This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit a469598191d630e3c0706146f7af68d72f77ff55 Author: John Chadwick <[email protected]> AuthorDate: Tue Nov 18 23:45:34 2025 -0500 Commit: Marton Balint <[email protected]> CommitDate: Wed Feb 11 20:35:20 2026 +0000 avcodec/psd: Support auxiliary channels Photoshop documents can contain additional "auxiliary" channels that don't take part in layer compositing, therefore, using the channel count to determine the presence of an alpha channel in the merged image is incorrect. Instead, as per the PSD specification, use the sign of the layer count (present in the layers and masks section) to determine if there is an alpha channel, then determine the number of primary channels using both the presence of the alpha channel and the pixel format. --- libavcodec/psd.c | 100 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 63 insertions(+), 37 deletions(-) diff --git a/libavcodec/psd.c b/libavcodec/psd.c index ea88f254bf..426ac40b01 100644 --- a/libavcodec/psd.c +++ b/libavcodec/psd.c @@ -52,6 +52,7 @@ typedef struct PSDContext { uint16_t channel_count; uint16_t channel_depth; + uint16_t primary_channels; uint64_t uncompressed_size; unsigned int pixel_size;/* 1 for 8 bits, 2 for 16 bits */ @@ -60,6 +61,8 @@ typedef struct PSDContext { int width; int height; + int16_t layer_count; + enum PsdCompr compression; enum PsdColorMode color_mode; @@ -193,6 +196,13 @@ static int decode_header(PSDContext * s) return AVERROR_INVALIDDATA; } + if (len_section >= 6) { + /* layer count (in layers and masks section) */ + bytestream2_skip(&s->gb, 4); + s->layer_count = bytestream2_get_be16(&s->gb); + len_section -= 6; + } + if (bytestream2_get_bytes_left(&s->gb) < len_section) { av_log(s->avctx, AV_LOG_ERROR, "Incomplete file.\n"); return AVERROR_INVALIDDATA; @@ -301,11 +311,13 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, uint8_t plane_number; PSDContext *s = avctx->priv_data; - s->avctx = avctx; - s->channel_count = 0; - s->channel_depth = 0; - s->tmp = NULL; - s->line_size = 0; + s->avctx = avctx; + s->channel_count = 0; + s->channel_depth = 0; + s->primary_channels = 0; + s->tmp = NULL; + s->line_size = 0; + s->layer_count = 0; bytestream2_init(&s->gb, avpkt->data, avpkt->size); @@ -317,98 +329,112 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, switch (s->color_mode) { case PSD_BITMAP: - if (s->channel_depth != 1 || s->channel_count != 1) { + 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; + s->primary_channels = 1; avctx->pix_fmt = AV_PIX_FMT_MONOWHITE; break; case PSD_INDEXED: - if (s->channel_depth != 8 || s->channel_count != 1) { + 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; } + s->primary_channels = 1; avctx->pix_fmt = AV_PIX_FMT_PAL8; break; case PSD_CMYK: - if (s->channel_count == 4) { + if (s->layer_count < 0 && s->channel_count >= 5) { if (s->channel_depth == 8) { - avctx->pix_fmt = AV_PIX_FMT_GBRP; + avctx->pix_fmt = AV_PIX_FMT_GBRAP; } else if (s->channel_depth == 16) { - avctx->pix_fmt = AV_PIX_FMT_GBRP16BE; + avctx->pix_fmt = AV_PIX_FMT_GBRAP16BE; } else { avpriv_report_missing_feature(avctx, "channel depth %d for cmyk", s->channel_depth); return AVERROR_PATCHWELCOME; } - } else if (s->channel_count == 5) { + s->primary_channels = 5; + } else if (s->channel_count >= 4) { if (s->channel_depth == 8) { - avctx->pix_fmt = AV_PIX_FMT_GBRAP; + avctx->pix_fmt = AV_PIX_FMT_GBRP; } else if (s->channel_depth == 16) { - avctx->pix_fmt = AV_PIX_FMT_GBRAP16BE; + avctx->pix_fmt = AV_PIX_FMT_GBRP16BE; } else { avpriv_report_missing_feature(avctx, "channel depth %d for cmyk", s->channel_depth); return AVERROR_PATCHWELCOME; } + s->primary_channels = 4; } else { - avpriv_report_missing_feature(avctx, "channel count %d for cmyk", s->channel_count); - return AVERROR_PATCHWELCOME; + av_log(s->avctx, AV_LOG_ERROR, + "Invalid cmyk file (channel_count %d)\n", + s->channel_count); + return AVERROR_INVALIDDATA; } break; case PSD_RGB: - if (s->channel_count == 3) { + if (s->layer_count < 0 && s->channel_count >= 4) { if (s->channel_depth == 8) { - avctx->pix_fmt = AV_PIX_FMT_GBRP; + avctx->pix_fmt = AV_PIX_FMT_GBRAP; } else if (s->channel_depth == 16) { - avctx->pix_fmt = AV_PIX_FMT_GBRP16BE; + 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 if (s->channel_count == 4) { + s->primary_channels = 4; + } else if (s->channel_count >= 3) { if (s->channel_depth == 8) { - avctx->pix_fmt = AV_PIX_FMT_GBRAP; + avctx->pix_fmt = AV_PIX_FMT_GBRP; } else if (s->channel_depth == 16) { - avctx->pix_fmt = AV_PIX_FMT_GBRAP16BE; + avctx->pix_fmt = AV_PIX_FMT_GBRP16BE; } else { avpriv_report_missing_feature(avctx, "channel depth %d for rgb", s->channel_depth); return AVERROR_PATCHWELCOME; } + s->primary_channels = 3; } else { - avpriv_report_missing_feature(avctx, "channel count %d for rgb", s->channel_count); - return AVERROR_PATCHWELCOME; + av_log(s->avctx, AV_LOG_ERROR, + "Invalid rgb file (channel_count %d)\n", + s->channel_count); + return AVERROR_INVALIDDATA; } 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->layer_count < 0 && s->channel_count >= 2) { if (s->channel_depth == 8) { - avctx->pix_fmt = AV_PIX_FMT_GRAY8; + avctx->pix_fmt = AV_PIX_FMT_YA8; } 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; + 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 if (s->channel_count == 2) { + s->primary_channels = 2; + } else if (s->channel_count >= 1) { if (s->channel_depth == 8) { - avctx->pix_fmt = AV_PIX_FMT_YA8; + avctx->pix_fmt = AV_PIX_FMT_GRAY8; } else if (s->channel_depth == 16) { - avctx->pix_fmt = AV_PIX_FMT_YA16BE; + 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; } + s->primary_channels = 1; } else { - avpriv_report_missing_feature(avctx, "channel count %d for grayscale", s->channel_count); - return AVERROR_PATCHWELCOME; + av_log(s->avctx, AV_LOG_ERROR, + "Invalid grayscale file (channel_count %d)\n", + s->channel_count); + return AVERROR_INVALIDDATA; } break; default: @@ -446,10 +472,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, /* 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 (c = 0; c < 2; 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; + index_out = y * picture->linesize[0] + x * 2 * s->pixel_size + c * s->pixel_size; for (p = 0; p < s->pixel_size; p++) { ptr[index_out + p] = *ptr_data; ptr_data ++; @@ -518,10 +544,10 @@ static int decode_frame(AVCodecContext *avctx, AVFrame *picture, } } } else {/* Planar */ - if (s->channel_count == 1)/* gray 8 or gray 16be */ + if (s->primary_channels == 1)/* bitmap, indexed, grayscale */ eq_channel[0] = 0;/* assign first channel, to first plane */ - for (c = 0; c < s->channel_count; c++) { + for (c = 0; c < s->primary_channels; c++) { plane_number = eq_channel[c]; ptr = picture->data[plane_number];/* get the right plane */ for (y = 0; y < s->height; y++) { _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
