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