diff --git a/examples/HelloGrid/main.cpp b/examples/HelloGrid/main.cpp index 261cc1d..9f1f6eb 100644 --- a/examples/HelloGrid/main.cpp +++ b/examples/HelloGrid/main.cpp @@ -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 diff --git a/examples/HelloUI/main.cpp b/examples/HelloUI/main.cpp index 23b0ee5..300b71d 100644 --- a/examples/HelloUI/main.cpp +++ b/examples/HelloUI/main.cpp @@ -6,24 +6,34 @@ using namespace Crafter; int main() { WindowWayland window(1280, 720, "Hello Input!"); - RenderingElement element( + RenderingElement 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 clickListener(&mouse.onMouseLeftClick, [&mouse, &window](MousePoint point){ diff --git a/examples/HelloUI/project.json b/examples/HelloUI/project.json index 70e3d7a..976af27 100644 --- a/examples/HelloUI/project.json +++ b/examples/HelloUI/project.json @@ -7,9 +7,10 @@ "dependencies": [ { "path":"../../project.json", - "configuration":"lib-wayland" + "configuration":"lib-wayland-debug" } - ] + ], + "debug": true } ] } diff --git a/implementations/Crafter.Graphics-GridElement.cpp b/implementations/Crafter.Graphics-GridElement.cpp index 2e358f4..f789344 100644 --- a/implementations/Crafter.Graphics-GridElement.cpp +++ b/implementations/Crafter.Graphics-GridElement.cpp @@ -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); } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-ImageElement.cpp b/implementations/Crafter.Graphics-ImageElement.cpp deleted file mode 100644 index 21c0a32..0000000 --- a/implementations/Crafter.Graphics-ImageElement.cpp +++ /dev/null @@ -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; - } - } -} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-MouseElement.cpp b/implementations/Crafter.Graphics-MouseElement.cpp index 5cfc642..529371c 100644 --- a/implementations/Crafter.Graphics-MouseElement.cpp +++ b/implementations/Crafter.Graphics-MouseElement.cpp @@ -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) { diff --git a/implementations/Crafter.Graphics-RenderingElement.cpp b/implementations/Crafter.Graphics-RenderingElement.cpp deleted file mode 100644 index c7c5f6b..0000000 --- a/implementations/Crafter.Graphics-RenderingElement.cpp +++ /dev/null @@ -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); -} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-RenderingElementScalingRotating2D.cpp b/implementations/Crafter.Graphics-RenderingElementScalingRotating2D.cpp deleted file mode 100644 index 5611f94..0000000 --- a/implementations/Crafter.Graphics-RenderingElementScalingRotating2D.cpp +++ /dev/null @@ -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(rotation) / static_cast(std::numeric_limits::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(scaled.width) - 1.0) * 0.5; - const double dstCy = (static_cast(scaled.height) - 1.0) * 0.5; - - // Source center - const double srcCx = (static_cast(bufferWidth) - 1.0) * 0.5; - const double srcCy = (static_cast(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(bufferWidth) / dstWidth; - const double scaleY = static_cast(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(xB) - dstCx) * scaleX; - const double dy = (static_cast(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::round(sx)); - const std::int_fast32_t srcY = static_cast(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(rotation) / static_cast(std::numeric_limits::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::ceil(rotatedWidth)); - scaled.height = static_cast(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(rotation) / static_cast(std::numeric_limits::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::ceil(rotatedWidth)); - scaled.height = static_cast(std::ceil(rotatedHeight)); - - window.AddDirtyRect(oldScale); - window.AddDirtyRect(scaled); - } - - for(Transform* child : children) { - child->UpdatePosition(window, *this); - } -} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-TextElement.cpp b/implementations/Crafter.Graphics-TextElement.cpp index 1e00076..db360f8 100644 --- a/implementations/Crafter.Graphics-TextElement.cpp +++ b/implementations/Crafter.Graphics-TextElement.cpp @@ -30,7 +30,7 @@ import std; using namespace Crafter; -TextElement::TextElement(std::int_fast32_t anchorX, std::int_fast32_t anchorY, std::uint_fast32_t relativeWidth, std::uint_fast32_t relativeHeight, std::int_fast32_t anchorOffsetX, std::int_fast32_t anchorOffsetY, std::int_fast32_t z) : RenderingElementPreScaled(OpaqueType::Transparent, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z) { +TextElement::TextElement(Anchor anchor): RenderingElement(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]}; } } } diff --git a/implementations/Crafter.Graphics-Transform.cpp b/implementations/Crafter.Graphics-Transform.cpp index 359f223..c3d0dbb 100644 --- a/implementations/Crafter.Graphics-Transform.cpp +++ b/implementations/Crafter.Graphics-Transform.cpp @@ -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) { diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index d53570f..3d85d6d 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -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); diff --git a/implementations/Crafter.Graphics-Window_wayland.cpp b/implementations/Crafter.Graphics-Window_wayland.cpp index 88e59fd..3c2f96e 100644 --- a/implementations/Crafter.Graphics-Window_wayland.cpp +++ b/implementations/Crafter.Graphics-Window_wayland.cpp @@ -23,7 +23,6 @@ module; #include #include #include -#include #include "../lib/xdg-shell-client-protocol.h" #include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h" #include @@ -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(transform); + RenderingElementBase* element = dynamic_cast(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(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 newClip; + //std::vector 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 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(data); #ifdef CRAFTER_TIMING window->vblank = duration_cast(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(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(crafterKey)]) { + window->onKeyHold[static_cast(crafterKey)].Invoke(); + window->onAnyKeyHold.Invoke(crafterKey); + } else{ + window->heldkeys[static_cast(crafterKey)] = true; + window->onKeyDown[static_cast(crafterKey)].Invoke(); + window->onAnyKeyDown.Invoke(crafterKey); + } + } else{ + window->heldkeys[static_cast(crafterKey)] = false; + window->onKeyUp[static_cast(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) { diff --git a/interfaces/Crafter.Graphics-GridElement.cppm b/interfaces/Crafter.Graphics-GridElement.cppm index 656ece7..66ce144 100644 --- a/interfaces/Crafter.Graphics-GridElement.cppm +++ b/interfaces/Crafter.Graphics-GridElement.cppm @@ -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; }; diff --git a/interfaces/Crafter.Graphics-ImageElement.cppm b/interfaces/Crafter.Graphics-ImageElement.cppm deleted file mode 100644 index 6ce51d9..0000000 --- a/interfaces/Crafter.Graphics-ImageElement.cppm +++ /dev/null @@ -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); - }; -} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-MouseElement.cppm b/interfaces/Crafter.Graphics-MouseElement.cppm index ad9afec..d8c89d6 100644 --- a/interfaces/Crafter.Graphics-MouseElement.cppm +++ b/interfaces/Crafter.Graphics-MouseElement.cppm @@ -38,10 +38,8 @@ export namespace Crafter { Event onMouseRightRelease; Event 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; }; diff --git a/interfaces/Crafter.Graphics-RenderingElement.cppm b/interfaces/Crafter.Graphics-RenderingElement.cppm index 9f76045..591895b 100644 --- a/interfaces/Crafter.Graphics-RenderingElement.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement.cppm @@ -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 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 buffer; + struct RenderElementScalingOwning { + std::vector 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 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 + using ScalingBase = + std::conditional_t< + Scaling, + std::conditional_t, + EmptyScalingBase + >; + + template + using RotatingBase = + std::conditional_t< + Rotating, + RenderElementRotating, + EmptyRotatingBase + >; + + class RenderingElementBase : public Transform { + public: + std::vector buffer; + OpaqueType opaque; + RenderingElementBase(Anchor anchor, OpaqueType opaque) : Transform(anchor), opaque(opaque) { + scaled.width = 0; + } + }; + + template requires ((!Rotating || Scaling) && (!Owning || Scaling)) + class RenderingElement : public RenderingElementBase, public ScalingBase, public RotatingBase { + 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(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(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(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(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(bufferWidth, bufferHeight, scalingBuffer), RotatingBase(rotation) { + + } + + RenderingElement(Anchor anchor, OpaqueType opaque, std::uint_fast32_t bufferWidth, std::uint_fast32_t bufferHeight) requires(Owning) : RenderingElementBase(anchor, opaque), ScalingBase(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(bufferWidth, bufferHeight) , RotatingBase(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::bufferHeight / scaled.height; + for (std::uint_fast32_t x = 0; x < scaled.width; x++) { + std::uint_fast32_t srcX = x * ScalingBase::bufferWidth / scaled.width; + buffer[y * scaled.width + x] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; + } + } + } + + void ScaleRotating() requires(Scaling) { + const double rad = (static_cast(RotatingBase::rotation) / static_cast(std::numeric_limits::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::ceil((rotatedWidth - dstWidth) * 0.5)); + const std::uint_fast32_t diffY = static_cast(std::ceil((rotatedHeight - dstHeight) * 0.5)); + + scaled.width += diffX + diffX; + scaled.height += diffY + diffY; + + scaled.x -= diffX; + scaled.y -= diffY; + + ScalingBase::scalingBuffer.clear(); + ScalingBase::scalingBuffer.resize(scaled.width * scaled.height); + + // Destination center + const double dstCx = (static_cast(scaled.width) - 1.0) * 0.5; + const double dstCy = (static_cast(scaled.height) - 1.0) * 0.5; + + // Source center + const double srcCx = (static_cast(ScalingBase::bufferWidth) - 1.0) * 0.5; + const double srcCy = (static_cast(ScalingBase::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(ScalingBase::bufferWidth) / dstWidth; + const double scaleY = static_cast(ScalingBase::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(xB) - dstCx) * scaleX; + const double dy = (static_cast(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::round(sx)); + const std::int_fast32_t srcY = static_cast(std::round(sy)); + + if (srcX >= 0 && srcX < ScalingBase::bufferWidth && srcY >= 0 && srcY < ScalingBase::bufferHeight) { + buffer[yB * scaled.width + xB] = ScalingBase::scalingBuffer[srcY * ScalingBase::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::bufferUpdated) { + ScaleNearestNeighbor(); + ScalingBase::bufferUpdated = false; + } + } else if(ScalingBase::bufferUpdated) { + ScaleNearestNeighbor(); + ScalingBase::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::bufferUpdated) { + ScaleRotating(); + ScalingBase::bufferUpdated = false; + RotatingBase::rotationUpdated = false; + } + } else if(ScalingBase::bufferUpdated || RotatingBase::rotationUpdated) { + ScaleRotating(); + ScalingBase::bufferUpdated = false; + RotatingBase::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::bufferUpdated = true; + } else if constexpr(Scaling && Owning) { + ScalingBase::bufferWidth = xSize; + ScalingBase::bufferHeight = ySize; + ScalingBase::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::scalingBuffer[x*ySize+y].r = bgData[idx]; + ScalingBase::scalingBuffer[x*ySize+y].g = bgData[idx+1]; + ScalingBase::scalingBuffer[x*ySize+y].b = bgData[idx+2]; + ScalingBase::calingBuffer[x*ySize+y].a = bgData[idx+3]; + } + } + + for(std::uint_fast32_t i = 0; i < xSize*ySize; i++) { + if(ScalingBase::scalingBuffer[i].a != 255) { + opaque = OpaqueType::SemiOpaque; + for(std::uint_fast32_t i2 = 0; i2 < xSize*ySize; i2++) { + if(ScalingBase::scalingBuffer[i2].a != 0 && ScalingBase::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::bufferUpdated = true; + } else if constexpr(Scaling && Owning) { + ScalingBase::bufferWidth = xSize; + ScalingBase::bufferHeight = ySize; + ScalingBase::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::scalingBuffer[x*ySize+y].r = bgData[idx]; + ScalingBase::scalingBuffer[x*ySize+y].g = bgData[idx+1]; + ScalingBase::scalingBuffer[x*ySize+y].b = bgData[idx+2]; + ScalingBase::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]; + } + } + } + } }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-TextElement.cppm b/interfaces/Crafter.Graphics-TextElement.cppm index ccd543c..f77f298 100644 --- a/interfaces/Crafter.Graphics-TextElement.cppm +++ b/interfaces/Crafter.Graphics-TextElement.cppm @@ -41,11 +41,11 @@ export namespace Crafter { Wrap // Wrap text to multiple lines }; - class TextElement : public RenderingElementPreScaled { + class TextElement : public RenderingElement { 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); }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Transform.cppm b/interfaces/Crafter.Graphics-Transform.cppm index 34bfc38..6130eac 100644 --- a/interfaces/Crafter.Graphics-Transform.cppm +++ b/interfaces/Crafter.Graphics-Transform.cppm @@ -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 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); diff --git a/interfaces/Crafter.Graphics-Types.cppm b/interfaces/Crafter.Graphics-Types.cppm index 9b76a15..189c9ef 100644 --- a/interfaces/Crafter.Graphics-Types.cppm +++ b/interfaces/Crafter.Graphics-Types.cppm @@ -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( + (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 + }; } diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index 43a09c3..7e2ec51 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -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::chrono::nanoseconds>> updateTimings; std::chrono::nanoseconds totalRender; - std::vector> renderTimings; + std::vector> renderTimings; std::chrono::nanoseconds vblank; std::chrono::nanoseconds totalFrame; std::chrono::time_point frameEnd; @@ -87,13 +86,13 @@ export namespace Crafter { class WindowKeyboard { public: - bool heldkeys[255] = {}; - Event onKeyDown[255]; - Event onKeyHold[255]; - Event onKeyUp[255]; - Event onAnyKeyDown; - Event onAnyKeyHold; - Event onAnyKeyUp; + bool heldkeys[static_cast(CrafterKeys::CrafterKeysMax)] = {}; + Event onKeyDown[static_cast(CrafterKeys::CrafterKeysMax)]; + Event onKeyHold[static_cast(CrafterKeys::CrafterKeysMax)]; + Event onKeyUp[static_cast(CrafterKeys::CrafterKeysMax)]; + Event onAnyKeyDown; + Event onAnyKeyHold; + Event 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; diff --git a/interfaces/Crafter.Graphics.cppm b/interfaces/Crafter.Graphics.cppm index 9b64da2..6655f56 100644 --- a/interfaces/Crafter.Graphics.cppm +++ b/interfaces/Crafter.Graphics.cppm @@ -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; diff --git a/project.json b/project.json index ec72a99..70f90ff 100644 --- a/project.json +++ b/project.json @@ -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" }, {