1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-29 15:23:05 +04:00

Merge pull request #27040 from vrabaud:png_leak

Make sure there are enough channels to check for opacity #27040

### 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
This commit is contained in:
Vincent Rabaud
2025-03-12 19:06:01 +01:00
committed by GitHub
parent 7481cb50b5
commit 71fe903121
+54 -48
View File
@@ -42,6 +42,8 @@
#include "precomp.hpp"
#include <memory>
#ifdef HAVE_PNG
/****************************************************************************************\
@@ -328,6 +330,8 @@ bool PngDecoder::readHeader()
delay_den = png_get_uint_16(&chunk.p[30]);
dop = chunk.p[32];
bop = chunk.p[33];
if (dop > 2 || bop > 1)
return false;
}
if (id == id_PLTE || id == id_tRNS)
@@ -480,6 +484,11 @@ bool PngDecoder::readData( Mat& img )
{
return false;
}
// Asking for blend over with no alpha is invalid.
if (bop == 1 && img.channels() != 4)
{
return false;
}
memcpy(&m_chunkIHDR.p[8], &chunk.p[12], 8);
return true;
@@ -607,6 +616,15 @@ bool PngDecoder::nextPage() {
void PngDecoder::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)
{
const size_t elem_size = img.elemSize();
if (_bop == 0) {
// Overwrite mode: copy source row directly to destination
for(uint32_t j = 0; j < h; ++j) {
std::memcpy(rows_dst[j + y] + x * elem_size,rows_src[j], w * elem_size);
}
return;
}
int channels = img.channels();
if (img.depth() == CV_16U)
cv::parallel_for_(cv::Range(0, h), [&](const cv::Range& range) {
@@ -614,30 +632,24 @@ void PngDecoder::compose_frame(std::vector<png_bytep>& rows_dst, const std::vect
uint16_t* sp = reinterpret_cast<uint16_t*>(rows_src[j]);
uint16_t* dp = reinterpret_cast<uint16_t*>(rows_dst[j + y]) + x * channels;
if (_bop == 0) {
// Overwrite mode: copy source row directly to destination
memcpy(dp, sp, w * channels * sizeof(uint16_t));
}
else {
// Blending mode
for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) {
if (sp[3] == 65535) { // Fully opaque in 16-bit (max value)
memcpy(dp, sp, channels * sizeof(uint16_t));
// Blending mode
for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) {
if (channels < 4 || sp[3] == 65535) { // Fully opaque in 16-bit (max value)
memcpy(dp, sp, elem_size);
}
else if (sp[3] != 0) { // Partially transparent
if (dp[3] != 0) { // Both source and destination have alpha
uint32_t u = sp[3] * 65535; // 16-bit max
uint32_t v = (65535 - sp[3]) * dp[3];
uint32_t al = u + v;
dp[0] = static_cast<uint16_t>((sp[0] * u + dp[0] * v) / al); // Red
dp[1] = static_cast<uint16_t>((sp[1] * u + dp[1] * v) / al); // Green
dp[2] = static_cast<uint16_t>((sp[2] * u + dp[2] * v) / al); // Blue
dp[3] = static_cast<uint16_t>(al / 65535); // Alpha
}
else if (sp[3] != 0) { // Partially transparent
if (dp[3] != 0) { // Both source and destination have alpha
uint32_t u = sp[3] * 65535; // 16-bit max
uint32_t v = (65535 - sp[3]) * dp[3];
uint32_t al = u + v;
dp[0] = static_cast<uint16_t>((sp[0] * u + dp[0] * v) / al); // Red
dp[1] = static_cast<uint16_t>((sp[1] * u + dp[1] * v) / al); // Green
dp[2] = static_cast<uint16_t>((sp[2] * u + dp[2] * v) / al); // Blue
dp[3] = static_cast<uint16_t>(al / 65535); // Alpha
}
else {
// If destination alpha is 0, copy source pixel
memcpy(dp, sp, channels * sizeof(uint16_t));
}
else {
// If destination alpha is 0, copy source pixel
memcpy(dp, sp, elem_size);
}
}
}
@@ -649,32 +661,26 @@ void PngDecoder::compose_frame(std::vector<png_bytep>& rows_dst, const std::vect
unsigned char* sp = rows_src[j];
unsigned char* dp = rows_dst[j + y] + x * channels;
if (_bop == 0) {
// Overwrite mode: copy source row directly to destination
memcpy(dp, sp, w * channels);
}
else {
// Blending mode
for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) {
if (sp[3] == 255) {
// Fully opaque: copy source pixel directly
memcpy(dp, sp, channels);
// Blending mode
for (unsigned int i = 0; i < w; i++, sp += channels, dp += channels) {
if (channels < 4 || sp[3] == 255) {
// Fully opaque: copy source pixel directly
memcpy(dp, sp, elem_size);
}
else if (sp[3] != 0) {
// Alpha blending
if (dp[3] != 0) {
int u = sp[3] * 255;
int v = (255 - sp[3]) * dp[3];
int al = u + v;
dp[0] = (sp[0] * u + dp[0] * v) / al; // Red
dp[1] = (sp[1] * u + dp[1] * v) / al; // Green
dp[2] = (sp[2] * u + dp[2] * v) / al; // Blue
dp[3] = al / 255; // Alpha
}
else if (sp[3] != 0) {
// Alpha blending
if (dp[3] != 0) {
int u = sp[3] * 255;
int v = (255 - sp[3]) * dp[3];
int al = u + v;
dp[0] = (sp[0] * u + dp[0] * v) / al; // Red
dp[1] = (sp[1] * u + dp[1] * v) / al; // Green
dp[2] = (sp[2] * u + dp[2] * v) / al; // Blue
dp[3] = al / 255; // Alpha
}
else {
// If destination alpha is 0, copy source pixel
memcpy(dp, sp, channels);
}
else {
// If destination alpha is 0, copy source pixel
memcpy(dp, sp, elem_size);
}
}
}