2025-12-21 19:52:41 +01:00
|
|
|
/*
|
|
|
|
|
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, bool ignoreScaling) : RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling), rotation(0) {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-21 20:26:20 +01:00
|
|
|
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, bool ignoreScaling)
|
2025-12-21 19:52:41 +01:00
|
|
|
: bufferWidth(bufferWidth), bufferHeight(bufferHeight), buffer(bufferWidth*bufferHeight),
|
|
|
|
|
rotation(rotation),
|
|
|
|
|
RenderingElement(opaque, anchorX, anchorY, relativeWidth, relativeHeight, anchorOffsetX, anchorOffsetY, z, ignoreScaling) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2025-12-21 20:26:20 +01:00
|
|
|
const std::uint_fast32_t dstWidth = scaled.width;
|
|
|
|
|
const std::uint_fast32_t dstHeight = scaled.height;
|
2025-12-21 19:52:41 +01:00
|
|
|
|
2025-12-21 20:26:20 +01:00
|
|
|
const double rotatedWidth = scaled.width * (std::abs(std::cos(rad)) + std::abs(std::sin(rad)));
|
2025-12-21 19:52:41 +01:00
|
|
|
const double rotatedHeight = scaled.height * (std::abs(std::cos(rad)) + std::abs(std::sin(rad)));
|
|
|
|
|
|
2025-12-21 20:26:20 +01:00
|
|
|
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;
|
2025-12-21 19:52:41 +01:00
|
|
|
|
2025-12-21 20:26:20 +01:00
|
|
|
bufferScaled.clear();
|
2025-12-21 19:52:41 +01:00
|
|
|
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 ----
|
2025-12-21 20:26:20 +01:00
|
|
|
const double dx = (static_cast<double>(xB) - dstCx) * scaleX;
|
|
|
|
|
const double dy = (static_cast<double>(yB) - dstCy) * scaleY;
|
2025-12-21 19:52:41 +01:00
|
|
|
|
|
|
|
|
// ---- Inverse rotation ----
|
2025-12-21 20:26:20 +01:00
|
|
|
const double sx = (c * dx - s * dy) + srcCx;
|
|
|
|
|
const double sy = (s * dx + c * dy) + srcCy;
|
2025-12-21 19:52:41 +01:00
|
|
|
|
|
|
|
|
// ---- 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);
|
|
|
|
|
}
|
|
|
|
|
}
|