OLD | NEW |
(Empty) | |
| 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 // Tweak ABI of functions that pass Sk4f by value to pass them via registers. |
| 12 #if defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 |
| 13 #define VECTORCALL __vectorcall |
| 14 #elif defined(SK_CPU_ARM32) && defined(SK_ARM_HAS_NEON) |
| 15 #define VECTORCALL __attribute__((pcs("aapcs-vfp"))) |
| 16 #else |
| 17 #define VECTORCALL |
| 18 #endif |
| 19 |
| 20 namespace { |
| 21 struct X { |
| 22 explicit X(SkScalar val) : fVal{val} { } |
| 23 explicit X(SkPoint pt) : fVal{pt.fX} { } |
| 24 explicit X(SkSize s) : fVal{s.fWidth} { } |
| 25 explicit X(SkISize s) : fVal(s.fWidth) { } |
| 26 operator SkScalar () const {return fVal;} |
| 27 private: |
| 28 SkScalar fVal; |
| 29 }; |
| 30 |
| 31 struct Y { |
| 32 explicit Y(SkScalar val) : fVal{val} { } |
| 33 explicit Y(SkPoint pt) : fVal{pt.fY} { } |
| 34 explicit Y(SkSize s) : fVal{s.fHeight} { } |
| 35 explicit Y(SkISize s) : fVal(s.fHeight) { } |
| 36 operator SkScalar () const {return fVal;} |
| 37 private: |
| 38 SkScalar fVal; |
| 39 }; |
| 40 |
| 41 // The Span class enables efficient processing horizontal spans of pixels. |
| 42 // * start - the point where to start the span. |
| 43 // * length - the number of pixels to traverse in source space. |
| 44 // * count - the number of pixels to produce in destination space. |
| 45 // Both start and length are mapped through the inversion matrix to produce valu
es in source |
| 46 // space. After the matrix operation, the tilers may break the spans up into sma
ller spans. |
| 47 // The tilers can produce spans that seem nonsensical. |
| 48 // * The clamp tiler can create spans with length of 0. This indicates to copy a
n edge pixel out |
| 49 // to the edge of the destination scan. |
| 50 // * The mirror tiler can produce spans with negative length. This indicates tha
t the source |
| 51 // should be traversed in the opposite direction to the destination pixels. |
| 52 class Span { |
| 53 public: |
| 54 Span(SkPoint start, SkScalar length, int count) |
| 55 : fStart(start) |
| 56 , fLength(length) |
| 57 , fCount{count} { |
| 58 SkASSERT(std::isfinite(length)); |
| 59 } |
| 60 |
| 61 operator std::tuple<SkPoint&, SkScalar&, int&>() { |
| 62 return std::tie(fStart, fLength, fCount); |
| 63 } |
| 64 |
| 65 bool isEmpty() const { return 0 == fCount; } |
| 66 SkScalar length() const { return fLength; } |
| 67 SkScalar startX() const { return X(fStart); } |
| 68 SkScalar endX() const { return startX() + length(); } |
| 69 void clear() { |
| 70 fCount = 0; |
| 71 } |
| 72 |
| 73 bool completelyWithin(SkScalar xMin, SkScalar xMax) const { |
| 74 SkScalar sMin, sMax; |
| 75 std::tie(sMin, sMax) = std::minmax(startX(), endX()); |
| 76 return xMin <= sMin && sMax <= xMax; |
| 77 } |
| 78 |
| 79 void offset(SkScalar offsetX) { |
| 80 fStart.offset(offsetX, 0.0f); |
| 81 } |
| 82 |
| 83 Span breakAt(SkScalar breakX, SkScalar dx) { |
| 84 SkASSERT(std::isfinite(breakX)); |
| 85 SkASSERT(std::isfinite(dx)); |
| 86 SkASSERT(dx != 0.0f); |
| 87 |
| 88 if (this->isEmpty()) { |
| 89 return Span{{0.0, 0.0}, 0.0f, 0}; |
| 90 } |
| 91 |
| 92 int dxSteps = SkScalarFloorToInt((breakX - this->startX()) / dx); |
| 93 if (dxSteps < 0) { |
| 94 // The span is wholly after breakX. |
| 95 return Span{{0.0, 0.0}, 0.0f, 0}; |
| 96 } else if (dxSteps > fCount) { |
| 97 // The span is wholly before breakX. |
| 98 Span answer = *this; |
| 99 this->clear(); |
| 100 return answer; |
| 101 } |
| 102 |
| 103 // Calculate the values for the span to cleave off. |
| 104 SkPoint newStart = fStart; |
| 105 SkScalar newLength = dxSteps * dx; |
| 106 int newCount = dxSteps + 1; |
| 107 SkASSERT(newCount > 0); |
| 108 |
| 109 // Update this span to reflect the break. |
| 110 SkScalar lengthToStart = newLength + dx; |
| 111 fLength -= lengthToStart; |
| 112 fCount -= newCount; |
| 113 fStart = {this->startX() + lengthToStart, Y(fStart)}; |
| 114 |
| 115 return Span{newStart, newLength, newCount}; |
| 116 } |
| 117 |
| 118 void clampToSinglePixel(SkPoint pixel) { |
| 119 fStart = pixel; |
| 120 fLength = 0.0f; |
| 121 } |
| 122 |
| 123 private: |
| 124 SkPoint fStart; |
| 125 SkScalar fLength; |
| 126 int fCount; |
| 127 }; |
| 128 |
| 129 // BilerpSpans are similar to Spans, but they represent four source samples conv
erting to single |
| 130 // destination pixel per count. The pixels for the four samples are collect alon
g two horizontal |
| 131 // lines; one starting at {x, y0} and the other starting at {x, y1}. There are t
wo distinct lines |
| 132 // to deal with the edge case of the tile mode. For example, y0 may be at the la
st y position in |
| 133 // a tile while y1 would be at the first. |
| 134 // The step of a Bilerp (dx) is still length / (count - 1) and the start to the
next sample is |
| 135 // still dx * count, but the bounds are complicated by the sampling kernel so th
at the pixels |
| 136 // touched are from x to x + length + 1. |
| 137 class BilerpSpan { |
| 138 public: |
| 139 BilerpSpan(SkScalar x, SkScalar y0, SkScalar y1, SkScalar length, int count) |
| 140 : fX{x}, fY0{y0}, fY1{y1}, fLength{length}, fCount{count} { |
| 141 SkASSERT(count >= 0); |
| 142 SkASSERT(std::isfinite(length)); |
| 143 SkASSERT(std::isfinite(x)); |
| 144 SkASSERT(std::isfinite(y0)); |
| 145 SkASSERT(std::isfinite(y1)); |
| 146 } |
| 147 |
| 148 operator std::tuple<SkScalar&, SkScalar&, SkScalar&, SkScalar&, int&>() { |
| 149 return std::tie(fX, fY0, fY1, fLength, fCount); |
| 150 } |
| 151 |
| 152 bool isEmpty() const { return 0 == fCount; } |
| 153 |
| 154 private: |
| 155 SkScalar fX; |
| 156 SkScalar fY0; |
| 157 SkScalar fY1; |
| 158 SkScalar fLength; |
| 159 int fCount; |
| 160 }; |
| 161 |
| 162 template<typename Stage> |
| 163 void span_fallback(Span span, Stage* stage) { |
| 164 SkPoint start; |
| 165 SkScalar length; |
| 166 int count; |
| 167 std::tie(start, length, count) = span; |
| 168 Sk4f xs{X(start)}; |
| 169 Sk4f ys{Y(start)}; |
| 170 |
| 171 // Initializing this is not needed, but some compilers can't figure this out
. |
| 172 Sk4s fourDx{0.0f}; |
| 173 if (count > 1) { |
| 174 SkScalar dx = length / (count - 1); |
| 175 xs = xs + Sk4f{0.0f, 1.0f, 2.0f, 3.0f} * dx; |
| 176 // Only used if count is >= 4. |
| 177 fourDx = Sk4f{4.0f * dx}; |
| 178 } |
| 179 |
| 180 while (count >= 4) { |
| 181 stage->pointList4(xs, ys); |
| 182 xs = xs + fourDx; |
| 183 count -= 4; |
| 184 } |
| 185 if (count > 0) { |
| 186 stage->pointListFew(count, xs, ys); |
| 187 } |
| 188 } |
| 189 |
| 190 template <typename Next> |
| 191 void bilerp_span_fallback(BilerpSpan span, Next* next) { |
| 192 SkScalar x, y0, y1; SkScalar length; int count; |
| 193 std::tie(x, y0, y1, length, count) = span; |
| 194 |
| 195 SkASSERT(!span.isEmpty()); |
| 196 float dx = length / (count - 1); |
| 197 |
| 198 Sk4f xs = Sk4f{x} + Sk4f{0.0f, 1.0f, 0.0f, 1.0f}; |
| 199 Sk4f ys = Sk4f{y0, y0, y1, y1}; |
| 200 |
| 201 // If count == 1 then dx will be inf or NaN, but that is ok because the resu
lting addition is |
| 202 // never used. |
| 203 while (count > 0) { |
| 204 next->bilerpList(xs, ys); |
| 205 xs = xs + dx; |
| 206 count -= 1; |
| 207 } |
| 208 } |
| 209 } // namespace |
| 210 |
| 211 #endif // SkLinearBitmapPipeline_core_DEFINED |
OLD | NEW |