Libav
|
00001 /* 00002 * CD Graphics Video Decoder 00003 * Copyright (c) 2009 Michael Tison 00004 * 00005 * This file is part of FFmpeg. 00006 * 00007 * FFmpeg is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * FFmpeg is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with FFmpeg; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 #include "avcodec.h" 00023 #include "bytestream.h" 00024 00033 00034 #define CDG_FULL_WIDTH 300 00035 #define CDG_FULL_HEIGHT 216 00036 #define CDG_DISPLAY_WIDTH 294 00037 #define CDG_DISPLAY_HEIGHT 204 00038 #define CDG_BORDER_WIDTH 6 00039 #define CDG_BORDER_HEIGHT 12 00040 00042 #define CDG_COMMAND 0x09 00043 #define CDG_MASK 0x3F 00044 00046 #define CDG_INST_MEMORY_PRESET 1 00047 #define CDG_INST_BORDER_PRESET 2 00048 #define CDG_INST_TILE_BLOCK 6 00049 #define CDG_INST_SCROLL_PRESET 20 00050 #define CDG_INST_SCROLL_COPY 24 00051 #define CDG_INST_LOAD_PAL_LO 30 00052 #define CDG_INST_LOAD_PAL_HIGH 31 00053 #define CDG_INST_TILE_BLOCK_XOR 38 00054 00056 #define CDG_PACKET_SIZE 24 00057 #define CDG_DATA_SIZE 16 00058 #define CDG_TILE_HEIGHT 12 00059 #define CDG_TILE_WIDTH 6 00060 #define CDG_MINIMUM_PKT_SIZE 6 00061 #define CDG_MINIMUM_SCROLL_SIZE 3 00062 #define CDG_HEADER_SIZE 8 00063 #define CDG_PALETTE_SIZE 16 00064 00065 typedef struct CDGraphicsContext { 00066 AVFrame frame; 00067 int hscroll; 00068 int vscroll; 00069 } CDGraphicsContext; 00070 00071 static void cdg_init_frame(AVFrame *frame) 00072 { 00073 avcodec_get_frame_defaults(frame); 00074 frame->reference = 3; 00075 frame->buffer_hints = FF_BUFFER_HINTS_VALID | 00076 FF_BUFFER_HINTS_READABLE | 00077 FF_BUFFER_HINTS_PRESERVE | 00078 FF_BUFFER_HINTS_REUSABLE; 00079 } 00080 00081 static av_cold int cdg_decode_init(AVCodecContext *avctx) 00082 { 00083 CDGraphicsContext *cc = avctx->priv_data; 00084 00085 cdg_init_frame(&cc->frame); 00086 00087 avctx->width = CDG_FULL_WIDTH; 00088 avctx->height = CDG_FULL_HEIGHT; 00089 avctx->pix_fmt = PIX_FMT_PAL8; 00090 00091 return 0; 00092 } 00093 00094 static void cdg_border_preset(CDGraphicsContext *cc, uint8_t *data) 00095 { 00096 int y; 00097 int lsize = cc->frame.linesize[0]; 00098 uint8_t *buf = cc->frame.data[0]; 00099 int color = data[0] & 0x0F; 00100 00101 if (!(data[1] & 0x0F)) { 00103 memset(buf, color, CDG_BORDER_HEIGHT * lsize); 00104 memset(buf + (CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT) * lsize, 00105 color, CDG_BORDER_HEIGHT * lsize); 00106 00108 for (y = CDG_BORDER_HEIGHT; y < CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT; y++) { 00109 memset(buf + y * lsize, color, CDG_BORDER_WIDTH); 00110 memset(buf + CDG_FULL_WIDTH - CDG_BORDER_WIDTH + y * lsize, 00111 color, CDG_BORDER_WIDTH); 00112 } 00113 } 00114 } 00115 00116 static void cdg_load_palette(CDGraphicsContext *cc, uint8_t *data, int low) 00117 { 00118 uint8_t r, g, b; 00119 uint16_t color; 00120 int i; 00121 int array_offset = low ? 0 : 8; 00122 uint32_t *palette = (uint32_t *) cc->frame.data[1]; 00123 00124 for (i = 0; i < 8; i++) { 00125 color = (data[2 * i] << 6) + (data[2 * i + 1] & 0x3F); 00126 r = ((color >> 8) & 0x000F) * 17; 00127 g = ((color >> 4) & 0x000F) * 17; 00128 b = ((color ) & 0x000F) * 17; 00129 palette[i + array_offset] = r << 16 | g << 8 | b; 00130 } 00131 cc->frame.palette_has_changed = 1; 00132 } 00133 00134 static int cdg_tile_block(CDGraphicsContext *cc, uint8_t *data, int b) 00135 { 00136 unsigned ci, ri; 00137 int color; 00138 int x, y; 00139 int ai; 00140 int stride = cc->frame.linesize[0]; 00141 uint8_t *buf = cc->frame.data[0]; 00142 00143 ri = (data[2] & 0x1F) * CDG_TILE_HEIGHT + cc->vscroll; 00144 ci = (data[3] & 0x3F) * CDG_TILE_WIDTH + cc->hscroll; 00145 00146 if (ri > (CDG_FULL_HEIGHT - CDG_TILE_HEIGHT)) 00147 return AVERROR(EINVAL); 00148 if (ci > (CDG_FULL_WIDTH - CDG_TILE_WIDTH)) 00149 return AVERROR(EINVAL); 00150 00151 for (y = 0; y < CDG_TILE_HEIGHT; y++) { 00152 for (x = 0; x < CDG_TILE_WIDTH; x++) { 00153 if (!((data[4 + y] >> (5 - x)) & 0x01)) 00154 color = data[0] & 0x0F; 00155 else 00156 color = data[1] & 0x0F; 00157 00158 ai = ci + x + (stride * (ri + y)); 00159 if (b) 00160 color ^= buf[ai]; 00161 buf[ai] = color; 00162 } 00163 } 00164 00165 return 0; 00166 } 00167 00168 #define UP 2 00169 #define DOWN 1 00170 #define LEFT 2 00171 #define RIGHT 1 00172 00173 static void cdg_copy_rect_buf(int out_tl_x, int out_tl_y, uint8_t *out, 00174 int in_tl_x, int in_tl_y, uint8_t *in, 00175 int w, int h, int stride) 00176 { 00177 int y; 00178 00179 in += in_tl_x + in_tl_y * stride; 00180 out += out_tl_x + out_tl_y * stride; 00181 for (y = 0; y < h; y++) 00182 memcpy(out + y * stride, in + y * stride, w); 00183 } 00184 00185 static void cdg_fill_rect_preset(int tl_x, int tl_y, uint8_t *out, 00186 int color, int w, int h, int stride) 00187 { 00188 int y; 00189 00190 for (y = tl_y; y < tl_y + h; y++) 00191 memset(out + tl_x + y * stride, color, w); 00192 } 00193 00194 static void cdg_fill_wrapper(int out_tl_x, int out_tl_y, uint8_t *out, 00195 int in_tl_x, int in_tl_y, uint8_t *in, 00196 int color, int w, int h, int stride, int roll) 00197 { 00198 if (roll) { 00199 cdg_copy_rect_buf(out_tl_x, out_tl_y, out, in_tl_x, in_tl_y, 00200 in, w, h, stride); 00201 } else { 00202 cdg_fill_rect_preset(out_tl_x, out_tl_y, out, color, w, h, stride); 00203 } 00204 } 00205 00206 static void cdg_scroll(CDGraphicsContext *cc, uint8_t *data, 00207 AVFrame *new_frame, int roll_over) 00208 { 00209 int color; 00210 int hscmd, h_off, hinc, vscmd, v_off, vinc; 00211 int y; 00212 int stride = cc->frame.linesize[0]; 00213 uint8_t *in = cc->frame.data[0]; 00214 uint8_t *out = new_frame->data[0]; 00215 00216 color = data[0] & 0x0F; 00217 hscmd = (data[1] & 0x30) >> 4; 00218 vscmd = (data[2] & 0x30) >> 4; 00219 00220 h_off = FFMIN(data[1] & 0x07, CDG_BORDER_WIDTH - 1); 00221 v_off = FFMIN(data[2] & 0x07, CDG_BORDER_HEIGHT - 1); 00222 00224 hinc = h_off - cc->hscroll; 00225 vinc = v_off - cc->vscroll; 00226 cc->hscroll = h_off; 00227 cc->vscroll = v_off; 00228 00229 if (vscmd == UP) 00230 vinc -= 12; 00231 if (vscmd == DOWN) 00232 vinc += 12; 00233 if (hscmd == LEFT) 00234 hinc -= 6; 00235 if (hscmd == RIGHT) 00236 hinc += 6; 00237 00238 if (!hinc && !vinc) 00239 return; 00240 00241 memcpy(new_frame->data[1], cc->frame.data[1], CDG_PALETTE_SIZE * 4); 00242 00243 for (y = FFMAX(0, vinc); y < FFMIN(CDG_FULL_HEIGHT + vinc, CDG_FULL_HEIGHT); y++) 00244 memcpy(out + FFMAX(0, hinc) + stride * y, 00245 in + FFMAX(0, hinc) - hinc + (y - vinc) * stride, 00246 FFMIN(stride + hinc, stride)); 00247 00248 if (vinc > 0) 00249 cdg_fill_wrapper(0, 0, out, 00250 0, CDG_FULL_HEIGHT - vinc, in, color, 00251 stride, vinc, stride, roll_over); 00252 else if (vinc < 0) 00253 cdg_fill_wrapper(0, CDG_FULL_HEIGHT + vinc, out, 00254 0, 0, in, color, 00255 stride, -1 * vinc, stride, roll_over); 00256 00257 if (hinc > 0) 00258 cdg_fill_wrapper(0, 0, out, 00259 CDG_FULL_WIDTH - hinc, 0, in, color, 00260 hinc, CDG_FULL_HEIGHT, stride, roll_over); 00261 else if (hinc < 0) 00262 cdg_fill_wrapper(CDG_FULL_WIDTH + hinc, 0, out, 00263 0, 0, in, color, 00264 -1 * hinc, CDG_FULL_HEIGHT, stride, roll_over); 00265 00266 } 00267 00268 static int cdg_decode_frame(AVCodecContext *avctx, 00269 void *data, int *data_size, AVPacket *avpkt) 00270 { 00271 const uint8_t *buf = avpkt->data; 00272 int buf_size = avpkt->size; 00273 int ret; 00274 uint8_t command, inst; 00275 uint8_t cdg_data[CDG_DATA_SIZE]; 00276 AVFrame new_frame; 00277 CDGraphicsContext *cc = avctx->priv_data; 00278 00279 if (buf_size < CDG_MINIMUM_PKT_SIZE) { 00280 av_log(avctx, AV_LOG_ERROR, "buffer too small for decoder\n"); 00281 return AVERROR(EINVAL); 00282 } 00283 00284 ret = avctx->reget_buffer(avctx, &cc->frame); 00285 if (ret) { 00286 av_log(avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); 00287 return ret; 00288 } 00289 00290 command = bytestream_get_byte(&buf); 00291 inst = bytestream_get_byte(&buf); 00292 inst &= CDG_MASK; 00293 buf += 2; 00294 bytestream_get_buffer(&buf, cdg_data, buf_size - CDG_HEADER_SIZE); 00295 00296 if ((command & CDG_MASK) == CDG_COMMAND) { 00297 switch (inst) { 00298 case CDG_INST_MEMORY_PRESET: 00299 if (!(cdg_data[1] & 0x0F)) 00300 memset(cc->frame.data[0], cdg_data[0] & 0x0F, 00301 cc->frame.linesize[0] * CDG_FULL_HEIGHT); 00302 break; 00303 case CDG_INST_LOAD_PAL_LO: 00304 case CDG_INST_LOAD_PAL_HIGH: 00305 if (buf_size - CDG_HEADER_SIZE < CDG_DATA_SIZE) { 00306 av_log(avctx, AV_LOG_ERROR, "buffer too small for loading palette\n"); 00307 return AVERROR(EINVAL); 00308 } 00309 00310 cdg_load_palette(cc, cdg_data, inst == CDG_INST_LOAD_PAL_LO); 00311 break; 00312 case CDG_INST_BORDER_PRESET: 00313 cdg_border_preset(cc, cdg_data); 00314 break; 00315 case CDG_INST_TILE_BLOCK_XOR: 00316 case CDG_INST_TILE_BLOCK: 00317 if (buf_size - CDG_HEADER_SIZE < CDG_DATA_SIZE) { 00318 av_log(avctx, AV_LOG_ERROR, "buffer too small for drawing tile\n"); 00319 return AVERROR(EINVAL); 00320 } 00321 00322 ret = cdg_tile_block(cc, cdg_data, inst == CDG_INST_TILE_BLOCK_XOR); 00323 if (ret) { 00324 av_log(avctx, AV_LOG_ERROR, "tile is out of range\n"); 00325 return ret; 00326 } 00327 break; 00328 case CDG_INST_SCROLL_PRESET: 00329 case CDG_INST_SCROLL_COPY: 00330 if (buf_size - CDG_HEADER_SIZE < CDG_MINIMUM_SCROLL_SIZE) { 00331 av_log(avctx, AV_LOG_ERROR, "buffer too small for scrolling\n"); 00332 return AVERROR(EINVAL); 00333 } 00334 00335 cdg_init_frame(&new_frame); 00336 ret = avctx->get_buffer(avctx, &new_frame); 00337 if (ret) { 00338 av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); 00339 return ret; 00340 } 00341 00342 cdg_scroll(cc, cdg_data, &new_frame, inst == CDG_INST_SCROLL_COPY); 00343 avctx->release_buffer(avctx, &cc->frame); 00344 cc->frame = new_frame; 00345 break; 00346 default: 00347 break; 00348 } 00349 00350 *data_size = sizeof(AVFrame); 00351 } else { 00352 *data_size = 0; 00353 buf_size = 0; 00354 } 00355 00356 *(AVFrame *) data = cc->frame; 00357 return buf_size; 00358 } 00359 00360 static av_cold int cdg_decode_end(AVCodecContext *avctx) 00361 { 00362 CDGraphicsContext *cc = avctx->priv_data; 00363 00364 if (cc->frame.data[0]) 00365 avctx->release_buffer(avctx, &cc->frame); 00366 00367 return 0; 00368 } 00369 00370 AVCodec cdgraphics_decoder = { 00371 "cdgraphics", 00372 AVMEDIA_TYPE_VIDEO, 00373 CODEC_ID_CDGRAPHICS, 00374 sizeof(CDGraphicsContext), 00375 cdg_decode_init, 00376 NULL, 00377 cdg_decode_end, 00378 cdg_decode_frame, 00379 CODEC_CAP_DR1, 00380 .long_name = NULL_IF_CONFIG_SMALL("CD Graphics video"), 00381 };