Home | History | Annotate | Download | only in core
      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     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;}
     32 private:
     33     SkScalar fVal;
     34 };
     35 
     36 struct 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;}
     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     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 
    148 private:
    149     SkPoint  fStart;
    150     SkScalar fLength;
    151     int      fCount;
    152 };
    153 
    154 template<typename 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 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 
    187 inline 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 
    197 class SkLinearBitmapPipeline::PointProcessorInterface {
    198 public:
    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 
    212 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 class SkLinearBitmapPipeline::DestinationInterface {
    221 public:
    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 
    229 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 class SkLinearBitmapPipeline::PixelAccessorInterface {
    237 public:
    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