| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #ifndef SkPM4fPriv_DEFINED |
| #define SkPM4fPriv_DEFINED |
| |
| #include "SkColorPriv.h" |
| #include "SkColorSpace.h" |
| #include "SkColorSpace_Base.h" |
| #include "SkArenaAlloc.h" |
| #include "SkPM4f.h" |
| #include "SkRasterPipeline.h" |
| #include "SkSRGB.h" |
| |
| static inline Sk4f set_alpha(const Sk4f& px, float alpha) { |
| return { px[0], px[1], px[2], alpha }; |
| } |
| |
| static inline float get_alpha(const Sk4f& px) { |
| return px[3]; |
| } |
| |
| |
| static inline Sk4f Sk4f_fromL32(uint32_t px) { |
| return SkNx_cast<float>(Sk4b::Load(&px)) * (1/255.0f); |
| } |
| |
| static inline Sk4f Sk4f_fromS32(uint32_t px) { |
| return { sk_linear_from_srgb[(px >> 0) & 0xff], |
| sk_linear_from_srgb[(px >> 8) & 0xff], |
| sk_linear_from_srgb[(px >> 16) & 0xff], |
| (1/255.0f) * (px >> 24) }; |
| } |
| |
| static inline uint32_t Sk4f_toL32(const Sk4f& px) { |
| uint32_t l32; |
| SkNx_cast<uint8_t>(Sk4f_round(px * 255.0f)).store(&l32); |
| return l32; |
| } |
| |
| static inline uint32_t Sk4f_toS32(const Sk4f& px) { |
| Sk4i rgb = sk_linear_to_srgb(px), |
| srgb = { rgb[0], rgb[1], rgb[2], (int)(255.0f * px[3] + 0.5f) }; |
| |
| uint32_t s32; |
| SkNx_cast<uint8_t>(srgb).store(&s32); |
| return s32; |
| } |
| |
| |
| // SkColor handling: |
| // SkColor has an ordering of (b, g, r, a) if cast to an Sk4f, so the code swizzles r and b to |
| // produce the needed (r, g, b, a) ordering. |
| static inline Sk4f Sk4f_from_SkColor(SkColor color) { |
| return swizzle_rb(Sk4f_fromS32(color)); |
| } |
| |
| static inline void assert_unit(float x) { |
| SkASSERT(0 <= x && x <= 1); |
| } |
| |
| static inline float exact_srgb_to_linear(float srgb) { |
| assert_unit(srgb); |
| float linear; |
| if (srgb <= 0.04045) { |
| linear = srgb / 12.92f; |
| } else { |
| linear = powf((srgb + 0.055f) / 1.055f, 2.4f); |
| } |
| assert_unit(linear); |
| return linear; |
| } |
| |
| static inline void analyze_3x4_matrix(const float matrix[12], |
| bool* can_underflow, bool* can_overflow) { |
| // | 0 3 6 9 | |r| |x| |
| // | 1 4 7 10 | x |g| = |y| |
| // | 2 5 8 11 | |b| |z| |
| // |1| |
| // We'll find min/max bounds on each of x,y,z assuming r,g,b are all in [0,1]. |
| // If any can be <0, we'll set can_underflow; if any can be >1, can_overflow. |
| bool underflow = false, |
| overflow = false; |
| for (int i = 0; i < 3; i++) { |
| SkScalar min = matrix[i+9], |
| max = matrix[i+9]; |
| (matrix[i+0] < 0 ? min : max) += matrix[i+0]; |
| (matrix[i+3] < 0 ? min : max) += matrix[i+3]; |
| (matrix[i+6] < 0 ? min : max) += matrix[i+6]; |
| underflow = underflow || min < 0; |
| overflow = overflow || max > 1; |
| } |
| *can_underflow = underflow; |
| *can_overflow = overflow; |
| } |
| |
| |
| // N.B. scratch_matrix_3x4 must live at least as long as p. |
| static inline void append_gamut_transform(SkRasterPipeline* p, float scratch_matrix_3x4[12], |
| SkColorSpace* src, SkColorSpace* dst, |
| SkAlphaType alphaType) { |
| if (src == dst) { return; } // That was easy. |
| if (!dst) { return; } // Legacy modes intentionally ignore color gamut. |
| if (!src) { return; } // A null src color space means linear gamma, dst gamut. |
| |
| auto toXYZ = as_CSB(src)-> toXYZD50(), |
| fromXYZ = as_CSB(dst)->fromXYZD50(); |
| if (!toXYZ || !fromXYZ) { |
| SkASSERT(false); // We really don't want to get here with a weird colorspace. |
| return; |
| } |
| |
| // Slightly more sophisticated version of if (src == dst) |
| if (as_CSB(src)->toXYZD50Hash() == as_CSB(dst)->toXYZD50Hash()) { |
| return; |
| } |
| |
| SkMatrix44 m44(*fromXYZ, *toXYZ); |
| |
| // Convert from 4x4 to (column-major) 3x4. |
| auto ptr = scratch_matrix_3x4; |
| *ptr++ = m44.get(0,0); *ptr++ = m44.get(1,0); *ptr++ = m44.get(2,0); |
| *ptr++ = m44.get(0,1); *ptr++ = m44.get(1,1); *ptr++ = m44.get(2,1); |
| *ptr++ = m44.get(0,2); *ptr++ = m44.get(1,2); *ptr++ = m44.get(2,2); |
| *ptr++ = m44.get(0,3); *ptr++ = m44.get(1,3); *ptr++ = m44.get(2,3); |
| |
| bool needs_clamp_0, needs_clamp_1; |
| analyze_3x4_matrix(scratch_matrix_3x4, &needs_clamp_0, &needs_clamp_1); |
| |
| p->append(SkRasterPipeline::matrix_3x4, scratch_matrix_3x4); |
| if (needs_clamp_0) { p->append(SkRasterPipeline::clamp_0); } |
| if (needs_clamp_1) { |
| (kPremul_SkAlphaType == alphaType) ? p->append(SkRasterPipeline::clamp_a) |
| : p->append(SkRasterPipeline::clamp_1); |
| } |
| } |
| |
| static inline void append_gamut_transform(SkRasterPipeline* p, SkArenaAlloc* scratch, |
| SkColorSpace* src, SkColorSpace* dst, |
| SkAlphaType alphaType) { |
| append_gamut_transform(p, scratch->makeArrayDefault<float>(12), src, dst, alphaType); |
| } |
| |
| static inline SkColor4f to_colorspace(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) { |
| SkColor4f color4f = c; |
| if (src && dst) { |
| void* color4f_ptr = &color4f; |
| |
| float scratch_matrix_3x4[12]; |
| |
| SkRasterPipeline_<256> p; |
| p.append(SkRasterPipeline::uniform_color, color4f_ptr); |
| append_gamut_transform(&p, scratch_matrix_3x4, src, dst, kUnpremul_SkAlphaType); |
| p.append(SkRasterPipeline::store_f32, &color4f_ptr); |
| |
| p.run(0,0,1); |
| } |
| return color4f; |
| } |
| |
| static inline SkColor4f SkColor4f_from_SkColor(SkColor color, SkColorSpace* dst) { |
| SkColor4f color4f; |
| if (dst) { |
| // sRGB gamma, sRGB gamut. |
| color4f = to_colorspace(SkColor4f::FromColor(color), |
| SkColorSpace::MakeSRGB().get(), dst); |
| } else { |
| // Linear gamma, dst gamut. |
| swizzle_rb(SkNx_cast<float>(Sk4b::Load(&color)) * (1/255.0f)).store(&color4f); |
| } |
| return color4f; |
| } |
| |
| static inline SkPM4f SkPM4f_from_SkColor(SkColor color, SkColorSpace* dst) { |
| return SkColor4f_from_SkColor(color, dst).premul(); |
| } |
| |
| #endif |