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

Side by Side Diff: src/core/SkColorSpaceXform.cpp

Issue 2038823002: Add SkDefaultXform as a catch-all to handle color conversions (Closed) Base URL: https://skia.googlesource.com/skia.git@master
Patch Set: Add tests and fix a few things Created 4 years, 6 months 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
OLDNEW
1 /* 1 /*
2 * Copyright 2016 Google Inc. 2 * Copyright 2016 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 "SkColorPriv.h" 8 #include "SkColorPriv.h"
9 #include "SkColorSpace_Base.h" 9 #include "SkColorSpace_Base.h"
10 #include "SkColorSpaceXform.h" 10 #include "SkColorSpaceXform.h"
11 11
12 bool compute_gamut_xform(SkMatrix44* srcToDst, const SkMatrix44& srcToXYZ, 12 bool compute_gamut_xform(SkMatrix44* srcToDst, const SkMatrix44& srcToXYZ,
13 const SkMatrix44& dstToXYZ) { 13 const SkMatrix44& dstToXYZ) {
14 if (!dstToXYZ.invert(srcToDst)) { 14 if (!dstToXYZ.invert(srcToDst)) {
15 return false; 15 return false;
16 } 16 }
17 17
18 srcToDst->postConcat(srcToXYZ); 18 srcToDst->postConcat(srcToXYZ);
19 return true; 19 return true;
20 } 20 }
21 21
22 std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpa ce>& srcSpace, 22 std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpa ce>& srcSpace,
23 const sk_sp<SkColorSpa ce>& dstSpace) { 23 const sk_sp<SkColorSpa ce>& dstSpace) {
24 if (!srcSpace || !dstSpace) { 24 if (!srcSpace || !dstSpace || as_CSB(srcSpace)->colorLUT() || as_CSB(dstSpac e)->colorLUT()) {
scroggo 2016/06/06 14:06:44 Just making sure I understand - colorLUT is not ye
msarett 2016/06/06 17:33:44 Yes, this is a mix of unimplemented and invalid.
25 return nullptr;
26 }
27
28 SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor);
29 if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) {
25 return nullptr; 30 return nullptr;
26 } 31 }
27 32
28 if (as_CSB(srcSpace)->gammas()->isValues() && as_CSB(dstSpace)->gammas()->is Values()) { 33 if (as_CSB(srcSpace)->gammas()->isValues() && as_CSB(dstSpace)->gammas()->is Values()) {
29 SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor);
30 if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) {
31 return nullptr;
32 }
33
34 float srcGammas[3]; 34 float srcGammas[3];
35 float dstGammas[3]; 35 float dstGammas[3];
36 srcGammas[0] = as_CSB(srcSpace)->gammas()->fRed.fValue; 36 srcGammas[0] = as_CSB(srcSpace)->gammas()->fRed.fValue;
37 srcGammas[1] = as_CSB(srcSpace)->gammas()->fGreen.fValue; 37 srcGammas[1] = as_CSB(srcSpace)->gammas()->fGreen.fValue;
38 srcGammas[2] = as_CSB(srcSpace)->gammas()->fBlue.fValue; 38 srcGammas[2] = as_CSB(srcSpace)->gammas()->fBlue.fValue;
39 dstGammas[0] = 1.0f / as_CSB(dstSpace)->gammas()->fRed.fValue; 39 dstGammas[0] = 1.0f / as_CSB(dstSpace)->gammas()->fRed.fValue;
40 dstGammas[1] = 1.0f / as_CSB(dstSpace)->gammas()->fGreen.fValue; 40 dstGammas[1] = 1.0f / as_CSB(dstSpace)->gammas()->fGreen.fValue;
41 dstGammas[2] = 1.0f / as_CSB(dstSpace)->gammas()->fBlue.fValue; 41 dstGammas[2] = 1.0f / as_CSB(dstSpace)->gammas()->fBlue.fValue;
42 42
43 return std::unique_ptr<SkColorSpaceXform>( 43 return std::unique_ptr<SkColorSpaceXform>(
44 new SkGammaByValueXform(srcGammas, srcToDst, dstGammas)); 44 new SkGammaByValueXform(srcGammas, srcToDst, dstGammas));
45 } 45 }
46 46
47 // Unimplemeted 47 return std::unique_ptr<SkColorSpaceXform>(
48 return nullptr; 48 new SkDefaultXform(as_CSB(srcSpace)->gammas(), srcToDst, as_CSB(dstS pace)->gammas()));
49 } 49 }
50 50
51 //////////////////////////////////////////////////////////////////////////////// /////////////////// 51 //////////////////////////////////////////////////////////////////////////////// ///////////////////
52 52
53 SkGammaByValueXform::SkGammaByValueXform(float srcGammas[3], const SkMatrix44& s rcToDst, 53 static float byte_to_float(uint8_t v) {
scroggo 2016/06/06 14:06:44 Should this be inline?
msarett 2016/06/06 17:33:44 Yeah I think so. "inline" is just a suggestion to
54 float dstGammas[3]) 54 return ((float) v) * (1.0f / 255.0f);
55 : fSrcToDst(srcToDst)
56 {
57 memcpy(fSrcGammas, srcGammas, 3 * sizeof(float));
58 memcpy(fDstGammas, dstGammas, 3 * sizeof(float));
59 } 55 }
60 56
61 static uint8_t clamp_float_to_byte(float v) { 57 static uint8_t clamp_float_to_byte(float v) {
62 v = v * 255.0f; 58 v = v * 255.0f;
63 if (v > 255.0f) { 59 if (v > 255.0f) {
64 return 255; 60 return 255;
65 } else if (v <= 0.0f) { 61 } else if (v <= 0.0f) {
66 return 0; 62 return 0;
67 } else { 63 } else {
68 return (uint8_t) (v + 0.5f); 64 return (uint8_t) (v + 0.5f);
69 } 65 }
70 } 66 }
71 67
68 //////////////////////////////////////////////////////////////////////////////// ///////////////////
69
70 SkGammaByValueXform::SkGammaByValueXform(float srcGammas[3], const SkMatrix44& s rcToDst,
71 float dstGammas[3])
72 : fSrcToDst(srcToDst)
73 {
74 memcpy(fSrcGammas, srcGammas, 3 * sizeof(float));
75 memcpy(fDstGammas, dstGammas, 3 * sizeof(float));
76 }
77
72 void SkGammaByValueXform::xform_RGBA_8888(uint32_t* dst, const uint32_t* src, ui nt32_t len) const { 78 void SkGammaByValueXform::xform_RGBA_8888(uint32_t* dst, const uint32_t* src, ui nt32_t len) const {
73 while (len-- > 0) { 79 while (len-- > 0) {
74 float srcFloats[3]; 80 float srcFloats[3];
75 srcFloats[0] = ((*src >> 0) & 0xFF) * (1.0f / 255.0f); 81 srcFloats[0] = byte_to_float((*src >> 0) & 0xFF);
76 srcFloats[1] = ((*src >> 8) & 0xFF) * (1.0f / 255.0f); 82 srcFloats[1] = byte_to_float((*src >> 8) & 0xFF);
77 srcFloats[2] = ((*src >> 16) & 0xFF) * (1.0f / 255.0f); 83 srcFloats[2] = byte_to_float((*src >> 16) & 0xFF);
78 84
79 // Convert to linear. 85 // Convert to linear.
80 srcFloats[0] = pow(srcFloats[0], fSrcGammas[0]); 86 srcFloats[0] = pow(srcFloats[0], fSrcGammas[0]);
81 srcFloats[1] = pow(srcFloats[1], fSrcGammas[1]); 87 srcFloats[1] = pow(srcFloats[1], fSrcGammas[1]);
82 srcFloats[2] = pow(srcFloats[2], fSrcGammas[2]); 88 srcFloats[2] = pow(srcFloats[2], fSrcGammas[2]);
83 89
84 // Convert to dst gamut. 90 // Convert to dst gamut.
85 float dstFloats[3]; 91 float dstFloats[3];
86 dstFloats[0] = srcFloats[0] * fSrcToDst.getFloat(0, 0) + 92 dstFloats[0] = srcFloats[0] * fSrcToDst.getFloat(0, 0) +
87 srcFloats[1] * fSrcToDst.getFloat(1, 0) + 93 srcFloats[1] * fSrcToDst.getFloat(1, 0) +
(...skipping 12 matching lines...) Expand all
100 106
101 *dst = SkPackARGB32NoCheck(((*src >> 24) & 0xFF), 107 *dst = SkPackARGB32NoCheck(((*src >> 24) & 0xFF),
102 clamp_float_to_byte(dstFloats[0]), 108 clamp_float_to_byte(dstFloats[0]),
103 clamp_float_to_byte(dstFloats[1]), 109 clamp_float_to_byte(dstFloats[1]),
104 clamp_float_to_byte(dstFloats[2])); 110 clamp_float_to_byte(dstFloats[2]));
105 111
106 dst++; 112 dst++;
107 src++; 113 src++;
108 } 114 }
109 } 115 }
116
117 //////////////////////////////////////////////////////////////////////////////// ///////////////////
118
119 // Interpolating lookup in a variably sized table.
120 float interp_lut(uint8_t byte, float* table, size_t tableSize) {
121 float index = byte * (1.0f / 255.0f) * (tableSize - 1);
scroggo 2016/06/06 14:06:44 nit: byte * (1.0f / 255.0f) Won't "byte" get
msarett 2016/06/06 17:33:43 Yes it is, thanks!
122 float diff = index - floor(index);
123 return table[(int) floor(index)] * (1.0f - diff) + table[(int) ceil(index)] * diff;
scroggo 2016/06/06 14:06:44 Note: We have macros for sk_float_floor2int and sk
msarett 2016/06/06 17:33:43 Agreed, done.
124 }
125
126 // Inverse table lookup. Ex: what index corresponds to the input value? This w ill
127 // have strange results when the table is non-increasing. But any sane gamma
scroggo 2016/06/06 14:06:44 Have you seen any insane gamma functions? In that
msarett 2016/06/06 17:33:44 I have not seen any insane gammas. If we did enco
scroggo 2016/06/06 17:40:50 FIXME sounds good to me.
128 // function will be increasing.
129 float interp_lut_inv(float input, float* table, size_t tableSize) {
130 if (input <= table[0]) {
131 return table[0];
132 } else if (input >= table[tableSize - 1]) {
133 return 1.0f;
134 }
135
136 for (uint32_t i = 1; i < tableSize; i++) {
scroggo 2016/06/06 14:06:44 Would it be worth it to use something faster than
msarett 2016/06/06 17:33:44 If we verify that this function is increasing befo
scroggo 2016/06/06 17:40:50 sgtm
137 if (table[i] >= input) {
138 // We are guaranteed that input is greater than table[i - 1].
139 float diff = input - table[i - 1];
140 float distance = table[i] - table[i - 1];
141 float index = (i - 1) + diff / distance;
142 return index / (tableSize - 1);
143 }
144 }
145
146 // Should be unreachable, since we'll return before the loop if input is
147 // larger than the last entry.
148 SkASSERT(false);
149 return 0.0f;
150 }
151
152 SkDefaultXform::SkDefaultXform(const sk_sp<SkGammas>& srcGammas, const SkMatrix4 4& srcToDst,
153 const sk_sp<SkGammas>& dstGammas)
154 : fSrcGammas(srcGammas)
155 , fSrcToDst(srcToDst)
156 , fDstGammas(dstGammas)
157 {}
158
159 void SkDefaultXform::xform_RGBA_8888(uint32_t* dst, const uint32_t* src, uint32_ t len) const {
160 while (len-- > 0) {
161 // Convert to linear.
162 // FIXME (msarett):
163 // Rather than support three different strategies of transforming gamma, QCMS
164 // builds a 256 entry float lookup table from the gamma info. This hand les
165 // the gamma transform and the conversion from bytes to floats. This ma y
166 // be simpler and faster than our current approach.
167 float srcFloats[3];
168 for (int i = 0; i < 3; i++) {
169 const SkGammaCurve& gamma = (*fSrcGammas)[i];
170 uint8_t byte = (*src >> (8 * i)) & 0xFF;
171 if (gamma.isValue()) {
172 srcFloats[i] = pow(byte_to_float(byte), gamma.fValue);
173 } else if (gamma.isTable()) {
174 srcFloats[i] = interp_lut(byte, gamma.fTable.get(), gamma.fTable Size);
175 } else {
176 SkASSERT(gamma.isParametric());
177 float component = byte_to_float(byte);
178 if (component < gamma.fD) {
179 // Y = E * X + F
180 srcFloats[i] = gamma.fE * component + gamma.fF;
181 } else {
182 // Y = (A * X + B)^G + C
183 srcFloats[i] = pow(gamma.fA * component + gamma.fB, gamma.fG ) + gamma.fC;
184 }
185 }
186 }
187
188 // Convert to dst gamut.
189 float dstFloats[3];
190 dstFloats[0] = srcFloats[0] * fSrcToDst.getFloat(0, 0) +
191 srcFloats[1] * fSrcToDst.getFloat(1, 0) +
192 srcFloats[2] * fSrcToDst.getFloat(2, 0) + fSrcToDst.getFl oat(3, 0);
193 dstFloats[1] = srcFloats[0] * fSrcToDst.getFloat(0, 1) +
194 srcFloats[1] * fSrcToDst.getFloat(1, 1) +
195 srcFloats[2] * fSrcToDst.getFloat(2, 1) + fSrcToDst.getFl oat(3, 1);
196 dstFloats[2] = srcFloats[0] * fSrcToDst.getFloat(0, 2) +
197 srcFloats[1] * fSrcToDst.getFloat(1, 2) +
198 srcFloats[2] * fSrcToDst.getFloat(2, 2) + fSrcToDst.getFl oat(3, 2);
199
200 // Convert to dst gamma.
201 // FIXME (msarett):
202 // Rather than support three different strategies of transforming invers e gamma,
203 // QCMS builds a large float lookup table from the gamma info. Is this faster or
204 // better than our approach?
205 for (int i = 0; i < 3; i++) {
206 const SkGammaCurve& gamma = (*fDstGammas)[i];
207 if (gamma.isValue()) {
208 dstFloats[i] = pow(dstFloats[i], 1.0f / gamma.fValue);
209 } else if (gamma.isTable()) {
210 // FIXME (msarett):
211 // An inverse table lookup is particularly strange and non-optim al.
212 dstFloats[i] = interp_lut_inv(dstFloats[i], gamma.fTable.get(), gamma.fTableSize);
213 } else {
214 SkASSERT(gamma.isParametric());
215 // We need to take the inverse of a piecewise function. Assume that
216 // the gamma function is continuous, or this won't make much sen se
217 // anyway.
218 // Plug in |fD| to the first equation to calculate the new piece wise
219 // interval. Then simply use the inverse of the original functi ons.
220 float interval = gamma.fE * gamma.fD + gamma.fF;
221
222 // FIXME (msarett):
223 // Is this math safe? Are we at risk of dividing by zero?
scroggo 2016/06/06 14:06:44 The risk is dividing by E or G. Those were read fr
msarett 2016/06/06 17:33:44 You're right. I added some checks and removed the
224 if (dstFloats[i] < interval) {
225 // X = (Y - F) / E
226 dstFloats[i] = (dstFloats[i] - gamma.fF) / gamma.fE;
227 } else {
228 // X = (Y - C)^(1 / G) - B
229 dstFloats[i] = pow(dstFloats[i] - gamma.fC, 1.0f / gamma.fG) - gamma.fB;
230 }
231 }
232 }
233
234 *dst = SkPackARGB32NoCheck(((*src >> 24) & 0xFF),
scroggo 2016/06/06 14:06:44 Why is it safe to use the NoCheck version? Is this
msarett 2016/06/06 17:33:44 This function is (currently) opaque->opaque or unp
235 clamp_float_to_byte(dstFloats[0]),
236 clamp_float_to_byte(dstFloats[1]),
237 clamp_float_to_byte(dstFloats[2]));
238
239 dst++;
240 src++;
241 }
242 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698