Index: src/core/SkColorSpaceXform.cpp |
diff --git a/src/core/SkColorSpaceXform.cpp b/src/core/SkColorSpaceXform.cpp |
index 599adc90f959609062c6d9955ec01b0560428321..12c3801e47567b7a7125b46c2a9deb25da2094e8 100644 |
--- a/src/core/SkColorSpaceXform.cpp |
+++ b/src/core/SkColorSpaceXform.cpp |
@@ -1329,6 +1329,173 @@ const |
} |
} |
+static inline Sk4f srgb_to_linear(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 y = (x + 0.055f) * (1.0f / 1.055f); |
+ auto twodotfive = y * y * y.rsqrt().invert(); |
+ auto lo = ((1.0f / 12.92f)) * x; |
+ auto hi = twodotfive; |
+ auto out = (x < 0.04045f).thenElse(lo, hi); |
+ return out; |
+} |
+ |
+template <SrcGamma kSrc, DstGamma kDst, ColorSpaceMatch kCSM> |
+void SkColorSpaceXform_Base<kSrc, kDst, kCSM> |
+::applyYUV(void* dst, const uint8_t* srcY, const uint8_t* srcU, const uint8_t* srcV, int len) |
+const |
+{ |
+ Sk4f rXgXbX, rYgYbY, rZgZbZ, rTgTbT; |
+ load_matrix(fSrcToDst, rXgXbX, rYgYbY, rZgZbZ, rTgTbT); |
+ |
+ if (len >= 4) { |
+ // Naively this would be a loop of load-transform-store, but we found it faster to |
+ // move the N+1th load ahead of the Nth store. We don't bother doing this for N<4. |
+ Sk4f r, g, b, a; |
+ |
+ Sk4f y = (1.0f / 255.0f) * SkNx_cast<float, uint8_t>(Sk4b::Load(srcY)); |
+ Sk4f u = (1.0f / 255.0f) * (SkNx_cast<float, uint8_t>(Sk4b::Load(srcU)) - 128.0f); |
+ Sk4f v = (1.0f / 255.0f) * (SkNx_cast<float, uint8_t>(Sk4b::Load(srcV)) - 128.0f); |
+ |
+ r = (y + 1.402f * v); |
+ g = (y - 0.34414f * u - 0.71414f * v); |
+ b = (y + 1.772f * u); |
+ |
+ r = srgb_to_linear(r); |
+ g = srgb_to_linear(g); |
+ b = srgb_to_linear(b); |
+ |
+ srcY += 4; |
+ srcU += 4; |
+ srcV += 4; |
+ len -= 4; |
+ |
+ Sk4f dr, dg, db, da; |
+ while (len >= 4) { |
+ transform_gamut(r, g, b, a, rXgXbX, rYgYbY, rZgZbZ, dr, dg, db, da); |
+ translate_gamut(rTgTbT, dr, dg, db); |
+ |
+ y = (1.0f / 255.0f) * SkNx_cast<float, uint8_t>(Sk4b::Load(srcY)); |
+ u = (1.0f / 255.0f) * (SkNx_cast<float, uint8_t>(Sk4b::Load(srcU)) - 128.0f); |
+ v = (1.0f / 255.0f) * (SkNx_cast<float, uint8_t>(Sk4b::Load(srcV)) - 128.0f); |
+ |
+ r = (y + 1.402f * v); |
+ g = (y - 0.34414f * u - 0.71414f * v); |
+ b = (y + 1.772f * u); |
+ |
+ r = srgb_to_linear(r); |
+ g = srgb_to_linear(g); |
+ b = srgb_to_linear(b); |
+ |
+ srcY += 4; |
+ srcU += 4; |
+ srcV += 4; |
+ len -= 4; |
+ |
+ dr = sk_linear_to_srgb_needs_trunc(dr); |
+ dg = sk_linear_to_srgb_needs_trunc(dg); |
+ db = sk_linear_to_srgb_needs_trunc(db); |
+ |
+ dr = sk_clamp_0_255(dr); |
+ dg = sk_clamp_0_255(dg); |
+ db = sk_clamp_0_255(db); |
+ |
+ Sk4i rgba = (SkNx_cast<int>(dr) << 16) |
+ | (SkNx_cast<int>(dg) << 8) |
+ | (SkNx_cast<int>(db) << 0) |
+ | (0xFF << 24 ); |
+ rgba.store(dst); |
+ dst = SkTAddOffset<void>(dst, 4 * 4); |
+ } |
+ |
+ transform_gamut(r, g, b, a, rXgXbX, rYgYbY, rZgZbZ, dr, dg, db, da); |
+ translate_gamut(rTgTbT, dr, dg, db); |
+ |
+ dr = sk_linear_to_srgb_needs_trunc(dr); |
+ dg = sk_linear_to_srgb_needs_trunc(dg); |
+ db = sk_linear_to_srgb_needs_trunc(db); |
+ |
+ dr = sk_clamp_0_255(dr); |
+ dg = sk_clamp_0_255(dg); |
+ db = sk_clamp_0_255(db); |
+ |
+ Sk4i rgba = (SkNx_cast<int>(dr) << 16) |
+ | (SkNx_cast<int>(dg) << 8) |
+ | (SkNx_cast<int>(db) << 0) |
+ | (0xFF << 24 ); |
+ rgba.store(dst); |
+ dst = SkTAddOffset<void>(dst, 4 * 4); |
+ } |
+} |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
+ |
/////////////////////////////////////////////////////////////////////////////////////////////////// |
std::unique_ptr<SkColorSpaceXform> SlowIdentityXform(const sk_sp<SkColorSpace>& space) { |