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