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_THRESHOLD 127
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;
#define BIT_DEPTH 8
// 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 )
{
// base case, reached the bottom of the tree
if(treeRoot > (1<<pPal->bitDepth)-1) {
int ind = treeRoot-(1<<pPal->bitDepth);
if (treeRoot > (1 << BIT_DEPTH) - 1) {
int ind = treeRoot-(1 << BIT_DEPTH);
if(ind == TRANSPARENT_IDX) return;
// 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
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 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->g[firstElt] = (uint8_t)g;
pal->b[firstElt] = (uint8_t)b;
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.
// 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));
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 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
pPal->treeSplit[1 << (bitDepth-1)] = 0;
pPal->treeSplitElt[1 << (bitDepth-1)] = 0;
pPal->r[0] = pPal->g[0] = pPal->b[0] = 0;
pal.treeSplit[1 << (bitDepth-1)] = 0;
pal.treeSplitElt[1 << (bitDepth-1)] = 0;
pal.r[0] = pal.g[0] = pal.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
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;
if (transparent) {
@ -354,7 +340,7 @@ static void _thresholdImage(const uint8_t* lastFrame, const uint8_t* nextFrame,
outFrame[2] = 0;
outFrame[3] = TRANSPARENT_IDX;
} else {
_palettizePixel(nextFrame, outFrame, pPal);
_palettizePixel(nextFrame, outFrame, &writer->pal);
}
if (lastFrame) lastFrame += 4;
outFrame += 4;
@ -370,7 +356,7 @@ static void _thresholdImage(const uint8_t* lastFrame, const uint8_t* nextFrame,
outFrame[2] = lastFrame[2];
outFrame[3] = TRANSPARENT_IDX;
} else {
_palettizePixel(nextFrame, outFrame, pPal);
_palettizePixel(nextFrame, outFrame, &writer->pal);
}
if (lastFrame) lastFrame += 4;
outFrame += 4;
@ -427,7 +413,7 @@ static void _writePalette(const GifPalette* pPal, FILE* 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 g = pPal->g[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
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
fputc(0x21, 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(left & 0xff, f); // corner of image in canvas space
fputc((left >> 8) & 0xff, f);
fputc(top & 0xff, f);
fputc((top >> 8) & 0xff, f);
// corner of image (left, top) in canvas space
fputc(0, f);
fputc(0, f);
fputc(0, f);
fputc(0, f);
fputc(width & 0xff, f); // width and height of image
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(0x80, f); // no local color table, but transparency
fputc(0x80 + pPal->bitDepth-1, f); // local color table present, 2 ^ bitDepth entries
_writePalette(pPal, f);
fputc(0x80 + BIT_DEPTH - 1, f); // local color table present, 2 ^ bitDepth entries
_writePalette(&writer->pal, f);
const int minCodeSize = pPal->bitDepth;
const uint32_t clearCode = 1 << pPal->bitDepth;
const int minCodeSize = BIT_DEPTH;
const uint32_t clearCode = 1 << BIT_DEPTH;
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;
writer->firstFrame = false;
GifPalette pal;
_makePalette(writer, oldImage, image, width, height, 8, &pal, transparent);
_thresholdImage(oldImage, image, writer->oldImage, width, height, &pal, transparent);
_writeLzwImage(writer->f, writer->oldImage, 0, 0, width, height, delay, &pal, transparent);
_makePalette(writer, oldImage, image, width, height, 8, transparent);
_thresholdImage(writer, oldImage, image, width, height, transparent);
_writeLzwImage(writer, width, height, delay, transparent);
return true;
}

View file

@ -27,11 +27,27 @@
#include <stdio.h>
#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
{
FILE* f;
uint8_t* oldImage;
uint8_t* tmpImage;
GifPalette pal;
bool firstFrame;
} GifWriter;