From patchwork Tue Jul 26 13:07:37 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Martin Vignali X-Patchwork-Id: 8 Delivered-To: ffmpegpatchwork@gmail.com Received: by 10.103.140.67 with SMTP id o64csp1286876vsd; Tue, 26 Jul 2016 06:07:53 -0700 (PDT) X-Received: by 10.28.20.77 with SMTP id 74mr48906920wmu.1.1469538473858; Tue, 26 Jul 2016 06:07:53 -0700 (PDT) Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id 203si1249170wmh.93.2016.07.26.06.07.53; Tue, 26 Jul 2016 06:07:53 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org; dmarc=fail (p=NONE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 1905A68A445; Tue, 26 Jul 2016 16:07:46 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-it0-f53.google.com (mail-it0-f53.google.com [209.85.214.53]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 8ECEF68A43F for ; Tue, 26 Jul 2016 16:07:36 +0300 (EEST) Received: by mail-it0-f53.google.com with SMTP id j124so9694990ith.1 for ; Tue, 26 Jul 2016 06:07:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:from:date:message-id:subject:to; bh=4A1gEqF5tfOJesKWVpr9ehv4mXVVcpjBfEyTf7qqU0g=; b=zaxDXEUPnyWZR37f7T/6C7VvDcEw21t6pOn5HugzDKprsejxaaNpG3RS5n4dEfF33d zGfrU3qMQm/yLptfe1Do+9oylC6/RqM8b3QpLFr053yrUV5zF1fE+fh7R4LxJKHikAih FGHzsv9SsZhnkuGhMtoABHBR89E45El959sB0pBKY0u4XKnsPKfjM3GHHb6uh03gLQTW pnJIyJURgEoTK6UqM/GMUDsFBupSssiotVmeK9Tx9QOoLkH74ptjncIJLOct+JuoKZaf wdAmYxqFhx2bt0NeSB0icKrKfqQBZLFrohuyICgQN7xwaDAwGjH3SDkMuLjOkTSawGlE Kg9A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to; bh=4A1gEqF5tfOJesKWVpr9ehv4mXVVcpjBfEyTf7qqU0g=; b=Oh1f25/j5oWHkWDtxw3PFsb9WevE+J1TP7M3nTiZkmu4pHSI95fbN4GrU5ie/Sou1i egqSRIiz3/PmXxbf30IHnGxSyon1DgQC3RDoCIYbYXjJ33mLTty9e/FoPG2iKozVjZd9 3giMCwtKl3btS5H7SeTlIQqE/K8TWGJJCUpmA+A4ZnljoAH5WocFkvoU4kCSjjSq6ZWN kFPL4HjzorYoQczL9ndON7Z/7zS1WA36U+l/BX7uo+AgMGnngnIjRCery1ARpFI7IIh0 t8pOWHDqkTKVMRjvE09Wje+G0ha++B+vH0mx7W2qfvd+mdbzxZN5Y1hosJY6dmI/mP43 DKUA== X-Gm-Message-State: AEkoousA7rSnNHHRX9yaU5Uwc/qvGyXdsOjs3VXTuaN80ihrU7w9eRIEMymu5K8zglmAMnAayi6kDGSKQw1nWQ== X-Received: by 10.202.4.204 with SMTP id 195mr11522590oie.101.1469538458364; Tue, 26 Jul 2016 06:07:38 -0700 (PDT) MIME-Version: 1.0 Received: by 10.202.107.130 with HTTP; Tue, 26 Jul 2016 06:07:37 -0700 (PDT) In-Reply-To: References: <22691e45-5015-447d-032a-533d57436359@gmx.net> <20160723180734.GA4887@nb4> <20160725004647.GV4887@nb4> From: Martin Vignali Date: Tue, 26 Jul 2016 15:07:37 +0200 Message-ID: To: FFmpeg development discussions and patches X-Content-Filtered-By: Mailman/MimeDel 2.1.20 Subject: Re: [FFmpeg-devel] libavcodec : add psd image file decoder X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Sorry, forgot one out of array issue in the previous patch Correct patch in attach Martin From df3cfa66fa71e512125526c226c1629f00949409 Mon Sep 17 00:00:00 2001 From: Martin Vignali Date: Tue, 26 Jul 2016 15:06:10 +0200 Subject: [PATCH 2/2] libavcodec : add decoder for Photoshop PSD image file. Decode the Image Data Section (who contain merge picture). Support RGB/A and Grayscale/A in 8bits and 16 bits by channel. Support uncompress and rle compression in Image Data Section. --- Changelog | 1 + doc/general.texi | 2 + libavcodec/Makefile | 1 + libavcodec/allcodecs.c | 1 + libavcodec/avcodec.h | 1 + libavcodec/psd.c | 402 +++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 408 insertions(+) create mode 100644 libavcodec/psd.c diff --git a/Changelog b/Changelog index 99cdb80..f41d2b6 100644 --- a/Changelog +++ b/Changelog @@ -3,6 +3,7 @@ releases are sorted from youngest to oldest. version : +- PSD Decoder version 3.1: - DXVA2-accelerated HEVC Main10 decoding diff --git a/doc/general.texi b/doc/general.texi index 4db209f..06775f4 100644 --- a/doc/general.texi +++ b/doc/general.texi @@ -577,6 +577,8 @@ following image formats are supported: @item PNG @tab X @tab X @item PPM @tab X @tab X @tab Portable PixelMap image +@item PSD @tab @tab X + @tab Photoshop @item PTX @tab @tab X @tab V.Flash PTX format @item SGI @tab X @tab X diff --git a/libavcodec/Makefile b/libavcodec/Makefile index fd0d1f0..913b4a8 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -456,6 +456,7 @@ OBJS-$(CONFIG_PRORES_LGPL_DECODER) += proresdec_lgpl.o proresdsp.o proresdat OBJS-$(CONFIG_PRORES_ENCODER) += proresenc_anatoliy.o OBJS-$(CONFIG_PRORES_AW_ENCODER) += proresenc_anatoliy.o OBJS-$(CONFIG_PRORES_KS_ENCODER) += proresenc_kostya.o proresdata.o +OBJS-$(CONFIG_PSD_DECODER) += psd.o OBJS-$(CONFIG_PTX_DECODER) += ptx.o OBJS-$(CONFIG_QCELP_DECODER) += qcelpdec.o \ celp_filters.o acelp_vectors.o \ diff --git a/libavcodec/allcodecs.c b/libavcodec/allcodecs.c index 54efaad..458f603 100644 --- a/libavcodec/allcodecs.c +++ b/libavcodec/allcodecs.c @@ -278,6 +278,7 @@ void avcodec_register_all(void) REGISTER_ENCODER(PRORES_AW, prores_aw); REGISTER_ENCODER(PRORES_KS, prores_ks); REGISTER_DECODER(PRORES_LGPL, prores_lgpl); + REGISTER_DECODER(PSD, psd); REGISTER_DECODER(PTX, ptx); REGISTER_DECODER(QDRAW, qdraw); REGISTER_DECODER(QPEG, qpeg); diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 39713ed..75a9970 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -409,6 +409,7 @@ enum AVCodecID { AV_CODEC_ID_MAGICYUV, AV_CODEC_ID_SHEERVIDEO, AV_CODEC_ID_YLC, + AV_CODEC_ID_PSD, /* various PCM "codecs" */ AV_CODEC_ID_FIRST_AUDIO = 0x10000, ///< A dummy id pointing at the start of audio codecs diff --git a/libavcodec/psd.c b/libavcodec/psd.c new file mode 100644 index 0000000..16808ad --- /dev/null +++ b/libavcodec/psd.c @@ -0,0 +1,402 @@ +/* + * 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; + + unsigned long uncompressed_size; + unsigned int pixel_size;/* 1 for 8 bits, 2 for 16 bits */ + unsigned int line_size;/* length of src data (even width) */ + + int width; + int height; + + enum PsdCompr compression; + enum PsdColorMode color_mode; +} PSDContext; + +static int decode_header(PSDContext * s) +{ + int signature, version, color_mode, len_section, compression; + 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 < 1) || (s->height > 30000)) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid height %d.\n", s->height); + return AVERROR_INVALIDDATA; + } + + s->width = bytestream2_get_be32(&s->gb); + if ((s->width < 1) || (s->width > 30000)) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid width %d.\n", s->width); + return AVERROR_INVALIDDATA; + } + + 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 (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); + + /* image ressources */ + len_section = bytestream2_get_be32(&s->gb); + 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 (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; + } + compression = bytestream2_get_be16(&s->gb); + + switch (compression) { + case 0: + s->compression = PSD_RAW; + break; + case 1: + s->compression = PSD_RLE; + break; + case 2: + s->compression = PSD_ZIP_WITHOUT_P; + avpriv_request_sample(s->avctx, "ZIP without predictor compression"); + return AVERROR_PATCHWELCOME; + break; + case 3: + s->compression = PSD_ZIP_WITH_P; + 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", 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; + + 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; + s->uncompressed_size = s->line_size * s->height * s->channel_count; + + switch (s->color_mode) { + case PSD_RGB: + if (s->channel_count == 3) { + if (s->channel_depth == 8) { + avctx->pix_fmt = AV_PIX_FMT_RGB24; + } else if (s->channel_depth == 16) { + avctx->pix_fmt = AV_PIX_FMT_RGB48BE; + } else { + avpriv_report_missing_feature(avctx, "channel depth %d unsupported 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_RGBA; + } else if (s->channel_depth == 16) { + avctx->pix_fmt = AV_PIX_FMT_RGBA64BE; + } else { + avpriv_report_missing_feature(avctx, "channel depth %d unsupported for rgb", s->channel_depth); + return AVERROR_PATCHWELCOME; + } + } else { + avpriv_report_missing_feature(avctx, "channel count %d unsupported for rgb", s->channel_count); + return AVERROR_PATCHWELCOME; + } + break; + 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 { + avpriv_report_missing_feature(avctx, "channel depth %d unsupported 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 unsupported for grayscale", s->channel_depth); + return AVERROR_PATCHWELCOME; + } + } else { + avpriv_report_missing_feature(avctx, "channel count %d unsupported for grayscale", s->channel_count); + return AVERROR_PATCHWELCOME; + } + break; + default: + avpriv_report_missing_feature(avctx, "color mode %d unsupported", s->color_mode); + return AVERROR_PATCHWELCOME; + } + + 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; + } + + /* reorganize uncompress data. Each channel is stored one after the other */ + 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 ++; + } + } + } + } + + 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, +}; -- 1.9.3 (Apple Git-50)