1 | /* |
2 | * Copyright 2008 The Android Open Source Project |
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/SkCanvas.h" |
9 | |
10 | #include "include/core/SkColorFilter.h" |
11 | #include "include/core/SkImage.h" |
12 | #include "include/core/SkImageFilter.h" |
13 | #include "include/core/SkPathEffect.h" |
14 | #include "include/core/SkPicture.h" |
15 | #include "include/core/SkRRect.h" |
16 | #include "include/core/SkRasterHandleAllocator.h" |
17 | #include "include/core/SkString.h" |
18 | #include "include/core/SkTextBlob.h" |
19 | #include "include/core/SkVertices.h" |
20 | #include "include/effects/SkRuntimeEffect.h" |
21 | #include "include/private/SkNx.h" |
22 | #include "include/private/SkTo.h" |
23 | #include "include/utils/SkNoDrawCanvas.h" |
24 | #include "src/core/SkArenaAlloc.h" |
25 | #include "src/core/SkBitmapDevice.h" |
26 | #include "src/core/SkCanvasMatrix.h" |
27 | #include "src/core/SkCanvasPriv.h" |
28 | #include "src/core/SkClipOpPriv.h" |
29 | #include "src/core/SkClipStack.h" |
30 | #include "src/core/SkDraw.h" |
31 | #include "src/core/SkGlyphRun.h" |
32 | #include "src/core/SkImageFilterCache.h" |
33 | #include "src/core/SkImageFilter_Base.h" |
34 | #include "src/core/SkLatticeIter.h" |
35 | #include "src/core/SkMSAN.h" |
36 | #include "src/core/SkMatrixPriv.h" |
37 | #include "src/core/SkMatrixUtils.h" |
38 | #include "src/core/SkPaintPriv.h" |
39 | #include "src/core/SkRasterClip.h" |
40 | #include "src/core/SkSpecialImage.h" |
41 | #include "src/core/SkStrikeCache.h" |
42 | #include "src/core/SkTLazy.h" |
43 | #include "src/core/SkTextFormatParams.h" |
44 | #include "src/core/SkTraceEvent.h" |
45 | #include "src/core/SkVerticesPriv.h" |
46 | #include "src/image/SkImage_Base.h" |
47 | #include "src/image/SkSurface_Base.h" |
48 | #include "src/utils/SkPatchUtils.h" |
49 | |
50 | #include <new> |
51 | |
52 | #if SK_SUPPORT_GPU |
53 | #include "include/gpu/GrContext.h" |
54 | #include "src/gpu/SkGr.h" |
55 | #endif |
56 | |
57 | #define RETURN_ON_NULL(ptr) do { if (nullptr == (ptr)) return; } while (0) |
58 | #define RETURN_ON_FALSE(pred) do { if (!(pred)) return; } while (0) |
59 | |
60 | // This is a test: static_assert with no message is a c++17 feature, |
61 | // and std::max() is constexpr only since the c++14 stdlib. |
62 | static_assert(std::max(3,4) == 4); |
63 | |
64 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
65 | |
66 | /* |
67 | * Return true if the drawing this rect would hit every pixels in the canvas. |
68 | * |
69 | * Returns false if |
70 | * - rect does not contain the canvas' bounds |
71 | * - paint is not fill |
72 | * - paint would blur or otherwise change the coverage of the rect |
73 | */ |
74 | bool SkCanvas::wouldOverwriteEntireSurface(const SkRect* rect, const SkPaint* paint, |
75 | ShaderOverrideOpacity overrideOpacity) const { |
76 | static_assert((int)SkPaintPriv::kNone_ShaderOverrideOpacity == |
77 | (int)kNone_ShaderOverrideOpacity, |
78 | "need_matching_enums0" ); |
79 | static_assert((int)SkPaintPriv::kOpaque_ShaderOverrideOpacity == |
80 | (int)kOpaque_ShaderOverrideOpacity, |
81 | "need_matching_enums1" ); |
82 | static_assert((int)SkPaintPriv::kNotOpaque_ShaderOverrideOpacity == |
83 | (int)kNotOpaque_ShaderOverrideOpacity, |
84 | "need_matching_enums2" ); |
85 | |
86 | const SkISize size = this->getBaseLayerSize(); |
87 | const SkRect bounds = SkRect::MakeIWH(size.width(), size.height()); |
88 | |
89 | // if we're clipped at all, we can't overwrite the entire surface |
90 | { |
91 | SkBaseDevice* base = this->getDevice(); |
92 | SkBaseDevice* top = this->getTopDevice(); |
93 | if (base != top) { |
94 | return false; // we're in a saveLayer, so conservatively don't assume we'll overwrite |
95 | } |
96 | if (!base->clipIsWideOpen()) { |
97 | return false; |
98 | } |
99 | } |
100 | |
101 | if (rect) { |
102 | if (!this->getTotalMatrix().isScaleTranslate()) { |
103 | return false; // conservative |
104 | } |
105 | |
106 | SkRect devRect; |
107 | this->getTotalMatrix().mapRectScaleTranslate(&devRect, *rect); |
108 | if (!devRect.contains(bounds)) { |
109 | return false; |
110 | } |
111 | } |
112 | |
113 | if (paint) { |
114 | SkPaint::Style paintStyle = paint->getStyle(); |
115 | if (!(paintStyle == SkPaint::kFill_Style || |
116 | paintStyle == SkPaint::kStrokeAndFill_Style)) { |
117 | return false; |
118 | } |
119 | if (paint->getMaskFilter() || paint->getPathEffect() || paint->getImageFilter()) { |
120 | return false; // conservative |
121 | } |
122 | } |
123 | return SkPaintPriv::Overwrites(paint, (SkPaintPriv::ShaderOverrideOpacity)overrideOpacity); |
124 | } |
125 | |
126 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
127 | |
128 | // experimental for faster tiled drawing... |
129 | //#define SK_TRACE_SAVERESTORE |
130 | |
131 | #ifdef SK_TRACE_SAVERESTORE |
132 | static int gLayerCounter; |
133 | static void inc_layer() { ++gLayerCounter; printf("----- inc layer %d\n" , gLayerCounter); } |
134 | static void dec_layer() { --gLayerCounter; printf("----- dec layer %d\n" , gLayerCounter); } |
135 | |
136 | static int gRecCounter; |
137 | static void inc_rec() { ++gRecCounter; printf("----- inc rec %d\n" , gRecCounter); } |
138 | static void dec_rec() { --gRecCounter; printf("----- dec rec %d\n" , gRecCounter); } |
139 | |
140 | static int gCanvasCounter; |
141 | static void inc_canvas() { ++gCanvasCounter; printf("----- inc canvas %d\n" , gCanvasCounter); } |
142 | static void dec_canvas() { --gCanvasCounter; printf("----- dec canvas %d\n" , gCanvasCounter); } |
143 | #else |
144 | #define inc_layer() |
145 | #define dec_layer() |
146 | #define inc_rec() |
147 | #define dec_rec() |
148 | #define inc_canvas() |
149 | #define dec_canvas() |
150 | #endif |
151 | |
152 | typedef SkTLazy<SkPaint> SkLazyPaint; |
153 | |
154 | void SkCanvas::predrawNotify(bool willOverwritesEntireSurface) { |
155 | if (fSurfaceBase) { |
156 | fSurfaceBase->aboutToDraw(willOverwritesEntireSurface |
157 | ? SkSurface::kDiscard_ContentChangeMode |
158 | : SkSurface::kRetain_ContentChangeMode); |
159 | } |
160 | } |
161 | |
162 | void SkCanvas::predrawNotify(const SkRect* rect, const SkPaint* paint, |
163 | ShaderOverrideOpacity overrideOpacity) { |
164 | if (fSurfaceBase) { |
165 | SkSurface::ContentChangeMode mode = SkSurface::kRetain_ContentChangeMode; |
166 | // Since willOverwriteAllPixels() may not be complete free to call, we only do so if |
167 | // there is an outstanding snapshot, since w/o that, there will be no copy-on-write |
168 | // and therefore we don't care which mode we're in. |
169 | // |
170 | if (fSurfaceBase->outstandingImageSnapshot()) { |
171 | if (this->wouldOverwriteEntireSurface(rect, paint, overrideOpacity)) { |
172 | mode = SkSurface::kDiscard_ContentChangeMode; |
173 | } |
174 | } |
175 | fSurfaceBase->aboutToDraw(mode); |
176 | } |
177 | } |
178 | |
179 | /////////////////////////////////////////////////////////////////////////////// |
180 | |
181 | /* This is the record we keep for each SkBaseDevice that the user installs. |
182 | The clip/matrix/proc are fields that reflect the top of the save/restore |
183 | stack. Whenever the canvas changes, it marks a dirty flag, and then before |
184 | these are used (assuming we're not on a layer) we rebuild these cache |
185 | values: they reflect the top of the save stack, but translated and clipped |
186 | by the device's XY offset and bitmap-bounds. |
187 | */ |
188 | struct DeviceCM { |
189 | DeviceCM* fNext; |
190 | sk_sp<SkBaseDevice> fDevice; |
191 | SkRasterClip fClip; |
192 | std::unique_ptr<const SkPaint> fPaint; // may be null (in the future) |
193 | SkMatrix fStashedMatrix; // original CTM; used by imagefilter in saveLayer |
194 | sk_sp<SkImage> fClipImage; |
195 | SkMatrix fClipMatrix; |
196 | |
197 | DeviceCM(sk_sp<SkBaseDevice> device, const SkPaint* paint, const SkMatrix& stashed, |
198 | const SkImage* clipImage, const SkMatrix* clipMatrix) |
199 | : fNext(nullptr) |
200 | , fDevice(std::move(device)) |
201 | , fPaint(paint ? std::make_unique<SkPaint>(*paint) : nullptr) |
202 | , fStashedMatrix(stashed) |
203 | , fClipImage(sk_ref_sp(const_cast<SkImage*>(clipImage))) |
204 | , fClipMatrix(clipMatrix ? *clipMatrix : SkMatrix::I()) |
205 | {} |
206 | |
207 | void reset(const SkIRect& bounds) { |
208 | SkASSERT(!fPaint); |
209 | SkASSERT(!fNext); |
210 | SkASSERT(fDevice); |
211 | fClip.setRect(bounds); |
212 | } |
213 | }; |
214 | |
215 | namespace { |
216 | // Encapsulate state needed to restore from saveBehind() |
217 | struct BackImage { |
218 | sk_sp<SkSpecialImage> fImage; |
219 | SkIPoint fLoc; |
220 | }; |
221 | } |
222 | |
223 | /* This is the record we keep for each save/restore level in the stack. |
224 | Since a level optionally copies the matrix and/or stack, we have pointers |
225 | for these fields. If the value is copied for this level, the copy is |
226 | stored in the ...Storage field, and the pointer points to that. If the |
227 | value is not copied for this level, we ignore ...Storage, and just point |
228 | at the corresponding value in the previous level in the stack. |
229 | */ |
230 | class SkCanvas::MCRec { |
231 | public: |
232 | DeviceCM* fLayer; |
233 | /* If there are any layers in the stack, this points to the top-most |
234 | one that is at or below this level in the stack (so we know what |
235 | bitmap/device to draw into from this level. This value is NOT |
236 | reference counted, since the real owner is either our fLayer field, |
237 | or a previous one in a lower level.) |
238 | */ |
239 | DeviceCM* fTopLayer; |
240 | std::unique_ptr<BackImage> fBackImage; |
241 | SkConservativeClip fRasterClip; |
242 | SkCanvasMatrix fMatrix; |
243 | int fDeferredSaveCount; |
244 | |
245 | MCRec() { |
246 | fLayer = nullptr; |
247 | fTopLayer = nullptr; |
248 | fMatrix.reset(); |
249 | fDeferredSaveCount = 0; |
250 | |
251 | // don't bother initializing fNext |
252 | inc_rec(); |
253 | } |
254 | MCRec(const MCRec& prev) : fRasterClip(prev.fRasterClip), fMatrix(prev.fMatrix) { |
255 | fLayer = nullptr; |
256 | fTopLayer = prev.fTopLayer; |
257 | fDeferredSaveCount = 0; |
258 | |
259 | // don't bother initializing fNext |
260 | inc_rec(); |
261 | } |
262 | ~MCRec() { |
263 | delete fLayer; |
264 | dec_rec(); |
265 | } |
266 | |
267 | void reset(const SkIRect& bounds) { |
268 | SkASSERT(fLayer); |
269 | SkASSERT(fDeferredSaveCount == 0); |
270 | |
271 | fMatrix.reset(); |
272 | fRasterClip.setRect(bounds); |
273 | fLayer->reset(bounds); |
274 | } |
275 | }; |
276 | |
277 | class SkDrawIter { |
278 | public: |
279 | SkDrawIter(SkCanvas* canvas) |
280 | : fDevice(nullptr), fCurrLayer(canvas->fMCRec->fTopLayer), fPaint(nullptr) |
281 | {} |
282 | |
283 | bool next() { |
284 | const DeviceCM* rec = fCurrLayer; |
285 | if (rec && rec->fDevice) { |
286 | fDevice = rec->fDevice.get(); |
287 | fPaint = rec->fPaint.get(); |
288 | fCurrLayer = rec->fNext; |
289 | // fCurrLayer may be nullptr now |
290 | return true; |
291 | } |
292 | return false; |
293 | } |
294 | |
295 | const SkPaint* getPaint() const { return fPaint; } |
296 | |
297 | SkBaseDevice* fDevice; |
298 | |
299 | private: |
300 | const DeviceCM* fCurrLayer; |
301 | const SkPaint* fPaint; // May be null. |
302 | }; |
303 | |
304 | #define FOR_EACH_TOP_DEVICE( code ) \ |
305 | do { \ |
306 | DeviceCM* layer = fMCRec->fTopLayer; \ |
307 | while (layer) { \ |
308 | SkBaseDevice* device = layer->fDevice.get(); \ |
309 | if (device) { \ |
310 | code; \ |
311 | } \ |
312 | layer = layer->fNext; \ |
313 | } \ |
314 | } while (0) |
315 | |
316 | ///////////////////////////////////////////////////////////////////////////// |
317 | |
318 | /** |
319 | * If the paint has an imagefilter, but it can be simplified to just a colorfilter, return that |
320 | * colorfilter, else return nullptr. |
321 | */ |
322 | static sk_sp<SkColorFilter> image_to_color_filter(const SkPaint& paint) { |
323 | SkImageFilter* imgf = paint.getImageFilter(); |
324 | if (!imgf) { |
325 | return nullptr; |
326 | } |
327 | |
328 | SkColorFilter* imgCFPtr; |
329 | if (!imgf->asAColorFilter(&imgCFPtr)) { |
330 | return nullptr; |
331 | } |
332 | sk_sp<SkColorFilter> imgCF(imgCFPtr); |
333 | |
334 | SkColorFilter* paintCF = paint.getColorFilter(); |
335 | if (nullptr == paintCF) { |
336 | // there is no existing paint colorfilter, so we can just return the imagefilter's |
337 | return imgCF; |
338 | } |
339 | |
340 | // The paint has both a colorfilter(paintCF) and an imagefilter-which-is-a-colorfilter(imgCF) |
341 | // and we need to combine them into a single colorfilter. |
342 | return imgCF->makeComposed(sk_ref_sp(paintCF)); |
343 | } |
344 | |
345 | /** |
346 | * There are many bounds in skia. A circle's bounds is just its center extended by its radius. |
347 | * However, if we stroke a circle, then the "bounds" of that is larger, since it will now draw |
348 | * outside of its raw-bounds by 1/2 the stroke width. SkPaint has lots of optional |
349 | * effects/attributes that can modify the effective bounds of a given primitive -- maskfilters, |
350 | * patheffects, stroking, etc. This function takes a raw bounds and a paint, and returns the |
351 | * conservative "effective" bounds based on the settings in the paint... with one exception. This |
352 | * function does *not* look at the imagefilter, which can also modify the effective bounds. It is |
353 | * deliberately ignored. |
354 | */ |
355 | static const SkRect& apply_paint_to_bounds_sans_imagefilter(const SkPaint& paint, |
356 | const SkRect& rawBounds, |
357 | SkRect* storage) { |
358 | SkPaint tmpUnfiltered(paint); |
359 | tmpUnfiltered.setImageFilter(nullptr); |
360 | if (tmpUnfiltered.canComputeFastBounds()) { |
361 | return tmpUnfiltered.computeFastBounds(rawBounds, storage); |
362 | } else { |
363 | return rawBounds; |
364 | } |
365 | } |
366 | |
367 | class AutoLayerForImageFilter { |
368 | public: |
369 | // "rawBounds" is the original bounds of the primitive about to be drawn, unmodified by the |
370 | // paint. It's used to determine the size of the offscreen layer for filters. |
371 | // If null, the clip will be used instead. |
372 | AutoLayerForImageFilter(SkCanvas* canvas, const SkPaint& origPaint, |
373 | bool skipLayerForImageFilter = false, |
374 | const SkRect* rawBounds = nullptr) { |
375 | fCanvas = canvas; |
376 | fPaint = &origPaint; |
377 | fSaveCount = canvas->getSaveCount(); |
378 | fTempLayerForImageFilter = false; |
379 | |
380 | if (auto simplifiedCF = image_to_color_filter(origPaint)) { |
381 | SkASSERT(!fLazyPaint.isValid()); |
382 | SkPaint* paint = fLazyPaint.set(origPaint); |
383 | paint->setColorFilter(std::move(simplifiedCF)); |
384 | paint->setImageFilter(nullptr); |
385 | fPaint = paint; |
386 | } |
387 | |
388 | if (!skipLayerForImageFilter && fPaint->getImageFilter()) { |
389 | /** |
390 | * We implement ImageFilters for a given draw by creating a layer, then applying the |
391 | * imagefilter to the pixels of that layer (its backing surface/image), and then |
392 | * we call restore() to xfer that layer to the main canvas. |
393 | * |
394 | * 1. SaveLayer (with a paint containing the current imagefilter and xfermode) |
395 | * 2. Generate the src pixels: |
396 | * Remove the imagefilter and the xfermode from the paint that we (AutoDrawLooper) |
397 | * return (fPaint). We then draw the primitive (using srcover) into a cleared |
398 | * buffer/surface. |
399 | * 3. Restore the layer created in #1 |
400 | * The imagefilter is passed the buffer/surface from the layer (now filled with the |
401 | * src pixels of the primitive). It returns a new "filtered" buffer, which we |
402 | * draw onto the previous layer using the xfermode from the original paint. |
403 | */ |
404 | |
405 | SkPaint restorePaint; |
406 | restorePaint.setImageFilter(fPaint->refImageFilter()); |
407 | restorePaint.setBlendMode(fPaint->getBlendMode()); |
408 | |
409 | SkRect storage; |
410 | if (rawBounds) { |
411 | // Make rawBounds include all paint outsets except for those due to image filters. |
412 | rawBounds = &apply_paint_to_bounds_sans_imagefilter(*fPaint, *rawBounds, &storage); |
413 | } |
414 | (void)canvas->internalSaveLayer(SkCanvas::SaveLayerRec(rawBounds, &restorePaint), |
415 | SkCanvas::kFullLayer_SaveLayerStrategy); |
416 | fTempLayerForImageFilter = true; |
417 | |
418 | // Remove the restorePaint fields from our "working" paint |
419 | SkASSERT(!fLazyPaint.isValid()); |
420 | SkPaint* paint = fLazyPaint.set(origPaint); |
421 | paint->setImageFilter(nullptr); |
422 | paint->setBlendMode(SkBlendMode::kSrcOver); |
423 | fPaint = paint; |
424 | } |
425 | } |
426 | |
427 | ~AutoLayerForImageFilter() { |
428 | if (fTempLayerForImageFilter) { |
429 | fCanvas->internalRestore(); |
430 | } |
431 | SkASSERT(fCanvas->getSaveCount() == fSaveCount); |
432 | } |
433 | |
434 | const SkPaint& paint() const { |
435 | SkASSERT(fPaint); |
436 | return *fPaint; |
437 | } |
438 | |
439 | private: |
440 | SkLazyPaint fLazyPaint; // base paint storage in case we need to modify it |
441 | SkCanvas* fCanvas; |
442 | const SkPaint* fPaint; // points to either the original paint, or lazy (if we needed it) |
443 | int fSaveCount; |
444 | bool fTempLayerForImageFilter; |
445 | }; |
446 | |
447 | ////////// macros to place around the internal draw calls ////////////////// |
448 | |
449 | #define DRAW_BEGIN_DRAWBITMAP(paint, skipLayerForFilter, bounds) \ |
450 | this->predrawNotify(); \ |
451 | AutoLayerForImageFilter draw(this, paint, skipLayerForFilter, bounds); \ |
452 | { SkDrawIter iter(this); |
453 | |
454 | |
455 | #define DRAW_BEGIN_DRAWDEVICE(paint) \ |
456 | this->predrawNotify(); \ |
457 | AutoLayerForImageFilter draw(this, paint, true); \ |
458 | { SkDrawIter iter(this); |
459 | |
460 | #define DRAW_BEGIN(paint, bounds) \ |
461 | this->predrawNotify(); \ |
462 | AutoLayerForImageFilter draw(this, paint, false, bounds); \ |
463 | { SkDrawIter iter(this); |
464 | |
465 | #define DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, bounds, auxOpaque) \ |
466 | this->predrawNotify(bounds, &paint, auxOpaque); \ |
467 | AutoLayerForImageFilter draw(this, paint, false, bounds); \ |
468 | { SkDrawIter iter(this); |
469 | |
470 | #define DRAW_END } |
471 | |
472 | //////////////////////////////////////////////////////////////////////////// |
473 | |
474 | static inline SkRect qr_clip_bounds(const SkIRect& bounds) { |
475 | if (bounds.isEmpty()) { |
476 | return SkRect::MakeEmpty(); |
477 | } |
478 | |
479 | // Expand bounds out by 1 in case we are anti-aliasing. We store the |
480 | // bounds as floats to enable a faster quick reject implementation. |
481 | SkRect dst; |
482 | SkNx_cast<float>(Sk4i::Load(&bounds.fLeft) + Sk4i(-1,-1,1,1)).store(&dst.fLeft); |
483 | return dst; |
484 | } |
485 | |
486 | void SkCanvas::resetForNextPicture(const SkIRect& bounds) { |
487 | this->restoreToCount(1); |
488 | fMCRec->reset(bounds); |
489 | |
490 | // We're peering through a lot of structs here. Only at this scope do we |
491 | // know that the device is a SkNoPixelsDevice. |
492 | static_cast<SkNoPixelsDevice*>(fMCRec->fLayer->fDevice.get())->resetForNextPicture(bounds); |
493 | fDeviceClipBounds = qr_clip_bounds(bounds); |
494 | fIsScaleTranslate = true; |
495 | } |
496 | |
497 | void SkCanvas::init(sk_sp<SkBaseDevice> device) { |
498 | fSaveCount = 1; |
499 | |
500 | fMCRec = (MCRec*)fMCStack.push_back(); |
501 | new (fMCRec) MCRec; |
502 | fMCRec->fRasterClip.setDeviceClipRestriction(&fClipRestrictionRect); |
503 | fIsScaleTranslate = true; |
504 | |
505 | SkASSERT(sizeof(DeviceCM) <= sizeof(fDeviceCMStorage)); |
506 | fMCRec->fLayer = (DeviceCM*)fDeviceCMStorage; |
507 | new (fDeviceCMStorage) DeviceCM(device, nullptr, fMCRec->fMatrix, nullptr, nullptr); |
508 | |
509 | fMCRec->fTopLayer = fMCRec->fLayer; |
510 | |
511 | fSurfaceBase = nullptr; |
512 | |
513 | if (device) { |
514 | // The root device and the canvas should always have the same pixel geometry |
515 | SkASSERT(fProps.pixelGeometry() == device->surfaceProps().pixelGeometry()); |
516 | fMCRec->fRasterClip.setRect(device->getGlobalBounds()); |
517 | fDeviceClipBounds = qr_clip_bounds(device->getGlobalBounds()); |
518 | |
519 | device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect); |
520 | } |
521 | |
522 | fScratchGlyphRunBuilder = std::make_unique<SkGlyphRunBuilder>(); |
523 | } |
524 | |
525 | SkCanvas::SkCanvas() |
526 | : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) |
527 | , fProps(SkSurfaceProps::kLegacyFontHost_InitType) |
528 | { |
529 | inc_canvas(); |
530 | |
531 | this->init(nullptr); |
532 | } |
533 | |
534 | SkCanvas::SkCanvas(int width, int height, const SkSurfaceProps* props) |
535 | : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) |
536 | , fProps(SkSurfacePropsCopyOrDefault(props)) |
537 | { |
538 | inc_canvas(); |
539 | this->init(sk_make_sp<SkNoPixelsDevice>( |
540 | SkIRect::MakeWH(std::max(width, 0), std::max(height, 0)), fProps)); |
541 | } |
542 | |
543 | SkCanvas::SkCanvas(const SkIRect& bounds) |
544 | : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) |
545 | , fProps(SkSurfaceProps::kLegacyFontHost_InitType) |
546 | { |
547 | inc_canvas(); |
548 | |
549 | SkIRect r = bounds.isEmpty() ? SkIRect::MakeEmpty() : bounds; |
550 | this->init(sk_make_sp<SkNoPixelsDevice>(r, fProps)); |
551 | } |
552 | |
553 | SkCanvas::SkCanvas(sk_sp<SkBaseDevice> device) |
554 | : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) |
555 | , fProps(device->surfaceProps()) |
556 | { |
557 | inc_canvas(); |
558 | |
559 | this->init(device); |
560 | } |
561 | |
562 | SkCanvas::SkCanvas(const SkBitmap& bitmap, const SkSurfaceProps& props) |
563 | : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) |
564 | , fProps(props) |
565 | { |
566 | inc_canvas(); |
567 | |
568 | sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, nullptr, nullptr)); |
569 | this->init(device); |
570 | } |
571 | |
572 | SkCanvas::SkCanvas(const SkBitmap& bitmap, std::unique_ptr<SkRasterHandleAllocator> alloc, |
573 | SkRasterHandleAllocator::Handle hndl) |
574 | : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) |
575 | , fProps(SkSurfaceProps::kLegacyFontHost_InitType) |
576 | , fAllocator(std::move(alloc)) |
577 | { |
578 | inc_canvas(); |
579 | |
580 | sk_sp<SkBaseDevice> device(new SkBitmapDevice(bitmap, fProps, hndl, nullptr)); |
581 | this->init(device); |
582 | } |
583 | |
584 | SkCanvas::SkCanvas(const SkBitmap& bitmap) : SkCanvas(bitmap, nullptr, nullptr) {} |
585 | |
586 | #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK |
587 | SkCanvas::SkCanvas(const SkBitmap& bitmap, ColorBehavior) |
588 | : fMCStack(sizeof(MCRec), fMCRecStorage, sizeof(fMCRecStorage)) |
589 | , fProps(SkSurfaceProps::kLegacyFontHost_InitType) |
590 | , fAllocator(nullptr) |
591 | { |
592 | inc_canvas(); |
593 | |
594 | SkBitmap tmp(bitmap); |
595 | *const_cast<SkImageInfo*>(&tmp.info()) = tmp.info().makeColorSpace(nullptr); |
596 | sk_sp<SkBaseDevice> device(new SkBitmapDevice(tmp, fProps, nullptr, nullptr)); |
597 | this->init(device); |
598 | } |
599 | #endif |
600 | |
601 | SkCanvas::~SkCanvas() { |
602 | // free up the contents of our deque |
603 | this->restoreToCount(1); // restore everything but the last |
604 | |
605 | this->internalRestore(); // restore the last, since we're going away |
606 | |
607 | dec_canvas(); |
608 | } |
609 | |
610 | /////////////////////////////////////////////////////////////////////////////// |
611 | |
612 | void SkCanvas::flush() { |
613 | this->onFlush(); |
614 | } |
615 | |
616 | void SkCanvas::onFlush() { |
617 | SkBaseDevice* device = this->getDevice(); |
618 | if (device) { |
619 | device->flush(); |
620 | } |
621 | } |
622 | |
623 | SkSurface* SkCanvas::getSurface() const { |
624 | return fSurfaceBase; |
625 | } |
626 | |
627 | SkISize SkCanvas::getBaseLayerSize() const { |
628 | SkBaseDevice* d = this->getDevice(); |
629 | return d ? SkISize::Make(d->width(), d->height()) : SkISize::Make(0, 0); |
630 | } |
631 | |
632 | SkIRect SkCanvas::getTopLayerBounds() const { |
633 | SkBaseDevice* d = this->getTopDevice(); |
634 | if (!d) { |
635 | return SkIRect::MakeEmpty(); |
636 | } |
637 | return d->getGlobalBounds(); |
638 | } |
639 | |
640 | SkBaseDevice* SkCanvas::getDevice() const { |
641 | // return root device |
642 | MCRec* rec = (MCRec*) fMCStack.front(); |
643 | SkASSERT(rec && rec->fLayer); |
644 | return rec->fLayer->fDevice.get(); |
645 | } |
646 | |
647 | SkBaseDevice* SkCanvas::getTopDevice() const { |
648 | return fMCRec->fTopLayer->fDevice.get(); |
649 | } |
650 | |
651 | bool SkCanvas::readPixels(const SkPixmap& pm, int x, int y) { |
652 | SkBaseDevice* device = this->getDevice(); |
653 | return device && pm.addr() && device->readPixels(pm, x, y); |
654 | } |
655 | |
656 | bool SkCanvas::readPixels(const SkImageInfo& dstInfo, void* dstP, size_t rowBytes, int x, int y) { |
657 | return this->readPixels({ dstInfo, dstP, rowBytes}, x, y); |
658 | } |
659 | |
660 | bool SkCanvas::readPixels(const SkBitmap& bm, int x, int y) { |
661 | SkPixmap pm; |
662 | return bm.peekPixels(&pm) && this->readPixels(pm, x, y); |
663 | } |
664 | |
665 | bool SkCanvas::writePixels(const SkBitmap& bitmap, int x, int y) { |
666 | SkPixmap pm; |
667 | if (bitmap.peekPixels(&pm)) { |
668 | return this->writePixels(pm.info(), pm.addr(), pm.rowBytes(), x, y); |
669 | } |
670 | return false; |
671 | } |
672 | |
673 | bool SkCanvas::writePixels(const SkImageInfo& srcInfo, const void* pixels, size_t rowBytes, |
674 | int x, int y) { |
675 | SkBaseDevice* device = this->getDevice(); |
676 | if (!device) { |
677 | return false; |
678 | } |
679 | |
680 | // This check gives us an early out and prevents generation ID churn on the surface. |
681 | // This is purely optional: it is a subset of the checks performed by SkWritePixelsRec. |
682 | SkIRect srcRect = SkIRect::MakeXYWH(x, y, srcInfo.width(), srcInfo.height()); |
683 | if (!srcRect.intersect({0, 0, device->width(), device->height()})) { |
684 | return false; |
685 | } |
686 | |
687 | // Tell our owning surface to bump its generation ID. |
688 | const bool completeOverwrite = |
689 | srcRect.size() == SkISize::Make(device->width(), device->height()); |
690 | this->predrawNotify(completeOverwrite); |
691 | |
692 | // This can still fail, most notably in the case of a invalid color type or alpha type |
693 | // conversion. We could pull those checks into this function and avoid the unnecessary |
694 | // generation ID bump. But then we would be performing those checks twice, since they |
695 | // are also necessary at the bitmap/pixmap entry points. |
696 | return device->writePixels({srcInfo, pixels, rowBytes}, x, y); |
697 | } |
698 | |
699 | ////////////////////////////////////////////////////////////////////////////// |
700 | |
701 | void SkCanvas::checkForDeferredSave() { |
702 | if (fMCRec->fDeferredSaveCount > 0) { |
703 | this->doSave(); |
704 | } |
705 | } |
706 | |
707 | int SkCanvas::getSaveCount() const { |
708 | #ifdef SK_DEBUG |
709 | int count = 0; |
710 | SkDeque::Iter iter(fMCStack, SkDeque::Iter::kFront_IterStart); |
711 | for (;;) { |
712 | const MCRec* rec = (const MCRec*)iter.next(); |
713 | if (!rec) { |
714 | break; |
715 | } |
716 | count += 1 + rec->fDeferredSaveCount; |
717 | } |
718 | SkASSERT(count == fSaveCount); |
719 | #endif |
720 | return fSaveCount; |
721 | } |
722 | |
723 | int SkCanvas::save() { |
724 | fSaveCount += 1; |
725 | fMCRec->fDeferredSaveCount += 1; |
726 | return this->getSaveCount() - 1; // return our prev value |
727 | } |
728 | |
729 | void SkCanvas::doSave() { |
730 | this->willSave(); |
731 | |
732 | SkASSERT(fMCRec->fDeferredSaveCount > 0); |
733 | fMCRec->fDeferredSaveCount -= 1; |
734 | this->internalSave(); |
735 | } |
736 | |
737 | void SkCanvas::restore() { |
738 | if (fMCRec->fDeferredSaveCount > 0) { |
739 | SkASSERT(fSaveCount > 1); |
740 | fSaveCount -= 1; |
741 | fMCRec->fDeferredSaveCount -= 1; |
742 | } else { |
743 | // check for underflow |
744 | if (fMCStack.count() > 1) { |
745 | this->willRestore(); |
746 | SkASSERT(fSaveCount > 1); |
747 | fSaveCount -= 1; |
748 | this->internalRestore(); |
749 | this->didRestore(); |
750 | } |
751 | } |
752 | } |
753 | |
754 | void SkCanvas::restoreToCount(int count) { |
755 | // sanity check |
756 | if (count < 1) { |
757 | count = 1; |
758 | } |
759 | |
760 | int n = this->getSaveCount() - count; |
761 | for (int i = 0; i < n; ++i) { |
762 | this->restore(); |
763 | } |
764 | } |
765 | |
766 | void SkCanvas::internalSave() { |
767 | MCRec* newTop = (MCRec*)fMCStack.push_back(); |
768 | new (newTop) MCRec(*fMCRec); // balanced in restore() |
769 | fMCRec = newTop; |
770 | |
771 | FOR_EACH_TOP_DEVICE(device->save()); |
772 | } |
773 | |
774 | bool SkCanvas::BoundsAffectsClip(SaveLayerFlags saveLayerFlags) { |
775 | return !(saveLayerFlags & SkCanvasPriv::kDontClipToLayer_SaveLayerFlag); |
776 | } |
777 | |
778 | bool SkCanvas::clipRectBounds(const SkRect* bounds, SaveLayerFlags saveLayerFlags, |
779 | SkIRect* intersection, const SkImageFilter* imageFilter) { |
780 | // clipRectBounds() is called to determine the input layer size needed for a given image filter. |
781 | // The coordinate space of the rectangle passed to filterBounds(kReverse) is meant to be in the |
782 | // filtering layer space. Here, 'clipBounds' is always in the true device space. When an image |
783 | // filter does not require a decomposed CTM matrix, the filter space and device space are the |
784 | // same. When it has been decomposed, we want the original image filter node to process the |
785 | // bounds in the layer space represented by the decomposed scale matrix. 'imageFilter' is no |
786 | // longer the original filter, but has the remainder matrix baked into it, and passing in the |
787 | // the true device clip bounds ensures that the matrix image filter provides a layer clip bounds |
788 | // to the original filter node (barring inflation from consecutive calls to mapRect). While |
789 | // initially counter-intuitive given the apparent inconsistency of coordinate spaces, always |
790 | // passing getDeviceClipBounds() to 'imageFilter' is correct. |
791 | // FIXME (michaelludwig) - When the remainder matrix is instead applied as a final draw, it will |
792 | // be important to more accurately calculate the clip bounds in the layer space for the original |
793 | // image filter (similar to how matrix image filter does it, but ideally without the inflation). |
794 | SkIRect clipBounds = this->getDeviceClipBounds(); |
795 | if (clipBounds.isEmpty()) { |
796 | return false; |
797 | } |
798 | |
799 | const SkMatrix& ctm = fMCRec->fMatrix; // this->getTotalMatrix() |
800 | |
801 | if (imageFilter && bounds && !imageFilter->canComputeFastBounds()) { |
802 | // If the image filter DAG affects transparent black then we will need to render |
803 | // out to the clip bounds |
804 | bounds = nullptr; |
805 | } |
806 | |
807 | SkIRect inputSaveLayerBounds; |
808 | if (bounds) { |
809 | SkRect r; |
810 | ctm.mapRect(&r, *bounds); |
811 | r.roundOut(&inputSaveLayerBounds); |
812 | } else { // no user bounds, so just use the clip |
813 | inputSaveLayerBounds = clipBounds; |
814 | } |
815 | |
816 | if (imageFilter) { |
817 | // expand the clip bounds by the image filter DAG to include extra content that might |
818 | // be required by the image filters. |
819 | clipBounds = imageFilter->filterBounds(clipBounds, ctm, |
820 | SkImageFilter::kReverse_MapDirection, |
821 | &inputSaveLayerBounds); |
822 | } |
823 | |
824 | SkIRect clippedSaveLayerBounds; |
825 | if (bounds) { |
826 | // For better or for worse, user bounds currently act as a hard clip on the layer's |
827 | // extent (i.e., they implement the CSS filter-effects 'filter region' feature). |
828 | clippedSaveLayerBounds = inputSaveLayerBounds; |
829 | } else { |
830 | // If there are no user bounds, we don't want to artificially restrict the resulting |
831 | // layer bounds, so allow the expanded clip bounds free reign. |
832 | clippedSaveLayerBounds = clipBounds; |
833 | } |
834 | |
835 | // early exit if the layer's bounds are clipped out |
836 | if (!clippedSaveLayerBounds.intersect(clipBounds)) { |
837 | if (BoundsAffectsClip(saveLayerFlags)) { |
838 | fMCRec->fTopLayer->fDevice->clipRegion(SkRegion(), SkClipOp::kIntersect); // empty |
839 | fMCRec->fRasterClip.setEmpty(); |
840 | fDeviceClipBounds.setEmpty(); |
841 | } |
842 | return false; |
843 | } |
844 | SkASSERT(!clippedSaveLayerBounds.isEmpty()); |
845 | |
846 | if (BoundsAffectsClip(saveLayerFlags)) { |
847 | // Simplify the current clips since they will be applied properly during restore() |
848 | fMCRec->fRasterClip.setRect(clippedSaveLayerBounds); |
849 | fDeviceClipBounds = qr_clip_bounds(clippedSaveLayerBounds); |
850 | } |
851 | |
852 | if (intersection) { |
853 | *intersection = clippedSaveLayerBounds; |
854 | } |
855 | |
856 | return true; |
857 | } |
858 | |
859 | int SkCanvas::saveLayer(const SkRect* bounds, const SkPaint* paint) { |
860 | return this->saveLayer(SaveLayerRec(bounds, paint, 0)); |
861 | } |
862 | |
863 | int SkCanvas::saveLayer(const SaveLayerRec& rec) { |
864 | TRACE_EVENT0("skia" , TRACE_FUNC); |
865 | if (rec.fPaint && rec.fPaint->nothingToDraw()) { |
866 | // no need for the layer (or any of the draws until the matching restore() |
867 | this->save(); |
868 | this->clipRect({0,0,0,0}); |
869 | } else { |
870 | SaveLayerStrategy strategy = this->getSaveLayerStrategy(rec); |
871 | fSaveCount += 1; |
872 | this->internalSaveLayer(rec, strategy); |
873 | } |
874 | return this->getSaveCount() - 1; |
875 | } |
876 | |
877 | int SkCanvas::only_axis_aligned_saveBehind(const SkRect* bounds) { |
878 | if (bounds && !this->getLocalClipBounds().intersects(*bounds)) { |
879 | // Assuming clips never expand, if the request bounds is outside of the current clip |
880 | // there is no need to copy/restore the area, so just devolve back to a regular save. |
881 | this->save(); |
882 | } else { |
883 | bool doTheWork = this->onDoSaveBehind(bounds); |
884 | fSaveCount += 1; |
885 | this->internalSave(); |
886 | if (doTheWork) { |
887 | this->internalSaveBehind(bounds); |
888 | } |
889 | } |
890 | return this->getSaveCount() - 1; |
891 | } |
892 | |
893 | void SkCanvas::DrawDeviceWithFilter(SkBaseDevice* src, const SkImageFilter* filter, |
894 | SkBaseDevice* dst, const SkIPoint& dstOrigin, |
895 | const SkMatrix& ctm) { |
896 | // The local bounds of the src device; all the bounds passed to snapSpecial must be intersected |
897 | // with this rect. |
898 | const SkIRect srcDevRect = SkIRect::MakeWH(src->width(), src->height()); |
899 | // TODO(michaelludwig) - Update this function to use the relative transforms between src and |
900 | // dst; for now, since devices never have complex transforms, we can keep using getOrigin(). |
901 | if (!filter) { |
902 | // All non-filtered devices are currently axis aligned, so they only differ by their origin. |
903 | // This means that we only have to copy a dst-sized block of pixels out of src and translate |
904 | // it to the matching position relative to dst's origin. |
905 | SkIRect snapBounds = SkIRect::MakeXYWH(dstOrigin.x() - src->getOrigin().x(), |
906 | dstOrigin.y() - src->getOrigin().y(), |
907 | dst->width(), dst->height()); |
908 | if (!snapBounds.intersect(srcDevRect)) { |
909 | return; |
910 | } |
911 | |
912 | auto special = src->snapSpecial(snapBounds); |
913 | if (special) { |
914 | // The image is drawn at 1-1 scale with integer translation, so no filtering is needed. |
915 | SkPaint p; |
916 | dst->drawSpecial(special.get(), 0, 0, p, nullptr, SkMatrix::I()); |
917 | } |
918 | return; |
919 | } |
920 | |
921 | // First decompose the ctm into a post-filter transform and a filter matrix that is supported |
922 | // by the backdrop filter. |
923 | SkMatrix toRoot, layerMatrix; |
924 | SkSize scale; |
925 | if (ctm.isScaleTranslate() || as_IFB(filter)->canHandleComplexCTM()) { |
926 | toRoot = SkMatrix::I(); |
927 | layerMatrix = ctm; |
928 | } else if (ctm.decomposeScale(&scale, &toRoot)) { |
929 | layerMatrix = SkMatrix::MakeScale(scale.fWidth, scale.fHeight); |
930 | } else { |
931 | // Perspective, for now, do no scaling of the layer itself. |
932 | // TODO (michaelludwig) - perhaps it'd be better to explore a heuristic scale pulled from |
933 | // the matrix, e.g. based on the midpoint of the near/far planes? |
934 | toRoot = ctm; |
935 | layerMatrix = SkMatrix::I(); |
936 | } |
937 | |
938 | // We have to map the dst bounds from the root space into the layer space where filtering will |
939 | // occur. If we knew the input bounds of the content that defined the original dst bounds, we |
940 | // could map that forward by layerMatrix and have tighter bounds, but toRoot^-1 * dst bounds |
941 | // is a safe, conservative estimate. |
942 | SkMatrix fromRoot; |
943 | if (!toRoot.invert(&fromRoot)) { |
944 | return; |
945 | } |
946 | |
947 | // This represents what the backdrop filter needs to produce in the layer space, and is sized |
948 | // such that drawing it into dst with the toRoot transform will cover the actual dst device. |
949 | SkIRect layerTargetBounds = fromRoot.mapRect( |
950 | SkRect::MakeXYWH(dstOrigin.x(), dstOrigin.y(), dst->width(), dst->height())).roundOut(); |
951 | // While layerTargetBounds is what needs to be output by the filter, the filtering process may |
952 | // require some extra input pixels. |
953 | SkIRect layerInputBounds = filter->filterBounds( |
954 | layerTargetBounds, layerMatrix, SkImageFilter::kReverse_MapDirection, |
955 | &layerTargetBounds); |
956 | |
957 | // Map the required input into the root space, then make relative to the src device. This will |
958 | // be the conservative contents required to fill a layerInputBounds-sized surface with the |
959 | // backdrop content (transformed back into the layer space using fromRoot). |
960 | SkIRect backdropBounds = toRoot.mapRect(SkRect::Make(layerInputBounds)).roundOut(); |
961 | backdropBounds.offset(-src->getOrigin().x(), -src->getOrigin().y()); |
962 | if (!backdropBounds.intersect(srcDevRect)) { |
963 | return; |
964 | } |
965 | |
966 | auto special = src->snapSpecial(backdropBounds); |
967 | if (!special) { |
968 | return; |
969 | } |
970 | |
971 | SkColorType colorType = src->imageInfo().colorType(); |
972 | if (colorType == kUnknown_SkColorType) { |
973 | colorType = kRGBA_8888_SkColorType; |
974 | } |
975 | SkColorSpace* colorSpace = src->imageInfo().colorSpace(); |
976 | |
977 | SkPaint p; |
978 | if (!toRoot.isIdentity()) { |
979 | // Drawing the temporary and final filtered image requires a higher filter quality if the |
980 | // 'toRoot' transformation is not identity, in order to minimize the impact on already |
981 | // rendered edges/content. |
982 | // TODO (michaelludwig) - Explore reducing this quality, identify visual tradeoffs |
983 | p.setFilterQuality(kHigh_SkFilterQuality); |
984 | |
985 | // The snapped backdrop content needs to be transformed by fromRoot into the layer space, |
986 | // and stored in a temporary surface, which is then used as the input to the actual filter. |
987 | auto tmpSurface = special->makeSurface(colorType, colorSpace, layerInputBounds.size()); |
988 | if (!tmpSurface) { |
989 | return; |
990 | } |
991 | |
992 | auto tmpCanvas = tmpSurface->getCanvas(); |
993 | tmpCanvas->clear(SK_ColorTRANSPARENT); |
994 | // Reading in reverse, this takes the backdrop bounds from src device space into the root |
995 | // space, then maps from root space into the layer space, then maps it so the input layer's |
996 | // top left corner is (0, 0). This transformation automatically accounts for any cropping |
997 | // performed on backdropBounds. |
998 | tmpCanvas->translate(-layerInputBounds.fLeft, -layerInputBounds.fTop); |
999 | tmpCanvas->concat(fromRoot); |
1000 | tmpCanvas->translate(src->getOrigin().x(), src->getOrigin().y()); |
1001 | |
1002 | tmpCanvas->drawImageRect(special->asImage(), special->subset(), |
1003 | SkRect::Make(backdropBounds), &p, kStrict_SrcRectConstraint); |
1004 | special = tmpSurface->makeImageSnapshot(); |
1005 | } else { |
1006 | // Since there is no extra transform that was done, update the input bounds to reflect |
1007 | // cropping of the snapped backdrop image. In this case toRoot = I, so layerInputBounds |
1008 | // was equal to backdropBounds before it was made relative to the src device and cropped. |
1009 | // When we use the original snapped image directly, just map the update backdrop bounds |
1010 | // back into the shared layer space |
1011 | layerInputBounds = backdropBounds; |
1012 | layerInputBounds.offset(src->getOrigin().x(), src->getOrigin().y()); |
1013 | |
1014 | // Similar to the unfiltered case above, when toRoot is the identity, then the final |
1015 | // draw will be 1-1 so there is no need to increase filter quality. |
1016 | p.setFilterQuality(kNone_SkFilterQuality); |
1017 | } |
1018 | |
1019 | // Now evaluate the filter on 'special', which contains the backdrop content mapped back into |
1020 | // layer space. This has to further offset everything so that filter evaluation thinks the |
1021 | // source image's top left corner is (0, 0). |
1022 | // TODO (michaelludwig) - Once image filters are robust to non-(0,0) image origins for inputs, |
1023 | // this can be simplified. |
1024 | layerTargetBounds.offset(-layerInputBounds.fLeft, -layerInputBounds.fTop); |
1025 | SkMatrix filterCTM = layerMatrix; |
1026 | filterCTM.postTranslate(-layerInputBounds.fLeft, -layerInputBounds.fTop); |
1027 | skif::Context ctx(filterCTM, layerTargetBounds, nullptr, colorType, colorSpace, special.get()); |
1028 | |
1029 | SkIPoint offset; |
1030 | special = as_IFB(filter)->filterImage(ctx).imageAndOffset(&offset); |
1031 | if (special) { |
1032 | // Draw the filtered backdrop content into the dst device. We add layerInputBounds origin |
1033 | // to offset because the original value in 'offset' was relative to 'filterCTM'. 'filterCTM' |
1034 | // had subtracted the layerInputBounds origin, so adding that back makes 'offset' relative |
1035 | // to 'layerMatrix' (what we need it to be when drawing the image by 'toRoot'). |
1036 | offset += layerInputBounds.topLeft(); |
1037 | |
1038 | // Manually setting the device's CTM requires accounting for the device's origin. |
1039 | // TODO (michaelludwig) - This could be simpler if the dst device had its origin configured |
1040 | // before filtering the backdrop device, and if SkAutoDeviceTransformRestore had a way to accept |
1041 | // a global CTM instead of a device CTM. |
1042 | SkMatrix dstCTM = toRoot; |
1043 | dstCTM.postTranslate(-dstOrigin.x(), -dstOrigin.y()); |
1044 | SkAutoDeviceTransformRestore adr(dst, dstCTM); |
1045 | |
1046 | // And because devices don't have a special-image draw function that supports arbitrary |
1047 | // matrices, we are abusing the asImage() functionality here... |
1048 | SkRect specialSrc = SkRect::Make(special->subset()); |
1049 | auto looseImage = special->asImage(); |
1050 | dst->drawImageRect( |
1051 | looseImage.get(), &specialSrc, |
1052 | SkRect::MakeXYWH(offset.x(), offset.y(), special->width(), special->height()), |
1053 | p, kStrict_SrcRectConstraint); |
1054 | } |
1055 | } |
1056 | |
1057 | static SkImageInfo make_layer_info(const SkImageInfo& prev, int w, int h, const SkPaint* paint) { |
1058 | SkColorType ct = prev.colorType(); |
1059 | if (prev.bytesPerPixel() <= 4 && |
1060 | prev.colorType() != kRGBA_8888_SkColorType && |
1061 | prev.colorType() != kBGRA_8888_SkColorType) { |
1062 | // "Upgrade" A8, G8, 565, 4444, 1010102, 101010x, and 888x to 8888, |
1063 | // ensuring plenty of alpha bits for the layer, perhaps losing some color bits in return. |
1064 | ct = kN32_SkColorType; |
1065 | } |
1066 | return SkImageInfo::Make(w, h, ct, kPremul_SkAlphaType, prev.refColorSpace()); |
1067 | } |
1068 | |
1069 | void SkCanvas::internalSaveLayer(const SaveLayerRec& rec, SaveLayerStrategy strategy) { |
1070 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1071 | const SkRect* bounds = rec.fBounds; |
1072 | SaveLayerFlags saveLayerFlags = rec.fSaveLayerFlags; |
1073 | |
1074 | SkTCopyOnFirstWrite<SkPaint> paint(rec.fPaint); |
1075 | // saveLayer ignores mask filters, so force it to null |
1076 | if (paint.get() && paint->getMaskFilter()) { |
1077 | paint.writable()->setMaskFilter(nullptr); |
1078 | } |
1079 | |
1080 | // If we have a backdrop filter, then we must apply it to the entire layer (clip-bounds) |
1081 | // regardless of any hint-rect from the caller. skbug.com/8783 |
1082 | if (rec.fBackdrop) { |
1083 | bounds = nullptr; |
1084 | } |
1085 | |
1086 | SkImageFilter* imageFilter = paint.get() ? paint->getImageFilter() : nullptr; |
1087 | SkMatrix stashedMatrix = fMCRec->fMatrix; |
1088 | MCRec* modifiedRec = nullptr; |
1089 | |
1090 | /* |
1091 | * Many ImageFilters (so far) do not (on their own) correctly handle matrices (CTM) that |
1092 | * contain rotation/skew/etc. We rely on applyCTM to create a new image filter DAG as needed to |
1093 | * accommodate this, but it requires update the CTM we use when drawing into the layer. |
1094 | * |
1095 | * 1. Stash off the current CTM |
1096 | * 2. Apply the CTM to imagefilter, which decomposes it into simple and complex transforms |
1097 | * if necessary. |
1098 | * 3. Wack the CTM to be the remaining scale matrix and use the modified imagefilter, which |
1099 | * is a MatrixImageFilter that contains the complex matrix. |
1100 | * 4. Proceed as usual, allowing the client to draw into the layer (now with a scale-only CTM) |
1101 | * 5. During restore, the MatrixImageFilter automatically applies complex stage to the output |
1102 | * of the original imagefilter, and draw that (via drawSprite) |
1103 | * 6. Unwack the CTM to its original state (i.e. stashedMatrix) |
1104 | * |
1105 | * Perhaps in the future we could augment #5 to apply REMAINDER as part of the draw (no longer |
1106 | * a sprite operation) to avoid the extra buffer/overhead of MatrixImageFilter. |
1107 | */ |
1108 | if (imageFilter) { |
1109 | SkMatrix modifiedCTM; |
1110 | sk_sp<SkImageFilter> modifiedFilter = as_IFB(imageFilter)->applyCTM(stashedMatrix, |
1111 | &modifiedCTM); |
1112 | if (as_IFB(modifiedFilter)->uniqueID() != as_IFB(imageFilter)->uniqueID()) { |
1113 | // The original filter couldn't support the CTM entirely |
1114 | SkASSERT(modifiedCTM.isScaleTranslate() || as_IFB(imageFilter)->canHandleComplexCTM()); |
1115 | modifiedRec = fMCRec; |
1116 | this->internalSetMatrix(modifiedCTM); |
1117 | imageFilter = modifiedFilter.get(); |
1118 | paint.writable()->setImageFilter(std::move(modifiedFilter)); |
1119 | } |
1120 | // Else the filter didn't change, so modifiedCTM == stashedMatrix and there's nothing |
1121 | // left to do since the stack already has that as the CTM. |
1122 | } |
1123 | |
1124 | // do this before we create the layer. We don't call the public save() since |
1125 | // that would invoke a possibly overridden virtual |
1126 | this->internalSave(); |
1127 | |
1128 | SkIRect ir; |
1129 | if (!this->clipRectBounds(bounds, saveLayerFlags, &ir, imageFilter)) { |
1130 | if (modifiedRec) { |
1131 | // In this case there will be no layer in which to stash the matrix so we need to |
1132 | // revert the prior MCRec to its earlier state. |
1133 | modifiedRec->fMatrix = stashedMatrix; |
1134 | } |
1135 | return; |
1136 | } |
1137 | |
1138 | // FIXME: do willSaveLayer() overriders returning kNoLayer_SaveLayerStrategy really care about |
1139 | // the clipRectBounds() call above? |
1140 | if (kNoLayer_SaveLayerStrategy == strategy) { |
1141 | return; |
1142 | } |
1143 | |
1144 | SkPixelGeometry geo = fProps.pixelGeometry(); |
1145 | if (paint) { |
1146 | // TODO: perhaps add a query to filters so we might preserve opaqueness... |
1147 | if (paint->getImageFilter() || paint->getColorFilter()) { |
1148 | geo = kUnknown_SkPixelGeometry; |
1149 | } |
1150 | } |
1151 | |
1152 | SkBaseDevice* priorDevice = this->getTopDevice(); |
1153 | if (nullptr == priorDevice) { // Do we still need this check??? |
1154 | SkDebugf("Unable to find device for layer." ); |
1155 | return; |
1156 | } |
1157 | |
1158 | SkImageInfo info = make_layer_info(priorDevice->imageInfo(), ir.width(), ir.height(), paint); |
1159 | if (rec.fSaveLayerFlags & kF16ColorType) { |
1160 | info = info.makeColorType(kRGBA_F16_SkColorType); |
1161 | } |
1162 | |
1163 | sk_sp<SkBaseDevice> newDevice; |
1164 | { |
1165 | SkASSERT(info.alphaType() != kOpaque_SkAlphaType); |
1166 | const SkBaseDevice::TileUsage usage = SkBaseDevice::kNever_TileUsage; |
1167 | const bool trackCoverage = |
1168 | SkToBool(saveLayerFlags & kMaskAgainstCoverage_EXPERIMENTAL_DONT_USE_SaveLayerFlag); |
1169 | const SkBaseDevice::CreateInfo createInfo = SkBaseDevice::CreateInfo(info, usage, geo, |
1170 | trackCoverage, |
1171 | fAllocator.get()); |
1172 | newDevice.reset(priorDevice->onCreateDevice(createInfo, paint)); |
1173 | if (!newDevice) { |
1174 | return; |
1175 | } |
1176 | } |
1177 | DeviceCM* layer = new DeviceCM(newDevice, paint, stashedMatrix, rec.fClipMask, rec.fClipMatrix); |
1178 | |
1179 | // only have a "next" if this new layer doesn't affect the clip (rare) |
1180 | layer->fNext = BoundsAffectsClip(saveLayerFlags) ? nullptr : fMCRec->fTopLayer; |
1181 | fMCRec->fLayer = layer; |
1182 | fMCRec->fTopLayer = layer; // this field is NOT an owner of layer |
1183 | |
1184 | if ((rec.fSaveLayerFlags & kInitWithPrevious_SaveLayerFlag) || rec.fBackdrop) { |
1185 | DrawDeviceWithFilter(priorDevice, rec.fBackdrop, newDevice.get(), { ir.fLeft, ir.fTop }, |
1186 | fMCRec->fMatrix); |
1187 | } |
1188 | |
1189 | newDevice->setOrigin(fMCRec->fMatrix, ir.fLeft, ir.fTop); |
1190 | |
1191 | newDevice->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect); |
1192 | if (layer->fNext) { |
1193 | // need to punch a hole in the previous device, so we don't draw there, given that |
1194 | // the new top-layer will allow drawing to happen "below" it. |
1195 | SkRegion hole(ir); |
1196 | do { |
1197 | layer = layer->fNext; |
1198 | layer->fDevice->clipRegion(hole, SkClipOp::kDifference); |
1199 | } while (layer->fNext); |
1200 | } |
1201 | } |
1202 | |
1203 | int SkCanvas::saveLayerAlpha(const SkRect* bounds, U8CPU alpha) { |
1204 | if (0xFF == alpha) { |
1205 | return this->saveLayer(bounds, nullptr); |
1206 | } else { |
1207 | SkPaint tmpPaint; |
1208 | tmpPaint.setAlpha(alpha); |
1209 | return this->saveLayer(bounds, &tmpPaint); |
1210 | } |
1211 | } |
1212 | |
1213 | void SkCanvas::internalSaveBehind(const SkRect* localBounds) { |
1214 | SkBaseDevice* device = this->getTopDevice(); |
1215 | if (nullptr == device) { // Do we still need this check??? |
1216 | return; |
1217 | } |
1218 | |
1219 | // Map the local bounds into the top device's coordinate space (this is not |
1220 | // necessarily the full global CTM transform). |
1221 | SkIRect devBounds; |
1222 | if (localBounds) { |
1223 | SkRect tmp; |
1224 | device->localToDevice().mapRect(&tmp, *localBounds); |
1225 | if (!devBounds.intersect(tmp.round(), device->devClipBounds())) { |
1226 | devBounds.setEmpty(); |
1227 | } |
1228 | } else { |
1229 | devBounds = device->devClipBounds(); |
1230 | } |
1231 | if (devBounds.isEmpty()) { |
1232 | return; |
1233 | } |
1234 | |
1235 | // This is getting the special image from the current device, which is then drawn into (both by |
1236 | // a client, and the drawClippedToSaveBehind below). Since this is not saving a layer, with its |
1237 | // own device, we need to explicitly copy the back image contents so that its original content |
1238 | // is available when we splat it back later during restore. |
1239 | auto backImage = device->snapSpecial(devBounds, /* copy */ true); |
1240 | if (!backImage) { |
1241 | return; |
1242 | } |
1243 | |
1244 | // we really need the save, so we can wack the fMCRec |
1245 | this->checkForDeferredSave(); |
1246 | |
1247 | fMCRec->fBackImage.reset(new BackImage{std::move(backImage), devBounds.topLeft()}); |
1248 | |
1249 | SkPaint paint; |
1250 | paint.setBlendMode(SkBlendMode::kClear); |
1251 | this->drawClippedToSaveBehind(paint); |
1252 | } |
1253 | |
1254 | void SkCanvas::internalRestore() { |
1255 | SkASSERT(fMCStack.count() != 0); |
1256 | |
1257 | // reserve our layer (if any) |
1258 | DeviceCM* layer = fMCRec->fLayer; // may be null |
1259 | // now detach it from fMCRec so we can pop(). Gets freed after its drawn |
1260 | fMCRec->fLayer = nullptr; |
1261 | |
1262 | // move this out before we do the actual restore |
1263 | auto backImage = std::move(fMCRec->fBackImage); |
1264 | |
1265 | while (!fMarkerStack.empty() && fMarkerStack.back().fMCRec == fMCRec) { |
1266 | fMarkerStack.pop_back(); |
1267 | } |
1268 | |
1269 | // now do the normal restore() |
1270 | fMCRec->~MCRec(); // balanced in save() |
1271 | fMCStack.pop_back(); |
1272 | fMCRec = (MCRec*)fMCStack.back(); |
1273 | |
1274 | if (fMCRec) { |
1275 | FOR_EACH_TOP_DEVICE(device->restore(fMCRec->fMatrix)); |
1276 | } |
1277 | |
1278 | if (backImage) { |
1279 | SkPaint paint; |
1280 | paint.setBlendMode(SkBlendMode::kDstOver); |
1281 | const int x = backImage->fLoc.x(); |
1282 | const int y = backImage->fLoc.y(); |
1283 | this->getTopDevice()->drawSpecial(backImage->fImage.get(), x, y, paint, |
1284 | nullptr, SkMatrix::I()); |
1285 | } |
1286 | |
1287 | /* Time to draw the layer's offscreen. We can't call the public drawSprite, |
1288 | since if we're being recorded, we don't want to record this (the |
1289 | recorder will have already recorded the restore). |
1290 | */ |
1291 | if (layer) { |
1292 | if (fMCRec) { |
1293 | layer->fDevice->setImmutable(); |
1294 | // At this point, 'layer' has been removed from the device stack, so the devices that |
1295 | // internalDrawDevice sees are the destinations that 'layer' is drawn into. |
1296 | this->internalDrawDevice(layer->fDevice.get(), layer->fPaint.get(), |
1297 | layer->fClipImage.get(), layer->fClipMatrix); |
1298 | // restore what we smashed in internalSaveLayer |
1299 | this->internalSetMatrix(layer->fStashedMatrix); |
1300 | delete layer; |
1301 | } else { |
1302 | // we're at the root |
1303 | SkASSERT(layer == (void*)fDeviceCMStorage); |
1304 | layer->~DeviceCM(); |
1305 | // no need to update fMCRec, 'cause we're killing the canvas |
1306 | } |
1307 | } |
1308 | |
1309 | if (fMCRec) { |
1310 | fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate(); |
1311 | fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
1312 | } |
1313 | } |
1314 | |
1315 | sk_sp<SkSurface> SkCanvas::makeSurface(const SkImageInfo& info, const SkSurfaceProps* props) { |
1316 | if (nullptr == props) { |
1317 | props = &fProps; |
1318 | } |
1319 | return this->onNewSurface(info, *props); |
1320 | } |
1321 | |
1322 | sk_sp<SkSurface> SkCanvas::onNewSurface(const SkImageInfo& info, const SkSurfaceProps& props) { |
1323 | SkBaseDevice* dev = this->getDevice(); |
1324 | return dev ? dev->makeSurface(info, props) : nullptr; |
1325 | } |
1326 | |
1327 | SkImageInfo SkCanvas::imageInfo() const { |
1328 | return this->onImageInfo(); |
1329 | } |
1330 | |
1331 | SkImageInfo SkCanvas::onImageInfo() const { |
1332 | SkBaseDevice* dev = this->getDevice(); |
1333 | if (dev) { |
1334 | return dev->imageInfo(); |
1335 | } else { |
1336 | return SkImageInfo::MakeUnknown(0, 0); |
1337 | } |
1338 | } |
1339 | |
1340 | bool SkCanvas::getProps(SkSurfaceProps* props) const { |
1341 | return this->onGetProps(props); |
1342 | } |
1343 | |
1344 | bool SkCanvas::onGetProps(SkSurfaceProps* props) const { |
1345 | SkBaseDevice* dev = this->getDevice(); |
1346 | if (dev) { |
1347 | if (props) { |
1348 | *props = fProps; |
1349 | } |
1350 | return true; |
1351 | } else { |
1352 | return false; |
1353 | } |
1354 | } |
1355 | |
1356 | bool SkCanvas::peekPixels(SkPixmap* pmap) { |
1357 | return this->onPeekPixels(pmap); |
1358 | } |
1359 | |
1360 | bool SkCanvas::onPeekPixels(SkPixmap* pmap) { |
1361 | SkBaseDevice* dev = this->getDevice(); |
1362 | return dev && dev->peekPixels(pmap); |
1363 | } |
1364 | |
1365 | void* SkCanvas::accessTopLayerPixels(SkImageInfo* info, size_t* rowBytes, SkIPoint* origin) { |
1366 | SkPixmap pmap; |
1367 | if (!this->onAccessTopLayerPixels(&pmap)) { |
1368 | return nullptr; |
1369 | } |
1370 | if (info) { |
1371 | *info = pmap.info(); |
1372 | } |
1373 | if (rowBytes) { |
1374 | *rowBytes = pmap.rowBytes(); |
1375 | } |
1376 | if (origin) { |
1377 | // If the caller requested the origin, they presumably are expecting the returned pixels to |
1378 | // be axis-aligned with the root canvas. If the top level device isn't axis aligned, that's |
1379 | // not the case. Until we update accessTopLayerPixels() to accept a coord space matrix |
1380 | // instead of an origin, just don't expose the pixels in that case. Note that this means |
1381 | // that layers with complex coordinate spaces can still report their pixels if the caller |
1382 | // does not ask for the origin (e.g. just to dump its output to a file, etc). |
1383 | if (this->getTopDevice()->isPixelAlignedToGlobal()) { |
1384 | *origin = this->getTopDevice()->getOrigin(); |
1385 | } else { |
1386 | return nullptr; |
1387 | } |
1388 | } |
1389 | return pmap.writable_addr(); |
1390 | } |
1391 | |
1392 | bool SkCanvas::onAccessTopLayerPixels(SkPixmap* pmap) { |
1393 | SkBaseDevice* dev = this->getTopDevice(); |
1394 | return dev && dev->accessPixels(pmap); |
1395 | } |
1396 | |
1397 | ///////////////////////////////////////////////////////////////////////////// |
1398 | |
1399 | // In our current design/features, we should never have a layer (src) in a different colorspace |
1400 | // than its parent (dst), so we assert that here. This is called out from other asserts, in case |
1401 | // we add some feature in the future to allow a given layer/imagefilter to operate in a specific |
1402 | // colorspace. |
1403 | static void check_drawdevice_colorspaces(SkColorSpace* src, SkColorSpace* dst) { |
1404 | SkASSERT(src == dst); |
1405 | } |
1406 | |
1407 | void SkCanvas::internalDrawDevice(SkBaseDevice* srcDev, const SkPaint* paint, |
1408 | SkImage* clipImage, const SkMatrix& clipMatrix) { |
1409 | SkPaint tmp; |
1410 | if (nullptr == paint) { |
1411 | paint = &tmp; |
1412 | } |
1413 | |
1414 | DRAW_BEGIN_DRAWDEVICE(*paint) |
1415 | |
1416 | while (iter.next()) { |
1417 | SkBaseDevice* dstDev = iter.fDevice; |
1418 | check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(), |
1419 | srcDev->imageInfo().colorSpace()); |
1420 | paint = &draw.paint(); |
1421 | SkImageFilter* filter = paint->getImageFilter(); |
1422 | // TODO(michaelludwig) - Devices aren't created with complex coordinate systems yet, |
1423 | // so it should always be possible to use the relative origin. Once drawDevice() and |
1424 | // drawSpecial() take an SkMatrix, this can switch to getRelativeTransform() instead. |
1425 | SkIPoint pos = srcDev->getOrigin() - dstDev->getOrigin(); |
1426 | if (filter || clipImage) { |
1427 | sk_sp<SkSpecialImage> specialImage = srcDev->snapSpecial(); |
1428 | if (specialImage) { |
1429 | check_drawdevice_colorspaces(dstDev->imageInfo().colorSpace(), |
1430 | specialImage->getColorSpace()); |
1431 | dstDev->drawSpecial(specialImage.get(), pos.x(), pos.y(), *paint, |
1432 | clipImage, clipMatrix); |
1433 | } |
1434 | } else { |
1435 | dstDev->drawDevice(srcDev, pos.x(), pos.y(), *paint); |
1436 | } |
1437 | } |
1438 | |
1439 | DRAW_END |
1440 | } |
1441 | |
1442 | ///////////////////////////////////////////////////////////////////////////// |
1443 | |
1444 | void SkCanvas::translate(SkScalar dx, SkScalar dy) { |
1445 | if (dx || dy) { |
1446 | this->checkForDeferredSave(); |
1447 | fMCRec->fMatrix.preTranslate(dx, dy); |
1448 | |
1449 | // Translate shouldn't affect the is-scale-translateness of the matrix. |
1450 | // However, if either is non-finite, we might still complicate the matrix type, |
1451 | // so we still have to compute this. |
1452 | fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate(); |
1453 | |
1454 | FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix)); |
1455 | |
1456 | this->didTranslate(dx,dy); |
1457 | } |
1458 | } |
1459 | |
1460 | void SkCanvas::scale(SkScalar sx, SkScalar sy) { |
1461 | if (sx != 1 || sy != 1) { |
1462 | this->checkForDeferredSave(); |
1463 | fMCRec->fMatrix.preScale(sx, sy); |
1464 | |
1465 | // shouldn't need to do this (theoretically), as the state shouldn't have changed, |
1466 | // but pre-scaling by a non-finite does change it, so we have to recompute. |
1467 | fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate(); |
1468 | |
1469 | FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix)); |
1470 | |
1471 | this->didScale(sx, sy); |
1472 | } |
1473 | } |
1474 | |
1475 | void SkCanvas::rotate(SkScalar degrees) { |
1476 | SkMatrix m; |
1477 | m.setRotate(degrees); |
1478 | this->concat(m); |
1479 | } |
1480 | |
1481 | void SkCanvas::rotate(SkScalar degrees, SkScalar px, SkScalar py) { |
1482 | SkMatrix m; |
1483 | m.setRotate(degrees, px, py); |
1484 | this->concat(m); |
1485 | } |
1486 | |
1487 | void SkCanvas::skew(SkScalar sx, SkScalar sy) { |
1488 | SkMatrix m; |
1489 | m.setSkew(sx, sy); |
1490 | this->concat(m); |
1491 | } |
1492 | |
1493 | void SkCanvas::concat(const SkMatrix& matrix) { |
1494 | if (matrix.isIdentity()) { |
1495 | return; |
1496 | } |
1497 | |
1498 | this->checkForDeferredSave(); |
1499 | fMCRec->fMatrix.preConcat(matrix); |
1500 | |
1501 | fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate(); |
1502 | |
1503 | FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix)); |
1504 | |
1505 | this->didConcat(matrix); |
1506 | } |
1507 | |
1508 | void SkCanvas::internalConcat44(const SkM44& m) { |
1509 | this->checkForDeferredSave(); |
1510 | |
1511 | fMCRec->fMatrix.preConcat(m); |
1512 | |
1513 | fIsScaleTranslate = fMCRec->fMatrix.isScaleTranslate(); |
1514 | |
1515 | FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix)); |
1516 | } |
1517 | |
1518 | void SkCanvas::concat(const SkM44& m) { |
1519 | this->internalConcat44(m); |
1520 | // notify subclasses |
1521 | #ifdef SK_SUPPORT_LEGACY_DIDCONCAT44 |
1522 | this->didConcat44(SkMatrixPriv::M44ColMajor(m)); |
1523 | #else |
1524 | this->didConcat44(m); |
1525 | #endif |
1526 | } |
1527 | |
1528 | void SkCanvas::internalSetMatrix(const SkMatrix& matrix) { |
1529 | fMCRec->fMatrix = matrix; |
1530 | fIsScaleTranslate = matrix.isScaleTranslate(); |
1531 | |
1532 | FOR_EACH_TOP_DEVICE(device->setGlobalCTM(fMCRec->fMatrix)); |
1533 | } |
1534 | |
1535 | void SkCanvas::setMatrix(const SkMatrix& matrix) { |
1536 | this->checkForDeferredSave(); |
1537 | this->internalSetMatrix(matrix); |
1538 | this->didSetMatrix(matrix); |
1539 | } |
1540 | |
1541 | void SkCanvas::resetMatrix() { |
1542 | this->setMatrix(SkMatrix::I()); |
1543 | } |
1544 | |
1545 | void SkCanvas::markCTM(MarkerID id) { |
1546 | if (id == 0) return; |
1547 | |
1548 | this->onMarkCTM(id); |
1549 | |
1550 | SkM44 mx = this->getLocalToDevice(); |
1551 | |
1552 | // Look if we've already seen id in this save-frame. |
1553 | // If so, replace, else append |
1554 | for (int i = fMarkerStack.size() - 1; i >= 0; --i) { |
1555 | auto& m = fMarkerStack[i]; |
1556 | if (m.fMCRec != fMCRec) { // we've gone past the current save-frame |
1557 | break; // fall out so we append |
1558 | } |
1559 | if (m.fID == id) { // in current frame, so replace |
1560 | m.fMatrix = mx; |
1561 | return; |
1562 | } |
1563 | } |
1564 | // if we get here, we should append a new marker |
1565 | fMarkerStack.push_back({fMCRec, mx, id}); |
1566 | } |
1567 | |
1568 | bool SkCanvas::findMarkedCTM(MarkerID id, SkM44* mx) const { |
1569 | // search from top to bottom, so we find the most recent id |
1570 | for (int i = fMarkerStack.size() - 1; i >= 0; --i) { |
1571 | if (fMarkerStack[i].fID == id) { |
1572 | if (mx) { |
1573 | *mx = fMarkerStack[i].fMatrix; |
1574 | } |
1575 | return true; |
1576 | } |
1577 | } |
1578 | return false; |
1579 | } |
1580 | |
1581 | ////////////////////////////////////////////////////////////////////////////// |
1582 | |
1583 | void SkCanvas::clipRect(const SkRect& rect, SkClipOp op, bool doAA) { |
1584 | if (!rect.isFinite()) { |
1585 | return; |
1586 | } |
1587 | this->checkForDeferredSave(); |
1588 | ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle; |
1589 | this->onClipRect(rect, op, edgeStyle); |
1590 | } |
1591 | |
1592 | void SkCanvas::onClipRect(const SkRect& rect, SkClipOp op, ClipEdgeStyle edgeStyle) { |
1593 | const bool isAA = kSoft_ClipEdgeStyle == edgeStyle; |
1594 | |
1595 | FOR_EACH_TOP_DEVICE(device->clipRect(rect, op, isAA)); |
1596 | |
1597 | AutoValidateClip avc(this); |
1598 | fMCRec->fRasterClip.opRect(rect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op, |
1599 | isAA); |
1600 | fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
1601 | } |
1602 | |
1603 | void SkCanvas::androidFramework_setDeviceClipRestriction(const SkIRect& rect) { |
1604 | fClipRestrictionRect = rect; |
1605 | if (fClipRestrictionRect.isEmpty()) { |
1606 | // we notify the device, but we *dont* resolve deferred saves (since we're just |
1607 | // removing the restriction if the rect is empty. how I hate this api. |
1608 | FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect)); |
1609 | } else { |
1610 | this->checkForDeferredSave(); |
1611 | FOR_EACH_TOP_DEVICE(device->androidFramework_setDeviceClipRestriction(&fClipRestrictionRect)); |
1612 | AutoValidateClip avc(this); |
1613 | fMCRec->fRasterClip.opIRect(fClipRestrictionRect, SkRegion::kIntersect_Op); |
1614 | fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
1615 | } |
1616 | } |
1617 | |
1618 | void SkCanvas::clipRRect(const SkRRect& rrect, SkClipOp op, bool doAA) { |
1619 | this->checkForDeferredSave(); |
1620 | ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle; |
1621 | if (rrect.isRect()) { |
1622 | this->onClipRect(rrect.getBounds(), op, edgeStyle); |
1623 | } else { |
1624 | this->onClipRRect(rrect, op, edgeStyle); |
1625 | } |
1626 | } |
1627 | |
1628 | void SkCanvas::onClipRRect(const SkRRect& rrect, SkClipOp op, ClipEdgeStyle edgeStyle) { |
1629 | AutoValidateClip avc(this); |
1630 | |
1631 | bool isAA = kSoft_ClipEdgeStyle == edgeStyle; |
1632 | |
1633 | FOR_EACH_TOP_DEVICE(device->clipRRect(rrect, op, isAA)); |
1634 | |
1635 | fMCRec->fRasterClip.opRRect(rrect, fMCRec->fMatrix, this->getTopLayerBounds(), (SkRegion::Op)op, |
1636 | isAA); |
1637 | fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
1638 | } |
1639 | |
1640 | void SkCanvas::clipPath(const SkPath& path, SkClipOp op, bool doAA) { |
1641 | this->checkForDeferredSave(); |
1642 | ClipEdgeStyle edgeStyle = doAA ? kSoft_ClipEdgeStyle : kHard_ClipEdgeStyle; |
1643 | |
1644 | if (!path.isInverseFillType() && fMCRec->fMatrix.rectStaysRect()) { |
1645 | SkRect r; |
1646 | if (path.isRect(&r)) { |
1647 | this->onClipRect(r, op, edgeStyle); |
1648 | return; |
1649 | } |
1650 | SkRRect rrect; |
1651 | if (path.isOval(&r)) { |
1652 | rrect.setOval(r); |
1653 | this->onClipRRect(rrect, op, edgeStyle); |
1654 | return; |
1655 | } |
1656 | if (path.isRRect(&rrect)) { |
1657 | this->onClipRRect(rrect, op, edgeStyle); |
1658 | return; |
1659 | } |
1660 | } |
1661 | |
1662 | this->onClipPath(path, op, edgeStyle); |
1663 | } |
1664 | |
1665 | void SkCanvas::onClipPath(const SkPath& path, SkClipOp op, ClipEdgeStyle edgeStyle) { |
1666 | AutoValidateClip avc(this); |
1667 | |
1668 | bool isAA = kSoft_ClipEdgeStyle == edgeStyle; |
1669 | |
1670 | FOR_EACH_TOP_DEVICE(device->clipPath(path, op, isAA)); |
1671 | |
1672 | const SkPath* rasterClipPath = &path; |
1673 | fMCRec->fRasterClip.opPath(*rasterClipPath, fMCRec->fMatrix, this->getTopLayerBounds(), |
1674 | (SkRegion::Op)op, isAA); |
1675 | fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
1676 | } |
1677 | |
1678 | void SkCanvas::clipShader(sk_sp<SkShader> sh, SkClipOp op) { |
1679 | if (sh) { |
1680 | if (sh->isOpaque()) { |
1681 | if (op == SkClipOp::kIntersect) { |
1682 | // we don't occlude anything, so skip this call |
1683 | } else { |
1684 | SkASSERT(op == SkClipOp::kDifference); |
1685 | // we occlude everything, so set the clip to empty |
1686 | this->clipRect({0,0,0,0}); |
1687 | } |
1688 | } else { |
1689 | this->onClipShader(std::move(sh), op); |
1690 | } |
1691 | } |
1692 | } |
1693 | |
1694 | void SkCanvas::onClipShader(sk_sp<SkShader> sh, SkClipOp op) { |
1695 | AutoValidateClip avc(this); |
1696 | |
1697 | FOR_EACH_TOP_DEVICE(device->clipShader(sh, op)); |
1698 | |
1699 | // we don't know how to mutate our conservative bounds, so we don't |
1700 | } |
1701 | |
1702 | void SkCanvas::clipRegion(const SkRegion& rgn, SkClipOp op) { |
1703 | this->checkForDeferredSave(); |
1704 | this->onClipRegion(rgn, op); |
1705 | } |
1706 | |
1707 | void SkCanvas::onClipRegion(const SkRegion& rgn, SkClipOp op) { |
1708 | FOR_EACH_TOP_DEVICE(device->clipRegion(rgn, op)); |
1709 | |
1710 | AutoValidateClip avc(this); |
1711 | |
1712 | fMCRec->fRasterClip.opRegion(rgn, (SkRegion::Op)op); |
1713 | fDeviceClipBounds = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
1714 | } |
1715 | |
1716 | #ifdef SK_DEBUG |
1717 | void SkCanvas::validateClip() const { |
1718 | // construct clipRgn from the clipstack |
1719 | const SkBaseDevice* device = this->getDevice(); |
1720 | if (!device) { |
1721 | SkASSERT(this->isClipEmpty()); |
1722 | return; |
1723 | } |
1724 | } |
1725 | #endif |
1726 | |
1727 | bool SkCanvas::androidFramework_isClipAA() const { |
1728 | bool containsAA = false; |
1729 | |
1730 | FOR_EACH_TOP_DEVICE(containsAA |= device->onClipIsAA()); |
1731 | |
1732 | return containsAA; |
1733 | } |
1734 | |
1735 | class RgnAccumulator { |
1736 | SkRegion* fRgn; |
1737 | public: |
1738 | RgnAccumulator(SkRegion* total) : fRgn(total) {} |
1739 | void accumulate(SkBaseDevice* device, SkRegion* rgn) { |
1740 | SkIPoint origin = device->getOrigin(); |
1741 | if (origin.x() | origin.y()) { |
1742 | rgn->translate(origin.x(), origin.y()); |
1743 | } |
1744 | fRgn->op(*rgn, SkRegion::kUnion_Op); |
1745 | } |
1746 | }; |
1747 | |
1748 | void SkCanvas::temporary_internal_getRgnClip(SkRegion* rgn) { |
1749 | RgnAccumulator accum(rgn); |
1750 | SkRegion tmp; |
1751 | |
1752 | rgn->setEmpty(); |
1753 | FOR_EACH_TOP_DEVICE(device->onAsRgnClip(&tmp); accum.accumulate(device, &tmp)); |
1754 | } |
1755 | |
1756 | /////////////////////////////////////////////////////////////////////////////// |
1757 | |
1758 | bool SkCanvas::isClipEmpty() const { |
1759 | return fMCRec->fRasterClip.isEmpty(); |
1760 | |
1761 | // TODO: should we only use the conservative answer in a recording canvas? |
1762 | #if 0 |
1763 | SkBaseDevice* dev = this->getTopDevice(); |
1764 | // if no device we return true |
1765 | return !dev || dev->onGetClipType() == SkBaseDevice::kEmpty_ClipType; |
1766 | #endif |
1767 | } |
1768 | |
1769 | bool SkCanvas::isClipRect() const { |
1770 | SkBaseDevice* dev = this->getTopDevice(); |
1771 | // if no device we return false |
1772 | return dev && dev->onGetClipType() == SkBaseDevice::ClipType::kRect; |
1773 | } |
1774 | |
1775 | static inline bool is_nan_or_clipped(const Sk4f& devRect, const Sk4f& devClip) { |
1776 | #if !defined(SKNX_NO_SIMD) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 |
1777 | __m128 lLtT = _mm_unpacklo_ps(devRect.fVec, devClip.fVec); |
1778 | __m128 RrBb = _mm_unpackhi_ps(devClip.fVec, devRect.fVec); |
1779 | __m128 mask = _mm_cmplt_ps(lLtT, RrBb); |
1780 | return 0xF != _mm_movemask_ps(mask); |
1781 | #elif !defined(SKNX_NO_SIMD) && defined(SK_ARM_HAS_NEON) |
1782 | float32x4_t lLtT = vzipq_f32(devRect.fVec, devClip.fVec).val[0]; |
1783 | float32x4_t RrBb = vzipq_f32(devClip.fVec, devRect.fVec).val[1]; |
1784 | uint32x4_t mask = vcltq_f32(lLtT, RrBb); |
1785 | return 0xFFFFFFFFFFFFFFFF != (uint64_t) vmovn_u32(mask); |
1786 | #else |
1787 | SkRect devRectAsRect; |
1788 | SkRect devClipAsRect; |
1789 | devRect.store(&devRectAsRect.fLeft); |
1790 | devClip.store(&devClipAsRect.fLeft); |
1791 | return !devRectAsRect.isFinite() || !devRectAsRect.intersect(devClipAsRect); |
1792 | #endif |
1793 | } |
1794 | |
1795 | // It's important for this function to not be inlined. Otherwise the compiler will share code |
1796 | // between the fast path and the slow path, resulting in two slow paths. |
1797 | static SK_NEVER_INLINE bool quick_reject_slow_path(const SkRect& src, const SkRect& deviceClip, |
1798 | const SkMatrix& matrix) { |
1799 | SkRect deviceRect; |
1800 | matrix.mapRect(&deviceRect, src); |
1801 | return !deviceRect.isFinite() || !deviceRect.intersect(deviceClip); |
1802 | } |
1803 | |
1804 | bool SkCanvas::quickReject(const SkRect& src) const { |
1805 | #ifdef SK_DEBUG |
1806 | // Verify that fDeviceClipBounds are set properly. |
1807 | SkRect tmp = qr_clip_bounds(fMCRec->fRasterClip.getBounds()); |
1808 | if (fMCRec->fRasterClip.isEmpty()) { |
1809 | SkASSERT(fDeviceClipBounds.isEmpty()); |
1810 | } else { |
1811 | SkASSERT(tmp == fDeviceClipBounds); |
1812 | } |
1813 | |
1814 | // Verify that fIsScaleTranslate is set properly. |
1815 | SkASSERT(fIsScaleTranslate == fMCRec->fMatrix.isScaleTranslate()); |
1816 | #endif |
1817 | |
1818 | if (!fIsScaleTranslate) { |
1819 | return quick_reject_slow_path(src, fDeviceClipBounds, fMCRec->fMatrix); |
1820 | } |
1821 | |
1822 | // We inline the implementation of mapScaleTranslate() for the fast path. |
1823 | float sx = fMCRec->fMatrix.getScaleX(); |
1824 | float sy = fMCRec->fMatrix.getScaleY(); |
1825 | float tx = fMCRec->fMatrix.getTranslateX(); |
1826 | float ty = fMCRec->fMatrix.getTranslateY(); |
1827 | Sk4f scale(sx, sy, sx, sy); |
1828 | Sk4f trans(tx, ty, tx, ty); |
1829 | |
1830 | // Apply matrix. |
1831 | Sk4f ltrb = Sk4f::Load(&src.fLeft) * scale + trans; |
1832 | |
1833 | // Make sure left < right, top < bottom. |
1834 | Sk4f rblt(ltrb[2], ltrb[3], ltrb[0], ltrb[1]); |
1835 | Sk4f min = Sk4f::Min(ltrb, rblt); |
1836 | Sk4f max = Sk4f::Max(ltrb, rblt); |
1837 | // We can extract either pair [0,1] or [2,3] from min and max and be correct, but on |
1838 | // ARM this sequence generates the fastest (a single instruction). |
1839 | Sk4f devRect = Sk4f(min[2], min[3], max[0], max[1]); |
1840 | |
1841 | // Check if the device rect is NaN or outside the clip. |
1842 | return is_nan_or_clipped(devRect, Sk4f::Load(&fDeviceClipBounds.fLeft)); |
1843 | } |
1844 | |
1845 | bool SkCanvas::quickReject(const SkPath& path) const { |
1846 | return path.isEmpty() || this->quickReject(path.getBounds()); |
1847 | } |
1848 | |
1849 | SkRect SkCanvas::getLocalClipBounds() const { |
1850 | SkIRect ibounds = this->getDeviceClipBounds(); |
1851 | if (ibounds.isEmpty()) { |
1852 | return SkRect::MakeEmpty(); |
1853 | } |
1854 | |
1855 | SkMatrix inverse; |
1856 | // if we can't invert the CTM, we can't return local clip bounds |
1857 | if (!fMCRec->fMatrix.asM33().invert(&inverse)) { |
1858 | return SkRect::MakeEmpty(); |
1859 | } |
1860 | |
1861 | SkRect bounds; |
1862 | // adjust it outwards in case we are antialiasing |
1863 | const int margin = 1; |
1864 | |
1865 | SkRect r = SkRect::Make(ibounds.makeOutset(margin, margin)); |
1866 | inverse.mapRect(&bounds, r); |
1867 | return bounds; |
1868 | } |
1869 | |
1870 | SkIRect SkCanvas::getDeviceClipBounds() const { |
1871 | return fMCRec->fRasterClip.getBounds(); |
1872 | } |
1873 | |
1874 | /////////////////////////////////////////////////////////////////////// |
1875 | |
1876 | SkMatrix SkCanvas::getTotalMatrix() const { |
1877 | return fMCRec->fMatrix; |
1878 | } |
1879 | |
1880 | SkM44 SkCanvas::getLocalToDevice() const { |
1881 | return fMCRec->fMatrix; |
1882 | } |
1883 | |
1884 | GrRenderTargetContext* SkCanvas::internal_private_accessTopLayerRenderTargetContext() { |
1885 | SkBaseDevice* dev = this->getTopDevice(); |
1886 | return dev ? dev->accessRenderTargetContext() : nullptr; |
1887 | } |
1888 | |
1889 | GrContext* SkCanvas::getGrContext() { |
1890 | SkBaseDevice* device = this->getTopDevice(); |
1891 | return device ? device->context() : nullptr; |
1892 | } |
1893 | |
1894 | void SkCanvas::drawDRRect(const SkRRect& outer, const SkRRect& inner, |
1895 | const SkPaint& paint) { |
1896 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1897 | if (outer.isEmpty()) { |
1898 | return; |
1899 | } |
1900 | if (inner.isEmpty()) { |
1901 | this->drawRRect(outer, paint); |
1902 | return; |
1903 | } |
1904 | |
1905 | // We don't have this method (yet), but technically this is what we should |
1906 | // be able to return ... |
1907 | // if (!outer.contains(inner))) { |
1908 | // |
1909 | // For now at least check for containment of bounds |
1910 | if (!outer.getBounds().contains(inner.getBounds())) { |
1911 | return; |
1912 | } |
1913 | |
1914 | this->onDrawDRRect(outer, inner, paint); |
1915 | } |
1916 | |
1917 | void SkCanvas::drawPaint(const SkPaint& paint) { |
1918 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1919 | this->onDrawPaint(paint); |
1920 | } |
1921 | |
1922 | void SkCanvas::drawRect(const SkRect& r, const SkPaint& paint) { |
1923 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1924 | // To avoid redundant logic in our culling code and various backends, we always sort rects |
1925 | // before passing them along. |
1926 | this->onDrawRect(r.makeSorted(), paint); |
1927 | } |
1928 | |
1929 | void SkCanvas::drawClippedToSaveBehind(const SkPaint& paint) { |
1930 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1931 | this->onDrawBehind(paint); |
1932 | } |
1933 | |
1934 | void SkCanvas::drawRegion(const SkRegion& region, const SkPaint& paint) { |
1935 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1936 | if (region.isEmpty()) { |
1937 | return; |
1938 | } |
1939 | |
1940 | if (region.isRect()) { |
1941 | return this->drawIRect(region.getBounds(), paint); |
1942 | } |
1943 | |
1944 | this->onDrawRegion(region, paint); |
1945 | } |
1946 | |
1947 | void SkCanvas::drawOval(const SkRect& r, const SkPaint& paint) { |
1948 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1949 | // To avoid redundant logic in our culling code and various backends, we always sort rects |
1950 | // before passing them along. |
1951 | this->onDrawOval(r.makeSorted(), paint); |
1952 | } |
1953 | |
1954 | void SkCanvas::drawRRect(const SkRRect& rrect, const SkPaint& paint) { |
1955 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1956 | this->onDrawRRect(rrect, paint); |
1957 | } |
1958 | |
1959 | void SkCanvas::drawPoints(PointMode mode, size_t count, const SkPoint pts[], const SkPaint& paint) { |
1960 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1961 | this->onDrawPoints(mode, count, pts, paint); |
1962 | } |
1963 | |
1964 | void SkCanvas::drawVertices(const sk_sp<SkVertices>& vertices, SkBlendMode mode, |
1965 | const SkPaint& paint) { |
1966 | this->drawVertices(vertices.get(), mode, paint); |
1967 | } |
1968 | |
1969 | void SkCanvas::drawVertices(const SkVertices* vertices, SkBlendMode mode, const SkPaint& paint) { |
1970 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1971 | RETURN_ON_NULL(vertices); |
1972 | |
1973 | // We expect fans to be converted to triangles when building or deserializing SkVertices. |
1974 | SkASSERT(vertices->priv().mode() != SkVertices::kTriangleFan_VertexMode); |
1975 | |
1976 | // If the vertices contain custom attributes, ensure they line up with the paint's shader |
1977 | const SkRuntimeEffect* effect = |
1978 | paint.getShader() ? as_SB(paint.getShader())->asRuntimeEffect() : nullptr; |
1979 | if ((size_t)vertices->priv().attributeCount() != (effect ? effect->varyings().count() : 0)) { |
1980 | return; |
1981 | } |
1982 | if (effect) { |
1983 | int attrIndex = 0; |
1984 | for (const auto& v : effect->varyings()) { |
1985 | if (vertices->priv().attributes()[attrIndex++].channelCount() != v.fWidth) { |
1986 | return; |
1987 | } |
1988 | } |
1989 | } |
1990 | |
1991 | this->onDrawVerticesObject(vertices, mode, paint); |
1992 | } |
1993 | |
1994 | void SkCanvas::drawPath(const SkPath& path, const SkPaint& paint) { |
1995 | TRACE_EVENT0("skia" , TRACE_FUNC); |
1996 | this->onDrawPath(path, paint); |
1997 | } |
1998 | |
1999 | void SkCanvas::drawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) { |
2000 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2001 | RETURN_ON_NULL(image); |
2002 | this->onDrawImage(image, x, y, paint); |
2003 | } |
2004 | |
2005 | // Returns true if the rect can be "filled" : non-empty and finite |
2006 | static bool fillable(const SkRect& r) { |
2007 | SkScalar w = r.width(); |
2008 | SkScalar h = r.height(); |
2009 | return SkScalarIsFinite(w) && w > 0 && SkScalarIsFinite(h) && h > 0; |
2010 | } |
2011 | |
2012 | void SkCanvas::drawImageRect(const SkImage* image, const SkRect& src, const SkRect& dst, |
2013 | const SkPaint* paint, SrcRectConstraint constraint) { |
2014 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2015 | RETURN_ON_NULL(image); |
2016 | if (!fillable(dst) || !fillable(src)) { |
2017 | return; |
2018 | } |
2019 | this->onDrawImageRect(image, &src, dst, paint, constraint); |
2020 | } |
2021 | |
2022 | void SkCanvas::drawImageRect(const SkImage* image, const SkIRect& isrc, const SkRect& dst, |
2023 | const SkPaint* paint, SrcRectConstraint constraint) { |
2024 | RETURN_ON_NULL(image); |
2025 | this->drawImageRect(image, SkRect::Make(isrc), dst, paint, constraint); |
2026 | } |
2027 | |
2028 | void SkCanvas::drawImageRect(const SkImage* image, const SkRect& dst, const SkPaint* paint) { |
2029 | RETURN_ON_NULL(image); |
2030 | this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), dst, paint, |
2031 | kFast_SrcRectConstraint); |
2032 | } |
2033 | |
2034 | namespace { |
2035 | class LatticePaint : SkNoncopyable { |
2036 | public: |
2037 | LatticePaint(const SkPaint* origPaint) : fPaint(origPaint) { |
2038 | if (!origPaint) { |
2039 | return; |
2040 | } |
2041 | if (origPaint->getFilterQuality() > kLow_SkFilterQuality) { |
2042 | fPaint.writable()->setFilterQuality(kLow_SkFilterQuality); |
2043 | } |
2044 | if (origPaint->getMaskFilter()) { |
2045 | fPaint.writable()->setMaskFilter(nullptr); |
2046 | } |
2047 | if (origPaint->isAntiAlias()) { |
2048 | fPaint.writable()->setAntiAlias(false); |
2049 | } |
2050 | } |
2051 | |
2052 | const SkPaint* get() const { |
2053 | return fPaint; |
2054 | } |
2055 | |
2056 | private: |
2057 | SkTCopyOnFirstWrite<SkPaint> fPaint; |
2058 | }; |
2059 | } // namespace |
2060 | |
2061 | void SkCanvas::drawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst, |
2062 | const SkPaint* paint) { |
2063 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2064 | RETURN_ON_NULL(image); |
2065 | if (dst.isEmpty()) { |
2066 | return; |
2067 | } |
2068 | if (SkLatticeIter::Valid(image->width(), image->height(), center)) { |
2069 | LatticePaint latticePaint(paint); |
2070 | this->onDrawImageNine(image, center, dst, latticePaint.get()); |
2071 | } else { |
2072 | this->drawImageRect(image, dst, paint); |
2073 | } |
2074 | } |
2075 | |
2076 | void SkCanvas::drawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst, |
2077 | const SkPaint* paint) { |
2078 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2079 | RETURN_ON_NULL(image); |
2080 | if (dst.isEmpty()) { |
2081 | return; |
2082 | } |
2083 | |
2084 | SkIRect bounds; |
2085 | Lattice latticePlusBounds = lattice; |
2086 | if (!latticePlusBounds.fBounds) { |
2087 | bounds = SkIRect::MakeWH(image->width(), image->height()); |
2088 | latticePlusBounds.fBounds = &bounds; |
2089 | } |
2090 | |
2091 | if (SkLatticeIter::Valid(image->width(), image->height(), latticePlusBounds)) { |
2092 | LatticePaint latticePaint(paint); |
2093 | this->onDrawImageLattice(image, latticePlusBounds, dst, latticePaint.get()); |
2094 | } else { |
2095 | this->drawImageRect(image, dst, paint); |
2096 | } |
2097 | } |
2098 | |
2099 | static sk_sp<SkImage> bitmap_as_image(const SkBitmap& bitmap) { |
2100 | if (bitmap.drawsNothing()) { |
2101 | return nullptr; |
2102 | } |
2103 | return SkImage::MakeFromBitmap(bitmap); |
2104 | } |
2105 | |
2106 | void SkCanvas::drawBitmap(const SkBitmap& bitmap, SkScalar dx, SkScalar dy, const SkPaint* paint) { |
2107 | this->drawImage(bitmap_as_image(bitmap), dx, dy, paint); |
2108 | } |
2109 | |
2110 | void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& src, const SkRect& dst, |
2111 | const SkPaint* paint, SrcRectConstraint constraint) { |
2112 | this->drawImageRect(bitmap_as_image(bitmap), src, dst, paint, constraint); |
2113 | } |
2114 | |
2115 | void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkIRect& isrc, const SkRect& dst, |
2116 | const SkPaint* paint, SrcRectConstraint constraint) { |
2117 | this->drawBitmapRect(bitmap, SkRect::Make(isrc), dst, paint, constraint); |
2118 | } |
2119 | |
2120 | void SkCanvas::drawBitmapRect(const SkBitmap& bitmap, const SkRect& dst, const SkPaint* paint, |
2121 | SrcRectConstraint constraint) { |
2122 | this->drawBitmapRect(bitmap, SkRect::MakeIWH(bitmap.width(), bitmap.height()), dst, paint, |
2123 | constraint); |
2124 | } |
2125 | |
2126 | void SkCanvas::drawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], |
2127 | const SkColor colors[], int count, SkBlendMode mode, |
2128 | const SkRect* cull, const SkPaint* paint) { |
2129 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2130 | RETURN_ON_NULL(atlas); |
2131 | if (count <= 0) { |
2132 | return; |
2133 | } |
2134 | SkASSERT(atlas); |
2135 | SkASSERT(tex); |
2136 | this->onDrawAtlas(atlas, xform, tex, colors, count, mode, cull, paint); |
2137 | } |
2138 | |
2139 | void SkCanvas::drawAnnotation(const SkRect& rect, const char key[], SkData* value) { |
2140 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2141 | if (key) { |
2142 | this->onDrawAnnotation(rect, key, value); |
2143 | } |
2144 | } |
2145 | |
2146 | void SkCanvas::legacy_drawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, |
2147 | const SkPaint* paint, SrcRectConstraint constraint) { |
2148 | if (src) { |
2149 | this->drawImageRect(image, *src, dst, paint, constraint); |
2150 | } else { |
2151 | this->drawImageRect(image, SkRect::MakeIWH(image->width(), image->height()), |
2152 | dst, paint, constraint); |
2153 | } |
2154 | } |
2155 | |
2156 | void SkCanvas::private_draw_shadow_rec(const SkPath& path, const SkDrawShadowRec& rec) { |
2157 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2158 | this->onDrawShadowRec(path, rec); |
2159 | } |
2160 | |
2161 | void SkCanvas::onDrawShadowRec(const SkPath& path, const SkDrawShadowRec& rec) { |
2162 | SkPaint paint; |
2163 | const SkRect& pathBounds = path.getBounds(); |
2164 | |
2165 | DRAW_BEGIN(paint, &pathBounds) |
2166 | while (iter.next()) { |
2167 | iter.fDevice->drawShadow(path, rec); |
2168 | } |
2169 | DRAW_END |
2170 | } |
2171 | |
2172 | void SkCanvas::experimental_DrawEdgeAAQuad(const SkRect& rect, const SkPoint clip[4], |
2173 | QuadAAFlags aaFlags, const SkColor4f& color, |
2174 | SkBlendMode mode) { |
2175 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2176 | // Make sure the rect is sorted before passing it along |
2177 | this->onDrawEdgeAAQuad(rect.makeSorted(), clip, aaFlags, color, mode); |
2178 | } |
2179 | |
2180 | void SkCanvas::experimental_DrawEdgeAAImageSet(const ImageSetEntry imageSet[], int cnt, |
2181 | const SkPoint dstClips[], |
2182 | const SkMatrix preViewMatrices[], |
2183 | const SkPaint* paint, |
2184 | SrcRectConstraint constraint) { |
2185 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2186 | this->onDrawEdgeAAImageSet(imageSet, cnt, dstClips, preViewMatrices, paint, constraint); |
2187 | } |
2188 | |
2189 | ////////////////////////////////////////////////////////////////////////////// |
2190 | // These are the virtual drawing methods |
2191 | ////////////////////////////////////////////////////////////////////////////// |
2192 | |
2193 | void SkCanvas::onDiscard() { |
2194 | if (fSurfaceBase) { |
2195 | fSurfaceBase->aboutToDraw(SkSurface::kDiscard_ContentChangeMode); |
2196 | } |
2197 | } |
2198 | |
2199 | void SkCanvas::onDrawPaint(const SkPaint& paint) { |
2200 | this->internalDrawPaint(paint); |
2201 | } |
2202 | |
2203 | void SkCanvas::internalDrawPaint(const SkPaint& paint) { |
2204 | DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, nullptr, false) |
2205 | |
2206 | while (iter.next()) { |
2207 | iter.fDevice->drawPaint(draw.paint()); |
2208 | } |
2209 | |
2210 | DRAW_END |
2211 | } |
2212 | |
2213 | void SkCanvas::onDrawPoints(PointMode mode, size_t count, const SkPoint pts[], |
2214 | const SkPaint& paint) { |
2215 | if ((long)count <= 0) { |
2216 | return; |
2217 | } |
2218 | |
2219 | SkRect r; |
2220 | const SkRect* bounds = nullptr; |
2221 | if (paint.canComputeFastBounds()) { |
2222 | // special-case 2 points (common for drawing a single line) |
2223 | if (2 == count) { |
2224 | r.set(pts[0], pts[1]); |
2225 | } else { |
2226 | r.setBounds(pts, SkToInt(count)); |
2227 | } |
2228 | if (!r.isFinite()) { |
2229 | return; |
2230 | } |
2231 | SkRect storage; |
2232 | if (this->quickReject(paint.computeFastStrokeBounds(r, &storage))) { |
2233 | return; |
2234 | } |
2235 | bounds = &r; |
2236 | } |
2237 | |
2238 | SkASSERT(pts != nullptr); |
2239 | |
2240 | DRAW_BEGIN(paint, bounds) |
2241 | |
2242 | while (iter.next()) { |
2243 | iter.fDevice->drawPoints(mode, count, pts, draw.paint()); |
2244 | } |
2245 | |
2246 | DRAW_END |
2247 | } |
2248 | |
2249 | static bool needs_autodrawlooper(SkCanvas* canvas, const SkPaint& paint) { |
2250 | return paint.getImageFilter() != nullptr; |
2251 | } |
2252 | |
2253 | void SkCanvas::onDrawRect(const SkRect& r, const SkPaint& paint) { |
2254 | SkASSERT(r.isSorted()); |
2255 | if (paint.canComputeFastBounds()) { |
2256 | SkRect storage; |
2257 | if (this->quickReject(paint.computeFastBounds(r, &storage))) { |
2258 | return; |
2259 | } |
2260 | } |
2261 | |
2262 | if (needs_autodrawlooper(this, paint)) { |
2263 | DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(paint, &r, false) |
2264 | |
2265 | while (iter.next()) { |
2266 | iter.fDevice->drawRect(r, draw.paint()); |
2267 | } |
2268 | |
2269 | DRAW_END |
2270 | } else if (!paint.nothingToDraw()) { |
2271 | this->predrawNotify(&r, &paint, false); |
2272 | SkDrawIter iter(this); |
2273 | while (iter.next()) { |
2274 | iter.fDevice->drawRect(r, paint); |
2275 | } |
2276 | } |
2277 | } |
2278 | |
2279 | void SkCanvas::onDrawRegion(const SkRegion& region, const SkPaint& paint) { |
2280 | SkRect regionRect = SkRect::Make(region.getBounds()); |
2281 | if (paint.canComputeFastBounds()) { |
2282 | SkRect storage; |
2283 | if (this->quickReject(paint.computeFastBounds(regionRect, &storage))) { |
2284 | return; |
2285 | } |
2286 | } |
2287 | |
2288 | DRAW_BEGIN(paint, ®ionRect) |
2289 | |
2290 | while (iter.next()) { |
2291 | iter.fDevice->drawRegion(region, draw.paint()); |
2292 | } |
2293 | |
2294 | DRAW_END |
2295 | } |
2296 | |
2297 | void SkCanvas::onDrawBehind(const SkPaint& paint) { |
2298 | SkIRect bounds; |
2299 | SkDeque::Iter iter(fMCStack, SkDeque::Iter::kBack_IterStart); |
2300 | for (;;) { |
2301 | const MCRec* rec = (const MCRec*)iter.prev(); |
2302 | if (!rec) { |
2303 | return; // no backimages, so nothing to draw |
2304 | } |
2305 | if (rec->fBackImage) { |
2306 | bounds = SkIRect::MakeXYWH(rec->fBackImage->fLoc.fX, rec->fBackImage->fLoc.fY, |
2307 | rec->fBackImage->fImage->width(), |
2308 | rec->fBackImage->fImage->height()); |
2309 | break; |
2310 | } |
2311 | } |
2312 | |
2313 | DRAW_BEGIN(paint, nullptr) |
2314 | |
2315 | while (iter.next()) { |
2316 | SkBaseDevice* dev = iter.fDevice; |
2317 | |
2318 | dev->save(); |
2319 | // We use clipRegion because it is already defined to operate in dev-space |
2320 | // (i.e. ignores the ctm). However, it is going to first translate by -origin, |
2321 | // but we don't want that, so we undo that before calling in. |
2322 | SkRegion rgn(bounds.makeOffset(dev->getOrigin())); |
2323 | dev->clipRegion(rgn, SkClipOp::kIntersect); |
2324 | dev->drawPaint(draw.paint()); |
2325 | dev->restore(fMCRec->fMatrix); |
2326 | } |
2327 | |
2328 | DRAW_END |
2329 | } |
2330 | |
2331 | void SkCanvas::onDrawOval(const SkRect& oval, const SkPaint& paint) { |
2332 | SkASSERT(oval.isSorted()); |
2333 | if (paint.canComputeFastBounds()) { |
2334 | SkRect storage; |
2335 | if (this->quickReject(paint.computeFastBounds(oval, &storage))) { |
2336 | return; |
2337 | } |
2338 | } |
2339 | |
2340 | DRAW_BEGIN(paint, &oval) |
2341 | |
2342 | while (iter.next()) { |
2343 | iter.fDevice->drawOval(oval, draw.paint()); |
2344 | } |
2345 | |
2346 | DRAW_END |
2347 | } |
2348 | |
2349 | void SkCanvas::onDrawArc(const SkRect& oval, SkScalar startAngle, |
2350 | SkScalar sweepAngle, bool useCenter, |
2351 | const SkPaint& paint) { |
2352 | SkASSERT(oval.isSorted()); |
2353 | if (paint.canComputeFastBounds()) { |
2354 | SkRect storage; |
2355 | // Note we're using the entire oval as the bounds. |
2356 | if (this->quickReject(paint.computeFastBounds(oval, &storage))) { |
2357 | return; |
2358 | } |
2359 | } |
2360 | |
2361 | DRAW_BEGIN(paint, &oval) |
2362 | |
2363 | while (iter.next()) { |
2364 | iter.fDevice->drawArc(oval, startAngle, sweepAngle, useCenter, draw.paint()); |
2365 | } |
2366 | |
2367 | DRAW_END |
2368 | } |
2369 | |
2370 | void SkCanvas::onDrawRRect(const SkRRect& rrect, const SkPaint& paint) { |
2371 | if (paint.canComputeFastBounds()) { |
2372 | SkRect storage; |
2373 | if (this->quickReject(paint.computeFastBounds(rrect.getBounds(), &storage))) { |
2374 | return; |
2375 | } |
2376 | } |
2377 | |
2378 | if (rrect.isRect()) { |
2379 | // call the non-virtual version |
2380 | this->SkCanvas::drawRect(rrect.getBounds(), paint); |
2381 | return; |
2382 | } else if (rrect.isOval()) { |
2383 | // call the non-virtual version |
2384 | this->SkCanvas::drawOval(rrect.getBounds(), paint); |
2385 | return; |
2386 | } |
2387 | |
2388 | DRAW_BEGIN(paint, &rrect.getBounds()) |
2389 | |
2390 | while (iter.next()) { |
2391 | iter.fDevice->drawRRect(rrect, draw.paint()); |
2392 | } |
2393 | |
2394 | DRAW_END |
2395 | } |
2396 | |
2397 | void SkCanvas::onDrawDRRect(const SkRRect& outer, const SkRRect& inner, const SkPaint& paint) { |
2398 | if (paint.canComputeFastBounds()) { |
2399 | SkRect storage; |
2400 | if (this->quickReject(paint.computeFastBounds(outer.getBounds(), &storage))) { |
2401 | return; |
2402 | } |
2403 | } |
2404 | |
2405 | DRAW_BEGIN(paint, &outer.getBounds()) |
2406 | |
2407 | while (iter.next()) { |
2408 | iter.fDevice->drawDRRect(outer, inner, draw.paint()); |
2409 | } |
2410 | |
2411 | DRAW_END |
2412 | } |
2413 | |
2414 | void SkCanvas::onDrawPath(const SkPath& path, const SkPaint& paint) { |
2415 | if (!path.isFinite()) { |
2416 | return; |
2417 | } |
2418 | |
2419 | const SkRect& pathBounds = path.getBounds(); |
2420 | if (!path.isInverseFillType() && paint.canComputeFastBounds()) { |
2421 | SkRect storage; |
2422 | if (this->quickReject(paint.computeFastBounds(pathBounds, &storage))) { |
2423 | return; |
2424 | } |
2425 | } |
2426 | |
2427 | if (pathBounds.width() <= 0 && pathBounds.height() <= 0) { |
2428 | if (path.isInverseFillType()) { |
2429 | this->internalDrawPaint(paint); |
2430 | return; |
2431 | } |
2432 | } |
2433 | |
2434 | DRAW_BEGIN(paint, &pathBounds) |
2435 | |
2436 | while (iter.next()) { |
2437 | iter.fDevice->drawPath(path, draw.paint()); |
2438 | } |
2439 | |
2440 | DRAW_END |
2441 | } |
2442 | |
2443 | bool SkCanvas::canDrawBitmapAsSprite(SkScalar x, SkScalar y, int w, int h, const SkPaint& paint) { |
2444 | if (!paint.getImageFilter()) { |
2445 | return false; |
2446 | } |
2447 | |
2448 | const SkMatrix& ctm = this->getTotalMatrix(); |
2449 | if (!SkTreatAsSprite(ctm, SkISize::Make(w, h), paint)) { |
2450 | return false; |
2451 | } |
2452 | |
2453 | // The other paint effects need to be applied before the image filter, but the sprite draw |
2454 | // applies the filter explicitly first. |
2455 | if (paint.getAlphaf() < 1.f || paint.getColorFilter() || paint.getMaskFilter()) { |
2456 | return false; |
2457 | } |
2458 | // Currently we can only use the filterSprite code if we are clipped to the bitmap's bounds. |
2459 | // Once we can filter and the filter will return a result larger than itself, we should be |
2460 | // able to remove this constraint. |
2461 | // skbug.com/4526 |
2462 | // |
2463 | SkPoint pt; |
2464 | ctm.mapXY(x, y, &pt); |
2465 | SkIRect ir = SkIRect::MakeXYWH(SkScalarRoundToInt(pt.x()), SkScalarRoundToInt(pt.y()), w, h); |
2466 | return ir.contains(fMCRec->fRasterClip.getBounds()); |
2467 | } |
2468 | |
2469 | // Given storage for a real paint, and an optional paint parameter, clean-up the param (if non-null) |
2470 | // given the drawing semantics for drawImage/bitmap (skbug.com/7804) and return it, or the original |
2471 | // null. |
2472 | static const SkPaint* init_image_paint(SkPaint* real, const SkPaint* paintParam) { |
2473 | if (paintParam) { |
2474 | *real = *paintParam; |
2475 | real->setStyle(SkPaint::kFill_Style); |
2476 | real->setPathEffect(nullptr); |
2477 | paintParam = real; |
2478 | } |
2479 | return paintParam; |
2480 | } |
2481 | |
2482 | void SkCanvas::onDrawImage(const SkImage* image, SkScalar x, SkScalar y, const SkPaint* paint) { |
2483 | SkPaint realPaint; |
2484 | paint = init_image_paint(&realPaint, paint); |
2485 | |
2486 | SkRect bounds = SkRect::MakeXYWH(x, y, |
2487 | SkIntToScalar(image->width()), SkIntToScalar(image->height())); |
2488 | if (nullptr == paint || paint->canComputeFastBounds()) { |
2489 | SkRect tmp = bounds; |
2490 | if (paint) { |
2491 | paint->computeFastBounds(tmp, &tmp); |
2492 | } |
2493 | if (this->quickReject(tmp)) { |
2494 | return; |
2495 | } |
2496 | } |
2497 | // At this point we need a real paint object. If the caller passed null, then we should |
2498 | // use realPaint (in its default state). If the caller did pass a paint, then we have copied |
2499 | // (and modified) it in realPaint. Thus either way, "realPaint" is what we want to use. |
2500 | paint = &realPaint; |
2501 | |
2502 | sk_sp<SkSpecialImage> special; |
2503 | bool drawAsSprite = this->canDrawBitmapAsSprite(x, y, image->width(), image->height(), |
2504 | *paint); |
2505 | if (drawAsSprite && paint->getImageFilter()) { |
2506 | special = this->getDevice()->makeSpecial(image); |
2507 | if (!special) { |
2508 | drawAsSprite = false; |
2509 | } |
2510 | } |
2511 | |
2512 | DRAW_BEGIN_DRAWBITMAP(*paint, drawAsSprite, &bounds) |
2513 | |
2514 | while (iter.next()) { |
2515 | const SkPaint& pnt = draw.paint(); |
2516 | if (special) { |
2517 | SkPoint pt; |
2518 | iter.fDevice->localToDevice().mapXY(x, y, &pt); |
2519 | iter.fDevice->drawSpecial(special.get(), |
2520 | SkScalarRoundToInt(pt.fX), |
2521 | SkScalarRoundToInt(pt.fY), pnt, |
2522 | nullptr, SkMatrix::I()); |
2523 | } else { |
2524 | iter.fDevice->drawImageRect( |
2525 | image, nullptr, SkRect::MakeXYWH(x, y, image->width(), image->height()), pnt, |
2526 | kStrict_SrcRectConstraint); |
2527 | } |
2528 | } |
2529 | |
2530 | DRAW_END |
2531 | } |
2532 | |
2533 | void SkCanvas::onDrawImageRect(const SkImage* image, const SkRect* src, const SkRect& dst, |
2534 | const SkPaint* paint, SrcRectConstraint constraint) { |
2535 | SkPaint realPaint; |
2536 | paint = init_image_paint(&realPaint, paint); |
2537 | |
2538 | if (nullptr == paint || paint->canComputeFastBounds()) { |
2539 | SkRect storage = dst; |
2540 | if (paint) { |
2541 | paint->computeFastBounds(dst, &storage); |
2542 | } |
2543 | if (this->quickReject(storage)) { |
2544 | return; |
2545 | } |
2546 | } |
2547 | paint = &realPaint; |
2548 | |
2549 | DRAW_BEGIN_CHECK_COMPLETE_OVERWRITE(*paint, &dst, image->isOpaque()) |
2550 | |
2551 | while (iter.next()) { |
2552 | iter.fDevice->drawImageRect(image, src, dst, draw.paint(), constraint); |
2553 | } |
2554 | |
2555 | DRAW_END |
2556 | } |
2557 | |
2558 | void SkCanvas::onDrawImageNine(const SkImage* image, const SkIRect& center, const SkRect& dst, |
2559 | const SkPaint* paint) { |
2560 | SkPaint realPaint; |
2561 | paint = init_image_paint(&realPaint, paint); |
2562 | |
2563 | if (nullptr == paint || paint->canComputeFastBounds()) { |
2564 | SkRect storage; |
2565 | if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) { |
2566 | return; |
2567 | } |
2568 | } |
2569 | paint = &realPaint; |
2570 | |
2571 | DRAW_BEGIN(*paint, &dst) |
2572 | |
2573 | while (iter.next()) { |
2574 | iter.fDevice->drawImageNine(image, center, dst, draw.paint()); |
2575 | } |
2576 | |
2577 | DRAW_END |
2578 | } |
2579 | |
2580 | void SkCanvas::onDrawImageLattice(const SkImage* image, const Lattice& lattice, const SkRect& dst, |
2581 | const SkPaint* paint) { |
2582 | SkPaint realPaint; |
2583 | paint = init_image_paint(&realPaint, paint); |
2584 | |
2585 | if (nullptr == paint || paint->canComputeFastBounds()) { |
2586 | SkRect storage; |
2587 | if (this->quickReject(paint ? paint->computeFastBounds(dst, &storage) : dst)) { |
2588 | return; |
2589 | } |
2590 | } |
2591 | paint = &realPaint; |
2592 | |
2593 | DRAW_BEGIN(*paint, &dst) |
2594 | |
2595 | while (iter.next()) { |
2596 | iter.fDevice->drawImageLattice(image, lattice, dst, draw.paint()); |
2597 | } |
2598 | |
2599 | DRAW_END |
2600 | } |
2601 | |
2602 | void SkCanvas::onDrawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, |
2603 | const SkPaint& paint) { |
2604 | SkRect storage; |
2605 | const SkRect* bounds = nullptr; |
2606 | if (paint.canComputeFastBounds()) { |
2607 | storage = blob->bounds().makeOffset(x, y); |
2608 | SkRect tmp; |
2609 | if (this->quickReject(paint.computeFastBounds(storage, &tmp))) { |
2610 | return; |
2611 | } |
2612 | bounds = &storage; |
2613 | } |
2614 | |
2615 | // We cannot filter in the looper as we normally do, because the paint is |
2616 | // incomplete at this point (text-related attributes are embedded within blob run paints). |
2617 | DRAW_BEGIN(paint, bounds) |
2618 | |
2619 | while (iter.next()) { |
2620 | fScratchGlyphRunBuilder->drawTextBlob(draw.paint(), *blob, {x, y}, iter.fDevice); |
2621 | } |
2622 | |
2623 | DRAW_END |
2624 | } |
2625 | |
2626 | // These call the (virtual) onDraw... method |
2627 | void SkCanvas::drawSimpleText(const void* text, size_t byteLength, SkTextEncoding encoding, |
2628 | SkScalar x, SkScalar y, const SkFont& font, const SkPaint& paint) { |
2629 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2630 | if (byteLength) { |
2631 | sk_msan_assert_initialized(text, SkTAddOffset<const void>(text, byteLength)); |
2632 | this->drawTextBlob(SkTextBlob::MakeFromText(text, byteLength, font, encoding), x, y, paint); |
2633 | } |
2634 | } |
2635 | |
2636 | void SkCanvas::drawTextBlob(const SkTextBlob* blob, SkScalar x, SkScalar y, |
2637 | const SkPaint& paint) { |
2638 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2639 | RETURN_ON_NULL(blob); |
2640 | RETURN_ON_FALSE(blob->bounds().makeOffset(x, y).isFinite()); |
2641 | this->onDrawTextBlob(blob, x, y, paint); |
2642 | } |
2643 | |
2644 | void SkCanvas::onDrawVerticesObject(const SkVertices* vertices, SkBlendMode bmode, |
2645 | const SkPaint& paint) { |
2646 | DRAW_BEGIN(paint, nullptr) |
2647 | |
2648 | while (iter.next()) { |
2649 | // In the common case of one iteration we could std::move vertices here. |
2650 | iter.fDevice->drawVertices(vertices, bmode, draw.paint()); |
2651 | } |
2652 | |
2653 | DRAW_END |
2654 | } |
2655 | |
2656 | void SkCanvas::drawPatch(const SkPoint cubics[12], const SkColor colors[4], |
2657 | const SkPoint texCoords[4], SkBlendMode bmode, |
2658 | const SkPaint& paint) { |
2659 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2660 | if (nullptr == cubics) { |
2661 | return; |
2662 | } |
2663 | |
2664 | this->onDrawPatch(cubics, colors, texCoords, bmode, paint); |
2665 | } |
2666 | |
2667 | void SkCanvas::onDrawPatch(const SkPoint cubics[12], const SkColor colors[4], |
2668 | const SkPoint texCoords[4], SkBlendMode bmode, |
2669 | const SkPaint& paint) { |
2670 | // Since a patch is always within the convex hull of the control points, we discard it when its |
2671 | // bounding rectangle is completely outside the current clip. |
2672 | SkRect bounds; |
2673 | bounds.setBounds(cubics, SkPatchUtils::kNumCtrlPts); |
2674 | if (this->quickReject(bounds)) { |
2675 | return; |
2676 | } |
2677 | |
2678 | DRAW_BEGIN(paint, nullptr) |
2679 | |
2680 | while (iter.next()) { |
2681 | iter.fDevice->drawPatch(cubics, colors, texCoords, bmode, paint); |
2682 | } |
2683 | |
2684 | DRAW_END |
2685 | } |
2686 | |
2687 | void SkCanvas::drawDrawable(SkDrawable* dr, SkScalar x, SkScalar y) { |
2688 | #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK |
2689 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2690 | #endif |
2691 | RETURN_ON_NULL(dr); |
2692 | if (x || y) { |
2693 | SkMatrix matrix = SkMatrix::MakeTrans(x, y); |
2694 | this->onDrawDrawable(dr, &matrix); |
2695 | } else { |
2696 | this->onDrawDrawable(dr, nullptr); |
2697 | } |
2698 | } |
2699 | |
2700 | void SkCanvas::drawDrawable(SkDrawable* dr, const SkMatrix* matrix) { |
2701 | #ifndef SK_BUILD_FOR_ANDROID_FRAMEWORK |
2702 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2703 | #endif |
2704 | RETURN_ON_NULL(dr); |
2705 | if (matrix && matrix->isIdentity()) { |
2706 | matrix = nullptr; |
2707 | } |
2708 | this->onDrawDrawable(dr, matrix); |
2709 | } |
2710 | |
2711 | void SkCanvas::onDrawDrawable(SkDrawable* dr, const SkMatrix* matrix) { |
2712 | // drawable bounds are no longer reliable (e.g. android displaylist) |
2713 | // so don't use them for quick-reject |
2714 | this->getDevice()->drawDrawable(dr, matrix, this); |
2715 | } |
2716 | |
2717 | void SkCanvas::onDrawAtlas(const SkImage* atlas, const SkRSXform xform[], const SkRect tex[], |
2718 | const SkColor colors[], int count, SkBlendMode bmode, |
2719 | const SkRect* cull, const SkPaint* paint) { |
2720 | if (cull && this->quickReject(*cull)) { |
2721 | return; |
2722 | } |
2723 | |
2724 | SkPaint pnt; |
2725 | if (paint) { |
2726 | pnt = *paint; |
2727 | } |
2728 | |
2729 | DRAW_BEGIN(pnt, nullptr) |
2730 | while (iter.next()) { |
2731 | iter.fDevice->drawAtlas(atlas, xform, tex, colors, count, bmode, pnt); |
2732 | } |
2733 | DRAW_END |
2734 | } |
2735 | |
2736 | void SkCanvas::onDrawAnnotation(const SkRect& rect, const char key[], SkData* value) { |
2737 | SkASSERT(key); |
2738 | |
2739 | SkPaint paint; |
2740 | DRAW_BEGIN(paint, nullptr) |
2741 | while (iter.next()) { |
2742 | iter.fDevice->drawAnnotation(rect, key, value); |
2743 | } |
2744 | DRAW_END |
2745 | } |
2746 | |
2747 | void SkCanvas::onDrawEdgeAAQuad(const SkRect& r, const SkPoint clip[4], QuadAAFlags edgeAA, |
2748 | const SkColor4f& color, SkBlendMode mode) { |
2749 | SkASSERT(r.isSorted()); |
2750 | |
2751 | // If this used a paint, it would be a filled color with blend mode, which does not |
2752 | // need to use an autodraw loop, so use SkDrawIter directly. |
2753 | if (this->quickReject(r)) { |
2754 | return; |
2755 | } |
2756 | |
2757 | this->predrawNotify(&r, nullptr, false); |
2758 | SkDrawIter iter(this); |
2759 | while(iter.next()) { |
2760 | iter.fDevice->drawEdgeAAQuad(r, clip, edgeAA, color, mode); |
2761 | } |
2762 | } |
2763 | |
2764 | void SkCanvas::onDrawEdgeAAImageSet(const ImageSetEntry imageSet[], int count, |
2765 | const SkPoint dstClips[], const SkMatrix preViewMatrices[], |
2766 | const SkPaint* paint, SrcRectConstraint constraint) { |
2767 | if (count <= 0) { |
2768 | // Nothing to draw |
2769 | return; |
2770 | } |
2771 | |
2772 | SkPaint realPaint; |
2773 | init_image_paint(&realPaint, paint); |
2774 | |
2775 | // We could calculate the set's dstRect union to always check quickReject(), but we can't reject |
2776 | // individual entries and Chromium's occlusion culling already makes it likely that at least one |
2777 | // entry will be visible. So, we only calculate the draw bounds when it's trivial (count == 1), |
2778 | // or we need it for the autolooper (since it greatly improves image filter perf). |
2779 | bool needsAutoLooper = needs_autodrawlooper(this, realPaint); |
2780 | bool setBoundsValid = count == 1 || needsAutoLooper; |
2781 | SkRect setBounds = imageSet[0].fDstRect; |
2782 | if (imageSet[0].fMatrixIndex >= 0) { |
2783 | // Account for the per-entry transform that is applied prior to the CTM when drawing |
2784 | preViewMatrices[imageSet[0].fMatrixIndex].mapRect(&setBounds); |
2785 | } |
2786 | if (needsAutoLooper) { |
2787 | for (int i = 1; i < count; ++i) { |
2788 | SkRect entryBounds = imageSet[i].fDstRect; |
2789 | if (imageSet[i].fMatrixIndex >= 0) { |
2790 | preViewMatrices[imageSet[i].fMatrixIndex].mapRect(&entryBounds); |
2791 | } |
2792 | setBounds.joinPossiblyEmptyRect(entryBounds); |
2793 | } |
2794 | } |
2795 | |
2796 | // If we happen to have the draw bounds, though, might as well check quickReject(). |
2797 | if (setBoundsValid && realPaint.canComputeFastBounds()) { |
2798 | SkRect tmp; |
2799 | if (this->quickReject(realPaint.computeFastBounds(setBounds, &tmp))) { |
2800 | return; |
2801 | } |
2802 | } |
2803 | |
2804 | if (needsAutoLooper) { |
2805 | SkASSERT(setBoundsValid); |
2806 | DRAW_BEGIN(realPaint, &setBounds) |
2807 | while (iter.next()) { |
2808 | iter.fDevice->drawEdgeAAImageSet( |
2809 | imageSet, count, dstClips, preViewMatrices, draw.paint(), constraint); |
2810 | } |
2811 | DRAW_END |
2812 | } else { |
2813 | this->predrawNotify(); |
2814 | SkDrawIter iter(this); |
2815 | while(iter.next()) { |
2816 | iter.fDevice->drawEdgeAAImageSet( |
2817 | imageSet, count, dstClips, preViewMatrices, realPaint, constraint); |
2818 | } |
2819 | } |
2820 | } |
2821 | |
2822 | ////////////////////////////////////////////////////////////////////////////// |
2823 | // These methods are NOT virtual, and therefore must call back into virtual |
2824 | // methods, rather than actually drawing themselves. |
2825 | ////////////////////////////////////////////////////////////////////////////// |
2826 | |
2827 | void SkCanvas::drawColor(SkColor c, SkBlendMode mode) { |
2828 | SkPaint paint; |
2829 | paint.setColor(c); |
2830 | paint.setBlendMode(mode); |
2831 | this->drawPaint(paint); |
2832 | } |
2833 | |
2834 | void SkCanvas::drawPoint(SkScalar x, SkScalar y, const SkPaint& paint) { |
2835 | const SkPoint pt = { x, y }; |
2836 | this->drawPoints(kPoints_PointMode, 1, &pt, paint); |
2837 | } |
2838 | |
2839 | void SkCanvas::drawLine(SkScalar x0, SkScalar y0, SkScalar x1, SkScalar y1, const SkPaint& paint) { |
2840 | SkPoint pts[2]; |
2841 | pts[0].set(x0, y0); |
2842 | pts[1].set(x1, y1); |
2843 | this->drawPoints(kLines_PointMode, 2, pts, paint); |
2844 | } |
2845 | |
2846 | void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius, const SkPaint& paint) { |
2847 | if (radius < 0) { |
2848 | radius = 0; |
2849 | } |
2850 | |
2851 | SkRect r; |
2852 | r.setLTRB(cx - radius, cy - radius, cx + radius, cy + radius); |
2853 | this->drawOval(r, paint); |
2854 | } |
2855 | |
2856 | void SkCanvas::drawRoundRect(const SkRect& r, SkScalar rx, SkScalar ry, |
2857 | const SkPaint& paint) { |
2858 | if (rx > 0 && ry > 0) { |
2859 | SkRRect rrect; |
2860 | rrect.setRectXY(r, rx, ry); |
2861 | this->drawRRect(rrect, paint); |
2862 | } else { |
2863 | this->drawRect(r, paint); |
2864 | } |
2865 | } |
2866 | |
2867 | void SkCanvas::drawArc(const SkRect& oval, SkScalar startAngle, |
2868 | SkScalar sweepAngle, bool useCenter, |
2869 | const SkPaint& paint) { |
2870 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2871 | if (oval.isEmpty() || !sweepAngle) { |
2872 | return; |
2873 | } |
2874 | this->onDrawArc(oval, startAngle, sweepAngle, useCenter, paint); |
2875 | } |
2876 | |
2877 | /////////////////////////////////////////////////////////////////////////////// |
2878 | #ifdef SK_DISABLE_SKPICTURE |
2879 | void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) {} |
2880 | |
2881 | |
2882 | void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, |
2883 | const SkPaint* paint) {} |
2884 | #else |
2885 | /** |
2886 | * This constant is trying to balance the speed of ref'ing a subpicture into a parent picture, |
2887 | * against the playback cost of recursing into the subpicture to get at its actual ops. |
2888 | * |
2889 | * For now we pick a conservatively small value, though measurement (and other heuristics like |
2890 | * the type of ops contained) may justify changing this value. |
2891 | */ |
2892 | #define kMaxPictureOpsToUnrollInsteadOfRef 1 |
2893 | |
2894 | void SkCanvas::drawPicture(const SkPicture* picture, const SkMatrix* matrix, const SkPaint* paint) { |
2895 | TRACE_EVENT0("skia" , TRACE_FUNC); |
2896 | RETURN_ON_NULL(picture); |
2897 | |
2898 | if (matrix && matrix->isIdentity()) { |
2899 | matrix = nullptr; |
2900 | } |
2901 | if (picture->approximateOpCount() <= kMaxPictureOpsToUnrollInsteadOfRef) { |
2902 | SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect()); |
2903 | picture->playback(this); |
2904 | } else { |
2905 | this->onDrawPicture(picture, matrix, paint); |
2906 | } |
2907 | } |
2908 | |
2909 | void SkCanvas::onDrawPicture(const SkPicture* picture, const SkMatrix* matrix, |
2910 | const SkPaint* paint) { |
2911 | if (!paint || paint->canComputeFastBounds()) { |
2912 | SkRect bounds = picture->cullRect(); |
2913 | if (paint) { |
2914 | paint->computeFastBounds(bounds, &bounds); |
2915 | } |
2916 | if (matrix) { |
2917 | matrix->mapRect(&bounds); |
2918 | } |
2919 | if (this->quickReject(bounds)) { |
2920 | return; |
2921 | } |
2922 | } |
2923 | |
2924 | SkAutoCanvasMatrixPaint acmp(this, matrix, paint, picture->cullRect()); |
2925 | picture->playback(this); |
2926 | } |
2927 | #endif |
2928 | |
2929 | /////////////////////////////////////////////////////////////////////////////// |
2930 | /////////////////////////////////////////////////////////////////////////////// |
2931 | |
2932 | SkCanvas::LayerIter::LayerIter(SkCanvas* canvas) { |
2933 | static_assert(sizeof(fStorage) >= sizeof(SkDrawIter), "fStorage_too_small" ); |
2934 | |
2935 | SkASSERT(canvas); |
2936 | |
2937 | fImpl = new (fStorage) SkDrawIter(canvas); |
2938 | // This advances the base iterator to the first device and caches its origin, |
2939 | // correctly handling the case where there are no devices. |
2940 | this->next(); |
2941 | } |
2942 | |
2943 | SkCanvas::LayerIter::~LayerIter() { |
2944 | fImpl->~SkDrawIter(); |
2945 | } |
2946 | |
2947 | void SkCanvas::LayerIter::next() { |
2948 | fDone = !fImpl->next(); |
2949 | if (!fDone) { |
2950 | // Cache the device origin. LayerIter is only used in Android, which doesn't use image |
2951 | // filters, so its devices will always be able to report the origin exactly. |
2952 | fDeviceOrigin = fImpl->fDevice->getOrigin(); |
2953 | } |
2954 | } |
2955 | |
2956 | SkBaseDevice* SkCanvas::LayerIter::device() const { |
2957 | return fImpl->fDevice; |
2958 | } |
2959 | |
2960 | const SkMatrix& SkCanvas::LayerIter::matrix() const { |
2961 | return fImpl->fDevice->localToDevice(); |
2962 | } |
2963 | |
2964 | const SkPaint& SkCanvas::LayerIter::paint() const { |
2965 | const SkPaint* paint = fImpl->getPaint(); |
2966 | if (nullptr == paint) { |
2967 | paint = &fDefaultPaint; |
2968 | } |
2969 | return *paint; |
2970 | } |
2971 | |
2972 | SkIRect SkCanvas::LayerIter::clipBounds() const { |
2973 | return fImpl->fDevice->getGlobalBounds(); |
2974 | } |
2975 | |
2976 | int SkCanvas::LayerIter::x() const { return fDeviceOrigin.fX; } |
2977 | int SkCanvas::LayerIter::y() const { return fDeviceOrigin.fY; } |
2978 | |
2979 | /////////////////////////////////////////////////////////////////////////////// |
2980 | |
2981 | SkCanvas::ImageSetEntry::ImageSetEntry() = default; |
2982 | SkCanvas::ImageSetEntry::~ImageSetEntry() = default; |
2983 | SkCanvas::ImageSetEntry::ImageSetEntry(const ImageSetEntry&) = default; |
2984 | SkCanvas::ImageSetEntry& SkCanvas::ImageSetEntry::operator=(const ImageSetEntry&) = default; |
2985 | |
2986 | SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect, |
2987 | const SkRect& dstRect, int matrixIndex, float alpha, |
2988 | unsigned aaFlags, bool hasClip) |
2989 | : fImage(std::move(image)) |
2990 | , fSrcRect(srcRect) |
2991 | , fDstRect(dstRect) |
2992 | , fMatrixIndex(matrixIndex) |
2993 | , fAlpha(alpha) |
2994 | , fAAFlags(aaFlags) |
2995 | , fHasClip(hasClip) {} |
2996 | |
2997 | SkCanvas::ImageSetEntry::ImageSetEntry(sk_sp<const SkImage> image, const SkRect& srcRect, |
2998 | const SkRect& dstRect, float alpha, unsigned aaFlags) |
2999 | : fImage(std::move(image)) |
3000 | , fSrcRect(srcRect) |
3001 | , fDstRect(dstRect) |
3002 | , fAlpha(alpha) |
3003 | , fAAFlags(aaFlags) {} |
3004 | |
3005 | /////////////////////////////////////////////////////////////////////////////// |
3006 | |
3007 | std::unique_ptr<SkCanvas> SkCanvas::MakeRasterDirect(const SkImageInfo& info, void* pixels, |
3008 | size_t rowBytes, const SkSurfaceProps* props) { |
3009 | if (!SkSurfaceValidateRasterInfo(info, rowBytes)) { |
3010 | return nullptr; |
3011 | } |
3012 | |
3013 | SkBitmap bitmap; |
3014 | if (!bitmap.installPixels(info, pixels, rowBytes)) { |
3015 | return nullptr; |
3016 | } |
3017 | |
3018 | return props ? |
3019 | std::make_unique<SkCanvas>(bitmap, *props) : |
3020 | std::make_unique<SkCanvas>(bitmap); |
3021 | } |
3022 | |
3023 | /////////////////////////////////////////////////////////////////////////////// |
3024 | |
3025 | SkNoDrawCanvas::SkNoDrawCanvas(int width, int height) |
3026 | : INHERITED(SkIRect::MakeWH(width, height)) {} |
3027 | |
3028 | SkNoDrawCanvas::SkNoDrawCanvas(const SkIRect& bounds) |
3029 | : INHERITED(bounds) {} |
3030 | |
3031 | SkNoDrawCanvas::SkNoDrawCanvas(sk_sp<SkBaseDevice> device) |
3032 | : INHERITED(device) {} |
3033 | |
3034 | SkCanvas::SaveLayerStrategy SkNoDrawCanvas::getSaveLayerStrategy(const SaveLayerRec& rec) { |
3035 | (void)this->INHERITED::getSaveLayerStrategy(rec); |
3036 | return kNoLayer_SaveLayerStrategy; |
3037 | } |
3038 | |
3039 | bool SkNoDrawCanvas::onDoSaveBehind(const SkRect*) { |
3040 | return false; |
3041 | } |
3042 | |
3043 | /////////////////////////////////////////////////////////////////////////////// |
3044 | |
3045 | static_assert((int)SkRegion::kDifference_Op == (int)kDifference_SkClipOp, "" ); |
3046 | static_assert((int)SkRegion::kIntersect_Op == (int)kIntersect_SkClipOp, "" ); |
3047 | static_assert((int)SkRegion::kUnion_Op == (int)kUnion_SkClipOp, "" ); |
3048 | static_assert((int)SkRegion::kXOR_Op == (int)kXOR_SkClipOp, "" ); |
3049 | static_assert((int)SkRegion::kReverseDifference_Op == (int)kReverseDifference_SkClipOp, "" ); |
3050 | static_assert((int)SkRegion::kReplace_Op == (int)kReplace_SkClipOp, "" ); |
3051 | |
3052 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
3053 | |
3054 | SkRasterHandleAllocator::Handle SkCanvas::accessTopRasterHandle() const { |
3055 | if (fAllocator && fMCRec->fTopLayer->fDevice) { |
3056 | const auto& dev = fMCRec->fTopLayer->fDevice; |
3057 | SkRasterHandleAllocator::Handle handle = dev->getRasterHandle(); |
3058 | SkIRect clip = dev->devClipBounds(); |
3059 | if (!clip.intersect({0, 0, dev->width(), dev->height()})) { |
3060 | clip.setEmpty(); |
3061 | } |
3062 | |
3063 | fAllocator->updateHandle(handle, dev->localToDevice(), clip); |
3064 | return handle; |
3065 | } |
3066 | return nullptr; |
3067 | } |
3068 | |
3069 | static bool install(SkBitmap* bm, const SkImageInfo& info, |
3070 | const SkRasterHandleAllocator::Rec& rec) { |
3071 | return bm->installPixels(info, rec.fPixels, rec.fRowBytes, rec.fReleaseProc, rec.fReleaseCtx); |
3072 | } |
3073 | |
3074 | SkRasterHandleAllocator::Handle SkRasterHandleAllocator::allocBitmap(const SkImageInfo& info, |
3075 | SkBitmap* bm) { |
3076 | SkRasterHandleAllocator::Rec rec; |
3077 | if (!this->allocHandle(info, &rec) || !install(bm, info, rec)) { |
3078 | return nullptr; |
3079 | } |
3080 | return rec.fHandle; |
3081 | } |
3082 | |
3083 | std::unique_ptr<SkCanvas> |
3084 | SkRasterHandleAllocator::MakeCanvas(std::unique_ptr<SkRasterHandleAllocator> alloc, |
3085 | const SkImageInfo& info, const Rec* rec) { |
3086 | if (!alloc || !SkSurfaceValidateRasterInfo(info, rec ? rec->fRowBytes : kIgnoreRowBytesValue)) { |
3087 | return nullptr; |
3088 | } |
3089 | |
3090 | SkBitmap bm; |
3091 | Handle hndl; |
3092 | |
3093 | if (rec) { |
3094 | hndl = install(&bm, info, *rec) ? rec->fHandle : nullptr; |
3095 | } else { |
3096 | hndl = alloc->allocBitmap(info, &bm); |
3097 | } |
3098 | return hndl ? std::unique_ptr<SkCanvas>(new SkCanvas(bm, std::move(alloc), hndl)) : nullptr; |
3099 | } |
3100 | |
3101 | /////////////////////////////////////////////////////////////////////////////////////////////////// |
3102 | |