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
25namespace {
26struct X {
27    explicit X(SkScalar val) : fVal{val} { }
28    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    operator SkScalar () const {return fVal;}
32private:
33    SkScalar fVal;
34};
35
36struct Y {
37    explicit Y(SkScalar val) : fVal{val} { }
38    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    operator SkScalar () const {return fVal;}
42private:
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.
57class Span {
58public:
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
70    bool isEmpty() const { return 0 == fCount; }
71    void clear() { fCount = 0; }
72    int count() const { return fCount; }
73    SkScalar length() const { return fLength; }
74    SkScalar startX() const { return X(fStart); }
75    SkScalar endX() const { return this->startX() + this->length(); }
76    SkScalar startY() const { return Y(fStart); }
77    Span emptySpan() { return Span{{0.0, 0.0}, 0.0f, 0}; }
78
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
85    void offset(SkScalar offsetX) {
86        fStart.offset(offsetX, 0.0f);
87    }
88
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
143    void clampToSinglePixel(SkPoint pixel) {
144        fStart = pixel;
145        fLength = 0.0f;
146    }
147
148private:
149    SkPoint  fStart;
150    SkScalar fLength;
151    int      fCount;
152};
153
154template<typename Stage>
155void span_fallback(Span span, Stage* stage) {
156    SkPoint start;
157    SkScalar length;
158    int count;
159    std::tie(start, length, count) = span;
160    Sk4f startXs{X(start)};
161    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    if (count > 1) {
167        SkScalar dx = length / (count - 1);
168        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    Sk4f xs = startXs + mults * dXs;
174    while (count >= 4) {
175        stage->pointList4(xs, ys);
176
177        mults += Sk4f{4.0f};
178        xs = mults * dXs + startXs;
179        count -= 4;
180    }
181
182    if (count > 0) {
183        stage->pointListFew(count, xs, ys);
184    }
185}
186
187inline Sk4f SK_VECTORCALL check_pixel(const Sk4f& pixel) {
188    SkASSERTF(0.0f <= pixel[0] && pixel[0] <= 1.0f, "pixel[0]: %f", pixel[0]);
189    SkASSERTF(0.0f <= pixel[1] && pixel[1] <= 1.0f, "pixel[1]: %f", pixel[1]);
190    SkASSERTF(0.0f <= pixel[2] && pixel[2] <= 1.0f, "pixel[2]: %f", pixel[2]);
191    SkASSERTF(0.0f <= pixel[3] && pixel[3] <= 1.0f, "pixel[3]: %f", pixel[3]);
192    return pixel;
193}
194
195}  // namespace
196
197class SkLinearBitmapPipeline::PointProcessorInterface {
198public:
199    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
212class SkLinearBitmapPipeline::SampleProcessorInterface
213    : public SkLinearBitmapPipeline::PointProcessorInterface {
214public:
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
220class SkLinearBitmapPipeline::DestinationInterface {
221public:
222    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
229class SkLinearBitmapPipeline::BlendProcessorInterface
230    : public SkLinearBitmapPipeline::DestinationInterface {
231public:
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
236class SkLinearBitmapPipeline::PixelAccessorInterface {
237public:
238    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
256