LCOV - code coverage report
Current view: top level - libavcodec - pcx.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 61 127 48.0 %
Date: 2017-12-14 08:27:08 Functions: 2 3 66.7 %

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

Generated by: LCOV version 1.13