Blame gdk-pixbuf/io-tga.c

Packit a4058c
/* -*- mode: C; c-file-style: "linux" -*- */
Packit a4058c
/* 
Packit a4058c
 * GdkPixbuf library - TGA image loader
Packit a4058c
 * Copyright (C) 1999 Nicola Girardi <nikke@swlibero.org>
Packit a4058c
 *
Packit a4058c
 * This library is free software; you can redistribute it and/or
Packit a4058c
 * modify it under the terms of the GNU Library General Public
Packit a4058c
 * License as published by the Free Software Foundation; either
Packit a4058c
 * version 2 of the License, or (at your option) any later version.
Packit a4058c
 *
Packit a4058c
 * This library is distributed in the hope that it will be useful,
Packit a4058c
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit a4058c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit a4058c
 * Library General Public License for more details.
Packit a4058c
 *
Packit a4058c
 * You should have received a copy of the GNU Library General Public
Packit a4058c
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit a4058c
 *
Packit a4058c
 */
Packit a4058c
Packit a4058c
/*
Packit a4058c
 * Some NOTES about the TGA loader (2001/06/07, nikke@swlibero.org)
Packit a4058c
 *
Packit a4058c
 * - The TGAFooter isn't present in all TGA files.  In fact, there's an older
Packit a4058c
 *   format specification, still in use, which doesn't cover the TGAFooter.
Packit a4058c
 *   Actually, most TGA files I have are of the older type.  Anyway I put the 
Packit a4058c
 *   struct declaration here for completeness.
Packit a4058c
 *
Packit a4058c
 * - Error handling was designed to be very paranoid.
Packit a4058c
 */
Packit a4058c
Packit a4058c
#include "config.h"
Packit a4058c
#include <stdio.h>
Packit a4058c
#include <string.h>
Packit a4058c
Packit a4058c
#include "gdk-pixbuf-private.h"
Packit a4058c
#include "gdk-pixbuf-buffer-queue-private.h"
Packit a4058c
Packit a4058c
#undef DEBUG_TGA
Packit a4058c
Packit a4058c
#define TGA_INTERLEAVE_MASK     0xc0
Packit a4058c
#define TGA_INTERLEAVE_NONE     0x00
Packit a4058c
#define TGA_INTERLEAVE_2WAY     0x40
Packit a4058c
#define TGA_INTERLEAVE_4WAY     0x80
Packit a4058c
Packit a4058c
#define TGA_ORIGIN_MASK         0x30
Packit a4058c
#define TGA_ORIGIN_RIGHT        0x10
Packit a4058c
#define TGA_ORIGIN_UPPER        0x20
Packit a4058c
Packit a4058c
enum {
Packit a4058c
	TGA_TYPE_NODATA = 0,
Packit a4058c
	TGA_TYPE_PSEUDOCOLOR = 1,
Packit a4058c
	TGA_TYPE_TRUECOLOR = 2,
Packit a4058c
	TGA_TYPE_GRAYSCALE = 3,
Packit a4058c
	TGA_TYPE_RLE_PSEUDOCOLOR = 9,
Packit a4058c
	TGA_TYPE_RLE_TRUECOLOR = 10,
Packit a4058c
	TGA_TYPE_RLE_GRAYSCALE = 11
Packit a4058c
};
Packit a4058c
Packit a4058c
#define LE16(p) ((p)[0] + ((p)[1] << 8))
Packit a4058c
Packit a4058c
typedef struct _TGAHeader TGAHeader;
Packit a4058c
typedef struct _TGAFooter TGAFooter;
Packit a4058c
Packit a4058c
typedef struct _TGAColor TGAColor;
Packit a4058c
typedef struct _TGAColormap TGAColormap;
Packit a4058c
Packit a4058c
typedef struct _TGAContext TGAContext;
Packit a4058c
Packit a4058c
struct _TGAHeader {
Packit a4058c
	guint8 infolen;
Packit a4058c
	guint8 has_cmap;
Packit a4058c
	guint8 type;
Packit a4058c
	
Packit a4058c
	guint8 cmap_start[2];
Packit a4058c
	guint8 cmap_n_colors[2];
Packit a4058c
	guint8 cmap_bpp;
Packit a4058c
	
Packit a4058c
	guint8 x_origin[2];
Packit a4058c
	guint8 y_origin[2];
Packit a4058c
	
Packit a4058c
	guint8 width[2];
Packit a4058c
	guint8 height[2];
Packit a4058c
	guint8 bpp;
Packit a4058c
	
Packit a4058c
	guint8 flags;
Packit a4058c
};
Packit a4058c
Packit a4058c
struct _TGAFooter {
Packit a4058c
	guint32 extension_area_offset;
Packit a4058c
	guint32 developer_directory_offset;
Packit a4058c
Packit a4058c
	/* Standard TGA signature, "TRUEVISION-XFILE.\0". */
Packit a4058c
	union {
Packit a4058c
		gchar sig_full[18];
Packit a4058c
		struct {
Packit a4058c
			gchar sig_chunk[16];
Packit a4058c
			gchar dot, null;
Packit a4058c
		} sig_struct;
Packit a4058c
	} sig;
Packit a4058c
};
Packit a4058c
Packit a4058c
struct _TGAColor {
Packit a4058c
	guchar r, g, b, a;
Packit a4058c
};
Packit a4058c
Packit a4058c
struct _TGAColormap {
Packit a4058c
	guint n_colors;
Packit a4058c
	TGAColor colors[1];
Packit a4058c
};
Packit a4058c
Packit a4058c
typedef gboolean (* TGAProcessFunc) (TGAContext *ctx, GError **error);
Packit a4058c
Packit a4058c
struct _TGAContext {
Packit a4058c
	TGAHeader *hdr;
Packit a4058c
Packit a4058c
	TGAColormap *cmap;
Packit a4058c
	guint cmap_size;
Packit a4058c
Packit a4058c
	GdkPixbuf *pbuf;
Packit a4058c
	int pbuf_x;
Packit a4058c
	int pbuf_y;
Packit a4058c
	int pbuf_y_notified;
Packit a4058c
Packit a4058c
	GdkPixbufBufferQueue *input;
Packit a4058c
Packit a4058c
        TGAProcessFunc process;
Packit a4058c
Packit a4058c
	GdkPixbufModuleSizeFunc sfunc;
Packit a4058c
	GdkPixbufModulePreparedFunc pfunc;
Packit a4058c
	GdkPixbufModuleUpdatedFunc ufunc;
Packit a4058c
	gpointer udata;
Packit a4058c
};
Packit a4058c
Packit a4058c
static TGAColormap *
Packit a4058c
colormap_new (guint n_colors)
Packit a4058c
{
Packit a4058c
  TGAColormap *cmap;
Packit a4058c
Packit a4058c
  g_assert (n_colors <= G_MAXUINT16);
Packit a4058c
Packit a4058c
  cmap = g_try_malloc0 (sizeof (TGAColormap) + (MAX (n_colors, 1) - 1) * sizeof (TGAColor));
Packit a4058c
  if (cmap == NULL)
Packit a4058c
    return NULL;
Packit a4058c
Packit a4058c
  cmap->n_colors = n_colors;
Packit a4058c
Packit a4058c
  return cmap;
Packit a4058c
}
Packit a4058c
Packit a4058c
static const TGAColor *
Packit a4058c
colormap_get_color (TGAColormap *cmap,
Packit a4058c
                    guint        id)
Packit a4058c
{
Packit a4058c
  static const TGAColor transparent_black = { 0, 0, 0, 0 };
Packit a4058c
Packit a4058c
  if (id >= cmap->n_colors)
Packit a4058c
    return &transparent_black;
Packit a4058c
Packit a4058c
  return &cmap->colors[id];
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
colormap_set_color (TGAColormap    *cmap,
Packit a4058c
                    guint           id,
Packit a4058c
                    const TGAColor *color)
Packit a4058c
{
Packit a4058c
  if (id >= cmap->n_colors)
Packit a4058c
    return;
Packit a4058c
Packit a4058c
  cmap->colors[id] = *color;
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
colormap_free (TGAColormap *cmap)
Packit a4058c
{
Packit a4058c
  g_free (cmap);
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
tga_skip_rest_of_image (TGAContext  *ctx,
Packit a4058c
                        GError     **err)
Packit a4058c
{
Packit a4058c
  gdk_pixbuf_buffer_queue_flush (ctx->input, gdk_pixbuf_buffer_queue_get_size (ctx->input));
Packit a4058c
Packit a4058c
  return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static inline void
Packit a4058c
tga_write_pixel (TGAContext     *ctx,
Packit a4058c
                 const TGAColor *color)
Packit a4058c
{
Packit a4058c
  guint x = (ctx->hdr->flags & TGA_ORIGIN_RIGHT) ? ctx->pbuf->width - ctx->pbuf_x - 1 : ctx->pbuf_x;
Packit a4058c
  guint y = (ctx->hdr->flags & TGA_ORIGIN_UPPER) ? ctx->pbuf_y : ctx->pbuf->height - ctx->pbuf_y - 1;
Packit a4058c
Packit a4058c
  memcpy (ctx->pbuf->pixels + y * ctx->pbuf->rowstride + x * ctx->pbuf->n_channels, color, ctx->pbuf->n_channels);
Packit a4058c
Packit a4058c
  ctx->pbuf_x++;
Packit a4058c
  if (ctx->pbuf_x >= ctx->pbuf->width)
Packit a4058c
    {
Packit a4058c
      ctx->pbuf_x = 0;
Packit a4058c
      ctx->pbuf_y++;
Packit a4058c
    }
Packit a4058c
}
Packit a4058c
Packit a4058c
static gsize
Packit a4058c
tga_pixels_remaining (TGAContext *ctx)
Packit a4058c
{
Packit a4058c
  return ctx->pbuf->width * (ctx->pbuf->height - ctx->pbuf_y) - ctx->pbuf_x;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
tga_all_pixels_written (TGAContext *ctx)
Packit a4058c
{
Packit a4058c
  return ctx->pbuf_y >= ctx->pbuf->height;
Packit a4058c
}
Packit a4058c
Packit a4058c
static void
Packit a4058c
tga_emit_update (TGAContext *ctx)
Packit a4058c
{
Packit a4058c
  if (!ctx->ufunc)
Packit a4058c
    return;
Packit a4058c
Packit a4058c
  /* We only notify row-by-row for now.
Packit a4058c
   * I was too lazy to handle line-breaks.
Packit a4058c
   */
Packit a4058c
  if (ctx->pbuf_y_notified == ctx->pbuf_y)
Packit a4058c
    return;
Packit a4058c
Packit a4058c
  if (ctx->hdr->flags & TGA_ORIGIN_UPPER)
Packit a4058c
    (*ctx->ufunc) (ctx->pbuf,
Packit a4058c
                   0, ctx->pbuf_y_notified,
Packit a4058c
                   ctx->pbuf->width, ctx->pbuf_y - ctx->pbuf_y_notified,
Packit a4058c
                   ctx->udata);
Packit a4058c
  else
Packit a4058c
    (*ctx->ufunc) (ctx->pbuf,
Packit a4058c
                   0, ctx->pbuf->height - ctx->pbuf_y,
Packit a4058c
                   ctx->pbuf->width, ctx->pbuf_y - ctx->pbuf_y_notified,
Packit a4058c
                   ctx->udata);
Packit a4058c
Packit a4058c
  ctx->pbuf_y_notified = ctx->pbuf_y;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
tga_format_supported (guint type,
Packit a4058c
                      guint bits_per_pixel)
Packit a4058c
{
Packit a4058c
  switch (type)
Packit a4058c
    {
Packit a4058c
      case TGA_TYPE_PSEUDOCOLOR:
Packit a4058c
      case TGA_TYPE_RLE_PSEUDOCOLOR:
Packit a4058c
        return bits_per_pixel == 8;
Packit a4058c
Packit a4058c
      case TGA_TYPE_TRUECOLOR:
Packit a4058c
      case TGA_TYPE_RLE_TRUECOLOR:
Packit a4058c
        return bits_per_pixel == 16
Packit a4058c
            || bits_per_pixel == 24
Packit a4058c
            || bits_per_pixel == 32;
Packit a4058c
Packit a4058c
      case TGA_TYPE_GRAYSCALE:
Packit a4058c
      case TGA_TYPE_RLE_GRAYSCALE:
Packit a4058c
        return bits_per_pixel == 8
Packit a4058c
            || bits_per_pixel == 16;
Packit a4058c
Packit a4058c
      default:
Packit a4058c
        return FALSE;
Packit a4058c
    }
Packit a4058c
}
Packit a4058c
Packit a4058c
static inline void
Packit a4058c
tga_read_pixel (TGAContext   *ctx,
Packit a4058c
                const guchar *data,
Packit a4058c
                TGAColor     *color)
Packit a4058c
{
Packit a4058c
  switch (ctx->hdr->type)
Packit a4058c
    {
Packit a4058c
      case TGA_TYPE_PSEUDOCOLOR:
Packit a4058c
      case TGA_TYPE_RLE_PSEUDOCOLOR:
Packit a4058c
        *color = *colormap_get_color (ctx->cmap, data[0]);
Packit a4058c
        break;
Packit a4058c
Packit a4058c
      case TGA_TYPE_TRUECOLOR:
Packit a4058c
      case TGA_TYPE_RLE_TRUECOLOR:
Packit a4058c
        if (ctx->hdr->bpp == 16)
Packit a4058c
          {
Packit a4058c
            guint16 col = data[0] + (data[1] << 8);
Packit a4058c
            color->r = (col >> 7) & 0xf8;
Packit a4058c
            color->r |= color->r >> 5;
Packit a4058c
            color->g = (col >> 2) & 0xf8;
Packit a4058c
            color->g |= color->g >> 5;
Packit a4058c
            color->b = col << 3;
Packit a4058c
            color->b |= color->b >> 5;
Packit a4058c
            color->a = 255;
Packit a4058c
          }
Packit a4058c
        else
Packit a4058c
          {
Packit a4058c
            color->b = data[0];
Packit a4058c
            color->g = data[1];
Packit a4058c
            color->r = data[2];
Packit a4058c
            if (ctx->hdr->bpp == 32)
Packit a4058c
              color->a = data[3];
Packit a4058c
            else
Packit a4058c
              color->a = 255;
Packit a4058c
          }
Packit a4058c
        break;
Packit a4058c
Packit a4058c
      case TGA_TYPE_GRAYSCALE:
Packit a4058c
      case TGA_TYPE_RLE_GRAYSCALE:
Packit a4058c
        color->r = color->g = color->b = data[0];
Packit a4058c
        if (ctx->hdr->bpp == 16)
Packit a4058c
          color->a = data[1];
Packit a4058c
        else
Packit a4058c
          color->a = 255;
Packit a4058c
        break;
Packit a4058c
Packit a4058c
      default:
Packit a4058c
        g_assert_not_reached ();
Packit a4058c
    }
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean fill_in_context(TGAContext *ctx, GError **err)
Packit a4058c
{
Packit a4058c
	gboolean alpha;
Packit a4058c
	guint w, h;
Packit a4058c
Packit a4058c
	g_return_val_if_fail(ctx != NULL, FALSE);
Packit a4058c
Packit a4058c
        ctx->cmap_size = ((ctx->hdr->cmap_bpp + 7) >> 3) *
Packit a4058c
                LE16(ctx->hdr->cmap_n_colors);
Packit a4058c
        ctx->cmap = colormap_new (LE16(ctx->hdr->cmap_n_colors));
Packit a4058c
	if (!ctx->cmap) {
Packit a4058c
		g_set_error_literal(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                    _("Cannot allocate colormap"));
Packit a4058c
		return FALSE;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	alpha = ((ctx->hdr->bpp == 16) || 
Packit a4058c
		 (ctx->hdr->bpp == 32) ||
Packit a4058c
		 (ctx->hdr->has_cmap && (ctx->hdr->cmap_bpp == 32)));
Packit a4058c
Packit a4058c
	w = LE16(ctx->hdr->width);
Packit a4058c
	h = LE16(ctx->hdr->height);
Packit a4058c
Packit a4058c
	if (ctx->sfunc) {
Packit a4058c
		gint wi = w;
Packit a4058c
		gint hi = h;
Packit a4058c
		
Packit a4058c
		(*ctx->sfunc) (&wi, &hi, ctx->udata);
Packit a4058c
		
Packit a4058c
		if (wi == 0 || hi == 0) 
Packit a4058c
			return FALSE;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	ctx->pbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, alpha, 8, w, h);
Packit a4058c
Packit a4058c
	if (!ctx->pbuf) {
Packit a4058c
		g_set_error_literal(err, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                    _("Cannot allocate new pixbuf"));
Packit a4058c
		return FALSE;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
tga_load_image (TGAContext  *ctx,
Packit a4058c
                GError     **err)
Packit a4058c
{
Packit a4058c
  TGAColor color;
Packit a4058c
  GBytes *bytes;
Packit a4058c
  gsize i, size, bytes_per_pixel;
Packit a4058c
  const guchar *data;
Packit a4058c
Packit a4058c
  bytes_per_pixel = (ctx->hdr->bpp + 7) / 8;
Packit a4058c
  size = gdk_pixbuf_buffer_queue_get_size (ctx->input) / bytes_per_pixel;
Packit a4058c
  size = MIN (size, tga_pixels_remaining (ctx));
Packit a4058c
Packit a4058c
  bytes = gdk_pixbuf_buffer_queue_pull (ctx->input, size * bytes_per_pixel);
Packit a4058c
  g_assert (bytes != NULL);
Packit a4058c
Packit a4058c
  data = g_bytes_get_data (bytes, NULL);
Packit a4058c
Packit a4058c
  for (i = 0; i < size; i++)
Packit a4058c
    {
Packit a4058c
      tga_read_pixel (ctx, data, &color;;
Packit a4058c
      tga_write_pixel (ctx, &color;;
Packit a4058c
      data += bytes_per_pixel;
Packit a4058c
    }
Packit a4058c
Packit a4058c
  g_bytes_unref (bytes);
Packit a4058c
Packit a4058c
  tga_emit_update (ctx);
Packit a4058c
  
Packit a4058c
  if (tga_all_pixels_written (ctx))
Packit a4058c
    ctx->process = tga_skip_rest_of_image;
Packit a4058c
  return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
tga_load_rle_image (TGAContext  *ctx,
Packit a4058c
                    GError     **err)
Packit a4058c
{
Packit a4058c
        GBytes *bytes;
Packit a4058c
	TGAColor color;
Packit a4058c
	guint rle_num, raw_num;
Packit a4058c
	const guchar *s;
Packit a4058c
        guchar tag;
Packit a4058c
	gsize n, size, bytes_per_pixel;
Packit a4058c
Packit a4058c
        bytes_per_pixel = (ctx->hdr->bpp + 7) / 8;
Packit a4058c
        bytes = gdk_pixbuf_buffer_queue_peek (ctx->input, gdk_pixbuf_buffer_queue_get_size (ctx->input));
Packit a4058c
	s = g_bytes_get_data (bytes, &size);
Packit a4058c
Packit a4058c
	for (n = 0; n < size; ) {
Packit a4058c
		tag = *s;
Packit a4058c
		s++, n++;
Packit a4058c
		if (tag & 0x80) {
Packit a4058c
			if (n + bytes_per_pixel > size) {
Packit a4058c
				--n;
Packit a4058c
                                break;
Packit a4058c
			} else {
Packit a4058c
				rle_num = (tag & 0x7f) + 1;
Packit a4058c
                                tga_read_pixel (ctx, s, &color;;
Packit a4058c
				s += bytes_per_pixel;
Packit a4058c
				n += bytes_per_pixel;
Packit a4058c
                                rle_num = MIN (rle_num, tga_pixels_remaining (ctx));
Packit a4058c
                                for (; rle_num; rle_num--)
Packit a4058c
                                  {
Packit a4058c
                                    tga_write_pixel (ctx, &color;;
Packit a4058c
                                  }
Packit a4058c
	                        if (tga_all_pixels_written (ctx))
Packit a4058c
                                        break;
Packit a4058c
			}
Packit a4058c
		} else {
Packit a4058c
			raw_num = tag + 1;
Packit a4058c
			if (n + (raw_num * bytes_per_pixel) > size) {
Packit a4058c
			        --n;
Packit a4058c
                                break;
Packit a4058c
			} else {
Packit a4058c
                                raw_num = MIN (raw_num, tga_pixels_remaining (ctx));
Packit a4058c
				for (; raw_num; raw_num--) {
Packit a4058c
                                        tga_read_pixel (ctx, s, &color;;
Packit a4058c
					s += bytes_per_pixel;
Packit a4058c
					n += bytes_per_pixel;
Packit a4058c
                                        tga_write_pixel (ctx, &color;;
Packit a4058c
				}
Packit a4058c
				
Packit a4058c
	                        if (tga_all_pixels_written (ctx))
Packit a4058c
                                        break;
Packit a4058c
			}
Packit a4058c
		}
Packit a4058c
	}
Packit a4058c
Packit a4058c
        g_bytes_unref (bytes);
Packit a4058c
        gdk_pixbuf_buffer_queue_flush (ctx->input, n);
Packit a4058c
Packit a4058c
        tga_emit_update (ctx);
Packit a4058c
Packit a4058c
	if (tga_all_pixels_written (ctx))
Packit a4058c
                ctx->process = tga_skip_rest_of_image;
Packit a4058c
        return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
tga_load_colormap (TGAContext  *ctx,
Packit a4058c
                   GError     **err)
Packit a4058c
{
Packit a4058c
  GBytes *bytes;
Packit a4058c
  TGAColor color;
Packit a4058c
  const guchar *p;
Packit a4058c
  guint i, n_colors;
Packit a4058c
Packit a4058c
  if (ctx->hdr->has_cmap)
Packit a4058c
    {
Packit a4058c
      bytes = gdk_pixbuf_buffer_queue_pull (ctx->input, ctx->cmap_size);
Packit a4058c
      if (bytes == NULL)
Packit a4058c
        return TRUE;
Packit a4058c
Packit a4058c
      n_colors = LE16(ctx->hdr->cmap_n_colors);
Packit a4058c
Packit a4058c
      p = g_bytes_get_data (bytes, NULL);
Packit a4058c
      color.a = 255;
Packit a4058c
Packit a4058c
      for (i = 0; i < n_colors; i++)
Packit a4058c
        {
Packit a4058c
          if ((ctx->hdr->cmap_bpp == 15) || (ctx->hdr->cmap_bpp == 16))
Packit a4058c
            {
Packit a4058c
              guint16 col = p[0] + (p[1] << 8);
Packit a4058c
              color.b = (col >> 7) & 0xf8;
Packit a4058c
              color.g = (col >> 2) & 0xf8;
Packit a4058c
              color.r = col << 3;
Packit a4058c
              p += 2;
Packit a4058c
            }
Packit a4058c
          else if ((ctx->hdr->cmap_bpp == 24) || (ctx->hdr->cmap_bpp == 32))
Packit a4058c
            {
Packit a4058c
              color.b = *p++;
Packit a4058c
              color.g = *p++;
Packit a4058c
              color.r = *p++;
Packit a4058c
              if (ctx->hdr->cmap_bpp == 32)
Packit a4058c
                color.a = *p++;
Packit a4058c
            }
Packit a4058c
          else
Packit a4058c
            {
Packit a4058c
              g_set_error_literal (err, GDK_PIXBUF_ERROR, 
Packit a4058c
                                   GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                   _("Unexpected bitdepth for colormap entries"));
Packit a4058c
              g_bytes_unref (bytes);
Packit a4058c
              return FALSE;
Packit a4058c
            }
Packit a4058c
          colormap_set_color (ctx->cmap, i, &color;;
Packit a4058c
        }
Packit a4058c
Packit a4058c
      g_bytes_unref (bytes);
Packit a4058c
    }
Packit a4058c
  else
Packit a4058c
    {
Packit a4058c
      if ((ctx->hdr->type == TGA_TYPE_PSEUDOCOLOR)
Packit a4058c
          || (ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR))
Packit a4058c
        {
Packit a4058c
          g_set_error_literal (err, GDK_PIXBUF_ERROR, 
Packit a4058c
                               GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                               _("Pseudocolor image does not contain a colormap"));
Packit a4058c
          return FALSE;
Packit a4058c
        }
Packit a4058c
    }
Packit a4058c
  
Packit a4058c
  if ((ctx->hdr->type == TGA_TYPE_RLE_PSEUDOCOLOR)
Packit a4058c
      || (ctx->hdr->type == TGA_TYPE_RLE_TRUECOLOR)
Packit a4058c
      || (ctx->hdr->type == TGA_TYPE_RLE_GRAYSCALE))
Packit a4058c
    ctx->process = tga_load_rle_image;
Packit a4058c
  else
Packit a4058c
    ctx->process = tga_load_image;
Packit a4058c
Packit a4058c
  return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
tga_read_info (TGAContext  *ctx,
Packit a4058c
               GError     **err)
Packit a4058c
{
Packit a4058c
  if (gdk_pixbuf_buffer_queue_get_size (ctx->input) < ctx->hdr->infolen)
Packit a4058c
    return TRUE;
Packit a4058c
  
Packit a4058c
  gdk_pixbuf_buffer_queue_flush (ctx->input, ctx->hdr->infolen);
Packit a4058c
Packit a4058c
  ctx->process = tga_load_colormap;
Packit a4058c
  return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean
Packit a4058c
tga_load_header (TGAContext  *ctx,
Packit a4058c
                 GError     **err)
Packit a4058c
{
Packit a4058c
  GBytes *bytes;
Packit a4058c
  
Packit a4058c
  bytes = gdk_pixbuf_buffer_queue_pull (ctx->input, sizeof (TGAHeader));
Packit a4058c
  if (bytes == NULL)
Packit a4058c
    return TRUE;
Packit a4058c
Packit a4058c
  ctx->hdr = g_try_malloc (sizeof (TGAHeader));
Packit a4058c
  if (!ctx->hdr)
Packit a4058c
    {
Packit a4058c
      g_set_error_literal (err, GDK_PIXBUF_ERROR,
Packit a4058c
                           GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                           _("Cannot allocate TGA header memory"));
Packit a4058c
      return FALSE;
Packit a4058c
  }
Packit a4058c
  g_memmove(ctx->hdr, g_bytes_get_data (bytes, NULL), sizeof(TGAHeader));
Packit a4058c
  g_bytes_unref (bytes);
Packit a4058c
#ifdef DEBUG_TGA
Packit a4058c
  g_print ("infolen %d "
Packit a4058c
           "has_cmap %d "
Packit a4058c
           "type %d "
Packit a4058c
           "cmap_start %d "
Packit a4058c
           "cmap_n_colors %d "
Packit a4058c
           "cmap_bpp %d "
Packit a4058c
           "x %d y %d width %d height %d bpp %d "
Packit a4058c
           "flags %#x",
Packit a4058c
           ctx->hdr->infolen,
Packit a4058c
           ctx->hdr->has_cmap,
Packit a4058c
           ctx->hdr->type,
Packit a4058c
           LE16(ctx->hdr->cmap_start),
Packit a4058c
           LE16(ctx->hdr->cmap_n_colors),
Packit a4058c
           ctx->hdr->cmap_bpp,
Packit a4058c
           LE16(ctx->hdr->x_origin),
Packit a4058c
           LE16(ctx->hdr->y_origin),
Packit a4058c
           LE16(ctx->hdr->width),
Packit a4058c
           LE16(ctx->hdr->height),
Packit a4058c
           ctx->hdr->bpp,
Packit a4058c
           ctx->hdr->flags);
Packit a4058c
#endif
Packit a4058c
  if (LE16(ctx->hdr->width) == 0 || 
Packit a4058c
      LE16(ctx->hdr->height) == 0) {
Packit a4058c
          g_set_error_literal(err, GDK_PIXBUF_ERROR,
Packit a4058c
                              GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                              _("TGA image has invalid dimensions"));
Packit a4058c
          return FALSE;
Packit a4058c
  }
Packit a4058c
  if ((ctx->hdr->flags & TGA_INTERLEAVE_MASK) != TGA_INTERLEAVE_NONE) {
Packit a4058c
          g_set_error_literal(err, GDK_PIXBUF_ERROR, 
Packit a4058c
                              GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
Packit a4058c
                              _("TGA image type not supported"));
Packit a4058c
          return FALSE;
Packit a4058c
  }
Packit a4058c
  if (!tga_format_supported (ctx->hdr->type, ctx->hdr->bpp))
Packit a4058c
    {
Packit a4058c
      g_set_error_literal(err, GDK_PIXBUF_ERROR,
Packit a4058c
                          GDK_PIXBUF_ERROR_UNKNOWN_TYPE,
Packit a4058c
                          _("TGA image type not supported"));
Packit a4058c
      return FALSE;
Packit a4058c
    }
Packit a4058c
Packit a4058c
  if (!fill_in_context(ctx, err))
Packit a4058c
          return FALSE;
Packit a4058c
Packit a4058c
  if (ctx->pfunc)
Packit a4058c
          (*ctx->pfunc) (ctx->pbuf, NULL, ctx->udata);
Packit a4058c
Packit a4058c
  ctx->process = tga_read_info;
Packit a4058c
  return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gpointer gdk_pixbuf__tga_begin_load(GdkPixbufModuleSizeFunc f0,
Packit a4058c
                                           GdkPixbufModulePreparedFunc f1,
Packit a4058c
					   GdkPixbufModuleUpdatedFunc f2,
Packit a4058c
					   gpointer udata, GError **err)
Packit a4058c
{
Packit a4058c
	TGAContext *ctx;
Packit a4058c
Packit a4058c
	ctx = g_try_malloc(sizeof(TGAContext));
Packit a4058c
	if (!ctx) {
Packit a4058c
		g_set_error_literal(err, GDK_PIXBUF_ERROR, 
Packit a4058c
                                    GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
Packit a4058c
                                    _("Cannot allocate memory for TGA context struct"));
Packit a4058c
		return NULL;
Packit a4058c
	}
Packit a4058c
Packit a4058c
	ctx->hdr = NULL;
Packit a4058c
Packit a4058c
	ctx->cmap = NULL;
Packit a4058c
	ctx->cmap_size = 0;
Packit a4058c
Packit a4058c
	ctx->pbuf = NULL;
Packit a4058c
        ctx->pbuf_x = 0;
Packit a4058c
        ctx->pbuf_y = 0;
Packit a4058c
        ctx->pbuf_y_notified = 0;
Packit a4058c
Packit a4058c
	ctx->input = gdk_pixbuf_buffer_queue_new ();
Packit a4058c
Packit a4058c
        ctx->process = tga_load_header;
Packit a4058c
Packit a4058c
	ctx->sfunc = f0;
Packit a4058c
	ctx->pfunc = f1;
Packit a4058c
	ctx->ufunc = f2;
Packit a4058c
	ctx->udata = udata;
Packit a4058c
Packit a4058c
	return ctx;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean gdk_pixbuf__tga_load_increment(gpointer data,
Packit a4058c
					       const guchar *buffer,
Packit a4058c
					       guint size,
Packit a4058c
					       GError **err)
Packit a4058c
{
Packit a4058c
	TGAContext *ctx = (TGAContext*) data;
Packit a4058c
        TGAProcessFunc process;
Packit a4058c
Packit a4058c
	g_return_val_if_fail(buffer != NULL, TRUE);
Packit a4058c
        gdk_pixbuf_buffer_queue_push (ctx->input, g_bytes_new (buffer, size));
Packit a4058c
Packit a4058c
        do
Packit a4058c
          {
Packit a4058c
            process = ctx->process;
Packit a4058c
Packit a4058c
            if (!process (ctx, err))
Packit a4058c
              return FALSE;
Packit a4058c
          }
Packit a4058c
        while (process != ctx->process);
Packit a4058c
Packit a4058c
	return TRUE;
Packit a4058c
}
Packit a4058c
Packit a4058c
static gboolean gdk_pixbuf__tga_stop_load(gpointer data, GError **err)
Packit a4058c
{
Packit a4058c
	TGAContext *ctx = (TGAContext *) data;
Packit a4058c
        gboolean result = TRUE;
Packit a4058c
Packit a4058c
	g_return_val_if_fail (ctx != NULL, FALSE);
Packit a4058c
Packit a4058c
        if (ctx->pbuf == NULL || tga_pixels_remaining (ctx))
Packit a4058c
          {
Packit a4058c
            g_set_error_literal (err,
Packit a4058c
                                 GDK_PIXBUF_ERROR,
Packit a4058c
                                 GDK_PIXBUF_ERROR_CORRUPT_IMAGE,
Packit a4058c
                                 _("TGA image was truncated or incomplete."));
Packit a4058c
Packit a4058c
            result = FALSE;
Packit a4058c
          }
Packit a4058c
Packit a4058c
	g_free (ctx->hdr);
Packit a4058c
	if (ctx->cmap)
Packit a4058c
          colormap_free (ctx->cmap);
Packit a4058c
	if (ctx->pbuf)
Packit a4058c
          g_object_unref (ctx->pbuf);
Packit a4058c
	gdk_pixbuf_buffer_queue_unref (ctx->input);
Packit a4058c
	g_free (ctx);
Packit a4058c
Packit a4058c
	return result;
Packit a4058c
}
Packit a4058c
Packit a4058c
#ifndef INCLUDE_tga
Packit a4058c
#define MODULE_ENTRY(function) G_MODULE_EXPORT void function
Packit a4058c
#else
Packit a4058c
#define MODULE_ENTRY(function) void _gdk_pixbuf__tga_ ## function
Packit a4058c
#endif
Packit a4058c
Packit a4058c
MODULE_ENTRY (fill_vtable) (GdkPixbufModule *module)
Packit a4058c
{
Packit a4058c
	module->begin_load = gdk_pixbuf__tga_begin_load;
Packit a4058c
	module->stop_load = gdk_pixbuf__tga_stop_load;
Packit a4058c
	module->load_increment = gdk_pixbuf__tga_load_increment;
Packit a4058c
}
Packit a4058c
Packit a4058c
MODULE_ENTRY (fill_info) (GdkPixbufFormat *info)
Packit a4058c
{
Packit a4058c
	static const GdkPixbufModulePattern signature[] = {
Packit a4058c
		{ " \x1\x1", "x  ", 100 },
Packit a4058c
		{ " \x1\x9", "x  ", 100 },
Packit a4058c
		{ "  \x2", "xz ",  99 }, /* only 99 since .CUR also matches this */
Packit a4058c
		{ "  \x3", "xz ", 100 },
Packit a4058c
		{ "  \xa", "xz ", 100 },
Packit a4058c
		{ "  \xb", "xz ", 100 },
Packit a4058c
		{ NULL, NULL, 0 }
Packit a4058c
	};
Packit a4058c
	static const gchar *mime_types[] = {
Packit a4058c
		"image/x-tga",
Packit a4058c
		NULL
Packit a4058c
	};
Packit a4058c
	static const gchar *extensions[] = {
Packit a4058c
		"tga",
Packit a4058c
		"targa",
Packit a4058c
		NULL
Packit a4058c
	};
Packit a4058c
Packit a4058c
	info->name = "tga";
Packit a4058c
	info->signature = (GdkPixbufModulePattern *) signature;
Packit a4058c
	info->description = NC_("image format", "Targa");
Packit a4058c
	info->mime_types = (gchar **) mime_types;
Packit a4058c
	info->extensions = (gchar **) extensions;
Packit a4058c
	info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
Packit a4058c
	info->license = "LGPL";
Packit a4058c
}