diff --git a/modules/imgcodecs/src/grfmt_png.cpp b/modules/imgcodecs/src/grfmt_png.cpp index 824cf961f9..5792f2a4d7 100644 --- a/modules/imgcodecs/src/grfmt_png.cpp +++ b/modules/imgcodecs/src/grfmt_png.cpp @@ -490,8 +490,11 @@ bool PngDecoder::readData( Mat& img ) { memcpy(frameNext.getPixels(), frameCur.getPixels(), imagesize); if (dop == 1) + { + const size_t elem_size = mat_cur.elemSize(); for (j = 0; j < h0; j++) - memset(frameNext.getRows()[y0 + j] + x0 * img.channels(), 0, w0 * img.channels()); + memset(frameNext.getRows()[y0 + j] + x0 * elem_size, 0, w0 * elem_size); + } } } else diff --git a/modules/imgcodecs/test/test_animation.cpp b/modules/imgcodecs/test/test_animation.cpp index 2d45132ffd..6cc5d54fb3 100644 --- a/modules/imgcodecs/test/test_animation.cpp +++ b/modules/imgcodecs/test/test_animation.cpp @@ -682,6 +682,56 @@ TEST(Imgcodecs_APNG, animation_imread_preview) EXPECT_EQ(0, cv::norm(animation.still_image, imread_result, cv::NORM_INF)); } +TEST(Imgcodecs_APNG, decode_dispose_background_gray) +{ + Animation animation; + Mat frame(8, 64, CV_8UC1, Scalar(192)); + frame(Rect(0, 0, 32, 8)).setTo(Scalar(64)); + animation.frames.push_back(frame.clone()); + animation.durations.push_back(100); + frame.setTo(Scalar(192)); + animation.frames.push_back(frame.clone()); + animation.durations.push_back(100); + frame.at(0, 0) = 255; + animation.frames.push_back(frame.clone()); + animation.durations.push_back(100); + + std::vector buf; + ASSERT_TRUE(imencodeanimation(".png", animation, buf)); + + // Give the second frame APNG_DISPOSE_OP_BACKGROUND, which the encoder does not + // pick for these frames. dispose_op is the 25th byte of the fcTL payload. + int fcTL_count = 0; + for (size_t i = 8; i + 12 <= buf.size(); ) + { + const size_t len = ((size_t)buf[i] << 24) | ((size_t)buf[i + 1] << 16) | + ((size_t)buf[i + 2] << 8) | (size_t)buf[i + 3]; + if (buf[i + 4] == 'f' && buf[i + 5] == 'c' && buf[i + 6] == 'T' && buf[i + 7] == 'L') + { + if (fcTL_count == 1) + buf[i + 8 + 24] = 1; + fcTL_count++; + } + i += len + 12; + } + ASSERT_EQ(3, fcTL_count); + + // The disposed region is the one named by fcTL, whatever the caller asked the + // decoder to convert the frames to. + std::vector gray, color; + ASSERT_TRUE(imdecodemulti(buf, IMREAD_UNCHANGED, gray)); + ASSERT_TRUE(imdecodemulti(buf, IMREAD_COLOR, color)); + ASSERT_EQ(animation.frames.size(), gray.size()); + ASSERT_EQ(gray.size(), color.size()); + + for (size_t i = 0; i < gray.size(); i++) + { + Mat expected; + cvtColor(gray[i], expected, COLOR_GRAY2BGR); + EXPECT_EQ(0, cvtest::norm(expected, color[i], NORM_INF)) << "frame " << i; + } +} + #endif // HAVE_PNG #if defined(HAVE_PNG) || defined(HAVE_SPNG)