← ziglang.inraylib-zigGitHub

tiled drawing

★★★☆by Vlad Adrian (@demizdor)

There is no browser build of this example yet.

Is it the same as C?

//! raylib [textures] example - tiled drawing
//!
//! Zig-API translation of examples/textures/textures_tiled_drawing.c (raylib 6.2).
//! Example contributed by Vlad Adrian (@demizdor) and reviewed by Ramon Santamaria (@raysan5)
//! Example licensed under an unmodified zlib/libpng license.
//! Copyright (c) 2020-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)

const rl = @import("raylib");

const opt_width = 220; // Max width for the options container
const margin_size = 8; // Size for the margins
const color_size = 16; // Size of the color select buttons

// Coordinates for all patterns inside the texture
const rec_pattern = [_]rl.Rectangle{
    .{ .x = 3, .y = 3, .width = 66, .height = 66 },
    .{ .x = 75, .y = 3, .width = 100, .height = 100 },
    .{ .x = 3, .y = 75, .width = 66, .height = 66 },
    .{ .x = 7, .y = 156, .width = 50, .height = 50 },
    .{ .x = 85, .y = 106, .width = 90, .height = 45 },
    .{ .x = 75, .y = 154, .width = 100, .height = 60 },
};

// Setup colors
const colors = [_]rl.Color{ .black, .maroon, .orange, .blue, .purple, .beige, .lime, .red, .dark_gray, .sky_blue };

pub fn main() !void {
    // Initialization
    const screen_width = 800;
    const screen_height = 450;

    rl.setConfigFlags(.{ .window_resizable = true }); // Make the window resizable
    rl.initWindow(screen_width, screen_height, "raylib [textures] example - tiled drawing");
    defer rl.closeWindow(); // Close window and OpenGL context

    // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
    const tex_pattern = try rl.loadTexture("resources/patterns.png");
    defer rl.unloadTexture(tex_pattern); // Unload texture
    rl.setTextureFilter(tex_pattern, .bilinear); // Makes the texture smoother when upscaled

    // Calculate rectangle for each color: two rows of buttons
    var color_rec: [colors.len]rl.Rectangle = undefined;
    for (&color_rec, 0..) |*rec, i| {
        const column: i32 = @intCast(i % (colors.len / 2));
        const row: i32 = @intCast(i / (colors.len / 2));
        const x = column * (color_size * 2 + margin_size);
        const y = row * (color_size + margin_size);
        rec.* = .{
            .x = 2.0 + margin_size + @as(f32, @floatFromInt(x)),
            .y = 22.0 + 256.0 + margin_size + @as(f32, @floatFromInt(y)),
            .width = color_size * 2.0,
            .height = color_size,
        };
    }

    var active_pattern: usize = 0;
    var active_col: usize = 0;
    var scale: f32 = 1.0;
    var rotation: f32 = 0.0;

    rl.setTargetFPS(60);

    // Main game loop
    while (!rl.windowShouldClose()) { // Detect window close button or ESC key
        // Update
        // Handle mouse
        if (rl.isMouseButtonPressed(.left)) {
            const mouse = rl.getMousePosition();

            // Check which pattern was clicked and set it as the active pattern
            for (rec_pattern, 0..) |pattern, i| {
                if (rl.checkCollisionPointRec(mouse, .{
                    .x = 2 + margin_size + pattern.x,
                    .y = 40 + margin_size + pattern.y,
                    .width = pattern.width,
                    .height = pattern.height,
                })) {
                    active_pattern = i;
                    break;
                }
            }

            // Check to see which color was clicked and set it as the active color
            for (color_rec, 0..) |rec, i| {
                if (rl.checkCollisionPointRec(mouse, rec)) {
                    active_col = i;
                    break;
                }
            }
        }

        // Handle keys: change scale
        if (rl.isKeyPressed(.up)) scale += 0.25;
        if (rl.isKeyPressed(.down)) scale -= 0.25;
        if (scale > 10.0) {
            scale = 10.0;
        } else if (scale <= 0.0) {
            scale = 0.25;
        }

        // Handle keys: change rotation
        if (rl.isKeyPressed(.left)) rotation -= 25.0;
        if (rl.isKeyPressed(.right)) rotation += 25.0;

        // Handle keys: reset
        if (rl.isKeyPressed(.space)) {
            rotation = 0.0;
            scale = 1.0;
        }

        // Draw
        rl.beginDrawing();
        defer rl.endDrawing();

        rl.clearBackground(.ray_white);

        // Draw the tiled area
        drawTextureTiled(tex_pattern, rec_pattern[active_pattern], .{
            .x = opt_width + margin_size,
            .y = margin_size,
            .width = @as(f32, @floatFromInt(rl.getScreenWidth() - opt_width)) - 2.0 * margin_size,
            .height = @as(f32, @floatFromInt(rl.getScreenHeight())) - 2.0 * margin_size,
        }, .{ .x = 0.0, .y = 0.0 }, rotation, scale, colors[active_col]);

        // Draw options
        rl.drawRectangle(margin_size, margin_size, opt_width - margin_size, rl.getScreenHeight() - 2 * margin_size, rl.colorAlpha(.light_gray, 0.5));

        rl.drawText("Select Pattern", 2 + margin_size, 30 + margin_size, 10, .black);
        rl.drawTexture(tex_pattern, 2 + margin_size, 40 + margin_size, .black);
        const pattern = rec_pattern[active_pattern];
        rl.drawRectangle(
            2 + margin_size + @as(i32, @intFromFloat(pattern.x)),
            40 + margin_size + @as(i32, @intFromFloat(pattern.y)),
            @intFromFloat(pattern.width),
            @intFromFloat(pattern.height),
            rl.colorAlpha(.dark_blue, 0.3),
        );

        rl.drawText("Select Color", 2 + margin_size, 10 + 256 + margin_size, 10, .black);
        for (color_rec, colors, 0..) |rec, color, i| {
            rl.drawRectangleRec(rec, color);
            if (active_col == i) rl.drawRectangleLinesEx(rec, 3, rl.colorAlpha(.white, 0.5));
        }

        rl.drawText("Scale (UP/DOWN to change)", 2 + margin_size, 80 + 256 + margin_size, 10, .black);
        rl.drawText(rl.textFormat("{d:.2}x", .{scale}), 2 + margin_size, 92 + 256 + margin_size, 20, .black);

        rl.drawText("Rotation (LEFT/RIGHT to change)", 2 + margin_size, 122 + 256 + margin_size, 10, .black);
        rl.drawText(rl.textFormat("{d:.0} degrees", .{rotation}), 2 + margin_size, 134 + 256 + margin_size, 20, .black);

        rl.drawText("Press [SPACE] to reset", 2 + margin_size, 164 + 256 + margin_size, 10, .dark_blue);

        // Draw FPS
        rl.drawText(rl.textFormat("{d} FPS", .{rl.getFPS()}), 2 + margin_size, 2 + margin_size, 20, .black);
    }
}

/// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest
fn drawTextureTiled(texture: rl.Texture2D, source: rl.Rectangle, dest: rl.Rectangle, origin: rl.Vector2, rotation: f32, scale: f32, tint: rl.Color) void {
    if ((texture.id == 0) or (scale <= 0.0)) return; // Wanna see a infinite loop?!...just delete this line!
    if ((source.width == 0) or (source.height == 0)) return;

    const tile_width: i32 = @intFromFloat(source.width * scale);
    const tile_height: i32 = @intFromFloat(source.height * scale);
    const tile_w: f32 = @floatFromInt(tile_width);
    const tile_h: f32 = @floatFromInt(tile_height);

    const Tile = struct {
        texture: rl.Texture2D,
        source: rl.Rectangle,
        origin: rl.Vector2,
        rotation: f32,
        tint: rl.Color,

        /// Draw the source part scaled by (sx, sy) into `rec`
        fn draw(t: @This(), sx: f32, sy: f32, rec: rl.Rectangle) void {
            rl.drawTexturePro(t.texture, .{
                .x = t.source.x,
                .y = t.source.y,
                .width = sx * t.source.width,
                .height = sy * t.source.height,
            }, rec, t.origin, t.rotation, t.tint);
        }
    };
    const tile: Tile = .{ .texture = texture, .source = source, .origin = origin, .rotation = rotation, .tint = tint };

    if ((dest.width < tile_w) and (dest.height < tile_h)) {
        // Can fit only one tile
        tile.draw(dest.width / tile_w, dest.height / tile_h, .{ .x = dest.x, .y = dest.y, .width = dest.width, .height = dest.height });
    } else if (dest.width <= tile_w) {
        // Tiled vertically (one column)
        var dy: i32 = 0;
        while (@as(f32, @floatFromInt(dy + tile_height)) < dest.height) : (dy += tile_height) {
            const fdy: f32 = @floatFromInt(dy);
            tile.draw(dest.width / tile_w, 1, .{ .x = dest.x, .y = dest.y + fdy, .width = dest.width, .height = tile_h });
        }

        // Fit last tile
        const fdy: f32 = @floatFromInt(dy);
        if (fdy < dest.height) {
            tile.draw(dest.width / tile_w, (dest.height - fdy) / tile_h, .{ .x = dest.x, .y = dest.y + fdy, .width = dest.width, .height = dest.height - fdy });
        }
    } else if (dest.height <= tile_h) {
        // Tiled horizontally (one row)
        var dx: i32 = 0;
        while (@as(f32, @floatFromInt(dx + tile_width)) < dest.width) : (dx += tile_width) {
            const fdx: f32 = @floatFromInt(dx);
            tile.draw(1, dest.height / tile_h, .{ .x = dest.x + fdx, .y = dest.y, .width = tile_w, .height = dest.height });
        }

        // Fit last tile
        const fdx: f32 = @floatFromInt(dx);
        if (fdx < dest.width) {
            tile.draw((dest.width - fdx) / tile_w, dest.height / tile_h, .{ .x = dest.x + fdx, .y = dest.y, .width = dest.width - fdx, .height = dest.height });
        }
    } else {
        // Tiled both horizontally and vertically (rows and columns)
        var dx: i32 = 0;
        while (@as(f32, @floatFromInt(dx + tile_width)) < dest.width) : (dx += tile_width) {
            const fdx: f32 = @floatFromInt(dx);
            var dy: i32 = 0;
            while (@as(f32, @floatFromInt(dy + tile_height)) < dest.height) : (dy += tile_height) {
                const fdy: f32 = @floatFromInt(dy);
                rl.drawTexturePro(texture, source, .{ .x = dest.x + fdx, .y = dest.y + fdy, .width = tile_w, .height = tile_h }, origin, rotation, tint);
            }

            const fdy: f32 = @floatFromInt(dy);
            if (fdy < dest.height) {
                tile.draw(1, (dest.height - fdy) / tile_h, .{ .x = dest.x + fdx, .y = dest.y + fdy, .width = tile_w, .height = dest.height - fdy });
            }
        }

        // Fit last column of tiles
        const fdx: f32 = @floatFromInt(dx);
        if (fdx < dest.width) {
            var dy: i32 = 0;
            while (@as(f32, @floatFromInt(dy + tile_height)) < dest.height) : (dy += tile_height) {
                const fdy: f32 = @floatFromInt(dy);
                tile.draw((dest.width - fdx) / tile_w, 1, .{ .x = dest.x + fdx, .y = dest.y + fdy, .width = dest.width - fdx, .height = tile_h });
            }

            // Draw final tile in the bottom right corner
            const fdy: f32 = @floatFromInt(dy);
            if (fdy < dest.height) {
                tile.draw((dest.width - fdx) / tile_w, (dest.height - fdy) / tile_h, .{ .x = dest.x + fdx, .y = dest.y + fdy, .width = dest.width - fdx, .height = dest.height - fdy });
            }
        }
    }
}
/*******************************************************************************************
*
*   raylib [textures] example - tiled drawing
*
*   Example complexity rating: [★★★☆] 3/4
*
*   Example originally created with raylib 3.0, last time updated with raylib 4.2
*
*   Example contributed by Vlad Adrian (@demizdor) 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) 2020-2025 Vlad Adrian (@demizdor) and Ramon Santamaria (@raysan5)
*
********************************************************************************************/

#include "raylib.h"

#define SIZEOF(A) (sizeof(A)/sizeof(A[0]))
#define OPT_WIDTH       220       // Max width for the options container
#define MARGIN_SIZE       8       // Size for the margins
#define COLOR_SIZE       16       // Size of the color select buttons

// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest
void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint);

//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
    // Initialization
    //--------------------------------------------------------------------------------------
    const int screenWidth = 800;
    const int screenHeight = 450;

    SetConfigFlags(FLAG_WINDOW_RESIZABLE); // Make the window resizable
    InitWindow(screenWidth, screenHeight, "raylib [textures] example - tiled drawing");

    // NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
    Texture texPattern = LoadTexture("resources/patterns.png");
    SetTextureFilter(texPattern, TEXTURE_FILTER_BILINEAR); // Makes the texture smoother when upscaled

    // Coordinates for all patterns inside the texture
    const Rectangle recPattern[] = {
        (Rectangle){ 3, 3, 66, 66 },
        (Rectangle){ 75, 3, 100, 100 },
        (Rectangle){ 3, 75, 66, 66 },
        (Rectangle){ 7, 156, 50, 50 },
        (Rectangle){ 85, 106, 90, 45 },
        (Rectangle){ 75, 154, 100, 60}
    };

    // Setup colors
    const Color colors[] = { BLACK, MAROON, ORANGE, BLUE, PURPLE, BEIGE, LIME, RED, DARKGRAY, SKYBLUE };
    enum { MAX_COLORS = SIZEOF(colors) };
    Rectangle colorRec[MAX_COLORS] = { 0 };

    // Calculate rectangle for each color
    for (int i = 0, x = 0, y = 0; i < MAX_COLORS; i++)
    {
        colorRec[i].x = 2.0f + MARGIN_SIZE + x;
        colorRec[i].y = 22.0f + 256.0f + MARGIN_SIZE + y;
        colorRec[i].width = COLOR_SIZE*2.0f;
        colorRec[i].height = (float)COLOR_SIZE;

        if (i == (MAX_COLORS/2 - 1))
        {
            x = 0;
            y += COLOR_SIZE + MARGIN_SIZE;
        }
        else x += (COLOR_SIZE*2 + MARGIN_SIZE);
    }

    int activePattern = 0, activeCol = 0;
    float scale = 1.0f, rotation = 0.0f;

    SetTargetFPS(60);
    //---------------------------------------------------------------------------------------

    // Main game loop
    while (!WindowShouldClose())    // Detect window close button or ESC key
    {
        // Update
        //----------------------------------------------------------------------------------
        // Handle mouse
        if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT))
        {
            const Vector2 mouse = GetMousePosition();

            // Check which pattern was clicked and set it as the active pattern
            for (int i = 0; i < SIZEOF(recPattern); i++)
            {
                if (CheckCollisionPointRec(mouse, (Rectangle){ 2 + MARGIN_SIZE + recPattern[i].x, 40 + MARGIN_SIZE + recPattern[i].y, recPattern[i].width, recPattern[i].height }))
                {
                    activePattern = i;
                    break;
                }
            }

            // Check to see which color was clicked and set it as the active color
            for (int i = 0; i < MAX_COLORS; i++)
            {
                if (CheckCollisionPointRec(mouse, colorRec[i]))
                {
                    activeCol = i;
                    break;
                }
            }
        }

        // Handle keys: change scale
        if (IsKeyPressed(KEY_UP)) scale += 0.25f;
        if (IsKeyPressed(KEY_DOWN)) scale -= 0.25f;
        if (scale > 10.0f) scale = 10.0f;
        else if ( scale <= 0.0f) scale = 0.25f;

        // Handle keys: change rotation
        if (IsKeyPressed(KEY_LEFT)) rotation -= 25.0f;
        if (IsKeyPressed(KEY_RIGHT)) rotation += 25.0f;

        // Handle keys: reset
        if (IsKeyPressed(KEY_SPACE)) { rotation = 0.0f; scale = 1.0f; }
        //----------------------------------------------------------------------------------

        // Draw
        //----------------------------------------------------------------------------------
        BeginDrawing();
            ClearBackground(RAYWHITE);

            // Draw the tiled area
            DrawTextureTiled(texPattern, recPattern[activePattern], (Rectangle){(float)OPT_WIDTH+MARGIN_SIZE, (float)MARGIN_SIZE, GetScreenWidth() - OPT_WIDTH - 2.0f*MARGIN_SIZE, GetScreenHeight() - 2.0f*MARGIN_SIZE},
                (Vector2){0.0f, 0.0f}, rotation, scale, colors[activeCol]);

            // Draw options
            DrawRectangle(MARGIN_SIZE, MARGIN_SIZE, OPT_WIDTH - MARGIN_SIZE, GetScreenHeight() - 2*MARGIN_SIZE, ColorAlpha(LIGHTGRAY, 0.5f));

            DrawText("Select Pattern", 2 + MARGIN_SIZE, 30 + MARGIN_SIZE, 10, BLACK);
            DrawTexture(texPattern, 2 + MARGIN_SIZE, 40 + MARGIN_SIZE, BLACK);
            DrawRectangle(2 + MARGIN_SIZE + (int)recPattern[activePattern].x, 40 + MARGIN_SIZE + (int)recPattern[activePattern].y, (int)recPattern[activePattern].width, (int)recPattern[activePattern].height, ColorAlpha(DARKBLUE, 0.3f));

            DrawText("Select Color", 2+MARGIN_SIZE, 10+256+MARGIN_SIZE, 10, BLACK);
            for (int i = 0; i < MAX_COLORS; i++)
            {
                DrawRectangleRec(colorRec[i], colors[i]);
                if (activeCol == i) DrawRectangleLinesEx(colorRec[i], 3, ColorAlpha(WHITE, 0.5f));
            }

            DrawText("Scale (UP/DOWN to change)", 2 + MARGIN_SIZE, 80 + 256 + MARGIN_SIZE, 10, BLACK);
            DrawText(TextFormat("%.2fx", scale), 2 + MARGIN_SIZE, 92 + 256 + MARGIN_SIZE, 20, BLACK);

            DrawText("Rotation (LEFT/RIGHT to change)", 2 + MARGIN_SIZE, 122 + 256 + MARGIN_SIZE, 10, BLACK);
            DrawText(TextFormat("%.0f degrees", rotation), 2 + MARGIN_SIZE, 134 + 256 + MARGIN_SIZE, 20, BLACK);

            DrawText("Press [SPACE] to reset", 2 + MARGIN_SIZE, 164 + 256 + MARGIN_SIZE, 10, DARKBLUE);

            // Draw FPS
            DrawText(TextFormat("%i FPS", GetFPS()), 2 + MARGIN_SIZE, 2 + MARGIN_SIZE, 20, BLACK);
        EndDrawing();
        //----------------------------------------------------------------------------------
    }

    // De-Initialization
    //--------------------------------------------------------------------------------------
    UnloadTexture(texPattern);  // Unload texture

    CloseWindow();              // Close window and OpenGL context
    //--------------------------------------------------------------------------------------

    return 0;
}

// Draw part of a texture (defined by a rectangle) with rotation and scale tiled into dest
void DrawTextureTiled(Texture2D texture, Rectangle source, Rectangle dest, Vector2 origin, float rotation, float scale, Color tint)
{
    if ((texture.id <= 0) || (scale <= 0.0f)) return;  // Wanna see a infinite loop?!...just delete this line!
    if ((source.width == 0) || (source.height == 0)) return;

    int tileWidth = (int)(source.width*scale), tileHeight = (int)(source.height*scale);
    if ((dest.width < tileWidth) && (dest.height < tileHeight))
    {
        // Can fit only one tile
        DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height},
                    (Rectangle){dest.x, dest.y, dest.width, dest.height}, origin, rotation, tint);
    }
    else if (dest.width <= tileWidth)
    {
        // Tiled vertically (one column)
        int dy = 0;
        for (;dy+tileHeight < dest.height; dy += tileHeight)
        {
            DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, source.height}, (Rectangle){dest.x, dest.y + dy, dest.width, (float)tileHeight}, origin, rotation, tint);
        }

        // Fit last tile
        if (dy < dest.height)
        {
            DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)dest.width/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
                        (Rectangle){dest.x, dest.y + dy, dest.width, dest.height - dy}, origin, rotation, tint);
        }
    }
    else if (dest.height <= tileHeight)
    {
        // Tiled horizontally (one row)
        int dx = 0;
        for (;dx+tileWidth < dest.width; dx += tileWidth)
        {
            DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)dest.height/tileHeight)*source.height}, (Rectangle){dest.x + dx, dest.y, (float)tileWidth, dest.height}, origin, rotation, tint);
        }

        // Fit last tile
        if (dx < dest.width)
        {
            DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)dest.height/tileHeight)*source.height},
                        (Rectangle){dest.x + dx, dest.y, dest.width - dx, dest.height}, origin, rotation, tint);
        }
    }
    else
    {
        // Tiled both horizontally and vertically (rows and columns)
        int dx = 0;
        for (;dx+tileWidth < dest.width; dx += tileWidth)
        {
            int dy = 0;
            for (;dy+tileHeight < dest.height; dy += tileHeight)
            {
                DrawTexturePro(texture, source, (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, (float)tileHeight}, origin, rotation, tint);
            }

            if (dy < dest.height)
            {
                DrawTexturePro(texture, (Rectangle){source.x, source.y, source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
                    (Rectangle){dest.x + dx, dest.y + dy, (float)tileWidth, dest.height - dy}, origin, rotation, tint);
            }
        }

        // Fit last column of tiles
        if (dx < dest.width)
        {
            int dy = 0;
            for (;dy+tileHeight < dest.height; dy += tileHeight)
            {
                DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, source.height},
                        (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, (float)tileHeight}, origin, rotation, tint);
            }

            // Draw final tile in the bottom right corner
            if (dy < dest.height)
            {
                DrawTexturePro(texture, (Rectangle){source.x, source.y, ((float)(dest.width - dx)/tileWidth)*source.width, ((float)(dest.height - dy)/tileHeight)*source.height},
                    (Rectangle){dest.x + dx, dest.y + dy, dest.width - dx, dest.height - dy}, origin, rotation, tint);
            }
        }
    }
}

From raylib 6.2. The original on GitHub.