From 3df545e95ce69fb50817a2d87b4a73f6d10943f8 Mon Sep 17 00:00:00 2001 From: Johanness Date: Tue, 19 May 2026 00:37:12 +0300 Subject: [PATCH] Preserve 16:9 tile aspect on resize --- TileLayout.cpp | 123 ++++++++++++++++++++++++++++++++++++++++--------- TileLayout.hpp | 28 +++++++++-- main.cpp | 89 +++++++++++++---------------------- 3 files changed, 157 insertions(+), 83 deletions(-) diff --git a/TileLayout.cpp b/TileLayout.cpp index 0c9b0be..e53e7f2 100644 --- a/TileLayout.cpp +++ b/TileLayout.cpp @@ -21,46 +21,116 @@ std::vector makeQuad( {{x0, y1}, {0.0f, 1.0f}}, }; } + +float toNdcX(float x, float viewportWidth) +{ + return x * 2.0f / viewportWidth - 1.0f; } -std::vector makeGridLayout( +float toNdcY(float y, float viewportHeight) +{ + return y * 2.0f / viewportHeight - 1.0f; +} +} + +std::vector makeGridTileRects( uint32_t cols, uint32_t rows, - float gapX, - float gapY) + float viewportWidth, + float viewportHeight, + float gapPixels, + float targetAspect) { - std::vector vertices; - vertices.reserve( - static_cast(cols) * rows * 6); + std::vector rects; + rects.reserve(static_cast(cols) * rows); - const float tileW = - (2.0f - gapX * static_cast(cols - 1)) / + if (cols == 0 || rows == 0 || + viewportWidth <= 0.0f || viewportHeight <= 0.0f || + targetAspect <= 0.0f) + { + return rects; + } + + const float gapX = + std::min(gapPixels, viewportWidth * 0.1f); + const float gapY = + std::min(gapPixels, viewportHeight * 0.1f); + + const float cellW = + (viewportWidth - gapX * static_cast(cols - 1)) / static_cast(cols); - const float tileH = - (2.0f - gapY * static_cast(rows - 1)) / + const float cellH = + (viewportHeight - gapY * static_cast(rows - 1)) / static_cast(rows); + float tileW = cellW; + float tileH = tileW / targetAspect; + + if (tileH > cellH) + { + tileH = cellH; + tileW = tileH * targetAspect; + } + + const float insetX = (cellW - tileW) * 0.5f; + const float insetY = (cellH - tileH) * 0.5f; + for (uint32_t row = 0; row < rows; ++row) { for (uint32_t col = 0; col < cols; ++col) { const float x0 = - -1.0f + - static_cast(col) * (tileW + gapX); + static_cast(col) * (cellW + gapX) + + insetX; const float y0 = - -1.0f + - static_cast(row) * (tileH + gapY); + static_cast(row) * (cellH + gapY) + + insetY; const float x1 = x0 + tileW; const float y1 = y0 + tileH; - auto quad = makeQuad(x0, y0, x1, y1); - vertices.insert( - vertices.end(), - quad.begin(), - quad.end()); + rects.push_back({x0, y0, x1, y1}); } } + return rects; +} + +std::vector makeGridLayout( + uint32_t cols, + uint32_t rows, + float viewportWidth, + float viewportHeight, + float gapPixels, + float targetAspect) +{ + std::vector vertices; + vertices.reserve( + static_cast(cols) * rows * 6); + + std::vector rects = + makeGridTileRects( + cols, + rows, + viewportWidth, + viewportHeight, + gapPixels, + targetAspect + ); + + for (const TileRect& rect : rects) + { + auto quad = makeQuad( + toNdcX(rect.x0, viewportWidth), + toNdcY(rect.y0, viewportHeight), + toNdcX(rect.x1, viewportWidth), + toNdcY(rect.y1, viewportHeight) + ); + vertices.insert( + vertices.end(), + quad.begin(), + quad.end()); + } + return vertices; } @@ -68,11 +138,20 @@ void updateGridLayout( Vertex* vertices, uint32_t cols, uint32_t rows, - float gapX, - float gapY) + float viewportWidth, + float viewportHeight, + float gapPixels, + float targetAspect) { std::vector updated = - makeGridLayout(cols, rows, gapX, gapY); + makeGridLayout( + cols, + rows, + viewportWidth, + viewportHeight, + gapPixels, + targetAspect + ); std::copy( updated.begin(), diff --git a/TileLayout.hpp b/TileLayout.hpp index 8c10674..966053f 100644 --- a/TileLayout.hpp +++ b/TileLayout.hpp @@ -9,18 +9,38 @@ struct Vertex float uv[2]; }; +struct TileRect +{ + float x0; + float y0; + float x1; + float y1; +}; + +std::vector makeGridTileRects( + uint32_t cols, + uint32_t rows, + float viewportWidth, + float viewportHeight, + float gapPixels, + float targetAspect); + std::vector makeGridLayout( uint32_t cols, uint32_t rows, - float gapX, - float gapY); + float viewportWidth, + float viewportHeight, + float gapPixels, + float targetAspect); void updateGridLayout( Vertex* vertices, uint32_t cols, uint32_t rows, - float gapX, - float gapY); + float viewportWidth, + float viewportHeight, + float gapPixels, + float targetAspect); void updateUvForAspectRatio( Vertex* vertices, diff --git a/main.cpp b/main.cpp index 7568cb8..c0601fa 100644 --- a/main.cpp +++ b/main.cpp @@ -37,6 +37,7 @@ constexpr uint32_t TEXTURE_WIDTH = 512; constexpr uint32_t TEXTURE_HEIGHT = 288; constexpr uint32_t VERTICES_PER_QUAD = 6; constexpr float TILE_GAP_PIXELS = 8.0f; +constexpr float TILE_ASPECT = 16.0f / 9.0f; static std::atomic g_running{true}; @@ -465,32 +466,23 @@ static void drawTileLabels( ImDrawList* drawList = ImGui::GetForegroundDrawList(); const ImVec2 displaySize = ImGui::GetIO().DisplaySize; - const float gapX = - std::min(TILE_GAP_PIXELS, displaySize.x * 0.1f); - const float gapY = - std::min(TILE_GAP_PIXELS, displaySize.y * 0.1f); - const float tileW = - (displaySize.x - - gapX * static_cast(gridCols - 1)) / - static_cast(gridCols); - const float tileH = - (displaySize.y - - gapY * static_cast(gridRows - 1)) / - static_cast(gridRows); + const std::vector tileRects = + makeGridTileRects( + gridCols, + gridRows, + displaySize.x, + displaySize.y, + TILE_GAP_PIXELS, + TILE_ASPECT + ); const float labelH = std::max(24.0f, ImGui::GetFontSize() + 10.0f); - for (uint32_t i = 0; i < feeds.size(); ++i) + for (uint32_t i = 0; + i < feeds.size() && i < tileRects.size(); + ++i) { - const uint32_t col = i % gridCols; - const uint32_t row = i / gridCols; - - const float x0 = - static_cast(col) * (tileW + gapX); - const float y0 = - static_cast(row) * (tileH + gapY); - const float x1 = x0 + tileW; - const float y1 = y0 + tileH; + const TileRect& tileRect = tileRects[i]; const FeedRuntimeStatus status = feeds[i]->status(); @@ -503,9 +495,15 @@ static void drawTileLabels( ? feedRuntimeStatusDisplayName(status) : sourceInfo); - const ImVec2 labelMin(x0, y1 - labelH); - const ImVec2 labelMax(x1, y1); - const ImVec2 textPos(x0 + 10.0f, y1 - labelH + 5.0f); + const ImVec2 labelMin( + tileRect.x0, + tileRect.y1 - labelH + ); + const ImVec2 labelMax(tileRect.x1, tileRect.y1); + const ImVec2 textPos( + tileRect.x0 + 10.0f, + tileRect.y1 - labelH + 5.0f + ); drawList->AddRectFilled( labelMin, @@ -630,19 +628,14 @@ int main(int argc, char* argv[]) // Vertex buffer (quad tiles) // ---------------------------------------- - const float initialGapX = - 2.0f * TILE_GAP_PIXELS / - static_cast(winWidth); - const float initialGapY = - 2.0f * TILE_GAP_PIXELS / - static_cast(winHeight); - std::vector quadVertices = makeGridLayout( config.gridCols, config.gridRows, - initialGapX, - initialGapY); + static_cast(winWidth), + static_cast(winHeight), + TILE_GAP_PIXELS, + TILE_ASPECT); VkDeviceSize vbSize = sizeof(quadVertices[0]) * quadVertices.size(); @@ -1680,34 +1673,16 @@ int main(int argc, char* argv[]) SDL_GetPerformanceCounter(); VkExtent2D swapExtent = swapchain.extent(); - const float gapX = - 2.0f * TILE_GAP_PIXELS / - static_cast(swapExtent.width); - const float gapY = - 2.0f * TILE_GAP_PIXELS / - static_cast(swapExtent.height); - updateGridLayout( quadVertices.data(), config.gridCols, config.gridRows, - gapX, - gapY + static_cast(swapExtent.width), + static_cast(swapExtent.height), + TILE_GAP_PIXELS, + TILE_ASPECT ); - const float tileWidthPx = - (static_cast(swapExtent.width) - - TILE_GAP_PIXELS * - static_cast(config.gridCols - 1)) / - static_cast(config.gridCols); - const float tileHeightPx = - (static_cast(swapExtent.height) - - TILE_GAP_PIXELS * - static_cast(config.gridRows - 1)) / - static_cast(config.gridRows); - const float tileAspect = - tileWidthPx / tileHeightPx; - for (uint32_t i = 0; i < feedCount; ++i) { float srcAspect = feeds[i]->srcAspectRatio(); @@ -1716,7 +1691,7 @@ int main(int argc, char* argv[]) quadVertices.data(), i, srcAspect, - tileAspect); + TILE_ASPECT); } std::memcpy(