2 * Copyright (c) 2021 Paul B Mahol
4 * This file is part of FFmpeg.
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "libavutil/common.h"
22 #include "libavutil/mem.h"
25 #include "bytestream.h"
26 #include "codec_internal.h"
29 #define PALDATA_FOLLOWS_TILEDATA 4
30 #define HAVE_COMPRESSED_TILEMAP 32
31 #define HAVE_TILEMAP 128
33 typedef struct SGAVideoContext
{
40 int tilemapdata_offset
;
43 int palmapdata_offset
;
55 uint8_t *tileindex_data
;
56 unsigned tileindex_size
;
57 uint8_t *palmapindex_data
;
58 unsigned palmapindex_size
;
59 uint8_t uncompressed
[65536];
62 static av_cold
int sga_decode_init(AVCodecContext
*avctx
)
64 avctx
->pix_fmt
= AV_PIX_FMT_PAL8
;
68 static int decode_palette(GetByteContext
*gb
, uint32_t *pal
)
72 if (bytestream2_get_bytes_left(gb
) < 18)
73 return AVERROR_INVALIDDATA
;
75 memset(pal
, 0, 16 * sizeof(*pal
));
76 (void)init_get_bits8(&gbit
, gb
->buffer
, 18);
78 for (int RGBIndex
= 0; RGBIndex
< 3; RGBIndex
++) {
79 for (int index
= 0; index
< 16; index
++) {
80 unsigned color
= get_bits1(&gbit
) << RGBIndex
;
81 pal
[15 - index
] |= color
<< (5 + 16);
85 for (int RGBIndex
= 0; RGBIndex
< 3; RGBIndex
++) {
86 for (int index
= 0; index
< 16; index
++) {
87 unsigned color
= get_bits1(&gbit
) << RGBIndex
;
88 pal
[15 - index
] |= color
<< (5 + 8);
92 for (int RGBIndex
= 0; RGBIndex
< 3; RGBIndex
++) {
93 for (int index
= 0; index
< 16; index
++) {
94 unsigned color
= get_bits1(&gbit
) << RGBIndex
;
95 pal
[15 - index
] |= color
<< (5 + 0);
99 for (int index
= 0; index
< 16; index
++)
100 pal
[index
] = (0xFFU
<< 24) | pal
[index
] | (pal
[index
] >> 3);
102 bytestream2_skip(gb
, 18);
107 static int decode_index_palmap(SGAVideoContext
*s
, AVFrame
*frame
)
109 const uint8_t *tt
= s
->tileindex_data
;
111 for (int y
= 0; y
< s
->tiles_h
; y
++) {
112 for (int x
= 0; x
< s
->tiles_w
; x
++) {
113 int pal_idx
= s
->palmapindex_data
[y
* s
->tiles_w
+ x
] * 16;
114 uint8_t *dst
= frame
->data
[0] + y
* 8 * frame
->linesize
[0] + x
* 8;
116 for (int yy
= 0; yy
< 8; yy
++) {
117 for (int xx
= 0; xx
< 8; xx
++)
118 dst
[xx
] = pal_idx
+ tt
[xx
];
121 dst
+= frame
->linesize
[0];
129 static int decode_index_tilemap(SGAVideoContext
*s
, AVFrame
*frame
)
131 GetByteContext
*gb
= &s
->gb
, gb2
;
133 bytestream2_seek(gb
, s
->tilemapdata_offset
, SEEK_SET
);
134 if (bytestream2_get_bytes_left(gb
) < s
->tilemapdata_size
)
135 return AVERROR_INVALIDDATA
;
139 for (int y
= 0; y
< s
->tiles_h
; y
++) {
140 for (int x
= 0; x
< s
->tiles_w
; x
++) {
142 int tilemap
= bytestream2_get_be16u(&gb2
);
143 int flip_x
= (tilemap
>> 11) & 1;
144 int flip_y
= (tilemap
>> 12) & 1;
145 int tindex
= av_clip((tilemap
& 511) - 1, 0, s
->nb_tiles
- 1);
146 const uint8_t *tt
= s
->tileindex_data
+ tindex
* 64;
147 int pal_idx
= ((tilemap
>> 13) & 3) * 16;
148 uint8_t *dst
= frame
->data
[0] + y
* 8 * frame
->linesize
[0] + x
* 8;
150 if (!flip_x
&& !flip_y
) {
151 memcpy(tile
, tt
, 64);
152 } else if (flip_x
&& flip_y
) {
153 for (int i
= 0; i
< 8; i
++) {
154 for (int j
= 0; j
< 8; j
++)
155 tile
[i
* 8 + j
] = tt
[(7 - i
) * 8 + 7 - j
];
158 for (int i
= 0; i
< 8; i
++) {
159 for (int j
= 0; j
< 8; j
++)
160 tile
[i
* 8 + j
] = tt
[i
* 8 + 7 - j
];
163 for (int i
= 0; i
< 8; i
++) {
164 for (int j
= 0; j
< 8; j
++)
165 tile
[i
* 8 + j
] = tt
[(7 - i
) * 8 + j
];
169 for (int yy
= 0; yy
< 8; yy
++) {
170 for (int xx
= 0; xx
< 8; xx
++)
171 dst
[xx
] = pal_idx
+ tile
[xx
+ yy
* 8];
173 dst
+= frame
->linesize
[0];
181 static int decode_index(SGAVideoContext
*s
, AVFrame
*frame
)
183 const uint8_t *src
= s
->tileindex_data
;
184 uint8_t *dst
= frame
->data
[0];
186 for (int y
= 0; y
< frame
->height
; y
+= 8) {
187 for (int x
= 0; x
< frame
->width
; x
+= 8) {
188 for (int yy
= 0; yy
< 8; yy
++) {
189 for (int xx
= 0; xx
< 8; xx
++)
190 dst
[x
+ xx
+ yy
* frame
->linesize
[0]] = src
[xx
];
195 dst
+= 8 * frame
->linesize
[0];
201 static int lzss_decompress(AVCodecContext
*avctx
,
202 GetByteContext
*gb
, uint8_t *dst
,
203 int dst_size
, int shift
, int plus
)
207 while (bytestream2_get_bytes_left(gb
) > 0 && oi
< dst_size
) {
208 uint16_t displace
, header
= bytestream2_get_be16(gb
);
211 for (int i
= 0; i
< 16; i
++) {
212 switch (header
>> 15) {
214 if (oi
+ 2 < dst_size
) {
215 dst
[oi
++] = bytestream2_get_byte(gb
);
216 dst
[oi
++] = bytestream2_get_byte(gb
);
220 displace
= bytestream2_get_be16(gb
);
221 count
= displace
>> shift
;
222 offset
= displace
& ((1 << shift
) - 1);
225 while (bytestream2_get_bytes_left(gb
) > 0 &&
227 dst
[oi
++] = bytestream2_get_byte(gb
);
235 if (oi
< offset
|| oi
+ count
* 2 > dst_size
)
236 return AVERROR_INVALIDDATA
;
237 for (int j
= 0; j
< count
* 2; j
++) {
238 dst
[oi
] = dst
[oi
- offset
];
248 return AVERROR_INVALIDDATA
;
251 static int decode_palmapdata(AVCodecContext
*avctx
)
253 SGAVideoContext
*s
= avctx
->priv_data
;
254 const int bits
= (s
->nb_pal
+ 1) / 2;
255 GetByteContext
*gb
= &s
->gb
;
259 bytestream2_seek(gb
, s
->palmapdata_offset
, SEEK_SET
);
260 if (bytestream2_get_bytes_left(gb
) < s
->palmapdata_size
)
261 return AVERROR_INVALIDDATA
;
262 ret
= init_get_bits8(&pm
, gb
->buffer
, s
->palmapdata_size
);
263 av_assert1(ret
>= 0);
265 for (int y
= 0; y
< s
->tiles_h
; y
++) {
266 uint8_t *dst
= s
->palmapindex_data
+ y
* s
->tiles_w
;
268 for (int x
= 0; x
< s
->tiles_w
; x
++)
269 dst
[x
] = get_bits(&pm
, bits
);
277 static int decode_tiledata(AVCodecContext
*avctx
)
279 SGAVideoContext
*s
= avctx
->priv_data
;
280 GetByteContext
*gb
= &s
->gb
;
284 bytestream2_seek(gb
, s
->tiledata_offset
, SEEK_SET
);
285 if (bytestream2_get_bytes_left(gb
) < s
->tiledata_size
)
286 return AVERROR_INVALIDDATA
;
287 ret
= init_get_bits8(&tm
, gb
->buffer
, s
->tiledata_size
);
288 av_assert1(ret
>= 0);
290 for (int n
= 0; n
< s
->nb_tiles
; n
++) {
291 uint8_t *dst
= s
->tileindex_data
+ n
* 64;
293 for (int yy
= 0; yy
< 8; yy
++) {
294 for (int xx
= 0; xx
< 8; xx
++)
295 dst
[xx
] = get_bits(&tm
, 4);
301 for (int i
= 0; i
< s
->nb_tiles
&& s
->swap
; i
++) {
302 uint8_t *dst
= s
->tileindex_data
+ i
* 64;
304 for (int j
= 8; j
< 64; j
+= 16) {
305 for (int k
= 0; k
< 8; k
+= 2)
306 FFSWAP(uint8_t, dst
[j
+ k
], dst
[j
+k
+1]);
313 static int sga_decode_frame(AVCodecContext
*avctx
, AVFrame
*frame
,
314 int *got_frame
, AVPacket
*avpkt
)
316 SGAVideoContext
*s
= avctx
->priv_data
;
317 GetByteContext
*gb
= &s
->gb
;
320 if (avpkt
->size
<= 14)
321 return AVERROR_INVALIDDATA
;
323 s
->flags
= avpkt
->data
[8];
324 s
->nb_pal
= avpkt
->data
[9];
325 s
->tiles_w
= avpkt
->data
[10];
326 s
->tiles_h
= avpkt
->data
[11];
329 return AVERROR_INVALIDDATA
;
331 if ((ret
= ff_set_dimensions(avctx
,
333 s
->tiles_h
* 8)) < 0)
336 av_fast_padded_malloc(&s
->tileindex_data
, &s
->tileindex_size
,
337 avctx
->width
* avctx
->height
);
338 if (!s
->tileindex_data
)
339 return AVERROR(ENOMEM
);
341 av_fast_padded_malloc(&s
->palmapindex_data
, &s
->palmapindex_size
,
342 s
->tiles_w
* s
->tiles_h
);
343 if (!s
->palmapindex_data
)
344 return AVERROR(ENOMEM
);
346 if ((ret
= ff_get_buffer(avctx
, frame
, 0)) < 0)
349 bytestream2_init(gb
, avpkt
->data
, avpkt
->size
);
351 type
= bytestream2_get_byte(gb
);
352 s
->metadata_size
= 12 + ((!!(s
->flags
& HAVE_TILEMAP
)) * 2);
353 s
->nb_tiles
= s
->flags
& HAVE_TILEMAP
? AV_RB16(avpkt
->data
+ 12) : s
->tiles_w
* s
->tiles_h
;
354 if (s
->nb_tiles
> s
->tiles_w
* s
->tiles_h
)
355 return AVERROR_INVALIDDATA
;
357 av_log(avctx
, AV_LOG_DEBUG
, "type: %X flags: %X nb_tiles: %d\n", type
, s
->flags
, s
->nb_tiles
);
390 int offset
= s
->metadata_size
, left
;
393 bytestream2_seek(gb
, s
->metadata_size
, SEEK_SET
);
395 for (int i
= 0; i
< 3; i
++)
396 sizes
[i
] = bytestream2_get_be16(gb
);
398 for (int i
= 0; i
< 3; i
++) {
400 int raw
= size
>> 15;
402 size
&= (1 << 15) - 1;
405 if (bytestream2_get_bytes_left(gb
) < size
)
406 return AVERROR_INVALIDDATA
;
408 if (sizeof(s
->uncompressed
) - offset
< size
)
409 return AVERROR_INVALIDDATA
;
411 memcpy(s
->uncompressed
+ offset
, gb
->buffer
, size
);
412 bytestream2_skip(gb
, size
);
416 if (bytestream2_get_bytes_left(gb
) < size
)
417 return AVERROR_INVALIDDATA
;
419 bytestream2_init(&gb2
, gb
->buffer
, size
);
420 ret
= lzss_decompress(avctx
, &gb2
, s
->uncompressed
+ offset
,
421 sizeof(s
->uncompressed
) - offset
, s
->shift
, s
->plus
);
424 bytestream2_skip(gb
, size
);
431 left
= bytestream2_get_bytes_left(gb
);
432 if (sizeof(s
->uncompressed
) - offset
< left
)
433 return AVERROR_INVALIDDATA
;
435 bytestream2_get_buffer(gb
, s
->uncompressed
+ offset
, left
);
438 bytestream2_init(gb
, s
->uncompressed
, offset
);
448 bytestream2_seek(gb
, s
->metadata_size
, SEEK_SET
);
449 ret
= lzss_decompress(avctx
, gb
, s
->uncompressed
+ s
->metadata_size
,
450 sizeof(s
->uncompressed
) - s
->metadata_size
, s
->shift
, s
->plus
);
453 bytestream2_init(gb
, s
->uncompressed
, ret
+ s
->metadata_size
);
456 s
->tiledata_size
= s
->nb_tiles
* 32;
457 s
->paldata_size
= s
->nb_pal
* 18;
458 s
->tiledata_offset
= s
->flags
& PALDATA_FOLLOWS_TILEDATA
? s
->metadata_size
: s
->metadata_size
+ s
->paldata_size
;
459 s
->paldata_offset
= s
->flags
& PALDATA_FOLLOWS_TILEDATA
? s
->metadata_size
+ s
->tiledata_size
: s
->metadata_size
;
460 s
->palmapdata_offset
= (s
->flags
& HAVE_TILEMAP
) ? -1 : s
->paldata_offset
+ s
->paldata_size
;
461 s
->palmapdata_size
= (s
->flags
& HAVE_TILEMAP
) || s
->nb_pal
< 2 ? 0 : (s
->tiles_w
* s
->tiles_h
* ((s
->nb_pal
+ 1) / 2) + 7) / 8;
462 s
->tilemapdata_size
= (s
->flags
& HAVE_TILEMAP
) ? s
->tiles_w
* s
->tiles_h
* 2 : 0;
463 s
->tilemapdata_offset
= (s
->flags
& HAVE_TILEMAP
) ? s
->paldata_offset
+ s
->paldata_size
: -1;
465 bytestream2_seek(gb
, s
->paldata_offset
, SEEK_SET
);
466 for (int n
= 0; n
< s
->nb_pal
; n
++) {
467 ret
= decode_palette(gb
, s
->pal
+ 16 * n
);
472 if (s
->tiledata_size
> 0) {
473 ret
= decode_tiledata(avctx
);
478 if (s
->palmapdata_size
> 0) {
479 ret
= decode_palmapdata(avctx
);
484 if (s
->palmapdata_size
> 0 && s
->tiledata_size
> 0) {
485 ret
= decode_index_palmap(s
, frame
);
488 } else if (s
->tilemapdata_size
> 0 && s
->tiledata_size
> 0) {
489 ret
= decode_index_tilemap(s
, frame
);
492 } else if (s
->tiledata_size
> 0) {
493 ret
= decode_index(s
, frame
);
499 av_log(avctx
, AV_LOG_ERROR
, "Unknown type: %X\n", type
);
500 return AVERROR_INVALIDDATA
;
503 memcpy(frame
->data
[1], s
->pal
, AVPALETTE_SIZE
);
504 #if FF_API_PALETTE_HAS_CHANGED
505 FF_DISABLE_DEPRECATION_WARNINGS
506 frame
->palette_has_changed
= 1;
507 FF_ENABLE_DEPRECATION_WARNINGS
509 frame
->pict_type
= AV_PICTURE_TYPE_I
;
510 frame
->flags
|= AV_FRAME_FLAG_KEY
;
517 static av_cold
int sga_decode_end(AVCodecContext
*avctx
)
519 SGAVideoContext
*s
= avctx
->priv_data
;
521 av_freep(&s
->tileindex_data
);
522 s
->tileindex_size
= 0;
524 av_freep(&s
->palmapindex_data
);
525 s
->palmapindex_size
= 0;
530 const FFCodec ff_sga_decoder
= {
532 CODEC_LONG_NAME("Digital Pictures SGA Video"),
533 .p
.type
= AVMEDIA_TYPE_VIDEO
,
534 .p
.id
= AV_CODEC_ID_SGA_VIDEO
,
535 .priv_data_size
= sizeof(SGAVideoContext
),
536 .init
= sga_decode_init
,
537 FF_CODEC_DECODE_CB(sga_decode_frame
),
538 .close
= sga_decode_end
,
539 .p
.capabilities
= AV_CODEC_CAP_DR1
,