Miscellaneous bmp loader fixes (#85448, #86286, #86287):

* io-bmp.c (grow_buffer): New function to avoid crashes
	on unchecked reallocs.
	(DecodeHeader, DecodeColormap, decode_bitmasks,
	DoCompressed): Use grow_buffer instead of g_realloc
	throughout. Change signatures where necessary to pass the
	errors up.
	(OneLine16): Fix loading of 16bpp BI_RGB bmps.
	(DoCompressed): Rewritten to properly support BI_RLE4 and
	skips and jumps.
This commit is contained in:
Matthias Clasen 2002-07-02 17:54:06 +00:00
parent 7b0ef96dd9
commit 096e8ea297
2 changed files with 222 additions and 158 deletions

View File

@ -1,5 +1,17 @@
2002-07-02 Matthias Clasen <maclas@gmx.de>
Miscellaneous bmp loader fixes (#85448, #86286, #86287):
* io-bmp.c (grow_buffer): New function to avoid crashes
on unchecked reallocs.
(DecodeHeader, DecodeColormap, decode_bitmasks,
DoCompressed): Use grow_buffer instead of g_realloc
throughout. Change signatures where necessary to pass the
errors up.
(OneLine16): Fix loading of 16bpp BI_RGB bmps.
(DoCompressed): Rewritten to properly support BI_RLE4 and
skips and jumps.
Support for compressed ras images (#84994):
* io-ras.c (RAS2State): Error on unsupported ras variations.
@ -16,6 +28,8 @@
gdk_pixbuf__jpeg_image_load_increment): Allocate a pixbuf with
alpha for 4-channel jpegs and call convert_cmyk_to_rgb for these.
All of this needs to be merged to GNOME 1.4 gdk-pixbuf.
2002-06-28 Sven Neumann <sven@gimp.org>
* gdk-pixbuf-csource.c (print_blurb): converted a Tab to spaces.

View File

@ -134,11 +134,10 @@ struct headerpair {
/* Data needed for the "state" during decompression */
struct bmp_compression_state {
gint phase;
gint RunCount;
guchar *linebuff;
gint linebuffsize; /* these two counts in nibbles */
gint linebuffdone;
gint run;
gint count;
gint x, y;
guchar *p;
};
/* Progressive loading */
@ -167,7 +166,7 @@ struct bmp_progressive_state {
8 = 8 bpp colormapped
1 = 1 bit bitonal
*/
gint Compressed;
guint Compressed;
struct bmp_compression_state compr;
@ -250,6 +249,21 @@ lsb_16 (guchar *src)
return src[0] | (src[1] << 8);
}
static gboolean grow_buffer (struct bmp_progressive_state *State,
GError **error)
{
State->buff = g_try_realloc (State->buff, State->BufferSize);
if (State->buff == NULL) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Not enough memory to load bitmap image"));
State->read_state = READ_STATE_ERROR;
return FALSE;
}
return TRUE;
}
static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
struct bmp_progressive_state *State,
GError **error)
@ -258,15 +272,8 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
if (State->BufferSize < lsb_32 (&BIH[0]) + 14) {
State->BufferSize = lsb_32 (&BIH[0]) + 14;
State->buff = g_try_realloc (State->buff, State->BufferSize);
if (State->buff == NULL) {
g_set_error (error,
GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Not enough memory to load bitmap image"));
State->read_state = READ_STATE_ERROR;
if (!grow_buffer (State, error))
return FALSE;
}
return TRUE;
}
@ -348,7 +355,9 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
State->LineWidth = (State->LineWidth / 4) * 4 + 4;
if (State->pixbuf == NULL) {
if (State->Type == 32)
if (State->Type == 32 ||
State->Compressed == BI_RLE4 ||
State->Compressed == BI_RLE8)
State->pixbuf =
gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8,
(gint) State->Header.width,
@ -373,19 +382,18 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
(*State->prepared_func) (State->pixbuf, NULL, State->user_data);
}
if (!(State->Compressed == BI_RGB || State->Compressed == BI_BITFIELDS)) {
State->compr.linebuffdone = 0;
State->compr.linebuffsize = State->Header.width;
if (State->Type == 8)
State->compr.linebuffsize *= 2;
State->compr.linebuff = g_malloc ((State->compr.linebuffsize + 1) / 2);
/* make all pixels initially transparent */
if (State->Compressed == BI_RLE4 || State->Compressed == BI_RLE8) {
memset (State->pixbuf->pixels, 0, State->pixbuf->rowstride * State->Header.height);
State->compr.p = State->pixbuf->pixels
+ State->pixbuf->rowstride * (State->Header.height- 1);
}
State->BufferDone = 0;
if (State->Type <= 8) {
State->read_state = READ_STATE_PALETTE;
State->BufferSize = lsb_32 (&BFH[10]) - 14 - State->Header.size;
State->BufferSize = lsb_32 (&BFH[10]) - 14 - State->Header.size;
} else if (State->Compressed == BI_RGB) {
State->read_state = READ_STATE_DATA;
State->BufferSize = State->LineWidth;
@ -401,14 +409,15 @@ static gboolean DecodeHeader(unsigned char *BFH, unsigned char *BIH,
return FALSE;
}
State->buff = g_realloc (State->buff, State->BufferSize);
if (!grow_buffer (State, error))
return FALSE;
return TRUE;
}
static void DecodeColormap (guchar *buff,
struct bmp_progressive_state *State,
GError **error)
static gboolean DecodeColormap (guchar *buff,
struct bmp_progressive_state *State,
GError **error)
{
gint i;
@ -421,6 +430,12 @@ static void DecodeColormap (guchar *buff,
State->Colormap[i][0] = buff[i * (State->Header.size == 12 ? 3 : 4)];
State->Colormap[i][1] = buff[i * (State->Header.size == 12 ? 3 : 4) + 1];
State->Colormap[i][2] = buff[i * (State->Header.size == 12 ? 3 : 4) + 2];
#ifdef DUMPCMAP
g_print ("color %d %x %x %x\n", i,
State->Colormap[i][0],
State->Colormap[i][1],
State->Colormap[i][2]);
#endif
}
State->read_state = READ_STATE_DATA;
@ -430,8 +445,11 @@ static void DecodeColormap (guchar *buff,
State->BufferSize = 2;
else
State->BufferSize = State->LineWidth;
if (!grow_buffer (State, error))
return FALSE;
State->buff = g_realloc (State->buff, State->BufferSize);
return TRUE;
}
/* Finds the lowest set bit and the number of set bits */
@ -450,8 +468,10 @@ find_bits (int n, int *lowest, int *n_set)
}
/* Decodes the 3 shorts that follow for the bitmasks for BI_BITFIELDS coding */
static void
decode_bitmasks (struct bmp_progressive_state *State, guchar *buf)
static gboolean
decode_bitmasks (guchar *buf,
struct bmp_progressive_state *State,
GError **error)
{
State->r_mask = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
buf += 4;
@ -479,7 +499,10 @@ decode_bitmasks (struct bmp_progressive_state *State, guchar *buf)
State->read_state = READ_STATE_DATA;
State->BufferDone = 0;
State->BufferSize = State->LineWidth;
State->buff = g_realloc (State->buff, State->BufferSize);
if (!grow_buffer (State, error))
return FALSE;
return TRUE;
}
/*
@ -540,9 +563,6 @@ static gboolean gdk_pixbuf__bmp_image_stop_load(gpointer data, GError **error)
g_return_val_if_fail(context != NULL, TRUE);
if (context->compr.linebuff != NULL)
g_free(context->compr.linebuff);
if (context->Colormap != NULL)
g_free(context->Colormap);
@ -694,6 +714,8 @@ static void OneLine16(struct bmp_progressive_state *context)
*pixels++ = (r << 3) | (r >> 2);
*pixels++ = (g << 3) | (g >> 2);
*pixels++ = (b << 3) | (b >> 2);
src += 2;
}
}
@ -818,139 +840,165 @@ static void OneLine(struct bmp_progressive_state *context)
0,
context->Lines,
context->Header.width,
1,
2,
context->user_data);
}
}
static void
DoCompressed(struct bmp_progressive_state *context)
#define NEUTRAL 0
#define ENCODED 1
#define ESCAPE 2
#define DELTA_X 3
#define DELTA_Y 4
#define ABSOLUTE 5
#define SKIP 6
#define END_OF_LINE 0
#define END_OF_BITMAP 1
#define DELTA 2
static gboolean
DoCompressed(struct bmp_progressive_state *context, GError **error)
{
gint count, pos;
switch (context->compr.phase) {
case 0: /* Neutral state */
if (context->buff[0] != 0) { /* run count */
context->compr.RunCount = context->buff[0];
if (context->Type == 8)
context->compr.RunCount *= 2;
while (context->compr.RunCount > 0) {
if (context->compr.linebuffdone & 1) {
guchar *ptr = context->compr.linebuff +
context->compr.linebuffdone / 2;
gint i, j;
gint y;
guchar c;
gint idx;
*ptr = (*ptr & 0xF0) | (context->buff[1] >> 4);
context->buff[1] = (context->buff[1] << 4) |
(context->buff[1] >> 4);
context->compr.linebuffdone++;
context->compr.RunCount--;
}
if (context->compr.y >= context->Header.height)
return TRUE;
if (context->compr.RunCount) {
count = context->compr.linebuffsize -
context->compr.linebuffdone;
if (count > context->compr.RunCount)
count = context->compr.RunCount;
y = context->compr.y;
memset (context->compr.linebuff +
context->compr.linebuffdone / 2,
context->buff[1],
(count + 1) / 2);
context->compr.RunCount -= count;
context->compr.linebuffdone += count;
}
if (context->compr.linebuffdone == context->compr.linebuffsize) {
guchar *tmp = context->buff;
context->buff = context->compr.linebuff;
OneLine (context);
context->buff = tmp;
for (i = 0; i < context->BufferSize; i++) {
c = context->buff[i];
switch (context->compr.phase) {
case NEUTRAL:
if (c) {
context->compr.run = c;
context->compr.phase = ENCODED;
}
else
context->compr.phase = ESCAPE;
break;
case ENCODED:
for (j = 0; j < context->compr.run; j++) {
if (context->Compressed == BI_RLE8)
idx = c;
else if (j & 1)
idx = c & 0x0f;
else
idx = (c >> 4) & 0x0f;
if (context->compr.x < context->Header.width) {
*context->compr.p++ = context->Colormap[idx][2];
*context->compr.p++ = context->Colormap[idx][1];
*context->compr.p++ = context->Colormap[idx][0];
*context->compr.p++ = 0xff;
context->compr.x++;
}
}
context->compr.phase = NEUTRAL;
break;
case ESCAPE:
switch (c) {
case END_OF_LINE:
context->compr.x = 0;
context->compr.y++;
context->compr.p = context->pixbuf->pixels
+ (context->pixbuf->rowstride * (context->Header.height - context->compr.y - 1))
+ (4 * context->compr.x);
context->compr.phase = NEUTRAL;
break;
case END_OF_BITMAP:
context->compr.x = 0;
context->compr.y = context->Header.height;
context->compr.phase = NEUTRAL;
break;
case DELTA:
context->compr.phase = DELTA_X;
break;
default:
context->compr.run = c;
context->compr.count = 0;
context->compr.phase = ABSOLUTE;
break;
}
break;
case DELTA_X:
context->compr.x += c;
context->compr.phase = DELTA_Y;
break;
case DELTA_Y:
context->compr.y += c;
context->compr.p = context->pixbuf->pixels
+ (context->pixbuf->rowstride * (context->Header.height - context->compr.y - 1))
+ (4 * context->compr.x);
context->compr.phase = NEUTRAL;
break;
case ABSOLUTE:
if (context->Compressed == BI_RLE8) {
idx = c;
if (context->compr.x < context->Header.width) {
*context->compr.p++ = context->Colormap[idx][2];
*context->compr.p++ = context->Colormap[idx][1];
*context->compr.p++ = context->Colormap[idx][0];
*context->compr.p++ = 0xff;
context->compr.x++;
}
context->compr.count++;
if (context->compr.linebuffdone & 1)
context->buff[1] = (context->buff[1] << 4) |
(context->buff[1] >> 4);
context->compr.linebuffdone = 0;
}
}
} else { /* Escape */
if (context->buff[1] == 0) { /* End of line */
if (context->compr.linebuffdone) {
guchar *tmp = context->buff;
context->buff = context->compr.linebuff;
OneLine (context);
context->buff = tmp;
if (context->compr.count == context->compr.run) {
if (context->compr.run & 1)
context->compr.phase = SKIP;
else
context->compr.phase = NEUTRAL;
}
}
else {
for (j = 0; j < 2; j++) {
if (context->compr.count & 1)
idx = c & 0x0f;
else
idx = (c >> 4) & 0x0f;
if (context->compr.x < context->Header.width) {
*context->compr.p++ = context->Colormap[idx][2];
*context->compr.p++ = context->Colormap[idx][1];
*context->compr.p++ = context->Colormap[idx][0];
*context->compr.p++ = 0xff;
context->compr.x++;
}
context->compr.count++;
context->compr.linebuffdone = 0;
}
} else if (context->buff[1] == 1) { /* End of image */
if (context->compr.linebuffdone) {
guchar *tmp = context->buff;
context->buff = context->compr.linebuff;
OneLine (context);
context->buff = tmp;
}
context->compr.phase = 2;
} else if (context->buff[1] == 2) /* Cursor displacement */
; /* not implemented */
else {
context->compr.phase = 1;
context->compr.RunCount = context->buff[1];
if (context->Type == 8)
context->compr.RunCount *= 2;
context->BufferSize = (context->compr.RunCount + 3) / 4 * 2;
context->buff = g_realloc (context->buff, context->BufferSize);
}
if (context->compr.count == context->compr.run) {
if ((context->compr.run & 3) == 1
|| (context->compr.run & 3) == 2)
context->compr.phase = SKIP;
else
context->compr.phase = NEUTRAL;
break;
}
}
}
break;
case SKIP:
context->compr.phase = NEUTRAL;
break;
}
context->BufferDone = 0;
break;
case 1:
pos = 0;
while (pos < context->compr.RunCount) {
count = context->compr.linebuffsize - context->compr.linebuffdone;
if (count > context->compr.RunCount)
count = context->compr.RunCount;
if ((context->compr.linebuffdone & 1) || (pos & 1)) {
gint i, newval;
guchar *ptr;
for (i = 0; i < count; i++) {
ptr = context->compr.linebuff + (i +
context->compr.linebuffdone) / 2;
newval = *(context->buff + (pos + i) / 2) & (0xf0 >> (((pos + i) % 2) * 4));
if (((pos + i) % 2) ^ ((context->compr.linebuffdone + i) % 2)) {
if ((pos + i) % 2)
newval <<= 4;
else
newval >>= 4;
}
*ptr = (*ptr & (0xf << (((i + context->compr.linebuffdone) % 2) * 4))) | newval;
}
} else {
memmove (context->compr.linebuff +
context->compr.linebuffdone / 2,
context->buff + pos / 2,
(count + 1) / 2);
}
pos += count;
context->compr.linebuffdone += count;
if (context->compr.linebuffdone == context->compr.linebuffsize) {
guchar *tmp = context->buff;
context->buff = context->compr.linebuff;
OneLine (context);
context->buff = tmp;
context->compr.linebuffdone = 0;
}
}
context->compr.phase = 0;
context->BufferSize = 2;
context->buff = g_realloc (context->buff, context->BufferSize);
context->BufferDone = 0;
break;
case 2:
context->BufferDone = 0;
break;
}
if (context->updated_func != NULL) {
if (context->compr.y > y)
(*context->updated_func) (context->pixbuf,
0,
y,
context->Header.width,
context->compr.y - y,
context->user_data);
}
context->BufferDone = 0;
return TRUE;
}
/*
@ -1005,18 +1053,20 @@ gdk_pixbuf__bmp_image_load_increment(gpointer data,
break;
case READ_STATE_PALETTE:
DecodeColormap (context->buff, context, error);
if (!DecodeColormap (context->buff, context, error))
return FALSE;
break;
case READ_STATE_BITMASKS:
decode_bitmasks (context, context->buff);
if (!decode_bitmasks (context->buff, context, error))
return FALSE;
break;
case READ_STATE_DATA:
if (context->Compressed == BI_RGB || context->Compressed == BI_BITFIELDS)
OneLine (context);
else
DoCompressed (context);
else if (!DoCompressed (context, error))
return FALSE;
break;