blob: 78af038660f12094e3843644bfca58468ba4ef32 [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
benjaminwagner6c71e0a2016-04-07 08:49:31 -070013#include "SkFixed.h"
herbd5f2e2e2016-04-14 11:16:44 -070014#include "SkHalf.h"
herb6eff52a2016-03-23 09:00:33 -070015#include "SkLinearBitmapPipeline_core.h"
herbcf05dcd2016-05-11 11:53:36 -070016#include "SkPM4fPriv.h"
herb6eff52a2016-03-23 09:00:33 -070017
18namespace {
19// Explaination of the math:
20// 1 - x x
21// +--------+--------+
22// | | |
23// 1 - y | px00 | px10 |
24// | | |
25// +--------+--------+
26// | | |
27// y | px01 | px11 |
28// | | |
29// +--------+--------+
30//
31//
32// Given a pixelxy each is multiplied by a different factor derived from the fractional part of x
33// and y:
34// * px00 -> (1 - x)(1 - y) = 1 - x - y + xy
35// * px10 -> x(1 - y) = x - xy
36// * px01 -> (1 - x)y = y - xy
37// * px11 -> xy
38// So x * y is calculated first and then used to calculate all the other factors.
39static Sk4s VECTORCALL bilerp4(Sk4s xs, Sk4s ys, Sk4f px00, Sk4f px10,
40 Sk4f px01, Sk4f px11) {
41 // Calculate fractional xs and ys.
42 Sk4s fxs = xs - xs.floor();
43 Sk4s fys = ys - ys.floor();
44 Sk4s fxys{fxs * fys};
45 Sk4f sum = px11 * fxys;
46 sum = sum + px01 * (fys - fxys);
47 sum = sum + px10 * (fxs - fxys);
48 sum = sum + px00 * (Sk4f{1.0f} - fxs - fys + fxys);
49 return sum;
50}
51
herb15332a82016-05-12 11:37:00 -070052////////////////////////////////////////////////////////////////////////////////////////////////////
53// PixelGetter is the lowest level interface to the source data. There is a PixelGetter for each
54// of the different SkColorTypes.
55template <SkColorType colorType, SkColorProfileType colorProfile> class PixelGetter;
56
57template <SkColorProfileType colorProfile>
58class PixelGetter<kRGBA_8888_SkColorType, colorProfile> {
59public:
60 using Element = uint32_t;
61 PixelGetter(const SkPixmap& srcPixmap) { }
62
63 Sk4f getPixelAt(const uint32_t* src) {
64 return colorProfile == kSRGB_SkColorProfileType
65 ? Sk4f_fromS32(*src)
66 : Sk4f_fromL32(*src);
67 }
68};
69
70template <SkColorProfileType colorProfile>
71class PixelGetter<kBGRA_8888_SkColorType, colorProfile> {
72public:
73 using Element = uint32_t;
74 PixelGetter(const SkPixmap& srcPixmap) { }
75
76 Sk4f getPixelAt(const uint32_t* src) {
77 Sk4f pixel = colorProfile == kSRGB_SkColorProfileType
78 ? Sk4f_fromS32(*src)
79 : Sk4f_fromL32(*src);
80 return SkNx_shuffle<2, 1, 0, 3>(pixel);
81 }
82};
83
84template <SkColorProfileType colorProfile>
85class PixelGetter<kIndex_8_SkColorType, colorProfile> {
86public:
87 using Element = uint8_t;
88 PixelGetter(const SkPixmap& srcPixmap) {
89 SkColorTable* skColorTable = srcPixmap.ctable();
90 SkASSERT(skColorTable != nullptr);
91
92 fColorTable = (Sk4f*)SkAlign16((intptr_t)fColorTableStorage.get());
93 for (int i = 0; i < skColorTable->count(); i++) {
94 fColorTable[i] = this->convertPixel((*skColorTable)[i]);
95 }
96 }
97
98 PixelGetter(const PixelGetter& strategy) {
99 fColorTable = (Sk4f*)SkAlign16((intptr_t)fColorTableStorage.get());
100 // TODO: figure out the count.
101 for (int i = 0; i < 256; i++) {
102 fColorTable[i] = strategy.fColorTable[i];
103 }
104 }
105
106 Sk4f getPixelAt(const uint8_t* src) {
107 return fColorTable[*src];
108 }
109
110private:
111 static const size_t kColorTableSize = sizeof(Sk4f[256]) + 12;
112 Sk4f convertPixel(SkPMColor pmColor) {
113 Sk4f pixel = to_4f(pmColor);
114 float alpha = get_alpha(pixel);
115 if (alpha != 0.0f) {
116 float invAlpha = 1.0f / alpha;
117 Sk4f normalize = {invAlpha, invAlpha, invAlpha, 1.0f / 255.0f};
118 pixel = pixel * normalize;
119 if (colorProfile == kSRGB_SkColorProfileType) {
120 pixel = linear_to_srgb(pixel);
121 }
122 return pixel;
123 } else {
124 return Sk4f{0.0f};
125 }
126 }
127 SkAutoMalloc fColorTableStorage{kColorTableSize};
128 Sk4f* fColorTable;
129};
130
131template <>
132class PixelGetter<kRGBA_F16_SkColorType, kLinear_SkColorProfileType> {
133public:
134 using Element = uint64_t;
135 PixelGetter(const SkPixmap& srcPixmap) { }
136
137 Sk4f getPixelAt(const uint64_t* src) {
138 return SkHalfToFloat_01(*src);
139 }
140};
141
142////////////////////////////////////////////////////////////////////////////////////////////////////
143// PixelAccessor handles all the same plumbing for all the PixelGetters.
144template <SkColorType colorType, SkColorProfileType colorProfile>
145class PixelAccessor {
146 using Element = typename PixelGetter<colorType, colorProfile>::Element;
147public:
148 PixelAccessor(const SkPixmap& srcPixmap)
149 : fSrc{static_cast<const Element*>(srcPixmap.addr())}
150 , fWidth{srcPixmap.rowBytesAsPixels()}
151 , fGetter{srcPixmap} { }
152
153 void VECTORCALL getFewPixels(int n, Sk4s xs, Sk4s ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) {
154 Sk4i XIs = SkNx_cast<int, SkScalar>(xs);
155 Sk4i YIs = SkNx_cast<int, SkScalar>(ys);
156 Sk4i bufferLoc = YIs * fWidth + XIs;
157 switch (n) {
158 case 3:
159 *px2 = this->getPixelAt(bufferLoc[2]);
160 case 2:
161 *px1 = this->getPixelAt(bufferLoc[1]);
162 case 1:
163 *px0 = this->getPixelAt(bufferLoc[0]);
164 default:
165 break;
166 }
167 }
168
169 void VECTORCALL get4Pixels(Sk4s xs, Sk4s ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
170 Sk4i XIs = SkNx_cast<int, SkScalar>(xs);
171 Sk4i YIs = SkNx_cast<int, SkScalar>(ys);
172 Sk4i bufferLoc = YIs * fWidth + XIs;
173 *px0 = this->getPixelAt(bufferLoc[0]);
174 *px1 = this->getPixelAt(bufferLoc[1]);
175 *px2 = this->getPixelAt(bufferLoc[2]);
176 *px3 = this->getPixelAt(bufferLoc[3]);
177 }
178
179 void get4Pixels(const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
180 *px0 = this->getPixelFromRow(src, index + 0);
181 *px1 = this->getPixelFromRow(src, index + 1);
182 *px2 = this->getPixelFromRow(src, index + 2);
183 *px3 = this->getPixelFromRow(src, index + 3);
184 }
185
186 Sk4f getPixelFromRow(const void* row, int index) {
187 const Element* src = static_cast<const Element*>(row);
188 return fGetter.getPixelAt(src + index);
189 }
190
191 Sk4f getPixelAt(int index) {
192 return this->getPixelFromRow(fSrc, index);
193 }
194
195 const void* row(int y) const { return fSrc + y * fWidth[0]; }
196
197private:
198 const Element* const fSrc;
199 const Sk4i fWidth;
200 PixelGetter<colorType, colorProfile> fGetter;
201};
202
203////////////////////////////////////////////////////////////////////////////////////////////////////
204// GeneralSampler handles all the different sampling scenarios. It makes runtime decisions to
205// choose the fastest stratagy given a particular job. It ultimately uses PixelGetters to access
206// the pixels.
207template<SkColorType colorType, SkColorProfileType colorProfile, typename Next>
herb6eff52a2016-03-23 09:00:33 -0700208class GeneralSampler {
209public:
210 template<typename... Args>
herb9e0efe52016-04-08 13:25:28 -0700211 GeneralSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next, Args&& ... args)
herb6eff52a2016-03-23 09:00:33 -0700212 : fNext{next}, fStrategy{std::forward<Args>(args)...} { }
213
herb9e0efe52016-04-08 13:25:28 -0700214 GeneralSampler(SkLinearBitmapPipeline::BlendProcessorInterface* next,
215 const GeneralSampler& sampler)
216 : fNext{next}, fStrategy{sampler.fStrategy} { }
217
herb6eff52a2016-03-23 09:00:33 -0700218 void VECTORCALL nearestListFew(int n, Sk4s xs, Sk4s ys) {
219 SkASSERT(0 < n && n < 4);
220 Sk4f px0, px1, px2;
221 fStrategy.getFewPixels(n, xs, ys, &px0, &px1, &px2);
herb9e0efe52016-04-08 13:25:28 -0700222 if (n >= 1) fNext->blendPixel(px0);
223 if (n >= 2) fNext->blendPixel(px1);
224 if (n >= 3) fNext->blendPixel(px2);
herb6eff52a2016-03-23 09:00:33 -0700225 }
226
227 void VECTORCALL nearestList4(Sk4s xs, Sk4s ys) {
228 Sk4f px0, px1, px2, px3;
229 fStrategy.get4Pixels(xs, ys, &px0, &px1, &px2, &px3);
herb9e0efe52016-04-08 13:25:28 -0700230 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700231 }
232
233 void nearestSpan(Span span) {
234 SkASSERT(!span.isEmpty());
235 SkPoint start;
236 SkScalar length;
237 int count;
238 std::tie(start, length, count) = span;
239 SkScalar absLength = SkScalarAbs(length);
240 if (absLength < (count - 1)) {
241 this->nearestSpanSlowRate(span);
242 } else if (absLength == (count - 1)) {
243 this->nearestSpanUnitRate(span);
244 } else {
245 this->nearestSpanFastRate(span);
246 }
247 }
248
249 Sk4f bilerNonEdgePixel(SkScalar x, SkScalar y) {
250 Sk4f px00, px10, px01, px11;
251 Sk4f xs = Sk4f{x};
252 Sk4f ys = Sk4f{y};
253 Sk4f sampleXs = xs + Sk4f{-0.5f, 0.5f, -0.5f, 0.5f};
254 Sk4f sampleYs = ys + Sk4f{-0.5f, -0.5f, 0.5f, 0.5f};
255 fStrategy.get4Pixels(sampleXs, sampleYs, &px00, &px10, &px01, &px11);
256 return bilerp4(xs, ys, px00, px10, px01, px11);
257 }
258
259 void VECTORCALL bilerpListFew(int n, Sk4s xs, Sk4s ys) {
260 SkASSERT(0 < n && n < 4);
261 auto bilerpPixel = [&](int index) {
262 return this->bilerNonEdgePixel(xs[index], ys[index]);
263 };
264
herb9e0efe52016-04-08 13:25:28 -0700265 if (n >= 1) fNext->blendPixel(bilerpPixel(0));
266 if (n >= 2) fNext->blendPixel(bilerpPixel(1));
267 if (n >= 3) fNext->blendPixel(bilerpPixel(2));
herb6eff52a2016-03-23 09:00:33 -0700268 }
269
270 void VECTORCALL bilerpList4(Sk4s xs, Sk4s ys) {
271 auto bilerpPixel = [&](int index) {
272 return this->bilerNonEdgePixel(xs[index], ys[index]);
273 };
herb9e0efe52016-04-08 13:25:28 -0700274 fNext->blend4Pixels(bilerpPixel(0), bilerpPixel(1), bilerpPixel(2), bilerpPixel(3));
herb6eff52a2016-03-23 09:00:33 -0700275 }
276
277 void VECTORCALL bilerpEdge(Sk4s sampleXs, Sk4s sampleYs) {
278 Sk4f px00, px10, px01, px11;
279 Sk4f xs = Sk4f{sampleXs[0]};
280 Sk4f ys = Sk4f{sampleYs[0]};
281 fStrategy.get4Pixels(sampleXs, sampleYs, &px00, &px10, &px01, &px11);
282 Sk4f pixel = bilerp4(xs, ys, px00, px10, px01, px11);
herb9e0efe52016-04-08 13:25:28 -0700283 fNext->blendPixel(pixel);
herb6eff52a2016-03-23 09:00:33 -0700284 }
285
286 void bilerpSpan(Span span) {
287 this->bilerpSpanWithY(span, span.startY());
288 }
289
290 void bilerpSpanWithY(Span span, SkScalar y) {
291 SkASSERT(!span.isEmpty());
292 SkPoint start;
293 SkScalar length;
294 int count;
295 std::tie(start, length, count) = span;
296 SkScalar absLength = SkScalarAbs(length);
297 if (absLength == 0.0f) {
298 this->bilerpSpanZeroRate(span, y);
299 } else if (absLength < (count - 1)) {
300 this->bilerpSpanSlowRate(span, y);
301 } else if (absLength == (count - 1)) {
302 if (std::fmod(span.startX() - 0.5f, 1.0f) == 0.0f) {
303 if (std::fmod(span.startY() - 0.5f, 1.0f) == 0.0f) {
304 this->nearestSpanUnitRate(span);
305 } else {
306 this->bilerpSpanUnitRateAlignedX(span, y);
307 }
308 } else {
309 this->bilerpSpanUnitRate(span, y);
310 }
311 } else {
312 this->bilerpSpanFastRate(span, y);
313 }
314 }
315
316private:
317 // When moving through source space more slowly than dst space (zoomed in),
318 // we'll be sampling from the same source pixel more than once.
319 void nearestSpanSlowRate(Span span) {
320 SkPoint start;
321 SkScalar length;
322 int count;
323 std::tie(start, length, count) = span;
324 SkScalar x = X(start);
325 SkFixed fx = SkScalarToFixed(x);
326 SkScalar dx = length / (count - 1);
327 SkFixed fdx = SkScalarToFixed(dx);
328
329 const void* row = fStrategy.row((int)std::floor(Y(start)));
330 Next* next = fNext;
331
332 int ix = SkFixedFloorToInt(fx);
333 int prevIX = ix;
herbdd964892016-05-11 10:39:55 -0700334 Sk4f fpixel = fStrategy.getPixelFromRow(row, ix);
herb6eff52a2016-03-23 09:00:33 -0700335
336 // When dx is less than one, each pixel is used more than once. Using the fixed point fx
337 // allows the code to quickly check that the same pixel is being used. The code uses this
338 // same pixel check to do the sRGB and normalization only once.
339 auto getNextPixel = [&]() {
340 if (ix != prevIX) {
herbdd964892016-05-11 10:39:55 -0700341 fpixel = fStrategy.getPixelFromRow(row, ix);
herb6eff52a2016-03-23 09:00:33 -0700342 prevIX = ix;
343 }
344 fx += fdx;
345 ix = SkFixedFloorToInt(fx);
346 return fpixel;
347 };
348
349 while (count >= 4) {
350 Sk4f px0 = getNextPixel();
351 Sk4f px1 = getNextPixel();
352 Sk4f px2 = getNextPixel();
353 Sk4f px3 = getNextPixel();
herb9e0efe52016-04-08 13:25:28 -0700354 next->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700355 count -= 4;
356 }
357 while (count > 0) {
herb9e0efe52016-04-08 13:25:28 -0700358 next->blendPixel(getNextPixel());
herb6eff52a2016-03-23 09:00:33 -0700359 count -= 1;
360 }
361 }
362
363 // We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
364 // We'll never re-use pixels, but we can at least load contiguous pixels.
365 void nearestSpanUnitRate(Span span) {
366 SkPoint start;
367 SkScalar length;
368 int count;
369 std::tie(start, length, count) = span;
370 int ix = SkScalarFloorToInt(X(start));
371 const void* row = fStrategy.row((int)std::floor(Y(start)));
372 Next* next = fNext;
373 if (length > 0) {
374 while (count >= 4) {
375 Sk4f px0, px1, px2, px3;
376 fStrategy.get4Pixels(row, ix, &px0, &px1, &px2, &px3);
herb9e0efe52016-04-08 13:25:28 -0700377 next->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700378 ix += 4;
379 count -= 4;
380 }
381
382 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700383 next->blendPixel(fStrategy.getPixelFromRow(row, ix));
herb6eff52a2016-03-23 09:00:33 -0700384 ix += 1;
385 count -= 1;
386 }
387 } else {
388 while (count >= 4) {
389 Sk4f px0, px1, px2, px3;
390 fStrategy.get4Pixels(row, ix - 3, &px3, &px2, &px1, &px0);
herb9e0efe52016-04-08 13:25:28 -0700391 next->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700392 ix -= 4;
393 count -= 4;
394 }
395
396 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700397 next->blendPixel(fStrategy.getPixelFromRow(row, ix));
herb6eff52a2016-03-23 09:00:33 -0700398 ix -= 1;
399 count -= 1;
400 }
401 }
402 }
403
404 // We're moving through source space faster than dst (zoomed out),
405 // so we'll never reuse a source pixel or be able to do contiguous loads.
406 void nearestSpanFastRate(Span span) {
407 struct NearestWrapper {
408 void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) {
409 fSampler.nearestListFew(n, xs, ys);
410 }
411
412 void VECTORCALL pointList4(Sk4s xs, Sk4s ys) {
413 fSampler.nearestList4(xs, ys);
414 }
415
416 GeneralSampler& fSampler;
417 };
418 NearestWrapper wrapper{*this};
419 span_fallback(span, &wrapper);
420 }
421
422 void bilerpSpanZeroRate(Span span, SkScalar y1) {
423 SkScalar y0 = span.startY() - 0.5f;
424 y1 += 0.5f;
425 int iy0 = SkScalarFloorToInt(y0);
426 SkScalar filterY1 = y0 - iy0;
427 SkScalar filterY0 = 1.0f - filterY1;
428 int iy1 = SkScalarFloorToInt(y1);
429 int ix = SkScalarFloorToInt(span.startX());
herbdd964892016-05-11 10:39:55 -0700430 Sk4f pixelY0 = fStrategy.getPixelFromRow(fStrategy.row(iy0), ix);
431 Sk4f pixelY1 = fStrategy.getPixelFromRow(fStrategy.row(iy1), ix);
herb6eff52a2016-03-23 09:00:33 -0700432 Sk4f filterPixel = pixelY0 * filterY0 + pixelY1 * filterY1;
433 int count = span.count();
434 while (count >= 4) {
herb9e0efe52016-04-08 13:25:28 -0700435 fNext->blend4Pixels(filterPixel, filterPixel, filterPixel, filterPixel);
herb6eff52a2016-03-23 09:00:33 -0700436 count -= 4;
437 }
438 while (count > 0) {
herb9e0efe52016-04-08 13:25:28 -0700439 fNext->blendPixel(filterPixel);
herb6eff52a2016-03-23 09:00:33 -0700440 count -= 1;
441 }
442 }
443
444 // When moving through source space more slowly than dst space (zoomed in),
445 // we'll be sampling from the same source pixel more than once.
446 void bilerpSpanSlowRate(Span span, SkScalar ry1) {
447 SkPoint start;
448 SkScalar length;
449 int count;
450 std::tie(start, length, count) = span;
451 SkFixed fx = SkScalarToFixed(X(start)
452 -0.5f);
453
454 SkFixed fdx = SkScalarToFixed(length / (count - 1));
455 //start = start + SkPoint{-0.5f, -0.5f};
456
457 Sk4f xAdjust;
458 if (fdx >= 0) {
459 xAdjust = Sk4f{-1.0f};
460 } else {
461 xAdjust = Sk4f{1.0f};
462 }
463 int ix = SkFixedFloorToInt(fx);
464 int ioldx = ix;
465 Sk4f x{SkFixedToScalar(fx) - ix};
466 Sk4f dx{SkFixedToScalar(fdx)};
467 SkScalar ry0 = Y(start) - 0.5f;
468 ry1 += 0.5f;
469 SkScalar yFloor = std::floor(ry0);
470 Sk4f y1 = Sk4f{ry0 - yFloor};
471 Sk4f y0 = Sk4f{1.0f} - y1;
herb222f8ff2016-03-23 15:14:23 -0700472 const void* const row0 = fStrategy.row(SkScalarFloorToInt(ry0));
473 const void* const row1 = fStrategy.row(SkScalarFloorToInt(ry1));
herbdd964892016-05-11 10:39:55 -0700474 Sk4f fpixel00 = y0 * fStrategy.getPixelFromRow(row0, ix);
475 Sk4f fpixel01 = y1 * fStrategy.getPixelFromRow(row1, ix);
476 Sk4f fpixel10 = y0 * fStrategy.getPixelFromRow(row0, ix + 1);
477 Sk4f fpixel11 = y1 * fStrategy.getPixelFromRow(row1, ix + 1);
herb6eff52a2016-03-23 09:00:33 -0700478 auto getNextPixel = [&]() {
479 if (ix != ioldx) {
480 fpixel00 = fpixel10;
481 fpixel01 = fpixel11;
herbdd964892016-05-11 10:39:55 -0700482 fpixel10 = y0 * fStrategy.getPixelFromRow(row0, ix + 1);
483 fpixel11 = y1 * fStrategy.getPixelFromRow(row1, ix + 1);
herb6eff52a2016-03-23 09:00:33 -0700484 ioldx = ix;
485 x = x + xAdjust;
486 }
487
488 Sk4f x0, x1;
489 x0 = Sk4f{1.0f} - x;
490 x1 = x;
491 Sk4f fpixel = x0 * (fpixel00 + fpixel01) + x1 * (fpixel10 + fpixel11);
492 fx += fdx;
493 ix = SkFixedFloorToInt(fx);
494 x = x + dx;
495 return fpixel;
496 };
497
498 while (count >= 4) {
499 Sk4f fpixel0 = getNextPixel();
500 Sk4f fpixel1 = getNextPixel();
501 Sk4f fpixel2 = getNextPixel();
502 Sk4f fpixel3 = getNextPixel();
503
herb9e0efe52016-04-08 13:25:28 -0700504 fNext->blend4Pixels(fpixel0, fpixel1, fpixel2, fpixel3);
herb6eff52a2016-03-23 09:00:33 -0700505 count -= 4;
506 }
507
508 while (count > 0) {
herb9e0efe52016-04-08 13:25:28 -0700509 fNext->blendPixel(getNextPixel());
herb6eff52a2016-03-23 09:00:33 -0700510
511 count -= 1;
512 }
513 }
514
515 // We're moving through source space at a rate of 1 source pixel per 1 dst pixel.
516 // We'll never re-use pixels, but we can at least load contiguous pixels.
517 void bilerpSpanUnitRate(Span span, SkScalar y1) {
518 y1 += 0.5f;
519 SkScalar y0 = span.startY() - 0.5f;
520 int iy0 = SkScalarFloorToInt(y0);
521 SkScalar filterY1 = y0 - iy0;
522 SkScalar filterY0 = 1.0f - filterY1;
523 int iy1 = SkScalarFloorToInt(y1);
524 const void* rowY0 = fStrategy.row(iy0);
525 const void* rowY1 = fStrategy.row(iy1);
526 SkScalar x0 = span.startX() - 0.5f;
527 int ix0 = SkScalarFloorToInt(x0);
528 SkScalar filterX1 = x0 - ix0;
529 SkScalar filterX0 = 1.0f - filterX1;
530
531 auto getPixelY0 = [&]() {
herbdd964892016-05-11 10:39:55 -0700532 Sk4f px = fStrategy.getPixelFromRow(rowY0, ix0);
herb6eff52a2016-03-23 09:00:33 -0700533 return px * filterY0;
534 };
535
536 auto getPixelY1 = [&]() {
herbdd964892016-05-11 10:39:55 -0700537 Sk4f px = fStrategy.getPixelFromRow(rowY1, ix0);
herb6eff52a2016-03-23 09:00:33 -0700538 return px * filterY1;
539 };
540
541 auto get4PixelsY0 = [&](int ix, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
542 fStrategy.get4Pixels(rowY0, ix, px0, px1, px2, px3);
543 *px0 = *px0 * filterY0;
544 *px1 = *px1 * filterY0;
545 *px2 = *px2 * filterY0;
546 *px3 = *px3 * filterY0;
547 };
548
549 auto get4PixelsY1 = [&](int ix, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) {
550 fStrategy.get4Pixels(rowY1, ix, px0, px1, px2, px3);
551 *px0 = *px0 * filterY1;
552 *px1 = *px1 * filterY1;
553 *px2 = *px2 * filterY1;
554 *px3 = *px3 * filterY1;
555 };
556
557 auto lerp = [&](Sk4f& pixelX0, Sk4f& pixelX1) {
558 return pixelX0 * filterX0 + pixelX1 * filterX1;
559 };
560
561 // Mid making 4 unit rate.
562 Sk4f pxB = getPixelY0() + getPixelY1();
563 if (span.length() > 0) {
564 int count = span.count();
565 while (count >= 4) {
566 Sk4f px00, px10, px20, px30;
567 get4PixelsY0(ix0, &px00, &px10, &px20, &px30);
568 Sk4f px01, px11, px21, px31;
569 get4PixelsY1(ix0, &px01, &px11, &px21, &px31);
570 Sk4f pxS0 = px00 + px01;
571 Sk4f px0 = lerp(pxB, pxS0);
572 Sk4f pxS1 = px10 + px11;
573 Sk4f px1 = lerp(pxS0, pxS1);
574 Sk4f pxS2 = px20 + px21;
575 Sk4f px2 = lerp(pxS1, pxS2);
576 Sk4f pxS3 = px30 + px31;
577 Sk4f px3 = lerp(pxS2, pxS3);
578 pxB = pxS3;
herb9e0efe52016-04-08 13:25:28 -0700579 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700580 ix0 += 4;
581 count -= 4;
582 }
583 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700584 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix0);
585 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix0);
herb6eff52a2016-03-23 09:00:33 -0700586
herb9e0efe52016-04-08 13:25:28 -0700587 fNext->blendPixel(lerp(pixelY0, pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700588 ix0 += 1;
589 count -= 1;
590 }
591 } else {
592 int count = span.count();
593 while (count >= 4) {
594 Sk4f px00, px10, px20, px30;
595 get4PixelsY0(ix0 - 3, &px00, &px10, &px20, &px30);
596 Sk4f px01, px11, px21, px31;
597 get4PixelsY1(ix0 - 3, &px01, &px11, &px21, &px31);
598 Sk4f pxS3 = px30 + px31;
599 Sk4f px0 = lerp(pxS3, pxB);
600 Sk4f pxS2 = px20 + px21;
601 Sk4f px1 = lerp(pxS2, pxS3);
602 Sk4f pxS1 = px10 + px11;
603 Sk4f px2 = lerp(pxS1, pxS2);
604 Sk4f pxS0 = px00 + px01;
605 Sk4f px3 = lerp(pxS0, pxS1);
606 pxB = pxS0;
herb9e0efe52016-04-08 13:25:28 -0700607 fNext->blend4Pixels(px0, px1, px2, px3);
herb6eff52a2016-03-23 09:00:33 -0700608 ix0 -= 4;
609 count -= 4;
610 }
611 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700612 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix0);
613 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix0);
herb6eff52a2016-03-23 09:00:33 -0700614
herb9e0efe52016-04-08 13:25:28 -0700615 fNext->blendPixel(lerp(pixelY0, pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700616 ix0 -= 1;
617 count -= 1;
618 }
619 }
620 }
621
622 void bilerpSpanUnitRateAlignedX(Span span, SkScalar y1) {
623 SkScalar y0 = span.startY() - 0.5f;
624 y1 += 0.5f;
625 int iy0 = SkScalarFloorToInt(y0);
626 SkScalar filterY1 = y0 - iy0;
627 SkScalar filterY0 = 1.0f - filterY1;
628 int iy1 = SkScalarFloorToInt(y1);
629 int ix = SkScalarFloorToInt(span.startX());
630 const void* rowY0 = fStrategy.row(iy0);
631 const void* rowY1 = fStrategy.row(iy1);
632 auto lerp = [&](Sk4f* pixelY0, Sk4f* pixelY1) {
633 return *pixelY0 * filterY0 + *pixelY1 * filterY1;
634 };
635
636 if (span.length() > 0) {
637 int count = span.count();
638 while (count >= 4) {
639 Sk4f px00, px10, px20, px30;
640 fStrategy.get4Pixels(rowY0, ix, &px00, &px10, &px20, &px30);
641 Sk4f px01, px11, px21, px31;
642 fStrategy.get4Pixels(rowY1, ix, &px01, &px11, &px21, &px31);
herb9e0efe52016-04-08 13:25:28 -0700643 fNext->blend4Pixels(
herb6eff52a2016-03-23 09:00:33 -0700644 lerp(&px00, &px01), lerp(&px10, &px11), lerp(&px20, &px21), lerp(&px30, &px31));
645 ix += 4;
646 count -= 4;
647 }
648 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700649 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix);
650 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix);
herb6eff52a2016-03-23 09:00:33 -0700651
herb9e0efe52016-04-08 13:25:28 -0700652 fNext->blendPixel(lerp(&pixelY0, &pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700653 ix += 1;
654 count -= 1;
655 }
656 } else {
657 int count = span.count();
658 while (count >= 4) {
659 Sk4f px00, px10, px20, px30;
660 fStrategy.get4Pixels(rowY0, ix - 3, &px30, &px20, &px10, &px00);
661 Sk4f px01, px11, px21, px31;
662 fStrategy.get4Pixels(rowY1, ix - 3, &px31, &px21, &px11, &px01);
herb9e0efe52016-04-08 13:25:28 -0700663 fNext->blend4Pixels(
herb6eff52a2016-03-23 09:00:33 -0700664 lerp(&px00, &px01), lerp(&px10, &px11), lerp(&px20, &px21), lerp(&px30, &px31));
665 ix -= 4;
666 count -= 4;
667 }
668 while (count > 0) {
herbdd964892016-05-11 10:39:55 -0700669 Sk4f pixelY0 = fStrategy.getPixelFromRow(rowY0, ix);
670 Sk4f pixelY1 = fStrategy.getPixelFromRow(rowY1, ix);
herb6eff52a2016-03-23 09:00:33 -0700671
herb9e0efe52016-04-08 13:25:28 -0700672 fNext->blendPixel(lerp(&pixelY0, &pixelY1));
herb6eff52a2016-03-23 09:00:33 -0700673 ix -= 1;
674 count -= 1;
675 }
676 }
677 }
678
679 // We're moving through source space faster than dst (zoomed out),
680 // so we'll never reuse a source pixel or be able to do contiguous loads.
681 void bilerpSpanFastRate(Span span, SkScalar y1) {
682 SkPoint start;
683 SkScalar length;
684 int count;
685 std::tie(start, length, count) = span;
686 SkScalar x = X(start);
687 SkScalar y = Y(start);
688 if (false && y == y1) {
689 struct BilerpWrapper {
690 void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) {
691 fSampler.bilerpListFew(n, xs, ys);
692 }
693
694 void VECTORCALL pointList4(Sk4s xs, Sk4s ys) {
695 fSampler.bilerpList4(xs, ys);
696 }
697
698 GeneralSampler& fSampler;
699 };
700 BilerpWrapper wrapper{*this};
701 span_fallback(span, &wrapper);
702 } else {
703 SkScalar dx = length / (count - 1);
704 Sk4f ys = {y - 0.5f, y - 0.5f, y1 + 0.5f, y1 + 0.5f};
705 while (count > 0) {
706 Sk4f xs = Sk4f{-0.5f, 0.5f, -0.5f, 0.5f} + Sk4f{x};
707 this->bilerpEdge(xs, ys);
708 x += dx;
709 count -= 1;
710 }
711 }
712 }
713
714 Next* const fNext;
herb15332a82016-05-12 11:37:00 -0700715 PixelAccessor<colorType, colorProfile> fStrategy;
herb6eff52a2016-03-23 09:00:33 -0700716};
717
herbdd964892016-05-11 10:39:55 -0700718
herb6eff52a2016-03-23 09:00:33 -0700719} // namespace
720
721#endif // SkLinearBitmapPipeline_sampler_DEFINED