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:
raftias 2016-12-01 15:31:29 -05:00 committed by Skia Commit-Bot
parent 35455f931c
commit 197e311ac9
5 changed files with 74 additions and 68 deletions

View File

@ -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,

View File

@ -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

View File

@ -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(),

View File

@ -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)

View File

@ -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 {