diff --git a/example/src/main.cpp b/example/src/main.cpp index a1cbe40..991b880 100644 --- a/example/src/main.cpp +++ b/example/src/main.cpp @@ -9,7 +9,7 @@ #include "pixel.h" #include "misc/mapped_file/mapped_file.h" #include "stb_image_loader.h" -#include "render/texture2d.h" +#include "texture/texture2d.h" #include "widget/widget_new.h" #include "widget/leaf_widget/mimage.h" @@ -17,18 +17,8 @@ int main(int argc, char* argv[]) { mirage_app::get().init(); - auto i = std::make_shared(); auto ii = std::make_shared(); - { - auto file = mapped_file::create(); - file->map_file(L"Z:/Root/Local/NanakoDisk/涩图/可爱偷猴计划/蕾米莉亚 & 芙兰朵露/1708702057588.jpg"); - stb_image_loader image_loader; - image_loader.init(file->get_data(), file->get_size()); - const auto& heap = image_loader.load(); - - i->create(heap->data, { heap->info.width, heap->info.height }, heap->info.get_pixel_format()); - } { auto file = mapped_file::create(); file->map_file(L"Z:/Root/Local/NanakoDisk/涩图/可爱偷猴计划/蕾米莉亚 & 芙兰朵露/1712938522333.jpg"); @@ -36,11 +26,18 @@ int main(int argc, char* argv[]) { image_loader.init(file->get_data(), file->get_size()); const auto& heap = image_loader.load(); - ii->create(heap->data, { heap->info.width, heap->info.height }, heap->info.get_pixel_format()); + texture2d_data data; + data.pixel_format = heap->info.get_pixel_format(); + data.size = { heap->info.width, heap->info.height }; + data.data_ptr = heap->data; + + ii->create(data); + } + { + auto map = ii->map(texture_map_type::read); + map->read_only; } - auto image = std::make_shared(); - image->set_image(i); auto image2 = std::make_shared(); image2->set_image(ii); image2->set_sampler(sampler_type::linear_clamp); @@ -49,15 +46,6 @@ int main(int argc, char* argv[]) { window->set_content( mnew(mh_box) - mslot(mh_box) - [ - mnew(mborder) - [ - image - ] - .margin({ 5 }) - ] - mslot(mh_box) [ image2 diff --git a/src/mirage_core/misc/mapped_file/unix/mapped_file.cpp b/src/mirage_core/misc/mapped_file/unix/mapped_file.cpp index f19ab41..d970b03 100644 --- a/src/mirage_core/misc/mapped_file/unix/mapped_file.cpp +++ b/src/mirage_core/misc/mapped_file/unix/mapped_file.cpp @@ -67,12 +67,14 @@ bool mapped_file_unix::map_file(const std::wstring& filename) { std::string utf8_filename(filename.begin(), filename.end()); fd = open(utf8_filename.c_str(), O_RDONLY); if (fd == -1) { + // 无法打开文件 throw std::runtime_error("Failed to open file"); } struct stat sb; if (fstat(fd, &sb) == -1) { cleanup(); + // 无法获取文件大小 throw std::runtime_error("Failed to get file size"); } size = static_cast(sb.st_size); @@ -80,6 +82,7 @@ bool mapped_file_unix::map_file(const std::wstring& filename) { data = mmap(nullptr, size, PROT_READ, MAP_PRIVATE, fd, 0); if (data == MAP_FAILED) { cleanup(); + // 无法映射文件 throw std::runtime_error("Failed to map file"); } diff --git a/src/mirage_core/misc/mapped_file/windows/mapped_file.cpp b/src/mirage_core/misc/mapped_file/windows/mapped_file.cpp index 6e2dc37..e41982f 100644 --- a/src/mirage_core/misc/mapped_file/windows/mapped_file.cpp +++ b/src/mirage_core/misc/mapped_file/windows/mapped_file.cpp @@ -82,12 +82,14 @@ bool mapped_file_win::map_file(const std::wstring& filename) { ); if (file_handle_ == INVALID_HANDLE_VALUE) { + // 无法打开文件 throw std::runtime_error("Failed to open file"); } LARGE_INTEGER file_size; if (!GetFileSizeEx(file_handle_, &file_size)) { cleanup(); + // 无法获取文件大小 throw std::runtime_error("Failed to get file size"); } size_ = static_cast(file_size.QuadPart); @@ -103,6 +105,7 @@ bool mapped_file_win::map_file(const std::wstring& filename) { if (mapping_handle_ == nullptr) { cleanup(); + // 无法创建文件映射 throw std::runtime_error("Failed to create file mapping"); } @@ -116,6 +119,7 @@ bool mapped_file_win::map_file(const std::wstring& filename) { if (data_ == nullptr) { cleanup(); + // 无法映射文件视图 throw std::runtime_error("Failed to map view of file"); } diff --git a/src/mirage_image/image_accessor_factory.h b/src/mirage_image/image_accessor_factory.h new file mode 100644 index 0000000..2f11586 --- /dev/null +++ b/src/mirage_image/image_accessor_factory.h @@ -0,0 +1,192 @@ +#pragma once +#include + +#include "pixel.h" +#include "sokol_gfx.h" + +/** + * @brief 像素格式工厂 + * + * 提供从sg_pixel_format到对应像素类型的转换功能 + */ +struct image_accessor_factory { + /** + * @brief 获取与sg_pixel_format对应的像素类型信息 + * + * @param format 像素格式 + * @return std::tuple 类型名称、元素大小和通道数 + */ + static std::tuple get_pixel_type_info(const sg_pixel_format& format) { + switch (format) { + // 8位单通道格式 + case SG_PIXELFORMAT_R8: + return {"uint8_t", sizeof(uint8_t), 1}; + case SG_PIXELFORMAT_R8SN: + return {"int8_t", sizeof(int8_t), 1}; + case SG_PIXELFORMAT_R8UI: + return {"uint8_t", sizeof(uint8_t), 1}; + case SG_PIXELFORMAT_R8SI: + return {"int8_t", sizeof(int8_t), 1}; + + // 16位单通道和8位双通道格式 + case SG_PIXELFORMAT_R16: + return {"uint16_t", sizeof(uint16_t), 1}; + case SG_PIXELFORMAT_R16SN: + return {"int16_t", sizeof(int16_t), 1}; + case SG_PIXELFORMAT_R16UI: + return {"uint16_t", sizeof(uint16_t), 1}; + case SG_PIXELFORMAT_R16SI: + return {"int16_t", sizeof(int16_t), 1}; + case SG_PIXELFORMAT_R16F: + return {"half", sizeof(Eigen::half), 1}; + case SG_PIXELFORMAT_RG8: + return {"uint8_t", sizeof(uint8_t), 2}; + case SG_PIXELFORMAT_RG8SN: + return {"int8_t", sizeof(int8_t), 2}; + case SG_PIXELFORMAT_RG8UI: + return {"uint8_t", sizeof(uint8_t), 2}; + case SG_PIXELFORMAT_RG8SI: + return {"int8_t", sizeof(int8_t), 2}; + + // 32位单通道和16位双通道和8位四通道格式 + case SG_PIXELFORMAT_R32UI: + return {"uint32_t", sizeof(uint32_t), 1}; + case SG_PIXELFORMAT_R32SI: + return {"int32_t", sizeof(int32_t), 1}; + case SG_PIXELFORMAT_R32F: + return {"float", sizeof(float), 1}; + case SG_PIXELFORMAT_RG16: + return {"uint16_t", sizeof(uint16_t), 2}; + case SG_PIXELFORMAT_RG16SN: + return {"int16_t", sizeof(int16_t), 2}; + case SG_PIXELFORMAT_RG16UI: + return {"uint16_t", sizeof(uint16_t), 2}; + case SG_PIXELFORMAT_RG16SI: + return {"int16_t", sizeof(int16_t), 2}; + case SG_PIXELFORMAT_RG16F: + return {"half", sizeof(Eigen::half), 2}; + case SG_PIXELFORMAT_RGBA8: + case SG_PIXELFORMAT_SRGB8A8: + case SG_PIXELFORMAT_BGRA8: + return {"uint8_t", sizeof(uint8_t), 4}; + case SG_PIXELFORMAT_RGBA8SN: + return {"int8_t", sizeof(int8_t), 4}; + case SG_PIXELFORMAT_RGBA8UI: + return {"uint8_t", sizeof(uint8_t), 4}; + case SG_PIXELFORMAT_RGBA8SI: + return {"int8_t", sizeof(int8_t), 4}; + + // 64位双通道和32位四通道格式 + case SG_PIXELFORMAT_RG32UI: + return {"uint32_t", sizeof(uint32_t), 2}; + case SG_PIXELFORMAT_RG32SI: + return {"int32_t", sizeof(int32_t), 2}; + case SG_PIXELFORMAT_RG32F: + return {"float", sizeof(float), 2}; + case SG_PIXELFORMAT_RGBA16: + return {"uint16_t", sizeof(uint16_t), 4}; + case SG_PIXELFORMAT_RGBA16SN: + return {"int16_t", sizeof(int16_t), 4}; + case SG_PIXELFORMAT_RGBA16UI: + return {"uint16_t", sizeof(uint16_t), 4}; + case SG_PIXELFORMAT_RGBA16SI: + return {"int16_t", sizeof(int16_t), 4}; + case SG_PIXELFORMAT_RGBA16F: + return {"half", sizeof(Eigen::half), 4}; + + // 128位四通道格式 + case SG_PIXELFORMAT_RGBA32UI: + return {"uint32_t", sizeof(uint32_t), 4}; + case SG_PIXELFORMAT_RGBA32SI: + return {"int32_t", sizeof(int32_t), 4}; + case SG_PIXELFORMAT_RGBA32F: + return {"float", sizeof(float), 4}; + + // 特殊格式和压缩格式不直接支持 + default: + return {"unknown", 0, 0}; + } + } + + /** + * @brief 判断是否可以直接支持该像素格式 + * + * @param format 像素格式 + * @return bool 如果格式可以用pixel表示则返回true + */ + static bool is_supported(const sg_pixel_format format) { + auto [type_name, element_size, channels] = get_pixel_type_info(format); + return element_size > 0 && channels > 0; + } + + /** + * @brief 创建新的图像访问器 + * + * 根据像素格式创建适当类型的图像访问器 + * + * @param format 像素格式 + * @param data 图像数据指针 + * @param width 图像宽度 + * @param height 图像高度 + * @return void* 创建的图像访问器指针(需要根据格式自行转换为正确类型) + */ + // static image_accessor_interface* create_image_accessor(const sg_pixel_format format, void* data, int width, int height) { + // // 根据格式创建对应类型的图像访问器 + // switch (format) { + // case SG_PIXELFORMAT_R8: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_R8SN: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RG8: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RG8SN: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RGBA8: + // case SG_PIXELFORMAT_SRGB8A8: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RGBA8SN: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_BGRA8: + // return new image_accessor>(data, width, height); // 需要单独处理RGB顺序 + // + // case SG_PIXELFORMAT_R16: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_R16SN: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_R16F: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RG16: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RG16SN: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RG16F: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RGBA16: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RGBA16SN: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RGBA16F: + // return new image_accessor>(data, width, height); + // + // case SG_PIXELFORMAT_R32F: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RG32F: + // return new image_accessor>(data, width, height); + // case SG_PIXELFORMAT_RGBA32F: + // return new image_accessor>(data, width, height); + // + // default: + // return nullptr; + // } + // } + + /** + * @brief 删除图像访问器 + * + * @param accessor 图像访问器指针 + */ + // static void destroy_image_accessor(image_accessor_interface* accessor) { + // delete accessor; + // } +}; + diff --git a/src/mirage_image/pixel.h b/src/mirage_image/pixel.h index 7e45d2e..e2b5531 100644 --- a/src/mirage_image/pixel.h +++ b/src/mirage_image/pixel.h @@ -1795,6 +1795,7 @@ struct image_accessor : image_accessor_interface { */ P& get_pixel(const int x, const int y) { if (x < 0 || x >= width || y < 0 || y >= height) { + // 超出图像范围 throw std::out_of_range("Pixel access out of bounds."); } const int pixel_size = sizeof(P); @@ -1926,192 +1927,6 @@ private: } }; -/** - * @brief 像素格式工厂 - * - * 提供从sg_pixel_format到对应像素类型的转换功能 - */ -struct pixel_factory { - /** - * @brief 获取与sg_pixel_format对应的像素类型信息 - * - * @param format 像素格式 - * @return std::tuple 类型名称、元素大小和通道数 - */ - static std::tuple get_pixel_type_info(const sg_pixel_format format) { - switch (format) { - // 8位单通道格式 - case SG_PIXELFORMAT_R8: - return {"uint8_t", sizeof(uint8_t), 1}; - case SG_PIXELFORMAT_R8SN: - return {"int8_t", sizeof(int8_t), 1}; - case SG_PIXELFORMAT_R8UI: - return {"uint8_t", sizeof(uint8_t), 1}; - case SG_PIXELFORMAT_R8SI: - return {"int8_t", sizeof(int8_t), 1}; - - // 16位单通道和8位双通道格式 - case SG_PIXELFORMAT_R16: - return {"uint16_t", sizeof(uint16_t), 1}; - case SG_PIXELFORMAT_R16SN: - return {"int16_t", sizeof(int16_t), 1}; - case SG_PIXELFORMAT_R16UI: - return {"uint16_t", sizeof(uint16_t), 1}; - case SG_PIXELFORMAT_R16SI: - return {"int16_t", sizeof(int16_t), 1}; - case SG_PIXELFORMAT_R16F: - return {"half", sizeof(Eigen::half), 1}; - case SG_PIXELFORMAT_RG8: - return {"uint8_t", sizeof(uint8_t), 2}; - case SG_PIXELFORMAT_RG8SN: - return {"int8_t", sizeof(int8_t), 2}; - case SG_PIXELFORMAT_RG8UI: - return {"uint8_t", sizeof(uint8_t), 2}; - case SG_PIXELFORMAT_RG8SI: - return {"int8_t", sizeof(int8_t), 2}; - - // 32位单通道和16位双通道和8位四通道格式 - case SG_PIXELFORMAT_R32UI: - return {"uint32_t", sizeof(uint32_t), 1}; - case SG_PIXELFORMAT_R32SI: - return {"int32_t", sizeof(int32_t), 1}; - case SG_PIXELFORMAT_R32F: - return {"float", sizeof(float), 1}; - case SG_PIXELFORMAT_RG16: - return {"uint16_t", sizeof(uint16_t), 2}; - case SG_PIXELFORMAT_RG16SN: - return {"int16_t", sizeof(int16_t), 2}; - case SG_PIXELFORMAT_RG16UI: - return {"uint16_t", sizeof(uint16_t), 2}; - case SG_PIXELFORMAT_RG16SI: - return {"int16_t", sizeof(int16_t), 2}; - case SG_PIXELFORMAT_RG16F: - return {"half", sizeof(Eigen::half), 2}; - case SG_PIXELFORMAT_RGBA8: - case SG_PIXELFORMAT_SRGB8A8: - case SG_PIXELFORMAT_BGRA8: - return {"uint8_t", sizeof(uint8_t), 4}; - case SG_PIXELFORMAT_RGBA8SN: - return {"int8_t", sizeof(int8_t), 4}; - case SG_PIXELFORMAT_RGBA8UI: - return {"uint8_t", sizeof(uint8_t), 4}; - case SG_PIXELFORMAT_RGBA8SI: - return {"int8_t", sizeof(int8_t), 4}; - - // 64位双通道和32位四通道格式 - case SG_PIXELFORMAT_RG32UI: - return {"uint32_t", sizeof(uint32_t), 2}; - case SG_PIXELFORMAT_RG32SI: - return {"int32_t", sizeof(int32_t), 2}; - case SG_PIXELFORMAT_RG32F: - return {"float", sizeof(float), 2}; - case SG_PIXELFORMAT_RGBA16: - return {"uint16_t", sizeof(uint16_t), 4}; - case SG_PIXELFORMAT_RGBA16SN: - return {"int16_t", sizeof(int16_t), 4}; - case SG_PIXELFORMAT_RGBA16UI: - return {"uint16_t", sizeof(uint16_t), 4}; - case SG_PIXELFORMAT_RGBA16SI: - return {"int16_t", sizeof(int16_t), 4}; - case SG_PIXELFORMAT_RGBA16F: - return {"half", sizeof(Eigen::half), 4}; - - // 128位四通道格式 - case SG_PIXELFORMAT_RGBA32UI: - return {"uint32_t", sizeof(uint32_t), 4}; - case SG_PIXELFORMAT_RGBA32SI: - return {"int32_t", sizeof(int32_t), 4}; - case SG_PIXELFORMAT_RGBA32F: - return {"float", sizeof(float), 4}; - - // 特殊格式和压缩格式不直接支持 - default: - return {"unknown", 0, 0}; - } - } - - /** - * @brief 判断是否可以直接支持该像素格式 - * - * @param format 像素格式 - * @return bool 如果格式可以用pixel表示则返回true - */ - static bool is_supported(const sg_pixel_format format) { - auto [type_name, element_size, channels] = get_pixel_type_info(format); - return element_size > 0 && channels > 0; - } - - /** - * @brief 创建新的图像访问器 - * - * 根据像素格式创建适当类型的图像访问器 - * - * @param format 像素格式 - * @param data 图像数据指针 - * @param width 图像宽度 - * @param height 图像高度 - * @return void* 创建的图像访问器指针(需要根据格式自行转换为正确类型) - */ - static image_accessor_interface* create_image_accessor(const sg_pixel_format format, void* data, int width, int height) { - // 根据格式创建对应类型的图像访问器 - switch (format) { - case SG_PIXELFORMAT_R8: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_R8SN: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RG8: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RG8SN: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RGBA8: - case SG_PIXELFORMAT_SRGB8A8: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RGBA8SN: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_BGRA8: - return new image_accessor>(data, width, height); // 需要单独处理RGB顺序 - - case SG_PIXELFORMAT_R16: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_R16SN: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_R16F: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RG16: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RG16SN: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RG16F: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RGBA16: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RGBA16SN: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RGBA16F: - return new image_accessor>(data, width, height); - - case SG_PIXELFORMAT_R32F: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RG32F: - return new image_accessor>(data, width, height); - case SG_PIXELFORMAT_RGBA32F: - return new image_accessor>(data, width, height); - - default: - return nullptr; - } - } - - /** - * @brief 删除图像访问器 - * - * @param accessor 图像访问器指针 - */ - static void destroy_image_accessor(image_accessor_interface* accessor) { - delete accessor; - } -}; - // 预定义的常用像素类型 // 8位整数类型 diff --git a/src/mirage_render/render/texture2d.h b/src/mirage_render/render/texture2d.h deleted file mode 100644 index 6330134..0000000 --- a/src/mirage_render/render/texture2d.h +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include - -#include "sokol_gfx.h" -#include "pixel.h" - -/** - * @class texture2d - * @brief 渲染图像类 - * - * 用于静态图片的渲染,提供创建和清除操作。 - */ -class texture2d { -public: - ~texture2d() { - clear(); - } - void create(void* in_data, const Eigen::Vector2i& in_size, sg_pixel_format in_pixel_format); - void clear() { - if (sg_isvalid()) - sg_destroy_image(image_); - } - - [[nodiscard]] const auto& get_image() const { return image_; } - [[nodiscard]] const auto& get_size() const { return size_; } - [[nodiscard]] const auto& get_pixel_format() const { return pixel_format_; } -private: - sg_image image_{}; - sg_pixel_format pixel_format_{}; - Eigen::Vector2i size_{}; -}; diff --git a/src/mirage_render/render/windows/texture2d.cpp b/src/mirage_render/render/windows/texture2d.cpp deleted file mode 100644 index bbb6b6c..0000000 --- a/src/mirage_render/render/windows/texture2d.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#include "render/texture2d.h" -#include -#include - -#include "pixel_format_convert.h" -#include "windows_render_context.h" - -void texture2d::create(void* in_data, const Eigen::Vector2i& in_size, sg_pixel_format in_pixel_format) { - auto format_info = sg_query_pixelformat(in_pixel_format); - pixel_format_ = in_pixel_format; - size_ = in_size; - - sg_image_desc sg_desc{}; - sg_desc.type = SG_IMAGETYPE_2D; - sg_desc.render_target = false; - sg_desc.width = in_size.x(); - sg_desc.height = in_size.y(); - sg_desc.num_slices = 1; - sg_desc.num_mipmaps = 1; - sg_desc.usage = SG_USAGE_IMMUTABLE; - sg_desc.pixel_format = in_pixel_format; - sg_desc.sample_count = 1; - sg_desc.data.subimage[0][0].ptr = in_data; - sg_desc.data.subimage[0][0].size = in_size.x() * in_size.y() * format_info.bytes_per_pixel; - - image_ = sg_make_image(sg_desc); -} diff --git a/src/mirage_render/texture/atlas/atlas_allocator.h b/src/mirage_render/texture/atlas/atlas_allocator.h new file mode 100644 index 0000000..df9df05 --- /dev/null +++ b/src/mirage_render/texture/atlas/atlas_allocator.h @@ -0,0 +1,54 @@ +#pragma once +/** + * @file atlas_allocator.h + * @brief 纹理图集分配器接口 + */ +#include "geometry/rect.h" +#include +#include + +/** + * @class atlas_allocator + * @brief 纹理图集分配器接口 + */ +class atlas_allocator { +public: + virtual ~atlas_allocator() = default; + + /** + * @brief 分配指定大小的区域 + * @param size 要分配的区域大小 + * @return 分配的区域,失败返回std::nullopt + */ + virtual std::optional> allocate(const Eigen::Vector2i& size) = 0; + + /** + * @brief 释放之前分配的区域 + * @param rect 要释放的区域 + * @return 是否成功释放 + */ + virtual bool free(const rect_t& rect) = 0; + + /** + * @brief 重置分配器 + */ + virtual void reset() = 0; + + /** + * @brief 调整分配器大小 + * @param new_size 新的大小 + */ + virtual void resize(const Eigen::Vector2i& new_size) = 0; + + /** + * @brief 获取总面积 + * @return 总面积 + */ + virtual int total_area() const = 0; + + /** + * @brief 获取已分配面积 + * @return 已分配面积 + */ + virtual int allocated_area() const = 0; +}; diff --git a/src/mirage_render/texture/atlas/bin_packing_allocator.cpp b/src/mirage_render/texture/atlas/bin_packing_allocator.cpp new file mode 100644 index 0000000..5a481ea --- /dev/null +++ b/src/mirage_render/texture/atlas/bin_packing_allocator.cpp @@ -0,0 +1,106 @@ +/** + * @file bin_packing_allocator.cpp + * @brief 二叉树分割纹理分配算法实现 + */ +#include "bin_packing_allocator.h" + +bin_packing_allocator::bin_packing_allocator(const Eigen::Vector2i& size) + : size_(size) +{ + reset(); +} + +std::optional> bin_packing_allocator::allocate(const Eigen::Vector2i& size) { + // 检查尺寸是否有效 + if (size.x() <= 0 || size.y() <= 0 || size.x() > size_.x() || size.y() > size_.y()) { + return std::nullopt; + } + + // 寻找可以容纳的节点 + Node* node = find_node(root_.get(), size); + if (!node) { + return std::nullopt; // 没有足够空间 + } + + // 分割该节点以最佳利用空间 + node = split_node(node, size); + node->used = true; + + // 更新已分配区域 + allocated_area_ += size.x() * size.y(); + + // 返回分配的矩形 + return node->rect; +} + +bool bin_packing_allocator::free(const rect_t& rect) { + // 二叉树分配算法不支持单个矩形的释放 + return false; +} + +void bin_packing_allocator::reset() { + // 重新创建根节点 + root_ = std::make_unique(rect_t({0, 0}, {size_.x(), size_.y()})); + allocated_area_ = 0; +} + +void bin_packing_allocator::resize(const Eigen::Vector2i& new_size) { + size_ = new_size; + reset(); +} + +int bin_packing_allocator::total_area() const { + return size_.x() * size_.y(); +} + +int bin_packing_allocator::allocated_area() const { + return allocated_area_; +} + +bin_packing_allocator::Node* bin_packing_allocator::find_node(Node* node, const Eigen::Vector2i& size) { + if (!node) { + return nullptr; + } + + // 如果节点已被使用或大小不足,继续搜索 + if (node->used) { + // 尝试右侧节点 + if (auto found = find_node(node->right.get(), size)) { + return found; + } + // 尝试下方节点 + return find_node(node->down.get(), size); + } + else if (size.x() <= node->rect.width() && size.y() <= node->rect.height()) { + // 找到合适的未使用节点 + return node; + } + + return nullptr; +} + +bin_packing_allocator::Node* bin_packing_allocator::split_node(Node* node, const Eigen::Vector2i& size) { + // 标记当前节点为已使用 + node->used = true; + + // 创建右侧剩余空间节点 + if (node->rect.width() > size.x()) { + int right_width = node->rect.width() - size.x(); + node->right = std::make_unique( + rect_t({node->rect.left() + size.x(), node->rect.top()}, + {right_width, size.y()})); + } + + // 创建底部剩余空间节点 + if (node->rect.height() > size.y()) { + int bottom_height = node->rect.height() - size.y(); + node->down = std::make_unique( + rect_t({node->rect.left(), node->rect.top() + size.y()}, + {node->rect.width(), bottom_height})); + } + + // 调整当前节点的大小为请求的大小 + node->rect = rect_t({node->rect.left(), node->rect.top()}, {size.x(), size.y()}); + + return node; +} diff --git a/src/mirage_render/texture/atlas/bin_packing_allocator.h b/src/mirage_render/texture/atlas/bin_packing_allocator.h new file mode 100644 index 0000000..e2d2f92 --- /dev/null +++ b/src/mirage_render/texture/atlas/bin_packing_allocator.h @@ -0,0 +1,49 @@ +#pragma once +/** + * @file bin_packing_allocator.h + * @brief 二叉树分割纹理分配算法 + */ +#include "atlas_allocator.h" +#include + +/** + * @class bin_packing_allocator + * @brief 二叉树分割纹理分配算法 + */ +class bin_packing_allocator : public atlas_allocator { +public: + /** + * @brief 构造函数 + * @param size 分配区域的总大小 + */ + explicit bin_packing_allocator(const Eigen::Vector2i& size); + ~bin_packing_allocator() override = default; + + std::optional> allocate(const Eigen::Vector2i& size) override; + bool free(const rect_t& rect) override; + void reset() override; + void resize(const Eigen::Vector2i& new_size) override; + int total_area() const override; + int allocated_area() const override; + +private: + struct Node { + rect_t rect; + bool used = false; + std::unique_ptr right; + std::unique_ptr down; + + Node() = default; + explicit Node(const rect_t& r) : rect(r) {} + }; + + std::unique_ptr root_; + Eigen::Vector2i size_; + int allocated_area_ = 0; + + // 在给定节点中递归查找可放置位置 + Node* find_node(Node* node, const Eigen::Vector2i& size); + + // 分割节点 + Node* split_node(Node* node, const Eigen::Vector2i& size); +}; diff --git a/src/mirage_render/texture/atlas/grid_allocator.cpp b/src/mirage_render/texture/atlas/grid_allocator.cpp new file mode 100644 index 0000000..8c5df5a --- /dev/null +++ b/src/mirage_render/texture/atlas/grid_allocator.cpp @@ -0,0 +1,154 @@ +/** + * @file grid_allocator.cpp + * @brief 网格式纹理分配算法实现 + */ +#include "grid_allocator.h" + +grid_allocator::grid_allocator(const Eigen::Vector2i& size, const Eigen::Vector2i& cell_size) + : size_(size), cell_size_(cell_size) +{ + // 计算网格维度 + grid_dims_.x() = (size.x() + cell_size.x() - 1) / cell_size.x(); + grid_dims_.y() = (size.y() + cell_size.y() - 1) / cell_size.y(); + + // 初始化所有单元格为未使用 + cells_.resize(grid_dims_.x() * grid_dims_.y(), false); + allocated_cells_ = 0; +} + +std::optional> grid_allocator::allocate(const Eigen::Vector2i& size) { + // 计算需要多少个单元格来容纳请求的大小 + Eigen::Vector2i num_cells( + (size.x() + cell_size_.x() - 1) / cell_size_.x(), + (size.y() + cell_size_.y() - 1) / cell_size_.y() + ); + + // 如果请求的大小超过整个网格,返回失败 + if (num_cells.x() > grid_dims_.x() || num_cells.y() > grid_dims_.y()) { + return std::nullopt; + } + + // 查找足够的连续单元格 + auto start_cell_opt = find_free_cells(num_cells); + if (!start_cell_opt) { + return std::nullopt; // 没有足够的空间 + } + + // 找到了合适的位置,标记这些单元格为已使用 + Eigen::Vector2i start_cell = *start_cell_opt; + for (int y = 0; y < num_cells.y(); ++y) { + for (int x = 0; x < num_cells.x(); ++x) { + cells_[cell_index(start_cell.x() + x, start_cell.y() + y)] = true; + } + } + + // 更新已分配的单元格数量 + allocated_cells_ += num_cells.x() * num_cells.y(); + + // 计算实际像素矩形 + Eigen::Vector2i position = start_cell.cwiseProduct(cell_size_); + return rect_t(position, size); +} + +bool grid_allocator::free(const rect_t& rect) { + // 计算对应的单元格范围 + Eigen::Vector2i start_cell( + rect.left() / cell_size_.x(), + rect.top() / cell_size_.y() + ); + + Eigen::Vector2i num_cells( + (rect.width() + cell_size_.x() - 1) / cell_size_.x(), + (rect.height() + cell_size_.y() - 1) / cell_size_.y() + ); + + // 验证单元格范围是否有效 + if (start_cell.x() < 0 || start_cell.y() < 0 || + start_cell.x() + num_cells.x() > grid_dims_.x() || + start_cell.y() + num_cells.y() > grid_dims_.y()) { + return false; + } + + // 检查所有单元格是否都是已分配的 + for (int y = 0; y < num_cells.y(); ++y) { + for (int x = 0; x < num_cells.x(); ++x) { + int idx = cell_index(start_cell.x() + x, start_cell.y() + y); + if (!cells_[idx]) { + return false; // 至少有一个单元格未被分配 + } + } + } + + // 释放这些单元格 + for (int y = 0; y < num_cells.y(); ++y) { + for (int x = 0; x < num_cells.x(); ++x) { + cells_[cell_index(start_cell.x() + x, start_cell.y() + y)] = false; + } + } + + // 更新已分配的单元格数量 + allocated_cells_ -= num_cells.x() * num_cells.y(); + return true; +} + +void grid_allocator::reset() { + // 将所有单元格重置为未使用 + std::fill(cells_.begin(), cells_.end(), false); + allocated_cells_ = 0; +} + +void grid_allocator::resize(const Eigen::Vector2i& new_size) { + size_ = new_size; + + // 重新计算网格维度 + Eigen::Vector2i new_grid_dims( + (new_size.x() + cell_size_.x() - 1) / cell_size_.x(), + (new_size.y() + cell_size_.y() - 1) / cell_size_.y() + ); + + // 如果网格维度没有变化,不需要重新分配 + if (new_grid_dims == grid_dims_) { + return; + } + + // 更新网格维度并重置单元格 + grid_dims_ = new_grid_dims; + cells_.resize(grid_dims_.x() * grid_dims_.y()); + reset(); +} + +int grid_allocator::total_area() const { + return size_.x() * size_.y(); +} + +int grid_allocator::allocated_area() const { + return allocated_cells_ * cell_size_.x() * cell_size_.y(); +} + +int grid_allocator::cell_index(int x, int y) const { + return y * grid_dims_.x() + x; +} + +std::optional grid_allocator::find_free_cells(const Eigen::Vector2i& num_cells) { + // 遍历整个网格,寻找能容纳请求大小的连续空闲单元格 + for (int y = 0; y <= grid_dims_.y() - num_cells.y(); ++y) { + for (int x = 0; x <= grid_dims_.x() - num_cells.x(); ++x) { + bool can_fit = true; + + // 检查这块区域的所有单元格是否都是空闲的 + for (int cy = 0; cy < num_cells.y() && can_fit; ++cy) { + for (int cx = 0; cx < num_cells.x() && can_fit; ++cx) { + if (cells_[cell_index(x + cx, y + cy)]) { + can_fit = false; + } + } + } + + if (can_fit) { + return Eigen::Vector2i(x, y); + } + } + } + + return std::nullopt; // 没有找到合适的空间 +} diff --git a/src/mirage_render/texture/atlas/grid_allocator.h b/src/mirage_render/texture/atlas/grid_allocator.h new file mode 100644 index 0000000..b5cd25e --- /dev/null +++ b/src/mirage_render/texture/atlas/grid_allocator.h @@ -0,0 +1,42 @@ +#pragma once +/** + * @file grid_allocator.h + * @brief 网格式纹理分配算法 + */ +#include "atlas_allocator.h" +#include + +/** + * @class grid_allocator + * @brief 网格式纹理分配算法 + */ +class grid_allocator : public atlas_allocator { +public: + /** + * @brief 构造函数 + * @param size 分配区域的总大小 + * @param cell_size 单元格大小 + */ + grid_allocator(const Eigen::Vector2i& size, const Eigen::Vector2i& cell_size); + ~grid_allocator() override = default; + + std::optional> allocate(const Eigen::Vector2i& size) override; + bool free(const rect_t& rect) override; + void reset() override; + void resize(const Eigen::Vector2i& new_size) override; + int total_area() const override; + int allocated_area() const override; + +private: + Eigen::Vector2i size_; + Eigen::Vector2i cell_size_; + Eigen::Vector2i grid_dims_; + std::vector cells_; + int allocated_cells_ = 0; + + // 计算网格索引 + int cell_index(int x, int y) const; + + // 查找可容纳纹理的网格起始位置 + std::optional find_free_cells(const Eigen::Vector2i& num_cells); +}; diff --git a/src/mirage_render/texture/atlas/skyline_allocator.cpp b/src/mirage_render/texture/atlas/skyline_allocator.cpp new file mode 100644 index 0000000..89846c4 --- /dev/null +++ b/src/mirage_render/texture/atlas/skyline_allocator.cpp @@ -0,0 +1,225 @@ +/** + * @file skyline_allocator.cpp + * @brief 天际线纹理分配算法实现 + */ +#include "skyline_allocator.h" +#include +#include + +skyline_allocator::skyline_allocator(const Eigen::Vector2i& size) + : size_(size) +{ + reset(); +} + +std::optional> skyline_allocator::allocate(const Eigen::Vector2i& size) { + // 检查尺寸是否有效 + if (size.x() <= 0 || size.y() <= 0 || size.x() > size_.x() || size.y() > size_.y()) { + return std::nullopt; + } + + // 查找可用位置 + auto pos_opt = find_position(size); + if (!pos_opt) { + return std::nullopt; // 没有找到合适的位置 + } + + // 分配成功,更新天际线 + Eigen::Vector2i pos = *pos_opt; + update_skyline(pos, size); + + // 创建矩形并存储 + rect_t rect({pos.x(), pos.y()}, {size.x(), size.y()}); + int id = next_id_++; + allocated_rects_[id] = rect; + + // 更新已分配面积 + allocated_area_ += size.x() * size.y(); + + return rect; +} + +bool skyline_allocator::free(const rect_t& rect) { + // 在已分配矩形中查找匹配的矩形 + for (auto it = allocated_rects_.begin(); it != allocated_rects_.end(); ++it) { + if (it->second == rect) { + // 找到了匹配的矩形,从分配表中移除 + allocated_area_ -= rect.width() * rect.height(); + allocated_rects_.erase(it); + + // 注意:天际线分配器不支持单个矩形的高效释放 + // 我们需要重建整个天际线,这是一种折衷 + reset(); + + // 重新分配所有剩余的矩形 + std::vector> rects_to_reallocate; + for (const auto& [id, r] : allocated_rects_) { + rects_to_reallocate.push_back(r); + } + + // 清空已分配记录,因为我们要重新分配 + allocated_rects_.clear(); + allocated_area_ = 0; + next_id_ = 1; + + // 重新分配所有矩形 + for (const auto& r : rects_to_reallocate) { + Eigen::Vector2i r_size(r.width(), r.height()); + auto pos_opt = find_position(r_size); + if (pos_opt) { + update_skyline(*pos_opt, r_size); + int id = next_id_++; + allocated_rects_[id] = rect_t(*pos_opt, r_size); + allocated_area_ += r_size.x() * r_size.y(); + } + } + + return true; + } + } + + return false; // 没有找到匹配的矩形 +} + +void skyline_allocator::reset() { + // 初始状态只有一条线段:沿着底部的一条水平线 + skyline_.clear(); + skyline_.push_back({0, 0, size_.x()}); + allocated_area_ = 0; + allocated_rects_.clear(); + next_id_ = 1; +} + +void skyline_allocator::resize(const Eigen::Vector2i& new_size) { + size_ = new_size; + reset(); // 调整大小会导致所有分配失效 +} + +int skyline_allocator::total_area() const { + return size_.x() * size_.y(); +} + +int skyline_allocator::allocated_area() const { + return allocated_area_; +} + +std::optional skyline_allocator::find_position(const Eigen::Vector2i& size) { + int best_height = std::numeric_limits::max(); + int best_width = std::numeric_limits::max(); + int best_index = -1; + Eigen::Vector2i best_pos; + + // 遍历所有天际线段,查找最佳放置位置 + for (size_t i = 0; i < skyline_.size(); ++i) { + auto& segment = skyline_[i]; + + // 尝试将矩形放在当前线段上 + Eigen::Vector2i pos(segment.x, segment.y); + + // 检查宽度是否足够 + if (segment.width < size.x()) { + continue; + } + + // 计算放置此矩形后的高度 + int height = segment.y + size.y(); + + // 检查高度是否超出边界 + if (height > size_.y()) { + continue; + } + + // 使用"最低高度优先"的策略 + // 如果有多个相同高度的位置,选择宽度最小的 + if (height < best_height || (height == best_height && segment.width < best_width)) { + best_height = height; + best_index = static_cast(i); + best_pos = pos; + best_width = segment.width; + } + } + + if (best_index == -1) { + return std::nullopt; // 没有找到合适的位置 + } + + return best_pos; +} + +void skyline_allocator::update_skyline(const Eigen::Vector2i& pos, const Eigen::Vector2i& size) { + // 找到受影响的线段范围 + int start_index = -1; + int end_index = -1; + + for (size_t i = 0; i < skyline_.size(); ++i) { + auto& segment = skyline_[i]; + + // 找到第一个与分配区域重叠的线段 + if (start_index == -1) { + if (segment.x + segment.width > pos.x()) { + start_index = static_cast(i); + } + } + + // 找到最后一个与分配区域重叠的线段 + if (start_index != -1 && segment.x >= pos.x() + size.x()) { + end_index = static_cast(i); + break; + } + } + + if (end_index == -1) { + end_index = static_cast(skyline_.size()); + } + + // 计算新的天际线段 + int new_y = pos.y() + size.y(); + int new_start_x = pos.x(); + int new_end_x = pos.x() + size.x(); + + // 处理受影响区域的前一个线段 + if (start_index > 0 && skyline_[start_index - 1].x + skyline_[start_index - 1].width > pos.x()) { + // 如果前一个线段与分配区域重叠,截断它 + skyline_[start_index - 1].width = pos.x() - skyline_[start_index - 1].x; + } + + // 创建新的线段 + Segment new_segment = {new_start_x, new_y, new_end_x - new_start_x}; + + // 处理受影响区域的下一个线段 + if (end_index < static_cast(skyline_.size()) && skyline_[end_index].x < new_end_x) { + // 如果下一个线段与分配区域重叠,调整它 + int width_diff = (skyline_[end_index].x + skyline_[end_index].width) - new_end_x; + if (width_diff > 0) { + // 截断这个线段,使其从new_end_x开始 + skyline_[end_index].x = new_end_x; + skyline_[end_index].width = width_diff; + + // 插入新的线段 + skyline_.insert(skyline_.begin() + start_index, new_segment); + } else { + // 下一个线段完全被新线段覆盖,替换它 + skyline_[start_index] = new_segment; + // 删除被完全覆盖的线段 + if (end_index > start_index) { + skyline_.erase(skyline_.begin() + start_index + 1, skyline_.begin() + end_index + 1); + } + } + } else { + // 没有与下一个线段重叠 + skyline_[start_index] = new_segment; + // 删除被完全覆盖的线段 + if (end_index > start_index + 1) { + skyline_.erase(skyline_.begin() + start_index + 1, skyline_.begin() + end_index); + } + } + + // 合并相邻的相同高度线段 + for (size_t i = 0; i < skyline_.size() - 1; ++i) { + if (skyline_[i].y == skyline_[i + 1].y) { + skyline_[i].width += skyline_[i + 1].width; + skyline_.erase(skyline_.begin() + i + 1); + --i; + } + } +} diff --git a/src/mirage_render/texture/atlas/skyline_allocator.h b/src/mirage_render/texture/atlas/skyline_allocator.h new file mode 100644 index 0000000..cae5204 --- /dev/null +++ b/src/mirage_render/texture/atlas/skyline_allocator.h @@ -0,0 +1,63 @@ +#pragma once +/** + * @file skyline_allocator.h + * @brief 天际线纹理分配算法 + */ +#include "atlas_allocator.h" +#include +#include + +/** + * @class skyline_allocator + * @brief 天际线纹理分配算法 + * + * 天际线算法通过维护一系列水平线段来跟踪已分配区域的上边界。 + * 它在分配效率和空间利用率之间取得了良好的平衡。 + */ +class skyline_allocator : public atlas_allocator { +public: + /** + * @brief 构造函数 + * @param size 分配区域的总大小 + */ + explicit skyline_allocator(const Eigen::Vector2i& size); + ~skyline_allocator() override = default; + + std::optional> allocate(const Eigen::Vector2i& size) override; + bool free(const rect_t& rect) override; + void reset() override; + void resize(const Eigen::Vector2i& new_size) override; + int total_area() const override; + int allocated_area() const override; + +private: + /** + * @struct Segment + * @brief 表示天际线的一个水平线段 + */ + struct Segment { + int x; ///< 线段起始x坐标 + int y; ///< 线段的y坐标(高度) + int width; ///< 线段宽度 + }; + + Eigen::Vector2i size_; ///< 总区域大小 + std::vector skyline_; ///< 天际线段集合 + int allocated_area_ = 0; ///< 已分配的面积 + std::unordered_map> allocated_rects_; ///< 已分配的矩形,按ID索引 + int next_id_ = 1; ///< 下一个分配的ID + + /** + * @brief 查找可放置位置 + * @param size 要分配的大小 + * @return 可放置的位置,如果没有足够空间则返回std::nullopt + */ + std::optional find_position(const Eigen::Vector2i& size); + + /** + * @brief 更新天际线 + * @param pos 分配区域的位置 + * @param size 分配区域的大小 + */ + void update_skyline(const Eigen::Vector2i& pos, const Eigen::Vector2i& size); +}; diff --git a/src/mirage_render/texture/atlas/texture2d_atlas.cpp b/src/mirage_render/texture/atlas/texture2d_atlas.cpp new file mode 100644 index 0000000..fc32963 --- /dev/null +++ b/src/mirage_render/texture/atlas/texture2d_atlas.cpp @@ -0,0 +1,169 @@ +/** + * @file texture2d_atlas.cpp + * @brief 纹理图集类实现 + */ +#include "texture2d_atlas.h" +#include "bin_packing_allocator.h" +#include "grid_allocator.h" +#include "skyline_allocator.h" +#include "texture/texture_map_utils.h" // 从知识库引用 +#include + +texture2d_atlas::texture2d_atlas(const Eigen::Vector2i& size, sg_pixel_format pixel_format, + allocation_strategy_t strategy) +{ + // 创建底层纹理 + rw_texture2d_data tex_data; + tex_data.size = size; + tex_data.pixel_format = pixel_format; + tex_data.data_ptr = nullptr; // 初始为空 + texture_.create(tex_data); + + // 创建分配器 + allocator_ = create_allocator(strategy, size); +} + +texture2d_atlas::~texture2d_atlas() = default; + +std::optional> texture2d_atlas::allocate(const Eigen::Vector2i& size) { + auto region_opt = allocate_region(size); + if (region_opt) { + return region_opt->rect; + } + return std::nullopt; +} + +std::optional texture2d_atlas::allocate_region(const Eigen::Vector2i& size) { + if (!allocator_) { + return std::nullopt; + } + + // 使用分配器分配区域 + auto rect_opt = allocator_->allocate(size); + if (!rect_opt) { + return std::nullopt; // 分配失败 + } + + // 分配成功,创建区域信息 + int region_id = next_region_id_++; + atlas_region_t region(*rect_opt, calculate_uv_rect(*rect_opt), region_id); + + // 存储区域信息 + allocated_regions_[region.id] = region; + + return region; +} + +bool texture2d_atlas::free(const rect_t& rect) { + // 查找与此矩形对应的区域ID + for (const auto& [id, region] : allocated_regions_) { + if (region.rect == rect) { + return free_region(id); + } + } + return false; +} + +bool texture2d_atlas::free_region(int region_id) { + auto it = allocated_regions_.find(region_id); + if (it == allocated_regions_.end()) { + return false; // 区域不存在 + } + + // 使用分配器释放区域 + bool result = allocator_->free(it->second.rect); + if (result) { + // 从已分配列表中移除 + allocated_regions_.erase(it); + } + + return result; +} + +void texture2d_atlas::resize(const Eigen::Vector2i& new_size) { + texture_.resize(new_size); + if (allocator_) { + allocator_->resize(new_size); + } + // 调整大小会导致所有已分配区域失效 + allocated_regions_.clear(); +} + +void texture2d_atlas::clear() { + if (allocator_) { + allocator_->reset(); + } + allocated_regions_.clear(); +} + +void texture2d_atlas::update_region(const void* data, size_t data_size, const rect_t& rect) { + if (!data || data_size == 0) { + return; + } + + // 确保区域在纹理范围内 + if (rect.left() < 0 || rect.top() < 0 || + rect.right() > texture_.get_size().x() || + rect.bottom() > texture_.get_size().y()) { + return; + } + + // 更新纹理区域 + auto map = texture_.map(texture_map_type::write); + + // 使用texture_map_utils更新区域 + texture_map_utils::write_region(*map.get(), data, rect, texture_.get_bytes_per_pixel()); +} + +float texture2d_atlas::get_free_space_ratio() const { + if (!allocator_) { + return 0.0f; + } + + int total = allocator_->total_area(); + if (total == 0) { + return 0.0f; + } + + return 1.0f - static_cast(allocator_->allocated_area()) / total; +} + +std::vector texture2d_atlas::get_allocated_regions() const { + std::vector result; + result.reserve(allocated_regions_.size()); + + for (const auto& [id, region] : allocated_regions_) { + result.push_back(region); + } + + return result; +} + +rect_t texture2d_atlas::calculate_uv_rect(const rect_t& rect) const { + // 计算UV坐标 (归一化到0-1范围) + const Eigen::Vector2i texture_size = texture_.get_size(); + float left = static_cast(rect.left()) / texture_size.x(); + float top = static_cast(rect.top()) / texture_size.y(); + float width = static_cast(rect.width()) / texture_size.x(); + float height = static_cast(rect.height()) / texture_size.y(); + + return rect_t({left, top}, {width, height}); +} + +std::unique_ptr texture2d_atlas::create_allocator(allocation_strategy_t strategy, + const Eigen::Vector2i& size) { + switch (strategy) { + case allocation_strategy_t::bin_packing: + return std::make_unique(size); + + case allocation_strategy_t::grid: + // 默认使用16x16的网格 + return std::make_unique(size, Eigen::Vector2i(16, 16)); + + case allocation_strategy_t::skyline: + return std::make_unique(size); + + default: + throw std::invalid_argument("不支持的分配策略"); + } +} diff --git a/src/mirage_render/texture/atlas/texture2d_atlas.h b/src/mirage_render/texture/atlas/texture2d_atlas.h new file mode 100644 index 0000000..3778fba --- /dev/null +++ b/src/mirage_render/texture/atlas/texture2d_atlas.h @@ -0,0 +1,126 @@ +#pragma once +/** + * @file texture2d_atlas.h + * @brief 纹理图集类定义 + */ +#include "texture_atlas_types.h" +#include "texture/rw_texture2d.h" +#include +#include +#include +#include + +/** + * @class texture2d_atlas + * @brief 纹理图集类,管理单个大纹理上的多个子区域 + */ +class texture2d_atlas { +public: + //-------------- 构造和析构 -------------- + /** + * @brief 构造函数 + * @param size 图集纹理大小 + * @param pixel_format 纹理像素格式 + * @param strategy 分配策略 + */ + texture2d_atlas(const Eigen::Vector2i& size, sg_pixel_format pixel_format, + allocation_strategy_t strategy = allocation_strategy_t::bin_packing); + + /** + * @brief 析构函数 + */ + ~texture2d_atlas(); + + //-------------- 区域分配 -------------- + /** + * @brief 分配指定大小的纹理区域 + * @param size 要分配的区域大小 + * @return 分配的区域,失败返回std::nullopt + */ + std::optional> allocate(const Eigen::Vector2i& size); + + /** + * @brief 分配区域并返回完整区域信息 + * @param size 要分配的区域大小 + * @return 分配的区域信息,失败返回std::nullopt + */ + std::optional allocate_region(const Eigen::Vector2i& size); + + /** + * @brief 释放之前分配的纹理区域 + * @param rect 要释放的区域 + * @return 是否成功释放 + */ + bool free(const rect_t& rect); + + /** + * @brief 根据ID释放区域 + * @param region_id 要释放的区域ID + * @return 是否成功释放 + */ + bool free_region(int region_id); + + //-------------- 纹理访问 -------------- + /** + * @brief 获取底层纹理 + */ + const rw_texture2d& get_texture() const { return texture_; } + rw_texture2d& get_texture() { return texture_; } + + //-------------- 图集管理 -------------- + /** + * @brief 调整图集大小 + * @note 会导致所有已分配区域失效 + */ + void resize(const Eigen::Vector2i& new_size); + + /** + * @brief 清空所有分配 + */ + void clear(); + + /** + * @brief 更新指定区域的数据 + */ + void update_region(const void* data, size_t data_size, const rect_t& rect); + + //-------------- 状态查询 -------------- + /** + * @brief 获取图集大小 + */ + Eigen::Vector2i get_size() const { return texture_.get_size(); } + + /** + * @brief 获取剩余可用空间比例 + * @return 0.0-1.0之间的值 + */ + float get_free_space_ratio() const; + + /** + * @brief 获取所有已分配区域 + * @return 所有已分配区域的列表 + */ + std::vector get_allocated_regions() const; + + /** + * @brief 计算区域的UV坐标 + * @param rect 像素坐标中的矩形 + * @return UV坐标中的矩形 + */ + rect_t calculate_uv_rect(const rect_t& rect) const; + +private: + /** + * @brief 创建分配器 + * @param strategy 分配策略 + * @param size 区域大小 + * @return 创建的分配器指针 + */ + std::unique_ptr create_allocator(allocation_strategy_t strategy, + const Eigen::Vector2i& size); + + rw_texture2d texture_; ///< 底层纹理 + std::unique_ptr allocator_; ///< 区域分配器 + std::unordered_map allocated_regions_; ///< 存储已分配区域 + int next_region_id_ = 1; ///< 区域ID生成器 +}; diff --git a/src/mirage_render/texture/atlas/texture_atlas_types.h b/src/mirage_render/texture/atlas/texture_atlas_types.h new file mode 100644 index 0000000..1a3a321 --- /dev/null +++ b/src/mirage_render/texture/atlas/texture_atlas_types.h @@ -0,0 +1,37 @@ +#pragma once +/** + * @file texture_atlas_types.h + * @brief 纹理图集相关的基础类型定义 + */ +#include "geometry/rect.h" + +// 前向声明 +class atlas_allocator; + +/** + * @enum allocation_strategy_t + * @brief 纹理图集分配算法策略枚举 + */ +enum class allocation_strategy_t { + bin_packing, ///< 二叉树分割算法 + grid, ///< 网格分配算法 + skyline ///< 天际线算法 +}; + +/** + * @struct atlas_region_t + * @brief 表示纹理图集中的一个已分配区域 + */ +struct atlas_region_t { + rect_t rect; ///< 像素坐标中的矩形 + rect_t uv_rect; ///< UV坐标中的矩形 (0-1范围) + int id; ///< 区域唯一标识符 + + // 构造函数 + atlas_region_t() = default; + + atlas_region_t(const rect_t& r, const rect_t& uv, const int region_id) : + rect(r), + uv_rect(uv), + id(region_id) {} +}; diff --git a/src/mirage_render/texture/rw_texture2d.cpp b/src/mirage_render/texture/rw_texture2d.cpp new file mode 100644 index 0000000..3c73086 --- /dev/null +++ b/src/mirage_render/texture/rw_texture2d.cpp @@ -0,0 +1,143 @@ +#include "rw_texture2d.h" + +#include + +#include "image_accessor_factory.h" + +void rw_texture2d::create(const texture_data& in_data) { + sg_image_desc desc{}; + desc.type = SG_IMAGETYPE_2D; + desc.render_target = false; + desc.width = in_data.size.x(); + desc.height = in_data.size.y(); + desc.num_slices = 1; + desc.num_mipmaps = 1; + desc.usage = SG_USAGE_DYNAMIC; + desc.pixel_format = in_data.pixel_format; + desc.sample_count = 1; + desc.data = in_data.get_image_data(); + + create_from_desc(desc); +} + +void rw_texture2d::update(const sg_image_data& in_data) { + sg_update_image(get_image(), in_data); +} + +void rw_texture2d::sub_update(const sg_range& in_data, const rect_t& in_rect) { + // 验证输入参数 + if (!in_data.ptr || in_data.size == 0 || in_rect.width() <= 0 || in_rect.height() <= 0) { return; } + + // 获取像素格式信息 + const auto& pixel_info = sg_query_pixelformat(get_pixel_format()); + + // 计算源数据中一行的大小(字节数) + const int src_row_size = in_rect.width() * pixel_info.bytes_per_pixel; + + try { + // 映射纹理用于写入,现在返回的是unique_ptr + const auto mapped = map(texture_map_type::write_discard); + if (!mapped) { + return; // 映射失败 + } + + // 获取源数据的指针 + const auto src_ptr = static_cast(in_data.ptr); + + // 计算目标数据的起始位置 + std::byte* dst_ptr = mapped->data.data(); + + // 遍历矩形的每一行 + for (int y = 0; y < in_rect.height(); ++y) { + // 计算目标行在纹理数据中的位置 + std::byte* dst_row = dst_ptr + (in_rect.top() + y) * mapped->row_pitch + in_rect.left() * pixel_info. + bytes_per_pixel; + + // 计算源数据行的位置 + const std::byte* src_row = src_ptr + y * src_row_size; + + // 复制这一行的数据 + std::memcpy(dst_row, src_row, src_row_size); + } + } + catch (const std::exception& e) { + // 可以记录错误或处理异常 + std::cerr << "Error in sub_update: " << e.what() << std::endl; + } +} + +void rw_texture2d::resize(const Eigen::Vector2i& in_size) { + const auto& current_size = get_size(); + + // 如果大小相同,直接返回 + if (current_size == in_size) { return; } + + // 获取当前纹理的像素格式 + auto pixel_format = get_pixel_format(); + const auto& pixel_info = sg_query_pixelformat(pixel_format); + + // 保存当前纹理数据(如果有有效数据) + std::vector old_data; + + if (is_valid() && current_size.x() > 0 && current_size.y() > 0) { + try { + // 映射当前纹理进行读取 + auto mapped = map(texture_map_type::read); + if (mapped) { + // 计算需要复制的行数和每行的字节数 + int copy_rows = std::min(current_size.y(), in_size.y()); + int copy_bytes_per_row = std::min(current_size.x(), in_size.x()) * pixel_info.bytes_per_pixel; + + // 为旧数据分配空间 + old_data.resize(copy_rows * copy_bytes_per_row); + + // 复制需要保留的数据 + for (int y = 0; y < copy_rows; ++y) { + std::memcpy(old_data.data() + y * copy_bytes_per_row, + mapped->data.data() + y * mapped->row_pitch, + copy_bytes_per_row); + } + } + // mapped离开作用域会自动解除映射 + } + catch (const std::exception& e) { + // 读取失败,继续创建空纹理 + std::cerr << "Error reading texture data: " << e.what() << std::endl; + } + } + + // 创建新纹理 + sg_image_desc desc = get_image_desc(); + desc.width = in_size.x(); + desc.height = in_size.y(); + desc.pixel_format = pixel_format; + + // 清除旧的图像 + clear(); + + // 创建新的图像 + create_from_desc(desc); + + // 如果有旧数据,复制到新纹理 + if (!old_data.empty() && is_valid()) { + try { + // 映射新纹理进行写入 + if (const auto mapped = map(texture_map_type::write)) { + // 计算需要复制的行数和每行的字节数 + const auto copy_rows = std::min(current_size.y(), in_size.y()); + const auto copy_bytes_per_row = std::min(current_size.x(), in_size.x()) * pixel_info.bytes_per_pixel; + + // 复制数据到新纹理 + for (int y = 0; y < copy_rows; ++y) { + std::memcpy(mapped->data.data() + y * mapped->row_pitch, + old_data.data() + y * copy_bytes_per_row, + copy_bytes_per_row); + } + } + } + catch (const std::exception& e) { + // 写入失败 + std::cerr << "Error writing to new texture: " << e.what() << std::endl; + } + } +} diff --git a/src/mirage_render/texture/rw_texture2d.h b/src/mirage_render/texture/rw_texture2d.h new file mode 100644 index 0000000..4105a11 --- /dev/null +++ b/src/mirage_render/texture/rw_texture2d.h @@ -0,0 +1,21 @@ +#pragma once +#include "texture.h" + +struct rw_texture2d_data : texture_data { + void* data_ptr; + virtual sg_image_data get_image_data() const override { + const auto& pixel_info = sg_query_pixelformat(pixel_format); + sg_image_data data; + data.subimage[0][0].ptr = data_ptr; + data.subimage[0][0].size = size.x() * size.y() * pixel_info.bytes_per_pixel; + return data; + } +}; + +class rw_texture2d : public texture { +public: + virtual void create(const texture_data& in_data) override; + virtual void update(const sg_image_data& in_data) override; + virtual void sub_update(const sg_range& in_data, const rect_t& in_rect) override; + virtual void resize(const Eigen::Vector2i& in_size) override; +}; diff --git a/src/mirage_render/texture/texture.cpp b/src/mirage_render/texture/texture.cpp new file mode 100644 index 0000000..d85b949 --- /dev/null +++ b/src/mirage_render/texture/texture.cpp @@ -0,0 +1,28 @@ +#include "texture.h" + +void texture::create_from_desc(const sg_image_desc& in_desc) { + set_image(sg_make_image(in_desc)); +} + +void texture::clear() { + if (sg_isvalid()) { + if (const auto& image = get_image(); image.id != SG_INVALID_ID) + sg_destroy_image(image); + image_.id = SG_INVALID_ID; + } +} + +Eigen::Vector2i texture::get_size() const { + const auto& desc = get_image_desc(); + return Eigen::Vector2i(desc.width, desc.height); +} + +sg_pixel_format texture::get_pixel_format() const { + const auto& desc = get_image_desc(); + return desc.pixel_format; +} + +int32_t texture::get_bytes_per_pixel() const { + const auto& pixel_info = sg_query_pixelformat(get_pixel_format()); + return pixel_info.bytes_per_pixel; +} diff --git a/src/mirage_render/texture/texture.h b/src/mirage_render/texture/texture.h new file mode 100644 index 0000000..dbaa8cd --- /dev/null +++ b/src/mirage_render/texture/texture.h @@ -0,0 +1,41 @@ +#pragma once +#include "sokol_gfx.h" +#include "geometry/rect.h" +#include + +#include "texture_map_types.h" + +struct texture_data { + virtual ~texture_data() = default; + + Eigen::Vector2i size; + sg_pixel_format pixel_format; + virtual sg_image_data get_image_data() const = 0; +}; + +class texture { +public: + virtual ~texture() = default; + + void create_from_desc(const sg_image_desc& in_desc); + + virtual void create(const texture_data& in_data) = 0; + virtual void update(const sg_image_data& in_data) = 0; + virtual void sub_update(const sg_range& in_data, const rect_t& in_rect) = 0; + virtual void resize(const Eigen::Vector2i& in_size) = 0; + virtual std::unique_ptr map(texture_map_type in_type) const; + + void clear(); + + [[nodiscard]] bool is_valid() const { return image_.id != SG_INVALID_ID; } + [[nodiscard]] const auto& get_image() const { return image_; } + [[nodiscard]] auto get_image_desc() const { return sg_query_image_desc(image_); } + + [[nodiscard]] Eigen::Vector2i get_size() const; + [[nodiscard]] sg_pixel_format get_pixel_format() const; + [[nodiscard]] int32_t get_bytes_per_pixel() const; +protected: + void set_image(const sg_image& in_image) { image_ = in_image; } +private: + sg_image image_{}; +}; diff --git a/src/mirage_render/texture/texture2d.cpp b/src/mirage_render/texture/texture2d.cpp new file mode 100644 index 0000000..9e22b7e --- /dev/null +++ b/src/mirage_render/texture/texture2d.cpp @@ -0,0 +1,34 @@ +#include "texture2d.h" +#include +#include + +void texture2d::create(const texture_data& in_data) { + sg_image_desc desc{}; + desc.type = SG_IMAGETYPE_2D; + desc.render_target = false; + desc.width = in_data.size.x(); + desc.height = in_data.size.y(); + desc.num_slices = 1; + desc.num_mipmaps = 1; + desc.usage = SG_USAGE_IMMUTABLE; + desc.pixel_format = in_data.pixel_format; + desc.sample_count = 1; + desc.data = in_data.get_image_data(); + + create_from_desc(desc); +} + +void texture2d::update(const sg_image_data& in_data) { + auto desc = get_image_desc(); + desc.data = in_data; + clear(); + create_from_desc(desc); +} + +void texture2d::sub_update(const sg_range& in_data, const rect_t& in_rect) { + std::cerr << "texture2d can't sub_update!" << std::endl; +} + +void texture2d::resize(const Eigen::Vector2i& in_size) { + std::cerr << "texture2d can't resize!" << std::endl; +} diff --git a/src/mirage_render/texture/texture2d.h b/src/mirage_render/texture/texture2d.h new file mode 100644 index 0000000..75d14cd --- /dev/null +++ b/src/mirage_render/texture/texture2d.h @@ -0,0 +1,28 @@ +#pragma once + +#include "texture.h" + +struct texture2d_data : texture_data { + void* data_ptr; + virtual sg_image_data get_image_data() const override { + const auto& pixel_info = sg_query_pixelformat(pixel_format); + sg_image_data data; + data.subimage[0][0].ptr = data_ptr; + data.subimage[0][0].size = size.x() * size.y() * pixel_info.bytes_per_pixel; + return data; + } +}; + +/** + * @class texture2d + * @brief 渲染图像类 + * + * 用于静态图片的渲染,提供创建和清除操作。 + */ +class texture2d : public texture { +public: + virtual void create(const texture_data& in_data) override; + virtual void update(const sg_image_data& in_data) override; + virtual void sub_update(const sg_range& in_data, const rect_t& in_rect) override; + virtual void resize(const Eigen::Vector2i& in_size) override; +}; diff --git a/src/mirage_render/texture/texture_map_types.h b/src/mirage_render/texture/texture_map_types.h new file mode 100644 index 0000000..d8e447e --- /dev/null +++ b/src/mirage_render/texture/texture_map_types.h @@ -0,0 +1,23 @@ +#pragma once +#include + +struct texture_map { + ~texture_map() { + if (unmap) + unmap(); + } + + bool read_only; // 是否只读 + size_t row_pitch; // 行间距 + Eigen::Vector2i size; // 纹理大小 + std::span data; // 数据 + std::function unmap; // 取消映射函数,用于在销毁时调用 +}; + +enum class texture_map_type { + read, + write, + read_write, + write_discard, + write_no_overwrite +}; diff --git a/src/mirage_render/texture/texture_map_utils.h b/src/mirage_render/texture/texture_map_utils.h new file mode 100644 index 0000000..fa5187c --- /dev/null +++ b/src/mirage_render/texture/texture_map_utils.h @@ -0,0 +1,68 @@ +#pragma once +#include "texture/texture_map_types.h" +#include +#include + +#include "geometry/rect.h" + +class texture_map_utils { +public: + // 从映射纹理中提取区域数据 + static std::vector extract_region( + const texture_map& map, + const rect_t& region, + int bytes_per_pixel) + { + // 验证区域有效性 + if (region.left() < 0 || region.top() < 0 || + region.left() + region.width() > map.size.x() || + region.top() + region.height() > map.size.y() || + region.width() <= 0 || region.height() <= 0) { + return {}; // 返回空数据 + } + + // 分配结果缓冲区 + std::vector result(region.width() * region.height() * bytes_per_pixel); + + // 从映射内存中复制指定区域 + const auto dst = reinterpret_cast(result.data()); + const auto src = reinterpret_cast(map.data.data()); + + for (int y = 0; y < region.height(); ++y) { + const uint8_t* src_row = src + (region.top() + y) * map.row_pitch + region.left() * bytes_per_pixel; + uint8_t* dst_row = dst + y * region.width() * bytes_per_pixel; + std::memcpy(dst_row, src_row, region.width() * bytes_per_pixel); + } + + return result; + } + + // 将数据写入映射纹理的指定区域 + static bool write_region(const texture_map& map, const void* data, const rect_t& region, + const int bytes_per_pixel) { + // 检查只读标志 + if (map.read_only) { + return false; + } + + // 验证区域有效性 + if (region.left() < 0 || region.top() < 0 || + region.left() + region.width() > map.size.x() || + region.top() + region.height() > map.size.y() || + region.width() <= 0 || region.height() <= 0) { + return false; + } + + // 写入映射内存 + const auto src = static_cast(data); + const auto dst = reinterpret_cast(map.data.data()); + + for (int y = 0; y < region.height(); ++y) { + uint8_t* dst_row = dst + (region.top() + y) * map.row_pitch + region.left() * bytes_per_pixel; + const uint8_t* src_row = src + y * region.width() * bytes_per_pixel; + std::memcpy(dst_row, src_row, region.width() * bytes_per_pixel); + } + + return true; + } +}; diff --git a/src/mirage_render/texture/windows/texture_d3d11.cpp b/src/mirage_render/texture/windows/texture_d3d11.cpp new file mode 100644 index 0000000..0a21497 --- /dev/null +++ b/src/mirage_render/texture/windows/texture_d3d11.cpp @@ -0,0 +1,123 @@ +#include "texture/texture.h" +#include "sokol_gfx.h" +#include + +auto convert_map_type_to_d3d(texture_map_type in_type) { + switch (in_type) { + case texture_map_type::read: + return D3D11_MAP_READ; + case texture_map_type::write: + return D3D11_MAP_WRITE; + case texture_map_type::read_write: + return D3D11_MAP_READ_WRITE; + case texture_map_type::write_discard: + return D3D11_MAP_WRITE_DISCARD; + case texture_map_type::write_no_overwrite: + return D3D11_MAP_WRITE_NO_OVERWRITE; + default: + return D3D11_MAP_WRITE_DISCARD; + } +} + +// 验证映射类型与纹理描述是否兼容 +void validate_map_type(texture_map_type in_type, const D3D11_TEXTURE2D_DESC& desc) { + D3D11_MAP d3d_map_type = convert_map_type_to_d3d(in_type); + + // 检查读访问权限 + if ((d3d_map_type == D3D11_MAP_READ || d3d_map_type == D3D11_MAP_READ_WRITE) && !(desc.CPUAccessFlags & D3D11_CPU_ACCESS_READ)) { + // 纹理不支持 CPU 读取访问 + throw std::runtime_error("Texture does not support CPU read access"); + } + + // 检查写访问权限 + if ((d3d_map_type != D3D11_MAP_READ) && !(desc.CPUAccessFlags & D3D11_CPU_ACCESS_WRITE)) { + // 纹理不支持 CPU 写入访问 + throw std::runtime_error("Texture does not support CPU write access"); + } + + // 特殊检查:WRITE_DISCARD 仅适用于 DYNAMIC 用途的纹理 + if (d3d_map_type == D3D11_MAP_WRITE_DISCARD && desc.Usage != D3D11_USAGE_DYNAMIC) { + // WRITE_DISCARD 映射需要动态纹理 + throw std::runtime_error("WRITE_DISCARD mapping requires a dynamic texture"); + } +} + +// 处理 D3D11 错误代码 +void handle_d3d_error(HRESULT hr, const char* operation) { + if (FAILED(hr)) { + std::string error_msg = std::string(operation) + ": "; + switch (hr) { + case E_INVALIDARG: + // 无效参数 + error_msg += "Invalid arguments"; + break; + case E_OUTOFMEMORY: + // 内存不足 + error_msg += "Out of memory"; + break; + case DXGI_ERROR_DEVICE_REMOVED: + // 设备移除 + error_msg += "Device removed"; + break; + case DXGI_ERROR_DEVICE_RESET: + // 设备重置 + error_msg += "Device reset"; + break; + default: + // 未知错误 + error_msg += "Unknown error (0x" + std::to_string(hr) + ")"; + break; + } + throw std::runtime_error(error_msg); + } +} + +std::unique_ptr texture::map(texture_map_type in_type) const { + if (!is_valid()) { + // 无法映射无效纹理 + throw std::runtime_error("Cannot map invalid texture"); + } + + // 获取 D3D11 资源 + const auto& image_info = sg_d3d11_query_image_info(get_image()); + const auto& context = (ID3D11DeviceContext*) sg_d3d11_device_context(); + const auto& tex2d = (ID3D11Texture2D*) image_info.tex2d; + + if (!tex2d || !context) { + // 无法获取 D3D11 纹理或上下文 + throw std::runtime_error("Failed to get D3D11 texture or context"); + } + + // 获取纹理描述 + D3D11_TEXTURE2D_DESC desc; + tex2d->GetDesc(&desc); + + // 验证映射类型 + validate_map_type(in_type, desc); + + // 尝试映射纹理 + D3D11_MAPPED_SUBRESOURCE mapped; + const auto& d3d_map_type = convert_map_type_to_d3d(in_type); + auto hr = context->Map(tex2d, 0, d3d_map_type, 0, &mapped); + + // 处理映射错误 + handle_d3d_error(hr, "Failed to map texture"); + + // 使用智能指针确保纹理始终被正确解除映射 + std::shared_ptr shared_tex(tex2d, [](ID3D11Texture2D* ptr) {}); // 不增加引用计数 + std::shared_ptr shared_ctx(context, [](ID3D11DeviceContext* ptr) {}); // 不增加引用计数 + + // 设置映射结果 + auto map = std::make_unique(); + map->size = { desc.Width, desc.Height }; + map->data = { static_cast(mapped.pData), mapped.RowPitch * desc.Height }; + map->unmap = [shared_tex, shared_ctx] { + if (shared_tex && shared_ctx) { + shared_ctx->Unmap(shared_tex.get(), 0); + } + }; + map->row_pitch = mapped.RowPitch; + map->read_only = in_type == texture_map_type::read; + + return map; +} diff --git a/src/mirage_widget/widget/leaf_widget/mimage.cpp b/src/mirage_widget/widget/leaf_widget/mimage.cpp index 3716b27..69a4bb2 100644 --- a/src/mirage_widget/widget/leaf_widget/mimage.cpp +++ b/src/mirage_widget/widget/leaf_widget/mimage.cpp @@ -1,6 +1,6 @@ #include "mimage.h" -#include "render/texture2d.h" +#include "texture/texture.h" void mimage::on_paint(mirage_paint_context& in_context) { if (!image_) { @@ -17,7 +17,7 @@ void mimage::on_paint(mirage_paint_context& in_context) { ); } -void mimage::set_image(const std::shared_ptr& in_image) { +void mimage::set_image(const std::shared_ptr& in_image) { image_ = in_image; } diff --git a/src/mirage_widget/widget/leaf_widget/mimage.h b/src/mirage_widget/widget/leaf_widget/mimage.h index 496bd02..135171f 100644 --- a/src/mirage_widget/widget/leaf_widget/mimage.h +++ b/src/mirage_widget/widget/leaf_widget/mimage.h @@ -2,7 +2,7 @@ #include "render/texture_sampler.h" #include "widget/mleaf_widget.h" -class texture2d; +class texture; class mimage : public mleaf_widget { public: @@ -10,7 +10,7 @@ public: Eigen::Vector2f compute_desired_size(float in_layout_scale_multiplier) const override; const auto& get_image() const { return image_; } - void set_image(const std::shared_ptr& in_image); + void set_image(const std::shared_ptr& in_image); const auto& get_color() const { return color_; } void set_color(const linear_color& in_color) { color_ = in_color; } @@ -18,7 +18,7 @@ public: void set_sampler(const std::shared_ptr& in_sampler) { sampler_ = in_sampler; } void set_sampler(sampler_type in_type); private: - std::shared_ptr image_; + std::shared_ptr image_; std::shared_ptr sampler_ = texture_sampler_builder::get_sampler(sampler_type::default_); linear_color color_ = { 1, 1, 1, 1 }; };