libavcodec/pcx.c
Go to the documentation of this file.
00001 /*
00002  * PC Paintbrush PCX (.pcx) image decoder
00003  * Copyright (c) 2007, 2008 Ivo van Poorten
00004  *
00005  * This decoder does not support CGA palettes. I am unable to find samples
00006  * and Netpbm cannot generate them.
00007  *
00008  * This file is part of Libav.
00009  *
00010  * Libav is free software; you can redistribute it and/or
00011  * modify it under the terms of the GNU Lesser General Public
00012  * License as published by the Free Software Foundation; either
00013  * version 2.1 of the License, or (at your option) any later version.
00014  *
00015  * Libav is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018  * Lesser General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU Lesser General Public
00021  * License along with Libav; if not, write to the Free Software
00022  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00023  */
00024 
00025 #include "libavutil/imgutils.h"
00026 #include "avcodec.h"
00027 #include "bytestream.h"
00028 #include "get_bits.h"
00029 
00030 typedef struct PCXContext {
00031     AVFrame picture;
00032 } PCXContext;
00033 
00034 static av_cold int pcx_init(AVCodecContext *avctx) {
00035     PCXContext *s = avctx->priv_data;
00036 
00037     avcodec_get_frame_defaults(&s->picture);
00038     avctx->coded_frame= &s->picture;
00039 
00040     return 0;
00041 }
00042 
00046 static const uint8_t *pcx_rle_decode(const uint8_t *src,
00047                                      const uint8_t *end,
00048                                      uint8_t *dst,
00049                                      unsigned int bytes_per_scanline,
00050                                      int compressed) {
00051     unsigned int i = 0;
00052     unsigned char run, value;
00053 
00054     if (compressed) {
00055         while (i < bytes_per_scanline && src < end) {
00056             run = 1;
00057             value = *src++;
00058             if (value >= 0xc0 && src < end) {
00059                 run = value & 0x3f;
00060                 value = *src++;
00061             }
00062             while (i<bytes_per_scanline && run--)
00063                 dst[i++] = value;
00064         }
00065     } else {
00066         memcpy(dst, src, bytes_per_scanline);
00067         src += bytes_per_scanline;
00068     }
00069 
00070     return src;
00071 }
00072 
00073 static void pcx_palette(const uint8_t **src, uint32_t *dst, unsigned int pallen) {
00074     unsigned int i;
00075 
00076     for (i=0; i<pallen; i++)
00077         *dst++ = bytestream_get_be24(src);
00078     if (pallen < 256)
00079         memset(dst, 0, (256 - pallen) * sizeof(*dst));
00080 }
00081 
00082 static int pcx_decode_frame(AVCodecContext *avctx, void *data, int *data_size,
00083                             AVPacket *avpkt) {
00084     const uint8_t *buf = avpkt->data;
00085     int buf_size = avpkt->size;
00086     PCXContext * const s = avctx->priv_data;
00087     AVFrame *picture = data;
00088     AVFrame * const p = &s->picture;
00089     int compressed, xmin, ymin, xmax, ymax;
00090     unsigned int w, h, bits_per_pixel, bytes_per_line, nplanes, stride, y, x,
00091                  bytes_per_scanline;
00092     uint8_t *ptr;
00093     const uint8_t *buf_end = buf + buf_size;
00094     uint8_t const *bufstart = buf;
00095     uint8_t *scanline;
00096     int ret = -1;
00097 
00098     if (buf[0] != 0x0a || buf[1] > 5) {
00099         av_log(avctx, AV_LOG_ERROR, "this is not PCX encoded data\n");
00100         return -1;
00101     }
00102 
00103     compressed = buf[2];
00104     xmin = AV_RL16(buf+ 4);
00105     ymin = AV_RL16(buf+ 6);
00106     xmax = AV_RL16(buf+ 8);
00107     ymax = AV_RL16(buf+10);
00108 
00109     if (xmax < xmin || ymax < ymin) {
00110         av_log(avctx, AV_LOG_ERROR, "invalid image dimensions\n");
00111         return -1;
00112     }
00113 
00114     w = xmax - xmin + 1;
00115     h = ymax - ymin + 1;
00116 
00117     bits_per_pixel     = buf[3];
00118     bytes_per_line     = AV_RL16(buf+66);
00119     nplanes            = buf[65];
00120     bytes_per_scanline = nplanes * bytes_per_line;
00121 
00122     if (bytes_per_scanline < w * bits_per_pixel * nplanes / 8 ||
00123         (!compressed && bytes_per_scanline > buf_size / h)) {
00124         av_log(avctx, AV_LOG_ERROR, "PCX data is corrupted\n");
00125         return -1;
00126     }
00127 
00128     switch ((nplanes<<8) + bits_per_pixel) {
00129         case 0x0308:
00130             avctx->pix_fmt = PIX_FMT_RGB24;
00131             break;
00132         case 0x0108:
00133         case 0x0104:
00134         case 0x0102:
00135         case 0x0101:
00136         case 0x0401:
00137         case 0x0301:
00138         case 0x0201:
00139             avctx->pix_fmt = PIX_FMT_PAL8;
00140             break;
00141         default:
00142             av_log(avctx, AV_LOG_ERROR, "invalid PCX file\n");
00143             return -1;
00144     }
00145 
00146     buf += 128;
00147 
00148     if (p->data[0])
00149         avctx->release_buffer(avctx, p);
00150 
00151     if (av_image_check_size(w, h, 0, avctx))
00152         return -1;
00153     if (w != avctx->width || h != avctx->height)
00154         avcodec_set_dimensions(avctx, w, h);
00155     if (avctx->get_buffer(avctx, p) < 0) {
00156         av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
00157         return -1;
00158     }
00159 
00160     p->pict_type = AV_PICTURE_TYPE_I;
00161 
00162     ptr    = p->data[0];
00163     stride = p->linesize[0];
00164 
00165     scanline = av_malloc(bytes_per_scanline);
00166     if (!scanline)
00167         return AVERROR(ENOMEM);
00168 
00169     if (nplanes == 3 && bits_per_pixel == 8) {
00170         for (y=0; y<h; y++) {
00171             buf = pcx_rle_decode(buf, buf_end,
00172                                  scanline, bytes_per_scanline, compressed);
00173 
00174             for (x=0; x<w; x++) {
00175                 ptr[3*x  ] = scanline[x                    ];
00176                 ptr[3*x+1] = scanline[x+ bytes_per_line    ];
00177                 ptr[3*x+2] = scanline[x+(bytes_per_line<<1)];
00178             }
00179 
00180             ptr += stride;
00181         }
00182 
00183     } else if (nplanes == 1 && bits_per_pixel == 8) {
00184         const uint8_t *palstart = bufstart + buf_size - 769;
00185 
00186         if (buf_size < 769) {
00187             av_log(avctx, AV_LOG_ERROR, "File is too short\n");
00188             ret = buf_size;
00189             goto end;
00190         }
00191 
00192         for (y = 0; y < h; y++, ptr += stride) {
00193             buf = pcx_rle_decode(buf, buf_end,
00194                                  scanline, bytes_per_scanline, compressed);
00195             memcpy(ptr, scanline, w);
00196         }
00197 
00198         if (buf != palstart) {
00199             av_log(avctx, AV_LOG_WARNING, "image data possibly corrupted\n");
00200             buf = palstart;
00201         }
00202         if (*buf++ != 12) {
00203             av_log(avctx, AV_LOG_ERROR, "expected palette after image data\n");
00204             ret = buf_size;
00205             goto end;
00206         }
00207 
00208     } else if (nplanes == 1) {   /* all packed formats, max. 16 colors */
00209         GetBitContext s;
00210 
00211         for (y=0; y<h; y++) {
00212             init_get_bits(&s, scanline, bytes_per_scanline<<3);
00213 
00214             buf = pcx_rle_decode(buf, buf_end,
00215                                  scanline, bytes_per_scanline, compressed);
00216 
00217             for (x=0; x<w; x++)
00218                 ptr[x] = get_bits(&s, bits_per_pixel);
00219             ptr += stride;
00220         }
00221 
00222     } else {    /* planar, 4, 8 or 16 colors */
00223         int i;
00224 
00225         for (y=0; y<h; y++) {
00226             buf = pcx_rle_decode(buf, buf_end,
00227                                  scanline, bytes_per_scanline, compressed);
00228 
00229             for (x=0; x<w; x++) {
00230                 int m = 0x80 >> (x&7), v = 0;
00231                 for (i=nplanes - 1; i>=0; i--) {
00232                     v <<= 1;
00233                     v  += !!(scanline[i*bytes_per_line + (x>>3)] & m);
00234                 }
00235                 ptr[x] = v;
00236             }
00237             ptr += stride;
00238         }
00239     }
00240 
00241     if (nplanes == 1 && bits_per_pixel == 8) {
00242         pcx_palette(&buf, (uint32_t *) p->data[1], 256);
00243     } else if (bits_per_pixel < 8) {
00244         const uint8_t *palette = bufstart+16;
00245         pcx_palette(&palette, (uint32_t *) p->data[1], 16);
00246     }
00247 
00248     *picture = s->picture;
00249     *data_size = sizeof(AVFrame);
00250 
00251     ret = buf - bufstart;
00252 end:
00253     av_free(scanline);
00254     return ret;
00255 }
00256 
00257 static av_cold int pcx_end(AVCodecContext *avctx) {
00258     PCXContext *s = avctx->priv_data;
00259 
00260     if(s->picture.data[0])
00261         avctx->release_buffer(avctx, &s->picture);
00262 
00263     return 0;
00264 }
00265 
00266 AVCodec ff_pcx_decoder = {
00267     .name           = "pcx",
00268     .type           = AVMEDIA_TYPE_VIDEO,
00269     .id             = CODEC_ID_PCX,
00270     .priv_data_size = sizeof(PCXContext),
00271     .init           = pcx_init,
00272     .close          = pcx_end,
00273     .decode         = pcx_decode_frame,
00274     .capabilities   = CODEC_CAP_DR1,
00275     .long_name = NULL_IF_CONFIG_SMALL("PC Paintbrush PCX image"),
00276 };