1 /*
2 * Copyright 2014 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkBBHFactory.h"
9 #include "include/core/SkImage.h"
10 #include "include/private/base/SkTDArray.h"
11 #include "src/core/SkCanvasPriv.h"
12 #include "src/core/SkColorFilterBase.h"
13 #include "src/core/SkImageFilter_Base.h"
14 #include "src/core/SkRecordDraw.h"
15 #include "src/utils/SkPatchUtils.h"
16
SkRecordDraw(const SkRecord & record,SkCanvas * canvas,SkPicture const * const drawablePicts[],SkDrawable * const drawables[],int drawableCount,const SkBBoxHierarchy * bbh,SkPicture::AbortCallback * callback)17 void SkRecordDraw(const SkRecord& record,
18 SkCanvas* canvas,
19 SkPicture const* const drawablePicts[],
20 SkDrawable* const drawables[],
21 int drawableCount,
22 const SkBBoxHierarchy* bbh,
23 SkPicture::AbortCallback* callback) {
24 SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
25
26 if (bbh) {
27 // Draw only ops that affect pixels in the canvas's current clip.
28 // The SkRecord and BBH were recorded in identity space. This canvas
29 // is not necessarily in that same space. getLocalClipBounds() returns us
30 // this canvas' clip bounds transformed back into identity space, which
31 // lets us query the BBH.
32 SkRect query = canvas->getLocalClipBounds();
33
34 std::vector<int> ops;
35 bbh->search(query, &ops);
36
37 SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount);
38 for (int i = 0; i < (int)ops.size(); i++) {
39 if (callback && callback->abort()) {
40 return;
41 }
42 // This visit call uses the SkRecords::Draw::operator() to call
43 // methods on the |canvas|, wrapped by methods defined with the
44 // DRAW() macro.
45 record.visit(ops[i], draw);
46 }
47 } else {
48 // Draw all ops.
49 SkRecords::Draw draw(canvas, drawablePicts, drawables, drawableCount);
50 for (int i = 0; i < record.count(); i++) {
51 if (callback && callback->abort()) {
52 return;
53 }
54 // This visit call uses the SkRecords::Draw::operator() to call
55 // methods on the |canvas|, wrapped by methods defined with the
56 // DRAW() macro.
57 record.visit(i, draw);
58 }
59 }
60 }
61
SkRecordPartialDraw(const SkRecord & record,SkCanvas * canvas,SkPicture const * const drawablePicts[],int drawableCount,int start,int stop,const SkM44 & initialCTM)62 void SkRecordPartialDraw(const SkRecord& record, SkCanvas* canvas,
63 SkPicture const* const drawablePicts[], int drawableCount,
64 int start, int stop,
65 const SkM44& initialCTM) {
66 SkAutoCanvasRestore saveRestore(canvas, true /*save now, restore at exit*/);
67
68 stop = std::min(stop, record.count());
69 SkRecords::Draw draw(canvas, drawablePicts, nullptr, drawableCount, &initialCTM);
70 for (int i = start; i < stop; i++) {
71 record.visit(i, draw);
72 }
73 }
74
75 namespace SkRecords {
76
77 // NoOps draw nothing.
draw(const NoOp &)78 template <> void Draw::draw(const NoOp&) {}
79
80 #define DRAW(T, call) template <> void Draw::draw(const T& r) { fCanvas->call; }
DRAW(Flush,flush ())81 DRAW(Flush, flush())
82 DRAW(Restore, restore())
83 DRAW(Save, save())
84 DRAW(SaveLayer, saveLayer(SkCanvasPriv::ScaledBackdropLayer(r.bounds,
85 r.paint,
86 r.backdrop.get(),
87 r.backdropScale,
88 r.saveLayerFlags)))
89
90 template <> void Draw::draw(const SaveBehind& r) {
91 SkCanvasPriv::SaveBehind(fCanvas, r.subset);
92 }
93
draw(const DrawBehind & r)94 template <> void Draw::draw(const DrawBehind& r) {
95 SkCanvasPriv::DrawBehind(fCanvas, r.paint);
96 }
97
98 DRAW(SetMatrix, setMatrix(fInitialCTM.asM33() * r.matrix))
99 DRAW(SetM44, setMatrix(fInitialCTM * r.matrix))
100 DRAW(Concat44, concat(r.matrix))
101 DRAW(Concat, concat(r.matrix))
102 DRAW(Translate, translate(r.dx, r.dy))
103 DRAW(Scale, scale(r.sx, r.sy))
104
105 DRAW(ClipPath, clipPath(r.path, r.opAA.op(), r.opAA.aa()))
106 DRAW(ClipRRect, clipRRect(r.rrect, r.opAA.op(), r.opAA.aa()))
107 DRAW(ClipRect, clipRect(r.rect, r.opAA.op(), r.opAA.aa()))
108 DRAW(ClipRegion, clipRegion(r.region, r.op))
109 DRAW(ClipShader, clipShader(r.shader, r.op))
110
draw(const ResetClip & r)111 template <> void Draw::draw(const ResetClip& r) {
112 SkCanvasPriv::ResetClip(fCanvas);
113 }
114
115 DRAW(DrawArc, drawArc(r.oval, r.startAngle, r.sweepAngle, r.useCenter, r.paint))
116 DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint))
117 DRAW(DrawImage, drawImage(r.image.get(), r.left, r.top, r.sampling, r.paint))
118
draw(const DrawImageLattice & r)119 template <> void Draw::draw(const DrawImageLattice& r) {
120 SkCanvas::Lattice lattice;
121 lattice.fXCount = r.xCount;
122 lattice.fXDivs = r.xDivs;
123 lattice.fYCount = r.yCount;
124 lattice.fYDivs = r.yDivs;
125 lattice.fRectTypes = (0 == r.flagCount) ? nullptr : r.flags;
126 lattice.fColors = (0 == r.flagCount) ? nullptr : r.colors;
127 lattice.fBounds = &r.src;
128 fCanvas->drawImageLattice(r.image.get(), lattice, r.dst, r.filter, r.paint);
129 }
130
131 DRAW(DrawImageRect, drawImageRect(r.image.get(), r.src, r.dst, r.sampling, r.paint, r.constraint))
132 DRAW(DrawOval, drawOval(r.oval, r.paint))
133 DRAW(DrawPaint, drawPaint(r.paint))
134 DRAW(DrawPath, drawPath(r.path, r.paint))
135 DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.bmode, r.paint))
136 DRAW(DrawPicture, drawPicture(r.picture.get(), &r.matrix, r.paint))
137 DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint))
138 DRAW(DrawRRect, drawRRect(r.rrect, r.paint))
139 DRAW(DrawRect, drawRect(r.rect, r.paint))
140 DRAW(DrawRegion, drawRegion(r.region, r.paint))
141 DRAW(DrawTextBlob, drawTextBlob(r.blob.get(), r.x, r.y, r.paint))
142 #if defined(SK_GANESH)
143 DRAW(DrawSlug, drawSlug(r.slug.get()))
144 #else
145 // Turn draw into a nop.
146 template <> void Draw::draw(const DrawSlug&) {}
147 #endif
148 DRAW(DrawAtlas, drawAtlas(r.atlas.get(), r.xforms, r.texs, r.colors, r.count, r.mode, r.sampling,
149 r.cull, r.paint))
150 DRAW(DrawVertices, drawVertices(r.vertices, r.bmode, r.paint))
151 #ifdef SK_ENABLE_SKSL
152 DRAW(DrawMesh, drawMesh(r.mesh, r.blender, r.paint))
153 #else
154 // Turn draw into a nop.
155 template <> void Draw::draw(const DrawMesh&) {}
156 #endif
157 DRAW(DrawShadowRec, private_draw_shadow_rec(r.path, r.rec))
158 DRAW(DrawAnnotation, drawAnnotation(r.rect, r.key.c_str(), r.value.get()))
159
160 DRAW(DrawEdgeAAQuad, experimental_DrawEdgeAAQuad(
161 r.rect, r.clip, r.aa, r.color, r.mode))
162 DRAW(DrawEdgeAAImageSet, experimental_DrawEdgeAAImageSet(
163 r.set.get(), r.count, r.dstClips, r.preViewMatrices, r.sampling, r.paint, r.constraint))
164
165 #undef DRAW
166
draw(const DrawDrawable & r)167 template <> void Draw::draw(const DrawDrawable& r) {
168 SkASSERT(r.index >= 0);
169 SkASSERT(r.index < fDrawableCount);
170 if (fDrawables) {
171 SkASSERT(nullptr == fDrawablePicts);
172 fCanvas->drawDrawable(fDrawables[r.index], r.matrix);
173 } else {
174 fCanvas->drawPicture(fDrawablePicts[r.index], r.matrix, nullptr);
175 }
176 }
177
178 // This is an SkRecord visitor that fills an SkBBoxHierarchy.
179 //
180 // The interesting part here is how to calculate bounds for ops which don't
181 // have intrinsic bounds. What is the bounds of a Save or a Translate?
182 //
183 // We answer this by thinking about a particular definition of bounds: if I
184 // don't execute this op, pixels in this rectangle might draw incorrectly. So
185 // the bounds of a Save, a Translate, a Restore, etc. are the union of the
186 // bounds of Draw* ops that they might have an effect on. For any given
187 // Save/Restore block, the bounds of the Save, the Restore, and any other
188 // non-drawing ("control") ops inside are exactly the union of the bounds of
189 // the drawing ops inside that block.
190 //
191 // To implement this, we keep a stack of active Save blocks. As we consume ops
192 // inside the Save/Restore block, drawing ops are unioned with the bounds of
193 // the block, and control ops are stashed away for later. When we finish the
194 // block with a Restore, our bounds are complete, and we go back and fill them
195 // in for all the control ops we stashed away.
196 class FillBounds : SkNoncopyable {
197 public:
FillBounds(const SkRect & cullRect,const SkRecord & record,SkRect bounds[],SkBBoxHierarchy::Metadata meta[])198 FillBounds(const SkRect& cullRect, const SkRecord& record,
199 SkRect bounds[], SkBBoxHierarchy::Metadata meta[])
200 : fCullRect(cullRect)
201 , fBounds(bounds)
202 , fMeta(meta) {
203 fCTM = SkMatrix::I();
204
205 // We push an extra save block to track the bounds of any top-level control operations.
206 fSaveStack.push_back({ 0, Bounds::MakeEmpty(), nullptr, fCTM });
207 }
208
~FillBounds()209 ~FillBounds() {
210 // If we have any lingering unpaired Saves, simulate restores to make
211 // sure all ops in those Save blocks have their bounds calculated.
212 while (!fSaveStack.empty()) {
213 this->popSaveBlock();
214 }
215
216 // Any control ops not part of any Save/Restore block draw everywhere.
217 while (!fControlIndices.empty()) {
218 this->popControl(fCullRect);
219 }
220 }
221
setCurrentOp(int currentOp)222 void setCurrentOp(int currentOp) { fCurrentOp = currentOp; }
223
224
operator ()(const T & op)225 template <typename T> void operator()(const T& op) {
226 this->updateCTM(op);
227 this->trackBounds(op);
228 }
229
230 // In this file, SkRect are in local coordinates, Bounds are translated back to identity space.
231 typedef SkRect Bounds;
232
233 // Adjust rect for all paints that may affect its geometry, then map it to identity space.
adjustAndMap(SkRect rect,const SkPaint * paint) const234 Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const {
235 // Inverted rectangles really confuse our BBHs.
236 rect.sort();
237
238 // Adjust the rect for its own paint.
239 if (!AdjustForPaint(paint, &rect)) {
240 // The paint could do anything to our bounds. The only safe answer is the cull.
241 return fCullRect;
242 }
243
244 // Adjust rect for all the paints from the SaveLayers we're inside.
245 if (!this->adjustForSaveLayerPaints(&rect)) {
246 // Same deal as above.
247 return fCullRect;
248 }
249
250 // Map the rect back to identity space.
251 fCTM.mapRect(&rect);
252
253 // Nothing can draw outside the cull rect.
254 if (!rect.intersect(fCullRect)) {
255 return Bounds::MakeEmpty();
256 }
257
258 return rect;
259 }
260
261 private:
262 struct SaveBounds {
263 int controlOps; // Number of control ops in this Save block, including the Save.
264 Bounds bounds; // Bounds of everything in the block.
265 const SkPaint* paint; // Unowned. If set, adjusts the bounds of all ops in this block.
266 SkMatrix ctm;
267 };
268
269 // Only Restore, SetMatrix, Concat, and Translate change the CTM.
updateCTM(const T &)270 template <typename T> void updateCTM(const T&) {}
updateCTM(const Restore & op)271 void updateCTM(const Restore& op) { fCTM = op.matrix; }
updateCTM(const SetMatrix & op)272 void updateCTM(const SetMatrix& op) { fCTM = op.matrix; }
updateCTM(const SetM44 & op)273 void updateCTM(const SetM44& op) { fCTM = op.matrix.asM33(); }
updateCTM(const Concat44 & op)274 void updateCTM(const Concat44& op) { fCTM.preConcat(op.matrix.asM33()); }
updateCTM(const Concat & op)275 void updateCTM(const Concat& op) { fCTM.preConcat(op.matrix); }
updateCTM(const Scale & op)276 void updateCTM(const Scale& op) { fCTM.preScale(op.sx, op.sy); }
updateCTM(const Translate & op)277 void updateCTM(const Translate& op) { fCTM.preTranslate(op.dx, op.dy); }
278
279 // The bounds of these ops must be calculated when we hit the Restore
280 // from the bounds of the ops in the same Save block.
trackBounds(const Save &)281 void trackBounds(const Save&) { this->pushSaveBlock(nullptr); }
trackBounds(const SaveLayer & op)282 void trackBounds(const SaveLayer& op) { this->pushSaveBlock(op.paint); }
trackBounds(const SaveBehind &)283 void trackBounds(const SaveBehind&) { this->pushSaveBlock(nullptr); }
trackBounds(const Restore &)284 void trackBounds(const Restore&) {
285 const bool isSaveLayer = fSaveStack.back().paint != nullptr;
286 fBounds[fCurrentOp] = this->popSaveBlock();
287 fMeta [fCurrentOp].isDraw = isSaveLayer;
288 }
289
trackBounds(const SetMatrix &)290 void trackBounds(const SetMatrix&) { this->pushControl(); }
trackBounds(const SetM44 &)291 void trackBounds(const SetM44&) { this->pushControl(); }
trackBounds(const Concat &)292 void trackBounds(const Concat&) { this->pushControl(); }
trackBounds(const Concat44 &)293 void trackBounds(const Concat44&) { this->pushControl(); }
trackBounds(const Scale &)294 void trackBounds(const Scale&) { this->pushControl(); }
trackBounds(const Translate &)295 void trackBounds(const Translate&) { this->pushControl(); }
trackBounds(const ClipRect &)296 void trackBounds(const ClipRect&) { this->pushControl(); }
trackBounds(const ClipRRect &)297 void trackBounds(const ClipRRect&) { this->pushControl(); }
trackBounds(const ClipPath &)298 void trackBounds(const ClipPath&) { this->pushControl(); }
trackBounds(const ClipRegion &)299 void trackBounds(const ClipRegion&) { this->pushControl(); }
trackBounds(const ClipShader &)300 void trackBounds(const ClipShader&) { this->pushControl(); }
trackBounds(const ResetClip &)301 void trackBounds(const ResetClip&) { this->pushControl(); }
302
303
304 // For all other ops, we can calculate and store the bounds directly now.
trackBounds(const T & op)305 template <typename T> void trackBounds(const T& op) {
306 fBounds[fCurrentOp] = this->bounds(op);
307 fMeta [fCurrentOp].isDraw = true;
308 this->updateSaveBounds(fBounds[fCurrentOp]);
309 }
310
pushSaveBlock(const SkPaint * paint)311 void pushSaveBlock(const SkPaint* paint) {
312 // Starting a new Save block. Push a new entry to represent that.
313 SaveBounds sb;
314 sb.controlOps = 0;
315 // If the paint affects transparent black,
316 // the bound shouldn't be smaller than the cull.
317 sb.bounds =
318 PaintMayAffectTransparentBlack(paint) ? fCullRect : Bounds::MakeEmpty();
319 sb.paint = paint;
320 sb.ctm = this->fCTM;
321
322 fSaveStack.push_back(sb);
323 this->pushControl();
324 }
325
PaintMayAffectTransparentBlack(const SkPaint * paint)326 static bool PaintMayAffectTransparentBlack(const SkPaint* paint) {
327 if (paint) {
328 // FIXME: this is very conservative
329 if ((paint->getImageFilter() &&
330 as_IFB(paint->getImageFilter())->affectsTransparentBlack()) ||
331 (paint->getColorFilter() &&
332 as_CFB(paint->getColorFilter())->affectsTransparentBlack())) {
333 return true;
334 }
335 const auto bm = paint->asBlendMode();
336 if (!bm) {
337 return true; // can we query other blenders for this?
338 }
339
340 // Unusual blendmodes require us to process a saved layer
341 // even with operations outisde the clip.
342 // For example, DstIn is used by masking layers.
343 // https://code.google.com/p/skia/issues/detail?id=1291
344 // https://crbug.com/401593
345 switch (bm.value()) {
346 // For each of the following transfer modes, if the source
347 // alpha is zero (our transparent black), the resulting
348 // blended alpha is not necessarily equal to the original
349 // destination alpha.
350 case SkBlendMode::kClear:
351 case SkBlendMode::kSrc:
352 case SkBlendMode::kSrcIn:
353 case SkBlendMode::kDstIn:
354 case SkBlendMode::kSrcOut:
355 case SkBlendMode::kDstATop:
356 case SkBlendMode::kModulate:
357 return true;
358 default:
359 break;
360 }
361 }
362 return false;
363 }
364
popSaveBlock()365 Bounds popSaveBlock() {
366 // We're done the Save block. Apply the block's bounds to all control ops inside it.
367 SaveBounds sb = fSaveStack.back();
368 fSaveStack.pop_back();
369
370 while (sb.controlOps --> 0) {
371 this->popControl(sb.bounds);
372 }
373
374 // This whole Save block may be part another Save block.
375 this->updateSaveBounds(sb.bounds);
376
377 // If called from a real Restore (not a phony one for balance), it'll need the bounds.
378 return sb.bounds;
379 }
380
pushControl()381 void pushControl() {
382 fControlIndices.push_back(fCurrentOp);
383 if (!fSaveStack.empty()) {
384 fSaveStack.back().controlOps++;
385 }
386 }
387
popControl(const Bounds & bounds)388 void popControl(const Bounds& bounds) {
389 fBounds[fControlIndices.back()] = bounds;
390 fMeta [fControlIndices.back()].isDraw = false;
391 fControlIndices.pop_back();
392 }
393
updateSaveBounds(const Bounds & bounds)394 void updateSaveBounds(const Bounds& bounds) {
395 // If we're in a Save block, expand its bounds to cover these bounds too.
396 if (!fSaveStack.empty()) {
397 fSaveStack.back().bounds.join(bounds);
398 }
399 }
400
bounds(const Flush &) const401 Bounds bounds(const Flush&) const { return fCullRect; }
402
bounds(const DrawPaint &) const403 Bounds bounds(const DrawPaint&) const { return fCullRect; }
bounds(const DrawBehind &) const404 Bounds bounds(const DrawBehind&) const { return fCullRect; }
bounds(const NoOp &) const405 Bounds bounds(const NoOp&) const { return Bounds::MakeEmpty(); } // NoOps don't draw.
406
bounds(const DrawRect & op) const407 Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); }
bounds(const DrawRegion & op) const408 Bounds bounds(const DrawRegion& op) const {
409 SkRect rect = SkRect::Make(op.region.getBounds());
410 return this->adjustAndMap(rect, &op.paint);
411 }
bounds(const DrawOval & op) const412 Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); }
413 // Tighter arc bounds?
bounds(const DrawArc & op) const414 Bounds bounds(const DrawArc& op) const { return this->adjustAndMap(op.oval, &op.paint); }
bounds(const DrawRRect & op) const415 Bounds bounds(const DrawRRect& op) const {
416 return this->adjustAndMap(op.rrect.rect(), &op.paint);
417 }
bounds(const DrawDRRect & op) const418 Bounds bounds(const DrawDRRect& op) const {
419 return this->adjustAndMap(op.outer.rect(), &op.paint);
420 }
bounds(const DrawImage & op) const421 Bounds bounds(const DrawImage& op) const {
422 const SkImage* image = op.image.get();
423 SkRect rect = SkRect::MakeXYWH(op.left, op.top, image->width(), image->height());
424
425 return this->adjustAndMap(rect, op.paint);
426 }
bounds(const DrawImageLattice & op) const427 Bounds bounds(const DrawImageLattice& op) const {
428 return this->adjustAndMap(op.dst, op.paint);
429 }
bounds(const DrawImageRect & op) const430 Bounds bounds(const DrawImageRect& op) const {
431 return this->adjustAndMap(op.dst, op.paint);
432 }
bounds(const DrawPath & op) const433 Bounds bounds(const DrawPath& op) const {
434 return op.path.isInverseFillType() ? fCullRect
435 : this->adjustAndMap(op.path.getBounds(), &op.paint);
436 }
bounds(const DrawPoints & op) const437 Bounds bounds(const DrawPoints& op) const {
438 SkRect dst;
439 dst.setBounds(op.pts, op.count);
440
441 // Pad the bounding box a little to make sure hairline points' bounds aren't empty.
442 SkScalar stroke = std::max(op.paint.getStrokeWidth(), 0.01f);
443 dst.outset(stroke/2, stroke/2);
444
445 return this->adjustAndMap(dst, &op.paint);
446 }
bounds(const DrawPatch & op) const447 Bounds bounds(const DrawPatch& op) const {
448 SkRect dst;
449 dst.setBounds(op.cubics, SkPatchUtils::kNumCtrlPts);
450 return this->adjustAndMap(dst, &op.paint);
451 }
bounds(const DrawVertices & op) const452 Bounds bounds(const DrawVertices& op) const {
453 return this->adjustAndMap(op.vertices->bounds(), &op.paint);
454 }
bounds(const DrawMesh & op) const455 Bounds bounds(const DrawMesh& op) const {
456 #ifdef SK_ENABLE_SKSL
457 return this->adjustAndMap(op.mesh.bounds(), &op.paint);
458 #else
459 return SkRect::MakeEmpty();
460 #endif
461 }
bounds(const DrawAtlas & op) const462 Bounds bounds(const DrawAtlas& op) const {
463 if (op.cull) {
464 // TODO: <reed> can we pass nullptr for the paint? Isn't cull already "correct"
465 // for the paint (by the caller)?
466 return this->adjustAndMap(*op.cull, op.paint);
467 } else {
468 return fCullRect;
469 }
470 }
471
bounds(const DrawShadowRec & op) const472 Bounds bounds(const DrawShadowRec& op) const {
473 SkRect bounds;
474 SkDrawShadowMetrics::GetLocalBounds(op.path, op.rec, fCTM, &bounds);
475 return this->adjustAndMap(bounds, nullptr);
476 }
477
bounds(const DrawPicture & op) const478 Bounds bounds(const DrawPicture& op) const {
479 SkRect dst = op.picture->cullRect();
480 op.matrix.mapRect(&dst);
481 return this->adjustAndMap(dst, op.paint);
482 }
483
bounds(const DrawTextBlob & op) const484 Bounds bounds(const DrawTextBlob& op) const {
485 SkRect dst = op.blob->bounds();
486 dst.offset(op.x, op.y);
487 return this->adjustAndMap(dst, &op.paint);
488 }
489
490 #if defined(SK_GANESH)
bounds(const DrawSlug & op) const491 Bounds bounds(const DrawSlug& op) const {
492 SkRect dst = op.slug->sourceBoundsWithOrigin();
493 return this->adjustAndMap(dst, &op.slug->initialPaint());
494 }
495 #else
bounds(const DrawSlug & op) const496 Bounds bounds(const DrawSlug& op) const {
497 return SkRect::MakeEmpty();
498 }
499 #endif
500
bounds(const DrawDrawable & op) const501 Bounds bounds(const DrawDrawable& op) const {
502 return this->adjustAndMap(op.worstCaseBounds, nullptr);
503 }
504
bounds(const DrawAnnotation & op) const505 Bounds bounds(const DrawAnnotation& op) const {
506 return this->adjustAndMap(op.rect, nullptr);
507 }
bounds(const DrawEdgeAAQuad & op) const508 Bounds bounds(const DrawEdgeAAQuad& op) const {
509 SkRect bounds = op.rect;
510 if (op.clip) {
511 bounds.setBounds(op.clip, 4);
512 }
513 return this->adjustAndMap(bounds, nullptr);
514 }
bounds(const DrawEdgeAAImageSet & op) const515 Bounds bounds(const DrawEdgeAAImageSet& op) const {
516 SkRect rect = SkRect::MakeEmpty();
517 int clipIndex = 0;
518 for (int i = 0; i < op.count; ++i) {
519 SkRect entryBounds = op.set[i].fDstRect;
520 if (op.set[i].fHasClip) {
521 entryBounds.setBounds(op.dstClips + clipIndex, 4);
522 clipIndex += 4;
523 }
524 if (op.set[i].fMatrixIndex >= 0) {
525 op.preViewMatrices[op.set[i].fMatrixIndex].mapRect(&entryBounds);
526 }
527 rect.join(this->adjustAndMap(entryBounds, nullptr));
528 }
529 return rect;
530 }
531
532 // Returns true if rect was meaningfully adjusted for the effects of paint,
533 // false if the paint could affect the rect in unknown ways.
AdjustForPaint(const SkPaint * paint,SkRect * rect)534 static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) {
535 if (paint) {
536 if (paint->canComputeFastBounds()) {
537 *rect = paint->computeFastBounds(*rect, rect);
538 return true;
539 }
540 return false;
541 }
542 return true;
543 }
544
adjustForSaveLayerPaints(SkRect * rect,int savesToIgnore=0) const545 bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const {
546 for (int i = fSaveStack.size() - 1 - savesToIgnore; i >= 0; i--) {
547 SkMatrix inverse;
548 if (!fSaveStack[i].ctm.invert(&inverse)) {
549 return false;
550 }
551 inverse.mapRect(rect);
552 if (!AdjustForPaint(fSaveStack[i].paint, rect)) {
553 return false;
554 }
555 fSaveStack[i].ctm.mapRect(rect);
556 }
557 return true;
558 }
559
560 // We do not guarantee anything for operations outside of the cull rect
561 const SkRect fCullRect;
562
563 // Conservative identity-space bounds for each op in the SkRecord.
564 Bounds* fBounds;
565
566 // Parallel array to fBounds, holding metadata for each bounds rect.
567 SkBBoxHierarchy::Metadata* fMeta;
568
569 // We walk fCurrentOp through the SkRecord,
570 // as we go using updateCTM() to maintain the exact CTM (fCTM).
571 int fCurrentOp;
572 SkMatrix fCTM;
573
574 // Used to track the bounds of Save/Restore blocks and the control ops inside them.
575 SkTDArray<SaveBounds> fSaveStack;
576 SkTDArray<int> fControlIndices;
577 };
578
579 } // namespace SkRecords
580
SkRecordFillBounds(const SkRect & cullRect,const SkRecord & record,SkRect bounds[],SkBBoxHierarchy::Metadata meta[])581 void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record,
582 SkRect bounds[], SkBBoxHierarchy::Metadata meta[]) {
583 {
584 SkRecords::FillBounds visitor(cullRect, record, bounds, meta);
585 for (int i = 0; i < record.count(); i++) {
586 visitor.setCurrentOp(i);
587 record.visit(i, visitor);
588 }
589 }
590 }
591