Commit 64fc55b6c for imagemagick.org

commit 64fc55b6c3c7e481923b87789e8abb7c653f6de9
Author: Dirk Lemstra <dirk@lemstra.org>
Date:   Thu May 14 15:47:40 2026 +0200

    Allow reading more pixel masks in the DDS decoder (#8723)

diff --git a/coders/dds.c b/coders/dds.c
index c4ad8ae39..329319888 100644
--- a/coders/dds.c
+++ b/coders/dds.c
@@ -2370,6 +2370,30 @@ static MagickBooleanType ReadBC7(const ImageInfo *image_info,Image *image,
     return(SkipMipmaps(image,dds_info,16,exception));
 }

+static inline void GetMaskShiftAndMaxval(const size_t mask, size_t *shift,
+  size_t *max_val)
+{
+  *shift=0;
+  *max_val=0;
+  if (mask == 0)
+    return;
+  while (((mask >> *shift) & 1) == 0)
+    (*shift)++;
+  *max_val=mask >> *shift;
+}
+
+static inline unsigned char ExtractChannelFromMask(unsigned short color,
+  size_t mask, size_t shift, size_t max_val)
+{
+  size_t
+    value;
+
+  if (max_val == 0)
+    return(0);
+  value=(((size_t) color & mask) >> shift) * 255U + max_val / 2U;
+  return((unsigned char) (value / max_val));
+}
+
 static MagickBooleanType ReadUncompressedRGBPixels(Image *image,
   const DDSInfo *dds_info,ExceptionInfo *exception)
 {
@@ -2379,6 +2403,17 @@ static MagickBooleanType ReadUncompressedRGBPixels(Image *image,
   Quantum
     *q;

+  size_t
+    b_mask,
+    b_max,
+    b_shift,
+    g_mask,
+    g_max,
+    g_shift,
+    r_mask,
+    r_max,
+    r_shift;
+
   ssize_t
     x,
     y;
@@ -2386,6 +2421,20 @@ static MagickBooleanType ReadUncompressedRGBPixels(Image *image,
   unsigned short
     color;

+  r_mask=dds_info->pixelformat.r_bitmask;
+  g_mask=dds_info->pixelformat.g_bitmask;
+  b_mask=dds_info->pixelformat.b_bitmask;
+  if ((r_mask == 0) && (g_mask == 0) && (b_mask == 0))
+    {
+      r_mask=0xf800;
+      g_mask=0x07e0;
+      b_mask=0x001f;
+    }
+
+  GetMaskShiftAndMaxval(r_mask,&r_shift,&r_max);
+  GetMaskShiftAndMaxval(g_mask,&g_shift,&g_max);
+  GetMaskShiftAndMaxval(b_mask,&b_shift,&b_max);
+
   is_rgb=IsBitMask(dds_info->pixelformat,0x000000ff,0x0000ff00,0x00ff0000,0x0000000) ? MagickTrue : MagickFalse;
   for (y = 0; y < (ssize_t) image->rows; y++)
   {
@@ -2403,12 +2452,12 @@ static MagickBooleanType ReadUncompressedRGBPixels(Image *image,
                (dds_info->extFormat == DXGI_FORMAT_B5G6R5_UNORM))
         {
            color=ReadBlobShort(image);
-           SetPixelRed(image,ScaleCharToQuantum((unsigned char)
-             (((color >> 11)/31.0)*255)),q);
-           SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
-             ((((unsigned short)(color << 5) >> 10)/63.0)*255)),q);
-           SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
-             ((((unsigned short)(color << 11) >> 11)/31.0)*255)),q);
+           SetPixelRed(image,ScaleCharToQuantum(ExtractChannelFromMask(
+             color,r_mask,r_shift,r_max)),q);
+           SetPixelGreen(image,ScaleCharToQuantum(ExtractChannelFromMask(
+             color,g_mask,g_shift,g_max)),q);
+           SetPixelBlue(image,ScaleCharToQuantum(ExtractChannelFromMask(
+             color,b_mask,b_shift,b_max)),q);
         }
       else
         {
@@ -2569,39 +2618,61 @@ static MagickBooleanType ReadUncompressedRGBAPixels(Image *image,
   const DDSInfo *dds_info,ExceptionInfo *exception)
 {
   MagickBooleanType
+    is_grayscale,
     is_rgba;

   Quantum
     *q;

+  size_t
+    a_mask,
+    a_max,
+    a_shift,
+    b_mask,
+    b_max,
+    b_shift,
+    g_mask,
+    g_max,
+    g_shift,
+    r_mask,
+    r_max,
+    r_shift;
+
   ssize_t
-    alphaBits,
     x,
     y;

   unsigned short
     color;

-  alphaBits=0;
-  if (dds_info->pixelformat.rgb_bitcount == 16)
+  a_mask=dds_info->pixelformat.alpha_bitmask;
+  b_mask=dds_info->pixelformat.b_bitmask;
+  g_mask=dds_info->pixelformat.g_bitmask;
+  r_mask=dds_info->pixelformat.r_bitmask;
+
+  is_grayscale=MagickFalse;
+  if ((dds_info->pixelformat.rgb_bitcount == 16) ||
+      (dds_info->extFormat == DXGI_FORMAT_B5G5R5A1_UNORM))
     {
-      if (IsBitMask(dds_info->pixelformat,0x7c00,0x03e0,0x001f,0x8000))
-        alphaBits=1;
-      else if ((IsBitMask(dds_info->pixelformat,0x00ff,0x00ff,0x00ff,0xff00)) ||
-               (IsBitMask(dds_info->pixelformat,0x00ff,0x0000,0x0000,0xff00)))
+      if ((r_mask == 0) && (g_mask == 0) && (b_mask == 0) && (a_mask == 0))
+        {
+          r_mask=0x7c00;
+          g_mask=0x03e0;
+          b_mask=0x001f;
+          a_mask=0x8000;
+        }
+      if ((r_mask == g_mask && g_mask == b_mask) ||
+          (g_mask == 0 && b_mask == 0))
         {
-          alphaBits=2;
+          is_grayscale=MagickTrue;
           (void) SetImageType(image,GrayscaleAlphaType,exception);
         }
-      else if (IsBitMask(dds_info->pixelformat,0x0f00,0x00f0,0x000f,0xf000))
-        alphaBits=4;
-      else
-        ThrowBinaryException(CorruptImageError,"ImageTypeNotSupported",
-          image->filename);
     }

-  if (dds_info->extFormat == DXGI_FORMAT_B5G5R5A1_UNORM)
-    alphaBits=1;
+  GetMaskShiftAndMaxval(a_mask,&a_shift,&a_max);
+  GetMaskShiftAndMaxval(r_mask,&r_shift,&r_max);
+  GetMaskShiftAndMaxval(g_mask,&g_shift,&g_max);
+  GetMaskShiftAndMaxval(b_mask,&b_shift,&b_max);

   is_rgba=IsBitMask(dds_info->pixelformat,0x000000ff,0x0000ff00,0x00ff0000,0xff00000) ? MagickTrue : MagickFalse;
   for (y = 0; y < (ssize_t) image->rows; y++)
@@ -2613,37 +2684,23 @@ static MagickBooleanType ReadUncompressedRGBAPixels(Image *image,

     for (x = 0; x < (ssize_t) image->columns; x++)
     {
-      if (dds_info->pixelformat.rgb_bitcount == 16 ||
-          dds_info->extFormat == DXGI_FORMAT_B5G5R5A1_UNORM)
+      if ((dds_info->pixelformat.rgb_bitcount == 16) ||
+          (dds_info->extFormat == DXGI_FORMAT_B5G5R5A1_UNORM))
         {
            color=ReadBlobShort(image);
-           if (alphaBits == 1)
+           SetPixelAlpha(image,ScaleCharToQuantum(ExtractChannelFromMask(
+             color,a_mask,a_shift,a_max)),q);
+           if (is_grayscale != MagickFalse)
+             SetPixelGray(image,ScaleCharToQuantum((unsigned char) color),q);
+           else
              {
-               SetPixelAlpha(image,(color & (1 << 15)) ? QuantumRange : 0,q);
-               SetPixelRed(image,ScaleCharToQuantum((unsigned char)
-                 ((((unsigned short)(color << 1) >> 11)/31.0)*255)),q);
-               SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
-                 ((((unsigned short)(color << 6) >> 11)/31.0)*255)),q);
-               SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
-                 ((((unsigned short)(color << 11) >> 11)/31.0)*255)),q);
+               SetPixelRed(image,ScaleCharToQuantum(ExtractChannelFromMask(
+                 color,r_mask,r_shift,r_max)),q);
+               SetPixelGreen(image,ScaleCharToQuantum(ExtractChannelFromMask(
+                 color,g_mask,g_shift,g_max)),q);
+               SetPixelBlue(image,ScaleCharToQuantum(ExtractChannelFromMask(
+                 color,b_mask,b_shift,b_max)),q);
              }
-          else if (alphaBits == 2)
-            {
-               SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
-                 (color >> 8)),q);
-               SetPixelGray(image,ScaleCharToQuantum((unsigned char)color),q);
-            }
-          else
-            {
-               SetPixelAlpha(image,ScaleCharToQuantum((unsigned char)
-                 (((color >> 12)/15.0)*255)),q);
-               SetPixelRed(image,ScaleCharToQuantum((unsigned char)
-                 ((((unsigned short)(color << 4) >> 12)/15.0)*255)),q);
-               SetPixelGreen(image,ScaleCharToQuantum((unsigned char)
-                 ((((unsigned short)(color << 8) >> 12)/15.0)*255)),q);
-               SetPixelBlue(image,ScaleCharToQuantum((unsigned char)
-                 ((((unsigned short)(color << 12) >> 12)/15.0)*255)),q);
-            }
         }
       else if (dds_info->extFormat == DXGI_FORMAT_R10G10B10A2_UNORM)
         {