1 // Writes a pretty color gradient to a totally real TGA file.
2 //
3 const std = @import("std");
4
5 pub fn main() !void {
6 const file = try std.fs.cwd().createFile("foo.tga", .{ .read = true });
7 defer file.close();
8
9 const width: u16 = 300;
10 const height: u16 = 200;
11
12 // https://stackoverflow.com/a/49658800/695615
13 // https://en.wikipedia.org/wiki/Truevision_TGA
14 // http://paulbourke.net/dataformats/tga/
15 // Note that all multi-byte values are little-endian.
16 const header: [18]u8 = .{
17 0, // 1 - No ID field (length of 0)
18 0, // 2 - No color map
19 2, // 3 - Uncompressed true-color image
20 0, // 4 \
21 0, // 5 |
22 0, // 6 | Color map information (none)
23 0, // 7 |
24 0, // 8 /
25 0, // 9 \ Origin X (16 bits)
26 0, // 10 /
27 0, // 11 \ Origin Y (16 bits)
28 0, // 12 /
29 width & 255, // 13 \ Width (px) mask last 8 bits
30 (width >> 8) & 255, // 14 / Width (px) right shift and mask to get first 8 bits
31 height & 255, // 15 \ Height (px) last
32 (height >> 8) & 255, // 16 / Height (px) first
33 24, // 17 - Bits per pixel (3 colors, 8 bits each)
34 0b00100000, // 18 - Image descriptor (Bits 4,5 are origin. Set to "top left".)
35 };
36
37 try file.writeAll(&header);
38
39
40 // Scale from image dimensions to color min and max.
41 var w_scale: f32 = 255 / @as(f32, width);
42 var h_scale: f32 = 255 / @as(f32, height);
43
44 // Buffer one row's worth of pixels (makes a huge difference on huge images).
45 var out_buffer: [3 * width]u8 = undefined;
46
47 var w: u32 = 0; // Pixel counter per row (width)
48 var h: u32 = 0; // Row counter (height)
49
50 // For height's worth of rows...
51 while (h < height) : (h += 1) {
52
53 // Reset row pixel counter
54 w = 0;
55
56 // Fill row buffer
57 while (w < width) : (w += 1) {
58 // IMPORTANT: TGA stores in BGR order, not RGB.
59 const pixel_r = w * 3 + 2;
60 const pixel_g = w * 3 + 1;
61 const pixel_b = w * 3;
62
63 out_buffer[pixel_r] = @floatToInt(u8, @intToFloat(f32, w) * w_scale); // increase across
64 out_buffer[pixel_g] = @floatToInt(u8, @intToFloat(f32, h) * h_scale); // increase down
65 out_buffer[pixel_b] = @floatToInt(u8, @intToFloat(f32, width-w) * w_scale); // decrease across
66 }
67
68 // Write row
69 try file.writeAll(&out_buffer);
70 }
71 }