rendering element rewrite

This commit is contained in:
Jorijn van der Graaf 2025-12-28 00:05:03 +01:00
commit 3d40256bde
22 changed files with 799 additions and 795 deletions

View file

@ -18,7 +18,7 @@ int main() {
FractionalToMapped(1), // relativeHeight
FractionalToMapped(0), // anchorOffsetX
FractionalToMapped(0), // anchorOffsetY
0, // z
0 // z
);
for (int i = 0; i < 6; i++) {
@ -26,13 +26,13 @@ int main() {
OpaqueType::FullyOpaque,
1,
1,
FractionalToMapped(0.5), // anchorX
FractionalToMapped(0.5), // anchorY
FractionalToMapped(0), // anchorX
FractionalToMapped(0), // anchorY
FractionalToMapped(1.0), // relativeSizeX (will be overridden by grid)
FractionalToMapped(1.0), // relativeSizeY (will be overridden by grid)
FractionalToMapped(0.0), // anchorOffsetX
FractionalToMapped(0.0), // anchorOffsetY
0, // z
0 // z
);
// // Set different colors for each element

View file

@ -6,24 +6,34 @@ using namespace Crafter;
int main() {
WindowWayland window(1280, 720, "Hello Input!");
RenderingElement element(
RenderingElement<true, true, false> element(
{
FractionalToMapped(0.5), //anchorX: relative position where this elements x anchor (top-left) is placed to its parent x anchor
FractionalToMapped(0.5), //anchorY: relative position where this elements y anchor (top-left) is placed to its parent y anchor
FractionalToMapped(0.5), //relativeSizeX: the relative x size this element should be scaled to compared to its parent
FractionalToMapped(0.5), //relativeSizeY: the relative y size this element should be scaled to compared to its parent
FractionalToMapped(0.5), //anchorOffsetX: the amount this element's anchor should be offset from the top left corner (0.5 to in the middle)
FractionalToMapped(0.5), //anchorOffsetY: the amount this element's anchor should be offset from the top left corner (0.5 to place it in the middle)
0 //z: this elements Z position
},
OpaqueType::FullyOpaque,
2,
1,
FractionalToMapped(0.5), //anchorX: relative position where this elements x anchor (top-left) is placed to its parent x anchor
FractionalToMapped(0.5), //anchorY: relative position where this elements y anchor (top-left) is placed to its parent y anchor
FractionalToMapped(0.5), //relativeSizeX: the relative x size this element should be scaled to compared to its parent
FractionalToMapped(0.5), //relativeSizeY: the relative y size this element should be scaled to compared to its parent
FractionalToMapped(0.5), //anchorOffsetX: the amount this element's anchor should be offset from the top left corner (0.5 to in the middle)
FractionalToMapped(0.5), //anchorOffsetY: the amount this element's anchor should be offset from the top left corner (0.5 to place it in the middle)
0, //z: this elements Z position
1
);
MouseElement mouse(window);
MouseElement mouse({
FractionalToMapped(0), //anchorX: relative position where this elements x anchor (top-left) is placed to its parent x anchor
FractionalToMapped(0), //anchorY: relative position where this elements y anchor (top-left) is placed to its parent y anchor
FractionalToMapped(1), //relativeSizeX: the relative x size this element should be scaled to compared to its parent
FractionalToMapped(1), //relativeSizeY: the relative y size this element should be scaled to compared to its parent
FractionalToMapped(0), //anchorOffsetX: the amount this element's anchor should be offset from the top left corner (0.5 to in the middle)
FractionalToMapped(0), //anchorOffsetY: the amount this element's anchor should be offset from the top left corner (0.5 to place it in the middle)
0 //z: this elements Z position
}, window);
element.children.push_back(&mouse);
window.elements.push_back(&element);
element.buffer = {{255, 0, 0 ,255}, {0, 255, 0 ,255}};
element.scalingBuffer = {{255, 0, 0 ,255}, {0, 255, 0 ,255}};
element.UpdatePosition(window);
EventListener<MousePoint> clickListener(&mouse.onMouseLeftClick, [&mouse, &window](MousePoint point){

View file

@ -7,9 +7,10 @@
"dependencies": [
{
"path":"../../project.json",
"configuration":"lib-wayland"
"configuration":"lib-wayland-debug"
}
]
],
"debug": true
}
]
}

View file

@ -26,94 +26,42 @@ import std;
using namespace Crafter;
GridElement::GridElement(std::uint_fast32_t columns, std::uint_fast32_t rows,
std::int_fast32_t spacingX, std::int_fast32_t spacingY,
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)
: Transform(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z),
columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY) {
GridElement::GridElement(std::uint_fast32_t columns, std::uint_fast32_t rows, std::int_fast32_t spacingX, std::int_fast32_t spacingY, Anchor anchor) : Transform(anchor), columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY) {
}
void GridElement::SetGridSize(std::uint_fast32_t columns, std::uint_fast32_t rows) {
this->columns = columns;
this->rows = rows;
}
void GridElement::SetSpacing(std::int_fast32_t spacingX, std::int_fast32_t spacingY) {
this->spacingX = spacingX;
this->spacingY = spacingY;
void GridElement::UpdatePositionScaled(Window& window) {
std::int_fast32_t cellWidth = SCALE / columns;
std::int_fast32_t cellHeight = SCALE / rows;
std::size_t childIndex = 0;
for (std::uint_fast32_t row = 0; row < rows && childIndex < children.size(); ++row) {
for (std::uint_fast32_t col = 0; col < columns && childIndex < children.size(); ++col) {
Transform* child = children[childIndex];
// Calculate position for this child
std::int_fast32_t childX = (cellWidth * col) + (spacingX * col);
std::int_fast32_t childY = (cellHeight * row) + (spacingY * row);
// Apply relative positioning
child->anchor.x = childX;
child->anchor.y = childY;
child->anchor.width = cellWidth;
child->anchor.height = cellHeight;
// Update child position
child->UpdatePosition(window, *this);
childIndex++;
}
}
}
void GridElement::UpdatePosition(Window& window) {
window.ScaleElement(*this);
// Calculate grid dimensions
std::int_fast32_t totalWidth = 0;
std::int_fast32_t totalHeight = 0;
if (columns > 0 && rows > 0) {
// Calculate the space available for children based on grid size
std::int_fast32_t cellWidth = (relativeWidth - (columns - 1) * spacingX) / columns;
std::int_fast32_t cellHeight = (relativeHeight - (rows - 1) * spacingY) / rows;
// Position each child in the grid
std::size_t childIndex = 0;
for (std::uint_fast32_t row = 0; row < rows && childIndex < children.size(); ++row) {
for (std::uint_fast32_t col = 0; col < columns && childIndex < children.size(); ++col) {
Transform* child = children[childIndex];
// Calculate position for this child
std::int_fast32_t childX = anchorOffsetX + (cellWidth * col) + (spacingX * col);
std::int_fast32_t childY = anchorOffsetY + (cellHeight * row) + (spacingY * row);
// Apply relative positioning
child->anchorX = childX;
child->anchorY = childY;
child->relativeWidth = cellWidth;
child->relativeHeight = cellHeight;
// Update child position
child->UpdatePosition(window, *this);
childIndex++;
}
}
}
UpdatePositionScaled(window);
}
void GridElement::UpdatePosition(Window& window, Transform& parent) {
window.ScaleElement(*this, parent);
// Calculate grid dimensions
std::int_fast32_t totalWidth = 0;
std::int_fast32_t totalHeight = 0;
if (columns > 0 && rows > 0) {
// Calculate the space available for children based on grid size
std::int_fast32_t cellWidth = (relativeWidth - (columns - 1) * spacingX) / columns;
std::int_fast32_t cellHeight = (relativeHeight - (rows - 1) * spacingY) / rows;
// Position each child in the grid
std::size_t childIndex = 0;
for (std::uint_fast32_t row = 0; row < rows && childIndex < children.size(); ++row) {
for (std::uint_fast32_t col = 0; col < columns && childIndex < children.size(); ++col) {
Transform* child = children[childIndex];
// Calculate position for this child
std::int_fast32_t childX = anchorOffsetX + (cellWidth * col) + (spacingX * col);
std::int_fast32_t childY = anchorOffsetY + (cellHeight * row) + (spacingY * row);
// Apply relative positioning
child->anchorX = childX;
child->anchorY = childY;
child->relativeWidth = cellWidth;
child->relativeHeight = cellHeight;
// Update child position
child->UpdatePosition(window, *this);
childIndex++;
}
}
}
UpdatePositionScaled(window);
}

View file

@ -1,73 +0,0 @@
/*
Crafter®.Graphics
Copyright (C) 2025 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 as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
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"
module Crafter.Graphics:ImageElement_impl;
import :ImageElement;
import :RenderingElement;
import :Window;
import :Types;
import std;
using namespace Crafter;
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(OpaqueType::FullyOpaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) {
RenderImage(imagePath);
}
void ImageElement::RenderImage(const std::string_view path) {
std::filesystem::path abs = std::filesystem::absolute(path);
int xSize;
int ySize;
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;
buffer[x*ySize+y].r = bgData[idx];
buffer[x*ySize+y].g = bgData[idx+1];
buffer[x*ySize+y].b = bgData[idx+2];
buffer[x*ySize+y].a = bgData[idx+3];
}
}
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,14 +27,15 @@ import std;
using namespace Crafter;
MouseElement::MouseElement(WindowMouse& window, 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) : Transform(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z) {
MouseElement::MouseElement(Anchor anchor, WindowMouse& window) : Transform(anchor) {
window.mouseElements.push_back(this);
}
MouseElement::MouseElement(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) : Transform(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z) {
MouseElement::MouseElement(Anchor anchor) : Transform(anchor) {
}
void MouseElement::UpdatePosition(Window& window) {
window.ScaleMouse(*this);
for(Transform* child : children) {

View file

@ -1,162 +0,0 @@
/*
Crafter®.Graphics
Copyright (C) 2025 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 as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
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 Crafter.Graphics:RenderingElement_impl;
import :RenderingElement;
import :Window;
import :Types;
import :Font;
import std;
using namespace Crafter;
RenderingElement::RenderingElement(OpaqueType opaque) : Transform(), 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) : Transform(anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z), opaque(opaque) {
}
RenderingElementPreScaled::RenderingElementPreScaled(OpaqueType opaque) : RenderingElement(opaque) {
}
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) : RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z) {
}
RenderingElementScaling::RenderingElementScaling(OpaqueType opaque) : RenderingElement(opaque) {
}
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) : RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z) {
}
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) : bufferWidth(bufferWidth), bufferHeight(bufferHeight), buffer(bufferWidth*bufferHeight), RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z) {
}
void RenderingElementPreScaled::UpdatePosition(Window& window) {
ScaleData oldScale = scaled;
window.ScaleElement(*this);
if(oldScale.width != scaled.width || oldScale.height != scaled.height) {
bufferScaled.resize(scaled.width * scaled.height);
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
} else if(oldScale.x != scaled.x || oldScale.y != scaled.y) {
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
}
for(Transform* child : children) {
child->UpdatePosition(window, *this);
}
}
void RenderingElementPreScaled::UpdatePosition(Window& window, Transform& parent) {
ScaleData oldScale = scaled;
window.ScaleElement(*this, parent);
if(oldScale.width != scaled.width || oldScale.height != scaled.height) {
bufferScaled.resize(scaled.width * scaled.height);
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
} else if(oldScale.x != scaled.x || oldScale.y != scaled.y) {
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
}
for(Transform* child : children) {
child->UpdatePosition(window, *this);
}
}
void RenderingElementPreScaled::CopyNearestNeighbour(Pixel_BU8_GU8_RU8_AU8* dst, std::uint_fast32_t dstWidth, std::uint_fast32_t dstHeight) const {
for (std::uint_fast32_t y = 0; y < dstHeight; y++) {
std::uint_fast32_t srcY = y * scaled.height / dstHeight;
for (std::uint_fast32_t x = 0; x < dstWidth; x++) {
std::uint_fast32_t srcX = x * scaled.width / dstWidth;
dst[y * dstWidth + x] = bufferScaled[srcY * scaled.width + srcX];
}
}
}
void RenderingElementScaling::CopyNearestNeighbour(Pixel_BU8_GU8_RU8_AU8* dst, std::uint_fast32_t dstWidth, std::uint_fast32_t dstHeight) const {
for (std::uint_fast32_t y = 0; y < dstHeight; y++) {
std::uint_fast32_t srcY = y * bufferHeight / dstHeight;
for (std::uint_fast32_t x = 0; x < dstWidth; x++) {
std::uint_fast32_t srcX = x * bufferWidth / dstWidth;
dst[y * dstWidth + x] = buffer[srcY * bufferWidth + srcX];
}
}
}
void RenderingElementScaling::UpdatePosition(Window& window) {
ScaleData oldScale = scaled;
std::cout << MappedToFractional(relativeWidth) << std::endl;
window.ScaleElement(*this);
std::cout << scaled.width << std::endl;
if(oldScale.width != scaled.width || oldScale.height != scaled.height) {
bufferScaled.resize(scaled.width * scaled.height);
CopyNearestNeighbour(bufferScaled.data(), scaled.width, scaled.height);
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
} else if(oldScale.x != scaled.x || oldScale.y != scaled.y) {
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
}
for(Transform* child : children) {
child->UpdatePosition(window, *this);
}
}
void RenderingElementScaling::UpdatePosition(Window& window, Transform& parent) {
ScaleData oldScale = scaled;
window.ScaleElement(*this, parent);
if(oldScale.width != scaled.width || oldScale.height != scaled.height) {
bufferScaled.resize(scaled.width * scaled.height);
CopyNearestNeighbour(bufferScaled.data(), scaled.width, scaled.height);
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
} else if(oldScale.x != scaled.x || oldScale.y != scaled.y) {
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
}
for(Transform* child : children) {
child->UpdatePosition(window, *this);
}
}
void RenderingElementScaling::ResizeBuffer(std::uint_fast32_t width, std::uint_fast32_t height) {
this->bufferWidth = width;
this->bufferHeight = height;
buffer.resize(width * height);
}

View file

@ -1,172 +0,0 @@
/*
Crafter®.Graphics
Copyright (C) 2025 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 as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
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 Crafter.Graphics:RenderingElementScalingRotating2D_impl;
import :RenderingElement;
import :Window;
import :Types;
import :Font;
import std;
using namespace Crafter;
RenderingElementScalingRotating2D::RenderingElementScalingRotating2D(OpaqueType opaque) : RenderingElement(opaque), rotation(0) {
}
RenderingElementScalingRotating2D::RenderingElementScalingRotating2D(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) : RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z), rotation(0) {
}
RenderingElementScalingRotating2D::RenderingElementScalingRotating2D(OpaqueType opaque, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight, std::uint_fast32_t rotation, 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)
: bufferWidth(bufferWidth), bufferHeight(bufferHeight), buffer(bufferWidth*bufferHeight),
rotation(rotation),
RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z) {
}
void RenderingElementScalingRotating2D::ResizeBuffer(std::uint_fast32_t width, std::uint_fast32_t height) {
this->bufferWidth = width;
this->bufferHeight = height;
buffer.resize(width * height);
}
void RenderingElementScalingRotating2D::UpdateRotation(std::uint_fast32_t rotation) {
rotationUpdated = true;
this->rotation = rotation;
}
void RenderingElementScalingRotating2D::UpdateScaledBuffer(ScaleData& scaled) {
// Rotation
const double rad = (static_cast<double>(rotation) / static_cast<double>(std::numeric_limits<std::uint_fast32_t>::max())) * 2.0 * std::numbers::pi;
const std::uint_fast32_t dstWidth = scaled.width;
const std::uint_fast32_t dstHeight = scaled.height;
const double rotatedWidth = scaled.width * (std::abs(std::cos(rad)) + std::abs(std::sin(rad)));
const double rotatedHeight = scaled.height * (std::abs(std::cos(rad)) + std::abs(std::sin(rad)));
const std::uint_fast32_t diffX = std::ceil((rotatedWidth - scaled.width)/2);
const std::uint_fast32_t diffY = std::ceil((rotatedHeight - scaled.height)/2);
scaled.width += diffX + diffX;
scaled.height += diffY + diffY;
scaled.x -= diffX;
scaled.y -= diffY;
bufferScaled.clear();
bufferScaled.resize(scaled.width * scaled.height);
// Destination center
const double dstCx = (static_cast<double>(scaled.width) - 1.0) * 0.5;
const double dstCy = (static_cast<double>(scaled.height) - 1.0) * 0.5;
// Source center
const double srcCx = (static_cast<double>(bufferWidth) - 1.0) * 0.5;
const double srcCy = (static_cast<double>(bufferHeight) - 1.0) * 0.5;
const double c = std::cos(rad);
const double s = std::sin(rad);
// Scale factors (destination → source)
const double scaleX = static_cast<double>(bufferWidth) / dstWidth;
const double scaleY = static_cast<double>(bufferHeight) / dstHeight;
for (std::uint_fast32_t yB = 0; yB < scaled.height; ++yB) {
for (std::uint_fast32_t xB = 0; xB < scaled.width; ++xB) {
// ---- Destination pixel relative to center ----
const double dx = (static_cast<double>(xB) - dstCx) * scaleX;
const double dy = (static_cast<double>(yB) - dstCy) * scaleY;
// ---- Inverse rotation ----
const double sx = (c * dx - s * dy) + srcCx;
const double sy = (s * dx + c * dy) + srcCy;
// ---- Nearest neighbour sampling ----
const std::int_fast32_t srcX = static_cast<std::int_fast32_t>(std::round(sx));
const std::int_fast32_t srcY = static_cast<std::int_fast32_t>(std::round(sy));
if (srcX >= 0 && srcX < bufferWidth && srcY >= 0 && srcY < bufferHeight) {
bufferScaled[yB * scaled.width + xB] = buffer[srcY * bufferWidth + srcX];
}
}
}
}
void RenderingElementScalingRotating2D::UpdatePosition(Window& window) {
ScaleData oldScale = scaled;
window.ScaleElement(*this);
if(oldScale.width != scaled.width || oldScale.height != scaled.height || rotationUpdated) {
UpdateScaledBuffer(scaled);
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
rotationUpdated = false;
} else if(oldScale.x != scaled.x || oldScale.y != scaled.y) {
const double rad = (static_cast<double>(rotation) / static_cast<double>(std::numeric_limits<std::uint_fast32_t>::max())) * 2.0 * std::numbers::pi;
const double rotatedWidth = scaled.width * (std::abs(std::cos(rad)) + std::abs(std::sin(rad)));
const double rotatedHeight = scaled.height * (std::abs(std::cos(rad)) + std::abs(std::sin(rad)));
scaled.width = static_cast<std::uint_fast32_t>(std::ceil(rotatedWidth));
scaled.height = static_cast<std::uint_fast32_t>(std::ceil(rotatedHeight));
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
}
for(Transform* child : children) {
child->UpdatePosition(window, *this);
}
}
void RenderingElementScalingRotating2D::UpdatePosition(Window& window, Transform& parent) {
ScaleData oldScale = scaled;
window.ScaleElement(*this, parent);
if(oldScale.width != scaled.width || oldScale.height != scaled.height || rotationUpdated) {
UpdateScaledBuffer(scaled);
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
rotationUpdated = false;
} else if(oldScale.x != scaled.x || oldScale.y != scaled.y) {
const double rad = (static_cast<double>(rotation) / static_cast<double>(std::numeric_limits<std::uint_fast32_t>::max())) * 2.0 * std::numbers::pi;
const double rotatedWidth = scaled.width * (std::abs(std::cos(rad)) + std::abs(std::sin(rad)));
const double rotatedHeight = scaled.height * (std::abs(std::cos(rad)) + std::abs(std::sin(rad)));
scaled.width = static_cast<std::uint_fast32_t>(std::ceil(rotatedWidth));
scaled.height = static_cast<std::uint_fast32_t>(std::ceil(rotatedHeight));
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
}
for(Transform* child : children) {
child->UpdatePosition(window, *this);
}
}

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) : RenderingElementPreScaled(OpaqueType::Transparent, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z) {
TextElement::TextElement(Anchor anchor): RenderingElement<false, false, false>(anchor, OpaqueType::Transparent) {
}
@ -41,7 +41,7 @@ void TextElement::RenderText(Window& window, const std::string_view text, float
int baseline = (int)(font.ascent * scale);
// Clear the scaled buffer
for (auto& pixel : bufferScaled) {
for (auto& pixel : buffer) {
pixel = {0, 0, 0, 0};
}
@ -144,7 +144,7 @@ void TextElement::RenderText(Window& window, const std::string_view text, float
// Only draw pixels that are within our scaled buffer bounds
if (bufferX >= 0 && bufferX < (int)scaled.width && bufferY >= 0 && bufferY < (int)scaled.height) {
bufferScaled[bufferY * scaled.width + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]};
buffer[bufferY * scaled.width + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]};
}
}
}
@ -288,11 +288,11 @@ void TextElement::RenderText(Window& window, const std::string_view text, float
for (std::uint_fast32_t y = 0; y < scaled.height; ++y) {
for (std::uint_fast32_t x = 0; x < scaled.width; ++x) {
std::uint_fast32_t index = y * scaled.width + x;
if (index < bufferScaled.size()) {
if (index < buffer.size()) {
// Move the pixel vertically
std::uint_fast32_t newIndex = (y + verticalOffset) * scaled.width + x;
if (newIndex < bufferScaled.size()) {
bufferScaled[newIndex] = bufferScaled[index];
if (newIndex < buffer.size()) {
buffer[newIndex] = buffer[index];
}
}
}
@ -355,7 +355,7 @@ void TextElement::RenderWrappedLine(const std::string_view line, float scale, in
// Only draw pixels that are within our scaled buffer bounds
if (bufferX >= 0 && bufferX < (int)scaled.width && bufferY >= 0 && bufferY < (int)scaled.height) {
bufferScaled[bufferY * scaled.width + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]};
buffer[bufferY * scaled.width + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]};
}
}
}

View file

@ -27,15 +27,20 @@ import std;
using namespace Crafter;
Transform::Transform(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) : anchorX(anchorX), anchorY(anchorY), relativeWidth(relativeWidth), relativeHeight(relativeHeight), anchorOffsetX(anchorOffsetX), anchorOffsetY(anchorOffsetY), z(z) {
Anchor::Anchor(std::int_fast32_t x, std::int_fast32_t y, std::uint_fast32_t width, std::uint_fast32_t height, std::int_fast32_t offsetX, std::int_fast32_t offsetY, std::int_fast32_t z): x(x), y(y), width(width), height(height), offsetX(offsetX), offsetY(offsetY), z(z) {
}
Transform::Transform(Anchor anchor) : anchor(anchor) {
}
void Transform::UpdatePosition(Window& window) {
window.ScaleElement(*this);
for(Transform* child : children) {
child->UpdatePosition(window, *this);
}
child->UpdatePosition(window, *this);
}
}
void Transform::UpdatePosition(Window& window, Transform& parent) {

View file

@ -29,35 +29,35 @@ Window::Window(std::int_fast32_t width, std::int_fast32_t height) : width(width)
}
void Window::ScaleElement(Transform& element) {
element.scaled.width = MappedToPixel(element.relativeWidth, width);
element.scaled.height = MappedToPixel(element.relativeHeight, height);
element.scaled.x = MappedToPixel(element.anchorX, width) - MappedToPixel(element.anchorOffsetX, element.scaled.width);
element.scaled.y = MappedToPixel(element.anchorY, height) - MappedToPixel(element.anchorOffsetY, element.scaled.height);
element.scaled.width = MappedToPixel(element.anchor.width, width);
element.scaled.height = MappedToPixel(element.anchor.height, height);
element.scaled.x = MappedToPixel(element.anchor.x, width) - MappedToPixel(element.anchor.offsetX, element.scaled.width);
element.scaled.y = MappedToPixel(element.anchor.y, height) - MappedToPixel(element.anchor.offsetY, element.scaled.height);
}
void Window::ScaleElement(Transform& element, Transform& parent) {
element.scaled.width = MappedToPixel(element.relativeWidth, parent.scaled.width);
element.scaled.height = MappedToPixel(element.relativeHeight, parent.scaled.height);
element.scaled.x = MappedToPixel(element.anchorX, parent.scaled.width) - MappedToPixel(element.anchorOffsetX, element.scaled.width) + parent.scaled.x;
element.scaled.y = MappedToPixel(element.anchorY, parent.scaled.height) - MappedToPixel(element.anchorOffsetY, element.scaled.height) + parent.scaled.y;
element.scaled.width = MappedToPixel(element.anchor.width, parent.scaled.width);
element.scaled.height = MappedToPixel(element.anchor.height, parent.scaled.height);
element.scaled.x = MappedToPixel(element.anchor.x, parent.scaled.width) - MappedToPixel(element.anchor.offsetX, element.scaled.width) + parent.scaled.x;
element.scaled.y = MappedToPixel(element.anchor.y, parent.scaled.height) - MappedToPixel(element.anchor.offsetY, element.scaled.height) + parent.scaled.y;
}
void Window::ScaleMouse(Transform& element, Transform& parent) {
std::int_fast32_t boundlessWidth = PixelToMappedBoundless(parent.scaled.width, width);
std::int_fast32_t boundlessHeight = PixelToMappedBoundless(parent.scaled.height, height);
element.scaled.width = BoundToBoundless(MappedToPixel(element.relativeWidth, PixelToMapped(parent.scaled.width, width)));
element.scaled.height = BoundToBoundless(MappedToPixel(element.relativeHeight, PixelToMapped(parent.scaled.height, height)));
element.scaled.x = MappedToPixelBoundless(element.anchorX, boundlessWidth) - MappedToPixelBoundless(element.anchorOffsetX, element.scaled.width) + PixelToMappedBoundless(parent.scaled.x, width);
element.scaled.y = MappedToPixelBoundless(element.anchorY, boundlessHeight) - MappedToPixelBoundless(element.anchorOffsetY, element.scaled.height) + PixelToMappedBoundless(parent.scaled.y, height);
element.scaled.width = BoundToBoundless(MappedToPixel(element.anchor.width, PixelToMapped(parent.scaled.width, width)));
element.scaled.height = BoundToBoundless(MappedToPixel(element.anchor.height, PixelToMapped(parent.scaled.height, height)));
element.scaled.x = MappedToPixelBoundless(element.anchor.x, boundlessWidth) - MappedToPixelBoundless(element.anchor.offsetX, element.scaled.width) + PixelToMappedBoundless(parent.scaled.x, width);
element.scaled.y = MappedToPixelBoundless(element.anchor.y, boundlessHeight) - MappedToPixelBoundless(element.anchor.offsetY, element.scaled.height) + PixelToMappedBoundless(parent.scaled.y, height);
}
void Window::ScaleMouse(Transform& element) {
// std::int_fast32_t boundlessWidth = PixelToMappedBoundless(parent.scaled.width, width);
// std::int_fast32_t boundlessHeight = PixelToMappedBoundless(parent.scaled.height, height);
// element.scaled.width = BoundToBoundless(MappedToPixel(element.relativeWidth, width));
// element.scaled.height = BoundToBoundless(MappedToPixel(element.relativeHeight, height));
// element.scaled.x = MappedToPixelBoundless(element.anchorX, boundlessWidth) - MappedToPixelBoundless(element.anchorOffsetX, element.scaled.width) + PixelToMappedBoundless(parent.scaled.x, width);
// element.scaled.y = MappedToPixelBoundless(element.anchorY, boundlessHeight) - MappedToPixelBoundless(element.anchorOffsetY, element.scaled.height) + PixelToMappedBoundless(parent.scaled.y, height);
// element.scaled.width = BoundToBoundless(MappedToPixel(element.anchor.width, width));
// element.scaled.height = BoundToBoundless(MappedToPixel(element.anchor.height, height));
// element.scaled.x = MappedToPixelBoundless(element.anchor.x, boundlessWidth) - MappedToPixelBoundless(element.anchor.offsetX, element.scaled.width) + PixelToMappedBoundless(parent.scaled.x, width);
// element.scaled.y = MappedToPixelBoundless(element.anchor.y, boundlessHeight) - MappedToPixelBoundless(element.anchor.offsetY, element.scaled.height) + PixelToMappedBoundless(parent.scaled.y, height);
}
#ifdef CRAFTER_TIMING
@ -107,30 +107,30 @@ void Window::AddDirtyRect(ScaleData scale) {
.bottom = std::min(scale.y + scale.height, height),
};
if (rect.left >= rect.right || rect.top >= rect.bottom) {
return;
}
// if (rect.left >= rect.right || rect.top >= rect.bottom) {
// return;
// }
for(ClipRect existing : dirtyRects) {
//fully enclosed
if(rect.left >= existing.left && rect.right <= existing.right && rect.top >= existing.top && rect.bottom <= existing.bottom) {
return;
}
// for(ClipRect existing : dirtyRects) {
// //fully enclosed
// if(rect.left >= existing.left && rect.right <= existing.right && rect.top >= existing.top && rect.bottom <= existing.bottom) {
// return;
// }
//horizontal line
if(rect.top == existing.top && rect.bottom == existing.bottom) {
existing.left = std::min(rect.left, existing.left);
existing.right = std::max(rect.right, existing.right);
return;
}
// //horizontal line
// if(rect.top == existing.top && rect.bottom == existing.bottom) {
// existing.left = std::min(rect.left, existing.left);
// existing.right = std::max(rect.right, existing.right);
// return;
// }
//vertical line
if(rect.left == existing.left && rect.right == existing.right) {
existing.top = std::min(rect.top, existing.top);
existing.bottom = std::max(rect.bottom, existing.bottom);
return;
}
}
// //vertical line
// if(rect.left == existing.left && rect.right == existing.right) {
// existing.top = std::min(rect.top, existing.top);
// existing.bottom = std::max(rect.bottom, existing.bottom);
// return;
// }
// }
//no overlap
dirtyRects.push_back(rect);

View file

@ -23,7 +23,6 @@ module;
#include <stdlib.h>
#include <unistd.h>
#include <linux/input-event-codes.h>
#include <xkbcommon/xkbcommon.h>
#include "../lib/xdg-shell-client-protocol.h"
#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h"
#include <string.h>
@ -155,7 +154,7 @@ inline void blend_pixel_optimized(Pixel_BU8_GU8_RU8_AU8& dst, const Pixel_BU8_GU
}
void WindowWayland::RenderElement(Transform* transform) {
RenderingElement* element = dynamic_cast<RenderingElement*>(transform);
RenderingElementBase* element = dynamic_cast<RenderingElementBase*>(transform);
if(element) {
#ifdef CRAFTER_TIMING
auto start = std::chrono::high_resolution_clock::now();
@ -171,7 +170,7 @@ void WindowWayland::RenderElement(Transform* transform) {
dirty.right = std::min(element->scaled.x+element->scaled.width, dirty.right);
dirty.bottom = std::min(element->scaled.y+element->scaled.height, dirty.bottom);
const Pixel_BU8_GU8_RU8_AU8* src_buffer = element->bufferScaled.data();
const Pixel_BU8_GU8_RU8_AU8* src_buffer = element->buffer.data();
std::int_fast32_t src_width = element->scaled.width;
std::int_fast32_t src_height = element->scaled.height;
@ -226,7 +225,7 @@ void WindowWayland::RenderElement(Transform* transform) {
#endif
}
std::sort(transform->children.begin(), transform->children.end(), [](Transform* a, Transform* b){ return a->z < b->z; });
std::sort(transform->children.begin(), transform->children.end(), [](Transform* a, Transform* b){ return a->anchor.z < b->anchor.z; });
for(Transform* child : transform->children) {
this->RenderElement(child);
}
@ -234,87 +233,107 @@ void WindowWayland::RenderElement(Transform* transform) {
void WindowWayland::Render() {
elements.erase(std::remove(elements.begin(), elements.end(), static_cast<Transform*>(nullptr)), elements.end());
std::sort(elements.begin(), elements.end(), [](Transform* a, Transform* b){ return a->z < b->z; });
std::sort(elements.begin(), elements.end(), [](Transform* a, Transform* b){ return a->anchor.z < b->anchor.z; });
std::vector<ClipRect> newClip;
//std::vector<ClipRect> newClip;
for (std::uint_fast32_t i = 0; i < dirtyRects.size(); i++) {
ClipRect rect = dirtyRects[i];
for (std::uint_fast32_t i2 = i + 1; i2 < dirtyRects.size(); i2++) {
ClipRect existing = dirtyRects[i2];
if(rect.bottom >= existing.top && rect.top <= existing.top) {
newClip.push_back({
.left = rect.left,
.right = rect.right,
.top = rect.top,
.bottom = existing.top,
});
//-| shape
if(rect.right > existing.right) {
newClip.push_back({
.left = existing.right,
.right = rect.right,
.top = existing.top,
.bottom = existing.bottom,
});
}
//|- shape
if(rect.left < existing.left) {
newClip.push_back({
.left = rect.left,
.right = existing.left,
.top = existing.top,
.bottom = existing.bottom,
});
}
//-| or |- shape where rect extends further down
if(rect.bottom > existing.bottom) {
newClip.push_back({
.left = rect.left,
.right = rect.right,
.top = existing.bottom,
.bottom = rect.bottom,
});
}
goto inner;
}
if (rect.left <= existing.right && rect.right >= existing.left) {
newClip.push_back({
.left = rect.left,
.right = existing.left,
.top = rect.top,
.bottom = rect.bottom,
});
if (rect.right > existing.right) {
newClip.push_back({
.left = existing.right,
.right = rect.right,
.top = rect.top,
.bottom = rect.bottom,
});
}
goto inner;
}
}
newClip.push_back(rect);
inner:;
}
dirtyRects = std::move(newClip);
// for(uint_fast32_t i = 0; i < width*height; i++) {
// framebuffer[i] = {0, 0, 0, 255};
// for (std::uint_fast32_t i = 0; i < dirtyRects.size(); i++) {
// ClipRect rect = dirtyRects[i];
// for (std::uint_fast32_t i2 = i + 1; i2 < dirtyRects.size(); i2++) {
// ClipRect existing = dirtyRects[i2];
// if(rect.bottom >= existing.top && rect.top <= existing.top) {
// newClip.push_back({
// .left = rect.left,
// .right = rect.right,
// .top = rect.top,
// .bottom = existing.top,
// });
// //-| shape
// if(rect.right > existing.right) {
// newClip.push_back({
// .left = existing.right,
// .right = rect.right,
// .top = existing.top,
// .bottom = existing.bottom,
// });
// }
// //|- shape
// if(rect.left < existing.left) {
// newClip.push_back({
// .left = rect.left,
// .right = existing.left,
// .top = existing.top,
// .bottom = existing.bottom,
// });
// }
// //-| or |- shape where rect extends further down
// if(rect.bottom > existing.bottom) {
// newClip.push_back({
// .left = rect.left,
// .right = rect.right,
// .top = existing.bottom,
// .bottom = rect.bottom,
// });
// }
// goto inner;
// }
// if (rect.left <= existing.right && rect.right >= existing.left) {
// newClip.push_back({
// .left = rect.left,
// .right = existing.left,
// .top = rect.top,
// .bottom = rect.bottom,
// });
// if (rect.right > existing.right) {
// newClip.push_back({
// .left = existing.right,
// .right = rect.right,
// .top = rect.top,
// .bottom = rect.bottom,
// });
// }
// goto inner;
// }
// }
// newClip.push_back(rect);
// inner:;
// }
//dirtyRects = std::move(newClip);
// std::memset(framebuffer, 0, width*height*4);
// std::cout << dirtyRects.size() << std::endl;
// for (ClipRect rect : dirtyRects) {
// //std::cout << std::format("{}, {}, {}, {}", rect.left, rect.top, rect.right, rect.bottom) << std::endl;
// for (std::int_fast32_t y = rect.top; y < rect.bottom; y++) {
// for (std::int_fast32_t x = rect.left; x < rect.right; x++) {
// framebuffer[y * width + x].r += 30;
// }
// }
// }
// // Color palette
// static const std::vector<Pixel_BU8_GU8_RU8_AU8> colors = {
// {255, 0, 0, 255}, // red
// { 0, 255, 0, 255}, // green
// { 0, 0, 255, 255}, // blue
// {255, 255, 0, 255}, // yellow
// {255, 0, 255, 255}, // magenta
// { 0, 255, 255, 255}, // cyan
// };
// std::size_t rectIndex = 0;
// for (const ClipRect& rect : dirtyRects) {
// const Pixel_BU8_GU8_RU8_AU8& color = colors[rectIndex % colors.size()];
// std::cout << std::format(
// "ClipRect {}: [{}, {}, {}, {}] Color = RGBA({}, {}, {}, {})",
// rectIndex,
// rect.left, rect.top, rect.right, rect.bottom,
// color.r, color.g, color.b, color.a
// ) << std::endl;
// for (std::int_fast32_t y = rect.top; y < rect.bottom; ++y) {
// for (std::int_fast32_t x = rect.left; x < rect.right; ++x) {
// framebuffer[y * width + x] = color;
// }
// }
// ++rectIndex;
// }
if (!dirtyRects.empty()) {
for (ClipRect rect : dirtyRects) {
@ -335,11 +354,17 @@ void WindowWayland::Render() {
dirtyRects.clear();
}
dirtyRects.clear();
wl_surface_damage(surface, 0, 0, width, height);
wl_surface_attach(surface, buffer, 0, 0);
wl_surface_commit(surface);
wl_surface_commit(surface);
wl_surface_damage(surface, 0, 0, 10000, 100000);
}
void WindowWayland::QueueRender() {
std::cout << cb << std::endl;
if(cb == nullptr) {
cb = wl_surface_frame(surface);
wl_callback_add_listener(cb, &wl_callback_listener, this);
}
}
void WindowWayland::StartUpdate() {
@ -396,6 +421,7 @@ void WindowWayland::wl_surface_frame_done(void* data, struct wl_callback *cb, ui
{
auto start = std::chrono::high_resolution_clock::now();
wl_callback_destroy(cb);
cb = nullptr;
WindowWayland* window = reinterpret_cast<WindowWayland*>(data);
#ifdef CRAFTER_TIMING
window->vblank = duration_cast<std::chrono::milliseconds>(start - window->frameEnd);
@ -413,18 +439,18 @@ void WindowWayland::wl_surface_frame_done(void* data, struct wl_callback *cb, ui
window->totalUpdate += entry.second;
}
#endif
#ifdef CRAFTER_TIMING
auto renderStart = std::chrono::high_resolution_clock::now();
window->renderTimings.clear();
#endif
window->Render();
#ifdef CRAFTER_TIMING
auto renderEnd = std::chrono::high_resolution_clock::now();
window->totalRender = renderEnd - renderStart;
#endif
}
#ifdef CRAFTER_TIMING
auto renderStart = std::chrono::high_resolution_clock::now();
window->renderTimings.clear();
#endif
window->Render();
#ifdef CRAFTER_TIMING
auto renderEnd = std::chrono::high_resolution_clock::now();
window->totalRender = renderEnd - renderStart;
#endif
#ifdef CRAFTER_TIMING
window->frameEnd = std::chrono::high_resolution_clock::now();
@ -557,6 +583,132 @@ void WindowWayland::keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t s
}
CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym)
{
switch (sym)
{
// Alphabet
case XKB_KEY_a: return CrafterKeys::A;
case XKB_KEY_b: return CrafterKeys::B;
case XKB_KEY_c: return CrafterKeys::C;
case XKB_KEY_d: return CrafterKeys::D;
case XKB_KEY_e: return CrafterKeys::E;
case XKB_KEY_f: return CrafterKeys::F;
case XKB_KEY_g: return CrafterKeys::G;
case XKB_KEY_h: return CrafterKeys::H;
case XKB_KEY_i: return CrafterKeys::I;
case XKB_KEY_j: return CrafterKeys::J;
case XKB_KEY_k: return CrafterKeys::K;
case XKB_KEY_l: return CrafterKeys::L;
case XKB_KEY_m: return CrafterKeys::M;
case XKB_KEY_n: return CrafterKeys::N;
case XKB_KEY_o: return CrafterKeys::O;
case XKB_KEY_p: return CrafterKeys::P;
case XKB_KEY_q: return CrafterKeys::Q;
case XKB_KEY_r: return CrafterKeys::R;
case XKB_KEY_s: return CrafterKeys::S;
case XKB_KEY_t: return CrafterKeys::T;
case XKB_KEY_u: return CrafterKeys::U;
case XKB_KEY_v: return CrafterKeys::V;
case XKB_KEY_w: return CrafterKeys::W;
case XKB_KEY_x: return CrafterKeys::X;
case XKB_KEY_y: return CrafterKeys::Y;
case XKB_KEY_z: return CrafterKeys::Z;
// Numbers
case XKB_KEY_0: return CrafterKeys::_0;
case XKB_KEY_1: return CrafterKeys::_1;
case XKB_KEY_2: return CrafterKeys::_2;
case XKB_KEY_3: return CrafterKeys::_3;
case XKB_KEY_4: return CrafterKeys::_4;
case XKB_KEY_5: return CrafterKeys::_5;
case XKB_KEY_6: return CrafterKeys::_6;
case XKB_KEY_7: return CrafterKeys::_7;
case XKB_KEY_8: return CrafterKeys::_8;
case XKB_KEY_9: return CrafterKeys::_9;
// Function keys
case XKB_KEY_F1: return CrafterKeys::F1;
case XKB_KEY_F2: return CrafterKeys::F2;
case XKB_KEY_F3: return CrafterKeys::F3;
case XKB_KEY_F4: return CrafterKeys::F4;
case XKB_KEY_F5: return CrafterKeys::F5;
case XKB_KEY_F6: return CrafterKeys::F6;
case XKB_KEY_F7: return CrafterKeys::F7;
case XKB_KEY_F8: return CrafterKeys::F8;
case XKB_KEY_F9: return CrafterKeys::F9;
case XKB_KEY_F10: return CrafterKeys::F10;
case XKB_KEY_F11: return CrafterKeys::F11;
case XKB_KEY_F12: return CrafterKeys::F12;
// Control keys
case XKB_KEY_Escape: return CrafterKeys::Escape;
case XKB_KEY_Tab: return CrafterKeys::Tab;
case XKB_KEY_Return: return CrafterKeys::Enter;
case XKB_KEY_space: return CrafterKeys::Space;
case XKB_KEY_BackSpace: return CrafterKeys::Backspace;
case XKB_KEY_Delete: return CrafterKeys::Delete;
case XKB_KEY_Insert: return CrafterKeys::Insert;
case XKB_KEY_Home: return CrafterKeys::Home;
case XKB_KEY_End: return CrafterKeys::End;
case XKB_KEY_Page_Up: return CrafterKeys::PageUp;
case XKB_KEY_Page_Down: return CrafterKeys::PageDown;
case XKB_KEY_Caps_Lock: return CrafterKeys::CapsLock;
case XKB_KEY_Num_Lock: return CrafterKeys::NumLock;
case XKB_KEY_Scroll_Lock:return CrafterKeys::ScrollLock;
// Modifiers
case XKB_KEY_Shift_L: return CrafterKeys::LeftShift;
case XKB_KEY_Shift_R: return CrafterKeys::RightShift;
case XKB_KEY_Control_L: return CrafterKeys::LeftCtrl;
case XKB_KEY_Control_R: return CrafterKeys::RightCtrl;
case XKB_KEY_Alt_L: return CrafterKeys::LeftAlt;
case XKB_KEY_Alt_R: return CrafterKeys::RightAlt;
case XKB_KEY_Super_L: return CrafterKeys::LeftSuper;
case XKB_KEY_Super_R: return CrafterKeys::RightSuper;
// Arrows
case XKB_KEY_Up: return CrafterKeys::Up;
case XKB_KEY_Down: return CrafterKeys::Down;
case XKB_KEY_Left: return CrafterKeys::Left;
case XKB_KEY_Right: return CrafterKeys::Right;
// Keypad
case XKB_KEY_KP_0: return CrafterKeys::keypad_0;
case XKB_KEY_KP_1: return CrafterKeys::keypad_1;
case XKB_KEY_KP_2: return CrafterKeys::keypad_2;
case XKB_KEY_KP_3: return CrafterKeys::keypad_3;
case XKB_KEY_KP_4: return CrafterKeys::keypad_4;
case XKB_KEY_KP_5: return CrafterKeys::keypad_5;
case XKB_KEY_KP_6: return CrafterKeys::keypad_6;
case XKB_KEY_KP_7: return CrafterKeys::keypad_7;
case XKB_KEY_KP_8: return CrafterKeys::keypad_8;
case XKB_KEY_KP_9: return CrafterKeys::keypad_9;
case XKB_KEY_KP_Enter: return CrafterKeys::keypad_enter;
case XKB_KEY_KP_Add: return CrafterKeys::keypad_plus;
case XKB_KEY_KP_Subtract: return CrafterKeys::keypad_minus;
case XKB_KEY_KP_Multiply: return CrafterKeys::keypad_multiply;
case XKB_KEY_KP_Divide: return CrafterKeys::keypad_divide;
case XKB_KEY_KP_Decimal: return CrafterKeys::keypad_decimal;
// Punctuation
case XKB_KEY_grave: return CrafterKeys::grave;
case XKB_KEY_minus: return CrafterKeys::minus;
case XKB_KEY_equal: return CrafterKeys::equal;
case XKB_KEY_bracketleft: return CrafterKeys::bracket_left;
case XKB_KEY_bracketright:return CrafterKeys::bracket_right;
case XKB_KEY_backslash: return CrafterKeys::backslash;
case XKB_KEY_semicolon: return CrafterKeys::semicolon;
case XKB_KEY_apostrophe: return CrafterKeys::quote;
case XKB_KEY_comma: return CrafterKeys::comma;
case XKB_KEY_period: return CrafterKeys::period;
case XKB_KEY_slash: return CrafterKeys::slash;
default:
return CrafterKeys::CrafterKeysMax;
}
}
void WindowWayland::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) {
WindowWayland* window = reinterpret_cast<WindowWayland*>(data);
@ -566,33 +718,22 @@ void WindowWayland::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t ser
xkb_keycode_t keycode = key + 8;
xkb_keysym_t keysym = xkb_state_key_get_one_sym(window->xkb_state, keycode);
CrafterKeys crafterKey = keysym_to_crafter_key(keysym);
char utf8[8] = {0};
int len = xkb_keysym_to_utf8(keysym, utf8, sizeof(utf8));
if (len != 0) {
char keypress = utf8[0];
if(state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if(window->heldkeys[keypress]) {
window->onKeyHold[keypress].Invoke();
window->onAnyKeyHold.Invoke(keypress);
} else{
window->heldkeys[keypress] = true;
window->onKeyDown[keypress].Invoke();
window->onAnyKeyDown.Invoke(keypress);
}
} else{
window->heldkeys[keypress] = false;
window->onKeyUp[keypress].Invoke();
window->onAnyKeyUp.Invoke(keypress);
}
} else {
// // fallback for keys like Return, Escape, etc.
// char name[64];
// if (xkb_keysym_get_name(keysym, name, sizeof(name)) > 0) {
// printf("Key %s pressed (non-printable or multi-char)\n", name);
// }
}
if(state == WL_KEYBOARD_KEY_STATE_PRESSED) {
if(window->heldkeys[static_cast<std::uint8_t>(crafterKey)]) {
window->onKeyHold[static_cast<std::uint8_t>(crafterKey)].Invoke();
window->onAnyKeyHold.Invoke(crafterKey);
} else{
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = true;
window->onKeyDown[static_cast<std::uint8_t>(crafterKey)].Invoke();
window->onAnyKeyDown.Invoke(crafterKey);
}
} else{
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = false;
window->onKeyUp[static_cast<std::uint8_t>(crafterKey)].Invoke();
window->onAnyKeyUp.Invoke(crafterKey);
}
}
void WindowWayland::keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) {

View file

@ -31,18 +31,8 @@ export namespace Crafter {
std::int_fast32_t spacingY;
public:
GridElement(std::uint_fast32_t columns = 1, std::uint_fast32_t rows = 1,
std::int_fast32_t spacingX = 0, std::int_fast32_t spacingY = 0,
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);
void SetGridSize(std::uint_fast32_t columns, std::uint_fast32_t rows);
void SetSpacing(std::int_fast32_t spacingX, std::int_fast32_t spacingY);
GridElement(std::uint_fast32_t columns, std::uint_fast32_t rows, std::int_fast32_t spacingX, std::int_fast32_t spacingY, Anchor anchor);
void UpdatePositionScaled(Window& window);
void UpdatePosition(Window& window) override;
void UpdatePosition(Window& window, Transform& parent) override;
};

View file

@ -1,33 +0,0 @@
/*
Crafter®.Graphics
Copyright (C) 2025 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
*/
export module Crafter.Graphics:ImageElement;
import std;
import :RenderingElement;
import :Types;
export namespace Crafter {
class ImageElement : public RenderingElementScaling {
public:
ImageElement();
ImageElement(const std::string_view imagePath, 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);
void RenderImage(const std::string_view path);
};
}

View file

@ -38,10 +38,8 @@ export namespace Crafter {
Event<MousePoint> onMouseRightRelease;
Event<MousePoint> onMouseLeftRelease;
MouseElement(WindowMouse& window, 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);
MouseElement(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);
MouseElement(MouseElement&) = delete;
MouseElement& operator=(MouseElement&) = delete;
MouseElement(Anchor anchor);
MouseElement(Anchor anchor, WindowMouse& window);
void UpdatePosition(Window& window) override;
void UpdatePosition(Window& window, Transform& parent) override;
};

View file

@ -17,10 +17,14 @@ 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.Graphics:RenderingElement;
import std;
import :Transform;
import :Types;
import :Window;
export namespace Crafter {
enum class OpaqueType {
@ -29,66 +33,355 @@ export namespace Crafter {
Transparent // Color blending is used
};
class Window;
class RenderingElement : public Transform {
public:
std::vector<Pixel_BU8_GU8_RU8_AU8> bufferScaled;
OpaqueType opaque = OpaqueType::Transparent;
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);
RenderingElement(RenderingElement&) = delete;
RenderingElement& operator=(RenderingElement&) = delete;
};
class RenderingElementPreScaled : public RenderingElement {
public:
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);
RenderingElementPreScaled(RenderingElementPreScaled&) = delete;
RenderingElementPreScaled& operator=(RenderingElementPreScaled&) = delete;
void CopyNearestNeighbour(Pixel_BU8_GU8_RU8_AU8* dst, std::uint_fast32_t dstWidth, std::uint_fast32_t dstHeight) const;
void UpdatePosition(Window& window) override;
void UpdatePosition(Window& window, Transform& parent) override;
};
class RenderingElementScaling: public RenderingElement {
public:
std::vector<Pixel_BU8_GU8_RU8_AU8> buffer;
struct RenderElementScalingOwning {
std::vector<Pixel_BU8_GU8_RU8_AU8> scalingBuffer;
std::uint_fast32_t bufferWidth;
std::uint_fast32_t bufferHeight;
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);
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);
RenderingElementScaling(RenderingElementScaling&) = delete;
RenderingElementScaling& operator=(RenderingElementScaling&) = delete;
void ResizeBuffer(std::uint_fast32_t width, std::uint_fast32_t height);
void CopyNearestNeighbour(Pixel_BU8_GU8_RU8_AU8* dst, std::uint_fast32_t dstWidth, std::uint_fast32_t dstHeight) const;
void UpdatePosition(Window& window) override;
void UpdatePosition(Window& window, Transform& parent) override;
bool bufferUpdated = true;
RenderElementScalingOwning() = default;
RenderElementScalingOwning(std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight) : scalingBuffer(bufferWidth*bufferHeight), bufferWidth(bufferWidth), bufferHeight(bufferHeight) {
}
};
class RenderingElementScalingRotating2D : public RenderingElement {
public:
std::vector<Pixel_BU8_GU8_RU8_AU8> buffer;
struct RenderElementScalingNonOwning {
Pixel_BU8_GU8_RU8_AU8* scalingBuffer;
std::uint_fast32_t bufferWidth;
std::uint_fast32_t bufferHeight;
bool bufferUpdated = true;
RenderElementScalingNonOwning() = default;
RenderElementScalingNonOwning(Pixel_BU8_GU8_RU8_AU8* scalingBuffer, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) {
}
};
struct RenderElementRotating {
std::uint_fast32_t rotation;
bool rotationUpdated = true;
RenderElementRotating() = default;
RenderElementRotating(std::uint_fast32_t rotation) : rotation(rotation) {
}
};
struct EmptyScalingBase {};
struct EmptyRotatingBase {};
template<bool Scaling, bool Owning>
using ScalingBase =
std::conditional_t<
Scaling,
std::conditional_t<Owning,
RenderElementScalingOwning,
RenderElementScalingNonOwning>,
EmptyScalingBase
>;
template<bool Rotating>
using RotatingBase =
std::conditional_t<
Rotating,
RenderElementRotating,
EmptyRotatingBase
>;
class RenderingElementBase : public Transform {
public:
std::vector<Pixel_BU8_GU8_RU8_AU8> buffer;
OpaqueType opaque;
RenderingElementBase(Anchor anchor, OpaqueType opaque) : Transform(anchor), opaque(opaque) {
scaled.width = 0;
}
};
template<bool Scaling, bool Owning, bool Rotating> requires ((!Rotating || Scaling) && (!Owning || Scaling))
class RenderingElement : public RenderingElementBase, public ScalingBase<Scaling, Owning>, public RotatingBase<Rotating> {
public:
RenderingElement() = default;
RenderingElement(Anchor anchor, OpaqueType opaque) : RenderingElementBase(anchor, opaque) {
}
RenderingElement(Anchor anchor, OpaqueType opaque, std::uint_fast32_t rotation) requires(Rotating) : RenderingElementBase(anchor, opaque), RotatingBase<Rotating>(rotation) {
}
RenderingElement(Anchor anchor, const std::string_view imagePath) : Transform(anchor) {
LoadImage(imagePath);
}
RenderingElement(Anchor anchor, const std::string_view imagePath, std::uint_fast32_t rotation) requires(Rotating) : Transform(anchor), RotatingBase<Rotating>(rotation) {
LoadImage(imagePath);
}
RenderingElement(Anchor anchor, const std::string_view imagePath, OpaqueType opaque) : RenderingElementBase(anchor, opaque) {
LoadImageNoOpaqueCheck(imagePath);
}
RenderingElement(Anchor anchor, const std::string_view imagePath, OpaqueType opaque, std::uint_fast32_t rotation) requires(Rotating) : RenderingElementBase(anchor, opaque), RotatingBase<Rotating>(rotation) {
LoadImageNoOpaqueCheck(imagePath);
}
RenderingElement(Anchor anchor, OpaqueType opaque, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight, Pixel_BU8_GU8_RU8_AU8* scalingBuffer) requires(Scaling && !Owning) : RenderingElementBase(anchor, opaque), ScalingBase<Scaling, Owning>(bufferWidth, bufferHeight, scalingBuffer) {
}
RenderingElement(Anchor anchor, OpaqueType opaque, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight, Pixel_BU8_GU8_RU8_AU8* scalingBuffer, std::uint_fast32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElementBase(anchor, opaque), ScalingBase<Scaling, Owning>(bufferWidth, bufferHeight, scalingBuffer), RotatingBase<Rotating>(rotation) {
}
RenderingElement(Anchor anchor, OpaqueType opaque, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight) requires(Owning) : RenderingElementBase(anchor, opaque), ScalingBase<Scaling, Owning>(bufferWidth, bufferHeight) {
}
RenderingElement(Anchor anchor, OpaqueType opaque, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight, std::uint_fast32_t rotation) requires(Owning && Rotating) : RenderingElementBase(anchor, opaque), ScalingBase<Scaling, Owning>(bufferWidth, bufferHeight) , RotatingBase<Rotating>(rotation) {
}
RenderingElement(RenderingElement&) = delete;
RenderingElement& operator=(RenderingElement&) = delete;
void ScaleNearestNeighbor() requires(Scaling) {
for (std::uint_fast32_t y = 0; y < scaled.height; y++) {
std::uint_fast32_t srcY = y * ScalingBase<true, Owning>::bufferHeight / scaled.height;
for (std::uint_fast32_t x = 0; x < scaled.width; x++) {
std::uint_fast32_t srcX = x * ScalingBase<true, Owning>::bufferWidth / scaled.width;
buffer[y * scaled.width + x] = ScalingBase<true, Owning>::scalingBuffer[srcY * ScalingBase<true, Owning>::bufferWidth + srcX];
}
}
}
void ScaleRotating() requires(Scaling) {
const double rad = (static_cast<double>(RotatingBase<true>::rotation) / static_cast<double>(std::numeric_limits<std::uint_fast32_t>::max())) * 2.0 * std::numbers::pi;
const std::uint_fast32_t dstWidth = scaled.width;
const std::uint_fast32_t dstHeight = scaled.height;
const double c2 = std::abs(std::cos(rad));
const double s2 = std::abs(std::sin(rad));
const double rotatedWidth = dstWidth * c2 + dstHeight * s2;
const double rotatedHeight = dstWidth * s2 + dstHeight * c2;
const std::uint_fast32_t diffX = static_cast<std::uint_fast32_t>(std::ceil((rotatedWidth - dstWidth) * 0.5));
const std::uint_fast32_t diffY = static_cast<std::uint_fast32_t>(std::ceil((rotatedHeight - dstHeight) * 0.5));
scaled.width += diffX + diffX;
scaled.height += diffY + diffY;
scaled.x -= diffX;
scaled.y -= diffY;
ScalingBase<true, Owning>::scalingBuffer.clear();
ScalingBase<true, Owning>::scalingBuffer.resize(scaled.width * scaled.height);
// Destination center
const double dstCx = (static_cast<double>(scaled.width) - 1.0) * 0.5;
const double dstCy = (static_cast<double>(scaled.height) - 1.0) * 0.5;
// Source center
const double srcCx = (static_cast<double>(ScalingBase<true, Owning>::bufferWidth) - 1.0) * 0.5;
const double srcCy = (static_cast<double>(ScalingBase<true, Owning>::bufferHeight) - 1.0) * 0.5;
const double c = std::cos(rad);
const double s = std::sin(rad);
// Scale factors (destination → source)
const double scaleX = static_cast<double>(ScalingBase<true, Owning>::bufferWidth) / dstWidth;
const double scaleY = static_cast<double>(ScalingBase<true, Owning>::bufferHeight) / dstHeight;
for (std::uint_fast32_t yB = 0; yB < scaled.height; ++yB) {
for (std::uint_fast32_t xB = 0; xB < scaled.width; ++xB) {
// ---- Destination pixel relative to center ----
const double dx = (static_cast<double>(xB) - dstCx) * scaleX;
const double dy = (static_cast<double>(yB) - dstCy) * scaleY;
// ---- Inverse rotation ----
const double sx = (c * dx - s * dy) + srcCx;
const double sy = (s * dx + c * dy) + srcCy;
// ---- Nearest neighbour sampling ----
const std::int_fast32_t srcX = static_cast<std::int_fast32_t>(std::round(sx));
const std::int_fast32_t srcY = static_cast<std::int_fast32_t>(std::round(sy));
if (srcX >= 0 && srcX < ScalingBase<true, Owning>::bufferWidth && srcY >= 0 && srcY < ScalingBase<true, Owning>::bufferHeight) {
buffer[yB * scaled.width + xB] = ScalingBase<true, Owning>::scalingBuffer[srcY * ScalingBase<true, Owning>::bufferWidth + srcX];
}
}
}
}
RenderingElementScalingRotating2D(OpaqueType opaque = OpaqueType::Transparent);
RenderingElementScalingRotating2D(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);
RenderingElementScalingRotating2D(OpaqueType opaque, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight, std::uint_fast32_t rotationAngle = 0, 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);
RenderingElementScalingRotating2D(RenderingElementScalingRotating2D&) = delete;
RenderingElementScalingRotating2D& operator=(RenderingElementScalingRotating2D&) = delete;
void UpdateRotation(std::uint_fast32_t newRotation);
void ResizeBuffer(std::uint_fast32_t width, std::uint_fast32_t height);
void UpdateScaledBuffer(ScaleData& data);
void UpdatePosition(Window& window) override;
void UpdatePosition(Window& window, Transform& parent) override;
void UpdatePosition(Window& window, ScaleData oldScale) {
if constexpr(Scaling && !Rotating) {
if(oldScale.width != scaled.width || oldScale.height != scaled.height) {
buffer.resize(scaled.width * scaled.height);
ScaleNearestNeighbor();
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
} else if(oldScale.x != scaled.x || oldScale.y != scaled.y) {
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
if(ScalingBase<true, Owning>::bufferUpdated) {
ScaleNearestNeighbor();
ScalingBase<true, Owning>::bufferUpdated = false;
}
} else if(ScalingBase<true, Owning>::bufferUpdated) {
ScaleNearestNeighbor();
ScalingBase<true, Owning>::bufferUpdated = false;
}
} else if constexpr(Rotating) {
if(oldScale.width != scaled.width || oldScale.height != scaled.height) {
buffer.resize(scaled.width * scaled.height);
ScaleNearestNeighbor();
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
} else if(oldScale.x != scaled.x || oldScale.y != scaled.y) {
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
if(ScalingBase<true, Owning>::bufferUpdated) {
ScaleRotating();
ScalingBase<true, Owning>::bufferUpdated = false;
RotatingBase<true>::rotationUpdated = false;
}
} else if(ScalingBase<true, Owning>::bufferUpdated || RotatingBase<true>::rotationUpdated) {
ScaleRotating();
ScalingBase<true, Owning>::bufferUpdated = false;
RotatingBase<true>::rotationUpdated = false;
}
} else {
if(oldScale.width != scaled.width || oldScale.height != scaled.height) {
buffer.resize(scaled.width * scaled.height);
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
}
if(oldScale.x != scaled.x || oldScale.y != scaled.y) {
window.AddDirtyRect(oldScale);
window.AddDirtyRect(scaled);
}
}
}
void UpdatePosition(Window& window) override {
ScaleData oldScale = scaled;
window.ScaleElement(*this);
UpdatePosition(window, oldScale);
for(Transform* child : children) {
child->UpdatePosition(window, *this);
}
}
void UpdatePosition(Window& window, Transform& parent) override {
ScaleData oldScale = scaled;
window.ScaleElement(*this, parent);
UpdatePosition(window, oldScale);
for(Transform* child : children) {
child->UpdatePosition(window, *this);
}
}
void LoadImage(const std::string_view imagePath) {
std::filesystem::path abs = std::filesystem::absolute(imagePath);
int xSize;
int ySize;
unsigned char* bgData = stbi_load(abs.string().c_str(), &xSize, &ySize, nullptr, 4);
if constexpr(Scaling && !Owning) {
ScalingBase<true, false>::bufferUpdated = true;
} else if constexpr(Scaling && Owning) {
ScalingBase<true, true>::bufferWidth = xSize;
ScalingBase<true, true>::bufferHeight = ySize;
ScalingBase<true, true>::bufferUpdated = true;
} else {
buffer.resize(xSize*ySize);
}
opaque = OpaqueType::FullyOpaque;
if constexpr(Scaling) {
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;
ScalingBase<true, Owning>::scalingBuffer[x*ySize+y].r = bgData[idx];
ScalingBase<true, Owning>::scalingBuffer[x*ySize+y].g = bgData[idx+1];
ScalingBase<true, Owning>::scalingBuffer[x*ySize+y].b = bgData[idx+2];
ScalingBase<true, Owning>::calingBuffer[x*ySize+y].a = bgData[idx+3];
}
}
for(std::uint_fast32_t i = 0; i < xSize*ySize; i++) {
if(ScalingBase<true, Owning>::scalingBuffer[i].a != 255) {
opaque = OpaqueType::SemiOpaque;
for(std::uint_fast32_t i2 = 0; i2 < xSize*ySize; i2++) {
if(ScalingBase<true, Owning>::scalingBuffer[i2].a != 0 && ScalingBase<true, Owning>::scalingBuffer[i2].a != 255) {
opaque = OpaqueType::Transparent;
return;
}
}
return;
}
}
} else {
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;
buffer[x*ySize+y].r = bgData[idx];
buffer[x*ySize+y].g = bgData[idx+1];
buffer[x*ySize+y].b = bgData[idx+2];
buffer[x*ySize+y].a = bgData[idx+3];
}
}
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;
}
}
}
}
void LoadImageNoOpaqueCheck(const std::string_view imagePath) {
std::filesystem::path abs = std::filesystem::absolute(imagePath);
int xSize;
int ySize;
unsigned char* bgData = stbi_load(abs.string().c_str(), &xSize, &ySize, nullptr, 4);
if constexpr(Scaling && !Owning) {
ScalingBase<true, false>::bufferUpdated = true;
} else if constexpr(Scaling && Owning) {
ScalingBase<true, true>::bufferWidth = xSize;
ScalingBase<true, true>::bufferHeight = ySize;
ScalingBase<true, true>::bufferUpdated = true;
} else {
buffer.resize(xSize*ySize);
}
if constexpr(Scaling) {
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;
ScalingBase<true, Owning>::scalingBuffer[x*ySize+y].r = bgData[idx];
ScalingBase<true, Owning>::scalingBuffer[x*ySize+y].g = bgData[idx+1];
ScalingBase<true, Owning>::scalingBuffer[x*ySize+y].b = bgData[idx+2];
ScalingBase<true, Owning>::scalingBuffer[x*ySize+y].a = bgData[idx+3];
}
}
} else {
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;
buffer[x*ySize+y].r = bgData[idx];
buffer[x*ySize+y].g = bgData[idx+1];
buffer[x*ySize+y].b = bgData[idx+2];
buffer[x*ySize+y].a = bgData[idx+3];
}
}
}
}
};
}

View file

@ -41,11 +41,11 @@ export namespace Crafter {
Wrap // Wrap text to multiple lines
};
class TextElement : public RenderingElementPreScaled {
class TextElement : public RenderingElement<false, false, false> {
private:
void RenderWrappedLine(const std::string_view line, float scale, int baseline, std::uint_fast32_t startY, Pixel_BU8_GU8_RU8_AU8 color, Font& font, TextAlignment alignment);
public:
TextElement(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);
TextElement(Anchor anchor);
void RenderText(Window& window, const std::string_view text, float size, Pixel_BU8_GU8_RU8_AU8 pixel, Font& font, TextAlignment alignment = TextAlignment::Left, VerticalTextAlignment verticalAlignment = VerticalTextAlignment::Top, TextOverflowMode overflowMode = TextOverflowMode::Clip);
};
}

View file

@ -23,19 +23,25 @@ import :Types;
export namespace Crafter {
class Window;
struct Anchor {
std::int_fast32_t x;
std::int_fast32_t y;
std::uint_fast32_t width;
std::uint_fast32_t height;
std::int_fast32_t offsetX;
std::int_fast32_t offsetY;
std::int_fast32_t z;
Anchor() = default;
Anchor(std::int_fast32_t x, std::int_fast32_t y, std::uint_fast32_t width, std::uint_fast32_t height, std::int_fast32_t offsetX, std::int_fast32_t offsetY, std::int_fast32_t z);
};
class Transform {
public:
std::int_fast32_t z;
std::int_fast32_t anchorX;
std::int_fast32_t anchorY;
std::int_fast32_t anchorOffsetX;
std::int_fast32_t anchorOffsetY;
std::uint_fast32_t relativeWidth;
std::uint_fast32_t relativeHeight;
Anchor anchor;
ScaleData scaled;
std::vector<Transform*> children;
Transform(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);
Transform(Anchor anchor);
Transform(Transform&) = delete;
Transform(Transform&&) = delete;
Transform& operator=(Transform&) = delete;
virtual ~Transform() = default;
virtual void UpdatePosition(Window& window);

View file

@ -139,10 +139,20 @@ namespace Crafter {
return pixel * (SCALE / width);
}
export constexpr std::int_fast32_t RelativeToAbsolute(std::int_fast32_t relative, std::int_fast32_t full) {
return static_cast<std::int_fast32_t>(
(static_cast<__int128>(relative) * full) / SCALE
);
}
export constexpr std::int_fast32_t BoundToBoundless(std::int_fast32_t bound) {
return bound * BOUND;
}
export constexpr std::int_fast32_t BoundlessToBound(std::int_fast32_t bound) {
return bound / BOUND;
}
export constexpr std::int_fast32_t FractionalToMappedBoundless(double f) {
return std::int_fast32_t(f * SCALEDOUBLEBOUNDLESS);
}
@ -162,4 +172,47 @@ namespace Crafter {
export constexpr std::int_fast32_t PixelToMappedBoundless(std::int_fast32_t pixel, std::int_fast32_t width) {
return pixel * (SCALEBOUNDLESS / width);
}
export enum class CrafterKeys {
// Alphabetic keys
A, B, C, D, E, F, G, H, I, J, K, L, M,
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
// Numeric keys
_0, _1, _2, _3, _4, _5, _6, _7, _8, _9,
// Function keys
F1, F2, F3, F4, F5, F6, F7, F8, F9, F10, F11, F12,
// Control keys
Escape, Tab, Enter, Space, Backspace, Delete, Insert,
Home, End, PageUp, PageDown, CapsLock, NumLock, ScrollLock,
// Modifier keys
LeftShift, RightShift, LeftCtrl, RightCtrl,
LeftAlt, RightAlt, LeftSuper, RightSuper,
// Arrow keys
Up, Down, Left, Right,
// Keypad keys
keypad_0, keypad_1, keypad_2, keypad_3, keypad_4,
keypad_5, keypad_6, keypad_7, keypad_8, keypad_9,
keypad_enter, keypad_plus, keypad_minus, keypad_multiply,
keypad_divide, keypad_decimal,
// Punctuation and special keys
grave, minus, equal, bracket_left, bracket_right,
backslash, semicolon, quote, comma, period, slash,
print_screen, pause, menu,
// Additional keys
volume_up, volume_down, volume_mute,
media_play, media_stop, media_prev, media_next,
browser_back, browser_forward, browser_refresh,
browser_stop, browser_search, browser_home,
launch_mail, launch_calculator, launch_media_player,
CrafterKeysMax
};
}

View file

@ -45,7 +45,6 @@ import Crafter.Event;
export namespace Crafter {
class Transform;
class RenderingElement;
class Window {
public:
std::int_fast32_t width;
@ -76,7 +75,7 @@ export namespace Crafter {
std::chrono::nanoseconds totalUpdate;
std::vector<std::pair<const EventListener<FrameTime>*, std::chrono::nanoseconds>> updateTimings;
std::chrono::nanoseconds totalRender;
std::vector<std::tuple<const RenderingElement*, std::uint_fast32_t, std::uint_fast32_t, std::chrono::nanoseconds>> renderTimings;
std::vector<std::tuple<const Transform*, std::uint_fast32_t, std::uint_fast32_t, std::chrono::nanoseconds>> renderTimings;
std::chrono::nanoseconds vblank;
std::chrono::nanoseconds totalFrame;
std::chrono::time_point<std::chrono::high_resolution_clock> frameEnd;
@ -87,13 +86,13 @@ export namespace Crafter {
class WindowKeyboard {
public:
bool heldkeys[255] = {};
Event<void> onKeyDown[255];
Event<void> onKeyHold[255];
Event<void> onKeyUp[255];
Event<char> onAnyKeyDown;
Event<char> onAnyKeyHold;
Event<char> onAnyKeyUp;
bool heldkeys[static_cast<std::uint32_t>(CrafterKeys::CrafterKeysMax)] = {};
Event<void> onKeyDown[static_cast<std::uint32_t>(CrafterKeys::CrafterKeysMax)];
Event<void> onKeyHold[static_cast<std::uint32_t>(CrafterKeys::CrafterKeysMax)];
Event<void> onKeyUp[static_cast<std::uint32_t>(CrafterKeys::CrafterKeysMax)];
Event<CrafterKeys> onAnyKeyDown;
Event<CrafterKeys> onAnyKeyHold;
Event<CrafterKeys> onAnyKeyUp;
};
class MouseElement;
@ -138,7 +137,6 @@ export namespace Crafter {
};
#ifdef CRAFTER_GRAPHICS_WAYLAND
class RenderingElement;
class WindowWayland final : public WindowKeyboard, public WindowMouse, public WindowFramebuffer, public WindowTitle {
public:
Pixel_BU8_GU8_RU8_AU8* framebuffer = nullptr;
@ -162,6 +160,7 @@ export namespace Crafter {
xkb_state* xkb_state;
void RenderElement(Transform* transform);
void Render() override;
void QueueRender();
void StartSync() override;
void StartUpdate() override;
void StopUpdate() override;

View file

@ -24,7 +24,6 @@ export import :Window;
export import :Transform;
export import :RenderingElement;
export import :MouseElement;
export import :ImageElement;
export import :TextElement;
export import :GridElement;
export import :Types;

View file

@ -3,8 +3,8 @@
"configurations": [
{
"name": "base",
"implementations": ["implementations/Crafter.Graphics-Font", "implementations/Crafter.Graphics-Shm", "implementations/Crafter.Graphics-Window", "implementations/Crafter.Graphics-RenderingElement", "implementations/Crafter.Graphics-RenderingElementScalingRotating2D", "implementations/Crafter.Graphics-TextElement", "implementations/Crafter.Graphics-ImageElement", "implementations/Crafter.Graphics-MouseElement", "implementations/Crafter.Graphics-Transform", "implementations/Crafter.Graphics-GridElement"],
"interfaces": ["interfaces/Crafter.Graphics-Window", "interfaces/Crafter.Graphics", "interfaces/Crafter.Graphics-Types", "interfaces/Crafter.Graphics-Font", "interfaces/Crafter.Graphics-Shm", "interfaces/Crafter.Graphics-Animation", "interfaces/Crafter.Graphics-RenderingElement", "interfaces/Crafter.Graphics-TextElement", "interfaces/Crafter.Graphics-ImageElement", "interfaces/Crafter.Graphics-MouseElement", "interfaces/Crafter.Graphics-Transform", "interfaces/Crafter.Graphics-GridElement"],
"implementations": ["implementations/Crafter.Graphics-Font", "implementations/Crafter.Graphics-Shm", "implementations/Crafter.Graphics-Window", "implementations/Crafter.Graphics-TextElement", "implementations/Crafter.Graphics-MouseElement", "implementations/Crafter.Graphics-Transform", "implementations/Crafter.Graphics-GridElement"],
"interfaces": ["interfaces/Crafter.Graphics-Window", "interfaces/Crafter.Graphics", "interfaces/Crafter.Graphics-Types", "interfaces/Crafter.Graphics-Font", "interfaces/Crafter.Graphics-Shm", "interfaces/Crafter.Graphics-Animation", "interfaces/Crafter.Graphics-RenderingElement", "interfaces/Crafter.Graphics-TextElement", "interfaces/Crafter.Graphics-MouseElement", "interfaces/Crafter.Graphics-Transform", "interfaces/Crafter.Graphics-GridElement"],
"type": "library"
},
{