1 /*
2 * Copyright (c) 2008-2010 Travis Geiselbrecht
3 *
4 * Use of this source code is governed by a MIT-style
5 * license that can be found in the LICENSE file or at
6 * https://opensource.org/licenses/MIT
7 */
8
9 /**
10 * @file
11 * @brief Parse tga format files
12 *
13 * @ingroup graphics
14 */
15
16 #include <lk/debug.h>
17 #include <lk/trace.h>
18 #include <assert.h>
19 #include <lk/compiler.h>
20 #include <lib/tga.h>
21
22 #define LOCAL_TRACE 0
23
24 struct tga_header {
25 uint8_t idlength;
26 uint8_t colormaptype;
27 uint8_t datatypecode;
28 uint16_t colormaporigin;
29 uint16_t colormaplength;
30 uint8_t colormapdepth;
31 uint16_t x_origin;
32 uint16_t y_origin;
33 uint16_t width;
34 uint16_t height;
35 uint8_t bitsperpixel;
36 uint8_t imagedescriptor;
37 } __PACKED;
38
print_tga_info(const struct tga_header * header)39 static void print_tga_info(const struct tga_header *header) {
40 LTRACEF("idlength %hhd\n", header->idlength);
41 LTRACEF("colormaptype %hhd\n", header->colormaptype);
42 LTRACEF("datatypecode %hhd\n", header->datatypecode);
43 LTRACEF("colormaporigin %hd\n", header->colormaporigin);
44 LTRACEF("colormaplength %hd\n", header->colormaplength);
45 LTRACEF("colormapdepth %hhd\n", header->colormapdepth);
46 LTRACEF("x_origin %hd\n", header->x_origin);
47 LTRACEF("y_origin %hd\n", header->y_origin);
48 LTRACEF("width %hd\n", header->width);
49 LTRACEF("height %hd\n", header->height);
50 LTRACEF("bitsperpixel %hhd\n", header->bitsperpixel);
51 LTRACEF("imagedescriptor %hhd\n", header->imagedescriptor);
52
53 }
54
decode_2byte(gfx_surface * surface,uint x,uint y,const void * input)55 static void decode_2byte(gfx_surface *surface, uint x, uint y, const void *input) {
56 const uint8_t *in = (const uint8_t *)input;
57
58 // printf("in 0x%hhx 0x%hhx\n", in[0], in[1]);
59 uint r,g,b;
60
61 b = (in[0] & 0x1f) << 3;
62 g = (((in[0] >> 5) & 0x7) | ((in[1] & 0x3) << 3)) << 3;
63 r = ((in[1] >> 2) & 0x1f) << 3;
64
65 gfx_putpixel(surface, x, y, 0xff000000 | r << 16 | g << 8 | b);
66 }
67
decode_3byte(gfx_surface * surface,uint x,uint y,const void * input)68 static void decode_3byte(gfx_surface *surface, uint x, uint y, const void *input) {
69 const uint8_t *in = (const uint8_t *)input;
70
71 // printf("in 0x%hhx 0x%hhx\n", in[0], in[1]);
72
73 gfx_putpixel(surface, x, y, 0xff000000 | in[2] << 16 | in[1] << 8 | in[0]);
74 }
75
decode_4byte(gfx_surface * surface,uint x,uint y,const void * input)76 static void decode_4byte(gfx_surface *surface, uint x, uint y, const void *input) {
77 const uint8_t *in = (const uint8_t *)input;
78
79 // printf("in 0x%hhx 0x%hhx 0x%hhx 0x%hhx\n", in[0], in[1], in[2], in[3]);
80
81 if (in[3] == 0)
82 gfx_putpixel(surface, x, y, 0);
83 else
84 gfx_putpixel(surface, x, y, in[3] << 24 | in[2] << 16 | in[1] << 8 | in[0]);
85 }
86
87 /**
88 * @brief Decode a tga image
89 *
90 * @param ptr Pointer to tga data in memory
91 * @param len Length of tga data
92 * @param format Desired format of returned graphics surface
93 *
94 * @return Graphics surface or NULL on error.
95 *
96 * @ingroup graphics
97 */
tga_decode(const void * ptr,size_t len,gfx_format format)98 gfx_surface *tga_decode(const void *ptr, size_t len, gfx_format format) {
99 const struct tga_header *header = (const struct tga_header *)ptr;
100
101 LTRACEF("ptr %p, len %zu\n", ptr, len);
102
103 #if LOCAL_TRACE > 0
104 print_tga_info(header);
105 #endif
106
107 /* do some sanity checks */
108 if (header->datatypecode != 2 && header->datatypecode != 10) {
109 dprintf(INFO, "tga_decode: unknown data type %d\n", header->datatypecode);
110 return NULL;
111 }
112 if (header->bitsperpixel != 16 && header->bitsperpixel != 24 && header->bitsperpixel != 32) {
113 dprintf(INFO, "tga_decode: unsupported bits per pixel %d\n", header->bitsperpixel);
114 return NULL;
115 }
116 if (header->colormaptype != 0) {
117 dprintf(INFO, "tga_decode: has colormap, can't handle\n");
118 return NULL;
119 }
120
121 const void *imagestart = ((const uint8_t *)ptr + sizeof(struct tga_header) + header->idlength);
122
123 /* create a surface to hold the decoded bits */
124 gfx_surface *surface = gfx_create_surface(NULL, header->width, header->height, header->width, format);
125 DEBUG_ASSERT(surface);
126
127 /* copy the bits out */
128 void (*decodefunc)(gfx_surface *, uint x, uint y, const void *) = NULL;
129
130 uint step = 1;
131 if (header->bitsperpixel == 16) {
132 step = 2;
133 decodefunc = decode_2byte;
134 } else if (header->bitsperpixel == 24) {
135 step = 3;
136 decodefunc = decode_3byte;
137 } else if (header->bitsperpixel == 32) {
138 step = 4;
139 decodefunc = decode_4byte;
140 }
141
142 if (header->datatypecode == 2) {
143 /* no RLE */
144 uint pos = 0;
145 uint x, y;
146 uint surfacey;
147
148 for (y = 0; y < header->height; y++) {
149
150 if ((header->imagedescriptor & (1 << 5)) == 0)
151 surfacey = (surface->height - 1) - y;
152 else
153 surfacey = y;
154
155 for (x = 0; x < header->width; x++) {
156 decodefunc(surface, x, surfacey, (const uint8_t *)imagestart + pos);
157 pos += step;
158 }
159 }
160 } else if (header->datatypecode == 10) {
161 /* RLE compression */
162 uint pos = 0;
163 uint count = 0;
164 uint x, y;
165
166 x = 0;
167 if ((header->imagedescriptor & (1 << 5)) == 0)
168 y = header->height - 1;
169 else
170 y = 0;
171
172 while (count < (uint)header->height * (uint)header->width) {
173 uint runpos;
174
175 uint8_t run = *((const uint8_t *)imagestart + pos);
176 bool repeat_run = (run & 0x80);
177 uint runlen = (run & 0x7f) + 1;
178
179 // printf("pos 0x%x count %u run 0x%hhx runtype %d runlen %u\n", pos, count, run, run & 0x80, runlen);
180
181 /* consume the run byte */
182 pos++;
183
184 /* start of a run */
185 for (runpos = 0; runpos < runlen; runpos++) {
186 decodefunc(surface, x, y, (const uint8_t *)imagestart + pos);
187 count++;
188
189 x++;
190 if (x == surface->width) {
191 if ((header->imagedescriptor & (1 << 5)) == 0)
192 y--;
193 else
194 y++;
195 x = 0;
196 }
197
198 /* if a run of raw pixels, consume an input pixel */
199 if (!repeat_run)
200 pos += step;
201 }
202 /* if this was a run of repeated pixels, consume the one input pixel we repeated */
203 if (repeat_run)
204 pos += step;
205
206 }
207 // printf("done with RLE: x %d, y %d, pos %d, count %d\n", x, y, pos, count);
208 }
209
210 return surface;
211 }
212
213