Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(139)

Unified Diff: src/core/SkColorSpaceXform_A2B.cpp

Issue 2449243003: Initial implementation of a SkColorSpace_A2B xform (Closed)
Patch Set: build error+forgot to premuliplty last 0-3 pixels of a line+whitespace warnings Created 4 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: src/core/SkColorSpaceXform_A2B.cpp
diff --git a/src/core/SkColorSpaceXform_A2B.cpp b/src/core/SkColorSpaceXform_A2B.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..73b954cb731d387d3db4c9d0640f5dad97c1d3c4
--- /dev/null
+++ b/src/core/SkColorSpaceXform_A2B.cpp
@@ -0,0 +1,643 @@
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "SkColorSpaceXform_A2B.h"
+
+#include "SkColorPriv.h"
+#include "SkColorSpace_A2B.h"
+#include "SkColorSpace_XYZ.h"
+#include "SkColorSpacePriv.h"
+#include "SkColorSpaceXformPriv.h"
+#include "SkMakeUnique.h"
+#include "SkNx.h"
+#include "SkSRGB.h"
+#include "SkTypes.h"
+
+class SkColorSpaceXform_A2B::ProcessingElement {
+public:
+ virtual ~ProcessingElement() {}
+ virtual void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) = 0;
+ virtual bool merge(ProcessingElement* other) = 0;
+
+ enum class Type {
+ kGamma,
+ kGammaTable,
+ kMatrix,
+ kCLUT,
+ kLabToXYZ
+ };
+ virtual Type type() const = 0;
+};
+
+class SkColorSpaceXform_A2B::ApplyCLUT : public SkColorSpaceXform_A2B::ProcessingElement {
+public:
+ ApplyCLUT(const SkColorLookUpTable* clut)
+ : fCLUT(sk_ref_sp(clut))
+ {}
+
+ void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override {
msarett 2016/10/26 20:43:29 This feels like a really good place to use SkRaste
raftias 2016/10/27 18:39:39 Acknowledged.
+ while (len > 0) {
+ // unpack into float[3] = {r, g, b} arrays like interp_3d_clut() needs
msarett 2016/10/26 20:43:29 If it's convenient, you are welcome to change the
raftias 2016/10/27 18:39:38 It involved look-up tables so without constructing
+ float rgb[4][3] = {
+ {(*rSrc)[0], (*gSrc)[0], (*bSrc)[0]},
+ {(*rSrc)[1], (*gSrc)[1], (*bSrc)[1]},
+ {(*rSrc)[2], (*gSrc)[2], (*bSrc)[2]},
+ {(*rSrc)[3], (*gSrc)[3], (*bSrc)[3]}
+ };
+ for (int i = 0; i < 4; ++i) {
+ interp_3d_clut(rgb[i], rgb[i], fCLUT.get());
+ }
+ *rSrc = Sk4f(rgb[0][0], rgb[1][0], rgb[2][0], rgb[3][0]);
+ *gSrc = Sk4f(rgb[0][1], rgb[1][1], rgb[2][1], rgb[3][1]);
+ *bSrc = Sk4f(rgb[0][2], rgb[1][2], rgb[2][2], rgb[3][2]);
+ ++rSrc;
+ ++gSrc;
+ ++bSrc;
+ --len;
+ }
+ }
+
+ bool merge(ProcessingElement*) override { return false; }
+
+ Type type() const override { return Type::kCLUT; }
+
+private:
+ sk_sp<const SkColorLookUpTable> fCLUT;
+};
+
+class SkColorSpaceXform_A2B::LabToXYZ : public SkColorSpaceXform_A2B::ProcessingElement {
+public:
+ void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override {
+ while (len > 0) {
+ // convert from Lab to XYZ
+ Sk4f Y = *rSrc*(100.f/116.f) + (16.f/116.f);
+ Sk4f X = *gSrc * (255.f/500.f) - (128.f/500.f) + Y;
+ Sk4f Z = Y - *bSrc * (255.f/200.f) - (128.f/200.f);
+
+ /*const Sk4f l = *rSrc * 100.f;
+ const Sk4f a = *gSrc * 255.f - 128.f;
+ const Sk4f b = *bSrc * 255.f - 128.f;
+ Sk4f Y = (l + 16.f) * (1.f/116.f);
+ Sk4f X = a * (1.f/500.f) + Y;
+ Sk4f Z = Y - (b * (1.f/200.f));*/
+
+
+ Sk4f cubed;
+ cubed = X*X*X;
+ X = (cubed > 0.008856f).thenElse(cubed, (X - (16.f/116.f)) * (1.f/7.787f));
+ cubed = Y*Y*Y;
+ Y = (cubed > 0.008856f).thenElse(cubed, (Y - (16.f/116.f)) * (1.f/7.787f));
+ cubed = Z*Z*Z;
+ Z = (cubed > 0.008856f).thenElse(cubed, (Z - (16.f/116.f)) * (1.f/7.787f));
+
+ // adjust to D50 illuminant
+ X *= 0.96422f;
+ Y *= 1.00000f;
+ Z *= 0.82521f;
+
+ *rSrc = X;
+ *gSrc = Y;
+ *bSrc = Z;
+ ++rSrc;
+ ++gSrc;
+ ++bSrc;
+ --len;
+ }
+ }
+
+ bool merge(ProcessingElement*) override { return false; }
+
+ Type type() const override { return Type::kLabToXYZ; }
+};
+
+class SkColorSpaceXform_A2B::ApplyMatrix : public SkColorSpaceXform_A2B::ProcessingElement {
+public:
+ ApplyMatrix(const SkMatrix44& matrix)
+ : fMatrix(matrix)
+ {}
+
+ void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override {
+ Sk4f rXgXbX, rYgYbY, rZgZbZ, rTgTbT;
+ float colMajor[16];
+ fMatrix.asColMajorf(colMajor);
+ load_matrix(colMajor, rXgXbX, rYgYbY, rZgZbZ, rTgTbT);
+ while (len > 0) {
+ Sk4f dr, dg, db, a; // a is ignored
+ transform_gamut(*rSrc, *gSrc, *bSrc, a, rXgXbX, rYgYbY, rZgZbZ, dr, dg, db, a);
+ translate_gamut(rTgTbT, dr, dg, db);
+ *rSrc = dr;
+ *gSrc = dg;
+ *bSrc = db;
+ ++rSrc;
+ ++gSrc;
+ ++bSrc;
+ --len;
+ }
+ }
+ bool merge(ProcessingElement* other) override {
+ if (other->type() == Type::kMatrix) {
+ fMatrix.preConcat(static_cast<ApplyMatrix*>(other)->fMatrix);
+ return true;
+ }
+ return false;
+ }
+ Type type() const override {
+ return Type::kMatrix;
+ }
+private:
+ SkMatrix44 fMatrix;
+};
+
+
+
+// adapted from the integer-input version in SkSRGB.h
+template <int N>
+static inline SkNx<N,float> sk_linear_from_float_srgb_math(const SkNx<N,float>& x) {
msarett 2016/10/26 20:43:29 Let's move this to SkSRGB.h. I think the int->flo
raftias 2016/10/27 18:39:38 Removed function entirely. Will consider this if/w
+ // Non-linear segment of sRGB curve approximated by
+ // l = 0.0025 + 0.6975x^2 + 0.3x^3
+ const SkNx<N,float> k0 = 0.0025f,
+ k2 = 0.6975f,
+ k3 = 0.3000f;
+ auto hi = x*x*(x*k3 + k2) + k0;
+
+ // Linear segment of sRGB curve: the normal slope, extended a little further than normal.
+ auto lo = x * (1.f/12.92f);
+
+ return (x < (14.025f / 255.f)).thenElse(lo, hi);
+}
+
+
+class SkColorSpaceXform_A2B::ApplyGammaSRGB : public SkColorSpaceXform_A2B::ProcessingElement {
+public:
+ void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override {
+ while (len > 0) {
+ *rSrc = sk_linear_from_float_srgb_math(*rSrc);
+ *gSrc = sk_linear_from_float_srgb_math(*gSrc);
+ *bSrc = sk_linear_from_float_srgb_math(*bSrc);
+ ++rSrc;
+ ++gSrc;
+ ++bSrc;
+ --len;
+ }
+ }
+ bool merge(ProcessingElement* other) override { return false; }
+ Type type() const override { return Type::kGamma; }
+};
+
+static inline Sk4f linear_from_2dot2(const Sk4f& x) {
+
+ // x^(141/64) = x^(2.20312) is a great approximation of the true value, x^(2.2).
+ auto x16 = x.rsqrt().rsqrt().rsqrt().rsqrt(); // x^(1/16) = x^(4/64);
+ auto x64 = x16.rsqrt().rsqrt(); // x^(1/64)
+
+ // x^(35/16) = x^(2.1875) is an okay one as well and would be quicker:
msarett 2016/10/26 20:43:29 Please remove this comment. It's not a bad point,
raftias 2016/10/27 18:39:39 Removed function entirely. Will consider this if/w
+ // (both pass ColorSpaceXformTests's +/-1 difference vs x^2.2)
+ // return x*x * x16*x16*x16;
+
+ // x^(141/64) = x^(128/64) * x^(12/64) * x^(1/64)
+ return x*x * x16*x16*x16 * x64;
+}
+
+class SkColorSpaceXform_A2B::ApplyGamma2Dot2 : public SkColorSpaceXform_A2B::ProcessingElement {
+public:
+ void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override {
+ while (len > 0) {
+ *rSrc = linear_from_2dot2(*rSrc);
+ *gSrc = linear_from_2dot2(*gSrc);
+ *bSrc = linear_from_2dot2(*bSrc);
+ ++rSrc;
+ ++gSrc;
+ ++bSrc;
+ --len;
+ }
+ }
+ bool merge(ProcessingElement* other) override { return false; }
+ Type type() const override { return Type::kGamma; }
+};
+
+// TODO(raftias): See if there's some actually-vectorized implementation somewhere?
msarett 2016/10/26 20:43:29 As far I know, there's not. I don't even think th
+// or remove once all powf() calling transforms (value, param) are converted into tables
+static inline Sk4f SkNx_powf(const Sk4f& x, float exp) {
+ return Sk4f{::powf(x[0], exp), ::powf(x[1], exp), ::powf(x[2], exp), ::powf(x[3], exp)};
+}
+
+// TODO(raftias): remove and store in tables
+class SkColorSpaceXform_A2B::ApplyGammaValue : public SkColorSpaceXform_A2B::ProcessingElement {
+public:
+ ApplyGammaValue(float rExp, float gExp, float bExp)
+ : fRExp(rExp)
+ , fGExp(gExp)
+ , fBExp(bExp)
+ {}
+ void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override {
+ while (len > 0) {
+ *rSrc = SkNx_powf(*rSrc, fRExp);
+ *gSrc = SkNx_powf(*gSrc, fGExp);
+ *bSrc = SkNx_powf(*bSrc, fBExp);
+ ++rSrc;
+ ++gSrc;
+ ++bSrc;
+ --len;
+ }
+ }
+ bool merge(ProcessingElement* other) override { return false; }
+ Type type() const override { return Type::kGamma; }
+private:
+ float fRExp;
+ float fGExp;
+ float fBExp;
+};
+
+// TODO(raftias): remove and store in tables?
+class SkColorSpaceXform_A2B::ApplyGammaParams : public SkColorSpaceXform_A2B::ProcessingElement {
+public:
+ ApplyGammaParams(const SkColorSpaceTransferFn& rParams, const SkColorSpaceTransferFn& gParams,
+ const SkColorSpaceTransferFn& bParams)
+ : fRP(rParams)
+ , fGP(gParams)
+ , fBP(bParams)
+ {}
+ void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override {
+ // TODO(raftias): profile against a non-vectorized implementation, since if the (short)
+ // linear segment is hit a lot then there's a lot of unnecessary powf() calls.
+ // Or just remove these and cache into tables.
+ while (len > 0) {
+ *rSrc = (*rSrc <= fRP.fD).thenElse(fRP.fE * (*rSrc) + fRP.fF,
+ SkNx_powf(*rSrc*fRP.fA + fRP.fB, fRP.fG) + fRP.fC);
+ *gSrc = (*gSrc <= fGP.fD).thenElse(fGP.fE * (*gSrc) + fGP.fF,
+ SkNx_powf(*gSrc*fGP.fA + fGP.fB, fGP.fG) + fGP.fC);
+ *bSrc = (*bSrc <= fBP.fD).thenElse(fBP.fE * (*bSrc) + fBP.fF,
+ SkNx_powf(*bSrc*fBP.fA + fBP.fB, fBP.fG) + fBP.fC);
+ ++rSrc;
+ ++gSrc;
+ ++bSrc;
+ --len;
+ }
+ }
+ bool merge(ProcessingElement* other) override { return false; }
+ Type type() const override { return Type::kGamma; }
+private:
+ const SkColorSpaceTransferFn fRP;
+ const SkColorSpaceTransferFn fGP;
+ const SkColorSpaceTransferFn fBP;
+};
+
+class SkColorSpaceXform_A2B::ApplyGammaTable : public SkColorSpaceXform_A2B::ProcessingElement {
+public:
+ ApplyGammaTable(const SkGammas* gammas) {
+ // TODO(raftias): use 1 table if all channels point to the same table
+ const int numTables = 3;
+ const size_t tableBytes = numTables * kDstGammaTableSize * sizeof(float);
+ const float tableStep = 1.f / (float)(kDstGammaTableSize - 1);
+ fStorage.reset(tableBytes);
+ float* outTable = fStorage.get();
+ for (int i = 0; i < numTables; ++i) {
+ SkASSERT(gammas->isTable(i));
+ const float* inTable = gammas->table(i);
+ const int inTableSize = gammas->data(i).fTable.fSize;
+ fGammaTables[i] = outTable;
+ if (kDstGammaTableSize == inTableSize) {
+ memcpy(outTable, inTable, sizeof(float) * kDstGammaTableSize);
+ return;
+ }
+
+ for (float x = 0.0f; x <= 1.0f; x += tableStep) {
+ *outTable++ = interp_lut(x, inTable, inTableSize);
+ }
+ }
+ }
+
+ void apply(Sk4f* rSrc, Sk4f* gSrc, Sk4f* bSrc, int len) override {
+ const float maxIndex = kDstGammaTableSize - 1;
+ while (len > 0) {
+ *rSrc = Sk4f::Min(Sk4f::Max(maxIndex * (*rSrc), 0.f), maxIndex);
+ *gSrc = Sk4f::Min(Sk4f::Max(maxIndex * (*gSrc), 0.f), maxIndex);
+ *bSrc = Sk4f::Min(Sk4f::Max(maxIndex * (*bSrc), 0.f), maxIndex);
+ const Sk4i ir = Sk4f_round(*rSrc);
+ const Sk4i ig = Sk4f_round(*gSrc);
+ const Sk4i ib = Sk4f_round(*bSrc);
+ *rSrc = Sk4f((float) fGammaTables[0][ir[0]],
+ (float) fGammaTables[0][ir[1]],
+ (float) fGammaTables[0][ir[2]],
+ (float) fGammaTables[0][ir[3]]);
+ *gSrc = Sk4f((float) fGammaTables[1][ig[0]],
+ (float) fGammaTables[1][ig[1]],
+ (float) fGammaTables[1][ig[2]],
+ (float) fGammaTables[1][ig[3]]);
+ *bSrc = Sk4f((float) fGammaTables[2][ib[0]],
+ (float) fGammaTables[2][ib[1]],
+ (float) fGammaTables[2][ib[2]],
+ (float) fGammaTables[2][ib[3]]);
+ ++rSrc;
+ ++gSrc;
+ ++bSrc;
+ --len;
+ }
+ }
+
+ bool merge(ProcessingElement* other) override {
+ if (Type::kGammaTable == other->type()) {
+ auto otherGamma = static_cast<const SkColorSpaceXform_A2B::ApplyGammaTable*>(other);
+ const int numTables = 3; // set to 1 if matching
+ for (int table = 0; table < numTables; ++table) {
+ for (int i = 0; i < kDstGammaTableSize; ++i) {
+ const int indexIntoOther = (int)(fGammaTables[table][i] + 0.5f);
+ fGammaTables[table][i] = otherGamma->fGammaTables[table][indexIntoOther];
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ Type type() const override { return Type::kGammaTable; }
+
+private:
+ SkAutoTMalloc<float> fStorage;
+ float* fGammaTables[3];
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+
+bool SkColorSpaceXform_A2B::onApply(ColorFormat dstFormat, void* dst, ColorFormat srcFormat,
+ const void* src, int count, SkAlphaType alphaType) const {
+ LoadFn load;
+ const bool loadAlpha = (kPremul_SkAlphaType == alphaType) ||
+ (kRGBA_F16_ColorFormat == dstFormat) ||
+ (kRGBA_F32_ColorFormat == dstFormat);
+
+ switch (srcFormat) {
+ case kRGBA_8888_ColorFormat:
+ load = loadAlpha ? load_rgba_linear<kRGBA_Order>
+ : load_rgb_linear<kRGBA_Order>;
+ break;
+ case kBGRA_8888_ColorFormat:
+ load = loadAlpha ? load_rgba_linear<kBGRA_Order>
+ : load_rgb_linear<kBGRA_Order>;
+ break;
+ default:
+ SkCSXformPrintf("F16/F32 source color format not supported\n");
+ return false;
+ }
+
+ StoreFn store = store_linear<kRGBA_Order>;
+ size_t sizeOfDstPixel = 0;
+ switch (dstFormat) {
+ case kRGBA_8888_ColorFormat:
+ switch (fDstGamma) {
+ case kLinear_SkGammaNamed:
+ store = store_linear<kRGBA_Order>;
+ break;
+ case kSRGB_SkGammaNamed:
+ store = store_srgb<kRGBA_Order>;
+ break;
+ case k2Dot2Curve_SkGammaNamed:
+ store = store_2dot2<kRGBA_Order>;
+ break;
+ default:
+ store = store_generic<kRGBA_Order>;
+ break;
+ }
+ sizeOfDstPixel = 4;
+ break;
+ case kBGRA_8888_ColorFormat:
+ switch (fDstGamma) {
+ case kLinear_SkGammaNamed:
+ store = store_linear<kBGRA_Order>;
+ break;
+ case kSRGB_SkGammaNamed:
+ store = store_srgb<kBGRA_Order>;
+ break;
+ case k2Dot2Curve_SkGammaNamed:
+ store = store_2dot2<kBGRA_Order>;
+ break;
+ default:
+ store = store_generic<kBGRA_Order>;
+ break;
+ }
+ sizeOfDstPixel = 4;
+ break;
+ case kRGBA_F16_ColorFormat:
+ if (fDstGamma != kLinear_SkGammaNamed) {
+ return false;
+ }
+ store = (kOpaque_SkAlphaType == alphaType) ? store_f16_opaque<kRGBA_Order>
+ : store_f16<kRGBA_Order>;
+ sizeOfDstPixel = 8;
+ break;
+ case kRGBA_F32_ColorFormat:
+ if (fDstGamma != kLinear_SkGammaNamed) {
+ return false;
+ }
+ store = store_f32<kRGBA_Order>;
+ sizeOfDstPixel = 16;
+ break;
+ }
+ SkASSERT(sizeOfDstPixel > 0);
+
+ const int allocLen = (count + 3) / 4;
+ SkAutoTMalloc<Sk4f> rSrc(allocLen);
+ SkAutoTMalloc<Sk4f> gSrc(allocLen);
+ SkAutoTMalloc<Sk4f> bSrc(allocLen);
+ Sk4f ignore;
+ const uint32_t* loadSrc = (const uint32_t*)src;
+ int loadLen = count;
+ Sk4f* loadRSrc = rSrc.get();
+ Sk4f* loadGSrc = gSrc.get();
+ Sk4f* loadBSrc = bSrc.get();
+ while (loadLen >= 4) {
+ load(loadSrc, *loadRSrc, *loadGSrc, *loadBSrc, ignore, nullptr);
+ loadSrc += 4;
+ loadLen -= 4;
+ ++loadRSrc;
+ ++loadGSrc;
+ ++loadBSrc;
+ }
+ if (loadLen > 0) {
+ // read the last 1-3 RGBA values into a buffer and load all 4 RGBA
+ // values out of the buffer, letting the last 1-3 be arbitrary values
+ // (all byte combos are valid RGBA values)
+ uint32_t readOverrun[16];
+ memcpy(readOverrun, loadSrc, loadLen * sizeof(uint32_t));
+ load(readOverrun, *loadRSrc, *loadGSrc, *loadBSrc, ignore, nullptr);
+ }
+
+ for (const std::unique_ptr<ProcessingElement>& element : fProcessingElements) {
+ element->apply(rSrc.get(), gSrc.get(), bSrc.get(), allocLen);
+ }
+
+ Sk4f* storeRSrc = rSrc.get();
+ Sk4f* storeGSrc = gSrc.get();
+ Sk4f* storeBSrc = bSrc.get();
+ loadSrc = (const uint32_t*)src;
+ Sk4f alpha;
+ while (count >= 4) {
+ // load the alpha from the source image since our transformations only affected R/G/B
+ load(loadSrc, ignore, ignore, ignore, alpha, nullptr);
+
+ if (kPremul_SkAlphaType == alphaType) {
+ premultiply(*storeRSrc, *storeGSrc, *storeBSrc, alpha);
+ }
+
+ store(dst, (const uint32_t*)src, *storeRSrc, *storeGSrc, *storeBSrc, alpha,
+ fDstGammaTables);
+ dst = SkTAddOffset<void>(dst, 4 * sizeOfDstPixel);
+ count -= 4;
+ loadSrc += 4; // to load alpha
+ ++storeRSrc;
+ ++storeGSrc;
+ ++storeBSrc;
+ }
+ if (count > 0) {
+ const int maxSizeOfDstPixel = 16;
+ SkASSERT(sizeOfDstPixel <= maxSizeOfDstPixel);
+ uint32_t writeOverrun[4*maxSizeOfDstPixel];
+
+ load(loadSrc, ignore, ignore, ignore, alpha, nullptr);
+
+ if (kPremul_SkAlphaType == alphaType) {
+ premultiply(*storeRSrc, *storeGSrc, *storeBSrc, alpha);
+ }
+
+ store(writeOverrun, (const uint32_t*)src, *storeRSrc, *storeGSrc, *storeBSrc, alpha,
+ fDstGammaTables);
+ memcpy(dst, writeOverrun, count * sizeOfDstPixel);
+ }
+ return true;
+}
+
+SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
+ SkColorSpace_XYZ* dstSpace) {
+#if (SkCSXformPrintfDefined)
+ static const char* debugGammaNamed[4] = {
+ "Linear", "SRGB", "2.2", "NonStandard"
+ };
+ static const char* debugGammas[5] = {
+ "None", "Named", "Value", "Table", "Param"
+ };
+#endif
+ // add in all device -> PCS xforms
+ for (size_t i = 0; i < srcSpace->count(); ++i) {
+ const SkColorSpace_A2B::Element& e = srcSpace->element(i);
+ switch (e.type()) {
+ case SkColorSpace_A2B::Element::Type::kGammaNamed:
+ SkCSXformPrintf("Gamma element added: %s\n",
+ debugGammaNamed[(int)e.gammaNamed()]);
+ switch (e.gammaNamed()) {
+ case kLinear_SkGammaNamed:
+ // there is no point in adding a transform here
+ break;
+ case kSRGB_SkGammaNamed:
+ fProcessingElements.push_back(skstd::make_unique<ApplyGammaSRGB>());
+ break;
+ case k2Dot2Curve_SkGammaNamed:
+ fProcessingElements.push_back(skstd::make_unique<ApplyGamma2Dot2>());
+ break;
+ case kNonStandard_SkGammaNamed:
+ SkASSERT(false);
+ break;
+ }
+ break;
+ case SkColorSpace_A2B::Element::Type::kGammas: {
+ const SkGammas* gammas = &e.gammas();
+ SkCSXformPrintf("Adding gamma element:");
+ for (int channel = 0; channel < 3; ++channel) {
+ SkCSXformPrintf(" %s", debugGammas[(int)gammas->type(channel)]);
+ }
+ SkCSXformPrintf("\n");
+ if (gammas->type(0) != gammas->type(1) || gammas->type(0) != gammas->type(2)) {
+ SkCSXformPrintf("Mixed gamma types are not supported right now.");
+ SkCSXformPrintf(" Using only 1st gamma channel.\n");
+ }
+ switch (gammas->type(0))
+ {
+ case SkGammas::Type::kNone_Type:
+ SkCSXformPrintf("Invalid gamma type\n");
+ break;
+ case SkGammas::Type::kNamed_Type:
+ switch (gammas->data(0).fNamed) {
+ case kLinear_SkGammaNamed:
+ // there is no point in adding a transform here
+ break;
+ case kSRGB_SkGammaNamed:
+ fProcessingElements.push_back(
+ skstd::make_unique<ApplyGammaSRGB>());
+ break;
+ case k2Dot2Curve_SkGammaNamed:
+ fProcessingElements.push_back(
+ skstd::make_unique<ApplyGamma2Dot2>());
+ break;
+ case kNonStandard_SkGammaNamed:
+ SkASSERT(false);
+ break;
+ }
+ break;
+ case SkGammas::Type::kValue_Type:
+ // TODO(raftias): pre-compute and cache these into a table
+ fProcessingElements.push_back(skstd::make_unique<ApplyGammaValue>(
+ gammas->data(0).fValue, gammas->data(1).fValue,
+ gammas->data(2).fValue));
+ break;
+ case SkGammas::Type::kTable_Type:
+ fProcessingElements.push_back(
+ skstd::make_unique<ApplyGammaTable>(gammas));
+ break;
+ case SkGammas::Type::kParam_Type: {
+ // TODO(raftias): pre-compute and cache these into a table?
+ const SkColorSpaceTransferFn& pr = gammas->params(0);
+ const SkColorSpaceTransferFn& pg = gammas->params(1);
+ const SkColorSpaceTransferFn& pb = gammas->params(2);
+ fProcessingElements.push_back(skstd::make_unique<ApplyGammaParams>(
+ pr, pg, pb));
+ }
+ break;
+ }
+ }
+ break;
+ case SkColorSpace_A2B::Element::Type::kCLUT:
+ fProcessingElements.push_back(skstd::make_unique<ApplyCLUT>(&e.colorLUT()));
+ break;
+ case SkColorSpace_A2B::Element::Type::kMatrix:
+ if (!e.matrix().isIdentity()) {
+ fProcessingElements.push_back(skstd::make_unique<ApplyMatrix>(e.matrix()));
+ }
+ break;
+ }
+ }
+
+ // Lab PCS -> XYZ PCS
+ if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) {
+ SkCSXformPrintf("Lab -> XYZ element added\n");
+ fProcessingElements.push_back(skstd::make_unique<LabToXYZ>());
+ }
+
+ // and XYZ PCS -> device xforms
+ if (!dstSpace->fromXYZD50()->isIdentity()) {
+ fProcessingElements.push_back(skstd::make_unique<ApplyMatrix>(*dstSpace->fromXYZD50()));
+ }
+
+ //concatTransforms();
msarett 2016/10/26 20:43:29 Let's drop this for now if it's not being used yet
raftias 2016/10/27 18:39:39 Done.
+
+ const int numDstTables = num_tables(dstSpace);
+ dstSpace->toDstGammaTables(fDstGammaTables, &fDstStorage, numDstTables);
+ fDstGamma = dstSpace->gammaNamed();
+}
+
+void SkColorSpaceXform_A2B::concatTransforms() {
+ uint32_t i = 0;
+ while (i + 1 < fProcessingElements.size()) {
+ if (fProcessingElements[i]->merge(fProcessingElements[i + 1].get())) {
+ // We could just reset the (i+1)th element then do 1 pass at the end
+ // to remove null elements to get O(n) instead of O(n^2), but n should
+ // be very small so this should not matter
+ fProcessingElements.erase(fProcessingElements.begin() + i + 1);
+ } else {
+ ++i;
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698