Универсальная растровая заливка

This commit is contained in:
2026-03-03 18:30:24 +03:00
parent 5b1b3a8c5e
commit b1177265ea
3 changed files with 117 additions and 88 deletions

View File

@@ -17,11 +17,12 @@ pub fn draw(ctx: *DrawContext, obj: *const Object) void {
const end_y = ep_prop.end_point.y;
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;
drawLine(ctx, 0, 0, end_x, end_y, stroke, thickness);
drawLine(ctx, 0, 0, end_x, end_y, stroke, thickness, false);
}
/// Рисует отрезок по локальным концам (перевод в буфер внутри).
pub fn drawLine(ctx: *DrawContext, x0: f32, y0: f32, x1: f32, y1: f32, color: Color.PMA, thickness: f32) void {
/// draw_when_outside: если true, участки линии за экраном тоже рисуются (толщиной 1 px); в буфере — обычная толщина.
pub fn drawLine(ctx: *DrawContext, x0: f32, y0: f32, x1: f32, y1: f32, color: Color.PMA, thickness: f32, draw_when_outside: bool) void {
const w0 = ctx.localToWorld(x0, y0);
const w1 = ctx.localToWorld(x1, y1);
const b0 = ctx.worldToBuffer(w0.x, w0.y);
@@ -30,7 +31,7 @@ pub fn drawLine(ctx: *DrawContext, x0: f32, y0: f32, x1: f32, y1: f32, color: Co
const scale = @sqrt(t.scale.scale_x * ctx.scale_x * t.scale.scale_y * ctx.scale_y);
const thickness_px: u32 = @as(u32, @intFromFloat(std.math.round(thickness * scale)));
if (thickness_px > 0)
drawLineInBuffer(ctx, b0.x, b0.y, b1.x, b1.y, color, thickness_px);
drawLineInBuffer(ctx, b0.x, b0.y, b1.x, b1.y, color, thickness_px, draw_when_outside);
}
inline fn clip(p: f32, q: f32, t0: *f32, t1: *f32) bool {
@@ -115,7 +116,7 @@ fn clipLineToBuffer(ctx: *DrawContext, a: *Point2_i, b: *Point2_i, thickness: i3
return true;
}
fn drawLineInBuffer(ctx: *DrawContext, bx0: i32, by0: i32, bx1: i32, by1: i32, color: Color.PMA, thickness_px: u32) void {
fn drawLineInBuffer(ctx: *DrawContext, bx0: i32, by0: i32, bx1: i32, by1: i32, color: Color.PMA, thickness_px: u32, draw_when_outside: bool) void {
// Коррекция толщины в зависимости от угла линии.
var thickness_corrected: u32 = thickness_px;
var use_vertical: bool = undefined;
@@ -137,12 +138,15 @@ fn drawLineInBuffer(ctx: *DrawContext, bx0: i32, by0: i32, bx1: i32, by1: i32, c
thickness_corrected = @max(@as(u32, 1), @as(u32, @intFromFloat(std.math.round(corrected_f))));
}
const half_thickness: i32 = @intCast(thickness_corrected / 2);
const thickness_corrected_i: i32 = @as(i32, @intCast(thickness_corrected));
var p0 = Point2_i{ .x = bx0, .y = by0 };
var p1 = Point2_i{ .x = bx1, .y = by1 };
// Отсечение отрезка буфером. Если он целиком вне — рисовать нечего.
if (!clipLineToBuffer(ctx, &p0, &p1, @as(i32, @intCast(thickness_corrected)))) return;
// Отсечение только когда не рисуем вне viewport: иначе линия идёт целиком, толщина 1 px снаружи.
if (!draw_when_outside) {
if (!clipLineToBuffer(ctx, &p0, &p1, @as(i32, @intCast(thickness_corrected)))) return;
}
var x0 = p0.x;
var y0 = p0.y;
@@ -156,10 +160,16 @@ fn drawLineInBuffer(ctx: *DrawContext, bx0: i32, by0: i32, bx1: i32, by1: i32, c
const sy: i32 = if (y0 < ey) 1 else -1;
var err: i32 = dx + dy;
const buf_w_i: i32 = @intCast(ctx.buf_width);
const buf_h_i: i32 = @intCast(ctx.buf_height);
while (true) {
var thick: i32 = -half_thickness;
while (thick <= half_thickness) {
// Внутри viewport — полная толщина; снаружи при draw_when_outside — только 1 пиксель.
const in_viewport = x0 >= -thickness_corrected_i and x0 < buf_w_i + thickness_corrected_i and y0 >= -thickness_corrected_i and y0 < buf_h_i + thickness_corrected_i;
const effective_half: i32 = if (draw_when_outside and !in_viewport) 0 else half_thickness;
var thick: i32 = -effective_half;
while (thick <= effective_half) {
const x = if (use_vertical) x0 + thick else x0;
const y = if (use_vertical) y0 else y0 + thick;
ctx.blendPixelAtBuffer(x, y, color);