SkColorSpaceXform_A2B.cpp revision 113d05fa7b26797e3e468f78ea94a214476b63fb
12563601fc2b0505619f905f86bd249ae630197ccraftias/*
22563601fc2b0505619f905f86bd249ae630197ccraftias * Copyright 2016 Google Inc.
32563601fc2b0505619f905f86bd249ae630197ccraftias *
42563601fc2b0505619f905f86bd249ae630197ccraftias * Use of this source code is governed by a BSD-style license that can be
52563601fc2b0505619f905f86bd249ae630197ccraftias * found in the LICENSE file.
62563601fc2b0505619f905f86bd249ae630197ccraftias */
72563601fc2b0505619f905f86bd249ae630197ccraftias
82563601fc2b0505619f905f86bd249ae630197ccraftias#include "SkColorSpaceXform_A2B.h"
92563601fc2b0505619f905f86bd249ae630197ccraftias
102563601fc2b0505619f905f86bd249ae630197ccraftias#include "SkColorPriv.h"
112563601fc2b0505619f905f86bd249ae630197ccraftias#include "SkColorSpace_A2B.h"
122563601fc2b0505619f905f86bd249ae630197ccraftias#include "SkColorSpace_XYZ.h"
132563601fc2b0505619f905f86bd249ae630197ccraftias#include "SkColorSpacePriv.h"
142563601fc2b0505619f905f86bd249ae630197ccraftias#include "SkColorSpaceXformPriv.h"
152563601fc2b0505619f905f86bd249ae630197ccraftias#include "SkMakeUnique.h"
162563601fc2b0505619f905f86bd249ae630197ccraftias#include "SkNx.h"
172563601fc2b0505619f905f86bd249ae630197ccraftias#include "SkSRGB.h"
182563601fc2b0505619f905f86bd249ae630197ccraftias#include "SkTypes.h"
192563601fc2b0505619f905f86bd249ae630197ccraftias
2091db12d89c214235e24599f3ec18df2f952e99ebraftias#include <algorithm>
212563601fc2b0505619f905f86bd249ae630197ccraftias
222563601fc2b0505619f905f86bd249ae630197ccraftias#define AI SK_ALWAYS_INLINE
232563601fc2b0505619f905f86bd249ae630197ccraftias
242563601fc2b0505619f905f86bd249ae630197ccraftiasbool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorFormat srcFormat,
252563601fc2b0505619f905f86bd249ae630197ccraftias                                    const void* src, int count, SkAlphaType alphaType) const {
262563601fc2b0505619f905f86bd249ae630197ccraftias    SkRasterPipeline pipeline;
272563601fc2b0505619f905f86bd249ae630197ccraftias    switch (srcFormat) {
282563601fc2b0505619f905f86bd249ae630197ccraftias        case kBGRA_8888_ColorFormat:
29729b58296282da00fb9c0f92db2e2e8a8347d431Mike Klein            pipeline.append(SkRasterPipeline::load_8888, &src);
302563601fc2b0505619f905f86bd249ae630197ccraftias            pipeline.append(SkRasterPipeline::swap_rb);
312563601fc2b0505619f905f86bd249ae630197ccraftias            break;
322563601fc2b0505619f905f86bd249ae630197ccraftias        case kRGBA_8888_ColorFormat:
33729b58296282da00fb9c0f92db2e2e8a8347d431Mike Klein            pipeline.append(SkRasterPipeline::load_8888, &src);
342563601fc2b0505619f905f86bd249ae630197ccraftias            break;
352563601fc2b0505619f905f86bd249ae630197ccraftias        default:
362563601fc2b0505619f905f86bd249ae630197ccraftias            SkCSXformPrintf("F16/F32 source color format not supported\n");
372563601fc2b0505619f905f86bd249ae630197ccraftias            return false;
382563601fc2b0505619f905f86bd249ae630197ccraftias    }
392563601fc2b0505619f905f86bd249ae630197ccraftias
402563601fc2b0505619f905f86bd249ae630197ccraftias    pipeline.extend(fElementsPipeline);
412563601fc2b0505619f905f86bd249ae630197ccraftias
422563601fc2b0505619f905f86bd249ae630197ccraftias    if (kPremul_SkAlphaType == alphaType) {
432563601fc2b0505619f905f86bd249ae630197ccraftias        pipeline.append(SkRasterPipeline::premul);
442563601fc2b0505619f905f86bd249ae630197ccraftias    }
452563601fc2b0505619f905f86bd249ae630197ccraftias
462563601fc2b0505619f905f86bd249ae630197ccraftias    switch (dstFormat) {
472563601fc2b0505619f905f86bd249ae630197ccraftias        case kBGRA_8888_ColorFormat:
482563601fc2b0505619f905f86bd249ae630197ccraftias            pipeline.append(SkRasterPipeline::swap_rb);
492563601fc2b0505619f905f86bd249ae630197ccraftias            pipeline.append(SkRasterPipeline::store_8888, &dst);
502563601fc2b0505619f905f86bd249ae630197ccraftias            break;
512563601fc2b0505619f905f86bd249ae630197ccraftias        case kRGBA_8888_ColorFormat:
522563601fc2b0505619f905f86bd249ae630197ccraftias            pipeline.append(SkRasterPipeline::store_8888, &dst);
532563601fc2b0505619f905f86bd249ae630197ccraftias            break;
542563601fc2b0505619f905f86bd249ae630197ccraftias        case kRGBA_F16_ColorFormat:
552563601fc2b0505619f905f86bd249ae630197ccraftias            if (!fLinearDstGamma) {
562563601fc2b0505619f905f86bd249ae630197ccraftias                return false;
572563601fc2b0505619f905f86bd249ae630197ccraftias            }
582563601fc2b0505619f905f86bd249ae630197ccraftias            pipeline.append(SkRasterPipeline::store_f16, &dst);
592563601fc2b0505619f905f86bd249ae630197ccraftias            break;
602563601fc2b0505619f905f86bd249ae630197ccraftias        case kRGBA_F32_ColorFormat:
612563601fc2b0505619f905f86bd249ae630197ccraftias            if (!fLinearDstGamma) {
622563601fc2b0505619f905f86bd249ae630197ccraftias                return false;
632563601fc2b0505619f905f86bd249ae630197ccraftias            }
642563601fc2b0505619f905f86bd249ae630197ccraftias            pipeline.append(SkRasterPipeline::store_f32, &dst);
652563601fc2b0505619f905f86bd249ae630197ccraftias            break;
662563601fc2b0505619f905f86bd249ae630197ccraftias    }
67c789b61167dd98efc3c3bfcf9673eef24c2e57f4Mike Klein    pipeline.run(0,0, count);
682563601fc2b0505619f905f86bd249ae630197ccraftias
692563601fc2b0505619f905f86bd249ae630197ccraftias    return true;
702563601fc2b0505619f905f86bd249ae630197ccraftias}
712563601fc2b0505619f905f86bd249ae630197ccraftias
72113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistrystatic inline bool gamma_to_parametric(SkColorSpaceTransferFn* coeffs, const SkGammas& gammas,
73113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                                       int channel) {
742563601fc2b0505619f905f86bd249ae630197ccraftias    switch (gammas.type(channel)) {
752563601fc2b0505619f905f86bd249ae630197ccraftias        case SkGammas::Type::kNamed_Type:
76113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry            return named_to_parametric(coeffs, gammas.data(channel).fNamed);
772563601fc2b0505619f905f86bd249ae630197ccraftias        case SkGammas::Type::kValue_Type:
78113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry            value_to_parametric(coeffs, gammas.data(channel).fValue);
79113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry            return true;
802563601fc2b0505619f905f86bd249ae630197ccraftias        case SkGammas::Type::kParam_Type:
81113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry            *coeffs = gammas.params(channel);
82113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry            return true;
832563601fc2b0505619f905f86bd249ae630197ccraftias        default:
84113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry            return false;
852563601fc2b0505619f905f86bd249ae630197ccraftias    }
862563601fc2b0505619f905f86bd249ae630197ccraftias}
872563601fc2b0505619f905f86bd249ae630197ccraftiasstatic inline SkColorSpaceTransferFn invert_parametric(const SkColorSpaceTransferFn& fn) {
882563601fc2b0505619f905f86bd249ae630197ccraftias    // Original equation is:       y = (ax + b)^g + c   for x >= d
892563601fc2b0505619f905f86bd249ae630197ccraftias    //                             y = ex + f           otherwise
902563601fc2b0505619f905f86bd249ae630197ccraftias    //
912563601fc2b0505619f905f86bd249ae630197ccraftias    // so 1st inverse is:          (y - c)^(1/g) = ax + b
922563601fc2b0505619f905f86bd249ae630197ccraftias    //                             x = ((y - c)^(1/g) - b) / a
932563601fc2b0505619f905f86bd249ae630197ccraftias    //
942563601fc2b0505619f905f86bd249ae630197ccraftias    // which can be re-written as: x = (1/a)(y - c)^(1/g) - b/a
952563601fc2b0505619f905f86bd249ae630197ccraftias    //                             x = ((1/a)^g)^(1/g) * (y - c)^(1/g) - b/a
962563601fc2b0505619f905f86bd249ae630197ccraftias    //                             x = ([(1/a)^g]y + [-((1/a)^g)c]) ^ [1/g] + [-b/a]
972563601fc2b0505619f905f86bd249ae630197ccraftias    //
982563601fc2b0505619f905f86bd249ae630197ccraftias    // and 2nd inverse is:         x = (y - f) / e
992563601fc2b0505619f905f86bd249ae630197ccraftias    // which can be re-written as: x = [1/e]y + [-f/e]
1002563601fc2b0505619f905f86bd249ae630197ccraftias    //
1012563601fc2b0505619f905f86bd249ae630197ccraftias    // and now both can be expressed in terms of the same parametric form as the
102197e311ac9d15696fae929d8f5fcf9d93ec55e18raftias    // original - parameters are enclosed in square brackets.
1032563601fc2b0505619f905f86bd249ae630197ccraftias
1042563601fc2b0505619f905f86bd249ae630197ccraftias    // find inverse for linear segment (if possible)
1052563601fc2b0505619f905f86bd249ae630197ccraftias    float e, f;
1062563601fc2b0505619f905f86bd249ae630197ccraftias    if (0.f == fn.fE) {
1072563601fc2b0505619f905f86bd249ae630197ccraftias        // otherwise assume it should be 0 as it is the lower segment
1082563601fc2b0505619f905f86bd249ae630197ccraftias        // as y = f is a constant function
1092563601fc2b0505619f905f86bd249ae630197ccraftias        e = 0.f;
1102563601fc2b0505619f905f86bd249ae630197ccraftias        f = 0.f;
1112563601fc2b0505619f905f86bd249ae630197ccraftias    } else {
1122563601fc2b0505619f905f86bd249ae630197ccraftias        e = 1.f / fn.fE;
1132563601fc2b0505619f905f86bd249ae630197ccraftias        f = -fn.fF / fn.fE;
1142563601fc2b0505619f905f86bd249ae630197ccraftias    }
1152563601fc2b0505619f905f86bd249ae630197ccraftias    // find inverse for the other segment (if possible)
1162563601fc2b0505619f905f86bd249ae630197ccraftias    float g, a, b, c;
1172563601fc2b0505619f905f86bd249ae630197ccraftias    if (0.f == fn.fA || 0.f == fn.fG) {
1182563601fc2b0505619f905f86bd249ae630197ccraftias        // otherwise assume it should be 1 as it is the top segment
1192563601fc2b0505619f905f86bd249ae630197ccraftias        // as you can't invert the constant functions y = b^g + c, or y = 1 + c
1202563601fc2b0505619f905f86bd249ae630197ccraftias        g = 1.f;
1212563601fc2b0505619f905f86bd249ae630197ccraftias        a = 0.f;
1222563601fc2b0505619f905f86bd249ae630197ccraftias        b = 0.f;
1232563601fc2b0505619f905f86bd249ae630197ccraftias        c = 1.f;
1242563601fc2b0505619f905f86bd249ae630197ccraftias    } else {
1252563601fc2b0505619f905f86bd249ae630197ccraftias        g = 1.f / fn.fG;
1262563601fc2b0505619f905f86bd249ae630197ccraftias        a = powf(1.f / fn.fA, fn.fG);
1272563601fc2b0505619f905f86bd249ae630197ccraftias        b = -a * fn.fC;
1282563601fc2b0505619f905f86bd249ae630197ccraftias        c = -fn.fB / fn.fA;
1292563601fc2b0505619f905f86bd249ae630197ccraftias    }
1302563601fc2b0505619f905f86bd249ae630197ccraftias    const float d = fn.fE * fn.fD + fn.fF;
1312563601fc2b0505619f905f86bd249ae630197ccraftias    return {g, a, b, c, d, e, f};
1322563601fc2b0505619f905f86bd249ae630197ccraftias}
1332563601fc2b0505619f905f86bd249ae630197ccraftias
1342563601fc2b0505619f905f86bd249ae630197ccraftiasSkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
1352563601fc2b0505619f905f86bd249ae630197ccraftias                                             SkColorSpace_XYZ* dstSpace)
1362563601fc2b0505619f905f86bd249ae630197ccraftias    : fLinearDstGamma(kLinear_SkGammaNamed == dstSpace->gammaNamed()) {
1372563601fc2b0505619f905f86bd249ae630197ccraftias#if (SkCSXformPrintfDefined)
1382563601fc2b0505619f905f86bd249ae630197ccraftias    static const char* debugGammaNamed[4] = {
1392563601fc2b0505619f905f86bd249ae630197ccraftias        "Linear", "SRGB", "2.2", "NonStandard"
1402563601fc2b0505619f905f86bd249ae630197ccraftias    };
1412563601fc2b0505619f905f86bd249ae630197ccraftias    static const char* debugGammas[5] = {
1422563601fc2b0505619f905f86bd249ae630197ccraftias        "None", "Named", "Value", "Table", "Param"
1432563601fc2b0505619f905f86bd249ae630197ccraftias    };
1442563601fc2b0505619f905f86bd249ae630197ccraftias#endif
1455476128f0a88217414f05e6a7ee518cdb411d026raftias    int currentChannels = -1;
1465476128f0a88217414f05e6a7ee518cdb411d026raftias    switch (srcSpace->inputColorFormat()) {
1475476128f0a88217414f05e6a7ee518cdb411d026raftias        case SkColorSpace_Base::InputColorFormat::kRGB:
1485476128f0a88217414f05e6a7ee518cdb411d026raftias            currentChannels = 3;
1495476128f0a88217414f05e6a7ee518cdb411d026raftias            break;
1505476128f0a88217414f05e6a7ee518cdb411d026raftias        case SkColorSpace_Base::InputColorFormat::kCMYK:
1515476128f0a88217414f05e6a7ee518cdb411d026raftias            currentChannels = 4;
1525476128f0a88217414f05e6a7ee518cdb411d026raftias            // CMYK images from JPEGs (the only format that supports it) are actually
1535476128f0a88217414f05e6a7ee518cdb411d026raftias            // inverted CMYK, so we need to invert every channel.
1545476128f0a88217414f05e6a7ee518cdb411d026raftias            // TransferFn is y = -x + 1 for x < 1.f, otherwise 0x + 0, ie y = 1 - x for x in [0,1]
1555476128f0a88217414f05e6a7ee518cdb411d026raftias            this->addTransferFns({1.f, 0.f, 0.f, 0.f, 1.f, -1.f, 1.f}, 4);
1565476128f0a88217414f05e6a7ee518cdb411d026raftias            break;
15791db12d89c214235e24599f3ec18df2f952e99ebraftias        case SkColorSpace_Base::InputColorFormat::kGray:
15891db12d89c214235e24599f3ec18df2f952e99ebraftias            currentChannels = 1;
15991db12d89c214235e24599f3ec18df2f952e99ebraftias            break;
1605476128f0a88217414f05e6a7ee518cdb411d026raftias        default:
1615476128f0a88217414f05e6a7ee518cdb411d026raftias            SkASSERT(false);
1625476128f0a88217414f05e6a7ee518cdb411d026raftias    }
1632563601fc2b0505619f905f86bd249ae630197ccraftias    // add in all input color space -> PCS xforms
1642563601fc2b0505619f905f86bd249ae630197ccraftias    for (int i = 0; i < srcSpace->count(); ++i) {
1652563601fc2b0505619f905f86bd249ae630197ccraftias        const SkColorSpace_A2B::Element& e = srcSpace->element(i);
1665476128f0a88217414f05e6a7ee518cdb411d026raftias        SkASSERT(e.inputChannels() == currentChannels);
1675476128f0a88217414f05e6a7ee518cdb411d026raftias        currentChannels = e.outputChannels();
1682563601fc2b0505619f905f86bd249ae630197ccraftias        switch (e.type()) {
1692563601fc2b0505619f905f86bd249ae630197ccraftias            case SkColorSpace_A2B::Element::Type::kGammaNamed:
170113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                if (kLinear_SkGammaNamed == e.gammaNamed()) {
171113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                    break;
172113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                }
173113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry
174975245407a16dba58ee42cb12f70f8db87f02da0raftias                // take the fast path for 3-channel named gammas
175975245407a16dba58ee42cb12f70f8db87f02da0raftias                if (3 == currentChannels) {
176975245407a16dba58ee42cb12f70f8db87f02da0raftias                    if (k2Dot2Curve_SkGammaNamed == e.gammaNamed()) {
177975245407a16dba58ee42cb12f70f8db87f02da0raftias                        SkCSXformPrintf("fast path from 2.2\n");
178975245407a16dba58ee42cb12f70f8db87f02da0raftias                        fElementsPipeline.append(SkRasterPipeline::from_2dot2);
179975245407a16dba58ee42cb12f70f8db87f02da0raftias                        break;
180975245407a16dba58ee42cb12f70f8db87f02da0raftias                    } else if (kSRGB_SkGammaNamed == e.gammaNamed()) {
181975245407a16dba58ee42cb12f70f8db87f02da0raftias                        SkCSXformPrintf("fast path from sRGB\n");
182975245407a16dba58ee42cb12f70f8db87f02da0raftias                        // Images should always start the pipeline as unpremul
183975245407a16dba58ee42cb12f70f8db87f02da0raftias                        fElementsPipeline.append_from_srgb(kUnpremul_SkAlphaType);
184975245407a16dba58ee42cb12f70f8db87f02da0raftias                        break;
185975245407a16dba58ee42cb12f70f8db87f02da0raftias                    }
186975245407a16dba58ee42cb12f70f8db87f02da0raftias                }
187113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry
188113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                SkCSXformPrintf("Gamma stage added: %s\n", debugGammaNamed[(int)e.gammaNamed()]);
189113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                SkColorSpaceTransferFn fn;
190113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                SkAssertResult(named_to_parametric(&fn, e.gammaNamed()));
191113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                this->addTransferFns(fn, currentChannels);
1922563601fc2b0505619f905f86bd249ae630197ccraftias                break;
1932563601fc2b0505619f905f86bd249ae630197ccraftias            case SkColorSpace_A2B::Element::Type::kGammas: {
194db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                const SkGammas& gammas = e.gammas();
195db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                SkCSXformPrintf("Gamma stage added:");
1965476128f0a88217414f05e6a7ee518cdb411d026raftias                for (int channel = 0; channel < gammas.channels(); ++channel) {
197db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                    SkCSXformPrintf("  %s", debugGammas[(int)gammas.type(channel)]);
198db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                }
199db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                SkCSXformPrintf("\n");
200db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                bool gammaNeedsRef = false;
2015476128f0a88217414f05e6a7ee518cdb411d026raftias                for (int channel = 0; channel < gammas.channels(); ++channel) {
202db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                    if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
203db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                        SkTableTransferFn table = {
204db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                                gammas.table(channel),
205db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                                gammas.data(channel).fTable.fSize,
206db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                        };
207db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett
2085476128f0a88217414f05e6a7ee518cdb411d026raftias                        this->addTableFn(table, channel);
209db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                        gammaNeedsRef = true;
210db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                    } else {
211113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                        SkColorSpaceTransferFn fn;
212113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                        SkAssertResult(gamma_to_parametric(&fn, gammas, channel));
2135476128f0a88217414f05e6a7ee518cdb411d026raftias                        this->addTransferFn(fn, channel);
2142563601fc2b0505619f905f86bd249ae630197ccraftias                    }
2152563601fc2b0505619f905f86bd249ae630197ccraftias                }
216db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                if (gammaNeedsRef) {
217db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                    fGammaRefs.push_back(sk_ref_sp(&gammas));
218db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett                }
2192563601fc2b0505619f905f86bd249ae630197ccraftias                break;
220db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            }
2212563601fc2b0505619f905f86bd249ae630197ccraftias            case SkColorSpace_A2B::Element::Type::kCLUT:
2225476128f0a88217414f05e6a7ee518cdb411d026raftias                SkCSXformPrintf("CLUT (%d -> %d) stage added\n", e.colorLUT().inputChannels(),
2235476128f0a88217414f05e6a7ee518cdb411d026raftias                                                                 e.colorLUT().outputChannels());
2242563601fc2b0505619f905f86bd249ae630197ccraftias                fCLUTs.push_back(sk_ref_sp(&e.colorLUT()));
2252563601fc2b0505619f905f86bd249ae630197ccraftias                fElementsPipeline.append(SkRasterPipeline::color_lookup_table,
2262563601fc2b0505619f905f86bd249ae630197ccraftias                                         fCLUTs.back().get());
2272563601fc2b0505619f905f86bd249ae630197ccraftias                break;
2282563601fc2b0505619f905f86bd249ae630197ccraftias            case SkColorSpace_A2B::Element::Type::kMatrix:
2292563601fc2b0505619f905f86bd249ae630197ccraftias                if (!e.matrix().isIdentity()) {
2302563601fc2b0505619f905f86bd249ae630197ccraftias                    SkCSXformPrintf("Matrix stage added\n");
2312563601fc2b0505619f905f86bd249ae630197ccraftias                    addMatrix(e.matrix());
2322563601fc2b0505619f905f86bd249ae630197ccraftias                }
2332563601fc2b0505619f905f86bd249ae630197ccraftias                break;
2342563601fc2b0505619f905f86bd249ae630197ccraftias        }
2352563601fc2b0505619f905f86bd249ae630197ccraftias    }
2362563601fc2b0505619f905f86bd249ae630197ccraftias
23791db12d89c214235e24599f3ec18df2f952e99ebraftias    // take care of monochrome ICC profiles (but not A2B with gray input color space!)
23891db12d89c214235e24599f3ec18df2f952e99ebraftias    if (1 == currentChannels) {
23991db12d89c214235e24599f3ec18df2f952e99ebraftias        // Gray color spaces must multiply their channel by the PCS whitepoint to convert to
24091db12d89c214235e24599f3ec18df2f952e99ebraftias        // the PCS however, PCSLAB profiles must be n-component LUT-based ones, which
24191db12d89c214235e24599f3ec18df2f952e99ebraftias        // need to have 3 (to match PCS) output channels, not 1
24291db12d89c214235e24599f3ec18df2f952e99ebraftias        SkASSERT(SkColorSpace_Base::InputColorFormat::kGray == srcSpace->inputColorFormat());
24391db12d89c214235e24599f3ec18df2f952e99ebraftias        SkASSERT(SkColorSpace_A2B::PCS::kXYZ == srcSpace->pcs());
24491db12d89c214235e24599f3ec18df2f952e99ebraftias        constexpr float PCSXYZWhitePoint[3] = {0.9642f, 1.f, 0.8249f};
24591db12d89c214235e24599f3ec18df2f952e99ebraftias        fMatrices.push_front(std::vector<float>(12, 0.f));
24691db12d89c214235e24599f3ec18df2f952e99ebraftias        std::copy_n(PCSXYZWhitePoint, 3, fMatrices.front().begin());
24791db12d89c214235e24599f3ec18df2f952e99ebraftias        fElementsPipeline.append(SkRasterPipeline::matrix_3x4, fMatrices.front().data());
24891db12d89c214235e24599f3ec18df2f952e99ebraftias        currentChannels = 3;
24991db12d89c214235e24599f3ec18df2f952e99ebraftias    }
2505476128f0a88217414f05e6a7ee518cdb411d026raftias
2512563601fc2b0505619f905f86bd249ae630197ccraftias    // Lab PCS -> XYZ PCS
2522563601fc2b0505619f905f86bd249ae630197ccraftias    if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) {
2532563601fc2b0505619f905f86bd249ae630197ccraftias        SkCSXformPrintf("Lab -> XYZ element added\n");
2542563601fc2b0505619f905f86bd249ae630197ccraftias        fElementsPipeline.append(SkRasterPipeline::lab_to_xyz);
2552563601fc2b0505619f905f86bd249ae630197ccraftias    }
2562563601fc2b0505619f905f86bd249ae630197ccraftias
25791db12d89c214235e24599f3ec18df2f952e99ebraftias    // we should now be in XYZ PCS
25891db12d89c214235e24599f3ec18df2f952e99ebraftias    SkASSERT(3 == currentChannels);
25991db12d89c214235e24599f3ec18df2f952e99ebraftias
2602563601fc2b0505619f905f86bd249ae630197ccraftias    // and XYZ PCS -> output color space xforms
2612563601fc2b0505619f905f86bd249ae630197ccraftias    if (!dstSpace->fromXYZD50()->isIdentity()) {
2622563601fc2b0505619f905f86bd249ae630197ccraftias        addMatrix(*dstSpace->fromXYZD50());
2632563601fc2b0505619f905f86bd249ae630197ccraftias    }
2642563601fc2b0505619f905f86bd249ae630197ccraftias
265975245407a16dba58ee42cb12f70f8db87f02da0raftias    switch (dstSpace->gammaNamed()) {
266975245407a16dba58ee42cb12f70f8db87f02da0raftias        case kLinear_SkGammaNamed:
267975245407a16dba58ee42cb12f70f8db87f02da0raftias            // do nothing
268975245407a16dba58ee42cb12f70f8db87f02da0raftias            break;
269975245407a16dba58ee42cb12f70f8db87f02da0raftias        case k2Dot2Curve_SkGammaNamed:
270975245407a16dba58ee42cb12f70f8db87f02da0raftias            fElementsPipeline.append(SkRasterPipeline::to_2dot2);
271975245407a16dba58ee42cb12f70f8db87f02da0raftias            break;
272975245407a16dba58ee42cb12f70f8db87f02da0raftias        case kSRGB_SkGammaNamed:
273975245407a16dba58ee42cb12f70f8db87f02da0raftias            fElementsPipeline.append(SkRasterPipeline::to_srgb);
274975245407a16dba58ee42cb12f70f8db87f02da0raftias            break;
275975245407a16dba58ee42cb12f70f8db87f02da0raftias        case kNonStandard_SkGammaNamed: {
276975245407a16dba58ee42cb12f70f8db87f02da0raftias            for (int channel = 0; channel < 3; ++channel) {
277975245407a16dba58ee42cb12f70f8db87f02da0raftias                const SkGammas& gammas = *dstSpace->gammas();
278975245407a16dba58ee42cb12f70f8db87f02da0raftias                if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
279975245407a16dba58ee42cb12f70f8db87f02da0raftias                    static constexpr int kInvTableSize = 256;
280975245407a16dba58ee42cb12f70f8db87f02da0raftias                    std::vector<float> storage(kInvTableSize);
281975245407a16dba58ee42cb12f70f8db87f02da0raftias                    invert_table_gamma(storage.data(), nullptr, storage.size(),
282975245407a16dba58ee42cb12f70f8db87f02da0raftias                                       gammas.table(channel),
283975245407a16dba58ee42cb12f70f8db87f02da0raftias                                       gammas.data(channel).fTable.fSize);
284975245407a16dba58ee42cb12f70f8db87f02da0raftias                    SkTableTransferFn table = {
285975245407a16dba58ee42cb12f70f8db87f02da0raftias                            storage.data(),
286975245407a16dba58ee42cb12f70f8db87f02da0raftias                            (int) storage.size(),
287975245407a16dba58ee42cb12f70f8db87f02da0raftias                    };
288975245407a16dba58ee42cb12f70f8db87f02da0raftias                    fTableStorage.push_front(std::move(storage));
289db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett
290975245407a16dba58ee42cb12f70f8db87f02da0raftias                    this->addTableFn(table, channel);
291975245407a16dba58ee42cb12f70f8db87f02da0raftias                } else {
292113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                    SkColorSpaceTransferFn fn;
293113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                    SkAssertResult(gamma_to_parametric(&fn, gammas, channel));
294113d05fa7b26797e3e468f78ea94a214476b63fbRavi Mistry                    this->addTransferFn(invert_parametric(fn), channel);
295975245407a16dba58ee42cb12f70f8db87f02da0raftias                }
2962563601fc2b0505619f905f86bd249ae630197ccraftias            }
2972563601fc2b0505619f905f86bd249ae630197ccraftias        }
298975245407a16dba58ee42cb12f70f8db87f02da0raftias        break;
299db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett    }
300db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett}
301db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett
3025476128f0a88217414f05e6a7ee518cdb411d026raftiasvoid SkColorSpaceXform_A2B::addTransferFns(const SkColorSpaceTransferFn& fn, int channelCount) {
3035476128f0a88217414f05e6a7ee518cdb411d026raftias    for (int i = 0; i < channelCount; ++i) {
3045476128f0a88217414f05e6a7ee518cdb411d026raftias        this->addTransferFn(fn, i);
3055476128f0a88217414f05e6a7ee518cdb411d026raftias    }
3065476128f0a88217414f05e6a7ee518cdb411d026raftias}
3075476128f0a88217414f05e6a7ee518cdb411d026raftias
3085476128f0a88217414f05e6a7ee518cdb411d026raftiasvoid SkColorSpaceXform_A2B::addTransferFn(const SkColorSpaceTransferFn& fn, int channelIndex) {
309db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett    fTransferFns.push_front(fn);
3105476128f0a88217414f05e6a7ee518cdb411d026raftias    switch (channelIndex) {
3115476128f0a88217414f05e6a7ee518cdb411d026raftias        case 0:
312db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            fElementsPipeline.append(SkRasterPipeline::parametric_r, &fTransferFns.front());
313db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            break;
3145476128f0a88217414f05e6a7ee518cdb411d026raftias        case 1:
315db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            fElementsPipeline.append(SkRasterPipeline::parametric_g, &fTransferFns.front());
316db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            break;
3175476128f0a88217414f05e6a7ee518cdb411d026raftias        case 2:
318db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            fElementsPipeline.append(SkRasterPipeline::parametric_b, &fTransferFns.front());
319db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            break;
3205476128f0a88217414f05e6a7ee518cdb411d026raftias        case 3:
3215476128f0a88217414f05e6a7ee518cdb411d026raftias            fElementsPipeline.append(SkRasterPipeline::parametric_a, &fTransferFns.front());
3225476128f0a88217414f05e6a7ee518cdb411d026raftias            break;
323db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett        default:
324db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            SkASSERT(false);
3252563601fc2b0505619f905f86bd249ae630197ccraftias    }
3262563601fc2b0505619f905f86bd249ae630197ccraftias}
3272563601fc2b0505619f905f86bd249ae630197ccraftias
3285476128f0a88217414f05e6a7ee518cdb411d026raftiasvoid SkColorSpaceXform_A2B::addTableFn(const SkTableTransferFn& fn, int channelIndex) {
329db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett    fTableTransferFns.push_front(fn);
3305476128f0a88217414f05e6a7ee518cdb411d026raftias    switch (channelIndex) {
3315476128f0a88217414f05e6a7ee518cdb411d026raftias        case 0:
332db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            fElementsPipeline.append(SkRasterPipeline::table_r, &fTableTransferFns.front());
3332563601fc2b0505619f905f86bd249ae630197ccraftias            break;
3345476128f0a88217414f05e6a7ee518cdb411d026raftias        case 1:
335db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            fElementsPipeline.append(SkRasterPipeline::table_g, &fTableTransferFns.front());
3362563601fc2b0505619f905f86bd249ae630197ccraftias            break;
3375476128f0a88217414f05e6a7ee518cdb411d026raftias        case 2:
338db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett            fElementsPipeline.append(SkRasterPipeline::table_b, &fTableTransferFns.front());
3392563601fc2b0505619f905f86bd249ae630197ccraftias            break;
3405476128f0a88217414f05e6a7ee518cdb411d026raftias        case 3:
3415476128f0a88217414f05e6a7ee518cdb411d026raftias            fElementsPipeline.append(SkRasterPipeline::table_a, &fTableTransferFns.front());
3425476128f0a88217414f05e6a7ee518cdb411d026raftias            break;
3432563601fc2b0505619f905f86bd249ae630197ccraftias        default:
3442563601fc2b0505619f905f86bd249ae630197ccraftias            SkASSERT(false);
3452563601fc2b0505619f905f86bd249ae630197ccraftias    }
3462563601fc2b0505619f905f86bd249ae630197ccraftias}
3472563601fc2b0505619f905f86bd249ae630197ccraftias
3482563601fc2b0505619f905f86bd249ae630197ccraftiasvoid SkColorSpaceXform_A2B::addMatrix(const SkMatrix44& matrix) {
3492563601fc2b0505619f905f86bd249ae630197ccraftias    fMatrices.push_front(std::vector<float>(12));
3502563601fc2b0505619f905f86bd249ae630197ccraftias    auto& m = fMatrices.front();
3512563601fc2b0505619f905f86bd249ae630197ccraftias    m[ 0] = matrix.get(0, 0);
3522563601fc2b0505619f905f86bd249ae630197ccraftias    m[ 1] = matrix.get(1, 0);
3532563601fc2b0505619f905f86bd249ae630197ccraftias    m[ 2] = matrix.get(2, 0);
3542563601fc2b0505619f905f86bd249ae630197ccraftias    m[ 3] = matrix.get(0, 1);
3552563601fc2b0505619f905f86bd249ae630197ccraftias    m[ 4] = matrix.get(1, 1);
3562563601fc2b0505619f905f86bd249ae630197ccraftias    m[ 5] = matrix.get(2, 1);
3572563601fc2b0505619f905f86bd249ae630197ccraftias    m[ 6] = matrix.get(0, 2);
3582563601fc2b0505619f905f86bd249ae630197ccraftias    m[ 7] = matrix.get(1, 2);
3592563601fc2b0505619f905f86bd249ae630197ccraftias    m[ 8] = matrix.get(2, 2);
3602563601fc2b0505619f905f86bd249ae630197ccraftias    m[ 9] = matrix.get(0, 3);
3612563601fc2b0505619f905f86bd249ae630197ccraftias    m[10] = matrix.get(1, 3);
3622563601fc2b0505619f905f86bd249ae630197ccraftias    m[11] = matrix.get(2, 3);
3632563601fc2b0505619f905f86bd249ae630197ccraftias    SkASSERT(matrix.get(3, 0) == 0.f);
3642563601fc2b0505619f905f86bd249ae630197ccraftias    SkASSERT(matrix.get(3, 1) == 0.f);
3652563601fc2b0505619f905f86bd249ae630197ccraftias    SkASSERT(matrix.get(3, 2) == 0.f);
3662563601fc2b0505619f905f86bd249ae630197ccraftias    SkASSERT(matrix.get(3, 3) == 1.f);
3672563601fc2b0505619f905f86bd249ae630197ccraftias    fElementsPipeline.append(SkRasterPipeline::matrix_3x4, m.data());
3682563601fc2b0505619f905f86bd249ae630197ccraftias    fElementsPipeline.append(SkRasterPipeline::clamp_0);
369db4d406e7e311113e56663b1f2286c18adcee985Matt Sarett    fElementsPipeline.append(SkRasterPipeline::clamp_1);
3702563601fc2b0505619f905f86bd249ae630197ccraftias}
371