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 "SkColorData.h" 12 #include "SkColorSpace.h" 13 #include "SkArenaAlloc.h" 14 #include "SkPM4f.h" 15 #include "SkRasterPipeline.h" 16 #include "SkSRGB.h" 17 #include "../jumper/SkJumper.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 // N.B. scratch_matrix_3x4 must live at least as long as p. 102 // Returns false if no gamut tranformation was necessary. 103 static inline bool append_gamut_transform_noclamp(SkRasterPipeline* p, 104 float scratch_matrix_3x4[12], 105 SkColorSpace* src, 106 SkColorSpace* dst) { 107 if (src == dst || !dst || !src) { 108 return false; 109 } 110 111 const SkMatrix44 *fromSrc = src-> toXYZD50(), 112 *toDst = dst->fromXYZD50(); 113 if (!fromSrc || !toDst) { 114 SkDEBUGFAIL("We can't handle non-XYZ color spaces in append_gamut_transform()."); 115 return false; 116 } 117 118 // Slightly more sophisticated version of if (src == dst) 119 if (src->toXYZD50Hash() == dst->toXYZD50Hash()) { 120 return false; 121 } 122 123 // Convert from 4x4 to (column-major) 3x4. 124 SkMatrix44 m44(*toDst, *fromSrc); 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 p->append(SkRasterPipeline::matrix_3x4, scratch_matrix_3x4); 132 return true; 133 } 134 135 136 // N.B. scratch_matrix_3x4 must live at least as long as p. 137 static inline void append_gamut_transform(SkRasterPipeline* p, 138 float scratch_matrix_3x4[12], 139 SkColorSpace* src, 140 SkColorSpace* dst, 141 SkAlphaType alphaType) { 142 if (append_gamut_transform_noclamp(p, scratch_matrix_3x4, src, dst)) { 143 bool needs_clamp_0, needs_clamp_1; 144 analyze_3x4_matrix(scratch_matrix_3x4, &needs_clamp_0, &needs_clamp_1); 145 146 if (needs_clamp_0) { p->append(SkRasterPipeline::clamp_0); } 147 if (needs_clamp_1) { 148 (kPremul_SkAlphaType == alphaType) ? p->append(SkRasterPipeline::clamp_a) 149 : p->append(SkRasterPipeline::clamp_1); 150 } 151 } 152 } 153 154 static inline void append_gamut_transform(SkRasterPipeline* p, 155 SkArenaAlloc* alloc, 156 SkColorSpace* src, 157 SkColorSpace* dst, 158 SkAlphaType alphaType) { 159 append_gamut_transform(p, alloc->makeArrayDefault<float>(12), src, dst, alphaType); 160 } 161 162 static inline SkColor4f to_colorspace(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) { 163 SkColor4f color4f = c; 164 if (src && dst && !SkColorSpace::Equals(src, dst)) { 165 SkJumper_MemoryCtx color4f_ptr = { &color4f, 0 }; 166 167 float scratch_matrix_3x4[12]; 168 169 SkSTArenaAlloc<256> alloc; 170 SkRasterPipeline p(&alloc); 171 p.append_constant_color(&alloc, color4f); 172 append_gamut_transform(&p, scratch_matrix_3x4, src, dst, kUnpremul_SkAlphaType); 173 p.append(SkRasterPipeline::store_f32, &color4f_ptr); 174 175 p.run(0,0,1,1); 176 } 177 return color4f; 178 } 179 180 static inline SkColor4f SkColor4f_from_SkColor(SkColor color, SkColorSpace* dst) { 181 SkColor4f color4f; 182 if (dst) { 183 // sRGB gamma, sRGB gamut. 184 color4f = to_colorspace(SkColor4f::FromColor(color), 185 SkColorSpace::MakeSRGB().get(), dst); 186 } else { 187 // Linear gamma, dst gamut. 188 swizzle_rb(SkNx_cast<float>(Sk4b::Load(&color)) * (1/255.0f)).store(&color4f); 189 } 190 return color4f; 191 } 192 193 static inline SkPM4f SkPM4f_from_SkColor(SkColor color, SkColorSpace* dst) { 194 return SkColor4f_from_SkColor(color, dst).premul(); 195 } 196 197 #endif 198