mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
Add new background image modes
This commit is contained in:
@ -30,8 +30,10 @@ pub const RepeatableFontVariation = Config.RepeatableFontVariation;
|
|||||||
pub const RepeatableString = Config.RepeatableString;
|
pub const RepeatableString = Config.RepeatableString;
|
||||||
pub const RepeatableStringMap = @import("config/RepeatableStringMap.zig");
|
pub const RepeatableStringMap = @import("config/RepeatableStringMap.zig");
|
||||||
pub const RepeatablePath = Config.RepeatablePath;
|
pub const RepeatablePath = Config.RepeatablePath;
|
||||||
|
pub const SinglePath = Config.SinglePath;
|
||||||
pub const ShellIntegrationFeatures = Config.ShellIntegrationFeatures;
|
pub const ShellIntegrationFeatures = Config.ShellIntegrationFeatures;
|
||||||
pub const WindowPaddingColor = Config.WindowPaddingColor;
|
pub const WindowPaddingColor = Config.WindowPaddingColor;
|
||||||
|
pub const BackgroundImageMode = Config.BackgroundImageMode;
|
||||||
|
|
||||||
// Alternate APIs
|
// Alternate APIs
|
||||||
pub const CAPI = @import("config/CAPI.zig");
|
pub const CAPI = @import("config/CAPI.zig");
|
||||||
|
@ -461,22 +461,22 @@ background: Color = .{ .r = 0x28, .g = 0x2C, .b = 0x34 },
|
|||||||
foreground: Color = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF },
|
foreground: Color = .{ .r = 0xFF, .g = 0xFF, .b = 0xFF },
|
||||||
|
|
||||||
/// Background image for the window.
|
/// Background image for the window.
|
||||||
@"background-image": RepeatablePath = .{},
|
@"background-image": SinglePath = .{},
|
||||||
|
|
||||||
/// Background image opactity
|
/// Background image opactity
|
||||||
@"background-image-opacity": f32 = 0.0,
|
@"background-image-opacity": f32 = 1.0,
|
||||||
|
|
||||||
/// Background image mode to use.
|
/// Background image mode to use.
|
||||||
///
|
///
|
||||||
/// `aspect` keeps the aspect-ratio of the background image and `scaled` scales
|
|
||||||
/// the image to fit the window. `aspect` is the default mode.
|
|
||||||
///
|
|
||||||
/// Valid values are:
|
/// Valid values are:
|
||||||
///
|
///
|
||||||
/// * `aspect`
|
/// * `zoomed` - Image is scaled to fit the window, preserving aspect ratio.
|
||||||
/// * `scaled`
|
/// * `scaled` - Image is scaled to fill the window, not preserving aspect ratio.
|
||||||
|
/// * `tiled` - Image is repeated horizontally and vertically to fill the window.
|
||||||
|
/// * `centered` - Image is centered in the window and displayed 1-to-1 pixel
|
||||||
|
/// scale, preserving both the aspect ratio and the image size.
|
||||||
///
|
///
|
||||||
@"background-image-mode": BackgroundImageProgram.BackgroundMode = .aspect,
|
@"background-image-mode": BackgroundImageMode = .zoomed,
|
||||||
|
|
||||||
/// The foreground and background color for selection. If this is not set, then
|
/// The foreground and background color for selection. If this is not set, then
|
||||||
/// the selection color is just the inverted window background and foreground
|
/// the selection color is just the inverted window background and foreground
|
||||||
@ -4181,6 +4181,84 @@ pub const Palette = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Path is a path to a single file.
|
||||||
|
pub const SinglePath = struct {
|
||||||
|
const Self = @This();
|
||||||
|
|
||||||
|
/// The actual value that is updated as we parse.
|
||||||
|
value: []const u8 = "",
|
||||||
|
|
||||||
|
/// Parse a single path.
|
||||||
|
pub fn parseCLI(self: *Self, alloc: Allocator, input: ?[]const u8) !void {
|
||||||
|
const value = input orelse return error.ValueRequired;
|
||||||
|
const copy = try alloc.dupe(u8, value);
|
||||||
|
self.value = copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deep copy of the struct. Required by Config.
|
||||||
|
pub fn clone(self: Self, alloc: Allocator) Allocator.Error!Self {
|
||||||
|
const copy_path = try alloc.dupe(u8, self.value);
|
||||||
|
return .{
|
||||||
|
.value = copy_path,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Used by Formatter
|
||||||
|
pub fn formatEntry(self: Self, formatter: anytype) !void {
|
||||||
|
try formatter.formatEntry([]const u8, self.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expand(
|
||||||
|
self: *Self,
|
||||||
|
alloc: Allocator,
|
||||||
|
base: []const u8,
|
||||||
|
diags: *cli.DiagnosticList,
|
||||||
|
) !void {
|
||||||
|
assert(std.fs.path.isAbsolute(base));
|
||||||
|
var dir = try std.fs.cwd().openDir(base, .{});
|
||||||
|
defer dir.close();
|
||||||
|
|
||||||
|
const path = self.value;
|
||||||
|
|
||||||
|
// If it is already absolute we can ignore it.
|
||||||
|
if (path.len == 0 or std.fs.path.isAbsolute(path)) return;
|
||||||
|
|
||||||
|
// If it isn't absolute, we need to make it absolute relative
|
||||||
|
// to the base.
|
||||||
|
var buf: [std.fs.max_path_bytes]u8 = undefined;
|
||||||
|
const abs = dir.realpath(path, &buf) catch |err| abs: {
|
||||||
|
if (err == error.FileNotFound) {
|
||||||
|
// The file doesn't exist. Try to resolve the relative path
|
||||||
|
// another way.
|
||||||
|
const resolved = try std.fs.path.resolve(alloc, &.{ base, path });
|
||||||
|
defer alloc.free(resolved);
|
||||||
|
@memcpy(buf[0..resolved.len], resolved);
|
||||||
|
break :abs buf[0..resolved.len];
|
||||||
|
}
|
||||||
|
|
||||||
|
try diags.append(alloc, .{
|
||||||
|
.message = try std.fmt.allocPrintZ(
|
||||||
|
alloc,
|
||||||
|
"error resolving file path {s}: {}",
|
||||||
|
.{ path, err },
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Blank this path so that we don't attempt to resolve it again
|
||||||
|
self.value = "";
|
||||||
|
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
log.debug(
|
||||||
|
"expanding file path relative={s} abs={s}",
|
||||||
|
.{ path, abs },
|
||||||
|
);
|
||||||
|
|
||||||
|
self.value = try alloc.dupeZ(u8, abs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// RepeatableString is a string value that can be repeated to accumulate
|
/// RepeatableString is a string value that can be repeated to accumulate
|
||||||
/// a list of strings. This isn't called "StringList" because I find that
|
/// a list of strings. This isn't called "StringList" because I find that
|
||||||
/// sometimes leads to confusion that it _accepts_ a list such as
|
/// sometimes leads to confusion that it _accepts_ a list such as
|
||||||
@ -6177,6 +6255,14 @@ pub const AlphaBlending = enum {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// See background-image-mode
|
||||||
|
pub const BackgroundImageMode = enum(u8) {
|
||||||
|
zoomed = 0,
|
||||||
|
stretched = 1,
|
||||||
|
tiled = 2,
|
||||||
|
centered = 3,
|
||||||
|
};
|
||||||
|
|
||||||
/// See freetype-load-flag
|
/// See freetype-load-flag
|
||||||
pub const FreetypeLoadFlags = packed struct {
|
pub const FreetypeLoadFlags = packed struct {
|
||||||
// The defaults here at the time of writing this match the defaults
|
// The defaults here at the time of writing this match the defaults
|
||||||
|
@ -4,6 +4,7 @@ pub const OpenGL = @This();
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const glfw = @import("glfw");
|
const glfw = @import("glfw");
|
||||||
|
const wuffs = @import("wuffs");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
const testing = std.testing;
|
const testing = std.testing;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
@ -44,6 +45,9 @@ else
|
|||||||
const DrawMutex = if (single_threaded_draw) std.Thread.Mutex else void;
|
const DrawMutex = if (single_threaded_draw) std.Thread.Mutex else void;
|
||||||
const drawMutexZero: DrawMutex = if (DrawMutex == void) void{} else .{};
|
const drawMutexZero: DrawMutex = if (DrawMutex == void) void{} else .{};
|
||||||
|
|
||||||
|
/// The maximum size of a background image.
|
||||||
|
const max_image_size = 400 * 1024 * 1024; // 400MB
|
||||||
|
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
|
|
||||||
/// The configuration we need derived from the main config.
|
/// The configuration we need derived from the main config.
|
||||||
@ -137,13 +141,13 @@ draw_mutex: DrawMutex = drawMutexZero,
|
|||||||
draw_background: terminal.color.RGB,
|
draw_background: terminal.color.RGB,
|
||||||
|
|
||||||
/// The background image(s) to draw. Currentlly, we always draw the last image.
|
/// The background image(s) to draw. Currentlly, we always draw the last image.
|
||||||
background_image: configpkg.RepeatablePath,
|
background_image: configpkg.SinglePath,
|
||||||
|
|
||||||
/// The opacity of the background image. Not to be confused with background-opacity
|
/// The opacity of the background image. Not to be confused with background-opacity
|
||||||
background_image_opacity: f32,
|
background_image_opacity: f32,
|
||||||
|
|
||||||
/// The background image mode to use.
|
/// The background image mode to use.
|
||||||
background_image_mode: BackgroundImageProgram.BackgroundMode,
|
background_image_mode: configpkg.BackgroundImageMode,
|
||||||
|
|
||||||
/// The current background image to draw. If it is null, then we will not
|
/// The current background image to draw. If it is null, then we will not
|
||||||
/// draw any background image.
|
/// draw any background image.
|
||||||
@ -295,9 +299,9 @@ pub const DerivedConfig = struct {
|
|||||||
cursor_opacity: f64,
|
cursor_opacity: f64,
|
||||||
background: terminal.color.RGB,
|
background: terminal.color.RGB,
|
||||||
background_opacity: f64,
|
background_opacity: f64,
|
||||||
background_image: configpkg.RepeatablePath,
|
background_image: configpkg.SinglePath,
|
||||||
background_image_opacity: f32,
|
background_image_opacity: f32,
|
||||||
background_image_mode: BackgroundImageProgram.BackgroundMode,
|
background_image_mode: configpkg.BackgroundImageMode,
|
||||||
foreground: terminal.color.RGB,
|
foreground: terminal.color.RGB,
|
||||||
selection_background: ?terminal.color.RGB,
|
selection_background: ?terminal.color.RGB,
|
||||||
selection_foreground: ?terminal.color.RGB,
|
selection_foreground: ?terminal.color.RGB,
|
||||||
@ -824,14 +828,13 @@ pub fn updateFrame(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (self.current_background_image == null and
|
if (self.current_background_image == null and
|
||||||
self.background_image.value.items.len > 0)
|
self.background_image.value.len > 0)
|
||||||
{
|
{
|
||||||
if (single_threaded_draw) self.draw_mutex.lock();
|
if (single_threaded_draw) self.draw_mutex.lock();
|
||||||
defer if (single_threaded_draw) self.draw_mutex.unlock();
|
defer if (single_threaded_draw) self.draw_mutex.unlock();
|
||||||
self.prepBackgroundImage() catch |err| switch (err) {
|
self.prepBackgroundImage() catch |err| switch (err) {
|
||||||
error.InvalidData => {
|
error.InvalidData => {
|
||||||
log.warn("invalid image data", .{});
|
log.warn("invalid image data, skipping", .{});
|
||||||
self.current_background_image = null;
|
|
||||||
},
|
},
|
||||||
else => return err,
|
else => return err,
|
||||||
};
|
};
|
||||||
@ -1205,52 +1208,63 @@ fn prepKittyImage(
|
|||||||
/// Prepares the current background image for upload
|
/// Prepares the current background image for upload
|
||||||
pub fn prepBackgroundImage(self: *OpenGL) !void {
|
pub fn prepBackgroundImage(self: *OpenGL) !void {
|
||||||
// If the user doesn't have a background image, do nothing...
|
// If the user doesn't have a background image, do nothing...
|
||||||
const last_image = self.background_image.value.getLastOrNull() orelse return;
|
if (self.background_image.value.len == 0) return;
|
||||||
|
const path = self.background_image.value;
|
||||||
|
|
||||||
// Get the last background image
|
// Read the file content
|
||||||
const path = switch (last_image) {
|
const file_content = try self.readImageContent(path);
|
||||||
.optional, .required => |path| path,
|
defer self.alloc.free(file_content);
|
||||||
};
|
|
||||||
const command = terminal.kitty.graphics.Command{
|
|
||||||
.control = .{
|
|
||||||
.transmit = .{
|
|
||||||
.format = .png,
|
|
||||||
.medium = .file,
|
|
||||||
.width = 0,
|
|
||||||
.height = 0,
|
|
||||||
.compression = .none,
|
|
||||||
.image_id = 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
.data = try self.alloc.dupe(u8, path),
|
|
||||||
};
|
|
||||||
defer command.deinit(self.alloc);
|
|
||||||
|
|
||||||
// Load the iamge
|
// Decode the png (currently, we only support png)
|
||||||
var loading = try terminal.kitty.graphics.LoadingImage.init(self.alloc, &command);
|
const decoded_image = try wuffs.png.decode(self.alloc, file_content);
|
||||||
defer loading.deinit(self.alloc);
|
defer self.alloc.free(decoded_image.data);
|
||||||
|
|
||||||
// Complete the image to get the final data
|
// Copy the data into the pending state
|
||||||
var image = try loading.complete(self.alloc);
|
const data = try self.alloc.dupe(u8, decoded_image.data);
|
||||||
defer image.deinit(self.alloc);
|
|
||||||
|
|
||||||
// Copy the data into the pending state.
|
|
||||||
const data = try self.alloc.dupe(u8, image.data);
|
|
||||||
errdefer self.alloc.free(data);
|
errdefer self.alloc.free(data);
|
||||||
|
|
||||||
const pending: Image.Pending = .{
|
const pending: Image.Pending = .{
|
||||||
.width = image.width,
|
.width = decoded_image.width,
|
||||||
.height = image.height,
|
.height = decoded_image.height,
|
||||||
.data = data.ptr,
|
.data = data.ptr,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.current_background_image = switch (image.format) {
|
// Store the image
|
||||||
.gray => .{ .pending_gray = pending },
|
self.current_background_image = .{ .pending_rgba = pending };
|
||||||
.gray_alpha => .{ .pending_gray_alpha = pending },
|
}
|
||||||
.rgb => .{ .pending_rgb = pending },
|
|
||||||
.rgba => .{ .pending_rgba = pending },
|
/// Reads the content of the given image path and returns it
|
||||||
.png => unreachable, // should be decoded by now
|
pub fn readImageContent(self: *OpenGL, path: []const u8) ![]u8 {
|
||||||
|
// Open the file
|
||||||
|
var file = std.fs.cwd().openFile(path, .{}) catch |err| {
|
||||||
|
log.warn("failed to open file: {}", .{err});
|
||||||
|
return error.InvalidData;
|
||||||
};
|
};
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
// File must be a regular file
|
||||||
|
if (file.stat()) |stat| {
|
||||||
|
if (stat.kind != .file) {
|
||||||
|
log.warn("file is not a regular file kind={}", .{stat.kind});
|
||||||
|
return error.InvalidData;
|
||||||
|
}
|
||||||
|
} else |err| {
|
||||||
|
log.warn("failed to stat file: {}", .{err});
|
||||||
|
return error.InvalidData;
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf_reader = std.io.bufferedReader(file.reader());
|
||||||
|
const reader = buf_reader.reader();
|
||||||
|
|
||||||
|
// Read the file
|
||||||
|
var managed = std.ArrayList(u8).init(self.alloc);
|
||||||
|
errdefer managed.deinit();
|
||||||
|
const size: usize = max_image_size;
|
||||||
|
reader.readAllArrayList(&managed, size) catch |err| {
|
||||||
|
log.warn("failed to read file: {}", .{err});
|
||||||
|
return error.InvalidData;
|
||||||
|
};
|
||||||
|
|
||||||
|
return managed.items;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// rebuildCells rebuilds all the GPU cells from our CPU state. This is a
|
/// rebuildCells rebuilds all the GPU cells from our CPU state. This is a
|
||||||
|
@ -3,6 +3,7 @@ const BackgroundImageProgram = @This();
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const gl = @import("opengl");
|
const gl = @import("opengl");
|
||||||
|
const configpkg = @import("../../config.zig");
|
||||||
|
|
||||||
pub const Input = extern struct {
|
pub const Input = extern struct {
|
||||||
/// vec2 terminal_size
|
/// vec2 terminal_size
|
||||||
@ -10,13 +11,7 @@ pub const Input = extern struct {
|
|||||||
terminal_height: u32 = 0,
|
terminal_height: u32 = 0,
|
||||||
|
|
||||||
/// uint mode
|
/// uint mode
|
||||||
mode: BackgroundMode = .aspect,
|
mode: configpkg.BackgroundImageMode = .zoomed,
|
||||||
};
|
|
||||||
|
|
||||||
pub const BackgroundMode = enum(u8) {
|
|
||||||
aspect = 0,
|
|
||||||
scaled = 1,
|
|
||||||
_,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
program: gl.Program,
|
program: gl.Program,
|
||||||
@ -63,7 +58,7 @@ pub fn init() !BackgroundImageProgram {
|
|||||||
var offset: usize = 0;
|
var offset: usize = 0;
|
||||||
try vbobind.attributeAdvanced(0, 2, gl.c.GL_UNSIGNED_INT, false, @sizeOf(Input), offset);
|
try vbobind.attributeAdvanced(0, 2, gl.c.GL_UNSIGNED_INT, false, @sizeOf(Input), offset);
|
||||||
offset += 2 * @sizeOf(u32);
|
offset += 2 * @sizeOf(u32);
|
||||||
try vbobind.attributeAdvanced(1, 2, gl.c.GL_UNSIGNED_BYTE, false, @sizeOf(Input), offset);
|
try vbobind.attributeIAdvanced(1, 1, gl.c.GL_UNSIGNED_BYTE, @sizeOf(Input), offset);
|
||||||
offset += 1 * @sizeOf(u8);
|
offset += 1 * @sizeOf(u8);
|
||||||
try vbobind.enableAttribArray(0);
|
try vbobind.enableAttribArray(0);
|
||||||
try vbobind.enableAttribArray(1);
|
try vbobind.enableAttribArray(1);
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
|
|
||||||
|
const uint MODE_ZOOMED = 0u;
|
||||||
|
const uint MODE_STRETCHED = 1u;
|
||||||
|
const uint MODE_TILED = 2u;
|
||||||
|
const uint MODE_CENTERED = 3u;
|
||||||
|
|
||||||
in vec2 tex_coord;
|
in vec2 tex_coord;
|
||||||
|
flat in uint mode;
|
||||||
|
|
||||||
layout(location = 0) out vec4 out_FragColor;
|
layout(location = 0) out vec4 out_FragColor;
|
||||||
|
|
||||||
@ -8,6 +14,12 @@ uniform sampler2D image;
|
|||||||
uniform float opacity;
|
uniform float opacity;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
vec4 color = texture(image, tex_coord);
|
// Normalize the coordinate if we are tiling
|
||||||
|
vec2 norm_coord = tex_coord;
|
||||||
|
// if (mode == MODE_TILED) {
|
||||||
|
// norm_coord = fract(tex_coord);
|
||||||
|
// }
|
||||||
|
norm_coord = fract(tex_coord);
|
||||||
|
vec4 color = texture(image, norm_coord);
|
||||||
out_FragColor = vec4(color.rgb * color.a * opacity, color.a * opacity);
|
out_FragColor = vec4(color.rgb * color.a * opacity, color.a * opacity);
|
||||||
}
|
}
|
||||||
|
@ -1,24 +1,37 @@
|
|||||||
#version 330 core
|
#version 330 core
|
||||||
|
|
||||||
const uint MODE_ASPECT = 0u;
|
const uint MODE_ZOOMED = 0u;
|
||||||
const uint MODE_SCALED = 1u;
|
const uint MODE_STRETCHED = 1u;
|
||||||
|
const uint MODE_TILED = 2u;
|
||||||
|
const uint MODE_CENTERED = 3u;
|
||||||
|
|
||||||
layout (location = 0) in vec2 terminal_size;
|
layout (location = 0) in vec2 terminal_size;
|
||||||
layout (location = 1) in uint mode;
|
layout (location = 1) in uint mode_in;
|
||||||
|
|
||||||
out vec2 tex_coord;
|
out vec2 tex_coord;
|
||||||
|
flat out uint mode;
|
||||||
|
|
||||||
uniform sampler2D image;
|
uniform sampler2D image;
|
||||||
uniform mat4 projection;
|
uniform mat4 projection;
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
|
// Set mode so that we can use it in the fragment shader
|
||||||
|
mode = mode_in;
|
||||||
|
|
||||||
|
// Calculate the position of the image
|
||||||
vec2 position;
|
vec2 position;
|
||||||
position.x = (gl_VertexID == 0 || gl_VertexID == 1) ? 1. : 0.;
|
position.x = (gl_VertexID == 0 || gl_VertexID == 1) ? 1. : 0.;
|
||||||
position.y = (gl_VertexID == 0 || gl_VertexID == 3) ? 0. : 1.;
|
position.y = (gl_VertexID == 0 || gl_VertexID == 3) ? 0. : 1.;
|
||||||
|
|
||||||
|
// Get the size of the image
|
||||||
vec2 image_size = textureSize(image, 0);
|
vec2 image_size = textureSize(image, 0);
|
||||||
|
|
||||||
|
// Handles the scale of the image relative to the terminal size
|
||||||
vec2 scale = vec2(1.0, 1.0);
|
vec2 scale = vec2(1.0, 1.0);
|
||||||
|
|
||||||
switch (mode) {
|
switch (mode) {
|
||||||
case MODE_ASPECT:
|
case MODE_ZOOMED:
|
||||||
|
// If zoomed, we want to scale the image to fit the terminal
|
||||||
vec2 aspect_ratio = vec2(
|
vec2 aspect_ratio = vec2(
|
||||||
terminal_size.x / terminal_size.y,
|
terminal_size.x / terminal_size.y,
|
||||||
image_size.x / image_size.y
|
image_size.x / image_size.y
|
||||||
@ -29,12 +42,24 @@ void main() {
|
|||||||
else {
|
else {
|
||||||
scale.y = aspect_ratio.x / aspect_ratio.y;
|
scale.y = aspect_ratio.x / aspect_ratio.y;
|
||||||
}
|
}
|
||||||
case MODE_SCALED:
|
break;
|
||||||
|
case MODE_CENTERED:
|
||||||
|
// If centered, the final scale of the image should match the actual
|
||||||
|
// size of the image and should be centered
|
||||||
|
scale.x = image_size.x / terminal_size.x;
|
||||||
|
scale.y = image_size.y / terminal_size.y;
|
||||||
|
break;
|
||||||
|
case MODE_STRETCHED:
|
||||||
|
case MODE_TILED:
|
||||||
|
// We don't need to do anything for stretched or tiled
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
vec2 image_pos = terminal_size * position * scale;
|
vec2 final_image_size = terminal_size * position * scale;
|
||||||
vec2 offset = (terminal_size * (1.0 - scale)) / 2.0;
|
vec2 offset = (terminal_size * (1.0 - scale)) / 2.0;
|
||||||
gl_Position = projection * vec4(image_pos.xy + offset, 0.0, 1.0);
|
gl_Position = projection * vec4(final_image_size.xy + offset, 0.0, 1.0);
|
||||||
tex_coord = position;
|
tex_coord = position;
|
||||||
|
if (mode == MODE_TILED) {
|
||||||
|
tex_coord = position * terminal_size / image_size;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ const storage = @import("graphics_storage.zig");
|
|||||||
pub const unicode = @import("graphics_unicode.zig");
|
pub const unicode = @import("graphics_unicode.zig");
|
||||||
pub const Command = command.Command;
|
pub const Command = command.Command;
|
||||||
pub const CommandParser = command.Parser;
|
pub const CommandParser = command.Parser;
|
||||||
pub const LoadingImage = image.LoadingImage;
|
|
||||||
pub const Image = image.Image;
|
pub const Image = image.Image;
|
||||||
pub const ImageStorage = storage.ImageStorage;
|
pub const ImageStorage = storage.ImageStorage;
|
||||||
pub const RenderPlacement = render.Placement;
|
pub const RenderPlacement = render.Placement;
|
||||||
|
Reference in New Issue
Block a user