/* Crafter®.Asset Copyright (C) 2026 Catcrafts® catcrafts.net This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3.0 as published by the Free Software Foundation; This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; #define STB_IMAGE_IMPLEMENTATION #include "../lib/stb_image.h" export module Crafter.Asset:Texture; import std; import Crafter.Math; namespace fs = std::filesystem; export namespace Crafter { enum class OpaqueType : std::uint8_t { FullyOpaque, // All pixels have A of 255 SemiOpaque, // All pixels have A of 0 or 255 (no blending needed) Transparent // Color blending is used }; struct TextureAssetInfo { std::uint16_t sizeX; std::uint16_t sizeY; OpaqueType opaque; }; template struct TextureAsset { std::uint16_t sizeX; std::uint16_t sizeY; OpaqueType opaque; std::vector pixels; void Save(fs::path path) { std::ofstream file(path, std::ios::binary); file.write(reinterpret_cast(&sizeX), sizeof(sizeX)); file.write(reinterpret_cast(&sizeY), sizeof(sizeY)); file.write(reinterpret_cast(&opaque), sizeof(opaque)); file.write(reinterpret_cast(pixels.data()), pixels.size() * sizeof(T)); } static TextureAsset Load(fs::path path) { TextureAsset tex; std::ifstream file(path, std::ios::binary); file.read(reinterpret_cast(&tex.sizeX), sizeof(tex.sizeX)); file.read(reinterpret_cast(&tex.sizeY), sizeof(tex.sizeY)); file.read(reinterpret_cast(&tex.opaque), sizeof(tex.opaque)); tex.pixels.resize(tex.sizeX * tex.sizeY); file.read(reinterpret_cast(tex.pixels.data()), tex.sizeX * tex.sizeY * sizeof(T)); return tex; } static TextureAssetInfo LoadInfo(fs::path path) { TextureAssetInfo info; std::ifstream file(path, std::ios::binary); file.read(reinterpret_cast(&info.sizeX), sizeof(info.sizeX)); file.read(reinterpret_cast(&info.sizeY), sizeof(info.sizeY)); file.read(reinterpret_cast(&info.opaque), sizeof(info.opaque)); return info; } static void Load(fs::path path, T* pixels, std::uint16_t sizeX, std::uint16_t sizeY) { std::ifstream file(path, std::ios::binary); file.seekg(sizeof(std::uint16_t) + sizeof(std::uint16_t) + sizeof(std::uint8_t), std::ios::cur); file.read(reinterpret_cast(pixels), sizeX * sizeY * sizeof(T)); } template static TextureAsset LoadPNG(fs::path path) { TextureAsset tex; std::filesystem::path abs = std::filesystem::absolute(path); int sizeX; int sizeY; unsigned char* data = stbi_load(abs.string().c_str(), &sizeX, &sizeY, nullptr, 4); tex.sizeX = sizeX; tex.sizeY = sizeY; tex.pixels.resize(tex.sizeX*tex.sizeY); tex.opaque = OpaqueType::FullyOpaque; if constexpr(std::same_as || std::same_as || std::same_as) { for(std::uint32_t i = 0; i < sizeX*sizeY; i++) { tex.pixels[i].r = TT(data[i*4])/255; tex.pixels[i].g = TT(data[i*4+1])/255; tex.pixels[i].b = TT(data[i*4+2])/255; tex.pixels[i].a = TT(data[i*4+3])/255; } for(std::uint32_t i = 0; i < tex.sizeX* tex.sizeY; i++) { if(tex.pixels[i].a != 1) { tex.opaque = OpaqueType::SemiOpaque; for(std::uint32_t i2 = i; i2 < tex.sizeX* tex.sizeY; i2++) { if(tex.pixels[i2].a != 0 && tex.pixels[i2].a != 1) { tex.opaque = OpaqueType::Transparent; stbi_image_free(data); return tex; } } stbi_image_free(data); return tex; } } } else { std::memcpy(tex.pixels.data(), data, tex.sizeX * tex.sizeY * 4); for(std::uint32_t i = 0; i < tex.sizeX* tex.sizeY; i++) { if(tex.pixels[i].a != 255) { tex.opaque = OpaqueType::SemiOpaque; for(std::uint32_t i2 = i; i2 < tex.sizeX* tex.sizeY; i2++) { if(tex.pixels[i2].a != 0 && tex.pixels[i2].a != 255) { tex.opaque = OpaqueType::Transparent; stbi_image_free(data); return tex; } } stbi_image_free(data); return tex; } } } stbi_image_free(data); return tex; } }; }