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