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