Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Targa (.tga) image decoder | |
3 | * Copyright (c) 2006 Konstantin Shishkov | |
4 | * | |
5 | * This file is part of FFmpeg. | |
6 | * | |
7 | * FFmpeg is free software; you can redistribute it and/or | |
8 | * modify it under the terms of the GNU Lesser General Public | |
9 | * License as published by the Free Software Foundation; either | |
10 | * version 2.1 of the License, or (at your option) any later version. | |
11 | * | |
12 | * FFmpeg is distributed in the hope that it will be useful, | |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * Lesser General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU Lesser General Public | |
18 | * License along with FFmpeg; if not, write to the Free Software | |
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
20 | */ | |
21 | ||
22 | #include "libavutil/intreadwrite.h" | |
23 | #include "libavutil/imgutils.h" | |
24 | #include "avcodec.h" | |
25 | #include "bytestream.h" | |
26 | #include "internal.h" | |
27 | #include "targa.h" | |
28 | ||
29 | typedef struct TargaContext { | |
30 | GetByteContext gb; | |
31 | } TargaContext; | |
32 | ||
33 | static uint8_t *advance_line(uint8_t *start, uint8_t *line, | |
34 | int stride, int *y, int h, int interleave) | |
35 | { | |
36 | *y += interleave; | |
37 | ||
38 | if (*y < h) { | |
39 | return line + interleave * stride; | |
40 | } else { | |
41 | *y = (*y + 1) & (interleave - 1); | |
42 | if (*y && *y < h) { | |
43 | return start + *y * stride; | |
44 | } else { | |
45 | return NULL; | |
46 | } | |
47 | } | |
48 | } | |
49 | ||
50 | static int targa_decode_rle(AVCodecContext *avctx, TargaContext *s, | |
51 | uint8_t *start, int w, int h, int stride, | |
52 | int bpp, int interleave) | |
53 | { | |
54 | int x, y; | |
55 | int depth = (bpp + 1) >> 3; | |
56 | int type, count; | |
57 | uint8_t *line = start; | |
58 | uint8_t *dst = line; | |
59 | ||
60 | x = y = count = 0; | |
61 | while (dst) { | |
62 | if (bytestream2_get_bytes_left(&s->gb) <= 0) { | |
63 | av_log(avctx, AV_LOG_ERROR, | |
64 | "Ran ouf of data before end-of-image\n"); | |
65 | return AVERROR_INVALIDDATA; | |
66 | } | |
67 | type = bytestream2_get_byteu(&s->gb); | |
68 | count = (type & 0x7F) + 1; | |
69 | type &= 0x80; | |
70 | if (!type) { | |
71 | do { | |
72 | int n = FFMIN(count, w - x); | |
73 | bytestream2_get_buffer(&s->gb, dst, n * depth); | |
74 | count -= n; | |
75 | dst += n * depth; | |
76 | x += n; | |
77 | if (x == w) { | |
78 | x = 0; | |
79 | dst = line = advance_line(start, line, stride, &y, h, interleave); | |
80 | } | |
81 | } while (dst && count > 0); | |
82 | } else { | |
83 | uint8_t tmp[4]; | |
84 | bytestream2_get_buffer(&s->gb, tmp, depth); | |
85 | do { | |
86 | int n = FFMIN(count, w - x); | |
87 | count -= n; | |
88 | x += n; | |
89 | do { | |
90 | memcpy(dst, tmp, depth); | |
91 | dst += depth; | |
92 | } while (--n); | |
93 | if (x == w) { | |
94 | x = 0; | |
95 | dst = line = advance_line(start, line, stride, &y, h, interleave); | |
96 | } | |
97 | } while (dst && count > 0); | |
98 | } | |
99 | } | |
100 | ||
101 | if (count) { | |
102 | av_log(avctx, AV_LOG_ERROR, "Packet went out of bounds\n"); | |
103 | return AVERROR_INVALIDDATA; | |
104 | } | |
105 | ||
106 | return 0; | |
107 | } | |
108 | ||
109 | static int decode_frame(AVCodecContext *avctx, | |
110 | void *data, int *got_frame, | |
111 | AVPacket *avpkt) | |
112 | { | |
113 | TargaContext * const s = avctx->priv_data; | |
114 | AVFrame * const p = data; | |
115 | uint8_t *dst; | |
116 | int stride; | |
117 | int idlen, pal, compr, y, w, h, bpp, flags, ret; | |
118 | int first_clr, colors, csize; | |
119 | int interleave; | |
120 | ||
121 | bytestream2_init(&s->gb, avpkt->data, avpkt->size); | |
122 | ||
123 | /* parse image header */ | |
124 | idlen = bytestream2_get_byte(&s->gb); | |
125 | pal = bytestream2_get_byte(&s->gb); | |
126 | compr = bytestream2_get_byte(&s->gb); | |
127 | first_clr = bytestream2_get_le16(&s->gb); | |
128 | colors = bytestream2_get_le16(&s->gb); | |
129 | csize = bytestream2_get_byte(&s->gb); | |
130 | bytestream2_skip(&s->gb, 4); /* 2: x, 2: y */ | |
131 | w = bytestream2_get_le16(&s->gb); | |
132 | h = bytestream2_get_le16(&s->gb); | |
133 | bpp = bytestream2_get_byte(&s->gb); | |
134 | ||
135 | if (bytestream2_get_bytes_left(&s->gb) <= idlen) { | |
136 | av_log(avctx, AV_LOG_ERROR, | |
137 | "Not enough data to read header\n"); | |
138 | return AVERROR_INVALIDDATA; | |
139 | } | |
140 | ||
141 | flags = bytestream2_get_byte(&s->gb); | |
142 | ||
143 | if (!pal && (first_clr || colors || csize)) { | |
144 | av_log(avctx, AV_LOG_WARNING, "File without colormap has colormap information set.\n"); | |
145 | // specification says we should ignore those value in this case | |
146 | first_clr = colors = csize = 0; | |
147 | } | |
148 | ||
149 | // skip identifier if any | |
150 | bytestream2_skip(&s->gb, idlen); | |
151 | ||
152 | switch (bpp) { | |
153 | case 8: | |
154 | avctx->pix_fmt = ((compr & (~TGA_RLE)) == TGA_BW) ? AV_PIX_FMT_GRAY8 : AV_PIX_FMT_PAL8; | |
155 | break; | |
156 | case 15: | |
157 | case 16: | |
158 | avctx->pix_fmt = AV_PIX_FMT_RGB555LE; | |
159 | break; | |
160 | case 24: | |
161 | avctx->pix_fmt = AV_PIX_FMT_BGR24; | |
162 | break; | |
163 | case 32: | |
164 | avctx->pix_fmt = AV_PIX_FMT_BGRA; | |
165 | break; | |
166 | default: | |
167 | av_log(avctx, AV_LOG_ERROR, "Bit depth %i is not supported\n", bpp); | |
168 | return AVERROR_INVALIDDATA; | |
169 | } | |
170 | ||
171 | if (colors && (colors + first_clr) > 256) { | |
172 | av_log(avctx, AV_LOG_ERROR, "Incorrect palette: %i colors with offset %i\n", colors, first_clr); | |
173 | return AVERROR_INVALIDDATA; | |
174 | } | |
175 | ||
176 | if ((ret = ff_set_dimensions(avctx, w, h)) < 0) | |
177 | return ret; | |
178 | ||
179 | if ((ret = ff_get_buffer(avctx, p, 0)) < 0) | |
180 | return ret; | |
181 | p->pict_type = AV_PICTURE_TYPE_I; | |
182 | ||
183 | if (flags & TGA_TOPTOBOTTOM) { | |
184 | dst = p->data[0]; | |
185 | stride = p->linesize[0]; | |
186 | } else { //image is upside-down | |
187 | dst = p->data[0] + p->linesize[0] * (h - 1); | |
188 | stride = -p->linesize[0]; | |
189 | } | |
190 | ||
191 | interleave = flags & TGA_INTERLEAVE2 ? 2 : | |
192 | flags & TGA_INTERLEAVE4 ? 4 : 1; | |
193 | ||
194 | if (colors) { | |
195 | int pal_size, pal_sample_size; | |
196 | ||
197 | switch (csize) { | |
198 | case 32: pal_sample_size = 4; break; | |
199 | case 24: pal_sample_size = 3; break; | |
200 | case 16: | |
201 | case 15: pal_sample_size = 2; break; | |
202 | default: | |
203 | av_log(avctx, AV_LOG_ERROR, "Palette entry size %i bits is not supported\n", csize); | |
204 | return AVERROR_INVALIDDATA; | |
205 | } | |
206 | pal_size = colors * pal_sample_size; | |
207 | if (avctx->pix_fmt != AV_PIX_FMT_PAL8) //should not occur but skip palette anyway | |
208 | bytestream2_skip(&s->gb, pal_size); | |
209 | else { | |
210 | int t; | |
211 | uint32_t *pal = ((uint32_t *)p->data[1]) + first_clr; | |
212 | ||
213 | if (bytestream2_get_bytes_left(&s->gb) < pal_size) { | |
214 | av_log(avctx, AV_LOG_ERROR, | |
215 | "Not enough data to read palette\n"); | |
216 | return AVERROR_INVALIDDATA; | |
217 | } | |
218 | switch (pal_sample_size) { | |
219 | case 4: | |
220 | for (t = 0; t < colors; t++) | |
221 | *pal++ = bytestream2_get_le32u(&s->gb); | |
222 | break; | |
223 | case 3: | |
224 | /* RGB24 */ | |
225 | for (t = 0; t < colors; t++) | |
226 | *pal++ = (0xffU<<24) | bytestream2_get_le24u(&s->gb); | |
227 | break; | |
228 | case 2: | |
229 | /* RGB555 */ | |
230 | for (t = 0; t < colors; t++) { | |
231 | uint32_t v = bytestream2_get_le16u(&s->gb); | |
232 | v = ((v & 0x7C00) << 9) | | |
233 | ((v & 0x03E0) << 6) | | |
234 | ((v & 0x001F) << 3); | |
235 | /* left bit replication */ | |
236 | v |= (v & 0xE0E0E0U) >> 5; | |
237 | *pal++ = (0xffU<<24) | v; | |
238 | } | |
239 | break; | |
240 | } | |
241 | p->palette_has_changed = 1; | |
242 | } | |
243 | } | |
244 | ||
245 | if ((compr & (~TGA_RLE)) == TGA_NODATA) { | |
246 | memset(p->data[0], 0, p->linesize[0] * h); | |
247 | } else { | |
248 | if (compr & TGA_RLE) { | |
249 | int res = targa_decode_rle(avctx, s, dst, w, h, stride, bpp, interleave); | |
250 | if (res < 0) | |
251 | return res; | |
252 | } else { | |
253 | size_t img_size = w * ((bpp + 1) >> 3); | |
254 | uint8_t *line; | |
255 | if (bytestream2_get_bytes_left(&s->gb) < img_size * h) { | |
256 | av_log(avctx, AV_LOG_ERROR, | |
257 | "Not enough data available for image\n"); | |
258 | return AVERROR_INVALIDDATA; | |
259 | } | |
260 | ||
261 | line = dst; | |
262 | y = 0; | |
263 | do { | |
264 | bytestream2_get_buffer(&s->gb, line, img_size); | |
265 | line = advance_line(dst, line, stride, &y, h, interleave); | |
266 | } while (line); | |
267 | } | |
268 | } | |
269 | ||
270 | if (flags & TGA_RIGHTTOLEFT) { // right-to-left, needs horizontal flip | |
271 | int x; | |
272 | for (y = 0; y < h; y++) { | |
273 | void *line = &p->data[0][y * p->linesize[0]]; | |
274 | for (x = 0; x < w >> 1; x++) { | |
275 | switch (bpp) { | |
276 | case 32: | |
277 | FFSWAP(uint32_t, ((uint32_t *)line)[x], ((uint32_t *)line)[w - x - 1]); | |
278 | break; | |
279 | case 24: | |
280 | FFSWAP(uint8_t, ((uint8_t *)line)[3 * x ], ((uint8_t *)line)[3 * w - 3 * x - 3]); | |
281 | FFSWAP(uint8_t, ((uint8_t *)line)[3 * x + 1], ((uint8_t *)line)[3 * w - 3 * x - 2]); | |
282 | FFSWAP(uint8_t, ((uint8_t *)line)[3 * x + 2], ((uint8_t *)line)[3 * w - 3 * x - 1]); | |
283 | break; | |
284 | case 16: | |
285 | FFSWAP(uint16_t, ((uint16_t *)line)[x], ((uint16_t *)line)[w - x - 1]); | |
286 | break; | |
287 | case 8: | |
288 | FFSWAP(uint8_t, ((uint8_t *)line)[x], ((uint8_t *)line)[w - x - 1]); | |
289 | } | |
290 | } | |
291 | } | |
292 | } | |
293 | ||
294 | *got_frame = 1; | |
295 | ||
296 | return avpkt->size; | |
297 | } | |
298 | ||
299 | AVCodec ff_targa_decoder = { | |
300 | .name = "targa", | |
301 | .long_name = NULL_IF_CONFIG_SMALL("Truevision Targa image"), | |
302 | .type = AVMEDIA_TYPE_VIDEO, | |
303 | .id = AV_CODEC_ID_TARGA, | |
304 | .priv_data_size = sizeof(TargaContext), | |
305 | .decode = decode_frame, | |
306 | .capabilities = CODEC_CAP_DR1, | |
307 | }; |