Crafter.Graphics/implementations/Crafter.Graphics-Window.cpp
2025-11-26 20:41:54 +01:00

120 lines
No EOL
6.4 KiB
C++

/*
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
*/
module Crafter.Graphics:Window_impl;
import :Window;
import :Transform;
import std;
using namespace Crafter;
Window::Window(std::int_fast32_t width, std::int_fast32_t height) : width(width), height(height) {
}
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);
}
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;
}
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);
}
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);
}
#ifdef CRAFTER_TIMING
void Window::LogTiming() {
std::cout << std::format("Update: {}", duration_cast<std::chrono::milliseconds>(totalUpdate)) << std::endl;
for (const std::pair<const EventListener<FrameTime>*, std::chrono::nanoseconds>& entry : updateTimings) {
std::cout << std::format("\t{} {}", reinterpret_cast<const void*>(entry.first), duration_cast<std::chrono::microseconds>(entry.second)) << std::endl;
}
std::cout << std::format("Render: {}", duration_cast<std::chrono::milliseconds>(totalRender)) << std::endl;
for (const std::tuple<const RenderingElement*, std::uint_fast32_t, std::uint_fast32_t, std::chrono::nanoseconds>& entry : renderTimings) {
std::cout << std::format("\t{} {}x{} {}", reinterpret_cast<const void*>(std::get<0>(entry)), std::get<1>(entry), std::get<2>(entry), duration_cast<std::chrono::microseconds>(std::get<3>(entry))) << std::endl;
}
std::cout << std::format("Total: {}", duration_cast<std::chrono::milliseconds>(totalUpdate+totalRender)) << std::endl;
std::cout << std::format("Vblank: {}", duration_cast<std::chrono::milliseconds>(vblank)) << std::endl;
// Add 100-frame average and min-max timing info
if (!frameTimes.empty()) {
// Calculate average
std::chrono::nanoseconds sum(0);
for (const auto& frameTime : frameTimes) {
sum += frameTime;
}
auto average = sum / frameTimes.size();
// Find min and max
auto min = frameTimes.front();
auto max = frameTimes.front();
for (const auto& frameTime : frameTimes) {
if (frameTime < min) min = frameTime;
if (frameTime > max) max = frameTime;
}
std::cout << std::format("Last 100 Frame Times - Avg: {}, Min: {}, Max: {}",
duration_cast<std::chrono::milliseconds>(average),
duration_cast<std::chrono::milliseconds>(min),
duration_cast<std::chrono::milliseconds>(max)) << std::endl;
}
}
#endif
void Window::AddDirtyRect(ScaleData scale) {
ClipRect rect {
.left = std::max(scale.x, std::int_fast32_t(0)),
.right = std::min(scale.x + scale.width, width),
.top = std::max(scale.y, std::int_fast32_t(0)),
.bottom = std::min(scale.y + scale.height, height),
};
if (rect.left >= rect.right || rect.top >= rect.bottom) {
return;
}
//merging logic should work so that no pixel is drawn twice, and that no pixel not marked dirty is drawn.
//so lets say there is already an existing horizontal bar and the new rect is a vertical bar making a cross shape, the center of the cross will currently be drawn twice
//so we need to turn it into 3 rects, the top part of the vertical bar, the horizontal bar, and the bottom part of the vertical bar
//in this way no pixel is drawn twice and no area not marked dirty is included
dirtyRects.push_back(rect);
}