SkColorSpaceXform.cpp revision c4ce6b592487305de251bbebaf8eeee38371b877
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"
129876ac5b3016e5353c072378ac1545a0a2270757msarett
13dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettstatic inline bool compute_gamut_xform(SkMatrix44* srcToDst, const SkMatrix44& srcToXYZ,
14dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                                       const SkMatrix44& dstToXYZ) {
159876ac5b3016e5353c072378ac1545a0a2270757msarett    if (!dstToXYZ.invert(srcToDst)) {
169876ac5b3016e5353c072378ac1545a0a2270757msarett        return false;
179876ac5b3016e5353c072378ac1545a0a2270757msarett    }
189876ac5b3016e5353c072378ac1545a0a2270757msarett
199876ac5b3016e5353c072378ac1545a0a2270757msarett    srcToDst->postConcat(srcToXYZ);
209876ac5b3016e5353c072378ac1545a0a2270757msarett    return true;
219876ac5b3016e5353c072378ac1545a0a2270757msarett}
229876ac5b3016e5353c072378ac1545a0a2270757msarett
239876ac5b3016e5353c072378ac1545a0a2270757msarettstd::unique_ptr<SkColorSpaceXform> SkColorSpaceXform::New(const sk_sp<SkColorSpace>& srcSpace,
249876ac5b3016e5353c072378ac1545a0a2270757msarett                                                          const sk_sp<SkColorSpace>& dstSpace) {
259876ac5b3016e5353c072378ac1545a0a2270757msarett    if (!srcSpace || !dstSpace) {
26dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // Invalid input
279876ac5b3016e5353c072378ac1545a0a2270757msarett        return nullptr;
289876ac5b3016e5353c072378ac1545a0a2270757msarett    }
299876ac5b3016e5353c072378ac1545a0a2270757msarett
30dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    if (as_CSB(srcSpace)->colorLUT() || as_CSB(dstSpace)->colorLUT()) {
31dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // Unimplemented
32dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        return nullptr;
33dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    }
349876ac5b3016e5353c072378ac1545a0a2270757msarett
35dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    SkMatrix44 srcToDst(SkMatrix44::kUninitialized_Constructor);
36dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    if (!compute_gamut_xform(&srcToDst, srcSpace->xyz(), dstSpace->xyz())) {
37dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        return nullptr;
38dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    }
39dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
40a9e878c836994bce695274b4c28890290139dcdfmsarett    if (SkColorSpace::k2Dot2Curve_GammaNamed == srcSpace->gammaNamed() &&
41a9e878c836994bce695274b4c28890290139dcdfmsarett        SkColorSpace::k2Dot2Curve_GammaNamed == dstSpace->gammaNamed())
42a9e878c836994bce695274b4c28890290139dcdfmsarett    {
43a9e878c836994bce695274b4c28890290139dcdfmsarett        return std::unique_ptr<SkColorSpaceXform>(new Sk2Dot2Xform(srcToDst));
449876ac5b3016e5353c072378ac1545a0a2270757msarett    }
459876ac5b3016e5353c072378ac1545a0a2270757msarett
46dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    return std::unique_ptr<SkColorSpaceXform>(
47dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            new SkDefaultXform(as_CSB(srcSpace)->gammas(), srcToDst, as_CSB(dstSpace)->gammas()));
489876ac5b3016e5353c072378ac1545a0a2270757msarett}
499876ac5b3016e5353c072378ac1545a0a2270757msarett
509876ac5b3016e5353c072378ac1545a0a2270757msarett///////////////////////////////////////////////////////////////////////////////////////////////////
519876ac5b3016e5353c072378ac1545a0a2270757msarett
52a9e878c836994bce695274b4c28890290139dcdfmsarettSk2Dot2Xform::Sk2Dot2Xform(const SkMatrix44& srcToDst)
53a9e878c836994bce695274b4c28890290139dcdfmsarett{
54a9e878c836994bce695274b4c28890290139dcdfmsarett    // Build row major 4x4 matrix:
55a9e878c836994bce695274b4c28890290139dcdfmsarett    //   rX gX bX 0
56a9e878c836994bce695274b4c28890290139dcdfmsarett    //   rY gY bY 0
57a9e878c836994bce695274b4c28890290139dcdfmsarett    //   rZ gZ bZ 0
58a9e878c836994bce695274b4c28890290139dcdfmsarett    //   rQ gQ bQ 0
59a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[0] = srcToDst.getFloat(0, 0);
60a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[1] = srcToDst.getFloat(0, 1);
61a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[2] = srcToDst.getFloat(0, 2);
62a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[3] = 0.0f;
63a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[4] = srcToDst.getFloat(1, 0);
64a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[5] = srcToDst.getFloat(1, 1);
65a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[6] = srcToDst.getFloat(1, 2);
66a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[7] = 0.0f;
67a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[8] = srcToDst.getFloat(2, 0);
68a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[9] = srcToDst.getFloat(2, 1);
69a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[10] = srcToDst.getFloat(2, 2);
70a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[11] = 0.0f;
71a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[12] = srcToDst.getFloat(3, 0);
72a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[13] = srcToDst.getFloat(3, 1);
73a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[14] = srcToDst.getFloat(3, 2);
74a9e878c836994bce695274b4c28890290139dcdfmsarett    fSrcToDst[15] = 0.0f;
75a9e878c836994bce695274b4c28890290139dcdfmsarett}
76a9e878c836994bce695274b4c28890290139dcdfmsarett
77a9e878c836994bce695274b4c28890290139dcdfmsarettvoid Sk2Dot2Xform::xform_RGBA_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const {
78a9e878c836994bce695274b4c28890290139dcdfmsarett    SkOpts::color_xform_2Dot2_RGBA_to_8888(dst, src, len, fSrcToDst);
79a9e878c836994bce695274b4c28890290139dcdfmsarett}
80a9e878c836994bce695274b4c28890290139dcdfmsarett
81a9e878c836994bce695274b4c28890290139dcdfmsarett///////////////////////////////////////////////////////////////////////////////////////////////////
82a9e878c836994bce695274b4c28890290139dcdfmsarett
83dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettstatic inline float byte_to_float(uint8_t v) {
84dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    return ((float) v) * (1.0f / 255.0f);
859876ac5b3016e5353c072378ac1545a0a2270757msarett}
869876ac5b3016e5353c072378ac1545a0a2270757msarett
87a9e878c836994bce695274b4c28890290139dcdfmsarett// Expand range from 0-1 to 0-255, then convert.
88a9e878c836994bce695274b4c28890290139dcdfmsarettstatic inline uint8_t clamp_normalized_float_to_byte(float v) {
899876ac5b3016e5353c072378ac1545a0a2270757msarett    v = v * 255.0f;
90a9e878c836994bce695274b4c28890290139dcdfmsarett    if (v >= 254.5f) {
919876ac5b3016e5353c072378ac1545a0a2270757msarett        return 255;
92a9e878c836994bce695274b4c28890290139dcdfmsarett    } else if (v < 0.5f) {
939876ac5b3016e5353c072378ac1545a0a2270757msarett        return 0;
949876ac5b3016e5353c072378ac1545a0a2270757msarett    } else {
959876ac5b3016e5353c072378ac1545a0a2270757msarett        return (uint8_t) (v + 0.5f);
969876ac5b3016e5353c072378ac1545a0a2270757msarett    }
979876ac5b3016e5353c072378ac1545a0a2270757msarett}
989876ac5b3016e5353c072378ac1545a0a2270757msarett
99dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// Interpolating lookup in a variably sized table.
100dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettstatic inline float interp_lut(uint8_t byte, float* table, size_t tableSize) {
101dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    float index = byte_to_float(byte) * (tableSize - 1);
102dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    float diff = index - sk_float_floor2int(index);
103dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    return table[(int) sk_float_floor2int(index)] * (1.0f - diff) +
104dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            table[(int) sk_float_ceil2int(index)] * diff;
105dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett}
106dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
107dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// Inverse table lookup.  Ex: what index corresponds to the input value?  This will
108dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// have strange results when the table is non-increasing.  But any sane gamma
109dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// function will be increasing.
110dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// FIXME (msarett):
111dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// This is a placeholder implementation for inverting table gammas.  First, I need to
112dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// verify if there are actually destination profiles that require this functionality.
113dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// Next, there are certainly faster and more robust approaches to solving this problem.
114dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett// The LUT based approach in QCMS would be a good place to start.
115dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettstatic inline float interp_lut_inv(float input, float* table, size_t tableSize) {
116dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    if (input <= table[0]) {
117dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        return table[0];
118dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    } else if (input >= table[tableSize - 1]) {
119dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        return 1.0f;
120dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    }
121dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
122dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    for (uint32_t i = 1; i < tableSize; i++) {
123dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        if (table[i] >= input) {
124dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            // We are guaranteed that input is greater than table[i - 1].
125dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            float diff = input - table[i - 1];
126dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            float distance = table[i] - table[i - 1];
127dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            float index = (i - 1) + diff / distance;
128dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            return index / (tableSize - 1);
129dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        }
130dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    }
131dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
132dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    // Should be unreachable, since we'll return before the loop if input is
133dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    // larger than the last entry.
134dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    SkASSERT(false);
135dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    return 0.0f;
136dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett}
137dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
138dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettSkDefaultXform::SkDefaultXform(const sk_sp<SkGammas>& srcGammas, const SkMatrix44& srcToDst,
139dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                               const sk_sp<SkGammas>& dstGammas)
140dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    : fSrcGammas(srcGammas)
141dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    , fSrcToDst(srcToDst)
142dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    , fDstGammas(dstGammas)
143dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett{}
144dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
145dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarettvoid SkDefaultXform::xform_RGBA_8888(uint32_t* dst, const uint32_t* src, uint32_t len) const {
146dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    while (len-- > 0) {
147dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // Convert to linear.
148dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // FIXME (msarett):
149dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // Rather than support three different strategies of transforming gamma, QCMS
150dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // builds a 256 entry float lookup table from the gamma info.  This handles
151dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // the gamma transform and the conversion from bytes to floats.  This may
152dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // be simpler and faster than our current approach.
153dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        float srcFloats[3];
154dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        for (int i = 0; i < 3; i++) {
155dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            uint8_t byte = (*src >> (8 * i)) & 0xFF;
156c4ce6b592487305de251bbebaf8eeee38371b877msarett            if (fSrcGammas) {
157c4ce6b592487305de251bbebaf8eeee38371b877msarett                const SkGammaCurve& gamma = (*fSrcGammas)[i];
158c4ce6b592487305de251bbebaf8eeee38371b877msarett                if (gamma.isValue()) {
159c4ce6b592487305de251bbebaf8eeee38371b877msarett                    srcFloats[i] = powf(byte_to_float(byte), gamma.fValue);
160c4ce6b592487305de251bbebaf8eeee38371b877msarett                } else if (gamma.isTable()) {
161c4ce6b592487305de251bbebaf8eeee38371b877msarett                    srcFloats[i] = interp_lut(byte, gamma.fTable.get(), gamma.fTableSize);
162dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                } else {
163c4ce6b592487305de251bbebaf8eeee38371b877msarett                    SkASSERT(gamma.isParametric());
164c4ce6b592487305de251bbebaf8eeee38371b877msarett                    float component = byte_to_float(byte);
165c4ce6b592487305de251bbebaf8eeee38371b877msarett                    if (component < gamma.fD) {
166c4ce6b592487305de251bbebaf8eeee38371b877msarett                        // Y = E * X + F
167c4ce6b592487305de251bbebaf8eeee38371b877msarett                        srcFloats[i] = gamma.fE * component + gamma.fF;
168c4ce6b592487305de251bbebaf8eeee38371b877msarett                    } else {
169c4ce6b592487305de251bbebaf8eeee38371b877msarett                        // Y = (A * X + B)^G + C
170c4ce6b592487305de251bbebaf8eeee38371b877msarett                        srcFloats[i] = powf(gamma.fA * component + gamma.fB, gamma.fG) + gamma.fC;
171c4ce6b592487305de251bbebaf8eeee38371b877msarett                    }
172dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                }
173c4ce6b592487305de251bbebaf8eeee38371b877msarett            } else {
174c4ce6b592487305de251bbebaf8eeee38371b877msarett                // FIXME: Handle named gammas.
175c4ce6b592487305de251bbebaf8eeee38371b877msarett                srcFloats[i] = powf(byte_to_float(byte), 2.2f);
176dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            }
177dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        }
178dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
179dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // Convert to dst gamut.
180dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        float dstFloats[3];
181dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        dstFloats[0] = srcFloats[0] * fSrcToDst.getFloat(0, 0) +
182dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[1] * fSrcToDst.getFloat(1, 0) +
183dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[2] * fSrcToDst.getFloat(2, 0) + fSrcToDst.getFloat(3, 0);
184dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        dstFloats[1] = srcFloats[0] * fSrcToDst.getFloat(0, 1) +
185dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[1] * fSrcToDst.getFloat(1, 1) +
186dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[2] * fSrcToDst.getFloat(2, 1) + fSrcToDst.getFloat(3, 1);
187dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        dstFloats[2] = srcFloats[0] * fSrcToDst.getFloat(0, 2) +
188dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[1] * fSrcToDst.getFloat(1, 2) +
189dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                       srcFloats[2] * fSrcToDst.getFloat(2, 2) + fSrcToDst.getFloat(3, 2);
190dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
191dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // Convert to dst gamma.
192dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // FIXME (msarett):
193dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // Rather than support three different strategies of transforming inverse gamma,
194dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // QCMS builds a large float lookup table from the gamma info.  Is this faster or
195dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        // better than our approach?
196dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        for (int i = 0; i < 3; i++) {
197c4ce6b592487305de251bbebaf8eeee38371b877msarett            if (fDstGammas) {
198c4ce6b592487305de251bbebaf8eeee38371b877msarett                const SkGammaCurve& gamma = (*fDstGammas)[i];
199c4ce6b592487305de251bbebaf8eeee38371b877msarett                if (gamma.isValue()) {
200c4ce6b592487305de251bbebaf8eeee38371b877msarett                    dstFloats[i] = powf(dstFloats[i], 1.0f / gamma.fValue);
201c4ce6b592487305de251bbebaf8eeee38371b877msarett                } else if (gamma.isTable()) {
202c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // FIXME (msarett):
203c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // An inverse table lookup is particularly strange and non-optimal.
204c4ce6b592487305de251bbebaf8eeee38371b877msarett                    dstFloats[i] = interp_lut_inv(dstFloats[i], gamma.fTable.get(),
205c4ce6b592487305de251bbebaf8eeee38371b877msarett                                                  gamma.fTableSize);
206dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                } else {
207c4ce6b592487305de251bbebaf8eeee38371b877msarett                    SkASSERT(gamma.isParametric());
208c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // FIXME (msarett):
209c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // This is a placeholder implementation for inverting parametric gammas.
210c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // First, I need to verify if there are actually destination profiles that
211c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // require this functionality. Next, I need to explore other possibilities
212c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // for this implementation.  The LUT based approach in QCMS would be a good
213c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // place to start.
214c4ce6b592487305de251bbebaf8eeee38371b877msarett
215c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // We need to take the inverse of a piecewise function.  Assume that
216c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // the gamma function is continuous, or this won't make much sense
217c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // anyway.
218c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // Plug in |fD| to the first equation to calculate the new piecewise
219c4ce6b592487305de251bbebaf8eeee38371b877msarett                    // interval.  Then simply use the inverse of the original functions.
220c4ce6b592487305de251bbebaf8eeee38371b877msarett                    float interval = gamma.fE * gamma.fD + gamma.fF;
221c4ce6b592487305de251bbebaf8eeee38371b877msarett                    if (dstFloats[i] < interval) {
222c4ce6b592487305de251bbebaf8eeee38371b877msarett                        // X = (Y - F) / E
223c4ce6b592487305de251bbebaf8eeee38371b877msarett                        if (0.0f == gamma.fE) {
224c4ce6b592487305de251bbebaf8eeee38371b877msarett                            // The gamma curve for this segment is constant, so the inverse
225c4ce6b592487305de251bbebaf8eeee38371b877msarett                            // is undefined.
226c4ce6b592487305de251bbebaf8eeee38371b877msarett                            dstFloats[i] = 0.0f;
227c4ce6b592487305de251bbebaf8eeee38371b877msarett                        } else {
228c4ce6b592487305de251bbebaf8eeee38371b877msarett                            dstFloats[i] = (dstFloats[i] - gamma.fF) / gamma.fE;
229c4ce6b592487305de251bbebaf8eeee38371b877msarett                        }
230dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                    } else {
231c4ce6b592487305de251bbebaf8eeee38371b877msarett                        // X = ((Y - C)^(1 / G) - B) / A
232c4ce6b592487305de251bbebaf8eeee38371b877msarett                        if (0.0f == gamma.fA || 0.0f == gamma.fG) {
233c4ce6b592487305de251bbebaf8eeee38371b877msarett                            // The gamma curve for this segment is constant, so the inverse
234c4ce6b592487305de251bbebaf8eeee38371b877msarett                            // is undefined.
235c4ce6b592487305de251bbebaf8eeee38371b877msarett                            dstFloats[i] = 0.0f;
236c4ce6b592487305de251bbebaf8eeee38371b877msarett                        } else {
237c4ce6b592487305de251bbebaf8eeee38371b877msarett                            dstFloats[i] = (powf(dstFloats[i] - gamma.fC, 1.0f / gamma.fG) -
238c4ce6b592487305de251bbebaf8eeee38371b877msarett                                            gamma.fB) / gamma.fA;
239c4ce6b592487305de251bbebaf8eeee38371b877msarett                        }
240dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                    }
241dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett                }
242c4ce6b592487305de251bbebaf8eeee38371b877msarett            } else {
243c4ce6b592487305de251bbebaf8eeee38371b877msarett                // FIXME: Handle named gammas.
244c4ce6b592487305de251bbebaf8eeee38371b877msarett                dstFloats[i] = powf(dstFloats[i], 1.0f / 2.2f);
245dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett            }
246dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        }
247dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
248dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        *dst = SkPackARGB32NoCheck(((*src >> 24) & 0xFF),
249a9e878c836994bce695274b4c28890290139dcdfmsarett                                   clamp_normalized_float_to_byte(dstFloats[0]),
250a9e878c836994bce695274b4c28890290139dcdfmsarett                                   clamp_normalized_float_to_byte(dstFloats[1]),
251a9e878c836994bce695274b4c28890290139dcdfmsarett                                   clamp_normalized_float_to_byte(dstFloats[2]));
252dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett
253dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        dst++;
254dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett        src++;
255dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett    }
256dc27a648d2ff23b2e96232c00c15976c46e1d48dmsarett}
257