summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
authorNico Weber <thakis@chromium.org>2023-02-18 20:23:04 -0500
committerSam Atkins <atkinssj@gmail.com>2023-02-19 08:12:04 +0000
commit19e91e5211ea2394af85e694b4925d5976f671b2 (patch)
treee986f6725e374c0c455999ce79deee4446b60bbd /Userland
parent2504f2035c20e01f2cf6d75d6dc0b278a5f91671 (diff)
downloadserenity-19e91e5211ea2394af85e694b4925d5976f671b2.zip
LibGfx: Dedupe identical tag data objects when writing ICC data
With this, common v4 profiles, such as embedded into jpgs by iPhones (when configured to write jpegs) or Pixel phones, are identical to the input when reexported :^)
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibGfx/ICC/BinaryWriter.cpp27
1 files changed, 16 insertions, 11 deletions
diff --git a/Userland/Libraries/LibGfx/ICC/BinaryWriter.cpp b/Userland/Libraries/LibGfx/ICC/BinaryWriter.cpp
index 6af21719d5..df861cbdb5 100644
--- a/Userland/Libraries/LibGfx/ICC/BinaryWriter.cpp
+++ b/Userland/Libraries/LibGfx/ICC/BinaryWriter.cpp
@@ -238,21 +238,23 @@ static ErrorOr<ByteBuffer> encode_tag_data(TagData const& tag_data)
return ByteBuffer {};
}
-static ErrorOr<Vector<ByteBuffer>> encode_tag_datas(Profile const& profile)
+static ErrorOr<Vector<ByteBuffer>> encode_tag_datas(Profile const& profile, HashMap<TagData*, size_t>& tag_data_map)
{
Vector<ByteBuffer> tag_data_bytes;
+ TRY(tag_data_bytes.try_ensure_capacity(profile.tag_count()));
- // FIXME: If two tags refer to the same TagData object, write it just once to the output.
- TRY(tag_data_bytes.try_resize(profile.tag_count()));
- size_t i = 0;
profile.for_each_tag([&](auto, auto tag_data) {
+ if (tag_data_map.contains(tag_data.ptr()))
+ return;
+
// FIXME: Come up with a way to allow TRY instead of MUST here.
- tag_data_bytes[i++] = MUST(encode_tag_data(tag_data));
+ tag_data_bytes.append(MUST(encode_tag_data(tag_data)));
+ MUST(tag_data_map.try_set(tag_data.ptr(), tag_data_bytes.size() - 1));
});
return tag_data_bytes;
}
-static ErrorOr<void> encode_tag_table(ByteBuffer& bytes, Profile const& profile, Vector<size_t> const& offsets, Vector<ByteBuffer> const& tag_data_bytes)
+static ErrorOr<void> encode_tag_table(ByteBuffer& bytes, Profile const& profile, Vector<size_t> const& offsets, Vector<ByteBuffer> const& tag_data_bytes, HashMap<TagData*, size_t> const& tag_data_map)
{
VERIFY(bytes.size() >= sizeof(ICCHeader) + sizeof(u32) + profile.tag_count() * sizeof(TagTableEntry));
@@ -260,10 +262,12 @@ static ErrorOr<void> encode_tag_table(ByteBuffer& bytes, Profile const& profile,
TagTableEntry* tag_table_entries = bit_cast<TagTableEntry*>(bytes.data() + sizeof(ICCHeader) + sizeof(u32));
int i = 0;
- profile.for_each_tag([&](auto tag_signature, auto) {
+ profile.for_each_tag([&](auto tag_signature, auto tag_data) {
tag_table_entries[i].tag_signature = tag_signature;
- tag_table_entries[i].offset_to_beginning_of_tag_data_element = offsets[i];
- tag_table_entries[i].size_of_tag_data_element = tag_data_bytes[i].size();
+
+ auto index = tag_data_map.get(tag_data.ptr()).value();
+ tag_table_entries[i].offset_to_beginning_of_tag_data_element = offsets[index];
+ tag_table_entries[i].size_of_tag_data_element = tag_data_bytes[index].size();
++i;
});
@@ -324,7 +328,8 @@ ErrorOr<ByteBuffer> encode(Profile const& profile)
// Valid profiles always have tags. Profile only represents valid profiles.
VERIFY(profile.tag_count() > 0);
- Vector<ByteBuffer> tag_data_bytes = TRY(encode_tag_datas(profile));
+ HashMap<TagData*, size_t> tag_data_map;
+ Vector<ByteBuffer> tag_data_bytes = TRY(encode_tag_datas(profile, tag_data_map));
size_t tag_table_size = sizeof(u32) + profile.tag_count() * sizeof(TagTableEntry);
size_t offset = sizeof(ICCHeader) + tag_table_size;
@@ -344,7 +349,7 @@ ErrorOr<ByteBuffer> encode(Profile const& profile)
for (size_t i = 0; i < tag_data_bytes.size(); ++i)
memcpy(bytes.data() + offsets[i], tag_data_bytes[i].data(), tag_data_bytes[i].size());
- TRY(encode_tag_table(bytes, profile, offsets, tag_data_bytes));
+ TRY(encode_tag_table(bytes, profile, offsets, tag_data_bytes, tag_data_map));
TRY(encode_header(bytes, profile));
return bytes;