aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkPM4fPriv.h
blob: f4142737b59388728f5f37ae299b096c3ff3bc50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
 * Copyright 2016 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#ifndef SkPM4fPriv_DEFINED
#define SkPM4fPriv_DEFINED

#include "SkColorPriv.h"
#include "SkColorSpace.h"
#include "SkColorSpace_Base.h"
#include "SkArenaAlloc.h"
#include "SkPM4f.h"
#include "SkRasterPipeline.h"
#include "SkSRGB.h"

static inline Sk4f set_alpha(const Sk4f& px, float alpha) {
    return { px[0], px[1], px[2], alpha };
}

static inline float get_alpha(const Sk4f& px) {
    return px[3];
}


static inline Sk4f Sk4f_fromL32(uint32_t px) {
    return SkNx_cast<float>(Sk4b::Load(&px)) * (1/255.0f);
}

static inline Sk4f Sk4f_fromS32(uint32_t px) {
    return { sk_linear_from_srgb[(px >>  0) & 0xff],
             sk_linear_from_srgb[(px >>  8) & 0xff],
             sk_linear_from_srgb[(px >> 16) & 0xff],
                    (1/255.0f) * (px >> 24)          };
}

static inline uint32_t Sk4f_toL32(const Sk4f& px) {
    uint32_t l32;
    SkNx_cast<uint8_t>(Sk4f_round(px * 255.0f)).store(&l32);
    return l32;
}

static inline uint32_t Sk4f_toS32(const Sk4f& px) {
    Sk4i  rgb = sk_linear_to_srgb(px),
         srgb = { rgb[0], rgb[1], rgb[2], (int)(255.0f * px[3] + 0.5f) };

    uint32_t s32;
    SkNx_cast<uint8_t>(srgb).store(&s32);
    return s32;
}


// SkColor handling:
//   SkColor has an ordering of (b, g, r, a) if cast to an Sk4f, so the code swizzles r and b to
// produce the needed (r, g, b, a) ordering.
static inline Sk4f Sk4f_from_SkColor(SkColor color) {
    return swizzle_rb(Sk4f_fromS32(color));
}

static inline void assert_unit(float x) {
    SkASSERT(0 <= x && x <= 1);
}

static inline float exact_srgb_to_linear(float srgb) {
    assert_unit(srgb);
    float linear;
    if (srgb <= 0.04045) {
        linear = srgb / 12.92f;
    } else {
        linear = powf((srgb + 0.055f) / 1.055f, 2.4f);
    }
    assert_unit(linear);
    return linear;
}

static inline void analyze_3x4_matrix(const float matrix[12],
                                      bool* can_underflow, bool* can_overflow) {
    // | 0 3 6  9 |   |r|   |x|
    // | 1 4 7 10 | x |g| = |y|
    // | 2 5 8 11 |   |b|   |z|
    //                |1|
    // We'll find min/max bounds on each of x,y,z assuming r,g,b are all in [0,1].
    // If any can be <0, we'll set can_underflow; if any can be >1, can_overflow.
    bool underflow = false,
          overflow = false;
    for (int i = 0; i < 3; i++) {
        SkScalar min = matrix[i+9],
                 max = matrix[i+9];
        (matrix[i+0] < 0 ? min : max) += matrix[i+0];
        (matrix[i+3] < 0 ? min : max) += matrix[i+3];
        (matrix[i+6] < 0 ? min : max) += matrix[i+6];
        underflow = underflow || min < 0;
        overflow  =  overflow || max > 1;
    }
    *can_underflow = underflow;
    *can_overflow  =  overflow;
}


// N.B. scratch_matrix_3x4 must live at least as long as p.
static inline void append_gamut_transform(SkRasterPipeline* p, float scratch_matrix_3x4[12],
                                          SkColorSpace* src, SkColorSpace* dst) {
    if (src == dst) { return; }   // That was easy.
    if (!dst)       { return; }   // Legacy modes intentionally ignore color gamut.
    if (!src)       { return; }   // A null src color space means linear gamma, dst gamut.

    auto toXYZ = as_CSB(src)->  toXYZD50(),
       fromXYZ = as_CSB(dst)->fromXYZD50();
    if (!toXYZ || !fromXYZ) {
        SkASSERT(false);  // We really don't want to get here with a weird colorspace.
        return;
    }

    // Slightly more sophisticated version of if (src == dst)
    if (as_CSB(src)->toXYZD50Hash() == as_CSB(dst)->toXYZD50Hash()) {
        return;
    }

    SkMatrix44 m44(*fromXYZ, *toXYZ);

    // Convert from 4x4 to (column-major) 3x4.
    auto ptr = scratch_matrix_3x4;
    *ptr++ = m44.get(0,0); *ptr++ = m44.get(1,0); *ptr++ = m44.get(2,0);
    *ptr++ = m44.get(0,1); *ptr++ = m44.get(1,1); *ptr++ = m44.get(2,1);
    *ptr++ = m44.get(0,2); *ptr++ = m44.get(1,2); *ptr++ = m44.get(2,2);
    *ptr++ = m44.get(0,3); *ptr++ = m44.get(1,3); *ptr++ = m44.get(2,3);

    bool needs_clamp_0, needs_clamp_a;
    analyze_3x4_matrix(scratch_matrix_3x4, &needs_clamp_0, &needs_clamp_a);

    p->append(SkRasterPipeline::matrix_3x4, scratch_matrix_3x4);
    if (needs_clamp_0) { p->append(SkRasterPipeline::clamp_0); }
    if (needs_clamp_a) { p->append(SkRasterPipeline::clamp_a); }
}

static inline void append_gamut_transform(SkRasterPipeline* p, SkArenaAlloc* scratch,
                                          SkColorSpace* src, SkColorSpace* dst) {
    append_gamut_transform(p, scratch->makeArrayDefault<float>(12), src, dst);
}

static inline SkColor4f to_colorspace(const SkColor4f& c, SkColorSpace* src, SkColorSpace* dst) {
    SkColor4f color4f = c;
    if (src && dst) {
        void* color4f_ptr = &color4f;

        float scratch_matrix_3x4[12];

        SkRasterPipeline p;
        p.append(SkRasterPipeline::constant_color, color4f_ptr);
        append_gamut_transform(&p, scratch_matrix_3x4, src, dst);
        p.append(SkRasterPipeline::store_f32, &color4f_ptr);

        p.run(0,1);
    }
    return color4f;
}

static inline SkColor4f SkColor4f_from_SkColor(SkColor color, SkColorSpace* dst) {
    SkColor4f color4f;
    if (dst) {
        // sRGB gamma, sRGB gamut.
        color4f = to_colorspace(SkColor4f::FromColor(color),
                                SkColorSpace::MakeSRGB().get(), dst);
    } else {
        // Linear gamma, dst gamut.
        swizzle_rb(SkNx_cast<float>(Sk4b::Load(&color)) * (1/255.0f)).store(&color4f);
    }
    return color4f;
}

static inline SkPM4f SkPM4f_from_SkColor(SkColor color, SkColorSpace* dst) {
    return SkColor4f_from_SkColor(color, dst).premul();
}

#endif