| 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 |