|
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 |
}
|