MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
ImageReader.cpp
Go to the documentation of this file.
1#include "ImageReader.hpp"
2
4
7
9
10#ifdef MAYAFLUX_ARCH_X64
11#define STBI_SSE2
12#ifdef MAYAFLUX_COMPILER_MSVC
13#include <immintrin.h>
14#endif
15#endif
16
17#ifdef MAYAFLUX_ARCH_ARM64
18#define STBI_NEON
19#endif
20
21#define STBI_NO_FAILURE_STRINGS
22#define STB_IMAGE_IMPLEMENTATION
23#define STBI_NO_STDIO
24
25#if __has_include("stb/stb_image.h")
26#include "stb/stb_image.h"
27#elif __has_include("stb_image.h")
28#include "stb_image.h"
29#else
30#error "stb_image.h not found"
31#endif
32
33#include <tinyexr.h>
34
35#include <cstddef>
36#include <fstream>
37
38namespace MayaFlux::IO {
39
40namespace {
41
42 std::string extension_of(const std::filesystem::path& path)
43 {
44 auto ext = path.extension().string();
45 if (!ext.empty() && ext[0] == '.') {
46 ext = ext.substr(1);
47 }
48 std::ranges::transform(ext, ext.begin(),
49 [](unsigned char c) { return std::tolower(c); });
50 return ext;
51 }
52
53 std::optional<ImageData> crop_image(
54 const ImageData& image,
55 const Kakshya::Region& region)
56 {
57 const auto& start = region.start_coordinates;
58 const auto& end = region.end_coordinates;
59 if (start.size() != 2 || end.size() != 2
60 || start[0] > end[0] || start[1] > end[1]
61 || end[0] >= image.width || end[1] >= image.height) {
63 "Image crop region is outside the decoded image");
64 return std::nullopt;
65 }
66
67 const auto width = static_cast<uint32_t>(end[0] - start[0] + 1);
68 const auto height = static_cast<uint32_t>(end[1] - start[1] + 1);
69 auto cropped = ImageData::allocate(width, height, image.channels, image.format);
70 if (!cropped) {
71 return std::nullopt;
72 }
73
74 std::visit([&](const auto& source) {
75 using Storage = std::decay_t<decltype(source)>;
76 using Element = typename Storage::value_type;
77 auto& target = std::get<Storage>(cropped->pixels);
78 const size_t row_elements = static_cast<size_t>(width) * image.channels;
79 for (uint32_t y = 0; y < height; ++y) {
80 const size_t source_offset =
81 (static_cast<size_t>(start[1] + y) * image.width + start[0]) * image.channels;
82 std::memcpy(target.data() + static_cast<size_t>(y) * row_elements,
83 source.data() + source_offset, row_elements * sizeof(Element));
84 }
85 }, image.pixels);
86
87 return cropped;
88 }
89
90 /**
91 * @brief Decode an OpenEXR file from an in-memory byte buffer.
92 *
93 * Uses tinyexr's ParseEXRHeaderFromMemory + LoadEXRImageFromMemory pair to
94 * access arbitrary channel counts and names. Reinterleaves tinyexr's planar
95 * output into pixel-interleaved float layout to match ImageData's contract.
96 *
97 * Maps channel count to ImageFormat (R32F / RG32F / RGBA32F). 3-channel EXR
98 * (BGR) is promoted to 4-channel (ABGR) with A=1.0 because we don't support
99 * a 3-channel float ImageFormat.
100 *
101 * Half-precision pixel types are converted to float on the fly; EXR files
102 * with mixed pixel types are not currently supported.
103 */
104 std::optional<ImageData> load_exr_from_memory(
105 const unsigned char* bytes, size_t size)
106 {
108
109 EXRVersion version;
110 if (ParseEXRVersionFromMemory(&version, bytes, size) != TINYEXR_SUCCESS) {
112 "EXR: failed to parse version");
113 return std::nullopt;
114 }
115 if (version.multipart || version.non_image) {
117 "EXR: multipart and deep images are not supported");
118 return std::nullopt;
119 }
120
121 EXRHeader header;
122 InitEXRHeader(&header);
123
124 const char* err = nullptr;
125 if (ParseEXRHeaderFromMemory(&header, &version, bytes, size, &err)
126 != TINYEXR_SUCCESS) {
128 "EXR: header parse failed: {}", err ? err : "unknown");
129 if (err)
130 FreeEXRErrorMessage(err);
131 return std::nullopt;
132 }
133
134 for (int i = 0; i < header.num_channels; ++i) {
135 if (header.pixel_types[i] == TINYEXR_PIXELTYPE_UINT) {
137 "EXR: uint pixel type channel '{}' not supported",
138 header.channels[i].name);
139 FreeEXRHeader(&header);
140 return std::nullopt;
141 }
142 header.requested_pixel_types[i] = TINYEXR_PIXELTYPE_FLOAT;
143 }
144
145 EXRImage image;
146 InitEXRImage(&image);
147
148 if (LoadEXRImageFromMemory(&image, &header, bytes, size, &err)
149 != TINYEXR_SUCCESS) {
151 "EXR: image load failed: {}", err ? err : "unknown");
152 if (err)
153 FreeEXRErrorMessage(err);
154 FreeEXRHeader(&header);
155 return std::nullopt;
156 }
157
158 const auto width = static_cast<uint32_t>(image.width);
159 const auto height = static_cast<uint32_t>(image.height);
160 const auto src_channels = static_cast<uint32_t>(image.num_channels);
161
162 if (width == 0 || height == 0 || src_channels == 0) {
164 "EXR: zero dimensions or channels");
165 FreeEXRImage(&image);
166 FreeEXRHeader(&header);
167 return std::nullopt;
168 }
169
170 uint32_t out_channels = src_channels;
171 F format;
172 switch (src_channels) {
173 case 1:
174 format = F::R32F;
175 break;
176 case 2:
177 format = F::RG32F;
178 break;
179 case 3:
180 out_channels = 4;
181 format = F::RGBA32F;
182 break;
183 case 4:
184 format = F::RGBA32F;
185 break;
186 default:
188 "EXR: {} channels not supported (1/2/3/4 only)", src_channels);
189 FreeEXRImage(&image);
190 FreeEXRHeader(&header);
191 return std::nullopt;
192 }
193
194 const size_t pixel_count = static_cast<size_t>(width) * height;
195 const size_t element_count = pixel_count * out_channels;
196
197 ImageData result;
198 auto& dst = result.pixels.emplace<std::vector<float>>(element_count, 0.0F);
199
200 auto** planes = reinterpret_cast<float**>(image.images);
201
202 for (size_t p = 0; p < pixel_count; ++p) {
203 for (uint32_t c = 0; c < src_channels; ++c) {
204 dst[p * out_channels + c] = planes[c][p];
205 }
206 if (out_channels > src_channels) {
207 dst[p * out_channels + (out_channels - 1)] = 1.0F;
208 }
209 }
210
211 result.width = width;
212 result.height = height;
213 result.channels = out_channels;
214 result.format = format;
215
217 "Loaded EXR: {}x{}, {} channels{}",
218 width, height, out_channels,
219 (src_channels == 3) ? " [BGR→ABGR, A=1.0]" : "");
220
221 FreeEXRImage(&image);
222 FreeEXRHeader(&header);
223
224 return result;
225 }
226
227} // namespace
228
229[[maybe_unused]] static bool g_stb_simd_logged = []() {
230#ifdef STBI_SSE2
232 "STB Image: SSE2 SIMD optimizations enabled (x64)");
233#elif defined(STBI_NEON)
235 "STB Image: NEON SIMD optimizations enabled (ARM64)");
236#else
238 "STB Image: No SIMD optimizations (scalar fallback)");
239#endif
240 return true;
241}();
242
243ImageReader::ImageReader(int desired_channels,
244 std::optional<Kakshya::Region> region)
245 : m_desired_channels(desired_channels)
246 , m_region(std::move(region))
247 , m_is_open(false)
248{
249}
250
255
256bool ImageReader::can_read(const std::string& filepath) const
257{
258 auto ext = std::filesystem::path(filepath).extension().string();
259 if (!ext.empty() && ext[0] == '.') {
260 ext = ext.substr(1);
261 }
262
263 static const std::vector<std::string> supported = {
264 "png", "jpg", "jpeg", "bmp", "tga", "psd", "gif", "hdr", "pic", "pnm",
265 "exr"
266 };
267
268 return std::ranges::find(supported, ext) != supported.end();
269}
270
271bool ImageReader::open(const std::string& filepath, FileReadOptions /*options*/)
272{
273 if (m_is_open) {
274 close();
275 }
276
277 if (!can_read(filepath)) {
278 m_last_error = "Unsupported image format: " + filepath;
280 return false;
281 }
282
283 auto resolved = resolve_path(filepath);
284
286
287 if (!m_image_data) {
288 m_last_error = "Failed to load image data";
289 return false;
290 }
291
292 m_filepath = filepath;
293 m_is_open = true;
294
296 "Opened image: {} ({}x{}, {} channels)",
297 filepath, m_image_data->width, m_image_data->height, m_image_data->channels);
298
299 return true;
300}
301
303{
304 if (m_is_open) {
305 m_image_data.reset();
306 m_filepath.clear();
307 m_is_open = false;
308 }
309}
310
312{
313 return m_is_open;
314}
315
316std::optional<FileMetadata> ImageReader::get_metadata() const
317{
318 if (!m_is_open || !m_image_data) {
319 return std::nullopt;
320 }
321
322 FileMetadata meta;
323 meta.format = "8-bit";
324
325 meta.attributes["width"] = m_image_data->width;
326 meta.attributes["height"] = m_image_data->height;
328
329 return meta;
330}
331
332std::vector<FileRegion> ImageReader::get_regions() const
333{
334 // Images don't typically have regions
335 return {};
336}
337
338std::vector<Kakshya::DataVariant> ImageReader::read_all()
339{
340 if (!m_is_open || !m_image_data) {
341 m_last_error = "No image open";
342 return {};
343 }
344
345 return std::visit(
346 [](const auto& vec) -> std::vector<Kakshya::DataVariant> {
347 return { Kakshya::DataVariant { vec } };
348 },
349 m_image_data->pixels);
350}
351
352std::vector<Kakshya::DataVariant> ImageReader::read_region(const FileRegion& region)
353{
354 if (!m_is_open || !m_image_data) {
355 m_last_error = "No image open";
356 return {};
357 }
358
359 if (region.start_coordinates.size() < 2 || region.end_coordinates.size() < 2) {
360 m_last_error = "Invalid region coordinates for image";
361 return {};
362 }
363
364 auto x_start = static_cast<uint32_t>(region.start_coordinates[0]);
365 auto y_start = static_cast<uint32_t>(region.start_coordinates[1]);
366 auto x_end = static_cast<uint32_t>(region.end_coordinates[0]);
367 auto y_end = static_cast<uint32_t>(region.end_coordinates[1]);
368
369 if (x_end > m_image_data->width || y_end > m_image_data->height) {
370 m_last_error = "Region out of bounds";
371 return {};
372 }
373
374 uint32_t region_width = x_end - x_start;
375 uint32_t region_height = y_end - y_start;
376
377 const size_t bytes_per_pixel = m_image_data->byte_size() / m_image_data->element_count() * m_image_data->channels;
378 const size_t bytes_per_elem = m_image_data->byte_size() / m_image_data->element_count();
379 const size_t pixel_stride_bytes = bytes_per_elem * m_image_data->channels;
380
381 std::vector<uint8_t> region_data(
382 static_cast<size_t>(region_width) * region_height * pixel_stride_bytes);
383
384 const auto* src = static_cast<const uint8_t*>(m_image_data->data());
385
386 for (uint32_t y = 0; y < region_height; ++y) {
387 size_t src_offset = (static_cast<size_t>((y_start + y) * m_image_data->width + x_start)) * pixel_stride_bytes;
388 size_t dst_offset = static_cast<size_t>(y * region_width) * pixel_stride_bytes;
389 size_t row_size = static_cast<size_t>(region_width) * pixel_stride_bytes;
390 std::memcpy(
391 region_data.data() + dst_offset,
392 src + src_offset,
393 row_size);
394 }
395
396 return { region_data };
397}
398
399std::shared_ptr<Kakshya::SignalSourceContainer> ImageReader::create_container()
400{
401 m_last_error = "Images use direct GPU texture creation, not containers";
402 return nullptr;
403}
404
405bool ImageReader::load_into_container(std::shared_ptr<Kakshya::SignalSourceContainer> /*container*/)
406{
407 m_last_error = "Images cannot be loaded into SignalSourceContainer";
408 return false;
409}
410
411std::vector<uint64_t> ImageReader::get_read_position() const
412{
413 return { 0, 0 };
414}
415
416bool ImageReader::seek(const std::vector<uint64_t>& /*position*/)
417{
418 return true;
419}
420
421std::vector<std::string> ImageReader::get_supported_extensions() const
422{
423 return { "png", "jpg", "jpeg", "bmp", "tga", "psd", "gif", "hdr", "pic", "pnm",
424 "exr" };
425}
426
427std::type_index ImageReader::get_data_type() const
428{
429 return typeid(std::vector<uint8_t>);
430}
431
432std::type_index ImageReader::get_container_type() const
433{
434 return typeid(void);
435}
436
438{
439 return m_last_error;
440}
441
443{
444 return false;
445}
446
448{
449 return 0;
450}
451
453{
454 return 2;
455}
456
457std::vector<uint64_t> ImageReader::get_dimension_sizes() const
458{
459 if (!m_is_open || !m_image_data) {
460 return {};
461 }
462 return { m_image_data->width, m_image_data->height };
463}
464
465//==============================================================================
466// Static Utility Methods
467//==============================================================================
468
469std::optional<ImageData> ImageReader::load(
470 const std::filesystem::path& path, int desired_channels,
471 std::optional<Kakshya::Region> region)
472{
473 auto resolved = std::filesystem::path(resolve_path(path.string()));
474 if (!std::filesystem::exists(resolved)) {
476 "Image file not found: {}", resolved.string());
477 return std::nullopt;
478 }
479
480 std::ifstream file(resolved, std::ios::binary | std::ios::ate);
481 if (!file.is_open()) {
483 "Failed to open image file: {}", resolved.string());
484 return std::nullopt;
485 }
486
487 std::streamsize file_size = file.tellg();
488 file.seekg(0, std::ios::beg);
489
490 std::vector<unsigned char> file_buffer(file_size);
491 if (!file.read(reinterpret_cast<char*>(file_buffer.data()), file_size)) {
493 "Failed to read image file: {}", resolved.string());
494 return std::nullopt;
495 }
496 file.close();
497
498 if (extension_of(resolved) == "exr") {
499 (void)desired_channels;
500 auto image = load_exr_from_memory(file_buffer.data(), static_cast<size_t>(file_size));
501 if (image && region) {
502 return crop_image(*image, *region);
503 }
504 return image;
505 }
506
507 int width {}, height {}, channels {};
508
509 if (desired_channels == 0) {
510 stbi_info_from_memory(file_buffer.data(), static_cast<int>(file_buffer.size()),
511 &width, &height, &channels);
512 if (channels == 3) {
513 desired_channels = 4;
514 }
515 }
516
517 unsigned char* pixels = stbi_load_from_memory(
518 file_buffer.data(),
519 static_cast<int>(file_buffer.size()),
520 &width, &height, &channels,
521 desired_channels);
522
523 if (!pixels) {
525 "Failed to decode image: {} - {}",
526 path.string(), stbi_failure_reason());
527 return std::nullopt;
528 }
529
530 int result_channels = (desired_channels != 0) ? desired_channels : channels;
531
533 "Loaded image: {} ({}x{}, {} channels{})",
534 resolved.filename().string(), width, height, result_channels,
535 (channels == 3 && result_channels == 4) ? " [RGB→RGBA]" : "");
536
537 ImageData result;
538 auto& buf = result.pixels.emplace<std::vector<uint8_t>>();
539 size_t data_size = static_cast<size_t>(width) * height * result_channels;
540 buf.resize(data_size);
541 std::memcpy(buf.data(), pixels, data_size);
542
543 result.width = width;
544 result.height = height;
545 result.channels = result_channels;
546
547 switch (result_channels) {
548 case 1:
549 result.format = Portal::Graphics::ImageFormat::R8;
550 break;
551 case 2:
552 result.format = Portal::Graphics::ImageFormat::RG8;
553 break;
554 case 4:
555 result.format = Portal::Graphics::ImageFormat::RGBA8;
556 break;
557 default:
559 "Unsupported channel count: {}", result_channels);
560 stbi_image_free(pixels);
561 return std::nullopt;
562 }
563
564 stbi_image_free(pixels);
565 if (region) {
566 return crop_image(result, *region);
567 }
568 return result;
569}
570
571std::optional<ImageData> ImageReader::load(
572 const std::string& path, int desired_channels,
573 std::optional<Kakshya::Region> region)
574{
575 return load(std::filesystem::path(path), desired_channels, std::move(region));
576}
577
578std::optional<ImageData> ImageReader::load_from_memory(const void* data, size_t size)
579{
580 if (!data || size == 0) {
582 "Invalid memory buffer for image loading");
583 return std::nullopt;
584 }
585
586 const auto* bytes = static_cast<const unsigned char*>(data);
587
588 if (size >= 4
589 && bytes[0] == 0x76 && bytes[1] == 0x2F
590 && bytes[2] == 0x31 && bytes[3] == 0x01) {
591 return load_exr_from_memory(bytes, size);
592 }
593
594 int width {}, height {}, channels {};
595
596 stbi_info_from_memory(bytes, static_cast<int>(size),
597 &width, &height, &channels);
598
599 int load_as = (channels == 3) ? 4 : 0;
600
601 unsigned char* pixels = stbi_load_from_memory(
602 bytes,
603 static_cast<int>(size),
604 &width, &height, &channels,
605 load_as);
606
607 if (!pixels) {
609 "Failed to decode image from memory: {}",
610 stbi_failure_reason());
611 return std::nullopt;
612 }
613
614 int result_channels = (load_as != 0) ? load_as : channels;
615
617 "Loaded image from memory ({}x{}, {} channels{})",
618 width, height, result_channels,
619 (channels == 3 && result_channels == 4) ? " [RGB→RGBA]" : "");
620
621 ImageData result;
622 auto& buf = result.pixels.emplace<std::vector<uint8_t>>();
623 size_t data_size = static_cast<size_t>(width) * height * result_channels;
624 buf.resize(data_size);
625 std::memcpy(buf.data(), pixels, data_size);
626
627 result.width = width;
628 result.height = height;
629 result.channels = result_channels;
630
631 switch (result_channels) {
632 case 1:
633 result.format = Portal::Graphics::ImageFormat::R8;
634 break;
635 case 2:
636 result.format = Portal::Graphics::ImageFormat::RG8;
637 break;
638 case 4:
639 result.format = Portal::Graphics::ImageFormat::RGBA8;
640 break;
641 default:
643 "Unsupported channel count: {}", result_channels);
644 stbi_image_free(pixels);
645 return std::nullopt;
646 }
647
648 stbi_image_free(pixels);
649 return result;
650}
651
652std::shared_ptr<Core::VKImage> ImageReader::load_texture(
653 const std::string& path, int desired_channels,
654 std::optional<Kakshya::Region> region)
655{
656 auto image_data = load(path, desired_channels, std::move(region));
657 if (!image_data) {
658 return nullptr;
659 }
660
662 auto texture = mgr.create_2d(
663 image_data->width,
664 image_data->height,
665 image_data->format,
666 image_data->data());
667
668 if (texture) {
670 "Created GPU texture from image: {}", path);
671 }
672
673 return texture;
674}
675
676std::optional<ImageData> ImageReader::get_image_data() const
677{
678 return m_image_data;
679}
680
681std::shared_ptr<Buffers::TextureBuffer> ImageReader::create_texture_buffer()
682{
683 if (!m_is_open || !m_image_data) {
684 m_last_error = "No image open";
685 return nullptr;
686 }
687
688 auto tex_buffer = std::make_shared<Buffers::TextureBuffer>(
689 m_image_data->width, m_image_data->height,
690 m_image_data->format, m_image_data->data());
691
693 "Created TextureBuffer from image: {}x{} ({} bytes)",
694 m_image_data->width, m_image_data->height, tex_buffer->get_size_bytes());
695
696 return tex_buffer;
697}
698
699bool ImageReader::load_into_buffer(const std::shared_ptr<Buffers::VKBuffer>& buffer)
700{
701 if (!m_is_open || !m_image_data) {
702 m_last_error = "No image open";
703 return false;
704 }
705
706 if (!buffer || !buffer->is_initialized()) {
707 m_last_error = "Invalid or uninitialized buffer";
708 return false;
709 }
710
711 size_t required_size = m_image_data->byte_size();
712 if (buffer->get_size_bytes() < required_size) {
713 m_last_error = "Buffer too small for image data";
714 return false;
715 }
716
718 m_image_data->data(),
719 required_size,
720 buffer);
721
723 "Loaded image into VKBuffer: {}x{} ({} bytes)",
724 m_image_data->width, m_image_data->height, required_size);
725
726 return true;
727}
728
729} // namespace MayaFlux::IO
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
IO::ImageData image
Definition Decoder.cpp:47
const std::vector< float > * pixels
Definition Decoder.cpp:48
bool format
bool size
std::shared_ptr< NetworkGeometryBuffer > buffer
uint32_t * dst
const uint32_t * src
uint32_t channels
uint32_t width
uint32_t height
static std::string resolve_path(const std::string &filepath)
Resolve a filepath against the project source root if not found as-is.
static std::optional< ImageData > load_from_memory(const void *data, size_t size)
Load image from memory (static utility)
std::type_index get_container_type() const override
Get the container type this reader creates.
std::shared_ptr< Kakshya::SignalSourceContainer > create_container() override
Create and initialize a container from the file.
std::vector< std::string > get_supported_extensions() const override
Get supported file extensions for this reader.
std::type_index get_data_type() const override
Get the data type this reader produces.
static std::optional< ImageData > load(const std::string &path, int desired_channels=4, std::optional< Kakshya::Region > region=std::nullopt)
Load image from file (static utility)
static std::shared_ptr< Core::VKImage > load_texture(const std::string &path, int desired_channels=4, std::optional< Kakshya::Region > region=std::nullopt)
Load image directly into GPU texture (static utility)
std::optional< ImageData > m_image_data
bool is_open() const override
Check if a file is currently open.
bool supports_streaming() const override
Check if streaming is supported for the current file.
std::shared_ptr< Buffers::TextureBuffer > create_texture_buffer()
Create a VKBuffer containing the loaded image pixel data.
bool open(const std::string &filepath, FileReadOptions options=FileReadOptions::ALL) override
Open a file for reading.
size_t get_num_dimensions() const override
Get the dimensionality of the file data.
bool load_into_container(std::shared_ptr< Kakshya::SignalSourceContainer > container) override
Load file data into an existing container.
std::vector< Kakshya::DataVariant > read_region(const FileRegion &region) override
Read a specific region of data.
std::optional< Kakshya::Region > m_region
std::vector< Kakshya::DataVariant > read_all() override
Read all data from the file into memory.
std::vector< uint64_t > get_dimension_sizes() const override
Get size of each dimension in the file data.
std::optional< FileMetadata > get_metadata() const override
Get metadata from the open file.
std::vector< FileRegion > get_regions() const override
Get semantic regions from the file.
bool load_into_buffer(const std::shared_ptr< Buffers::VKBuffer > &buffer)
Load image directly into an existing VKBuffer.
bool seek(const std::vector< uint64_t > &position) override
Seek to a specific position in the file.
std::vector< uint64_t > get_read_position() const override
Get current read position in primary dimension.
void close() override
Close the currently open file.
bool can_read(const std::string &filepath) const override
Check if a file can be read by this reader.
ImageReader(int desired_channels=4, std::optional< Kakshya::Region > region=std::nullopt)
Configure channel conversion and an optional source-pixel crop for open().
std::optional< ImageData > get_image_data() const
Get the loaded image data.
std::string get_last_error() const override
Get the last error message.
uint64_t get_preferred_chunk_size() const override
Get the preferred chunk size for streaming.
void upload_to_gpu(const void *data, size_t size, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging, size_t dst_offset)
Upload raw data to GPU buffer (auto-detects host-visible vs device-local)
FileReadOptions
Generic options for file reading behavior.
Kakshya::ImageData ImageData
static bool g_stb_simd_logged
@ FileIO
Filesystem I/O operations.
@ Init
Engine/subsystem initialization.
@ IO
Networking, file handling, streaming.
std::variant< std::vector< double >, std::vector< float >, std::vector< uint8_t >, std::vector< uint16_t >, std::vector< uint32_t >, std::vector< std::complex< float > >, std::vector< std::complex< double > >, std::vector< glm::vec2 >, std::vector< glm::vec3 >, std::vector< glm::vec4 >, std::vector< glm::mat4 > > DataVariant
Multi-type data storage for different precision needs.
Definition NDData.hpp:70
@ IMAGE_COLOR
2D RGB/RGBA image
MAYAFLUX_API TextureLoom & get_texture_manager()
Get the global texture manager instance.
Kakshya::ImageFormat ImageFormat
Portal name for Kakshya::ImageFormat, which maps to vk::Format in TextureLoom.
std::unordered_map< std::string, std::any > attributes
Type-specific metadata stored as key-value pairs (e.g., sample rate, channels)
std::string format
File format identifier (e.g., "wav", "mp3", "hdf5")
Generic metadata structure for any file type.
std::vector< uint64_t > start_coordinates
N-dimensional start position (e.g., frame, x, y)
std::vector< uint64_t > end_coordinates
N-dimensional end position (inclusive)
Generic region descriptor for any file type.
static std::optional< ImageData > allocate(uint32_t width, uint32_t height, uint32_t channels, ImageFormat format)
Allocate zeroed pixel storage of the variant format calls for.
Definition ImageData.cpp:5
Raw image data loaded from file or read back from a GPU image.
Definition ImageData.hpp:55