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, .rgba_8_8_8_8); } 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, .rgba_8_8_8_8); } pub fn getStats(self: CpuRenderEngine) RenderStats { return self._renderStats; }