aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/core/SkColorSpaceXform_skcms.cpp
blob: 34afaaa7c57615c1695f62ee78de28a748e713ac (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
/*
 * Copyright 2018 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkColorSpaceXform.h"
#include "SkData.h"
#include "SkMakeUnique.h"
#include "skcms.h"

class SkColorSpaceXform_skcms : public SkColorSpaceXform {
public:
    SkColorSpaceXform_skcms(const skcms_ICCProfile& srcProfile,
                            const skcms_ICCProfile& dstProfile,
                            skcms_AlphaFormat premulFormat)
        : fSrcProfile(srcProfile)
        , fDstProfile(dstProfile)
        , fPremulFormat(premulFormat)
    {}

    bool apply(ColorFormat, void*, ColorFormat, const void*, int, SkAlphaType) const override;

private:
    skcms_ICCProfile  fSrcProfile;
    skcms_ICCProfile  fDstProfile;
    skcms_AlphaFormat fPremulFormat;
};

static skcms_PixelFormat get_skcms_format(SkColorSpaceXform::ColorFormat fmt) {
    switch (fmt) {
        case SkColorSpaceXform::kRGBA_8888_ColorFormat:
            return skcms_PixelFormat_RGBA_8888;
        case SkColorSpaceXform::kBGRA_8888_ColorFormat:
            return skcms_PixelFormat_BGRA_8888;
        case SkColorSpaceXform::kRGB_U16_BE_ColorFormat:
            return skcms_PixelFormat_RGB_161616;
        case SkColorSpaceXform::kRGBA_U16_BE_ColorFormat:
            return skcms_PixelFormat_RGBA_16161616;
        case SkColorSpaceXform::kRGBA_F16_ColorFormat:
            return skcms_PixelFormat_RGBA_hhhh;
        case SkColorSpaceXform::kRGBA_F32_ColorFormat:
            return skcms_PixelFormat_RGBA_ffff;
        case SkColorSpaceXform::kBGR_565_ColorFormat:
            return skcms_PixelFormat_BGR_565;
        default:
            SkDEBUGFAIL("Invalid ColorFormat");
            return skcms_PixelFormat_RGBA_8888;
    }
}

bool SkColorSpaceXform_skcms::apply(ColorFormat dstFormat, void* dst,
                                    ColorFormat srcFormat, const void* src,
                                    int count, SkAlphaType alphaType) const {
    skcms_AlphaFormat srcAlpha = skcms_AlphaFormat_Unpremul;
    skcms_AlphaFormat dstAlpha = kPremul_SkAlphaType == alphaType ? fPremulFormat
                                                                  : skcms_AlphaFormat_Unpremul;

    return skcms_Transform(src, get_skcms_format(srcFormat), srcAlpha, &fSrcProfile,
                           dst, get_skcms_format(dstFormat), dstAlpha, &fDstProfile, count);
}

static bool cs_to_profile(const SkColorSpace* cs, skcms_ICCProfile* profile) {
    if (cs->profileData()) {
        bool result = skcms_Parse(cs->profileData()->data(), cs->profileData()->size(), profile);
        // We shouldn't encounter color spaces that were constructed from invalid profiles!
        SkASSERT(result);
        return result;
    }

    SkMatrix44 toXYZ;
    SkColorSpaceTransferFn tf;
    if (cs->toXYZD50(&toXYZ) && cs->isNumericalTransferFn(&tf)) {
        memset(profile, 0, sizeof(*profile));

        profile->has_trc = true;
        profile->trc[0].parametric.g = tf.fG;
        profile->trc[0].parametric.a = tf.fA;
        profile->trc[0].parametric.b = tf.fB;
        profile->trc[0].parametric.c = tf.fC;
        profile->trc[0].parametric.d = tf.fD;
        profile->trc[0].parametric.e = tf.fE;
        profile->trc[0].parametric.f = tf.fF;
        profile->trc[1].parametric = profile->trc[0].parametric;
        profile->trc[2].parametric = profile->trc[0].parametric;

        profile->has_toXYZD50 = true;
        for (int r = 0; r < 3; ++r) {
            for (int c = 0; c < 3; ++c) {
                profile->toXYZD50.vals[r][c] = toXYZ.get(r, c);
            }
        }

        return true;
    }

    // It should be impossible to make a color space that gets here with our available factories.
    // All ICC-based profiles have profileData. All remaining factories produce XYZ spaces with
    // a single (numerical) transfer function.
    SkDEBUGFAIL("How did we get here?");
    return false;
}

std::unique_ptr<SkColorSpaceXform> MakeSkcmsXform(SkColorSpace* src, SkColorSpace* dst,
                                                  SkTransferFunctionBehavior premulBehavior) {
    // Construct skcms_ICCProfiles from each color space. For now, support A2B and XYZ.
    // Eventually, only need to support XYZ. Map premulBehavior to one of the two premul formats
    // in skcms.
    skcms_ICCProfile srcProfile, dstProfile;

    if (!cs_to_profile(src, &srcProfile) || !cs_to_profile(dst, &dstProfile)) {
        return nullptr;
    }

    skcms_AlphaFormat premulFormat = SkTransferFunctionBehavior::kRespect == premulBehavior
            ? skcms_AlphaFormat_PremulLinear : skcms_AlphaFormat_PremulAsEncoded;
    return skstd::make_unique<SkColorSpaceXform_skcms>(srcProfile, dstProfile, premulFormat);
}