SHOW:
|
|
- or go back to the newest paste.
1 | const std = @import("std"); | |
2 | const warn = std.debug.warn; | |
3 | const assert = std.debug.assert; | |
4 | ||
5 | const File = std.os.File; | |
6 | const ASCII = std.ASCII; | |
7 | ||
8 | const TypeId = @import("builtin").TypeId; | |
9 | const TypeInfo = @import("builtin").TypeInfo; | |
10 | ||
11 | /// clamps a value into the given range [min;max] | |
12 | fn clamp(comptime T: type, v: T, min: T, max: T) T { | |
13 | if (v < min) { | |
14 | return min; | |
15 | } else { | |
16 | if (v > max) { | |
17 | return max; | |
18 | } else { | |
19 | return v; | |
20 | } | |
21 | } | |
22 | } | |
23 | ||
24 | fn Color(comptime T: type) type { | |
25 | return packed struct { | |
26 | const Self = @This(); | |
27 | const Component = T; | |
28 | ||
29 | R: Component, | |
30 | G: Component, | |
31 | B: Component, | |
32 | ||
33 | fn equals(self: Self, b: Self) bool { | |
34 | return self.R == b.R and self.G == b.G and self.B == b.B; | |
35 | } | |
36 | }; | |
37 | } | |
38 | ||
39 | /// convert any numeric type into range [0;1] | |
40 | /// integers will be mapped from "min" to "max", floats will be clamped. | |
41 | fn map_to_uniform(comptime T: type, value: T) f128 { | |
42 | switch (@typeInfo(T)) { | |
43 | TypeId.Int => |t| { | |
44 | if (t.is_signed) { | |
45 | const min = @intToFloat(f128, -(1 << (t.bits - 1))); | |
46 | const max = @intToFloat(f128, (1 << (t.bits - 1))); | |
47 | return (@intToFloat(f128, value) - min) / (max - min); | |
48 | } else { | |
49 | const max = @intToFloat(f128, (1 << t.bits) - 1); | |
50 | return @intToFloat(f128, value) / max; | |
51 | } | |
52 | }, | |
53 | TypeId.Float => |t| { | |
54 | return value; | |
55 | }, | |
56 | else => @panic("Unsupported type " ++ @typeName(T)), | |
57 | } | |
58 | } | |
59 | ||
60 | /// convert range [0;1] to any numeric type | |
61 | /// integers will be mapped into rang "min" to "max", floats will be clamped | |
62 | fn map_from_uniform(comptime T: type, value: f128) T { | |
63 | switch (@typeInfo(T)) { | |
64 | TypeId.Int => |t| { | |
65 | if (t.is_signed) { | |
66 | const min = @intToFloat(f128, -(1 << (t.bits - 1))); | |
67 | const max = @intToFloat(f128, (1 << (t.bits - 1))); | |
68 | return @floatToInt(T, (max - min) * value + min); | |
69 | } else { | |
70 | const max = @intToFloat(f128, (1 << t.bits) - 1); | |
71 | return @floatToInt(T, max * value); | |
72 | } | |
73 | }, | |
74 | TypeId.Float => |t| { | |
75 | return @floatCast(T, value); | |
76 | }, | |
77 | else => @panic("Unsupported type " ++ @typeName(T)), | |
78 | } | |
79 | } | |
80 | ||
81 | /// Maps a color from one type to another. | |
82 | fn mapColor(comptime TIn: type, comptime TOut: type, c: TIn) TOut { | |
83 | if (TIn == TOut) | |
84 | return c; | |
85 | ||
86 | var r = map_to_uniform(TIn.Component, c.R); | |
87 | var g = map_to_uniform(TIn.Component, c.G); | |
88 | var b = map_to_uniform(TIn.Component, c.B); | |
89 | ||
90 | r = clamp(f128, r, 0.0, 1.0); | |
91 | g = clamp(f128, g, 0.0, 1.0); | |
92 | b = clamp(f128, b, 0.0, 1.0); | |
93 | ||
94 | return TOut{ | |
95 | .R = map_from_uniform(TOut.Component, r), | |
96 | .G = map_from_uniform(TOut.Component, g), | |
97 | .B = map_from_uniform(TOut.Component, b), | |
98 | }; | |
99 | } | |
100 | ||
101 | test "mapColor" { | |
102 | // test will run with comptime, but won't link without it. (lld: error: undefined symbol: __divtf3) | |
103 | comptime { | |
104 | var c_u8 = Color(u8) { .R = 0xFF, .G = 0x00, .B = 0x00 }; | |
105 | var c_f32 = Color(f32) { .R = 1.0, .G = 0.0, .B = 0.0 }; | |
106 | ||
107 | assert(mapColor(@typeOf(c_u8), @typeOf(c_f32), c_u8).equals(c_f32)); | |
108 | assert(mapColor(@typeOf(c_f32), @typeOf(c_u8), c_f32).equals(c_u8)); | |
109 | } | |
110 | } | |
111 | ||
112 | fn render() Color(f32) { | |
113 | return Color(f32){ .R = 0, .G = 0, .B = 0 }; | |
114 | } | |
115 | ||
116 | pub fn main() void { | |
117 | /// comptime will yield a compiler error (error: evaluation exceeded 1000 backwards branches) | |
118 | /// no comptime will result in stack overflow. | |
119 | // comptime | |
120 | { | |
121 | var y: usize = 0; | |
122 | while (y < 240) : (y += 1) { | |
123 | var x: usize = 0; | |
124 | while (x < 320) : (x += 1) { | |
125 | const val = render(); | |
126 | ||
127 | const mapped = mapColor(@typeOf(val), Color(u8), val); | |
128 | } | |
129 | } | |
130 | } | |
131 | } |