200 lines
7.8 KiB
Zig
200 lines
7.8 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const dvui = @import("dvui");
|
|
const RenderEngine = @import("RenderEngine.zig").RenderEngine;
|
|
const Document = @import("../models/Document.zig");
|
|
const basic_models = @import("../models/basic_models.zig");
|
|
const cpu_draw = @import("cpu/draw.zig");
|
|
const RenderStats = @import("RenderStats.zig");
|
|
const Size_i = basic_models.Size_i;
|
|
const Rect_i = basic_models.Rect_i;
|
|
const Allocator = std.mem.Allocator;
|
|
const Color = dvui.Color;
|
|
|
|
const CpuRenderEngine = @This();
|
|
const Type = enum {
|
|
Gradient,
|
|
Squares,
|
|
};
|
|
|
|
type: Type,
|
|
gradient_start: Color.PMA = .{ .r = 0, .g = 0, .b = 0, .a = 255 },
|
|
gradient_end: Color.PMA = .{ .r = 255, .g = 255, .b = 255, .a = 255 },
|
|
_allocator: Allocator,
|
|
_renderStats: RenderStats,
|
|
|
|
pub fn init(allocator: Allocator, render_type: Type) CpuRenderEngine {
|
|
return .{
|
|
._allocator = allocator,
|
|
.type = render_type,
|
|
._renderStats = .{
|
|
.render_time_ns = 0,
|
|
},
|
|
};
|
|
}
|
|
|
|
pub fn exampleReset(self: *CpuRenderEngine) void {
|
|
var prng = std.Random.DefaultPrng.init(@intCast(std.time.microTimestamp()));
|
|
const random = prng.random();
|
|
self.gradient_start = Color.PMA{ .r = random.int(u8), .g = random.int(u8), .b = random.int(u8), .a = 255 };
|
|
self.gradient_end = Color.PMA{ .r = random.int(u8), .g = random.int(u8), .b = random.int(u8), .a = 255 };
|
|
}
|
|
|
|
fn renderGradient(self: CpuRenderEngine, pixels: []Color.PMA, width: u32, height: u32, full_w: u32, full_h: u32, visible_rect: Rect_i) void {
|
|
var y: u32 = 0;
|
|
while (y < height) : (y += 1) {
|
|
var x: u32 = 0;
|
|
while (x < width) : (x += 1) {
|
|
const gx: u32 = visible_rect.x + x;
|
|
const gy: u32 = visible_rect.y + y;
|
|
|
|
const denom_x: f32 = if (full_w > 1) @as(f32, @floatFromInt(full_w - 1)) else 1;
|
|
const denom_y: f32 = if (full_h > 1) @as(f32, @floatFromInt(full_h - 1)) else 1;
|
|
const fx: f32 = @as(f32, @floatFromInt(gx)) / denom_x;
|
|
const fy: f32 = @as(f32, @floatFromInt(gy)) / denom_y;
|
|
const factor: f32 = std.math.clamp((fx + fy) / 2, 0, 1);
|
|
|
|
const r_f: f32 = @as(f32, @floatFromInt(self.gradient_start.r)) + factor * (@as(f32, @floatFromInt(self.gradient_end.r)) - @as(f32, @floatFromInt(self.gradient_start.r)));
|
|
const g_f: f32 = @as(f32, @floatFromInt(self.gradient_start.g)) + factor * (@as(f32, @floatFromInt(self.gradient_end.g)) - @as(f32, @floatFromInt(self.gradient_start.g)));
|
|
const b_f: f32 = @as(f32, @floatFromInt(self.gradient_start.b)) + factor * (@as(f32, @floatFromInt(self.gradient_end.b)) - @as(f32, @floatFromInt(self.gradient_start.b)));
|
|
|
|
const r: u8 = @intFromFloat(std.math.clamp(r_f, 0, 255));
|
|
const g: u8 = @intFromFloat(std.math.clamp(g_f, 0, 255));
|
|
const b: u8 = @intFromFloat(std.math.clamp(b_f, 0, 255));
|
|
pixels[y * width + x] = .{ .r = r, .g = g, .b = b, .a = 255 };
|
|
}
|
|
}
|
|
}
|
|
|
|
fn renderSquares(self: CpuRenderEngine, pixels: []Color.PMA, canvas_size: Size_i, visible_rect: Rect_i) void {
|
|
_ = self;
|
|
|
|
const colors = [_]Color.PMA{
|
|
.{ .r = 255, .g = 0, .b = 0, .a = 255 },
|
|
.{ .r = 255, .g = 165, .b = 0, .a = 255 },
|
|
.{ .r = 255, .g = 255, .b = 0, .a = 255 },
|
|
.{ .r = 0, .g = 255, .b = 0, .a = 255 },
|
|
.{ .r = 0, .g = 255, .b = 255, .a = 255 },
|
|
.{ .r = 0, .g = 0, .b = 255, .a = 255 },
|
|
};
|
|
|
|
const squares_num = 5;
|
|
var thikness: u32 = @intFromFloat(@as(f32, @floatFromInt(canvas_size.w + canvas_size.h)) / 2 * 0.03);
|
|
if (thikness == 0) thikness = 1;
|
|
|
|
const squares_sum_w = canvas_size.w - thikness * (squares_num + 1);
|
|
const base_w = squares_sum_w / squares_num;
|
|
const extra_w = squares_sum_w % squares_num;
|
|
const squares_sum_h = canvas_size.h - thikness * (squares_num + 1);
|
|
const base_h = squares_sum_h / squares_num;
|
|
const extra_h = squares_sum_h % squares_num;
|
|
|
|
var x_pos: [6]u32 = undefined;
|
|
x_pos[0] = 0;
|
|
for (1..squares_num + 1) |i| {
|
|
const w = base_w + if (i - 1 < extra_w) @as(u32, 1) else 0;
|
|
x_pos[i] = x_pos[i - 1] + thikness + w;
|
|
}
|
|
|
|
var y_pos: [6]u32 = undefined;
|
|
y_pos[0] = 0;
|
|
for (1..squares_num + 1) |i| {
|
|
const h = base_h + if (i - 1 < extra_h) @as(u32, 1) else 0;
|
|
y_pos[i] = y_pos[i - 1] + thikness + h;
|
|
}
|
|
|
|
var y: u32 = 0;
|
|
while (y < visible_rect.h) : (y += 1) {
|
|
const canvas_y = y + visible_rect.y;
|
|
if (canvas_y >= canvas_size.h) continue;
|
|
var x: u32 = 0;
|
|
while (x < visible_rect.w) : (x += 1) {
|
|
const canvas_x = x + visible_rect.x;
|
|
if (canvas_x >= canvas_size.w) continue;
|
|
|
|
var vertical_index: ?u32 = null;
|
|
for (0..x_pos.len) |i| {
|
|
if (canvas_x >= x_pos[i] and canvas_x < x_pos[i] + thikness) {
|
|
vertical_index = @intCast(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
var horizontal_index: ?u32 = null;
|
|
for (0..y_pos.len) |i| {
|
|
if (canvas_y >= y_pos[i] and canvas_y < y_pos[i] + thikness) {
|
|
horizontal_index = @intCast(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (vertical_index) |idx| {
|
|
pixels[y * visible_rect.w + x] = colors[idx];
|
|
} else if (horizontal_index) |idx| {
|
|
pixels[y * visible_rect.w + x] = colors[idx];
|
|
} else {
|
|
var square_x: u32 = 0;
|
|
for (0..squares_num) |i| {
|
|
if (canvas_x >= x_pos[i] + thikness and canvas_x < x_pos[i + 1]) {
|
|
square_x = @intCast(i);
|
|
break;
|
|
}
|
|
}
|
|
var square_y: u32 = 0;
|
|
for (0..squares_num) |i| {
|
|
if (canvas_y >= y_pos[i] + thikness and canvas_y < y_pos[i + 1]) {
|
|
square_y = @intCast(i);
|
|
break;
|
|
}
|
|
}
|
|
if (square_x % 2 == square_y % 2) {
|
|
pixels[y * visible_rect.w + x] = .{ .r = 255, .g = 255, .b = 255, .a = 255 };
|
|
} else {
|
|
pixels[y * visible_rect.w + x] = .{ .r = 0, .g = 0, .b = 0, .a = 255 };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn example(self: CpuRenderEngine, canvas_size: Size_i, visible_rect: Rect_i) !?dvui.Texture {
|
|
const full_w = canvas_size.w;
|
|
const full_h = canvas_size.h;
|
|
|
|
const width = visible_rect.w;
|
|
const height = visible_rect.h;
|
|
|
|
const pixels = try self._allocator.alloc(Color.PMA, @as(usize, width) * height);
|
|
defer self._allocator.free(pixels);
|
|
|
|
switch (self.type) {
|
|
.Gradient => self.renderGradient(pixels, width, height, full_w, full_h, visible_rect),
|
|
.Squares => self.renderSquares(pixels, canvas_size, visible_rect),
|
|
}
|
|
|
|
return try dvui.textureCreate(pixels, width, height, .nearest);
|
|
}
|
|
|
|
pub fn renderEngine(self: *CpuRenderEngine) RenderEngine {
|
|
return .{ .cpu = self };
|
|
}
|
|
|
|
/// Растеризует документ в текстуру (фон + фигуры через конвейер).
|
|
pub fn renderDocument(self: *CpuRenderEngine, document: *const Document, canvas_size: Size_i, visible_rect: Rect_i) !?dvui.Texture {
|
|
const width = visible_rect.w;
|
|
const height = visible_rect.h;
|
|
const pixels = try self._allocator.alloc(Color.PMA, @as(usize, width) * height);
|
|
defer self._allocator.free(pixels);
|
|
|
|
for (pixels) |*p| p.* = .{ .r = 255, .g = 255, .b = 255, .a = 255 };
|
|
var t = try std.time.Timer.start();
|
|
try cpu_draw.drawDocument(pixels, width, height, visible_rect, document, canvas_size, self._allocator);
|
|
self._renderStats.render_time_ns = t.read();
|
|
|
|
return try dvui.textureCreate(pixels, width, height, .nearest);
|
|
}
|
|
|
|
pub fn getStats(self: CpuRenderEngine) RenderStats {
|
|
return self._renderStats;
|
|
}
|