1 2 /* 3 * Copyright 2012 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8 9 #include "SkLinearGradient.h" 10 11 static inline int repeat_bits(int x, const int bits) { 12 return x & ((1 << bits) - 1); 13 } 14 15 static inline int repeat_8bits(int x) { 16 return x & 0xFF; 17 } 18 19 // Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly. 20 // See http://code.google.com/p/skia/issues/detail?id=472 21 #if defined(_MSC_VER) && (_MSC_VER >= 1600) 22 #pragma optimize("", off) 23 #endif 24 25 static inline int mirror_bits(int x, const int bits) { 26 if (x & (1 << bits)) { 27 x = ~x; 28 } 29 return x & ((1 << bits) - 1); 30 } 31 32 static inline int mirror_8bits(int x) { 33 if (x & 256) { 34 x = ~x; 35 } 36 return x & 255; 37 } 38 39 #if defined(_MSC_VER) && (_MSC_VER >= 1600) 40 #pragma optimize("", on) 41 #endif 42 43 static void pts_to_unit_matrix(const SkPoint pts[2], SkMatrix* matrix) { 44 SkVector vec = pts[1] - pts[0]; 45 SkScalar mag = vec.length(); 46 SkScalar inv = mag ? SkScalarInvert(mag) : 0; 47 48 vec.scale(inv); 49 matrix->setSinCos(-vec.fY, vec.fX, pts[0].fX, pts[0].fY); 50 matrix->postTranslate(-pts[0].fX, -pts[0].fY); 51 matrix->postScale(inv, inv); 52 } 53 54 /////////////////////////////////////////////////////////////////////////////// 55 56 SkLinearGradient::SkLinearGradient(const SkPoint pts[2], const Descriptor& desc) 57 : SkGradientShaderBase(desc) 58 , fStart(pts[0]) 59 , fEnd(pts[1]) { 60 pts_to_unit_matrix(pts, &fPtsToUnit); 61 } 62 63 SkLinearGradient::SkLinearGradient(SkFlattenableReadBuffer& buffer) 64 : INHERITED(buffer) 65 , fStart(buffer.readPoint()) 66 , fEnd(buffer.readPoint()) { 67 } 68 69 void SkLinearGradient::flatten(SkFlattenableWriteBuffer& buffer) const { 70 this->INHERITED::flatten(buffer); 71 buffer.writePoint(fStart); 72 buffer.writePoint(fEnd); 73 } 74 75 bool SkLinearGradient::setContext(const SkBitmap& device, const SkPaint& paint, 76 const SkMatrix& matrix) { 77 if (!this->INHERITED::setContext(device, paint, matrix)) { 78 return false; 79 } 80 81 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; 82 if ((fDstToIndex.getType() & ~mask) == 0) { 83 // when we dither, we are (usually) not const-in-Y 84 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) { 85 // only claim this if we do have a 16bit mode (i.e. none of our 86 // colors have alpha), and if we are not dithering (which obviously 87 // is not const in Y). 88 fFlags |= SkShader::kConstInY16_Flag; 89 } 90 } 91 return true; 92 } 93 94 #define NO_CHECK_ITER \ 95 do { \ 96 unsigned fi = fx >> SkGradientShaderBase::kCache32Shift; \ 97 SkASSERT(fi <= 0xFF); \ 98 fx += dx; \ 99 *dstC++ = cache[toggle + fi]; \ 100 toggle = next_dither_toggle(toggle); \ 101 } while (0) 102 103 namespace { 104 105 typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx, 106 SkPMColor* dstC, const SkPMColor* cache, 107 int toggle, int count); 108 109 // Linear interpolation (lerp) is unnecessary if there are no sharp 110 // discontinuities in the gradient - which must be true if there are 111 // only 2 colors - but it's cheap. 112 void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx, 113 SkPMColor* SK_RESTRICT dstC, 114 const SkPMColor* SK_RESTRICT cache, 115 int toggle, int count) { 116 // We're a vertical gradient, so no change in a span. 117 // If colors change sharply across the gradient, dithering is 118 // insufficient (it subsamples the color space) and we need to lerp. 119 unsigned fullIndex = proc(fx); 120 unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift; 121 unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1); 122 123 int index0 = fi + toggle; 124 int index1 = index0; 125 if (fi < SkGradientShaderBase::kCache32Count - 1) { 126 index1 += 1; 127 } 128 SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder); 129 index0 ^= SkGradientShaderBase::kDitherStride32; 130 index1 ^= SkGradientShaderBase::kDitherStride32; 131 SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder); 132 sk_memset32_dither(dstC, lerp, dlerp, count); 133 } 134 135 void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx, 136 SkPMColor* SK_RESTRICT dstC, 137 const SkPMColor* SK_RESTRICT cache, 138 int toggle, int count) { 139 SkClampRange range; 140 range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); 141 142 if ((count = range.fCount0) > 0) { 143 sk_memset32_dither(dstC, 144 cache[toggle + range.fV0], 145 cache[next_dither_toggle(toggle) + range.fV0], 146 count); 147 dstC += count; 148 } 149 if ((count = range.fCount1) > 0) { 150 int unroll = count >> 3; 151 fx = range.fFx1; 152 for (int i = 0; i < unroll; i++) { 153 NO_CHECK_ITER; NO_CHECK_ITER; 154 NO_CHECK_ITER; NO_CHECK_ITER; 155 NO_CHECK_ITER; NO_CHECK_ITER; 156 NO_CHECK_ITER; NO_CHECK_ITER; 157 } 158 if ((count &= 7) > 0) { 159 do { 160 NO_CHECK_ITER; 161 } while (--count != 0); 162 } 163 } 164 if ((count = range.fCount2) > 0) { 165 sk_memset32_dither(dstC, 166 cache[toggle + range.fV1], 167 cache[next_dither_toggle(toggle) + range.fV1], 168 count); 169 } 170 } 171 172 void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx, 173 SkPMColor* SK_RESTRICT dstC, 174 const SkPMColor* SK_RESTRICT cache, 175 int toggle, int count) { 176 do { 177 unsigned fi = mirror_8bits(fx >> 8); 178 SkASSERT(fi <= 0xFF); 179 fx += dx; 180 *dstC++ = cache[toggle + fi]; 181 toggle = next_dither_toggle(toggle); 182 } while (--count != 0); 183 } 184 185 void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx, 186 SkPMColor* SK_RESTRICT dstC, 187 const SkPMColor* SK_RESTRICT cache, 188 int toggle, int count) { 189 do { 190 unsigned fi = repeat_8bits(fx >> 8); 191 SkASSERT(fi <= 0xFF); 192 fx += dx; 193 *dstC++ = cache[toggle + fi]; 194 toggle = next_dither_toggle(toggle); 195 } while (--count != 0); 196 } 197 198 } 199 200 void SkLinearGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, 201 int count) { 202 SkASSERT(count > 0); 203 204 SkPoint srcPt; 205 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 206 TileProc proc = fTileProc; 207 const SkPMColor* SK_RESTRICT cache = this->getCache32(); 208 int toggle = init_dither_toggle(x, y); 209 210 if (fDstToIndexClass != kPerspective_MatrixClass) { 211 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 212 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 213 SkFixed dx, fx = SkScalarToFixed(srcPt.fX); 214 215 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 216 SkFixed dxStorage[1]; 217 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); 218 dx = dxStorage[0]; 219 } else { 220 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 221 dx = SkScalarToFixed(fDstToIndex.getScaleX()); 222 } 223 224 LinearShadeProc shadeProc = shadeSpan_linear_repeat; 225 if (0 == dx) { 226 shadeProc = shadeSpan_linear_vertical_lerp; 227 } else if (SkShader::kClamp_TileMode == fTileMode) { 228 shadeProc = shadeSpan_linear_clamp; 229 } else if (SkShader::kMirror_TileMode == fTileMode) { 230 shadeProc = shadeSpan_linear_mirror; 231 } else { 232 SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 233 } 234 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count); 235 } else { 236 SkScalar dstX = SkIntToScalar(x); 237 SkScalar dstY = SkIntToScalar(y); 238 do { 239 dstProc(fDstToIndex, dstX, dstY, &srcPt); 240 unsigned fi = proc(SkScalarToFixed(srcPt.fX)); 241 SkASSERT(fi <= 0xFFFF); 242 *dstC++ = cache[toggle + (fi >> kCache32Shift)]; 243 toggle = next_dither_toggle(toggle); 244 dstX += SK_Scalar1; 245 } while (--count != 0); 246 } 247 } 248 249 SkShader::BitmapType SkLinearGradient::asABitmap(SkBitmap* bitmap, 250 SkMatrix* matrix, 251 TileMode xy[]) const { 252 if (bitmap) { 253 this->getGradientTableBitmap(bitmap); 254 } 255 if (matrix) { 256 matrix->preConcat(fPtsToUnit); 257 } 258 if (xy) { 259 xy[0] = fTileMode; 260 xy[1] = kClamp_TileMode; 261 } 262 return kLinear_BitmapType; 263 } 264 265 SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const { 266 if (info) { 267 commonAsAGradient(info); 268 info->fPoint[0] = fStart; 269 info->fPoint[1] = fEnd; 270 } 271 return kLinear_GradientType; 272 } 273 274 static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, 275 int count) { 276 if (reinterpret_cast<uintptr_t>(dst) & 2) { 277 *dst++ = value; 278 count -= 1; 279 SkTSwap(value, other); 280 } 281 282 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1); 283 284 if (count & 1) { 285 dst[count - 1] = value; 286 } 287 } 288 289 #define NO_CHECK_ITER_16 \ 290 do { \ 291 unsigned fi = fx >> SkGradientShaderBase::kCache16Shift; \ 292 SkASSERT(fi < SkGradientShaderBase::kCache16Count); \ 293 fx += dx; \ 294 *dstC++ = cache[toggle + fi]; \ 295 toggle = next_dither_toggle16(toggle); \ 296 } while (0) 297 298 namespace { 299 300 typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx, 301 uint16_t* dstC, const uint16_t* cache, 302 int toggle, int count); 303 304 void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx, 305 uint16_t* SK_RESTRICT dstC, 306 const uint16_t* SK_RESTRICT cache, 307 int toggle, int count) { 308 // we're a vertical gradient, so no change in a span 309 unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift; 310 SkASSERT(fi < SkGradientShaderBase::kCache16Count); 311 dither_memset16(dstC, cache[toggle + fi], 312 cache[next_dither_toggle16(toggle) + fi], count); 313 } 314 315 void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx, 316 uint16_t* SK_RESTRICT dstC, 317 const uint16_t* SK_RESTRICT cache, 318 int toggle, int count) { 319 SkClampRange range; 320 range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); 321 322 if ((count = range.fCount0) > 0) { 323 dither_memset16(dstC, 324 cache[toggle + range.fV0], 325 cache[next_dither_toggle16(toggle) + range.fV0], 326 count); 327 dstC += count; 328 } 329 if ((count = range.fCount1) > 0) { 330 int unroll = count >> 3; 331 fx = range.fFx1; 332 for (int i = 0; i < unroll; i++) { 333 NO_CHECK_ITER_16; NO_CHECK_ITER_16; 334 NO_CHECK_ITER_16; NO_CHECK_ITER_16; 335 NO_CHECK_ITER_16; NO_CHECK_ITER_16; 336 NO_CHECK_ITER_16; NO_CHECK_ITER_16; 337 } 338 if ((count &= 7) > 0) { 339 do { 340 NO_CHECK_ITER_16; 341 } while (--count != 0); 342 } 343 } 344 if ((count = range.fCount2) > 0) { 345 dither_memset16(dstC, 346 cache[toggle + range.fV1], 347 cache[next_dither_toggle16(toggle) + range.fV1], 348 count); 349 } 350 } 351 352 void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx, 353 uint16_t* SK_RESTRICT dstC, 354 const uint16_t* SK_RESTRICT cache, 355 int toggle, int count) { 356 do { 357 unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift, 358 SkGradientShaderBase::kCache16Bits); 359 SkASSERT(fi < SkGradientShaderBase::kCache16Count); 360 fx += dx; 361 *dstC++ = cache[toggle + fi]; 362 toggle = next_dither_toggle16(toggle); 363 } while (--count != 0); 364 } 365 366 void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx, 367 uint16_t* SK_RESTRICT dstC, 368 const uint16_t* SK_RESTRICT cache, 369 int toggle, int count) { 370 do { 371 unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift, 372 SkGradientShaderBase::kCache16Bits); 373 SkASSERT(fi < SkGradientShaderBase::kCache16Count); 374 fx += dx; 375 *dstC++ = cache[toggle + fi]; 376 toggle = next_dither_toggle16(toggle); 377 } while (--count != 0); 378 } 379 } 380 381 void SkLinearGradient::shadeSpan16(int x, int y, 382 uint16_t* SK_RESTRICT dstC, int count) { 383 SkASSERT(count > 0); 384 385 SkPoint srcPt; 386 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 387 TileProc proc = fTileProc; 388 const uint16_t* SK_RESTRICT cache = this->getCache16(); 389 int toggle = init_dither_toggle16(x, y); 390 391 if (fDstToIndexClass != kPerspective_MatrixClass) { 392 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 393 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 394 SkFixed dx, fx = SkScalarToFixed(srcPt.fX); 395 396 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 397 SkFixed dxStorage[1]; 398 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); 399 dx = dxStorage[0]; 400 } else { 401 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 402 dx = SkScalarToFixed(fDstToIndex.getScaleX()); 403 } 404 405 LinearShade16Proc shadeProc = shadeSpan16_linear_repeat; 406 if (SkFixedNearlyZero(dx)) { 407 shadeProc = shadeSpan16_linear_vertical; 408 } else if (SkShader::kClamp_TileMode == fTileMode) { 409 shadeProc = shadeSpan16_linear_clamp; 410 } else if (SkShader::kMirror_TileMode == fTileMode) { 411 shadeProc = shadeSpan16_linear_mirror; 412 } else { 413 SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 414 } 415 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count); 416 } else { 417 SkScalar dstX = SkIntToScalar(x); 418 SkScalar dstY = SkIntToScalar(y); 419 do { 420 dstProc(fDstToIndex, dstX, dstY, &srcPt); 421 unsigned fi = proc(SkScalarToFixed(srcPt.fX)); 422 SkASSERT(fi <= 0xFFFF); 423 424 int index = fi >> kCache16Shift; 425 *dstC++ = cache[toggle + index]; 426 toggle = next_dither_toggle16(toggle); 427 428 dstX += SK_Scalar1; 429 } while (--count != 0); 430 } 431 } 432 433 #if SK_SUPPORT_GPU 434 435 #include "GrTBackendEffectFactory.h" 436 437 ///////////////////////////////////////////////////////////////////// 438 439 class GrGLLinearGradient : public GrGLGradientEffect { 440 public: 441 442 GrGLLinearGradient(const GrBackendEffectFactory& factory, const GrDrawEffect&) 443 : INHERITED (factory) { } 444 445 virtual ~GrGLLinearGradient() { } 446 447 virtual void emitCode(GrGLShaderBuilder*, 448 const GrDrawEffect&, 449 EffectKey, 450 const char* outputColor, 451 const char* inputColor, 452 const TextureSamplerArray&) SK_OVERRIDE; 453 454 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 455 return GenMatrixKey(drawEffect); 456 } 457 458 private: 459 460 typedef GrGLGradientEffect INHERITED; 461 }; 462 463 ///////////////////////////////////////////////////////////////////// 464 465 class GrLinearGradient : public GrGradientEffect { 466 public: 467 468 static GrEffectRef* Create(GrContext* ctx, 469 const SkLinearGradient& shader, 470 const SkMatrix& matrix, 471 SkShader::TileMode tm) { 472 AutoEffectUnref effect(SkNEW_ARGS(GrLinearGradient, (ctx, shader, matrix, tm))); 473 return CreateEffectRef(effect); 474 } 475 476 virtual ~GrLinearGradient() { } 477 478 static const char* Name() { return "Linear Gradient"; } 479 const GrBackendEffectFactory& getFactory() const SK_OVERRIDE { 480 return GrTBackendEffectFactory<GrLinearGradient>::getInstance(); 481 } 482 483 typedef GrGLLinearGradient GLEffect; 484 485 private: 486 GrLinearGradient(GrContext* ctx, 487 const SkLinearGradient& shader, 488 const SkMatrix& matrix, 489 SkShader::TileMode tm) 490 : INHERITED(ctx, shader, matrix, tm) { } 491 GR_DECLARE_EFFECT_TEST; 492 493 typedef GrGradientEffect INHERITED; 494 }; 495 496 ///////////////////////////////////////////////////////////////////// 497 498 GR_DEFINE_EFFECT_TEST(GrLinearGradient); 499 500 GrEffectRef* GrLinearGradient::TestCreate(SkMWCRandom* random, 501 GrContext* context, 502 const GrDrawTargetCaps&, 503 GrTexture**) { 504 SkPoint points[] = {{random->nextUScalar1(), random->nextUScalar1()}, 505 {random->nextUScalar1(), random->nextUScalar1()}}; 506 507 SkColor colors[kMaxRandomGradientColors]; 508 SkScalar stopsArray[kMaxRandomGradientColors]; 509 SkScalar* stops = stopsArray; 510 SkShader::TileMode tm; 511 int colorCount = RandomGradientParams(random, colors, &stops, &tm); 512 SkAutoTUnref<SkShader> shader(SkGradientShader::CreateLinear(points, 513 colors, stops, colorCount, 514 tm)); 515 SkPaint paint; 516 return shader->asNewEffect(context, paint); 517 } 518 519 ///////////////////////////////////////////////////////////////////// 520 521 void GrGLLinearGradient::emitCode(GrGLShaderBuilder* builder, 522 const GrDrawEffect&, 523 EffectKey key, 524 const char* outputColor, 525 const char* inputColor, 526 const TextureSamplerArray& samplers) { 527 this->emitYCoordUniform(builder); 528 const char* coords; 529 this->setupMatrix(builder, key, &coords); 530 SkString t; 531 t.append(coords); 532 t.append(".x"); 533 this->emitColorLookup(builder, t.c_str(), outputColor, inputColor, samplers[0]); 534 } 535 536 ///////////////////////////////////////////////////////////////////// 537 538 GrEffectRef* SkLinearGradient::asNewEffect(GrContext* context, const SkPaint&) const { 539 SkASSERT(NULL != context); 540 SkMatrix matrix; 541 if (!this->getLocalMatrix().invert(&matrix)) { 542 return NULL; 543 } 544 matrix.postConcat(fPtsToUnit); 545 return GrLinearGradient::Create(context, *this, matrix, fTileMode); 546 } 547 548 #else 549 550 GrEffectRef* SkLinearGradient::asNewEffect(GrContext*, const SkPaint&) const { 551 SkDEBUGFAIL("Should not call in GPU-less build"); 552 return NULL; 553 } 554 555 #endif 556 557 #ifdef SK_DEVELOPER 558 void SkLinearGradient::toString(SkString* str) const { 559 str->append("SkLinearGradient ("); 560 561 str->appendf("start: (%f, %f)", fStart.fX, fStart.fY); 562 str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY); 563 564 this->INHERITED::toString(str); 565 566 str->append(")"); 567 } 568 #endif 569