• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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