1/* 2 * Quicktime Planar RGB (8BPS) Video Decoder 3 * Copyright (C) 2003 Roberto Togni 4 * 5 * This file is part of FFmpeg. 6 * 7 * FFmpeg is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * FFmpeg is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with FFmpeg; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 20 */ 21 22/** 23 * @file 24 * QT 8BPS Video Decoder by Roberto Togni 25 * For more information about the 8BPS format, visit: 26 * http://www.pcisys.net/~melanson/codecs/ 27 * 28 * Supports: PAL8 (RGB 8bpp, paletted) 29 * : BGR24 (RGB 24bpp) (can also output it as RGB32) 30 * : RGB32 (RGB 32bpp, 4th plane is alpha) 31 */ 32 33#include <stdio.h> 34#include <stdlib.h> 35#include <string.h> 36 37#include "libavutil/internal.h" 38#include "libavutil/intreadwrite.h" 39#include "avcodec.h" 40#include "codec_internal.h" 41#include "decode.h" 42#include "internal.h" 43 44 45static const enum AVPixelFormat pixfmt_rgb24[] = { 46 AV_PIX_FMT_BGR24, AV_PIX_FMT_0RGB32, AV_PIX_FMT_NONE }; 47 48typedef struct EightBpsContext { 49 AVCodecContext *avctx; 50 51 unsigned char planes; 52 unsigned char planemap[4]; 53 54 uint32_t pal[256]; 55} EightBpsContext; 56 57static int decode_frame(AVCodecContext *avctx, AVFrame *frame, 58 int *got_frame, AVPacket *avpkt) 59{ 60 const uint8_t *buf = avpkt->data; 61 int buf_size = avpkt->size; 62 EightBpsContext * const c = avctx->priv_data; 63 const unsigned char *encoded = buf; 64 unsigned char *pixptr, *pixptr_end; 65 unsigned int height = avctx->height; // Real image height 66 unsigned int dlen, p, row; 67 const unsigned char *lp, *dp, *ep; 68 unsigned char count; 69 unsigned int px_inc; 70 unsigned int planes = c->planes; 71 unsigned char *planemap = c->planemap; 72 int ret; 73 74 if (buf_size < planes * height *2) 75 return AVERROR_INVALIDDATA; 76 77 if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) 78 return ret; 79 80 ep = encoded + buf_size; 81 82 /* Set data pointer after line lengths */ 83 dp = encoded + planes * (height << 1); 84 85 px_inc = planes + (avctx->pix_fmt == AV_PIX_FMT_0RGB32); 86 87 for (p = 0; p < planes; p++) { 88 /* Lines length pointer for this plane */ 89 lp = encoded + p * (height << 1); 90 91 /* Decode a plane */ 92 for (row = 0; row < height; row++) { 93 pixptr = frame->data[0] + row * frame->linesize[0] + planemap[p]; 94 pixptr_end = pixptr + frame->linesize[0]; 95 if (ep - lp < row * 2 + 2) 96 return AVERROR_INVALIDDATA; 97 dlen = av_be2ne16(*(const unsigned short *)(lp + row * 2)); 98 /* Decode a row of this plane */ 99 while (dlen > 0) { 100 if (ep - dp <= 1) 101 return AVERROR_INVALIDDATA; 102 if ((count = *dp++) <= 127) { 103 count++; 104 dlen -= count + 1; 105 if (pixptr_end - pixptr < count * px_inc) 106 break; 107 if (ep - dp < count) 108 return AVERROR_INVALIDDATA; 109 while (count--) { 110 *pixptr = *dp++; 111 pixptr += px_inc; 112 } 113 } else { 114 count = 257 - count; 115 if (pixptr_end - pixptr < count * px_inc) 116 break; 117 while (count--) { 118 *pixptr = *dp; 119 pixptr += px_inc; 120 } 121 dp++; 122 dlen -= 2; 123 } 124 } 125 } 126 } 127 128 if (avctx->bits_per_coded_sample <= 8) { 129 frame->palette_has_changed = ff_copy_palette(c->pal, avpkt, avctx); 130 131 memcpy (frame->data[1], c->pal, AVPALETTE_SIZE); 132 } 133 134 *got_frame = 1; 135 136 /* always report that the buffer was completely consumed */ 137 return buf_size; 138} 139 140static av_cold int decode_init(AVCodecContext *avctx) 141{ 142 EightBpsContext * const c = avctx->priv_data; 143 144 c->avctx = avctx; 145 146 switch (avctx->bits_per_coded_sample) { 147 case 8: 148 avctx->pix_fmt = AV_PIX_FMT_PAL8; 149 c->planes = 1; 150 c->planemap[0] = 0; // 1st plane is palette indexes 151 break; 152 case 24: 153 avctx->pix_fmt = ff_get_format(avctx, pixfmt_rgb24); 154 c->planes = 3; 155 c->planemap[0] = 2; // 1st plane is red 156 c->planemap[1] = 1; // 2nd plane is green 157 c->planemap[2] = 0; // 3rd plane is blue 158 break; 159 case 32: 160 avctx->pix_fmt = AV_PIX_FMT_RGB32; 161 c->planes = 4; 162 /* handle planemap setup later for decoding rgb24 data as rbg32 */ 163 break; 164 default: 165 av_log(avctx, AV_LOG_ERROR, "Error: Unsupported color depth: %u.\n", 166 avctx->bits_per_coded_sample); 167 return AVERROR_INVALIDDATA; 168 } 169 170 if (avctx->pix_fmt == AV_PIX_FMT_RGB32) { 171 c->planemap[0] = HAVE_BIGENDIAN ? 1 : 2; // 1st plane is red 172 c->planemap[1] = HAVE_BIGENDIAN ? 2 : 1; // 2nd plane is green 173 c->planemap[2] = HAVE_BIGENDIAN ? 3 : 0; // 3rd plane is blue 174 c->planemap[3] = HAVE_BIGENDIAN ? 0 : 3; // 4th plane is alpha 175 } 176 return 0; 177} 178 179const FFCodec ff_eightbps_decoder = { 180 .p.name = "8bps", 181 .p.long_name = NULL_IF_CONFIG_SMALL("QuickTime 8BPS video"), 182 .p.type = AVMEDIA_TYPE_VIDEO, 183 .p.id = AV_CODEC_ID_8BPS, 184 .priv_data_size = sizeof(EightBpsContext), 185 .init = decode_init, 186 FF_CODEC_DECODE_CB(decode_frame), 187 .p.capabilities = AV_CODEC_CAP_DR1, 188 .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 189}; 190