SkPM4fPriv.h revision 77a7a1b57c16c97f056c1e50c03bdc954947778c
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
19static inline Sk4f set_alpha(const Sk4f& px, float alpha) {
20    return { px[0], px[1], px[2], alpha };
21}
22
23static inline float get_alpha(const Sk4f& px) {
24    return px[3];
25}
26
27
28static inline Sk4f Sk4f_fromL32(uint32_t px) {
29    return SkNx_cast<float>(Sk4b::Load(&px)) * (1/255.0f);
30}
31
32static 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
39static 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
45static 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.
58static inline Sk4f Sk4f_from_SkColor(SkColor color) {
59    return swizzle_rb(Sk4f_fromS32(color));
60}
61
62static inline void assert_unit(float x) {
63    SkASSERT(0 <= x && x <= 1);
64}
65
66static 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
78static 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.
103static inline bool append_gamut_transform(SkRasterPipeline* p, float scratch_matrix_3x4[12],
104                                          SkColorSpace* src, SkColorSpace* dst) {
105    if (src == dst) { return true; }
106    if (!dst)       { return true; }   // Legacy modes intentionally ignore color gamut.
107    if (!src)       { return true; }   // A null src color space means linear gamma, dst gamut.
108
109    auto toXYZ = as_CSB(src)->  toXYZD50(),
110       fromXYZ = as_CSB(dst)->fromXYZD50();
111    if (!toXYZ || !fromXYZ) { return false; }  // Unsupported color space type.
112
113    if (as_CSB(src)->toXYZD50Hash() == as_CSB(dst)->toXYZD50Hash()) { return true; }
114
115    SkMatrix44 m44(*fromXYZ, *toXYZ);
116
117    // Convert from 4x4 to (column-major) 3x4.
118    auto ptr = scratch_matrix_3x4;
119    *ptr++ = m44.get(0,0); *ptr++ = m44.get(1,0); *ptr++ = m44.get(2,0);
120    *ptr++ = m44.get(0,1); *ptr++ = m44.get(1,1); *ptr++ = m44.get(2,1);
121    *ptr++ = m44.get(0,2); *ptr++ = m44.get(1,2); *ptr++ = m44.get(2,2);
122    *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    analyze_3x4_matrix(scratch_matrix_3x4, &needs_clamp_0, &needs_clamp_a);
126
127    p->append(SkRasterPipeline::matrix_3x4, scratch_matrix_3x4);
128    if (needs_clamp_0) { p->append(SkRasterPipeline::clamp_0); }
129    if (needs_clamp_a) { p->append(SkRasterPipeline::clamp_a); }
130    return true;
131}
132
133static inline bool append_gamut_transform(SkRasterPipeline* p, SkArenaAlloc* scratch,
134                                          SkColorSpace* src, SkColorSpace* dst) {
135    return append_gamut_transform(p, scratch->makeArrayDefault<float>(12), src, dst);
136}
137
138static inline SkColor4f to_colorspace(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
139    SkColor4f color4f = c;
140    if (src && dst) {
141        void* color4f_ptr = &color4f;
142
143        float scratch_matrix_3x4[12];
144
145        SkRasterPipeline p;
146        p.append(SkRasterPipeline::constant_color, color4f_ptr);
147        append_gamut_transform(&p, scratch_matrix_3x4, src, dst);
148        p.append(SkRasterPipeline::store_f32, &color4f_ptr);
149
150        p.run(0,1);
151    }
152    return color4f;
153}
154
155static inline SkColor4f SkColor4f_from_SkColor(SkColor color, SkColorSpace* dst) {
156    SkColor4f color4f;
157    if (dst) {
158        // sRGB gamma, sRGB gamut.
159        color4f = to_colorspace(SkColor4f::FromColor(color),
160                                SkColorSpace::MakeSRGB().get(), dst);
161    } else {
162        // Linear gamma, dst gamut.
163        swizzle_rb(SkNx_cast<float>(Sk4b::Load(&color)) * (1/255.0f)).store(&color4f);
164    }
165    return color4f;
166}
167
168static inline SkPM4f SkPM4f_from_SkColor(SkColor color, SkColorSpace* dst) {
169    return SkColor4f_from_SkColor(color, dst).premul();
170}
171
172#endif
173