Add JpegWithTagsWriter helper

This commit is contained in:
Georg Hagen
2025-02-13 23:26:50 +01:00
parent fc70941cab
commit 7a82abcb18

View File

@@ -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 <algorithm>
#include <filesystem>
#include <span>
#include <string_view>
namespace OpenVulkano
{
class JpegWithTagsWriter final
{
FILE* file;
template<typename T> static T EndianSwap(T value)
{
T result;
char* ptr = reinterpret_cast<char*>(&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<uint8_t>& 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<uint8_t>& 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; }
};
}