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 "libavutil/imgutils.h" 26#include "avcodec.h" 27#include "bytestream.h" 28#include "get_bits.h" 29#include "internal.h" 30 31static void pcx_rle_decode(GetByteContext *gb, 32 uint8_t *dst, 33 unsigned int bytes_per_scanline, 34 int compressed) 35{ 36 unsigned int i = 0; 37 unsigned char run, value; 38 39 if (compressed) { 40 while (i < bytes_per_scanline && bytestream2_get_bytes_left(gb)>0) { 41 run = 1; 42 value = bytestream2_get_byte(gb); 43 if (value >= 0xc0 && bytestream2_get_bytes_left(gb)>0) { 44 run = value & 0x3f; 45 value = bytestream2_get_byte(gb); 46 } 47 while (i < bytes_per_scanline && run--) 48 dst[i++] = value; 49 } 50 } else { 51 bytestream2_get_buffer(gb, dst, bytes_per_scanline); 52 } 53} 54 55static void pcx_palette(GetByteContext *gb, uint32_t *dst, int pallen) 56{ 57 int i; 58 59 pallen = FFMIN(pallen, bytestream2_get_bytes_left(gb) / 3); 60 for (i = 0; i < pallen; i++) 61 *dst++ = 0xFF000000 | bytestream2_get_be24u(gb); 62 if (pallen < 256) 63 memset(dst, 0, (256 - pallen) * sizeof(*dst)); 64} 65 66static int pcx_decode_frame(AVCodecContext *avctx, void *data, int *got_frame, 67 AVPacket *avpkt) 68{ 69 GetByteContext gb; 70 AVFrame * const p = data; 71 int compressed, xmin, ymin, xmax, ymax; 72 int ret; 73 unsigned int w, h, bits_per_pixel, bytes_per_line, nplanes, stride, y, x, 74 bytes_per_scanline; 75 uint8_t *ptr, *scanline; 76 77 if (avpkt->size < 128) 78 return AVERROR_INVALIDDATA; 79 80 bytestream2_init(&gb, avpkt->data, avpkt->size); 81 82 if (bytestream2_get_byteu(&gb) != 0x0a || bytestream2_get_byteu(&gb) > 5) { 83 av_log(avctx, AV_LOG_ERROR, "this is not PCX encoded data\n"); 84 return AVERROR_INVALIDDATA; 85 } 86 87 compressed = bytestream2_get_byteu(&gb); 88 bits_per_pixel = bytestream2_get_byteu(&gb); 89 xmin = bytestream2_get_le16u(&gb); 90 ymin = bytestream2_get_le16u(&gb); 91 xmax = bytestream2_get_le16u(&gb); 92 ymax = bytestream2_get_le16u(&gb); 93 avctx->sample_aspect_ratio.num = bytestream2_get_le16u(&gb); 94 avctx->sample_aspect_ratio.den = bytestream2_get_le16u(&gb); 95 96 if (xmax < xmin || ymax < ymin) { 97 av_log(avctx, AV_LOG_ERROR, "invalid image dimensions\n"); 98 return AVERROR_INVALIDDATA; 99 } 100 101 w = xmax - xmin + 1; 102 h = ymax - ymin + 1; 103 104 bytestream2_skipu(&gb, 49); 105 nplanes = bytestream2_get_byteu(&gb); 106 bytes_per_line = bytestream2_get_le16u(&gb); 107 bytes_per_scanline = nplanes * bytes_per_line; 108 109 if (bytes_per_scanline < (w * bits_per_pixel * nplanes + 7) / 8 || 110 (!compressed && bytes_per_scanline > bytestream2_get_bytes_left(&gb) / h)) { 111 av_log(avctx, AV_LOG_ERROR, "PCX data is corrupted\n"); 112 return AVERROR_INVALIDDATA; 113 } 114 115 switch ((nplanes << 8) + bits_per_pixel) { 116 case 0x0308: 117 avctx->pix_fmt = AV_PIX_FMT_RGB24; 118 break; 119 case 0x0108: 120 case 0x0104: 121 case 0x0102: 122 case 0x0101: 123 case 0x0401: 124 case 0x0301: 125 case 0x0201: 126 avctx->pix_fmt = AV_PIX_FMT_PAL8; 127 break; 128 default: 129 av_log(avctx, AV_LOG_ERROR, "invalid PCX file\n"); 130 return AVERROR_INVALIDDATA; 131 } 132 133 bytestream2_skipu(&gb, 60); 134 135 if ((ret = ff_set_dimensions(avctx, w, h)) < 0) 136 return ret; 137 138 if ((ret = ff_get_buffer(avctx, p, 0)) < 0) 139 return ret; 140 141 p->pict_type = AV_PICTURE_TYPE_I; 142 143 ptr = p->data[0]; 144 stride = p->linesize[0]; 145 146 scanline = av_malloc(bytes_per_scanline + FF_INPUT_BUFFER_PADDING_SIZE); 147 if (!scanline) 148 return AVERROR(ENOMEM); 149 150 if (nplanes == 3 && bits_per_pixel == 8) { 151 for (y = 0; y < h; y++) { 152 pcx_rle_decode(&gb, scanline, bytes_per_scanline, compressed); 153 154 for (x = 0; x < w; x++) { 155 ptr[3 * x] = scanline[x]; 156 ptr[3 * x + 1] = scanline[x + bytes_per_line]; 157 ptr[3 * x + 2] = scanline[x + (bytes_per_line << 1)]; 158 } 159 160 ptr += stride; 161 } 162 } else if (nplanes == 1 && bits_per_pixel == 8) { 163 int palstart = avpkt->size - 769; 164 165 if (avpkt->size < 769) { 166 av_log(avctx, AV_LOG_ERROR, "File is too short\n"); 167 ret = avctx->err_recognition & AV_EF_EXPLODE ? 168 AVERROR_INVALIDDATA : avpkt->size; 169 goto end; 170 } 171 172 for (y = 0; y < h; y++, ptr += stride) { 173 pcx_rle_decode(&gb, scanline, bytes_per_scanline, compressed); 174 memcpy(ptr, scanline, w); 175 } 176 177 if (bytestream2_tell(&gb) != palstart) { 178 av_log(avctx, AV_LOG_WARNING, "image data possibly corrupted\n"); 179 bytestream2_seek(&gb, palstart, SEEK_SET); 180 } 181 if (bytestream2_get_byte(&gb) != 12) { 182 av_log(avctx, AV_LOG_ERROR, "expected palette after image data\n"); 183 ret = avctx->err_recognition & AV_EF_EXPLODE ? 184 AVERROR_INVALIDDATA : avpkt->size; 185 goto end; 186 } 187 } else if (nplanes == 1) { /* all packed formats, max. 16 colors */ 188 GetBitContext s; 189 190 for (y = 0; y < h; y++) { 191 init_get_bits8(&s, scanline, bytes_per_scanline); 192 193 pcx_rle_decode(&gb, scanline, bytes_per_scanline, compressed); 194 195 for (x = 0; x < w; x++) 196 ptr[x] = get_bits(&s, bits_per_pixel); 197 ptr += stride; 198 } 199 } else { /* planar, 4, 8 or 16 colors */ 200 int i; 201 202 for (y = 0; y < h; y++) { 203 pcx_rle_decode(&gb, scanline, bytes_per_scanline, compressed); 204 205 for (x = 0; x < w; x++) { 206 int m = 0x80 >> (x & 7), v = 0; 207 for (i = nplanes - 1; i >= 0; i--) { 208 v <<= 1; 209 v += !!(scanline[i * bytes_per_line + (x >> 3)] & m); 210 } 211 ptr[x] = v; 212 } 213 ptr += stride; 214 } 215 } 216 217 ret = bytestream2_tell(&gb); 218 if (nplanes == 1 && bits_per_pixel == 8) { 219 pcx_palette(&gb, (uint32_t *)p->data[1], 256); 220 ret += 256 * 3; 221 } else if (bits_per_pixel * nplanes == 1) { 222 AV_WN32A(p->data[1] , 0xFF000000); 223 AV_WN32A(p->data[1]+4, 0xFFFFFFFF); 224 } else if (bits_per_pixel < 8) { 225 bytestream2_seek(&gb, 16, SEEK_SET); 226 pcx_palette(&gb, (uint32_t *)p->data[1], 16); 227 } 228 229 *got_frame = 1; 230 231end: 232 av_free(scanline); 233 return ret; 234} 235 236AVCodec ff_pcx_decoder = { 237 .name = "pcx", 238 .long_name = NULL_IF_CONFIG_SMALL("PC Paintbrush PCX image"), 239 .type = AVMEDIA_TYPE_VIDEO, 240 .id = AV_CODEC_ID_PCX, 241 .decode = pcx_decode_frame, 242 .capabilities = CODEC_CAP_DR1, 243}; 244