1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 05:27:46 +00:00

LibGfx: Read profile creation time from ICCProfile header

This commit is contained in:
Nico Weber 2022-12-27 13:37:37 -05:00 committed by Brian Gianforcaro
parent bb1f6f71f1
commit 782ab0a11f
2 changed files with 63 additions and 6 deletions

View file

@ -6,6 +6,7 @@
#include <AK/Endian.h>
#include <LibGfx/ICCProfile.h>
#include <time.h>
// V2 spec: https://color.org/specification/ICC.1-2001-04.pdf
// V4 spec: https://color.org/specification/ICC.1-2022-05.pdf
@ -14,6 +15,58 @@ namespace Gfx::ICC {
namespace {
// ICC V4, 4.2 dateTimeNumber
// "All the dateTimeNumber values in a profile shall be in Coordinated Universal Time [...]."
struct DateTimeNumber {
BigEndian<u16> year;
BigEndian<u16> month;
BigEndian<u16> day;
BigEndian<u16> hour;
BigEndian<u16> minutes;
BigEndian<u16> seconds;
};
ErrorOr<time_t> parse_date_time_number(DateTimeNumber const& date_time)
{
// ICC V4, 4.2 dateTimeNumber
// "Number of the month (1 to 12)"
if (date_time.month < 1 || date_time.month > 12)
return Error::from_string_literal("ICC::Profile: dateTimeNumber month out of bounds");
// "Number of the day of the month (1 to 31)"
if (date_time.day < 1 || date_time.day > 31)
return Error::from_string_literal("ICC::Profile: dateTimeNumber day out of bounds");
// "Number of hours (0 to 23)"
if (date_time.hour > 23)
return Error::from_string_literal("ICC::Profile: dateTimeNumber hour out of bounds");
// "Number of minutes (0 to 59)"
if (date_time.minutes > 59)
return Error::from_string_literal("ICC::Profile: dateTimeNumber minutes out of bounds");
// "Number of seconds (0 to 59)"
// ICC profiles apparently can't be created during leap seconds (seconds would be 60 there, but the spec doesn't allow that).
if (date_time.seconds > 59)
return Error::from_string_literal("ICC::Profile: dateTimeNumber seconds out of bounds");
struct tm tm = {};
tm.tm_year = date_time.year - 1900;
tm.tm_mon = date_time.month - 1;
tm.tm_mday = date_time.day;
tm.tm_hour = date_time.hour;
tm.tm_min = date_time.minutes;
tm.tm_sec = date_time.seconds;
// timegm() doesn't read tm.tm_isdst, tm.tm_wday, and tm.tm_yday, no need to fill them in.
time_t timestamp = timegm(&tm);
if (timestamp == -1)
return Error::from_string_literal("ICC::Profile: dateTimeNumber not representable as timestamp");
return timestamp;
}
// ICC V4, 7.2 Profile header
struct ICCHeader {
BigEndian<u32> profile_size;
@ -27,12 +80,7 @@ struct ICCHeader {
BigEndian<u32> data_color_space;
BigEndian<u32> pcs; // "Profile Connection Space"
BigEndian<u16> year;
BigEndian<u16> month;
BigEndian<u16> day;
BigEndian<u16> hour;
BigEndian<u16> minutes;
BigEndian<u16> seconds;
DateTimeNumber profile_creation_time;
BigEndian<u32> profile_file_signature;
BigEndian<u32> primary_platform;
@ -78,6 +126,12 @@ ErrorOr<DeviceClass> parse_device_class(ICCHeader const& header)
return Error::from_string_literal("ICC::Profile: Invalid device class");
}
ErrorOr<time_t> parse_creation_date_time(ICCHeader const& header)
{
// iCC v4, 7.2.8 Date and time field
return parse_date_time_number(header.profile_creation_time);
}
ErrorOr<void> parse_file_signature(ICCHeader const& header)
{
// iCC v4, 7.2.9 Profile file signature field
@ -121,6 +175,7 @@ ErrorOr<NonnullRefPtr<Profile>> Profile::try_load_from_externally_owned_memory(R
TRY(parse_file_signature(header));
profile->m_version = TRY(parse_version(header));
profile->m_device_class = TRY(parse_device_class(header));
profile->m_creation_timestamp = TRY(parse_creation_date_time(header));
return profile;
}

View file

@ -51,10 +51,12 @@ public:
Version version() const { return m_version; }
DeviceClass device_class() const { return m_device_class; }
time_t creation_timestamp() const { return m_creation_timestamp; }
private:
Version m_version;
DeviceClass m_device_class;
time_t m_creation_timestamp;
};
}