summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNico Weber <thakis@chromium.org>2022-12-27 13:37:37 -0500
committerBrian Gianforcaro <b.gianfo@gmail.com>2022-12-27 15:58:39 -0800
commit782ab0a11f650e8b44341855d9cd93ac489e1900 (patch)
tree78456021648445152d30076f27d97208ee3800ce
parentbb1f6f71f1c1efc675958390a52a9e52d9a9fd7b (diff)
downloadserenity-782ab0a11f650e8b44341855d9cd93ac489e1900.zip
LibGfx: Read profile creation time from ICCProfile header
-rw-r--r--Userland/Libraries/LibGfx/ICCProfile.cpp67
-rw-r--r--Userland/Libraries/LibGfx/ICCProfile.h2
2 files changed, 63 insertions, 6 deletions
diff --git a/Userland/Libraries/LibGfx/ICCProfile.cpp b/Userland/Libraries/LibGfx/ICCProfile.cpp
index 15621e4124..c1e0fa3ae3 100644
--- a/Userland/Libraries/LibGfx/ICCProfile.cpp
+++ b/Userland/Libraries/LibGfx/ICCProfile.cpp
@@ -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;
}
diff --git a/Userland/Libraries/LibGfx/ICCProfile.h b/Userland/Libraries/LibGfx/ICCProfile.h
index f28cee6267..2a918c205f 100644
--- a/Userland/Libraries/LibGfx/ICCProfile.h
+++ b/Userland/Libraries/LibGfx/ICCProfile.h
@@ -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;
};
}