diff options
Diffstat (limited to 'libavcodec/psd.c')
-rw-r--r-- | libavcodec/psd.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/libavcodec/psd.c b/libavcodec/psd.c new file mode 100644 index 0000000000..43814471da --- /dev/null +++ b/libavcodec/psd.c @@ -0,0 +1,474 @@ +/* + * Photoshop (PSD) image decoder + * Copyright (c) 2016 Jokyo Images + * + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "bytestream.h" +#include "internal.h" + +enum PsdCompr { + PSD_RAW, + PSD_RLE, + PSD_ZIP_WITHOUT_P, + PSD_ZIP_WITH_P, +}; + +enum PsdColorMode { + PSD_BITMAP, + PSD_GRAYSCALE, + PSD_INDEXED, + PSD_RGB, + PSD_CMYK, + PSD_MULTICHANNEL, + PSD_DUOTONE, + PSD_LAB, +}; + +typedef struct PSDContext { + AVClass *class; + AVFrame *picture; + AVCodecContext *avctx; + GetByteContext gb; + + uint8_t * tmp; + + uint16_t channel_count; + uint16_t channel_depth; + + uint64_t uncompressed_size; + unsigned int pixel_size;/* 1 for 8 bits, 2 for 16 bits */ + uint64_t line_size;/* length of src data (even width) */ + + int width; + int height; + + enum PsdCompr compression; + enum PsdColorMode color_mode; + + uint8_t palette[AVPALETTE_SIZE]; +} PSDContext; + +static int decode_header(PSDContext * s) +{ + int signature, version, color_mode; + int64_t len_section; + int ret = 0; + + if (bytestream2_get_bytes_left(&s->gb) < 30) {/* File header section + color map data section length */ + av_log(s->avctx, AV_LOG_ERROR, "Header too short to parse.\n"); + return AVERROR_INVALIDDATA; + } + + signature = bytestream2_get_le32(&s->gb); + if (signature != MKTAG('8','B','P','S')) { + av_log(s->avctx, AV_LOG_ERROR, "Wrong signature %d.\n", signature); + return AVERROR_INVALIDDATA; + } + + version = bytestream2_get_be16(&s->gb); + if (version != 1) { + av_log(s->avctx, AV_LOG_ERROR, "Wrong version %d.\n", version); + return AVERROR_INVALIDDATA; + } + + bytestream2_skip(&s->gb, 6);/* reserved */ + + s->channel_count = bytestream2_get_be16(&s->gb); + if ((s->channel_count < 1) || (s->channel_count > 56)) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid channel count %d.\n", s->channel_count); + return AVERROR_INVALIDDATA; + } + + s->height = bytestream2_get_be32(&s->gb); + + if ((s->height > 30000) && (s->avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL)) { + av_log(s->avctx, AV_LOG_ERROR, + "Height > 30000 is experimental, add " + "'-strict %d' if you want to try to decode the picture.\n", + FF_COMPLIANCE_EXPERIMENTAL); + return AVERROR_EXPERIMENTAL; + } + + s->width = bytestream2_get_be32(&s->gb); + if ((s->width > 30000) && (s->avctx->strict_std_compliance > FF_COMPLIANCE_EXPERIMENTAL)) { + av_log(s->avctx, AV_LOG_ERROR, + "Width > 30000 is experimental, add " + "'-strict %d' if you want to try to decode the picture.\n", + FF_COMPLIANCE_EXPERIMENTAL); + return AVERROR_EXPERIMENTAL; + } + + if ((ret = ff_set_dimensions(s->avctx, s->width, s->height)) < 0) + return ret; + + s->channel_depth = bytestream2_get_be16(&s->gb); + + color_mode = bytestream2_get_be16(&s->gb); + switch (color_mode) { + case 0: + s->color_mode = PSD_BITMAP; + break; + case 1: + s->color_mode = PSD_GRAYSCALE; + break; + case 2: + s->color_mode = PSD_INDEXED; + break; + case 3: + s->color_mode = PSD_RGB; + break; + case 4: + s->color_mode = PSD_CMYK; + break; + case 7: + s->color_mode = PSD_MULTICHANNEL; + break; + case 8: + s->color_mode = PSD_DUOTONE; + break; + case 9: + s->color_mode = PSD_LAB; + break; + default: + av_log(s->avctx, AV_LOG_ERROR, "Unknown color mode %d.\n", color_mode); + return AVERROR_INVALIDDATA; + } + + /* color map data */ + len_section = bytestream2_get_be32(&s->gb); + if (len_section < 0) { + av_log(s->avctx, AV_LOG_ERROR, "Negative size for color map data section.\n"); + return AVERROR_INVALIDDATA; + } + + if (bytestream2_get_bytes_left(&s->gb) < (len_section + 4)) { /* section and len next section */ + av_log(s->avctx, AV_LOG_ERROR, "Incomplete file.\n"); + return AVERROR_INVALIDDATA; + } + if (len_section) { + int i,j; + memset(s->palette, 0xff, AVPALETTE_SIZE); + for (j = HAVE_BIGENDIAN; j < 3 + HAVE_BIGENDIAN; j++) + for (i = 0; i < FFMIN(256, len_section / 3); i++) + s->palette[i * 4 + (HAVE_BIGENDIAN ? j : 2 - j)] = bytestream2_get_byteu(&s->gb); + len_section -= i * 3; + } + bytestream2_skip(&s->gb, len_section); + + /* image ressources */ + len_section = bytestream2_get_be32(&s->gb); + if (len_section < 0) { + av_log(s->avctx, AV_LOG_ERROR, "Negative size for image ressources section.\n"); + return AVERROR_INVALIDDATA; + } + + if (bytestream2_get_bytes_left(&s->gb) < (len_section + 4)) { /* section and len next section */ + av_log(s->avctx, AV_LOG_ERROR, "Incomplete file.\n"); + return AVERROR_INVALIDDATA; + } + bytestream2_skip(&s->gb, len_section); + + /* layers and masks */ + len_section = bytestream2_get_be32(&s->gb); + if (len_section < 0) { + av_log(s->avctx, AV_LOG_ERROR, "Negative size for layers and masks data section.\n"); + return AVERROR_INVALIDDATA; + } + + if (bytestream2_get_bytes_left(&s->gb) < len_section) { + av_log(s->avctx, AV_LOG_ERROR, "Incomplete file.\n"); + return AVERROR_INVALIDDATA; + } + bytestream2_skip(&s->gb, len_section); + + /* image section */ + if (bytestream2_get_bytes_left(&s->gb) < 2) { + av_log(s->avctx, AV_LOG_ERROR, "File without image data section.\n"); + return AVERROR_INVALIDDATA; + } + + s->compression = bytestream2_get_be16(&s->gb); + switch (s->compression) { + case 0: + case 1: + break; + case 2: + avpriv_request_sample(s->avctx, "ZIP without predictor compression"); + return AVERROR_PATCHWELCOME; + break; + case 3: + avpriv_request_sample(s->avctx, "ZIP with predictor compression"); + return AVERROR_PATCHWELCOME; + break; + default: + av_log(s->avctx, AV_LOG_ERROR, "Unknown compression %d.\n", s->compression); + return AVERROR_INVALIDDATA; + } + + return ret; +} + +static int decode_rle(PSDContext * s){ + unsigned int scanline_count; + unsigned int sl, count; + unsigned long target_index = 0; + unsigned int p; + int8_t rle_char; + unsigned int repeat_count; + uint8_t v; + + scanline_count = s->height * s->channel_count; + + /* scanline table */ + if (bytestream2_get_bytes_left(&s->gb) < scanline_count * 2) { + av_log(s->avctx, AV_LOG_ERROR, "Not enough data for rle scanline table.\n"); + return AVERROR_INVALIDDATA; + } + bytestream2_skip(&s->gb, scanline_count * 2);/* size of each scanline */ + + /* decode rle data scanline by scanline */ + for (sl = 0; sl < scanline_count; sl++) { + count = 0; + + while (count < s->line_size) { + rle_char = bytestream2_get_byte(&s->gb); + + if (rle_char <= 0) {/* byte repeat */ + repeat_count = rle_char * -1; + + if (bytestream2_get_bytes_left(&s->gb) < 1) { + av_log(s->avctx, AV_LOG_ERROR, "Not enough data for rle scanline.\n"); + return AVERROR_INVALIDDATA; + } + + if (target_index + repeat_count >= s->uncompressed_size) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid rle char.\n"); + return AVERROR_INVALIDDATA; + } + + v = bytestream2_get_byte(&s->gb); + for (p = 0; p <= repeat_count; p++) { + s->tmp[target_index++] = v; + } + count += repeat_count + 1; + } else { + if (bytestream2_get_bytes_left(&s->gb) < rle_char) { + av_log(s->avctx, AV_LOG_ERROR, "Not enough data for rle scanline.\n"); + return AVERROR_INVALIDDATA; + } + + if (target_index + rle_char >= s->uncompressed_size) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid rle char.\n"); + return AVERROR_INVALIDDATA; + } + + for (p = 0; p <= rle_char; p++) { + v = bytestream2_get_byte(&s->gb); + s->tmp[target_index++] = v; + } + count += rle_char + 1; + } + } + } + + return 0; +} + +static int decode_frame(AVCodecContext *avctx, void *data, + int *got_frame, AVPacket *avpkt) +{ + int ret; + uint8_t *ptr; + const uint8_t *ptr_data; + int index_out, c, y, x, p; + uint8_t eq_channel[4] = {2,0,1,3};/* RGBA -> GBRA channel order */ + uint8_t plane_number; + + AVFrame *picture = data; + + PSDContext *s = avctx->priv_data; + s->avctx = avctx; + s->channel_count = 0; + s->channel_depth = 0; + s->tmp = NULL; + s->line_size = 0; + + bytestream2_init(&s->gb, avpkt->data, avpkt->size); + + if ((ret = decode_header(s)) < 0) + return ret; + + s->pixel_size = s->channel_depth >> 3;/* in byte */ + s->line_size = s->width * s->pixel_size; + + switch (s->color_mode) { + case PSD_BITMAP: + if (s->channel_depth != 1 || s->channel_count != 1) { + av_log(s->avctx, AV_LOG_ERROR, + "Invalid bitmap file (channel_depth %d, channel_count %d)\n", + s->channel_depth, s->channel_count); + return AVERROR_INVALIDDATA; + } + s->line_size = s->width + 7 >> 3; + avctx->pix_fmt = AV_PIX_FMT_MONOWHITE; + break; + case PSD_INDEXED: + if (s->channel_depth != 8 || s->channel_count != 1) { + av_log(s->avctx, AV_LOG_ERROR, + "Invalid indexed file (channel_depth %d, channel_count %d)\n", + s->channel_depth, s->channel_count); + return AVERROR_INVALIDDATA; + } + avctx->pix_fmt = AV_PIX_FMT_PAL8; + break; + case PSD_RGB: + if (s->channel_count == 3) { + if (s->channel_depth == 8) { + avctx->pix_fmt = AV_PIX_FMT_GBRP; + } else if (s->channel_depth == 16) { + avctx->pix_fmt = AV_PIX_FMT_GBRP16BE; + } else { + avpriv_report_missing_feature(avctx, "channel depth %d for rgb", s->channel_depth); + return AVERROR_PATCHWELCOME; + } + } else if (s->channel_count == 4) { + if (s->channel_depth == 8) { + avctx->pix_fmt = AV_PIX_FMT_GBRAP; + } else if (s->channel_depth == 16) { + avctx->pix_fmt = AV_PIX_FMT_GBRAP16BE; + } else { + avpriv_report_missing_feature(avctx, "channel depth %d for rgb", s->channel_depth); + return AVERROR_PATCHWELCOME; + } + } else { + avpriv_report_missing_feature(avctx, "channel count %d for rgb", s->channel_count); + return AVERROR_PATCHWELCOME; + } + break; + case PSD_DUOTONE: + av_log(avctx, AV_LOG_WARNING, "ignoring unknown duotone specification.\n"); + case PSD_GRAYSCALE: + if (s->channel_count == 1) { + if (s->channel_depth == 8) { + avctx->pix_fmt = AV_PIX_FMT_GRAY8; + } else if (s->channel_depth == 16) { + avctx->pix_fmt = AV_PIX_FMT_GRAY16BE; + } else if (s->channel_depth == 32) { + avctx->pix_fmt = AV_PIX_FMT_GRAYF32BE; + } else { + avpriv_report_missing_feature(avctx, "channel depth %d for grayscale", s->channel_depth); + return AVERROR_PATCHWELCOME; + } + } else if (s->channel_count == 2) { + if (s->channel_depth == 8) { + avctx->pix_fmt = AV_PIX_FMT_YA8; + } else if (s->channel_depth == 16) { + avctx->pix_fmt = AV_PIX_FMT_YA16BE; + } else { + avpriv_report_missing_feature(avctx, "channel depth %d for grayscale", s->channel_depth); + return AVERROR_PATCHWELCOME; + } + } else { + avpriv_report_missing_feature(avctx, "channel count %d for grayscale", s->channel_count); + return AVERROR_PATCHWELCOME; + } + break; + default: + avpriv_report_missing_feature(avctx, "color mode %d", s->color_mode); + return AVERROR_PATCHWELCOME; + } + + s->uncompressed_size = s->line_size * s->height * s->channel_count; + + if ((ret = ff_get_buffer(avctx, picture, 0)) < 0) + return ret; + + /* decode picture if need */ + if (s->compression == PSD_RLE) { + s->tmp = av_malloc(s->uncompressed_size); + if (!s->tmp) + return AVERROR(ENOMEM); + + ret = decode_rle(s); + + if (ret < 0) { + av_freep(&s->tmp); + return ret; + } + + ptr_data = s->tmp; + } else { + if (bytestream2_get_bytes_left(&s->gb) < s->uncompressed_size) { + av_log(s->avctx, AV_LOG_ERROR, "Not enough data for raw image data section.\n"); + return AVERROR_INVALIDDATA; + } + ptr_data = s->gb.buffer; + } + + /* Store data */ + if ((avctx->pix_fmt == AV_PIX_FMT_YA8)||(avctx->pix_fmt == AV_PIX_FMT_YA16BE)){/* Interleaved */ + ptr = picture->data[0]; + for (c = 0; c < s->channel_count; c++) { + for (y = 0; y < s->height; y++) { + for (x = 0; x < s->width; x++) { + index_out = y * picture->linesize[0] + x * s->channel_count * s->pixel_size + c * s->pixel_size; + for (p = 0; p < s->pixel_size; p++) { + ptr[index_out + p] = *ptr_data; + ptr_data ++; + } + } + } + } + } else {/* Planar */ + if (s->channel_count == 1)/* gray 8 or gray 16be */ + eq_channel[0] = 0;/* assign first channel, to first plane */ + + for (c = 0; c < s->channel_count; c++) { + plane_number = eq_channel[c]; + ptr = picture->data[plane_number];/* get the right plane */ + for (y = 0; y < s->height; y++) { + memcpy(ptr, ptr_data, s->line_size); + ptr += picture->linesize[plane_number]; + ptr_data += s->line_size; + } + } + } + + if (s->color_mode == PSD_INDEXED) { + picture->palette_has_changed = 1; + memcpy(picture->data[1], s->palette, AVPALETTE_SIZE); + } + + av_freep(&s->tmp); + + picture->pict_type = AV_PICTURE_TYPE_I; + *got_frame = 1; + + return avpkt->size; +} + +AVCodec ff_psd_decoder = { + .name = "psd", + .long_name = NULL_IF_CONFIG_SMALL("Photoshop PSD file"), + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_PSD, + .priv_data_size = sizeof(PSDContext), + .decode = decode_frame, + .capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, +}; |