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