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