Дополнительный буфер для отрисовки broken line
This commit is contained in:
@@ -182,7 +182,7 @@ pub fn renderDocument(self: *CpuRenderEngine, document: *const Document, canvas_
|
||||
defer self._allocator.free(pixels);
|
||||
|
||||
for (pixels) |*p| p.* = .{ .r = 255, .g = 255, .b = 255, .a = 255 };
|
||||
cpu_draw.drawDocument(pixels, width, height, visible_rect, document, canvas_size);
|
||||
try cpu_draw.drawDocument(pixels, width, height, visible_rect, document, canvas_size, self._allocator);
|
||||
|
||||
return try dvui.textureCreate(pixels, width, height, .nearest);
|
||||
}
|
||||
|
||||
@@ -9,15 +9,30 @@ const Object = Document.Object;
|
||||
const default_stroke: Color.PMA = .{ .r = 0, .g = 0, .b = 0, .a = 255 };
|
||||
const default_thickness: f32 = 2.0;
|
||||
|
||||
/// Ломаная по точкам, обводка stroke_rgba.
|
||||
pub fn draw(ctx: *DrawContext, obj: *const Object) void {
|
||||
/// Ломаная по точкам, обводка stroke_rgba
|
||||
pub fn draw(
|
||||
ctx: *DrawContext,
|
||||
obj: *const Object,
|
||||
allocator: std.mem.Allocator,
|
||||
) !void {
|
||||
const p_prop = obj.getProperty(.points) orelse return;
|
||||
const pts = p_prop.points.items;
|
||||
if (pts.len < 2) return;
|
||||
const stroke = if (obj.getProperty(.stroke_rgba)) |s| pipeline.rgbaToPma(s.stroke_rgba) else default_stroke;
|
||||
const thickness = if (obj.getProperty(.thickness)) |t| t.thickness else default_thickness;
|
||||
|
||||
const buffer = try allocator.alloc(Color.PMA, ctx.buf_width * ctx.buf_height);
|
||||
@memset(buffer, .{ .r = 0, .g = 0, .b = 0, .a = 0 });
|
||||
defer allocator.free(buffer);
|
||||
|
||||
var copy_ctx = ctx.*;
|
||||
copy_ctx.pixels = buffer;
|
||||
copy_ctx.replace_mode = true;
|
||||
|
||||
var i: usize = 0;
|
||||
while (i + 1 < pts.len) : (i += 1) {
|
||||
line.drawLine(ctx, pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y, stroke, thickness);
|
||||
line.drawLine(©_ctx, pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y, stroke, thickness);
|
||||
}
|
||||
|
||||
ctx.compositeDrawerContext(©_ctx, copy_ctx.transform.opacity);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,12 @@ fn isVisible(obj: *const Object) bool {
|
||||
return if (obj.getProperty(.visible)) |p| p.visible else true;
|
||||
}
|
||||
|
||||
fn drawObject(ctx: *DrawContext, obj: *const Object, parent_transform: Transform) void {
|
||||
fn drawObject(
|
||||
ctx: *DrawContext,
|
||||
obj: *const Object,
|
||||
parent_transform: Transform,
|
||||
allocator: std.mem.Allocator,
|
||||
) !void {
|
||||
if (!isVisible(obj)) return;
|
||||
const local = getLocalTransform(obj);
|
||||
const world = Transform.compose(parent_transform, local);
|
||||
@@ -39,16 +44,17 @@ fn drawObject(ctx: *DrawContext, obj: *const Object, parent_transform: Transform
|
||||
switch (obj.shape) {
|
||||
.line => line.draw(ctx, obj),
|
||||
.ellipse => ellipse.draw(ctx, obj),
|
||||
.broken => broken.draw(ctx, obj),
|
||||
.broken => try broken.draw(ctx, obj, allocator),
|
||||
.arc => arc.draw(ctx, obj),
|
||||
}
|
||||
|
||||
for (obj.children.items) |*child| {
|
||||
drawObject(ctx, child, world);
|
||||
try drawObject(ctx, child, world, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
/// Рекурсивно рисует документ в буфер (объекты и потомки по порядку).
|
||||
/// allocator опционален; если передан, ломаные рисуются через слой (без двойного наложения при alpha < 1).
|
||||
pub fn drawDocument(
|
||||
pixels: []@import("dvui").Color.PMA,
|
||||
buf_width: u32,
|
||||
@@ -56,7 +62,8 @@ pub fn drawDocument(
|
||||
visible_rect: Rect_i,
|
||||
document: *const Document,
|
||||
canvas_size: Size_i,
|
||||
) void {
|
||||
allocator: std.mem.Allocator,
|
||||
) !void {
|
||||
const scale_x: f32 = if (document.size.w > 0) @as(f32, @floatFromInt(canvas_size.w)) / document.size.w else 0;
|
||||
const scale_y: f32 = if (document.size.h > 0) @as(f32, @floatFromInt(canvas_size.h)) / document.size.h else 0;
|
||||
|
||||
@@ -70,6 +77,6 @@ pub fn drawDocument(
|
||||
};
|
||||
const identity = Transform{};
|
||||
for (document.objects.items) |*obj| {
|
||||
drawObject(&ctx, obj, identity);
|
||||
try drawObject(&ctx, obj, identity, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ pub const DrawContext = struct {
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
transform: Transform = .{},
|
||||
/// Если true, blendPixelAtBuffer перезаписывает пиксель без бленда
|
||||
replace_mode: bool = false,
|
||||
|
||||
pub fn setTransform(self: *DrawContext, t: Transform) void {
|
||||
self.transform = t;
|
||||
@@ -111,12 +113,16 @@ pub const DrawContext = struct {
|
||||
};
|
||||
}
|
||||
|
||||
/// Смешивает цвет в пикселе буфера с учётом opacity трансформа.
|
||||
/// Смешивает цвет в пикселе буфера с учётом opacity трансформа. В replace_mode просто перезаписывает пиксель.
|
||||
pub fn blendPixelAtBuffer(self: *DrawContext, bx: u32, by: u32, color: Color.PMA) void {
|
||||
if (bx >= self.buf_width or by >= self.buf_height) return;
|
||||
const t = &self.transform;
|
||||
const idx = by * self.buf_width + bx;
|
||||
const dst = &self.pixels[idx];
|
||||
if (self.replace_mode) {
|
||||
dst.* = color;
|
||||
return;
|
||||
}
|
||||
const t = &self.transform;
|
||||
const a = @as(f32, @floatFromInt(color.a)) / 255.0 * t.opacity;
|
||||
const src_r = @as(f32, @floatFromInt(color.r)) * t.opacity;
|
||||
const src_g = @as(f32, @floatFromInt(color.g)) * t.opacity;
|
||||
@@ -128,6 +134,26 @@ pub const DrawContext = struct {
|
||||
dst.a = @intFromFloat(std.math.clamp(a * 255 + inv_a * @as(f32, @floatFromInt(dst.a)), 0, 255));
|
||||
}
|
||||
|
||||
/// Накладывает буфер другого контекста на этот с заданной прозрачностью (один бленд на пиксель). Размеры буферов должны совпадать.
|
||||
pub fn compositeDrawerContext(self: *DrawContext, other: *const DrawContext, opacity: f32) void {
|
||||
if (self.buf_width != other.buf_width or self.buf_height != other.buf_height) return;
|
||||
const n = self.buf_width * self.buf_height;
|
||||
for (0..n) |i| {
|
||||
const src = other.pixels[i];
|
||||
if (src.a == 0) continue;
|
||||
const dst = &self.pixels[i];
|
||||
const a = @as(f32, @floatFromInt(src.a)) / 255.0 * opacity;
|
||||
const src_r = @as(f32, @floatFromInt(src.r)) * opacity;
|
||||
const src_g = @as(f32, @floatFromInt(src.g)) * opacity;
|
||||
const src_b = @as(f32, @floatFromInt(src.b)) * opacity;
|
||||
const inv_a = 1.0 - a;
|
||||
dst.r = @intFromFloat(std.math.clamp(src_r + inv_a * @as(f32, @floatFromInt(dst.r)), 0, 255));
|
||||
dst.g = @intFromFloat(std.math.clamp(src_g + inv_a * @as(f32, @floatFromInt(dst.g)), 0, 255));
|
||||
dst.b = @intFromFloat(std.math.clamp(src_b + inv_a * @as(f32, @floatFromInt(dst.b)), 0, 255));
|
||||
dst.a = @intFromFloat(std.math.clamp(a * 255 + inv_a * @as(f32, @floatFromInt(dst.a)), 0, 255));
|
||||
}
|
||||
}
|
||||
|
||||
/// Пиксель в локальных координатах (трансформ + PMA).
|
||||
pub fn blendPixelLocal(self: *DrawContext, local_x: f32, local_y: f32, color: Color.PMA) void {
|
||||
const w = self.localToWorld(local_x, local_y);
|
||||
|
||||
Reference in New Issue
Block a user