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