mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-09-09 20:28:57 -04:00
Use harfbuzz for layouting.
This commit is contained in:
parent
b616716eee
commit
5098d4fe0f
@ -63,7 +63,8 @@ pub fn build(b: *std.build.Builder) !void {
|
|||||||
}
|
}
|
||||||
exe.addCSourceFiles(&[_][]const u8{"lib/glad.c", "lib/stb_image.c", "lib/cross_platform_udp_socket.c"}, &[_][]const u8{"-g"});
|
exe.addCSourceFiles(&[_][]const u8{"lib/glad.c", "lib/stb_image.c", "lib/cross_platform_udp_socket.c"}, &[_][]const u8{"-g"});
|
||||||
exe.addPackage(freetype.pkg);
|
exe.addPackage(freetype.pkg);
|
||||||
freetype.link(b, exe, .{});
|
exe.addPackage(freetype.harfbuzz_pkg);
|
||||||
|
freetype.link(b, exe, .{ .harfbuzz = .{} });
|
||||||
exe.setTarget(target);
|
exe.setTarget(target);
|
||||||
exe.setBuildMode(mode);
|
exe.setBuildMode(mode);
|
||||||
//exe.sanitize_thread = true;
|
//exe.sanitize_thread = true;
|
||||||
|
309
src/graphics.zig
309
src/graphics.zig
@ -4,6 +4,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const freetype = @import("freetype");
|
const freetype = @import("freetype");
|
||||||
|
const harfbuzz = @import("harfbuzz");
|
||||||
|
|
||||||
const Vec4i = @import("vec.zig").Vec4i;
|
const Vec4i = @import("vec.zig").Vec4i;
|
||||||
const Vec2f = @import("vec.zig").Vec2f;
|
const Vec2f = @import("vec.zig").Vec2f;
|
||||||
@ -294,7 +295,180 @@ pub const Draw = struct {
|
|||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
pub fn text(_text: []const u8, x: f32, y: f32, fontSize: f32) !void {
|
pub fn text(_text: []const u8, x: f32, y: f32, fontSize: f32) !void {
|
||||||
try TextRendering.renderText(_text, x, y, fontSize);
|
try TextRendering.renderText(_text, x, y, fontSize, .{.color = @truncate(u24, color)});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const TextBuffer = struct {
|
||||||
|
|
||||||
|
pub const FontEffect = packed struct(u28) {
|
||||||
|
color: u24 = 0,
|
||||||
|
bold: bool = false,
|
||||||
|
italic: bool = false,
|
||||||
|
underline: bool = false,
|
||||||
|
overline: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
buffer: harfbuzz.Buffer,
|
||||||
|
glyphInfos: []harfbuzz.GlyphInfo,
|
||||||
|
glyphPositions: []harfbuzz.Position,
|
||||||
|
textIndexGuess: []u32,
|
||||||
|
fontEffects: std.ArrayList(FontEffect),
|
||||||
|
|
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, text: []const u8, initialFontEffect: FontEffect, showControlCharacters: bool) !TextBuffer {
|
||||||
|
var self: TextBuffer = undefined;
|
||||||
|
// Parse the input text:
|
||||||
|
var parsedText = std.ArrayList(u32).init(main.threadAllocator);
|
||||||
|
defer parsedText.deinit();
|
||||||
|
self.fontEffects = std.ArrayList(FontEffect).init(allocator);
|
||||||
|
var unicodeIterator = std.unicode.Utf8Iterator{.bytes = text, .i = 0};
|
||||||
|
var state: enum(u5) {
|
||||||
|
colorRU = 5,
|
||||||
|
colorRL = 4,
|
||||||
|
colorGU = 3,
|
||||||
|
colorGL = 2,
|
||||||
|
colorBU = 1,
|
||||||
|
colorBL = 0,
|
||||||
|
text = 6,
|
||||||
|
star,
|
||||||
|
underscore,
|
||||||
|
backslash,
|
||||||
|
} = .text;
|
||||||
|
var currentFontEffect: FontEffect = initialFontEffect;
|
||||||
|
while(unicodeIterator.nextCodepoint()) |codepoint| {
|
||||||
|
const isControlCharacter: bool = blk: {
|
||||||
|
switch(state) {
|
||||||
|
.text => {
|
||||||
|
switch(codepoint) {
|
||||||
|
'*' => {
|
||||||
|
state = .star;
|
||||||
|
break :blk true;
|
||||||
|
},
|
||||||
|
'_' => {
|
||||||
|
state = .underscore;
|
||||||
|
break :blk true;
|
||||||
|
},
|
||||||
|
'\\' => {
|
||||||
|
state = .backslash;
|
||||||
|
break :blk true;
|
||||||
|
},
|
||||||
|
'#' => {
|
||||||
|
state = .colorRU;
|
||||||
|
break :blk true;
|
||||||
|
},
|
||||||
|
else => {
|
||||||
|
break :blk false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.star => {
|
||||||
|
state = .text;
|
||||||
|
if(codepoint == '*') {
|
||||||
|
currentFontEffect.bold = !currentFontEffect.bold;
|
||||||
|
break :blk true;
|
||||||
|
} else {
|
||||||
|
currentFontEffect.italic = !currentFontEffect.italic;
|
||||||
|
break :blk false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.underscore => {
|
||||||
|
state = .text;
|
||||||
|
if(codepoint == '_') {
|
||||||
|
currentFontEffect.overline = !currentFontEffect.overline;
|
||||||
|
break :blk true;
|
||||||
|
} else {
|
||||||
|
currentFontEffect.underline = !currentFontEffect.underline;
|
||||||
|
break :blk false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.backslash => {
|
||||||
|
state = .text;
|
||||||
|
break :blk false;
|
||||||
|
},
|
||||||
|
else => |colorEnum| {
|
||||||
|
const shift = 4*@enumToInt(colorEnum);
|
||||||
|
currentFontEffect.color = (currentFontEffect.color & ~(@as(u24, 0xf) << shift)) | @as(u24, switch(codepoint) {
|
||||||
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => codepoint - '0',
|
||||||
|
'a', 'b', 'c', 'd', 'e', 'f' => codepoint - 'a' + 10,
|
||||||
|
'A', 'B', 'C', 'D', 'E', 'F' => codepoint - 'A' + 10,
|
||||||
|
else => 0,
|
||||||
|
}) << shift;
|
||||||
|
if(colorEnum == .colorBL) {
|
||||||
|
state = .text;
|
||||||
|
} else {
|
||||||
|
state = @intToEnum(@TypeOf(state), @enumToInt(colorEnum) - 1);
|
||||||
|
}
|
||||||
|
break :blk true;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if(isControlCharacter) {
|
||||||
|
if(showControlCharacters) {
|
||||||
|
try parsedText.append(codepoint);
|
||||||
|
try self.fontEffects.append(.{.color = 0x808080});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try parsedText.append(codepoint);
|
||||||
|
try self.fontEffects.append(currentFontEffect);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Let harfbuzz do its thing:
|
||||||
|
self.buffer = harfbuzz.Buffer.init() orelse return error.CouldNotInitHarfbuzzBuffer;
|
||||||
|
self.buffer.addUTF32(parsedText.items, 0, null);
|
||||||
|
self.buffer.setDirection(.ltr);
|
||||||
|
self.buffer.setScript(.common);
|
||||||
|
self.buffer.setLanguage(harfbuzz.Language.getDefault());
|
||||||
|
harfbuzz.hb_shape(TextRendering.harfbuzzFont.handle, self.buffer.handle, null, 0);
|
||||||
|
self.glyphInfos = self.buffer.getGlyphInfos();
|
||||||
|
self.glyphPositions = self.buffer.getGlyphPositions() orelse return error.CouldNotGetGlyphPositions;
|
||||||
|
|
||||||
|
// Guess the text index from the given cluster indices. Only works if the number of glyphs and the number of characters in a cluster is the same.
|
||||||
|
self.textIndexGuess = try allocator.alloc(u32, self.glyphInfos.len);
|
||||||
|
for(self.textIndexGuess) |*index, i| {
|
||||||
|
if(i == 0 or self.glyphInfos[i-1].cluster != self.glyphInfos[i].cluster) {
|
||||||
|
index.* = self.glyphInfos[i].cluster;
|
||||||
|
} else {
|
||||||
|
index.* = @min(self.textIndexGuess[i-1] + 1, @intCast(u32, parsedText.items.len-1));
|
||||||
|
for(self.glyphInfos[i..]) |glyphInfo| {
|
||||||
|
if(glyphInfo.cluster != self.glyphInfos[i].cluster) {
|
||||||
|
index.* = @min(index.*, glyphInfo.cluster - 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: TextBuffer) void {
|
||||||
|
self.fontEffects.allocator.free(self.textIndexGuess);
|
||||||
|
self.fontEffects.deinit();
|
||||||
|
self.buffer.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render(self: TextBuffer, _x: f32, _y: f32, fontSize: f32) !void {
|
||||||
|
const fontScaling = fontSize/16.0;
|
||||||
|
var x = _x/fontScaling;
|
||||||
|
var y = _y/fontScaling;
|
||||||
|
TextRendering.shader.bind();
|
||||||
|
c.glUniform2f(TextRendering.uniforms.scene, @intToFloat(f32, main.Window.width), @intToFloat(f32, main.Window.height));
|
||||||
|
c.glUniform1f(TextRendering.uniforms.ratio, fontScaling);
|
||||||
|
c.glActiveTexture(c.GL_TEXTURE0);
|
||||||
|
c.glBindTexture(c.GL_TEXTURE_2D, TextRendering.glyphTexture[0]);
|
||||||
|
c.glBindVertexArray(Draw.rectVAO);
|
||||||
|
for(self.glyphInfos) |glyphInfo, i| {
|
||||||
|
const position = self.glyphPositions[i];
|
||||||
|
const codepoint = glyphInfo.codepoint;
|
||||||
|
|
||||||
|
const glyph = try TextRendering.getGlyph(codepoint);
|
||||||
|
TextRendering.drawGlyph(glyph, x + @intToFloat(f32, position.x_offset)/@intToFloat(f32, 1 << 2), y - @intToFloat(f32, position.y_offset)/@intToFloat(f32, 1 << 2), @bitCast(u28, self.fontEffects.items[self.textIndexGuess[i]]));
|
||||||
|
|
||||||
|
x += @intToFloat(f32, position.x_advance)/@intToFloat(f32, 1 << 2);
|
||||||
|
y -= @intToFloat(f32, position.y_advance)/@intToFloat(f32, 1 << 2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -318,7 +492,9 @@ const TextRendering = struct {
|
|||||||
} = undefined;
|
} = undefined;
|
||||||
|
|
||||||
var freetypeLib: freetype.Library = undefined;
|
var freetypeLib: freetype.Library = undefined;
|
||||||
var font: freetype.Face = undefined;
|
var freetypeFace: freetype.Face = undefined;
|
||||||
|
var harfbuzzFace: harfbuzz.Face = undefined;
|
||||||
|
var harfbuzzFont: harfbuzz.Font = undefined;
|
||||||
var glyphMapping: std.ArrayList(u31) = undefined;
|
var glyphMapping: std.ArrayList(u31) = undefined;
|
||||||
var glyphData: std.ArrayList(Glyph) = undefined;
|
var glyphData: std.ArrayList(Glyph) = undefined;
|
||||||
var glyphTexture: [2]c_uint = undefined;
|
var glyphTexture: [2]c_uint = undefined;
|
||||||
@ -333,8 +509,10 @@ const TextRendering = struct {
|
|||||||
c.glUniform1f(uniforms.alpha, 1.0);
|
c.glUniform1f(uniforms.alpha, 1.0);
|
||||||
c.glUniform2f(uniforms.fontSize, @intToFloat(f32, textureWidth), @intToFloat(f32, textureHeight));
|
c.glUniform2f(uniforms.fontSize, @intToFloat(f32, textureWidth), @intToFloat(f32, textureHeight));
|
||||||
freetypeLib = try freetype.Library.init();
|
freetypeLib = try freetype.Library.init();
|
||||||
font = try freetypeLib.createFace("assets/cubyz/fonts/unscii-16-full.ttf", 0);
|
freetypeFace = try freetypeLib.createFace("assets/cubyz/fonts/unscii-16-full.ttf", 0);
|
||||||
try font.setPixelSizes(0, textureHeight);
|
try freetypeFace.setPixelSizes(0, textureHeight);
|
||||||
|
harfbuzzFace = harfbuzz.Face.fromFreetypeFace(freetypeFace);
|
||||||
|
harfbuzzFont = harfbuzz.Font.init(harfbuzzFace);
|
||||||
|
|
||||||
glyphMapping = std.ArrayList(u31).init(main.globalAllocator);
|
glyphMapping = std.ArrayList(u31).init(main.globalAllocator);
|
||||||
glyphData = std.ArrayList(Glyph).init(main.globalAllocator);
|
glyphData = std.ArrayList(Glyph).init(main.globalAllocator);
|
||||||
@ -350,11 +528,11 @@ const TextRendering = struct {
|
|||||||
|
|
||||||
fn deinit() void {
|
fn deinit() void {
|
||||||
shader.delete();
|
shader.delete();
|
||||||
font.deinit();
|
|
||||||
freetypeLib.deinit();
|
freetypeLib.deinit();
|
||||||
glyphMapping.deinit();
|
glyphMapping.deinit();
|
||||||
glyphData.deinit();
|
glyphData.deinit();
|
||||||
c.glDeleteTextures(2, &glyphTexture);
|
c.glDeleteTextures(2, &glyphTexture);
|
||||||
|
harfbuzzFont.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resizeTexture(newWidth: i32) !void {
|
fn resizeTexture(newWidth: i32) !void {
|
||||||
@ -385,17 +563,17 @@ const TextRendering = struct {
|
|||||||
textureOffset += width;
|
textureOffset += width;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn getGlyph(char: u21) !Glyph {
|
fn getGlyph(codepoint: u32) !Glyph {
|
||||||
if(char >= glyphMapping.items.len) {
|
if(codepoint >= glyphMapping.items.len) {
|
||||||
try glyphMapping.appendNTimes(0, char - glyphMapping.items.len + 1);
|
try glyphMapping.appendNTimes(0, codepoint - glyphMapping.items.len + 1);
|
||||||
}
|
}
|
||||||
if(glyphMapping.items[char] == 0) {// glyph was not initialized yet.
|
if(glyphMapping.items[codepoint] == 0) {// glyph was not initialized yet.
|
||||||
try font.loadChar(char, freetype.LoadFlags{.render = true});
|
try freetypeFace.loadGlyph(codepoint, freetype.LoadFlags{.render = true});
|
||||||
const glyph = font.glyph();
|
const glyph = freetypeFace.glyph();
|
||||||
const bitmap = glyph.bitmap();
|
const bitmap = glyph.bitmap();
|
||||||
const width = bitmap.width();
|
const width = bitmap.width();
|
||||||
const height = bitmap.rows();
|
const height = bitmap.rows();
|
||||||
glyphMapping.items[char] = @intCast(u31, glyphData.items.len);
|
glyphMapping.items[codepoint] = @intCast(u31, glyphData.items.len);
|
||||||
(try glyphData.addOne()).* = Glyph {
|
(try glyphData.addOne()).* = Glyph {
|
||||||
.textureX = textureOffset,
|
.textureX = textureOffset,
|
||||||
.size = Vec2i{@intCast(i32, width), @intCast(i32, height)},
|
.size = Vec2i{@intCast(i32, width), @intCast(i32, height)},
|
||||||
@ -404,7 +582,7 @@ const TextRendering = struct {
|
|||||||
};
|
};
|
||||||
try uploadData(bitmap);
|
try uploadData(bitmap);
|
||||||
}
|
}
|
||||||
return glyphData.items[glyphMapping.items[char]];
|
return glyphData.items[glyphMapping.items[codepoint]];
|
||||||
}
|
}
|
||||||
|
|
||||||
fn drawGlyph(glyph: Glyph, x: f32, y: f32, fontEffects: u28) void {
|
fn drawGlyph(glyph: Glyph, x: f32, y: f32, fontEffects: u28) void {
|
||||||
@ -424,108 +602,11 @@ const TextRendering = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn renderText(text: []const u8, _x: f32, _y: f32, fontSize: f32) !void {
|
fn renderText(text: []const u8, x: f32, y: f32, fontSize: f32, initialFontEffect: TextBuffer.FontEffect) !void {
|
||||||
const fontScaling = fontSize/16.0;
|
const buf = try TextBuffer.init(main.threadAllocator, text, initialFontEffect, false);
|
||||||
var x = _x/fontScaling;
|
defer buf.deinit();
|
||||||
const y = _y/fontScaling;
|
|
||||||
shader.bind();
|
|
||||||
c.glUniform2f(uniforms.scene, @intToFloat(f32, main.Window.width), @intToFloat(f32, main.Window.height));
|
|
||||||
c.glUniform1f(uniforms.ratio, fontScaling);
|
|
||||||
c.glActiveTexture(c.GL_TEXTURE0);
|
|
||||||
c.glBindTexture(c.GL_TEXTURE_2D, glyphTexture[0]);
|
|
||||||
c.glBindVertexArray(Draw.rectVAO);
|
|
||||||
|
|
||||||
var unicodeIterator = std.unicode.Utf8Iterator{.bytes = text, .i = 0};
|
try buf.render(x, y, fontSize);
|
||||||
var state: enum(u5) {
|
|
||||||
colorRU = 5,
|
|
||||||
colorRL = 4,
|
|
||||||
colorGU = 3,
|
|
||||||
colorGL = 2,
|
|
||||||
colorBU = 1,
|
|
||||||
colorBL = 0,
|
|
||||||
text = 6,
|
|
||||||
star,
|
|
||||||
underscore,
|
|
||||||
backslash,
|
|
||||||
} = .text;
|
|
||||||
var fontEffects: packed struct(u28) {
|
|
||||||
color: u24 = 0,
|
|
||||||
bold: bool = false,
|
|
||||||
italic: bool = false,
|
|
||||||
underline: bool = false,
|
|
||||||
overline: bool = false,
|
|
||||||
} = .{};
|
|
||||||
while(unicodeIterator.nextCodepoint()) |codepoint| {
|
|
||||||
const isControlCharacter: bool = blk: {
|
|
||||||
switch(state) {
|
|
||||||
.text => {
|
|
||||||
switch(codepoint) {
|
|
||||||
'*' => {
|
|
||||||
state = .star;
|
|
||||||
break :blk true;
|
|
||||||
},
|
|
||||||
'_' => {
|
|
||||||
state = .underscore;
|
|
||||||
break :blk true;
|
|
||||||
},
|
|
||||||
'\\' => {
|
|
||||||
state = .backslash;
|
|
||||||
break :blk true;
|
|
||||||
},
|
|
||||||
'#' => {
|
|
||||||
state = .colorRU;
|
|
||||||
break :blk true;
|
|
||||||
},
|
|
||||||
else => {
|
|
||||||
break :blk false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.star => {
|
|
||||||
state = .text;
|
|
||||||
if(codepoint == '*') {
|
|
||||||
fontEffects.bold = !fontEffects.bold;
|
|
||||||
break :blk true;
|
|
||||||
} else {
|
|
||||||
fontEffects.italic = !fontEffects.italic;
|
|
||||||
break :blk false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.underscore => {
|
|
||||||
state = .text;
|
|
||||||
if(codepoint == '_') {
|
|
||||||
fontEffects.bold = !fontEffects.bold;
|
|
||||||
break :blk true;
|
|
||||||
} else {
|
|
||||||
fontEffects.italic = !fontEffects.italic;
|
|
||||||
break :blk false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
.backslash => {
|
|
||||||
state = .text;
|
|
||||||
break :blk false;
|
|
||||||
},
|
|
||||||
else => |colorEnum| {
|
|
||||||
const shift = 4*@enumToInt(colorEnum);
|
|
||||||
fontEffects.color = (fontEffects.color & ~(@as(u24, 0xf) << shift)) | @as(u24, switch(codepoint) {
|
|
||||||
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9' => codepoint - '0',
|
|
||||||
'a', 'b', 'c', 'd', 'e', 'f' => codepoint - 'a' + 10,
|
|
||||||
'A', 'B', 'C', 'D', 'E', 'F' => codepoint - 'A' + 10,
|
|
||||||
else => 0,
|
|
||||||
}) << shift;
|
|
||||||
if(colorEnum == .colorBL) {
|
|
||||||
state = .text;
|
|
||||||
} else {
|
|
||||||
state = @intToEnum(@TypeOf(state), @enumToInt(colorEnum) - 1);
|
|
||||||
}
|
|
||||||
break :blk true;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
};
|
|
||||||
const glyph = try getGlyph(codepoint);
|
|
||||||
drawGlyph(glyph, x, y, if(isControlCharacter) 0x808080 else @bitCast(u28, fontEffects));
|
|
||||||
x += glyph.advance;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -318,7 +318,12 @@ pub fn main() !void {
|
|||||||
c.glDisable(c.GL_CULL_FACE);
|
c.glDisable(c.GL_CULL_FACE);
|
||||||
c.glDisable(c.GL_DEPTH_TEST);
|
c.glDisable(c.GL_DEPTH_TEST);
|
||||||
|
|
||||||
try graphics.Draw.text("*italic* **bold** ***both*** #ff0000red#00ff00green#0000ffblue", 500, 500, 64.0);
|
const buffer = try graphics.TextBuffer.init(threadAllocator, "Ử̵̢̧̧̨̢̡̧̧̢̡̡̡̧̧̡̡̢̡̢̛̛̛̲͔̲̰̮̱͉̪͎̱̳̖̩̠̙͔̺̼̘̻̝̣̠͔̺̤͕̼͍̠̩͔̘̱͕̻͔͈̳͇̩͓̲̙͍̟̟͍̪̦̜͔̪͈̤̲̖̫̞̠͇̫̣̮̩̖̘̫̘̲̻̞̜̲͉͚͇̣͎̖͕̲̣̭̖̲̬̠̻̫̜͔̙̦̫̯͕̞̩̺̙̫̪̘͖͎̞͓͚̭̲̝̣̫̬̜̣̮̥̟̣̖̜̮̘̩͎̱͕̳͓̞̼̟̝̭̠͕̦̩͉̜̜̪̗͖̝͉̺̥͉̫͔̭̯̮̜̼̩̺̙̝̖͖̠͉̥̻̰͈̳̩̱͓̼̮̗̞̝͖̟̟͎̠͈̞̜̲͚̺̮̺͎̠̜͉̗͎̥̣̦̱̰͇̞͍͍̻͍̦̖̮̜̤̪̹̭͔̙͕̼̱̩̲͚̮̫̞̼̙̪̞̦̦̰̖̯̙͕̣͇͕̩̦̤̭͇̑̈́̋̾̃̀̌̒͋͗͗̏̌́̇̐̋̑̅̑͐͐̀͛̽͗̒̀̓̃̄͆͑̉̆͋̆͐̍͌̅̂̑̿͂͑̈́̂͛͐̑̇͂͌̓͌͑̽̔͌̎͗́̈́̓̀̏̿̐̈́̀̀̎̑̉̆͒͘͘̚̕̚͘̚͘͜͜͜͜͜͜͜͜͠͝͝͝͠͝͝ͅͅͅͅͅͅṈ̴̨̧̧̧̧̛̛̛̛͔̩̫͖̞͉̠̝͓͈͚̻̙̩̠̪̭̤̪̞̲͍̻̟͍̦͇̦͈̙̼̜͎̥͍̖̗͍̘̙̬̥̺̭̝̞̺̬̭͔̝̠̯͈̠̦̖̩̱͎̘̲̱̦͖̠̩͈̦̦̤̤̂̔͊̄̊́̓̾́̊̀̍̓͛̂͒͂̒̋̅̂̎̀͌̒̃̊͛͒̽̓͌̐̔͛̔̋̈́̌̈́͋̆͐̽̄̋̽̌́̆͒̈́̀̍͗̈́͒͗́̔̎́̑̄̀͂̆͊͆͊̄̍̔̌̂̈́̓̒̓͆͌͒͗̍̽̏͊͒̅̿̉͗̆̊͌̔̇̃̿́͒͛̽͂̃̑̅͋͛͛̅̒͋̍̉̋̔̔̈̃̍̅̉̄̈̏̓́̏̄͋̅͊͂͊̒͐͗̋̽̔͑͌̇̐͛̋̇̐͌̃͆̈́̓͛͌̔̄͐̂̃̂͗͆̈̾͂͌̋̏̇̽͐̽̈̎̒͛̄̆̊̓͐͂̐͛̾͒̿̓̿͊͒̀̎͒̀̉̈͐̀̾́͘͘͘̚̚͘̚̚͘̚̕̕̚̕͘͘͜͝͠͝͝͠͝͠͝͝͝͠͝͝͠I̵̧̧̢̧̡̡̨̡̢̢̡̛̛̛̛̤̟̘͙͙̜̻͍̳̳̘̜̮̹͇̹͙̬͎͎̜͎̙͙̝̳̙͍͍̱̟̳̫̲͕̗̜̫̤͔̲̥̗̼̣̹͔̞̺̳͍̯̳̳̯̟̟͍͓̖̳̯̥̘̻̯̟̭͖͍̥̔͗͐̈̍̆̄̄́́̒́͐͋͛͒̀͛͐͐̍͐̍̆̈̆̋̆͆̔̏͛̊͑͂̐̈̿̄̿̄̇̈́͌̋̈́̈́͊̀͐̈́͆̅̐̔̄̆͂́͊͛̆͋̂̅̇̊̔͗͂̍̒͛̆̊̑̒̿̎̅͒͒͒͗̿̽̔̒͑̀̃͑́͗́͊͊̑̽̑̿̈́̅̏̈́͂̓̏͗̈́̊̈́̏̏̽̐̉̂̎͊̈́͋́̂͛̏́̆̓̊͗̊̇̓̈́͗͐̇͐͒̐̓́͊͐̓́̌͂̅̓̍̅̉̐͋̀̂̅͆̕̕̚̕̕̕͘͘̚̚̚͜͜͜͜͜͝͝͝͝͝͠͠͝͝ͅͅC̶̢̡̡̧̧̧̧̧̡̧̡̧̡̨̛̛̛̛͉͖̯͇͙͉̳͎̤̘͔̜̜̳̤̤̰̲̱͙̗̩̦̪̰̲̜̣͉̯̥̤̩͔̯̪̘̯̭̹̼͇͙̩̮̫̜͙̥͍̰͙̖̯͉̱̬͚͇̖͉͓͎̥̹̗̖͚̥̩̱͇̜͇̜̻͔̘͙̲̬̫̹͚̖̭̲͚͇͚͈͍̭̼̗͙̘̠̠͇̮̱̑̋̈́̂̂̿͌̀̎̓̊͑̊͐̈́̀̓́̈́̊̎̇̆̈͒̏͌͌͒̓̔̃͑̓̿̐̈́̄̅̈́̾̋̄̀̽̾̔͂͒͐̄̒͛̄͌͆͌̄̔̆͑͒̑̍̆̅̌̇̇̉͛̃̊̎̽̑́̈́͋͋͊͒͋̿̇̓̀͑̿̓̇͌̀̐̄͑̉̿̍̊̽̈́̈́́͑̍͛́̆͂͒̈́̈́̃͛͑̍̀͗̿̋̇̐̃̒́̊̍̇̈̽̀̌̈́̉̃̈́̏̈́̂̄͌͛͌̿̽͂̋̕̚͘̚̚̚̚͘̚͘̚͘͜͜͝͠͝͝͝͝͝͝ͅͅͅÒ̷̡̡̧̡̨̡̡̧̨̨̧̢̧̧̨̨̢̨̨̢̨̡̨̢̦̲̝̟̫͍̜͉̥͙͖͔̼̹͚͕̱̫̬̥͔͕̘͉͚͕̝̺͓͓͚̖͓̭̻̗̩̯̗̫̖̗͚͔̰̳͚̰̫̺̻͔̳̙̖͉̣̮͕̻̣̱͉̲̻͖̙̫̘̪͍͚͖̝̲̲̻̫̩̲͎̬̱̝̹͙̱͚͕̱̯̦̩̞̖̟͉̥͎̳̟̠̪̞̳̯̬̲͕̦̺̩̝̠̥̩͎̖̲̦̦͖̜̟̱̥͔̙̯͇̥͖̳̺̞̬̜̼̙̥͕͈̠̼̥̖̼̺̼̜̬̲͕͚̪̳̙̤̬̗̺̫̦̥̘͎͇̯͔̘̝͔͙͙̹̟̰̲̞͙̣͍̫̘͈̖̙̰̗͔̘̜̤̬̝͇̩̗̩̳͚̻̭̖̯̫͎̲̘͙̺̩̗̝̟̰̪͙͇̝̦̱̺̺̠͍̱̺̻̗͖̖̳̩̩̣͎̪͕͈͍͕̬̗̣̺̬̫̋͋̄̌͂͑͛̍̈̀̾̀͗̅̕͜͜͜͝͝ͅͅͅD̴̨̡̢̨̧̧̢̧̧̡̡͚̟͇̝̝̤̘͙͕͖̙̗̱̦͇̞͕̥̱̘͖͉̻͖̥̺̮͎͎͔̻͉̖̖͍͍̠͖͇̦̝͙̱͉̱̦̩̰̤̟̼̻̲̣̞̬͓̖̪̞̼̲̖̫̗̼̯͔̻̖͍̬̯͓͓̱͕͙͈͓͚̣̰̩̺̱͓̩̘̟̖͇̹̼̠͇̝̟̭͎͙̺͈̈̍͜͜͜ͅͅͅĚ̴̢̢̡̢̨̢̨̧̢̢̨̧̧̨̨̡̛̛̥̳̼̣̩̗̲̪͚̤̟͉̼̹͔̙̥̫͇̗̻̝͖̼̟̙̞̹̼͈͔̯̮̖̫̼̮͕͈̟̝̤͖̬͇͍̙̠͎̖̺̲̪̬͕̫̬̠͚͍̗͍̩̭̜̪͖̯͖̱̮̭̠̲̥͍̼̱̻̘̻̪̠̲̤̪͚̼̘͚̲̝͈̹̜͓̲̫̝̝̦̭̘̱̜̺͚̩͕̼̻̺͇͖̗̮̳̫̙̖̖͓̜̝̜̭̖͚͔͔̰̗̱͉̱̭̗͎̦͇̭̩͕̝͙̰̝͕̗̲͚̮̦͓̰͙̺̬̦̪̤͇̤̩̹̗̲͍̭̩̭̲̰͖̳̭͍̞̙̣͎͖͍͉̱͖̙̙̳͎̯͇̲̥̭͖͎̩͓̰̼͚̥̟̖̘͍̺̠͔̙̞͚̩͙͔̺̬̬̤̟̟̙̘̓̈́͊͌̀̍̎̈̾͊͊̎̐͐̍̓͒͒́͐͆̆̏͂̄͋̉͂̎͗͆̊̑̀̈́̔̔͐̃͊̆͊̏̉̓͂̉̔͐̽̅̿͆̈́̉͂͂̌̾̂̈̂̆̇̿̎̏͌̈́̐̍̊̊̌͗̏͒̉̆̔̊̂͛̔̍͒͐͐̎̃̓̊͒̾̈́̂̈́̌̀̐̔͂̒̅͘̚͘͘̚͘̕͘̕͜͜͜͜͝͝͝͝͝͝͝͝ͅͅͅͅͅͅͅͅ a⃗ a⃗⃗ a#ff0000⃗#ffff00⃗#00ff00⃗#00ffff⃗#0000ff⃗#ff00ff⃗#000000 ⌬ *italic* **bold** ***both*** #ff0000red#00ff00green#0000ffblue", .{}, true);
|
||||||
|
defer buffer.deinit();
|
||||||
|
const buffer2 = try graphics.TextBuffer.init(threadAllocator, "Ử̵̢̧̧̨̢̡̧̧̢̡̡̡̧̧̡̡̢̡̢̛̛̛̲͔̲̰̮̱͉̪͎̱̳̖̩̠̙͔̺̼̘̻̝̣̠͔̺̤͕̼͍̠̩͔̘̱͕̻͔͈̳͇̩͓̲̙͍̟̟͍̪̦̜͔̪͈̤̲̖̫̞̠͇̫̣̮̩̖̘̫̘̲̻̞̜̲͉͚͇̣͎̖͕̲̣̭̖̲̬̠̻̫̜͔̙̦̫̯͕̞̩̺̙̫̪̘͖͎̞͓͚̭̲̝̣̫̬̜̣̮̥̟̣̖̜̮̘̩͎̱͕̳͓̞̼̟̝̭̠͕̦̩͉̜̜̪̗͖̝͉̺̥͉̫͔̭̯̮̜̼̩̺̙̝̖͖̠͉̥̻̰͈̳̩̱͓̼̮̗̞̝͖̟̟͎̠͈̞̜̲͚̺̮̺͎̠̜͉̗͎̥̣̦̱̰͇̞͍͍̻͍̦̖̮̜̤̪̹̭͔̙͕̼̱̩̲͚̮̫̞̼̙̪̞̦̦̰̖̯̙͕̣͇͕̩̦̤̭͇̑̈́̋̾̃̀̌̒͋͗͗̏̌́̇̐̋̑̅̑͐͐̀͛̽͗̒̀̓̃̄͆͑̉̆͋̆͐̍͌̅̂̑̿͂͑̈́̂͛͐̑̇͂͌̓͌͑̽̔͌̎͗́̈́̓̀̏̿̐̈́̀̀̎̑̉̆͒͘͘̚̕̚͘̚͘͜͜͜͜͜͜͜͜͠͝͝͝͠͝͝ͅͅͅͅͅͅṈ̴̨̧̧̧̧̛̛̛̛͔̩̫͖̞͉̠̝͓͈͚̻̙̩̠̪̭̤̪̞̲͍̻̟͍̦͇̦͈̙̼̜͎̥͍̖̗͍̘̙̬̥̺̭̝̞̺̬̭͔̝̠̯͈̠̦̖̩̱͎̘̲̱̦͖̠̩͈̦̦̤̤̂̔͊̄̊́̓̾́̊̀̍̓͛̂͒͂̒̋̅̂̎̀͌̒̃̊͛͒̽̓͌̐̔͛̔̋̈́̌̈́͋̆͐̽̄̋̽̌́̆͒̈́̀̍͗̈́͒͗́̔̎́̑̄̀͂̆͊͆͊̄̍̔̌̂̈́̓̒̓͆͌͒͗̍̽̏͊͒̅̿̉͗̆̊͌̔̇̃̿́͒͛̽͂̃̑̅͋͛͛̅̒͋̍̉̋̔̔̈̃̍̅̉̄̈̏̓́̏̄͋̅͊͂͊̒͐͗̋̽̔͑͌̇̐͛̋̇̐͌̃͆̈́̓͛͌̔̄͐̂̃̂͗͆̈̾͂͌̋̏̇̽͐̽̈̎̒͛̄̆̊̓͐͂̐͛̾͒̿̓̿͊͒̀̎͒̀̉̈͐̀̾́͘͘͘̚̚͘̚̚͘̚̕̕̚̕͘͘͜͝͠͝͝͠͝͠͝͝͝͠͝͝͠I̵̧̧̢̧̡̡̨̡̢̢̡̛̛̛̛̤̟̘͙͙̜̻͍̳̳̘̜̮̹͇̹͙̬͎͎̜͎̙͙̝̳̙͍͍̱̟̳̫̲͕̗̜̫̤͔̲̥̗̼̣̹͔̞̺̳͍̯̳̳̯̟̟͍͓̖̳̯̥̘̻̯̟̭͖͍̥̔͗͐̈̍̆̄̄́́̒́͐͋͛͒̀͛͐͐̍͐̍̆̈̆̋̆͆̔̏͛̊͑͂̐̈̿̄̿̄̇̈́͌̋̈́̈́͊̀͐̈́͆̅̐̔̄̆͂́͊͛̆͋̂̅̇̊̔͗͂̍̒͛̆̊̑̒̿̎̅͒͒͒͗̿̽̔̒͑̀̃͑́͗́͊͊̑̽̑̿̈́̅̏̈́͂̓̏͗̈́̊̈́̏̏̽̐̉̂̎͊̈́͋́̂͛̏́̆̓̊͗̊̇̓̈́͗͐̇͐͒̐̓́͊͐̓́̌͂̅̓̍̅̉̐͋̀̂̅͆̕̕̚̕̕̕͘͘̚̚̚͜͜͜͜͜͝͝͝͝͝͠͠͝͝ͅͅC̶̢̡̡̧̧̧̧̧̡̧̡̧̡̨̛̛̛̛͉͖̯͇͙͉̳͎̤̘͔̜̜̳̤̤̰̲̱͙̗̩̦̪̰̲̜̣͉̯̥̤̩͔̯̪̘̯̭̹̼͇͙̩̮̫̜͙̥͍̰͙̖̯͉̱̬͚͇̖͉͓͎̥̹̗̖͚̥̩̱͇̜͇̜̻͔̘͙̲̬̫̹͚̖̭̲͚͇͚͈͍̭̼̗͙̘̠̠͇̮̱̑̋̈́̂̂̿͌̀̎̓̊͑̊͐̈́̀̓́̈́̊̎̇̆̈͒̏͌͌͒̓̔̃͑̓̿̐̈́̄̅̈́̾̋̄̀̽̾̔͂͒͐̄̒͛̄͌͆͌̄̔̆͑͒̑̍̆̅̌̇̇̉͛̃̊̎̽̑́̈́͋͋͊͒͋̿̇̓̀͑̿̓̇͌̀̐̄͑̉̿̍̊̽̈́̈́́͑̍͛́̆͂͒̈́̈́̃͛͑̍̀͗̿̋̇̐̃̒́̊̍̇̈̽̀̌̈́̉̃̈́̏̈́̂̄͌͛͌̿̽͂̋̕̚͘̚̚̚̚͘̚͘̚͘͜͜͝͠͝͝͝͝͝͝ͅͅͅÒ̷̡̡̧̡̨̡̡̧̨̨̧̢̧̧̨̨̢̨̨̢̨̡̨̢̦̲̝̟̫͍̜͉̥͙͖͔̼̹͚͕̱̫̬̥͔͕̘͉͚͕̝̺͓͓͚̖͓̭̻̗̩̯̗̫̖̗͚͔̰̳͚̰̫̺̻͔̳̙̖͉̣̮͕̻̣̱͉̲̻͖̙̫̘̪͍͚͖̝̲̲̻̫̩̲͎̬̱̝̹͙̱͚͕̱̯̦̩̞̖̟͉̥͎̳̟̠̪̞̳̯̬̲͕̦̺̩̝̠̥̩͎̖̲̦̦͖̜̟̱̥͔̙̯͇̥͖̳̺̞̬̜̼̙̥͕͈̠̼̥̖̼̺̼̜̬̲͕͚̪̳̙̤̬̗̺̫̦̥̘͎͇̯͔̘̝͔͙͙̹̟̰̲̞͙̣͍̫̘͈̖̙̰̗͔̘̜̤̬̝͇̩̗̩̳͚̻̭̖̯̫͎̲̘͙̺̩̗̝̟̰̪͙͇̝̦̱̺̺̠͍̱̺̻̗͖̖̳̩̩̣͎̪͕͈͍͕̬̗̣̺̬̫̋͋̄̌͂͑͛̍̈̀̾̀͗̅̕͜͜͜͝͝ͅͅͅD̴̨̡̢̨̧̧̢̧̧̡̡͚̟͇̝̝̤̘͙͕͖̙̗̱̦͇̞͕̥̱̘͖͉̻͖̥̺̮͎͎͔̻͉̖̖͍͍̠͖͇̦̝͙̱͉̱̦̩̰̤̟̼̻̲̣̞̬͓̖̪̞̼̲̖̫̗̼̯͔̻̖͍̬̯͓͓̱͕͙͈͓͚̣̰̩̺̱͓̩̘̟̖͇̹̼̠͇̝̟̭͎͙̺͈̈̍͜͜͜ͅͅͅĚ̴̢̢̡̢̨̢̨̧̢̢̨̧̧̨̨̡̛̛̥̳̼̣̩̗̲̪͚̤̟͉̼̹͔̙̥̫͇̗̻̝͖̼̟̙̞̹̼͈͔̯̮̖̫̼̮͕͈̟̝̤͖̬͇͍̙̠͎̖̺̲̪̬͕̫̬̠͚͍̗͍̩̭̜̪͖̯͖̱̮̭̠̲̥͍̼̱̻̘̻̪̠̲̤̪͚̼̘͚̲̝͈̹̜͓̲̫̝̝̦̭̘̱̜̺͚̩͕̼̻̺͇͖̗̮̳̫̙̖̖͓̜̝̜̭̖͚͔͔̰̗̱͉̱̭̗͎̦͇̭̩͕̝͙̰̝͕̗̲͚̮̦͓̰͙̺̬̦̪̤͇̤̩̹̗̲͍̭̩̭̲̰͖̳̭͍̞̙̣͎͖͍͉̱͖̙̙̳͎̯͇̲̥̭͖͎̩͓̰̼͚̥̟̖̘͍̺̠͔̙̞͚̩͙͔̺̬̬̤̟̟̙̘̓̈́͊͌̀̍̎̈̾͊͊̎̐͐̍̓͒͒́͐͆̆̏͂̄͋̉͂̎͗͆̊̑̀̈́̔̔͐̃͊̆͊̏̉̓͂̉̔͐̽̅̿͆̈́̉͂͂̌̾̂̈̂̆̇̿̎̏͌̈́̐̍̊̊̌͗̏͒̉̆̔̊̂͛̔̍͒͐͐̎̃̓̊͒̾̈́̂̈́̌̀̐̔͂̒̅͘̚͘͘̚͘̕͘̕͜͜͜͜͝͝͝͝͝͝͝͝ͅͅͅͅͅͅͅͅ a⃗ a⃗⃗ a#ff0000⃗#ffff00⃗#00ff00⃗#00ffff⃗#0000ff⃗#ff00ff⃗#000000 ⌬ *italic* **bold** ***both*** #ff0000red#00ff00green#0000ffblue", .{}, false);
|
||||||
|
defer buffer2.deinit();
|
||||||
|
try buffer.render(100, 200, 64);
|
||||||
|
try buffer2.render(100, 1200, 64);
|
||||||
|
|
||||||
//graphics.Draw.setColor(0xff0000ff);
|
//graphics.Draw.setColor(0xff0000ff);
|
||||||
//graphics.Draw.rect(Vec2f{.x = 100, .y = 100}, Vec2f{.x = 200, .y = 100});
|
//graphics.Draw.rect(Vec2f{.x = 100, .y = 100}, Vec2f{.x = 200, .y = 100});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user