move display config to separate file
This commit is contained in:
		
							parent
							
								
									a7b917d3e9
								
							
						
					
					
						commit
						f1e635d786
					
				
					 2 changed files with 56 additions and 53 deletions
				
			
		
							
								
								
									
										14
									
								
								src/display.zig
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/display.zig
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | |||
| // Image specifications | ||||
| pub const WIDTH = 128; | ||||
| pub const HEIGHT = 64; | ||||
| 
 | ||||
| // Text specifications | ||||
| pub const FONT_WIDTH = 5; | ||||
| pub const FONT_HEIGHT = 8; | ||||
| 
 | ||||
| pub const CHARS_PER_LINE = 25; // 25 * 5 = 125 so we have 3px left over | ||||
| pub const BORDER_LEFT = 1; // 1 empty px left, 2 empty on right | ||||
| pub const LINES = 8; | ||||
| 
 | ||||
| // Device specifications | ||||
| pub const PAGES = 8; | ||||
							
								
								
									
										95
									
								
								src/main.zig
									
										
									
									
									
								
							
							
						
						
									
										95
									
								
								src/main.zig
									
										
									
									
									
								
							|  | @ -1,4 +1,5 @@ | |||
| const std = @import("std"); | ||||
| const display = @import("display.zig"); | ||||
| const chars = @import("images/images.zig").chars; | ||||
| 
 | ||||
| // The package manager will install headers from our dependency in zig's build | ||||
|  | @ -9,22 +10,7 @@ const c = @cImport({ | |||
|     @cInclude("i2cdriver.h"); | ||||
| }); | ||||
| 
 | ||||
| // Image specifications | ||||
| const WIDTH = 128; | ||||
| const HEIGHT = 64; | ||||
| 
 | ||||
| // Text specifications | ||||
| const FONT_WIDTH = 5; | ||||
| const FONT_HEIGHT = 8; | ||||
| 
 | ||||
| const CHARS_PER_LINE = 25; // 25 * 5 = 125 so we have 3px left over | ||||
| const BORDER_LEFT = 1; // 1 empty px left, 2 empty on right | ||||
| const LINES = 8; | ||||
| 
 | ||||
| // Device specifications | ||||
| const PAGES = 8; | ||||
| 
 | ||||
| var lines: [LINES]*[:0]u8 = undefined; | ||||
| var lines: [display.LINES]*[:0]u8 = undefined; | ||||
| 
 | ||||
| fn usage(args: [][]u8) !void { | ||||
|     const writer = std.io.getStdErr().writer(); | ||||
|  | @ -52,10 +38,10 @@ pub fn main() !void { | |||
|     // we take command line options. So cli will overwrite any lines specified | ||||
|     // from the file | ||||
|     const stdin_file = std.io.getStdIn(); | ||||
|     var stdin_data: [WIDTH * HEIGHT + 1]u8 = undefined; | ||||
|     var stdin_data: [display.WIDTH * display.HEIGHT + 1]u8 = undefined; | ||||
|     // Need this to support deallocation of memory | ||||
|     var line_inx: usize = 0; | ||||
|     var stdin_lines: [LINES][:0]u8 = undefined; | ||||
|     var stdin_lines: [display.LINES][:0]u8 = undefined; | ||||
|     defer { | ||||
|         for (0..line_inx) |i| { | ||||
|             alloc.free(stdin_lines[i]); | ||||
|  | @ -84,7 +70,7 @@ pub fn main() !void { | |||
|     const opts = try processArgs(alloc, args, &lines); | ||||
|     defer alloc.destroy(opts); | ||||
|     if (opts.background_filename.len > 0) try stdout.print("Converting {s}\n", .{opts.background_filename}); | ||||
|     var pixels: [WIDTH * HEIGHT]u8 = undefined; | ||||
|     var pixels: [display.WIDTH * display.HEIGHT]u8 = undefined; | ||||
|     try convertImage(opts.background_filename, &pixels, textForLine); | ||||
|     try bw.flush(); | ||||
| 
 | ||||
|  | @ -95,7 +81,7 @@ pub fn main() !void { | |||
|     // try stdout.print("Run `zig build test` to run the tests.\n", .{}); | ||||
| } | ||||
| 
 | ||||
| fn processArgs(allocator: std.mem.Allocator, args: [][:0]u8, line_array: *[LINES]*const [:0]u8) !*Options { | ||||
| fn processArgs(allocator: std.mem.Allocator, args: [][:0]u8, line_array: *[display.LINES]*const [:0]u8) !*Options { | ||||
|     if (args.len < 2) try usage(args); | ||||
|     const prefix = "/dev/ttyUSB"; | ||||
|     var opts = try allocator.create(Options); | ||||
|  | @ -119,10 +105,10 @@ fn processArgs(allocator: std.mem.Allocator, args: [][:0]u8, line_array: *[LINES | |||
|             continue; | ||||
|         } | ||||
|         if (line_number) |line| { | ||||
|             if (arg.len > CHARS_PER_LINE) { | ||||
|             if (arg.len > display.CHARS_PER_LINE) { | ||||
|                 try std.io.getStdErr().writer().print( | ||||
|                     "ERROR: text for line {d} has {d} chars, exceeding maximum length {d}\n", | ||||
|                     .{ line, arg.len, CHARS_PER_LINE }, | ||||
|                     .{ line, arg.len, display.CHARS_PER_LINE }, | ||||
|                 ); | ||||
|                 std.os.exit(1); | ||||
|             } | ||||
|  | @ -162,13 +148,16 @@ fn sendPixelsToStdOut(pixels: []const u8) !void { | |||
|     var bw = std.io.bufferedWriter(stdout_file); | ||||
|     const stdout = bw.writer(); | ||||
|     defer bw.flush() catch unreachable; // don't forget to flush! | ||||
|     for (0..HEIGHT) |i| { | ||||
|         try stdout.print("{d:0>2}: {s}\n", .{ i, fmtSliceGreyscaleImage(pixels[(i * WIDTH)..((i + 1) * WIDTH)]) }); | ||||
|     for (0..display.HEIGHT) |i| { | ||||
|         try stdout.print( | ||||
|             "{d:0>2}: {s}\n", | ||||
|             .{ i, fmtSliceGreyscaleImage(pixels[(i * display.WIDTH)..((i + 1) * display.WIDTH)]) }, | ||||
|         ); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fn sendPixelsThroughI2CDriver(pixels: []const u8, file: [*:0]const u8, device_id: u8) !void { | ||||
|     var pixels_write_command = [_]u8{0x00} ** ((WIDTH * PAGES) + 1); | ||||
|     var pixels_write_command = [_]u8{0x00} ** ((display.WIDTH * display.PAGES) + 1); | ||||
|     pixels_write_command[0] = 0x40; | ||||
|     packPixelsToDeviceFormat(pixels, pixels_write_command[1..]); | ||||
|     var i2c = c.I2CDriver{ | ||||
|  | @ -228,8 +217,8 @@ fn sendPixelsThroughI2CDriver(pixels: []const u8, file: [*:0]const u8, device_id | |||
| fn packPixelsToDeviceFormat(pixels: []const u8, packed_pixels: []u8) void { | ||||
|     // Each u8 in pixels is a single bit. We need to pack these bits | ||||
|     for (packed_pixels, 0..) |*b, i| { | ||||
|         const column = i % WIDTH; | ||||
|         const page = i / WIDTH; | ||||
|         const column = i % display.WIDTH; | ||||
|         const page = i / display.WIDTH; | ||||
| 
 | ||||
|         // if (column == 0) std.debug.print("{d}: ", .{page}); | ||||
|         // pixel array will be 8x as "high" as the data array we are sending to | ||||
|  | @ -238,7 +227,7 @@ fn packPixelsToDeviceFormat(pixels: []const u8, packed_pixels: []u8) void { | |||
|         // | ||||
|         // To convert from the pixel array above, we need to: | ||||
|         //   1. convert from device page to a base "row" in the pixel array | ||||
|         const row = page * PAGES; | ||||
|         const row = page * display.PAGES; | ||||
|         //   2. We will have 8 rows for each base row | ||||
|         //   3. Multiple each row by the width to get the index of the start of | ||||
|         //      the row | ||||
|  | @ -253,14 +242,14 @@ fn packPixelsToDeviceFormat(pixels: []const u8, packed_pixels: []u8) void { | |||
|         //      per source byte | ||||
|         //   2. Shift that bit into the proper position in our destination byte | ||||
| 
 | ||||
|         b.* = (pixels[(0 + row) * WIDTH + column] & 0x01) << 0 | | ||||
|             (pixels[(1 + row) * WIDTH + column] & 0x01) << 1 | | ||||
|             (pixels[(2 + row) * WIDTH + column] & 0x01) << 2 | | ||||
|             (pixels[(3 + row) * WIDTH + column] & 0x01) << 3 | | ||||
|             (pixels[(4 + row) * WIDTH + column] & 0x01) << 4 | | ||||
|             (pixels[(5 + row) * WIDTH + column] & 0x01) << 5 | | ||||
|             (pixels[(6 + row) * WIDTH + column] & 0x01) << 6 | | ||||
|             (pixels[(7 + row) * WIDTH + column] & 0x01) << 7; | ||||
|         b.* = (pixels[(0 + row) * display.WIDTH + column] & 0x01) << 0 | | ||||
|             (pixels[(1 + row) * display.WIDTH + column] & 0x01) << 1 | | ||||
|             (pixels[(2 + row) * display.WIDTH + column] & 0x01) << 2 | | ||||
|             (pixels[(3 + row) * display.WIDTH + column] & 0x01) << 3 | | ||||
|             (pixels[(4 + row) * display.WIDTH + column] & 0x01) << 4 | | ||||
|             (pixels[(5 + row) * display.WIDTH + column] & 0x01) << 5 | | ||||
|             (pixels[(6 + row) * display.WIDTH + column] & 0x01) << 6 | | ||||
|             (pixels[(7 + row) * display.WIDTH + column] & 0x01) << 7; | ||||
| 
 | ||||
|         // std.debug.print("{s}", .{std.fmt.fmtSliceHexLower(&[_]u8{b.*})}); | ||||
|         // if (column == 127) std.debug.print("\n", .{}); | ||||
|  | @ -305,7 +294,7 @@ fn reportMagickError(mw: ?*c.MagickWand) !void { | |||
| fn textForLine(line: usize) []u8 { | ||||
|     return lines[line].*; | ||||
| } | ||||
| fn convertImage(filename: [:0]u8, pixels: *[WIDTH * HEIGHT]u8, text_fn: *const fn (usize) []u8) !void { | ||||
| fn convertImage(filename: [:0]u8, pixels: *[display.WIDTH * display.HEIGHT]u8, text_fn: *const fn (usize) []u8) !void { | ||||
|     c.MagickWandGenesis(); | ||||
|     defer c.MagickWandTerminus(); | ||||
|     var mw = c.NewMagickWand(); | ||||
|  | @ -343,7 +332,7 @@ fn convertImage(filename: [:0]u8, pixels: *[WIDTH * HEIGHT]u8, text_fn: *const f | |||
|     // This should be 48x64 with our test | ||||
|     // Command line resize works differently than this. Here we need to find | ||||
|     // new width and height based on the input aspect ratio ourselves | ||||
|     const resize_dimensions = getNewDimensions(w, h, WIDTH, HEIGHT); | ||||
|     const resize_dimensions = getNewDimensions(w, h, display.WIDTH, display.HEIGHT); | ||||
| 
 | ||||
|     std.log.debug("Dimensions for resize: {d}x{d}\n", .{ resize_dimensions.width, resize_dimensions.height }); | ||||
| 
 | ||||
|  | @ -369,21 +358,21 @@ fn convertImage(filename: [:0]u8, pixels: *[WIDTH * HEIGHT]u8, text_fn: *const f | |||
|     // around it means that the offset will be negative | ||||
|     status = c.MagickExtentImage( | ||||
|         mw, | ||||
|         WIDTH, | ||||
|         HEIGHT, | ||||
|         -@intCast(isize, (WIDTH - resize_dimensions.width) / 2), | ||||
|         -@intCast(isize, (HEIGHT - resize_dimensions.height) / 2), | ||||
|         display.WIDTH, | ||||
|         display.HEIGHT, | ||||
|         -@intCast(isize, (display.WIDTH - resize_dimensions.width) / 2), | ||||
|         -@intCast(isize, (display.HEIGHT - resize_dimensions.height) / 2), | ||||
|     ); | ||||
| 
 | ||||
|     if (status == c.MagickFalse) | ||||
|         return error.CouldNotSetExtent; | ||||
| 
 | ||||
|     for (0..LINES) |i| { | ||||
|     for (0..display.LINES) |i| { | ||||
|         const text = text_fn(i); | ||||
|         if (text.len == 0) continue; | ||||
|         // We have text! | ||||
|         const y: isize = FONT_HEIGHT * @intCast(isize, i); | ||||
|         var x: isize = BORDER_LEFT; | ||||
|         const y: isize = display.FONT_HEIGHT * @intCast(isize, i); | ||||
|         var x: isize = display.BORDER_LEFT; | ||||
|         var left_spaces: isize = 0; | ||||
|         for (text) |ch| { | ||||
|             if (ch == ' ') { | ||||
|  | @ -392,7 +381,7 @@ fn convertImage(filename: [:0]u8, pixels: *[WIDTH * HEIGHT]u8, text_fn: *const f | |||
|             } | ||||
|             break; | ||||
|         } | ||||
|         x += (FONT_WIDTH * left_spaces); | ||||
|         x += (display.FONT_WIDTH * left_spaces); | ||||
|         mw = try drawString(mw, text[@intCast(usize, left_spaces)..], x, y); | ||||
|     } | ||||
| 
 | ||||
|  | @ -415,12 +404,12 @@ fn convertImage(filename: [:0]u8, pixels: *[WIDTH * HEIGHT]u8, text_fn: *const f | |||
|     if (status == c.MagickFalse) | ||||
|         return error.CouldNotQuantizeImage; | ||||
| 
 | ||||
|     status = c.MagickExportImagePixels(mw, 0, 0, WIDTH, HEIGHT, "I", c.CharPixel, @ptrCast(*anyopaque, pixels)); | ||||
|     status = c.MagickExportImagePixels(mw, 0, 0, display.WIDTH, display.HEIGHT, "I", c.CharPixel, @ptrCast(*anyopaque, pixels)); | ||||
| 
 | ||||
|     if (status == c.MagickFalse) | ||||
|         return error.CouldNotExportImage; | ||||
| 
 | ||||
|     for (0..WIDTH * HEIGHT) |i| { | ||||
|     for (0..display.WIDTH * display.HEIGHT) |i| { | ||||
|         switch (pixels[i]) { | ||||
|             0x00 => pixels[i] = 0xFF, | ||||
|             0xFF => pixels[i] = 0x00, | ||||
|  | @ -434,7 +423,7 @@ fn drawString(mw: ?*c.MagickWand, str: []const u8, x: isize, y: isize) !?*c.Magi | |||
|         rc = try drawCharacter( | ||||
|             rc, | ||||
|             ch, | ||||
|             -(x + @intCast(isize, FONT_WIDTH * i)), | ||||
|             -(x + @intCast(isize, display.FONT_WIDTH * i)), | ||||
|             -y, | ||||
|         ); | ||||
|     } | ||||
|  | @ -476,8 +465,8 @@ fn drawCharacter(mw: ?*c.MagickWand, char: u8, x: isize, y: isize) !?*c.MagickWa | |||
|         // I think our characters are offset by 6px in the x and 8 in the y | ||||
|         status = c.MagickExtentImage( | ||||
|             cw, | ||||
|             WIDTH, | ||||
|             HEIGHT, | ||||
|             display.WIDTH, | ||||
|             display.HEIGHT, | ||||
|             x, | ||||
|             y, | ||||
|         ); | ||||
|  | @ -526,9 +515,9 @@ test "gets correct bytes" { | |||
|     } | ||||
|     const line: [:0]u8 = @constCast("Hello\\!"); | ||||
|     lines[5] = &line; | ||||
|     var pixels: [WIDTH * HEIGHT]u8 = undefined; | ||||
|     var pixels: [display.WIDTH * display.HEIGHT]u8 = undefined; | ||||
| 
 | ||||
|     var expected_pixels: *const [WIDTH * HEIGHT]u8 = @embedFile("testExpectedBytes.bin"); | ||||
|     var expected_pixels: *const [display.WIDTH * display.HEIGHT]u8 = @embedFile("testExpectedBytes.bin"); | ||||
| 
 | ||||
|     // [_]u8{..,..,..} | ||||
|     try convertImage(opts.background_filename, &pixels, textForLine); | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue