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 "CanvasLayer"
27#define LOG_NDEBUG 1
28
29#include "config.h"
30#include "CanvasLayer.h"
31
32#if USE(ACCELERATED_COMPOSITING)
33
34#include "AndroidLog.h"
35#include "CanvasTexture.h"
36#include "DrawQuadData.h"
37#include "Image.h"
38#include "ImageBuffer.h"
39#include "RenderLayerCompositor.h"
40#include "SkBitmap.h"
41#include "SkBitmapRef.h"
42#include "SkCanvas.h"
43#include "TilesManager.h"
44
45namespace WebCore {
46
47CanvasLayer::CanvasLayer(RenderLayer* owner, HTMLCanvasElement* canvas)
48    : LayerAndroid(owner)
49    , m_canvas(canvas)
50    , m_dirtyCanvas()
51    , m_bitmap(0)
52{
53    init();
54    m_canvas->addObserver(this);
55    // Make sure we initialize in case the canvas has already been laid out
56    canvasResized(m_canvas);
57}
58
59CanvasLayer::CanvasLayer(const CanvasLayer& layer)
60    : LayerAndroid(layer)
61    , m_canvas(0)
62    , m_bitmap(0)
63{
64    init();
65    if (!layer.m_canvas) {
66        // The canvas has already been destroyed - this shouldn't happen
67        ALOGW("Creating a CanvasLayer for a destroyed canvas!");
68        m_visibleContentRect = IntRect();
69        m_offsetFromRenderer = IntSize();
70        m_texture->setHwAccelerated(false);
71        return;
72    }
73    // We are making a copy for the UI, sync the interesting bits
74    m_visibleContentRect = layer.visibleContentRect();
75    m_offsetFromRenderer = layer.offsetFromRenderer();
76    bool previousState = m_texture->hasValidTexture();
77    if (!previousState && layer.m_dirtyCanvas.isEmpty()) {
78        // We were previously in software and don't have anything new to draw,
79        // so stay in software
80        m_bitmap = layer.bitmap();
81        SkSafeRef(m_bitmap);
82    } else {
83        // Attempt to upload to a surface texture
84        if (!m_texture->uploadImageBuffer(layer.m_canvas->buffer())) {
85            // Blargh, no surface texture or ImageBuffer - fall back to software
86            m_bitmap = layer.bitmap();
87            SkSafeRef(m_bitmap);
88            // Merge the canvas invals with the layer's invals to repaint the needed
89            // tiles.
90            SkRegion::Iterator iter(layer.m_dirtyCanvas);
91            const IntPoint& offset = m_visibleContentRect.location();
92            for (; !iter.done(); iter.next()) {
93                SkIRect diff = iter.rect();
94                diff.fLeft += offset.x();
95                diff.fRight += offset.x();
96                diff.fTop += offset.y();
97                diff.fBottom += offset.y();
98                m_dirtyRegion.op(diff, SkRegion::kUnion_Op);
99            }
100        }
101        if (previousState != m_texture->hasValidTexture()) {
102            // Need to do a full inval of the canvas content as we are mode switching
103            m_dirtyRegion.op(m_visibleContentRect.x(), m_visibleContentRect.y(),
104                    m_visibleContentRect.maxX(), m_visibleContentRect.maxY(), SkRegion::kUnion_Op);
105        }
106    }
107}
108
109CanvasLayer::~CanvasLayer()
110{
111    if (m_canvas)
112        m_canvas->removeObserver(this);
113    SkSafeUnref(m_bitmap);
114}
115
116void CanvasLayer::init()
117{
118    m_texture = CanvasTexture::getCanvasTexture(this);
119}
120
121void CanvasLayer::canvasChanged(HTMLCanvasElement*, const FloatRect& changedRect)
122{
123    if (!m_texture->hasValidTexture()) {
124        // We only need to track invals if we aren't using a SurfaceTexture.
125        // If we drop out of hwa, we will do a full inval anyway
126        SkIRect irect = SkIRect::MakeXYWH(changedRect.x(), changedRect.y(),
127                                          changedRect.width(), changedRect.height());
128        m_dirtyCanvas.op(irect, SkRegion::kUnion_Op);
129    }
130    owningLayer()->compositor()->scheduleLayerFlush();
131}
132
133void CanvasLayer::canvasResized(HTMLCanvasElement*)
134{
135    const IntSize& size = m_canvas->size();
136    m_dirtyCanvas.setRect(0, 0, size.width(), size.height());
137    // If we are smaller than one tile, don't bother using a surface texture
138    if (size.width() <= TilesManager::tileWidth()
139            && size.height() <= TilesManager::tileHeight())
140        m_texture->setSize(IntSize());
141    else
142        m_texture->setSize(size);
143}
144
145void CanvasLayer::canvasDestroyed(HTMLCanvasElement*)
146{
147    m_canvas = 0;
148}
149
150void CanvasLayer::clearDirtyRegion()
151{
152    LayerAndroid::clearDirtyRegion();
153    m_dirtyCanvas.setEmpty();
154    if (m_canvas)
155        m_canvas->clearDirtyRect();
156}
157
158SkBitmapRef* CanvasLayer::bitmap() const
159{
160    if (!m_canvas || !m_canvas->buffer())
161        return 0;
162    return m_canvas->copiedImage()->nativeImageForCurrentFrame();
163}
164
165IntRect CanvasLayer::visibleContentRect() const
166{
167    if (!m_canvas
168            || !m_canvas->renderer()
169            || !m_canvas->renderer()->style()
170            || !m_canvas->inDocument()
171            || m_canvas->renderer()->style()->visibility() != VISIBLE)
172        return IntRect();
173    return m_canvas->renderBox()->contentBoxRect();
174}
175
176IntSize CanvasLayer::offsetFromRenderer() const
177{
178    return m_canvas->renderBox()->layer()->backing()->graphicsLayer()->offsetFromRenderer();
179}
180
181bool CanvasLayer::needsTexture()
182{
183    return (m_bitmap && !masksToBounds()) || LayerAndroid::needsTexture();
184}
185
186void CanvasLayer::contentDraw(SkCanvas* canvas, PaintStyle style)
187{
188    LayerAndroid::contentDraw(canvas, style);
189    if (!m_bitmap || masksToBounds())
190        return;
191    SkBitmap& bitmap = m_bitmap->bitmap();
192    SkRect dst = SkRect::MakeXYWH(m_visibleContentRect.x() - m_offsetFromRenderer.width(),
193                                  m_visibleContentRect.y() - m_offsetFromRenderer.height(),
194                                  m_visibleContentRect.width(), m_visibleContentRect.height());
195    canvas->drawBitmapRect(bitmap, 0, dst, 0);
196}
197
198bool CanvasLayer::drawGL(bool layerTilesDisabled)
199{
200    bool ret = LayerAndroid::drawGL(layerTilesDisabled);
201    m_texture->requireTexture();
202    if (!m_bitmap && m_texture->updateTexImage()) {
203        SkRect rect = SkRect::MakeXYWH(m_visibleContentRect.x() - m_offsetFromRenderer.width(),
204                                       m_visibleContentRect.y() - m_offsetFromRenderer.height(),
205                                       m_visibleContentRect.width(), m_visibleContentRect.height());
206        TextureQuadData data(m_texture->texture(), GL_TEXTURE_EXTERNAL_OES,
207                             GL_LINEAR, LayerQuad, &m_drawTransform, &rect);
208        TilesManager::instance()->shader()->drawQuad(&data);
209    }
210    return ret;
211}
212
213LayerAndroid::InvalidateFlags CanvasLayer::onSetHwAccelerated(bool hwAccelerated)
214{
215    if (m_texture->setHwAccelerated(hwAccelerated))
216        return LayerAndroid::InvalidateLayers;
217    return LayerAndroid::InvalidateNone;
218}
219
220} // namespace WebCore
221
222#endif // USE(ACCELERATED_COMPOSITING)
223