/src/skia/src/core/SkBlitter_Sprite.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
20 | | SkSpriteBlitter::SkSpriteBlitter(const SkPixmap& source) |
21 | 123k | : fSource(source) {} |
22 | | |
23 | 107k | bool SkSpriteBlitter::setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) { |
24 | 107k | fDst = dst; |
25 | 107k | fLeft = left; |
26 | 107k | fTop = top; |
27 | 107k | fPaint = &paint; |
28 | 107k | return true; |
29 | 107k | } |
30 | | |
31 | 0 | void SkSpriteBlitter::blitH(int x, int y, int width) { |
32 | 0 | SkDEBUGFAIL("how did we get here?"); |
33 | | |
34 | | // Fallback to blitRect. |
35 | 0 | this->blitRect(x, y, width, 1); |
36 | 0 | } Unexecuted instantiation: SkSpriteBlitter::blitH(int, int, int) Unexecuted instantiation: SkSpriteBlitter::blitH(int, int, int) |
37 | | |
38 | 0 | void SkSpriteBlitter::blitAntiH(int x, int y, const SkAlpha antialias[], const int16_t runs[]) { |
39 | 0 | SkDEBUGFAIL("how did we get here?"); |
40 | | |
41 | | // No fallback strategy. |
42 | 0 | } Unexecuted instantiation: SkSpriteBlitter::blitAntiH(int, int, unsigned char const*, short const*) Unexecuted instantiation: SkSpriteBlitter::blitAntiH(int, int, unsigned char const*, short const*) |
43 | | |
44 | 0 | void SkSpriteBlitter::blitV(int x, int y, int height, SkAlpha alpha) { |
45 | 0 | SkDEBUGFAIL("how did we get here?"); |
46 | | |
47 | | // Fall back to superclass if the code gets here in release mode. |
48 | 0 | INHERITED::blitV(x, y, height, alpha); |
49 | 0 | } Unexecuted instantiation: SkSpriteBlitter::blitV(int, int, int, unsigned char) Unexecuted instantiation: SkSpriteBlitter::blitV(int, int, int, unsigned char) |
50 | | |
51 | 0 | void SkSpriteBlitter::blitMask(const SkMask& mask, const SkIRect& clip) { |
52 | 0 | SkDEBUGFAIL("how did we get here?"); |
53 | | |
54 | | // Fall back to superclass if the code gets here in release mode. |
55 | 0 | INHERITED::blitMask(mask, clip); |
56 | 0 | } Unexecuted instantiation: SkSpriteBlitter::blitMask(SkMask const&, SkIRect const&) Unexecuted instantiation: SkSpriteBlitter::blitMask(SkMask const&, SkIRect const&) |
57 | | |
58 | | /////////////////////////////////////////////////////////////////////////////// |
59 | | |
60 | | class SkSpriteBlitter_Memcpy final : public SkSpriteBlitter { |
61 | | public: |
62 | 123k | 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 | 123k | SkASSERT(0 == SkColorSpaceXformSteps(src,dst).flags.mask()); |
65 | | |
66 | 123k | if (dst.colorType() != src.colorType()) { |
67 | 1.21k | return false; |
68 | 1.21k | } |
69 | 122k | if (paint.getMaskFilter() || paint.getColorFilter() || paint.getImageFilter()) { |
70 | 14.6k | return false; |
71 | 14.6k | } |
72 | 107k | if (0xFF != paint.getAlpha()) { |
73 | 4.39k | return false; |
74 | 4.39k | } |
75 | 103k | const auto mode = paint.asBlendMode(); |
76 | 103k | return mode == SkBlendMode::kSrc || (mode == SkBlendMode::kSrcOver && src.isOpaque()); |
77 | 103k | } |
78 | | |
79 | | SkSpriteBlitter_Memcpy(const SkPixmap& src) |
80 | 16.3k | : INHERITED(src) {} |
81 | | |
82 | 16.3k | void blitRect(int x, int y, int width, int height) override { |
83 | 16.3k | SkASSERT(fDst.colorType() == fSource.colorType()); |
84 | 16.3k | SkASSERT(width > 0 && height > 0); |
85 | | |
86 | 16.3k | char* dst = (char*)fDst.writable_addr(x, y); |
87 | 16.3k | const char* src = (const char*)fSource.addr(x - fLeft, y - fTop); |
88 | 16.3k | const size_t dstRB = fDst.rowBytes(); |
89 | 16.3k | const size_t srcRB = fSource.rowBytes(); |
90 | 16.3k | const size_t bytesToCopy = width << fSource.shiftPerPixel(); |
91 | | |
92 | 1.81M | while (height --> 0) { |
93 | 1.79M | memcpy(dst, src, bytesToCopy); |
94 | 1.79M | dst += dstRB; |
95 | 1.79M | src += srcRB; |
96 | 1.79M | } |
97 | 16.3k | } |
98 | | |
99 | | private: |
100 | | using INHERITED = SkSpriteBlitter; |
101 | | }; |
102 | | |
103 | | class SkRasterPipelineSpriteBlitter : public SkSpriteBlitter { |
104 | | public: |
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 | 16.0k | {} |
113 | | |
114 | 16.0k | bool setup(const SkPixmap& dst, int left, int top, const SkPaint& paint) override { |
115 | 16.0k | fDst = dst; |
116 | 16.0k | fLeft = left; |
117 | 16.0k | fTop = top; |
118 | 16.0k | fPaintColor = paint.getColor4f(); |
119 | | |
120 | 16.0k | SkRasterPipeline p(fAlloc); |
121 | 16.0k | p.append_load(fSource.colorType(), &fSrcPtr); |
122 | | |
123 | 16.0k | if (fSource.colorType() == kAlpha_8_SkColorType) { |
124 | | // The color for A8 images comes from the (sRGB) paint color. |
125 | 0 | p.append_set_rgb(fAlloc, fPaintColor); |
126 | 0 | p.append(SkRasterPipeline::premul); |
127 | 0 | } |
128 | 16.0k | if (auto dstCS = fDst.colorSpace()) { |
129 | 167 | auto srcCS = fSource.colorSpace(); |
130 | 167 | 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 | 29 | srcCS = sk_srgb_singleton(); |
134 | 29 | } |
135 | 0 | auto srcAT = fSource.isOpaque() ? kOpaque_SkAlphaType |
136 | 167 | : kPremul_SkAlphaType; |
137 | 167 | fAlloc->make<SkColorSpaceXformSteps>(srcCS, srcAT, |
138 | 167 | dstCS, kPremul_SkAlphaType) |
139 | 167 | ->apply(&p); |
140 | 167 | } |
141 | 16.0k | if (fPaintColor.fA != 1.0f) { |
142 | 633 | p.append(SkRasterPipeline::scale_1_float, &fPaintColor.fA); |
143 | 633 | } |
144 | | |
145 | 16.0k | bool is_opaque = fSource.isOpaque() && fPaintColor.fA == 1.0f; |
146 | 16.0k | fBlitter = SkCreateRasterPipelineBlitter(fDst, paint, p, is_opaque, fAlloc, fClipShader); |
147 | 16.0k | return fBlitter != nullptr; |
148 | 16.0k | } |
149 | | |
150 | 16.0k | void blitRect(int x, int y, int width, int height) override { |
151 | 16.0k | 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 | 16.0k | size_t bpp = fSource.info().bytesPerPixel(); |
158 | 16.0k | fSrcPtr.pixels = (char*)fSource.addr(-fLeft+x, -fTop+y) - bpp * x |
159 | 16.0k | - bpp * y * fSrcPtr.stride; |
160 | | |
161 | 16.0k | fBlitter->blitRect(x,y,width,height); |
162 | 16.0k | } |
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 |
176 | | SkBlitter* SkBlitter::ChooseSprite(const SkPixmap& dst, const SkPaint& paint, |
177 | | const SkPixmap& source, int left, int top, |
178 | 123k | 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 | 123k | SkASSERT(alloc != nullptr); |
189 | | |
190 | 123k | if (gUseSkVMBlitter) { |
191 | 0 | return SkVMBlitter::Make(dst, paint, source,left,top, alloc, std::move(clipShader)); |
192 | 0 | } |
193 | | |
194 | | // TODO: in principle SkRasterPipelineSpriteBlitter could be made to handle this. |
195 | 123k | if (source.alphaType() == kUnpremul_SkAlphaType) { |
196 | 0 | return nullptr; |
197 | 0 | } |
198 | | |
199 | 123k | SkSpriteBlitter* blitter = nullptr; |
200 | | |
201 | 123k | if (0 == SkColorSpaceXformSteps(source,dst).flags.mask() && !clipShader) { |
202 | 123k | if (!blitter && SkSpriteBlitter_Memcpy::Supports(dst, source, paint)) { |
203 | 16.3k | blitter = alloc->make<SkSpriteBlitter_Memcpy>(source); |
204 | 16.3k | } |
205 | 123k | if (!blitter) { |
206 | 107k | switch (dst.colorType()) { |
207 | 107k | case kN32_SkColorType: |
208 | 107k | blitter = SkSpriteBlitter::ChooseL32(source, paint, alloc); |
209 | 107k | break; |
210 | 0 | case kRGB_565_SkColorType: |
211 | 0 | blitter = SkSpriteBlitter::ChooseL565(source, paint, alloc); |
212 | 0 | break; |
213 | 0 | case kAlpha_8_SkColorType: |
214 | 0 | blitter = SkSpriteBlitter::ChooseLA8(source, paint, alloc); |
215 | 0 | break; |
216 | 166 | default: |
217 | 166 | break; |
218 | 123k | } |
219 | 123k | } |
220 | 123k | } |
221 | 123k | if (!blitter && !paint.getMaskFilter()) { |
222 | 16.0k | blitter = alloc->make<SkRasterPipelineSpriteBlitter>(source, alloc, clipShader); |
223 | 16.0k | } |
224 | | |
225 | 123k | if (blitter && blitter->setup(dst, left,top, paint)) { |
226 | 123k | return blitter; |
227 | 123k | } |
228 | | |
229 | 12 | return SkVMBlitter::Make(dst, paint, source,left,top, alloc, std::move(clipShader)); |
230 | 12 | } |