OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkLinearBitmapPipeline.h" | 8 #include "SkLinearBitmapPipeline.h" |
| 9 |
9 #include "SkPM4f.h" | 10 #include "SkPM4f.h" |
10 | |
11 #include <algorithm> | 11 #include <algorithm> |
12 #include <cmath> | 12 #include <cmath> |
13 #include <limits> | 13 #include <limits> |
14 #include "SkColor.h" | 14 #include "SkColor.h" |
15 #include "SkSize.h" | 15 #include "SkSize.h" |
16 #include <tuple> | 16 #include <tuple> |
17 | 17 #include "SkLinearBitmapPipeline_core.h" |
18 // Tweak ABI of functions that pass Sk4f by value to pass them via registers. | 18 #include "SkLinearBitmapPipeline_matrix.h" |
19 #if defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 | 19 #include "SkLinearBitmapPipeline_tile.h" |
20 #define VECTORCALL __vectorcall | |
21 #elif defined(SK_CPU_ARM32) && defined(SK_ARM_HAS_NEON) | |
22 #define VECTORCALL __attribute__((pcs("aapcs-vfp"))) | |
23 #else | |
24 #define VECTORCALL | |
25 #endif | |
26 | |
27 namespace { | |
28 struct X { | |
29 explicit X(SkScalar val) : fVal{val} { } | |
30 explicit X(SkPoint pt) : fVal{pt.fX} { } | |
31 explicit X(SkSize s) : fVal{s.fWidth} { } | |
32 explicit X(SkISize s) : fVal(s.fWidth) { } | |
33 operator SkScalar () const {return fVal;} | |
34 private: | |
35 SkScalar fVal; | |
36 }; | |
37 | |
38 struct Y { | |
39 explicit Y(SkScalar val) : fVal{val} { } | |
40 explicit Y(SkPoint pt) : fVal{pt.fY} { } | |
41 explicit Y(SkSize s) : fVal{s.fHeight} { } | |
42 explicit Y(SkISize s) : fVal(s.fHeight) { } | |
43 operator SkScalar () const {return fVal;} | |
44 private: | |
45 SkScalar fVal; | |
46 }; | |
47 | |
48 // The Span class enables efficient processing horizontal spans of pixels. | |
49 // * start - the point where to start the span. | |
50 // * length - the number of pixels to traverse in source space. | |
51 // * count - the number of pixels to produce in destination space. | |
52 // Both start and length are mapped through the inversion matrix to produce valu
es in source | |
53 // space. After the matrix operation, the tilers may break the spans up into sma
ller spans. | |
54 // The tilers can produce spans that seem nonsensical. | |
55 // * The clamp tiler can create spans with length of 0. This indicates to copy a
n edge pixel out | |
56 // to the edge of the destination scan. | |
57 // * The mirror tiler can produce spans with negative length. This indicates tha
t the source | |
58 // should be traversed in the opposite direction to the destination pixels. | |
59 class Span { | |
60 public: | |
61 Span(SkPoint start, SkScalar length, int count) | |
62 : fStart(start) | |
63 , fLength(length) | |
64 , fCount{count} { | |
65 SkASSERT(std::isfinite(length)); | |
66 } | |
67 | |
68 operator std::tuple<SkPoint&, SkScalar&, int&>() { | |
69 return std::tie(fStart, fLength, fCount); | |
70 } | |
71 | |
72 bool isEmpty() const { return 0 == fCount; } | |
73 SkScalar length() const { return fLength; } | |
74 SkScalar startX() const { return X(fStart); } | |
75 SkScalar endX() const { return startX() + length(); } | |
76 void clear() { | |
77 fCount = 0; | |
78 } | |
79 | |
80 bool completelyWithin(SkScalar xMin, SkScalar xMax) const { | |
81 SkScalar sMin, sMax; | |
82 std::tie(sMin, sMax) = std::minmax(startX(), endX()); | |
83 return xMin <= sMin && sMax <= xMax; | |
84 } | |
85 | |
86 void offset(SkScalar offsetX) { | |
87 fStart.offset(offsetX, 0.0f); | |
88 } | |
89 | |
90 Span breakAt(SkScalar breakX, SkScalar dx) { | |
91 SkASSERT(std::isfinite(breakX)); | |
92 SkASSERT(std::isfinite(dx)); | |
93 SkASSERT(dx != 0.0f); | |
94 | |
95 if (this->isEmpty()) { | |
96 return Span{{0.0, 0.0}, 0.0f, 0}; | |
97 } | |
98 | |
99 int dxSteps = SkScalarFloorToInt((breakX - this->startX()) / dx); | |
100 if (dxSteps < 0) { | |
101 // The span is wholly after breakX. | |
102 return Span{{0.0, 0.0}, 0.0f, 0}; | |
103 } else if (dxSteps > fCount) { | |
104 // The span is wholly before breakX. | |
105 Span answer = *this; | |
106 this->clear(); | |
107 return answer; | |
108 } | |
109 | |
110 // Calculate the values for the span to cleave off. | |
111 SkPoint newStart = fStart; | |
112 SkScalar newLength = dxSteps * dx; | |
113 int newCount = dxSteps + 1; | |
114 SkASSERT(newCount > 0); | |
115 | |
116 // Update this span to reflect the break. | |
117 SkScalar lengthToStart = newLength + dx; | |
118 fLength -= lengthToStart; | |
119 fCount -= newCount; | |
120 fStart = {this->startX() + lengthToStart, Y(fStart)}; | |
121 | |
122 return Span{newStart, newLength, newCount}; | |
123 } | |
124 | |
125 void clampToSinglePixel(SkPoint pixel) { | |
126 fStart = pixel; | |
127 fLength = 0.0f; | |
128 } | |
129 | |
130 private: | |
131 SkPoint fStart; | |
132 SkScalar fLength; | |
133 int fCount; | |
134 }; | |
135 | |
136 // BilerpSpans are similar to Spans, but they represent four source samples conv
erting to single | |
137 // destination pixel per count. The pixels for the four samples are collect alon
g two horizontal | |
138 // lines; one starting at {x, y0} and the other starting at {x, y1}. There are t
wo distinct lines | |
139 // to deal with the edge case of the tile mode. For example, y0 may be at the la
st y position in | |
140 // a tile while y1 would be at the first. | |
141 // The step of a Bilerp (dx) is still length / (count - 1) and the start to the
next sample is | |
142 // still dx * count, but the bounds are complicated by the sampling kernel so th
at the pixels | |
143 // touched are from x to x + length + 1. | |
144 class BilerpSpan { | |
145 public: | |
146 BilerpSpan(SkScalar x, SkScalar y0, SkScalar y1, SkScalar length, int count) | |
147 : fX{x}, fY0{y0}, fY1{y1}, fLength{length}, fCount{count} { | |
148 SkASSERT(count >= 0); | |
149 SkASSERT(std::isfinite(length)); | |
150 SkASSERT(std::isfinite(x)); | |
151 SkASSERT(std::isfinite(y0)); | |
152 SkASSERT(std::isfinite(y1)); | |
153 } | |
154 | |
155 operator std::tuple<SkScalar&, SkScalar&, SkScalar&, SkScalar&, int&>() { | |
156 return std::tie(fX, fY0, fY1, fLength, fCount); | |
157 } | |
158 | |
159 bool isEmpty() const { return 0 == fCount; } | |
160 | |
161 private: | |
162 SkScalar fX; | |
163 SkScalar fY0; | |
164 SkScalar fY1; | |
165 SkScalar fLength; | |
166 int fCount; | |
167 }; | |
168 } // namespace | |
169 | 20 |
170 class SkLinearBitmapPipeline::PointProcessorInterface { | 21 class SkLinearBitmapPipeline::PointProcessorInterface { |
171 public: | 22 public: |
172 virtual ~PointProcessorInterface() { } | 23 virtual ~PointProcessorInterface() { } |
173 virtual void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) = 0; | 24 virtual void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) = 0; |
174 virtual void VECTORCALL pointList4(Sk4s xs, Sk4s ys) = 0; | 25 virtual void VECTORCALL pointList4(Sk4s xs, Sk4s ys) = 0; |
175 virtual void pointSpan(Span span) = 0; | 26 virtual void pointSpan(Span span) = 0; |
176 }; | 27 }; |
177 | 28 |
178 class SkLinearBitmapPipeline::BilerpProcessorInterface | 29 class SkLinearBitmapPipeline::BilerpProcessorInterface |
(...skipping 17 matching lines...) Expand all Loading... |
196 | 47 |
197 class SkLinearBitmapPipeline::PixelPlacerInterface { | 48 class SkLinearBitmapPipeline::PixelPlacerInterface { |
198 public: | 49 public: |
199 virtual ~PixelPlacerInterface() { } | 50 virtual ~PixelPlacerInterface() { } |
200 virtual void setDestination(SkPM4f* dst) = 0; | 51 virtual void setDestination(SkPM4f* dst) = 0; |
201 virtual void VECTORCALL placePixel(Sk4f pixel0) = 0; | 52 virtual void VECTORCALL placePixel(Sk4f pixel0) = 0; |
202 virtual void VECTORCALL place4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) = 0
; | 53 virtual void VECTORCALL place4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) = 0
; |
203 }; | 54 }; |
204 | 55 |
205 namespace { | 56 namespace { |
206 template <typename Stage> | |
207 void span_fallback(Span span, Stage* stage) { | |
208 SkPoint start; | |
209 SkScalar length; | |
210 int count; | |
211 std::tie(start, length, count) = span; | |
212 Sk4f xs{X(start)}; | |
213 Sk4f ys{Y(start)}; | |
214 | |
215 // Initializing this is not needed, but some compilers can't figure this out
. | |
216 Sk4s fourDx{0.0f}; | |
217 if (count > 1) { | |
218 SkScalar dx = length / (count - 1); | |
219 xs = xs + Sk4f{0.0f, 1.0f, 2.0f, 3.0f} * dx; | |
220 // Only used if count is >= 4. | |
221 fourDx = Sk4f{4.0f * dx}; | |
222 } | |
223 | |
224 while (count >= 4) { | |
225 stage->pointList4(xs, ys); | |
226 xs = xs + fourDx; | |
227 count -= 4; | |
228 } | |
229 if (count > 0) { | |
230 stage->pointListFew(count, xs, ys); | |
231 } | |
232 } | |
233 | |
234 template <typename Next> | |
235 void bilerp_span_fallback(BilerpSpan span, Next* next) { | |
236 SkScalar x, y0, y1; SkScalar length; int count; | |
237 std::tie(x, y0, y1, length, count) = span; | |
238 | |
239 SkASSERT(!span.isEmpty()); | |
240 float dx = length / (count - 1); | |
241 | |
242 Sk4f xs = Sk4f{x} + Sk4f{0.0f, 1.0f, 0.0f, 1.0f}; | |
243 Sk4f ys = Sk4f{y0, y0, y1, y1}; | |
244 | |
245 // If count == 1 then dx will be inf or NaN, but that is ok because the resu
lting addition is | |
246 // never used. | |
247 while (count > 0) { | |
248 next->bilerpList(xs, ys); | |
249 xs = xs + dx; | |
250 count -= 1; | |
251 } | |
252 } | |
253 | 57 |
254 // PointProcessor uses a strategy to help complete the work of the different sta
ges. The strategy | 58 // PointProcessor uses a strategy to help complete the work of the different sta
ges. The strategy |
255 // must implement the following methods: | 59 // must implement the following methods: |
256 // * processPoints(xs, ys) - must mutate the xs and ys for the stage. | 60 // * processPoints(xs, ys) - must mutate the xs and ys for the stage. |
257 // * maybeProcessSpan(span, next) - This represents a horizontal series of pixel
s | 61 // * maybeProcessSpan(span, next) - This represents a horizontal series of pixel
s |
258 // to work over. | 62 // to work over. |
259 // span - encapsulation of span. | 63 // span - encapsulation of span. |
260 // next - a pointer to the next stage. | 64 // next - a pointer to the next stage. |
261 // maybeProcessSpan - returns false if it can not process the span and needs t
o fallback to | 65 // maybeProcessSpan - returns false if it can not process the span and needs t
o fallback to |
262 // point lists for processing. | 66 // point lists for processing. |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
327 if (!fStrategy.maybeProcessBilerpSpan(bSpan, fNext)) { | 131 if (!fStrategy.maybeProcessBilerpSpan(bSpan, fNext)) { |
328 bilerp_span_fallback(bSpan, this); | 132 bilerp_span_fallback(bSpan, this); |
329 } | 133 } |
330 } | 134 } |
331 | 135 |
332 private: | 136 private: |
333 Next* const fNext; | 137 Next* const fNext; |
334 Strategy fStrategy; | 138 Strategy fStrategy; |
335 }; | 139 }; |
336 | 140 |
337 class TranslateMatrixStrategy { | 141 ////////////////////////////////////////////////////////////////////////////////
//////////////////// |
338 public: | 142 // Matrix Stage |
339 TranslateMatrixStrategy(SkVector offset) | |
340 : fXOffset{X(offset)} | |
341 , fYOffset{Y(offset)} { } | |
342 | |
343 void processPoints(Sk4s* xs, Sk4s* ys) { | |
344 *xs = *xs + fXOffset; | |
345 *ys = *ys + fYOffset; | |
346 } | |
347 | |
348 template <typename Next> | |
349 bool maybeProcessSpan(Span span, Next* next) { | |
350 SkPoint start; SkScalar length; int count; | |
351 std::tie(start, length, count) = span; | |
352 next->pointSpan(Span{start + SkPoint{fXOffset[0], fYOffset[0]}, length,
count}); | |
353 return true; | |
354 } | |
355 | |
356 private: | |
357 const Sk4s fXOffset, fYOffset; | |
358 }; | |
359 template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> | 143 template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> |
360 using TranslateMatrix = PointProcessor<TranslateMatrixStrategy, Next>; | 144 using TranslateMatrix = PointProcessor<TranslateMatrixStrategy, Next>; |
361 | 145 |
362 class ScaleMatrixStrategy { | |
363 public: | |
364 ScaleMatrixStrategy(SkVector offset, SkVector scale) | |
365 : fXOffset{X(offset)}, fYOffset{Y(offset)} | |
366 , fXScale{X(scale)}, fYScale{Y(scale)} { } | |
367 void processPoints(Sk4s* xs, Sk4s* ys) { | |
368 *xs = *xs * fXScale + fXOffset; | |
369 *ys = *ys * fYScale + fYOffset; | |
370 } | |
371 | |
372 template <typename Next> | |
373 bool maybeProcessSpan(Span span, Next* next) { | |
374 SkPoint start; SkScalar length; int count; | |
375 std::tie(start, length, count) = span; | |
376 SkPoint newStart = | |
377 SkPoint{X(start) * fXScale[0] + fXOffset[0], Y(start) * fYScale[0] +
fYOffset[0]}; | |
378 SkScalar newLength = length * fXScale[0]; | |
379 next->pointSpan(Span{newStart, newLength, count}); | |
380 return true; | |
381 } | |
382 | |
383 private: | |
384 const Sk4s fXOffset, fYOffset; | |
385 const Sk4s fXScale, fYScale; | |
386 }; | |
387 template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> | 146 template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> |
388 using ScaleMatrix = PointProcessor<ScaleMatrixStrategy, Next>; | 147 using ScaleMatrix = PointProcessor<ScaleMatrixStrategy, Next>; |
389 | 148 |
390 class AffineMatrixStrategy { | |
391 public: | |
392 AffineMatrixStrategy(SkVector offset, SkVector scale, SkVector skew) | |
393 : fXOffset{X(offset)}, fYOffset{Y(offset)} | |
394 , fXScale{X(scale)}, fYScale{Y(scale)} | |
395 , fXSkew{X(skew)}, fYSkew{Y(skew)} { } | |
396 void processPoints(Sk4s* xs, Sk4s* ys) { | |
397 Sk4s newXs = fXScale * *xs + fXSkew * *ys + fXOffset; | |
398 Sk4s newYs = fYSkew * *xs + fYScale * *ys + fYOffset; | |
399 | |
400 *xs = newXs; | |
401 *ys = newYs; | |
402 } | |
403 | |
404 template <typename Next> | |
405 bool maybeProcessSpan(Span span, Next* next) { | |
406 return false; | |
407 } | |
408 | |
409 private: | |
410 const Sk4s fXOffset, fYOffset; | |
411 const Sk4s fXScale, fYScale; | |
412 const Sk4s fXSkew, fYSkew; | |
413 }; | |
414 template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> | 149 template <typename Next = SkLinearBitmapPipeline::PointProcessorInterface> |
415 using AffineMatrix = PointProcessor<AffineMatrixStrategy, Next>; | 150 using AffineMatrix = PointProcessor<AffineMatrixStrategy, Next>; |
416 | 151 |
417 static SkLinearBitmapPipeline::PointProcessorInterface* choose_matrix( | 152 static SkLinearBitmapPipeline::PointProcessorInterface* choose_matrix( |
418 SkLinearBitmapPipeline::PointProcessorInterface* next, | 153 SkLinearBitmapPipeline::PointProcessorInterface* next, |
419 const SkMatrix& inverse, | 154 const SkMatrix& inverse, |
420 SkLinearBitmapPipeline::MatrixStage* matrixProc) { | 155 SkLinearBitmapPipeline::MatrixStage* matrixProc) { |
421 if (inverse.hasPerspective()) { | 156 if (inverse.hasPerspective()) { |
422 SkFAIL("Not implemented."); | 157 SkFAIL("Not implemented."); |
423 } else if (inverse.getSkewX() != 0.0f || inverse.getSkewY() != 0.0f) { | 158 } else if (inverse.getSkewX() != 0.0f || inverse.getSkewY() != 0.0f) { |
(...skipping 10 matching lines...) Expand all Loading... |
434 } else if (inverse.getTranslateX() != 0.0f || inverse.getTranslateY() != 0.0
f) { | 169 } else if (inverse.getTranslateX() != 0.0f || inverse.getTranslateY() != 0.0
f) { |
435 matrixProc->Initialize<TranslateMatrix<>>( | 170 matrixProc->Initialize<TranslateMatrix<>>( |
436 next, | 171 next, |
437 SkVector{inverse.getTranslateX(), inverse.getTranslateY()}); | 172 SkVector{inverse.getTranslateX(), inverse.getTranslateY()}); |
438 } else { | 173 } else { |
439 return next; | 174 return next; |
440 } | 175 } |
441 return matrixProc->get(); | 176 return matrixProc->get(); |
442 } | 177 } |
443 | 178 |
| 179 ////////////////////////////////////////////////////////////////////////////////
//////////////////// |
| 180 // Bilerp Expansion Stage |
444 template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> | 181 template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> |
445 class ExpandBilerp final : public SkLinearBitmapPipeline::PointProcessorInterfac
e { | 182 class ExpandBilerp final : public SkLinearBitmapPipeline::PointProcessorInterfac
e { |
446 public: | 183 public: |
447 ExpandBilerp(Next* next) : fNext{next} { } | 184 ExpandBilerp(Next* next) : fNext{next} { } |
448 | 185 |
449 void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { | 186 void VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) override { |
450 SkASSERT(0 < n && n < 4); | 187 SkASSERT(0 < n && n < 4); |
451 // px00 px10 px01 px11 | 188 // px00 px10 px01 px11 |
452 const Sk4s kXOffsets{-0.5f, 0.5f, -0.5f, 0.5f}, | 189 const Sk4s kXOffsets{-0.5f, 0.5f, -0.5f, 0.5f}, |
453 kYOffsets{-0.5f, -0.5f, 0.5f, 0.5f}; | 190 kYOffsets{-0.5f, -0.5f, 0.5f, 0.5f}; |
(...skipping 30 matching lines...) Expand all Loading... |
484 SkFilterQuality filterQuailty, | 221 SkFilterQuality filterQuailty, |
485 SkLinearBitmapPipeline::FilterStage* filterProc) { | 222 SkLinearBitmapPipeline::FilterStage* filterProc) { |
486 if (SkFilterQuality::kNone_SkFilterQuality == filterQuailty) { | 223 if (SkFilterQuality::kNone_SkFilterQuality == filterQuailty) { |
487 return next; | 224 return next; |
488 } else { | 225 } else { |
489 filterProc->Initialize<ExpandBilerp<>>(next); | 226 filterProc->Initialize<ExpandBilerp<>>(next); |
490 return filterProc->get(); | 227 return filterProc->get(); |
491 } | 228 } |
492 } | 229 } |
493 | 230 |
494 class ClampStrategy { | 231 ////////////////////////////////////////////////////////////////////////////////
//////////////////// |
495 public: | 232 // Tile Stage |
496 ClampStrategy(X max) | |
497 : fXMin{0.0f} | |
498 , fXMax{max - 1.0f} { } | |
499 ClampStrategy(Y max) | |
500 : fYMin{0.0f} | |
501 , fYMax{max - 1.0f} { } | |
502 ClampStrategy(SkSize max) | |
503 : fXMin{0.0f} | |
504 , fYMin{0.0f} | |
505 , fXMax{X(max) - 1.0f} | |
506 , fYMax{Y(max) - 1.0f} { } | |
507 | |
508 void processPoints(Sk4s* xs, Sk4s* ys) { | |
509 *xs = Sk4s::Min(Sk4s::Max(*xs, fXMin), fXMax); | |
510 *ys = Sk4s::Min(Sk4s::Max(*ys, fYMin), fYMax); | |
511 } | |
512 | |
513 template <typename Next> | |
514 bool maybeProcessSpan(Span originalSpan, Next* next) { | |
515 SkASSERT(!originalSpan.isEmpty()); | |
516 SkPoint start; SkScalar length; int count; | |
517 std::tie(start, length, count) = originalSpan; | |
518 SkScalar xMin = fXMin[0]; | |
519 SkScalar xMax = fXMax[0] + 1.0f; | |
520 SkScalar yMin = fYMin[0]; | |
521 SkScalar yMax = fYMax[0]; | |
522 SkScalar x = X(start); | |
523 SkScalar y = std::min(std::max<SkScalar>(yMin, Y(start)), yMax); | |
524 | |
525 Span span{{x, y}, length, count}; | |
526 | |
527 if (span.completelyWithin(xMin, xMax)) { | |
528 next->pointSpan(span); | |
529 return true; | |
530 } | |
531 if (1 == count || 0.0f == length) { | |
532 return false; | |
533 } | |
534 | |
535 SkScalar dx = length / (count - 1); | |
536 | |
537 // A B C | |
538 // +-------+-------+-------++-------+-------+-------+ +-------+-----
--++------ | |
539 // | *---*|---*---|*---*--||-*---*-|---*---|*---...| |--*---*|---*-
--||*---*.... | |
540 // | | | || | | | ... | |
|| | |
541 // | | | || | | | | |
|| | |
542 // +-------+-------+-------++-------+-------+-------+ +-------+-----
--++------ | |
543 // ^
^ | |
544 // | xMin xMax-
1 | xMax | |
545 // | |
546 // *---*---*---... - track of samples. * = sample | |
547 // | |
548 // +-+ || | |
549 // | | - pixels in source space. || - tile border. | |
550 // +-+ || | |
551 // | |
552 // The length from A to B is the length in source space or 4 * dx or (co
unt - 1) * dx | |
553 // where dx is the distance between samples. There are 5 destination pix
els | |
554 // corresponding to 5 samples specified in the A, B span. The distance f
rom A to the next | |
555 // span starting at C is 5 * dx, so count * dx. | |
556 // Remember, count is the number of pixels needed for the destination an
d the number of | |
557 // samples. | |
558 // Overall Strategy: | |
559 // * Under - for portions of the span < xMin, take the color at pixel {x
Min, y} and use it | |
560 // to fill in the 5 pixel sampled from A to B. | |
561 // * Middle - for the portion of the span between xMin and xMax sample n
ormally. | |
562 // * Over - for the portion of the span > xMax, take the color at pixel
{xMax-1, y} and | |
563 // use it to fill in the rest of the destination pixels. | |
564 if (dx >= 0) { | |
565 Span leftClamped = span.breakAt(xMin, dx); | |
566 if (!leftClamped.isEmpty()) { | |
567 leftClamped.clampToSinglePixel({xMin, y}); | |
568 next->pointSpan(leftClamped); | |
569 } | |
570 Span middle = span.breakAt(xMax, dx); | |
571 if (!middle.isEmpty()) { | |
572 next->pointSpan(middle); | |
573 } | |
574 if (!span.isEmpty()) { | |
575 span.clampToSinglePixel({xMax - 1, y}); | |
576 next->pointSpan(span); | |
577 } | |
578 } else { | |
579 Span rightClamped = span.breakAt(xMax, dx); | |
580 if (!rightClamped.isEmpty()) { | |
581 rightClamped.clampToSinglePixel({xMax - 1, y}); | |
582 next->pointSpan(rightClamped); | |
583 } | |
584 Span middle = span.breakAt(xMin, dx); | |
585 if (!middle.isEmpty()) { | |
586 next->pointSpan(middle); | |
587 } | |
588 if (!span.isEmpty()) { | |
589 span.clampToSinglePixel({xMin, y}); | |
590 next->pointSpan(span); | |
591 } | |
592 } | |
593 return true; | |
594 } | |
595 | |
596 template <typename Next> | |
597 bool maybeProcessBilerpSpan(BilerpSpan bSpan, Next* next) { | |
598 return false; | |
599 } | |
600 | |
601 private: | |
602 const Sk4s fXMin{SK_FloatNegativeInfinity}; | |
603 const Sk4s fYMin{SK_FloatNegativeInfinity}; | |
604 const Sk4s fXMax{SK_FloatInfinity}; | |
605 const Sk4s fYMax{SK_FloatInfinity}; | |
606 }; | |
607 template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> | 233 template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> |
608 using Clamp = BilerpProcessor<ClampStrategy, Next>; | 234 using Clamp = BilerpProcessor<ClampStrategy, Next>; |
609 | 235 |
610 static SkScalar tile_mod(SkScalar x, SkScalar base) { | |
611 return x - std::floor(x / base) * base; | |
612 } | |
613 | |
614 class RepeatStrategy { | |
615 public: | |
616 RepeatStrategy(X max) : fXMax{max}, fXInvMax{1.0f/max} { } | |
617 RepeatStrategy(Y max) : fYMax{max}, fYInvMax{1.0f/max} { } | |
618 RepeatStrategy(SkSize max) | |
619 : fXMax{X(max)} | |
620 , fXInvMax{1.0f / X(max)} | |
621 , fYMax{Y(max)} | |
622 , fYInvMax{1.0f / Y(max)} { } | |
623 | |
624 void processPoints(Sk4s* xs, Sk4s* ys) { | |
625 Sk4s divX = (*xs * fXInvMax).floor(); | |
626 Sk4s divY = (*ys * fYInvMax).floor(); | |
627 Sk4s baseX = (divX * fXMax); | |
628 Sk4s baseY = (divY * fYMax); | |
629 *xs = *xs - baseX; | |
630 *ys = *ys - baseY; | |
631 } | |
632 | |
633 template <typename Next> | |
634 bool maybeProcessSpan(Span originalSpan, Next* next) { | |
635 SkASSERT(!originalSpan.isEmpty()); | |
636 SkPoint start; SkScalar length; int count; | |
637 std::tie(start, length, count) = originalSpan; | |
638 // Make x and y in range on the tile. | |
639 SkScalar x = tile_mod(X(start), fXMax[0]); | |
640 SkScalar y = tile_mod(Y(start), fYMax[0]); | |
641 SkScalar xMax = fXMax[0]; | |
642 SkScalar xMin = 0.0f; | |
643 SkScalar dx = length / (count - 1); | |
644 | |
645 // No need trying to go fast because the steps are larger than a tile or
there is one point. | |
646 if (SkScalarAbs(dx) >= xMax || count <= 1) { | |
647 return false; | |
648 } | |
649 | |
650 // A B C D Z | |
651 // +-------+-------+-------++-------+-------+-------++ +-------+----
---++------ | |
652 // | | *---|*---*--||-*---*-|---*---|*---*--|| |--*---*|
|| | |
653 // | | | || | | || ... | |
|| | |
654 // | | | || | | || | |
|| | |
655 // +-------+-------+-------++-------+-------+-------++ +-------+----
---++------ | |
656 // ^^ ^^
^^ | |
657 // xMax || xMin xMax || xMin xM
ax || xMin | |
658 // | |
659 // *---*---*---... - track of samples. * = sample | |
660 // | |
661 // +-+ || | |
662 // | | - pixels in source space. || - tile border. | |
663 // +-+ || | |
664 // | |
665 // | |
666 // The given span starts at A and continues on through several tiles to
sample point Z. | |
667 // The idea is to break this into several spans one on each tile the ent
ire span | |
668 // intersects. The A to B span only covers a partial tile and has a coun
t of 3 and the | |
669 // distance from A to B is (count - 1) * dx or 2 * dx. The distance from
A to the start of | |
670 // the next span is count * dx or 3 * dx. Span C to D covers an entire t
ile has a count | |
671 // of 5 and a length of 4 * dx. Remember, count is the number of pixels
needed for the | |
672 // destination and the number of samples. | |
673 // | |
674 // Overall Strategy: | |
675 // While the span hangs over the edge of the tile, draw the span coverin
g the tile then | |
676 // slide the span over to the next tile. | |
677 | |
678 // The guard could have been count > 0, but then a bunch of math would b
e done in the | |
679 // common case. | |
680 | |
681 Span span({x,y}, length, count); | |
682 if (dx > 0) { | |
683 while (!span.isEmpty() && span.endX() > xMax) { | |
684 Span toDraw = span.breakAt(xMax, dx); | |
685 next->pointSpan(toDraw); | |
686 span.offset(-xMax); | |
687 } | |
688 } else { | |
689 while (!span.isEmpty() && span.endX() < xMin) { | |
690 Span toDraw = span.breakAt(xMin, dx); | |
691 next->pointSpan(toDraw); | |
692 span.offset(xMax); | |
693 } | |
694 } | |
695 | |
696 // All on a single tile. | |
697 if (!span.isEmpty()) { | |
698 next->pointSpan(span); | |
699 } | |
700 | |
701 return true; | |
702 } | |
703 | |
704 template <typename Next> | |
705 bool maybeProcessBilerpSpan(BilerpSpan bSpan, Next* next) { | |
706 return false; | |
707 } | |
708 | |
709 private: | |
710 const Sk4s fXMax{0.0f}; | |
711 const Sk4s fXInvMax{0.0f}; | |
712 const Sk4s fYMax{0.0f}; | |
713 const Sk4s fYInvMax{0.0f}; | |
714 }; | |
715 | |
716 template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> | 236 template <typename Next = SkLinearBitmapPipeline::BilerpProcessorInterface> |
717 using Repeat = BilerpProcessor<RepeatStrategy, Next>; | 237 using Repeat = BilerpProcessor<RepeatStrategy, Next>; |
718 | 238 |
719 static SkLinearBitmapPipeline::BilerpProcessorInterface* choose_tiler( | 239 static SkLinearBitmapPipeline::BilerpProcessorInterface* choose_tiler( |
720 SkLinearBitmapPipeline::BilerpProcessorInterface* next, | 240 SkLinearBitmapPipeline::BilerpProcessorInterface* next, |
721 SkSize dimensions, | 241 SkSize dimensions, |
722 SkShader::TileMode xMode, | 242 SkShader::TileMode xMode, |
723 SkShader::TileMode yMode, | 243 SkShader::TileMode yMode, |
724 SkLinearBitmapPipeline::TileStage* tileProcXOrBoth, | 244 SkLinearBitmapPipeline::TileStage* tileProcXOrBoth, |
725 SkLinearBitmapPipeline::TileStage* tileProcY) { | 245 SkLinearBitmapPipeline::TileStage* tileProcY) { |
(...skipping 29 matching lines...) Expand all Loading... |
755 tileProcXOrBoth->Initialize<Repeat<>>(tileProcY->get(), X(dimens
ions)); | 275 tileProcXOrBoth->Initialize<Repeat<>>(tileProcY->get(), X(dimens
ions)); |
756 break; | 276 break; |
757 case SkShader::kMirror_TileMode: | 277 case SkShader::kMirror_TileMode: |
758 SkFAIL("Not implemented."); | 278 SkFAIL("Not implemented."); |
759 break; | 279 break; |
760 } | 280 } |
761 } | 281 } |
762 return tileProcXOrBoth->get(); | 282 return tileProcXOrBoth->get(); |
763 } | 283 } |
764 | 284 |
| 285 ////////////////////////////////////////////////////////////////////////////////
//////////////////// |
| 286 // Source Sampling Stage |
765 class sRGBFast { | 287 class sRGBFast { |
766 public: | 288 public: |
767 static Sk4s VECTORCALL sRGBToLinear(Sk4s pixel) { | 289 static Sk4s VECTORCALL sRGBToLinear(Sk4s pixel) { |
768 Sk4s l = pixel * pixel; | 290 Sk4s l = pixel * pixel; |
769 return Sk4s{l[0], l[1], l[2], pixel[3]}; | 291 return Sk4s{l[0], l[1], l[2], pixel[3]}; |
770 } | 292 } |
771 }; | 293 }; |
772 | 294 |
773 enum class ColorOrder { | 295 enum class ColorOrder { |
774 kRGBA = false, | 296 kRGBA = false, |
(...skipping 246 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1021 sampleStage->Initialize<Sampler<Pixel8888LBGR>>(next, srcPixmap)
; | 543 sampleStage->Initialize<Sampler<Pixel8888LBGR>>(next, srcPixmap)
; |
1022 } | 544 } |
1023 break; | 545 break; |
1024 default: | 546 default: |
1025 SkFAIL("Not implemented. Unsupported src"); | 547 SkFAIL("Not implemented. Unsupported src"); |
1026 break; | 548 break; |
1027 } | 549 } |
1028 return sampleStage->get(); | 550 return sampleStage->get(); |
1029 } | 551 } |
1030 | 552 |
| 553 ////////////////////////////////////////////////////////////////////////////////
//////////////////// |
| 554 // Pixel Placement Stage |
1031 template <SkAlphaType alphaType> | 555 template <SkAlphaType alphaType> |
1032 class PlaceFPPixel final : public SkLinearBitmapPipeline::PixelPlacerInterface { | 556 class PlaceFPPixel final : public SkLinearBitmapPipeline::PixelPlacerInterface { |
1033 public: | 557 public: |
1034 void VECTORCALL placePixel(Sk4f pixel) override { | 558 void VECTORCALL placePixel(Sk4f pixel) override { |
1035 PlacePixel(fDst, pixel, 0); | 559 PlacePixel(fDst, pixel, 0); |
1036 fDst += 1; | 560 fDst += 1; |
1037 } | 561 } |
1038 | 562 |
1039 void VECTORCALL place4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) override { | 563 void VECTORCALL place4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) override { |
1040 SkPM4f* dst = fDst; | 564 SkPM4f* dst = fDst; |
(...skipping 30 matching lines...) Expand all Loading... |
1071 if (alphaType == kUnpremul_SkAlphaType) { | 595 if (alphaType == kUnpremul_SkAlphaType) { |
1072 placerStage->Initialize<PlaceFPPixel<kUnpremul_SkAlphaType>>(); | 596 placerStage->Initialize<PlaceFPPixel<kUnpremul_SkAlphaType>>(); |
1073 } else { | 597 } else { |
1074 // kOpaque_SkAlphaType is treated the same as kPremul_SkAlphaType | 598 // kOpaque_SkAlphaType is treated the same as kPremul_SkAlphaType |
1075 placerStage->Initialize<PlaceFPPixel<kPremul_SkAlphaType>>(); | 599 placerStage->Initialize<PlaceFPPixel<kPremul_SkAlphaType>>(); |
1076 } | 600 } |
1077 return placerStage->get(); | 601 return placerStage->get(); |
1078 } | 602 } |
1079 } // namespace | 603 } // namespace |
1080 | 604 |
| 605 ////////////////////////////////////////////////////////////////////////////////
//////////////////// |
1081 SkLinearBitmapPipeline::~SkLinearBitmapPipeline() {} | 606 SkLinearBitmapPipeline::~SkLinearBitmapPipeline() {} |
1082 | 607 |
1083 SkLinearBitmapPipeline::SkLinearBitmapPipeline( | 608 SkLinearBitmapPipeline::SkLinearBitmapPipeline( |
1084 const SkMatrix& inverse, | 609 const SkMatrix& inverse, |
1085 SkFilterQuality filterQuality, | 610 SkFilterQuality filterQuality, |
1086 SkShader::TileMode xTile, SkShader::TileMode yTile, | 611 SkShader::TileMode xTile, SkShader::TileMode yTile, |
1087 const SkPixmap& srcPixmap) { | 612 const SkPixmap& srcPixmap) { |
1088 SkSize size = SkSize::Make(srcPixmap.width(), srcPixmap.height()); | 613 SkSize size = SkSize::Make(srcPixmap.width(), srcPixmap.height()); |
1089 const SkImageInfo& srcImageInfo = srcPixmap.info(); | 614 const SkImageInfo& srcImageInfo = srcPixmap.info(); |
1090 | 615 |
1091 // As the stages are built, the chooser function may skip a stage. For examp
le, with the | 616 // As the stages are built, the chooser function may skip a stage. For examp
le, with the |
1092 // identity matrix, the matrix stage is skipped, and the tilerStage is the f
irst stage. | 617 // identity matrix, the matrix stage is skipped, and the tilerStage is the f
irst stage. |
1093 auto placementStage = choose_pixel_placer(srcImageInfo.alphaType(), &fPixelS
tage); | 618 auto placementStage = choose_pixel_placer(srcImageInfo.alphaType(), &fPixelS
tage); |
1094 auto samplerStage = choose_pixel_sampler(placementStage, srcPixmap, &fSamp
leStage); | 619 auto samplerStage = choose_pixel_sampler(placementStage, srcPixmap, &fSamp
leStage); |
1095 auto tilerStage = choose_tiler(samplerStage, size, xTile, yTile, &fTileX
OrBothStage, | 620 auto tilerStage = choose_tiler(samplerStage, size, xTile, yTile, &fTileX
OrBothStage, |
1096 &fTileYStage); | 621 &fTileYStage); |
1097 auto filterStage = choose_filter(tilerStage, filterQuality, &fFilterStage
); | 622 auto filterStage = choose_filter(tilerStage, filterQuality, &fFilterStage
); |
1098 fFirstStage = choose_matrix(filterStage, inverse, &fMatrixStage); | 623 fFirstStage = choose_matrix(filterStage, inverse, &fMatrixStage); |
1099 } | 624 } |
1100 | 625 |
1101 void SkLinearBitmapPipeline::shadeSpan4f(int x, int y, SkPM4f* dst, int count) { | 626 void SkLinearBitmapPipeline::shadeSpan4f(int x, int y, SkPM4f* dst, int count) { |
1102 SkASSERT(count > 0); | 627 SkASSERT(count > 0); |
1103 fPixelStage->setDestination(dst); | 628 fPixelStage->setDestination(dst); |
1104 // The count and length arguments start out in a precise relation in order t
o keep the | 629 // The count and length arguments start out in a precise relation in order t
o keep the |
1105 // math correct through the different stages. Count is the number of pixel t
o produce. | 630 // math correct through the different stages. Count is the number of pixel t
o produce. |
1106 // Since the code samples at pixel centers, length is the distance from the
center of the | 631 // Since the code samples at pixel centers, length is the distance from the
center of the |
1107 // first pixel to the center of the last pixel. This implies that length is
count-1. | 632 // first pixel to the center of the last pixel. This implies that length is
count-1. |
1108 fFirstStage->pointSpan(Span{SkPoint{x + 0.5f, y + 0.5f}, count - 1.0f, count
}); | 633 fFirstStage->pointSpan(Span{SkPoint{x + 0.5f, y + 0.5f}, count - 1.0f, count
}); |
1109 } | 634 } |
OLD | NEW |