1/*
2 * QPEG codec
3 * Copyright (c) 2004 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/**
23 * @file
24 * QPEG codec.
25 */
26
27#include "avcodec.h"
28
29typedef struct QpegContext{
30    AVCodecContext *avctx;
31    AVFrame pic;
32    uint8_t *refdata;
33} QpegContext;
34
35static void qpeg_decode_intra(const uint8_t *src, uint8_t *dst, int size,
36                            int stride, int width, int height)
37{
38    int i;
39    int code;
40    int c0, c1;
41    int run, copy;
42    int filled = 0;
43    int rows_to_go;
44
45    rows_to_go = height;
46    height--;
47    dst = dst + height * stride;
48
49    while((size > 0) && (rows_to_go > 0)) {
50        code = *src++;
51        size--;
52        run = copy = 0;
53        if(code == 0xFC) /* end-of-picture code */
54            break;
55        if(code >= 0xF8) { /* very long run */
56            c0 = *src++;
57            c1 = *src++;
58            size -= 2;
59            run = ((code & 0x7) << 16) + (c0 << 8) + c1 + 2;
60        } else if (code >= 0xF0) { /* long run */
61            c0 = *src++;
62            size--;
63            run = ((code & 0xF) << 8) + c0 + 2;
64        } else if (code >= 0xE0) { /* short run */
65            run = (code & 0x1F) + 2;
66        } else if (code >= 0xC0) { /* very long copy */
67            c0 = *src++;
68            c1 = *src++;
69            size -= 2;
70            copy = ((code & 0x3F) << 16) + (c0 << 8) + c1 + 1;
71        } else if (code >= 0x80) { /* long copy */
72            c0 = *src++;
73            size--;
74            copy = ((code & 0x7F) << 8) + c0 + 1;
75        } else { /* short copy */
76            copy = code + 1;
77        }
78
79        /* perform actual run or copy */
80        if(run) {
81            int p;
82
83            p = *src++;
84            size--;
85            for(i = 0; i < run; i++) {
86                dst[filled++] = p;
87                if (filled >= width) {
88                    filled = 0;
89                    dst -= stride;
90                    rows_to_go--;
91                    if(rows_to_go <= 0)
92                        break;
93                }
94            }
95        } else {
96            size -= copy;
97            for(i = 0; i < copy; i++) {
98                dst[filled++] = *src++;
99                if (filled >= width) {
100                    filled = 0;
101                    dst -= stride;
102                    rows_to_go--;
103                    if(rows_to_go <= 0)
104                        break;
105                }
106            }
107        }
108    }
109}
110
111static const int qpeg_table_h[16] =
112 { 0x00, 0x20, 0x20, 0x20, 0x18, 0x10, 0x10, 0x20, 0x10, 0x08, 0x18, 0x08, 0x08, 0x18, 0x10, 0x04};
113static const int qpeg_table_w[16] =
114 { 0x00, 0x20, 0x18, 0x08, 0x18, 0x10, 0x20, 0x10, 0x08, 0x10, 0x20, 0x20, 0x08, 0x10, 0x18, 0x04};
115
116/* Decodes delta frames */
117static void qpeg_decode_inter(const uint8_t *src, uint8_t *dst, int size,
118                            int stride, int width, int height,
119                            int delta, const uint8_t *ctable, uint8_t *refdata)
120{
121    int i, j;
122    int code;
123    int filled = 0;
124    int orig_height;
125
126    /* copy prev frame */
127    for(i = 0; i < height; i++)
128        memcpy(refdata + (i * width), dst + (i * stride), width);
129
130    orig_height = height;
131    height--;
132    dst = dst + height * stride;
133
134    while((size > 0) && (height >= 0)) {
135        code = *src++;
136        size--;
137
138        if(delta) {
139            /* motion compensation */
140            while((code & 0xF0) == 0xF0) {
141                if(delta == 1) {
142                    int me_idx;
143                    int me_w, me_h, me_x, me_y;
144                    uint8_t *me_plane;
145                    int corr, val;
146
147                    /* get block size by index */
148                    me_idx = code & 0xF;
149                    me_w = qpeg_table_w[me_idx];
150                    me_h = qpeg_table_h[me_idx];
151
152                    /* extract motion vector */
153                    corr = *src++;
154                    size--;
155
156                    val = corr >> 4;
157                    if(val > 7)
158                        val -= 16;
159                    me_x = val;
160
161                    val = corr & 0xF;
162                    if(val > 7)
163                        val -= 16;
164                    me_y = val;
165
166                    /* check motion vector */
167                    if ((me_x + filled < 0) || (me_x + me_w + filled > width) ||
168                       (height - me_y - me_h < 0) || (height - me_y > orig_height) ||
169                       (filled + me_w > width) || (height - me_h < 0))
170                        av_log(NULL, AV_LOG_ERROR, "Bogus motion vector (%i,%i), block size %ix%i at %i,%i\n",
171                               me_x, me_y, me_w, me_h, filled, height);
172                    else {
173                        /* do motion compensation */
174                        me_plane = refdata + (filled + me_x) + (height - me_y) * width;
175                        for(j = 0; j < me_h; j++) {
176                            for(i = 0; i < me_w; i++)
177                                dst[filled + i - (j * stride)] = me_plane[i - (j * width)];
178                        }
179                    }
180                }
181                code = *src++;
182                size--;
183            }
184        }
185
186        if(code == 0xE0) /* end-of-picture code */
187            break;
188        if(code > 0xE0) { /* run code: 0xE1..0xFF */
189            int p;
190
191            code &= 0x1F;
192            p = *src++;
193            size--;
194            for(i = 0; i <= code; i++) {
195                dst[filled++] = p;
196                if(filled >= width) {
197                    filled = 0;
198                    dst -= stride;
199                    height--;
200                }
201            }
202        } else if(code >= 0xC0) { /* copy code: 0xC0..0xDF */
203            code &= 0x1F;
204
205            for(i = 0; i <= code; i++) {
206                dst[filled++] = *src++;
207                if(filled >= width) {
208                    filled = 0;
209                    dst -= stride;
210                    height--;
211                }
212            }
213            size -= code + 1;
214        } else if(code >= 0x80) { /* skip code: 0x80..0xBF */
215            int skip;
216
217            code &= 0x3F;
218            /* codes 0x80 and 0x81 are actually escape codes,
219               skip value minus constant is in the next byte */
220            if(!code)
221                skip = (*src++) + 64;
222            else if(code == 1)
223                skip = (*src++) + 320;
224            else
225                skip = code;
226            filled += skip;
227            while( filled >= width) {
228                filled -= width;
229                dst -= stride;
230                height--;
231                if(height < 0)
232                    break;
233            }
234        } else {
235            /* zero code treated as one-pixel skip */
236            if(code)
237                dst[filled++] = ctable[code & 0x7F];
238            else
239                filled++;
240            if(filled >= width) {
241                filled = 0;
242                dst -= stride;
243                height--;
244            }
245        }
246    }
247}
248
249static int decode_frame(AVCodecContext *avctx,
250                        void *data, int *data_size,
251                        AVPacket *avpkt)
252{
253    const uint8_t *buf = avpkt->data;
254    int buf_size = avpkt->size;
255    QpegContext * const a = avctx->priv_data;
256    AVFrame * const p= (AVFrame*)&a->pic;
257    uint8_t* outdata;
258    int delta;
259
260    if(p->data[0])
261        avctx->release_buffer(avctx, p);
262
263    p->reference= 0;
264    if(avctx->get_buffer(avctx, p) < 0){
265        av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
266        return -1;
267    }
268    outdata = a->pic.data[0];
269    if(buf[0x85] == 0x10) {
270        qpeg_decode_intra(buf+0x86, outdata, buf_size - 0x86, a->pic.linesize[0], avctx->width, avctx->height);
271    } else {
272        delta = buf[0x85];
273        qpeg_decode_inter(buf+0x86, outdata, buf_size - 0x86, a->pic.linesize[0], avctx->width, avctx->height, delta, buf + 4, a->refdata);
274    }
275
276    /* make the palette available on the way out */
277    memcpy(a->pic.data[1], a->avctx->palctrl->palette, AVPALETTE_SIZE);
278    if (a->avctx->palctrl->palette_changed) {
279        a->pic.palette_has_changed = 1;
280        a->avctx->palctrl->palette_changed = 0;
281    }
282
283    *data_size = sizeof(AVFrame);
284    *(AVFrame*)data = a->pic;
285
286    return buf_size;
287}
288
289static av_cold int decode_init(AVCodecContext *avctx){
290    QpegContext * const a = avctx->priv_data;
291
292    if (!avctx->palctrl) {
293        av_log(avctx, AV_LOG_FATAL, "Missing required palette via palctrl\n");
294        return -1;
295    }
296    a->avctx = avctx;
297    avctx->pix_fmt= PIX_FMT_PAL8;
298    a->refdata = av_malloc(avctx->width * avctx->height);
299
300    return 0;
301}
302
303static av_cold int decode_end(AVCodecContext *avctx){
304    QpegContext * const a = avctx->priv_data;
305    AVFrame * const p= (AVFrame*)&a->pic;
306
307    if(p->data[0])
308        avctx->release_buffer(avctx, p);
309
310    av_free(a->refdata);
311    return 0;
312}
313
314AVCodec qpeg_decoder = {
315    "qpeg",
316    AVMEDIA_TYPE_VIDEO,
317    CODEC_ID_QPEG,
318    sizeof(QpegContext),
319    decode_init,
320    NULL,
321    decode_end,
322    decode_frame,
323    CODEC_CAP_DR1,
324    .long_name = NULL_IF_CONFIG_SMALL("Q-team QPEG"),
325};
326