Something went wrong. Try again.
Windows, a GPU renderer and text for Zig programs that draw their own interface.
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798// SPDX-FileCopyrightText: © 2026 Jeffrey C. Ollie <jeff@ocjtech.us>// SPDX-License-Identifier: MIT
//! Images, as the renderer takes them: each a texture of its own, named by//! the application, and uploaded only when the renderer does not already//! hold the pixels asked for.
const std = @import("std");const Allocator = std.mem.Allocator;
/// An image as the renderer holds it. What the two halves mean is the/// application's: harrier's are a pane, and a screen and image number/// within it.pub const Key = struct { owner: u64, id: u64,};
/// An image's pixels, for a renderer that does not hold them.pub const Upload = struct { key: Key, generation: u64, width: u32, height: u32, format: Format, /// Owned. data: []u8,
pub const Format = enum { rgb, rgba, gray, gray_alpha };
/// Writes the pixels to `out` as premultiplied RGBA, four bytes each: /// what a texture filtered as it is scaled wants. `out` holds at least /// `width * height * 4` bytes. pub fn toRgba(u: *const Upload, out: []u8) void { const n: usize = @as(usize, u.width) * u.height; switch (u.format) { .rgba => for (0..n) |i| { const px = u.data[i * 4 ..][0..4]; out[i * 4 ..][0..4].* = .{ mul(px[0], px[3]), mul(px[1], px[3]), mul(px[2], px[3]), px[3] }; }, .rgb => for (0..n) |i| { out[i * 4 ..][0..3].* = u.data[i * 3 ..][0..3].*; out[i * 4 + 3] = 255; }, .gray => for (0..n) |i| { const g = u.data[i]; out[i * 4 ..][0..4].* = .{ g, g, g, 255 }; }, .gray_alpha => for (0..n) |i| { const a = u.data[i * 2 + 1]; const g = mul(u.data[i * 2], a); out[i * 4 ..][0..4].* = .{ g, g, g, a }; }, } }
fn mul(c: u8, a: u8) u8 { return @intCast((@as(u16, c) * a + 127) / 255); }
test toRgba { // One white pixel at half alpha, and one opaque red one. var data = [_]u8{ 255, 255, 255, 128, 255, 0, 0, 255 }; const u: Upload = .{ .key = .{ .owner = 0, .id = 1 }, .generation = 0, .width = 2, .height = 1, .format = .rgba, .data = &data }; var out: [8]u8 = undefined; u.toRgba(&out); try std.testing.expectEqualSlices(u8, &.{ 128, 128, 128, 128, 255, 0, 0, 255 }, &out); }
/// Frees `data` with `gpa`, which is what it was allocated with. pub fn deinit(u: *Upload, gpa: Allocator) void { gpa.free(u.data); u.* = undefined; }
test deinit { const gpa = std.testing.allocator; var u: Upload = .{ .key = .{ .owner = 0, .id = 1 }, .generation = 0, .width = 1, .height = 1, .format = .gray, .data = try gpa.dupe(u8, &.{200}), }; defer u.deinit(gpa); var out: [4]u8 = undefined; u.toRgba(&out); try std.testing.expectEqualSlices(u8, &.{ 200, 200, 200, 255 }, &out); }};
/// What a renderer already holds: whether it has `key` at `generation`.pub const Held = struct { context: *const anyopaque, has: *const fn (context: *const anyopaque, key: Key, generation: u64) bool,};