blob: 759075b3e5ab3f2481e1f0af8d999448770bbd7d [file] [log] [blame]
herb6eff52a2016-03-23 09:00:33 -07001/*
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#ifndef SkLinearBitmapPipeline_sampler_DEFINED
9#define SkLinearBitmapPipeline_sampler_DEFINED
10
herbcf05dcd2016-05-11 11:53:36 -070011#include <tuple>
12
herb670f01f2016-05-13 10:04:46 -070013#include "SkColor.h"
14#include "SkColorPriv.h"
benjaminwagner6c71e0a2016-04-07 08:49:31 -070015#include "SkFixed.h"
herbd5f2e2e2016-04-14 11:16:44 -070016#include "SkHalf.h"
herb6eff52a2016-03-23 09:00:33 -070017#include "SkLinearBitmapPipeline_core.h"
herb670f01f2016-05-13 10:04:46 -070018#include "SkNx.h"
herbcf05dcd2016-05-11 11:53:36 -070019#include "SkPM4fPriv.h"
herb6eff52a2016-03-23 09:00:33 -070020
21namespace {
22// Explaination of the math:
23// 1 - x x
24// +--------+--------+
25// | | |
26// 1 - y | px00 | px10 |
27// | | |
28// +--------+--------+
29// | | |
30// y | px01 | px11 |
31// | | |
32// +--------+--------+
33//
34//
35// Given a pixelxy each is multiplied by a different factor derived from the fractional part of x
36// and y:
37// * px00 -> (1 - x)(1 - y) = 1 - x - y + xy
38// * px10 -> x(1 - y) = x - xy
39// * px01 -> (1 - x)y = y - xy
40// * px11 -> xy
41// So x * y is calculated first and then used to calculate all the other factors.
mtkleine5fb9c82016-07-07 08:12:09 -070042static Sk4s SK_VECTORCALL bilerp4(Sk4s xs, Sk4s ys, Sk4f px00, Sk4f px10,
jcgregorioda626aa2016-07-22 05:40:58 -070043 Sk4f px01, Sk4f px11) {
herb6eff52a2016-03-23 09:00:33 -070044 // Calculate fractional xs and ys.
45 Sk4s fxs = xs - xs.floor();
46 Sk4s fys = ys - ys.floor();
47 Sk4s fxys{fxs * fys};
48 Sk4f sum = px11 * fxys;
49 sum = sum + px01 * (fys - fxys);
50 sum = sum + px10 * (fxs - fxys);
51 sum = sum + px00 * (Sk4f{1.0f} - fxs - fys + fxys);
52 return sum;
53}
54
herb15332a82016-05-12 11:37:00 -070055////////////////////////////////////////////////////////////////////////////////////////////////////
herb00dd4532016-07-11 10:33:37 -070056// PixelGetter is the lowest level interface to the source data. There is a PixelConverter for each
herb15332a82016-05-12 11:37:00 -070057// of the different SkColorTypes.
herb00dd4532016-07-11 10:33:37 -070058template <SkColorType, SkGammaType> class PixelConverter;
herb15332a82016-05-12 11:37:00 -070059
herb670f01f2016-05-13 10:04:46 -070060// Alpha handling:
61// The alpha from the paint (tintColor) is used in the blend part of the pipeline to modulate
62// the entire bitmap. So, the tint color is given an alpha of 1.0 so that the later alpha can
63// modulate this color later.
64template <>
herb00dd4532016-07-11 10:33:37 -070065class PixelConverter<kAlpha_8_SkColorType, kLinear_SkGammaType> {
herb670f01f2016-05-13 10:04:46 -070066public:
67 using Element = uint8_t;
herb00dd4532016-07-11 10:33:37 -070068 PixelConverter(const SkPixmap& srcPixmap, SkColor tintColor)
herb670f01f2016-05-13 10:04:46 -070069 : fTintColor{set_alpha(Sk4f_from_SkColor(tintColor), 1.0f)} { }
70
herb00dd4532016-07-11 10:33:37 -070071 Sk4f toSk4f(const Element pixel) const {
72 return fTintColor * (pixel * (1.0f/255.0f));
herb670f01f2016-05-13 10:04:46 -070073 }
74
75private:
76 const Sk4f fTintColor;
77};
78
reeddabe5d32016-06-21 10:28:14 -070079template <SkGammaType gammaType>
mtklein53574b72016-07-20 05:23:31 -070080static inline Sk4f pmcolor_to_rgba(SkPMColor pixel) {
81 return swizzle_rb_if_bgra(
82 (gammaType == kSRGB_SkGammaType) ? Sk4f_fromS32(pixel)
83 : Sk4f_fromL32(pixel));
84}
85
86template <SkGammaType gammaType>
herb00dd4532016-07-11 10:33:37 -070087class PixelConverter<kRGB_565_SkColorType, gammaType> {
herb670f01f2016-05-13 10:04:46 -070088public:
89 using Element = uint16_t;
herb00dd4532016-07-11 10:33:37 -070090 PixelConverter(const SkPixmap& srcPixmap) { }
herb670f01f2016-05-13 10:04:46 -070091
herb00dd4532016-07-11 10:33:37 -070092 Sk4f toSk4f(Element pixel) const {
mtklein53574b72016-07-20 05:23:31 -070093 return pmcolor_to_rgba<gammaType>(SkPixel16ToPixel32(pixel));
herb00dd4532016-07-11 10:33:37 -070094 }
95};
96
97template <SkGammaType gammaType>
98class PixelConverter<kARGB_4444_SkColorType, gammaType> {
99public:
100 using Element = uint16_t;
101 PixelConverter(const SkPixmap& srcPixmap) { }
102
103 Sk4f toSk4f(Element pixel) const {
mtklein53574b72016-07-20 05:23:31 -0700104 return pmcolor_to_rgba<gammaType>(SkPixel4444ToPixel32(pixel));
herb00dd4532016-07-11 10:33:37 -0700105 }
106};
107
108template <SkGammaType gammaType>
109class PixelConverter<kRGBA_8888_SkColorType, gammaType> {
110public:
111 using Element = uint32_t;
112 PixelConverter(const SkPixmap& srcPixmap) { }
113
114 Sk4f toSk4f(Element pixel) const {
reeddabe5d32016-06-21 10:28:14 -0700115 return gammaType == kSRGB_SkGammaType
herb670f01f2016-05-13 10:04:46 -0700116 ? Sk4f_fromS32(pixel)
117 : Sk4f_fromL32(pixel);
118 }
119};
120
reeddabe5d32016-06-21 10:28:14 -0700121template <SkGammaType gammaType>
herb00dd4532016-07-11 10:33:37 -0700122class PixelConverter<kBGRA_8888_SkColorType, gammaType> {
herb15332a82016-05-12 11:37:00 -0700123public:
124 using Element = uint32_t;
herb00dd4532016-07-11 10:33:37 -0700125 PixelConverter(const SkPixmap& srcPixmap) { }
herb15332a82016-05-12 11:37:00 -0700126
herb00dd4532016-07-11 10:33:37 -0700127 Sk4f toSk4f(Element pixel) const {
128 return swizzle_rb(
129 gammaType == kSRGB_SkGammaType ? Sk4f_fromS32(pixel) : Sk4f_fromL32(pixel));
herb15332a82016-05-12 11:37:00 -0700130 }
131};
132
reeddabe5d32016-06-21 10:28:14 -0700133template <SkGammaType gammaType>
herb00dd4532016-07-11 10:33:37 -0700134class PixelConverter<kIndex_8_SkColorType, gammaType> {
herb15332a82016-05-12 11:37:00 -0700135public:
136 using Element = uint8_t;
jcgregorioda626aa2016-07-22 05:40:58 -0700137 PixelConverter(const SkPixmap& srcPixmap) {
herb15332a82016-05-12 11:37:00 -0700138 SkColorTable* skColorTable = srcPixmap.ctable();
139 SkASSERT(skColorTable != nullptr);
140
141 fColorTable = (Sk4f*)SkAlign16((intptr_t)fColorTableStorage.get());
jcgregorioda626aa2016-07-22 05:40:58 -0700142 for (int i = 0; i < skColorTable->count(); i++) {
mtklein53574b72016-07-20 05:23:31 -0700143 fColorTable[i] = pmcolor_to_rgba<gammaType>((*skColorTable)[i]);
herb15332a82016-05-12 11:37:00 -0700144 }
145 }
146
jcgregorioda626aa2016-07-22 05:40:58 -0700147 PixelConverter(const PixelConverter& strategy) {
herb15332a82016-05-12 11:37:00 -0700148 fColorTable = (Sk4f*)SkAlign16((intptr_t)fColorTableStorage.get());
jcgregorioda626aa2016-07-22 05:40:58 -0700149 // TODO: figure out the count.
150 for (int i = 0; i < 256; i++) {
herb15332a82016-05-12 11:37:00 -0700151 fColorTable[i] = strategy.fColorTable[i];
152 }
153 }
154
herb00dd4532016-07-11 10:33:37 -0700155 Sk4f toSk4f(Element index) const {
156 return fColorTable[index];
herb15332a82016-05-12 11:37:00 -0700157 }
158
159private:
160 static const size_t kColorTableSize = sizeof(Sk4f[256]) + 12;
jcgregorioda626aa2016-07-22 05:40:58 -0700161
162 SkAutoMalloc fColorTableStorage{kColorTableSize};
163 Sk4f* fColorTable;
herb15332a82016-05-12 11:37:00 -0700164};
165
reeddabe5d32016-06-21 10:28:14 -0700166template <SkGammaType gammaType>
herb00dd4532016-07-11 10:33:37 -0700167class PixelConverter<kGray_8_SkColorType, gammaType> {
herb670f01f2016-05-13 10:04:46 -0700168public:
169 using Element = uint8_t;
herb00dd4532016-07-11 10:33:37 -0700170 PixelConverter(const SkPixmap& srcPixmap) { }
herb670f01f2016-05-13 10:04:46 -0700171
herb00dd4532016-07-11 10:33:37 -0700172 Sk4f toSk4f(Element pixel) const {
mtklein0c902472016-07-20 18:10:07 -0700173 float gray = (gammaType == kSRGB_SkGammaType)
174 ? sk_linear_from_srgb[pixel]
175 : pixel * (1/255.0f);
176 return {gray, gray, gray, 1.0f};
herb670f01f2016-05-13 10:04:46 -0700177 }
178};
179
herb15332a82016-05-12 11:37:00 -0700180template <>
herb00dd4532016-07-11 10:33:37 -0700181class PixelConverter<kRGBA_F16_SkColorType, kLinear_SkGammaType> {
herb15332a82016-05-12 11:37:00 -0700182public:
183 using Element = uint64_t;
herb00dd4532016-07-11 10:33:37 -0700184 PixelConverter(const SkPixmap& srcPixmap) { }
herb15332a82016-05-12 11:37:00 -0700185
herb00dd4532016-07-11 10:33:37 -0700186 Sk4f toSk4f(const Element pixel) const {
mtklein58e389b2016-07-15 07:00:11 -0700187 return SkHalfToFloat_finite(pixel);
herb15332a82016-05-12 11:37:00 -0700188 }
189};
190
herb00dd4532016-07-11 10:33:37 -0700191class PixelAccessorShim {
192public:
193 explicit PixelAccessorShim(SkLinearBitmapPipeline::PixelAccessorInterface* accessor)
194 : fPixelAccessor(accessor) { }
195
196 void SK_VECTORCALL getFewPixels(
jcgregorioda626aa2016-07-22 05:40:58 -0700197 int n, Sk4s xs, Sk4s ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) const {
herb00dd4532016-07-11 10:33:37 -0700198 fPixelAccessor->getFewPixels(n, xs, ys, px0, px1, px2);
199 }
200
201 void SK_VECTORCALL get4Pixels(
jcgregorioda626aa2016-07-22 05:40:58 -0700202 Sk4s xs, Sk4s ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const {
herb00dd4532016-07-11 10:33:37 -0700203 fPixelAccessor->get4Pixels(xs, ys, px0, px1, px2, px3);
204 }
205
206 void get4Pixels(
207 const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const {
208 fPixelAccessor->get4Pixels(src, index, px0, px1, px2, px3);
209 };
210
211 Sk4f getPixelFromRow(const void* row, int index) const {
212 return fPixelAccessor->getPixelFromRow(row, index);
213 }
214
215 Sk4f getPixelAt(int index) const {
216 return fPixelAccessor->getPixelAt(index);
217 }
218
219 const void* row(int y) const {
220 return fPixelAccessor->row(y);
221 }
222
223private:
224 SkLinearBitmapPipeline::PixelAccessorInterface* const fPixelAccessor;
225};
226
herb15332a82016-05-12 11:37:00 -0700227////////////////////////////////////////////////////////////////////////////////////////////////////
228// PixelAccessor handles all the same plumbing for all the PixelGetters.
reeddabe5d32016-06-21 10:28:14 -0700229template <SkColorType colorType, SkGammaType gammaType>
herb00dd4532016-07-11 10:33:37 -0700230class PixelAccessor final : public SkLinearBitmapPipeline::PixelAccessorInterface {
231 using Element = typename PixelConverter<colorType, gammaType>::Element;
herb15332a82016-05-12 11:37:00 -0700232public:
herb670f01f2016-05-13 10:04:46 -0700233 template <typename... Args>
234 PixelAccessor(const SkPixmap& srcPixmap, Args&&... args)
herb15332a82016-05-12 11:37:00 -0700235 : fSrc{static_cast<const Element*>(srcPixmap.addr())}
236 , fWidth{srcPixmap.rowBytesAsPixels()}
herb00dd4532016-07-11 10:33:37 -0700237 , fConverter{srcPixmap, std::move<Args>(args)...} { }
herb15332a82016-05-12 11:37:00 -0700238
herb00dd4532016-07-11 10:33:37 -0700239 void SK_VECTORCALL getFewPixels (
jcgregorioda626aa2016-07-22 05:40:58 -0700240 int n, Sk4s xs, Sk4s ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) const override {
241 Sk4i XIs = SkNx_cast<int, SkScalar>(xs);
242 Sk4i YIs = SkNx_cast<int, SkScalar>(ys);
243 Sk4i bufferLoc = YIs * fWidth + XIs;
herb15332a82016-05-12 11:37:00 -0700244 switch (n) {
245 case 3:
246 *px2 = this->getPixelAt(bufferLoc[2]);
247 case 2:
248 *px1 = this->getPixelAt(bufferLoc[1]);
249 case 1:
250 *px0 = this->getPixelAt(bufferLoc[0]);
251 default:
252 break;
253 }
254 }
255
herb00dd4532016-07-11 10:33:37 -0700256 void SK_VECTORCALL get4Pixels(
jcgregorioda626aa2016-07-22 05:40:58 -0700257 Sk4s xs, Sk4s ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const override {
258 Sk4i XIs = SkNx_cast<int, SkScalar>(xs);
259 Sk4i YIs = SkNx_cast<int, SkScalar>(ys);
260 Sk4i bufferLoc = YIs * fWidth + XIs;
herb15332a82016-05-12 11:37:00 -0700261 *px0 = this->getPixelAt(bufferLoc[0]);
262 *px1 = this->getPixelAt(bufferLoc[1]);
263 *px2 = this->getPixelAt(bufferLoc[2]);
264 *px3 = this->getPixelAt(bufferLoc[3]);
265 }
266
herb00dd4532016-07-11 10:33:37 -0700267 void get4Pixels(
268 const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const override {
herb15332a82016-05-12 11:37:00 -0700269 *px0 = this->getPixelFromRow(src, index + 0);
270 *px1 = this->getPixelFromRow(src, index + 1);
271 *px2 = this->getPixelFromRow(src, index + 2);
272 *px3 = this->getPixelFromRow(src, index + 3);
273 }
274
herb00dd4532016-07-11 10:33:37 -0700275 Sk4f getPixelFromRow(const void* row, int index) const override {
herb15332a82016-05-12 11:37:00 -0700276 const Element* src = static_cast<const Element*>(row);
herb00dd4532016-07-11 10:33:37 -0700277 return fConverter.toSk4f(src[index]);
herb15332a82016-05-12 11:37:00 -0700278 }
279
herb00dd4532016-07-11 10:33:37 -0700280 Sk4f getPixelAt(int index) const override {
herb15332a82016-05-12 11:37:00 -0700281 return this->getPixelFromRow(fSrc, index);
282 }
283
herb00dd4532016-07-11 10:33:37 -0700284 const void* row(int y) const override { return fSrc + y * fWidth; }
herb15332a82016-05-12 11:37:00 -0700285
286private:
287 const Element* const fSrc;
herb00dd4532016-07-11 10:33:37 -0700288 const int fWidth;
289 PixelConverter<colorType, gammaType> fConverter;
herb15332a82016-05-12 11:37:00 -0700290};
291
herb7ccbc1a2016-06-09 09:05:00 -0700292// We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
293// We'll never re-use pixels, but we can at least load contiguous pixels.
294template <typename Next, typename Strategy>
295static void src_strategy_blend(Span span, Next* next, Strategy* strategy) {
296 SkPoint start;
297 SkScalar length;
298 int count;
299 std::tie(start, length, count) = span;
300 int ix = SkScalarFloorToInt(X(start));
301 const void* row = strategy->row((int)std::floor(Y(start)));
302 if (length > 0) {
303 while (count >= 4) {
304 Sk4f px0, px1, px2, px3;
305 strategy->get4Pixels(row, ix, &px0, &px1, &px2, &px3);
306 next->blend4Pixels(px0, px1, px2, px3);
307 ix += 4;
308 count -= 4;
309 }
310
311 while (count > 0) {
312 next->blendPixel(strategy->getPixelFromRow(row, ix));
313 ix += 1;
314 count -= 1;
315 }
316 } else {
317 while (count >= 4) {
318 Sk4f px0, px1, px2, px3;
319 strategy->get4Pixels(row, ix - 3, &px3, &px2, &px1, &px0);
320 next->blend4Pixels(px0, px1, px2, px3);
321 ix -= 4;
322 count -= 4;
323 }
324
325 while (count > 0) {
326 next->blendPixel(strategy->getPixelFromRow(row, ix));
327 ix -= 1;
328 count -= 1;
329 }
330 }
331}
332
333// NearestNeighborSampler - use nearest neighbor filtering to create runs of destination pixels.
herb00dd4532016-07-11 10:33:37 -0700334template<typename Accessor, typename Next>
herb7ccbc1a2016-06-09 09:05:00 -0700335class NearestNeighborSampler : public SkLinearBitmapPipeline::SampleProcessorInterface {
herb6eff52a2016-03-23 09:00:33 -0700336public:
337 template<typename... Args>
herb7ccbc1a2016-06-09 09:05:00 -0700338 NearestNeighborSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next, Args&& ... args)
herb00dd4532016-07-11 10:33:37 -0700339 : fNext{next}, fAccessor{std::forward<Args>(args)...} { }
herb6eff52a2016-03-23 09:00:33 -0700340
herb7ccbc1a2016-06-09 09:05:00 -0700341 NearestNeighborSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next,
342 const NearestNeighborSampler& sampler)
herb00dd4532016-07-11 10:33:37 -0700343 : fNext{next}, fAccessor{sampler.fAccessor} { }
herb9e0efe52016-04-08 13:25:28 -0700344
mtkleine5fb9c82016-07-07 08:12:09 -0700345 void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
herb6eff52a2016-03-23 09:00:33 -0700346 SkASSERT(0 < n && n < 4);
347 Sk4f px0, px1, px2;
jcgregorioda626aa2016-07-22 05:40:58 -0700348 fAccessor.getFewPixels(n, xs, ys, &px0, &px1, &px2);
herb9e0efe52016-04-08 13:25:28 -0700349 if (n >= 1) fNext->blendPixel(px0);
350 if (n >= 2) fNext->blendPixel(px1);
351 if (n >= 3) fNext->blendPixel(px2);
herb6eff52a2016-03-23 09:00:33 -0700352 }
353
mtkleine5fb9c82016-07-07 08:12:09 -0700354 void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
herb6eff52a2016-03-23 09:00:33 -0700355 Sk4f px0, px1, px2, px3;
jcgregorioda626aa2016-07-22 05:40:58 -0700356 fAccessor.get4Pixels(xs, ys, &px0, &px1, &px2, &px3);
herb9e0efe52016-04-08 13:25:28 -0700357 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700358 }
359
herb7ccbc1a2016-06-09 09:05:00 -0700360 void pointSpan(Span span) override {
herb6eff52a2016-03-23 09:00:33 -0700361 SkASSERT(!span.isEmpty());
362 SkPoint start;
363 SkScalar length;
364 int count;
365 std::tie(start, length, count) = span;
366 SkScalar absLength = SkScalarAbs(length);
367 if (absLength < (count - 1)) {
herb7ccbc1a2016-06-09 09:05:00 -0700368 this->spanSlowRate(span);
herb6eff52a2016-03-23 09:00:33 -0700369 } else if (absLength == (count - 1)) {
herb00dd4532016-07-11 10:33:37 -0700370 src_strategy_blend(span, fNext, &fAccessor);
herb6eff52a2016-03-23 09:00:33 -0700371 } else {
herb7ccbc1a2016-06-09 09:05:00 -0700372 this->spanFastRate(span);
herb6eff52a2016-03-23 09:00:33 -0700373 }
374 }
375
herb7ccbc1a2016-06-09 09:05:00 -0700376 void repeatSpan(Span span, int32_t repeatCount) override {
377 while (repeatCount > 0) {
378 this->pointSpan(span);
379 repeatCount--;
herb6eff52a2016-03-23 09:00:33 -0700380 }
381 }
382
jcgregorioda626aa2016-07-22 05:40:58 -0700383 void SK_VECTORCALL bilerpEdge(Sk4s xs, Sk4s ys) override {
384 SkFAIL("Using nearest neighbor sampler, but calling a bilerpEdge.");
385 }
386
387 void bilerpSpan(Span span, SkScalar y) override {
388 SkFAIL("Using nearest neighbor sampler, but calling a bilerpSpan.");
389 }
390
herb6eff52a2016-03-23 09:00:33 -0700391private:
392 // When moving through source space more slowly than dst space (zoomed in),
393 // we'll be sampling from the same source pixel more than once.
herb7ccbc1a2016-06-09 09:05:00 -0700394 void spanSlowRate(Span span) {
jcgregorioda626aa2016-07-22 05:40:58 -0700395 SkPoint start;
396 SkScalar length;
397 int count;
herb6eff52a2016-03-23 09:00:33 -0700398 std::tie(start, length, count) = span;
399 SkScalar x = X(start);
400 SkFixed fx = SkScalarToFixed(x);
401 SkScalar dx = length / (count - 1);
402 SkFixed fdx = SkScalarToFixed(dx);
403
herb00dd4532016-07-11 10:33:37 -0700404 const void* row = fAccessor.row((int)std::floor(Y(start)));
herb6eff52a2016-03-23 09:00:33 -0700405 Next* next = fNext;
406
407 int ix = SkFixedFloorToInt(fx);
408 int prevIX = ix;
herb00dd4532016-07-11 10:33:37 -0700409 Sk4f fpixel = fAccessor.getPixelFromRow(row, ix);
herb6eff52a2016-03-23 09:00:33 -0700410
411 // When dx is less than one, each pixel is used more than once. Using the fixed point fx
412 // allows the code to quickly check that the same pixel is being used. The code uses this
413 // same pixel check to do the sRGB and normalization only once.
414 auto getNextPixel = [&]() {
415 if (ix != prevIX) {
herb00dd4532016-07-11 10:33:37 -0700416 fpixel = fAccessor.getPixelFromRow(row, ix);
herb6eff52a2016-03-23 09:00:33 -0700417 prevIX = ix;
418 }
419 fx += fdx;
420 ix = SkFixedFloorToInt(fx);
421 return fpixel;
422 };
423
424 while (count >= 4) {
425 Sk4f px0 = getNextPixel();
426 Sk4f px1 = getNextPixel();
427 Sk4f px2 = getNextPixel();
428 Sk4f px3 = getNextPixel();
herb9e0efe52016-04-08 13:25:28 -0700429 next->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700430 count -= 4;
431 }
432 while (count > 0) {
herb9e0efe52016-04-08 13:25:28 -0700433 next->blendPixel(getNextPixel());
herb6eff52a2016-03-23 09:00:33 -0700434 count -= 1;
435 }
436 }
437
438 // We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
439 // We'll never re-use pixels, but we can at least load contiguous pixels.
herb7ccbc1a2016-06-09 09:05:00 -0700440 void spanUnitRate(Span span) {
herb00dd4532016-07-11 10:33:37 -0700441 src_strategy_blend(span, fNext, &fAccessor);
herb6eff52a2016-03-23 09:00:33 -0700442 }
443
444 // We're moving through source space faster than dst (zoomed out),
445 // so we'll never reuse a source pixel or be able to do contiguous loads.
herb7ccbc1a2016-06-09 09:05:00 -0700446 void spanFastRate(Span span) {
447 span_fallback(span, this);
herb6eff52a2016-03-23 09:00:33 -0700448 }
449
herb00dd4532016-07-11 10:33:37 -0700450 Next* const fNext;
451 Accessor fAccessor;
herb7ccbc1a2016-06-09 09:05:00 -0700452};
453
herb7df9e4a2016-06-10 13:01:27 -0700454// -- BilerpSampler --------------------------------------------------------------------------------
herb7ccbc1a2016-06-09 09:05:00 -0700455// BilerpSampler - use a bilerp filter to create runs of destination pixels.
herb00dd4532016-07-11 10:33:37 -0700456template<typename Accessor, typename Next>
herb7ccbc1a2016-06-09 09:05:00 -0700457class BilerpSampler : public SkLinearBitmapPipeline::SampleProcessorInterface {
458public:
459 template<typename... Args>
jcgregorioda626aa2016-07-22 05:40:58 -0700460 BilerpSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next, Args&& ... args)
461 : fNext{next}, fAccessor{std::forward<Args>(args)...} { }
herb7ccbc1a2016-06-09 09:05:00 -0700462
463 BilerpSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next,
464 const BilerpSampler& sampler)
jcgregorioda626aa2016-07-22 05:40:58 -0700465 : fNext{next}, fAccessor{sampler.fAccessor} { }
466
467 Sk4f bilerpNonEdgePixel(SkScalar x, SkScalar y) {
468 Sk4f px00, px10, px01, px11;
469
470 // bilerp4() expects xs, ys are the top-lefts of the 2x2 kernel.
471 Sk4f xs = Sk4f{x} - 0.5f;
472 Sk4f ys = Sk4f{y} - 0.5f;
473 Sk4f sampleXs = xs + Sk4f{0.0f, 1.0f, 0.0f, 1.0f};
474 Sk4f sampleYs = ys + Sk4f{0.0f, 0.0f, 1.0f, 1.0f};
475 fAccessor.get4Pixels(sampleXs, sampleYs, &px00, &px10, &px01, &px11);
476 return bilerp4(xs, ys, px00, px10, px01, px11);
477 }
herb7ccbc1a2016-06-09 09:05:00 -0700478
mtkleine5fb9c82016-07-07 08:12:09 -0700479 void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
herb7ccbc1a2016-06-09 09:05:00 -0700480 SkASSERT(0 < n && n < 4);
481 auto bilerpPixel = [&](int index) {
jcgregorioda626aa2016-07-22 05:40:58 -0700482 return this->bilerpNonEdgePixel(xs[index], ys[index]);
herb7ccbc1a2016-06-09 09:05:00 -0700483 };
484
485 if (n >= 1) fNext->blendPixel(bilerpPixel(0));
486 if (n >= 2) fNext->blendPixel(bilerpPixel(1));
487 if (n >= 3) fNext->blendPixel(bilerpPixel(2));
488 }
489
mtkleine5fb9c82016-07-07 08:12:09 -0700490 void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
herb7ccbc1a2016-06-09 09:05:00 -0700491 auto bilerpPixel = [&](int index) {
jcgregorioda626aa2016-07-22 05:40:58 -0700492 return this->bilerpNonEdgePixel(xs[index], ys[index]);
herb7ccbc1a2016-06-09 09:05:00 -0700493 };
494 fNext->blend4Pixels(bilerpPixel(0), bilerpPixel(1), bilerpPixel(2), bilerpPixel(3));
495 }
496
497 void pointSpan(Span span) override {
jcgregorioda626aa2016-07-22 05:40:58 -0700498 this->bilerpSpan(span, span.startY());
herb7ccbc1a2016-06-09 09:05:00 -0700499 }
500
501 void repeatSpan(Span span, int32_t repeatCount) override {
502 while (repeatCount > 0) {
503 this->pointSpan(span);
504 repeatCount--;
505 }
506 }
507
jcgregorioda626aa2016-07-22 05:40:58 -0700508 void SK_VECTORCALL bilerpEdge(Sk4s sampleXs, Sk4s sampleYs) override {
herb8602ede2016-07-21 13:22:04 -0700509 Sk4f px00, px10, px01, px11;
jcgregorioda626aa2016-07-22 05:40:58 -0700510 Sk4f xs = Sk4f{sampleXs[0]};
511 Sk4f ys = Sk4f{sampleYs[0]};
512 fAccessor.get4Pixels(sampleXs, sampleYs, &px00, &px10, &px01, &px11);
513 Sk4f pixel = bilerp4(xs, ys, px00, px10, px01, px11);
514 fNext->blendPixel(pixel);
herb8602ede2016-07-21 13:22:04 -0700515 }
516
jcgregorioda626aa2016-07-22 05:40:58 -0700517 void bilerpSpan(Span span, SkScalar y) override {
518 SkASSERT(!span.isEmpty());
519 SkPoint start;
520 SkScalar length;
521 int count;
herb6eff52a2016-03-23 09:00:33 -0700522 std::tie(start, length, count) = span;
jcgregorioda626aa2016-07-22 05:40:58 -0700523 SkScalar absLength = SkScalarAbs(length);
524 if (absLength == 0.0f) {
525 this->spanZeroRate(span, y);
526 } else if (absLength < (count - 1)) {
527 this->spanSlowRate(span, y);
528 } else if (absLength == (count - 1)) {
529 if (std::fmod(span.startX() - 0.5f, 1.0f) == 0.0f) {
530 if (std::fmod(span.startY() - 0.5f, 1.0f) == 0.0f) {
531 src_strategy_blend(span, fNext, &fAccessor);
532 } else {
533 this->spanUnitRateAlignedX(span, y);
534 }
535 } else {
536 this->spanUnitRate(span, y);
537 }
538 } else {
539 this->spanFastRate(span, y);
540 }
541 }
herb6eff52a2016-03-23 09:00:33 -0700542
jcgregorioda626aa2016-07-22 05:40:58 -0700543private:
544 void spanZeroRate(Span span, SkScalar y1) {
545 SkScalar y0 = span.startY() - 0.5f;
546 y1 += 0.5f;
547 int iy0 = SkScalarFloorToInt(y0);
548 SkScalar filterY1 = y0 - iy0;
549 SkScalar filterY0 = 1.0f - filterY1;
550 int iy1 = SkScalarFloorToInt(y1);
551 int ix = SkScalarFloorToInt(span.startX());
552 Sk4f pixelY0 = fAccessor.getPixelFromRow(fAccessor.row(iy0), ix);
553 Sk4f pixelY1 = fAccessor.getPixelFromRow(fAccessor.row(iy1), ix);
554 Sk4f filterPixel = pixelY0 * filterY0 + pixelY1 * filterY1;
555 int count = span.count();
herb6eff52a2016-03-23 09:00:33 -0700556 while (count >= 4) {
jcgregorioda626aa2016-07-22 05:40:58 -0700557 fNext->blend4Pixels(filterPixel, filterPixel, filterPixel, filterPixel);
herb6eff52a2016-03-23 09:00:33 -0700558 count -= 4;
559 }
herb6eff52a2016-03-23 09:00:33 -0700560 while (count > 0) {
jcgregorioda626aa2016-07-22 05:40:58 -0700561 fNext->blendPixel(filterPixel);
herb6eff52a2016-03-23 09:00:33 -0700562 count -= 1;
563 }
564 }
565
jcgregorioda626aa2016-07-22 05:40:58 -0700566 // When moving through source space more slowly than dst space (zoomed in),
567 // we'll be sampling from the same source pixel more than once.
568 void spanSlowRate(Span span, SkScalar ry1) {
569 SkPoint start;
570 SkScalar length;
571 int count;
herb8602ede2016-07-21 13:22:04 -0700572 std::tie(start, length, count) = span;
jcgregorioda626aa2016-07-22 05:40:58 -0700573 SkFixed fx = SkScalarToFixed(X(start)-0.5f);
herb6eff52a2016-03-23 09:00:33 -0700574
jcgregorioda626aa2016-07-22 05:40:58 -0700575 SkFixed fdx = SkScalarToFixed(length / (count - 1));
herb8602ede2016-07-21 13:22:04 -0700576
jcgregorioda626aa2016-07-22 05:40:58 -0700577 Sk4f xAdjust;
578 if (fdx >= 0) {
579 xAdjust = Sk4f{-1.0f};
580 } else {
581 xAdjust = Sk4f{1.0f};
582 }
583 int ix = SkFixedFloorToInt(fx);
584 int ioldx = ix;
585 Sk4f x{SkFixedToScalar(fx) - ix};
586 Sk4f dx{SkFixedToScalar(fdx)};
587 SkScalar ry0 = Y(start) - 0.5f;
588 ry1 += 0.5f;
589 SkScalar yFloor = std::floor(ry0);
590 Sk4f y1 = Sk4f{ry0 - yFloor};
591 Sk4f y0 = Sk4f{1.0f} - y1;
592 const void* const row0 = fAccessor.row(SkScalarFloorToInt(ry0));
593 const void* const row1 = fAccessor.row(SkScalarFloorToInt(ry1));
594 Sk4f fpixel00 = y0 * fAccessor.getPixelFromRow(row0, ix);
595 Sk4f fpixel01 = y1 * fAccessor.getPixelFromRow(row1, ix);
596 Sk4f fpixel10 = y0 * fAccessor.getPixelFromRow(row0, ix + 1);
597 Sk4f fpixel11 = y1 * fAccessor.getPixelFromRow(row1, ix + 1);
598 auto getNextPixel = [&]() {
599 if (ix != ioldx) {
600 fpixel00 = fpixel10;
601 fpixel01 = fpixel11;
602 fpixel10 = y0 * fAccessor.getPixelFromRow(row0, ix + 1);
603 fpixel11 = y1 * fAccessor.getPixelFromRow(row1, ix + 1);
604 ioldx = ix;
605 x = x + xAdjust;
herb6eff52a2016-03-23 09:00:33 -0700606 }
herb6eff52a2016-03-23 09:00:33 -0700607
jcgregorioda626aa2016-07-22 05:40:58 -0700608 Sk4f x0, x1;
609 x0 = Sk4f{1.0f} - x;
610 x1 = x;
611 Sk4f fpixel = x0 * (fpixel00 + fpixel01) + x1 * (fpixel10 + fpixel11);
612 fx += fdx;
613 ix = SkFixedFloorToInt(fx);
614 x = x + dx;
615 return fpixel;
616 };
617
618 while (count >= 4) {
619 Sk4f fpixel0 = getNextPixel();
620 Sk4f fpixel1 = getNextPixel();
621 Sk4f fpixel2 = getNextPixel();
622 Sk4f fpixel3 = getNextPixel();
623
624 fNext->blend4Pixels(fpixel0, fpixel1, fpixel2, fpixel3);
625 count -= 4;
626 }
627
628 while (count > 0) {
629 fNext->blendPixel(getNextPixel());
630
631 count -= 1;
632 }
633 }
634
635 // We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
636 // We'll never re-use pixels, but we can at least load contiguous pixels.
637 void spanUnitRate(Span span, SkScalar y1) {
638 y1 += 0.5f;
639 SkScalar y0 = span.startY() - 0.5f;
640 int iy0 = SkScalarFloorToInt(y0);
641 SkScalar filterY1 = y0 - iy0;
642 SkScalar filterY0 = 1.0f - filterY1;
643 int iy1 = SkScalarFloorToInt(y1);
644 const void* rowY0 = fAccessor.row(iy0);
645 const void* rowY1 = fAccessor.row(iy1);
646 SkScalar x0 = span.startX() - 0.5f;
647 int ix0 = SkScalarFloorToInt(x0);
648 SkScalar filterX1 = x0 - ix0;
649 SkScalar filterX0 = 1.0f - filterX1;
650
651 auto getPixelY0 = [&]() {
652 Sk4f px = fAccessor.getPixelFromRow(rowY0, ix0);
653 return px * filterY0;
654 };
655
656 auto getPixelY1 = [&]() {
657 Sk4f px = fAccessor.getPixelFromRow(rowY1, ix0);
658 return px * filterY1;
659 };
660
661 auto get4PixelsY0 = [&](int ix, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
662 fAccessor.get4Pixels(rowY0, ix, px0, px1, px2, px3);
663 *px0 = *px0 * filterY0;
664 *px1 = *px1 * filterY0;
665 *px2 = *px2 * filterY0;
666 *px3 = *px3 * filterY0;
667 };
668
669 auto get4PixelsY1 = [&](int ix, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
670 fAccessor.get4Pixels(rowY1, ix, px0, px1, px2, px3);
671 *px0 = *px0 * filterY1;
672 *px1 = *px1 * filterY1;
673 *px2 = *px2 * filterY1;
674 *px3 = *px3 * filterY1;
675 };
676
677 auto lerp = [&](Sk4f& pixelX0, Sk4f& pixelX1) {
678 return pixelX0 * filterX0 + pixelX1 * filterX1;
679 };
680
681 // Mid making 4 unit rate.
682 Sk4f pxB = getPixelY0() + getPixelY1();
683 if (span.length() > 0) {
684 int count = span.count();
685 while (count >= 4) {
686 Sk4f px00, px10, px20, px30;
687 get4PixelsY0(ix0, &px00, &px10, &px20, &px30);
688 Sk4f px01, px11, px21, px31;
689 get4PixelsY1(ix0, &px01, &px11, &px21, &px31);
690 Sk4f pxS0 = px00 + px01;
691 Sk4f px0 = lerp(pxB, pxS0);
692 Sk4f pxS1 = px10 + px11;
693 Sk4f px1 = lerp(pxS0, pxS1);
694 Sk4f pxS2 = px20 + px21;
695 Sk4f px2 = lerp(pxS1, pxS2);
696 Sk4f pxS3 = px30 + px31;
697 Sk4f px3 = lerp(pxS2, pxS3);
698 pxB = pxS3;
699 fNext->blend4Pixels(px0, px1, px2, px3);
700 ix0 += 4;
701 count -= 4;
702 }
herb8602ede2016-07-21 13:22:04 -0700703 while (count > 0) {
jcgregorioda626aa2016-07-22 05:40:58 -0700704 Sk4f pixelY0 = fAccessor.getPixelFromRow(rowY0, ix0);
705 Sk4f pixelY1 = fAccessor.getPixelFromRow(rowY1, ix0);
706
707 fNext->blendPixel(lerp(pixelY0, pixelY1));
708 ix0 += 1;
herb6eff52a2016-03-23 09:00:33 -0700709 count -= 1;
710 }
711 } else {
jcgregorioda626aa2016-07-22 05:40:58 -0700712 int count = span.count();
herb6eff52a2016-03-23 09:00:33 -0700713 while (count >= 4) {
jcgregorioda626aa2016-07-22 05:40:58 -0700714 Sk4f px00, px10, px20, px30;
715 get4PixelsY0(ix0 - 3, &px00, &px10, &px20, &px30);
716 Sk4f px01, px11, px21, px31;
717 get4PixelsY1(ix0 - 3, &px01, &px11, &px21, &px31);
718 Sk4f pxS3 = px30 + px31;
719 Sk4f px0 = lerp(pxS3, pxB);
720 Sk4f pxS2 = px20 + px21;
721 Sk4f px1 = lerp(pxS2, pxS3);
722 Sk4f pxS1 = px10 + px11;
723 Sk4f px2 = lerp(pxS1, pxS2);
724 Sk4f pxS0 = px00 + px01;
725 Sk4f px3 = lerp(pxS0, pxS1);
726 pxB = pxS0;
herb9e0efe52016-04-08 13:25:28 -0700727 fNext->blend4Pixels(px0, px1, px2, px3);
jcgregorioda626aa2016-07-22 05:40:58 -0700728 ix0 -= 4;
herb6eff52a2016-03-23 09:00:33 -0700729 count -= 4;
730 }
herb8602ede2016-07-21 13:22:04 -0700731 while (count > 0) {
jcgregorioda626aa2016-07-22 05:40:58 -0700732 Sk4f pixelY0 = fAccessor.getPixelFromRow(rowY0, ix0);
733 Sk4f pixelY1 = fAccessor.getPixelFromRow(rowY1, ix0);
734
735 fNext->blendPixel(lerp(pixelY0, pixelY1));
736 ix0 -= 1;
herb6eff52a2016-03-23 09:00:33 -0700737 count -= 1;
738 }
739 }
740 }
741
jcgregorioda626aa2016-07-22 05:40:58 -0700742 void spanUnitRateAlignedX(Span span, SkScalar y1) {
743 SkScalar y0 = span.startY() - 0.5f;
744 y1 += 0.5f;
745 int iy0 = SkScalarFloorToInt(y0);
746 SkScalar filterY1 = y0 - iy0;
747 SkScalar filterY0 = 1.0f - filterY1;
748 int iy1 = SkScalarFloorToInt(y1);
749 int ix = SkScalarFloorToInt(span.startX());
750 const void* rowY0 = fAccessor.row(iy0);
751 const void* rowY1 = fAccessor.row(iy1);
752 auto lerp = [&](Sk4f* pixelY0, Sk4f* pixelY1) {
753 return *pixelY0 * filterY0 + *pixelY1 * filterY1;
herb6eff52a2016-03-23 09:00:33 -0700754 };
755
jcgregorioda626aa2016-07-22 05:40:58 -0700756 if (span.length() > 0) {
757 int count = span.count();
herb6eff52a2016-03-23 09:00:33 -0700758 while (count >= 4) {
jcgregorioda626aa2016-07-22 05:40:58 -0700759 Sk4f px00, px10, px20, px30;
760 fAccessor.get4Pixels(rowY0, ix, &px00, &px10, &px20, &px30);
761 Sk4f px01, px11, px21, px31;
762 fAccessor.get4Pixels(rowY1, ix, &px01, &px11, &px21, &px31);
763 fNext->blend4Pixels(
764 lerp(&px00, &px01), lerp(&px10, &px11), lerp(&px20, &px21), lerp(&px30, &px31));
765 ix += 4;
herb6eff52a2016-03-23 09:00:33 -0700766 count -= 4;
767 }
herb8602ede2016-07-21 13:22:04 -0700768 while (count > 0) {
jcgregorioda626aa2016-07-22 05:40:58 -0700769 Sk4f pixelY0 = fAccessor.getPixelFromRow(rowY0, ix);
770 Sk4f pixelY1 = fAccessor.getPixelFromRow(rowY1, ix);
herb8602ede2016-07-21 13:22:04 -0700771
jcgregorioda626aa2016-07-22 05:40:58 -0700772 fNext->blendPixel(lerp(&pixelY0, &pixelY1));
773 ix += 1;
herb6eff52a2016-03-23 09:00:33 -0700774 count -= 1;
775 }
776 } else {
jcgregorioda626aa2016-07-22 05:40:58 -0700777 int count = span.count();
herb6eff52a2016-03-23 09:00:33 -0700778 while (count >= 4) {
jcgregorioda626aa2016-07-22 05:40:58 -0700779 Sk4f px00, px10, px20, px30;
780 fAccessor.get4Pixels(rowY0, ix - 3, &px30, &px20, &px10, &px00);
781 Sk4f px01, px11, px21, px31;
782 fAccessor.get4Pixels(rowY1, ix - 3, &px31, &px21, &px11, &px01);
783 fNext->blend4Pixels(
784 lerp(&px00, &px01), lerp(&px10, &px11), lerp(&px20, &px21), lerp(&px30, &px31));
785 ix -= 4;
herb6eff52a2016-03-23 09:00:33 -0700786 count -= 4;
787 }
herb8602ede2016-07-21 13:22:04 -0700788 while (count > 0) {
jcgregorioda626aa2016-07-22 05:40:58 -0700789 Sk4f pixelY0 = fAccessor.getPixelFromRow(rowY0, ix);
790 Sk4f pixelY1 = fAccessor.getPixelFromRow(rowY1, ix);
herb8602ede2016-07-21 13:22:04 -0700791
jcgregorioda626aa2016-07-22 05:40:58 -0700792 fNext->blendPixel(lerp(&pixelY0, &pixelY1));
793 ix -= 1;
herb6eff52a2016-03-23 09:00:33 -0700794 count -= 1;
795 }
796 }
797 }
798
799 // We're moving through source space faster than dst (zoomed out),
800 // so we'll never reuse a source pixel or be able to do contiguous loads.
jcgregorioda626aa2016-07-22 05:40:58 -0700801 void spanFastRate(Span span, SkScalar y1) {
802 SkPoint start;
803 SkScalar length;
804 int count;
herb6eff52a2016-03-23 09:00:33 -0700805 std::tie(start, length, count) = span;
806 SkScalar x = X(start);
807 SkScalar y = Y(start);
herb7ccbc1a2016-06-09 09:05:00 -0700808
jcgregorioda626aa2016-07-22 05:40:58 -0700809 // In this sampler, it is assumed that if span.StartY() and y1 are the same then both
810 // y-lines are on the same tile.
811 if (y == y1) {
812 // Both y-lines are on the same tile.
813 span_fallback(span, this);
814 } else {
815 // The y-lines are on different tiles.
816 SkScalar dx = length / (count - 1);
817 Sk4f ys = {y - 0.5f, y - 0.5f, y1 + 0.5f, y1 + 0.5f};
818 while (count > 0) {
819 Sk4f xs = Sk4f{-0.5f, 0.5f, -0.5f, 0.5f} + Sk4f{x};
820 this->bilerpEdge(xs, ys);
821 x += dx;
822 count -= 1;
823 }
herb6eff52a2016-03-23 09:00:33 -0700824 }
825 }
826
jcgregorioda626aa2016-07-22 05:40:58 -0700827 Next* const fNext;
828 Accessor fAccessor;
herb6eff52a2016-03-23 09:00:33 -0700829};
830
herb6eff52a2016-03-23 09:00:33 -0700831} // namespace
832
833#endif // SkLinearBitmapPipeline_sampler_DEFINED