OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2011 Google Inc. | 2 * Copyright 2011 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 "SkColorMatrixFilter.h" | 8 #include "SkColorMatrixFilter.h" |
9 #include "SkColorMatrix.h" | |
10 #include "SkColorPriv.h" | |
11 #include "SkNx.h" | |
12 #include "SkReadBuffer.h" | |
13 #include "SkWriteBuffer.h" | |
14 #include "SkUnPreMultiply.h" | |
15 #include "SkString.h" | |
16 | |
17 #define SK_PMORDER_INDEX_A (SK_A32_SHIFT / 8) | |
18 #define SK_PMORDER_INDEX_R (SK_R32_SHIFT / 8) | |
19 #define SK_PMORDER_INDEX_G (SK_G32_SHIFT / 8) | |
20 #define SK_PMORDER_INDEX_B (SK_B32_SHIFT / 8) | |
21 | |
22 static void transpose_to_pmorder(float dst[20], const float src[20]) { | |
23 const float* srcR = src + 0; | |
24 const float* srcG = src + 5; | |
25 const float* srcB = src + 10; | |
26 const float* srcA = src + 15; | |
27 | |
28 for (int i = 0; i < 20; i += 4) { | |
29 dst[i + SK_PMORDER_INDEX_A] = *srcA++; | |
30 dst[i + SK_PMORDER_INDEX_R] = *srcR++; | |
31 dst[i + SK_PMORDER_INDEX_G] = *srcG++; | |
32 dst[i + SK_PMORDER_INDEX_B] = *srcB++; | |
33 } | |
34 } | |
35 | |
36 // src is [20] but some compilers won't accept __restrict__ on anything | |
37 // but an raw pointer or reference | |
38 void SkColorMatrixFilter::initState(const SkScalar* SK_RESTRICT src) { | |
39 transpose_to_pmorder(fTranspose, src); | |
40 | |
41 const float* array = fMatrix.fMat; | |
42 | |
43 // check if we have to munge Alpha | |
44 bool changesAlpha = (array[15] || array[16] || array[17] || (array[18] - 1)
|| array[19]); | |
45 bool usesAlpha = (array[3] || array[8] || array[13]); | |
46 | |
47 if (changesAlpha || usesAlpha) { | |
48 fFlags = changesAlpha ? 0 : kAlphaUnchanged_Flag; | |
49 } else { | |
50 fFlags = kAlphaUnchanged_Flag; | |
51 } | |
52 fFlags |= kSupports4f_Flag; | |
53 } | |
54 | |
55 /////////////////////////////////////////////////////////////////////////////// | |
56 | |
57 SkColorMatrixFilter::SkColorMatrixFilter(const SkColorMatrix& cm) : fMatrix(cm)
{ | |
58 this->initState(cm.fMat); | |
59 } | |
60 | |
61 SkColorMatrixFilter::SkColorMatrixFilter(const SkScalar array[20]) { | |
62 memcpy(fMatrix.fMat, array, 20 * sizeof(SkScalar)); | |
63 this->initState(array); | |
64 } | |
65 | |
66 uint32_t SkColorMatrixFilter::getFlags() const { | |
67 return this->INHERITED::getFlags() | fFlags; | |
68 } | |
69 | |
70 static Sk4f scale_rgb(float scale) { | |
71 static_assert(SkPM4f::A == 3, "Alpha is lane 3"); | |
72 return Sk4f(scale, scale, scale, 1); | |
73 } | |
74 | |
75 static Sk4f premul(const Sk4f& x) { | |
76 return x * scale_rgb(x.kth<SkPM4f::A>()); | |
77 } | |
78 | |
79 static Sk4f unpremul(const Sk4f& x) { | |
80 return x * scale_rgb(1 / x.kth<SkPM4f::A>()); // TODO: fast/approx invert? | |
81 } | |
82 | |
83 static Sk4f clamp_0_1(const Sk4f& x) { | |
84 return Sk4f::Max(Sk4f::Min(x, Sk4f(1)), Sk4f(0)); | |
85 } | |
86 | |
87 static SkPMColor round(const Sk4f& x) { | |
88 SkPMColor c; | |
89 SkNx_cast<uint8_t>(x * Sk4f(255) + Sk4f(0.5f)).store(&c); | |
90 return c; | |
91 } | |
92 | |
93 template <typename Adaptor, typename T> | |
94 void filter_span(const float array[], const T src[], int count, T dst[]) { | |
95 // c0-c3 are already in [0,1]. | |
96 const Sk4f c0 = Sk4f::Load(array + 0); | |
97 const Sk4f c1 = Sk4f::Load(array + 4); | |
98 const Sk4f c2 = Sk4f::Load(array + 8); | |
99 const Sk4f c3 = Sk4f::Load(array + 12); | |
100 // c4 (the translate vector) is in [0, 255]. Bring it back to [0,1]. | |
101 const Sk4f c4 = Sk4f::Load(array + 16)*Sk4f(1.0f/255); | |
102 | |
103 // todo: we could cache this in the constructor... | |
104 T matrix_translate_pmcolor = Adaptor::From4f(premul(clamp_0_1(c4))); | |
105 | |
106 for (int i = 0; i < count; i++) { | |
107 Sk4f srcf = Adaptor::To4f(src[i]); | |
108 float srcA = srcf.kth<SkPM4f::A>(); | |
109 | |
110 if (0 == srcA) { | |
111 dst[i] = matrix_translate_pmcolor; | |
112 continue; | |
113 } | |
114 if (1 != srcA) { | |
115 srcf = unpremul(srcf); | |
116 } | |
117 | |
118 Sk4f r4 = SkNx_dup<SK_R32_SHIFT/8>(srcf); | |
119 Sk4f g4 = SkNx_dup<SK_G32_SHIFT/8>(srcf); | |
120 Sk4f b4 = SkNx_dup<SK_B32_SHIFT/8>(srcf); | |
121 Sk4f a4 = SkNx_dup<SK_A32_SHIFT/8>(srcf); | |
122 | |
123 // apply matrix | |
124 Sk4f dst4 = c0 * r4 + c1 * g4 + c2 * b4 + c3 * a4 + c4; | |
125 | |
126 dst[i] = Adaptor::From4f(premul(clamp_0_1(dst4))); | |
127 } | |
128 } | |
129 | |
130 struct SkPMColorAdaptor { | |
131 static SkPMColor From4f(const Sk4f& c4) { | |
132 return round(c4); | |
133 } | |
134 static Sk4f To4f(SkPMColor c) { | |
135 return SkNx_cast<float>(Sk4b::Load(&c)) * Sk4f(1.0f/255); | |
136 } | |
137 }; | |
138 void SkColorMatrixFilter::filterSpan(const SkPMColor src[], int count, SkPMColor
dst[]) const { | |
139 filter_span<SkPMColorAdaptor>(fTranspose, src, count, dst); | |
140 } | |
141 | |
142 struct SkPM4fAdaptor { | |
143 static SkPM4f From4f(const Sk4f& c4) { | |
144 SkPM4f c; | |
145 c4.store(&c); | |
146 return c; | |
147 } | |
148 static Sk4f To4f(const SkPM4f& c) { | |
149 return Sk4f::Load(&c); | |
150 } | |
151 }; | |
152 void SkColorMatrixFilter::filterSpan4f(const SkPM4f src[], int count, SkPM4f dst
[]) const { | |
153 filter_span<SkPM4fAdaptor>(fTranspose, src, count, dst); | |
154 } | |
155 | |
156 /////////////////////////////////////////////////////////////////////////////// | |
157 | |
158 void SkColorMatrixFilter::flatten(SkWriteBuffer& buffer) const { | |
159 SkASSERT(sizeof(fMatrix.fMat)/sizeof(SkScalar) == 20); | |
160 buffer.writeScalarArray(fMatrix.fMat, 20); | |
161 } | |
162 | |
163 SkFlattenable* SkColorMatrixFilter::CreateProc(SkReadBuffer& buffer) { | |
164 SkColorMatrix matrix; | |
165 if (buffer.readScalarArray(matrix.fMat, 20)) { | |
166 return Create(matrix); | |
167 } | |
168 return nullptr; | |
169 } | |
170 | |
171 bool SkColorMatrixFilter::asColorMatrix(SkScalar matrix[20]) const { | |
172 if (matrix) { | |
173 memcpy(matrix, fMatrix.fMat, 20 * sizeof(SkScalar)); | |
174 } | |
175 return true; | |
176 } | |
177 | |
178 SkColorFilter* SkColorMatrixFilter::newComposed(const SkColorFilter* innerFilter
) const { | |
179 SkScalar innerMatrix[20]; | |
180 if (innerFilter->asColorMatrix(innerMatrix) && !SkColorMatrix::NeedsClamping
(innerMatrix)) { | |
181 SkScalar concat[20]; | |
182 SkColorMatrix::SetConcat(concat, fMatrix.fMat, innerMatrix); | |
183 return SkColorMatrixFilter::Create(concat); | |
184 } | |
185 return nullptr; | |
186 } | |
187 | |
188 #if SK_SUPPORT_GPU | |
189 #include "GrFragmentProcessor.h" | |
190 #include "GrInvariantOutput.h" | |
191 #include "glsl/GrGLSLFragmentProcessor.h" | |
192 #include "glsl/GrGLSLFragmentShaderBuilder.h" | |
193 #include "glsl/GrGLSLProgramDataManager.h" | |
194 #include "glsl/GrGLSLUniformHandler.h" | |
195 | |
196 class ColorMatrixEffect : public GrFragmentProcessor { | |
197 public: | |
198 static const GrFragmentProcessor* Create(const SkColorMatrix& matrix) { | |
199 return new ColorMatrixEffect(matrix); | |
200 } | |
201 | |
202 const char* name() const override { return "Color Matrix"; } | |
203 | |
204 GR_DECLARE_FRAGMENT_PROCESSOR_TEST; | |
205 | |
206 class GLSLProcessor : public GrGLSLFragmentProcessor { | |
207 public: | |
208 // this class always generates the same code. | |
209 static void GenKey(const GrProcessor&, const GrGLSLCaps&, GrProcessorKey
Builder* b) {} | |
210 | |
211 GLSLProcessor(const GrProcessor&) {} | |
212 | |
213 virtual void emitCode(EmitArgs& args) override { | |
214 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; | |
215 fMatrixHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFr
agment_Visibility, | |
216 kMat44f_GrSLType, kDefaul
t_GrSLPrecision, | |
217 "ColorMatrix"); | |
218 fVectorHandle = uniformHandler->addUniform(GrGLSLUniformHandler::kFr
agment_Visibility, | |
219 kVec4f_GrSLType, kDefault
_GrSLPrecision, | |
220 "ColorMatrixVector"); | |
221 | |
222 if (nullptr == args.fInputColor) { | |
223 // could optimize this case, but we aren't for now. | |
224 args.fInputColor = "vec4(1)"; | |
225 } | |
226 GrGLSLFragmentBuilder* fragBuilder = args.fFragBuilder; | |
227 // The max() is to guard against 0 / 0 during unpremul when the inco
ming color is | |
228 // transparent black. | |
229 fragBuilder->codeAppendf("\tfloat nonZeroAlpha = max(%s.a, 0.00001);
\n", | |
230 args.fInputColor); | |
231 fragBuilder->codeAppendf("\t%s = %s * vec4(%s.rgb / nonZeroAlpha, no
nZeroAlpha) + %s;\n", | |
232 args.fOutputColor, | |
233 uniformHandler->getUniformCStr(fMatrixHandl
e), | |
234 args.fInputColor, | |
235 uniformHandler->getUniformCStr(fVectorHandl
e)); | |
236 fragBuilder->codeAppendf("\t%s = clamp(%s, 0.0, 1.0);\n", | |
237 args.fOutputColor, args.fOutputColor); | |
238 fragBuilder->codeAppendf("\t%s.rgb *= %s.a;\n", args.fOutputColor, a
rgs.fOutputColor); | |
239 } | |
240 | |
241 protected: | |
242 virtual void onSetData(const GrGLSLProgramDataManager& uniManager, | |
243 const GrProcessor& proc) override { | |
244 const ColorMatrixEffect& cme = proc.cast<ColorMatrixEffect>(); | |
245 const float* m = cme.fMatrix.fMat; | |
246 // The GL matrix is transposed from SkColorMatrix. | |
247 float mt[] = { | |
248 m[0], m[5], m[10], m[15], | |
249 m[1], m[6], m[11], m[16], | |
250 m[2], m[7], m[12], m[17], | |
251 m[3], m[8], m[13], m[18], | |
252 }; | |
253 static const float kScale = 1.0f / 255.0f; | |
254 float vec[] = { | |
255 m[4] * kScale, m[9] * kScale, m[14] * kScale, m[19] * kScale, | |
256 }; | |
257 uniManager.setMatrix4fv(fMatrixHandle, 1, mt); | |
258 uniManager.set4fv(fVectorHandle, 1, vec); | |
259 } | |
260 | |
261 private: | |
262 GrGLSLProgramDataManager::UniformHandle fMatrixHandle; | |
263 GrGLSLProgramDataManager::UniformHandle fVectorHandle; | |
264 | |
265 typedef GrGLSLFragmentProcessor INHERITED; | |
266 }; | |
267 | |
268 private: | |
269 ColorMatrixEffect(const SkColorMatrix& matrix) : fMatrix(matrix) { | |
270 this->initClassID<ColorMatrixEffect>(); | |
271 } | |
272 | |
273 GrGLSLFragmentProcessor* onCreateGLSLInstance() const override { | |
274 return new GLSLProcessor(*this); | |
275 } | |
276 | |
277 virtual void onGetGLSLProcessorKey(const GrGLSLCaps& caps, | |
278 GrProcessorKeyBuilder* b) const override
{ | |
279 GLSLProcessor::GenKey(*this, caps, b); | |
280 } | |
281 | |
282 bool onIsEqual(const GrFragmentProcessor& s) const override { | |
283 const ColorMatrixEffect& cme = s.cast<ColorMatrixEffect>(); | |
284 return cme.fMatrix == fMatrix; | |
285 } | |
286 | |
287 void onComputeInvariantOutput(GrInvariantOutput* inout) const override { | |
288 // We only bother to check whether the alpha channel will be constant. I
f SkColorMatrix had | |
289 // type flags it might be worth checking the other components. | |
290 | |
291 // The matrix is defined such the 4th row determines the output alpha. T
he first four | |
292 // columns of that row multiply the input r, g, b, and a, respectively,
and the last column | |
293 // is the "translation". | |
294 static const uint32_t kRGBAFlags[] = { | |
295 kR_GrColorComponentFlag, | |
296 kG_GrColorComponentFlag, | |
297 kB_GrColorComponentFlag, | |
298 kA_GrColorComponentFlag | |
299 }; | |
300 static const int kShifts[] = { | |
301 GrColor_SHIFT_R, GrColor_SHIFT_G, GrColor_SHIFT_B, GrColor_SHIFT_A, | |
302 }; | |
303 enum { | |
304 kAlphaRowStartIdx = 15, | |
305 kAlphaRowTranslateIdx = 19, | |
306 }; | |
307 | |
308 SkScalar outputA = 0; | |
309 for (int i = 0; i < 4; ++i) { | |
310 // If any relevant component of the color to be passed through the m
atrix is non-const | |
311 // then we can't know the final result. | |
312 if (0 != fMatrix.fMat[kAlphaRowStartIdx + i]) { | |
313 if (!(inout->validFlags() & kRGBAFlags[i])) { | |
314 inout->setToUnknown(GrInvariantOutput::kWill_ReadInput); | |
315 return; | |
316 } else { | |
317 uint32_t component = (inout->color() >> kShifts[i]) & 0xFF; | |
318 outputA += fMatrix.fMat[kAlphaRowStartIdx + i] * component; | |
319 } | |
320 } | |
321 } | |
322 outputA += fMatrix.fMat[kAlphaRowTranslateIdx]; | |
323 // We pin the color to [0,1]. This would happen to the *final* color out
put from the frag | |
324 // shader but currently the effect does not pin its own output. So in th
e case of over/ | |
325 // underflow this may deviate from the actual result. Maybe the effect s
hould pin its | |
326 // result if the matrix could over/underflow for any component? | |
327 inout->setToOther(kA_GrColorComponentFlag, | |
328 static_cast<uint8_t>(SkScalarPin(outputA, 0, 255)) <<
GrColor_SHIFT_A, | |
329 GrInvariantOutput::kWill_ReadInput); | |
330 } | |
331 | |
332 SkColorMatrix fMatrix; | |
333 | |
334 typedef GrFragmentProcessor INHERITED; | |
335 }; | |
336 | |
337 GR_DEFINE_FRAGMENT_PROCESSOR_TEST(ColorMatrixEffect); | |
338 | |
339 const GrFragmentProcessor* ColorMatrixEffect::TestCreate(GrProcessorTestData* d)
{ | |
340 SkColorMatrix colorMatrix; | |
341 for (size_t i = 0; i < SK_ARRAY_COUNT(colorMatrix.fMat); ++i) { | |
342 colorMatrix.fMat[i] = d->fRandom->nextSScalar1(); | |
343 } | |
344 return ColorMatrixEffect::Create(colorMatrix); | |
345 } | |
346 | |
347 const GrFragmentProcessor* SkColorMatrixFilter::asFragmentProcessor(GrContext*)
const { | |
348 return ColorMatrixEffect::Create(fMatrix); | |
349 } | |
350 | |
351 #endif | |
352 | |
353 #ifndef SK_IGNORE_TO_STRING | |
354 void SkColorMatrixFilter::toString(SkString* str) const { | |
355 str->append("SkColorMatrixFilter: "); | |
356 | |
357 str->append("matrix: ("); | |
358 for (int i = 0; i < 20; ++i) { | |
359 str->appendScalar(fMatrix.fMat[i]); | |
360 if (i < 19) { | |
361 str->append(", "); | |
362 } | |
363 } | |
364 str->append(")"); | |
365 } | |
366 #endif | |
367 | |
368 /////////////////////////////////////////////////////////////////////////////// | |
369 | 9 |
370 static SkScalar byte_to_scale(U8CPU byte) { | 10 static SkScalar byte_to_scale(U8CPU byte) { |
371 if (0xFF == byte) { | 11 if (0xFF == byte) { |
372 // want to get this exact | 12 // want to get this exact |
373 return 1; | 13 return 1; |
374 } else { | 14 } else { |
375 return byte * 0.00392156862745f; | 15 return byte * 0.00392156862745f; |
376 } | 16 } |
377 } | 17 } |
378 | 18 |
379 SkColorFilter* SkColorMatrixFilter::CreateLightingFilter(SkColor mul, SkColor ad
d) { | 19 SkColorFilter* SkColorMatrixFilter::CreateLightingFilter(SkColor mul, SkColor ad
d) { |
380 SkColorMatrix matrix; | 20 SkColorMatrix matrix; |
381 matrix.setScale(byte_to_scale(SkColorGetR(mul)), | 21 matrix.setScale(byte_to_scale(SkColorGetR(mul)), |
382 byte_to_scale(SkColorGetG(mul)), | 22 byte_to_scale(SkColorGetG(mul)), |
383 byte_to_scale(SkColorGetB(mul)), | 23 byte_to_scale(SkColorGetB(mul)), |
384 1); | 24 1); |
385 matrix.postTranslate(SkIntToScalar(SkColorGetR(add)), | 25 matrix.postTranslate(SkIntToScalar(SkColorGetR(add)), |
386 SkIntToScalar(SkColorGetG(add)), | 26 SkIntToScalar(SkColorGetG(add)), |
387 SkIntToScalar(SkColorGetB(add)), | 27 SkIntToScalar(SkColorGetB(add)), |
388 0); | 28 0); |
389 return SkColorMatrixFilter::Create(matrix); | 29 return SkColorMatrixFilter::Create(matrix); |
390 } | 30 } |
OLD | NEW |