From 81cc64f29c95362b4865f2d89a60780783caa201 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Mon, 23 Jan 2023 19:32:06 -0500 Subject: [PATCH] LibGfx: Dedupe ICC TagData objects Several tags can refer to the same TagData. In particular, the rTRC, gTRC, bTRC tags usually all three refer to the same curve. Curve objects can be large, so allocate only a single TagData object in that case and make all tags point to it. (If we end up storing some cache in the curve object later on, this will also increase the effectiveness of that cache.) --- Userland/Libraries/LibGfx/ICCProfile.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Userland/Libraries/LibGfx/ICCProfile.cpp b/Userland/Libraries/LibGfx/ICCProfile.cpp index a6e5a14807..83885c4867 100644 --- a/Userland/Libraries/LibGfx/ICCProfile.cpp +++ b/Userland/Libraries/LibGfx/ICCProfile.cpp @@ -962,10 +962,7 @@ ErrorOr Profile::read_tag_table(ReadonlyBytes bytes) // to reach a four-byte boundary) and the byte offset of the following tag element, or the end of the file. // Duplicate tag signatures shall not be included in the tag table. // Tag data elements shall not partially overlap, so there shall be no part of any tag data element that falls within - // the range defined for another tag in the tag table. - // The tag table may contain multiple tags signatures that all reference the same tag data element offset, allowing - // efficient reuse of tag data elements. In such cases, both the offset and size of the tag data elements in the tag - // table shall be the same." + // the range defined for another tag in the tag table." ReadonlyBytes tag_table_bytes = bytes.slice(sizeof(ICCHeader)); @@ -986,10 +983,22 @@ ErrorOr Profile::read_tag_table(ReadonlyBytes bytes) return Error::from_string_literal("ICC::Profile: Not enough data for tag table entries"); auto tag_table_entries = bit_cast(tag_table_bytes.data()); + // "The tag table may contain multiple tags signatures that all reference the same tag data element offset, allowing + // efficient reuse of tag data elements." + HashMap> offset_to_tag_data; + for (u32 i = 0; i < tag_count; ++i) { // FIXME: optionally ignore tags with unknown signature - // FIXME: dedupe identical offset/sizes - auto tag_data = TRY(read_tag(bytes, tag_table_entries[i].offset_to_beginning_of_tag_data_element, tag_table_entries[i].size_of_tag_data_element)); + + // Dedupe identical offset/sizes. + NonnullRefPtr tag_data = TRY(offset_to_tag_data.try_ensure(tag_table_entries[i].offset_to_beginning_of_tag_data_element, [=, this]() { + return read_tag(bytes, tag_table_entries[i].offset_to_beginning_of_tag_data_element, tag_table_entries[i].size_of_tag_data_element); + })); + + // "In such cases, both the offset and size of the tag data elements in the tag table shall be the same." + if (tag_data->size() != tag_table_entries[i].size_of_tag_data_element) + return Error::from_string_literal("ICC::Profile: two tags have same offset but different sizes"); + // "Duplicate tag signatures shall not be included in the tag table." if (TRY(m_tag_table.try_set(tag_table_entries[i].tag_signature, move(tag_data))) != AK::HashSetResult::InsertedNewEntry) return Error::from_string_literal("ICC::Profile: duplicate tag signature");