/* * Copyright 2012, The Android Open Source Project * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #define LOG_TAG "BaseLayerAndroid" #define LOG_NDEBUG 1 #include "config.h" #include "BaseLayerAndroid.h" #include "AndroidLog.h" #include "CachedImage.h" #include "ClassTracker.h" #include "DrawQuadData.h" #include "FixedPositioning.h" #include "GLWebViewState.h" #include "ImagesManager.h" #include "LayerContent.h" #include "RenderStyle.h" #include "StyleCachedImage.h" #include "TilesManager.h" namespace WebCore { // Note: this must match the use of ID 0 specifying the base layer in DrawExtra #define BASE_UNIQUE_ID 0 BaseLayerAndroid::BaseLayerAndroid(LayerContent* content) : LayerAndroid((RenderLayer*)0) , m_color(Color::white) , m_positionsCalculated(false) { if (content) { setContent(content); setSize(content->width(), content->height()); } m_uniqueId = BASE_UNIQUE_ID; } void BaseLayerAndroid::getLocalTransform(SkMatrix* matrix) const { matrix->reset(); // base layer doesn't use size in transform calculation matrix->preConcat(getMatrix()); } IFrameLayerAndroid* BaseLayerAndroid::updatePosition(SkRect viewport, IFrameLayerAndroid* parentIframeLayer) { if (viewport.fRight > getWidth() || viewport.fBottom > getHeight()) { // To handle the viewport expanding past the layer's size with HW accel, // expand the size of the layer, so that tiles will cover the viewport. setSize(std::max(viewport.fRight, getWidth()), std::max(viewport.fBottom, getHeight())); } return LayerAndroid::updatePosition(viewport, parentIframeLayer); } void BaseLayerAndroid::updatePositionsRecursive(const SkRect& visibleContentRect) { TRACE_METHOD(); updateLayerPositions(visibleContentRect); TransformationMatrix ident; // Start with an unnecessarily large clip, since the base layer can // dynamically increase in size to cover the viewport, and we cache its draw // clip. This way the base layer will never have it's visible area clipped // by its m_clippingRect, only the viewport. // Note: values larger than this suffer from floating point rounding issues FloatRect clip(0, 0, 1e7, 1e7); bool forcePositionCalculation = !m_positionsCalculated; float scale = 1.0f; // To minimize tearing in single surface mode, don't update the fixed element // when scrolling. The fixed element will move incorrectly when scrolling, // but its position will be corrected after scrolling. bool disableFixedElemUpdate = false; GLWebViewState* webViewState = state(); if (webViewState) { scale = webViewState->scale(); disableFixedElemUpdate = webViewState->isScrolling() && webViewState->isSingleSurfaceRenderingMode(); } updateGLPositionsAndScale(ident, clip, 1, scale, forcePositionCalculation, disableFixedElemUpdate); m_positionsCalculated = true; } ForegroundBaseLayerAndroid::ForegroundBaseLayerAndroid(LayerContent* content) : LayerAndroid((RenderLayer*)0) { setIntrinsicallyComposited(true); } FixedBackgroundImageLayerAndroid::FixedBackgroundImageLayerAndroid(PassRefPtr aStyle, int w, int h) : LayerAndroid((RenderLayer*)0) , m_width(w) , m_height(h) { RefPtr style = aStyle; FillLayer* layers = style->accessBackgroundLayers(); StyleImage* styleImage = layers->image(); CachedImage* cachedImage = static_cast(styleImage)->cachedImage(); WebCore::Image* image = cachedImage->image(); setContentsImage(image->nativeImageForCurrentFrame()); setSize(image->width(), image->height()); setIntrinsicallyComposited(true); SkLength left, top; left = SkLength::convertLength(style->backgroundXPosition()); top = SkLength::convertLength(style->backgroundYPosition()); BackgroundImagePositioning* position = new BackgroundImagePositioning(this); position->setRepeatX(style->backgroundRepeatX() != WebCore::NoRepeatFill); position->setRepeatY(style->backgroundRepeatY() != WebCore::NoRepeatFill); setFixedPosition(position); position->setPosition(left, top); #ifdef DEBUG_COUNT ClassTracker::instance()->increment("FixedBackgroundImageLayerAndroid"); #endif } FixedBackgroundImageLayerAndroid::FixedBackgroundImageLayerAndroid(const FixedBackgroundImageLayerAndroid& layer) : LayerAndroid(layer) , m_width(layer.m_width) , m_height(layer.m_height) { #ifdef DEBUG_COUNT ClassTracker::instance()->increment("FixedBackgroundImageLayerAndroid"); #endif } FixedBackgroundImageLayerAndroid::~FixedBackgroundImageLayerAndroid() { #ifdef DEBUG_COUNT ClassTracker::instance()->decrement("FixedBackgroundImageLayerAndroid"); #endif } static bool needToDisplayImage(bool repeatX, bool repeatY, float dx, float dy) { // handles the repeat attribute for the background image if (repeatX && repeatY) return true; if (repeatX && !repeatY && dy == 0) return true; if (!repeatX && repeatY && dx == 0) return true; if (dx == 0 && dy == 0) return true; return false; } // Return true when fast draw succeeds. // For the repeated image content, we just need to draw a single quad and use // the GL shader to repeat. bool FixedBackgroundImageLayerAndroid::drawSimpleQuad(ImageTexture* imageTexture, BackgroundImagePositioning* position, const IntPoint& repeatTimes, const FloatPoint& startPoint, const FloatPoint& origin, const Color& backgroundColor) { // The limitation for current implementation is that we can only speed up // single tile size image. // TODO: add the fast path to imageTexture which contains >1 tiles. GLuint imageTextureId = imageTexture->getImageTextureId(); if (!imageTextureId) return false; int nbX = repeatTimes.x(); int nbY = repeatTimes.y(); float startX = startPoint.x(); float startY = startPoint.y(); bool repeatX = position->repeatX(); bool repeatY = position->repeatY(); // Draw the entire background when repeat only in one direction or no repeat. if (!repeatX || !repeatY) { SkRect backgroundRect; backgroundRect.fLeft = origin.x() - startX; backgroundRect.fTop = origin.y() - startY; backgroundRect.fRight = backgroundRect.fLeft + getWidth() * nbX; backgroundRect.fBottom = backgroundRect.fTop + getHeight() * nbY; PureColorQuadData backgroundData(backgroundColor, BaseQuad, 0, &backgroundRect, 1.0, true); TilesManager::instance()->shader()->drawQuad(&backgroundData); } // Now draw the repeated images. // We set the quad size as the image size, then imageRepeatRanges will // control how many times the image will be repeated by expanding the // quad and texture coordinates. // The image size can be smaller than a tile, so repeatScale will passed // into the shader to scale the texture coordinates. SkRect imageRect = SkRect::MakeXYWH(0, 0, getWidth(), getHeight()); FloatRect imageRepeatRanges(0, 0, repeatX ? nbX : 1, repeatY ? nbY : 1); FloatSize repeatScale(float(getWidth()) / TilesManager::tileWidth(), float(getHeight()) / TilesManager::tileHeight()); ALOGV("repeatedQuadData: startX %f, startY %f , getWidth() %f, getHeight() %f," " nbX %d, nbY %d, repeatImageTimesX, repeatImageTimesY %d %d" " repeatScale width %f, height %f, origin x %f y %f", startX , startY , getWidth(), getHeight(), nbX , nbY, imageRepeatRanges.width(), imageRepeatRanges.height(), repeatScale.width(), repeatScale.height(), origin.x(), origin.y()); // Adding startX and startY into the transform can handle the fixed right / // fixed bottom case. TransformationMatrix matrix = *drawTransform(); matrix.translate(repeatX ? -startX : 0, repeatY ? -startY : 0); TextureQuadData repeatedQuadData(imageTextureId, GL_TEXTURE_2D, GL_LINEAR, LayerQuad, &matrix, &imageRect, getOpacity(), true, imageRepeatRanges, repeatScale); TilesManager::instance()->shader()->drawQuad(&repeatedQuadData); return true; } void FixedBackgroundImageLayerAndroid::drawRepeatedGrid(ImageTexture* imageTexture, BackgroundImagePositioning* position, const IntPoint& repeatTimes, const FloatPoint& startPoint, const FloatPoint& origin, const Color& backgroundColor) { // Cover the entire background int nbX = repeatTimes.x(); int nbY = repeatTimes.y(); float startX = startPoint.x(); float startY = startPoint.y(); for (int i = 0; i < nbY; i++) { float dy = (i * getHeight()) - startY; for (int j = 0; j < nbX; j++) { float dx = (j * getWidth()) - startX; if (needToDisplayImage(position->repeatX(), position->repeatY(), dx, dy)) { FloatPoint p(dx, dy); imageTexture->drawGL(this, getOpacity(), &p); } else { // If the image is not displayed, we still need to fill // with the background color SkRect rect; rect.fLeft = origin.x() + dx; rect.fTop = origin.y() + dy; rect.fRight = rect.fLeft + getWidth(); rect.fBottom = rect.fTop + getHeight(); PureColorQuadData backgroundData(backgroundColor, BaseQuad, 0, &rect, 1.0); TilesManager::instance()->shader()->drawQuad(&backgroundData); } } } } bool FixedBackgroundImageLayerAndroid::drawGL(bool layerTilesDisabled) { if (layerTilesDisabled) return false; if (!m_imageCRC) return false; ImageTexture* imageTexture = ImagesManager::instance()->retainImage(m_imageCRC); if (!imageTexture) { ImagesManager::instance()->releaseImage(m_imageCRC); return false; } // We have a fixed background image, let's draw it if (m_fixedPosition && m_fixedPosition->isBackgroundImagePositioning()) { BackgroundImagePositioning* position = static_cast(m_fixedPosition); IntPoint repeatTimes(position->nbRepeatX(), position->nbRepeatY()); FloatPoint startPoint(position->offsetX() * getWidth(), position->offsetY() * getHeight()); FloatPoint origin; origin = drawTransform()->mapPoint(origin); Color backgroundColor = Color((int)SkColorGetR(m_backgroundColor), (int)SkColorGetG(m_backgroundColor), (int)SkColorGetB(m_backgroundColor), (int)SkColorGetA(m_backgroundColor)); bool drawSimpleQuadSuccess = drawSimpleQuad(imageTexture, position, repeatTimes, startPoint, origin, backgroundColor); if (!drawSimpleQuadSuccess) { drawRepeatedGrid(imageTexture, position, repeatTimes, startPoint, origin, backgroundColor); } } else imageTexture->drawGL(this, getOpacity()); ImagesManager::instance()->releaseImage(m_imageCRC); return false; } Image* FixedBackgroundImageLayerAndroid::GetCachedImage(PassRefPtr aStyle) { RefPtr style = aStyle; if (!style) return 0; if (!style->hasFixedBackgroundImage()) return 0; FillLayer* layers = style->accessBackgroundLayers(); StyleImage* styleImage = layers->image(); if (!styleImage) return 0; if (!styleImage->isLoaded()) return 0; if (!styleImage->isCachedImage()) return 0; CachedImage* cachedImage = static_cast(styleImage)->cachedImage(); Image* image = cachedImage->image(); if (image && !image->nativeImageForCurrentFrame()) return 0; if (image == Image::nullImage()) return 0; return image; } } // namespace WebCore