Line data Source code
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 {
27 : explicit X(SkScalar val) : fVal{val} { }
28 0 : explicit X(SkPoint pt) : fVal{pt.fX} { }
29 : explicit X(SkSize s) : fVal{s.fWidth} { }
30 : explicit X(SkISize s) : fVal((SkScalar)s.fWidth) { }
31 0 : operator SkScalar () const {return fVal;}
32 : private:
33 : SkScalar fVal;
34 : };
35 :
36 : struct Y {
37 : explicit Y(SkScalar val) : fVal{val} { }
38 0 : explicit Y(SkPoint pt) : fVal{pt.fY} { }
39 : explicit Y(SkSize s) : fVal{s.fHeight} { }
40 : explicit Y(SkISize s) : fVal((SkScalar)s.fHeight) { }
41 0 : 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:
59 0 : Span(SkPoint start, SkScalar length, int count)
60 0 : : fStart(start)
61 : , fLength(length)
62 0 : , fCount{count} {
63 0 : SkASSERT(std::isfinite(length));
64 0 : }
65 :
66 0 : operator std::tuple<SkPoint&, SkScalar&, int&>() {
67 0 : return std::tie(fStart, fLength, fCount);
68 : }
69 :
70 0 : bool isEmpty() const { return 0 == fCount; }
71 0 : void clear() { fCount = 0; }
72 0 : int count() const { return fCount; }
73 0 : SkScalar length() const { return fLength; }
74 0 : SkScalar startX() const { return X(fStart); }
75 0 : SkScalar endX() const { return this->startX() + this->length(); }
76 0 : SkScalar startY() const { return Y(fStart); }
77 0 : Span emptySpan() { return Span{{0.0, 0.0}, 0.0f, 0}; }
78 :
79 0 : bool completelyWithin(SkScalar xMin, SkScalar xMax) const {
80 : SkScalar sMin, sMax;
81 0 : std::tie(sMin, sMax) = std::minmax(startX(), endX());
82 0 : return xMin <= sMin && sMax < xMax;
83 : }
84 :
85 0 : void offset(SkScalar offsetX) {
86 0 : fStart.offset(offsetX, 0.0f);
87 0 : }
88 :
89 0 : Span breakAt(SkScalar breakX, SkScalar dx) {
90 0 : SkASSERT(std::isfinite(breakX));
91 0 : SkASSERT(std::isfinite(dx));
92 0 : SkASSERT(dx != 0.0f);
93 :
94 0 : if (this->isEmpty()) {
95 0 : return this->emptySpan();
96 : }
97 :
98 0 : int dxSteps = SkScalarFloorToInt((breakX - this->startX()) / dx);
99 :
100 0 : if (dxSteps < 0) {
101 : // The span is wholly after breakX.
102 0 : return this->emptySpan();
103 0 : } else if (dxSteps >= fCount) {
104 : // The span is wholly before breakX.
105 0 : Span answer = *this;
106 0 : this->clear();
107 0 : return answer;
108 : }
109 :
110 : // Calculate the values for the span to cleave off.
111 0 : 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 0 : if (this->startX() + newLength == breakX && dx > 0) {
121 0 : if (dxSteps > 0) {
122 0 : dxSteps -= 1;
123 0 : newLength -= dx;
124 : } else {
125 0 : return this->emptySpan();
126 : }
127 : }
128 :
129 : // Calculate new span parameters
130 0 : SkPoint newStart = fStart;
131 0 : int newCount = dxSteps + 1;
132 0 : SkASSERT(newCount > 0);
133 :
134 : // Update this span to reflect the break.
135 0 : SkScalar lengthToStart = newLength + dx;
136 0 : fLength -= lengthToStart;
137 0 : fCount -= newCount;
138 0 : fStart = {this->startX() + lengthToStart, Y(fStart)};
139 :
140 0 : return Span{newStart, newLength, newCount};
141 : }
142 :
143 0 : void clampToSinglePixel(SkPoint pixel) {
144 0 : fStart = pixel;
145 0 : fLength = 0.0f;
146 0 : }
147 :
148 : private:
149 : SkPoint fStart;
150 : SkScalar fLength;
151 : int fCount;
152 : };
153 :
154 : template<typename Stage>
155 0 : void span_fallback(Span span, Stage* stage) {
156 : SkPoint start;
157 : SkScalar length;
158 : int count;
159 0 : std::tie(start, length, count) = span;
160 0 : Sk4f startXs{X(start)};
161 0 : Sk4f ys{Y(start)};
162 : Sk4f mults = {0.0f, 1.0f, 2.0f, 3.0f};
163 :
164 : // Initializing this is not needed, but some compilers can't figure this out.
165 : Sk4s dXs{0.0f};
166 0 : if (count > 1) {
167 0 : SkScalar dx = length / (count - 1);
168 0 : dXs = Sk4f{dx};
169 : }
170 :
171 : // Instead of using xs = xs + dx every round, this uses xs = i * dx + X(start). This
172 : // eliminates the rounding error for the sum.
173 0 : Sk4f xs = startXs + mults * dXs;
174 0 : while (count >= 4) {
175 0 : stage->pointList4(xs, ys);
176 :
177 0 : mults += Sk4f{4.0f};
178 0 : xs = mults * dXs + startXs;
179 0 : count -= 4;
180 : }
181 :
182 0 : if (count > 0) {
183 0 : stage->pointListFew(count, xs, ys);
184 : }
185 0 : }
186 :
187 0 : inline Sk4f SK_VECTORCALL check_pixel(const Sk4f& pixel) {
188 0 : SkASSERTF(0.0f <= pixel[0] && pixel[0] <= 1.0f, "pixel[0]: %f", pixel[0]);
189 0 : SkASSERTF(0.0f <= pixel[1] && pixel[1] <= 1.0f, "pixel[1]: %f", pixel[1]);
190 0 : SkASSERTF(0.0f <= pixel[2] && pixel[2] <= 1.0f, "pixel[2]: %f", pixel[2]);
191 0 : SkASSERTF(0.0f <= pixel[3] && pixel[3] <= 1.0f, "pixel[3]: %f", pixel[3]);
192 0 : return pixel;
193 : }
194 :
195 : } // namespace
196 :
197 0 : class SkLinearBitmapPipeline::PointProcessorInterface {
198 : public:
199 0 : virtual ~PointProcessorInterface() { }
200 : // Take the first n (where 0 < n && n < 4) items from xs and ys and sample those points. For
201 : // nearest neighbor, that means just taking the floor xs and ys. For bilerp, this means
202 : // to expand the bilerp filter around the point and sample using that filter.
203 : virtual void SK_VECTORCALL pointListFew(int n, Sk4s xs, Sk4s ys) = 0;
204 : // Same as pointListFew, but n = 4.
205 : virtual void SK_VECTORCALL pointList4(Sk4s xs, Sk4s ys) = 0;
206 : // A span is a compact form of sample points that are obtained by mapping points from
207 : // destination space to source space. This is used for horizontal lines only, and is mainly
208 : // used to take advantage of memory coherence for horizontal spans.
209 : virtual void pointSpan(Span span) = 0;
210 : };
211 :
212 0 : class SkLinearBitmapPipeline::SampleProcessorInterface
213 : : public SkLinearBitmapPipeline::PointProcessorInterface {
214 : public:
215 : // Used for nearest neighbor when scale factor is 1.0. The span can just be repeated with no
216 : // edge pixel alignment problems. This is for handling a very common case.
217 : virtual void repeatSpan(Span span, int32_t repeatCount) = 0;
218 : };
219 :
220 0 : class SkLinearBitmapPipeline::DestinationInterface {
221 : public:
222 0 : virtual ~DestinationInterface() { }
223 : // Count is normally not needed, but in these early stages of development it is useful to
224 : // check bounds.
225 : // TODO(herb): 4/6/2016 - remove count when code is stable.
226 : virtual void setDestination(void* dst, int count) = 0;
227 : };
228 :
229 0 : class SkLinearBitmapPipeline::BlendProcessorInterface
230 : : public SkLinearBitmapPipeline::DestinationInterface {
231 : public:
232 : virtual void SK_VECTORCALL blendPixel(Sk4f pixel0) = 0;
233 : virtual void SK_VECTORCALL blend4Pixels(Sk4f p0, Sk4f p1, Sk4f p2, Sk4f p3) = 0;
234 : };
235 :
236 0 : class SkLinearBitmapPipeline::PixelAccessorInterface {
237 : public:
238 0 : virtual ~PixelAccessorInterface() { }
239 : virtual void SK_VECTORCALL getFewPixels(
240 : int n, Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2) const = 0;
241 :
242 : virtual void SK_VECTORCALL get4Pixels(
243 : Sk4i xs, Sk4i ys, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const = 0;
244 :
245 : virtual void get4Pixels(
246 : const void* src, int index, Sk4f* px0, Sk4f* px1, Sk4f* px2, Sk4f* px3) const = 0;
247 :
248 : virtual Sk4f getPixelFromRow(const void* row, int index) const = 0;
249 :
250 : virtual Sk4f getPixelAt(int index) const = 0;
251 :
252 : virtual const void* row(int y) const = 0;
253 : };
254 :
255 : #endif // SkLinearBitmapPipeline_core_DEFINED
|