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; | |
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 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 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 Loading... | |
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 Loading... | |
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 | |
OLD | NEW |