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; |
+ } |
+ } |
+} |