1/* 2 * Copyright 2008 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#include "include/core/SkShader.h" 9#include "include/private/SkTPin.h" 10#include "include/private/SkTo.h" 11#include "src/core/SkBitmapProcState.h" 12#include "src/core/SkOpts.h" 13 14/* 15 * The decal_ functions require that 16 * 1. dx > 0 17 * 2. [fx, fx+dx, fx+2dx, fx+3dx, ... fx+(count-1)dx] are all <= maxX 18 * 19 * In addition, we use SkFractionalInt to keep more fractional precision than 20 * just SkFixed, so we will abort the decal_ call if dx is very small, since 21 * the decal_ function just operates on SkFixed. If that were changed, we could 22 * skip the very_small test here. 23 */ 24static inline bool can_truncate_to_fixed_for_decal(SkFixed fx, 25 SkFixed dx, 26 int count, unsigned max) { 27 SkASSERT(count > 0); 28 29 // if decal_ kept SkFractionalInt precision, this would just be dx <= 0 30 // I just made up the 1/256. Just don't want to perceive accumulated error 31 // if we truncate frDx and lose its low bits. 32 if (dx <= SK_Fixed1 / 256) { 33 return false; 34 } 35 36 // Note: it seems the test should be (fx <= max && lastFx <= max); but 37 // historically it's been a strict inequality check, and changing produces 38 // unexpected diffs. Further investigation is needed. 39 40 // We cast to unsigned so we don't have to check for negative values, which 41 // will now appear as very large positive values, and thus fail our test! 42 if ((unsigned)SkFixedFloorToInt(fx) >= max) { 43 return false; 44 } 45 46 // Promote to 64bit (48.16) to avoid overflow. 47 const uint64_t lastFx = fx + sk_64_mul(dx, count - 1); 48 49 return SkTFitsIn<int32_t>(lastFx) && (unsigned)SkFixedFloorToInt(SkTo<int32_t>(lastFx)) < max; 50} 51 52// When not filtering, we store 32-bit y, 16-bit x, 16-bit x, 16-bit x, ... 53// When filtering we write out 32-bit encodings, pairing 14.4 x0 with 14-bit x1. 54 55// The clamp routines may try to fall into one of these unclamped decal fast-paths. 56// (Only clamp works in the right coordinate space to check for decal.) 57static void decal_nofilter_scale(uint32_t dst[], SkFixed fx, SkFixed dx, int count) { 58 // can_truncate_to_fixed_for_decal() checked only that stepping fx+=dx count-1 59 // times doesn't overflow fx, so we take unusual care not to step count times. 60 for (; count > 2; count -= 2) { 61 *dst++ = pack_two_shorts( (fx + 0) >> 16, 62 (fx + dx) >> 16); 63 fx += dx+dx; 64 } 65 66 SkASSERT(count <= 2); 67 switch (count) { 68 case 2: ((uint16_t*)dst)[1] = SkToU16((fx + dx) >> 16); [[fallthrough]]; 69 case 1: ((uint16_t*)dst)[0] = SkToU16((fx + 0) >> 16); 70 } 71} 72 73// A generic implementation for unfiltered scale+translate, templated on tiling method. 74template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), bool tryDecal> 75static void nofilter_scale(const SkBitmapProcState& s, 76 uint32_t xy[], int count, int x, int y) { 77 SkASSERT(s.fInvMatrix.isScaleTranslate()); 78 79 // Write out our 32-bit y, and get our intial fx. 80 SkFractionalInt fx; 81 { 82 const SkBitmapProcStateAutoMapper mapper(s, x, y); 83 *xy++ = tiley(mapper.fixedY(), s.fPixmap.height() - 1); 84 fx = mapper.fractionalIntX(); 85 } 86 87 const unsigned maxX = s.fPixmap.width() - 1; 88 if (0 == maxX) { 89 // If width == 1, all the x-values must refer to that pixel, and must be zero. 90 memset(xy, 0, count * sizeof(uint16_t)); 91 return; 92 } 93 94 const SkFractionalInt dx = s.fInvSxFractionalInt; 95 96 if (tryDecal) { 97 const SkFixed fixedFx = SkFractionalIntToFixed(fx); 98 const SkFixed fixedDx = SkFractionalIntToFixed(dx); 99 100 if (can_truncate_to_fixed_for_decal(fixedFx, fixedDx, count, maxX)) { 101 decal_nofilter_scale(xy, fixedFx, fixedDx, count); 102 return; 103 } 104 } 105 106 // Remember, each x-coordinate is 16-bit. 107 for (; count >= 2; count -= 2) { 108 *xy++ = pack_two_shorts(tilex(SkFractionalIntToFixed(fx ), maxX), 109 tilex(SkFractionalIntToFixed(fx + dx), maxX)); 110 fx += dx+dx; 111 } 112 113 auto xx = (uint16_t*)xy; 114 while (count --> 0) { 115 *xx++ = tilex(SkFractionalIntToFixed(fx), maxX); 116 fx += dx; 117 } 118} 119 120template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int)> 121static void nofilter_affine(const SkBitmapProcState& s, 122 uint32_t xy[], int count, int x, int y) { 123 SkASSERT(!s.fInvMatrix.hasPerspective()); 124 125 const SkBitmapProcStateAutoMapper mapper(s, x, y); 126 127 SkFractionalInt fx = mapper.fractionalIntX(), 128 fy = mapper.fractionalIntY(), 129 dx = s.fInvSxFractionalInt, 130 dy = s.fInvKyFractionalInt; 131 int maxX = s.fPixmap.width () - 1, 132 maxY = s.fPixmap.height() - 1; 133 134 while (count --> 0) { 135 *xy++ = (tiley(SkFractionalIntToFixed(fy), maxY) << 16) 136 | (tilex(SkFractionalIntToFixed(fx), maxX) ); 137 fx += dx; 138 fy += dy; 139 } 140} 141 142// used when both tilex and tiley are clamp 143// Extract the high four fractional bits from fx, the lerp parameter when filtering. 144static unsigned extract_low_bits_clamp_clamp(SkFixed fx, int /*max*/) { 145 // If we're already scaled up to by max like clamp/decal, 146 // just grab the high four fractional bits. 147 return (fx >> 12) & 0xf; 148} 149 150//used when one of tilex and tiley is not clamp 151static unsigned extract_low_bits_general(SkFixed fx, int max) { 152 // In repeat or mirror fx is in [0,1], so scale up by max first. 153 // TODO: remove the +1 here and the -1 at the call sites... 154 return extract_low_bits_clamp_clamp((fx & 0xffff) * (max+1), max); 155} 156 157template <unsigned (*tile)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int)> 158static uint32_t pack(SkFixed f, unsigned max, SkFixed one) { 159 uint32_t packed = tile(f, max); // low coordinate in high bits 160 packed = (packed << 4) | extract_low_bits(f, max); // (lerp weight _is_ coord fractional part) 161 packed = (packed << 14) | tile((f + one), max); // high coordinate in low bits 162 return packed; 163} 164 165template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int), bool tryDecal> 166static void filter_scale(const SkBitmapProcState& s, 167 uint32_t xy[], int count, int x, int y) { 168 SkASSERT(s.fInvMatrix.isScaleTranslate()); 169 170 const unsigned maxX = s.fPixmap.width() - 1; 171 const SkFractionalInt dx = s.fInvSxFractionalInt; 172 SkFractionalInt fx; 173 { 174 const SkBitmapProcStateAutoMapper mapper(s, x, y); 175 const unsigned maxY = s.fPixmap.height() - 1; 176 // compute our two Y values up front 177 *xy++ = pack<tiley, extract_low_bits>(mapper.fixedY(), maxY, s.fFilterOneY); 178 // now initialize fx 179 fx = mapper.fractionalIntX(); 180 } 181 182 // For historical reasons we check both ends are < maxX rather than <= maxX. 183 // TODO: try changing this? See also can_truncate_to_fixed_for_decal(). 184 if (tryDecal && 185 (unsigned)SkFractionalIntToInt(fx ) < maxX && 186 (unsigned)SkFractionalIntToInt(fx + dx*(count-1)) < maxX) { 187 while (count --> 0) { 188 SkFixed fixedFx = SkFractionalIntToFixed(fx); 189 SkASSERT((fixedFx >> (16 + 14)) == 0); 190 *xy++ = (fixedFx >> 12 << 14) | ((fixedFx >> 16) + 1); 191 fx += dx; 192 } 193 return; 194 } 195 196 while (count --> 0) { 197 *xy++ = pack<tilex, extract_low_bits>(SkFractionalIntToFixed(fx), maxX, s.fFilterOneX); 198 fx += dx; 199 } 200} 201 202template <unsigned (*tilex)(SkFixed, int), unsigned (*tiley)(SkFixed, int), unsigned (*extract_low_bits)(SkFixed, int)> 203static void filter_affine(const SkBitmapProcState& s, 204 uint32_t xy[], int count, int x, int y) { 205 SkASSERT(!s.fInvMatrix.hasPerspective()); 206 207 const SkBitmapProcStateAutoMapper mapper(s, x, y); 208 209 SkFixed oneX = s.fFilterOneX, 210 oneY = s.fFilterOneY; 211 212 SkFractionalInt fx = mapper.fractionalIntX(), 213 fy = mapper.fractionalIntY(), 214 dx = s.fInvSxFractionalInt, 215 dy = s.fInvKyFractionalInt; 216 unsigned maxX = s.fPixmap.width () - 1, 217 maxY = s.fPixmap.height() - 1; 218 while (count --> 0) { 219 *xy++ = pack<tiley, extract_low_bits>(SkFractionalIntToFixed(fy), maxY, oneY); 220 *xy++ = pack<tilex, extract_low_bits>(SkFractionalIntToFixed(fx), maxX, oneX); 221 222 fy += dy; 223 fx += dx; 224 } 225} 226 227// Helper to ensure that when we shift down, we do it w/o sign-extension 228// so the caller doesn't have to manually mask off the top 16 bits. 229static inline unsigned SK_USHIFT16(unsigned x) { 230 return x >> 16; 231} 232 233static unsigned repeat(SkFixed fx, int max) { 234 SkASSERT(max < 65535); 235 return SK_USHIFT16((unsigned)(fx & 0xFFFF) * (max + 1)); 236} 237static unsigned mirror(SkFixed fx, int max) { 238 SkASSERT(max < 65535); 239 // s is 0xFFFFFFFF if we're on an odd interval, or 0 if an even interval 240 SkFixed s = SkLeftShift(fx, 15) >> 31; 241 242 // This should be exactly the same as repeat(fx ^ s, max) from here on. 243 return SK_USHIFT16( ((fx ^ s) & 0xFFFF) * (max + 1) ); 244} 245 246static unsigned clamp(SkFixed fx, int max) { 247 return SkTPin(fx >> 16, 0, max); 248} 249 250static const SkBitmapProcState::MatrixProc ClampX_ClampY_Procs[] = { 251 nofilter_scale <clamp, clamp, true>, filter_scale <clamp, clamp, extract_low_bits_clamp_clamp, true>, 252 nofilter_affine<clamp, clamp>, filter_affine<clamp, clamp, extract_low_bits_clamp_clamp>, 253}; 254static const SkBitmapProcState::MatrixProc RepeatX_RepeatY_Procs[] = { 255 nofilter_scale <repeat, repeat, false>, filter_scale <repeat, repeat, extract_low_bits_general, false>, 256 nofilter_affine<repeat, repeat>, filter_affine<repeat, repeat, extract_low_bits_general> 257}; 258static const SkBitmapProcState::MatrixProc MirrorX_MirrorY_Procs[] = { 259 nofilter_scale <mirror, mirror, false>, filter_scale <mirror, mirror, extract_low_bits_general, false>, 260 nofilter_affine<mirror, mirror>, filter_affine<mirror, mirror, extract_low_bits_general>, 261}; 262 263 264/////////////////////////////////////////////////////////////////////////////// 265// This next chunk has some specializations for unfiltered translate-only matrices. 266 267static inline U16CPU int_clamp(int x, int n) { 268 if (x < 0) { x = 0; } 269 if (x >= n) { x = n - 1; } 270 return x; 271} 272 273/* returns 0...(n-1) given any x (positive or negative). 274 275 As an example, if n (which is always positive) is 5... 276 277 x: -8 -7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7 8 278 returns: 2 3 4 0 1 2 3 4 0 1 2 3 4 0 1 2 3 279 */ 280static inline int sk_int_mod(int x, int n) { 281 SkASSERT(n > 0); 282 if ((unsigned)x >= (unsigned)n) { 283 if (x < 0) { 284 x = n + ~(~x % n); 285 } else { 286 x = x % n; 287 } 288 } 289 return x; 290} 291 292static inline U16CPU int_repeat(int x, int n) { 293 return sk_int_mod(x, n); 294} 295 296static inline U16CPU int_mirror(int x, int n) { 297 x = sk_int_mod(x, 2 * n); 298 if (x >= n) { 299 x = n + ~(x - n); 300 } 301 return x; 302} 303 304static void fill_sequential(uint16_t xptr[], int pos, int count) { 305 while (count --> 0) { 306 *xptr++ = pos++; 307 } 308} 309 310static void fill_backwards(uint16_t xptr[], int pos, int count) { 311 while (count --> 0) { 312 SkASSERT(pos >= 0); 313 *xptr++ = pos--; 314 } 315} 316 317template< U16CPU (tiley)(int x, int n) > 318static void clampx_nofilter_trans(const SkBitmapProcState& s, 319 uint32_t xy[], int count, int x, int y) { 320 SkASSERT(s.fInvMatrix.isTranslate()); 321 322 const SkBitmapProcStateAutoMapper mapper(s, x, y); 323 *xy++ = tiley(mapper.intY(), s.fPixmap.height()); 324 int xpos = mapper.intX(); 325 326 const int width = s.fPixmap.width(); 327 if (1 == width) { 328 // all of the following X values must be 0 329 memset(xy, 0, count * sizeof(uint16_t)); 330 return; 331 } 332 333 uint16_t* xptr = reinterpret_cast<uint16_t*>(xy); 334 int n; 335 336 // fill before 0 as needed 337 if (xpos < 0) { 338 n = -xpos; 339 if (n > count) { 340 n = count; 341 } 342 memset(xptr, 0, n * sizeof(uint16_t)); 343 count -= n; 344 if (0 == count) { 345 return; 346 } 347 xptr += n; 348 xpos = 0; 349 } 350 351 // fill in 0..width-1 if needed 352 if (xpos < width) { 353 n = width - xpos; 354 if (n > count) { 355 n = count; 356 } 357 fill_sequential(xptr, xpos, n); 358 count -= n; 359 if (0 == count) { 360 return; 361 } 362 xptr += n; 363 } 364 365 // fill the remaining with the max value 366 sk_memset16(xptr, width - 1, count); 367} 368 369template< U16CPU (tiley)(int x, int n) > 370static void repeatx_nofilter_trans(const SkBitmapProcState& s, 371 uint32_t xy[], int count, int x, int y) { 372 SkASSERT(s.fInvMatrix.isTranslate()); 373 374 const SkBitmapProcStateAutoMapper mapper(s, x, y); 375 *xy++ = tiley(mapper.intY(), s.fPixmap.height()); 376 int xpos = mapper.intX(); 377 378 const int width = s.fPixmap.width(); 379 if (1 == width) { 380 // all of the following X values must be 0 381 memset(xy, 0, count * sizeof(uint16_t)); 382 return; 383 } 384 385 uint16_t* xptr = reinterpret_cast<uint16_t*>(xy); 386 int start = sk_int_mod(xpos, width); 387 int n = width - start; 388 if (n > count) { 389 n = count; 390 } 391 fill_sequential(xptr, start, n); 392 xptr += n; 393 count -= n; 394 395 while (count >= width) { 396 fill_sequential(xptr, 0, width); 397 xptr += width; 398 count -= width; 399 } 400 401 if (count > 0) { 402 fill_sequential(xptr, 0, count); 403 } 404} 405 406template< U16CPU (tiley)(int x, int n) > 407static void mirrorx_nofilter_trans(const SkBitmapProcState& s, 408 uint32_t xy[], int count, int x, int y) { 409 SkASSERT(s.fInvMatrix.isTranslate()); 410 411 const SkBitmapProcStateAutoMapper mapper(s, x, y); 412 *xy++ = tiley(mapper.intY(), s.fPixmap.height()); 413 int xpos = mapper.intX(); 414 415 const int width = s.fPixmap.width(); 416 if (1 == width) { 417 // all of the following X values must be 0 418 memset(xy, 0, count * sizeof(uint16_t)); 419 return; 420 } 421 422 uint16_t* xptr = reinterpret_cast<uint16_t*>(xy); 423 // need to know our start, and our initial phase (forward or backward) 424 bool forward; 425 int n; 426 int start = sk_int_mod(xpos, 2 * width); 427 if (start >= width) { 428 start = width + ~(start - width); 429 forward = false; 430 n = start + 1; // [start .. 0] 431 } else { 432 forward = true; 433 n = width - start; // [start .. width) 434 } 435 if (n > count) { 436 n = count; 437 } 438 if (forward) { 439 fill_sequential(xptr, start, n); 440 } else { 441 fill_backwards(xptr, start, n); 442 } 443 forward = !forward; 444 xptr += n; 445 count -= n; 446 447 while (count >= width) { 448 if (forward) { 449 fill_sequential(xptr, 0, width); 450 } else { 451 fill_backwards(xptr, width - 1, width); 452 } 453 forward = !forward; 454 xptr += width; 455 count -= width; 456 } 457 458 if (count > 0) { 459 if (forward) { 460 fill_sequential(xptr, 0, count); 461 } else { 462 fill_backwards(xptr, width - 1, count); 463 } 464 } 465} 466 467 468/////////////////////////////////////////////////////////////////////////////// 469// The main entry point to the file, choosing between everything above. 470 471SkBitmapProcState::MatrixProc SkBitmapProcState::chooseMatrixProc(bool translate_only_matrix) { 472 SkASSERT(!fInvMatrix.hasPerspective()); 473 SkASSERT(fTileModeX != SkTileMode::kDecal); 474 475 if( fTileModeX == fTileModeY ) { 476 // Check for our special case translate methods when there is no scale/affine/perspective. 477 if (translate_only_matrix && !fBilerp) { 478 switch (fTileModeX) { 479 default: SkASSERT(false); [[fallthrough]]; 480 case SkTileMode::kClamp: return clampx_nofilter_trans<int_clamp>; 481 case SkTileMode::kRepeat: return repeatx_nofilter_trans<int_repeat>; 482 case SkTileMode::kMirror: return mirrorx_nofilter_trans<int_mirror>; 483 } 484 } 485 486 // The arrays are all [ nofilter, filter ]. 487 int index = fBilerp ? 1 : 0; 488 if (!fInvMatrix.isScaleTranslate()) { 489 index |= 2; 490 } 491 492 if (fTileModeX == SkTileMode::kClamp) { 493 // clamp gets special version of filterOne, working in non-normalized space (allowing decal) 494 fFilterOneX = SK_Fixed1; 495 fFilterOneY = SK_Fixed1; 496 return ClampX_ClampY_Procs[index]; 497 } 498 499 // all remaining procs use this form for filterOne, putting them into normalized space. 500 fFilterOneX = SK_Fixed1 / fPixmap.width(); 501 fFilterOneY = SK_Fixed1 / fPixmap.height(); 502 503 if (fTileModeX == SkTileMode::kRepeat) { 504 return RepeatX_RepeatY_Procs[index]; 505 } 506 507 return MirrorX_MirrorY_Procs[index]; 508 } 509 510 SkASSERT(fTileModeX == fTileModeY); 511 return nullptr; 512} 513