public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
* [rpms/opencv] opencv5: Backport all post-4.11.0 PNG fixes, including big-endian fix
@ 2026-08-28 13:32 Adam Williamson
  0 siblings, 0 replies; only message in thread
From: Adam Williamson @ 2026-08-28 13:32 UTC (permalink / raw)
  To: git-commits

            A new commit has been pushed.

            Repo   : rpms/opencv
            Branch : opencv5
            Commit : 9a00dd90726a9ad006af7f1941b7f20de2ddd6be
            Author : Adam Williamson <awilliam@redhat.com>
            Date   : 2025-02-18T12:16:02-08:00
            Stats  : +1850/-1 in 11 file(s)
            URL    : https://src.fedoraproject.org/rpms/opencv/c/9a00dd90726a9ad006af7f1941b7f20de2ddd6be?branch=opencv5

            Log:
            Backport all post-4.11.0 PNG fixes, including big-endian fix

This backports all changes to the PNG reading code since 4.11.0,
mainly to get the fix for #2345306, PNG read fails on big-endian.
It's difficult to backport just that fix to 4.11.0 as it's built
on all the earlier changes, and the earlier changes look like
useful fixes too in any case.

See upstream https://github.com/opencv/opencv/issues/26913 and
https://github.com/opencv/opencv/pull/26915 .

Resolves: rhbz#2345306

Signed-off-by: Adam Williamson <awilliam@redhat.com>

---
diff --git a/0001-Merge-pull-request-26739-from-vrabaud-png_leak.patch b/0001-Merge-pull-request-26739-from-vrabaud-png_leak.patch
new file mode 100644
index 0000000..a24eff9
--- /dev/null
+++ b/0001-Merge-pull-request-26739-from-vrabaud-png_leak.patch
@@ -0,0 +1,155 @@
+From c399203e9861bf3ff5b976cd597b9820eee5d93a Mon Sep 17 00:00:00 2001
+From: Vincent Rabaud <vrabaud@google.com>
+Date: Fri, 10 Jan 2025 09:33:43 +0100
+Subject: [PATCH 01/10] Merge pull request #26739 from vrabaud:png_leak
+
+Add more boundary checks. #26739
+
+Also fix a bug in read_chunk where we could end up with png_get_uint_32(len) + 12 < 4
+
+### Pull Request Readiness Checklist
+
+See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
+
+- [x] I agree to contribute to the project under Apache 2 License.
+- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
+- [x] The PR is proposed to the proper branch
+- [ ] There is a reference to the original bug report and related work
+- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
+      Patch to opencv_extra has the same branch name.
+- [ ] The feature is well documented and sample code can be built with the project CMake
+---
+ modules/imgcodecs/src/grfmt_png.cpp | 54 +++++++++++++++++------------
+ modules/imgcodecs/src/grfmt_png.hpp |  2 +-
+ 2 files changed, 33 insertions(+), 23 deletions(-)
+
+diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp
+index 6b4cea405c..744f244a20 100644
+--- a/modules/imgcodecs/src/grfmt_png.cpp
++++ b/modules/imgcodecs/src/grfmt_png.cpp
+@@ -255,11 +255,14 @@ bool  PngDecoder::readHeader()
+                 png_init_io(png_ptr, m_f);
+             }
+ 
+-            if (read_from_io(&sig, 8, 1) != 1)
++            // Read PNG header: 137 80 78 71 13 10 26 10
++            if (!read_from_io(&sig, 8))
+                 return false;
+ 
+             id = read_chunk(m_chunkIHDR);
+-            if (!(id == id_IHDR && m_chunkIHDR.p.size() == 25))
++            // 8=HDR+size, 13=size of IHDR chunk, 4=CRC
++            // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR
++            if (!(id == id_IHDR && m_chunkIHDR.p.size() == 8 + 13 + 4))
+             {
+                 return false;
+             }
+@@ -283,23 +286,25 @@ bool  PngDecoder::readHeader()
+                     break;
+                 }
+ 
+-                if (id == id_acTL && chunk.p.size() == 20)
++                if (id == id_acTL)
+                 {
++                    // 8=HDR+size, 8=size of acTL chunk, 4=CRC
++                    // https://wiki.mozilla.org/APNG_Specification#%60acTL%60:_The_Animation_Control_Chunk
++                    if (chunk.p.size() != 8 + 8 + 4)
++                        return false;
+                     m_animation.loop_count = png_get_uint_32(&chunk.p[12]);
+ 
+-                    if (chunk.p[8] > 0)
+-                    {
+-                        chunk.p[8] = 0;
+-                        chunk.p[9] = 0;
+-                        m_frame_count = png_get_uint_32(&chunk.p[8]);
+-                        m_frame_count++;
+-                    }
+-                    else
+-                        m_frame_count = png_get_uint_32(&chunk.p[8]);
++                    m_frame_count = png_get_uint_32(&chunk.p[8]);
++                    if (m_frame_count == 0)
++                        return false;
+                 }
+ 
+                 if (id == id_fcTL)
+                 {
++                    // 8=HDR+size, 26=size of fcTL chunk, 4=CRC
++                    // https://wiki.mozilla.org/APNG_Specification#%60fcTL%60:_The_Frame_Control_Chunk
++                    if (chunk.p.size() != 8 + 26 + 4)
++                        return false;
+                     m_is_fcTL_loaded = true;
+                     w0 = png_get_uint_32(&chunk.p[12]);
+                     h0 = png_get_uint_32(&chunk.p[16]);
+@@ -313,6 +318,11 @@ bool  PngDecoder::readHeader()
+ 
+                 if (id == id_bKGD)
+                 {
++                    // 8=HDR+size, ??=size of bKGD chunk, 4=CRC
++                    // The spec is actually more complex: http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.bKGD
++                    // TODO: we only check that 4 bytes can be read from &chunk.p[8]. Fix.
++                    if (chunk.p.size() < 8 + 4)
++                        return false;
+                     int bgcolor = png_get_uint_32(&chunk.p[8]);
+                     m_animation.bgcolor[3] = (bgcolor >> 24) & 0xFF;
+                     m_animation.bgcolor[2] = (bgcolor >> 16) & 0xFF;
+@@ -669,34 +679,34 @@ void PngDecoder::compose_frame(std::vector<png_bytep>& rows_dst, const std::vect
+             });
+ }
+ 
+-size_t PngDecoder::read_from_io(void* _Buffer, size_t _ElementSize, size_t _ElementCount)
++bool PngDecoder::read_from_io(void* buffer, size_t num_bytes)
+ {
+     if (m_f)
+-        return fread(_Buffer, _ElementSize, _ElementCount, m_f);
++        return fread(buffer, 1, num_bytes, m_f) == num_bytes;
+ 
+-    if (m_buf_pos + _ElementSize > m_buf.cols * m_buf.rows * m_buf.elemSize()) {
++    if (m_buf_pos + num_bytes > m_buf.cols * m_buf.rows * m_buf.elemSize()) {
+         CV_LOG_WARNING(NULL, "PNG input buffer is incomplete");
+-        return 0;
++        return false;
+     }
+ 
+-    memcpy( _Buffer, m_buf.ptr() + m_buf_pos, _ElementSize );
+-    m_buf_pos += _ElementSize;
+-    return 1;
++    memcpy( buffer, m_buf.ptr() + m_buf_pos, num_bytes );
++    m_buf_pos += num_bytes;
++    return true;
+ }
+ 
+ uint32_t PngDecoder::read_chunk(Chunk& chunk)
+ {
+     unsigned char len[4];
+-    if (read_from_io(&len, 4, 1) == 1)
++    if (read_from_io(&len, 4))
+     {
+-        const size_t size = png_get_uint_32(len) + 12;
++        const size_t size = static_cast<size_t>(png_get_uint_32(len)) + 12;
+         if (size > PNG_USER_CHUNK_MALLOC_MAX)
+         {
+             CV_LOG_WARNING(NULL, "chunk data is too large");
+         }
+         chunk.p.resize(size);
+         memcpy(chunk.p.data(), len, 4);
+-        if (read_from_io(&chunk.p[4], chunk.p.size() - 4, 1) == 1)
++        if (read_from_io(&chunk.p[4], chunk.p.size() - 4))
+             return *(uint32_t*)(&chunk.p[4]);
+     }
+     return 0;
+diff --git a/modules/imgcodecs/src/grfmt_png.hpp b/modules/imgcodecs/src/grfmt_png.hpp
+index a950b9e941..dec2cd0b61 100644
+--- a/modules/imgcodecs/src/grfmt_png.hpp
++++ b/modules/imgcodecs/src/grfmt_png.hpp
+@@ -137,7 +137,7 @@ protected:
+     bool processing_start(void* frame_ptr, const Mat& img);
+     bool processing_finish();
+     void compose_frame(std::vector<png_bytep>& rows_dst, const std::vector<png_bytep>& rows_src, unsigned char bop, uint32_t x, uint32_t y, uint32_t w, uint32_t h, Mat& img);
+-    size_t read_from_io(void* _Buffer, size_t _ElementSize, size_t _ElementCount);
++    bool read_from_io(void* buffer, size_t num_bytes);
+     uint32_t  read_chunk(Chunk& chunk);
+ 
+     struct PngPtrs {
+-- 
+2.48.1
+

diff --git a/0002-Fix-remaining-bugs-in-PNG-reader.patch b/0002-Fix-remaining-bugs-in-PNG-reader.patch
new file mode 100644
index 0000000..f4804e6
--- /dev/null
+++ b/0002-Fix-remaining-bugs-in-PNG-reader.patch
@@ -0,0 +1,37 @@
+From b7b84ec6364809306776b48206ad36266274c297 Mon Sep 17 00:00:00 2001
+From: Vincent Rabaud <vrabaud@google.com>
+Date: Fri, 10 Jan 2025 14:57:39 +0100
+Subject: [PATCH 02/10] Fix remaining bugs in PNG reader
+
+- free chunk before a potential longjmp
+- do not try to allocate when the chunk is > PNG_USER_CHUNK_MALLOC_MAX
+---
+ modules/imgcodecs/src/grfmt_png.cpp | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp
+index 744f244a20..1ecc01f17f 100644
+--- a/modules/imgcodecs/src/grfmt_png.cpp
++++ b/modules/imgcodecs/src/grfmt_png.cpp
+@@ -339,6 +339,10 @@ bool  PngDecoder::readHeader()
+             png_bytep trans;
+             png_color_16p trans_values;
+ 
++            // Free chunk in case png_read_info uses longjmp.
++            chunk.p.clear();
++            chunk.p.shrink_to_fit();
++
+             png_read_info( png_ptr, info_ptr );
+             png_get_IHDR(png_ptr, info_ptr, &wdth, &hght,
+                 &bit_depth, &color_type, 0, 0, 0);
+@@ -703,6 +707,7 @@ uint32_t PngDecoder::read_chunk(Chunk& chunk)
+         if (size > PNG_USER_CHUNK_MALLOC_MAX)
+         {
+             CV_LOG_WARNING(NULL, "chunk data is too large");
++            return 0;
+         }
+         chunk.p.resize(size);
+         memcpy(chunk.p.data(), len, 4);
+-- 
+2.48.1
+

diff --git a/0003-Merge-pull-request-26782-from-vrabaud-png_leak.patch b/0003-Merge-pull-request-26782-from-vrabaud-png_leak.patch
new file mode 100644
index 0000000..9d11730
--- /dev/null
+++ b/0003-Merge-pull-request-26782-from-vrabaud-png_leak.patch
@@ -0,0 +1,654 @@
+From c29de7cc4b89c80f7ee910f318dfc1bc462d576c Mon Sep 17 00:00:00 2001
+From: Vincent Rabaud <vrabaud@google.com>
+Date: Wed, 22 Jan 2025 12:47:28 +0100
+Subject: [PATCH 03/10] Merge pull request #26782 from vrabaud:png_leak
+
+Fix potential READ memory access #26782
+
+This fixes https://oss-fuzz.com/testcase-detail/4923671881252864 and https://oss-fuzz.com/testcase-detail/5048650127966208
+
+### Pull Request Readiness Checklist
+
+See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
+
+- [x] I agree to contribute to the project under Apache 2 License.
+- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
+- [x] The PR is proposed to the proper branch
+- [x] There is a reference to the original bug report and related work
+- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
+      Patch to opencv_extra has the same branch name.
+- [ ] The feature is well documented and sample code can be built with the project CMake
+---
+ modules/imgcodecs/src/grfmt_png.cpp | 394 ++++++++++++++--------------
+ modules/imgcodecs/src/grfmt_png.hpp |  53 +---
+ 2 files changed, 206 insertions(+), 241 deletions(-)
+
+diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp
+index 1ecc01f17f..105288c5e5 100644
+--- a/modules/imgcodecs/src/grfmt_png.cpp
++++ b/modules/imgcodecs/src/grfmt_png.cpp
+@@ -198,6 +198,7 @@ PngDecoder::PngDecoder()
+ 
+ PngDecoder::~PngDecoder()
+ {
++    ClearPngPtr();
+     if( m_f )
+     {
+         fclose( m_f );
+@@ -205,6 +206,26 @@ PngDecoder::~PngDecoder()
+     }
+ }
+ 
++bool PngDecoder::InitPngPtr() {
++    ClearPngPtr();
++
++    m_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
++    if (!m_png_ptr)
++        return false;
++
++    m_info_ptr = png_create_info_struct(m_png_ptr);
++    m_end_info = png_create_info_struct(m_png_ptr);
++    return (m_info_ptr && m_end_info);
++}
++
++void PngDecoder::ClearPngPtr() {
++    if (m_png_ptr)
++        png_destroy_read_struct(&m_png_ptr, &m_info_ptr, &m_end_info);
++    m_png_ptr = nullptr;
++    m_info_ptr = nullptr;
++    m_end_info = nullptr;
++}
++
+ ImageDecoder PngDecoder::newDecoder() const
+ {
+     return makePtr<PngDecoder>();
+@@ -227,167 +248,164 @@ void  PngDecoder::readDataFromBuf( void* _png_ptr, unsigned char* dst, size_t si
+ 
+ bool  PngDecoder::readHeader()
+ {
+-    volatile bool result = false;
++    // Declare dynamic variables before a potential longjmp.
++    Chunk chunk;
++
++    if (!InitPngPtr())
++        return false;
++
++    if (setjmp(png_jmpbuf(m_png_ptr)))
++        return false;
+ 
+-    PngPtrs png_ptrs;
+-    png_structp png_ptr = png_ptrs.getPng();
+-    png_infop info_ptr = png_ptrs.getInfo();
+-    png_infop end_info = png_ptrs.getEndInfo();
++    m_buf_pos = 0;
++    unsigned char sig[8];
++    uint32_t id = 0;
+ 
+-    if( png_ptr && info_ptr && end_info )
++    if( !m_buf.empty() )
++        png_set_read_fn(m_png_ptr, this, (png_rw_ptr)readDataFromBuf );
++    else
+     {
+-        m_buf_pos = 0;
+-        if( setjmp( png_jmpbuf( png_ptr ) ) == 0 )
++        m_f = fopen(m_filename.c_str(), "rb");
++        if (!m_f)
+         {
+-            unsigned char sig[8];
+-            uint32_t id = 0;
+-            Chunk chunk;
++            return false;
++        }
++        png_init_io(m_png_ptr, m_f);
++    }
+ 
+-            if( !m_buf.empty() )
+-                png_set_read_fn(png_ptr, this, (png_rw_ptr)readDataFromBuf );
+-            else
+-            {
+-                m_f = fopen(m_filename.c_str(), "rb");
+-                if (!m_f)
+-                {
+-                    return false;
+-                }
+-                png_init_io(png_ptr, m_f);
+-            }
++    // Read PNG header: 137 80 78 71 13 10 26 10
++    if (!read_from_io(&sig, 8))
++        return false;
+ 
+-            // Read PNG header: 137 80 78 71 13 10 26 10
+-            if (!read_from_io(&sig, 8))
+-                return false;
++    id = read_chunk(m_chunkIHDR);
++    // 8=HDR+size, 13=size of IHDR chunk, 4=CRC
++    // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR
++    if (!(id == id_IHDR && m_chunkIHDR.p.size() == 8 + 13 + 4))
++    {
++        return false;
++    }
+ 
+-            id = read_chunk(m_chunkIHDR);
+-            // 8=HDR+size, 13=size of IHDR chunk, 4=CRC
+-            // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR
+-            if (!(id == id_IHDR && m_chunkIHDR.p.size() == 8 + 13 + 4))
+-            {
+-                return false;
+-            }
++    m_is_fcTL_loaded = false;
++    while (true)
++    {
++        id = read_chunk(chunk);
+ 
+-            while (true)
+-            {
+-                m_is_fcTL_loaded = false;
+-                id = read_chunk(chunk);
++        if (!id || (m_f && feof(m_f)) || (!m_buf.empty() && m_buf_pos > m_buf.total()))
++        {
++            return false;
++        }
+ 
+-                if (!id || (m_f && feof(m_f)) || (!m_buf.empty() && m_buf_pos > m_buf.total()))
+-                {
+-                    return false;
+-                }
++        if (id == id_IDAT)
++        {
++            if (m_f)
++                fseek(m_f, 0, SEEK_SET);
++            else
++                m_buf_pos = 0;
++            break;
++        }
+ 
+-                if (id == id_IDAT)
+-                {
+-                    if (m_f)
+-                        fseek(m_f, 0, SEEK_SET);
+-                    else
+-                        m_buf_pos = 0;
+-                    break;
+-                }
++        if (id == id_acTL)
++        {
++            // 8=HDR+size, 8=size of acTL chunk, 4=CRC
++            // https://wiki.mozilla.org/APNG_Specification#%60acTL%60:_The_Animation_Control_Chunk
++            if (chunk.p.size() != 8 + 8 + 4)
++                return false;
++            m_animation.loop_count = png_get_uint_32(&chunk.p[12]);
+ 
+-                if (id == id_acTL)
+-                {
+-                    // 8=HDR+size, 8=size of acTL chunk, 4=CRC
+-                    // https://wiki.mozilla.org/APNG_Specification#%60acTL%60:_The_Animation_Control_Chunk
+-                    if (chunk.p.size() != 8 + 8 + 4)
+-                        return false;
+-                    m_animation.loop_count = png_get_uint_32(&chunk.p[12]);
+-
+-                    m_frame_count = png_get_uint_32(&chunk.p[8]);
+-                    if (m_frame_count == 0)
+-                        return false;
+-                }
++            m_frame_count = png_get_uint_32(&chunk.p[8]);
++            if (m_frame_count == 0)
++                return false;
++        }
+ 
+-                if (id == id_fcTL)
+-                {
+-                    // 8=HDR+size, 26=size of fcTL chunk, 4=CRC
+-                    // https://wiki.mozilla.org/APNG_Specification#%60fcTL%60:_The_Frame_Control_Chunk
+-                    if (chunk.p.size() != 8 + 26 + 4)
+-                        return false;
+-                    m_is_fcTL_loaded = true;
+-                    w0 = png_get_uint_32(&chunk.p[12]);
+-                    h0 = png_get_uint_32(&chunk.p[16]);
+-                    x0 = png_get_uint_32(&chunk.p[20]);
+-                    y0 = png_get_uint_32(&chunk.p[24]);
+-                    delay_num = png_get_uint_16(&chunk.p[28]);
+-                    delay_den = png_get_uint_16(&chunk.p[30]);
+-                    dop = chunk.p[32];
+-                    bop = chunk.p[33];
+-                }
++        if (id == id_fcTL)
++        {
++            // 8=HDR+size, 26=size of fcTL chunk, 4=CRC
++            // https://wiki.mozilla.org/APNG_Specification#%60fcTL%60:_The_Frame_Control_Chunk
++            if (chunk.p.size() != 8 + 26 + 4)
++                return false;
++            m_is_fcTL_loaded = true;
++            w0 = png_get_uint_32(&chunk.p[12]);
++            h0 = png_get_uint_32(&chunk.p[16]);
++            x0 = png_get_uint_32(&chunk.p[20]);
++            y0 = png_get_uint_32(&chunk.p[24]);
++            delay_num = png_get_uint_16(&chunk.p[28]);
++            delay_den = png_get_uint_16(&chunk.p[30]);
++            dop = chunk.p[32];
++            bop = chunk.p[33];
++        }
+ 
+-                if (id == id_bKGD)
+-                {
+-                    // 8=HDR+size, ??=size of bKGD chunk, 4=CRC
+-                    // The spec is actually more complex: http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.bKGD
+-                    // TODO: we only check that 4 bytes can be read from &chunk.p[8]. Fix.
+-                    if (chunk.p.size() < 8 + 4)
+-                        return false;
+-                    int bgcolor = png_get_uint_32(&chunk.p[8]);
+-                    m_animation.bgcolor[3] = (bgcolor >> 24) & 0xFF;
+-                    m_animation.bgcolor[2] = (bgcolor >> 16) & 0xFF;
+-                    m_animation.bgcolor[1] = (bgcolor >> 8) & 0xFF;
+-                    m_animation.bgcolor[0] = bgcolor & 0xFF;
+-                }
++        if (id == id_bKGD)
++        {
++            // 8=HDR+size, ??=size of bKGD chunk, 4=CRC
++            // The spec is actually more complex: http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.bKGD
++            // TODO: we only check that 4 bytes can be read from &chunk.p[8]. Fix.
++            if (chunk.p.size() < 8 + 4)
++                return false;
++            int bgcolor = png_get_uint_32(&chunk.p[8]);
++            m_animation.bgcolor[3] = (bgcolor >> 24) & 0xFF;
++            m_animation.bgcolor[2] = (bgcolor >> 16) & 0xFF;
++            m_animation.bgcolor[1] = (bgcolor >> 8) & 0xFF;
++            m_animation.bgcolor[0] = bgcolor & 0xFF;
++        }
+ 
+-                if (id == id_PLTE || id == id_tRNS)
+-                    m_chunksInfo.push_back(chunk);
+-            }
++        if (id == id_PLTE || id == id_tRNS)
++            m_chunksInfo.push_back(chunk);
++    }
+ 
+-            png_uint_32 wdth, hght;
+-            int bit_depth, color_type, num_trans=0;
+-            png_bytep trans;
+-            png_color_16p trans_values;
++    png_uint_32 wdth, hght;
++    int bit_depth, color_type, num_trans=0;
++    png_bytep trans;
++    png_color_16p trans_values;
+ 
+-            // Free chunk in case png_read_info uses longjmp.
+-            chunk.p.clear();
+-            chunk.p.shrink_to_fit();
++    // Free chunk in case png_read_info uses longjmp.
++    chunk.p.clear();
++    chunk.p.shrink_to_fit();
+ 
+-            png_read_info( png_ptr, info_ptr );
+-            png_get_IHDR(png_ptr, info_ptr, &wdth, &hght,
+-                &bit_depth, &color_type, 0, 0, 0);
++    png_read_info( m_png_ptr, m_info_ptr );
++    png_get_IHDR(m_png_ptr, m_info_ptr, &wdth, &hght,
++        &bit_depth, &color_type, 0, 0, 0);
+ 
+-            m_width = (int)wdth;
+-            m_height = (int)hght;
+-            m_color_type = color_type;
+-            m_bit_depth = bit_depth;
++    m_width = (int)wdth;
++    m_height = (int)hght;
++    m_color_type = color_type;
++    m_bit_depth = bit_depth;
+ 
+-            if (bit_depth <= 8 || bit_depth == 16)
+-            {
+-                switch (color_type)
+-                {
+-                case PNG_COLOR_TYPE_RGB:
+-                case PNG_COLOR_TYPE_PALETTE:
+-                    png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, &trans_values);
+-                    if (num_trans > 0)
+-                        m_type = CV_8UC4;
+-                    else
+-                        m_type = CV_8UC3;
+-                    break;
+-                case PNG_COLOR_TYPE_GRAY_ALPHA:
+-                case PNG_COLOR_TYPE_RGB_ALPHA:
+-                    m_type = CV_8UC4;
+-                    break;
+-                default:
+-                    m_type = CV_8UC1;
+-                }
+-                if (bit_depth == 16)
+-                    m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type));
+-                result = true;
+-            }
+-        }
+-    }
++    if (m_is_fcTL_loaded && (int(x0 + w0) > m_width || int(y0 + h0) > m_height || dop > 2 || bop > 1))
++        return false;
+ 
+-    if(result)
++    if (bit_depth <= 8 || bit_depth == 16)
+     {
+-        m_png_ptrs = std::move(png_ptrs);
++        switch (color_type)
++        {
++        case PNG_COLOR_TYPE_RGB:
++        case PNG_COLOR_TYPE_PALETTE:
++            png_get_tRNS(m_png_ptr, m_info_ptr, &trans, &num_trans, &trans_values);
++            if (num_trans > 0)
++                m_type = CV_8UC4;
++            else
++                m_type = CV_8UC3;
++            break;
++        case PNG_COLOR_TYPE_GRAY_ALPHA:
++        case PNG_COLOR_TYPE_RGB_ALPHA:
++            m_type = CV_8UC4;
++            break;
++        default:
++            m_type = CV_8UC1;
++        }
++        if (bit_depth == 16)
++            m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type));
+     }
+ 
+-    return result;
++    return true;
+ }
+ 
+ bool  PngDecoder::readData( Mat& img )
+ {
++    // Declare dynamic variables before a potential longjmp.
++    AutoBuffer<unsigned char*> _buffer(m_height);
++    unsigned char** buffer = _buffer.data();
++    Chunk chunk;
++
+     if (m_frame_count > 1)
+     {
+         Mat mat_cur = Mat::zeros(img.rows, img.cols, m_type);
+@@ -412,13 +430,14 @@ bool  PngDecoder::readData( Mat& img )
+ 
+         frameCur.setMat(mat_cur);
+ 
+-        processing_start((void*)&frameRaw, mat_cur);
+-        png_structp png_ptr = m_png_ptrs.getPng();
+-        png_infop info_ptr = m_png_ptrs.getInfo();
++        if (!processing_start((void*)&frameRaw, mat_cur))
++            return false;
++
++        if(setjmp(png_jmpbuf(m_png_ptr)))
++            return false;
+ 
+         while (true)
+         {
+-            Chunk chunk;
+             id = read_chunk(chunk);
+             if (!id)
+                 return false;
+@@ -482,14 +501,14 @@ bool  PngDecoder::readData( Mat& img )
+             else if (id == id_IDAT)
+             {
+                 m_is_IDAT_loaded = true;
+-                png_process_data(png_ptr, info_ptr, chunk.p.data(), chunk.p.size());
++                png_process_data(m_png_ptr, m_info_ptr, chunk.p.data(), chunk.p.size());
+             }
+             else if (id == id_fdAT && m_is_fcTL_loaded)
+             {
+                 m_is_IDAT_loaded = true;
+                 png_save_uint_32(&chunk.p[4], static_cast<uint32_t>(chunk.p.size() - 16));
+                 memcpy(&chunk.p[8], "IDAT", 4);
+-                png_process_data(png_ptr, info_ptr, &chunk.p[4], chunk.p.size() - 4);
++                png_process_data(m_png_ptr, m_info_ptr, &chunk.p[4], chunk.p.size() - 4);
+             }
+             else if (id == id_IEND)
+             {
+@@ -513,30 +532,24 @@ bool  PngDecoder::readData( Mat& img )
+                 return true;
+             }
+             else
+-                png_process_data(png_ptr, info_ptr, chunk.p.data(), chunk.p.size());
++                png_process_data(m_png_ptr, m_info_ptr, chunk.p.data(), chunk.p.size());
+         }
+         return false;
+     }
+ 
+     volatile bool result = false;
+-    AutoBuffer<unsigned char*> _buffer(m_height);
+-    unsigned char** buffer = _buffer.data();
+     bool color = img.channels() > 1;
+ 
+-    png_structp png_ptr = m_png_ptrs.getPng();
+-    png_infop info_ptr = m_png_ptrs.getInfo();
+-    png_infop end_info = m_png_ptrs.getEndInfo();
+-
+-    if( png_ptr && info_ptr && end_info && m_width && m_height )
++    if( m_png_ptr && m_info_ptr && m_end_info && m_width && m_height )
+     {
+-        if( setjmp( png_jmpbuf ( png_ptr ) ) == 0 )
++        if( setjmp( png_jmpbuf ( m_png_ptr ) ) == 0 )
+         {
+             int y;
+ 
+             if( img.depth() == CV_8U && m_bit_depth == 16 )
+-                png_set_strip_16( png_ptr );
++                png_set_strip_16( m_png_ptr );
+             else if( !isBigEndian() )
+-                png_set_swap( png_ptr );
++                png_set_swap( m_png_ptr );
+ 
+             if(img.channels() < 4)
+             {
+@@ -548,46 +561,46 @@ bool  PngDecoder::readData( Mat& img )
+                  * indicate that it is a good idea to always ask for
+                  * stripping alpha..  18.11.2004 Axel Walthelm
+                  */
+-                 png_set_strip_alpha( png_ptr );
++                 png_set_strip_alpha( m_png_ptr );
+             } else
+-                png_set_tRNS_to_alpha( png_ptr );
++                png_set_tRNS_to_alpha( m_png_ptr );
+ 
+             if( m_color_type == PNG_COLOR_TYPE_PALETTE )
+-                png_set_palette_to_rgb( png_ptr );
++                png_set_palette_to_rgb( m_png_ptr );
+ 
+             if( (m_color_type & PNG_COLOR_MASK_COLOR) == 0 && m_bit_depth < 8 )
+ #if (PNG_LIBPNG_VER_MAJOR*10000 + PNG_LIBPNG_VER_MINOR*100 + PNG_LIBPNG_VER_RELEASE >= 10209) || \
+     (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR == 0 && PNG_LIBPNG_VER_RELEASE >= 18)
+-                png_set_expand_gray_1_2_4_to_8( png_ptr );
++                png_set_expand_gray_1_2_4_to_8( m_png_ptr );
+ #else
+                 png_set_gray_1_2_4_to_8( png_ptr );
+ #endif
+ 
+             if( (m_color_type & PNG_COLOR_MASK_COLOR) && color && !m_use_rgb)
+-                png_set_bgr( png_ptr ); // convert RGB to BGR
++                png_set_bgr( m_png_ptr ); // convert RGB to BGR
+             else if( color )
+-                png_set_gray_to_rgb( png_ptr ); // Gray->RGB
++                png_set_gray_to_rgb( m_png_ptr ); // Gray->RGB
+             else
+-                png_set_rgb_to_gray( png_ptr, 1, 0.299, 0.587 ); // RGB->Gray
++                png_set_rgb_to_gray( m_png_ptr, 1, 0.299, 0.587 ); // RGB->Gray
+ 
+-            png_set_interlace_handling( png_ptr );
+-            png_read_update_info( png_ptr, info_ptr );
++            png_set_interlace_handling( m_png_ptr );
++            png_read_update_info( m_png_ptr, m_info_ptr );
+ 
+             for( y = 0; y < m_height; y++ )
+                 buffer[y] = img.data + y*img.step;
+ 
+-            png_read_image( png_ptr, buffer );
+-            png_read_end( png_ptr, end_info );
++            png_read_image( m_png_ptr, buffer );
++            png_read_end( m_png_ptr, m_end_info );
+ 
+ #ifdef PNG_eXIf_SUPPORTED
+             png_uint_32 num_exif = 0;
+             png_bytep exif = 0;
+ 
+             // Exif info could be in info_ptr (intro_info) or end_info per specification
+-            if( png_get_valid(png_ptr, info_ptr, PNG_INFO_eXIf) )
+-                png_get_eXIf_1(png_ptr, info_ptr, &num_exif, &exif);
+-            else if( png_get_valid(png_ptr, end_info, PNG_INFO_eXIf) )
+-                png_get_eXIf_1(png_ptr, end_info, &num_exif, &exif);
++            if( png_get_valid(m_png_ptr, m_info_ptr, PNG_INFO_eXIf) )
++                png_get_eXIf_1(m_png_ptr, m_info_ptr, &num_exif, &exif);
++            else if( png_get_valid(m_png_ptr, m_end_info, PNG_INFO_eXIf) )
++                png_get_eXIf_1(m_png_ptr, m_end_info, &num_exif, &exif);
+ 
+             if( exif && num_exif > 0 )
+             {
+@@ -719,42 +732,34 @@ uint32_t PngDecoder::read_chunk(Chunk& chunk)
+ 
+ bool PngDecoder::processing_start(void* frame_ptr, const Mat& img)
+ {
+-    static uint8_t header[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
+-
+-    PngPtrs png_ptrs;
+-    png_structp png_ptr = png_ptrs.getPng();
+-    png_infop info_ptr = png_ptrs.getInfo();
+-
+-    if (!png_ptr || !info_ptr) {
++    if (!InitPngPtr())
+         return false;
+-    }
+ 
+-    if (setjmp(png_jmpbuf(png_ptr)))
+-    {
++    if (setjmp(png_jmpbuf(m_png_ptr)))
+         return false;
+-    }
+ 
+-    m_png_ptrs = std::move(png_ptrs);
+-    png_set_crc_action(png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
+-    png_set_progressive_read_fn(png_ptr, frame_ptr, (png_progressive_info_ptr)info_fn, row_fn, NULL);
++    static uint8_t header[8] = { 137, 80, 78, 71, 13, 10, 26, 10 };
++
++    png_set_crc_action(m_png_ptr, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
++    png_set_progressive_read_fn(m_png_ptr, frame_ptr, (png_progressive_info_ptr)info_fn, row_fn, NULL);
+ 
+     if (img.channels() < 4)
+-        png_set_strip_alpha(png_ptr);
++        png_set_strip_alpha(m_png_ptr);
+     else
+-        png_set_tRNS_to_alpha(png_ptr);
++        png_set_tRNS_to_alpha(m_png_ptr);
+ 
+-    png_process_data(png_ptr, info_ptr, header, 8);
+-    png_process_data(png_ptr, info_ptr, m_chunkIHDR.p.data(), m_chunkIHDR.p.size());
++    png_process_data(m_png_ptr, m_info_ptr, header, 8);
++    png_process_data(m_png_ptr, m_info_ptr, m_chunkIHDR.p.data(), m_chunkIHDR.p.size());
+ 
+     if ((m_color_type & PNG_COLOR_MASK_COLOR) && img.channels() > 1 && !m_use_rgb)
+-        png_set_bgr(png_ptr); // convert RGB to BGR
++        png_set_bgr(m_png_ptr); // convert RGB to BGR
+     else if (img.channels() > 1)
+-        png_set_gray_to_rgb(png_ptr); // Gray->RGB
++        png_set_gray_to_rgb(m_png_ptr); // Gray->RGB
+     else
+-        png_set_rgb_to_gray(png_ptr, 1, 0.299, 0.587); // RGB->Gray
++        png_set_rgb_to_gray(m_png_ptr, 1, 0.299, 0.587); // RGB->Gray
+ 
+     for (size_t i = 0; i < m_chunksInfo.size(); i++)
+-        png_process_data(png_ptr, info_ptr, m_chunksInfo[i].p.data(), m_chunksInfo[i].p.size());
++        png_process_data(m_png_ptr, m_info_ptr, m_chunksInfo[i].p.data(), m_chunksInfo[i].p.size());
+ 
+     return true;
+ }
+@@ -763,22 +768,17 @@ bool PngDecoder::processing_finish()
+ {
+     static uint8_t footer[12] = { 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130 };
+ 
+-    png_structp png_ptr = m_png_ptrs.getPng();
+-    png_infop info_ptr = m_png_ptrs.getInfo();
+-
+-    if (!png_ptr) {
+-        m_png_ptrs.clear();
++    if (!m_png_ptr) {
+         return false;
+     }
+ 
+-    if (setjmp(png_jmpbuf(png_ptr)))
++    if (setjmp(png_jmpbuf(m_png_ptr)))
+     {
+-        m_png_ptrs.clear();
+         return false;
+     }
+ 
+-    png_process_data(png_ptr, info_ptr, footer, 12);
+-    m_png_ptrs.clear();
++    png_process_data(m_png_ptr, m_info_ptr, footer, 12);
++    ClearPngPtr();
+ 
+     return true;
+ }
+diff --git a/modules/imgcodecs/src/grfmt_png.hpp b/modules/imgcodecs/src/grfmt_png.hpp
+index dec2cd0b61..5dfc86efcc 100644
+--- a/modules/imgcodecs/src/grfmt_png.hpp
++++ b/modules/imgcodecs/src/grfmt_png.hpp
+@@ -130,56 +130,21 @@ public:
+ 
+     ImageDecoder newDecoder() const CV_OVERRIDE;
+ 
+-protected:
++private:
+     static void readDataFromBuf(void* png_ptr, uchar* dst, size_t size);
+     static void info_fn(png_structp png_ptr, png_infop info_ptr);
+     static void row_fn(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass);
+-    bool processing_start(void* frame_ptr, const Mat& img);
+-    bool processing_finish();
++    CV_NODISCARD_STD bool processing_start(void* frame_ptr, const Mat& img);
++    CV_NODISCARD_STD bool processing_finish();
+     void compose_frame(std::vector<png_bytep>& rows_dst, const std::vector<png_bytep>& rows_src, unsigned char bop, uint32_t x, uint32_t y, uint32_t w, uint32_t h, Mat& img);
+-    bool read_from_io(void* buffer, size_t num_bytes);
++    CV_NODISCARD_STD bool read_from_io(void* buffer, size_t num_bytes);
+     uint32_t  read_chunk(Chunk& chunk);
++    CV_NODISCARD_STD bool InitPngPtr();
++    void ClearPngPtr();
+ 
+-    struct PngPtrs {
+-        public:
+-            PngPtrs() {
+-                png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
+-                if (png_ptr) {
+-                    info_ptr = png_create_info_struct( png_ptr );
+-                    end_info = png_create_info_struct( png_ptr );
+-                } else {
+-                    info_ptr = end_info = nullptr;
+-                }
+-            }
+-            ~PngPtrs() {
+-                clear();
+-            }
+-            PngPtrs& operator=(PngPtrs&& other) {
+-                clear();
+-                png_ptr = other.png_ptr;
+-                info_ptr = other.info_ptr;
+-                end_info = other.end_info;
+-                other.png_ptr = nullptr;
+-                other.info_ptr = other.end_info = nullptr;
+-                return *this;
+-            }
+-            void clear() {
+-                if (png_ptr) {
+-                    png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+-                    png_ptr = nullptr;
+-                    info_ptr = end_info = nullptr;
+-                }
+-            }
+-            png_structp getPng() const { return png_ptr; }
+-            png_infop getInfo() const { return info_ptr; }
+-            png_infop getEndInfo() const { return end_info; }
+-        private:
+-            png_structp png_ptr; // pointer to decompression structure
+-            png_infop info_ptr; // pointer to image information structure
+-            png_infop end_info; // pointer to one more image information structure
+-    };
+-
+-    PngPtrs m_png_ptrs;
++    png_structp m_png_ptr = nullptr; // pointer to decompression structure
++    png_infop m_info_ptr = nullptr; // pointer to image information structure
++    png_infop m_end_info = nullptr; // pointer to one more image information structure
+     int   m_bit_depth;
+     FILE* m_f;
+     int   m_color_type;
+-- 
+2.48.1
+

diff --git a/0004-Move-the-checks-to-read_chunk.patch b/0004-Move-the-checks-to-read_chunk.patch
new file mode 100644
index 0000000..5137872
--- /dev/null
+++ b/0004-Move-the-checks-to-read_chunk.patch
@@ -0,0 +1,121 @@
+From eba1a8955f9d7f8aa8c6b3ddee4565ee4185136c Mon Sep 17 00:00:00 2001
+From: Vincent Rabaud <vrabaud@google.com>
+Date: Thu, 23 Jan 2025 16:30:38 +0100
+Subject: [PATCH 04/10] Move the checks to read_chunk.
+
+Only user chunks need to be compared to PNG_USER_CHUNK_MALLOC_MAX
+---
+ modules/imgcodecs/src/grfmt_png.cpp | 60 +++++++++++++++++------------
+ 1 file changed, 36 insertions(+), 24 deletions(-)
+
+diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp
+index 105288c5e5..64ef56c8c5 100644
+--- a/modules/imgcodecs/src/grfmt_png.cpp
++++ b/modules/imgcodecs/src/grfmt_png.cpp
+@@ -278,12 +278,8 @@ bool  PngDecoder::readHeader()
+         return false;
+ 
+     id = read_chunk(m_chunkIHDR);
+-    // 8=HDR+size, 13=size of IHDR chunk, 4=CRC
+-    // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR
+-    if (!(id == id_IHDR && m_chunkIHDR.p.size() == 8 + 13 + 4))
+-    {
++    if (id != id_IHDR)
+         return false;
+-    }
+ 
+     m_is_fcTL_loaded = false;
+     while (true)
+@@ -306,10 +302,7 @@ bool  PngDecoder::readHeader()
+ 
+         if (id == id_acTL)
+         {
+-            // 8=HDR+size, 8=size of acTL chunk, 4=CRC
+             // https://wiki.mozilla.org/APNG_Specification#%60acTL%60:_The_Animation_Control_Chunk
+-            if (chunk.p.size() != 8 + 8 + 4)
+-                return false;
+             m_animation.loop_count = png_get_uint_32(&chunk.p[12]);
+ 
+             m_frame_count = png_get_uint_32(&chunk.p[8]);
+@@ -319,10 +312,7 @@ bool  PngDecoder::readHeader()
+ 
+         if (id == id_fcTL)
+         {
+-            // 8=HDR+size, 26=size of fcTL chunk, 4=CRC
+             // https://wiki.mozilla.org/APNG_Specification#%60fcTL%60:_The_Frame_Control_Chunk
+-            if (chunk.p.size() != 8 + 26 + 4)
+-                return false;
+             m_is_fcTL_loaded = true;
+             w0 = png_get_uint_32(&chunk.p[12]);
+             h0 = png_get_uint_32(&chunk.p[16]);
+@@ -336,11 +326,7 @@ bool  PngDecoder::readHeader()
+ 
+         if (id == id_bKGD)
+         {
+-            // 8=HDR+size, ??=size of bKGD chunk, 4=CRC
+             // The spec is actually more complex: http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.bKGD
+-            // TODO: we only check that 4 bytes can be read from &chunk.p[8]. Fix.
+-            if (chunk.p.size() < 8 + 4)
+-                return false;
+             int bgcolor = png_get_uint_32(&chunk.p[8]);
+             m_animation.bgcolor[3] = (bgcolor >> 24) & 0xFF;
+             m_animation.bgcolor[2] = (bgcolor >> 16) & 0xFF;
+@@ -713,20 +699,46 @@ bool PngDecoder::read_from_io(void* buffer, size_t num_bytes)
+ 
+ uint32_t PngDecoder::read_chunk(Chunk& chunk)
+ {
+-    unsigned char len[4];
+-    if (read_from_io(&len, 4))
+-    {
+-        const size_t size = static_cast<size_t>(png_get_uint_32(len)) + 12;
++    unsigned char size_id[8];
++    if (!read_from_io(&size_id, 8))
++        return 0;
++    const size_t size = static_cast<size_t>(png_get_uint_32(size_id)) + 12;
++
++    const uint32_t id = *(uint32_t*)(&size_id[4]);
++    if (id == id_IHDR) {
++        // 8=HDR+size, 13=size of IHDR chunk, 4=CRC
++        // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR
++        if (size != 8 + 13 + 4)
++            return 0;
++    } else if (id == id_acTL) {
++        // 8=HDR+size, 8=size of acTL chunk, 4=CRC
++        // https://wiki.mozilla.org/APNG_Specification#%60acTL%60:_The_Animation_Control_Chunk
++        if (size != 8 + 8 + 4)
++            return 0;
++    } else if (id == id_fcTL) {
++        // 8=HDR+size, 26=size of fcTL chunk, 4=CRC
++        // https://wiki.mozilla.org/APNG_Specification#%60fcTL%60:_The_Frame_Control_Chunk
++        if (size != 8 + 26 + 4)
++            return 0;
++    } else if (id == id_bKGD) {
++        // 8=HDR+size, ??=size of bKGD chunk, 4=CRC
++        // The spec is actually more complex:
++        // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.bKGD
++        // TODO: we only check that 4 bytes can be read from &chunk.p[8]. Fix.
++        if (size < 8 + 4)
++            return 0;
++    } else if (id != id_fdAT && id != id_IDAT && id != id_IEND && id != id_PLTE && id != id_tRNS) {
+         if (size > PNG_USER_CHUNK_MALLOC_MAX)
+         {
+-            CV_LOG_WARNING(NULL, "chunk data is too large");
++            CV_LOG_WARNING(NULL, "user chunk data is too large");
+             return 0;
+         }
+-        chunk.p.resize(size);
+-        memcpy(chunk.p.data(), len, 4);
+-        if (read_from_io(&chunk.p[4], chunk.p.size() - 4))
+-            return *(uint32_t*)(&chunk.p[4]);
+     }
++
++    chunk.p.resize(size);
++    memcpy(chunk.p.data(), size_id, 8);
++    if (read_from_io(&chunk.p[8], chunk.p.size() - 8))
++        return id;
+     return 0;
+ }
+ 
+-- 
+2.48.1
+

diff --git a/0005-minor-improvement-for-better-code-readibility.patch b/0005-minor-improvement-for-better-code-readibility.patch
new file mode 100644
index 0000000..c677c33
--- /dev/null
+++ b/0005-minor-improvement-for-better-code-readibility.patch
@@ -0,0 +1,170 @@
+From 49c3a5eca58276daca4207c38a1579080aade23e Mon Sep 17 00:00:00 2001
+From: Suleyman TURKMEN <sturkmen@hotmail.com>
+Date: Fri, 24 Jan 2025 15:31:53 +0300
+Subject: [PATCH 05/10] minor improvement for better code readibility
+
+---
+ modules/imgcodecs/src/grfmt_png.cpp | 41 ++++++++++++++++++-----------
+ modules/imgcodecs/src/grfmt_png.hpp | 27 +++++++++++++++++--
+ 2 files changed, 50 insertions(+), 18 deletions(-)
+
+diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp
+index 64ef56c8c5..08e37ec0c3 100644
+--- a/modules/imgcodecs/src/grfmt_png.cpp
++++ b/modules/imgcodecs/src/grfmt_png.cpp
+@@ -274,7 +274,7 @@ bool  PngDecoder::readHeader()
+     }
+ 
+     // Read PNG header: 137 80 78 71 13 10 26 10
+-    if (!read_from_io(&sig, 8))
++    if (!readFromStreamOrBuffer(&sig, 8))
+         return false;
+ 
+     id = read_chunk(m_chunkIHDR);
+@@ -682,7 +682,7 @@ void PngDecoder::compose_frame(std::vector<png_bytep>& rows_dst, const std::vect
+             });
+ }
+ 
+-bool PngDecoder::read_from_io(void* buffer, size_t num_bytes)
++bool PngDecoder::readFromStreamOrBuffer(void* buffer, size_t num_bytes)
+ {
+     if (m_f)
+         return fread(buffer, 1, num_bytes, m_f) == num_bytes;
+@@ -700,7 +700,7 @@ bool PngDecoder::read_from_io(void* buffer, size_t num_bytes)
+ uint32_t PngDecoder::read_chunk(Chunk& chunk)
+ {
+     unsigned char size_id[8];
+-    if (!read_from_io(&size_id, 8))
++    if (!readFromStreamOrBuffer(&size_id, 8))
+         return 0;
+     const size_t size = static_cast<size_t>(png_get_uint_32(size_id)) + 12;
+ 
+@@ -737,7 +737,7 @@ uint32_t PngDecoder::read_chunk(Chunk& chunk)
+ 
+     chunk.p.resize(size);
+     memcpy(chunk.p.data(), size_id, 8);
+-    if (read_from_io(&chunk.p[8], chunk.p.size() - 8))
++    if (readFromStreamOrBuffer(&chunk.p[8], chunk.p.size() - 8))
+         return id;
+     return 0;
+ }
+@@ -960,15 +960,24 @@ bool  PngEncoder::write( const Mat& img, const std::vector<int>& params )
+     return result;
+ }
+ 
+-size_t PngEncoder::write_to_io(void const* _Buffer, size_t  _ElementSize, size_t _ElementCount, FILE * _Stream)
++size_t PngEncoder::writeToStreamOrBuffer(void const* buffer, size_t num_bytes, FILE* stream)
+ {
+-    if (_Stream)
+-        return fwrite(_Buffer, _ElementSize, _ElementCount, _Stream);
++    if (!buffer || !num_bytes)
++        return 0; // Handle null buffer or empty writes
++
++    if (stream)
++    {
++        size_t written = fwrite(buffer, 1, num_bytes, stream);
++        return written; // fwrite handles the write count
++    }
+ 
+     size_t cursz = m_buf->size();
+-    m_buf->resize(cursz + _ElementCount);
+-    memcpy( &(*m_buf)[cursz], _Buffer, _ElementCount );
+-    return _ElementCount;
++    if (cursz + num_bytes > m_buf->max_size())
++        throw std::runtime_error("Buffer size exceeds maximum capacity");
++
++    m_buf->resize(cursz + num_bytes);
++    memcpy(&(*m_buf)[cursz], buffer, num_bytes);
++    return num_bytes;
+ }
+ 
+ void PngEncoder::writeChunk(FILE* f, const char* name, unsigned char* data, uint32_t length)
+@@ -977,26 +986,26 @@ void PngEncoder::writeChunk(FILE* f, const char* name, unsigned char* data, uint
+     uint32_t crc = crc32(0, Z_NULL, 0);
+ 
+     png_save_uint_32(buf, length);
+-    write_to_io(buf, 1, 4, f);
+-    write_to_io(name, 1, 4, f);
++    writeToStreamOrBuffer(buf, 4, f);
++    writeToStreamOrBuffer(name, 4, f);
+     crc = crc32(crc, (const Bytef*)name, 4);
+ 
+     if (memcmp(name, "fdAT", 4) == 0)
+     {
+         png_save_uint_32(buf, next_seq_num++);
+-        write_to_io(buf, 1, 4, f);
++        writeToStreamOrBuffer(buf, 4, f);
+         crc = crc32(crc, buf, 4);
+         length -= 4;
+     }
+ 
+     if (data != NULL && length > 0)
+     {
+-        write_to_io(data, 1, length, f);
++        writeToStreamOrBuffer(data, length, f);
+         crc = crc32(crc, data, length);
+     }
+ 
+     png_save_uint_32(buf, crc);
+-    write_to_io(buf, 1, 4, f);
++    writeToStreamOrBuffer(buf, 4, f);
+ }
+ 
+ void PngEncoder::writeIDATs(FILE* f, int frame, unsigned char* data, uint32_t length, uint32_t idat_size)
+@@ -1521,7 +1530,7 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
+         png_save_uint_32(buf_acTL, num_frames - first);
+         png_save_uint_32(buf_acTL + 4, loops);
+ 
+-        write_to_io(header, 1, 8, m_f);
++        writeToStreamOrBuffer(header, 8, m_f);
+ 
+         writeChunk(m_f, "IHDR", buf_IHDR, 13);
+ 
+diff --git a/modules/imgcodecs/src/grfmt_png.hpp b/modules/imgcodecs/src/grfmt_png.hpp
+index 5dfc86efcc..6e1a06473d 100644
+--- a/modules/imgcodecs/src/grfmt_png.hpp
++++ b/modules/imgcodecs/src/grfmt_png.hpp
+@@ -137,7 +137,13 @@ private:
+     CV_NODISCARD_STD bool processing_start(void* frame_ptr, const Mat& img);
+     CV_NODISCARD_STD bool processing_finish();
+     void compose_frame(std::vector<png_bytep>& rows_dst, const std::vector<png_bytep>& rows_src, unsigned char bop, uint32_t x, uint32_t y, uint32_t w, uint32_t h, Mat& img);
+-    CV_NODISCARD_STD bool read_from_io(void* buffer, size_t num_bytes);
++    /**
++     * @brief Reads data from an I/O source into the provided buffer.
++     * @param buffer Pointer to the buffer where the data will be stored.
++     * @param num_bytes Number of bytes to read into the buffer.
++     * @return true if the operation is successful, false otherwise.
++     */
++    CV_NODISCARD_STD bool readFromStreamOrBuffer(void* buffer, size_t num_bytes);
+     uint32_t  read_chunk(Chunk& chunk);
+     CV_NODISCARD_STD bool InitPngPtr();
+     void ClearPngPtr();
+@@ -185,7 +191,24 @@ public:
+ protected:
+     static void writeDataToBuf(void* png_ptr, unsigned char* src, size_t size);
+     static void flushBuf(void* png_ptr);
+-    size_t write_to_io(void const* _Buffer, size_t  _ElementSize, size_t _ElementCount, FILE* _Stream);
++    /**
++    * @brief Writes data to an output destination, either a file stream or an in-memory buffer.
++    *
++    * This function handles two output scenarios:
++    * 1. If a file stream is provided, the data is written to the stream using `fwrite`.
++    * 2. If `stream` is null, the data is written to an in-memory buffer (`m_buf`), which is resized as needed.
++    *
++    * @param buffer Pointer to the data to be written.
++    * @param num_bytes The number of bytes to be written.
++    * @param stream Pointer to the file stream for writing. If null, the data is written to the in-memory buffer.
++    * @return The number of bytes successfully written.
++    *         - For file-based writes, this is the number of bytes written to the stream.
++    *         - For buffer-based writes, this is the total number of bytes added to the buffer.
++    *
++    * @throws std::runtime_error If the in-memory buffer (`m_buf`) exceeds its maximum capacity.
++    * @note If `num_bytes` is 0 or `buffer` is null, the function returns 0.
++    */
++    size_t writeToStreamOrBuffer(void const* buffer, size_t  num_bytes, FILE* stream);
+ 
+ private:
+     void writeChunk(FILE* f, const char* name, unsigned char* data, uint32_t length);
+-- 
+2.48.1
+

diff --git a/0006-Merge-pull-request-26835-from-sturkmen72-patch-4.patch b/0006-Merge-pull-request-26835-from-sturkmen72-patch-4.patch
new file mode 100644
index 0000000..ba5c46f
--- /dev/null
+++ b/0006-Merge-pull-request-26835-from-sturkmen72-patch-4.patch
@@ -0,0 +1,133 @@
+From 8131e27e824740afa447a129e72a2f6a3876cbc9 Mon Sep 17 00:00:00 2001
+From: Suleyman TURKMEN <sturkmen@hotmail.com>
+Date: Sat, 25 Jan 2025 09:31:00 +0300
+Subject: [PATCH 06/10] Merge pull request #26835 from sturkmen72:patch-4
+
+Corrections on bKGD chunk writing and reading in PNG #26835
+
+### Pull Request Readiness Checklist
+
+See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
+
+- [x] I agree to contribute to the project under Apache 2 License.
+- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
+- [x] The PR is proposed to the proper branch
+- [ ] There is a reference to the original bug report and related work
+- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
+      Patch to opencv_extra has the same branch name.
+- [ ] The feature is well documented and sample code can be built with the project CMake
+---
+ modules/imgcodecs/src/grfmt_png.cpp       | 26 ++++++++---------
+ modules/imgcodecs/test/test_animation.cpp | 35 ++++++++++++++++++++++-
+ 2 files changed, 46 insertions(+), 15 deletions(-)
+
+diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp
+index 08e37ec0c3..4ec3280607 100644
+--- a/modules/imgcodecs/src/grfmt_png.cpp
++++ b/modules/imgcodecs/src/grfmt_png.cpp
+@@ -327,11 +327,10 @@ bool  PngDecoder::readHeader()
+         if (id == id_bKGD)
+         {
+             // The spec is actually more complex: http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.bKGD
+-            int bgcolor = png_get_uint_32(&chunk.p[8]);
+-            m_animation.bgcolor[3] = (bgcolor >> 24) & 0xFF;
+-            m_animation.bgcolor[2] = (bgcolor >> 16) & 0xFF;
+-            m_animation.bgcolor[1] = (bgcolor >> 8) & 0xFF;
+-            m_animation.bgcolor[0] = bgcolor & 0xFF;
++            m_animation.bgcolor[0] = png_get_uint_16(&chunk.p[8]);
++            m_animation.bgcolor[1] = png_get_uint_16(&chunk.p[10]);
++            m_animation.bgcolor[2] = png_get_uint_16(&chunk.p[12]);
++            m_animation.bgcolor[3] = 0;
+         }
+ 
+         if (id == id_PLTE || id == id_tRNS)
+@@ -721,11 +720,10 @@ uint32_t PngDecoder::read_chunk(Chunk& chunk)
+         if (size != 8 + 26 + 4)
+             return 0;
+     } else if (id == id_bKGD) {
+-        // 8=HDR+size, ??=size of bKGD chunk, 4=CRC
++        // 8=HDR+size, (1, 2 or 6)=size of bKGD chunk, 4=CRC
+         // The spec is actually more complex:
+         // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.bKGD
+-        // TODO: we only check that 4 bytes can be read from &chunk.p[8]. Fix.
+-        if (size < 8 + 4)
++        if (size != 8 + 1 + 4 && size != 8 + 2 + 4 && size != 8 + 6 + 4)
+             return 0;
+     } else if (id != id_fdAT && id != id_IDAT && id != id_IEND && id != id_PLTE && id != id_tRNS) {
+         if (size > PNG_USER_CHUNK_MALLOC_MAX)
+@@ -1542,13 +1540,13 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
+         if (palsize > 0)
+             writeChunk(m_f, "PLTE", (unsigned char*)(&palette), palsize * 3);
+ 
+-        if ((animation.bgcolor != Scalar()) && (animation.frames.size() > 1))
++        if ((animation.bgcolor != Scalar()) && coltype)
+         {
+-            uint64_t bgvalue = (static_cast<int>(animation.bgcolor[0]) & 0xFF) << 24 |
+-                (static_cast<int>(animation.bgcolor[1]) & 0xFF) << 16 |
+-                (static_cast<int>(animation.bgcolor[2]) & 0xFF) << 8 |
+-                (static_cast<int>(animation.bgcolor[3]) & 0xFF);
+-            writeChunk(m_f, "bKGD", (unsigned char*)(&bgvalue), 6); //the bKGD chunk must precede the first IDAT chunk, and must follow the PLTE chunk.
++            unsigned char bgvalue[6] = {};
++            bgvalue[1] = animation.bgcolor[0];
++            bgvalue[3] = animation.bgcolor[1];
++            bgvalue[5] = animation.bgcolor[2];
++            writeChunk(m_f, "bKGD", bgvalue, 6); //the bKGD chunk must precede the first IDAT chunk, and must follow the PLTE chunk.
+         }
+ 
+         if (trnssize > 0)
+diff --git a/modules/imgcodecs/test/test_animation.cpp b/modules/imgcodecs/test/test_animation.cpp
+index e8c42cbcc0..df0a00a8b1 100644
+--- a/modules/imgcodecs/test/test_animation.cpp
++++ b/modules/imgcodecs/test/test_animation.cpp
+@@ -425,6 +425,39 @@ TEST(Imgcodecs_APNG, imwriteanimation_rgb)
+     EXPECT_EQ(0, remove(output.c_str()));
+ }
+ 
++TEST(Imgcodecs_APNG, imwriteanimation_gray)
++{
++    Animation s_animation, l_animation;
++    EXPECT_TRUE(fillFrames(s_animation, false));
++
++    for (size_t i = 0; i < s_animation.frames.size(); i++)
++    {
++        cvtColor(s_animation.frames[i], s_animation.frames[i], COLOR_BGR2GRAY);
++    }
++
++    s_animation.bgcolor = Scalar(50, 100, 150);
++    string output = cv::tempfile(".png");
++    // Write the animation to a .png file and verify success.
++    EXPECT_TRUE(imwriteanimation(output, s_animation));
++
++    // Read the animation back and compare with the original.
++    EXPECT_TRUE(imreadanimation(output, l_animation));
++
++    EXPECT_EQ(Scalar(), l_animation.bgcolor);
++    size_t expected_frame_count = s_animation.frames.size() - 2;
++
++    // Verify that the number of frames matches the expected count.
++    EXPECT_EQ(expected_frame_count, imcount(output));
++    EXPECT_EQ(expected_frame_count, l_animation.frames.size());
++
++    EXPECT_EQ(0, remove(output.c_str()));
++
++    for (size_t i = 0; i < l_animation.frames.size(); i++)
++    {
++        EXPECT_EQ(0, cvtest::norm(s_animation.frames[i], l_animation.frames[i], NORM_INF));
++    }
++}
++
+ TEST(Imgcodecs_APNG, imwritemulti_rgba)
+ {
+     Animation s_animation;
+@@ -492,7 +525,7 @@ TEST(Imgcodecs_APNG, imwriteanimation_bgcolor)
+ {
+     Animation s_animation, l_animation;
+     EXPECT_TRUE(fillFrames(s_animation, true, 2));
+-    s_animation.bgcolor = Scalar(50, 100, 150, 128); // different values for test purpose.
++    s_animation.bgcolor = Scalar(50, 100, 150); // will be written in bKGD chunk as RGB.
+ 
+     // Create a temporary output filename for saving the animation.
+     string output = cv::tempfile(".png");
+-- 
+2.48.1
+

diff --git a/0007-fix-for-large-tEXt-chunk.patch b/0007-fix-for-large-tEXt-chunk.patch
new file mode 100644
index 0000000..20193cb
--- /dev/null
+++ b/0007-fix-for-large-tEXt-chunk.patch
@@ -0,0 +1,39 @@
+From d6c4ac2e5e9cb7ea607ecb8e70884a3c5a06654c Mon Sep 17 00:00:00 2001
+From: Suleyman TURKMEN <sturkmen@hotmail.com>
+Date: Tue, 28 Jan 2025 01:06:41 +0300
+Subject: [PATCH 07/10] fix for large tEXt chunk
+
+---
+ modules/imgcodecs/src/grfmt_png.cpp | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp
+index 4ec3280607..909a9017b2 100644
+--- a/modules/imgcodecs/src/grfmt_png.cpp
++++ b/modules/imgcodecs/src/grfmt_png.cpp
+@@ -126,9 +126,10 @@ const uint32_t id_acTL = 0x4C546361; // Animation control chunk
+ const uint32_t id_fcTL = 0x4C546366; // Frame control chunk
+ const uint32_t id_IDAT = 0x54414449; // first frame and/or default image
+ const uint32_t id_fdAT = 0x54416466; // Frame data chunk
+-const uint32_t id_PLTE = 0x45544C50;
+-const uint32_t id_bKGD = 0x44474B62;
+-const uint32_t id_tRNS = 0x534E5274;
++const uint32_t id_PLTE = 0x45544C50; // The PLTE chunk contains a color palette for indexed-color images
++const uint32_t id_bKGD = 0x44474B62; // The bKGD chunk specifies a default background color for the image
++const uint32_t id_tRNS = 0x534E5274; // The tRNS chunk provides transparency information
++const uint32_t id_tEXt = 0x74584574; // The tEXt chunk stores metadata as text in key-value pairs
+ const uint32_t id_IEND = 0x444E4549; // end/footer chunk
+ 
+ APNGFrame::APNGFrame()
+@@ -725,7 +726,7 @@ uint32_t PngDecoder::read_chunk(Chunk& chunk)
+         // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.bKGD
+         if (size != 8 + 1 + 4 && size != 8 + 2 + 4 && size != 8 + 6 + 4)
+             return 0;
+-    } else if (id != id_fdAT && id != id_IDAT && id != id_IEND && id != id_PLTE && id != id_tRNS) {
++    } else if (id != id_fdAT && id != id_IDAT && id != id_IEND && id != id_PLTE && id != id_tEXt && id != id_tRNS) {
+         if (size > PNG_USER_CHUNK_MALLOC_MAX)
+         {
+             CV_LOG_WARNING(NULL, "user chunk data is too large");
+-- 
+2.48.1
+

diff --git a/0008-Merge-pull-request-26854-from-vrabaud-png_leak.patch b/0008-Merge-pull-request-26854-from-vrabaud-png_leak.patch
new file mode 100644
index 0000000..f673d2f
--- /dev/null
+++ b/0008-Merge-pull-request-26854-from-vrabaud-png_leak.patch
@@ -0,0 +1,90 @@
+From 0d99c4283620671be3009ec00eb260e5a759cc39 Mon Sep 17 00:00:00 2001
+From: Vincent Rabaud <vrabaud@google.com>
+Date: Fri, 31 Jan 2025 09:00:23 +0100
+Subject: [PATCH 08/10] Merge pull request #26854 from vrabaud:png_leak
+
+Fix oss-fuzz bugs 391934081 and 392318892 #26854
+
+- fix a potential overflow in x0+w0
+- use the proper function to deal with background color to deal with all cases of the spec
+- use BGR layout for APNG background color
+
+### Pull Request Readiness Checklist
+
+See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
+
+- [x] I agree to contribute to the project under Apache 2 License.
+- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
+- [x] The PR is proposed to the proper branch
+- [x] There is a reference to the original bug report and related work
+- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
+      Patch to opencv_extra has the same branch name.
+- [ ] The feature is well documented and sample code can be built with the project CMake
+---
+ .../imgcodecs/include/opencv2/imgcodecs.hpp   |  2 +-
+ modules/imgcodecs/src/grfmt_png.cpp           | 19 +++++++------------
+ 2 files changed, 8 insertions(+), 13 deletions(-)
+
+diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp
+index cd648c2c6e..c802033e6b 100644
+--- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp
++++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp
+@@ -263,7 +263,7 @@ struct CV_EXPORTS_W_SIMPLE Animation
+     - If a negative value or a value beyond the maximum of `0xffff` (65535) is provided, it is reset to `0`
+     (infinite looping) to maintain valid bounds.
+ 
+-    @param bgColor A `Scalar` object representing the background color in BGRA format:
++    @param bgColor A `Scalar` object representing the background color in BGR format:
+     - Defaults to `Scalar()`, indicating an empty color (usually transparent if supported).
+     - This background color provides a solid fill behind frames that have transparency, ensuring a consistent display appearance.
+     */
+diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp
+index 909a9017b2..f7a19c2bf5 100644
+--- a/modules/imgcodecs/src/grfmt_png.cpp
++++ b/modules/imgcodecs/src/grfmt_png.cpp
+@@ -325,15 +325,6 @@ bool  PngDecoder::readHeader()
+             bop = chunk.p[33];
+         }
+ 
+-        if (id == id_bKGD)
+-        {
+-            // The spec is actually more complex: http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.bKGD
+-            m_animation.bgcolor[0] = png_get_uint_16(&chunk.p[8]);
+-            m_animation.bgcolor[1] = png_get_uint_16(&chunk.p[10]);
+-            m_animation.bgcolor[2] = png_get_uint_16(&chunk.p[12]);
+-            m_animation.bgcolor[3] = 0;
+-        }
+-
+         if (id == id_PLTE || id == id_tRNS)
+             m_chunksInfo.push_back(chunk);
+     }
+@@ -356,9 +347,13 @@ bool  PngDecoder::readHeader()
+     m_color_type = color_type;
+     m_bit_depth = bit_depth;
+ 
+-    if (m_is_fcTL_loaded && (int(x0 + w0) > m_width || int(y0 + h0) > m_height || dop > 2 || bop > 1))
++    if (m_is_fcTL_loaded && ((long long int)x0 + w0 > m_width || (long long int)y0 + h0 > m_height || dop > 2 || bop > 1))
+         return false;
+ 
++    png_color_16p background_color;
++    if (png_get_bKGD(m_png_ptr, m_info_ptr, &background_color))
++        m_animation.bgcolor = Scalar(background_color->blue, background_color->green, background_color->red);
++
+     if (bit_depth <= 8 || bit_depth == 16)
+     {
+         switch (color_type)
+@@ -1544,9 +1539,9 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
+         if ((animation.bgcolor != Scalar()) && coltype)
+         {
+             unsigned char bgvalue[6] = {};
+-            bgvalue[1] = animation.bgcolor[0];
++            bgvalue[1] = animation.bgcolor[2];
+             bgvalue[3] = animation.bgcolor[1];
+-            bgvalue[5] = animation.bgcolor[2];
++            bgvalue[5] = animation.bgcolor[0];
+             writeChunk(m_f, "bKGD", bgvalue, 6); //the bKGD chunk must precede the first IDAT chunk, and must follow the PLTE chunk.
+         }
+ 
+-- 
+2.48.1
+

diff --git a/0009-Merge-pull-request-26872-from-sturkmen72-ImageEncode.patch b/0009-Merge-pull-request-26872-from-sturkmen72-ImageEncode.patch
new file mode 100644
index 0000000..3b404b3
--- /dev/null
+++ b/0009-Merge-pull-request-26872-from-sturkmen72-ImageEncode.patch
@@ -0,0 +1,367 @@
+From 8aa1086ab475ef0040d3865e49edd6feba4eb7d3 Mon Sep 17 00:00:00 2001
+From: Suleyman TURKMEN <sturkmen@hotmail.com>
+Date: Tue, 4 Feb 2025 12:21:55 +0300
+Subject: [PATCH 09/10] Merge pull request #26872 from
+ sturkmen72:ImageEncoders_revisions
+
+Performance tests for image encoders and decoders and code cleanup #26872
+
+### Pull Request Readiness Checklist
+
+See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request
+
+- [x] I agree to contribute to the project under Apache 2 License.
+- [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV
+- [x] The PR is proposed to the proper branch
+- [ ] There is a reference to the original bug report and related work
+- [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable
+      Patch to opencv_extra has the same branch name.
+- [ ] The feature is well documented and sample code can be built with the project CMake
+---
+ modules/highgui/src/window_w32.cpp            |  10 +-
+ modules/imgcodecs/perf/perf_decode_encode.cpp | 131 ++++++++++++++++++
+ modules/imgcodecs/src/grfmt_avif.cpp          |   5 -
+ modules/imgcodecs/src/grfmt_avif.hpp          |   1 -
+ modules/imgcodecs/src/grfmt_base.cpp          |   8 +-
+ modules/imgcodecs/src/grfmt_base.hpp          |   5 +-
+ modules/imgcodecs/src/grfmt_gif.cpp           |  10 +-
+ modules/imgcodecs/src/grfmt_gif.hpp           |   3 -
+ modules/imgcodecs/src/grfmt_png.cpp           |   6 +-
+ modules/imgcodecs/src/loadsave.cpp            |   2 +-
+ 10 files changed, 155 insertions(+), 26 deletions(-)
+ create mode 100644 modules/imgcodecs/perf/perf_decode_encode.cpp
+
+diff --git a/modules/highgui/src/window_w32.cpp b/modules/highgui/src/window_w32.cpp
+index 2543c81c6a..8e041c9609 100644
+--- a/modules/highgui/src/window_w32.cpp
++++ b/modules/highgui/src/window_w32.cpp
+@@ -2170,9 +2170,15 @@ static void showSaveDialog(CvWindow& window)
+ #ifdef HAVE_WEBP
+                       "WebP files (*.webp)\0*.webp\0"
+ #endif
+-                      "Portable image format (*.pbm;*.pgm;*.ppm;*.pxm;*.pnm)\0*.pbm;*.pgm;*.ppm;*.pxm;*.pnm\0"
++                      "Portable image format (*.pbm;*.pgm;*.ppm;*.pnm;*.pam)\0*.pbm;*.pgm;*.ppm;*.pnm;*.pam\0"
+ #ifdef HAVE_OPENEXR
+                       "OpenEXR Image files (*.exr)\0*.exr\0"
++#endif
++#ifdef HAVE_AVIF
++                      "AVIF files (*.avif)\0*.avif\0"
++#endif
++#ifdef HAVE_IMGCODEC_GIF
++                      "Graphics Interchange Format 89a(*.gif)\0*.gif\0"
+ #endif
+                       "Radiance HDR (*.hdr;*.pic)\0*.hdr;*.pic\0"
+                       "Sun raster files (*.sr;*.ras)\0*.sr;*.ras\0"
+@@ -2194,7 +2200,7 @@ static void showSaveDialog(CvWindow& window)
+     }
+ #else
+     CV_UNUSED(window);
+-    CV_LOG_WARNING("Save dialog requires enabled 'imgcodecs' module.");
++    CV_LOG_WARNING(NULL, "Save dialog requires enabled 'imgcodecs' module.");
+     return;
+ #endif
+ }
+diff --git a/modules/imgcodecs/perf/perf_decode_encode.cpp b/modules/imgcodecs/perf/perf_decode_encode.cpp
+new file mode 100644
+index 0000000000..ce693cb878
+--- /dev/null
++++ b/modules/imgcodecs/perf/perf_decode_encode.cpp
+@@ -0,0 +1,131 @@
++// This file is part of OpenCV project.
++// It is subject to the license terms in the LICENSE file found in the top-level directory
++// of this distribution and at http://opencv.org/license.html
++
++#include "perf_precomp.hpp"
++
++namespace opencv_test
++{
++
++#ifdef HAVE_PNG
++
++using namespace perf;
++
++typedef perf::TestBaseWithParam<std::string> Decode;
++typedef perf::TestBaseWithParam<std::string> Encode;
++
++const string exts[] = {
++#ifdef HAVE_AVIF
++    ".avif",
++#endif
++    ".bmp",
++#ifdef HAVE_IMGCODEC_GIF
++    ".gif",
++#endif
++#if (defined(HAVE_JASPER) && defined(OPENCV_IMGCODECS_ENABLE_JASPER_TESTS)) \
++    || defined(HAVE_OPENJPEG)
++    ".jp2",
++#endif
++#ifdef HAVE_JPEG
++    ".jpg",
++#endif
++#ifdef HAVE_JPEGXL
++    ".jxl",
++#endif
++    ".png",
++#ifdef HAVE_IMGCODEC_PXM
++    ".ppm",
++#endif
++#ifdef HAVE_IMGCODEC_SUNRASTER
++    ".ras",
++#endif
++#ifdef HAVE_TIFF
++    ".tiff",
++#endif
++#ifdef HAVE_WEBP
++    ".webp",
++#endif
++};
++
++const string exts_multi[] = {
++#ifdef HAVE_AVIF
++    ".avif",
++#endif
++#ifdef HAVE_IMGCODEC_GIF
++    ".gif",
++#endif
++    ".png",
++#ifdef HAVE_TIFF
++    ".tiff",
++#endif
++#ifdef HAVE_WEBP
++    ".webp",
++#endif
++};
++
++PERF_TEST_P(Decode, bgr, testing::ValuesIn(exts))
++{
++    String filename = getDataPath("perf/1920x1080.png");
++
++    Mat src = imread(filename);
++    EXPECT_FALSE(src.empty()) << "Cannot open test image perf/1920x1080.png";
++    vector<uchar> buf;
++    EXPECT_TRUE(imencode(GetParam(), src, buf));
++
++    TEST_CYCLE() imdecode(buf, IMREAD_UNCHANGED);
++
++    SANITY_CHECK_NOTHING();
++}
++
++PERF_TEST_P(Decode, rgb, testing::ValuesIn(exts))
++{
++    String filename = getDataPath("perf/1920x1080.png");
++
++    Mat src = imread(filename);
++    EXPECT_FALSE(src.empty()) << "Cannot open test image perf/1920x1080.png";
++    vector<uchar> buf;
++    EXPECT_TRUE(imencode(GetParam(), src, buf));
++
++    TEST_CYCLE() imdecode(buf, IMREAD_COLOR_RGB);
++
++    SANITY_CHECK_NOTHING();
++}
++
++PERF_TEST_P(Encode, bgr, testing::ValuesIn(exts))
++{
++    String filename = getDataPath("perf/1920x1080.png");
++
++    Mat src = imread(filename);
++    EXPECT_FALSE(src.empty()) << "Cannot open test image perf/1920x1080.png";
++    vector<uchar> buf;
++
++    TEST_CYCLE() imencode(GetParam(), src, buf);
++
++    std::cout << "Encoded buffer size: " << buf.size()
++        << " bytes, Compression ratio: " << std::fixed << std::setprecision(2)
++        << (static_cast<double>(buf.size()) / (src.total() * src.channels())) * 100.0 << "%" << std::endl;
++
++    SANITY_CHECK_NOTHING();
++}
++
++PERF_TEST_P(Encode, multi, testing::ValuesIn(exts_multi))
++{
++    String filename = getDataPath("perf/1920x1080.png");
++    vector<Mat> vec;
++    EXPECT_TRUE(imreadmulti(filename, vec));
++    vec.push_back(vec.back().clone());
++    circle(vec.back(), Point(100, 100), 45, Scalar(0, 0, 255, 0), 2, LINE_AA);
++    vector<uchar> buf;
++    EXPECT_TRUE(imwrite("test" + GetParam(), vec));
++
++    TEST_CYCLE() imencode(GetParam(), vec, buf);
++
++    std::cout << "Encoded buffer size: " << buf.size()
++        << " bytes, Compression ratio: " << std::fixed << std::setprecision(2)
++        << (static_cast<double>(buf.size()) / (vec[0].total() * vec[0].channels())) * 100.0 << "%" << std::endl;
++
++    SANITY_CHECK_NOTHING();
++}
++#endif // HAVE_PNG
++
++} // namespace
+diff --git a/modules/imgcodecs/src/grfmt_avif.cpp b/modules/imgcodecs/src/grfmt_avif.cpp
+index d3fb500604..c35eb50306 100644
+--- a/modules/imgcodecs/src/grfmt_avif.cpp
++++ b/modules/imgcodecs/src/grfmt_avif.cpp
+@@ -298,11 +298,6 @@ bool AvifEncoder::isFormatSupported(int depth) const {
+   return (depth == CV_8U || depth == CV_16U);
+ }
+ 
+-bool AvifEncoder::write(const Mat &img, const std::vector<int> &params) {
+-  std::vector<Mat> img_vec(1, img);
+-  return writemulti(img_vec, params);
+-}
+-
+ bool AvifEncoder::writeanimation(const Animation& animation,
+                                  const std::vector<int> &params) {
+   int bit_depth = 8;
+diff --git a/modules/imgcodecs/src/grfmt_avif.hpp b/modules/imgcodecs/src/grfmt_avif.hpp
+index 87b765619e..9f097aaf55 100644
+--- a/modules/imgcodecs/src/grfmt_avif.hpp
++++ b/modules/imgcodecs/src/grfmt_avif.hpp
+@@ -41,7 +41,6 @@ class AvifEncoder CV_FINAL : public BaseImageEncoder {
+   ~AvifEncoder() CV_OVERRIDE;
+ 
+   bool isFormatSupported(int depth) const CV_OVERRIDE;
+-  bool write(const Mat& img, const std::vector<int>& params) CV_OVERRIDE;
+   bool writeanimation(const Animation& animation, const std::vector<int>& params) CV_OVERRIDE;
+ 
+   ImageEncoder newEncoder() const CV_OVERRIDE;
+diff --git a/modules/imgcodecs/src/grfmt_base.cpp b/modules/imgcodecs/src/grfmt_base.cpp
+index 1e09882780..dc3d07ab78 100644
+--- a/modules/imgcodecs/src/grfmt_base.cpp
++++ b/modules/imgcodecs/src/grfmt_base.cpp
+@@ -140,6 +140,11 @@ bool BaseImageEncoder::setDestination( std::vector<uchar>& buf )
+     return true;
+ }
+ 
++bool BaseImageEncoder::write(const Mat &img, const std::vector<int> &params) {
++    std::vector<Mat> img_vec(1, img);
++    return writemulti(img_vec, params);
++}
++
+ bool BaseImageEncoder::writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params)
+ {
+     if(img_vec.size() > 1)
+@@ -157,6 +162,7 @@ bool BaseImageEncoder::writemulti(const std::vector<Mat>& img_vec, const std::ve
+ 
+ bool BaseImageEncoder::writeanimation(const Animation&, const std::vector<int>& )
+ {
++    CV_LOG_WARNING(NULL, "No Animation encoder for specified file extension");
+     return false;
+ }
+ 
+@@ -165,7 +171,7 @@ ImageEncoder BaseImageEncoder::newEncoder() const
+     return ImageEncoder();
+ }
+ 
+-void BaseImageEncoder::throwOnEror() const
++void BaseImageEncoder::throwOnError() const
+ {
+     if(!m_last_error.empty())
+     {
+diff --git a/modules/imgcodecs/src/grfmt_base.hpp b/modules/imgcodecs/src/grfmt_base.hpp
+index a90bd8a3de..ae5622528c 100644
+--- a/modules/imgcodecs/src/grfmt_base.hpp
++++ b/modules/imgcodecs/src/grfmt_base.hpp
+@@ -202,12 +202,11 @@ public:
+ 
+     /**
+      * @brief Encode and write the image data.
+-     * This is a pure virtual function that must be implemented by derived classes.
+      * @param img The Mat object containing the image data to be encoded.
+      * @param params A vector of parameters controlling the encoding process (e.g., compression level).
+      * @return true if the image was successfully written, false otherwise.
+      */
+-    virtual bool write(const Mat& img, const std::vector<int>& params) = 0;
++    virtual bool write(const Mat& img, const std::vector<int>& params);
+ 
+     /**
+      * @brief Encode and write multiple images (e.g., for animated formats).
+@@ -236,7 +235,7 @@ public:
+      * @brief Throw an exception based on the last error encountered during encoding.
+      * This method can be used to propagate error conditions back to the caller.
+      */
+-    virtual void throwOnEror() const;
++    virtual void throwOnError() const;
+ 
+ protected:
+     String m_description;    ///< Description of the encoder (e.g., format name, capabilities).
+diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp
+index 5a65ae04b1..b0533b644f 100644
+--- a/modules/imgcodecs/src/grfmt_gif.cpp
++++ b/modules/imgcodecs/src/grfmt_gif.cpp
+@@ -488,19 +488,11 @@ GifEncoder::~GifEncoder() {
+     close();
+ }
+ 
+-bool GifEncoder::isFormatSupported(int depth) const {
+-    return depth == CV_8U;
+-}
+-
+-bool GifEncoder::write(const Mat &img, const std::vector<int> &params) {
+-    std::vector<Mat> img_vec(1, img);
+-    return writemulti(img_vec, params);
+-}
+-
+ bool GifEncoder::writeanimation(const Animation& animation, const std::vector<int>& params) {
+     if (animation.frames.empty()) {
+         return false;
+     }
++    CV_CheckDepthEQ(animation.frames[0].depth(), CV_8U, "GIF encoder supports only 8-bit unsigned images");
+ 
+     if (m_buf) {
+         if (!strm.open(*m_buf)) {
+diff --git a/modules/imgcodecs/src/grfmt_gif.hpp b/modules/imgcodecs/src/grfmt_gif.hpp
+index 8f520745ba..8552718d00 100644
+--- a/modules/imgcodecs/src/grfmt_gif.hpp
++++ b/modules/imgcodecs/src/grfmt_gif.hpp
+@@ -83,9 +83,6 @@ public:
+     GifEncoder();
+     ~GifEncoder() CV_OVERRIDE;
+ 
+-    bool isFormatSupported(int depth) const CV_OVERRIDE;
+-
+-    bool write(const Mat& img, const std::vector<int>& params) CV_OVERRIDE;
+     bool writeanimation(const Animation& animation, const std::vector<int>& params) CV_OVERRIDE;
+ 
+     ImageEncoder newEncoder() const CV_OVERRIDE;
+diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp
+index f7a19c2bf5..825122304a 100644
+--- a/modules/imgcodecs/src/grfmt_png.cpp
++++ b/modules/imgcodecs/src/grfmt_png.cpp
+@@ -1412,6 +1412,9 @@ void PngEncoder::deflateRectFin(unsigned char* zbuf, uint32_t* zsize, int bpp, i
+ 
+ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<int>& params)
+ {
++    int frame_type = animation.frames[0].type();
++    int frame_depth = animation.frames[0].depth();
++    CV_CheckType(frame_type, frame_depth == CV_8U || frame_depth == CV_16U, "APNG decoder supports only 8 or 16 bit unsigned images");
+     int compression_level = 6;
+     int compression_strategy = IMWRITE_PNG_STRATEGY_RLE; // Default strategy
+     bool isBilevel = false;
+@@ -1435,7 +1438,8 @@ bool PngEncoder::writeanimation(const Animation& animation, const std::vector<in
+         }
+     }
+ 
+-    CV_UNUSED(isBilevel);
++    if (isBilevel)
++        CV_LOG_WARNING(NULL, "IMWRITE_PNG_BILEVEL parameter is not supported yet.");
+     uint32_t first =0;
+     uint32_t loops= animation.loop_count;
+     uint32_t coltype= animation.frames[0].channels() == 1 ? PNG_COLOR_TYPE_GRAY : animation.frames[0].channels() == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA;
+diff --git a/modules/imgcodecs/src/loadsave.cpp b/modules/imgcodecs/src/loadsave.cpp
+index ec25f8c610..37b0701c8a 100644
+--- a/modules/imgcodecs/src/loadsave.cpp
++++ b/modules/imgcodecs/src/loadsave.cpp
+@@ -1372,7 +1372,7 @@ bool imencode( const String& ext, InputArray _img,
+         else
+             code = encoder->writemulti(write_vec, params);
+ 
+-        encoder->throwOnEror();
++        encoder->throwOnError();
+         CV_Assert( code );
+     }
+     catch (const cv::Exception& e)
+-- 
+2.48.1
+

diff --git a/0010-Merge-pull-request-26915-from-mshabunin-fix-png-be.patch b/0010-Merge-pull-request-26915-from-mshabunin-fix-png-be.patch
new file mode 100644
index 0000000..17f4955
--- /dev/null
+++ b/0010-Merge-pull-request-26915-from-mshabunin-fix-png-be.patch
@@ -0,0 +1,54 @@
+From ab0a4167057dadcfc497f0d4d653b5eec7fd586a Mon Sep 17 00:00:00 2001
+From: Maksim Shabunin <maksim.shabunin@gmail.com>
+Date: Thu, 13 Feb 2025 16:58:15 +0300
+Subject: [PATCH 10/10] Merge pull request #26915 from mshabunin:fix-png-be
+
+Resolves #26913
+Related(?): #25715 #26832
+---
+ modules/imgcodecs/src/grfmt_png.cpp | 22 +++++++++++-----------
+ 1 file changed, 11 insertions(+), 11 deletions(-)
+
+diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp
+index 825122304a..84df975471 100644
+--- a/modules/imgcodecs/src/grfmt_png.cpp
++++ b/modules/imgcodecs/src/grfmt_png.cpp
+@@ -121,16 +121,16 @@
+ namespace cv
+ {
+ 
+-const uint32_t id_IHDR = 0x52444849; // PNG header
+-const uint32_t id_acTL = 0x4C546361; // Animation control chunk
+-const uint32_t id_fcTL = 0x4C546366; // Frame control chunk
+-const uint32_t id_IDAT = 0x54414449; // first frame and/or default image
+-const uint32_t id_fdAT = 0x54416466; // Frame data chunk
+-const uint32_t id_PLTE = 0x45544C50; // The PLTE chunk contains a color palette for indexed-color images
+-const uint32_t id_bKGD = 0x44474B62; // The bKGD chunk specifies a default background color for the image
+-const uint32_t id_tRNS = 0x534E5274; // The tRNS chunk provides transparency information
+-const uint32_t id_tEXt = 0x74584574; // The tEXt chunk stores metadata as text in key-value pairs
+-const uint32_t id_IEND = 0x444E4549; // end/footer chunk
++const uint32_t id_IHDR = 0x49484452; // PNG header
++const uint32_t id_acTL = 0x6163544C; // Animation control chunk
++const uint32_t id_fcTL = 0x6663544C; // Frame control chunk
++const uint32_t id_IDAT = 0x49444154; // first frame and/or default image
++const uint32_t id_fdAT = 0x66644154; // Frame data chunk
++const uint32_t id_PLTE = 0x504C5445; // The PLTE chunk contains a color palette for indexed-color images
++const uint32_t id_bKGD = 0x624B4744; // The bKGD chunk specifies a default background color for the image
++const uint32_t id_tRNS = 0x74524E53; // The tRNS chunk provides transparency information
++const uint32_t id_tEXt = 0x74455874; // The tEXt chunk stores metadata as text in key-value pairs
++const uint32_t id_IEND = 0x49454E44; // end/footer chunk
+ 
+ APNGFrame::APNGFrame()
+ {
+@@ -699,7 +699,7 @@ uint32_t PngDecoder::read_chunk(Chunk& chunk)
+         return 0;
+     const size_t size = static_cast<size_t>(png_get_uint_32(size_id)) + 12;
+ 
+-    const uint32_t id = *(uint32_t*)(&size_id[4]);
++    const uint32_t id = png_get_uint_32(size_id + 4);
+     if (id == id_IHDR) {
+         // 8=HDR+size, 13=size of IHDR chunk, 4=CRC
+         // http://www.libpng.org/pub/png/spec/1.2/PNG-Chunks.html#C.IHDR
+-- 
+2.48.1
+

diff --git a/opencv.spec b/opencv.spec
index 02af591..9beb5c2 100644
--- a/opencv.spec
+++ b/opencv.spec
@@ -77,7 +77,7 @@ Version:        4.11.0
 %global minorver %(foo=%{version}; a=(${foo//./ }); echo ${a[1]} )
 %global padding  %(digits=00; num=%{minorver}; echo ${digits:${#num}:${#digits}} )
 %global abiver   %(echo %{majorver}%{padding}%{minorver} )
-Release:        1%{?dist}
+Release:        2%{?dist}
 Summary:        Collection of algorithms for computer vision
 # This is normal three clause BSD.
 License:        BSD-3-Clause AND Apache-2.0 AND ISC
@@ -106,6 +106,21 @@ Patch0:         opencv-4.1.0-install_3rdparty_licenses.patch
 Patch3:         opencv.python.patch
 Patch4:         https://github.com/opencv/opencv/pull/26750.patch
 Patch5:         https://github.com/opencv/opencv/pull/26786.patch
+# backport all PNG patches from 4.11.0 to 45aa502549 - fixes issues
+# including complete failure to read PNGs on s390x (big-endian)
+# https://bugzilla.redhat.com/show_bug.cgi?id=2345306
+# https://github.com/opencv/opencv/issues/26913
+Patch6:         0001-Merge-pull-request-26739-from-vrabaud-png_leak.patch
+Patch7:         0002-Fix-remaining-bugs-in-PNG-reader.patch
+Patch8:         0003-Merge-pull-request-26782-from-vrabaud-png_leak.patch
+Patch9:         0004-Move-the-checks-to-read_chunk.patch
+Patch10:        0005-minor-improvement-for-better-code-readibility.patch
+Patch11:        0006-Merge-pull-request-26835-from-sturkmen72-patch-4.patch
+Patch12:        0007-fix-for-large-tEXt-chunk.patch
+Patch13:        0008-Merge-pull-request-26854-from-vrabaud-png_leak.patch
+Patch14:        0009-Merge-pull-request-26872-from-sturkmen72-ImageEncode.patch
+Patch15:        0010-Merge-pull-request-26915-from-mshabunin-fix-png-be.patch
+
 
 BuildRequires:  gcc-c++
 BuildRequires:  cmake >= 2.6.3
@@ -407,6 +422,16 @@ popd &>/dev/null
 %patch -P 3 -p1 -b .python_install_binary
 %patch -P 4 -p1 -b .VSX_intrinsics
 %patch -P 5 -p1 -b .GCC15
+%patch -P 6 -p1 -b .png1
+%patch -P 7 -p1 -b .png2
+%patch -P 8 -p1 -b .png3
+%patch -P 9 -p1 -b .png4
+%patch -P 10 -p1 -b .png5
+%patch -P 11 -p1 -b .png6
+%patch -P 12 -p1 -b .png7
+%patch -P 13 -p1 -b .png8
+%patch -P 14 -p1 -b .png9
+%patch -P 15 -p1 -b .png10
 
 pushd %{name}_contrib-%{version}
 #patch1 -p1 -b .install_cvv
@@ -583,6 +608,10 @@ ln -s -r %{buildroot}%{_jnidir}/opencv-%{javaver}.jar %{buildroot}%{_jnidir}/ope
 
 
 %changelog
+* Tue Feb 18 2025 Adam Williamson <awilliam@redhat.com> - 4.11.0-2
+- Backport all post-4.11.0 PNG fixes, including big-endian fix
+- Resolves: rhbz#2345306
+
 * Mon Feb 03 2025 Sérgio Basto <sergio@serjux.com> 4.11.0-1
 - Update to version 4.11.0
 - Resolves: rhbz#2336422

^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-08-28 13:32 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-08-28 13:32 [rpms/opencv] opencv5: Backport all post-4.11.0 PNG fixes, including big-endian fix Adam Williamson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox