mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 01:57:45 +00:00
LibGfx: Support ICCs in JPEGs that are split across several tags
Most jpegs that use multi-tag ICCs are in CMYK format, so running `icc` on them still produces Runtime error: Unsupported number of components in SOF but it no longer prints several jpg: Ignoring ICC profile spanning several chunks (1/9) lines before that. (And if I manually comment out that error message, the profile is printed fine. But we can't decode the actual pixel data yet.)
This commit is contained in:
parent
78def34c5e
commit
fef15becb2
1 changed files with 50 additions and 6 deletions
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <AK/Debug.h>
|
#include <AK/Debug.h>
|
||||||
#include <AK/Error.h>
|
#include <AK/Error.h>
|
||||||
|
#include <AK/FixedArray.h>
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
#include <AK/Math.h>
|
#include <AK/Math.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
|
@ -166,6 +167,11 @@ struct HuffmanStreamState {
|
||||||
size_t byte_offset { 0 };
|
size_t byte_offset { 0 };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ICCMultiChunkState {
|
||||||
|
u8 seen_number_of_icc_chunks { 0 };
|
||||||
|
FixedArray<ByteBuffer> chunks;
|
||||||
|
};
|
||||||
|
|
||||||
struct JPGLoadingContext {
|
struct JPGLoadingContext {
|
||||||
enum State {
|
enum State {
|
||||||
NotDecoded = 0,
|
NotDecoded = 0,
|
||||||
|
@ -193,6 +199,8 @@ struct JPGLoadingContext {
|
||||||
i32 previous_dc_values[3] = { 0 };
|
i32 previous_dc_values[3] = { 0 };
|
||||||
MacroblockMeta mblock_meta;
|
MacroblockMeta mblock_meta;
|
||||||
OwnPtr<Core::Stream::FixedMemoryStream> stream;
|
OwnPtr<Core::Stream::FixedMemoryStream> stream;
|
||||||
|
|
||||||
|
Optional<ICCMultiChunkState> icc_multi_chunk_state;
|
||||||
Optional<ByteBuffer> icc_data;
|
Optional<ByteBuffer> icc_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -601,14 +609,50 @@ static ErrorOr<void> read_icc_profile(Core::Stream::SeekableStream& stream, JPGL
|
||||||
auto number_of_chunks = TRY(stream.read_value<u8>());
|
auto number_of_chunks = TRY(stream.read_value<u8>());
|
||||||
bytes_to_read -= 2;
|
bytes_to_read -= 2;
|
||||||
|
|
||||||
// FIXME: Support ICC profiles larger than a single jpeg marker. (They are very rare in practice.)
|
if (!context.icc_multi_chunk_state.has_value())
|
||||||
if (number_of_chunks != 1) {
|
context.icc_multi_chunk_state.emplace(ICCMultiChunkState { 0, TRY(FixedArray<ByteBuffer>::create(number_of_chunks)) });
|
||||||
dbgln("jpg: Ignoring ICC profile spanning several chunks ({}/{})", chunk_sequence_number, number_of_chunks);
|
auto& chunk_state = context.icc_multi_chunk_state;
|
||||||
return stream.discard(bytes_to_read);
|
|
||||||
|
if (chunk_state->seen_number_of_icc_chunks >= number_of_chunks)
|
||||||
|
return Error::from_string_literal("Too many ICC chunks");
|
||||||
|
|
||||||
|
if (chunk_state->chunks.size() != number_of_chunks)
|
||||||
|
return Error::from_string_literal("Inconsistent number of total ICC chunks");
|
||||||
|
|
||||||
|
if (chunk_sequence_number == 0)
|
||||||
|
return Error::from_string_literal("ICC chunk sequence number not 1 based");
|
||||||
|
u8 index = chunk_sequence_number - 1;
|
||||||
|
|
||||||
|
if (index >= chunk_state->chunks.size())
|
||||||
|
return Error::from_string_literal("ICC chunk sequence number larger than number of chunks");
|
||||||
|
|
||||||
|
if (!chunk_state->chunks[index].is_empty())
|
||||||
|
return Error::from_string_literal("Duplicate ICC chunk at sequence number");
|
||||||
|
|
||||||
|
chunk_state->chunks[index] = TRY(ByteBuffer::create_zeroed(bytes_to_read));
|
||||||
|
TRY(stream.read_entire_buffer(chunk_state->chunks[index]));
|
||||||
|
|
||||||
|
chunk_state->seen_number_of_icc_chunks++;
|
||||||
|
|
||||||
|
if (chunk_state->seen_number_of_icc_chunks != chunk_state->chunks.size())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (number_of_chunks == 1) {
|
||||||
|
context.icc_data = move(chunk_state->chunks[0]);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t total_size = 0;
|
||||||
|
for (auto const& chunk : chunk_state->chunks)
|
||||||
|
total_size += chunk.size();
|
||||||
|
|
||||||
|
auto icc_bytes = TRY(ByteBuffer::create_zeroed(total_size));
|
||||||
|
size_t start = 0;
|
||||||
|
for (auto const& chunk : chunk_state->chunks) {
|
||||||
|
memcpy(icc_bytes.data() + start, chunk.data(), chunk.size());
|
||||||
|
start += chunk.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuffer icc_bytes = TRY(ByteBuffer::create_zeroed(bytes_to_read));
|
|
||||||
TRY(stream.read_entire_buffer(icc_bytes));
|
|
||||||
context.icc_data = move(icc_bytes);
|
context.icc_data = move(icc_bytes);
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue