OLD | NEW |
(Empty) | |
| 1 /* |
| 2 * Copyright 2016 Google Inc. |
| 3 * |
| 4 * Use of this source code is governed by a BSD-style license that can be |
| 5 * found in the LICENSE file. |
| 6 */ |
| 7 |
| 8 #include "SkColorSpaceXform_A2B.h" |
| 9 |
| 10 #include "SkColorPriv.h" |
| 11 #include "SkColorSpace_A2B.h" |
| 12 #include "SkColorSpace_XYZ.h" |
| 13 #include "SkColorSpacePriv.h" |
| 14 #include "SkColorSpaceXformPriv.h" |
| 15 #include "SkMakeUnique.h" |
| 16 #include "SkNx.h" |
| 17 #include "SkSRGB.h" |
| 18 #include "SkTypes.h" |
| 19 |
| 20 #include "SkRasterPipeline_opts.h" |
| 21 |
| 22 #define AI SK_ALWAYS_INLINE |
| 23 |
| 24 namespace { |
| 25 |
| 26 class ApplyParametric { |
| 27 public: |
| 28 ApplyParametric(const SkColorSpaceTransferFn& fn) |
| 29 : fFn(fn) |
| 30 {} |
| 31 |
| 32 float operator()(float x) const { |
| 33 float y; |
| 34 if (x >= fFn.fD) { |
| 35 y = ::powf(fFn.fA * x + fFn.fB, fFn.fG) + fFn.fC; |
| 36 } else { |
| 37 y = fFn.fE * x + fFn.fF; |
| 38 } |
| 39 if (y >= 1.f) { |
| 40 return 1.f; |
| 41 } else if (y >= 0.f) { |
| 42 return y; |
| 43 } |
| 44 return 0.f; |
| 45 } |
| 46 |
| 47 private: |
| 48 SkColorSpaceTransferFn fFn; |
| 49 }; |
| 50 |
| 51 class ApplyTable { |
| 52 public: |
| 53 ApplyTable(const float* table, int size) |
| 54 : fTable(table) |
| 55 , fSize(size) |
| 56 {} |
| 57 |
| 58 float operator()(float x) const { |
| 59 return interp_lut(x, fTable, fSize); |
| 60 } |
| 61 |
| 62 private: |
| 63 const float* fTable; |
| 64 int fSize; |
| 65 }; |
| 66 |
| 67 } |
| 68 |
| 69 ////////////////////////////////////////////////////////////////////////////////
/////////////////// |
| 70 bool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorForma
t srcFormat, |
| 71 const void* src, int count, SkAlphaType alph
aType) const { |
| 72 SkRasterPipeline pipeline; |
| 73 switch (srcFormat) { |
| 74 case kBGRA_8888_ColorFormat: |
| 75 pipeline.append(SkRasterPipeline::load_s_8888, &src); |
| 76 pipeline.append(SkRasterPipeline::swap_rb); |
| 77 break; |
| 78 case kRGBA_8888_ColorFormat: |
| 79 pipeline.append(SkRasterPipeline::load_s_8888, &src); |
| 80 break; |
| 81 default: |
| 82 SkCSXformPrintf("F16/F32 source color format not supported\n"); |
| 83 return false; |
| 84 } |
| 85 |
| 86 pipeline.extend(fElementsPipeline); |
| 87 |
| 88 if (kPremul_SkAlphaType == alphaType) { |
| 89 pipeline.append(SkRasterPipeline::premul); |
| 90 } |
| 91 |
| 92 switch (dstFormat) { |
| 93 case kBGRA_8888_ColorFormat: |
| 94 pipeline.append(SkRasterPipeline::swap_rb); |
| 95 pipeline.append(SkRasterPipeline::store_8888, &dst); |
| 96 break; |
| 97 case kRGBA_8888_ColorFormat: |
| 98 pipeline.append(SkRasterPipeline::store_8888, &dst); |
| 99 break; |
| 100 case kRGBA_F16_ColorFormat: |
| 101 if (!fLinearDstGamma) { |
| 102 return false; |
| 103 } |
| 104 pipeline.append(SkRasterPipeline::store_f16, &dst); |
| 105 break; |
| 106 case kRGBA_F32_ColorFormat: |
| 107 if (!fLinearDstGamma) { |
| 108 return false; |
| 109 } |
| 110 pipeline.append(SkRasterPipeline::store_f32, &dst); |
| 111 break; |
| 112 } |
| 113 |
| 114 auto p = pipeline.compile(); |
| 115 |
| 116 p(0, count); |
| 117 |
| 118 return true; |
| 119 } |
| 120 |
| 121 static inline SkColorSpaceTransferFn value_to_parametric(float exp) { |
| 122 return {exp, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; |
| 123 } |
| 124 |
| 125 static inline SkColorSpaceTransferFn gammanamed_to_parametric(SkGammaNamed gamma
Named) { |
| 126 switch (gammaNamed) { |
| 127 case kLinear_SkGammaNamed: |
| 128 return value_to_parametric(1.f); |
| 129 case kSRGB_SkGammaNamed: |
| 130 return {2.4f, (1.f / 1.055f), (0.055f / 1.055f), 0.f, 0.04045f, (1.f
/ 12.92f), 0.f}; |
| 131 case k2Dot2Curve_SkGammaNamed: |
| 132 return value_to_parametric(2.2f); |
| 133 default: |
| 134 SkASSERT(false); |
| 135 return {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; |
| 136 } |
| 137 } |
| 138 |
| 139 static inline SkColorSpaceTransferFn gamma_to_parametric(const SkGammas& gammas,
int channel) { |
| 140 switch (gammas.type(channel)) { |
| 141 case SkGammas::Type::kNamed_Type: |
| 142 return gammanamed_to_parametric(gammas.data(channel).fNamed); |
| 143 case SkGammas::Type::kValue_Type: |
| 144 return value_to_parametric(gammas.data(channel).fValue); |
| 145 case SkGammas::Type::kParam_Type: |
| 146 return gammas.params(channel); |
| 147 default: |
| 148 SkASSERT(false); |
| 149 return {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; |
| 150 } |
| 151 } |
| 152 static inline SkColorSpaceTransferFn invert_parametric(const SkColorSpaceTransfe
rFn& fn) { |
| 153 // Original equation is: y = (ax + b)^g + c for x >= d |
| 154 // y = ex + f otherwise |
| 155 // |
| 156 // so 1st inverse is: (y - c)^(1/g) = ax + b |
| 157 // x = ((y - c)^(1/g) - b) / a |
| 158 // |
| 159 // which can be re-written as: x = (1/a)(y - c)^(1/g) - b/a |
| 160 // x = ((1/a)^g)^(1/g) * (y - c)^(1/g) - b/a |
| 161 // x = ([(1/a)^g]y + [-((1/a)^g)c]) ^ [1/g] + [-
b/a] |
| 162 // |
| 163 // and 2nd inverse is: x = (y - f) / e |
| 164 // which can be re-written as: x = [1/e]y + [-f/e] |
| 165 // |
| 166 // and now both can be expressed in terms of the same parametric form as the |
| 167 // original - parameters are enclosed in square barckets. |
| 168 |
| 169 // find inverse for linear segment (if possible) |
| 170 float e, f; |
| 171 if (0.f == fn.fE) { |
| 172 // otherwise assume it should be 0 as it is the lower segment |
| 173 // as y = f is a constant function |
| 174 e = 0.f; |
| 175 f = 0.f; |
| 176 } else { |
| 177 e = 1.f / fn.fE; |
| 178 f = -fn.fF / fn.fE; |
| 179 } |
| 180 // find inverse for the other segment (if possible) |
| 181 float g, a, b, c; |
| 182 if (0.f == fn.fA || 0.f == fn.fG) { |
| 183 // otherwise assume it should be 1 as it is the top segment |
| 184 // as you can't invert the constant functions y = b^g + c, or y = 1 + c |
| 185 g = 1.f; |
| 186 a = 0.f; |
| 187 b = 0.f; |
| 188 c = 1.f; |
| 189 } else { |
| 190 g = 1.f / fn.fG; |
| 191 a = powf(1.f / fn.fA, fn.fG); |
| 192 b = -a * fn.fC; |
| 193 c = -fn.fB / fn.fA; |
| 194 } |
| 195 const float d = fn.fE * fn.fD + fn.fF; |
| 196 return {g, a, b, c, d, e, f}; |
| 197 } |
| 198 |
| 199 static std::vector<float> build_inverse_table(const float* inTable, int inTableS
ize) { |
| 200 static constexpr int kInvTableSize = 256; |
| 201 std::vector<float> outTable(kInvTableSize); |
| 202 for (int i = 0; i < kInvTableSize; ++i) { |
| 203 const float x = ((float) i) * (1.f / ((float) (kInvTableSize - 1))); |
| 204 const float y = inverse_interp_lut(x, inTable, inTableSize); |
| 205 outTable[i] = y; |
| 206 } |
| 207 return outTable; |
| 208 } |
| 209 |
| 210 SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace, |
| 211 SkColorSpace_XYZ* dstSpace) |
| 212 : fLinearDstGamma(kLinear_SkGammaNamed == dstSpace->gammaNamed()) { |
| 213 #if (SkCSXformPrintfDefined) |
| 214 static const char* debugGammaNamed[4] = { |
| 215 "Linear", "SRGB", "2.2", "NonStandard" |
| 216 }; |
| 217 static const char* debugGammas[5] = { |
| 218 "None", "Named", "Value", "Table", "Param" |
| 219 }; |
| 220 #endif |
| 221 // add in all input color space -> PCS xforms |
| 222 for (int i = 0; i < srcSpace->count(); ++i) { |
| 223 const SkColorSpace_A2B::Element& e = srcSpace->element(i); |
| 224 switch (e.type()) { |
| 225 case SkColorSpace_A2B::Element::Type::kGammaNamed: |
| 226 if (kLinear_SkGammaNamed != e.gammaNamed()) { |
| 227 SkCSXformPrintf("Gamma stage added: %s\n", |
| 228 debugGammaNamed[(int)e.gammaNamed()]); |
| 229 addGamma(ApplyParametric(gammanamed_to_parametric(e.gammaNam
ed())), |
| 230 kRGB_Channels); |
| 231 } |
| 232 break; |
| 233 case SkColorSpace_A2B::Element::Type::kGammas: { |
| 234 const SkGammas& gammas = e.gammas(); |
| 235 SkCSXformPrintf("Gamma stage added:"); |
| 236 for (int channel = 0; channel < 3; ++channel) { |
| 237 SkCSXformPrintf(" %s", debugGammas[(int)gammas.type(cha
nnel)]); |
| 238 } |
| 239 SkCSXformPrintf("\n"); |
| 240 bool gammaNeedsRef = false; |
| 241 for (int channel = 0; channel < 3; ++channel) { |
| 242 if (SkGammas::Type::kTable_Type == gammas.type(channel))
{ |
| 243 addGamma(ApplyTable(gammas.table(channel), |
| 244 gammas.data(channel).fTable.fSiz
e), |
| 245 static_cast<Channels>(channel)); |
| 246 gammaNeedsRef = true; |
| 247 } else { |
| 248 addGamma(ApplyParametric(gamma_to_parametric(gammas,
channel)), |
| 249 static_cast<Channels>(channel)); |
| 250 } |
| 251 } |
| 252 if (gammaNeedsRef) { |
| 253 fGammaRefs.push_back(sk_ref_sp(&gammas)); |
| 254 } |
| 255 } |
| 256 break; |
| 257 case SkColorSpace_A2B::Element::Type::kCLUT: |
| 258 SkCSXformPrintf("CLUT stage added [%d][%d][%d]\n", e.colorLUT().
fGridPoints[0], |
| 259 e.colorLUT().fGridPoints[1], e.colorLUT().fGridP
oints[2]); |
| 260 fCLUTs.push_back(sk_ref_sp(&e.colorLUT())); |
| 261 fElementsPipeline.append(SkRasterPipeline::color_lookup_table, |
| 262 fCLUTs.back().get()); |
| 263 break; |
| 264 case SkColorSpace_A2B::Element::Type::kMatrix: |
| 265 if (!e.matrix().isIdentity()) { |
| 266 SkCSXformPrintf("Matrix stage added\n"); |
| 267 addMatrix(e.matrix()); |
| 268 } |
| 269 break; |
| 270 } |
| 271 } |
| 272 |
| 273 // Lab PCS -> XYZ PCS |
| 274 if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) { |
| 275 SkCSXformPrintf("Lab -> XYZ element added\n"); |
| 276 fElementsPipeline.append(SkRasterPipeline::lab_to_xyz); |
| 277 } |
| 278 |
| 279 // and XYZ PCS -> output color space xforms |
| 280 if (!dstSpace->fromXYZD50()->isIdentity()) { |
| 281 addMatrix(*dstSpace->fromXYZD50()); |
| 282 } |
| 283 |
| 284 if (kNonStandard_SkGammaNamed != dstSpace->gammaNamed()) { |
| 285 if (!fLinearDstGamma) { |
| 286 addGamma(ApplyParametric( |
| 287 invert_parametric(gammanamed_to_parametric(dstSpace-
>gammaNamed()))), |
| 288 kRGB_Channels); |
| 289 } |
| 290 } else { |
| 291 for (int channel = 0; channel < 3; ++channel) { |
| 292 const SkGammas& gammas = *dstSpace->gammas(); |
| 293 if (SkGammas::Type::kTable_Type == gammas.type(channel)) { |
| 294 fGammaTables.push_front(build_inverse_table(gammas.table(channel
), |
| 295 gammas.data(channel)
.fTable.fSize)); |
| 296 addGamma(ApplyTable(fGammaTables.front().data(), fGammaTables.fr
ont().size()), |
| 297 static_cast<Channels>(channel)); |
| 298 } else { |
| 299 addGamma(ApplyParametric(invert_parametric(gamma_to_parametric(g
ammas, channel))), |
| 300 static_cast<Channels>(channel)); |
| 301 } |
| 302 } |
| 303 } |
| 304 } |
| 305 |
| 306 void SkColorSpaceXform_A2B::addGamma(std::function<float(float)> fn, Channels ch
annels) { |
| 307 fGammaFunctions.push_front(std::move(fn)); |
| 308 switch (channels) { |
| 309 case kRGB_Channels: |
| 310 fElementsPipeline.append(SkRasterPipeline::fn_1_r, &fGammaFunctions.
front()); |
| 311 fElementsPipeline.append(SkRasterPipeline::fn_1_g, &fGammaFunctions.
front()); |
| 312 fElementsPipeline.append(SkRasterPipeline::fn_1_b, &fGammaFunctions.
front()); |
| 313 break; |
| 314 case kR_Channels: |
| 315 fElementsPipeline.append(SkRasterPipeline::fn_1_r, &fGammaFunctions.
front()); |
| 316 break; |
| 317 case kG_Channels: |
| 318 fElementsPipeline.append(SkRasterPipeline::fn_1_g, &fGammaFunctions.
front()); |
| 319 break; |
| 320 case kB_Channels: |
| 321 fElementsPipeline.append(SkRasterPipeline::fn_1_b, &fGammaFunctions.
front()); |
| 322 break; |
| 323 default: |
| 324 SkASSERT(false); |
| 325 } |
| 326 } |
| 327 |
| 328 void SkColorSpaceXform_A2B::addMatrix(const SkMatrix44& matrix) { |
| 329 fMatrices.push_front(std::vector<float>(12)); |
| 330 auto& m = fMatrices.front(); |
| 331 m[ 0] = matrix.get(0, 0); |
| 332 m[ 1] = matrix.get(1, 0); |
| 333 m[ 2] = matrix.get(2, 0); |
| 334 m[ 3] = matrix.get(0, 1); |
| 335 m[ 4] = matrix.get(1, 1); |
| 336 m[ 5] = matrix.get(2, 1); |
| 337 m[ 6] = matrix.get(0, 2); |
| 338 m[ 7] = matrix.get(1, 2); |
| 339 m[ 8] = matrix.get(2, 2); |
| 340 m[ 9] = matrix.get(0, 3); |
| 341 m[10] = matrix.get(1, 3); |
| 342 m[11] = matrix.get(2, 3); |
| 343 SkASSERT(matrix.get(3, 0) == 0.f); |
| 344 SkASSERT(matrix.get(3, 1) == 0.f); |
| 345 SkASSERT(matrix.get(3, 2) == 0.f); |
| 346 SkASSERT(matrix.get(3, 3) == 1.f); |
| 347 fElementsPipeline.append(SkRasterPipeline::matrix_3x4, m.data()); |
| 348 fElementsPipeline.append(SkRasterPipeline::clamp_0); |
| 349 fElementsPipeline.append(SkRasterPipeline::clamp_a); |
| 350 } |
| 351 |
| 352 |
OLD | NEW |