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]

Reply via email to