aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--dm/DMSrcSink.cpp6
-rw-r--r--gm/labpcsdemo.cpp192
-rw-r--r--gn/gm.gni1
-rw-r--r--src/codec/SkJpegCodec.cpp19
-rw-r--r--src/core/SkColorLookUpTable.cpp51
-rw-r--r--src/core/SkColorLookUpTable.h36
-rw-r--r--src/core/SkColorSpace.cpp13
-rw-r--r--src/core/SkColorSpaceXform_A2B.cpp81
-rw-r--r--src/core/SkColorSpaceXform_A2B.h12
-rw-r--r--src/core/SkColorSpace_A2B.cpp7
-rw-r--r--src/core/SkColorSpace_A2B.h55
-rw-r--r--src/core/SkColorSpace_Base.h61
-rw-r--r--src/core/SkColorSpace_ICC.cpp604
-rw-r--r--src/core/SkColorSpace_XYZ.cpp5
-rw-r--r--src/core/SkRasterPipeline.h3
-rw-r--r--src/opts/SkRasterPipeline_opts.h23
-rw-r--r--tests/ColorSpaceXformTest.cpp101
17 files changed, 618 insertions, 652 deletions
diff --git a/dm/DMSrcSink.cpp b/dm/DMSrcSink.cpp
index 31a3bc7059..0a54702d08 100644
--- a/dm/DMSrcSink.cpp
+++ b/dm/DMSrcSink.cpp
@@ -1018,7 +1018,11 @@ Error ColorCodecSrc::draw(SkCanvas* canvas) const {
size_t rowBytes = bitmap.rowBytes();
SkCodec::Result r = codec->getPixels(decodeInfo, bitmap.getPixels(), rowBytes);
if (SkCodec::kSuccess != r && SkCodec::kIncompleteInput != r) {
- return SkStringPrintf("Couldn't getPixels %s. Error code %d", fPath.c_str(), r);
+ // FIXME (raftias):
+ // This should be a fatal error. We need to add support for
+ // A2B images in SkColorSpaceXform.
+ return Error::Nonfatal(SkStringPrintf("Couldn't getPixels %s. Error code %d",
+ fPath.c_str(), r));
}
switch (fMode) {
diff --git a/gm/labpcsdemo.cpp b/gm/labpcsdemo.cpp
new file mode 100644
index 0000000000..26e48a8890
--- /dev/null
+++ b/gm/labpcsdemo.cpp
@@ -0,0 +1,192 @@
+/*
+ * 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 <cmath>
+#include "gm.h"
+#include "Resources.h"
+#include "SkCodec.h"
+#include "SkColorSpace_Base.h"
+#include "SkColorSpace_A2B.h"
+#include "SkColorSpacePriv.h"
+#include "SkData.h"
+#include "SkFloatingPoint.h"
+#include "SkImageInfo.h"
+#include "SkScalar.h"
+#include "SkSRGB.h"
+#include "SkStream.h"
+#include "SkSurface.h"
+#include "SkTypes.h"
+
+/**
+ * This tests decoding from a Lab source image and displays on the left
+ * the image as raw RGB values, and on the right a Lab PCS.
+ * It currently does NOT apply a/b/m-curves, as in the .icc profile
+ * We are testing it on these are all identity transforms.
+ */
+class LabPCSDemoGM : public skiagm::GM {
+public:
+ LabPCSDemoGM()
+ : fWidth(1080)
+ , fHeight(480)
+ {}
+
+protected:
+
+
+ SkString onShortName() override {
+ return SkString("labpcsdemo");
+ }
+
+ SkISize onISize() override {
+ return SkISize::Make(fWidth, fHeight);
+ }
+
+ void onDraw(SkCanvas* canvas) override {
+ canvas->drawColor(SK_ColorGREEN);
+ const char* filename = "brickwork-texture.jpg";
+ renderImage(canvas, filename, 0, false);
+ renderImage(canvas, filename, 1, true);
+ }
+
+ void renderImage(SkCanvas* canvas, const char* filename, int col, bool convertLabToXYZ) {
+ SkBitmap bitmap;
+ SkStream* stream(GetResourceAsStream(filename));
+ if (stream == nullptr) {
+ return;
+ }
+ std::unique_ptr<SkCodec> codec(SkCodec::NewFromStream(stream));
+
+
+ // srgb_lab_pcs.icc is an elaborate way to specify sRGB but uses
+ // Lab as the PCS, so we can take any arbitrary image that should
+ // be sRGB and this should show a reasonable image
+ const SkString iccFilename(GetResourcePath("icc_profiles/srgb_lab_pcs.icc"));
+ sk_sp<SkData> iccData = SkData::MakeFromFileName(iccFilename.c_str());
+ if (iccData == nullptr) {
+ return;
+ }
+ sk_sp<SkColorSpace> colorSpace = SkColorSpace::MakeICC(iccData->bytes(), iccData->size());
+
+ const int imageWidth = codec->getInfo().width();
+ const int imageHeight = codec->getInfo().height();
+ // Using nullptr as the color space instructs the codec to decode in legacy mode,
+ // meaning that we will get the raw encoded bytes without any color correction.
+ SkImageInfo imageInfo = SkImageInfo::Make(imageWidth, imageHeight, kN32_SkColorType,
+ kOpaque_SkAlphaType, nullptr);
+ bitmap.allocPixels(imageInfo);
+ codec->getPixels(imageInfo, bitmap.getPixels(), bitmap.rowBytes());
+ if (convertLabToXYZ) {
+ SkASSERT(SkColorSpace_Base::Type::kA2B == as_CSB(colorSpace)->type());
+ SkColorSpace_A2B& cs = *static_cast<SkColorSpace_A2B*>(colorSpace.get());
+ const SkColorLookUpTable* colorLUT = nullptr;
+ bool printConversions = false;
+ // We're skipping evaluating the TRCs and the matrix here since they aren't
+ // in the ICC profile initially used here.
+ for (int e = 0; e < cs.count(); ++e) {
+ switch (cs.element(e).type()) {
+ case SkColorSpace_A2B::Element::Type::kGammaNamed:
+ SkASSERT(kLinear_SkGammaNamed == cs.element(e).gammaNamed());
+ break;
+ case SkColorSpace_A2B::Element::Type::kGammas:
+ SkASSERT(false);
+ break;
+ case SkColorSpace_A2B::Element::Type::kCLUT:
+ colorLUT = &cs.element(e).colorLUT();
+ break;
+ case SkColorSpace_A2B::Element::Type::kMatrix:
+ SkASSERT(cs.element(e).matrix().isIdentity());
+ break;
+ }
+ }
+ SkASSERT(colorLUT);
+ for (int y = 0; y < imageHeight; ++y) {
+ for (int x = 0; x < imageWidth; ++x) {
+ uint32_t& p = *bitmap.getAddr32(x, y);
+ const int r = SkColorGetR(p);
+ const int g = SkColorGetG(p);
+ const int b = SkColorGetB(p);
+ if (printConversions) {
+ SkColorSpacePrintf("\nraw = (%d, %d, %d)\t", r, g, b);
+ }
+
+ float lab[4] = { r * (1.f/255.f), g * (1.f/255.f), b * (1.f/255.f), 1.f };
+
+ colorLUT->interp3D(lab, lab);
+
+ // Lab has ranges [0,100] for L and [-128,127] for a and b
+ // but the ICC profile loader stores as [0,1]. The ICC
+ // specifies an offset of -128 to convert.
+ // note: formula could be adjusted to remove this conversion,
+ // but for now let's keep it like this for clarity until
+ // an optimized version is added.
+ lab[0] *= 100.f;
+ lab[1] = 255.f * lab[1] - 128.f;
+ lab[2] = 255.f * lab[2] - 128.f;
+ if (printConversions) {
+ SkColorSpacePrintf("Lab = < %f, %f, %f >\n", lab[0], lab[1], lab[2]);
+ }
+
+ // convert from Lab to XYZ
+ float Y = (lab[0] + 16.f) * (1.f/116.f);
+ float X = lab[1] * (1.f/500.f) + Y;
+ float Z = Y - (lab[2] * (1.f/200.f));
+ float cubed;
+ cubed = X*X*X;
+ if (cubed > 0.008856f)
+ X = cubed;
+ else
+ X = (X - (16.f/116.f)) * (1.f/7.787f);
+ cubed = Y*Y*Y;
+ if (cubed > 0.008856f)
+ Y = cubed;
+ else
+ Y = (Y - (16.f/116.f)) * (1.f/7.787f);
+ cubed = Z*Z*Z;
+ if (cubed > 0.008856f)
+ Z = cubed;
+ else
+ Z = (Z - (16.f/116.f)) * (1.f/7.787f);
+
+ // adjust to D50 illuminant
+ X *= 0.96422f;
+ Y *= 1.00000f;
+ Z *= 0.82521f;
+
+ if (printConversions) {
+ SkColorSpacePrintf("XYZ = (%4f, %4f, %4f)\t", X, Y, Z);
+ }
+
+ // convert XYZ -> linear sRGB
+ Sk4f lRGB( 3.1338561f*X - 1.6168667f*Y - 0.4906146f*Z,
+ -0.9787684f*X + 1.9161415f*Y + 0.0334540f*Z,
+ 0.0719453f*X - 0.2289914f*Y + 1.4052427f*Z,
+ 1.f);
+ // and apply sRGB gamma
+ Sk4i sRGB = sk_linear_to_srgb(lRGB);
+ if (printConversions) {
+ SkColorSpacePrintf("sRGB = (%d, %d, %d)\n", sRGB[0], sRGB[1], sRGB[2]);
+ }
+ p = SkColorSetRGB(sRGB[0], sRGB[1], sRGB[2]);
+ }
+ }
+ }
+ const int freeWidth = fWidth - 2*imageWidth;
+ const int freeHeight = fHeight - imageHeight;
+ canvas->drawBitmap(bitmap,
+ static_cast<SkScalar>((col+1) * (freeWidth / 3) + col*imageWidth),
+ static_cast<SkScalar>(freeHeight / 2));
+ ++col;
+ }
+
+private:
+ const int fWidth;
+ const int fHeight;
+
+ typedef skiagm::GM INHERITED;
+};
+
+DEF_GM( return new LabPCSDemoGM; )
diff --git a/gn/gm.gni b/gn/gm.gni
index 6ff8ec96ad..4732e0368d 100644
--- a/gn/gm.gni
+++ b/gn/gm.gni
@@ -171,6 +171,7 @@ gm_sources = [
"$_gm/imagetoyuvplanes.cpp",
"$_gm/internal_links.cpp",
"$_gm/inversepaths.cpp",
+ "$_gm/labpcsdemo.cpp",
"$_gm/largeglyphblur.cpp",
"$_gm/lattice.cpp",
"$_gm/lcdblendmodes.cpp",
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index 7a264d5d99..69b80273df 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -11,7 +11,6 @@
#include "SkJpegDecoderMgr.h"
#include "SkCodecPriv.h"
#include "SkColorPriv.h"
-#include "SkColorSpace_Base.h"
#include "SkStream.h"
#include "SkTemplates.h"
#include "SkTypes.h"
@@ -230,18 +229,7 @@ bool SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
sk_sp<SkData> iccData = get_icc_profile(decoderMgr->dinfo());
sk_sp<SkColorSpace> colorSpace = nullptr;
if (iccData) {
- SkColorSpace_Base::InputColorFormat inputColorFormat =
- SkColorSpace_Base::InputColorFormat::kRGB;
- switch (decoderMgr->dinfo()->jpeg_color_space) {
- case JCS_CMYK:
- case JCS_YCCK:
- inputColorFormat = SkColorSpace_Base::InputColorFormat::kCMYK;
- break;
- default:
- break;
- }
- colorSpace = SkColorSpace_Base::MakeICC(iccData->data(), iccData->size(),
- inputColorFormat);
+ colorSpace = SkColorSpace::MakeICC(iccData->data(), iccData->size());
if (!colorSpace) {
SkCodecPrintf("Could not create SkColorSpace from ICC data.\n");
}
@@ -380,6 +368,9 @@ bool SkJpegCodec::setOutputColorSpace(const SkImageInfo& dstInfo) {
// we must do it ourselves.
J_COLOR_SPACE encodedColorType = fDecoderMgr->dinfo()->jpeg_color_space;
bool isCMYK = (JCS_CMYK == encodedColorType || JCS_YCCK == encodedColorType);
+ if (isCMYK && this->colorXform()) {
+ return false;
+ }
// Check for valid color types and set the output color space
switch (dstInfo.colorType()) {
@@ -578,7 +569,7 @@ SkCodec::Result SkJpegCodec::onGetPixels(const SkImageInfo& dstInfo,
SkASSERT(1 == dinfo->rec_outbuf_height);
J_COLOR_SPACE colorSpace = dinfo->out_color_space;
- if (JCS_CMYK == colorSpace && nullptr == this->colorXform()) {
+ if (JCS_CMYK == colorSpace) {
this->initializeSwizzler(dstInfo, options);
}
diff --git a/src/core/SkColorLookUpTable.cpp b/src/core/SkColorLookUpTable.cpp
index e2da357234..eb832b3214 100644
--- a/src/core/SkColorLookUpTable.cpp
+++ b/src/core/SkColorLookUpTable.cpp
@@ -8,23 +8,7 @@
#include "SkColorLookUpTable.h"
#include "SkFloatingPoint.h"
-void SkColorLookUpTable::interp(float* dst, const float* src) const {
- if (fInputChannels == 3) {
- interp3D(dst, src);
- } else {
- SkASSERT(dst != src);
- // index gets initialized as the algorithm proceeds by interpDimension.
- // It's just there to store the choice of low/high so far.
- int index[kMaxColorChannels];
- for (uint8_t outputDimension = 0; outputDimension < kOutputChannels; ++outputDimension) {
- dst[outputDimension] = interpDimension(src, fInputChannels - 1, outputDimension,
- index);
- }
- }
-}
-
-void SkColorLookUpTable::interp3D(float* dst, const float* src) const {
- SkASSERT(3 == kOutputChannels);
+void SkColorLookUpTable::interp3D(float dst[3], float src[3]) const {
// Call the src components x, y, and z.
const uint8_t maxX = fGridPoints[0] - 1;
const uint8_t maxY = fGridPoints[1] - 1;
@@ -127,36 +111,3 @@ void SkColorLookUpTable::interp3D(float* dst, const float* src) const {
ptr++;
}
}
-
-float SkColorLookUpTable::interpDimension(const float* src, int inputDimension,
- int outputDimension,
- int index[kMaxColorChannels]) const {
- // Base case. We've already decided whether to use the low or high point for each dimension
- // which is stored inside of index[] where index[i] gives the point in the CLUT to use for
- // input dimension i.
- if (-1 == inputDimension) {
- // compute index into CLUT and look up the colour
- int outputIndex = outputDimension;
- int indexMultiplier = kOutputChannels;
- for (int i = fInputChannels - 1; i >= 0; --i) {
- outputIndex += index[i] * indexMultiplier;
- indexMultiplier *= fGridPoints[i];
- }
- return table()[outputIndex];
- }
- // for each dimension (input channel), try both the low and high point for it
- // and then do the same recursively for the later dimensions.
- // Finally, we need to LERP the results. ie LERP X then LERP Y then LERP Z.
- const float x = src[inputDimension] * (fGridPoints[inputDimension] - 1);
- // try the low point for this dimension
- index[inputDimension] = sk_float_floor2int(x);
- const float diff = x - index[inputDimension];
- // and recursively LERP all sub-dimensions with the current dimension fixed to the low point
- const float lo = interpDimension(src, inputDimension - 1, outputDimension, index);
- // now try the high point for this dimension
- index[inputDimension] = sk_float_ceil2int(x);
- // and recursively LERP all sub-dimensions with the current dimension fixed to the high point
- const float hi = interpDimension(src, inputDimension - 1, outputDimension, index);
- // then LERP the results based on the current dimension
- return (1 - diff) * lo + diff * hi;
-}
diff --git a/src/core/SkColorLookUpTable.h b/src/core/SkColorLookUpTable.h
index e563b2d4a2..020a953700 100644
--- a/src/core/SkColorLookUpTable.h
+++ b/src/core/SkColorLookUpTable.h
@@ -11,49 +11,25 @@
#include "SkRefCnt.h"
#include "SkTemplates.h"
-static constexpr uint8_t kMaxColorChannels = 4;
-
class SkColorLookUpTable : public SkRefCnt {
public:
static constexpr uint8_t kOutputChannels = 3;
- SkColorLookUpTable(uint8_t inputChannels, const uint8_t gridPoints[kMaxColorChannels])
- : fInputChannels(inputChannels) {
- SkASSERT(inputChannels >= 1 && inputChannels <= kMaxColorChannels);
- memcpy(fGridPoints, gridPoints, fInputChannels * sizeof(uint8_t));
+ SkColorLookUpTable(uint8_t inputChannels, const uint8_t gridPoints[3]) {
+ SkASSERT(3 == inputChannels);
+ memcpy(fGridPoints, gridPoints, 3 * sizeof(uint8_t));
}
- /**
- * If fInputChannels == kOutputChannels == 3, performs tetrahedral interpolation, otherwise
- * performs multilinear interpolation (ie LERP for n =1, bilinear for n=2, trilinear for n=3)
- * with fInputChannels input dimensions and kOutputChannels output dimensions.
- * |dst| can be |src| only when fInputChannels == kOutputChannels == 3
- * |dst| is the destination pixel, must have at least kOutputChannels elements.
- * |src| is the source pixel, must have at least fInputChannels elements.
- */
- void interp(float* dst, const float* src) const;
-
- int inputChannels() const { return fInputChannels; }
-
- int outputChannels() const { return kOutputChannels; }
+ void interp3D(float dst[3], float src[3]) const;
private:
const float* table() const {
return SkTAddOffset<const float>(this, sizeof(SkColorLookUpTable));
}
- /**
- * Performs tetrahedral interpolation with 3 input and 3 output dimensions.
- * |dst| can be |src|
- */
- void interp3D(float* dst, const float* src) const;
-
- // recursively LERPs one dimension at a time. Used by interp() for the general case
- float interpDimension(const float* src, int inputDimension, int outputDimension,
- int index[kMaxColorChannels]) const;
+ uint8_t fGridPoints[3];
- uint8_t fInputChannels;
- uint8_t fGridPoints[kMaxColorChannels];
+ friend class SkColorSpaceXform_A2B;
public:
// Objects of this type are created in a custom fashion using sk_malloc_throw
diff --git a/src/core/SkColorSpace.cpp b/src/core/SkColorSpace.cpp
index e9db37a810..c09387bbad 100644
--- a/src/core/SkColorSpace.cpp
+++ b/src/core/SkColorSpace.cpp
@@ -178,15 +178,18 @@ sk_sp<SkColorSpace> SkColorSpace::MakeRGB(const SkColorSpaceTransferFn& coeffs,
}
void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransferFn));
- sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
+ sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas());
SkColorSpaceTransferFn* fn = SkTAddOffset<SkColorSpaceTransferFn>(memory, sizeof(SkGammas));
*fn = coeffs;
+ gammas->fRedType = SkGammas::Type::kParam_Type;
+ gammas->fGreenType = SkGammas::Type::kParam_Type;
+ gammas->fBlueType = SkGammas::Type::kParam_Type;
+
SkGammas::Data data;
data.fParamOffset = 0;
- for (int channel = 0; channel < 3; ++channel) {
- gammas->fType[channel] = SkGammas::Type::kParam_Type;
- gammas->fData[channel] = data;
- }
+ gammas->fRedData = data;
+ gammas->fGreenData = data;
+ gammas->fBlueData = data;
return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(kNonStandard_SkGammaNamed,
std::move(gammas), toXYZD50, nullptr));
}
diff --git a/src/core/SkColorSpaceXform_A2B.cpp b/src/core/SkColorSpaceXform_A2B.cpp
index b0495cfef2..f2f694349f 100644
--- a/src/core/SkColorSpaceXform_A2B.cpp
+++ b/src/core/SkColorSpaceXform_A2B.cpp
@@ -169,33 +169,16 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
"None", "Named", "Value", "Table", "Param"
};
#endif
- int currentChannels = -1;
- switch (srcSpace->inputColorFormat()) {
- case SkColorSpace_Base::InputColorFormat::kRGB:
- currentChannels = 3;
- break;
- case SkColorSpace_Base::InputColorFormat::kCMYK:
- currentChannels = 4;
- // CMYK images from JPEGs (the only format that supports it) are actually
- // inverted CMYK, so we need to invert every channel.
- // TransferFn is y = -x + 1 for x < 1.f, otherwise 0x + 0, ie y = 1 - x for x in [0,1]
- this->addTransferFns({1.f, 0.f, 0.f, 0.f, 1.f, -1.f, 1.f}, 4);
- break;
- default:
- SkASSERT(false);
- }
// add in all input color space -> PCS xforms
for (int i = 0; i < srcSpace->count(); ++i) {
const SkColorSpace_A2B::Element& e = srcSpace->element(i);
- SkASSERT(e.inputChannels() == currentChannels);
- currentChannels = e.outputChannels();
switch (e.type()) {
case SkColorSpace_A2B::Element::Type::kGammaNamed:
if (kLinear_SkGammaNamed != e.gammaNamed()) {
SkCSXformPrintf("Gamma stage added: %s\n",
debugGammaNamed[(int)e.gammaNamed()]);
SkColorSpaceTransferFn fn = gammanamed_to_parametric(e.gammaNamed());
- this->addTransferFns(fn, currentChannels);
+ this->addTransferFn(fn, kRGB_Channels);
fElementsPipeline.append(SkRasterPipeline::clamp_0);
fElementsPipeline.append(SkRasterPipeline::clamp_1);
@@ -204,23 +187,23 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
case SkColorSpace_A2B::Element::Type::kGammas: {
const SkGammas& gammas = e.gammas();
SkCSXformPrintf("Gamma stage added:");
- for (int channel = 0; channel < gammas.channels(); ++channel) {
+ for (int channel = 0; channel < 3; ++channel) {
SkCSXformPrintf(" %s", debugGammas[(int)gammas.type(channel)]);
}
SkCSXformPrintf("\n");
bool gammaNeedsRef = false;
- for (int channel = 0; channel < gammas.channels(); ++channel) {
+ for (int channel = 0; channel < 3; ++channel) {
if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
SkTableTransferFn table = {
gammas.table(channel),
gammas.data(channel).fTable.fSize,
};
- this->addTableFn(table, channel);
+ this->addTableFn(table, static_cast<Channels>(channel));
gammaNeedsRef = true;
} else {
SkColorSpaceTransferFn fn = gamma_to_parametric(gammas, channel);
- this->addTransferFn(fn, channel);
+ this->addTransferFn(fn, static_cast<Channels>(channel));
}
}
if (gammaNeedsRef) {
@@ -232,8 +215,8 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
break;
}
case SkColorSpace_A2B::Element::Type::kCLUT:
- SkCSXformPrintf("CLUT (%d -> %d) stage added\n", e.colorLUT().inputChannels(),
- e.colorLUT().outputChannels());
+ SkCSXformPrintf("CLUT stage added [%d][%d][%d]\n", e.colorLUT().fGridPoints[0],
+ e.colorLUT().fGridPoints[1], e.colorLUT().fGridPoints[2]);
fCLUTs.push_back(sk_ref_sp(&e.colorLUT()));
fElementsPipeline.append(SkRasterPipeline::color_lookup_table,
fCLUTs.back().get());
@@ -247,8 +230,6 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
}
}
- SkASSERT(3 == currentChannels);
-
// Lab PCS -> XYZ PCS
if (SkColorSpace_A2B::PCS::kLAB == srcSpace->pcs()) {
SkCSXformPrintf("Lab -> XYZ element added\n");
@@ -264,7 +245,7 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
if (!fLinearDstGamma) {
SkColorSpaceTransferFn fn =
invert_parametric(gammanamed_to_parametric(dstSpace->gammaNamed()));
- this->addTransferFns(fn, 3);
+ this->addTransferFn(fn, kRGB_Channels);
fElementsPipeline.append(SkRasterPipeline::clamp_0);
fElementsPipeline.append(SkRasterPipeline::clamp_1);
}
@@ -280,10 +261,10 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
};
fTableStorage.push_front(std::move(storage));
- this->addTableFn(table, channel);
+ this->addTableFn(table, static_cast<Channels>(channel));
} else {
SkColorSpaceTransferFn fn = invert_parametric(gamma_to_parametric(gammas, channel));
- this->addTransferFn(fn, channel);
+ this->addTransferFn(fn, static_cast<Channels>(channel));
}
}
@@ -292,47 +273,45 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
}
}
-void SkColorSpaceXform_A2B::addTransferFns(const SkColorSpaceTransferFn& fn, int channelCount) {
- for (int i = 0; i < channelCount; ++i) {
- this->addTransferFn(fn, i);
- }
-}
-
-void SkColorSpaceXform_A2B::addTransferFn(const SkColorSpaceTransferFn& fn, int channelIndex) {
+void SkColorSpaceXform_A2B::addTransferFn(const SkColorSpaceTransferFn& fn, Channels channels) {
fTransferFns.push_front(fn);
- switch (channelIndex) {
- case 0:
+ switch (channels) {
+ case kRGB_Channels:
+ fElementsPipeline.append(SkRasterPipeline::parametric_r, &fTransferFns.front());
+ fElementsPipeline.append(SkRasterPipeline::parametric_g, &fTransferFns.front());
+ fElementsPipeline.append(SkRasterPipeline::parametric_b, &fTransferFns.front());
+ break;
+ case kR_Channels:
fElementsPipeline.append(SkRasterPipeline::parametric_r, &fTransferFns.front());
break;
- case 1:
+ case kG_Channels:
fElementsPipeline.append(SkRasterPipeline::parametric_g, &fTransferFns.front());
break;
- case 2:
+ case kB_Channels:
fElementsPipeline.append(SkRasterPipeline::parametric_b, &fTransferFns.front());
break;
- case 3:
- fElementsPipeline.append(SkRasterPipeline::parametric_a, &fTransferFns.front());
- break;
default:
SkASSERT(false);
}
}
-void SkColorSpaceXform_A2B::addTableFn(const SkTableTransferFn& fn, int channelIndex) {
+void SkColorSpaceXform_A2B::addTableFn(const SkTableTransferFn& fn, Channels channels) {
fTableTransferFns.push_front(fn);
- switch (channelIndex) {
- case 0:
+ switch (channels) {
+ case kRGB_Channels:
+ fElementsPipeline.append(SkRasterPipeline::table_r, &fTableTransferFns.front());
+ fElementsPipeline.append(SkRasterPipeline::table_g, &fTableTransferFns.front());
+ fElementsPipeline.append(SkRasterPipeline::table_b, &fTableTransferFns.front());
+ break;
+ case kR_Channels:
fElementsPipeline.append(SkRasterPipeline::table_r, &fTableTransferFns.front());
break;
- case 1:
+ case kG_Channels:
fElementsPipeline.append(SkRasterPipeline::table_g, &fTableTransferFns.front());
break;
- case 2:
+ case kB_Channels:
fElementsPipeline.append(SkRasterPipeline::table_b, &fTableTransferFns.front());
break;
- case 3:
- fElementsPipeline.append(SkRasterPipeline::table_a, &fTableTransferFns.front());
- break;
default:
SkASSERT(false);
}
diff --git a/src/core/SkColorSpaceXform_A2B.h b/src/core/SkColorSpaceXform_A2B.h
index 9376491fa1..6beda285e1 100644
--- a/src/core/SkColorSpaceXform_A2B.h
+++ b/src/core/SkColorSpaceXform_A2B.h
@@ -32,11 +32,17 @@ public:
private:
SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace, SkColorSpace_XYZ* dstSpace);
- void addTransferFns(const SkColorSpaceTransferFn& fn, int channelCount);
+ enum Channels {
+ kRGB_Channels = -1,
+ kR_Channels = 0,
+ kG_Channels = 1,
+ kB_Channels = 2
+ };
- void addTransferFn(const SkColorSpaceTransferFn& fn, int channelIndex);
- void addTableFn(const SkTableTransferFn& table, int channelIndex);
+
+ void addTransferFn(const SkColorSpaceTransferFn& fn, Channels channels);
+ void addTableFn(const SkTableTransferFn& table, Channels channels);
void addMatrix(const SkMatrix44& matrix);
diff --git a/src/core/SkColorSpace_A2B.cpp b/src/core/SkColorSpace_A2B.cpp
index 1fef71a617..9fed82379a 100644
--- a/src/core/SkColorSpace_A2B.cpp
+++ b/src/core/SkColorSpace_A2B.cpp
@@ -7,10 +7,9 @@
#include "SkColorSpace_A2B.h"
-SkColorSpace_A2B::SkColorSpace_A2B(InputColorFormat inputColorFormat, std::vector<Element> elements,
- PCS pcs, sk_sp<SkData> profileData)
+SkColorSpace_A2B::SkColorSpace_A2B(PCS pcs, sk_sp<SkData> profileData,
+ std::vector<Element> elements)
: INHERITED(std::move(profileData))
- , fInputColorFormat(inputColorFormat)
- , fElements(std::move(elements))
, fPCS(pcs)
+ , fElements(std::move(elements))
{}
diff --git a/src/core/SkColorSpace_A2B.h b/src/core/SkColorSpace_A2B.h
index b42de77137..2fb7a83cab 100644
--- a/src/core/SkColorSpace_A2B.h
+++ b/src/core/SkColorSpace_A2B.h
@@ -16,15 +16,14 @@
// is stored in an A2B0 ICC tag. This allows us to use alternative profile
// connection spaces (CIELAB instead of just CIEXYZ), use color-lookup-tables
// to do color space transformations not representable as TRC functions or
-// matrix operations, as well as have multiple TRC functions. The CLUT also
-// allows conversion between non-3-channel input color spaces ie CMYK(4) to
-// a workable PCS (ie XYZ).
+// matrix operations, as well as have multiple TRC functions. The CLUT also has
+// the potential to allow conversion from input color spaces with a different
+// number of channels such as CMYK (4) or GRAY (1), but that is not supported yet.
//
-// AtoBType, lut8Type and lut16Type A2B0 tag types are supported. There are
-// also MPET (multi-processing-elements) A2B0 tags in the standard which allow
-// you to combine these 3 primitives (TRC, CLUT, matrix) in any order/quantity.
-// MPET tags are currently unsupported by the MakeICC parser, could be supported
-// here by the nature of the design.
+// Currently AtoBType A2B0 tag types are supported. There are also lut8Type,
+// lut16Type and MPET (multi-processing-elements) A2B0 tags which allow you to
+// combine these 3 primitives (TRC, CLUT, matrix) in any order/quantitiy,
+// but support for that is not implemented.
class SkColorSpace_A2B : public SkColorSpace_Base {
public:
const SkMatrix44* toXYZD50() const override {
@@ -46,12 +45,12 @@ public:
// as destination color spaces, so an inverse matrix is never wanted.
return nullptr;
}
-
+
bool onGammaCloseToSRGB() const override {
// There is no single gamma curve in an A2B0 profile
return false;
}
-
+
bool onGammaIsLinear() const override {
// There is no single gamma curve in an A2B0 profile
return false;
@@ -72,37 +71,29 @@ public:
class Element {
public:
- Element(SkGammaNamed gammaNamed, int channelCount)
+ explicit Element(SkGammaNamed gammaNamed)
: fType(Type::kGammaNamed)
, fGammaNamed(gammaNamed)
, fMatrix(SkMatrix44::kUninitialized_Constructor)
- , fInputChannels(channelCount)
- , fOutputChannels(channelCount)
{}
explicit Element(sk_sp<SkGammas> gammas)
: fType(Type::kGammas)
, fGammas(std::move(gammas))
- , fMatrix(SkMatrix44::kUninitialized_Constructor)
- , fInputChannels(fGammas->channels())
- , fOutputChannels(fGammas->channels())
+ , fMatrix(SkMatrix44::kUninitialized_Constructor)
{}
explicit Element(sk_sp<SkColorLookUpTable> colorLUT)
: fType(Type::kCLUT)
, fCLUT(std::move(colorLUT))
, fMatrix(SkMatrix44::kUninitialized_Constructor)
- , fInputChannels(fCLUT->inputChannels())
- , fOutputChannels(fCLUT->outputChannels())
{}
explicit Element(const SkMatrix44& matrix)
: fType(Type::kMatrix)
, fMatrix(matrix)
- , fInputChannels(3)
- , fOutputChannels(3)
{}
-
+
enum class Type {
kGammaNamed,
kGammas,
@@ -132,21 +123,15 @@ public:
return fMatrix;
}
- int inputChannels() const { return fInputChannels; }
-
- int outputChannels() const { return fOutputChannels; }
-
private:
Type fType;
SkGammaNamed fGammaNamed;
sk_sp<SkGammas> fGammas;
sk_sp<SkColorLookUpTable> fCLUT;
SkMatrix44 fMatrix;
- int fInputChannels;
- int fOutputChannels;
};
- const Element& element(int i) const { return fElements[i]; }
-
+ const Element& element(size_t i) const { return fElements[i]; }
+
int count() const { return (int)fElements.size(); }
// the intermediate profile connection space that this color space
@@ -155,20 +140,16 @@ public:
kLAB, // CIELAB
kXYZ // CIEXYZ
};
-
+
PCS pcs() const { return fPCS; }
- InputColorFormat inputColorFormat() const { return fInputColorFormat; }
-
private:
- SkColorSpace_A2B(InputColorFormat inputColorFormat, std::vector<Element> elements, PCS pcs,
- sk_sp<SkData> profileData);
+ SkColorSpace_A2B(PCS pcs, sk_sp<SkData> profileData, std::vector<Element> elements);
- InputColorFormat fInputColorFormat;
- std::vector<Element> fElements;
PCS fPCS;
+ std::vector<Element> fElements;
- friend class SkColorSpace_Base;
+ friend class SkColorSpace;
friend class ColorSpaceXformTest;
typedef SkColorSpace_Base INHERITED;
};
diff --git a/src/core/SkColorSpace_Base.h b/src/core/SkColorSpace_Base.h
index 947f725036..480febd7ff 100644
--- a/src/core/SkColorSpace_Base.h
+++ b/src/core/SkColorSpace_Base.h
@@ -83,8 +83,17 @@ struct SkGammas : SkRefCnt {
}
const Data& data(int i) const {
- SkASSERT(i >= 0 && i < fChannels);
- return fData[i];
+ switch (i) {
+ case 0:
+ return fRedData;
+ case 1:
+ return fGreenData;
+ case 2:
+ return fBlueData;
+ default:
+ SkASSERT(false);
+ return fRedData;
+ }
}
const float* table(int i) const {
@@ -98,24 +107,32 @@ struct SkGammas : SkRefCnt {
}
Type type(int i) const {
- SkASSERT(i >= 0 && i < fChannels);
- return fType[i];
- }
-
- uint8_t channels() const { return fChannels; }
-
- SkGammas(uint8_t channels)
- : fChannels(channels) {
- SkASSERT(channels <= kMaxColorChannels);
- for (uint8_t i = 0; i < kMaxColorChannels; ++i) {
- fType[i] = Type::kNone_Type;
+ switch (i) {
+ case 0:
+ return fRedType;
+ case 1:
+ return fGreenType;
+ case 2:
+ return fBlueType;
+ default:
+ SkASSERT(false);
+ return fRedType;
}
}
+ SkGammas()
+ : fRedType(Type::kNone_Type)
+ , fGreenType(Type::kNone_Type)
+ , fBlueType(Type::kNone_Type)
+ {}
+
// These fields should only be modified when initializing the struct.
- uint8_t fChannels;
- Data fData[kMaxColorChannels];
- Type fType[kMaxColorChannels];
+ Data fRedData;
+ Data fGreenData;
+ Data fBlueData;
+ Type fRedType;
+ Type fGreenType;
+ Type fBlueType;
// Objects of this type are sometimes created in a custom fashion using
// sk_malloc_throw and therefore must be sk_freed. We overload new to
@@ -171,17 +188,9 @@ public:
kXYZ,
kA2B
};
-
+
virtual Type type() const = 0;
-
- enum class InputColorFormat {
- kRGB,
- kCMYK
- };
-
- static sk_sp<SkColorSpace> MakeICC(const void* input, size_t len,
- InputColorFormat inputColorFormat);
-
+
protected:
SkColorSpace_Base(sk_sp<SkData> profileData);
diff --git a/src/core/SkColorSpace_ICC.cpp b/src/core/SkColorSpace_ICC.cpp
index ed65f816e1..5fe066ac11 100644
--- a/src/core/SkColorSpace_ICC.cpp
+++ b/src/core/SkColorSpace_ICC.cpp
@@ -49,7 +49,6 @@ static constexpr size_t kICCHeaderSize = 132;
static constexpr size_t kICCTagTableEntrySize = 12;
static constexpr uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' ');
-static constexpr uint32_t kCMYK_ColorSpace = SkSetFourByteTag('C', 'M', 'Y', 'K');
static constexpr uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r');
static constexpr uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r');
static constexpr uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r');
@@ -128,19 +127,9 @@ struct ICCProfileHeader {
fProfileClass == kColorSpace_Profile,
"Unsupported profile");
- switch (fInputColorSpace) {
- case kRGB_ColorSpace:
- SkColorSpacePrintf("RGB Input Color Space");
- break;
- case kCMYK_ColorSpace:
- SkColorSpacePrintf("CMYK Input Color Space\n");
- break;
- default:
- SkColorSpacePrintf("Unsupported Input Color Space: %c%c%c%c\n",
- (fInputColorSpace>>24)&0xFF, (fInputColorSpace>>16)&0xFF,
- (fInputColorSpace>> 8)&0xFF, (fInputColorSpace>> 0)&0xFF);
- return false;
- }
+ // TODO (msarett):
+ // All the profiles we've tested so far use RGB as the input color space.
+ return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space");
switch (fPCS) {
case kXYZ_PCSSpace:
@@ -151,9 +140,7 @@ struct ICCProfileHeader {
break;
default:
// ICC currently (V4.3) only specifices XYZ and Lab PCS spaces
- SkColorSpacePrintf("Unsupported PCS space: %c%c%c%c\n",
- (fPCS>>24)&0xFF, (fPCS>>16)&0xFF,
- (fPCS>> 8)&0xFF, (fPCS>> 0)&0xFF);
+ SkColorSpacePrintf("Unsupported PCS space\n");
return false;
}
@@ -629,6 +616,7 @@ static bool load_color_lut(sk_sp<SkColorLookUpTable>* colorLUT, uint32_t inputCh
return false;
}
+ SkASSERT(3 == inputChannels);
uint32_t numEntries = SkColorLookUpTable::kOutputChannels;
for (uint32_t i = 0; i < inputChannels; i++) {
if (0 == gridPoints[i]) {
@@ -747,12 +735,14 @@ static bool load_matrix(SkMatrix44* matrix, const uint8_t* src, size_t len, bool
}
static inline SkGammaNamed is_named(const sk_sp<SkGammas>& gammas) {
- for (uint8_t i = 0; i < gammas->channels(); ++i) {
- if (!gammas->isNamed(i) || gammas->data(i).fNamed != gammas->data(0).fNamed) {
- return kNonStandard_SkGammaNamed;
- }
+ if (gammas->isNamed(0) && gammas->isNamed(1) && gammas->isNamed(2) &&
+ gammas->fRedData.fNamed == gammas->fGreenData.fNamed &&
+ gammas->fRedData.fNamed == gammas->fBlueData.fNamed)
+ {
+ return gammas->fRedData.fNamed;
}
- return gammas->data(0).fNamed;
+
+ return kNonStandard_SkGammaNamed;
}
/**
@@ -762,87 +752,93 @@ static inline SkGammaNamed is_named(const sk_sp<SkGammas>& gammas) {
* read the table into heap memory. And for parametric gammas, we need to copy over the
* parameter values.
*
- * @param gammaNamed Out-variable. The named gamma curve.
- * @param gammas Out-variable. The stored gamma curve information. Can be null if
- * gammaNamed is a named curve
- * @param inputChannels The number of gamma input channels
- * @param rTagPtr Pointer to start of the gamma tag.
- * @param taglen The size in bytes of the tag
+ * @param gammaNamed Out-variable. The named gamma curve.
+ * @param gammas Out-variable. The stored gamma curve information. Can be null if
+ * gammaNamed is a named curve
+ * @param rTagPtr Pointer to start of the gamma tag.
+ * @param taglen The size in bytes of the tag
*
- * @return false on failure, true on success
+ * @return false on failure, true on success
*/
static bool parse_and_load_gamma(SkGammaNamed* gammaNamed, sk_sp<SkGammas>* gammas,
- uint8_t inputChannels, const uint8_t* tagSrc, size_t tagLen) {
- SkGammas::Data data[kMaxColorChannels];
- SkColorSpaceTransferFn params[kMaxColorChannels];
- SkGammas::Type type[kMaxColorChannels];
- const uint8_t* tagPtr[kMaxColorChannels];
-
- tagPtr[0] = tagSrc;
+ const uint8_t* rTagPtr, size_t tagLen)
+{
+ SkGammas::Data rData;
+ SkColorSpaceTransferFn rParams;
*gammaNamed = kNonStandard_SkGammaNamed;
// On an invalid first gamma, tagBytes remains set as zero. This causes the two
// subsequent to be treated as identical (which is what we want).
size_t tagBytes = 0;
- type[0] = parse_gamma(&data[0], &params[0], &tagBytes, tagPtr[0], tagLen);
- handle_invalid_gamma(&type[0], &data[0]);
+ SkGammas::Type rType = parse_gamma(&rData, &rParams, &tagBytes, rTagPtr, tagLen);
+ handle_invalid_gamma(&rType, &rData);
size_t alignedTagBytes = SkAlign4(tagBytes);
- bool allChannelsSame = false;
- if (inputChannels * alignedTagBytes <= tagLen) {
- allChannelsSame = true;
- for (uint8_t i = 1; i < inputChannels; ++i) {
- if (0 != memcmp(tagPtr, tagPtr + i * alignedTagBytes, tagBytes)) {
- allChannelsSame = false;
- break;
- }
- }
- }
- if (allChannelsSame) {
- if (SkGammas::Type::kNamed_Type == type[0]) {
- *gammaNamed = data[0].fNamed;
+ if ((3 * alignedTagBytes <= tagLen) &&
+ !memcmp(rTagPtr, rTagPtr + 1 * alignedTagBytes, tagBytes) &&
+ !memcmp(rTagPtr, rTagPtr + 2 * alignedTagBytes, tagBytes))
+ {
+ if (SkGammas::Type::kNamed_Type == rType) {
+ *gammaNamed = rData.fNamed;
} else {
size_t allocSize = sizeof(SkGammas);
- return_if_false(safe_add(allocSize, gamma_alloc_size(type[0], data[0]), &allocSize),
+ return_if_false(safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize),
"SkGammas struct is too large to allocate");
void* memory = sk_malloc_throw(allocSize);
- *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels));
- load_gammas(memory, 0, type[0], &data[0], params[0], tagPtr[0]);
+ *gammas = sk_sp<SkGammas>(new (memory) SkGammas());
+ load_gammas(memory, 0, rType, &rData, rParams, rTagPtr);
- for (uint8_t channel = 0; channel < inputChannels; ++channel) {
- (*gammas)->fType[channel] = type[0];
- (*gammas)->fData[channel] = data[0];
- }
+ (*gammas)->fRedType = rType;
+ (*gammas)->fGreenType = rType;
+ (*gammas)->fBlueType = rType;
+
+ (*gammas)->fRedData = rData;
+ (*gammas)->fGreenData = rData;
+ (*gammas)->fBlueData = rData;
}
} else {
- for (uint8_t channel = 1; channel < inputChannels; ++channel) {
- tagPtr[channel] = tagPtr[channel - 1] + alignedTagBytes;
- tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0;
- tagBytes = 0;
- type[channel] = parse_gamma(&data[channel], &params[channel], &tagBytes,
- tagPtr[channel], tagLen);
- handle_invalid_gamma(&type[channel], &data[channel]);
- alignedTagBytes = SkAlign4(tagBytes);
- }
+ const uint8_t* gTagPtr = rTagPtr + alignedTagBytes;
+ tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0;
+ SkGammas::Data gData;
+ SkColorSpaceTransferFn gParams;
+ tagBytes = 0;
+ SkGammas::Type gType = parse_gamma(&gData, &gParams, &tagBytes, gTagPtr,
+ tagLen);
+ handle_invalid_gamma(&gType, &gData);
+
+ alignedTagBytes = SkAlign4(tagBytes);
+ const uint8_t* bTagPtr = gTagPtr + alignedTagBytes;
+ tagLen = tagLen > alignedTagBytes ? tagLen - alignedTagBytes : 0;
+ SkGammas::Data bData;
+ SkColorSpaceTransferFn bParams;
+ SkGammas::Type bType = parse_gamma(&bData, &bParams, &tagBytes, bTagPtr,
+ tagLen);
+ handle_invalid_gamma(&bType, &bData);
size_t allocSize = sizeof(SkGammas);
- for (uint8_t channel = 0; channel < inputChannels; ++channel) {
- return_if_false(safe_add(allocSize, gamma_alloc_size(type[channel], data[channel]),
- &allocSize),
- "SkGammas struct is too large to allocate");
- }
+ return_if_false(safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize),
+ "SkGammas struct is too large to allocate");
+ return_if_false(safe_add(allocSize, gamma_alloc_size(gType, gData), &allocSize),
+ "SkGammas struct is too large to allocate");
+ return_if_false(safe_add(allocSize, gamma_alloc_size(bType, bData), &allocSize),
+ "SkGammas struct is too large to allocate");
void* memory = sk_malloc_throw(allocSize);
- *gammas = sk_sp<SkGammas>(new (memory) SkGammas(inputChannels));
+ *gammas = sk_sp<SkGammas>(new (memory) SkGammas());
uint32_t offset = 0;
- for (uint8_t channel = 0; channel < inputChannels; ++channel) {
- (*gammas)->fType[channel] = type[channel];
- offset += load_gammas(memory,offset, type[channel], &data[channel], params[channel],
- tagPtr[channel]);
- (*gammas)->fData[channel] = data[channel];
+ (*gammas)->fRedType = rType;
+ offset += load_gammas(memory, offset, rType, &rData, rParams, rTagPtr);
- }
+ (*gammas)->fGreenType = gType;
+ offset += load_gammas(memory, offset, gType, &gData, gParams, gTagPtr);
+
+ (*gammas)->fBlueType = bType;
+ load_gammas(memory, offset, bType, &bData, bParams, bTagPtr);
+
+ (*gammas)->fRedData = rData;
+ (*gammas)->fGreenData = gData;
+ (*gammas)->fBlueData = bData;
}
if (kNonStandard_SkGammaNamed == *gammaNamed) {
@@ -893,7 +889,7 @@ static bool load_lut_gammas(sk_sp<SkGammas>* gammas, size_t numTables, size_t en
"SkGammas struct is too large to allocate");
void* memory = sk_malloc_throw(allocSize);
- *gammas = sk_sp<SkGammas>(new (memory) SkGammas(numTables));
+ *gammas = sk_sp<SkGammas>(new (memory) SkGammas());
for (size_t tableIndex = 0; tableIndex < numTablesToUse; ++tableIndex) {
const uint8_t* ptr = src + readBytesPerChannel * tableIndex;
@@ -910,18 +906,24 @@ static bool load_lut_gammas(sk_sp<SkGammas>* gammas, size_t numTables, size_t en
}
}
- SkASSERT(1 == numTablesToUse|| numTables == numTablesToUse);
+ (*gammas)->fRedType = SkGammas::Type::kTable_Type;
+ (*gammas)->fGreenType = SkGammas::Type::kTable_Type;
+ (*gammas)->fBlueType = SkGammas::Type::kTable_Type;
- size_t tableOffset = 0;
- for (size_t tableIndex = 0; tableIndex < numTables; ++tableIndex) {
- (*gammas)->fType[tableIndex] = SkGammas::Type::kTable_Type;
- (*gammas)->fData[tableIndex].fTable.fOffset = tableOffset;
- (*gammas)->fData[tableIndex].fTable.fSize = entriesPerTable;
- if (numTablesToUse > 1) {
- tableOffset += writeBytesPerChannel;
- }
+ if (1 == numTablesToUse) {
+ (*gammas)->fRedData.fTable.fOffset = 0;
+ (*gammas)->fGreenData.fTable.fOffset = 0;
+ (*gammas)->fBlueData.fTable.fOffset = 0;
+ } else {
+ (*gammas)->fRedData.fTable.fOffset = 0;
+ (*gammas)->fGreenData.fTable.fOffset = writeBytesPerChannel;
+ (*gammas)->fBlueData.fTable.fOffset = writeBytesPerChannel * 2;
}
+ (*gammas)->fRedData.fTable.fSize = entriesPerTable;
+ (*gammas)->fGreenData.fTable.fSize = entriesPerTable;
+ (*gammas)->fBlueData.fTable.fSize = entriesPerTable;
+
return true;
}
@@ -932,23 +934,15 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con
// must be zero.
const uint8_t inputChannels = src[8];
const uint8_t outputChannels = src[9];
- if (SkColorLookUpTable::kOutputChannels != outputChannels) {
- // We only handle RGB outputs. The number of output channels must be 3.
- SkColorSpacePrintf("Output channels (%d) must equal 3 in A to B tag.\n", outputChannels);
- return false;
- }
- if (inputChannels == 0 || inputChannels > 4) {
- // And we only support 4 input channels.
- // ICC says up to 16 but our decode can only handle 4.
- // It could easily be extended to support up to 8, but we only allow CMYK/RGB
- // input color spaces which are 3 and 4 so let's restrict it to 4 instead of 8.
- // We can always change this check when we support bigger input spaces.
- SkColorSpacePrintf("Input channels (%d) must be between 1 and 4 in A to B tag.\n",
- inputChannels);
+ if (3 != inputChannels || SkColorLookUpTable::kOutputChannels != outputChannels) {
+ // We only handle (supposedly) RGB inputs and RGB outputs. The numbers of input
+ // channels and output channels both must be 3.
+ // TODO (msarett):
+ // Support different numbers of input channels. Ex: CMYK (4).
+ SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag.\n");
return false;
}
-
// It is important that these are loaded in the order of application, as the
// order you construct an A2B color space's elements is the order it is applied
@@ -958,14 +952,13 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con
const size_t tagLen = len - offsetToACurves;
SkGammaNamed gammaNamed;
sk_sp<SkGammas> gammas;
- if (!parse_and_load_gamma(&gammaNamed, &gammas, inputChannels, src + offsetToACurves,
- tagLen)) {
+ if (!parse_and_load_gamma(&gammaNamed, &gammas, src + offsetToACurves, tagLen)) {
return false;
}
if (gammas) {
elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
- } else if (kLinear_SkGammaNamed != gammaNamed) {
- elements->push_back(SkColorSpace_A2B::Element(gammaNamed, inputChannels));
+ } else {
+ elements->push_back(SkColorSpace_A2B::Element(gammaNamed));
}
}
@@ -982,8 +975,8 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con
return false;
}
- SkASSERT(inputChannels <= kMaxColorChannels);
- uint8_t gridPoints[kMaxColorChannels];
+ SkASSERT(3 == inputChannels);
+ uint8_t gridPoints[3];
for (uint32_t i = 0; i < inputChannels; ++i) {
gridPoints[i] = clutSrc[i];
}
@@ -1003,14 +996,13 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con
const size_t tagLen = len - offsetToMCurves;
SkGammaNamed gammaNamed;
sk_sp<SkGammas> gammas;
- if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToMCurves,
- tagLen)) {
+ if (!parse_and_load_gamma(&gammaNamed, &gammas, src + offsetToMCurves, tagLen)) {
return false;
}
if (gammas) {
elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
- } else if (kLinear_SkGammaNamed != gammaNamed) {
- elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels));
+ } else {
+ elements->push_back(SkColorSpace_A2B::Element(gammaNamed));
}
}
@@ -1019,7 +1011,7 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con
SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
if (!load_matrix(&matrix, src + offsetToMatrix, len - offsetToMatrix, true, pcs)) {
SkColorSpacePrintf("Failed to read matrix from A to B tag.\n");
- } else if (!matrix.isIdentity()) {
+ } else {
elements->push_back(SkColorSpace_A2B::Element(matrix));
}
}
@@ -1029,14 +1021,13 @@ bool load_a2b0_a_to_b_type(std::vector<SkColorSpace_A2B::Element>* elements, con
const size_t tagLen = len - offsetToBCurves;
SkGammaNamed gammaNamed;
sk_sp<SkGammas> gammas;
- if (!parse_and_load_gamma(&gammaNamed, &gammas, outputChannels, src + offsetToBCurves,
- tagLen)) {
+ if (!parse_and_load_gamma(&gammaNamed, &gammas, src + offsetToBCurves, tagLen)) {
return false;
}
if (gammas) {
elements->push_back(SkColorSpace_A2B::Element(std::move(gammas)));
- } else if (kLinear_SkGammaNamed != gammaNamed) {
- elements->push_back(SkColorSpace_A2B::Element(gammaNamed, outputChannels));
+ } else {
+ elements->push_back(SkColorSpace_A2B::Element(gammaNamed));
}
}
@@ -1061,19 +1052,12 @@ bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const
// The four bytes (4-7) that we skipped are reserved and must be zero.
const uint8_t inputChannels = src[8];
const uint8_t outputChannels = src[9];
- if (SkColorLookUpTable::kOutputChannels != outputChannels) {
- // We only handle RGB outputs. The number of output channels must be 3.
- SkColorSpacePrintf("Output channels (%d) must equal 3 in A to B tag.\n", outputChannels);
- return false;
- }
- if (inputChannels == 0 || inputChannels > 4) {
- // And we only support 4 input channels.
- // ICC says up to 16 but our decode can only handle 4.
- // It could easily be extended to support up to 8, but we only allow CMYK/RGB
- // input color spaces which are 3 and 4 so let's restrict it to 4 instead of 8.
- // We can always change this check when we support bigger input spaces.
- SkColorSpacePrintf("Input channels (%d) must be between 1 and 4 in A to B tag.\n",
- inputChannels);
+ if (3 != inputChannels || SkColorLookUpTable::kOutputChannels != outputChannels) {
+ // We only handle (supposedly) RGB inputs and RGB outputs. The numbers of input
+ // channels and output channels both must be 3.
+ // TODO (msarett):
+ // Support different numbers of input channels. Ex: CMYK (4).
+ SkColorSpacePrintf("Input and output channels must equal 3 in A to B tag.\n");
return false;
}
@@ -1082,13 +1066,7 @@ bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const
SkMatrix44 matrix(SkMatrix44::kUninitialized_Constructor);
load_matrix(&matrix, &src[12], len - 12, false, pcs);
- if (!matrix.isIdentity()) {
- // ICC specs (10.8/10.9) say lut8/16Type profiles must have identity matrices
- // if the input color space is not PCSXYZ, and we do not support PCSXYZ input color spaces
- // so we should never encounter a non-identity matrix here.
- SkColorSpacePrintf("non-Identity matrix found in non-XYZ input color space lut profile");
- return false;
- }
+ elements->push_back(SkColorSpace_A2B::Element(matrix));
size_t dataOffset = 48;
// # of input table entries
@@ -1133,21 +1111,12 @@ bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const
return false;
}
SkASSERT(inputGammas);
- const SkGammaNamed inputGammaNamed = is_named(inputGammas);
- if (kLinear_SkGammaNamed != inputGammaNamed) {
- if (kNonStandard_SkGammaNamed != inputGammaNamed) {
- elements->push_back(SkColorSpace_A2B::Element(inputGammaNamed, inputChannels));
- } else {
- elements->push_back(SkColorSpace_A2B::Element(std::move(inputGammas)));
- }
- }
+ elements->push_back(SkColorSpace_A2B::Element(std::move(inputGammas)));
const size_t clutOffset = inputOffset + precision*inTableEntries*inputChannels;
return_if_false(len >= clutOffset, "A2B0 lutnType tag too small for CLUT");
sk_sp<SkColorLookUpTable> colorLUT;
- const uint8_t gridPoints[kMaxColorChannels] = {
- clutGridPoints, clutGridPoints, clutGridPoints, clutGridPoints
- };
+ const uint8_t gridPoints[3] = {clutGridPoints, clutGridPoints, clutGridPoints};
if (!load_color_lut(&colorLUT, inputChannels, precision, gridPoints, src + clutOffset,
len - clutOffset)) {
SkColorSpacePrintf("Failed to read color LUT from lutnType tag.\n");
@@ -1169,33 +1138,13 @@ bool load_a2b0_lutn_type(std::vector<SkColorSpace_A2B::Element>* elements, const
return false;
}
SkASSERT(outputGammas);
- const SkGammaNamed outputGammaNamed = is_named(outputGammas);
- if (kLinear_SkGammaNamed != outputGammaNamed) {
- if (kNonStandard_SkGammaNamed != outputGammaNamed) {
- elements->push_back(SkColorSpace_A2B::Element(outputGammaNamed, outputChannels));
- } else {
- elements->push_back(SkColorSpace_A2B::Element(std::move(outputGammas)));
- }
- }
+ elements->push_back(SkColorSpace_A2B::Element(std::move(outputGammas)));
return true;
}
-static inline int icf_channels(SkColorSpace_Base::InputColorFormat inputColorFormat) {
- switch (inputColorFormat) {
- case SkColorSpace_Base::InputColorFormat::kRGB:
- return 3;
- case SkColorSpace_Base::InputColorFormat::kCMYK:
- return 4;
- default:
- SkASSERT(false);
- return -1;
- }
-}
-
static bool load_a2b0(std::vector<SkColorSpace_A2B::Element>* elements, const uint8_t* src,
- size_t len, SkColorSpace_A2B::PCS pcs,
- SkColorSpace_Base::InputColorFormat inputColorFormat) {
+ size_t len, SkColorSpace_A2B::PCS pcs) {
const uint32_t type = read_big_endian_u32(src);
switch (type) {
case kTAG_AtoBType:
@@ -1204,54 +1153,26 @@ static bool load_a2b0(std::vector<SkColorSpace_A2B::Element>* elements, const ui
return false;
}
SkColorSpacePrintf("A2B0 tag is of type lutAtoBType\n");
- if (!load_a2b0_a_to_b_type(elements, src, len, pcs)) {
- return false;
- }
- break;
+ return load_a2b0_a_to_b_type(elements, src, len, pcs);
case kTAG_lut8Type:
if (len < 48) {
SkColorSpacePrintf("lut8 tag is too small (%d bytes).", len);
return false;
}
SkColorSpacePrintf("A2B0 tag of type lut8Type\n");
- if (!load_a2b0_lutn_type(elements, src, len, pcs)) {
- return false;
- }
- break;
+ return load_a2b0_lutn_type(elements, src, len, pcs);
case kTAG_lut16Type:
if (len < 52) {
SkColorSpacePrintf("lut16 tag is too small (%d bytes).", len);
return false;
}
SkColorSpacePrintf("A2B0 tag of type lut16Type\n");
- if (!load_a2b0_lutn_type(elements, src, len, pcs)) {
- return false;
- }
- break;
+ return load_a2b0_lutn_type(elements, src, len, pcs);
default:
SkColorSpacePrintf("Unsupported A to B tag type: %c%c%c%c\n", (type>>24)&0xFF,
(type>>16)&0xFF, (type>>8)&0xFF, type&0xFF);
- return false;
- }
- // now let's verify that the input/output channels of each A2B element actually match up
- SkASSERT(!elements->empty());
- if (icf_channels(inputColorFormat) != elements->front().inputChannels()) {
- SkColorSpacePrintf("Input channel count does not match first A2B element's input count");
- return false;
- }
- for (size_t i = 1; i < elements->size(); ++i) {
- if ((*elements)[i - 1].outputChannels() != (*elements)[i].inputChannels()) {
- SkColorSpacePrintf("A2B elements don't agree in input/output channel counts");
- return false;
- }
}
- SkASSERT(SkColorSpace_A2B::PCS::kLAB == pcs || SkColorSpace_A2B::PCS::kXYZ == pcs);
- static constexpr int kPCSChannels = 3; // must be PCSLAB or PCSXYZ
- if (kPCSChannels != elements->back().outputChannels()) {
- SkColorSpacePrintf("PCS channel count doesn't match last A2B element's output count");
- return false;
- }
- return true;
+ return false;
}
static bool tag_equals(const ICCTag* a, const ICCTag* b, const uint8_t* base) {
@@ -1289,11 +1210,6 @@ static inline bool is_close_to_d50(const SkMatrix44& matrix) {
}
sk_sp<SkColorSpace> SkColorSpace::MakeICC(const void* input, size_t len) {
- return SkColorSpace_Base::MakeICC(input, len, SkColorSpace_Base::InputColorFormat::kRGB);
-}
-
-sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len,
- InputColorFormat inputColorFormat) {
if (!input || len < kICCHeaderSize) {
return_null("Data is null or not large enough to contain an ICC profile");
}
@@ -1312,22 +1228,6 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len,
return nullptr;
}
- switch (inputColorFormat) {
- case InputColorFormat::kRGB:
- if (header.fInputColorSpace != kRGB_ColorSpace) {
- return_null("Provided input color format (RGB) does not match profile.\n");
- }
- break;
- case InputColorFormat::kCMYK:
- if (header.fInputColorSpace != kCMYK_ColorSpace) {
- return_null("Provided input color format (CMYK) does not match profile.\n");
- return nullptr;
- }
- break;
- default:
- return_null("Provided input color format not supported");
- }
-
// Adjust ptr and len before reading the tags.
if (len < header.fSize) {
SkColorSpacePrintf("ICC profile might be truncated.\n");
@@ -1357,84 +1257,102 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len,
}
}
- // Recognize color profile specified by A2B0 tag.
- // this must be done before XYZ profile checking, as a profile can have both
- // in which case we should use the A2B case to be accurate
- // (XYZ is there as a fallback / quick preview)
- const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0);
- if (a2b0) {
- const SkColorSpace_A2B::PCS pcs = kXYZ_PCSSpace == header.fPCS
- ? SkColorSpace_A2B::PCS::kXYZ
- : SkColorSpace_A2B::PCS::kLAB;
- std::vector<SkColorSpace_A2B::Element> elements;
- if (load_a2b0(&elements, a2b0->addr(base), a2b0->fLength, pcs, inputColorFormat)) {
- return sk_sp<SkColorSpace>(new SkColorSpace_A2B(inputColorFormat, std::move(elements),
- pcs, std::move(data)));
- }
- SkColorSpacePrintf("Ignoring malformed A2B0 tag.\n");
- }
-
- if (kRGB_ColorSpace == header.fInputColorSpace) {
- // Recognize the rXYZ, gXYZ, and bXYZ tags.
- const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ);
- const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ);
- const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ);
- // Lab PCS means the profile is required to be an n-component LUT-based
- // profile, so 3-component matrix-based profiles can only have an XYZ PCS
- if (r && g && b && kXYZ_PCSSpace == header.fPCS) {
- float toXYZ[9];
- if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) ||
- !load_xyz(&toXYZ[3], g->addr(base), g->fLength) ||
- !load_xyz(&toXYZ[6], b->addr(base), b->fLength))
- {
- return_null("Need valid rgb tags for XYZ space");
- }
- SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
- mat.set3x3(toXYZ[0], toXYZ[1], toXYZ[2],
- toXYZ[3], toXYZ[4], toXYZ[5],
- toXYZ[6], toXYZ[7], toXYZ[8]);
- if (!is_close_to_d50(mat)) {
- // QCMS treats these profiles as "bogus". I'm not sure if that's
- // correct, but we certainly do not handle non-D50 matrices
- // correctly. So I'll disable this for now.
- SkColorSpacePrintf("Matrix is not close to D50");
- return nullptr;
+ switch (header.fInputColorSpace) {
+ case kRGB_ColorSpace: {
+ // Recognize color profile specified by A2B0 tag.
+ // this must be done before XYZ profile checking, as a profile can have both
+ // in which case we should use the A2B case to be accurate
+ // (XYZ is there as a fallback / quick preview)
+ const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0);
+ if (a2b0) {
+ const SkColorSpace_A2B::PCS pcs = kXYZ_PCSSpace == header.fPCS
+ ? SkColorSpace_A2B::PCS::kXYZ
+ : SkColorSpace_A2B::PCS::kLAB;
+ std::vector<SkColorSpace_A2B::Element> elements;
+ if (load_a2b0(&elements, a2b0->addr(base), a2b0->fLength, pcs)) {
+ return sk_sp<SkColorSpace>(new SkColorSpace_A2B(pcs, std::move(data),
+ std::move(elements)));
+ }
+ SkColorSpacePrintf("Ignoring malformed A2B0 tag.\n");
}
- r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC);
- g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC);
- b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC);
-
- // If some, but not all, of the gamma tags are missing, assume that all
- // gammas are meant to be the same. This behavior is an arbitrary guess,
- // but it simplifies the code below.
- if ((!r || !g || !b) && (r || g || b)) {
- if (!r) {
- r = g ? g : b;
+ // Recognize the rXYZ, gXYZ, and bXYZ tags.
+ const ICCTag* r = ICCTag::Find(tags.get(), tagCount, kTAG_rXYZ);
+ const ICCTag* g = ICCTag::Find(tags.get(), tagCount, kTAG_gXYZ);
+ const ICCTag* b = ICCTag::Find(tags.get(), tagCount, kTAG_bXYZ);
+ // Lab PCS means the profile is required to be an n-component LUT-based
+ // profile, so 3-component matrix-based profiles can only have an XYZ PCS
+ if (r && g && b && kXYZ_PCSSpace == header.fPCS) {
+ float toXYZ[9];
+ if (!load_xyz(&toXYZ[0], r->addr(base), r->fLength) ||
+ !load_xyz(&toXYZ[3], g->addr(base), g->fLength) ||
+ !load_xyz(&toXYZ[6], b->addr(base), b->fLength))
+ {
+ return_null("Need valid rgb tags for XYZ space");
}
-
- if (!g) {
- g = r ? r : b;
+ SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
+ mat.set3x3(toXYZ[0], toXYZ[1], toXYZ[2],
+ toXYZ[3], toXYZ[4], toXYZ[5],
+ toXYZ[6], toXYZ[7], toXYZ[8]);
+ if (!is_close_to_d50(mat)) {
+ // QCMS treats these profiles as "bogus". I'm not sure if that's
+ // correct, but we certainly do not handle non-D50 matrices
+ // correctly. So I'll disable this for now.
+ SkColorSpacePrintf("Matrix is not close to D50");
+ return nullptr;
}
- if (!b) {
- b = r ? r : g;
+ r = ICCTag::Find(tags.get(), tagCount, kTAG_rTRC);
+ g = ICCTag::Find(tags.get(), tagCount, kTAG_gTRC);
+ b = ICCTag::Find(tags.get(), tagCount, kTAG_bTRC);
+
+ // If some, but not all, of the gamma tags are missing, assume that all
+ // gammas are meant to be the same. This behavior is an arbitrary guess,
+ // but it simplifies the code below.
+ if ((!r || !g || !b) && (r || g || b)) {
+ if (!r) {
+ r = g ? g : b;
+ }
+
+ if (!g) {
+ g = r ? r : b;
+ }
+
+ if (!b) {
+ b = r ? r : g;
+ }
}
- }
- SkGammaNamed gammaNamed = kNonStandard_SkGammaNamed;
- sk_sp<SkGammas> gammas = nullptr;
- size_t tagBytes;
- if (r && g && b) {
- if (tag_equals(r, g, base) && tag_equals(g, b, base)) {
- SkGammas::Data data;
- SkColorSpaceTransferFn params;
- SkGammas::Type type =
- parse_gamma(&data, &params, &tagBytes, r->addr(base), r->fLength);
- handle_invalid_gamma(&type, &data);
-
- if (SkGammas::Type::kNamed_Type == type) {
- gammaNamed = data.fNamed;
+ SkGammaNamed gammaNamed = kNonStandard_SkGammaNamed;
+ sk_sp<SkGammas> gammas = nullptr;
+ size_t tagBytes;
+ if (r && g && b) {
+ if (tag_equals(r, g, base) && tag_equals(g, b, base)) {
+ SkGammas::Data data;
+ SkColorSpaceTransferFn params;
+ SkGammas::Type type =
+ parse_gamma(&data, &params, &tagBytes, r->addr(base), r->fLength);
+ handle_invalid_gamma(&type, &data);
+
+ if (SkGammas::Type::kNamed_Type == type) {
+ gammaNamed = data.fNamed;
+ } else {
+ size_t allocSize = sizeof(SkGammas);
+ if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) {
+ return_null("SkGammas struct is too large to allocate");
+ }
+ void* memory = sk_malloc_throw(allocSize);
+ gammas = sk_sp<SkGammas>(new (memory) SkGammas());
+ load_gammas(memory, 0, type, &data, params, r->addr(base));
+
+ gammas->fRedType = type;
+ gammas->fGreenType = type;
+ gammas->fBlueType = type;
+
+ gammas->fRedData = data;
+ gammas->fGreenData = data;
+ gammas->fBlueData = data;
+ }
} else {
SkGammas::Data rData;
SkColorSpaceTransferFn rParams;
@@ -1455,81 +1373,53 @@ sk_sp<SkColorSpace> SkColorSpace_Base::MakeICC(const void* input, size_t len,
handle_invalid_gamma(&bType, &bData);
size_t allocSize = sizeof(SkGammas);
- if (!safe_add(allocSize, gamma_alloc_size(type, data), &allocSize)) {
+ if (!safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize) ||
+ !safe_add(allocSize, gamma_alloc_size(gType, gData), &allocSize) ||
+ !safe_add(allocSize, gamma_alloc_size(bType, bData), &allocSize))
+ {
return_null("SkGammas struct is too large to allocate");
}
void* memory = sk_malloc_throw(allocSize);
- gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
- load_gammas(memory, 0, type, &data, params, r->addr(base));
+ gammas = sk_sp<SkGammas>(new (memory) SkGammas());
- for (int i = 0; i < 3; ++i) {
- gammas->fType[i] = type;
- gammas->fData[i] = data;
- }
- }
- } else {
- SkGammas::Data rData;
- SkColorSpaceTransferFn rParams;
- SkGammas::Type rType =
- parse_gamma(&rData, &rParams, &tagBytes, r->addr(base), r->fLength);
- handle_invalid_gamma(&rType, &rData);
-
- SkGammas::Data gData;
- SkColorSpaceTransferFn gParams;
- SkGammas::Type gType =
- parse_gamma(&gData, &gParams, &tagBytes, g->addr(base), g->fLength);
- handle_invalid_gamma(&gType, &gData);
-
- SkGammas::Data bData;
- SkColorSpaceTransferFn bParams;
- SkGammas::Type bType =
- parse_gamma(&bData, &bParams, &tagBytes, b->addr(base), b->fLength);
- handle_invalid_gamma(&bType, &bData);
-
- size_t allocSize = sizeof(SkGammas);
- if (!safe_add(allocSize, gamma_alloc_size(rType, rData), &allocSize) ||
- !safe_add(allocSize, gamma_alloc_size(gType, gData), &allocSize) ||
- !safe_add(allocSize, gamma_alloc_size(bType, bData), &allocSize)) {
- return_null("SkGammas struct is too large to allocate");
- }
- void* memory = sk_malloc_throw(allocSize);
- gammas = sk_sp<SkGammas>(new (memory) SkGammas(3));
+ uint32_t offset = 0;
+ gammas->fRedType = rType;
+ offset += load_gammas(memory, offset, rType, &rData, rParams,
+ r->addr(base));
- uint32_t offset = 0;
- gammas->fType[0] = rType;
- offset += load_gammas(memory, offset, rType, &rData, rParams,
- r->addr(base));
+ gammas->fGreenType = gType;
+ offset += load_gammas(memory, offset, gType, &gData, gParams,
+ g->addr(base));
- gammas->fType[1] = gType;
- offset += load_gammas(memory, offset, gType, &gData, gParams,
- g->addr(base));
+ gammas->fBlueType = bType;
+ load_gammas(memory, offset, bType, &bData, bParams, b->addr(base));
- gammas->fType[2] = bType;
- load_gammas(memory, offset, bType, &bData, bParams, b->addr(base));
+ gammas->fRedData = rData;
+ gammas->fGreenData = gData;
+ gammas->fBlueData = bData;
+ }
+ } else {
+ // Guess sRGB if the profile is missing transfer functions.
+ gammaNamed = kSRGB_SkGammaNamed;
+ }
- gammas->fData[0] = rData;
- gammas->fData[1] = gData;
- gammas->fData[2] = bData;
+ if (kNonStandard_SkGammaNamed == gammaNamed) {
+ // It's possible that we'll initially detect non-matching gammas, only for
+ // them to evaluate to the same named gamma curve.
+ gammaNamed = is_named(gammas);
}
- } else {
- // Guess sRGB if the profile is missing transfer functions.
- gammaNamed = kSRGB_SkGammaNamed;
- }
- if (kNonStandard_SkGammaNamed == gammaNamed) {
- // It's possible that we'll initially detect non-matching gammas, only for
- // them to evaluate to the same named gamma curve.
- gammaNamed = is_named(gammas);
- }
+ if (kNonStandard_SkGammaNamed == gammaNamed) {
+ return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed,
+ std::move(gammas),
+ mat, std::move(data)));
+ }
- if (kNonStandard_SkGammaNamed == gammaNamed) {
- return sk_sp<SkColorSpace>(new SkColorSpace_XYZ(gammaNamed,
- std::move(gammas),
- mat, std::move(data)));
+ return SkColorSpace_Base::MakeRGB(gammaNamed, mat);
}
-
- return SkColorSpace_Base::MakeRGB(gammaNamed, mat);
}
+ default:
+ break;
}
return_null("ICC profile contains unsupported colorspace");
diff --git a/src/core/SkColorSpace_XYZ.cpp b/src/core/SkColorSpace_XYZ.cpp
index 4b90cae6a4..f99702d19b 100644
--- a/src/core/SkColorSpace_XYZ.cpp
+++ b/src/core/SkColorSpace_XYZ.cpp
@@ -31,9 +31,8 @@ SkColorSpace_XYZ::SkColorSpace_XYZ(SkGammaNamed gammaNamed, sk_sp<SkGammas> gamm
, fGammas(std::move(gammas))
, fToXYZD50(toXYZD50)
, fToXYZD50Hash(SkGoodHash()(toXYZD50))
- , fFromXYZD50(SkMatrix44::kUninitialized_Constructor) {
- SkASSERT(!fGammas || 3 == fGammas->channels());
-}
+ , fFromXYZD50(SkMatrix44::kUninitialized_Constructor)
+{}
const SkMatrix44* SkColorSpace_XYZ::fromXYZD50() const {
fFromXYZOnce([this] {
diff --git a/src/core/SkRasterPipeline.h b/src/core/SkRasterPipeline.h
index 7338c4d001..9e8f8f7906 100644
--- a/src/core/SkRasterPipeline.h
+++ b/src/core/SkRasterPipeline.h
@@ -76,8 +76,7 @@
M(matrix_2x3) M(matrix_3x4) M(matrix_4x5) \
M(matrix_perspective) \
M(parametric_r) M(parametric_g) M(parametric_b) \
- M(parametric_a) \
- M(table_r) M(table_g) M(table_b) M(table_a) \
+ M(table_r) M(table_g) M(table_b) \
M(color_lookup_table) M(lab_to_xyz) \
M(clamp_x) M(mirror_x) M(repeat_x) \
M(clamp_y) M(mirror_y) M(repeat_y) \
diff --git a/src/opts/SkRasterPipeline_opts.h b/src/opts/SkRasterPipeline_opts.h
index 66668bd6d9..ee8a9bf029 100644
--- a/src/opts/SkRasterPipeline_opts.h
+++ b/src/opts/SkRasterPipeline_opts.h
@@ -632,7 +632,6 @@ SI SkNf parametric(const SkNf& v, const SkColorSpaceTransferFn& p) {
STAGE(parametric_r) { r = parametric(r, *(const SkColorSpaceTransferFn*)ctx); }
STAGE(parametric_g) { g = parametric(g, *(const SkColorSpaceTransferFn*)ctx); }
STAGE(parametric_b) { b = parametric(b, *(const SkColorSpaceTransferFn*)ctx); }
-STAGE(parametric_a) { a = parametric(a, *(const SkColorSpaceTransferFn*)ctx); }
SI SkNf table(const SkNf& v, const SkTableTransferFn& table) {
float result[N];
@@ -644,29 +643,23 @@ SI SkNf table(const SkNf& v, const SkTableTransferFn& table) {
STAGE(table_r) { r = table(r, *(const SkTableTransferFn*)ctx); }
STAGE(table_g) { g = table(g, *(const SkTableTransferFn*)ctx); }
STAGE(table_b) { b = table(b, *(const SkTableTransferFn*)ctx); }
-STAGE(table_a) { a = table(a, *(const SkTableTransferFn*)ctx); }
STAGE(color_lookup_table) {
const SkColorLookUpTable* colorLUT = (const SkColorLookUpTable*)ctx;
- SkASSERT(3 == colorLUT->inputChannels() || 4 == colorLUT->inputChannels());
- SkASSERT(3 == colorLUT->outputChannels());
+ float rgb[3];
float result[3][N];
for (int i = 0; i < N; ++i) {
- const float in[4] = { r[i], g[i], b[i], a[i] };
- float out[3];
- colorLUT->interp(out, in);
- for (int j = 0; j < colorLUT->outputChannels(); ++j) {
- result[j][i] = out[j];
- }
+ rgb[0] = r[i];
+ rgb[1] = g[i];
+ rgb[2] = b[i];
+ colorLUT->interp3D(rgb, rgb);
+ result[0][i] = rgb[0];
+ result[1][i] = rgb[1];
+ result[2][i] = rgb[2];
}
r = SkNf::Load(result[0]);
g = SkNf::Load(result[1]);
b = SkNf::Load(result[2]);
- if (4 == colorLUT->inputChannels()) {
- // we must set the pixel to opaque, as the alpha channel was used
- // as input before this.
- a = 1.f;
- }
}
STAGE(lab_to_xyz) {
diff --git a/tests/ColorSpaceXformTest.cpp b/tests/ColorSpaceXformTest.cpp
index d2bd2a3358..0e67fe497d 100644
--- a/tests/ColorSpaceXformTest.cpp
+++ b/tests/ColorSpaceXformTest.cpp
@@ -16,8 +16,6 @@
#include "SkColorSpaceXform_Base.h"
#include "Test.h"
-static constexpr int kChannels = 3;
-
class ColorSpaceXformTest {
public:
static std::unique_ptr<SkColorSpaceXform> CreateIdentityXform(const sk_sp<SkGammas>& gammas) {
@@ -42,16 +40,13 @@ public:
SkMatrix44 arbitraryMatrix{SkMatrix44::kUninitialized_Constructor};
arbitraryMatrix.setRowMajorf(values);
if (kNonStandard_SkGammaNamed == gammaNamed) {
- SkASSERT(gammas);
srcElements.push_back(SkColorSpace_A2B::Element(gammas));
} else {
- srcElements.push_back(SkColorSpace_A2B::Element(gammaNamed, kChannels));
+ srcElements.push_back(SkColorSpace_A2B::Element(gammaNamed));
}
srcElements.push_back(SkColorSpace_A2B::Element(arbitraryMatrix));
- auto srcSpace =
- ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PCS::kXYZ,
- SkColorSpace_Base::InputColorFormat::kRGB,
- std::move(srcElements));
+ auto srcSpace = ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PCS::kXYZ,
+ std::move(srcElements));
sk_sp<SkColorSpace> dstSpace(new SkColorSpace_XYZ(gammaNamed, gammas, arbitraryMatrix,
nullptr));
@@ -60,15 +55,13 @@ public:
}
static sk_sp<SkColorSpace> CreateA2BSpace(SkColorSpace_A2B::PCS pcs,
- SkColorSpace_Base::InputColorFormat inputColorFormat,
std::vector<SkColorSpace_A2B::Element> elements) {
- return sk_sp<SkColorSpace>(new SkColorSpace_A2B(inputColorFormat, std::move(elements),
- pcs, nullptr));
+ return sk_sp<SkColorSpace>(new SkColorSpace_A2B(pcs, nullptr, std::move(elements)));
}
};
static bool almost_equal(int x, int y) {
- return SkTAbs(x - y) <= 1;
+ return SkTAbs(x - y) <= 1 ;
}
static void test_identity_xform(skiatest::Reporter* r, const sk_sp<SkGammas>& gammas,
@@ -108,7 +101,7 @@ static void test_identity_xform(skiatest::Reporter* r, const sk_sp<SkGammas>& ga
}
static void test_identity_xform_A2B(skiatest::Reporter* r, SkGammaNamed gammaNamed,
- const sk_sp<SkGammas>& gammas) {
+ const sk_sp<SkGammas>& gammas, bool repeat) {
// Arbitrary set of 10 pixels
constexpr int width = 10;
constexpr uint32_t srcPixels[width] = {
@@ -135,19 +128,24 @@ static void test_identity_xform_A2B(skiatest::Reporter* r, SkGammaNamed gammaNam
REPORTER_ASSERT(r, almost_equal(((srcPixels[i] >> 24) & 0xFF),
SkGetPackedA32(dstPixels[i])));
}
+
+ if (repeat) {
+ // We should cache part of the transform after the run. So it is interesting
+ // to make sure it still runs correctly the second time.
+ test_identity_xform_A2B(r, gammaNamed, gammas, false);
+ }
}
DEF_TEST(ColorSpaceXform_TableGamma, r) {
// Lookup-table based gamma curves
constexpr size_t tableSize = 10;
void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize);
- sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels));
- for (int i = 0; i < kChannels; ++i) {
- gammas->fType[i] = SkGammas::Type::kTable_Type;
- gammas->fData[i].fTable.fSize = tableSize;
- gammas->fData[i].fTable.fOffset = 0;
- }
-
+ sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas());
+ gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::kTable_Type;
+ gammas->fRedData.fTable.fSize = gammas->fGreenData.fTable.fSize =
+ gammas->fBlueData.fTable.fSize = tableSize;
+ gammas->fRedData.fTable.fOffset = gammas->fGreenData.fTable.fOffset =
+ gammas->fBlueData.fTable.fOffset = 0;
float* table = SkTAddOffset<float>(memory, sizeof(SkGammas));
table[0] = 0.00f;
@@ -161,18 +159,16 @@ DEF_TEST(ColorSpaceXform_TableGamma, r) {
table[8] = 0.75f;
table[9] = 1.00f;
test_identity_xform(r, gammas, true);
- test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas);
+ test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, true);
}
DEF_TEST(ColorSpaceXform_ParametricGamma, r) {
// Parametric gamma curves
void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(SkColorSpaceTransferFn));
- sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels));
- for (int i = 0; i < kChannels; ++i) {
- gammas->fType[i] = SkGammas::Type::kParam_Type;
- gammas->fData[i].fParamOffset = 0;
- }
-
+ sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas());
+ gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::kParam_Type;
+ gammas->fRedData.fParamOffset = gammas->fGreenData.fParamOffset =
+ gammas->fBlueData.fParamOffset = 0;
SkColorSpaceTransferFn* params = SkTAddOffset<SkColorSpaceTransferFn>
(memory, sizeof(SkGammas));
@@ -190,38 +186,36 @@ DEF_TEST(ColorSpaceXform_ParametricGamma, r) {
params->fC = 0.0f;
params->fG = 2.4f;
test_identity_xform(r, gammas, true);
- test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas);
+ test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, true);
}
DEF_TEST(ColorSpaceXform_ExponentialGamma, r) {
// Exponential gamma curves
- sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas(kChannels));
- for (int i = 0; i < kChannels; ++i) {
- gammas->fType[i] = SkGammas::Type::kValue_Type;
- gammas->fData[i].fValue = 1.4f;
- }
+ sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas());
+ gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::kValue_Type;
+ gammas->fRedData.fValue = gammas->fGreenData.fValue = gammas->fBlueData.fValue = 1.4f;
test_identity_xform(r, gammas, true);
- test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas);
+ test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, true);
}
DEF_TEST(ColorSpaceXform_NamedGamma, r) {
- sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas(kChannels));
- gammas->fType[0] = gammas->fType[1] = gammas->fType[2] = SkGammas::Type::kNamed_Type;
- gammas->fData[0].fNamed = kSRGB_SkGammaNamed;
- gammas->fData[1].fNamed = k2Dot2Curve_SkGammaNamed;
- gammas->fData[2].fNamed = kLinear_SkGammaNamed;
+ sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new SkGammas());
+ gammas->fRedType = gammas->fGreenType = gammas->fBlueType = SkGammas::Type::kNamed_Type;
+ gammas->fRedData.fNamed = kSRGB_SkGammaNamed;
+ gammas->fGreenData.fNamed = k2Dot2Curve_SkGammaNamed;
+ gammas->fBlueData.fNamed = kLinear_SkGammaNamed;
test_identity_xform(r, gammas, true);
- test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas);
- test_identity_xform_A2B(r, kSRGB_SkGammaNamed, nullptr);
- test_identity_xform_A2B(r, k2Dot2Curve_SkGammaNamed, nullptr);
- test_identity_xform_A2B(r, kLinear_SkGammaNamed, nullptr);
+ test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, true);
+ test_identity_xform_A2B(r, kSRGB_SkGammaNamed, nullptr, true);
+ test_identity_xform_A2B(r, k2Dot2Curve_SkGammaNamed, nullptr, true);
+ test_identity_xform_A2B(r, kLinear_SkGammaNamed, nullptr, true);
}
DEF_TEST(ColorSpaceXform_NonMatchingGamma, r) {
constexpr size_t tableSize = 10;
void* memory = sk_malloc_throw(sizeof(SkGammas) + sizeof(float) * tableSize +
sizeof(SkColorSpaceTransferFn));
- sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas(kChannels));
+ sk_sp<SkGammas> gammas = sk_sp<SkGammas>(new (memory) SkGammas());
float* table = SkTAddOffset<float>(memory, sizeof(SkGammas));
table[0] = 0.00f;
@@ -245,18 +239,18 @@ DEF_TEST(ColorSpaceXform_NonMatchingGamma, r) {
params->fF = 0.0f;
params->fG = 2.4f;
- gammas->fType[0] = SkGammas::Type::kValue_Type;
- gammas->fData[0].fValue = 1.2f;
+ gammas->fRedType = SkGammas::Type::kValue_Type;
+ gammas->fRedData.fValue = 1.2f;
- gammas->fType[1] = SkGammas::Type::kTable_Type;
- gammas->fData[1].fTable.fSize = tableSize;
- gammas->fData[1].fTable.fOffset = 0;
+ gammas->fGreenType = SkGammas::Type::kTable_Type;
+ gammas->fGreenData.fTable.fSize = tableSize;
+ gammas->fGreenData.fTable.fOffset = 0;
- gammas->fType[2] = SkGammas::Type::kParam_Type;
- gammas->fData[2].fParamOffset = sizeof(float) * tableSize;
+ gammas->fBlueType = SkGammas::Type::kParam_Type;
+ gammas->fBlueData.fParamOffset = sizeof(float) * tableSize;
test_identity_xform(r, gammas, true);
- test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas);
+ test_identity_xform_A2B(r, kNonStandard_SkGammaNamed, gammas, true);
}
DEF_TEST(ColorSpaceXform_A2BCLUT, r) {
@@ -264,7 +258,7 @@ DEF_TEST(ColorSpaceXform_A2BCLUT, r) {
constexpr int gp = 4; // # grid points
constexpr int numEntries = gp*gp*gp*3;
- const uint8_t gridPoints[3] = {gp, gp, gp};
+ uint8_t gridPoints[3] = {gp, gp, gp};
void* memory = sk_malloc_throw(sizeof(SkColorLookUpTable) + sizeof(float) * numEntries);
sk_sp<SkColorLookUpTable> colorLUT(new (memory) SkColorLookUpTable(inputChannels, gridPoints));
// make a CLUT that rotates R, G, and B ie R->G, G->B, B->R
@@ -302,7 +296,6 @@ DEF_TEST(ColorSpaceXform_A2BCLUT, r) {
std::vector<SkColorSpace_A2B::Element> srcElements;
srcElements.push_back(SkColorSpace_A2B::Element(std::move(colorLUT)));
auto srcSpace = ColorSpaceXformTest::CreateA2BSpace(SkColorSpace_A2B::PCS::kXYZ,
- SkColorSpace_Base::InputColorFormat::kRGB,
std::move(srcElements));
// dst space is entirely identity
auto dstSpace = SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma, SkMatrix44::I());