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

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: 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;
caryclark 2015/11/16 14:44:29 This is a popular pattern: src/effects/SkColorMat
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 static uint32_t SkSwizzle_Color_to_PMColor(SkColor c) {
92 return SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c), SkColorGetG(c), S kColorGetB(c));
93 }
94
89 SkLinearGradient::LinearGradientContext::LinearGradientContext( 95 SkLinearGradient::LinearGradientContext::LinearGradientContext(
90 const SkLinearGradient& shader, const ContextRec& rec) 96 const SkLinearGradient& shader, const ContextRec& ctx)
91 : INHERITED(shader, rec) 97 : INHERITED(shader, ctx)
92 { 98 {
93 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask; 99 unsigned mask = SkMatrix::kTranslate_Mask | SkMatrix::kScale_Mask;
94 if ((fDstToIndex.getType() & ~mask) == 0) { 100 if ((fDstToIndex.getType() & ~mask) == 0) {
95 // when we dither, we are (usually) not const-in-Y 101 // when we dither, we are (usually) not const-in-Y
96 if ((fFlags & SkShader::kHasSpan16_Flag) && !rec.fPaint->isDither()) { 102 if ((fFlags & SkShader::kHasSpan16_Flag) && !ctx.fPaint->isDither()) {
97 // only claim this if we do have a 16bit mode (i.e. none of our 103 // 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 104 // colors have alpha), and if we are not dithering (which obviously
99 // is not const in Y). 105 // is not const in Y).
100 fFlags |= SkShader::kConstInY16_Flag; 106 fFlags |= SkShader::kConstInY16_Flag;
101 } 107 }
102 } 108 }
109
110 // setup for Sk4f
111 int count = shader.fColorCount;
112 fRecs.setCount(count);
113 Rec* rec = fRecs.begin();
114 if (shader.fOrigPos) {
115 rec[0].fPos = 0;
116 rec[0].fPosScale = 0; // should never get used
caryclark 2015/11/16 14:44:29 SkDEBUGCODE() ? Assign SK_ScalarNaN instead?
reed1 2015/11/16 16:43:33 Done.
117 for (int i = 1; i < count; ++i) {
118 rec[i].fPos = SkTMin(SkTMax(rec[i - 1].fPos, shader.fOrigPos[i]), 1. 0f);
caryclark 2015/11/16 14:44:29 SkASSERT(rec[i - 1].fPos <= rec[i].fPos); (see com
119 rec[i].fPosScale = 1.0f / (rec[i].fPos - rec[i - 1].fPos);
120 }
121 rec[count - 1].fPos = 1; // overwrite the last value just to be sure we end at 1.0
caryclark 2015/11/16 14:44:29 This seems odd -- is it OK for the last iteration
caryclark 2015/11/16 14:44:29 SkASSERT(rec[count - 2].fPos <= rec[count - 1].fPo
122 } else {
123 float invScale = 1.0f / (count - 1);
124 for (int i = 0; i < count; ++i) {
125 rec[i].fPos = i * invScale;
126 rec[i].fPosScale = count - 1;
127 }
128 }
129
130 fApplyAlphaAfterInterp = true;
131 if ((shader.getGradFlags() & SkGradientShader::kInterpolateColorsInPremul_Fl ag) ||
132 shader.colorsAreOpaque())
133 {
134 fApplyAlphaAfterInterp = false;
135 }
caryclark 2015/11/16 14:44:29 fApplyAlphaAfterInterp = !(shader.getGradFlags() &
136
137 if (fApplyAlphaAfterInterp) {
138 const float paintAlpha = ctx.fPaint->getAlpha() * kInv255Float;
139 const Sk4f scale(1, 1, 1, paintAlpha);
140 for (int i = 0; i < count; ++i) {
141 uint32_t c = SkSwizzle_Color_to_PMColor(shader.fOrigColors[i]);
142 rec[i].fColor = Sk4f::FromBytes((const uint8_t*)&c) * scale;
143 if (i > 0) {
144 SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
caryclark 2015/11/16 14:44:29 this assert looks like it would be more at home ab
145 }
146 }
147 } else {
148 unsigned alphaScale = ctx.fPaint->getAlpha() + (ctx.fPaint->getAlpha() > > 7);
149 for (int i = 0; i < count; ++i) {
150 SkPMColor pmc = SkPreMultiplyColor(shader.fOrigColors[i]);
151 pmc = SkAlphaMulQ(pmc, alphaScale);
152 rec[i].fColor = Sk4f::FromBytes((const uint8_t*)&pmc);
153 if (i > 0) {
154 SkASSERT(rec[i - 1].fPos <= rec[i].fPos);
caryclark 2015/11/16 14:44:29 Ditto
155 }
156 }
157 }
103 } 158 }
104 159
105 #define NO_CHECK_ITER \ 160 #define NO_CHECK_ITER \
106 do { \ 161 do { \
107 unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \ 162 unsigned fi = SkGradFixedToFixed(fx) >> SkGradientShaderBase::kCache32Shift; \
108 SkASSERT(fi <= 0xFF); \ 163 SkASSERT(fi <= 0xFF); \
109 fx += dx; \ 164 fx += dx; \
110 *dstC++ = cache[toggle + fi]; \ 165 *dstC++ = cache[toggle + fi]; \
111 toggle = next_dither_toggle(toggle); \ 166 toggle = next_dither_toggle(toggle); \
112 } while (0) 167 } while (0)
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
205 *dstC++ = cache[toggle + fi]; 260 *dstC++ = cache[toggle + fi];
206 toggle = next_dither_toggle(toggle); 261 toggle = next_dither_toggle(toggle);
207 } while (--count != 0); 262 } while (--count != 0);
208 } 263 }
209 264
210 } 265 }
211 266
212 void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC, 267 void SkLinearGradient::LinearGradientContext::shadeSpan(int x, int y, SkPMColor* SK_RESTRICT dstC,
213 int count) { 268 int count) {
214 SkASSERT(count > 0); 269 SkASSERT(count > 0);
270 const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient& >(fShader);
215 271
216 const SkLinearGradient& linearGradient = static_cast<const SkLinearGradient& >(fShader); 272 #ifndef SK_SUPPORT_LEGACY_LINEAR_GRADIENT_TABLE
273 if (SkShader::kClamp_TileMode == linearGradient.fTileMode &&
274 kLinear_MatrixClass == fDstToIndexClass &&
275 linearGradient.fColorCount > 2)
276 {
277 this->shade4_clamp(x, y, dstC, count);
278 return;
279 }
280 #endif
217 281
218 SkPoint srcPt; 282 SkPoint srcPt;
219 SkMatrix::MapXYProc dstProc = fDstToIndexProc; 283 SkMatrix::MapXYProc dstProc = fDstToIndexProc;
220 TileProc proc = linearGradient.fTileProc; 284 TileProc proc = linearGradient.fTileProc;
221 const SkPMColor* SK_RESTRICT cache = fCache->getCache32(); 285 const SkPMColor* SK_RESTRICT cache = fCache->getCache32();
222 int toggle = init_dither_toggle(x, y); 286 int toggle = init_dither_toggle(x, y);
223 287
224 if (fDstToIndexClass != kPerspective_MatrixClass) { 288 if (fDstToIndexClass != kPerspective_MatrixClass) {
225 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf, 289 dstProc(fDstToIndex, SkIntToScalar(x) + SK_ScalarHalf,
226 SkIntToScalar(y) + SK_ScalarHalf, &srcPt); 290 SkIntToScalar(y) + SK_ScalarHalf, &srcPt);
(...skipping 342 matching lines...) Expand 10 before | Expand all | Expand 10 after
569 str->append("SkLinearGradient ("); 633 str->append("SkLinearGradient (");
570 634
571 str->appendf("start: (%f, %f)", fStart.fX, fStart.fY); 635 str->appendf("start: (%f, %f)", fStart.fX, fStart.fY);
572 str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY); 636 str->appendf(" end: (%f, %f) ", fEnd.fX, fEnd.fY);
573 637
574 this->INHERITED::toString(str); 638 this->INHERITED::toString(str);
575 639
576 str->append(")"); 640 str->append(")");
577 } 641 }
578 #endif 642 #endif
643
644 //////////////////////////////////////////////////////////////////////////////// ///////////////////
645
646 #include "SkNx.h"
647
648 static const SkLinearGradient::LinearGradientContext::Rec* find(float tiledX,
649 const SkLinearGradient::LinearGradientCo ntext::Rec rec[],
650 int count) {
651 SkASSERT(count > 1);
652 SkASSERT(0 == rec[0].fPos);
653 SkASSERT(1 == rec[count - 1].fPos);
654 SkASSERT(tiledX >= 0 && tiledX <= 1);
655
656 int i = 1;
657 while (rec[i].fPos < tiledX) {
658 i += 1;
659 }
caryclark 2015/11/16 14:44:29 Is this array ever so large that it's worth a non-
reed1 2015/11/16 16:43:33 Could be (though my guess is it is usually between
660 return &rec[i - 1];
661 }
662
663 template <bool apply_alpha> SkPMColor trunc_from_255(Sk4f x) {
664 SkPMColor c;
665 x.toBytes((uint8_t*)&c);
666 if (apply_alpha) {
667 c = SkPreMultiplyARGB(SkGetPackedA32(c), SkGetPackedR32(c),
668 SkGetPackedG32(c), SkGetPackedB32(c));
669 }
670 return c;
671 }
672
673 template <bool apply_alpha> void fill(SkPMColor dst[], int count, Sk4f c4, Sk4f c4other) {
674 sk_memset32_dither(dst, trunc_from_255<apply_alpha>(c4),
675 trunc_from_255<apply_alpha>(c4other), count);
676 }
677
678 /*
679 * TODOs
680 *
681 * - tilemodes
682 * - interp before or after premul
683 * - perspective
684 * - optimizations
f(malita) 2015/11/16 15:16:20 How does current perf compare to existing impl?
reed1 2015/11/16 16:43:33 Slower, by 10-20% depending.
685 * - use fixed (32bit or 16bit) instead of floats?
686 */
687
688 static Sk4f lerp_color(float fx, const SkLinearGradient::LinearGradientContext:: Rec* rec) {
689 const float p0 = rec[0].fPos;
690 const Sk4f c0 = rec[0].fColor;
691 const Sk4f c1 = rec[1].fColor;
692 const Sk4f diffc = c1 - c0;
693 const float scale = rec[1].fPosScale;
694 const float t = (fx - p0) * scale;
695 return c0 + Sk4f(t) * diffc;
696 }
697
698 template <bool apply_alpha> void ramp(SkPMColor dstC[], int n, Sk4f c, Sk4f dc,
699 Sk4f dither0, Sk4f dither1) {
700 Sk4f dc2 = dc + dc;
701 Sk4f dc4 = dc2 + dc2;
702 Sk4f cd0 = c + dither0;
703 Sk4f cd1 = c + dc + dither1;
704 Sk4f cd2 = cd0 + dc2;
705 Sk4f cd3 = cd1 + dc2;
706 while (n >= 4) {
707 *dstC++ = trunc_from_255<apply_alpha>(cd0);
708 *dstC++ = trunc_from_255<apply_alpha>(cd1);
709 *dstC++ = trunc_from_255<apply_alpha>(cd2);
710 *dstC++ = trunc_from_255<apply_alpha>(cd3);
caryclark 2015/11/16 14:44:29 probably not worth it, but if (n >= 4) { do {
reed1 2015/11/16 16:43:33 Good observation. But it took me a full minute to
711 cd0 = cd0 + dc4;
712 cd1 = cd1 + dc4;
713 cd2 = cd2 + dc4;
714 cd3 = cd3 + dc4;
715 n -= 4;
716 }
717 if (n & 2) {
718 *dstC++ = trunc_from_255<apply_alpha>(cd0);
719 *dstC++ = trunc_from_255<apply_alpha>(cd1);
720 cd0 = cd0 + dc2;
caryclark 2015/11/16 14:44:29 move this into the condition below?
721 }
722 if (n & 1) {
723 *dstC++ = trunc_from_255<apply_alpha>(cd0);
724 }
725 }
726
727 template <bool apply_alpha> void SkLinearGradient::LinearGradientContext::shade4 _pos_clamp(
728 SkPMColor dstC[], int count,
729 float fx, float d x, float invDx,
730 Sk4f dither0, Sk4 f dither1) {
731 const Rec* rec = fRecs.begin();
732
733 const Sk4f dx4 = Sk4f(dx);
734 SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
735
736 if (fx < 0) {
737 int n = SkTMin(SkFloatToIntFloor(-fx * invDx) + 1, count);
738 fill<apply_alpha>(dstC, n, rec[0].fColor + dither0, rec[0].fColor + dith er1);
739 count -= n;
740 SkASSERT(count >= 0);
741 dstC += n;
742 fx += n * dx;
743 SkASSERT(0 == count || fx >= 0);
744 if (n & 1) {
745 SkTSwap(dither0, dither1);
746 }
747 }
748
749 while (count > 0) {
750 if (fx >= 1) {
751 Sk4f color = rec[fRecs.count() - 1].fColor;
752 fill<apply_alpha>(dstC, count, color + dither0, color + dither1);
753 return;
754 }
755
756 const Rec* r = find(fx, rec, fRecs.count());
caryclark 2015/11/16 14:44:29 add const float p0 = r[0].fPos; to mirr
f(malita) 2015/11/16 15:16:20 Since we're peeking at r[1] below, I would SkASSER
reed1 2015/11/16 17:31:24 Done.
reed1 2015/11/16 17:31:24 Done.
757 const Sk4f c0 = r[0].fColor;
758 const Sk4f diffc = r[1].fColor - c0;
759 const float scale = r[1].fPosScale;
760 const float t = (fx - r[0].fPos) * scale;
caryclark 2015/11/16 14:44:29 const float t = (fx - p0) * scale;
761 Sk4f c = c0 + Sk4f(t) * diffc;
762
763 const Sk4f dc = diffc * dx4 * Sk4f(scale);
764 const float p1 = r[1].fPos;
765
766 int n = SkTMin((int)((p1-fx)*invDx) + 1, count);
caryclark 2015/11/16 14:44:29 Is this the same as the SkFloatToIntFloor in the <
767
768 fx += n * dx;
769 count -= n;
770 SkASSERT(0 == count || fx >= p1);
771 SkASSERT(count >= 0);
caryclark 2015/11/16 14:44:29 may be worth changing either the <0 condition or t
772
773 ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
774 dstC += n;
775 SkASSERT(dstC <= endDstC);
776
777 if (n & 1) {
778 SkTSwap(dither0, dither1);
779 }
780 }
781 }
782
783 template <bool apply_alpha>void SkLinearGradient::LinearGradientContext::shade4_ neg_clamp(
f(malita) 2015/11/16 15:16:20 shade4_neg_clamp & shade4_pos_clamp look pretty si
784 SkPMColor dstC[], int count,
785 float fx, float d x, float invDx,
786 Sk4f dither0, Sk4 f dither1) {
787 const Rec* rec = fRecs.begin();
788
789 const Sk4f dx4 = Sk4f(dx);
790 SkDEBUGCODE(SkPMColor* endDstC = dstC + count;)
791
792 if (fx > 1) {
793 int n = SkTMin(SkFloatToIntFloor((1 - fx) * invDx) + 1, count);
794 Sk4f c = rec[fRecs.count() - 1].fColor;
795 fill<apply_alpha>(dstC, n, c + dither0, c + dither1);
796 count -= n;
797 SkASSERT(count >= 0);
798 dstC += n;
799 fx += n * dx;
800 SkASSERT(0 == count || fx <= 1);
801 if (n & 1) {
802 SkTSwap(dither0, dither1);
803 }
804 }
805
806 while (count > 0) {
807 if (fx <= 0) {
808 fill<apply_alpha>(dstC, count, rec[0].fColor + dither0, rec[0].fColo r + dither1);
caryclark 2015/11/16 14:44:29 maybe Sk4f color = rec[0].fColor;
reed1 2015/11/16 16:43:33 Done.
809 return;
810 }
811
812 const Rec* r = find(fx, rec, fRecs.count());
813 const float p0 = r[0].fPos;
814 const Sk4f c0 = r[0].fColor;
815 const Sk4f diffc = r[1].fColor - c0;
816 const float scale = r[1].fPosScale;
817 const float t = (fx - p0) * scale;
818 Sk4f c = c0 + Sk4f(t) * diffc;
819
820 const Sk4f dc = diffc * dx4 * Sk4f(scale);
821
822 int n = SkTMin((int)((p0 - fx)*invDx) + 1, count);
823
824 fx += n * dx;
825 count -= n;
826 SkASSERT(0 == count || fx <= p0);
827 SkASSERT(count >= 0);
828
829 ramp<apply_alpha>(dstC, n, c, dc, dither0, dither1);
830 dstC += n;
831 SkASSERT(dstC <= endDstC);
832
833 if (n & 1) {
834 SkTSwap(dither0, dither1);
835 }
836 }
837 }
838
839 void SkLinearGradient::LinearGradientContext::shade4_clamp(int x, int y, SkPMCol or dstC[],
840 int count) {
841 SkASSERT(count > 0);
842 SkASSERT(kLinear_MatrixClass == fDstToIndexClass);
843
844 SkPoint srcPt;
845 fDstToIndexProc(fDstToIndex, x + SK_ScalarHalf, y + SK_ScalarHalf, &srcPt);
846 float fx = srcPt.x();
847 const float dx = fDstToIndex.getScaleX();
848
849 Sk4f dither0, dither1;
850 if (fDither) {
851 // [ 1/8 5/8 ]
852 // [ 7/8 3/8 ]
853 const float ditherCell[] = {
854 0.125f, 0.625f,
855 0.875f, 0.375f,
856 };
caryclark 2015/11/16 14:44:29 Out of curiosity, could this be 1.0
reed1 2015/11/16 16:43:33 Very good idea. Done.
857 const int rowIndex = (y & 1) << 1;
858 dither0 = Sk4f(ditherCell[rowIndex]);
859 dither1 = Sk4f(ditherCell[rowIndex + 1]);
860 } else {
861 dither0 = dither1 = Sk4f(0.5f);
862 }
863
864 const float invDx = 1 / dx;
865
866 if (!SkScalarIsFinite(invDx)) { // dx is effectively zero, gradient is verti cal
867 fx = SkTMin(SkTMax(fx, 0.0f), 1.0f);
caryclark 2015/11/16 14:44:28 SkTPin(fx, 0.0f, 1.0f)
reed1 2015/11/16 16:43:33 Done.
868 Sk4f c = lerp_color(fx, find(fx, fRecs.begin(), fRecs.count()));
869 if (fApplyAlphaAfterInterp) {
870 fill<true>(dstC, count, c + dither0, c + dither1);
871 } else {
872 fill<false>(dstC, count, c + dither0, c + dither1);
873 }
874 return;
875 }
876
877 if (dx > 0) {
878 if (fApplyAlphaAfterInterp) {
879 this->shade4_pos_clamp<true>(dstC, count, fx, dx, invDx, dither0, di ther1);
880 } else {
881 this->shade4_pos_clamp<false>(dstC, count, fx, dx, invDx, dither0, d ither1);
882 }
883 } else {
884 if (fApplyAlphaAfterInterp) {
885 this->shade4_neg_clamp<true>(dstC, count, fx, dx, invDx, dither0, di ther1);
886 } else {
887 this->shade4_neg_clamp<false>(dstC, count, fx, dx, invDx, dither0, d ither1);
888 }
889 }
890 }
891
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