viewport scaling
There is no browser build of this example yet.
Is it the same as C?
- Desktop:verified The Zig version renders a final frame that is byte-identical to the C example.
- Browser:not checked yet The browser comparison does not cover this example yet.
//! raylib [core] example - viewport scaling
//!
//! Zig-API translation of examples/core/core_viewport_scaling.c (raylib 6.2).
//! Example contributed by Agnis Aldiņš (@nezvers) and reviewed by Ramon Santamaria (@raysan5)
//! Example licensed under an unmodified zlib/libpng license.
//! Copyright (c) 2025 Agnis Aldiņš (@nezvers)
const std = @import("std");
const rl = @import("raylib");
const ViewportType = enum {
// Only upscale, useful for pixel art
keep_aspect_integer,
keep_height_integer,
keep_width_integer,
// Can also downscale
keep_aspect,
keep_height,
keep_width,
const count = @typeInfo(ViewportType).@"enum".field_names.len;
/// For displaying on GUI
fn name(viewport_type: ViewportType) [:0]const u8 {
return switch (viewport_type) {
.keep_aspect_integer => "KEEP_ASPECT_INTEGER",
.keep_height_integer => "KEEP_HEIGHT_INTEGER",
.keep_width_integer => "KEEP_WIDTH_INTEGER",
.keep_aspect => "KEEP_ASPECT",
.keep_height => "KEEP_HEIGHT",
.keep_width => "KEEP_WIDTH",
};
}
fn cycle(viewport_type: ViewportType, step: usize) ViewportType {
return @fromBackingInt(@intCast((@backingInt(viewport_type) + step) % count));
}
};
/// Source (render texture) and destination (screen) rectangles of the scaled viewport.
const Viewport = struct {
source: rl.Rectangle,
dest: rl.Rectangle,
};
/// The scaled game view: the window size it was computed for, the game
/// resolution, the scaling rule and the render texture the game draws to.
const Scaler = struct {
screen_width: i32,
screen_height: i32,
game_width: i32,
game_height: i32,
viewport_type: ViewportType,
viewport: Viewport,
target: rl.RenderTexture2D,
/// Recompute the viewport for the current window size and recreate the render texture
fn resize(scaler: *Scaler) !void {
scaler.screen_width = rl.getScreenWidth();
scaler.screen_height = rl.getScreenHeight();
const rule: *const fn (i32, i32, i32, i32) Viewport = switch (scaler.viewport_type) {
.keep_aspect_integer => keepAspectCenteredInteger,
.keep_height_integer => keepHeightCenteredInteger,
.keep_width_integer => keepWidthCenteredInteger,
.keep_aspect => keepAspectCentered,
.keep_height => keepHeightCentered,
.keep_width => keepWidthCentered,
};
scaler.viewport = rule(scaler.screen_width, scaler.screen_height, scaler.game_width, scaler.game_height);
rl.unloadRenderTexture(scaler.target);
const source = scaler.viewport.source;
scaler.target = try rl.loadRenderTexture(@intFromFloat(source.width), -@as(i32, @intFromFloat(source.height)));
}
};
pub fn main() !void {
// Initialization
rl.setConfigFlags(.{ .window_resizable = true });
rl.initWindow(800, 450, "raylib [core] example - viewport scaling");
defer rl.closeWindow(); // Close window and OpenGL context
// Preset resolutions that could be created by subdividing screen resolution
const resolution_list = [_]rl.Vector2{
.{ .x = 64, .y = 64 },
.{ .x = 256, .y = 240 },
.{ .x = 320, .y = 180 },
// 4K doesn't work with integer scaling but included for example purposes with non-integer scaling
.{ .x = 3840, .y = 2160 },
};
var resolution_index: usize = 0;
var scaler: Scaler = .{
.screen_width = 800,
.screen_height = 450,
.game_width = 64,
.game_height = 64,
.viewport_type = .keep_aspect_integer,
.viewport = undefined,
.target = std.mem.zeroes(rl.RenderTexture2D),
};
try scaler.resize();
defer rl.unloadRenderTexture(scaler.target);
// Button rectangles
const decrease_resolution_button: rl.Rectangle = .{ .x = 200, .y = 30, .width = 10, .height = 10 };
const increase_resolution_button: rl.Rectangle = .{ .x = 215, .y = 30, .width = 10, .height = 10 };
const decrease_type_button: rl.Rectangle = .{ .x = 200, .y = 45, .width = 10, .height = 10 };
const increase_type_button: rl.Rectangle = .{ .x = 215, .y = 45, .width = 10, .height = 10 };
rl.setTargetFPS(60); // Set our game to run at 60 frames-per-second
// Main game loop
while (!rl.windowShouldClose()) { // Detect window close button or ESC key
// Update
if (rl.isWindowResized()) try scaler.resize();
const mouse_position = rl.getMousePosition();
const mouse_pressed = rl.isMouseButtonPressed(.left);
// Check buttons and rescale
if (rl.checkCollisionPointRec(mouse_position, decrease_resolution_button) and mouse_pressed) {
resolution_index = (resolution_index + resolution_list.len - 1) % resolution_list.len;
scaler.game_width = @intFromFloat(resolution_list[resolution_index].x);
scaler.game_height = @intFromFloat(resolution_list[resolution_index].y);
try scaler.resize();
}
if (rl.checkCollisionPointRec(mouse_position, increase_resolution_button) and mouse_pressed) {
resolution_index = (resolution_index + 1) % resolution_list.len;
scaler.game_width = @intFromFloat(resolution_list[resolution_index].x);
scaler.game_height = @intFromFloat(resolution_list[resolution_index].y);
try scaler.resize();
}
if (rl.checkCollisionPointRec(mouse_position, decrease_type_button) and mouse_pressed) {
scaler.viewport_type = scaler.viewport_type.cycle(ViewportType.count - 1);
try scaler.resize();
}
if (rl.checkCollisionPointRec(mouse_position, increase_type_button) and mouse_pressed) {
scaler.viewport_type = scaler.viewport_type.cycle(1);
try scaler.resize();
}
const source_rect = scaler.viewport.source;
const dest_rect = scaler.viewport.dest;
const texture_mouse_position = screenToRenderTexturePosition(mouse_position, source_rect, dest_rect);
// Draw
// Draw our scene to the render texture
{
rl.beginTextureMode(scaler.target);
defer rl.endTextureMode();
rl.clearBackground(.white);
rl.drawCircleV(texture_mouse_position, 20.0, .lime);
}
// Draw render texture to main framebuffer
rl.beginDrawing();
defer rl.endDrawing();
rl.clearBackground(.black);
// Draw our render texture with rotation applied
rl.drawTexturePro(scaler.target.texture, source_rect, dest_rect, .{ .x = 0.0, .y = 0.0 }, 0.0, .white);
// Draw Native resolution (GUI or anything)
// Draw info box
const info_rect: rl.Rectangle = .{ .x = 5, .y = 5, .width = 330, .height = 105 };
rl.drawRectangleRec(info_rect, rl.fade(.light_gray, 0.7));
rl.drawRectangleLinesEx(info_rect, 1, .blue);
rl.drawText(rl.textFormat("Window Resolution: {d} x {d}", .{ scaler.screen_width, scaler.screen_height }), 15, 15, 10, .black);
rl.drawText(rl.textFormat("Game Resolution: {d} x {d}", .{ scaler.game_width, scaler.game_height }), 15, 30, 10, .black);
rl.drawText(rl.textFormat("Type: {s}", .{scaler.viewport_type.name()}), 15, 45, 10, .black);
const scale_ratio: rl.Vector2 = .{ .x = dest_rect.width / source_rect.width, .y = -dest_rect.height / source_rect.height };
if (scale_ratio.x < 0.001 or scale_ratio.y < 0.001) {
rl.drawText("Scale ratio: INVALID", 15, 60, 10, .black);
} else {
rl.drawText(rl.textFormat("Scale ratio: {d:.2} x {d:.2}", .{ scale_ratio.x, scale_ratio.y }), 15, 60, 10, .black);
}
rl.drawText(rl.textFormat("Source size: {d:.2} x {d:.2}", .{ source_rect.width, -source_rect.height }), 15, 75, 10, .black);
rl.drawText(rl.textFormat("Destination size: {d:.2} x {d:.2}", .{ dest_rect.width, dest_rect.height }), 15, 90, 10, .black);
// Draw buttons
rl.drawRectangleRec(decrease_type_button, .sky_blue);
rl.drawRectangleRec(increase_type_button, .sky_blue);
rl.drawRectangleRec(decrease_resolution_button, .sky_blue);
rl.drawRectangleRec(increase_resolution_button, .sky_blue);
drawButtonLabel("<", decrease_type_button);
drawButtonLabel(">", increase_type_button);
drawButtonLabel("<", decrease_resolution_button);
drawButtonLabel(">", increase_resolution_button);
}
}
fn drawButtonLabel(label: [:0]const u8, button: rl.Rectangle) void {
rl.drawText(label, @as(i32, @intFromFloat(button.x)) + 3, @as(i32, @intFromFloat(button.y)) + 1, 10, .black);
}
/// C's `(float)(int)x`: truncate toward zero through an integer (so -0.5 gives +0.0).
fn truncate(x: f32) f32 {
return @floatFromInt(@as(i32, @intFromFloat(x)));
}
fn float(x: i32) f32 {
return @floatFromInt(x);
}
fn keepAspectCenteredInteger(screen_width: i32, screen_height: i32, game_width: i32, game_height: i32) Viewport {
const ratio_x = @divTrunc(screen_width, game_width);
const ratio_y = @divTrunc(screen_height, game_height);
const resize_ratio = float(@min(ratio_x, ratio_y));
return .{
.source = .{ .x = 0.0, .y = float(game_height), .width = float(game_width), .height = float(-game_height) },
.dest = .{
.x = truncate((float(screen_width) - float(game_width) * resize_ratio) * 0.5),
.y = truncate((float(screen_height) - float(game_height) * resize_ratio) * 0.5),
.width = truncate(float(game_width) * resize_ratio),
.height = truncate(float(game_height) * resize_ratio),
},
};
}
fn keepHeightCenteredInteger(screen_width: i32, screen_height: i32, _: i32, game_height: i32) Viewport {
const resize_ratio = float(screen_height) / float(game_height);
const source_width = truncate(float(screen_width) / resize_ratio);
return .{
.source = .{ .x = 0.0, .y = 0.0, .width = source_width, .height = float(-game_height) },
.dest = .{
.x = truncate((float(screen_width) - source_width * resize_ratio) * 0.5),
.y = truncate((float(screen_height) - float(game_height) * resize_ratio) * 0.5),
.width = truncate(source_width * resize_ratio),
.height = truncate(float(game_height) * resize_ratio),
},
};
}
fn keepWidthCenteredInteger(screen_width: i32, screen_height: i32, game_width: i32, _: i32) Viewport {
const resize_ratio = float(screen_width) / float(game_width);
const source_height = truncate(float(screen_height) / resize_ratio);
return .{
.source = .{ .x = 0.0, .y = 0.0, .width = float(game_width), .height = source_height * -1.0 },
.dest = .{
.x = truncate((float(screen_width) - float(game_width) * resize_ratio) * 0.5),
.y = truncate((float(screen_height) - source_height * resize_ratio) * 0.5),
.width = truncate(float(game_width) * resize_ratio),
.height = truncate(source_height * resize_ratio),
},
};
}
fn keepAspectCentered(screen_width: i32, screen_height: i32, game_width: i32, game_height: i32) Viewport {
const ratio_x = float(screen_width) / float(game_width);
const ratio_y = float(screen_height) / float(game_height);
const resize_ratio = @min(ratio_x, ratio_y);
return .{
.source = .{ .x = 0.0, .y = float(game_height), .width = float(game_width), .height = float(-game_height) },
.dest = .{
.x = truncate((float(screen_width) - float(game_width) * resize_ratio) * 0.5),
.y = truncate((float(screen_height) - float(game_height) * resize_ratio) * 0.5),
.width = truncate(float(game_width) * resize_ratio),
.height = truncate(float(game_height) * resize_ratio),
},
};
}
fn keepHeightCentered(screen_width: i32, screen_height: i32, _: i32, game_height: i32) Viewport {
const resize_ratio = float(screen_height) / float(game_height);
const source_width = truncate(float(screen_width) / resize_ratio);
return .{
.source = .{ .x = 0.0, .y = 0.0, .width = source_width, .height = float(-game_height) },
.dest = .{
.x = truncate((float(screen_width) - source_width * resize_ratio) * 0.5),
.y = truncate((float(screen_height) - float(game_height) * resize_ratio) * 0.5),
.width = truncate(source_width * resize_ratio),
.height = truncate(float(game_height) * resize_ratio),
},
};
}
fn keepWidthCentered(screen_width: i32, screen_height: i32, game_width: i32, _: i32) Viewport {
const resize_ratio = float(screen_width) / float(game_width);
const source_height = truncate(float(screen_height) / resize_ratio);
return .{
.source = .{ .x = 0.0, .y = 0.0, .width = float(game_width), .height = source_height * -1.0 },
.dest = .{
.x = truncate((float(screen_width) - float(game_width) * resize_ratio) * 0.5),
.y = truncate((float(screen_height) - source_height * resize_ratio) * 0.5),
.width = truncate(float(game_width) * resize_ratio),
.height = truncate(source_height * resize_ratio),
},
};
}
/// Example how to calculate position on RenderTexture
fn screenToRenderTexturePosition(point: rl.Vector2, texture_rect: rl.Rectangle, scaled_rect: rl.Rectangle) rl.Vector2 {
const relative_position: rl.Vector2 = .{ .x = point.x - scaled_rect.x, .y = point.y - scaled_rect.y };
const ratio: rl.Vector2 = .{ .x = texture_rect.width / scaled_rect.width, .y = -texture_rect.height / scaled_rect.height };
return .{ .x = relative_position.x * ratio.x, .y = relative_position.y * ratio.x };
}/*******************************************************************************************
*
* raylib [core] example - viewport scaling
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
* Example contributed by Agnis Aldiņš (@nezvers) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025 Agnis Aldiņš (@nezvers)
*
********************************************************************************************/
#include "raylib.h"
#define RESOLUTION_COUNT 4 // For iteration purposes and teaching example
typedef enum {
// Only upscale, useful for pixel art
KEEP_ASPECT_INTEGER,
KEEP_HEIGHT_INTEGER,
KEEP_WIDTH_INTEGER,
// Can also downscale
KEEP_ASPECT,
KEEP_HEIGHT,
KEEP_WIDTH,
// For itteration purposes and as a teaching example
VIEWPORT_TYPE_COUNT,
} ViewportType;
// For displaying on GUI
const char *ViewportTypeNames[VIEWPORT_TYPE_COUNT] = {
"KEEP_ASPECT_INTEGER",
"KEEP_HEIGHT_INTEGER",
"KEEP_WIDTH_INTEGER",
"KEEP_ASPECT",
"KEEP_HEIGHT",
"KEEP_WIDTH",
};
//--------------------------------------------------------------------------------------
// Module Functions Declaration
//--------------------------------------------------------------------------------------
static void KeepAspectCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
static void KeepHeightCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
static void KeepWidthCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
static void KeepAspectCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
static void KeepHeightCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
static void KeepWidthCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect);
static void ResizeRenderSize(ViewportType viewportType, int *screenWidth, int *screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect, RenderTexture2D *target);
// Example how to calculate position on RenderTexture
static Vector2 Screen2RenderTexturePosition(Vector2 point, Rectangle *textureRect, Rectangle *scaledRect);
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//---------------------------------------------------------
int screenWidth = 800;
int screenHeight = 450;
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
InitWindow(screenWidth, screenHeight, "raylib [core] example - viewport scaling");
// Preset resolutions that could be created by subdividing screen resolution
Vector2 resolutionList[RESOLUTION_COUNT] = {
(Vector2){ 64, 64 },
(Vector2){ 256, 240 },
(Vector2){ 320, 180 },
// 4K doesn't work with integer scaling but included for example purposes with non-integer scaling
(Vector2){ 3840, 2160 },
};
int resolutionIndex = 0;
int gameWidth = 64;
int gameHeight = 64;
RenderTexture2D target = (RenderTexture2D){ 0 };
Rectangle sourceRect = (Rectangle){ 0 };
Rectangle destRect = (Rectangle){ 0 };
ViewportType viewportType = KEEP_ASPECT_INTEGER;
ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
// Button rectangles
Rectangle decreaseResolutionButton = (Rectangle){ 200, 30, 10, 10 };
Rectangle increaseResolutionButton = (Rectangle){ 215, 30, 10, 10 };
Rectangle decreaseTypeButton = (Rectangle){ 200, 45, 10, 10 };
Rectangle increaseTypeButton = (Rectangle){ 215, 45, 10, 10 };
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//----------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
if (IsWindowResized()) ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
Vector2 mousePosition = GetMousePosition();
bool mousePressed = IsMouseButtonPressed(MOUSE_BUTTON_LEFT);
// Check buttons and rescale
if (CheckCollisionPointRec(mousePosition, decreaseResolutionButton) && mousePressed)
{
resolutionIndex = (resolutionIndex + RESOLUTION_COUNT - 1)%RESOLUTION_COUNT;
gameWidth = (int)resolutionList[resolutionIndex].x;
gameHeight = (int)resolutionList[resolutionIndex].y;
ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
}
if (CheckCollisionPointRec(mousePosition, increaseResolutionButton) && mousePressed)
{
resolutionIndex = (resolutionIndex + 1)%RESOLUTION_COUNT;
gameWidth = (int)resolutionList[resolutionIndex].x;
gameHeight = (int)resolutionList[resolutionIndex].y;
ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
}
if (CheckCollisionPointRec(mousePosition, decreaseTypeButton) && mousePressed)
{
viewportType = (viewportType + VIEWPORT_TYPE_COUNT - 1)%VIEWPORT_TYPE_COUNT;
ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
}
if (CheckCollisionPointRec(mousePosition, increaseTypeButton) && mousePressed)
{
viewportType = (viewportType + 1)%VIEWPORT_TYPE_COUNT;
ResizeRenderSize(viewportType, &screenWidth, &screenHeight, gameWidth, gameHeight, &sourceRect, &destRect, &target);
}
Vector2 textureMousePosition = Screen2RenderTexturePosition(mousePosition, &sourceRect, &destRect);
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
// Draw our scene to the render texture
BeginTextureMode(target);
ClearBackground(WHITE);
DrawCircleV(textureMousePosition, 20.0f, LIME);
EndTextureMode();
// Draw render texture to main framebuffer
BeginDrawing();
ClearBackground(BLACK);
// Draw our render texture with rotation applied
DrawTexturePro(target.texture, sourceRect, destRect, (Vector2){ 0.0f, 0.0f }, 0.0f, WHITE);
// Draw Native resolution (GUI or anything)
// Draw info box
Rectangle infoRect = (Rectangle){5, 5, 330, 105};
DrawRectangleRec(infoRect, Fade(LIGHTGRAY, 0.7f));
DrawRectangleLinesEx(infoRect, 1, BLUE);
DrawText(TextFormat("Window Resolution: %d x %d", screenWidth, screenHeight), 15, 15, 10, BLACK);
DrawText(TextFormat("Game Resolution: %d x %d", gameWidth, gameHeight), 15, 30, 10, BLACK);
DrawText(TextFormat("Type: %s", ViewportTypeNames[viewportType]), 15, 45, 10, BLACK);
Vector2 scaleRatio = (Vector2){destRect.width/sourceRect.width, -destRect.height/sourceRect.height};
if (scaleRatio.x < 0.001f || scaleRatio.y < 0.001f) DrawText(TextFormat("Scale ratio: INVALID"), 15, 60, 10, BLACK);
else DrawText(TextFormat("Scale ratio: %.2f x %.2f", scaleRatio.x, scaleRatio.y), 15, 60, 10, BLACK);
DrawText(TextFormat("Source size: %.2f x %.2f", sourceRect.width, -sourceRect.height), 15, 75, 10, BLACK);
DrawText(TextFormat("Destination size: %.2f x %.2f", destRect.width, destRect.height), 15, 90, 10, BLACK);
// Draw buttons
DrawRectangleRec(decreaseTypeButton, SKYBLUE);
DrawRectangleRec(increaseTypeButton, SKYBLUE);
DrawRectangleRec(decreaseResolutionButton, SKYBLUE);
DrawRectangleRec(increaseResolutionButton, SKYBLUE);
DrawText("<", (int)decreaseTypeButton.x + 3, (int)decreaseTypeButton.y + 1, 10, BLACK);
DrawText(">", (int)increaseTypeButton.x + 3, (int)increaseTypeButton.y + 1, 10, BLACK);
DrawText("<", (int)decreaseResolutionButton.x + 3, (int)decreaseResolutionButton.y + 1, 10, BLACK);
DrawText(">", (int)increaseResolutionButton.x + 3, (int)increaseResolutionButton.y + 1, 10, BLACK);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//----------------------------------------------------------------------------------
CloseWindow(); // Close window and OpenGL context
//----------------------------------------------------------------------------------
return 0;
}
//--------------------------------------------------------------------------------------
// Module Functions Definition
//--------------------------------------------------------------------------------------
static void KeepAspectCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
{
sourceRect->x = 0.0f;
sourceRect->y = (float)gameHeight;
sourceRect->width = (float)gameWidth;
sourceRect->height = (float)-gameHeight;
const int ratio_x = (screenWidth/gameWidth);
const int ratio_y = (screenHeight/gameHeight);
const float resizeRatio = (float)((ratio_x < ratio_y)? ratio_x : ratio_y);
destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
destRect->width = (float)(int)(gameWidth*resizeRatio);
destRect->height = (float)(int)(gameHeight*resizeRatio);
}
static void KeepHeightCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
{
const float resizeRatio = (float)screenHeight/gameHeight;
sourceRect->x = 0.0f;
sourceRect->y = 0.0f;
sourceRect->width = (float)(int)(screenWidth/resizeRatio);
sourceRect->height = (float)-gameHeight;
destRect->x = (float)(int)((screenWidth - (sourceRect->width*resizeRatio))*0.5f);
destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
destRect->width = (float)(int)(sourceRect->width*resizeRatio);
destRect->height = (float)(int)(gameHeight*resizeRatio);
}
static void KeepWidthCenteredInteger(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
{
const float resizeRatio = (float)screenWidth/gameWidth;
sourceRect->x = 0.0f;
sourceRect->y = 0.0f;
sourceRect->width = (float)gameWidth;
sourceRect->height = (float)(int)(screenHeight/resizeRatio);
destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
destRect->y = (float)(int)((screenHeight - (sourceRect->height*resizeRatio))*0.5f);
destRect->width = (float)(int)(gameWidth*resizeRatio);
destRect->height = (float)(int)(sourceRect->height*resizeRatio);
sourceRect->height *= -1.0f;
}
static void KeepAspectCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
{
sourceRect->x = 0.0f;
sourceRect->y = (float)gameHeight;
sourceRect->width = (float)gameWidth;
sourceRect->height = (float)-gameHeight;
const float ratio_x = ((float)screenWidth/(float)gameWidth);
const float ratio_y = ((float)screenHeight/(float)gameHeight);
const float resizeRatio = (ratio_x < ratio_y ? ratio_x : ratio_y);
destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
destRect->width = (float)(int)(gameWidth*resizeRatio);
destRect->height = (float)(int)(gameHeight*resizeRatio);
}
static void KeepHeightCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
{
const float resizeRatio = ((float)screenHeight/(float)gameHeight);
sourceRect->x = 0.0f;
sourceRect->y = 0.0f;
sourceRect->width = (float)(int)((float)screenWidth/resizeRatio);
sourceRect->height = (float)-gameHeight;
destRect->x = (float)(int)((screenWidth - (sourceRect->width*resizeRatio))*0.5f);
destRect->y = (float)(int)((screenHeight - (gameHeight*resizeRatio))*0.5f);
destRect->width = (float)(int)(sourceRect->width*resizeRatio);
destRect->height = (float)(int)(gameHeight*resizeRatio);
}
static void KeepWidthCentered(int screenWidth, int screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect)
{
const float resizeRatio = ((float)screenWidth/(float)gameWidth);
sourceRect->x = 0.0f;
sourceRect->y = 0.0f;
sourceRect->width = (float)gameWidth;
sourceRect->height = (float)(int)((float)screenHeight/resizeRatio);
destRect->x = (float)(int)((screenWidth - (gameWidth*resizeRatio))*0.5f);
destRect->y = (float)(int)((screenHeight - (sourceRect->height*resizeRatio))*0.5f);
destRect->width = (float)(int)(gameWidth*resizeRatio);
destRect->height = (float)(int)(sourceRect->height*resizeRatio);
sourceRect->height *= -1.f;
}
static void ResizeRenderSize(ViewportType viewportType, int *screenWidth, int *screenHeight, int gameWidth, int gameHeight, Rectangle *sourceRect, Rectangle *destRect, RenderTexture2D *target)
{
*screenWidth = GetScreenWidth();
*screenHeight = GetScreenHeight();
switch(viewportType)
{
case KEEP_ASPECT_INTEGER: KeepAspectCenteredInteger(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
case KEEP_HEIGHT_INTEGER: KeepHeightCenteredInteger(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
case KEEP_WIDTH_INTEGER: KeepWidthCenteredInteger(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
case KEEP_ASPECT: KeepAspectCentered(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
case KEEP_HEIGHT: KeepHeightCentered(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
case KEEP_WIDTH: KeepWidthCentered(*screenWidth, *screenHeight, gameWidth, gameHeight, sourceRect, destRect); break;
default: break;
}
UnloadRenderTexture(*target);
*target = LoadRenderTexture((int)sourceRect->width, -(int)sourceRect->height);
}
// Example how to calculate position on RenderTexture
static Vector2 Screen2RenderTexturePosition(Vector2 point, Rectangle *textureRect, Rectangle *scaledRect)
{
Vector2 relativePosition = {point.x - scaledRect->x, point.y - scaledRect->y};
Vector2 ratio = {textureRect->width/scaledRect->width, -textureRect->height/scaledRect->height};
return (Vector2){relativePosition.x*ratio.x, relativePosition.y*ratio.x};
}From raylib 6.2. The original on GitHub.