tiny.gui.paint.cpu.pixel
Defined in paint.cpu.
API (23)
Actions
Public operations.
ByteStore.fillByteStore.loadByteStore.loadVectorByteStore.storeByteStore.storeVectorWordStore.fillWordStore.loadWordStore.loadVectorWordStore.storeWordStore.storeVectorblendChannelblendPackedblendSpanblendSpanScalarcolorWithCoveragecoverageAlphadiv255packRgbareadPackedunpackRgbawritePacked
Types and contracts
Public types and contracts.
Source
Source: lib/gui/src/paint/cpu/pixel.zig
zig
const std = @import("std");const simd = @import("simd");const gui = @import("../../root.zig");const Color = gui.model.UiColor;const PackedVector = simd.ScalableTag(u32);const span_vector_lanes = PackedVector.lane_count;const span_vector_threshold = span_vector_lanes * 2;pub fn packRgba(color: Color) u32 { return @as(u32, color.r) | (@as(u32, color.g) << 8) | (@as(u32, color.b) << 16) | (@as(u32, color.a) << 24);}pub fn unpackRgba(pixel: u32) Color { return .{ .r = @truncate(pixel), .g = @truncate(pixel >> 8), .b = @truncate(pixel >> 16), .a = @truncate(pixel >> 24), };}pub fn readPacked(rgba8: []const u8, index: usize) u32 { return std.mem.readInt(u32, rgba8[index * 4 ..][0..4], .little);}pub fn writePacked(rgba8: []u8, index: usize, pixel: u32) void { std.mem.writeInt(u32, rgba8[index * 4 ..][0..4], pixel, .little);}pub fn blendPacked(dst: u32, src: Color) u32 { if (src.a == 0) return dst; const inv = 255 - @as(u32, src.a); const dst_r = dst & 0xff; const dst_g = (dst >> 8) & 0xff; const dst_b = (dst >> 16) & 0xff; const dst_a = (dst >> 24) & 0xff; const r = blendChannel(src.r, src.a, dst_r, inv); const g = blendChannel(src.g, src.a, dst_g, inv); const b = blendChannel(src.b, src.a, dst_b, inv); const a = @as(u32, src.a) + div255(dst_a * inv); return r | (g << 8) | (b << 16) | (a << 24);}pub fn blendSpanScalar( comptime Store: type, store: Store, start: usize, count: usize, src: Color,) void { var index = start; const end = start + count; while (index < end) : (index += 1) { store.store(index, blendPacked(store.load(index), src)); }}pub fn blendSpan( comptime Store: type, store: Store, start: usize, count: usize, src: Color,) void { if (src.a == 0) return; var index = start; const end = start + count; if (comptime span_vector_lanes > 1) { if (count >= span_vector_threshold) { const vector_end = end - count % span_vector_lanes; while (index < vector_end) : (index += span_vector_lanes) { const dst = store.loadVector(PackedVector, index); store.storeVector( PackedVector, index, blendPackedVector(PackedVector, dst, src), ); } } } blendSpanScalar(Store, store, index, end - index, src);}pub fn blendChannel(src_value: u8, src_alpha: u8, dst_value: u32, inv_alpha: u32) u32 { return div255(@as(u32, src_value) * @as(u32, src_alpha) + dst_value * inv_alpha);}pub fn div255(value: u32) u32 { return (value + 127) / 255;}fn blendPackedVector( comptime D: type, dst: D.Vector, src: Color,) D.Vector { const byte_mask = simd.set(D, 0xff); const alpha = @as(u32, src.a); const inv = simd.set(D, 255 - alpha); const dst_r = simd.bitAnd(D, dst, byte_mask); const dst_g = simd.bitAnd(D, shiftRight(D, dst, 8), byte_mask); const dst_b = simd.bitAnd(D, shiftRight(D, dst, 16), byte_mask); const dst_a = shiftRight(D, dst, 24); const r = blendChannelVector(D, dst_r, src.r, alpha, inv); const g = blendChannelVector(D, dst_g, src.g, alpha, inv); const b = blendChannelVector(D, dst_b, src.b, alpha, inv); const a = simd.add( D, simd.set(D, alpha), div255Vector(D, simd.mul(D, dst_a, inv)), ); return simd.bitOr( D, simd.or3( D, r, shiftLeft(D, g, 8), shiftLeft(D, b, 16), ), shiftLeft(D, a, 24), );}fn blendChannelVector( comptime D: type, dst: D.Vector, src: u8, alpha: u32, inv: D.Vector,) D.Vector { return div255Vector( D, simd.add( D, simd.set(D, @as(u32, src) * alpha), simd.mul(D, dst, inv), ), );}fn div255Vector(comptime D: type, value: D.Vector) D.Vector { const rounded = simd.add(D, value, simd.set(D, 128)); return shiftRight(D, simd.add(D, rounded, shiftRight(D, rounded, 8)), 8);}fn shiftLeft(comptime D: type, value: D.Vector, comptime amount: u5) D.Vector { const Shift = @Vector(D.lane_count, u5); return value << @as(Shift, @splat(amount));}fn shiftRight(comptime D: type, value: D.Vector, comptime amount: u5) D.Vector { const Shift = @Vector(D.lane_count, u5); return value >> @as(Shift, @splat(amount));}pub fn coverageAlpha(alpha: u8, coverage: u32) u8 { return @intCast((@as(u32, alpha) * coverage + 2) / 4);}pub fn colorWithCoverage(color: Color, coverage: u32) Color { return .{ .r = color.r, .g = color.g, .b = color.b, .a = coverageAlpha(color.a, coverage), };}pub const ByteStore = struct { bytes: []u8, pub inline fn load(self: ByteStore, index: usize) u32 { return readPacked(self.bytes, index); } pub inline fn store(self: ByteStore, index: usize, value: u32) void { writePacked(self.bytes, index, value); } pub inline fn fill(self: ByteStore, start: usize, count: usize, value: u32) void { var index = start; const end = start + count; while (index < end) : (index += 1) writePacked(self.bytes, index, value); } pub inline fn loadVector(self: ByteStore, comptime D: type, index: usize) D.Vector { var result: D.Vector = undefined; inline for (0..D.lane_count) |lane_index| { result[lane_index] = readPacked(self.bytes, index + lane_index); } return result; } pub inline fn storeVector( self: ByteStore, comptime D: type, index: usize, value: D.Vector, ) void { inline for (0..D.lane_count) |lane_index| { writePacked(self.bytes, index + lane_index, value[lane_index]); } }};pub const WordStore = struct { words: []u32, pub inline fn load(self: WordStore, index: usize) u32 { return self.words[index]; } pub inline fn store(self: WordStore, index: usize, value: u32) void { self.words[index] = value; } pub inline fn fill(self: WordStore, start: usize, count: usize, value: u32) void { @memset(self.words[start .. start + count], value); } pub inline fn loadVector(self: WordStore, comptime D: type, index: usize) D.Vector { return simd.load(D, self.words[index..]); } pub inline fn storeVector( self: WordStore, comptime D: type, index: usize, value: D.Vector, ) void { simd.store(D, value, self.words[index..]); }};test "packed byte layout round-trips through both stores" { const color = Color{ .r = 12, .g = 34, .b = 56, .a = 78 }; const pixel = packRgba(color); var bytes = @as([8]u8, @splat(0)); const byte_store = ByteStore{ .bytes = bytes[0..] }; byte_store.store(1, pixel); try std.testing.expectEqual(pixel, byte_store.load(1)); try std.testing.expectEqual(@as(u8, 12), bytes[4]); try std.testing.expectEqual(@as(u8, 78), bytes[7]); var words = @as([2]u32, @splat(0)); const word_store = WordStore{ .words = words[0..] }; word_store.store(1, pixel); try std.testing.expectEqual(pixel, word_store.load(1)); try std.testing.expectEqual(color, unpackRgba(word_store.load(1)));}test "coverageAlpha at full coverage preserves alpha exactly" { var alpha: u32 = 0; while (alpha <= 255) : (alpha += 1) { try std.testing.expectEqual(@as(u8, @intCast(alpha)), coverageAlpha(@intCast(alpha), 4)); }}test "blendPacked with opaque source replaces destination" { const src = Color{ .r = 10, .g = 20, .b = 30, .a = 255 }; try std.testing.expectEqual(packRgba(src), blendPacked(0xdead_beef, src));}test "vector div255 is bit-exact over packed blend range" { var value: u32 = 0; while (value <= 65_025) : (value += 1) { const vector = div255Vector(PackedVector, simd.set(PackedVector, value)); const lanes: [PackedVector.lane_count]u32 = vector; try std.testing.expectEqual(div255(value), lanes[0]); }}test "selected packed spans match scalar words and unaligned bytes" { const pixel_count = span_vector_lanes * 3 + 5; var initial: [pixel_count]u32 = undefined; var expected: [pixel_count]u32 = undefined; var words: [pixel_count]u32 = undefined; var byte_storage: [pixel_count * 4 + 1]u8 = undefined; const bytes = byte_storage[1..]; for (&initial, 0..) |*value, index| { value.* = @as(u32, @truncate(index *% 0x9e37_79b9)) ^ 0xa5c3_71e9; } var alpha: u16 = 0; while (alpha <= 255) : (alpha += 1) { const src = Color{ .r = @truncate(alpha *% 29 + 3), .g = @truncate(alpha *% 73 + 5), .b = @truncate(alpha *% 151 + 7), .a = @intCast(alpha), }; var offset: usize = 0; while (offset < span_vector_lanes + 1) : (offset += 1) { var count: usize = 0; while (count <= pixel_count - offset) : (count += 1) { @memcpy(&expected, &initial); @memcpy(&words, &initial); for (initial, 0..) |value, index| writePacked(bytes, index, value); blendSpanScalar( WordStore, .{ .words = &expected }, offset, count, src, ); blendSpan(WordStore, .{ .words = &words }, offset, count, src); blendSpan(ByteStore, .{ .bytes = bytes }, offset, count, src); try std.testing.expectEqualSlices(u32, &expected, &words); for (expected, 0..) |value, index| { try std.testing.expectEqual(value, readPacked(bytes, index)); } } } }}Source: lib/gui/src/paint/cpu/root.zig:4
zig
pub const pixel = render.pixel;Complete caller list for paint.cpu.pixel.coverageAlpha
7 direct callers.
lib.gui.src.paint.cpu.coverage.glyphColorAt[function] — private source atlib/gui/src/paint/cpu/coverage.zig:91in nearest public ownerlib.gui.src.paint.cpu.coveragelib.gui.src.paint.cpu.coverage.imageColorAt[function] — private source atlib/gui/src/paint/cpu/coverage.zig:85in nearest public ownerlib.gui.src.paint.cpu.coveragelib.gui.src.paint.cpu.coverage.shadowColorAt[function] — private source atlib/gui/src/paint/cpu/coverage.zig:75in nearest public ownerlib.gui.src.paint.cpu.coveragelib.gui.src.paint.cpu.coverage.texturedRun[function] — private source atlib/gui/src/paint/cpu/coverage.zig:122in nearest public ownerlib.gui.src.paint.cpu.coveragetiny.gui.paint.cpu.pixel.colorWithCoverage[function] atlib/gui/src/paint/cpu/pixel.zig:166lib.gui.src.paint.cpu.pixel.test_coverageAlpha_at_full_coverage_preserves_alpha_exactly[function] — test source atlib/gui/src/paint/cpu/pixel.zig:259in nearest public ownertiny.gui.paint.cpu.pixellib.gui.src.paint.image.rasterShadowMask[function] — private source atlib/gui/src/paint/image.zig:1175in nearest public ownertiny.gui.paint.image
Complete caller list for paint.cpu.pixel.div255
7 direct callers.
lib.gui.src.paint.cpu.coverage.glyphColorAt[function] — private source atlib/gui/src/paint/cpu/coverage.zig:91in nearest public ownerlib.gui.src.paint.cpu.coveragelib.gui.src.paint.cpu.coverage.imageColorAt[function] — private source atlib/gui/src/paint/cpu/coverage.zig:85in nearest public ownerlib.gui.src.paint.cpu.coveragelib.gui.src.paint.cpu.coverage.texturedRun[function] — private source atlib/gui/src/paint/cpu/coverage.zig:122in nearest public ownerlib.gui.src.paint.cpu.coveragetiny.gui.paint.cpu.pixel.blendChannel[function] atlib/gui/src/paint/cpu/pixel.zig:90tiny.gui.paint.cpu.pixel.blendPacked[function] atlib/gui/src/paint/cpu/pixel.zig:36lib.gui.src.paint.cpu.pixel.test_vector_div255_is_bit-exact_over_packed_blend_range[function] — test source atlib/gui/src/paint/cpu/pixel.zig:271in nearest public ownertiny.gui.paint.cpu.pixellib.gui.src.paint.cpu.span.texturedRows[function] — private source atlib/gui/src/paint/cpu/span.zig:287in nearest public ownerlib.gui.src.paint.cpu.span
Complete caller list for paint.cpu.pixel.packRgba
10 direct callers.
lib.gui.src.paint.cpu.pixel.test_blendPacked_with_opaque_source_replaces_destination[function] — test source atlib/gui/src/paint/cpu/pixel.zig:266in nearest public ownertiny.gui.paint.cpu.pixellib.gui.src.paint.cpu.pixel.test_packed_byte_layout_round-trips_through_both_stores[function] — test source atlib/gui/src/paint/cpu/pixel.zig:241in nearest public ownertiny.gui.paint.cpu.pixellib.gui.src.paint.cpu.span.fillRows[function] — private source atlib/gui/src/paint/cpu/span.zig:101in nearest public ownerlib.gui.src.paint.cpu.spanlib.gui.src.paint.cpu.span.gradientRows[function] — private source atlib/gui/src/paint/cpu/span.zig:182in nearest public ownerlib.gui.src.paint.cpu.spanlib.gui.src.paint.cpu.span.renderRegion[function] — private source atlib/gui/src/paint/cpu/span.zig:17in nearest public ownerlib.gui.src.paint.cpu.spanlib.gui.src.paint.cpu.span.texturedRows[function] — private source atlib/gui/src/paint/cpu/span.zig:287in nearest public ownerlib.gui.src.paint.cpu.spanlib.gui.src.paint.image.rasterShadowMask[function] — private source atlib/gui/src/paint/image.zig:1175in nearest public ownertiny.gui.paint.imagelib.gui.src.paint.image.test_paint_image_processor_expands_shadow_commands_into_appended_images[function] — test source atlib/gui/src/paint/image.zig:1787in nearest public ownertiny.gui.paint.imagelib.gui.src.paint.image.test_paint_image_processor_expansion_preserves_command_lists_without_shadows[function] — test source atlib/gui/src/paint/image.zig:1894in nearest public ownertiny.gui.paint.imagelib.gui.src.paint.image.tintShadowPixels[function] — private source atlib/gui/src/paint/image.zig:1194in nearest public ownertiny.gui.paint.image
Audit
| Definitions | 24 |
|---|---|
| Public names | 26 |
| Members | 2 |
| Version | 26.7.0 |
| Revision | daab053ee433 |