blob: 56f43224aea5b2f4f939162c94c3bbea5dccf95e [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.
42static Sk4s VECTORCALL bilerp4(Sk4s xs, Sk4s ys, Sk4f px00, Sk4f px10,
43 Sk4f px01, Sk4f px11) {
44 // 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////////////////////////////////////////////////////////////////////////////////////////////////////
56// PixelGetter is the lowest level interface to the source data. There is a PixelGetter for each
57// of the different SkColorTypes.
reeddabe5d32016-06-21 10:28:14 -070058template <SkColorType, SkGammaType> class PixelGetter;
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 <>
reeddabe5d32016-06-21 10:28:14 -070065class PixelGetter<kAlpha_8_SkColorType, kLinear_SkGammaType> {
herb670f01f2016-05-13 10:04:46 -070066public:
67 using Element = uint8_t;
68 PixelGetter(const SkPixmap& srcPixmap, SkColor tintColor)
69 : fTintColor{set_alpha(Sk4f_from_SkColor(tintColor), 1.0f)} { }
70
71 Sk4f getPixelAt(const uint8_t* src) {
72 return fTintColor * (*src * (1.0f/255.0f));
73 }
74
75private:
76 const Sk4f fTintColor;
77};
78
reeddabe5d32016-06-21 10:28:14 -070079template <SkGammaType gammaType>
80class PixelGetter<kRGB_565_SkColorType, gammaType> {
herb670f01f2016-05-13 10:04:46 -070081public:
82 using Element = uint16_t;
83 PixelGetter(const SkPixmap& srcPixmap) { }
84
85 Sk4f getPixelAt(const uint16_t* src) {
86 SkPMColor pixel = SkPixel16ToPixel32(*src);
reeddabe5d32016-06-21 10:28:14 -070087 return gammaType == kSRGB_SkGammaType
herb670f01f2016-05-13 10:04:46 -070088 ? Sk4f_fromS32(pixel)
89 : Sk4f_fromL32(pixel);
90 }
91};
92
reeddabe5d32016-06-21 10:28:14 -070093template <SkGammaType gammaType>
94class PixelGetter<kARGB_4444_SkColorType, gammaType> {
herb670f01f2016-05-13 10:04:46 -070095public:
96 using Element = uint16_t;
97 PixelGetter(const SkPixmap& srcPixmap) { }
98
99 Sk4f getPixelAt(const uint16_t* src) {
100 SkPMColor pixel = SkPixel4444ToPixel32(*src);
reeddabe5d32016-06-21 10:28:14 -0700101 return gammaType == kSRGB_SkGammaType
herb670f01f2016-05-13 10:04:46 -0700102 ? Sk4f_fromS32(pixel)
103 : Sk4f_fromL32(pixel);
104 }
105};
106
reeddabe5d32016-06-21 10:28:14 -0700107template <SkGammaType gammaType>
108class PixelGetter<kRGBA_8888_SkColorType, gammaType> {
herb15332a82016-05-12 11:37:00 -0700109public:
110 using Element = uint32_t;
111 PixelGetter(const SkPixmap& srcPixmap) { }
112
113 Sk4f getPixelAt(const uint32_t* src) {
reeddabe5d32016-06-21 10:28:14 -0700114 return gammaType == kSRGB_SkGammaType
herb15332a82016-05-12 11:37:00 -0700115 ? Sk4f_fromS32(*src)
116 : Sk4f_fromL32(*src);
117 }
118};
119
reeddabe5d32016-06-21 10:28:14 -0700120template <SkGammaType gammaType>
121class PixelGetter<kBGRA_8888_SkColorType, gammaType> {
herb15332a82016-05-12 11:37:00 -0700122public:
123 using Element = uint32_t;
124 PixelGetter(const SkPixmap& srcPixmap) { }
125
126 Sk4f getPixelAt(const uint32_t* src) {
reeddabe5d32016-06-21 10:28:14 -0700127 Sk4f pixel = gammaType == kSRGB_SkGammaType
herb15332a82016-05-12 11:37:00 -0700128 ? Sk4f_fromS32(*src)
129 : Sk4f_fromL32(*src);
herb670f01f2016-05-13 10:04:46 -0700130 return swizzle_rb(pixel);
herb15332a82016-05-12 11:37:00 -0700131 }
132};
133
reeddabe5d32016-06-21 10:28:14 -0700134template <SkGammaType gammaType>
135class PixelGetter<kIndex_8_SkColorType, gammaType> {
herb15332a82016-05-12 11:37:00 -0700136public:
137 using Element = uint8_t;
138 PixelGetter(const SkPixmap& srcPixmap) {
139 SkColorTable* skColorTable = srcPixmap.ctable();
140 SkASSERT(skColorTable != nullptr);
141
142 fColorTable = (Sk4f*)SkAlign16((intptr_t)fColorTableStorage.get());
143 for (int i = 0; i < skColorTable->count(); i++) {
144 fColorTable[i] = this->convertPixel((*skColorTable)[i]);
145 }
146 }
147
148 PixelGetter(const PixelGetter& strategy) {
149 fColorTable = (Sk4f*)SkAlign16((intptr_t)fColorTableStorage.get());
150 // TODO: figure out the count.
151 for (int i = 0; i < 256; i++) {
152 fColorTable[i] = strategy.fColorTable[i];
153 }
154 }
155
156 Sk4f getPixelAt(const uint8_t* src) {
157 return fColorTable[*src];
158 }
159
160private:
161 static const size_t kColorTableSize = sizeof(Sk4f[256]) + 12;
162 Sk4f convertPixel(SkPMColor pmColor) {
163 Sk4f pixel = to_4f(pmColor);
164 float alpha = get_alpha(pixel);
165 if (alpha != 0.0f) {
166 float invAlpha = 1.0f / alpha;
167 Sk4f normalize = {invAlpha, invAlpha, invAlpha, 1.0f / 255.0f};
168 pixel = pixel * normalize;
reeddabe5d32016-06-21 10:28:14 -0700169 if (gammaType == kSRGB_SkGammaType) {
herb15332a82016-05-12 11:37:00 -0700170 pixel = linear_to_srgb(pixel);
171 }
172 return pixel;
173 } else {
174 return Sk4f{0.0f};
175 }
176 }
177 SkAutoMalloc fColorTableStorage{kColorTableSize};
178 Sk4f* fColorTable;
179};
180
reeddabe5d32016-06-21 10:28:14 -0700181template <SkGammaType gammaType>
182class PixelGetter<kGray_8_SkColorType, gammaType> {
herb670f01f2016-05-13 10:04:46 -0700183public:
184 using Element = uint8_t;
185 PixelGetter(const SkPixmap& srcPixmap) { }
186
187 Sk4f getPixelAt(const uint8_t* src) {
188 float gray = *src * (1.0f/255.0f);
189 Sk4f pixel = Sk4f{gray, gray, gray, 1.0f};
reeddabe5d32016-06-21 10:28:14 -0700190 return gammaType == kSRGB_SkGammaType
herb670f01f2016-05-13 10:04:46 -0700191 ? srgb_to_linear(pixel)
192 : pixel;
193 }
194};
195
herb15332a82016-05-12 11:37:00 -0700196template <>
reeddabe5d32016-06-21 10:28:14 -0700197class PixelGetter<kRGBA_F16_SkColorType, kLinear_SkGammaType> {
herb15332a82016-05-12 11:37:00 -0700198public:
199 using Element = uint64_t;
200 PixelGetter(const SkPixmap& srcPixmap) { }
201
202 Sk4f getPixelAt(const uint64_t* src) {
203 return SkHalfToFloat_01(*src);
204 }
205};
206
207////////////////////////////////////////////////////////////////////////////////////////////////////
208// PixelAccessor handles all the same plumbing for all the PixelGetters.
reeddabe5d32016-06-21 10:28:14 -0700209template <SkColorType colorType, SkGammaType gammaType>
herb15332a82016-05-12 11:37:00 -0700210class PixelAccessor {
reeddabe5d32016-06-21 10:28:14 -0700211 using Element = typename PixelGetter<colorType, gammaType>::Element;
herb15332a82016-05-12 11:37:00 -0700212public:
herb670f01f2016-05-13 10:04:46 -0700213 template <typename... Args>
214 PixelAccessor(const SkPixmap& srcPixmap, Args&&... args)
herb15332a82016-05-12 11:37:00 -0700215 : fSrc{static_cast<const Element*>(srcPixmap.addr())}
216 , fWidth{srcPixmap.rowBytesAsPixels()}
herb670f01f2016-05-13 10:04:46 -0700217 , fGetter{srcPixmap, std::move<Args>(args)...} { }
herb15332a82016-05-12 11:37:00 -0700218
219 void VECTORCALL getFewPixels(int n, Sk4s xs, Sk4s ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) {
220 Sk4i XIs = SkNx_cast<int, SkScalar>(xs);
221 Sk4i YIs = SkNx_cast<int, SkScalar>(ys);
222 Sk4i bufferLoc = YIs * fWidth + XIs;
223 switch (n) {
224 case 3:
225 *px2 = this->getPixelAt(bufferLoc[2]);
226 case 2:
227 *px1 = this->getPixelAt(bufferLoc[1]);
228 case 1:
229 *px0 = this->getPixelAt(bufferLoc[0]);
230 default:
231 break;
232 }
233 }
234
235 void VECTORCALL get4Pixels(Sk4s xs, Sk4s ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
236 Sk4i XIs = SkNx_cast<int, SkScalar>(xs);
237 Sk4i YIs = SkNx_cast<int, SkScalar>(ys);
238 Sk4i bufferLoc = YIs * fWidth + XIs;
239 *px0 = this->getPixelAt(bufferLoc[0]);
240 *px1 = this->getPixelAt(bufferLoc[1]);
241 *px2 = this->getPixelAt(bufferLoc[2]);
242 *px3 = this->getPixelAt(bufferLoc[3]);
243 }
244
245 void get4Pixels(const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
246 *px0 = this->getPixelFromRow(src, index + 0);
247 *px1 = this->getPixelFromRow(src, index + 1);
248 *px2 = this->getPixelFromRow(src, index + 2);
249 *px3 = this->getPixelFromRow(src, index + 3);
250 }
251
252 Sk4f getPixelFromRow(const void* row, int index) {
253 const Element* src = static_cast<const Element*>(row);
254 return fGetter.getPixelAt(src + index);
255 }
256
257 Sk4f getPixelAt(int index) {
258 return this->getPixelFromRow(fSrc, index);
259 }
260
261 const void* row(int y) const { return fSrc + y * fWidth[0]; }
262
263private:
264 const Element* const fSrc;
265 const Sk4i fWidth;
reeddabe5d32016-06-21 10:28:14 -0700266 PixelGetter<colorType, gammaType> fGetter;
herb15332a82016-05-12 11:37:00 -0700267};
268
herb7ccbc1a2016-06-09 09:05:00 -0700269// We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
270// We'll never re-use pixels, but we can at least load contiguous pixels.
271template <typename Next, typename Strategy>
272static void src_strategy_blend(Span span, Next* next, Strategy* strategy) {
273 SkPoint start;
274 SkScalar length;
275 int count;
276 std::tie(start, length, count) = span;
277 int ix = SkScalarFloorToInt(X(start));
278 const void* row = strategy->row((int)std::floor(Y(start)));
279 if (length > 0) {
280 while (count >= 4) {
281 Sk4f px0, px1, px2, px3;
282 strategy->get4Pixels(row, ix, &px0, &px1, &px2, &px3);
283 next->blend4Pixels(px0, px1, px2, px3);
284 ix += 4;
285 count -= 4;
286 }
287
288 while (count > 0) {
289 next->blendPixel(strategy->getPixelFromRow(row, ix));
290 ix += 1;
291 count -= 1;
292 }
293 } else {
294 while (count >= 4) {
295 Sk4f px0, px1, px2, px3;
296 strategy->get4Pixels(row, ix - 3, &px3, &px2, &px1, &px0);
297 next->blend4Pixels(px0, px1, px2, px3);
298 ix -= 4;
299 count -= 4;
300 }
301
302 while (count > 0) {
303 next->blendPixel(strategy->getPixelFromRow(row, ix));
304 ix -= 1;
305 count -= 1;
306 }
307 }
308}
309
310// NearestNeighborSampler - use nearest neighbor filtering to create runs of destination pixels.
reeddabe5d32016-06-21 10:28:14 -0700311template<SkColorType colorType, SkGammaType gammaType, typename Next>
herb7ccbc1a2016-06-09 09:05:00 -0700312class NearestNeighborSampler : public SkLinearBitmapPipeline::SampleProcessorInterface {
herb6eff52a2016-03-23 09:00:33 -0700313public:
314 template<typename... Args>
herb7ccbc1a2016-06-09 09:05:00 -0700315 NearestNeighborSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next, Args&& ... args)
316 : fNext{next}, fStrategy{std::forward<Args>(args)...} { }
herb6eff52a2016-03-23 09:00:33 -0700317
herb7ccbc1a2016-06-09 09:05:00 -0700318 NearestNeighborSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next,
319 const NearestNeighborSampler& sampler)
320 : fNext{next}, fStrategy{sampler.fStrategy} { }
herb9e0efe52016-04-08 13:25:28 -0700321
herb7ccbc1a2016-06-09 09:05:00 -0700322 void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
herb6eff52a2016-03-23 09:00:33 -0700323 SkASSERT(0 < n && n < 4);
324 Sk4f px0, px1, px2;
325 fStrategy.getFewPixels(n, xs, ys, &px0, &px1, &px2);
herb9e0efe52016-04-08 13:25:28 -0700326 if (n >= 1) fNext->blendPixel(px0);
327 if (n >= 2) fNext->blendPixel(px1);
328 if (n >= 3) fNext->blendPixel(px2);
herb6eff52a2016-03-23 09:00:33 -0700329 }
330
herb7ccbc1a2016-06-09 09:05:00 -0700331 void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
herb6eff52a2016-03-23 09:00:33 -0700332 Sk4f px0, px1, px2, px3;
333 fStrategy.get4Pixels(xs, ys, &px0, &px1, &px2, &px3);
herb9e0efe52016-04-08 13:25:28 -0700334 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700335 }
336
herb7ccbc1a2016-06-09 09:05:00 -0700337 void pointSpan(Span span) override {
herb6eff52a2016-03-23 09:00:33 -0700338 SkASSERT(!span.isEmpty());
339 SkPoint start;
340 SkScalar length;
341 int count;
342 std::tie(start, length, count) = span;
343 SkScalar absLength = SkScalarAbs(length);
344 if (absLength < (count - 1)) {
herb7ccbc1a2016-06-09 09:05:00 -0700345 this->spanSlowRate(span);
herb6eff52a2016-03-23 09:00:33 -0700346 } else if (absLength == (count - 1)) {
herb7ccbc1a2016-06-09 09:05:00 -0700347 src_strategy_blend(span, fNext, &fStrategy);
herb6eff52a2016-03-23 09:00:33 -0700348 } else {
herb7ccbc1a2016-06-09 09:05:00 -0700349 this->spanFastRate(span);
herb6eff52a2016-03-23 09:00:33 -0700350 }
351 }
352
herb7ccbc1a2016-06-09 09:05:00 -0700353 void repeatSpan(Span span, int32_t repeatCount) override {
354 while (repeatCount > 0) {
355 this->pointSpan(span);
356 repeatCount--;
herb6eff52a2016-03-23 09:00:33 -0700357 }
358 }
359
herb7ccbc1a2016-06-09 09:05:00 -0700360 void VECTORCALL bilerpEdge(Sk4s xs, Sk4s ys) override {
361 SkFAIL("Using nearest neighbor sampler, but calling a bilerpEdge.");
362 }
363
364 void bilerpSpan(Span span, SkScalar y) override {
365 SkFAIL("Using nearest neighbor sampler, but calling a bilerpSpan.");
366 }
367
herb6eff52a2016-03-23 09:00:33 -0700368private:
369 // When moving through source space more slowly than dst space (zoomed in),
370 // we'll be sampling from the same source pixel more than once.
herb7ccbc1a2016-06-09 09:05:00 -0700371 void spanSlowRate(Span span) {
herb6eff52a2016-03-23 09:00:33 -0700372 SkPoint start;
373 SkScalar length;
374 int count;
375 std::tie(start, length, count) = span;
376 SkScalar x = X(start);
377 SkFixed fx = SkScalarToFixed(x);
378 SkScalar dx = length / (count - 1);
379 SkFixed fdx = SkScalarToFixed(dx);
380
381 const void* row = fStrategy.row((int)std::floor(Y(start)));
382 Next* next = fNext;
383
384 int ix = SkFixedFloorToInt(fx);
385 int prevIX = ix;
herbdd964892016-05-11 10:39:55 -0700386 Sk4f fpixel = fStrategy.getPixelFromRow(row, ix);
herb6eff52a2016-03-23 09:00:33 -0700387
388 // When dx is less than one, each pixel is used more than once. Using the fixed point fx
389 // allows the code to quickly check that the same pixel is being used. The code uses this
390 // same pixel check to do the sRGB and normalization only once.
391 auto getNextPixel = [&]() {
392 if (ix != prevIX) {
herbdd964892016-05-11 10:39:55 -0700393 fpixel = fStrategy.getPixelFromRow(row, ix);
herb6eff52a2016-03-23 09:00:33 -0700394 prevIX = ix;
395 }
396 fx += fdx;
397 ix = SkFixedFloorToInt(fx);
398 return fpixel;
399 };
400
401 while (count >= 4) {
402 Sk4f px0 = getNextPixel();
403 Sk4f px1 = getNextPixel();
404 Sk4f px2 = getNextPixel();
405 Sk4f px3 = getNextPixel();
herb9e0efe52016-04-08 13:25:28 -0700406 next->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700407 count -= 4;
408 }
409 while (count > 0) {
herb9e0efe52016-04-08 13:25:28 -0700410 next->blendPixel(getNextPixel());
herb6eff52a2016-03-23 09:00:33 -0700411 count -= 1;
412 }
413 }
414
415 // We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
416 // We'll never re-use pixels, but we can at least load contiguous pixels.
herb7ccbc1a2016-06-09 09:05:00 -0700417 void spanUnitRate(Span span) {
418 src_strategy_blend(span, fNext, &fStrategy);
herb6eff52a2016-03-23 09:00:33 -0700419 }
420
421 // We're moving through source space faster than dst (zoomed out),
422 // so we'll never reuse a source pixel or be able to do contiguous loads.
herb7ccbc1a2016-06-09 09:05:00 -0700423 void spanFastRate(Span span) {
424 span_fallback(span, this);
herb6eff52a2016-03-23 09:00:33 -0700425 }
426
reeddabe5d32016-06-21 10:28:14 -0700427 Next* const fNext;
428 PixelAccessor<colorType, gammaType> fStrategy;
herb7ccbc1a2016-06-09 09:05:00 -0700429};
430
herb7df9e4a2016-06-10 13:01:27 -0700431// -- BilerpSampler --------------------------------------------------------------------------------
herb7ccbc1a2016-06-09 09:05:00 -0700432// BilerpSampler - use a bilerp filter to create runs of destination pixels.
reeddabe5d32016-06-21 10:28:14 -0700433template<SkColorType colorType, SkGammaType gammaType, typename Next>
herb7ccbc1a2016-06-09 09:05:00 -0700434class BilerpSampler : public SkLinearBitmapPipeline::SampleProcessorInterface {
435public:
436 template<typename... Args>
437 BilerpSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next, Args&& ... args)
438 : fNext{next}, fStrategy{std::forward<Args>(args)...} { }
439
440 BilerpSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next,
441 const BilerpSampler& sampler)
442 : fNext{next}, fStrategy{sampler.fStrategy} { }
443
444 Sk4f bilerpNonEdgePixel(SkScalar x, SkScalar y) {
445 Sk4f px00, px10, px01, px11;
446
447 // bilerp4() expects xs, ys are the top-lefts of the 2x2 kernel.
448 Sk4f xs = Sk4f{x} - 0.5f;
449 Sk4f ys = Sk4f{y} - 0.5f;
450 Sk4f sampleXs = xs + Sk4f{0.0f, 1.0f, 0.0f, 1.0f};
451 Sk4f sampleYs = ys + Sk4f{0.0f, 0.0f, 1.0f, 1.0f};
452 fStrategy.get4Pixels(sampleXs, sampleYs, &px00, &px10, &px01, &px11);
453 return bilerp4(xs, ys, px00, px10, px01, px11);
454 }
455
456 void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
457 SkASSERT(0 < n && n < 4);
458 auto bilerpPixel = [&](int index) {
459 return this->bilerpNonEdgePixel(xs[index], ys[index]);
460 };
461
462 if (n >= 1) fNext->blendPixel(bilerpPixel(0));
463 if (n >= 2) fNext->blendPixel(bilerpPixel(1));
464 if (n >= 3) fNext->blendPixel(bilerpPixel(2));
465 }
466
467 void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
468 auto bilerpPixel = [&](int index) {
469 return this->bilerpNonEdgePixel(xs[index], ys[index]);
470 };
471 fNext->blend4Pixels(bilerpPixel(0), bilerpPixel(1), bilerpPixel(2), bilerpPixel(3));
472 }
473
474 void pointSpan(Span span) override {
475 this->bilerpSpan(span, span.startY());
476 }
477
478 void repeatSpan(Span span, int32_t repeatCount) override {
479 while (repeatCount > 0) {
480 this->pointSpan(span);
481 repeatCount--;
482 }
483 }
484
485 void VECTORCALL bilerpEdge(Sk4s sampleXs, Sk4s sampleYs) override {
486 Sk4f px00, px10, px01, px11;
487 Sk4f xs = Sk4f{sampleXs[0]};
488 Sk4f ys = Sk4f{sampleYs[0]};
489 fStrategy.get4Pixels(sampleXs, sampleYs, &px00, &px10, &px01, &px11);
490 Sk4f pixel = bilerp4(xs, ys, px00, px10, px01, px11);
491 fNext->blendPixel(pixel);
492 }
493
494 void bilerpSpan(Span span, SkScalar y) override {
495 SkASSERT(!span.isEmpty());
496 SkPoint start;
497 SkScalar length;
498 int count;
499 std::tie(start, length, count) = span;
500 SkScalar absLength = SkScalarAbs(length);
501 if (absLength == 0.0f) {
502 this->spanZeroRate(span, y);
503 } else if (absLength < (count - 1)) {
504 this->spanSlowRate(span, y);
505 } else if (absLength == (count - 1)) {
506 if (std::fmod(span.startX() - 0.5f, 1.0f) == 0.0f) {
507 if (std::fmod(span.startY() - 0.5f, 1.0f) == 0.0f) {
508 src_strategy_blend(span, fNext, &fStrategy);
509 } else {
510 this->spanUnitRateAlignedX(span, y);
511 }
512 } else {
513 this->spanUnitRate(span, y);
514 }
515 } else {
516 this->spanFastRate(span, y);
517 }
518 }
519
520private:
521 void spanZeroRate(Span span, SkScalar y1) {
herb6eff52a2016-03-23 09:00:33 -0700522 SkScalar y0 = span.startY() - 0.5f;
523 y1 += 0.5f;
524 int iy0 = SkScalarFloorToInt(y0);
525 SkScalar filterY1 = y0 - iy0;
526 SkScalar filterY0 = 1.0f - filterY1;
527 int iy1 = SkScalarFloorToInt(y1);
528 int ix = SkScalarFloorToInt(span.startX());
herbdd964892016-05-11 10:39:55 -0700529 Sk4f pixelY0 = fStrategy.getPixelFromRow(fStrategy.row(iy0), ix);
530 Sk4f pixelY1 = fStrategy.getPixelFromRow(fStrategy.row(iy1), ix);
herb6eff52a2016-03-23 09:00:33 -0700531 Sk4f filterPixel = pixelY0 * filterY0 + pixelY1 * filterY1;
532 int count = span.count();
533 while (count >= 4) {
herb9e0efe52016-04-08 13:25:28 -0700534 fNext->blend4Pixels(filterPixel, filterPixel, filterPixel, filterPixel);
herb6eff52a2016-03-23 09:00:33 -0700535 count -= 4;
536 }
537 while (count > 0) {
herb9e0efe52016-04-08 13:25:28 -0700538 fNext->blendPixel(filterPixel);
herb6eff52a2016-03-23 09:00:33 -0700539 count -= 1;
540 }
541 }
542
543 // When moving through source space more slowly than dst space (zoomed in),
544 // we'll be sampling from the same source pixel more than once.
herb7ccbc1a2016-06-09 09:05:00 -0700545 void spanSlowRate(Span span, SkScalar ry1) {
herb6eff52a2016-03-23 09:00:33 -0700546 SkPoint start;
547 SkScalar length;
548 int count;
549 std::tie(start, length, count) = span;
herbdd404832016-06-08 14:33:15 -0700550 SkFixed fx = SkScalarToFixed(X(start)-0.5f);
herb6eff52a2016-03-23 09:00:33 -0700551
552 SkFixed fdx = SkScalarToFixed(length / (count - 1));
herb6eff52a2016-03-23 09:00:33 -0700553
554 Sk4f xAdjust;
555 if (fdx >= 0) {
556 xAdjust = Sk4f{-1.0f};
557 } else {
558 xAdjust = Sk4f{1.0f};
559 }
560 int ix = SkFixedFloorToInt(fx);
561 int ioldx = ix;
562 Sk4f x{SkFixedToScalar(fx) - ix};
563 Sk4f dx{SkFixedToScalar(fdx)};
564 SkScalar ry0 = Y(start) - 0.5f;
565 ry1 += 0.5f;
566 SkScalar yFloor = std::floor(ry0);
567 Sk4f y1 = Sk4f{ry0 - yFloor};
568 Sk4f y0 = Sk4f{1.0f} - y1;
herb222f8ff2016-03-23 15:14:23 -0700569 const void* const row0 = fStrategy.row(SkScalarFloorToInt(ry0));
570 const void* const row1 = fStrategy.row(SkScalarFloorToInt(ry1));
herbdd964892016-05-11 10:39:55 -0700571 Sk4f fpixel00 = y0 * fStrategy.getPixelFromRow(row0, ix);
572 Sk4f fpixel01 = y1 * fStrategy.getPixelFromRow(row1, ix);
573 Sk4f fpixel10 = y0 * fStrategy.getPixelFromRow(row0, ix + 1);
574 Sk4f fpixel11 = y1 * fStrategy.getPixelFromRow(row1, ix + 1);
herb6eff52a2016-03-23 09:00:33 -0700575 auto getNextPixel = [&]() {
576 if (ix != ioldx) {
577 fpixel00 = fpixel10;
578 fpixel01 = fpixel11;
herbdd964892016-05-11 10:39:55 -0700579 fpixel10 = y0 * fStrategy.getPixelFromRow(row0, ix + 1);
580 fpixel11 = y1 * fStrategy.getPixelFromRow(row1, ix + 1);
herb6eff52a2016-03-23 09:00:33 -0700581 ioldx = ix;
582 x = x + xAdjust;
583 }
584
585 Sk4f x0, x1;
586 x0 = Sk4f{1.0f} - x;
587 x1 = x;
588 Sk4f fpixel = x0 * (fpixel00 + fpixel01) + x1 * (fpixel10 + fpixel11);
589 fx += fdx;
590 ix = SkFixedFloorToInt(fx);
591 x = x + dx;
592 return fpixel;
593 };
594
595 while (count >= 4) {
596 Sk4f fpixel0 = getNextPixel();
597 Sk4f fpixel1 = getNextPixel();
598 Sk4f fpixel2 = getNextPixel();
599 Sk4f fpixel3 = getNextPixel();
600
herb9e0efe52016-04-08 13:25:28 -0700601 fNext->blend4Pixels(fpixel0, fpixel1, fpixel2, fpixel3);
herb6eff52a2016-03-23 09:00:33 -0700602 count -= 4;
603 }
604
605 while (count > 0) {
herb9e0efe52016-04-08 13:25:28 -0700606 fNext->blendPixel(getNextPixel());
herb6eff52a2016-03-23 09:00:33 -0700607
608 count -= 1;
609 }
610 }
611
612 // We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
613 // We'll never re-use pixels, but we can at least load contiguous pixels.
herb7ccbc1a2016-06-09 09:05:00 -0700614 void spanUnitRate(Span span, SkScalar y1) {
herb6eff52a2016-03-23 09:00:33 -0700615 y1 += 0.5f;
616 SkScalar y0 = span.startY() - 0.5f;
617 int iy0 = SkScalarFloorToInt(y0);
618 SkScalar filterY1 = y0 - iy0;
619 SkScalar filterY0 = 1.0f - filterY1;
620 int iy1 = SkScalarFloorToInt(y1);
621 const void* rowY0 = fStrategy.row(iy0);
622 const void* rowY1 = fStrategy.row(iy1);
623 SkScalar x0 = span.startX() - 0.5f;
624 int ix0 = SkScalarFloorToInt(x0);
625 SkScalar filterX1 = x0 - ix0;
626 SkScalar filterX0 = 1.0f - filterX1;
627
628 auto getPixelY0 = [&]() {
herbdd964892016-05-11 10:39:55 -0700629 Sk4f px = fStrategy.getPixelFromRow(rowY0, ix0);
herb6eff52a2016-03-23 09:00:33 -0700630 return px * filterY0;
631 };
632
633 auto getPixelY1 = [&]() {
herbdd964892016-05-11 10:39:55 -0700634 Sk4f px = fStrategy.getPixelFromRow(rowY1, ix0);
herb6eff52a2016-03-23 09:00:33 -0700635 return px * filterY1;
636 };
637
638 auto get4PixelsY0 = [&](int ix, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
639 fStrategy.get4Pixels(rowY0, ix, px0, px1, px2, px3);
640 *px0 = *px0 * filterY0;
641 *px1 = *px1 * filterY0;
642 *px2 = *px2 * filterY0;
643 *px3 = *px3 * filterY0;
644 };
645
646 auto get4PixelsY1 = [&](int ix, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
647 fStrategy.get4Pixels(rowY1, ix, px0, px1, px2, px3);
648 *px0 = *px0 * filterY1;
649 *px1 = *px1 * filterY1;
650 *px2 = *px2 * filterY1;
651 *px3 = *px3 * filterY1;
652 };
653
654 auto lerp = [&](Sk4f& pixelX0, Sk4f& pixelX1) {
655 return pixelX0 * filterX0 + pixelX1 * filterX1;
656 };
657
658 // Mid making 4 unit rate.
659 Sk4f pxB = getPixelY0() + getPixelY1();
660 if (span.length() > 0) {
661 int count = span.count();
662 while (count >= 4) {
663 Sk4f px00, px10, px20, px30;
664 get4PixelsY0(ix0, &px00, &px10, &px20, &px30);
665 Sk4f px01, px11, px21, px31;
666 get4PixelsY1(ix0, &px01, &px11, &px21, &px31);
667 Sk4f pxS0 = px00 + px01;
668 Sk4f px0 = lerp(pxB, pxS0);
669 Sk4f pxS1 = px10 + px11;
670 Sk4f px1 = lerp(pxS0, pxS1);
671 Sk4f pxS2 = px20 + px21;
672 Sk4f px2 = lerp(pxS1, pxS2);
673 Sk4f pxS3 = px30 + px31;
674 Sk4f px3 = lerp(pxS2, pxS3);
675 pxB = pxS3;
herb9e0efe52016-04-08 13:25:28 -0700676 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700677 ix0 += 4;
678 count -= 4;
679 }
680 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700681 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix0);
682 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix0);
herb6eff52a2016-03-23 09:00:33 -0700683
herb9e0efe52016-04-08 13:25:28 -0700684 fNext->blendPixel(lerp(pixelY0, pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700685 ix0 += 1;
686 count -= 1;
687 }
688 } else {
689 int count = span.count();
690 while (count >= 4) {
691 Sk4f px00, px10, px20, px30;
692 get4PixelsY0(ix0 - 3, &px00, &px10, &px20, &px30);
693 Sk4f px01, px11, px21, px31;
694 get4PixelsY1(ix0 - 3, &px01, &px11, &px21, &px31);
695 Sk4f pxS3 = px30 + px31;
696 Sk4f px0 = lerp(pxS3, pxB);
697 Sk4f pxS2 = px20 + px21;
698 Sk4f px1 = lerp(pxS2, pxS3);
699 Sk4f pxS1 = px10 + px11;
700 Sk4f px2 = lerp(pxS1, pxS2);
701 Sk4f pxS0 = px00 + px01;
702 Sk4f px3 = lerp(pxS0, pxS1);
703 pxB = pxS0;
herb9e0efe52016-04-08 13:25:28 -0700704 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700705 ix0 -= 4;
706 count -= 4;
707 }
708 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700709 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix0);
710 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix0);
herb6eff52a2016-03-23 09:00:33 -0700711
herb9e0efe52016-04-08 13:25:28 -0700712 fNext->blendPixel(lerp(pixelY0, pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700713 ix0 -= 1;
714 count -= 1;
715 }
716 }
717 }
718
herb7ccbc1a2016-06-09 09:05:00 -0700719 void spanUnitRateAlignedX(Span span, SkScalar y1) {
herb6eff52a2016-03-23 09:00:33 -0700720 SkScalar y0 = span.startY() - 0.5f;
721 y1 += 0.5f;
722 int iy0 = SkScalarFloorToInt(y0);
723 SkScalar filterY1 = y0 - iy0;
724 SkScalar filterY0 = 1.0f - filterY1;
725 int iy1 = SkScalarFloorToInt(y1);
726 int ix = SkScalarFloorToInt(span.startX());
727 const void* rowY0 = fStrategy.row(iy0);
728 const void* rowY1 = fStrategy.row(iy1);
729 auto lerp = [&](Sk4f* pixelY0, Sk4f* pixelY1) {
730 return *pixelY0 * filterY0 + *pixelY1 * filterY1;
731 };
732
733 if (span.length() > 0) {
734 int count = span.count();
735 while (count >= 4) {
736 Sk4f px00, px10, px20, px30;
737 fStrategy.get4Pixels(rowY0, ix, &px00, &px10, &px20, &px30);
738 Sk4f px01, px11, px21, px31;
739 fStrategy.get4Pixels(rowY1, ix, &px01, &px11, &px21, &px31);
herb9e0efe52016-04-08 13:25:28 -0700740 fNext->blend4Pixels(
herb6eff52a2016-03-23 09:00:33 -0700741 lerp(&px00, &px01), lerp(&px10, &px11), lerp(&px20, &px21), lerp(&px30, &px31));
742 ix += 4;
743 count -= 4;
744 }
745 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700746 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix);
747 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix);
herb6eff52a2016-03-23 09:00:33 -0700748
herb9e0efe52016-04-08 13:25:28 -0700749 fNext->blendPixel(lerp(&pixelY0, &pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700750 ix += 1;
751 count -= 1;
752 }
753 } else {
754 int count = span.count();
755 while (count >= 4) {
756 Sk4f px00, px10, px20, px30;
757 fStrategy.get4Pixels(rowY0, ix - 3, &px30, &px20, &px10, &px00);
758 Sk4f px01, px11, px21, px31;
759 fStrategy.get4Pixels(rowY1, ix - 3, &px31, &px21, &px11, &px01);
herb9e0efe52016-04-08 13:25:28 -0700760 fNext->blend4Pixels(
herb6eff52a2016-03-23 09:00:33 -0700761 lerp(&px00, &px01), lerp(&px10, &px11), lerp(&px20, &px21), lerp(&px30, &px31));
762 ix -= 4;
763 count -= 4;
764 }
765 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700766 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix);
767 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix);
herb6eff52a2016-03-23 09:00:33 -0700768
herb9e0efe52016-04-08 13:25:28 -0700769 fNext->blendPixel(lerp(&pixelY0, &pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700770 ix -= 1;
771 count -= 1;
772 }
773 }
774 }
775
776 // We're moving through source space faster than dst (zoomed out),
777 // so we'll never reuse a source pixel or be able to do contiguous loads.
herb7ccbc1a2016-06-09 09:05:00 -0700778 void spanFastRate(Span span, SkScalar y1) {
herb6eff52a2016-03-23 09:00:33 -0700779 SkPoint start;
780 SkScalar length;
781 int count;
782 std::tie(start, length, count) = span;
783 SkScalar x = X(start);
784 SkScalar y = Y(start);
herb7ccbc1a2016-06-09 09:05:00 -0700785
herbdd404832016-06-08 14:33:15 -0700786 // In this sampler, it is assumed that if span.StartY() and y1 are the same then both
787 // y-lines are on the same tile.
788 if (y == y1) {
789 // Both y-lines are on the same tile.
herb7ccbc1a2016-06-09 09:05:00 -0700790 span_fallback(span, this);
herb6eff52a2016-03-23 09:00:33 -0700791 } else {
herbdd404832016-06-08 14:33:15 -0700792 // The y-lines are on different tiles.
herb6eff52a2016-03-23 09:00:33 -0700793 SkScalar dx = length / (count - 1);
794 Sk4f ys = {y - 0.5f, y - 0.5f, y1 + 0.5f, y1 + 0.5f};
795 while (count > 0) {
796 Sk4f xs = Sk4f{-0.5f, 0.5f, -0.5f, 0.5f} + Sk4f{x};
797 this->bilerpEdge(xs, ys);
798 x += dx;
799 count -= 1;
800 }
801 }
802 }
803
reeddabe5d32016-06-21 10:28:14 -0700804 Next* const fNext;
805 PixelAccessor<colorType, gammaType> fStrategy;
herb6eff52a2016-03-23 09:00:33 -0700806};
807
herb6eff52a2016-03-23 09:00:33 -0700808} // namespace
809
810#endif // SkLinearBitmapPipeline_sampler_DEFINED