/src/skia/src/core/SkCanvasPriv.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2018 Google Inc. |
3 | | * |
4 | | * Use of this source code is governed by a BSD-style license that can be |
5 | | * found in the LICENSE file. |
6 | | */ |
7 | | |
8 | | #include "src/core/SkCanvasPriv.h" |
9 | | |
10 | | #include "include/core/SkBlendMode.h" |
11 | | #include "include/core/SkColor.h" |
12 | | #include "include/core/SkColorFilter.h" |
13 | | #include "include/core/SkImageFilter.h" |
14 | | #include "include/core/SkMaskFilter.h" |
15 | | #include "include/core/SkMatrix.h" |
16 | | #include "include/core/SkRect.h" |
17 | | #include "include/core/SkRefCnt.h" |
18 | | #include "include/core/SkShader.h" |
19 | | #include "include/private/base/SkAlign.h" |
20 | | #include "include/private/base/SkAssert.h" |
21 | | #include "include/private/base/SkTo.h" |
22 | | #include "src/base/SkAutoMalloc.h" |
23 | | #include "src/core/SkMaskFilterBase.h" |
24 | | #include "src/core/SkReadBuffer.h" |
25 | | #include "src/core/SkWriteBuffer.h" |
26 | | #include "src/core/SkWriter32.h" |
27 | | |
28 | | #include <utility> |
29 | | #include <cstdint> |
30 | | |
31 | | SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix, |
32 | | const SkPaint* paint, const SkRect& bounds) |
33 | | : fCanvas(canvas) |
34 | 26.6k | , fSaveCount(canvas->getSaveCount()) { |
35 | 26.6k | if (paint) { |
36 | 1.60k | SkRect newBounds = bounds; |
37 | 1.60k | if (matrix) { |
38 | 178 | matrix->mapRect(&newBounds); |
39 | 178 | } |
40 | 1.60k | canvas->saveLayer(&newBounds, paint); |
41 | 25.0k | } else if (matrix) { |
42 | 332 | canvas->save(); |
43 | 332 | } |
44 | | |
45 | 26.6k | if (matrix) { |
46 | 510 | canvas->concat(*matrix); |
47 | 510 | } |
48 | 26.6k | } |
49 | | |
50 | 26.6k | SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() { |
51 | 26.6k | fCanvas->restoreToCount(fSaveCount); |
52 | 26.6k | } |
53 | | |
54 | | /////////////////////////////////////////////////////////////////////////////////////////////////// |
55 | | |
56 | 1.33k | bool SkCanvasPriv::ReadLattice(SkReadBuffer& buffer, SkCanvas::Lattice* lattice) { |
57 | 1.33k | lattice->fXCount = buffer.readInt(); |
58 | 1.33k | lattice->fXDivs = buffer.skipT<int32_t>(lattice->fXCount); |
59 | 1.33k | lattice->fYCount = buffer.readInt(); |
60 | 1.33k | lattice->fYDivs = buffer.skipT<int32_t>(lattice->fYCount); |
61 | 1.33k | int flagCount = buffer.readInt(); |
62 | 1.33k | lattice->fRectTypes = nullptr; |
63 | 1.33k | lattice->fColors = nullptr; |
64 | 1.33k | if (flagCount) { |
65 | 257 | lattice->fRectTypes = buffer.skipT<SkCanvas::Lattice::RectType>(flagCount); |
66 | 257 | lattice->fColors = buffer.skipT<SkColor>(flagCount); |
67 | 257 | } |
68 | 1.33k | lattice->fBounds = buffer.skipT<SkIRect>(); |
69 | 1.33k | return buffer.isValid(); |
70 | 1.33k | } |
71 | | |
72 | 0 | size_t SkCanvasPriv::WriteLattice(void* buffer, const SkCanvas::Lattice& lattice) { |
73 | 0 | int flagCount = lattice.fRectTypes ? (lattice.fXCount + 1) * (lattice.fYCount + 1) : 0; |
74 | |
|
75 | 0 | const size_t size = (1 + lattice.fXCount + 1 + lattice.fYCount + 1) * sizeof(int32_t) + |
76 | 0 | SkAlign4(flagCount * sizeof(SkCanvas::Lattice::RectType)) + |
77 | 0 | SkAlign4(flagCount * sizeof(SkColor)) + |
78 | 0 | sizeof(SkIRect); |
79 | |
|
80 | 0 | if (buffer) { |
81 | 0 | SkWriter32 writer(buffer, size); |
82 | 0 | writer.write32(lattice.fXCount); |
83 | 0 | writer.write(lattice.fXDivs, lattice.fXCount * sizeof(uint32_t)); |
84 | 0 | writer.write32(lattice.fYCount); |
85 | 0 | writer.write(lattice.fYDivs, lattice.fYCount * sizeof(uint32_t)); |
86 | 0 | writer.write32(flagCount); |
87 | 0 | writer.writePad(lattice.fRectTypes, flagCount * sizeof(uint8_t)); |
88 | 0 | writer.write(lattice.fColors, flagCount * sizeof(SkColor)); |
89 | 0 | SkASSERT(lattice.fBounds); |
90 | 0 | writer.write(lattice.fBounds, sizeof(SkIRect)); |
91 | 0 | SkASSERT(writer.bytesWritten() == size); |
92 | 0 | } |
93 | 0 | return size; |
94 | 0 | } Unexecuted instantiation: SkCanvasPriv::WriteLattice(void*, SkCanvas::Lattice const&) Unexecuted instantiation: SkCanvasPriv::WriteLattice(void*, SkCanvas::Lattice const&) |
95 | | |
96 | 0 | void SkCanvasPriv::WriteLattice(SkWriteBuffer& buffer, const SkCanvas::Lattice& lattice) { |
97 | 0 | const size_t size = WriteLattice(nullptr, lattice); |
98 | 0 | SkAutoSMalloc<1024> storage(size); |
99 | 0 | WriteLattice(storage.get(), lattice); |
100 | 0 | buffer.writePad32(storage.get(), size); |
101 | 0 | } |
102 | | |
103 | | void SkCanvasPriv::GetDstClipAndMatrixCounts(const SkCanvas::ImageSetEntry set[], int count, |
104 | 603 | int* totalDstClipCount, int* totalMatrixCount) { |
105 | 603 | int dstClipCount = 0; |
106 | 603 | int maxMatrixIndex = -1; |
107 | 705 | for (int i = 0; i < count; ++i) { |
108 | 102 | dstClipCount += 4 * set[i].fHasClip; |
109 | 102 | if (set[i].fMatrixIndex > maxMatrixIndex) { |
110 | 19 | maxMatrixIndex = set[i].fMatrixIndex; |
111 | 19 | } |
112 | 102 | } |
113 | | |
114 | 603 | *totalDstClipCount = dstClipCount; |
115 | 603 | *totalMatrixCount = maxMatrixIndex + 1; |
116 | 603 | } |
117 | | |
118 | | // Attempts to convert an image filter to its equivalent color filter, which if possible, modifies |
119 | | // the paint to compose the image filter's color filter into the paint's color filter slot. Returns |
120 | | // true if the paint has been modified. Requires the paint to have an image filter. |
121 | 11.2k | bool SkCanvasPriv::ImageToColorFilter(SkPaint* paint) { |
122 | 11.2k | SkASSERT(SkToBool(paint) && paint->getImageFilter()); |
123 | | |
124 | | // An image filter logically runs after any mask filter and the src-over blending against the |
125 | | // layer's transparent black initial content. Moving the image filter (as a color filter) into |
126 | | // the color filter slot causes it to run before the mask filter or blending. |
127 | | // |
128 | | // Src-over blending against transparent black is a no-op, so skipping the layer and drawing the |
129 | | // output of the color filter-image filter with the original blender is valid. |
130 | | // |
131 | | // If there's also a mask filter on the paint, it will operate on an alpha-only layer that's |
132 | | // then shaded with the paint's effects. Moving the CF-IF into the paint's color filter slot |
133 | | // will mean that the CF-IF operates on the output of the original CF *before* it's combined |
134 | | // with the coverage value. Under normal circumstances the CF-IF evaluates the color after |
135 | | // coverage has been multiplied into the alpha channel. |
136 | | // |
137 | | // Some color filters may behave the same, e.g. cf(color)*coverage == cf(color*coverage), but |
138 | | // that's hard to detect so we disable the optimization when both image filters and mask filters |
139 | | // are present. |
140 | 11.2k | if (paint->getMaskFilter()) { |
141 | 1.08k | return false; |
142 | 1.08k | } |
143 | | |
144 | 10.1k | SkColorFilter* imgCFPtr; |
145 | 10.1k | if (!paint->getImageFilter()->asAColorFilter(&imgCFPtr)) { |
146 | 10.1k | return false; |
147 | 10.1k | } |
148 | 6 | sk_sp<SkColorFilter> imgCF(imgCFPtr); |
149 | | |
150 | 6 | SkColorFilter* paintCF = paint->getColorFilter(); |
151 | 6 | if (paintCF) { |
152 | | // The paint has both a colorfilter(paintCF) and an imagefilter-that-is-a-colorfilter(imgCF) |
153 | | // and we need to combine them into a single colorfilter. |
154 | 5 | imgCF = imgCF->makeComposed(sk_ref_sp(paintCF)); |
155 | 5 | } |
156 | | |
157 | 6 | paint->setColorFilter(std::move(imgCF)); |
158 | 6 | paint->setImageFilter(nullptr); |
159 | 6 | return true; |
160 | 10.1k | } |
161 | | |
162 | | AutoLayerForImageFilter::AutoLayerForImageFilter(SkCanvas* canvas, |
163 | | const SkPaint& paint, |
164 | | const SkRect* rawBounds, |
165 | | bool skipMaskFilterLayer) |
166 | | : fPaint(paint) |
167 | | , fCanvas(canvas) |
168 | 1.16M | , fTempLayersForFilters(0) { |
169 | 1.16M | SkDEBUGCODE(fSaveCount = canvas->getSaveCount();) |
170 | | |
171 | | // Depending on the original paint, this will add 0, 1, or 2 layers that apply the |
172 | | // filter effects to a temporary layer that rasterized the remaining effects. Image filters |
173 | | // are applied to the result of any mask filter, so its layer is added first in the stack. |
174 | | // |
175 | | // If present on the original paint, the image filter layer's restore paint steals the blender |
176 | | // and the image filter so that the draw's paint will never have an image filter. |
177 | 1.16M | if (fPaint.getImageFilter() && !SkCanvasPriv::ImageToColorFilter(&fPaint)) { |
178 | 22.4k | this->addImageFilterLayer(rawBounds); |
179 | 22.4k | } |
180 | | |
181 | | // If present on the original paint, the mask filter layer's restore paint steals all shading |
182 | | // effects and the draw's paint shading is updated to draw a solid opaque color (thus encoding |
183 | | // coverage into the alpha channel). The draw's paint preserves all geometric effects that have |
184 | | // to be applied before the mask filter. The layer's restore paint adds an image filter |
185 | | // representing the mask filter. |
186 | 1.16M | if (fPaint.getMaskFilter() && !skipMaskFilterLayer) { |
187 | 0 | this->addMaskFilterLayer(rawBounds); |
188 | 0 | } |
189 | | |
190 | | // When the original paint has both an image filter and a mask filter, this will create two |
191 | | // internal layers and perform two restores when finished. This actually creates one fewer |
192 | | // offscreen passes compared to directly composing the mask filter's output with an |
193 | | // SkImageFilters::Shader node and passing that into the rest of the image filter. |
194 | 1.16M | } AutoLayerForImageFilter::AutoLayerForImageFilter(SkCanvas*, SkPaint const&, SkRect const*, bool) Line | Count | Source | 168 | 583k | , fTempLayersForFilters(0) { | 169 | 583k | SkDEBUGCODE(fSaveCount = canvas->getSaveCount();) | 170 | | | 171 | | // Depending on the original paint, this will add 0, 1, or 2 layers that apply the | 172 | | // filter effects to a temporary layer that rasterized the remaining effects. Image filters | 173 | | // are applied to the result of any mask filter, so its layer is added first in the stack. | 174 | | // | 175 | | // If present on the original paint, the image filter layer's restore paint steals the blender | 176 | | // and the image filter so that the draw's paint will never have an image filter. | 177 | 583k | if (fPaint.getImageFilter() && !SkCanvasPriv::ImageToColorFilter(&fPaint)) { | 178 | 11.2k | this->addImageFilterLayer(rawBounds); | 179 | 11.2k | } | 180 | | | 181 | | // If present on the original paint, the mask filter layer's restore paint steals all shading | 182 | | // effects and the draw's paint shading is updated to draw a solid opaque color (thus encoding | 183 | | // coverage into the alpha channel). The draw's paint preserves all geometric effects that have | 184 | | // to be applied before the mask filter. The layer's restore paint adds an image filter | 185 | | // representing the mask filter. | 186 | 583k | if (fPaint.getMaskFilter() && !skipMaskFilterLayer) { | 187 | 0 | this->addMaskFilterLayer(rawBounds); | 188 | 0 | } | 189 | | | 190 | | // When the original paint has both an image filter and a mask filter, this will create two | 191 | | // internal layers and perform two restores when finished. This actually creates one fewer | 192 | | // offscreen passes compared to directly composing the mask filter's output with an | 193 | | // SkImageFilters::Shader node and passing that into the rest of the image filter. | 194 | 583k | } |
AutoLayerForImageFilter::AutoLayerForImageFilter(SkCanvas*, SkPaint const&, SkRect const*, bool) Line | Count | Source | 168 | 583k | , fTempLayersForFilters(0) { | 169 | 583k | SkDEBUGCODE(fSaveCount = canvas->getSaveCount();) | 170 | | | 171 | | // Depending on the original paint, this will add 0, 1, or 2 layers that apply the | 172 | | // filter effects to a temporary layer that rasterized the remaining effects. Image filters | 173 | | // are applied to the result of any mask filter, so its layer is added first in the stack. | 174 | | // | 175 | | // If present on the original paint, the image filter layer's restore paint steals the blender | 176 | | // and the image filter so that the draw's paint will never have an image filter. | 177 | 583k | if (fPaint.getImageFilter() && !SkCanvasPriv::ImageToColorFilter(&fPaint)) { | 178 | 11.2k | this->addImageFilterLayer(rawBounds); | 179 | 11.2k | } | 180 | | | 181 | | // If present on the original paint, the mask filter layer's restore paint steals all shading | 182 | | // effects and the draw's paint shading is updated to draw a solid opaque color (thus encoding | 183 | | // coverage into the alpha channel). The draw's paint preserves all geometric effects that have | 184 | | // to be applied before the mask filter. The layer's restore paint adds an image filter | 185 | | // representing the mask filter. | 186 | 583k | if (fPaint.getMaskFilter() && !skipMaskFilterLayer) { | 187 | 0 | this->addMaskFilterLayer(rawBounds); | 188 | 0 | } | 189 | | | 190 | | // When the original paint has both an image filter and a mask filter, this will create two | 191 | | // internal layers and perform two restores when finished. This actually creates one fewer | 192 | | // offscreen passes compared to directly composing the mask filter's output with an | 193 | | // SkImageFilters::Shader node and passing that into the rest of the image filter. | 194 | 583k | } |
|
195 | | |
196 | 583k | AutoLayerForImageFilter::~AutoLayerForImageFilter() { |
197 | 595k | for (int i = 0; i < fTempLayersForFilters; ++i) { |
198 | 11.2k | fCanvas->fSaveCount -= 1; |
199 | 11.2k | fCanvas->internalRestore(); |
200 | 11.2k | } |
201 | | // Negative save count occurs when this layer was moved. |
202 | 583k | SkASSERT(fSaveCount < 0 || fCanvas->getSaveCount() == fSaveCount); |
203 | 583k | } |
204 | | |
205 | 0 | AutoLayerForImageFilter::AutoLayerForImageFilter(AutoLayerForImageFilter&& other) { |
206 | 0 | *this = std::move(other); |
207 | 0 | } |
208 | | |
209 | 0 | AutoLayerForImageFilter& AutoLayerForImageFilter::operator=(AutoLayerForImageFilter&& other) { |
210 | 0 | fPaint = std::move(other.fPaint); |
211 | 0 | fCanvas = other.fCanvas; |
212 | 0 | fTempLayersForFilters = other.fTempLayersForFilters; |
213 | 0 | SkDEBUGCODE(fSaveCount = other.fSaveCount;) |
214 | |
|
215 | 0 | other.fTempLayersForFilters = 0; |
216 | 0 | SkDEBUGCODE(other.fSaveCount = -1;) |
217 | |
|
218 | 0 | return *this; |
219 | 0 | } Unexecuted instantiation: AutoLayerForImageFilter::operator=(AutoLayerForImageFilter&&) Unexecuted instantiation: AutoLayerForImageFilter::operator=(AutoLayerForImageFilter&&) |
220 | | |
221 | 11.2k | void AutoLayerForImageFilter::addImageFilterLayer(const SkRect* drawBounds) { |
222 | | // Shouldn't be adding a layer if there was no image filter to begin with. |
223 | 11.2k | SkASSERT(fPaint.getImageFilter()); |
224 | | |
225 | | // The restore paint for an image filter layer simply takes the image filter and blending off |
226 | | // the original paint. The blending is applied post image filter because otherwise it'd be |
227 | | // applied with the new layer's transparent dst and not be very interesting. |
228 | 11.2k | SkPaint restorePaint; |
229 | 11.2k | restorePaint.setImageFilter(fPaint.refImageFilter()); |
230 | 11.2k | restorePaint.setBlender(fPaint.refBlender()); |
231 | | |
232 | | // Remove the restorePaint fields from our "working" paint, leaving all other shading and |
233 | | // geometry effects to be rendered into the layer. If there happens to be a mask filter, this |
234 | | // paint will still trigger a second layer for that filter. |
235 | 11.2k | fPaint.setImageFilter(nullptr); |
236 | 11.2k | fPaint.setBlendMode(SkBlendMode::kSrcOver); |
237 | | |
238 | 11.2k | this->addLayer(restorePaint, drawBounds, /*coverageOnly=*/false); |
239 | 11.2k | } |
240 | | |
241 | 0 | void AutoLayerForImageFilter::addMaskFilterLayer(const SkRect* drawBounds) { |
242 | | // Shouldn't be adding a layer if there was no mask filter to begin with. |
243 | 0 | SkASSERT(fPaint.getMaskFilter()); |
244 | | |
245 | | // Image filters are evaluated after mask filters so any filter should have been converted to |
246 | | // a layer and removed from fPaint already. |
247 | 0 | SkASSERT(!fPaint.getImageFilter()); |
248 | | |
249 | | // TODO: Eventually all SkMaskFilters will implement this method so this can switch to an assert |
250 | 0 | sk_sp<SkImageFilter> maskFilterAsImageFilter = |
251 | 0 | as_MFB(fPaint.getMaskFilter())->asImageFilter(fCanvas->getTotalMatrix()); |
252 | 0 | if (!maskFilterAsImageFilter) { |
253 | | // This is a legacy mask filter that can be handled by raster and Ganesh directly, but will |
254 | | // be ignored by Graphite. Return now, leaving the paint with the mask filter so that the |
255 | | // underlying SkDevice can handle it if it will. |
256 | 0 | return; |
257 | 0 | } |
258 | | |
259 | | // The restore paint for the coverage layer takes over all shading effects that had been on the |
260 | | // original paint, which will be applied to the alpha-only output image from the mask filter |
261 | | // converted to an image filter. |
262 | 0 | SkPaint restorePaint; |
263 | 0 | restorePaint.setColor4f(fPaint.getColor4f()); |
264 | 0 | restorePaint.setShader(fPaint.refShader()); |
265 | 0 | restorePaint.setColorFilter(fPaint.refColorFilter()); |
266 | 0 | restorePaint.setBlender(fPaint.refBlender()); |
267 | 0 | restorePaint.setDither(fPaint.isDither()); |
268 | 0 | restorePaint.setImageFilter(maskFilterAsImageFilter); |
269 | | |
270 | | // Remove all shading effects from the "working" paint so that the layer's alpha channel |
271 | | // will correspond to the coverage. This leaves the original style and AA settings that |
272 | | // contribute to coverage (including any path effect). |
273 | 0 | fPaint.setColor4f(SkColors::kWhite); |
274 | 0 | fPaint.setShader(nullptr); |
275 | 0 | fPaint.setColorFilter(nullptr); |
276 | 0 | fPaint.setMaskFilter(nullptr); |
277 | 0 | fPaint.setDither(false); |
278 | 0 | fPaint.setBlendMode(SkBlendMode::kSrcOver); |
279 | |
|
280 | 0 | this->addLayer(restorePaint, drawBounds, /*coverageOnly=*/true); |
281 | 0 | } Unexecuted instantiation: AutoLayerForImageFilter::addMaskFilterLayer(SkRect const*) Unexecuted instantiation: AutoLayerForImageFilter::addMaskFilterLayer(SkRect const*) |
282 | | |
283 | | void AutoLayerForImageFilter::addLayer(const SkPaint& restorePaint, |
284 | | const SkRect* drawBounds, |
285 | 11.2k | bool coverageOnly) { |
286 | 11.2k | SkRect storage; |
287 | 11.2k | const SkRect* contentBounds = nullptr; |
288 | 11.2k | if (drawBounds && fPaint.canComputeFastBounds()) { |
289 | | // The content bounds will include all paint outsets except for those that have been |
290 | | // extracted into 'restorePaint' or a previously added layer. |
291 | 10.7k | contentBounds = &fPaint.computeFastBounds(*drawBounds, &storage); |
292 | 10.7k | } |
293 | | |
294 | 11.2k | fCanvas->fSaveCount += 1; |
295 | 11.2k | fCanvas->internalSaveLayer(SkCanvas::SaveLayerRec(contentBounds, &restorePaint), |
296 | 11.2k | SkCanvas::kFullLayer_SaveLayerStrategy, |
297 | 11.2k | coverageOnly); |
298 | 11.2k | fTempLayersForFilters += 1; |
299 | 11.2k | } |