1 /* 2 * Copyright 2011 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 "SkBitmapCache.h" 9 #include "SkBitmapController.h" 10 #include "SkBitmapProcState.h" 11 #include "SkColorData.h" 12 #include "SkPaint.h" 13 #include "SkShader.h" // for tilemodes 14 #include "SkUtilsArm.h" 15 #include "SkMipMap.h" 16 #include "SkPixelRef.h" 17 #include "SkImageEncoder.h" 18 #include "SkResourceCache.h" 19 20 #if defined(SK_ARM_HAS_NEON) 21 // These are defined in src/opts/SkBitmapProcState_arm_neon.cpp 22 extern const SkBitmapProcState::SampleProc32 gSkBitmapProcStateSample32_neon[]; 23 #endif 24 25 extern void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void*, int, int, uint32_t*, int); 26 27 #define NAME_WRAP(x) x 28 #include "SkBitmapProcState_filter.h" 29 #include "SkBitmapProcState_procs.h" 30 31 SkBitmapProcInfo::SkBitmapProcInfo(const SkBitmapProvider& provider, 32 SkShader::TileMode tmx, SkShader::TileMode tmy) 33 : fProvider(provider) 34 , fTileModeX(tmx) 35 , fTileModeY(tmy) 36 , fBMState(nullptr) 37 {} 38 39 SkBitmapProcInfo::~SkBitmapProcInfo() { 40 SkInPlaceDeleteCheck(fBMState, fBMStateStorage.get()); 41 } 42 43 /////////////////////////////////////////////////////////////////////////////// 44 45 // true iff the matrix has a scale and no more than an optional translate. 46 static bool matrix_only_scale_translate(const SkMatrix& m) { 47 return (m.getType() & ~SkMatrix::kTranslate_Mask) == SkMatrix::kScale_Mask; 48 } 49 50 /** 51 * For the purposes of drawing bitmaps, if a matrix is "almost" translate 52 * go ahead and treat it as if it were, so that subsequent code can go fast. 53 */ 54 static bool just_trans_general(const SkMatrix& matrix) { 55 SkASSERT(matrix_only_scale_translate(matrix)); 56 57 const SkScalar tol = SK_Scalar1 / 32768; 58 59 return SkScalarNearlyZero(matrix[SkMatrix::kMScaleX] - SK_Scalar1, tol) 60 && SkScalarNearlyZero(matrix[SkMatrix::kMScaleY] - SK_Scalar1, tol); 61 } 62 63 /** 64 * Determine if the matrix can be treated as integral-only-translate, 65 * for the purpose of filtering. 66 */ 67 static bool just_trans_integral(const SkMatrix& m) { 68 static constexpr SkScalar tol = SK_Scalar1 / 256; 69 70 return m.getType() <= SkMatrix::kTranslate_Mask 71 && SkScalarNearlyEqual(m.getTranslateX(), SkScalarRoundToScalar(m.getTranslateX()), tol) 72 && SkScalarNearlyEqual(m.getTranslateY(), SkScalarRoundToScalar(m.getTranslateY()), tol); 73 } 74 75 static bool valid_for_filtering(unsigned dimension) { 76 // for filtering, width and height must fit in 14bits, since we use steal 77 // 2 bits from each to store our 4bit subpixel data 78 return (dimension & ~0x3FFF) == 0; 79 } 80 81 bool SkBitmapProcInfo::init(const SkMatrix& inv, const SkPaint& paint) { 82 SkASSERT(inv.isScaleTranslate()); 83 84 fPixmap.reset(); 85 fInvMatrix = inv; 86 fFilterQuality = paint.getFilterQuality(); 87 88 SkDefaultBitmapController controller; 89 fBMState = controller.requestBitmap(fProvider, inv, paint.getFilterQuality(), 90 fBMStateStorage.get(), fBMStateStorage.size()); 91 // Note : we allow the controller to return an empty (zero-dimension) result. Should we? 92 if (nullptr == fBMState || fBMState->pixmap().info().isEmpty()) { 93 return false; 94 } 95 fPixmap = fBMState->pixmap(); 96 fInvMatrix = fBMState->invMatrix(); 97 fRealInvMatrix = fBMState->invMatrix(); 98 fPaintColor = paint.getColor(); 99 fFilterQuality = fBMState->quality(); 100 SkASSERT(fFilterQuality <= kLow_SkFilterQuality); 101 SkASSERT(fPixmap.addr()); 102 103 bool integral_translate_only = just_trans_integral(fInvMatrix); 104 if (!integral_translate_only) { 105 // Most of the scanline procs deal with "unit" texture coordinates, as this 106 // makes it easy to perform tiling modes (repeat = (x & 0xFFFF)). To generate 107 // those, we divide the matrix by its dimensions here. 108 // 109 // We don't do this if we're either trivial (can ignore the matrix) or clamping 110 // in both X and Y since clamping to width,height is just as easy as to 0xFFFF. 111 112 if (fTileModeX != SkShader::kClamp_TileMode || 113 fTileModeY != SkShader::kClamp_TileMode) { 114 fInvMatrix.postIDiv(fPixmap.width(), fPixmap.height()); 115 } 116 117 // Now that all possible changes to the matrix have taken place, check 118 // to see if we're really close to a no-scale matrix. If so, explicitly 119 // set it to be so. Subsequent code may inspect this matrix to choose 120 // a faster path in this case. 121 122 // This code will only execute if the matrix has some scale component; 123 // if it's already pure translate then we won't do this inversion. 124 125 if (matrix_only_scale_translate(fInvMatrix)) { 126 SkMatrix forward; 127 if (fInvMatrix.invert(&forward) && just_trans_general(forward)) { 128 fInvMatrix.setTranslate(-forward.getTranslateX(), -forward.getTranslateY()); 129 } 130 } 131 132 // Recompute the flag after matrix adjustments. 133 integral_translate_only = just_trans_integral(fInvMatrix); 134 } 135 136 fInvType = fInvMatrix.getType(); 137 138 if (kLow_SkFilterQuality == fFilterQuality && 139 (!valid_for_filtering(fPixmap.width() | fPixmap.height()) || 140 integral_translate_only)) { 141 fFilterQuality = kNone_SkFilterQuality; 142 } 143 144 return true; 145 } 146 147 /* 148 * Analyze filter-quality and matrix, and decide how to implement that. 149 * 150 * In general, we cascade down the request level [ High ... None ] 151 * - for a given level, if we can fulfill it, fine, else 152 * - else we downgrade to the next lower level and try again. 153 * We can always fulfill requests for Low and None 154 * - sometimes we will "ignore" Low and give None, but this is likely a legacy perf hack 155 * and may be removed. 156 */ 157 bool SkBitmapProcState::chooseProcs() { 158 fInvProc = SkMatrixPriv::GetMapXYProc(fInvMatrix); 159 fInvSx = SkScalarToFixed(fInvMatrix.getScaleX()); 160 fInvSxFractionalInt = SkScalarToFractionalInt(fInvMatrix.getScaleX()); 161 fInvKy = SkScalarToFixed(fInvMatrix.getSkewY()); 162 fInvKyFractionalInt = SkScalarToFractionalInt(fInvMatrix.getSkewY()); 163 164 fAlphaScale = SkAlpha255To256(SkColorGetA(fPaintColor)); 165 166 fShaderProc32 = nullptr; 167 fShaderProc16 = nullptr; 168 fSampleProc32 = nullptr; 169 170 const bool trivialMatrix = (fInvMatrix.getType() & ~SkMatrix::kTranslate_Mask) == 0; 171 const bool clampClamp = SkShader::kClamp_TileMode == fTileModeX && 172 SkShader::kClamp_TileMode == fTileModeY; 173 174 return this->chooseScanlineProcs(trivialMatrix, clampClamp); 175 } 176 177 bool SkBitmapProcState::chooseScanlineProcs(bool trivialMatrix, bool clampClamp) { 178 SkASSERT(fPixmap.colorType() == kN32_SkColorType); 179 180 fMatrixProc = this->chooseMatrixProc(trivialMatrix); 181 // TODO(dominikg): SkASSERT(fMatrixProc) instead? chooseMatrixProc never returns nullptr. 182 if (nullptr == fMatrixProc) { 183 return false; 184 } 185 186 const SkAlphaType at = fPixmap.alphaType(); 187 if (kPremul_SkAlphaType != at && kOpaque_SkAlphaType != at) { 188 return false; 189 } 190 191 // No need to do this if we're doing HQ sampling; if filter quality is 192 // still set to HQ by the time we get here, then we must have installed 193 // the shader procs above and can skip all this. 194 195 if (fFilterQuality < kHigh_SkFilterQuality) { 196 int index = 0; 197 if (fAlphaScale < 256) { // note: this distinction is not used for D16 198 index |= 1; 199 } 200 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) { 201 index |= 2; 202 } 203 if (fFilterQuality > kNone_SkFilterQuality) { 204 index |= 4; 205 } 206 207 #if !defined(SK_ARM_HAS_NEON) 208 static const SampleProc32 gSkBitmapProcStateSample32[] = { 209 S32_opaque_D32_nofilter_DXDY, 210 S32_alpha_D32_nofilter_DXDY, 211 S32_opaque_D32_nofilter_DX, 212 S32_alpha_D32_nofilter_DX, 213 S32_opaque_D32_filter_DXDY, 214 S32_alpha_D32_filter_DXDY, 215 S32_opaque_D32_filter_DX, 216 S32_alpha_D32_filter_DX, 217 }; 218 #endif 219 220 fSampleProc32 = SK_ARM_NEON_WRAP(gSkBitmapProcStateSample32)[index]; 221 222 // our special-case shaderprocs 223 if (S32_opaque_D32_nofilter_DX == fSampleProc32 && clampClamp) { 224 fShaderProc32 = Clamp_S32_opaque_D32_nofilter_DX_shaderproc; 225 } 226 227 if (nullptr == fShaderProc32) { 228 fShaderProc32 = this->chooseShaderProc32(); 229 } 230 } 231 232 // see if our platform has any accelerated overrides 233 this->platformProcs(); 234 235 return true; 236 } 237 238 static void Clamp_S32_D32_nofilter_trans_shaderproc(const void* sIn, 239 int x, int y, 240 SkPMColor* SK_RESTRICT colors, 241 int count) { 242 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn); 243 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0); 244 SkASSERT(s.fInvKy == 0); 245 SkASSERT(count > 0 && colors != nullptr); 246 SkASSERT(kNone_SkFilterQuality == s.fFilterQuality); 247 248 const int maxX = s.fPixmap.width() - 1; 249 const int maxY = s.fPixmap.height() - 1; 250 int ix = s.fFilterOneX + x; 251 int iy = SkClampMax(s.fFilterOneY + y, maxY); 252 const SkPMColor* row = s.fPixmap.addr32(0, iy); 253 254 // clamp to the left 255 if (ix < 0) { 256 int n = SkMin32(-ix, count); 257 sk_memset32(colors, row[0], n); 258 count -= n; 259 if (0 == count) { 260 return; 261 } 262 colors += n; 263 SkASSERT(-ix == n); 264 ix = 0; 265 } 266 // copy the middle 267 if (ix <= maxX) { 268 int n = SkMin32(maxX - ix + 1, count); 269 memcpy(colors, row + ix, n * sizeof(SkPMColor)); 270 count -= n; 271 if (0 == count) { 272 return; 273 } 274 colors += n; 275 } 276 SkASSERT(count > 0); 277 // clamp to the right 278 sk_memset32(colors, row[maxX], count); 279 } 280 281 static inline int sk_int_mod(int x, int n) { 282 SkASSERT(n > 0); 283 if ((unsigned)x >= (unsigned)n) { 284 if (x < 0) { 285 x = n + ~(~x % n); 286 } else { 287 x = x % n; 288 } 289 } 290 return x; 291 } 292 293 static inline int sk_int_mirror(int x, int n) { 294 x = sk_int_mod(x, 2 * n); 295 if (x >= n) { 296 x = n + ~(x - n); 297 } 298 return x; 299 } 300 301 static void Repeat_S32_D32_nofilter_trans_shaderproc(const void* sIn, 302 int x, int y, 303 SkPMColor* SK_RESTRICT colors, 304 int count) { 305 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn); 306 SkASSERT(((s.fInvType & ~SkMatrix::kTranslate_Mask)) == 0); 307 SkASSERT(s.fInvKy == 0); 308 SkASSERT(count > 0 && colors != nullptr); 309 SkASSERT(kNone_SkFilterQuality == s.fFilterQuality); 310 311 const int stopX = s.fPixmap.width(); 312 const int stopY = s.fPixmap.height(); 313 int ix = s.fFilterOneX + x; 314 int iy = sk_int_mod(s.fFilterOneY + y, stopY); 315 const SkPMColor* row = s.fPixmap.addr32(0, iy); 316 317 ix = sk_int_mod(ix, stopX); 318 for (;;) { 319 int n = SkMin32(stopX - ix, count); 320 memcpy(colors, row + ix, n * sizeof(SkPMColor)); 321 count -= n; 322 if (0 == count) { 323 return; 324 } 325 colors += n; 326 ix = 0; 327 } 328 } 329 330 static void S32_D32_constX_shaderproc(const void* sIn, 331 int x, int y, 332 SkPMColor* SK_RESTRICT colors, 333 int count) { 334 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn); 335 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) == 0); 336 SkASSERT(s.fInvKy == 0); 337 SkASSERT(count > 0 && colors != nullptr); 338 SkASSERT(1 == s.fPixmap.width()); 339 340 int iY0; 341 int iY1 SK_INIT_TO_AVOID_WARNING; 342 int iSubY SK_INIT_TO_AVOID_WARNING; 343 344 if (kNone_SkFilterQuality != s.fFilterQuality) { 345 SkBitmapProcState::MatrixProc mproc = s.getMatrixProc(); 346 uint32_t xy[2]; 347 348 mproc(s, xy, 1, x, y); 349 350 iY0 = xy[0] >> 18; 351 iY1 = xy[0] & 0x3FFF; 352 iSubY = (xy[0] >> 14) & 0xF; 353 } else { 354 int yTemp; 355 356 if (s.fInvType > SkMatrix::kTranslate_Mask) { 357 const SkBitmapProcStateAutoMapper mapper(s, x, y); 358 359 // When the matrix has a scale component the setup code in 360 // chooseProcs multiples the inverse matrix by the inverse of the 361 // bitmap's width and height. Since this method is going to do 362 // its own tiling and sampling we need to undo that here. 363 if (SkShader::kClamp_TileMode != s.fTileModeX || 364 SkShader::kClamp_TileMode != s.fTileModeY) { 365 yTemp = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height()); 366 } else { 367 yTemp = mapper.intY(); 368 } 369 } else { 370 yTemp = s.fFilterOneY + y; 371 } 372 373 const int stopY = s.fPixmap.height(); 374 switch (s.fTileModeY) { 375 case SkShader::kClamp_TileMode: 376 iY0 = SkClampMax(yTemp, stopY-1); 377 break; 378 case SkShader::kRepeat_TileMode: 379 iY0 = sk_int_mod(yTemp, stopY); 380 break; 381 case SkShader::kMirror_TileMode: 382 default: 383 iY0 = sk_int_mirror(yTemp, stopY); 384 break; 385 } 386 387 #ifdef SK_DEBUG 388 { 389 const SkBitmapProcStateAutoMapper mapper(s, x, y); 390 int iY2; 391 392 if (s.fInvType > SkMatrix::kTranslate_Mask && 393 (SkShader::kClamp_TileMode != s.fTileModeX || 394 SkShader::kClamp_TileMode != s.fTileModeY)) { 395 iY2 = SkFractionalIntToInt(mapper.fractionalIntY() * s.fPixmap.height()); 396 } else { 397 iY2 = mapper.intY(); 398 } 399 400 switch (s.fTileModeY) { 401 case SkShader::kClamp_TileMode: 402 iY2 = SkClampMax(iY2, stopY-1); 403 break; 404 case SkShader::kRepeat_TileMode: 405 iY2 = sk_int_mod(iY2, stopY); 406 break; 407 case SkShader::kMirror_TileMode: 408 default: 409 iY2 = sk_int_mirror(iY2, stopY); 410 break; 411 } 412 413 SkASSERT(iY0 == iY2); 414 } 415 #endif 416 } 417 418 const SkPMColor* row0 = s.fPixmap.addr32(0, iY0); 419 SkPMColor color; 420 421 if (kNone_SkFilterQuality != s.fFilterQuality) { 422 const SkPMColor* row1 = s.fPixmap.addr32(0, iY1); 423 424 if (s.fAlphaScale < 256) { 425 Filter_32_alpha(iSubY, *row0, *row1, &color, s.fAlphaScale); 426 } else { 427 Filter_32_opaque(iSubY, *row0, *row1, &color); 428 } 429 } else { 430 if (s.fAlphaScale < 256) { 431 color = SkAlphaMulQ(*row0, s.fAlphaScale); 432 } else { 433 color = *row0; 434 } 435 } 436 437 sk_memset32(colors, color, count); 438 } 439 440 static void DoNothing_shaderproc(const void*, int x, int y, 441 SkPMColor* SK_RESTRICT colors, int count) { 442 // if we get called, the matrix is too tricky, so we just draw nothing 443 sk_memset32(colors, 0, count); 444 } 445 446 bool SkBitmapProcState::setupForTranslate() { 447 SkPoint pt; 448 const SkBitmapProcStateAutoMapper mapper(*this, 0, 0, &pt); 449 450 /* 451 * if the translate is larger than our ints, we can get random results, or 452 * worse, we might get 0x80000000, which wreaks havoc on us, since we can't 453 * negate it. 454 */ 455 const SkScalar too_big = SkIntToScalar(1 << 30); 456 if (SkScalarAbs(pt.fX) > too_big || SkScalarAbs(pt.fY) > too_big) { 457 return false; 458 } 459 460 // Since we know we're not filtered, we re-purpose these fields allow 461 // us to go from device -> src coordinates w/ just an integer add, 462 // rather than running through the inverse-matrix 463 fFilterOneX = mapper.intX(); 464 fFilterOneY = mapper.intY(); 465 466 return true; 467 } 468 469 SkBitmapProcState::ShaderProc32 SkBitmapProcState::chooseShaderProc32() { 470 471 if (kN32_SkColorType != fPixmap.colorType()) { 472 return nullptr; 473 } 474 475 static const unsigned kMask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; 476 477 if (1 == fPixmap.width() && 0 == (fInvType & ~kMask)) { 478 if (kNone_SkFilterQuality == fFilterQuality && 479 fInvType <= SkMatrix::kTranslate_Mask && 480 !this->setupForTranslate()) { 481 return DoNothing_shaderproc; 482 } 483 return S32_D32_constX_shaderproc; 484 } 485 486 if (fAlphaScale < 256) { 487 return nullptr; 488 } 489 if (fInvType > SkMatrix::kTranslate_Mask) { 490 return nullptr; 491 } 492 if (kNone_SkFilterQuality != fFilterQuality) { 493 return nullptr; 494 } 495 496 SkShader::TileMode tx = (SkShader::TileMode)fTileModeX; 497 SkShader::TileMode ty = (SkShader::TileMode)fTileModeY; 498 499 if (SkShader::kClamp_TileMode == tx && SkShader::kClamp_TileMode == ty) { 500 if (this->setupForTranslate()) { 501 return Clamp_S32_D32_nofilter_trans_shaderproc; 502 } 503 return DoNothing_shaderproc; 504 } 505 if (SkShader::kRepeat_TileMode == tx && SkShader::kRepeat_TileMode == ty) { 506 if (this->setupForTranslate()) { 507 return Repeat_S32_D32_nofilter_trans_shaderproc; 508 } 509 return DoNothing_shaderproc; 510 } 511 return nullptr; 512 } 513 514 /////////////////////////////////////////////////////////////////////////////// 515 516 #ifdef SK_DEBUG 517 518 static void check_scale_nofilter(uint32_t bitmapXY[], int count, 519 unsigned mx, unsigned my) { 520 unsigned y = *bitmapXY++; 521 SkASSERT(y < my); 522 523 const uint16_t* xptr = reinterpret_cast<const uint16_t*>(bitmapXY); 524 for (int i = 0; i < count; ++i) { 525 SkASSERT(xptr[i] < mx); 526 } 527 } 528 529 static void check_scale_filter(uint32_t bitmapXY[], int count, 530 unsigned mx, unsigned my) { 531 uint32_t YY = *bitmapXY++; 532 unsigned y0 = YY >> 18; 533 unsigned y1 = YY & 0x3FFF; 534 SkASSERT(y0 < my); 535 SkASSERT(y1 < my); 536 537 for (int i = 0; i < count; ++i) { 538 uint32_t XX = bitmapXY[i]; 539 unsigned x0 = XX >> 18; 540 unsigned x1 = XX & 0x3FFF; 541 SkASSERT(x0 < mx); 542 SkASSERT(x1 < mx); 543 } 544 } 545 546 void SkBitmapProcState::DebugMatrixProc(const SkBitmapProcState& state, 547 uint32_t bitmapXY[], int count, 548 int x, int y) { 549 SkASSERT(bitmapXY); 550 SkASSERT(count > 0); 551 552 state.fMatrixProc(state, bitmapXY, count, x, y); 553 554 void (*proc)(uint32_t bitmapXY[], int count, unsigned mx, unsigned my); 555 556 // There are two formats possible: 557 // filter -vs- nofilter 558 SkASSERT(state.fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)); 559 proc = state.fFilterQuality != kNone_SkFilterQuality ? 560 check_scale_filter : check_scale_nofilter; 561 proc(bitmapXY, count, state.fPixmap.width(), state.fPixmap.height()); 562 } 563 564 SkBitmapProcState::MatrixProc SkBitmapProcState::getMatrixProc() const { 565 return DebugMatrixProc; 566 } 567 568 #endif 569 570 /////////////////////////////////////////////////////////////////////////////// 571 /* 572 The storage requirements for the different matrix procs are as follows, 573 where each X or Y is 2 bytes, and N is the number of pixels/elements: 574 575 scale/translate nofilter Y(4bytes) + N * X 576 affine/perspective nofilter N * (X Y) 577 scale/translate filter Y Y + N * (X X) 578 affine filter N * (Y Y X X) 579 */ 580 int SkBitmapProcState::maxCountForBufferSize(size_t bufferSize) const { 581 int32_t size = static_cast<int32_t>(bufferSize); 582 583 size &= ~3; // only care about 4-byte aligned chunks 584 if (fInvType <= (SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask)) { 585 size -= 4; // the shared Y (or YY) coordinate 586 if (size < 0) { 587 size = 0; 588 } 589 size >>= 1; 590 } else { 591 size >>= 2; 592 } 593 594 if (fFilterQuality != kNone_SkFilterQuality) { 595 size >>= 1; 596 } 597 598 return size; 599 } 600 601 /////////////////////// 602 603 void Clamp_S32_opaque_D32_nofilter_DX_shaderproc(const void* sIn, int x, int y, 604 SkPMColor* SK_RESTRICT dst, int count) { 605 const SkBitmapProcState& s = *static_cast<const SkBitmapProcState*>(sIn); 606 SkASSERT((s.fInvType & ~(SkMatrix::kTranslate_Mask | 607 SkMatrix::kScale_Mask)) == 0); 608 609 const unsigned maxX = s.fPixmap.width() - 1; 610 SkFractionalInt fx; 611 int dstY; 612 { 613 const SkBitmapProcStateAutoMapper mapper(s, x, y); 614 const unsigned maxY = s.fPixmap.height() - 1; 615 dstY = SkClampMax(mapper.intY(), maxY); 616 fx = mapper.fractionalIntX(); 617 } 618 619 const SkPMColor* SK_RESTRICT src = s.fPixmap.addr32(0, dstY); 620 const SkFractionalInt dx = s.fInvSxFractionalInt; 621 622 // Check if we're safely inside [0...maxX] so no need to clamp each computed index. 623 // 624 if ((uint64_t)SkFractionalIntToInt(fx) <= maxX && 625 (uint64_t)SkFractionalIntToInt(fx + dx * (count - 1)) <= maxX) 626 { 627 int count4 = count >> 2; 628 for (int i = 0; i < count4; ++i) { 629 SkPMColor src0 = src[SkFractionalIntToInt(fx)]; fx += dx; 630 SkPMColor src1 = src[SkFractionalIntToInt(fx)]; fx += dx; 631 SkPMColor src2 = src[SkFractionalIntToInt(fx)]; fx += dx; 632 SkPMColor src3 = src[SkFractionalIntToInt(fx)]; fx += dx; 633 dst[0] = src0; 634 dst[1] = src1; 635 dst[2] = src2; 636 dst[3] = src3; 637 dst += 4; 638 } 639 for (int i = (count4 << 2); i < count; ++i) { 640 unsigned index = SkFractionalIntToInt(fx); 641 SkASSERT(index <= maxX); 642 *dst++ = src[index]; 643 fx += dx; 644 } 645 } else { 646 for (int i = 0; i < count; ++i) { 647 dst[i] = src[SkClampMax(SkFractionalIntToInt(fx), maxX)]; 648 fx += dx; 649 } 650 } 651 } 652