xref: /aosp_15_r20/external/skia/src/core/SkRecordDraw.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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.backdropTileMode,
105                r.saveLayerFlags,
106                SkCanvas::FilterSpan{const_cast<sk_sp<SkImageFilter>*>(r.filters.data()),
107                                     r.filters.size()})))
108 
109 template <> void Draw::draw(const SaveBehind& r) {
110     SkCanvasPriv::SaveBehind(fCanvas, r.subset);
111 }
112 
draw(const DrawBehind & r)113 template <> void Draw::draw(const DrawBehind& r) {
114     SkCanvasPriv::DrawBehind(fCanvas, r.paint);
115 }
116 
117 DRAW(SetMatrix, setMatrix(fInitialCTM.asM33() * r.matrix))
118 DRAW(SetM44, setMatrix(fInitialCTM * r.matrix))
119 DRAW(Concat44, concat(r.matrix))
120 DRAW(Concat, concat(r.matrix))
121 DRAW(Translate, translate(r.dx, r.dy))
122 DRAW(Scale, scale(r.sx, r.sy))
123 
124 DRAW(ClipPath, clipPath(r.path, r.opAA.op(), r.opAA.aa()))
125 DRAW(ClipRRect, clipRRect(r.rrect, r.opAA.op(), r.opAA.aa()))
126 DRAW(ClipRect, clipRect(r.rect, r.opAA.op(), r.opAA.aa()))
127 DRAW(ClipRegion, clipRegion(r.region, r.op))
128 DRAW(ClipShader, clipShader(r.shader, r.op))
129 
draw(const ResetClip & r)130 template <> void Draw::draw(const ResetClip& r) {
131     SkCanvasPriv::ResetClip(fCanvas);
132 }
133 
134 DRAW(DrawArc, drawArc(r.oval, r.startAngle, r.sweepAngle, r.useCenter, r.paint))
135 DRAW(DrawDRRect, drawDRRect(r.outer, r.inner, r.paint))
136 DRAW(DrawImage, drawImage(r.image.get(), r.left, r.top, r.sampling, r.paint))
137 
draw(const DrawImageLattice & r)138 template <> void Draw::draw(const DrawImageLattice& r) {
139     SkCanvas::Lattice lattice;
140     lattice.fXCount = r.xCount;
141     lattice.fXDivs = r.xDivs;
142     lattice.fYCount = r.yCount;
143     lattice.fYDivs = r.yDivs;
144     lattice.fRectTypes = (0 == r.flagCount) ? nullptr : r.flags;
145     lattice.fColors = (0 == r.flagCount) ? nullptr : r.colors;
146     lattice.fBounds = &r.src;
147     fCanvas->drawImageLattice(r.image.get(), lattice, r.dst, r.filter, r.paint);
148 }
149 
150 DRAW(DrawImageRect, drawImageRect(r.image.get(), r.src, r.dst, r.sampling, r.paint, r.constraint))
151 DRAW(DrawOval, drawOval(r.oval, r.paint))
152 DRAW(DrawPaint, drawPaint(r.paint))
153 DRAW(DrawPath, drawPath(r.path, r.paint))
154 DRAW(DrawPatch, drawPatch(r.cubics, r.colors, r.texCoords, r.bmode, r.paint))
155 DRAW(DrawPicture, drawPicture(r.picture.get(), &r.matrix, r.paint))
156 DRAW(DrawPoints, drawPoints(r.mode, r.count, r.pts, r.paint))
157 DRAW(DrawRRect, drawRRect(r.rrect, r.paint))
158 DRAW(DrawRect, drawRect(r.rect, r.paint))
159 DRAW(DrawRegion, drawRegion(r.region, r.paint))
160 DRAW(DrawTextBlob, drawTextBlob(r.blob.get(), r.x, r.y, r.paint))
161 DRAW(DrawSlug, drawSlug(r.slug.get(), r.paint))
162 DRAW(DrawAtlas, drawAtlas(r.atlas.get(), r.xforms, r.texs, r.colors, r.count, r.mode, r.sampling,
163                           r.cull, r.paint))
164 DRAW(DrawVertices, drawVertices(r.vertices, r.bmode, r.paint))
165 DRAW(DrawMesh, drawMesh(r.mesh, r.blender, r.paint))
166 DRAW(DrawShadowRec, private_draw_shadow_rec(r.path, r.rec))
167 DRAW(DrawAnnotation, drawAnnotation(r.rect, r.key.c_str(), r.value.get()))
168 
169 DRAW(DrawEdgeAAQuad, experimental_DrawEdgeAAQuad(
170         r.rect, r.clip, r.aa, r.color, r.mode))
171 DRAW(DrawEdgeAAImageSet, experimental_DrawEdgeAAImageSet(
172         r.set.get(), r.count, r.dstClips, r.preViewMatrices, r.sampling, r.paint, r.constraint))
173 
174 #undef DRAW
175 
draw(const DrawDrawable & r)176 template <> void Draw::draw(const DrawDrawable& r) {
177     SkASSERT(r.index >= 0);
178     SkASSERT(r.index < fDrawableCount);
179     if (fDrawables) {
180         SkASSERT(nullptr == fDrawablePicts);
181         fCanvas->drawDrawable(fDrawables[r.index], r.matrix);
182     } else {
183         fCanvas->drawPicture(fDrawablePicts[r.index], r.matrix, nullptr);
184     }
185 }
186 
187 // This is an SkRecord visitor that fills an SkBBoxHierarchy.
188 //
189 // The interesting part here is how to calculate bounds for ops which don't
190 // have intrinsic bounds.  What is the bounds of a Save or a Translate?
191 //
192 // We answer this by thinking about a particular definition of bounds: if I
193 // don't execute this op, pixels in this rectangle might draw incorrectly.  So
194 // the bounds of a Save, a Translate, a Restore, etc. are the union of the
195 // bounds of Draw* ops that they might have an effect on.  For any given
196 // Save/Restore block, the bounds of the Save, the Restore, and any other
197 // non-drawing ("control") ops inside are exactly the union of the bounds of
198 // the drawing ops inside that block.
199 //
200 // To implement this, we keep a stack of active Save blocks.  As we consume ops
201 // inside the Save/Restore block, drawing ops are unioned with the bounds of
202 // the block, and control ops are stashed away for later.  When we finish the
203 // block with a Restore, our bounds are complete, and we go back and fill them
204 // in for all the control ops we stashed away.
205 class FillBounds : SkNoncopyable {
206 public:
FillBounds(const SkRect & cullRect,const SkRecord & record,SkRect bounds[],SkBBoxHierarchy::Metadata meta[])207     FillBounds(const SkRect& cullRect, const SkRecord& record,
208                SkRect bounds[], SkBBoxHierarchy::Metadata meta[])
209         : fCullRect(cullRect)
210         , fBounds(bounds)
211         , fMeta(meta) {
212         fCTM = SkMatrix::I();
213 
214         // We push an extra save block to track the bounds of any top-level control operations.
215         fSaveStack.push_back({ 0, Bounds::MakeEmpty(), nullptr, fCTM });
216     }
217 
~FillBounds()218     ~FillBounds() {
219         // If we have any lingering unpaired Saves, simulate restores to make
220         // sure all ops in those Save blocks have their bounds calculated.
221         while (!fSaveStack.empty()) {
222             this->popSaveBlock();
223         }
224 
225         // Any control ops not part of any Save/Restore block draw everywhere.
226         while (!fControlIndices.empty()) {
227             this->popControl(fCullRect);
228         }
229     }
230 
setCurrentOp(int currentOp)231     void setCurrentOp(int currentOp) { fCurrentOp = currentOp; }
232 
233 
operator ()(const T & op)234     template <typename T> void operator()(const T& op) {
235         this->updateCTM(op);
236         this->trackBounds(op);
237     }
238 
239     // In this file, SkRect are in local coordinates, Bounds are translated back to identity space.
240     typedef SkRect Bounds;
241 
242     // Adjust rect for all paints that may affect its geometry, then map it to identity space.
adjustAndMap(SkRect rect,const SkPaint * paint) const243     Bounds adjustAndMap(SkRect rect, const SkPaint* paint) const {
244         // Inverted rectangles really confuse our BBHs.
245         rect.sort();
246 
247         // Adjust the rect for its own paint.
248         if (!AdjustForPaint(paint, &rect)) {
249             // The paint could do anything to our bounds.  The only safe answer is the cull.
250             return fCullRect;
251         }
252 
253         // Adjust rect for all the paints from the SaveLayers we're inside.
254         if (!this->adjustForSaveLayerPaints(&rect)) {
255             // Same deal as above.
256             return fCullRect;
257         }
258 
259         // Map the rect back to identity space.
260         fCTM.mapRect(&rect);
261 
262         // Nothing can draw outside the cull rect.
263         if (!rect.intersect(fCullRect)) {
264             return Bounds::MakeEmpty();
265         }
266 
267         return rect;
268     }
269 
270 private:
271     struct SaveBounds {
272         int controlOps;        // Number of control ops in this Save block, including the Save.
273         Bounds bounds;         // Bounds of everything in the block.
274         const SkPaint* paint;  // Unowned.  If set, adjusts the bounds of all ops in this block.
275         SkMatrix ctm;
276     };
277 
278     // Only Restore, SetMatrix, Concat, and Translate change the CTM.
updateCTM(const T &)279     template <typename T> void updateCTM(const T&) {}
updateCTM(const Restore & op)280     void updateCTM(const Restore& op)   { fCTM = op.matrix; }
updateCTM(const SetMatrix & op)281     void updateCTM(const SetMatrix& op) { fCTM = op.matrix; }
updateCTM(const SetM44 & op)282     void updateCTM(const SetM44& op)    { fCTM = op.matrix.asM33(); }
updateCTM(const Concat44 & op)283     void updateCTM(const Concat44& op)  { fCTM.preConcat(op.matrix.asM33()); }
updateCTM(const Concat & op)284     void updateCTM(const Concat& op)    { fCTM.preConcat(op.matrix); }
updateCTM(const Scale & op)285     void updateCTM(const Scale& op)     { fCTM.preScale(op.sx, op.sy); }
updateCTM(const Translate & op)286     void updateCTM(const Translate& op) { fCTM.preTranslate(op.dx, op.dy); }
287 
288     // The bounds of these ops must be calculated when we hit the Restore
289     // from the bounds of the ops in the same Save block.
trackBounds(const Save &)290     void trackBounds(const Save&) {
291         this->pushSaveBlock(nullptr, /*hasBackdropFilter=*/false);
292     }
trackBounds(const SaveLayer & op)293     void trackBounds(const SaveLayer& op) {
294         this->pushSaveBlock(op.paint, /*hasBackdropFilter=*/op.backdrop != nullptr);
295     }
trackBounds(const SaveBehind &)296     void trackBounds(const SaveBehind&) {
297         this->pushSaveBlock(nullptr, /*hasBackdropFilter=*/false);
298     }
trackBounds(const Restore &)299     void trackBounds(const Restore&) {
300         const bool isSaveLayer = fSaveStack.back().paint != nullptr;
301         fBounds[fCurrentOp] = this->popSaveBlock();
302         fMeta  [fCurrentOp].isDraw = isSaveLayer;
303     }
304 
trackBounds(const SetMatrix &)305     void trackBounds(const SetMatrix&)         { this->pushControl(); }
trackBounds(const SetM44 &)306     void trackBounds(const SetM44&)            { this->pushControl(); }
trackBounds(const Concat &)307     void trackBounds(const Concat&)            { this->pushControl(); }
trackBounds(const Concat44 &)308     void trackBounds(const Concat44&)          { this->pushControl(); }
trackBounds(const Scale &)309     void trackBounds(const Scale&)             { this->pushControl(); }
trackBounds(const Translate &)310     void trackBounds(const Translate&)         { this->pushControl(); }
trackBounds(const ClipRect &)311     void trackBounds(const ClipRect&)          { this->pushControl(); }
trackBounds(const ClipRRect &)312     void trackBounds(const ClipRRect&)         { this->pushControl(); }
trackBounds(const ClipPath &)313     void trackBounds(const ClipPath&)          { this->pushControl(); }
trackBounds(const ClipRegion &)314     void trackBounds(const ClipRegion&)        { this->pushControl(); }
trackBounds(const ClipShader &)315     void trackBounds(const ClipShader&)        { this->pushControl(); }
trackBounds(const ResetClip &)316     void trackBounds(const ResetClip&)         { this->pushControl(); }
317 
318 
319     // For all other ops, we can calculate and store the bounds directly now.
trackBounds(const T & op)320     template <typename T> void trackBounds(const T& op) {
321         fBounds[fCurrentOp] = this->bounds(op);
322         fMeta  [fCurrentOp].isDraw = true;
323         this->updateSaveBounds(fBounds[fCurrentOp]);
324     }
325 
pushSaveBlock(const SkPaint * paint,bool hasBackdropFilter)326     void pushSaveBlock(const SkPaint* paint, bool hasBackdropFilter) {
327         // Starting a new Save block.  Push a new entry to represent that.
328         SaveBounds sb;
329         sb.controlOps = 0;
330 
331         // If the paint affects transparent black, or we have a backdrop filter,
332         // the bound shouldn't be smaller than the cull.
333         bool affectsFullCullRect = hasBackdropFilter || PaintMayAffectTransparentBlack(paint);
334         sb.bounds = affectsFullCullRect ? fCullRect : Bounds::MakeEmpty();
335         sb.paint = paint;
336         sb.ctm = this->fCTM;
337 
338         fSaveStack.push_back(sb);
339         this->pushControl();
340     }
341 
PaintMayAffectTransparentBlack(const SkPaint * paint)342     static bool PaintMayAffectTransparentBlack(const SkPaint* paint) {
343         if (paint) {
344             // FIXME: this is very conservative
345             if ((paint->getImageFilter() &&
346                  as_IFB(paint->getImageFilter())->affectsTransparentBlack()) ||
347                 (paint->getColorFilter() &&
348                  as_CFB(paint->getColorFilter())->affectsTransparentBlack())) {
349                 return true;
350             }
351             const auto bm = paint->asBlendMode();
352             if (!bm) {
353                 return true;    // can we query other blenders for this?
354             }
355 
356             // Unusual blendmodes require us to process a saved layer
357             // even with operations outisde the clip.
358             // For example, DstIn is used by masking layers.
359             // https://code.google.com/p/skia/issues/detail?id=1291
360             // https://crbug.com/401593
361             switch (bm.value()) {
362                 // For each of the following transfer modes, if the source
363                 // alpha is zero (our transparent black), the resulting
364                 // blended alpha is not necessarily equal to the original
365                 // destination alpha.
366                 case SkBlendMode::kClear:
367                 case SkBlendMode::kSrc:
368                 case SkBlendMode::kSrcIn:
369                 case SkBlendMode::kDstIn:
370                 case SkBlendMode::kSrcOut:
371                 case SkBlendMode::kDstATop:
372                 case SkBlendMode::kModulate:
373                     return true;
374                 default:
375                     break;
376             }
377         }
378         return false;
379     }
380 
popSaveBlock()381     Bounds popSaveBlock() {
382         // We're done the Save block.  Apply the block's bounds to all control ops inside it.
383         SaveBounds sb = fSaveStack.back();
384         fSaveStack.pop_back();
385 
386         while (sb.controlOps --> 0) {
387             this->popControl(sb.bounds);
388         }
389 
390         // This whole Save block may be part another Save block.
391         this->updateSaveBounds(sb.bounds);
392 
393         // If called from a real Restore (not a phony one for balance), it'll need the bounds.
394         return sb.bounds;
395     }
396 
pushControl()397     void pushControl() {
398         fControlIndices.push_back(fCurrentOp);
399         if (!fSaveStack.empty()) {
400             fSaveStack.back().controlOps++;
401         }
402     }
403 
popControl(const Bounds & bounds)404     void popControl(const Bounds& bounds) {
405         fBounds[fControlIndices.back()] = bounds;
406         fMeta  [fControlIndices.back()].isDraw = false;
407         fControlIndices.pop_back();
408     }
409 
updateSaveBounds(const Bounds & bounds)410     void updateSaveBounds(const Bounds& bounds) {
411         // If we're in a Save block, expand its bounds to cover these bounds too.
412         if (!fSaveStack.empty()) {
413             fSaveStack.back().bounds.join(bounds);
414         }
415     }
416 
bounds(const DrawPaint &) const417     Bounds bounds(const DrawPaint&) const { return fCullRect; }
bounds(const DrawBehind &) const418     Bounds bounds(const DrawBehind&) const { return fCullRect; }
bounds(const NoOp &) const419     Bounds bounds(const NoOp&)  const { return Bounds::MakeEmpty(); }    // NoOps don't draw.
420 
bounds(const DrawRect & op) const421     Bounds bounds(const DrawRect& op) const { return this->adjustAndMap(op.rect, &op.paint); }
bounds(const DrawRegion & op) const422     Bounds bounds(const DrawRegion& op) const {
423         SkRect rect = SkRect::Make(op.region.getBounds());
424         return this->adjustAndMap(rect, &op.paint);
425     }
bounds(const DrawOval & op) const426     Bounds bounds(const DrawOval& op) const { return this->adjustAndMap(op.oval, &op.paint); }
427     // Tighter arc bounds?
bounds(const DrawArc & op) const428     Bounds bounds(const DrawArc& op) const { return this->adjustAndMap(op.oval, &op.paint); }
bounds(const DrawRRect & op) const429     Bounds bounds(const DrawRRect& op) const {
430         return this->adjustAndMap(op.rrect.rect(), &op.paint);
431     }
bounds(const DrawDRRect & op) const432     Bounds bounds(const DrawDRRect& op) const {
433         return this->adjustAndMap(op.outer.rect(), &op.paint);
434     }
bounds(const DrawImage & op) const435     Bounds bounds(const DrawImage& op) const {
436         const SkImage* image = op.image.get();
437         SkRect rect = SkRect::MakeXYWH(op.left, op.top, image->width(), image->height());
438 
439         return this->adjustAndMap(rect, op.paint);
440     }
bounds(const DrawImageLattice & op) const441     Bounds bounds(const DrawImageLattice& op) const {
442         return this->adjustAndMap(op.dst, op.paint);
443     }
bounds(const DrawImageRect & op) const444     Bounds bounds(const DrawImageRect& op) const {
445         return this->adjustAndMap(op.dst, op.paint);
446     }
bounds(const DrawPath & op) const447     Bounds bounds(const DrawPath& op) const {
448         return op.path.isInverseFillType() ? fCullRect
449                                            : this->adjustAndMap(op.path.getBounds(), &op.paint);
450     }
bounds(const DrawPoints & op) const451     Bounds bounds(const DrawPoints& op) const {
452         SkRect dst;
453         dst.setBounds(op.pts, op.count);
454 
455         // Pad the bounding box a little to make sure hairline points' bounds aren't empty.
456         SkScalar stroke = std::max(op.paint.getStrokeWidth(), 0.01f);
457         dst.outset(stroke/2, stroke/2);
458 
459         return this->adjustAndMap(dst, &op.paint);
460     }
bounds(const DrawPatch & op) const461     Bounds bounds(const DrawPatch& op) const {
462         SkRect dst;
463         dst.setBounds(op.cubics, SkPatchUtils::kNumCtrlPts);
464         return this->adjustAndMap(dst, &op.paint);
465     }
bounds(const DrawVertices & op) const466     Bounds bounds(const DrawVertices& op) const {
467         return this->adjustAndMap(op.vertices->bounds(), &op.paint);
468     }
bounds(const DrawMesh & op) const469     Bounds bounds(const DrawMesh& op) const {
470         return this->adjustAndMap(op.mesh.bounds(), &op.paint);
471     }
bounds(const DrawAtlas & op) const472     Bounds bounds(const DrawAtlas& op) const {
473         if (op.cull) {
474             // TODO: <reed> can we pass nullptr for the paint? Isn't cull already "correct"
475             // for the paint (by the caller)?
476             return this->adjustAndMap(*op.cull, op.paint);
477         } else {
478             return fCullRect;
479         }
480     }
481 
bounds(const DrawShadowRec & op) const482     Bounds bounds(const DrawShadowRec& op) const {
483         SkRect bounds;
484         SkDrawShadowMetrics::GetLocalBounds(op.path, op.rec, fCTM, &bounds);
485         return this->adjustAndMap(bounds, nullptr);
486     }
487 
bounds(const DrawPicture & op) const488     Bounds bounds(const DrawPicture& op) const {
489         SkRect dst = op.picture->cullRect();
490         op.matrix.mapRect(&dst);
491         return this->adjustAndMap(dst, op.paint);
492     }
493 
bounds(const DrawTextBlob & op) const494     Bounds bounds(const DrawTextBlob& op) const {
495         SkRect dst = op.blob->bounds();
496         dst.offset(op.x, op.y);
497         return this->adjustAndMap(dst, &op.paint);
498     }
499 
bounds(const DrawSlug & op) const500     Bounds bounds(const DrawSlug& op) const {
501         SkRect dst = op.slug->sourceBoundsWithOrigin();
502         return this->adjustAndMap(dst, &op.paint);
503     }
504 
bounds(const DrawDrawable & op) const505     Bounds bounds(const DrawDrawable& op) const {
506         return this->adjustAndMap(op.worstCaseBounds, nullptr);
507     }
508 
bounds(const DrawAnnotation & op) const509     Bounds bounds(const DrawAnnotation& op) const {
510         return this->adjustAndMap(op.rect, nullptr);
511     }
bounds(const DrawEdgeAAQuad & op) const512     Bounds bounds(const DrawEdgeAAQuad& op) const {
513         SkRect bounds = op.rect;
514         if (op.clip) {
515             bounds.setBounds(op.clip, 4);
516         }
517         return this->adjustAndMap(bounds, nullptr);
518     }
bounds(const DrawEdgeAAImageSet & op) const519     Bounds bounds(const DrawEdgeAAImageSet& op) const {
520         SkRect rect = SkRect::MakeEmpty();
521         int clipIndex = 0;
522         for (int i = 0; i < op.count; ++i) {
523             SkRect entryBounds = op.set[i].fDstRect;
524             if (op.set[i].fHasClip) {
525                 entryBounds.setBounds(op.dstClips + clipIndex, 4);
526                 clipIndex += 4;
527             }
528             if (op.set[i].fMatrixIndex >= 0) {
529                 op.preViewMatrices[op.set[i].fMatrixIndex].mapRect(&entryBounds);
530             }
531             rect.join(this->adjustAndMap(entryBounds, nullptr));
532         }
533         return rect;
534     }
535 
536     // Returns true if rect was meaningfully adjusted for the effects of paint,
537     // false if the paint could affect the rect in unknown ways.
AdjustForPaint(const SkPaint * paint,SkRect * rect)538     static bool AdjustForPaint(const SkPaint* paint, SkRect* rect) {
539         if (paint) {
540             if (paint->canComputeFastBounds()) {
541                 *rect = paint->computeFastBounds(*rect, rect);
542                 return true;
543             }
544             return false;
545         }
546         return true;
547     }
548 
adjustForSaveLayerPaints(SkRect * rect,int savesToIgnore=0) const549     bool adjustForSaveLayerPaints(SkRect* rect, int savesToIgnore = 0) const {
550         for (int i = fSaveStack.size() - 1 - savesToIgnore; i >= 0; i--) {
551             SkMatrix inverse;
552             if (!fSaveStack[i].ctm.invert(&inverse)) {
553                 return false;
554             }
555             inverse.mapRect(rect);
556             if (!AdjustForPaint(fSaveStack[i].paint, rect)) {
557                 return false;
558             }
559             fSaveStack[i].ctm.mapRect(rect);
560         }
561         return true;
562     }
563 
564     // We do not guarantee anything for operations outside of the cull rect
565     const SkRect fCullRect;
566 
567     // Conservative identity-space bounds for each op in the SkRecord.
568     Bounds* fBounds;
569 
570     // Parallel array to fBounds, holding metadata for each bounds rect.
571     SkBBoxHierarchy::Metadata* fMeta;
572 
573     // We walk fCurrentOp through the SkRecord,
574     // as we go using updateCTM() to maintain the exact CTM (fCTM).
575     int fCurrentOp;
576     SkMatrix fCTM;
577 
578     // Used to track the bounds of Save/Restore blocks and the control ops inside them.
579     SkTDArray<SaveBounds> fSaveStack;
580     SkTDArray<int>   fControlIndices;
581 };
582 
583 }  // namespace SkRecords
584 
SkRecordFillBounds(const SkRect & cullRect,const SkRecord & record,SkRect bounds[],SkBBoxHierarchy::Metadata meta[])585 void SkRecordFillBounds(const SkRect& cullRect, const SkRecord& record,
586                         SkRect bounds[], SkBBoxHierarchy::Metadata meta[]) {
587     {
588         SkRecords::FillBounds visitor(cullRect, record, bounds, meta);
589         for (int i = 0; i < record.count(); i++) {
590             visitor.setCurrentOp(i);
591             record.visit(i, visitor);
592         }
593     }
594 }
595