BinaryWriter.cpp 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. /*
  2. * Copyright (c) 2023, Nico Weber <thakis@chromium.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Utf16View.h>
  7. #include <LibGfx/ICC/BinaryFormat.h>
  8. #include <LibGfx/ICC/BinaryWriter.h>
  9. #include <LibGfx/ICC/Profile.h>
  10. #include <time.h>
  11. #pragma GCC diagnostic ignored "-Warray-bounds"
  12. namespace Gfx::ICC {
  13. static ErrorOr<ByteBuffer> encode_multi_localized_unicode(MultiLocalizedUnicodeTagData const& tag_data)
  14. {
  15. // ICC v4, 10.15 multiLocalizedUnicodeType
  16. // "The Unicode strings in storage should be encoded as 16-bit big-endian, UTF-16BE,
  17. // and should not be NULL terminated."
  18. size_t number_of_records = tag_data.records().size();
  19. size_t header_and_record_size = 4 * sizeof(u32) + number_of_records * sizeof(MultiLocalizedUnicodeRawRecord);
  20. size_t number_of_codepoints = 0;
  21. Vector<Utf16Data> utf16_strings;
  22. TRY(utf16_strings.try_ensure_capacity(number_of_records));
  23. for (auto const& record : tag_data.records()) {
  24. TRY(utf16_strings.try_append(TRY(utf8_to_utf16(record.text))));
  25. number_of_codepoints += utf16_strings.last().size();
  26. }
  27. size_t string_table_size = number_of_codepoints * sizeof(u16);
  28. auto bytes = TRY(ByteBuffer::create_uninitialized(header_and_record_size + string_table_size));
  29. auto* header = bit_cast<BigEndian<u32>*>(bytes.data());
  30. header[0] = (u32)MultiLocalizedUnicodeTagData::Type;
  31. header[1] = 0;
  32. header[2] = number_of_records;
  33. header[3] = sizeof(MultiLocalizedUnicodeRawRecord);
  34. size_t offset = header_and_record_size;
  35. auto* records = bit_cast<MultiLocalizedUnicodeRawRecord*>(bytes.data() + 16);
  36. for (size_t i = 0; i < number_of_records; ++i) {
  37. records[i].language_code = tag_data.records()[i].iso_639_1_language_code;
  38. records[i].country_code = tag_data.records()[i].iso_3166_1_country_code;
  39. records[i].string_length_in_bytes = utf16_strings[i].size() * sizeof(u16);
  40. records[i].string_offset_in_bytes = offset;
  41. offset += records[i].string_offset_in_bytes;
  42. }
  43. auto* string_table = bit_cast<BigEndian<u16>*>(bytes.data() + header_and_record_size);
  44. for (auto const& utf16_string : utf16_strings) {
  45. for (size_t i = 0; i < utf16_string.size(); ++i)
  46. string_table[i] = utf16_string[i];
  47. string_table += utf16_string.size();
  48. }
  49. return bytes;
  50. }
  51. static ErrorOr<ByteBuffer> encode_s15_fixed_array(S15Fixed16ArrayTagData const& tag_data)
  52. {
  53. // ICC v4, 10.22 s15Fixed16ArrayType
  54. auto bytes = TRY(ByteBuffer::create_uninitialized(2 * sizeof(u32) + tag_data.values().size() * sizeof(s15Fixed16Number)));
  55. *bit_cast<BigEndian<u32>*>(bytes.data()) = (u32)S15Fixed16ArrayTagData::Type;
  56. *bit_cast<BigEndian<u32>*>(bytes.data() + 4) = 0;
  57. auto* values = bit_cast<BigEndian<s15Fixed16Number>*>(bytes.data() + 8);
  58. for (size_t i = 0; i < tag_data.values().size(); ++i)
  59. values[i] = tag_data.values()[i].raw();
  60. return bytes;
  61. }
  62. static ErrorOr<ByteBuffer> encode_xyz(XYZTagData const& tag_data)
  63. {
  64. // ICC v4, 10.31 XYZType
  65. auto bytes = TRY(ByteBuffer::create_uninitialized(2 * sizeof(u32) + tag_data.xyzs().size() * sizeof(XYZNumber)));
  66. *bit_cast<BigEndian<u32>*>(bytes.data()) = (u32)XYZTagData::Type;
  67. *bit_cast<BigEndian<u32>*>(bytes.data() + 4) = 0;
  68. auto* xyzs = bit_cast<XYZNumber*>(bytes.data() + 8);
  69. for (size_t i = 0; i < tag_data.xyzs().size(); ++i)
  70. xyzs[i] = tag_data.xyzs()[i];
  71. return bytes;
  72. }
  73. static ErrorOr<ByteBuffer> encode_tag_data(TagData const& tag_data)
  74. {
  75. switch (tag_data.type()) {
  76. case MultiLocalizedUnicodeTagData::Type:
  77. return encode_multi_localized_unicode(static_cast<MultiLocalizedUnicodeTagData const&>(tag_data));
  78. case S15Fixed16ArrayTagData::Type:
  79. return encode_s15_fixed_array(static_cast<S15Fixed16ArrayTagData const&>(tag_data));
  80. case XYZTagData::Type:
  81. return encode_xyz(static_cast<XYZTagData const&>(tag_data));
  82. }
  83. return ByteBuffer {};
  84. }
  85. static ErrorOr<Vector<ByteBuffer>> encode_tag_datas(Profile const& profile)
  86. {
  87. Vector<ByteBuffer> tag_data_bytes;
  88. // FIXME: If two tags refer to the same TagData object, write it just once to the output.
  89. TRY(tag_data_bytes.try_resize(profile.tag_count()));
  90. size_t i = 0;
  91. profile.for_each_tag([&](auto, auto tag_data) {
  92. // FIXME: Come up with a way to allow TRY instead of MUST here.
  93. tag_data_bytes[i++] = MUST(encode_tag_data(tag_data));
  94. });
  95. return tag_data_bytes;
  96. }
  97. static ErrorOr<void> encode_tag_table(ByteBuffer& bytes, Profile const& profile, Vector<size_t> const& offsets, Vector<ByteBuffer> const& tag_data_bytes)
  98. {
  99. VERIFY(bytes.size() >= sizeof(ICCHeader) + sizeof(u32) + profile.tag_count() * sizeof(TagTableEntry));
  100. *bit_cast<BigEndian<u32>*>(bytes.data() + sizeof(ICCHeader)) = profile.tag_count();
  101. TagTableEntry* tag_table_entries = bit_cast<TagTableEntry*>(bytes.data() + sizeof(ICCHeader) + sizeof(u32));
  102. int i = 0;
  103. profile.for_each_tag([&](auto tag_signature, auto) {
  104. tag_table_entries[i].tag_signature = tag_signature;
  105. tag_table_entries[i].offset_to_beginning_of_tag_data_element = offsets[i];
  106. tag_table_entries[i].size_of_tag_data_element = tag_data_bytes[i].size();
  107. ++i;
  108. });
  109. return {};
  110. }
  111. static ErrorOr<void> encode_header(ByteBuffer& bytes, Profile const& profile)
  112. {
  113. VERIFY(bytes.size() >= sizeof(ICCHeader));
  114. auto& raw_header = *bit_cast<ICCHeader*>(bytes.data());
  115. raw_header.profile_size = bytes.size();
  116. raw_header.preferred_cmm_type = profile.preferred_cmm_type().value_or(PreferredCMMType { 0 });
  117. raw_header.profile_version_major = profile.version().major_version();
  118. raw_header.profile_version_minor_bugfix = profile.version().minor_and_bugfix_version();
  119. raw_header.profile_version_zero = 0;
  120. raw_header.profile_device_class = profile.device_class();
  121. raw_header.data_color_space = profile.data_color_space();
  122. raw_header.profile_connection_space = profile.connection_space();
  123. time_t profile_timestamp = profile.creation_timestamp();
  124. struct tm tm;
  125. if (!gmtime_r(&profile_timestamp, &tm))
  126. return Error::from_errno(errno);
  127. raw_header.profile_creation_time.year = tm.tm_year + 1900;
  128. raw_header.profile_creation_time.month = tm.tm_mon + 1;
  129. raw_header.profile_creation_time.day = tm.tm_mday;
  130. raw_header.profile_creation_time.hours = tm.tm_hour;
  131. raw_header.profile_creation_time.minutes = tm.tm_min;
  132. raw_header.profile_creation_time.seconds = tm.tm_sec;
  133. raw_header.profile_file_signature = ProfileFileSignature;
  134. raw_header.primary_platform = profile.primary_platform().value_or(PrimaryPlatform { 0 });
  135. raw_header.profile_flags = profile.flags().bits();
  136. raw_header.device_manufacturer = profile.device_manufacturer().value_or(DeviceManufacturer { 0 });
  137. raw_header.device_model = profile.device_model().value_or(DeviceModel { 0 });
  138. raw_header.device_attributes = profile.device_attributes().bits();
  139. raw_header.rendering_intent = profile.rendering_intent();
  140. raw_header.pcs_illuminant = profile.pcs_illuminant();
  141. raw_header.profile_creator = profile.creator().value_or(Creator { 0 });
  142. memset(raw_header.reserved, 0, sizeof(raw_header.reserved));
  143. auto id = Profile::compute_id(bytes);
  144. static_assert(sizeof(id.data) == sizeof(raw_header.profile_id));
  145. memcpy(raw_header.profile_id, id.data, sizeof(id.data));
  146. return {};
  147. }
  148. ErrorOr<ByteBuffer> encode(Profile const& profile)
  149. {
  150. // Valid profiles always have tags. Profile only represents valid profiles.
  151. VERIFY(profile.tag_count() > 0);
  152. Vector<ByteBuffer> tag_data_bytes = TRY(encode_tag_datas(profile));
  153. size_t tag_table_size = sizeof(u32) + profile.tag_count() * sizeof(TagTableEntry);
  154. size_t offset = sizeof(ICCHeader) + tag_table_size;
  155. Vector<size_t> offsets;
  156. for (auto const& bytes : tag_data_bytes) {
  157. TRY(offsets.try_append(offset));
  158. offset += align_up_to(bytes.size(), 4);
  159. }
  160. // Omit padding after last element.
  161. // FIXME: Is that correct?
  162. size_t total_size = offsets.last() + tag_data_bytes.last().size();
  163. // Leave enough room for the profile header and the tag table count.
  164. auto bytes = TRY(ByteBuffer::create_zeroed(total_size));
  165. for (size_t i = 0; i < tag_data_bytes.size(); ++i)
  166. memcpy(bytes.data() + offsets[i], tag_data_bytes[i].data(), tag_data_bytes[i].size());
  167. TRY(encode_tag_table(bytes, profile, offsets, tag_data_bytes));
  168. TRY(encode_header(bytes, profile));
  169. return bytes;
  170. }
  171. }