Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Microsoft RLE decoder | |
3 | * Copyright (C) 2008 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 | * MS RLE decoder based on decoder by Mike Melanson and my own for TSCC | |
25 | * For more information about the MS RLE format, visit: | |
26 | * http://www.multimedia.cx/msrle.txt | |
27 | */ | |
28 | ||
29 | #include "libavutil/intreadwrite.h" | |
30 | #include "avcodec.h" | |
31 | #include "msrledec.h" | |
32 | ||
33 | static int msrle_decode_pal4(AVCodecContext *avctx, AVPicture *pic, | |
34 | GetByteContext *gb) | |
35 | { | |
36 | unsigned char rle_code; | |
37 | unsigned char extra_byte, odd_pixel; | |
38 | unsigned char stream_byte; | |
39 | unsigned int pixel_ptr = 0; | |
40 | int row_dec = pic->linesize[0]; | |
41 | int row_ptr = (avctx->height - 1) * row_dec; | |
42 | int frame_size = row_dec * avctx->height; | |
43 | int i; | |
44 | ||
45 | while (row_ptr >= 0) { | |
46 | if (bytestream2_get_bytes_left(gb) <= 0) { | |
47 | av_log(avctx, AV_LOG_ERROR, | |
48 | "MS RLE: bytestream overrun, %d rows left\n", | |
49 | row_ptr); | |
50 | return AVERROR_INVALIDDATA; | |
51 | } | |
52 | rle_code = stream_byte = bytestream2_get_byteu(gb); | |
53 | if (rle_code == 0) { | |
54 | /* fetch the next byte to see how to handle escape code */ | |
55 | stream_byte = bytestream2_get_byte(gb); | |
56 | if (stream_byte == 0) { | |
57 | /* line is done, goto the next one */ | |
58 | row_ptr -= row_dec; | |
59 | pixel_ptr = 0; | |
60 | } else if (stream_byte == 1) { | |
61 | /* decode is done */ | |
62 | return 0; | |
63 | } else if (stream_byte == 2) { | |
64 | /* reposition frame decode coordinates */ | |
65 | stream_byte = bytestream2_get_byte(gb); | |
66 | pixel_ptr += stream_byte; | |
67 | stream_byte = bytestream2_get_byte(gb); | |
68 | row_ptr -= stream_byte * row_dec; | |
69 | } else { | |
70 | // copy pixels from encoded stream | |
71 | odd_pixel = stream_byte & 1; | |
72 | rle_code = (stream_byte + 1) / 2; | |
73 | extra_byte = rle_code & 0x01; | |
74 | if (row_ptr + pixel_ptr + stream_byte > frame_size || | |
75 | bytestream2_get_bytes_left(gb) < rle_code) { | |
76 | av_log(avctx, AV_LOG_ERROR, | |
77 | "MS RLE: frame/stream ptr just went out of bounds (copy)\n"); | |
78 | return AVERROR_INVALIDDATA; | |
79 | } | |
80 | ||
81 | for (i = 0; i < rle_code; i++) { | |
82 | if (pixel_ptr >= avctx->width) | |
83 | break; | |
84 | stream_byte = bytestream2_get_byteu(gb); | |
85 | pic->data[0][row_ptr + pixel_ptr] = stream_byte >> 4; | |
86 | pixel_ptr++; | |
87 | if (i + 1 == rle_code && odd_pixel) | |
88 | break; | |
89 | if (pixel_ptr >= avctx->width) | |
90 | break; | |
91 | pic->data[0][row_ptr + pixel_ptr] = stream_byte & 0x0F; | |
92 | pixel_ptr++; | |
93 | } | |
94 | ||
95 | // if the RLE code is odd, skip a byte in the stream | |
96 | if (extra_byte) | |
97 | bytestream2_skip(gb, 1); | |
98 | } | |
99 | } else { | |
100 | // decode a run of data | |
101 | if (row_ptr + pixel_ptr + stream_byte > frame_size) { | |
102 | av_log(avctx, AV_LOG_ERROR, | |
103 | "MS RLE: frame ptr just went out of bounds (run)\n"); | |
104 | return AVERROR_INVALIDDATA; | |
105 | } | |
106 | stream_byte = bytestream2_get_byte(gb); | |
107 | for (i = 0; i < rle_code; i++) { | |
108 | if (pixel_ptr >= avctx->width) | |
109 | break; | |
110 | if ((i & 1) == 0) | |
111 | pic->data[0][row_ptr + pixel_ptr] = stream_byte >> 4; | |
112 | else | |
113 | pic->data[0][row_ptr + pixel_ptr] = stream_byte & 0x0F; | |
114 | pixel_ptr++; | |
115 | } | |
116 | } | |
117 | } | |
118 | ||
119 | /* one last sanity check on the way out */ | |
120 | if (bytestream2_get_bytes_left(gb)) { | |
121 | av_log(avctx, AV_LOG_ERROR, | |
122 | "MS RLE: ended frame decode with %d bytes left over\n", | |
123 | bytestream2_get_bytes_left(gb)); | |
124 | return AVERROR_INVALIDDATA; | |
125 | } | |
126 | ||
127 | return 0; | |
128 | } | |
129 | ||
130 | ||
131 | static int msrle_decode_8_16_24_32(AVCodecContext *avctx, AVPicture *pic, | |
132 | int depth, GetByteContext *gb) | |
133 | { | |
134 | uint8_t *output, *output_end; | |
135 | int p1, p2, line=avctx->height - 1, pos=0, i; | |
136 | uint16_t pix16; | |
137 | uint32_t pix32; | |
138 | unsigned int width= FFABS(pic->linesize[0]) / (depth >> 3); | |
139 | ||
140 | output = pic->data[0] + (avctx->height - 1) * pic->linesize[0]; | |
141 | output_end = output + FFABS(pic->linesize[0]); | |
142 | ||
143 | while (bytestream2_get_bytes_left(gb) > 0) { | |
144 | p1 = bytestream2_get_byteu(gb); | |
145 | if(p1 == 0) { //Escape code | |
146 | p2 = bytestream2_get_byte(gb); | |
147 | if(p2 == 0) { //End-of-line | |
148 | if (--line < 0) { | |
149 | if (bytestream2_get_be16(gb) == 1) { // end-of-picture | |
150 | return 0; | |
151 | } else { | |
152 | av_log(avctx, AV_LOG_ERROR, | |
153 | "Next line is beyond picture bounds (%d bytes left)\n", | |
154 | bytestream2_get_bytes_left(gb)); | |
155 | return AVERROR_INVALIDDATA; | |
156 | } | |
157 | } | |
158 | output = pic->data[0] + line * pic->linesize[0]; | |
159 | output_end = output + FFABS(pic->linesize[0]); | |
160 | pos = 0; | |
161 | continue; | |
162 | } else if(p2 == 1) { //End-of-picture | |
163 | return 0; | |
164 | } else if(p2 == 2) { //Skip | |
165 | p1 = bytestream2_get_byte(gb); | |
166 | p2 = bytestream2_get_byte(gb); | |
167 | line -= p2; | |
168 | pos += p1; | |
169 | if (line < 0 || pos >= width){ | |
170 | av_log(avctx, AV_LOG_ERROR, "Skip beyond picture bounds\n"); | |
171 | return -1; | |
172 | } | |
173 | output = pic->data[0] + line * pic->linesize[0] + pos * (depth >> 3); | |
174 | output_end = pic->data[0] + line * pic->linesize[0] + FFABS(pic->linesize[0]); | |
175 | continue; | |
176 | } | |
177 | // Copy data | |
178 | if (output + p2 * (depth >> 3) > output_end) { | |
179 | bytestream2_skip(gb, 2 * (depth >> 3)); | |
180 | continue; | |
181 | } else if (bytestream2_get_bytes_left(gb) < p2 * (depth >> 3)) { | |
182 | av_log(avctx, AV_LOG_ERROR, "bytestream overrun\n"); | |
183 | return AVERROR_INVALIDDATA; | |
184 | } | |
185 | ||
186 | if ((depth == 8) || (depth == 24)) { | |
187 | bytestream2_get_bufferu(gb, output, p2 * (depth >> 3)); | |
188 | output += p2 * (depth >> 3); | |
189 | ||
190 | // RLE8 copy is actually padded - and runs are not! | |
191 | if(depth == 8 && (p2 & 1)) { | |
192 | bytestream2_skip(gb, 1); | |
193 | } | |
194 | } else if (depth == 16) { | |
195 | for(i = 0; i < p2; i++) { | |
196 | *(uint16_t*)output = bytestream2_get_le16u(gb); | |
197 | output += 2; | |
198 | } | |
199 | } else if (depth == 32) { | |
200 | for(i = 0; i < p2; i++) { | |
201 | *(uint32_t*)output = bytestream2_get_le32u(gb); | |
202 | output += 4; | |
203 | } | |
204 | } | |
205 | pos += p2; | |
206 | } else { //run of pixels | |
207 | uint8_t pix[3]; //original pixel | |
208 | if (output + p1 * (depth >> 3) > output_end) | |
209 | continue; | |
210 | ||
211 | switch(depth){ | |
212 | case 8: | |
213 | pix[0] = bytestream2_get_byte(gb); | |
214 | memset(output, pix[0], p1); | |
215 | output += p1; | |
216 | break; | |
217 | case 16: | |
218 | pix16 = bytestream2_get_le16(gb); | |
219 | for(i = 0; i < p1; i++) { | |
220 | *(uint16_t*)output = pix16; | |
221 | output += 2; | |
222 | } | |
223 | break; | |
224 | case 24: | |
225 | pix[0] = bytestream2_get_byte(gb); | |
226 | pix[1] = bytestream2_get_byte(gb); | |
227 | pix[2] = bytestream2_get_byte(gb); | |
228 | for(i = 0; i < p1; i++) { | |
229 | *output++ = pix[0]; | |
230 | *output++ = pix[1]; | |
231 | *output++ = pix[2]; | |
232 | } | |
233 | break; | |
234 | case 32: | |
235 | pix32 = bytestream2_get_le32(gb); | |
236 | for(i = 0; i < p1; i++) { | |
237 | *(uint32_t*)output = pix32; | |
238 | output += 4; | |
239 | } | |
240 | break; | |
241 | } | |
242 | pos += p1; | |
243 | } | |
244 | } | |
245 | ||
246 | av_log(avctx, AV_LOG_WARNING, "MS RLE warning: no end-of-picture code\n"); | |
247 | return 0; | |
248 | } | |
249 | ||
250 | ||
251 | int ff_msrle_decode(AVCodecContext *avctx, AVPicture *pic, | |
252 | int depth, GetByteContext *gb) | |
253 | { | |
254 | switch(depth){ | |
255 | case 4: | |
256 | return msrle_decode_pal4(avctx, pic, gb); | |
257 | case 8: | |
258 | case 16: | |
259 | case 24: | |
260 | case 32: | |
261 | return msrle_decode_8_16_24_32(avctx, pic, depth, gb); | |
262 | default: | |
263 | av_log(avctx, AV_LOG_ERROR, "Unknown depth %d\n", depth); | |
264 | return -1; | |
265 | } | |
266 | } |