SkColorSpaceXform.cpp revision ac41bac40f5a80d2bc5ccec584c23478a6900179
19876ac5b3016e5353c072378ac1545a0a2270757msarett/*
29876ac5b3016e5353c072378ac1545a0a2270757msarett * Copyright 2016 Google Inc.
39876ac5b3016e5353c072378ac1545a0a2270757msarett *
49876ac5b3016e5353c072378ac1545a0a2270757msarett * Use of this source code is governed by a BSD-style license that can be
59876ac5b3016e5353c072378ac1545a0a2270757msarett * found in the LICENSE file.
69876ac5b3016e5353c072378ac1545a0a2270757msarett */
79876ac5b3016e5353c072378ac1545a0a2270757msarett
89876ac5b3016e5353c072378ac1545a0a2270757msarett#include "SkColorPriv.h"
99876ac5b3016e5353c072378ac1545a0a2270757msarett#include "SkColorSpace_Base.h"
109876ac5b3016e5353c072378ac1545a0a2270757msarett#include "SkColorSpaceXform.h"
11a9e878c836994bce695274b4c28890290139dcdfmsarett#include "SkOpts.h"
12ac41bac40f5a80d2bc5ccec584c23478a6900179mtklein#include "SkSRGB.h"
139876ac5b3016e5353c072378ac1545a0a2270757msarett
14dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettstatic inline bool compute_gamut_xform(SkMatrix44* srcToDst, const SkMatrix44& srcToXYZ,
15dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                                       const SkMatrix44& dstToXYZ) {
169876ac5b3016e5353c072378ac1545a0a2270757msarett    if (!dstToXYZ.invert(srcToDst)) {
179876ac5b3016e5353c072378ac1545a0a2270757msarett        return false;
189876ac5b3016e5353c072378ac1545a0a2270757msarett    }
199876ac5b3016e5353c072378ac1545a0a2270757msarett
209876ac5b3016e5353c072378ac1545a0a2270757msarett    srcToDst->postConcat(srcToXYZ);
219876ac5b3016e5353c072378ac1545a0a2270757msarett    return true;
229876ac5b3016e5353c072378ac1545a0a2270757msarett}
239876ac5b3016e5353c072378ac1545a0a2270757msarett
249876ac5b3016e5353c072378ac1545a0a2270757msarettstd::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpace>& srcSpace,
259876ac5b3016e5353c072378ac1545a0a2270757msarett                                                          const sk_sp<SkColorSpace>& dstSpace) {
269876ac5b3016e5353c072378ac1545a0a2270757msarett    if (!srcSpace || !dstSpace) {
27dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // Invalid input
289876ac5b3016e5353c072378ac1545a0a2270757msarett        return nullptr;
299876ac5b3016e5353c072378ac1545a0a2270757msarett    }
309876ac5b3016e5353c072378ac1545a0a2270757msarett
310f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    if (as_CSB(dstSpace)->colorLUT()) {
320f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        // It would be really weird for a dst profile to have a color LUT.  I don't think
330f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        // we need to support this.
34dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        return nullptr;
35dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    }
369876ac5b3016e5353c072378ac1545a0a2270757msarett
37dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor);
38dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) {
39dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        return nullptr;
40dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    }
41dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
42d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett    if (0.0f == srcToDst.getFloat(3, 0) &&
43dea0340cadb759932e53416a657f5ea75fee8b5fmsarett        0.0f == srcToDst.getFloat(3, 1) &&
440f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        0.0f == srcToDst.getFloat(3, 2) &&
450f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        !as_CSB(srcSpace)->colorLUT())
46a9e878c836994bce695274b4c28890290139dcdfmsarett    {
47d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett        switch (srcSpace->gammaNamed()) {
48d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett            case SkColorSpace::kSRGB_GammaNamed:
49d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                if (SkColorSpace::kSRGB_GammaNamed == dstSpace->gammaNamed()) {
50d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                    return std::unique_ptr<SkColorSpaceXform>(
51d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                            new SkFastXform<SkColorSpace::kSRGB_GammaNamed,
52d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                                            SkColorSpace::kSRGB_GammaNamed>(srcToDst));
53d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                } else if (SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gammaNamed()) {
54d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                    return std::unique_ptr<SkColorSpaceXform>(
55d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                            new SkFastXform<SkColorSpace::kSRGB_GammaNamed,
56d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                                            SkColorSpace::k2Dot2Curve_GammaNamed>(srcToDst));
57d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                }
58d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                break;
59d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett            case SkColorSpace::k2Dot2Curve_GammaNamed:
60d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                if (SkColorSpace::kSRGB_GammaNamed == dstSpace->gammaNamed()) {
61d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                    return std::unique_ptr<SkColorSpaceXform>(
62d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                            new SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed,
63d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                                            SkColorSpace::kSRGB_GammaNamed>(srcToDst));
64d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                } else if (SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gammaNamed()) {
65d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                    return std::unique_ptr<SkColorSpaceXform>(
66d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                            new SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed,
67d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                                            SkColorSpace::k2Dot2Curve_GammaNamed>(srcToDst));
68d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                }
69d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                break;
70d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett            default:
71d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett                break;
72dea0340cadb759932e53416a657f5ea75fee8b5fmsarett        }
739876ac5b3016e5353c072378ac1545a0a2270757msarett    }
749876ac5b3016e5353c072378ac1545a0a2270757msarett
750f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    return std::unique_ptr<SkColorSpaceXform>(new SkDefaultXform(srcSpace, srcToDst, dstSpace));
769876ac5b3016e5353c072378ac1545a0a2270757msarett}
779876ac5b3016e5353c072378ac1545a0a2270757msarett
789876ac5b3016e5353c072378ac1545a0a2270757msarett///////////////////////////////////////////////////////////////////////////////////////////////////
799876ac5b3016e5353c072378ac1545a0a2270757msarett
80dea0340cadb759932e53416a657f5ea75fee8b5fmsarettstatic void build_src_to_dst(float srcToDstArray[12], const SkMatrix44& srcToDstMatrix) {
81dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    // Build the following row major matrix:
82a9e878c836994bce695274b4c28890290139dcdfmsarett    //   rX gX bX 0
83a9e878c836994bce695274b4c28890290139dcdfmsarett    //   rY gY bY 0
84a9e878c836994bce695274b4c28890290139dcdfmsarett    //   rZ gZ bZ 0
85dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    // Swap R and B if necessary to make sure that we output SkPMColor order.
86dea0340cadb759932e53416a657f5ea75fee8b5fmsarett#ifdef SK_PMCOLOR_IS_BGRA
87dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[0] = srcToDstMatrix.getFloat(0, 2);
88dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[1] = srcToDstMatrix.getFloat(0, 1);
89dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[2] = srcToDstMatrix.getFloat(0, 0);
90dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[3] = 0.0f;
91dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[4] = srcToDstMatrix.getFloat(1, 2);
92dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[5] = srcToDstMatrix.getFloat(1, 1);
93dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[6] = srcToDstMatrix.getFloat(1, 0);
94dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[7] = 0.0f;
95dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[8] = srcToDstMatrix.getFloat(2, 2);
96dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[9] = srcToDstMatrix.getFloat(2, 1);
97dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[10] = srcToDstMatrix.getFloat(2, 0);
98dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[11] = 0.0f;
99dea0340cadb759932e53416a657f5ea75fee8b5fmsarett#else
100dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[0] = srcToDstMatrix.getFloat(0, 0);
101dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[1] = srcToDstMatrix.getFloat(0, 1);
102dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[2] = srcToDstMatrix.getFloat(0, 2);
103dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[3] = 0.0f;
104dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[4] = srcToDstMatrix.getFloat(1, 0);
105dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[5] = srcToDstMatrix.getFloat(1, 1);
106dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[6] = srcToDstMatrix.getFloat(1, 2);
107dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[7] = 0.0f;
108dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[8] = srcToDstMatrix.getFloat(2, 0);
109dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[9] = srcToDstMatrix.getFloat(2, 1);
110dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[10] = srcToDstMatrix.getFloat(2, 2);
111dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    srcToDstArray[11] = 0.0f;
112dea0340cadb759932e53416a657f5ea75fee8b5fmsarett#endif
113dea0340cadb759932e53416a657f5ea75fee8b5fmsarett}
114dea0340cadb759932e53416a657f5ea75fee8b5fmsarett
115d2809573deb7b99e764f7f71fe34a5b5322df0b2msaretttemplate <SkColorSpace::GammaNamed Src, SkColorSpace::GammaNamed Dst>
116d2809573deb7b99e764f7f71fe34a5b5322df0b2msarettSkFastXform<Src, Dst>::SkFastXform(const SkMatrix44& srcToDst)
117dea0340cadb759932e53416a657f5ea75fee8b5fmsarett{
118dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    build_src_to_dst(fSrcToDst, srcToDst);
119dea0340cadb759932e53416a657f5ea75fee8b5fmsarett}
120dea0340cadb759932e53416a657f5ea75fee8b5fmsarett
121d2809573deb7b99e764f7f71fe34a5b5322df0b2msaretttemplate <>
122d2809573deb7b99e764f7f71fe34a5b5322df0b2msarettvoid SkFastXform<SkColorSpace::kSRGB_GammaNamed, SkColorSpace::kSRGB_GammaNamed>
123d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const
124d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett{
125d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett    SkOpts::color_xform_RGB1_srgb_to_srgb(dst, src, len, fSrcToDst);
126dea0340cadb759932e53416a657f5ea75fee8b5fmsarett}
127dea0340cadb759932e53416a657f5ea75fee8b5fmsarett
128d2809573deb7b99e764f7f71fe34a5b5322df0b2msaretttemplate <>
129d2809573deb7b99e764f7f71fe34a5b5322df0b2msarettvoid SkFastXform<SkColorSpace::kSRGB_GammaNamed, SkColorSpace::k2Dot2Curve_GammaNamed>
130d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const
131d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett{
132d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett    SkOpts::color_xform_RGB1_srgb_to_2dot2(dst, src, len, fSrcToDst);
133d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett}
134dea0340cadb759932e53416a657f5ea75fee8b5fmsarett
135d2809573deb7b99e764f7f71fe34a5b5322df0b2msaretttemplate <>
136d2809573deb7b99e764f7f71fe34a5b5322df0b2msarettvoid SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed, SkColorSpace::kSRGB_GammaNamed>
137d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const
138dea0340cadb759932e53416a657f5ea75fee8b5fmsarett{
139d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett    SkOpts::color_xform_RGB1_2dot2_to_srgb(dst, src, len, fSrcToDst);
140a9e878c836994bce695274b4c28890290139dcdfmsarett}
141a9e878c836994bce695274b4c28890290139dcdfmsarett
142d2809573deb7b99e764f7f71fe34a5b5322df0b2msaretttemplate <>
143d2809573deb7b99e764f7f71fe34a5b5322df0b2msarettvoid SkFastXform<SkColorSpace::k2Dot2Curve_GammaNamed, SkColorSpace::k2Dot2Curve_GammaNamed>
144d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const
145d2809573deb7b99e764f7f71fe34a5b5322df0b2msarett{
146dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    SkOpts::color_xform_RGB1_2dot2_to_2dot2(dst, src, len, fSrcToDst);
147a9e878c836994bce695274b4c28890290139dcdfmsarett}
148a9e878c836994bce695274b4c28890290139dcdfmsarett
149a9e878c836994bce695274b4c28890290139dcdfmsarett///////////////////////////////////////////////////////////////////////////////////////////////////
150a9e878c836994bce695274b4c28890290139dcdfmsarett
151b39067696ad08a26bbe49b71a71f0546dc42a075msarettextern const float sk_linear_from_2dot2[256] = {
152b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.000000000000000000f, 0.000005077051900662f, 0.000023328004666099f, 0.000056921765712193f,
153b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.000107187362341244f, 0.000175123977503027f, 0.000261543754548491f, 0.000367136269815943f,
154b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.000492503787191433f, 0.000638182842167022f, 0.000804658499513058f, 0.000992374304074325f,
155b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.001201739522438400f, 0.001433134589671860f, 0.001686915316789280f, 0.001963416213396470f,
156b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.002262953160706430f, 0.002585825596234170f, 0.002932318323938360f, 0.003302703032003640f,
157b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.003697239578900130f, 0.004116177093282750f, 0.004559754922526020f, 0.005028203456855540f,
158b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.005521744850239660f, 0.006040593654849810f, 0.006584957382581690f, 0.007155037004573030f,
159b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.007751027397660610f, 0.008373117745148580f, 0.009021491898012130f, 0.009696328701658230f,
160b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.010397802292555300f, 0.011126082368383200f, 0.011881334434813700f, 0.012663720031582100f,
161b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.013473396940142600f, 0.014310519374884100f, 0.015175238159625200f, 0.016067700890886900f,
162b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.016988052089250000f, 0.017936433339950200f, 0.018912983423721500f, 0.019917838438785700f,
163b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.020951131914781100f, 0.022012994919336500f, 0.023103556157921400f, 0.024222942067534200f,
164b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.025371276904734600f, 0.026548682828472900f, 0.027755279978126000f, 0.028991186547107800f,
165b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.030256518852388700f, 0.031551391400226400f, 0.032875916948383800f, 0.034230206565082000f,
166b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.035614369684918800f, 0.037028514161960200f, 0.038472746320194600f, 0.039947171001525600f,
167b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.041451891611462500f, 0.042987010162657100f, 0.044552627316421400f, 0.046148842422351000f,
168b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.047775753556170600f, 0.049433457555908000f, 0.051122050056493400f, 0.052841625522879000f,
169b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.054592277281760300f, 0.056374097551979800f, 0.058187177473685400f, 0.060031607136313200f,
170b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.061907475605455800f, 0.063814870948677200f, 0.065753880260330100f, 0.067724589685424300f,
171b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.069727084442598800f, 0.071761448846239100f, 0.073827766327784600f, 0.075926119456264800f,
172b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.078056589958101900f, 0.080219258736215100f, 0.082414205888459200f, 0.084641510725429500f,
173b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.086901251787660300f, 0.089193506862247800f, 0.091518352998919500f, 0.093875866525577800f,
174b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.096266123063339700f, 0.098689197541094500f, 0.101145164209600000f, 0.103634096655137000f,
175b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.106156067812744000f, 0.108711149979039000f, 0.111299414824660000f, 0.113920933406333000f,
176b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.116575776178572000f, 0.119264013005047000f, 0.121985713169619000f, 0.124740945387051000f,
177b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.127529777813422000f, 0.130352278056244000f, 0.133208513184300000f, 0.136098549737202000f,
178b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.139022453734703000f, 0.141980290685736000f, 0.144972125597231000f, 0.147998022982685000f,
179b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.151058046870511000f, 0.154152260812165000f, 0.157280727890073000f, 0.160443510725344000f,
180b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.163640671485290000f, 0.166872271890766000f, 0.170138373223312000f, 0.173439036332135000f,
181b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.176774321640903000f, 0.180144289154390000f, 0.183548998464951000f, 0.186988508758844000f,
182b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.190462878822409000f, 0.193972167048093000f, 0.197516431440340000f, 0.201095729621346000f,
183b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.204710118836677000f, 0.208359655960767000f, 0.212044397502288000f, 0.215764399609395000f,
184b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.219519718074868000f, 0.223310408341127000f, 0.227136525505149000f, 0.230998124323267000f,
185b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.234895259215880000f, 0.238827984272048000f, 0.242796353254002000f, 0.246800419601550000f,
186b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.250840236436400000f, 0.254915856566385000f, 0.259027332489606000f, 0.263174716398492000f,
187b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.267358060183772000f, 0.271577415438375000f, 0.275832833461245000f, 0.280124365261085000f,
188b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.284452061560024000f, 0.288815972797219000f, 0.293216149132375000f, 0.297652640449211000f,
189b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.302125496358853000f, 0.306634766203158000f, 0.311180499057984000f, 0.315762743736397000f,
190b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.320381548791810000f, 0.325036962521076000f, 0.329729032967515000f, 0.334457807923889000f,
191b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.339223334935327000f, 0.344025661302187000f, 0.348864834082879000f, 0.353740900096629000f,
192b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.358653905926199000f, 0.363603897920553000f, 0.368590922197487000f, 0.373615024646202000f,
193b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.378676250929840000f, 0.383774646487975000f, 0.388910256539059000f, 0.394083126082829000f,
194b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.399293299902674000f, 0.404540822567962000f, 0.409825738436323000f, 0.415148091655907000f,
195b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.420507926167587000f, 0.425905285707146000f, 0.431340213807410000f, 0.436812753800359000f,
196b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.442322948819202000f, 0.447870841800410000f, 0.453456475485731000f, 0.459079892424160000f,
197b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.464741134973889000f, 0.470440245304218000f, 0.476177265397440000f, 0.481952237050698000f,
198b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.487765201877811000f, 0.493616201311074000f, 0.499505276603030000f, 0.505432468828216000f,
199b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.511397818884880000f, 0.517401367496673000f, 0.523443155214325000f, 0.529523222417277000f,
200b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.535641609315311000f, 0.541798355950137000f, 0.547993502196972000f, 0.554227087766085000f,
201b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.560499152204328000f, 0.566809734896638000f, 0.573158875067523000f, 0.579546611782525000f,
202b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.585972983949661000f, 0.592438030320847000f, 0.598941789493296000f, 0.605484299910907000f,
203b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.612065599865624000f, 0.618685727498780000f, 0.625344720802427000f, 0.632042617620641000f,
204b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.638779455650817000f, 0.645555272444935000f, 0.652370105410821000f, 0.659223991813387000f,
205b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.666116968775851000f, 0.673049073280942000f, 0.680020342172095000f, 0.687030812154625000f,
206b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.694080519796882000f, 0.701169501531402000f, 0.708297793656032000f, 0.715465432335048000f,
207b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.722672453600255000f, 0.729918893352071000f, 0.737204787360605000f, 0.744530171266715000f,
208b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.751895080583051000f, 0.759299550695091000f, 0.766743616862161000f, 0.774227314218442000f,
209b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.781750677773962000f, 0.789313742415586000f, 0.796916542907978000f, 0.804559113894567000f,
210b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.812241489898490000f, 0.819963705323528000f, 0.827725794455034000f, 0.835527791460841000f,
211b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.843369730392169000f, 0.851251645184515000f, 0.859173569658532000f, 0.867135537520905000f,
212b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.875137582365205000f, 0.883179737672745000f, 0.891262036813419000f, 0.899384513046529000f,
213b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.907547199521614000f, 0.915750129279253000f, 0.923993335251873000f, 0.932276850264543000f,
214b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.940600707035753000f, 0.948964938178195000f, 0.957369576199527000f, 0.965814653503130000f,
215b39067696ad08a26bbe49b71a71f0546dc42a075msarett        0.974300202388861000f, 0.982826255053791000f, 0.991392843592940000f, 1.000000000000000000f,
216b39067696ad08a26bbe49b71a71f0546dc42a075msarett};
217b39067696ad08a26bbe49b71a71f0546dc42a075msarett
218b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic void build_table_linear_from_gamma(float* outTable, float exponent) {
219b39067696ad08a26bbe49b71a71f0546dc42a075msarett    for (float x = 0.0f; x <= 1.0f; x += (1.0f/255.0f)) {
220b39067696ad08a26bbe49b71a71f0546dc42a075msarett        *outTable++ = powf(x, exponent);
221b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
2229876ac5b3016e5353c072378ac1545a0a2270757msarett}
2239876ac5b3016e5353c072378ac1545a0a2270757msarett
224b39067696ad08a26bbe49b71a71f0546dc42a075msarett// Interpolating lookup in a variably sized table.
225b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic float interp_lut(float input, const float* table, int tableSize) {
226b39067696ad08a26bbe49b71a71f0546dc42a075msarett    float index = input * (tableSize - 1);
227b39067696ad08a26bbe49b71a71f0546dc42a075msarett    float diff = index - sk_float_floor2int(index);
228b39067696ad08a26bbe49b71a71f0546dc42a075msarett    return table[(int) sk_float_floor2int(index)] * (1.0f - diff) +
229b39067696ad08a26bbe49b71a71f0546dc42a075msarett            table[(int) sk_float_ceil2int(index)] * diff;
230b39067696ad08a26bbe49b71a71f0546dc42a075msarett}
231b39067696ad08a26bbe49b71a71f0546dc42a075msarett
232b39067696ad08a26bbe49b71a71f0546dc42a075msarett// outTable is always 256 entries, inTable may be larger or smaller.
233b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic void build_table_linear_from_gamma(float* outTable, const float* inTable,
234b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                          int inTableSize) {
235b39067696ad08a26bbe49b71a71f0546dc42a075msarett    if (256 == inTableSize) {
236b39067696ad08a26bbe49b71a71f0546dc42a075msarett        memcpy(outTable, inTable, sizeof(float) * 256);
237b39067696ad08a26bbe49b71a71f0546dc42a075msarett        return;
238b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
239b39067696ad08a26bbe49b71a71f0546dc42a075msarett
240b39067696ad08a26bbe49b71a71f0546dc42a075msarett    for (float x = 0.0f; x <= 1.0f; x += (1.0f/255.0f)) {
241b39067696ad08a26bbe49b71a71f0546dc42a075msarett        *outTable++ = interp_lut(x, inTable, inTableSize);
242b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
243b39067696ad08a26bbe49b71a71f0546dc42a075msarett}
244b39067696ad08a26bbe49b71a71f0546dc42a075msarett
245b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic void build_table_linear_from_gamma(float* outTable, float g, float a, float b, float c,
246b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                          float d, float e, float f) {
247b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // Y = (aX + b)^g + c  for X >= d
248b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // Y = eX + f          otherwise
249b39067696ad08a26bbe49b71a71f0546dc42a075msarett    for (float x = 0.0f; x <= 1.0f; x += (1.0f/255.0f)) {
250b39067696ad08a26bbe49b71a71f0546dc42a075msarett        if (x >= d) {
251b39067696ad08a26bbe49b71a71f0546dc42a075msarett            *outTable++ = powf(a * x + b, g) + c;
252b39067696ad08a26bbe49b71a71f0546dc42a075msarett        } else {
253b39067696ad08a26bbe49b71a71f0546dc42a075msarett            *outTable++ = e * x + f;
254b39067696ad08a26bbe49b71a71f0546dc42a075msarett        }
255b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
256b39067696ad08a26bbe49b71a71f0546dc42a075msarett}
257b39067696ad08a26bbe49b71a71f0546dc42a075msarett
258b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic constexpr uint8_t linear_to_srgb[1024] = {
259b39067696ad08a26bbe49b71a71f0546dc42a075msarett          0,   3,   6,  10,  13,  15,  18,  20,  22,  23,  25,  27,  28,  30,  31,  32,  34,  35,
260b39067696ad08a26bbe49b71a71f0546dc42a075msarett         36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  49,  50,  51,  52,
261b39067696ad08a26bbe49b71a71f0546dc42a075msarett         53,  53,  54,  55,  56,  56,  57,  58,  58,  59,  60,  61,  61,  62,  62,  63,  64,  64,
262b39067696ad08a26bbe49b71a71f0546dc42a075msarett         65,  66,  66,  67,  67,  68,  68,  69,  70,  70,  71,  71,  72,  72,  73,  73,  74,  74,
263b39067696ad08a26bbe49b71a71f0546dc42a075msarett         75,  76,  76,  77,  77,  78,  78,  79,  79,  79,  80,  80,  81,  81,  82,  82,  83,  83,
264b39067696ad08a26bbe49b71a71f0546dc42a075msarett         84,  84,  85,  85,  85,  86,  86,  87,  87,  88,  88,  88,  89,  89,  90,  90,  91,  91,
265b39067696ad08a26bbe49b71a71f0546dc42a075msarett         91,  92,  92,  93,  93,  93,  94,  94,  95,  95,  95,  96,  96,  97,  97,  97,  98,  98,
266b39067696ad08a26bbe49b71a71f0546dc42a075msarett         98,  99,  99,  99, 100, 100, 101, 101, 101, 102, 102, 102, 103, 103, 103, 104, 104, 104,
267b39067696ad08a26bbe49b71a71f0546dc42a075msarett        105, 105, 106, 106, 106, 107, 107, 107, 108, 108, 108, 109, 109, 109, 110, 110, 110, 110,
268b39067696ad08a26bbe49b71a71f0546dc42a075msarett        111, 111, 111, 112, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115, 115, 115, 116, 116,
269b39067696ad08a26bbe49b71a71f0546dc42a075msarett        116, 117, 117, 117, 118, 118, 118, 118, 119, 119, 119, 120, 120, 120, 121, 121, 121, 121,
270b39067696ad08a26bbe49b71a71f0546dc42a075msarett        122, 122, 122, 123, 123, 123, 123, 124, 124, 124, 125, 125, 125, 125, 126, 126, 126, 126,
271b39067696ad08a26bbe49b71a71f0546dc42a075msarett        127, 127, 127, 128, 128, 128, 128, 129, 129, 129, 129, 130, 130, 130, 130, 131, 131, 131,
272b39067696ad08a26bbe49b71a71f0546dc42a075msarett        131, 132, 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 135, 135, 135, 135, 136, 136,
273b39067696ad08a26bbe49b71a71f0546dc42a075msarett        136, 136, 137, 137, 137, 137, 138, 138, 138, 138, 138, 139, 139, 139, 139, 140, 140, 140,
274b39067696ad08a26bbe49b71a71f0546dc42a075msarett        140, 141, 141, 141, 141, 142, 142, 142, 142, 143, 143, 143, 143, 143, 144, 144, 144, 144,
275b39067696ad08a26bbe49b71a71f0546dc42a075msarett        145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 147, 147, 147, 148, 148, 148, 148, 148,
276b39067696ad08a26bbe49b71a71f0546dc42a075msarett        149, 149, 149, 149, 150, 150, 150, 150, 150, 151, 151, 151, 151, 152, 152, 152, 152, 152,
277b39067696ad08a26bbe49b71a71f0546dc42a075msarett        153, 153, 153, 153, 153, 154, 154, 154, 154, 155, 155, 155, 155, 155, 156, 156, 156, 156,
278b39067696ad08a26bbe49b71a71f0546dc42a075msarett        156, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158, 159, 159, 159, 159, 159, 160, 160,
279b39067696ad08a26bbe49b71a71f0546dc42a075msarett        160, 160, 160, 161, 161, 161, 161, 161, 162, 162, 162, 162, 162, 163, 163, 163, 163, 163,
280b39067696ad08a26bbe49b71a71f0546dc42a075msarett        164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 166, 166, 166, 166, 166, 167, 167, 167,
281b39067696ad08a26bbe49b71a71f0546dc42a075msarett        167, 167, 168, 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, 170, 170, 170, 170, 170,
282b39067696ad08a26bbe49b71a71f0546dc42a075msarett        171, 171, 171, 171, 171, 171, 172, 172, 172, 172, 172, 173, 173, 173, 173, 173, 173, 174,
283b39067696ad08a26bbe49b71a71f0546dc42a075msarett        174, 174, 174, 174, 175, 175, 175, 175, 175, 175, 176, 176, 176, 176, 176, 177, 177, 177,
284b39067696ad08a26bbe49b71a71f0546dc42a075msarett        177, 177, 177, 178, 178, 178, 178, 178, 178, 179, 179, 179, 179, 179, 179, 180, 180, 180,
285b39067696ad08a26bbe49b71a71f0546dc42a075msarett        180, 180, 181, 181, 181, 181, 181, 181, 182, 182, 182, 182, 182, 182, 183, 183, 183, 183,
286b39067696ad08a26bbe49b71a71f0546dc42a075msarett        183, 183, 184, 184, 184, 184, 184, 184, 185, 185, 185, 185, 185, 185, 186, 186, 186, 186,
287b39067696ad08a26bbe49b71a71f0546dc42a075msarett        186, 186, 187, 187, 187, 187, 187, 187, 188, 188, 188, 188, 188, 188, 189, 189, 189, 189,
288b39067696ad08a26bbe49b71a71f0546dc42a075msarett        189, 189, 190, 190, 190, 190, 190, 190, 191, 191, 191, 191, 191, 191, 191, 192, 192, 192,
289b39067696ad08a26bbe49b71a71f0546dc42a075msarett        192, 192, 192, 193, 193, 193, 193, 193, 193, 194, 194, 194, 194, 194, 194, 194, 195, 195,
290b39067696ad08a26bbe49b71a71f0546dc42a075msarett        195, 195, 195, 195, 196, 196, 196, 196, 196, 196, 197, 197, 197, 197, 197, 197, 197, 198,
291b39067696ad08a26bbe49b71a71f0546dc42a075msarett        198, 198, 198, 198, 198, 199, 199, 199, 199, 199, 199, 199, 200, 200, 200, 200, 200, 200,
292b39067696ad08a26bbe49b71a71f0546dc42a075msarett        200, 201, 201, 201, 201, 201, 201, 202, 202, 202, 202, 202, 202, 202, 203, 203, 203, 203,
293b39067696ad08a26bbe49b71a71f0546dc42a075msarett        203, 203, 203, 204, 204, 204, 204, 204, 204, 204, 205, 205, 205, 205, 205, 205, 206, 206,
294b39067696ad08a26bbe49b71a71f0546dc42a075msarett        206, 206, 206, 206, 206, 207, 207, 207, 207, 207, 207, 207, 208, 208, 208, 208, 208, 208,
295b39067696ad08a26bbe49b71a71f0546dc42a075msarett        208, 209, 209, 209, 209, 209, 209, 209, 210, 210, 210, 210, 210, 210, 210, 211, 211, 211,
296b39067696ad08a26bbe49b71a71f0546dc42a075msarett        211, 211, 211, 211, 212, 212, 212, 212, 212, 212, 212, 212, 213, 213, 213, 213, 213, 213,
297b39067696ad08a26bbe49b71a71f0546dc42a075msarett        213, 214, 214, 214, 214, 214, 214, 214, 215, 215, 215, 215, 215, 215, 215, 216, 216, 216,
298b39067696ad08a26bbe49b71a71f0546dc42a075msarett        216, 216, 216, 216, 216, 217, 217, 217, 217, 217, 217, 217, 218, 218, 218, 218, 218, 218,
299b39067696ad08a26bbe49b71a71f0546dc42a075msarett        218, 219, 219, 219, 219, 219, 219, 219, 219, 220, 220, 220, 220, 220, 220, 220, 221, 221,
300b39067696ad08a26bbe49b71a71f0546dc42a075msarett        221, 221, 221, 221, 221, 221, 222, 222, 222, 222, 222, 222, 222, 222, 223, 223, 223, 223,
301b39067696ad08a26bbe49b71a71f0546dc42a075msarett        223, 223, 223, 224, 224, 224, 224, 224, 224, 224, 224, 225, 225, 225, 225, 225, 225, 225,
302b39067696ad08a26bbe49b71a71f0546dc42a075msarett        225, 226, 226, 226, 226, 226, 226, 226, 227, 227, 227, 227, 227, 227, 227, 227, 228, 228,
303b39067696ad08a26bbe49b71a71f0546dc42a075msarett        228, 228, 228, 228, 228, 228, 229, 229, 229, 229, 229, 229, 229, 229, 230, 230, 230, 230,
304b39067696ad08a26bbe49b71a71f0546dc42a075msarett        230, 230, 230, 230, 231, 231, 231, 231, 231, 231, 231, 231, 232, 232, 232, 232, 232, 232,
305b39067696ad08a26bbe49b71a71f0546dc42a075msarett        232, 232, 233, 233, 233, 233, 233, 233, 233, 233, 234, 234, 234, 234, 234, 234, 234, 234,
306b39067696ad08a26bbe49b71a71f0546dc42a075msarett        235, 235, 235, 235, 235, 235, 235, 235, 236, 236, 236, 236, 236, 236, 236, 236, 236, 237,
307b39067696ad08a26bbe49b71a71f0546dc42a075msarett        237, 237, 237, 237, 237, 237, 237, 238, 238, 238, 238, 238, 238, 238, 238, 239, 239, 239,
308b39067696ad08a26bbe49b71a71f0546dc42a075msarett        239, 239, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 240, 240, 241, 241, 241, 241,
309b39067696ad08a26bbe49b71a71f0546dc42a075msarett        241, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243, 243, 243, 243,
310b39067696ad08a26bbe49b71a71f0546dc42a075msarett        243, 243, 243, 243, 244, 244, 244, 244, 244, 244, 244, 244, 245, 245, 245, 245, 245, 245,
311b39067696ad08a26bbe49b71a71f0546dc42a075msarett        245, 245, 245, 246, 246, 246, 246, 246, 246, 246, 246, 246, 247, 247, 247, 247, 247, 247,
312b39067696ad08a26bbe49b71a71f0546dc42a075msarett        247, 247, 248, 248, 248, 248, 248, 248, 248, 248, 248, 249, 249, 249, 249, 249, 249, 249,
313b39067696ad08a26bbe49b71a71f0546dc42a075msarett        249, 249, 250, 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, 251, 251,
314b39067696ad08a26bbe49b71a71f0546dc42a075msarett        251, 251, 252, 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253, 253,
315b39067696ad08a26bbe49b71a71f0546dc42a075msarett        253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 255, 255, 255, 255, 255
316b39067696ad08a26bbe49b71a71f0546dc42a075msarett};
317b39067696ad08a26bbe49b71a71f0546dc42a075msarett
318b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic constexpr uint8_t linear_to_2dot2[1024] = {
319b39067696ad08a26bbe49b71a71f0546dc42a075msarett          0,  11,  15,  18,  21,  23,  25,  26,  28,  30,  31,  32,  34,  35,  36,  37,  39,  40,
320b39067696ad08a26bbe49b71a71f0546dc42a075msarett         41,  42,  43,  44,  45,  45,  46,  47,  48,  49,  50,  50,  51,  52,  53,  54,  54,  55,
321b39067696ad08a26bbe49b71a71f0546dc42a075msarett         56,  56,  57,  58,  58,  59,  60,  60,  61,  62,  62,  63,  63,  64,  65,  65,  66,  66,
322b39067696ad08a26bbe49b71a71f0546dc42a075msarett         67,  68,  68,  69,  69,  70,  70,  71,  71,  72,  72,  73,  73,  74,  74,  75,  75,  76,
323b39067696ad08a26bbe49b71a71f0546dc42a075msarett         76,  77,  77,  78,  78,  79,  79,  80,  80,  81,  81,  81,  82,  82,  83,  83,  84,  84,
324b39067696ad08a26bbe49b71a71f0546dc42a075msarett         84,  85,  85,  86,  86,  87,  87,  87,  88,  88,  89,  89,  89,  90,  90,  91,  91,  91,
325b39067696ad08a26bbe49b71a71f0546dc42a075msarett         92,  92,  93,  93,  93,  94,  94,  94,  95,  95,  96,  96,  96,  97,  97,  97,  98,  98,
326b39067696ad08a26bbe49b71a71f0546dc42a075msarett         98,  99,  99,  99, 100, 100, 101, 101, 101, 102, 102, 102, 103, 103, 103, 104, 104, 104,
327b39067696ad08a26bbe49b71a71f0546dc42a075msarett        105, 105, 105, 106, 106, 106, 107, 107, 107, 108, 108, 108, 108, 109, 109, 109, 110, 110,
328b39067696ad08a26bbe49b71a71f0546dc42a075msarett        110, 111, 111, 111, 112, 112, 112, 112, 113, 113, 113, 114, 114, 114, 115, 115, 115, 115,
329b39067696ad08a26bbe49b71a71f0546dc42a075msarett        116, 116, 116, 117, 117, 117, 117, 118, 118, 118, 119, 119, 119, 119, 120, 120, 120, 121,
330b39067696ad08a26bbe49b71a71f0546dc42a075msarett        121, 121, 121, 122, 122, 122, 123, 123, 123, 123, 124, 124, 124, 124, 125, 125, 125, 125,
331b39067696ad08a26bbe49b71a71f0546dc42a075msarett        126, 126, 126, 127, 127, 127, 127, 128, 128, 128, 128, 129, 129, 129, 129, 130, 130, 130,
332b39067696ad08a26bbe49b71a71f0546dc42a075msarett        130, 131, 131, 131, 131, 132, 132, 132, 132, 133, 133, 133, 133, 134, 134, 134, 134, 135,
333b39067696ad08a26bbe49b71a71f0546dc42a075msarett        135, 135, 135, 136, 136, 136, 136, 137, 137, 137, 137, 138, 138, 138, 138, 138, 139, 139,
334b39067696ad08a26bbe49b71a71f0546dc42a075msarett        139, 139, 140, 140, 140, 140, 141, 141, 141, 141, 142, 142, 142, 142, 142, 143, 143, 143,
335b39067696ad08a26bbe49b71a71f0546dc42a075msarett        143, 144, 144, 144, 144, 144, 145, 145, 145, 145, 146, 146, 146, 146, 146, 147, 147, 147,
336b39067696ad08a26bbe49b71a71f0546dc42a075msarett        147, 148, 148, 148, 148, 148, 149, 149, 149, 149, 149, 150, 150, 150, 150, 151, 151, 151,
337b39067696ad08a26bbe49b71a71f0546dc42a075msarett        151, 151, 152, 152, 152, 152, 152, 153, 153, 153, 153, 154, 154, 154, 154, 154, 155, 155,
338b39067696ad08a26bbe49b71a71f0546dc42a075msarett        155, 155, 155, 156, 156, 156, 156, 156, 157, 157, 157, 157, 157, 158, 158, 158, 158, 158,
339b39067696ad08a26bbe49b71a71f0546dc42a075msarett        159, 159, 159, 159, 159, 160, 160, 160, 160, 160, 161, 161, 161, 161, 161, 162, 162, 162,
340b39067696ad08a26bbe49b71a71f0546dc42a075msarett        162, 162, 163, 163, 163, 163, 163, 164, 164, 164, 164, 164, 165, 165, 165, 165, 165, 165,
341b39067696ad08a26bbe49b71a71f0546dc42a075msarett        166, 166, 166, 166, 166, 167, 167, 167, 167, 167, 168, 168, 168, 168, 168, 168, 169, 169,
342b39067696ad08a26bbe49b71a71f0546dc42a075msarett        169, 169, 169, 170, 170, 170, 170, 170, 171, 171, 171, 171, 171, 171, 172, 172, 172, 172,
343b39067696ad08a26bbe49b71a71f0546dc42a075msarett        172, 173, 173, 173, 173, 173, 173, 174, 174, 174, 174, 174, 174, 175, 175, 175, 175, 175,
344b39067696ad08a26bbe49b71a71f0546dc42a075msarett        176, 176, 176, 176, 176, 176, 177, 177, 177, 177, 177, 177, 178, 178, 178, 178, 178, 179,
345b39067696ad08a26bbe49b71a71f0546dc42a075msarett        179, 179, 179, 179, 179, 180, 180, 180, 180, 180, 180, 181, 181, 181, 181, 181, 181, 182,
346b39067696ad08a26bbe49b71a71f0546dc42a075msarett        182, 182, 182, 182, 182, 183, 183, 183, 183, 183, 183, 184, 184, 184, 184, 184, 185, 185,
347b39067696ad08a26bbe49b71a71f0546dc42a075msarett        185, 185, 185, 185, 186, 186, 186, 186, 186, 186, 186, 187, 187, 187, 187, 187, 187, 188,
348b39067696ad08a26bbe49b71a71f0546dc42a075msarett        188, 188, 188, 188, 188, 189, 189, 189, 189, 189, 189, 190, 190, 190, 190, 190, 190, 191,
349b39067696ad08a26bbe49b71a71f0546dc42a075msarett        191, 191, 191, 191, 191, 192, 192, 192, 192, 192, 192, 192, 193, 193, 193, 193, 193, 193,
350b39067696ad08a26bbe49b71a71f0546dc42a075msarett        194, 194, 194, 194, 194, 194, 195, 195, 195, 195, 195, 195, 195, 196, 196, 196, 196, 196,
351b39067696ad08a26bbe49b71a71f0546dc42a075msarett        196, 197, 197, 197, 197, 197, 197, 197, 198, 198, 198, 198, 198, 198, 199, 199, 199, 199,
352b39067696ad08a26bbe49b71a71f0546dc42a075msarett        199, 199, 199, 200, 200, 200, 200, 200, 200, 201, 201, 201, 201, 201, 201, 201, 202, 202,
353b39067696ad08a26bbe49b71a71f0546dc42a075msarett        202, 202, 202, 202, 202, 203, 203, 203, 203, 203, 203, 204, 204, 204, 204, 204, 204, 204,
354b39067696ad08a26bbe49b71a71f0546dc42a075msarett        205, 205, 205, 205, 205, 205, 205, 206, 206, 206, 206, 206, 206, 206, 207, 207, 207, 207,
355b39067696ad08a26bbe49b71a71f0546dc42a075msarett        207, 207, 207, 208, 208, 208, 208, 208, 208, 209, 209, 209, 209, 209, 209, 209, 210, 210,
356b39067696ad08a26bbe49b71a71f0546dc42a075msarett        210, 210, 210, 210, 210, 211, 211, 211, 211, 211, 211, 211, 212, 212, 212, 212, 212, 212,
357b39067696ad08a26bbe49b71a71f0546dc42a075msarett        212, 213, 213, 213, 213, 213, 213, 213, 213, 214, 214, 214, 214, 214, 214, 214, 215, 215,
358b39067696ad08a26bbe49b71a71f0546dc42a075msarett        215, 215, 215, 215, 215, 216, 216, 216, 216, 216, 216, 216, 217, 217, 217, 217, 217, 217,
359b39067696ad08a26bbe49b71a71f0546dc42a075msarett        217, 218, 218, 218, 218, 218, 218, 218, 218, 219, 219, 219, 219, 219, 219, 219, 220, 220,
360b39067696ad08a26bbe49b71a71f0546dc42a075msarett        220, 220, 220, 220, 220, 221, 221, 221, 221, 221, 221, 221, 221, 222, 222, 222, 222, 222,
361b39067696ad08a26bbe49b71a71f0546dc42a075msarett        222, 222, 223, 223, 223, 223, 223, 223, 223, 223, 224, 224, 224, 224, 224, 224, 224, 225,
362b39067696ad08a26bbe49b71a71f0546dc42a075msarett        225, 225, 225, 225, 225, 225, 225, 226, 226, 226, 226, 226, 226, 226, 226, 227, 227, 227,
363b39067696ad08a26bbe49b71a71f0546dc42a075msarett        227, 227, 227, 227, 228, 228, 228, 228, 228, 228, 228, 228, 229, 229, 229, 229, 229, 229,
364b39067696ad08a26bbe49b71a71f0546dc42a075msarett        229, 229, 230, 230, 230, 230, 230, 230, 230, 230, 231, 231, 231, 231, 231, 231, 231, 232,
365b39067696ad08a26bbe49b71a71f0546dc42a075msarett        232, 232, 232, 232, 232, 232, 232, 233, 233, 233, 233, 233, 233, 233, 233, 234, 234, 234,
366b39067696ad08a26bbe49b71a71f0546dc42a075msarett        234, 234, 234, 234, 234, 235, 235, 235, 235, 235, 235, 235, 235, 236, 236, 236, 236, 236,
367b39067696ad08a26bbe49b71a71f0546dc42a075msarett        236, 236, 236, 237, 237, 237, 237, 237, 237, 237, 237, 238, 238, 238, 238, 238, 238, 238,
368b39067696ad08a26bbe49b71a71f0546dc42a075msarett        238, 238, 239, 239, 239, 239, 239, 239, 239, 239, 240, 240, 240, 240, 240, 240, 240, 240,
369b39067696ad08a26bbe49b71a71f0546dc42a075msarett        241, 241, 241, 241, 241, 241, 241, 241, 242, 242, 242, 242, 242, 242, 242, 242, 243, 243,
370b39067696ad08a26bbe49b71a71f0546dc42a075msarett        243, 243, 243, 243, 243, 243, 243, 244, 244, 244, 244, 244, 244, 244, 244, 245, 245, 245,
371b39067696ad08a26bbe49b71a71f0546dc42a075msarett        245, 245, 245, 245, 245, 245, 246, 246, 246, 246, 246, 246, 246, 246, 247, 247, 247, 247,
372b39067696ad08a26bbe49b71a71f0546dc42a075msarett        247, 247, 247, 247, 248, 248, 248, 248, 248, 248, 248, 248, 248, 249, 249, 249, 249, 249,
373b39067696ad08a26bbe49b71a71f0546dc42a075msarett        249, 249, 249, 249, 250, 250, 250, 250, 250, 250, 250, 250, 251, 251, 251, 251, 251, 251,
374b39067696ad08a26bbe49b71a71f0546dc42a075msarett        251, 251, 251, 252, 252, 252, 252, 252, 252, 252, 252, 252, 253, 253, 253, 253, 253, 253,
375b39067696ad08a26bbe49b71a71f0546dc42a075msarett        253, 253, 254, 254, 254, 254, 254, 254, 254, 254, 254, 255, 255, 255, 255, 255,
376b39067696ad08a26bbe49b71a71f0546dc42a075msarett};
377b39067696ad08a26bbe49b71a71f0546dc42a075msarett
378a9e878c836994bce695274b4c28890290139dcdfmsarett// Expand range from 0-1 to 0-255, then convert.
379b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic uint8_t clamp_normalized_float_to_byte(float v) {
380dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    // The ordering of the logic is a little strange here in order
381dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    // to make sure we convert NaNs to 0.
3829876ac5b3016e5353c072378ac1545a0a2270757msarett    v = v * 255.0f;
383a9e878c836994bce695274b4c28890290139dcdfmsarett    if (v >= 254.5f) {
3849876ac5b3016e5353c072378ac1545a0a2270757msarett        return 255;
385dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    } else if (v >= 0.5f) {
3869876ac5b3016e5353c072378ac1545a0a2270757msarett        return (uint8_t) (v + 0.5f);
387dea0340cadb759932e53416a657f5ea75fee8b5fmsarett    } else {
388dea0340cadb759932e53416a657f5ea75fee8b5fmsarett        return 0;
3899876ac5b3016e5353c072378ac1545a0a2270757msarett    }
3909876ac5b3016e5353c072378ac1545a0a2270757msarett}
3919876ac5b3016e5353c072378ac1545a0a2270757msarett
392b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic void build_table_linear_to_gamma(uint8_t* outTable, int outTableSize, float exponent) {
393b39067696ad08a26bbe49b71a71f0546dc42a075msarett    float toGammaExp = 1.0f / exponent;
394b39067696ad08a26bbe49b71a71f0546dc42a075msarett
395b39067696ad08a26bbe49b71a71f0546dc42a075msarett    for (int i = 0; i < outTableSize; i++) {
396b39067696ad08a26bbe49b71a71f0546dc42a075msarett        float x = ((float) i) * (1.0f / ((float) (outTableSize - 1)));
397b39067696ad08a26bbe49b71a71f0546dc42a075msarett        outTable[i] = clamp_normalized_float_to_byte(powf(x, toGammaExp));
398b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
399dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett}
400dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
401dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// Inverse table lookup.  Ex: what index corresponds to the input value?  This will
402dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// have strange results when the table is non-increasing.  But any sane gamma
403dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// function will be increasing.
404b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic float inverse_interp_lut(float input, float* table, int tableSize) {
405dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    if (input <= table[0]) {
406dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        return table[0];
407dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    } else if (input >= table[tableSize - 1]) {
408dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        return 1.0f;
409dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    }
410dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
411b39067696ad08a26bbe49b71a71f0546dc42a075msarett    for (int i = 1; i < tableSize; i++) {
412dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        if (table[i] >= input) {
413dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            // We are guaranteed that input is greater than table[i - 1].
414dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            float diff = input - table[i - 1];
415dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            float distance = table[i] - table[i - 1];
416dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            float index = (i - 1) + diff / distance;
417dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            return index / (tableSize - 1);
418dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        }
419dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    }
420dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
421dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    // Should be unreachable, since we'll return before the loop if input is
422dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    // larger than the last entry.
423dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    SkASSERT(false);
424dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    return 0.0f;
425dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett}
426dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
427b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic void build_table_linear_to_gamma(uint8_t* outTable, int outTableSize, float* inTable,
428b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                        int inTableSize) {
429b39067696ad08a26bbe49b71a71f0546dc42a075msarett    for (int i = 0; i < outTableSize; i++) {
430b39067696ad08a26bbe49b71a71f0546dc42a075msarett        float x = ((float) i) * (1.0f / ((float) (outTableSize - 1)));
431b39067696ad08a26bbe49b71a71f0546dc42a075msarett        float y = inverse_interp_lut(x, inTable, inTableSize);
432b39067696ad08a26bbe49b71a71f0546dc42a075msarett        outTable[i] = clamp_normalized_float_to_byte(y);
433b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
434b39067696ad08a26bbe49b71a71f0546dc42a075msarett}
435dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
436b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic float inverse_parametric(float x, float g, float a, float b, float c, float d, float e,
437b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                float f) {
438b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // We need to take the inverse of the following piecewise function.
439b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // Y = (aX + b)^g + c  for X >= d
440b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // Y = eX + f          otherwise
441b39067696ad08a26bbe49b71a71f0546dc42a075msarett
442b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // Assume that the gamma function is continuous, or this won't make much sense anyway.
443b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // Plug in |d| to the first equation to calculate the new piecewise interval.
444b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // Then simply use the inverse of the original functions.
445b39067696ad08a26bbe49b71a71f0546dc42a075msarett    float interval = e * d + f;
446b39067696ad08a26bbe49b71a71f0546dc42a075msarett    if (x < interval) {
447b39067696ad08a26bbe49b71a71f0546dc42a075msarett        // X = (Y - F) / E
448b39067696ad08a26bbe49b71a71f0546dc42a075msarett        if (0.0f == e) {
449b39067696ad08a26bbe49b71a71f0546dc42a075msarett            // The gamma curve for this segment is constant, so the inverse is undefined.
450b39067696ad08a26bbe49b71a71f0546dc42a075msarett            // Since this is the lower segment, guess zero.
451b39067696ad08a26bbe49b71a71f0546dc42a075msarett            return 0.0f;
452b39067696ad08a26bbe49b71a71f0546dc42a075msarett        }
453b39067696ad08a26bbe49b71a71f0546dc42a075msarett
454b39067696ad08a26bbe49b71a71f0546dc42a075msarett        return (x - f) / e;
455b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
456b39067696ad08a26bbe49b71a71f0546dc42a075msarett
457b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // X = ((Y - C)^(1 / G) - B) / A
458b39067696ad08a26bbe49b71a71f0546dc42a075msarett    if (0.0f == a || 0.0f == g) {
459b39067696ad08a26bbe49b71a71f0546dc42a075msarett        // The gamma curve for this segment is constant, so the inverse is undefined.
460b39067696ad08a26bbe49b71a71f0546dc42a075msarett        // Since this is the upper segment, guess one.
461b39067696ad08a26bbe49b71a71f0546dc42a075msarett        return 1.0f;
462b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
463b39067696ad08a26bbe49b71a71f0546dc42a075msarett
464b39067696ad08a26bbe49b71a71f0546dc42a075msarett    return (powf(x - c, 1.0f / g) - b) / a;
465b39067696ad08a26bbe49b71a71f0546dc42a075msarett}
466b39067696ad08a26bbe49b71a71f0546dc42a075msarett
467b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic void build_table_linear_to_gamma(uint8_t* outTable, int outTableSize, float g, float a,
468b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                        float b, float c, float d, float e, float f) {
469b39067696ad08a26bbe49b71a71f0546dc42a075msarett    for (int i = 0; i < outTableSize; i++) {
470b39067696ad08a26bbe49b71a71f0546dc42a075msarett        float x = ((float) i) * (1.0f / ((float) (outTableSize - 1)));
471b39067696ad08a26bbe49b71a71f0546dc42a075msarett        float y = inverse_parametric(x, g, a, b, c, d, e, f);
472b39067696ad08a26bbe49b71a71f0546dc42a075msarett        outTable[i] = clamp_normalized_float_to_byte(y);
473b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
474b39067696ad08a26bbe49b71a71f0546dc42a075msarett}
475b39067696ad08a26bbe49b71a71f0546dc42a075msarett
476b39067696ad08a26bbe49b71a71f0546dc42a075msarettSkDefaultXform::SkDefaultXform(const sk_sp<SkColorSpace>& srcSpace, const SkMatrix44& srcToDst,
477b39067696ad08a26bbe49b71a71f0546dc42a075msarett                               const sk_sp<SkColorSpace>& dstSpace)
4780f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    : fColorLUT(sk_ref_sp((SkColorLookUpTable*) as_CSB(srcSpace)->colorLUT()))
4790f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    , fSrcToDst(srcToDst)
480b39067696ad08a26bbe49b71a71f0546dc42a075msarett{
481b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // Build tables to transform src gamma to linear.
482b39067696ad08a26bbe49b71a71f0546dc42a075msarett    switch (srcSpace->gammaNamed()) {
483b39067696ad08a26bbe49b71a71f0546dc42a075msarett        case SkColorSpace::kSRGB_GammaNamed:
484b39067696ad08a26bbe49b71a71f0546dc42a075msarett            fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = sk_linear_from_srgb;
485b39067696ad08a26bbe49b71a71f0546dc42a075msarett            break;
486b39067696ad08a26bbe49b71a71f0546dc42a075msarett        case SkColorSpace::k2Dot2Curve_GammaNamed:
487b39067696ad08a26bbe49b71a71f0546dc42a075msarett            fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = sk_linear_from_2dot2;
488b39067696ad08a26bbe49b71a71f0546dc42a075msarett            break;
489b39067696ad08a26bbe49b71a71f0546dc42a075msarett        case SkColorSpace::kLinear_GammaNamed:
490b39067696ad08a26bbe49b71a71f0546dc42a075msarett            build_table_linear_from_gamma(fSrcGammaTableStorage, 1.0f);
491b39067696ad08a26bbe49b71a71f0546dc42a075msarett            fSrcGammaTables[0] = fSrcGammaTables[1] = fSrcGammaTables[2] = fSrcGammaTableStorage;
492b39067696ad08a26bbe49b71a71f0546dc42a075msarett            break;
493b39067696ad08a26bbe49b71a71f0546dc42a075msarett        default: {
494b39067696ad08a26bbe49b71a71f0546dc42a075msarett            const SkGammas* gammas = as_CSB(srcSpace)->gammas();
495b39067696ad08a26bbe49b71a71f0546dc42a075msarett            SkASSERT(gammas);
496b39067696ad08a26bbe49b71a71f0546dc42a075msarett
497b39067696ad08a26bbe49b71a71f0546dc42a075msarett            for (int i = 0; i < 3; i++) {
498b39067696ad08a26bbe49b71a71f0546dc42a075msarett                const SkGammaCurve& curve = (*gammas)[i];
499b39067696ad08a26bbe49b71a71f0546dc42a075msarett
500b39067696ad08a26bbe49b71a71f0546dc42a075msarett                if (i > 0) {
501b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // Check if this curve matches the first curve.  In this case, we can
502b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // share the same table pointer.  Logically, this should almost always
503b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // be true.  I've never seen a profile where all three gamma curves
504b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // didn't match.  But it is possible that they won't.
505b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // TODO (msarett):
506b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // This comparison won't catch the case where each gamma curve has a
507b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // pointer to its own look-up table, but the tables actually match.
508b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // Should we perform a deep compare of gamma tables here?  Or should
509b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // we catch this when parsing the profile?  Or should we not worry
510b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // about a bit of redundant work?
511b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    if (curve.quickEquals((*gammas)[0])) {
512b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        fSrcGammaTables[i] = fSrcGammaTables[0];
513b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        continue;
514b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    }
515b39067696ad08a26bbe49b71a71f0546dc42a075msarett                }
516b39067696ad08a26bbe49b71a71f0546dc42a075msarett
517b39067696ad08a26bbe49b71a71f0546dc42a075msarett                if (curve.isNamed()) {
518b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    switch (curve.fNamed) {
519b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        case SkColorSpace::kSRGB_GammaNamed:
520b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            fSrcGammaTables[i] = sk_linear_from_srgb;
521b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            break;
522b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        case SkColorSpace::k2Dot2Curve_GammaNamed:
523b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            fSrcGammaTables[i] = sk_linear_from_2dot2;
524b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            break;
525b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        case SkColorSpace::kLinear_GammaNamed:
526b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256], 1.0f);
527b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
528b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            break;
529b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        default:
530b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            SkASSERT(false);
531b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            break;
532b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    }
533b39067696ad08a26bbe49b71a71f0546dc42a075msarett                } else if (curve.isValue()) {
534b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256], curve.fValue);
535b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
536b39067696ad08a26bbe49b71a71f0546dc42a075msarett                } else if (curve.isTable()) {
537b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256],
538b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                                  curve.fTable.get(), curve.fTableSize);
539b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
540dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                } else {
541b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    SkASSERT(curve.isParametric());
542b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    build_table_linear_from_gamma(&fSrcGammaTableStorage[i * 256], curve.fG,
543b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                                  curve.fA, curve.fB, curve.fC, curve.fD, curve.fE,
544b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                                  curve.fF);
545b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    fSrcGammaTables[i] = &fSrcGammaTableStorage[i * 256];
546b39067696ad08a26bbe49b71a71f0546dc42a075msarett                }
547b39067696ad08a26bbe49b71a71f0546dc42a075msarett            }
548b39067696ad08a26bbe49b71a71f0546dc42a075msarett        }
549b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
550b39067696ad08a26bbe49b71a71f0546dc42a075msarett
551b39067696ad08a26bbe49b71a71f0546dc42a075msarett    // Build tables to transform linear to dst gamma.
552b39067696ad08a26bbe49b71a71f0546dc42a075msarett    switch (dstSpace->gammaNamed()) {
553b39067696ad08a26bbe49b71a71f0546dc42a075msarett        case SkColorSpace::kSRGB_GammaNamed:
554b39067696ad08a26bbe49b71a71f0546dc42a075msarett            fDstGammaTables[0] = fDstGammaTables[1] = fDstGammaTables[2] = linear_to_srgb;
555b39067696ad08a26bbe49b71a71f0546dc42a075msarett            break;
556b39067696ad08a26bbe49b71a71f0546dc42a075msarett        case SkColorSpace::k2Dot2Curve_GammaNamed:
557b39067696ad08a26bbe49b71a71f0546dc42a075msarett            fDstGammaTables[0] = fDstGammaTables[1] = fDstGammaTables[2] = linear_to_2dot2;
558b39067696ad08a26bbe49b71a71f0546dc42a075msarett            break;
559b39067696ad08a26bbe49b71a71f0546dc42a075msarett        case SkColorSpace::kLinear_GammaNamed:
560b39067696ad08a26bbe49b71a71f0546dc42a075msarett            build_table_linear_to_gamma(fDstGammaTableStorage, kDstGammaTableSize, 1.0f);
561b39067696ad08a26bbe49b71a71f0546dc42a075msarett            fDstGammaTables[0] = fDstGammaTables[1] = fDstGammaTables[2] = fDstGammaTableStorage;
562b39067696ad08a26bbe49b71a71f0546dc42a075msarett            break;
563b39067696ad08a26bbe49b71a71f0546dc42a075msarett        default: {
564b39067696ad08a26bbe49b71a71f0546dc42a075msarett            const SkGammas* gammas = as_CSB(dstSpace)->gammas();
565b39067696ad08a26bbe49b71a71f0546dc42a075msarett            SkASSERT(gammas);
566b39067696ad08a26bbe49b71a71f0546dc42a075msarett
567b39067696ad08a26bbe49b71a71f0546dc42a075msarett            for (int i = 0; i < 3; i++) {
568b39067696ad08a26bbe49b71a71f0546dc42a075msarett                const SkGammaCurve& curve = (*gammas)[i];
569b39067696ad08a26bbe49b71a71f0546dc42a075msarett
570b39067696ad08a26bbe49b71a71f0546dc42a075msarett                if (i > 0) {
571b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // Check if this curve matches the first curve.  In this case, we can
572b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // share the same table pointer.  Logically, this should almost always
573b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // be true.  I've never seen a profile where all three gamma curves
574b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // didn't match.  But it is possible that they won't.
575b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // TODO (msarett):
576b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // This comparison won't catch the case where each gamma curve has a
577b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // pointer to its own look-up table (but the tables actually match).
578b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // Should we perform a deep compare of gamma tables here?  Or should
579b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // we catch this when parsing the profile?  Or should we not worry
580b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    // about a bit of redundant work?
581b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    if (curve.quickEquals((*gammas)[0])) {
582b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        fDstGammaTables[i] = fDstGammaTables[0];
583b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        continue;
584b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    }
585b39067696ad08a26bbe49b71a71f0546dc42a075msarett                }
586b39067696ad08a26bbe49b71a71f0546dc42a075msarett
587b39067696ad08a26bbe49b71a71f0546dc42a075msarett                if (curve.isNamed()) {
588b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    switch (curve.fNamed) {
589b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        case SkColorSpace::kSRGB_GammaNamed:
590b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            fDstGammaTables[i] = linear_to_srgb;
591b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            break;
592b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        case SkColorSpace::k2Dot2Curve_GammaNamed:
593b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            fDstGammaTables[i] = linear_to_2dot2;
594b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            break;
595b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        case SkColorSpace::kLinear_GammaNamed:
596b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            build_table_linear_to_gamma(
597b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                    &fDstGammaTableStorage[i * kDstGammaTableSize],
598b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                    kDstGammaTableSize, 1.0f);
599b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
600b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            break;
601b39067696ad08a26bbe49b71a71f0546dc42a075msarett                        default:
602b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            SkASSERT(false);
603b39067696ad08a26bbe49b71a71f0546dc42a075msarett                            break;
604c4ce6b592487305de251bbebaf8eeee38371b877msarett                    }
605b39067696ad08a26bbe49b71a71f0546dc42a075msarett                } else if (curve.isValue()) {
606b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstGammaTableSize],
607b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                                kDstGammaTableSize, curve.fValue);
608b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
609b39067696ad08a26bbe49b71a71f0546dc42a075msarett                } else if (curve.isTable()) {
610b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstGammaTableSize],
611b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                                kDstGammaTableSize, curve.fTable.get(),
612b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                                curve.fTableSize);
613b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
614b39067696ad08a26bbe49b71a71f0546dc42a075msarett                } else {
615b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    SkASSERT(curve.isParametric());
616b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    build_table_linear_to_gamma(&fDstGammaTableStorage[i * kDstGammaTableSize],
617b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                                kDstGammaTableSize, curve.fG, curve.fA, curve.fB,
618b39067696ad08a26bbe49b71a71f0546dc42a075msarett                                                curve.fC, curve.fD, curve.fE, curve.fF);
619b39067696ad08a26bbe49b71a71f0546dc42a075msarett                    fDstGammaTables[i] = &fDstGammaTableStorage[i * kDstGammaTableSize];
620dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                }
621dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            }
622dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        }
623b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
624b39067696ad08a26bbe49b71a71f0546dc42a075msarett}
625b39067696ad08a26bbe49b71a71f0546dc42a075msarett
6260f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarettstatic float byte_to_float(uint8_t byte) {
6270f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    return ((float) byte) * (1.0f / 255.0f);
6280f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett}
6290f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
630b39067696ad08a26bbe49b71a71f0546dc42a075msarett// Clamp to the 0-1 range.
631b39067696ad08a26bbe49b71a71f0546dc42a075msarettstatic float clamp_normalized_float(float v) {
632b39067696ad08a26bbe49b71a71f0546dc42a075msarett    if (v > 1.0f) {
633b39067696ad08a26bbe49b71a71f0546dc42a075msarett        return 1.0f;
634b39067696ad08a26bbe49b71a71f0546dc42a075msarett    } else if ((v < 0.0f) || (v != v)) {
635b39067696ad08a26bbe49b71a71f0546dc42a075msarett        return 0.0f;
636b39067696ad08a26bbe49b71a71f0546dc42a075msarett    } else {
637b39067696ad08a26bbe49b71a71f0546dc42a075msarett        return v;
638b39067696ad08a26bbe49b71a71f0546dc42a075msarett    }
639b39067696ad08a26bbe49b71a71f0546dc42a075msarett}
640b39067696ad08a26bbe49b71a71f0546dc42a075msarett
6410f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarettstatic void interp_3d_clut(float dst[3], float src[3], const SkColorLookUpTable* colorLUT) {
6420f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // Call the src components x, y, and z.
6430f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    uint8_t maxX = colorLUT->fGridPoints[0] - 1;
6440f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    uint8_t maxY = colorLUT->fGridPoints[1] - 1;
6450f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    uint8_t maxZ = colorLUT->fGridPoints[2] - 1;
6460f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
6470f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // An approximate index into each of the three dimensions of the table.
6480f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    float x = src[0] * maxX;
6490f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    float y = src[1] * maxY;
6500f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    float z = src[2] * maxZ;
6510f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
6520f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // This gives us the low index for our interpolation.
6530f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    int ix = sk_float_floor2int(x);
6540f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    int iy = sk_float_floor2int(y);
6550f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    int iz = sk_float_floor2int(z);
6560f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
6570f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // Make sure the low index is not also the max index.
6580f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    ix = (maxX == ix) ? ix - 1 : ix;
6590f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    iy = (maxY == iy) ? iy - 1 : iy;
6600f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    iz = (maxZ == iz) ? iz - 1 : iz;
6610f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
6620f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // Weighting factors for the interpolation.
6630f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    float diffX = x - ix;
6640f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    float diffY = y - iy;
6650f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    float diffZ = z - iz;
6660f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
6670f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // Constants to help us navigate the 3D table.
6680f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // Ex: Assume x = a, y = b, z = c.
6690f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    //     table[a * n001 + b * n010 + c * n100] logically equals table[a][b][c].
6700f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    const int n000 = 0;
6710f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    const int n001 = 3 * colorLUT->fGridPoints[1] * colorLUT->fGridPoints[2];
6720f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    const int n010 = 3 * colorLUT->fGridPoints[2];
6730f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    const int n011 = n001 + n010;
6740f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    const int n100 = 3;
6750f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    const int n101 = n100 + n001;
6760f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    const int n110 = n100 + n010;
6770f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    const int n111 = n110 + n001;
6780f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
6790f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // Base ptr into the table.
6800f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    float* ptr = &colorLUT->fTable[ix*n001 + iy*n010 + iz*n100];
6810f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
6820f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // The code below performs a tetrahedral interpolation for each of the three
6830f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // dst components.  Once the tetrahedron containing the interpolation point is
6840f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // identified, the interpolation is a weighted sum of grid values at the
6850f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // vertices of the tetrahedron.  The claim is that tetrahedral interpolation
6860f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // provides a more accurate color conversion.
6870f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // blogs.mathworks.com/steve/2006/11/24/tetrahedral-interpolation-for-colorspace-conversion/
6880f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    //
6890f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // I have one test image, and visually I can't tell the difference between
6900f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // tetrahedral and trilinear interpolation.  In terms of computation, the
6910f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // tetrahedral code requires more branches but less computation.  The
6920f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // SampleICC library provides an option for the client to choose either
6930f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    // tetrahedral or trilinear.
6940f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    for (int i = 0; i < 3; i++) {
6950f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        if (diffZ < diffY) {
6960f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            if (diffZ < diffX) {
6970f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                dst[i] = (ptr[n000] + diffZ * (ptr[n110] - ptr[n010]) +
6980f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffY * (ptr[n010] - ptr[n000]) +
6990f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffX * (ptr[n111] - ptr[n110]));
7000f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            } else if (diffY < diffX) {
7010f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                dst[i] = (ptr[n000] + diffZ * (ptr[n111] - ptr[n011]) +
7020f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffY * (ptr[n011] - ptr[n001]) +
7030f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffX * (ptr[n001] - ptr[n000]));
7040f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            } else {
7050f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                dst[i] = (ptr[n000] + diffZ * (ptr[n111] - ptr[n011]) +
7060f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffY * (ptr[n010] - ptr[n000]) +
7070f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffX * (ptr[n011] - ptr[n010]));
7080f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            }
7090f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        } else {
7100f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            if (diffZ < diffX) {
7110f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                dst[i] = (ptr[n000] + diffZ * (ptr[n101] - ptr[n001]) +
7120f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffY * (ptr[n111] - ptr[n101]) +
7130f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffX * (ptr[n001] - ptr[n000]));
7140f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            } else if (diffY < diffX) {
7150f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                dst[i] = (ptr[n000] + diffZ * (ptr[n100] - ptr[n000]) +
7160f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffY * (ptr[n111] - ptr[n101]) +
7170f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffX * (ptr[n101] - ptr[n100]));
7180f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            } else {
7190f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                dst[i] = (ptr[n000] + diffZ * (ptr[n100] - ptr[n000]) +
7200f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffY * (ptr[n110] - ptr[n100]) +
7210f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                                      diffX * (ptr[n111] - ptr[n110]));
7220f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            }
7230f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        }
7240f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
7250f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        // Increment the table ptr in order to handle the next component.
7260f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        // Note that this is the how table is designed: all of nXXX
7270f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        // variables are multiples of 3 because there are 3 output
7280f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        // components.
7290f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        ptr++;
7300f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett    }
7310f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett}
7320f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
733b39067696ad08a26bbe49b71a71f0546dc42a075msarettvoid SkDefaultXform::xform_RGB1_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const {
734b39067696ad08a26bbe49b71a71f0546dc42a075msarett    while (len-- > 0) {
7350f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        uint8_t r = (*src >>  0) & 0xFF,
7360f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                g = (*src >>  8) & 0xFF,
7370f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett                b = (*src >> 16) & 0xFF;
7380f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
7390f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        if (fColorLUT) {
7400f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            float in[3];
7410f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            float out[3];
7420f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
7430f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            in[0] = byte_to_float(r);
7440f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            in[1] = byte_to_float(g);
7450f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            in[2] = byte_to_float(b);
7460f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
7470f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            interp_3d_clut(out, in, fColorLUT.get());
7480f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
7490f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            r = sk_float_round2int(255.0f * clamp_normalized_float(out[0]));
7500f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            g = sk_float_round2int(255.0f * clamp_normalized_float(out[1]));
7510f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett            b = sk_float_round2int(255.0f * clamp_normalized_float(out[2]));
7520f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        }
7530f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett
754b39067696ad08a26bbe49b71a71f0546dc42a075msarett        // Convert to linear.
755b39067696ad08a26bbe49b71a71f0546dc42a075msarett        float srcFloats[3];
7560f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        srcFloats[0] = fSrcGammaTables[0][r];
7570f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        srcFloats[1] = fSrcGammaTables[1][g];
7580f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        srcFloats[2] = fSrcGammaTables[2][b];
759dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
760dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // Convert to dst gamut.
761dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        float dstFloats[3];
762dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        dstFloats[0] = srcFloats[0] * fSrcToDst.getFloat(0, 0) +
763dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[1] * fSrcToDst.getFloat(1, 0) +
764dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[2] * fSrcToDst.getFloat(2, 0) + fSrcToDst.getFloat(3, 0);
765dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        dstFloats[1] = srcFloats[0] * fSrcToDst.getFloat(0, 1) +
766dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[1] * fSrcToDst.getFloat(1, 1) +
767dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[2] * fSrcToDst.getFloat(2, 1) + fSrcToDst.getFloat(3, 1);
768dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        dstFloats[2] = srcFloats[0] * fSrcToDst.getFloat(0, 2) +
769dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[1] * fSrcToDst.getFloat(1, 2) +
770dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[2] * fSrcToDst.getFloat(2, 2) + fSrcToDst.getFloat(3, 2);
771dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
772b39067696ad08a26bbe49b71a71f0546dc42a075msarett        // Clamp to 0-1.
773b39067696ad08a26bbe49b71a71f0546dc42a075msarett        dstFloats[0] = clamp_normalized_float(dstFloats[0]);
774b39067696ad08a26bbe49b71a71f0546dc42a075msarett        dstFloats[1] = clamp_normalized_float(dstFloats[1]);
775b39067696ad08a26bbe49b71a71f0546dc42a075msarett        dstFloats[2] = clamp_normalized_float(dstFloats[2]);
776b39067696ad08a26bbe49b71a71f0546dc42a075msarett
777dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // Convert to dst gamma.
7780f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        r = fDstGammaTables[0][sk_float_round2int((kDstGammaTableSize - 1) * dstFloats[0])];
7790f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        g = fDstGammaTables[1][sk_float_round2int((kDstGammaTableSize - 1) * dstFloats[1])];
7800f83e0151f757ecd8d55d55ffefef58ecb11a97bmsarett        b = fDstGammaTables[2][sk_float_round2int((kDstGammaTableSize - 1) * dstFloats[2])];
781dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
782b39067696ad08a26bbe49b71a71f0546dc42a075msarett        *dst = SkPackARGB32NoCheck(0xFF, r, g, b);
783dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
784dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        dst++;
785dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        src++;
786dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    }
787dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett}
788