From 7a82abcb189a98b06c7a2b97c50e320173599670 Mon Sep 17 00:00:00 2001 From: Georg Hagen Date: Thu, 13 Feb 2025 23:26:50 +0100 Subject: [PATCH] Add JpegWithTagsWriter helper --- openVulkanoCpp/Image/JpegWithTagsWriter.hpp | 98 +++++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 openVulkanoCpp/Image/JpegWithTagsWriter.hpp diff --git a/openVulkanoCpp/Image/JpegWithTagsWriter.hpp b/openVulkanoCpp/Image/JpegWithTagsWriter.hpp new file mode 100644 index 0000000..e7dffc3 --- /dev/null +++ b/openVulkanoCpp/Image/JpegWithTagsWriter.hpp @@ -0,0 +1,98 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include +#include +#include +#include + +namespace OpenVulkano +{ + class JpegWithTagsWriter final + { + FILE* file; + + template static T EndianSwap(T value) + { + T result; + + char* ptr = reinterpret_cast(&value); + std::reverse(ptr, ptr + sizeof(T)); + result = value; + + return result; + } + + void Check() const + { + if (file == nullptr) throw std::runtime_error("Can't append to jpeg file, it has already been completed."); + } + + // ReSharper disable once CppDFAConstantParameter + void WriteAppMarker(const int app = 1) const + { + // ReSharper disable once CppDFAConstantConditions + if (app > 0xF) [[unlikely]] throw std::runtime_error("App marker is out of range."); + fputc(0xFF, file); + fputc(0xE0 + app, file); + } + + void Write16(uint16_t value) const + { + value = EndianSwap(value); + fwrite(&value, sizeof(value), 1, file); + } + + void Write32(uint32_t value) const + { + value = EndianSwap(value); + fwrite(&value, sizeof(value), 1, file); + } + + public: + JpegWithTagsWriter(const std::filesystem::path& filePath) + { + file = fopen(filePath.c_str(), "wb"); + if (file == nullptr) throw std::runtime_error("Can't open file."); + // Write soi marker + fputc(0xFF, file); + fputc(0xD8, file); + } + + void WriteExifTag(const std::span& exifTag) const + { + WriteAppMarker(); + Write16(exifTag.size() + 2); // 2 byte extra for the size value itself + fwrite(exifTag.data(), sizeof(uint8_t), exifTag.size(), file); + } + + void WriteXmpTag(const std::string& xmpTag) const + { + constexpr std::string_view XMP_MARKER = "http://ns.adobe.com/xap/1.0/"; + WriteAppMarker(1); + Write16(xmpTag.size() + XMP_MARKER.size() + 3); // 1 byte extra for null termination, 2 byte extra for the size value itself + fwrite(XMP_MARKER.data(), XMP_MARKER.size(), 1, file); + fputc(0, file); // Null terminate string + fwrite(xmpTag.c_str(), 1, xmpTag.size(), file); + } + + // Must be called as last function, as this will finalize the image writing and will close the file + void WriteImageData(const std::span& imageData) + { + Check(); + // write image data and skip soi marker from og data + fwrite(imageData.data() + 2, sizeof(uint8_t), imageData.size() - 2, file); + fclose(file); + file = nullptr; + } + + operator bool() const { return file != nullptr; } + + bool IsOpen() const { return file != nullptr; } + }; +}