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