prepare for the DOS port
[retroray] / libs / imago / src / filetga.c
1 /*
2 libimago - a multi-format image file input/output library.
3 Copyright (C) 2010-2021 John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published
7 by the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /* -- Targa (tga) module -- */
20
21 #include <string.h>
22 #include <stdlib.h>
23 #include "imago2.h"
24 #include "ftmodule.h"
25 #include "byteord.h"
26
27 enum {
28         IMG_NONE,
29         IMG_CMAP,
30         IMG_RGBA,
31         IMG_BW,
32
33         IMG_RLE_CMAP = 9,
34         IMG_RLE_RGBA,
35         IMG_RLE_BW
36 };
37
38 #define IS_RLE(x)       ((x) >= IMG_RLE_CMAP)
39 #define IS_RGBA(x)      ((x) == IMG_RGBA || (x) == IMG_RLE_RGBA)
40
41
42 struct tga_header {
43         uint8_t idlen;                  /* id field length */
44         uint8_t cmap_type;              /* color map type (0:no color map, 1:color map present) */
45         uint8_t img_type;               /* image type:
46                                                          * 0: no image data
47                                                          *      1: uncomp. color-mapped          9: RLE color-mapped
48                                                          *      2: uncomp. true color           10: RLE true color
49                                                          *      3: uncomp. black/white          11: RLE black/white */
50         uint16_t cmap_first;    /* color map first entry index */
51         uint16_t cmap_len;              /* color map length */
52         uint8_t cmap_entry_sz;  /* color map entry size */
53         uint16_t img_x;                 /* X-origin of the image */
54         uint16_t img_y;                 /* Y-origin of the image */
55         uint16_t img_width;             /* image width */
56         uint16_t img_height;    /* image height */
57         uint8_t img_bpp;                /* bits per pixel */
58         uint8_t img_desc;               /* descriptor:
59                                                          * bits 0 - 3: alpha or overlay bits
60                                                          * bits 5 & 4: origin (0 = bottom/left, 1 = top/right)
61                                                          * bits 7 & 6: data interleaving */
62 };
63
64 struct tga_footer {
65         uint32_t ext_off;               /* extension area offset */
66         uint32_t devdir_off;    /* developer directory offset */
67         char sig[18];                           /* signature with . and \0 */
68 };
69
70
71 static int check(struct img_io *io);
72 static int read_tga(struct img_pixmap *img, struct img_io *io);
73 static int write_tga(struct img_pixmap *img, struct img_io *io);
74 static int read_pixel(struct img_io *io, int fmt, unsigned char *pix);
75
76 int img_register_tga(void)
77 {
78         static struct ftype_module mod = {".tga:.targa", check, read_tga, write_tga};
79         return img_register_module(&mod);
80 }
81
82
83 static int check(struct img_io *io)
84 {
85         struct tga_footer foot;
86         int res = -1;
87         long pos = io->seek(0, SEEK_CUR, io->uptr);
88         io->seek(-18, SEEK_END, io->uptr);
89
90         if(io->read(foot.sig, 17, io->uptr) < 17) {
91                 io->seek(pos, SEEK_SET, io->uptr);
92                 return -1;
93         }
94
95         if(memcmp(foot.sig, "TRUEVISION-XFILE.", 17) == 0) {
96                 res = 0;
97         }
98         io->seek(pos, SEEK_SET, io->uptr);
99         return res;
100 }
101
102 static int iofgetc(struct img_io *io)
103 {
104         int c = 0;
105         return io->read(&c, 1, io->uptr) < 1 ? -1 : c;
106 }
107
108 static int read_tga(struct img_pixmap *img, struct img_io *io)
109 {
110         struct tga_header hdr;
111         unsigned long x, y;
112         int i, idx, c, r, g, b;
113         int rle_mode = 0, rle_pix_left = 0;
114         int pixel_bytes;
115         int fmt;
116         struct img_colormap cmap;
117
118         /* read header */
119         hdr.idlen = iofgetc(io);
120         hdr.cmap_type = iofgetc(io);
121         hdr.img_type = iofgetc(io);
122         hdr.cmap_first = img_read_int16_le(io);
123         hdr.cmap_len = img_read_int16_le(io);
124         hdr.cmap_entry_sz = iofgetc(io);
125         hdr.img_x = img_read_int16_le(io);
126         hdr.img_y = img_read_int16_le(io);
127         hdr.img_width = img_read_int16_le(io);
128         hdr.img_height = img_read_int16_le(io);
129         hdr.img_bpp = iofgetc(io);
130         if((c = iofgetc(io)) == -1) {
131                 return -1;
132         }
133         hdr.img_desc = c;
134
135         io->seek(hdr.idlen, SEEK_CUR, io->uptr);        /* skip the image ID */
136
137         /* read the color map if it exists */
138         if(hdr.cmap_type == 1) {
139                 cmap.ncolors = hdr.cmap_len;
140
141                 for(i=0; i<(int)hdr.cmap_len; i++) {
142                         switch(hdr.cmap_entry_sz) {
143                         case 16:
144                                 c = img_read_int16_le(io);
145                                 r = (c & 0x7c00) >> 7;
146                                 g = (c & 0x03e0) >> 2;
147                                 b = (c & 0x001f) << 3;
148                                 break;
149
150                         case 24:
151                                 b = iofgetc(io);
152                                 g = iofgetc(io);
153                                 r = iofgetc(io);
154                                 break;
155
156                         case 32:
157                                 b = iofgetc(io);
158                                 g = iofgetc(io);
159                                 r = iofgetc(io);
160                                 iofgetc(io);    /* ignore attribute byte */
161                         }
162
163                         idx = i + hdr.cmap_first;
164                         if(idx < 256) {
165                                 cmap.color[idx].r = r;
166                                 cmap.color[idx].g = g;
167                                 cmap.color[idx].b = b;
168                                 if(cmap.ncolors <= idx) cmap.ncolors = idx + 1;
169                         }
170                 }
171         }
172
173         x = hdr.img_width;
174         y = hdr.img_height;
175
176         if(hdr.img_type == IMG_CMAP || hdr.img_type == IMG_RLE_CMAP) {
177                 if(hdr.img_bpp != 8) {
178                         fprintf(stderr, "read_tga: indexed images with more than 8bpp not supported\n");
179                         return -1;
180                 }
181                 pixel_bytes = 1;
182                 fmt = IMG_FMT_IDX8;
183         } else {
184                 int alpha = hdr.img_desc & 0xf;
185                 pixel_bytes = alpha ? 4 : 3;
186                 fmt = alpha ? IMG_FMT_RGBA32 : IMG_FMT_RGB24;
187         }
188
189         if(img_set_pixels(img, x, y, fmt, 0) == -1) {
190                 return -1;
191         }
192
193         for(i=0; i<(int)y; i++) {
194                 unsigned char *ptr;
195                 int j, k;
196
197                 ptr = (unsigned char*)img->pixels + ((hdr.img_desc & 0x20) ? i : y - (i + 1)) * x * pixel_bytes;
198
199                 for(j=0; j<(int)x; j++) {
200                         /* if the image is raw, then just read the next pixel */
201                         if(!IS_RLE(hdr.img_type)) {
202                                 if(read_pixel(io, fmt, ptr) == -1) {
203                                         return -1;
204                                 }
205                         } else {
206                                 /* otherwise, for RLE... */
207
208                                 /* if we have pixels left in the packet ... */
209                                 if(rle_pix_left) {
210                                         /* if it's a raw packet, read the next pixel, otherwise keep the same */
211                                         if(!rle_mode) {
212                                                 if(read_pixel(io, fmt, ptr) == -1) {
213                                                         return -1;
214                                                 }
215                                         } else {
216                                                 for(k=0; k<pixel_bytes; k++) {
217                                                         ptr[k] = ptr[k - pixel_bytes];
218                                                 }
219                                         }
220                                         --rle_pix_left;
221                                 } else {
222                                         /* read RLE packet header */
223                                         unsigned char phdr = iofgetc(io);
224                                         rle_mode = (phdr & 128);                /* last bit shows the mode for this packet (1: rle, 0: raw) */
225                                         rle_pix_left = (phdr & ~128);   /* the rest gives the count of pixels minus one (we also read one here, so no +1) */
226                                         /* and read the first pixel of the packet */
227                                         if(read_pixel(io, fmt, ptr) == -1) {
228                                                 return -1;
229                                         }
230                                 }
231                         }
232
233                         ptr += pixel_bytes;
234                 }
235         }
236
237         if(fmt == IMG_FMT_IDX8) {
238                 struct img_colormap *dest = img_colormap(img);
239                 *dest = cmap;
240         }
241
242         return 0;
243 }
244
245 static int write_tga(struct img_pixmap *img, struct img_io *io)
246 {
247         return -1;      /* TODO */
248 }
249
250 static int read_pixel(struct img_io *io, int fmt, unsigned char *pix)
251 {
252         int r, g, b, a;
253
254         if(fmt == IMG_FMT_IDX8) {
255                 if((b = iofgetc(io)) == -1) {
256                         return -1;
257                 }
258                 *pix = b;
259                 return 0;
260         }
261
262         if((b = iofgetc(io)) == -1 || (g = iofgetc(io)) == -1 || (r = iofgetc(io)) == -1) {
263                 return -1;
264         }
265
266         pix[0] = r;
267         pix[1] = g;
268         pix[2] = b;
269
270         if(fmt == IMG_FMT_RGBA32) {
271                 if((a = iofgetc(io)) == -1) {
272                         return -1;
273                 }
274                 pix[3] = a;
275         }
276         return 0;
277 }