mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-10-17 15:21:50 +00:00
renderer: unify image.zig
The code in metal/image.zig and opengl/image.zig was virtually identical save for the texture options, so I've moved that to the GraphicsAPI and unified them in to renderer/image.zig
This commit is contained in:
@@ -3,6 +3,7 @@ const std = @import("std");
|
||||
pub const png = @import("png.zig");
|
||||
pub const jpeg = @import("jpeg.zig");
|
||||
pub const swizzle = @import("swizzle.zig");
|
||||
pub const Error = @import("error.zig").Error;
|
||||
|
||||
pub const ImageData = struct {
|
||||
width: u32,
|
||||
|
@@ -29,8 +29,6 @@ pub const Buffer = bufferpkg.Buffer;
|
||||
pub const Texture = @import("metal/Texture.zig");
|
||||
pub const shaders = @import("metal/shaders.zig");
|
||||
|
||||
pub const imagepkg = @import("metal/image.zig");
|
||||
|
||||
pub const custom_shader_target: shadertoy.Target = .msl;
|
||||
// The fragCoord for Metal shaders is +Y = down.
|
||||
pub const custom_shader_y_is_down = true;
|
||||
@@ -304,6 +302,44 @@ pub inline fn textureOptions(self: Metal) Texture.Options {
|
||||
};
|
||||
}
|
||||
|
||||
/// Pixel format for image texture options.
|
||||
pub const ImageTextureFormat = enum {
|
||||
/// 1 byte per pixel grayscale.
|
||||
gray,
|
||||
/// 4 bytes per pixel RGBA.
|
||||
rgba,
|
||||
/// 4 bytes per pixel BGRA.
|
||||
bgra,
|
||||
|
||||
fn toPixelFormat(
|
||||
self: ImageTextureFormat,
|
||||
srgb: bool,
|
||||
) mtl.MTLPixelFormat {
|
||||
return switch (self) {
|
||||
.gray => if (srgb) .r8unorm_srgb else .r8unorm,
|
||||
.rgba => if (srgb) .rgba8unorm_srgb else .rgba8unorm,
|
||||
.bgra => if (srgb) .bgra8unorm_srgb else .bgra8unorm,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns the options to use when constructing textures for images.
|
||||
pub inline fn imageTextureOptions(
|
||||
self: Metal,
|
||||
format: ImageTextureFormat,
|
||||
srgb: bool,
|
||||
) Texture.Options {
|
||||
return .{
|
||||
.device = self.device,
|
||||
.pixel_format = format.toPixelFormat(srgb),
|
||||
.resource_options = .{
|
||||
// Indicate that the CPU writes to this resource but never reads it.
|
||||
.cpu_cache_mode = .write_combined,
|
||||
.storage_mode = self.default_storage_mode,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/// Initializes a Texture suitable for the provided font atlas.
|
||||
pub fn initAtlasTexture(
|
||||
self: *const Metal,
|
||||
|
@@ -24,8 +24,6 @@ pub const Buffer = bufferpkg.Buffer;
|
||||
pub const Texture = @import("opengl/Texture.zig");
|
||||
pub const shaders = @import("opengl/shaders.zig");
|
||||
|
||||
pub const imagepkg = @import("opengl/image.zig");
|
||||
|
||||
pub const custom_shader_target: shadertoy.Target = .glsl;
|
||||
// The fragCoord for OpenGL shaders is +Y = up.
|
||||
pub const custom_shader_y_is_down = false;
|
||||
@@ -401,6 +399,38 @@ pub inline fn textureOptions(self: OpenGL) Texture.Options {
|
||||
};
|
||||
}
|
||||
|
||||
/// Pixel format for image texture options.
|
||||
pub const ImageTextureFormat = enum {
|
||||
/// 1 byte per pixel grayscale.
|
||||
gray,
|
||||
/// 4 bytes per pixel RGBA.
|
||||
rgba,
|
||||
/// 4 bytes per pixel BGRA.
|
||||
bgra,
|
||||
|
||||
fn toPixelFormat(self: ImageTextureFormat) gl.Texture.Format {
|
||||
return switch (self) {
|
||||
.gray => .red,
|
||||
.rgba => .rgba,
|
||||
.bgra => .bgra,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// Returns the options to use when constructing textures for images.
|
||||
pub inline fn imageTextureOptions(
|
||||
self: OpenGL,
|
||||
format: ImageTextureFormat,
|
||||
srgb: bool,
|
||||
) Texture.Options {
|
||||
_ = self;
|
||||
return .{
|
||||
.format = format.toPixelFormat(),
|
||||
.internal_format = if (srgb) .srgba else .rgba,
|
||||
.target = .Rectangle,
|
||||
};
|
||||
}
|
||||
|
||||
/// Initializes a Texture suitable for the provided font atlas.
|
||||
pub fn initAtlasTexture(
|
||||
self: *const OpenGL,
|
||||
|
@@ -14,6 +14,10 @@ const link = @import("link.zig");
|
||||
const cellpkg = @import("cell.zig");
|
||||
const fgMode = cellpkg.fgMode;
|
||||
const isCovering = cellpkg.isCovering;
|
||||
const imagepkg = @import("image.zig");
|
||||
const Image = imagepkg.Image;
|
||||
const ImageMap = imagepkg.ImageMap;
|
||||
const ImagePlacementList = std.ArrayListUnmanaged(imagepkg.Placement);
|
||||
const shadertoy = @import("shadertoy.zig");
|
||||
const assert = std.debug.assert;
|
||||
const Allocator = std.mem.Allocator;
|
||||
@@ -78,16 +82,10 @@ pub fn Renderer(comptime GraphicsAPI: type) type {
|
||||
const Buffer = GraphicsAPI.Buffer;
|
||||
const Texture = GraphicsAPI.Texture;
|
||||
const RenderPass = GraphicsAPI.RenderPass;
|
||||
|
||||
const shaderpkg = GraphicsAPI.shaders;
|
||||
|
||||
const imagepkg = GraphicsAPI.imagepkg;
|
||||
const Image = imagepkg.Image;
|
||||
const ImageMap = imagepkg.ImageMap;
|
||||
|
||||
const Shaders = shaderpkg.Shaders;
|
||||
|
||||
const ImagePlacementList = std.ArrayListUnmanaged(imagepkg.Placement);
|
||||
|
||||
/// Allocator that can be used
|
||||
alloc: std.mem.Allocator,
|
||||
|
||||
|
@@ -1,16 +1,14 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const objc = @import("objc");
|
||||
const wuffs = @import("wuffs");
|
||||
|
||||
const Metal = @import("../Metal.zig");
|
||||
const Texture = Metal.Texture;
|
||||
const Renderer = @import("../renderer.zig").Renderer;
|
||||
const GraphicsAPI = Renderer.API;
|
||||
const Texture = GraphicsAPI.Texture;
|
||||
|
||||
const mtl = @import("api.zig");
|
||||
|
||||
/// Represents a single image placement on the grid. A placement is a
|
||||
/// request to render an instance of an image.
|
||||
/// Represents a single image placement on the grid.
|
||||
/// A placement is a request to render an instance of an image.
|
||||
pub const Placement = struct {
|
||||
/// The image being rendered. This MUST be in the image map.
|
||||
image_id: u32,
|
||||
@@ -174,8 +172,8 @@ pub const Image = union(enum) {
|
||||
// scenarios where there is no existing texture and we can modify
|
||||
// the self pointer directly.
|
||||
const existing: Texture = switch (self.*) {
|
||||
// For pending, we can free the old data and become pending
|
||||
// ourselves.
|
||||
// For pending, we can free the old
|
||||
// data and become pending ourselves.
|
||||
.pending_gray => |p| {
|
||||
alloc.free(p.dataSlice(1));
|
||||
self.* = img;
|
||||
@@ -214,8 +212,8 @@ pub const Image = union(enum) {
|
||||
break :existing r[1];
|
||||
},
|
||||
|
||||
// If we were already pending a replacement, then we free our
|
||||
// existing pending data and use the same texture.
|
||||
// If we were already pending a replacement, then we free
|
||||
// our existing pending data and use the same texture.
|
||||
.replace_gray => |r| existing: {
|
||||
alloc.free(r.pending.dataSlice(1));
|
||||
break :existing r.texture;
|
||||
@@ -236,9 +234,9 @@ pub const Image = union(enum) {
|
||||
break :existing r.texture;
|
||||
},
|
||||
|
||||
// For both ready and unload_ready, we need to replace the
|
||||
// texture. We can't do that here, so we just mark ourselves
|
||||
// for replacement.
|
||||
// For both ready and unload_ready, we need to replace
|
||||
// the texture. We can't do that here, so we just mark
|
||||
// ourselves for replacement.
|
||||
.ready, .unload_ready => |tex| tex,
|
||||
};
|
||||
|
||||
@@ -281,6 +279,8 @@ pub const Image = union(enum) {
|
||||
=> true,
|
||||
|
||||
.ready,
|
||||
.pending_gray,
|
||||
.pending_gray_alpha,
|
||||
.pending_rgb,
|
||||
.pending_rgba,
|
||||
=> false,
|
||||
@@ -290,7 +290,10 @@ pub const Image = union(enum) {
|
||||
/// Converts the image data to a format that can be uploaded to the GPU.
|
||||
/// If the data is already in a format that can be uploaded, this is a
|
||||
/// no-op.
|
||||
pub fn convert(self: *Image, alloc: Allocator) !void {
|
||||
pub fn convert(self: *Image, alloc: Allocator) wuffs.Error!void {
|
||||
// As things stand, we currently convert all images to RGBA before
|
||||
// uploading to the GPU. This just makes things easier. In the future
|
||||
// we may want to support other formats.
|
||||
switch (self.*) {
|
||||
.ready,
|
||||
.unload_pending,
|
||||
@@ -302,8 +305,6 @@ pub const Image = union(enum) {
|
||||
.replace_rgba,
|
||||
=> {}, // ready
|
||||
|
||||
// RGB needs to be converted to RGBA because Metal textures
|
||||
// don't support RGB.
|
||||
.pending_rgb => |*p| {
|
||||
const data = p.dataSlice(3);
|
||||
const rgba = try wuffs.swizzle.rgbToRgba(alloc, data);
|
||||
@@ -320,7 +321,6 @@ pub const Image = union(enum) {
|
||||
self.* = .{ .replace_rgba = r.* };
|
||||
},
|
||||
|
||||
// Gray and Gray+Alpha need to be converted to RGBA, too.
|
||||
.pending_gray => |*p| {
|
||||
const data = p.dataSlice(1);
|
||||
const rgba = try wuffs.swizzle.gToRgba(alloc, data);
|
||||
@@ -360,11 +360,8 @@ pub const Image = union(enum) {
|
||||
pub fn upload(
|
||||
self: *Image,
|
||||
alloc: Allocator,
|
||||
metal: *const Metal,
|
||||
api: *const GraphicsAPI,
|
||||
) !void {
|
||||
const device = metal.device;
|
||||
const storage_mode = metal.default_storage_mode;
|
||||
|
||||
// Convert our data if we have to
|
||||
try self.convert(alloc);
|
||||
|
||||
@@ -373,15 +370,7 @@ pub const Image = union(enum) {
|
||||
|
||||
// Create our texture
|
||||
const texture = try Texture.init(
|
||||
.{
|
||||
.device = device,
|
||||
.pixel_format = .rgba8unorm_srgb,
|
||||
.resource_options = .{
|
||||
// Indicate that the CPU writes to this resource but never reads it.
|
||||
.cpu_cache_mode = .write_combined,
|
||||
.storage_mode = storage_mode,
|
||||
},
|
||||
},
|
||||
api.imageTextureOptions(.rgba, true),
|
||||
@intCast(p.width),
|
||||
@intCast(p.height),
|
||||
p.data[0 .. p.width * p.height * self.depth()],
|
@@ -1,423 +0,0 @@
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
const gl = @import("opengl");
|
||||
const wuffs = @import("wuffs");
|
||||
const OpenGL = @import("../OpenGL.zig");
|
||||
const Texture = OpenGL.Texture;
|
||||
|
||||
/// Represents a single image placement on the grid. A placement is a
|
||||
/// request to render an instance of an image.
|
||||
pub const Placement = struct {
|
||||
/// The image being rendered. This MUST be in the image map.
|
||||
image_id: u32,
|
||||
|
||||
/// The grid x/y where this placement is located.
|
||||
x: i32,
|
||||
y: i32,
|
||||
z: i32,
|
||||
|
||||
/// The width/height of the placed image.
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
||||
/// The offset in pixels from the top left of the cell. This is
|
||||
/// clamped to the size of a cell.
|
||||
cell_offset_x: u32,
|
||||
cell_offset_y: u32,
|
||||
|
||||
/// The source rectangle of the placement.
|
||||
source_x: u32,
|
||||
source_y: u32,
|
||||
source_width: u32,
|
||||
source_height: u32,
|
||||
};
|
||||
|
||||
/// The map used for storing images.
|
||||
pub const ImageMap = std.AutoHashMapUnmanaged(u32, struct {
|
||||
image: Image,
|
||||
transmit_time: std.time.Instant,
|
||||
});
|
||||
|
||||
/// The state for a single image that is to be rendered. The image can be
|
||||
/// pending upload or ready to use with a texture.
|
||||
pub const Image = union(enum) {
|
||||
/// The image is pending upload to the GPU. The different keys are
|
||||
/// different formats since some formats aren't accepted by the GPU
|
||||
/// and require conversion.
|
||||
///
|
||||
/// This data is owned by this union so it must be freed once the
|
||||
/// image is uploaded.
|
||||
pending_gray: Pending,
|
||||
pending_gray_alpha: Pending,
|
||||
pending_rgb: Pending,
|
||||
pending_rgba: Pending,
|
||||
|
||||
/// This is the same as the pending states but there is a texture
|
||||
/// already allocated that we want to replace.
|
||||
replace_gray: Replace,
|
||||
replace_gray_alpha: Replace,
|
||||
replace_rgb: Replace,
|
||||
replace_rgba: Replace,
|
||||
|
||||
/// The image is uploaded and ready to be used.
|
||||
ready: Texture,
|
||||
|
||||
/// The image is uploaded but is scheduled to be unloaded.
|
||||
unload_pending: []u8,
|
||||
unload_ready: Texture,
|
||||
unload_replace: struct { []u8, Texture },
|
||||
|
||||
pub const Replace = struct {
|
||||
texture: Texture,
|
||||
pending: Pending,
|
||||
};
|
||||
|
||||
/// Pending image data that needs to be uploaded to the GPU.
|
||||
pub const Pending = struct {
|
||||
height: u32,
|
||||
width: u32,
|
||||
|
||||
/// Data is always expected to be (width * height * depth). Depth
|
||||
/// is based on the union key.
|
||||
data: [*]u8,
|
||||
|
||||
pub fn dataSlice(self: Pending, d: u32) []u8 {
|
||||
return self.data[0..self.len(d)];
|
||||
}
|
||||
|
||||
pub fn len(self: Pending, d: u32) u32 {
|
||||
return self.width * self.height * d;
|
||||
}
|
||||
};
|
||||
|
||||
pub fn deinit(self: Image, alloc: Allocator) void {
|
||||
switch (self) {
|
||||
.pending_gray => |p| alloc.free(p.dataSlice(1)),
|
||||
.pending_gray_alpha => |p| alloc.free(p.dataSlice(2)),
|
||||
.pending_rgb => |p| alloc.free(p.dataSlice(3)),
|
||||
.pending_rgba => |p| alloc.free(p.dataSlice(4)),
|
||||
.unload_pending => |data| alloc.free(data),
|
||||
|
||||
.replace_gray => |r| {
|
||||
alloc.free(r.pending.dataSlice(1));
|
||||
r.texture.deinit();
|
||||
},
|
||||
|
||||
.replace_gray_alpha => |r| {
|
||||
alloc.free(r.pending.dataSlice(2));
|
||||
r.texture.deinit();
|
||||
},
|
||||
|
||||
.replace_rgb => |r| {
|
||||
alloc.free(r.pending.dataSlice(3));
|
||||
r.texture.deinit();
|
||||
},
|
||||
|
||||
.replace_rgba => |r| {
|
||||
alloc.free(r.pending.dataSlice(4));
|
||||
r.texture.deinit();
|
||||
},
|
||||
|
||||
.unload_replace => |r| {
|
||||
alloc.free(r[0]);
|
||||
r[1].deinit();
|
||||
},
|
||||
|
||||
.ready,
|
||||
.unload_ready,
|
||||
=> |tex| tex.deinit(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark this image for unload whatever state it is in.
|
||||
pub fn markForUnload(self: *Image) void {
|
||||
self.* = switch (self.*) {
|
||||
.unload_pending,
|
||||
.unload_replace,
|
||||
.unload_ready,
|
||||
=> return,
|
||||
|
||||
.ready => |obj| .{ .unload_ready = obj },
|
||||
.pending_gray => |p| .{ .unload_pending = p.dataSlice(1) },
|
||||
.pending_gray_alpha => |p| .{ .unload_pending = p.dataSlice(2) },
|
||||
.pending_rgb => |p| .{ .unload_pending = p.dataSlice(3) },
|
||||
.pending_rgba => |p| .{ .unload_pending = p.dataSlice(4) },
|
||||
.replace_gray => |r| .{ .unload_replace = .{
|
||||
r.pending.dataSlice(1), r.texture,
|
||||
} },
|
||||
.replace_gray_alpha => |r| .{ .unload_replace = .{
|
||||
r.pending.dataSlice(2), r.texture,
|
||||
} },
|
||||
.replace_rgb => |r| .{ .unload_replace = .{
|
||||
r.pending.dataSlice(3), r.texture,
|
||||
} },
|
||||
.replace_rgba => |r| .{ .unload_replace = .{
|
||||
r.pending.dataSlice(4), r.texture,
|
||||
} },
|
||||
};
|
||||
}
|
||||
|
||||
/// Replace the currently pending image with a new one. This will
|
||||
/// attempt to update the existing texture if it is already allocated.
|
||||
/// If the texture is not allocated, this will act like a new upload.
|
||||
///
|
||||
/// This function only marks the image for replace. The actual logic
|
||||
/// to replace is done later.
|
||||
pub fn markForReplace(self: *Image, alloc: Allocator, img: Image) !void {
|
||||
assert(img.pending() != null);
|
||||
|
||||
// Get our existing texture. This switch statement will also handle
|
||||
// scenarios where there is no existing texture and we can modify
|
||||
// the self pointer directly.
|
||||
const existing: Texture = switch (self.*) {
|
||||
// For pending, we can free the old data and become pending ourselves.
|
||||
.pending_gray => |p| {
|
||||
alloc.free(p.dataSlice(1));
|
||||
self.* = img;
|
||||
return;
|
||||
},
|
||||
|
||||
.pending_gray_alpha => |p| {
|
||||
alloc.free(p.dataSlice(2));
|
||||
self.* = img;
|
||||
return;
|
||||
},
|
||||
|
||||
.pending_rgb => |p| {
|
||||
alloc.free(p.dataSlice(3));
|
||||
self.* = img;
|
||||
return;
|
||||
},
|
||||
|
||||
.pending_rgba => |p| {
|
||||
alloc.free(p.dataSlice(4));
|
||||
self.* = img;
|
||||
return;
|
||||
},
|
||||
|
||||
// If we're marked for unload but we just have pending data,
|
||||
// this behaves the same as a normal "pending": free the data,
|
||||
// become new pending.
|
||||
.unload_pending => |data| {
|
||||
alloc.free(data);
|
||||
self.* = img;
|
||||
return;
|
||||
},
|
||||
|
||||
.unload_replace => |r| existing: {
|
||||
alloc.free(r[0]);
|
||||
break :existing r[1];
|
||||
},
|
||||
|
||||
// If we were already pending a replacement, then we free our
|
||||
// existing pending data and use the same texture.
|
||||
.replace_gray => |r| existing: {
|
||||
alloc.free(r.pending.dataSlice(1));
|
||||
break :existing r.texture;
|
||||
},
|
||||
|
||||
.replace_gray_alpha => |r| existing: {
|
||||
alloc.free(r.pending.dataSlice(2));
|
||||
break :existing r.texture;
|
||||
},
|
||||
|
||||
.replace_rgb => |r| existing: {
|
||||
alloc.free(r.pending.dataSlice(3));
|
||||
break :existing r.texture;
|
||||
},
|
||||
|
||||
.replace_rgba => |r| existing: {
|
||||
alloc.free(r.pending.dataSlice(4));
|
||||
break :existing r.texture;
|
||||
},
|
||||
|
||||
// For both ready and unload_ready, we need to replace the
|
||||
// texture. We can't do that here, so we just mark ourselves
|
||||
// for replacement.
|
||||
.ready, .unload_ready => |tex| tex,
|
||||
};
|
||||
|
||||
// We now have an existing texture, so set the proper replace key.
|
||||
self.* = switch (img) {
|
||||
.pending_gray => |p| .{ .replace_gray = .{
|
||||
.texture = existing,
|
||||
.pending = p,
|
||||
} },
|
||||
|
||||
.pending_gray_alpha => |p| .{ .replace_gray_alpha = .{
|
||||
.texture = existing,
|
||||
.pending = p,
|
||||
} },
|
||||
|
||||
.pending_rgb => |p| .{ .replace_rgb = .{
|
||||
.texture = existing,
|
||||
.pending = p,
|
||||
} },
|
||||
|
||||
.pending_rgba => |p| .{ .replace_rgba = .{
|
||||
.texture = existing,
|
||||
.pending = p,
|
||||
} },
|
||||
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true if this image is pending upload.
|
||||
pub fn isPending(self: Image) bool {
|
||||
return self.pending() != null;
|
||||
}
|
||||
|
||||
/// Returns true if this image is pending an unload.
|
||||
pub fn isUnloading(self: Image) bool {
|
||||
return switch (self) {
|
||||
.unload_pending,
|
||||
.unload_ready,
|
||||
=> true,
|
||||
|
||||
.ready,
|
||||
.pending_gray,
|
||||
.pending_gray_alpha,
|
||||
.pending_rgb,
|
||||
.pending_rgba,
|
||||
=> false,
|
||||
};
|
||||
}
|
||||
|
||||
/// Converts the image data to a format that can be uploaded to the GPU.
|
||||
/// If the data is already in a format that can be uploaded, this is a
|
||||
/// no-op.
|
||||
pub fn convert(self: *Image, alloc: Allocator) !void {
|
||||
switch (self.*) {
|
||||
.ready,
|
||||
.unload_pending,
|
||||
.unload_replace,
|
||||
.unload_ready,
|
||||
=> unreachable, // invalid
|
||||
|
||||
.pending_rgba,
|
||||
.replace_rgba,
|
||||
=> {}, // ready
|
||||
|
||||
// RGB needs to be converted to RGBA because Metal textures
|
||||
// don't support RGB.
|
||||
.pending_rgb => |*p| {
|
||||
const data = p.dataSlice(3);
|
||||
const rgba = try wuffs.swizzle.rgbToRgba(alloc, data);
|
||||
alloc.free(data);
|
||||
p.data = rgba.ptr;
|
||||
self.* = .{ .pending_rgba = p.* };
|
||||
},
|
||||
|
||||
.replace_rgb => |*r| {
|
||||
const data = r.pending.dataSlice(3);
|
||||
const rgba = try wuffs.swizzle.rgbToRgba(alloc, data);
|
||||
alloc.free(data);
|
||||
r.pending.data = rgba.ptr;
|
||||
self.* = .{ .replace_rgba = r.* };
|
||||
},
|
||||
|
||||
// Gray and Gray+Alpha need to be converted to RGBA, too.
|
||||
.pending_gray => |*p| {
|
||||
const data = p.dataSlice(1);
|
||||
const rgba = try wuffs.swizzle.gToRgba(alloc, data);
|
||||
alloc.free(data);
|
||||
p.data = rgba.ptr;
|
||||
self.* = .{ .pending_rgba = p.* };
|
||||
},
|
||||
|
||||
.replace_gray => |*r| {
|
||||
const data = r.pending.dataSlice(2);
|
||||
const rgba = try wuffs.swizzle.gToRgba(alloc, data);
|
||||
alloc.free(data);
|
||||
r.pending.data = rgba.ptr;
|
||||
self.* = .{ .replace_rgba = r.* };
|
||||
},
|
||||
|
||||
.pending_gray_alpha => |*p| {
|
||||
const data = p.dataSlice(2);
|
||||
const rgba = try wuffs.swizzle.gaToRgba(alloc, data);
|
||||
alloc.free(data);
|
||||
p.data = rgba.ptr;
|
||||
self.* = .{ .pending_rgba = p.* };
|
||||
},
|
||||
|
||||
.replace_gray_alpha => |*r| {
|
||||
const data = r.pending.dataSlice(2);
|
||||
const rgba = try wuffs.swizzle.gaToRgba(alloc, data);
|
||||
alloc.free(data);
|
||||
r.pending.data = rgba.ptr;
|
||||
self.* = .{ .replace_rgba = r.* };
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Upload the pending image to the GPU and change the state of this
|
||||
/// image to ready.
|
||||
pub fn upload(
|
||||
self: *Image,
|
||||
alloc: Allocator,
|
||||
opengl: *const OpenGL,
|
||||
) !void {
|
||||
_ = opengl;
|
||||
|
||||
// Convert our data if we have to
|
||||
try self.convert(alloc);
|
||||
|
||||
// Get our pending info
|
||||
const p = self.pending().?;
|
||||
|
||||
// Get our format
|
||||
const formats: struct {
|
||||
internal: gl.Texture.InternalFormat,
|
||||
format: gl.Texture.Format,
|
||||
} = switch (self.*) {
|
||||
.pending_rgb, .replace_rgb => .{ .internal = .srgb, .format = .rgb },
|
||||
.pending_rgba, .replace_rgba => .{ .internal = .srgba, .format = .rgba },
|
||||
else => unreachable,
|
||||
};
|
||||
|
||||
// Create our texture
|
||||
const tex = try Texture.init(
|
||||
.{
|
||||
.format = formats.format,
|
||||
.internal_format = formats.internal,
|
||||
.target = .Rectangle,
|
||||
},
|
||||
@intCast(p.width),
|
||||
@intCast(p.height),
|
||||
p.data[0 .. p.width * p.height * self.depth()],
|
||||
);
|
||||
|
||||
// Uploaded. We can now clear our data and change our state.
|
||||
self.deinit(alloc);
|
||||
self.* = .{ .ready = tex };
|
||||
}
|
||||
|
||||
/// Our pixel depth
|
||||
fn depth(self: Image) u32 {
|
||||
return switch (self) {
|
||||
.pending_rgb => 3,
|
||||
.pending_rgba => 4,
|
||||
.replace_rgb => 3,
|
||||
.replace_rgba => 4,
|
||||
else => unreachable,
|
||||
};
|
||||
}
|
||||
|
||||
/// Returns true if this image is in a pending state and requires upload.
|
||||
fn pending(self: Image) ?Pending {
|
||||
return switch (self) {
|
||||
.pending_rgb,
|
||||
.pending_rgba,
|
||||
=> |p| p,
|
||||
|
||||
.replace_rgb,
|
||||
.replace_rgba,
|
||||
=> |r| r.pending,
|
||||
|
||||
else => null,
|
||||
};
|
||||
}
|
||||
};
|
Reference in New Issue
Block a user