MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ImageExport.cpp
Go to the documentation of this file.
1#include "ImageExport.hpp"
2
9
10namespace MayaFlux::IO {
11
12namespace {
13
15 using Portal::Graphics::TextureLoom;
16
17 /**
18 * @brief Emplace the correct variant in ImageData based on format byte-width.
19 *
20 * Sizes the vector in element counts (not bytes): one element per channel
21 * per pixel. The emplaced variant is left default-initialized to zero.
22 */
23 bool emplace_variant_for_format(ImageData& data, ImageFormat format,
24 size_t pixel_count, uint32_t channels)
25 {
26 const size_t element_count = pixel_count * channels;
27 const size_t bpp = TextureLoom::get_bytes_per_pixel(format);
28 const size_t bpe = channels > 0 ? (bpp / channels) : 0;
29
30 switch (bpe) {
31 case 1:
32 data.pixels.emplace<std::vector<uint8_t>>(element_count);
33 return true;
34 case 2:
35 data.pixels.emplace<std::vector<uint16_t>>(element_count);
36 return true;
37 case 4:
38 data.pixels.emplace<std::vector<float>>(element_count);
39 return true;
40 default:
41 return false;
42 }
43 }
44
45} // namespace
46
47// ============================================================================
48// download_image
49// ============================================================================
50
51std::optional<ImageData> download_image(
52 const std::shared_ptr<Core::VKImage>& image)
53{
54 if (!image) {
56 "download_image: null image");
57 return std::nullopt;
58 }
59
60 const vk::Format vk_format = image->get_format();
61 const auto format_opt = Portal::Graphics::TextureLoom::from_vulkan_format(vk_format);
62 if (!format_opt) {
64 "download_image: vk::Format {} has no ImageFormat mapping",
65 vk::to_string(vk_format));
66 return std::nullopt;
67 }
68
69 const ImageFormat format = *format_opt;
70 const uint32_t channels = Portal::Graphics::TextureLoom::get_channel_count(format);
71 if (channels == 0) {
73 "download_image: zero channels for format {}",
74 static_cast<int>(format));
75 return std::nullopt;
76 }
77
78 const uint32_t width = image->get_width();
79 const uint32_t height = image->get_height();
80 if (width == 0 || height == 0) {
82 "download_image: zero dimensions {}x{}", width, height);
83 return std::nullopt;
84 }
85
86 const size_t pixel_count = static_cast<size_t>(width) * height;
87 const size_t mip0_bytes = pixel_count * TextureLoom::get_bytes_per_pixel(format);
88
89 ImageData result;
90 result.width = width;
91 result.height = height;
92 result.channels = channels;
93 result.format = format;
94
95 if (!emplace_variant_for_format(result, format, pixel_count, channels)) {
97 "download_image: unsupported bytes-per-element for format {}",
98 static_cast<int>(format));
99 return std::nullopt;
100 }
101
102 TextureLoom::instance().download_data_async(
103 image,
104 const_cast<void*>(result.data()),
105 mip0_bytes);
106
107 if (!result.is_consistent()) {
109 "download_image: resulting ImageData failed is_consistent()");
110 return std::nullopt;
111 }
112
114 "download_image: {}x{}, {} channels, {} bytes",
115 width, height, channels, mip0_bytes);
116
117 return result;
118}
119
120// ============================================================================
121// download_texture_buffer
122// ============================================================================
123
124std::optional<ImageData> download_texture_buffer(
125 const std::shared_ptr<Buffers::TextureBuffer>& buffer)
126{
127 if (!buffer) {
129 "download_texture_buffer: null buffer");
130 return std::nullopt;
131 }
132
133 auto image = buffer->get_gpu_texture();
134 if (!image) {
136 "download_texture_buffer: buffer has no GPU texture yet "
137 "(has the buffer been processed at least once?)");
138 return std::nullopt;
139 }
140
141 return download_image(image);
142}
143
144// ============================================================================
145// save_image / save_texture_buffer / save_text_buffer
146// ============================================================================
147
149 const std::shared_ptr<Core::VKImage>& image,
150 const std::string& filepath,
151 const ImageWriteOptions& options)
152{
153 auto data = download_image(image);
154 if (!data) {
155 return false;
156 }
157
158 auto writer = ImageWriterRegistry::instance().create_writer(filepath);
159 if (!writer) {
161 "save_image: no writer registered for extension of '{}'", filepath);
162 return false;
163 }
164
165 const bool ok = writer->write(filepath, *data, options);
166 if (!ok) {
168 "save_image: writer failed: {}", writer->get_last_error());
169 }
170 return ok;
171}
172
174 const std::shared_ptr<Buffers::TextureBuffer>& buffer,
175 const std::string& filepath,
176 const ImageWriteOptions& options)
177{
178 if (!buffer) {
180 "save_texture_buffer: null buffer");
181 return false;
182 }
183 auto image = buffer->get_gpu_texture();
184 if (!image) {
186 "save_texture_buffer: buffer has no GPU texture");
187 return false;
188 }
189 return save_image(image, filepath, options);
190}
191
193 const std::shared_ptr<Buffers::TextBuffer>& buffer,
194 const std::string& filepath,
195 const ImageWriteOptions& options)
196{
197 return save_texture_buffer(buffer, filepath, options);
198}
199
200} // namespace MayaFlux::IO
#define MF_ERROR(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
IO::ImageData image
uint32_t width
std::unique_ptr< ImageWriter > create_writer(const std::string &filepath) const
static ImageWriterRegistry & instance()
static uint32_t get_channel_count(ImageFormat format)
Get the number of color channels for a given format.
static std::optional< ImageFormat > from_vulkan_format(vk::Format vk_format)
Convert Vulkan format to Portal ImageFormat.
bool save_texture_buffer(const std::shared_ptr< Buffers::TextureBuffer > &buffer, const std::string &filepath, const ImageWriteOptions &options)
Save a TextureBuffer's current GPU state to disk.
bool save_image(const std::shared_ptr< Core::VKImage > &image, const std::string &filepath, const ImageWriteOptions &options)
Save a VKImage directly to disk via the ImageWriter registry.
bool save_text_buffer(const std::shared_ptr< Buffers::TextBuffer > &buffer, const std::string &filepath, const ImageWriteOptions &options)
Save a TextBuffer's rendered glyph texture to disk.
std::optional< ImageData > download_texture_buffer(const std::shared_ptr< Buffers::TextureBuffer > &buffer)
Download a TextureBuffer's GPU texture into host ImageData.
std::optional< ImageData > download_image(const std::shared_ptr< Core::VKImage > &image)
Download pixel data from a GPU-resident VKImage into host ImageData.
@ FileIO
Filesystem I/O operations.
@ IO
Networking, file handling, streaming.
ImageFormat
User-friendly image format enum.
bool is_consistent() const
Check that the active variant matches the declared format.
Portal::Graphics::ImageFormat format
const void * data() const
Raw data pointer, dispatched on variant.
Raw image data loaded from file.
Configuration for image writing.