diff options
author | Matthias Clasen <matthiasc@src.gnome.org> | 2002-03-18 22:19:24 +0000 |
---|---|---|
committer | Matthias Clasen <matthiasc@src.gnome.org> | 2002-03-18 22:19:24 +0000 |
commit | 63462c6f789fe954a13a3cc17351246b6308b0f2 (patch) | |
tree | c1d3d6ca139caa45a8348aedfb8b0f9833d158f4 /gdk-pixbuf/io-tga.c | |
parent | 1e246e1f860b80636e3d14c2005803769309416e (diff) | |
download | gdk-pixbuf-63462c6f789fe954a13a3cc17351246b6308b0f2.tar.gz |
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).
Diffstat (limited to 'gdk-pixbuf/io-tga.c')
-rw-r--r-- | gdk-pixbuf/io-tga.c | 302 |
1 files changed, 231 insertions, 71 deletions
diff --git a/gdk-pixbuf/io-tga.c b/gdk-pixbuf/io-tga.c index fb1aa7561..716f56ab5 100644 --- a/gdk-pixbuf/io-tga.c +++ b/gdk-pixbuf/io-tga.c @@ -42,6 +42,8 @@ #include "gdk-pixbuf-io.h" #include "gdk-pixbuf-private.h" +#undef DEBUG_TGA + #define TGA_INTERLEAVE_MASK 0xc0 #define TGA_INTERLEAVE_NONE 0x00 #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) { gboolean alpha; + guint w, h; + guchar *pixels; 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) * 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->pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, alpha, 8, - LE16(ctx->hdr->width), - LE16(ctx->hdr->height)); + w = LE16(ctx->hdr->width); + h = 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) { g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("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->pptr = ctx->pbuf->pixels; - if ((ctx->hdr->type == TGA_TYPE_PSEUDOCOLOR) || - (ctx->hdr->type == TGA_TYPE_GRAYSCALE)) - ctx->rowstride = ctx->pbuf->width; + if (ctx->hdr->type == TGA_TYPE_PSEUDOCOLOR) + 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) ctx->rowstride = ctx->pbuf->rowstride; @@ -305,28 +323,31 @@ static void parse_data_for_row_pseudocolor(TGAContext *ctx) { guchar *s = ctx->in->data; guint upper_bound = ctx->pbuf->width; + guchar *p = ctx->pptr; for (; upper_bound; upper_bound--, s++) { - *ctx->pptr++ = ctx->cmap->cols[*s].r; - *ctx->pptr++ = ctx->cmap->cols[*s].g; - *ctx->pptr++ = ctx->cmap->cols[*s].b; + *p++ = ctx->cmap->cols[*s].r; + *p++ = ctx->cmap->cols[*s].g; + *p++ = ctx->cmap->cols[*s].b; 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) ctx->done = TRUE; } static void swap_channels(TGAContext *ctx) { - register guchar swap; - register guint count; + guchar swap; + guint count; + guchar *p = ctx->pptr; for (count = ctx->pbuf->width; count; count--) { - swap = ctx->pptr[0]; - ctx->pptr[0] = ctx->pptr[2]; - ctx->pptr[2] = swap; - ctx->pptr += ctx->pbuf->n_channels; + swap = p[0]; + p[0] = p[2]; + p[2] = swap; + 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); swap_channels(ctx); + ctx->pptr += ctx->pbuf->rowstride; ctx->pbuf_bytes_done += ctx->pbuf->rowstride; if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) ctx->done = TRUE; @@ -344,11 +366,15 @@ static void parse_data_for_row_grayscale(TGAContext *ctx) guchar *s = ctx->in->data; guint upper_bound = ctx->pbuf->width; + guchar *p = ctx->pptr; for (; upper_bound; upper_bound--) { - ctx->pptr[0] = ctx->pptr[1] = ctx->pptr[2] = *s++; - ctx->pptr += 3; + p[0] = p[1] = p[2] = *s++; + 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) 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) { - ctx->pbuf_bytes_done += ctx->pbuf->n_channels * (*rle_count); for (; *rle_count; (*rle_count)--) { g_memmove(ctx->pptr, (guchar *) color, 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; write_rle_data(ctx, &ctx->cmap->cols[*s], &rle_num); s++, n++; + if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) { + ctx->done = TRUE; + return n; + } } } else { raw_num = tag + 1; @@ -415,26 +447,20 @@ static guint parse_rle_data_pseudocolor(TGAContext *ctx) *ctx->pptr++ = ctx->cmap->cols[*s].a; s++, n++; 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; } -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) { TGAColor col; @@ -458,19 +484,37 @@ static guint parse_rle_data_truecolor(TGAContext *ctx) col.r = *s++; if (ctx->hdr->bpp == 32) col.a = *s++; - write_rle_data(ctx, &col, &rle_num); 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 { raw_num = tag + 1; if (n + (raw_num * ctx->pbuf->n_channels) >= ctx->in->size) { return --n; } else { - g_memmove(ctx->pptr, s, raw_num * ctx->pbuf->n_channels); - swap_channels_rle(ctx, raw_num); - s += raw_num * ctx->pbuf->n_channels; - n += raw_num * ctx->pbuf->n_channels; - ctx->pbuf_bytes_done += raw_num * ctx->pbuf->n_channels; + for (; raw_num; raw_num--) { + ctx->pptr[2] = *s++; + ctx->pptr[1] = *s++; + ctx->pptr[0] = *s++; + 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; s++, n++; if (tag & 0x80) { - if (n == ctx->in->size) { + if (n + (ctx->pbuf->n_channels == 4 ? 2 : 1) >= ctx->in->size) { return --n; } else { rle_num = (tag & 0x7f) + 1; tone.r = tone.g = tone.b = *s; s++, n++; + if (ctx->pbuf->n_channels == 4) { + tone.a = *s++; + n++; + } write_rle_data(ctx, &tone, &rle_num); + if (ctx->pbuf_bytes_done == ctx->pbuf_bytes) { + ctx->done = TRUE; + return n; + } } } else { 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; } else { for (; raw_num; raw_num--) { ctx->pptr[0] = ctx->pptr[1] = ctx->pptr[2] = *s; s++, n++; - ctx->pptr += 3; - ctx->pbuf_bytes_done += 3; + if (ctx->pbuf->n_channels == 4) { + 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; if (ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR) { 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) { count = parse_rle_data_truecolor(ctx); pbuf_count = count; } else if (ctx->hdr->type == TGA_TYPE_RLE_GRAYSCALE) { 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); if (!ctx->in) @@ -609,6 +669,28 @@ static gboolean try_preload(TGAContext *ctx, GError **err) } g_memmove(ctx->hdr, ctx->in->data, sizeof(TGAHeader)); 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) return FALSE; if (LE16(ctx->hdr->width) == 0 || @@ -635,17 +717,39 @@ static gboolean try_preload(TGAContext *ctx, GError **err) } switch (ctx->hdr->type) { case TGA_TYPE_PSEUDOCOLOR: - case TGA_TYPE_TRUECOLOR: - case TGA_TYPE_GRAYSCALE: 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: + 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: - 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: - g_set_error(err, GDK_PIXBUF_ERROR, - GDK_PIXBUF_ERROR_UNKNOWN_TYPE, - _("TGA image type not supported")); - return FALSE; + g_set_error(err, GDK_PIXBUF_ERROR, + GDK_PIXBUF_ERROR_UNKNOWN_TYPE, + _("TGA image type not supported")); + return FALSE; } if (!fill_in_context(ctx, err)) return FALSE; @@ -894,7 +998,9 @@ static GdkPixbuf *get_image_pseudocolor(FILE *f, TGAHeader *hdr, GdkPixbuf *pbuf; guchar *p, color, tag; glong n, image_offset; - guint count; + guint count, w, h; + guchar *pixels; + gboolean alpha; image_offset = sizeof(TGAHeader) + hdr->infolen; if (!hdr->has_cmap) { @@ -911,8 +1017,22 @@ static GdkPixbuf *get_image_pseudocolor(FILE *f, TGAHeader *hdr, return NULL; } - pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, (hdr->cmap_bpp == 32), 8, - LE16(hdr->width), LE16(hdr->height)); + w = LE16(hdr->width); + 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) { g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, @@ -998,7 +1118,9 @@ static GdkPixbuf *get_image_truecolor(FILE *f, TGAHeader *hdr, guchar *p, tag; glong n, image_offset; guint32 pixel; - guint count; + guint count, w, h; + guchar *pixels; + gboolean alpha; image_offset = sizeof(TGAHeader) + hdr->infolen; /* 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)) return NULL; - pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, (hdr->bpp == 32), 8, - LE16(hdr->width), LE16(hdr->height)); + w = LE16(hdr->width); + 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) { g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, @@ -1063,8 +1200,10 @@ static GdkPixbuf *get_image_grayscale(FILE *f, TGAHeader *hdr, { GdkPixbuf *pbuf; glong n, image_offset; - guchar *p, color, tag; - guint count; + guchar *p, color[2], tag; + guint count, w, h; + guchar *pixels; + gboolean alpha; image_offset = sizeof(TGAHeader) + hdr->infolen; /* 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)) return NULL; - pbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, - LE16(hdr->width), LE16(hdr->height)); + w = LE16(hdr->width); + 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) { g_set_error(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, @@ -1095,35 +1249,41 @@ static GdkPixbuf *get_image_grayscale(FILE *f, TGAHeader *hdr, if (tag & 0x80) { count = (tag & 0x7f) + 1; n += count; - if (!fread_check(&color, 1, 1, f, err)) { + if (!fread_check(color, (alpha ? 2 : 1), 1, f, err)) { g_object_unref(pbuf); return NULL; } for (; count; count--) { - p[0] = p[1] = p[2] = color; - p += 3; + p[0] = p[1] = p[2] = color[0]; + if (alpha) + p[3] = color[1]; + p += pbuf->n_channels; } } else { count = tag + 1; n += 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); return NULL; } - p[0] = p[1] = p[2] = color; - p += 3; + p[0] = p[1] = p[2] = color[0]; + if (alpha) + p[3] = color[1]; + p += pbuf->n_channels; } } } } else { 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); return NULL; } - p[0] = p[1] = p[2] = color; - p += 3; + p[0] = p[1] = p[2] = color[0]; + if (alpha) + p[3] = color[1]; + p += pbuf->n_channels; } } |