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