xref: /aosp_15_r20/external/skia/src/core/SkCanvasPriv.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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 
SkAutoCanvasMatrixPaint(SkCanvas * canvas,const SkMatrix * matrix,const SkPaint * paint,const SkRect & bounds)31 SkAutoCanvasMatrixPaint::SkAutoCanvasMatrixPaint(SkCanvas* canvas, const SkMatrix* matrix,
32                                                  const SkPaint* paint, const SkRect& bounds)
33         : fCanvas(canvas)
34         , fSaveCount(canvas->getSaveCount()) {
35     if (paint) {
36         SkRect newBounds = bounds;
37         if (matrix) {
38             matrix->mapRect(&newBounds);
39         }
40         canvas->saveLayer(&newBounds, paint);
41     } else if (matrix) {
42         canvas->save();
43     }
44 
45     if (matrix) {
46         canvas->concat(*matrix);
47     }
48 }
49 
~SkAutoCanvasMatrixPaint()50 SkAutoCanvasMatrixPaint::~SkAutoCanvasMatrixPaint() {
51     fCanvas->restoreToCount(fSaveCount);
52 }
53 
54 ///////////////////////////////////////////////////////////////////////////////////////////////////
55 
ReadLattice(SkReadBuffer & buffer,SkCanvas::Lattice * lattice)56 bool SkCanvasPriv::ReadLattice(SkReadBuffer& buffer, SkCanvas::Lattice* lattice) {
57     lattice->fXCount = buffer.readInt();
58     lattice->fXDivs = buffer.skipT<int32_t>(lattice->fXCount);
59     lattice->fYCount = buffer.readInt();
60     lattice->fYDivs = buffer.skipT<int32_t>(lattice->fYCount);
61     int flagCount = buffer.readInt();
62     lattice->fRectTypes = nullptr;
63     lattice->fColors = nullptr;
64     if (flagCount) {
65         lattice->fRectTypes = buffer.skipT<SkCanvas::Lattice::RectType>(flagCount);
66         lattice->fColors = buffer.skipT<SkColor>(flagCount);
67     }
68     lattice->fBounds = buffer.skipT<SkIRect>();
69     return buffer.isValid();
70 }
71 
WriteLattice(void * buffer,const SkCanvas::Lattice & lattice)72 size_t SkCanvasPriv::WriteLattice(void* buffer, const SkCanvas::Lattice& lattice) {
73     int flagCount = lattice.fRectTypes ? (lattice.fXCount + 1) * (lattice.fYCount + 1) : 0;
74 
75     const size_t size = (1 + lattice.fXCount + 1 + lattice.fYCount + 1) * sizeof(int32_t) +
76                         SkAlign4(flagCount * sizeof(SkCanvas::Lattice::RectType)) +
77                         SkAlign4(flagCount * sizeof(SkColor)) +
78                         sizeof(SkIRect);
79 
80     if (buffer) {
81         SkWriter32 writer(buffer, size);
82         writer.write32(lattice.fXCount);
83         writer.write(lattice.fXDivs, lattice.fXCount * sizeof(uint32_t));
84         writer.write32(lattice.fYCount);
85         writer.write(lattice.fYDivs, lattice.fYCount * sizeof(uint32_t));
86         writer.write32(flagCount);
87         writer.writePad(lattice.fRectTypes, flagCount * sizeof(uint8_t));
88         writer.write(lattice.fColors, flagCount * sizeof(SkColor));
89         SkASSERT(lattice.fBounds);
90         writer.write(lattice.fBounds, sizeof(SkIRect));
91         SkASSERT(writer.bytesWritten() == size);
92     }
93     return size;
94 }
95 
WriteLattice(SkWriteBuffer & buffer,const SkCanvas::Lattice & lattice)96 void SkCanvasPriv::WriteLattice(SkWriteBuffer& buffer, const SkCanvas::Lattice& lattice) {
97     const size_t size = WriteLattice(nullptr, lattice);
98     SkAutoSMalloc<1024> storage(size);
99     WriteLattice(storage.get(), lattice);
100     buffer.writePad32(storage.get(), size);
101 }
102 
GetDstClipAndMatrixCounts(const SkCanvas::ImageSetEntry set[],int count,int * totalDstClipCount,int * totalMatrixCount)103 void SkCanvasPriv::GetDstClipAndMatrixCounts(const SkCanvas::ImageSetEntry set[], int count,
104                                              int* totalDstClipCount, int* totalMatrixCount) {
105     int dstClipCount = 0;
106     int maxMatrixIndex = -1;
107     for (int i = 0; i < count; ++i) {
108         dstClipCount += 4 * set[i].fHasClip;
109         if (set[i].fMatrixIndex > maxMatrixIndex) {
110             maxMatrixIndex = set[i].fMatrixIndex;
111         }
112     }
113 
114     *totalDstClipCount = dstClipCount;
115     *totalMatrixCount = maxMatrixIndex + 1;
116 }
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.
ImageToColorFilter(SkPaint * paint)121 bool SkCanvasPriv::ImageToColorFilter(SkPaint* paint) {
122     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     if (paint->getMaskFilter()) {
141         return false;
142     }
143 
144     SkColorFilter* imgCFPtr;
145     if (!paint->getImageFilter()->asAColorFilter(&imgCFPtr)) {
146         return false;
147     }
148     sk_sp<SkColorFilter> imgCF(imgCFPtr);
149 
150     SkColorFilter* paintCF = paint->getColorFilter();
151     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         imgCF = imgCF->makeComposed(sk_ref_sp(paintCF));
155     }
156 
157     paint->setColorFilter(std::move(imgCF));
158     paint->setImageFilter(nullptr);
159     return true;
160 }
161 
AutoLayerForImageFilter(SkCanvas * canvas,const SkPaint & paint,const SkRect * rawBounds,bool skipMaskFilterLayer)162 AutoLayerForImageFilter::AutoLayerForImageFilter(SkCanvas* canvas,
163                                                  const SkPaint& paint,
164                                                  const SkRect* rawBounds,
165                                                  bool skipMaskFilterLayer)
166             : fPaint(paint)
167             , fCanvas(canvas)
168             , fTempLayersForFilters(0) {
169     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     if (fPaint.getImageFilter() && !SkCanvasPriv::ImageToColorFilter(&fPaint)) {
178         this->addImageFilterLayer(rawBounds);
179     }
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     if (fPaint.getMaskFilter() && !skipMaskFilterLayer) {
187         this->addMaskFilterLayer(rawBounds);
188     }
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 }
195 
~AutoLayerForImageFilter()196 AutoLayerForImageFilter::~AutoLayerForImageFilter() {
197     for (int i = 0; i < fTempLayersForFilters; ++i) {
198         fCanvas->fSaveCount -= 1;
199         fCanvas->internalRestore();
200     }
201     // Negative save count occurs when this layer was moved.
202     SkASSERT(fSaveCount < 0 || fCanvas->getSaveCount() == fSaveCount);
203 }
204 
AutoLayerForImageFilter(AutoLayerForImageFilter && other)205 AutoLayerForImageFilter::AutoLayerForImageFilter(AutoLayerForImageFilter&& other) {
206     *this = std::move(other);
207 }
208 
operator =(AutoLayerForImageFilter && other)209 AutoLayerForImageFilter& AutoLayerForImageFilter::operator=(AutoLayerForImageFilter&& other) {
210     fPaint = std::move(other.fPaint);
211     fCanvas = other.fCanvas;
212     fTempLayersForFilters = other.fTempLayersForFilters;
213     SkDEBUGCODE(fSaveCount = other.fSaveCount;)
214 
215     other.fTempLayersForFilters = 0;
216     SkDEBUGCODE(other.fSaveCount = -1;)
217 
218     return *this;
219 }
220 
addImageFilterLayer(const SkRect * drawBounds)221 void AutoLayerForImageFilter::addImageFilterLayer(const SkRect* drawBounds) {
222     // Shouldn't be adding a layer if there was no image filter to begin with.
223     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     SkPaint restorePaint;
229     restorePaint.setImageFilter(fPaint.refImageFilter());
230     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     fPaint.setImageFilter(nullptr);
236     fPaint.setBlendMode(SkBlendMode::kSrcOver);
237 
238     this->addLayer(restorePaint, drawBounds, /*coverageOnly=*/false);
239 }
240 
addMaskFilterLayer(const SkRect * drawBounds)241 void AutoLayerForImageFilter::addMaskFilterLayer(const SkRect* drawBounds) {
242     // Shouldn't be adding a layer if there was no mask filter to begin with.
243     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     SkASSERT(!fPaint.getImageFilter());
248 
249     // TODO: Eventually all SkMaskFilters will implement this method so this can switch to an assert
250     sk_sp<SkImageFilter> maskFilterAsImageFilter =
251             as_MFB(fPaint.getMaskFilter())->asImageFilter(fCanvas->getTotalMatrix());
252     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         return;
257     }
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     SkPaint restorePaint;
263     restorePaint.setColor4f(fPaint.getColor4f());
264     restorePaint.setShader(fPaint.refShader());
265     restorePaint.setColorFilter(fPaint.refColorFilter());
266     restorePaint.setBlender(fPaint.refBlender());
267     restorePaint.setDither(fPaint.isDither());
268     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     fPaint.setColor4f(SkColors::kWhite);
274     fPaint.setShader(nullptr);
275     fPaint.setColorFilter(nullptr);
276     fPaint.setMaskFilter(nullptr);
277     fPaint.setDither(false);
278     fPaint.setBlendMode(SkBlendMode::kSrcOver);
279 
280     this->addLayer(restorePaint, drawBounds, /*coverageOnly=*/true);
281 }
282 
addLayer(const SkPaint & restorePaint,const SkRect * drawBounds,bool coverageOnly)283 void AutoLayerForImageFilter::addLayer(const SkPaint& restorePaint,
284                                        const SkRect* drawBounds,
285                                        bool coverageOnly) {
286     SkRect storage;
287     const SkRect* contentBounds = nullptr;
288     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         contentBounds = &fPaint.computeFastBounds(*drawBounds, &storage);
292     }
293 
294     fCanvas->fSaveCount += 1;
295     fCanvas->internalSaveLayer(SkCanvas::SaveLayerRec(contentBounds, &restorePaint),
296                                SkCanvas::kFullLayer_SaveLayerStrategy,
297                                coverageOnly);
298     fTempLayersForFilters += 1;
299 }
300