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