1/*
2 * Copyright 2012, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#define LOG_TAG "BaseLayerAndroid"
27#define LOG_NDEBUG 1
28
29#include "config.h"
30#include "BaseLayerAndroid.h"
31
32#include "AndroidLog.h"
33#include "CachedImage.h"
34#include "ClassTracker.h"
35#include "DrawQuadData.h"
36#include "FixedPositioning.h"
37#include "GLWebViewState.h"
38#include "ImagesManager.h"
39#include "LayerContent.h"
40#include "RenderStyle.h"
41#include "StyleCachedImage.h"
42#include "TilesManager.h"
43
44namespace WebCore {
45
46// Note: this must match the use of ID 0 specifying the base layer in DrawExtra
47#define BASE_UNIQUE_ID 0
48
49BaseLayerAndroid::BaseLayerAndroid(LayerContent* content)
50    : LayerAndroid((RenderLayer*)0)
51    , m_color(Color::white)
52    , m_positionsCalculated(false)
53{
54    if (content) {
55        setContent(content);
56        setSize(content->width(), content->height());
57    }
58    m_uniqueId = BASE_UNIQUE_ID;
59}
60
61void BaseLayerAndroid::getLocalTransform(SkMatrix* matrix) const
62{
63    matrix->reset();
64    // base layer doesn't use size in transform calculation
65    matrix->preConcat(getMatrix());
66}
67
68IFrameLayerAndroid* BaseLayerAndroid::updatePosition(SkRect viewport,
69                                                     IFrameLayerAndroid* parentIframeLayer)
70{
71    if (viewport.fRight > getWidth() || viewport.fBottom > getHeight()) {
72        // To handle the viewport expanding past the layer's size with HW accel,
73        // expand the size of the layer, so that tiles will cover the viewport.
74        setSize(std::max(viewport.fRight, getWidth()),
75                std::max(viewport.fBottom, getHeight()));
76    }
77
78    return LayerAndroid::updatePosition(viewport, parentIframeLayer);
79}
80
81void BaseLayerAndroid::updatePositionsRecursive(const SkRect& visibleContentRect)
82{
83    TRACE_METHOD();
84
85    updateLayerPositions(visibleContentRect);
86    TransformationMatrix ident;
87
88    // Start with an unnecessarily large clip, since the base layer can
89    // dynamically increase in size to cover the viewport, and we cache its draw
90    // clip. This way the base layer will never have it's visible area clipped
91    // by its m_clippingRect, only the viewport.
92    // Note: values larger than this suffer from floating point rounding issues
93    FloatRect clip(0, 0, 1e7, 1e7);
94
95    bool forcePositionCalculation = !m_positionsCalculated;
96    float scale = 1.0f;
97    // To minimize tearing in single surface mode, don't update the fixed element
98    // when scrolling. The fixed element will move incorrectly when scrolling,
99    // but its position will be corrected after scrolling.
100    bool disableFixedElemUpdate = false;
101    GLWebViewState* webViewState = state();
102    if (webViewState) {
103        scale = webViewState->scale();
104        disableFixedElemUpdate = webViewState->isScrolling()
105                                 && webViewState->isSingleSurfaceRenderingMode();
106    }
107    updateGLPositionsAndScale(ident, clip, 1, scale, forcePositionCalculation,
108                              disableFixedElemUpdate);
109
110    m_positionsCalculated = true;
111}
112
113ForegroundBaseLayerAndroid::ForegroundBaseLayerAndroid(LayerContent* content)
114    : LayerAndroid((RenderLayer*)0)
115{
116    setIntrinsicallyComposited(true);
117}
118
119FixedBackgroundImageLayerAndroid::FixedBackgroundImageLayerAndroid(PassRefPtr<RenderStyle> aStyle,
120                                                                   int w, int h)
121    : LayerAndroid((RenderLayer*)0)
122    , m_width(w)
123    , m_height(h)
124{
125    RefPtr<RenderStyle> style = aStyle;
126    FillLayer* layers = style->accessBackgroundLayers();
127    StyleImage* styleImage = layers->image();
128    CachedImage* cachedImage = static_cast<StyleCachedImage*>(styleImage)->cachedImage();
129    WebCore::Image* image = cachedImage->image();
130    setContentsImage(image->nativeImageForCurrentFrame());
131    setSize(image->width(), image->height());
132
133    setIntrinsicallyComposited(true);
134
135    SkLength left, top;
136    left = SkLength::convertLength(style->backgroundXPosition());
137    top = SkLength::convertLength(style->backgroundYPosition());
138
139    BackgroundImagePositioning* position = new BackgroundImagePositioning(this);
140    position->setRepeatX(style->backgroundRepeatX() != WebCore::NoRepeatFill);
141    position->setRepeatY(style->backgroundRepeatY() != WebCore::NoRepeatFill);
142
143    setFixedPosition(position);
144    position->setPosition(left, top);
145
146#ifdef DEBUG_COUNT
147    ClassTracker::instance()->increment("FixedBackgroundImageLayerAndroid");
148#endif
149}
150
151FixedBackgroundImageLayerAndroid::FixedBackgroundImageLayerAndroid(const FixedBackgroundImageLayerAndroid& layer)
152    : LayerAndroid(layer)
153    , m_width(layer.m_width)
154    , m_height(layer.m_height)
155{
156#ifdef DEBUG_COUNT
157    ClassTracker::instance()->increment("FixedBackgroundImageLayerAndroid");
158#endif
159}
160
161FixedBackgroundImageLayerAndroid::~FixedBackgroundImageLayerAndroid()
162{
163#ifdef DEBUG_COUNT
164    ClassTracker::instance()->decrement("FixedBackgroundImageLayerAndroid");
165#endif
166}
167
168static bool needToDisplayImage(bool repeatX, bool repeatY, float dx, float dy)
169{
170    // handles the repeat attribute for the background image
171    if (repeatX && repeatY)
172        return true;
173    if (repeatX && !repeatY && dy == 0)
174        return true;
175    if (!repeatX && repeatY && dx == 0)
176        return true;
177    if (dx == 0 && dy == 0)
178        return true;
179
180    return false;
181}
182
183// Return true when fast draw succeeds.
184// For the repeated image content, we just need to draw a single quad and use
185// the GL shader to repeat.
186bool FixedBackgroundImageLayerAndroid::drawSimpleQuad(ImageTexture* imageTexture,
187                                                      BackgroundImagePositioning* position,
188                                                      const IntPoint& repeatTimes,
189                                                      const FloatPoint& startPoint,
190                                                      const FloatPoint& origin,
191                                                      const Color& backgroundColor)
192{
193    // The limitation for current implementation is that we can only speed up
194    // single tile size image.
195    // TODO: add the fast path to imageTexture which contains >1 tiles.
196    GLuint imageTextureId = imageTexture->getImageTextureId();
197    if (!imageTextureId)
198        return false;
199
200    int nbX = repeatTimes.x();
201    int nbY = repeatTimes.y();
202    float startX = startPoint.x();
203    float startY = startPoint.y();
204    bool repeatX = position->repeatX();
205    bool repeatY = position->repeatY();
206
207    // Draw the entire background when repeat only in one direction or no repeat.
208    if (!repeatX || !repeatY) {
209        SkRect backgroundRect;
210        backgroundRect.fLeft = origin.x() - startX;
211        backgroundRect.fTop = origin.y() - startY;
212        backgroundRect.fRight = backgroundRect.fLeft + getWidth() * nbX;
213        backgroundRect.fBottom = backgroundRect.fTop + getHeight() * nbY;
214        PureColorQuadData backgroundData(backgroundColor, BaseQuad,
215                                         0, &backgroundRect, 1.0, true);
216        TilesManager::instance()->shader()->drawQuad(&backgroundData);
217    }
218
219    // Now draw the repeated images.
220    // We set the quad size as the image size, then imageRepeatRanges will
221    // control how many times the image will be repeated by expanding the
222    // quad and texture coordinates.
223    // The image size can be smaller than a tile, so repeatScale will passed
224    // into the shader to scale the texture coordinates.
225    SkRect imageRect = SkRect::MakeXYWH(0, 0, getWidth(), getHeight());
226    FloatRect imageRepeatRanges(0, 0, repeatX ? nbX : 1, repeatY ? nbY : 1);
227
228    FloatSize repeatScale(float(getWidth()) / TilesManager::tileWidth(),
229                          float(getHeight()) / TilesManager::tileHeight());
230
231    ALOGV("repeatedQuadData: startX %f, startY %f , getWidth() %f, getHeight() %f,"
232          " nbX %d, nbY %d, repeatImageTimesX, repeatImageTimesY %d %d"
233          " repeatScale width %f, height %f, origin x %f y %f",
234          startX , startY  , getWidth(), getHeight(), nbX , nbY,
235          imageRepeatRanges.width(), imageRepeatRanges.height(),
236          repeatScale.width(), repeatScale.height(), origin.x(), origin.y());
237
238    // Adding startX and startY into the transform can handle the fixed right /
239    // fixed bottom case.
240    TransformationMatrix matrix = *drawTransform();
241    matrix.translate(repeatX ? -startX : 0, repeatY ? -startY : 0);
242
243    TextureQuadData repeatedQuadData(imageTextureId, GL_TEXTURE_2D, GL_LINEAR,
244                                     LayerQuad, &matrix, &imageRect, getOpacity(),
245                                     true, imageRepeatRanges, repeatScale);
246    TilesManager::instance()->shader()->drawQuad(&repeatedQuadData);
247    return true;
248}
249
250void FixedBackgroundImageLayerAndroid::drawRepeatedGrid(ImageTexture* imageTexture,
251                                                        BackgroundImagePositioning* position,
252                                                        const IntPoint& repeatTimes,
253                                                        const FloatPoint& startPoint,
254                                                        const FloatPoint& origin,
255                                                        const Color& backgroundColor)
256{
257    // Cover the entire background
258    int nbX = repeatTimes.x();
259    int nbY = repeatTimes.y();
260    float startX = startPoint.x();
261    float startY = startPoint.y();
262    for (int i = 0; i < nbY; i++) {
263        float dy = (i * getHeight()) - startY;
264        for (int j = 0; j < nbX; j++) {
265            float dx = (j * getWidth()) - startX;
266            if (needToDisplayImage(position->repeatX(),
267                                   position->repeatY(),
268                                   dx, dy)) {
269                FloatPoint p(dx, dy);
270                imageTexture->drawGL(this, getOpacity(), &p);
271            } else {
272                // If the image is not displayed, we still need to fill
273                // with the background color
274                SkRect rect;
275                rect.fLeft = origin.x() + dx;
276                rect.fTop = origin.y() + dy;
277                rect.fRight = rect.fLeft + getWidth();
278                rect.fBottom = rect.fTop + getHeight();
279                PureColorQuadData backgroundData(backgroundColor, BaseQuad,
280                                                 0, &rect, 1.0);
281                TilesManager::instance()->shader()->drawQuad(&backgroundData);
282            }
283        }
284    }
285}
286
287bool FixedBackgroundImageLayerAndroid::drawGL(bool layerTilesDisabled)
288{
289    if (layerTilesDisabled)
290        return false;
291    if (!m_imageCRC)
292        return false;
293
294    ImageTexture* imageTexture = ImagesManager::instance()->retainImage(m_imageCRC);
295    if (!imageTexture) {
296        ImagesManager::instance()->releaseImage(m_imageCRC);
297        return false;
298    }
299
300    // We have a fixed background image, let's draw it
301    if (m_fixedPosition && m_fixedPosition->isBackgroundImagePositioning()) {
302        BackgroundImagePositioning* position =
303            static_cast<BackgroundImagePositioning*>(m_fixedPosition);
304
305        IntPoint repeatTimes(position->nbRepeatX(), position->nbRepeatY());
306        FloatPoint startPoint(position->offsetX() * getWidth(),
307                              position->offsetY() * getHeight());
308
309        FloatPoint origin;
310        origin = drawTransform()->mapPoint(origin);
311
312        Color backgroundColor = Color((int)SkColorGetR(m_backgroundColor),
313                                      (int)SkColorGetG(m_backgroundColor),
314                                      (int)SkColorGetB(m_backgroundColor),
315                                      (int)SkColorGetA(m_backgroundColor));
316
317        bool drawSimpleQuadSuccess = drawSimpleQuad(imageTexture, position,
318                                                    repeatTimes, startPoint,
319                                                    origin, backgroundColor);
320
321        if (!drawSimpleQuadSuccess) {
322            drawRepeatedGrid(imageTexture, position, repeatTimes, startPoint,
323                             origin, backgroundColor);
324        }
325    } else
326        imageTexture->drawGL(this, getOpacity());
327
328    ImagesManager::instance()->releaseImage(m_imageCRC);
329
330    return false;
331}
332
333Image* FixedBackgroundImageLayerAndroid::GetCachedImage(PassRefPtr<RenderStyle> aStyle)
334{
335    RefPtr<RenderStyle> style = aStyle;
336    if (!style)
337        return 0;
338
339    if (!style->hasFixedBackgroundImage())
340        return 0;
341
342    FillLayer* layers = style->accessBackgroundLayers();
343    StyleImage* styleImage = layers->image();
344
345    if (!styleImage)
346        return 0;
347
348    if (!styleImage->isLoaded())
349        return 0;
350
351    if (!styleImage->isCachedImage())
352        return 0;
353
354    CachedImage* cachedImage = static_cast<StyleCachedImage*>(styleImage)->cachedImage();
355
356    Image* image = cachedImage->image();
357
358    if (image && !image->nativeImageForCurrentFrame())
359        return 0;
360
361    if (image == Image::nullImage())
362        return 0;
363
364    return image;
365}
366
367} // namespace WebCore
368