• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "PicturePile"
27 #define LOG_NDEBUG 1
28 
29 #include "config.h"
30 #include "PicturePile.h"
31 
32 #include "AndroidLog.h"
33 #include "FloatRect.h"
34 #include "GraphicsContext.h"
35 #include "PlatformGraphicsContextSkia.h"
36 #include "SkCanvas.h"
37 #include "SkNWayCanvas.h"
38 #include "SkPicture.h"
39 #include "SkPixelRef.h"
40 #include "SkRect.h"
41 #include "SkRegion.h"
42 
43 #define ENABLE_PRERENDERED_INVALS true
44 #define MAX_OVERLAP_COUNT 2
45 #define MAX_OVERLAP_AREA .7
46 
47 namespace WebCore {
48 
toSkIRect(const IntRect & rect)49 static SkIRect toSkIRect(const IntRect& rect) {
50     return SkIRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height());
51 }
52 
extractClipBounds(SkCanvas * canvas,const IntSize & size)53 static IntRect extractClipBounds(SkCanvas* canvas, const IntSize& size) {
54     SkRect clip;
55     canvas->getClipBounds(&clip);
56     clip.intersect(0, 0, size.width(), size.height());
57     return enclosingIntRect(clip);
58 }
59 
PicturePile(const PicturePile & other)60 PicturePile::PicturePile(const PicturePile& other)
61     : m_size(other.m_size)
62     , m_pile(other.m_pile)
63     , m_webkitInvals(other.m_webkitInvals)
64 {
65 }
66 
PicturePile(SkPicture * picture)67 PicturePile::PicturePile(SkPicture* picture)
68 {
69     m_size = IntSize(picture->width(), picture->height());
70     PictureContainer pc(IntRect(0, 0, m_size.width(), m_size.height()));
71     pc.picture = picture;
72     pc.dirty = false;
73     m_pile.append(pc);
74 }
75 
draw(SkCanvas * canvas)76 void PicturePile::draw(SkCanvas* canvas)
77 {
78     /* Loop down recursively, subtracting the previous clip from the SkRegion,
79      * stopping when the SkRegion is empty. This will still draw back-to-front,
80      * but it will clip out anything obscured. For performance reasons we use
81      * the rect bounds of the SkRegion for the clip, so this still can't be
82      * used for translucent surfaces
83      */
84     TRACE_METHOD();
85     IntRect clipBounds = extractClipBounds(canvas, m_size);
86     SkRegion clipRegion(toSkIRect(clipBounds));
87     drawWithClipRecursive(canvas, clipRegion, m_pile.size() - 1);
88 }
89 
clearPrerenders()90 void PicturePile::clearPrerenders()
91 {
92     for (size_t i = 0; i < m_pile.size(); i++)
93         m_pile[i].prerendered.clear();
94 }
95 
drawWithClipRecursive(SkCanvas * canvas,SkRegion & clipRegion,int index)96 void PicturePile::drawWithClipRecursive(SkCanvas* canvas, SkRegion& clipRegion,
97                                         int index)
98 {
99     // TODO: Add some debug visualizations of this
100     if (index < 0 || clipRegion.isEmpty())
101         return;
102     PictureContainer& pc = m_pile[index];
103     IntRect intersection = clipRegion.getBounds();
104     intersection.intersect(pc.area);
105     if (pc.picture && !intersection.isEmpty()) {
106         clipRegion.op(intersection, SkRegion::kDifference_Op);
107         drawWithClipRecursive(canvas, clipRegion, index - 1);
108         int saved = canvas->save();
109         canvas->clipRect(intersection);
110         canvas->translate(pc.area.x(), pc.area.y());
111         canvas->drawPicture(*pc.picture);
112         canvas->restoreToCount(saved);
113     } else
114         drawWithClipRecursive(canvas, clipRegion, index - 1);
115 }
116 
117 // Used by WebViewCore
invalidate(const IntRect & dirtyRect)118 void PicturePile::invalidate(const IntRect& dirtyRect)
119 {
120     // This will typically happen if the document has been resized but we haven't
121     // drawn yet. As the first draw after a size change will do a full inval anyway,
122     // don't bother tracking individual rects
123     // TODO: Instead of clipping here, we should take the invals as given
124     // and when the size changes just inval the deltas. This prevents a full
125     // redraw for a page that grows
126     IntRect inval = dirtyRect;
127     inval.intersect(IntRect(0, 0, m_size.width(), m_size.height()));
128     if (inval.isEmpty()) {
129         ALOGV("Rejecting inval " INT_RECT_FORMAT, INT_RECT_ARGS(dirtyRect));
130         return;
131     }
132     // TODO: Support multiple non-intersecting webkit invals
133     if (m_webkitInvals.size())
134         m_webkitInvals[0].unite(inval);
135     else
136         m_webkitInvals.append(inval);
137 }
138 
setSize(const IntSize & size)139 void PicturePile::setSize(const IntSize& size)
140 {
141     if (m_size == size)
142         return;
143     m_size = size;
144     // TODO: See above about just adding invals for new content
145     m_pile.clear();
146     m_webkitInvals.clear();
147     if (!size.isEmpty()) {
148         IntRect area(0, 0, size.width(), size.height());
149         m_webkitInvals.append(area);
150         m_pile.append(area);
151     }
152 }
153 
updatePicturesIfNeeded(PicturePainter * painter)154 void PicturePile::updatePicturesIfNeeded(PicturePainter* painter)
155 {
156     applyWebkitInvals();
157     for (size_t i = 0; i < m_pile.size(); i++) {
158         PictureContainer& pc = m_pile[i];
159         if (pc.dirty)
160             updatePicture(painter, pc);
161     }
162 }
163 
updatePicture(PicturePainter * painter,PictureContainer & pc)164 void PicturePile::updatePicture(PicturePainter* painter, PictureContainer& pc)
165 {
166     /* The ref counting here is a bit unusual. What happens is begin/end recording
167      * will ref/unref the recording canvas. However, 'canvas' might be pointing
168      * at an SkNWayCanvas instead of the recording canvas, which needs to be
169      * unref'd. Thus what we do is ref the recording canvas so that we can
170      * always unref whatever canvas we have at the end.
171      */
172     TRACE_METHOD();
173     SkPicture* picture = new SkPicture();
174     SkCanvas* canvas = picture->beginRecording(pc.area.width(), pc.area.height(),
175             SkPicture::kUsePathBoundsForClip_RecordingFlag);
176     SkSafeRef(canvas);
177     canvas->translate(-pc.area.x(), -pc.area.y());
178     IntRect drawArea = pc.area;
179     if (pc.prerendered.get()) {
180         SkCanvas* prerender = painter->createPrerenderCanvas(pc.prerendered.get());
181         if (!prerender) {
182             ALOGV("Failed to create prerendered for " INT_RECT_FORMAT,
183                     INT_RECT_ARGS(pc.prerendered->area));
184             pc.prerendered.clear();
185         } else {
186             drawArea.unite(pc.prerendered->area);
187             SkNWayCanvas* nwayCanvas = new SkNWayCanvas(drawArea.width(), drawArea.height());
188             nwayCanvas->translate(-drawArea.x(), -drawArea.y());
189             nwayCanvas->addCanvas(canvas);
190             nwayCanvas->addCanvas(prerender);
191             SkSafeUnref(canvas);
192             SkSafeUnref(prerender);
193             canvas = nwayCanvas;
194         }
195     }
196     WebCore::PlatformGraphicsContextSkia pgc(canvas);
197     WebCore::GraphicsContext gc(&pgc);
198     ALOGV("painting picture: " INT_RECT_FORMAT, INT_RECT_ARGS(drawArea));
199     painter->paintContents(&gc, drawArea);
200     SkSafeUnref(canvas);
201     picture->endRecording();
202 
203     SkSafeUnref(pc.picture);
204     pc.picture = picture;
205     pc.dirty = false;
206 }
207 
reset()208 void PicturePile::reset()
209 {
210     m_size = IntSize(0,0);
211     m_pile.clear();
212     m_webkitInvals.clear();
213 }
214 
applyWebkitInvals()215 void PicturePile::applyWebkitInvals()
216 {
217     m_dirtyRegion.setEmpty();
218     if (!m_webkitInvals.size())
219         return;
220     // Build the invals (TODO: Support multiple inval regions)
221     IntRect inval = m_webkitInvals[0];
222     m_dirtyRegion.setRect(toSkIRect(inval));
223     for (size_t i = 1; i < m_webkitInvals.size(); i++) {
224         inval.unite(m_webkitInvals[i]);
225         m_dirtyRegion.op(toSkIRect(m_webkitInvals[i]), SkRegion::kUnion_Op);
226     }
227     m_webkitInvals.clear();
228     ALOGV("Webkit inval: " INT_RECT_FORMAT, INT_RECT_ARGS(inval));
229     if (inval.isEmpty())
230         return;
231 
232     // Find the overlaps
233     Vector<int> overlaps;
234     for (size_t i = 0; i < m_pile.size(); i++) {
235         PictureContainer& pc = m_pile[i];
236         if (pc.area.contains(inval)) {
237             if (pc.dirty) {
238                 ALOGV("Found already dirty intersection");
239                 return;
240             }
241             if (pc.area == inval) {
242                 appendToPile(inval);
243                 return;
244             }
245             // Don't count the base surface as an overlap
246             if (pc.area.size() != m_size)
247                 overlaps.append(i);
248         } else if (pc.area.intersects(inval))
249             overlaps.append(i);
250     }
251 
252     if (overlaps.size() >= MAX_OVERLAP_COUNT) {
253         ALOGV("Exceeds overlap count");
254         IntRect overlap = inval;
255         for (int i = (int) overlaps.size() - 1; i >= 0; i--) {
256             overlap.unite(m_pile[overlaps[i]].area);
257             m_pile.remove(overlaps[i]);
258         }
259         float overlapArea = overlap.width() * overlap.height();
260         float totalArea = m_size.width() * m_size.height();
261         if (overlapArea / totalArea > MAX_OVERLAP_AREA)
262             overlap = IntRect(0, 0, m_size.width(), m_size.height());
263         appendToPile(overlap, inval);
264         return;
265     }
266 
267     // Append!
268     appendToPile(inval);
269 }
270 
appendToPile(const IntRect & inval,const IntRect & originalInval)271 void PicturePile::appendToPile(const IntRect& inval, const IntRect& originalInval)
272 {
273     ALOGV("Adding inval " INT_RECT_FORMAT " for original inval " INT_RECT_FORMAT,
274             INT_RECT_ARGS(inval), INT_RECT_ARGS(originalInval));
275     // Remove any entries this obscures
276     for (int i = (int) m_pile.size() - 1; i >= 0; i--) {
277         if (inval.contains(m_pile[i].area))
278             m_pile.remove(i);
279     }
280     PictureContainer container(inval);
281     if (ENABLE_PRERENDERED_INVALS) {
282         container.prerendered = PrerenderedInval::create(originalInval.isEmpty()
283                                                          ? inval : originalInval);
284     }
285     m_pile.append(container);
286 }
287 
prerenderedInvalForArea(const IntRect & area)288 PrerenderedInval* PicturePile::prerenderedInvalForArea(const IntRect& area)
289 {
290     for (int i = (int) m_pile.size() - 1; i >= 0; i--) {
291         if (m_pile[i].area.intersects(area)) {
292             RefPtr<PrerenderedInval> inval = m_pile[i].prerendered;
293             if (inval.get() && inval->area.contains(area))
294                 return inval.get();
295             return 0;
296         }
297     }
298     return 0;
299 }
300 
301 } // namespace WebCore
302