Allocate avifReformatState tables dynamically
Instead of a fixed-length array that may be larger than necessary and
uses 1<<12 * 2 = 8kB of stack memory which may be limited on some
environments.
diff --git a/include/avif/internal.h b/include/avif/internal.h
index 31bf425..db163ca 100644
--- a/include/avif/internal.h
+++ b/include/avif/internal.h
@@ -135,10 +135,6 @@
avifPixelFormatInfo formatInfo;
- // LUTs for going from YUV limited/full unorm -> full range RGB FP32
- float unormFloatTableY[1 << 12];
- float unormFloatTableUV[1 << 12];
-
avifReformatMode mode;
// Used by avifImageYUVToRGB() only. avifImageRGBToYUV() uses a local variable (alphaMode) instead.
avifAlphaMultiplyMode toRGBAlphaMode;
diff --git a/src/reformat.c b/src/reformat.c
index 2200e8c..5ec6ee8 100644
--- a/src/reformat.c
+++ b/src/reformat.c
@@ -135,20 +135,6 @@
state->rangeY = (float)((state->yuvRange == AVIF_RANGE_LIMITED) ? (219 << (state->yuvDepth - 8)) : state->yuvMaxChannel);
state->rangeUV = (float)((state->yuvRange == AVIF_RANGE_LIMITED) ? (224 << (state->yuvDepth - 8)) : state->yuvMaxChannel);
- uint32_t cpCount = 1 << image->depth;
- if (state->mode == AVIF_REFORMAT_MODE_IDENTITY) {
- for (uint32_t cp = 0; cp < cpCount; ++cp) {
- state->unormFloatTableY[cp] = ((float)cp - state->biasY) / state->rangeY;
- state->unormFloatTableUV[cp] = ((float)cp - state->biasY) / state->rangeY;
- }
- } else {
- for (uint32_t cp = 0; cp < cpCount; ++cp) {
- // Review this when implementing YCgCo limited range support.
- state->unormFloatTableY[cp] = ((float)cp - state->biasY) / state->rangeY;
- state->unormFloatTableUV[cp] = ((float)cp - state->biasUV) / state->rangeUV;
- }
- }
-
state->toRGBAlphaMode = AVIF_ALPHA_MULTIPLY_MODE_NO_OP;
if (image->alphaPlane) {
if (!avifRGBFormatHasAlpha(rgb->format) || rgb->ignoreAlpha) {
@@ -461,6 +447,52 @@
return AVIF_RESULT_OK;
}
+// Allocates and fills look-up tables for going from YUV limited/full unorm -> full range RGB FP32.
+static avifBool avifCreateYUVToRGBLookUpTables(float ** unormFloatTableY, float ** unormFloatTableUV, int depth, const avifReformatState * state)
+{
+ const size_t cpCount = (size_t)1 << depth;
+
+ assert(unormFloatTableY);
+ *unormFloatTableY = avifAlloc(cpCount * sizeof(**unormFloatTableY));
+ AVIF_CHECK(*unormFloatTableY);
+ for (uint32_t cp = 0; cp < cpCount; ++cp) {
+ (*unormFloatTableY)[cp] = ((float)cp - state->biasY) / state->rangeY;
+ }
+
+ if (unormFloatTableUV) {
+ if (state->mode == AVIF_REFORMAT_MODE_IDENTITY) {
+ // Just reuse the luma table since the chroma values are the same.
+ *unormFloatTableUV = *unormFloatTableY;
+ } else {
+ *unormFloatTableUV = avifAlloc(cpCount * sizeof(**unormFloatTableUV));
+ if (!*unormFloatTableUV) {
+ avifFree(*unormFloatTableY);
+ *unormFloatTableY = NULL;
+ return AVIF_FALSE;
+ }
+ for (uint32_t cp = 0; cp < cpCount; ++cp) {
+ // Review this when implementing YCgCo limited range support.
+ (*unormFloatTableUV)[cp] = ((float)cp - state->biasUV) / state->rangeUV;
+ }
+ }
+ }
+ return AVIF_TRUE;
+}
+
+// Frees look-up tables allocated with avifCreateYUVToRGBLookUpTables().
+static void avifFreeYUVToRGBLookUpTables(float ** unormFloatTableY, float ** unormFloatTableUV)
+{
+ if (unormFloatTableUV) {
+ if (*unormFloatTableUV != *unormFloatTableY) {
+ avifFree(*unormFloatTableUV);
+ }
+ *unormFloatTableUV = NULL;
+ }
+
+ avifFree(*unormFloatTableY);
+ *unormFloatTableY = NULL;
+}
+
#define RGB565(R, G, B) ((uint16_t)(((B) >> 3) | (((G) >> 2) << 5) | (((R) >> 3) << 11)))
static void avifStoreRGB8Pixel(avifRGBFormat format, uint8_t R, uint8_t G, uint8_t B, uint8_t * ptrR, uint8_t * ptrG, uint8_t * ptrB)
@@ -484,8 +516,9 @@
const float kr = state->kr;
const float kg = state->kg;
const float kb = state->kb;
- const float * const unormFloatTableY = state->unormFloatTableY;
- const float * const unormFloatTableUV = state->unormFloatTableUV;
+ float * unormFloatTableY = NULL;
+ float * unormFloatTableUV = NULL;
+ AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY);
const uint32_t yuvChannelBytes = state->yuvChannelBytes;
const uint32_t rgbPixelBytes = state->rgbPixelBytes;
@@ -747,6 +780,7 @@
ptrB += rgbPixelBytes;
}
}
+ avifFreeYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV);
return AVIF_RESULT_OK;
}
@@ -756,8 +790,9 @@
const float kg = state->kg;
const float kb = state->kb;
const uint32_t rgbPixelBytes = state->rgbPixelBytes;
- const float * const unormFloatTableY = state->unormFloatTableY;
- const float * const unormFloatTableUV = state->unormFloatTableUV;
+ float * unormFloatTableY = NULL;
+ float * unormFloatTableUV = NULL;
+ AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY);
const uint16_t yuvMaxChannel = (uint16_t)state->yuvMaxChannel;
const float rgbMaxChannelF = state->rgbMaxChannelF;
@@ -799,6 +834,7 @@
ptrB += rgbPixelBytes;
}
}
+ avifFreeYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV);
return AVIF_RESULT_OK;
}
@@ -808,7 +844,8 @@
const float kg = state->kg;
const float kb = state->kb;
const uint32_t rgbPixelBytes = state->rgbPixelBytes;
- const float * const unormFloatTableY = state->unormFloatTableY;
+ float * unormFloatTableY = NULL;
+ AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, NULL, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY);
const uint16_t yuvMaxChannel = (uint16_t)state->yuvMaxChannel;
const float rgbMaxChannelF = state->rgbMaxChannelF;
@@ -843,6 +880,7 @@
ptrB += rgbPixelBytes;
}
}
+ avifFreeYUVToRGBLookUpTables(&unormFloatTableY, NULL);
return AVIF_RESULT_OK;
}
@@ -852,8 +890,9 @@
const float kg = state->kg;
const float kb = state->kb;
const uint32_t rgbPixelBytes = state->rgbPixelBytes;
- const float * const unormFloatTableY = state->unormFloatTableY;
- const float * const unormFloatTableUV = state->unormFloatTableUV;
+ float * unormFloatTableY = NULL;
+ float * unormFloatTableUV = NULL;
+ AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY);
const uint16_t yuvMaxChannel = (uint16_t)state->yuvMaxChannel;
const float rgbMaxChannelF = state->rgbMaxChannelF;
@@ -899,6 +938,7 @@
ptrB += rgbPixelBytes;
}
}
+ avifFreeYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV);
return AVIF_RESULT_OK;
}
@@ -908,7 +948,8 @@
const float kg = state->kg;
const float kb = state->kb;
const uint32_t rgbPixelBytes = state->rgbPixelBytes;
- const float * const unormFloatTableY = state->unormFloatTableY;
+ float * unormFloatTableY = NULL;
+ AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, NULL, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY);
const uint16_t yuvMaxChannel = (uint16_t)state->yuvMaxChannel;
const float rgbMaxChannelF = state->rgbMaxChannelF;
@@ -947,6 +988,7 @@
ptrB += rgbPixelBytes;
}
}
+ avifFreeYUVToRGBLookUpTables(&unormFloatTableY, NULL);
return AVIF_RESULT_OK;
}
@@ -956,8 +998,9 @@
const float kg = state->kg;
const float kb = state->kb;
const uint32_t rgbPixelBytes = state->rgbPixelBytes;
- const float * const unormFloatTableY = state->unormFloatTableY;
- const float * const unormFloatTableUV = state->unormFloatTableUV;
+ float * unormFloatTableY = NULL;
+ float * unormFloatTableUV = NULL;
+ AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY);
const float rgbMaxChannelF = state->rgbMaxChannelF;
for (uint32_t j = 0; j < image->height; ++j) {
@@ -993,6 +1036,7 @@
ptrB += rgbPixelBytes;
}
}
+ avifFreeYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV);
return AVIF_RESULT_OK;
}
@@ -1002,7 +1046,8 @@
const float kg = state->kg;
const float kb = state->kb;
const uint32_t rgbPixelBytes = state->rgbPixelBytes;
- const float * const unormFloatTableY = state->unormFloatTableY;
+ float * unormFloatTableY = NULL;
+ AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, NULL, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY);
const float rgbMaxChannelF = state->rgbMaxChannelF;
for (uint32_t j = 0; j < image->height; ++j) {
@@ -1033,6 +1078,7 @@
ptrB += rgbPixelBytes;
}
}
+ avifFreeYUVToRGBLookUpTables(&unormFloatTableY, NULL);
return AVIF_RESULT_OK;
}
@@ -1075,8 +1121,9 @@
const float kg = state->kg;
const float kb = state->kb;
const uint32_t rgbPixelBytes = state->rgbPixelBytes;
- const float * const unormFloatTableY = state->unormFloatTableY;
- const float * const unormFloatTableUV = state->unormFloatTableUV;
+ float * unormFloatTableY = NULL;
+ float * unormFloatTableUV = NULL;
+ AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY);
const float rgbMaxChannelF = state->rgbMaxChannelF;
for (uint32_t j = 0; j < image->height; ++j) {
@@ -1116,6 +1163,7 @@
ptrB += rgbPixelBytes;
}
}
+ avifFreeYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV);
return AVIF_RESULT_OK;
}
@@ -1125,7 +1173,8 @@
const float kg = state->kg;
const float kb = state->kb;
const uint32_t rgbPixelBytes = state->rgbPixelBytes;
- const float * const unormFloatTableY = state->unormFloatTableY;
+ float * unormFloatTableY = NULL;
+ AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, NULL, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY);
const float rgbMaxChannelF = state->rgbMaxChannelF;
for (uint32_t j = 0; j < image->height; ++j) {
@@ -1160,6 +1209,7 @@
ptrB += rgbPixelBytes;
}
}
+ avifFreeYUVToRGBLookUpTables(&unormFloatTableY, NULL);
return AVIF_RESULT_OK;
}