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