1/* 2 * Pictor/PC Paint decoder 3 * Copyright (c) 2010 Peter Ross <pross@xvid.org> 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 * Pictor/PC Paint decoder 25 */ 26 27#include "libavutil/imgutils.h" 28#include "avcodec.h" 29#include "bytestream.h" 30#include "cga_data.h" 31#include "codec_internal.h" 32#include "internal.h" 33 34typedef struct PicContext { 35 int width, height; 36 int nb_planes; 37 GetByteContext g; 38} PicContext; 39 40static void picmemset_8bpp(PicContext *s, AVFrame *frame, int value, int run, 41 int *x, int *y) 42{ 43 while (run > 0) { 44 uint8_t *d = frame->data[0] + *y * frame->linesize[0]; 45 if (*x + run >= s->width) { 46 int n = s->width - *x; 47 memset(d + *x, value, n); 48 run -= n; 49 *x = 0; 50 *y -= 1; 51 if (*y < 0) 52 break; 53 } else { 54 memset(d + *x, value, run); 55 *x += run; 56 break; 57 } 58 } 59} 60 61static void picmemset(PicContext *s, AVFrame *frame, unsigned value, int run, 62 int *x, int *y, int *plane, int bits_per_plane) 63{ 64 uint8_t *d; 65 int shift = *plane * bits_per_plane; 66 unsigned mask = ((1U << bits_per_plane) - 1) << shift; 67 int xl = *x; 68 int yl = *y; 69 int planel = *plane; 70 int pixels_per_value = 8/bits_per_plane; 71 value <<= shift; 72 73 d = frame->data[0] + yl * frame->linesize[0]; 74 while (run > 0) { 75 int j; 76 for (j = 8-bits_per_plane; j >= 0; j -= bits_per_plane) { 77 d[xl] |= (value >> j) & mask; 78 xl += 1; 79 while (xl == s->width) { 80 yl -= 1; 81 xl = 0; 82 if (yl < 0) { 83 yl = s->height - 1; 84 planel += 1; 85 if (planel >= s->nb_planes) 86 goto end; 87 value <<= bits_per_plane; 88 mask <<= bits_per_plane; 89 } 90 d = frame->data[0] + yl * frame->linesize[0]; 91 if (s->nb_planes == 1 && 92 run*pixels_per_value >= s->width && 93 pixels_per_value < (s->width / pixels_per_value * pixels_per_value) 94 ) { 95 for (; xl < pixels_per_value; xl ++) { 96 j = (j < bits_per_plane ? 8 : j) - bits_per_plane; 97 d[xl] |= (value >> j) & mask; 98 } 99 av_memcpy_backptr(d+xl, pixels_per_value, s->width - xl); 100 run -= s->width / pixels_per_value; 101 xl = s->width / pixels_per_value * pixels_per_value; 102 } 103 } 104 } 105 run--; 106 } 107end: 108 *x = xl; 109 *y = yl; 110 *plane = planel; 111} 112 113static const uint8_t cga_mode45_index[6][4] = { 114 [0] = { 0, 3, 5, 7 }, // mode4, palette#1, low intensity 115 [1] = { 0, 2, 4, 6 }, // mode4, palette#2, low intensity 116 [2] = { 0, 3, 4, 7 }, // mode5, low intensity 117 [3] = { 0, 11, 13, 15 }, // mode4, palette#1, high intensity 118 [4] = { 0, 10, 12, 14 }, // mode4, palette#2, high intensity 119 [5] = { 0, 11, 12, 15 }, // mode5, high intensity 120}; 121 122static int decode_frame(AVCodecContext *avctx, AVFrame *frame, 123 int *got_frame, AVPacket *avpkt) 124{ 125 PicContext *s = avctx->priv_data; 126 uint32_t *palette; 127 int bits_per_plane, bpp, etype, esize, npal, pos_after_pal; 128 int i, x, y, plane, tmp, ret, val; 129 130 bytestream2_init(&s->g, avpkt->data, avpkt->size); 131 132 if (bytestream2_get_bytes_left(&s->g) < 11) 133 return AVERROR_INVALIDDATA; 134 135 if (bytestream2_get_le16u(&s->g) != 0x1234) 136 return AVERROR_INVALIDDATA; 137 138 s->width = bytestream2_get_le16u(&s->g); 139 s->height = bytestream2_get_le16u(&s->g); 140 bytestream2_skip(&s->g, 4); 141 tmp = bytestream2_get_byteu(&s->g); 142 bits_per_plane = tmp & 0xF; 143 s->nb_planes = (tmp >> 4) + 1; 144 bpp = bits_per_plane * s->nb_planes; 145 if (bits_per_plane > 8 || bpp < 1 || bpp > 32) { 146 avpriv_request_sample(avctx, "Unsupported bit depth"); 147 return AVERROR_PATCHWELCOME; 148 } 149 150 if (bytestream2_peek_byte(&s->g) == 0xFF || bpp == 1 || bpp == 4 || bpp == 8) { 151 bytestream2_skip(&s->g, 2); 152 etype = bytestream2_get_le16(&s->g); 153 esize = bytestream2_get_le16(&s->g); 154 if (bytestream2_get_bytes_left(&s->g) < esize) 155 return AVERROR_INVALIDDATA; 156 } else { 157 etype = -1; 158 esize = 0; 159 } 160 161 avctx->pix_fmt = AV_PIX_FMT_PAL8; 162 163 if (av_image_check_size(s->width, s->height, 0, avctx) < 0) 164 return -1; 165 if (s->width != avctx->width || s->height != avctx->height) { 166 ret = ff_set_dimensions(avctx, s->width, s->height); 167 if (ret < 0) 168 return ret; 169 } 170 171 if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) 172 return ret; 173 memset(frame->data[0], 0, s->height * frame->linesize[0]); 174 frame->pict_type = AV_PICTURE_TYPE_I; 175 frame->palette_has_changed = 1; 176 177 pos_after_pal = bytestream2_tell(&s->g) + esize; 178 palette = (uint32_t*)frame->data[1]; 179 if (etype == 1 && esize > 1 && bytestream2_peek_byte(&s->g) < 6) { 180 int idx = bytestream2_get_byte(&s->g); 181 npal = 4; 182 for (i = 0; i < npal; i++) 183 palette[i] = ff_cga_palette[ cga_mode45_index[idx][i] ]; 184 } else if (etype == 2) { 185 npal = FFMIN(esize, 16); 186 for (i = 0; i < npal; i++) { 187 int pal_idx = bytestream2_get_byte(&s->g); 188 palette[i] = ff_cga_palette[FFMIN(pal_idx, 15)]; 189 } 190 } else if (etype == 3) { 191 npal = FFMIN(esize, 16); 192 for (i = 0; i < npal; i++) { 193 int pal_idx = bytestream2_get_byte(&s->g); 194 palette[i] = ff_ega_palette[FFMIN(pal_idx, 63)]; 195 } 196 } else if (etype == 4 || etype == 5) { 197 npal = FFMIN(esize / 3, 256); 198 for (i = 0; i < npal; i++) { 199 palette[i] = bytestream2_get_be24(&s->g) << 2; 200 palette[i] |= 0xFFU << 24 | palette[i] >> 6 & 0x30303; 201 } 202 } else { 203 if (bpp == 1) { 204 npal = 2; 205 palette[0] = 0xFF000000; 206 palette[1] = 0xFFFFFFFF; 207 } else if (bpp == 2) { 208 npal = 4; 209 for (i = 0; i < npal; i++) 210 palette[i] = ff_cga_palette[ cga_mode45_index[0][i] ]; 211 } else { 212 npal = 16; 213 memcpy(palette, ff_cga_palette, npal * 4); 214 } 215 } 216 // fill remaining palette entries 217 memset(palette + npal, 0, AVPALETTE_SIZE - npal * 4); 218 // skip remaining palette bytes 219 bytestream2_seek(&s->g, pos_after_pal, SEEK_SET); 220 221 val = 0; 222 y = s->height - 1; 223 if (bytestream2_get_le16(&s->g)) { 224 x = 0; 225 plane = 0; 226 while (bytestream2_get_bytes_left(&s->g) >= 6) { 227 int stop_size, marker, t1, t2; 228 229 t1 = bytestream2_get_bytes_left(&s->g); 230 t2 = bytestream2_get_le16(&s->g); 231 stop_size = t1 - FFMIN(t1, t2); 232 // ignore uncompressed block size 233 bytestream2_skip(&s->g, 2); 234 marker = bytestream2_get_byte(&s->g); 235 236 while (plane < s->nb_planes && 237 bytestream2_get_bytes_left(&s->g) > stop_size) { 238 int run = 1; 239 val = bytestream2_get_byte(&s->g); 240 if (val == marker) { 241 run = bytestream2_get_byte(&s->g); 242 if (run == 0) 243 run = bytestream2_get_le16(&s->g); 244 val = bytestream2_get_byte(&s->g); 245 } 246 247 if (bits_per_plane == 8) { 248 picmemset_8bpp(s, frame, val, run, &x, &y); 249 if (y < 0) 250 goto finish; 251 } else { 252 picmemset(s, frame, val, run, &x, &y, &plane, bits_per_plane); 253 } 254 } 255 } 256 257 if (s->nb_planes - plane > 1) 258 return AVERROR_INVALIDDATA; 259 260 if (plane < s->nb_planes && x < avctx->width) { 261 int run = (y + 1) * avctx->width - x; 262 if (bits_per_plane == 8) 263 picmemset_8bpp(s, frame, val, run, &x, &y); 264 else 265 picmemset(s, frame, val, run / (8 / bits_per_plane), &x, &y, &plane, bits_per_plane); 266 } 267 } else { 268 while (y >= 0 && bytestream2_get_bytes_left(&s->g) > 0) { 269 memcpy(frame->data[0] + y * frame->linesize[0], s->g.buffer, FFMIN(avctx->width, bytestream2_get_bytes_left(&s->g))); 270 bytestream2_skip(&s->g, avctx->width); 271 y--; 272 } 273 } 274finish: 275 276 *got_frame = 1; 277 return avpkt->size; 278} 279 280const FFCodec ff_pictor_decoder = { 281 .p.name = "pictor", 282 .p.long_name = NULL_IF_CONFIG_SMALL("Pictor/PC Paint"), 283 .p.type = AVMEDIA_TYPE_VIDEO, 284 .p.id = AV_CODEC_ID_PICTOR, 285 .p.capabilities = AV_CODEC_CAP_DR1, 286 .priv_data_size = sizeof(PicContext), 287 FF_CODEC_DECODE_CB(decode_frame), 288}; 289