--- /dev/null
+package imgui.bitmap
+
+use package core
+#private_file ttf :: package imgui.ttf
+
+Point :: struct {
+ x, y: f32;
+}
+
+
+Bitmap :: struct {
+ Style :: enum {
+ Outlined_Aliased;
+ Outlined_Border;
+ Filled;
+ Raw;
+ }
+
+ allocator : Allocator;
+ ttf : ^ttf.TrueTypeFont;
+ buffer : [] u8;
+
+ width : i32 = 1;
+ height : i32 = 1;
+
+ bytes_per_pixel : u32 = 4;
+ background_color : u32 = 0xFFFFFF00;
+ foreground_color : u32 = 0x000000FF;
+
+ scale : f32 = 1.0f;
+ scale_x : f32 = 1.0f;
+ scale_y : f32 = 1.0f;
+
+ space_width : f32 = 1.0f;
+ space_multiplier : f32 = 0.0f;
+
+ transformation_matrix: [9] f32 = f32.[1,0,0,0,1,0,0,0,0];
+ character_matrix: [9] f32 = f32.[1,0,0,0,1,0,0,0,0];
+
+ style: Style = Style.Filled;
+ justify := false;
+ justify_fill_ratio : f32 = 0.5;
+
+ filler : [..][..] i32;
+
+ use_font_metrics: bool = false;
+}
+
+bitmap_clear :: (use bitmap: ^Bitmap) {
+ size := width * width * bytes_per_pixel;
+ memory.set(buffer.data, 0, size);
+}
+
+bitmap_transform_text :: (use bitmap: ^Bitmap, p: Point) -> (i32, i32) {
+ return
+ cast(i32) (p.x * transformation_matrix[0] + p.y * transformation_matrix[1] + transformation_matrix[4]),
+ cast(i32) (p.x * transformation_matrix[2] + p.y * transformation_matrix[3] + transformation_matrix[5]);
+}
+
+bitmap_transform_character :: (use bitmap: ^Bitmap, p: Point) -> (i32, i32) {
+ return
+ cast(i32) (p.x * character_matrix[0] + p.y * character_matrix[1] + character_matrix[4]),
+ cast(i32) (p.x * character_matrix[2] + p.y * character_matrix[3] + character_matrix[5]);
+}
+
+bitmap_set_pos :: (use bitmap: ^Bitmap, x: f32, y: f32) {
+ transformation_matrix[6] = x;
+ transformation_matrix[7] = y;
+}
+
+bitmap_set_rotation :: (use bitmap: ^Bitmap, angle: f32) {
+ transformation_matrix[0] = math.cos(angle);
+ transformation_matrix[1] = -math.sin(angle);
+ transformation_matrix[2] = math.sin(angle);
+ transformation_matrix[3] = math.cos(angle);
+}
+
+bitmap_init_filler :: (use bitmap: ^Bitmap) {
+ h := height - filler.count;
+ if h < 1 do return;
+
+ for _: h {
+ new_row := array.make(i32, allocator=allocator);
+ array.push(^filler, new_row);
+ }
+}
+
+bitmap_clear_filler :: (use bitmap: ^Bitmap) {
+ for i: height {
+ array.clear(^filler[i]);
+ }
+}
+
+bitmap_exec_filler :: (use bitmap: ^Bitmap) {
+ cmp_asc :: (x: $T, y: T) -> i32 do return cast(i32) (x - y);
+
+ for y: height {
+ if filler[y].count > 0 {
+ array.sort(^filler[y], cmp_asc);
+
+ if filler[y].count % 2 != 0 {
+ // Need to have an even number of lines
+ continue;
+ }
+
+ index := 0;
+ while index < filler[y].count {
+ defer index += 2;
+
+ startx := filler[y][index] + 1;
+ endx := filler[y][index + 1];
+
+ if startx >= endx do continue;
+
+ for x: startx .. endx + 1 { // @Check: +1 may not be necessary
+ bitmap_plot(bitmap, x, y, foreground_color);
+ }
+ }
+ }
+ }
+}
+
+// This function should be inlined at some point because it is very small
+// and cheap.
+bitmap_plot :: (use bitmap: ^Bitmap, x: i32, y: i32, color: u32) -> bool {
+ if x < 0 || x >= width || y < 0 || y >= height do return false;
+
+ index := (x + y * width) * bytes_per_pixel;
+ buffer[index] = cast(u8) (color & 0xFF);
+
+ return true;
+}
+
+bitmap_fline :: (use bitmap: ^Bitmap, ix0: i32, iy0: i32, ix1: i32, iy1: i32, color: u32) {
+
+ if (ix0 < 0 && ix1 < 0) || (ix0 > width && ix1 > width) do return;
+
+ x0 := cast(f32) ix0;
+ y0 := cast(f32) iy0;
+ x1 := cast(f32) ix1;
+ y1 := cast(f32) iy1;
+
+ if y1 < y0 {
+ x0, x1 = x1, x0;
+ y0, y1 = y1, y0;
+ }
+
+ dx := x1 - x0;
+ dy := y1 - y0;
+
+ if dy == 0 {
+ if iy0 >= 0 && iy0 < filler.count {
+ if ix0 <= ix1 {
+ array.push(^filler[iy0], ix0);
+ array.push(^filler[iy0], ix1);
+ } else {
+ array.push(^filler[iy0], ix1);
+ array.push(^filler[iy0], ix0);
+ }
+ }
+
+ return;
+ }
+
+ n := dx / dy;
+ for y: cast(i32) (dy + 0.5) {
+ yd := cast(i32) (y + iy0);
+ x := n * ~~y + x0;
+
+ if x > ~~width || yd >= ~~filler.count do break;
+
+ if yd >= 0 && yd < filler.count {
+ array.push(^filler[yd], cast(i32) (x + 0.5));
+ }
+ }
+}
+
+bitmap_aline :: (use bitmap: ^Bitmap, ix0: i32, iy0: i32, ix1: i32, iy1: i32, color: u32) {
+ x0 := cast(f32) ix0;
+ y0 := cast(f32) iy0;
+ x1 := cast(f32) ix1;
+ y1 := cast(f32) iy1;
+
+ dx := x1 - x0;
+ dy := y1 - y0;
+
+ dist := cast(f32) 0.4;
+ if math.abs(dx) > math.abs(dy) {
+ if x1 > x0 {
+ x0, x1 = x1, x0;
+ y0, y1 = y1, y0;
+ }
+
+ dx := x1 - x0;
+ dy := y1 - y0;
+
+ x0 += 0.5;
+ y0 += 0.5;
+
+ m := dy / dx;
+ x := x0;
+ while x <= x1 + 0.5 {
+ y := m * (x - x0) + y0;
+ e := 1 - math.abs(y - 0.5 - math.floor(y));
+ bitmap_plot(bitmap, ~~x, ~~y, foreground_color); // Needs to be aliased!
+
+ ys1 := y + dist;
+ if cast(i32) ys1 != cast(i32) y {
+ v1 := math.abs(ys1 - y) / dist * (1 - e);
+ bitmap_plot(bitmap, ~~x, ~~ys1, foreground_color); // Needs to be aliased!
+ }
+
+ ys2 := y - dist;
+ if cast(i32) ys2 != cast(i32) y {
+ v2 := math.abs(y - ys2) / dist * (1 - e);
+ bitmap_plot(bitmap, ~~x, ~~ys2, foreground_color); // Needs to be aliased!
+ }
+
+ x += 1;
+ }
+
+ } else {
+ if y1 > y0 {
+ x0, x1 = x1, x0;
+ y0, y1 = y1, y0;
+ }
+
+ dx := x1 - x0;
+ dy := y1 - y0;
+
+ x0 += 0.5;
+ y0 += 0.5;
+
+ n := dx / dy;
+ y := y0;
+ while y <= y1 + 0.5 {
+ x := n * (y - x0) + y0;
+ e := 1 - math.abs(x - 0.5 - math.floor(x));
+ bitmap_plot(bitmap, ~~x, ~~y, foreground_color); // Needs to be aliased!
+
+ xs1 := x + dist;
+ if cast(i32) xs1 != cast(i32) x {
+ v1 := math.abs(xs1 - x) / dist * (1 - e);
+ bitmap_plot(bitmap, ~~xs1, ~~y, foreground_color); // Needs to be aliased!
+ }
+
+ xs2 := x - dist;
+ if cast(i32) xs1 != cast(i32) x {
+ v1 := math.abs(x - xs2) / dist * (1 - e);
+ bitmap_plot(bitmap, ~~xs2, ~~y, foreground_color); // Needs to be aliased!
+ }
+
+ y += 1.0;
+ }
+ }
+}
+
+bitmap_line :: (use bitmap: ^Bitmap, ix0: i32, iy0: i32, ix1: i32, iy1: i32, color: u32) {
+ use Bitmap.Style;
+ switch style {
+ case Outlined_Aliased {
+ bitmap_aline(bitmap, ix0, iy0, ix1, iy1, color);
+ return;
+ }
+ case Filled {
+ bitmap_aline(bitmap, ix0, iy0, ix1, iy1, color);
+ bitmap_fline(bitmap, ix0, iy0, ix1, iy1, color);
+ return;
+ }
+ case Raw {
+ bitmap_fline(bitmap, ix0, iy0, ix1, iy1, color);
+ return;
+ }
+ }
+
+ assert(false, "Unhandled bitmap_line case");
+}
+
+bitmap_box :: (use bitmap: ^Bitmap, ix0: i32, iy0: i32, ix1: i32, iy1: i32, color: u32) {
+ bitmap_line(bitmap, ix0, iy0, ix1, iy0, color);
+ bitmap_line(bitmap, ix1, iy0, ix1, iy1, color);
+ bitmap_line(bitmap, ix0, iy1, ix1, iy1, color);
+ bitmap_line(bitmap, ix0, iy0, ix0, iy1, color);
+}
+
+bitmap_quadratic :: (use bitmap: ^Bitmap, ix0: i32, iy0: i32, ix1: i32, iy1: i32, icx: i32, icy: i32, color: u32) {
+ x0 := cast(f64) ix0;
+ x1 := cast(f64) ix1;
+ y0 := cast(f64) iy0;
+ y1 := cast(f64) iy1;
+ cx := cast(f64) icx;
+ cy := cast(f64) icy;
+
+ division := cast(f64) 1;
+ dx := math.abs(x0 - x1);
+ dy := math.abs(y0 - y1);
+
+ if dx <= 2 || dy <= 2 {
+ bitmap_line(bitmap, ~~x0, ~~y0, ~~x1, ~~y1, color);
+ return;
+ }
+
+ division = 1 / cast(f64) math.max(dx, dy);
+
+ x_old := x0;
+ y_old := y0;
+ t := cast(f64) 0;
+
+ while t <= (1 + division / 2) {
+ s := 1 - t;
+ x := s * s * x0 + 2.0 * s * t * cx + t * t * x1;
+ y := s * s * y0 + 2.0 * s * t * cy + t * t * y1;
+ xi := cast(i32) (x + 0.5);
+ yi := cast(i32) (y + 0.5);
+
+ bitmap_line(bitmap, ~~x_old, ~~y_old, ~~xi, ~~yi, color);
+ x_old = ~~ xi;
+ y_old = ~~ yi;
+ t += division;
+ }
+}
ttf_read_head_table(ttf);
ttf_read_cmap_table(ttf);
ttf_read_hhea_table(ttf);
+ ttf_read_kern_table(ttf);
return ttf;
}
}
TTGlyph :: struct {
+ Type :: enum {
+ Unknown :: 0x00;
+ Simple :: 0x01;
+ Compound :: 0x02;
+ }
+
+ type: Type = Type.Unknown;
allocator: Allocator;
+ valid_glyph := false;
+
contour_count : i16;
x_min : i16;
x_max : i16;
points : [..] TTGlyphPoint;
contour_ends : [..] u16;
+
+ components: [] GlyphComponent;
+
+ GlyphComponent :: struct {
+ glyph_index : u16;
+ dest_point_index : i16;
+ src_point_index : i16;
+ matrix : [6] f32;
+ }
}
TTGlyphPoint :: struct {
on_curve : bool;
- x : i16 = ~~0;
- y : i16 = ~~0;
+ x : i16 = 0;
+ y : i16 = 0;
}
ttf_read_offset_table :: (use ttf: ^TrueTypeFont) {
for i: num_tables {
tag := read_string(^reader, 4);
- println(tag);
table_info : TTTableInfo;
table_info.checksum = read_u32(^reader);
index_to_loc_format = cast(TTIndexToLocFormat) read_i16(^reader);
glyph_data_format = read_i16(^reader);
- assert(magic_number == 0x54053cf5, "TTF Magic Number wrong!");
+ assert(magic_number == 0x5f0f3cf5, "TTF Magic Number wrong!");
}
ttf_lookup_glyph_offset :: (use ttf: ^TrueTypeFont, glyph_index: i32) -> u32 {
ttf_read_simple_glyph(ttf, glyph);
}
+ glyph.valid_glyph = true;
return glyph;
}
}
}
+#private
+TFKC_Flags :: enum #flags {
+ arg_1_and_2_are_words;
+ args_are_xy_values;
+ round_xy_to_grid;
+ we_have_a_scale;
+ _; // reserved
+ more_components;
+ we_have_an_x_and_y_scale;
+ we_have_a_two_by_two;
+ we_have_instructions;
+ use_my_metrics;
+ overlap_component;
+}
+
+ttf_read_compound_glyph :: (use ttf: ^TrueTypeFont, glyph: ^TTGlyph) {
+ use package core.io.binary
+
+ glyph.type = TTGlyph.Type.Compound;
+
+ component : TTGlyph.GlyphComponent;
+ flags := TFKC_Flags.more_components;
+
+ while (flags & TFKC_Flags.more_components) != ~~ 0 {
+ arg1, arg2 : i16;
+
+ flags = ~~ read_u16(^reader);
+
+ component.glyph_index = read_u16(^reader);
+
+ if (flags & TFKC_Flags.arg_1_and_2_are_words) != ~~ 0 {
+ arg1 = read_i16(^reader);
+ arg2 = read_i16(^reader);
+ } else {
+ arg1 = cast(i16) read_u8(^reader);
+ arg2 = cast(i16) read_u8(^reader);
+ }
+
+ if (flags & TFKC_Flags.args_are_xy_values) != ~~ 0 {
+ component.matrix[4] = ~~ cast(i32) arg1;
+ component.matrix[5] = ~~ cast(i32) arg2;
+ } else {
+ component.dest_point_index = arg1;
+ component.src_point_index = arg2;
+ }
+
+ if (flags & TFKC_Flags.we_have_a_scale) != ~~ 0 {
+ component.matrix[0] = read_2dot14(^reader);
+ component.matrix[3] = component.matrix[0];
+ }
+ elseif (flags & TFKC_Flags.we_have_an_x_and_y_scale) != ~~ 0 {
+ component.matrix[0] = read_2dot14(^reader);
+ component.matrix[3] = read_2dot14(^reader);
+ }
+ elseif (flags & TFKC_Flags.we_have_a_two_by_two) != ~~ 0 {
+ component.matrix[0] = read_2dot14(^reader);
+ component.matrix[2] = read_2dot14(^reader);
+ component.matrix[3] = read_2dot14(^reader);
+ component.matrix[4] = read_2dot14(^reader);
+ }
+
+ old_pos := tell(^reader);
+
+ simple_glyph := ttf_read_glyph(ttf, ~~ component.glyph_index, ttf.allocator);
+ if simple_glyph.valid_glyph {
+ point_offset := glyph.points.count;
+ for i: simple_glyph.contour_ends.count {
+ array.push(^glyph.contour_ends, cast(u16) (simple_glyph.contour_ends[i] + ~~ point_offset));
+ }
+
+ for p: simple_glyph.points {
+ px := cast(f32) cast(i32) p.x;
+ py := cast(f32) cast(i32) p.y;
+
+ x := component.matrix[0] * px + component.matrix[1] * py + component.matrix[4];
+ y := component.matrix[2] * px + component.matrix[3] * py + component.matrix[5];
+
+ array.push(^glyph.points, .{
+ x = ~~ cast(i32) x,
+ y = ~~ cast(i32) y,
+ on_curve = p.on_curve,
+ });
+ }
+ }
+
+ seek(^reader, old_pos);
+ }
+
+ glyph.contour_count = cast(i16) glyph.contour_ends.count;
+
+ if (flags & TFKC_Flags.we_have_instructions) != ~~ 0 {
+ seek(^reader, tell(^reader) + ~~ read_u16(^reader));
+ }
+
+ glyph.valid_glyph = true;
+}
+
ttf_glyph_count :: (use ttf: ^TrueTypeFont) -> u32 {
use package core.io.binary
}
get :: (use kern: ^TTKern0Table, glyph_index: i32) -> (i32, i32) {
- x := 0
+ x := 0;
if old_index >= 0 {
ch := ((cast(u32) old_index & 0xFFFF) << 16) | (cast(u32) glyph_index & 0xFFFF);
return kt;
}
+ttf_next_kern :: (use ttf: ^TrueTypeFont, glyph_index: i32) -> (i32, i32) {
+ // BUG: I cannot do this here because x and y do not
+ // automatically become a 'i32' type.
+ // x, y := 0, 0;
+ x := 0;
+ y := 0;
+ for i: 0 .. kern.count {
+ tmp_x, tmp_y := kern[i]->get(glyph_index);
+ x += tmp_x;
+ y += tmp_y;
+ }
+ return x, y;
+}
+
TTHorizontalMetrics :: struct {
advance_width : u16;
left_side_bearing : i16;