blob: 87d6486a70e0202dd81b8fa6c64672dba141b8f3 [file] [log] [blame]
/*
* 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<RenderStyle> aStyle,
int w, int h)
: LayerAndroid((RenderLayer*)0)
, m_width(w)
, m_height(h)
{
RefPtr<RenderStyle> style = aStyle;
FillLayer* layers = style->accessBackgroundLayers();
StyleImage* styleImage = layers->image();
CachedImage* cachedImage = static_cast<StyleCachedImage*>(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<BackgroundImagePositioning*>(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<RenderStyle> aStyle)
{
RefPtr<RenderStyle> 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<StyleCachedImage*>(styleImage)->cachedImage();
Image* image = cachedImage->image();
if (image && !image->nativeImageForCurrentFrame())
return 0;
if (image == Image::nullImage())
return 0;
return image;
}
} // namespace WebCore