blob: 5f9948c644c7923a6150a4471f7db801994db90b [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,
herb86a6c6d2016-07-22 14:06:27 -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;
herb86a6c6d2016-07-22 14:06:27 -0700137 PixelConverter(const SkPixmap& srcPixmap)
138 : fColorTableSize(srcPixmap.ctable()->count()){
herb15332a82016-05-12 11:37:00 -0700139 SkColorTable* skColorTable = srcPixmap.ctable();
140 SkASSERT(skColorTable != nullptr);
141
142 fColorTable = (Sk4f*)SkAlign16((intptr_t)fColorTableStorage.get());
herb86a6c6d2016-07-22 14:06:27 -0700143 for (int i = 0; i < fColorTableSize; i++) {
mtklein53574b72016-07-20 05:23:31 -0700144 fColorTable[i] = pmcolor_to_rgba<gammaType>((*skColorTable)[i]);
herb15332a82016-05-12 11:37:00 -0700145 }
146 }
147
herb86a6c6d2016-07-22 14:06:27 -0700148 PixelConverter(const PixelConverter& strategy)
149 : fColorTableSize{strategy.fColorTableSize}{
herb15332a82016-05-12 11:37:00 -0700150 fColorTable = (Sk4f*)SkAlign16((intptr_t)fColorTableStorage.get());
herb86a6c6d2016-07-22 14:06:27 -0700151 for (int i = 0; i < fColorTableSize; i++) {
herb15332a82016-05-12 11:37:00 -0700152 fColorTable[i] = strategy.fColorTable[i];
153 }
154 }
155
herb00dd4532016-07-11 10:33:37 -0700156 Sk4f toSk4f(Element index) const {
157 return fColorTable[index];
herb15332a82016-05-12 11:37:00 -0700158 }
159
160private:
161 static const size_t kColorTableSize = sizeof(Sk4f[256]) + 12;
herb86a6c6d2016-07-22 14:06:27 -0700162 const int fColorTableSize;
163 SkAutoMalloc fColorTableStorage{kColorTableSize};
164 Sk4f* fColorTable;
herb15332a82016-05-12 11:37:00 -0700165};
166
reeddabe5d32016-06-21 10:28:14 -0700167template <SkGammaType gammaType>
herb00dd4532016-07-11 10:33:37 -0700168class PixelConverter<kGray_8_SkColorType, gammaType> {
herb670f01f2016-05-13 10:04:46 -0700169public:
170 using Element = uint8_t;
herb00dd4532016-07-11 10:33:37 -0700171 PixelConverter(const SkPixmap& srcPixmap) { }
herb670f01f2016-05-13 10:04:46 -0700172
herb00dd4532016-07-11 10:33:37 -0700173 Sk4f toSk4f(Element pixel) const {
mtklein0c902472016-07-20 18:10:07 -0700174 float gray = (gammaType == kSRGB_SkGammaType)
175 ? sk_linear_from_srgb[pixel]
176 : pixel * (1/255.0f);
177 return {gray, gray, gray, 1.0f};
herb670f01f2016-05-13 10:04:46 -0700178 }
179};
180
herb15332a82016-05-12 11:37:00 -0700181template <>
herb00dd4532016-07-11 10:33:37 -0700182class PixelConverter<kRGBA_F16_SkColorType, kLinear_SkGammaType> {
herb15332a82016-05-12 11:37:00 -0700183public:
184 using Element = uint64_t;
herb00dd4532016-07-11 10:33:37 -0700185 PixelConverter(const SkPixmap& srcPixmap) { }
herb15332a82016-05-12 11:37:00 -0700186
herb00dd4532016-07-11 10:33:37 -0700187 Sk4f toSk4f(const Element pixel) const {
mtklein8ae991e2016-08-22 13:20:18 -0700188 return SkHalfToFloat_finite_ftz(pixel);
herb15332a82016-05-12 11:37:00 -0700189 }
190};
191
herb00dd4532016-07-11 10:33:37 -0700192class PixelAccessorShim {
193public:
194 explicit PixelAccessorShim(SkLinearBitmapPipeline::PixelAccessorInterface* accessor)
195 : fPixelAccessor(accessor) { }
196
197 void SK_VECTORCALL getFewPixels(
herb86a6c6d2016-07-22 14:06:27 -0700198 int n, Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) const {
herb00dd4532016-07-11 10:33:37 -0700199 fPixelAccessor->getFewPixels(n, xs, ys, px0, px1, px2);
200 }
201
202 void SK_VECTORCALL get4Pixels(
herb86a6c6d2016-07-22 14:06:27 -0700203 Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const {
herb00dd4532016-07-11 10:33:37 -0700204 fPixelAccessor->get4Pixels(xs, ys, px0, px1, px2, px3);
205 }
206
207 void get4Pixels(
208 const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const {
209 fPixelAccessor->get4Pixels(src, index, px0, px1, px2, px3);
Mike Kleinfc6c37b2016-09-27 09:34:10 -0400210 }
herb00dd4532016-07-11 10:33:37 -0700211
212 Sk4f getPixelFromRow(const void* row, int index) const {
213 return fPixelAccessor->getPixelFromRow(row, index);
214 }
215
216 Sk4f getPixelAt(int index) const {
217 return fPixelAccessor->getPixelAt(index);
218 }
219
220 const void* row(int y) const {
221 return fPixelAccessor->row(y);
222 }
223
224private:
225 SkLinearBitmapPipeline::PixelAccessorInterface* const fPixelAccessor;
226};
227
herb15332a82016-05-12 11:37:00 -0700228////////////////////////////////////////////////////////////////////////////////////////////////////
229// PixelAccessor handles all the same plumbing for all the PixelGetters.
reeddabe5d32016-06-21 10:28:14 -0700230template <SkColorType colorType, SkGammaType gammaType>
herb00dd4532016-07-11 10:33:37 -0700231class PixelAccessor final : public SkLinearBitmapPipeline::PixelAccessorInterface {
232 using Element = typename PixelConverter<colorType, gammaType>::Element;
herb15332a82016-05-12 11:37:00 -0700233public:
herb670f01f2016-05-13 10:04:46 -0700234 template <typename... Args>
235 PixelAccessor(const SkPixmap& srcPixmap, Args&&... args)
herb15332a82016-05-12 11:37:00 -0700236 : fSrc{static_cast<const Element*>(srcPixmap.addr())}
237 , fWidth{srcPixmap.rowBytesAsPixels()}
herb00dd4532016-07-11 10:33:37 -0700238 , fConverter{srcPixmap, std::move<Args>(args)...} { }
herb15332a82016-05-12 11:37:00 -0700239
herb00dd4532016-07-11 10:33:37 -0700240 void SK_VECTORCALL getFewPixels (
herb86a6c6d2016-07-22 14:06:27 -0700241 int n, Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) const override {
242 Sk4i bufferLoc = ys * fWidth + xs;
herb15332a82016-05-12 11:37:00 -0700243 switch (n) {
244 case 3:
245 *px2 = this->getPixelAt(bufferLoc[2]);
246 case 2:
247 *px1 = this->getPixelAt(bufferLoc[1]);
248 case 1:
249 *px0 = this->getPixelAt(bufferLoc[0]);
250 default:
251 break;
252 }
253 }
254
herb00dd4532016-07-11 10:33:37 -0700255 void SK_VECTORCALL get4Pixels(
herb86a6c6d2016-07-22 14:06:27 -0700256 Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const override {
257 Sk4i bufferLoc = ys * fWidth + xs;
herb15332a82016-05-12 11:37:00 -0700258 *px0 = this->getPixelAt(bufferLoc[0]);
259 *px1 = this->getPixelAt(bufferLoc[1]);
260 *px2 = this->getPixelAt(bufferLoc[2]);
261 *px3 = this->getPixelAt(bufferLoc[3]);
262 }
263
herb00dd4532016-07-11 10:33:37 -0700264 void get4Pixels(
265 const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const override {
herb15332a82016-05-12 11:37:00 -0700266 *px0 = this->getPixelFromRow(src, index + 0);
267 *px1 = this->getPixelFromRow(src, index + 1);
268 *px2 = this->getPixelFromRow(src, index + 2);
269 *px3 = this->getPixelFromRow(src, index + 3);
270 }
271
herb00dd4532016-07-11 10:33:37 -0700272 Sk4f getPixelFromRow(const void* row, int index) const override {
herb15332a82016-05-12 11:37:00 -0700273 const Element* src = static_cast<const Element*>(row);
herb00dd4532016-07-11 10:33:37 -0700274 return fConverter.toSk4f(src[index]);
herb15332a82016-05-12 11:37:00 -0700275 }
276
herb00dd4532016-07-11 10:33:37 -0700277 Sk4f getPixelAt(int index) const override {
herb15332a82016-05-12 11:37:00 -0700278 return this->getPixelFromRow(fSrc, index);
279 }
280
herb00dd4532016-07-11 10:33:37 -0700281 const void* row(int y) const override { return fSrc + y * fWidth; }
herb15332a82016-05-12 11:37:00 -0700282
283private:
284 const Element* const fSrc;
herb00dd4532016-07-11 10:33:37 -0700285 const int fWidth;
286 PixelConverter<colorType, gammaType> fConverter;
herb15332a82016-05-12 11:37:00 -0700287};
288
herb7ccbc1a2016-06-09 09:05:00 -0700289// We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
290// We'll never re-use pixels, but we can at least load contiguous pixels.
291template <typename Next, typename Strategy>
292static void src_strategy_blend(Span span, Next* next, Strategy* strategy) {
293 SkPoint start;
294 SkScalar length;
295 int count;
296 std::tie(start, length, count) = span;
297 int ix = SkScalarFloorToInt(X(start));
298 const void* row = strategy->row((int)std::floor(Y(start)));
299 if (length > 0) {
300 while (count >= 4) {
301 Sk4f px0, px1, px2, px3;
302 strategy->get4Pixels(row, ix, &px0, &px1, &px2, &px3);
303 next->blend4Pixels(px0, px1, px2, px3);
304 ix += 4;
305 count -= 4;
306 }
307
308 while (count > 0) {
309 next->blendPixel(strategy->getPixelFromRow(row, ix));
310 ix += 1;
311 count -= 1;
312 }
313 } else {
314 while (count >= 4) {
315 Sk4f px0, px1, px2, px3;
316 strategy->get4Pixels(row, ix - 3, &px3, &px2, &px1, &px0);
317 next->blend4Pixels(px0, px1, px2, px3);
318 ix -= 4;
319 count -= 4;
320 }
321
322 while (count > 0) {
323 next->blendPixel(strategy->getPixelFromRow(row, ix));
324 ix -= 1;
325 count -= 1;
326 }
327 }
328}
329
herb86a6c6d2016-07-22 14:06:27 -0700330// -- NearestNeighborSampler -----------------------------------------------------------------------
herb7ccbc1a2016-06-09 09:05:00 -0700331// NearestNeighborSampler - use nearest neighbor filtering to create runs of destination pixels.
herb00dd4532016-07-11 10:33:37 -0700332template<typename Accessor, typename Next>
herb7ccbc1a2016-06-09 09:05:00 -0700333class NearestNeighborSampler : public SkLinearBitmapPipeline::SampleProcessorInterface {
herb6eff52a2016-03-23 09:00:33 -0700334public:
335 template<typename... Args>
herb7ccbc1a2016-06-09 09:05:00 -0700336 NearestNeighborSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next, Args&& ... args)
herb00dd4532016-07-11 10:33:37 -0700337 : fNext{next}, fAccessor{std::forward<Args>(args)...} { }
herb6eff52a2016-03-23 09:00:33 -0700338
herb7ccbc1a2016-06-09 09:05:00 -0700339 NearestNeighborSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next,
340 const NearestNeighborSampler& sampler)
herb00dd4532016-07-11 10:33:37 -0700341 : fNext{next}, fAccessor{sampler.fAccessor} { }
herb9e0efe52016-04-08 13:25:28 -0700342
mtkleine5fb9c82016-07-07 08:12:09 -0700343 void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
herb6eff52a2016-03-23 09:00:33 -0700344 SkASSERT(0 < n && n < 4);
345 Sk4f px0, px1, px2;
herb86a6c6d2016-07-22 14:06:27 -0700346 fAccessor.getFewPixels(n, SkNx_cast<int>(xs), SkNx_cast<int>(ys), &px0, &px1, &px2);
herb9e0efe52016-04-08 13:25:28 -0700347 if (n >= 1) fNext->blendPixel(px0);
348 if (n >= 2) fNext->blendPixel(px1);
349 if (n >= 3) fNext->blendPixel(px2);
herb6eff52a2016-03-23 09:00:33 -0700350 }
351
mtkleine5fb9c82016-07-07 08:12:09 -0700352 void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
herb6eff52a2016-03-23 09:00:33 -0700353 Sk4f px0, px1, px2, px3;
herb86a6c6d2016-07-22 14:06:27 -0700354 fAccessor.get4Pixels(SkNx_cast<int>(xs), SkNx_cast<int>(ys), &px0, &px1, &px2, &px3);
herb9e0efe52016-04-08 13:25:28 -0700355 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700356 }
357
herb7ccbc1a2016-06-09 09:05:00 -0700358 void pointSpan(Span span) override {
herb6eff52a2016-03-23 09:00:33 -0700359 SkASSERT(!span.isEmpty());
360 SkPoint start;
361 SkScalar length;
362 int count;
363 std::tie(start, length, count) = span;
364 SkScalar absLength = SkScalarAbs(length);
365 if (absLength < (count - 1)) {
herb7ccbc1a2016-06-09 09:05:00 -0700366 this->spanSlowRate(span);
herb6eff52a2016-03-23 09:00:33 -0700367 } else if (absLength == (count - 1)) {
herb00dd4532016-07-11 10:33:37 -0700368 src_strategy_blend(span, fNext, &fAccessor);
herb6eff52a2016-03-23 09:00:33 -0700369 } else {
herb7ccbc1a2016-06-09 09:05:00 -0700370 this->spanFastRate(span);
herb6eff52a2016-03-23 09:00:33 -0700371 }
372 }
373
herb7ccbc1a2016-06-09 09:05:00 -0700374 void repeatSpan(Span span, int32_t repeatCount) override {
375 while (repeatCount > 0) {
376 this->pointSpan(span);
377 repeatCount--;
herb6eff52a2016-03-23 09:00:33 -0700378 }
379 }
380
381private:
382 // When moving through source space more slowly than dst space (zoomed in),
383 // we'll be sampling from the same source pixel more than once.
herb7ccbc1a2016-06-09 09:05:00 -0700384 void spanSlowRate(Span span) {
herb86a6c6d2016-07-22 14:06:27 -0700385 SkPoint start; SkScalar length; int count;
herb6eff52a2016-03-23 09:00:33 -0700386 std::tie(start, length, count) = span;
387 SkScalar x = X(start);
388 SkFixed fx = SkScalarToFixed(x);
389 SkScalar dx = length / (count - 1);
390 SkFixed fdx = SkScalarToFixed(dx);
391
herb00dd4532016-07-11 10:33:37 -0700392 const void* row = fAccessor.row((int)std::floor(Y(start)));
herb6eff52a2016-03-23 09:00:33 -0700393 Next* next = fNext;
394
395 int ix = SkFixedFloorToInt(fx);
396 int prevIX = ix;
herb00dd4532016-07-11 10:33:37 -0700397 Sk4f fpixel = fAccessor.getPixelFromRow(row, ix);
herb6eff52a2016-03-23 09:00:33 -0700398
399 // When dx is less than one, each pixel is used more than once. Using the fixed point fx
400 // allows the code to quickly check that the same pixel is being used. The code uses this
401 // same pixel check to do the sRGB and normalization only once.
402 auto getNextPixel = [&]() {
403 if (ix != prevIX) {
herb00dd4532016-07-11 10:33:37 -0700404 fpixel = fAccessor.getPixelFromRow(row, ix);
herb6eff52a2016-03-23 09:00:33 -0700405 prevIX = ix;
406 }
407 fx += fdx;
408 ix = SkFixedFloorToInt(fx);
409 return fpixel;
410 };
411
412 while (count >= 4) {
413 Sk4f px0 = getNextPixel();
414 Sk4f px1 = getNextPixel();
415 Sk4f px2 = getNextPixel();
416 Sk4f px3 = getNextPixel();
herb9e0efe52016-04-08 13:25:28 -0700417 next->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700418 count -= 4;
419 }
420 while (count > 0) {
herb9e0efe52016-04-08 13:25:28 -0700421 next->blendPixel(getNextPixel());
herb6eff52a2016-03-23 09:00:33 -0700422 count -= 1;
423 }
424 }
425
426 // We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
427 // We'll never re-use pixels, but we can at least load contiguous pixels.
herb7ccbc1a2016-06-09 09:05:00 -0700428 void spanUnitRate(Span span) {
herb00dd4532016-07-11 10:33:37 -0700429 src_strategy_blend(span, fNext, &fAccessor);
herb6eff52a2016-03-23 09:00:33 -0700430 }
431
432 // We're moving through source space faster than dst (zoomed out),
433 // so we'll never reuse a source pixel or be able to do contiguous loads.
herb7ccbc1a2016-06-09 09:05:00 -0700434 void spanFastRate(Span span) {
435 span_fallback(span, this);
herb6eff52a2016-03-23 09:00:33 -0700436 }
437
herb00dd4532016-07-11 10:33:37 -0700438 Next* const fNext;
439 Accessor fAccessor;
herb7ccbc1a2016-06-09 09:05:00 -0700440};
441
herb86a6c6d2016-07-22 14:06:27 -0700442// From an edgeType, the integer value of a pixel vs, and the integer value of the extreme edge
443// vMax, take the point which might be off the tile by one pixel and either wrap it or pin it to
444// generate the right pixel. The value vs is on the interval [-1, vMax + 1]. It produces a value
445// on the interval [0, vMax].
446// Note: vMax is not width or height, but width-1 or height-1 because it is the largest valid pixel.
447static inline int adjust_edge(SkShader::TileMode edgeType, int vs, int vMax) {
caryclarkd6562002016-07-27 12:02:07 -0700448 SkASSERT(-1 <= vs && vs <= vMax + 1);
herb86a6c6d2016-07-22 14:06:27 -0700449 switch (edgeType) {
450 case SkShader::kClamp_TileMode:
451 case SkShader::kMirror_TileMode:
452 vs = std::max(vs, 0);
453 vs = std::min(vs, vMax);
454 break;
455 case SkShader::kRepeat_TileMode:
456 vs = (vs <= vMax) ? vs : 0;
457 vs = (vs >= 0) ? vs : vMax;
458 break;
459 }
460 SkASSERT(0 <= vs && vs <= vMax);
461 return vs;
462}
463
464// From a sample point on the tile, return the top or left filter value.
465// The result r should be in the range (0, 1]. Since this represents the weight given to the top
466// left element, then if x == 0.5 the filter value should be 1.0.
467// The input sample point must be on the tile, therefore it must be >= 0.
468static SkScalar sample_to_filter(SkScalar x) {
469 SkASSERT(x >= 0.0f);
470 // The usual form of the top or left edge is x - .5, but since we are working on the unit
471 // square, then x + .5 works just as well. This also guarantees that v > 0.0 allowing the use
472 // of trunc.
473 SkScalar v = x + 0.5f;
474 // Produce the top or left offset a value on the range [0, 1).
475 SkScalar f = v - SkScalarTruncToScalar(v);
476 // Produce the filter value which is on the range (0, 1].
477 SkScalar r = 1.0f - f;
478 SkASSERT(0.0f < r && r <= 1.0f);
479 return r;
480}
481
herb7df9e4a2016-06-10 13:01:27 -0700482// -- BilerpSampler --------------------------------------------------------------------------------
herb7ccbc1a2016-06-09 09:05:00 -0700483// BilerpSampler - use a bilerp filter to create runs of destination pixels.
herb86a6c6d2016-07-22 14:06:27 -0700484// Note: in the code below, there are two types of points
485// * sample points - these are the points passed in by pointList* and Spans.
486// * filter points - are created from a sample point to form the coordinates of the points
487// to use in the filter and to generate the filter values.
herb00dd4532016-07-11 10:33:37 -0700488template<typename Accessor, typename Next>
herb7ccbc1a2016-06-09 09:05:00 -0700489class BilerpSampler : public SkLinearBitmapPipeline::SampleProcessorInterface {
490public:
491 template<typename... Args>
herb86a6c6d2016-07-22 14:06:27 -0700492 BilerpSampler(
493 SkLinearBitmapPipeline::BlendProcessorInterface* next,
494 SkISize dimensions,
495 SkShader::TileMode xTile, SkShader::TileMode yTile,
496 Args&& ... args
497 )
498 : fNext{next}
499 , fXEdgeType{xTile}
500 , fXMax{dimensions.width() - 1}
501 , fYEdgeType{yTile}
502 , fYMax{dimensions.height() - 1}
503 , fAccessor{std::forward<Args>(args)...} { }
herb7ccbc1a2016-06-09 09:05:00 -0700504
505 BilerpSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next,
506 const BilerpSampler& sampler)
herb86a6c6d2016-07-22 14:06:27 -0700507 : fNext{next}
508 , fXEdgeType{sampler.fXEdgeType}
509 , fXMax{sampler.fXMax}
510 , fYEdgeType{sampler.fYEdgeType}
511 , fYMax{sampler.fYMax}
512 , fAccessor{sampler.fAccessor} { }
herb7ccbc1a2016-06-09 09:05:00 -0700513
mtkleine5fb9c82016-07-07 08:12:09 -0700514 void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
herb7ccbc1a2016-06-09 09:05:00 -0700515 SkASSERT(0 < n && n < 4);
516 auto bilerpPixel = [&](int index) {
herb86a6c6d2016-07-22 14:06:27 -0700517 return this->bilerpSamplePoint(SkPoint{xs[index], ys[index]});
herb7ccbc1a2016-06-09 09:05:00 -0700518 };
519
520 if (n >= 1) fNext->blendPixel(bilerpPixel(0));
521 if (n >= 2) fNext->blendPixel(bilerpPixel(1));
522 if (n >= 3) fNext->blendPixel(bilerpPixel(2));
523 }
524
mtkleine5fb9c82016-07-07 08:12:09 -0700525 void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
herb7ccbc1a2016-06-09 09:05:00 -0700526 auto bilerpPixel = [&](int index) {
herb86a6c6d2016-07-22 14:06:27 -0700527 return this->bilerpSamplePoint(SkPoint{xs[index], ys[index]});
herb7ccbc1a2016-06-09 09:05:00 -0700528 };
529 fNext->blend4Pixels(bilerpPixel(0), bilerpPixel(1), bilerpPixel(2), bilerpPixel(3));
530 }
531
532 void pointSpan(Span span) override {
herb86a6c6d2016-07-22 14:06:27 -0700533 SkASSERT(!span.isEmpty());
534 SkPoint start;
535 SkScalar length;
536 int count;
537 std::tie(start, length, count) = span;
538
539 // Nothing to do.
540 if (count == 0) {
541 return;
542 }
543
544 // Trivial case. No sample points are generated other than start.
545 if (count == 1) {
546 fNext->blendPixel(this->bilerpSamplePoint(start));
547 return;
548 }
549
550 // Note: the following code could be done in terms of dx = length / (count -1), but that
551 // would introduce a divide that is not needed for the most common dx == 1 cases.
552 SkScalar absLength = SkScalarAbs(length);
553 if (absLength == 0.0f) {
554 // |dx| == 0
555 // length is zero, so clamp an edge pixel.
556 this->spanZeroRate(span);
557 } else if (absLength < (count - 1)) {
558 // 0 < |dx| < 1.
559 this->spanSlowRate(span);
560 } else if (absLength == (count - 1)) {
561 // |dx| == 1.
562 if (sample_to_filter(span.startX()) == 1.0f
563 && sample_to_filter(span.startY()) == 1.0f) {
564 // All the pixels are aligned with the dest; go fast.
565 src_strategy_blend(span, fNext, &fAccessor);
566 } else {
567 // There is some sub-pixel offsets, so bilerp.
568 this->spanUnitRate(span);
569 }
570 } else if (absLength < 2.0f * (count - 1)) {
571 // 1 < |dx| < 2.
572 this->spanMediumRate(span);
573 } else {
574 // |dx| >= 2.
575 this->spanFastRate(span);
576 }
herb7ccbc1a2016-06-09 09:05:00 -0700577 }
578
579 void repeatSpan(Span span, int32_t repeatCount) override {
580 while (repeatCount > 0) {
581 this->pointSpan(span);
582 repeatCount--;
583 }
584 }
585
jcgregorioda626aa2016-07-22 05:40:58 -0700586private:
herb86a6c6d2016-07-22 14:06:27 -0700587
588 // Convert a sample point to the points used by the filter.
589 void filterPoints(SkPoint sample, Sk4i* filterXs, Sk4i* filterYs) {
590 // May be less than zero. Be careful to use Floor.
591 int x0 = adjust_edge(fXEdgeType, SkScalarFloorToInt(X(sample) - 0.5), fXMax);
592 // Always greater than zero. Use the faster Trunc.
593 int x1 = adjust_edge(fXEdgeType, SkScalarTruncToInt(X(sample) + 0.5), fXMax);
594 int y0 = adjust_edge(fYEdgeType, SkScalarFloorToInt(Y(sample) - 0.5), fYMax);
595 int y1 = adjust_edge(fYEdgeType, SkScalarTruncToInt(Y(sample) + 0.5), fYMax);
596
597 *filterXs = Sk4i{x0, x1, x0, x1};
598 *filterYs = Sk4i{y0, y0, y1, y1};
herb6eff52a2016-03-23 09:00:33 -0700599 }
600
herb86a6c6d2016-07-22 14:06:27 -0700601 // Given a sample point, generate a color by bilerping the four filter points.
602 Sk4f bilerpSamplePoint(SkPoint sample) {
603 Sk4i iXs, iYs;
604 filterPoints(sample, &iXs, &iYs);
605 Sk4f px00, px10, px01, px11;
606 fAccessor.get4Pixels(iXs, iYs, &px00, &px10, &px01, &px11);
607 return bilerp4(Sk4f{X(sample) - 0.5f}, Sk4f{Y(sample) - 0.5f}, px00, px10, px01, px11);
608 }
609
610 // Get two pixels at x from row0 and row1.
611 void get2PixelColumn(const void* row0, const void* row1, int x, Sk4f* px0, Sk4f* px1) {
612 *px0 = fAccessor.getPixelFromRow(row0, x);
613 *px1 = fAccessor.getPixelFromRow(row1, x);
614 }
615
616 // |dx| == 0. This code assumes that length is zero.
617 void spanZeroRate(Span span) {
618 SkPoint start; SkScalar length; int count;
herb8602ede2016-07-21 13:22:04 -0700619 std::tie(start, length, count) = span;
herb86a6c6d2016-07-22 14:06:27 -0700620 SkASSERT(length == 0.0f);
herb6eff52a2016-03-23 09:00:33 -0700621
herb86a6c6d2016-07-22 14:06:27 -0700622 // Filter for the blending of the top and bottom pixels.
623 SkScalar filterY = sample_to_filter(Y(start));
herb8602ede2016-07-21 13:22:04 -0700624
herb86a6c6d2016-07-22 14:06:27 -0700625 // Generate the four filter points from the sample point start. Generate the row* values.
626 Sk4i iXs, iYs;
627 this->filterPoints(start, &iXs, &iYs);
628 const void* const row0 = fAccessor.row(iYs[0]);
629 const void* const row1 = fAccessor.row(iYs[2]);
herb6eff52a2016-03-23 09:00:33 -0700630
herb86a6c6d2016-07-22 14:06:27 -0700631 // Get the two pixels that make up the clamping pixel.
632 Sk4f pxTop, pxBottom;
633 this->get2PixelColumn(row0, row1, SkScalarFloorToInt(X(start)), &pxTop, &pxBottom);
634 Sk4f pixel = pxTop * filterY + (1.0f - filterY) * pxBottom;
jcgregorioda626aa2016-07-22 05:40:58 -0700635
636 while (count >= 4) {
herb86a6c6d2016-07-22 14:06:27 -0700637 fNext->blend4Pixels(pixel, pixel, pixel, pixel);
jcgregorioda626aa2016-07-22 05:40:58 -0700638 count -= 4;
639 }
jcgregorioda626aa2016-07-22 05:40:58 -0700640 while (count > 0) {
herb86a6c6d2016-07-22 14:06:27 -0700641 fNext->blendPixel(pixel);
jcgregorioda626aa2016-07-22 05:40:58 -0700642 count -= 1;
643 }
644 }
645
herb86a6c6d2016-07-22 14:06:27 -0700646 // 0 < |dx| < 1. This code reuses the calculations from previous pixels to reduce
647 // computation. In particular, several destination pixels maybe generated from the same four
648 // source pixels.
649 // In the following code a "part" is a combination of two pixels from the same column of the
650 // filter.
651 void spanSlowRate(Span span) {
652 SkPoint start; SkScalar length; int count;
653 std::tie(start, length, count) = span;
jcgregorioda626aa2016-07-22 05:40:58 -0700654
herb86a6c6d2016-07-22 14:06:27 -0700655 // Calculate the distance between each sample point.
656 const SkScalar dx = length / (count - 1);
657 SkASSERT(-1.0f < dx && dx < 1.0f && dx != 0.0f);
658
659 // Generate the filter values for the top-left corner.
660 // Note: these values are in filter space; this has implications about how to adjust
661 // these values at each step. For example, as the sample point increases, the filter
662 // value decreases, this is because the filter and position are related by
663 // (1 - (X(sample) - .5)) % 1. The (1 - stuff) causes the filter to move in the opposite
664 // direction of the sample point which is increasing by dx.
665 SkScalar filterX = sample_to_filter(X(start));
666 SkScalar filterY = sample_to_filter(Y(start));
667
668 // Generate the four filter points from the sample point start. Generate the row* values.
669 Sk4i iXs, iYs;
670 this->filterPoints(start, &iXs, &iYs);
671 const void* const row0 = fAccessor.row(iYs[0]);
672 const void* const row1 = fAccessor.row(iYs[2]);
673
674 // Generate part of the filter value at xColumn.
675 auto partAtColumn = [&](int xColumn) {
676 int adjustedColumn = adjust_edge(fXEdgeType, xColumn, fXMax);
677 Sk4f pxTop, pxBottom;
678 this->get2PixelColumn(row0, row1, adjustedColumn, &pxTop, &pxBottom);
679 return pxTop * filterY + (1.0f - filterY) * pxBottom;
jcgregorioda626aa2016-07-22 05:40:58 -0700680 };
681
herb86a6c6d2016-07-22 14:06:27 -0700682 // The leftPart is made up of two pixels from the left column of the filter, right part
683 // is similar. The top and bottom pixels in the *Part are created as a linear blend of
684 // the top and bottom pixels using filterY. See the partAtColumn function above.
685 Sk4f leftPart = partAtColumn(iXs[0]);
686 Sk4f rightPart = partAtColumn(iXs[1]);
687
688 // Create a destination color by blending together a left and right part using filterX.
689 auto bilerp = [&](const Sk4f& leftPart, const Sk4f& rightPart) {
690 Sk4f pixel = leftPart * filterX + rightPart * (1.0f - filterX);
691 return check_pixel(pixel);
jcgregorioda626aa2016-07-22 05:40:58 -0700692 };
693
herb86a6c6d2016-07-22 14:06:27 -0700694 // Send the first pixel to the destination. This simplifies the loop structure so that no
695 // extra pixels are fetched for the last iteration of the loop.
696 fNext->blendPixel(bilerp(leftPart, rightPart));
697 count -= 1;
jcgregorioda626aa2016-07-22 05:40:58 -0700698
herb86a6c6d2016-07-22 14:06:27 -0700699 if (dx > 0.0f) {
700 // * positive direction - generate destination pixels by sliding the filter from left
701 // to right.
702 int rightPartCursor = iXs[1];
jcgregorioda626aa2016-07-22 05:40:58 -0700703
herb86a6c6d2016-07-22 14:06:27 -0700704 // Advance the filter from left to right. Remember that moving the top-left corner of
705 // the filter to the right actually makes the filter value smaller.
706 auto advanceFilter = [&]() {
707 filterX -= dx;
708 if (filterX <= 0.0f) {
709 filterX += 1.0f;
710 leftPart = rightPart;
711 rightPartCursor += 1;
712 rightPart = partAtColumn(rightPartCursor);
713 }
714 SkASSERT(0.0f < filterX && filterX <= 1.0f);
jcgregorioda626aa2016-07-22 05:40:58 -0700715
herb86a6c6d2016-07-22 14:06:27 -0700716 return bilerp(leftPart, rightPart);
717 };
718
jcgregorioda626aa2016-07-22 05:40:58 -0700719 while (count >= 4) {
herb86a6c6d2016-07-22 14:06:27 -0700720 Sk4f px0 = advanceFilter(),
721 px1 = advanceFilter(),
722 px2 = advanceFilter(),
723 px3 = advanceFilter();
jcgregorioda626aa2016-07-22 05:40:58 -0700724 fNext->blend4Pixels(px0, px1, px2, px3);
jcgregorioda626aa2016-07-22 05:40:58 -0700725 count -= 4;
726 }
jcgregorioda626aa2016-07-22 05:40:58 -0700727
herb86a6c6d2016-07-22 14:06:27 -0700728 while (count > 0) {
729 fNext->blendPixel(advanceFilter());
herb6eff52a2016-03-23 09:00:33 -0700730 count -= 1;
731 }
732 } else {
herb86a6c6d2016-07-22 14:06:27 -0700733 // * negative direction - generate destination pixels by sliding the filter from
734 // right to left.
735 int leftPartCursor = iXs[0];
736
737 // Advance the filter from right to left. Remember that moving the top-left corner of
738 // the filter to the left actually makes the filter value larger.
739 auto advanceFilter = [&]() {
740 // Remember, dx < 0 therefore this adds |dx| to filterX.
741 filterX -= dx;
742 // At this point filterX may be > 1, and needs to be wrapped back on to the filter
743 // interval, and the next column in the filter is calculated.
744 if (filterX > 1.0f) {
745 filterX -= 1.0f;
746 rightPart = leftPart;
747 leftPartCursor -= 1;
748 leftPart = partAtColumn(leftPartCursor);
749 }
750 SkASSERT(0.0f < filterX && filterX <= 1.0f);
751
752 return bilerp(leftPart, rightPart);
753 };
754
herb6eff52a2016-03-23 09:00:33 -0700755 while (count >= 4) {
herb86a6c6d2016-07-22 14:06:27 -0700756 Sk4f px0 = advanceFilter(),
757 px1 = advanceFilter(),
758 px2 = advanceFilter(),
759 px3 = advanceFilter();
herb9e0efe52016-04-08 13:25:28 -0700760 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700761 count -= 4;
762 }
jcgregorioda626aa2016-07-22 05:40:58 -0700763
herb86a6c6d2016-07-22 14:06:27 -0700764 while (count > 0) {
765 fNext->blendPixel(advanceFilter());
herb6eff52a2016-03-23 09:00:33 -0700766 count -= 1;
767 }
768 }
769 }
770
herb86a6c6d2016-07-22 14:06:27 -0700771 // |dx| == 1. Moving through source space at a rate of 1 source pixel per 1 dst pixel.
772 // Every filter part is used for two destination pixels, and the code can bulk load four
773 // pixels at a time.
774 void spanUnitRate(Span span) {
775 SkPoint start; SkScalar length; int count;
776 std::tie(start, length, count) = span;
777 SkASSERT(SkScalarAbs(length) == (count - 1));
778
779 // Calculate the four filter points of start, and use the two different Y values to
780 // generate the row pointers.
781 Sk4i iXs, iYs;
782 filterPoints(start, &iXs, &iYs);
783 const void* row0 = fAccessor.row(iYs[0]);
784 const void* row1 = fAccessor.row(iYs[2]);
785
786 // Calculate the filter values for the top-left filter element.
787 const SkScalar filterX = sample_to_filter(X(start));
788 const SkScalar filterY = sample_to_filter(Y(start));
789
790 // Generate part of the filter value at xColumn.
791 auto partAtColumn = [&](int xColumn) {
792 int adjustedColumn = adjust_edge(fXEdgeType, xColumn, fXMax);
793 Sk4f pxTop, pxBottom;
794 this->get2PixelColumn(row0, row1, adjustedColumn, &pxTop, &pxBottom);
795 return pxTop * filterY + (1.0f - filterY) * pxBottom;
herb6eff52a2016-03-23 09:00:33 -0700796 };
797
herb86a6c6d2016-07-22 14:06:27 -0700798 auto get4Parts = [&](int ix, Sk4f* part0, Sk4f* part1, Sk4f* part2, Sk4f* part3) {
799 // Check if the pixels needed are near the edges. If not go fast using bulk pixels,
800 // otherwise be careful.
801 if (0 <= ix && ix <= fXMax - 3) {
802 Sk4f px00, px10, px20, px30,
803 px01, px11, px21, px31;
804 fAccessor.get4Pixels(row0, ix, &px00, &px10, &px20, &px30);
805 fAccessor.get4Pixels(row1, ix, &px01, &px11, &px21, &px31);
806 *part0 = filterY * px00 + (1.0f - filterY) * px01;
807 *part1 = filterY * px10 + (1.0f - filterY) * px11;
808 *part2 = filterY * px20 + (1.0f - filterY) * px21;
809 *part3 = filterY * px30 + (1.0f - filterY) * px31;
810 } else {
811 *part0 = partAtColumn(ix + 0);
812 *part1 = partAtColumn(ix + 1);
813 *part2 = partAtColumn(ix + 2);
814 *part3 = partAtColumn(ix + 3);
815 }
816 };
817
818 auto bilerp = [&](const Sk4f& part0, const Sk4f& part1) {
819 return part0 * filterX + part1 * (1.0f - filterX);
820 };
821
822 if (length > 0) {
823 // * positive direction - generate destination pixels by sliding the filter from left
824 // to right.
825
826 // overlapPart is the filter part from the end of the previous four pixels used at
827 // the start of the next four pixels.
828 Sk4f overlapPart = partAtColumn(iXs[0]);
829 int rightColumnCursor = iXs[1];
herb6eff52a2016-03-23 09:00:33 -0700830 while (count >= 4) {
herb86a6c6d2016-07-22 14:06:27 -0700831 Sk4f part0, part1, part2, part3;
832 get4Parts(rightColumnCursor, &part0, &part1, &part2, &part3);
833 Sk4f px0 = bilerp(overlapPart, part0);
834 Sk4f px1 = bilerp(part0, part1);
835 Sk4f px2 = bilerp(part1, part2);
836 Sk4f px3 = bilerp(part2, part3);
837 overlapPart = part3;
838 fNext->blend4Pixels(px0, px1, px2, px3);
839 rightColumnCursor += 4;
herb6eff52a2016-03-23 09:00:33 -0700840 count -= 4;
841 }
herb8602ede2016-07-21 13:22:04 -0700842
herb86a6c6d2016-07-22 14:06:27 -0700843 while (count > 0) {
844 Sk4f rightPart = partAtColumn(rightColumnCursor);
845
846 fNext->blendPixel(bilerp(overlapPart, rightPart));
847 overlapPart = rightPart;
848 rightColumnCursor += 1;
herb6eff52a2016-03-23 09:00:33 -0700849 count -= 1;
850 }
851 } else {
herb86a6c6d2016-07-22 14:06:27 -0700852 // * negative direction - generate destination pixels by sliding the filter from
853 // right to left.
854 Sk4f overlapPart = partAtColumn(iXs[1]);
855 int leftColumnCursor = iXs[0];
856
herb6eff52a2016-03-23 09:00:33 -0700857 while (count >= 4) {
herb86a6c6d2016-07-22 14:06:27 -0700858 Sk4f part0, part1, part2, part3;
859 get4Parts(leftColumnCursor - 3, &part3, &part2, &part1, &part0);
860 Sk4f px0 = bilerp(part0, overlapPart);
861 Sk4f px1 = bilerp(part1, part0);
862 Sk4f px2 = bilerp(part2, part1);
863 Sk4f px3 = bilerp(part3, part2);
864 overlapPart = part3;
865 fNext->blend4Pixels(px0, px1, px2, px3);
866 leftColumnCursor -= 4;
herb6eff52a2016-03-23 09:00:33 -0700867 count -= 4;
868 }
herb8602ede2016-07-21 13:22:04 -0700869
herb86a6c6d2016-07-22 14:06:27 -0700870 while (count > 0) {
871 Sk4f leftPart = partAtColumn(leftColumnCursor);
872
873 fNext->blendPixel(bilerp(leftPart, overlapPart));
874 overlapPart = leftPart;
875 leftColumnCursor -= 1;
876 count -= 1;
877 }
878 }
879 }
880
881 // 1 < |dx| < 2. Going through the source pixels at a faster rate than the dest pixels, but
882 // still slow enough to take advantage of previous calculations.
883 void spanMediumRate(Span span) {
884 SkPoint start; SkScalar length; int count;
885 std::tie(start, length, count) = span;
886
887 // Calculate the distance between each sample point.
888 const SkScalar dx = length / (count - 1);
889 SkASSERT((-2.0f < dx && dx < -1.0f) || (1.0f < dx && dx < 2.0f));
890
891 // Generate the filter values for the top-left corner.
892 // Note: these values are in filter space; this has implications about how to adjust
893 // these values at each step. For example, as the sample point increases, the filter
894 // value decreases, this is because the filter and position are related by
895 // (1 - (X(sample) - .5)) % 1. The (1 - stuff) causes the filter to move in the opposite
896 // direction of the sample point which is increasing by dx.
897 SkScalar filterX = sample_to_filter(X(start));
898 SkScalar filterY = sample_to_filter(Y(start));
899
900 // Generate the four filter points from the sample point start. Generate the row* values.
901 Sk4i iXs, iYs;
902 this->filterPoints(start, &iXs, &iYs);
903 const void* const row0 = fAccessor.row(iYs[0]);
904 const void* const row1 = fAccessor.row(iYs[2]);
905
906 // Generate part of the filter value at xColumn.
907 auto partAtColumn = [&](int xColumn) {
908 int adjustedColumn = adjust_edge(fXEdgeType, xColumn, fXMax);
909 Sk4f pxTop, pxBottom;
910 this->get2PixelColumn(row0, row1, adjustedColumn, &pxTop, &pxBottom);
911 return pxTop * filterY + (1.0f - filterY) * pxBottom;
912 };
913
914 // The leftPart is made up of two pixels from the left column of the filter, right part
915 // is similar. The top and bottom pixels in the *Part are created as a linear blend of
916 // the top and bottom pixels using filterY. See the nextPart function below.
917 Sk4f leftPart = partAtColumn(iXs[0]);
918 Sk4f rightPart = partAtColumn(iXs[1]);
919
920 // Create a destination color by blending together a left and right part using filterX.
921 auto bilerp = [&](const Sk4f& leftPart, const Sk4f& rightPart) {
922 Sk4f pixel = leftPart * filterX + rightPart * (1.0f - filterX);
923 return check_pixel(pixel);
924 };
925
926 // Send the first pixel to the destination. This simplifies the loop structure so that no
927 // extra pixels are fetched for the last iteration of the loop.
928 fNext->blendPixel(bilerp(leftPart, rightPart));
929 count -= 1;
930
931 if (dx > 0.0f) {
932 // * positive direction - generate destination pixels by sliding the filter from left
933 // to right.
934 int rightPartCursor = iXs[1];
935
936 // Advance the filter from left to right. Remember that moving the top-left corner of
937 // the filter to the right actually makes the filter value smaller.
938 auto advanceFilter = [&]() {
939 filterX -= dx;
940 // At this point filterX is less than zero, but might actually be less than -1.
941 if (filterX > -1.0f) {
942 filterX += 1.0f;
943 leftPart = rightPart;
944 rightPartCursor += 1;
945 rightPart = partAtColumn(rightPartCursor);
946 } else {
947 filterX += 2.0f;
948 rightPartCursor += 2;
949 leftPart = partAtColumn(rightPartCursor - 1);
950 rightPart = partAtColumn(rightPartCursor);
951 }
952 SkASSERT(0.0f < filterX && filterX <= 1.0f);
953
954 return bilerp(leftPart, rightPart);
955 };
956
957 while (count >= 4) {
958 Sk4f px0 = advanceFilter(),
959 px1 = advanceFilter(),
960 px2 = advanceFilter(),
961 px3 = advanceFilter();
962 fNext->blend4Pixels(px0, px1, px2, px3);
963 count -= 4;
964 }
965
966 while (count > 0) {
967 fNext->blendPixel(advanceFilter());
968 count -= 1;
969 }
970 } else {
971 // * negative direction - generate destination pixels by sliding the filter from
972 // right to left.
973 int leftPartCursor = iXs[0];
974
975 auto advanceFilter = [&]() {
976 // Remember, dx < 0 therefore this adds |dx| to filterX.
977 filterX -= dx;
978 // At this point, filterX is greater than one, but may actually be greater than two.
979 if (filterX < 2.0f) {
980 filterX -= 1.0f;
981 rightPart = leftPart;
982 leftPartCursor -= 1;
983 leftPart = partAtColumn(leftPartCursor);
984 } else {
985 filterX -= 2.0f;
986 leftPartCursor -= 2;
987 rightPart = partAtColumn(leftPartCursor - 1);
988 leftPart = partAtColumn(leftPartCursor);
989 }
990 SkASSERT(0.0f < filterX && filterX <= 1.0f);
991 return bilerp(leftPart, rightPart);
992 };
993
994 while (count >= 4) {
995 Sk4f px0 = advanceFilter(),
996 px1 = advanceFilter(),
997 px2 = advanceFilter(),
998 px3 = advanceFilter();
999 fNext->blend4Pixels(px0, px1, px2, px3);
1000 count -= 4;
1001 }
1002
1003 while (count > 0) {
1004 fNext->blendPixel(advanceFilter());
herb6eff52a2016-03-23 09:00:33 -07001005 count -= 1;
1006 }
1007 }
1008 }
1009
1010 // We're moving through source space faster than dst (zoomed out),
1011 // so we'll never reuse a source pixel or be able to do contiguous loads.
herb86a6c6d2016-07-22 14:06:27 -07001012 void spanFastRate(Span span) {
1013 SkPoint start; SkScalar length; int count;
herb6eff52a2016-03-23 09:00:33 -07001014 std::tie(start, length, count) = span;
1015 SkScalar x = X(start);
1016 SkScalar y = Y(start);
herb7ccbc1a2016-06-09 09:05:00 -07001017
herb86a6c6d2016-07-22 14:06:27 -07001018 SkScalar dx = length / (count - 1);
1019 while (count > 0) {
1020 fNext->blendPixel(this->bilerpSamplePoint(SkPoint{x, y}));
1021 x += dx;
1022 count -= 1;
herb6eff52a2016-03-23 09:00:33 -07001023 }
1024 }
1025
herb86a6c6d2016-07-22 14:06:27 -07001026 Next* const fNext;
1027 const SkShader::TileMode fXEdgeType;
1028 const int fXMax;
1029 const SkShader::TileMode fYEdgeType;
1030 const int fYMax;
1031 Accessor fAccessor;
herb6eff52a2016-03-23 09:00:33 -07001032};
1033
herb6eff52a2016-03-23 09:00:33 -07001034} // namespace
1035
1036#endif // SkLinearBitmapPipeline_sampler_DEFINED