PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/mythtv/external/FFmpeg/libavcodec/cdgraphics.c

http://github.com/MythTV/mythtv
C | 387 lines | 299 code | 54 blank | 34 comment | 52 complexity | d447b12fd0c5cc0dc993ddff4acddcf5 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, MIT, GPL-2.0, LGPL-2.0, BSD-3-Clause, GPL-3.0, LGPL-2.1, LGPL-3.0, MPL-2.0-no-copyleft-exception
  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. #include "avcodec.h"
  22. #include "bytestream.h"
  23. #include "internal.h"
  24. /**
  25. * @file
  26. * @brief CD Graphics Video Decoder
  27. * @author Michael Tison
  28. * @see http://wiki.multimedia.cx/index.php?title=CD_Graphics
  29. * @see http://www.ccs.neu.edu/home/bchafy/cdb/info/cdg
  30. */
  31. /// default screen sizes
  32. #define CDG_FULL_WIDTH 300
  33. #define CDG_FULL_HEIGHT 216
  34. #define CDG_DISPLAY_WIDTH 294
  35. #define CDG_DISPLAY_HEIGHT 204
  36. #define CDG_BORDER_WIDTH 6
  37. #define CDG_BORDER_HEIGHT 12
  38. /// masks
  39. #define CDG_COMMAND 0x09
  40. #define CDG_MASK 0x3F
  41. /// instruction codes
  42. #define CDG_INST_MEMORY_PRESET 1
  43. #define CDG_INST_BORDER_PRESET 2
  44. #define CDG_INST_TILE_BLOCK 6
  45. #define CDG_INST_SCROLL_PRESET 20
  46. #define CDG_INST_SCROLL_COPY 24
  47. #define CDG_INST_TRANSPARENT_COL 28
  48. #define CDG_INST_LOAD_PAL_LO 30
  49. #define CDG_INST_LOAD_PAL_HIGH 31
  50. #define CDG_INST_TILE_BLOCK_XOR 38
  51. /// data sizes
  52. #define CDG_PACKET_SIZE 24
  53. #define CDG_DATA_SIZE 16
  54. #define CDG_TILE_HEIGHT 12
  55. #define CDG_TILE_WIDTH 6
  56. #define CDG_MINIMUM_PKT_SIZE 6
  57. #define CDG_MINIMUM_SCROLL_SIZE 3
  58. #define CDG_HEADER_SIZE 8
  59. #define CDG_PALETTE_SIZE 16
  60. typedef struct CDGraphicsContext {
  61. AVFrame *frame;
  62. int hscroll;
  63. int vscroll;
  64. int transparency;
  65. int cleared;
  66. } CDGraphicsContext;
  67. static av_cold int cdg_decode_init(AVCodecContext *avctx)
  68. {
  69. CDGraphicsContext *cc = avctx->priv_data;
  70. cc->frame = av_frame_alloc();
  71. if (!cc->frame)
  72. return AVERROR(ENOMEM);
  73. cc->transparency = -1;
  74. avctx->pix_fmt = AV_PIX_FMT_PAL8;
  75. return ff_set_dimensions(avctx, CDG_FULL_WIDTH, CDG_FULL_HEIGHT);
  76. }
  77. static void cdg_border_preset(CDGraphicsContext *cc, uint8_t *data)
  78. {
  79. int y;
  80. int lsize = cc->frame->linesize[0];
  81. uint8_t *buf = cc->frame->data[0];
  82. int color = data[0] & 0x0F;
  83. if (!(data[1] & 0x0F)) {
  84. /// fill the top and bottom borders
  85. memset(buf, color, CDG_BORDER_HEIGHT * lsize);
  86. memset(buf + (CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT) * lsize,
  87. color, CDG_BORDER_HEIGHT * lsize);
  88. /// fill the side borders
  89. for (y = CDG_BORDER_HEIGHT; y < CDG_FULL_HEIGHT - CDG_BORDER_HEIGHT; y++) {
  90. memset(buf + y * lsize, color, CDG_BORDER_WIDTH);
  91. memset(buf + CDG_FULL_WIDTH - CDG_BORDER_WIDTH + y * lsize,
  92. color, CDG_BORDER_WIDTH);
  93. }
  94. }
  95. }
  96. static void cdg_load_palette(CDGraphicsContext *cc, uint8_t *data, int low)
  97. {
  98. uint8_t r, g, b;
  99. uint16_t color;
  100. int i;
  101. int array_offset = low ? 0 : 8;
  102. uint32_t *palette = (uint32_t *) cc->frame->data[1];
  103. for (i = 0; i < 8; i++) {
  104. color = (data[2 * i] << 6) + (data[2 * i + 1] & 0x3F);
  105. r = ((color >> 8) & 0x000F) * 17;
  106. g = ((color >> 4) & 0x000F) * 17;
  107. b = ((color ) & 0x000F) * 17;
  108. palette[i + array_offset] = 0xFFU << 24 | r << 16 | g << 8 | b;
  109. if (cc->transparency >= 0)
  110. palette[cc->transparency] &= 0xFFFFFF;
  111. }
  112. cc->frame->palette_has_changed = 1;
  113. }
  114. static int cdg_tile_block(CDGraphicsContext *cc, uint8_t *data, int b)
  115. {
  116. unsigned ci, ri;
  117. int color;
  118. int x, y;
  119. int ai;
  120. int stride = cc->frame->linesize[0];
  121. uint8_t *buf = cc->frame->data[0];
  122. ri = (data[2] & 0x1F) * CDG_TILE_HEIGHT + cc->vscroll;
  123. ci = (data[3] & 0x3F) * CDG_TILE_WIDTH + cc->hscroll;
  124. if (ri > (CDG_FULL_HEIGHT - CDG_TILE_HEIGHT))
  125. return AVERROR(EINVAL);
  126. if (ci > (CDG_FULL_WIDTH - CDG_TILE_WIDTH))
  127. return AVERROR(EINVAL);
  128. for (y = 0; y < CDG_TILE_HEIGHT; y++) {
  129. for (x = 0; x < CDG_TILE_WIDTH; x++) {
  130. if (!((data[4 + y] >> (5 - x)) & 0x01))
  131. color = data[0] & 0x0F;
  132. else
  133. color = data[1] & 0x0F;
  134. ai = ci + x + (stride * (ri + y));
  135. if (b)
  136. color ^= buf[ai];
  137. buf[ai] = color;
  138. }
  139. }
  140. return 0;
  141. }
  142. #define UP 2
  143. #define DOWN 1
  144. #define LEFT 2
  145. #define RIGHT 1
  146. static void cdg_copy_rect_buf(int out_tl_x, int out_tl_y, uint8_t *out,
  147. int in_tl_x, int in_tl_y, uint8_t *in,
  148. int w, int h, int stride)
  149. {
  150. int y;
  151. in += in_tl_x + in_tl_y * stride;
  152. out += out_tl_x + out_tl_y * stride;
  153. for (y = 0; y < h; y++)
  154. memcpy(out + y * stride, in + y * stride, w);
  155. }
  156. static void cdg_fill_rect_preset(int tl_x, int tl_y, uint8_t *out,
  157. int color, int w, int h, int stride)
  158. {
  159. int y;
  160. for (y = tl_y; y < tl_y + h; y++)
  161. memset(out + tl_x + y * stride, color, w);
  162. }
  163. static void cdg_fill_wrapper(int out_tl_x, int out_tl_y, uint8_t *out,
  164. int in_tl_x, int in_tl_y, uint8_t *in,
  165. int color, int w, int h, int stride, int roll)
  166. {
  167. if (roll) {
  168. cdg_copy_rect_buf(out_tl_x, out_tl_y, out, in_tl_x, in_tl_y,
  169. in, w, h, stride);
  170. } else {
  171. cdg_fill_rect_preset(out_tl_x, out_tl_y, out, color, w, h, stride);
  172. }
  173. }
  174. static void cdg_scroll(CDGraphicsContext *cc, uint8_t *data,
  175. AVFrame *new_frame, int roll_over)
  176. {
  177. int color;
  178. int hscmd, h_off, hinc, vscmd, v_off, vinc;
  179. int y;
  180. int stride = cc->frame->linesize[0];
  181. uint8_t *in = cc->frame->data[0];
  182. uint8_t *out = new_frame->data[0];
  183. color = data[0] & 0x0F;
  184. hscmd = (data[1] & 0x30) >> 4;
  185. vscmd = (data[2] & 0x30) >> 4;
  186. h_off = FFMIN(data[1] & 0x07, CDG_BORDER_WIDTH - 1);
  187. v_off = FFMIN(data[2] & 0x0F, CDG_BORDER_HEIGHT - 1);
  188. /// find the difference and save the offset for cdg_tile_block usage
  189. hinc = h_off - cc->hscroll;
  190. vinc = v_off - cc->vscroll;
  191. cc->hscroll = h_off;
  192. cc->vscroll = v_off;
  193. if (vscmd == UP)
  194. vinc -= 12;
  195. if (vscmd == DOWN)
  196. vinc += 12;
  197. if (hscmd == LEFT)
  198. hinc -= 6;
  199. if (hscmd == RIGHT)
  200. hinc += 6;
  201. if (!hinc && !vinc)
  202. return;
  203. memcpy(new_frame->data[1], cc->frame->data[1], CDG_PALETTE_SIZE * 4);
  204. for (y = FFMAX(0, vinc); y < FFMIN(CDG_FULL_HEIGHT + vinc, CDG_FULL_HEIGHT); y++)
  205. memcpy(out + FFMAX(0, hinc) + stride * y,
  206. in + FFMAX(0, hinc) - hinc + (y - vinc) * stride,
  207. FFMIN(stride + hinc, stride));
  208. if (vinc > 0)
  209. cdg_fill_wrapper(0, 0, out,
  210. 0, CDG_FULL_HEIGHT - vinc, in, color,
  211. stride, vinc, stride, roll_over);
  212. else if (vinc < 0)
  213. cdg_fill_wrapper(0, CDG_FULL_HEIGHT + vinc, out,
  214. 0, 0, in, color,
  215. stride, -1 * vinc, stride, roll_over);
  216. if (hinc > 0)
  217. cdg_fill_wrapper(0, 0, out,
  218. CDG_FULL_WIDTH - hinc, 0, in, color,
  219. hinc, CDG_FULL_HEIGHT, stride, roll_over);
  220. else if (hinc < 0)
  221. cdg_fill_wrapper(CDG_FULL_WIDTH + hinc, 0, out,
  222. 0, 0, in, color,
  223. -1 * hinc, CDG_FULL_HEIGHT, stride, roll_over);
  224. }
  225. static int cdg_decode_frame(AVCodecContext *avctx,
  226. void *data, int *got_frame, AVPacket *avpkt)
  227. {
  228. GetByteContext gb;
  229. int buf_size = avpkt->size;
  230. int ret;
  231. uint8_t command, inst;
  232. uint8_t cdg_data[CDG_DATA_SIZE] = {0};
  233. AVFrame *frame = data;
  234. CDGraphicsContext *cc = avctx->priv_data;
  235. if (buf_size < CDG_MINIMUM_PKT_SIZE) {
  236. av_log(avctx, AV_LOG_ERROR, "buffer too small for decoder\n");
  237. return AVERROR(EINVAL);
  238. }
  239. if (buf_size > CDG_HEADER_SIZE + CDG_DATA_SIZE) {
  240. av_log(avctx, AV_LOG_ERROR, "buffer too big for decoder\n");
  241. return AVERROR(EINVAL);
  242. }
  243. bytestream2_init(&gb, avpkt->data, avpkt->size);
  244. if ((ret = ff_reget_buffer(avctx, cc->frame)) < 0)
  245. return ret;
  246. if (!cc->cleared) {
  247. memset(cc->frame->data[0], 0, cc->frame->linesize[0] * avctx->height);
  248. memset(cc->frame->data[1], 0, AVPALETTE_SIZE);
  249. cc->cleared = 1;
  250. }
  251. command = bytestream2_get_byte(&gb);
  252. inst = bytestream2_get_byte(&gb);
  253. inst &= CDG_MASK;
  254. bytestream2_skip(&gb, 2);
  255. bytestream2_get_buffer(&gb, cdg_data, sizeof(cdg_data));
  256. if ((command & CDG_MASK) == CDG_COMMAND) {
  257. switch (inst) {
  258. case CDG_INST_MEMORY_PRESET:
  259. if (!(cdg_data[1] & 0x0F))
  260. memset(cc->frame->data[0], cdg_data[0] & 0x0F,
  261. cc->frame->linesize[0] * CDG_FULL_HEIGHT);
  262. break;
  263. case CDG_INST_LOAD_PAL_LO:
  264. case CDG_INST_LOAD_PAL_HIGH:
  265. if (buf_size - CDG_HEADER_SIZE < CDG_DATA_SIZE) {
  266. av_log(avctx, AV_LOG_ERROR, "buffer too small for loading palette\n");
  267. return AVERROR(EINVAL);
  268. }
  269. cdg_load_palette(cc, cdg_data, inst == CDG_INST_LOAD_PAL_LO);
  270. break;
  271. case CDG_INST_BORDER_PRESET:
  272. cdg_border_preset(cc, cdg_data);
  273. break;
  274. case CDG_INST_TILE_BLOCK_XOR:
  275. case CDG_INST_TILE_BLOCK:
  276. if (buf_size - CDG_HEADER_SIZE < CDG_DATA_SIZE) {
  277. av_log(avctx, AV_LOG_ERROR, "buffer too small for drawing tile\n");
  278. return AVERROR(EINVAL);
  279. }
  280. ret = cdg_tile_block(cc, cdg_data, inst == CDG_INST_TILE_BLOCK_XOR);
  281. if (ret) {
  282. av_log(avctx, AV_LOG_ERROR, "tile is out of range\n");
  283. return ret;
  284. }
  285. break;
  286. case CDG_INST_SCROLL_PRESET:
  287. case CDG_INST_SCROLL_COPY:
  288. if (buf_size - CDG_HEADER_SIZE < CDG_MINIMUM_SCROLL_SIZE) {
  289. av_log(avctx, AV_LOG_ERROR, "buffer too small for scrolling\n");
  290. return AVERROR(EINVAL);
  291. }
  292. if ((ret = ff_get_buffer(avctx, frame, AV_GET_BUFFER_FLAG_REF)) < 0)
  293. return ret;
  294. cdg_scroll(cc, cdg_data, frame, inst == CDG_INST_SCROLL_COPY);
  295. av_frame_unref(cc->frame);
  296. ret = av_frame_ref(cc->frame, frame);
  297. if (ret < 0)
  298. return ret;
  299. break;
  300. case CDG_INST_TRANSPARENT_COL:
  301. cc->transparency = cdg_data[0] & 0xF;
  302. break;
  303. default:
  304. break;
  305. }
  306. if (!frame->data[0]) {
  307. ret = av_frame_ref(frame, cc->frame);
  308. if (ret < 0)
  309. return ret;
  310. }
  311. *got_frame = 1;
  312. } else {
  313. *got_frame = 0;
  314. }
  315. return avpkt->size;
  316. }
  317. static av_cold int cdg_decode_end(AVCodecContext *avctx)
  318. {
  319. CDGraphicsContext *cc = avctx->priv_data;
  320. av_frame_free(&cc->frame);
  321. return 0;
  322. }
  323. AVCodec ff_cdgraphics_decoder = {
  324. .name = "cdgraphics",
  325. .long_name = NULL_IF_CONFIG_SMALL("CD Graphics video"),
  326. .type = AVMEDIA_TYPE_VIDEO,
  327. .id = AV_CODEC_ID_CDGRAPHICS,
  328. .priv_data_size = sizeof(CDGraphicsContext),
  329. .init = cdg_decode_init,
  330. .close = cdg_decode_end,
  331. .decode = cdg_decode_frame,
  332. .capabilities = AV_CODEC_CAP_DR1,
  333. };