| OLD | NEW |
| 1 /* | 1 /* |
| 2 * Copyright 2016 Google Inc. | 2 * Copyright 2016 Google Inc. |
| 3 * | 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
| 6 */ | 6 */ |
| 7 | 7 |
| 8 #ifndef SkLinearBitmapPipeline_core_DEFINED | 8 #ifndef SkLinearBitmapPipeline_core_DEFINED |
| 9 #define SkLinearBitmapPipeline_core_DEFINED | 9 #define SkLinearBitmapPipeline_core_DEFINED |
| 10 | 10 |
| 11 #include <cmath> | 11 #include <cmath> |
| 12 | 12 |
| 13 // New bilerp strategy: |
| 14 // Pass through on bilerpList4 and bilerpListFew (analogs to pointList), introdu
ce bilerpEdge |
| 15 // which takes 4 points. If the sample spans an edge, then break it into a biler
pEdge. Bilerp |
| 16 // span then becomes a normal span except in special cases where an extra Y is g
iven. The bilerp |
| 17 // need to stay single point calculations until the tile layer. |
| 18 // TODO: |
| 19 // - edge span predicate. |
| 20 // - introduce new point API |
| 21 // - Add tile for new api. |
| 22 |
| 13 // Tweak ABI of functions that pass Sk4f by value to pass them via registers. | 23 // Tweak ABI of functions that pass Sk4f by value to pass them via registers. |
| 14 #if defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 | 24 #if defined(_MSC_VER) && SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE2 |
| 15 #define VECTORCALL __vectorcall | 25 #define VECTORCALL __vectorcall |
| 16 #elif defined(SK_CPU_ARM32) && defined(SK_ARM_HAS_NEON) | 26 #elif defined(SK_CPU_ARM32) && defined(SK_ARM_HAS_NEON) |
| 17 #define VECTORCALL __attribute__((pcs("aapcs-vfp"))) | 27 #define VECTORCALL __attribute__((pcs("aapcs-vfp"))) |
| 18 #else | 28 #else |
| 19 #define VECTORCALL | 29 #define VECTORCALL |
| 20 #endif | 30 #endif |
| 21 | 31 |
| 22 namespace { | 32 namespace { |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 58 , fLength(length) | 68 , fLength(length) |
| 59 , fCount{count} { | 69 , fCount{count} { |
| 60 SkASSERT(std::isfinite(length)); | 70 SkASSERT(std::isfinite(length)); |
| 61 } | 71 } |
| 62 | 72 |
| 63 operator std::tuple<SkPoint&, SkScalar&, int&>() { | 73 operator std::tuple<SkPoint&, SkScalar&, int&>() { |
| 64 return std::tie(fStart, fLength, fCount); | 74 return std::tie(fStart, fLength, fCount); |
| 65 } | 75 } |
| 66 | 76 |
| 67 bool isEmpty() const { return 0 == fCount; } | 77 bool isEmpty() const { return 0 == fCount; } |
| 78 void clear() { fCount = 0; } |
| 79 int count() const { return fCount; } |
| 68 SkScalar length() const { return fLength; } | 80 SkScalar length() const { return fLength; } |
| 69 SkScalar startX() const { return X(fStart); } | 81 SkScalar startX() const { return X(fStart); } |
| 70 SkScalar endX() const { return startX() + length(); } | 82 SkScalar endX() const { return this->startX() + this->length(); } |
| 71 void clear() { | 83 SkScalar startY() const { return Y(fStart); } |
| 72 fCount = 0; | 84 Span emptySpan() { return Span{{0.0, 0.0}, 0.0f, 0}; } |
| 73 } | |
| 74 | 85 |
| 75 bool completelyWithin(SkScalar xMin, SkScalar xMax) const { | 86 bool completelyWithin(SkScalar xMin, SkScalar xMax) const { |
| 76 SkScalar sMin, sMax; | 87 SkScalar sMin, sMax; |
| 77 std::tie(sMin, sMax) = std::minmax(startX(), endX()); | 88 std::tie(sMin, sMax) = std::minmax(startX(), endX()); |
| 78 return xMin <= sMin && sMax < xMax; | 89 return xMin <= sMin && sMax < xMax; |
| 79 } | 90 } |
| 80 | 91 |
| 81 void offset(SkScalar offsetX) { | 92 void offset(SkScalar offsetX) { |
| 82 fStart.offset(offsetX, 0.0f); | 93 fStart.offset(offsetX, 0.0f); |
| 83 } | 94 } |
| 84 | 95 |
| 85 Span breakAt(SkScalar breakX, SkScalar dx) { | 96 Span breakAt(SkScalar breakX, SkScalar dx) { |
| 86 SkASSERT(std::isfinite(breakX)); | 97 SkASSERT(std::isfinite(breakX)); |
| 87 SkASSERT(std::isfinite(dx)); | 98 SkASSERT(std::isfinite(dx)); |
| 88 SkASSERT(dx != 0.0f); | 99 SkASSERT(dx != 0.0f); |
| 89 | 100 |
| 90 if (this->isEmpty()) { | 101 if (this->isEmpty()) { |
| 91 return Span{{0.0, 0.0}, 0.0f, 0}; | 102 return this->emptySpan(); |
| 92 } | 103 } |
| 93 | 104 |
| 94 int dxSteps = SkScalarFloorToInt((breakX - this->startX()) / dx); | 105 int dxSteps = SkScalarFloorToInt((breakX - this->startX()) / dx); |
| 95 | 106 |
| 96 // Calculate the values for the span to cleave off. | |
| 97 SkScalar newLength = dxSteps * dx; | |
| 98 | |
| 99 if (dxSteps < 0) { | 107 if (dxSteps < 0) { |
| 100 // The span is wholly after breakX. | 108 // The span is wholly after breakX. |
| 101 return Span{{0.0, 0.0}, 0.0f, 0}; | 109 return this->emptySpan(); |
| 102 } else if (dxSteps >= fCount) { | 110 } else if (dxSteps >= fCount) { |
| 103 // The span is wholly before breakX. | 111 // The span is wholly before breakX. |
| 104 Span answer = *this; | 112 Span answer = *this; |
| 105 this->clear(); | 113 this->clear(); |
| 106 return answer; | 114 return answer; |
| 107 } | 115 } |
| 108 | 116 |
| 117 // Calculate the values for the span to cleave off. |
| 118 SkScalar newLength = dxSteps * dx; |
| 119 |
| 109 // If the last (or first if count = 1) sample lands directly on the boun
dary. Include it | 120 // If the last (or first if count = 1) sample lands directly on the boun
dary. Include it |
| 110 // when dx < 0 and exclude it when dx > 0. | 121 // when dx < 0 and exclude it when dx > 0. |
| 111 // Reasoning: | 122 // Reasoning: |
| 112 // dx > 0: The sample point on the boundary is part of the next span be
cause the entire | 123 // dx > 0: The sample point on the boundary is part of the next span be
cause the entire |
| 113 // pixel is after the boundary. | 124 // pixel is after the boundary. |
| 114 // dx < 0: The sample point on the boundary is part of the current span
because the | 125 // dx < 0: The sample point on the boundary is part of the current span
because the |
| 115 // entire pixel is before the boundary. | 126 // entire pixel is before the boundary. |
| 116 if (startX() + newLength == breakX && dx > 0) { | 127 if (this->startX() + newLength == breakX && dx > 0) { |
| 117 if (dxSteps != 0) { | 128 if (dxSteps > 0) { |
| 118 dxSteps -= 1; | 129 dxSteps -= 1; |
| 119 newLength -= dx; | 130 newLength -= dx; |
| 120 } else { | 131 } else { |
| 121 return Span{{0.0, 0.0}, 0.0f, 0}; | 132 return this->emptySpan(); |
| 122 } | 133 } |
| 123 } | 134 } |
| 124 | 135 |
| 136 // Calculate new span parameters |
| 125 SkPoint newStart = fStart; | 137 SkPoint newStart = fStart; |
| 126 int newCount = dxSteps + 1; | 138 int newCount = dxSteps + 1; |
| 127 SkASSERT(newCount > 0); | 139 SkASSERT(newCount > 0); |
| 128 | 140 |
| 129 // Update this span to reflect the break. | 141 // Update this span to reflect the break. |
| 130 SkScalar lengthToStart = newLength + dx; | 142 SkScalar lengthToStart = newLength + dx; |
| 131 fLength -= lengthToStart; | 143 fLength -= lengthToStart; |
| 132 fCount -= newCount; | 144 fCount -= newCount; |
| 133 fStart = {this->startX() + lengthToStart, Y(fStart)}; | 145 fStart = {this->startX() + lengthToStart, Y(fStart)}; |
| 134 | 146 |
| 135 return Span{newStart, newLength, newCount}; | 147 return Span{newStart, newLength, newCount}; |
| 136 } | 148 } |
| 137 | 149 |
| 138 void clampToSinglePixel(SkPoint pixel) { | 150 void clampToSinglePixel(SkPoint pixel) { |
| 139 fStart = pixel; | 151 fStart = pixel; |
| 140 fLength = 0.0f; | 152 fLength = 0.0f; |
| 141 } | 153 } |
| 142 | 154 |
| 143 private: | 155 private: |
| 144 SkPoint fStart; | 156 SkPoint fStart; |
| 145 SkScalar fLength; | 157 SkScalar fLength; |
| 146 int fCount; | 158 int fCount; |
| 147 }; | 159 }; |
| 148 | 160 |
| 149 // BilerpSpans are similar to Spans, but they represent four source samples conv
erting to single | |
| 150 // destination pixel per count. The pixels for the four samples are collect alon
g two horizontal | |
| 151 // lines; one starting at {x, y0} and the other starting at {x, y1}. There are t
wo distinct lines | |
| 152 // to deal with the edge case of the tile mode. For example, y0 may be at the la
st y position in | |
| 153 // a tile while y1 would be at the first. | |
| 154 // The step of a Bilerp (dx) is still length / (count - 1) and the start to the
next sample is | |
| 155 // still dx * count, but the bounds are complicated by the sampling kernel so th
at the pixels | |
| 156 // touched are from x to x + length + 1. | |
| 157 class BilerpSpan { | |
| 158 public: | |
| 159 BilerpSpan(SkScalar x, SkScalar y0, SkScalar y1, SkScalar length, int count) | |
| 160 : fX{x}, fY0{y0}, fY1{y1}, fLength{length}, fCount{count} { | |
| 161 SkASSERT(count >= 0); | |
| 162 SkASSERT(std::isfinite(length)); | |
| 163 SkASSERT(std::isfinite(x)); | |
| 164 SkASSERT(std::isfinite(y0)); | |
| 165 SkASSERT(std::isfinite(y1)); | |
| 166 } | |
| 167 | |
| 168 operator std::tuple<SkScalar&, SkScalar&, SkScalar&, SkScalar&, int&>() { | |
| 169 return std::tie(fX, fY0, fY1, fLength, fCount); | |
| 170 } | |
| 171 | |
| 172 bool isEmpty() const { return 0 == fCount; } | |
| 173 | |
| 174 private: | |
| 175 SkScalar fX; | |
| 176 SkScalar fY0; | |
| 177 SkScalar fY1; | |
| 178 SkScalar fLength; | |
| 179 int fCount; | |
| 180 }; | |
| 181 | |
| 182 template<typename Stage> | 161 template<typename Stage> |
| 183 void span_fallback(Span span, Stage* stage) { | 162 void span_fallback(Span span, Stage* stage) { |
| 184 SkPoint start; | 163 SkPoint start; |
| 185 SkScalar length; | 164 SkScalar length; |
| 186 int count; | 165 int count; |
| 187 std::tie(start, length, count) = span; | 166 std::tie(start, length, count) = span; |
| 188 Sk4f xs{X(start)}; | 167 Sk4f xs{X(start)}; |
| 189 Sk4f ys{Y(start)}; | 168 Sk4f ys{Y(start)}; |
| 190 | 169 |
| 191 // Initializing this is not needed, but some compilers can't figure this out
. | 170 // Initializing this is not needed, but some compilers can't figure this out
. |
| 192 Sk4s fourDx{0.0f}; | 171 Sk4s fourDx{0.0f}; |
| 193 if (count > 1) { | 172 if (count > 1) { |
| 194 SkScalar dx = length / (count - 1); | 173 SkScalar dx = length / (count - 1); |
| 195 xs = xs + Sk4f{0.0f, 1.0f, 2.0f, 3.0f} * dx; | 174 xs = xs + Sk4f{0.0f, 1.0f, 2.0f, 3.0f} * dx; |
| 196 // Only used if count is >= 4. | 175 // Only used if count is >= 4. |
| 197 fourDx = Sk4f{4.0f * dx}; | 176 fourDx = Sk4f{4.0f * dx}; |
| 198 } | 177 } |
| 199 | 178 |
| 200 while (count >= 4) { | 179 while (count >= 4) { |
| 201 stage->pointList4(xs, ys); | 180 stage->pointList4(xs, ys); |
| 202 xs = xs + fourDx; | 181 xs = xs + fourDx; |
| 203 count -= 4; | 182 count -= 4; |
| 204 } | 183 } |
| 205 if (count > 0) { | 184 if (count > 0) { |
| 206 stage->pointListFew(count, xs, ys); | 185 stage->pointListFew(count, xs, ys); |
| 207 } | 186 } |
| 208 } | 187 } |
| 209 | |
| 210 template <typename Next> | |
| 211 void bilerp_span_fallback(BilerpSpan span, Next* next) { | |
| 212 SkScalar x, y0, y1; SkScalar length; int count; | |
| 213 std::tie(x, y0, y1, length, count) = span; | |
| 214 | |
| 215 SkASSERT(!span.isEmpty()); | |
| 216 float dx = length / (count - 1); | |
| 217 | |
| 218 Sk4f xs = Sk4f{x} + Sk4f{0.0f, 1.0f, 0.0f, 1.0f}; | |
| 219 Sk4f ys = Sk4f{y0, y0, y1, y1}; | |
| 220 | |
| 221 // If count == 1 then dx will be inf or NaN, but that is ok because the resu
lting addition is | |
| 222 // never used. | |
| 223 while (count > 0) { | |
| 224 next->bilerpList(xs, ys); | |
| 225 xs = xs + dx; | |
| 226 count -= 1; | |
| 227 } | |
| 228 } | |
| 229 } // namespace | 188 } // namespace |
| 230 | 189 |
| 231 #endif // SkLinearBitmapPipeline_core_DEFINED | 190 #endif // SkLinearBitmapPipeline_core_DEFINED |
| OLD | NEW |