Chromium Code Reviews| Index: src/core/SkColorSpaceXform.cpp |
| diff --git a/src/core/SkColorSpaceXform.cpp b/src/core/SkColorSpaceXform.cpp |
| index cb95c2999d027993521319f9c7945c1a8074c6e1..56789e5b0b5704240a1c6568081101d332a79220 100644 |
| --- a/src/core/SkColorSpaceXform.cpp |
| +++ b/src/core/SkColorSpaceXform.cpp |
| @@ -10,144 +10,7 @@ |
| #include "SkColorSpaceXform.h" |
| #include "SkOpts.h" |
| -static inline bool compute_gamut_xform(SkMatrix44* srcToDst, const SkMatrix44& srcToXYZ, |
| - const SkMatrix44& dstToXYZ) { |
| - if (!dstToXYZ.invert(srcToDst)) { |
| - return false; |
| - } |
| - |
| - srcToDst->postConcat(srcToXYZ); |
| - return true; |
| -} |
| - |
| -std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpace>& srcSpace, |
| - const sk_sp<SkColorSpace>& dstSpace) { |
| - if (!srcSpace || !dstSpace) { |
| - // Invalid input |
| - return nullptr; |
| - } |
| - |
| - if (as_CSB(dstSpace)->colorLUT()) { |
| - // It would be really weird for a dst profile to have a color LUT. I don't think |
| - // we need to support this. |
| - return nullptr; |
| - } |
| - |
| - SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); |
| - if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) { |
| - return nullptr; |
| - } |
| - |
| - if (0.0f == srcToDst.getFloat(3, 0) && |
| - 0.0f == srcToDst.getFloat(3, 1) && |
| - 0.0f == srcToDst.getFloat(3, 2) && |
| - !as_CSB(srcSpace)->colorLUT()) |
| - { |
| - switch (srcSpace->gammaNamed()) { |
| - case SkColorSpace::kSRGB_GammaNamed: |
| - if (SkColorSpace::kSRGB_GammaNamed == dstSpace->gammaNamed()) { |
| - return std::unique_ptr<SkColorSpaceXform>( |
| - new SkFastXform<SkColorSpace::kSRGB_GammaNamed, |
| - SkColorSpace::kSRGB_GammaNamed>(srcToDst)); |
| - } else if (SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gammaNamed()) { |
| - return std::unique_ptr<SkColorSpaceXform>( |
| - new SkFastXform<SkColorSpace::kSRGB_GammaNamed, |
| - SkColorSpace::k2Dot2Curve_GammaNamed>(srcToDst)); |
| - } |
| - break; |
| - case SkColorSpace::k2Dot2Curve_GammaNamed: |
| - if (SkColorSpace::kSRGB_GammaNamed == dstSpace->gammaNamed()) { |
| - return std::unique_ptr<SkColorSpaceXform>( |
| - new SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed, |
| - SkColorSpace::kSRGB_GammaNamed>(srcToDst)); |
| - } else if (SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gammaNamed()) { |
| - return std::unique_ptr<SkColorSpaceXform>( |
| - new SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed, |
| - SkColorSpace::k2Dot2Curve_GammaNamed>(srcToDst)); |
| - } |
| - break; |
| - default: |
| - break; |
| - } |
| - } |
| - |
| - return std::unique_ptr<SkColorSpaceXform>(new SkDefaultXform(srcSpace, srcToDst, dstSpace)); |
| -} |
| - |
| -/////////////////////////////////////////////////////////////////////////////////////////////////// |
| - |
| -static void build_src_to_dst(float srcToDstArray[12], const SkMatrix44& srcToDstMatrix) { |
| - // Build the following row major matrix: |
| - // rX gX bX 0 |
| - // rY gY bY 0 |
| - // rZ gZ bZ 0 |
| - // Swap R and B if necessary to make sure that we output SkPMColor order. |
| -#ifdef SK_PMCOLOR_IS_BGRA |
| - srcToDstArray[0] = srcToDstMatrix.getFloat(0, 2); |
| - srcToDstArray[1] = srcToDstMatrix.getFloat(0, 1); |
| - srcToDstArray[2] = srcToDstMatrix.getFloat(0, 0); |
| - srcToDstArray[3] = 0.0f; |
| - srcToDstArray[4] = srcToDstMatrix.getFloat(1, 2); |
| - srcToDstArray[5] = srcToDstMatrix.getFloat(1, 1); |
| - srcToDstArray[6] = srcToDstMatrix.getFloat(1, 0); |
| - srcToDstArray[7] = 0.0f; |
| - srcToDstArray[8] = srcToDstMatrix.getFloat(2, 2); |
| - srcToDstArray[9] = srcToDstMatrix.getFloat(2, 1); |
| - srcToDstArray[10] = srcToDstMatrix.getFloat(2, 0); |
| - srcToDstArray[11] = 0.0f; |
| -#else |
| - srcToDstArray[0] = srcToDstMatrix.getFloat(0, 0); |
| - srcToDstArray[1] = srcToDstMatrix.getFloat(0, 1); |
| - srcToDstArray[2] = srcToDstMatrix.getFloat(0, 2); |
| - srcToDstArray[3] = 0.0f; |
| - srcToDstArray[4] = srcToDstMatrix.getFloat(1, 0); |
| - srcToDstArray[5] = srcToDstMatrix.getFloat(1, 1); |
| - srcToDstArray[6] = srcToDstMatrix.getFloat(1, 2); |
| - srcToDstArray[7] = 0.0f; |
| - srcToDstArray[8] = srcToDstMatrix.getFloat(2, 0); |
| - srcToDstArray[9] = srcToDstMatrix.getFloat(2, 1); |
| - srcToDstArray[10] = srcToDstMatrix.getFloat(2, 2); |
| - srcToDstArray[11] = 0.0f; |
| -#endif |
| -} |
| - |
| -template <SkColorSpace::GammaNamed Src, SkColorSpace::GammaNamed Dst> |
| -SkFastXform<Src, Dst>::SkFastXform(const SkMatrix44& srcToDst) |
| -{ |
| - build_src_to_dst(fSrcToDst, srcToDst); |
| -} |
| - |
| -template <> |
| -void SkFastXform<SkColorSpace::kSRGB_GammaNamed, SkColorSpace::kSRGB_GammaNamed> |
| -::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const |
| -{ |
| - SkOpts::color_xform_RGB1_srgb_to_srgb(dst, src, len, fSrcToDst); |
| -} |
| - |
| -template <> |
| -void SkFastXform<SkColorSpace::kSRGB_GammaNamed, SkColorSpace::k2Dot2Curve_GammaNamed> |
| -::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const |
| -{ |
| - SkOpts::color_xform_RGB1_srgb_to_2dot2(dst, src, len, fSrcToDst); |
| -} |
| - |
| -template <> |
| -void SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed, SkColorSpace::kSRGB_GammaNamed> |
| -::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const |
| -{ |
| - SkOpts::color_xform_RGB1_2dot2_to_srgb(dst, src, len, fSrcToDst); |
| -} |
| - |
| -template <> |
| -void SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed, SkColorSpace::k2Dot2Curve_GammaNamed> |
| -::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const |
| -{ |
| - SkOpts::color_xform_RGB1_2dot2_to_2dot2(dst, src, len, fSrcToDst); |
| -} |
| - |
| -/////////////////////////////////////////////////////////////////////////////////////////////////// |
| - |
| -extern const float sk_linear_from_srgb[256] = { |
| +static constexpr float sk_linear_from_srgb[256] = { |
| 0.000000000000000000f, 0.000303526983548838f, 0.000607053967097675f, 0.000910580950646513f, |
| 0.001214107934195350f, 0.001517634917744190f, 0.001821161901293030f, 0.002124688884841860f, |
| 0.002428215868390700f, 0.002731742851939540f, 0.003034518678424960f, 0.003346535763899160f, |
| @@ -214,7 +77,7 @@ extern const float sk_linear_from_srgb[256] = { |
| 0.973445290398413000f, 0.982250550333117000f, 0.991102097113830000f, 1.000000000000000000f, |
| }; |
| -extern const float sk_linear_from_2dot2[256] = { |
| +static constexpr float sk_linear_from_2dot2[256] = { |
| 0.000000000000000000f, 0.000005077051900662f, 0.000023328004666099f, 0.000056921765712193f, |
| 0.000107187362341244f, 0.000175123977503027f, 0.000261543754548491f, 0.000367136269815943f, |
| 0.000492503787191433f, 0.000638182842167022f, 0.000804658499513058f, 0.000992374304074325f, |
| @@ -321,6 +184,18 @@ static void build_table_linear_from_gamma(float* outTable, float g, float a, flo |
| } |
| } |
| +static inline bool compute_gamut_xform(SkMatrix44* srcToDst, const SkMatrix44& srcToXYZ, |
|
msarett
2016/07/07 18:53:18
This function was moved (unchanged).
|
| + const SkMatrix44& dstToXYZ) { |
| + if (!dstToXYZ.invert(srcToDst)) { |
| + return false; |
| + } |
| + |
| + srcToDst->postConcat(srcToXYZ); |
| + return true; |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////////////////////////// |
| + |
| static constexpr uint8_t linear_to_srgb[1024] = { |
| 0, 3, 6, 10, 13, 15, 18, 20, 22, 23, 25, 27, 28, 30, 31, 32, 34, 35, |
| 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 49, 50, 51, 52, |
| @@ -539,6 +414,258 @@ static void build_table_linear_to_gamma(uint8_t* outTable, int outTableSize, flo |
| } |
| } |
| +/////////////////////////////////////////////////////////////////////////////////////////////////// |
| + |
| +std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpace>& srcSpace, |
| + const sk_sp<SkColorSpace>& dstSpace) { |
| + if (!srcSpace || !dstSpace) { |
| + // Invalid input |
| + return nullptr; |
| + } |
| + |
| + if (as_CSB(dstSpace)->colorLUT()) { |
| + // It would be really weird for a dst profile to have a color LUT. I don't think |
| + // we need to support this. |
| + return nullptr; |
| + } |
| + |
| + SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor); |
| + if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) { |
| + return nullptr; |
| + } |
| + |
| + if (0.0f == srcToDst.getFloat(3, 0) && |
| + 0.0f == srcToDst.getFloat(3, 1) && |
| + 0.0f == srcToDst.getFloat(3, 2) && |
| + !as_CSB(srcSpace)->colorLUT()) |
| + { |
| + switch (dstSpace->gammaNamed()) { |
|
msarett
2016/07/07 18:53:18
Changed to support far more "fast" xforms.
|
| + case SkColorSpace::kSRGB_GammaNamed: |
| + return std::unique_ptr<SkColorSpaceXform>( |
| + new SkFastXform<SkColorSpace::kSRGB_GammaNamed>(srcSpace, srcToDst, |
| + dstSpace)); |
| + case SkColorSpace::k2Dot2Curve_GammaNamed: |
| + return std::unique_ptr<SkColorSpaceXform>( |
| + new SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed>(srcSpace, srcToDst, |
| + dstSpace)); |
| + default: |
| + return std::unique_ptr<SkColorSpaceXform>( |
| + new SkFastXform<SkColorSpace::kNonStandard_GammaNamed>(srcSpace, srcToDst, |
| + dstSpace)); |
| + } |
| + } |
| + |
| + return std::unique_ptr<SkColorSpaceXform>(new SkDefaultXform(srcSpace, srcToDst, dstSpace)); |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////////////////////////// |
| + |
| +static void build_src_to_dst(float srcToDstArray[12], const SkMatrix44& srcToDstMatrix) { |
|
msarett
2016/07/07 18:53:18
This function was moved (unchanged). I'm hoping t
|
| + // Build the following row major matrix: |
| + // rX gX bX 0 |
| + // rY gY bY 0 |
| + // rZ gZ bZ 0 |
| + // Swap R and B if necessary to make sure that we output SkPMColor order. |
| +#ifdef SK_PMCOLOR_IS_BGRA |
| + srcToDstArray[0] = srcToDstMatrix.getFloat(0, 2); |
| + srcToDstArray[1] = srcToDstMatrix.getFloat(0, 1); |
| + srcToDstArray[2] = srcToDstMatrix.getFloat(0, 0); |
| + srcToDstArray[3] = 0.0f; |
| + srcToDstArray[4] = srcToDstMatrix.getFloat(1, 2); |
| + srcToDstArray[5] = srcToDstMatrix.getFloat(1, 1); |
| + srcToDstArray[6] = srcToDstMatrix.getFloat(1, 0); |
| + srcToDstArray[7] = 0.0f; |
| + srcToDstArray[8] = srcToDstMatrix.getFloat(2, 2); |
| + srcToDstArray[9] = srcToDstMatrix.getFloat(2, 1); |
| + srcToDstArray[10] = srcToDstMatrix.getFloat(2, 0); |
| + srcToDstArray[11] = 0.0f; |
| +#else |
| + srcToDstArray[0] = srcToDstMatrix.getFloat(0, 0); |
| + srcToDstArray[1] = srcToDstMatrix.getFloat(0, 1); |
| + srcToDstArray[2] = srcToDstMatrix.getFloat(0, 2); |
| + srcToDstArray[3] = 0.0f; |
| + srcToDstArray[4] = srcToDstMatrix.getFloat(1, 0); |
| + srcToDstArray[5] = srcToDstMatrix.getFloat(1, 1); |
| + srcToDstArray[6] = srcToDstMatrix.getFloat(1, 2); |
| + srcToDstArray[7] = 0.0f; |
| + srcToDstArray[8] = srcToDstMatrix.getFloat(2, 0); |
| + srcToDstArray[9] = srcToDstMatrix.getFloat(2, 1); |
| + srcToDstArray[10] = srcToDstMatrix.getFloat(2, 2); |
| + srcToDstArray[11] = 0.0f; |
| +#endif |
| +} |
| + |
| +template <SkColorSpace::GammaNamed Dst> |
| +SkFastXform<Dst>::SkFastXform(const sk_sp<SkColorSpace>& srcSpace, const SkMatrix44& srcToDst, |
| + const sk_sp<SkColorSpace>& dstSpace) |
| +{ |
| + build_src_to_dst(fSrcToDst, srcToDst); |
| + |
| + // Build tables to transform src gamma to linear. |
|
msarett
2016/07/07 18:53:18
This code is duplicated from SkDefaultXform. I'm
|
| + 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: |
| + 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 <> |
| +void SkFastXform<SkColorSpace::kSRGB_GammaNamed> |
|
msarett
2016/07/07 18:53:18
Now just templated on the dst gamma.
|
| +::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const |
| +{ |
| + SkOpts::color_xform_RGB1_to_srgb(dst, src, len, fSrcGammaTables, fSrcToDst); |
| +} |
| + |
| +template <> |
| +void SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed> |
| +::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const |
| +{ |
| + SkOpts::color_xform_RGB1_to_2dot2(dst, src, len, fSrcGammaTables, fSrcToDst); |
| +} |
| + |
| +template <> |
| +void SkFastXform<SkColorSpace::kNonStandard_GammaNamed> |
| +::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const |
| +{ |
| + SkOpts::color_xform_RGB1_to_table(dst, src, len, fSrcGammaTables, fSrcToDst, nullptr); |
| +} |
| + |
| +/////////////////////////////////////////////////////////////////////////////////////////////////// |
| + |
| SkDefaultXform::SkDefaultXform(const sk_sp<SkColorSpace>& srcSpace, const SkMatrix44& srcToDst, |
| const sk_sp<SkColorSpace>& dstSpace) |
| : fColorLUT(sk_ref_sp((SkColorLookUpTable*) as_CSB(srcSpace)->colorLUT())) |