Surface.cpp revision 893264ea664be9af3ac64e24116045b51df6f031
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 "Surface"
27#define LOG_NDEBUG 1
28
29#include "config.h"
30#include "Surface.h"
31
32#include "AndroidLog.h"
33#include "BaseLayerAndroid.h"
34#include "ClassTracker.h"
35#include "LayerAndroid.h"
36#include "GLWebViewState.h"
37#include "SkCanvas.h"
38#include "SurfaceBacking.h"
39#include "TilesManager.h"
40
41// Surfaces with an area larger than 2048*2048 should never be unclipped
42#define MAX_UNCLIPPED_AREA 4194304
43
44namespace WebCore {
45
46Surface::Surface()
47    : m_surfaceBacking(0)
48    , m_needsTexture(false)
49    , m_hasText(false)
50{
51#ifdef DEBUG_COUNT
52    ClassTracker::instance()->increment("Surface");
53#endif
54}
55
56Surface::~Surface()
57{
58    for (unsigned int i = 0; i < m_layers.size(); i++)
59        SkSafeUnref(m_layers[i]);
60    if (m_surfaceBacking)
61        SkSafeUnref(m_surfaceBacking);
62#ifdef DEBUG_COUNT
63    ClassTracker::instance()->decrement("Surface");
64#endif
65}
66
67bool Surface::tryUpdateSurface(Surface* oldSurface)
68{
69    if (!needsTexture() || !oldSurface->needsTexture())
70        return false;
71
72    // merge surfaces based on first layer ID
73    if (getFirstLayer()->uniqueId() != oldSurface->getFirstLayer()->uniqueId())
74        return false;
75
76    m_surfaceBacking = oldSurface->m_surfaceBacking;
77    SkSafeRef(m_surfaceBacking);
78
79    ALOGV("%p taking old SurfBack %p from surface %p, nt %d",
80          this, m_surfaceBacking, oldSurface, oldSurface->needsTexture());
81
82    if (!m_surfaceBacking) {
83        // no SurfBack to inval, so don't worry about it.
84        return true;
85    }
86
87    if (singleLayer() && oldSurface->singleLayer()) {
88        // both are single matching layers, simply apply inval
89        SkRegion* layerInval = getFirstLayer()->getInvalRegion();
90        m_surfaceBacking->markAsDirty(*layerInval);
91    } else {
92        SkRegion invalRegion;
93        bool fullInval = m_layers.size() != oldSurface->m_layers.size();
94        if (!fullInval) {
95            for (unsigned int i = 0; i < m_layers.size(); i++) {
96                if (m_layers[i]->uniqueId() != oldSurface->m_layers[i]->uniqueId()) {
97                    // layer list has changed, fully invalidate
98                    // TODO: partially invalidate based on layer size/position
99                    fullInval = true;
100                    break;
101                } else if (!m_layers[i]->getInvalRegion()->isEmpty()) {
102                    // merge layer inval - translate the layer's inval region into surface coordinates
103                    SkPoint pos = m_layers[i]->getPosition();
104                    m_layers[i]->getInvalRegion()->translate(pos.fX, pos.fY);
105                    invalRegion.op(*(m_layers[i]->getInvalRegion()), SkRegion::kUnion_Op);
106                    break;
107                }
108            }
109        }
110
111        if (fullInval)
112            invalRegion.setRect(-1e8, -1e8, 2e8, 2e8);
113
114        m_surfaceBacking->markAsDirty(invalRegion);
115    }
116    return true;
117}
118
119void Surface::addLayer(LayerAndroid* layer, const TransformationMatrix& transform)
120{
121    m_layers.append(layer);
122    SkSafeRef(layer);
123
124    m_needsTexture |= layer->needsTexture();
125    m_hasText |= layer->hasText();
126
127    // calculate area size for comparison later
128    IntRect rect = layer->unclippedArea();
129    SkPoint pos = layer->getPosition();
130    rect.setLocation(IntPoint(pos.fX, pos.fY));
131
132    if (layer->needsTexture()) {
133        if (m_unclippedArea.isEmpty()) {
134            m_drawTransform = transform;
135            m_drawTransform.translate3d(-pos.fX, -pos.fY, 0);
136            m_unclippedArea = rect;
137        } else
138            m_unclippedArea.unite(rect);
139        ALOGV("Surf %p adding LA %p, size  %d, %d  %dx%d, now Surf size %d,%d  %dx%d",
140              this, layer, rect.x(), rect.y(), rect.width(), rect.height(),
141              m_unclippedArea.x(), m_unclippedArea.y(),
142              m_unclippedArea.width(), m_unclippedArea.height());
143    }
144
145    if (isBase())
146        m_background = static_cast<BaseLayerAndroid*>(layer)->getBackgroundColor();
147}
148
149IntRect Surface::visibleArea()
150{
151    if (singleLayer())
152        return getFirstLayer()->visibleArea();
153
154    IntRect rect = m_unclippedArea;
155
156    // clip with the viewport in documents coordinate
157    IntRect documentViewport(TilesManager::instance()->shader()->documentViewport());
158    rect.intersect(documentViewport);
159
160    // TODO: handle recursive layer clip
161
162    return rect;
163}
164
165IntRect Surface::unclippedArea()
166{
167    if (singleLayer())
168        return getFirstLayer()->unclippedArea();
169    return m_unclippedArea;
170}
171
172bool Surface::useAggressiveRendering()
173{
174    // When the background is semi-opaque, 0 < alpha < 255, we had to turn off
175    // low res to avoid artifacts from double drawing.
176    // TODO: avoid double drawing for low res tiles.
177    return isBase()
178           && (!m_background.alpha()
179           || !m_background.hasAlpha());
180}
181
182void Surface::prepareGL(bool layerTilesDisabled)
183{
184    bool tilesDisabled = layerTilesDisabled && !isBase();
185    if (!m_surfaceBacking) {
186        ALOGV("prepareGL on Surf %p, no SurfBack, needsTexture? %d",
187              this, m_surfaceBacking, needsTexture());
188
189        if (!needsTexture())
190            return;
191
192        m_surfaceBacking = new SurfaceBacking(isBase());
193    }
194
195    if (tilesDisabled) {
196        m_surfaceBacking->discardTextures();
197    } else {
198        bool allowZoom = hasText(); // only allow for scale > 1 if painting vectors
199        IntRect prepareArea = computePrepareArea();
200        IntRect fullArea = unclippedArea();
201
202        ALOGV("prepareGL on Surf %p with SurfBack %p, %d layers",
203              this, m_surfaceBacking, m_layers.size());
204
205        m_surfaceBacking->prepareGL(getFirstLayer()->state(), allowZoom,
206                                      prepareArea, fullArea,
207                                      this, useAggressiveRendering());
208    }
209}
210
211bool Surface::drawGL(bool layerTilesDisabled)
212{
213    bool tilesDisabled = layerTilesDisabled && !isBase();
214    if (!getFirstLayer()->visible())
215        return false;
216
217    if (!isBase()) {
218        // TODO: why are clipping regions wrong for base layer?
219        FloatRect drawClip = getFirstLayer()->drawClip();
220        FloatRect clippingRect = TilesManager::instance()->shader()->rectInScreenCoord(drawClip);
221        TilesManager::instance()->shader()->clip(clippingRect);
222    }
223
224    bool askRedraw = false;
225    if (m_surfaceBacking && !tilesDisabled) {
226        ALOGV("drawGL on Surf %p with SurfBack %p", this, m_surfaceBacking);
227
228        // TODO: why this visibleArea is different from visibleRect at zooming for base?
229        IntRect drawArea = visibleArea();
230        m_surfaceBacking->drawGL(drawArea, opacity(), drawTransform(),
231                                 useAggressiveRendering(), background());
232    }
233
234    // draw member layers (draws image textures, glextras)
235    for (unsigned int i = 0; i < m_layers.size(); i++)
236        askRedraw |= m_layers[i]->drawGL(tilesDisabled);
237
238    return askRedraw;
239}
240
241void Surface::swapTiles()
242{
243    if (!m_surfaceBacking)
244        return;
245
246    m_surfaceBacking->swapTiles();
247}
248
249bool Surface::isReady()
250{
251    if (!m_surfaceBacking)
252        return true;
253
254    return m_surfaceBacking->isReady();
255}
256
257bool Surface::isMissingContent()
258{
259    if (!m_surfaceBacking)
260        return true;
261
262    return m_surfaceBacking->isMissingContent();
263}
264
265IntRect Surface::computePrepareArea() {
266    IntRect area;
267
268    if (!getFirstLayer()->contentIsScrollable()
269        && !isBase()
270        && getFirstLayer()->state()->layersRenderingMode() == GLWebViewState::kAllTextures) {
271
272        area = unclippedArea();
273
274        double total = ((double) area.width()) * ((double) area.height());
275        if (total > MAX_UNCLIPPED_AREA)
276            area = visibleArea();
277    } else {
278        area = visibleArea();
279    }
280
281    return area;
282}
283
284void Surface::computeTexturesAmount(TexturesResult* result)
285{
286    if (!m_surfaceBacking || isBase())
287        return;
288
289    m_surfaceBacking->computeTexturesAmount(result, getFirstLayer());
290}
291
292bool Surface::isBase()
293{
294    // base layer surface
295    // - doesn't use layer tiles (disables blending, doesn't compute textures amount)
296    // - ignores clip rects
297    // - only prepares clippedArea
298    return getFirstLayer()->subclassType() == LayerAndroid::BaseLayer;
299}
300
301bool Surface::paint(SkCanvas* canvas)
302{
303    if (singleLayer()) {
304        getFirstLayer()->contentDraw(canvas, Layer::UnmergedLayers);
305
306        // TODO: double buffer by disabling SurfaceCollection swaps and position
307        // updates until painting complete
308
309        // In single surface mode, draw layer content onto the base layer
310        if (isBase()
311            && getFirstLayer()->countChildren()
312            && getFirstLayer()->state()->layersRenderingMode() > GLWebViewState::kClippedTextures)
313            getFirstLayer()->getChild(0)->drawCanvas(canvas, true, Layer::FlattenedLayers);
314    } else {
315        SkAutoCanvasRestore acr(canvas, true);
316        SkMatrix matrix;
317        GLUtils::toSkMatrix(matrix, m_drawTransform);
318
319        SkMatrix inverse;
320        inverse.reset();
321        matrix.invert(&inverse);
322
323        SkMatrix canvasMatrix = canvas->getTotalMatrix();
324        inverse.postConcat(canvasMatrix);
325        canvas->setMatrix(inverse);
326
327        for (unsigned int i=0; i<m_layers.size(); i++)
328            m_layers[i]->drawCanvas(canvas, false, Layer::MergedLayers);
329    }
330    return true;
331}
332
333float Surface::opacity()
334{
335    if (singleLayer())
336        return getFirstLayer()->drawOpacity();
337    return 1.0;
338}
339
340Color* Surface::background()
341{
342    if (!isBase() || !m_background.isValid())
343        return 0;
344    return &m_background;
345}
346
347const TransformationMatrix* Surface::drawTransform()
348{
349    // single layer surfaces query the layer's draw transform, while multi-layer
350    // surfaces copy the draw transform once, during initialization
351    // TODO: support fixed multi-layer surfaces by querying the changing drawTransform
352    if (singleLayer())
353        return getFirstLayer()->drawTransform();
354
355    return &m_drawTransform;
356}
357
358} // namespace WebCore
359