#include "fiko/ui/simge.hpp" #include #include #include #include #include #include "fiko/core/temel.hpp" #include "fiko/platform/gorsel.hpp" #include "fiko/render/vk_kontrol.hpp" #include "fiko/render/vulkan_baglam.hpp" #include "imgui_impl_vulkan.h" #include "vk_mem_alloc.h" #pragma warning(push) #pragma warning(disable : 4244 4456 4457) // vendored nanosvg uyarıları #define NANOSVG_IMPLEMENTATION #include "nanosvg.h" #define NANOSVGRAST_IMPLEMENTATION #include "nanosvgrast.h" #pragma warning(pop) namespace fiko::ui { void simgeCiz(ImDrawList* dl, const Simge& s, ImVec2 merkez, float boyut, ImU32 renk) { if (s.doku) { const float y = boyut * 0.5f; dl->AddImage(s.doku, {merkez.x - y, merkez.y - y}, {merkez.x + y, merkez.y + y}, {0, 0}, {1, 1}, renk); return; } if (!s.glif) return; ImFont* font = ImGui::GetFont(); const ImVec2 mb = font->CalcTextSizeA(boyut, FLT_MAX, 0.0f, s.glif); dl->AddText(font, boyut, {merkez.x - mb.x * 0.5f, merkez.y - mb.y * 0.5f}, renk, s.glif); } namespace kitaplik { namespace { struct DokuKayit { VkImage gorsel = VK_NULL_HANDLE; VmaAllocation tahsis = nullptr; VkImageView gorunum = VK_NULL_HANDLE; VkDescriptorSet tanim = VK_NULL_HANDLE; }; VulkanBaglam* g_vb = nullptr; VkSampler g_ornekleyici = VK_NULL_HANDLE; std::unordered_map g_dokular; void kayitSil(DokuKayit& k) { if (k.tanim) ImGui_ImplVulkan_RemoveTexture(k.tanim); if (k.gorunum) vkDestroyImageView(g_vb->aygit(), k.gorunum, nullptr); if (k.gorsel) vmaDestroyImage(g_vb->ayirici(), k.gorsel, k.tahsis); k = {}; } // RGBA8 pikselleri GPU dokusuna yükler ve ImGui descriptor'ı döner. ImTextureID rgbaYukle(const std::string& ad, const unsigned char* rgba, int g, int y) { if (!g_vb || g <= 0 || y <= 0) return 0; auto eski = g_dokular.find(ad); if (eski != g_dokular.end()) { g_vb->bekle(); kayitSil(eski->second); g_dokular.erase(eski); } DokuKayit k; VkImageCreateInfo gb{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO}; gb.imageType = VK_IMAGE_TYPE_2D; gb.format = VK_FORMAT_R8G8B8A8_UNORM; gb.extent = {(u32)g, (u32)y, 1}; gb.mipLevels = 1; gb.arrayLayers = 1; gb.samples = VK_SAMPLE_COUNT_1_BIT; gb.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; VmaAllocationCreateInfo gt{}; gt.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE; VK_KONTROL(vmaCreateImage(g_vb->ayirici(), &gb, >, &k.gorsel, &k.tahsis, nullptr)); // Staging tampon → kopya → shader-read geçişi const u64 bayt = (u64)g * (u64)y * 4; VkBufferCreateInfo tb{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; tb.size = bayt; tb.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; VmaAllocationCreateInfo tt{}; tt.usage = VMA_MEMORY_USAGE_AUTO; tt.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT | VMA_ALLOCATION_CREATE_MAPPED_BIT; VkBuffer kaynak; VmaAllocation kaynakT; VmaAllocationInfo bilgi; VK_KONTROL(vmaCreateBuffer(g_vb->ayirici(), &tb, &tt, &kaynak, &kaynakT, &bilgi)); std::memcpy(bilgi.pMappedData, rgba, bayt); g_vb->anindaGonder([&](VkCommandBuffer kb) { VkImageMemoryBarrier b{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; b.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; b.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; b.srcQueueFamilyIndex = b.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; b.image = k.gorsel; b.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; b.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; vkCmdPipelineBarrier(kb, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b); VkBufferImageCopy kopya{}; kopya.imageSubresource = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 0, 1}; kopya.imageExtent = {(u32)g, (u32)y, 1}; vkCmdCopyBufferToImage(kb, kaynak, k.gorsel, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &kopya); b.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; b.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; b.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; b.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; vkCmdPipelineBarrier(kb, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, nullptr, 0, nullptr, 1, &b); }); vmaDestroyBuffer(g_vb->ayirici(), kaynak, kaynakT); VkImageViewCreateInfo gv{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; gv.image = k.gorsel; gv.viewType = VK_IMAGE_VIEW_TYPE_2D; gv.format = VK_FORMAT_R8G8B8A8_UNORM; gv.subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1}; VK_KONTROL(vkCreateImageView(g_vb->aygit(), &gv, nullptr, &k.gorunum)); k.tanim = ImGui_ImplVulkan_AddTexture(g_ornekleyici, k.gorunum, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); const ImTextureID kimlik = (ImTextureID)k.tanim; g_dokular.emplace(ad, k); return kimlik; } // Raster dosya decode (PNG/JPG/BMP/GIF/TIFF) → RGBA8. Kodek merkezi // platform::gorselOku'dadir (WIC) — ikinci bir decoder tutulmaz. bool wicOku(const std::string& yol, std::vector& rgba, int& g, int& y) { platform::Gorsel gr = platform::gorselOku(yol); if (!gr.gecerli()) return false; g = gr.genislik; y = gr.yukseklik; rgba = std::move(gr.rgba); return true; } // nanosvg ile parse edilmiş görüntüyü hedef boyuta rasterize eder. ImTextureID svgRasterYukle(const std::string& ad, NSVGimage* resim, int pikselBoyut) { if (!resim) return 0; if (resim->width <= 0 || resim->height <= 0) { nsvgDelete(resim); return 0; } const int hedef = pikselBoyut > 0 ? pikselBoyut : 64; const float olcek = (float)hedef / (resim->width > resim->height ? resim->width : resim->height); const int g = (int)(resim->width * olcek + 0.5f); const int y = (int)(resim->height * olcek + 0.5f); std::vector rgba((size_t)g * y * 4); NSVGrasterizer* r = nsvgCreateRasterizer(); nsvgRasterize(r, resim, 0, 0, olcek, rgba.data(), g, y, g * 4); nsvgDeleteRasterizer(r); nsvgDelete(resim); return rgbaYukle(ad, rgba.data(), g, y); } bool svgMi(const std::string& yol) { const size_t n = yol.size(); return n > 4 && (yol.compare(n - 4, 4, ".svg") == 0 || yol.compare(n - 4, 4, ".SVG") == 0); } } // namespace void kur(VulkanBaglam& vb) { g_vb = &vb; VkSamplerCreateInfo sb{VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO}; sb.magFilter = VK_FILTER_LINEAR; sb.minFilter = VK_FILTER_LINEAR; sb.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sb.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sb.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; sb.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; VK_KONTROL(vkCreateSampler(vb.aygit(), &sb, nullptr, &g_ornekleyici)); } void kapat() { if (!g_vb) return; for (auto& [ad, k] : g_dokular) kayitSil(k); g_dokular.clear(); vkDestroySampler(g_vb->aygit(), g_ornekleyici, nullptr); g_ornekleyici = VK_NULL_HANDLE; g_vb = nullptr; } ImTextureID yukle(const std::string& ad, const std::string& dosyaYolu, int pikselBoyut) { if (!g_vb) return 0; if (svgMi(dosyaYolu)) return svgRasterYukle(ad, nsvgParseFromFile(dosyaYolu.c_str(), "px", 96.0f), pikselBoyut); std::vector rgba; int g = 0, y = 0; if (!wicOku(dosyaYolu, rgba, g, y)) return 0; return rgbaYukle(ad, rgba.data(), g, y); } ImTextureID yukleSvgMetin(const std::string& ad, const std::string& svg, int pikselBoyut) { if (!g_vb) return 0; std::string kopya = svg; // nsvgParse girdiyi değiştirir return svgRasterYukle(ad, nsvgParse(kopya.data(), "px", 96.0f), pikselBoyut); } ImTextureID yukleRgba(const std::string& ad, const unsigned char* rgba, int g, int y) { return g_vb ? rgbaYukle(ad, rgba, g, y) : 0; } ImTextureID al(const std::string& ad) { auto it = g_dokular.find(ad); return it == g_dokular.end() ? 0 : (ImTextureID)it->second.tanim; } Simge simge(const std::string& ad) { return Simge::dokulu(al(ad)); } } // namespace kitaplik } // namespace fiko::ui