diff --git a/Userland/Libraries/LibGfx/CMakeLists.txt b/Userland/Libraries/LibGfx/CMakeLists.txt index 892259cc12..c3fbb6f56f 100644 --- a/Userland/Libraries/LibGfx/CMakeLists.txt +++ b/Userland/Libraries/LibGfx/CMakeLists.txt @@ -28,6 +28,7 @@ set(SOURCES Font/WOFF/Font.cpp GradientPainting.cpp GIFLoader.cpp + ICC/BinaryWriter.cpp ICC/Profile.cpp ICC/Tags.cpp ICC/TagTypes.cpp diff --git a/Userland/Libraries/LibGfx/ICC/BinaryWriter.cpp b/Userland/Libraries/LibGfx/ICC/BinaryWriter.cpp new file mode 100644 index 0000000000..a403734113 --- /dev/null +++ b/Userland/Libraries/LibGfx/ICC/BinaryWriter.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include +#include + +namespace Gfx::ICC { + +ErrorOr encode(Profile const& profile) +{ + + // Leaves enough room for the profile header and the tag table count. + // FIXME: Serialize tag data and write tag table and tag data too. + auto bytes = TRY(ByteBuffer::create_zeroed(sizeof(ICCHeader) + sizeof(u32))); + + VERIFY(bytes.size() >= sizeof(ICCHeader)); + auto& raw_header = *bit_cast(bytes.data()); + + raw_header.profile_size = bytes.size(); + raw_header.preferred_cmm_type = profile.preferred_cmm_type().value_or(PreferredCMMType { 0 }); + + raw_header.profile_version_major = profile.version().major_version(); + raw_header.profile_version_minor_bugfix = profile.version().minor_and_bugfix_version(); + raw_header.profile_version_zero = 0; + + raw_header.profile_device_class = profile.device_class(); + raw_header.data_color_space = profile.data_color_space(); + raw_header.profile_connection_space = profile.connection_space(); + + time_t profile_timestamp = profile.creation_timestamp(); + struct tm tm; + if (!gmtime_r(&profile_timestamp, &tm)) + return Error::from_errno(errno); + raw_header.profile_creation_time.year = tm.tm_year + 1900; + raw_header.profile_creation_time.month = tm.tm_mon + 1; + raw_header.profile_creation_time.day = tm.tm_mday; + raw_header.profile_creation_time.hours = tm.tm_hour; + raw_header.profile_creation_time.minutes = tm.tm_min; + raw_header.profile_creation_time.seconds = tm.tm_sec; + + raw_header.profile_file_signature = ProfileFileSignature; + raw_header.primary_platform = profile.primary_platform().value_or(PrimaryPlatform { 0 }); + + raw_header.profile_flags = profile.flags().bits(); + raw_header.device_manufacturer = profile.device_manufacturer().value_or(DeviceManufacturer { 0 }); + raw_header.device_model = profile.device_model().value_or(DeviceModel { 0 }); + raw_header.device_attributes = profile.device_attributes().bits(); + raw_header.rendering_intent = profile.rendering_intent(); + + raw_header.pcs_illuminant = profile.pcs_illuminant(); + + raw_header.profile_creator = profile.creator().value_or(Creator { 0 }); + + memset(raw_header.reserved, 0, sizeof(raw_header.reserved)); + + auto id = Profile::compute_id(bytes); + static_assert(sizeof(id.data) == sizeof(raw_header.profile_id)); + memcpy(raw_header.profile_id, id.data, sizeof(id.data)); + + return bytes; +} + +} diff --git a/Userland/Libraries/LibGfx/ICC/BinaryWriter.h b/Userland/Libraries/LibGfx/ICC/BinaryWriter.h new file mode 100644 index 0000000000..7b741b4d9d --- /dev/null +++ b/Userland/Libraries/LibGfx/ICC/BinaryWriter.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2023, Nico Weber + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace Gfx::ICC { + +class Profile; + +// Serializes a Profile object. +// Ignores the Profile's on_disk_size() and id() and recomputes them instead. +// Also ignores and the offsets and sizes in tag data. +// But if the profile has its tag data in tag order and has a computed id, +// it's a goal that encode(Profile::try_load_from_externally_owned_memory(bytes) returns `bytes`. +// Unconditionally computes a Profile ID (which is an MD5 hash of most of the contents, see Profile::compute_id()) and writes it to the output. +ErrorOr encode(Profile const&); + +}