semi opaque

This commit is contained in:
Jorijn van der Graaf 2025-11-27 00:08:10 +01:00
commit 0329616148
6 changed files with 80 additions and 91 deletions

View file

@ -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;
}
}
}

View file

@ -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);
}
}

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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<Pixel_BU8_GU8_RU8_AU8> 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<Pixel_BU8_GU8_RU8_AU8> 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;

View file

@ -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<double>(
// std::numeric_limits<std::uint_fast32_t>::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<std::uint_fast32_t>(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<double>(
// std::numeric_limits<std::uint_fast32_t>::max()
// );
// double t = static_cast<double>(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<double>(
// std::numeric_limits<std::uint_fast32_t>::max()
// );
// // scale mapped ∈ [0, MAX] to pixel ∈ [0, size]
// double t = static_cast<double>(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<std::int_fast32_t>(t * static_cast<double>(size));
// }
}