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