mirror of https://github.com/GNOME/gimp.git
1480 lines
36 KiB
C
1480 lines
36 KiB
C
/* The GIMP -- an image manipulation program
|
||
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
||
*
|
||
* $Id$
|
||
* TrueVision Targa loading and saving file filter for the Gimp.
|
||
* Targa code Copyright (C) 1997 Raphael FRANCOIS and Gordon Matzigkeit
|
||
*
|
||
* The Targa reading and writing code was written from scratch by
|
||
* Raphael FRANCOIS <fraph@ibm.net> and Gordon Matzigkeit
|
||
* <gord@gnu.ai.mit.edu> based on the TrueVision TGA File Format
|
||
* Specification, Version 2.0:
|
||
*
|
||
* <URL:ftp://ftp.truevision.com/pub/TGA.File.Format.Spec/>
|
||
*
|
||
* It does not contain any code written for other TGA file loaders.
|
||
* Not even the RLE handling. ;)
|
||
*
|
||
* This program is free software; you can redistribute it and/or modify
|
||
* it under the terms of the GNU General Public License as published by
|
||
* the Free Software Foundation; either version 2 of the License, or
|
||
* (at your option) any later version.
|
||
*
|
||
* This program is distributed in the hope that it will be useful,
|
||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
* GNU General Public License for more details.
|
||
*
|
||
* You should have received a copy of the GNU General Public License
|
||
* along with this program; if not, write to the Free Software
|
||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||
*/
|
||
|
||
/*
|
||
* Release 1.2, 1997-09-24, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
|
||
* - Bug fixes and source cleanups.
|
||
*
|
||
* Release 1.1, 1997-09-19, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
|
||
* - Preserve alpha channels. For indexed images, this can only be
|
||
* done if there is at least one free colormap entry.
|
||
*
|
||
* Release 1.0, 1997-09-06, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
|
||
* - Handle loading all image types from the 2.0 specification.
|
||
* - Fix many alignment and endianness problems.
|
||
* - Use tiles for lower memory consumption and better speed.
|
||
* - Rewrite RLE code for clarity and speed.
|
||
* - Handle saving with RLE.
|
||
*
|
||
* Release 0.9, 1997-06-18, Raphael FRANCOIS <fraph@ibm.net>:
|
||
* - Can load 24 and 32-bit Truecolor images, with and without RLE.
|
||
* - Saving currently only works without RLE.
|
||
*
|
||
*
|
||
* TODO:
|
||
* - Handle loading images that aren't 8 bits per channel.
|
||
* - Maybe handle special features in developer and extension sections
|
||
* (the `save' dialogue would give access to them).
|
||
* - The GIMP stores the indexed alpha channel as a separate byte,
|
||
* one for each pixel. The TGA file format spec requires that the
|
||
* alpha channel be stored as part of the colormap, not with each
|
||
* individual pixel. This means that we have no good way of
|
||
* saving and loading INDEXEDA images that use alpha channel values
|
||
* other than 0 and 255. Find a workaround.
|
||
*/
|
||
|
||
#define SAVE_ID_STRING "CREATOR: The GIMP's TGA Filter Version 1.2"
|
||
|
||
/* Set these for debugging. */
|
||
/* #define PROFILE 1 */
|
||
/* #define VERBOSE 1 */
|
||
|
||
#ifdef PROFILE
|
||
# include <sys/times.h>
|
||
#endif
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
#include <gtk/gtk.h>
|
||
#include "libgimp/gimp.h"
|
||
|
||
#define FALSE 0
|
||
#define TRUE 1
|
||
|
||
/* Round up a division to the nearest integer. */
|
||
#define ROUNDUP_DIVIDE(n,d) (((n) + (d - 1)) / (d))
|
||
|
||
typedef struct _TgaSaveVals
|
||
{
|
||
gint rle;
|
||
} TgaSaveVals;
|
||
|
||
static TgaSaveVals tsvals =
|
||
{
|
||
1, /* rle */
|
||
};
|
||
|
||
typedef struct _TgaSaveInterface
|
||
{
|
||
unsigned char run;
|
||
} TgaSaveInterface;
|
||
|
||
static TgaSaveInterface tsint =
|
||
{
|
||
FALSE /* run */
|
||
};
|
||
|
||
struct tga_header
|
||
{
|
||
guint8 idLength;
|
||
guint8 colorMapType;
|
||
|
||
/* The image type. */
|
||
#define TGA_TYPE_MAPPED 1
|
||
#define TGA_TYPE_COLOR 2
|
||
#define TGA_TYPE_GRAY 3
|
||
#define TGA_TYPE_MAPPED_RLE 9
|
||
#define TGA_TYPE_COLOR_RLE 10
|
||
#define TGA_TYPE_GRAY_RLE 11
|
||
guint8 imageType;
|
||
|
||
/* Color Map Specification. */
|
||
/* We need to separately specify high and low bytes to avoid endianness
|
||
and alignment problems. */
|
||
guint8 colorMapIndexLo, colorMapIndexHi;
|
||
guint8 colorMapLengthLo, colorMapLengthHi;
|
||
guint8 colorMapSize;
|
||
|
||
/* Image Specification. */
|
||
guint8 xOriginLo, xOriginHi;
|
||
guint8 yOriginLo, yOriginHi;
|
||
|
||
guint8 widthLo, widthHi;
|
||
guint8 heightLo, heightHi;
|
||
|
||
guint8 bpp;
|
||
|
||
/* Image descriptor.
|
||
3-0: attribute bpp
|
||
4: left-to-right ordering
|
||
5: top-to-bottom ordering
|
||
7-6: zero
|
||
*/
|
||
#define TGA_DESC_ABITS 0x0f
|
||
#define TGA_DESC_HORIZONTAL 0x10
|
||
#define TGA_DESC_VERTICAL 0x20
|
||
guint8 descriptor;
|
||
};
|
||
|
||
static struct
|
||
{
|
||
guint32 extensionAreaOffset;
|
||
guint32 developerDirectoryOffset;
|
||
#define TGA_SIGNATURE "TRUEVISION-XFILE"
|
||
gchar signature[16];
|
||
gchar dot;
|
||
gchar null;
|
||
} tga_footer;
|
||
|
||
|
||
/* Declare some local functions.
|
||
*/
|
||
static void query (void);
|
||
static void run (char *name,
|
||
int nparams,
|
||
GParam *param,
|
||
int *nreturn_vals,
|
||
GParam **return_vals);
|
||
static gint32 load_image (char *filename);
|
||
static gint save_image (char *filename,
|
||
gint32 image_ID,
|
||
gint32 drawable_ID);
|
||
|
||
static gint save_dialog ();
|
||
|
||
static void save_close_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void save_ok_callback (GtkWidget *widget,
|
||
gpointer data);
|
||
static void save_toggle_update (GtkWidget *widget,
|
||
gpointer data);
|
||
|
||
GPlugInInfo PLUG_IN_INFO =
|
||
{
|
||
NULL, /* init_proc */
|
||
NULL, /* quit_proc */
|
||
query, /* query_proc */
|
||
run, /* run_proc */
|
||
};
|
||
|
||
|
||
MAIN ()
|
||
|
||
#ifdef VERBOSE
|
||
static int verbose = VERBOSE;
|
||
#endif
|
||
|
||
static void
|
||
query ()
|
||
{
|
||
static GParamDef load_args[] =
|
||
{
|
||
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
|
||
{ PARAM_STRING, "filename", "The name of the file to load" },
|
||
{ PARAM_STRING, "raw_filename", "The name entered" },
|
||
};
|
||
static GParamDef load_return_vals[] =
|
||
{
|
||
{ PARAM_IMAGE, "image", "Output image" },
|
||
};
|
||
static int nload_args = sizeof (load_args) / sizeof (load_args[0]);
|
||
static int nload_return_vals = sizeof (load_return_vals) / sizeof (load_return_vals[0]);
|
||
|
||
|
||
static GParamDef save_args[] =
|
||
{
|
||
{ PARAM_INT32, "run_mode", "Interactive, non-interactive" },
|
||
{ PARAM_IMAGE, "image", "Input image" },
|
||
{ PARAM_DRAWABLE, "drawable", "Drawable to save" },
|
||
{ PARAM_STRING, "filename", "The name of the file to save the image in" },
|
||
{ PARAM_STRING, "raw_filename", "The name of the file to save the image in" },
|
||
{ PARAM_INT32, "rle", "Enable RLE compression" },
|
||
} ;
|
||
static int nsave_args = sizeof (save_args) / sizeof (save_args[0]);
|
||
|
||
gimp_install_procedure ("file_tga_load",
|
||
"Loads files of Targa file format",
|
||
"FIXME: write help for tga_load",
|
||
"Raphael FRANCOIS, Gordon Matzigkeit",
|
||
"Raphael FRANCOIS, Gordon Matzigkeit",
|
||
"1997",
|
||
"<Load>/TGA",
|
||
NULL,
|
||
PROC_PLUG_IN,
|
||
nload_args, nload_return_vals,
|
||
load_args, load_return_vals);
|
||
|
||
|
||
gimp_install_procedure ("file_tga_save",
|
||
"saves files in the Targa file format",
|
||
"FIXME: write help for tga_save",
|
||
"Raphael FRANCOIS, Gordon Matzigkeit",
|
||
"Raphael FRANCOIS, Gordon Matzigkeit",
|
||
"1997",
|
||
"<Save>/TGA",
|
||
"RGB*, GRAY*, INDEXED*",
|
||
PROC_PLUG_IN,
|
||
nsave_args, 0,
|
||
save_args, NULL);
|
||
|
||
gimp_register_magic_load_handler ("file_tga_load", "tga", "",
|
||
"0&,byte,10,2&,byte,1,3&,byte,>0,3,byte,<9");
|
||
gimp_register_save_handler ("file_tga_save", "tga", "");
|
||
|
||
}
|
||
|
||
static void
|
||
run (char *name,
|
||
int nparams,
|
||
GParam *param,
|
||
int *nreturn_vals,
|
||
GParam **return_vals)
|
||
{
|
||
static GParam values[2];
|
||
GStatusType status = STATUS_SUCCESS;
|
||
GRunModeType run_mode;
|
||
gint32 image_ID;
|
||
|
||
#ifdef PROFILE
|
||
struct tms tbuf1, tbuf2;
|
||
#endif
|
||
|
||
run_mode = param[0].data.d_int32;
|
||
|
||
*nreturn_vals = 1;
|
||
*return_vals = values;
|
||
values[0].type = PARAM_STATUS;
|
||
values[0].data.d_status = STATUS_CALLING_ERROR;
|
||
|
||
#ifdef VERBOSE
|
||
if (verbose)
|
||
printf ("TGA: RUN %s\n", name);
|
||
#endif
|
||
|
||
if (strcmp (name, "file_tga_load") == 0)
|
||
{
|
||
#ifdef PROFILE
|
||
times (&tbuf1);
|
||
#endif
|
||
image_ID = load_image (param[1].data.d_string);
|
||
|
||
if (image_ID != -1)
|
||
{
|
||
*nreturn_vals = 2;
|
||
values[0].data.d_status = STATUS_SUCCESS;
|
||
values[1].type = PARAM_IMAGE;
|
||
values[1].data.d_image = image_ID;
|
||
}
|
||
else
|
||
{
|
||
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
||
}
|
||
}
|
||
else if (strcmp (name, "file_tga_save") == 0)
|
||
{
|
||
switch (run_mode)
|
||
{
|
||
case RUN_INTERACTIVE:
|
||
/* Possibly retrieve data */
|
||
gimp_get_data ("file_tga_save", &tsvals);
|
||
|
||
/* First acquire information with a dialog */
|
||
if (! save_dialog ())
|
||
return;
|
||
|
||
break;
|
||
|
||
case RUN_NONINTERACTIVE:
|
||
/* Make sure all the arguments are there! */
|
||
if (nparams != 6)
|
||
status = STATUS_CALLING_ERROR;
|
||
if (status == STATUS_SUCCESS)
|
||
{
|
||
tsvals.rle = (param[5].data.d_int32) ? TRUE : FALSE;
|
||
}
|
||
break;
|
||
|
||
case RUN_WITH_LAST_VALS:
|
||
/* Possibly retrieve data */
|
||
gimp_get_data ("file_tga_save", &tsvals);
|
||
break;
|
||
|
||
default:
|
||
break;
|
||
}
|
||
|
||
#ifdef PROFILE
|
||
times (&tbuf1);
|
||
#endif
|
||
*nreturn_vals = 1;
|
||
if (save_image (param[3].data.d_string, param[1].data.d_int32, param[2].data.d_int32))
|
||
{
|
||
/* Store psvals data */
|
||
gimp_set_data ("file_tga_save", &tsvals, sizeof (tsvals));
|
||
|
||
values[0].data.d_status = STATUS_SUCCESS;
|
||
}
|
||
else
|
||
values[0].data.d_status = STATUS_EXECUTION_ERROR;
|
||
}
|
||
|
||
#ifdef PROFILE
|
||
times (&tbuf2);
|
||
printf ("TGA: %s profile: %ld user %ld system\n", name,
|
||
(long) tbuf2.tms_utime - tbuf1.tms_utime,
|
||
(long) tbuf2.tms_stime - tbuf2.tms_stime);
|
||
#endif
|
||
}
|
||
|
||
static gint32 ReadImage (FILE *fp, struct tga_header *hdr, char *filename);
|
||
|
||
static gint32
|
||
load_image (char *filename)
|
||
{
|
||
FILE *fp;
|
||
char * name_buf;
|
||
struct tga_header hdr;
|
||
|
||
gint32 image_ID = -1;
|
||
|
||
fp = fopen (filename, "rb");
|
||
if (!fp) {
|
||
printf ("TGA: can't open \"%s\"\n", filename);
|
||
return -1;
|
||
}
|
||
|
||
name_buf = g_malloc (strlen (filename) + 11);
|
||
sprintf (name_buf, "Loading %s:", filename);
|
||
gimp_progress_init (name_buf);
|
||
g_free (name_buf);
|
||
|
||
/* Check the footer. */
|
||
if (fseek (fp, 0L - (sizeof (tga_footer)), SEEK_END)
|
||
|| fread (&tga_footer, sizeof (tga_footer), 1, fp) != 1)
|
||
{
|
||
printf ("TGA: Cannot read footer from \"%s\"\n", filename);
|
||
return -1;
|
||
}
|
||
|
||
/* Check the signature. */
|
||
#ifdef VERBOSE
|
||
if (memcmp (tga_footer.signature, TGA_SIGNATURE,
|
||
sizeof (tga_footer.signature)) == 0)
|
||
{
|
||
if (verbose)
|
||
printf ("TGA: found New TGA\n");
|
||
}
|
||
else if (verbose)
|
||
printf ("TGA: found Original TGA\n");
|
||
#endif
|
||
|
||
if (fseek (fp, 0, SEEK_SET) ||
|
||
fread (&hdr, sizeof (hdr), 1, fp) != 1) {
|
||
printf("TGA: Cannot read header from \"%s\"\n", filename);
|
||
return -1;
|
||
}
|
||
|
||
/* Skip the image ID field. */
|
||
if (hdr.idLength && fseek (fp, hdr.idLength, SEEK_CUR))
|
||
{
|
||
printf ("TGA: Cannot skip ID field in \"%s\"\n", filename);
|
||
return -1;
|
||
}
|
||
|
||
image_ID = ReadImage (fp, &hdr, filename);
|
||
fclose (fp);
|
||
return image_ID;
|
||
}
|
||
|
||
|
||
#ifdef VERBOSE
|
||
static int totbytes = 0;
|
||
#endif
|
||
|
||
static int
|
||
std_fread (guchar *buf, int datasize, int nelems, FILE *fp)
|
||
{
|
||
#ifdef VERBOSE
|
||
if (verbose > 1)
|
||
{
|
||
totbytes += nelems * datasize;
|
||
printf ("TGA: std_fread %d (total %d)\n",
|
||
nelems * datasize, totbytes);
|
||
}
|
||
#endif
|
||
|
||
return fread (buf, datasize, nelems, fp);
|
||
}
|
||
|
||
static int
|
||
std_fwrite (guchar *buf, int datasize, int nelems, FILE *fp)
|
||
{
|
||
#ifdef VERBOSE
|
||
if (verbose > 1)
|
||
{
|
||
totbytes += nelems * datasize;
|
||
printf ("TGA: std_fwrite %d (total %d)\n",
|
||
nelems * datasize, totbytes);
|
||
}
|
||
#endif
|
||
|
||
return fwrite (buf, datasize, nelems, fp);
|
||
}
|
||
|
||
#define RLE_PACKETSIZE 0x80
|
||
|
||
/* Decode a bufferful of file. */
|
||
static int
|
||
rle_fread (guchar *buf, int datasize, int nelems, FILE *fp)
|
||
{
|
||
static guchar *statebuf = 0;
|
||
static int statelen = 0;
|
||
static int laststate = 0;
|
||
|
||
int j, k;
|
||
int buflen, count, bytes;
|
||
guchar *p;
|
||
#ifdef VERBOSE
|
||
int curbytes = totbytes;
|
||
#endif
|
||
|
||
/* Scale the buffer length. */
|
||
buflen = nelems * datasize;
|
||
|
||
j = 0;
|
||
while (j < buflen)
|
||
{
|
||
if (laststate < statelen)
|
||
{
|
||
/* Copy bytes from our previously decoded buffer. */
|
||
bytes = MIN (buflen - j, statelen - laststate);
|
||
memcpy (buf + j, statebuf + laststate, bytes);
|
||
j += bytes;
|
||
laststate += bytes;
|
||
|
||
/* If we used up all of our state bytes, then reset them. */
|
||
if (laststate >= statelen)
|
||
{
|
||
laststate = 0;
|
||
statelen = 0;
|
||
}
|
||
|
||
/* If we filled the buffer, then exit the loop. */
|
||
if (j >= buflen)
|
||
break;
|
||
}
|
||
|
||
/* Decode the next packet. */
|
||
count = fgetc (fp);
|
||
if (count == EOF)
|
||
{
|
||
#ifdef VERBOSE
|
||
if (verbose)
|
||
printf ("TGA: hit EOF while looking for count\n");
|
||
#endif
|
||
return j / datasize;
|
||
}
|
||
|
||
/* Scale the byte length to the size of the data. */
|
||
bytes = ((count & ~RLE_PACKETSIZE) + 1) * datasize;
|
||
|
||
if (j + bytes <= buflen)
|
||
{
|
||
/* We can copy directly into the image buffer. */
|
||
p = buf + j;
|
||
}
|
||
else {
|
||
#if defined(PROFILE) || defined(VERBOSE)
|
||
printf ("TGA: needed to use statebuf for %d bytes\n", buflen - j);
|
||
#endif
|
||
/* Allocate the state buffer if we haven't already. */
|
||
if (!statebuf)
|
||
statebuf = (guchar *) g_malloc (RLE_PACKETSIZE * datasize);
|
||
p = statebuf;
|
||
}
|
||
|
||
if (count & RLE_PACKETSIZE)
|
||
{
|
||
/* Fill the buffer with the next value. */
|
||
if (fread (p, datasize, 1, fp) != 1)
|
||
{
|
||
#ifdef VERBOSE
|
||
if (verbose)
|
||
printf ("TGA: EOF while reading %d/%d element RLE packet\n",
|
||
bytes, datasize);
|
||
#endif
|
||
return j / datasize;
|
||
}
|
||
|
||
/* Optimized case for single-byte encoded data. */
|
||
if (datasize == 1)
|
||
memset (p + 1, *p, bytes - 1);
|
||
else
|
||
for (k = datasize; k < bytes; k += datasize)
|
||
memcpy (p + k, p, datasize);
|
||
}
|
||
else
|
||
{
|
||
/* Read in the buffer. */
|
||
if (fread (p, bytes, 1, fp) != 1)
|
||
{
|
||
#ifdef VERBOSE
|
||
if (verbose)
|
||
printf ("TGA: EOF while reading %d/%d element raw packet\n",
|
||
bytes, datasize);
|
||
#endif
|
||
return j / datasize;
|
||
}
|
||
}
|
||
|
||
#ifdef VERBOSE
|
||
if (verbose > 1)
|
||
{
|
||
totbytes += bytes;
|
||
if (verbose > 2)
|
||
printf ("TGA: %s packet %d/%d\n",
|
||
(count & RLE_PACKETSIZE) ? "RLE" : "raw",
|
||
bytes, totbytes);
|
||
}
|
||
#endif
|
||
|
||
/* We may need to copy bytes from the state buffer. */
|
||
if (p == statebuf)
|
||
statelen = bytes;
|
||
else
|
||
j += bytes;
|
||
}
|
||
|
||
#ifdef VERBOSE
|
||
if (verbose > 1)
|
||
{
|
||
printf ("TGA: rle_fread %d/%d (total %d)\n",
|
||
nelems * datasize, totbytes - curbytes, totbytes);
|
||
}
|
||
#endif
|
||
return nelems;
|
||
}
|
||
|
||
|
||
/* This function is stateless, which means that we always finish packets
|
||
on buffer boundaries. As a beneficial side-effect, rle_fread
|
||
never has to allocate a state buffer when it loads our files, provided
|
||
it is called using the same buffer lengths!
|
||
|
||
So, we get better compression than line-by-line encoders, and better
|
||
loading performance than whole-stream images. */
|
||
/* RunLength Encode a bufferful of file. */
|
||
static int
|
||
rle_fwrite (guchar *buf, int datasize, int nelems, FILE *fp)
|
||
{
|
||
/* Now runlength-encode the whole buffer. */
|
||
int count, j, buflen;
|
||
guchar *begin;
|
||
|
||
#ifdef VERBOSE
|
||
int curbytes = totbytes;
|
||
#endif
|
||
|
||
/* Scale the buffer length. */
|
||
buflen = datasize * nelems;
|
||
|
||
begin = buf;
|
||
j = datasize;
|
||
while (j < buflen)
|
||
{
|
||
/* BUF[J] is our lookahead element, BEGIN is the beginning of this
|
||
run, and COUNT is the number of elements in this run. */
|
||
if (memcmp (buf + j, begin, datasize) == 0)
|
||
{
|
||
/* We have a run of identical characters. */
|
||
count = 1;
|
||
do
|
||
{
|
||
j += datasize;
|
||
count ++;
|
||
}
|
||
while (j < buflen && count < RLE_PACKETSIZE &&
|
||
memcmp (buf + j, begin, datasize) == 0);
|
||
|
||
/* J now either points to the beginning of the next run,
|
||
or close to the end of the buffer. */
|
||
|
||
/* Write out the run. */
|
||
if (fputc ((count - 1) | RLE_PACKETSIZE, fp) == EOF ||
|
||
fwrite (begin, datasize, 1, fp) != 1)
|
||
return 0;
|
||
|
||
#ifdef VERBOSE
|
||
if (verbose > 1)
|
||
{
|
||
totbytes += count * datasize;
|
||
if (verbose > 2)
|
||
printf ("TGA: RLE packet %d/%d\n", count * datasize, totbytes);
|
||
}
|
||
#endif
|
||
}
|
||
else
|
||
{
|
||
/* We have a run of raw characters. */
|
||
count = 0;
|
||
do
|
||
{
|
||
j += datasize;
|
||
count ++;
|
||
}
|
||
while (j < buflen && count < RLE_PACKETSIZE &&
|
||
memcmp (buf + j - datasize, buf + j, datasize) != 0);
|
||
|
||
/* Back up to the previous character. */
|
||
j -= datasize;
|
||
|
||
/* J now either points to the beginning of the next run,
|
||
or at the end of the buffer. */
|
||
|
||
/* Write out the raw packet. */
|
||
if (fputc (count - 1, fp) == EOF ||
|
||
fwrite (begin, datasize, count, fp) != count)
|
||
return 0;
|
||
|
||
#ifdef VERBOSE
|
||
if (verbose > 1)
|
||
{
|
||
totbytes += count * datasize;
|
||
if (verbose > 2)
|
||
printf ("TGA: raw packet %d/%d\n", count * datasize, totbytes);
|
||
}
|
||
#endif
|
||
}
|
||
|
||
/* Set the beginning of the next run and the next lookahead. */
|
||
begin = buf + j;
|
||
j += datasize;
|
||
}
|
||
|
||
/* If we didn't encode all the elements, write one last packet. */
|
||
if (begin < buf + buflen)
|
||
{
|
||
#ifdef VERBOSE
|
||
if (verbose > 1)
|
||
{
|
||
totbytes += datasize;
|
||
if (verbose > 2)
|
||
printf ("TGA: FINAL raw packet %d/%d\n", datasize, totbytes);
|
||
}
|
||
#endif
|
||
if (fputc (0, fp) == EOF ||
|
||
fwrite (begin, datasize, 1, fp) != 1)
|
||
return 0;
|
||
}
|
||
|
||
#ifdef VERBOSE
|
||
if (verbose > 1)
|
||
{
|
||
printf ("TGA: rle_fwrite %d/%d (total %d)\n",
|
||
totbytes - curbytes, nelems * datasize, totbytes);
|
||
}
|
||
#endif
|
||
return nelems;
|
||
}
|
||
|
||
|
||
|
||
|
||
static gint32
|
||
ReadImage (FILE *fp, struct tga_header *hdr, char *filename)
|
||
{
|
||
static gint32 image_ID;
|
||
gint32 layer_ID;
|
||
|
||
GPixelRgn pixel_rgn;
|
||
GDrawable *drawable;
|
||
guchar *data, *buffer;
|
||
GDrawableType dtype;
|
||
GImageType itype;
|
||
guchar *alphas;
|
||
|
||
int width, height, bpp, abpp, pbpp, nalphas;
|
||
int i, j, k;
|
||
int pelbytes, tileheight, wbytes, bsize, npels, pels;
|
||
int rle, badread;
|
||
|
||
int (*myfread)(guchar *, int, int, FILE *);
|
||
|
||
/* Find out whether the image is horizontally or vertically reversed.
|
||
The GIMP likes things left-to-right, top-to-bottom. */
|
||
gchar horzrev = hdr->descriptor & TGA_DESC_HORIZONTAL;
|
||
gchar vertrev = !(hdr->descriptor & TGA_DESC_VERTICAL);
|
||
|
||
/* Reassemble the multi-byte values correctly, regardless of
|
||
host endianness. */
|
||
width = (hdr->widthHi << 8) | hdr->widthLo;
|
||
height = (hdr->heightHi << 8) | hdr->heightLo;
|
||
|
||
bpp = hdr->bpp;
|
||
abpp = hdr->descriptor & TGA_DESC_ABITS;
|
||
|
||
if (hdr->imageType == TGA_TYPE_COLOR ||
|
||
hdr->imageType == TGA_TYPE_COLOR_RLE)
|
||
pbpp = MIN (bpp / 3, 8) * 3;
|
||
else if (abpp < bpp)
|
||
pbpp = bpp - abpp;
|
||
else
|
||
pbpp = bpp;
|
||
|
||
if (abpp + pbpp > bpp)
|
||
{
|
||
printf ("TGA: %d bit image, %d bit alpha is greater than %d total bits per pixel\n",
|
||
pbpp, abpp, bpp);
|
||
|
||
/* Assume that alpha bits were set incorrectly. */
|
||
abpp = bpp - pbpp;
|
||
printf ("TGA: reducing to %d bit alpha\n", abpp);
|
||
}
|
||
else if (abpp + pbpp < bpp)
|
||
{
|
||
printf ("TGA: %d bit image, %d bit alpha is less than %d total bits per pixel\n",
|
||
pbpp, abpp, bpp);
|
||
|
||
/* Again, assume that alpha bits were set incorrectly. */
|
||
abpp = bpp - pbpp;
|
||
printf ("TGA: increasing to %d bit alpha\n", abpp);
|
||
}
|
||
|
||
rle = 0;
|
||
switch (hdr->imageType)
|
||
{
|
||
case TGA_TYPE_MAPPED_RLE:
|
||
rle = 1;
|
||
case TGA_TYPE_MAPPED:
|
||
itype = INDEXED;
|
||
|
||
/* Find the size of palette elements. */
|
||
pbpp = MIN (hdr->colorMapSize / 3, 8) * 3;
|
||
if (pbpp < hdr->colorMapSize)
|
||
abpp = hdr->colorMapSize - pbpp;
|
||
else
|
||
abpp = 0;
|
||
|
||
#ifdef VERBOSE
|
||
if (verbose)
|
||
printf ("TGA: %d bit indexed image, %d bit alpha (%d bit indices)\n",
|
||
pbpp, abpp, bpp);
|
||
#endif
|
||
|
||
if (bpp != 8)
|
||
{
|
||
/* We can only cope with 8-bit indices. */
|
||
printf ("TGA: index sizes other than 8 bits are unimplemented\n");
|
||
return -1;
|
||
}
|
||
|
||
if (abpp)
|
||
dtype = INDEXEDA_IMAGE;
|
||
else
|
||
dtype = INDEXED_IMAGE;
|
||
break;
|
||
|
||
case TGA_TYPE_GRAY_RLE:
|
||
rle = 1;
|
||
case TGA_TYPE_GRAY:
|
||
itype = GRAY;
|
||
#ifdef VERBOSE
|
||
if (verbose)
|
||
printf ("TGA: %d bit grayscale image, %d bit alpha\n",
|
||
pbpp, abpp);
|
||
#endif
|
||
|
||
if (abpp)
|
||
dtype = GRAYA_IMAGE;
|
||
else
|
||
dtype = GRAY_IMAGE;
|
||
break;
|
||
|
||
case TGA_TYPE_COLOR_RLE:
|
||
rle = 1;
|
||
case TGA_TYPE_COLOR:
|
||
itype = RGB;
|
||
#ifdef VERBOSE
|
||
if (verbose)
|
||
printf ("TGA: %d bit color image, %d bit alpha\n", pbpp, abpp);
|
||
#endif
|
||
|
||
if (abpp)
|
||
dtype = RGBA_IMAGE;
|
||
else
|
||
dtype = RGB_IMAGE;
|
||
break;
|
||
|
||
default:
|
||
printf ("TGA: unrecognized image type %d\n", hdr->imageType);
|
||
return -1;
|
||
}
|
||
|
||
if ((abpp && abpp != 8) ||
|
||
((itype == RGB || itype == INDEXED) && pbpp != 24) ||
|
||
(itype == GRAY && pbpp != 8))
|
||
{
|
||
/* FIXME: We haven't implemented bit-packed fields yet. */
|
||
printf ("TGA: channel sizes other than 8 bits are unimplemented\n");
|
||
return -1;
|
||
}
|
||
|
||
/* Check that we have a color map only when we need it. */
|
||
if (itype == INDEXED)
|
||
{
|
||
if (hdr->colorMapType != 1)
|
||
{
|
||
printf ("TGA: indexed image has invalid color map type %d\n",
|
||
hdr->colorMapType);
|
||
return -1;
|
||
}
|
||
}
|
||
else if (hdr->colorMapType != 0)
|
||
{
|
||
printf ("TGA: non-indexed image has invalid color map type %d\n",
|
||
hdr->colorMapType);
|
||
return -1;
|
||
}
|
||
|
||
image_ID = gimp_image_new (width, height, itype);
|
||
gimp_image_set_filename (image_ID, filename);
|
||
|
||
alphas = 0;
|
||
nalphas = 0;
|
||
if (hdr->colorMapType == 1)
|
||
{
|
||
/* We need to read in the colormap. */
|
||
int index, length, colors;
|
||
int tmp;
|
||
|
||
guchar *cmap;
|
||
|
||
index = (hdr->colorMapIndexHi << 8) | hdr->colorMapIndexLo;
|
||
length = (hdr->colorMapLengthHi << 8) | hdr->colorMapLengthLo;
|
||
|
||
#ifdef VERBOSE
|
||
if (verbose)
|
||
printf ("TGA: reading color map (%d + %d) * (%d bits)\n",
|
||
index, length, hdr->colorMapSize);
|
||
#endif
|
||
if (length == 0)
|
||
{
|
||
printf ("TGA: invalid color map length %d\n", length);
|
||
gimp_image_delete (image_ID);
|
||
return -1;
|
||
}
|
||
|
||
pelbytes = ROUNDUP_DIVIDE (hdr->colorMapSize, 8);
|
||
colors = length + index;
|
||
cmap = g_malloc (colors * pelbytes);
|
||
|
||
/* Zero the entries up to the beginning of the map. */
|
||
memset (cmap, 0, index * pelbytes);
|
||
|
||
/* Read in the rest of the colormap. */
|
||
if (fread (cmap + (index * pelbytes), pelbytes, length, fp) != length)
|
||
{
|
||
printf ("TGA: error reading colormap (ftell == %ld)\n", ftell (fp));
|
||
gimp_image_delete (image_ID);
|
||
return -1;
|
||
}
|
||
|
||
/* If we have an alpha channel, then create a mapping to the alpha
|
||
values. */
|
||
if (pelbytes > 3)
|
||
alphas = (guchar *) g_malloc (colors);
|
||
|
||
k = 0;
|
||
for (j = 0; j < colors * pelbytes; j += pelbytes)
|
||
{
|
||
/* Swap from BGR to RGB. */
|
||
tmp = cmap[j];
|
||
cmap[k ++] = cmap[j + 2];
|
||
cmap[k ++] = cmap[j + 1];
|
||
cmap[k ++] = tmp;
|
||
|
||
/* Take the alpha values out of the colormap. */
|
||
if (alphas)
|
||
alphas[nalphas ++] = cmap[j + 3];
|
||
}
|
||
|
||
/* If the last color was transparent, then omit it from the
|
||
GIMP mapping. */
|
||
if (nalphas && alphas[nalphas - 1] == 0)
|
||
colors --;
|
||
|
||
/* Set the colormap. */
|
||
gimp_image_set_cmap (image_ID, cmap, colors);
|
||
g_free (cmap);
|
||
|
||
/* Now pretend as if we only have 8 bpp. */
|
||
abpp = 0;
|
||
pbpp = 8;
|
||
}
|
||
|
||
/* Continue initializing. */
|
||
layer_ID = gimp_layer_new (image_ID,
|
||
"Background",
|
||
width, height,
|
||
dtype,
|
||
100,
|
||
NORMAL_MODE);
|
||
|
||
gimp_image_add_layer (image_ID, layer_ID, 0);
|
||
|
||
drawable = gimp_drawable_get (layer_ID);
|
||
|
||
/* Prepare the pixel region. */
|
||
gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, TRUE, FALSE);
|
||
|
||
/* Calculate number of GIMP bytes per pixel. */
|
||
pelbytes = drawable->bpp;
|
||
|
||
/* Calculate TGA bytes per pixel. */
|
||
bpp = ROUNDUP_DIVIDE (pbpp + abpp, 8);
|
||
|
||
/* Allocate the data. */
|
||
tileheight = gimp_tile_height ();
|
||
data = (guchar *) g_malloc (width * tileheight * pelbytes);
|
||
|
||
/* Maybe we need to reverse the data. */
|
||
buffer = 0;
|
||
if (horzrev || vertrev)
|
||
buffer = (guchar *) g_malloc (width * tileheight * pelbytes);
|
||
|
||
if (rle)
|
||
myfread = rle_fread;
|
||
else
|
||
myfread = std_fread;
|
||
|
||
wbytes = width * pelbytes;
|
||
badread = 0;
|
||
for (i = 0; i < height; i += tileheight)
|
||
{
|
||
tileheight = MIN (tileheight, height - i);
|
||
|
||
#ifdef VERBOSE
|
||
if (verbose > 1)
|
||
printf ("TGA: reading %dx(%d+%d)x%d pixel region\n", width,
|
||
(vertrev ? (height - (i + tileheight)) : i),
|
||
tileheight, pelbytes);
|
||
#endif
|
||
|
||
npels = width * tileheight;
|
||
bsize = wbytes * tileheight;
|
||
|
||
/* Suck in the data one tileheight at a time. */
|
||
if (badread)
|
||
pels = 0;
|
||
else
|
||
pels = (*myfread) (data, bpp, npels, fp);
|
||
|
||
if (pels != npels)
|
||
{
|
||
if (!badread)
|
||
{
|
||
/* Probably premature end of file. */
|
||
printf ("TGA: error reading (ftell == %ld)\n", ftell (fp));
|
||
badread = 1;
|
||
}
|
||
|
||
#if 0
|
||
verbose = 2;
|
||
printf ("TGA: debug %d\n", getpid ());
|
||
kill (getpid (), 19);
|
||
#endif
|
||
|
||
/* Fill the rest of this tile with zeros. */
|
||
memset (data + (pels * bpp), 0, ((npels - pels) * bpp));
|
||
}
|
||
|
||
/* If we have indexed alphas, then set them. */
|
||
if (nalphas)
|
||
{
|
||
/* Start at the end of the buffer, and work backwards. */
|
||
k = (npels - 1) * bpp;
|
||
for (j = bsize - pelbytes; j >= 0; j -= pelbytes)
|
||
{
|
||
/* Find the alpha for this index. */
|
||
data[j + 1] = alphas[data[k]];
|
||
data[j] = data[k --];
|
||
}
|
||
}
|
||
|
||
if (pelbytes >= 3)
|
||
{
|
||
/* Rearrange the colors from BGR to RGB. */
|
||
int tmp;
|
||
for (j = 0; j < bsize; j += pelbytes)
|
||
{
|
||
tmp = data[j];
|
||
data[j] = data[j + 2];
|
||
data[j + 2] = tmp;
|
||
}
|
||
}
|
||
|
||
|
||
if (horzrev || vertrev)
|
||
{
|
||
guchar *tmp;
|
||
if (vertrev)
|
||
{
|
||
/* We need to mirror only vertically. */
|
||
for (j = 0; j < bsize; j += wbytes)
|
||
memcpy (buffer + j,
|
||
data + bsize - (j + wbytes), wbytes);
|
||
}
|
||
else if (horzrev)
|
||
{
|
||
/* We need to mirror only horizontally. */
|
||
for (j = 0; j < bsize; j += wbytes)
|
||
for (k = 0; k < wbytes; k += pelbytes)
|
||
memcpy (buffer + k + j,
|
||
data + (j + wbytes) - (k + pelbytes), pelbytes);
|
||
}
|
||
else
|
||
{
|
||
/* Completely reverse the pixels in the buffer. */
|
||
for (j = 0; j < bsize; j += pelbytes)
|
||
memcpy (buffer + j,
|
||
data + bsize - (j + pelbytes), pelbytes);
|
||
}
|
||
|
||
/* Swap the buffers because we modified them. */
|
||
tmp = buffer;
|
||
buffer = data;
|
||
data = tmp;
|
||
}
|
||
|
||
gimp_progress_update ((double) (i + tileheight) / (double) height);
|
||
|
||
/* If vertically reversed, put the data at the end. */
|
||
gimp_pixel_rgn_set_rect
|
||
(&pixel_rgn, data,
|
||
0, (vertrev ? (height - (i + tileheight)) : i),
|
||
width, tileheight);
|
||
}
|
||
|
||
if (fgetc (fp) != EOF)
|
||
printf ("TGA: too much input data, ignoring extra...\n");
|
||
|
||
g_free (data);
|
||
g_free (buffer);
|
||
|
||
if (alphas)
|
||
g_free (alphas);
|
||
|
||
gimp_drawable_flush (drawable);
|
||
gimp_drawable_detach (drawable);
|
||
|
||
return image_ID;
|
||
} /*read_image*/
|
||
|
||
|
||
static gint
|
||
save_image (char *filename,
|
||
gint32 image_ID,
|
||
gint32 drawable_ID)
|
||
{
|
||
GPixelRgn pixel_rgn;
|
||
GDrawable *drawable;
|
||
GDrawableType dtype;
|
||
int width, height;
|
||
FILE *fp;
|
||
guchar *name_buf;
|
||
int i, j, k;
|
||
int npels, tileheight, pelbytes, bsize;
|
||
int transparent, status;
|
||
|
||
struct tga_header hdr;
|
||
|
||
int (*myfwrite)(guchar *, int, int, FILE *);
|
||
guchar *data;
|
||
|
||
drawable = gimp_drawable_get(drawable_ID);
|
||
dtype = gimp_drawable_type(drawable_ID);
|
||
width = drawable->width;
|
||
height = drawable->height;
|
||
|
||
name_buf = (guchar *) g_malloc(strlen(filename) + 11);
|
||
sprintf((char *)name_buf, "Saving %s:", filename);
|
||
gimp_progress_init((char *)name_buf);
|
||
g_free(name_buf);
|
||
|
||
memset (&hdr, 0, sizeof (hdr));
|
||
/* We like our images top-to-bottom, thank you! */
|
||
hdr.descriptor |= TGA_DESC_VERTICAL;
|
||
|
||
/* Choose the imageType based on our drawable and compression option. */
|
||
switch (dtype)
|
||
{
|
||
case INDEXEDA_IMAGE:
|
||
case INDEXED_IMAGE:
|
||
hdr.bpp = 8;
|
||
hdr.imageType = TGA_TYPE_MAPPED;
|
||
break;
|
||
|
||
case GRAYA_IMAGE:
|
||
hdr.bpp = 8;
|
||
hdr.descriptor |= 8;
|
||
case GRAY_IMAGE:
|
||
hdr.bpp += 8;
|
||
hdr.imageType = TGA_TYPE_GRAY;
|
||
break;
|
||
|
||
case RGBA_IMAGE:
|
||
hdr.bpp = 8;
|
||
hdr.descriptor |= 8;
|
||
case RGB_IMAGE:
|
||
hdr.bpp += 24;
|
||
hdr.imageType = TGA_TYPE_COLOR;
|
||
break;
|
||
|
||
default:
|
||
return FALSE;
|
||
}
|
||
|
||
if (tsvals.rle)
|
||
{
|
||
/* Here we take advantage of the fact that the RLE image type codes
|
||
are exactly 8 greater than the non-RLE. */
|
||
hdr.imageType += 8;
|
||
myfwrite = rle_fwrite;
|
||
}
|
||
else
|
||
myfwrite = std_fwrite;
|
||
|
||
hdr.widthLo = (width & 0xff);
|
||
hdr.widthHi = (width >> 8);
|
||
|
||
hdr.heightLo = (height & 0xff);
|
||
hdr.heightHi = (height >> 8);
|
||
|
||
if((fp = fopen(filename, "wb")) == NULL) {
|
||
printf("TGA: can't create \"%s\"\n", filename);
|
||
return FALSE;
|
||
}
|
||
|
||
/* Mark our save ID. */
|
||
hdr.idLength = strlen (SAVE_ID_STRING);
|
||
|
||
/* See if we have to write out the colour map. */
|
||
transparent = 0;
|
||
if (hdr.imageType == TGA_TYPE_MAPPED_RLE ||
|
||
hdr.imageType == TGA_TYPE_MAPPED)
|
||
{
|
||
guchar *cmap;
|
||
int colors;
|
||
|
||
hdr.colorMapType = 1;
|
||
cmap = gimp_image_get_cmap (image_ID, &colors);
|
||
if (colors > 256)
|
||
{
|
||
printf ("TGA: cannot handle colormap with more than 256 colors (got %d)\n", colors);
|
||
return FALSE;
|
||
}
|
||
|
||
/* If we already have more than 256 colors, then ignore the
|
||
alpha channel. Otherwise, create an entry for any completely
|
||
transparent pixels. */
|
||
if (dtype == INDEXEDA_IMAGE && colors < 256)
|
||
{
|
||
transparent = colors;
|
||
hdr.colorMapSize = 32;
|
||
hdr.colorMapLengthLo = ((colors + 1) & 0xff);
|
||
hdr.colorMapLengthHi = ((colors + 1) >> 8);
|
||
}
|
||
else
|
||
{
|
||
hdr.colorMapSize = 24;
|
||
hdr.colorMapLengthLo = (colors & 0xff);
|
||
hdr.colorMapLengthHi = (colors >> 8);
|
||
}
|
||
|
||
/* Write the header. */
|
||
if (fwrite(&hdr, sizeof (hdr), 1, fp) != 1)
|
||
return FALSE;
|
||
if (fwrite (SAVE_ID_STRING, hdr.idLength, 1, fp) != 1)
|
||
return FALSE;
|
||
|
||
pelbytes = ROUNDUP_DIVIDE (hdr.colorMapSize, 8);
|
||
if (transparent)
|
||
{
|
||
guchar *newcmap;
|
||
|
||
/* Reallocate our colormap to have an alpha channel and
|
||
a fully transparent color. */
|
||
newcmap = (guchar *) g_malloc ((colors + 1) * pelbytes);
|
||
|
||
k = 0;
|
||
for (j = 0; j < colors * 3; j += 3)
|
||
{
|
||
/* Rearrange from RGB to BGR. */
|
||
newcmap[k ++] = cmap[j + 2];
|
||
newcmap[k ++] = cmap[j + 1];
|
||
newcmap[k ++] = cmap[j];
|
||
|
||
/* Set to maximum opacity. */
|
||
newcmap[k ++] = -1;
|
||
}
|
||
|
||
/* Add the transparent color. */
|
||
memset (newcmap + k, 0, pelbytes);
|
||
|
||
/* Write out the colormap. */
|
||
if (fwrite (newcmap, pelbytes, colors + 1, fp) != colors + 1)
|
||
return FALSE;
|
||
g_free (newcmap);
|
||
}
|
||
else
|
||
{
|
||
/* Rearrange the colors from RGB to BGR. */
|
||
int tmp;
|
||
for (j = 0; j < colors * pelbytes; j += pelbytes)
|
||
{
|
||
tmp = cmap[j];
|
||
cmap[j] = cmap[j + 2];
|
||
cmap[j + 2] = tmp;
|
||
}
|
||
|
||
/* Write out the colormap. */
|
||
if (fwrite (cmap, pelbytes, colors, fp) != colors)
|
||
return FALSE;
|
||
}
|
||
|
||
g_free (cmap);
|
||
}
|
||
/* Just write the header. */
|
||
else
|
||
{
|
||
if (fwrite(&hdr, sizeof (hdr), 1, fp) != 1)
|
||
return FALSE;
|
||
if (fwrite (SAVE_ID_STRING, hdr.idLength, 1, fp) != 1)
|
||
return FALSE;
|
||
}
|
||
|
||
/* Allocate a new set of pixels. */
|
||
tileheight = gimp_tile_height ();
|
||
data = (guchar *) g_malloc(width * tileheight * drawable->bpp);
|
||
|
||
gimp_pixel_rgn_init(&pixel_rgn, drawable, 0, 0, width, height, FALSE, FALSE);
|
||
|
||
/* Write out the pixel data. */
|
||
pelbytes = ROUNDUP_DIVIDE (hdr.bpp, 8);
|
||
status = TRUE;
|
||
for (i = 0; i < height; i += tileheight)
|
||
{
|
||
/* Get a horizontal slice of the image. */
|
||
tileheight = MIN(tileheight, height - i);
|
||
gimp_pixel_rgn_get_rect(&pixel_rgn, data, 0, i, width, tileheight);
|
||
|
||
#ifdef VERBOSE
|
||
if (verbose > 1)
|
||
printf ("TGA: writing %dx(%d+%d)x%d pixel region\n",
|
||
width, i, tileheight, pelbytes);
|
||
#endif
|
||
|
||
npels = width * tileheight;
|
||
bsize = npels * pelbytes;
|
||
|
||
/* If the GIMP's bpp does not match the TGA format, strip
|
||
the excess bytes. */
|
||
if (drawable->bpp > pelbytes)
|
||
{
|
||
int nbytes, difference, fullbsize;
|
||
|
||
j = k = 0;
|
||
fullbsize = npels * drawable->bpp;
|
||
|
||
difference = drawable->bpp - pelbytes;
|
||
while (j < fullbsize)
|
||
{
|
||
nbytes = 0;
|
||
for (nbytes = 0; nbytes < pelbytes; nbytes ++)
|
||
/* Be careful to handle overlapping pixels. */
|
||
data[k ++] = data[j ++];
|
||
|
||
/* If this is an indexed image, and data[j] (alpha
|
||
channel) is zero, then we should write our transparent
|
||
pixel's index. */
|
||
if (dtype == INDEXEDA_IMAGE && transparent && data[j] == 0)
|
||
data[k - 1] = transparent;
|
||
|
||
/* Increment J to the next pixel. */
|
||
j += difference;
|
||
}
|
||
}
|
||
|
||
if (pelbytes >= 3)
|
||
{
|
||
/* Rearrange the colors from RGB to BGR. */
|
||
int tmp;
|
||
for (j = 0; j < bsize; j += pelbytes)
|
||
{
|
||
tmp = data[j];
|
||
data[j] = data[j + 2];
|
||
data[j + 2] = tmp;
|
||
}
|
||
}
|
||
|
||
if ((*myfwrite) (data, pelbytes, npels, fp) != npels)
|
||
{
|
||
/* We have no choice but to break and truncate the file
|
||
if we are writing with RLE. */
|
||
status = FALSE;
|
||
break;
|
||
}
|
||
|
||
gimp_progress_update ((double) (i + tileheight) / (double) height);
|
||
}
|
||
|
||
gimp_drawable_detach(drawable);
|
||
g_free (data);
|
||
|
||
fclose(fp);
|
||
return status;
|
||
}
|
||
|
||
|
||
static gint
|
||
save_dialog ()
|
||
{
|
||
GtkWidget *dlg;
|
||
GtkWidget *button;
|
||
GtkWidget *toggle;
|
||
GtkWidget *frame;
|
||
GtkWidget *vbox;
|
||
gchar **argv;
|
||
gint argc;
|
||
|
||
argc = 1;
|
||
argv = g_new (gchar *, 1);
|
||
argv[0] = g_strdup ("save");
|
||
|
||
gtk_init (&argc, &argv);
|
||
gtk_rc_parse (gimp_gtkrc ());
|
||
|
||
dlg = gtk_dialog_new ();
|
||
gtk_window_set_title (GTK_WINDOW (dlg), "Save as Tga");
|
||
gtk_window_position (GTK_WINDOW (dlg), GTK_WIN_POS_MOUSE);
|
||
gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
|
||
(GtkSignalFunc) save_close_callback,
|
||
NULL);
|
||
|
||
/* Action area */
|
||
button = gtk_button_new_with_label ("OK");
|
||
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
|
||
gtk_signal_connect (GTK_OBJECT (button), "clicked",
|
||
(GtkSignalFunc) save_ok_callback,
|
||
dlg);
|
||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
|
||
gtk_widget_grab_default (button);
|
||
gtk_widget_show (button);
|
||
|
||
button = gtk_button_new_with_label ("Cancel");
|
||
GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
|
||
gtk_signal_connect_object (GTK_OBJECT (button), "clicked",
|
||
(GtkSignalFunc) gtk_widget_destroy,
|
||
GTK_OBJECT (dlg));
|
||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->action_area), button, TRUE, TRUE, 0);
|
||
gtk_widget_show (button);
|
||
|
||
/* regular tga parameter settings */
|
||
frame = gtk_frame_new ("Targa Options");
|
||
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_ETCHED_IN);
|
||
gtk_container_border_width (GTK_CONTAINER (frame), 10);
|
||
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, TRUE, TRUE, 0);
|
||
vbox = gtk_vbox_new (FALSE, 5);
|
||
gtk_container_border_width (GTK_CONTAINER (vbox), 5);
|
||
gtk_container_add (GTK_CONTAINER (frame), vbox);
|
||
|
||
/* rle */
|
||
toggle = gtk_check_button_new_with_label ("RLE compression");
|
||
gtk_box_pack_start (GTK_BOX (vbox), toggle, TRUE, TRUE, 0);
|
||
gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
|
||
(GtkSignalFunc) save_toggle_update,
|
||
&tsvals.rle);
|
||
gtk_toggle_button_set_state (GTK_TOGGLE_BUTTON (toggle), tsvals.rle);
|
||
gtk_widget_show (toggle);
|
||
|
||
gtk_widget_show (vbox);
|
||
gtk_widget_show (frame);
|
||
|
||
gtk_widget_show (dlg);
|
||
|
||
gtk_main ();
|
||
gdk_flush ();
|
||
|
||
return tsint.run;
|
||
}
|
||
|
||
/* Save interface functions */
|
||
|
||
static void
|
||
save_close_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
gtk_main_quit ();
|
||
}
|
||
|
||
static void
|
||
save_ok_callback (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
tsint.run = TRUE;
|
||
gtk_widget_destroy (GTK_WIDGET (data));
|
||
}
|
||
|
||
static void
|
||
save_toggle_update (GtkWidget *widget,
|
||
gpointer data)
|
||
{
|
||
int *toggle_val;
|
||
|
||
toggle_val = (int *) data;
|
||
|
||
if (GTK_TOGGLE_BUTTON (widget)->active)
|
||
*toggle_val = TRUE;
|
||
else
|
||
*toggle_val = FALSE;
|
||
}
|
||
|
||
/* The End */
|
||
|
||
/*
|
||
Local Variables:
|
||
compile-command:"gcc -Wall -g -O -o tga tga.c -lgimp -lgtk -lgdk -lglib -lm"
|
||
End:
|
||
*/
|