Chromium Code Reviews| 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; |
| + } |
| + } |
| +} |