From 1dd06eb0da3fc96bc030c6bd0fb4584d220d71a2 Mon Sep 17 00:00:00 2001 From: Kumataro Date: Fri, 1 Aug 2025 09:49:43 +0900 Subject: [PATCH] imgcodecs: png: adjust value range for IMWRITE_PNG_ZLIBBUFFER_SIZE --- .../imgcodecs/include/opencv2/imgcodecs.hpp | 2 +- modules/imgcodecs/src/grfmt_png.cpp | 6 +++++- modules/imgcodecs/src/grfmt_spng.cpp | 6 ++++++ modules/imgcodecs/test/test_png.cpp | 20 +++++++++++++++++++ 4 files changed, 32 insertions(+), 2 deletions(-) diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 039482017f..841d5b32cf 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -97,7 +97,7 @@ enum ImwriteFlags { IMWRITE_PNG_STRATEGY = 17, //!< For PNG, One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_RLE. IMWRITE_PNG_BILEVEL = 18, //!< For PNG, Binary level PNG, 0 or 1, default is 0. IMWRITE_PNG_FILTER = 19, //!< For PNG, One of cv::ImwritePNGFilterFlags, default is IMWRITE_PNG_FILTER_SUB. - IMWRITE_PNG_ZLIBBUFFER_SIZE = 20, //!< For PNG, sets the size of the internal zlib compression buffer in bytes. + IMWRITE_PNG_ZLIBBUFFER_SIZE = 20, //!< For PNG with libpng, sets the size of the internal zlib compression buffer in bytes, from 6 to 1048576(1024 KiB). Default is 8192(8 KiB). For normal use, 131072(128 KiB) or 262144(256 KiB) may be sufficient. If WITH_SPNG=ON, it is not supported. IMWRITE_PXM_BINARY = 32, //!< For PPM, PGM, or PBM, it can be a binary format flag, 0 or 1. Default value is 1. IMWRITE_EXR_TYPE = (3 << 4) + 0 /* 48 */, //!< override EXR storage type (FLOAT (FP32) is default) IMWRITE_EXR_COMPRESSION = (3 << 4) + 1 /* 49 */, //!< override EXR compression type (ZIP_COMPRESSION = 3 is default) diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index 55e5f7ff11..4d3d8f9d8d 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -1007,7 +1007,11 @@ bool PngEncoder::write( const Mat& img, const std::vector& params ) break; case IMWRITE_PNG_ZLIBBUFFER_SIZE: - png_set_compression_buffer_size(png_ptr, params[i+1]); + // The default value is 8 KiB. + // The minimum limit is 6, which is from from https://github.com/opencv/opencv/blob/4.12.0/3rdparty/libpng/pngset.c#L1600 . + // The maximum limit is 1 MiB, which has been provisionally set. libpng limitation is 2 GiB(INT32_MAX), but it is too large. + // For normal use, 128 or 256 KiB may be sufficient. See https://zlib.net/zlib_how.html . + png_set_compression_buffer_size(png_ptr, MIN(MAX(params[i+1],6), 1024*1024)); break; default: diff --git a/modules/imgcodecs/src/grfmt_spng.cpp b/modules/imgcodecs/src/grfmt_spng.cpp index 8057af1222..e2f6f02bb9 100644 --- a/modules/imgcodecs/src/grfmt_spng.cpp +++ b/modules/imgcodecs/src/grfmt_spng.cpp @@ -23,6 +23,7 @@ #include #include "grfmt_spng.hpp" +#include /* * libspng does not support RGB -> Gray conversion. In order to decode colorful images as grayscale @@ -554,6 +555,11 @@ bool SPngEncoder::write(const Mat &img, const std::vector ¶ms) filter = params[i+1]; set_filter = true; } + if( params[i] == IMWRITE_PNG_ZLIBBUFFER_SIZE ) + { + // See https://libspng.org/docs/migrate-libpng/#miscellaneous-functions + CV_LOG_WARNING(nullptr, "libspng does not support png_set_compression_buffer_size() which is required for IMWRITE_PNG_ZLIBBUFFER_SIZE"); + } } ihdr.bit_depth = depth == CV_8U ? isBilevel ? 1 : 8 : 16; diff --git a/modules/imgcodecs/test/test_png.cpp b/modules/imgcodecs/test/test_png.cpp index 0d2512fd20..967b852d76 100644 --- a/modules/imgcodecs/test/test_png.cpp +++ b/modules/imgcodecs/test/test_png.cpp @@ -569,6 +569,26 @@ INSTANTIATE_TEST_CASE_P(/**/, make_tuple("../perf/512x512.png", 8, 153708), make_tuple("../perf/512x512.png", 9, 152181))); +// See https://github.com/opencv/opencv/issues/27614 +typedef testing::TestWithParam Imgcodecs_Png_ZLIBBUFFER_SIZE; +TEST_P(Imgcodecs_Png_ZLIBBUFFER_SIZE, encode_regression_27614) +{ + Mat img(320,240,CV_8UC3,cv::Scalar(64,76,43)); + vector buff; + bool status = false; + ASSERT_NO_THROW(status = imencode(".png", img, buff, { IMWRITE_PNG_ZLIBBUFFER_SIZE, GetParam() })); + ASSERT_TRUE(status); +} + +INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Png_ZLIBBUFFER_SIZE, + testing::Values(5, + 6, // Minimum limit + 8192, // Default value + 131072, // 128 KiB + 262144, // 256 KiB + 1048576, // Maximum limit + 1048577)); + #endif // HAVE_PNG }} // namespace