Mercurial > libavcodec.hg
annotate cdgraphics.c @ 12266:48d6738904a9 libavcodec
Fix SPLATB_REG mess. Used to be a if/elseif/elseif/elseif spaghetti, so this
splits it into small optimization-specific macros which are selected for each
DSP function. The advantage of this approach is that the sse4 functions now
use the ssse3 codepath also without needing an explicit sse4 codepath.
author | rbultje |
---|---|
date | Sat, 24 Jul 2010 19:33:05 +0000 |
parents | c35d7bc64882 |
children |
rev | line source |
---|---|
10692 | 1 /* |
2 * CD Graphics Video Decoder | |
3 * Copyright (c) 2009 Michael Tison | |
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 #include "avcodec.h" | |
23 #include "bytestream.h" | |
24 | |
25 /** | |
11644
7dd2a45249a9
Remove explicit filename from Doxygen @file commands.
diego
parents:
11560
diff
changeset
|
26 * @file |
10692 | 27 * @brief CD Graphics Video Decoder |
28 * @author Michael Tison | |
29 * @sa http://wiki.multimedia.cx/index.php?title=CD_Graphics | |
30 * @sa http://www.ccs.neu.edu/home/bchafy/cdb/info/cdg | |
31 */ | |
32 | |
33 /// default screen sizes | |
34 #define CDG_FULL_WIDTH 300 | |
35 #define CDG_FULL_HEIGHT 216 | |
36 #define CDG_DISPLAY_WIDTH 294 | |
37 #define CDG_DISPLAY_HEIGHT 204 | |
38 #define CDG_BORDER_WIDTH 6 | |
39 #define CDG_BORDER_HEIGHT 12 | |
40 | |
41 /// masks | |
42 #define CDG_COMMAND 0x09 | |
43 #define CDG_MASK 0x3F | |
44 | |
45 /// instruction codes | |
46 #define CDG_INST_MEMORY_PRESET 1 | |
47 #define CDG_INST_BORDER_PRESET 2 | |
48 #define CDG_INST_TILE_BLOCK 6 | |
49 #define CDG_INST_SCROLL_PRESET 20 | |
50 #define CDG_INST_SCROLL_COPY 24 | |
51 #define CDG_INST_LOAD_PAL_LO 30 | |
52 #define CDG_INST_LOAD_PAL_HIGH 31 | |
53 #define CDG_INST_TILE_BLOCK_XOR 38 | |
54 | |
55 /// data sizes | |
56 #define CDG_PACKET_SIZE 24 | |
57 #define CDG_DATA_SIZE 16 | |
58 #define CDG_TILE_HEIGHT 12 | |
59 #define CDG_TILE_WIDTH 6 | |
60 #define CDG_MINIMUM_PKT_SIZE 6 | |
61 #define CDG_MINIMUM_SCROLL_SIZE 3 | |
62 #define CDG_HEADER_SIZE 8 | |
63 #define CDG_PALETTE_SIZE 16 | |
64 | |
65 typedef struct CDGraphicsContext { | |
66 AVFrame frame; | |
67 int hscroll; | |
68 int vscroll; | |
69 } CDGraphicsContext; | |
70 | |
71 static void cdg_init_frame(AVFrame *frame) | |
72 { | |
73 avcodec_get_frame_defaults(frame); | |
10710 | 74 frame->reference = 3; |
10692 | 75 frame->buffer_hints = FF_BUFFER_HINTS_VALID | |
10710 | 76 FF_BUFFER_HINTS_READABLE | |
10692 | 77 FF_BUFFER_HINTS_PRESERVE | |
78 FF_BUFFER_HINTS_REUSABLE; | |
79 } | |
80 | |
81 static av_cold int cdg_decode_init(AVCodecContext *avctx) | |
82 { | |
83 CDGraphicsContext *cc = avctx->priv_data; | |
84 | |
85 cdg_init_frame(&cc->frame); | |
86 | |
87 avctx->width = CDG_FULL_WIDTH; | |
88 avctx->height = CDG_FULL_HEIGHT; | |
89 avctx->pix_fmt = PIX_FMT_PAL8; | |
90 | |
91 return 0; | |
92 } | |
93 | |
94 static void cdg_border_preset(CDGraphicsContext *cc, uint8_t *data) | |
95 { | |
96 int y; | |
97 int lsize = cc->frame.linesize[0]; | |
98 uint8_t *buf = cc->frame.data[0]; | |
99 int color = data[0] & 0x0F; | |
100 | |
101 if (!(data[1] & 0x0F)) { | |
102 /// fill the top and bottom borders | |
103 memset(buf, color, CDG_BORDER_HEIGHT * lsize); | |
104 memset(buf + (CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT) * lsize, | |
105 color, CDG_BORDER_HEIGHT * lsize); | |
106 | |
107 /// fill the side borders | |
108 for (y = CDG_BORDER_HEIGHT; y < CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT; y++) { | |
109 memset(buf + y * lsize, color, CDG_BORDER_WIDTH); | |
110 memset(buf + CDG_FULL_WIDTH - CDG_BORDER_WIDTH + y * lsize, | |
111 color, CDG_BORDER_WIDTH); | |
112 } | |
113 } | |
114 } | |
115 | |
116 static void cdg_load_palette(CDGraphicsContext *cc, uint8_t *data, int low) | |
117 { | |
118 uint8_t r, g, b; | |
119 uint16_t color; | |
120 int i; | |
121 int array_offset = low ? 0 : 8; | |
122 uint32_t *palette = (uint32_t *) cc->frame.data[1]; | |
123 | |
124 for (i = 0; i < 8; i++) { | |
125 color = (data[2 * i] << 6) + (data[2 * i + 1] & 0x3F); | |
126 r = ((color >> 8) & 0x000F) * 17; | |
127 g = ((color >> 4) & 0x000F) * 17; | |
128 b = ((color ) & 0x000F) * 17; | |
129 palette[i + array_offset] = r << 16 | g << 8 | b; | |
130 } | |
131 cc->frame.palette_has_changed = 1; | |
132 } | |
133 | |
134 static int cdg_tile_block(CDGraphicsContext *cc, uint8_t *data, int b) | |
135 { | |
136 unsigned ci, ri; | |
137 int color; | |
138 int x, y; | |
139 int ai; | |
140 int stride = cc->frame.linesize[0]; | |
141 uint8_t *buf = cc->frame.data[0]; | |
142 | |
143 ri = (data[2] & 0x1F) * CDG_TILE_HEIGHT + cc->vscroll; | |
144 ci = (data[3] & 0x3F) * CDG_TILE_WIDTH + cc->hscroll; | |
145 | |
146 if (ri > (CDG_FULL_HEIGHT - CDG_TILE_HEIGHT)) | |
147 return AVERROR(EINVAL); | |
148 if (ci > (CDG_FULL_WIDTH - CDG_TILE_WIDTH)) | |
149 return AVERROR(EINVAL); | |
150 | |
151 for (y = 0; y < CDG_TILE_HEIGHT; y++) { | |
152 for (x = 0; x < CDG_TILE_WIDTH; x++) { | |
153 if (!((data[4 + y] >> (5 - x)) & 0x01)) | |
154 color = data[0] & 0x0F; | |
155 else | |
156 color = data[1] & 0x0F; | |
157 | |
158 ai = ci + x + (stride * (ri + y)); | |
159 if (b) | |
160 color ^= buf[ai]; | |
161 buf[ai] = color; | |
162 } | |
163 } | |
164 | |
165 return 0; | |
166 } | |
167 | |
168 #define UP 2 | |
169 #define DOWN 1 | |
170 #define LEFT 2 | |
171 #define RIGHT 1 | |
172 | |
173 static void cdg_copy_rect_buf(int out_tl_x, int out_tl_y, uint8_t *out, | |
174 int in_tl_x, int in_tl_y, uint8_t *in, | |
175 int w, int h, int stride) | |
176 { | |
177 int y; | |
178 | |
179 in += in_tl_x + in_tl_y * stride; | |
180 out += out_tl_x + out_tl_y * stride; | |
181 for (y = 0; y < h; y++) | |
182 memcpy(out + y * stride, in + y * stride, w); | |
183 } | |
184 | |
185 static void cdg_fill_rect_preset(int tl_x, int tl_y, uint8_t *out, | |
186 int color, int w, int h, int stride) | |
187 { | |
188 int y; | |
189 | |
190 for (y = tl_y; y < tl_y + h; y++) | |
191 memset(out + tl_x + y * stride, color, w); | |
192 } | |
193 | |
194 static void cdg_fill_wrapper(int out_tl_x, int out_tl_y, uint8_t *out, | |
195 int in_tl_x, int in_tl_y, uint8_t *in, | |
196 int color, int w, int h, int stride, int roll) | |
197 { | |
198 if (roll) { | |
199 cdg_copy_rect_buf(out_tl_x, out_tl_y, out, in_tl_x, in_tl_y, | |
200 in, w, h, stride); | |
201 } else { | |
202 cdg_fill_rect_preset(out_tl_x, out_tl_y, out, color, w, h, stride); | |
203 } | |
204 } | |
205 | |
206 static void cdg_scroll(CDGraphicsContext *cc, uint8_t *data, | |
207 AVFrame *new_frame, int roll_over) | |
208 { | |
209 int color; | |
210 int hscmd, h_off, hinc, vscmd, v_off, vinc; | |
211 int y; | |
212 int stride = cc->frame.linesize[0]; | |
213 uint8_t *in = cc->frame.data[0]; | |
214 uint8_t *out = new_frame->data[0]; | |
215 | |
216 color = data[0] & 0x0F; | |
217 hscmd = (data[1] & 0x30) >> 4; | |
218 vscmd = (data[2] & 0x30) >> 4; | |
219 | |
220 h_off = FFMIN(data[1] & 0x07, CDG_BORDER_WIDTH - 1); | |
221 v_off = FFMIN(data[2] & 0x07, CDG_BORDER_HEIGHT - 1); | |
222 | |
223 /// find the difference and save the offset for cdg_tile_block usage | |
224 hinc = h_off - cc->hscroll; | |
225 vinc = v_off - cc->vscroll; | |
226 cc->hscroll = h_off; | |
227 cc->vscroll = v_off; | |
228 | |
229 if (vscmd == UP) | |
230 vinc -= 12; | |
231 if (vscmd == DOWN) | |
232 vinc += 12; | |
233 if (hscmd == LEFT) | |
234 hinc -= 6; | |
235 if (hscmd == RIGHT) | |
236 hinc += 6; | |
237 | |
238 if (!hinc && !vinc) | |
239 return; | |
240 | |
241 memcpy(new_frame->data[1], cc->frame.data[1], CDG_PALETTE_SIZE * 4); | |
242 | |
243 for (y = FFMAX(0, vinc); y < FFMIN(CDG_FULL_HEIGHT + vinc, CDG_FULL_HEIGHT); y++) | |
244 memcpy(out + FFMAX(0, hinc) + stride * y, | |
245 in + FFMAX(0, hinc) - hinc + (y - vinc) * stride, | |
246 FFMIN(stride + hinc, stride)); | |
247 | |
248 if (vinc > 0) | |
249 cdg_fill_wrapper(0, 0, out, | |
250 0, CDG_FULL_HEIGHT - vinc, in, color, | |
251 stride, vinc, stride, roll_over); | |
252 else if (vinc < 0) | |
253 cdg_fill_wrapper(0, CDG_FULL_HEIGHT + vinc, out, | |
254 0, 0, in, color, | |
255 stride, -1 * vinc, stride, roll_over); | |
256 | |
257 if (hinc > 0) | |
258 cdg_fill_wrapper(0, 0, out, | |
259 CDG_FULL_WIDTH - hinc, 0, in, color, | |
260 hinc, CDG_FULL_HEIGHT, stride, roll_over); | |
261 else if (hinc < 0) | |
262 cdg_fill_wrapper(CDG_FULL_WIDTH + hinc, 0, out, | |
263 0, 0, in, color, | |
264 -1 * hinc, CDG_FULL_HEIGHT, stride, roll_over); | |
265 | |
266 } | |
267 | |
268 static int cdg_decode_frame(AVCodecContext *avctx, | |
269 void *data, int *data_size, AVPacket *avpkt) | |
270 { | |
271 const uint8_t *buf = avpkt->data; | |
272 int buf_size = avpkt->size; | |
273 int ret; | |
274 uint8_t command, inst; | |
275 uint8_t cdg_data[CDG_DATA_SIZE]; | |
276 AVFrame new_frame; | |
277 CDGraphicsContext *cc = avctx->priv_data; | |
278 | |
279 if (buf_size < CDG_MINIMUM_PKT_SIZE) { | |
280 av_log(avctx, AV_LOG_ERROR, "buffer too small for decoder\n"); | |
281 return AVERROR(EINVAL); | |
282 } | |
283 | |
284 ret = avctx->reget_buffer(avctx, &cc->frame); | |
285 if (ret) { | |
286 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); | |
287 return ret; | |
288 } | |
289 | |
290 command = bytestream_get_byte(&buf); | |
291 inst = bytestream_get_byte(&buf); | |
292 inst &= CDG_MASK; | |
293 buf += 2; /// skipping 2 unneeded bytes | |
294 bytestream_get_buffer(&buf, cdg_data, buf_size - CDG_HEADER_SIZE); | |
295 | |
296 if ((command & CDG_MASK) == CDG_COMMAND) { | |
297 switch (inst) { | |
298 case CDG_INST_MEMORY_PRESET: | |
299 if (!(cdg_data[1] & 0x0F)) | |
300 memset(cc->frame.data[0], cdg_data[0] & 0x0F, | |
301 cc->frame.linesize[0] * CDG_FULL_HEIGHT); | |
302 break; | |
303 case CDG_INST_LOAD_PAL_LO: | |
304 case CDG_INST_LOAD_PAL_HIGH: | |
305 if (buf_size - CDG_HEADER_SIZE < CDG_DATA_SIZE) { | |
306 av_log(avctx, AV_LOG_ERROR, "buffer too small for loading palette\n"); | |
307 return AVERROR(EINVAL); | |
308 } | |
309 | |
310 cdg_load_palette(cc, cdg_data, inst == CDG_INST_LOAD_PAL_LO); | |
311 break; | |
312 case CDG_INST_BORDER_PRESET: | |
313 cdg_border_preset(cc, cdg_data); | |
314 break; | |
315 case CDG_INST_TILE_BLOCK_XOR: | |
316 case CDG_INST_TILE_BLOCK: | |
317 if (buf_size - CDG_HEADER_SIZE < CDG_DATA_SIZE) { | |
318 av_log(avctx, AV_LOG_ERROR, "buffer too small for drawing tile\n"); | |
319 return AVERROR(EINVAL); | |
320 } | |
321 | |
322 ret = cdg_tile_block(cc, cdg_data, inst == CDG_INST_TILE_BLOCK_XOR); | |
323 if (ret) { | |
324 av_log(avctx, AV_LOG_ERROR, "tile is out of range\n"); | |
325 return ret; | |
326 } | |
327 break; | |
328 case CDG_INST_SCROLL_PRESET: | |
329 case CDG_INST_SCROLL_COPY: | |
330 if (buf_size - CDG_HEADER_SIZE < CDG_MINIMUM_SCROLL_SIZE) { | |
331 av_log(avctx, AV_LOG_ERROR, "buffer too small for scrolling\n"); | |
332 return AVERROR(EINVAL); | |
333 } | |
334 | |
335 cdg_init_frame(&new_frame); | |
336 ret = avctx->get_buffer(avctx, &new_frame); | |
337 if (ret) { | |
338 av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); | |
339 return ret; | |
340 } | |
341 | |
342 cdg_scroll(cc, cdg_data, &new_frame, inst == CDG_INST_SCROLL_COPY); | |
343 avctx->release_buffer(avctx, &cc->frame); | |
344 cc->frame = new_frame; | |
345 break; | |
346 default: | |
347 break; | |
348 } | |
349 | |
350 *data_size = sizeof(AVFrame); | |
351 } else { | |
352 *data_size = 0; | |
353 buf_size = 0; | |
354 } | |
355 | |
356 *(AVFrame *) data = cc->frame; | |
357 return buf_size; | |
358 } | |
359 | |
360 static av_cold int cdg_decode_end(AVCodecContext *avctx) | |
361 { | |
362 CDGraphicsContext *cc = avctx->priv_data; | |
363 | |
364 if (cc->frame.data[0]) | |
365 avctx->release_buffer(avctx, &cc->frame); | |
366 | |
367 return 0; | |
368 } | |
369 | |
370 AVCodec cdgraphics_decoder = { | |
371 "cdgraphics", | |
11560
8a4984c5cacc
Define AVMediaType enum, and use it instead of enum CodecType, which
stefano
parents:
10710
diff
changeset
|
372 AVMEDIA_TYPE_VIDEO, |
10692 | 373 CODEC_ID_CDGRAPHICS, |
374 sizeof(CDGraphicsContext), | |
375 cdg_decode_init, | |
376 NULL, | |
377 cdg_decode_end, | |
378 cdg_decode_frame, | |
379 CODEC_CAP_DR1, | |
12108
c35d7bc64882
Add new decoder property max_lowres and do not init decoder if requested value is higher.
cehoyos
parents:
11644
diff
changeset
|
380 .max_lowres = 5, |
10692 | 381 .long_name = NULL_IF_CONFIG_SMALL("CD Graphics video"), |
382 }; |