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
set_alpha(const Sk4f & px,float alpha)19 static inline Sk4f set_alpha(const Sk4f& px, float alpha) {
20 return { px[0], px[1], px[2], alpha };
21 }
22
get_alpha(const Sk4f & px)23 static inline float get_alpha(const Sk4f& px) {
24 return px[3];
25 }
26
27
Sk4f_fromL32(uint32_t px)28 static inline Sk4f Sk4f_fromL32(uint32_t px) {
29 return SkNx_cast<float>(Sk4b::Load(&px)) * (1/255.0f);
30 }
31
Sk4f_fromS32(uint32_t px)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
Sk4f_toL32(const Sk4f & px)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
Sk4f_toS32(const Sk4f & px)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.
Sk4f_from_SkColor(SkColor color)58 static inline Sk4f Sk4f_from_SkColor(SkColor color) {
59 return swizzle_rb(Sk4f_fromS32(color));
60 }
61
assert_unit(float x)62 static inline void assert_unit(float x) {
63 SkASSERT(0 <= x && x <= 1);
64 }
65
exact_srgb_to_linear(float srgb)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
analyze_3x4_matrix(const float matrix[12],bool * can_underflow,bool * can_overflow)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.
append_gamut_transform(SkRasterPipeline * p,float scratch_matrix_3x4[12],SkColorSpace * src,SkColorSpace * dst,SkAlphaType alphaType)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
append_gamut_transform(SkRasterPipeline * p,SkArenaAlloc * scratch,SkColorSpace * src,SkColorSpace * dst,SkAlphaType alphaType)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
to_colorspace(const SkColor4f & c,SkColorSpace * src,SkColorSpace * dst)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
SkColor4f_from_SkColor(SkColor color,SkColorSpace * dst)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
SkPM4f_from_SkColor(SkColor color,SkColorSpace * dst)178 static inline SkPM4f SkPM4f_from_SkColor(SkColor color, SkColorSpace* dst) {
179 return SkColor4f_from_SkColor(color, dst).premul();
180 }
181
182 #endif
183