/* * 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 "SkColorData.h" #include "SkColorSpace.h" #include "SkColorSpace_Base.h" #include "SkArenaAlloc.h" #include "SkPM4f.h" #include "SkRasterPipeline.h" #include "SkSRGB.h" #include "../jumper/SkJumper.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(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(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(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. // Returns false if no gamut tranformation was necessary. static inline bool append_gamut_transform_noclamp(SkRasterPipeline* p, float scratch_matrix_3x4[12], SkColorSpace* src, SkColorSpace* dst) { if (src == dst || !dst || !src) { return false; } const SkMatrix44 *fromSrc = as_CSB(src)-> toXYZD50(), *toDst = as_CSB(dst)->fromXYZD50(); if (!fromSrc || !toDst) { SkDEBUGFAIL("We can't handle non-XYZ color spaces in append_gamut_transform()."); return false; } // Slightly more sophisticated version of if (src == dst) if (as_CSB(src)->toXYZD50Hash() == as_CSB(dst)->toXYZD50Hash()) { return false; } // Convert from 4x4 to (column-major) 3x4. SkMatrix44 m44(*toDst, *fromSrc); 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); p->append(SkRasterPipeline::matrix_3x4, scratch_matrix_3x4); return true; } // 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 (append_gamut_transform_noclamp(p, scratch_matrix_3x4, src, dst)) { bool needs_clamp_0, needs_clamp_1; analyze_3x4_matrix(scratch_matrix_3x4, &needs_clamp_0, &needs_clamp_1); 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* alloc, SkColorSpace* src, SkColorSpace* dst, SkAlphaType alphaType) { append_gamut_transform(p, alloc->makeArrayDefault(12), src, dst, alphaType); } static inline SkColor4f to_colorspace(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) { SkColor4f color4f = c; if (src && dst && !SkColorSpace::Equals(src, dst)) { SkJumper_MemoryCtx color4f_ptr = { &color4f, 0 }; float scratch_matrix_3x4[12]; SkSTArenaAlloc<256> alloc; SkRasterPipeline p(&alloc); p.append_constant_color(&alloc, color4f); append_gamut_transform(&p, scratch_matrix_3x4, src, dst, kUnpremul_SkAlphaType); p.append(SkRasterPipeline::store_f32, &color4f_ptr); p.run(0,0,1,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(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