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 : SkGradientShaderBase(desc) 57 , fStart(pts[0]) 58 , fEnd(pts[1]) { 59 pts_to_unit_matrix(pts, &fPtsToUnit); 60 } 61 62 SkLinearGradient::SkLinearGradient(SkFlattenableReadBuffer& buffer) 63 : INHERITED(buffer) 64 , fStart(buffer.readPoint()) 65 , fEnd(buffer.readPoint()) { 66 } 67 68 void SkLinearGradient::flatten(SkFlattenableWriteBuffer& buffer) const { 69 this->INHERITED::flatten(buffer); 70 buffer.writePoint(fStart); 71 buffer.writePoint(fEnd); 72 } 73 74 bool SkLinearGradient::setContext(const SkBitmap& device, const SkPaint& paint, 75 const SkMatrix& matrix) { 76 if (!this->INHERITED::setContext(device, paint, matrix)) { 77 return false; 78 } 79 80 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; 81 if ((fDstToIndex.getType() & ~mask) == 0) { 82 // when we dither, we are (usually) not const-in-Y 83 if ((fFlags & SkShader::kHasSpan16_Flag) && !paint.isDither()) { 84 // only claim this if we do have a 16bit mode (i.e. none of our 85 // colors have alpha), and if we are not dithering (which obviously 86 // is not const in Y). 87 fFlags |= SkShader::kConstInY16_Flag; 88 } 89 } 90 return true; 91 } 92 93 #define NO_CHECK_ITER \ 94 do { \ 95 unsigned fi = fx >> SkGradientShaderBase::kCache32Shift; \ 96 SkASSERT(fi <= 0xFF); \ 97 fx += dx; \ 98 *dstC++ = cache[toggle + fi]; \ 99 toggle = next_dither_toggle(toggle); \ 100 } while (0) 101 102 namespace { 103 104 typedef void (*LinearShadeProc)(TileProc proc, SkFixed dx, SkFixed fx, 105 SkPMColor* dstC, const SkPMColor* cache, 106 int toggle, int count); 107 108 // Linear interpolation (lerp) is unnecessary if there are no sharp 109 // discontinuities in the gradient - which must be true if there are 110 // only 2 colors - but it's cheap. 111 void shadeSpan_linear_vertical_lerp(TileProc proc, SkFixed dx, SkFixed fx, 112 SkPMColor* SK_RESTRICT dstC, 113 const SkPMColor* SK_RESTRICT cache, 114 int toggle, int count) { 115 // We're a vertical gradient, so no change in a span. 116 // If colors change sharply across the gradient, dithering is 117 // insufficient (it subsamples the color space) and we need to lerp. 118 unsigned fullIndex = proc(fx); 119 unsigned fi = fullIndex >> SkGradientShaderBase::kCache32Shift; 120 unsigned remainder = fullIndex & ((1 << SkGradientShaderBase::kCache32Shift) - 1); 121 122 int index0 = fi + toggle; 123 int index1 = index0; 124 if (fi < SkGradientShaderBase::kCache32Count - 1) { 125 index1 += 1; 126 } 127 SkPMColor lerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder); 128 index0 ^= SkGradientShaderBase::kDitherStride32; 129 index1 ^= SkGradientShaderBase::kDitherStride32; 130 SkPMColor dlerp = SkFastFourByteInterp(cache[index1], cache[index0], remainder); 131 sk_memset32_dither(dstC, lerp, dlerp, count); 132 } 133 134 void shadeSpan_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx, 135 SkPMColor* SK_RESTRICT dstC, 136 const SkPMColor* SK_RESTRICT cache, 137 int toggle, int count) { 138 SkClampRange range; 139 range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); 140 141 if ((count = range.fCount0) > 0) { 142 sk_memset32_dither(dstC, 143 cache[toggle + range.fV0], 144 cache[next_dither_toggle(toggle) + range.fV0], 145 count); 146 dstC += count; 147 } 148 if ((count = range.fCount1) > 0) { 149 int unroll = count >> 3; 150 fx = range.fFx1; 151 for (int i = 0; i < unroll; i++) { 152 NO_CHECK_ITER; NO_CHECK_ITER; 153 NO_CHECK_ITER; NO_CHECK_ITER; 154 NO_CHECK_ITER; NO_CHECK_ITER; 155 NO_CHECK_ITER; NO_CHECK_ITER; 156 } 157 if ((count &= 7) > 0) { 158 do { 159 NO_CHECK_ITER; 160 } while (--count != 0); 161 } 162 } 163 if ((count = range.fCount2) > 0) { 164 sk_memset32_dither(dstC, 165 cache[toggle + range.fV1], 166 cache[next_dither_toggle(toggle) + range.fV1], 167 count); 168 } 169 } 170 171 void shadeSpan_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx, 172 SkPMColor* SK_RESTRICT dstC, 173 const SkPMColor* SK_RESTRICT cache, 174 int toggle, int count) { 175 do { 176 unsigned fi = mirror_8bits(fx >> 8); 177 SkASSERT(fi <= 0xFF); 178 fx += dx; 179 *dstC++ = cache[toggle + fi]; 180 toggle = next_dither_toggle(toggle); 181 } while (--count != 0); 182 } 183 184 void shadeSpan_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx, 185 SkPMColor* SK_RESTRICT dstC, 186 const SkPMColor* SK_RESTRICT cache, 187 int toggle, int count) { 188 do { 189 unsigned fi = repeat_8bits(fx >> 8); 190 SkASSERT(fi <= 0xFF); 191 fx += dx; 192 *dstC++ = cache[toggle + fi]; 193 toggle = next_dither_toggle(toggle); 194 } while (--count != 0); 195 } 196 197 } 198 199 void SkLinearGradient::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, 200 int count) { 201 SkASSERT(count > 0); 202 203 SkPoint srcPt; 204 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 205 TileProc proc = fTileProc; 206 const SkPMColor* SK_RESTRICT cache = this->getCache32(); 207 int toggle = init_dither_toggle(x, y); 208 209 if (fDstToIndexClass != kPerspective_MatrixClass) { 210 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 211 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 212 SkFixed dx, fx = SkScalarToFixed(srcPt.fX); 213 214 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 215 SkFixed dxStorage[1]; 216 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); 217 dx = dxStorage[0]; 218 } else { 219 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 220 dx = SkScalarToFixed(fDstToIndex.getScaleX()); 221 } 222 223 LinearShadeProc shadeProc = shadeSpan_linear_repeat; 224 if (0 == dx) { 225 shadeProc = shadeSpan_linear_vertical_lerp; 226 } else if (SkShader::kClamp_TileMode == fTileMode) { 227 shadeProc = shadeSpan_linear_clamp; 228 } else if (SkShader::kMirror_TileMode == fTileMode) { 229 shadeProc = shadeSpan_linear_mirror; 230 } else { 231 SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 232 } 233 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count); 234 } else { 235 SkScalar dstX = SkIntToScalar(x); 236 SkScalar dstY = SkIntToScalar(y); 237 do { 238 dstProc(fDstToIndex, dstX, dstY, &srcPt); 239 unsigned fi = proc(SkScalarToFixed(srcPt.fX)); 240 SkASSERT(fi <= 0xFFFF); 241 *dstC++ = cache[toggle + (fi >> kCache32Shift)]; 242 toggle = next_dither_toggle(toggle); 243 dstX += SK_Scalar1; 244 } while (--count != 0); 245 } 246 } 247 248 SkShader::BitmapType SkLinearGradient::asABitmap(SkBitmap* bitmap, 249 SkMatrix* matrix, 250 TileMode xy[]) const { 251 if (bitmap) { 252 this->getGradientTableBitmap(bitmap); 253 } 254 if (matrix) { 255 matrix->preConcat(fPtsToUnit); 256 } 257 if (xy) { 258 xy[0] = fTileMode; 259 xy[1] = kClamp_TileMode; 260 } 261 return kLinear_BitmapType; 262 } 263 264 SkShader::GradientType SkLinearGradient::asAGradient(GradientInfo* info) const { 265 if (info) { 266 commonAsAGradient(info); 267 info->fPoint[0] = fStart; 268 info->fPoint[1] = fEnd; 269 } 270 return kLinear_GradientType; 271 } 272 273 static void dither_memset16(uint16_t dst[], uint16_t value, uint16_t other, 274 int count) { 275 if (reinterpret_cast<uintptr_t>(dst) & 2) { 276 *dst++ = value; 277 count -= 1; 278 SkTSwap(value, other); 279 } 280 281 sk_memset32((uint32_t*)dst, (value << 16) | other, count >> 1); 282 283 if (count & 1) { 284 dst[count - 1] = value; 285 } 286 } 287 288 #define NO_CHECK_ITER_16 \ 289 do { \ 290 unsigned fi = fx >> SkGradientShaderBase::kCache16Shift; \ 291 SkASSERT(fi < SkGradientShaderBase::kCache16Count); \ 292 fx += dx; \ 293 *dstC++ = cache[toggle + fi]; \ 294 toggle = next_dither_toggle16(toggle); \ 295 } while (0) 296 297 namespace { 298 299 typedef void (*LinearShade16Proc)(TileProc proc, SkFixed dx, SkFixed fx, 300 uint16_t* dstC, const uint16_t* cache, 301 int toggle, int count); 302 303 void shadeSpan16_linear_vertical(TileProc proc, SkFixed dx, SkFixed fx, 304 uint16_t* SK_RESTRICT dstC, 305 const uint16_t* SK_RESTRICT cache, 306 int toggle, int count) { 307 // we're a vertical gradient, so no change in a span 308 unsigned fi = proc(fx) >> SkGradientShaderBase::kCache16Shift; 309 SkASSERT(fi < SkGradientShaderBase::kCache16Count); 310 dither_memset16(dstC, cache[toggle + fi], 311 cache[next_dither_toggle16(toggle) + fi], count); 312 } 313 314 void shadeSpan16_linear_clamp(TileProc proc, SkFixed dx, SkFixed fx, 315 uint16_t* SK_RESTRICT dstC, 316 const uint16_t* SK_RESTRICT cache, 317 int toggle, int count) { 318 SkClampRange range; 319 range.init(fx, dx, count, 0, SkGradientShaderBase::kCache32Count - 1); 320 321 if ((count = range.fCount0) > 0) { 322 dither_memset16(dstC, 323 cache[toggle + range.fV0], 324 cache[next_dither_toggle16(toggle) + range.fV0], 325 count); 326 dstC += count; 327 } 328 if ((count = range.fCount1) > 0) { 329 int unroll = count >> 3; 330 fx = range.fFx1; 331 for (int i = 0; i < unroll; i++) { 332 NO_CHECK_ITER_16; NO_CHECK_ITER_16; 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 } 337 if ((count &= 7) > 0) { 338 do { 339 NO_CHECK_ITER_16; 340 } while (--count != 0); 341 } 342 } 343 if ((count = range.fCount2) > 0) { 344 dither_memset16(dstC, 345 cache[toggle + range.fV1], 346 cache[next_dither_toggle16(toggle) + range.fV1], 347 count); 348 } 349 } 350 351 void shadeSpan16_linear_mirror(TileProc proc, SkFixed dx, SkFixed fx, 352 uint16_t* SK_RESTRICT dstC, 353 const uint16_t* SK_RESTRICT cache, 354 int toggle, int count) { 355 do { 356 unsigned fi = mirror_bits(fx >> SkGradientShaderBase::kCache16Shift, 357 SkGradientShaderBase::kCache16Bits); 358 SkASSERT(fi < SkGradientShaderBase::kCache16Count); 359 fx += dx; 360 *dstC++ = cache[toggle + fi]; 361 toggle = next_dither_toggle16(toggle); 362 } while (--count != 0); 363 } 364 365 void shadeSpan16_linear_repeat(TileProc proc, SkFixed dx, SkFixed fx, 366 uint16_t* SK_RESTRICT dstC, 367 const uint16_t* SK_RESTRICT cache, 368 int toggle, int count) { 369 do { 370 unsigned fi = repeat_bits(fx >> SkGradientShaderBase::kCache16Shift, 371 SkGradientShaderBase::kCache16Bits); 372 SkASSERT(fi < SkGradientShaderBase::kCache16Count); 373 fx += dx; 374 *dstC++ = cache[toggle + fi]; 375 toggle = next_dither_toggle16(toggle); 376 } while (--count != 0); 377 } 378 } 379 380 void SkLinearGradient::shadeSpan16(int x, int y, 381 uint16_t* SK_RESTRICT dstC, int count) { 382 SkASSERT(count > 0); 383 384 SkPoint srcPt; 385 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 386 TileProc proc = fTileProc; 387 const uint16_t* SK_RESTRICT cache = this->getCache16(); 388 int toggle = init_dither_toggle16(x, y); 389 390 if (fDstToIndexClass != kPerspective_MatrixClass) { 391 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 392 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 393 SkFixed dx, fx = SkScalarToFixed(srcPt.fX); 394 395 if (fDstToIndexClass == kFixedStepInX_MatrixClass) { 396 SkFixed dxStorage[1]; 397 (void)fDstToIndex.fixedStepInX(SkIntToScalar(y), dxStorage, NULL); 398 dx = dxStorage[0]; 399 } else { 400 SkASSERT(fDstToIndexClass == kLinear_MatrixClass); 401 dx = SkScalarToFixed(fDstToIndex.getScaleX()); 402 } 403 404 LinearShade16Proc shadeProc = shadeSpan16_linear_repeat; 405 if (SkFixedNearlyZero(dx)) { 406 shadeProc = shadeSpan16_linear_vertical; 407 } else if (SkShader::kClamp_TileMode == fTileMode) { 408 shadeProc = shadeSpan16_linear_clamp; 409 } else if (SkShader::kMirror_TileMode == fTileMode) { 410 shadeProc = shadeSpan16_linear_mirror; 411 } else { 412 SkASSERT(SkShader::kRepeat_TileMode == fTileMode); 413 } 414 (*shadeProc)(proc, dx, fx, dstC, cache, toggle, count); 415 } else { 416 SkScalar dstX = SkIntToScalar(x); 417 SkScalar dstY = SkIntToScalar(y); 418 do { 419 dstProc(fDstToIndex, dstX, dstY, &srcPt); 420 unsigned fi = proc(SkScalarToFixed(srcPt.fX)); 421 SkASSERT(fi <= 0xFFFF); 422 423 int index = fi >> kCache16Shift; 424 *dstC++ = cache[toggle + index]; 425 toggle = next_dither_toggle16(toggle); 426 427 dstX += SK_Scalar1; 428 } while (--count != 0); 429 } 430 } 431 432 #if SK_SUPPORT_GPU 433 434 #include "GrTBackendEffectFactory.h" 435 436 ///////////////////////////////////////////////////////////////////// 437 438 class GrGLLinearGradient : public GrGLGradientEffect { 439 public: 440 441 GrGLLinearGradient(const GrBackendEffectFactory& factory, const GrDrawEffect&) 442 : INHERITED (factory) { } 443 444 virtual ~GrGLLinearGradient() { } 445 446 virtual void emitCode(GrGLShaderBuilder*, 447 const GrDrawEffect&, 448 EffectKey, 449 const char* outputColor, 450 const char* inputColor, 451 const TransformedCoordsArray&, 452 const TextureSamplerArray&) SK_OVERRIDE; 453 454 static EffectKey GenKey(const GrDrawEffect& drawEffect, const GrGLCaps&) { 455 return GenBaseGradientKey(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(SkRandom* 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 TransformedCoordsArray& coords, 527 const TextureSamplerArray& samplers) { 528 this->emitUniforms(builder, key); 529 SkString t = builder->ensureFSCoords2D(coords, 0); 530 t.append(".x"); 531 this->emitColor(builder, t.c_str(), key, outputColor, inputColor, samplers); 532 } 533 534 ///////////////////////////////////////////////////////////////////// 535 536 GrEffectRef* SkLinearGradient::asNewEffect(GrContext* context, const SkPaint&) const { 537 SkASSERT(NULL != context); 538 SkMatrix matrix; 539 if (!this->getLocalMatrix().invert(&matrix)) { 540 return NULL; 541 } 542 matrix.postConcat(fPtsToUnit); 543 return GrLinearGradient::Create(context, *this, matrix, fTileMode); 544 } 545 546 #else 547 548 GrEffectRef* SkLinearGradient::asNewEffect(GrContext*, const SkPaint&) const { 549 SkDEBUGFAIL("Should not call in GPU-less build"); 550 return NULL; 551 } 552 553 #endif 554 555 #ifdef SK_DEVELOPER 556 void SkLinearGradient::toString(SkString* str) const { 557 str->append("SkLinearGradient ("); 558 559 str->appendf("start: (%f, %f)", fStart.fX, fStart.fY); 560 str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY); 561 562 this->INHERITED::toString(str); 563 564 str->append(")"); 565 } 566 #endif 567