mirror of
https://github.com/opencv/opencv.git
synced 2025-07-30 17:37:05 +08:00
Merge pull request #26844 from Kumataro:fix26843
imgcodecs: jpegxl: imdecode() directly read from memory #26844 Close #26843 ### 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:
parent
ae57c54d83
commit
c840e24e94
@ -28,13 +28,15 @@ static void cbRGBAtoGRAY_32F(void *opaque, size_t x, size_t y, size_t num_pixels
|
||||
|
||||
/////////////////////// JpegXLDecoder ///////////////////
|
||||
|
||||
JpegXLDecoder::JpegXLDecoder() : m_f(nullptr, &fclose)
|
||||
JpegXLDecoder::JpegXLDecoder() : m_f(nullptr, &fclose),
|
||||
m_read_buffer(16384,0) // 16KB chunks
|
||||
{
|
||||
m_signature = "\xFF\x0A";
|
||||
m_decoder = nullptr;
|
||||
m_buf_supported = false;
|
||||
m_buf_supported = true;
|
||||
m_type = -1;
|
||||
m_status = JXL_DEC_NEED_MORE_INPUT;
|
||||
m_is_mbuf_set = false;
|
||||
}
|
||||
|
||||
JpegXLDecoder::~JpegXLDecoder()
|
||||
@ -52,6 +54,7 @@ void JpegXLDecoder::close()
|
||||
m_width = m_height = 0;
|
||||
m_type = -1;
|
||||
m_status = JXL_DEC_NEED_MORE_INPUT;
|
||||
m_is_mbuf_set = false;
|
||||
}
|
||||
|
||||
// see https://github.com/libjxl/libjxl/blob/v0.10.0/doc/format_overview.md
|
||||
@ -91,11 +94,14 @@ ImageDecoder JpegXLDecoder::newDecoder() const
|
||||
|
||||
bool JpegXLDecoder::readHeader()
|
||||
{
|
||||
// Open file
|
||||
if (!m_f) {
|
||||
m_f.reset(fopen(m_filename.c_str(), "rb"));
|
||||
if (!m_f)
|
||||
return false;
|
||||
if (m_buf.empty()) {
|
||||
// Open file
|
||||
if (!m_f) {
|
||||
m_f.reset(fopen(m_filename.c_str(), "rb"));
|
||||
if (!m_f) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize decoder
|
||||
@ -119,6 +125,9 @@ bool JpegXLDecoder::readHeader()
|
||||
}
|
||||
}
|
||||
|
||||
// Reset to read header data stream
|
||||
m_is_mbuf_set = false;
|
||||
|
||||
return read();
|
||||
}
|
||||
|
||||
@ -195,38 +204,53 @@ bool JpegXLDecoder::readData(Mat& img)
|
||||
// Common reading routine for readHeader() and readBody()
|
||||
bool JpegXLDecoder::read()
|
||||
{
|
||||
// Create buffer for reading
|
||||
const size_t read_buffer_size = 16384; // 16KB chunks
|
||||
if (m_read_buffer.capacity() < read_buffer_size)
|
||||
m_read_buffer.resize(read_buffer_size);
|
||||
|
||||
// Start decoding loop
|
||||
do {
|
||||
// Check if we need more input
|
||||
if (m_status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
size_t remaining = JxlDecoderReleaseInput(m_decoder.get());
|
||||
// Move any remaining bytes to the beginning
|
||||
if (remaining > 0)
|
||||
memmove(m_read_buffer.data(), m_read_buffer.data() + m_read_buffer.size() - remaining, remaining);
|
||||
// Read more data from file
|
||||
size_t bytes_read = fread(m_read_buffer.data() + remaining,
|
||||
1, m_read_buffer.size() - remaining, m_f.get());
|
||||
if (bytes_read == 0) {
|
||||
if (ferror(m_f.get())) {
|
||||
CV_LOG_WARNING(NULL, "Error reading input file");
|
||||
uint8_t* data_ptr = nullptr;
|
||||
size_t data_len = 0;
|
||||
|
||||
if( !m_buf.empty() ) {
|
||||
// When data source in on memory
|
||||
if (m_is_mbuf_set) {
|
||||
// We expect m_buf contains whole JpegXL data stream.
|
||||
// If it had been truncated, m_status will be JXL_DEC_NEED_MORE_INPUT again.
|
||||
CV_LOG_WARNING(NULL, "Truncated JXL data in memory");
|
||||
return false;
|
||||
}
|
||||
// If we reached EOF but decoder needs more input, file is truncated
|
||||
if (m_status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
CV_LOG_WARNING(NULL, "Truncated JXL file");
|
||||
return false;
|
||||
data_ptr = m_buf.ptr();
|
||||
data_len = m_buf.total();
|
||||
m_is_mbuf_set = true;
|
||||
}
|
||||
else {
|
||||
// When data source is on file
|
||||
// Release input buffer if it had been set already. If not, there are no errors.
|
||||
size_t remaining = JxlDecoderReleaseInput(m_decoder.get());
|
||||
// Move any remaining bytes to the beginning
|
||||
if (remaining > 0)
|
||||
memmove(m_read_buffer.data(), m_read_buffer.data() + m_read_buffer.size() - remaining, remaining);
|
||||
// Read more data from file
|
||||
size_t bytes_read = fread(m_read_buffer.data() + remaining,
|
||||
1, m_read_buffer.size() - remaining, m_f.get());
|
||||
if (bytes_read == 0) {
|
||||
if (ferror(m_f.get())) {
|
||||
CV_LOG_WARNING(NULL, "Error reading input file");
|
||||
return false;
|
||||
}
|
||||
// If we reached EOF but decoder needs more input, file is truncated
|
||||
if (m_status == JXL_DEC_NEED_MORE_INPUT) {
|
||||
CV_LOG_WARNING(NULL, "Truncated JXL file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
data_ptr = m_read_buffer.data();
|
||||
data_len = bytes_read + remaining;
|
||||
}
|
||||
|
||||
// Set input buffer
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetInput(m_decoder.get(),
|
||||
m_read_buffer.data(),
|
||||
bytes_read + remaining)) {
|
||||
// It must be kept until calling JxlDecoderReleaseInput() or m_decoder.reset().
|
||||
if (JXL_DEC_SUCCESS != JxlDecoderSetInput(m_decoder.get(), data_ptr, data_len)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ protected:
|
||||
JxlPixelFormat m_format;
|
||||
std::vector<uint8_t> m_read_buffer;
|
||||
JxlDecoderStatus m_status;
|
||||
bool m_is_mbuf_set;
|
||||
|
||||
private:
|
||||
bool read();
|
||||
|
@ -248,6 +248,63 @@ INSTANTIATE_TEST_CASE_P(
|
||||
)
|
||||
) );
|
||||
|
||||
TEST(Imgcodecs_JpegXL, imdecode_truncated_stream)
|
||||
{
|
||||
cv::Mat src(100, 100, CV_8UC1, Scalar(40,50,10));
|
||||
vector<uint8_t> buff;
|
||||
vector<int> param;
|
||||
|
||||
bool ret = false;
|
||||
EXPECT_NO_THROW(ret = cv::imencode(".jxl", src, buff, param));
|
||||
EXPECT_TRUE(ret);
|
||||
|
||||
// Try to decode non-truncated image.
|
||||
cv::Mat decoded;
|
||||
EXPECT_NO_THROW(decoded = cv::imdecode(buff, cv::IMREAD_COLOR));
|
||||
EXPECT_FALSE(decoded.empty());
|
||||
|
||||
// Try to decode truncated image.
|
||||
buff.resize(buff.size() - 1 );
|
||||
EXPECT_NO_THROW(decoded = cv::imdecode(buff, cv::IMREAD_COLOR));
|
||||
EXPECT_TRUE(decoded.empty());
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_JpegXL, imread_truncated_stream)
|
||||
{
|
||||
string tmp_fname = cv::tempfile(".jxl");
|
||||
cv::Mat src(100, 100, CV_8UC1, Scalar(40,50,10));
|
||||
vector<uint8_t> buff;
|
||||
vector<int> param;
|
||||
|
||||
bool ret = false;
|
||||
EXPECT_NO_THROW(ret = cv::imencode(".jxl", src, buff, param));
|
||||
EXPECT_TRUE(ret);
|
||||
|
||||
// Try to decode non-truncated image.
|
||||
FILE *fp = nullptr;
|
||||
|
||||
fp = fopen(tmp_fname.c_str(), "wb");
|
||||
EXPECT_TRUE(fp != nullptr);
|
||||
fwrite(&buff[0], sizeof(uint8_t), buff.size(), fp);
|
||||
fclose(fp);
|
||||
|
||||
cv::Mat decoded;
|
||||
EXPECT_NO_THROW(decoded = cv::imread(tmp_fname, cv::IMREAD_COLOR));
|
||||
EXPECT_FALSE(decoded.empty());
|
||||
|
||||
// Try to decode truncated image.
|
||||
fp = fopen(tmp_fname.c_str(), "wb");
|
||||
EXPECT_TRUE(fp != nullptr);
|
||||
fwrite(&buff[0], sizeof(uint8_t), buff.size() - 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
EXPECT_NO_THROW(decoded = cv::imread(tmp_fname, cv::IMREAD_COLOR));
|
||||
EXPECT_TRUE(decoded.empty());
|
||||
|
||||
// Delete temporary file
|
||||
remove(tmp_fname.c_str());
|
||||
}
|
||||
|
||||
|
||||
#endif // HAVE_JPEGXL
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user