brender_pix: use a grayscale palette as fallback instead of a uninitialized one.
[ffmpeg.git] / libavcodec / brender_pix.c
1 /*
2  * BRender PIX (.pix) image decoder
3  * Copyright (c) 2012 Aleksi Nurmi
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 /*
23  * Tested against samples from I-War / Independence War and Defiance.
24  * If the PIX file does not contain a palette, the
25  * palette_has_changed property of the AVFrame is set to 0.
26  */
27
28 #include "libavutil/imgutils.h"
29 #include "avcodec.h"
30 #include "bytestream.h"
31 #include "internal.h"
32
33 typedef struct BRPixContext {
34     AVFrame frame;
35 } BRPixContext;
36
37 typedef struct BRPixHeader {
38     int format;
39     unsigned int width, height;
40 } BRPixHeader;
41
42 static av_cold int brpix_init(AVCodecContext *avctx)
43 {
44     BRPixContext *s = avctx->priv_data;
45
46     avcodec_get_frame_defaults(&s->frame);
47     avctx->coded_frame = &s->frame;
48
49     return 0;
50 }
51
52 static int brpix_decode_header(BRPixHeader *out, GetByteContext *pgb)
53 {
54     unsigned int header_len = bytestream2_get_be32(pgb);
55
56     out->format = bytestream2_get_byte(pgb);
57     bytestream2_skip(pgb, 2);
58     out->width = bytestream2_get_be16(pgb);
59     out->height = bytestream2_get_be16(pgb);
60
61     // the header is at least 11 bytes long; we read the first 7
62     if (header_len < 11) {
63         return 0;
64     }
65
66     // skip the rest of the header
67     bytestream2_skip(pgb, header_len-7);
68
69     return 1;
70 }
71
72 static int brpix_decode_frame(AVCodecContext *avctx,
73                               void *data, int *got_frame,
74                               AVPacket *avpkt)
75 {
76     BRPixContext *s = avctx->priv_data;
77     AVFrame *frame_out = data;
78
79     int ret;
80     GetByteContext gb;
81
82     unsigned int bytes_pp;
83
84     unsigned int magic[4];
85     unsigned int chunk_type;
86     unsigned int data_len;
87     BRPixHeader hdr;
88
89     bytestream2_init(&gb, avpkt->data, avpkt->size);
90
91     magic[0] = bytestream2_get_be32(&gb);
92     magic[1] = bytestream2_get_be32(&gb);
93     magic[2] = bytestream2_get_be32(&gb);
94     magic[3] = bytestream2_get_be32(&gb);
95
96     if (magic[0] != 0x12 ||
97         magic[1] != 0x8 ||
98         magic[2] != 0x2 ||
99         magic[3] != 0x2) {
100         av_log(avctx, AV_LOG_ERROR, "Not a BRender PIX file\n");
101         return AVERROR_INVALIDDATA;
102     }
103
104     chunk_type = bytestream2_get_be32(&gb);
105     if (chunk_type != 0x3 && chunk_type != 0x3d) {
106         av_log(avctx, AV_LOG_ERROR, "Invalid chunk type %d\n", chunk_type);
107         return AVERROR_INVALIDDATA;
108     }
109
110     ret = brpix_decode_header(&hdr, &gb);
111     if (!ret) {
112         av_log(avctx, AV_LOG_ERROR, "Invalid header length\n");
113         return AVERROR_INVALIDDATA;
114     }
115     switch (hdr.format) {
116     case 3:
117         avctx->pix_fmt = AV_PIX_FMT_PAL8;
118         bytes_pp = 1;
119         break;
120     case 4:
121         avctx->pix_fmt = AV_PIX_FMT_RGB555BE;
122         bytes_pp = 2;
123         break;
124     case 5:
125         avctx->pix_fmt = AV_PIX_FMT_RGB565BE;
126         bytes_pp = 2;
127         break;
128     case 6:
129         avctx->pix_fmt = AV_PIX_FMT_RGB24;
130         bytes_pp = 3;
131         break;
132     case 7:
133         avctx->pix_fmt = AV_PIX_FMT_0RGB;
134         bytes_pp = 4;
135         break;
136     case 18:
137         avctx->pix_fmt = AV_PIX_FMT_GRAY8A;
138         bytes_pp = 2;
139         break;
140     default:
141         av_log(avctx, AV_LOG_ERROR, "Format %d is not supported\n",
142                                     hdr.format);
143         return AVERROR_PATCHWELCOME;
144     }
145
146     if (s->frame.data[0])
147         avctx->release_buffer(avctx, &s->frame);
148
149     if (av_image_check_size(hdr.width, hdr.height, 0, avctx) < 0)
150         return AVERROR_INVALIDDATA;
151
152     if (hdr.width != avctx->width || hdr.height != avctx->height)
153         avcodec_set_dimensions(avctx, hdr.width, hdr.height);
154
155     if ((ret = ff_get_buffer(avctx, &s->frame)) < 0) {
156         av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
157         return ret;
158     }
159
160     chunk_type = bytestream2_get_be32(&gb);
161
162     if (avctx->pix_fmt == AV_PIX_FMT_PAL8 &&
163         (chunk_type == 0x3 || chunk_type == 0x3d)) {
164         BRPixHeader palhdr;
165         uint32_t *pal_out = (uint32_t *)s->frame.data[1];
166         int i;
167
168         ret = brpix_decode_header(&palhdr, &gb);
169         if (!ret) {
170             av_log(avctx, AV_LOG_ERROR, "Invalid palette header length\n");
171             return AVERROR_INVALIDDATA;
172         }
173         if (palhdr.format != 7) {
174             av_log(avctx, AV_LOG_ERROR, "Palette is not in 0RGB format\n");
175             return AVERROR_INVALIDDATA;
176         }
177
178         chunk_type = bytestream2_get_be32(&gb);
179         data_len = bytestream2_get_be32(&gb);
180         bytestream2_skip(&gb, 8);
181         if (chunk_type != 0x21 || data_len != 1032 ||
182             bytestream2_get_bytes_left(&gb) < 1032) {
183             av_log(avctx, AV_LOG_ERROR, "Invalid palette data\n");
184             return AVERROR_INVALIDDATA;
185         }
186         // convert 0RGB to machine endian format (ARGB32)
187         for (i = 0; i < 256; ++i) {
188             bytestream2_skipu(&gb, 1);
189             *pal_out++ = (0xFFU << 24) | bytestream2_get_be24u(&gb);
190         }
191         bytestream2_skip(&gb, 8);
192
193         s->frame.palette_has_changed = 1;
194
195         chunk_type = bytestream2_get_be32(&gb);
196     } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
197         uint32_t *pal_out = (uint32_t *)s->frame.data[1];
198         int i;
199
200         for (i = 0; i < 256; ++i) {
201             *pal_out++ = (0xFFU << 24) | (i * 0x010101);
202         }
203         s->frame.palette_has_changed = 1;
204     }
205
206     data_len = bytestream2_get_be32(&gb);
207     bytestream2_skip(&gb, 8);
208
209     // read the image data to the buffer
210     {
211         unsigned int bytes_per_scanline = bytes_pp * hdr.width;
212         unsigned int bytes_left = bytestream2_get_bytes_left(&gb);
213
214         if (chunk_type != 0x21 || data_len != bytes_left ||
215             bytes_left / bytes_per_scanline < hdr.height)
216         {
217             av_log(avctx, AV_LOG_ERROR, "Invalid image data\n");
218             return AVERROR_INVALIDDATA;
219         }
220
221         av_image_copy_plane(s->frame.data[0], s->frame.linesize[0],
222                             avpkt->data + bytestream2_tell(&gb),
223                             bytes_per_scanline,
224                             bytes_per_scanline, hdr.height);
225     }
226
227     *frame_out = s->frame;
228     *got_frame = 1;
229
230     return avpkt->size;
231 }
232
233 static av_cold int brpix_end(AVCodecContext *avctx)
234 {
235     BRPixContext *s = avctx->priv_data;
236
237     if(s->frame.data[0])
238         avctx->release_buffer(avctx, &s->frame);
239
240     return 0;
241 }
242
243 AVCodec ff_brender_pix_decoder = {
244     .name           = "brender_pix",
245     .type           = AVMEDIA_TYPE_VIDEO,
246     .id             = AV_CODEC_ID_BRENDER_PIX,
247     .priv_data_size = sizeof(BRPixContext),
248     .init           = brpix_init,
249     .close          = brpix_end,
250     .decode         = brpix_decode_frame,
251     .capabilities   = CODEC_CAP_DR1,
252     .long_name      = NULL_IF_CONFIG_SMALL("BRender PIX image"),
253 };