Chromium Code Reviews| Index: src/core/SkColorSpaceXform_A2B.cpp |
| diff --git a/src/core/SkColorSpaceXform_A2B.cpp b/src/core/SkColorSpaceXform_A2B.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..baa318856e08022b50e9c091ae3e7b516f824894 |
| --- /dev/null |
| +++ b/src/core/SkColorSpaceXform_A2B.cpp |
| @@ -0,0 +1,286 @@ |
| +/* |
| + * Copyright 2016 Google Inc. |
| + * |
| + * Use of this source code is governed by a BSD-style license that can be |
| + * found in the LICENSE file. |
| + */ |
| + |
| +#include "SkColorSpaceXform_A2B.h" |
| + |
| +#include "SkColorPriv.h" |
| +#include "SkColorSpace_A2B.h" |
| +#include "SkColorSpace_XYZ.h" |
| +#include "SkColorSpacePriv.h" |
| +#include "SkColorSpaceXformPriv.h" |
| +#include "SkMakeUnique.h" |
| +#include "SkNx.h" |
| +#include "SkSRGB.h" |
| +#include "SkTypes.h" |
| + |
| +#include "SkRasterPipeline_opts.h" |
| + |
| +#define AI SK_ALWAYS_INLINE |
| + |
| +/////////////////////////////////////////////////////////////////////////////////////////////////// |
| +bool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorFormat srcFormat, |
| + const void* src, int count, SkAlphaType alphaType) const { |
|
mtklein_C
2016/11/09 11:04:36
Is this the alphaType for src or dst? I'm guessin
msarett1
2016/11/11 14:36:51
It refers to the dst, and the src is always unprem
|
| + SkRasterPipeline entirePipeline; |
|
msarett1
2016/11/09 00:01:04
I don't love this variable name.
raftias
2016/11/10 21:36:06
Acknowledged.
|
| + switch (srcFormat) { |
| + case kBGRA_8888_ColorFormat: |
| + entirePipeline.append(SkRasterPipeline::load_s_linear_bgra, &src); |
| + break; |
| + case kRGBA_8888_ColorFormat: |
| + entirePipeline.append(SkRasterPipeline::load_s_linear_rgba, &src); |
| + break; |
| + default: |
| + SkCSXformPrintf("F16/F32 source color format not supported\n"); |
|
mtklein_C
2016/11/09 11:04:36
load_s_f16 will probably work just fine. And we c
|
| + return false; |
| + } |
| + |
| + entirePipeline.extend(fElementsPipeline); |
| + |
| + if (kPremul_SkAlphaType == alphaType) { |
| + //entirePipeline.append(SkRasterPipeline::premul); |
|
msarett1
2016/11/09 00:01:04
Why comment this out?
raftias
2016/11/10 21:36:06
Mistake, I forgot to uncomment it after I tested s
|
| + } |
| + |
| + switch (dstFormat) { |
| + case kBGRA_8888_ColorFormat: |
| + entirePipeline.append(SkRasterPipeline::store_linear_bgra, &dst); |
| + break; |
| + case kRGBA_8888_ColorFormat: |
| + entirePipeline.append(SkRasterPipeline::store_linear_rgba, &dst); |
| + break; |
| + case kRGBA_F16_ColorFormat: |
| + if (!fLinearDstGamma) { |
| + return false; |
| + } |
| + entirePipeline.append(SkRasterPipeline::store_f16, &dst); |
| + break; |
| + case kRGBA_F32_ColorFormat: |
| + if (!fLinearDstGamma) { |
| + return false; |
| + } |
| + entirePipeline.append(SkRasterPipeline::store_f32, &dst); |
| + break; |
| + } |
| + |
| + auto p = entirePipeline.compile(); |
| + |
| + p(0, count); |
| + |
| + return true; |
| +} |
| + |
| +static inline SkColorSpaceTransferFn value_to_parametric(float exp) { |
| + return {exp, 1.f, 0.f, 0.f, 0.f, 0.f, 0.f}; |
| +} |
| + |
| +static inline SkColorSpaceTransferFn gammanamed_to_parametric(SkGammaNamed gammaNamed) { |
| + switch (gammaNamed) { |
| + case kLinear_SkGammaNamed: |
| + return value_to_parametric(1.f); |
| + case kSRGB_SkGammaNamed: |
| + return {2.4f, (1.f / 1.055f), (0.055f / 1.055f), 0.f, 0.04045f, (1.f / 12.92f), 0.f}; |
| + case k2Dot2Curve_SkGammaNamed: |
| + return value_to_parametric(2.2f); |
| + default: |
| + SkASSERT(false); |
| + return {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; |
| + } |
| +} |
| + |
| +static inline SkColorSpaceTransferFn gamma_to_parametric(const SkGammas& gammas, int channel) { |
| + switch (gammas.type(channel)) { |
| + case SkGammas::Type::kNamed_Type: |
| + return gammanamed_to_parametric(gammas.data(channel).fNamed); |
| + case SkGammas::Type::kValue_Type: |
| + return value_to_parametric(gammas.data(channel).fValue); |
| + case SkGammas::Type::kParam_Type: |
| + return gammas.params(channel); |
| + default: |
| + SkASSERT(false); |
| + return {-1.f, -1.f, -1.f, -1.f, -1.f, -1.f, -1.f}; |
| + } |
| +} |
| +static inline SkColorSpaceTransferFn invert_parametric(const SkColorSpaceTransferFn& fn) { |
| + // Original equation is: y = (ax + b)^g + c for x >= d |
| + // y = ex + f otherwise |
| + // |
| + // so 1st inverse is: (y - c)^(1/g) = ax + b |
| + // x = ((y - c)^(1/g) - b) / a |
| + // |
| + // which can be re-written as: x = (1/a)(y - c)^(1/g) - b/a |
| + // x = ((1/a)^g)^(1/g) * (y - c)^(1/g) - b/a |
| + // x = ([(1/a)^g]y + [-((1/a)^g)c]) ^ [1/g] + [-b/a] |
| + // |
| + // and 2nd inverse is: x = (y - f) / e |
| + // which can be re-written as: x = [1/e]y + [-f/e] |
| + // |
| + // and now both can be expressed in terms of the same parametric form as the |
| + // original - parameters are enclosed in square barckets. |
| + |
| + // find inverse for linear segment (if possible) |
| + float e, f; |
| + if (0.f == fn.fE) { |
| + // otherwise assume it should be 0 as it is the lower segment |
| + // as y = f is a constant function |
| + e = 0.f; |
| + f = 0.f; |
| + } else { |
| + e = 1.f / fn.fE; |
| + f = -fn.fF / fn.fE; |
| + } |
| + // find inverse for the other segment (if possible) |
| + float g, a, b, c; |
| + if (0.f == fn.fA || 0.f == fn.fG) { |
| + // otherwise assume it should be 1 as it is the top segment |
| + // as you can't invert the constant functions y = b^g + c, or y = 1 + c |
| + g = 1.f; |
| + a = 0.f; |
| + b = 0.f; |
| + c = 1.f; |
| + } else { |
| + g = 1.f / fn.fG; |
| + a = powf(1.f / fn.fA, fn.fG); |
| + b = -a * fn.fC; |
| + c = -fn.fB / fn.fA; |
| + } |
| + const float d = fn.fE * fn.fD + fn.fF; |
| + return {g, a, b, c, d, e, f}; |
| +} |
| + |
| +#define M(stage) stage, |
| +static const SkRasterPipeline::StockStage param_gamma_stages[] = { |
| + DECLARE_GAMMA(M, SkRasterPipeline::param_gamma) |
| +}; |
| +static const SkRasterPipeline::StockStage table_gamma_stages[] = { |
| + DECLARE_GAMMA(M, SkRasterPipeline::table_gamma) |
| +}; |
| +#undef M |
| + |
| +SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace, |
| + SkColorSpace_XYZ* dstSpace) |
| + : fLinearDstGamma(kLinear_SkGammaNamed == dstSpace->gammaNamed()) { |
| +#if (SkCSXformPrintfDefined) |
|
msarett1
2016/11/09 00:01:05
How extremely helpful is this for debugging?
msarett1
2016/11/11 14:36:51
The reason I ask is that we like to avoid #if code
|
| + static const char* debugGammaNamed[4] = { |
| + "Linear", "SRGB", "2.2", "NonStandard" |
| + }; |
| + static const char* debugGammas[5] = { |
| + "None", "Named", "Value", "Table", "Param" |
| + }; |
| +#endif |
| + // add in all device -> PCS xforms |
| + for (size_t i = 0; i < srcSpace->count(); ++i) { |
|
msarett1
2016/11/09 00:01:05
nit: Count with ints
raftias
2016/11/10 21:36:06
Done. (forgot to upload the file though - I'll do
|
| + const SkColorSpace_A2B::Element& e = srcSpace->element(i); |
| + switch (e.type()) { |
| + case SkColorSpace_A2B::Element::Type::kGammaNamed: |
| + if (kLinear_SkGammaNamed != e.gammaNamed()) { |
| + SkCSXformPrintf("Gamma stage added: %s\n", |
| + debugGammaNamed[(int)e.gammaNamed()]); |
| + fParametricGammas.push_front(gammanamed_to_parametric(e.gammaNamed())); |
| + for (int channel = 0; channel < 3; ++channel) { |
| + fElementsPipeline.append(param_gamma_stages[channel], &fParametricGammas.front()); |
|
msarett1
2016/11/09 00:01:05
nit: 100 chars
|
| + } |
| + } |
| + break; |
| + case SkColorSpace_A2B::Element::Type::kGammas: { |
|
msarett1
2016/11/09 00:01:04
I think it might be possible to make the table cas
raftias
2016/11/10 21:36:06
Done.
|
| + const SkGammas& gammas = e.gammas(); |
| + SkCSXformPrintf("Adding gamme stage:"); |
|
msarett1
2016/11/09 00:01:04
nit: spelling
raftias
2016/11/10 21:36:06
Acknowledged.
|
| + for (int channel = 0; channel < 3; ++channel) { |
| + SkCSXformPrintf(" %s", debugGammas[(int)gammas.type(channel)]); |
| + } |
| + SkCSXformPrintf("\n"); |
| + float* firstDstTable = nullptr; |
| + for (int channel = 0; channel < 3; ++channel) { |
| + if (SkGammas::Type::kTable_Type == gammas.type(channel)) { |
| + float* dstTable; |
| + if (channel > 0 && SkGammas::Type::kTable_Type == gammas.type(0) && |
| + gammas.table(0) == gammas.table(channel)) { |
| + SkASSERT(firstDstTable != nullptr); |
| + dstTable = firstDstTable; |
| + } else { |
| + const float* inTable = gammas.table(channel); |
| + const int inTableSize = gammas.data(channel).fTable.fSize; |
| + fTableGammas.push_front(std::vector<float>(kDstGammaTableSize)); |
| + dstTable = fTableGammas.front().data(); |
| + if (0 == channel) { |
| + firstDstTable = dstTable; |
| + } |
| + if (kDstGammaTableSize == inTableSize) { |
| + memcpy(dstTable, inTable, sizeof(float) * kDstGammaTableSize); |
| + } else { |
| + const float tableStep = 1.f / (float)(kDstGammaTableSize - 1); |
| + float* ptr = dstTable; |
| + for (float x = 0.0f; x <= 1.0f; x += tableStep) { |
| + *(ptr++) = interp_lut(x, inTable, inTableSize); |
| + } |
| + } |
| + } |
| + fElementsPipeline.append(table_gamma_stages[channel], dstTable); |
| + } else { |
| + fParametricGammas.push_front(gamma_to_parametric(gammas, channel)); |
| + fElementsPipeline.append(param_gamma_stages[channel], |
| + &fParametricGammas.front()); |
| + } |
| + } |
| + } |
| + break; |
| + case SkColorSpace_A2B::Element::Type::kCLUT: |
| + SkCSXformPrintf("CLUT stage added [%d][%d][%d]\n", e.colorLUT().fGridPoints[0], |
| + e.colorLUT().fGridPoints[1], e.colorLUT().fGridPoints[2]); |
| + fCLUTs.push_front(sk_ref_sp(&e.colorLUT())); |
| + fElementsPipeline.append(SkRasterPipeline::clut, fCLUTs.front().get()); |
| + break; |
| + case SkColorSpace_A2B::Element::Type::kMatrix: |
| + if (!e.matrix().isIdentity()) { |
| + SkCSXformPrintf("Matrix stage added\n"); |
| + fMatrices.push_front(e.matrix()); |
| + fElementsPipeline.append(SkRasterPipeline::matrix_4x4, &fMatrices.front()); |
| + } |
| + break; |
| + } |
| + } |
| + |
| + // Lab PCS -> XYZ PCS |
| + if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) { |
| + SkCSXformPrintf("Lab -> XYZ element added\n"); |
| + fElementsPipeline.append(SkRasterPipeline::labtoxyz); |
| + } |
| + |
| + // and XYZ PCS -> device xforms |
| + if (!dstSpace->fromXYZD50()->isIdentity()) { |
| + fMatrices.push_front(*dstSpace->fromXYZD50()); |
| + fElementsPipeline.append(SkRasterPipeline::matrix_4x4, &fMatrices.front()); |
| + } |
| + |
| + if (!fLinearDstGamma) { |
| + if (kNonStandard_SkGammaNamed != dstSpace->gammaNamed()) { |
|
msarett1
2016/11/09 00:01:04
Is there some refactoring you can do to make this
raftias
2016/11/10 21:36:05
I had an addGamma() method that would add the stor
msarett1
2016/11/11 14:36:51
Sorry my comment was unclear - please ignore
|
| + fParametricGammas.push_front( |
| + invert_parametric(gammanamed_to_parametric(dstSpace->gammaNamed()))); |
|
msarett1
2016/11/09 00:01:04
I have confidence that this is much, much better t
raftias
2016/11/10 21:36:06
And use tables instead? The code before only evalu
msarett1
2016/11/11 14:36:51
That is what I meant, but I think you're right, be
|
| + for (int channel = 0; channel < 3; ++channel) { |
| + fElementsPipeline.append(param_gamma_stages[channel], &fParametricGammas.front()); |
| + } |
| + } else { |
| + for (int channel = 0; channel < 3; ++channel) { |
| + const SkGammas& gammas = *dstSpace->gammas(); |
| + if (SkGammas::Type::kTable_Type == gammas.type(channel)) { |
| + fTableGammas.push_front(std::vector<float>(kDstGammaTableSize)); |
| + float* dstTable = fTableGammas.front().data(); |
| + for (int i = 0; i < kDstGammaTableSize; ++i) { |
| + const float x = ((float) i) * (1.0f / ((float) (kDstGammaTableSize - 1))); |
| + const float y = inverse_interp_lut(x, gammas.table(channel), |
| + gammas.data(channel).fTable.fSize); |
| + dstTable[i] = y; |
| + } |
| + fElementsPipeline.append(table_gamma_stages[channel], dstTable); |
| + } else { |
| + fParametricGammas.push_front( |
| + invert_parametric(gamma_to_parametric(gammas, channel))); |
| + fElementsPipeline.append(param_gamma_stages[channel], &fParametricGammas.front()); |
|
msarett1
2016/11/09 00:01:04
nit: 100 chars
|
| + } |
| + } |
| + } |
| + } |
| +} |
| + |
| + |