saver/gif: up to date the gif encoder.

this fixes the memory sanitizer report:

../src/savers/gif/gif.h:315:31: runtime error: index 255 out of bounds for type 'unsigned char [255]'
../src/savers/gif/gif.h:113:54: runtime error: index 255 out of bounds for type 'unsigned char [255]'

Issue: https://github.com/thorvg/thorvg/issues/1758
This commit is contained in:
Hermet Park 2023-11-06 14:50:10 +09:00
parent 875b623c95
commit acb67dad8c

View file

@ -32,6 +32,7 @@
#include <stdio.h> // for FILE* #include <stdio.h> // for FILE*
#include <string.h> // for memcpy and bzero #include <string.h> // for memcpy and bzero
#include <stdint.h> // for integer typedefs #include <stdint.h> // for integer typedefs
#include <stdbool.h> // for bool macros
// Define these macros to hook into a custom memory allocator. // Define these macros to hook into a custom memory allocator.
// TEMP_MALLOC and TEMP_FREE will only be called in stack fashion - frees in the reverse order of mallocs // TEMP_MALLOC and TEMP_FREE will only be called in stack fashion - frees in the reverse order of mallocs
@ -61,7 +62,7 @@
const int kGifTransIndex = 0; const int kGifTransIndex = 0;
struct GifPalette typedef struct
{ {
int bitDepth; int bitDepth;
@ -72,9 +73,9 @@ struct GifPalette
// k-d tree over RGB space, organized in heap fashion // k-d tree over RGB space, organized in heap fashion
// i.e. left child of node i is node i*2, right child is node i*2+1 // i.e. left child of node i is node i*2, right child is node i*2+1
// nodes 256-511 are implicitly the leaves, containing a color // nodes 256-511 are implicitly the leaves, containing a color
uint8_t treeSplitElt[255]; uint8_t treeSplitElt[256];
uint8_t treeSplit[255]; uint8_t treeSplit[256];
}; } GifPalette;
// max, min, and abs functions // max, min, and abs functions
int GifIMax(int l, int r) { return l>r?l:r; } int GifIMax(int l, int r) { return l>r?l:r; }
@ -85,7 +86,7 @@ int GifIAbs(int i) { return i<0?-i:i; }
// Takes as in/out parameters the current best color and its error - // Takes as in/out parameters the current best color and its error -
// only changes them if it finds a better color in its subtree. // only changes them if it finds a better color in its subtree.
// this is the major hotspot in the code at the moment. // this is the major hotspot in the code at the moment.
void GifGetClosestPaletteColor(GifPalette* pPal, int r, int g, int b, int& bestInd, int& bestDiff, int treeRoot = 1) void GifGetClosestPaletteColor( GifPalette* pPal, int r, int g, int b, int* bestInd, int* bestDiff, int treeRoot )
{ {
// base case, reached the bottom of the tree // base case, reached the bottom of the tree
if(treeRoot > (1<<pPal->bitDepth)-1) if(treeRoot > (1<<pPal->bitDepth)-1)
@ -99,10 +100,10 @@ void GifGetClosestPaletteColor(GifPalette* pPal, int r, int g, int b, int& bestI
int b_err = b - ((int32_t)pPal->b[ind]); int b_err = b - ((int32_t)pPal->b[ind]);
int diff = GifIAbs(r_err)+GifIAbs(g_err)+GifIAbs(b_err); int diff = GifIAbs(r_err)+GifIAbs(g_err)+GifIAbs(b_err);
if(diff < bestDiff) if(diff < *bestDiff)
{ {
bestInd = ind; *bestInd = ind;
bestDiff = diff; *bestDiff = diff;
} }
return; return;
@ -117,7 +118,7 @@ void GifGetClosestPaletteColor(GifPalette* pPal, int r, int g, int b, int& bestI
{ {
// check the left subtree // check the left subtree
GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2); GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2);
if( bestDiff > splitPos - splitComp ) if( *bestDiff > splitPos - splitComp )
{ {
// cannot prove there's not a better value in the right subtree, check that too // cannot prove there's not a better value in the right subtree, check that too
GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1); GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1);
@ -126,7 +127,7 @@ void GifGetClosestPaletteColor(GifPalette* pPal, int r, int g, int b, int& bestI
else else
{ {
GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1); GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2+1);
if( bestDiff > splitComp - splitPos ) if( *bestDiff > splitComp - splitPos )
{ {
GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2); GifGetClosestPaletteColor(pPal, r, g, b, bestInd, bestDiff, treeRoot*2);
} }
@ -391,7 +392,7 @@ void GifDitherImage( const uint8_t* lastFrame, const uint8_t* nextFrame, uint8_t
for( int ii=0; ii<numPixels*4; ++ii ) for( int ii=0; ii<numPixels*4; ++ii )
{ {
uint8_t pix = nextFrame[ii]; uint8_t pix = nextFrame[ii];
int32_t pix16 = int32_t(pix) * 256; int32_t pix16 = (int32_t)(pix) * 256;
quantPixels[ii] = pix16; quantPixels[ii] = pix16;
} }
@ -425,12 +426,12 @@ void GifDitherImage( const uint8_t* lastFrame, const uint8_t* nextFrame, uint8_t
int32_t bestInd = kGifTransIndex; int32_t bestInd = kGifTransIndex;
// Search the palete // Search the palete
GifGetClosestPaletteColor(pPal, rr, gg, bb, bestInd, bestDiff); GifGetClosestPaletteColor(pPal, rr, gg, bb, &bestInd, &bestDiff, 1);
// Write the result to the temp buffer // Write the result to the temp buffer
int32_t r_err = nextPix[0] - int32_t(pPal->r[bestInd]) * 256; int32_t r_err = nextPix[0] - (int32_t)(pPal->r[bestInd]) * 256;
int32_t g_err = nextPix[1] - int32_t(pPal->g[bestInd]) * 256; int32_t g_err = nextPix[1] - (int32_t)(pPal->g[bestInd]) * 256;
int32_t b_err = nextPix[2] - int32_t(pPal->b[bestInd]) * 256; int32_t b_err = nextPix[2] - (int32_t)(pPal->b[bestInd]) * 256;
nextPix[0] = pPal->r[bestInd]; nextPix[0] = pPal->r[bestInd];
nextPix[1] = pPal->g[bestInd]; nextPix[1] = pPal->g[bestInd];
@ -510,7 +511,7 @@ void GifThresholdImage( const uint8_t* lastFrame, const uint8_t* nextFrame, uint
// palettize the pixel // palettize the pixel
int32_t bestDiff = 1000000; int32_t bestDiff = 1000000;
int32_t bestInd = 1; int32_t bestInd = 1;
GifGetClosestPaletteColor(pPal, nextFrame[0], nextFrame[1], nextFrame[2], bestInd, bestDiff); GifGetClosestPaletteColor(pPal, nextFrame[0], nextFrame[1], nextFrame[2], &bestInd, &bestDiff, 1);
// Write the resulting color to the output buffer // Write the resulting color to the output buffer
outFrame[0] = pPal->r[bestInd]; outFrame[0] = pPal->r[bestInd];
@ -527,52 +528,52 @@ void GifThresholdImage( const uint8_t* lastFrame, const uint8_t* nextFrame, uint
// Simple structure to write out the LZW-compressed portion of the image // Simple structure to write out the LZW-compressed portion of the image
// one bit at a time // one bit at a time
struct GifBitStatus typedef struct
{ {
uint8_t bitIndex; // how many bits in the partial byte written so far uint8_t bitIndex; // how many bits in the partial byte written so far
uint8_t byte; // current partial byte uint8_t byte; // current partial byte
uint32_t chunkIndex; uint32_t chunkIndex;
uint8_t chunk[256]; // bytes are written in here until we have 256 of them, then written to the file uint8_t chunk[256]; // bytes are written in here until we have 256 of them, then written to the file
}; } GifBitStatus;
// insert a single bit // insert a single bit
void GifWriteBit( GifBitStatus& stat, uint32_t bit ) void GifWriteBit( GifBitStatus* stat, uint32_t bit )
{ {
bit = bit & 1; bit = bit & 1;
bit = bit << stat.bitIndex; bit = bit << stat->bitIndex;
stat.byte |= bit; stat->byte |= bit;
++stat.bitIndex; ++stat->bitIndex;
if( stat.bitIndex > 7 ) if( stat->bitIndex > 7 )
{ {
// move the newly-finished byte to the chunk buffer // move the newly-finished byte to the chunk buffer
stat.chunk[stat.chunkIndex++] = stat.byte; stat->chunk[stat->chunkIndex++] = stat->byte;
// and start a new byte // and start a new byte
stat.bitIndex = 0; stat->bitIndex = 0;
stat.byte = 0; stat->byte = 0;
} }
} }
// write all bytes so far to the file // write all bytes so far to the file
void GifWriteChunk( FILE* f, GifBitStatus& stat ) void GifWriteChunk( FILE* f, GifBitStatus* stat )
{ {
fputc((int)stat.chunkIndex, f); fputc((int)stat->chunkIndex, f);
fwrite(stat.chunk, 1, stat.chunkIndex, f); fwrite(stat->chunk, 1, stat->chunkIndex, f);
stat.bitIndex = 0; stat->bitIndex = 0;
stat.byte = 0; stat->byte = 0;
stat.chunkIndex = 0; stat->chunkIndex = 0;
} }
void GifWriteCode( FILE* f, GifBitStatus& stat, uint32_t code, uint32_t length ) void GifWriteCode( FILE* f, GifBitStatus* stat, uint32_t code, uint32_t length )
{ {
for( uint32_t ii=0; ii<length; ++ii ) for( uint32_t ii=0; ii<length; ++ii )
{ {
GifWriteBit(stat, code); GifWriteBit(stat, code);
code = code >> 1; code = code >> 1;
if( stat.chunkIndex == 255 ) if( stat->chunkIndex == 255 )
{ {
GifWriteChunk(f, stat); GifWriteChunk(f, stat);
} }
@ -581,10 +582,10 @@ void GifWriteCode( FILE* f, GifBitStatus& stat, uint32_t code, uint32_t length )
// The LZW dictionary is a 256-ary tree constructed as the file is encoded, // The LZW dictionary is a 256-ary tree constructed as the file is encoded,
// this is one node // this is one node
struct GifLzwNode typedef struct
{ {
uint16_t m_next[256]; uint16_t m_next[256];
}; } GifLzwNode;
// write a 256-color (8-bit) image palette to the file // write a 256-color (8-bit) image palette to the file
void GifWritePalette( const GifPalette* pPal, FILE* f ) void GifWritePalette( const GifPalette* pPal, FILE* f )
@ -653,7 +654,7 @@ void GifWriteLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, uin
stat.bitIndex = 0; stat.bitIndex = 0;
stat.chunkIndex = 0; stat.chunkIndex = 0;
GifWriteCode(f, stat, clearCode, codeSize); // start with a fresh LZW dictionary GifWriteCode(f, &stat, clearCode, codeSize); // start with a fresh LZW dictionary
for(uint32_t yy=0; yy<height; ++yy) for(uint32_t yy=0; yy<height; ++yy)
{ {
@ -684,7 +685,7 @@ void GifWriteLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, uin
else else
{ {
// finish the current run, write a code // finish the current run, write a code
GifWriteCode(f, stat, (uint32_t)curCode, codeSize); GifWriteCode(f, &stat, (uint32_t)curCode, codeSize);
// insert the new run into the dictionary // insert the new run into the dictionary
codetree[curCode].m_next[nextValue] = (uint16_t)++maxCode; codetree[curCode].m_next[nextValue] = (uint16_t)++maxCode;
@ -698,7 +699,7 @@ void GifWriteLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, uin
if( maxCode == 4095 ) if( maxCode == 4095 )
{ {
// the dictionary is full, clear it out and begin anew // the dictionary is full, clear it out and begin anew
GifWriteCode(f, stat, clearCode, codeSize); // clear tree GifWriteCode(f, &stat, clearCode, codeSize); // clear tree
memset(codetree, 0, sizeof(GifLzwNode)*4096); memset(codetree, 0, sizeof(GifLzwNode)*4096);
codeSize = (uint32_t)(minCodeSize + 1); codeSize = (uint32_t)(minCodeSize + 1);
@ -711,25 +712,25 @@ void GifWriteLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, uin
} }
// compression footer // compression footer
GifWriteCode(f, stat, (uint32_t)curCode, codeSize); GifWriteCode(f, &stat, (uint32_t)curCode, codeSize);
GifWriteCode(f, stat, clearCode, codeSize); GifWriteCode(f, &stat, clearCode, codeSize);
GifWriteCode(f, stat, clearCode + 1, (uint32_t)minCodeSize + 1); GifWriteCode(f, &stat, clearCode + 1, (uint32_t)minCodeSize + 1);
// write out the last partial chunk // write out the last partial chunk
while( stat.bitIndex ) GifWriteBit(stat, 0); while( stat.bitIndex ) GifWriteBit(&stat, 0);
if( stat.chunkIndex ) GifWriteChunk(f, stat); if( stat.chunkIndex ) GifWriteChunk(f, &stat);
fputc(0, f); // image block terminator fputc(0, f); // image block terminator
GIF_TEMP_FREE(codetree); GIF_TEMP_FREE(codetree);
} }
struct GifWriter typedef struct
{ {
FILE* f; FILE* f;
uint8_t* oldImage; uint8_t* oldImage;
bool firstFrame; bool firstFrame;
}; } GifWriter;
// Creates a gif file. // Creates a gif file.
// The input GIFWriter is assumed to be uninitialized. // The input GIFWriter is assumed to be uninitialized.