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