font rendering?
This commit is contained in:
parent
e439198097
commit
818382f37b
93
assets/fonts/Vollkorn/OFL.txt
Normal file
93
assets/fonts/Vollkorn/OFL.txt
Normal file
@ -0,0 +1,93 @@
|
||||
Copyright 2017 The Vollkorn Project Authors (https://github.com/FAlthausen/Vollkorn-Typeface)
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
https://openfontlicense.org
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
75
assets/fonts/Vollkorn/README.txt
Normal file
75
assets/fonts/Vollkorn/README.txt
Normal file
@ -0,0 +1,75 @@
|
||||
Vollkorn Variable Font
|
||||
======================
|
||||
|
||||
This download contains Vollkorn as both variable fonts and static fonts.
|
||||
|
||||
Vollkorn is a variable font with this axis:
|
||||
wght
|
||||
|
||||
This means all the styles are contained in these files:
|
||||
Vollkorn-VariableFont_wght.ttf
|
||||
Vollkorn-Italic-VariableFont_wght.ttf
|
||||
|
||||
If your app fully supports variable fonts, you can now pick intermediate styles
|
||||
that aren’t available as static fonts. Not all apps support variable fonts, and
|
||||
in those cases you can use the static font files for Vollkorn:
|
||||
static/Vollkorn-Regular.ttf
|
||||
static/Vollkorn-Medium.ttf
|
||||
static/Vollkorn-SemiBold.ttf
|
||||
static/Vollkorn-Bold.ttf
|
||||
static/Vollkorn-ExtraBold.ttf
|
||||
static/Vollkorn-Black.ttf
|
||||
static/Vollkorn-Italic.ttf
|
||||
static/Vollkorn-MediumItalic.ttf
|
||||
static/Vollkorn-SemiBoldItalic.ttf
|
||||
static/Vollkorn-BoldItalic.ttf
|
||||
static/Vollkorn-ExtraBoldItalic.ttf
|
||||
static/Vollkorn-BlackItalic.ttf
|
||||
|
||||
Get started
|
||||
-----------
|
||||
|
||||
1. Install the font files you want to use
|
||||
|
||||
2. Use your app's font picker to view the font family and all the
|
||||
available styles
|
||||
|
||||
Learn more about variable fonts
|
||||
-------------------------------
|
||||
|
||||
https://developers.google.com/web/fundamentals/design-and-ux/typography/variable-fonts
|
||||
https://variablefonts.typenetwork.com
|
||||
https://medium.com/variable-fonts
|
||||
|
||||
In desktop apps
|
||||
|
||||
https://theblog.adobe.com/can-variable-fonts-illustrator-cc
|
||||
https://helpx.adobe.com/nz/photoshop/using/fonts.html#variable_fonts
|
||||
|
||||
Online
|
||||
|
||||
https://developers.google.com/fonts/docs/getting_started
|
||||
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Fonts/Variable_Fonts_Guide
|
||||
https://developer.microsoft.com/en-us/microsoft-edge/testdrive/demos/variable-fonts
|
||||
|
||||
Installing fonts
|
||||
|
||||
MacOS: https://support.apple.com/en-us/HT201749
|
||||
Linux: https://www.google.com/search?q=how+to+install+a+font+on+gnu%2Blinux
|
||||
Windows: https://support.microsoft.com/en-us/help/314960/how-to-install-or-remove-a-font-in-windows
|
||||
|
||||
Android Apps
|
||||
|
||||
https://developers.google.com/fonts/docs/android
|
||||
https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts
|
||||
|
||||
License
|
||||
-------
|
||||
Please read the full license text (OFL.txt) to understand the permissions,
|
||||
restrictions and requirements for usage, redistribution, and modification.
|
||||
|
||||
You can use them in your products & projects – print or digital,
|
||||
commercial or otherwise.
|
||||
|
||||
This isn't legal advice, please consider consulting a lawyer and see the full
|
||||
license for all details.
|
||||
BIN
assets/fonts/Vollkorn/Vollkorn-Italic-VariableFont_wght.ttf
Normal file
BIN
assets/fonts/Vollkorn/Vollkorn-Italic-VariableFont_wght.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/Vollkorn-VariableFont_wght.ttf
Normal file
BIN
assets/fonts/Vollkorn/Vollkorn-VariableFont_wght.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-Black.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-Black.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-BlackItalic.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-BlackItalic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-Bold.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-Bold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-BoldItalic.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-BoldItalic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-ExtraBold.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-ExtraBold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-ExtraBoldItalic.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-ExtraBoldItalic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-Italic.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-Italic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-Medium.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-Medium.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-MediumItalic.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-MediumItalic.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-Regular.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-Regular.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-SemiBold.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-SemiBold.ttf
Normal file
Binary file not shown.
BIN
assets/fonts/Vollkorn/static/Vollkorn-SemiBoldItalic.ttf
Normal file
BIN
assets/fonts/Vollkorn/static/Vollkorn-SemiBoldItalic.ttf
Normal file
Binary file not shown.
@ -23,5 +23,5 @@ void main()
|
||||
// float x = fract(fragTexCoord.s);
|
||||
// float final = smoothstep(divider - 0.1, divider + 0.1, x);
|
||||
|
||||
finalColor = fragColor;
|
||||
finalColor = vec4(fragTexCoord, 0.0, 1.0);
|
||||
}
|
||||
|
||||
@ -103,6 +103,14 @@ pub fn build(b: *std.Build) void {
|
||||
});
|
||||
client.root_module.addImport("kb", kb_text_shape.createModule());
|
||||
|
||||
client.root_module.addCSourceFile(.{ .file = b.path("src/stb/stb_rect_pack.h"), .language = .c, .flags = &.{} });
|
||||
const stb_rect_pack = b.addTranslateC(.{
|
||||
.root_source_file = b.path("src/stb/stb_rect_pack.h"),
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
});
|
||||
client.root_module.addImport("stb_rect_pack", stb_rect_pack.createModule());
|
||||
|
||||
const zmath = b.dependency("zmath", .{});
|
||||
client.root_module.addImport("zmath", zmath.module("root"));
|
||||
server.root_module.addImport("zmath", zmath.module("root"));
|
||||
|
||||
372
src/client/font.zig
Normal file
372
src/client/font.zig
Normal file
@ -0,0 +1,372 @@
|
||||
const std = @import("std");
|
||||
|
||||
const rl = @import("raylib");
|
||||
const kb = @import("kb");
|
||||
const ft = @import("freetype");
|
||||
const rp = @import("stb_rect_pack");
|
||||
|
||||
pub var ft_lib : ft.Library = undefined;
|
||||
|
||||
const ATLAS_SIZE = 4096;
|
||||
|
||||
const Glyph = struct {
|
||||
utf32 : u32,
|
||||
index : u32,
|
||||
|
||||
x : i16,
|
||||
y : i16,
|
||||
width : u32,
|
||||
height : u32,
|
||||
rwidth : i16,
|
||||
rheight : i16,
|
||||
|
||||
bearing_x : i32,
|
||||
bearing_y : i32,
|
||||
|
||||
// y_max : i16,
|
||||
// y_min : i16,
|
||||
|
||||
ascent : i32,
|
||||
descent : i32,
|
||||
advance : i16,
|
||||
|
||||
st0 : rl.Vector2,
|
||||
st1 : rl.Vector2,
|
||||
|
||||
bitmap : []u8,
|
||||
};
|
||||
|
||||
pub const Font = struct {
|
||||
face : ft.Face,
|
||||
glyphs : std.AutoHashMap(u32, Glyph),
|
||||
kb : kb.kbts_font,
|
||||
texture : rl.Texture2D,
|
||||
font_data : []u8,
|
||||
|
||||
pub fn init(filename : []const u8, size : i32, allocator: std.mem.Allocator) !Font {
|
||||
|
||||
// const pixel_size = size * 96.0 / 72.0;
|
||||
|
||||
const font_data = try std.fs.cwd().readFileAlloc(allocator, filename, 10 * 1024 * 1024);
|
||||
|
||||
|
||||
// if !success {
|
||||
// log("[Error] Could not read the font file.");
|
||||
// return;
|
||||
// }
|
||||
|
||||
var face = try ft_lib.createFaceMemory(font_data, 0);
|
||||
var glyphs = std.AutoHashMap(u32, Glyph).init(allocator);
|
||||
// .FT_New_Memory_Face(ftlib, font_data.data, xx font_data.count, 0, *face);
|
||||
// if error {
|
||||
// log("[Error] %", to_string(FT_Error_String(error)));
|
||||
// }
|
||||
|
||||
//FT_Set_Pixel_Sizes(face, 0, size);
|
||||
try face.setCharSize(0, size * 64, 0, 96);
|
||||
// error = FT_Set_Char_Size(face, 0, size * 64, 0, 96);
|
||||
// if error {
|
||||
// log("[Error] %", to_string(FT_Error_String(error)));
|
||||
// }
|
||||
|
||||
var rects = try std.ArrayList(rp.stbrp_rect).initCapacity(allocator, 1024);
|
||||
defer rects.deinit(allocator);
|
||||
|
||||
var nodes : [ATLAS_SIZE]rp.stbrp_node = std.mem.zeroes([ATLAS_SIZE]rp.stbrp_node);
|
||||
|
||||
var stbrpcontext : rp.stbrp_context = .{};
|
||||
rp.stbrp_init_target(&stbrpcontext, ATLAS_SIZE, ATLAS_SIZE, &nodes, nodes.len);
|
||||
|
||||
const _atlas : []u8 = try allocator.alloc(u8, ATLAS_SIZE * ATLAS_SIZE);
|
||||
defer allocator.free(_atlas);
|
||||
|
||||
var atlas : rl.Image = .{
|
||||
.data = _atlas.ptr,
|
||||
.width = ATLAS_SIZE,
|
||||
.height = ATLAS_SIZE,
|
||||
.mipmaps = 1,
|
||||
.format = .uncompressed_grayscale
|
||||
};
|
||||
|
||||
std.log.info("We have {} glyphs", .{face.numGlyphs()});
|
||||
// for (0..1500) |i| {
|
||||
for (0..face.numGlyphs()) |i| {
|
||||
//for 65..90 {
|
||||
//index := FT_Get_Char_Index(face, xx it);
|
||||
try face.loadGlyph(@intCast(i), .{ .render = true });
|
||||
|
||||
// array_add(*rects, stbrp_rect.{cast(s32) face.glyph.glyph_index, cast(s32) face.glyph.bitmap.width,
|
||||
// cast(s32) face.glyph.bitmap.rows, 0, 0, 0});
|
||||
try rects.append(allocator,
|
||||
rp.stbrp_rect{
|
||||
.id = @intCast(face.glyph().glyphIndex()),
|
||||
.w = @intCast(face.glyph().bitmap().width()),
|
||||
.h = @intCast(face.glyph().bitmap().rows()),
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.was_packed = 0,
|
||||
}
|
||||
);
|
||||
|
||||
const width = face.glyph().bitmap().width();
|
||||
const height = face.glyph().bitmap().rows();
|
||||
const bearing_y = face.glyph().bitmapTop();
|
||||
const descent = @as(i32, @intCast(height)) - bearing_y;
|
||||
const ascent = @as(i32, @intCast(height)) - descent;
|
||||
|
||||
const glyph = Glyph{
|
||||
.x = 0,
|
||||
.y = 0,
|
||||
.utf32 = @intCast(i),
|
||||
.index = face.glyph().glyphIndex(),
|
||||
.bearing_x = face.glyph().bitmapLeft(),
|
||||
.bearing_y = face.glyph().bitmapTop(),
|
||||
.width = width,
|
||||
.height = height,
|
||||
.rwidth = @intCast(face.glyph().metrics().width >> 6),
|
||||
.rheight = @intCast(face.glyph().metrics().height >> 6),
|
||||
.descent = @as(i32, @intCast(face.glyph().bitmap().rows())) - face.glyph().bitmapTop(),
|
||||
.ascent = ascent,
|
||||
.advance = @intCast(face.glyph().advance().x >> 6),
|
||||
.st0 = rl.Vector2.zero(),
|
||||
.st1 = rl.Vector2.zero(),
|
||||
.bitmap = try allocator.alloc(u8, width * height),
|
||||
};
|
||||
const s = glyph.height * glyph.width;
|
||||
const buf = face.glyph().bitmap().buffer() orelse continue;
|
||||
@memcpy(glyph.bitmap, buf[0..s]);
|
||||
|
||||
// table_set(*font.glyphs, glyph.index, glyph);
|
||||
try glyphs.put(glyph.index, glyph);
|
||||
// if (try glyphs.fetchPut(glyph.index, glyph)) |old| {
|
||||
// allocator.free(old.value.bitmap);
|
||||
// }
|
||||
}
|
||||
|
||||
_ = rp.stbrp_pack_rects(&stbrpcontext, rects.items.ptr, @intCast(rects.items.len));
|
||||
// stbrp_pack_rects(*stbrpcontext, rects.data, cast(s32) rects.count);
|
||||
|
||||
for (rects.items) |rect| {
|
||||
// for rect : rects {
|
||||
const glyph = glyphs.getPtr(@intCast(rect.id)) orelse continue;
|
||||
if (glyph.bitmap.len != 0) {
|
||||
glyph.x = @intCast(rect.x);
|
||||
glyph.y = @as(i16, @intCast(rect.y)) + @as(i16, @intCast(glyph.height));
|
||||
|
||||
const fx = @as(f32, @floatFromInt(glyph.x));
|
||||
const fy = @as(f32, @floatFromInt(glyph.y));
|
||||
const fw = @as(f32, @floatFromInt(glyph.width));
|
||||
const fh = @as(f32, @floatFromInt(glyph.height));
|
||||
const fs = @as(f32, ATLAS_SIZE);
|
||||
|
||||
glyph.st0 = rl.Vector2.init((fx + 0.5) / fs, (fy - 0.5) / fs);
|
||||
|
||||
glyph.st1 = .{
|
||||
.x = (fx + fw) / fs,
|
||||
.y = (fy - fh) / fs,
|
||||
};
|
||||
|
||||
const g = rl.Image{
|
||||
.data = glyph.bitmap.ptr,
|
||||
.width = @intCast(glyph.width),
|
||||
.height = @intCast(glyph.height),
|
||||
.mipmaps = 1,
|
||||
.format = .uncompressed_grayscale
|
||||
};
|
||||
|
||||
atlas.drawImage(g,
|
||||
.{ .x = 0, .y = 0, .width = @floatFromInt(g.width), .height = @floatFromInt(g.height) },
|
||||
.{ .x = @floatFromInt(rect.x), .y = @floatFromInt(rect.y), .width = @floatFromInt(g.width), .height = @floatFromInt(g.height)},
|
||||
.white
|
||||
);
|
||||
|
||||
// copyBitmapToAtlas(glyph.bitmap, glyph.width, glyph.height, atlas, @intCast(rect.x), @intCast(rect.y));
|
||||
}
|
||||
// glyph : *Glyph = table_find_pointer(*font.glyphs, cast(u32) rect.id);
|
||||
// if (glyph.bitmap) {
|
||||
// glyph.x = cast,trunc(s16, rect.x);
|
||||
// glyph.y = cast,trunc(s16, rect.y) + xx glyph.height;
|
||||
// glyph.st0 = .{cast(float, glyph.x + 0.5) / ATLAS_SIZE, cast(float, glyph.y - 0.5) / ATLAS_SIZE};
|
||||
// //glyph.st1 = glyph.st0 + .{cast(float, glyph.width) / ATLAS_SIZE, -cast(float, glyph.height) / ATLAS_SIZE};
|
||||
// glyph.st1 = .{(cast(float, glyph.x + glyph.width )) / cast(float, ATLAS_SIZE), (cast(float, glyph.y - glyph.height )) / cast(float, ATLAS_SIZE)};
|
||||
// copyBitmapToAtlas(glyph.bitmap, cast(s32) glyph.width, cast(s32) glyph.height, atlas, cast(s32) rect.x, cast(s32) rect.y);
|
||||
// }
|
||||
}
|
||||
|
||||
const texture = try rl.Texture.fromImage(atlas);
|
||||
|
||||
// image.flipVertical();
|
||||
// texture = make_texture_from_data(atlas, ATLAS_SIZE, ATLAS_SIZE);
|
||||
|
||||
// const kb = kbts_FontFromMemory(font_data.data, xx font_data.count, 0, null, null);
|
||||
var _kb = kb.kbts_FontFromMemory(font_data.ptr, @intCast(font_data.len), 0, null, null);
|
||||
|
||||
if (kb.kbts_FontIsValid(&_kb) != 0) {
|
||||
std.log.info("[Error] [kb_text_shape] Failed to load the font.", .{});
|
||||
}
|
||||
|
||||
return .{
|
||||
.face = face,
|
||||
.glyphs = glyphs,
|
||||
.kb = _kb,
|
||||
.texture = texture,
|
||||
.font_data = font_data,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Font, allocator: std.mem.Allocator) void {
|
||||
var it = self.glyphs.valueIterator();
|
||||
while (it.next()) |g| {
|
||||
allocator.free(g.bitmap);
|
||||
}
|
||||
self.glyphs.deinit();
|
||||
self.face.deinit();
|
||||
kb.kbts_FreeFont(&self.kb);
|
||||
self.texture.unload();
|
||||
allocator.free(self.font_data);
|
||||
}
|
||||
|
||||
pub fn render_text(
|
||||
self: *Font,
|
||||
text: []const u8,
|
||||
pos: rl.Vector2,
|
||||
window_space: bool,
|
||||
colour: rl.Color,
|
||||
background: rl.Color,
|
||||
nice_background: bool,
|
||||
count_descent: bool
|
||||
) void {
|
||||
if (text.len == 0)
|
||||
return;
|
||||
|
||||
if (nice_background) {
|
||||
self.render_text(text, pos.add(.{.x = 3.0, .y = -3.0}), window_space, rl.Color.init(0, 0, 0, 255), rl.Color.init(0, 0, 0, 0), false, false);
|
||||
}
|
||||
|
||||
var render_pos = pos;
|
||||
//_ = count_descent;
|
||||
//_ = colour;
|
||||
_ = background;
|
||||
|
||||
const draw_size, const max_ascent, const max_descent = self.size_row(text, 0, 0);
|
||||
_ = max_ascent;
|
||||
_ = max_descent;
|
||||
|
||||
const Context = kb.kbts_CreateShapeContext(null, null);
|
||||
const kb_font = kb.kbts_ShapePushFont(Context, &self.kb);
|
||||
if (kb_font == null) {
|
||||
std.log.info("Could not open font!", .{});
|
||||
return;
|
||||
}
|
||||
|
||||
kb.kbts_ShapeBegin(Context, kb.KBTS_DIRECTION_DONT_KNOW, kb.KBTS_LANGUAGE_DONT_KNOW);
|
||||
kb.kbts_ShapeUtf8(Context, text.ptr, @intCast(text.len), kb.KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX);
|
||||
kb.kbts_ShapeEnd(Context);
|
||||
|
||||
var x_offset : f32 = 0;
|
||||
var y_offset : f32 = 0;
|
||||
|
||||
// Layout runs naively left to right.
|
||||
var Run = kb.kbts_run{};
|
||||
while (kb.kbts_ShapeRun(Context, &Run) != 0) {
|
||||
if ((Run.Flags & kb.KBTS_BREAK_FLAG_LINE_HARD) != 0) {
|
||||
|
||||
}
|
||||
|
||||
var g : [*c]kb.kbts_glyph = null;
|
||||
while (kb.kbts_GlyphIteratorNext(&Run.Glyphs, &g) != 0) {
|
||||
const RunGlyph : *kb.kbts_glyph = g.?; // assert non-null
|
||||
const CodepointIndex : i32 = RunGlyph.UserIdOrCodepointIndex;
|
||||
var ShapeCodepoint : kb.kbts_shape_codepoint = undefined;
|
||||
if (kb.kbts_ShapeGetShapeCodepoint(Context, CodepointIndex, &ShapeCodepoint) == 0) {
|
||||
std.log.info("aah it's wrong!! idk how to handle the error rn!!", .{});
|
||||
}
|
||||
|
||||
// glyph : *Glyph = table_find_pointer(*text.font.glyphs, RunGlyph.Id);
|
||||
if (self.glyphs.getPtr(RunGlyph.Id)) |glyph| {
|
||||
var v0 = rl.Vector2.zero();
|
||||
var v1 = rl.Vector2.zero();
|
||||
if (count_descent) {
|
||||
v0 = render_pos.add(.{ .x = x_offset,// + glyph.bearing_x,
|
||||
.y = y_offset - @as(f32, @floatFromInt(glyph.bearing_y)) + draw_size.y });// /*- max_descent*/};
|
||||
} else {
|
||||
v0 = render_pos.add(.{ .x = x_offset,// + glyph.bearing_x,
|
||||
.y = y_offset - @as(f32, @floatFromInt(@as(i32, @intCast(glyph.height)) - glyph.bearing_y)) });//* - glyph.height + draw_size.y*/};
|
||||
}
|
||||
|
||||
v1 = v0.add(rl.Vector2{ .x = @floatFromInt(glyph.width), .y = @floatFromInt(glyph.height) });
|
||||
const p0 : rl.Vector4 = .{ .x = v0.x, .y = v0.y, .z = 0.0, .w = 1.0 };
|
||||
const p1 : rl.Vector4 = .{ .x = v1.x, .y = v1.y, .z = 0.0, .w = 1.0 };
|
||||
x_offset += @floatFromInt(ft.mulFix(RunGlyph.AdvanceX, @intCast(self.face.size().metrics().x_scale)) >> 6);
|
||||
y_offset += @floatFromInt(ft.mulFix(RunGlyph.AdvanceY, @intCast(self.face.size().metrics().y_scale)) >> 6);
|
||||
|
||||
// #if Y_IS_UP {
|
||||
// t0 := Vector2.{cast(float, glyph.x) / cast(float, ATLAS_SIZE), cast(float, glyph.y) / cast(float, ATLAS_SIZE)};
|
||||
// t1 := t0 + Vector2.{cast(float, glyph.width) / cast(float, ATLAS_SIZE), -cast(float, glyph.height) / cast(float, ATLAS_SIZE)};
|
||||
const st0 = glyph.st0;
|
||||
const st1 = glyph.st1;
|
||||
// } else {
|
||||
// t0 := Vector2.{cast(float, glyph.x / ATLAS_SIZE), cast(float, glyph.y / ATLAS_SIZE)};
|
||||
// t1 := t0 + .{cast(float, glyph.width / ATLAS_SIZE), cast(float, glyph.height / ATLAS_SIZE)};
|
||||
// }
|
||||
|
||||
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st0.x, st0.y); rl.gl.rlVertex2f(p0.x, p0.y);
|
||||
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st1.x, st1.y); rl.gl.rlVertex2f(p1.x, p1.y);
|
||||
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st0.x, st1.y); rl.gl.rlVertex2f(p0.x, p1.y);
|
||||
|
||||
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st1.x, st0.y); rl.gl.rlVertex2f(p1.x, p0.y);
|
||||
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st1.x, st1.y); rl.gl.rlVertex2f(p1.x, p1.y);
|
||||
rl.gl.rlColor4ub(colour.r, colour.g, colour.b, colour.a); rl.gl.rlTexCoord2f(st0.x, st0.y); rl.gl.rlVertex2f(p0.x, p0.y);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -> vec2, float, s32
|
||||
pub fn size_row(self: *Font, str: []const u8, n: i32, max_width: f32) struct {rl.Vector2, f32, i32} {
|
||||
_ = max_width;
|
||||
_ = n;
|
||||
const Context = kb.kbts_CreateShapeContext(null, null);
|
||||
const kb_font = kb.kbts_ShapePushFont(Context, &self.kb);
|
||||
if (kb_font == null) {
|
||||
std.log.info("Could not open font!", .{});
|
||||
return .{ rl.Vector2.zero(), 0, 0 };
|
||||
}
|
||||
|
||||
kb.kbts_ShapeBegin(Context, kb.KBTS_DIRECTION_DONT_KNOW, kb.KBTS_LANGUAGE_DONT_KNOW);
|
||||
kb.kbts_ShapeUtf8(Context, str.ptr, @intCast(str.len), kb.KBTS_USER_ID_GENERATION_MODE_CODEPOINT_INDEX);
|
||||
kb.kbts_ShapeEnd(Context);
|
||||
|
||||
var size = rl.Vector2.zero();
|
||||
var max_descent : f32 = 0;
|
||||
|
||||
// Layout runs naively left to right.
|
||||
var Run = kb.kbts_run{};
|
||||
while(kb.kbts_ShapeRun(Context, &Run) != 0) {
|
||||
if ((Run.Flags & kb.KBTS_BREAK_FLAG_LINE_HARD) != 0) {
|
||||
|
||||
}
|
||||
|
||||
var g : [*c]kb.kbts_glyph = null;
|
||||
while (kb.kbts_GlyphIteratorNext(&Run.Glyphs, &g) != 0) {
|
||||
const RunGlyph : *kb.kbts_glyph = g.?; // assert non-null
|
||||
const CodepointIndex : i32 = RunGlyph.UserIdOrCodepointIndex;
|
||||
var ShapeCodepoint : kb.kbts_shape_codepoint = undefined;
|
||||
if (kb.kbts_ShapeGetShapeCodepoint(Context, CodepointIndex, &ShapeCodepoint) == 0) {
|
||||
std.log.info("aah it's wrong!! idk how to handle the error rn!!", .{});
|
||||
}
|
||||
|
||||
if (self.glyphs.getPtr(@intCast(ShapeCodepoint.Codepoint))) |glyph| {
|
||||
size.y = @max(size.y, @as(f32, @floatFromInt(glyph.height)));
|
||||
// size.x += (ft.mulFix(RunGlyph.AdvanceX, self.face.size().metrics().x_scale) >> 6);
|
||||
size.x += @floatFromInt(ft.mulFix(RunGlyph.AdvanceX, @intCast(self.face.size().metrics().x_scale)) >> 6);
|
||||
max_descent = @max(max_descent, @as(f32, @floatFromInt(glyph.descent)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return .{ size, max_descent, 0};
|
||||
}
|
||||
};
|
||||
@ -3,14 +3,14 @@ const zm = @import("zmath");
|
||||
const znet = @import("znet");
|
||||
const rl = @import("raylib");
|
||||
const bufzilla = @import("bufzilla");
|
||||
const ft = @import("freetype");
|
||||
|
||||
const shared = @import("shared");
|
||||
const kb = @import("kb");
|
||||
const ft = @import("freetype");
|
||||
|
||||
const client = @import("client.zig");
|
||||
const entity = @import("entity.zig");
|
||||
const misc = @import("misc.zig");
|
||||
const font = @import("font.zig");
|
||||
|
||||
const dt : f32 = 1.0 / 200.0;
|
||||
var t : f32 = 0;
|
||||
@ -35,6 +35,7 @@ pub fn main() !void {
|
||||
std.log.info("Hello Client!", .{});
|
||||
|
||||
try init();
|
||||
defer _ = dbg_allocator.deinit();
|
||||
try znet.init();
|
||||
defer znet.deinit();
|
||||
rl.initWindow(1280, 720, "zzz");
|
||||
@ -42,11 +43,12 @@ pub fn main() !void {
|
||||
|
||||
// kbts_shape_context *Context = kbts_CreateShapeContext(0, 0);
|
||||
|
||||
_ = try ft.Library.init();
|
||||
font.ft_lib = try ft.Library.init();
|
||||
|
||||
_ = kb.kbts_CreateShapeContext(null, null);
|
||||
var f = try font.Font.init("assets/fonts/Vollkorn/static/Vollkorn-Regular.ttf", 48, allocator);
|
||||
defer f.deinit(allocator);
|
||||
|
||||
// const shader = try rl.loadShader(null, "assets/test.frag");
|
||||
const shader = try rl.loadShader(null, "assets/test.frag");
|
||||
|
||||
// const img = rl.genImageColor(32, 32, .blank);
|
||||
// const tx = try rl.loadTextureFromImage(img);
|
||||
@ -70,8 +72,6 @@ pub fn main() !void {
|
||||
.data = 0,
|
||||
});
|
||||
|
||||
defer _ = dbg_allocator.deinit();
|
||||
|
||||
// connect() catch |err| switch (err) {
|
||||
// error.ConnectionRefused => {
|
||||
// std.log.err("server refused connection", .{});
|
||||
@ -180,28 +180,44 @@ pub fn main() !void {
|
||||
|
||||
rl.beginDrawing();
|
||||
|
||||
// rl.beginShaderMode(shader);
|
||||
rl.gl.rlBegin(rl.gl.rl_triangles);
|
||||
rl.beginShaderMode(shader);
|
||||
|
||||
// const color = rl.Color.init(255, 0, 255, 255);
|
||||
const top : rl.Vector2 = .{ .x = @as(f32, @floatFromInt(rl.getScreenWidth())) / 2.0, .y = 0.0 };
|
||||
const bottomLeft : rl.Vector2 = .{ .x = 0.0, .y = @floatFromInt(rl.getScreenHeight()) };
|
||||
const bottomRight : rl.Vector2 = .{ .x = @floatFromInt(rl.getScreenWidth()), .y = @floatFromInt(rl.getScreenHeight()) };
|
||||
f.render_text(
|
||||
"H",
|
||||
rl.Vector2.init(400, 400),
|
||||
true,
|
||||
rl.Color.white,
|
||||
rl.Color.blank,
|
||||
true,
|
||||
true
|
||||
);
|
||||
|
||||
// rl.gl.vertex
|
||||
rl.gl.rlColor4ub(0, 255, 0, 255);
|
||||
rl.gl.rlVertex2f(top.x, top.y);
|
||||
rl.gl.rlColor4ub(255, 0, 0, 255);
|
||||
rl.gl.rlVertex2f(bottomLeft.x, bottomLeft.y);
|
||||
rl.gl.rlColor4ub(0, 0, 255, 255);
|
||||
rl.gl.rlVertex2f(bottomRight.x, bottomRight.y);
|
||||
|
||||
// rl.gl.rlVertex2f(topRight.x, topRight.y);
|
||||
// rl.gl.rlVertex2f(bottomLeft.x, bottomLeft.y);
|
||||
// rl.gl.rlVertex2f(bottomRight.x, bottomRight.y);
|
||||
// rl.gl.rlBegin(rl.gl.rl_triangles);
|
||||
|
||||
rl.gl.rlEnd();
|
||||
// rl.endShaderMode();
|
||||
// {
|
||||
// const topLeft : rl.Vector2 = .{ .x = 0.0, .y = 0.0 };
|
||||
// const topRight : rl.Vector2 = .{ .x = @floatFromInt(rl.getScreenWidth()), .y = 0.0 };
|
||||
// const bottomLeft : rl.Vector2 = .{ .x = 0.0, .y = @floatFromInt(rl.getScreenHeight()) };
|
||||
// const bottomRight : rl.Vector2 = .{ .x = @floatFromInt(rl.getScreenWidth()), .y = @floatFromInt(rl.getScreenHeight()) };
|
||||
|
||||
// rl.gl.rlTexCoord2f(0.0, 0.0);
|
||||
// rl.gl.rlVertex2f(topLeft.x, topLeft.y);
|
||||
// rl.gl.rlTexCoord2f(0.0, 1.0);
|
||||
// rl.gl.rlVertex2f(bottomLeft.x, bottomLeft.y);
|
||||
// rl.gl.rlTexCoord2f(1.0, 0.0);
|
||||
// rl.gl.rlVertex2f(topRight.x, topRight.y);
|
||||
|
||||
// rl.gl.rlTexCoord2f(1.0, 0.0);
|
||||
// rl.gl.rlVertex2f(topRight.x, topRight.y);
|
||||
// rl.gl.rlTexCoord2f(0.0, 1.0);
|
||||
// rl.gl.rlVertex2f(bottomLeft.x, bottomLeft.y);
|
||||
// rl.gl.rlTexCoord2f(1.0, 1.0);
|
||||
// rl.gl.rlVertex2f(bottomRight.x, bottomRight.y);
|
||||
// }
|
||||
|
||||
// rl.gl.rlEnd();
|
||||
rl.endShaderMode();
|
||||
|
||||
// const connected_text = "Connected";
|
||||
//const not_connected_text = "Not Connected";
|
||||
@ -232,7 +248,7 @@ pub fn main() !void {
|
||||
|
||||
//rl.drawText("Congrats! You created your first window!", rl.getMouseX(), rl.getMouseY(), 20, .white);
|
||||
//rl.drawRectangleLines(0, 0, 100, 100, .red);
|
||||
//misc.drawFPS(0, 0, frame_time, frame);
|
||||
misc.drawFPS(0, 0, frame_time, frame);
|
||||
|
||||
//elf.draw();
|
||||
|
||||
|
||||
623
src/stb/stb_rect_pack.h
Normal file
623
src/stb/stb_rect_pack.h
Normal file
@ -0,0 +1,623 @@
|
||||
// stb_rect_pack.h - v1.01 - public domain - rectangle packing
|
||||
// Sean Barrett 2014
|
||||
//
|
||||
// Useful for e.g. packing rectangular textures into an atlas.
|
||||
// Does not do rotation.
|
||||
//
|
||||
// Before #including,
|
||||
//
|
||||
// #define STB_RECT_PACK_IMPLEMENTATION
|
||||
//
|
||||
// in the file that you want to have the implementation.
|
||||
//
|
||||
// Not necessarily the awesomest packing method, but better than
|
||||
// the totally naive one in stb_truetype (which is primarily what
|
||||
// this is meant to replace).
|
||||
//
|
||||
// Has only had a few tests run, may have issues.
|
||||
//
|
||||
// More docs to come.
|
||||
//
|
||||
// No memory allocations; uses qsort() and assert() from stdlib.
|
||||
// Can override those by defining STBRP_SORT and STBRP_ASSERT.
|
||||
//
|
||||
// This library currently uses the Skyline Bottom-Left algorithm.
|
||||
//
|
||||
// Please note: better rectangle packers are welcome! Please
|
||||
// implement them to the same API, but with a different init
|
||||
// function.
|
||||
//
|
||||
// Credits
|
||||
//
|
||||
// Library
|
||||
// Sean Barrett
|
||||
// Minor features
|
||||
// Martins Mozeiko
|
||||
// github:IntellectualKitty
|
||||
//
|
||||
// Bugfixes / warning fixes
|
||||
// Jeremy Jaussaud
|
||||
// Fabian Giesen
|
||||
//
|
||||
// Version history:
|
||||
//
|
||||
// 1.01 (2021-07-11) always use large rect mode, expose STBRP__MAXVAL in public section
|
||||
// 1.00 (2019-02-25) avoid small space waste; gracefully fail too-wide rectangles
|
||||
// 0.99 (2019-02-07) warning fixes
|
||||
// 0.11 (2017-03-03) return packing success/fail result
|
||||
// 0.10 (2016-10-25) remove cast-away-const to avoid warnings
|
||||
// 0.09 (2016-08-27) fix compiler warnings
|
||||
// 0.08 (2015-09-13) really fix bug with empty rects (w=0 or h=0)
|
||||
// 0.07 (2015-09-13) fix bug with empty rects (w=0 or h=0)
|
||||
// 0.06 (2015-04-15) added STBRP_SORT to allow replacing qsort
|
||||
// 0.05: added STBRP_ASSERT to allow replacing assert
|
||||
// 0.04: fixed minor bug in STBRP_LARGE_RECTS support
|
||||
// 0.01: initial release
|
||||
//
|
||||
// LICENSE
|
||||
//
|
||||
// See end of file for license information.
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// INCLUDE SECTION
|
||||
//
|
||||
|
||||
#ifndef STB_INCLUDE_STB_RECT_PACK_H
|
||||
#define STB_INCLUDE_STB_RECT_PACK_H
|
||||
|
||||
#define STB_RECT_PACK_VERSION 1
|
||||
|
||||
#ifdef STBRP_STATIC
|
||||
#define STBRP_DEF static
|
||||
#else
|
||||
#define STBRP_DEF extern
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct stbrp_context stbrp_context;
|
||||
typedef struct stbrp_node stbrp_node;
|
||||
typedef struct stbrp_rect stbrp_rect;
|
||||
|
||||
typedef int stbrp_coord;
|
||||
|
||||
#define STBRP__MAXVAL 0x7fffffff
|
||||
// Mostly for internal use, but this is the maximum supported coordinate value.
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects (stbrp_context *context, stbrp_rect *rects, int num_rects);
|
||||
// Assign packed locations to rectangles. The rectangles are of type
|
||||
// 'stbrp_rect' defined below, stored in the array 'rects', and there
|
||||
// are 'num_rects' many of them.
|
||||
//
|
||||
// Rectangles which are successfully packed have the 'was_packed' flag
|
||||
// set to a non-zero value and 'x' and 'y' store the minimum location
|
||||
// on each axis (i.e. bottom-left in cartesian coordinates, top-left
|
||||
// if you imagine y increasing downwards). Rectangles which do not fit
|
||||
// have the 'was_packed' flag set to 0.
|
||||
//
|
||||
// You should not try to access the 'rects' array from another thread
|
||||
// while this function is running, as the function temporarily reorders
|
||||
// the array while it executes.
|
||||
//
|
||||
// To pack into another rectangle, you need to call stbrp_init_target
|
||||
// again. To continue packing into the same rectangle, you can call
|
||||
// this function again. Calling this multiple times with multiple rect
|
||||
// arrays will probably produce worse packing results than calling it
|
||||
// a single time with the full rectangle array, but the option is
|
||||
// available.
|
||||
//
|
||||
// The function returns 1 if all of the rectangles were successfully
|
||||
// packed and 0 otherwise.
|
||||
|
||||
struct stbrp_rect
|
||||
{
|
||||
// reserved for your use:
|
||||
int id;
|
||||
|
||||
// input:
|
||||
stbrp_coord w, h;
|
||||
|
||||
// output:
|
||||
stbrp_coord x, y;
|
||||
int was_packed; // non-zero if valid packing
|
||||
|
||||
}; // 16 bytes, nominally
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_init_target (stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes);
|
||||
// Initialize a rectangle packer to:
|
||||
// pack a rectangle that is 'width' by 'height' in dimensions
|
||||
// using temporary storage provided by the array 'nodes', which is 'num_nodes' long
|
||||
//
|
||||
// You must call this function every time you start packing into a new target.
|
||||
//
|
||||
// There is no "shutdown" function. The 'nodes' memory must stay valid for
|
||||
// the following stbrp_pack_rects() call (or calls), but can be freed after
|
||||
// the call (or calls) finish.
|
||||
//
|
||||
// Note: to guarantee best results, either:
|
||||
// 1. make sure 'num_nodes' >= 'width'
|
||||
// or 2. call stbrp_allow_out_of_mem() defined below with 'allow_out_of_mem = 1'
|
||||
//
|
||||
// If you don't do either of the above things, widths will be quantized to multiples
|
||||
// of small integers to guarantee the algorithm doesn't run out of temporary storage.
|
||||
//
|
||||
// If you do #2, then the non-quantized algorithm will be used, but the algorithm
|
||||
// may run out of temporary storage and be unable to pack some rectangles.
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem (stbrp_context *context, int allow_out_of_mem);
|
||||
// Optionally call this function after init but before doing any packing to
|
||||
// change the handling of the out-of-temp-memory scenario, described above.
|
||||
// If you call init again, this will be reset to the default (false).
|
||||
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic (stbrp_context *context, int heuristic);
|
||||
// Optionally select which packing heuristic the library should use. Different
|
||||
// heuristics will produce better/worse results for different data sets.
|
||||
// If you call init again, this will be reset to the default.
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP_HEURISTIC_Skyline_default=0,
|
||||
STBRP_HEURISTIC_Skyline_BL_sortHeight = STBRP_HEURISTIC_Skyline_default,
|
||||
STBRP_HEURISTIC_Skyline_BF_sortHeight
|
||||
};
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// the details of the following structures don't matter to you, but they must
|
||||
// be visible so you can handle the memory allocations for them
|
||||
|
||||
struct stbrp_node
|
||||
{
|
||||
stbrp_coord x,y;
|
||||
stbrp_node *next;
|
||||
};
|
||||
|
||||
struct stbrp_context
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
int align;
|
||||
int init_mode;
|
||||
int heuristic;
|
||||
int num_nodes;
|
||||
stbrp_node *active_head;
|
||||
stbrp_node *free_head;
|
||||
stbrp_node extra[2]; // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// IMPLEMENTATION SECTION
|
||||
//
|
||||
|
||||
#ifdef STB_RECT_PACK_IMPLEMENTATION
|
||||
#ifndef STBRP_SORT
|
||||
#include <stdlib.h>
|
||||
#define STBRP_SORT qsort
|
||||
#endif
|
||||
|
||||
#ifndef STBRP_ASSERT
|
||||
#include <assert.h>
|
||||
#define STBRP_ASSERT assert
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define STBRP__NOTUSED(v) (void)(v)
|
||||
#define STBRP__CDECL __cdecl
|
||||
#else
|
||||
#define STBRP__NOTUSED(v) (void)sizeof(v)
|
||||
#define STBRP__CDECL
|
||||
#endif
|
||||
|
||||
enum
|
||||
{
|
||||
STBRP__INIT_skyline = 1
|
||||
};
|
||||
|
||||
STBRP_DEF void stbrp_setup_heuristic(stbrp_context *context, int heuristic)
|
||||
{
|
||||
switch (context->init_mode) {
|
||||
case STBRP__INIT_skyline:
|
||||
STBRP_ASSERT(heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight || heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight);
|
||||
context->heuristic = heuristic;
|
||||
break;
|
||||
default:
|
||||
STBRP_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_setup_allow_out_of_mem(stbrp_context *context, int allow_out_of_mem)
|
||||
{
|
||||
if (allow_out_of_mem)
|
||||
// if it's ok to run out of memory, then don't bother aligning them;
|
||||
// this gives better packing, but may fail due to OOM (even though
|
||||
// the rectangles easily fit). @TODO a smarter approach would be to only
|
||||
// quantize once we've hit OOM, then we could get rid of this parameter.
|
||||
context->align = 1;
|
||||
else {
|
||||
// if it's not ok to run out of memory, then quantize the widths
|
||||
// so that num_nodes is always enough nodes.
|
||||
//
|
||||
// I.e. num_nodes * align >= width
|
||||
// align >= width / num_nodes
|
||||
// align = ceil(width/num_nodes)
|
||||
|
||||
context->align = (context->width + context->num_nodes-1) / context->num_nodes;
|
||||
}
|
||||
}
|
||||
|
||||
STBRP_DEF void stbrp_init_target(stbrp_context *context, int width, int height, stbrp_node *nodes, int num_nodes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i=0; i < num_nodes-1; ++i)
|
||||
nodes[i].next = &nodes[i+1];
|
||||
nodes[i].next = NULL;
|
||||
context->init_mode = STBRP__INIT_skyline;
|
||||
context->heuristic = STBRP_HEURISTIC_Skyline_default;
|
||||
context->free_head = &nodes[0];
|
||||
context->active_head = &context->extra[0];
|
||||
context->width = width;
|
||||
context->height = height;
|
||||
context->num_nodes = num_nodes;
|
||||
stbrp_setup_allow_out_of_mem(context, 0);
|
||||
|
||||
// node 0 is the full width, node 1 is the sentinel (lets us not store width explicitly)
|
||||
context->extra[0].x = 0;
|
||||
context->extra[0].y = 0;
|
||||
context->extra[0].next = &context->extra[1];
|
||||
context->extra[1].x = (stbrp_coord) width;
|
||||
context->extra[1].y = (1<<30);
|
||||
context->extra[1].next = NULL;
|
||||
}
|
||||
|
||||
// find minimum y position if it starts at x1
|
||||
static int stbrp__skyline_find_min_y(stbrp_context *c, stbrp_node *first, int x0, int width, int *pwaste)
|
||||
{
|
||||
stbrp_node *node = first;
|
||||
int x1 = x0 + width;
|
||||
int min_y, visited_width, waste_area;
|
||||
|
||||
STBRP__NOTUSED(c);
|
||||
|
||||
STBRP_ASSERT(first->x <= x0);
|
||||
|
||||
#if 0
|
||||
// skip in case we're past the node
|
||||
while (node->next->x <= x0)
|
||||
++node;
|
||||
#else
|
||||
STBRP_ASSERT(node->next->x > x0); // we ended up handling this in the caller for efficiency
|
||||
#endif
|
||||
|
||||
STBRP_ASSERT(node->x <= x0);
|
||||
|
||||
min_y = 0;
|
||||
waste_area = 0;
|
||||
visited_width = 0;
|
||||
while (node->x < x1) {
|
||||
if (node->y > min_y) {
|
||||
// raise min_y higher.
|
||||
// we've accounted for all waste up to min_y,
|
||||
// but we'll now add more waste for everything we've visted
|
||||
waste_area += visited_width * (node->y - min_y);
|
||||
min_y = node->y;
|
||||
// the first time through, visited_width might be reduced
|
||||
if (node->x < x0)
|
||||
visited_width += node->next->x - x0;
|
||||
else
|
||||
visited_width += node->next->x - node->x;
|
||||
} else {
|
||||
// add waste area
|
||||
int under_width = node->next->x - node->x;
|
||||
if (under_width + visited_width > width)
|
||||
under_width = width - visited_width;
|
||||
waste_area += under_width * (min_y - node->y);
|
||||
visited_width += under_width;
|
||||
}
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
*pwaste = waste_area;
|
||||
return min_y;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int x,y;
|
||||
stbrp_node **prev_link;
|
||||
} stbrp__findresult;
|
||||
|
||||
static stbrp__findresult stbrp__skyline_find_best_pos(stbrp_context *c, int width, int height)
|
||||
{
|
||||
int best_waste = (1<<30), best_x, best_y = (1 << 30);
|
||||
stbrp__findresult fr;
|
||||
stbrp_node **prev, *node, *tail, **best = NULL;
|
||||
|
||||
// align to multiple of c->align
|
||||
width = (width + c->align - 1);
|
||||
width -= width % c->align;
|
||||
STBRP_ASSERT(width % c->align == 0);
|
||||
|
||||
// if it can't possibly fit, bail immediately
|
||||
if (width > c->width || height > c->height) {
|
||||
fr.prev_link = NULL;
|
||||
fr.x = fr.y = 0;
|
||||
return fr;
|
||||
}
|
||||
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
while (node->x + width <= c->width) {
|
||||
int y,waste;
|
||||
y = stbrp__skyline_find_min_y(c, node, node->x, width, &waste);
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BL_sortHeight) { // actually just want to test BL
|
||||
// bottom left
|
||||
if (y < best_y) {
|
||||
best_y = y;
|
||||
best = prev;
|
||||
}
|
||||
} else {
|
||||
// best-fit
|
||||
if (y + height <= c->height) {
|
||||
// can only use it if it first vertically
|
||||
if (y < best_y || (y == best_y && waste < best_waste)) {
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
|
||||
best_x = (best == NULL) ? 0 : (*best)->x;
|
||||
|
||||
// if doing best-fit (BF), we also have to try aligning right edge to each node position
|
||||
//
|
||||
// e.g, if fitting
|
||||
//
|
||||
// ____________________
|
||||
// |____________________|
|
||||
//
|
||||
// into
|
||||
//
|
||||
// | |
|
||||
// | ____________|
|
||||
// |____________|
|
||||
//
|
||||
// then right-aligned reduces waste, but bottom-left BL is always chooses left-aligned
|
||||
//
|
||||
// This makes BF take about 2x the time
|
||||
|
||||
if (c->heuristic == STBRP_HEURISTIC_Skyline_BF_sortHeight) {
|
||||
tail = c->active_head;
|
||||
node = c->active_head;
|
||||
prev = &c->active_head;
|
||||
// find first node that's admissible
|
||||
while (tail->x < width)
|
||||
tail = tail->next;
|
||||
while (tail) {
|
||||
int xpos = tail->x - width;
|
||||
int y,waste;
|
||||
STBRP_ASSERT(xpos >= 0);
|
||||
// find the left position that matches this
|
||||
while (node->next->x <= xpos) {
|
||||
prev = &node->next;
|
||||
node = node->next;
|
||||
}
|
||||
STBRP_ASSERT(node->next->x > xpos && node->x <= xpos);
|
||||
y = stbrp__skyline_find_min_y(c, node, xpos, width, &waste);
|
||||
if (y + height <= c->height) {
|
||||
if (y <= best_y) {
|
||||
if (y < best_y || waste < best_waste || (waste==best_waste && xpos < best_x)) {
|
||||
best_x = xpos;
|
||||
STBRP_ASSERT(y <= best_y);
|
||||
best_y = y;
|
||||
best_waste = waste;
|
||||
best = prev;
|
||||
}
|
||||
}
|
||||
}
|
||||
tail = tail->next;
|
||||
}
|
||||
}
|
||||
|
||||
fr.prev_link = best;
|
||||
fr.x = best_x;
|
||||
fr.y = best_y;
|
||||
return fr;
|
||||
}
|
||||
|
||||
static stbrp__findresult stbrp__skyline_pack_rectangle(stbrp_context *context, int width, int height)
|
||||
{
|
||||
// find best position according to heuristic
|
||||
stbrp__findresult res = stbrp__skyline_find_best_pos(context, width, height);
|
||||
stbrp_node *node, *cur;
|
||||
|
||||
// bail if:
|
||||
// 1. it failed
|
||||
// 2. the best node doesn't fit (we don't always check this)
|
||||
// 3. we're out of memory
|
||||
if (res.prev_link == NULL || res.y + height > context->height || context->free_head == NULL) {
|
||||
res.prev_link = NULL;
|
||||
return res;
|
||||
}
|
||||
|
||||
// on success, create new node
|
||||
node = context->free_head;
|
||||
node->x = (stbrp_coord) res.x;
|
||||
node->y = (stbrp_coord) (res.y + height);
|
||||
|
||||
context->free_head = node->next;
|
||||
|
||||
// insert the new node into the right starting point, and
|
||||
// let 'cur' point to the remaining nodes needing to be
|
||||
// stiched back in
|
||||
|
||||
cur = *res.prev_link;
|
||||
if (cur->x < res.x) {
|
||||
// preserve the existing one, so start testing with the next one
|
||||
stbrp_node *next = cur->next;
|
||||
cur->next = node;
|
||||
cur = next;
|
||||
} else {
|
||||
*res.prev_link = node;
|
||||
}
|
||||
|
||||
// from here, traverse cur and free the nodes, until we get to one
|
||||
// that shouldn't be freed
|
||||
while (cur->next && cur->next->x <= res.x + width) {
|
||||
stbrp_node *next = cur->next;
|
||||
// move the current node to the free list
|
||||
cur->next = context->free_head;
|
||||
context->free_head = cur;
|
||||
cur = next;
|
||||
}
|
||||
|
||||
// stitch the list back in
|
||||
node->next = cur;
|
||||
|
||||
if (cur->x < res.x + width)
|
||||
cur->x = (stbrp_coord) (res.x + width);
|
||||
|
||||
#ifdef _DEBUG
|
||||
cur = context->active_head;
|
||||
while (cur->x < context->width) {
|
||||
STBRP_ASSERT(cur->x < cur->next->x);
|
||||
cur = cur->next;
|
||||
}
|
||||
STBRP_ASSERT(cur->next == NULL);
|
||||
|
||||
{
|
||||
int count=0;
|
||||
cur = context->active_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
cur = context->free_head;
|
||||
while (cur) {
|
||||
cur = cur->next;
|
||||
++count;
|
||||
}
|
||||
STBRP_ASSERT(count == context->num_nodes+2);
|
||||
}
|
||||
#endif
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_height_compare(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
if (p->h > q->h)
|
||||
return -1;
|
||||
if (p->h < q->h)
|
||||
return 1;
|
||||
return (p->w > q->w) ? -1 : (p->w < q->w);
|
||||
}
|
||||
|
||||
static int STBRP__CDECL rect_original_order(const void *a, const void *b)
|
||||
{
|
||||
const stbrp_rect *p = (const stbrp_rect *) a;
|
||||
const stbrp_rect *q = (const stbrp_rect *) b;
|
||||
return (p->was_packed < q->was_packed) ? -1 : (p->was_packed > q->was_packed);
|
||||
}
|
||||
|
||||
STBRP_DEF int stbrp_pack_rects(stbrp_context *context, stbrp_rect *rects, int num_rects)
|
||||
{
|
||||
int i, all_rects_packed = 1;
|
||||
|
||||
// we use the 'was_packed' field internally to allow sorting/unsorting
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = i;
|
||||
}
|
||||
|
||||
// sort according to heuristic
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_height_compare);
|
||||
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
if (rects[i].w == 0 || rects[i].h == 0) {
|
||||
rects[i].x = rects[i].y = 0; // empty rect needs no space
|
||||
} else {
|
||||
stbrp__findresult fr = stbrp__skyline_pack_rectangle(context, rects[i].w, rects[i].h);
|
||||
if (fr.prev_link) {
|
||||
rects[i].x = (stbrp_coord) fr.x;
|
||||
rects[i].y = (stbrp_coord) fr.y;
|
||||
} else {
|
||||
rects[i].x = rects[i].y = STBRP__MAXVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// unsort
|
||||
STBRP_SORT(rects, num_rects, sizeof(rects[0]), rect_original_order);
|
||||
|
||||
// set was_packed flags and all_rects_packed status
|
||||
for (i=0; i < num_rects; ++i) {
|
||||
rects[i].was_packed = !(rects[i].x == STBRP__MAXVAL && rects[i].y == STBRP__MAXVAL);
|
||||
if (!rects[i].was_packed)
|
||||
all_rects_packed = 0;
|
||||
}
|
||||
|
||||
// return the all_rects_packed status
|
||||
return all_rects_packed;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
------------------------------------------------------------------------------
|
||||
This software is available under 2 licenses -- choose whichever you prefer.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE A - MIT License
|
||||
Copyright (c) 2017 Sean Barrett
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
ALTERNATIVE B - Public Domain (www.unlicense.org)
|
||||
This is free and unencumbered software released into the public domain.
|
||||
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
||||
software, either in source code form or as a compiled binary, for any purpose,
|
||||
commercial or non-commercial, and by any means.
|
||||
In jurisdictions that recognize copyright laws, the author or authors of this
|
||||
software dedicate any and all copyright interest in the software to the public
|
||||
domain. We make this dedication for the benefit of the public at large and to
|
||||
the detriment of our heirs and successors. We intend this dedication to be an
|
||||
overt act of relinquishment in perpetuity of all present and future rights to
|
||||
this software under copyright law.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
||||
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
------------------------------------------------------------------------------
|
||||
*/
|
||||
Loading…
x
Reference in New Issue
Block a user