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