mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-02 12:48:14 +00:00
Merge pull request #1168 from odin-lang/vendor-stb
`vendor:stb` - Add stb libraries (image+image_write, rect_pack, truetype)
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
setlocal EnableDelayedExpansion
|
setlocal EnableDelayedExpansion
|
||||||
|
|
||||||
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
for /f "usebackq tokens=1,2 delims=,=- " %%i in (`wmic os get LocalDateTime /value`) do @if %%i==LocalDateTime (
|
||||||
set CURR_DATE_TIME=%%j
|
set CURR_DATE_TIME=%%j
|
||||||
)
|
)
|
||||||
|
|
||||||
set curr_year=%CURR_DATE_TIME:~0,4%
|
set curr_year=%CURR_DATE_TIME:~0,4%
|
||||||
@@ -23,9 +23,9 @@ if "%1" == "1" (
|
|||||||
|
|
||||||
:: Normal = 0, CI Nightly = 1
|
:: Normal = 0, CI Nightly = 1
|
||||||
if "%2" == "1" (
|
if "%2" == "1" (
|
||||||
set nightly=1
|
set nightly=1
|
||||||
) else (
|
) else (
|
||||||
set nightly=0
|
set nightly=0
|
||||||
)
|
)
|
||||||
|
|
||||||
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
set odin_version_raw="dev-%curr_year%-%curr_month%"
|
||||||
@@ -69,8 +69,11 @@ del *.pdb > NUL 2> NUL
|
|||||||
del *.ilk > NUL 2> NUL
|
del *.ilk > NUL 2> NUL
|
||||||
|
|
||||||
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
|
cl %compiler_settings% "src\main.cpp" "src\libtommath.cpp" /link %linker_settings% -OUT:%exe_name%
|
||||||
|
|
||||||
if %errorlevel% neq 0 goto end_of_build
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
|
|
||||||
|
call build_vendor.bat
|
||||||
|
if %errorlevel% neq 0 goto end_of_build
|
||||||
|
|
||||||
if %release_mode% EQU 0 odin run examples/demo
|
if %release_mode% EQU 0 odin run examples/demo
|
||||||
|
|
||||||
del *.obj > NUL 2> NUL
|
del *.obj > NUL 2> NUL
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
setlocal EnableDelayedExpansion
|
||||||
|
|
||||||
|
if not exist "vendor\stb\lib\*.lib" (
|
||||||
|
rem build the .lib fiels already exist
|
||||||
|
pushd vendor\stb\src
|
||||||
|
call build.bat
|
||||||
|
popd
|
||||||
|
)
|
||||||
Vendored
+107
@@ -0,0 +1,107 @@
|
|||||||
|
package stb_image
|
||||||
|
|
||||||
|
import c "core:c/libc"
|
||||||
|
|
||||||
|
#assert(size_of(c.int) == size_of(b32))
|
||||||
|
|
||||||
|
when ODIN_OS == "windows" { foreign import stbi "../lib/stb_image.lib" }
|
||||||
|
when ODIN_OS == "linux" { foreign import stbi "../lib/stb_image.a" }
|
||||||
|
|
||||||
|
#assert(size_of(b32) == size_of(c.int));
|
||||||
|
|
||||||
|
//
|
||||||
|
// load image by filename, open file, or memory buffer
|
||||||
|
//
|
||||||
|
Io_Callbacks :: struct {
|
||||||
|
read: proc "c" (user: rawptr, data: [^]byte, size: c.int) -> c.int, // fill 'data' with 'size' u8s. return number of u8s actually read
|
||||||
|
skip: proc "c" (user: rawptr, n: c.int), // skip the next 'n' u8s, or 'unget' the last -n u8s if negative
|
||||||
|
eof: proc "c" (user: rawptr) -> c.int, // returns nonzero if we are at end of file/data
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbi_")
|
||||||
|
foreign stbi {
|
||||||
|
////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 8-bits-per-channel interface
|
||||||
|
//
|
||||||
|
load :: proc(filename: cstring, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]byte ---
|
||||||
|
load_from_file :: proc(f: ^c.FILE, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]byte ---
|
||||||
|
load_from_memory :: proc(buffer: [^]byte, len: c.int, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]byte ---
|
||||||
|
load_from_callbacks :: proc(clbk: ^Io_Callbacks, user: rawptr, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]byte ---
|
||||||
|
|
||||||
|
load_gif_from_memory :: proc(buffer: [^]byte, len: c.int, delays: ^[^]c.int, x, y, z, comp: ^c.int, req_comp: c.int) -> [^]byte ---
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
//
|
||||||
|
// 16-bits-per-channel interface
|
||||||
|
//
|
||||||
|
load_16 :: proc(filename: cstring, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]u16 ---
|
||||||
|
load_16_from_file :: proc(f: ^c.FILE, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]u16 ---
|
||||||
|
load_16_from_memory :: proc(buffer: [^]byte, len: c.int, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]u16 ---
|
||||||
|
load_16_from_callbacks :: proc(clbk: ^Io_Callbacks, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]u16 ---
|
||||||
|
|
||||||
|
////////////////////////////////////
|
||||||
|
//
|
||||||
|
// float-per-channel interface
|
||||||
|
//
|
||||||
|
loadf :: proc(filename: cstring, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]f32 ---
|
||||||
|
loadf_from_file :: proc(f: ^c.FILE, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]f32 ---
|
||||||
|
loadf_from_memory :: proc(buffer: [^]byte, len: c.int, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]f32 ---
|
||||||
|
loadf_from_callbacks :: proc(clbk: ^Io_Callbacks, user: rawptr, x, y, channels_in_file: ^c.int, desired_channels: c.int) -> [^]f32 ---
|
||||||
|
|
||||||
|
hdr_to_ldr_gamma :: proc(gamma: f32) ---
|
||||||
|
hdr_to_ldr_scale :: proc(scale: f32) ---
|
||||||
|
|
||||||
|
ldr_to_hdr_gamma :: proc(gamma: f32) ---
|
||||||
|
ldr_to_hdr_scale :: proc(scale: f32) ---
|
||||||
|
|
||||||
|
is_hdr_from_callbacks :: proc(clbk: ^Io_Callbacks, user: rawptr) -> c.int ---
|
||||||
|
is_hdr_from_memory :: proc(buffer: [^]byte, len: c.int) -> c.int ---
|
||||||
|
|
||||||
|
is_hdr :: proc(filename: cstring) -> c.int ---
|
||||||
|
is_hdr_from_file :: proc(f: ^c.FILE) -> c.int ---
|
||||||
|
|
||||||
|
// get a VERY brief reason for failure
|
||||||
|
// NOT THREADSAFE
|
||||||
|
failure_reason :: proc() -> cstring ---
|
||||||
|
|
||||||
|
// free the loaded image -- this is just free()
|
||||||
|
image_free :: proc(retval_from_load: rawptr) ---
|
||||||
|
|
||||||
|
// get image dimensions & components without fully decoding
|
||||||
|
info :: proc(filename: cstring, x, y, comp: ^c.int) -> c.int ---
|
||||||
|
info_from_file :: proc(f: ^c.FILE, x, y, comp: ^c.int) -> c.int ---
|
||||||
|
info_from_memory :: proc(buffer: [^]byte, len: c.int, x, y, comp: ^c.int) -> c.int ---
|
||||||
|
info_from_callbacks :: proc(clbk: ^Io_Callbacks, user: rawptr, x, y, comp: ^c.int) -> c.int ---
|
||||||
|
|
||||||
|
is_16_bit :: proc(filename: cstring) -> b32 ---
|
||||||
|
is_16_bit_from_file :: proc(f: ^c.FILE) -> b32 ---
|
||||||
|
|
||||||
|
// for image formats that explicitly notate that they have premultiplied alpha,
|
||||||
|
// we just return the colors as stored in the file. set this flag to force
|
||||||
|
// unpremultiplication. results are undefined if the unpremultiply overflow.
|
||||||
|
set_unpremultiply_on_load :: proc (flag_true_if_should_unpremultiply: c.int) ---
|
||||||
|
|
||||||
|
// indicate whether we should process iphone images back to canonical format,
|
||||||
|
// or just pass them through "as-is"
|
||||||
|
convert_iphone_png_to_rgb :: proc(flag_true_if_should_convert: c.int) ---
|
||||||
|
|
||||||
|
// flip the image vertically, so the first pixel in the output array is the bottom left
|
||||||
|
set_flip_vertically_on_load :: proc(flag_true_if_should_flip: c.int) ---
|
||||||
|
|
||||||
|
// as above, but only applies to images loaded on the thread that calls the function
|
||||||
|
// this function is only available if your compiler supports thread-local variables;
|
||||||
|
// calling it will fail to link if your compiler doesn't
|
||||||
|
set_unpremultiply_on_load_thread :: proc(flag_true_if_should_unpremultiply: b32) ---
|
||||||
|
convert_iphone_png_to_rgb_thread :: proc(flag_true_if_should_convert: b32) ---
|
||||||
|
set_flip_vertically_on_load_thread :: proc(flag_true_if_should_flip: b32) ---
|
||||||
|
|
||||||
|
// ZLIB client - used by PNG, available for other purposes
|
||||||
|
zlib_decode_malloc_guesssize :: proc(buffer: [^]byte, len: c.int, initial_size: c.int, outlen: ^c.int) -> [^]byte ---
|
||||||
|
zlib_decode_malloc_guesssize_headerflag :: proc(buffer: [^]byte, len: c.int, initial_size: c.int, outlen: ^c.int, parse_header: b32) -> [^]byte ---
|
||||||
|
zlib_decode_malloc :: proc(buffer: [^]byte, len: c.int, outlen: ^c.int) -> [^]byte ---
|
||||||
|
zlib_decode_buffer :: proc(obuffer: [^]byte, olen: c.int, ibuffer: [^]byte, ilen: c.int) -> c.int ---
|
||||||
|
|
||||||
|
zlib_decode_noheader_malloc :: proc(buffer: [^]byte, len: c.int, outlen: ^c.int) -> [^]byte ---
|
||||||
|
zlib_decode_noheader_buffer :: proc(obuffer: [^]byte, olen: c.int, ibuffer: [^]byte, ilen: c.int) -> c.int ---
|
||||||
|
}
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package stb_image
|
||||||
|
|
||||||
|
import c "core:c/libc"
|
||||||
|
|
||||||
|
when ODIN_OS == "windows" { foreign import stbiw "../lib/stb_image_write.lib" }
|
||||||
|
when ODIN_OS == "linux" { foreign import stbiw "../lib/stb_image_write.a" }
|
||||||
|
|
||||||
|
|
||||||
|
write_func :: proc "c" (ctx: rawptr, data: rawptr, size: c.int);
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbi_")
|
||||||
|
foreign stbiw {
|
||||||
|
write_png :: proc(filename: cstring, w, h, comp: c.int, data: rawptr, stride_in_bytes: c.int) -> c.int ---
|
||||||
|
write_bmp :: proc(filename: cstring, w, h, comp: c.int, data: rawptr) -> c.int ---
|
||||||
|
write_tga :: proc(filename: cstring, w, h, comp: c.int, data: rawptr) -> c.int ---
|
||||||
|
write_hdr :: proc(filename: cstring, w, h, comp: c.int, data: [^]f32) -> c.int ---
|
||||||
|
write_jpg :: proc(filename: cstring, w, h, comp: c.int, data: rawptr, quality: c.int /*0..=100*/) -> c.int ---
|
||||||
|
|
||||||
|
write_png_to_func :: proc(func: write_func, ctx: rawptr, w, h, comp: c.int, data: rawptr, stride_in_bytes: c.int) -> c.int ---
|
||||||
|
write_bmp_to_func :: proc(func: write_func, ctx: rawptr, w, h, comp: c.int, data: rawptr) -> c.int ---
|
||||||
|
write_tga_to_func :: proc(func: write_func, ctx: rawptr, w, h, comp: c.int, data: rawptr) -> c.int ---
|
||||||
|
write_hdr_to_func :: proc(func: write_func, ctx: rawptr, w, h, comp: c.int, data: [^]f32) -> c.int ---
|
||||||
|
write_jpg_to_func :: proc(func: write_func, ctx: rawptr, x, y, comp: c.int, data: rawptr, quality: c.int /*0..=100*/) -> c.int ---
|
||||||
|
|
||||||
|
flip_vertically_on_write :: proc(flip_boolean: b32) ---
|
||||||
|
}
|
||||||
Vendored
+112
@@ -0,0 +1,112 @@
|
|||||||
|
package stb_rect_pack
|
||||||
|
|
||||||
|
import c "core:c/libc"
|
||||||
|
|
||||||
|
#assert(size_of(b32) == size_of(c.int))
|
||||||
|
|
||||||
|
when ODIN_OS == "windows" { foreign import lib "../lib/stb_rect_pack.lib" }
|
||||||
|
when ODIN_OS == "linux" { foreign import lib "../lib/stb_rect_pack.a" }
|
||||||
|
|
||||||
|
Coord :: distinct c.int
|
||||||
|
_MAXVAL :: max(Coord)
|
||||||
|
|
||||||
|
Rect :: struct {
|
||||||
|
// reserved for your use:
|
||||||
|
id: c.int,
|
||||||
|
|
||||||
|
// input:
|
||||||
|
w, h: Coord,
|
||||||
|
|
||||||
|
// output:
|
||||||
|
x, y: Coord,
|
||||||
|
was_packed: b32, // non-zero if valid packing
|
||||||
|
}
|
||||||
|
|
||||||
|
Heuristic :: enum c.int {
|
||||||
|
Skyline_default = 0,
|
||||||
|
Skyline_BL_sortHeight = Skyline_default,
|
||||||
|
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
|
||||||
|
|
||||||
|
Node :: struct {
|
||||||
|
x, y: Coord,
|
||||||
|
next: ^Node,
|
||||||
|
}
|
||||||
|
|
||||||
|
Context :: struct {
|
||||||
|
width: c.int,
|
||||||
|
height: c.int,
|
||||||
|
align: c.int,
|
||||||
|
init_mode: c.int,
|
||||||
|
heuristic: Heuristic,
|
||||||
|
num_nodes: c.int,
|
||||||
|
active_head: ^Node,
|
||||||
|
free_head: ^Node,
|
||||||
|
extra: [2]Node, // we allocate two extra nodes so optimal user-node-count is 'width' not 'width+2'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbrp_")
|
||||||
|
foreign lib {
|
||||||
|
// Assign packed locations to rectangles. The rectangles are of type
|
||||||
|
// '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 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.
|
||||||
|
pack_rects :: proc(ctx: ^Context, rects: [^]Rect, num_rects: c.int) -> c.int ---
|
||||||
|
|
||||||
|
|
||||||
|
// 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 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 setup_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.
|
||||||
|
init_target :: proc(ctx: ^Context, width, height: c.int, nodes: [^]Node, num_nodes: c.int) ---
|
||||||
|
|
||||||
|
// 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).
|
||||||
|
setup_allow_out_of_mem :: proc(ctx: ^Context, allow_out_of_mem: b32) ---
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
setup_heuristic :: proc(ctx: ^Context, heuristic: Heuristic) ---
|
||||||
|
}
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
all:
|
||||||
|
mkdir -p ../lib
|
||||||
|
gcc -c -O2 -march=native -Os -fPIC stb_image.c stb_image_write.c stb_truetype.c stb_rect_pack.c
|
||||||
|
ar rcs ../lib/stb_image.a stb_image.o
|
||||||
|
ar rcs ../lib/stb_image_write.a stb_image_write.o
|
||||||
|
ar rcs ../lib/stb_truetype.a stb_truetype.o
|
||||||
|
ar rcs ../lib/stb_rect_pack.a stb_rect_pack.o
|
||||||
|
#gcc -fPIC -shared -Wl,-soname=stb_image.so -o ../lib/stb_image.so stb_image.o
|
||||||
|
#gcc -fPIC -shared -Wl,-soname=stb_image_write.so -o ../lib/stb_image_write.so stb_image_write.o
|
||||||
|
#gcc -fPIC -shared -Wl,-soname=stb_truetype.so -o ../lib/stb_truetype.so stb_image_truetype.o
|
||||||
|
#gcc -fPIC -shared -Wl,-soname=stb_rect_pack.so -o ../lib/stb_rect_pack.so stb_rect_packl.o
|
||||||
|
rm *.o
|
||||||
Vendored
+11
@@ -0,0 +1,11 @@
|
|||||||
|
@echo off
|
||||||
|
|
||||||
|
if not exist "..\lib" mkdir ..\lib
|
||||||
|
|
||||||
|
cl -nologo -MT -TC -O2 -c stb_image.c stb_image_write.c stb_truetype.c stb_rect_pack.c
|
||||||
|
lib -nologo stb_image.obj -out:..\lib\stb_image.lib
|
||||||
|
lib -nologo stb_image_write.obj -out:..\lib\stb_image_write.lib
|
||||||
|
lib -nologo stb_truetype.obj -out:..\lib\stb_truetype.lib
|
||||||
|
lib -nologo stb_rect_pack.obj -out:..\lib\stb_rect_pack.lib
|
||||||
|
|
||||||
|
del stb_image.obj stb_image_write.obj stb_truetype.obj stb_rect_pack.obj
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
Vendored
+7897
File diff suppressed because it is too large
Load Diff
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
|
#include "stb_image_write.h"
|
||||||
Vendored
+1724
File diff suppressed because it is too large
Load Diff
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
#define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#include "stb_rect_pack.h"
|
||||||
Vendored
+623
@@ -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.
|
||||||
|
------------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
Vendored
+5
@@ -0,0 +1,5 @@
|
|||||||
|
#define STB_RECT_PACK_IMPLEMENTATION
|
||||||
|
#include "stb_rect_pack.h"
|
||||||
|
|
||||||
|
#define STB_TRUETYPE_IMPLEMENTATION
|
||||||
|
#include "stb_truetype.h"
|
||||||
Vendored
+5077
File diff suppressed because it is too large
Load Diff
+609
@@ -0,0 +1,609 @@
|
|||||||
|
package stb_truetype
|
||||||
|
|
||||||
|
import c "core:c"
|
||||||
|
import stbrp "vendor:stb/rect_pack"
|
||||||
|
|
||||||
|
when ODIN_OS == "windows" { foreign import stbtt "../lib/stb_truetype.lib" }
|
||||||
|
when ODIN_OS == "linux" { foreign import stbtt "../lib/stb_truetype.a" }
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
////
|
||||||
|
//// INTERFACE
|
||||||
|
////
|
||||||
|
////
|
||||||
|
|
||||||
|
#assert(size_of(c.int) == size_of(rune))
|
||||||
|
#assert(size_of(c.int) == size_of(b32))
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// TEXTURE BAKING API
|
||||||
|
//
|
||||||
|
// If you use this API, you only have to call two functions ever.
|
||||||
|
//
|
||||||
|
|
||||||
|
bakedchar :: struct {
|
||||||
|
x0, y0, x1, y1: u16, // coordinates of bbox in bitmap
|
||||||
|
xoff, yoff, xadvance: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
aligned_quad :: struct {
|
||||||
|
x0, y0, s0, t0: f32, // top-left
|
||||||
|
x1, y1, s1, t1: f32, // bottom-right
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// bindings
|
||||||
|
@(default_calling_convention="c", link_prefix="stbtt_")
|
||||||
|
foreign stbtt {
|
||||||
|
// if return is positive, the first unused row of the bitmap
|
||||||
|
// if return is negative, returns the negative of the number of characters that fit
|
||||||
|
// if return is 0, no characters fit and no rows were used
|
||||||
|
// This uses a very crappy packing.
|
||||||
|
BakeFontBitmap :: proc(data: [^]byte, offset: c.int, // font location (use offset=0 for plain .ttf)
|
||||||
|
pixel_height: f32, // height of font in pixels
|
||||||
|
pixels: [^]byte, pw, ph: c.int, // bitmap to be filled in
|
||||||
|
first_char, num_chars: c.int, // characters to bake
|
||||||
|
chardata: [^]bakedchar, // you allocate this, it's num_chars long
|
||||||
|
) -> c.int ---
|
||||||
|
|
||||||
|
// Call GetBakedQuad with char_index = 'character - first_char', and it
|
||||||
|
// creates the quad you need to draw and advances the current position.
|
||||||
|
//
|
||||||
|
// The coordinate system used assumes y increases downwards.
|
||||||
|
//
|
||||||
|
// Characters will extend both above and below the current position;
|
||||||
|
// see discussion of "BASELINE" above.
|
||||||
|
//
|
||||||
|
// It's inefficient; you might want to c&p it and optimize it.
|
||||||
|
GetBakedQuad :: proc(chardata: ^bakedchar, pw, ph: c.int, // same data as above
|
||||||
|
char_index: c.int, // character to display
|
||||||
|
xpos, ypos: ^f32, // pointers to current position in screen pixel space
|
||||||
|
q: ^aligned_quad, // output: quad to draw
|
||||||
|
opengl_fillrule: b32, // true if opengl fill rule; false if DX9 or earlier
|
||||||
|
) ---
|
||||||
|
|
||||||
|
// Query the font vertical metrics without having to create a font first.
|
||||||
|
GetScaledFontVMetrics :: proc(fontdata: [^]byte, index: c.int, size: f32, ascent, descent, lineGap: ^f32) ---
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// NEW TEXTURE BAKING API
|
||||||
|
//
|
||||||
|
// This provides options for packing multiple fonts into one atlas, not
|
||||||
|
// perfectly but better than nothing.
|
||||||
|
|
||||||
|
packedchar :: struct {
|
||||||
|
x0, y0, x1, y1: u16,
|
||||||
|
xoff, yoff, xadvance: f32,
|
||||||
|
xoff2, yoff2: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pack_range :: struct {
|
||||||
|
font_size: f32,
|
||||||
|
first_unicode_codepoint_in_range: c.int,
|
||||||
|
array_of_unicode_codepoints: [^]rune,
|
||||||
|
num_chars: c.int,
|
||||||
|
chardata_for_range: ^packedchar,
|
||||||
|
_, _: u8, // used internally to store oversample info
|
||||||
|
}
|
||||||
|
|
||||||
|
pack_context :: struct {
|
||||||
|
user_allocator_context, pack_info: rawptr,
|
||||||
|
width, height, stride_in_bytes, padding: c.int,
|
||||||
|
h_oversample, v_oversample: u32,
|
||||||
|
pixels: [^]byte,
|
||||||
|
nodes: rawptr,
|
||||||
|
}
|
||||||
|
|
||||||
|
POINT_SIZE :: #force_inline proc(x: $T) -> T { return -x } // @NOTE: this was a macro
|
||||||
|
|
||||||
|
// bindings
|
||||||
|
@(default_calling_convention="c", link_prefix="stbtt_")
|
||||||
|
foreign stbtt {
|
||||||
|
// Initializes a packing context stored in the passed-in stbtt_pack_context.
|
||||||
|
// Future calls using this context will pack characters into the bitmap passed
|
||||||
|
// in here: a 1-channel bitmap that is width * height. stride_in_bytes is
|
||||||
|
// the distance from one row to the next (or 0 to mean they are packed tightly
|
||||||
|
// together). "padding" is the amount of padding to leave between each
|
||||||
|
// character (normally you want '1' for bitmaps you'll use as textures with
|
||||||
|
// bilinear filtering).
|
||||||
|
//
|
||||||
|
// Returns 0 on failure, 1 on success.
|
||||||
|
PackBegin :: proc(spc: ^pack_context, pixels: [^]byte, width, height, stride_in_bytes, padding: c.int, alloc_context: rawptr) -> c.int ---
|
||||||
|
|
||||||
|
// Cleans up the packing context and frees all memory.
|
||||||
|
PackEnd :: proc(spc: ^pack_context) ---
|
||||||
|
|
||||||
|
// Creates character bitmaps from the font_index'th font found in fontdata (use
|
||||||
|
// font_index=0 if you don't know what that is). It creates num_chars_in_range
|
||||||
|
// bitmaps for characters with unicode values starting at first_unicode_char_in_range
|
||||||
|
// and increasing. Data for how to render them is stored in chardata_for_range;
|
||||||
|
// pass these to stbtt_GetPackedQuad to get back renderable quads.
|
||||||
|
//
|
||||||
|
// font_size is the full height of the character from ascender to descender,
|
||||||
|
// as computed by stbtt_ScaleForPixelHeight. To use a point size as computed
|
||||||
|
// by stbtt_ScaleForMappingEmToPixels, wrap the point size in POINT_SIZE()
|
||||||
|
// and pass that result as 'font_size':
|
||||||
|
// ..., 20 , ... // font max minus min y is 20 pixels tall
|
||||||
|
// ..., POINT_SIZE(20), ... // 'M' is 20 pixels tall
|
||||||
|
PackFontRange :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, font_size: f32, first_unicode_char_in_range, num_chars_in_range: c.int, chardata_for_range: ^packedchar) -> c.int ---
|
||||||
|
|
||||||
|
// Creates character bitmaps from multiple ranges of characters stored in
|
||||||
|
// ranges. This will usually create a better-packed bitmap than multiple
|
||||||
|
// calls to stbtt_PackFontRange. Note that you can call this multiple
|
||||||
|
// times within a single PackBegin/PackEnd.
|
||||||
|
PackFontRanges :: proc(spc: ^pack_context, fontdata: [^]byte, font_index: c.int, ranges: [^]pack_range, num_ranges: c.int) -> c.int ---
|
||||||
|
|
||||||
|
// Oversampling a font increases the quality by allowing higher-quality subpixel
|
||||||
|
// positioning, and is especially valuable at smaller text sizes.
|
||||||
|
//
|
||||||
|
// This function sets the amount of oversampling for all following calls to
|
||||||
|
// stbtt_PackFontRange(s) or stbtt_PackFontRangesGatherRects for a given
|
||||||
|
// pack context. The default (no oversampling) is achieved by h_oversample=1
|
||||||
|
// and v_oversample=1. The total number of pixels required is
|
||||||
|
// h_oversample*v_oversample larger than the default; for example, 2x2
|
||||||
|
// oversampling requires 4x the storage of 1x1. For best results, render
|
||||||
|
// oversampled textures with bilinear filtering. Look at the readme in
|
||||||
|
// stb/tests/oversample for information about oversampled fonts
|
||||||
|
//
|
||||||
|
// To use with PackFontRangesGather etc., you must set it before calls
|
||||||
|
// call to PackFontRangesGatherRects.
|
||||||
|
PackSetOversampling :: proc(spc: ^pack_context, h_oversample, v_oversample: c.uint) ---
|
||||||
|
|
||||||
|
// If skip != false, this tells stb_truetype to skip any codepoints for which
|
||||||
|
// there is no corresponding glyph. If skip=false, which is the default, then
|
||||||
|
// codepoints without a glyph recived the font's "missing character" glyph,
|
||||||
|
// typically an empty box by convention.
|
||||||
|
PackSetSkipMissingCodepoints :: proc(spc: ^pack_context, skip: b32) ---
|
||||||
|
|
||||||
|
GetPackedQuad :: proc(chardata: ^packedchar, pw, ph: c.int, // same data as above
|
||||||
|
char_index: c.int, // character to display
|
||||||
|
xpos, ypos: ^f32, // pointers to current position in screen pixel space
|
||||||
|
q: ^aligned_quad, // output: quad to draw
|
||||||
|
align_to_integer: b32,
|
||||||
|
) ---
|
||||||
|
|
||||||
|
// Calling these functions in sequence is roughly equivalent to calling
|
||||||
|
// stbtt_PackFontRanges(). If you more control over the packing of multiple
|
||||||
|
// fonts, or if you want to pack custom data into a font texture, take a look
|
||||||
|
// at the source to of stbtt_PackFontRanges() and create a custom version
|
||||||
|
// using these functions, e.g. call GatherRects multiple times,
|
||||||
|
// building up a single array of rects, then call PackRects once,
|
||||||
|
// then call RenderIntoRects repeatedly. This may result in a
|
||||||
|
// better packing than calling PackFontRanges multiple times
|
||||||
|
// (or it may not).
|
||||||
|
PackFontRangesGatherRects :: proc(spc: ^pack_context, info: ^fontinfo, ranges: ^pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> c.int ---
|
||||||
|
PackFontRangesPackRects :: proc(spc: ^pack_context, rects: [^]stbrp.Rect, num_rects: c.int) ---
|
||||||
|
PackFontRangesRenderIntoRects :: proc(spc: ^pack_context, info: ^fontinfo, ranges: ^pack_range, num_ranges: c.int, rects: [^]stbrp.Rect) -> c.int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// FONT LOADING
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
fontinfo :: struct {
|
||||||
|
userdata: rawptr,
|
||||||
|
data: [^]byte,
|
||||||
|
fontstart: c.int,
|
||||||
|
|
||||||
|
numGlyphs: c.int,
|
||||||
|
|
||||||
|
loca, head, glyf, hhea, hmtx, kern: c.int,
|
||||||
|
index_map: c.int,
|
||||||
|
indexToLocFormat: c.int,
|
||||||
|
|
||||||
|
cff: _buf,
|
||||||
|
charstrings: _buf,
|
||||||
|
gsubrs: _buf,
|
||||||
|
subrs: _buf,
|
||||||
|
fontdicts: _buf,
|
||||||
|
fdselect: _buf,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbtt_")
|
||||||
|
foreign stbtt {
|
||||||
|
// Given an offset into the file that defines a font, this function builds
|
||||||
|
// the necessary cached info for the rest of the system. You must allocate
|
||||||
|
// the stbtt_fontinfo yourself, and stbtt_InitFont will fill it out. You don't
|
||||||
|
// need to do anything special to free it, because the contents are pure
|
||||||
|
// value data with no additional data structures. Returns 0 on failure.
|
||||||
|
InitFont :: proc(info: ^fontinfo, data: [^]byte, offset: c.int) -> b32 ---
|
||||||
|
|
||||||
|
// This function will determine the number of fonts in a font file. TrueType
|
||||||
|
// collection (.ttc) files may contain multiple fonts, while TrueType font
|
||||||
|
// (.ttf) files only contain one font. The number of fonts can be used for
|
||||||
|
// indexing with the previous function where the index is between zero and one
|
||||||
|
// less than the total fonts. If an error occurs, -1 is returned.
|
||||||
|
GetNumberOfFonts :: proc(data: [^]byte) -> b32 ---
|
||||||
|
|
||||||
|
// Each .ttf/.ttc file may have more than one font. Each font has a sequential
|
||||||
|
// index number starting from 0. Call this function to get the font offset for
|
||||||
|
// a given index; it returns -1 if the index is out of range. A regular .ttf
|
||||||
|
// file will only define one font and it always be at offset 0, so it will
|
||||||
|
// return '0' for index 0, and -1 for all other indices.
|
||||||
|
GetFontOffsetForIndex :: proc(data: [^]byte, index: c.int) -> c.int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// CHARACTER TO GLYPH-INDEX CONVERSION
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbtt_")
|
||||||
|
foreign stbtt {
|
||||||
|
// If you're going to perform multiple operations on the same character
|
||||||
|
// and you want a speed-up, call this function with the character you're
|
||||||
|
// going to process, then use glyph-based functions instead of the
|
||||||
|
// codepoint-based functions.
|
||||||
|
// Returns 0 if the character codepoint is not defined in the font.
|
||||||
|
FindGlyphIndex :: proc(info: ^fontinfo, unicode_codepoint: rune) -> c.int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// CHARACTER PROPERTIES
|
||||||
|
//
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbtt_")
|
||||||
|
foreign stbtt {
|
||||||
|
// computes a scale factor to produce a font whose "height" is 'pixels' tall.
|
||||||
|
// Height is measured as the distance from the highest ascender to the lowest
|
||||||
|
// descender; in other words, it's equivalent to calling stbtt_GetFontVMetrics
|
||||||
|
// and computing:
|
||||||
|
// scale = pixels / (ascent - descent)
|
||||||
|
// so if you prefer to measure height by the ascent only, use a similar calculation.
|
||||||
|
ScaleForPixelHeight :: proc(info: ^fontinfo, pixels: f32) -> f32 ---
|
||||||
|
|
||||||
|
// computes a scale factor to produce a font whose EM size is mapped to
|
||||||
|
// 'pixels' tall. This is probably what traditional APIs compute, but
|
||||||
|
// I'm not positive.
|
||||||
|
ScaleForMappingEmToPixels :: proc(info: ^fontinfo, pixels: f32) -> f32 ---
|
||||||
|
|
||||||
|
// ascent is the coordinate above the baseline the font extends; descent
|
||||||
|
// is the coordinate below the baseline the font extends (i.e. it is typically negative)
|
||||||
|
// lineGap is the spacing between one row's descent and the next row's ascent...
|
||||||
|
// so you should advance the vertical position by "*ascent - *descent + *lineGap"
|
||||||
|
// these are expressed in unscaled coordinates, so you must multiply by
|
||||||
|
// the scale factor for a given size
|
||||||
|
GetFontVMetrics :: proc(info: ^fontinfo, ascent, descent, lineGap: ^c.int) ---
|
||||||
|
|
||||||
|
// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2
|
||||||
|
// table (specific to MS/Windows TTF files).
|
||||||
|
//
|
||||||
|
// Returns 1 on success (table present), 0 on failure.
|
||||||
|
GetFontVMetricsOS2 :: proc(info: ^fontinfo, typoAscent, typoDescent, typoLineGap: ^c.int) -> b32 ---
|
||||||
|
|
||||||
|
// the bounding box around all possible characters
|
||||||
|
GetFontBoundingBox :: proc(info: ^fontinfo, x0, y0, x1, y1: ^c.int) ---
|
||||||
|
|
||||||
|
// leftSideBearing is the offset from the current horizontal position to the left edge of the character
|
||||||
|
// advanceWidth is the offset from the current horizontal position to the next horizontal position
|
||||||
|
// these are expressed in unscaled coordinates
|
||||||
|
GetCodepointHMetrics :: proc(info: ^fontinfo, codepoint: rune, advanceWidth, leftSideBearing: ^c.int) ---
|
||||||
|
|
||||||
|
// an additional amount to add to the 'advance' value between ch1 and ch2
|
||||||
|
GetCodepointKernAdvance :: proc(info: ^fontinfo, ch1, ch2: rune) -> (advance: c.int) ---
|
||||||
|
|
||||||
|
// Gets the bounding box of the visible part of the glyph, in unscaled coordinates
|
||||||
|
GetCodepointBox :: proc(info: ^fontinfo, codepoint: rune, x0, y0, x1, y1: ^c.int) -> c.int ---
|
||||||
|
|
||||||
|
// as above, but takes one or more glyph indices for greater efficiency
|
||||||
|
GetGlyphHMetrics :: proc(info: ^fontinfo, glyph_index: c.int, advanceWidth, leftSideBearing: ^c.int) ---
|
||||||
|
GetGlyphKernAdvance :: proc(info: ^fontinfo, glyph1, glyph2: c.int) -> c.int ---
|
||||||
|
GetGlyphBox :: proc(info: ^fontinfo, glyph_index: c.int, x0, y0, x1, y1: ^c.int) -> c.int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
kerningentry :: struct {
|
||||||
|
glyph1: rune, // use FindGlyphIndex
|
||||||
|
glyph2: rune,
|
||||||
|
advance: c.int,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbtt_")
|
||||||
|
foreign stbtt {
|
||||||
|
// Retrieves a complete list of all of the kerning pairs provided by the font
|
||||||
|
// stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write.
|
||||||
|
// The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1)
|
||||||
|
GetKerningTableLength :: proc(info: ^fontinfo) -> c.int ---
|
||||||
|
GetKerningTable :: proc(info: ^fontinfo, table: [^]kerningentry, table_length: c.int) -> c.int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// GLYPH SHAPES (you probably don't need these, but they have to go before
|
||||||
|
// the bitmaps for C declaration-order reasons)
|
||||||
|
//
|
||||||
|
|
||||||
|
vmove :: enum c.int {
|
||||||
|
none,
|
||||||
|
vmove=1,
|
||||||
|
vline,
|
||||||
|
vcurve,
|
||||||
|
vcubic,
|
||||||
|
}
|
||||||
|
|
||||||
|
vertex_type :: distinct c.short // can't use stbtt_int16 because that's not visible in the header file
|
||||||
|
vertex :: struct {
|
||||||
|
x, y, cx, cy, cx1, cy1: vertex_type,
|
||||||
|
type, padding: byte,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbtt_")
|
||||||
|
foreign stbtt {
|
||||||
|
// returns true if nothing is drawn for this glyph
|
||||||
|
IsGlyphEmpty :: proc(info: ^fontinfo, glyph_index: c.int) -> b32 ---
|
||||||
|
|
||||||
|
// returns # of vertices and fills *vertices with the pointer to them
|
||||||
|
// these are expressed in "unscaled" coordinates
|
||||||
|
//
|
||||||
|
// The shape is a series of contours. Each one starts with
|
||||||
|
// a STBTT_moveto, then consists of a series of mixed
|
||||||
|
// STBTT_lineto and STBTT_curveto segments. A lineto
|
||||||
|
// draws a line from previous endpoint to its x,y; a curveto
|
||||||
|
// draws a quadratic bezier from previous endpoint to
|
||||||
|
// its x,y, using cx,cy as the bezier control point.
|
||||||
|
GetCodepointShape :: proc(info: ^fontinfo, unicode_codepoint: rune, vertices: ^[^]vertex) -> c.int ---
|
||||||
|
GetGlyphShape :: proc(info: ^fontinfo, glyph_index: c.int, vertices: ^[^]vertex) -> c.int ---
|
||||||
|
|
||||||
|
// frees the data allocated above
|
||||||
|
FreeShape :: proc(info: ^fontinfo, vertices: [^]vertex) ---
|
||||||
|
|
||||||
|
// fills svg with the character's SVG data.
|
||||||
|
// returns data size or 0 if SVG not found.
|
||||||
|
FindSVGDoc :: proc(info: ^fontinfo, gl: b32) -> [^]byte ---
|
||||||
|
GetCodepointSVG :: proc(info: ^fontinfo, unicode_codepoint: rune, svg: ^cstring) -> c.int ---
|
||||||
|
GetGlyphSVG :: proc(info: ^fontinfo, gl: b32, svg: ^cstring) -> c.int ---
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// BITMAP RENDERING
|
||||||
|
//
|
||||||
|
|
||||||
|
_bitmap :: struct {
|
||||||
|
w, h, stride: c.int,
|
||||||
|
pixels: [^]byte,
|
||||||
|
}
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbtt_")
|
||||||
|
foreign stbtt {
|
||||||
|
// frees the bitmap allocated below
|
||||||
|
FreeBitmap :: proc(bitmap: [^]byte, userdata: rawptr) ---
|
||||||
|
|
||||||
|
// allocates a large-enough single-channel 8bpp bitmap and renders the
|
||||||
|
// specified character/glyph at the specified scale into it, with
|
||||||
|
// antialiasing. 0 is no coverage (transparent), 255 is fully covered (opaque).
|
||||||
|
// *width & *height are filled out with the width & height of the bitmap,
|
||||||
|
// which is stored left-to-right, top-to-bottom.
|
||||||
|
//
|
||||||
|
// xoff/yoff are the offset it pixel space from the glyph origin to the top-left of the bitmap
|
||||||
|
GetCodepointBitmap :: proc(info: ^fontinfo, scale_x, scale_y: f32, codepoint: rune, width, height, xoff, yoff: ^c.int) -> [^]byte ---
|
||||||
|
|
||||||
|
// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel
|
||||||
|
// shift for the character
|
||||||
|
GetCodepointBitmapSubpixel :: proc(info: ^fontinfo, scale_x, scale_y, shift_x, shift_y: f32, codepoint: rune, width, height, xoff, yoff: ^c.int) -> [^]byte ---
|
||||||
|
|
||||||
|
// the same as stbtt_GetCodepointBitmap, but you pass in storage for the bitmap
|
||||||
|
// in the form of 'output', with row spacing of 'out_stride' bytes. the bitmap
|
||||||
|
// is clipped to out_w/out_h bytes. Call stbtt_GetCodepointBitmapBox to get the
|
||||||
|
// width and height and positioning info for it first.
|
||||||
|
MakeCodepointBitmap :: proc(info: ^fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y: f32, codepoint: rune) ---
|
||||||
|
|
||||||
|
// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel
|
||||||
|
// shift for the character
|
||||||
|
MakeCodepointBitmapSubpixel :: proc(info: ^fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y, shift_x, shift_y: f32, codepoint: rune) ---
|
||||||
|
|
||||||
|
// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering
|
||||||
|
// is performed (see stbtt_PackSetOversampling)
|
||||||
|
MakeCodepointBitmapSubpixelPrefilter :: proc(info: ^fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y, shift_x, shift_y: f32, oversample_x, oversample_y: b32, sub_x, sub_y: ^f32, codepoint: rune) ---
|
||||||
|
|
||||||
|
// get the bbox of the bitmap centered around the glyph origin; so the
|
||||||
|
// bitmap width is ix1-ix0, height is iy1-iy0, and location to place
|
||||||
|
// the bitmap top left is (leftSideBearing*scale,iy0).
|
||||||
|
// (Note that the bitmap uses y-increases-down, but the shape uses
|
||||||
|
// y-increases-up, so CodepointBitmapBox and CodepointBox are inverted.)
|
||||||
|
GetCodepointBitmapBox :: proc(font: ^fontinfo, codepoint: rune, scale_x, scale_y: f32, ix0, iy0, ix1, iy1: ^c.int) ---
|
||||||
|
|
||||||
|
// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel
|
||||||
|
// shift for the character
|
||||||
|
GetCodepointBitmapBoxSubpixel :: proc(font: ^fontinfo, codepoint: rune, scale_x, scale_y, shift_x, shift_y: f32, ix0, iy0, ix1, iy1: ^c.int) ---
|
||||||
|
|
||||||
|
// the following functions are equivalent to the above functions, but operate
|
||||||
|
// on glyph indices instead of Unicode codepoints (for efficiency)
|
||||||
|
GetGlyphBitmap :: proc(info: ^fontinfo, scale_x, scale_y: f32, glyph: c.int, width, height, xoff, yoff: ^c.int) -> [^]byte ---
|
||||||
|
GetGlyphBitmapSubpixel :: proc(info: ^fontinfo, scale_x, scale_y, shift_x, shift_y: f32, glyph: c.int, width, height, xoff, yoff: ^c.int) -> [^]byte ---
|
||||||
|
MakeGlyphBitmap :: proc(info: ^fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y: f32, glyph: c.int) ---
|
||||||
|
MakeGlyphBitmapSubpixel :: proc(info: ^fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y, shift_x, shift_y: f32, glyph: c.int) ---
|
||||||
|
MakeGlyphBitmapSubpixelPrefilter :: proc(info: ^fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y, shift_x, shift_y: f32, oversample_x, oversample_y: c.int, sub_x, sub_y: ^f32, glyph: c.int) ---
|
||||||
|
GetGlyphBitmapBox :: proc(font: ^fontinfo, glyph: c.int, scale_x, scale_y: f32, ix0, iy0, ix1, iy1: ^c.int) ---
|
||||||
|
GetGlyphBitmapBoxSubpixel :: proc(font: ^fontinfo, glyph: c.int, scale_x, scale_y, shift_x, shift_y: f32, ix0, iy0, ix1, iy1: ^c.int) ---
|
||||||
|
|
||||||
|
// rasterize a shape with quadratic beziers into a bitmap
|
||||||
|
Rasterize :: proc(result: ^_bitmap, // 1-channel bitmap to draw into
|
||||||
|
flatness_in_pixels: f32, // allowable error of curve in pixels
|
||||||
|
vertices: [^]vertex, // array of vertices defining shape
|
||||||
|
num_verts: c.int, // number of vertices in above array
|
||||||
|
scale_x, scale_y: f32, // scale applied to input vertices
|
||||||
|
shift_x, shift_y: f32, // translation applied to input vertices
|
||||||
|
x_off, y_off: c.int, // another translation applied to input
|
||||||
|
invert: b32, // if non-zero, vertically flip shape
|
||||||
|
userdata: rawptr, // context for to STBTT_MALLOC
|
||||||
|
) ---
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Signed Distance Function (or Field) rendering
|
||||||
|
//
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbtt_")
|
||||||
|
foreign stbtt {
|
||||||
|
// frees the SDF bitmap allocated below
|
||||||
|
FreeSDF :: proc(bitmap: [^]byte, userdata: rawptr) ---
|
||||||
|
|
||||||
|
// These functions compute a discretized SDF field for a single character, suitable for storing
|
||||||
|
// in a single-channel texture, sampling with bilinear filtering, and testing against
|
||||||
|
// larger than some threshold to produce scalable fonts.
|
||||||
|
// info -- the font
|
||||||
|
// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap
|
||||||
|
// glyph/codepoint -- the character to generate the SDF for
|
||||||
|
// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0),
|
||||||
|
// which allows effects like bit outlines
|
||||||
|
// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character)
|
||||||
|
// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale)
|
||||||
|
// if positive, > onedge_value is inside; if negative, < onedge_value is inside
|
||||||
|
// width,height -- output height & width of the SDF bitmap (including padding)
|
||||||
|
// xoff,yoff -- output origin of the character
|
||||||
|
// return value -- a 2D array of bytes 0..255, width*height in size
|
||||||
|
//
|
||||||
|
// pixel_dist_scale & onedge_value are a scale & bias that allows you to make
|
||||||
|
// optimal use of the limited 0..255 for your application, trading off precision
|
||||||
|
// and special effects. SDF values outside the range 0..255 are clamped to 0..255.
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// scale = stbtt_ScaleForPixelHeight(22)
|
||||||
|
// padding = 5
|
||||||
|
// onedge_value = 180
|
||||||
|
// pixel_dist_scale = 180/5.0 = 36.0
|
||||||
|
//
|
||||||
|
// This will create an SDF bitmap in which the character is about 22 pixels
|
||||||
|
// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled
|
||||||
|
// shape, sample the SDF at each pixel and fill the pixel if the SDF value
|
||||||
|
// is greater than or equal to 180/255. (You'll actually want to antialias,
|
||||||
|
// which is beyond the scope of this example.) Additionally, you can compute
|
||||||
|
// offset outlines (e.g. to stroke the character border inside & outside,
|
||||||
|
// or only outside). For example, to fill outside the character up to 3 SDF
|
||||||
|
// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above
|
||||||
|
// choice of variables maps a range from 5 pixels outside the shape to
|
||||||
|
// 2 pixels inside the shape to 0..255; this is intended primarily for apply
|
||||||
|
// outside effects only (the interior range is needed to allow proper
|
||||||
|
// antialiasing of the font at *smaller* sizes)
|
||||||
|
//
|
||||||
|
// The function computes the SDF analytically at each SDF pixel, not by e.g.
|
||||||
|
// building a higher-res bitmap and approximating it. In theory the quality
|
||||||
|
// should be as high as possible for an SDF of this size & representation, but
|
||||||
|
// unclear if this is true in practice (perhaps building a higher-res bitmap
|
||||||
|
// and computing from that can allow drop-out prevention).
|
||||||
|
//
|
||||||
|
// The algorithm has not been optimized at all, so expect it to be slow
|
||||||
|
// if computing lots of characters or very large sizes.
|
||||||
|
|
||||||
|
GetGlyphSDF :: proc(info: ^fontinfo, scale: f32, glyph, padding: c.int, onedge_value: u8, pixel_dist_scale: f32, width, height, xoff, yoff: ^c.int) -> [^]byte ---
|
||||||
|
GetCodepointSDF :: proc(info: ^fontinfo, scale: f32, codepoint, padding: c.int, onedge_value: u8, pixel_dist_scale: f32, width, height, xoff, yoff: ^c.int) -> [^]byte ---
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Finding the right font...
|
||||||
|
//
|
||||||
|
// You should really just solve this offline, keep your own tables
|
||||||
|
// of what font is what, and don't try to get it out of the .ttf file.
|
||||||
|
// That's because getting it out of the .ttf file is really hard, because
|
||||||
|
// the names in the file can appear in many possible encodings, in many
|
||||||
|
// possible languages, and e.g. if you need a case-insensitive comparison,
|
||||||
|
// the details of that depend on the encoding & language in a complex way
|
||||||
|
// (actually underspecified in truetype, but also gigantic).
|
||||||
|
//
|
||||||
|
// But you can use the provided functions in two possible ways:
|
||||||
|
// stbtt_FindMatchingFont() will use *case-sensitive* comparisons on
|
||||||
|
// unicode-encoded names to try to find the font you want;
|
||||||
|
// you can run this before calling stbtt_InitFont()
|
||||||
|
//
|
||||||
|
// stbtt_GetFontNameString() lets you get any of the various strings
|
||||||
|
// from the file yourself and do your own comparisons on them.
|
||||||
|
// You have to have called stbtt_InitFont() first.
|
||||||
|
|
||||||
|
MACSTYLE_DONTCARE :: 0
|
||||||
|
MACSTYLE_BOLD :: 1
|
||||||
|
MACSTYLE_ITALIC :: 2
|
||||||
|
MACSTYLE_UNDERSCORE :: 4
|
||||||
|
MACSTYLE_NONE :: 8 // <= not same as 0, this makes us check the bitfield is 0
|
||||||
|
|
||||||
|
@(default_calling_convention="c", link_prefix="stbtt_")
|
||||||
|
foreign stbtt {
|
||||||
|
// returns the offset (not index) of the font that matches, or -1 if none
|
||||||
|
// if you use STBTT_MACSTYLE_DONTCARE, use a font name like "Arial Bold".
|
||||||
|
// if you use any other flag, use a font name like "Arial"; this checks
|
||||||
|
// the 'macStyle' header field; i don't know if fonts set this consistently
|
||||||
|
FindMatchingFont :: proc(fontdata: [^]byte, name: cstring, flags: c.int) -> c.int ---
|
||||||
|
|
||||||
|
// returns 1/0 whether the first string interpreted as utf8 is identical to
|
||||||
|
// the second string interpreted as big-endian utf16... useful for strings from next func
|
||||||
|
CompareUTF8toUTF16_bigendian :: proc(s1: cstring, len1: c.int, s2: cstring, len2: c.int) -> c.int ---
|
||||||
|
|
||||||
|
// returns the string (which may be big-endian double byte, e.g. for unicode)
|
||||||
|
// and puts the length in bytes in *length.
|
||||||
|
//
|
||||||
|
// some of the values for the IDs are below; for more see the truetype spec:
|
||||||
|
// http://developer.apple.com/textfonts/TTRefMan/RM06/Chap6name.html
|
||||||
|
// http://www.microsoft.com/typography/otspec/name.htm
|
||||||
|
GetFontNameString :: proc(font: ^fontinfo, length: c.int, platformID: PLATFORM_ID, encodingID, languageID, nameID: c.int) -> cstring ---
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PLATFORM_ID :: enum c.int { // platformID
|
||||||
|
PLATFORM_ID_UNICODE = 0,
|
||||||
|
PLATFORM_ID_MAC = 1,
|
||||||
|
PLATFORM_ID_ISO = 2,
|
||||||
|
PLATFORM_ID_MICROSOFT = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// encodingID for PLATFORM_ID_UNICODE
|
||||||
|
UNICODE_EID_UNICODE_1_0 :: 0
|
||||||
|
UNICODE_EID_UNICODE_1_1 :: 1
|
||||||
|
UNICODE_EID_ISO_10646 :: 2
|
||||||
|
UNICODE_EID_UNICODE_2_0_BMP :: 3
|
||||||
|
UNICODE_EID_UNICODE_2_0_FULL :: 4
|
||||||
|
|
||||||
|
// encodingID for PLATFORM_ID_MICROSOFT
|
||||||
|
MS_EID_SYMBOL :: 0
|
||||||
|
MS_EID_UNICODE_BMP :: 1
|
||||||
|
MS_EID_SHIFTJIS :: 2
|
||||||
|
MS_EID_UNICODE_FULL :: 10
|
||||||
|
|
||||||
|
|
||||||
|
// encodingID for PLATFORM_ID_MAC; same as Script Manager codes
|
||||||
|
MAC_EID_ROMAN, MAC_EID_ARABIC :: 0, 4
|
||||||
|
MAC_EID_JAPANESE, MAC_EID_HEBREW :: 1, 5
|
||||||
|
MAC_EID_CHINESE_TRAD, MAC_EID_GREEK :: 2, 6
|
||||||
|
MAC_EID_KOREAN, MAC_EID_RUSSIAN :: 3, 7
|
||||||
|
|
||||||
|
// languageID for PLATFORM_ID_MICROSOFT; same as LCID...
|
||||||
|
// problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs
|
||||||
|
MS_LANG_ENGLISH, MS_LANG_ITALIAN :: 0x0409, 0x0410
|
||||||
|
MS_LANG_CHINESE, MS_LANG_JAPANESE :: 0x0804, 0x0411
|
||||||
|
MS_LANG_DUTCH, MS_LANG_KOREAN :: 0x0413, 0x0412
|
||||||
|
MS_LANG_FRENCH, MS_LANG_RUSSIAN :: 0x040c, 0x0419
|
||||||
|
MS_LANG_GERMAN, MS_LANG_SPANISH :: 0x0407, 0x0409
|
||||||
|
MS_LANG_HEBREW, MS_LANG_SWEDISH :: 0x040d, 0x041D
|
||||||
|
|
||||||
|
|
||||||
|
// languageID for PLATFORM_ID_MAC
|
||||||
|
MAC_LANG_ENGLISH, MAC_LANG_JAPANESE :: 0, 11
|
||||||
|
MAC_LANG_ARABIC, MAC_LANG_KOREAN :: 12, 23
|
||||||
|
MAC_LANG_DUTCH, MAC_LANG_RUSSIAN :: 4, 32
|
||||||
|
MAC_LANG_FRENCH, MAC_LANG_SPANISH :: 1, 6
|
||||||
|
MAC_LANG_GERMAN, MAC_LANG_SWEDISH :: 2, 5
|
||||||
|
MAC_LANG_HEBREW, MAC_LANG_CHINESE_SIMPLIFIED :: 10, 33
|
||||||
|
MAC_LANG_ITALIAN, MAC_LANG_CHINESE_TRAD :: 3, 19
|
||||||
|
|
||||||
|
// private structure
|
||||||
|
_buf :: struct {
|
||||||
|
data: [^]byte,
|
||||||
|
cursor: c.int,
|
||||||
|
size: c.int,
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user