~ubuntu-branches/ubuntu/breezy/gimp/breezy

« back to all changes in this revision

Viewing changes to plug-ins/common/tga.c

  • Committer: Bazaar Package Importer
  • Author(s): Sebastien Bacher
  • Date: 2005-10-04 19:04:46 UTC
  • Revision ID: james.westby@ubuntu.com-20051004190446-ukh32kwk56s4sjhu
Tags: upstream-2.2.8
ImportĀ upstreamĀ versionĀ 2.2.8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* The GIMP -- an image manipulation program
 
2
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 
3
 *
 
4
 * $Id: tga.c,v 1.50 2004/11/02 12:00:24 mitch Exp $
 
5
 * TrueVision Targa loading and saving file filter for the Gimp.
 
6
 * Targa code Copyright (C) 1997 Raphael FRANCOIS and Gordon Matzigkeit
 
7
 *
 
8
 * The Targa reading and writing code was written from scratch by
 
9
 * Raphael FRANCOIS <fraph@ibm.net> and Gordon Matzigkeit
 
10
 * <gord@gnu.ai.mit.edu> based on the TrueVision TGA File Format
 
11
 * Specification, Version 2.0:
 
12
 *
 
13
 *   <URL:ftp://ftp.truevision.com/pub/TGA.File.Format.Spec/>
 
14
 *
 
15
 * It does not contain any code written for other TGA file loaders.
 
16
 * Not even the RLE handling. ;)
 
17
 *
 
18
 * This program is free software; you can redistribute it and/or modify
 
19
 * it under the terms of the GNU General Public License as published by
 
20
 * the Free Software Foundation; either version 2 of the License, or
 
21
 * (at your option) any later version.
 
22
 *
 
23
 * This program is distributed in the hope that it will be useful,
 
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
26
 * GNU General Public License for more details.
 
27
 *
 
28
 * You should have received a copy of the GNU General Public License
 
29
 * along with this program; if not, write to the Free Software
 
30
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 
31
 */
 
32
 
 
33
/*
 
34
 * Modified August-November 2000, Nick Lamb <njl195@zepler.org.uk>
 
35
 *   - Clean-up more code, avoid structure implementation dependency,
 
36
 *   - Load more types of images reliably, reject others firmly
 
37
 *   - This is not perfect, but I think it's much better. Test please!
 
38
 *
 
39
 * Release 1.2, 1997-09-24, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
 
40
 *   - Bug fixes and source cleanups.
 
41
 *
 
42
 * Release 1.1, 1997-09-19, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
 
43
 *   - Preserve alpha channels.  For indexed images, this can only be
 
44
 *     done if there is at least one free colormap entry.
 
45
 *
 
46
 * Release 1.0, 1997-09-06, Gordon Matzigkeit <gord@gnu.ai.mit.edu>:
 
47
 *   - Handle loading all image types from the 2.0 specification.
 
48
 *   - Fix many alignment and endianness problems.
 
49
 *   - Use tiles for lower memory consumption and better speed.
 
50
 *   - Rewrite RLE code for clarity and speed.
 
51
 *   - Handle saving with RLE.
 
52
 *
 
53
 * Release 0.9, 1997-06-18, Raphael FRANCOIS <fraph@ibm.net>:
 
54
 *   - Can load 24 and 32-bit Truecolor images, with and without RLE.
 
55
 *   - Saving currently only works without RLE.
 
56
 *
 
57
 *
 
58
 * TODO:
 
59
 *   - The GIMP stores the indexed alpha channel as a separate byte,
 
60
 *     one for each pixel.  The TGA file format spec requires that the
 
61
 *     alpha channel be stored as part of the colormap, not with each
 
62
 *     individual pixel.  This means that we have no good way of
 
63
 *     saving and loading INDEXEDA images that use alpha channel values
 
64
 *     other than 0 and 255.  Find a workaround.
 
65
 */
 
66
 
 
67
/* Set these for debugging. */
 
68
/* #define PROFILE 1 */
 
69
 
 
70
#include "config.h"
 
71
 
 
72
#ifdef PROFILE
 
73
# include <sys/times.h>
 
74
#endif
 
75
 
 
76
#include <errno.h>
 
77
#include <stdio.h>
 
78
#include <stdlib.h>
 
79
#include <string.h>
 
80
#ifdef HAVE_UNISTD_H
 
81
#include <unistd.h>
 
82
#endif
 
83
 
 
84
#include <gtk/gtk.h>
 
85
 
 
86
#include <libgimp/gimp.h>
 
87
#include <libgimp/gimpui.h>
 
88
 
 
89
#include "libgimp/stdplugins-intl.h"
 
90
 
 
91
 
 
92
/* Round up a division to the nearest integer. */
 
93
#define ROUNDUP_DIVIDE(n,d) (((n) + (d - 1)) / (d))
 
94
 
 
95
typedef struct _TgaSaveVals
 
96
{
 
97
  gint rle;
 
98
  gint origin;
 
99
} TgaSaveVals;
 
100
 
 
101
static TgaSaveVals tsvals =
 
102
{
 
103
  1,    /* rle = ON */
 
104
  1,    /* origin = bottom left */
 
105
};
 
106
 
 
107
 
 
108
 /* TRUEVISION-XFILE magic signature string */
 
109
static guchar magic[18] =
 
110
{
 
111
  0x54, 0x52, 0x55, 0x45, 0x56, 0x49, 0x53, 0x49, 0x4f,
 
112
  0x4e, 0x2d, 0x58, 0x46, 0x49, 0x4c, 0x45, 0x2e, 0x0
 
113
};
 
114
 
 
115
typedef struct tga_info_struct
 
116
{
 
117
  guint8 idLength;
 
118
  guint8 colorMapType;
 
119
 
 
120
  guint8 imageType;
 
121
  /* Known image types. */
 
122
#define TGA_TYPE_MAPPED      1
 
123
#define TGA_TYPE_COLOR       2
 
124
#define TGA_TYPE_GRAY        3
 
125
 
 
126
  guint8 imageCompression;
 
127
  /* Only known compression is RLE */
 
128
#define TGA_COMP_NONE        0
 
129
#define TGA_COMP_RLE         1
 
130
 
 
131
  /* Color Map Specification. */
 
132
  /* We need to separately specify high and low bytes to avoid endianness
 
133
     and alignment problems. */
 
134
 
 
135
  guint16 colorMapIndex;
 
136
  guint16 colorMapLength;
 
137
  guint8 colorMapSize;
 
138
 
 
139
  /* Image Specification. */
 
140
  guint16 xOrigin;
 
141
  guint16 yOrigin;
 
142
 
 
143
  guint16 width;
 
144
  guint16 height;
 
145
 
 
146
  guint8 bpp;
 
147
  guint8 bytes;
 
148
 
 
149
  guint8 alphaBits;
 
150
  guint8 flipHoriz;
 
151
  guint8 flipVert;
 
152
 
 
153
  /* Extensions (version 2) */
 
154
 
 
155
/* Not all the structures described in the standard are transcribed here
 
156
   only those which seem applicable to Gimp */
 
157
 
 
158
  gchar authorName[41];
 
159
  gchar comment[324];
 
160
  guint month, day, year, hour, minute, second;
 
161
  gchar jobName[41];
 
162
  gchar softwareID[41];
 
163
  guint pixelWidth, pixelHeight;  /* write dpi? */
 
164
  gdouble gamma;
 
165
} tga_info;
 
166
 
 
167
 
 
168
/* Declare some local functions.
 
169
 */
 
170
static void      query         (void);
 
171
static void      run           (const gchar      *name,
 
172
                                gint              nparams,
 
173
                                const GimpParam  *param,
 
174
                                gint             *nreturn_vals,
 
175
                                GimpParam       **return_vals);
 
176
 
 
177
static gint32    load_image    (const gchar      *filename);
 
178
static gint      save_image    (const gchar      *filename,
 
179
                                gint32            image_ID,
 
180
                                gint32            drawable_ID);
 
181
 
 
182
static gboolean  save_dialog   (void);
 
183
 
 
184
static gint32    ReadImage     (FILE             *fp,
 
185
                                tga_info         *info,
 
186
                                const gchar      *filename);
 
187
 
 
188
 
 
189
GimpPlugInInfo PLUG_IN_INFO =
 
190
{
 
191
  NULL,  /* init_proc  */
 
192
  NULL,  /* quit_proc  */
 
193
  query, /* query_proc */
 
194
  run,   /* run_proc   */
 
195
};
 
196
 
 
197
 
 
198
MAIN ()
 
199
 
 
200
static void
 
201
query (void)
 
202
{
 
203
  static GimpParamDef load_args[] =
 
204
  {
 
205
    { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
 
206
    { GIMP_PDB_STRING, "filename", "The name of the file to load" },
 
207
    { GIMP_PDB_STRING, "raw_filename", "The name entered" }
 
208
  };
 
209
 
 
210
  static GimpParamDef load_return_vals[] =
 
211
  {
 
212
    { GIMP_PDB_IMAGE, "image", "Output image" }
 
213
  };
 
214
 
 
215
  static GimpParamDef save_args[] =
 
216
  {
 
217
    { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
 
218
    { GIMP_PDB_IMAGE, "image", "Input image" },
 
219
    { GIMP_PDB_DRAWABLE, "drawable", "Drawable to save" },
 
220
    { GIMP_PDB_STRING, "filename", "The name of the file to save the image in" },
 
221
    { GIMP_PDB_STRING, "raw_filename", "The name of the file to save the image in" },
 
222
    { GIMP_PDB_INT32, "rle", "Use RLE compression" },
 
223
    { GIMP_PDB_INT32, "origin", "Image origin (0 = top-left, 1 = bottom-left)"}
 
224
 
 
225
  } ;
 
226
 
 
227
  gimp_install_procedure ("file_tga_load",
 
228
                          "Loads files of Targa file format",
 
229
                          "FIXME: write help for tga_load",
 
230
                          "Raphael FRANCOIS, Gordon Matzigkeit",
 
231
                          "Raphael FRANCOIS, Gordon Matzigkeit",
 
232
                          "1997",
 
233
                          N_("TarGA image"),
 
234
                          NULL,
 
235
                          GIMP_PLUGIN,
 
236
                          G_N_ELEMENTS (load_args),
 
237
                          G_N_ELEMENTS (load_return_vals),
 
238
                          load_args, load_return_vals);
 
239
 
 
240
  gimp_register_file_handler_mime ("file_tga_load", "image/x-tga");
 
241
  gimp_register_load_handler ("file_tga_load", "tga", "");
 
242
 
 
243
  gimp_install_procedure ("file_tga_save",
 
244
                          "saves files in the Targa file format",
 
245
                          "FIXME: write help for tga_save",
 
246
                          "Raphael FRANCOIS, Gordon Matzigkeit",
 
247
                          "Raphael FRANCOIS, Gordon Matzigkeit",
 
248
                          "1997",
 
249
                          N_("TarGA image"),
 
250
                          "RGB*, GRAY*, INDEXED*",
 
251
                          GIMP_PLUGIN,
 
252
                          G_N_ELEMENTS (save_args), 0,
 
253
                          save_args, NULL);
 
254
 
 
255
  gimp_register_file_handler_mime ("file_tga_save", "image/x-tga");
 
256
  gimp_register_save_handler ("file_tga_save", "tga", "");
 
257
}
 
258
 
 
259
static void
 
260
run (const gchar      *name,
 
261
     gint              nparams,
 
262
     const GimpParam  *param,
 
263
     gint             *nreturn_vals,
 
264
     GimpParam       **return_vals)
 
265
{
 
266
  static GimpParam  values[2];
 
267
  GimpRunMode       run_mode;
 
268
  GimpPDBStatusType status = GIMP_PDB_SUCCESS;
 
269
  gint32            image_ID;
 
270
  gint32            drawable_ID;
 
271
  GimpExportReturn  export = GIMP_EXPORT_CANCEL;
 
272
 
 
273
#ifdef PROFILE
 
274
  struct tms tbuf1, tbuf2;
 
275
#endif
 
276
 
 
277
  run_mode = param[0].data.d_int32;
 
278
 
 
279
  INIT_I18N ();
 
280
 
 
281
  *nreturn_vals = 1;
 
282
  *return_vals  = values;
 
283
  values[0].type          = GIMP_PDB_STATUS;
 
284
  values[0].data.d_status = GIMP_PDB_EXECUTION_ERROR;
 
285
 
 
286
  if (strcmp (name, "file_tga_load") == 0)
 
287
    {
 
288
#ifdef PROFILE
 
289
      times (&tbuf1);
 
290
#endif
 
291
 
 
292
      image_ID = load_image (param[1].data.d_string);
 
293
 
 
294
      if (image_ID != -1)
 
295
        {
 
296
          *nreturn_vals = 2;
 
297
          values[1].type         = GIMP_PDB_IMAGE;
 
298
          values[1].data.d_image = image_ID;
 
299
        }
 
300
      else
 
301
        {
 
302
          status = GIMP_PDB_EXECUTION_ERROR;
 
303
        }
 
304
    }
 
305
  else if (strcmp (name, "file_tga_save") == 0)
 
306
    {
 
307
      gimp_ui_init ("tga", FALSE);
 
308
 
 
309
      image_ID     = param[1].data.d_int32;
 
310
      drawable_ID  = param[2].data.d_int32;
 
311
 
 
312
      /*  eventually export the image */
 
313
      switch (run_mode)
 
314
        {
 
315
        case GIMP_RUN_INTERACTIVE:
 
316
        case GIMP_RUN_WITH_LAST_VALS:
 
317
          export = gimp_export_image (&image_ID, &drawable_ID, "TGA",
 
318
                                      (GIMP_EXPORT_CAN_HANDLE_RGB |
 
319
                                       GIMP_EXPORT_CAN_HANDLE_GRAY |
 
320
                                       GIMP_EXPORT_CAN_HANDLE_INDEXED |
 
321
                                       GIMP_EXPORT_CAN_HANDLE_ALPHA ));
 
322
          if (export == GIMP_EXPORT_CANCEL)
 
323
            {
 
324
              values[0].data.d_status = GIMP_PDB_CANCEL;
 
325
              return;
 
326
            }
 
327
          break;
 
328
        default:
 
329
          break;
 
330
        }
 
331
 
 
332
      switch (run_mode)
 
333
        {
 
334
        case GIMP_RUN_INTERACTIVE:
 
335
          /*  Possibly retrieve data  */
 
336
          gimp_get_data ("file_tga_save", &tsvals);
 
337
 
 
338
          /*  First acquire information with a dialog  */
 
339
          if (! save_dialog ())
 
340
            status = GIMP_PDB_CANCEL;
 
341
          break;
 
342
 
 
343
        case GIMP_RUN_NONINTERACTIVE:
 
344
          /*  Make sure all the arguments are there!  */
 
345
          if (nparams != 7)
 
346
            {
 
347
              status = GIMP_PDB_CALLING_ERROR;
 
348
            }
 
349
          else
 
350
            {
 
351
              tsvals.rle = (param[5].data.d_int32) ? TRUE : FALSE;
 
352
            }
 
353
          break;
 
354
 
 
355
        case GIMP_RUN_WITH_LAST_VALS:
 
356
          /*  Possibly retrieve data  */
 
357
          gimp_get_data ("file_tga_save", &tsvals);
 
358
          break;
 
359
 
 
360
        default:
 
361
          break;
 
362
        }
 
363
 
 
364
#ifdef PROFILE
 
365
      times (&tbuf1);
 
366
#endif
 
367
 
 
368
      if (status == GIMP_PDB_SUCCESS)
 
369
        {
 
370
          if (save_image (param[3].data.d_string, image_ID, drawable_ID))
 
371
            {
 
372
              /*  Store psvals data  */
 
373
              gimp_set_data ("file_tga_save", &tsvals, sizeof (tsvals));
 
374
            }
 
375
          else
 
376
            {
 
377
              status = GIMP_PDB_EXECUTION_ERROR;
 
378
            }
 
379
        }
 
380
 
 
381
      if (export == GIMP_EXPORT_EXPORT)
 
382
        gimp_image_delete (image_ID);
 
383
    }
 
384
  else
 
385
    {
 
386
      status = GIMP_PDB_CALLING_ERROR;
 
387
    }
 
388
 
 
389
  values[0].data.d_status = status;
 
390
 
 
391
#ifdef PROFILE
 
392
  times (&tbuf2);
 
393
  printf ("TGA: %s profile: %ld user %ld system\n", name,
 
394
          (long) tbuf2.tms_utime - tbuf1.tms_utime,
 
395
          (long) tbuf2.tms_stime - tbuf2.tms_stime);
 
396
#endif
 
397
}
 
398
 
 
399
static gint32
 
400
load_image (const gchar *filename)
 
401
{
 
402
  FILE     *fp;
 
403
  gchar    *name_buf;
 
404
  tga_info  info;
 
405
  guchar    header[18];
 
406
  guchar    footer[26];
 
407
  guchar    extension[495];
 
408
  long      offset;
 
409
 
 
410
  gint32 image_ID = -1;
 
411
 
 
412
  fp = fopen (filename, "rb");
 
413
  if (!fp)
 
414
    {
 
415
      g_message (_("Could not open '%s' for reading: %s"),
 
416
                 gimp_filename_to_utf8 (filename), g_strerror (errno));
 
417
      return -1;
 
418
    }
 
419
 
 
420
  name_buf = g_strdup_printf (_("Opening '%s'..."),
 
421
                              gimp_filename_to_utf8 (filename));
 
422
  gimp_progress_init (name_buf);
 
423
  g_free (name_buf);
 
424
 
 
425
  if (!fseek (fp, -26L, SEEK_END)) { /* Is file big enough for a footer? */
 
426
    if (fread (footer, sizeof (footer), 1, fp) != 1)
 
427
      {
 
428
        g_message (_("Cannot read footer from '%s'"),
 
429
                   gimp_filename_to_utf8 (filename));
 
430
        return -1;
 
431
      }
 
432
    else if (memcmp (footer + 8, magic, sizeof (magic)) == 0)
 
433
      {
 
434
        /* Check the signature. */
 
435
 
 
436
        offset= footer[0] + (footer[1] * 256) + (footer[2] * 65536)
 
437
                          + (footer[3] * 16777216);
 
438
 
 
439
        if (offset != 0)
 
440
          {
 
441
            if (fseek (fp, offset, SEEK_SET) ||
 
442
                fread (extension, sizeof (extension), 1, fp) != 1)
 
443
              {
 
444
                g_message (_("Cannot read extension from '%s'"),
 
445
                           gimp_filename_to_utf8 (filename));
 
446
                return -1;
 
447
              }
 
448
            /* Eventually actually handle version 2 TGA here */
 
449
          }
 
450
      }
 
451
  }
 
452
 
 
453
  if (fseek (fp, 0, SEEK_SET) ||
 
454
      fread (header, sizeof (header), 1, fp) != 1)
 
455
    {
 
456
      g_message ("Cannot read header from '%s'",
 
457
                 gimp_filename_to_utf8 (filename));
 
458
      return -1;
 
459
    }
 
460
 
 
461
  switch (header[2])
 
462
    {
 
463
    case 1:
 
464
      info.imageType = TGA_TYPE_MAPPED;
 
465
      info.imageCompression = TGA_COMP_NONE;
 
466
      break;
 
467
    case 2:
 
468
      info.imageType = TGA_TYPE_COLOR;
 
469
      info.imageCompression = TGA_COMP_NONE;
 
470
      break;
 
471
    case 3:
 
472
      info.imageType = TGA_TYPE_GRAY;
 
473
      info.imageCompression = TGA_COMP_NONE;
 
474
      break;
 
475
 
 
476
    case 9:
 
477
      info.imageType = TGA_TYPE_MAPPED;
 
478
      info.imageCompression = TGA_COMP_RLE;
 
479
      break;
 
480
    case 10:
 
481
      info.imageType = TGA_TYPE_COLOR;
 
482
      info.imageCompression = TGA_COMP_RLE;
 
483
      break;
 
484
    case 11:
 
485
      info.imageType = TGA_TYPE_GRAY;
 
486
      info.imageCompression = TGA_COMP_RLE;
 
487
      break;
 
488
 
 
489
    default:
 
490
      info.imageType = 0;
 
491
    }
 
492
 
 
493
  info.idLength     = header[0];
 
494
  info.colorMapType = header[1];
 
495
 
 
496
  info.colorMapIndex  = header[3] + header[4] * 256;
 
497
  info.colorMapLength = header[5] + header[6] * 256;
 
498
  info.colorMapSize   = header[7];
 
499
 
 
500
  info.xOrigin = header[8] + header[9] * 256;
 
501
  info.yOrigin = header[10] + header[11] * 256;
 
502
  info.width   = header[12] + header[13] * 256;
 
503
  info.height  = header[14] + header[15] * 256;
 
504
 
 
505
  info.bpp       = header[16];
 
506
  info.bytes     = (info.bpp + 7) / 8;
 
507
  info.alphaBits = header[17] & 0x0f; /* Just the low 4 bits */
 
508
  info.flipHoriz = (header[17] & 0x10) ? 1 : 0;
 
509
  info.flipVert  = (header[17] & 0x20) ? 0 : 1;
 
510
 
 
511
  switch (info.imageType)
 
512
    {
 
513
      case TGA_TYPE_MAPPED:
 
514
        if (info.bpp != 8)
 
515
          {
 
516
            g_message ("Unhandled sub-format in '%s'",
 
517
                       gimp_filename_to_utf8 (filename));
 
518
            return -1;
 
519
          }
 
520
        break;
 
521
      case TGA_TYPE_COLOR:
 
522
        if (info.bpp != 15 && info.bpp != 16 && info.bpp != 24
 
523
                     && info.bpp != 32)
 
524
          {
 
525
            g_message ("Unhandled sub-format in '%s'",
 
526
                       gimp_filename_to_utf8 (filename));
 
527
            return -1;
 
528
          }
 
529
        break;
 
530
      case TGA_TYPE_GRAY:
 
531
        if (info.bpp != 8 && (info.alphaBits != 8 || (info.bpp != 16 && info.bpp != 15)))
 
532
          {
 
533
            g_message ("Unhandled sub-format in '%s'",
 
534
                       gimp_filename_to_utf8 (filename));
 
535
            return -1;
 
536
          }
 
537
        break;
 
538
 
 
539
      default:
 
540
        g_message ("Unknown image type for '%s'",
 
541
                   gimp_filename_to_utf8 (filename));
 
542
        return -1;
 
543
    }
 
544
 
 
545
  /* Plausible but unhandled formats */
 
546
  if (info.bytes * 8 != info.bpp && !(info.bytes == 2 && info.bpp == 15))
 
547
    {
 
548
      g_message ("No support yet for TGA with these parameters");
 
549
      return -1;
 
550
    }
 
551
 
 
552
  /* Check that we have a color map only when we need it. */
 
553
  if (info.imageType == TGA_TYPE_MAPPED && info.colorMapType != 1)
 
554
    {
 
555
      g_message ("Indexed image has invalid color map type %d",
 
556
                 info.colorMapType);
 
557
      return -1;
 
558
    }
 
559
  else if (info.imageType != TGA_TYPE_MAPPED && info.colorMapType != 0)
 
560
    {
 
561
      g_message ("Non-indexed image has invalid color map type %d",
 
562
                 info.colorMapType);
 
563
      return -1;
 
564
    }
 
565
 
 
566
  /* Skip the image ID field. */
 
567
  if (info.idLength && fseek (fp, info.idLength, SEEK_CUR))
 
568
    {
 
569
      g_message ("File '%s' is truncated or corrupted",
 
570
                 gimp_filename_to_utf8 (filename));
 
571
      return -1;
 
572
    }
 
573
 
 
574
  image_ID = ReadImage (fp, &info, filename);
 
575
  fclose (fp);
 
576
  return image_ID;
 
577
}
 
578
 
 
579
static void
 
580
rle_write (FILE   *fp,
 
581
           guchar *buffer,
 
582
           guint   width,
 
583
           guint   bytes)
 
584
{
 
585
  gint    repeat = 0;
 
586
  gint    direct = 0;
 
587
  guchar *from   = buffer;
 
588
  gint    x;
 
589
 
 
590
  for (x = 1; x < width; ++x)
 
591
    {
 
592
      if (memcmp (buffer, buffer + bytes, bytes))
 
593
        {
 
594
          /* next pixel is different */
 
595
          if (repeat)
 
596
            {
 
597
              putc (128 + repeat, fp);
 
598
              fwrite (from, bytes, 1, fp);
 
599
              from = buffer+ bytes; /* point to first different pixel */
 
600
              repeat = 0;
 
601
              direct = 0;
 
602
            }
 
603
          else
 
604
            {
 
605
              direct += 1;
 
606
            }
 
607
        }
 
608
      else
 
609
        {
 
610
          /* next pixel is the same */
 
611
          if (direct)
 
612
            {
 
613
              putc (direct - 1, fp);
 
614
              fwrite (from, bytes, direct, fp);
 
615
              from = buffer; /* point to first identical pixel */
 
616
              direct = 0;
 
617
              repeat = 1;
 
618
            }
 
619
          else
 
620
            {
 
621
              repeat += 1;
 
622
            }
 
623
        }
 
624
 
 
625
      if (repeat == 128)
 
626
        {
 
627
          putc (255, fp);
 
628
          fwrite (from, bytes, 1, fp);
 
629
          from = buffer+ bytes;
 
630
          direct = 0;
 
631
          repeat = 0;
 
632
        }
 
633
      else if (direct == 128)
 
634
        {
 
635
          putc (127, fp);
 
636
          fwrite (from, bytes, direct, fp);
 
637
          from = buffer+ bytes;
 
638
          direct = 0;
 
639
          repeat = 0;
 
640
        }
 
641
 
 
642
      buffer += bytes;
 
643
    }
 
644
 
 
645
  if (repeat > 0)
 
646
    {
 
647
      putc (128 + repeat, fp);
 
648
      fwrite (from, bytes, 1, fp);
 
649
    }
 
650
  else
 
651
    {
 
652
      putc (direct, fp);
 
653
      fwrite (from, bytes, direct + 1, fp);
 
654
    }
 
655
}
 
656
 
 
657
static gint
 
658
rle_read (FILE     *fp,
 
659
          guchar   *buffer,
 
660
          tga_info *info)
 
661
{
 
662
  static gint   repeat = 0;
 
663
  static gint   direct = 0;
 
664
  static guchar sample[4];
 
665
  gint head;
 
666
  gint x, k;
 
667
 
 
668
  for (x = 0; x < info->width; x++)
 
669
    {
 
670
      if (repeat == 0 && direct == 0)
 
671
        {
 
672
          head = getc (fp);
 
673
 
 
674
          if (head == EOF)
 
675
            {
 
676
              return EOF;
 
677
            }
 
678
          else if (head >= 128)
 
679
            {
 
680
              repeat = head - 127;
 
681
 
 
682
              if (fread (sample, info->bytes, 1, fp) < 1)
 
683
                return EOF;
 
684
            }
 
685
          else
 
686
            {
 
687
              direct = head + 1;
 
688
            }
 
689
        }
 
690
 
 
691
      if (repeat > 0)
 
692
        {
 
693
          for (k = 0; k < info->bytes; ++k)
 
694
            {
 
695
              buffer[k] = sample[k];
 
696
            }
 
697
 
 
698
          repeat--;
 
699
        }
 
700
      else /* direct > 0 */
 
701
        {
 
702
          if (fread (buffer, info->bytes, 1, fp) < 1)
 
703
            return EOF;
 
704
 
 
705
          direct--;
 
706
        }
 
707
 
 
708
      buffer += info->bytes;
 
709
    }
 
710
 
 
711
  return 0;
 
712
}
 
713
 
 
714
static void
 
715
flip_line (guchar   *buffer,
 
716
           tga_info *info)
 
717
{
 
718
  guchar  temp;
 
719
  guchar *alt;
 
720
  gint    x, s;
 
721
 
 
722
  alt = buffer + (info->bytes * (info->width - 1));
 
723
 
 
724
  for (x = 0; x * 2 <= info->width; x++)
 
725
    {
 
726
      for (s = 0; s < info->bytes; ++s)
 
727
        {
 
728
          temp = buffer[s];
 
729
          buffer[s] = alt[s];
 
730
          alt[s] = temp;
 
731
        }
 
732
 
 
733
      buffer += info->bytes;
 
734
      alt -= info->bytes;
 
735
    }
 
736
}
 
737
 
 
738
/* Some people write 16-bit RGB TGA files. The spec would probably
 
739
   allow 27-bit RGB too, for what it's worth, but I won't fix that
 
740
   unless someone actually provides an existence proof */
 
741
 
 
742
static void
 
743
upsample (guchar *dest,
 
744
          guchar *src,
 
745
          guint   width,
 
746
          guint   bytes,
 
747
          guint8  alphaBits)
 
748
{
 
749
  gint x;
 
750
 
 
751
  for (x = 0; x < width; x++)
 
752
    {
 
753
      dest[0] =  ((src[1] << 1) & 0xf8);
 
754
      dest[0] += (dest[0] >> 5);
 
755
 
 
756
      dest[1] =  ((src[0] & 0xe0) >> 2) + ((src[1] & 0x03) << 6);
 
757
      dest[1] += (dest[1] >> 5);
 
758
 
 
759
      dest[2] =  ((src[0] << 3) & 0xf8);
 
760
      dest[2] += (dest[2] >> 5);
 
761
 
 
762
      switch (alphaBits)
 
763
        {
 
764
        case 1:
 
765
          dest[3] = (src[1] & 0x80)? 0: 255;
 
766
          dest += 4;
 
767
          break;
 
768
        default:
 
769
          dest += 3;
 
770
        }
 
771
 
 
772
      src += bytes;
 
773
    }
 
774
}
 
775
 
 
776
static void
 
777
bgr2rgb (guchar *dest,
 
778
         guchar *src,
 
779
         guint   width,
 
780
         guint   bytes,
 
781
         guint   alpha)
 
782
{
 
783
  guint x;
 
784
 
 
785
  if (alpha)
 
786
    {
 
787
      for (x = 0; x < width; x++)
 
788
        {
 
789
          *(dest++) = src[2];
 
790
          *(dest++) = src[1];
 
791
          *(dest++) = src[0];
 
792
          *(dest++) = src[3];
 
793
 
 
794
          src += bytes;
 
795
        }
 
796
    }
 
797
  else
 
798
    {
 
799
      for (x = 0; x < width; x++)
 
800
        {
 
801
          *(dest++) = src[2];
 
802
          *(dest++) = src[1];
 
803
          *(dest++) = src[0];
 
804
 
 
805
          src += bytes;
 
806
        }
 
807
    }
 
808
}
 
809
 
 
810
static void
 
811
read_line (FILE         *fp,
 
812
           guchar       *row,
 
813
           guchar       *buffer,
 
814
           tga_info     *info,
 
815
           GimpDrawable *drawable)
 
816
{
 
817
  if (info->imageCompression == TGA_COMP_RLE)
 
818
    {
 
819
      rle_read (fp, buffer, info);
 
820
    }
 
821
  else
 
822
    {
 
823
      fread (buffer, info->bytes, info->width, fp);
 
824
    }
 
825
 
 
826
  if (info->flipHoriz)
 
827
    {
 
828
      flip_line (buffer, info);
 
829
    }
 
830
 
 
831
  if (info->imageType == TGA_TYPE_COLOR)
 
832
    {
 
833
      if (info->bpp == 16 || info->bpp == 15)
 
834
        {
 
835
          upsample (row, buffer, info->width, info->bytes, info->alphaBits);
 
836
        }
 
837
      else
 
838
        {
 
839
          bgr2rgb (row, buffer,info->width,info->bytes,info->alphaBits);
 
840
        }
 
841
    }
 
842
  else
 
843
    {
 
844
      memcpy (row, buffer, info->width * drawable->bpp);
 
845
    }
 
846
}
 
847
 
 
848
static gint32
 
849
ReadImage (FILE        *fp,
 
850
           tga_info    *info,
 
851
           const gchar *filename)
 
852
{
 
853
  static gint32 image_ID;
 
854
  gint32        layer_ID;
 
855
 
 
856
  GimpPixelRgn       pixel_rgn;
 
857
  GimpDrawable      *drawable;
 
858
  guchar            *data, *buffer, *row;
 
859
  GimpImageType      dtype = 0;
 
860
  GimpImageBaseType  itype = 0;
 
861
  gint               i, y;
 
862
 
 
863
  gint max_tileheight, tileheight;
 
864
 
 
865
  guint  cmap_bytes;
 
866
  guchar tga_cmap[4 * 256], gimp_cmap[3 * 256];
 
867
 
 
868
  switch (info->imageType)
 
869
    {
 
870
    case TGA_TYPE_MAPPED:
 
871
      itype = GIMP_INDEXED;
 
872
 
 
873
      if (info->alphaBits)
 
874
        dtype = GIMP_INDEXEDA_IMAGE;
 
875
      else
 
876
        dtype = GIMP_INDEXED_IMAGE;
 
877
      break;
 
878
 
 
879
    case TGA_TYPE_GRAY:
 
880
      itype = GIMP_GRAY;
 
881
 
 
882
      if (info->alphaBits)
 
883
        dtype = GIMP_GRAYA_IMAGE;
 
884
      else
 
885
        dtype = GIMP_GRAY_IMAGE;
 
886
      break;
 
887
 
 
888
    case TGA_TYPE_COLOR:
 
889
      itype = GIMP_RGB;
 
890
 
 
891
      if (info->alphaBits)
 
892
        dtype = GIMP_RGBA_IMAGE;
 
893
      else
 
894
        dtype = GIMP_RGB_IMAGE;
 
895
      break;
 
896
    }
 
897
 
 
898
  /* Handle colormap */
 
899
 
 
900
  if (info->colorMapType == 1)
 
901
    {
 
902
      cmap_bytes= (info->colorMapSize + 7 ) / 8;
 
903
      if (cmap_bytes <= 4 &&
 
904
          fread (tga_cmap, info->colorMapLength * cmap_bytes, 1, fp) == 1)
 
905
        {
 
906
          if (info->colorMapSize == 32)
 
907
            bgr2rgb(gimp_cmap, tga_cmap, info->colorMapLength, cmap_bytes, 1);
 
908
          else if (info->colorMapSize == 24)
 
909
            bgr2rgb(gimp_cmap, tga_cmap, info->colorMapLength, cmap_bytes, 0);
 
910
          else if (info->colorMapSize == 16 || info->colorMapSize == 15)
 
911
            upsample(gimp_cmap, tga_cmap, info->colorMapLength, cmap_bytes, info->alphaBits);
 
912
 
 
913
        }
 
914
      else
 
915
        {
 
916
          g_message ("File '%s' is truncated or corrupted",
 
917
                     gimp_filename_to_utf8 (filename));
 
918
          return -1;
 
919
        }
 
920
    }
 
921
 
 
922
  image_ID = gimp_image_new (info->width, info->height, itype);
 
923
  gimp_image_set_filename (image_ID, filename);
 
924
 
 
925
  if (info->colorMapType == 1)
 
926
    gimp_image_set_colormap(image_ID, gimp_cmap, info->colorMapLength);
 
927
 
 
928
  layer_ID = gimp_layer_new (image_ID,
 
929
                             _("Background"),
 
930
                             info->width, info->height,
 
931
                             dtype, 100,
 
932
                             GIMP_NORMAL_MODE);
 
933
 
 
934
  gimp_image_add_layer (image_ID, layer_ID, 0);
 
935
 
 
936
  drawable = gimp_drawable_get (layer_ID);
 
937
 
 
938
  /* Prepare the pixel region. */
 
939
  gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0,
 
940
                        info->width, info->height, TRUE, FALSE);
 
941
 
 
942
  /* Allocate the data. */
 
943
  max_tileheight = gimp_tile_height ();
 
944
  data = (guchar *) g_malloc (info->width * max_tileheight * drawable->bpp);
 
945
  buffer = (guchar *) g_malloc (info->width * info->bytes);
 
946
 
 
947
  if (info->flipVert)
 
948
    {
 
949
      for (i = 0; i < info->height; i += tileheight)
 
950
        {
 
951
          tileheight = i ? max_tileheight : (info->height % max_tileheight);
 
952
          if (tileheight == 0) tileheight= max_tileheight;
 
953
 
 
954
          for (y= 1; y <= tileheight; ++y)
 
955
            {
 
956
              row= data + (info->width * drawable->bpp * (tileheight - y));
 
957
              read_line(fp, row, buffer, info, drawable);
 
958
            }
 
959
 
 
960
            gimp_progress_update ((double) (i + tileheight) /
 
961
                                  (double) info->height);
 
962
            gimp_pixel_rgn_set_rect (&pixel_rgn, data, 0,
 
963
                                     info->height - i - tileheight,
 
964
                                     info->width, tileheight);
 
965
        }
 
966
    }
 
967
  else
 
968
    {
 
969
      for (i = 0; i < info->height; i += max_tileheight)
 
970
        {
 
971
          tileheight = MIN (max_tileheight, info->height - i);
 
972
 
 
973
          for (y= 0; y < tileheight; ++y)
 
974
            {
 
975
              row= data + (info->width * drawable->bpp * y);
 
976
              read_line(fp, row, buffer, info, drawable);
 
977
            }
 
978
 
 
979
            gimp_progress_update ((double) (i + tileheight) /
 
980
                                  (double) info->height);
 
981
            gimp_pixel_rgn_set_rect (&pixel_rgn, data, 0, i,
 
982
                                     info->width, tileheight);
 
983
        }
 
984
    }
 
985
 
 
986
  g_free (data);
 
987
  g_free (buffer);
 
988
 
 
989
  gimp_drawable_flush (drawable);
 
990
  gimp_drawable_detach (drawable);
 
991
 
 
992
  return image_ID;
 
993
}  /*read_image*/
 
994
 
 
995
 
 
996
static gint
 
997
save_image (const gchar *filename,
 
998
            gint32       image_ID,
 
999
            gint32       drawable_ID)
 
1000
{
 
1001
  GimpPixelRgn   pixel_rgn;
 
1002
  GimpDrawable  *drawable;
 
1003
  GimpImageType  dtype;
 
1004
  gint           width;
 
1005
  gint           height;
 
1006
 
 
1007
  FILE     *fp;
 
1008
  gchar    *name_buf;
 
1009
  gint      tileheight;
 
1010
  gint      out_bpp = 0;
 
1011
  gboolean  status  = TRUE;
 
1012
  gint      i, row;
 
1013
 
 
1014
  guchar  header[18];
 
1015
  guchar  footer[26];
 
1016
  guchar *pixels;
 
1017
  guchar *data;
 
1018
 
 
1019
  gint    num_colors;
 
1020
  guchar *gimp_cmap = NULL;
 
1021
 
 
1022
  drawable = gimp_drawable_get (drawable_ID);
 
1023
  dtype    = gimp_drawable_type (drawable_ID);
 
1024
 
 
1025
  width  = drawable->width;
 
1026
  height = drawable->height;
 
1027
 
 
1028
  if ((fp = fopen (filename, "wb")) == NULL)
 
1029
    {
 
1030
      g_message (_("Could not open '%s' for writing: %s"),
 
1031
                 gimp_filename_to_utf8 (filename), g_strerror (errno));
 
1032
      return FALSE;
 
1033
    }
 
1034
 
 
1035
  name_buf = g_strdup_printf (_("Saving '%s'..."),
 
1036
                              gimp_filename_to_utf8 (filename));
 
1037
  gimp_progress_init (name_buf);
 
1038
  g_free (name_buf);
 
1039
 
 
1040
  header[0] = 0; /* No image identifier / description */
 
1041
 
 
1042
  if (dtype == GIMP_INDEXED_IMAGE || dtype == GIMP_INDEXEDA_IMAGE)
 
1043
    {
 
1044
      gimp_cmap = gimp_image_get_colormap (image_ID, &num_colors);
 
1045
 
 
1046
      header[1] = 1; /* cmap type */
 
1047
      header[2] = (tsvals.rle) ? 9 : 1;
 
1048
      header[3] = header[4]= 0; /* no offset */
 
1049
      header[5] = num_colors % 256;
 
1050
      header[6] = num_colors / 256;
 
1051
      header[7] = 24; /* cmap size / bits */
 
1052
    }
 
1053
  else
 
1054
    {
 
1055
      header[1]= 0;
 
1056
 
 
1057
      if (dtype == GIMP_RGB_IMAGE || dtype == GIMP_RGBA_IMAGE)
 
1058
        {
 
1059
          header[2]= (tsvals.rle) ? 10 : 2;
 
1060
        }
 
1061
      else
 
1062
        {
 
1063
          header[2]= (tsvals.rle) ? 11 : 3;
 
1064
        }
 
1065
 
 
1066
      header[3] = header[4] = header[5] = header[6] = header[7] = 0;
 
1067
    }
 
1068
 
 
1069
  header[8]  = header[9]  = 0; /* xorigin */
 
1070
  header[10] = header[11] = 0; /* yorigin */
 
1071
 
 
1072
  header[12] = width % 256;
 
1073
  header[13] = width / 256;
 
1074
 
 
1075
  header[14] = height % 256;
 
1076
  header[15] = height / 256;
 
1077
 
 
1078
  switch (dtype)
 
1079
    {
 
1080
    case GIMP_INDEXED_IMAGE:
 
1081
    case GIMP_GRAY_IMAGE:
 
1082
    case GIMP_INDEXEDA_IMAGE:
 
1083
      out_bpp = 1;
 
1084
      header[16] = 8; /* bpp */
 
1085
      header[17] = (tsvals.origin) ? 0 :  0x20; /* alpha + orientation */
 
1086
      break;
 
1087
 
 
1088
    case GIMP_GRAYA_IMAGE:
 
1089
      out_bpp = 2;
 
1090
      header[16] = 16; /* bpp */
 
1091
      header[17] = (tsvals.origin) ? 8 : 0x28; /* alpha + orientation */
 
1092
      break;
 
1093
 
 
1094
    case GIMP_RGB_IMAGE:
 
1095
      out_bpp = 3;
 
1096
      header[16] = 24; /* bpp */
 
1097
      header[17] = (tsvals.origin) ? 0 : 0x20; /* alpha + orientation */
 
1098
      break;
 
1099
 
 
1100
    case GIMP_RGBA_IMAGE:
 
1101
      out_bpp = 4;
 
1102
      header[16] = 32; /* bpp */
 
1103
      header[17] = (tsvals.origin) ? 8 : 0x28; /* alpha + orientation */
 
1104
      break;
 
1105
    }
 
1106
 
 
1107
  /* write header to front of file */
 
1108
  fwrite (header, sizeof (header), 1, fp);
 
1109
 
 
1110
  if (dtype == GIMP_INDEXED_IMAGE || dtype == GIMP_INDEXEDA_IMAGE)
 
1111
    {
 
1112
      /* write out palette */
 
1113
      for (i= 0; i < num_colors; ++i)
 
1114
        {
 
1115
          fputc (gimp_cmap[(i * 3) + 2], fp);
 
1116
          fputc (gimp_cmap[(i * 3) + 1], fp);
 
1117
          fputc (gimp_cmap[(i * 3) + 0], fp);
 
1118
        }
 
1119
    }
 
1120
 
 
1121
  /* Allocate a new set of pixels. */
 
1122
  tileheight = gimp_tile_height ();
 
1123
 
 
1124
  gimp_pixel_rgn_init (&pixel_rgn, drawable, 0, 0, width, height, FALSE, FALSE);
 
1125
 
 
1126
  pixels = g_new (guchar, width * drawable->bpp);
 
1127
  data   = g_new (guchar, width * out_bpp);
 
1128
 
 
1129
  for (row = 0; row < height; ++row)
 
1130
    {
 
1131
      if(tsvals.origin)
 
1132
        {
 
1133
          gimp_pixel_rgn_get_rect (&pixel_rgn, pixels, 0, height-(row+1), width, 1);
 
1134
        }
 
1135
      else
 
1136
        {
 
1137
          gimp_pixel_rgn_get_rect (&pixel_rgn, pixels, 0, row, width, 1);
 
1138
        }
 
1139
 
 
1140
      if (dtype == GIMP_RGB_IMAGE)
 
1141
        {
 
1142
          bgr2rgb(data, pixels, width, drawable->bpp, 0);
 
1143
        }
 
1144
      else if (dtype == GIMP_RGBA_IMAGE)
 
1145
        {
 
1146
          bgr2rgb(data, pixels, width, drawable->bpp, 1);
 
1147
        }
 
1148
      else if (dtype == GIMP_INDEXEDA_IMAGE)
 
1149
        {
 
1150
          for (i = 0; i < width; ++i)
 
1151
            {
 
1152
              data[i]= pixels[i*2];
 
1153
            }
 
1154
        }
 
1155
      else
 
1156
        {
 
1157
          memcpy (data, pixels, width * drawable->bpp);
 
1158
        }
 
1159
 
 
1160
      if (tsvals.rle)
 
1161
        {
 
1162
          rle_write (fp, data, width, out_bpp);
 
1163
        }
 
1164
      else
 
1165
        {
 
1166
          fwrite (data, width * out_bpp, 1, fp);
 
1167
        }
 
1168
 
 
1169
      gimp_progress_update ((gdouble) row / (gdouble) height);
 
1170
    }
 
1171
 
 
1172
  gimp_drawable_detach (drawable);
 
1173
  g_free (data);
 
1174
 
 
1175
  /* footer must be the last thing written to file */
 
1176
  memset (footer, 0, 8); /* No extensions, no developer directory */
 
1177
  memcpy (footer + 8, magic, sizeof (magic)); /* magic signature */
 
1178
  fwrite (footer, sizeof (footer), 1, fp);
 
1179
 
 
1180
  fclose (fp);
 
1181
 
 
1182
  return status;
 
1183
}
 
1184
 
 
1185
static gboolean
 
1186
save_dialog (void)
 
1187
{
 
1188
  GtkWidget *dlg;
 
1189
  GtkWidget *toggle;
 
1190
  GtkWidget *origin;
 
1191
  GtkWidget *vbox;
 
1192
  gboolean   run;
 
1193
 
 
1194
  dlg = gimp_dialog_new (_("Save as TGA"), "tga",
 
1195
                         NULL, 0,
 
1196
                         gimp_standard_help_func, "file-tga-save",
 
1197
 
 
1198
                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 
1199
                         GTK_STOCK_OK,     GTK_RESPONSE_OK,
 
1200
 
 
1201
                         NULL);
 
1202
 
 
1203
  vbox = gtk_vbox_new (FALSE, 12);
 
1204
  gtk_container_set_border_width (GTK_CONTAINER (vbox), 12);
 
1205
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), vbox, TRUE, TRUE, 0);
 
1206
  gtk_widget_show (vbox);
 
1207
 
 
1208
  /*  rle  */
 
1209
  toggle = gtk_check_button_new_with_mnemonic (_("_RLE compression"));
 
1210
  gtk_box_pack_start (GTK_BOX (vbox), toggle, FALSE, FALSE, 0);
 
1211
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), tsvals.rle);
 
1212
  gtk_widget_show (toggle);
 
1213
 
 
1214
  g_signal_connect (toggle, "toggled",
 
1215
                    G_CALLBACK (gimp_toggle_button_update),
 
1216
                    &tsvals.rle);
 
1217
 
 
1218
  /*  origin  */
 
1219
  origin = gtk_check_button_new_with_mnemonic (_("Or_igin at bottom left"));
 
1220
  gtk_box_pack_start (GTK_BOX (vbox), origin, FALSE, FALSE, 0);
 
1221
  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (origin), tsvals.origin);
 
1222
  gtk_widget_show (origin);
 
1223
 
 
1224
  g_signal_connect (origin, "toggled",
 
1225
                    G_CALLBACK (gimp_toggle_button_update),
 
1226
                    &tsvals.origin);
 
1227
 
 
1228
  gtk_widget_show (dlg);
 
1229
 
 
1230
  run = (gimp_dialog_run (GIMP_DIALOG (dlg)) == GTK_RESPONSE_OK);
 
1231
 
 
1232
  gtk_widget_destroy (dlg);
 
1233
 
 
1234
  return run;
 
1235
}