xref: /aosp_15_r20/external/skia/src/core/SkBlitter_Sprite.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2006 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/SkAlphaType.h"
9 #include "include/core/SkBlendMode.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorType.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkPaint.h"
14 #include "include/core/SkPixmap.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkShader.h"
17 #include "include/private/base/SkAssert.h"
18 #include "src/base/SkArenaAlloc.h"
19 #include "src/core/SkBlitter.h"
20 #include "src/core/SkColorSpacePriv.h"
21 #include "src/core/SkColorSpaceXformSteps.h"
22 #include "src/core/SkCoreBlitters.h"
23 #include "src/core/SkImageInfoPriv.h"
24 #include "src/core/SkRasterPipeline.h"
25 #include "src/core/SkRasterPipelineOpContexts.h"
26 #include "src/core/SkRasterPipelineOpList.h"
27 #include "src/core/SkSpriteBlitter.h"
28 
29 #include <cstdint>
30 #include <cstring>
31 #include <optional>
32 #include <utility>
33 
34 struct SkIRect;
35 struct SkMask;
36 
37 extern bool gSkForceRasterPipelineBlitter;
38 
SkSpriteBlitter(const SkPixmap & source)39 SkSpriteBlitter::SkSpriteBlitter(const SkPixmap& source)
40     : fSource(source) {}
41 
setup(const SkPixmap & dst,int left,int top,const SkPaint & paint)42 bool SkSpriteBlitter::setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) {
43     fDst = dst;
44     fLeft = left;
45     fTop = top;
46     fPaint = &paint;
47     return true;
48 }
49 
blitH(int x,int y,int width)50 void SkSpriteBlitter::blitH(int x, int y, int width) {
51     SkDEBUGFAIL("how did we get here?");
52 
53     // Fallback to blitRect.
54     this->blitRect(x, y, width, 1);
55 }
56 
blitAntiH(int x,int y,const SkAlpha antialias[],const int16_t runs[])57 void SkSpriteBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) {
58     SkDEBUGFAIL("how did we get here?");
59 
60     // No fallback strategy.
61 }
62 
blitV(int x,int y,int height,SkAlpha alpha)63 void SkSpriteBlitter::blitV(int x, int y, int height, SkAlpha alpha) {
64     SkDEBUGFAIL("how did we get here?");
65 
66     // Fall back to superclass if the code gets here in release mode.
67     INHERITED::blitV(x, y, height, alpha);
68 }
69 
blitMask(const SkMask & mask,const SkIRect & clip)70 void SkSpriteBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
71     SkDEBUGFAIL("how did we get here?");
72 
73     // Fall back to superclass if the code gets here in release mode.
74     INHERITED::blitMask(mask, clip);
75 }
76 
77 ///////////////////////////////////////////////////////////////////////////////
78 
79 class SkSpriteBlitter_Memcpy final : public SkSpriteBlitter {
80 public:
Supports(const SkPixmap & dst,const SkPixmap & src,const SkPaint & paint)81     static bool Supports(const SkPixmap& dst, const SkPixmap& src, const SkPaint& paint) {
82         // the caller has already inspected the colorspace on src and dst
83         SkASSERT(0 == SkColorSpaceXformSteps(src,dst).flags.mask());
84 
85         if (dst.colorType() != src.colorType()) {
86             return false;
87         }
88         if (paint.getMaskFilter() || paint.getColorFilter() || paint.getImageFilter()) {
89             return false;
90         }
91         if (0xFF != paint.getAlpha()) {
92             return false;
93         }
94         const auto mode = paint.asBlendMode();
95         return mode == SkBlendMode::kSrc || (mode == SkBlendMode::kSrcOver && src.isOpaque());
96     }
97 
SkSpriteBlitter_Memcpy(const SkPixmap & src)98     SkSpriteBlitter_Memcpy(const SkPixmap& src)
99         : INHERITED(src) {}
100 
blitRect(int x,int y,int width,int height)101     void blitRect(int x, int y, int width, int height) override {
102         SkASSERT(fDst.colorType() == fSource.colorType());
103         SkASSERT(width > 0 && height > 0);
104 
105         char* dst = (char*)fDst.writable_addr(x, y);
106         const char* src = (const char*)fSource.addr(x - fLeft, y - fTop);
107         const size_t dstRB = fDst.rowBytes();
108         const size_t srcRB = fSource.rowBytes();
109         const size_t bytesToCopy = width << fSource.shiftPerPixel();
110 
111         while (height --> 0) {
112             memcpy(dst, src, bytesToCopy);
113             dst += dstRB;
114             src += srcRB;
115         }
116     }
117 
118 private:
119     using INHERITED = SkSpriteBlitter;
120 };
121 
122 class SkRasterPipelineSpriteBlitter : public SkSpriteBlitter {
123 public:
SkRasterPipelineSpriteBlitter(const SkPixmap & src,SkArenaAlloc * alloc,sk_sp<SkShader> clipShader)124     SkRasterPipelineSpriteBlitter(const SkPixmap& src, SkArenaAlloc* alloc,
125                                   sk_sp<SkShader> clipShader)
126         : INHERITED(src)
127         , fAlloc(alloc)
128         , fBlitter(nullptr)
129         , fSrcPtr{nullptr, 0}
130         , fClipShader(std::move(clipShader))
131     {}
132 
setup(const SkPixmap & dst,int left,int top,const SkPaint & paint)133     bool setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) override {
134         fDst  = dst;
135         fLeft = left;
136         fTop  = top;
137         fPaintColor = paint.getColor4f();
138 
139         SkRasterPipeline p(fAlloc);
140         p.appendLoad(fSource.colorType(), &fSrcPtr);
141 
142         if (SkColorTypeIsAlphaOnly(fSource.colorType())) {
143             // The color for A8 images comes from the (sRGB) paint color.
144             p.appendSetRGB(fAlloc, fPaintColor);
145             p.append(SkRasterPipelineOp::premul);
146         }
147         if (auto dstCS = fDst.colorSpace()) {
148             auto srcCS = fSource.colorSpace();
149             if (!srcCS || SkColorTypeIsAlphaOnly(fSource.colorType())) {
150                 // We treat untagged images as sRGB.
151                 // Alpha-only images get their r,g,b from the paint color, so they're also sRGB.
152                 srcCS = sk_srgb_singleton();
153             }
154             auto srcAT = fSource.isOpaque() ? kOpaque_SkAlphaType
155                                             : kPremul_SkAlphaType;
156             fAlloc->make<SkColorSpaceXformSteps>(srcCS, srcAT,
157                                                  dstCS, kPremul_SkAlphaType)
158                 ->apply(&p);
159         }
160         if (fPaintColor.fA != 1.0f) {
161             p.append(SkRasterPipelineOp::scale_1_float, &fPaintColor.fA);
162         }
163 
164         bool is_opaque = fSource.isOpaque() && fPaintColor.fA == 1.0f;
165         fBlitter = SkCreateRasterPipelineBlitter(fDst, paint, p, is_opaque, fAlloc, fClipShader);
166         return fBlitter != nullptr;
167     }
168 
blitRect(int x,int y,int width,int height)169     void blitRect(int x, int y, int width, int height) override {
170         fSrcPtr.stride = fSource.rowBytesAsPixels();
171 
172         // We really want fSrcPtr.pixels = fSource.addr(-fLeft, -fTop) here, but that asserts.
173         // Instead we ask for addr(-fLeft+x, -fTop+y), then back up (x,y) manually.
174         // Representing bpp as a size_t keeps all this math in size_t instead of int,
175         // which could wrap around with large enough fSrcPtr.stride and y.
176         size_t bpp = fSource.info().bytesPerPixel();
177         fSrcPtr.pixels = (char*)fSource.writable_addr(-fLeft+x, -fTop+y) - bpp * x
178                                                                          - bpp * y * fSrcPtr.stride;
179 
180         fBlitter->blitRect(x,y,width,height);
181     }
182 
183 private:
184     SkArenaAlloc*              fAlloc;
185     SkBlitter*                 fBlitter;
186     SkRasterPipeline_MemoryCtx fSrcPtr;
187     SkColor4f                  fPaintColor;
188     sk_sp<SkShader>            fClipShader;
189 
190     using INHERITED = SkSpriteBlitter;
191 };
192 
193 // returning null means the caller will call SkBlitter::Choose() and
194 // have wrapped the source bitmap inside a shader
ChooseSprite(const SkPixmap & dst,const SkPaint & paint,const SkPixmap & source,int left,int top,SkArenaAlloc * alloc,sk_sp<SkShader> clipShader)195 SkBlitter* SkBlitter::ChooseSprite(const SkPixmap& dst, const SkPaint& paint,
196                                    const SkPixmap& source, int left, int top,
197                                    SkArenaAlloc* alloc, sk_sp<SkShader> clipShader) {
198     /*  We currently ignore antialiasing and filtertype, meaning we will take our
199         special blitters regardless of these settings. Ignoring filtertype seems fine
200         since by definition there is no scale in the matrix. Ignoring antialiasing is
201         a bit of a hack, since we "could" pass in the fractional left/top for the bitmap,
202         and respect that by blending the edges of the bitmap against the device. To support
203         this we could either add more special blitters here, or detect antialiasing in the
204         paint and return null if it is set, forcing the client to take the slow shader case
205         (which does respect soft edges).
206     */
207     SkASSERT(alloc != nullptr);
208 
209     // TODO: in principle SkRasterPipelineSpriteBlitter could be made to handle this.
210     if (source.alphaType() == kUnpremul_SkAlphaType) {
211         return nullptr;
212     }
213 
214     SkSpriteBlitter* blitter = nullptr;
215 
216     if (gSkForceRasterPipelineBlitter) {
217         // Do not use any of these optimized memory blitters
218     } else if (0 == SkColorSpaceXformSteps(source,dst).flags.mask() && !clipShader) {
219         if (!blitter && SkSpriteBlitter_Memcpy::Supports(dst, source, paint)) {
220             blitter = alloc->make<SkSpriteBlitter_Memcpy>(source);
221         }
222         if (!blitter) {
223             switch (dst.colorType()) {
224                 case kN32_SkColorType:
225                     blitter = SkSpriteBlitter::ChooseL32(source, paint, alloc);
226                     break;
227                 default:
228                     break;
229             }
230         }
231     }
232     if (!blitter && !paint.getMaskFilter()) {
233         blitter = alloc->make<SkRasterPipelineSpriteBlitter>(source, alloc, clipShader);
234     }
235 
236     if (blitter && blitter->setup(dst, left,top, paint)) {
237         return blitter;
238     }
239 
240     return nullptr;
241 }
242