1 /*
2 * Copyright 2016 Google Inc.
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 "SkBlitter.h"
10 #include "SkBlendModePriv.h"
11 #include "SkColor.h"
12 #include "SkColorFilter.h"
13 #include "SkOpts.h"
14 #include "SkPM4f.h"
15 #include "SkPM4fPriv.h"
16 #include "SkRasterPipeline.h"
17 #include "SkShader.h"
18 #include "SkUtils.h"
19
20
21 class SkRasterPipelineBlitter : public SkBlitter {
22 public:
23 static SkBlitter* Create(const SkPixmap&, const SkPaint&, const SkMatrix& ctm,
24 SkArenaAlloc*);
25
SkRasterPipelineBlitter(SkPixmap dst,SkBlendMode blend,SkPM4f paintColor)26 SkRasterPipelineBlitter(SkPixmap dst, SkBlendMode blend, SkPM4f paintColor)
27 : fDst(dst)
28 , fBlend(blend)
29 , fPaintColor(paintColor)
30 {}
31
32 void blitH (int x, int y, int w) override;
33 void blitAntiH(int x, int y, const SkAlpha[], const int16_t[]) override;
34 void blitMask (const SkMask&, const SkIRect& clip) override;
35
36 // TODO: The default implementations of the other blits look fine,
37 // but some of them like blitV could probably benefit from custom
38 // blits using something like a SkRasterPipeline::runFew() method.
39
40 private:
41 void append_load_d(SkRasterPipeline*) const;
42 void append_blend (SkRasterPipeline*) const;
43 void maybe_clamp (SkRasterPipeline*) const;
44 void append_store (SkRasterPipeline*) const;
45
46 SkPixmap fDst;
47 SkBlendMode fBlend;
48 SkPM4f fPaintColor;
49 SkRasterPipeline fShader;
50
51 // We may be able to specialize blitH() into a memset.
52 bool fCanMemsetInBlitH = false;
53 uint64_t fMemsetColor = 0; // Big enough for largest dst format, F16.
54
55 // Built lazily on first use.
56 SkRasterPipeline fBlitH,
57 fBlitAntiH,
58 fBlitMaskA8,
59 fBlitMaskLCD16;
60
61 // These values are pointed to by the blit pipelines above,
62 // which allows us to adjust them from call to call.
63 void* fDstPtr = nullptr;
64 const void* fMaskPtr = nullptr;
65 float fCurrentCoverage = 0.0f;
66 int fCurrentY = 0;
67
68 typedef SkBlitter INHERITED;
69 };
70
SkCreateRasterPipelineBlitter(const SkPixmap & dst,const SkPaint & paint,const SkMatrix & ctm,SkArenaAlloc * alloc)71 SkBlitter* SkCreateRasterPipelineBlitter(const SkPixmap& dst,
72 const SkPaint& paint,
73 const SkMatrix& ctm,
74 SkArenaAlloc* alloc) {
75 return SkRasterPipelineBlitter::Create(dst, paint, ctm, alloc);
76 }
77
supported(const SkImageInfo & info)78 static bool supported(const SkImageInfo& info) {
79 switch (info.colorType()) {
80 case kAlpha_8_SkColorType: return true;
81 case kRGB_565_SkColorType: return true;
82 case kN32_SkColorType: return info.gammaCloseToSRGB();
83 case kRGBA_F16_SkColorType: return true;
84 default: return false;
85 }
86 }
87
Create(const SkPixmap & dst,const SkPaint & paint,const SkMatrix & ctm,SkArenaAlloc * alloc)88 SkBlitter* SkRasterPipelineBlitter::Create(const SkPixmap& dst,
89 const SkPaint& paint,
90 const SkMatrix& ctm,
91 SkArenaAlloc* alloc) {
92 auto blitter = alloc->make<SkRasterPipelineBlitter>(
93 dst,
94 paint.getBlendMode(),
95 SkPM4f_from_SkColor(paint.getColor(), dst.colorSpace()));
96
97
98 SkBlendMode* blend = &blitter->fBlend;
99 SkPM4f* paintColor = &blitter->fPaintColor;
100 SkRasterPipeline* pipeline = &blitter->fShader;
101
102 SkShader* shader = paint.getShader();
103 SkColorFilter* colorFilter = paint.getColorFilter();
104
105 // TODO: all temporary
106 if (!supported(dst.info()) || !SkBlendMode_AppendStages(*blend)) {
107 return nullptr;
108 }
109
110 bool is_opaque = paintColor->a() == 1.0f,
111 is_constant = true;
112 if (shader) {
113 pipeline->append(SkRasterPipeline::seed_shader, &blitter->fCurrentY);
114 if (!shader->appendStages(pipeline, dst.colorSpace(), alloc, ctm, paint)) {
115 return nullptr;
116 }
117 if (!is_opaque) {
118 pipeline->append(SkRasterPipeline::scale_1_float,
119 &paintColor->fVec[SkPM4f::A]);
120 }
121
122 is_opaque = is_opaque && shader->isOpaque();
123 is_constant = shader->isConstant();
124 } else {
125 pipeline->append(SkRasterPipeline::constant_color, paintColor);
126 }
127
128 if (colorFilter) {
129 if (!colorFilter->appendStages(pipeline, dst.colorSpace(), alloc, is_opaque)) {
130 return nullptr;
131 }
132 is_opaque = is_opaque && (colorFilter->getFlags() & SkColorFilter::kAlphaUnchanged_Flag);
133 }
134
135 if (is_constant) {
136 pipeline->append(SkRasterPipeline::store_f32, &paintColor);
137 pipeline->run(0,1);
138
139 *pipeline = SkRasterPipeline();
140 pipeline->append(SkRasterPipeline::constant_color, paintColor);
141
142 is_opaque = paintColor->a() == 1.0f;
143 }
144
145 if (is_opaque && *blend == SkBlendMode::kSrcOver) {
146 *blend = SkBlendMode::kSrc;
147 }
148
149 if (is_constant && *blend == SkBlendMode::kSrc) {
150 SkRasterPipeline p;
151 p.extend(*pipeline);
152 blitter->fDstPtr = &blitter->fMemsetColor;
153 blitter->append_store(&p);
154 p.run(0,1);
155
156 blitter->fCanMemsetInBlitH = true;
157 }
158
159 return blitter;
160 }
161
append_load_d(SkRasterPipeline * p) const162 void SkRasterPipelineBlitter::append_load_d(SkRasterPipeline* p) const {
163 SkASSERT(supported(fDst.info()));
164
165 p->append(SkRasterPipeline::move_src_dst);
166 switch (fDst.info().colorType()) {
167 case kAlpha_8_SkColorType: p->append(SkRasterPipeline::load_a8, &fDstPtr); break;
168 case kRGB_565_SkColorType: p->append(SkRasterPipeline::load_565, &fDstPtr); break;
169 case kBGRA_8888_SkColorType:
170 case kRGBA_8888_SkColorType: p->append(SkRasterPipeline::load_8888, &fDstPtr); break;
171 case kRGBA_F16_SkColorType: p->append(SkRasterPipeline::load_f16, &fDstPtr); break;
172 default: break;
173 }
174 if (fDst.info().colorType() == kBGRA_8888_SkColorType) {
175 p->append(SkRasterPipeline::swap_rb);
176 }
177 if (fDst.info().gammaCloseToSRGB()) {
178 p->append_from_srgb(fDst.info().alphaType());
179 }
180 p->append(SkRasterPipeline::swap);
181 }
182
append_store(SkRasterPipeline * p) const183 void SkRasterPipelineBlitter::append_store(SkRasterPipeline* p) const {
184 if (fDst.info().gammaCloseToSRGB()) {
185 p->append(SkRasterPipeline::to_srgb);
186 }
187 if (fDst.info().colorType() == kBGRA_8888_SkColorType) {
188 p->append(SkRasterPipeline::swap_rb);
189 }
190
191 SkASSERT(supported(fDst.info()));
192 switch (fDst.info().colorType()) {
193 case kAlpha_8_SkColorType: p->append(SkRasterPipeline::store_a8, &fDstPtr); break;
194 case kRGB_565_SkColorType: p->append(SkRasterPipeline::store_565, &fDstPtr); break;
195 case kBGRA_8888_SkColorType:
196 case kRGBA_8888_SkColorType: p->append(SkRasterPipeline::store_8888, &fDstPtr); break;
197 case kRGBA_F16_SkColorType: p->append(SkRasterPipeline::store_f16, &fDstPtr); break;
198 default: break;
199 }
200 }
201
append_blend(SkRasterPipeline * p) const202 void SkRasterPipelineBlitter::append_blend(SkRasterPipeline* p) const {
203 SkAssertResult(SkBlendMode_AppendStages(fBlend, p));
204 }
205
maybe_clamp(SkRasterPipeline * p) const206 void SkRasterPipelineBlitter::maybe_clamp(SkRasterPipeline* p) const {
207 if (SkBlendMode_CanOverflow(fBlend)) {
208 p->append(SkRasterPipeline::clamp_a);
209 }
210 }
211
blitH(int x,int y,int w)212 void SkRasterPipelineBlitter::blitH(int x, int y, int w) {
213 fDstPtr = fDst.writable_addr(0,y);
214 fCurrentY = y;
215
216 if (fCanMemsetInBlitH) {
217 switch (fDst.shiftPerPixel()) {
218 case 0: memset ((uint8_t *)fDstPtr + x, fMemsetColor, w); return;
219 case 1: sk_memset16((uint16_t*)fDstPtr + x, fMemsetColor, w); return;
220 case 2: sk_memset32((uint32_t*)fDstPtr + x, fMemsetColor, w); return;
221 case 3: sk_memset64((uint64_t*)fDstPtr + x, fMemsetColor, w); return;
222 default: break;
223 }
224 }
225
226 auto& p = fBlitH;
227 if (p.empty()) {
228 p.extend(fShader);
229 if (fBlend != SkBlendMode::kSrc) {
230 this->append_load_d(&p);
231 this->append_blend(&p);
232 this->maybe_clamp(&p);
233 }
234 this->append_store(&p);
235 }
236 p.run(x,w);
237 }
238
blitAntiH(int x,int y,const SkAlpha aa[],const int16_t runs[])239 void SkRasterPipelineBlitter::blitAntiH(int x, int y, const SkAlpha aa[], const int16_t runs[]) {
240 auto& p = fBlitAntiH;
241 if (p.empty()) {
242 p.extend(fShader);
243 if (fBlend == SkBlendMode::kSrcOver) {
244 p.append(SkRasterPipeline::scale_1_float, &fCurrentCoverage);
245 this->append_load_d(&p);
246 this->append_blend(&p);
247 } else {
248 this->append_load_d(&p);
249 this->append_blend(&p);
250 p.append(SkRasterPipeline::lerp_1_float, &fCurrentCoverage);
251 }
252 this->maybe_clamp(&p);
253 this->append_store(&p);
254 }
255
256 fDstPtr = fDst.writable_addr(0,y);
257 fCurrentY = y;
258 for (int16_t run = *runs; run > 0; run = *runs) {
259 switch (*aa) {
260 case 0x00: break;
261 case 0xff: this->blitH(x,y,run); break;
262 default:
263 fCurrentCoverage = *aa * (1/255.0f);
264 p.run(x,run);
265 }
266 x += run;
267 runs += run;
268 aa += run;
269 }
270 }
271
blitMask(const SkMask & mask,const SkIRect & clip)272 void SkRasterPipelineBlitter::blitMask(const SkMask& mask, const SkIRect& clip) {
273 if (mask.fFormat == SkMask::kBW_Format) {
274 // TODO: native BW masks?
275 return INHERITED::blitMask(mask, clip);
276 }
277
278 if (mask.fFormat == SkMask::kA8_Format && fBlitMaskA8.empty()) {
279 auto& p = fBlitMaskA8;
280 p.extend(fShader);
281 if (fBlend == SkBlendMode::kSrcOver) {
282 p.append(SkRasterPipeline::scale_u8, &fMaskPtr);
283 this->append_load_d(&p);
284 this->append_blend(&p);
285 } else {
286 this->append_load_d(&p);
287 this->append_blend(&p);
288 p.append(SkRasterPipeline::lerp_u8, &fMaskPtr);
289 }
290 this->maybe_clamp(&p);
291 this->append_store(&p);
292 }
293
294 if (mask.fFormat == SkMask::kLCD16_Format && fBlitMaskLCD16.empty()) {
295 auto& p = fBlitMaskLCD16;
296 p.extend(fShader);
297 this->append_load_d(&p);
298 this->append_blend(&p);
299 p.append(SkRasterPipeline::lerp_565, &fMaskPtr);
300 this->maybe_clamp(&p);
301 this->append_store(&p);
302 }
303
304 int x = clip.left();
305 for (int y = clip.top(); y < clip.bottom(); y++) {
306 fDstPtr = fDst.writable_addr(0,y);
307 fCurrentY = y;
308
309 switch (mask.fFormat) {
310 case SkMask::kA8_Format:
311 fMaskPtr = mask.getAddr8(x,y)-x;
312 fBlitMaskA8.run(x,clip.width());
313 break;
314 case SkMask::kLCD16_Format:
315 fMaskPtr = mask.getAddrLCD16(x,y)-x;
316 fBlitMaskLCD16.run(x,clip.width());
317 break;
318 default:
319 // TODO
320 break;
321 }
322 }
323 }
324