ghostty/src/renderer/opengl/Texture.zig
Qwerasd 87f35bd1c1 renderer/opengl: explicit texture options
This sets up for a couple improvments (see TODO comments) and also sets
the glyph atlas textures to nearest neighbor sampling since we can do
that now that we never scale glyphs.
2025-07-05 22:10:39 -06:00

107 lines
2.7 KiB
Zig

//! Wrapper for handling textures.
const Self = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const builtin = @import("builtin");
const gl = @import("opengl");
const OpenGL = @import("../OpenGL.zig");
const log = std.log.scoped(.opengl);
/// Options for initializing a texture.
pub const Options = struct {
format: gl.Texture.Format,
internal_format: gl.Texture.InternalFormat,
target: gl.Texture.Target,
min_filter: gl.Texture.MinFilter,
mag_filter: gl.Texture.MagFilter,
wrap_s: gl.Texture.Wrap,
wrap_t: gl.Texture.Wrap,
};
texture: gl.Texture,
/// The width of this texture.
width: usize,
/// The height of this texture.
height: usize,
/// Format for this texture.
format: gl.Texture.Format,
/// Target for this texture.
target: gl.Texture.Target,
pub const Error = error{
/// An OpenGL API call failed.
OpenGLFailed,
};
/// Initialize a texture
pub fn init(
opts: Options,
width: usize,
height: usize,
data: ?[]const u8,
) Error!Self {
const tex = gl.Texture.create() catch return error.OpenGLFailed;
errdefer tex.destroy();
{
const texbind = tex.bind(opts.target) catch return error.OpenGLFailed;
defer texbind.unbind();
texbind.parameter(.WrapS, @intFromEnum(opts.wrap_s)) catch return error.OpenGLFailed;
texbind.parameter(.WrapT, @intFromEnum(opts.wrap_t)) catch return error.OpenGLFailed;
texbind.parameter(.MinFilter, @intFromEnum(opts.min_filter)) catch return error.OpenGLFailed;
texbind.parameter(.MagFilter, @intFromEnum(opts.mag_filter)) catch return error.OpenGLFailed;
texbind.image2D(
0,
opts.internal_format,
@intCast(width),
@intCast(height),
opts.format,
.UnsignedByte,
if (data) |d| @ptrCast(d.ptr) else null,
) catch return error.OpenGLFailed;
}
return .{
.texture = tex,
.width = width,
.height = height,
.format = opts.format,
.target = opts.target,
};
}
pub fn deinit(self: Self) void {
self.texture.destroy();
}
/// Replace a region of the texture with the provided data.
///
/// Does NOT check the dimensions of the data to ensure correctness.
pub fn replaceRegion(
self: Self,
x: usize,
y: usize,
width: usize,
height: usize,
data: []const u8,
) Error!void {
const texbind = self.texture.bind(self.target) catch return error.OpenGLFailed;
defer texbind.unbind();
texbind.subImage2D(
0,
@intCast(x),
@intCast(y),
@intCast(width),
@intCast(height),
self.format,
.UnsignedByte,
data.ptr,
) catch return error.OpenGLFailed;
}