| Index: src/core/SkColorSpaceXform.cpp
|
| diff --git a/src/core/SkColorSpaceXform.cpp b/src/core/SkColorSpaceXform.cpp
|
| index 80835f713af94f3c686760ad8ca7aa02117779ed..8b23d18e1ae8015f4c8545cac9fdad55147304bb 100644
|
| --- a/src/core/SkColorSpaceXform.cpp
|
| +++ b/src/core/SkColorSpaceXform.cpp
|
| @@ -264,11 +264,11 @@
|
| }
|
| }
|
|
|
| -static void build_table_linear_to_gamma(uint8_t* outTable, float exponent) {
|
| +static void build_table_linear_to_gamma(uint8_t* outTable, int outTableSize, float exponent) {
|
| float toGammaExp = 1.0f / exponent;
|
|
|
| - for (int i = 0; i < SkDefaultXform::kDstGammaTableSize; i++) {
|
| - float x = ((float) i) * (1.0f / ((float) (SkDefaultXform::kDstGammaTableSize - 1)));
|
| + for (int i = 0; i < outTableSize; i++) {
|
| + float x = ((float) i) * (1.0f / ((float) (outTableSize - 1)));
|
| outTable[i] = clamp_normalized_float_to_byte(powf(x, toGammaExp));
|
| }
|
| }
|
| @@ -276,7 +276,7 @@
|
| // Inverse table lookup. Ex: what index corresponds to the input value? This will
|
| // have strange results when the table is non-increasing. But any sane gamma
|
| // function will be increasing.
|
| -static float inverse_interp_lut(float input, const float* table, int tableSize) {
|
| +static float inverse_interp_lut(float input, float* table, int tableSize) {
|
| if (input <= table[0]) {
|
| return table[0];
|
| } else if (input >= table[tableSize - 1]) {
|
| @@ -299,10 +299,10 @@
|
| return 0.0f;
|
| }
|
|
|
| -static void build_table_linear_to_gamma(uint8_t* outTable, const float* inTable,
|
| +static void build_table_linear_to_gamma(uint8_t* outTable, int outTableSize, float* inTable,
|
| int inTableSize) {
|
| - for (int i = 0; i < SkDefaultXform::kDstGammaTableSize; i++) {
|
| - float x = ((float) i) * (1.0f / ((float) (SkDefaultXform::kDstGammaTableSize - 1)));
|
| + for (int i = 0; i < outTableSize; i++) {
|
| + float x = ((float) i) * (1.0f / ((float) (outTableSize - 1)));
|
| float y = inverse_interp_lut(x, inTable, inTableSize);
|
| outTable[i] = clamp_normalized_float_to_byte(y);
|
| }
|
| @@ -339,108 +339,12 @@
|
| return (powf(x - c, 1.0f / g) - b) / a;
|
| }
|
|
|
| -static void build_table_linear_to_gamma(uint8_t* outTable, float g, float a,
|
| +static void build_table_linear_to_gamma(uint8_t* outTable, int outTableSize, float g, float a,
|
| float b, float c, float d, float e, float f) {
|
| - for (int i = 0; i < SkDefaultXform::kDstGammaTableSize; i++) {
|
| - float x = ((float) i) * (1.0f / ((float) (SkDefaultXform::kDstGammaTableSize - 1)));
|
| + for (int i = 0; i < outTableSize; i++) {
|
| + float x = ((float) i) * (1.0f / ((float) (outTableSize - 1)));
|
| float y = inverse_parametric(x, g, a, b, c, d, e, f);
|
| outTable[i] = clamp_normalized_float_to_byte(y);
|
| - }
|
| -}
|
| -
|
| -///////////////////////////////////////////////////////////////////////////////////////////////////
|
| -
|
| -template <typename T>
|
| -struct GammaFns {
|
| - const T* fSRGBTable;
|
| - const T* f2Dot2Table;
|
| -
|
| - void (*fBuildFromValue)(T*, float);
|
| - void (*fBuildFromTable)(T*, const float*, int);
|
| - void (*fBuildFromParam)(T*, float, float, float, float, float, float, float);
|
| -};
|
| -
|
| -static const GammaFns<float> kToLinear {
|
| - sk_linear_from_srgb,
|
| - sk_linear_from_2dot2,
|
| - &build_table_linear_from_gamma,
|
| - &build_table_linear_from_gamma,
|
| - &build_table_linear_from_gamma,
|
| -};
|
| -
|
| -static const GammaFns<uint8_t> kFromLinear {
|
| - linear_to_srgb,
|
| - linear_to_2dot2,
|
| - &build_table_linear_to_gamma,
|
| - &build_table_linear_to_gamma,
|
| - &build_table_linear_to_gamma,
|
| -};
|
| -
|
| -// Build tables to transform src gamma to linear.
|
| -template <typename T>
|
| -static void build_gamma_tables(const T* outGammaTables[3], T* gammaTableStorage, int gammaTableSize,
|
| - const sk_sp<SkColorSpace>& space, const GammaFns<T>& fns) {
|
| - switch (space->gammaNamed()) {
|
| - case SkColorSpace::kSRGB_GammaNamed:
|
| - outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = fns.fSRGBTable;
|
| - break;
|
| - case SkColorSpace::k2Dot2Curve_GammaNamed:
|
| - outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = fns.f2Dot2Table;
|
| - break;
|
| - case SkColorSpace::kLinear_GammaNamed:
|
| - (*fns.fBuildFromValue)(gammaTableStorage, 1.0f);
|
| - outGammaTables[0] = outGammaTables[1] = outGammaTables[2] = gammaTableStorage;
|
| - break;
|
| - default: {
|
| - const SkGammas* gammas = as_CSB(space)->gammas();
|
| - SkASSERT(gammas);
|
| -
|
| - for (int i = 0; i < 3; i++) {
|
| - if (i > 0) {
|
| - // Check if this curve matches the first curve. In this case, we can
|
| - // share the same table pointer. This should almost always be true.
|
| - // I've never seen a profile where all three gamma curves didn't match.
|
| - // But it is possible that they won't.
|
| - if (0 == memcmp(&gammas->data(0), &gammas->data(i), sizeof(SkGammas::Data))) {
|
| - outGammaTables[i] = outGammaTables[0];
|
| - continue;
|
| - }
|
| - }
|
| -
|
| - if (gammas->isNamed(i)) {
|
| - switch (gammas->data(i).fNamed) {
|
| - case SkColorSpace::kSRGB_GammaNamed:
|
| - outGammaTables[i] = fns.fSRGBTable;
|
| - break;
|
| - case SkColorSpace::k2Dot2Curve_GammaNamed:
|
| - outGammaTables[i] = fns.f2Dot2Table;
|
| - break;
|
| - case SkColorSpace::kLinear_GammaNamed:
|
| - (*fns.fBuildFromValue)(&gammaTableStorage[i * gammaTableSize], 1.0f);
|
| - outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
|
| - break;
|
| - default:
|
| - SkASSERT(false);
|
| - break;
|
| - }
|
| - } else if (gammas->isValue(i)) {
|
| - (*fns.fBuildFromValue)(&gammaTableStorage[i * gammaTableSize],
|
| - gammas->data(i).fValue);
|
| - outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
|
| - } else if (gammas->isTable(i)) {
|
| - (*fns.fBuildFromTable)(&gammaTableStorage[i * gammaTableSize], gammas->table(i),
|
| - gammas->data(i).fTable.fSize);
|
| - outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
|
| - } else {
|
| - SkASSERT(gammas->isParametric(i));
|
| - const SkGammas::Params& params = gammas->params(i);
|
| - (*fns.fBuildFromParam)(&gammaTableStorage[i * gammaTableSize], params.fG,
|
| - params.fA, params.fB, params.fC, params.fD, params.fE,
|
| - params.fF);
|
| - outGammaTables[i] = &gammaTableStorage[i * gammaTableSize];
|
| - }
|
| - }
|
| - }
|
| }
|
| }
|
|
|
| @@ -516,9 +420,150 @@
|
| const sk_sp<SkColorSpace>& dstSpace)
|
| {
|
| build_src_to_dst(fSrcToDst, srcToDst);
|
| - build_gamma_tables(fSrcGammaTables, fSrcGammaTableStorage, 256, srcSpace, kToLinear);
|
| - build_gamma_tables(fDstGammaTables, fDstGammaTableStorage, SkDefaultXform::kDstGammaTableSize,
|
| - dstSpace, kFromLinear);
|
| +
|
| + // Build tables to transform src gamma to linear.
|
| + switch (srcSpace->gammaNamed()) {
|
| + case SkColorSpace::kSRGB_GammaNamed:
|
| + fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = sk_linear_from_srgb;
|
| + break;
|
| + case SkColorSpace::k2Dot2Curve_GammaNamed:
|
| + fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = sk_linear_from_2dot2;
|
| + break;
|
| + case SkColorSpace::kLinear_GammaNamed:
|
| + build_table_linear_from_gamma(fSrcGammaTableStorage, 1.0f);
|
| + fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = fSrcGammaTableStorage;
|
| + break;
|
| + default: {
|
| + const SkGammas* gammas = as_CSB(srcSpace)->gammas();
|
| + SkASSERT(gammas);
|
| +
|
| + for (int i = 0; i < 3; i++) {
|
| + const SkGammaCurve& curve = (*gammas)[i];
|
| +
|
| + if (i > 0) {
|
| + // Check if this curve matches the first curve. In this case, we can
|
| + // share the same table pointer. Logically, this should almost always
|
| + // be true. I've never seen a profile where all three gamma curves
|
| + // didn't match. But it is possible that they won't.
|
| + // TODO (msarett):
|
| + // This comparison won't catch the case where each gamma curve has a
|
| + // pointer to its own look-up table, but the tables actually match.
|
| + // Should we perform a deep compare of gamma tables here? Or should
|
| + // we catch this when parsing the profile? Or should we not worry
|
| + // about a bit of redundant work?
|
| + if (curve.quickEquals((*gammas)[0])) {
|
| + fSrcGammaTables[i] = fSrcGammaTables[0];
|
| + continue;
|
| + }
|
| + }
|
| +
|
| + if (curve.isNamed()) {
|
| + switch (curve.fNamed) {
|
| + case SkColorSpace::kSRGB_GammaNamed:
|
| + fSrcGammaTables[i] = sk_linear_from_srgb;
|
| + break;
|
| + case SkColorSpace::k2Dot2Curve_GammaNamed:
|
| + fSrcGammaTables[i] = sk_linear_from_2dot2;
|
| + break;
|
| + case SkColorSpace::kLinear_GammaNamed:
|
| + build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256], 1.0f);
|
| + fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
|
| + break;
|
| + default:
|
| + SkASSERT(false);
|
| + break;
|
| + }
|
| + } else if (curve.isValue()) {
|
| + build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256], curve.fValue);
|
| + fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
|
| + } else if (curve.isTable()) {
|
| + build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256],
|
| + curve.fTable.get(), curve.fTableSize);
|
| + fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
|
| + } else {
|
| + SkASSERT(curve.isParametric());
|
| + build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256], curve.fG,
|
| + curve.fA, curve.fB, curve.fC, curve.fD, curve.fE,
|
| + curve.fF);
|
| + fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Build tables to transform linear to dst gamma.
|
| + // FIXME (msarett):
|
| + // Should we spend all of this time bulding the dst gamma tables when the client only
|
| + // wants to convert to F16?
|
| + switch (dstSpace->gammaNamed()) {
|
| + case SkColorSpace::kSRGB_GammaNamed:
|
| + case SkColorSpace::k2Dot2Curve_GammaNamed:
|
| + break;
|
| + case SkColorSpace::kLinear_GammaNamed:
|
| + build_table_linear_to_gamma(fDstGammaTableStorage, kDstGammaTableSize, 1.0f);
|
| + fDstGammaTables[0] = fDstGammaTables[1] = fDstGammaTables[2] = fDstGammaTableStorage;
|
| + break;
|
| + default: {
|
| + const SkGammas* gammas = as_CSB(dstSpace)->gammas();
|
| + SkASSERT(gammas);
|
| +
|
| + for (int i = 0; i < 3; i++) {
|
| + const SkGammaCurve& curve = (*gammas)[i];
|
| +
|
| + if (i > 0) {
|
| + // Check if this curve matches the first curve. In this case, we can
|
| + // share the same table pointer. Logically, this should almost always
|
| + // be true. I've never seen a profile where all three gamma curves
|
| + // didn't match. But it is possible that they won't.
|
| + // TODO (msarett):
|
| + // This comparison won't catch the case where each gamma curve has a
|
| + // pointer to its own look-up table (but the tables actually match).
|
| + // Should we perform a deep compare of gamma tables here? Or should
|
| + // we catch this when parsing the profile? Or should we not worry
|
| + // about a bit of redundant work?
|
| + if (curve.quickEquals((*gammas)[0])) {
|
| + fDstGammaTables[i] = fDstGammaTables[0];
|
| + continue;
|
| + }
|
| + }
|
| +
|
| + if (curve.isNamed()) {
|
| + switch (curve.fNamed) {
|
| + case SkColorSpace::kSRGB_GammaNamed:
|
| + fDstGammaTables[i] = linear_to_srgb;
|
| + break;
|
| + case SkColorSpace::k2Dot2Curve_GammaNamed:
|
| + fDstGammaTables[i] = linear_to_2dot2;
|
| + break;
|
| + case SkColorSpace::kLinear_GammaNamed:
|
| + build_table_linear_to_gamma(
|
| + &fDstGammaTableStorage[i * kDstGammaTableSize],
|
| + kDstGammaTableSize, 1.0f);
|
| + fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
|
| + break;
|
| + default:
|
| + SkASSERT(false);
|
| + break;
|
| + }
|
| + } else if (curve.isValue()) {
|
| + build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstGammaTableSize],
|
| + kDstGammaTableSize, curve.fValue);
|
| + fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
|
| + } else if (curve.isTable()) {
|
| + build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstGammaTableSize],
|
| + kDstGammaTableSize, curve.fTable.get(),
|
| + curve.fTableSize);
|
| + fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
|
| + } else {
|
| + SkASSERT(curve.isParametric());
|
| + build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstGammaTableSize],
|
| + kDstGammaTableSize, curve.fG, curve.fA, curve.fB,
|
| + curve.fC, curve.fD, curve.fE, curve.fF);
|
| + fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
|
| + }
|
| + }
|
| + }
|
| + }
|
| }
|
|
|
| template <>
|
| @@ -556,9 +601,149 @@
|
| : fColorLUT(sk_ref_sp((SkColorLookUpTable*) as_CSB(srcSpace)->colorLUT()))
|
| , fSrcToDst(srcToDst)
|
| {
|
| - build_gamma_tables(fSrcGammaTables, fSrcGammaTableStorage, 256, srcSpace, kToLinear);
|
| - build_gamma_tables(fDstGammaTables, fDstGammaTableStorage, SkDefaultXform::kDstGammaTableSize,
|
| - dstSpace, kFromLinear);
|
| + // Build tables to transform src gamma to linear.
|
| + switch (srcSpace->gammaNamed()) {
|
| + case SkColorSpace::kSRGB_GammaNamed:
|
| + fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = sk_linear_from_srgb;
|
| + break;
|
| + case SkColorSpace::k2Dot2Curve_GammaNamed:
|
| + fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = sk_linear_from_2dot2;
|
| + break;
|
| + case SkColorSpace::kLinear_GammaNamed:
|
| + build_table_linear_from_gamma(fSrcGammaTableStorage, 1.0f);
|
| + fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = fSrcGammaTableStorage;
|
| + break;
|
| + default: {
|
| + const SkGammas* gammas = as_CSB(srcSpace)->gammas();
|
| + SkASSERT(gammas);
|
| +
|
| + for (int i = 0; i < 3; i++) {
|
| + const SkGammaCurve& curve = (*gammas)[i];
|
| +
|
| + if (i > 0) {
|
| + // Check if this curve matches the first curve. In this case, we can
|
| + // share the same table pointer. Logically, this should almost always
|
| + // be true. I've never seen a profile where all three gamma curves
|
| + // didn't match. But it is possible that they won't.
|
| + // TODO (msarett):
|
| + // This comparison won't catch the case where each gamma curve has a
|
| + // pointer to its own look-up table, but the tables actually match.
|
| + // Should we perform a deep compare of gamma tables here? Or should
|
| + // we catch this when parsing the profile? Or should we not worry
|
| + // about a bit of redundant work?
|
| + if (curve.quickEquals((*gammas)[0])) {
|
| + fSrcGammaTables[i] = fSrcGammaTables[0];
|
| + continue;
|
| + }
|
| + }
|
| +
|
| + if (curve.isNamed()) {
|
| + switch (curve.fNamed) {
|
| + case SkColorSpace::kSRGB_GammaNamed:
|
| + fSrcGammaTables[i] = sk_linear_from_srgb;
|
| + break;
|
| + case SkColorSpace::k2Dot2Curve_GammaNamed:
|
| + fSrcGammaTables[i] = sk_linear_from_2dot2;
|
| + break;
|
| + case SkColorSpace::kLinear_GammaNamed:
|
| + build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256], 1.0f);
|
| + fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
|
| + break;
|
| + default:
|
| + SkASSERT(false);
|
| + break;
|
| + }
|
| + } else if (curve.isValue()) {
|
| + build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256], curve.fValue);
|
| + fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
|
| + } else if (curve.isTable()) {
|
| + build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256],
|
| + curve.fTable.get(), curve.fTableSize);
|
| + fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
|
| + } else {
|
| + SkASSERT(curve.isParametric());
|
| + build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256], curve.fG,
|
| + curve.fA, curve.fB, curve.fC, curve.fD, curve.fE,
|
| + curve.fF);
|
| + fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
|
| + }
|
| + }
|
| + }
|
| + }
|
| +
|
| + // Build tables to transform linear to dst gamma.
|
| + switch (dstSpace->gammaNamed()) {
|
| + case SkColorSpace::kSRGB_GammaNamed:
|
| + fDstGammaTables[0] = fDstGammaTables[1] = fDstGammaTables[2] = linear_to_srgb;
|
| + break;
|
| + case SkColorSpace::k2Dot2Curve_GammaNamed:
|
| + fDstGammaTables[0] = fDstGammaTables[1] = fDstGammaTables[2] = linear_to_2dot2;
|
| + break;
|
| + case SkColorSpace::kLinear_GammaNamed:
|
| + build_table_linear_to_gamma(fDstGammaTableStorage, kDstGammaTableSize, 1.0f);
|
| + fDstGammaTables[0] = fDstGammaTables[1] = fDstGammaTables[2] = fDstGammaTableStorage;
|
| + break;
|
| + default: {
|
| + const SkGammas* gammas = as_CSB(dstSpace)->gammas();
|
| + SkASSERT(gammas);
|
| +
|
| + for (int i = 0; i < 3; i++) {
|
| + const SkGammaCurve& curve = (*gammas)[i];
|
| +
|
| + if (i > 0) {
|
| + // Check if this curve matches the first curve. In this case, we can
|
| + // share the same table pointer. Logically, this should almost always
|
| + // be true. I've never seen a profile where all three gamma curves
|
| + // didn't match. But it is possible that they won't.
|
| + // TODO (msarett):
|
| + // This comparison won't catch the case where each gamma curve has a
|
| + // pointer to its own look-up table (but the tables actually match).
|
| + // Should we perform a deep compare of gamma tables here? Or should
|
| + // we catch this when parsing the profile? Or should we not worry
|
| + // about a bit of redundant work?
|
| + if (curve.quickEquals((*gammas)[0])) {
|
| + fDstGammaTables[i] = fDstGammaTables[0];
|
| + continue;
|
| + }
|
| + }
|
| +
|
| + if (curve.isNamed()) {
|
| + switch (curve.fNamed) {
|
| + case SkColorSpace::kSRGB_GammaNamed:
|
| + fDstGammaTables[i] = linear_to_srgb;
|
| + break;
|
| + case SkColorSpace::k2Dot2Curve_GammaNamed:
|
| + fDstGammaTables[i] = linear_to_2dot2;
|
| + break;
|
| + case SkColorSpace::kLinear_GammaNamed:
|
| + build_table_linear_to_gamma(
|
| + &fDstGammaTableStorage[i * kDstGammaTableSize],
|
| + kDstGammaTableSize, 1.0f);
|
| + fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
|
| + break;
|
| + default:
|
| + SkASSERT(false);
|
| + break;
|
| + }
|
| + } else if (curve.isValue()) {
|
| + build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstGammaTableSize],
|
| + kDstGammaTableSize, curve.fValue);
|
| + fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
|
| + } else if (curve.isTable()) {
|
| + build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstGammaTableSize],
|
| + kDstGammaTableSize, curve.fTable.get(),
|
| + curve.fTableSize);
|
| + fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
|
| + } else {
|
| + SkASSERT(curve.isParametric());
|
| + build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstGammaTableSize],
|
| + kDstGammaTableSize, curve.fG, curve.fA, curve.fB,
|
| + curve.fC, curve.fD, curve.fE, curve.fF);
|
| + fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
|
| + }
|
| + }
|
| + }
|
| + }
|
| }
|
|
|
| static float byte_to_float(uint8_t byte) {
|
|
|