-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
336 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
const std = @import("std"); | ||
const Build = std.Build; | ||
const sycl_badge = @import("sycl_badge"); | ||
|
||
pub const author_name = "Stevie Hryciw"; | ||
pub const author_handle = "hryx"; | ||
pub const cart_title = "dvd"; | ||
pub const description = "Bouncing DVD logo screensaver"; | ||
|
||
pub fn build(b: *Build) void { | ||
const optimize = b.standardOptimizeOption(.{}); | ||
const sycl_badge_dep = b.dependency("sycl_badge", .{}); | ||
|
||
const cart = sycl_badge.add_cart(sycl_badge_dep, b, .{ | ||
.name = "dvd", | ||
.optimize = optimize, | ||
.root_source_file = b.path("src/main.zig"), | ||
}); | ||
add_dvd_assets_step(b, sycl_badge_dep, cart); | ||
cart.install(b); | ||
} | ||
|
||
// Thank you to Fabio for the code generation step. | ||
fn add_dvd_assets_step( | ||
b: *Build, | ||
sycl_badge_dep: *Build.Dependency, | ||
cart: *sycl_badge.Cart, | ||
) void { | ||
const convert = b.addExecutable(.{ | ||
.name = "convert_gfx", | ||
.root_source_file = b.path("build/convert_gfx.zig"), | ||
.target = b.host, | ||
.optimize = cart.options.optimize, | ||
.link_libc = true, | ||
}); | ||
convert.root_module.addImport("zigimg", b.dependency("zigimg", .{}).module("zigimg")); | ||
|
||
const gen_gfx = b.addRunArtifact(convert); | ||
gen_gfx.addArg("-i"); | ||
gen_gfx.addFileArg(b.path("assets/dvd.png")); | ||
gen_gfx.addArg(std.fmt.comptimePrint("{}", .{8})); | ||
gen_gfx.addArg(std.fmt.comptimePrint("{}", .{false})); | ||
gen_gfx.addArg("-o"); | ||
const gfx_zig = gen_gfx.addOutputFileArg("gfx.zig"); | ||
|
||
const gfx_mod = b.addModule("gfx", .{ | ||
.root_source_file = gfx_zig, | ||
.optimize = cart.options.optimize, | ||
}); | ||
gfx_mod.addImport("cart-api", sycl_badge_dep.module("cart-api")); | ||
|
||
cart.wasm.step.dependOn(&gen_gfx.step); | ||
cart.wasm.root_module.addImport("gfx", gfx_mod); | ||
cart.cart_lib.root_module.addImport("gfx", gfx_mod); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
.{ | ||
.name = "dvd", | ||
.version = "0.0.0", | ||
.dependencies = .{ | ||
.sycl_badge = .{ .path = "../../.." }, | ||
.zigimg = .{ | ||
.url = "https://github.com/zigimg/zigimg/archive/637974e2d31dcdbc33f1e9cc8ffb2e46abd2e215.tar.gz", | ||
.hash = "122012026c3a65ff1d4acba3b3fe80785f7cee9c6b4cdaff7ed0fbf23b0a6c803989", | ||
}, | ||
}, | ||
.paths = .{ | ||
"build.zig", | ||
"build.zig.zon", | ||
"src", | ||
"build", | ||
"assets", | ||
}, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
const std = @import("std"); | ||
const allocator = std.heap.c_allocator; | ||
const Image = @import("zigimg").Image; | ||
|
||
const ConvertFile = struct { | ||
path: []const u8, | ||
bits: u4, | ||
transparency: bool, | ||
}; | ||
|
||
pub fn main() !void { | ||
var args = try std.process.argsWithAllocator(allocator); | ||
defer args.deinit(); | ||
|
||
_ = args.next(); | ||
var in_files = std.ArrayList(ConvertFile).init(allocator); | ||
var out_path: []const u8 = undefined; | ||
while (args.next()) |arg| { | ||
if (std.mem.eql(u8, arg, "-i")) { | ||
const path = args.next() orelse return error.MissingArg; | ||
const bits = args.next() orelse return error.MissingArg; | ||
const transparency = args.next() orelse return error.MissingArg; | ||
try in_files.append(.{ .path = path, .bits = @intCast(bits[0] - '0'), .transparency = transparency[0] == 't' }); | ||
} else if (std.mem.eql(u8, arg, "-o")) { | ||
out_path = args.next() orelse return error.MissingArg; | ||
} | ||
} | ||
|
||
const out_file = try std.fs.cwd().createFile(out_path, .{}); | ||
defer out_file.close(); | ||
|
||
const writer = out_file.writer(); | ||
try writer.writeAll("const PackedIntSlice = @import(\"std\").packed_int_array.PackedIntSlice;\n"); | ||
try writer.writeAll("const DisplayColor = @import(\"cart-api\").DisplayColor;\n\n"); | ||
|
||
for (in_files.items) |in_file| { | ||
try convert(in_file, writer); | ||
} | ||
} | ||
|
||
fn convert(args: ConvertFile, writer: std.fs.File.Writer) !void { | ||
const N = 8 / args.bits; | ||
|
||
var image = try Image.fromFilePath(allocator, args.path); | ||
defer image.deinit(); | ||
|
||
var colors = std.ArrayList(Color).init(allocator); | ||
defer colors.deinit(); | ||
if (args.transparency) try colors.append(.{ .r = 31, .g = 0, .b = 31 }); | ||
var indices = try std.ArrayList(usize).initCapacity(allocator, image.width * image.height); | ||
defer indices.deinit(); | ||
var it = image.iterator(); | ||
while (it.next()) |pixel| { | ||
const color = Color{ | ||
.r = @intFromFloat(31.0 * pixel.r), | ||
.g = @intFromFloat(63.0 * pixel.g), | ||
.b = @intFromFloat(31.0 * pixel.b), | ||
}; | ||
const index = try getIndex(&colors, color); | ||
indices.appendAssumeCapacity(index); | ||
} | ||
var packed_data = try allocator.alloc(u8, indices.items.len / N); | ||
defer allocator.free(packed_data); | ||
for (packed_data, 0..) |_, i| { | ||
packed_data[i] = 0; | ||
for (0..N) |n| { | ||
const shift: u3 = @intCast(n * args.bits); | ||
packed_data[i] |= @intCast(indices.items[N * i + n] << shift); | ||
} | ||
} | ||
|
||
{ | ||
const name = std.fs.path.stem(args.path); | ||
try writer.print("pub const {s} = struct {{\n", .{name}); | ||
|
||
try writer.print(" pub const width = {};\n", .{image.width}); | ||
try writer.print(" pub const height = {};\n", .{image.height}); | ||
|
||
try writer.writeAll(" pub const colors = [_]DisplayColor{\n"); | ||
for (colors.items) |c| { | ||
try writer.print(" .{{ .r = {}, .g = {}, .b = {} }},\n", .{ c.r, c.g, c.b }); | ||
} | ||
try writer.writeAll(" };\n"); | ||
|
||
try writer.print(" pub const indices = PackedIntSlice(u{}).init(@constCast(data[0..]), data.len * {});\n", .{ args.bits, N }); | ||
try writer.writeAll(" const data = [_]u8{\n"); | ||
for (packed_data, 0..) |index, i| { | ||
if (i % 32 == 0) try writer.writeAll(" "); | ||
try writer.print("{}, ", .{index}); | ||
if ((i + 1) % 32 == 0) try writer.writeAll("\n"); | ||
} | ||
try writer.writeAll(" };\n"); | ||
|
||
try writer.writeAll("};\n\n"); | ||
} | ||
} | ||
|
||
pub const Color = packed struct(u16) { | ||
b: u5, | ||
g: u6, | ||
r: u5, | ||
|
||
fn eql(self: Color, other: Color) bool { | ||
return @as(u16, @bitCast(self)) == @as(u16, @bitCast(other)); | ||
} | ||
}; | ||
|
||
fn getIndex(colors: *std.ArrayList(Color), color: Color) !usize { | ||
for (colors.items, 0..) |c, i| { | ||
if (c.eql(color)) return i; | ||
} | ||
try colors.append(color); | ||
return colors.items.len - 1; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
const std = @import("std"); | ||
const cart = @import("cart-api"); | ||
const gfx = @import("gfx"); | ||
|
||
export fn start() void { | ||
// Clear garbage bytes from framebuffer at init since the whole screen is not cleared otherwise. | ||
for (cart.framebuffer[0..]) |*pos| { | ||
pos.* = .{ .r = 0, .b = 0, .g = 0 }; | ||
} | ||
} | ||
|
||
var dvd_hue: f32 = 0; | ||
var dvd_x: isize = 0; | ||
var dvd_y: isize = 0; | ||
var dvd_dx: isize = 2; | ||
var dvd_dy: isize = 1; | ||
var odd_frame = false; | ||
|
||
// These things are super bright at full strength. | ||
const neopixel_brightness = 10.0; | ||
|
||
export fn update() void { | ||
const color = hsv_to_rgb(.{ .h = dvd_hue, .s = 1, .v = 1 }); | ||
drawDvd(gfx.dvd, @intCast(dvd_x), @intCast(dvd_y), color); | ||
dvd_hue += 5; | ||
if (dvd_hue >= 360.0) dvd_hue = 0; | ||
|
||
// The DVD logo gets stuck hitting the places without a fractional angle. | ||
// Offset the dx every other frame to make it look a tiny bit more interesting. | ||
odd_frame = !odd_frame; | ||
dvd_x += if (odd_frame) dvd_dx else @divFloor((dvd_dx * 3), 2); | ||
dvd_y += dvd_dy; | ||
if (dvd_x < 0) { | ||
dvd_dx *= -1; | ||
dvd_x = 0; | ||
} | ||
if (dvd_y < 0) { | ||
dvd_dy *= -1; | ||
dvd_y = 0; | ||
} | ||
if (dvd_x >= cart.screen_width - gfx.dvd.width) { | ||
dvd_dx *= -1; | ||
dvd_x = cart.screen_width - gfx.dvd.width; | ||
} | ||
if (dvd_y >= cart.screen_height - gfx.dvd.height) { | ||
dvd_dy *= -1; | ||
dvd_y = cart.screen_height - gfx.dvd.height; | ||
} | ||
|
||
// Press A to light up the neopixels. | ||
// This was just so we could create a light show in the theater. :> | ||
const np_color: cart.NeopixelColor = if (cart.controls.a) .{ | ||
.g = @intFromFloat(color.g * neopixel_brightness), | ||
.r = @intFromFloat(color.r * neopixel_brightness), | ||
.b = @intFromFloat(color.b * neopixel_brightness), | ||
} else .{ .g = 0, .r = 0, .b = 0 }; | ||
cart.neopixels.* = .{np_color} ** 5; | ||
} | ||
|
||
pub fn drawDvd(sprite: anytype, pos_x: usize, pos_y: usize, color: Rgb) void { | ||
var y: usize = 0; | ||
while (y < sprite.height) : (y += 1) { | ||
var x: usize = 0; | ||
while (x < sprite.width) : (x += 1) { | ||
const dst_x = pos_x + x; | ||
const dst_y = pos_y + y; | ||
const index = y * sprite.width + x; | ||
const src = sprite.colors[sprite.indices.get(index)]; | ||
var dst = &cart.framebuffer[dst_y * cart.screen_width + dst_x]; | ||
dst.r = @intFromFloat(@as(f32, @floatFromInt(src.r)) * color.r); | ||
dst.g = @intFromFloat(@as(f32, @floatFromInt(src.g)) * color.g); | ||
dst.b = @intFromFloat(@as(f32, @floatFromInt(src.b)) * color.b); | ||
cart.framebuffer[dst_y * cart.screen_width + dst_x] = dst.*; | ||
} | ||
} | ||
} | ||
|
||
const Hsv = struct { | ||
h: f32, | ||
s: f32, | ||
v: f32, | ||
}; | ||
|
||
const Rgb = struct { | ||
r: f32, | ||
g: f32, | ||
b: f32, | ||
}; | ||
|
||
fn hsv_to_rgb(in: Hsv) Rgb { | ||
var hh: f32 = undefined; | ||
var p: f32 = undefined; | ||
var q: f32 = undefined; | ||
var t: f32 = undefined; | ||
var ff: f32 = undefined; | ||
var i: i32 = undefined; | ||
var out: Rgb = undefined; | ||
|
||
if (in.s <= 0.0) { | ||
out.r = in.v; | ||
out.g = in.v; | ||
out.b = in.v; | ||
return out; | ||
} | ||
hh = in.h; | ||
if (hh >= 360.0) hh = 0.0; | ||
hh /= 60.0; | ||
i = @intFromFloat(hh); | ||
ff = hh - @as(f32, @floatFromInt(i)); | ||
p = in.v * (1.0 - in.s); | ||
q = in.v * (1.0 - (in.s * ff)); | ||
t = in.v * (1.0 - (in.s * (1.0 - ff))); | ||
|
||
switch (i) { | ||
0 => { | ||
out.r = in.v; | ||
out.g = t; | ||
out.b = p; | ||
}, | ||
1 => { | ||
out.r = q; | ||
out.g = in.v; | ||
out.b = p; | ||
}, | ||
2 => { | ||
out.r = p; | ||
out.g = in.v; | ||
out.b = t; | ||
}, | ||
3 => { | ||
out.r = p; | ||
out.g = q; | ||
out.b = in.v; | ||
}, | ||
4 => { | ||
out.r = t; | ||
out.g = p; | ||
out.b = in.v; | ||
}, | ||
else => { | ||
out.r = in.v; | ||
out.g = p; | ||
out.b = q; | ||
}, | ||
} | ||
return out; | ||
} |