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