OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 Google Inc. | 2 * Copyright 2012 Google Inc. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkLinearGradient.h" | 8 #include "SkLinearGradient.h" |
9 | 9 |
| 10 static const float kInv255Float = 1.0f / 255; |
| 11 |
10 static inline int repeat_bits(int x, const int bits) { | 12 static inline int repeat_bits(int x, const int bits) { |
11 return x & ((1 << bits) - 1); | 13 return x & ((1 << bits) - 1); |
12 } | 14 } |
13 | 15 |
14 static inline int repeat_8bits(int x) { | 16 static inline int repeat_8bits(int x) { |
15 return x & 0xFF; | 17 return x & 0xFF; |
16 } | 18 } |
17 | 19 |
18 // Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly. | 20 // Visual Studio 2010 (MSC_VER=1600) optimizes bit-shift code incorrectly. |
19 // See http://code.google.com/p/skia/issues/detail?id=472 | 21 // See http://code.google.com/p/skia/issues/detail?id=472 |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 } | 81 } |
80 | 82 |
81 size_t SkLinearGradient::contextSize() const { | 83 size_t SkLinearGradient::contextSize() const { |
82 return sizeof(LinearGradientContext); | 84 return sizeof(LinearGradientContext); |
83 } | 85 } |
84 | 86 |
85 SkShader::Context* SkLinearGradient::onCreateContext(const ContextRec& rec, void
* storage) const { | 87 SkShader::Context* SkLinearGradient::onCreateContext(const ContextRec& rec, void
* storage) const { |
86 return new (storage) LinearGradientContext(*this, rec); | 88 return new (storage) LinearGradientContext(*this, rec); |
87 } | 89 } |
88 | 90 |
| 91 // This swizzles SkColor into the same component order as SkPMColor, but does no
t actually |
| 92 // "pre" multiply the color components. |
| 93 // |
| 94 // This allows us to map directly to Sk4f, and eventually scale down to bytes to
output a |
| 95 // SkPMColor from the floats, without having to swizzle each time. |
| 96 // |
| 97 static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) { |
| 98 return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), S
kColorGetB(c)); |
| 99 } |
| 100 |
89 SkLinearGradient::LinearGradientContext::LinearGradientContext( | 101 SkLinearGradient::LinearGradientContext::LinearGradientContext( |
90 const SkLinearGradient& shader, const ContextRec& rec) | 102 const SkLinearGradient& shader, const ContextRec& ctx) |
91 : INHERITED(shader, rec) | 103 : INHERITED(shader, ctx) |
92 { | 104 { |
93 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; | 105 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; |
94 if ((fDstToIndex.getType() & ~mask) == 0) { | 106 if ((fDstToIndex.getType() & ~mask) == 0) { |
95 // when we dither, we are (usually) not const-in-Y | 107 // when we dither, we are (usually) not const-in-Y |
96 if ((fFlags & SkShader::kHasSpan16_Flag) && !rec.fPaint->isDither()) { | 108 if ((fFlags & SkShader::kHasSpan16_Flag) && !ctx.fPaint->isDither()) { |
97 // only claim this if we do have a 16bit mode (i.e. none of our | 109 // only claim this if we do have a 16bit mode (i.e. none of our |
98 // colors have alpha), and if we are not dithering (which obviously | 110 // colors have alpha), and if we are not dithering (which obviously |
99 // is not const in Y). | 111 // is not const in Y). |
100 fFlags |= SkShader::kConstInY16_Flag; | 112 fFlags |= SkShader::kConstInY16_Flag; |
101 } | 113 } |
102 } | 114 } |
| 115 |
| 116 // setup for Sk4f |
| 117 int count = shader.fColorCount; |
| 118 fRecs.setCount(count); |
| 119 Rec* rec = fRecs.begin(); |
| 120 if (shader.fOrigPos) { |
| 121 rec[0].fPos = 0; |
| 122 SkDEBUGCODE(rec[0].fPosScale = SK_FloatNaN;) // should never get used |
| 123 for (int i = 1; i < count; ++i) { |
| 124 rec[i].fPos = SkTPin(shader.fOrigPos[i], rec[i - 1].fPos, 1.0f); |
| 125 rec[i].fPosScale = 1.0f / (rec[i].fPos - rec[i - 1].fPos); |
| 126 } |
| 127 rec[count - 1].fPos = 1; // overwrite the last value just to be sure
we end at 1.0 |
| 128 } else { |
| 129 // no pos specified, so we compute evenly spaced values |
| 130 const float scale = float(count - 1); |
| 131 float invScale = 1.0f / scale; |
| 132 for (int i = 0; i < count; ++i) { |
| 133 rec[i].fPos = i * invScale; |
| 134 rec[i].fPosScale = scale; |
| 135 } |
| 136 } |
| 137 |
| 138 fApplyAlphaAfterInterp = true; |
| 139 if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Fl
ag) || |
| 140 shader.colorsAreOpaque()) |
| 141 { |
| 142 fApplyAlphaAfterInterp = false; |
| 143 } |
| 144 |
| 145 if (fApplyAlphaAfterInterp) { |
| 146 // Our fColor values are in PMColor order, but are still unpremultiplied
, allowing us to |
| 147 // interpolate in unpremultiplied space first, and then scale by alpha r
ight before we |
| 148 // convert to SkPMColor bytes. |
| 149 const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float; |
| 150 const Sk4f scale(1, 1, 1, paintAlpha); |
| 151 for (int i = 0; i < count; ++i) { |
| 152 uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]); |
| 153 rec[i].fColor = Sk4f::FromBytes((const uint8_t*)&c) * scale; |
| 154 if (i > 0) { |
| 155 SkASSERT(rec[i - 1].fPos <= rec[i].fPos); |
| 156 } |
| 157 } |
| 158 } else { |
| 159 // Our fColor values are premultiplied, so converting to SkPMColor is ju
st a matter |
| 160 // of converting the floats down to bytes. |
| 161 unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() >
> 7); |
| 162 for (int i = 0; i < count; ++i) { |
| 163 SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]); |
| 164 pmc = SkAlphaMulQ(pmc, alphaScale); |
| 165 rec[i].fColor = Sk4f::FromBytes((const uint8_t*)&pmc); |
| 166 if (i > 0) { |
| 167 SkASSERT(rec[i - 1].fPos <= rec[i].fPos); |
| 168 } |
| 169 } |
| 170 } |
103 } | 171 } |
104 | 172 |
105 #define NO_CHECK_ITER \ | 173 #define NO_CHECK_ITER \ |
106 do { \ | 174 do { \ |
107 unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift;
\ | 175 unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift;
\ |
108 SkASSERT(fi <= 0xFF); \ | 176 SkASSERT(fi <= 0xFF); \ |
109 fx += dx; \ | 177 fx += dx; \ |
110 *dstC++ = cache[toggle + fi]; \ | 178 *dstC++ = cache[toggle + fi]; \ |
111 toggle = next_dither_toggle(toggle); \ | 179 toggle = next_dither_toggle(toggle); \ |
112 } while (0) | 180 } while (0) |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
205 *dstC++ = cache[toggle + fi]; | 273 *dstC++ = cache[toggle + fi]; |
206 toggle = next_dither_toggle(toggle); | 274 toggle = next_dither_toggle(toggle); |
207 } while (--count != 0); | 275 } while (--count != 0); |
208 } | 276 } |
209 | 277 |
210 } | 278 } |
211 | 279 |
212 void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor*
SK_RESTRICT dstC, | 280 void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor*
SK_RESTRICT dstC, |
213 int count) { | 281 int count) { |
214 SkASSERT(count > 0); | 282 SkASSERT(count > 0); |
| 283 const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&
>(fShader); |
215 | 284 |
216 const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient&
>(fShader); | 285 #ifndef SK_SUPPORT_LEGACY_LINEAR_GRADIENT_TABLE |
| 286 if (SkShader::kClamp_TileMode == linearGradient.fTileMode && |
| 287 kLinear_MatrixClass == fDstToIndexClass) |
| 288 { |
| 289 this->shade4_clamp(x, y, dstC, count); |
| 290 return; |
| 291 } |
| 292 #endif |
217 | 293 |
218 SkPoint srcPt; | 294 SkPoint srcPt; |
219 SkMatrix::MapXYProc dstProc = fDstToIndexProc; | 295 SkMatrix::MapXYProc dstProc = fDstToIndexProc; |
220 TileProc proc = linearGradient.fTileProc; | 296 TileProc proc = linearGradient.fTileProc; |
221 const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); | 297 const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); |
222 int toggle = init_dither_toggle(x, y); | 298 int toggle = init_dither_toggle(x, y); |
223 | 299 |
224 if (fDstToIndexClass != kPerspective_MatrixClass) { | 300 if (fDstToIndexClass != kPerspective_MatrixClass) { |
225 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, | 301 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, |
226 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); | 302 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); |
(...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
569 str->append("SkLinearGradient ("); | 645 str->append("SkLinearGradient ("); |
570 | 646 |
571 str->appendf("start: (%f, %f)", fStart.fX, fStart.fY); | 647 str->appendf("start: (%f, %f)", fStart.fX, fStart.fY); |
572 str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY); | 648 str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY); |
573 | 649 |
574 this->INHERITED::toString(str); | 650 this->INHERITED::toString(str); |
575 | 651 |
576 str->append(")"); | 652 str->append(")"); |
577 } | 653 } |
578 #endif | 654 #endif |
| 655 |
| 656 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
| 657 |
| 658 #include "SkNx.h" |
| 659 |
| 660 static const SkLinearGradient::LinearGradientContext::Rec* |
| 661 find_forward(const SkLinearGradient::LinearGradientContext::Rec rec[], float til
edX) { |
| 662 SkASSERT(tiledX >= 0 && tiledX <= 1); |
| 663 |
| 664 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); |
| 665 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); |
| 666 SkASSERT(rec[0].fPos <= rec[1].fPos); |
| 667 rec += 1; |
| 668 while (rec->fPos < tiledX) { |
| 669 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); |
| 670 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); |
| 671 SkASSERT(rec[0].fPos <= rec[1].fPos); |
| 672 rec += 1; |
| 673 } |
| 674 return rec - 1; |
| 675 } |
| 676 |
| 677 static const SkLinearGradient::LinearGradientContext::Rec* |
| 678 find_backward(const SkLinearGradient::LinearGradientContext::Rec rec[], float ti
ledX) { |
| 679 SkASSERT(tiledX >= 0 && tiledX <= 1); |
| 680 |
| 681 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); |
| 682 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); |
| 683 SkASSERT(rec[0].fPos <= rec[1].fPos); |
| 684 while (tiledX < rec->fPos) { |
| 685 rec -= 1; |
| 686 SkASSERT(rec[0].fPos >= 0 && rec[0].fPos <= 1); |
| 687 SkASSERT(rec[1].fPos >= 0 && rec[1].fPos <= 1); |
| 688 SkASSERT(rec[0].fPos <= rec[1].fPos); |
| 689 } |
| 690 return rec; |
| 691 } |
| 692 |
| 693 template <bool apply_alpha> SkPMColor trunc_from_255(const Sk4f& x) { |
| 694 SkPMColor c; |
| 695 x.toBytes((uint8_t*)&c); |
| 696 if (apply_alpha) { |
| 697 c = SkPreMultiplyARGB(SkGetPackedA32(c), SkGetPackedR32(c), |
| 698 SkGetPackedG32(c), SkGetPackedB32(c)); |
| 699 } |
| 700 return c; |
| 701 } |
| 702 |
| 703 template <bool apply_alpha> void fill(SkPMColor dst[], int count, |
| 704 const Sk4f& c4, const Sk4f& c4other) { |
| 705 sk_memset32_dither(dst, trunc_from_255<apply_alpha>(c4), |
| 706 trunc_from_255<apply_alpha>(c4other), count); |
| 707 } |
| 708 |
| 709 template <bool apply_alpha> void fill(SkPMColor dst[], int count, const Sk4f& c4
) { |
| 710 // Assumes that c4 does not need to be dithered. |
| 711 sk_memset32(dst, trunc_from_255<apply_alpha>(c4), count); |
| 712 } |
| 713 |
| 714 /* |
| 715 * TODOs |
| 716 * |
| 717 * - tilemodes |
| 718 * - interp before or after premul |
| 719 * - perspective |
| 720 * - optimizations |
| 721 * - use fixed (32bit or 16bit) instead of floats? |
| 722 */ |
| 723 |
| 724 static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext::
Rec* rec) { |
| 725 const float p0 = rec[0].fPos; |
| 726 const Sk4f c0 = rec[0].fColor; |
| 727 const Sk4f c1 = rec[1].fColor; |
| 728 const Sk4f diffc = c1 - c0; |
| 729 const float scale = rec[1].fPosScale; |
| 730 const float t = (fx - p0) * scale; |
| 731 return c0 + Sk4f(t) * diffc; |
| 732 } |
| 733 |
| 734 template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, const Sk4f& c, co
nst Sk4f& dc, |
| 735 const Sk4f& dither0, const Sk4f& dither1)
{ |
| 736 Sk4f dc2 = dc + dc; |
| 737 Sk4f dc4 = dc2 + dc2; |
| 738 Sk4f cd0 = c + dither0; |
| 739 Sk4f cd1 = c + dc + dither1; |
| 740 Sk4f cd2 = cd0 + dc2; |
| 741 Sk4f cd3 = cd1 + dc2; |
| 742 while (n >= 4) { |
| 743 *dstC++ = trunc_from_255<apply_alpha>(cd0); |
| 744 *dstC++ = trunc_from_255<apply_alpha>(cd1); |
| 745 *dstC++ = trunc_from_255<apply_alpha>(cd2); |
| 746 *dstC++ = trunc_from_255<apply_alpha>(cd3); |
| 747 cd0 = cd0 + dc4; |
| 748 cd1 = cd1 + dc4; |
| 749 cd2 = cd2 + dc4; |
| 750 cd3 = cd3 + dc4; |
| 751 n -= 4; |
| 752 } |
| 753 if (n & 2) { |
| 754 *dstC++ = trunc_from_255<apply_alpha>(cd0); |
| 755 *dstC++ = trunc_from_255<apply_alpha>(cd1); |
| 756 cd0 = cd0 + dc2; |
| 757 } |
| 758 if (n & 1) { |
| 759 *dstC++ = trunc_from_255<apply_alpha>(cd0); |
| 760 } |
| 761 } |
| 762 |
| 763 template <bool apply_alpha, bool dx_is_pos> |
| 764 void SkLinearGradient::LinearGradientContext::shade4_dx_clamp(SkPMColor dstC[],
int count, |
| 765 float fx, float dx
, float invDx, |
| 766 const float dither
[2]) { |
| 767 Sk4f dither0(dither[0]); |
| 768 Sk4f dither1(dither[1]); |
| 769 const Rec* rec = fRecs.begin(); |
| 770 |
| 771 const Sk4f dx4 = Sk4f(dx); |
| 772 SkDEBUGCODE(SkPMColor* endDstC = dstC + count;) |
| 773 |
| 774 if (dx_is_pos) { |
| 775 if (fx < 0) { |
| 776 int n = SkTMin(SkFloatToIntFloor(-fx * invDx) + 1, count); |
| 777 fill<apply_alpha>(dstC, n, rec[0].fColor); |
| 778 count -= n; |
| 779 dstC += n; |
| 780 fx += n * dx; |
| 781 SkASSERT(0 == count || fx >= 0); |
| 782 if (n & 1) { |
| 783 SkTSwap(dither0, dither1); |
| 784 } |
| 785 } |
| 786 } else { // dx < 0 |
| 787 if (fx > 1) { |
| 788 int n = SkTMin(SkFloatToIntFloor((1 - fx) * invDx) + 1, count); |
| 789 fill<apply_alpha>(dstC, n, rec[fRecs.count() - 1].fColor); |
| 790 count -= n; |
| 791 dstC += n; |
| 792 fx += n * dx; |
| 793 SkASSERT(0 == count || fx <= 1); |
| 794 if (n & 1) { |
| 795 SkTSwap(dither0, dither1); |
| 796 } |
| 797 } |
| 798 } |
| 799 SkASSERT(count >= 0); |
| 800 |
| 801 const Rec* r; |
| 802 if (dx_is_pos) { |
| 803 r = fRecs.begin(); // start at the beginning |
| 804 } else { |
| 805 r = fRecs.begin() + fRecs.count() - 2; // start at the end |
| 806 } |
| 807 |
| 808 while (count > 0) { |
| 809 if (dx_is_pos) { |
| 810 if (fx >= 1) { |
| 811 fill<apply_alpha>(dstC, count, rec[fRecs.count() - 1].fColor); |
| 812 return; |
| 813 } |
| 814 } else { // dx < 0 |
| 815 if (fx <= 0) { |
| 816 fill<apply_alpha>(dstC, count, rec[0].fColor); |
| 817 return; |
| 818 } |
| 819 } |
| 820 |
| 821 if (dx_is_pos) { |
| 822 r = find_forward(r, fx); |
| 823 } else { |
| 824 r = find_backward(r, fx); |
| 825 } |
| 826 SkASSERT(r >= fRecs.begin() && r < fRecs.begin() + fRecs.count() - 1); |
| 827 |
| 828 const float p0 = r[0].fPos; |
| 829 const Sk4f c0 = r[0].fColor; |
| 830 const float p1 = r[1].fPos; |
| 831 const Sk4f diffc = Sk4f(r[1].fColor) - c0; |
| 832 const float scale = r[1].fPosScale; |
| 833 const float t = (fx - p0) * scale; |
| 834 const Sk4f c = c0 + Sk4f(t) * diffc; |
| 835 const Sk4f dc = diffc * dx4 * Sk4f(scale); |
| 836 |
| 837 int n; |
| 838 if (dx_is_pos) { |
| 839 n = SkTMin((int)((p1 - fx) * invDx) + 1, count); |
| 840 } else { |
| 841 n = SkTMin((int)((p0 - fx) * invDx) + 1, count); |
| 842 } |
| 843 |
| 844 fx += n * dx; |
| 845 count -= n; |
| 846 SkASSERT(count >= 0); |
| 847 if (dx_is_pos) { |
| 848 SkASSERT(0 == count || fx >= p1); |
| 849 } else { |
| 850 SkASSERT(0 == count || fx <= p0); |
| 851 } |
| 852 |
| 853 ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1); |
| 854 dstC += n; |
| 855 SkASSERT(dstC <= endDstC); |
| 856 |
| 857 if (n & 1) { |
| 858 SkTSwap(dither0, dither1); |
| 859 } |
| 860 } |
| 861 } |
| 862 |
| 863 void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMCol
or dstC[], |
| 864 int count) { |
| 865 SkASSERT(count > 0); |
| 866 SkASSERT(kLinear_MatrixClass == fDstToIndexClass); |
| 867 |
| 868 SkPoint srcPt; |
| 869 fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt); |
| 870 float fx = srcPt.x(); |
| 871 const float dx = fDstToIndex.getScaleX(); |
| 872 |
| 873 // Default our dither bias values to 1/2, (rounding), which is no dithering |
| 874 float dither0 = 0.5f; |
| 875 float dither1 = 0.5f; |
| 876 if (fDither) { |
| 877 const float ditherCell[] = { |
| 878 1/8.0f, 5/8.0f, |
| 879 7/8.0f, 3/8.0f, |
| 880 }; |
| 881 const int rowIndex = (y & 1) << 1; |
| 882 dither0 = ditherCell[rowIndex]; |
| 883 dither1 = ditherCell[rowIndex + 1]; |
| 884 if (x & 1) { |
| 885 SkTSwap(dither0, dither1); |
| 886 } |
| 887 } |
| 888 const float dither[2] = { dither0, dither1 }; |
| 889 const float invDx = 1 / dx; |
| 890 |
| 891 if (!SkScalarIsFinite(invDx)) { // dx is effectively zero, gradient is verti
cal |
| 892 Sk4f c = lerp_color(fx, find_forward(fRecs.begin(), SkTPin(fx, 0.0f, 1.0
f))); |
| 893 if (fApplyAlphaAfterInterp) { |
| 894 fill<true>(dstC, count, c + dither0, c + dither1); |
| 895 } else { |
| 896 fill<false>(dstC, count, c + dither0, c + dither1); |
| 897 } |
| 898 return; |
| 899 } |
| 900 |
| 901 if (dx > 0) { |
| 902 if (fApplyAlphaAfterInterp) { |
| 903 this->shade4_dx_clamp<true, true>(dstC, count, fx, dx, invDx, dither
); |
| 904 } else { |
| 905 this->shade4_dx_clamp<false, true>(dstC, count, fx, dx, invDx, dithe
r); |
| 906 } |
| 907 } else { |
| 908 if (fApplyAlphaAfterInterp) { |
| 909 this->shade4_dx_clamp<true, false>(dstC, count, fx, dx, invDx, dithe
r); |
| 910 } else { |
| 911 this->shade4_dx_clamp<false, false>(dstC, count, fx, dx, invDx, dith
er); |
| 912 } |
| 913 } |
| 914 } |
| 915 |
OLD | NEW |