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