Line data Source code
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 0 : static inline float get_alpha(const Sk4f& px) {
24 0 : return px[3];
25 : }
26 :
27 :
28 0 : static inline Sk4f Sk4f_fromL32(uint32_t px) {
29 0 : return SkNx_cast<float>(Sk4b::Load(&px)) * (1/255.0f);
30 : }
31 :
32 81 : static inline Sk4f Sk4f_fromS32(uint32_t px) {
33 81 : return { sk_linear_from_srgb[(px >> 0) & 0xff],
34 81 : sk_linear_from_srgb[(px >> 8) & 0xff],
35 81 : sk_linear_from_srgb[(px >> 16) & 0xff],
36 162 : (1/255.0f) * (px >> 24) };
37 : }
38 :
39 0 : static inline uint32_t Sk4f_toL32(const Sk4f& px) {
40 : uint32_t l32;
41 0 : SkNx_cast<uint8_t>(Sk4f_round(px * 255.0f)).store(&l32);
42 0 : return l32;
43 : }
44 :
45 81 : static inline uint32_t Sk4f_toS32(const Sk4f& px) {
46 81 : Sk4i rgb = sk_linear_to_srgb(px),
47 324 : srgb = { rgb[0], rgb[1], rgb[2], (int)(255.0f * px[3] + 0.5f) };
48 :
49 : uint32_t s32;
50 162 : SkNx_cast<uint8_t>(srgb).store(&s32);
51 81 : 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 0 : 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 0 : bool underflow = false,
87 0 : overflow = false;
88 0 : for (int i = 0; i < 3; i++) {
89 0 : SkScalar min = matrix[i+9],
90 0 : max = matrix[i+9];
91 0 : (matrix[i+0] < 0 ? min : max) += matrix[i+0];
92 0 : (matrix[i+3] < 0 ? min : max) += matrix[i+3];
93 0 : (matrix[i+6] < 0 ? min : max) += matrix[i+6];
94 0 : underflow = underflow || min < 0;
95 0 : overflow = overflow || max > 1;
96 : }
97 0 : *can_underflow = underflow;
98 0 : *can_overflow = overflow;
99 0 : }
100 :
101 :
102 : // N.B. scratch_matrix_3x4 must live at least as long as p.
103 0 : static inline bool append_gamut_transform(SkRasterPipeline* p, float scratch_matrix_3x4[12],
104 : SkColorSpace* src, SkColorSpace* dst) {
105 0 : if (src == dst) { return true; }
106 0 : if (!dst) { return true; } // Legacy modes intentionally ignore color gamut.
107 0 : if (!src) { return true; } // A null src color space means linear gamma, dst gamut.
108 :
109 0 : auto toXYZ = as_CSB(src)-> toXYZD50(),
110 0 : fromXYZ = as_CSB(dst)->fromXYZD50();
111 0 : if (!toXYZ || !fromXYZ) { return false; } // Unsupported color space type.
112 :
113 0 : if (as_CSB(src)->toXYZD50Hash() == as_CSB(dst)->toXYZD50Hash()) { return true; }
114 :
115 0 : SkMatrix44 m44(*fromXYZ, *toXYZ);
116 :
117 : // Convert from 4x4 to (column-major) 3x4.
118 0 : auto ptr = scratch_matrix_3x4;
119 0 : *ptr++ = m44.get(0,0); *ptr++ = m44.get(1,0); *ptr++ = m44.get(2,0);
120 0 : *ptr++ = m44.get(0,1); *ptr++ = m44.get(1,1); *ptr++ = m44.get(2,1);
121 0 : *ptr++ = m44.get(0,2); *ptr++ = m44.get(1,2); *ptr++ = m44.get(2,2);
122 0 : *ptr++ = m44.get(0,3); *ptr++ = m44.get(1,3); *ptr++ = m44.get(2,3);
123 :
124 : bool needs_clamp_0, needs_clamp_a;
125 0 : analyze_3x4_matrix(scratch_matrix_3x4, &needs_clamp_0, &needs_clamp_a);
126 :
127 0 : p->append(SkRasterPipeline::matrix_3x4, scratch_matrix_3x4);
128 0 : if (needs_clamp_0) { p->append(SkRasterPipeline::clamp_0); }
129 0 : if (needs_clamp_a) { p->append(SkRasterPipeline::clamp_a); }
130 0 : return true;
131 : }
132 :
133 0 : static inline bool append_gamut_transform(SkRasterPipeline* p, SkArenaAlloc* scratch,
134 : SkColorSpace* src, SkColorSpace* dst) {
135 0 : return append_gamut_transform(p, scratch->makeArrayDefault<float>(12), src, dst);
136 : }
137 :
138 0 : static inline SkColor4f to_colorspace(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
139 0 : SkColor4f color4f = c;
140 0 : if (src && dst) {
141 0 : void* color4f_ptr = &color4f;
142 :
143 : float scratch_matrix_3x4[12];
144 :
145 0 : SkRasterPipeline p;
146 0 : p.append(SkRasterPipeline::constant_color, color4f_ptr);
147 0 : append_gamut_transform(&p, scratch_matrix_3x4, src, dst);
148 0 : p.append(SkRasterPipeline::store_f32, &color4f_ptr);
149 :
150 0 : p.run(0,1);
151 : }
152 0 : return color4f;
153 : }
154 :
155 312 : static inline SkColor4f SkColor4f_from_SkColor(SkColor color, SkColorSpace* dst) {
156 : SkColor4f color4f;
157 312 : if (dst) {
158 : // sRGB gamma, sRGB gamut.
159 0 : color4f = to_colorspace(SkColor4f::FromColor(color),
160 0 : SkColorSpace::MakeSRGB().get(), dst);
161 : } else {
162 : // Linear gamma, dst gamut.
163 1248 : swizzle_rb(SkNx_cast<float>(Sk4b::Load(&color)) * (1/255.0f)).store(&color4f);
164 : }
165 312 : return color4f;
166 : }
167 :
168 312 : static inline SkPM4f SkPM4f_from_SkColor(SkColor color, SkColorSpace* dst) {
169 312 : return SkColor4f_from_SkColor(color, dst).premul();
170 : }
171 :
172 : #endif
|