yading@10: /* yading@10: * BRender PIX (.pix) image decoder yading@10: * Copyright (c) 2012 Aleksi Nurmi yading@10: * yading@10: * This file is part of FFmpeg. yading@10: * yading@10: * FFmpeg is free software; you can redistribute it and/or yading@10: * modify it under the terms of the GNU Lesser General Public yading@10: * License as published by the Free Software Foundation; either yading@10: * version 2.1 of the License, or (at your option) any later version. yading@10: * yading@10: * FFmpeg is distributed in the hope that it will be useful, yading@10: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@10: * Lesser General Public License for more details. yading@10: * yading@10: * You should have received a copy of the GNU Lesser General Public yading@10: * License along with FFmpeg; if not, write to the Free Software yading@10: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@10: */ yading@10: yading@10: /* yading@10: * Tested against samples from I-War / Independence War and Defiance. yading@10: * If the PIX file does not contain a palette, the yading@10: * palette_has_changed property of the AVFrame is set to 0. yading@10: */ yading@10: yading@10: #include "libavutil/imgutils.h" yading@10: #include "avcodec.h" yading@10: #include "bytestream.h" yading@10: #include "internal.h" yading@10: yading@10: typedef struct BRPixHeader { yading@10: int format; yading@10: unsigned int width, height; yading@10: } BRPixHeader; yading@10: yading@10: static int brpix_decode_header(BRPixHeader *out, GetByteContext *pgb) yading@10: { yading@10: unsigned int header_len = bytestream2_get_be32(pgb); yading@10: yading@10: out->format = bytestream2_get_byte(pgb); yading@10: bytestream2_skip(pgb, 2); yading@10: out->width = bytestream2_get_be16(pgb); yading@10: out->height = bytestream2_get_be16(pgb); yading@10: yading@10: // the header is at least 11 bytes long; we read the first 7 yading@10: if (header_len < 11) { yading@10: return 0; yading@10: } yading@10: yading@10: // skip the rest of the header yading@10: bytestream2_skip(pgb, header_len-7); yading@10: yading@10: return 1; yading@10: } yading@10: yading@10: static int brpix_decode_frame(AVCodecContext *avctx, yading@10: void *data, int *got_frame, yading@10: AVPacket *avpkt) yading@10: { yading@10: AVFrame *frame = data; yading@10: yading@10: int ret; yading@10: GetByteContext gb; yading@10: yading@10: unsigned int bytes_pp; yading@10: yading@10: unsigned int magic[4]; yading@10: unsigned int chunk_type; yading@10: unsigned int data_len; yading@10: BRPixHeader hdr; yading@10: yading@10: bytestream2_init(&gb, avpkt->data, avpkt->size); yading@10: yading@10: magic[0] = bytestream2_get_be32(&gb); yading@10: magic[1] = bytestream2_get_be32(&gb); yading@10: magic[2] = bytestream2_get_be32(&gb); yading@10: magic[3] = bytestream2_get_be32(&gb); yading@10: yading@10: if (magic[0] != 0x12 || yading@10: magic[1] != 0x8 || yading@10: magic[2] != 0x2 || yading@10: magic[3] != 0x2) { yading@10: av_log(avctx, AV_LOG_ERROR, "Not a BRender PIX file\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: chunk_type = bytestream2_get_be32(&gb); yading@10: if (chunk_type != 0x3 && chunk_type != 0x3d) { yading@10: av_log(avctx, AV_LOG_ERROR, "Invalid chunk type %d\n", chunk_type); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: ret = brpix_decode_header(&hdr, &gb); yading@10: if (!ret) { yading@10: av_log(avctx, AV_LOG_ERROR, "Invalid header length\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: switch (hdr.format) { yading@10: case 3: yading@10: avctx->pix_fmt = AV_PIX_FMT_PAL8; yading@10: bytes_pp = 1; yading@10: break; yading@10: case 4: yading@10: avctx->pix_fmt = AV_PIX_FMT_RGB555BE; yading@10: bytes_pp = 2; yading@10: break; yading@10: case 5: yading@10: avctx->pix_fmt = AV_PIX_FMT_RGB565BE; yading@10: bytes_pp = 2; yading@10: break; yading@10: case 6: yading@10: avctx->pix_fmt = AV_PIX_FMT_RGB24; yading@10: bytes_pp = 3; yading@10: break; yading@10: case 7: yading@10: avctx->pix_fmt = AV_PIX_FMT_0RGB; yading@10: bytes_pp = 4; yading@10: break; yading@10: case 18: yading@10: avctx->pix_fmt = AV_PIX_FMT_GRAY8A; yading@10: bytes_pp = 2; yading@10: break; yading@10: default: yading@10: av_log(avctx, AV_LOG_ERROR, "Format %d is not supported\n", yading@10: hdr.format); yading@10: return AVERROR_PATCHWELCOME; yading@10: } yading@10: yading@10: if (av_image_check_size(hdr.width, hdr.height, 0, avctx) < 0) yading@10: return AVERROR_INVALIDDATA; yading@10: yading@10: if (hdr.width != avctx->width || hdr.height != avctx->height) yading@10: avcodec_set_dimensions(avctx, hdr.width, hdr.height); yading@10: yading@10: if ((ret = ff_get_buffer(avctx, frame, 0)) < 0) yading@10: return ret; yading@10: yading@10: chunk_type = bytestream2_get_be32(&gb); yading@10: yading@10: if (avctx->pix_fmt == AV_PIX_FMT_PAL8 && yading@10: (chunk_type == 0x3 || chunk_type == 0x3d)) { yading@10: BRPixHeader palhdr; yading@10: uint32_t *pal_out = (uint32_t *)frame->data[1]; yading@10: int i; yading@10: yading@10: ret = brpix_decode_header(&palhdr, &gb); yading@10: if (!ret) { yading@10: av_log(avctx, AV_LOG_ERROR, "Invalid palette header length\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: if (palhdr.format != 7) { yading@10: av_log(avctx, AV_LOG_ERROR, "Palette is not in 0RGB format\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: chunk_type = bytestream2_get_be32(&gb); yading@10: data_len = bytestream2_get_be32(&gb); yading@10: bytestream2_skip(&gb, 8); yading@10: if (chunk_type != 0x21 || data_len != 1032 || yading@10: bytestream2_get_bytes_left(&gb) < 1032) { yading@10: av_log(avctx, AV_LOG_ERROR, "Invalid palette data\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: // convert 0RGB to machine endian format (ARGB32) yading@10: for (i = 0; i < 256; ++i) { yading@10: bytestream2_skipu(&gb, 1); yading@10: *pal_out++ = (0xFFU << 24) | bytestream2_get_be24u(&gb); yading@10: } yading@10: bytestream2_skip(&gb, 8); yading@10: yading@10: frame->palette_has_changed = 1; yading@10: yading@10: chunk_type = bytestream2_get_be32(&gb); yading@10: } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) { yading@10: uint32_t *pal_out = (uint32_t *)frame->data[1]; yading@10: int i; yading@10: yading@10: for (i = 0; i < 256; ++i) { yading@10: *pal_out++ = (0xFFU << 24) | (i * 0x010101); yading@10: } yading@10: frame->palette_has_changed = 1; yading@10: } yading@10: yading@10: data_len = bytestream2_get_be32(&gb); yading@10: bytestream2_skip(&gb, 8); yading@10: yading@10: // read the image data to the buffer yading@10: { yading@10: unsigned int bytes_per_scanline = bytes_pp * hdr.width; yading@10: unsigned int bytes_left = bytestream2_get_bytes_left(&gb); yading@10: yading@10: if (chunk_type != 0x21 || data_len != bytes_left || yading@10: bytes_left / bytes_per_scanline < hdr.height) yading@10: { yading@10: av_log(avctx, AV_LOG_ERROR, "Invalid image data\n"); yading@10: return AVERROR_INVALIDDATA; yading@10: } yading@10: yading@10: av_image_copy_plane(frame->data[0], frame->linesize[0], yading@10: avpkt->data + bytestream2_tell(&gb), yading@10: bytes_per_scanline, yading@10: bytes_per_scanline, hdr.height); yading@10: } yading@10: yading@10: *got_frame = 1; yading@10: yading@10: return avpkt->size; yading@10: } yading@10: yading@10: AVCodec ff_brender_pix_decoder = { yading@10: .name = "brender_pix", yading@10: .type = AVMEDIA_TYPE_VIDEO, yading@10: .id = AV_CODEC_ID_BRENDER_PIX, yading@10: .decode = brpix_decode_frame, yading@10: .capabilities = CODEC_CAP_DR1, yading@10: .long_name = NULL_IF_CONFIG_SMALL("BRender PIX image"), yading@10: };