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