Optimized gamma table inversion.
Brought calculation into a central place so the loop did not have to recalculate everything before the previous entry to find the inverse index. O(n) vs O(n^2). Assumes an increasing (or at least non-decreasing) table gamma just as the previous code did. BUG=skia: Change-Id: I7ea200c06511b3d74745fe4a6e3dde706bbee02f Reviewed-on: https://skia-review.googlesource.com/5402 Commit-Queue: Robert Aftias <raftias@google.com> Reviewed-by: Matt Sarett <msarett@google.com>
This commit is contained in:
parent
35455f931c
commit
197e311ac9
@ -112,15 +112,6 @@ static void build_table_linear_from_gamma(float* outTable, const float* inTable,
|
||||
}
|
||||
}
|
||||
|
||||
static inline float clamp_0_1(float v) {
|
||||
if (v >= 1.0f) {
|
||||
return 1.0f;
|
||||
} else if (v >= 0.0f) {
|
||||
return v;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
static void build_table_linear_from_gamma(float* outTable, float g, float a, float b, float c,
|
||||
float d, float e, float f) {
|
||||
@ -137,20 +128,6 @@ static void build_table_linear_from_gamma(float* outTable, float g, float a, flo
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Expand range from 0-1 to 0-255, then convert.
|
||||
static uint8_t clamp_normalized_float_to_byte(float v) {
|
||||
// The ordering of the logic is a little strange here in order
|
||||
// to make sure we convert NaNs to 0.
|
||||
v = v * 255.0f;
|
||||
if (v >= 254.5f) {
|
||||
return 255;
|
||||
} else if (v >= 0.5f) {
|
||||
return (uint8_t) (v + 0.5f);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static const int kDstGammaTableSize = SkColorSpaceXform_Base::kDstGammaTableSize;
|
||||
|
||||
static void build_table_linear_to_gamma(uint8_t* outTable, float exponent) {
|
||||
@ -164,11 +141,7 @@ static void build_table_linear_to_gamma(uint8_t* outTable, float exponent) {
|
||||
|
||||
static void build_table_linear_to_gamma(uint8_t* outTable, const float* inTable,
|
||||
int inTableSize) {
|
||||
for (int i = 0; i < kDstGammaTableSize; i++) {
|
||||
float x = ((float) i) * (1.0f / ((float) (kDstGammaTableSize - 1)));
|
||||
float y = inverse_interp_lut(x, inTable, inTableSize);
|
||||
outTable[i] = clamp_normalized_float_to_byte(y);
|
||||
}
|
||||
invert_table_gamma(nullptr, outTable, kDstGammaTableSize, inTable, inTableSize);
|
||||
}
|
||||
|
||||
static float inverse_parametric(float x, float g, float a, float b, float c, float d, float e,
|
||||
|
@ -23,32 +23,62 @@ static inline float interp_lut(float input, const float* table, int tableSize) {
|
||||
table[(int) sk_float_ceil2int(index)] * diff;
|
||||
}
|
||||
|
||||
// Inverse table lookup. Ex: what index corresponds to the input value? This will
|
||||
// have strange results when the table is non-increasing. But any sane gamma
|
||||
// function will be increasing.
|
||||
static inline float inverse_interp_lut(float input, const float* table, int tableSize) {
|
||||
if (input <= table[0]) {
|
||||
return table[0];
|
||||
} else if (input >= table[tableSize - 1]) {
|
||||
return 1.0f;
|
||||
// Expand range from 0-1 to 0-255, then convert.
|
||||
static inline uint8_t clamp_normalized_float_to_byte(float v) {
|
||||
// The ordering of the logic is a little strange here in order
|
||||
// to make sure we convert NaNs to 0.
|
||||
v = v * 255.0f;
|
||||
if (v >= 254.5f) {
|
||||
return 255;
|
||||
} else if (v >= 0.5f) {
|
||||
return (uint8_t) (v + 0.5f);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (int i = 1; i < tableSize; i++) {
|
||||
if (table[i] >= input) {
|
||||
// We are guaranteed that input is greater than table[i - 1].
|
||||
float diff = input - table[i - 1];
|
||||
float distance = table[i] - table[i - 1];
|
||||
float index = (i - 1) + diff / distance;
|
||||
return index / (tableSize - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Should be unreachable, since we'll return before the loop if input is
|
||||
// larger than the last entry.
|
||||
SkASSERT(false);
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
#undef AI
|
||||
static inline float clamp_0_1(float v) {
|
||||
if (v >= 1.0f) {
|
||||
return 1.0f;
|
||||
} else if (v >= 0.0f) {
|
||||
return v;
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invert table lookup. Ex: what indices corresponds to the input values?
|
||||
* This will have strange results when the table is not increasing.
|
||||
* But any sane gamma function will be increasing.
|
||||
* @param outTableFloat Destination table for float (0-1) results. Can be nullptr if not wanted.
|
||||
* @param outTableByte Destination table for byte (0-255) results. Can be nullptr if not wanted.
|
||||
* @param outTableSize Number of elements in |outTableFloat| or |outTableBytes|
|
||||
* @param inTable The source table to invert
|
||||
* @param inTableSize The number of elements in |inTable|
|
||||
*/
|
||||
static inline void invert_table_gamma(float* outTableFloat, uint8_t* outTableByte,
|
||||
int outTableSize, const float* inTable, int inTableSize) {
|
||||
// should never have a gamma table this small anyway, 0/1 are either not allowed
|
||||
// or imply a non-table gamma such as linear/exponential
|
||||
SkASSERT(inTableSize >= 2);
|
||||
int inIndex = 1;
|
||||
for (int outIndex = 0; outIndex < outTableSize; ++outIndex) {
|
||||
const float input = outIndex / (outTableSize - 1.0f);
|
||||
while (inIndex < inTableSize - 1 && inTable[inIndex] < input) {
|
||||
++inIndex;
|
||||
}
|
||||
const float diff = input - inTable[inIndex - 1];
|
||||
const float distance = inTable[inIndex] - inTable[inIndex - 1];
|
||||
const float normalizedIndex = (inIndex - 1) + diff / distance;
|
||||
const float index = normalizedIndex / (inTableSize - 1);
|
||||
if (outTableByte) {
|
||||
outTableByte[outIndex] = clamp_normalized_float_to_byte(index);
|
||||
}
|
||||
if (outTableFloat) {
|
||||
outTableFloat[outIndex] = clamp_0_1(index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -115,7 +115,7 @@ static inline SkColorSpaceTransferFn invert_parametric(const SkColorSpaceTransfe
|
||||
// which can be re-written as: x = [1/e]y + [-f/e]
|
||||
//
|
||||
// and now both can be expressed in terms of the same parametric form as the
|
||||
// original - parameters are enclosed in square barckets.
|
||||
// original - parameters are enclosed in square brackets.
|
||||
|
||||
// find inverse for linear segment (if possible)
|
||||
float e, f;
|
||||
@ -147,17 +147,6 @@ static inline SkColorSpaceTransferFn invert_parametric(const SkColorSpaceTransfe
|
||||
return {g, a, b, c, d, e, f};
|
||||
}
|
||||
|
||||
static std::vector<float> build_inverse_table(const float* inTable, int inTableSize) {
|
||||
static constexpr int kInvTableSize = 256;
|
||||
std::vector<float> outTable(kInvTableSize);
|
||||
for (int i = 0; i < kInvTableSize; ++i) {
|
||||
const float x = ((float) i) * (1.f / ((float) (kInvTableSize - 1)));
|
||||
const float y = inverse_interp_lut(x, inTable, inTableSize);
|
||||
outTable[i] = y;
|
||||
}
|
||||
return outTable;
|
||||
}
|
||||
|
||||
SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
|
||||
SkColorSpace_XYZ* dstSpace)
|
||||
: fLinearDstGamma(kLinear_SkGammaNamed == dstSpace->gammaNamed()) {
|
||||
@ -272,8 +261,10 @@ SkColorSpaceXform_A2B::SkColorSpaceXform_A2B(SkColorSpace_A2B* srcSpace,
|
||||
for (int channel = 0; channel < 3; ++channel) {
|
||||
const SkGammas& gammas = *dstSpace->gammas();
|
||||
if (SkGammas::Type::kTable_Type == gammas.type(channel)) {
|
||||
std::vector<float> storage = build_inverse_table(gammas.table(channel),
|
||||
gammas.data(channel).fTable.fSize);
|
||||
static constexpr int kInvTableSize = 256;
|
||||
std::vector<float> storage(kInvTableSize);
|
||||
invert_table_gamma(storage.data(), nullptr, storage.size(), gammas.table(channel),
|
||||
gammas.data(channel).fTable.fSize);
|
||||
SkTableTransferFn table = {
|
||||
storage.data(),
|
||||
(int) storage.size(),
|
||||
|
@ -85,8 +85,13 @@ public:
|
||||
, fGammas(std::move(gammas))
|
||||
, fMatrix(SkMatrix44::kUninitialized_Constructor)
|
||||
, fInputChannels(fGammas->channels())
|
||||
, fOutputChannels(fGammas->channels())
|
||||
{}
|
||||
, fOutputChannels(fGammas->channels()) {
|
||||
for (int i = 0; i < fGammas->channels(); ++i) {
|
||||
if (SkGammas::Type::kTable_Type == fGammas->type(i)) {
|
||||
SkASSERT(fGammas->data(i).fTable.fSize >= 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
explicit Element(sk_sp<SkColorLookUpTable> colorLUT)
|
||||
: fType(Type::kCLUT)
|
||||
|
@ -33,6 +33,13 @@ SkColorSpace_XYZ::SkColorSpace_XYZ(SkGammaNamed gammaNamed, sk_sp<SkGammas> gamm
|
||||
, fToXYZD50Hash(SkGoodHash()(toXYZD50))
|
||||
, fFromXYZD50(SkMatrix44::kUninitialized_Constructor) {
|
||||
SkASSERT(!fGammas || 3 == fGammas->channels());
|
||||
if (fGammas) {
|
||||
for (int i = 0; i < fGammas->channels(); ++i) {
|
||||
if (SkGammas::Type::kTable_Type == fGammas->type(i)) {
|
||||
SkASSERT(fGammas->data(i).fTable.fSize >= 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const SkMatrix44* SkColorSpace_XYZ::fromXYZD50() const {
|
||||
|
Loading…
Reference in New Issue
Block a user