blob: 930759f271ea1a108432a4ac4447209f794cccc7 [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.
58template <SkColorType colorType, SkColorProfileType colorProfile> class PixelGetter;
59
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 <>
65class PixelGetter<kAlpha_8_SkColorType, kLinear_SkColorProfileType> {
66public:
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
79template <SkColorProfileType colorProfile>
80class PixelGetter<kRGB_565_SkColorType, colorProfile> {
81public:
82 using Element = uint16_t;
83 PixelGetter(const SkPixmap& srcPixmap) { }
84
85 Sk4f getPixelAt(const uint16_t* src) {
86 SkPMColor pixel = SkPixel16ToPixel32(*src);
87 return colorProfile == kSRGB_SkColorProfileType
88 ? Sk4f_fromS32(pixel)
89 : Sk4f_fromL32(pixel);
90 }
91};
92
93template <SkColorProfileType colorProfile>
94class PixelGetter<kARGB_4444_SkColorType, colorProfile> {
95public:
96 using Element = uint16_t;
97 PixelGetter(const SkPixmap& srcPixmap) { }
98
99 Sk4f getPixelAt(const uint16_t* src) {
100 SkPMColor pixel = SkPixel4444ToPixel32(*src);
101 return colorProfile == kSRGB_SkColorProfileType
102 ? Sk4f_fromS32(pixel)
103 : Sk4f_fromL32(pixel);
104 }
105};
106
herb15332a82016-05-12 11:37:00 -0700107template <SkColorProfileType colorProfile>
108class PixelGetter<kRGBA_8888_SkColorType, colorProfile> {
109public:
110 using Element = uint32_t;
111 PixelGetter(const SkPixmap& srcPixmap) { }
112
113 Sk4f getPixelAt(const uint32_t* src) {
114 return colorProfile == kSRGB_SkColorProfileType
115 ? Sk4f_fromS32(*src)
116 : Sk4f_fromL32(*src);
117 }
118};
119
120template <SkColorProfileType colorProfile>
121class PixelGetter<kBGRA_8888_SkColorType, colorProfile> {
122public:
123 using Element = uint32_t;
124 PixelGetter(const SkPixmap& srcPixmap) { }
125
126 Sk4f getPixelAt(const uint32_t* src) {
127 Sk4f pixel = colorProfile == kSRGB_SkColorProfileType
128 ? 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
134template <SkColorProfileType colorProfile>
135class PixelGetter<kIndex_8_SkColorType, colorProfile> {
136public:
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;
169 if (colorProfile == kSRGB_SkColorProfileType) {
170 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
herb670f01f2016-05-13 10:04:46 -0700181template <SkColorProfileType colorProfile>
182class PixelGetter<kGray_8_SkColorType, colorProfile> {
183public:
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};
190 return colorProfile == kSRGB_SkColorProfileType
191 ? srgb_to_linear(pixel)
192 : pixel;
193 }
194};
195
herb15332a82016-05-12 11:37:00 -0700196template <>
197class PixelGetter<kRGBA_F16_SkColorType, kLinear_SkColorProfileType> {
198public:
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.
209template <SkColorType colorType, SkColorProfileType colorProfile>
210class PixelAccessor {
211 using Element = typename PixelGetter<colorType, colorProfile>::Element;
212public:
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;
266 PixelGetter<colorType, colorProfile> fGetter;
267};
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.
herb15332a82016-05-12 11:37:00 -0700311template<SkColorType colorType, SkColorProfileType colorProfile, 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
herb7ccbc1a2016-06-09 09:05:00 -0700427 Next* const fNext;
428 PixelAccessor<colorType, colorProfile> fStrategy;
429};
430
431// BilerpSampler - use a bilerp filter to create runs of destination pixels.
432template<SkColorType colorType, SkColorProfileType colorProfile, typename Next>
433class BilerpSampler : public SkLinearBitmapPipeline::SampleProcessorInterface {
434public:
435 template<typename... Args>
436 BilerpSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next, Args&& ... args)
437 : fNext{next}, fStrategy{std::forward<Args>(args)...} { }
438
439 BilerpSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next,
440 const BilerpSampler& sampler)
441 : fNext{next}, fStrategy{sampler.fStrategy} { }
442
443 Sk4f bilerpNonEdgePixel(SkScalar x, SkScalar y) {
444 Sk4f px00, px10, px01, px11;
445
446 // bilerp4() expects xs, ys are the top-lefts of the 2x2 kernel.
447 Sk4f xs = Sk4f{x} - 0.5f;
448 Sk4f ys = Sk4f{y} - 0.5f;
449 Sk4f sampleXs = xs + Sk4f{0.0f, 1.0f, 0.0f, 1.0f};
450 Sk4f sampleYs = ys + Sk4f{0.0f, 0.0f, 1.0f, 1.0f};
451 fStrategy.get4Pixels(sampleXs, sampleYs, &px00, &px10, &px01, &px11);
452 return bilerp4(xs, ys, px00, px10, px01, px11);
453 }
454
455 void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override {
456 SkASSERT(0 < n && n < 4);
457 auto bilerpPixel = [&](int index) {
458 return this->bilerpNonEdgePixel(xs[index], ys[index]);
459 };
460
461 if (n >= 1) fNext->blendPixel(bilerpPixel(0));
462 if (n >= 2) fNext->blendPixel(bilerpPixel(1));
463 if (n >= 3) fNext->blendPixel(bilerpPixel(2));
464 }
465
466 void VECTORCALL pointList4(Sk4s xs, Sk4s ys) override {
467 auto bilerpPixel = [&](int index) {
468 return this->bilerpNonEdgePixel(xs[index], ys[index]);
469 };
470 fNext->blend4Pixels(bilerpPixel(0), bilerpPixel(1), bilerpPixel(2), bilerpPixel(3));
471 }
472
473 void pointSpan(Span span) override {
474 this->bilerpSpan(span, span.startY());
475 }
476
477 void repeatSpan(Span span, int32_t repeatCount) override {
478 while (repeatCount > 0) {
479 this->pointSpan(span);
480 repeatCount--;
481 }
482 }
483
484 void VECTORCALL bilerpEdge(Sk4s sampleXs, Sk4s sampleYs) override {
485 Sk4f px00, px10, px01, px11;
486 Sk4f xs = Sk4f{sampleXs[0]};
487 Sk4f ys = Sk4f{sampleYs[0]};
488 fStrategy.get4Pixels(sampleXs, sampleYs, &px00, &px10, &px01, &px11);
489 Sk4f pixel = bilerp4(xs, ys, px00, px10, px01, px11);
490 fNext->blendPixel(pixel);
491 }
492
493 void bilerpSpan(Span span, SkScalar y) override {
494 SkASSERT(!span.isEmpty());
495 SkPoint start;
496 SkScalar length;
497 int count;
498 std::tie(start, length, count) = span;
499 SkScalar absLength = SkScalarAbs(length);
500 if (absLength == 0.0f) {
501 this->spanZeroRate(span, y);
502 } else if (absLength < (count - 1)) {
503 this->spanSlowRate(span, y);
504 } else if (absLength == (count - 1)) {
505 if (std::fmod(span.startX() - 0.5f, 1.0f) == 0.0f) {
506 if (std::fmod(span.startY() - 0.5f, 1.0f) == 0.0f) {
507 src_strategy_blend(span, fNext, &fStrategy);
508 } else {
509 this->spanUnitRateAlignedX(span, y);
510 }
511 } else {
512 this->spanUnitRate(span, y);
513 }
514 } else {
515 this->spanFastRate(span, y);
516 }
517 }
518
519private:
520 void spanZeroRate(Span span, SkScalar y1) {
herb6eff52a2016-03-23 09:00:33 -0700521 SkScalar y0 = span.startY() - 0.5f;
522 y1 += 0.5f;
523 int iy0 = SkScalarFloorToInt(y0);
524 SkScalar filterY1 = y0 - iy0;
525 SkScalar filterY0 = 1.0f - filterY1;
526 int iy1 = SkScalarFloorToInt(y1);
527 int ix = SkScalarFloorToInt(span.startX());
herbdd964892016-05-11 10:39:55 -0700528 Sk4f pixelY0 = fStrategy.getPixelFromRow(fStrategy.row(iy0), ix);
529 Sk4f pixelY1 = fStrategy.getPixelFromRow(fStrategy.row(iy1), ix);
herb6eff52a2016-03-23 09:00:33 -0700530 Sk4f filterPixel = pixelY0 * filterY0 + pixelY1 * filterY1;
531 int count = span.count();
532 while (count >= 4) {
herb9e0efe52016-04-08 13:25:28 -0700533 fNext->blend4Pixels(filterPixel, filterPixel, filterPixel, filterPixel);
herb6eff52a2016-03-23 09:00:33 -0700534 count -= 4;
535 }
536 while (count > 0) {
herb9e0efe52016-04-08 13:25:28 -0700537 fNext->blendPixel(filterPixel);
herb6eff52a2016-03-23 09:00:33 -0700538 count -= 1;
539 }
540 }
541
542 // When moving through source space more slowly than dst space (zoomed in),
543 // we'll be sampling from the same source pixel more than once.
herb7ccbc1a2016-06-09 09:05:00 -0700544 void spanSlowRate(Span span, SkScalar ry1) {
herb6eff52a2016-03-23 09:00:33 -0700545 SkPoint start;
546 SkScalar length;
547 int count;
548 std::tie(start, length, count) = span;
herbdd404832016-06-08 14:33:15 -0700549 SkFixed fx = SkScalarToFixed(X(start)-0.5f);
herb6eff52a2016-03-23 09:00:33 -0700550
551 SkFixed fdx = SkScalarToFixed(length / (count - 1));
herb6eff52a2016-03-23 09:00:33 -0700552
553 Sk4f xAdjust;
554 if (fdx >= 0) {
555 xAdjust = Sk4f{-1.0f};
556 } else {
557 xAdjust = Sk4f{1.0f};
558 }
559 int ix = SkFixedFloorToInt(fx);
560 int ioldx = ix;
561 Sk4f x{SkFixedToScalar(fx) - ix};
562 Sk4f dx{SkFixedToScalar(fdx)};
563 SkScalar ry0 = Y(start) - 0.5f;
564 ry1 += 0.5f;
565 SkScalar yFloor = std::floor(ry0);
566 Sk4f y1 = Sk4f{ry0 - yFloor};
567 Sk4f y0 = Sk4f{1.0f} - y1;
herb222f8ff2016-03-23 15:14:23 -0700568 const void* const row0 = fStrategy.row(SkScalarFloorToInt(ry0));
569 const void* const row1 = fStrategy.row(SkScalarFloorToInt(ry1));
herbdd964892016-05-11 10:39:55 -0700570 Sk4f fpixel00 = y0 * fStrategy.getPixelFromRow(row0, ix);
571 Sk4f fpixel01 = y1 * fStrategy.getPixelFromRow(row1, ix);
572 Sk4f fpixel10 = y0 * fStrategy.getPixelFromRow(row0, ix + 1);
573 Sk4f fpixel11 = y1 * fStrategy.getPixelFromRow(row1, ix + 1);
herb6eff52a2016-03-23 09:00:33 -0700574 auto getNextPixel = [&]() {
575 if (ix != ioldx) {
576 fpixel00 = fpixel10;
577 fpixel01 = fpixel11;
herbdd964892016-05-11 10:39:55 -0700578 fpixel10 = y0 * fStrategy.getPixelFromRow(row0, ix + 1);
579 fpixel11 = y1 * fStrategy.getPixelFromRow(row1, ix + 1);
herb6eff52a2016-03-23 09:00:33 -0700580 ioldx = ix;
581 x = x + xAdjust;
582 }
583
584 Sk4f x0, x1;
585 x0 = Sk4f{1.0f} - x;
586 x1 = x;
587 Sk4f fpixel = x0 * (fpixel00 + fpixel01) + x1 * (fpixel10 + fpixel11);
588 fx += fdx;
589 ix = SkFixedFloorToInt(fx);
590 x = x + dx;
591 return fpixel;
592 };
593
594 while (count >= 4) {
595 Sk4f fpixel0 = getNextPixel();
596 Sk4f fpixel1 = getNextPixel();
597 Sk4f fpixel2 = getNextPixel();
598 Sk4f fpixel3 = getNextPixel();
599
herb9e0efe52016-04-08 13:25:28 -0700600 fNext->blend4Pixels(fpixel0, fpixel1, fpixel2, fpixel3);
herb6eff52a2016-03-23 09:00:33 -0700601 count -= 4;
602 }
603
604 while (count > 0) {
herb9e0efe52016-04-08 13:25:28 -0700605 fNext->blendPixel(getNextPixel());
herb6eff52a2016-03-23 09:00:33 -0700606
607 count -= 1;
608 }
609 }
610
611 // We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
612 // We'll never re-use pixels, but we can at least load contiguous pixels.
herb7ccbc1a2016-06-09 09:05:00 -0700613 void spanUnitRate(Span span, SkScalar y1) {
herb6eff52a2016-03-23 09:00:33 -0700614 y1 += 0.5f;
615 SkScalar y0 = span.startY() - 0.5f;
616 int iy0 = SkScalarFloorToInt(y0);
617 SkScalar filterY1 = y0 - iy0;
618 SkScalar filterY0 = 1.0f - filterY1;
619 int iy1 = SkScalarFloorToInt(y1);
620 const void* rowY0 = fStrategy.row(iy0);
621 const void* rowY1 = fStrategy.row(iy1);
622 SkScalar x0 = span.startX() - 0.5f;
623 int ix0 = SkScalarFloorToInt(x0);
624 SkScalar filterX1 = x0 - ix0;
625 SkScalar filterX0 = 1.0f - filterX1;
626
627 auto getPixelY0 = [&]() {
herbdd964892016-05-11 10:39:55 -0700628 Sk4f px = fStrategy.getPixelFromRow(rowY0, ix0);
herb6eff52a2016-03-23 09:00:33 -0700629 return px * filterY0;
630 };
631
632 auto getPixelY1 = [&]() {
herbdd964892016-05-11 10:39:55 -0700633 Sk4f px = fStrategy.getPixelFromRow(rowY1, ix0);
herb6eff52a2016-03-23 09:00:33 -0700634 return px * filterY1;
635 };
636
637 auto get4PixelsY0 = [&](int ix, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
638 fStrategy.get4Pixels(rowY0, ix, px0, px1, px2, px3);
639 *px0 = *px0 * filterY0;
640 *px1 = *px1 * filterY0;
641 *px2 = *px2 * filterY0;
642 *px3 = *px3 * filterY0;
643 };
644
645 auto get4PixelsY1 = [&](int ix, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
646 fStrategy.get4Pixels(rowY1, ix, px0, px1, px2, px3);
647 *px0 = *px0 * filterY1;
648 *px1 = *px1 * filterY1;
649 *px2 = *px2 * filterY1;
650 *px3 = *px3 * filterY1;
651 };
652
653 auto lerp = [&](Sk4f& pixelX0, Sk4f& pixelX1) {
654 return pixelX0 * filterX0 + pixelX1 * filterX1;
655 };
656
657 // Mid making 4 unit rate.
658 Sk4f pxB = getPixelY0() + getPixelY1();
659 if (span.length() > 0) {
660 int count = span.count();
661 while (count >= 4) {
662 Sk4f px00, px10, px20, px30;
663 get4PixelsY0(ix0, &px00, &px10, &px20, &px30);
664 Sk4f px01, px11, px21, px31;
665 get4PixelsY1(ix0, &px01, &px11, &px21, &px31);
666 Sk4f pxS0 = px00 + px01;
667 Sk4f px0 = lerp(pxB, pxS0);
668 Sk4f pxS1 = px10 + px11;
669 Sk4f px1 = lerp(pxS0, pxS1);
670 Sk4f pxS2 = px20 + px21;
671 Sk4f px2 = lerp(pxS1, pxS2);
672 Sk4f pxS3 = px30 + px31;
673 Sk4f px3 = lerp(pxS2, pxS3);
674 pxB = pxS3;
herb9e0efe52016-04-08 13:25:28 -0700675 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700676 ix0 += 4;
677 count -= 4;
678 }
679 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700680 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix0);
681 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix0);
herb6eff52a2016-03-23 09:00:33 -0700682
herb9e0efe52016-04-08 13:25:28 -0700683 fNext->blendPixel(lerp(pixelY0, pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700684 ix0 += 1;
685 count -= 1;
686 }
687 } else {
688 int count = span.count();
689 while (count >= 4) {
690 Sk4f px00, px10, px20, px30;
691 get4PixelsY0(ix0 - 3, &px00, &px10, &px20, &px30);
692 Sk4f px01, px11, px21, px31;
693 get4PixelsY1(ix0 - 3, &px01, &px11, &px21, &px31);
694 Sk4f pxS3 = px30 + px31;
695 Sk4f px0 = lerp(pxS3, pxB);
696 Sk4f pxS2 = px20 + px21;
697 Sk4f px1 = lerp(pxS2, pxS3);
698 Sk4f pxS1 = px10 + px11;
699 Sk4f px2 = lerp(pxS1, pxS2);
700 Sk4f pxS0 = px00 + px01;
701 Sk4f px3 = lerp(pxS0, pxS1);
702 pxB = pxS0;
herb9e0efe52016-04-08 13:25:28 -0700703 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700704 ix0 -= 4;
705 count -= 4;
706 }
707 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700708 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix0);
709 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix0);
herb6eff52a2016-03-23 09:00:33 -0700710
herb9e0efe52016-04-08 13:25:28 -0700711 fNext->blendPixel(lerp(pixelY0, pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700712 ix0 -= 1;
713 count -= 1;
714 }
715 }
716 }
717
herb7ccbc1a2016-06-09 09:05:00 -0700718 void spanUnitRateAlignedX(Span span, SkScalar y1) {
herb6eff52a2016-03-23 09:00:33 -0700719 SkScalar y0 = span.startY() - 0.5f;
720 y1 += 0.5f;
721 int iy0 = SkScalarFloorToInt(y0);
722 SkScalar filterY1 = y0 - iy0;
723 SkScalar filterY0 = 1.0f - filterY1;
724 int iy1 = SkScalarFloorToInt(y1);
725 int ix = SkScalarFloorToInt(span.startX());
726 const void* rowY0 = fStrategy.row(iy0);
727 const void* rowY1 = fStrategy.row(iy1);
728 auto lerp = [&](Sk4f* pixelY0, Sk4f* pixelY1) {
729 return *pixelY0 * filterY0 + *pixelY1 * filterY1;
730 };
731
732 if (span.length() > 0) {
733 int count = span.count();
734 while (count >= 4) {
735 Sk4f px00, px10, px20, px30;
736 fStrategy.get4Pixels(rowY0, ix, &px00, &px10, &px20, &px30);
737 Sk4f px01, px11, px21, px31;
738 fStrategy.get4Pixels(rowY1, ix, &px01, &px11, &px21, &px31);
herb9e0efe52016-04-08 13:25:28 -0700739 fNext->blend4Pixels(
herb6eff52a2016-03-23 09:00:33 -0700740 lerp(&px00, &px01), lerp(&px10, &px11), lerp(&px20, &px21), lerp(&px30, &px31));
741 ix += 4;
742 count -= 4;
743 }
744 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700745 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix);
746 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix);
herb6eff52a2016-03-23 09:00:33 -0700747
herb9e0efe52016-04-08 13:25:28 -0700748 fNext->blendPixel(lerp(&pixelY0, &pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700749 ix += 1;
750 count -= 1;
751 }
752 } else {
753 int count = span.count();
754 while (count >= 4) {
755 Sk4f px00, px10, px20, px30;
756 fStrategy.get4Pixels(rowY0, ix - 3, &px30, &px20, &px10, &px00);
757 Sk4f px01, px11, px21, px31;
758 fStrategy.get4Pixels(rowY1, ix - 3, &px31, &px21, &px11, &px01);
herb9e0efe52016-04-08 13:25:28 -0700759 fNext->blend4Pixels(
herb6eff52a2016-03-23 09:00:33 -0700760 lerp(&px00, &px01), lerp(&px10, &px11), lerp(&px20, &px21), lerp(&px30, &px31));
761 ix -= 4;
762 count -= 4;
763 }
764 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700765 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix);
766 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix);
herb6eff52a2016-03-23 09:00:33 -0700767
herb9e0efe52016-04-08 13:25:28 -0700768 fNext->blendPixel(lerp(&pixelY0, &pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700769 ix -= 1;
770 count -= 1;
771 }
772 }
773 }
774
775 // We're moving through source space faster than dst (zoomed out),
776 // so we'll never reuse a source pixel or be able to do contiguous loads.
herb7ccbc1a2016-06-09 09:05:00 -0700777 void spanFastRate(Span span, SkScalar y1) {
herb6eff52a2016-03-23 09:00:33 -0700778 SkPoint start;
779 SkScalar length;
780 int count;
781 std::tie(start, length, count) = span;
782 SkScalar x = X(start);
783 SkScalar y = Y(start);
herb7ccbc1a2016-06-09 09:05:00 -0700784
herbdd404832016-06-08 14:33:15 -0700785 // In this sampler, it is assumed that if span.StartY() and y1 are the same then both
786 // y-lines are on the same tile.
787 if (y == y1) {
788 // Both y-lines are on the same tile.
herb7ccbc1a2016-06-09 09:05:00 -0700789 span_fallback(span, this);
herb6eff52a2016-03-23 09:00:33 -0700790 } else {
herbdd404832016-06-08 14:33:15 -0700791 // The y-lines are on different tiles.
herb6eff52a2016-03-23 09:00:33 -0700792 SkScalar dx = length / (count - 1);
793 Sk4f ys = {y - 0.5f, y - 0.5f, y1 + 0.5f, y1 + 0.5f};
794 while (count > 0) {
795 Sk4f xs = Sk4f{-0.5f, 0.5f, -0.5f, 0.5f} + Sk4f{x};
796 this->bilerpEdge(xs, ys);
797 x += dx;
798 count -= 1;
799 }
800 }
801 }
802
herb670f01f2016-05-13 10:04:46 -0700803 Next* const fNext;
herb15332a82016-05-12 11:37:00 -0700804 PixelAccessor<colorType, colorProfile> fStrategy;
herb6eff52a2016-03-23 09:00:33 -0700805};
806
herb6eff52a2016-03-23 09:00:33 -0700807} // namespace
808
809#endif // SkLinearBitmapPipeline_sampler_DEFINED