Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(44)

Side by Side Diff: src/effects/gradients/SkLinearGradient.cpp

Issue 1436663003: Implement multi-color-stops in linear gradients using Sk4f (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: fix win warnings Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « src/effects/gradients/SkLinearGradient.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « src/effects/gradients/SkLinearGradient.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698