tiny.gui.paint.gradient
Defined in paint.
API (10)
Actions
Public operations.
Sampler.colorAtSampler.colorAtRowSampler.initSampler.parameterAtSampler.parameterAtRowSampler.rowProjectioncolorAtcolorAtParameterparameter
Types and contracts
Public types and contracts.
Source
Source: lib/gui/src/paint/gradient.zig
zig
const std = @import("std");const gui = @import("../root.zig");const Color = gui.model.UiColor;const Command = gui.paint.command.Command;const Point = gui.model.UiPoint;pub fn colorAt(paint: Command, x: f32, y: f32) Color { return Sampler.init(paint).colorAt(x, y);}pub fn parameter(paint: Command, x: f32, y: f32) f32 { return Sampler.init(paint).parameterAt(x, y);}pub const Sampler = struct { start: Point, start_color: PremultipliedColor, end_color: PremultipliedColor, dx: f32, dy: f32, length_squared: f32, degenerate: bool, pub fn init(paint: Command) Sampler { const dx = paint.gradient_end.x - paint.gradient_start.x; const dy = paint.gradient_end.y - paint.gradient_start.y; const length_squared = dx * dx + dy * dy; return .{ .start = paint.gradient_start, .start_color = PremultipliedColor.init(paint.color), .end_color = PremultipliedColor.init(paint.color_end), .dx = dx, .dy = dy, .length_squared = length_squared, .degenerate = !std.math.isFinite(length_squared) or length_squared <= std.math.floatEps(f32), }; } pub fn colorAt(self: Sampler, x: f32, y: f32) Color { return self.colorAtRow(x, self.rowProjection(y)); } pub fn parameterAt(self: Sampler, x: f32, y: f32) f32 { return self.parameterAtRow(x, self.rowProjection(y)); } pub fn rowProjection(self: Sampler, y: f32) f32 { return (y - self.start.y) * self.dy; } pub fn colorAtRow(self: Sampler, x: f32, row_projection: f32) Color { const t = self.parameterAtRow(x, row_projection); const alpha_value = interpolateValue(self.start_color.a, self.end_color.a, t); if (alpha_value <= 0) return .{ .a = 0 }; return .{ .r = unpremultipliedValue(self.start_color.r, self.end_color.r, t, alpha_value), .g = unpremultipliedValue(self.start_color.g, self.end_color.g, t, alpha_value), .b = unpremultipliedValue(self.start_color.b, self.end_color.b, t, alpha_value), .a = roundedChannel(alpha_value), }; } pub fn parameterAtRow(self: Sampler, x: f32, row_projection: f32) f32 { if (self.degenerate) return 1; const projected = ((x - self.start.x) * self.dx + row_projection) / self.length_squared; if (!std.math.isFinite(projected)) return 1; return std.math.clamp(projected, @as(f32, 0), @as(f32, 1)); }};const PremultipliedColor = struct { r: f32, g: f32, b: f32, a: f32, fn init(color: Color) PremultipliedColor { const alpha: f32 = @floatFromInt(color.a); return .{ .r = @as(f32, @floatFromInt(color.r)) * alpha, .g = @as(f32, @floatFromInt(color.g)) * alpha, .b = @as(f32, @floatFromInt(color.b)) * alpha, .a = alpha, }; }};pub fn colorAtParameter(start: Color, end: Color, parameter_value: f32) Color { const t = std.math.clamp(parameter_value, @as(f32, 0), @as(f32, 1)); const alpha_value = interpolate(start.a, end.a, t); if (alpha_value <= 0) return .{ .a = 0 }; return .{ .r = unpremultiplied(start.r, start.a, end.r, end.a, t, alpha_value), .g = unpremultiplied(start.g, start.a, end.g, end.a, t, alpha_value), .b = unpremultiplied(start.b, start.a, end.b, end.a, t, alpha_value), .a = roundedChannel(alpha_value), };}fn unpremultiplied(start: u8, start_alpha: u8, end: u8, end_alpha: u8, t: f32, alpha_value: f32) u8 { const start_value = @as(f32, @floatFromInt(start)) * @as(f32, @floatFromInt(start_alpha)); const end_value = @as(f32, @floatFromInt(end)) * @as(f32, @floatFromInt(end_alpha)); return unpremultipliedValue(start_value, end_value, t, alpha_value);}fn unpremultipliedValue(start: f32, end: f32, t: f32, alpha_value: f32) u8 { return roundedChannel(interpolateValue(start, end, t) / alpha_value);}fn interpolate(start: u8, end: u8, t: f32) f32 { return interpolateValue(@floatFromInt(start), @floatFromInt(end), t);}fn interpolateValue(start: f32, end: f32, t: f32) f32 { return start + (end - start) * t;}fn roundedChannel(value: f32) u8 { return @intFromFloat(std.math.clamp(@round(value), @as(f32, 0), @as(f32, 255)));}test "linear gradient clamps and reverses direction" { const paint = Command{ .kind = .linear_gradient, .rect = .{ .width = 12, .height = 4 }, .clip = .{ .width = 12, .height = 4 }, .color = .{ .r = 10, .g = 20, .b = 30, .a = 255 }, .color_end = .{ .r = 210, .g = 120, .b = 80, .a = 255 }, .gradient_start = .{ .x = 10 }, .gradient_end = .{ .x = 2 }, }; try std.testing.expectEqual(paint.color, colorAt(paint, 12, 2)); try std.testing.expectEqual(paint.color_end, colorAt(paint, 0, 2)); try std.testing.expectEqual(@as(u8, 110), colorAt(paint, 6, 2).r);}test "linear gradient degenerate vector resolves to end color" { const paint = Command{ .kind = .linear_gradient, .rect = .{ .width = 4, .height = 4 }, .clip = .{ .width = 4, .height = 4 }, .color = .{ .r = 255, .a = 255 }, .color_end = .{ .b = 255, .a = 120 }, .gradient_start = .{ .x = 2, .y = 2 }, .gradient_end = .{ .x = 2, .y = 2 }, }; try std.testing.expectEqual(paint.color_end, colorAt(paint, 1, 3));}test "linear gradient premultiplies translucent interpolation" { const color = colorAtParameter( .{ .r = 255, .g = 40, .b = 20, .a = 255 }, .{ .r = 0, .g = 0, .b = 0, .a = 0 }, 0.5, ); try std.testing.expectEqual(@as(u8, 255), color.r); try std.testing.expectEqual(@as(u8, 40), color.g); try std.testing.expectEqual(@as(u8, 20), color.b); try std.testing.expectEqual(@as(u8, 128), color.a);}Source: lib/gui/src/paint/root.zig:7
zig
pub const gradient = @import("gradient.zig");Audit
| Definitions | 11 |
|---|---|
| Public names | 11 |
| Members | 7 |
| Version | 26.7.0 |
| Revision | daab053ee433 |