OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2014 Google Inc. | 2 * Copyright 2014 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkRecordDraw.h" | 8 #include "SkRecordDraw.h" |
9 #include "SkPatchUtils.h" | 9 #include "SkPatchUtils.h" |
10 | 10 |
(...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
109 DRAW(DrawRect, drawRect(r.rect, r.paint)); | 109 DRAW(DrawRect, drawRect(r.rect, r.paint)); |
110 DRAW(DrawSprite, drawSprite(shallow_copy(r.bitmap), r.left, r.top, r.paint)); | 110 DRAW(DrawSprite, drawSprite(shallow_copy(r.bitmap), r.left, r.top, r.paint)); |
111 DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); | 111 DRAW(DrawText, drawText(r.text, r.byteLength, r.x, r.y, r.paint)); |
112 DRAW(DrawTextBlob, drawTextBlob(r.blob, r.x, r.y, r.paint)); | 112 DRAW(DrawTextBlob, drawTextBlob(r.blob, r.x, r.y, r.paint)); |
113 DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.pa
int)); | 113 DRAW(DrawTextOnPath, drawTextOnPath(r.text, r.byteLength, r.path, r.matrix, r.pa
int)); |
114 DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.co
lors, | 114 DRAW(DrawVertices, drawVertices(r.vmode, r.vertexCount, r.vertices, r.texs, r.co
lors, |
115 r.xmode.get(), r.indices, r.indexCount, r.paint)
); | 115 r.xmode.get(), r.indices, r.indexCount, r.paint)
); |
116 DRAW(DrawData, drawData(r.data, r.length)); | 116 DRAW(DrawData, drawData(r.data, r.length)); |
117 #undef DRAW | 117 #undef DRAW |
118 | 118 |
119 | |
120 // This looks silly, I know. Why not just use SkRect::MakeLargest()? | |
121 // In practice, this is well large enough, and it has a few extra advantages: | |
122 // it fits in an SkIRect, and we can munge it a little in both SkRect and | |
123 // SKIRect space without worrying about overflow. | |
124 static const SkRect kUnbounded = { -2e9f, -2e9f, 2e9f, 2e9f }; | |
125 | |
126 | |
127 // This is an SkRecord visitor that fills an SkBBoxHierarchy. | 119 // This is an SkRecord visitor that fills an SkBBoxHierarchy. |
128 // | 120 // |
129 // The interesting part here is how to calculate bounds for ops which don't | 121 // The interesting part here is how to calculate bounds for ops which don't |
130 // have intrinsic bounds. What is the bounds of a Save or a Translate? | 122 // have intrinsic bounds. What is the bounds of a Save or a Translate? |
131 // | 123 // |
132 // We answer this by thinking about a particular definition of bounds: if I | 124 // We answer this by thinking about a particular definition of bounds: if I |
133 // don't execute this op, pixels in this rectangle might draw incorrectly. So | 125 // don't execute this op, pixels in this rectangle might draw incorrectly. So |
134 // the bounds of a Save, a Translate, a Restore, etc. are the union of the | 126 // the bounds of a Save, a Translate, a Restore, etc. are the union of the |
135 // bounds of Draw* ops that they might have an effect on. For any given | 127 // bounds of Draw* ops that they might have an effect on. For any given |
136 // Save/Restore block, the bounds of the Save, the Restore, and any other | 128 // Save/Restore block, the bounds of the Save, the Restore, and any other |
137 // non-drawing ("control") ops inside are exactly the union of the bounds of | 129 // non-drawing ("control") ops inside are exactly the union of the bounds of |
138 // the drawing ops inside that block. | 130 // the drawing ops inside that block. |
139 // | 131 // |
140 // To implement this, we keep a stack of active Save blocks. As we consume ops | 132 // To implement this, we keep a stack of active Save blocks. As we consume ops |
141 // inside the Save/Restore block, drawing ops are unioned with the bounds of | 133 // inside the Save/Restore block, drawing ops are unioned with the bounds of |
142 // the block, and control ops are stashed away for later. When we finish the | 134 // the block, and control ops are stashed away for later. When we finish the |
143 // block with a Restore, our bounds are complete, and we go back and fill them | 135 // block with a Restore, our bounds are complete, and we go back and fill them |
144 // in for all the control ops we stashed away. | 136 // in for all the control ops we stashed away. |
145 class FillBounds : SkNoncopyable { | 137 class FillBounds : SkNoncopyable { |
146 public: | 138 public: |
147 FillBounds(const SkRecord& record, SkBBoxHierarchy* bbh) : fBounds(record.co
unt()) { | 139 FillBounds(const SkRect& cullRect, const SkRecord& record, SkBBoxHierarchy*
bbh) |
| 140 : fCullRect(cullRect) |
| 141 , fBounds(record.count()) { |
148 // Calculate bounds for all ops. This won't go quite in order, so we'll
need | 142 // Calculate bounds for all ops. This won't go quite in order, so we'll
need |
149 // to store the bounds separately then feed them in to the BBH later in
order. | 143 // to store the bounds separately then feed them in to the BBH later in
order. |
150 fCTM = &SkMatrix::I(); | 144 fCTM = &SkMatrix::I(); |
151 fCurrentClipBounds = kUnbounded; | 145 fCurrentClipBounds = fCullRect; |
152 for (fCurrentOp = 0; fCurrentOp < record.count(); fCurrentOp++) { | 146 for (fCurrentOp = 0; fCurrentOp < record.count(); fCurrentOp++) { |
153 record.visit<void>(fCurrentOp, *this); | 147 record.visit<void>(fCurrentOp, *this); |
154 } | 148 } |
155 | 149 |
156 // If we have any lingering unpaired Saves, simulate restores to make | 150 // If we have any lingering unpaired Saves, simulate restores to make |
157 // sure all ops in those Save blocks have their bounds calculated. | 151 // sure all ops in those Save blocks have their bounds calculated. |
158 while (!fSaveStack.isEmpty()) { | 152 while (!fSaveStack.isEmpty()) { |
159 this->popSaveBlock(); | 153 this->popSaveBlock(); |
160 } | 154 } |
161 | 155 |
162 // Any control ops not part of any Save/Restore block draw everywhere. | 156 // Any control ops not part of any Save/Restore block draw everywhere. |
163 while (!fControlIndices.isEmpty()) { | 157 while (!fControlIndices.isEmpty()) { |
164 this->popControl(kUnbounded); | 158 this->popControl(fCullRect); |
165 } | 159 } |
166 | 160 |
167 // Finally feed all stored bounds into the BBH. They'll be returned in
this order. | 161 // Finally feed all stored bounds into the BBH. They'll be returned in
this order. |
168 SkASSERT(bbh); | 162 SkASSERT(bbh); |
169 bbh->insert(&fBounds, record.count()); | 163 bbh->insert(&fBounds, record.count()); |
170 } | 164 } |
171 | 165 |
172 template <typename T> void operator()(const T& op) { | 166 template <typename T> void operator()(const T& op) { |
173 this->updateCTM(op); | 167 this->updateCTM(op); |
174 this->updateClipBounds(op); | 168 this->updateClipBounds(op); |
(...skipping 22 matching lines...) Expand all Loading... |
197 void updateClipBounds(const ClipPath& op) { this->updateClipBoundsForClipO
p(op.devBounds); } | 191 void updateClipBounds(const ClipPath& op) { this->updateClipBoundsForClipO
p(op.devBounds); } |
198 void updateClipBounds(const ClipRRect& op) { this->updateClipBoundsForClipO
p(op.devBounds); } | 192 void updateClipBounds(const ClipRRect& op) { this->updateClipBoundsForClipO
p(op.devBounds); } |
199 void updateClipBounds(const ClipRect& op) { this->updateClipBoundsForClipO
p(op.devBounds); } | 193 void updateClipBounds(const ClipRect& op) { this->updateClipBoundsForClipO
p(op.devBounds); } |
200 void updateClipBounds(const ClipRegion& op) { this->updateClipBoundsForClipO
p(op.devBounds); } | 194 void updateClipBounds(const ClipRegion& op) { this->updateClipBoundsForClipO
p(op.devBounds); } |
201 | 195 |
202 // The bounds of clip ops need to be adjusted for the paints of saveLayers t
hey're inside. | 196 // The bounds of clip ops need to be adjusted for the paints of saveLayers t
hey're inside. |
203 void updateClipBoundsForClipOp(const SkIRect& devBounds) { | 197 void updateClipBoundsForClipOp(const SkIRect& devBounds) { |
204 Bounds clip = SkRect::Make(devBounds); | 198 Bounds clip = SkRect::Make(devBounds); |
205 // We don't call adjustAndMap() because as its last step it would inters
ect the adjusted | 199 // We don't call adjustAndMap() because as its last step it would inters
ect the adjusted |
206 // clip bounds with the previous clip, exactly what we can't do when the
clip grows. | 200 // clip bounds with the previous clip, exactly what we can't do when the
clip grows. |
207 fCurrentClipBounds = this->adjustForSaveLayerPaints(&clip) ? clip : kUnb
ounded; | 201 fCurrentClipBounds = this->adjustForSaveLayerPaints(&clip) ? clip : fCul
lRect; |
208 } | 202 } |
209 | 203 |
210 // Restore holds the devBounds for the clip after the {save,saveLayer}/resto
re block completes. | 204 // Restore holds the devBounds for the clip after the {save,saveLayer}/resto
re block completes. |
211 void updateClipBounds(const Restore& op) { | 205 void updateClipBounds(const Restore& op) { |
212 // This is just like the clip ops above, but we need to skip the effects
(if any) of our | 206 // This is just like the clip ops above, but we need to skip the effects
(if any) of our |
213 // paired saveLayer (if it is one); it has not yet been popped off the s
ave stack. Our | 207 // paired saveLayer (if it is one); it has not yet been popped off the s
ave stack. Our |
214 // devBounds reflect the state of the world after the saveLayer/restore
block is done, | 208 // devBounds reflect the state of the world after the saveLayer/restore
block is done, |
215 // so they are not affected by the saveLayer's paint. | 209 // so they are not affected by the saveLayer's paint. |
216 const int kSavesToIgnore = 1; | 210 const int kSavesToIgnore = 1; |
217 Bounds clip = SkRect::Make(op.devBounds); | 211 Bounds clip = SkRect::Make(op.devBounds); |
218 fCurrentClipBounds = | 212 fCurrentClipBounds = |
219 this->adjustForSaveLayerPaints(&clip, kSavesToIgnore) ? clip : kUnbo
unded; | 213 this->adjustForSaveLayerPaints(&clip, kSavesToIgnore) ? clip : fCull
Rect; |
220 } | 214 } |
221 | 215 |
222 // We also take advantage of SaveLayer bounds when present to further cut th
e clip down. | 216 // We also take advantage of SaveLayer bounds when present to further cut th
e clip down. |
223 void updateClipBounds(const SaveLayer& op) { | 217 void updateClipBounds(const SaveLayer& op) { |
224 if (op.bounds) { | 218 if (op.bounds) { |
225 // adjustAndMap() intersects these layer bounds with the previous cl
ip for us. | 219 // adjustAndMap() intersects these layer bounds with the previous cl
ip for us. |
226 fCurrentClipBounds = this->adjustAndMap(*op.bounds, op.paint); | 220 fCurrentClipBounds = this->adjustAndMap(*op.bounds, op.paint); |
227 } | 221 } |
228 } | 222 } |
229 | 223 |
(...skipping 16 matching lines...) Expand all Loading... |
246 void trackBounds(const DrawData&) { this->pushControl(); } | 240 void trackBounds(const DrawData&) { this->pushControl(); } |
247 | 241 |
248 // For all other ops, we can calculate and store the bounds directly now. | 242 // For all other ops, we can calculate and store the bounds directly now. |
249 template <typename T> void trackBounds(const T& op) { | 243 template <typename T> void trackBounds(const T& op) { |
250 fBounds[fCurrentOp] = this->bounds(op); | 244 fBounds[fCurrentOp] = this->bounds(op); |
251 this->updateSaveBounds(fBounds[fCurrentOp]); | 245 this->updateSaveBounds(fBounds[fCurrentOp]); |
252 } | 246 } |
253 | 247 |
254 void pushSaveBlock(const SkPaint* paint) { | 248 void pushSaveBlock(const SkPaint* paint) { |
255 // Starting a new Save block. Push a new entry to represent that. | 249 // Starting a new Save block. Push a new entry to represent that. |
256 SaveBounds sb = { 0, Bounds::MakeEmpty(), paint }; | 250 SaveBounds sb; |
| 251 sb.controlOps = 0; |
| 252 // If the paint affects transparent black, the bound shouldn't be smalle
r |
| 253 // than the current clip bounds. |
| 254 sb.bounds = |
| 255 PaintMayAffectTransparentBlack(paint) ? fCurrentClipBounds : Bounds:
:MakeEmpty(); |
| 256 sb.paint = paint; |
| 257 |
257 fSaveStack.push(sb); | 258 fSaveStack.push(sb); |
258 this->pushControl(); | 259 this->pushControl(); |
259 } | 260 } |
260 | 261 |
261 static bool PaintMayAffectTransparentBlack(const SkPaint* paint) { | 262 static bool PaintMayAffectTransparentBlack(const SkPaint* paint) { |
262 if (paint) { | 263 if (paint) { |
263 // FIXME: this is very conservative | 264 // FIXME: this is very conservative |
264 if (paint->getImageFilter() || paint->getColorFilter()) { | 265 if (paint->getImageFilter() || paint->getColorFilter()) { |
265 return true; | 266 return true; |
266 } | 267 } |
(...skipping 29 matching lines...) Expand all Loading... |
296 } | 297 } |
297 } | 298 } |
298 return false; | 299 return false; |
299 } | 300 } |
300 | 301 |
301 Bounds popSaveBlock() { | 302 Bounds popSaveBlock() { |
302 // We're done the Save block. Apply the block's bounds to all control o
ps inside it. | 303 // We're done the Save block. Apply the block's bounds to all control o
ps inside it. |
303 SaveBounds sb; | 304 SaveBounds sb; |
304 fSaveStack.pop(&sb); | 305 fSaveStack.pop(&sb); |
305 | 306 |
306 // If the paint affects transparent black, we can't trust any of our cal
culated bounds. | |
307 const Bounds& bounds = | |
308 PaintMayAffectTransparentBlack(sb.paint) ? fCurrentClipBounds : sb.b
ounds; | |
309 | |
310 while (sb.controlOps --> 0) { | 307 while (sb.controlOps --> 0) { |
311 this->popControl(bounds); | 308 this->popControl(sb.bounds); |
312 } | 309 } |
313 | 310 |
314 // This whole Save block may be part another Save block. | 311 // This whole Save block may be part another Save block. |
315 this->updateSaveBounds(bounds); | 312 this->updateSaveBounds(sb.bounds); |
316 | 313 |
317 // If called from a real Restore (not a phony one for balance), it'll ne
ed the bounds. | 314 // If called from a real Restore (not a phony one for balance), it'll ne
ed the bounds. |
318 return bounds; | 315 return sb.bounds; |
319 } | 316 } |
320 | 317 |
321 void pushControl() { | 318 void pushControl() { |
322 fControlIndices.push(fCurrentOp); | 319 fControlIndices.push(fCurrentOp); |
323 if (!fSaveStack.isEmpty()) { | 320 if (!fSaveStack.isEmpty()) { |
324 fSaveStack.top().controlOps++; | 321 fSaveStack.top().controlOps++; |
325 } | 322 } |
326 } | 323 } |
327 | 324 |
328 void popControl(const Bounds& bounds) { | 325 void popControl(const Bounds& bounds) { |
329 fBounds[fControlIndices.top()] = bounds; | 326 fBounds[fControlIndices.top()] = bounds; |
330 fControlIndices.pop(); | 327 fControlIndices.pop(); |
331 } | 328 } |
332 | 329 |
333 void updateSaveBounds(const Bounds& bounds) { | 330 void updateSaveBounds(const Bounds& bounds) { |
334 // If we're in a Save block, expand its bounds to cover these bounds too
. | 331 // If we're in a Save block, expand its bounds to cover these bounds too
. |
335 if (!fSaveStack.isEmpty()) { | 332 if (!fSaveStack.isEmpty()) { |
336 fSaveStack.top().bounds.join(bounds); | 333 fSaveStack.top().bounds.join(bounds); |
337 } | 334 } |
338 } | 335 } |
339 | 336 |
340 // FIXME: this method could use better bounds | 337 // FIXME: this method could use better bounds |
341 Bounds bounds(const DrawText&) const { return fCurrentClipBounds; } | 338 Bounds bounds(const DrawText&) const { return fCurrentClipBounds; } |
342 | 339 |
343 Bounds bounds(const Clear&) const { return kUnbounded; } // Igno
res the clip. | 340 Bounds bounds(const Clear&) const { return fCullRect; } // Ignor
es the clip. |
344 Bounds bounds(const DrawPaint&) const { return fCurrentClipBounds; } | 341 Bounds bounds(const DrawPaint&) const { return fCurrentClipBounds; } |
345 Bounds bounds(const NoOp&) const { return Bounds::MakeEmpty(); } // NoOp
s don't draw. | 342 Bounds bounds(const NoOp&) const { return Bounds::MakeEmpty(); } // NoOp
s don't draw. |
346 | 343 |
347 Bounds bounds(const DrawSprite& op) const { | 344 Bounds bounds(const DrawSprite& op) const { |
348 const SkBitmap& bm = op.bitmap; | 345 const SkBitmap& bm = op.bitmap; |
349 return Bounds::MakeXYWH(op.left, op.top, bm.width(), bm.height()); // I
gnores the matrix. | 346 return Bounds::MakeXYWH(op.left, op.top, bm.width(), bm.height()); // I
gnores the matrix. |
350 } | 347 } |
351 | 348 |
352 Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect,
&op.paint); } | 349 Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect,
&op.paint); } |
353 Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval,
&op.paint); } | 350 Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval,
&op.paint); } |
(...skipping 176 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
530 | 527 |
531 // Map the rect back to identity space. | 528 // Map the rect back to identity space. |
532 fCTM->mapRect(&rect); | 529 fCTM->mapRect(&rect); |
533 | 530 |
534 // Nothing can draw outside the current clip. | 531 // Nothing can draw outside the current clip. |
535 // (Only bounded ops call into this method, so oddballs like Clear don't
matter here.) | 532 // (Only bounded ops call into this method, so oddballs like Clear don't
matter here.) |
536 rect.intersect(fCurrentClipBounds); | 533 rect.intersect(fCurrentClipBounds); |
537 return rect; | 534 return rect; |
538 } | 535 } |
539 | 536 |
| 537 // We do not guarantee anything for operations outside of the cull rect |
| 538 const SkRect fCullRect; |
| 539 |
540 // Conservative identity-space bounds for each op in the SkRecord. | 540 // Conservative identity-space bounds for each op in the SkRecord. |
541 SkAutoTMalloc<Bounds> fBounds; | 541 SkAutoTMalloc<Bounds> fBounds; |
542 | 542 |
543 // We walk fCurrentOp through the SkRecord, as we go using updateCTM() | 543 // We walk fCurrentOp through the SkRecord, as we go using updateCTM() |
544 // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative | 544 // and updateClipBounds() to maintain the exact CTM (fCTM) and conservative |
545 // identity-space bounds of the current clip (fCurrentClipBounds). | 545 // identity-space bounds of the current clip (fCurrentClipBounds). |
546 unsigned fCurrentOp; | 546 unsigned fCurrentOp; |
547 const SkMatrix* fCTM; | 547 const SkMatrix* fCTM; |
548 Bounds fCurrentClipBounds; | 548 Bounds fCurrentClipBounds; |
549 | 549 |
550 // Used to track the bounds of Save/Restore blocks and the control ops insid
e them. | 550 // Used to track the bounds of Save/Restore blocks and the control ops insid
e them. |
551 SkTDArray<SaveBounds> fSaveStack; | 551 SkTDArray<SaveBounds> fSaveStack; |
552 SkTDArray<unsigned> fControlIndices; | 552 SkTDArray<unsigned> fControlIndices; |
553 }; | 553 }; |
554 | 554 |
555 } // namespace SkRecords | 555 } // namespace SkRecords |
556 | 556 |
557 void SkRecordFillBounds(const SkRecord& record, SkBBoxHierarchy* bbh) { | 557 void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record, SkBBoxHi
erarchy* bbh) { |
558 SkRecords::FillBounds(record, bbh); | 558 SkRecords::FillBounds(cullRect, record, bbh); |
559 } | 559 } |
OLD | NEW |