gif/encoder: fixed memory violation.

There was an invalid palette data access
when no frame data had been changed, detected by memory sanitizer.
This commit is contained in:
Hermet Park 2023-11-17 19:17:31 +09:00
parent f98d01c2c5
commit 0665837899
2 changed files with 48 additions and 45 deletions

View file

@ -52,21 +52,7 @@
#define TRANSPARENT_IDX 0 #define TRANSPARENT_IDX 0
#define TRANSPARENT_THRESHOLD 127 #define TRANSPARENT_THRESHOLD 127
#define BIT_DEPTH 8
typedef struct
{
int bitDepth;
uint8_t r[256];
uint8_t g[256];
uint8_t b[256];
// 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
// nodes 256-511 are implicitly the leaves, containing a color
uint8_t treeSplitElt[256];
uint8_t treeSplit[256];
} GifPalette;
// Simple structure to write out the LZW-compressed portion of the image // Simple structure to write out the LZW-compressed portion of the image
@ -100,8 +86,8 @@ typedef struct
static void _getClosestPaletteColor( GifPalette* pPal, int r, int g, int b, int* bestInd, int* bestDiff, int treeRoot ) static void _getClosestPaletteColor( 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 << BIT_DEPTH) - 1) {
int ind = treeRoot-(1<<pPal->bitDepth); int ind = treeRoot-(1 << BIT_DEPTH);
if(ind == TRANSPARENT_IDX) return; if(ind == TRANSPARENT_IDX) return;
// check whether this color is better than the current winner // check whether this color is better than the current winner
@ -118,7 +104,8 @@ static void _getClosestPaletteColor( GifPalette* pPal, int r, int g, int b, int*
} }
// take the appropriate color (r, g, or b) for this node of the k-d tree // take the appropriate color (r, g, or b) for this node of the k-d tree
int comps[3]; comps[0] = r; comps[1] = g; comps[2] = b; int comps[3] = {r, g, b};
int splitComp = comps[pPal->treeSplitElt[treeRoot]]; int splitComp = comps[pPal->treeSplitElt[treeRoot]];
int splitPos = pPal->treeSplit[treeRoot]; int splitPos = pPal->treeSplit[treeRoot];
@ -228,7 +215,6 @@ static void _splitPalette(uint8_t* image, int numPixels, int firstElt, int lastE
pal->r[firstElt] = (uint8_t)r; pal->r[firstElt] = (uint8_t)r;
pal->g[firstElt] = (uint8_t)g; pal->g[firstElt] = (uint8_t)g;
pal->b[firstElt] = (uint8_t)b; pal->b[firstElt] = (uint8_t)b;
return; return;
} }
@ -303,9 +289,9 @@ static int _pickChangedPixels(const uint8_t* lastFrame, uint8_t* frame, int numP
// Creates a palette by placing all the image pixels in a k-d tree and then averaging the blocks at the bottom. // Creates a palette by placing all the image pixels in a k-d tree and then averaging the blocks at the bottom.
// This is known as the "modified median split" technique // This is known as the "modified median split" technique
static void _makePalette(GifWriter* writer, const uint8_t* lastFrame, const uint8_t* nextFrame, uint32_t width, uint32_t height, int bitDepth, GifPalette* pPal, bool transparent) static void _makePalette(GifWriter* writer, const uint8_t* lastFrame, const uint8_t* nextFrame, uint32_t width, uint32_t height, int bitDepth, bool transparent)
{ {
pPal->bitDepth = bitDepth; auto& pal = writer->pal;
size_t imageSize = (size_t)(width * height * 4 * sizeof(uint8_t)); size_t imageSize = (size_t)(width * height * 4 * sizeof(uint8_t));
memcpy(writer->tmpImage, nextFrame, imageSize); memcpy(writer->tmpImage, nextFrame, imageSize);
@ -317,13 +303,12 @@ static void _makePalette(GifWriter* writer, const uint8_t* lastFrame, const uint
const int splitElt = lastElt/2; const int splitElt = lastElt/2;
const int splitDist = splitElt/2; const int splitDist = splitElt/2;
_splitPalette(writer->tmpImage, numPixels, 1, lastElt, splitElt, splitDist, 1, pPal); _splitPalette(writer->tmpImage, numPixels, 1, lastElt, splitElt, splitDist, 1, &pal);
// add the bottom node for the transparency index // add the bottom node for the transparency index
pPal->treeSplit[1 << (bitDepth-1)] = 0; pal.treeSplit[1 << (bitDepth-1)] = 0;
pPal->treeSplitElt[1 << (bitDepth-1)] = 0; pal.treeSplitElt[1 << (bitDepth-1)] = 0;
pal.r[0] = pal.g[0] = pal.b[0] = 0;
pPal->r[0] = pPal->g[0] = pPal->b[0] = 0;
} }
@ -342,8 +327,9 @@ void _palettizePixel(const uint8_t* nextFrame, uint8_t* outFrame, GifPalette* pP
// Picks palette colors for the image using simple thresholding, no dithering // Picks palette colors for the image using simple thresholding, no dithering
static void _thresholdImage(const uint8_t* lastFrame, const uint8_t* nextFrame, uint8_t* outFrame, uint32_t width, uint32_t height, GifPalette* pPal, bool transparent) static void _thresholdImage(GifWriter* writer, const uint8_t* lastFrame, const uint8_t* nextFrame, uint32_t width, uint32_t height, bool transparent)
{ {
auto outFrame = writer->oldImage;
uint32_t numPixels = width*height; uint32_t numPixels = width*height;
if (transparent) { if (transparent) {
@ -354,7 +340,7 @@ static void _thresholdImage(const uint8_t* lastFrame, const uint8_t* nextFrame,
outFrame[2] = 0; outFrame[2] = 0;
outFrame[3] = TRANSPARENT_IDX; outFrame[3] = TRANSPARENT_IDX;
} else { } else {
_palettizePixel(nextFrame, outFrame, pPal); _palettizePixel(nextFrame, outFrame, &writer->pal);
} }
if (lastFrame) lastFrame += 4; if (lastFrame) lastFrame += 4;
outFrame += 4; outFrame += 4;
@ -370,7 +356,7 @@ static void _thresholdImage(const uint8_t* lastFrame, const uint8_t* nextFrame,
outFrame[2] = lastFrame[2]; outFrame[2] = lastFrame[2];
outFrame[3] = TRANSPARENT_IDX; outFrame[3] = TRANSPARENT_IDX;
} else { } else {
_palettizePixel(nextFrame, outFrame, pPal); _palettizePixel(nextFrame, outFrame, &writer->pal);
} }
if (lastFrame) lastFrame += 4; if (lastFrame) lastFrame += 4;
outFrame += 4; outFrame += 4;
@ -427,7 +413,7 @@ static void _writePalette(const GifPalette* pPal, FILE* f)
fputc(0, f); fputc(0, f);
fputc(0, f); fputc(0, f);
for (int ii = 1; ii < (1 << pPal->bitDepth); ++ii) { for (int ii = 1; ii < (1 << BIT_DEPTH); ++ii) {
uint32_t r = pPal->r[ii]; uint32_t r = pPal->r[ii];
uint32_t g = pPal->g[ii]; uint32_t g = pPal->g[ii];
uint32_t b = pPal->b[ii]; uint32_t b = pPal->b[ii];
@ -440,8 +426,11 @@ static void _writePalette(const GifPalette* pPal, FILE* f)
// write the image header, LZW-compress and write out the image // write the image header, LZW-compress and write out the image
static void _writeLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top, uint32_t width, uint32_t height, uint32_t delay, GifPalette* pPal, bool transparent) static void _writeLzwImage(GifWriter* writer, uint32_t width, uint32_t height, uint32_t delay, bool transparent)
{ {
auto f = writer->f;
auto image = writer->oldImage;
// graphics control extension // graphics control extension
fputc(0x21, f); fputc(0x21, f);
fputc(0xf9, f); fputc(0xf9, f);
@ -454,10 +443,11 @@ static void _writeLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top,
fputc(0x2c, f); // image descriptor block fputc(0x2c, f); // image descriptor block
fputc(left & 0xff, f); // corner of image in canvas space // corner of image (left, top) in canvas space
fputc((left >> 8) & 0xff, f); fputc(0, f);
fputc(top & 0xff, f); fputc(0, f);
fputc((top >> 8) & 0xff, f); fputc(0, f);
fputc(0, f);
fputc(width & 0xff, f); // width and height of image fputc(width & 0xff, f); // width and height of image
fputc((width >> 8) & 0xff, f); fputc((width >> 8) & 0xff, f);
@ -467,11 +457,11 @@ static void _writeLzwImage(FILE* f, uint8_t* image, uint32_t left, uint32_t top,
//fputc(0, f); // no local color table, no transparency //fputc(0, f); // no local color table, no transparency
//fputc(0x80, f); // no local color table, but transparency //fputc(0x80, f); // no local color table, but transparency
fputc(0x80 + pPal->bitDepth-1, f); // local color table present, 2 ^ bitDepth entries fputc(0x80 + BIT_DEPTH - 1, f); // local color table present, 2 ^ bitDepth entries
_writePalette(pPal, f); _writePalette(&writer->pal, f);
const int minCodeSize = pPal->bitDepth; const int minCodeSize = BIT_DEPTH;
const uint32_t clearCode = 1 << pPal->bitDepth; const uint32_t clearCode = 1 << BIT_DEPTH;
fputc(minCodeSize, f); // min code size 8 bits fputc(minCodeSize, f); // min code size 8 bits
@ -614,12 +604,9 @@ bool gifWriteFrame(GifWriter* writer, const uint8_t* image, uint32_t width, uint
const uint8_t* oldImage = writer->firstFrame? NULL : writer->oldImage; const uint8_t* oldImage = writer->firstFrame? NULL : writer->oldImage;
writer->firstFrame = false; writer->firstFrame = false;
GifPalette pal; _makePalette(writer, oldImage, image, width, height, 8, transparent);
_makePalette(writer, oldImage, image, width, height, 8, &pal, transparent); _thresholdImage(writer, oldImage, image, width, height, transparent);
_writeLzwImage(writer, width, height, delay, transparent);
_thresholdImage(oldImage, image, writer->oldImage, width, height, &pal, transparent);
_writeLzwImage(writer->f, writer->oldImage, 0, 0, width, height, delay, &pal, transparent);
return true; return true;
} }

View file

@ -27,11 +27,27 @@
#include <stdio.h> #include <stdio.h>
#include <cstdint> #include <cstdint>
typedef struct
{
uint8_t r[256];
uint8_t g[256];
uint8_t b[256];
// 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
// nodes 256-511 are implicitly the leaves, containing a color
uint8_t treeSplitElt[256];
uint8_t treeSplit[256];
} GifPalette;
typedef struct typedef struct
{ {
FILE* f; FILE* f;
uint8_t* oldImage; uint8_t* oldImage;
uint8_t* tmpImage; uint8_t* tmpImage;
GifPalette pal;
bool firstFrame; bool firstFrame;
} GifWriter; } GifWriter;