1/* 2 * PC Paintbrush PCX (.pcx) image decoder 3 * Copyright (c) 2007, 2008 Ivo van Poorten 4 * 5 * This decoder does not support CGA palettes. I am unable to find samples 6 * and Netpbm cannot generate them. 7 * 8 * This file is part of FFmpeg. 9 * 10 * FFmpeg is free software; you can redistribute it and/or 11 * modify it under the terms of the GNU Lesser General Public 12 * License as published by the Free Software Foundation; either 13 * version 2.1 of the License, or (at your option) any later version. 14 * 15 * FFmpeg is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 * Lesser General Public License for more details. 19 * 20 * You should have received a copy of the GNU Lesser General Public 21 * License along with FFmpeg; if not, write to the Free Software 22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 23 */ 24 25#include "avcodec.h" 26#include "bytestream.h" 27#include "get_bits.h" 28 29typedef struct PCXContext { 30 AVFrame picture; 31} PCXContext; 32 33static av_cold int pcx_init(AVCodecContext *avctx) { 34 PCXContext *s = avctx->priv_data; 35 36 avcodec_get_frame_defaults(&s->picture); 37 avctx->coded_frame= &s->picture; 38 39 return 0; 40} 41 42/** 43 * @return advanced src pointer 44 */ 45static const uint8_t *pcx_rle_decode(const uint8_t *src, uint8_t *dst, 46 unsigned int bytes_per_scanline, int compressed) { 47 unsigned int i = 0; 48 unsigned char run, value; 49 50 if (compressed) { 51 while (i<bytes_per_scanline) { 52 run = 1; 53 value = *src++; 54 if (value >= 0xc0) { 55 run = value & 0x3f; 56 value = *src++; 57 } 58 while (i<bytes_per_scanline && run--) 59 dst[i++] = value; 60 } 61 } else { 62 memcpy(dst, src, bytes_per_scanline); 63 src += bytes_per_scanline; 64 } 65 66 return src; 67} 68 69static void pcx_palette(const uint8_t **src, uint32_t *dst, unsigned int pallen) { 70 unsigned int i; 71 72 for (i=0; i<pallen; i++) 73 *dst++ = bytestream_get_be24(src); 74 if (pallen < 256) 75 memset(dst, 0, (256 - pallen) * sizeof(*dst)); 76} 77 78static int pcx_decode_frame(AVCodecContext *avctx, void *data, int *data_size, 79 AVPacket *avpkt) { 80 const uint8_t *buf = avpkt->data; 81 int buf_size = avpkt->size; 82 PCXContext * const s = avctx->priv_data; 83 AVFrame *picture = data; 84 AVFrame * const p = &s->picture; 85 int compressed, xmin, ymin, xmax, ymax; 86 unsigned int w, h, bits_per_pixel, bytes_per_line, nplanes, stride, y, x, 87 bytes_per_scanline; 88 uint8_t *ptr; 89 uint8_t const *bufstart = buf; 90 91 if (buf[0] != 0x0a || buf[1] > 5) { 92 av_log(avctx, AV_LOG_ERROR, "this is not PCX encoded data\n"); 93 return -1; 94 } 95 96 compressed = buf[2]; 97 xmin = AV_RL16(buf+ 4); 98 ymin = AV_RL16(buf+ 6); 99 xmax = AV_RL16(buf+ 8); 100 ymax = AV_RL16(buf+10); 101 102 if (xmax < xmin || ymax < ymin) { 103 av_log(avctx, AV_LOG_ERROR, "invalid image dimensions\n"); 104 return -1; 105 } 106 107 w = xmax - xmin + 1; 108 h = ymax - ymin + 1; 109 110 bits_per_pixel = buf[3]; 111 bytes_per_line = AV_RL16(buf+66); 112 nplanes = buf[65]; 113 bytes_per_scanline = nplanes * bytes_per_line; 114 115 if (bytes_per_scanline < w * bits_per_pixel * nplanes / 8) { 116 av_log(avctx, AV_LOG_ERROR, "PCX data is corrupted\n"); 117 return -1; 118 } 119 120 switch ((nplanes<<8) + bits_per_pixel) { 121 case 0x0308: 122 avctx->pix_fmt = PIX_FMT_RGB24; 123 break; 124 case 0x0108: 125 case 0x0104: 126 case 0x0102: 127 case 0x0101: 128 case 0x0401: 129 case 0x0301: 130 case 0x0201: 131 avctx->pix_fmt = PIX_FMT_PAL8; 132 break; 133 default: 134 av_log(avctx, AV_LOG_ERROR, "invalid PCX file\n"); 135 return -1; 136 } 137 138 buf += 128; 139 140 if (p->data[0]) 141 avctx->release_buffer(avctx, p); 142 143 if (avcodec_check_dimensions(avctx, w, h)) 144 return -1; 145 if (w != avctx->width || h != avctx->height) 146 avcodec_set_dimensions(avctx, w, h); 147 if (avctx->get_buffer(avctx, p) < 0) { 148 av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); 149 return -1; 150 } 151 152 p->pict_type = FF_I_TYPE; 153 154 ptr = p->data[0]; 155 stride = p->linesize[0]; 156 157 if (nplanes == 3 && bits_per_pixel == 8) { 158 uint8_t scanline[bytes_per_scanline]; 159 160 for (y=0; y<h; y++) { 161 buf = pcx_rle_decode(buf, scanline, bytes_per_scanline, compressed); 162 163 for (x=0; x<w; x++) { 164 ptr[3*x ] = scanline[x ]; 165 ptr[3*x+1] = scanline[x+ bytes_per_line ]; 166 ptr[3*x+2] = scanline[x+(bytes_per_line<<1)]; 167 } 168 169 ptr += stride; 170 } 171 172 } else if (nplanes == 1 && bits_per_pixel == 8) { 173 uint8_t scanline[bytes_per_scanline]; 174 const uint8_t *palstart = bufstart + buf_size - 769; 175 176 for (y=0; y<h; y++, ptr+=stride) { 177 buf = pcx_rle_decode(buf, scanline, bytes_per_scanline, compressed); 178 memcpy(ptr, scanline, w); 179 } 180 181 if (buf != palstart) { 182 av_log(avctx, AV_LOG_WARNING, "image data possibly corrupted\n"); 183 buf = palstart; 184 } 185 if (*buf++ != 12) { 186 av_log(avctx, AV_LOG_ERROR, "expected palette after image data\n"); 187 return -1; 188 } 189 190 } else if (nplanes == 1) { /* all packed formats, max. 16 colors */ 191 uint8_t scanline[bytes_per_scanline]; 192 GetBitContext s; 193 194 for (y=0; y<h; y++) { 195 init_get_bits(&s, scanline, bytes_per_scanline<<3); 196 197 buf = pcx_rle_decode(buf, scanline, bytes_per_scanline, compressed); 198 199 for (x=0; x<w; x++) 200 ptr[x] = get_bits(&s, bits_per_pixel); 201 ptr += stride; 202 } 203 204 } else { /* planar, 4, 8 or 16 colors */ 205 uint8_t scanline[bytes_per_scanline]; 206 int i; 207 208 for (y=0; y<h; y++) { 209 buf = pcx_rle_decode(buf, scanline, bytes_per_scanline, compressed); 210 211 for (x=0; x<w; x++) { 212 int m = 0x80 >> (x&7), v = 0; 213 for (i=nplanes - 1; i>=0; i--) { 214 v <<= 1; 215 v += !!(scanline[i*bytes_per_line + (x>>3)] & m); 216 } 217 ptr[x] = v; 218 } 219 ptr += stride; 220 } 221 } 222 223 if (nplanes == 1 && bits_per_pixel == 8) { 224 pcx_palette(&buf, (uint32_t *) p->data[1], 256); 225 } else if (bits_per_pixel < 8) { 226 const uint8_t *palette = bufstart+16; 227 pcx_palette(&palette, (uint32_t *) p->data[1], 16); 228 } 229 230 *picture = s->picture; 231 *data_size = sizeof(AVFrame); 232 233 return buf - bufstart; 234} 235 236static av_cold int pcx_end(AVCodecContext *avctx) { 237 PCXContext *s = avctx->priv_data; 238 239 if(s->picture.data[0]) 240 avctx->release_buffer(avctx, &s->picture); 241 242 return 0; 243} 244 245AVCodec pcx_decoder = { 246 "pcx", 247 AVMEDIA_TYPE_VIDEO, 248 CODEC_ID_PCX, 249 sizeof(PCXContext), 250 pcx_init, 251 NULL, 252 pcx_end, 253 pcx_decode_frame, 254 CODEC_CAP_DR1, 255 NULL, 256 .long_name = NULL_IF_CONFIG_SMALL("PC Paintbrush PCX image"), 257}; 258