1 /* 2 * Copyright 2016 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkPM4fPriv_DEFINED 9 #define SkPM4fPriv_DEFINED 10 11 #include "SkColorPriv.h" 12 #include "SkColorSpace.h" 13 #include "SkColorSpace_Base.h" 14 #include "SkArenaAlloc.h" 15 #include "SkPM4f.h" 16 #include "SkRasterPipeline.h" 17 #include "SkSRGB.h" 18 19 static inline Sk4f set_alpha(const Sk4f& px, float alpha) { 20 return { px[0], px[1], px[2], alpha }; 21 } 22 23 static inline float get_alpha(const Sk4f& px) { 24 return px[3]; 25 } 26 27 28 static inline Sk4f Sk4f_fromL32(uint32_t px) { 29 return SkNx_cast<float>(Sk4b::Load(&px)) * (1/255.0f); 30 } 31 32 static inline Sk4f Sk4f_fromS32(uint32_t px) { 33 return { sk_linear_from_srgb[(px >> 0) & 0xff], 34 sk_linear_from_srgb[(px >> 8) & 0xff], 35 sk_linear_from_srgb[(px >> 16) & 0xff], 36 (1/255.0f) * (px >> 24) }; 37 } 38 39 static inline uint32_t Sk4f_toL32(const Sk4f& px) { 40 uint32_t l32; 41 SkNx_cast<uint8_t>(Sk4f_round(px * 255.0f)).store(&l32); 42 return l32; 43 } 44 45 static inline uint32_t Sk4f_toS32(const Sk4f& px) { 46 Sk4i rgb = sk_linear_to_srgb(px), 47 srgb = { rgb[0], rgb[1], rgb[2], (int)(255.0f * px[3] + 0.5f) }; 48 49 uint32_t s32; 50 SkNx_cast<uint8_t>(srgb).store(&s32); 51 return s32; 52 } 53 54 55 // SkColor handling: 56 // SkColor has an ordering of (b, g, r, a) if cast to an Sk4f, so the code swizzles r and b to 57 // produce the needed (r, g, b, a) ordering. 58 static inline Sk4f Sk4f_from_SkColor(SkColor color) { 59 return swizzle_rb(Sk4f_fromS32(color)); 60 } 61 62 static inline void assert_unit(float x) { 63 SkASSERT(0 <= x && x <= 1); 64 } 65 66 static inline float exact_srgb_to_linear(float srgb) { 67 assert_unit(srgb); 68 float linear; 69 if (srgb <= 0.04045) { 70 linear = srgb / 12.92f; 71 } else { 72 linear = powf((srgb + 0.055f) / 1.055f, 2.4f); 73 } 74 assert_unit(linear); 75 return linear; 76 } 77 78 static inline void analyze_3x4_matrix(const float matrix[12], 79 bool* can_underflow, bool* can_overflow) { 80 // | 0 3 6 9 | |r| |x| 81 // | 1 4 7 10 | x |g| = |y| 82 // | 2 5 8 11 | |b| |z| 83 // |1| 84 // We'll find min/max bounds on each of x,y,z assuming r,g,b are all in [0,1]. 85 // If any can be <0, we'll set can_underflow; if any can be >1, can_overflow. 86 bool underflow = false, 87 overflow = false; 88 for (int i = 0; i < 3; i++) { 89 SkScalar min = matrix[i+9], 90 max = matrix[i+9]; 91 (matrix[i+0] < 0 ? min : max) += matrix[i+0]; 92 (matrix[i+3] < 0 ? min : max) += matrix[i+3]; 93 (matrix[i+6] < 0 ? min : max) += matrix[i+6]; 94 underflow = underflow || min < 0; 95 overflow = overflow || max > 1; 96 } 97 *can_underflow = underflow; 98 *can_overflow = overflow; 99 } 100 101 102 // N.B. scratch_matrix_3x4 must live at least as long as p. 103 static inline void append_gamut_transform(SkRasterPipeline* p, float scratch_matrix_3x4[12], 104 SkColorSpace* src, SkColorSpace* dst, 105 SkAlphaType alphaType) { 106 if (src == dst) { return; } // That was easy. 107 if (!dst) { return; } // Legacy modes intentionally ignore color gamut. 108 if (!src) { return; } // A null src color space means linear gamma, dst gamut. 109 110 auto toXYZ = as_CSB(src)-> toXYZD50(), 111 fromXYZ = as_CSB(dst)->fromXYZD50(); 112 if (!toXYZ || !fromXYZ) { 113 SkASSERT(false); // We really don't want to get here with a weird colorspace. 114 return; 115 } 116 117 // Slightly more sophisticated version of if (src == dst) 118 if (as_CSB(src)->toXYZD50Hash() == as_CSB(dst)->toXYZD50Hash()) { 119 return; 120 } 121 122 SkMatrix44 m44(*fromXYZ, *toXYZ); 123 124 // Convert from 4x4 to (column-major) 3x4. 125 auto ptr = scratch_matrix_3x4; 126 *ptr++ = m44.get(0,0); *ptr++ = m44.get(1,0); *ptr++ = m44.get(2,0); 127 *ptr++ = m44.get(0,1); *ptr++ = m44.get(1,1); *ptr++ = m44.get(2,1); 128 *ptr++ = m44.get(0,2); *ptr++ = m44.get(1,2); *ptr++ = m44.get(2,2); 129 *ptr++ = m44.get(0,3); *ptr++ = m44.get(1,3); *ptr++ = m44.get(2,3); 130 131 bool needs_clamp_0, needs_clamp_1; 132 analyze_3x4_matrix(scratch_matrix_3x4, &needs_clamp_0, &needs_clamp_1); 133 134 p->append(SkRasterPipeline::matrix_3x4, scratch_matrix_3x4); 135 if (needs_clamp_0) { p->append(SkRasterPipeline::clamp_0); } 136 if (needs_clamp_1) { 137 (kPremul_SkAlphaType == alphaType) ? p->append(SkRasterPipeline::clamp_a) 138 : p->append(SkRasterPipeline::clamp_1); 139 } 140 } 141 142 static inline void append_gamut_transform(SkRasterPipeline* p, SkArenaAlloc* scratch, 143 SkColorSpace* src, SkColorSpace* dst, 144 SkAlphaType alphaType) { 145 append_gamut_transform(p, scratch->makeArrayDefault<float>(12), src, dst, alphaType); 146 } 147 148 static inline SkColor4f to_colorspace(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) { 149 SkColor4f color4f = c; 150 if (src && dst) { 151 void* color4f_ptr = &color4f; 152 153 float scratch_matrix_3x4[12]; 154 155 SkRasterPipeline_<256> p; 156 p.append(SkRasterPipeline::uniform_color, color4f_ptr); 157 append_gamut_transform(&p, scratch_matrix_3x4, src, dst, kUnpremul_SkAlphaType); 158 p.append(SkRasterPipeline::store_f32, &color4f_ptr); 159 160 p.run(0,0,1); 161 } 162 return color4f; 163 } 164 165 static inline SkColor4f SkColor4f_from_SkColor(SkColor color, SkColorSpace* dst) { 166 SkColor4f color4f; 167 if (dst) { 168 // sRGB gamma, sRGB gamut. 169 color4f = to_colorspace(SkColor4f::FromColor(color), 170 SkColorSpace::MakeSRGB().get(), dst); 171 } else { 172 // Linear gamma, dst gamut. 173 swizzle_rb(SkNx_cast<float>(Sk4b::Load(&color)) * (1/255.0f)).store(&color4f); 174 } 175 return color4f; 176 } 177 178 static inline SkPM4f SkPM4f_from_SkColor(SkColor color, SkColorSpace* dst) { 179 return SkColor4f_from_SkColor(color, dst).premul(); 180 } 181 182 #endif 183