Merge pull request #27297 from Kumataro:fix27295

imgcodecs: png: add log if first chunk is not IHDR #27297

Close https://github.com/opencv/opencv/issues/27295

To optimize for the native pixel format of the iPhone's early PowerVR GPUs, Apple implemented a non-standard PNG format.

Details: https://theapplewiki.com/wiki/PNG_CgBI_Format

### 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
- [x] 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
This commit is contained in:
Kumataro 2025-05-12 16:44:54 +09:00 committed by GitHub
parent 8035aade11
commit 3a69b11b6d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 1 deletions

View File

@ -133,6 +133,7 @@ const uint32_t id_bKGD = 0x624B4744; // The bKGD chunk specifies a default backg
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
const uint32_t id_CgBI = 0x43674249; // The CgBI chunk (Apple private) is not supported.
APNGFrame::APNGFrame()
{
@ -285,9 +286,18 @@ bool PngDecoder::readHeader()
if (!readFromStreamOrBuffer(&sig, 8))
return false;
// IHDR chunk shall be first. ( https://www.w3.org/TR/png-3/#5ChunkOrdering )
id = read_chunk(m_chunkIHDR);
if (id != id_IHDR)
if (id == id_CgBI)
{
CV_LOG_ERROR(NULL, "CgBI chunk (Apple private) found as the first chunk. IHDR is expected.");
return false;
}
if (id != id_IHDR)
{
CV_LOG_ERROR(NULL, "IHDR chunk shall be first. This data may be broken or malformed.");
return false;
}
m_is_fcTL_loaded = false;
while (true)

View File

@ -110,6 +110,44 @@ TEST(Imgcodecs_Png, read_color_palette_with_alpha)
EXPECT_EQ(img.at<Vec3b>(0, 1), Vec3b(255, 0, 0));
}
// IHDR shall be first.
// See https://github.com/opencv/opencv/issues/27295
TEST(Imgcodecs_Png, decode_regression27295)
{
vector<uchar> buff;
Mat src = Mat::zeros(240, 180, CV_8UC3);
vector<int> param;
EXPECT_NO_THROW(imencode(".png", src, buff, param));
Mat img;
// If IHDR chunk found as the first chunk, output shall not be empty.
// 8 means PNG signature length.
// 4 means length field(uint32_t).
EXPECT_EQ(buff[8+4+0], 'I');
EXPECT_EQ(buff[8+4+1], 'H');
EXPECT_EQ(buff[8+4+2], 'D');
EXPECT_EQ(buff[8+4+3], 'R');
EXPECT_NO_THROW(img = imdecode(buff, IMREAD_COLOR));
EXPECT_FALSE(img.empty());
// If Non-IHDR chunk found as the first chunk, output shall be empty.
buff[8+4+0] = 'i'; // Not 'I'
buff[8+4+1] = 'H';
buff[8+4+2] = 'D';
buff[8+4+3] = 'R';
EXPECT_NO_THROW(img = imdecode(buff, IMREAD_COLOR));
EXPECT_TRUE(img.empty());
// If CgBI chunk (Apple private) found as the first chunk, output shall be empty with special message.
buff[8+4+0] = 'C';
buff[8+4+1] = 'g';
buff[8+4+2] = 'B';
buff[8+4+3] = 'I';
EXPECT_NO_THROW(img = imdecode(buff, IMREAD_COLOR));
EXPECT_TRUE(img.empty());
}
typedef testing::TestWithParam<string> Imgcodecs_Png_PngSuite;
TEST_P(Imgcodecs_Png_PngSuite, decode)