/* * 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 SkGammas_DEFINED #define SkGammas_DEFINED #include "SkColorSpace.h" #include "SkData.h" #include "SkTemplates.h" struct SkGammas : SkRefCnt { // There are four possible representations for gamma curves. kNone_Type is used // as a placeholder until the struct is initialized. It is not a valid value. enum class Type { kNone_Type, kNamed_Type, kValue_Type, kTable_Type, kParam_Type, }; // Contains information for a gamma table. struct Table { size_t fOffset; int fSize; const float* table(const SkGammas* base) const { return SkTAddOffset(base, sizeof(SkGammas) + fOffset); } }; // Contains the actual gamma curve information. Should be interpreted // based on the type of the gamma curve. union Data { Data() : fTable{0, 0} {} SkGammaNamed fNamed; float fValue; Table fTable; size_t fParamOffset; const SkColorSpaceTransferFn& params(const SkGammas* base) const { return *SkTAddOffset(base, sizeof(SkGammas) + fParamOffset); } }; bool allChannelsSame() const { // All channels are the same type? Type type = this->type(0); for (int i = 1; i < this->channels(); i++) { if (type != this->type(i)) { return false; } } // All data the same? auto& first = this->data(0); for (int i = 1; i < this->channels(); i++) { auto& data = this->data(i); switch (type) { case Type:: kNone_Type: break; case Type::kNamed_Type: if (first.fNamed != data.fNamed) { return false; } break; case Type::kValue_Type: if (first.fValue != data.fValue) { return false; } break; case Type::kTable_Type: if (first.fTable.fOffset != data.fTable.fOffset) { return false; } if (first.fTable.fSize != data.fTable.fSize ) { return false; } break; case Type::kParam_Type: if (0 != memcmp(&first.params(this), &data.params(this), sizeof(SkColorSpaceTransferFn))) { return false; } break; } } return true; } bool isNamed (int i) const { return Type::kNamed_Type == this->type(i); } bool isValue (int i) const { return Type::kValue_Type == this->type(i); } bool isTable (int i) const { return Type::kTable_Type == this->type(i); } bool isParametric(int i) const { return Type::kParam_Type == this->type(i); } const Data& data(int i) const { SkASSERT(i >= 0 && i < fChannels); return fData[i]; } const float* table(int i) const { SkASSERT(this->isTable(i)); return this->data(i).fTable.table(this); } int tableSize(int i) const { SkASSERT(this->isTable(i)); return this->data(i).fTable.fSize; } const SkColorSpaceTransferFn& params(int i) const { SkASSERT(this->isParametric(i)); return this->data(i).params(this); } Type type(int i) const { SkASSERT(i >= 0 && i < fChannels); return fType[i]; } int channels() const { return fChannels; } SkGammas(int channels) : fChannels(channels) { SkASSERT(channels <= (int)SK_ARRAY_COUNT(fType)); for (Type& t : fType) { t = Type::kNone_Type; } } // These fields should only be modified when initializing the struct. int fChannels; Data fData[4]; Type fType[4]; // 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 // also call sk_malloc_throw so that memory can be unconditionally released // using sk_free in an overloaded delete. Overloading regular new means we // must also overload placement new. void* operator new(size_t size) { return sk_malloc_throw(size); } void* operator new(size_t, void* p) { return p; } void operator delete(void* p) { sk_free(p); } }; #endif