Fix assumptions on rowstride by manually allocating a contiguous pixel

* io-tga.c: Fix assumptions on rowstride by manually allocating a
	contiguous pixel buffer. Catch buffer overruns in RLE-modi.
	Support grayscale + alpha (which can be written, but not read (!)
	by the Gimp).
This commit is contained in:
Matthias Clasen 2002-03-18 22:19:24 +00:00
parent aa3d5719b7
commit ac7e3c9584
2 changed files with 238 additions and 71 deletions

View File

@ -1,3 +1,10 @@
2002-03-18 Matthias Clasen <maclas@gmx.de>
* io-tga.c: Fix assumptions on rowstride by manually allocating a
contiguous pixel buffer. Catch buffer overruns in RLE-modi.
Support grayscale + alpha (which can be written, but not read (!)
by the Gimp).
2002-03-15 Matthias Clasen <maclas@gmx.de> 2002-03-15 Matthias Clasen <maclas@gmx.de>
* io-gif.c (gif_get_frame_info): Catch invalid frame dimensions. * io-gif.c (gif_get_frame_info): Catch invalid frame dimensions.

View File

@ -42,6 +42,8 @@
#include "gdk-pixbuf-io.h" #include "gdk-pixbuf-io.h"
#include "gdk-pixbuf-private.h" #include "gdk-pixbuf-private.h"
#undef DEBUG_TGA
#define TGA_INTERLEAVE_MASK 0xc0 #define TGA_INTERLEAVE_MASK 0xc0
#define TGA_INTERLEAVE_NONE 0x00 #define TGA_INTERLEAVE_NONE 0x00
#define TGA_INTERLEAVE_2WAY 0x40 #define TGA_INTERLEAVE_2WAY 0x40
@ -265,6 +267,8 @@ static gboolean fseek_check(FILE *f, glong offset, gint whence, GError **err)
static gboolean fill_in_context(TGAContext *ctx, GError **err) static gboolean fill_in_context(TGAContext *ctx, GError **err)
{ {
gboolean alpha; gboolean alpha;
guint w, h;
guchar *pixels;
g_return_val_if_fail(ctx != NULL, FALSE); g_return_val_if_fail(ctx != NULL, FALSE);
@ -277,12 +281,25 @@ static gboolean fill_in_context(TGAContext *ctx, GError **err)
ctx->cmap_size = ((ctx->hdr->cmap_bpp + 7) >> 3) * ctx->cmap_size = ((ctx->hdr->cmap_bpp + 7) >> 3) *
LE16(ctx->hdr->cmap_n_colors); LE16(ctx->hdr->cmap_n_colors);
alpha = ((ctx->hdr->bpp == 32) || alpha = ((ctx->hdr->bpp == 16) ||
(ctx->hdr->bpp == 32) ||
(ctx->hdr->has_cmap && (ctx->hdr->cmap_bpp == 32))); (ctx->hdr->has_cmap && (ctx->hdr->cmap_bpp == 32)));
ctx->pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, alpha, 8, w = LE16(ctx->hdr->width);
LE16(ctx->hdr->width), h = LE16(ctx->hdr->height);
LE16(ctx->hdr->height));
pixels = g_try_malloc (w * h * (alpha ? 4 : 3));
if (!pixels) {
g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Insufficient memory to load TGA image"));
return FALSE;
}
ctx->pbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, alpha, 8,
w, h, w * (alpha ? 4 : 3),
free_buffer, NULL);
if (!ctx->pbuf) { if (!ctx->pbuf) {
g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Can't allocate new pixbuf")); _("Can't allocate new pixbuf"));
@ -291,9 +308,10 @@ static gboolean fill_in_context(TGAContext *ctx, GError **err)
ctx->pbuf_bytes = ctx->pbuf->rowstride * ctx->pbuf->height; ctx->pbuf_bytes = ctx->pbuf->rowstride * ctx->pbuf->height;
ctx->pptr = ctx->pbuf->pixels; ctx->pptr = ctx->pbuf->pixels;
if ((ctx->hdr->type == TGA_TYPE_PSEUDOCOLOR) || if (ctx->hdr->type == TGA_TYPE_PSEUDOCOLOR)
(ctx->hdr->type == TGA_TYPE_GRAYSCALE)) ctx->rowstride = ctx->pbuf->width;
ctx->rowstride = ctx->pbuf->width; else if (ctx->hdr->type == TGA_TYPE_GRAYSCALE)
ctx->rowstride = (alpha ? ctx->pbuf->width * 2 : ctx->pbuf->width);
else if (ctx->hdr->type == TGA_TYPE_TRUECOLOR) else if (ctx->hdr->type == TGA_TYPE_TRUECOLOR)
ctx->rowstride = ctx->pbuf->rowstride; ctx->rowstride = ctx->pbuf->rowstride;
@ -305,28 +323,31 @@ static void parse_data_for_row_pseudocolor(TGAContext *ctx)
{ {
guchar *s = ctx->in->data; guchar *s = ctx->in->data;
guint upper_bound = ctx->pbuf->width; guint upper_bound = ctx->pbuf->width;
guchar *p = ctx->pptr;
for (; upper_bound; upper_bound--, s++) { for (; upper_bound; upper_bound--, s++) {
*ctx->pptr++ = ctx->cmap->cols[*s].r; *p++ = ctx->cmap->cols[*s].r;
*ctx->pptr++ = ctx->cmap->cols[*s].g; *p++ = ctx->cmap->cols[*s].g;
*ctx->pptr++ = ctx->cmap->cols[*s].b; *p++ = ctx->cmap->cols[*s].b;
if (ctx->hdr->cmap_bpp == 32) if (ctx->hdr->cmap_bpp == 32)
*ctx->pptr++ = ctx->cmap->cols[*s].a; *p++ = ctx->cmap->cols[*s].a;
} }
ctx->pbuf_bytes_done += ctx->pbuf->n_channels * ctx->pbuf->width; ctx->pptr += ctx->pbuf->rowstride;
ctx->pbuf_bytes_done += ctx->pbuf->rowstride;
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
ctx->done = TRUE; ctx->done = TRUE;
} }
static void swap_channels(TGAContext *ctx) static void swap_channels(TGAContext *ctx)
{ {
register guchar swap; guchar swap;
register guint count; guint count;
guchar *p = ctx->pptr;
for (count = ctx->pbuf->width; count; count--) { for (count = ctx->pbuf->width; count; count--) {
swap = ctx->pptr[0]; swap = p[0];
ctx->pptr[0] = ctx->pptr[2]; p[0] = p[2];
ctx->pptr[2] = swap; p[2] = swap;
ctx->pptr += ctx->pbuf->n_channels; p += ctx->pbuf->n_channels;
} }
} }
@ -334,6 +355,7 @@ static void parse_data_for_row_truecolor(TGAContext *ctx)
{ {
g_memmove(ctx->pptr, ctx->in->data, ctx->pbuf->rowstride); g_memmove(ctx->pptr, ctx->in->data, ctx->pbuf->rowstride);
swap_channels(ctx); swap_channels(ctx);
ctx->pptr += ctx->pbuf->rowstride;
ctx->pbuf_bytes_done += ctx->pbuf->rowstride; ctx->pbuf_bytes_done += ctx->pbuf->rowstride;
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
ctx->done = TRUE; ctx->done = TRUE;
@ -344,11 +366,15 @@ static void parse_data_for_row_grayscale(TGAContext *ctx)
guchar *s = ctx->in->data; guchar *s = ctx->in->data;
guint upper_bound = ctx->pbuf->width; guint upper_bound = ctx->pbuf->width;
guchar *p = ctx->pptr;
for (; upper_bound; upper_bound--) { for (; upper_bound; upper_bound--) {
ctx->pptr[0] = ctx->pptr[1] = ctx->pptr[2] = *s++; p[0] = p[1] = p[2] = *s++;
ctx->pptr += 3; if (ctx->pbuf->n_channels == 4)
p[3] = *s++;
p += ctx->pbuf->n_channels;
} }
ctx->pbuf_bytes_done = ctx->pbuf->width * 3; ctx->pptr += ctx->pbuf->rowstride;
ctx->pbuf_bytes_done += ctx->pbuf->rowstride;
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
ctx->done = TRUE; ctx->done = TRUE;
} }
@ -372,10 +398,12 @@ static gboolean parse_data_for_row(TGAContext *ctx, GError **err)
static void write_rle_data(TGAContext *ctx, TGAColor *color, guint *rle_count) static void write_rle_data(TGAContext *ctx, TGAColor *color, guint *rle_count)
{ {
ctx->pbuf_bytes_done += ctx->pbuf->n_channels * (*rle_count);
for (; *rle_count; (*rle_count)--) { for (; *rle_count; (*rle_count)--) {
g_memmove(ctx->pptr, (guchar *) color, ctx->pbuf->n_channels); g_memmove(ctx->pptr, (guchar *) color, ctx->pbuf->n_channels);
ctx->pptr += ctx->pbuf->n_channels; ctx->pptr += ctx->pbuf->n_channels;
ctx->pbuf_bytes_done += ctx->pbuf->n_channels;
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
return;
} }
} }
@ -398,6 +426,10 @@ static guint parse_rle_data_pseudocolor(TGAContext *ctx)
rle_num = (tag & 0x7f) + 1; rle_num = (tag & 0x7f) + 1;
write_rle_data(ctx, &ctx->cmap->cols[*s], &rle_num); write_rle_data(ctx, &ctx->cmap->cols[*s], &rle_num);
s++, n++; s++, n++;
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) {
ctx->done = TRUE;
return n;
}
} }
} else { } else {
raw_num = tag + 1; raw_num = tag + 1;
@ -415,26 +447,20 @@ static guint parse_rle_data_pseudocolor(TGAContext *ctx)
*ctx->pptr++ = ctx->cmap->cols[*s].a; *ctx->pptr++ = ctx->cmap->cols[*s].a;
s++, n++; s++, n++;
ctx->pbuf_bytes_done += ctx->pbuf->n_channels; ctx->pbuf_bytes_done += ctx->pbuf->n_channels;
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) {
ctx->done = TRUE;
return n;
}
} }
} }
} }
} }
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) if (ctx->pbuf_bytes_done == ctx->pbuf_bytes)
ctx->done = TRUE; ctx->done = TRUE;
return n; return n;
} }
static void swap_channels_rle(TGAContext *ctx, guint count)
{
register guchar swap;
for (; count; count--) {
swap = ctx->pptr[0];
ctx->pptr[0] = ctx->pptr[2];
ctx->pptr[2] = swap;
ctx->pptr += ctx->pbuf->n_channels;
}
}
static guint parse_rle_data_truecolor(TGAContext *ctx) static guint parse_rle_data_truecolor(TGAContext *ctx)
{ {
TGAColor col; TGAColor col;
@ -458,19 +484,37 @@ static guint parse_rle_data_truecolor(TGAContext *ctx)
col.r = *s++; col.r = *s++;
if (ctx->hdr->bpp == 32) if (ctx->hdr->bpp == 32)
col.a = *s++; col.a = *s++;
write_rle_data(ctx, &col, &rle_num);
n += ctx->pbuf->n_channels; n += ctx->pbuf->n_channels;
write_rle_data(ctx, &col, &rle_num);
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) {
ctx->done = TRUE;
return n;
}
} }
} else { } else {
raw_num = tag + 1; raw_num = tag + 1;
if (n + (raw_num * ctx->pbuf->n_channels) >= ctx->in->size) { if (n + (raw_num * ctx->pbuf->n_channels) >= ctx->in->size) {
return --n; return --n;
} else { } else {
g_memmove(ctx->pptr, s, raw_num * ctx->pbuf->n_channels); for (; raw_num; raw_num--) {
swap_channels_rle(ctx, raw_num); ctx->pptr[2] = *s++;
s += raw_num * ctx->pbuf->n_channels; ctx->pptr[1] = *s++;
n += raw_num * ctx->pbuf->n_channels; ctx->pptr[0] = *s++;
ctx->pbuf_bytes_done += raw_num * ctx->pbuf->n_channels; if (ctx->hdr->bpp == 32)
ctx->pptr[3] = *s++;
n += ctx->pbuf->n_channels;
ctx->pptr += ctx->pbuf->n_channels;
ctx->pbuf_bytes_done += ctx->pbuf->n_channels;
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) {
ctx->done = TRUE;
return n;
}
}
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) {
ctx->done = TRUE;
return n;
}
} }
} }
} }
@ -493,24 +537,40 @@ static guint parse_rle_data_grayscale(TGAContext *ctx)
tag = *s; tag = *s;
s++, n++; s++, n++;
if (tag & 0x80) { if (tag & 0x80) {
if (n == ctx->in->size) { if (n + (ctx->pbuf->n_channels == 4 ? 2 : 1) >= ctx->in->size) {
return --n; return --n;
} else { } else {
rle_num = (tag & 0x7f) + 1; rle_num = (tag & 0x7f) + 1;
tone.r = tone.g = tone.b = *s; tone.r = tone.g = tone.b = *s;
s++, n++; s++, n++;
if (ctx->pbuf->n_channels == 4) {
tone.a = *s++;
n++;
}
write_rle_data(ctx, &tone, &rle_num); write_rle_data(ctx, &tone, &rle_num);
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) {
ctx->done = TRUE;
return n;
}
} }
} else { } else {
raw_num = tag + 1; raw_num = tag + 1;
if (n + raw_num >= ctx->in->size) { if (n + raw_num * (ctx->pbuf->n_channels == 4 ? 2 : 1) >= ctx->in->size) {
return --n; return --n;
} else { } else {
for (; raw_num; raw_num--) { for (; raw_num; raw_num--) {
ctx->pptr[0] = ctx->pptr[1] = ctx->pptr[2] = *s; ctx->pptr[0] = ctx->pptr[1] = ctx->pptr[2] = *s;
s++, n++; s++, n++;
ctx->pptr += 3; if (ctx->pbuf->n_channels == 4) {
ctx->pbuf_bytes_done += 3; ctx->pptr[3] = *s++;
n++;
}
ctx->pptr += ctx->pbuf->n_channels;
ctx->pbuf_bytes_done += ctx->pbuf->n_channels;
if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) {
ctx->done = TRUE;
return n;
}
} }
} }
} }
@ -526,13 +586,13 @@ static gboolean parse_rle_data(TGAContext *ctx, GError **err)
guint pbuf_count = 0; guint pbuf_count = 0;
if (ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR) { if (ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR) {
count = parse_rle_data_pseudocolor(ctx); count = parse_rle_data_pseudocolor(ctx);
pbuf_count = count *ctx->pbuf->n_channels; pbuf_count = count * ctx->pbuf->n_channels;
} else if (ctx->hdr->type == TGA_TYPE_RLE_TRUECOLOR) { } else if (ctx->hdr->type == TGA_TYPE_RLE_TRUECOLOR) {
count = parse_rle_data_truecolor(ctx); count = parse_rle_data_truecolor(ctx);
pbuf_count = count; pbuf_count = count;
} else if (ctx->hdr->type == TGA_TYPE_RLE_GRAYSCALE) { } else if (ctx->hdr->type == TGA_TYPE_RLE_GRAYSCALE) {
count = parse_rle_data_grayscale(ctx); count = parse_rle_data_grayscale(ctx);
pbuf_count = count * 3; pbuf_count = count * (ctx->pbuf->n_channels == 4 ? 2 : 3);
} }
ctx->in = io_buffer_free_segment(ctx->in, count, err); ctx->in = io_buffer_free_segment(ctx->in, count, err);
if (!ctx->in) if (!ctx->in)
@ -609,6 +669,28 @@ static gboolean try_preload(TGAContext *ctx, GError **err)
} }
g_memmove(ctx->hdr, ctx->in->data, sizeof(TGAHeader)); g_memmove(ctx->hdr, ctx->in->data, sizeof(TGAHeader));
ctx->in = io_buffer_free_segment(ctx->in, sizeof(TGAHeader), err); ctx->in = io_buffer_free_segment(ctx->in, sizeof(TGAHeader), err);
#ifdef DEBUG_TGA
g_print ("infolen %d "
"has_cmap %d "
"type %d "
"cmap_start %d "
"cmap_n_colors %d "
"cmap_bpp %d "
"x %d y %d width %d height %d bpp %d "
"flags %#x",
ctx->hdr->infolen,
ctx->hdr->has_cmap,
ctx->hdr->type,
LE16(ctx->hdr->cmap_start),
LE16(ctx->hdr->cmap_n_colors),
ctx->hdr->cmap_bpp,
LE16(ctx->hdr->x_origin),
LE16(ctx->hdr->y_origin),
LE16(ctx->hdr->width),
LE16(ctx->hdr->height),
ctx->hdr->bpp,
ctx->hdr->flags);
#endif
if (!ctx->in) if (!ctx->in)
return FALSE; return FALSE;
if (LE16(ctx->hdr->width) == 0 || if (LE16(ctx->hdr->width) == 0 ||
@ -635,17 +717,39 @@ static gboolean try_preload(TGAContext *ctx, GError **err)
} }
switch (ctx->hdr->type) { switch (ctx->hdr->type) {
case TGA_TYPE_PSEUDOCOLOR: case TGA_TYPE_PSEUDOCOLOR:
case TGA_TYPE_TRUECOLOR:
case TGA_TYPE_GRAYSCALE:
case TGA_TYPE_RLE_PSEUDOCOLOR: case TGA_TYPE_RLE_PSEUDOCOLOR:
if (ctx->hdr->bpp != 8) {
g_set_error(err, GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
_("TGA image type not supported"));
return FALSE;
}
break;
case TGA_TYPE_TRUECOLOR:
case TGA_TYPE_RLE_TRUECOLOR: case TGA_TYPE_RLE_TRUECOLOR:
if (ctx->hdr->bpp != 24 &&
ctx->hdr->bpp != 32) {
g_set_error(err, GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
_("TGA image type not supported"));
return FALSE;
}
break;
case TGA_TYPE_GRAYSCALE:
case TGA_TYPE_RLE_GRAYSCALE: case TGA_TYPE_RLE_GRAYSCALE:
break; if (ctx->hdr->bpp != 8 &&
ctx->hdr->bpp != 16) {
g_set_error(err, GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
_("TGA image type not supported"));
return FALSE;
}
break;
default: default:
g_set_error(err, GDK_PIXBUF_ERROR, g_set_error(err, GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_UNKNOWN_TYPE, GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
_("TGA image type not supported")); _("TGA image type not supported"));
return FALSE; return FALSE;
} }
if (!fill_in_context(ctx, err)) if (!fill_in_context(ctx, err))
return FALSE; return FALSE;
@ -894,7 +998,9 @@ static GdkPixbuf *get_image_pseudocolor(FILE *f, TGAHeader *hdr,
GdkPixbuf *pbuf; GdkPixbuf *pbuf;
guchar *p, color, tag; guchar *p, color, tag;
glong n, image_offset; glong n, image_offset;
guint count; guint count, w, h;
guchar *pixels;
gboolean alpha;
image_offset = sizeof(TGAHeader) + hdr->infolen; image_offset = sizeof(TGAHeader) + hdr->infolen;
if (!hdr->has_cmap) { if (!hdr->has_cmap) {
@ -911,8 +1017,22 @@ static GdkPixbuf *get_image_pseudocolor(FILE *f, TGAHeader *hdr,
return NULL; return NULL;
} }
pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, (hdr->cmap_bpp == 32), 8, w = LE16(hdr->width);
LE16(hdr->width), LE16(hdr->height)); h = LE16(hdr->height);
alpha = (hdr->cmap_bpp == 32);
pixels = g_try_malloc (w * h * (alpha ? 4 : 3));
if (!pixels) {
g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Insufficient memory to load TGA image"));
return FALSE;
}
pbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, alpha, 8,
w, h, w * (alpha ? 4 : 3),
free_buffer, NULL);
if (!pbuf) { if (!pbuf) {
g_set_error(err, GDK_PIXBUF_ERROR, g_set_error(err, GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
@ -998,7 +1118,9 @@ static GdkPixbuf *get_image_truecolor(FILE *f, TGAHeader *hdr,
guchar *p, tag; guchar *p, tag;
glong n, image_offset; glong n, image_offset;
guint32 pixel; guint32 pixel;
guint count; guint count, w, h;
guchar *pixels;
gboolean alpha;
image_offset = sizeof(TGAHeader) + hdr->infolen; image_offset = sizeof(TGAHeader) + hdr->infolen;
/* A truecolor image shouldn't actually have a colormap. */ /* A truecolor image shouldn't actually have a colormap. */
@ -1007,8 +1129,23 @@ static GdkPixbuf *get_image_truecolor(FILE *f, TGAHeader *hdr,
if (!fseek_check(f, image_offset, SEEK_SET, err)) if (!fseek_check(f, image_offset, SEEK_SET, err))
return NULL; return NULL;
pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, (hdr->bpp == 32), 8, w = LE16(hdr->width);
LE16(hdr->width), LE16(hdr->height)); h = LE16(hdr->height);
alpha = (hdr->bpp == 32);
pixels = g_try_malloc (w * h * (alpha ? 4 : 3));
if (!pixels) {
g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Insufficient memory to load TGA image"));
return FALSE;
}
pbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, alpha, 8,
w, h, w * (alpha ? 4 : 3),
free_buffer, NULL);
if (!pbuf) { if (!pbuf) {
g_set_error(err, GDK_PIXBUF_ERROR, g_set_error(err, GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
@ -1063,8 +1200,10 @@ static GdkPixbuf *get_image_grayscale(FILE *f, TGAHeader *hdr,
{ {
GdkPixbuf *pbuf; GdkPixbuf *pbuf;
glong n, image_offset; glong n, image_offset;
guchar *p, color, tag; guchar *p, color[2], tag;
guint count; guint count, w, h;
guchar *pixels;
gboolean alpha;
image_offset = sizeof(TGAHeader) + hdr->infolen; image_offset = sizeof(TGAHeader) + hdr->infolen;
/* A grayscale image shouldn't actually have a colormap. */ /* A grayscale image shouldn't actually have a colormap. */
@ -1073,8 +1212,23 @@ static GdkPixbuf *get_image_grayscale(FILE *f, TGAHeader *hdr,
if (!fseek_check(f, image_offset, SEEK_SET, err)) if (!fseek_check(f, image_offset, SEEK_SET, err))
return NULL; return NULL;
pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, w = LE16(hdr->width);
LE16(hdr->width), LE16(hdr->height)); h = LE16(hdr->height);
alpha = (hdr->bpp == 16);
pixels = g_try_malloc (w * h * (alpha ? 4 : 3));
if (!pixels) {
g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
_("Insufficient memory to load TGA image"));
return FALSE;
}
pbuf = gdk_pixbuf_new_from_data (pixels, GDK_COLORSPACE_RGB, alpha, 8,
w, h, w * (alpha ? 4 : 3),
free_buffer, NULL);
if (!pbuf) { if (!pbuf) {
g_set_error(err, GDK_PIXBUF_ERROR, g_set_error(err, GDK_PIXBUF_ERROR,
GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
@ -1095,35 +1249,41 @@ static GdkPixbuf *get_image_grayscale(FILE *f, TGAHeader *hdr,
if (tag & 0x80) { if (tag & 0x80) {
count = (tag & 0x7f) + 1; count = (tag & 0x7f) + 1;
n += count; n += count;
if (!fread_check(&color, 1, 1, f, err)) { if (!fread_check(color, (alpha ? 2 : 1), 1, f, err)) {
g_object_unref(pbuf); g_object_unref(pbuf);
return NULL; return NULL;
} }
for (; count; count--) { for (; count; count--) {
p[0] = p[1] = p[2] = color; p[0] = p[1] = p[2] = color[0];
p += 3; if (alpha)
p[3] = color[1];
p += pbuf->n_channels;
} }
} else { } else {
count = tag + 1; count = tag + 1;
n += count; n += count;
for (; count; count--) { for (; count; count--) {
if (!fread_check(&color, 1, 1, f, err)) { if (!fread_check(color, (alpha ? 2 : 1), 1, f, err)) {
g_object_unref(pbuf); g_object_unref(pbuf);
return NULL; return NULL;
} }
p[0] = p[1] = p[2] = color; p[0] = p[1] = p[2] = color[0];
p += 3; if (alpha)
p[3] = color[1];
p += pbuf->n_channels;
} }
} }
} }
} else { } else {
for (n = 0; n < pbuf->width * pbuf->height; n++) { for (n = 0; n < pbuf->width * pbuf->height; n++) {
if (!fread_check(&color, 1, 1, f, err)) { if (!fread_check(color, (alpha ? 2 : 1), 1, f, err)) {
g_object_unref(pbuf); g_object_unref(pbuf);
return NULL; return NULL;
} }
p[0] = p[1] = p[2] = color; p[0] = p[1] = p[2] = color[0];
p += 3; if (alpha)
p[3] = color[1];
p += pbuf->n_channels;
} }
} }