Merge tag 'v1.6.58' into branch 'libpng18' into develop
diff --git a/.gitignore b/.gitignore
index 3768c51..3d08cb3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -162,6 +162,7 @@
 /png-fix-itxt
 /pngcp
 /pngfix
+/pnggetset
 /pngimage
 /pngstest
 /pngtest
diff --git a/CHANGES b/CHANGES
index 7a6156e..50bd394 100644
--- a/CHANGES
+++ b/CHANGES
@@ -6379,6 +6379,12 @@
   Fixed integer overflow in rowbytes computation in read transforms.
     (Contributed by Mohammad Seet.)
 
+Version 1.6.58 [April 15, 2026]
+  Fixed a regression introduced in version 1.6.56 that caused `png_get_PLTE`
+    to return stale palette data after applying gamma and background transforms
+    in-place.
+    (Reported by ralfjunker <ralfjunker@users.noreply.github.com>.)
+
 Version 2.0.0 [TODO]
 
 Send comments/corrections/commendations to png-mng-implement at lists.sf.net.
diff --git a/contrib/libtests/pnggetset.c b/contrib/libtests/pnggetset.c
index 6ae43dc..e2c1ca5 100644
--- a/contrib/libtests/pnggetset.c
+++ b/contrib/libtests/pnggetset.c
@@ -6,12 +6,7 @@
  * For conditions of distribution and use, see the disclaimer
  * and license in png.h
  *
- * Test the get-then-set roundtrip for chunk types whose getters return
- * a pointer to internal storage.
- *
- * Passing such a pointer back into the corresponding setter must not
- * cause a use-after-free.  A previous version freed the internal buffer
- * before copying from the caller-supplied pointer.
+ * Test getter and setter correctness.
  */
 
 #include <stdio.h>
@@ -99,9 +94,9 @@
    }
    for (i = 0; i < 4; i++)
    {
-      if (got_palette[i].red   != (png_byte)(i * 10) ||
-          got_palette[i].green != (png_byte)(i * 20) ||
-          got_palette[i].blue  != (png_byte)(i * 30))
+      if ((got_palette[i].red != (png_byte)(i * 10))
+          || (got_palette[i].green != (png_byte)(i * 20))
+          || (got_palette[i].blue != (png_byte)(i * 30)))
       {
          fprintf(stderr,
              "pnggetset: PLTE entry %d corrupted after roundtrip\n", i);
@@ -569,6 +564,215 @@
 }
 #endif /* PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED */
 
+/* Memory buffer for PNG I/O without temp files. */
+#define MEM_BUF_SIZE 4096
+
+typedef struct
+{
+   png_byte data[MEM_BUF_SIZE];
+   size_t len;
+   size_t pos;
+} mem_buf;
+
+static void PNGCBAPI
+mem_write(png_structp png_ptr, png_bytep buf, png_size_t length)
+{
+   mem_buf *mb = (mem_buf *)png_get_io_ptr(png_ptr);
+
+   if (mb->len + length > MEM_BUF_SIZE)
+      png_error(png_ptr, "pnggetset: write overflow");
+
+   memcpy(mb->data + mb->len, buf, length);
+   mb->len += length;
+}
+
+static void PNGCBAPI
+mem_flush(png_structp png_ptr)
+{
+   (void)png_ptr;
+}
+
+static void PNGCBAPI
+mem_read(png_structp png_ptr, png_bytep buf, png_size_t length)
+{
+   mem_buf *mb = (mem_buf *)png_get_io_ptr(png_ptr);
+
+   if (mb->pos + length > mb->len)
+      png_error(png_ptr, "pnggetset: read overflow");
+
+   memcpy(buf, mb->data + mb->pos, length);
+   mb->pos += length;
+}
+
+/* Palette sync after gamma correction.
+ *
+ * When info_ptr->palette and png_ptr->palette are separate buffers,
+ * in-place gamma correction of png_ptr->palette must be synced back
+ * to info_ptr->palette so that png_get_PLTE returns the corrected
+ * values.
+ */
+#define PLTE_SYNC_NPALETTE 4
+
+static const png_color plte_sync_original[PLTE_SYNC_NPALETTE] =
+{
+   {  64,  96, 128 },
+   { 128, 160, 192 },
+   { 192, 224, 240 },
+   {  32,  48,  64 }
+};
+
+static int
+test_plte_palette_sync(void)
+{
+   mem_buf buf;
+   png_structp png_ptr;
+   png_infop info_ptr;
+   png_colorp got_palette;
+   int num_palette;
+   double file_gamma;
+   png_byte row[1];
+   int i;
+   int changed;
+
+   /* Write a 1x1 palette PNG with gAMA = 1.0 (linear). */
+   buf.len = 0;
+   buf.pos = 0;
+
+   png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+       NULL, NULL, NULL);
+   if (png_ptr == NULL)
+   {
+      fprintf(stderr, "pnggetset: png_create_write_struct failed\n");
+      return 1;
+   }
+
+   info_ptr = png_create_info_struct(png_ptr);
+   if (info_ptr == NULL)
+   {
+      fprintf(stderr, "pnggetset: png_create_info_struct failed\n");
+      png_destroy_write_struct(&png_ptr, NULL);
+      return 1;
+   }
+
+   if (setjmp(png_jmpbuf(png_ptr)))
+   {
+      fprintf(stderr, "pnggetset: libpng error in test_plte_palette_sync"
+          " (write)\n");
+      png_destroy_write_struct(&png_ptr, &info_ptr);
+      return 1;
+   }
+
+   png_set_write_fn(png_ptr, &buf, mem_write, mem_flush);
+   png_set_IHDR(png_ptr, info_ptr, 1, 1, 8, PNG_COLOR_TYPE_PALETTE,
+       PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE,
+       PNG_FILTER_TYPE_BASE);
+   png_set_PLTE(png_ptr, info_ptr,
+       (png_colorp)plte_sync_original, PLTE_SYNC_NPALETTE);
+   png_set_gAMA(png_ptr, info_ptr, 1.0);
+   png_write_info(png_ptr, info_ptr);
+
+   row[0] = 0;
+   png_write_row(png_ptr, row);
+   png_write_end(png_ptr, info_ptr);
+   png_destroy_write_struct(&png_ptr, &info_ptr);
+
+   /* Read back with gamma correction as the sole transform. */
+   buf.pos = 0;
+
+   png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
+       NULL, NULL, NULL);
+   if (png_ptr == NULL)
+   {
+      fprintf(stderr, "pnggetset: png_create_read_struct failed\n");
+      return 1;
+   }
+
+   info_ptr = png_create_info_struct(png_ptr);
+   if (info_ptr == NULL)
+   {
+      fprintf(stderr, "pnggetset: png_create_info_struct failed\n");
+      png_destroy_read_struct(&png_ptr, NULL, NULL);
+      return 1;
+   }
+
+   if (setjmp(png_jmpbuf(png_ptr)))
+   {
+      fprintf(stderr, "pnggetset: libpng error in test_plte_palette_sync"
+          " (read)\n");
+      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+      return 1;
+   }
+
+   png_set_read_fn(png_ptr, &buf, mem_read);
+   png_read_info(png_ptr, info_ptr);
+
+   if (png_get_gAMA(png_ptr, info_ptr, &file_gamma) == 0)
+   {
+      fprintf(stderr, "pnggetset: gAMA chunk not found\n");
+      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+      return 1;
+   }
+
+   png_set_gamma(png_ptr, 2.2, file_gamma);
+   png_read_update_info(png_ptr, info_ptr);
+
+   if (png_get_PLTE(png_ptr, info_ptr, &got_palette, &num_palette) == 0)
+   {
+      fprintf(stderr, "pnggetset: png_get_PLTE failed after update\n");
+      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+      return 1;
+   }
+
+   if (num_palette != PLTE_SYNC_NPALETTE)
+   {
+      fprintf(stderr, "pnggetset: palette size %d, expected %d\n",
+          num_palette, PLTE_SYNC_NPALETTE);
+      png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+      return 1;
+   }
+
+   /* Every entry must differ from the original after gamma correction
+    * (file_gamma=1.0, screen_gamma=2.2).  If the sync was skipped,
+    * info_ptr->palette still holds the stale pre-correction values.
+    */
+   changed = 0;
+
+   for (i = 0; i < PLTE_SYNC_NPALETTE; i++)
+   {
+      if ((got_palette[i].red != plte_sync_original[i].red)
+          || (got_palette[i].green != plte_sync_original[i].green)
+          || (got_palette[i].blue != plte_sync_original[i].blue))
+      {
+         changed++;
+      }
+      else
+      {
+         fprintf(stderr,
+             "pnggetset: palette entry %d NOT gamma-corrected: "
+             "got {%u, %u, %u}, same as original\n",
+             i,
+             (unsigned)got_palette[i].red,
+             (unsigned)got_palette[i].green,
+             (unsigned)got_palette[i].blue);
+      }
+   }
+
+   png_read_row(png_ptr, row, NULL);
+   png_read_end(png_ptr, NULL);
+   png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
+
+   if (changed != PLTE_SYNC_NPALETTE)
+   {
+      fprintf(stderr,
+          "pnggetset: only %d of %d palette entries were "
+          "gamma-corrected (palette sync failed)\n",
+          changed, PLTE_SYNC_NPALETTE);
+      return 1;
+   }
+
+   return 0;
+}
+
 int
 main(void)
 {
@@ -644,5 +848,15 @@
       printf("PASS\n");
 #endif
 
+   printf("Testing PLTE sync after gamma correction... ");
+   fflush(stdout);
+   if (test_plte_palette_sync() != 0)
+   {
+      printf("FAIL\n");
+      result = 1;
+   }
+   else
+      printf("PASS\n");
+
    return result;
 }
diff --git a/manuals/libpng-manual.txt b/manuals/libpng-manual.txt
index da40b1e..258ad25 100644
--- a/manuals/libpng-manual.txt
+++ b/manuals/libpng-manual.txt
@@ -9,7 +9,7 @@
 
  Based on:
 
- libpng version 1.6.36, December 2018, through 1.6.57 - April 2026
+ libpng version 1.6.36, December 2018, through 1.6.58 - April 2026
  Updated and distributed by Cosmin Truta
  Copyright (c) 2018-2026 Cosmin Truta
 
diff --git a/manuals/libpng.3 b/manuals/libpng.3
index 512a6d1..52e10cc 100644
--- a/manuals/libpng.3
+++ b/manuals/libpng.3
@@ -1,4 +1,4 @@
-.TH LIBPNG 3 "April 8, 2026"
+.TH LIBPNG 3 "April 15, 2026"
 .SH NAME
 libpng \- Portable Network Graphics (PNG) Reference Library 1.8.0.git
 
@@ -516,7 +516,7 @@
 
  Based on:
 
- libpng version 1.6.36, December 2018, through 1.6.57 - April 2026
+ libpng version 1.6.36, December 2018, through 1.6.58 - April 2026
  Updated and distributed by Cosmin Truta
  Copyright (c) 2018-2026 Cosmin Truta
 
diff --git a/manuals/png.5 b/manuals/png.5
index df25110..803f8f6 100644
--- a/manuals/png.5
+++ b/manuals/png.5
@@ -1,4 +1,4 @@
-.TH PNG 5 "April 8, 2026"
+.TH PNG 5 "April 15, 2026"
 .SH NAME
 png \- Portable Network Graphics (PNG) format
 
diff --git a/png.h b/png.h
index 6dc5c4c..6c15518 100644
--- a/png.h
+++ b/png.h
@@ -14,7 +14,7 @@
  *   libpng versions 0.89, June 1996, through 0.96, May 1997: Andreas Dilger
  *   libpng versions 0.97, January 1998, through 1.6.35, July 2018:
  *     Glenn Randers-Pehrson
- *   libpng versions 1.6.36, December 2018, through 1.6.57, April 2026:
+ *   libpng versions 1.6.36, December 2018, through 1.6.58, April 2026:
  *     Cosmin Truta
  *   See also "Contributing Authors", below.
  */
diff --git a/pngpread.c b/pngpread.c
index 340c636..98717ec 100644
--- a/pngpread.c
+++ b/pngpread.c
@@ -1,6 +1,6 @@
 /* pngpread.c - read a png file in push mode
  *
- * Copyright (c) 2018-2025 Cosmin Truta
+ * Copyright (c) 2018-2026 Cosmin Truta
  * Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson
  * Copyright (c) 1996-1997 Andreas Dilger
  * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.
@@ -216,19 +216,12 @@
             return;
          }
 
+         png_crc_finish(png_ptr, png_ptr->push_length);
          png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
          return;
       }
       else if (chunk_name == png_fdAT)
       {
-         if (png_ptr->buffer_size < 4)
-         {
-            png_push_save_buffer(png_ptr);
-            return;
-         }
-
-         png_ensure_sequence_number(png_ptr, 4);
-
          if (!(png_ptr->mode & PNG_HAVE_fcTL))
          {
             /* Discard trailing fdATs for frames other than the first. */
@@ -241,6 +234,8 @@
                return;
             }
 
+            png_ensure_sequence_number(png_ptr, png_ptr->push_length);
+            png_crc_finish(png_ptr, png_ptr->push_length - 4);
             png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
             return;
          }
@@ -248,6 +243,13 @@
          else
          {
             /* Frame data follows. */
+            if (png_ptr->buffer_size < 4)
+            {
+               png_push_save_buffer(png_ptr);
+               return;
+            }
+
+            png_ensure_sequence_number(png_ptr, png_ptr->push_length);
             png_ptr->idat_size = png_ptr->push_length - 4;
             png_ptr->mode |= PNG_HAVE_IDAT;
             png_ptr->process_mode = PNG_READ_IDAT_MODE;
@@ -291,6 +293,7 @@
             return;
          }
          png_warning(png_ptr, "Ignoring unexpected chunk in APNG sequence");
+         png_crc_finish(png_ptr, png_ptr->push_length);
          png_ptr->mode &= ~PNG_HAVE_CHUNK_HEADER;
          return;
       }
diff --git a/pngrtran.c b/pngrtran.c
index 85af06b..25a333f 100644
--- a/pngrtran.c
+++ b/pngrtran.c
@@ -2052,19 +2052,15 @@
 {
    png_debug(1, "in png_read_transform_info");
 
-   if (png_ptr->transformations != 0)
+   if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
+       info_ptr->palette != NULL && png_ptr->palette != NULL)
    {
-      if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE &&
-          info_ptr->palette != NULL && png_ptr->palette != NULL)
-      {
-         /* Sync info_ptr->palette with png_ptr->palette.
-          * The function png_init_read_transformations may have modified
-          * png_ptr->palette in place (e.g. for gamma correction or for
-          * background compositing).
-          */
-         memcpy(info_ptr->palette, png_ptr->palette,
-             PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)));
-      }
+      /* Sync info_ptr->palette with png_ptr->palette, which may
+       * have been modified by png_init_read_transformations
+       * (e.g. for gamma correction or background compositing).
+       */
+      memcpy(info_ptr->palette, png_ptr->palette,
+          PNG_MAX_PALETTE_LENGTH * (sizeof (png_color)));
    }
 
 #ifdef PNG_READ_EXPAND_SUPPORTED
diff --git a/pngtest.c b/pngtest.c
index 00fc0aa..57b38d1 100644
--- a/pngtest.c
+++ b/pngtest.c
@@ -50,9 +50,6 @@
  */
 #define STDERR stdout
 
-/* Generate a compiler error if there is an old png.h in the search path. */
-typedef png_libpng_version_2_0_0_git Your_png_h_is_not_version_2_0_0_git;
-
 /* Ensure that all version numbers in png.h are consistent with one another. */
 #if (PNG_LIBPNG_VER != PNG_LIBPNG_VER_MAJOR * 10000 + \
                        PNG_LIBPNG_VER_MINOR * 100 + \