ICCProfile.cpp 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255
  1. /*
  2. * Copyright (c) 2022, Nico Weber <thakis@chromium.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Endian.h>
  7. #include <LibGfx/ICCProfile.h>
  8. #include <LibTextCodec/Decoder.h>
  9. #include <math.h>
  10. #include <time.h>
  11. // V2 spec: https://color.org/specification/ICC.1-2001-04.pdf
  12. // V4 spec: https://color.org/specification/ICC.1-2022-05.pdf
  13. namespace Gfx::ICC {
  14. namespace {
  15. // ICC V4, 4.2 dateTimeNumber
  16. // "All the dateTimeNumber values in a profile shall be in Coordinated Universal Time [...]."
  17. struct DateTimeNumber {
  18. BigEndian<u16> year;
  19. BigEndian<u16> month;
  20. BigEndian<u16> day;
  21. BigEndian<u16> hours;
  22. BigEndian<u16> minutes;
  23. BigEndian<u16> seconds;
  24. };
  25. // ICC V4, 4.6 s15Fixed16Number
  26. using s15Fixed16Number = i32;
  27. // ICC V4, 4.14 XYZNumber
  28. struct XYZNumber {
  29. BigEndian<s15Fixed16Number> x;
  30. BigEndian<s15Fixed16Number> y;
  31. BigEndian<s15Fixed16Number> z;
  32. operator XYZ() const
  33. {
  34. return XYZ { x / (double)0x1'0000, y / (double)0x1'0000, z / (double)0x1'0000 };
  35. }
  36. };
  37. ErrorOr<time_t> parse_date_time_number(DateTimeNumber const& date_time)
  38. {
  39. // ICC V4, 4.2 dateTimeNumber
  40. // "Number of the month (1 to 12)"
  41. if (date_time.month < 1 || date_time.month > 12)
  42. return Error::from_string_literal("ICC::Profile: dateTimeNumber month out of bounds");
  43. // "Number of the day of the month (1 to 31)"
  44. if (date_time.day < 1 || date_time.day > 31)
  45. return Error::from_string_literal("ICC::Profile: dateTimeNumber day out of bounds");
  46. // "Number of hours (0 to 23)"
  47. if (date_time.hours > 23)
  48. return Error::from_string_literal("ICC::Profile: dateTimeNumber hours out of bounds");
  49. // "Number of minutes (0 to 59)"
  50. if (date_time.minutes > 59)
  51. return Error::from_string_literal("ICC::Profile: dateTimeNumber minutes out of bounds");
  52. // "Number of seconds (0 to 59)"
  53. // ICC profiles apparently can't be created during leap seconds (seconds would be 60 there, but the spec doesn't allow that).
  54. if (date_time.seconds > 59)
  55. return Error::from_string_literal("ICC::Profile: dateTimeNumber seconds out of bounds");
  56. struct tm tm = {};
  57. tm.tm_year = date_time.year - 1900;
  58. tm.tm_mon = date_time.month - 1;
  59. tm.tm_mday = date_time.day;
  60. tm.tm_hour = date_time.hours;
  61. tm.tm_min = date_time.minutes;
  62. tm.tm_sec = date_time.seconds;
  63. // timegm() doesn't read tm.tm_isdst, tm.tm_wday, and tm.tm_yday, no need to fill them in.
  64. time_t timestamp = timegm(&tm);
  65. if (timestamp == -1)
  66. return Error::from_string_literal("ICC::Profile: dateTimeNumber not representable as timestamp");
  67. return timestamp;
  68. }
  69. // ICC V4, 7.2 Profile header
  70. struct ICCHeader {
  71. BigEndian<u32> profile_size;
  72. BigEndian<PreferredCMMType> preferred_cmm_type;
  73. u8 profile_version_major;
  74. u8 profile_version_minor_bugfix;
  75. BigEndian<u16> profile_version_zero;
  76. BigEndian<DeviceClass> profile_device_class;
  77. BigEndian<ColorSpace> data_color_space;
  78. BigEndian<ColorSpace> profile_connection_space; // "PCS" in the spec.
  79. DateTimeNumber profile_creation_time;
  80. BigEndian<u32> profile_file_signature;
  81. BigEndian<PrimaryPlatform> primary_platform;
  82. BigEndian<u32> profile_flags;
  83. BigEndian<DeviceManufacturer> device_manufacturer;
  84. BigEndian<DeviceModel> device_model;
  85. BigEndian<u64> device_attributes;
  86. BigEndian<u32> rendering_intent;
  87. XYZNumber pcs_illuminant;
  88. BigEndian<Creator> profile_creator;
  89. u8 profile_id[16];
  90. u8 reserved[28];
  91. };
  92. static_assert(sizeof(ICCHeader) == 128);
  93. ErrorOr<u32> parse_size(ICCHeader const& header, ReadonlyBytes icc_bytes)
  94. {
  95. // ICC v4, 7.2.2 Profile size field
  96. // "The value in the profile size field shall be the exact size obtained by combining the profile header,
  97. // the tag table, and the tagged element data, including the pad bytes for the last tag."
  98. // Valid files have enough data for profile header and tag table entry count.
  99. if (header.profile_size < sizeof(ICCHeader) + sizeof(u32))
  100. return Error::from_string_literal("ICC::Profile: Profile size too small");
  101. if (header.profile_size > icc_bytes.size())
  102. return Error::from_string_literal("ICC::Profile: Profile size larger than input data");
  103. return header.profile_size;
  104. }
  105. Optional<PreferredCMMType> parse_preferred_cmm_type(ICCHeader const& header)
  106. {
  107. // ICC v4, 7.2.3 Preferred CMM type field
  108. // "This field may be used to identify the preferred CMM to be used.
  109. // If used, it shall match a CMM type signature registered in the ICC Tag Registry"
  110. // https://www.color.org/signatures2.xalter currently links to
  111. // https://www.color.org/registry/signature/TagRegistry-2021-03.pdf, which contains
  112. // some CMM signatures.
  113. // This requirement is often honored in practice, but not always. For example,
  114. // JPEGs exported in Adobe Lightroom contain profiles that set this to 'Lino',
  115. // which is not present in the "CMM Signatures" table in that PDF.
  116. // "If no preferred CMM is identified, this field shall be set to zero (00000000h)."
  117. if (header.preferred_cmm_type == PreferredCMMType { 0 })
  118. return {};
  119. return header.preferred_cmm_type;
  120. }
  121. ErrorOr<Version> parse_version(ICCHeader const& header)
  122. {
  123. // ICC v4, 7.2.4 Profile version field
  124. if (header.profile_version_zero != 0)
  125. return Error::from_string_literal("ICC::Profile: Reserved version bytes not zero");
  126. return Version(header.profile_version_major, header.profile_version_minor_bugfix);
  127. }
  128. ErrorOr<DeviceClass> parse_device_class(ICCHeader const& header)
  129. {
  130. // ICC v4, 7.2.5 Profile/device class field
  131. switch (header.profile_device_class) {
  132. case DeviceClass::InputDevice:
  133. case DeviceClass::DisplayDevice:
  134. case DeviceClass::OutputDevice:
  135. case DeviceClass::DeviceLink:
  136. case DeviceClass::ColorSpace:
  137. case DeviceClass::Abstract:
  138. case DeviceClass::NamedColor:
  139. return header.profile_device_class;
  140. }
  141. return Error::from_string_literal("ICC::Profile: Invalid device class");
  142. }
  143. ErrorOr<ColorSpace> parse_color_space(ColorSpace color_space)
  144. {
  145. // ICC v4, Table 19 — Data colour space signatures
  146. switch (color_space) {
  147. case ColorSpace::nCIEXYZ:
  148. case ColorSpace::CIELAB:
  149. case ColorSpace::CIELUV:
  150. case ColorSpace::YCbCr:
  151. case ColorSpace::CIEYxy:
  152. case ColorSpace::RGB:
  153. case ColorSpace::Gray:
  154. case ColorSpace::HSV:
  155. case ColorSpace::HLS:
  156. case ColorSpace::CMYK:
  157. case ColorSpace::CMY:
  158. case ColorSpace::TwoColor:
  159. case ColorSpace::ThreeColor:
  160. case ColorSpace::FourColor:
  161. case ColorSpace::FiveColor:
  162. case ColorSpace::SixColor:
  163. case ColorSpace::SevenColor:
  164. case ColorSpace::EightColor:
  165. case ColorSpace::NineColor:
  166. case ColorSpace::TenColor:
  167. case ColorSpace::ElevenColor:
  168. case ColorSpace::TwelveColor:
  169. case ColorSpace::ThirteenColor:
  170. case ColorSpace::FourteenColor:
  171. case ColorSpace::FifteenColor:
  172. return color_space;
  173. }
  174. return Error::from_string_literal("ICC::Profile: Invalid color space");
  175. }
  176. ErrorOr<ColorSpace> parse_data_color_space(ICCHeader const& header)
  177. {
  178. // ICC v4, 7.2.6 Data colour space field
  179. return parse_color_space(header.data_color_space);
  180. }
  181. ErrorOr<ColorSpace> parse_connection_space(ICCHeader const& header)
  182. {
  183. // ICC v4, 7.2.7 PCS field
  184. // and Annex D
  185. auto space = TRY(parse_color_space(header.profile_connection_space));
  186. if (header.profile_device_class != DeviceClass::DeviceLink && (space != ColorSpace::PCSXYZ && space != ColorSpace::PCSLAB))
  187. return Error::from_string_literal("ICC::Profile: Invalid profile connection space: Non-PCS space on non-DeviceLink profile");
  188. return space;
  189. }
  190. ErrorOr<time_t> parse_creation_date_time(ICCHeader const& header)
  191. {
  192. // ICC v4, 7.2.8 Date and time field
  193. return parse_date_time_number(header.profile_creation_time);
  194. }
  195. ErrorOr<void> parse_file_signature(ICCHeader const& header)
  196. {
  197. // ICC v4, 7.2.9 Profile file signature field
  198. // "The profile file signature field shall contain the value “acsp” (61637370h) as a profile file signature."
  199. if (header.profile_file_signature != 0x61637370)
  200. return Error::from_string_literal("ICC::Profile: profile file signature not 'acsp'");
  201. return {};
  202. }
  203. ErrorOr<PrimaryPlatform> parse_primary_platform(ICCHeader const& header)
  204. {
  205. // ICC v4, 7.2.10 Primary platform field
  206. switch (header.primary_platform) {
  207. case PrimaryPlatform::Apple:
  208. case PrimaryPlatform::Microsoft:
  209. case PrimaryPlatform::SiliconGraphics:
  210. case PrimaryPlatform::Sun:
  211. return header.primary_platform;
  212. }
  213. return Error::from_string_literal("ICC::Profile: Invalid primary platform");
  214. }
  215. Optional<DeviceManufacturer> parse_device_manufacturer(ICCHeader const& header)
  216. {
  217. // ICC v4, 7.2.12 Device manufacturer field
  218. // "This field may be used to identify a device manufacturer.
  219. // If used the signature shall match the signature contained in the appropriate section of the ICC signature registry found at www.color.org"
  220. // Device manufacturers can be looked up at https://www.color.org/signatureRegistry/index.xalter
  221. // For example: https://www.color.org/signatureRegistry/?entityEntry=APPL-4150504C
  222. // Some icc files use codes not in that registry. For example. D50_XYZ.icc from https://www.color.org/XYZprofiles.xalter
  223. // has its device manufacturer set to 'none', but https://www.color.org/signatureRegistry/?entityEntry=none-6E6F6E65 does not exist.
  224. // "If not used this field shall be set to zero (00000000h)."
  225. if (header.device_manufacturer == DeviceManufacturer { 0 })
  226. return {};
  227. return header.device_manufacturer;
  228. }
  229. Optional<DeviceModel> parse_device_model(ICCHeader const& header)
  230. {
  231. // ICC v4, 7.2.13 Device model field
  232. // "This field may be used to identify a device model.
  233. // If used the signature shall match the signature contained in the appropriate section of the ICC signature registry found at www.color.org"
  234. // Device models can be looked up at https://www.color.org/signatureRegistry/deviceRegistry/index.xalter
  235. // For example: https://www.color.org/signatureRegistry/deviceRegistry/?entityEntry=7FD8-37464438
  236. // Some icc files use codes not in that registry. For example. D50_XYZ.icc from https://www.color.org/XYZprofiles.xalter
  237. // has its device model set to 'none', but https://www.color.org/signatureRegistry/deviceRegistry?entityEntry=none-6E6F6E65 does not exist.
  238. // "If not used this field shall be set to zero (00000000h)."
  239. if (header.device_model == DeviceModel { 0 })
  240. return {};
  241. return header.device_model;
  242. }
  243. ErrorOr<DeviceAttributes> parse_device_attributes(ICCHeader const& header)
  244. {
  245. // ICC v4, 7.2.14 Device attributes field
  246. // "4 to 31": "Reserved (set to binary zero)"
  247. if (header.device_attributes & 0xffff'fff0)
  248. return Error::from_string_literal("ICC::Profile: Device attributes reserved bits not set to 0");
  249. return DeviceAttributes { header.device_attributes };
  250. }
  251. ErrorOr<RenderingIntent> parse_rendering_intent(ICCHeader const& header)
  252. {
  253. // ICC v4, 7.2.15 Rendering intent field
  254. switch (header.rendering_intent) {
  255. case 0:
  256. return RenderingIntent::Perceptual;
  257. case 1:
  258. return RenderingIntent::MediaRelativeColorimetric;
  259. case 2:
  260. return RenderingIntent::Saturation;
  261. case 3:
  262. return RenderingIntent::ICCAbsoluteColorimetric;
  263. }
  264. return Error::from_string_literal("ICC::Profile: Invalid rendering intent");
  265. }
  266. ErrorOr<XYZ> parse_pcs_illuminant(ICCHeader const& header)
  267. {
  268. // ICC v4, 7.2.16 PCS illuminant field
  269. XYZ xyz = (XYZ)header.pcs_illuminant;
  270. /// "The value, when rounded to four decimals, shall be X = 0,9642, Y = 1,0 and Z = 0,8249."
  271. if (round(xyz.x * 10'000) != 9'642 || round(xyz.y * 10'000) != 10'000 || round(xyz.z * 10'000) != 8'249)
  272. return Error::from_string_literal("ICC::Profile: Invalid pcs illuminant");
  273. return xyz;
  274. }
  275. Optional<Creator> parse_profile_creator(ICCHeader const& header)
  276. {
  277. // ICC v4, 7.2.17 Profile creator field
  278. // "This field may be used to identify the creator of the profile.
  279. // If used the signature should match the signature contained in the device manufacturer section of the ICC signature registry found at www.color.org."
  280. // This is not always true in practice.
  281. // For example, .icc files in /System/ColorSync/Profiles on macOS 12.6 set this to 'appl', which is a CMM signature, not a device signature (that one would be 'APPL').
  282. // "If not used this field shall be set to zero (00000000h)."
  283. if (header.profile_creator == Creator { 0 })
  284. return {};
  285. return header.profile_creator;
  286. }
  287. template<size_t N>
  288. bool all_bytes_are_zero(const u8 (&bytes)[N])
  289. {
  290. for (u8 byte : bytes) {
  291. if (byte != 0)
  292. return false;
  293. }
  294. return true;
  295. }
  296. ErrorOr<Optional<Crypto::Hash::MD5::DigestType>> parse_profile_id(ICCHeader const& header, ReadonlyBytes icc_bytes)
  297. {
  298. // ICC v4, 7.2.18 Profile ID field
  299. // "A profile ID field value of zero (00h) shall indicate that a profile ID has not been calculated."
  300. if (all_bytes_are_zero(header.profile_id))
  301. return OptionalNone {};
  302. Crypto::Hash::MD5::DigestType id;
  303. static_assert(sizeof(id.data) == sizeof(header.profile_id));
  304. memcpy(id.data, header.profile_id, sizeof(id.data));
  305. auto computed_id = Profile::compute_id(icc_bytes);
  306. if (id != computed_id)
  307. return Error::from_string_literal("ICC::Profile: Invalid profile id");
  308. return id;
  309. }
  310. ErrorOr<void> parse_reserved(ICCHeader const& header)
  311. {
  312. // ICC v4, 7.2.19 Reserved field
  313. // "This field of the profile header is reserved for future ICC definition and shall be set to zero."
  314. if (!all_bytes_are_zero(header.reserved))
  315. return Error::from_string_literal("ICC::Profile: Reserved header bytes are not zero");
  316. return {};
  317. }
  318. }
  319. URL device_manufacturer_url(DeviceManufacturer device_manufacturer)
  320. {
  321. return URL(DeprecatedString::formatted("https://www.color.org/signatureRegistry/?entityEntry={:c}{:c}{:c}{:c}-{:08X}",
  322. device_manufacturer.c0(), device_manufacturer.c1(), device_manufacturer.c2(), device_manufacturer.c3(), device_manufacturer.value));
  323. }
  324. URL device_model_url(DeviceModel device_model)
  325. {
  326. return URL(DeprecatedString::formatted("https://www.color.org/signatureRegistry/deviceRegistry/?entityEntry={:c}{:c}{:c}{:c}-{:08X}",
  327. device_model.c0(), device_model.c1(), device_model.c2(), device_model.c3(), device_model.value));
  328. }
  329. Optional<StringView> tag_signature_spec_name(TagSignature tag_signature)
  330. {
  331. switch (tag_signature) {
  332. #define TAG(name, id) \
  333. case name: \
  334. return #name##sv;
  335. ENUMERATE_TAG_SIGNATURES(TAG)
  336. #undef TAG
  337. }
  338. return {};
  339. }
  340. StringView device_class_name(DeviceClass device_class)
  341. {
  342. switch (device_class) {
  343. case DeviceClass::InputDevice:
  344. return "InputDevice"sv;
  345. case DeviceClass::DisplayDevice:
  346. return "DisplayDevice"sv;
  347. case DeviceClass::OutputDevice:
  348. return "OutputDevice"sv;
  349. case DeviceClass::DeviceLink:
  350. return "DeviceLink"sv;
  351. case DeviceClass::ColorSpace:
  352. return "ColorSpace"sv;
  353. case DeviceClass::Abstract:
  354. return "Abstract"sv;
  355. case DeviceClass::NamedColor:
  356. return "NamedColor"sv;
  357. }
  358. VERIFY_NOT_REACHED();
  359. }
  360. StringView data_color_space_name(ColorSpace color_space)
  361. {
  362. switch (color_space) {
  363. case ColorSpace::nCIEXYZ:
  364. return "nCIEXYZ"sv;
  365. case ColorSpace::CIELAB:
  366. return "CIELAB"sv;
  367. case ColorSpace::CIELUV:
  368. return "CIELUV"sv;
  369. case ColorSpace::YCbCr:
  370. return "YCbCr"sv;
  371. case ColorSpace::CIEYxy:
  372. return "CIEYxy"sv;
  373. case ColorSpace::RGB:
  374. return "RGB"sv;
  375. case ColorSpace::Gray:
  376. return "Gray"sv;
  377. case ColorSpace::HSV:
  378. return "HSV"sv;
  379. case ColorSpace::HLS:
  380. return "HLS"sv;
  381. case ColorSpace::CMYK:
  382. return "CMYK"sv;
  383. case ColorSpace::CMY:
  384. return "CMY"sv;
  385. case ColorSpace::TwoColor:
  386. return "2 color"sv;
  387. case ColorSpace::ThreeColor:
  388. return "3 color (other than XYZ, Lab, Luv, YCbCr, CIEYxy, RGB, HSV, HLS, CMY)"sv;
  389. case ColorSpace::FourColor:
  390. return "4 color (other than CMYK)"sv;
  391. case ColorSpace::FiveColor:
  392. return "5 color"sv;
  393. case ColorSpace::SixColor:
  394. return "6 color"sv;
  395. case ColorSpace::SevenColor:
  396. return "7 color"sv;
  397. case ColorSpace::EightColor:
  398. return "8 color"sv;
  399. case ColorSpace::NineColor:
  400. return "9 color"sv;
  401. case ColorSpace::TenColor:
  402. return "10 color"sv;
  403. case ColorSpace::ElevenColor:
  404. return "11 color"sv;
  405. case ColorSpace::TwelveColor:
  406. return "12 color"sv;
  407. case ColorSpace::ThirteenColor:
  408. return "13 color"sv;
  409. case ColorSpace::FourteenColor:
  410. return "14 color"sv;
  411. case ColorSpace::FifteenColor:
  412. return "15 color"sv;
  413. }
  414. VERIFY_NOT_REACHED();
  415. }
  416. StringView profile_connection_space_name(ColorSpace color_space)
  417. {
  418. switch (color_space) {
  419. case ColorSpace::PCSXYZ:
  420. return "PCSXYZ"sv;
  421. case ColorSpace::PCSLAB:
  422. return "PCSLAB"sv;
  423. default:
  424. return data_color_space_name(color_space);
  425. }
  426. }
  427. StringView primary_platform_name(PrimaryPlatform primary_platform)
  428. {
  429. switch (primary_platform) {
  430. case PrimaryPlatform::Apple:
  431. return "Apple"sv;
  432. case PrimaryPlatform::Microsoft:
  433. return "Microsoft"sv;
  434. case PrimaryPlatform::SiliconGraphics:
  435. return "Silicon Graphics"sv;
  436. case PrimaryPlatform::Sun:
  437. return "Sun"sv;
  438. }
  439. VERIFY_NOT_REACHED();
  440. }
  441. StringView rendering_intent_name(RenderingIntent rendering_intent)
  442. {
  443. switch (rendering_intent) {
  444. case RenderingIntent::Perceptual:
  445. return "Perceptual"sv;
  446. case RenderingIntent::MediaRelativeColorimetric:
  447. return "Media-relative colorimetric"sv;
  448. case RenderingIntent::Saturation:
  449. return "Saturation"sv;
  450. case RenderingIntent::ICCAbsoluteColorimetric:
  451. return "ICC-absolute colorimetric"sv;
  452. }
  453. VERIFY_NOT_REACHED();
  454. }
  455. Flags::Flags() = default;
  456. Flags::Flags(u32 bits)
  457. : m_bits(bits)
  458. {
  459. }
  460. DeviceAttributes::DeviceAttributes() = default;
  461. DeviceAttributes::DeviceAttributes(u64 bits)
  462. : m_bits(bits)
  463. {
  464. }
  465. static TagTypeSignature tag_type(ReadonlyBytes tag_bytes)
  466. {
  467. VERIFY(tag_bytes.size() >= sizeof(u32));
  468. return *bit_cast<BigEndian<TagTypeSignature> const*>(tag_bytes.data());
  469. }
  470. static ErrorOr<void> check_reserved(ReadonlyBytes tag_bytes)
  471. {
  472. if (tag_bytes.size() < 2 * sizeof(u32))
  473. return Error::from_string_literal("ICC::Profile: Not enough data for tag reserved field");
  474. if (*bit_cast<BigEndian<u32> const*>(tag_bytes.data() + sizeof(u32)) != 0)
  475. return Error::from_string_literal("ICC::Profile: tag reserved field not 0");
  476. return {};
  477. }
  478. ErrorOr<NonnullRefPtr<CurveTagData>> CurveTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
  479. {
  480. // ICC v4, 10.6 curveType
  481. VERIFY(tag_type(bytes) == Type);
  482. TRY(check_reserved(bytes));
  483. if (bytes.size() < 3 * sizeof(u32))
  484. return Error::from_string_literal("ICC::Profile: curveType has not enough data for count");
  485. u32 count = *bit_cast<BigEndian<u32> const*>(bytes.data() + 8);
  486. if (bytes.size() < 3 * sizeof(u32) + count * sizeof(u16))
  487. return Error::from_string_literal("ICC::Profile: curveType has not enough data for curve points");
  488. BigEndian<u16> const* raw_values = bit_cast<BigEndian<u16> const*>(bytes.data() + 12);
  489. Vector<u16> values;
  490. TRY(values.try_resize(count));
  491. for (u32 i = 0; i < count; ++i)
  492. values[i] = raw_values[i];
  493. return adopt_ref(*new CurveTagData(offset, size, move(values)));
  494. }
  495. ErrorOr<NonnullRefPtr<MultiLocalizedUnicodeTagData>> MultiLocalizedUnicodeTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
  496. {
  497. // ICC v4, 10.15 multiLocalizedUnicodeType
  498. VERIFY(tag_type(bytes) == Type);
  499. TRY(check_reserved(bytes));
  500. // "Multiple strings within this tag may share storage locations. For example, en/US and en/UK can refer to the
  501. // same string data."
  502. // This implementation makes redudant string copies in that case.
  503. // Most of the time, this costs just a few bytes, so that seems ok.
  504. if (bytes.size() < 4 * sizeof(u32))
  505. return Error::from_string_literal("ICC::Profile: multiLocalizedUnicodeType has not enough data");
  506. // Table 54 — multiLocalizedUnicodeType
  507. u32 number_of_records = *bit_cast<BigEndian<u32> const*>(bytes.data() + 8);
  508. u32 record_size = *bit_cast<BigEndian<u32> const*>(bytes.data() + 12);
  509. // "The fourth field of this tag, the record size, should contain the value 12, which corresponds to the size in bytes
  510. // of each record. Any code that needs to access the nth record should determine the record’s offset by multiplying
  511. // n by the contents of this size field and adding 16. This minor extra effort allows for future expansion of the record
  512. // encoding, should the need arise, without having to define a new tag type."
  513. if (record_size < 12)
  514. return Error::from_string_literal("ICC::Profile: multiLocalizedUnicodeType record size too small");
  515. if (bytes.size() < 16 + number_of_records * record_size)
  516. return Error::from_string_literal("ICC::Profile: multiLocalizedUnicodeType not enough data for records");
  517. Vector<Record> records;
  518. TRY(records.try_resize(number_of_records));
  519. // "For the definition of language codes and country codes, see respectively
  520. // ISO 639-1 and ISO 3166-1. The Unicode strings in storage should be encoded as 16-bit big-endian, UTF-16BE,
  521. // and should not be NULL terminated."
  522. auto& utf_16be_decoder = *TextCodec::decoder_for("utf-16be");
  523. struct RawRecord {
  524. BigEndian<u16> language_code;
  525. BigEndian<u16> country_code;
  526. BigEndian<u32> string_length_in_bytes;
  527. BigEndian<u32> string_offset_in_bytes;
  528. };
  529. for (u32 i = 0; i < number_of_records; ++i) {
  530. size_t offset = 16 + i * record_size;
  531. RawRecord record = *bit_cast<RawRecord const*>(bytes.data() + offset);
  532. records[i].iso_639_1_language_code = record.language_code;
  533. records[i].iso_3166_1_country_code = record.country_code;
  534. if (record.string_length_in_bytes % 2 != 0)
  535. return Error::from_string_literal("ICC::Profile: multiLocalizedUnicodeType odd UTF-16 byte length");
  536. if (record.string_offset_in_bytes + record.string_length_in_bytes > bytes.size())
  537. return Error::from_string_literal("ICC::Profile: multiLocalizedUnicodeType string offset out of bounds");
  538. StringView utf_16be_data { bytes.data() + record.string_offset_in_bytes, record.string_length_in_bytes };
  539. records[i].text = TRY(String::from_deprecated_string(utf_16be_decoder.to_utf8(utf_16be_data)));
  540. }
  541. return adopt_ref(*new MultiLocalizedUnicodeTagData(offset, size, move(records)));
  542. }
  543. unsigned ParametricCurveTagData::parameter_count(FunctionType function_type)
  544. {
  545. switch (function_type) {
  546. case FunctionType::Type0:
  547. return 1;
  548. case FunctionType::Type1:
  549. return 3;
  550. case FunctionType::Type2:
  551. return 4;
  552. case FunctionType::Type3:
  553. return 5;
  554. case FunctionType::Type4:
  555. return 7;
  556. }
  557. VERIFY_NOT_REACHED();
  558. }
  559. ErrorOr<NonnullRefPtr<ParametricCurveTagData>> ParametricCurveTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
  560. {
  561. // ICC v4, 10.18 parametricCurveType
  562. VERIFY(tag_type(bytes) == Type);
  563. TRY(check_reserved(bytes));
  564. // "The parametricCurveType describes a one-dimensional curve by specifying one of a predefined set of functions
  565. // using the parameters."
  566. if (bytes.size() < 2 * sizeof(u32) + 2 * sizeof(u16))
  567. return Error::from_string_literal("ICC::Profile: parametricCurveType has not enough data");
  568. u16 raw_function_type = *bit_cast<BigEndian<u16> const*>(bytes.data() + 8);
  569. u16 reserved = *bit_cast<BigEndian<u16> const*>(bytes.data() + 10);
  570. if (reserved != 0)
  571. return Error::from_string_literal("ICC::Profile: parametricCurveType reserved u16 after function type not 0");
  572. if (raw_function_type > 4)
  573. return Error::from_string_literal("ICC::Profile: parametricCurveType unknown function type");
  574. FunctionType function_type = (FunctionType)raw_function_type;
  575. unsigned count = parameter_count(function_type);
  576. if (bytes.size() < 2 * sizeof(u32) + 2 * sizeof(u16) + count * sizeof(s15Fixed16Number))
  577. return Error::from_string_literal("ICC::Profile: parametricCurveType has not enough data for parameters");
  578. BigEndian<s15Fixed16Number> const* raw_parameters = bit_cast<BigEndian<s15Fixed16Number> const*>(bytes.data() + 12);
  579. Array<S15Fixed16, 7> parameters;
  580. parameters.fill(0);
  581. for (unsigned i = 0; i < count; ++i)
  582. parameters[i] = S15Fixed16::create_raw(raw_parameters[i]);
  583. return adopt_ref(*new ParametricCurveTagData(offset, size, function_type, move(parameters)));
  584. }
  585. ErrorOr<NonnullRefPtr<S15Fixed16ArrayTagData>> S15Fixed16ArrayTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
  586. {
  587. // ICC v4, 10.22 s15Fixed16ArrayType
  588. VERIFY(tag_type(bytes) == Type);
  589. TRY(check_reserved(bytes));
  590. // "This type represents an array of generic 4-byte (32-bit) fixed point quantity. The number of values is determined
  591. // from the size of the tag."
  592. size_t byte_size = bytes.size() - 8;
  593. if (byte_size % sizeof(s15Fixed16Number) != 0)
  594. return Error::from_string_literal("ICC::Profile: s15Fixed16ArrayType has wrong size");
  595. size_t count = byte_size / sizeof(s15Fixed16Number);
  596. BigEndian<s15Fixed16Number> const* raw_values = bit_cast<BigEndian<s15Fixed16Number> const*>(bytes.data() + 8);
  597. Vector<S15Fixed16, 9> values;
  598. TRY(values.try_resize(count));
  599. for (size_t i = 0; i < count; ++i)
  600. values[i] = S15Fixed16::create_raw(raw_values[i]);
  601. return adopt_ref(*new S15Fixed16ArrayTagData(offset, size, move(values)));
  602. }
  603. ErrorOr<NonnullRefPtr<TextDescriptionTagData>> TextDescriptionTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
  604. {
  605. // ICC v2, 6.5.17 textDescriptionType
  606. // textDescriptionType is no longer in the V4 spec.
  607. // In both the V2 and V4 specs, 'desc' is a required tag. In V4, it has type multiLocalizedUnicodeType,
  608. // but in V2 it has type textDescriptionType. Since 'desc' is required, this type is present in every
  609. // V2 icc file, and there are still many V2 files in use. So textDescriptionType is here to stay for now.
  610. // It's a very 90s type, preceding universal adoption of Unicode.
  611. // "The textDescriptionType is a complex structure that contains three types of text description structures:
  612. // 7-bit ASCII, Unicode and ScriptCode. Since no single standard method for specifying localizable character
  613. // sets exists across the major platform vendors, including all three provides access for the major operating
  614. // systems. The 7-bit ASCII description is to be an invariant, nonlocalizable name for consistent reference.
  615. // It is preferred that both the Unicode and ScriptCode structures be properly localized."
  616. VERIFY(tag_type(bytes) == Type);
  617. TRY(check_reserved(bytes));
  618. // 7-bit ASCII
  619. // "ASCII: The count is the length of the string in bytes including the null terminator."
  620. if (bytes.size() < 3 * sizeof(u32))
  621. return Error::from_string_literal("ICC::Profile: textDescriptionType has not enough data for ASCII size");
  622. u32 ascii_description_length = *bit_cast<BigEndian<u32> const*>(bytes.data() + 8);
  623. if (bytes.size() < 3 * sizeof(u32) + ascii_description_length)
  624. return Error::from_string_literal("ICC::Profile: textDescriptionType has not enough data for ASCII description");
  625. u8 const* ascii_description_data = bytes.data() + 3 * sizeof(u32);
  626. for (u32 i = 0; i < ascii_description_length; ++i) {
  627. if (ascii_description_data[i] >= 128)
  628. return Error::from_string_literal("ICC::Profile: textDescriptionType ASCII description not 7-bit ASCII");
  629. }
  630. if (ascii_description_length == 0)
  631. return Error::from_string_literal("ICC::Profile: textDescriptionType ASCII description length does not include trailing \\0");
  632. if (ascii_description_data[ascii_description_length - 1] != '\0')
  633. return Error::from_string_literal("ICC::Profile: textDescriptionType ASCII description not \\0-terminated");
  634. StringView ascii_description { ascii_description_data, ascii_description_length - 1 };
  635. // Unicode
  636. if (bytes.size() < 3 * sizeof(u32) + ascii_description_length + 2 * sizeof(u32))
  637. return Error::from_string_literal("ICC::Profile: textDescriptionType has not enough data for Unicode metadata");
  638. // "Because the Unicode language code and Unicode count immediately follow the ASCII description,
  639. // their alignment is not correct when the ASCII count is not a multiple of four"
  640. // So we can't use BigEndian<u32> here.
  641. u8 const* cursor = ascii_description_data + ascii_description_length;
  642. u32 unicode_language_code = (u32)(cursor[0] << 24) | (u32)(cursor[1] << 16) | (u32)(cursor[2] << 8) | (u32)cursor[3];
  643. cursor += 4;
  644. // "Unicode: The count is the number of characters including a Unicode null where a character is always two bytes."
  645. // This implies UCS-2.
  646. u32 unicode_description_length = (u32)(cursor[0] << 24) | (u32)(cursor[1] << 16) | (u32)(cursor[2] << 8) | (u32)cursor[3];
  647. cursor += 4;
  648. if (bytes.size() < 3 * sizeof(u32) + ascii_description_length + 2 * sizeof(u32) + 2 * unicode_description_length)
  649. return Error::from_string_literal("ICC::Profile: textDescriptionType has not enough data for Unicode description");
  650. u8 const* unicode_description_data = cursor;
  651. cursor += 2 * unicode_description_length;
  652. for (u32 i = 0; i < unicode_description_length; ++i) {
  653. u16 code_point = (u16)(unicode_description_data[2 * i] << 8) | (u16)unicode_description_data[2 * i + 1];
  654. if (is_unicode_surrogate(code_point))
  655. return Error::from_string_literal("ICC::Profile: textDescriptionType Unicode description is not valid UCS-2");
  656. }
  657. // If Unicode is not native on the platform, then the Unicode language code and Unicode count should be
  658. // filled in as 0, with no data placed in the Unicode localizable profile description area.
  659. Optional<String> unicode_description;
  660. if (unicode_description_length > 0) {
  661. u16 last_code_point = (u16)(unicode_description_data[2 * (unicode_description_length - 1)] << 8) | (u16)unicode_description_data[2 * (unicode_description_length - 1) + 1];
  662. if (last_code_point != 0)
  663. return Error::from_string_literal("ICC::Profile: textDescriptionType Unicode description not \\0-terminated");
  664. StringView utf_16be_data { unicode_description_data, 2 * (unicode_description_length - 1) };
  665. unicode_description = TRY(String::from_deprecated_string(TextCodec::decoder_for("utf-16be")->to_utf8(utf_16be_data)));
  666. }
  667. // ScriptCode
  668. // What is a script code? It's an old, obsolete mac thing. It looks like it's documented in
  669. // https://developer.apple.com/library/archive/documentation/mac/pdf/Text.pdf
  670. // "Script Codes, Language Codes, and Region Codes 1", PDF page 82.
  671. // I haven't found a complete explanation though. PDF page 84 suggests that:
  672. // - There are 16 script codes
  673. // - 0 is Roman, 1 is Japanese, 2 is Chinese, 3 is Korean, 9 is Devanagari
  674. // Roman uses https://en.wikipedia.org/wiki/Mac_OS_Roman as encoding (also on page 89),
  675. // and "All non-Roman script systems include Roman as a subscript" (page 87).
  676. // Aha, "Script Codes 6" on page 676 has the complete list! There are 32 of them.
  677. // The document mentions that each script code possibly has its own encoding, but I haven't found
  678. // details on the encodings for script codes other than 0 (which uses Mac OS Roman).
  679. // http://www.kreativekorp.com/charset/encoding/ has an unofficial list of old Mac OS encodings,
  680. // but it's not clear to me which script codes map to which encoding.
  681. // From here on, quotes are from the ICC spec on textDescriptionType again.
  682. // "The ScriptCode code is misaligned when the ASCII count is odd."
  683. // So don't use BigEndian<u16> here.
  684. u16 scriptcode_code = (u16)(cursor[0] << 8) | (u32)cursor[1];
  685. cursor += 2;
  686. // "ScriptCode: The count is the length of the string in bytes including the terminating null."
  687. u8 macintosh_description_length = *cursor;
  688. cursor += 1;
  689. if (macintosh_description_length > 67)
  690. return Error::from_string_literal("ICC::Profile: textDescriptionType ScriptCode description too long");
  691. u8 const* macintosh_description_data = cursor;
  692. // "If Scriptcode is not native on the platform, then the ScriptCode code and ScriptCode count should be filled
  693. // in as 0. The 67-byte localizable Macintosh profile description should be filled with 0’s."
  694. Optional<String> macintosh_description;
  695. if (macintosh_description_length > 0) {
  696. // ScriptCode is old-timey and a complicated to fully support. Lightroom Classic does write the ScriptCode section of textDescriptionType.
  697. // But supporting only ASCII MacRoman is good enough for those files, and easy to implement, so let's do only that for now.
  698. if (scriptcode_code == 0) { // MacRoman
  699. if (macintosh_description_data[macintosh_description_length - 1] != '\0')
  700. return Error::from_string_literal("ICC::Profile: textDescriptionType ScriptCode not \\0-terminated");
  701. macintosh_description = TRY(String::from_deprecated_string(TextCodec::decoder_for("x-mac-roman")->to_utf8({ macintosh_description_data, (size_t)macintosh_description_length - 1 })));
  702. } else {
  703. dbgln("TODO: ICCProfile textDescriptionType ScriptCode {}, length {}", scriptcode_code, macintosh_description_length);
  704. }
  705. }
  706. return adopt_ref(*new TextDescriptionTagData(offset, size, TRY(String::from_utf8(ascii_description)), unicode_language_code, move(unicode_description), move(macintosh_description)));
  707. }
  708. ErrorOr<NonnullRefPtr<TextTagData>> TextTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
  709. {
  710. // ICC v4, 10.24 textType
  711. VERIFY(tag_type(bytes) == Type);
  712. TRY(check_reserved(bytes));
  713. // "The textType is a simple text structure that contains a 7-bit ASCII text string. The length of the string is obtained
  714. // by subtracting 8 from the element size portion of the tag itself. This string shall be terminated with a 00h byte."
  715. u32 length = bytes.size() - 8;
  716. u8 const* text_data = bytes.data() + 8;
  717. for (u32 i = 0; i < length; ++i) {
  718. if (text_data[i] >= 128)
  719. return Error::from_string_literal("ICC::Profile: textType data not 7-bit ASCII");
  720. }
  721. if (length == 0)
  722. return Error::from_string_literal("ICC::Profile: textType too short for \\0 byte");
  723. if (text_data[length - 1] != '\0')
  724. return Error::from_string_literal("ICC::Profile: textType data not \\0-terminated");
  725. return adopt_ref(*new TextTagData(offset, size, TRY(String::from_utf8(StringView(text_data, length - 1)))));
  726. }
  727. ErrorOr<NonnullRefPtr<XYZTagData>> XYZTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
  728. {
  729. // ICC v4, 10.31 XYZType
  730. VERIFY(tag_type(bytes) == Type);
  731. TRY(check_reserved(bytes));
  732. // "The XYZType contains an array of three encoded values for PCSXYZ, CIEXYZ, or nCIEXYZ values. The
  733. // number of sets of values is determined from the size of the tag."
  734. size_t byte_size = bytes.size() - 8;
  735. if (byte_size % sizeof(XYZNumber) != 0)
  736. return Error::from_string_literal("ICC::Profile: XYZType has wrong size");
  737. size_t xyz_count = byte_size / sizeof(XYZNumber);
  738. XYZNumber const* raw_xyzs = bit_cast<XYZNumber const*>(bytes.data() + 8);
  739. Vector<XYZ, 1> xyzs;
  740. TRY(xyzs.try_resize(xyz_count));
  741. for (size_t i = 0; i < xyz_count; ++i)
  742. xyzs[i] = (XYZ)raw_xyzs[i];
  743. return adopt_ref(*new XYZTagData(offset, size, move(xyzs)));
  744. }
  745. ErrorOr<void> Profile::read_header(ReadonlyBytes bytes)
  746. {
  747. if (bytes.size() < sizeof(ICCHeader))
  748. return Error::from_string_literal("ICC::Profile: Not enough data for header");
  749. auto header = *bit_cast<ICCHeader const*>(bytes.data());
  750. TRY(parse_file_signature(header));
  751. m_on_disk_size = TRY(parse_size(header, bytes));
  752. m_preferred_cmm_type = parse_preferred_cmm_type(header);
  753. m_version = TRY(parse_version(header));
  754. m_device_class = TRY(parse_device_class(header));
  755. m_data_color_space = TRY(parse_data_color_space(header));
  756. m_connection_space = TRY(parse_connection_space(header));
  757. m_creation_timestamp = TRY(parse_creation_date_time(header));
  758. m_primary_platform = TRY(parse_primary_platform(header));
  759. m_flags = Flags { header.profile_flags };
  760. m_device_manufacturer = parse_device_manufacturer(header);
  761. m_device_model = parse_device_model(header);
  762. m_device_attributes = TRY(parse_device_attributes(header));
  763. m_rendering_intent = TRY(parse_rendering_intent(header));
  764. m_pcs_illuminant = TRY(parse_pcs_illuminant(header));
  765. m_creator = parse_profile_creator(header);
  766. m_id = TRY(parse_profile_id(header, bytes));
  767. TRY(parse_reserved(header));
  768. return {};
  769. }
  770. ErrorOr<NonnullRefPtr<TagData>> Profile::read_tag(ReadonlyBytes bytes, u32 offset_to_beginning_of_tag_data_element, u32 size_of_tag_data_element)
  771. {
  772. if (offset_to_beginning_of_tag_data_element + size_of_tag_data_element > bytes.size())
  773. return Error::from_string_literal("ICC::Profile: Tag data out of bounds");
  774. auto tag_bytes = bytes.slice(offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
  775. // ICC v4, 9 Tag definitions
  776. // ICC v4, 9.1 General
  777. // "All tags, including private tags, have as their first four bytes a tag signature to identify to profile readers
  778. // what kind of data is contained within a tag."
  779. if (tag_bytes.size() < sizeof(u32))
  780. return Error::from_string_literal("ICC::Profile: Not enough data for tag type");
  781. auto type = tag_type(tag_bytes);
  782. switch (type) {
  783. case CurveTagData::Type:
  784. return CurveTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
  785. case MultiLocalizedUnicodeTagData::Type:
  786. return MultiLocalizedUnicodeTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
  787. case ParametricCurveTagData::Type:
  788. return ParametricCurveTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
  789. case S15Fixed16ArrayTagData::Type:
  790. return S15Fixed16ArrayTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
  791. case TextDescriptionTagData::Type:
  792. return TextDescriptionTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
  793. case TextTagData::Type:
  794. return TextTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
  795. case XYZTagData::Type:
  796. return XYZTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
  797. default:
  798. // FIXME: optionally ignore tags of unknown type
  799. return adopt_ref(*new UnknownTagData(offset_to_beginning_of_tag_data_element, size_of_tag_data_element, type));
  800. }
  801. }
  802. ErrorOr<void> Profile::read_tag_table(ReadonlyBytes bytes)
  803. {
  804. // ICC v4, 7.3 Tag table
  805. // ICC v4, 7.3.1 Overview
  806. // "The tag table acts as a table of contents for the tags and an index into the tag data element in the profiles. It
  807. // shall consist of a 4-byte entry that contains a count of the number of tags in the table followed by a series of 12-
  808. // byte entries with one entry for each tag. The tag table therefore contains 4+12n bytes where n is the number of
  809. // tags contained in the profile. The entries for the tags within the table are not required to be in any particular
  810. // order nor are they required to match the sequence of tag data element within the profile.
  811. // Each 12-byte tag entry following the tag count shall consist of a 4-byte tag signature, a 4-byte offset to define
  812. // the beginning of the tag data element, and a 4-byte entry identifying the length of the tag data element in bytes.
  813. // [...]
  814. // The tag table shall define a contiguous sequence of unique tag elements, with no gaps between the last byte
  815. // of any tag data element referenced from the tag table (inclusive of any necessary additional pad bytes required
  816. // to reach a four-byte boundary) and the byte offset of the following tag element, or the end of the file.
  817. // Duplicate tag signatures shall not be included in the tag table.
  818. // Tag data elements shall not partially overlap, so there shall be no part of any tag data element that falls within
  819. // the range defined for another tag in the tag table."
  820. ReadonlyBytes tag_table_bytes = bytes.slice(sizeof(ICCHeader));
  821. if (tag_table_bytes.size() < sizeof(u32))
  822. return Error::from_string_literal("ICC::Profile: Not enough data for tag count");
  823. auto tag_count = *bit_cast<BigEndian<u32> const*>(tag_table_bytes.data());
  824. // ICC V4, 7.3 Tag table, Table 24 - Tag table structure
  825. struct TagTableEntry {
  826. BigEndian<TagSignature> tag_signature;
  827. BigEndian<u32> offset_to_beginning_of_tag_data_element;
  828. BigEndian<u32> size_of_tag_data_element;
  829. };
  830. static_assert(sizeof(TagTableEntry) == 12);
  831. tag_table_bytes = tag_table_bytes.slice(sizeof(u32));
  832. if (tag_table_bytes.size() < tag_count * sizeof(TagTableEntry))
  833. return Error::from_string_literal("ICC::Profile: Not enough data for tag table entries");
  834. auto tag_table_entries = bit_cast<TagTableEntry const*>(tag_table_bytes.data());
  835. // "The tag table may contain multiple tags signatures that all reference the same tag data element offset, allowing
  836. // efficient reuse of tag data elements."
  837. HashMap<u32, NonnullRefPtr<TagData>> offset_to_tag_data;
  838. for (u32 i = 0; i < tag_count; ++i) {
  839. // FIXME: optionally ignore tags with unknown signature
  840. // Dedupe identical offset/sizes.
  841. NonnullRefPtr<TagData> tag_data = TRY(offset_to_tag_data.try_ensure(tag_table_entries[i].offset_to_beginning_of_tag_data_element, [=, this]() {
  842. return read_tag(bytes, tag_table_entries[i].offset_to_beginning_of_tag_data_element, tag_table_entries[i].size_of_tag_data_element);
  843. }));
  844. // "In such cases, both the offset and size of the tag data elements in the tag table shall be the same."
  845. if (tag_data->size() != tag_table_entries[i].size_of_tag_data_element)
  846. return Error::from_string_literal("ICC::Profile: two tags have same offset but different sizes");
  847. // "Duplicate tag signatures shall not be included in the tag table."
  848. if (TRY(m_tag_table.try_set(tag_table_entries[i].tag_signature, move(tag_data))) != AK::HashSetResult::InsertedNewEntry)
  849. return Error::from_string_literal("ICC::Profile: duplicate tag signature");
  850. }
  851. return {};
  852. }
  853. static bool is_xCLR(ColorSpace color_space)
  854. {
  855. switch (color_space) {
  856. case ColorSpace::TwoColor:
  857. case ColorSpace::ThreeColor:
  858. case ColorSpace::FourColor:
  859. case ColorSpace::FiveColor:
  860. case ColorSpace::SixColor:
  861. case ColorSpace::SevenColor:
  862. case ColorSpace::EightColor:
  863. case ColorSpace::NineColor:
  864. case ColorSpace::TenColor:
  865. case ColorSpace::ElevenColor:
  866. case ColorSpace::TwelveColor:
  867. case ColorSpace::ThirteenColor:
  868. case ColorSpace::FourteenColor:
  869. case ColorSpace::FifteenColor:
  870. return true;
  871. default:
  872. return false;
  873. }
  874. }
  875. ErrorOr<void> Profile::check_required_tags()
  876. {
  877. // ICC v4, 8 Required tags
  878. // ICC v4, 8.2 Common requirements
  879. // "With the exception of DeviceLink profiles, all profiles shall contain the following tags:
  880. // - profileDescriptionTag (see 9.2.41);
  881. // - copyrightTag (see 9.2.21);
  882. // - mediaWhitePointTag (see 9.2.34);
  883. // - chromaticAdaptationTag, when the measurement data used to calculate the profile was specified for an
  884. // adopted white with a chromaticity different from that of the PCS adopted white (see 9.2.15).
  885. // NOTE A DeviceLink profile is not required to have either a mediaWhitePointTag or a chromaticAdaptationTag."
  886. // profileDescriptionTag, copyrightTag are required for DeviceLink too (see ICC v4, 8.6 DeviceLink profile).
  887. // profileDescriptionTag, copyrightTag, mediaWhitePointTag are required in ICC v2 as well.
  888. // chromaticAdaptationTag isn't required in v2 profiles as far as I can tell.
  889. if (!m_tag_table.contains(profileDescriptionTag))
  890. return Error::from_string_literal("ICC::Profile: required profileDescriptionTag is missing");
  891. if (!m_tag_table.contains(copyrightTag))
  892. return Error::from_string_literal("ICC::Profile: required copyrightTag is missing");
  893. if (device_class() != DeviceClass::DeviceLink) {
  894. if (!m_tag_table.contains(mediaWhitePointTag))
  895. return Error::from_string_literal("ICC::Profile: required mediaWhitePointTag is missing");
  896. // FIXME: Check for chromaticAdaptationTag after figuring out when exactly it needs to be present.
  897. }
  898. auto has_tag = [&](auto& tag) { return m_tag_table.contains(tag); };
  899. auto has_all_tags = [&]<class T>(T tags) { return all_of(tags, has_tag); };
  900. switch (device_class()) {
  901. case DeviceClass::InputDevice: {
  902. // ICC v4, 8.3 Input profiles
  903. // "8.3.1 General
  904. // Input profiles are generally used with devices such as scanners and digital cameras. The types of profiles
  905. // available for use as Input profiles are N-component LUT-based, Three-component matrix-based, and
  906. // monochrome.
  907. // 8.3.2 N-component LUT-based Input profiles
  908. // In addition to the tags listed in 8.2 an N-component LUT-based Input profile shall contain the following tag:
  909. // - AToB0Tag (see 9.2.1).
  910. // 8.3.3 Three-component matrix-based Input profiles
  911. // In addition to the tags listed in 8.2, a three-component matrix-based Input profile shall contain the following tags:
  912. // - redMatrixColumnTag (see 9.2.44);
  913. // - greenMatrixColumnTag (see 9.2.30);
  914. // - blueMatrixColumnTag (see 9.2.4);
  915. // - redTRCTag (see 9.2.45);
  916. // - greenTRCTag (see 9.2.31);
  917. // - blueTRCTag (see 9.2.5).
  918. // [...] Only the PCSXYZ encoding can be used with matrix/TRC models.
  919. // 8.3.4 Monochrome Input profiles
  920. // In addition to the tags listed in 8.2, a monochrome Input profile shall contain the following tag:
  921. // - grayTRCTag (see 9.2.29).
  922. bool has_n_component_lut_based_tags = has_tag(AToB0Tag);
  923. bool has_three_component_matrix_based_tags = has_all_tags(Array { redMatrixColumnTag, greenMatrixColumnTag, blueMatrixColumnTag, redTRCTag, greenTRCTag, blueTRCTag });
  924. bool has_monochrome_tags = has_tag(grayTRCTag);
  925. if (!has_n_component_lut_based_tags && !has_three_component_matrix_based_tags && !has_monochrome_tags)
  926. return Error::from_string_literal("ICC::Profile: InputDevice required tags are missing");
  927. if (!has_n_component_lut_based_tags && has_three_component_matrix_based_tags && connection_space() != ColorSpace::PCSXYZ)
  928. return Error::from_string_literal("ICC::Profile: InputDevice three-component matrix-based profile must use PCSXYZ");
  929. break;
  930. }
  931. case DeviceClass::DisplayDevice: {
  932. // ICC v4, 8.4 Display profiles
  933. // "8.4.1 General
  934. // This class of profiles represents display devices such as monitors. The types of profiles available for use as
  935. // Display profiles are N-component LUT-based, Three-component matrix-based, and monochrome.
  936. // 8.4.2 N-Component LUT-based Display profiles
  937. // In addition to the tags listed in 8.2 an N-component LUT-based Input profile shall contain the following tags:
  938. // - AToB0Tag (see 9.2.1);
  939. // - BToA0Tag (see 9.2.6).
  940. // 8.4.3 Three-component matrix-based Display profiles
  941. // In addition to the tags listed in 8.2, a three-component matrix-based Display profile shall contain the following
  942. // tags:
  943. // - redMatrixColumnTag (see 9.2.44);
  944. // - greenMatrixColumnTag (see 9.2.30);
  945. // - blueMatrixColumnTag (see 9.2.4);
  946. // - redTRCTag (see 9.2.45);
  947. // - greenTRCTag (see 9.2.31);
  948. // - blueTRCTag (see 9.2.5).
  949. // [...] Only the PCSXYZ encoding can be used with matrix/TRC models.
  950. // 8.4.4 Monochrome Display profiles
  951. // In addition to the tags listed in 8.2 a monochrome Display profile shall contain the following tag:
  952. // - grayTRCTag (see 9.2.29)."
  953. bool has_n_component_lut_based_tags = has_all_tags(Array { AToB0Tag, BToA0Tag });
  954. bool has_three_component_matrix_based_tags = has_all_tags(Array { redMatrixColumnTag, greenMatrixColumnTag, blueMatrixColumnTag, redTRCTag, greenTRCTag, blueTRCTag });
  955. bool has_monochrome_tags = has_tag(grayTRCTag);
  956. if (!has_n_component_lut_based_tags && !has_three_component_matrix_based_tags && !has_monochrome_tags)
  957. return Error::from_string_literal("ICC::Profile: DisplayDevice required tags are missing");
  958. if (!has_n_component_lut_based_tags && has_three_component_matrix_based_tags && connection_space() != ColorSpace::PCSXYZ)
  959. return Error::from_string_literal("ICC::Profile: DisplayDevice three-component matrix-based profile must use PCSXYZ");
  960. break;
  961. }
  962. case DeviceClass::OutputDevice: {
  963. // ICC v4, 8.5 Output profiles
  964. // "8.5.1 General
  965. // Output profiles are used to support devices such as printers and film recorders. The types of profiles available
  966. // for use as Output profiles are N-component LUT-based and Monochrome.
  967. // 8.5.2 N-component LUT-based Output profiles
  968. // In addition to the tags listed in 8.2 an N-component LUT-based Output profile shall contain the following tags:
  969. // - AToB0Tag (see 9.2.1);
  970. // - AToB1Tag (see 9.2.2);
  971. // - AToB2Tag (see 9.2.3);
  972. // - BToA0Tag (see 9.2.6);
  973. // - BToA1Tag (see 9.2.7);
  974. // - BToA2Tag (see 9.2.8);
  975. // - gamutTag (see 9.2.28);
  976. // - colorantTableTag (see 9.2.18), for the xCLR colour spaces (see 7.2.6)
  977. // 8.5.3 Monochrome Output profiles
  978. // In addition to the tags listed in 8.2 a monochrome Output profile shall contain the following tag:
  979. // - grayTRCTag (see 9.2.29)."
  980. // The colorantTableTag requirement is new in v4.
  981. Vector<TagSignature, 8> required_n_component_lut_based_tags = { AToB0Tag, AToB1Tag, AToB2Tag, BToA0Tag, BToA1Tag, BToA2Tag, gamutTag };
  982. if (is_v4() && is_xCLR(connection_space()))
  983. required_n_component_lut_based_tags.append(colorantTableTag);
  984. bool has_n_component_lut_based_tags = has_all_tags(required_n_component_lut_based_tags);
  985. bool has_monochrome_tags = has_tag(grayTRCTag);
  986. if (!has_n_component_lut_based_tags && !has_monochrome_tags)
  987. return Error::from_string_literal("ICC::Profile: OutputDevice required tags are missing");
  988. break;
  989. }
  990. case DeviceClass::DeviceLink: {
  991. // ICC v4, 8.6 DeviceLink profile
  992. // "A DeviceLink profile shall contain the following tags:
  993. // - profileDescriptionTag (see 9.2.41);
  994. // - copyrightTag (see 9.2.21);
  995. // - profileSequenceDescTag (see 9.2.42);
  996. // - AToB0Tag (see 9.2.1);
  997. // - colorantTableTag (see 9.2.18) which is required only if the data colour space field is xCLR, where x is
  998. // hexadecimal 2 to F (see 7.2.6);
  999. // - colorantTableOutTag (see 9.2.19), required only if the PCS field is xCLR, where x is hexadecimal 2 to F
  1000. // (see 7.2.6)"
  1001. // profileDescriptionTag and copyrightTag are already checked above, in the code for section 8.2.
  1002. Vector<TagSignature, 4> required_tags = { profileSequenceDescTag, AToB0Tag };
  1003. if (is_v4() && is_xCLR(connection_space())) { // This requirement is new in v4.
  1004. required_tags.append(colorantTableTag);
  1005. required_tags.append(colorantTableOutTag);
  1006. }
  1007. if (!has_all_tags(required_tags))
  1008. return Error::from_string_literal("ICC::Profile: DeviceLink required tags are missing");
  1009. // "The data colour space field (see 7.2.6) in the DeviceLink profile will be the same as the data colour space field
  1010. // of the first profile in the sequence used to construct the device link. The PCS field (see 7.2.7) will be the same
  1011. // as the data colour space field of the last profile in the sequence."
  1012. // FIXME: Check that if profileSequenceDescType parsing is implemented.
  1013. break;
  1014. }
  1015. case DeviceClass::ColorSpace:
  1016. // ICC v4, 8.7 ColorSpace profile
  1017. // "In addition to the tags listed in 8.2, a ColorSpace profile shall contain the following tags:
  1018. // - BToA0Tag (see 9.2.6);
  1019. // - AToB0Tag (see 9.2.1).
  1020. // [...] ColorSpace profiles may be embedded in images."
  1021. if (!has_all_tags(Array { AToB0Tag, BToA0Tag }))
  1022. return Error::from_string_literal("ICC::Profile: ColorSpace required tags are missing");
  1023. break;
  1024. case DeviceClass::Abstract:
  1025. // ICC v4, 8.8 Abstract profile
  1026. // "In addition to the tags listed in 8.2, an Abstract profile shall contain the following tag:
  1027. // - AToB0Tag (see 9.2.1).
  1028. // [...] Abstract profiles cannot be embedded in images."
  1029. if (!has_tag(AToB0Tag))
  1030. return Error::from_string_literal("ICC::Profile: Abstract required AToB0Tag is missing");
  1031. break;
  1032. case DeviceClass::NamedColor:
  1033. // ICC v4, 8.9 NamedColor profile
  1034. // "In addition to the tags listed in 8.2, a NamedColor profile shall contain the following tag:
  1035. // - namedColor2Tag (see 9.2.35)."
  1036. if (!has_tag(namedColor2Tag))
  1037. return Error::from_string_literal("ICC::Profile: NamedColor required namedColor2Tag is missing");
  1038. break;
  1039. }
  1040. return {};
  1041. }
  1042. ErrorOr<NonnullRefPtr<Profile>> Profile::try_load_from_externally_owned_memory(ReadonlyBytes bytes)
  1043. {
  1044. auto profile = adopt_ref(*new Profile());
  1045. TRY(profile->read_header(bytes));
  1046. bytes = bytes.trim(profile->on_disk_size());
  1047. TRY(profile->read_tag_table(bytes));
  1048. TRY(profile->check_required_tags());
  1049. return profile;
  1050. }
  1051. Crypto::Hash::MD5::DigestType Profile::compute_id(ReadonlyBytes bytes)
  1052. {
  1053. // ICC v4, 7.2.18 Profile ID field
  1054. // "The Profile ID shall be calculated using the MD5 fingerprinting method as defined in Internet RFC 1321.
  1055. // The entire profile, whose length is given by the size field in the header, with the
  1056. // profile flags field (bytes 44 to 47, see 7.2.11),
  1057. // rendering intent field (bytes 64 to 67, see 7.2.15),
  1058. // and profile ID field (bytes 84 to 99)
  1059. // in the profile header temporarily set to zeros (00h),
  1060. // shall be used to calculate the ID."
  1061. const u8 zero[16] = {};
  1062. Crypto::Hash::MD5 md5;
  1063. md5.update(bytes.slice(0, 44));
  1064. md5.update(ReadonlyBytes { zero, 4 }); // profile flags field
  1065. md5.update(bytes.slice(48, 64 - 48));
  1066. md5.update(ReadonlyBytes { zero, 4 }); // rendering intent field
  1067. md5.update(bytes.slice(68, 84 - 68));
  1068. md5.update(ReadonlyBytes { zero, 16 }); // profile ID field
  1069. md5.update(bytes.slice(100));
  1070. return md5.digest();
  1071. }
  1072. }