shithub: libdraw-zig

Download patch

ref: 467a840b86b9ef31e44897566f789b78e3d051f8
parent: 128a8678fff4dca8cac1da8baa0006b048249b60
author: Jacob G-W <jacoblevgw@gmail.com>
date: Sun Jul 16 16:50:44 EDT 2023

code working!!!!!!

binary files /dev/null b/t differ
--- a/test.zig
+++ b/test.zig
@@ -1,13 +1,15 @@
 const std = @import("std");
-var display: *Display = undefined;
-var _screen: ?*Screen = null;
-var screen: ?*Image = null;
 pub fn main() !void {
     const ally = std.heap.page_allocator;
-    initDraw(ally, null, "bruh") catch |e| {
+    const d = initDraw(ally, null, "bruh") catch |e| {
         std.debug.print("errstr: {s}\n", .{std.os.plan9.errstr()});
         return e;
     };
+    const screen = d.getScreen();
+    var buf: [128]u8 = undefined;
+    _ = buf;
+    try screen.draw(screen.r, d.white, null, Point.Zero);
+    try d.flushImage(true);
 }
 
 pub fn parseIntSkipPreceedingSpaces(comptime T: type, buf: []const u8) !T {
@@ -56,11 +58,116 @@
         s.fill = fill;
         return s;
     }
+    pub fn free(self: *Image) !void {
+        try Display.freeImage1(self);
+        self.display.ally.destroy(self);
+    }
+    pub fn line(dest: *Image, p0: Point, p1: Point, end0: u32, end1: u32, radius: u32, src: *Image, sp: Point) !void {
+        return dest.lineop(p0, p1, end0, end1, radius, src, sp, DrawOp.SoverD);
+    }
+    pub fn lineop(dst: *Image, p0: Point, p1: Point, end0: u32, end1: u32, radius: u32, src: *Image, sp: Point, op: DrawOp) !void {
+        const d = dst.display;
+        try d.setDrawOp(op);
+        var bs = try d.bufImage(1 + 4 + 2 * 4 + 2 * 4 + 4 + 4 + 4 + 4 + 2 * 4);
+        var a = bs.writer();
+        a.writeByte('L') catch unreachable;
+        a.writeIntLittle(u32, dst.id) catch unreachable;
+        a.writeIntLittle(u32, p0.x) catch unreachable;
+        a.writeIntLittle(u32, p0.y) catch unreachable;
+        a.writeIntLittle(u32, p1.x) catch unreachable;
+        a.writeIntLittle(u32, p1.y) catch unreachable;
+        a.writeIntLittle(u32, end0) catch unreachable;
+        a.writeIntLittle(u32, end1) catch unreachable;
+        a.writeIntLittle(u32, radius) catch unreachable;
+        a.writeIntLittle(u32, src.id) catch unreachable;
+        a.writeIntLittle(u32, sp.x) catch unreachable;
+        a.writeIntLittle(u32, sp.y) catch unreachable;
+    }
+    pub fn draw1(dst: *Image, r: Rectangle, src: ?*Image, p0: Point, mask: ?*Image, p1: Point, op: DrawOp) !void {
+        const d = dst.display;
+        try d.setDrawOp(op);
+
+        var bs = try d.bufImage(1 + 4 + 4 + 4 + 4 * 4 + 2 * 4 + 2 * 4);
+        var a = bs.writer();
+        const s = src orelse d.black;
+        const m = mask orelse d.@"opaque";
+        a.writeByte('d') catch unreachable;
+        a.writeIntLittle(u32, dst.id) catch unreachable;
+        a.writeIntLittle(u32, s.id) catch unreachable;
+        a.writeIntLittle(u32, m.id) catch unreachable;
+        a.writeIntLittle(u32, @as(u32, @bitCast(r.min.x))) catch unreachable;
+        a.writeIntLittle(u32, @as(u32, @bitCast(r.min.y))) catch unreachable;
+        a.writeIntLittle(u32, @as(u32, @bitCast(r.max.x))) catch unreachable;
+        a.writeIntLittle(u32, @as(u32, @bitCast(r.max.y))) catch unreachable;
+        a.writeIntLittle(u32, @as(u32, @bitCast(p0.x))) catch unreachable;
+        a.writeIntLittle(u32, @as(u32, @bitCast(p0.y))) catch unreachable;
+        a.writeIntLittle(u32, @as(u32, @bitCast(p1.x))) catch unreachable;
+        a.writeIntLittle(u32, @as(u32, @bitCast(p1.y))) catch unreachable;
+    }
+
+    pub fn draw(dst: *Image, r: Rectangle, src: *Image, mask: ?*Image, p1: Point) !void {
+        return draw1(dst, r, src, p1, mask, p1, .soverD);
+    }
+
+    pub fn drawop(dst: *Image, r: Rectangle, src: ?*Image, mask: ?*Image, p1: Point, op: DrawOp) !void {
+        return draw1(dst, r, src, p1, mask, p1, op);
+    }
+
+    pub fn gendraw(dst: *Image, r: Rectangle, src: ?*Image, p0: Point, mask: ?*Image, p1: Point) !void {
+        return draw1(dst, r, src, p0, mask, p1, .soverD);
+    }
+
+    pub fn gendrawop(dst: *Image, r: Rectangle, src: ?*Image, p0: Point, mask: ?*Image, p1: Point, op: DrawOp) !void {
+        return draw1(dst, r, src, p0, mask, p1, op);
+    }
 };
+/// Porter-Duff compositing operators
+const DrawOp = enum(u8) {
+    pub const Clear = 0;
+
+    pub const SinD = 8;
+    pub const DinS = 4;
+    pub const SoutD = 2;
+    pub const DoutS = 1;
+
+    pub const S = SinD | SoutD;
+    pub const SoverD = SinD | SoutD | DoutS;
+    pub const SatopD = SinD | DoutS;
+    pub const SxorD = SoutD | DoutS;
+
+    pub const D = DinS | DoutS;
+    pub const DoverS = DinS | DoutS | SoutD;
+    pub const DatopS = DinS | SoutD;
+    pub const DxorS = DoutS | SoutD; // == SxorD
+
+    pub const Ncomp = 12;
+
+    sinD = SinD,
+    dinS = DinS,
+    soutD = SoutD,
+    doutS = DoutS,
+
+    s = S,
+    soverD = SoverD,
+    satopD = SatopD,
+    sxorD = SxorD,
+
+    d = D,
+    doverS = DoverS,
+    datopS = DatopS,
+    // dxorS = DxorS, // == SxorD TODO have multiple enum vals with the same name
+
+};
 var screenid: u32 = 0;
+pub const Point = struct {
+    x: i32,
+    y: i32,
+    pub const Zero: Point = .{ .x = 0, .y = 0 };
+};
 pub const Rectangle = struct {
     min: Point,
     max: Point,
+    pub const Zero: Rectangle = .{ .min = Point.Zero, .max = Point.Zero };
     pub fn init(min_x: i32, min_y: i32, max_x: i32, max_y: i32) Rectangle {
         return .{
             .min = .{
@@ -102,8 +209,12 @@
     id: u32, // id of system-held Screen
     image: *Image, // unused; for reference only
     fill: *Image, // color to paint behind windows
+    fn free(self: *Screen) !void {
+        const d = self.display;
+        try d.freeRemote(self.id, .screen);
+        d.ally.destroy(self);
+    }
 };
-pub const Point = struct { x: i32, y: i32 };
 pub const Display = struct {
     ally: std.mem.Allocator,
     qlock: void, // some sort of mutex???
@@ -132,6 +243,8 @@
     windows: ?*Image,
     screenimage: ?*Image,
     _isnewdisplay: bool,
+    screen: ?*Image = null,
+    _screen: ?*Screen = null,
     pub fn init(ally: std.mem.Allocator, options: struct { devdir: []const u8 = "/dev", windir: []const u8 = "/dev" }) !*Display {
         const NINFO = 12 * 12;
         var info: [NINFO + 1]u8 = undefined;
@@ -164,7 +277,7 @@
                 .display = disp,
                 .id = 0,
                 .chan = chan,
-                .depth = chan.getDepth(),
+                .depth = chan.depth(),
                 .repl = try parseIntSkipPreceedingSpaces(u32, infoslice[3 * 12 .. 4 * 12 - 1]) != 0,
                 .r = .{
                     .min = .{
@@ -225,8 +338,8 @@
         disp.buf = try ally.alloc(u8, bufsz + 5); // +5 for flush message;
         errdefer ally.free(disp.buf);
         disp.bufp = disp.buf.ptr;
-        disp.white = try disp.allocImage(Rectangle.init(0, 0, 1, 1), Chan.GREY1, true, DColor.White);
-        disp.black = try disp.allocImage(Rectangle.init(0, 0, 1, 1), Chan.GREY1, true, DColor.Black);
+        disp.white = try disp.allocImage(Rectangle.init(0, 0, 1, 1), .grey1, true, DColor.White);
+        disp.black = try disp.allocImage(Rectangle.init(0, 0, 1, 1), .grey1, true, DColor.Black);
         // disp.error = error;
         disp.windir = try ally.dupe(u8, options.windir);
         errdefer ally.free(disp.windir);
@@ -247,7 +360,7 @@
         if (@intFromEnum(chan) == 0) {
             return error.BadChanDesc;
         }
-        const depth = chan.getDepth();
+        const depth = chan.depth();
         if (depth == 0) {
             return error.BadChanDesc;
         }
@@ -319,7 +432,7 @@
             .display = self,
             .id = id,
             .chan = chan,
-            .depth = chan.getDepth(),
+            .depth = chan.depth(),
             .repl = try parseIntSkipPreceedingSpaces(u32, buf[3 * 12 .. 4 * 12 - 1]) != 0,
             .r = .{
                 .min = .{
@@ -346,7 +459,7 @@
         };
         return i;
     }
-    fn _allocWindow(self: *Display, i: *Image, s: *Screen, r: Rectangle, ref: Refresh, col: u32) !*Image {
+    fn _allocWindow(self: *Display, i: ?*Image, s: *Screen, r: Rectangle, ref: Refresh, col: u32) !*Image {
         var im = try self._allocImage(i, r, self.screenimage.?.chan, false, col, s.id, ref);
         im.screen = s;
         im.next = self.windows;
@@ -360,20 +473,16 @@
         a.writeByte(c) catch unreachable;
         a.writeIntLittle(u32, id) catch unreachable;
     }
-    fn freeImage1(i: ?*Image) !void {
-        if (i == null) {
-            return;
-        }
-        const image = i.?;
+    fn freeImage1(image: *Image) !void {
         const d = image.display;
         if (image.screen != null) {
             var w: ?*Image = d.windows;
-            if (w.? == i) {
+            if (w.? == image) {
                 d.windows = image.next;
             } else {
                 while (w != null) {
-                    if (w.?.next == i) {
-                        w.?.next = i.?.next;
+                    if (w.?.next == image) {
+                        w.?.next = image.next;
                         break;
                     }
                     w = w.?.next;
@@ -382,11 +491,6 @@
         }
         try d.freeRemote(image.id, .image);
     }
-    fn freeImage(i: ?*Image) !void {
-        _ = i;
-        // try freeImage1(i);
-        // if (i != null) i.?.display.ally.destroy(i.?);
-    }
     pub fn bufImage(self: *Display, n: usize) !std.io.FixedBufferStream([]u8) {
         if (n > self.bufsize) {
             return error.BadCountBufSize;
@@ -401,7 +505,7 @@
     pub fn flush(self: *Display) !void {
         const n: i64 = @intCast(@intFromPtr(self.bufp) - @intFromPtr(self.buf.ptr));
         if (n <= 0) return error.UnableToFlushInvalidN;
-        std.debug.print("about to flush: {}\n{s}\n", .{ std.fmt.fmtSliceHexLower(self.buf[0..@intCast(n)]), self.buf[0..@intCast(n)] });
+        // std.debug.print("about to flush: {}\n{s}\n", .{ std.fmt.fmtSliceHexLower(self.buf[0..@intCast(n)]), self.buf[0..@intCast(n)] });
         if ((self.fd.write(self.buf[0..@intCast(n)]) catch return error.UnableToFlushWrite) != n) {
             self.bufp = self.buf.ptr; // might as well; chance of continuing
             return error.UnableToFlushN;
@@ -451,11 +555,10 @@
             break;
         }
         if (winp.*) |i| {
-            _ = i;
-            // try freeImage1(i);
-            // if (scrp.*.?.image != self.image)
-            // try freeImage(scrp.*.?.image);
-            // self.freeScreen(scrp.*.?);
+            try freeImage1(i);
+            if (scrp.*.?.image != self.image)
+                try scrp.*.?.image.free();
+            try scrp.*.?.free();
             scrp.* = null;
         }
         if (image == null) {
@@ -468,7 +571,7 @@
             winp.* = null;
             self.screenimage = null;
             if (image != self.image) {
-                try freeImage(image);
+                if (image) |i| try i.free();
             }
             return err;
         };
@@ -477,19 +580,30 @@
         if (!std.mem.eql(u8, buf[0..8], "noborder")) {
             r = r.inset(Borderwidth);
         }
-        std.debug.print("about to call _allocWindow", .{});
-        std.debug.print("winp: {*}", .{winp.*.?});
-        winp.* = self._allocWindow(winp.*.?, scrp.*.?, r, ref, DColor.White) catch |err| {
+        winp.* = self._allocWindow(winp.*, scrp.*.?, r, ref, DColor.White) catch |err| {
             std.debug.print("could not alloc window {}\n", .{err});
-            // self.freeScreen(scrp.*.?);
+            try scrp.*.?.free();
             scrp.* = null;
             self.screenimage = null;
             if (image != self.image)
-                try freeImage(image);
+                if (image) |im|
+                    try im.free();
             return err;
         };
         self.screenimage = winp.*;
     }
+    pub fn setDrawOp(self: *Display, op: DrawOp) !void {
+        if (op != .soverD) {
+            var bs = try self.bufImage(1 + 1);
+            var a = bs.writer();
+            a.writeByte('O') catch unreachable;
+            a.writeByte(@intFromEnum(op)) catch unreachable;
+        }
+    }
+    // asserts self.screen != null
+    pub fn getScreen(self: Display) *Image {
+        return self.screen.?;
+    }
 };
 fn iounit(file: std.fs.File) u32 {
     var buf: [128]u8 = undefined;
@@ -518,22 +632,28 @@
         const Ignore = 6;
     };
     pub const NChan = 7;
-    GREY1 = chan1(Color.Grey, 1),
-    GREY2 = chan1(Color.Grey, 2),
-    GREY4 = chan1(Color.Grey, 4),
-    GREY8 = chan1(Color.Grey, 8),
-    CMAP8 = chan1(Color.Map, 8),
-    RGB15 = chan4(Color.Ignore, 1, Color.Red, 5, Color.Green, 5, Color.Blue, 5),
-    RGB16 = chan3(Color.Red, 5, Color.Green, 6, Color.Blue, 5),
-    RGB24 = chan3(Color.Red, 8, Color.Green, 8, Color.Blue, 8),
-    RGBA32 = chan4(Color.Red, 8, Color.Green, 8, Color.Blue, 8, Color.Alpha, 8),
-    ARGB32 = chan4(Color.Alpha, 8, Color.Red, 8, Color.Green, 8, Color.Blue, 8),
-    XRGB32 = chan4(Color.Ignore, 8, Color.Red, 8, Color.Green, 8, Color.Blue, 8),
-    BGR24 = chan3(Color.Blue, 8, Color.Green, 8, Color.Red, 8),
-    ABGR32 = chan4(Color.Alpha, 8, Color.Blue, 8, Color.Green, 8, Color.Red, 8),
-    XBGR32 = chan4(Color.Ignore, 8, Color.Blue, 8, Color.Green, 8, Color.Red, 8),
+    grey1 = chan1(Color.Grey, 1),
+    grey2 = chan1(Color.Grey, 2),
+    grey4 = chan1(Color.Grey, 4),
+    grey8 = chan1(Color.Grey, 8),
+    cmap8 = chan1(Color.Map, 8),
+    rgb15 = chan4(Color.Ignore, 1, Color.Red, 5, Color.Green, 5, Color.Blue, 5),
+    rgb16 = chan3(Color.Red, 5, Color.Green, 6, Color.Blue, 5),
+    rgb24 = chan3(Color.Red, 8, Color.Green, 8, Color.Blue, 8),
+    rgba32 = chan4(Color.Red, 8, Color.Green, 8, Color.Blue, 8, Color.Alpha, 8),
+    argb32 = chan4(Color.Alpha, 8, Color.Red, 8, Color.Green, 8, Color.Blue, 8),
+    xrgb32 = chan4(Color.Ignore, 8, Color.Red, 8, Color.Green, 8, Color.Blue, 8),
+    bgr24 = chan3(Color.Blue, 8, Color.Green, 8, Color.Red, 8),
+    abgr32 = chan4(Color.Alpha, 8, Color.Blue, 8, Color.Green, 8, Color.Red, 8),
+    xbgr32 = chan4(Color.Ignore, 8, Color.Blue, 8, Color.Green, 8, Color.Red, 8),
     _,
     const channames: []const u8 = "rgbkamx";
+    fn TYPE(self: u32) u32 {
+        return (self >> 4) & 15;
+    }
+    fn NBITS(self: u32) u32 {
+        return self & 15;
+    }
     pub fn fromString(str: []const u8) Chan {
         // strip str
         const spaces: []const u8 = &.{ ' ', '\t', '\r', '\n' };
@@ -540,33 +660,53 @@
         const pos = std.mem.indexOfNone(u8, str, spaces).?;
         const s = str[pos..];
 
-        var depth: u32 = 0;
+        var d: u32 = 0;
         var chan: u32 = 0;
         var i: usize = 0;
         const chan_ = blk: {
             while (i < s.len) : (i += 2) {
                 if (std.ascii.isWhitespace(s[i])) break;
-                if (std.mem.indexOfScalar(u8, channames, s[0])) |ty| {
+                if (std.mem.indexOfScalar(u8, channames, s[i])) |ty| {
                     const n = std.fmt.parseInt(u8, s[i + 1 .. i + 2], 10) catch break :blk 0;
-                    depth += n;
+                    d += n;
                     chan <<= 8;
                     chan |= dc(@intCast(ty), @intCast(n));
                 } else break :blk 0;
             }
-            if (depth == 0 or (depth > 8 and depth % 8 != 0) or (depth < 8 and 8 % depth != 0)) break :blk 0;
+            if (d == 0 or (d > 8 and d % 8 != 0) or (d < 8 and 8 % d != 0)) break :blk 0;
             break :blk chan;
         };
 
         return @enumFromInt(chan_);
     }
-    pub fn getDepth(self: Chan) u32 {
-        var depth: u32 = 0;
+    pub fn toString(self: Chan, buf: []u8) ![]const u8 {
+        if (self.depth() == 0) {
+            return error.ChanDepthIsZero;
+        }
+        var rc: u32 = 0;
+        var c = @intFromEnum(self);
+        while (c != 0) : (c >>= 8) {
+            rc <<= 8;
+            rc |= c & 0xff;
+        }
+        var i: usize = 0;
+        c = rc;
+        while (c != 0) : (c >>= 8) {
+            buf[i] = channames[TYPE(c)];
+            i += 1;
+            buf[i] = @intCast('0' + NBITS(c));
+            i += 1;
+        }
+        return buf[0..i];
+    }
+    pub fn depth(self: Chan) u32 {
+        var d: u32 = 0;
         var c: u32 = @intFromEnum(self);
         while (c != 0) : (c >>= 8) {
-            depth += cdepth(c);
+            d += cdepth(c);
         }
-        if (depth == 0 or (depth > 8 and depth % 8 != 0) or (depth < 8 and 8 % depth != 0)) return 0;
-        return depth;
+        if (d == 0 or (d > 8 and d % 8 != 0) or (d < 8 and 8 % d != 0)) return 0;
+        return d;
     }
     fn dc(ty: u32, nbit: u32) u32 {
         return ((ty & 15) << 4) | (nbit & 15);
@@ -625,7 +765,7 @@
     none = 1,
     mesg = 2,
 };
-pub fn initDraw(ally: std.mem.Allocator, fontname: ?[]const u8, label: ?[]const u8) !void { // TODO we are skipping the error function. I think this is okay since we do errors different in zig
+pub fn initDraw(ally: std.mem.Allocator, fontname: ?[]const u8, label: ?[]const u8) !*Display {
     return genInitDraw(
         ally,
         "/dev",
@@ -635,9 +775,9 @@
         .none,
     );
 }
-pub fn genInitDraw(ally: std.mem.Allocator, devdir: []const u8, fontname: ?[]const u8, label: ?[]const u8, windir: []const u8, ref: Refresh) !void {
+pub fn genInitDraw(ally: std.mem.Allocator, devdir: []const u8, fontname: ?[]const u8, label: ?[]const u8, windir: []const u8, ref: Refresh) !*Display {
     var buf: [128]u8 = undefined;
-    display = try Display.init(ally, .{ .devdir = devdir, .windir = windir });
+    var display = try Display.init(ally, .{ .devdir = devdir, .windir = windir });
     // TODO deal with fonts
     _ = fontname;
     if (label) |l| blk: {
@@ -647,5 +787,6 @@
         _ = try labelfd.write(l);
     }
     const winnamefds = std.fmt.bufPrint(&buf, "{s}/winname", .{display.windir}) catch unreachable;
-    try display.genGetWindow(winnamefds, &screen, &_screen, ref);
+    try display.genGetWindow(winnamefds, &display.screen, &display._screen, ref);
+    return display;
 }