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 "SkLinearBitmapPipeline.h"
9
10 #include <algorithm>
11 #include <cmath>
12 #include <limits>
13 #include <tuple>
14
15 #include "SkLinearBitmapPipeline_core.h"
16 #include "SkLinearBitmapPipeline_matrix.h"
17 #include "SkLinearBitmapPipeline_tile.h"
18 #include "SkLinearBitmapPipeline_sample.h"
19 #include "SkNx.h"
20 #include "SkOpts.h"
21 #include "SkPM4f.h"
22
23 ////////////////////////////////////////////////////////////////////////////////////////////////////
24 // SkLinearBitmapPipeline::Stage
25 template<typename Base, size_t kSize, typename Next>
~Stage()26 SkLinearBitmapPipeline::Stage<Base, kSize, Next>::~Stage() {
27 if (fIsInitialized) {
28 this->get()->~Base();
29 }
30 }
31
32 template<typename Base, size_t kSize, typename Next>
33 template<typename Variant, typename... Args>
initStage(Next * next,Args &&...args)34 void SkLinearBitmapPipeline::Stage<Base, kSize, Next>::initStage(Next* next, Args&& ... args) {
35 SkASSERTF(sizeof(Variant) <= sizeof(fSpace),
36 "Size Variant: %d, Space: %d", sizeof(Variant), sizeof(fSpace));
37
38 new (&fSpace) Variant(next, std::forward<Args>(args)...);
39 fStageCloner = [this](Next* nextClone, void* addr) {
40 new (addr) Variant(nextClone, (const Variant&)*this->get());
41 };
42 fIsInitialized = true;
43 };
44
45 template<typename Base, size_t kSize, typename Next>
46 template<typename Variant, typename... Args>
initSink(Args &&...args)47 void SkLinearBitmapPipeline::Stage<Base, kSize, Next>::initSink(Args&& ... args) {
48 SkASSERTF(sizeof(Variant) <= sizeof(fSpace),
49 "Size Variant: %d, Space: %d", sizeof(Variant), sizeof(fSpace));
50 new (&fSpace) Variant(std::forward<Args>(args)...);
51 fIsInitialized = true;
52 };
53
54 template<typename Base, size_t kSize, typename Next>
55 template <typename To, typename From>
56 To* SkLinearBitmapPipeline::Stage<Base, kSize, Next>::getInterface() {
57 From* down = static_cast<From*>(this->get());
58 return static_cast<To*>(down);
59 }
60
61 template<typename Base, size_t kSize, typename Next>
62 Base* SkLinearBitmapPipeline::Stage<Base, kSize, Next>::cloneStageTo(
63 Next* next, Stage* cloneToStage) const
64 {
65 if (!fIsInitialized) return nullptr;
66 fStageCloner(next, &cloneToStage->fSpace);
67 return cloneToStage->get();
68 }
69
70 namespace {
71
72 ////////////////////////////////////////////////////////////////////////////////////////////////////
73 // Matrix Stage
74 // PointProcessor uses a strategy to help complete the work of the different stages. The strategy
75 // must implement the following methods:
76 // * processPoints(xs, ys) - must mutate the xs and ys for the stage.
77 // * maybeProcessSpan(span, next) - This represents a horizontal series of pixels
78 // to work over.
79 // span - encapsulation of span.
80 // next - a pointer to the next stage.
81 // maybeProcessSpan - returns false if it can not process the span and needs to fallback to
82 // point lists for processing.
83 template<typename Strategy, typename Next>
84 class MatrixStage final : public SkLinearBitmapPipeline::PointProcessorInterface {
85 public:
86 template <typename... Args>
MatrixStage(Next * next,Args &&...args)87 MatrixStage(Next* next, Args&&... args)
88 : fNext{next}
89 , fStrategy{std::forward<Args>(args)...}{ }
90
MatrixStage(Next * next,const MatrixStage & stage)91 MatrixStage(Next* next, const MatrixStage& stage)
92 : fNext{next}
93 , fStrategy{stage.fStrategy} { }
94
pointListFew(int n,Sk4s xs,Sk4s ys)95 void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
96 fStrategy.processPoints(&xs, &ys);
97 fNext->pointListFew(n, xs, ys);
98 }
99
pointList4(Sk4s xs,Sk4s ys)100 void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
101 fStrategy.processPoints(&xs, &ys);
102 fNext->pointList4(xs, ys);
103 }
104
105 // The span you pass must not be empty.
pointSpan(Span span)106 void pointSpan(Span span) override {
107 SkASSERT(!span.isEmpty());
108 if (!fStrategy.maybeProcessSpan(span, fNext)) {
109 span_fallback(span, this);
110 }
111 }
112
113 private:
114 Next* const fNext;
115 Strategy fStrategy;
116 };
117
118 template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
119 using TranslateMatrix = MatrixStage<TranslateMatrixStrategy, Next>;
120
121 template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
122 using ScaleMatrix = MatrixStage<ScaleMatrixStrategy, Next>;
123
124 template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
125 using AffineMatrix = MatrixStage<AffineMatrixStrategy, Next>;
126
127 template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface>
128 using PerspectiveMatrix = MatrixStage<PerspectiveMatrixStrategy, Next>;
129
130
choose_matrix(SkLinearBitmapPipeline::PointProcessorInterface * next,const SkMatrix & inverse,SkLinearBitmapPipeline::MatrixStage * matrixProc)131 static SkLinearBitmapPipeline::PointProcessorInterface* choose_matrix(
132 SkLinearBitmapPipeline::PointProcessorInterface* next,
133 const SkMatrix& inverse,
134 SkLinearBitmapPipeline::MatrixStage* matrixProc) {
135 if (inverse.hasPerspective()) {
136 matrixProc->initStage<PerspectiveMatrix<>>(
137 next,
138 SkVector{inverse.getTranslateX(), inverse.getTranslateY()},
139 SkVector{inverse.getScaleX(), inverse.getScaleY()},
140 SkVector{inverse.getSkewX(), inverse.getSkewY()},
141 SkVector{inverse.getPerspX(), inverse.getPerspY()},
142 inverse.get(SkMatrix::kMPersp2));
143 } else if (inverse.getSkewX() != 0.0f || inverse.getSkewY() != 0.0f) {
144 matrixProc->initStage<AffineMatrix<>>(
145 next,
146 SkVector{inverse.getTranslateX(), inverse.getTranslateY()},
147 SkVector{inverse.getScaleX(), inverse.getScaleY()},
148 SkVector{inverse.getSkewX(), inverse.getSkewY()});
149 } else if (inverse.getScaleX() != 1.0f || inverse.getScaleY() != 1.0f) {
150 matrixProc->initStage<ScaleMatrix<>>(
151 next,
152 SkVector{inverse.getTranslateX(), inverse.getTranslateY()},
153 SkVector{inverse.getScaleX(), inverse.getScaleY()});
154 } else if (inverse.getTranslateX() != 0.0f || inverse.getTranslateY() != 0.0f) {
155 matrixProc->initStage<TranslateMatrix<>>(
156 next,
157 SkVector{inverse.getTranslateX(), inverse.getTranslateY()});
158 } else {
159 return next;
160 }
161 return matrixProc->get();
162 }
163
164 ////////////////////////////////////////////////////////////////////////////////////////////////////
165 // Tile Stage
166
167 template<typename XStrategy, typename YStrategy, typename Next>
168 class CombinedTileStage final : public SkLinearBitmapPipeline::PointProcessorInterface {
169 public:
CombinedTileStage(Next * next,SkISize dimensions)170 CombinedTileStage(Next* next, SkISize dimensions)
171 : fNext{next}
172 , fXStrategy{dimensions.width()}
173 , fYStrategy{dimensions.height()}{ }
174
CombinedTileStage(Next * next,const CombinedTileStage & stage)175 CombinedTileStage(Next* next, const CombinedTileStage& stage)
176 : fNext{next}
177 , fXStrategy{stage.fXStrategy}
178 , fYStrategy{stage.fYStrategy} { }
179
pointListFew(int n,Sk4s xs,Sk4s ys)180 void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
181 fXStrategy.tileXPoints(&xs);
182 fYStrategy.tileYPoints(&ys);
183 fNext->pointListFew(n, xs, ys);
184 }
185
pointList4(Sk4s xs,Sk4s ys)186 void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
187 fXStrategy.tileXPoints(&xs);
188 fYStrategy.tileYPoints(&ys);
189 fNext->pointList4(xs, ys);
190 }
191
192 // The span you pass must not be empty.
pointSpan(Span span)193 void pointSpan(Span span) override {
194 SkASSERT(!span.isEmpty());
195 SkPoint start; SkScalar length; int count;
196 std::tie(start, length, count) = span;
197
198 if (span.count() == 1) {
199 // DANGER:
200 // The explicit casts from float to Sk4f are not usually necessary, but are here to
201 // work around an MSVC 2015u2 c++ code generation bug. This is tracked using skia bug
202 // 5566.
203 this->pointListFew(1, Sk4f{span.startX()}, Sk4f{span.startY()});
204 return;
205 }
206
207 SkScalar x = X(start);
208 SkScalar y = fYStrategy.tileY(Y(start));
209 Span yAdjustedSpan{{x, y}, length, count};
210
211 if (!fXStrategy.maybeProcessSpan(yAdjustedSpan, fNext)) {
212 span_fallback(span, this);
213 }
214 }
215
216 private:
217 Next* const fNext;
218 XStrategy fXStrategy;
219 YStrategy fYStrategy;
220 };
221
222 template <typename XStrategy, typename Next>
choose_tiler_ymode(SkShader::TileMode yMode,SkFilterQuality filterQuality,SkISize dimensions,Next * next,SkLinearBitmapPipeline::TileStage * tileStage)223 void choose_tiler_ymode(
224 SkShader::TileMode yMode, SkFilterQuality filterQuality, SkISize dimensions,
225 Next* next,
226 SkLinearBitmapPipeline::TileStage* tileStage) {
227 switch (yMode) {
228 case SkShader::kClamp_TileMode: {
229 using Tiler = CombinedTileStage<XStrategy, YClampStrategy, Next>;
230 tileStage->initStage<Tiler>(next, dimensions);
231 break;
232 }
233 case SkShader::kRepeat_TileMode: {
234 using Tiler = CombinedTileStage<XStrategy, YRepeatStrategy, Next>;
235 tileStage->initStage<Tiler>(next, dimensions);
236 break;
237 }
238 case SkShader::kMirror_TileMode: {
239 using Tiler = CombinedTileStage<XStrategy, YMirrorStrategy, Next>;
240 tileStage->initStage<Tiler>(next, dimensions);
241 break;
242 }
243 }
244 };
245
choose_tiler(SkLinearBitmapPipeline::SampleProcessorInterface * next,SkISize dimensions,SkShader::TileMode xMode,SkShader::TileMode yMode,SkFilterQuality filterQuality,SkScalar dx,SkLinearBitmapPipeline::TileStage * tileStage)246 static SkLinearBitmapPipeline::PointProcessorInterface* choose_tiler(
247 SkLinearBitmapPipeline::SampleProcessorInterface* next,
248 SkISize dimensions,
249 SkShader::TileMode xMode,
250 SkShader::TileMode yMode,
251 SkFilterQuality filterQuality,
252 SkScalar dx,
253 SkLinearBitmapPipeline::TileStage* tileStage)
254 {
255 switch (xMode) {
256 case SkShader::kClamp_TileMode:
257 choose_tiler_ymode<XClampStrategy>(yMode, filterQuality, dimensions, next, tileStage);
258 break;
259 case SkShader::kRepeat_TileMode:
260 if (dx == 1.0f && filterQuality == kNone_SkFilterQuality) {
261 choose_tiler_ymode<XRepeatUnitScaleStrategy>(
262 yMode, kNone_SkFilterQuality, dimensions, next, tileStage);
263 } else {
264 choose_tiler_ymode<XRepeatStrategy>(
265 yMode, filterQuality, dimensions, next, tileStage);
266 }
267 break;
268 case SkShader::kMirror_TileMode:
269 choose_tiler_ymode<XMirrorStrategy>(yMode, filterQuality, dimensions, next, tileStage);
270 break;
271 }
272
273 return tileStage->get();
274 }
275
276 ////////////////////////////////////////////////////////////////////////////////////////////////////
277 // Specialized Samplers
278
279 // RGBA8888UnitRepeatSrc - A sampler that takes advantage of the fact the the src and destination
280 // are the same format and do not need in transformations in pixel space. Therefore, there is no
281 // need to convert them to HiFi pixel format.
282 class RGBA8888UnitRepeatSrc final : public SkLinearBitmapPipeline::SampleProcessorInterface,
283 public SkLinearBitmapPipeline::DestinationInterface {
284 public:
RGBA8888UnitRepeatSrc(const uint32_t * src,int32_t width)285 RGBA8888UnitRepeatSrc(const uint32_t* src, int32_t width)
286 : fSrc{src}, fWidth{width} { }
287
pointListFew(int n,Sk4s xs,Sk4s ys)288 void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
289 SkASSERT(fDest + n <= fEnd);
290 // At this point xs and ys should be >= 0, so trunc is the same as floor.
291 Sk4i iXs = SkNx_cast<int>(xs);
292 Sk4i iYs = SkNx_cast<int>(ys);
293
294 if (n >= 1) *fDest++ = *this->pixelAddress(iXs[0], iYs[0]);
295 if (n >= 2) *fDest++ = *this->pixelAddress(iXs[1], iYs[1]);
296 if (n >= 3) *fDest++ = *this->pixelAddress(iXs[2], iYs[2]);
297 }
298
pointList4(Sk4s xs,Sk4s ys)299 void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
300 SkASSERT(fDest + 4 <= fEnd);
301 Sk4i iXs = SkNx_cast<int>(xs);
302 Sk4i iYs = SkNx_cast<int>(ys);
303 *fDest++ = *this->pixelAddress(iXs[0], iYs[0]);
304 *fDest++ = *this->pixelAddress(iXs[1], iYs[1]);
305 *fDest++ = *this->pixelAddress(iXs[2], iYs[2]);
306 *fDest++ = *this->pixelAddress(iXs[3], iYs[3]);
307 }
308
pointSpan(Span span)309 void pointSpan(Span span) override {
310 SkASSERT(fDest + span.count() <= fEnd);
311 if (span.length() != 0.0f) {
312 int32_t x = SkScalarTruncToInt(span.startX());
313 int32_t y = SkScalarTruncToInt(span.startY());
314 const uint32_t* src = this->pixelAddress(x, y);
315 memmove(fDest, src, span.count() * sizeof(uint32_t));
316 fDest += span.count();
317 }
318 }
319
repeatSpan(Span span,int32_t repeatCount)320 void repeatSpan(Span span, int32_t repeatCount) override {
321 SkASSERT(fDest + span.count() * repeatCount <= fEnd);
322
323 int32_t x = SkScalarTruncToInt(span.startX());
324 int32_t y = SkScalarTruncToInt(span.startY());
325 const uint32_t* src = this->pixelAddress(x, y);
326 uint32_t* dest = fDest;
327 while (repeatCount --> 0) {
328 memmove(dest, src, span.count() * sizeof(uint32_t));
329 dest += span.count();
330 }
331 fDest = dest;
332 }
333
setDestination(void * dst,int count)334 void setDestination(void* dst, int count) override {
335 fDest = static_cast<uint32_t*>(dst);
336 fEnd = fDest + count;
337 }
338
339 private:
pixelAddress(int32_t x,int32_t y)340 const uint32_t* pixelAddress(int32_t x, int32_t y) {
341 return &fSrc[fWidth * y + x];
342 }
343 const uint32_t* const fSrc;
344 const int32_t fWidth;
345 uint32_t* fDest;
346 uint32_t* fEnd;
347 };
348
349 // RGBA8888UnitRepeatSrc - A sampler that takes advantage of the fact the the src and destination
350 // are the same format and do not need in transformations in pixel space. Therefore, there is no
351 // need to convert them to HiFi pixel format.
352 class RGBA8888UnitRepeatSrcOver final : public SkLinearBitmapPipeline::SampleProcessorInterface,
353 public SkLinearBitmapPipeline::DestinationInterface {
354 public:
RGBA8888UnitRepeatSrcOver(const uint32_t * src,int32_t width)355 RGBA8888UnitRepeatSrcOver(const uint32_t* src, int32_t width)
356 : fSrc{src}, fWidth{width} { }
357
pointListFew(int n,Sk4s xs,Sk4s ys)358 void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
359 SkASSERT(fDest + n <= fEnd);
360 // At this point xs and ys should be >= 0, so trunc is the same as floor.
361 Sk4i iXs = SkNx_cast<int>(xs);
362 Sk4i iYs = SkNx_cast<int>(ys);
363
364 if (n >= 1) blendPixelAt(iXs[0], iYs[0]);
365 if (n >= 2) blendPixelAt(iXs[1], iYs[1]);
366 if (n >= 3) blendPixelAt(iXs[2], iYs[2]);
367 }
368
pointList4(Sk4s xs,Sk4s ys)369 void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
370 SkASSERT(fDest + 4 <= fEnd);
371 Sk4i iXs = SkNx_cast<int>(xs);
372 Sk4i iYs = SkNx_cast<int>(ys);
373 blendPixelAt(iXs[0], iYs[0]);
374 blendPixelAt(iXs[1], iYs[1]);
375 blendPixelAt(iXs[2], iYs[2]);
376 blendPixelAt(iXs[3], iYs[3]);
377 }
378
pointSpan(Span span)379 void pointSpan(Span span) override {
380 if (span.length() != 0.0f) {
381 this->repeatSpan(span, 1);
382 }
383 }
384
repeatSpan(Span span,int32_t repeatCount)385 void repeatSpan(Span span, int32_t repeatCount) override {
386 SkASSERT(fDest + span.count() * repeatCount <= fEnd);
387 SkASSERT(span.count() > 0);
388 SkASSERT(repeatCount > 0);
389
390 int32_t x = (int32_t)span.startX();
391 int32_t y = (int32_t)span.startY();
392 const uint32_t* beginSpan = this->pixelAddress(x, y);
393
394 SkOpts::srcover_srgb_srgb(fDest, beginSpan, span.count() * repeatCount, span.count());
395
396 fDest += span.count() * repeatCount;
397
398 SkASSERT(fDest <= fEnd);
399 }
400
setDestination(void * dst,int count)401 void setDestination(void* dst, int count) override {
402 SkASSERT(count > 0);
403 fDest = static_cast<uint32_t*>(dst);
404 fEnd = fDest + count;
405 }
406
407 private:
pixelAddress(int32_t x,int32_t y)408 const uint32_t* pixelAddress(int32_t x, int32_t y) {
409 return &fSrc[fWidth * y + x];
410 }
411
blendPixelAt(int32_t x,int32_t y)412 void blendPixelAt(int32_t x, int32_t y) {
413 const uint32_t* src = this->pixelAddress(x, y);
414 SkOpts::srcover_srgb_srgb(fDest, src, 1, 1);
415 fDest += 1;
416 }
417
418 const uint32_t* const fSrc;
419 const int32_t fWidth;
420 uint32_t* fDest;
421 uint32_t* fEnd;
422 };
423
424 using Blender = SkLinearBitmapPipeline::BlendProcessorInterface;
425
426 template <SkColorType colorType>
choose_specific_accessor(const SkPixmap & srcPixmap,SkLinearBitmapPipeline::Accessor * accessor)427 static SkLinearBitmapPipeline::PixelAccessorInterface* choose_specific_accessor(
428 const SkPixmap& srcPixmap, SkLinearBitmapPipeline::Accessor* accessor)
429 {
430 if (srcPixmap.info().gammaCloseToSRGB()) {
431 using PA = PixelAccessor<colorType, kSRGB_SkGammaType>;
432 accessor->init<PA>(srcPixmap);
433 return accessor->get();
434 } else {
435 using PA = PixelAccessor<colorType, kLinear_SkGammaType>;
436 accessor->init<PA>(srcPixmap);
437 return accessor->get();
438 }
439 }
440
choose_pixel_accessor(const SkPixmap & srcPixmap,const SkColor A8TintColor,SkLinearBitmapPipeline::Accessor * accessor)441 static SkLinearBitmapPipeline::PixelAccessorInterface* choose_pixel_accessor(
442 const SkPixmap& srcPixmap,
443 const SkColor A8TintColor,
444 SkLinearBitmapPipeline::Accessor* accessor)
445 {
446 const SkImageInfo& imageInfo = srcPixmap.info();
447
448 SkLinearBitmapPipeline::PixelAccessorInterface* pixelAccessor = nullptr;
449 switch (imageInfo.colorType()) {
450 case kAlpha_8_SkColorType: {
451 using PA = PixelAccessor<kAlpha_8_SkColorType, kLinear_SkGammaType>;
452 accessor->init<PA>(srcPixmap, A8TintColor);
453 pixelAccessor = accessor->get();
454 }
455 break;
456 case kARGB_4444_SkColorType:
457 pixelAccessor = choose_specific_accessor<kARGB_4444_SkColorType>(srcPixmap, accessor);
458 break;
459 case kRGB_565_SkColorType:
460 pixelAccessor = choose_specific_accessor<kRGB_565_SkColorType>(srcPixmap, accessor);
461 break;
462 case kRGBA_8888_SkColorType:
463 pixelAccessor = choose_specific_accessor<kRGBA_8888_SkColorType>(srcPixmap, accessor);
464 break;
465 case kBGRA_8888_SkColorType:
466 pixelAccessor = choose_specific_accessor<kBGRA_8888_SkColorType>(srcPixmap, accessor);
467 break;
468 case kIndex_8_SkColorType:
469 pixelAccessor = choose_specific_accessor<kIndex_8_SkColorType>(srcPixmap, accessor);
470 break;
471 case kGray_8_SkColorType:
472 pixelAccessor = choose_specific_accessor<kGray_8_SkColorType>(srcPixmap, accessor);
473 break;
474 case kRGBA_F16_SkColorType: {
475 using PA = PixelAccessor<kRGBA_F16_SkColorType, kLinear_SkGammaType>;
476 accessor->init<PA>(srcPixmap);
477 pixelAccessor = accessor->get();
478 }
479 break;
480 default:
481 SkFAIL("Not implemented. Unsupported src");
482 break;
483 }
484
485 return pixelAccessor;
486 }
487
choose_pixel_sampler(Blender * next,SkFilterQuality filterQuality,SkShader::TileMode xTile,SkShader::TileMode yTile,const SkPixmap & srcPixmap,const SkColor A8TintColor,SkLinearBitmapPipeline::SampleStage * sampleStage,SkLinearBitmapPipeline::Accessor * accessor)488 SkLinearBitmapPipeline::SampleProcessorInterface* choose_pixel_sampler(
489 Blender* next,
490 SkFilterQuality filterQuality,
491 SkShader::TileMode xTile, SkShader::TileMode yTile,
492 const SkPixmap& srcPixmap,
493 const SkColor A8TintColor,
494 SkLinearBitmapPipeline::SampleStage* sampleStage,
495 SkLinearBitmapPipeline::Accessor* accessor) {
496 const SkImageInfo& imageInfo = srcPixmap.info();
497 SkISize dimensions = imageInfo.dimensions();
498
499 // Special case samplers with fully expanded templates
500 if (imageInfo.gammaCloseToSRGB()) {
501 if (filterQuality == kNone_SkFilterQuality) {
502 switch (imageInfo.colorType()) {
503 case kN32_SkColorType: {
504 using S =
505 NearestNeighborSampler<
506 PixelAccessor<kN32_SkColorType, kSRGB_SkGammaType>, Blender>;
507 sampleStage->initStage<S>(next, srcPixmap);
508 return sampleStage->get();
509 }
510 case kIndex_8_SkColorType: {
511 using S =
512 NearestNeighborSampler<
513 PixelAccessor<kIndex_8_SkColorType, kSRGB_SkGammaType>, Blender>;
514 sampleStage->initStage<S>(next, srcPixmap);
515 return sampleStage->get();
516 }
517 default:
518 break;
519 }
520 } else {
521 switch (imageInfo.colorType()) {
522 case kN32_SkColorType: {
523 using S =
524 BilerpSampler<
525 PixelAccessor<kN32_SkColorType, kSRGB_SkGammaType>, Blender>;
526 sampleStage->initStage<S>(next, dimensions, xTile, yTile, srcPixmap);
527 return sampleStage->get();
528 }
529 case kIndex_8_SkColorType: {
530 using S =
531 BilerpSampler<
532 PixelAccessor<kIndex_8_SkColorType, kSRGB_SkGammaType>, Blender>;
533 sampleStage->initStage<S>(next, dimensions, xTile, yTile, srcPixmap);
534 return sampleStage->get();
535 }
536 default:
537 break;
538 }
539 }
540 }
541
542 auto pixelAccessor = choose_pixel_accessor(srcPixmap, A8TintColor, accessor);
543 // General cases.
544 if (filterQuality == kNone_SkFilterQuality) {
545 using S = NearestNeighborSampler<PixelAccessorShim, Blender>;
546 sampleStage->initStage<S>(next, pixelAccessor);
547 } else {
548 using S = BilerpSampler<PixelAccessorShim, Blender>;
549 sampleStage->initStage<S>(next, dimensions, xTile, yTile, pixelAccessor);
550 }
551 return sampleStage->get();
552 }
553
554 ////////////////////////////////////////////////////////////////////////////////////////////////////
555 // Pixel Blender Stage
556 template <SkAlphaType alphaType>
557 class SrcFPPixel final : public SkLinearBitmapPipeline::BlendProcessorInterface {
558 public:
SrcFPPixel(float postAlpha)559 SrcFPPixel(float postAlpha) : fPostAlpha{postAlpha} { }
SrcFPPixel(const SrcFPPixel & Blender)560 SrcFPPixel(const SrcFPPixel& Blender) : fPostAlpha(Blender.fPostAlpha) {}
blendPixel(Sk4f pixel)561 void SK_VECTORCALL blendPixel(Sk4f pixel) override {
562 SkASSERT(fDst + 1 <= fEnd );
563 this->srcPixel(fDst, pixel, 0);
564 fDst += 1;
565 }
566
blend4Pixels(Sk4f p0,Sk4f p1,Sk4f p2,Sk4f p3)567 void SK_VECTORCALL blend4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) override {
568 SkASSERT(fDst + 4 <= fEnd);
569 SkPM4f* dst = fDst;
570 this->srcPixel(dst, p0, 0);
571 this->srcPixel(dst, p1, 1);
572 this->srcPixel(dst, p2, 2);
573 this->srcPixel(dst, p3, 3);
574 fDst += 4;
575 }
576
setDestination(void * dst,int count)577 void setDestination(void* dst, int count) override {
578 fDst = static_cast<SkPM4f*>(dst);
579 fEnd = fDst + count;
580 }
581
582 private:
srcPixel(SkPM4f * dst,Sk4f pixel,int index)583 void SK_VECTORCALL srcPixel(SkPM4f* dst, Sk4f pixel, int index) {
584 check_pixel(pixel);
585
586 Sk4f newPixel = pixel;
587 if (alphaType == kUnpremul_SkAlphaType) {
588 newPixel = Premultiply(pixel);
589 }
590 newPixel = newPixel * fPostAlpha;
591 newPixel.store(dst + index);
592 }
Premultiply(Sk4f pixel)593 static Sk4f SK_VECTORCALL Premultiply(Sk4f pixel) {
594 float alpha = pixel[3];
595 return pixel * Sk4f{alpha, alpha, alpha, 1.0f};
596 }
597
598 SkPM4f* fDst;
599 SkPM4f* fEnd;
600 Sk4f fPostAlpha;
601 };
602
choose_blender_for_shading(SkAlphaType alphaType,float postAlpha,SkLinearBitmapPipeline::BlenderStage * blenderStage)603 static SkLinearBitmapPipeline::BlendProcessorInterface* choose_blender_for_shading(
604 SkAlphaType alphaType,
605 float postAlpha,
606 SkLinearBitmapPipeline::BlenderStage* blenderStage) {
607 if (alphaType == kUnpremul_SkAlphaType) {
608 blenderStage->initSink<SrcFPPixel<kUnpremul_SkAlphaType>>(postAlpha);
609 } else {
610 // kOpaque_SkAlphaType is treated the same as kPremul_SkAlphaType
611 blenderStage->initSink<SrcFPPixel<kPremul_SkAlphaType>>(postAlpha);
612 }
613 return blenderStage->get();
614 }
615
616 } // namespace
617
618 ////////////////////////////////////////////////////////////////////////////////////////////////////
619 // SkLinearBitmapPipeline
~SkLinearBitmapPipeline()620 SkLinearBitmapPipeline::~SkLinearBitmapPipeline() {}
621
SkLinearBitmapPipeline(const SkMatrix & inverse,SkFilterQuality filterQuality,SkShader::TileMode xTile,SkShader::TileMode yTile,SkColor paintColor,const SkPixmap & srcPixmap)622 SkLinearBitmapPipeline::SkLinearBitmapPipeline(
623 const SkMatrix& inverse,
624 SkFilterQuality filterQuality,
625 SkShader::TileMode xTile, SkShader::TileMode yTile,
626 SkColor paintColor,
627 const SkPixmap& srcPixmap)
628 {
629 SkISize dimensions = srcPixmap.info().dimensions();
630 const SkImageInfo& srcImageInfo = srcPixmap.info();
631
632 SkMatrix adjustedInverse = inverse;
633 if (filterQuality == kNone_SkFilterQuality) {
634 if (inverse.getScaleX() >= 0.0f) {
635 adjustedInverse.setTranslateX(
636 nextafterf(inverse.getTranslateX(), std::floor(inverse.getTranslateX())));
637 }
638 if (inverse.getScaleY() >= 0.0f) {
639 adjustedInverse.setTranslateY(
640 nextafterf(inverse.getTranslateY(), std::floor(inverse.getTranslateY())));
641 }
642 }
643
644 SkScalar dx = adjustedInverse.getScaleX();
645
646 // If it is an index 8 color type, the sampler converts to unpremul for better fidelity.
647 SkAlphaType alphaType = srcImageInfo.alphaType();
648 if (srcPixmap.colorType() == kIndex_8_SkColorType) {
649 alphaType = kUnpremul_SkAlphaType;
650 }
651
652 float postAlpha = SkColorGetA(paintColor) * (1.0f / 255.0f);
653 // As the stages are built, the chooser function may skip a stage. For example, with the
654 // identity matrix, the matrix stage is skipped, and the tilerStage is the first stage.
655 auto blenderStage = choose_blender_for_shading(alphaType, postAlpha, &fBlenderStage);
656 auto samplerStage = choose_pixel_sampler(
657 blenderStage, filterQuality, xTile, yTile,
658 srcPixmap, paintColor, &fSampleStage, &fAccessor);
659 auto tilerStage = choose_tiler(samplerStage, dimensions, xTile, yTile,
660 filterQuality, dx, &fTileStage);
661 fFirstStage = choose_matrix(tilerStage, adjustedInverse, &fMatrixStage);
662 fLastStage = blenderStage;
663 }
664
ClonePipelineForBlitting(SkEmbeddableLinearPipeline * pipelineStorage,const SkLinearBitmapPipeline & pipeline,SkMatrix::TypeMask matrixMask,SkShader::TileMode xTileMode,SkShader::TileMode yTileMode,SkFilterQuality filterQuality,const SkPixmap & srcPixmap,float finalAlpha,SkXfermode::Mode xferMode,const SkImageInfo & dstInfo)665 bool SkLinearBitmapPipeline::ClonePipelineForBlitting(
666 SkEmbeddableLinearPipeline* pipelineStorage,
667 const SkLinearBitmapPipeline& pipeline,
668 SkMatrix::TypeMask matrixMask,
669 SkShader::TileMode xTileMode,
670 SkShader::TileMode yTileMode,
671 SkFilterQuality filterQuality,
672 const SkPixmap& srcPixmap,
673 float finalAlpha,
674 SkXfermode::Mode xferMode,
675 const SkImageInfo& dstInfo)
676 {
677 if (xferMode == SkXfermode::kSrcOver_Mode
678 && srcPixmap.info().alphaType() == kOpaque_SkAlphaType) {
679 xferMode = SkXfermode::kSrc_Mode;
680 }
681
682 if (matrixMask & ~SkMatrix::kTranslate_Mask ) { return false; }
683 if (filterQuality != SkFilterQuality::kNone_SkFilterQuality) { return false; }
684 if (finalAlpha != 1.0f) { return false; }
685 if (srcPixmap.info().colorType() != kRGBA_8888_SkColorType
686 || dstInfo.colorType() != kRGBA_8888_SkColorType) { return false; }
687
688 if (!srcPixmap.info().gammaCloseToSRGB() || !dstInfo.gammaCloseToSRGB()) {
689 return false;
690 }
691
692 if (xferMode != SkXfermode::kSrc_Mode && xferMode != SkXfermode::kSrcOver_Mode) {
693 return false;
694 }
695
696 pipelineStorage->init(pipeline, srcPixmap, xferMode, dstInfo);
697
698 return true;
699 }
700
SkLinearBitmapPipeline(const SkLinearBitmapPipeline & pipeline,const SkPixmap & srcPixmap,SkXfermode::Mode mode,const SkImageInfo & dstInfo)701 SkLinearBitmapPipeline::SkLinearBitmapPipeline(
702 const SkLinearBitmapPipeline& pipeline,
703 const SkPixmap& srcPixmap,
704 SkXfermode::Mode mode,
705 const SkImageInfo& dstInfo)
706 {
707 SkASSERT(mode == SkXfermode::kSrc_Mode || mode == SkXfermode::kSrcOver_Mode);
708 SkASSERT(srcPixmap.info().colorType() == dstInfo.colorType()
709 && srcPixmap.info().colorType() == kRGBA_8888_SkColorType);
710
711 if (mode == SkXfermode::kSrc_Mode) {
712 fSampleStage.initSink<RGBA8888UnitRepeatSrc>(
713 srcPixmap.writable_addr32(0, 0), srcPixmap.rowBytes() / 4);
714 fLastStage = fSampleStage.getInterface<DestinationInterface, RGBA8888UnitRepeatSrc>();
715 } else {
716 fSampleStage.initSink<RGBA8888UnitRepeatSrcOver>(
717 srcPixmap.writable_addr32(0, 0), srcPixmap.rowBytes() / 4);
718 fLastStage = fSampleStage.getInterface<DestinationInterface, RGBA8888UnitRepeatSrcOver>();
719 }
720
721 auto sampleStage = fSampleStage.get();
722 auto tilerStage = pipeline.fTileStage.cloneStageTo(sampleStage, &fTileStage);
723 tilerStage = (tilerStage != nullptr) ? tilerStage : sampleStage;
724 auto matrixStage = pipeline.fMatrixStage.cloneStageTo(tilerStage, &fMatrixStage);
725 matrixStage = (matrixStage != nullptr) ? matrixStage : tilerStage;
726 fFirstStage = matrixStage;
727 }
728
shadeSpan4f(int x,int y,SkPM4f * dst,int count)729 void SkLinearBitmapPipeline::shadeSpan4f(int x, int y, SkPM4f* dst, int count) {
730 SkASSERT(count > 0);
731 this->blitSpan(x, y, dst, count);
732 }
733
blitSpan(int x,int y,void * dst,int count)734 void SkLinearBitmapPipeline::blitSpan(int x, int y, void* dst, int count) {
735 SkASSERT(count > 0);
736 fLastStage->setDestination(dst, count);
737
738 // The count and length arguments start out in a precise relation in order to keep the
739 // math correct through the different stages. Count is the number of pixel to produce.
740 // Since the code samples at pixel centers, length is the distance from the center of the
741 // first pixel to the center of the last pixel. This implies that length is count-1.
742 fFirstStage->pointSpan(Span{{x + 0.5f, y + 0.5f}, count - 1.0f, count});
743 }
744