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 49 static SkIRect toSkIRect(const IntRect& rect) { 50 return SkIRect::MakeXYWH(rect.x(), rect.y(), rect.width(), rect.height()); 51 } 52 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 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 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 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 90 void PicturePile::clearPrerenders() 91 { 92 for (size_t i = 0; i < m_pile.size(); i++) 93 m_pile[i].prerendered.clear(); 94 } 95 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 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 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 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 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 208 void PicturePile::reset() 209 { 210 m_size = IntSize(0,0); 211 m_pile.clear(); 212 m_webkitInvals.clear(); 213 } 214 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 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 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