diff --git a/implementations/Crafter.Graphics-ImageElement.cpp b/implementations/Crafter.Graphics-ImageElement.cpp index 32c5cf1..21c0a32 100644 --- a/implementations/Crafter.Graphics-ImageElement.cpp +++ b/implementations/Crafter.Graphics-ImageElement.cpp @@ -34,7 +34,7 @@ ImageElement::ImageElement() : RenderingElementScaling() { } -ImageElement::ImageElement(const std::string_view imagePath, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : RenderingElementScaling(false, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { +ImageElement::ImageElement(const std::string_view imagePath, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : RenderingElementScaling(OpaqueType::FullyOpaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { RenderImage(imagePath); } @@ -42,11 +42,12 @@ void ImageElement::RenderImage(const std::string_view path) { std::filesystem::path abs = std::filesystem::absolute(path); int xSize; int ySize; - int channels; - unsigned char* bgData = stbi_load(abs.string().c_str(), &xSize, &ySize, &channels, 4); + unsigned char* bgData = stbi_load(abs.string().c_str(), &xSize, &ySize, nullptr, 4); ResizeBuffer(xSize, ySize); + opaque = OpaqueType::FullyOpaque; + for(std::uint_fast32_t x = 0; x < xSize; x++) { for(std::uint_fast32_t y = 0; y < ySize; y++) { std::uint_fast32_t idx = (x*ySize+y)*4; @@ -57,7 +58,16 @@ void ImageElement::RenderImage(const std::string_view path) { } } - if(channels != 4) { - opaque = true; - } + for(std::uint_fast32_t i = 0; i < xSize*ySize; i++) { + if(buffer[i].a != 255) { + opaque = OpaqueType::SemiOpaque; + for(std::uint_fast32_t i2 = 0; i2 < xSize*ySize; i2++) { + if(buffer[i2].a != 0 && buffer[i2].a != 255) { + opaque = OpaqueType::Transparent; + return; + } + } + return; + } + } } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-RenderingElement.cpp b/implementations/Crafter.Graphics-RenderingElement.cpp index 4dcf6e5..4e70351 100644 --- a/implementations/Crafter.Graphics-RenderingElement.cpp +++ b/implementations/Crafter.Graphics-RenderingElement.cpp @@ -27,32 +27,32 @@ import std; using namespace Crafter; -RenderingElement::RenderingElement(bool opaque) : Transform(), opaque(opaque) { +RenderingElement::RenderingElement(OpaqueType opaque) : Transform(), opaque(opaque) { } -RenderingElement::RenderingElement(bool opaque, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : Transform(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling), opaque(opaque) { +RenderingElement::RenderingElement(OpaqueType opaque, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : Transform(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling), opaque(opaque) { } -RenderingElementPreScaled::RenderingElementPreScaled(bool opaque) : RenderingElement(opaque) { +RenderingElementPreScaled::RenderingElementPreScaled(OpaqueType opaque) : RenderingElement(opaque) { } -RenderingElementPreScaled::RenderingElementPreScaled(bool opaque, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { +RenderingElementPreScaled::RenderingElementPreScaled(OpaqueType opaque, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { } -RenderingElementScaling::RenderingElementScaling(bool opaque) : RenderingElement(opaque) { +RenderingElementScaling::RenderingElementScaling(OpaqueType opaque) : RenderingElement(opaque) { } -RenderingElementScaling::RenderingElementScaling(bool opaque, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { +RenderingElementScaling::RenderingElementScaling(OpaqueType opaque, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { } -RenderingElementScaling::RenderingElementScaling(bool opaque, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : bufferWidth(bufferWidth), bufferHeight(bufferHeight), buffer(bufferWidth*bufferHeight), RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { +RenderingElementScaling::RenderingElementScaling(OpaqueType opaque, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : bufferWidth(bufferWidth), bufferHeight(bufferHeight), buffer(bufferWidth*bufferHeight), RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { } @@ -157,4 +157,4 @@ void RenderingElementScaling::ResizeBuffer(std::uint_fast32_t width, std::uint_f this->bufferWidth = width; this->bufferHeight = height; buffer.resize(width * height); -} +} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-TextElement.cpp b/implementations/Crafter.Graphics-TextElement.cpp index f6b193c..49376b7 100644 --- a/implementations/Crafter.Graphics-TextElement.cpp +++ b/implementations/Crafter.Graphics-TextElement.cpp @@ -30,7 +30,7 @@ import std; using namespace Crafter; -TextElement::TextElement(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : RenderingElementPreScaled(false, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { +TextElement::TextElement(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling) : RenderingElementPreScaled(OpaqueType::Transparent, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) { } @@ -249,7 +249,7 @@ void TextElement::RenderText(Window& window, const std::string_view text, float } else if (currentLine.empty() || (currentLine.length() + currentWord.length() + 1) * (int)(scale * 1.5f) <= maxLineWidth) { // Add word to current line if (!currentLine.empty()) { - currentLine = currentLine.substr(0) + std::string(" ") + currentWord; + currentLine = currentLine + std::string(" ") + currentWord; } else { currentLine = currentWord; } diff --git a/implementations/Crafter.Graphics-Window_wayland.cpp b/implementations/Crafter.Graphics-Window_wayland.cpp index 4c8a533..88e59fd 100644 --- a/implementations/Crafter.Graphics-Window_wayland.cpp +++ b/implementations/Crafter.Graphics-Window_wayland.cpp @@ -139,6 +139,7 @@ inline void blend_pixel_optimized(Pixel_BU8_GU8_RU8_AU8& dst, const Pixel_BU8_GU if(src.a == 0) { return; } + float srcA = src.a / 255.0f; float dstA = dst.a / 255.0f; @@ -173,26 +174,49 @@ void WindowWayland::RenderElement(Transform* transform) { const Pixel_BU8_GU8_RU8_AU8* src_buffer = element->bufferScaled.data(); std::int_fast32_t src_width = element->scaled.width; std::int_fast32_t src_height = element->scaled.height; - - if (element->opaque) { - for (std::int_fast32_t y = dirty.top; y < dirty.bottom; y++) { - std::int_fast32_t src_y = y - element->scaled.y; - - for (std::int_fast32_t x = dirty.left; x < dirty.right; x++) { - std::int_fast32_t src_x = x - element->scaled.x; + + switch (element->opaque) { + case OpaqueType::FullyOpaque: + // For fully opaque, just copy pixels directly + for (std::int_fast32_t y = dirty.top; y < dirty.bottom; y++) { + std::int_fast32_t src_y = y - element->scaled.y; - framebuffer[y * width + x] = src_buffer[src_y * src_width + src_x]; + for (std::int_fast32_t x = dirty.left; x < dirty.right; x++) { + std::int_fast32_t src_x = x - element->scaled.x; + + framebuffer[y * width + x] = src_buffer[src_y * src_width + src_x]; + } } - } - } else { - for (std::int_fast32_t y = dirty.top; y < dirty.bottom; y++) { - std::int_fast32_t src_y = y - element->scaled.y; + break; - for (std::int_fast32_t x = dirty.left; x < dirty.right; x++) { - std::int_fast32_t src_x = x - element->scaled.x; - blend_pixel_optimized(framebuffer[y * width + x], src_buffer[src_y * src_width + src_x]); + case OpaqueType::SemiOpaque: + // For semi-opaque, we can avoid blending when alpha is 0 or 255 + for (std::int_fast32_t y = dirty.top; y < dirty.bottom; y++) { + std::int_fast32_t src_y = y - element->scaled.y; + + for (std::int_fast32_t x = dirty.left; x < dirty.right; x++) { + std::int_fast32_t src_x = x - element->scaled.x; + Pixel_BU8_GU8_RU8_AU8 src_pixel = src_buffer[src_y * src_width + src_x]; + + if (src_pixel.a == 0) { + continue; + } + framebuffer[y * width + x] = src_pixel; + } } - } + break; + + case OpaqueType::Transparent: + // For transparent, always perform blending + for (std::int_fast32_t y = dirty.top; y < dirty.bottom; y++) { + std::int_fast32_t src_y = y - element->scaled.y; + + for (std::int_fast32_t x = dirty.left; x < dirty.right; x++) { + std::int_fast32_t src_x = x - element->scaled.x; + blend_pixel_optimized(framebuffer[y * width + x], src_buffer[src_y * src_width + src_x]); + } + } + break; } } diff --git a/interfaces/Crafter.Graphics-RenderingElement.cppm b/interfaces/Crafter.Graphics-RenderingElement.cppm index 37f163f..fb8f11a 100644 --- a/interfaces/Crafter.Graphics-RenderingElement.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement.cppm @@ -23,22 +23,28 @@ import :Transform; import :Types; export namespace Crafter { + enum class OpaqueType { + FullyOpaque, // All pixels have A of 255 + SemiOpaque, // All pixels have A of 0 or 255 (no blending needed) + Transparent // Color blending is used + }; + class Window; class RenderingElement : public Transform { public: std::vector bufferScaled; - bool opaque = false; + OpaqueType opaque = OpaqueType::Transparent; - RenderingElement(bool opague = false); - RenderingElement(bool opague, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling); + RenderingElement(OpaqueType opaque = OpaqueType::Transparent); + RenderingElement(OpaqueType opaque, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling); RenderingElement(RenderingElement&) = delete; RenderingElement& operator=(RenderingElement&) = delete; }; class RenderingElementPreScaled : public RenderingElement { public: - RenderingElementPreScaled(bool opague = false); - RenderingElementPreScaled(bool opague, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling); + RenderingElementPreScaled(OpaqueType opaque = OpaqueType::Transparent); + RenderingElementPreScaled(OpaqueType opaque, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling); RenderingElementPreScaled(RenderingElementPreScaled&) = delete; RenderingElementPreScaled& operator=(RenderingElementPreScaled&) = delete; @@ -52,11 +58,10 @@ export namespace Crafter { std::vector buffer; std::uint_fast32_t bufferWidth; std::uint_fast32_t bufferHeight; - bool opaque = false; - RenderingElementScaling(bool opague = false); - RenderingElementScaling(bool opague, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling); - RenderingElementScaling(bool opague, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight, std::int_fast32_t anchorX = FractionalToMapped(0.5), std::int_fast32_t anchorY = FractionalToMapped(0.5), std::uint_fast32_t relativeWidth = FractionalToMapped(1), std::uint_fast32_t relativeHeight = FractionalToMapped(1), std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); + RenderingElementScaling(OpaqueType opaque = OpaqueType::Transparent); + RenderingElementScaling(OpaqueType opaque, std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z, bool ignoreScaling); + RenderingElementScaling(OpaqueType opaque, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight, std::int_fast32_t anchorX = FractionalToMapped(0.5), std::int_fast32_t anchorY = FractionalToMapped(0.5), std::uint_fast32_t relativeWidth = FractionalToMapped(1), std::uint_fast32_t relativeHeight = FractionalToMapped(1), std::int_fast32_t anchorOffsetX = FractionalToMapped(0.5), std::int_fast32_t anchorOffsetY = FractionalToMapped(0.5), std::int_fast32_t z = 0, bool ignoreScaling = false); RenderingElementScaling(RenderingElementScaling&) = delete; RenderingElementScaling& operator=(RenderingElementScaling&) = delete; diff --git a/interfaces/Crafter.Graphics-Types.cppm b/interfaces/Crafter.Graphics-Types.cppm index 8816309..a1a3aea 100644 --- a/interfaces/Crafter.Graphics-Types.cppm +++ b/interfaces/Crafter.Graphics-Types.cppm @@ -150,54 +150,4 @@ namespace Crafter { export constexpr std::int_fast32_t PixelToMappedBoundless(std::int_fast32_t pixel, std::int_fast32_t width) { return pixel * (SCALEBOUNDLESS / width); } - - // export constexpr double bound = 10; - - // export constexpr std::uint_fast32_t FractionalToMapped(double fractional) { - // constexpr double min_x = -bound; - // constexpr double max_x = bound + 1.0; - // constexpr double range_x = max_x - min_x; - - // constexpr double MAXD = static_cast( - // std::numeric_limits::max() - // ); - - // // Normalize to [0,1] - // double t = (fractional - min_x) / range_x; - - // // Clamp (important for constexpr safety and edge behavior) - // if (t < 0.0) t = 0.0; - // if (t > 1.0) t = 1.0; - - // return static_cast(t * MAXD); - // } - - // export constexpr double MappedToFractional(std::uint_fast32_t mapped) { - // constexpr double min_x = -bound; - // constexpr double max_x = bound + 1.0; - // constexpr double range_x = max_x - min_x; - - // constexpr double MAXD = static_cast( - // std::numeric_limits::max() - // ); - - // double t = static_cast(mapped) / MAXD; - - // return min_x + t * range_x; - // } - - // export constexpr std::int_fast32_t MappedToPixel(std::uint_fast32_t mapped, std::uint_fast32_t size) { - // constexpr double MAXD = static_cast( - // std::numeric_limits::max() - // ); - - // // scale mapped ∈ [0, MAX] to pixel ∈ [0, size] - // double t = static_cast(mapped) / MAXD; - - // // Clamp (shouldn't be necessary, but protects constexpr eval) - // if (t < 0.0) t = 0.0; - // if (t > 1.0) t = 1.0; - - // return static_cast(t * static_cast(size)); - // } }