Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

  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. #include "internal.h"
  25.  
  26. /**
  27.  * @file
  28.  * @brief CD Graphics Video Decoder
  29.  * @author Michael Tison
  30.  * @see http://wiki.multimedia.cx/index.php?title=CD_Graphics
  31.  * @see http://www.ccs.neu.edu/home/bchafy/cdb/info/cdg
  32.  */
  33.  
  34. /// default screen sizes
  35. #define CDG_FULL_WIDTH           300
  36. #define CDG_FULL_HEIGHT          216
  37. #define CDG_DISPLAY_WIDTH        294
  38. #define CDG_DISPLAY_HEIGHT       204
  39. #define CDG_BORDER_WIDTH           6
  40. #define CDG_BORDER_HEIGHT         12
  41.  
  42. /// masks
  43. #define CDG_COMMAND             0x09
  44. #define CDG_MASK                0x3F
  45.  
  46. /// instruction codes
  47. #define CDG_INST_MEMORY_PRESET     1
  48. #define CDG_INST_BORDER_PRESET     2
  49. #define CDG_INST_TILE_BLOCK        6
  50. #define CDG_INST_SCROLL_PRESET    20
  51. #define CDG_INST_SCROLL_COPY      24
  52. #define CDG_INST_LOAD_PAL_LO      30
  53. #define CDG_INST_LOAD_PAL_HIGH    31
  54. #define CDG_INST_TILE_BLOCK_XOR   38
  55.  
  56. /// data sizes
  57. #define CDG_PACKET_SIZE           24
  58. #define CDG_DATA_SIZE             16
  59. #define CDG_TILE_HEIGHT           12
  60. #define CDG_TILE_WIDTH             6
  61. #define CDG_MINIMUM_PKT_SIZE       6
  62. #define CDG_MINIMUM_SCROLL_SIZE    3
  63. #define CDG_HEADER_SIZE            8
  64. #define CDG_PALETTE_SIZE          16
  65.  
  66. typedef struct CDGraphicsContext {
  67.     AVFrame *frame;
  68.     int hscroll;
  69.     int vscroll;
  70. } CDGraphicsContext;
  71.  
  72. static av_cold int cdg_decode_init(AVCodecContext *avctx)
  73. {
  74.     CDGraphicsContext *cc = avctx->priv_data;
  75.  
  76.     cc->frame = av_frame_alloc();
  77.     if (!cc->frame)
  78.         return AVERROR(ENOMEM);
  79.  
  80.     avctx->width   = CDG_FULL_WIDTH;
  81.     avctx->height  = CDG_FULL_HEIGHT;
  82.     avctx->pix_fmt = AV_PIX_FMT_PAL8;
  83.  
  84.     return 0;
  85. }
  86.  
  87. static void cdg_border_preset(CDGraphicsContext *cc, uint8_t *data)
  88. {
  89.     int y;
  90.     int lsize    = cc->frame->linesize[0];
  91.     uint8_t *buf = cc->frame->data[0];
  92.     int color    = data[0] & 0x0F;
  93.  
  94.     if (!(data[1] & 0x0F)) {
  95.         /// fill the top and bottom borders
  96.         memset(buf, color, CDG_BORDER_HEIGHT * lsize);
  97.         memset(buf + (CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT) * lsize,
  98.                color, CDG_BORDER_HEIGHT * lsize);
  99.  
  100.         /// fill the side borders
  101.         for (y = CDG_BORDER_HEIGHT; y < CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT; y++) {
  102.             memset(buf + y * lsize, color, CDG_BORDER_WIDTH);
  103.             memset(buf + CDG_FULL_WIDTH - CDG_BORDER_WIDTH + y * lsize,
  104.                    color, CDG_BORDER_WIDTH);
  105.         }
  106.     }
  107. }
  108.  
  109. static void cdg_load_palette(CDGraphicsContext *cc, uint8_t *data, int low)
  110. {
  111.     uint8_t r, g, b;
  112.     uint16_t color;
  113.     int i;
  114.     int array_offset  = low ? 0 : 8;
  115.     uint32_t *palette = (uint32_t *) cc->frame->data[1];
  116.  
  117.     for (i = 0; i < 8; i++) {
  118.         color = (data[2 * i] << 6) + (data[2 * i + 1] & 0x3F);
  119.         r = ((color >> 8) & 0x000F) * 17;
  120.         g = ((color >> 4) & 0x000F) * 17;
  121.         b = ((color     ) & 0x000F) * 17;
  122.         palette[i + array_offset] = 0xFFU << 24 | r << 16 | g << 8 | b;
  123.     }
  124.     cc->frame->palette_has_changed = 1;
  125. }
  126.  
  127. static int cdg_tile_block(CDGraphicsContext *cc, uint8_t *data, int b)
  128. {
  129.     unsigned ci, ri;
  130.     int color;
  131.     int x, y;
  132.     int ai;
  133.     int stride   = cc->frame->linesize[0];
  134.     uint8_t *buf = cc->frame->data[0];
  135.  
  136.     ri = (data[2] & 0x1F) * CDG_TILE_HEIGHT + cc->vscroll;
  137.     ci = (data[3] & 0x3F) * CDG_TILE_WIDTH  + cc->hscroll;
  138.  
  139.     if (ri > (CDG_FULL_HEIGHT - CDG_TILE_HEIGHT))
  140.         return AVERROR(EINVAL);
  141.     if (ci > (CDG_FULL_WIDTH - CDG_TILE_WIDTH))
  142.         return AVERROR(EINVAL);
  143.  
  144.     for (y = 0; y < CDG_TILE_HEIGHT; y++) {
  145.         for (x = 0; x < CDG_TILE_WIDTH; x++) {
  146.             if (!((data[4 + y] >> (5 - x)) & 0x01))
  147.                 color = data[0] & 0x0F;
  148.             else
  149.                 color = data[1] & 0x0F;
  150.  
  151.             ai = ci + x + (stride * (ri + y));
  152.             if (b)
  153.                 color ^= buf[ai];
  154.             buf[ai] = color;
  155.         }
  156.     }
  157.  
  158.     return 0;
  159. }
  160.  
  161. #define UP    2
  162. #define DOWN  1
  163. #define LEFT  2
  164. #define RIGHT 1
  165.  
  166. static void cdg_copy_rect_buf(int out_tl_x, int out_tl_y, uint8_t *out,
  167.                               int in_tl_x, int in_tl_y, uint8_t *in,
  168.                               int w, int h, int stride)
  169. {
  170.     int y;
  171.  
  172.     in  += in_tl_x  + in_tl_y  * stride;
  173.     out += out_tl_x + out_tl_y * stride;
  174.     for (y = 0; y < h; y++)
  175.         memcpy(out + y * stride, in + y * stride, w);
  176. }
  177.  
  178. static void cdg_fill_rect_preset(int tl_x, int tl_y, uint8_t *out,
  179.                                  int color, int w, int h, int stride)
  180. {
  181.     int y;
  182.  
  183.     for (y = tl_y; y < tl_y + h; y++)
  184.         memset(out + tl_x + y * stride, color, w);
  185. }
  186.  
  187. static void cdg_fill_wrapper(int out_tl_x, int out_tl_y, uint8_t *out,
  188.                              int in_tl_x, int in_tl_y, uint8_t *in,
  189.                              int color, int w, int h, int stride, int roll)
  190. {
  191.     if (roll) {
  192.         cdg_copy_rect_buf(out_tl_x, out_tl_y, out, in_tl_x, in_tl_y,
  193.                           in, w, h, stride);
  194.     } else {
  195.         cdg_fill_rect_preset(out_tl_x, out_tl_y, out, color, w, h, stride);
  196.     }
  197. }
  198.  
  199. static void cdg_scroll(CDGraphicsContext *cc, uint8_t *data,
  200.                        AVFrame *new_frame, int roll_over)
  201. {
  202.     int color;
  203.     int hscmd, h_off, hinc, vscmd, v_off, vinc;
  204.     int y;
  205.     int stride   = cc->frame->linesize[0];
  206.     uint8_t *in  = cc->frame->data[0];
  207.     uint8_t *out = new_frame->data[0];
  208.  
  209.     color =  data[0] & 0x0F;
  210.     hscmd = (data[1] & 0x30) >> 4;
  211.     vscmd = (data[2] & 0x30) >> 4;
  212.  
  213.     h_off =  FFMIN(data[1] & 0x07, CDG_BORDER_WIDTH  - 1);
  214.     v_off =  FFMIN(data[2] & 0x0F, CDG_BORDER_HEIGHT - 1);
  215.  
  216.     /// find the difference and save the offset for cdg_tile_block usage
  217.     hinc = h_off - cc->hscroll;
  218.     vinc = v_off - cc->vscroll;
  219.     cc->hscroll = h_off;
  220.     cc->vscroll = v_off;
  221.  
  222.     if (vscmd == UP)
  223.         vinc -= 12;
  224.     if (vscmd == DOWN)
  225.         vinc += 12;
  226.     if (hscmd == LEFT)
  227.         hinc -= 6;
  228.     if (hscmd == RIGHT)
  229.         hinc += 6;
  230.  
  231.     if (!hinc && !vinc)
  232.         return;
  233.  
  234.     memcpy(new_frame->data[1], cc->frame->data[1], CDG_PALETTE_SIZE * 4);
  235.  
  236.     for (y = FFMAX(0, vinc); y < FFMIN(CDG_FULL_HEIGHT + vinc, CDG_FULL_HEIGHT); y++)
  237.         memcpy(out + FFMAX(0, hinc) + stride * y,
  238.                in + FFMAX(0, hinc) - hinc + (y - vinc) * stride,
  239.                FFMIN(stride + hinc, stride));
  240.  
  241.     if (vinc > 0)
  242.         cdg_fill_wrapper(0, 0, out,
  243.                          0, CDG_FULL_HEIGHT - vinc, in, color,
  244.                          stride, vinc, stride, roll_over);
  245.     else if (vinc < 0)
  246.         cdg_fill_wrapper(0, CDG_FULL_HEIGHT + vinc, out,
  247.                          0, 0, in, color,
  248.                          stride, -1 * vinc, stride, roll_over);
  249.  
  250.     if (hinc > 0)
  251.         cdg_fill_wrapper(0, 0, out,
  252.                          CDG_FULL_WIDTH - hinc, 0, in, color,
  253.                          hinc, CDG_FULL_HEIGHT, stride, roll_over);
  254.     else if (hinc < 0)
  255.         cdg_fill_wrapper(CDG_FULL_WIDTH + hinc, 0, out,
  256.                          0, 0, in, color,
  257.                          -1 * hinc, CDG_FULL_HEIGHT, stride, roll_over);
  258.  
  259. }
  260.  
  261. static int cdg_decode_frame(AVCodecContext *avctx,
  262.                             void *data, int *got_frame, AVPacket *avpkt)
  263. {
  264.     const uint8_t *buf = avpkt->data;
  265.     int buf_size       = avpkt->size;
  266.     int ret;
  267.     uint8_t command, inst;
  268.     uint8_t cdg_data[CDG_DATA_SIZE] = {0};
  269.     AVFrame *frame = data;
  270.     CDGraphicsContext *cc = avctx->priv_data;
  271.  
  272.     if (buf_size < CDG_MINIMUM_PKT_SIZE) {
  273.         av_log(avctx, AV_LOG_ERROR, "buffer too small for decoder\n");
  274.         return AVERROR(EINVAL);
  275.     }
  276.     if (buf_size > CDG_HEADER_SIZE + CDG_DATA_SIZE) {
  277.         av_log(avctx, AV_LOG_ERROR, "buffer too big for decoder\n");
  278.         return AVERROR(EINVAL);
  279.     }
  280.  
  281.     if ((ret = ff_reget_buffer(avctx, cc->frame)) < 0)
  282.         return ret;
  283.     if (!avctx->frame_number) {
  284.         memset(cc->frame->data[0], 0, cc->frame->linesize[0] * avctx->height);
  285.         memset(cc->frame->data[1], 0, AVPALETTE_SIZE);
  286.     }
  287.  
  288.     command = bytestream_get_byte(&buf);
  289.     inst    = bytestream_get_byte(&buf);
  290.     inst    &= CDG_MASK;
  291.     buf += 2;  /// skipping 2 unneeded bytes
  292.  
  293.     if (buf_size > CDG_HEADER_SIZE)
  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.             if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)
  336.                 return ret;
  337.  
  338.             cdg_scroll(cc, cdg_data, frame, inst == CDG_INST_SCROLL_COPY);
  339.             av_frame_unref(cc->frame);
  340.             ret = av_frame_ref(cc->frame, frame);
  341.             if (ret < 0)
  342.                 return ret;
  343.             break;
  344.         default:
  345.             break;
  346.         }
  347.  
  348.         if (!frame->data[0]) {
  349.             ret = av_frame_ref(frame, cc->frame);
  350.             if (ret < 0)
  351.                 return ret;
  352.         }
  353.         *got_frame = 1;
  354.     } else {
  355.         *got_frame = 0;
  356.         buf_size   = 0;
  357.     }
  358.  
  359.     return buf_size;
  360. }
  361.  
  362. static av_cold int cdg_decode_end(AVCodecContext *avctx)
  363. {
  364.     CDGraphicsContext *cc = avctx->priv_data;
  365.  
  366.     av_frame_free(&cc->frame);
  367.  
  368.     return 0;
  369. }
  370.  
  371. AVCodec ff_cdgraphics_decoder = {
  372.     .name           = "cdgraphics",
  373.     .long_name      = NULL_IF_CONFIG_SMALL("CD Graphics video"),
  374.     .type           = AVMEDIA_TYPE_VIDEO,
  375.     .id             = AV_CODEC_ID_CDGRAPHICS,
  376.     .priv_data_size = sizeof(CDGraphicsContext),
  377.     .init           = cdg_decode_init,
  378.     .close          = cdg_decode_end,
  379.     .decode         = cdg_decode_frame,
  380.     .capabilities   = CODEC_CAP_DR1,
  381. };
  382.