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