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

Side by Side Diff: ui/gfx/skia_color_space_util.cc

Issue 2784673006: color: Do not enforce T(0)=0 and T(1)=1 constraints in approxmation (Closed)
Patch Set: Fix the right error bound Created 3 years, 8 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
« no previous file with comments | « ui/gfx/icc_profile_unittest.cc ('k') | ui/gfx/test/icc_profiles.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "ui/gfx/skia_color_space_util.h" 5 #include "ui/gfx/skia_color_space_util.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <cmath> 8 #include <cmath>
9 #include <vector> 9 #include <vector>
10 10
11 #include "base/logging.h" 11 #include "base/logging.h"
12 12
13 namespace gfx { 13 namespace gfx {
14 14
15 namespace { 15 namespace {
16 16
17 // Solve for the parameter fC, given the parameter fD, assuming fF is zero. 17 // Evaluate the gradient of the linear component of fn
18 void SkTransferFnEvalGradientLinear(const SkColorSpaceTransferFn& fn,
19 float x,
20 float* d_fn_d_fC_at_x,
21 float* d_fn_d_fF_at_x) {
22 *d_fn_d_fC_at_x = x;
23 *d_fn_d_fF_at_x = 1.f;
24 }
25
26 // Solve for the parameters fC and fF, given the parameter fD.
18 void SkTransferFnSolveLinear(SkColorSpaceTransferFn* fn, 27 void SkTransferFnSolveLinear(SkColorSpaceTransferFn* fn,
19 const float* x, 28 const float* x,
20 const float* t, 29 const float* t,
21 size_t n) { 30 size_t n) {
22 // If this has no linear segment, don't try to solve for one. 31 // If this has no linear segment, don't try to solve for one.
23 fn->fC = 1; 32 if (fn->fD <= 0) {
24 fn->fF = 0; 33 fn->fC = 1;
25 if (fn->fD <= 0) 34 fn->fF = 0;
26 return; 35 return;
36 }
27 37
28 // Because this is a linear least squares fit of a single variable, our normal 38 // Let ne_lhs be the left hand side of the normal equations, and let ne_rhs
29 // equations are 1x1. Use the same framework as in SolveNonlinear, even though 39 // be the right hand side. This is a 4x4 matrix, but we will only update the
30 // this is pretty trivial. 40 // upper-left 2x2 sub-matrix.
31 float ne_lhs = 0; 41 SkMatrix44 ne_lhs;
32 float ne_rhs = 0; 42 SkVector4 ne_rhs;
43 for (int row = 0; row < 2; ++row) {
44 for (int col = 0; col < 2; ++col) {
45 ne_lhs.set(row, col, 0);
46 }
47 }
48 for (int row = 0; row < 4; ++row)
49 ne_rhs.fData[row] = 0;
33 50
34 // Add the contributions from each sample to the normal equations. 51 // Add the contributions from each sample to the normal equations.
52 float x_linear_max = 0;
35 for (size_t i = 0; i < n; ++i) { 53 for (size_t i = 0; i < n; ++i) {
36 // Ignore points in the nonlinear segment. 54 // Ignore points in the nonlinear segment.
37 if (x[i] >= fn->fD) 55 if (x[i] >= fn->fD)
38 continue; 56 continue;
57 x_linear_max = std::max(x_linear_max, x[i]);
39 58
40 // Let J be the gradient of fn with respect to parameter C, evaluated at 59 // Let J be the gradient of fn with respect to parameters C and F, evaluated
41 // this point, and let r be the residual at this point. 60 // at this point.
42 float J = x[i]; 61 float J[2];
62 SkTransferFnEvalGradientLinear(*fn, x[i], &J[0], &J[1]);
63
64 // Let r be the residual at this point.
43 float r = t[i]; 65 float r = t[i];
44 66
45 // Update the normal equations left and right hand sides. 67 // Update the normal equations left hand side with the outer product of J
46 ne_lhs += J * J; 68 // with itself.
47 ne_rhs += J * r; 69 for (int row = 0; row < 2; ++row) {
70 for (int col = 0; col < 2; ++col) {
71 ne_lhs.set(row, col, ne_lhs.get(row, col) + J[row] * J[col]);
72 }
73 // Update the normal equations right hand side the product of J with the
74 // residual
75 ne_rhs.fData[row] += J[row] * r;
76 }
48 } 77 }
49 78
50 // If we only had a single x point at 0, that isn't enough to construct a 79 // If we only have a single x point at 0, that isn't enough to construct a
51 // linear segment, so add an additional point connecting to the nonlinear 80 // linear segment, so add an additional point connecting to the nonlinear
52 // segment. 81 // segment.
53 if (ne_lhs == 0) { 82 if (x_linear_max == 0) {
54 float J = fn->fD; 83 float J[2];
84 SkTransferFnEvalGradientLinear(*fn, fn->fD, &J[0], &J[1]);
55 float r = SkTransferFnEval(*fn, fn->fD); 85 float r = SkTransferFnEval(*fn, fn->fD);
56 ne_lhs += J * J; 86 for (int row = 0; row < 2; ++row) {
57 ne_lhs += J * r; 87 for (int col = 0; col < 2; ++col) {
88 ne_lhs.set(row, col, ne_lhs.get(row, col) + J[row] * J[col]);
89 }
90 ne_rhs.fData[row] += J[row] * r;
91 }
58 } 92 }
59 93
94 // Solve the normal equations.
95 SkMatrix44 ne_lhs_inv;
96 bool invert_result = ne_lhs.invert(&ne_lhs_inv);
97 DCHECK(invert_result);
98 SkVector4 solution = ne_lhs_inv * ne_rhs;
99
60 // Update the transfer function. 100 // Update the transfer function.
61 fn->fC = ne_rhs / ne_lhs; 101 fn->fC = solution.fData[0];
62 fn->fF = 0; 102 fn->fF = solution.fData[1];
63 } 103 }
64 104
65 // Evaluate the gradient of the nonlinear component of fn. This assumes that 105 // Evaluate the gradient of the nonlinear component of fn
66 // |fn| maps 1 to 1, and therefore fE is implicity 1 - pow(fA + fB, fG).
67 void SkTransferFnEvalGradientNonlinear(const SkColorSpaceTransferFn& fn, 106 void SkTransferFnEvalGradientNonlinear(const SkColorSpaceTransferFn& fn,
68 float x, 107 float x,
69 float* d_fn_d_fA_at_x, 108 float* d_fn_d_fA_at_x,
70 float* d_fn_d_fB_at_x, 109 float* d_fn_d_fB_at_x,
110 float* d_fn_d_fE_at_x,
71 float* d_fn_d_fG_at_x) { 111 float* d_fn_d_fG_at_x) {
72 float Ax_plus_B = fn.fA * x + fn.fB; 112 float base = fn.fA * x + fn.fB;
73 float A_plus_B = fn.fA + fn.fB; 113 if (base > 0.f) {
74 if (Ax_plus_B >= 0.f && A_plus_B >= 0.f) { 114 *d_fn_d_fA_at_x = fn.fG * x * std::pow(base, fn.fG - 1.f);
75 float Ax_plus_B_to_G = std::pow(fn.fA * x + fn.fB, fn.fG); 115 *d_fn_d_fB_at_x = fn.fG * std::pow(base, fn.fG - 1.f);
76 float Ax_plus_B_to_G_minus_1 = std::pow(fn.fA * x + fn.fB, fn.fG - 1.f); 116 *d_fn_d_fE_at_x = 1.f;
77 float A_plus_B_to_G = std::pow(fn.fA + fn.fB, fn.fG); 117 *d_fn_d_fG_at_x = std::pow(base, fn.fG) * std::log(base);
78 float A_plus_B_to_G_minus_1 = std::pow(fn.fA + fn.fB, fn.fG - 1.f);
79 *d_fn_d_fA_at_x =
80 (x * Ax_plus_B_to_G_minus_1 - A_plus_B_to_G_minus_1) * fn.fG;
81 *d_fn_d_fB_at_x = (Ax_plus_B_to_G_minus_1 - A_plus_B_to_G_minus_1) * fn.fG;
82 *d_fn_d_fG_at_x = Ax_plus_B_to_G * std::log(Ax_plus_B) -
83 A_plus_B_to_G * std::log(A_plus_B);
84 } else { 118 } else {
85 *d_fn_d_fA_at_x = 0.f; 119 *d_fn_d_fA_at_x = 0.f;
86 *d_fn_d_fB_at_x = 0.f; 120 *d_fn_d_fB_at_x = 0.f;
121 *d_fn_d_fE_at_x = 0.f;
87 *d_fn_d_fG_at_x = 0.f; 122 *d_fn_d_fG_at_x = 0.f;
88 } 123 }
89 } 124 }
90 125
91 // Take one Gauss-Newton step updating fA, fB, fE, and fG, given fD. 126 // Take one Gauss-Newton step updating fA, fB, fE, and fG, given fD.
92 bool SkTransferFnGaussNewtonStepNonlinear(SkColorSpaceTransferFn* fn, 127 bool SkTransferFnGaussNewtonStepNonlinear(SkColorSpaceTransferFn* fn,
93 float* error_Linfty_before, 128 float* error_Linfty_after,
94 const float* x, 129 const float* x,
95 const float* t, 130 const float* t,
96 size_t n) { 131 size_t n) {
97 float kEpsilon = 1.f / 1024.f;
98 // Let ne_lhs be the left hand side of the normal equations, and let ne_rhs 132 // Let ne_lhs be the left hand side of the normal equations, and let ne_rhs
99 // be the right hand side. Zero the diagonal of |ne_lhs| and all of |ne_rhs|. 133 // be the right hand side. Zero the diagonal of |ne_lhs| and all of |ne_rhs|.
100 SkMatrix44 ne_lhs(SkMatrix44::kIdentity_Constructor); 134 SkMatrix44 ne_lhs;
101 SkVector4 ne_rhs; 135 SkVector4 ne_rhs;
102 for (int row = 0; row < 3; ++row) { 136 for (int row = 0; row < 4; ++row) {
103 ne_lhs.set(row, row, 0); 137 for (int col = 0; col < 4; ++col) {
138 ne_lhs.set(row, col, 0);
139 }
104 ne_rhs.fData[row] = 0; 140 ne_rhs.fData[row] = 0;
105 } 141 }
106 142
107 // Add the contributions from each sample to the normal equations. 143 // Add the contributions from each sample to the normal equations.
108 *error_Linfty_before = 0;
109 for (size_t i = 0; i < n; ++i) { 144 for (size_t i = 0; i < n; ++i) {
110 // Ignore points in the linear segment. 145 // Ignore points in the linear segment.
111 if (x[i] < fn->fD) 146 if (x[i] < fn->fD)
112 continue; 147 continue;
113 148
114 // Let J be the gradient of fn with respect to parameters A, B, E, and G, 149 // Let J be the gradient of fn with respect to parameters A, B, E, and G,
115 // evaulated at this point. 150 // evaulated at this point.
116 float J[3]; 151 float J[4];
117 SkTransferFnEvalGradientNonlinear(*fn, x[i], &J[0], &J[1], &J[2]); 152 SkTransferFnEvalGradientNonlinear(*fn, x[i], &J[0], &J[1], &J[2], &J[3]);
118
119 // Let r be the residual at this point; 153 // Let r be the residual at this point;
120 float r = t[i] - SkTransferFnEval(*fn, x[i]); 154 float r = t[i] - SkTransferFnEval(*fn, x[i]);
121 *error_Linfty_before += std::abs(r);
122 155
123 // Update the normal equations left hand side with the outer product of J 156 // Update the normal equations left hand side with the outer product of J
124 // with itself. 157 // with itself.
125 for (int row = 0; row < 3; ++row) { 158 for (int row = 0; row < 4; ++row) {
126 for (int col = 0; col < 3; ++col) { 159 for (int col = 0; col < 4; ++col) {
127 ne_lhs.set(row, col, ne_lhs.get(row, col) + J[row] * J[col]); 160 ne_lhs.set(row, col, ne_lhs.get(row, col) + J[row] * J[col]);
128 } 161 }
129 162
130 // Update the normal equations right hand side the product of J with the 163 // Update the normal equations right hand side the product of J with the
131 // residual 164 // residual
132 ne_rhs.fData[row] += J[row] * r; 165 ne_rhs.fData[row] += J[row] * r;
133 } 166 }
134 } 167 }
135 168
136 // Note that if fG = 1, then the normal equations will be singular, because 169 // Note that if fG = 1, then the normal equations will be singular (because,
137 // fB cancels out with itself. This could be handled better by using QR 170 // when fG = 1, because fB and fE are equivalent parameters). To avoid
138 // factorization instead of solving the normal equations. 171 // problems, fix fE (row/column 3) in these circumstances.
139 if (std::abs(fn->fG - 1) < kEpsilon) { 172 float kEpsilonForG = 1.f / 1024.f;
140 for (int row = 0; row < 3; ++row) { 173 if (std::abs(fn->fG - 1) < kEpsilonForG) {
141 float value = (row == 1) ? 1.f : 0.f; 174 for (int row = 0; row < 4; ++row) {
142 ne_lhs.set(row, 1, value); 175 float value = (row == 2) ? 1.f : 0.f;
143 ne_lhs.set(1, row, value); 176 ne_lhs.set(row, 2, value);
177 ne_lhs.set(2, row, value);
144 } 178 }
145 ne_rhs.fData[1] = 0.f; 179 ne_rhs.fData[2] = 0.f;
146 fn->fB = 0.f;
147 } 180 }
148 181
149 // Solve the normal equations. 182 // Solve the normal equations.
150 SkMatrix44 ne_lhs_inv; 183 SkMatrix44 ne_lhs_inv;
151 if (!ne_lhs.invert(&ne_lhs_inv)) 184 if (!ne_lhs.invert(&ne_lhs_inv))
152 return false; 185 return false;
153 SkVector4 step = ne_lhs_inv * ne_rhs; 186 SkVector4 step = ne_lhs_inv * ne_rhs;
154 187
155 // Update the transfer function. 188 // Update the transfer function.
156 fn->fA += step.fData[0]; 189 fn->fA += step.fData[0];
157 fn->fB += step.fData[1]; 190 fn->fB += step.fData[1];
158 fn->fG += step.fData[2]; 191 fn->fE += step.fData[2];
192 fn->fG += step.fData[3];
159 193
160 // Clamp fA to the valid range. 194 // fA should always be positive.
161 fn->fA = std::max(fn->fA, 0.f); 195 fn->fA = std::max(fn->fA, 0.f);
162 196
163 // Shift fB to ensure that fA+fB > 0. 197 // Ensure that fn be defined at fD.
164 if (fn->fA + fn->fB < 0.f) 198 if (fn->fA * fn->fD + fn->fB < 0.f)
165 fn->fB = -fn->fA; 199 fn->fB = -fn->fA * fn->fD;
166 200
167 // Compute fE such that 1 maps to 1. 201 // Compute the Linfinity error.
168 fn->fE = 1.f - std::pow(fn->fA + fn->fB, fn->fG); 202 *error_Linfty_after = 0;
203 for (size_t i = 0; i < n; ++i) {
204 if (x[i] >= fn->fD) {
205 float error = std::abs(t[i] - SkTransferFnEval(*fn, x[i]));
206 *error_Linfty_after = std::max(error, *error_Linfty_after);
207 }
208 }
209
169 return true; 210 return true;
170 } 211 }
171 212
172 // Solve for fA, fB, fE, and fG, given fD. The initial value of |fn| is the 213 // Solve for fA, fB, fE, and fG, given fD. The initial value of |fn| is the
173 // point from which iteration starts. 214 // point from which iteration starts.
174 bool SkTransferFnSolveNonlinear(SkColorSpaceTransferFn* fn, 215 bool SkTransferFnSolveNonlinear(SkColorSpaceTransferFn* fn,
175 const float* x, 216 const float* x,
176 const float* t, 217 const float* t,
177 size_t n) { 218 size_t n) {
178 // Take a maximum of 16 Gauss-Newton steps. 219 // Take a maximum of 8 Gauss-Newton steps.
179 const size_t kNumSteps = 16; 220 const size_t kNumSteps = 8;
180 221
181 // The L-infinity error before each step. 222 // The L-infinity error after each step.
182 float step_error[kNumSteps] = {0}; 223 float step_error[kNumSteps] = {0};
183 224 size_t step = 0;
184 for (size_t step = 0; step < kNumSteps; ++step) { 225 for (;; ++step) {
185 // If the normal equations are singular, we can't continue. 226 // If the normal equations are singular, we can't continue.
186 if (!SkTransferFnGaussNewtonStepNonlinear(fn, &step_error[step], x, t, n)) 227 if (!SkTransferFnGaussNewtonStepNonlinear(fn, &step_error[step], x, t, n))
187 return false; 228 return false;
188 229
189 // If the error is inf or nan, we are clearly not converging. 230 // If the error is inf or nan, we are clearly not converging.
190 if (std::isnan(step_error[step]) || std::isinf(step_error[step])) 231 if (std::isnan(step_error[step]) || std::isinf(step_error[step]))
191 return false; 232 return false;
192 233
193 // If our error is non-negligable and increasing then we are not in the 234 // Stop if our error is tiny.
194 // region of convergence. 235 float kEarlyOutTinyErrorThreshold = (1.f / 16.f) / 256.f;
195 const float kNonNegligbleErrorEpsilon = 1.f / 256.f; 236 if (step_error[step] < kEarlyOutTinyErrorThreshold)
196 const float kGrowthFactor = 1.25f; 237 break;
197 if (step > 2 && step_error[step] > kNonNegligbleErrorEpsilon) { 238
198 if (step_error[step - 1] * kGrowthFactor < step_error[step] && 239 // Stop if our error is not changing, or changing in the wrong direction.
199 step_error[step - 2] * kGrowthFactor < step_error[step - 1]) { 240 if (step > 1) {
241 // If our error is is huge for two iterations, we're probably not in the
242 // region of convergence.
243 if (step_error[step] > 1.f && step_error[step - 1] > 1.f)
200 return false; 244 return false;
245
246 // If our error didn't change by ~1%, assume we've converged as much as we
247 // are going to.
248 const float kEarlyOutByPercentChangeThreshold = 32.f / 256.f;
249 const float kMinimumPercentChange = 1.f / 128.f;
250 float percent_change =
251 std::abs(step_error[step] - step_error[step - 1]) / step_error[step];
252 if (percent_change < kMinimumPercentChange &&
253 step_error[step] < kEarlyOutByPercentChangeThreshold) {
254 break;
201 } 255 }
202 } 256 }
257 if (step == kNumSteps - 1)
258 break;
203 } 259 }
204 260
261 // Declare failure if our error is obviously too high.
262 float kDidNotConvergeThreshold = 64.f / 256.f;
263 if (step_error[step] > kDidNotConvergeThreshold)
264 return false;
265
205 // We've converged to a reasonable solution. If some of the parameters are 266 // We've converged to a reasonable solution. If some of the parameters are
206 // extremely close to 0 or 1, set them to 0 or 1. 267 // extremely close to 0 or 1, set them to 0 or 1.
207 const float kRoundEpsilon = 1.f / 1024.f; 268 const float kRoundEpsilon = 1.f / 1024.f;
208 if (std::abs(fn->fA - 1.f) < kRoundEpsilon) 269 if (std::abs(fn->fA - 1.f) < kRoundEpsilon)
209 fn->fA = 1.f; 270 fn->fA = 1.f;
210 if (std::abs(fn->fB) < kRoundEpsilon) 271 if (std::abs(fn->fB) < kRoundEpsilon)
211 fn->fB = 0; 272 fn->fB = 0;
212 if (std::abs(fn->fE) < kRoundEpsilon) 273 if (std::abs(fn->fE) < kRoundEpsilon)
213 fn->fE = 0; 274 fn->fE = 0;
214 if (std::abs(fn->fG - 1.f) < kRoundEpsilon) 275 if (std::abs(fn->fG - 1.f) < kRoundEpsilon)
215 fn->fG = 1.f; 276 fn->fG = 1.f;
216 return true; 277 return true;
217 } 278 }
218 279
219 bool SkApproximateTransferFnInternal(const float* x, 280 bool SkApproximateTransferFnInternal(const float* x,
220 const float* t, 281 const float* t,
221 size_t n, 282 size_t n,
222 SkColorSpaceTransferFn* fn) { 283 SkColorSpaceTransferFn* fn) {
223 // First, guess at a value of fD. Assume that the nonlinear segment applies 284 // First, guess at a value of fD. Assume that the nonlinear segment applies
224 // to all x >= 0.25. This is generally a safe assumption (fD is usually less 285 // to all x >= 0.25. This is generally a safe assumption (fD is usually less
225 // than 0.1). 286 // than 0.1).
226 fn->fD = 0.25f; 287 fn->fD = 0.25f;
227 288
228 // Do a nonlinear regression on the nonlinear segment. Use a number of guesses 289 // Do a nonlinear regression on the nonlinear segment. Use a number of guesses
229 // for the initial value of fG, because not all values will converge. 290 // for the initial value of fG, because not all values will converge.
230 bool nonlinear_fit_converged = false; 291 bool nonlinear_fit_converged = false;
231 { 292 {
232 const size_t kNumInitialGammas = 4; 293 const size_t kNumInitialGammas = 4;
233 float initial_gammas[kNumInitialGammas] = {2.f, 1.f, 3.f, 0.5f}; 294 float initial_gammas[kNumInitialGammas] = {2.2f, 1.f, 3.f, 0.5f};
234 for (size_t i = 0; i < kNumInitialGammas; ++i) { 295 for (size_t i = 0; i < kNumInitialGammas; ++i) {
235 fn->fG = initial_gammas[i]; 296 fn->fG = initial_gammas[i];
236 fn->fA = 1; 297 fn->fA = 1;
237 fn->fB = 0; 298 fn->fB = 0;
238 fn->fC = 1; 299 fn->fC = 1;
239 fn->fE = 0; 300 fn->fE = 0;
240 fn->fF = 0; 301 fn->fF = 0;
241 if (SkTransferFnSolveNonlinear(fn, x, t, n)) { 302 if (SkTransferFnSolveNonlinear(fn, x, t, n)) {
242 nonlinear_fit_converged = true; 303 nonlinear_fit_converged = true;
243 break; 304 break;
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
365 426
366 bool GFX_EXPORT SkApproximateTransferFn(sk_sp<SkICC> sk_icc, 427 bool GFX_EXPORT SkApproximateTransferFn(sk_sp<SkICC> sk_icc,
367 float* max_error, 428 float* max_error,
368 SkColorSpaceTransferFn* fn) { 429 SkColorSpaceTransferFn* fn) {
369 SkICC::Tables tables; 430 SkICC::Tables tables;
370 bool got_tables = sk_icc->rawTransferFnData(&tables); 431 bool got_tables = sk_icc->rawTransferFnData(&tables);
371 if (!got_tables) 432 if (!got_tables)
372 return false; 433 return false;
373 434
374 // Merge all channels' tables into a single array. 435 // Merge all channels' tables into a single array.
436 SkICC::Channel* channels[3] = {&tables.fGreen, &tables.fRed, &tables.fBlue};
375 std::vector<float> x; 437 std::vector<float> x;
376 std::vector<float> t; 438 std::vector<float> t;
377 for (size_t c = 0; c < 3; ++c) { 439 for (size_t c = 0; c < 3; ++c) {
378 SkICC::Channel* channels[3] = {&tables.fRed, &tables.fGreen, &tables.fBlue};
379 SkICC::Channel* channel = channels[c]; 440 SkICC::Channel* channel = channels[c];
380 const float* data = reinterpret_cast<const float*>( 441 const float* data = reinterpret_cast<const float*>(
381 tables.fStorage->bytes() + channel->fOffset); 442 tables.fStorage->bytes() + channel->fOffset);
382 for (int i = 0; i < channel->fCount; ++i) { 443 for (int i = 0; i < channel->fCount; ++i) {
383 float xi = i / (channel->fCount - 1.f); 444 float xi = i / (channel->fCount - 1.f);
384 float ti = data[i]; 445 float ti = data[i];
385 x.push_back(xi); 446 x.push_back(xi);
386 t.push_back(ti); 447 t.push_back(ti);
387 } 448 }
388 } 449 }
389 450
390 // Approximate and evalaute. 451 // Approximate the transfer function.
391 bool converged = 452 bool converged =
392 SkApproximateTransferFnInternal(x.data(), t.data(), x.size(), fn); 453 SkApproximateTransferFnInternal(x.data(), t.data(), x.size(), fn);
393 if (!converged) 454 if (!converged)
394 return false; 455 return false;
456
457 // Compute the error among all channels.
395 *max_error = 0; 458 *max_error = 0;
396 for (size_t i = 0; i < x.size(); ++i) { 459 for (size_t i = 0; i < x.size(); ++i) {
397 float fn_of_xi = gfx::SkTransferFnEval(*fn, x[i]); 460 float fn_of_xi = gfx::SkTransferFnEval(*fn, x[i]);
398 float error_at_xi = std::abs(t[i] - fn_of_xi); 461 float error_at_xi = std::abs(t[i] - fn_of_xi);
399 *max_error = std::max(*max_error, error_at_xi); 462 *max_error = std::max(*max_error, error_at_xi);
400 } 463 }
401 return true; 464 return true;
402 } 465 }
403 466
404 bool SkMatrixIsApproximatelyIdentity(const SkMatrix44& m) { 467 bool SkMatrixIsApproximatelyIdentity(const SkMatrix44& m) {
405 const float kEpsilon = 1.f / 256.f; 468 const float kEpsilon = 1.f / 256.f;
406 for (int i = 0; i < 4; ++i) { 469 for (int i = 0; i < 4; ++i) {
407 for (int j = 0; j < 4; ++j) { 470 for (int j = 0; j < 4; ++j) {
408 float identity_value = i == j ? 1 : 0; 471 float identity_value = i == j ? 1 : 0;
409 float value = m.get(i, j); 472 float value = m.get(i, j);
410 if (std::abs(identity_value - value) > kEpsilon) 473 if (std::abs(identity_value - value) > kEpsilon)
411 return false; 474 return false;
412 } 475 }
413 } 476 }
414 return true; 477 return true;
415 } 478 }
416 479
417 } // namespace gfx 480 } // namespace gfx
OLDNEW
« no previous file with comments | « ui/gfx/icc_profile_unittest.cc ('k') | ui/gfx/test/icc_profiles.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698