// red -> blue, green -> red, blue -> green
mat.set3x3(0, 1, 0, 0, 0, 1, 1, 0, 0);
+ float linearGamma[3] = { 1.0f, 1.0f, 1.0f };
+
const SkColor4f colors[] {
{ 1, 1, 0, 0 },
{ 1, 0, 1, 0 },
sk_sp<SkShader> shaders[] {
SkShader::MakeColorShader(c4, nullptr),
SkShader::MakeColorShader(c4, SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named)),
- SkShader::MakeColorShader(c4, SkColorSpace::NewRGB(SkColorSpace::SkGammas(1, 1, 1),
- mat)),
+ SkShader::MakeColorShader(c4, SkColorSpace::NewRGB(linearGamma, mat)),
};
canvas->save();
png_fixed_point XYZ[9];
float toXYZD50[9];
png_fixed_point gamma;
- SkColorSpace::SkGammas gammas;
+ float gammas[3];
if (png_get_cHRM_XYZ_fixed(png_ptr, info_ptr, &XYZ[0], &XYZ[1], &XYZ[2], &XYZ[3], &XYZ[4],
&XYZ[5], &XYZ[6], &XYZ[7], &XYZ[8])) {
if (PNG_INFO_gAMA == png_get_gAMA_fixed(png_ptr, info_ptr, &gamma)) {
float value = png_inverted_fixed_point_to_float(gamma);
- gammas = SkColorSpace::SkGammas(value, value, value);
+ gammas[0] = value;
+ gammas[1] = value;
+ gammas[2] = value;
} else {
// Default to sRGB (gamma = 2.2f) if the image has color space information,
// but does not specify gamma.
- gammas = SkColorSpace::SkGammas(2.2f, 2.2f, 2.2f);
+ gammas[0] = 2.2f;
+ gammas[1] = 2.2f;
+ gammas[2] = 2.2f;
}
SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
mat.set3x3ColMajorf(toXYZD50);
- return SkColorSpace::NewRGB(std::move(gammas), mat);
+ return SkColorSpace::NewRGB(gammas, mat);
}
// Last, check for gamma.
// Set the gammas.
float value = png_inverted_fixed_point_to_float(gamma);
- gammas = SkColorSpace::SkGammas(value, value, value);
+ gammas[0] = value;
+ gammas[1] = value;
+ gammas[2] = value;
- return SkColorSpace::NewRGB(std::move(gammas), SkMatrix44::I());
+ return SkColorSpace::NewRGB(gammas, SkMatrix44::I());
}
#endif // LIBPNG >= 1.6
#include "SkAtomics.h"
#include "SkColorSpace.h"
+#include "SkColorSpacePriv.h"
#include "SkOnce.h"
static bool color_space_almost_equal(float a, float b) {
return SkTAbs(a - b) < 0.01f;
}
-void SkFloat3::dump() const {
- SkDebugf("[%7.4f %7.4f %7.4f]\n", fVec[0], fVec[1], fVec[2]);
-}
-
//////////////////////////////////////////////////////////////////////////////////////////////////
static int32_t gUniqueColorSpaceID;
-SkColorSpace::SkColorSpace(SkGammas gammas, const SkMatrix44& toXYZD50, Named named)
- : fGammas(std::move(gammas))
+SkColorSpace::SkColorSpace(sk_sp<SkGammas> gammas, const SkMatrix44& toXYZD50, Named named)
+ : fGammas(gammas)
, fToXYZD50(toXYZD50)
, fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
, fNamed(named)
{}
-SkColorSpace::SkColorSpace(SkColorLookUpTable colorLUT, SkGammas gammas,
+SkColorSpace::SkColorSpace(SkColorLookUpTable* colorLUT, sk_sp<SkGammas> gammas,
const SkMatrix44& toXYZD50)
- : fColorLUT(std::move(colorLUT))
- , fGammas(std::move(gammas))
+ : fColorLUT(colorLUT)
+ , fGammas(gammas)
, fToXYZD50(toXYZD50)
, fUniqueID(sk_atomic_inc(&gUniqueColorSpaceID))
, fNamed(kUnknown_Named)
color_space_almost_equal(toXYZD50.getFloat(3, 3), 1.0f);
}
-sk_sp<SkColorSpace> SkColorSpace::NewRGB(SkGammas gammas, const SkMatrix44& toXYZD50) {
+static SkOnce gStandardGammasOnce;
+static SkGammas* gStandardGammas;
+
+sk_sp<SkColorSpace> SkColorSpace::NewRGB(float gammaVals[3], const SkMatrix44& toXYZD50) {
+ sk_sp<SkGammas> gammas = nullptr;
+
// Check if we really have sRGB or Adobe RGB
- if (color_space_almost_equal(2.2f, gammas.fRed.fValue) &&
- color_space_almost_equal(2.2f, gammas.fGreen.fValue) &&
- color_space_almost_equal(2.2f, gammas.fBlue.fValue))
+ if (color_space_almost_equal(2.2f, gammaVals[0]) &&
+ color_space_almost_equal(2.2f, gammaVals[1]) &&
+ color_space_almost_equal(2.2f, gammaVals[2]))
{
+ gStandardGammasOnce([] {
+ gStandardGammas = new SkGammas(2.2f, 2.2f, 2.2f);
+ });
+ gammas = sk_ref_sp(gStandardGammas);
+
if (xyz_almost_equal(toXYZD50, gSRGB_toXYZD50)) {
return SkColorSpace::NewNamed(kSRGB_Named);
} else if (xyz_almost_equal(toXYZD50, gAdobeRGB_toXYZD50)) {
}
}
- return sk_sp<SkColorSpace>(new SkColorSpace(std::move(gammas), toXYZD50, kUnknown_Named));
+ if (!gammas) {
+ gammas = sk_ref_sp(new SkGammas(gammaVals[0], gammaVals[1], gammaVals[2]));
+ }
+ return sk_sp<SkColorSpace>(new SkColorSpace(gammas, toXYZD50, kUnknown_Named));
}
sk_sp<SkColorSpace> SkColorSpace::NewNamed(Named named) {
switch (named) {
case kSRGB_Named: {
+ gStandardGammasOnce([] {
+ gStandardGammas = new SkGammas(2.2f, 2.2f, 2.2f);
+ });
+
sRGBOnce([] {
SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor);
srgbToxyzD50.set3x3ColMajorf(gSRGB_toXYZD50);
- sRGB = new SkColorSpace(SkGammas(2.2f, 2.2f, 2.2f), srgbToxyzD50, kSRGB_Named);
+ sRGB = new SkColorSpace(sk_ref_sp(gStandardGammas), srgbToxyzD50, kSRGB_Named);
});
return sk_ref_sp(sRGB);
}
case kAdobeRGB_Named: {
+ gStandardGammasOnce([] {
+ gStandardGammas = new SkGammas(2.2f, 2.2f, 2.2f);
+ });
+
adobeRGBOnce([] {
SkMatrix44 adobergbToxyzD50(SkMatrix44::kUninitialized_Constructor);
adobergbToxyzD50.set3x3ColMajorf(gAdobeRGB_toXYZD50);
- adobeRGB = new SkColorSpace(SkGammas(2.2f, 2.2f, 2.2f), adobergbToxyzD50,
+ adobeRGB = new SkColorSpace(sk_ref_sp(gStandardGammas), adobergbToxyzD50,
kAdobeRGB_Named);
});
return sk_ref_sp(adobeRGB);
return true;
}
-bool SkColorSpace::LoadA2B0(SkColorLookUpTable* colorLUT, SkGammas* gammas, SkMatrix44* toXYZ,
+bool SkColorSpace::LoadA2B0(SkColorLookUpTable* colorLUT, sk_sp<SkGammas> gammas, SkMatrix44* toXYZ,
const uint8_t* src, size_t len) {
if (len < 32) {
SkColorSpacePrintf("A to B tag is too small (%d bytes).", len);
// It is not uncommon to see missing or empty gamma tags. This indicates
// that we should use unit gamma.
- SkGammas gammas;
+ sk_sp<SkGammas> gammas(new SkGammas());
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 (!r || !SkColorSpace::LoadGammas(&gammas.fRed, 1,
+ if (!r || !SkColorSpace::LoadGammas(&gammas->fRed, 1,
r->addr((const uint8_t*) base), r->fLength)) {
SkColorSpacePrintf("Failed to read R gamma tag.\n");
}
- if (!g || !SkColorSpace::LoadGammas(&gammas.fGreen, 1,
+ if (!g || !SkColorSpace::LoadGammas(&gammas->fGreen, 1,
g->addr((const uint8_t*) base), g->fLength)) {
SkColorSpacePrintf("Failed to read G gamma tag.\n");
}
- if (!b || !SkColorSpace::LoadGammas(&gammas.fBlue, 1,
+ if (!b || !SkColorSpace::LoadGammas(&gammas->fBlue, 1,
b->addr((const uint8_t*) base), b->fLength)) {
SkColorSpacePrintf("Failed to read B gamma tag.\n");
}
SkMatrix44 mat(SkMatrix44::kUninitialized_Constructor);
mat.set3x3ColMajorf(toXYZ);
- return SkColorSpace::NewRGB(std::move(gammas), mat);
+ if (gammas->isValues()) {
+ // When we have values, take advantage of the NewFromRGB initializer.
+ // This allows us to check for canonical sRGB and Adobe RGB.
+ float gammaVals[3];
+ gammaVals[0] = gammas->fRed.fValue;
+ gammaVals[1] = gammas->fGreen.fValue;
+ gammaVals[2] = gammas->fBlue.fValue;
+ return SkColorSpace::NewRGB(gammaVals, mat);
+ } else {
+ return sk_sp<SkColorSpace>(new SkColorSpace(gammas, mat, kUnknown_Named));
+ }
}
// Recognize color profile specified by A2B0 tag.
const ICCTag* a2b0 = ICCTag::Find(tags.get(), tagCount, kTAG_A2B0);
if (a2b0) {
- SkColorLookUpTable colorLUT;
- SkGammas gammas;
+ SkAutoTDelete<SkColorLookUpTable> colorLUT(new SkColorLookUpTable());
+ sk_sp<SkGammas> gammas(new SkGammas());
SkMatrix44 toXYZ(SkMatrix44::kUninitialized_Constructor);
- if (!SkColorSpace::LoadA2B0(&colorLUT, &gammas, &toXYZ,
+ if (!SkColorSpace::LoadA2B0(colorLUT, gammas, &toXYZ,
a2b0->addr((const uint8_t*) base), a2b0->fLength)) {
return_null("Failed to parse A2B0 tag");
}
- // If there is no colorLUT, use NewRGB. This allows us to check if the
- // profile is sRGB.
- if (!colorLUT.fTable) {
- return SkColorSpace::NewRGB(std::move(gammas), toXYZ);
+ if (colorLUT->fTable) {
+ return sk_sp<SkColorSpace>(new SkColorSpace(colorLUT.release(), gammas, toXYZ));
+ } else if (gammas->isValues()) {
+ // When we have values, take advantage of the NewFromRGB initializer.
+ // This allows us to check for canonical sRGB and Adobe RGB.
+ float gammaVals[3];
+ gammaVals[0] = gammas->fRed.fValue;
+ gammaVals[1] = gammas->fGreen.fValue;
+ gammaVals[2] = gammas->fBlue.fValue;
+ return SkColorSpace::NewRGB(gammaVals, toXYZ);
+ } else {
+ return sk_sp<SkColorSpace>(new SkColorSpace(gammas, toXYZ, kUnknown_Named));
}
-
- return sk_sp<SkColorSpace>(new SkColorSpace(std::move(colorLUT), std::move(gammas),
- toXYZ));
}
}
// http://www.poynton.com/notes/colour_and_gamma/ColorFAQ.html
//
-#include "SkRefCnt.h"
#include "SkMatrix44.h"
+#include "SkRefCnt.h"
+#include "SkTemplates.h"
-struct SkFloat3 {
- float fVec[3];
-
- void dump() const;
-};
+struct SkColorLookUpTable;
+struct SkGammaCurve;
+struct SkGammas;
class SkColorSpace : public SkRefCnt {
-private:
- struct SkGammaCurve {
- bool isValue() const {
- bool result = (0.0f != fValue);
- SkASSERT(!result || (0 == fTableSize));
- return result;
- }
-
- bool isTable() const {
- bool result = (0 != fTableSize);
- SkASSERT(!result || (0.0f == fValue));
- SkASSERT(!result || fTable);
- return result;
- }
-
- bool isParametric() const { return false; }
-
- // We have three different ways to represent gamma.
- // (1) A single value:
- float fValue;
-
- // (2) A lookup table:
- uint32_t fTableSize;
- std::unique_ptr<float[]> fTable;
-
- // (3) Parameters for a curve:
- // FIXME (msarett): Handle parametric curves.
-
- SkGammaCurve() {
- memset(this, 0, sizeof(struct SkGammaCurve));
- }
-
- SkGammaCurve(float value)
- : fValue(value)
- , fTableSize(0)
- , fTable(nullptr)
- {}
- };
-
- struct SkColorLookUpTable {
- static const uint8_t kMaxChannels = 16;
-
- uint8_t fInputChannels;
- uint8_t fOutputChannels;
- uint8_t fGridPoints[kMaxChannels];
- std::unique_ptr<float[]> fTable;
-
- SkColorLookUpTable() {
- memset(this, 0, sizeof(struct SkColorLookUpTable));
- }
- };
-
public:
+
enum Named {
kUnknown_Named,
kSRGB_Named,
kAdobeRGB_Named,
};
- struct SkGammas {
- public:
- SkGammas(float red, float green, float blue)
- : fRed(red)
- , fGreen(green)
- , fBlue(blue)
- {}
-
- SkGammas() {}
-
- SkDEBUGCODE(float red() const { return fRed.fValue; })
- SkDEBUGCODE(float green() const { return fGreen.fValue; })
- SkDEBUGCODE(float blue() const { return fBlue.fValue; })
-
- private:
- SkGammaCurve fRed;
- SkGammaCurve fGreen;
- SkGammaCurve fBlue;
-
- friend class SkColorSpace;
- };
-
/**
- * Return a colorspace instance, given a transform from linear_RGB to D50_XYZ
- * and the src-gamma, return a ColorSpace
+ * Given the src gamma and a transform from src gamut to D50_XYZ, return a SkColorSpace.
*/
- static sk_sp<SkColorSpace> NewRGB(SkGammas gammas, const SkMatrix44& toXYZD50);
+ static sk_sp<SkColorSpace> NewRGB(float gammas[3], const SkMatrix44& toXYZD50);
static sk_sp<SkColorSpace> NewNamed(Named);
static sk_sp<SkColorSpace> NewICC(const void*, size_t);
- const SkGammas& gammas() const { return fGammas; }
+ SkGammas* gammas() const { return fGammas.get(); }
SkMatrix44 xyz() const { return fToXYZD50; }
Named named() const { return fNamed; }
uint32_t uniqueID() const { return fUniqueID; }
uint32_t outputChannels, const uint8_t* src, size_t len);
- static bool LoadA2B0(SkColorLookUpTable* colorLUT, SkGammas* gammas, SkMatrix44* toXYZ,
+ static bool LoadA2B0(SkColorLookUpTable* colorLUT, sk_sp<SkGammas> gammas, SkMatrix44* toXYZ,
const uint8_t* src, size_t len);
- SkColorSpace(SkGammas gammas, const SkMatrix44& toXYZ, Named);
+ SkColorSpace(sk_sp<SkGammas> gammas, const SkMatrix44& toXYZ, Named);
- SkColorSpace(SkColorLookUpTable colorLUT, SkGammas gammas,
- const SkMatrix44& toXYZ);
+ SkColorSpace(SkColorLookUpTable* colorLUT, sk_sp<SkGammas> gammas, const SkMatrix44& toXYZ);
- const SkColorLookUpTable fColorLUT;
- const SkGammas fGammas;
- const SkMatrix44 fToXYZD50;
+ SkAutoTDelete<SkColorLookUpTable> fColorLUT;
+ sk_sp<SkGammas> fGammas;
+ const SkMatrix44 fToXYZD50;
- const uint32_t fUniqueID;
- const Named fNamed;
+ const uint32_t fUniqueID;
+ const Named fNamed;
};
#endif
--- /dev/null
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkColorSpacePriv_DEFINED
+#define SkColorSpacePriv_DEFINED
+
+struct SkGammaCurve {
+ bool isValue() const {
+ bool result = (0.0f != fValue);
+ SkASSERT(!result || (0 == fTableSize));
+ return result;
+ }
+
+ bool isTable() const {
+ bool result = (0 != fTableSize);
+ SkASSERT(!result || (0.0f == fValue));
+ SkASSERT(!result || fTable);
+ return result;
+ }
+
+ bool isParametric() const { return false; }
+
+ // We have three different ways to represent gamma.
+ // (1) A single value:
+ float fValue;
+
+ // (2) A lookup table:
+ uint32_t fTableSize;
+ std::unique_ptr<float[]> fTable;
+
+ // (3) Parameters for a curve:
+ // FIXME (msarett): Handle parametric curves.
+
+ SkGammaCurve() {
+ memset(this, 0, sizeof(struct SkGammaCurve));
+ }
+
+ SkGammaCurve(float value)
+ : fValue(value)
+ , fTableSize(0)
+ , fTable(nullptr)
+ {}
+};
+
+struct SkGammas : public SkRefCnt {
+public:
+ bool isValues() const {
+ return fRed.isValue() && fGreen.isValue() && fBlue.isValue();
+ }
+
+ SkGammaCurve fRed;
+ SkGammaCurve fGreen;
+ SkGammaCurve fBlue;
+
+ SkGammas(float red, float green, float blue)
+ : fRed(red)
+ , fGreen(green)
+ , fBlue(blue)
+ {}
+
+ SkGammas() {}
+
+ friend class SkColorSpace;
+};
+
+struct SkColorLookUpTable {
+ static const uint8_t kMaxChannels = 16;
+
+ uint8_t fInputChannels;
+ uint8_t fOutputChannels;
+ uint8_t fGridPoints[kMaxChannels];
+ std::unique_ptr<float[]> fTable;
+
+ SkColorLookUpTable() {
+ memset(this, 0, sizeof(struct SkColorLookUpTable));
+ }
+};
+
+#endif
#include "Resources.h"
#include "SkCodec.h"
#include "SkColorSpace.h"
+#include "SkColorSpacePriv.h"
#include "Test.h"
#include "png.h"
static void test_space(skiatest::Reporter* r, SkColorSpace* space,
const float red[], const float green[], const float blue[],
const float expectedGammas[]) {
-#ifdef SK_DEBUG
- const SkColorSpace::SkGammas& gammas = space->gammas();
- REPORTER_ASSERT(r, almost_equal(expectedGammas[0], gammas.red()));
- REPORTER_ASSERT(r, almost_equal(expectedGammas[1], gammas.green()));
- REPORTER_ASSERT(r, almost_equal(expectedGammas[2], gammas.blue()));
-#endif
+
+ SkGammas* gammas = space->gammas();
+ REPORTER_ASSERT(r, almost_equal(expectedGammas[0], gammas->fRed.fValue));
+ REPORTER_ASSERT(r, almost_equal(expectedGammas[1], gammas->fGreen.fValue));
+ REPORTER_ASSERT(r, almost_equal(expectedGammas[2], gammas->fBlue.fValue));
+
SkMatrix44 mat = space->xyz();
const float src[] = {
// Create an sRGB color space by name
sk_sp<SkColorSpace> namedColorSpace = SkColorSpace::NewNamed(SkColorSpace::kSRGB_Named);
+
// Create an sRGB color space by value
SkMatrix44 srgbToxyzD50(SkMatrix44::kUninitialized_Constructor);
+ float sRGBGammas[3] = { 2.2f, 2.2f, 2.2f };
srgbToxyzD50.set3x3ColMajorf(g_sRGB_XYZ);
- sk_sp<SkColorSpace> rgbColorSpace = SkColorSpace::NewRGB(
- SkColorSpace::SkGammas(2.2f, 2.2f, 2.2f), srgbToxyzD50);
+ sk_sp<SkColorSpace> rgbColorSpace = SkColorSpace::NewRGB(sRGBGammas, srgbToxyzD50);
REPORTER_ASSERT(r, namedColorSpace == namedColorSpace);
// Change a single value from the sRGB matrix
srgbToxyzD50.set(2, 2, 0.5f);
- sk_sp<SkColorSpace> strangeColorSpace = SkColorSpace::NewRGB(
- SkColorSpace::SkGammas(2.2f, 2.2f, 2.2f), srgbToxyzD50);
+ sk_sp<SkColorSpace> strangeColorSpace = SkColorSpace::NewRGB(sRGBGammas, srgbToxyzD50);
REPORTER_ASSERT(r, strangeColorSpace != namedColorSpace);
}