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 "SkArenaAlloc.h"
9 #include "SkColorSpace.h"
10 #include "SkColorSpacePriv.h"
11 #include "SkColorSpaceXformSteps.h"
12 #include "SkCoreBlitters.h"
13 #include "SkOpts.h"
14 #include "SkRasterPipeline.h"
15 #include "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 if (dst.colorType() != src.colorType()) {
60 return false;
61 }
62 if (!SkColorSpace::Equals(dst.colorSpace(), src.colorSpace())) {
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 (!blitter && SkSpriteBlitter_Memcpy::Supports(dst, source, paint)) {
188 blitter = allocator->make<SkSpriteBlitter_Memcpy>(source);
189 }
190 if (!blitter && !dst.colorSpace()) {
191 switch (dst.colorType()) {
192 case kN32_SkColorType:
193 blitter = SkSpriteBlitter::ChooseL32(source, paint, allocator);
194 break;
195 case kRGB_565_SkColorType:
196 blitter = SkSpriteBlitter::ChooseL565(source, paint, allocator);
197 break;
198 case kAlpha_8_SkColorType:
199 blitter = SkSpriteBlitter::ChooseLA8(source, paint, allocator);
200 break;
201 default:
202 break;
203 }
204 }
205 if (!blitter && !paint.getMaskFilter()) {
206 blitter = allocator->make<SkRasterPipelineSpriteBlitter>(source, allocator);
207 }
208
209 if (blitter) {
210 blitter->setup(dst, left, top, paint);
211 }
212 return blitter;
213 }
214