100l: sometime palette was not copied to output frame
[ffmpeg.git] / libavcodec / msrle.c
1 /*
2  * Micrsoft RLE Video Decoder
3  * Copyright (C) 2003 the ffmpeg project
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 /**
21  * @file msrle.c
22  * MS RLE Video Decoder by Mike Melanson (melanson@pcisys.net)
23  * For more information about the MS RLE format, visit:
24  *   http://www.pcisys.net/~melanson/codecs/
25  *
26  * The MS RLE decoder outputs PAL8 colorspace data.
27  *
28  * Note that this decoder expects the palette colors from the end of the
29  * BITMAPINFO header passed through palctrl.
30  */
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.h>
36
37 #include "common.h"
38 #include "avcodec.h"
39 #include "dsputil.h"
40
41 typedef struct MsrleContext {
42     AVCodecContext *avctx;
43     AVFrame frame;
44     AVFrame prev_frame;
45
46     unsigned char *buf;
47     int size;
48
49 } MsrleContext;
50
51 #define FETCH_NEXT_STREAM_BYTE() \
52     if (stream_ptr >= s->size) \
53     { \
54       av_log(s->avctx, AV_LOG_ERROR, " MS RLE: stream ptr just went out of bounds (1)\n"); \
55       return; \
56     } \
57     stream_byte = s->buf[stream_ptr++];
58
59 static void msrle_decode_pal8(MsrleContext *s)
60 {
61     int stream_ptr = 0;
62     unsigned char rle_code;
63     unsigned char extra_byte;
64     unsigned char stream_byte;
65     int pixel_ptr = 0;
66     int row_dec = s->frame.linesize[0];
67     int row_ptr = (s->avctx->height - 1) * row_dec;
68     int frame_size = row_dec * s->avctx->height;
69
70     /* make the palette available */
71     memcpy(s->frame.data[1], s->avctx->palctrl->palette, AVPALETTE_SIZE);
72     if (s->avctx->palctrl->palette_changed) {
73         s->frame.palette_has_changed = 1;
74         s->avctx->palctrl->palette_changed = 0;
75     }
76
77     while (row_ptr >= 0) {
78         FETCH_NEXT_STREAM_BYTE();
79         rle_code = stream_byte;
80         if (rle_code == 0) {
81             /* fetch the next byte to see how to handle escape code */
82             FETCH_NEXT_STREAM_BYTE();
83             if (stream_byte == 0) {
84                 /* line is done, goto the next one */
85                 row_ptr -= row_dec;
86                 pixel_ptr = 0;
87             } else if (stream_byte == 1) {
88                 /* decode is done */
89                 return;
90             } else if (stream_byte == 2) {
91                 /* reposition frame decode coordinates */
92                 FETCH_NEXT_STREAM_BYTE();
93                 pixel_ptr += stream_byte;
94                 FETCH_NEXT_STREAM_BYTE();
95                 row_ptr -= stream_byte * row_dec;
96             } else {
97                 /* copy pixels from encoded stream */
98                 if ((row_ptr + pixel_ptr + stream_byte > frame_size) ||
99                     (row_ptr < 0)) {
100                     av_log(s->avctx, AV_LOG_ERROR, " MS RLE: frame ptr just went out of bounds (1)\n");
101                     return;
102                 }
103
104                 rle_code = stream_byte;
105                 extra_byte = stream_byte & 0x01;
106                 if (stream_ptr + rle_code + extra_byte > s->size) {
107                     av_log(s->avctx, AV_LOG_ERROR, " MS RLE: stream ptr just went out of bounds (2)\n");
108                     return;
109                 }
110
111                 while (rle_code--) {
112                     FETCH_NEXT_STREAM_BYTE();
113                     s->frame.data[0][row_ptr + pixel_ptr] = stream_byte;
114                     pixel_ptr++;
115                 }
116
117                 /* if the RLE code is odd, skip a byte in the stream */
118                 if (extra_byte)
119                     stream_ptr++;
120             }
121         } else {
122             /* decode a run of data */
123             if ((row_ptr + pixel_ptr + stream_byte > frame_size) ||
124                 (row_ptr < 0)) {
125                 av_log(s->avctx, AV_LOG_ERROR, " MS RLE: frame ptr just went out of bounds (2)\n");
126                 return;
127             }
128
129             FETCH_NEXT_STREAM_BYTE();
130
131             while(rle_code--) {
132                 s->frame.data[0][row_ptr + pixel_ptr] = stream_byte;
133                 pixel_ptr++;
134             }
135         }
136     }
137
138     /* one last sanity check on the way out */
139     if (stream_ptr < s->size)
140         av_log(s->avctx, AV_LOG_ERROR, " MS RLE: ended frame decode with bytes left over (%d < %d)\n",
141             stream_ptr, s->size);
142 }
143
144 static int msrle_decode_init(AVCodecContext *avctx)
145 {
146     MsrleContext *s = (MsrleContext *)avctx->priv_data;
147
148     s->avctx = avctx;
149
150     avctx->pix_fmt = PIX_FMT_PAL8;
151     avctx->has_b_frames = 0;
152     s->frame.data[0] = s->prev_frame.data[0] = NULL;
153
154     return 0;
155 }
156
157 static int msrle_decode_frame(AVCodecContext *avctx,
158                               void *data, int *data_size,
159                               uint8_t *buf, int buf_size)
160 {
161     MsrleContext *s = (MsrleContext *)avctx->priv_data;
162
163     s->buf = buf;
164     s->size = buf_size;
165
166     s->frame.reference = 1;
167     if (avctx->get_buffer(avctx, &s->frame)) {
168         av_log(avctx, AV_LOG_ERROR, "  MS RLE: get_buffer() failed\n");
169         return -1;
170     }
171
172     if (s->prev_frame.data[0] && (s->frame.linesize[0] != s->prev_frame.linesize[0]))
173         av_log(avctx, AV_LOG_ERROR, "  MS RLE: Buffer linesize changed: current %u, previous %u.\n"
174                 "          Expect wrong image and/or crash!\n",
175                 s->frame.linesize[0], s->prev_frame.linesize[0]);
176
177     /* grossly inefficient, but...oh well */
178     if (s->prev_frame.data[0] != NULL)
179         memcpy(s->frame.data[0], s->prev_frame.data[0], 
180         s->frame.linesize[0] * s->avctx->height);
181
182     msrle_decode_pal8(s);
183
184     if (s->prev_frame.data[0])
185         avctx->release_buffer(avctx, &s->prev_frame);
186
187     /* shuffle frames */
188     s->prev_frame = s->frame;
189
190     *data_size = sizeof(AVFrame);
191     *(AVFrame*)data = s->frame;
192
193     /* report that the buffer was completely consumed */
194     return buf_size;
195 }
196
197 static int msrle_decode_end(AVCodecContext *avctx)
198 {
199     MsrleContext *s = (MsrleContext *)avctx->priv_data;
200
201     /* release the last frame */
202     if (s->prev_frame.data[0])
203         avctx->release_buffer(avctx, &s->prev_frame);
204
205     return 0;
206 }
207
208 AVCodec msrle_decoder = {
209     "msrle",
210     CODEC_TYPE_VIDEO,
211     CODEC_ID_MSRLE,
212     sizeof(MsrleContext),
213     msrle_decode_init,
214     NULL,
215     msrle_decode_end,
216     msrle_decode_frame,
217     CODEC_CAP_DR1,
218 };