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