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_tile_DEFINED | 8 #ifndef SkLinearBitmapPipeline_tile_DEFINED |
9 #define SkLinearBitmapPipeline_tile_DEFINED | 9 #define SkLinearBitmapPipeline_tile_DEFINED |
10 | 10 |
11 #include "SkLinearBitmapPipeline_core.h" | 11 #include "SkLinearBitmapPipeline_core.h" |
12 #include "SkPM4f.h" | 12 #include "SkPM4f.h" |
13 #include <algorithm> | 13 #include <algorithm> |
14 #include <cmath> | 14 #include <cmath> |
15 #include <limits> | 15 #include <limits> |
16 | 16 |
17 namespace { | 17 namespace { |
18 class ClampStrategy { | 18 class XClampStrategy { |
19 public: | 19 public: |
20 ClampStrategy(X max) | 20 XClampStrategy(int32_t max) |
21 : fXMin{0.0f}, fXMax{max - 1.0f} { } | 21 : fXsMax{SkScalar(max - 0.5f)} |
| 22 , fXMax{SkScalar(max)} { } |
22 | 23 |
23 ClampStrategy(Y max) | 24 void tileXPoints(Sk4s* xs) { |
24 : fYMin{0.0f}, fYMax{max - 1.0f} { } | 25 *xs = Sk4s::Min(Sk4s::Max(*xs, 0.0f), fXsMax); |
25 | 26 SkASSERT(0 <= (*xs)[0] && (*xs)[0] < fXMax); |
26 ClampStrategy(SkSize max) | 27 SkASSERT(0 <= (*xs)[1] && (*xs)[1] < fXMax); |
27 : fXMin{0.0f}, fYMin{0.0f}, fXMax{X(max) - 1.0f}, fYMax{Y(max) - 1.0f} {
} | 28 SkASSERT(0 <= (*xs)[2] && (*xs)[2] < fXMax); |
28 | 29 SkASSERT(0 <= (*xs)[3] && (*xs)[3] < fXMax); |
29 void processPoints(Sk4s* xs, Sk4s* ys) { | |
30 *xs = Sk4s::Min(Sk4s::Max(*xs, fXMin), fXMax); | |
31 *ys = Sk4s::Min(Sk4s::Max(*ys, fYMin), fYMax); | |
32 } | 30 } |
33 | 31 |
34 template<typename Next> | 32 template<typename Next> |
35 bool maybeProcessSpan(Span originalSpan, Next* next) { | 33 bool maybeProcessSpan(Span originalSpan, Next* next) { |
36 SkASSERT(!originalSpan.isEmpty()); | 34 SkASSERT(!originalSpan.isEmpty()); |
37 SkPoint start; | 35 SkPoint start; SkScalar length; int count; |
38 SkScalar length; | |
39 int count; | |
40 std::tie(start, length, count) = originalSpan; | 36 std::tie(start, length, count) = originalSpan; |
41 SkScalar xMin = fXMin[0]; | |
42 SkScalar xMax = fXMax[0] + 1.0f; | |
43 SkScalar yMin = fYMin[0]; | |
44 SkScalar yMax = fYMax[0]; | |
45 SkScalar x = X(start); | 37 SkScalar x = X(start); |
46 SkScalar y = std::min(std::max<SkScalar>(yMin, Y(start)), yMax); | 38 SkScalar y = Y(start); |
47 | |
48 Span span{{x, y}, length, count}; | 39 Span span{{x, y}, length, count}; |
49 | 40 |
50 if (span.completelyWithin(xMin, xMax)) { | 41 if (span.completelyWithin(0.0f, fXMax)) { |
51 next->pointSpan(span); | 42 next->pointSpan(span); |
52 return true; | 43 return true; |
53 } | 44 } |
54 if (1 == count || 0.0f == length) { | 45 if (1 == count || 0.0f == length) { |
55 return false; | 46 return false; |
56 } | 47 } |
57 | 48 |
58 SkScalar dx = length / (count - 1); | 49 SkScalar dx = length / (count - 1); |
59 | 50 |
60 // A B C | 51 // A B C |
(...skipping 17 matching lines...) Expand all Loading... |
78 // span starting at C is 5 * dx, so count * dx. | 69 // span starting at C is 5 * dx, so count * dx. |
79 // Remember, count is the number of pixels needed for the destination an
d the number of | 70 // Remember, count is the number of pixels needed for the destination an
d the number of |
80 // samples. | 71 // samples. |
81 // Overall Strategy: | 72 // Overall Strategy: |
82 // * Under - for portions of the span < xMin, take the color at pixel {x
Min, y} and use it | 73 // * Under - for portions of the span < xMin, take the color at pixel {x
Min, y} and use it |
83 // to fill in the 5 pixel sampled from A to B. | 74 // to fill in the 5 pixel sampled from A to B. |
84 // * Middle - for the portion of the span between xMin and xMax sample n
ormally. | 75 // * Middle - for the portion of the span between xMin and xMax sample n
ormally. |
85 // * Over - for the portion of the span > xMax, take the color at pixel
{xMax-1, y} and | 76 // * Over - for the portion of the span > xMax, take the color at pixel
{xMax-1, y} and |
86 // use it to fill in the rest of the destination pixels. | 77 // use it to fill in the rest of the destination pixels. |
87 if (dx >= 0) { | 78 if (dx >= 0) { |
88 Span leftClamped = span.breakAt(xMin, dx); | 79 Span leftClamped = span.breakAt(0.0f, dx); |
89 if (!leftClamped.isEmpty()) { | 80 if (!leftClamped.isEmpty()) { |
90 leftClamped.clampToSinglePixel({xMin, y}); | 81 leftClamped.clampToSinglePixel({0.0f, y}); |
91 next->pointSpan(leftClamped); | 82 next->pointSpan(leftClamped); |
92 } | 83 } |
93 Span middle = span.breakAt(xMax, dx); | 84 Span center = span.breakAt(fXMax, dx); |
94 if (!middle.isEmpty()) { | 85 if (!center.isEmpty()) { |
95 next->pointSpan(middle); | 86 next->pointSpan(center); |
96 } | 87 } |
97 if (!span.isEmpty()) { | 88 if (!span.isEmpty()) { |
98 span.clampToSinglePixel({xMax - 1, y}); | 89 span.clampToSinglePixel({fXMax - 1, y}); |
99 next->pointSpan(span); | 90 next->pointSpan(span); |
100 } | 91 } |
101 } else { | 92 } else { |
102 Span rightClamped = span.breakAt(xMax, dx); | 93 Span center = span.breakAt(fXMax, dx); |
103 | 94 |
104 if (!rightClamped.isEmpty()) { | 95 if (!span.isEmpty()) { |
105 rightClamped.clampToSinglePixel({xMax - 1, y}); | 96 span.clampToSinglePixel({fXMax - 1, y}); |
106 next->pointSpan(rightClamped); | 97 next->pointSpan(span); |
107 } | 98 } |
108 Span middle = span.breakAt(xMin, dx); | 99 Span leftEdge = center.breakAt(0.0f, dx); |
109 if (!middle.isEmpty()) { | 100 if (!center.isEmpty()) { |
110 next->pointSpan(middle); | 101 next->pointSpan(center); |
111 } | 102 } |
112 if (!span.isEmpty()) { | 103 if (!leftEdge.isEmpty()) { |
113 span.clampToSinglePixel({xMin, y}); | 104 leftEdge.clampToSinglePixel({0.0f, y}); |
114 next->pointSpan(span); | 105 next->pointSpan(leftEdge); |
115 } | 106 } |
116 } | 107 } |
117 return true; | 108 return true; |
118 } | 109 } |
119 | 110 |
120 template <typename Next> | 111 private: |
121 bool maybeProcessBilerpSpan(BilerpSpan bSpan, Next* next) { | 112 const Sk4s fXsMax; |
122 return false; | 113 const SkScalar fXMax; |
| 114 }; |
| 115 |
| 116 class YClampStrategy { |
| 117 public: |
| 118 YClampStrategy(int32_t max) |
| 119 : fYMax{SkScalar(max) - 0.5f} |
| 120 , fYsMax{SkScalar(max) - 0.5f} { } |
| 121 |
| 122 void tileYPoints(Sk4s* ys) { |
| 123 *ys = Sk4s::Min(Sk4s::Max(*ys, 0.0f), fYsMax); |
| 124 SkASSERT(0 <= (*ys)[0] && (*ys)[0] <= fYMax); |
| 125 SkASSERT(0 <= (*ys)[1] && (*ys)[1] <= fYMax); |
| 126 SkASSERT(0 <= (*ys)[2] && (*ys)[2] <= fYMax); |
| 127 SkASSERT(0 <= (*ys)[3] && (*ys)[3] <= fYMax); |
| 128 } |
| 129 |
| 130 SkScalar tileY(SkScalar y) { |
| 131 return std::min(std::max<SkScalar>(0.0f, y), fYMax); |
123 } | 132 } |
124 | 133 |
125 private: | 134 private: |
126 const Sk4s fXMin{SK_FloatNegativeInfinity}; | 135 const SkScalar fYMax; |
127 const Sk4s fYMin{SK_FloatNegativeInfinity}; | 136 const Sk4s fYsMax; |
128 const Sk4s fXMax{SK_FloatInfinity}; | |
129 const Sk4s fYMax{SK_FloatInfinity}; | |
130 }; | 137 }; |
131 | 138 |
132 class RepeatStrategy { | 139 SkScalar tile_mod(SkScalar x, SkScalar base) { |
| 140 return x - SkScalarFloorToScalar(x / base) * base; |
| 141 } |
| 142 |
| 143 class XRepeatStrategy { |
133 public: | 144 public: |
134 RepeatStrategy(X max) : fXMax{max}, fXInvMax{1.0f / max} { } | 145 XRepeatStrategy(int32_t max) |
| 146 : fXMax{SkScalar(max)} |
| 147 , fXsMax{SkScalar(max)} |
| 148 , fXsCap{SkScalar(nextafterf(SkScalar(max), 0.0f))} |
| 149 , fXsInvMax{1.0f / SkScalar(max)} { } |
135 | 150 |
136 RepeatStrategy(Y max) : fYMax{max}, fYInvMax{1.0f / max} { } | 151 void tileXPoints(Sk4s* xs) { |
137 | 152 Sk4s divX = *xs * fXsInvMax; |
138 RepeatStrategy(SkSize max) | 153 Sk4s modX = *xs - divX.floor() * fXsMax; |
139 : fXMax{X(max)}, fXInvMax{1.0f / X(max)}, fYMax{Y(max)}, fYInvMax{1.0f /
Y(max)} { } | 154 *xs = Sk4s::Min(fXsCap, modX); |
140 | 155 SkASSERT(0 <= (*xs)[0] && (*xs)[0] < fXMax); |
141 void processPoints(Sk4s* xs, Sk4s* ys) { | 156 SkASSERT(0 <= (*xs)[1] && (*xs)[1] < fXMax); |
142 Sk4s divX = (*xs * fXInvMax).floor(); | 157 SkASSERT(0 <= (*xs)[2] && (*xs)[2] < fXMax); |
143 Sk4s divY = (*ys * fYInvMax).floor(); | 158 SkASSERT(0 <= (*xs)[3] && (*xs)[3] < fXMax); |
144 Sk4s baseX = (divX * fXMax); | |
145 Sk4s baseY = (divY * fYMax); | |
146 *xs = *xs - baseX; | |
147 *ys = *ys - baseY; | |
148 } | 159 } |
149 | 160 |
150 template<typename Next> | 161 template<typename Next> |
151 bool maybeProcessSpan(Span originalSpan, Next* next) { | 162 bool maybeProcessSpan(Span originalSpan, Next* next) { |
152 SkASSERT(!originalSpan.isEmpty()); | 163 SkASSERT(!originalSpan.isEmpty()); |
153 SkPoint start; | 164 SkPoint start; SkScalar length; int count; |
154 SkScalar length; | |
155 int count; | |
156 std::tie(start, length, count) = originalSpan; | 165 std::tie(start, length, count) = originalSpan; |
157 // Make x and y in range on the tile. | 166 // Make x and y in range on the tile. |
158 SkScalar x = TileMod(X(start), fXMax[0]); | 167 SkScalar x = tile_mod(X(start), fXMax); |
159 SkScalar y = TileMod(Y(start), fYMax[0]); | 168 SkScalar y = Y(start); |
160 SkScalar xMax = fXMax[0]; | |
161 SkScalar xMin = 0.0f; | |
162 SkScalar dx = length / (count - 1); | 169 SkScalar dx = length / (count - 1); |
163 | 170 |
164 // No need trying to go fast because the steps are larger than a tile or
there is one point. | 171 // No need trying to go fast because the steps are larger than a tile or
there is one point. |
165 if (SkScalarAbs(dx) >= xMax || count <= 1) { | 172 if (SkScalarAbs(dx) >= fXMax || count <= 1) { |
166 return false; | 173 return false; |
167 } | 174 } |
168 | 175 |
169 // A B C D Z | 176 // A B C D Z |
170 // +-------+-------+-------++-------+-------+-------++ +-------+----
---++------ | 177 // +-------+-------+-------++-------+-------+-------++ +-------+----
---++------ |
171 // | | *---|*---*--||-*---*-|---*---|*---*--|| |--*---*|
|| | 178 // | | *---|*---*--||-*---*-|---*---|*---*--|| |--*---*|
|| |
172 // | | | || | | || ... | |
|| | 179 // | | | || | | || ... | |
|| |
173 // | | | || | | || | |
|| | 180 // | | | || | | || | |
|| |
174 // +-------+-------+-------++-------+-------+-------++ +-------+----
---++------ | 181 // +-------+-------+-------++-------+-------+-------++ +-------+----
---++------ |
175 // ^^ ^^
^^ | 182 // ^^ ^^
^^ |
(...skipping 16 matching lines...) Expand all Loading... |
192 // | 199 // |
193 // Overall Strategy: | 200 // Overall Strategy: |
194 // While the span hangs over the edge of the tile, draw the span coverin
g the tile then | 201 // While the span hangs over the edge of the tile, draw the span coverin
g the tile then |
195 // slide the span over to the next tile. | 202 // slide the span over to the next tile. |
196 | 203 |
197 // The guard could have been count > 0, but then a bunch of math would b
e done in the | 204 // The guard could have been count > 0, but then a bunch of math would b
e done in the |
198 // common case. | 205 // common case. |
199 | 206 |
200 Span span({x, y}, length, count); | 207 Span span({x, y}, length, count); |
201 if (dx > 0) { | 208 if (dx > 0) { |
202 while (!span.isEmpty() && span.endX() >= xMax) { | 209 while (!span.isEmpty() && span.endX() >= fXMax) { |
203 Span toDraw = span.breakAt(xMax, dx); | 210 Span toDraw = span.breakAt(fXMax, dx); |
204 next->pointSpan(toDraw); | 211 next->pointSpan(toDraw); |
205 span.offset(-xMax); | 212 span.offset(-fXMax); |
206 } | 213 } |
207 } else { | 214 } else { |
208 while (!span.isEmpty() && span.endX() < xMin) { | 215 while (!span.isEmpty() && span.endX() < 0.0f) { |
209 Span toDraw = span.breakAt(xMin, dx); | 216 Span toDraw = span.breakAt(0.0f, dx); |
210 next->pointSpan(toDraw); | 217 next->pointSpan(toDraw); |
211 span.offset(xMax); | 218 span.offset(fXMax); |
212 } | 219 } |
213 } | 220 } |
214 | 221 |
215 // All on a single tile. | 222 // All on a single tile. |
216 if (!span.isEmpty()) { | 223 if (!span.isEmpty()) { |
217 next->pointSpan(span); | 224 next->pointSpan(span); |
218 } | 225 } |
219 | 226 |
220 return true; | 227 return true; |
221 } | 228 } |
222 | 229 |
223 template <typename Next> | 230 private: |
224 bool maybeProcessBilerpSpan(BilerpSpan bSpan, Next* next) { | 231 const SkScalar fXMax; |
225 return false; | 232 const Sk4s fXsMax; |
| 233 const Sk4s fXsCap; |
| 234 const Sk4s fXsInvMax; |
| 235 }; |
| 236 |
| 237 class YRepeatStrategy { |
| 238 public: |
| 239 YRepeatStrategy(int32_t max) |
| 240 : fYMax{SkScalar(max)} |
| 241 , fYsMax{SkScalar(max)} |
| 242 , fYsInvMax{1.0f / SkScalar(max)} { } |
| 243 |
| 244 void tileYPoints(Sk4s* ys) { |
| 245 Sk4s divY = *ys * fYsInvMax; |
| 246 Sk4s modY = *ys - divY.floor() * fYsMax; |
| 247 *ys = modY; |
| 248 SkASSERT(0 <= (*ys)[0] && (*ys)[0] < fYMax); |
| 249 SkASSERT(0 <= (*ys)[1] && (*ys)[1] < fYMax); |
| 250 SkASSERT(0 <= (*ys)[2] && (*ys)[2] < fYMax); |
| 251 SkASSERT(0 <= (*ys)[3] && (*ys)[3] < fYMax); |
| 252 } |
| 253 |
| 254 SkScalar tileY(SkScalar y) { |
| 255 SkScalar answer = tile_mod(y, fYMax); |
| 256 SkASSERT(0 <= answer && answer < fYMax); |
| 257 return answer; |
226 } | 258 } |
227 | 259 |
228 private: | 260 private: |
229 SkScalar TileMod(SkScalar x, SkScalar base) { | 261 const SkScalar fYMax; |
230 return x - std::floor(x / base) * base; | 262 const Sk4s fYsMax; |
| 263 const Sk4s fYsInvMax; |
| 264 }; |
| 265 // max = 40 |
| 266 // mq2[x_] := Abs[(x - 40) - Floor[(x - 40)/80] * 80 - 40] |
| 267 class XMirrorStrategy { |
| 268 public: |
| 269 XMirrorStrategy(int32_t max) |
| 270 : fXsMax{SkScalar(max)} |
| 271 , fXsCap{SkScalar(nextafterf(SkScalar(max), 0.0f))} |
| 272 , fXsDoubleInvMax{1.0f / (2.0f * SkScalar(max))} { } |
| 273 |
| 274 void tileXPoints(Sk4s* xs) { |
| 275 Sk4f bias = *xs - fXsMax; |
| 276 Sk4f div = bias * fXsDoubleInvMax; |
| 277 Sk4f mod = bias - div.floor() * 2.0f * fXsMax; |
| 278 Sk4f unbias = mod - fXsMax; |
| 279 *xs = Sk4f::Min(unbias.abs(), fXsCap); |
| 280 SkASSERT(0 <= (*xs)[0] && (*xs)[0] < fXsMax[0]); |
| 281 SkASSERT(0 <= (*xs)[1] && (*xs)[1] < fXsMax[0]); |
| 282 SkASSERT(0 <= (*xs)[2] && (*xs)[2] < fXsMax[0]); |
| 283 SkASSERT(0 <= (*xs)[3] && (*xs)[3] < fXsMax[0]); |
231 } | 284 } |
232 const Sk4s fXMax{0.0f}; | 285 |
233 const Sk4s fXInvMax{0.0f}; | 286 template <typename Next> |
234 const Sk4s fYMax{0.0f}; | 287 bool maybeProcessSpan(Span originalSpan, Next* next) { return false; } |
235 const Sk4s fYInvMax{0.0f}; | 288 |
| 289 private: |
| 290 Sk4f fXsMax; |
| 291 Sk4f fXsCap; |
| 292 Sk4f fXsDoubleInvMax; |
| 293 }; |
| 294 |
| 295 class YMirrorStrategy { |
| 296 public: |
| 297 YMirrorStrategy(int32_t max) |
| 298 : fYMax{SkScalar(max)} |
| 299 , fYsMax{SkScalar(max)} |
| 300 , fYsCap{nextafterf(SkScalar(max), 0.0f)} |
| 301 , fYsDoubleInvMax{1.0f / (2.0f * SkScalar(max))} { } |
| 302 |
| 303 void tileYPoints(Sk4s* ys) { |
| 304 Sk4f bias = *ys - fYsMax; |
| 305 Sk4f div = bias * fYsDoubleInvMax; |
| 306 Sk4f mod = bias - div.floor() * 2.0f * fYsMax; |
| 307 Sk4f unbias = mod - fYsMax; |
| 308 *ys = Sk4f::Min(unbias.abs(), fYsCap); |
| 309 SkASSERT(0 <= (*ys)[0] && (*ys)[0] < fYMax); |
| 310 SkASSERT(0 <= (*ys)[1] && (*ys)[1] < fYMax); |
| 311 SkASSERT(0 <= (*ys)[2] && (*ys)[2] < fYMax); |
| 312 SkASSERT(0 <= (*ys)[3] && (*ys)[3] < fYMax); |
| 313 } |
| 314 |
| 315 SkScalar tileY(SkScalar y) { |
| 316 SkScalar bias = y - fYMax; |
| 317 SkScalar div = bias * fYsDoubleInvMax[0]; |
| 318 SkScalar mod = bias - SkScalarFloorToScalar(div) * 2.0f * fYMax; |
| 319 SkScalar unbias = mod - fYMax; |
| 320 SkScalar answer = SkMinScalar(SkScalarAbs(unbias), fYsCap[0]); |
| 321 SkASSERT(0 <= answer && answer < fYMax); |
| 322 return answer; |
| 323 }; |
| 324 |
| 325 private: |
| 326 SkScalar fYMax; |
| 327 Sk4f fYsMax; |
| 328 Sk4f fYsCap; |
| 329 Sk4f fYsDoubleInvMax; |
236 }; | 330 }; |
237 | 331 |
238 } // namespace | 332 } // namespace |
239 #endif // SkLinearBitmapPipeline_tile_DEFINED | 333 #endif // SkLinearBitmapPipeline_tile_DEFINED |
OLD | NEW |