1 /*
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_core_DEFINED
9 #define SkLinearBitmapPipeline_core_DEFINED
10
11 #include <algorithm>
12 #include <cmath>
13 #include "SkNx.h"
14
15 // New bilerp strategy:
16 // Pass through on bilerpList4 and bilerpListFew (analogs to pointList), introduce bilerpEdge
17 // which takes 4 points. If the sample spans an edge, then break it into a bilerpEdge. Bilerp
18 // span then becomes a normal span except in special cases where an extra Y is given. The bilerp
19 // need to stay single point calculations until the tile layer.
20 // TODO:
21 // - edge span predicate.
22 // - introduce new point API
23 // - Add tile for new api.
24
25 namespace {
26 struct X {
XX27 explicit X(SkScalar val) : fVal{val} { }
XX28 explicit X(SkPoint pt) : fVal{pt.fX} { }
XX29 explicit X(SkSize s) : fVal{s.fWidth} { }
XX30 explicit X(SkISize s) : fVal((SkScalar)s.fWidth) { }
SkScalarX31 operator SkScalar () const {return fVal;}
32 private:
33 SkScalar fVal;
34 };
35
36 struct Y {
YY37 explicit Y(SkScalar val) : fVal{val} { }
YY38 explicit Y(SkPoint pt) : fVal{pt.fY} { }
YY39 explicit Y(SkSize s) : fVal{s.fHeight} { }
YY40 explicit Y(SkISize s) : fVal((SkScalar)s.fHeight) { }
SkScalarY41 operator SkScalar () const {return fVal;}
42 private:
43 SkScalar fVal;
44 };
45
46 // The Span class enables efficient processing horizontal spans of pixels.
47 // * start - the point where to start the span.
48 // * length - the number of pixels to traverse in source space.
49 // * count - the number of pixels to produce in destination space.
50 // Both start and length are mapped through the inversion matrix to produce values in source
51 // space. After the matrix operation, the tilers may break the spans up into smaller spans.
52 // The tilers can produce spans that seem nonsensical.
53 // * The clamp tiler can create spans with length of 0. This indicates to copy an edge pixel out
54 // to the edge of the destination scan.
55 // * The mirror tiler can produce spans with negative length. This indicates that the source
56 // should be traversed in the opposite direction to the destination pixels.
57 class Span {
58 public:
Span(SkPoint start,SkScalar length,int count)59 Span(SkPoint start, SkScalar length, int count)
60 : fStart(start)
61 , fLength(length)
62 , fCount{count} {
63 SkASSERT(std::isfinite(length));
64 }
65
66 operator std::tuple<SkPoint&, SkScalar&, int&>() {
67 return std::tie(fStart, fLength, fCount);
68 }
69
isEmpty()70 bool isEmpty() const { return 0 == fCount; }
clear()71 void clear() { fCount = 0; }
count()72 int count() const { return fCount; }
length()73 SkScalar length() const { return fLength; }
startX()74 SkScalar startX() const { return X(fStart); }
endX()75 SkScalar endX() const { return this->startX() + this->length(); }
startY()76 SkScalar startY() const { return Y(fStart); }
emptySpan()77 Span emptySpan() { return Span{{0.0, 0.0}, 0.0f, 0}; }
78
completelyWithin(SkScalar xMin,SkScalar xMax)79 bool completelyWithin(SkScalar xMin, SkScalar xMax) const {
80 SkScalar sMin, sMax;
81 std::tie(sMin, sMax) = std::minmax(startX(), endX());
82 return xMin <= sMin && sMax < xMax;
83 }
84
offset(SkScalar offsetX)85 void offset(SkScalar offsetX) {
86 fStart.offset(offsetX, 0.0f);
87 }
88
breakAt(SkScalar breakX,SkScalar dx)89 Span breakAt(SkScalar breakX, SkScalar dx) {
90 SkASSERT(std::isfinite(breakX));
91 SkASSERT(std::isfinite(dx));
92 SkASSERT(dx != 0.0f);
93
94 if (this->isEmpty()) {
95 return this->emptySpan();
96 }
97
98 int dxSteps = SkScalarFloorToInt((breakX - this->startX()) / dx);
99
100 if (dxSteps < 0) {
101 // The span is wholly after breakX.
102 return this->emptySpan();
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 SkScalar newLength = dxSteps * dx;
112
113 // If the last (or first if count = 1) sample lands directly on the boundary. Include it
114 // when dx < 0 and exclude it when dx > 0.
115 // Reasoning:
116 // dx > 0: The sample point on the boundary is part of the next span because the entire
117 // pixel is after the boundary.
118 // dx < 0: The sample point on the boundary is part of the current span because the
119 // entire pixel is before the boundary.
120 if (this->startX() + newLength == breakX && dx > 0) {
121 if (dxSteps > 0) {
122 dxSteps -= 1;
123 newLength -= dx;
124 } else {
125 return this->emptySpan();
126 }
127 }
128
129 // Calculate new span parameters
130 SkPoint newStart = fStart;
131 int newCount = dxSteps + 1;
132 SkASSERT(newCount > 0);
133
134 // Update this span to reflect the break.
135 SkScalar lengthToStart = newLength + dx;
136 fLength -= lengthToStart;
137 fCount -= newCount;
138 fStart = {this->startX() + lengthToStart, Y(fStart)};
139
140 return Span{newStart, newLength, newCount};
141 }
142
clampToSinglePixel(SkPoint pixel)143 void clampToSinglePixel(SkPoint pixel) {
144 fStart = pixel;
145 fLength = 0.0f;
146 }
147
148 private:
149 SkPoint fStart;
150 SkScalar fLength;
151 int fCount;
152 };
153
154 template<typename Stage>
span_fallback(Span span,Stage * stage)155 void span_fallback(Span span, Stage* stage) {
156 SkPoint start;
157 SkScalar length;
158 int count;
159 std::tie(start, length, count) = span;
160 Sk4f xs{X(start)};
161 Sk4f ys{Y(start)};
162
163 // Initializing this is not needed, but some compilers can't figure this out.
164 Sk4s fourDx{0.0f};
165 if (count > 1) {
166 SkScalar dx = length / (count - 1);
167 xs = xs + Sk4f{0.0f, 1.0f, 2.0f, 3.0f} * dx;
168 // Only used if count is >= 4.
169 fourDx = Sk4f{4.0f * dx};
170 }
171
172 while (count >= 4) {
173 stage->pointList4(xs, ys);
174 xs = xs + fourDx;
175 count -= 4;
176 }
177 if (count > 0) {
178 stage->pointListFew(count, xs, ys);
179 }
180 }
181
check_pixel(const Sk4f & pixel)182 inline Sk4f SK_VECTORCALL check_pixel(const Sk4f& pixel) {
183 SkASSERTF(0.0f <= pixel[0] && pixel[0] <= 1.0f, "pixel[0]: %f", pixel[0]);
184 SkASSERTF(0.0f <= pixel[1] && pixel[1] <= 1.0f, "pixel[1]: %f", pixel[1]);
185 SkASSERTF(0.0f <= pixel[2] && pixel[2] <= 1.0f, "pixel[2]: %f", pixel[2]);
186 SkASSERTF(0.0f <= pixel[3] && pixel[3] <= 1.0f, "pixel[3]: %f", pixel[3]);
187 return pixel;
188 }
189
190 } // namespace
191
192 class SkLinearBitmapPipeline::PointProcessorInterface {
193 public:
~PointProcessorInterface()194 virtual ~PointProcessorInterface() { }
195 // Take the first n (where 0 < n && n < 4) items from xs and ys and sample those points. For
196 // nearest neighbor, that means just taking the floor xs and ys. For bilerp, this means
197 // to expand the bilerp filter around the point and sample using that filter.
198 virtual void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) = 0;
199 // Same as pointListFew, but n = 4.
200 virtual void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) = 0;
201 // A span is a compact form of sample points that are obtained by mapping points from
202 // destination space to source space. This is used for horizontal lines only, and is mainly
203 // used to take advantage of memory coherence for horizontal spans.
204 virtual void pointSpan(Span span) = 0;
205 };
206
207 class SkLinearBitmapPipeline::SampleProcessorInterface
208 : public SkLinearBitmapPipeline::PointProcessorInterface {
209 public:
210 // Used for nearest neighbor when scale factor is 1.0. The span can just be repeated with no
211 // edge pixel alignment problems. This is for handling a very common case.
212 virtual void repeatSpan(Span span, int32_t repeatCount) = 0;
213 };
214
215 class SkLinearBitmapPipeline::DestinationInterface {
216 public:
~DestinationInterface()217 virtual ~DestinationInterface() { }
218 // Count is normally not needed, but in these early stages of development it is useful to
219 // check bounds.
220 // TODO(herb): 4/6/2016 - remove count when code is stable.
221 virtual void setDestination(void* dst, int count) = 0;
222 };
223
224 class SkLinearBitmapPipeline::BlendProcessorInterface
225 : public SkLinearBitmapPipeline::DestinationInterface {
226 public:
227 virtual void SK_VECTORCALL blendPixel(Sk4f pixel0) = 0;
228 virtual void SK_VECTORCALL blend4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) = 0;
229 };
230
231 class SkLinearBitmapPipeline::PixelAccessorInterface {
232 public:
~PixelAccessorInterface()233 virtual ~PixelAccessorInterface() { }
234 virtual void SK_VECTORCALL getFewPixels(
235 int n, Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) const = 0;
236
237 virtual void SK_VECTORCALL get4Pixels(
238 Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const = 0;
239
240 virtual void get4Pixels(
241 const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const = 0;
242
243 virtual Sk4f getPixelFromRow(const void* row, int index) const = 0;
244
245 virtual Sk4f getPixelAt(int index) const = 0;
246
247 virtual const void* row(int y) const = 0;
248 };
249
250 #endif // SkLinearBitmapPipeline_core_DEFINED
251