Files
Zivro/src/Canvas.zig

133 lines
4.5 KiB
Zig
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
const std = @import("std");
const builtin = @import("builtin");
const dvui = @import("dvui");
const Document = @import("models/Document.zig");
const RenderEngine = @import("render/RenderEngine.zig").RenderEngine;
const ImageRect = @import("models/rasterization_models.zig").ImageRect;
const Size = dvui.Size;
const Color = dvui.Color;
const Canvas = @This();
allocator: std.mem.Allocator,
texture: ?dvui.Texture = null,
size: Size = .{ .w = 800, .h = 600 },
pos: dvui.Point = dvui.Point{ .x = 0, .y = 0 },
scroll: dvui.ScrollInfo = .{
.vertical = .auto,
.horizontal = .auto,
},
native_scaling: bool = false,
gradient_start: Color.PMA = .{ .r = 0, .g = 0, .b = 0, .a = 255 },
gradient_end: Color.PMA = .{ .r = 255, .g = 255, .b = 255, .a = 255 },
document: ?*Document = null,
render_engine: RenderEngine,
_visible_rect: ?ImageRect = null,
_zoom: f32 = 1,
pub fn init(allocator: std.mem.Allocator, engine: RenderEngine) Canvas {
return .{
.allocator = allocator,
.render_engine = engine,
};
}
pub fn deinit(self: *Canvas) void {
if (self.texture) |texture| {
dvui.Texture.destroyLater(texture);
self.texture = null;
}
}
/// Заполнить canvas градиентом
pub fn redrawExample(self: *Canvas) !void {
const full = self.getScaledImageSize();
const vis: ImageRect = self._visible_rect orelse ImageRect{ .x = 0, .y = 0, .w = 0, .h = 0 };
if (vis.w == 0 or vis.h == 0) {
if (self.texture) |tex| {
dvui.Texture.destroyLater(tex);
self.texture = null;
}
return;
}
const new_texture = self.render_engine.example(.{ .w = full.w, .h = full.h }, vis) catch null;
if (new_texture) |tex| {
// Удалить старую текстуру
if (self.texture) |old_tex| {
dvui.Texture.destroyLater(old_tex);
}
self.texture = tex;
}
}
// Ресетнуть example изображение в renderEngine
pub fn exampleReset(self: *Canvas) !void {
self.render_engine.exampleReset();
try self.redrawExample();
}
pub fn setZoom(self: *Canvas, value: f32) void {
self._zoom = @max(value, 0.01);
}
pub fn addZoom(self: *Canvas, value: f32) void {
self._zoom += value;
self._zoom = @max(self._zoom, 0.01);
}
pub fn getScaledImageSize(self: Canvas) ImageRect {
return .{
.x = @intFromFloat(self.pos.x),
.y = @intFromFloat(self.pos.y),
.w = @intFromFloat(self.size.w * self._zoom),
.h = @intFromFloat(self.size.h * self._zoom),
};
}
/// Обновить видимую часть изображения (в пикселях холста) и сохранить в `visible_rect`.
///
/// `viewport` и `scroll_offset` ожидаются в *physical* пикселях (т.е. уже умноженные на windowNaturalScale).
///
/// После обновления (или если текстуры ещё нет) перерисовывает текстуру, чтобы она содержала только видимую часть.
pub fn updateVisibleImageRect(self: *Canvas, viewport: dvui.Rect, scroll_offset: dvui.Point) !void {
const next = computeVisibleImageRect(self.*, viewport, scroll_offset);
var changed = false;
if (self._visible_rect) |vis| {
changed |= next.x != vis.x or next.y != vis.y or next.w != vis.w or next.h != vis.h;
}
self._visible_rect = next;
if (changed or self.texture == null) {
try self.redrawExample();
}
}
fn computeVisibleImageRect(self: Canvas, viewport: dvui.Rect, scroll_offset: dvui.Point) ImageRect {
const image_rect = self.getScaledImageSize();
const img_w: u32 = image_rect.w;
const img_h: u32 = image_rect.h;
// Видимый размер всегда равен размеру viewport, но не больше холста
const vis_w: u32 = @min(@as(u32, @intFromFloat(viewport.w)), img_w);
const vis_h: u32 = @min(@as(u32, @intFromFloat(viewport.h)), img_h);
// Вычисляем x и y на основе scroll_offset, clamped чтобы не выходить за границы
const raw_x: i64 = @intFromFloat(scroll_offset.x - @as(f32, @floatFromInt(image_rect.x)));
const raw_y: i64 = @intFromFloat(scroll_offset.y - @as(f32, @floatFromInt(image_rect.y)));
const vis_x: u32 = @intCast(std.math.clamp(raw_x, 0, @as(i64, img_w) - @as(i64, vis_w)));
const vis_y: u32 = @intCast(std.math.clamp(raw_y, 0, @as(i64, img_h) - @as(i64, vis_h)));
return .{
.x = vis_x,
.y = vis_y,
.w = vis_w,
.h = vis_h,
};
}