diff options
-rw-r--r-- | bench/ColorCodecBench.cpp | 10 | ||||
-rw-r--r-- | dm/DM.cpp | 3 | ||||
-rw-r--r-- | dm/DMSrcSink.cpp | 5 | ||||
-rw-r--r-- | dm/DMSrcSink.h | 4 | ||||
-rw-r--r-- | src/core/SkColorSpaceXform.cpp | 60 | ||||
-rw-r--r-- | src/core/SkColorSpaceXform.h | 18 | ||||
-rw-r--r-- | src/core/SkOpts.cpp | 4 | ||||
-rw-r--r-- | src/core/SkOpts.h | 4 | ||||
-rw-r--r-- | src/opts/SkColorXform_opts.h | 44 | ||||
-rw-r--r-- | src/opts/SkNx_sse.h | 4 | ||||
-rw-r--r-- | src/opts/SkOpts_sse41.cpp | 2 |
11 files changed, 117 insertions, 41 deletions
diff --git a/bench/ColorCodecBench.cpp b/bench/ColorCodecBench.cpp index 621fc4191e..9dde5da813 100644 --- a/bench/ColorCodecBench.cpp +++ b/bench/ColorCodecBench.cpp @@ -12,9 +12,10 @@ #include "SkCommandLineFlags.h" #if defined(SK_TEST_QCMS) -DEFINE_bool(qcms, false, "Bench qcms color conversion"); +DEFINE_bool(qcms, false, "Bench qcms color conversion"); #endif DEFINE_bool(xform_only, false, "Only time the color xform, do not include the decode time"); +DEFINE_bool(srgb, false, "Convert to srgb dst space"); ColorCodecBench::ColorCodecBench(const char* name, sk_sp<SkData> encoded) : fEncoded(std::move(encoded)) @@ -170,7 +171,9 @@ void ColorCodecBench::onDelayedSetup() { #if defined(SK_TEST_QCMS) if (FLAGS_qcms) { - fDstSpaceQCMS.reset(qcms_profile_from_memory(dstData->data(), dstData->size())); + fDstSpaceQCMS.reset(FLAGS_srgb ? + qcms_profile_sRGB() : + qcms_profile_from_memory(dstData->data(), dstData->size())); SkASSERT(fDstSpaceQCMS); // This call takes a non-trivial amount of time, but I think it's the most fair to @@ -179,7 +182,8 @@ void ColorCodecBench::onDelayedSetup() { } else #endif { - fDstSpace = SkColorSpace::NewICC(dstData->data(), dstData->size()); + fDstSpace = FLAGS_srgb ? SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named) : + SkColorSpace::NewICC(dstData->data(), dstData->size()); SkASSERT(fDstSpace); } } @@ -747,6 +747,9 @@ static bool gather_srcs() { src = new ColorCodecSrc(colorImage, ColorCodecSrc::kDst_HPZR30w_Mode); push_src("image", "color_codec_HPZR30w", src); + src = new ColorCodecSrc(colorImage, ColorCodecSrc::kDst_sRGB_Mode); + push_src("image", "color_codec_sRGB", src); + #if defined(SK_TEST_QCMS) src = new ColorCodecSrc(colorImage, ColorCodecSrc::kQCMS_HPZR30w_Mode); push_src("image", "color_codec_QCMS_HPZR30w", src); diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp index 97f1e48664..8afbfb9f2f 100644 --- a/dm/DMSrcSink.cpp +++ b/dm/DMSrcSink.cpp @@ -886,9 +886,12 @@ Error ColorCodecSrc::draw(SkCanvas* canvas) const { case kBaseline_Mode: canvas->drawBitmap(bitmap, 0, 0); break; + case kDst_sRGB_Mode: case kDst_HPZR30w_Mode: { sk_sp<SkColorSpace> srcSpace = sk_ref_sp(codec->getColorSpace()); - sk_sp<SkColorSpace> dstSpace = SkColorSpace::NewICC(dstData->data(), dstData->size()); + sk_sp<SkColorSpace> dstSpace = (kDst_sRGB_Mode == fMode) ? + SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named) : + SkColorSpace::NewICC(dstData->data(), dstData->size()); SkASSERT(dstSpace); std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New(srcSpace, dstSpace); diff --git a/dm/DMSrcSink.h b/dm/DMSrcSink.h index a2bfbca7f3..be7d68e638 100644 --- a/dm/DMSrcSink.h +++ b/dm/DMSrcSink.h @@ -215,10 +215,10 @@ public: // monitor, you're in luck! The unmarked outputs of this test should display // correctly on this monitor in the Chrome browser. If not, it's useful to know // that this monitor has a profile that is fairly similar to Adobe RGB. - // TODO (msarett): Should we add a new test with a new monitor and verify that outputs - // look identical on two different dsts? kDst_HPZR30w_Mode, + kDst_sRGB_Mode, + #if defined(SK_TEST_QCMS) // Use QCMS for color correction. kQCMS_HPZR30w_Mode, diff --git a/src/core/SkColorSpaceXform.cpp b/src/core/SkColorSpaceXform.cpp index 4c67e8d3f0..f42811a549 100644 --- a/src/core/SkColorSpaceXform.cpp +++ b/src/core/SkColorSpaceXform.cpp @@ -37,15 +37,35 @@ std::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpa return nullptr; } - if (SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gammaNamed() && - 0.0f == srcToDst.getFloat(3, 0) && + if (0.0f == srcToDst.getFloat(3, 0) && 0.0f == srcToDst.getFloat(3, 1) && 0.0f == srcToDst.getFloat(3, 2)) { - if (SkColorSpace::kSRGB_GammaNamed == srcSpace->gammaNamed()) { - return std::unique_ptr<SkColorSpaceXform>(new SkSRGBTo2Dot2Xform(srcToDst)); - } else if (SkColorSpace::k2Dot2Curve_GammaNamed == srcSpace->gammaNamed()) { - return std::unique_ptr<SkColorSpaceXform>(new Sk2Dot2To2Dot2Xform(srcToDst)); + 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; } } @@ -90,23 +110,37 @@ static void build_src_to_dst(float srcToDstArray[12], const SkMatrix44& srcToDst #endif } -SkSRGBTo2Dot2Xform::SkSRGBTo2Dot2Xform(const SkMatrix44& srcToDst) +template <SkColorSpace::GammaNamed Src, SkColorSpace::GammaNamed Dst> +SkFastXform<Src, Dst>::SkFastXform(const SkMatrix44& srcToDst) { build_src_to_dst(fSrcToDst, srcToDst); } -void SkSRGBTo2Dot2Xform::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::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); +} -Sk2Dot2To2Dot2Xform::Sk2Dot2To2Dot2Xform(const SkMatrix44& srcToDst) +template <> +void SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed, SkColorSpace::kSRGB_GammaNamed> +::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const { - build_src_to_dst(fSrcToDst, srcToDst); + SkOpts::color_xform_RGB1_2dot2_to_srgb(dst, src, len, fSrcToDst); } -void Sk2Dot2To2Dot2Xform::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const { +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); } diff --git a/src/core/SkColorSpaceXform.h b/src/core/SkColorSpaceXform.h index 1ea608094f..e6abdbb992 100644 --- a/src/core/SkColorSpaceXform.h +++ b/src/core/SkColorSpaceXform.h @@ -34,26 +34,14 @@ public: virtual ~SkColorSpaceXform() {} }; -class SkSRGBTo2Dot2Xform : public SkColorSpaceXform { +template <SkColorSpace::GammaNamed Src, SkColorSpace::GammaNamed Dst> +class SkFastXform : public SkColorSpaceXform { public: void xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const override; private: - SkSRGBTo2Dot2Xform(const SkMatrix44& srcToDst); - - float fSrcToDst[12]; - - friend class SkColorSpaceXform; -}; - -class Sk2Dot2To2Dot2Xform : public SkColorSpaceXform { -public: - - void xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const override; - -private: - Sk2Dot2To2Dot2Xform(const SkMatrix44& srcToDst); + SkFastXform(const SkMatrix44& srcToDst); float fSrcToDst[12]; diff --git a/src/core/SkOpts.cpp b/src/core/SkOpts.cpp index b4145acb7a..a2de1f59d2 100644 --- a/src/core/SkOpts.cpp +++ b/src/core/SkOpts.cpp @@ -81,6 +81,10 @@ namespace SkOpts { sk_default::color_xform_RGB1_srgb_to_2dot2; decltype(color_xform_RGB1_2dot2_to_2dot2) color_xform_RGB1_2dot2_to_2dot2 = sk_default::color_xform_RGB1_2dot2_to_2dot2; + decltype(color_xform_RGB1_srgb_to_srgb) color_xform_RGB1_srgb_to_srgb = + sk_default::color_xform_RGB1_srgb_to_srgb; + decltype(color_xform_RGB1_2dot2_to_srgb) color_xform_RGB1_2dot2_to_srgb = + sk_default::color_xform_RGB1_2dot2_to_srgb; // Each Init_foo() is defined in src/opts/SkOpts_foo.cpp. void Init_ssse3(); diff --git a/src/core/SkOpts.h b/src/core/SkOpts.h index 1c33529d52..186fa73d4c 100644 --- a/src/core/SkOpts.h +++ b/src/core/SkOpts.h @@ -74,6 +74,10 @@ namespace SkOpts { const float srcToDstMatrix[16]); extern void (*color_xform_RGB1_2dot2_to_2dot2)(uint32_t* dst, const uint32_t* src, int len, const float srcToDstMatrix[16]); + extern void (*color_xform_RGB1_srgb_to_srgb) (uint32_t* dst, const uint32_t* src, int len, + const float srcToDstMatrix[16]); + extern void (*color_xform_RGB1_2dot2_to_srgb)(uint32_t* dst, const uint32_t* src, int len, + const float srcToDstMatrix[16]); } #endif//SkOpts_DEFINED diff --git a/src/opts/SkColorXform_opts.h b/src/opts/SkColorXform_opts.h index 5af5c2aed7..2c14c80283 100644 --- a/src/opts/SkColorXform_opts.h +++ b/src/opts/SkColorXform_opts.h @@ -157,13 +157,33 @@ static Sk4f linear_to_2dot2(const Sk4f& x) { return 255.0f * x2.invert() * x32 * x64.invert(); } +static Sk4f linear_to_srgb(const Sk4f& x) { + // Approximation of the sRGB gamma curve (within 1 when scaled to 8-bit pixels). + // For 0.00000f <= x < 0.00349f, 12.92 * x + // For 0.00349f <= x <= 1.00000f, 0.679*(x.^0.5) + 0.423*x.^(0.25) - 0.101 + // Note that 0.00349 was selected because it is a point where both functions produce the + // same pixel value when rounded. + auto rsqrt = x.rsqrt(), + sqrt = rsqrt.invert(), + ftrt = rsqrt.rsqrt(); + + auto hi = (-0.101115084998961f * 255.0f) + + (+0.678513029959381f * 255.0f) * sqrt + + (+0.422602055039580f * 255.0f) * ftrt; + + auto lo = (12.92f * 255.0f) * x; + + auto mask = (x < 0.00349f); + return mask.thenElse(lo, hi); +} + static Sk4f clamp_0_to_255(const Sk4f& x) { // The order of the arguments is important here. We want to make sure that NaN // clamps to zero. Note that max(NaN, 0) = 0, while max(0, NaN) = NaN. return Sk4f::Min(Sk4f::Max(x, 0.0f), 255.0f); } -template <const float (&linear_from_curve)[256]> +template <const float (&linear_from_curve)[256], Sk4f (*linear_to_curve)(const Sk4f&)> static void color_xform_RGB1(uint32_t* dst, const uint32_t* src, int len, const float matrix[16]) { // Load transformation matrix. @@ -192,9 +212,9 @@ static void color_xform_RGB1(uint32_t* dst, const uint32_t* src, int len, dstBlues = rXgXbX[2]*reds + rYgYbY[2]*greens + rZgZbZ[2]*blues; // Convert to dst gamma. - dstReds = linear_to_2dot2(dstReds); - dstGreens = linear_to_2dot2(dstGreens); - dstBlues = linear_to_2dot2(dstBlues); + dstReds = linear_to_curve(dstReds); + dstGreens = linear_to_curve(dstGreens); + dstBlues = linear_to_curve(dstBlues); // Clamp floats to byte range. dstReds = clamp_0_to_255(dstReds); @@ -223,7 +243,7 @@ static void color_xform_RGB1(uint32_t* dst, const uint32_t* src, int len, auto dstPixel = rXgXbX*r + rYgYbY*g + rZgZbZ*b; // Convert to dst gamma. - dstPixel = linear_to_2dot2(dstPixel); + dstPixel = linear_to_curve(dstPixel); // Clamp floats to byte range. dstPixel = clamp_0_to_255(dstPixel); @@ -242,12 +262,22 @@ static void color_xform_RGB1(uint32_t* dst, const uint32_t* src, int len, static void color_xform_RGB1_srgb_to_2dot2(uint32_t* dst, const uint32_t* src, int len, const float matrix[16]) { - color_xform_RGB1<linear_from_srgb>(dst, src, len, matrix); + color_xform_RGB1<linear_from_srgb, linear_to_2dot2>(dst, src, len, matrix); } static void color_xform_RGB1_2dot2_to_2dot2(uint32_t* dst, const uint32_t* src, int len, const float matrix[16]) { - color_xform_RGB1<linear_from_2dot2>(dst, src, len, matrix); + color_xform_RGB1<linear_from_2dot2, linear_to_2dot2>(dst, src, len, matrix); +} + +static void color_xform_RGB1_srgb_to_srgb(uint32_t* dst, const uint32_t* src, int len, + const float matrix[16]) { + color_xform_RGB1<linear_from_srgb, linear_to_srgb>(dst, src, len, matrix); +} + +static void color_xform_RGB1_2dot2_to_srgb(uint32_t* dst, const uint32_t* src, int len, + const float matrix[16]) { + color_xform_RGB1<linear_from_2dot2, linear_to_srgb>(dst, src, len, matrix); } } // namespace SK_OPTS_NS diff --git a/src/opts/SkNx_sse.h b/src/opts/SkNx_sse.h index bdc6d77ce7..9751c4db91 100644 --- a/src/opts/SkNx_sse.h +++ b/src/opts/SkNx_sse.h @@ -124,8 +124,12 @@ public: bool anyTrue() const { return 0x0000 != _mm_movemask_epi8(_mm_castps_si128(fVec)); } SkNx thenElse(const SkNx& t, const SkNx& e) const { +#if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSE41 + return _mm_blendv_ps(e.fVec, t.fVec, fVec); +#else return _mm_or_ps(_mm_and_ps (fVec, t.fVec), _mm_andnot_ps(fVec, e.fVec)); +#endif } __m128 fVec; diff --git a/src/opts/SkOpts_sse41.cpp b/src/opts/SkOpts_sse41.cpp index 416157125f..4489242de7 100644 --- a/src/opts/SkOpts_sse41.cpp +++ b/src/opts/SkOpts_sse41.cpp @@ -23,5 +23,7 @@ namespace SkOpts { color_xform_RGB1_srgb_to_2dot2 = sk_sse41::color_xform_RGB1_srgb_to_2dot2; color_xform_RGB1_2dot2_to_2dot2 = sk_sse41::color_xform_RGB1_2dot2_to_2dot2; + color_xform_RGB1_srgb_to_srgb = sk_sse41::color_xform_RGB1_srgb_to_srgb; + color_xform_RGB1_2dot2_to_srgb = sk_sse41::color_xform_RGB1_2dot2_to_srgb; } } |