diff --git a/.github/workflows/linux_build.yaml b/.github/workflows/linux_build.yaml new file mode 100644 index 0000000..7727bc1 --- /dev/null +++ b/.github/workflows/linux_build.yaml @@ -0,0 +1,77 @@ +name: Linux Build + +on: + push: + branches: + - '**' + - '**' + pull_request: + branches: + - '**' + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Setup Odin + uses: laytan/setup-odin@v2 + with: + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up environment + run: | + if ! command -v git &> /dev/null; then + echo "Git not found. Installing Git..." + sudo apt-get update + sudo apt-get install -y git + else + echo "Git is already installed." + fi + git --version + + sudo apt-get update + sudo apt-get install -y \ + build-essential \ + bash \ + libfreetype6-dev \ + libharfbuzz-dev \ + libgl1-mesa-dev \ + libx11-dev \ + libxcursor-dev \ + libxrandr-dev \ + libxinerama-dev \ + libxi-dev \ + libglew-dev \ + libxtst-dev \ + libasound-dev + + make -C "/home/runner/odin/vendor/stb/src" + + - name: Run build script + run: | + echo "Setting execute permissions on specific .sh files" + chmod +x ./scripts/build_sokol_demo.sh + chmod +x ./scripts/build_sokol_library.sh + chmod +x ./scripts/compile_sokol_shaders.sh + chmod +x ./scripts/clean.sh + chmod +x ./scripts/helpers/misc.sh + chmod +x ./scripts/helpers/odin_compiler_defs.sh + + echo "Running build_sokol_demo.sh" + ./scripts/build_sokol_demo.sh + shell: bash + + - name: List build artifacts + run: ls -R ./build || echo "build directory not found" + + - name: Upload artifacts + uses: actions/upload-artifact@v3 + with: + name: linux-build + path: ./build/ + if-no-files-found: warn diff --git a/.github/workflows/macos_build.yaml b/.github/workflows/macos_build.yaml index e7f5ee5..b520d46 100644 --- a/.github/workflows/macos_build.yaml +++ b/.github/workflows/macos_build.yaml @@ -35,7 +35,7 @@ jobs: brew install harfbuzz brew install odin - make -C "/opt/homebrew/Cellar/odin/2024-10/libexec/vendor/stb/src" + make -C "/opt/homebrew/Cellar/odin/2024-12/libexec/vendor/stb/src" - name: Run build script run: | diff --git a/.gitignore b/.gitignore index dafaa3c..7a505e9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ -build -thirdparty -.vscode -# backend/sokol/render_glyph.odin -# backend/sokol/blit_atlas.odin -# backend/sokol/draw_text.odin +.vscode +ols.json +build +thirdparty/freetype +thirdparty/harfbuzz +thirdparty/sokol +thirdparty/sokol-tools diff --git a/LICENSE.md b/LICENSE.md index a602421..5652bec 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -VEFontCache Odin Port +VEFontCache Odin Copyright 2024 Edward R. Gonzalez This project is based on Vertex Engine GPU Font Cache diff --git a/Readme.md b/Readme.md index 197985d..d9a5735 100644 --- a/Readme.md +++ b/Readme.md @@ -1,28 +1,73 @@ -# VE Font Cache : Odin Port +# VE Font Cache -https://github.com/user-attachments/assets/b74f1ec1-f980-45df-b604-d6b7d87d40ff +Vertex Engine GPU Font Cache: A text shaping & rendering library. -This is a port of the [VEFontCache](https://github.com/hypernewbie/VEFontCache) library. +This project started as a port of the [VEFontCache](https://github.com/hypernewbie/VEFontCache) library to the Odin programming language. +While originally intended for game engines, its rendering quality and performance make it suitable for many other applications. -Its original purpose was for use in game engines, however its rendeirng quality and performance is more than adequate for many other applications. +Since then, the library has been overhauled to offer higher performance, improved visual fidelity, additional features, and quality of life improvements. -See: [docs/Readme.md](docs/Readme.md) for the library's interface. +Features: + +* Simple and well documented +* Load and unload fonts at any time +* Almost entirely configurable and tunable at runtime +* Full support for hot-reload + * Clear the caches at any time +* Robust quality of life features: + * Snap positioning to view for better hinting + * Tracks text layers + * Enforce even-only font sizing (useful for linear zoom) + * Push and pop stack for font, font_size, color, view, position, scale, and zoom +* Basic (latin) or advanced (harfbuzz) text shaping +* All rendering is real-time, with triangulation on the CPU, vertex rendering and texture blitting on the GPU + * Can handle thousands of draw text calls with very large or small shapes +* 4-Level Regioned Texture Atlas for caching rendered glyphs +* Text shape caching +* Glyph texture buffer for rendering text with super-sampling to downsample to the atlas or direct to target screen +* Super-sample by a font size scalar for sharper glyphs +* All caching backed by an optimized 32-bit LRU indexing cache +* Provides a backend-agnostic draw list (see [backend](./backend) for usage example) + +Upcoming: + +* Support choosing between top-left or bottom-left coordinate convention (currently bottom-left) +* Support for better triangulation + * Support for triangulation method selection on a per-font basis + * [Reference paper](https://www.microsoft.com/en-us/research/wp-content/uploads/2005/01/p1000-loop.pdf) +* Better support for tuning glyph render sampling + * Support for sub-pixel AA + * Ability to decide AA method & degree on a per-font basis +* Multi-threading supported job queue + * Lift heavy-lifting portion of the library's context into a thread context + * Synchronize threads by merging their generated layered draw list into a finished draw list for processing on the user's render thread + * User defines how thread contexts are distributed for drawing (a basic quadrant-based selector procedure will be provided) + +## Documentation + +* [docs/Readme.md](docs/Readme.md) for the library's interface +* [docs/guide_backend.md](docs/guide_backend.md) for information on implementing your own backend +* [docs/guide_architecture.md](docs/guide_architecture.md) for an in-depth breakdown of significant design decisions and code-paths + +For learning about text shaping & rendering see: [notes](https://github.com/Ed94/TextRendering_Notes) ## Building See [scripts/Readme.md](scripts/Readme.md) for building examples or utilizing the provided backends. -Currently the scripts provided & the library itself were developed & tested on Windows. There are bash scripts for building on linux & mac. +Currently, the scripts provided & the library itself were developed & tested on Windows. There are bash scripts for building on Linux (they build on WSL but need additional testing). + +The library depends on harfbuzz & stb_truetype to build. +Note: harfbuzz could technically be removed if the user removes their definitions, however this hasn't been made into a conditional compilation option yet. + +**NOTICE: All library dependency packages are in the "thirdparty" collection of this repository. For their codebase, the user soley has to modify that collection specification for where they would like to put these external vendor package not provided by the offical Odin vendor collections.** + +## Gallery -The library depends on freetype, harfbuzz, & stb_truetype to build. -Note: freetype and harfbuzz could technically be gutted if the user removes their definitions, however they have not been made into a conditional compilation option (yet). +![sokol_demo_2025-01-11_01-32-24](https://github.com/user-attachments/assets/4aea2b23-4362-47e6-b6d1-286e84891702) -## Changes from orignal +https://github.com/user-attachments/assets/db8c7725-84dd-48df-9a3f-65605d3ab444 -* Font Parser & Glyph shaper are abstracted to their own warpper interface -* ve_fontcache_loadfile not ported (ust use core:os or os2, then call load_font) -* Macro defines have been coverted (mostly) to runtime parameters -* Support for hot_reloading -* Curve quality step interpolation for glyph rendering can be set on a per font basis. +https://github.com/user-attachments/assets/40030308-37db-492d-a196-f830e8a39f3c -![image](https://github.com/user-attachments/assets/2f6c0b36-179c-42fe-8903-7640ae3c209e) +https://github.com/user-attachments/assets/0985246b-74f8-4d1c-82d8-053414c44aec diff --git a/backend/sokol/backend_sokol.odin b/backend/sokol/backend_sokol.odin index ecbdc3c..69aca80 100644 --- a/backend/sokol/backend_sokol.odin +++ b/backend/sokol/backend_sokol.odin @@ -12,12 +12,12 @@ Context :: struct { atlas_shader : gfx.Shader, screen_shader : gfx.Shader, - // 2k x 512, R8 + // ve.glyph_buffer.(width, height), R8 glyph_rt_color : gfx.Image, glyph_rt_depth : gfx.Image, glyph_rt_sampler : gfx.Sampler, - // 4k x 2k, R8 + // ve.atlas.(width, height), R8 atlas_rt_color : gfx.Image, atlas_rt_depth : gfx.Image, atlas_rt_sampler : gfx.Sampler, @@ -33,7 +33,6 @@ Context :: struct { setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index_cap : u64 ) { - using ctx Attachment_Desc :: gfx.Attachment_Desc Blend_Factor :: gfx.Blend_Factor Blend_Op :: gfx.Blend_Op @@ -60,23 +59,23 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index backend := gfx.query_backend() app_env := glue.environment() - glyph_shader = gfx.make_shader(render_glyph_shader_desc(backend) ) - atlas_shader = gfx.make_shader(blit_atlas_shader_desc(backend) ) - screen_shader = gfx.make_shader(draw_text_shader_desc(backend) ) + ctx.glyph_shader = gfx.make_shader(render_glyph_shader_desc(backend) ) + ctx.atlas_shader = gfx.make_shader(ve_blit_atlas_shader_desc(backend) ) + ctx.screen_shader = gfx.make_shader(ve_draw_text_shader_desc(backend) ) - draw_list_vbuf = gfx.make_buffer( Buffer_Desciption { + ctx.draw_list_vbuf = gfx.make_buffer( Buffer_Desciption { size = cast(uint)(size_of([4]f32) * vert_cap), usage = Buffer_Usage.STREAM, type = Buffer_Type.VERTEXBUFFER, }) - assert( gfx.query_buffer_state( draw_list_vbuf) < Resource_State.FAILED, "Failed to make draw_list_vbuf" ) + assert( gfx.query_buffer_state( ctx.draw_list_vbuf) < Resource_State.FAILED, "Failed to make draw_list_vbuf" ) - draw_list_ibuf = gfx.make_buffer( Buffer_Desciption { + ctx.draw_list_ibuf = gfx.make_buffer( Buffer_Desciption { size = cast(uint)(size_of(u32) * index_cap), usage = Buffer_Usage.STREAM, type = Buffer_Type.INDEXBUFFER, }) - assert( gfx.query_buffer_state( draw_list_ibuf) < Resource_State.FAILED, "Failed to make draw_list_iubuf" ) + assert( gfx.query_buffer_state( ctx.draw_list_ibuf) < Resource_State.FAILED, "Failed to make draw_list_iubuf" ) Image_Filter := Filter.LINEAR @@ -84,18 +83,17 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index { vs_layout : Vertex_Layout_State { - using vs_layout - attrs[ATTR_render_glyph_v_position] = Vertex_Attribute_State { + vs_layout.attrs[ATTR_render_glyph_v_position] = Vertex_Attribute_State { format = Vertex_Format.FLOAT2, offset = 0, buffer_index = 0, } - attrs[ATTR_render_glyph_v_texture] = Vertex_Attribute_State { + vs_layout.attrs[ATTR_render_glyph_v_texture] = Vertex_Attribute_State { format = Vertex_Format.FLOAT2, offset = size_of(ve.Vec2), buffer_index = 0, } - buffers[0] = Vertex_Buffer_Layout_State { + vs_layout.buffers[0] = Vertex_Buffer_Layout_State { stride = size_of([4]f32), step_func = Vertex_Step.PER_VERTEX } @@ -115,8 +113,8 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index }, } - glyph_pipeline = gfx.make_pipeline({ - shader = glyph_shader, + ctx.glyph_pipeline = gfx.make_pipeline({ + shader = ctx.glyph_shader, layout = vs_layout, index_type = Vertex_Index_Type.UINT32, colors = { @@ -131,29 +129,29 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index cull_mode = .NONE, sample_count = 1, }) - assert( gfx.query_pipeline_state(glyph_pipeline) < Resource_State.FAILED, "Failed to make glyph_pipeline" ) + assert( gfx.query_pipeline_state(ctx.glyph_pipeline) < Resource_State.FAILED, "Failed to make glyph_pipeline" ) } // glyph_pass { - glyph_rt_color = gfx.make_image( Image_Desc { + ctx.glyph_rt_color = gfx.make_image( Image_Desc { type = ._2D, render_target = true, - width = i32(ve_ctx.glyph_buffer.width), - height = i32(ve_ctx.glyph_buffer.height), + width = i32(ve_ctx.glyph_buffer.size.x), + height = i32(ve_ctx.glyph_buffer.size.y), num_slices = 1, num_mipmaps = 1, usage = .IMMUTABLE, pixel_format = .R8, sample_count = 1, }) - assert( gfx.query_image_state(glyph_rt_color) < Resource_State.FAILED, "Failed to make glyph_pipeline" ) + assert( gfx.query_image_state(ctx.glyph_rt_color) < Resource_State.FAILED, "Failed to make glyph_pipeline" ) - glyph_rt_depth = gfx.make_image( Image_Desc { + ctx.glyph_rt_depth = gfx.make_image( Image_Desc { type = ._2D, render_target = true, - width = i32(ve_ctx.glyph_buffer.width), - height = i32(ve_ctx.glyph_buffer.height), + width = i32(ve_ctx.glyph_buffer.size.x), + height = i32(ve_ctx.glyph_buffer.size.y), num_slices = 1, num_mipmaps = 1, usage = .IMMUTABLE, @@ -161,22 +159,22 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index sample_count = 1, }) - glyph_rt_sampler = gfx.make_sampler( Sampler_Description { + ctx.glyph_rt_sampler = gfx.make_sampler( Sampler_Description { min_filter = Image_Filter, mag_filter = Image_Filter, mipmap_filter = Filter.NEAREST, wrap_u = .CLAMP_TO_EDGE, wrap_v = .CLAMP_TO_EDGE, - min_lod = -1000.0, - max_lod = 1000.0, + min_lod = -1.0, + max_lod = 1.0, border_color = Border_Color.OPAQUE_BLACK, compare = .NEVER, max_anisotropy = 1, }) - assert( gfx.query_sampler_state( glyph_rt_sampler) < Resource_State.FAILED, "Failed to make atlas_rt_sampler" ) + assert( gfx.query_sampler_state( ctx.glyph_rt_sampler) < Resource_State.FAILED, "Failed to make atlas_rt_sampler" ) color_attach := Attachment_Desc { - image = glyph_rt_color, + image = ctx.glyph_rt_color, } glyph_attachments := gfx.make_attachments({ @@ -184,7 +182,7 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index 0 = color_attach, }, depth_stencil = { - image = glyph_rt_depth, + image = ctx.glyph_rt_depth, }, }) assert( gfx.query_attachments_state(glyph_attachments) < Resource_State.FAILED, "Failed to make glyph_attachments" ) @@ -209,7 +207,7 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index } } - glyph_pass = gfx.Pass { + ctx.glyph_pass = gfx.Pass { action = glyph_action, attachments = glyph_attachments, // label = @@ -220,18 +218,17 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index { vs_layout : Vertex_Layout_State { - using vs_layout - attrs[ATTR_blit_atlas_v_position] = Vertex_Attribute_State { + vs_layout.attrs[ATTR_ve_blit_atlas_v_position] = Vertex_Attribute_State { format = Vertex_Format.FLOAT2, offset = 0, buffer_index = 0, } - attrs[ATTR_blit_atlas_v_texture] = Vertex_Attribute_State { + vs_layout.attrs[ATTR_ve_blit_atlas_v_texture] = Vertex_Attribute_State { format = Vertex_Format.FLOAT2, offset = size_of(ve.Vec2), buffer_index = 0, } - buffers[0] = Vertex_Buffer_Layout_State { + vs_layout.buffers[0] = Vertex_Buffer_Layout_State { stride = size_of([4]f32), step_func = Vertex_Step.PER_VERTEX } @@ -251,8 +248,8 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index }, } - atlas_pipeline = gfx.make_pipeline({ - shader = atlas_shader, + ctx.atlas_pipeline = gfx.make_pipeline({ + shader = ctx.atlas_shader, layout = vs_layout, index_type = Vertex_Index_Type.UINT32, colors = { @@ -271,11 +268,11 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index // atlas_pass { - atlas_rt_color = gfx.make_image( Image_Desc { + ctx.atlas_rt_color = gfx.make_image( Image_Desc { type = ._2D, render_target = true, - width = i32(ve_ctx.atlas.width), - height = i32(ve_ctx.atlas.height), + width = i32(ve_ctx.atlas.size.x), + height = i32(ve_ctx.atlas.size.y), num_slices = 1, num_mipmaps = 1, usage = .IMMUTABLE, @@ -284,37 +281,37 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index // TODO(Ed): Setup labels for debug tracing/logging // label = }) - assert( gfx.query_image_state(atlas_rt_color) < Resource_State.FAILED, "Failed to make atlas_rt_color") + assert( gfx.query_image_state(ctx.atlas_rt_color) < Resource_State.FAILED, "Failed to make atlas_rt_color") - atlas_rt_depth = gfx.make_image( Image_Desc { + ctx.atlas_rt_depth = gfx.make_image( Image_Desc { type = ._2D, render_target = true, - width = i32(ve_ctx.atlas.width), - height = i32(ve_ctx.atlas.height), + width = i32(ve_ctx.atlas.size.x), + height = i32(ve_ctx.atlas.size.y), num_slices = 1, num_mipmaps = 1, usage = .IMMUTABLE, pixel_format = .DEPTH, sample_count = 1, }) - assert( gfx.query_image_state(atlas_rt_depth) < Resource_State.FAILED, "Failed to make atlas_rt_depth") + assert( gfx.query_image_state(ctx.atlas_rt_depth) < Resource_State.FAILED, "Failed to make atlas_rt_depth") - atlas_rt_sampler = gfx.make_sampler( Sampler_Description { + ctx.atlas_rt_sampler = gfx.make_sampler( Sampler_Description { min_filter = Image_Filter, mag_filter = Image_Filter, mipmap_filter = Filter.NEAREST, wrap_u = .CLAMP_TO_EDGE, wrap_v = .CLAMP_TO_EDGE, - min_lod = -1000.0, - max_lod = 1000.0, + min_lod = -1.0, + max_lod = 1.0, border_color = Border_Color.OPAQUE_BLACK, compare = .NEVER, max_anisotropy = 1, }) - assert( gfx.query_sampler_state( atlas_rt_sampler) < Resource_State.FAILED, "Failed to make atlas_rt_sampler" ) + assert( gfx.query_sampler_state( ctx.atlas_rt_sampler) < Resource_State.FAILED, "Failed to make atlas_rt_sampler" ) color_attach := Attachment_Desc { - image = atlas_rt_color, + image = ctx.atlas_rt_color, } atlas_attachments := gfx.make_attachments({ @@ -322,7 +319,7 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index 0 = color_attach, }, depth_stencil = { - image = atlas_rt_depth, + image = ctx.atlas_rt_depth, }, }) assert( gfx.query_attachments_state(atlas_attachments) < Resource_State.FAILED, "Failed to make atlas_attachments") @@ -347,7 +344,7 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index } } - atlas_pass = gfx.Pass { + ctx.atlas_pass = gfx.Pass { action = atlas_action, attachments = atlas_attachments, } @@ -357,18 +354,17 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index { vs_layout : Vertex_Layout_State { - using vs_layout - attrs[ATTR_draw_text_v_position] = Vertex_Attribute_State { + vs_layout.attrs[ATTR_ve_draw_text_v_position] = Vertex_Attribute_State { format = Vertex_Format.FLOAT2, offset = 0, buffer_index = 0, } - attrs[ATTR_draw_text_v_texture] = Vertex_Attribute_State { + vs_layout.attrs[ATTR_ve_draw_text_v_texture] = Vertex_Attribute_State { format = Vertex_Format.FLOAT2, offset = size_of(ve.Vec2), buffer_index = 0, } - buffers[0] = Vertex_Buffer_Layout_State { + vs_layout.buffers[0] = Vertex_Buffer_Layout_State { stride = size_of([4]f32), step_func = Vertex_Step.PER_VERTEX } @@ -388,8 +384,8 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index }, } - screen_pipeline = gfx.make_pipeline({ - shader = screen_shader, + ctx.screen_pipeline = gfx.make_pipeline({ + shader = ctx.screen_shader, layout = vs_layout, index_type = Vertex_Index_Type.UINT32, colors = { @@ -404,7 +400,7 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index }, cull_mode = .NONE, }) - assert( gfx.query_pipeline_state(screen_pipeline) < Resource_State.FAILED, "Failed to make screen_pipeline" ) + assert( gfx.query_pipeline_state(ctx.screen_pipeline) < Resource_State.FAILED, "Failed to make screen_pipeline" ) } // screen_pass @@ -439,7 +435,7 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index } } - screen_pass = gfx.Pass { + ctx.screen_pass = gfx.Pass { action = screen_action, } } @@ -448,8 +444,6 @@ setup_gfx_objects :: proc( ctx : ^Context, ve_ctx : ^ve.Context, vert_cap, index render_text_layer :: proc( screen_extent : ve.Vec2, ve_ctx : ^ve.Context, ctx : Context ) { // profile("VEFontCache: render text layer") - using ctx - Bindings :: gfx.Bindings Range :: gfx.Range Shader_Stage :: gfx.Shader_Stage @@ -459,8 +453,8 @@ render_text_layer :: proc( screen_extent : ve.Vec2, ve_ctx : ^ve.Context, ctx : vbuf_ve_range := Range{ raw_data(vbuf_layer_slice), cast(uint) len(vbuf_layer_slice) * size_of(ve.Vertex) } ibuf_ve_range := Range{ raw_data(ibuf_layer_slice), cast(uint) len(ibuf_layer_slice) * size_of(u32) } - gfx.append_buffer( draw_list_vbuf, vbuf_ve_range ) - gfx.append_buffer( draw_list_ibuf, ibuf_ve_range ) + gfx.append_buffer( ctx.draw_list_vbuf, vbuf_ve_range ) + gfx.append_buffer( ctx.draw_list_ibuf, ibuf_ve_range ) ve.flush_draw_list_layer( ve_ctx ) @@ -484,10 +478,10 @@ render_text_layer :: proc( screen_extent : ve.Vec2, ve_ctx : ^ve.Context, ctx : continue } - width := ve_ctx.glyph_buffer.width - height := ve_ctx.glyph_buffer.height + width := ve_ctx.glyph_buffer.size.x + height := ve_ctx.glyph_buffer.size.y - pass := glyph_pass + pass := ctx.glyph_pass if draw_call.clear_before_draw { pass.action.colors[0].load_action = .CLEAR pass.action.colors[0].clear_value.a = 1.0 @@ -497,16 +491,16 @@ render_text_layer :: proc( screen_extent : ve.Vec2, ve_ctx : ^ve.Context, ctx : gfx.apply_viewport( 0,0, width, height, origin_top_left = true ) gfx.apply_scissor_rect( 0,0, width, height, origin_top_left = true ) - gfx.apply_pipeline( glyph_pipeline ) + gfx.apply_pipeline( ctx.glyph_pipeline ) bindings := Bindings { vertex_buffers = { - 0 = draw_list_vbuf, + 0 = ctx.draw_list_vbuf, }, vertex_buffer_offsets = { 0 = 0, }, - index_buffer = draw_list_ibuf, + index_buffer = ctx.draw_list_ibuf, index_buffer_offset = 0, } gfx.apply_bindings( bindings ) @@ -519,10 +513,10 @@ render_text_layer :: proc( screen_extent : ve.Vec2, ve_ctx : ^ve.Context, ctx : continue } - width := ve_ctx.atlas.width - height := ve_ctx.atlas.height + width := ve_ctx.atlas.size.x + height := ve_ctx.atlas.size.y - pass := atlas_pass + pass := ctx.atlas_pass if draw_call.clear_before_draw { pass.action.colors[0].load_action = .CLEAR pass.action.colors[0].clear_value.a = 1.0 @@ -532,25 +526,29 @@ render_text_layer :: proc( screen_extent : ve.Vec2, ve_ctx : ^ve.Context, ctx : gfx.apply_viewport( 0, 0, width, height, origin_top_left = true ) gfx.apply_scissor_rect( 0, 0, width, height, origin_top_left = true ) - gfx.apply_pipeline( atlas_pipeline ) + gfx.apply_pipeline( ctx.atlas_pipeline ) - fs_uniform := Blit_Atlas_Fs_Params { region = cast(i32) draw_call.region } - gfx.apply_uniforms( UB_blit_atlas_fs_params, Range { & fs_uniform, size_of(Blit_Atlas_Fs_Params) }) + fs_uniform := Ve_Blit_Atlas_Fs_Params { + glyph_buffer_size = ve.vec2(ve_ctx.glyph_buffer.size), + over_sample = ve_ctx.glyph_buffer.over_sample.x, + region = cast(i32) draw_call.region, + } + gfx.apply_uniforms( UB_ve_blit_atlas_fs_params, Range { & fs_uniform, size_of(Ve_Blit_Atlas_Fs_Params) }) gfx.apply_bindings(Bindings { vertex_buffers = { - 0 = draw_list_vbuf, + 0 = ctx.draw_list_vbuf, }, vertex_buffer_offsets = { 0 = 0, }, - index_buffer = draw_list_ibuf, + index_buffer = ctx.draw_list_ibuf, index_buffer_offset = 0, - images = { IMG_blit_atlas_src_texture = glyph_rt_color, }, - samplers = { SMP_blit_atlas_src_sampler = glyph_rt_sampler, }, + images = { IMG_ve_blit_atlas_src_texture = ctx.glyph_rt_color, }, + samplers = { SMP_ve_blit_atlas_src_sampler = ctx.glyph_rt_sampler, }, }) - // 3. Use the atlas to then render the text. + // 3. Use the atlas (.Target) or the glyph buffer (.Target_Unchached) to then render the text. case .None, .Target, .Target_Uncached: if num_indices == 0 && ! draw_call.clear_before_draw { continue @@ -558,41 +556,42 @@ render_text_layer :: proc( screen_extent : ve.Vec2, ve_ctx : ^ve.Context, ctx : // profile("VEFontCache: draw call: target") - pass := screen_pass + pass := ctx.screen_pass pass.swapchain = glue.swapchain() gfx.begin_pass( pass ) gfx.apply_viewport( 0, 0, screen_width, screen_height, origin_top_left = true ) gfx.apply_scissor_rect( 0, 0, screen_width, screen_height, origin_top_left = true ) - gfx.apply_pipeline( screen_pipeline ) + gfx.apply_pipeline( ctx.screen_pipeline ) - src_rt := atlas_rt_color - src_sampler := atlas_rt_sampler + src_rt := ctx.atlas_rt_color + src_sampler := ctx.atlas_rt_sampler - fs_target_uniform := Draw_Text_Fs_Params { - down_sample = 0, - colour = draw_call.colour, + fs_target_uniform := Ve_Draw_Text_Fs_Params { + // glyph_buffer_size = glyph_buf_size, + over_sample = ve_ctx.glyph_buffer.over_sample.x, + colour = draw_call.colour, } if draw_call.pass == .Target_Uncached { - fs_target_uniform.down_sample = 1 - src_rt = glyph_rt_color - src_sampler = glyph_rt_sampler + // fs_target_uniform.over_sample = 1.0 + src_rt = ctx.glyph_rt_color + src_sampler = ctx.glyph_rt_sampler } - gfx.apply_uniforms( UB_draw_text_fs_params, Range { & fs_target_uniform, size_of(Draw_Text_Fs_Params) }) + gfx.apply_uniforms( UB_ve_draw_text_fs_params, Range { & fs_target_uniform, size_of(Ve_Draw_Text_Fs_Params) }) gfx.apply_bindings(Bindings { vertex_buffers = { - 0 = draw_list_vbuf, + 0 = ctx.draw_list_vbuf, }, vertex_buffer_offsets = { 0 = 0, }, - index_buffer = draw_list_ibuf, + index_buffer = ctx.draw_list_ibuf, index_buffer_offset = 0, - images = { IMG_draw_text_src_texture = src_rt, }, - samplers = { SMP_draw_text_src_sampler = src_sampler, }, + images = { IMG_ve_draw_text_src_texture = src_rt, }, + samplers = { SMP_ve_draw_text_src_sampler = src_sampler, }, }) } diff --git a/backend/sokol/blit_atlas.odin b/backend/sokol/blit_atlas.odin index 42a8cc5..9aa257e 100644 --- a/backend/sokol/blit_atlas.odin +++ b/backend/sokol/blit_atlas.odin @@ -10,35 +10,36 @@ import sg "thirdparty:sokol/gfx" Overview: ========= - Shader program: 'blit_atlas': - Get shader desc: blit_atlas_shader_desc(sg.query_backend()) - Vertex Shader: blit_atlas_vs - Fragment Shader: blit_atlas_fs + Shader program: 've_blit_atlas': + Get shader desc: ve_blit_atlas_shader_desc(sg.query_backend()) + Vertex Shader: ve_blit_atlas_vs + Fragment Shader: ve_blit_atlas_fs Attributes: - ATTR_blit_atlas_v_position => 0 - ATTR_blit_atlas_v_texture => 1 + ATTR_ve_blit_atlas_v_position => 0 + ATTR_ve_blit_atlas_v_texture => 1 Bindings: - Uniform block 'blit_atlas_fs_params': - Odin struct: Blit_Atlas_Fs_Params - Bind slot: UB_blit_atlas_fs_params => 0 - Image 'blit_atlas_src_texture': + Uniform block 've_blit_atlas_fs_params': + Odin struct: Ve_Blit_Atlas_Fs_Params + Bind slot: UB_ve_blit_atlas_fs_params => 0 + Image 've_blit_atlas_src_texture': Image type: ._2D Sample type: .FLOAT Multisampled: false - Bind slot: IMG_blit_atlas_src_texture => 0 - Sampler 'blit_atlas_src_sampler': + Bind slot: IMG_ve_blit_atlas_src_texture => 0 + Sampler 've_blit_atlas_src_sampler': Type: .FILTERING - Bind slot: SMP_blit_atlas_src_sampler => 0 + Bind slot: SMP_ve_blit_atlas_src_sampler => 0 */ -ATTR_blit_atlas_v_position :: 0 -ATTR_blit_atlas_v_texture :: 1 -UB_blit_atlas_fs_params :: 0 -IMG_blit_atlas_src_texture :: 0 -SMP_blit_atlas_src_sampler :: 0 -Blit_Atlas_Fs_Params :: struct #align(16) { +ATTR_ve_blit_atlas_v_position :: 0 +ATTR_ve_blit_atlas_v_texture :: 1 +UB_ve_blit_atlas_fs_params :: 0 +IMG_ve_blit_atlas_src_texture :: 0 +SMP_ve_blit_atlas_src_sampler :: 0 +Ve_Blit_Atlas_Fs_Params :: struct #align(16) { using _: struct #packed { + glyph_buffer_size: [2]f32, + over_sample: f32, region: i32, - _: [12]u8, }, } /* @@ -56,7 +57,7 @@ Blit_Atlas_Fs_Params :: struct #align(16) { */ @(private="file") -blit_atlas_vs_source_glsl410 := [235]u8 { +ve_blit_atlas_vs_source_glsl410 := [235]u8 { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x6c,0x61, 0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, 0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a, @@ -76,49 +77,67 @@ blit_atlas_vs_source_glsl410 := [235]u8 { /* #version 410 - uniform ivec4 blit_atlas_fs_params[1]; - uniform sampler2D blit_atlas_src_texture_blit_atlas_src_sampler; + struct ve_blit_atlas_fs_params + { + vec2 glyph_buffer_size; + float over_sample; + int region; + }; + + uniform ve_blit_atlas_fs_params _20; + + uniform sampler2D ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler; layout(location = 0) in vec2 uv; layout(location = 0) out vec4 frag_color; - float down_sample(vec2 uv_1, vec2 texture_size) + float down_sample_to_texture(vec2 uv_1, vec2 texture_size) { - return 0.25 * (((texture(blit_atlas_src_texture_blit_atlas_src_sampler, uv_1).x + texture(blit_atlas_src_texture_blit_atlas_src_sampler, fma(vec2(0.0, 1.0), texture_size, uv_1)).x) + texture(blit_atlas_src_texture_blit_atlas_src_sampler, fma(vec2(1.0, 0.0), texture_size, uv_1)).x) + texture(blit_atlas_src_texture_blit_atlas_src_sampler, uv_1 + texture_size).x); + return (1.0 / _20.over_sample) * (((texture(ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler, uv_1).x + texture(ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler, fma(vec2(0.0, 1.0), texture_size, uv_1)).x) + texture(ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler, fma(vec2(1.0, 0.0), texture_size, uv_1)).x) + texture(ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler, uv_1 + texture_size).x); } void main() { - bool _93 = blit_atlas_fs_params[0].x == 0; - bool _101; - if (!_93) + vec2 _98 = vec2(1.0) / _20.glyph_buffer_size; + bool _104 = _20.region == 0; + bool _111; + if (!_104) { - _101 = blit_atlas_fs_params[0].x == 1; + _111 = _20.region == 1; } else { - _101 = _93; + _111 = _104; } - bool _109; - if (!_101) + bool _118; + if (!_111) { - _109 = blit_atlas_fs_params[0].x == 2; + _118 = _20.region == 2; } else { - _109 = _101; + _118 = _111; } - if (_109) + bool _126; + if (!_118) { - vec2 param = uv + vec2(-0.00048828125, -0.0029296875); - vec2 param_1 = vec2(0.00048828125, 0.001953125); - vec2 param_2 = uv + vec2(0.000244140625, -0.0029296875); - vec2 param_3 = vec2(0.00048828125, 0.001953125); - vec2 param_4 = uv + vec2(-0.000732421875, 0.0009765625); - vec2 param_5 = vec2(0.00048828125, 0.001953125); - vec2 param_6 = uv + vec2(0.000244140625, 0.0009765625); - vec2 param_7 = vec2(0.00048828125, 0.001953125); - frag_color = vec4(1.0, 1.0, 1.0, 0.25 * (((down_sample(param, param_1) + down_sample(param_2, param_3)) + down_sample(param_4, param_5)) + down_sample(param_6, param_7))); + _126 = _20.region == 4; + } + else + { + _126 = _118; + } + if (_126) + { + vec2 param = uv + (vec2(-1.0, -1.5) / _20.glyph_buffer_size); + vec2 param_1 = _98; + vec2 param_2 = uv + (vec2(0.5, -1.5) / _20.glyph_buffer_size); + vec2 param_3 = _98; + vec2 param_4 = uv + (vec2(-1.5, 0.5) / _20.glyph_buffer_size); + vec2 param_5 = _98; + vec2 param_6 = uv + (vec2(0.5) / _20.glyph_buffer_size); + vec2 param_7 = _98; + frag_color = vec4(1.0, 1.0, 1.0, (1.0 / _20.over_sample) * (((down_sample_to_texture(param, param_1) + down_sample_to_texture(param_2, param_3)) + down_sample_to_texture(param_4, param_5)) + down_sample_to_texture(param_6, param_7))); } else { @@ -128,114 +147,130 @@ blit_atlas_vs_source_glsl410 := [235]u8 { */ @(private="file") -blit_atlas_fs_source_glsl410 := [1700]u8 { - 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x20,0x69,0x76,0x65,0x63,0x34,0x20,0x62,0x6c,0x69,0x74, - 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, - 0x5b,0x31,0x5d,0x3b,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61, - 0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x62,0x6c, - 0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f, - 0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76, - 0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c, - 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74, - 0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, - 0x3b,0x0a,0x0a,0x66,0x6c,0x6f,0x61,0x74,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61, - 0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x5f,0x31,0x2c,0x20, - 0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a, - 0x65,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, - 0x30,0x2e,0x32,0x35,0x20,0x2a,0x20,0x28,0x28,0x28,0x74,0x65,0x78,0x74,0x75,0x72, - 0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63, - 0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74, - 0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c, - 0x20,0x75,0x76,0x5f,0x31,0x29,0x2e,0x78,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72, - 0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61, +ve_blit_atlas_fs_source_glsl410 := [1954]u8 { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x73,0x74, + 0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c, + 0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20, + 0x20,0x20,0x20,0x76,0x65,0x63,0x32,0x20,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75, + 0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x20,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x67,0x69,0x6f,0x6e, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65, + 0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x20,0x5f,0x32,0x30,0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66, + 0x6f,0x72,0x6d,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x76,0x65, + 0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f, + 0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x72,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74, + 0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32, + 0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61, + 0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65, + 0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a, + 0x66,0x6c,0x6f,0x61,0x74,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76,0x65,0x63, + 0x32,0x20,0x75,0x76,0x5f,0x31,0x2c,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20, + 0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x28,0x31,0x2e,0x30,0x20,0x2f,0x20,0x5f, + 0x32,0x30,0x2e,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x29,0x20, + 0x2a,0x20,0x28,0x28,0x28,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76,0x65,0x5f, + 0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61, 0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x66,0x6d,0x61,0x28,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x2c,0x20, - 0x31,0x2e,0x30,0x29,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69, - 0x7a,0x65,0x2c,0x20,0x75,0x76,0x5f,0x31,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20, - 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c, - 0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x62, - 0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61, - 0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x66,0x6d,0x61,0x28,0x76,0x65,0x63,0x32,0x28, - 0x31,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x29,0x2c,0x20,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x75,0x76,0x5f,0x31,0x29,0x29,0x2e, - 0x78,0x29,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x62,0x6c,0x69, + 0x2c,0x20,0x75,0x76,0x5f,0x31,0x29,0x2e,0x78,0x20,0x2b,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61, + 0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65, + 0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f, + 0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x66,0x6d,0x61,0x28,0x76,0x65,0x63, + 0x32,0x28,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x2c,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x75,0x76,0x5f,0x31,0x29, + 0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76, + 0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63, + 0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74, + 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x72,0x2c,0x20,0x66,0x6d,0x61,0x28,0x76,0x65,0x63,0x32,0x28,0x31,0x2e,0x30, + 0x2c,0x20,0x30,0x2e,0x30,0x29,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f, + 0x73,0x69,0x7a,0x65,0x2c,0x20,0x75,0x76,0x5f,0x31,0x29,0x29,0x2e,0x78,0x29,0x20, + 0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69, 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74, - 0x75,0x72,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73, - 0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x5f,0x31, - 0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x29, - 0x2e,0x78,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69, - 0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f, - 0x39,0x33,0x20,0x3d,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f, - 0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x20,0x3d, - 0x3d,0x20,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31, - 0x30,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x39,0x33, + 0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61, + 0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75, + 0x76,0x5f,0x31,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69, + 0x7a,0x65,0x29,0x2e,0x78,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20, + 0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x76,0x65,0x63, + 0x32,0x20,0x5f,0x39,0x38,0x20,0x3d,0x20,0x76,0x65,0x63,0x32,0x28,0x31,0x2e,0x30, + 0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75, + 0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x62, + 0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x30,0x34,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x72, + 0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x31,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69, + 0x66,0x20,0x28,0x21,0x5f,0x31,0x30,0x34,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x31,0x20,0x3d,0x20,0x5f, + 0x32,0x30,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x31,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20, + 0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31, + 0x31,0x20,0x3d,0x20,0x5f,0x31,0x30,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a, + 0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x31,0x38,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x31,0x31,0x31,0x29,0x0a,0x20,0x20, + 0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x38, + 0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d, + 0x20,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c, + 0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x5f,0x31,0x31,0x38,0x20,0x3d,0x20,0x5f,0x31,0x31,0x31,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x32, + 0x36,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x31,0x31,0x38, 0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x5f,0x31,0x30,0x31,0x20,0x3d,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61, - 0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78, - 0x20,0x3d,0x3d,0x20,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20, - 0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x5f,0x31,0x30,0x31,0x20,0x3d,0x20,0x5f,0x39,0x33,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f, - 0x31,0x30,0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x31, - 0x30,0x31,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x5f,0x31,0x30,0x39,0x20,0x3d,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74, - 0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d, - 0x2e,0x78,0x20,0x3d,0x3d,0x20,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20, - 0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x30,0x39,0x20,0x3d,0x20,0x5f,0x31,0x30, - 0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20, - 0x28,0x5f,0x31,0x30,0x39,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x20, - 0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x2d,0x30,0x2e,0x30, - 0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c,0x20,0x2d,0x30,0x2e,0x30, - 0x30,0x32,0x39,0x32,0x39,0x36,0x38,0x37,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31, - 0x20,0x3d,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x34,0x38,0x38, - 0x32,0x38,0x31,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x31,0x39,0x35,0x33,0x31, - 0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x65,0x63, - 0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b, - 0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34, - 0x30,0x36,0x32,0x35,0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30,0x32,0x39,0x32,0x39,0x36, - 0x38,0x37,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x65, - 0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x20,0x3d,0x20,0x76,0x65,0x63, - 0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c, - 0x20,0x30,0x2e,0x30,0x30,0x31,0x39,0x35,0x33,0x31,0x32,0x35,0x29,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61, - 0x6d,0x5f,0x34,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28, - 0x2d,0x30,0x2e,0x30,0x30,0x30,0x37,0x33,0x32,0x34,0x32,0x31,0x38,0x37,0x35,0x2c, - 0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72, - 0x61,0x6d,0x5f,0x35,0x20,0x3d,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x30, - 0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x31, - 0x39,0x35,0x33,0x31,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x20,0x3d,0x20, - 0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x32, - 0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x30,0x39, - 0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x20,0x3d,0x20, - 0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31, - 0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x31,0x39,0x35,0x33,0x31,0x32,0x35,0x29, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63, - 0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x31,0x2e,0x30,0x2c, - 0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x32,0x35,0x20, - 0x2a,0x20,0x28,0x28,0x28,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x28,0x70,0x61,0x72,0x61,0x6d,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31,0x29, - 0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x70, - 0x61,0x72,0x61,0x6d,0x5f,0x32,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x29, - 0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28, - 0x70,0x61,0x72,0x61,0x6d,0x5f,0x34,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35, - 0x29,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f, - 0x37,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20, - 0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20, - 0x76,0x65,0x63,0x34,0x28,0x30,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x30, - 0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a, - 0x7d,0x0a,0x0a,0x00, + 0x5f,0x31,0x32,0x36,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x72,0x65,0x67,0x69,0x6f, + 0x6e,0x20,0x3d,0x3d,0x20,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20, + 0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x32,0x36,0x20,0x3d,0x20,0x5f,0x31,0x31,0x38, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28, + 0x5f,0x31,0x32,0x36,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x20,0x3d, + 0x20,0x75,0x76,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x28,0x2d,0x31,0x2e,0x30, + 0x2c,0x20,0x2d,0x31,0x2e,0x35,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x67,0x6c, + 0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x29, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x32,0x20,0x70, + 0x61,0x72,0x61,0x6d,0x5f,0x31,0x20,0x3d,0x20,0x5f,0x39,0x38,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d, + 0x5f,0x32,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x28, + 0x30,0x2e,0x35,0x2c,0x20,0x2d,0x31,0x2e,0x35,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30, + 0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69, + 0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x65,0x63, + 0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x20,0x3d,0x20,0x5f,0x39,0x38,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61, + 0x72,0x61,0x6d,0x5f,0x34,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x28,0x76,0x65, + 0x63,0x32,0x28,0x2d,0x31,0x2e,0x35,0x2c,0x20,0x30,0x2e,0x35,0x29,0x20,0x2f,0x20, + 0x5f,0x32,0x30,0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72, + 0x5f,0x73,0x69,0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x20,0x3d,0x20,0x5f, + 0x39,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x32, + 0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20, + 0x28,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x35,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30, + 0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69, + 0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x76,0x65,0x63, + 0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x20,0x3d,0x20,0x5f,0x39,0x38,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x31,0x2e,0x30,0x2c,0x20, + 0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x28,0x31,0x2e,0x30,0x20,0x2f, + 0x20,0x5f,0x32,0x30,0x2e,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x29,0x20,0x2a,0x20,0x28,0x28,0x28,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70, + 0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70,0x61, + 0x72,0x61,0x6d,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31,0x29,0x20,0x2b,0x20, + 0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x2c,0x20, + 0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x29,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e, + 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x34,0x2c,0x20,0x70,0x61,0x72,0x61, + 0x6d,0x5f,0x35,0x29,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70, + 0x61,0x72,0x61,0x6d,0x5f,0x36,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x29, + 0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c, + 0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65, + 0x63,0x34,0x28,0x30,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30, + 0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x7d,0x0a, + 0x0a,0x00, } /* #version 300 es @@ -252,7 +287,7 @@ blit_atlas_fs_source_glsl410 := [1700]u8 { */ @(private="file") -blit_atlas_vs_source_glsl300es := [217]u8 { +ve_blit_atlas_vs_source_glsl300es := [217]u8 { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, 0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61, 0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, @@ -273,49 +308,67 @@ blit_atlas_vs_source_glsl300es := [217]u8 { precision mediump float; precision highp int; - uniform ivec4 blit_atlas_fs_params[1]; - uniform highp sampler2D blit_atlas_src_texture_blit_atlas_src_sampler; + struct ve_blit_atlas_fs_params + { + highp vec2 glyph_buffer_size; + highp float over_sample; + int region; + }; + + uniform ve_blit_atlas_fs_params _20; + + uniform highp sampler2D ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler; in highp vec2 uv; layout(location = 0) out highp vec4 frag_color; - highp float down_sample(highp vec2 uv_1, highp vec2 texture_size) + highp float down_sample_to_texture(highp vec2 uv_1, highp vec2 texture_size) { - return 0.25 * (((texture(blit_atlas_src_texture_blit_atlas_src_sampler, uv_1).x + texture(blit_atlas_src_texture_blit_atlas_src_sampler, vec2(0.0, 1.0) * texture_size + uv_1).x) + texture(blit_atlas_src_texture_blit_atlas_src_sampler, vec2(1.0, 0.0) * texture_size + uv_1).x) + texture(blit_atlas_src_texture_blit_atlas_src_sampler, uv_1 + texture_size).x); + return (1.0 / _20.over_sample) * (((texture(ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler, uv_1).x + texture(ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler, vec2(0.0, 1.0) * texture_size + uv_1).x) + texture(ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler, vec2(1.0, 0.0) * texture_size + uv_1).x) + texture(ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler, uv_1 + texture_size).x); } void main() { - bool _93 = blit_atlas_fs_params[0].x == 0; - bool _101; - if (!_93) + highp vec2 _98 = vec2(1.0) / _20.glyph_buffer_size; + bool _104 = _20.region == 0; + bool _111; + if (!_104) + { + _111 = _20.region == 1; + } + else { - _101 = blit_atlas_fs_params[0].x == 1; + _111 = _104; + } + bool _118; + if (!_111) + { + _118 = _20.region == 2; } else { - _101 = _93; + _118 = _111; } - bool _109; - if (!_101) + bool _126; + if (!_118) { - _109 = blit_atlas_fs_params[0].x == 2; + _126 = _20.region == 4; } else { - _109 = _101; + _126 = _118; } - if (_109) + if (_126) { - highp vec2 param = uv + vec2(-0.00048828125, -0.0029296875); - highp vec2 param_1 = vec2(0.00048828125, 0.001953125); - highp vec2 param_2 = uv + vec2(0.000244140625, -0.0029296875); - highp vec2 param_3 = vec2(0.00048828125, 0.001953125); - highp vec2 param_4 = uv + vec2(-0.000732421875, 0.0009765625); - highp vec2 param_5 = vec2(0.00048828125, 0.001953125); - highp vec2 param_6 = uv + vec2(0.000244140625, 0.0009765625); - highp vec2 param_7 = vec2(0.00048828125, 0.001953125); - frag_color = vec4(1.0, 1.0, 1.0, 0.25 * (((down_sample(param, param_1) + down_sample(param_2, param_3)) + down_sample(param_4, param_5)) + down_sample(param_6, param_7))); + highp vec2 param = uv + (vec2(-1.0, -1.5) / _20.glyph_buffer_size); + highp vec2 param_1 = _98; + highp vec2 param_2 = uv + (vec2(0.5, -1.5) / _20.glyph_buffer_size); + highp vec2 param_3 = _98; + highp vec2 param_4 = uv + (vec2(-1.5, 0.5) / _20.glyph_buffer_size); + highp vec2 param_5 = _98; + highp vec2 param_6 = uv + (vec2(0.5) / _20.glyph_buffer_size); + highp vec2 param_7 = _98; + frag_color = vec4(1.0, 1.0, 1.0, (1.0 / _20.over_sample) * (((down_sample_to_texture(param, param_1) + down_sample_to_texture(param_2, param_3)) + down_sample_to_texture(param_4, param_5)) + down_sample_to_texture(param_6, param_7))); } else { @@ -325,114 +378,131 @@ blit_atlas_vs_source_glsl300es := [217]u8 { */ @(private="file") -blit_atlas_fs_source_glsl300es := [1806]u8 { +ve_blit_atlas_fs_source_glsl300es := [2078]u8 { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, - 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, - 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x69,0x76,0x65,0x63,0x34,0x20,0x62,0x6c,0x69, - 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, - 0x73,0x5b,0x31,0x5d,0x3b,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69, - 0x67,0x68,0x70,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x62,0x6c, - 0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78, - 0x74,0x75,0x72,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f, - 0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x69,0x6e, - 0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a, - 0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20, - 0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76, - 0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a, - 0x0a,0x68,0x69,0x67,0x68,0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x64,0x6f,0x77, - 0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x68,0x69,0x67,0x68,0x70,0x20,0x76, - 0x65,0x63,0x32,0x20,0x75,0x76,0x5f,0x31,0x2c,0x20,0x68,0x69,0x67,0x68,0x70,0x20, - 0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a, - 0x65,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, - 0x30,0x2e,0x32,0x35,0x20,0x2a,0x20,0x28,0x28,0x28,0x74,0x65,0x78,0x74,0x75,0x72, - 0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63, - 0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74, - 0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c, - 0x20,0x75,0x76,0x5f,0x31,0x29,0x2e,0x78,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72, - 0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61, - 0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29, - 0x20,0x2a,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x20, - 0x2b,0x20,0x75,0x76,0x5f,0x31,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x74,0x65,0x78, - 0x74,0x75,0x72,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f, - 0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x62,0x6c,0x69,0x74, - 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c, - 0x65,0x72,0x2c,0x20,0x76,0x65,0x63,0x32,0x28,0x31,0x2e,0x30,0x2c,0x20,0x30,0x2e, - 0x30,0x29,0x20,0x2a,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a, - 0x65,0x20,0x2b,0x20,0x75,0x76,0x5f,0x31,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x74, - 0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61, - 0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x62,0x6c, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74, + 0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x67, + 0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x20,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x67,0x69,0x6f,0x6e,0x3b,0x0a,0x7d, + 0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x5f,0x62,0x6c, + 0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x20,0x5f,0x32,0x30,0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d, + 0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44, + 0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73, + 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x62,0x6c, 0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x5f,0x31,0x20,0x2b,0x20,0x74,0x65,0x78, - 0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x29,0x2e,0x78,0x29,0x3b,0x0a,0x7d, - 0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x39,0x33,0x20,0x3d,0x20,0x62, - 0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72, - 0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x30,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x30,0x31,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x39,0x33,0x29,0x0a,0x20,0x20,0x20,0x20, - 0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x30,0x31,0x20,0x3d, - 0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x20,0x3d,0x3d,0x20,0x31,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a, - 0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31, - 0x30,0x31,0x20,0x3d,0x20,0x5f,0x39,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a, - 0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x30,0x39,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x31,0x30,0x31,0x29,0x0a,0x20,0x20, - 0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x30,0x39, - 0x20,0x3d,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73, - 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x20,0x3d,0x3d,0x20, - 0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73, - 0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x5f,0x31,0x30,0x39,0x20,0x3d,0x20,0x5f,0x31,0x30,0x31,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f,0x31,0x30,0x39,0x29, - 0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68, - 0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x20, - 0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x2d,0x30,0x2e,0x30, - 0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c,0x20,0x2d,0x30,0x2e,0x30, - 0x30,0x32,0x39,0x32,0x39,0x36,0x38,0x37,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x70, - 0x61,0x72,0x61,0x6d,0x5f,0x31,0x20,0x3d,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e, - 0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30, - 0x30,0x31,0x39,0x35,0x33,0x31,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61, - 0x72,0x61,0x6d,0x5f,0x32,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63, - 0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35, - 0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30,0x32,0x39,0x32,0x39,0x36,0x38,0x37,0x35,0x29, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20, - 0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x20,0x3d,0x20,0x76, - 0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32, - 0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x31,0x39,0x35,0x33,0x31,0x32,0x35,0x29,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76, - 0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x34,0x20,0x3d,0x20,0x75,0x76, - 0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x2d,0x30,0x2e,0x30,0x30,0x30,0x37,0x33, - 0x32,0x34,0x32,0x31,0x38,0x37,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37, - 0x36,0x35,0x36,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28, + 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75, + 0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61, + 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x68,0x69,0x67,0x68,0x70,0x20, + 0x66,0x6c,0x6f,0x61,0x74,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x68,0x69,0x67, + 0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x5f,0x31,0x2c,0x20,0x68,0x69, + 0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x5f,0x73,0x69,0x7a,0x65,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74, + 0x75,0x72,0x6e,0x20,0x28,0x31,0x2e,0x30,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x6f, + 0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x29,0x20,0x2a,0x20,0x28,0x28, + 0x28,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74, + 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x5f,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73, + 0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76, + 0x5f,0x31,0x29,0x2e,0x78,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72, + 0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x62,0x6c,0x69, + 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70, + 0x6c,0x65,0x72,0x2c,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x2c,0x20,0x31, + 0x2e,0x30,0x29,0x20,0x2a,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69, + 0x7a,0x65,0x20,0x2b,0x20,0x75,0x76,0x5f,0x31,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f, + 0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72, + 0x65,0x5f,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f, + 0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x76,0x65,0x63, + 0x32,0x28,0x31,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x29,0x20,0x2a,0x20,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x20,0x2b,0x20,0x75,0x76,0x5f, + 0x31,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28, + 0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72, + 0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x62,0x6c,0x69, + 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70, + 0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x5f,0x31,0x20,0x2b,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x29,0x2e,0x78,0x29,0x3b,0x0a,0x7d,0x0a, + 0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20, + 0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x5f,0x39, + 0x38,0x20,0x3d,0x20,0x76,0x65,0x63,0x32,0x28,0x31,0x2e,0x30,0x29,0x20,0x2f,0x20, + 0x5f,0x32,0x30,0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72, + 0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20, + 0x5f,0x31,0x30,0x34,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x72,0x65,0x67,0x69,0x6f, + 0x6e,0x20,0x3d,0x3d,0x20,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c, + 0x20,0x5f,0x31,0x31,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21, + 0x5f,0x31,0x30,0x34,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x31,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x72, + 0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x31,0x20,0x3d,0x20, + 0x5f,0x31,0x30,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20, + 0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x31,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69, + 0x66,0x20,0x28,0x21,0x5f,0x31,0x31,0x31,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x38,0x20,0x3d,0x20,0x5f, + 0x32,0x30,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x32,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20, + 0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31, + 0x38,0x20,0x3d,0x20,0x5f,0x31,0x31,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a, + 0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x32,0x36,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x31,0x31,0x38,0x29,0x0a,0x20,0x20, + 0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x32,0x36, + 0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d, + 0x20,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c, + 0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x5f,0x31,0x32,0x36,0x20,0x3d,0x20,0x5f,0x31,0x31,0x38,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f,0x31,0x32,0x36, + 0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, 0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d, - 0x5f,0x35,0x20,0x3d,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x34, - 0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x31,0x39,0x35, - 0x33,0x31,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68, - 0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f, - 0x36,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e, - 0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x2c,0x20,0x30,0x2e, - 0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20, - 0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x20,0x3d,0x20,0x76,0x65,0x63,0x32,0x28,0x30, - 0x2e,0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c,0x20,0x30,0x2e, - 0x30,0x30,0x31,0x39,0x35,0x33,0x31,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, - 0x20,0x76,0x65,0x63,0x34,0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20, - 0x31,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x32,0x35,0x20,0x2a,0x20,0x28,0x28,0x28,0x64, - 0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61,0x6d, - 0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77, - 0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32, - 0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x29,0x29,0x20,0x2b,0x20,0x64,0x6f, - 0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f, - 0x34,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x29,0x29,0x20,0x2b,0x20,0x64, - 0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61,0x6d, + 0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x28,0x2d,0x31, + 0x2e,0x30,0x2c,0x20,0x2d,0x31,0x2e,0x35,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e, + 0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a, + 0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68, + 0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31,0x20,0x3d, + 0x20,0x5f,0x39,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x69, + 0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32, + 0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x28,0x30,0x2e, + 0x35,0x2c,0x20,0x2d,0x31,0x2e,0x35,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x67, + 0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65, + 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70, + 0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x20,0x3d,0x20, + 0x5f,0x39,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x69,0x67, + 0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x34,0x20, + 0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x28,0x2d,0x31,0x2e, + 0x35,0x2c,0x20,0x30,0x2e,0x35,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x67,0x6c, + 0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x29, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20, + 0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x20,0x3d,0x20,0x5f, + 0x39,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68, + 0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x20,0x3d, + 0x20,0x75,0x76,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x35,0x29, + 0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66, + 0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x70,0x61, + 0x72,0x61,0x6d,0x5f,0x37,0x20,0x3d,0x20,0x5f,0x39,0x38,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20, + 0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c, + 0x20,0x31,0x2e,0x30,0x2c,0x20,0x28,0x31,0x2e,0x30,0x20,0x2f,0x20,0x5f,0x32,0x30, + 0x2e,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x29,0x20,0x2a,0x20, + 0x28,0x28,0x28,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74, + 0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x2c, + 0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e, + 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x2c,0x20,0x70,0x61,0x72,0x61, + 0x6d,0x5f,0x33,0x29,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70, + 0x61,0x72,0x61,0x6d,0x5f,0x34,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x29, + 0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f, + 0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70,0x61,0x72,0x61,0x6d, 0x5f,0x36,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x29,0x29,0x29,0x3b,0x0a, 0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20, 0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x72,0x61, @@ -476,7 +546,7 @@ blit_atlas_fs_source_glsl300es := [1806]u8 { } */ @(private="file") -blit_atlas_vs_source_hlsl4 := [705]u8 { +ve_blit_atlas_vs_source_hlsl4 := [705]u8 { 0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x6c, 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x73,0x74,0x61,0x74,0x69, 0x63,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x3b,0x0a,0x73,0x74,0x61, @@ -524,13 +594,15 @@ blit_atlas_vs_source_hlsl4 := [705]u8 { 0x00, } /* - cbuffer blit_atlas_fs_params : register(b0) + cbuffer ve_blit_atlas_fs_params : register(b0) { - int _88_region : packoffset(c0); + float2 _20_glyph_buffer_size : packoffset(c0); + float _20_over_sample : packoffset(c0.z); + int _20_region : packoffset(c0.w); }; - Texture2D blit_atlas_src_texture : register(t0); - SamplerState blit_atlas_src_sampler : register(s0); + Texture2D ve_blit_atlas_src_texture : register(t0); + SamplerState ve_blit_atlas_src_sampler : register(s0); static float2 uv; static float4 frag_color; @@ -545,43 +617,53 @@ blit_atlas_vs_source_hlsl4 := [705]u8 { float4 frag_color : SV_Target0; }; - float down_sample(float2 uv_1, float2 texture_size) + float down_sample_to_texture(float2 uv_1, float2 texture_size) { - return 0.25f * (((blit_atlas_src_texture.Sample(blit_atlas_src_sampler, uv_1).x + blit_atlas_src_texture.Sample(blit_atlas_src_sampler, mad(float2(0.0f, 1.0f), texture_size, uv_1)).x) + blit_atlas_src_texture.Sample(blit_atlas_src_sampler, mad(float2(1.0f, 0.0f), texture_size, uv_1)).x) + blit_atlas_src_texture.Sample(blit_atlas_src_sampler, uv_1 + texture_size).x); + return (1.0f / _20_over_sample) * (((ve_blit_atlas_src_texture.Sample(ve_blit_atlas_src_sampler, uv_1).x + ve_blit_atlas_src_texture.Sample(ve_blit_atlas_src_sampler, mad(float2(0.0f, 1.0f), texture_size, uv_1)).x) + ve_blit_atlas_src_texture.Sample(ve_blit_atlas_src_sampler, mad(float2(1.0f, 0.0f), texture_size, uv_1)).x) + ve_blit_atlas_src_texture.Sample(ve_blit_atlas_src_sampler, uv_1 + texture_size).x); } void frag_main() { - bool _93 = _88_region == 0; - bool _101; - if (!_93) + float2 _98 = 1.0f.xx / _20_glyph_buffer_size; + bool _104 = _20_region == 0; + bool _111; + if (!_104) + { + _111 = _20_region == 1; + } + else { - _101 = _88_region == 1; + _111 = _104; + } + bool _118; + if (!_111) + { + _118 = _20_region == 2; } else { - _101 = _93; + _118 = _111; } - bool _109; - if (!_101) + bool _126; + if (!_118) { - _109 = _88_region == 2; + _126 = _20_region == 4; } else { - _109 = _101; + _126 = _118; } - if (_109) + if (_126) { - float2 param = uv + float2(-0.00048828125f, -0.0029296875f); - float2 param_1 = float2(0.00048828125f, 0.001953125f); - float2 param_2 = uv + float2(0.000244140625f, -0.0029296875f); - float2 param_3 = float2(0.00048828125f, 0.001953125f); - float2 param_4 = uv + float2(-0.000732421875f, 0.0009765625f); - float2 param_5 = float2(0.00048828125f, 0.001953125f); - float2 param_6 = uv + float2(0.000244140625f, 0.0009765625f); - float2 param_7 = float2(0.00048828125f, 0.001953125f); - frag_color = float4(1.0f, 1.0f, 1.0f, 0.25f * (((down_sample(param, param_1) + down_sample(param_2, param_3)) + down_sample(param_4, param_5)) + down_sample(param_6, param_7))); + float2 param = uv + (float2(-1.0f, -1.5f) / _20_glyph_buffer_size); + float2 param_1 = _98; + float2 param_2 = uv + (float2(0.5f, -1.5f) / _20_glyph_buffer_size); + float2 param_3 = _98; + float2 param_4 = uv + (float2(-1.5f, 0.5f) / _20_glyph_buffer_size); + float2 param_5 = _98; + float2 param_6 = uv + (0.5f.xx / _20_glyph_buffer_size); + float2 param_7 = _98; + frag_color = float4(1.0f, 1.0f, 1.0f, (1.0f / _20_over_sample) * (((down_sample_to_texture(param, param_1) + down_sample_to_texture(param_2, param_3)) + down_sample_to_texture(param_4, param_5)) + down_sample_to_texture(param_6, param_7))); } else { @@ -599,139 +681,156 @@ blit_atlas_vs_source_hlsl4 := [705]u8 { } */ @(private="file") -blit_atlas_fs_source_hlsl4 := [2107]u8 { - 0x63,0x62,0x75,0x66,0x66,0x65,0x72,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c, - 0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x3a,0x20,0x72, - 0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x28,0x62,0x30,0x29,0x0a,0x7b,0x0a,0x20,0x20, - 0x20,0x20,0x69,0x6e,0x74,0x20,0x5f,0x38,0x38,0x5f,0x72,0x65,0x67,0x69,0x6f,0x6e, - 0x20,0x3a,0x20,0x70,0x61,0x63,0x6b,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x63,0x30, - 0x29,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x44, - 0x3c,0x66,0x6c,0x6f,0x61,0x74,0x34,0x3e,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74, - 0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x20, - 0x3a,0x20,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x28,0x74,0x30,0x29,0x3b,0x0a, - 0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x53,0x74,0x61,0x74,0x65,0x20,0x62,0x6c,0x69, - 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70, - 0x6c,0x65,0x72,0x20,0x3a,0x20,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x28,0x73, - 0x30,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x32,0x20,0x75,0x76,0x3b,0x0a,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b, - 0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x53,0x50,0x49,0x52,0x56,0x5f,0x43, - 0x72,0x6f,0x73,0x73,0x5f,0x49,0x6e,0x70,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x20,0x3a,0x20,0x54,0x45,0x58, - 0x43,0x4f,0x4f,0x52,0x44,0x30,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75, - 0x63,0x74,0x20,0x53,0x50,0x49,0x52,0x56,0x5f,0x43,0x72,0x6f,0x73,0x73,0x5f,0x4f, - 0x75,0x74,0x70,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, - 0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20, - 0x53,0x56,0x5f,0x54,0x61,0x72,0x67,0x65,0x74,0x30,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a, - 0x66,0x6c,0x6f,0x61,0x74,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c, - 0x65,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x5f,0x31,0x2c,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x32,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69, - 0x7a,0x65,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e, - 0x20,0x30,0x2e,0x32,0x35,0x66,0x20,0x2a,0x20,0x28,0x28,0x28,0x62,0x6c,0x69,0x74, - 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61, - 0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x75,0x76,0x5f,0x31,0x29,0x2e,0x78,0x20,0x2b,0x20,0x62,0x6c,0x69,0x74, - 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61, - 0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x6d,0x61,0x64,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30, - 0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72, - 0x65,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x75,0x76,0x5f,0x31,0x29,0x29,0x2e,0x78, - 0x29,0x20,0x2b,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73, - 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c, - 0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63, - 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x6d,0x61,0x64,0x28,0x66,0x6c, - 0x6f,0x61,0x74,0x32,0x28,0x31,0x2e,0x30,0x66,0x2c,0x20,0x30,0x2e,0x30,0x66,0x29, - 0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20, - 0x75,0x76,0x5f,0x31,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x62,0x6c,0x69,0x74, - 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61, +ve_blit_atlas_fs_source_hlsl4 := [2383]u8 { + 0x63,0x62,0x75,0x66,0x66,0x65,0x72,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f, + 0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20, + 0x3a,0x20,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x28,0x62,0x30,0x29,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x5f,0x32,0x30,0x5f, + 0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a, + 0x65,0x20,0x3a,0x20,0x70,0x61,0x63,0x6b,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x63, + 0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x32, + 0x30,0x5f,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x3a,0x20, + 0x70,0x61,0x63,0x6b,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x63,0x30,0x2e,0x7a,0x29, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x5f,0x32,0x30,0x5f,0x72,0x65, + 0x67,0x69,0x6f,0x6e,0x20,0x3a,0x20,0x70,0x61,0x63,0x6b,0x6f,0x66,0x66,0x73,0x65, + 0x74,0x28,0x63,0x30,0x2e,0x77,0x29,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x54,0x65,0x78, + 0x74,0x75,0x72,0x65,0x32,0x44,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x34,0x3e,0x20,0x76, + 0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63, + 0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x20,0x3a,0x20,0x72,0x65,0x67,0x69,0x73, + 0x74,0x65,0x72,0x28,0x74,0x30,0x29,0x3b,0x0a,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72, + 0x53,0x74,0x61,0x74,0x65,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74, + 0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20, + 0x3a,0x20,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x28,0x73,0x30,0x29,0x3b,0x0a, + 0x0a,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75, + 0x76,0x3b,0x0a,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x73,0x74, + 0x72,0x75,0x63,0x74,0x20,0x53,0x50,0x49,0x52,0x56,0x5f,0x43,0x72,0x6f,0x73,0x73, + 0x5f,0x49,0x6e,0x70,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, + 0x61,0x74,0x32,0x20,0x75,0x76,0x20,0x3a,0x20,0x54,0x45,0x58,0x43,0x4f,0x4f,0x52, + 0x44,0x30,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x53, + 0x50,0x49,0x52,0x56,0x5f,0x43,0x72,0x6f,0x73,0x73,0x5f,0x4f,0x75,0x74,0x70,0x75, + 0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x66, + 0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x53,0x56,0x5f,0x54, + 0x61,0x72,0x67,0x65,0x74,0x30,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x66,0x6c,0x6f,0x61, + 0x74,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f, + 0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20, + 0x75,0x76,0x5f,0x31,0x2c,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20, + 0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2f,0x20, + 0x5f,0x32,0x30,0x5f,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x29, + 0x20,0x2a,0x20,0x28,0x28,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74, + 0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e, + 0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61, 0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x75,0x76,0x5f,0x31,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, - 0x5f,0x73,0x69,0x7a,0x65,0x29,0x2e,0x78,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x76,0x6f, - 0x69,0x64,0x20,0x66,0x72,0x61,0x67,0x5f,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b, - 0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x39,0x33,0x20,0x3d,0x20, - 0x5f,0x38,0x38,0x5f,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x30,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x30,0x31,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x39,0x33,0x29,0x0a,0x20,0x20, - 0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x30,0x31, - 0x20,0x3d,0x20,0x5f,0x38,0x38,0x5f,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d, - 0x20,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c, - 0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x5f,0x31,0x30,0x31,0x20,0x3d,0x20,0x5f,0x39,0x33,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x30,0x39, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x31,0x30,0x31,0x29, + 0x2c,0x20,0x75,0x76,0x5f,0x31,0x29,0x2e,0x78,0x20,0x2b,0x20,0x76,0x65,0x5f,0x62, + 0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f, + 0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x6d,0x61,0x64,0x28,0x66,0x6c,0x6f,0x61, + 0x74,0x32,0x28,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x2c,0x20, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x75,0x76, + 0x5f,0x31,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69, + 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c, + 0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x2c,0x20,0x6d,0x61,0x64,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32, + 0x28,0x31,0x2e,0x30,0x66,0x2c,0x20,0x30,0x2e,0x30,0x66,0x29,0x2c,0x20,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x75,0x76,0x5f,0x31, + 0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f, + 0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72, + 0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74, + 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x72,0x2c,0x20,0x75,0x76,0x5f,0x31,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x29,0x2e,0x78,0x29,0x3b,0x0a,0x7d,0x0a,0x0a, + 0x76,0x6f,0x69,0x64,0x20,0x66,0x72,0x61,0x67,0x5f,0x6d,0x61,0x69,0x6e,0x28,0x29, + 0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x5f,0x39, + 0x38,0x20,0x3d,0x20,0x31,0x2e,0x30,0x66,0x2e,0x78,0x78,0x20,0x2f,0x20,0x5f,0x32, + 0x30,0x5f,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73, + 0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31, + 0x30,0x34,0x20,0x3d,0x20,0x5f,0x32,0x30,0x5f,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20, + 0x3d,0x3d,0x20,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f, + 0x31,0x31,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x31, + 0x30,0x34,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x5f,0x31,0x31,0x31,0x20,0x3d,0x20,0x5f,0x32,0x30,0x5f,0x72,0x65,0x67, + 0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a, + 0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x31,0x20,0x3d,0x20,0x5f,0x31, + 0x30,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f, + 0x6f,0x6c,0x20,0x5f,0x31,0x31,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20, + 0x28,0x21,0x5f,0x31,0x31,0x31,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x38,0x20,0x3d,0x20,0x5f,0x32,0x30, + 0x5f,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x32,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20, + 0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x38,0x20, + 0x3d,0x20,0x5f,0x31,0x31,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20, + 0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x32,0x36,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x31,0x31,0x38,0x29,0x0a,0x20,0x20,0x20,0x20, + 0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x32,0x36,0x20,0x3d, + 0x20,0x5f,0x32,0x30,0x5f,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x34, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65, 0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f, - 0x31,0x30,0x39,0x20,0x3d,0x20,0x5f,0x38,0x38,0x5f,0x72,0x65,0x67,0x69,0x6f,0x6e, - 0x20,0x3d,0x3d,0x20,0x32,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20, - 0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x5f,0x31,0x30,0x39,0x20,0x3d,0x20,0x5f,0x31,0x30,0x31,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f, - 0x31,0x30,0x39,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x20, - 0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x30, - 0x2e,0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x66,0x2c,0x20,0x2d, - 0x30,0x2e,0x30,0x30,0x32,0x39,0x32,0x39,0x36,0x38,0x37,0x35,0x66,0x29,0x3b,0x0a, + 0x31,0x32,0x36,0x20,0x3d,0x20,0x5f,0x31,0x31,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x7d,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f,0x31,0x32,0x36,0x29,0x0a, + 0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x20,0x3d,0x20,0x75,0x76,0x20, + 0x2b,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x31,0x2e,0x30,0x66,0x2c, + 0x20,0x2d,0x31,0x2e,0x35,0x66,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x5f,0x67,0x6c, + 0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x29, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32, + 0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31,0x20,0x3d,0x20,0x5f,0x39,0x38,0x3b,0x0a, 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70, - 0x61,0x72,0x61,0x6d,0x5f,0x31,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28, - 0x30,0x2e,0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x66,0x2c,0x20, - 0x30,0x2e,0x30,0x30,0x31,0x39,0x35,0x33,0x31,0x32,0x35,0x66,0x29,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61, - 0x72,0x61,0x6d,0x5f,0x32,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36, - 0x32,0x35,0x66,0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30,0x32,0x39,0x32,0x39,0x36,0x38, - 0x37,0x35,0x66,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x20,0x3d,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38, - 0x31,0x32,0x35,0x66,0x2c,0x20,0x30,0x2e,0x30,0x30,0x31,0x39,0x35,0x33,0x31,0x32, - 0x35,0x66,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x34,0x20,0x3d,0x20,0x75,0x76, - 0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x30,0x2e,0x30,0x30,0x30, - 0x37,0x33,0x32,0x34,0x32,0x31,0x38,0x37,0x35,0x66,0x2c,0x20,0x30,0x2e,0x30,0x30, - 0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d, - 0x5f,0x35,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x30, - 0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x66,0x2c,0x20,0x30,0x2e,0x30,0x30, - 0x31,0x39,0x35,0x33,0x31,0x32,0x35,0x66,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, + 0x61,0x72,0x61,0x6d,0x5f,0x32,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x28,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x35,0x66,0x2c,0x20,0x2d,0x31,0x2e,0x35, + 0x66,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x5f,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62, + 0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61, + 0x6d,0x5f,0x33,0x20,0x3d,0x20,0x5f,0x39,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20, 0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f, - 0x36,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28, - 0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x66,0x2c, - 0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66,0x29,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20, - 0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32, - 0x28,0x30,0x2e,0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x66,0x2c, - 0x20,0x30,0x2e,0x30,0x30,0x31,0x39,0x35,0x33,0x31,0x32,0x35,0x66,0x29,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, - 0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x31,0x2e,0x30,0x66, - 0x2c,0x20,0x31,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x2c,0x20,0x30,0x2e, - 0x32,0x35,0x66,0x20,0x2a,0x20,0x28,0x28,0x28,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61, - 0x6d,0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x2c,0x20,0x70,0x61,0x72,0x61, - 0x6d,0x5f,0x31,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70, - 0x6c,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x2c,0x20,0x70,0x61,0x72,0x61, - 0x6d,0x5f,0x33,0x29,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x34,0x2c,0x20,0x70,0x61,0x72, - 0x61,0x6d,0x5f,0x35,0x29,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61, - 0x6d,0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x2c,0x20,0x70,0x61, - 0x72,0x61,0x6d,0x5f,0x37,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a, - 0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f, - 0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x30,0x2e,0x30,0x66,0x2c, - 0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30, - 0x66,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x7d,0x0a,0x0a,0x53,0x50,0x49, - 0x52,0x56,0x5f,0x43,0x72,0x6f,0x73,0x73,0x5f,0x4f,0x75,0x74,0x70,0x75,0x74,0x20, - 0x6d,0x61,0x69,0x6e,0x28,0x53,0x50,0x49,0x52,0x56,0x5f,0x43,0x72,0x6f,0x73,0x73, - 0x5f,0x49,0x6e,0x70,0x75,0x74,0x20,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x70, - 0x75,0x74,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x73, - 0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x70,0x75,0x74,0x2e,0x75,0x76,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x6d,0x61,0x69,0x6e,0x28,0x29,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x53,0x50,0x49,0x52,0x56,0x5f,0x43,0x72,0x6f,0x73,0x73,0x5f, - 0x4f,0x75,0x74,0x70,0x75,0x74,0x20,0x73,0x74,0x61,0x67,0x65,0x5f,0x6f,0x75,0x74, - 0x70,0x75,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x73,0x74,0x61,0x67,0x65,0x5f,0x6f, - 0x75,0x74,0x70,0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, - 0x20,0x3d,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x73,0x74,0x61,0x67,0x65,0x5f, - 0x6f,0x75,0x74,0x70,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x00, + 0x34,0x20,0x3d,0x20,0x75,0x76,0x20,0x2b,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32, + 0x28,0x2d,0x31,0x2e,0x35,0x66,0x2c,0x20,0x30,0x2e,0x35,0x66,0x29,0x20,0x2f,0x20, + 0x5f,0x32,0x30,0x5f,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72, + 0x5f,0x73,0x69,0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x20,0x3d, + 0x20,0x5f,0x39,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x20,0x3d,0x20,0x75, + 0x76,0x20,0x2b,0x20,0x28,0x30,0x2e,0x35,0x66,0x2e,0x78,0x78,0x20,0x2f,0x20,0x5f, + 0x32,0x30,0x5f,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f, + 0x73,0x69,0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x20,0x3d,0x20, + 0x5f,0x39,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x72,0x61, + 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, + 0x28,0x31,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30, + 0x66,0x2c,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2f,0x20,0x5f,0x32,0x30,0x5f,0x6f, + 0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x29,0x20,0x2a,0x20,0x28,0x28, + 0x28,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x2c,0x20,0x70, + 0x61,0x72,0x61,0x6d,0x5f,0x31,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f, + 0x33,0x29,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70,0x61,0x72, + 0x61,0x6d,0x5f,0x34,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x29,0x29,0x20, + 0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f, + 0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36, + 0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20, + 0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x30, + 0x2e,0x30,0x66,0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c, + 0x20,0x31,0x2e,0x30,0x66,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x7d,0x0a, + 0x0a,0x53,0x50,0x49,0x52,0x56,0x5f,0x43,0x72,0x6f,0x73,0x73,0x5f,0x4f,0x75,0x74, + 0x70,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x28,0x53,0x50,0x49,0x52,0x56,0x5f,0x43, + 0x72,0x6f,0x73,0x73,0x5f,0x49,0x6e,0x70,0x75,0x74,0x20,0x73,0x74,0x61,0x67,0x65, + 0x5f,0x69,0x6e,0x70,0x75,0x74,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76, + 0x20,0x3d,0x20,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x70,0x75,0x74,0x2e,0x75, + 0x76,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x6d,0x61,0x69,0x6e, + 0x28,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x53,0x50,0x49,0x52,0x56,0x5f,0x43,0x72, + 0x6f,0x73,0x73,0x5f,0x4f,0x75,0x74,0x70,0x75,0x74,0x20,0x73,0x74,0x61,0x67,0x65, + 0x5f,0x6f,0x75,0x74,0x70,0x75,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x73,0x74,0x61, + 0x67,0x65,0x5f,0x6f,0x75,0x74,0x70,0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f, + 0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x73,0x74, + 0x61,0x67,0x65,0x5f,0x6f,0x75,0x74,0x70,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x00, } /* #include @@ -761,7 +860,7 @@ blit_atlas_fs_source_hlsl4 := [2107]u8 { */ @(private="file") -blit_atlas_vs_source_metal_macos := [473]u8 { +ve_blit_atlas_vs_source_metal_macos := [473]u8 { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, @@ -801,8 +900,10 @@ blit_atlas_vs_source_metal_macos := [473]u8 { using namespace metal; - struct blit_atlas_fs_params + struct ve_blit_atlas_fs_params { + float2 glyph_buffer_size; + float over_sample; int region; }; @@ -817,44 +918,54 @@ blit_atlas_vs_source_metal_macos := [473]u8 { }; static inline __attribute__((always_inline)) - float down_sample(thread const float2& uv, thread const float2& texture_size, texture2d blit_atlas_src_texture, sampler blit_atlas_src_sampler) + float down_sample_to_texture(thread const float2& uv, thread const float2& texture_size, constant ve_blit_atlas_fs_params& _20, texture2d ve_blit_atlas_src_texture, sampler ve_blit_atlas_src_sampler) { - return 0.25 * (((blit_atlas_src_texture.sample(blit_atlas_src_sampler, uv).x + blit_atlas_src_texture.sample(blit_atlas_src_sampler, fma(float2(0.0, 1.0), texture_size, uv)).x) + blit_atlas_src_texture.sample(blit_atlas_src_sampler, fma(float2(1.0, 0.0), texture_size, uv)).x) + blit_atlas_src_texture.sample(blit_atlas_src_sampler, (uv + texture_size)).x); + return (1.0 / _20.over_sample) * (((ve_blit_atlas_src_texture.sample(ve_blit_atlas_src_sampler, uv).x + ve_blit_atlas_src_texture.sample(ve_blit_atlas_src_sampler, fma(float2(0.0, 1.0), texture_size, uv)).x) + ve_blit_atlas_src_texture.sample(ve_blit_atlas_src_sampler, fma(float2(1.0, 0.0), texture_size, uv)).x) + ve_blit_atlas_src_texture.sample(ve_blit_atlas_src_sampler, (uv + texture_size)).x); } - fragment main0_out main0(main0_in in [[stage_in]], constant blit_atlas_fs_params& _88 [[buffer(0)]], texture2d blit_atlas_src_texture [[texture(0)]], sampler blit_atlas_src_sampler [[sampler(0)]]) + fragment main0_out main0(main0_in in [[stage_in]], constant ve_blit_atlas_fs_params& _20 [[buffer(0)]], texture2d ve_blit_atlas_src_texture [[texture(0)]], sampler ve_blit_atlas_src_sampler [[sampler(0)]]) { main0_out out = {}; - bool _93 = _88.region == 0; - bool _101; - if (!_93) + float2 _98 = float2(1.0) / _20.glyph_buffer_size; + bool _104 = _20.region == 0; + bool _111; + if (!_104) + { + _111 = _20.region == 1; + } + else { - _101 = _88.region == 1; + _111 = _104; + } + bool _118; + if (!_111) + { + _118 = _20.region == 2; } else { - _101 = _93; + _118 = _111; } - bool _109; - if (!_101) + bool _126; + if (!_118) { - _109 = _88.region == 2; + _126 = _20.region == 4; } else { - _109 = _101; + _126 = _118; } - if (_109) + if (_126) { - float2 param = in.uv + float2(-0.00048828125, -0.0029296875); - float2 param_1 = float2(0.00048828125, 0.001953125); - float2 param_2 = in.uv + float2(0.000244140625, -0.0029296875); - float2 param_3 = float2(0.00048828125, 0.001953125); - float2 param_4 = in.uv + float2(-0.000732421875, 0.0009765625); - float2 param_5 = float2(0.00048828125, 0.001953125); - float2 param_6 = in.uv + float2(0.000244140625, 0.0009765625); - float2 param_7 = float2(0.00048828125, 0.001953125); - out.frag_color = float4(1.0, 1.0, 1.0, 0.25 * (((down_sample(param, param_1, blit_atlas_src_texture, blit_atlas_src_sampler) + down_sample(param_2, param_3, blit_atlas_src_texture, blit_atlas_src_sampler)) + down_sample(param_4, param_5, blit_atlas_src_texture, blit_atlas_src_sampler)) + down_sample(param_6, param_7, blit_atlas_src_texture, blit_atlas_src_sampler))); + float2 param = in.uv + (float2(-1.0, -1.5) / _20.glyph_buffer_size); + float2 param_1 = _98; + float2 param_2 = in.uv + (float2(0.5, -1.5) / _20.glyph_buffer_size); + float2 param_3 = _98; + float2 param_4 = in.uv + (float2(-1.5, 0.5) / _20.glyph_buffer_size); + float2 param_5 = _98; + float2 param_6 = in.uv + (float2(0.5) / _20.glyph_buffer_size); + float2 param_7 = _98; + out.frag_color = float4(1.0, 1.0, 1.0, (1.0 / _20.over_sample) * (((down_sample_to_texture(param, param_1, _20, ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler) + down_sample_to_texture(param_2, param_3, _20, ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler)) + down_sample_to_texture(param_4, param_5, _20, ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler)) + down_sample_to_texture(param_6, param_7, _20, ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler))); } else { @@ -865,7 +976,7 @@ blit_atlas_vs_source_metal_macos := [473]u8 { */ @(private="file") -blit_atlas_fs_source_metal_macos := [2373]u8 { +ve_blit_atlas_fs_source_metal_macos := [2713]u8 { 0x23,0x70,0x72,0x61,0x67,0x6d,0x61,0x20,0x63,0x6c,0x61,0x6e,0x67,0x20,0x64,0x69, 0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x20,0x69,0x67,0x6e,0x6f,0x72,0x65,0x64, 0x20,0x22,0x2d,0x57,0x6d,0x69,0x73,0x73,0x69,0x6e,0x67,0x2d,0x70,0x72,0x6f,0x74, @@ -874,147 +985,168 @@ blit_atlas_fs_source_metal_macos := [2373]u8 { 0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f, 0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a,0x75,0x73,0x69,0x6e,0x67,0x20,0x6e, 0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20,0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a, - 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c, - 0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20, - 0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x72,0x65,0x67,0x69,0x6f,0x6e,0x3b,0x0a,0x7d, - 0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, - 0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, - 0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x63,0x6f, - 0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74, - 0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b, - 0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d, - 0x3b,0x0a,0x0a,0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x69,0x6e,0x6c,0x69,0x6e,0x65, - 0x20,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69,0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28, - 0x61,0x6c,0x77,0x61,0x79,0x73,0x5f,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x29,0x29,0x0a, - 0x66,0x6c,0x6f,0x61,0x74,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c, - 0x65,0x28,0x74,0x68,0x72,0x65,0x61,0x64,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x32,0x26,0x20,0x75,0x76,0x2c,0x20,0x74,0x68,0x72,0x65,0x61, - 0x64,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x26,0x20, - 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x74,0x65, - 0x78,0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x62, - 0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65, - 0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x62, + 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f, + 0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a, + 0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x67,0x6c,0x79, + 0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x6f,0x76,0x65,0x72,0x5f,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x72, + 0x65,0x67,0x69,0x6f,0x6e,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63, + 0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d, + 0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63, + 0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x61,0x74,0x69, + 0x63,0x20,0x69,0x6e,0x6c,0x69,0x6e,0x65,0x20,0x5f,0x5f,0x61,0x74,0x74,0x72,0x69, + 0x62,0x75,0x74,0x65,0x5f,0x5f,0x28,0x28,0x61,0x6c,0x77,0x61,0x79,0x73,0x5f,0x69, + 0x6e,0x6c,0x69,0x6e,0x65,0x29,0x29,0x0a,0x66,0x6c,0x6f,0x61,0x74,0x20,0x64,0x6f, + 0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x28,0x74,0x68,0x72,0x65,0x61,0x64,0x20,0x63,0x6f,0x6e,0x73, + 0x74,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x26,0x20,0x75,0x76,0x2c,0x20,0x74,0x68, + 0x72,0x65,0x61,0x64,0x20,0x63,0x6f,0x6e,0x73,0x74,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x32,0x26,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x2c, + 0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69, + 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x26,0x20,0x5f,0x32,0x30,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32, + 0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74, + 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x76,0x65,0x5f,0x62, 0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61, 0x6d,0x70,0x6c,0x65,0x72,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74, - 0x75,0x72,0x6e,0x20,0x30,0x2e,0x32,0x35,0x20,0x2a,0x20,0x28,0x28,0x28,0x62,0x6c, - 0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78, - 0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x62,0x6c,0x69,0x74, - 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c, - 0x65,0x72,0x2c,0x20,0x75,0x76,0x29,0x2e,0x78,0x20,0x2b,0x20,0x62,0x6c,0x69,0x74, - 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61, - 0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x66,0x6d,0x61,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30, - 0x2c,0x20,0x31,0x2e,0x30,0x29,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f, - 0x73,0x69,0x7a,0x65,0x2c,0x20,0x75,0x76,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20, - 0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74, - 0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x62,0x6c, - 0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x2c,0x20,0x66,0x6d,0x61,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32, - 0x28,0x31,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x29,0x2c,0x20,0x74,0x65,0x78,0x74, - 0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x75,0x76,0x29,0x29,0x2e,0x78, - 0x29,0x20,0x2b,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73, + 0x75,0x72,0x6e,0x20,0x28,0x31,0x2e,0x30,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x6f, + 0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x29,0x20,0x2a,0x20,0x28,0x28, + 0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73, 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c, - 0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63, - 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x75,0x76,0x20,0x2b,0x20, - 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x29,0x29,0x2e,0x78, - 0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d, - 0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d, - 0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61, - 0x67,0x65,0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e, - 0x74,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f, - 0x70,0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x38,0x38,0x20,0x5b,0x5b,0x62,0x75, - 0x66,0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x62,0x6c,0x69,0x74, + 0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f, + 0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x29, + 0x2e,0x78,0x20,0x2b,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c, + 0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74, + 0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c, + 0x20,0x66,0x6d,0x61,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x2c, + 0x20,0x31,0x2e,0x30,0x29,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73, + 0x69,0x7a,0x65,0x2c,0x20,0x75,0x76,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x76, + 0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63, + 0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28, + 0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72, + 0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x66,0x6d,0x61,0x28,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x28,0x31,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x29,0x2c, + 0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x75, + 0x76,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74, 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d, - 0x5d,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x62,0x6c,0x69,0x74,0x5f, - 0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x72,0x20,0x5b,0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d, - 0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75, - 0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x39,0x33,0x20,0x3d,0x20,0x5f,0x38,0x38,0x2e,0x72, - 0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x30,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69, - 0x66,0x20,0x28,0x21,0x5f,0x39,0x33,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x30,0x31,0x20,0x3d,0x20,0x5f,0x38, - 0x38,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x31,0x3b,0x0a,0x20, + 0x72,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69, + 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70, + 0x6c,0x65,0x72,0x2c,0x20,0x28,0x75,0x76,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x29,0x29,0x2e,0x78,0x29,0x3b,0x0a,0x7d,0x0a, + 0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f, + 0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e, + 0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x76,0x65,0x5f, + 0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x32,0x30,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66, + 0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69, + 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29, + 0x5d,0x5d,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x76,0x65,0x5f,0x62, + 0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x20,0x5b,0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28, + 0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x5f,0x39,0x38,0x20,0x3d, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x31,0x2e,0x30,0x29,0x20,0x2f,0x20,0x5f, + 0x32,0x30,0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f, + 0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f, + 0x31,0x30,0x34,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e, + 0x20,0x3d,0x3d,0x20,0x30,0x3b,0x0a,0x20,0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20, + 0x5f,0x31,0x31,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f, + 0x31,0x30,0x34,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x5f,0x31,0x31,0x31,0x20,0x3d,0x20,0x5f,0x32,0x30,0x2e,0x72,0x65, + 0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d, + 0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x31,0x20,0x3d,0x20,0x5f, + 0x31,0x30,0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x62, + 0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x31,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66, + 0x20,0x28,0x21,0x5f,0x31,0x31,0x31,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x38,0x20,0x3d,0x20,0x5f,0x32, + 0x30,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x32,0x3b,0x0a,0x20, 0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a,0x20,0x20, - 0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x30,0x31, - 0x20,0x3d,0x20,0x5f,0x39,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20, - 0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x30,0x39,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x31,0x30,0x31,0x29,0x0a,0x20,0x20,0x20,0x20, - 0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x30,0x39,0x20,0x3d, - 0x20,0x5f,0x38,0x38,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20,0x32, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65, - 0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f, - 0x31,0x30,0x39,0x20,0x3d,0x20,0x5f,0x31,0x30,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x7d,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f,0x31,0x30,0x39,0x29,0x0a, - 0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x20,0x3d,0x20,0x69,0x6e,0x2e, - 0x75,0x76,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x30,0x2e,0x30, - 0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c,0x20,0x2d,0x30,0x2e,0x30, - 0x30,0x32,0x39,0x32,0x39,0x36,0x38,0x37,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d, - 0x5f,0x31,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x30, - 0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x31, - 0x39,0x35,0x33,0x31,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x20, - 0x3d,0x20,0x69,0x6e,0x2e,0x75,0x76,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32, - 0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x2c, - 0x20,0x2d,0x30,0x2e,0x30,0x30,0x32,0x39,0x32,0x39,0x36,0x38,0x37,0x35,0x29,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20, - 0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32, - 0x28,0x30,0x2e,0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c,0x20, - 0x30,0x2e,0x30,0x30,0x31,0x39,0x35,0x33,0x31,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x31,0x38, + 0x20,0x3d,0x20,0x5f,0x31,0x31,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20, + 0x20,0x20,0x20,0x62,0x6f,0x6f,0x6c,0x20,0x5f,0x31,0x32,0x36,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x5f,0x31,0x31,0x38,0x29,0x0a,0x20,0x20,0x20, + 0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x5f,0x31,0x32,0x36,0x20, + 0x3d,0x20,0x5f,0x32,0x30,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3d,0x3d,0x20, + 0x34,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73, + 0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, + 0x5f,0x31,0x32,0x36,0x20,0x3d,0x20,0x5f,0x31,0x31,0x38,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f,0x31,0x32,0x36,0x29, + 0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x20,0x3d,0x20,0x69,0x6e, + 0x2e,0x75,0x76,0x20,0x2b,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x31, + 0x2e,0x30,0x2c,0x20,0x2d,0x31,0x2e,0x35,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e, + 0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a, + 0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31,0x20,0x3d,0x20,0x5f,0x39,0x38, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32, + 0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x75,0x76, + 0x20,0x2b,0x20,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x35,0x2c,0x20, + 0x2d,0x31,0x2e,0x35,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x67,0x6c,0x79,0x70, + 0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x29,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70, + 0x61,0x72,0x61,0x6d,0x5f,0x33,0x20,0x3d,0x20,0x5f,0x39,0x38,0x3b,0x0a,0x20,0x20, 0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72, - 0x61,0x6d,0x5f,0x34,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x75,0x76,0x20,0x2b,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x30,0x2e,0x30,0x30,0x30,0x37,0x33,0x32,0x34, - 0x32,0x31,0x38,0x37,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35, - 0x36,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x20,0x3d,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x34,0x38,0x38,0x32,0x38, - 0x31,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x31,0x39,0x35,0x33,0x31,0x32,0x35, - 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, - 0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x75, - 0x76,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x30,0x30, - 0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x30, - 0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, - 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37, - 0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x34, - 0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x31,0x39,0x35, - 0x33,0x31,0x32,0x35,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f, - 0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c, - 0x20,0x31,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x32,0x35,0x20,0x2a,0x20,0x28,0x28,0x28, - 0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61, - 0x6d,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31,0x2c,0x20,0x62,0x6c,0x69,0x74, - 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x2c,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73, - 0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x29,0x20,0x2b,0x20,0x64,0x6f, - 0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f, - 0x32,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x2c,0x20,0x62,0x6c,0x69,0x74, - 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x2c,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73, - 0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x29,0x29,0x20,0x2b,0x20,0x64, - 0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61,0x6d, - 0x5f,0x34,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x2c,0x20,0x62,0x6c,0x69, - 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74, - 0x75,0x72,0x65,0x2c,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f, - 0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x29,0x29,0x20,0x2b,0x20, - 0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x70,0x61,0x72,0x61, - 0x6d,0x5f,0x36,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x2c,0x20,0x62,0x6c, - 0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78, - 0x74,0x75,0x72,0x65,0x2c,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73, - 0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x29,0x29,0x29,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65,0x6c,0x73,0x65,0x0a, - 0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75, - 0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x34,0x28,0x30,0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20, - 0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d, - 0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b, - 0x0a,0x7d,0x0a,0x0a,0x00, + 0x61,0x6d,0x5f,0x34,0x20,0x3d,0x20,0x69,0x6e,0x2e,0x75,0x76,0x20,0x2b,0x20,0x28, + 0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x31,0x2e,0x35,0x2c,0x20,0x30,0x2e,0x35, + 0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75, + 0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d, + 0x5f,0x35,0x20,0x3d,0x20,0x5f,0x39,0x38,0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36, + 0x20,0x3d,0x20,0x69,0x6e,0x2e,0x75,0x76,0x20,0x2b,0x20,0x28,0x66,0x6c,0x6f,0x61, + 0x74,0x32,0x28,0x30,0x2e,0x35,0x29,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x67,0x6c, + 0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x29, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32, + 0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x20,0x3d,0x20,0x5f,0x39,0x38,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28, + 0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x2c,0x20,0x28, + 0x31,0x2e,0x30,0x20,0x2f,0x20,0x5f,0x32,0x30,0x2e,0x6f,0x76,0x65,0x72,0x5f,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x29,0x20,0x2a,0x20,0x28,0x28,0x28,0x64,0x6f,0x77,0x6e, + 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f, + 0x31,0x2c,0x20,0x5f,0x32,0x30,0x2c,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f, + 0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72, + 0x65,0x2c,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73, + 0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x29,0x20,0x2b,0x20, + 0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x2c,0x20, + 0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x2c,0x20,0x5f,0x32,0x30,0x2c,0x20,0x76,0x65, + 0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74, + 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x72,0x29,0x29,0x20,0x2b,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70, + 0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x70,0x61, + 0x72,0x61,0x6d,0x5f,0x34,0x2c,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x2c,0x20, + 0x5f,0x32,0x30,0x2c,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c, + 0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20, + 0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72, + 0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x29,0x29,0x20,0x2b,0x20,0x64,0x6f, + 0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x2c,0x20,0x70,0x61, + 0x72,0x61,0x6d,0x5f,0x37,0x2c,0x20,0x5f,0x32,0x30,0x2c,0x20,0x76,0x65,0x5f,0x62, + 0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61, + 0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, + 0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x65, + 0x6c,0x73,0x65,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20, + 0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, + 0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x30,0x2e,0x30,0x2c,0x20,0x30, + 0x2e,0x30,0x2c,0x20,0x30,0x2e,0x30,0x2c,0x20,0x31,0x2e,0x30,0x29,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, + 0x6f,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, } /* diagnostic(off, derivative_uniformity); @@ -1053,7 +1185,7 @@ blit_atlas_fs_source_metal_macos := [2373]u8 { */ @(private="file") -blit_atlas_vs_source_wgsl := [698]u8 { +ve_blit_atlas_vs_source_wgsl := [698]u8 { 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, @@ -1102,50 +1234,54 @@ blit_atlas_vs_source_wgsl := [698]u8 { /* diagnostic(off, derivative_uniformity); - struct blit_atlas_fs_params { + struct ve_blit_atlas_fs_params { /_ @offset(0) _/ + glyph_buffer_size : vec2f, + /_ @offset(8) _/ + over_sample : f32, + /_ @offset(12) _/ region : i32, } - @group(1) @binding(64) var blit_atlas_src_texture : texture_2d; + @group(0) @binding(8) var x_20 : ve_blit_atlas_fs_params; - @group(1) @binding(80) var blit_atlas_src_sampler : sampler; + @group(1) @binding(64) var ve_blit_atlas_src_texture : texture_2d; - @group(0) @binding(8) var x_88 : blit_atlas_fs_params; + @group(1) @binding(80) var ve_blit_atlas_src_sampler : sampler; var uv_1 : vec2f; var frag_color : vec4f; - fn down_sample_vf2_vf2_(uv : ptr, texture_size : ptr) -> f32 { - var down_sample_scale : f32; + fn down_sample_to_texture_vf2_vf2_(uv : ptr, texture_size : ptr) -> f32 { + var down_sample : f32; var value : f32; - down_sample_scale = 0.25f; - let x_28 : vec2f = *(uv); - let x_31 : vec2f = *(texture_size); - let x_35 : vec4f = textureSample(blit_atlas_src_texture, blit_atlas_src_sampler, (x_28 + (vec2f(0.0f, 0.0f) * x_31))); - let x_39 : f32 = down_sample_scale; - let x_44 : vec2f = *(uv); - let x_47 : vec2f = *(texture_size); - let x_50 : vec4f = textureSample(blit_atlas_src_texture, blit_atlas_src_sampler, (x_44 + (vec2f(0.0f, 1.0f) * x_47))); - let x_52 : f32 = down_sample_scale; - let x_58 : vec2f = *(uv); - let x_60 : vec2f = *(texture_size); - let x_63 : vec4f = textureSample(blit_atlas_src_texture, blit_atlas_src_sampler, (x_58 + (vec2f(1.0f, 0.0f) * x_60))); - let x_65 : f32 = down_sample_scale; - let x_71 : vec2f = *(uv); - let x_73 : vec2f = *(texture_size); - let x_76 : vec4f = textureSample(blit_atlas_src_texture, blit_atlas_src_sampler, (x_71 + (vec2f(1.0f, 1.0f) * x_73))); - let x_78 : f32 = down_sample_scale; - value = ((((x_35.x * x_39) + (x_50.x * x_52)) + (x_63.x * x_65)) + (x_76.x * x_78)); - let x_81 : f32 = value; - return x_81; + let x_24 : f32 = x_20.over_sample; + down_sample = (1.0f / x_24); + let x_37 : vec2f = *(uv); + let x_40 : vec2f = *(texture_size); + let x_44 : vec4f = textureSample(ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler, (x_37 + (vec2f(0.0f, 0.0f) * x_40))); + let x_48 : f32 = down_sample; + let x_53 : vec2f = *(uv); + let x_55 : vec2f = *(texture_size); + let x_58 : vec4f = textureSample(ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler, (x_53 + (vec2f(0.0f, 1.0f) * x_55))); + let x_60 : f32 = down_sample; + let x_66 : vec2f = *(uv); + let x_68 : vec2f = *(texture_size); + let x_71 : vec4f = textureSample(ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler, (x_66 + (vec2f(1.0f, 0.0f) * x_68))); + let x_73 : f32 = down_sample; + let x_79 : vec2f = *(uv); + let x_81 : vec2f = *(texture_size); + let x_84 : vec4f = textureSample(ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler, (x_79 + (vec2f(1.0f, 1.0f) * x_81))); + let x_86 : f32 = down_sample; + value = ((((x_44.x * x_48) + (x_58.x * x_60)) + (x_71.x * x_73)) + (x_84.x * x_86)); + let x_89 : f32 = value; + return x_89; } - const x_123 = vec2f(0.00048828125f, 0.001953125f); - fn main_1() { - var down_sample_scale_1 : f32; + var texture_size_1 : vec2f; + var down_sample_1 : f32; var alpha : f32; var param : vec2f; var param_1 : vec2f; @@ -1155,49 +1291,68 @@ blit_atlas_vs_source_wgsl := [698]u8 { var param_5 : vec2f; var param_6 : vec2f; var param_7 : vec2f; - var x_100 : bool; - var x_101 : bool; - var x_108 : bool; - var x_109 : bool; - let x_92 : i32 = x_88.region; - let x_93 : bool = (x_92 == 0i); - x_101 = x_93; - if (!(x_93)) { - let x_98 : i32 = x_88.region; - x_100 = (x_98 == 1i); - x_101 = x_100; + var x_110 : bool; + var x_111 : bool; + var x_117 : bool; + var x_118 : bool; + var x_125 : bool; + var x_126 : bool; + let x_96 : vec2f = x_20.glyph_buffer_size; + texture_size_1 = (vec2f(1.0f, 1.0f) / x_96); + let x_103 : i32 = x_20.region; + let x_104 : bool = (x_103 == 0i); + x_111 = x_104; + if (!(x_104)) { + let x_109 : i32 = x_20.region; + x_110 = (x_109 == 1i); + x_111 = x_110; + } + x_118 = x_111; + if (!(x_111)) { + let x_116 : i32 = x_20.region; + x_117 = (x_116 == 2i); + x_118 = x_117; } - x_109 = x_101; - if (!(x_101)) { - let x_106 : i32 = x_88.region; - x_108 = (x_106 == 2i); - x_109 = x_108; + x_126 = x_118; + if (!(x_118)) { + let x_123 : i32 = x_20.region; + x_125 = (x_123 == 4i); + x_126 = x_125; } - if (x_109) { - down_sample_scale_1 = 0.25f; - let x_116 : vec2f = uv_1; - param = (x_116 + vec2f(-0.00048828125f, -0.0029296875f)); - param_1 = x_123; - let x_126 : f32 = down_sample_vf2_vf2_(&(param), &(param_1)); - let x_127 : f32 = down_sample_scale_1; - let x_129 : vec2f = uv_1; - param_2 = (x_129 + vec2f(0.000244140625f, -0.0029296875f)); - param_3 = x_123; - let x_135 : f32 = down_sample_vf2_vf2_(&(param_2), &(param_3)); - let x_136 : f32 = down_sample_scale_1; - let x_139 : vec2f = uv_1; - param_4 = (x_139 + vec2f(-0.000732421875f, 0.0009765625f)); - param_5 = x_123; - let x_146 : f32 = down_sample_vf2_vf2_(&(param_4), &(param_5)); - let x_147 : f32 = down_sample_scale_1; - let x_150 : vec2f = uv_1; - param_6 = (x_150 + vec2f(0.000244140625f, 0.0009765625f)); - param_7 = x_123; - let x_155 : f32 = down_sample_vf2_vf2_(&(param_6), &(param_7)); - let x_156 : f32 = down_sample_scale_1; - alpha = ((((x_126 * x_127) + (x_135 * x_136)) + (x_146 * x_147)) + (x_155 * x_156)); - let x_161 : f32 = alpha; - frag_color = vec4f(1.0f, 1.0f, 1.0f, x_161); + if (x_126) { + let x_131 : f32 = x_20.over_sample; + down_sample_1 = (1.0f / x_131); + let x_136 : vec2f = uv_1; + let x_140 : vec2f = texture_size_1; + param = (x_136 + (vec2f(-1.0f, -1.5f) * x_140)); + let x_145 : vec2f = texture_size_1; + param_1 = x_145; + let x_146 : f32 = down_sample_to_texture_vf2_vf2_(&(param), &(param_1)); + let x_147 : f32 = down_sample_1; + let x_149 : vec2f = uv_1; + let x_152 : vec2f = texture_size_1; + param_2 = (x_149 + (vec2f(0.5f, -1.5f) * x_152)); + let x_157 : vec2f = texture_size_1; + param_3 = x_157; + let x_158 : f32 = down_sample_to_texture_vf2_vf2_(&(param_2), &(param_3)); + let x_159 : f32 = down_sample_1; + let x_162 : vec2f = uv_1; + let x_164 : vec2f = texture_size_1; + param_4 = (x_162 + (vec2f(-1.5f, 0.5f) * x_164)); + let x_169 : vec2f = texture_size_1; + param_5 = x_169; + let x_170 : f32 = down_sample_to_texture_vf2_vf2_(&(param_4), &(param_5)); + let x_171 : f32 = down_sample_1; + let x_174 : vec2f = uv_1; + let x_176 : vec2f = texture_size_1; + param_6 = (x_174 + (vec2f(0.5f, 0.5f) * x_176)); + let x_181 : vec2f = texture_size_1; + param_7 = x_181; + let x_182 : f32 = down_sample_to_texture_vf2_vf2_(&(param_6), &(param_7)); + let x_183 : f32 = down_sample_1; + alpha = ((((x_146 * x_147) + (x_158 * x_159)) + (x_170 * x_171)) + (x_182 * x_183)); + let x_188 : f32 = alpha; + frag_color = vec4f(1.0f, 1.0f, 1.0f, x_188); } else { frag_color = vec4f(0.0f, 0.0f, 0.0f, 1.0f); } @@ -1218,218 +1373,263 @@ blit_atlas_vs_source_wgsl := [698]u8 { */ @(private="file") -blit_atlas_fs_source_wgsl := [3640]u8 { +ve_blit_atlas_fs_source_wgsl := [4360]u8 { 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, - 0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61, - 0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66, - 0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x72,0x65,0x67,0x69, - 0x6f,0x6e,0x20,0x3a,0x20,0x69,0x33,0x32,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x67,0x72, + 0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40, + 0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x67, + 0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40, + 0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x38,0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x6f, + 0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x3a,0x20,0x66,0x33,0x32, + 0x2c,0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x31, + 0x32,0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x72,0x65,0x67,0x69,0x6f,0x6e,0x20,0x3a, + 0x20,0x69,0x33,0x32,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28, + 0x30,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x38,0x29,0x20,0x76, + 0x61,0x72,0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x32,0x30, + 0x20,0x3a,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73, + 0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x40,0x67,0x72, 0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28, - 0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c, - 0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x20,0x3a, - 0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e, - 0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69, - 0x6e,0x64,0x69,0x6e,0x67,0x28,0x38,0x30,0x29,0x20,0x76,0x61,0x72,0x20,0x62,0x6c, - 0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x20,0x3a,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a, - 0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x40,0x62,0x69,0x6e,0x64, - 0x69,0x6e,0x67,0x28,0x38,0x29,0x20,0x76,0x61,0x72,0x3c,0x75,0x6e,0x69,0x66,0x6f, - 0x72,0x6d,0x3e,0x20,0x78,0x5f,0x38,0x38,0x20,0x3a,0x20,0x62,0x6c,0x69,0x74,0x5f, - 0x61,0x74,0x6c,0x61,0x73,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b, - 0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75, - 0x76,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x76,0x61, - 0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67,0x5f, - 0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a,0x0a, - 0x66,0x6e,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x76, + 0x36,0x34,0x29,0x20,0x76,0x61,0x72,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f, + 0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72, + 0x65,0x20,0x3a,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66, + 0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20, + 0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x38,0x30,0x29,0x20,0x76,0x61,0x72, + 0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73, + 0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x3a,0x20,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61, + 0x74,0x65,0x3e,0x20,0x75,0x76,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, + 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76, 0x66,0x32,0x5f,0x76,0x66,0x32,0x5f,0x28,0x75,0x76,0x20,0x3a,0x20,0x70,0x74,0x72, 0x3c,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x2c,0x20,0x76,0x65,0x63,0x32,0x66, 0x3e,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x20, 0x3a,0x20,0x70,0x74,0x72,0x3c,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x2c,0x20, 0x76,0x65,0x63,0x32,0x66,0x3e,0x29,0x20,0x2d,0x3e,0x20,0x66,0x33,0x32,0x20,0x7b, 0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70, - 0x6c,0x65,0x5f,0x73,0x63,0x61,0x6c,0x65,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a, - 0x20,0x20,0x76,0x61,0x72,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3a,0x20,0x66,0x33, - 0x32,0x3b,0x0a,0x20,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x5f,0x73,0x63,0x61,0x6c,0x65,0x20,0x3d,0x20,0x30,0x2e,0x32,0x35,0x66,0x3b,0x0a, - 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x38,0x20,0x3a,0x20,0x76,0x65,0x63, - 0x32,0x66,0x20,0x3d,0x20,0x2a,0x28,0x75,0x76,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65, - 0x74,0x20,0x78,0x5f,0x33,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d, - 0x20,0x2a,0x28,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x29, - 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x35,0x20,0x3a,0x20,0x76, - 0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x53,0x61, - 0x6d,0x70,0x6c,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f, - 0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x62,0x6c,0x69, - 0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70, - 0x6c,0x65,0x72,0x2c,0x20,0x28,0x78,0x5f,0x32,0x38,0x20,0x2b,0x20,0x28,0x76,0x65, - 0x63,0x32,0x66,0x28,0x30,0x2e,0x30,0x66,0x2c,0x20,0x30,0x2e,0x30,0x66,0x29,0x20, - 0x2a,0x20,0x78,0x5f,0x33,0x31,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74, - 0x20,0x78,0x5f,0x33,0x39,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f, - 0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x73,0x63,0x61,0x6c,0x65,0x3b, - 0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x34,0x20,0x3a,0x20,0x76,0x65, + 0x6c,0x65,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20, + 0x76,0x61,0x6c,0x75,0x65,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x32,0x34,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20, + 0x78,0x5f,0x32,0x30,0x2e,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x3b,0x0a,0x20,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x20, + 0x3d,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2f,0x20,0x78,0x5f,0x32,0x34,0x29,0x3b, + 0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x37,0x20,0x3a,0x20,0x76,0x65, 0x63,0x32,0x66,0x20,0x3d,0x20,0x2a,0x28,0x75,0x76,0x29,0x3b,0x0a,0x20,0x20,0x6c, - 0x65,0x74,0x20,0x78,0x5f,0x34,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20, + 0x65,0x74,0x20,0x78,0x5f,0x34,0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20, 0x3d,0x20,0x2a,0x28,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65, - 0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x30,0x20,0x3a,0x20, + 0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x34,0x20,0x3a,0x20, 0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x53, - 0x61,0x6d,0x70,0x6c,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73, - 0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x62,0x6c, - 0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x78,0x5f,0x34,0x34,0x20,0x2b,0x20,0x28,0x76, - 0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29, - 0x20,0x2a,0x20,0x78,0x5f,0x34,0x37,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65, - 0x74,0x20,0x78,0x5f,0x35,0x32,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64, - 0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x73,0x63,0x61,0x6c,0x65, - 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x38,0x20,0x3a,0x20,0x76, + 0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74, + 0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c, + 0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73, + 0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x78,0x5f,0x33, + 0x37,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x30,0x66,0x2c, + 0x20,0x30,0x2e,0x30,0x66,0x29,0x20,0x2a,0x20,0x78,0x5f,0x34,0x30,0x29,0x29,0x29, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x38,0x20,0x3a,0x20,0x66, + 0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x33,0x20,0x3a,0x20,0x76, 0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x2a,0x28,0x75,0x76,0x29,0x3b,0x0a,0x20,0x20, - 0x6c,0x65,0x74,0x20,0x78,0x5f,0x36,0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x35,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, 0x20,0x3d,0x20,0x2a,0x28,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a, - 0x65,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x36,0x33,0x20,0x3a, + 0x65,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x38,0x20,0x3a, 0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, - 0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61, - 0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x62, - 0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61, - 0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x78,0x5f,0x35,0x38,0x20,0x2b,0x20,0x28, - 0x76,0x65,0x63,0x32,0x66,0x28,0x31,0x2e,0x30,0x66,0x2c,0x20,0x30,0x2e,0x30,0x66, - 0x29,0x20,0x2a,0x20,0x78,0x5f,0x36,0x30,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c, - 0x65,0x74,0x20,0x78,0x5f,0x36,0x35,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20, - 0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x73,0x63,0x61,0x6c, - 0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x37,0x31,0x20,0x3a,0x20, + 0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61, + 0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x2c,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f, + 0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x78,0x5f, + 0x35,0x33,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x30,0x66, + 0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x20,0x2a,0x20,0x78,0x5f,0x35,0x35,0x29,0x29, + 0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x36,0x30,0x20,0x3a,0x20, + 0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x36,0x36,0x20,0x3a,0x20, 0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x2a,0x28,0x75,0x76,0x29,0x3b,0x0a,0x20, - 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x37,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, + 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x36,0x38,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, 0x66,0x20,0x3d,0x20,0x2a,0x28,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69, - 0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x37,0x36,0x20, + 0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x37,0x31,0x20, 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72, - 0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c, - 0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20, - 0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x73, - 0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x78,0x5f,0x37,0x31,0x20,0x2b,0x20, - 0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x31,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30, - 0x66,0x29,0x20,0x2a,0x20,0x78,0x5f,0x37,0x33,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20, - 0x6c,0x65,0x74,0x20,0x78,0x5f,0x37,0x38,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d, - 0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x73,0x63,0x61, - 0x6c,0x65,0x3b,0x0a,0x20,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3d,0x20,0x28,0x28, - 0x28,0x28,0x78,0x5f,0x33,0x35,0x2e,0x78,0x20,0x2a,0x20,0x78,0x5f,0x33,0x39,0x29, - 0x20,0x2b,0x20,0x28,0x78,0x5f,0x35,0x30,0x2e,0x78,0x20,0x2a,0x20,0x78,0x5f,0x35, - 0x32,0x29,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x36,0x33,0x2e,0x78,0x20,0x2a,0x20, - 0x78,0x5f,0x36,0x35,0x29,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x37,0x36,0x2e,0x78, - 0x20,0x2a,0x20,0x78,0x5f,0x37,0x38,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74, - 0x20,0x78,0x5f,0x38,0x31,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x76,0x61, - 0x6c,0x75,0x65,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x78,0x5f, - 0x38,0x31,0x3b,0x0a,0x7d,0x0a,0x0a,0x63,0x6f,0x6e,0x73,0x74,0x20,0x78,0x5f,0x31, - 0x32,0x33,0x20,0x3d,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x30,0x30,0x30, - 0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x66,0x2c,0x20,0x30,0x2e,0x30,0x30,0x31, - 0x39,0x35,0x33,0x31,0x32,0x35,0x66,0x29,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61, - 0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x64, - 0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x73,0x63,0x61,0x6c,0x65, - 0x5f,0x31,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20, - 0x61,0x6c,0x70,0x68,0x61,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a,0x20,0x20,0x76, - 0x61,0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, - 0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31,0x20, - 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x70, - 0x61,0x72,0x61,0x6d,0x5f,0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a, - 0x20,0x20,0x76,0x61,0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x20,0x3a,0x20, + 0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f, + 0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72, + 0x65,0x2c,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61,0x73, + 0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x78, + 0x5f,0x36,0x36,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x31,0x2e,0x30, + 0x66,0x2c,0x20,0x30,0x2e,0x30,0x66,0x29,0x20,0x2a,0x20,0x78,0x5f,0x36,0x38,0x29, + 0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x37,0x33,0x20,0x3a, + 0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70, + 0x6c,0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x37,0x39,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x2a,0x28,0x75,0x76,0x29,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x38,0x31,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x2a,0x28,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73, + 0x69,0x7a,0x65,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x38,0x34, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74, + 0x5f,0x61,0x74,0x6c,0x61,0x73,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x2c,0x20,0x76,0x65,0x5f,0x62,0x6c,0x69,0x74,0x5f,0x61,0x74,0x6c,0x61, + 0x73,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x28, + 0x78,0x5f,0x37,0x39,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x31,0x2e, + 0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x29,0x20,0x2a,0x20,0x78,0x5f,0x38,0x31, + 0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x38,0x36,0x20, + 0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x3b,0x0a,0x20,0x20,0x76,0x61,0x6c,0x75,0x65,0x20,0x3d,0x20,0x28, + 0x28,0x28,0x28,0x78,0x5f,0x34,0x34,0x2e,0x78,0x20,0x2a,0x20,0x78,0x5f,0x34,0x38, + 0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x35,0x38,0x2e,0x78,0x20,0x2a,0x20,0x78,0x5f, + 0x36,0x30,0x29,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x37,0x31,0x2e,0x78,0x20,0x2a, + 0x20,0x78,0x5f,0x37,0x33,0x29,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x38,0x34,0x2e, + 0x78,0x20,0x2a,0x20,0x78,0x5f,0x38,0x36,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x38,0x39,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x76, + 0x61,0x6c,0x75,0x65,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x78, + 0x5f,0x38,0x39,0x3b,0x0a,0x7d,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f, + 0x31,0x28,0x29,0x20,0x7b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x5f,0x31,0x20,0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a,0x20, + 0x20,0x76,0x61,0x72,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3a,0x20,0x66,0x33,0x32, + 0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x20,0x3a,0x20, 0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x70,0x61,0x72, - 0x61,0x6d,0x5f,0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x20,0x20, - 0x76,0x61,0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x20,0x3a,0x20,0x76,0x65, + 0x61,0x6d,0x5f,0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x20,0x20, + 0x76,0x61,0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x20,0x3a,0x20,0x76,0x65, 0x63,0x32,0x66,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x70,0x61,0x72,0x61,0x6d, - 0x5f,0x36,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x20,0x20,0x76,0x61, - 0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, - 0x66,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x78,0x5f,0x31,0x30,0x30,0x20,0x3a, - 0x20,0x62,0x6f,0x6f,0x6c,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x78,0x5f,0x31, - 0x30,0x31,0x20,0x3a,0x20,0x62,0x6f,0x6f,0x6c,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72, - 0x20,0x78,0x5f,0x31,0x30,0x38,0x20,0x3a,0x20,0x62,0x6f,0x6f,0x6c,0x3b,0x0a,0x20, - 0x20,0x76,0x61,0x72,0x20,0x78,0x5f,0x31,0x30,0x39,0x20,0x3a,0x20,0x62,0x6f,0x6f, - 0x6c,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x39,0x32,0x20,0x3a,0x20, - 0x69,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f,0x38,0x38,0x2e,0x72,0x65,0x67,0x69,0x6f, - 0x6e,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x39,0x33,0x20,0x3a,0x20, - 0x62,0x6f,0x6f,0x6c,0x20,0x3d,0x20,0x28,0x78,0x5f,0x39,0x32,0x20,0x3d,0x3d,0x20, - 0x30,0x69,0x29,0x3b,0x0a,0x20,0x20,0x78,0x5f,0x31,0x30,0x31,0x20,0x3d,0x20,0x78, - 0x5f,0x39,0x33,0x3b,0x0a,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x28,0x78,0x5f,0x39, - 0x33,0x29,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f, - 0x39,0x38,0x20,0x3a,0x20,0x69,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f,0x38,0x38,0x2e, - 0x72,0x65,0x67,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x78,0x5f,0x31,0x30, - 0x30,0x20,0x3d,0x20,0x28,0x78,0x5f,0x39,0x38,0x20,0x3d,0x3d,0x20,0x31,0x69,0x29, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x78,0x5f,0x31,0x30,0x31,0x20,0x3d,0x20,0x78,0x5f, - 0x31,0x30,0x30,0x3b,0x0a,0x20,0x20,0x7d,0x0a,0x20,0x20,0x78,0x5f,0x31,0x30,0x39, - 0x20,0x3d,0x20,0x78,0x5f,0x31,0x30,0x31,0x3b,0x0a,0x20,0x20,0x69,0x66,0x20,0x28, - 0x21,0x28,0x78,0x5f,0x31,0x30,0x31,0x29,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x30,0x36,0x20,0x3a,0x20,0x69,0x33,0x32,0x20, - 0x3d,0x20,0x78,0x5f,0x38,0x38,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x78,0x5f,0x31,0x30,0x38,0x20,0x3d,0x20,0x28,0x78,0x5f,0x31,0x30, + 0x5f,0x33,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x20,0x20,0x76,0x61, + 0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, + 0x66,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20, + 0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b, + 0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x78,0x5f, + 0x31,0x31,0x30,0x20,0x3a,0x20,0x62,0x6f,0x6f,0x6c,0x3b,0x0a,0x20,0x20,0x76,0x61, + 0x72,0x20,0x78,0x5f,0x31,0x31,0x31,0x20,0x3a,0x20,0x62,0x6f,0x6f,0x6c,0x3b,0x0a, + 0x20,0x20,0x76,0x61,0x72,0x20,0x78,0x5f,0x31,0x31,0x37,0x20,0x3a,0x20,0x62,0x6f, + 0x6f,0x6c,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x78,0x5f,0x31,0x31,0x38,0x20, + 0x3a,0x20,0x62,0x6f,0x6f,0x6c,0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x78,0x5f, + 0x31,0x32,0x35,0x20,0x3a,0x20,0x62,0x6f,0x6f,0x6c,0x3b,0x0a,0x20,0x20,0x76,0x61, + 0x72,0x20,0x78,0x5f,0x31,0x32,0x36,0x20,0x3a,0x20,0x62,0x6f,0x6f,0x6c,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x39,0x36,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x78,0x5f,0x32,0x30,0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f, + 0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x31,0x20,0x3d,0x20, + 0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x31,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30, + 0x66,0x29,0x20,0x2f,0x20,0x78,0x5f,0x39,0x36,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x31,0x30,0x33,0x20,0x3a,0x20,0x69,0x33,0x32,0x20,0x3d,0x20, + 0x78,0x5f,0x32,0x30,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x31,0x30,0x34,0x20,0x3a,0x20,0x62,0x6f,0x6f,0x6c,0x20, + 0x3d,0x20,0x28,0x78,0x5f,0x31,0x30,0x33,0x20,0x3d,0x3d,0x20,0x30,0x69,0x29,0x3b, + 0x0a,0x20,0x20,0x78,0x5f,0x31,0x31,0x31,0x20,0x3d,0x20,0x78,0x5f,0x31,0x30,0x34, + 0x3b,0x0a,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x28,0x78,0x5f,0x31,0x30,0x34,0x29, + 0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x30, + 0x39,0x20,0x3a,0x20,0x69,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f,0x32,0x30,0x2e,0x72, + 0x65,0x67,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x78,0x5f,0x31,0x31,0x30, + 0x20,0x3d,0x20,0x28,0x78,0x5f,0x31,0x30,0x39,0x20,0x3d,0x3d,0x20,0x31,0x69,0x29, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x78,0x5f,0x31,0x31,0x31,0x20,0x3d,0x20,0x78,0x5f, + 0x31,0x31,0x30,0x3b,0x0a,0x20,0x20,0x7d,0x0a,0x20,0x20,0x78,0x5f,0x31,0x31,0x38, + 0x20,0x3d,0x20,0x78,0x5f,0x31,0x31,0x31,0x3b,0x0a,0x20,0x20,0x69,0x66,0x20,0x28, + 0x21,0x28,0x78,0x5f,0x31,0x31,0x31,0x29,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x31,0x36,0x20,0x3a,0x20,0x69,0x33,0x32,0x20, + 0x3d,0x20,0x78,0x5f,0x32,0x30,0x2e,0x72,0x65,0x67,0x69,0x6f,0x6e,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x78,0x5f,0x31,0x31,0x37,0x20,0x3d,0x20,0x28,0x78,0x5f,0x31,0x31, 0x36,0x20,0x3d,0x3d,0x20,0x32,0x69,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x78,0x5f, - 0x31,0x30,0x39,0x20,0x3d,0x20,0x78,0x5f,0x31,0x30,0x38,0x3b,0x0a,0x20,0x20,0x7d, - 0x0a,0x20,0x20,0x69,0x66,0x20,0x28,0x78,0x5f,0x31,0x30,0x39,0x29,0x20,0x7b,0x0a, + 0x31,0x31,0x38,0x20,0x3d,0x20,0x78,0x5f,0x31,0x31,0x37,0x3b,0x0a,0x20,0x20,0x7d, + 0x0a,0x20,0x20,0x78,0x5f,0x31,0x32,0x36,0x20,0x3d,0x20,0x78,0x5f,0x31,0x31,0x38, + 0x3b,0x0a,0x20,0x20,0x69,0x66,0x20,0x28,0x21,0x28,0x78,0x5f,0x31,0x31,0x38,0x29, + 0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x32, + 0x33,0x20,0x3a,0x20,0x69,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f,0x32,0x30,0x2e,0x72, + 0x65,0x67,0x69,0x6f,0x6e,0x3b,0x0a,0x20,0x20,0x20,0x20,0x78,0x5f,0x31,0x32,0x35, + 0x20,0x3d,0x20,0x28,0x78,0x5f,0x31,0x32,0x33,0x20,0x3d,0x3d,0x20,0x34,0x69,0x29, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x78,0x5f,0x31,0x32,0x36,0x20,0x3d,0x20,0x78,0x5f, + 0x31,0x32,0x35,0x3b,0x0a,0x20,0x20,0x7d,0x0a,0x20,0x20,0x69,0x66,0x20,0x28,0x78, + 0x5f,0x31,0x32,0x36,0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20, + 0x78,0x5f,0x31,0x33,0x31,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f, + 0x32,0x30,0x2e,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a, 0x20,0x20,0x20,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f, - 0x73,0x63,0x61,0x6c,0x65,0x5f,0x31,0x20,0x3d,0x20,0x30,0x2e,0x32,0x35,0x66,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x31,0x36,0x20,0x3a, - 0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x5f,0x31,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x70,0x61,0x72,0x61,0x6d,0x20,0x3d,0x20,0x28,0x78,0x5f,0x31,0x31, - 0x36,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x2d,0x30,0x2e,0x30,0x30,0x30, - 0x34,0x38,0x38,0x32,0x38,0x31,0x32,0x35,0x66,0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30, - 0x32,0x39,0x32,0x39,0x36,0x38,0x37,0x35,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31,0x20,0x3d,0x20,0x78,0x5f,0x31,0x32,0x33, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x32,0x36,0x20, - 0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x5f,0x76,0x66,0x32,0x5f,0x76,0x66,0x32,0x5f,0x28,0x26,0x28,0x70, - 0x61,0x72,0x61,0x6d,0x29,0x2c,0x20,0x26,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x31, - 0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x32, - 0x37,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73, - 0x61,0x6d,0x70,0x6c,0x65,0x5f,0x73,0x63,0x61,0x6c,0x65,0x5f,0x31,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x32,0x39,0x20,0x3a,0x20,0x76, - 0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x20,0x3d,0x20,0x28,0x78,0x5f,0x31,0x32, - 0x39,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x30,0x30,0x30,0x32, - 0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x66,0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30, - 0x32,0x39,0x32,0x39,0x36,0x38,0x37,0x35,0x66,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x20,0x3d,0x20,0x78,0x5f,0x31,0x32,0x33, - 0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x33,0x35,0x20, - 0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x5f,0x76,0x66,0x32,0x5f,0x76,0x66,0x32,0x5f,0x28,0x26,0x28,0x70, + 0x31,0x20,0x3d,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2f,0x20,0x78,0x5f,0x31,0x33, + 0x31,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x33, + 0x36,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x5f,0x31, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x34,0x30,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72, + 0x65,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x70,0x61, + 0x72,0x61,0x6d,0x20,0x3d,0x20,0x28,0x78,0x5f,0x31,0x33,0x36,0x20,0x2b,0x20,0x28, + 0x76,0x65,0x63,0x32,0x66,0x28,0x2d,0x31,0x2e,0x30,0x66,0x2c,0x20,0x2d,0x31,0x2e, + 0x35,0x66,0x29,0x20,0x2a,0x20,0x78,0x5f,0x31,0x34,0x30,0x29,0x29,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x34,0x35,0x20,0x3a,0x20,0x76, + 0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73, + 0x69,0x7a,0x65,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x70,0x61,0x72,0x61,0x6d, + 0x5f,0x31,0x20,0x3d,0x20,0x78,0x5f,0x31,0x34,0x35,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x34,0x36,0x20,0x3a,0x20,0x66,0x33,0x32,0x20, + 0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f, + 0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x66,0x32,0x5f,0x76,0x66,0x32, + 0x5f,0x28,0x26,0x28,0x70,0x61,0x72,0x61,0x6d,0x29,0x2c,0x20,0x26,0x28,0x70,0x61, + 0x72,0x61,0x6d,0x5f,0x31,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x31,0x34,0x37,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64, + 0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x31,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x34,0x39,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20, + 0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x35,0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x32, + 0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65, + 0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x32,0x20, + 0x3d,0x20,0x28,0x78,0x5f,0x31,0x34,0x39,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32, + 0x66,0x28,0x30,0x2e,0x35,0x66,0x2c,0x20,0x2d,0x31,0x2e,0x35,0x66,0x29,0x20,0x2a, + 0x20,0x78,0x5f,0x31,0x35,0x32,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x31,0x35,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20, + 0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x31, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x33,0x20,0x3d,0x20, + 0x78,0x5f,0x31,0x35,0x37,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78, + 0x5f,0x31,0x35,0x38,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77, + 0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5f,0x76,0x66,0x32,0x5f,0x76,0x66,0x32,0x5f,0x28,0x26,0x28,0x70, 0x61,0x72,0x61,0x6d,0x5f,0x32,0x29,0x2c,0x20,0x26,0x28,0x70,0x61,0x72,0x61,0x6d, 0x5f,0x33,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f, - 0x31,0x33,0x36,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e, - 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x73,0x63,0x61,0x6c,0x65,0x5f,0x31,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x33,0x39,0x20,0x3a, - 0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x5f,0x31,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x34,0x20,0x3d,0x20,0x28,0x78,0x5f, - 0x31,0x33,0x39,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x2d,0x30,0x2e,0x30, - 0x30,0x30,0x37,0x33,0x32,0x34,0x32,0x31,0x38,0x37,0x35,0x66,0x2c,0x20,0x30,0x2e, - 0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66,0x29,0x29,0x3b,0x0a,0x20, + 0x31,0x35,0x39,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e, + 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x31,0x36,0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, + 0x20,0x3d,0x20,0x75,0x76,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74, + 0x20,0x78,0x5f,0x31,0x36,0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d, + 0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x31,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x34,0x20,0x3d,0x20,0x28, + 0x78,0x5f,0x31,0x36,0x32,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x2d, + 0x31,0x2e,0x35,0x66,0x2c,0x20,0x30,0x2e,0x35,0x66,0x29,0x20,0x2a,0x20,0x78,0x5f, + 0x31,0x36,0x34,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78, + 0x5f,0x31,0x36,0x39,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x31,0x3b,0x0a,0x20, 0x20,0x20,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x20,0x3d,0x20,0x78,0x5f,0x31, - 0x32,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x34, - 0x36,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73, - 0x61,0x6d,0x70,0x6c,0x65,0x5f,0x76,0x66,0x32,0x5f,0x76,0x66,0x32,0x5f,0x28,0x26, - 0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x34,0x29,0x2c,0x20,0x26,0x28,0x70,0x61,0x72, - 0x61,0x6d,0x5f,0x35,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20, - 0x78,0x5f,0x31,0x34,0x37,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f, - 0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x73,0x63,0x61,0x6c,0x65,0x5f, - 0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x35,0x30, - 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x5f,0x31,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x20,0x3d,0x20,0x28, - 0x78,0x5f,0x31,0x35,0x30,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e, - 0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x66,0x2c,0x20,0x30, - 0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66,0x29,0x29,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x20,0x3d,0x20,0x78,0x5f, - 0x31,0x32,0x33,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31, - 0x35,0x35,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f, - 0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x76,0x66,0x32,0x5f,0x76,0x66,0x32,0x5f,0x28, - 0x26,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x29,0x2c,0x20,0x26,0x28,0x70,0x61, - 0x72,0x61,0x6d,0x5f,0x37,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74, - 0x20,0x78,0x5f,0x31,0x35,0x36,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64, - 0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x5f,0x73,0x63,0x61,0x6c,0x65, + 0x36,0x39,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x37, + 0x30,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x5f,0x76,0x66,0x32,0x5f,0x76,0x66,0x32,0x5f,0x28,0x26,0x28,0x70,0x61,0x72,0x61, + 0x6d,0x5f,0x34,0x29,0x2c,0x20,0x26,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x35,0x29, + 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x37,0x31, + 0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20, + 0x78,0x5f,0x31,0x37,0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20, + 0x75,0x76,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f, + 0x31,0x37,0x36,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x31,0x3b,0x0a,0x20,0x20, + 0x20,0x20,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x20,0x3d,0x20,0x28,0x78,0x5f,0x31, + 0x37,0x34,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x35,0x66, + 0x2c,0x20,0x30,0x2e,0x35,0x66,0x29,0x20,0x2a,0x20,0x78,0x5f,0x31,0x37,0x36,0x29, + 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x38,0x31, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x70, + 0x61,0x72,0x61,0x6d,0x5f,0x37,0x20,0x3d,0x20,0x78,0x5f,0x31,0x38,0x31,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x38,0x32,0x20,0x3a,0x20, + 0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x5f,0x74,0x6f,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x66,0x32, + 0x5f,0x76,0x66,0x32,0x5f,0x28,0x26,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x36,0x29, + 0x2c,0x20,0x26,0x28,0x70,0x61,0x72,0x61,0x6d,0x5f,0x37,0x29,0x29,0x3b,0x0a,0x20, + 0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x38,0x33,0x20,0x3a,0x20,0x66, + 0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, 0x5f,0x31,0x3b,0x0a,0x20,0x20,0x20,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3d,0x20, - 0x28,0x28,0x28,0x28,0x78,0x5f,0x31,0x32,0x36,0x20,0x2a,0x20,0x78,0x5f,0x31,0x32, - 0x37,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x31,0x33,0x35,0x20,0x2a,0x20,0x78,0x5f, - 0x31,0x33,0x36,0x29,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x31,0x34,0x36,0x20,0x2a, - 0x20,0x78,0x5f,0x31,0x34,0x37,0x29,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x31,0x35, - 0x35,0x20,0x2a,0x20,0x78,0x5f,0x31,0x35,0x36,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x36,0x31,0x20,0x3a,0x20,0x66,0x33,0x32, + 0x28,0x28,0x28,0x28,0x78,0x5f,0x31,0x34,0x36,0x20,0x2a,0x20,0x78,0x5f,0x31,0x34, + 0x37,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x31,0x35,0x38,0x20,0x2a,0x20,0x78,0x5f, + 0x31,0x35,0x39,0x29,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x31,0x37,0x30,0x20,0x2a, + 0x20,0x78,0x5f,0x31,0x37,0x31,0x29,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x31,0x38, + 0x32,0x20,0x2a,0x20,0x78,0x5f,0x31,0x38,0x33,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x38,0x38,0x20,0x3a,0x20,0x66,0x33,0x32, 0x20,0x3d,0x20,0x61,0x6c,0x70,0x68,0x61,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72, 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x66, 0x28,0x31,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30,0x66,0x2c,0x20,0x31,0x2e,0x30, - 0x66,0x2c,0x20,0x78,0x5f,0x31,0x36,0x31,0x29,0x3b,0x0a,0x20,0x20,0x7d,0x20,0x65, + 0x66,0x2c,0x20,0x78,0x5f,0x31,0x38,0x38,0x29,0x3b,0x0a,0x20,0x20,0x7d,0x20,0x65, 0x6c,0x73,0x65,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63, 0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x30,0x2e,0x30, 0x66,0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x30,0x2e,0x30,0x66,0x2c,0x20,0x31, @@ -1448,23 +1648,29 @@ blit_atlas_fs_source_wgsl := [3640]u8 { 0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f, 0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, } -blit_atlas_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { +ve_blit_atlas_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { desc: sg.Shader_Desc - desc.label = "blit_atlas_shader" + desc.label = "ve_blit_atlas_shader" #partial switch backend { case .GLCORE: - desc.vertex_func.source = transmute(cstring)&blit_atlas_vs_source_glsl410 + desc.vertex_func.source = transmute(cstring)&ve_blit_atlas_vs_source_glsl410 desc.vertex_func.entry = "main" - desc.fragment_func.source = transmute(cstring)&blit_atlas_fs_source_glsl410 + desc.fragment_func.source = transmute(cstring)&ve_blit_atlas_fs_source_glsl410 desc.fragment_func.entry = "main" desc.attrs[0].glsl_name = "v_position" desc.attrs[1].glsl_name = "v_texture" desc.uniform_blocks[0].stage = .FRAGMENT desc.uniform_blocks[0].layout = .STD140 desc.uniform_blocks[0].size = 16 - desc.uniform_blocks[0].glsl_uniforms[0].type = .INT4 - desc.uniform_blocks[0].glsl_uniforms[0].array_count = 1 - desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "blit_atlas_fs_params" + desc.uniform_blocks[0].glsl_uniforms[0].type = .FLOAT2 + desc.uniform_blocks[0].glsl_uniforms[0].array_count = 0 + desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "_20.glyph_buffer_size" + desc.uniform_blocks[0].glsl_uniforms[1].type = .FLOAT + desc.uniform_blocks[0].glsl_uniforms[1].array_count = 0 + desc.uniform_blocks[0].glsl_uniforms[1].glsl_name = "_20.over_sample" + desc.uniform_blocks[0].glsl_uniforms[2].type = .INT + desc.uniform_blocks[0].glsl_uniforms[2].array_count = 0 + desc.uniform_blocks[0].glsl_uniforms[2].glsl_name = "_20.region" desc.images[0].stage = .FRAGMENT desc.images[0].multisampled = false desc.images[0].image_type = ._2D @@ -1474,20 +1680,26 @@ blit_atlas_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { desc.image_sampler_pairs[0].stage = .FRAGMENT desc.image_sampler_pairs[0].image_slot = 0 desc.image_sampler_pairs[0].sampler_slot = 0 - desc.image_sampler_pairs[0].glsl_name = "blit_atlas_src_texture_blit_atlas_src_sampler" + desc.image_sampler_pairs[0].glsl_name = "ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler" case .GLES3: - desc.vertex_func.source = transmute(cstring)&blit_atlas_vs_source_glsl300es + desc.vertex_func.source = transmute(cstring)&ve_blit_atlas_vs_source_glsl300es desc.vertex_func.entry = "main" - desc.fragment_func.source = transmute(cstring)&blit_atlas_fs_source_glsl300es + desc.fragment_func.source = transmute(cstring)&ve_blit_atlas_fs_source_glsl300es desc.fragment_func.entry = "main" desc.attrs[0].glsl_name = "v_position" desc.attrs[1].glsl_name = "v_texture" desc.uniform_blocks[0].stage = .FRAGMENT desc.uniform_blocks[0].layout = .STD140 desc.uniform_blocks[0].size = 16 - desc.uniform_blocks[0].glsl_uniforms[0].type = .INT4 - desc.uniform_blocks[0].glsl_uniforms[0].array_count = 1 - desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "blit_atlas_fs_params" + desc.uniform_blocks[0].glsl_uniforms[0].type = .FLOAT2 + desc.uniform_blocks[0].glsl_uniforms[0].array_count = 0 + desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "_20.glyph_buffer_size" + desc.uniform_blocks[0].glsl_uniforms[1].type = .FLOAT + desc.uniform_blocks[0].glsl_uniforms[1].array_count = 0 + desc.uniform_blocks[0].glsl_uniforms[1].glsl_name = "_20.over_sample" + desc.uniform_blocks[0].glsl_uniforms[2].type = .INT + desc.uniform_blocks[0].glsl_uniforms[2].array_count = 0 + desc.uniform_blocks[0].glsl_uniforms[2].glsl_name = "_20.region" desc.images[0].stage = .FRAGMENT desc.images[0].multisampled = false desc.images[0].image_type = ._2D @@ -1497,12 +1709,12 @@ blit_atlas_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { desc.image_sampler_pairs[0].stage = .FRAGMENT desc.image_sampler_pairs[0].image_slot = 0 desc.image_sampler_pairs[0].sampler_slot = 0 - desc.image_sampler_pairs[0].glsl_name = "blit_atlas_src_texture_blit_atlas_src_sampler" + desc.image_sampler_pairs[0].glsl_name = "ve_blit_atlas_src_texture_ve_blit_atlas_src_sampler" case .D3D11: - desc.vertex_func.source = transmute(cstring)&blit_atlas_vs_source_hlsl4 + desc.vertex_func.source = transmute(cstring)&ve_blit_atlas_vs_source_hlsl4 desc.vertex_func.d3d11_target = "vs_4_0" desc.vertex_func.entry = "main" - desc.fragment_func.source = transmute(cstring)&blit_atlas_fs_source_hlsl4 + desc.fragment_func.source = transmute(cstring)&ve_blit_atlas_fs_source_hlsl4 desc.fragment_func.d3d11_target = "ps_4_0" desc.fragment_func.entry = "main" desc.attrs[0].hlsl_sem_name = "TEXCOORD" @@ -1525,9 +1737,9 @@ blit_atlas_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { desc.image_sampler_pairs[0].image_slot = 0 desc.image_sampler_pairs[0].sampler_slot = 0 case .METAL_MACOS: - desc.vertex_func.source = transmute(cstring)&blit_atlas_vs_source_metal_macos + desc.vertex_func.source = transmute(cstring)&ve_blit_atlas_vs_source_metal_macos desc.vertex_func.entry = "main0" - desc.fragment_func.source = transmute(cstring)&blit_atlas_fs_source_metal_macos + desc.fragment_func.source = transmute(cstring)&ve_blit_atlas_fs_source_metal_macos desc.fragment_func.entry = "main0" desc.uniform_blocks[0].stage = .FRAGMENT desc.uniform_blocks[0].layout = .STD140 @@ -1545,9 +1757,9 @@ blit_atlas_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { desc.image_sampler_pairs[0].image_slot = 0 desc.image_sampler_pairs[0].sampler_slot = 0 case .WGPU: - desc.vertex_func.source = transmute(cstring)&blit_atlas_vs_source_wgsl + desc.vertex_func.source = transmute(cstring)&ve_blit_atlas_vs_source_wgsl desc.vertex_func.entry = "main" - desc.fragment_func.source = transmute(cstring)&blit_atlas_fs_source_wgsl + desc.fragment_func.source = transmute(cstring)&ve_blit_atlas_fs_source_wgsl desc.fragment_func.entry = "main" desc.uniform_blocks[0].stage = .FRAGMENT desc.uniform_blocks[0].layout = .STD140 diff --git a/backend/sokol/blit_atlas.shdc.glsl b/backend/sokol/blit_atlas.shdc.glsl index de64c33..0cfdd8d 100644 --- a/backend/sokol/blit_atlas.shdc.glsl +++ b/backend/sokol/blit_atlas.shdc.glsl @@ -1,48 +1,50 @@ -@module blit_atlas +@module ve_blit_atlas @header package ve_sokol @header import sg "thirdparty:sokol/gfx" -@vs blit_atlas_vs +@vs ve_blit_atlas_vs @include ./source_shared.shdc.glsl @end -@fs blit_atlas_fs +@fs ve_blit_atlas_fs in vec2 uv; out vec4 frag_color; -layout(binding = 0) uniform texture2D blit_atlas_src_texture; -layout(binding = 0) uniform sampler blit_atlas_src_sampler; +layout(binding = 0) uniform texture2D ve_blit_atlas_src_texture; +layout(binding = 0) uniform sampler ve_blit_atlas_src_sampler; -layout(binding = 0) uniform blit_atlas_fs_params { - int region; +layout(binding = 0) uniform ve_blit_atlas_fs_params { + vec2 glyph_buffer_size; + float over_sample; + int region; }; -float down_sample( vec2 uv, vec2 texture_size ) +float down_sample_to_texture( vec2 uv, vec2 texture_size ) { - float down_sample_scale = 1.0f / 4.0f; + float down_sample = 1.0f / over_sample; float value = - texture(sampler2D( blit_atlas_src_texture, blit_atlas_src_sampler ), uv + vec2( 0.0f, 0.0f ) * texture_size ).x * down_sample_scale - + texture(sampler2D( blit_atlas_src_texture, blit_atlas_src_sampler ), uv + vec2( 0.0f, 1.0f ) * texture_size ).x * down_sample_scale - + texture(sampler2D( blit_atlas_src_texture, blit_atlas_src_sampler ), uv + vec2( 1.0f, 0.0f ) * texture_size ).x * down_sample_scale - + texture(sampler2D( blit_atlas_src_texture, blit_atlas_src_sampler ), uv + vec2( 1.0f, 1.0f ) * texture_size ).x * down_sample_scale; + texture(sampler2D( ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler ), uv + vec2( 0.0f, 0.0f ) * texture_size ).x * down_sample + + texture(sampler2D( ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler ), uv + vec2( 0.0f, 1.0f ) * texture_size ).x * down_sample + + texture(sampler2D( ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler ), uv + vec2( 1.0f, 0.0f ) * texture_size ).x * down_sample + + texture(sampler2D( ve_blit_atlas_src_texture, ve_blit_atlas_src_sampler ), uv + vec2( 1.0f, 1.0f ) * texture_size ).x * down_sample; + return value; } void main() { - // TODO(Ed): The original author made these consts, I want to instead expose as uniforms... - const vec2 texture_size = 1.0f / vec2( 2048.0f, 512.0f ); // VEFontCache.Context.buffer_width/buffer_height - if ( region == 0 || region == 1 || region == 2 ) + const vec2 texture_size = 1.0f / glyph_buffer_size; + if ( region == 0 || region == 1 || region == 2 || region == 4 ) { - float down_sample_scale = 1.0f / 4.0f; + float down_sample = 1.0f / over_sample; float alpha = - down_sample( uv + vec2( -1.0f, -1.5f ) * texture_size, texture_size ) * down_sample_scale - + down_sample( uv + vec2( 0.5f, -1.5f ) * texture_size, texture_size ) * down_sample_scale - + down_sample( uv + vec2( -1.5f, 0.5f ) * texture_size, texture_size ) * down_sample_scale - + down_sample( uv + vec2( 0.5f, 0.5f ) * texture_size, texture_size ) * down_sample_scale; + down_sample_to_texture( uv + vec2( -1.0f, -1.5f ) * texture_size, texture_size ) * down_sample + + down_sample_to_texture( uv + vec2( 0.5f, -1.5f ) * texture_size, texture_size ) * down_sample + + down_sample_to_texture( uv + vec2( -1.5f, 0.5f ) * texture_size, texture_size ) * down_sample + + down_sample_to_texture( uv + vec2( 0.5f, 0.5f ) * texture_size, texture_size ) * down_sample; frag_color = vec4( 1.0f, 1.0f, 1.0f, alpha ); } else @@ -52,4 +54,4 @@ void main() } @end -@program blit_atlas blit_atlas_vs blit_atlas_fs +@program ve_blit_atlas ve_blit_atlas_vs ve_blit_atlas_fs diff --git a/backend/sokol/draw_text.odin b/backend/sokol/draw_text.odin index 285e2fc..6bd09e4 100644 --- a/backend/sokol/draw_text.odin +++ b/backend/sokol/draw_text.odin @@ -10,35 +10,36 @@ import sg "thirdparty:sokol/gfx" Overview: ========= - Shader program: 'draw_text': - Get shader desc: draw_text_shader_desc(sg.query_backend()) - Vertex Shader: draw_text_vs - Fragment Shader: draw_text_fs + Shader program: 've_draw_text': + Get shader desc: ve_draw_text_shader_desc(sg.query_backend()) + Vertex Shader: ve_draw_text_vs + Fragment Shader: ve_draw_text_fs Attributes: - ATTR_draw_text_v_position => 0 - ATTR_draw_text_v_texture => 1 + ATTR_ve_draw_text_v_position => 0 + ATTR_ve_draw_text_v_texture => 1 Bindings: - Uniform block 'draw_text_fs_params': - Odin struct: Draw_Text_Fs_Params - Bind slot: UB_draw_text_fs_params => 0 - Image 'draw_text_src_texture': + Uniform block 've_draw_text_fs_params': + Odin struct: Ve_Draw_Text_Fs_Params + Bind slot: UB_ve_draw_text_fs_params => 0 + Image 've_draw_text_src_texture': Image type: ._2D Sample type: .FLOAT Multisampled: false - Bind slot: IMG_draw_text_src_texture => 0 - Sampler 'draw_text_src_sampler': + Bind slot: IMG_ve_draw_text_src_texture => 0 + Sampler 've_draw_text_src_sampler': Type: .FILTERING - Bind slot: SMP_draw_text_src_sampler => 0 + Bind slot: SMP_ve_draw_text_src_sampler => 0 */ -ATTR_draw_text_v_position :: 0 -ATTR_draw_text_v_texture :: 1 -UB_draw_text_fs_params :: 0 -IMG_draw_text_src_texture :: 0 -SMP_draw_text_src_sampler :: 0 -Draw_Text_Fs_Params :: struct #align(16) { +ATTR_ve_draw_text_v_position :: 0 +ATTR_ve_draw_text_v_texture :: 1 +UB_ve_draw_text_fs_params :: 0 +IMG_ve_draw_text_src_texture :: 0 +SMP_ve_draw_text_src_sampler :: 0 +Ve_Draw_Text_Fs_Params :: struct #align(16) { using _: struct #packed { - down_sample: i32, - _: [12]u8, + glyph_buffer_size: [2]f32, + over_sample: f32, + _: [4]u8, colour: [4]f32, }, } @@ -57,7 +58,7 @@ Draw_Text_Fs_Params :: struct #align(16) { */ @(private="file") -draw_text_vs_source_glsl410 := [255]u8 { +ve_draw_text_vs_source_glsl410 := [255]u8 { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x6c,0x61, 0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, 0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a, @@ -78,88 +79,71 @@ draw_text_vs_source_glsl410 := [255]u8 { /* #version 410 - struct draw_text_fs_params - { - int down_sample; - vec4 colour; - }; - - uniform draw_text_fs_params _31; - - uniform sampler2D draw_text_src_texture_draw_text_src_sampler; + uniform vec4 ve_draw_text_fs_params[2]; + uniform sampler2D ve_draw_text_src_texture_ve_draw_text_src_sampler; layout(location = 0) in vec2 uv; layout(location = 0) out vec4 frag_color; void main() { - float alpha = texture(draw_text_src_texture_draw_text_src_sampler, uv).x; - if (_31.down_sample == 1) - { - alpha = 0.25 * (((texture(draw_text_src_texture_draw_text_src_sampler, uv + vec2(-0.000244140625, -0.0009765625)).x + texture(draw_text_src_texture_draw_text_src_sampler, uv + vec2(-0.000244140625, 0.0009765625)).x) + texture(draw_text_src_texture_draw_text_src_sampler, uv + vec2(0.000244140625, -0.0009765625)).x) + texture(draw_text_src_texture_draw_text_src_sampler, uv + vec2(0.000244140625, 0.0009765625)).x); - } - frag_color = vec4(_31.colour.xyz, _31.colour.w * alpha); + frag_color = vec4(ve_draw_text_fs_params[1].xyz, ve_draw_text_fs_params[1].w * ((1.0 / ve_draw_text_fs_params[0].z) * (((texture(ve_draw_text_src_texture_ve_draw_text_src_sampler, fma(vec2(-0.5), ve_draw_text_fs_params[0].xy, uv)).x + texture(ve_draw_text_src_texture_ve_draw_text_src_sampler, fma(vec2(-0.5, 0.5), ve_draw_text_fs_params[0].xy, uv)).x) + texture(ve_draw_text_src_texture_ve_draw_text_src_sampler, fma(vec2(0.5, -0.5), ve_draw_text_fs_params[0].xy, uv)).x) + texture(ve_draw_text_src_texture_ve_draw_text_src_sampler, fma(vec2(0.5), ve_draw_text_fs_params[0].xy, uv)).x))); } */ @(private="file") -draw_text_fs_source_glsl410 := [882]u8 { - 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x73,0x74, - 0x72,0x75,0x63,0x74,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66, - 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x69, - 0x6e,0x74,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x76,0x65,0x63,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x3b, - 0x0a,0x7d,0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x64,0x72,0x61, - 0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, - 0x20,0x5f,0x33,0x31,0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x73, - 0x61,0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, - 0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x64, - 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f, - 0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x69,0x6e,0x20,0x76, - 0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c, - 0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74, - 0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, - 0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b, - 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x61,0x6c,0x70,0x68,0x61, - 0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x64,0x72,0x61,0x77,0x5f, +ve_draw_text_fs_source_glsl410 := [812]u8 { + 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x34,0x31,0x30,0x0a,0x0a,0x75,0x6e, + 0x69,0x66,0x6f,0x72,0x6d,0x20,0x76,0x65,0x63,0x34,0x20,0x76,0x65,0x5f,0x64,0x72, + 0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x32,0x5d,0x3b,0x0a,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x32,0x44,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f, 0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x5f,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72, + 0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x6c,0x61,0x79,0x6f, + 0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29, + 0x20,0x69,0x6e,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79, + 0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30, + 0x29,0x20,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f, + 0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69, + 0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x76,0x65,0x5f,0x64, + 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61, + 0x6d,0x73,0x5b,0x31,0x5d,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x76,0x65,0x5f,0x64,0x72, + 0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d, + 0x73,0x5b,0x31,0x5d,0x2e,0x77,0x20,0x2a,0x20,0x28,0x28,0x31,0x2e,0x30,0x20,0x2f, + 0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73, + 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x7a,0x29,0x20,0x2a,0x20, + 0x28,0x28,0x28,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76,0x65,0x5f,0x64,0x72, + 0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74, + 0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74, + 0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x66,0x6d, + 0x61,0x28,0x76,0x65,0x63,0x32,0x28,0x2d,0x30,0x2e,0x35,0x29,0x2c,0x20,0x76,0x65, + 0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61, + 0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x2c,0x20,0x75,0x76,0x29,0x29, + 0x2e,0x78,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76,0x65,0x5f, + 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, + 0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20, + 0x66,0x6d,0x61,0x28,0x76,0x65,0x63,0x32,0x28,0x2d,0x30,0x2e,0x35,0x2c,0x20,0x30, + 0x2e,0x35,0x29,0x2c,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78, + 0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78, + 0x79,0x2c,0x20,0x75,0x76,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78, + 0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65, 0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73, - 0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x29,0x2e,0x78,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f,0x33,0x31,0x2e,0x64,0x6f,0x77,0x6e,0x5f, - 0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x0a,0x20,0x20,0x20, - 0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x6c,0x70,0x68,0x61, - 0x20,0x3d,0x20,0x30,0x2e,0x32,0x35,0x20,0x2a,0x20,0x28,0x28,0x28,0x74,0x65,0x78, - 0x74,0x75,0x72,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73, - 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f, - 0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x2d,0x30,0x2e,0x30, - 0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x2c,0x20,0x2d,0x30,0x2e, - 0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x29,0x2e,0x78,0x20,0x2b, - 0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, - 0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x64, - 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28, - 0x2d,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x2c, - 0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x29,0x2e, - 0x78,0x29,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x64,0x72,0x61, - 0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63, - 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x20,0x2b,0x20,0x76, - 0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36, - 0x32,0x35,0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32, - 0x35,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, - 0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74, - 0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74, - 0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76, - 0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34, - 0x31,0x34,0x30,0x36,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36, - 0x35,0x36,0x32,0x35,0x29,0x29,0x2e,0x78,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d, - 0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20, - 0x3d,0x20,0x76,0x65,0x63,0x34,0x28,0x5f,0x33,0x31,0x2e,0x63,0x6f,0x6c,0x6f,0x75, - 0x72,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x5f,0x33,0x31,0x2e,0x63,0x6f,0x6c,0x6f,0x75, - 0x72,0x2e,0x77,0x20,0x2a,0x20,0x61,0x6c,0x70,0x68,0x61,0x29,0x3b,0x0a,0x7d,0x0a, - 0x0a,0x00, + 0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x66,0x6d,0x61,0x28,0x76,0x65,0x63,0x32, + 0x28,0x30,0x2e,0x35,0x2c,0x20,0x2d,0x30,0x2e,0x35,0x29,0x2c,0x20,0x76,0x65,0x5f, + 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x2c,0x20,0x75,0x76,0x29,0x29,0x2e, + 0x78,0x29,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76,0x65,0x5f, + 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, + 0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20, + 0x66,0x6d,0x61,0x28,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x35,0x29,0x2c,0x20,0x76, + 0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x2c,0x20,0x75,0x76,0x29, + 0x29,0x2e,0x78,0x29,0x29,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, } /* #version 300 es @@ -176,7 +160,7 @@ draw_text_fs_source_glsl410 := [882]u8 { */ @(private="file") -draw_text_vs_source_glsl300es := [237]u8 { +ve_draw_text_vs_source_glsl300es := [237]u8 { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, 0x0a,0x6f,0x75,0x74,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61, 0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20, @@ -198,91 +182,74 @@ draw_text_vs_source_glsl300es := [237]u8 { precision mediump float; precision highp int; - struct draw_text_fs_params - { - int down_sample; - highp vec4 colour; - }; - - uniform draw_text_fs_params _31; - - uniform highp sampler2D draw_text_src_texture_draw_text_src_sampler; + uniform highp vec4 ve_draw_text_fs_params[2]; + uniform highp sampler2D ve_draw_text_src_texture_ve_draw_text_src_sampler; in highp vec2 uv; layout(location = 0) out highp vec4 frag_color; void main() { - highp float alpha = texture(draw_text_src_texture_draw_text_src_sampler, uv).x; - if (_31.down_sample == 1) - { - alpha = 0.25 * (((texture(draw_text_src_texture_draw_text_src_sampler, uv + vec2(-0.000244140625, -0.0009765625)).x + texture(draw_text_src_texture_draw_text_src_sampler, uv + vec2(-0.000244140625, 0.0009765625)).x) + texture(draw_text_src_texture_draw_text_src_sampler, uv + vec2(0.000244140625, -0.0009765625)).x) + texture(draw_text_src_texture_draw_text_src_sampler, uv + vec2(0.000244140625, 0.0009765625)).x); - } - frag_color = vec4(_31.colour.xyz, _31.colour.w * alpha); + frag_color = vec4(ve_draw_text_fs_params[1].xyz, ve_draw_text_fs_params[1].w * ((1.0 / ve_draw_text_fs_params[0].z) * (((texture(ve_draw_text_src_texture_ve_draw_text_src_sampler, vec2(-0.5) * ve_draw_text_fs_params[0].xy + uv).x + texture(ve_draw_text_src_texture_ve_draw_text_src_sampler, vec2(-0.5, 0.5) * ve_draw_text_fs_params[0].xy + uv).x) + texture(ve_draw_text_src_texture_ve_draw_text_src_sampler, vec2(0.5, -0.5) * ve_draw_text_fs_params[0].xy + uv).x) + texture(ve_draw_text_src_texture_ve_draw_text_src_sampler, vec2(0.5) * ve_draw_text_fs_params[0].xy + uv).x))); } */ @(private="file") -draw_text_fs_source_glsl300es := [940]u8 { +ve_draw_text_fs_source_glsl300es := [852]u8 { 0x23,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x33,0x30,0x30,0x20,0x65,0x73,0x0a, 0x70,0x72,0x65,0x63,0x69,0x73,0x69,0x6f,0x6e,0x20,0x6d,0x65,0x64,0x69,0x75,0x6d, 0x70,0x20,0x66,0x6c,0x6f,0x61,0x74,0x3b,0x0a,0x70,0x72,0x65,0x63,0x69,0x73,0x69, - 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x73, - 0x74,0x72,0x75,0x63,0x74,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, - 0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x69,0x6e,0x74,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20, - 0x63,0x6f,0x6c,0x6f,0x75,0x72,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x75,0x6e,0x69,0x66, - 0x6f,0x72,0x6d,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73, - 0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x5f,0x33,0x31,0x3b,0x0a,0x0a,0x75,0x6e, - 0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d,0x70, - 0x6c,0x65,0x72,0x32,0x44,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, - 0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x64,0x72,0x61,0x77, - 0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x72,0x3b,0x0a,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63, - 0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75,0x74,0x28,0x6c,0x6f,0x63, - 0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20,0x6f,0x75,0x74,0x20,0x68, - 0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63, - 0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x6d,0x61,0x69,0x6e, - 0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3d,0x20,0x74,0x65,0x78, - 0x74,0x75,0x72,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73, - 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f, + 0x6f,0x6e,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x69,0x6e,0x74,0x3b,0x0a,0x0a,0x75, + 0x6e,0x69,0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63, + 0x34,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66, + 0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x32,0x5d,0x3b,0x0a,0x75,0x6e,0x69, + 0x66,0x6f,0x72,0x6d,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x72,0x32,0x44,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78, + 0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65, + 0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x69,0x6e,0x20,0x68,0x69,0x67,0x68, + 0x70,0x20,0x76,0x65,0x63,0x32,0x20,0x75,0x76,0x3b,0x0a,0x6c,0x61,0x79,0x6f,0x75, + 0x74,0x28,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x20,0x3d,0x20,0x30,0x29,0x20, + 0x6f,0x75,0x74,0x20,0x68,0x69,0x67,0x68,0x70,0x20,0x76,0x65,0x63,0x34,0x20,0x66, + 0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64, + 0x20,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72, + 0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x28, + 0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x76, + 0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x31,0x5d,0x2e,0x77,0x20,0x2a,0x20,0x28,0x28,0x31, + 0x2e,0x30,0x20,0x2f,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78, + 0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x7a, + 0x29,0x20,0x2a,0x20,0x28,0x28,0x28,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76, + 0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f, 0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x75,0x76,0x29,0x2e,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20, - 0x28,0x5f,0x33,0x31,0x2e,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x20,0x3d,0x3d,0x20,0x31,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3d,0x20,0x30,0x2e,0x32, - 0x35,0x20,0x2a,0x20,0x28,0x28,0x28,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x64, - 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78, - 0x74,0x75,0x72,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73, - 0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x20,0x2b, - 0x20,0x76,0x65,0x63,0x32,0x28,0x2d,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31, - 0x34,0x30,0x36,0x32,0x35,0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36, - 0x35,0x36,0x32,0x35,0x29,0x29,0x2e,0x78,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75, - 0x72,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63, - 0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, - 0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20, - 0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x2d,0x30,0x2e,0x30,0x30,0x30, - 0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x30, - 0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x74, - 0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74, - 0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x64,0x72,0x61, - 0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c, - 0x65,0x72,0x2c,0x20,0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e, - 0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x2c,0x20,0x2d,0x30, - 0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x29,0x2e,0x78,0x29, - 0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x64,0x72,0x61,0x77,0x5f, + 0x2c,0x20,0x76,0x65,0x63,0x32,0x28,0x2d,0x30,0x2e,0x35,0x29,0x20,0x2a,0x20,0x76, + 0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x20,0x2b,0x20,0x75,0x76, + 0x29,0x2e,0x78,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76,0x65, + 0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74, + 0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c, + 0x20,0x76,0x65,0x63,0x32,0x28,0x2d,0x30,0x2e,0x35,0x2c,0x20,0x30,0x2e,0x35,0x29, + 0x20,0x2a,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, + 0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d,0x2e,0x78,0x79,0x20, + 0x2b,0x20,0x75,0x76,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, + 0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x76,0x65,0x5f,0x64, + 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x2c,0x20,0x76,0x65,0x63,0x32,0x28,0x30,0x2e,0x35,0x2c,0x20, + 0x2d,0x30,0x2e,0x35,0x29,0x20,0x2a,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f, + 0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30, + 0x5d,0x2e,0x78,0x79,0x20,0x2b,0x20,0x75,0x76,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f, 0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65, - 0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73, - 0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x20,0x2b,0x20,0x76,0x65,0x63, - 0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35, - 0x2c,0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x29, - 0x2e,0x78,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x66, - 0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34, - 0x28,0x5f,0x33,0x31,0x2e,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x2e,0x78,0x79,0x7a,0x2c, - 0x20,0x5f,0x33,0x31,0x2e,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x2e,0x77,0x20,0x2a,0x20, - 0x61,0x6c,0x70,0x68,0x61,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x5f,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72, + 0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x76,0x65,0x63,0x32,0x28, + 0x30,0x2e,0x35,0x29,0x20,0x2a,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74, + 0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x5b,0x30,0x5d, + 0x2e,0x78,0x79,0x20,0x2b,0x20,0x75,0x76,0x29,0x2e,0x78,0x29,0x29,0x29,0x3b,0x0a, + 0x7d,0x0a,0x0a,0x00, } /* static float4 gl_Position; @@ -320,7 +287,7 @@ draw_text_fs_source_glsl300es := [940]u8 { } */ @(private="file") -draw_text_vs_source_hlsl4 := [724]u8 { +ve_draw_text_vs_source_hlsl4 := [724]u8 { 0x73,0x74,0x61,0x74,0x69,0x63,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x67,0x6c, 0x5f,0x50,0x6f,0x73,0x69,0x74,0x69,0x6f,0x6e,0x3b,0x0a,0x73,0x74,0x61,0x74,0x69, 0x63,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x3b,0x0a,0x73,0x74,0x61, @@ -369,14 +336,15 @@ draw_text_vs_source_hlsl4 := [724]u8 { 0x0a,0x7d,0x0a,0x00, } /* - cbuffer draw_text_fs_params : register(b0) + cbuffer ve_draw_text_fs_params : register(b0) { - int _31_down_sample : packoffset(c0); - float4 _31_colour : packoffset(c1); + float2 _32_glyph_buffer_size : packoffset(c0); + float _32_over_sample : packoffset(c0.z); + float4 _32_colour : packoffset(c1); }; - Texture2D draw_text_src_texture : register(t0); - SamplerState draw_text_src_sampler : register(s0); + Texture2D ve_draw_text_src_texture : register(t0); + SamplerState ve_draw_text_src_sampler : register(s0); static float2 uv; static float4 frag_color; @@ -393,12 +361,7 @@ draw_text_vs_source_hlsl4 := [724]u8 { void frag_main() { - float alpha = draw_text_src_texture.Sample(draw_text_src_sampler, uv).x; - if (_31_down_sample == 1) - { - alpha = 0.25f * (((draw_text_src_texture.Sample(draw_text_src_sampler, uv + float2(-0.000244140625f, -0.0009765625f)).x + draw_text_src_texture.Sample(draw_text_src_sampler, uv + float2(-0.000244140625f, 0.0009765625f)).x) + draw_text_src_texture.Sample(draw_text_src_sampler, uv + float2(0.000244140625f, -0.0009765625f)).x) + draw_text_src_texture.Sample(draw_text_src_sampler, uv + float2(0.000244140625f, 0.0009765625f)).x); - } - frag_color = float4(_31_colour.xyz, _31_colour.w * alpha); + frag_color = float4(_32_colour.xyz, _32_colour.w * ((1.0f / _32_over_sample) * (((ve_draw_text_src_texture.Sample(ve_draw_text_src_sampler, mad((-0.5f).xx, _32_glyph_buffer_size, uv)).x + ve_draw_text_src_texture.Sample(ve_draw_text_src_sampler, mad(float2(-0.5f, 0.5f), _32_glyph_buffer_size, uv)).x) + ve_draw_text_src_texture.Sample(ve_draw_text_src_sampler, mad(float2(0.5f, -0.5f), _32_glyph_buffer_size, uv)).x) + ve_draw_text_src_texture.Sample(ve_draw_text_src_sampler, mad(0.5f.xx, _32_glyph_buffer_size, uv)).x))); } SPIRV_Cross_Output main(SPIRV_Cross_Input stage_input) @@ -411,20 +374,24 @@ draw_text_vs_source_hlsl4 := [724]u8 { } */ @(private="file") -draw_text_fs_source_hlsl4 := [1257]u8 { - 0x63,0x62,0x75,0x66,0x66,0x65,0x72,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78, - 0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x3a,0x20,0x72,0x65, - 0x67,0x69,0x73,0x74,0x65,0x72,0x28,0x62,0x30,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20, - 0x20,0x69,0x6e,0x74,0x20,0x5f,0x33,0x31,0x5f,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61, - 0x6d,0x70,0x6c,0x65,0x20,0x3a,0x20,0x70,0x61,0x63,0x6b,0x6f,0x66,0x66,0x73,0x65, - 0x74,0x28,0x63,0x30,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, - 0x34,0x20,0x5f,0x33,0x31,0x5f,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x20,0x3a,0x20,0x70, - 0x61,0x63,0x6b,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x63,0x31,0x29,0x3b,0x0a,0x7d, - 0x3b,0x0a,0x0a,0x54,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x44,0x3c,0x66,0x6c,0x6f, - 0x61,0x74,0x34,0x3e,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73, - 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x20,0x3a,0x20,0x72,0x65,0x67, - 0x69,0x73,0x74,0x65,0x72,0x28,0x74,0x30,0x29,0x3b,0x0a,0x53,0x61,0x6d,0x70,0x6c, - 0x65,0x72,0x53,0x74,0x61,0x74,0x65,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78, +ve_draw_text_fs_source_hlsl4 := [1231]u8 { + 0x63,0x62,0x75,0x66,0x66,0x65,0x72,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f, + 0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x3a, + 0x20,0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x28,0x62,0x30,0x29,0x0a,0x7b,0x0a, + 0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x5f,0x33,0x32,0x5f,0x67, + 0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65, + 0x20,0x3a,0x20,0x70,0x61,0x63,0x6b,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x63,0x30, + 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x5f,0x33,0x32, + 0x5f,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x3a,0x20,0x70, + 0x61,0x63,0x6b,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x63,0x30,0x2e,0x7a,0x29,0x3b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x5f,0x33,0x32,0x5f, + 0x63,0x6f,0x6c,0x6f,0x75,0x72,0x20,0x3a,0x20,0x70,0x61,0x63,0x6b,0x6f,0x66,0x66, + 0x73,0x65,0x74,0x28,0x63,0x31,0x29,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x54,0x65,0x78, + 0x74,0x75,0x72,0x65,0x32,0x44,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x34,0x3e,0x20,0x76, + 0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x20,0x3a,0x20,0x72,0x65,0x67,0x69,0x73,0x74, + 0x65,0x72,0x28,0x74,0x30,0x29,0x3b,0x0a,0x53,0x61,0x6d,0x70,0x6c,0x65,0x72,0x53, + 0x74,0x61,0x74,0x65,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78, 0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x3a,0x20, 0x72,0x65,0x67,0x69,0x73,0x74,0x65,0x72,0x28,0x73,0x30,0x29,0x3b,0x0a,0x0a,0x73, 0x74,0x61,0x74,0x69,0x63,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x3b, @@ -439,58 +406,52 @@ draw_text_fs_source_hlsl4 := [1257]u8 { 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x53,0x56,0x5f,0x54,0x61,0x72, 0x67,0x65,0x74,0x30,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x76,0x6f,0x69,0x64,0x20,0x66, 0x72,0x61,0x67,0x5f,0x6d,0x61,0x69,0x6e,0x28,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20, - 0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3d,0x20,0x64, - 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78, - 0x74,0x75,0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x64,0x72,0x61,0x77, - 0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x72,0x2c,0x20,0x75,0x76,0x29,0x2e,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66, - 0x20,0x28,0x5f,0x33,0x31,0x5f,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c, - 0x65,0x20,0x3d,0x3d,0x20,0x31,0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20, - 0x20,0x20,0x20,0x20,0x20,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3d,0x20,0x30,0x2e, - 0x32,0x35,0x66,0x20,0x2a,0x20,0x28,0x28,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, - 0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x53, - 0x61,0x6d,0x70,0x6c,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, - 0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x20, - 0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x30,0x2e,0x30,0x30,0x30,0x32, - 0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x66,0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30, - 0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66,0x29,0x29,0x2e,0x78,0x20,0x2b,0x20, + 0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c, + 0x6f,0x61,0x74,0x34,0x28,0x5f,0x33,0x32,0x5f,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x2e, + 0x78,0x79,0x7a,0x2c,0x20,0x5f,0x33,0x32,0x5f,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x2e, + 0x77,0x20,0x2a,0x20,0x28,0x28,0x31,0x2e,0x30,0x66,0x20,0x2f,0x20,0x5f,0x33,0x32, + 0x5f,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x29,0x20,0x2a,0x20, + 0x28,0x28,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, + 0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x53,0x61,0x6d,0x70, + 0x6c,0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, + 0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x6d,0x61,0x64, + 0x28,0x28,0x2d,0x30,0x2e,0x35,0x66,0x29,0x2e,0x78,0x78,0x2c,0x20,0x5f,0x33,0x32, + 0x5f,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69, + 0x7a,0x65,0x2c,0x20,0x75,0x76,0x29,0x29,0x2e,0x78,0x20,0x2b,0x20,0x76,0x65,0x5f, 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65, - 0x78,0x74,0x75,0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x64,0x72,0x61, - 0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c, - 0x65,0x72,0x2c,0x20,0x75,0x76,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28, - 0x2d,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x66, - 0x2c,0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66,0x29, - 0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74, - 0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x53,0x61,0x6d, - 0x70,0x6c,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72, - 0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x75,0x76,0x20,0x2b,0x20, - 0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31, - 0x34,0x30,0x36,0x32,0x35,0x66,0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30,0x30,0x39,0x37, - 0x36,0x35,0x36,0x32,0x35,0x66,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x64,0x72, - 0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74, - 0x75,0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x64,0x72,0x61,0x77,0x5f, - 0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x75,0x76,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e, - 0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x66,0x2c,0x20,0x30, - 0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66,0x29,0x29,0x2e,0x78, - 0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61, - 0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34, - 0x28,0x5f,0x33,0x31,0x5f,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x2e,0x78,0x79,0x7a,0x2c, - 0x20,0x5f,0x33,0x31,0x5f,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x2e,0x77,0x20,0x2a,0x20, - 0x61,0x6c,0x70,0x68,0x61,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x53,0x50,0x49,0x52,0x56, - 0x5f,0x43,0x72,0x6f,0x73,0x73,0x5f,0x4f,0x75,0x74,0x70,0x75,0x74,0x20,0x6d,0x61, - 0x69,0x6e,0x28,0x53,0x50,0x49,0x52,0x56,0x5f,0x43,0x72,0x6f,0x73,0x73,0x5f,0x49, - 0x6e,0x70,0x75,0x74,0x20,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x70,0x75,0x74, - 0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76,0x20,0x3d,0x20,0x73,0x74,0x61, - 0x67,0x65,0x5f,0x69,0x6e,0x70,0x75,0x74,0x2e,0x75,0x76,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x66,0x72,0x61,0x67,0x5f,0x6d,0x61,0x69,0x6e,0x28,0x29,0x3b,0x0a,0x20,0x20, - 0x20,0x20,0x53,0x50,0x49,0x52,0x56,0x5f,0x43,0x72,0x6f,0x73,0x73,0x5f,0x4f,0x75, - 0x74,0x70,0x75,0x74,0x20,0x73,0x74,0x61,0x67,0x65,0x5f,0x6f,0x75,0x74,0x70,0x75, - 0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x73,0x74,0x61,0x67,0x65,0x5f,0x6f,0x75,0x74, - 0x70,0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d, - 0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x3b,0x0a,0x20,0x20,0x20, - 0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x73,0x74,0x61,0x67,0x65,0x5f,0x6f,0x75, - 0x74,0x70,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x00, + 0x78,0x74,0x75,0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f, + 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x6d,0x61,0x64,0x28,0x66,0x6c,0x6f,0x61,0x74, + 0x32,0x28,0x2d,0x30,0x2e,0x35,0x66,0x2c,0x20,0x30,0x2e,0x35,0x66,0x29,0x2c,0x20, + 0x5f,0x33,0x32,0x5f,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72, + 0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x75,0x76,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b, + 0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72, + 0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x53,0x61,0x6d,0x70,0x6c,0x65, + 0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72, + 0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x6d,0x61,0x64,0x28,0x66, + 0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x35,0x66,0x2c,0x20,0x2d,0x30,0x2e,0x35, + 0x66,0x29,0x2c,0x20,0x5f,0x33,0x32,0x5f,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75, + 0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x75,0x76,0x29,0x29,0x2e, + 0x78,0x29,0x20,0x2b,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78, + 0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x53,0x61, + 0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78, + 0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x6d, + 0x61,0x64,0x28,0x30,0x2e,0x35,0x66,0x2e,0x78,0x78,0x2c,0x20,0x5f,0x33,0x32,0x5f, + 0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a, + 0x65,0x2c,0x20,0x75,0x76,0x29,0x29,0x2e,0x78,0x29,0x29,0x29,0x3b,0x0a,0x7d,0x0a, + 0x0a,0x53,0x50,0x49,0x52,0x56,0x5f,0x43,0x72,0x6f,0x73,0x73,0x5f,0x4f,0x75,0x74, + 0x70,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x28,0x53,0x50,0x49,0x52,0x56,0x5f,0x43, + 0x72,0x6f,0x73,0x73,0x5f,0x49,0x6e,0x70,0x75,0x74,0x20,0x73,0x74,0x61,0x67,0x65, + 0x5f,0x69,0x6e,0x70,0x75,0x74,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x75,0x76, + 0x20,0x3d,0x20,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e,0x70,0x75,0x74,0x2e,0x75, + 0x76,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x6d,0x61,0x69,0x6e, + 0x28,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x53,0x50,0x49,0x52,0x56,0x5f,0x43,0x72, + 0x6f,0x73,0x73,0x5f,0x4f,0x75,0x74,0x70,0x75,0x74,0x20,0x73,0x74,0x61,0x67,0x65, + 0x5f,0x6f,0x75,0x74,0x70,0x75,0x74,0x3b,0x0a,0x20,0x20,0x20,0x20,0x73,0x74,0x61, + 0x67,0x65,0x5f,0x6f,0x75,0x74,0x70,0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63, + 0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f, + 0x72,0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x73,0x74, + 0x61,0x67,0x65,0x5f,0x6f,0x75,0x74,0x70,0x75,0x74,0x3b,0x0a,0x7d,0x0a,0x00, } /* #include @@ -520,7 +481,7 @@ draw_text_fs_source_hlsl4 := [1257]u8 { */ @(private="file") -draw_text_vs_source_metal_macos := [495]u8 { +ve_draw_text_vs_source_metal_macos := [495]u8 { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, @@ -559,9 +520,10 @@ draw_text_vs_source_metal_macos := [495]u8 { using namespace metal; - struct draw_text_fs_params + struct ve_draw_text_fs_params { - int down_sample; + float2 glyph_buffer_size; + float over_sample; float4 colour; }; @@ -575,93 +537,85 @@ draw_text_vs_source_metal_macos := [495]u8 { float2 uv [[user(locn0)]]; }; - fragment main0_out main0(main0_in in [[stage_in]], constant draw_text_fs_params& _31 [[buffer(0)]], texture2d draw_text_src_texture [[texture(0)]], sampler draw_text_src_sampler [[sampler(0)]]) + fragment main0_out main0(main0_in in [[stage_in]], constant ve_draw_text_fs_params& _32 [[buffer(0)]], texture2d ve_draw_text_src_texture [[texture(0)]], sampler ve_draw_text_src_sampler [[sampler(0)]]) { main0_out out = {}; - float alpha = draw_text_src_texture.sample(draw_text_src_sampler, in.uv).x; - if (_31.down_sample == 1) - { - alpha = 0.25 * (((draw_text_src_texture.sample(draw_text_src_sampler, (in.uv + float2(-0.000244140625, -0.0009765625))).x + draw_text_src_texture.sample(draw_text_src_sampler, (in.uv + float2(-0.000244140625, 0.0009765625))).x) + draw_text_src_texture.sample(draw_text_src_sampler, (in.uv + float2(0.000244140625, -0.0009765625))).x) + draw_text_src_texture.sample(draw_text_src_sampler, (in.uv + float2(0.000244140625, 0.0009765625))).x); - } - out.frag_color = float4(_31.colour.xyz, _31.colour.w * alpha); + out.frag_color = float4(_32.colour.xyz, _32.colour.w * ((1.0 / _32.over_sample) * (((ve_draw_text_src_texture.sample(ve_draw_text_src_sampler, fma(float2(-0.5), _32.glyph_buffer_size, in.uv)).x + ve_draw_text_src_texture.sample(ve_draw_text_src_sampler, fma(float2(-0.5, 0.5), _32.glyph_buffer_size, in.uv)).x) + ve_draw_text_src_texture.sample(ve_draw_text_src_sampler, fma(float2(0.5, -0.5), _32.glyph_buffer_size, in.uv)).x) + ve_draw_text_src_texture.sample(ve_draw_text_src_sampler, fma(float2(0.5), _32.glyph_buffer_size, in.uv)).x))); return out; } */ @(private="file") -draw_text_fs_source_metal_macos := [1141]u8 { +ve_draw_text_fs_source_metal_macos := [1094]u8 { 0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65,0x20,0x3c,0x6d,0x65,0x74,0x61,0x6c,0x5f, 0x73,0x74,0x64,0x6c,0x69,0x62,0x3e,0x0a,0x23,0x69,0x6e,0x63,0x6c,0x75,0x64,0x65, 0x20,0x3c,0x73,0x69,0x6d,0x64,0x2f,0x73,0x69,0x6d,0x64,0x2e,0x68,0x3e,0x0a,0x0a, 0x75,0x73,0x69,0x6e,0x67,0x20,0x6e,0x61,0x6d,0x65,0x73,0x70,0x61,0x63,0x65,0x20, - 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x64, - 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61, - 0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x69,0x6e,0x74,0x20,0x64,0x6f,0x77, - 0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c, - 0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x3b,0x0a,0x7d,0x3b,0x0a, - 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75, - 0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x66, - 0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x63,0x6f,0x6c,0x6f, - 0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75, - 0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b,0x0a,0x20,0x20, - 0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x20,0x5b,0x5b,0x75,0x73, - 0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a, - 0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f, - 0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69,0x6e,0x30,0x5f, - 0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65,0x5f,0x69,0x6e, - 0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20,0x64,0x72,0x61, - 0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, - 0x26,0x20,0x5f,0x33,0x31,0x20,0x5b,0x5b,0x62,0x75,0x66,0x66,0x65,0x72,0x28,0x30, - 0x29,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x32,0x64,0x3c,0x66, - 0x6c,0x6f,0x61,0x74,0x3e,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, - 0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x20,0x5b,0x5b,0x74,0x65, - 0x78,0x74,0x75,0x72,0x65,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61,0x6d,0x70, - 0x6c,0x65,0x72,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72, - 0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x5b,0x5b,0x73,0x61,0x6d,0x70, - 0x6c,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20, - 0x6d,0x61,0x69,0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20, - 0x7b,0x7d,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20,0x61,0x6c, - 0x70,0x68,0x61,0x20,0x3d,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, - 0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d,0x70, - 0x6c,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63, - 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x69,0x6e,0x2e,0x75,0x76,0x29, - 0x2e,0x78,0x3b,0x0a,0x20,0x20,0x20,0x20,0x69,0x66,0x20,0x28,0x5f,0x33,0x31,0x2e, - 0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x3d,0x3d,0x20,0x31, - 0x29,0x0a,0x20,0x20,0x20,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x20,0x20,0x20,0x20, - 0x61,0x6c,0x70,0x68,0x61,0x20,0x3d,0x20,0x30,0x2e,0x32,0x35,0x20,0x2a,0x20,0x28, - 0x28,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f, - 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x64, - 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, - 0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x69,0x6e,0x2e,0x75,0x76,0x20,0x2b,0x20,0x66, - 0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31, - 0x34,0x30,0x36,0x32,0x35,0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36, - 0x35,0x36,0x32,0x35,0x29,0x29,0x29,0x2e,0x78,0x20,0x2b,0x20,0x64,0x72,0x61,0x77, + 0x6d,0x65,0x74,0x61,0x6c,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x76, + 0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70, + 0x61,0x72,0x61,0x6d,0x73,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61, + 0x74,0x32,0x20,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f, + 0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x20, + 0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a,0x20,0x20,0x20, + 0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x20,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x3b,0x0a, + 0x7d,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30, + 0x5f,0x6f,0x75,0x74,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74, + 0x34,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x5b,0x5b,0x63, + 0x6f,0x6c,0x6f,0x72,0x28,0x30,0x29,0x5d,0x5d,0x3b,0x0a,0x7d,0x3b,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x5f,0x69,0x6e,0x0a,0x7b, + 0x0a,0x20,0x20,0x20,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x20,0x75,0x76,0x20,0x5b, + 0x5b,0x75,0x73,0x65,0x72,0x28,0x6c,0x6f,0x63,0x6e,0x30,0x29,0x5d,0x5d,0x3b,0x0a, + 0x7d,0x3b,0x0a,0x0a,0x66,0x72,0x61,0x67,0x6d,0x65,0x6e,0x74,0x20,0x6d,0x61,0x69, + 0x6e,0x30,0x5f,0x6f,0x75,0x74,0x20,0x6d,0x61,0x69,0x6e,0x30,0x28,0x6d,0x61,0x69, + 0x6e,0x30,0x5f,0x69,0x6e,0x20,0x69,0x6e,0x20,0x5b,0x5b,0x73,0x74,0x61,0x67,0x65, + 0x5f,0x69,0x6e,0x5d,0x5d,0x2c,0x20,0x63,0x6f,0x6e,0x73,0x74,0x61,0x6e,0x74,0x20, + 0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x26,0x20,0x5f,0x33,0x32,0x20,0x5b,0x5b,0x62,0x75, + 0x66,0x66,0x65,0x72,0x28,0x30,0x29,0x5d,0x5d,0x2c,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x32,0x64,0x3c,0x66,0x6c,0x6f,0x61,0x74,0x3e,0x20,0x76,0x65,0x5f,0x64, + 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x20,0x5b,0x5b,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x28,0x30, + 0x29,0x5d,0x5d,0x2c,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x76,0x65,0x5f, + 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x20,0x5b,0x5b,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x28, + 0x30,0x29,0x5d,0x5d,0x29,0x0a,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6d,0x61,0x69,0x6e, + 0x30,0x5f,0x6f,0x75,0x74,0x20,0x6f,0x75,0x74,0x20,0x3d,0x20,0x7b,0x7d,0x3b,0x0a, + 0x20,0x20,0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c, + 0x6f,0x72,0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x5f,0x33,0x32,0x2e, + 0x63,0x6f,0x6c,0x6f,0x75,0x72,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x5f,0x33,0x32,0x2e, + 0x63,0x6f,0x6c,0x6f,0x75,0x72,0x2e,0x77,0x20,0x2a,0x20,0x28,0x28,0x31,0x2e,0x30, + 0x20,0x2f,0x20,0x5f,0x33,0x32,0x2e,0x6f,0x76,0x65,0x72,0x5f,0x73,0x61,0x6d,0x70, + 0x6c,0x65,0x29,0x20,0x2a,0x20,0x28,0x28,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77, 0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72, - 0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, - 0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20, - 0x28,0x69,0x6e,0x2e,0x75,0x76,0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28, - 0x2d,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x2c, - 0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x29,0x29, - 0x2e,0x78,0x29,0x20,0x2b,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, - 0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d,0x70, - 0x6c,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63, - 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x69,0x6e,0x2e,0x75,0x76, - 0x20,0x2b,0x20,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x32, - 0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30,0x30, - 0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x29,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20, + 0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77, + 0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, + 0x72,0x2c,0x20,0x66,0x6d,0x61,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x30, + 0x2e,0x35,0x29,0x2c,0x20,0x5f,0x33,0x32,0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62, + 0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20,0x69,0x6e,0x2e,0x75, + 0x76,0x29,0x29,0x2e,0x78,0x20,0x2b,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f, + 0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f, + 0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, + 0x2c,0x20,0x66,0x6d,0x61,0x28,0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x2d,0x30,0x2e, + 0x35,0x2c,0x20,0x30,0x2e,0x35,0x29,0x2c,0x20,0x5f,0x33,0x32,0x2e,0x67,0x6c,0x79, + 0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x2c,0x20, + 0x69,0x6e,0x2e,0x75,0x76,0x29,0x29,0x2e,0x78,0x29,0x20,0x2b,0x20,0x76,0x65,0x5f, 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65, - 0x78,0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x64,0x72,0x61, - 0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c, - 0x65,0x72,0x2c,0x20,0x28,0x69,0x6e,0x2e,0x75,0x76,0x20,0x2b,0x20,0x66,0x6c,0x6f, - 0x61,0x74,0x32,0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36, - 0x32,0x35,0x2c,0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35, - 0x29,0x29,0x29,0x2e,0x78,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x7d,0x0a,0x20,0x20, - 0x20,0x20,0x6f,0x75,0x74,0x2e,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72, - 0x20,0x3d,0x20,0x66,0x6c,0x6f,0x61,0x74,0x34,0x28,0x5f,0x33,0x31,0x2e,0x63,0x6f, - 0x6c,0x6f,0x75,0x72,0x2e,0x78,0x79,0x7a,0x2c,0x20,0x5f,0x33,0x31,0x2e,0x63,0x6f, - 0x6c,0x6f,0x75,0x72,0x2e,0x77,0x20,0x2a,0x20,0x61,0x6c,0x70,0x68,0x61,0x29,0x3b, - 0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74,0x3b, - 0x0a,0x7d,0x0a,0x0a,0x00, + 0x78,0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f, + 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x66,0x6d,0x61,0x28,0x66,0x6c,0x6f,0x61,0x74, + 0x32,0x28,0x30,0x2e,0x35,0x2c,0x20,0x2d,0x30,0x2e,0x35,0x29,0x2c,0x20,0x5f,0x33, + 0x32,0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73, + 0x69,0x7a,0x65,0x2c,0x20,0x69,0x6e,0x2e,0x75,0x76,0x29,0x29,0x2e,0x78,0x29,0x20, + 0x2b,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73, + 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2e,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73, + 0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x66,0x6d,0x61,0x28, + 0x66,0x6c,0x6f,0x61,0x74,0x32,0x28,0x30,0x2e,0x35,0x29,0x2c,0x20,0x5f,0x33,0x32, + 0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69, + 0x7a,0x65,0x2c,0x20,0x69,0x6e,0x2e,0x75,0x76,0x29,0x29,0x2e,0x78,0x29,0x29,0x29, + 0x3b,0x0a,0x20,0x20,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20,0x6f,0x75,0x74, + 0x3b,0x0a,0x7d,0x0a,0x0a,0x00, } /* diagnostic(off, derivative_uniformity); @@ -701,7 +655,7 @@ draw_text_fs_source_metal_macos := [1141]u8 { */ @(private="file") -draw_text_vs_source_wgsl := [756]u8 { +ve_draw_text_vs_source_wgsl := [756]u8 { 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, @@ -754,45 +708,58 @@ draw_text_vs_source_wgsl := [756]u8 { /* diagnostic(off, derivative_uniformity); - struct draw_text_fs_params { + struct ve_draw_text_fs_params { /_ @offset(0) _/ - down_sample : i32, + glyph_buffer_size : vec2f, + /_ @offset(8) _/ + over_sample : f32, /_ @offset(16) _/ colour : vec4f, } - @group(1) @binding(64) var draw_text_src_texture : texture_2d; + @group(1) @binding(64) var ve_draw_text_src_texture : texture_2d; - @group(1) @binding(80) var draw_text_src_sampler : sampler; + @group(1) @binding(80) var ve_draw_text_src_sampler : sampler; var uv : vec2f; - @group(0) @binding(8) var x_31 : draw_text_fs_params; + @group(0) @binding(8) var x_32 : ve_draw_text_fs_params; var frag_color : vec4f; fn main_1() { var alpha : f32; + var texture_size : vec2f; + var down_sample : f32; let x_22 : vec2f = uv; - let x_24 : vec4f = textureSample(draw_text_src_texture, draw_text_src_sampler, x_22); + let x_24 : vec4f = textureSample(ve_draw_text_src_texture, ve_draw_text_src_sampler, x_22); alpha = x_24.x; - let x_35 : i32 = x_31.down_sample; - if ((x_35 == 1i)) { - let x_44 : vec2f = uv; - let x_49 : vec4f = textureSample(draw_text_src_texture, draw_text_src_sampler, (x_44 + vec2f(-0.000244140625f, -0.0009765625f))); - let x_56 : vec2f = uv; - let x_60 : vec4f = textureSample(draw_text_src_texture, draw_text_src_sampler, (x_56 + vec2f(-0.000244140625f, 0.0009765625f))); - let x_67 : vec2f = uv; - let x_71 : vec4f = textureSample(draw_text_src_texture, draw_text_src_sampler, (x_67 + vec2f(0.000244140625f, -0.0009765625f))); - let x_78 : vec2f = uv; - let x_81 : vec4f = textureSample(draw_text_src_texture, draw_text_src_sampler, (x_78 + vec2f(0.000244140625f, 0.0009765625f))); - alpha = ((((x_49.x * 0.25f) + (x_60.x * 0.25f)) + (x_71.x * 0.25f)) + (x_81.x * 0.25f)); - } - let x_90 : vec4f = x_31.colour; - let x_91 : vec3f = vec3f(x_90.x, x_90.y, x_90.z); - let x_95 : f32 = x_31.colour.w; - let x_96 : f32 = alpha; - frag_color = vec4f(x_91.x, x_91.y, x_91.z, (x_95 * x_96)); + let x_37 : vec2f = x_32.glyph_buffer_size; + texture_size = x_37; + let x_43 : f32 = x_32.over_sample; + down_sample = (1.0f / x_43); + let x_48 : vec2f = uv; + let x_51 : vec2f = texture_size; + let x_54 : vec4f = textureSample(ve_draw_text_src_texture, ve_draw_text_src_sampler, (x_48 + (vec2f(-0.5f, -0.5f) * x_51))); + let x_56 : f32 = down_sample; + let x_61 : vec2f = uv; + let x_64 : vec2f = texture_size; + let x_67 : vec4f = textureSample(ve_draw_text_src_texture, ve_draw_text_src_sampler, (x_61 + (vec2f(-0.5f, 0.5f) * x_64))); + let x_69 : f32 = down_sample; + let x_75 : vec2f = uv; + let x_77 : vec2f = texture_size; + let x_80 : vec4f = textureSample(ve_draw_text_src_texture, ve_draw_text_src_sampler, (x_75 + (vec2f(0.5f, -0.5f) * x_77))); + let x_82 : f32 = down_sample; + let x_88 : vec2f = uv; + let x_90 : vec2f = texture_size; + let x_93 : vec4f = textureSample(ve_draw_text_src_texture, ve_draw_text_src_sampler, (x_88 + (vec2f(0.5f, 0.5f) * x_90))); + let x_95 : f32 = down_sample; + alpha = ((((x_54.x * x_56) + (x_67.x * x_69)) + (x_80.x * x_82)) + (x_93.x * x_95)); + let x_104 : vec4f = x_32.colour; + let x_105 : vec3f = vec3f(x_104.x, x_104.y, x_104.z); + let x_108 : f32 = x_32.colour.w; + let x_109 : f32 = alpha; + frag_color = vec4f(x_105.x, x_105.y, x_105.z, (x_108 * x_109)); return; } @@ -810,139 +777,163 @@ draw_text_vs_source_wgsl := [756]u8 { */ @(private="file") -draw_text_fs_source_wgsl := [1772]u8 { +ve_draw_text_fs_source_wgsl := [2202]u8 { 0x64,0x69,0x61,0x67,0x6e,0x6f,0x73,0x74,0x69,0x63,0x28,0x6f,0x66,0x66,0x2c,0x20, 0x64,0x65,0x72,0x69,0x76,0x61,0x74,0x69,0x76,0x65,0x5f,0x75,0x6e,0x69,0x66,0x6f, 0x72,0x6d,0x69,0x74,0x79,0x29,0x3b,0x0a,0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20, - 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72, - 0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66,0x73, - 0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x64,0x6f,0x77,0x6e,0x5f, - 0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x3a,0x20,0x69,0x33,0x32,0x2c,0x0a,0x20,0x20, - 0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x31,0x36,0x29,0x20,0x2a, - 0x2f,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x20,0x3a,0x20,0x76,0x65,0x63, - 0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29, - 0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x36,0x34,0x29,0x20,0x76,0x61, - 0x72,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f, - 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x20,0x3a,0x20,0x74,0x65,0x78,0x74,0x75,0x72, - 0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f, - 0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x38, - 0x30,0x29,0x20,0x76,0x61,0x72,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74, - 0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x20,0x3a,0x20,0x73, - 0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69, - 0x76,0x61,0x74,0x65,0x3e,0x20,0x75,0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66, - 0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x30,0x29,0x20,0x40,0x62,0x69, - 0x6e,0x64,0x69,0x6e,0x67,0x28,0x38,0x29,0x20,0x76,0x61,0x72,0x3c,0x75,0x6e,0x69, - 0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78,0x5f,0x33,0x31,0x20,0x3a,0x20,0x64,0x72,0x61, - 0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73, - 0x3b,0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20, - 0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63, - 0x34,0x66,0x3b,0x0a,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29, - 0x20,0x7b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3a, - 0x20,0x66,0x33,0x32,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x32, - 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20, - 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34, + 0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x66,0x73,0x5f, + 0x70,0x61,0x72,0x61,0x6d,0x73,0x20,0x7b,0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f, + 0x66,0x66,0x73,0x65,0x74,0x28,0x30,0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x67,0x6c, + 0x79,0x70,0x68,0x5f,0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x20, + 0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x2c,0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f, + 0x66,0x66,0x73,0x65,0x74,0x28,0x38,0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x6f,0x76, + 0x65,0x72,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x3a,0x20,0x66,0x33,0x32,0x2c, + 0x0a,0x20,0x20,0x2f,0x2a,0x20,0x40,0x6f,0x66,0x66,0x73,0x65,0x74,0x28,0x31,0x36, + 0x29,0x20,0x2a,0x2f,0x0a,0x20,0x20,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70, + 0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x36,0x34,0x29, + 0x20,0x76,0x61,0x72,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78, + 0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x20,0x3a,0x20, + 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x32,0x64,0x3c,0x66,0x33,0x32,0x3e,0x3b, + 0x0a,0x0a,0x40,0x67,0x72,0x6f,0x75,0x70,0x28,0x31,0x29,0x20,0x40,0x62,0x69,0x6e, + 0x64,0x69,0x6e,0x67,0x28,0x38,0x30,0x29,0x20,0x76,0x61,0x72,0x20,0x76,0x65,0x5f, + 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x20,0x3a,0x20,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x3b, + 0x0a,0x0a,0x76,0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x75, + 0x76,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x0a,0x40,0x67,0x72,0x6f, + 0x75,0x70,0x28,0x30,0x29,0x20,0x40,0x62,0x69,0x6e,0x64,0x69,0x6e,0x67,0x28,0x38, + 0x29,0x20,0x76,0x61,0x72,0x3c,0x75,0x6e,0x69,0x66,0x6f,0x72,0x6d,0x3e,0x20,0x78, + 0x5f,0x33,0x32,0x20,0x3a,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, + 0x78,0x74,0x5f,0x66,0x73,0x5f,0x70,0x61,0x72,0x61,0x6d,0x73,0x3b,0x0a,0x0a,0x76, + 0x61,0x72,0x3c,0x70,0x72,0x69,0x76,0x61,0x74,0x65,0x3e,0x20,0x66,0x72,0x61,0x67, + 0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x3b,0x0a, + 0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x20,0x7b,0x0a,0x20, + 0x20,0x76,0x61,0x72,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3a,0x20,0x66,0x33,0x32, + 0x3b,0x0a,0x20,0x20,0x76,0x61,0x72,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f, + 0x73,0x69,0x7a,0x65,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x3b,0x0a,0x20,0x20, + 0x76,0x61,0x72,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x20, + 0x3a,0x20,0x66,0x33,0x32,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32, + 0x32,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x32,0x34,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x53,0x61,0x6d,0x70, + 0x6c,0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f, + 0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x76,0x65,0x5f, + 0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61, + 0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x78,0x5f,0x32,0x32,0x29,0x3b,0x0a,0x20,0x20, + 0x61,0x6c,0x70,0x68,0x61,0x20,0x3d,0x20,0x78,0x5f,0x32,0x34,0x2e,0x78,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x33,0x37,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x78,0x5f,0x33,0x32,0x2e,0x67,0x6c,0x79,0x70,0x68,0x5f, + 0x62,0x75,0x66,0x66,0x65,0x72,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x74, + 0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x20,0x3d,0x20,0x78,0x5f, + 0x33,0x37,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x33,0x20,0x3a, + 0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f,0x33,0x32,0x2e,0x6f,0x76,0x65,0x72, + 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a,0x20,0x20,0x64,0x6f,0x77,0x6e,0x5f, + 0x73,0x61,0x6d,0x70,0x6c,0x65,0x20,0x3d,0x20,0x28,0x31,0x2e,0x30,0x66,0x20,0x2f, + 0x20,0x78,0x5f,0x34,0x33,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f, + 0x34,0x38,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x3b, + 0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x31,0x20,0x3a,0x20,0x76,0x65, + 0x63,0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69, + 0x7a,0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x34,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65, + 0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74, + 0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c, + 0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72, + 0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x78,0x5f,0x34,0x38, + 0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x2d,0x30,0x2e,0x35,0x66,0x2c, + 0x20,0x2d,0x30,0x2e,0x35,0x66,0x29,0x20,0x2a,0x20,0x78,0x5f,0x35,0x31,0x29,0x29, + 0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35,0x36,0x20,0x3a,0x20, + 0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c, + 0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x36,0x31,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65, + 0x74,0x20,0x78,0x5f,0x36,0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d, + 0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20, + 0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x36,0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x34, 0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x53,0x61,0x6d,0x70,0x6c, - 0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f, - 0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, + 0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73, + 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x76,0x65,0x5f,0x64, + 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d, + 0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x78,0x5f,0x36,0x31,0x20,0x2b,0x20,0x28,0x76, + 0x65,0x63,0x32,0x66,0x28,0x2d,0x30,0x2e,0x35,0x66,0x2c,0x20,0x30,0x2e,0x35,0x66, + 0x29,0x20,0x2a,0x20,0x78,0x5f,0x36,0x34,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x36,0x39,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20, + 0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a,0x20,0x20,0x6c, + 0x65,0x74,0x20,0x78,0x5f,0x37,0x35,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20, + 0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x37,0x37, + 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75, + 0x72,0x65,0x5f,0x73,0x69,0x7a,0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78, + 0x5f,0x38,0x30,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65, + 0x78,0x74,0x75,0x72,0x65,0x53,0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x64, + 0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78, + 0x74,0x75,0x72,0x65,0x2c,0x20,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, 0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20, - 0x78,0x5f,0x32,0x32,0x29,0x3b,0x0a,0x20,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3d, - 0x20,0x78,0x5f,0x32,0x34,0x2e,0x78,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78, - 0x5f,0x33,0x35,0x20,0x3a,0x20,0x69,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f,0x33,0x31, - 0x2e,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a,0x20,0x20, - 0x69,0x66,0x20,0x28,0x28,0x78,0x5f,0x33,0x35,0x20,0x3d,0x3d,0x20,0x31,0x69,0x29, - 0x29,0x20,0x7b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x34, - 0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a,0x20, - 0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x34,0x39,0x20,0x3a,0x20,0x76,0x65, - 0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x53,0x61,0x6d, - 0x70,0x6c,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72, - 0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x64,0x72,0x61,0x77,0x5f, - 0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72, - 0x2c,0x20,0x28,0x78,0x5f,0x34,0x34,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x66,0x28, - 0x2d,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x66, - 0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66, - 0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x35, - 0x36,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x36,0x30,0x20,0x3a,0x20,0x76, - 0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x53,0x61, - 0x6d,0x70,0x6c,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73, - 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x64,0x72,0x61,0x77, - 0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x72,0x2c,0x20,0x28,0x78,0x5f,0x35,0x36,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x66, - 0x28,0x2d,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35, - 0x66,0x2c,0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66, - 0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x36, - 0x37,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x37,0x31,0x20,0x3a,0x20,0x76, - 0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x53,0x61, - 0x6d,0x70,0x6c,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73, - 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x64,0x72,0x61,0x77, - 0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x72,0x2c,0x20,0x28,0x78,0x5f,0x36,0x37,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x66, - 0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x66, - 0x2c,0x20,0x2d,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66, - 0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x37, + 0x28,0x78,0x5f,0x37,0x35,0x20,0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x30, + 0x2e,0x35,0x66,0x2c,0x20,0x2d,0x30,0x2e,0x35,0x66,0x29,0x20,0x2a,0x20,0x78,0x5f, + 0x37,0x37,0x29,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x38, + 0x32,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73, + 0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x38, 0x38,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x20,0x3d,0x20,0x75,0x76,0x3b,0x0a, - 0x20,0x20,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x38,0x31,0x20,0x3a,0x20,0x76, - 0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x53,0x61, - 0x6d,0x70,0x6c,0x65,0x28,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73, - 0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20,0x64,0x72,0x61,0x77, - 0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65, - 0x72,0x2c,0x20,0x28,0x78,0x5f,0x37,0x38,0x20,0x2b,0x20,0x76,0x65,0x63,0x32,0x66, - 0x28,0x30,0x2e,0x30,0x30,0x30,0x32,0x34,0x34,0x31,0x34,0x30,0x36,0x32,0x35,0x66, - 0x2c,0x20,0x30,0x2e,0x30,0x30,0x30,0x39,0x37,0x36,0x35,0x36,0x32,0x35,0x66,0x29, - 0x29,0x29,0x3b,0x0a,0x20,0x20,0x20,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3d,0x20, - 0x28,0x28,0x28,0x28,0x78,0x5f,0x34,0x39,0x2e,0x78,0x20,0x2a,0x20,0x30,0x2e,0x32, - 0x35,0x66,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x36,0x30,0x2e,0x78,0x20,0x2a,0x20, - 0x30,0x2e,0x32,0x35,0x66,0x29,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x37,0x31,0x2e, - 0x78,0x20,0x2a,0x20,0x30,0x2e,0x32,0x35,0x66,0x29,0x29,0x20,0x2b,0x20,0x28,0x78, - 0x5f,0x38,0x31,0x2e,0x78,0x20,0x2a,0x20,0x30,0x2e,0x32,0x35,0x66,0x29,0x29,0x3b, - 0x0a,0x20,0x20,0x7d,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x39,0x30,0x20, - 0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x33,0x31,0x2e,0x63, - 0x6f,0x6c,0x6f,0x75,0x72,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x39, - 0x31,0x20,0x3a,0x20,0x76,0x65,0x63,0x33,0x66,0x20,0x3d,0x20,0x76,0x65,0x63,0x33, - 0x66,0x28,0x78,0x5f,0x39,0x30,0x2e,0x78,0x2c,0x20,0x78,0x5f,0x39,0x30,0x2e,0x79, - 0x2c,0x20,0x78,0x5f,0x39,0x30,0x2e,0x7a,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74, - 0x20,0x78,0x5f,0x39,0x35,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f, - 0x33,0x31,0x2e,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x2e,0x77,0x3b,0x0a,0x20,0x20,0x6c, - 0x65,0x74,0x20,0x78,0x5f,0x39,0x36,0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20, - 0x61,0x6c,0x70,0x68,0x61,0x3b,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, - 0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76,0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x39,0x31, - 0x2e,0x78,0x2c,0x20,0x78,0x5f,0x39,0x31,0x2e,0x79,0x2c,0x20,0x78,0x5f,0x39,0x31, - 0x2e,0x7a,0x2c,0x20,0x28,0x78,0x5f,0x39,0x35,0x20,0x2a,0x20,0x78,0x5f,0x39,0x36, - 0x29,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a, - 0x0a,0x73,0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74, - 0x20,0x7b,0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30, - 0x29,0x0a,0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31, - 0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x66,0x72, - 0x61,0x67,0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40, - 0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76,0x5f,0x70, - 0x61,0x72,0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x29,0x20,0x2d,0x3e, - 0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x75,0x76, - 0x20,0x3d,0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d, - 0x61,0x69,0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72, - 0x6e,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f, - 0x63,0x6f,0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x39,0x30,0x20,0x3a,0x20,0x76,0x65,0x63, + 0x32,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x5f,0x73,0x69,0x7a, + 0x65,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x39,0x33,0x20,0x3a,0x20, + 0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x53, + 0x61,0x6d,0x70,0x6c,0x65,0x28,0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65, + 0x78,0x74,0x5f,0x73,0x72,0x63,0x5f,0x74,0x65,0x78,0x74,0x75,0x72,0x65,0x2c,0x20, + 0x76,0x65,0x5f,0x64,0x72,0x61,0x77,0x5f,0x74,0x65,0x78,0x74,0x5f,0x73,0x72,0x63, + 0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x72,0x2c,0x20,0x28,0x78,0x5f,0x38,0x38,0x20, + 0x2b,0x20,0x28,0x76,0x65,0x63,0x32,0x66,0x28,0x30,0x2e,0x35,0x66,0x2c,0x20,0x30, + 0x2e,0x35,0x66,0x29,0x20,0x2a,0x20,0x78,0x5f,0x39,0x30,0x29,0x29,0x29,0x3b,0x0a, + 0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x39,0x35,0x20,0x3a,0x20,0x66,0x33,0x32, + 0x20,0x3d,0x20,0x64,0x6f,0x77,0x6e,0x5f,0x73,0x61,0x6d,0x70,0x6c,0x65,0x3b,0x0a, + 0x20,0x20,0x61,0x6c,0x70,0x68,0x61,0x20,0x3d,0x20,0x28,0x28,0x28,0x28,0x78,0x5f, + 0x35,0x34,0x2e,0x78,0x20,0x2a,0x20,0x78,0x5f,0x35,0x36,0x29,0x20,0x2b,0x20,0x28, + 0x78,0x5f,0x36,0x37,0x2e,0x78,0x20,0x2a,0x20,0x78,0x5f,0x36,0x39,0x29,0x29,0x20, + 0x2b,0x20,0x28,0x78,0x5f,0x38,0x30,0x2e,0x78,0x20,0x2a,0x20,0x78,0x5f,0x38,0x32, + 0x29,0x29,0x20,0x2b,0x20,0x28,0x78,0x5f,0x39,0x33,0x2e,0x78,0x20,0x2a,0x20,0x78, + 0x5f,0x39,0x35,0x29,0x29,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31, + 0x30,0x34,0x20,0x3a,0x20,0x76,0x65,0x63,0x34,0x66,0x20,0x3d,0x20,0x78,0x5f,0x33, + 0x32,0x2e,0x63,0x6f,0x6c,0x6f,0x75,0x72,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20, + 0x78,0x5f,0x31,0x30,0x35,0x20,0x3a,0x20,0x76,0x65,0x63,0x33,0x66,0x20,0x3d,0x20, + 0x76,0x65,0x63,0x33,0x66,0x28,0x78,0x5f,0x31,0x30,0x34,0x2e,0x78,0x2c,0x20,0x78, + 0x5f,0x31,0x30,0x34,0x2e,0x79,0x2c,0x20,0x78,0x5f,0x31,0x30,0x34,0x2e,0x7a,0x29, + 0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x30,0x38,0x20,0x3a,0x20, + 0x66,0x33,0x32,0x20,0x3d,0x20,0x78,0x5f,0x33,0x32,0x2e,0x63,0x6f,0x6c,0x6f,0x75, + 0x72,0x2e,0x77,0x3b,0x0a,0x20,0x20,0x6c,0x65,0x74,0x20,0x78,0x5f,0x31,0x30,0x39, + 0x20,0x3a,0x20,0x66,0x33,0x32,0x20,0x3d,0x20,0x61,0x6c,0x70,0x68,0x61,0x3b,0x0a, + 0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x20,0x3d,0x20,0x76, + 0x65,0x63,0x34,0x66,0x28,0x78,0x5f,0x31,0x30,0x35,0x2e,0x78,0x2c,0x20,0x78,0x5f, + 0x31,0x30,0x35,0x2e,0x79,0x2c,0x20,0x78,0x5f,0x31,0x30,0x35,0x2e,0x7a,0x2c,0x20, + 0x28,0x78,0x5f,0x31,0x30,0x38,0x20,0x2a,0x20,0x78,0x5f,0x31,0x30,0x39,0x29,0x29, + 0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x3b,0x0a,0x7d,0x0a,0x0a,0x73, + 0x74,0x72,0x75,0x63,0x74,0x20,0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b, + 0x0a,0x20,0x20,0x40,0x6c,0x6f,0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x0a, + 0x20,0x20,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f,0x6c,0x6f,0x72,0x5f,0x31,0x20,0x3a, + 0x20,0x76,0x65,0x63,0x34,0x66,0x2c,0x0a,0x7d,0x0a,0x0a,0x40,0x66,0x72,0x61,0x67, + 0x6d,0x65,0x6e,0x74,0x0a,0x66,0x6e,0x20,0x6d,0x61,0x69,0x6e,0x28,0x40,0x6c,0x6f, + 0x63,0x61,0x74,0x69,0x6f,0x6e,0x28,0x30,0x29,0x20,0x75,0x76,0x5f,0x70,0x61,0x72, + 0x61,0x6d,0x20,0x3a,0x20,0x76,0x65,0x63,0x32,0x66,0x29,0x20,0x2d,0x3e,0x20,0x6d, + 0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x20,0x7b,0x0a,0x20,0x20,0x75,0x76,0x20,0x3d, + 0x20,0x75,0x76,0x5f,0x70,0x61,0x72,0x61,0x6d,0x3b,0x0a,0x20,0x20,0x6d,0x61,0x69, + 0x6e,0x5f,0x31,0x28,0x29,0x3b,0x0a,0x20,0x20,0x72,0x65,0x74,0x75,0x72,0x6e,0x20, + 0x6d,0x61,0x69,0x6e,0x5f,0x6f,0x75,0x74,0x28,0x66,0x72,0x61,0x67,0x5f,0x63,0x6f, + 0x6c,0x6f,0x72,0x29,0x3b,0x0a,0x7d,0x0a,0x0a,0x00, } -draw_text_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { +ve_draw_text_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { desc: sg.Shader_Desc - desc.label = "draw_text_shader" + desc.label = "ve_draw_text_shader" #partial switch backend { case .GLCORE: - desc.vertex_func.source = transmute(cstring)&draw_text_vs_source_glsl410 + desc.vertex_func.source = transmute(cstring)&ve_draw_text_vs_source_glsl410 desc.vertex_func.entry = "main" - desc.fragment_func.source = transmute(cstring)&draw_text_fs_source_glsl410 + desc.fragment_func.source = transmute(cstring)&ve_draw_text_fs_source_glsl410 desc.fragment_func.entry = "main" desc.attrs[0].glsl_name = "v_position" desc.attrs[1].glsl_name = "v_texture" desc.uniform_blocks[0].stage = .FRAGMENT desc.uniform_blocks[0].layout = .STD140 desc.uniform_blocks[0].size = 32 - desc.uniform_blocks[0].glsl_uniforms[0].type = .INT - desc.uniform_blocks[0].glsl_uniforms[0].array_count = 0 - desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "_31.down_sample" - desc.uniform_blocks[0].glsl_uniforms[1].type = .FLOAT4 - desc.uniform_blocks[0].glsl_uniforms[1].array_count = 0 - desc.uniform_blocks[0].glsl_uniforms[1].glsl_name = "_31.colour" + desc.uniform_blocks[0].glsl_uniforms[0].type = .FLOAT4 + desc.uniform_blocks[0].glsl_uniforms[0].array_count = 2 + desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "ve_draw_text_fs_params" desc.images[0].stage = .FRAGMENT desc.images[0].multisampled = false desc.images[0].image_type = ._2D @@ -952,23 +943,20 @@ draw_text_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { desc.image_sampler_pairs[0].stage = .FRAGMENT desc.image_sampler_pairs[0].image_slot = 0 desc.image_sampler_pairs[0].sampler_slot = 0 - desc.image_sampler_pairs[0].glsl_name = "draw_text_src_texture_draw_text_src_sampler" + desc.image_sampler_pairs[0].glsl_name = "ve_draw_text_src_texture_ve_draw_text_src_sampler" case .GLES3: - desc.vertex_func.source = transmute(cstring)&draw_text_vs_source_glsl300es + desc.vertex_func.source = transmute(cstring)&ve_draw_text_vs_source_glsl300es desc.vertex_func.entry = "main" - desc.fragment_func.source = transmute(cstring)&draw_text_fs_source_glsl300es + desc.fragment_func.source = transmute(cstring)&ve_draw_text_fs_source_glsl300es desc.fragment_func.entry = "main" desc.attrs[0].glsl_name = "v_position" desc.attrs[1].glsl_name = "v_texture" desc.uniform_blocks[0].stage = .FRAGMENT desc.uniform_blocks[0].layout = .STD140 desc.uniform_blocks[0].size = 32 - desc.uniform_blocks[0].glsl_uniforms[0].type = .INT - desc.uniform_blocks[0].glsl_uniforms[0].array_count = 0 - desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "_31.down_sample" - desc.uniform_blocks[0].glsl_uniforms[1].type = .FLOAT4 - desc.uniform_blocks[0].glsl_uniforms[1].array_count = 0 - desc.uniform_blocks[0].glsl_uniforms[1].glsl_name = "_31.colour" + desc.uniform_blocks[0].glsl_uniforms[0].type = .FLOAT4 + desc.uniform_blocks[0].glsl_uniforms[0].array_count = 2 + desc.uniform_blocks[0].glsl_uniforms[0].glsl_name = "ve_draw_text_fs_params" desc.images[0].stage = .FRAGMENT desc.images[0].multisampled = false desc.images[0].image_type = ._2D @@ -978,12 +966,12 @@ draw_text_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { desc.image_sampler_pairs[0].stage = .FRAGMENT desc.image_sampler_pairs[0].image_slot = 0 desc.image_sampler_pairs[0].sampler_slot = 0 - desc.image_sampler_pairs[0].glsl_name = "draw_text_src_texture_draw_text_src_sampler" + desc.image_sampler_pairs[0].glsl_name = "ve_draw_text_src_texture_ve_draw_text_src_sampler" case .D3D11: - desc.vertex_func.source = transmute(cstring)&draw_text_vs_source_hlsl4 + desc.vertex_func.source = transmute(cstring)&ve_draw_text_vs_source_hlsl4 desc.vertex_func.d3d11_target = "vs_4_0" desc.vertex_func.entry = "main" - desc.fragment_func.source = transmute(cstring)&draw_text_fs_source_hlsl4 + desc.fragment_func.source = transmute(cstring)&ve_draw_text_fs_source_hlsl4 desc.fragment_func.d3d11_target = "ps_4_0" desc.fragment_func.entry = "main" desc.attrs[0].hlsl_sem_name = "TEXCOORD" @@ -1006,9 +994,9 @@ draw_text_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { desc.image_sampler_pairs[0].image_slot = 0 desc.image_sampler_pairs[0].sampler_slot = 0 case .METAL_MACOS: - desc.vertex_func.source = transmute(cstring)&draw_text_vs_source_metal_macos + desc.vertex_func.source = transmute(cstring)&ve_draw_text_vs_source_metal_macos desc.vertex_func.entry = "main0" - desc.fragment_func.source = transmute(cstring)&draw_text_fs_source_metal_macos + desc.fragment_func.source = transmute(cstring)&ve_draw_text_fs_source_metal_macos desc.fragment_func.entry = "main0" desc.uniform_blocks[0].stage = .FRAGMENT desc.uniform_blocks[0].layout = .STD140 @@ -1026,9 +1014,9 @@ draw_text_shader_desc :: proc (backend: sg.Backend) -> sg.Shader_Desc { desc.image_sampler_pairs[0].image_slot = 0 desc.image_sampler_pairs[0].sampler_slot = 0 case .WGPU: - desc.vertex_func.source = transmute(cstring)&draw_text_vs_source_wgsl + desc.vertex_func.source = transmute(cstring)&ve_draw_text_vs_source_wgsl desc.vertex_func.entry = "main" - desc.fragment_func.source = transmute(cstring)&draw_text_fs_source_wgsl + desc.fragment_func.source = transmute(cstring)&ve_draw_text_fs_source_wgsl desc.fragment_func.entry = "main" desc.uniform_blocks[0].stage = .FRAGMENT desc.uniform_blocks[0].layout = .STD140 diff --git a/backend/sokol/draw_text.shdc.glsl b/backend/sokol/draw_text.shdc.glsl index fa73f4f..a3278c7 100644 --- a/backend/sokol/draw_text.shdc.glsl +++ b/backend/sokol/draw_text.shdc.glsl @@ -1,9 +1,9 @@ -@module draw_text +@module ve_draw_text @header package ve_sokol @header import sg "thirdparty:sokol/gfx" -@vs draw_text_vs +@vs ve_draw_text_vs in vec2 v_position; in vec2 v_texture; out vec2 uv; @@ -19,33 +19,33 @@ void main() } @end -@fs draw_text_fs +@fs ve_draw_text_fs in vec2 uv; out vec4 frag_color; -layout(binding = 0) uniform texture2D draw_text_src_texture; -layout(binding = 0) uniform sampler draw_text_src_sampler; +layout(binding = 0) uniform texture2D ve_draw_text_src_texture; +layout(binding = 0) uniform sampler ve_draw_text_src_sampler; -layout(binding = 0) uniform draw_text_fs_params { - int down_sample; - vec4 colour; +layout(binding = 0) uniform ve_draw_text_fs_params { + vec2 glyph_buffer_size; + float over_sample; + vec4 colour; }; void main() { - float alpha = texture(sampler2D( draw_text_src_texture, draw_text_src_sampler ), uv ).x; - if ( down_sample == 1 ) - { - // TODO(Ed): The original author made these consts, I want to instead expose as uniforms... - const vec2 texture_size = 1.0f / vec2( 2048.0f, 512.0f ); // VEFontCache.Context.buffer_width/buffer_height - alpha = - (texture(sampler2D( draw_text_src_texture, draw_text_src_sampler), uv + vec2( -0.5f, -0.5f) * texture_size ).x * 0.25f) - + (texture(sampler2D( draw_text_src_texture, draw_text_src_sampler), uv + vec2( -0.5f, 0.5f) * texture_size ).x * 0.25f) - + (texture(sampler2D( draw_text_src_texture, draw_text_src_sampler), uv + vec2( 0.5f, -0.5f) * texture_size ).x * 0.25f) - + (texture(sampler2D( draw_text_src_texture, draw_text_src_sampler), uv + vec2( 0.5f, 0.5f) * texture_size ).x * 0.25f); - } + float alpha = texture(sampler2D( ve_draw_text_src_texture, ve_draw_text_src_sampler ), uv ).x; + + const vec2 texture_size = glyph_buffer_size; + const float down_sample = 1.0f / over_sample; + + alpha = + (texture(sampler2D( ve_draw_text_src_texture, ve_draw_text_src_sampler), uv + vec2( -0.5f, -0.5f) * texture_size ).x * down_sample) + + (texture(sampler2D( ve_draw_text_src_texture, ve_draw_text_src_sampler), uv + vec2( -0.5f, 0.5f) * texture_size ).x * down_sample) + + (texture(sampler2D( ve_draw_text_src_texture, ve_draw_text_src_sampler), uv + vec2( 0.5f, -0.5f) * texture_size ).x * down_sample) + + (texture(sampler2D( ve_draw_text_src_texture, ve_draw_text_src_sampler), uv + vec2( 0.5f, 0.5f) * texture_size ).x * down_sample); frag_color = vec4( colour.xyz, colour.a * alpha ); } @end -@program draw_text draw_text_vs draw_text_fs +@program ve_draw_text ve_draw_text_vs ve_draw_text_fs diff --git a/backend/sokol/source_shared.shdc.glsl b/backend/sokol/source_shared.shdc.glsl index 2d30f5d..cd9ed9a 100644 --- a/backend/sokol/source_shared.shdc.glsl +++ b/backend/sokol/source_shared.shdc.glsl @@ -1,6 +1,5 @@ in vec2 v_position; in vec2 v_texture; -// in vec4 v_elem; out vec2 uv; void main() diff --git a/docs/Readme.md b/docs/Readme.md index 7195858..48594d6 100644 --- a/docs/Readme.md +++ b/docs/Readme.md @@ -1,23 +1,6 @@ # Interface -Notes ---- - -The freetype setup is not finished. Specifically due to cache_glyph_freetype not parsing the glyph outline data structure properly. - -Freetype supports specifying a FT_Memory handle which is a pointer to a FT_MemoryRect. This can be used to define an allocator for the parser. Currently this library does not wrap this interface (yet). If using freetype its recommend to update `parser_init` with the necessary changes to wrap the context's backing allocator for freetype to utilize. - -```c - struct FT_MemoryRec_ - { - void* user; - FT_Alloc_Func alloc; - FT_Free_Func free; - FT_Realloc_Func realloc; - }; - ``` - -This library (seems) to perform best if the text commands are fed in 'whitespace aware chunks', where instead of feeding it entire blobs of text, the user identfies "words" in the text and feeding the visible and whitespce chunks derived from this to draw_text as separate calls. It improves the caching of the text shapes. The downside is there has to be a time where the text is parsed into tokens beforehand so that the this iteration does not have to occur continously. +## Lifetime ### startup @@ -31,32 +14,68 @@ Much of the data structures within the context struct are not fixed-capacity all The library supports being used in a dynamically loaded module. If its hot-reloaded simply make sure to call this procedure with a reference to the backing allocator provided during startup as all dynamic containers tend to lose a proper reference to the allocator's procedure. -Call clear_atlas_region_caches & clear_shape_cache to reset the library's shape and glyph cache state if doing tuning of the library. +Call `clear_atlas_region_caches` & `clear_shape_cache` to reset the library's shape and glyph cache state to force a re-render. ### shutdown Release resources from the context. -### configure_snap +### clear_atlas_region_caches -You'll find this used immediately in draw_text it acts as a way to snap the position of the text to the nearest pixel for the width and height specified. +Clears the LRU caches of regions A-D of the Atlas & sets their next_idx to 0. Effectively will force a re-cache of all previously rendered glyphs. Shape configuration for the glyph will remain unchanged unless clear_shape_cache is also called. -If snapping is not desired, set the snap_width and height before calling draw_text to 0. +### clear_shape_cache -### get_cursor_pos +Clears the LRU cache of the shaper along with clearing all existing storage entries. Effectively will force a re-cache of previously cached text shapes (Does not recache their rendered glyphs). -Will provide the current cursor_pos for the resulting text drawn. +### load_font + +Will load an instance of a font. The user needs to load the file's bytes themselves, the font entry (Entry :: struct) will by tracked by the library. The user will be given a font_id which is a direct index for the entry in the tracked array. + +### unload_font + +Will free an entry, (parser and shaper resources also freed) + +## Shaping + +Ideally the user should track the shapes themselves in a time-scale beyond the per-frame draw call. This avoids having to do caching/lookups of the shope. + +### shape_text -### set_color +Will shape the text using the `shaper_proc` arugment (user overloadable). Shape will be cached by the library. -Sets the color to utilize on `Draw_Call`s for FrameBuffer.Target or .Target_Uncached passes +### shape_text_uncached + +Will shape the text using the `shaper_proc` arugment (user overloadable). +Shape will NOT be cached by the library. Use this if you want to roll your own solution for tracking shapes. + +## Draw list generation + +### draw_text procedures + +There a total of six procedures, 3 for shapes, 3 for text: + +* `draw_shape_normalized_space` +* `draw_shape_view_space` +* `draw_shape` +* `draw_text_normalized_space` +* `draw_text_view_space` +* `draw_text` + +The normalized space procedures are the `baseline` interface draw procedures. They expec the position, and scale provided to operate with an unsigned normalized space where the bottom left is 0.0, 0.0 and the top right is 1.0, 1.0. + +The view space will normalize the position and scale for the user based on the provided view and zoom. The coordinate system is still unsigned just scaled to the view's size. + +The non-suffix named procedures use the scope stack to derive the position and scale the user provides a relative position and scale for the text that will be adjusted to the scope's view, position, scale, & zoom. + +See the comment above each of the procedures for diagrams. ### get_draw_list Get the enqueded draw_list (vertices, indices, and draw call arrays) in its entirety. By default, if get_draw_list is called, it will first call `optimize_draw_list` to optimize the draw list's calls for the user. If this is undesired, make sure to pass `optimize_before_returning = false` in the arguments. -### get_draw_list_layer +### get_draw_list_layer Get the enqueued draw_list for the current "layer". A layer is considered the slice of the `Draw_List`'s content from the last call to `flush_draw_list_layer` onward. @@ -72,6 +91,12 @@ Will clear the draw list and draw layer offsets. Will update the draw list layer with the latest offset based on the current lenght of the draw list vertices, indices, and calls arrays. +## Metrics + +### measure_shape_size + +This provide's the shape size scaled down by the ctx.px_scale to get intended usage size. Size is equivalent to `measure_text_size`. + ### measure_text_size Provides a Vec2 the width and height occupied by the provided text string. The y is measured to be the the largest glyph box bounds height of the text. The width is derived from the `end_cursor_pos` field from a `Shaped_Text` entry. @@ -80,10 +105,84 @@ Provides a Vec2 the width and height occupied by the provided text string. The y A wrapper for `parser_get_font_vertical_metrics`. Will provide the ascent, descent, and line_gap for a font entry. -### clear_atlas_region_caches +## Miscellaneous -Clears the LRU caches of regions A-D of the Atlas & sets their next_idx to 0. Effectively will force a re-cache of all previously rendered glyphs. Shape configuration for the glyph will remain unchanged unless clear_shape_cache is also called. +Stuff used by the draw list generation interface or just getters and setters. -### clear_shape_cache +### get_cursor_pos -Clears the LRU cache of the shaper along with clearing all existing storage entries. Effectively will force a re-cache of previously cached text shapes (Does not recache their rendered glyphs). +Will provide the current cursor_pos for the resulting text drawn. + +### get_normalized_position_scale + +Will normalize the value of the position and scale based on the provided view. +Position will also be snapped to the nearest pixel via ceil. +Does nothing if view is 1 or 0 + +This is used by draw via view relative space procedures to normalize it to the intended space for the render pass. + +### resolve_draw_px_size + +Used to constrain the px_size used in `resolve_zoom_size_scale`. + +The view relative space and scoping stack-based procedures support zoom. When utilizing zoom their is a nasty jitter that will occur if the user smoothly goes across different font sizes because the spacing can drastically change between even and odd font-sizes. This is applied to enforce the font sticks to a specific interval. + +The library uses the context's zoom_px_interval as the reference interval in the draw procedures. It can be set with `set_zoom_px_interval` and the default value is 2. + +### resolve_zoom_size_scale + +Provides a way to get a "zoom" on the font size and scale, similar conceptually to a canvas UX zoom +Does nothing when zoom is 1.0 + +Uses `resolve_draw_px_size` to constrain which font size is used for the zoom. + +### set_alpha_scalar + +This is an artifact feature of the current shader, it *may* be removed in the future... Increasing the alpha of the colour draw with above 1.0 increases the edge contrast of the glyph shape. + +For the value to be added to the colour, the alph of the text must already be at 1.0 or greater. + +### set_px_scalar + +This another "super-scalar" applied to rendering glyphs. In each draw procedure the following is computed before passing the values to the shaper and draw list generation passes: + +```go +target_px_size := px_size * ctx.px_scalar +target_scale := scale * (1 / ctx.px_scalar) +target_font_scale := parser_scale( entry.parser_info, target_px_size ) +``` + +Essentially, `ctx.px_scalar` is used to upscale the px_size by its value and then downscale the render target scale back the indended size. Doing so provides better shape positioning and futher improves text hinting. The downside is that small text tends to become more jagged (as its really hitting the limits of of how well the shader can blend those edges at that resolution). + +This will most likely be preserved with future shader upgrades, however it will most likely not be as necessary as it is right now to achieve crisp text. + +### set_zoom_px_interval + +Used with by draw procedures with `resolve_draw_px_size` & `resolve_zoom_size_scale`. Provides the interval to use when constraining the px_size to a specific set of values when using zoom scaling. + +### set_snap_glyph_shape_position + +During the shaping pass, the position of each glyph can be rounded up to the integer to (ussually) allow better hinting. + +### set_snap_glyph_render_height + +During the draw list generation pass, the position of each glyph when blitting to atlas can have teh quad size rounded up to the integer. +Can yield better hinting but may significantly stretch the glyphs at small scales. + +## Scope Stack + +These are a set of push & pop pairs of functions that operator ont he context's stack containers. They are used with the draw_shape and draw_text procedures. This mainly for quick scratch usage where the user wants to directly compose a large amount of text without having a UI framework directly handle the text backend. + +* font +* font_size +* colour: Linear colour. +* view: Width and height of the 2D area the text will be drawn within. +* position: Uses relative positioning will offset the incoming position by the given amount. +* scale: Uses relative scaling, will scale the procedures incoming scale by the given amount. +* zoom: Affects scaling, will scale the procedure's incoming font size & scale based on an *UX canvas camera's* notion of it. + +Procedure types: + +* `scope_`: push with a defer pop +* `push_` +* `pop_` diff --git a/docs/guide_architecture.md b/docs/guide_architecture.md new file mode 100644 index 0000000..cac801a --- /dev/null +++ b/docs/guide_architecture.md @@ -0,0 +1,234 @@ +# Guide: Architecture + +Overview of the package design and code-path layout. + +--- + +The purpose of this library is to alleviate four key challenges with one encapsulating package: + +* Font parsing +* Text codepoint shaping +* Glyph shape triangulation +* Glyph draw-list generation + +Shaping text, getting metrics for glyphs, triangulating glyphs, and anti-aliasing their render are expensive operations to perform per frame. Therefore, any compute operations that can be cached, will be. + +There are two cache types used: + +* Shape cache (`Shaped_Text_Cache.state`) +* Atlas region cache (`Atlas_Region.state`) + +The shape cache stores all data for a piece of text that will be utilized in a draw call that is not dependent on a specific position & scale (and is faster to lookup vs compute per draw call). +The atlas region cache tracks what slots have glyphs rendered to the texture atlas. This essentially caches triangulation and super-sampling computations. + +All caching uses [LRU.odin](../vefontcache/LRU.odin) + +## Code Paths + +### Lifetime + +The library lifetime is straightforward: you have a startup procedure that should be called during your usual app initialization. From there you may either choose to manually shut it down or let the OS clean it up. + +If hot-reload is desired, you just need to call hot_reload with the context's backing allocator to refresh the procedure references. After the DLL has been reloaded, these should be the only aspects that have been scrambled. +Usually when hot-reloading the library for tuning or major changes, you'd also want to clear the caches. Simply call `clear_atlas_region_caches` & `clear_shape_cache` right after. + +Ideally, there should be zero dynamic allocation on a per-frame basis as long as the reserves for the dynamic containers are never exceeded. It's acceptable if they do exceed as their memory locality is so large their distance in the pages to load into CPU cache won't matter - it just needs to be a low incidence. + +### Shaping Pass + +If using the library's cache, `shaper_shape_text_cached` handles the hashing and lookup. As long as a shape is found, it will not enter the uncached code path. By default, this library uses `shaper_shape_harfbuzz` as the `shape_text_uncached` procedure. + +Shapes are cached using the following parameters to hash a key: + +* font: Font_ID +* font_size: f32 +* the text itself: string + +All shapers fulfill the following interface: + +```odin +Shaper_Shape_Text_Uncached_Proc :: #type proc( ctx : ^Shaper_Context, + atlas : Atlas, + glyph_buffer_size : Vec2, + font : Font_ID, + entry : Entry, + font_px_Size : f32, + font_scale : f32, + text_utf8 : string, + output : ^Shaped_Text +) +``` + +Which will resolve the output `Shaped_Text`. It has the following definition: + +```odin +Shaped_Text :: struct #packed { + glyph : [dynamic]Glyph, + position : [dynamic]Vec2, + visible : [dynamic]i16, + atlas_lru_code : [dynamic]Atlas_Key, + region_kind : [dynamic]Atlas_Region_Kind, + bounds : [dynamic]Range2, + end_cursor_pos : Vec2, + size : Vec2, + font : Font_ID, + px_size : f32, +} +``` + +The result of the shaping process is the glyphs and their positions for the the shape; historically resembling whats known as a *Slug* of prepared text for printing. The end position of where the user's "cursor" would be is also recorded which provided the end position of the shape. The size of the shape is also resolved here, which if using px_scalar must be downscaled. `measure_shape_size` does the downscaling for the user. + +`visible` tracks which of the glyphs will actually be relevant for the draw_list pass. This is to avoid a conditional jump during the draw list gen pass. When accessing glyph or position during the draw_list gen, they will use visible's relative index. + +The font and px_size is tracked here as well so they user does not need to provide it to the library's interface and related. + +As stated under the main heading of this guide, the the following are within shaped text so that they may be resolved outside of the draw list generation (see: `generate_shape_draw_list`): + +* atlas_lru_code +* region_kind +* bounds + +These are the same length as the `visible` array, so indexing those will not need to use visibile's relative index. + +`shaper_shape_text_latin` does naive shaping by utilizing the codepoint's kern_advance and detecting newlines. +`shaper_shape_harfbuzz` is an actual shaping *engine*. Here is the general idea of how the library utilizes it for shaping: + +1. Reset the state of the hb_buffer +2. Determine the line height +3. Go through the codepoints: (for each) + 1. Determine the codepoint's script + 2. If the script is netural (Uknown, Inherited, or of Common type), the script has not changed, or this is the first codepoint of the shape we can add the codepoint to the buffer. + 3. Otherwise we will have to start a shaping run if we do encounter a significant script change. After, we can add the codepoint to the post-run-cleared hb_buffer. + 4. This continues until all codepoints have been processed. +4. We do a final shape run after iterating to make sure all codepoints have been processed. +5. Set the size of the shape: X is max line width, Y is line height multiplied by the line count. +6. Resolve the atlas_lru_code, region_kind, and bounds for all visible glyphs +7. Store the font and px_size information. + +The `shape_run` procedure within does the following: + +1. Setup the buffer for the batch +2. Have harfbuzz shape the buffer +3. Extract glyph infos and positions from the buffer. +4. Iterate through all glyphs + 1. If the hb_glyph cluster is > 0, we need to treat it as the indication of a newline glyph. ***(We update position and skip)*** + 2. Update positioning and other metrics and append output shape's glyph and position. + 3. If the glyph is visible we append it to shape's visible (harfbuzz must specify it as not .nodef, and parser must identify it as non-empty) +5. We update the output.end_cursor_pos with the last position processed by the iteration +6. Clear the hb_buffer's contents to prepare for a possible upcoming shape run. + +**Note on shape_run.4: The iteration doesn't preserve tracking the clusters, so that information is lost.** +*In the future cluster tracking may be added if its found to be important for high level text features beyond rendering.* + +**Note on shape_run.4.1: Don't know if the glyph signifiying newline should be preserved** + +See [Harfbuzz documentation](https://harfbuzz.github.io) for additional information. + +There are other shapers out there: + +* [hamza](https://github.com/saidwho12/hamza): A notable C library that could be setup with bindings. + +***Note: Monospace fonts may have a much more trivial shaper (however for fonts with ligatures this may not be the case)*** +***They should only need the kern advance of a single glyph as they're all the same. ligatures (I believe) should preserve this kern advance.*** + +### Draw List Generation + +All interface draw text procedures will ultimately call `generate_shape_draw_list`. If the draw procedure is given text, it will call `shaper_shape_text_cached` the text immediately before calling it. + +Its implementation uses a batched-pipeline approach where its goal is to populate three arrays behavings as queues: + +* oversized: For drawing oversized glyphs +* to_cache: For glyphs that need triangulation & rendering to glyph buffer then blitting to atlas. +* cache: For glyphs that are already cached in the atlas and just need to be blit to the render target. + +And then sent those off to `batch_generate_glyphs_draw_list` for further actual generation to be done. The size of a batch is determined by the capacity of the glyph_buffer's `batch_cache`. This can be set in `glyph_draw_params` for startup. + +`glyph_buffer.glyph_pack` is utilized by both `generate_shape_draw_list` and `batch_generate_glyphs_draw_list` to various computed data in an SOA data structure for the glyphs. + +generate_shape_draw_list outline: + +1. Prepare glyph_pack, oversized, to_cache, cached, and reset the batch cache + * `glyph_pack` is resized to to the length of `shape.visible` + * The other arrays populated have their reserved set to that length as well (they will not bounds check capacity on append) +2. Iterate through the shape.visible and resolve glyph_pack's positions. +3. Iterate through shape.visible this time for final region resolution and segregation of glyphs to their appropriate queue. + 1. If the glyphs assigned region is `.E` its oversized. The `oversample` used for rendering to render target will either be 2x or 1x depending on how huge it is. + 2. The following glyphs are checked to see if their assigned region has the glyph `cached`. + 1. If it does, its just appended to cached and marked as seen in the `batch_cache`. + 2. If its doesn't then a slot is reserved for within the atlas's region and the glyph is appended to `to_cache`. + 3. For either case the atlas_region_bbox is computed. + 3. After a batch has been resolved, `batch_generate_glyphs_draw_list` is called. +4. If there is an partially filled batch (the usual case), batch_generate_glyphs_draw_list will be called for it. +5. The cursor_pos is updated with the shape's end cursor position adjusted for the target space. + +batch_generate_glyphs_draw_list outline: + +The batch is organized into three major stages: + +1. glyph transform & draw quads compute +2. glyph_buffer draw list generation (`oversized` & `to_cache`) +3. blit-from-atlas to render target draw list generation (`to_cache` & `cached`) + +Glyph transform & draw quads compute does an iteration for each of the 3 arrays. +Nearly all the math for all three is done there *except* for `to_cache`, which does its blitting compute in its glyph_buffer draw-list gen pass. + +glyph_buffer draw list generation paths for `oversized` and `to_cache` are unique to each. + +For `oversized`: + +1. Allocate glyph shapes +2. Iterate oversized: + 1. Flush the glyph buffer if flagged todo so (reached glyph allocation limit) + 2. Call `generate_glyph_pass_draw_list` for trianglation and rendering to buffer. + 3. blit quad. +3. flush the glyph buffer's draw list. +4. free glyph shapes + +For `to_cached`: + +1. Allocate glyph shapes +2. Iterate to_cache: + 1. Flush the glyph buffer if flagged todo so (reached glyph allocation limit) + 2. Compute & blit quads for clearing the atlas region and blitting from the buffer to the atlas. + 3. Call `generate_glyph_pass_draw_list` for trianglation and rendering to buffer. +3. flush the glyph buffer's draw list. +4. free glyph shapes +5. Do blits from atlas to draw list. + +`cached` only needs to blit from the atlas to the render target. + +`generate_glyph_pass_draw_list`: sets up the draw call for glyph to the glyph buffer. Currently it also handles triangulation as well. For now the shape triangulation is rudimentary and uses triangle fanning. Eventually it would be nice to offer alternative modes that can be specified on a per-font basis. + +`flush_glyph_buffer_draw_list`: Will merge the draw_lists contents of the glyph buffer over to the library's general draw_list, the clear the buffer's draw lists. + +### On Layering + +The base draw list generation pippline provided by the library allows the user to batch whatever they want into a single "layer". +However, the user most likely would want take into consideration: font instances, font size, colors; these are things that may benefit from having shared locality during a layer batch. Overlaping text benefits from the user to handle the ordering via layers. + +Layers (so far) are just a set of offssets tracked by the library's `Context.draw_layer` struct. When `flush_draw_list_layer` is called, the offsets are set to the current length of the draw list. This allows the rendering backend to retrieve the latest set of vertices, indices, and calls to render on a per-layer basis with: `get_draw_list_layer`. + +Importantly, this leads to the following pattern when enuquing a layer to render: + +1. Begin render pass +2. For codepath that will deal with text layers + 1. Process user-level code-path that calls the draw text interface, populating the draw list layer (usually a for loop) + 2. After iteration on the layer is complete, render the text layer + 1. grab the draw list layer + 2. flush the layer so the draw list offsets are reset + 3. Repeat until all layers for the codepath are exhausted. + +There is consideration to instead explicitly have a draw list with more contextual information of the start and end of each layer. So that batching can be orchestrated in an isolated section of their pipeline. + +This would involve just tracking *slices* of thier draw-list that represents layers: + +```odin +Draw_List_Layer :: struct { + vertices : []Vertex, + indices : []u32, + calls : []Draw_Call, +} +``` + +Eventually the library may provide this since adding that feature is relatively cheap and and a low line-count addition to the interface. +There should be little to no perfomrance loss from doing so as the iteration size is two large of a surface area to matter (so its just pipeline ergonomics) diff --git a/docs/guide_backend.md b/docs/guide_backend.md new file mode 100644 index 0000000..a388ccf --- /dev/null +++ b/docs/guide_backend.md @@ -0,0 +1,68 @@ +# Guide: Backend + +The end-user needs to adapt this library to hook into their own codebase. For reference, they can check the [examples](../examples/) and [backend](../backend/) directories for working code that demonstrates what this guide covers. + +When rendering text, users need to handle two main aspects: the text to draw and its "layering". Similar to UIs, text should be drawn in layer batches, where each layer can represent a pass with arbitrary distinctions from other layers. + +The following components are required: + +* Vertex and Index Buffers for glyph meshes +* Glyph shader for rendering glyphs to the glyph buffer +* Atlas shader for blitting upscaled glyph quads from the glyph buffer to an atlas region slot (downsampled) +* "Screen or Target" shader for blitting glyph quads from the atlas to a render target or swapchain +* The glyph, atlas, and target image buffers + +Currently, the library doesn't support sub-pixel AA, so we're only rendering to R8 images. + +## Rendering Passes + +There are four passes that need to be handled when rendering a draw list: + +* Glyph: Rendering a glyph mesh to the glyph buffer +* Atlas: Blitting a glyph quad from the glyph buffer to an atlas slot +* Target: Blitting from the atlas image to the target image +* Target_Uncached: Blitting from the glyph buffer image to the target image + +The Target & Target_Uncached passes can technically be handled in the same case. The user just needs to swap between using the atlas image and the glyph buffer image. This is how the backend_soko.odin's `render_text_layer` has these passes set up. + +## Vertex Buffer Layout + +The vertex buffer has the following layout for all passes: + +* `[2]f32` for positions +* `[2]f32` for texture coords (Offset is naturally `[2]f32`) +* Total stride: `[4]f32` + +--- + +The index buffer is a simple u32 stream. + +For quad mesh layout details, see `blit_quad` in [draw.odin](../vefontcache/draw.odin). + +For glyph shape triangulation meshes, the library currently only uses a triangle fanning technique, implemented in `fill_path_via_fan_triangulation` within [draw.odin](../vefontcache/draw.odin). Eventually, the library will support other modes on a per-font basis. + +## UV Coordinate Conventions (GLSL vs HLSL) + +DirectX, Metal, and Vulkan consider the top-left corner as (0, 0), where the Y axis increases downward (traditional screenspace). This library follows OpenGL's convention, where (0, 0) is at the bottom-left (Y goes up). + +Adjust the UV coordinates in your shader accordingly: + +```c +#if !OpenGL +uv = vec2(v_texture.x, 1.0 - v_texture.y); +#else +uv = vec2(v_texture.x, v_texture.y); +#endif +``` + +Eventually, the library will support both conventions as a comp-time conditional. + +## Retrieving & Processing the layer + +`get_draw_list_layer` will provide the layer's vertex, index, and draw call slices. Unless the default is overwritten, it will call `optimize_draw_list` before returning the slices (profile to see whats better for your use case). +Once those are retrived, call `flush_draw_list_layer` to update the layer offsets tracked by the library's `Context`. + +The vertex and index slices just needed to be appended to your backend's vertex and index buffers. +The draw calls need to be iterated with a switch statement for the aforementioned pass types. Within the case you can construct the enqueue the passes. + +--- diff --git a/examples/sokol_demo/sokol_demo.odin b/examples/sokol_demo/sokol_demo.odin index 3017e07..12026db 100644 --- a/examples/sokol_demo/sokol_demo.odin +++ b/examples/sokol_demo/sokol_demo.odin @@ -42,7 +42,7 @@ FONT_LARGEST_PIXEL_SIZE :: 400 FONT_SIZE_INTERVAL :: 2 FONT_DEFAULT :: Font_ID { "" } -FONT_DEFAULT_SIZEZ :: 12.0 +FONT_DEFAULT_SIZE :: 12.0 FONT_LOAD_USE_DEFAULT_SIZE :: -1 FONT_LOAD_GEN_ID :: "" @@ -56,16 +56,17 @@ Font_ID :: struct { label : string, } -FontDef :: struct { +Font_Entry :: struct { path_file : string, + data : []byte, default_size : i32, - size_table : [FONT_LARGEST_PIXEL_SIZE / FONT_SIZE_INTERVAL] ve.Font_ID, + ve_id : ve.Font_ID, } Demo_Context :: struct { ve_ctx : ve.Context, render_ctx : ve_sokol.Context, - font_ids : map[string]FontDef, + font_ids : map[string]Font_Entry, // Values between 1, & -1 on Y axis mouse_scroll : Vec2, @@ -106,7 +107,7 @@ font_load :: proc(path_file : string, font_data, read_succeded : = os.read_entire_file( path_file ) assert( bool(read_succeded), fmt.tprintf("Failed to read font file for: %v", path_file) ) font_data_size := cast(i32) len(font_data) -font_firacode : Font_ID + font_firacode : Font_ID desired_id := desired_id @@ -115,24 +116,20 @@ font_firacode : Font_ID desired_id = file_name_from_path(path_file) } - demo_ctx.font_ids[desired_id] = FontDef {} + demo_ctx.font_ids[desired_id] = Font_Entry {} def := & demo_ctx.font_ids[desired_id] default_size := default_size if default_size < 0 { - default_size = FONT_DEFAULT_SIZEZ + default_size = FONT_DEFAULT_SIZE } + error : ve.Load_Font_Error def.path_file = path_file def.default_size = default_size - - for font_size : i32 = clamp( FONT_SIZE_INTERVAL, 2, FONT_SIZE_INTERVAL ); font_size <= FONT_LARGEST_PIXEL_SIZE; font_size += FONT_SIZE_INTERVAL - { - id := (font_size / FONT_SIZE_INTERVAL) + (font_size % FONT_SIZE_INTERVAL) - ve_id := & def.size_table[id - 1] - ve_ret_id := ve.load_font( & demo_ctx.ve_ctx, desired_id, font_data, f32(font_size), curve_quality ) - (ve_id^) = ve_ret_id - } + def.data = font_data + def.ve_id, error = ve.load_font( & demo_ctx.ve_ctx, desired_id, font_data, curve_quality ) + assert(error == .None) fid := Font_ID { desired_id } return fid @@ -140,81 +137,39 @@ font_firacode : Font_ID Font_Use_Default_Size :: f32(0.0) -font_resolve_draw_id :: proc( id : Font_ID, size := Font_Use_Default_Size ) -> ( ve_id : ve.Font_ID, resolved_size : i32 ) -{ - def := demo_ctx.font_ids[ id.label ] - size := size == 0.0 ? f32(def.default_size) : size - even_size := math.round(size * (1.0 / f32(FONT_SIZE_INTERVAL))) * f32(FONT_SIZE_INTERVAL) - resolved_size = clamp( i32( even_size), 2, FONT_LARGEST_PIXEL_SIZE ) - - id := (resolved_size / FONT_SIZE_INTERVAL) + (resolved_size % FONT_SIZE_INTERVAL) - ve_id = def.size_table[ id - 1 ] - return -} - measure_text_size :: proc( text : string, font : Font_ID, font_size := Font_Use_Default_Size, spacing : f32 ) -> Vec2 { - ve_id, size := font_resolve_draw_id( font, font_size ) - measured := ve.measure_text_size( & demo_ctx.ve_ctx, ve_id, text ) + def := demo_ctx.font_ids[ font.label ] + measured := ve.measure_text_size( & demo_ctx.ve_ctx, def.ve_id, font_size, text ) return measured } get_font_vertical_metrics :: #force_inline proc ( font : Font_ID, font_size := Font_Use_Default_Size ) -> ( ascent, descent, line_gap : f32 ) { - ve_id, size := font_resolve_draw_id( font, font_size ) - ascent, descent, line_gap = ve.get_font_vertical_metrics( & demo_ctx.ve_ctx, ve_id ) + def := demo_ctx.font_ids[ font.label ] + ascent, descent, line_gap = ve.get_font_vertical_metrics( demo_ctx.ve_ctx, def.ve_id, font_size ) return } // Draw text using a string and normalized render coordinates -draw_text_string_pos_norm :: proc( content : string, id : Font_ID, size : f32, pos : Vec2, color := COLOR_WHITE, scale : f32 = 1.0 ) -{ - width := demo_ctx.screen_size.x - height := demo_ctx.screen_size.y - - ve_id, resolved_size := font_resolve_draw_id( id, size ) - color_norm := normalize_rgba8(color) - - ve.set_colour( & demo_ctx.ve_ctx, color_norm ) - ve.draw_text( & demo_ctx.ve_ctx, ve_id, content, pos, Vec2{1 / width, 1 / height} * scale ) - return -} - -// Draw text using a string and extent-based screen coordinates -draw_text_string_pos_extent :: proc( content : string, id : Font_ID, size : f32, pos : Vec2, color := COLOR_WHITE ) { - render_pos := pos + demo_ctx.screen_size * 0.5 - normalized_pos := render_pos * (1.0 / demo_ctx.screen_size) - draw_text_string_pos_norm( content, id, size, normalized_pos, color ) -} - -// Adapt the draw_text_string_pos_extent_zoomed procedure -draw_text_zoomed_norm :: proc(content : string, id : Font_ID, size : f32, pos : Vec2, zoom : f32, color := COLOR_WHITE) +draw_text :: proc( content : string, font : Font_ID, pos : Vec2, size : f32 = 0.0, color := COLOR_WHITE, scale : f32 = 1.0, zoom : f32 = 1.0 ) { - screen_size := demo_ctx.screen_size - screen_scale := Vec2{1.0 / screen_size.x, 1.0 / screen_size.y} - zoom_adjust_size := size * zoom - - // Over-sample font-size - - zoom_adjust_size *= OVER_SAMPLE_ZOOM - - ve_id, resolved_size := font_resolve_draw_id(id, zoom_adjust_size) - - text_scale := screen_scale - { - f32_resolved_size := f32(resolved_size) - diff_scalar := 1 + (zoom_adjust_size - f32_resolved_size) / f32_resolved_size - text_scale = diff_scalar * screen_scale - text_scale.x = clamp(text_scale.x, 0, 1) - text_scale.y = clamp(text_scale.y, 0, 1) - } - - // Down-sample back - text_scale /= OVER_SAMPLE_ZOOM - color_norm := normalize_rgba8(color) - ve.set_colour(&demo_ctx.ve_ctx, color_norm) - ve.draw_text(&demo_ctx.ve_ctx, ve_id, content, pos, text_scale) + def := demo_ctx.font_ids[ font.label ] + size := size >= 2.0 ? size : f32(def.default_size) + + resolved_size, zoom_scale := ve.resolve_zoom_size_scale( zoom, size, scale, 2, 2, 999.0, demo_ctx.screen_size ) + snapped_pos := ve.snap_normalized_position_to_view( pos, demo_ctx.screen_size ) + norm_scale := zoom_scale * (1 / demo_ctx.screen_size) + ve.draw_text_normalized_space( & demo_ctx.ve_ctx, + def.ve_id, + resolved_size, + color_norm, + snapped_pos, + norm_scale, + content + ) + return } sokol_app_alloc :: proc "c" ( size : uint, user_data : rawptr ) -> rawptr { @@ -271,11 +226,22 @@ init :: proc "c" () case .DUMMY: fmt.println(">> using dummy backend") } - ve.startup( & demo_ctx.ve_ctx, .STB_TrueType, allocator = context.allocator ) - ve_sokol.setup_gfx_objects( & demo_ctx.render_ctx, & demo_ctx.ve_ctx, vert_cap = 1024 * 1024, index_cap = 1024 * 1024 ) + glyph_draw_opts := ve.Init_Glyph_Draw_Params_Default + glyph_draw_opts.snap_glyph_height = true + + shaper_opts := ve.Init_Shaper_Params_Default + shaper_opts.snap_glyph_position = true + + ve.startup( & demo_ctx.ve_ctx, .STB_TrueType, allocator = context.allocator, + glyph_draw_params = glyph_draw_opts, + shaper_params = shaper_opts, + px_scalar = 1.6, + alpha_sharpen = 0.35, + ) + ve_sokol.setup_gfx_objects( & demo_ctx.render_ctx, & demo_ctx.ve_ctx, vert_cap = 256 * 1024, index_cap = 512 * 1024 ) error : mem.Allocator_Error - demo_ctx.font_ids, error = make( map[string]FontDef, 256 ) + demo_ctx.font_ids, error = make( map[string]Font_Entry, 256 ) assert( error == .None, "Failed to allocate demo_ctx.font_ids" ) path_sawarabi_mincho := strings.concatenate({ PATH_FONTS, "SawarabiMincho-Regular.ttf" }) @@ -294,26 +260,25 @@ init :: proc "c" () path_noto_sans_jp_reg := strings.concatenate({ PATH_FONTS, "NotoSansJP-Regular.otf" }) path_firacode := strings.concatenate({ PATH_FONTS, "FiraCode-Regular.ttf" }) - using demo_ctx - font_logo = font_load(path_sawarabi_mincho, 330.0, "SawarabiMincho", 6 ) - font_title = font_load(path_open_sans, 92.0, "OpenSans", 12 ) - font_print = font_load(path_noto_sans_jp, 19.0, "NotoSansJP") - font_mono = font_load(path_ubuntu_mono, 21.0, "UbuntuMono") - font_small = font_load(path_roboto, 10.0, "Roboto") - font_demo_sans = font_load(path_open_sans, 18.0, "OpenSans") - font_demo_serif = font_load(path_bitter, 18.0, "Bitter") - font_demo_script = font_load(path_dancing_script, 22.0, "DancingScript") - font_demo_mono = font_load(path_nova_mono, 18.0, "NovaMono") - font_demo_chinese = font_load(path_noto_serif_sc, 24.0, "NotoSerifSC") - font_demo_japanese = font_load(path_sawarabi_mincho, 24.0, "SawarabiMincho") - font_demo_korean = font_load(path_nanum_pen_script, 36.0, "NanumPenScript") - font_demo_thai = font_load(path_krub, 24.0, "Krub") - font_demo_arabic = font_load(path_tajawal, 24.0, "Tajawal") - font_demo_hebrew = font_load(path_david_libre, 22.0, "DavidLibre") - font_demo_raincode = font_load(path_noto_sans_jp_reg, 20.0, "NotoSansJPRegular") - font_demo_grid2 = font_load(path_noto_serif_sc, 54.0, "NotoSerifSC") - font_demo_grid3 = font_load(path_bitter, 44.0, "Bitter") - font_firacode = font_load(path_firacode, 16.0, "FiraCode", 12 ) + demo_ctx.font_logo = font_load(path_sawarabi_mincho, 150.0, "SawarabiMincho", 18 ) + demo_ctx.font_print = font_load(path_noto_sans_jp, 19.0, "NotoSansJP") + demo_ctx.font_mono = font_load(path_ubuntu_mono, 21.0, "UbuntuMono") + demo_ctx.font_small = font_load(path_roboto, 10.0, "Roboto") + demo_ctx.font_demo_sans = font_load(path_open_sans, 18.0, "OpenSans", 6) + demo_ctx.font_demo_serif = font_load(path_bitter, 18.0, "Bitter") + demo_ctx.font_demo_script = font_load(path_dancing_script, 22.0, "DancingScript") + demo_ctx.font_demo_mono = font_load(path_nova_mono, 18.0, "NovaMono") + demo_ctx.font_demo_chinese = font_load(path_noto_serif_sc, 24.0, "NotoSerifSC") + demo_ctx.font_demo_japanese = font_load(path_sawarabi_mincho, 24.0, "SawarabiMincho") + demo_ctx.font_demo_korean = font_load(path_nanum_pen_script, 36.0, "NanumPenScript") + demo_ctx.font_demo_thai = font_load(path_krub, 24.0, "Krub") + demo_ctx.font_demo_arabic = font_load(path_tajawal, 24.0, "Tajawal") + demo_ctx.font_demo_hebrew = font_load(path_david_libre, 22.0, "DavidLibre") + demo_ctx.font_demo_raincode = font_load(path_noto_sans_jp_reg, 20.0, "NotoSansJPRegular") + demo_ctx.font_title = demo_ctx.font_demo_sans + demo_ctx.font_demo_grid2 = demo_ctx.font_demo_chinese + demo_ctx.font_demo_grid3 = demo_ctx.font_demo_serif + demo_ctx.font_firacode = font_load(path_firacode, 16.0, "FiraCode", 12 ) } event :: proc "c" (sokol_event : ^app.Event) @@ -335,10 +300,8 @@ frame :: proc "c" () gfx.begin_pass({ action = pass_action, swapchain = glue.swapchain() }) gfx.end_pass() { - ve.configure_snap( & demo_ctx.ve_ctx, u32(demo_ctx.screen_size.x), u32(demo_ctx.screen_size.y) ) - ve.set_colour( & demo_ctx.ve_ctx, ve.Colour { 1.0, 1.0, 1.0, 1.0 }) - - using demo_ctx + // ve.configure_snap( & demo_ctx.ve_ctx, u32(demo_ctx.screen_size.x), u32(demo_ctx.screen_size.y) ) + // ve.set_colour( & demo_ctx.ve_ctx, ve.Colour { 1.0, 1.0, 1.0, 1.0 }) // Smooth scrolling implementation @static demo_autoscroll := false @@ -350,7 +313,7 @@ frame :: proc "c" () frame_duration := cast(f32) app.frame_duration() - scroll_velocity += mouse_scroll.y * 0.05 + scroll_velocity += demo_ctx.mouse_scroll.y * 0.035 mouse_down_pos = -1.0 substep_dt := frame_duration / 4.0 for _ in 0 ..< 4 { @@ -360,14 +323,16 @@ frame :: proc "c" () if demo_autoscroll { current_scroll += 0.05 * frame_duration } - mouse_scroll = {} // Reset mouse scroll + demo_ctx.mouse_scroll = {} // Reset mouse scroll // Clamp scroll value if needed current_scroll = clamp(current_scroll, 0, 6.1) // Adjust max value as needed // Frametime display frametime_text := fmt.tprintf("Frametime %v", frame_duration) - draw_text_string_pos_norm(frametime_text, font_title, 0, {0.0, 0.0}, COLOR_WHITE) + draw_text(frametime_text, demo_ctx.font_title, {0.0, 0.0}, size = 30) + + // Below is content based on the original demo from the C++ library. if current_scroll < 1.5 { intro := `Ça va! Everything here is rendered using VE Font Cache, a single header-only library designed for game engines. @@ -382,9 +347,9 @@ It aims to: • Support cached text shaping with HarfBuzz with simple Latin-style fallback. • Load and unload fonts at any time.` - draw_text_string_pos_norm("ゑ", font_logo, 330, {0.4, current_scroll}, COLOR_WHITE) - draw_text_string_pos_norm("VEFontCache Demo", font_title, 92, {0.2, current_scroll - 0.1}, COLOR_WHITE) - draw_text_string_pos_norm(intro, font_print, 19, {0.2, current_scroll - 0.14}, COLOR_WHITE) + draw_text("ゑ", demo_ctx.font_logo, { 0.4, current_scroll }, size = 200, scale = 2.0) + draw_text("VEFontCache Demo", demo_ctx.font_title, { 0.2, current_scroll - 0.1 }, size = 92) + draw_text(intro, demo_ctx.font_print, { 0.2, current_scroll - 0.14 }, size = 19) } section_start : f32 = 0.42 @@ -427,10 +392,10 @@ Glyphs are first rendered to an intermediate 2k x 512px R8 texture. This allows 4 x 4 = 16x supersampling, and 8 Region C glyphs similarly. A simple 16-tap box downsample shader is then used to blit from this intermediate texture to the final atlas location.` - draw_text_string_pos_norm("How it works", font_title, 92, {0.2, current_scroll - (section_start + 0.06)}, COLOR_WHITE) - draw_text_string_pos_norm(how_it_works, font_print, 19, {0.2, current_scroll - (section_start + 0.1)}, COLOR_WHITE) - draw_text_string_pos_norm(caching_strategy, demo_ctx.font_mono, 21, {0.28, current_scroll - (section_start + 0.32)}, COLOR_WHITE) - draw_text_string_pos_norm(how_it_works2, font_print, 19, {0.2, current_scroll - (section_start + 0.82)}, COLOR_WHITE) + draw_text("How it works", demo_ctx.font_title, { 0.2, current_scroll - (section_start + 0.06) }, size = 92) + draw_text(how_it_works, demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.1) }, size = 19) + draw_text(caching_strategy, demo_ctx.font_mono, { 0.28, current_scroll - (section_start + 0.32) }, size = 21) + draw_text(how_it_works2, demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.82) }, size = 19) } // Showcase section @@ -441,49 +406,49 @@ intermediate texture to the final atlas location.` incididunt ut labore et dolore magna aliqua. Est ullamcorper eget nulla facilisi etiam dignissim diam quis enim. Convallis convallis tellus id interdum.` - draw_text_string_pos_norm("Showcase", font_title, 92, {0.2, current_scroll - (section_start + 0.2)}, COLOR_WHITE) - draw_text_string_pos_norm("This is a showcase demonstrating different hb_font categories and languages.", font_print, 19, {0.2, current_scroll - (section_start + 0.24)}, COLOR_WHITE) + draw_text("Showcase", demo_ctx.font_title, { 0.2, current_scroll - (section_start + 0.2) }, size = 92) + draw_text("This is a showcase demonstrating different hb_font categories and languages.", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.24) }, size = 19) - draw_text_string_pos_norm("Sans serif", font_print, 19, {0.2, current_scroll - (section_start + 0.28)}, COLOR_WHITE) - draw_text_string_pos_norm(font_family_test, font_demo_sans, 18, {0.3, current_scroll - (section_start + 0.28)}, COLOR_WHITE) + draw_text("Sans serif", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.28) }, size = 19) + draw_text(font_family_test, demo_ctx.font_demo_sans, { 0.3, current_scroll - (section_start + 0.28) }, size = 18) - draw_text_string_pos_norm("Serif", font_print, 19, {0.2, current_scroll - (section_start + 0.36)}, COLOR_WHITE) - draw_text_string_pos_norm(font_family_test, font_demo_serif, 18, {0.3, current_scroll - (section_start + 0.36)}, COLOR_WHITE) + draw_text("Serif", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.36) }, size = 19) + draw_text(font_family_test, demo_ctx.font_demo_serif, { 0.3, current_scroll - (section_start + 0.36) }, size = 18) - draw_text_string_pos_norm("Script", font_print, 19, {0.2, current_scroll - (section_start + 0.44)}, COLOR_WHITE) - draw_text_string_pos_norm(font_family_test, font_demo_script, 22, {0.3, current_scroll - (section_start + 0.44)}, COLOR_WHITE) + draw_text("Script", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.44) }, size = 19) + draw_text(font_family_test, demo_ctx.font_demo_script, { 0.3, current_scroll - (section_start + 0.44) }, size = 22) - draw_text_string_pos_norm("Monospace", font_print, 19, {0.2, current_scroll - (section_start + 0.52)}, COLOR_WHITE) - draw_text_string_pos_norm(font_family_test, font_demo_mono, 18, {0.3, current_scroll - (section_start + 0.52)}, COLOR_WHITE) + draw_text("Monospace", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.52) }, size = 19) + draw_text(font_family_test, demo_ctx.font_demo_mono, { 0.3, current_scroll - (section_start + 0.52) }, size = 22) - draw_text_string_pos_norm("Small", font_print, 19, {0.2, current_scroll - (section_start + 0.60)}, COLOR_WHITE) - draw_text_string_pos_norm(font_family_test, font_small, 10, {0.3, current_scroll - (section_start + 0.60)}, COLOR_WHITE) + draw_text("Small", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.60) }, size = 19) + draw_text(font_family_test, demo_ctx.font_small, { 0.3, current_scroll - (section_start + 0.60) }, size = 10) - draw_text_string_pos_norm("Greek", font_print, 19, {0.2, current_scroll - (section_start + 0.72)}, COLOR_WHITE) - draw_text_string_pos_norm("Ήταν απλώς θέμα χρόνου.", font_demo_sans, 18, {0.3, current_scroll - (section_start + 0.72)}, COLOR_WHITE) + draw_text("Greek", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.72) }, size = 19) + draw_text("Ήταν απλώς θέμα χρόνου.", demo_ctx.font_demo_sans, { 0.3, current_scroll - (section_start + 0.72) }, size = 18) - draw_text_string_pos_norm("Vietnamese", font_print, 19, {0.2, current_scroll - (section_start + 0.76)}, COLOR_WHITE) - draw_text_string_pos_norm("Bầu trời trong xanh thăm thẳm, không một gợn mây.", font_demo_sans, 18, {0.3, current_scroll - (section_start + 0.76)}, COLOR_WHITE) + draw_text("Vietnamese", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.76) }, size = 19) + draw_text("Bầu trời trong xanh thăm thẳm, không một gợn mây.", demo_ctx.font_demo_sans, { 0.3, current_scroll - (section_start + 0.76) }, size = 18) - draw_text_string_pos_norm("Thai", font_print, 19, {0.2, current_scroll - (section_start + 0.80)}, COLOR_WHITE) - draw_text_string_pos_norm("การเดินทางขากลับคงจะเหงา", font_demo_thai, 24, {0.3, current_scroll - (section_start + 0.80)}, COLOR_WHITE) + draw_text("Thai", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.80) }, size = 19) + draw_text("การเดินทางขากลับคงจะเหงา", demo_ctx.font_demo_thai, { 0.3, current_scroll - (section_start + 0.80) }, size = 24) - draw_text_string_pos_norm("Chinese", font_print, 19, {0.2, current_scroll - (section_start + 0.84)}, COLOR_WHITE) - draw_text_string_pos_norm("床前明月光 疑是地上霜 举头望明月 低头思故乡", font_demo_chinese, 24, {0.3, current_scroll - (section_start + 0.84)}, COLOR_WHITE) + draw_text("Chinese", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.84) }, size = 19) + draw_text("床前明月光 疑是地上霜 举头望明月 低头思故乡", demo_ctx.font_demo_chinese, {0.3, current_scroll - (section_start + 0.84) }, size = 24) - draw_text_string_pos_norm("Japanese", font_print, 19, {0.2, current_scroll - (section_start + 0.88)}, COLOR_WHITE) - draw_text_string_pos_norm("ぎょしょうとナレズシの研究 モンスーン・アジアの食事文化", font_demo_japanese, 24, {0.3, current_scroll - (section_start + 0.88)}, COLOR_WHITE) + draw_text("Japanese", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.88) }, size = 19) + draw_text("ぎょしょうとナレズシの研究 モンスーン・アジアの食事文化", demo_ctx.font_demo_japanese, { 0.3, current_scroll - (section_start + 0.88) }, size = 24) - draw_text_string_pos_norm("Korean", font_print, 19, {0.2, current_scroll - (section_start + 0.92)}, COLOR_WHITE) - draw_text_string_pos_norm("그들의 장비와 기구는 모두 살아 있다.", font_demo_korean, 36, {0.3, current_scroll - (section_start + 0.92)}, COLOR_WHITE) + draw_text("Korean", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.92) }, size = 19) + draw_text("그들의 장비와 기구는 모두 살아 있다.", demo_ctx.font_demo_korean, { 0.3, current_scroll - (section_start + 0.92) }, size = 36) - draw_text_string_pos_norm("Needs harfbuzz to work:", font_print, 14, {0.2, current_scroll - (section_start + 0.96)}, COLOR_WHITE) + draw_text("Needs harfbuzz to work:", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 0.96)}, size = 14) - draw_text_string_pos_norm("Arabic", font_print, 19, {0.2, current_scroll - (section_start + 1.00)}, COLOR_WHITE) - draw_text_string_pos_norm("حب السماء لا تمطر غير الأحلام.", font_demo_arabic, 24, {0.3, current_scroll - (section_start + 1.00)}, COLOR_WHITE) + draw_text("Arabic", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 1.00) }, size = 19) + draw_text("حب السماء لا تمطر غير الأحلام.", demo_ctx.font_demo_arabic, { 0.3, current_scroll - (section_start + 1.00) }, size = 24) - draw_text_string_pos_norm("Hebrew", font_print, 19, {0.2, current_scroll - (section_start + 1.04)}, COLOR_WHITE) - draw_text_string_pos_norm("אז הגיע הלילה של כוכב השביט הראשון.", font_demo_hebrew, 22, {0.3, current_scroll - (section_start + 1.04)}, COLOR_WHITE) + draw_text("Hebrew", demo_ctx.font_print, { 0.2, current_scroll - (section_start + 1.04) }, size = 19) + draw_text("אז הגיע הלילה של כוכב השביט הראשון.", demo_ctx.font_demo_hebrew, { 0.3, current_scroll - (section_start + 1.04) }, size = 22) } // Zoom Test @@ -521,20 +486,18 @@ etiam dignissim diam quis enim. Convallis convallis tellus id interdum.` zoom_info_y := current_scroll - (section_start + 0.10) zoomed_text_y := current_scroll - (section_start + 0.30) + math.sin(zoom_time) * 0.02 - draw_text_string_pos_norm("Zoom Test", font_title, 92, {0.2, title_y}, COLOR_WHITE) + draw_text("Zoom Test", demo_ctx.font_title, { 0.2, title_y }, size = 92) zoomed_text_base_size : f32 = 12.0 zoom_adjust_size := zoomed_text_base_size * current_zoom - ve_id, resolved_size := font_resolve_draw_id( font_firacode, zoom_adjust_size * OVER_SAMPLE_ZOOM ) + resolved_size, _ := ve.resolve_zoom_size_scale(current_zoom, zoomed_text_base_size, 1.0, 2, 2, 999.0, demo_ctx.screen_size) current_zoom_text := fmt.tprintf("Current Zoom : %.2f x\nCurrent Resolved Size: %v px", current_zoom, resolved_size ) - draw_text_string_pos_norm(current_zoom_text, font_firacode, 19, {0.2, zoom_info_y}, COLOR_WHITE) + draw_text(current_zoom_text, demo_ctx.font_firacode, { 0.2, zoom_info_y }) - ve.configure_snap( & demo_ctx.ve_ctx, u32(0), u32(0) ) - - size := measure_text_size( zoom_text, font_firacode, zoomed_text_base_size, 0 ) * current_zoom + size := measure_text_size( zoom_text, demo_ctx.font_firacode, zoomed_text_base_size, 0 ) * current_zoom x_offset := (size.x / demo_ctx.screen_size.x) * 0.5 zoomed_text_pos := Vec2 { 0.5 - x_offset, zoomed_text_y } - draw_text_zoomed_norm(zoom_text, font_firacode, zoomed_text_base_size, zoomed_text_pos, current_zoom, COLOR_WHITE) + draw_text(zoom_text, demo_ctx.font_firacode, zoomed_text_pos, size = zoomed_text_base_size, zoom = current_zoom) } // Raincode Demo @@ -543,7 +506,7 @@ etiam dignissim diam quis enim. Convallis convallis tellus id interdum.` if current_scroll > section_start && current_scroll < section_end { GRID_W :: 80 - GRID_H :: 50 + GRID_H :: 45 NUM_RAINDROPS :: GRID_W / 3 @static init_grid := false @@ -557,7 +520,7 @@ etiam dignissim diam quis enim. Convallis convallis tellus id interdum.` " ", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "Z", "T", "H", "E", "|", "¦", "日", "ハ", "ミ", "ヒ", "ー", "ウ", "シ", "ナ", "モ", "ニ", "サ", "ワ", "ツ", "オ", "リ", "ア", "ホ", "テ", "マ", "ケ", "メ", "エ", "カ", "キ", "ム", "ユ", "ラ", "セ", "ネ", "ス", "ツ", "タ", "ヌ", "ヘ", ":", "・", ".", - "\"", "=", "*", "+", "-", "<", ">", "ç", "リ", "ク", "コ", "チ", "ヤ", "ル", "ン", "C", "O", "D" + "\"", "=", "*", "+", "-", "<", ">", "ç", "リ", "ク", "コ", "チ", "ヤ", "ル", "ン", "C", "O", "D", } if !init_grid { @@ -586,7 +549,7 @@ etiam dignissim diam quis enim. Convallis convallis tellus id interdum.` } // Draw grid - draw_text_string_pos_norm("Raincode demo", font_title, 92, { 0.2, current_scroll - (section_start + 0.2) }, COLOR_WHITE) + draw_text("Raincode demo", demo_ctx.font_title, { 0.2, current_scroll - (section_start + 0.2) }, size = 92) for y in 0 ..< GRID_H do for x in 0 ..< GRID_W { pos_x := 0.2 + f32(x) * 0.007 @@ -603,16 +566,14 @@ etiam dignissim diam quis enim. Convallis convallis tellus id interdum.` if code_colour.a == 0 do continue } - draw_text_string_pos_norm(codes[grid[y * GRID_W + x]], font_demo_raincode, 20, {pos_x, pos_y}, code_colour) + draw_text(codes[grid[y * GRID_W + x]], demo_ctx.font_demo_raincode, { pos_x, pos_y }, size = 20, color = code_colour) } - - ve.set_colour(&ve_ctx, {1.0, 1.0, 1.0, 1.0}) } // Cache pressure test section_start = 5.3 section_end = 6.2 - if current_scroll > section_start && current_scroll < section_end && true + if current_scroll > section_start && current_scroll < section_end { GRID_W :: 30 GRID_H :: 15 @@ -629,7 +590,7 @@ etiam dignissim diam quis enim. Convallis convallis tellus id interdum.` @static fixed_timestep_passed : f32 = 0.0 fixed_timestep_passed += frame_duration - fixed_timestep := f32(1.0 / 30.0) + fixed_timestep := f32(1.0 / 120.0) for fixed_timestep_passed > fixed_timestep { rotate_current = (rotate_current + 1) % 4 @@ -673,28 +634,28 @@ etiam dignissim diam quis enim. Convallis convallis tellus id interdum.` } // Draw grid - draw_text_string_pos_norm("Cache pressure test", font_title, 92, {0.2, current_scroll - (section_start + 0.2)}, COLOR_WHITE) + draw_text("Cache pressure test (throttled to 120 hz)", demo_ctx.font_title, { 0.2, current_scroll - (section_start + 0.2) }, size = 72) for y in 0..< GRID_H do for x in 0 ..< GRID_W { posx := 0.2 + f32(x) * 0.02 posy := current_scroll - (section_start + 0.24 + f32(y) * 0.025) c := [5]u8{} codepoint_to_utf8(c[:], grid[ y * GRID_W + x ]) - draw_text_string_pos_norm(string( c[:] ), font_demo_chinese, 24, {posx, posy}, COLOR_WHITE) + draw_text(string( c[:] ), demo_ctx.font_demo_chinese, { posx, posy }, size = 24) } for y in 0 ..< GRID2_H do for x in 0 ..< GRID2_W { posx := 0.2 + f32(x) * 0.03 posy := current_scroll - (section_start + 0.66 + f32(y) * 0.052) c := [5]u8{} codepoint_to_utf8(c[:], grid2[ y * GRID2_W + x ]) - draw_text_string_pos_norm(string( c[:] ), font_demo_grid2, 54, {posx, posy}, COLOR_WHITE) + draw_text(string( c[:] ), demo_ctx.font_demo_grid2, { posx, posy }, size = 54) } for y in 0 ..< GRID3_H do for x in 0 ..< GRID3_W { posx := 0.45 + f32(x) * 0.02 - posy := current_scroll - (section_start + 0.64 + f32(y) * 0.034) + posy := current_scroll - (section_start + 0.64 + f32(y) * 0.04) c := [5]u8{} codepoint_to_utf8( c[:], grid3[ y * GRID3_W + x ]) - draw_text_string_pos_norm(string( c[:] ), font_demo_grid3, 44, {posx, posy}, COLOR_WHITE) + draw_text(string( c[:] ), demo_ctx.font_demo_grid3, { posx, posy }, size = 44) } } diff --git a/ols.json b/ols.json deleted file mode 100644 index 4f0edda..0000000 --- a/ols.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/ols.schema.json", - "odin_command": "odin.exe", - "collections": [ - { - "name": "backend", - "path": "./backend" - }, - { - "name": "examples", - "path": "./sectr" - }, - { - "name": "thirdparty", - "path": "./thirdparty" - } - ], - "enable_document_symbols": true, - "enable_fake_methods": true, - "enable_format": false, - "enable_hover": true, - "enable_semantic_tokens": false, - "enable_snippets": false, - "enable_references": true, - "thread_pool_count": 10, - "enable_inlay_hints": true, - "enable_procedure_context": true, - "enable_procedure_snippet": false, - "verbose": true, - "disable_parser_errors": true -} diff --git a/scripts/Readme.md b/scripts/Readme.md index 2599011..ba1549b 100644 --- a/scripts/Readme.md +++ b/scripts/Readme.md @@ -12,7 +12,12 @@ Its assumed the user has Odin installed and exposed to the OS enviornment's PATH #### Note on dependency packages -All dependencies are cloned directly into a created thirdparty directory. +A custom version of the vendor:stb/truetype is maintained by this library: + +* Added ability to set the stb_truetype allocator for STBTT_MALLOC and STBTT_FREE. +* Changed procedure signatures to pass the font_info struct by immutable ptr (#by_ptr) when the C equivalent has their parameter as `const*`. + +All other dependencies are cloned directly into a created thirdparty directory. [harfbuzz](https://github.com/Ed94/odin_harfbuzz) is configured to pull & build the C++ library, it will use the MSVC toolchain (you can change it to use meson instead of preferred). [freetype](https://github.com/Ed94/odin-freetype) package has pre-built .lib files for windows (debug/release). diff --git a/scripts/build_sokol_demo.ps1 b/scripts/build_sokol_demo.ps1 index e9d1ef3..2b3d504 100644 --- a/scripts/build_sokol_demo.ps1 +++ b/scripts/build_sokol_demo.ps1 @@ -67,6 +67,15 @@ push-location $path_thirdparty } pop-location +$path_stb_truetype = join-path $path_thirdparty 'stb\src' + +push-location $path_stb_truetype + $devshell = Join-Path $PSScriptRoot 'helpers/devshell.ps1' + . $devshell -arch amd64 + + & .\build.bat +pop-location + $odin_compiler_defs = join-path $PSScriptRoot 'helpers/odin_compiler_defs.ps1' . $odin_compiler_defs @@ -96,8 +105,8 @@ function build-SokolBackendDemo $build_args += $flag_thread_count + $CoreCount_Physical # $build_args += $flag_optimize_none # $build_args += $flag_optimize_minimal - # $build_args += $flag_optimize_speed - $build_args += $falg_optimize_aggressive + $build_args += $flag_optimize_speed + # $build_args += $falg_optimize_aggressive # $build_args += $flag_debug $build_args += $flag_pdb_name + $pdb $build_args += $flag_subsystem + 'windows' @@ -111,6 +120,8 @@ function build-SokolBackendDemo # $build_args += $flag_sanitize_address # $build_args += $flag_sanitize_memory + Write-Host $build_args + Invoke-WithColorCodedOutput { & $odin_compiler $build_args } } build-SokolBackendDemo diff --git a/scripts/build_sokol_demo.sh b/scripts/build_sokol_demo.sh index 0cbfbdd..e76888d 100644 --- a/scripts/build_sokol_demo.sh +++ b/scripts/build_sokol_demo.sh @@ -81,6 +81,12 @@ pushd "$path_thirdparty" > /dev/null fi popd > /dev/null +path_stb_truetype="$path_thirdparty/stb/src" + +pushd "$path_stb_truetype" > /dev/null + make +popd > /dev/null + source "$(dirname "$0")/helpers/odin_compiler_defs.sh" pkg_collection_backend="backend=$path_backend" diff --git a/scripts/clean.sh b/scripts/clean.sh index 31613f8..0d73019 100644 --- a/scripts/clean.sh +++ b/scripts/clean.sh @@ -6,4 +6,4 @@ path_scripts="$path_root/scripts" path_thirdparty="$path_root/thirdparty" if [ -d "$path_build" ]; then rm -rf "$path_build"; fi -# if [ -d "$path_thirdparty" ]; then rm -rf "$path_thirdparty"; fi \ No newline at end of file +# if [ -d "$path_thirdparty" ]; then rm -rf "$path_thirdparty"; fi diff --git a/thirdparty/stb/lib/.gitkeep b/thirdparty/stb/lib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/thirdparty/stb/lib/darwin/stb_truetype.a b/thirdparty/stb/lib/darwin/stb_truetype.a new file mode 100644 index 0000000..b55fbe5 Binary files /dev/null and b/thirdparty/stb/lib/darwin/stb_truetype.a differ diff --git a/thirdparty/stb/lib/stb_truetype.a b/thirdparty/stb/lib/stb_truetype.a new file mode 100644 index 0000000..4c94036 Binary files /dev/null and b/thirdparty/stb/lib/stb_truetype.a differ diff --git a/thirdparty/stb/lib/stb_truetype.lib b/thirdparty/stb/lib/stb_truetype.lib new file mode 100644 index 0000000..fb5a449 Binary files /dev/null and b/thirdparty/stb/lib/stb_truetype.lib differ diff --git a/thirdparty/stb/lib/stb_truetype_wasm.o b/thirdparty/stb/lib/stb_truetype_wasm.o new file mode 100644 index 0000000..e545ef8 Binary files /dev/null and b/thirdparty/stb/lib/stb_truetype_wasm.o differ diff --git a/thirdparty/stb/src/Makefile b/thirdparty/stb/src/Makefile new file mode 100644 index 0000000..597616a --- /dev/null +++ b/thirdparty/stb/src/Makefile @@ -0,0 +1,27 @@ +OS=$(shell uname) + +ifeq ($(OS), Darwin) +all: darwin +else +all: unix +endif + +$(info Current OS is: $(OS)) + +wasm: + mkdir -p ../lib + $(CC) -c -Os --target=wasm32 --sysroot=$(shell odin root)/vendor/libc stb_truetype.c -o ../lib/stb_truetype_wasm.o + +unix: + mkdir -p ../lib + $(CC) -c -O2 -Os -fPIC stb_truetype.c + $(AR) rcs ../lib/stb_truetype.a stb_truetype.o + #$(CC) -fPIC -shared -Wl,-soname=stb_truetype.so -o ../lib/stb_truetype.so stb_image_truetype.o + rm *.o + +darwin: + mkdir -p ../lib + $(CC) -arch x86_64 -c -O2 -Os -fPIC stb_truetype.c -o stb_truetype-x86_64.o -mmacosx-version-min=10.12 + $(CC) -arch arm64 -c -O2 -Os -fPIC stb_truetype.c -o stb_truetype-arm64.o -mmacosx-version-min=10.12 + lipo -create stb_truetype-x86_64.o stb_truetype-arm64.o -output ../lib/darwin/stb_truetype.a + rm *.o diff --git a/thirdparty/stb/src/build.bat b/thirdparty/stb/src/build.bat new file mode 100644 index 0000000..e83d765 --- /dev/null +++ b/thirdparty/stb/src/build.bat @@ -0,0 +1,8 @@ +@echo off + +if not exist "..\lib" mkdir ..\lib + +cl -nologo -MT -TC -O2 -c stb_image.c stb_truetype.c +lib -nologo stb_truetype.obj -out:..\lib\stb_truetype.lib + +del *.obj diff --git a/thirdparty/stb/src/stb_truetype.c b/thirdparty/stb/src/stb_truetype.c new file mode 100644 index 0000000..05c23f5 --- /dev/null +++ b/thirdparty/stb/src/stb_truetype.c @@ -0,0 +1,2 @@ +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" \ No newline at end of file diff --git a/thirdparty/stb/src/stb_truetype.h b/thirdparty/stb/src/stb_truetype.h new file mode 100644 index 0000000..6c82c29 --- /dev/null +++ b/thirdparty/stb/src/stb_truetype.h @@ -0,0 +1,5114 @@ +// stb_truetype.h - v1.26 - public domain +// authored from 2009-2021 by Sean Barrett / RAD Game Tools +// +// ======================================================================= +// +// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES +// +// This library does no range checking of the offsets found in the file, +// meaning an attacker can use it to read arbitrary memory. +// +// ======================================================================= +// +// This library processes TrueType files: +// parse files +// extract glyph metrics +// extract glyph shapes +// render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) +// +// Todo: +// non-MS cmaps +// crashproof on bad data +// hinting? (no longer patented) +// cleartype-style AA? +// optimize: use simple memory allocator for intermediates +// optimize: build edge-list directly from curves +// optimize: rasterize directly from curves? +// +// ADDITIONAL CONTRIBUTORS +// +// Mikko Mononen: compound shape support, more cmap formats +// Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// Daniel Ribeiro Maciel: basic GPOS-based kerning +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya +// Daniel Ribeiro Maciel +// +// Bug/warning reports/fixes: +// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe +// Cass Everitt Martins Mozeiko github:aloucks +// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam +// Brian Hook Omar Cornut github:vassvik +// Walter van Niftrik Ryan Griege +// David Gow Peter LaValle +// David Given Sergey Popov +// Ivan-Assen Ivanov Giumo X. Clanjor +// Anthony Pesch Higor Euripedes +// Johan Duparc Thomas Fields +// Hou Qiming Derek Vinyard +// Rob Loach Cort Stratton +// Kenney Phillis Jr. Brian Costabile +// Ken Voskuil (kaesve) +// +// VERSION HISTORY +// +// 1.26 (2021-08-28) fix broken rasterizer +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// variant PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// +// Full history can be found at the end of this file. +// +// LICENSE +// +// See end of file for license information. +// +// USAGE +// +// Include this file in whatever places need to refer to it. In ONE C/C++ +// file, write: +// #define STB_TRUETYPE_IMPLEMENTATION +// before the #include of this file. This expands out the actual +// implementation into that C/C++ file. +// +// To make the implementation private to the file that generates the implementation, +// #define STBTT_STATIC +// +// Simple 3D API (don't ship this, but it's fine for tools and quick start) +// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture +// stbtt_GetBakedQuad() -- compute quad to draw for a given char +// +// Improved 3D API (more shippable): +// #include "stb_rect_pack.h" -- optional, but you really want it +// stbtt_PackBegin() +// stbtt_PackSetOversampling() -- for improved quality on small fonts +// stbtt_PackFontRanges() -- pack and renders +// stbtt_PackEnd() +// stbtt_GetPackedQuad() +// +// "Load" a font file from a memory buffer (you have to keep the buffer loaded) +// stbtt_InitFont() +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections +// +// Render a unicode codepoint to a bitmap +// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap +// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide +// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be +// +// Character advance/positioning +// stbtt_GetCodepointHMetrics() +// stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() +// stbtt_GetCodepointKernAdvance() +// +// Starting with version 1.06, the rasterizer was replaced with a new, +// faster and generally-more-precise rasterizer. The new rasterizer more +// accurately measures pixel coverage for anti-aliasing, except in the case +// where multiple shapes overlap, in which case it overestimates the AA pixel +// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If +// this turns out to be a problem, you can re-enable the old rasterizer with +// #define STBTT_RASTERIZER_VERSION 1 +// which will incur about a 15% speed hit. +// +// ADDITIONAL DOCUMENTATION +// +// Immediately after this block comment are a series of sample programs. +// +// After the sample programs is the "header file" section. This section +// includes documentation for each API function. +// +// Some important concepts to understand to use this library: +// +// Codepoint +// Characters are defined by unicode codepoints, e.g. 65 is +// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is +// the hiragana for "ma". +// +// Glyph +// A visual character shape (every codepoint is rendered as +// some glyph) +// +// Glyph index +// A font-specific integer ID representing a glyph +// +// Baseline +// Glyph shapes are defined relative to a baseline, which is the +// bottom of uppercase characters. Characters extend both above +// and below the baseline. +// +// Current Point +// As you draw text to the screen, you keep track of a "current point" +// which is the origin of each character. The current point's vertical +// position is the baseline. Even "baked fonts" use this model. +// +// Vertical Font Metrics +// The vertical qualities of the font, used to vertically position +// and space the characters. See docs for stbtt_GetFontVMetrics. +// +// Font Size in Pixels or Points +// The preferred interface for specifying font sizes in stb_truetype +// is to specify how tall the font's vertical extent should be in pixels. +// If that sounds good enough, skip the next paragraph. +// +// Most font APIs instead use "points", which are a common typographic +// measurement for describing font size, defined as 72 points per inch. +// stb_truetype provides a point API for compatibility. However, true +// "per inch" conventions don't make much sense on computer displays +// since different monitors have different number of pixels per +// inch. For example, Windows traditionally uses a convention that +// there are 96 pixels per inch, thus making 'inch' measurements have +// nothing to do with inches, and thus effectively defining a point to +// be 1.333 pixels. Additionally, the TrueType font data provides +// an explicit scale factor to scale a given font's glyphs to points, +// but the author has observed that this scale factor is often wrong +// for non-commercial fonts, thus making fonts scaled in points +// according to the TrueType spec incoherently sized in practice. +// +// DETAILED USAGE: +// +// Scale: +// Select how high you want the font to be, in points or pixels. +// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute +// a scale factor SF that will be used by all other functions. +// +// Baseline: +// You need to select a y-coordinate that is the baseline of where +// your text will appear. Call GetFontBoundingBox to get the baseline-relative +// bounding box for all characters. SF*-y0 will be the distance in pixels +// that the worst-case character could extend above the baseline, so if +// you want the top edge of characters to appear at the top of the +// screen where y=0, then you would set the baseline to SF*-y0. +// +// Current point: +// Set the current point where the first character will appear. The +// first character could extend left of the current point; this is font +// dependent. You can either choose a current point that is the leftmost +// point and hope, or add some padding, or check the bounding box or +// left-side-bearing of the first character to be displayed and set +// the current point based on that. +// +// Displaying a character: +// Compute the bounding box of the character. It will contain signed values +// relative to . I.e. if it returns x0,y0,x1,y1, +// then the character should be displayed in the rectangle from +// to = 32 && *text < 128) { + stbtt_aligned_quad q; + stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 + glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); + glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); + glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); + glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); + } + ++text; + } + glEnd(); +} +#endif +// +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program (this compiles): get a single bitmap, print as ASCII art +// +#if 0 +#include +#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation +#include "stb_truetype.h" + +char ttf_buffer[1<<25]; + +int main(int argc, char **argv) +{ + stbtt_fontinfo font; + unsigned char *bitmap; + int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); + + fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); + + stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); + bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); + + for (j=0; j < h; ++j) { + for (i=0; i < w; ++i) + putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); + putchar('\n'); + } + return 0; +} +#endif +// +// Output: +// +// .ii. +// @@@@@@. +// V@Mio@@o +// :i. V@V +// :oM@@M +// :@@@MM@M +// @@o o@M +// :@@. M@M +// @@@o@@@@ +// :M@@V:@@. +// +////////////////////////////////////////////////////////////////////////////// +// +// Complete program: print "Hello World!" banner, with bugs +// +#if 0 +char buffer[24<<20]; +unsigned char screen[20][79]; + +int main(int arg, char **argv) +{ + stbtt_fontinfo font; + int i,j,ascent,baseline,ch=0; + float scale, xpos=2; // leave a little padding in case the character extends left + char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness + + fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); + stbtt_InitFont(&font, buffer, 0); + + scale = stbtt_ScaleForPixelHeight(&font, 15); + stbtt_GetFontVMetrics(&font, &ascent,0,0); + baseline = (int) (ascent*scale); + + while (text[ch]) { + int advance,lsb,x0,y0,x1,y1; + float x_shift = xpos - (float) floor(xpos); + stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); + stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); + stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); + // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong + // because this API is really for baking character bitmaps into textures. if you want to render + // a sequence of characters, you really need to render each bitmap to a temp buffer, then + // "alpha blend" that into the working buffer + xpos += (advance * scale); + if (text[ch+1]) + xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); + ++ch; + } + + for (j=0; j < 20; ++j) { + for (i=0; i < 78; ++i) + putchar(" .:ioVM@"[screen[j][i]>>5]); + putchar('\n'); + } + + return 0; +} +#endif + +#pragma region ODIN: CUSTOM ALLOCATOR + +#ifdef STB_TRUETYPE_IMPLEMENTATION +#define ZPL_IMPLEMENTATION +#endif +#include "zpl/zpl.h" + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +STBTT_DEF void stbtt_SetAllocator( zpl_allocator allocator ); + +#ifdef __cplusplus +} +#endif + +#ifndef STBTT_malloc +#define STBTT_malloc(x,u) ((void)(u), zpl_alloc(stbtt__allocator, x)) +#define STBTT_free(x,u) ((void)(u), zpl_free(stbtt__allocator, x)) +#endif + +#ifdef STB_TRUETYPE_IMPLEMENTATION +zpl_global zpl_allocator stbtt__allocator = { zpl_heap_allocator_proc, NULL }; + +STBTT_DEF void stbtt_SetAllocator( zpl_allocator allocator ) { + stbtt__allocator = allocator; +} +#endif + +#pragma endregion ODIN: CUSTOM ALLOCATOR + +////////////////////////////////////////////////////////////////////////////// +////////////////////////////////////////////////////////////////////////////// +//// +//// INTEGRATION WITH YOUR CODEBASE +//// +//// The following sections allow you to supply alternate definitions +//// of C library functions used by stb_truetype, e.g. if you don't +//// link with the C runtime library. + +#ifdef STB_TRUETYPE_IMPLEMENTATION + // #define your own (u)stbtt_int8/16/32 before including to override this + #ifndef stbtt_uint8 + typedef unsigned char stbtt_uint8; + typedef signed char stbtt_int8; + typedef unsigned short stbtt_uint16; + typedef signed short stbtt_int16; + typedef unsigned int stbtt_uint32; + typedef signed int stbtt_int32; + #endif + + typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; + typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; + + // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h + #ifndef STBTT_ifloor + #include + #define STBTT_ifloor(x) ((int) floor(x)) + #define STBTT_iceil(x) ((int) ceil(x)) + #endif + + #ifndef STBTT_sqrt + #include + #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_fmod + #include + #define STBTT_fmod(x,y) fmod(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h + #ifndef STBTT_malloc + #include + #define STBTT_malloc(x,u) ((void)(u),malloc(x)) + #define STBTT_free(x,u) ((void)(u),free(x)) + #endif + + #ifndef STBTT_assert + #include + #define STBTT_assert(x) assert(x) + #endif + + #ifndef STBTT_strlen + #include + #define STBTT_strlen(x) strlen(x) + #endif + + #ifndef STBTT_memcpy + #include + #define STBTT_memcpy memcpy + #define STBTT_memset memset + #endif +#endif + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#ifndef __STB_INCLUDE_STB_TRUETYPE_H__ +#define __STB_INCLUDE_STB_TRUETYPE_H__ + +#ifdef STBTT_STATIC +#define STBTT_DEF static +#else +#define STBTT_DEF extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + +////////////////////////////////////////////////////////////////////////////// +// +// TEXTURE BAKING API +// +// If you use this API, you only have to call two functions ever. +// + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; +} stbtt_bakedchar; + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata); // you allocate this, it's num_chars long +// 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. + +typedef struct +{ + float x0,y0,s0,t0; // top-left + float x1,y1,s1,t1; // bottom-right +} stbtt_aligned_quad; + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int opengl_fillrule); // true if opengl fill rule; false if DX9 or earlier +// 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. + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); +// Query the font vertical metrics without having to create a font first. + + +////////////////////////////////////////////////////////////////////////////// +// +// NEW TEXTURE BAKING API +// +// This provides options for packing multiple fonts into one atlas, not +// perfectly but better than nothing. + +typedef struct +{ + unsigned short x0,y0,x1,y1; // coordinates of bbox in bitmap + float xoff,yoff,xadvance; + float xoff2,yoff2; +} stbtt_packedchar; + +typedef struct stbtt_pack_context stbtt_pack_context; +typedef struct stbtt_fontinfo stbtt_fontinfo; +#ifndef STB_RECT_PACK_VERSION +typedef struct stbrp_rect stbrp_rect; +#endif + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); +// 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. + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); +// Cleans up the packing context and frees all memory. + +#define STBTT_POINT_SIZE(x) (-(x)) + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); +// 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 STBTT_POINT_SIZE() +// and pass that result as 'font_size': +// ..., 20 , ... // font max minus min y is 20 pixels tall +// ..., STBTT_POINT_SIZE(20), ... // 'M' is 20 pixels tall + +typedef struct +{ + float font_size; + int first_unicode_codepoint_in_range; // if non-zero, then the chars are continuous, and this is the first codepoint + int *array_of_unicode_codepoints; // if non-zero, then this is an array of unicode codepoints + int num_chars; + stbtt_packedchar *chardata_for_range; // output + unsigned char h_oversample, v_oversample; // don't set these, they're used internally +} stbtt_pack_range; + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +// 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. + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample); +// 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. + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); +// If skip != 0, this tells stb_truetype to skip any codepoints for which +// there is no corresponding glyph. If skip=0, which is the default, then +// codepoints without a glyph recived the font's "missing character" glyph, +// typically an empty box by convention. + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above + int char_index, // character to display + float *xpos, float *ypos, // pointers to current position in screen pixel space + stbtt_aligned_quad *q, // output: quad to draw + int align_to_integer); + +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +// 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). + +// this is an opaque structure that you shouldn't mess with which holds +// all the context needed from PackBegin to PackEnd. +struct stbtt_pack_context { + void *user_allocator_context; + void *pack_info; + int width; + int height; + int stride_in_bytes; + int padding; + int skip_missing; + unsigned int h_oversample, v_oversample; + unsigned char *pixels; + void *nodes; +}; + +////////////////////////////////////////////////////////////////////////////// +// +// FONT LOADING +// +// + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// 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. + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); +// 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. + +// The following structure is defined publicly so you can declare one on +// the stack or as a global or etc, but you should treat it as opaque. +struct stbtt_fontinfo +{ + void * userdata; + unsigned char * data; // pointer to .ttf file + int fontstart; // offset of start of font + + int numGlyphs; // number of glyphs, needed for range checking + + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf + int index_map; // a cmap mapping for our chosen character encoding + int indexToLocFormat; // format needed to map from glyph index to glyph + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict +}; + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); +// 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. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER TO GLYPH-INDEX CONVERSIOn + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint); +// 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. + + +////////////////////////////////////////////////////////////////////////////// +// +// CHARACTER PROPERTIES +// + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float pixels); +// 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. + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels); +// 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. + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap); +// 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 + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +// 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. + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); +// the bounding box around all possible characters + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing); +// 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 + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2); +// an additional amount to add to the 'advance' value between ch1 and ch2 + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1); +// Gets the bounding box of the visible part of the glyph, in unscaled coordinates + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing); +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2); +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); +// as above, but takes one or more glyph indices for greater efficiency + +typedef struct stbtt_kerningentry +{ + int glyph1; // use stbtt_FindGlyphIndex + int glyph2; + int advance; +} stbtt_kerningentry; + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); +// 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) + +////////////////////////////////////////////////////////////////////////////// +// +// GLYPH SHAPES (you probably don't need these, but they have to go before +// the bitmaps for C declaration-order reasons) +// + +#ifndef STBTT_vmove // you can predefine these to use different values (but why?) + enum { + STBTT_vmove=1, + STBTT_vline, + STBTT_vcurve, + STBTT_vcubic + }; +#endif + +#ifndef stbtt_vertex // you can predefine this to use different values + // (we share this with other code at RAD) + #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file + typedef struct + { + stbtt_vertex_type x,y,cx,cy,cx1,cy1; + unsigned char type,padding; + } stbtt_vertex; +#endif + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index); +// returns non-zero if nothing is drawn for this glyph + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices); +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **vertices); +// 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. + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); +// frees the data allocated above + +STBTT_DEF unsigned char *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl); +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); +// fills svg with the character's SVG data. +// returns data size or 0 if SVG not found. + +////////////////////////////////////////////////////////////////////////////// +// +// BITMAP RENDERING +// + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata); +// frees the bitmap allocated below + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// 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 + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff); +// the same as stbtt_GetCodepoitnBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint); +// 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. + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint); +// same as stbtt_MakeCodepointBitmap, but you can specify a subpixel +// shift for the character + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +// 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.) + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); +// same as stbtt_GetCodepointBitmapBox, but you can specify a subpixel +// shift for the character + +// the following functions are equivalent to the above functions, but operate +// on glyph indices instead of Unicode codepoints (for efficiency) +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); + + +// @TODO: don't expose this structure +typedef struct +{ + int w,h,stride; + unsigned char *pixels; +} stbtt__bitmap; + +// rasterize a shape with quadratic beziers into a bitmap +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap to draw into + float flatness_in_pixels, // allowable error of curve in pixels + stbtt_vertex *vertices, // array of vertices defining shape + int num_verts, // number of vertices in above array + float scale_x, float scale_y, // scale applied to input vertices + float shift_x, float shift_y, // translation applied to input vertices + int x_off, int y_off, // another translation applied to input + int invert, // if non-zero, vertically flip shape + void *userdata); // context for to STBTT_MALLOC + +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// 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. + + + +////////////////////////////////////////////////////////////////////////////// +// +// 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. + + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags); +// 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 +#define STBTT_MACSTYLE_DONTCARE 0 +#define STBTT_MACSTYLE_BOLD 1 +#define STBTT_MACSTYLE_ITALIC 2 +#define STBTT_MACSTYLE_UNDERSCORE 4 +#define STBTT_MACSTYLE_NONE 8 // <= not same as 0, this makes us check the bitfield is 0 + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2); +// 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 + +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID); +// 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 + +enum { // platformID + STBTT_PLATFORM_ID_UNICODE =0, + STBTT_PLATFORM_ID_MAC =1, + STBTT_PLATFORM_ID_ISO =2, + STBTT_PLATFORM_ID_MICROSOFT =3 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_UNICODE + STBTT_UNICODE_EID_UNICODE_1_0 =0, + STBTT_UNICODE_EID_UNICODE_1_1 =1, + STBTT_UNICODE_EID_ISO_10646 =2, + STBTT_UNICODE_EID_UNICODE_2_0_BMP=3, + STBTT_UNICODE_EID_UNICODE_2_0_FULL=4 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MICROSOFT + STBTT_MS_EID_SYMBOL =0, + STBTT_MS_EID_UNICODE_BMP =1, + STBTT_MS_EID_SHIFTJIS =2, + STBTT_MS_EID_UNICODE_FULL =10 +}; + +enum { // encodingID for STBTT_PLATFORM_ID_MAC; same as Script Manager codes + STBTT_MAC_EID_ROMAN =0, STBTT_MAC_EID_ARABIC =4, + STBTT_MAC_EID_JAPANESE =1, STBTT_MAC_EID_HEBREW =5, + STBTT_MAC_EID_CHINESE_TRAD =2, STBTT_MAC_EID_GREEK =6, + STBTT_MAC_EID_KOREAN =3, STBTT_MAC_EID_RUSSIAN =7 +}; + +enum { // languageID for STBTT_PLATFORM_ID_MICROSOFT; same as LCID... + // problematic because there are e.g. 16 english LCIDs and 16 arabic LCIDs + STBTT_MS_LANG_ENGLISH =0x0409, STBTT_MS_LANG_ITALIAN =0x0410, + STBTT_MS_LANG_CHINESE =0x0804, STBTT_MS_LANG_JAPANESE =0x0411, + STBTT_MS_LANG_DUTCH =0x0413, STBTT_MS_LANG_KOREAN =0x0412, + STBTT_MS_LANG_FRENCH =0x040c, STBTT_MS_LANG_RUSSIAN =0x0419, + STBTT_MS_LANG_GERMAN =0x0407, STBTT_MS_LANG_SPANISH =0x0409, + STBTT_MS_LANG_HEBREW =0x040d, STBTT_MS_LANG_SWEDISH =0x041D +}; + +enum { // languageID for STBTT_PLATFORM_ID_MAC + STBTT_MAC_LANG_ENGLISH =0 , STBTT_MAC_LANG_JAPANESE =11, + STBTT_MAC_LANG_ARABIC =12, STBTT_MAC_LANG_KOREAN =23, + STBTT_MAC_LANG_DUTCH =4 , STBTT_MAC_LANG_RUSSIAN =32, + STBTT_MAC_LANG_FRENCH =1 , STBTT_MAC_LANG_SPANISH =6 , + STBTT_MAC_LANG_GERMAN =2 , STBTT_MAC_LANG_SWEDISH =5 , + STBTT_MAC_LANG_HEBREW =10, STBTT_MAC_LANG_CHINESE_SIMPLIFIED =33, + STBTT_MAC_LANG_ITALIAN =3 , STBTT_MAC_LANG_CHINESE_TRAD =19 +}; + +#ifdef __cplusplus +} +#endif + +#endif // __STB_INCLUDE_STB_TRUETYPE_H__ + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// IMPLEMENTATION +//// +//// + +#ifdef STB_TRUETYPE_IMPLEMENTATION + +#ifndef STBTT_MAX_OVERSAMPLE +#define STBTT_MAX_OVERSAMPLE 8 +#endif + +#if STBTT_MAX_OVERSAMPLE > 255 +#error "STBTT_MAX_OVERSAMPLE cannot be > 255" +#endif + +typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERSAMPLE-1)) == 0 ? 1 : -1]; + +#ifndef STBTT_RASTERIZER_VERSION +#define STBTT_RASTERIZER_VERSION 2 +#endif + +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + +////////////////////////////////////////////////////////////////////////// +// +// accessors to parse data from file +// + +// on platforms that don't allow misaligned reads, if we want to allow +// truetype fonts that aren't padded to alignment, define ALLOW_UNALIGNED_TRUETYPE + +#define ttBYTE(p) (* (stbtt_uint8 *) (p)) +#define ttCHAR(p) (* (stbtt_int8 *) (p)) +#define ttFixed(p) ttLONG(p) + +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } + +#define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) +#define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) + +static int stbtt__isfont(stbtt_uint8 *font) +{ + // check the version number + if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 + if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! + if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF + if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts + return 0; +} + +// @OPTIMIZE: binary search +static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, const char *tag) +{ + stbtt_int32 num_tables = ttUSHORT(data+fontstart+4); + stbtt_uint32 tabledir = fontstart + 12; + stbtt_int32 i; + for (i=0; i < num_tables; ++i) { + stbtt_uint32 loc = tabledir + 16*i; + if (stbtt_tag(data+loc+0, tag)) + return ttULONG(data+loc+8); + } + return 0; +} + +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) +{ + // if it's just a font, there's only one valid index + if (stbtt__isfont(font_collection)) + return index == 0 ? 0 : -1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + stbtt_int32 n = ttLONG(font_collection+8); + if (index >= n) + return -1; + return ttULONG(font_collection+12+index*4); + } + } + return -1; +} + +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +// since most people won't use this, find this table the first time it's needed +static int stbtt__get_svg(stbtt_fontinfo *info) +{ + stbtt_uint32 t; + if (info->svg < 0) { + t = stbtt__find_table(info->data, info->fontstart, "SVG "); + if (t) { + stbtt_uint32 offset = ttULONG(info->data + t + 2); + info->svg = t + offset; + } else { + info->svg = 0; + } + } + return info->svg; +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) +{ + stbtt_uint32 cmap, t; + stbtt_int32 i,numTables; + + info->data = data; + info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); + + cmap = stbtt__find_table(data, fontstart, "cmap"); // required + info->loca = stbtt__find_table(data, fontstart, "loca"); // required + info->head = stbtt__find_table(data, fontstart, "head"); // required + info->glyf = stbtt__find_table(data, fontstart, "glyf"); // required + info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required + info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required + info->kern = stbtt__find_table(data, fontstart, "kern"); // not required + info->gpos = stbtt__find_table(data, fontstart, "GPOS"); // not required + + if (!cmap || !info->head || !info->hhea || !info->hmtx) + return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } + + t = stbtt__find_table(data, fontstart, "maxp"); + if (t) + info->numGlyphs = ttUSHORT(data+t+4); + else + info->numGlyphs = 0xffff; + + info->svg = -1; + + // find a cmap encoding table we understand *now* to avoid searching + // later. (todo: could make this installable) + // the same regardless of glyph. + numTables = ttUSHORT(data + cmap + 2); + info->index_map = 0; + for (i=0; i < numTables; ++i) { + stbtt_uint32 encoding_record = cmap + 4 + 8 * i; + // find an encoding we understand: + switch(ttUSHORT(data+encoding_record)) { + case STBTT_PLATFORM_ID_MICROSOFT: + switch (ttUSHORT(data+encoding_record+2)) { + case STBTT_MS_EID_UNICODE_BMP: + case STBTT_MS_EID_UNICODE_FULL: + // MS/Unicode + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + break; + case STBTT_PLATFORM_ID_UNICODE: + // Mac/iOS has these + // all the encodingIDs are unicode, so we don't bother to check it + info->index_map = cmap + ttULONG(data+encoding_record+4); + break; + } + } + if (info->index_map == 0) + return 0; + + info->indexToLocFormat = ttUSHORT(data+info->head + 50); + return 1; +} + +STBTT_DEF int stbtt_FindGlyphIndex(const stbtt_fontinfo *info, int unicode_codepoint) +{ + stbtt_uint8 *data = info->data; + stbtt_uint32 index_map = info->index_map; + + stbtt_uint16 format = ttUSHORT(data + index_map + 0); + if (format == 0) { // apple byte encoding + stbtt_int32 bytes = ttUSHORT(data + index_map + 2); + if (unicode_codepoint < bytes-6) + return ttBYTE(data + index_map + 6 + unicode_codepoint); + return 0; + } else if (format == 6) { + stbtt_uint32 first = ttUSHORT(data + index_map + 6); + stbtt_uint32 count = ttUSHORT(data + index_map + 8); + if ((stbtt_uint32) unicode_codepoint >= first && (stbtt_uint32) unicode_codepoint < first+count) + return ttUSHORT(data + index_map + 10 + (unicode_codepoint - first)*2); + return 0; + } else if (format == 2) { + STBTT_assert(0); // @TODO: high-byte mapping for japanese/chinese/korean + return 0; + } else if (format == 4) { // standard mapping for windows fonts: binary search collection of ranges + stbtt_uint16 segcount = ttUSHORT(data+index_map+6) >> 1; + stbtt_uint16 searchRange = ttUSHORT(data+index_map+8) >> 1; + stbtt_uint16 entrySelector = ttUSHORT(data+index_map+10); + stbtt_uint16 rangeShift = ttUSHORT(data+index_map+12) >> 1; + + // do a binary search of the segments + stbtt_uint32 endCount = index_map + 14; + stbtt_uint32 search = endCount; + + if (unicode_codepoint > 0xffff) + return 0; + + // they lie from endCount .. endCount + segCount + // but searchRange is the nearest power of two, so... + if (unicode_codepoint >= ttUSHORT(data + search + rangeShift*2)) + search += rangeShift*2; + + // now decrement to bias correctly to find smallest + search -= 2; + while (entrySelector) { + stbtt_uint16 end; + searchRange >>= 1; + end = ttUSHORT(data + search + searchRange*2); + if (unicode_codepoint > end) + search += searchRange*2; + --entrySelector; + } + search += 2; + + { + stbtt_uint16 offset, start, last; + stbtt_uint16 item = (stbtt_uint16) ((search - endCount) >> 1); + + start = ttUSHORT(data + index_map + 14 + segcount*2 + 2 + 2*item); + last = ttUSHORT(data + endCount + 2*item); + if (unicode_codepoint < start || unicode_codepoint > last) + return 0; + + offset = ttUSHORT(data + index_map + 14 + segcount*6 + 2 + 2*item); + if (offset == 0) + return (stbtt_uint16) (unicode_codepoint + ttSHORT(data + index_map + 14 + segcount*4 + 2 + 2*item)); + + return ttUSHORT(data + offset + (unicode_codepoint-start)*2 + index_map + 14 + segcount*6 + 2 + 2*item); + } + } else if (format == 12 || format == 13) { + stbtt_uint32 ngroups = ttULONG(data+index_map+12); + stbtt_int32 low,high; + low = 0; high = (stbtt_int32)ngroups; + // Binary search the right group. + while (low < high) { + stbtt_int32 mid = low + ((high-low) >> 1); // rounds down, so low <= mid < high + stbtt_uint32 start_char = ttULONG(data+index_map+16+mid*12); + stbtt_uint32 end_char = ttULONG(data+index_map+16+mid*12+4); + if ((stbtt_uint32) unicode_codepoint < start_char) + high = mid; + else if ((stbtt_uint32) unicode_codepoint > end_char) + low = mid+1; + else { + stbtt_uint32 start_glyph = ttULONG(data+index_map+16+mid*12+8); + if (format == 12) + return start_glyph + unicode_codepoint-start_char; + else // format == 13 + return start_glyph; + } + } + return 0; // not found + } + // @TODO + STBTT_assert(0); + return 0; +} + +STBTT_DEF int stbtt_GetCodepointShape(const stbtt_fontinfo *info, int unicode_codepoint, stbtt_vertex **vertices) +{ + return stbtt_GetGlyphShape(info, stbtt_FindGlyphIndex(info, unicode_codepoint), vertices); +} + +static void stbtt_setvertex(stbtt_vertex *v, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy) +{ + v->type = type; + v->x = (stbtt_int16) x; + v->y = (stbtt_int16) y; + v->cx = (stbtt_int16) cx; + v->cy = (stbtt_int16) cy; +} + +static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) +{ + int g1,g2; + + STBTT_assert(!info->cff.size); + + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range + if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format + + if (info->indexToLocFormat == 0) { + g1 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2) * 2; + g2 = info->glyf + ttUSHORT(info->data + info->loca + glyph_index * 2 + 2) * 2; + } else { + g1 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4); + g2 = info->glyf + ttULONG (info->data + info->loca + glyph_index * 4 + 4); + } + + return g1==g2 ? -1 : g1; // if length is 0, return -1 +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + +STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; + + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } + return 1; +} + +STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, int *x0, int *y0, int *x1, int *y1) +{ + return stbtt_GetGlyphBox(info, stbtt_FindGlyphIndex(info,codepoint), x0,y0,x1,y1); +} + +STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt_int16 numberOfContours; + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 1; + numberOfContours = ttSHORT(info->data + g); + return numberOfContours == 0; +} + +static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_off, int start_off, + stbtt_int32 sx, stbtt_int32 sy, stbtt_int32 scx, stbtt_int32 scy, stbtt_int32 cx, stbtt_int32 cy) +{ + if (start_off) { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+scx)>>1, (cy+scy)>>1, cx,cy); + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, sx,sy,scx,scy); + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve,sx,sy,cx,cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline,sx,sy,0,0); + } + return num_vertices; +} + +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + stbtt_int16 numberOfContours; + stbtt_uint8 *endPtsOfContours; + stbtt_uint8 *data = info->data; + stbtt_vertex *vertices=0; + int num_vertices=0; + int g = stbtt__GetGlyfOffset(info, glyph_index); + + *pvertices = NULL; + + if (g < 0) return 0; + + numberOfContours = ttSHORT(data + g); + + if (numberOfContours > 0) { + stbtt_uint8 flags=0,flagcount; + stbtt_int32 ins, i,j=0,m,n, next_move, was_off=0, off, start_off=0; + stbtt_int32 x,y,cx,cy,sx,sy, scx,scy; + stbtt_uint8 *points; + endPtsOfContours = (data + g + 10); + ins = ttUSHORT(data + g + 10 + numberOfContours * 2); + points = data + g + 10 + numberOfContours * 2 + 2 + ins; + + n = 1+ttUSHORT(endPtsOfContours + numberOfContours*2-2); + + m = n + 2*numberOfContours; // a loose bound on how many vertices we might need + vertices = (stbtt_vertex *) STBTT_malloc(m * sizeof(vertices[0]), info->userdata); + if (vertices == 0) + return 0; + + next_move = 0; + flagcount=0; + + // in first pass, we load uninterpreted data into the allocated array + // above, shifted to the end of the array so we won't overwrite it when + // we create our final data starting from the front + + off = m - n; // starting offset for uninterpreted data, regardless of how m ends up being calculated + + // first load flags + + for (i=0; i < n; ++i) { + if (flagcount == 0) { + flags = *points++; + if (flags & 8) + flagcount = *points++; + } else + --flagcount; + vertices[off+i].type = flags; + } + + // now load x coordinates + x=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 2) { + stbtt_int16 dx = *points++; + x += (flags & 16) ? dx : -dx; // ??? + } else { + if (!(flags & 16)) { + x = x + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].x = (stbtt_int16) x; + } + + // now load y coordinates + y=0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + if (flags & 4) { + stbtt_int16 dy = *points++; + y += (flags & 32) ? dy : -dy; // ??? + } else { + if (!(flags & 32)) { + y = y + (stbtt_int16) (points[0]*256 + points[1]); + points += 2; + } + } + vertices[off+i].y = (stbtt_int16) y; + } + + // now convert them to our format + num_vertices=0; + sx = sy = cx = cy = scx = scy = 0; + for (i=0; i < n; ++i) { + flags = vertices[off+i].type; + x = (stbtt_int16) vertices[off+i].x; + y = (stbtt_int16) vertices[off+i].y; + + if (next_move == i) { + if (i != 0) + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + + // now start the new one + start_off = !(flags & 1); + if (start_off) { + // if we start off with an off-curve point, then when we need to find a point on the curve + // where we can start, and we need to save some state for when we wraparound. + scx = x; + scy = y; + if (!(vertices[off+i+1].type & 1)) { + // next point is also a curve point, so interpolate an on-point curve + sx = (x + (stbtt_int32) vertices[off+i+1].x) >> 1; + sy = (y + (stbtt_int32) vertices[off+i+1].y) >> 1; + } else { + // otherwise just use the next point as our start point + sx = (stbtt_int32) vertices[off+i+1].x; + sy = (stbtt_int32) vertices[off+i+1].y; + ++i; // we're using point i+1 as the starting point, so skip it + } + } else { + sx = x; + sy = y; + } + stbtt_setvertex(&vertices[num_vertices++], STBTT_vmove,sx,sy,0,0); + was_off = 0; + next_move = 1 + ttUSHORT(endPtsOfContours+j*2); + ++j; + } else { + if (!(flags & 1)) { // if it's a curve + if (was_off) // two off-curve control points in a row means interpolate an on-curve midpoint + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, (cx+x)>>1, (cy+y)>>1, cx, cy); + cx = x; + cy = y; + was_off = 1; + } else { + if (was_off) + stbtt_setvertex(&vertices[num_vertices++], STBTT_vcurve, x,y, cx, cy); + else + stbtt_setvertex(&vertices[num_vertices++], STBTT_vline, x,y,0,0); + was_off = 0; + } + } + } + num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); + } else if (numberOfContours < 0) { + // Compound shapes. + int more = 1; + stbtt_uint8 *comp = data + g + 10; + num_vertices = 0; + vertices = 0; + while (more) { + stbtt_uint16 flags, gidx; + int comp_num_verts = 0, i; + stbtt_vertex *comp_verts = 0, *tmp = 0; + float mtx[6] = {1,0,0,1,0,0}, m, n; + + flags = ttSHORT(comp); comp+=2; + gidx = ttSHORT(comp); comp+=2; + + if (flags & 2) { // XY values + if (flags & 1) { // shorts + mtx[4] = ttSHORT(comp); comp+=2; + mtx[5] = ttSHORT(comp); comp+=2; + } else { + mtx[4] = ttCHAR(comp); comp+=1; + mtx[5] = ttCHAR(comp); comp+=1; + } + } + else { + // @TODO handle matching point + STBTT_assert(0); + } + if (flags & (1<<3)) { // WE_HAVE_A_SCALE + mtx[0] = mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + } else if (flags & (1<<6)) { // WE_HAVE_AN_X_AND_YSCALE + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = mtx[2] = 0; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } else if (flags & (1<<7)) { // WE_HAVE_A_TWO_BY_TWO + mtx[0] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[1] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; + mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; + } + + // Find transformation scales. + m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); + n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); + + // Get indexed glyph. + comp_num_verts = stbtt_GetGlyphShape(info, gidx, &comp_verts); + if (comp_num_verts > 0) { + // Transform vertices. + for (i = 0; i < comp_num_verts; ++i) { + stbtt_vertex* v = &comp_verts[i]; + stbtt_vertex_type x,y; + x=v->x; y=v->y; + v->x = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->y = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + x=v->cx; y=v->cy; + v->cx = (stbtt_vertex_type)(m * (mtx[0]*x + mtx[2]*y + mtx[4])); + v->cy = (stbtt_vertex_type)(n * (mtx[1]*x + mtx[3]*y + mtx[5])); + } + // Append vertices. + tmp = (stbtt_vertex*)STBTT_malloc((num_vertices+comp_num_verts)*sizeof(stbtt_vertex), info->userdata); + if (!tmp) { + if (vertices) STBTT_free(vertices, info->userdata); + if (comp_verts) STBTT_free(comp_verts, info->userdata); + return 0; + } + if (num_vertices > 0 && vertices) STBTT_memcpy(tmp, vertices, num_vertices*sizeof(stbtt_vertex)); + STBTT_memcpy(tmp+num_vertices, comp_verts, comp_num_verts*sizeof(stbtt_vertex)); + if (vertices) STBTT_free(vertices, info->userdata); + vertices = tmp; + STBTT_free(comp_verts, info->userdata); + num_vertices += comp_num_verts; + } + // More components ? + more = flags & (1<<5); + } + } else { + // numberOfCounters == 0, do nothing + } + + *pvertices = vertices; + return num_vertices; +} + +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // FALLTHROUGH + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && b0 < 32) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)(stbtt_int32)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) *x0 = r ? c.min_x : 0; + if (y0) *y0 = r ? c.min_y : 0; + if (x1) *x1 = r ? c.max_x : 0; + if (y1) *y1 = r ? c.max_y : 0; + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + +STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) +{ + stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); + if (glyph_index < numOfLongHorMetrics) { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*glyph_index); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*glyph_index + 2); + } else { + if (advanceWidth) *advanceWidth = ttSHORT(info->data + info->hmtx + 4*(numOfLongHorMetrics-1)); + if (leftSideBearing) *leftSideBearing = ttSHORT(info->data + info->hmtx + 4*numOfLongHorMetrics + 2*(glyph_index - numOfLongHorMetrics)); + } +} + +STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) +{ + stbtt_uint8 *data = info->data + info->kern; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + return ttUSHORT(data+10); +} + +STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) +{ + stbtt_uint8 *data = info->data + info->kern; + int k, length; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + length = ttUSHORT(data+10); + if (table_length < length) + length = table_length; + + for (k = 0; k < length; k++) + { + table[k].glyph1 = ttUSHORT(data+18+(k*6)); + table[k].glyph2 = ttUSHORT(data+20+(k*6)); + table[k].advance = ttSHORT(data+22+(k*6)); + } + + return length; +} + +static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint8 *data = info->data + info->kern; + stbtt_uint32 needle, straw; + int l, r, m; + + // we only look at the first table. it must be 'horizontal' and format 0. + if (!info->kern) + return 0; + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 + return 0; + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format + return 0; + + l = 0; + r = ttUSHORT(data+10) - 1; + needle = glyph1 << 16 | glyph2; + while (l <= r) { + m = (l + r) >> 1; + straw = ttULONG(data+18+(m*6)); // note: unaligned read + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else + return ttSHORT(data+22+(m*6)); + } + return 0; +} + +static stbtt_int32 stbtt__GetCoverageIndex(stbtt_uint8 *coverageTable, int glyph) +{ + stbtt_uint16 coverageFormat = ttUSHORT(coverageTable); + switch (coverageFormat) { + case 1: { + stbtt_uint16 glyphCount = ttUSHORT(coverageTable + 2); + + // Binary search. + stbtt_int32 l=0, r=glyphCount-1, m; + int straw, needle=glyph; + while (l <= r) { + stbtt_uint8 *glyphArray = coverageTable + 4; + stbtt_uint16 glyphID; + m = (l + r) >> 1; + glyphID = ttUSHORT(glyphArray + 2 * m); + straw = glyphID; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + return m; + } + } + break; + } + + case 2: { + stbtt_uint16 rangeCount = ttUSHORT(coverageTable + 2); + stbtt_uint8 *rangeArray = coverageTable + 4; + + // Binary search. + stbtt_int32 l=0, r=rangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *rangeRecord; + m = (l + r) >> 1; + rangeRecord = rangeArray + 6 * m; + strawStart = ttUSHORT(rangeRecord); + strawEnd = ttUSHORT(rangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else { + stbtt_uint16 startCoverageIndex = ttUSHORT(rangeRecord + 4); + return startCoverageIndex + glyph - strawStart; + } + } + break; + } + + default: return -1; // unsupported + } + + return -1; +} + +static stbtt_int32 stbtt__GetGlyphClass(stbtt_uint8 *classDefTable, int glyph) +{ + stbtt_uint16 classDefFormat = ttUSHORT(classDefTable); + switch (classDefFormat) + { + case 1: { + stbtt_uint16 startGlyphID = ttUSHORT(classDefTable + 2); + stbtt_uint16 glyphCount = ttUSHORT(classDefTable + 4); + stbtt_uint8 *classDef1ValueArray = classDefTable + 6; + + if (glyph >= startGlyphID && glyph < startGlyphID + glyphCount) + return (stbtt_int32)ttUSHORT(classDef1ValueArray + 2 * (glyph - startGlyphID)); + break; + } + + case 2: { + stbtt_uint16 classRangeCount = ttUSHORT(classDefTable + 2); + stbtt_uint8 *classRangeRecords = classDefTable + 4; + + // Binary search. + stbtt_int32 l=0, r=classRangeCount-1, m; + int strawStart, strawEnd, needle=glyph; + while (l <= r) { + stbtt_uint8 *classRangeRecord; + m = (l + r) >> 1; + classRangeRecord = classRangeRecords + 6 * m; + strawStart = ttUSHORT(classRangeRecord); + strawEnd = ttUSHORT(classRangeRecord + 2); + if (needle < strawStart) + r = m - 1; + else if (needle > strawEnd) + l = m + 1; + else + return (stbtt_int32)ttUSHORT(classRangeRecord + 4); + } + break; + } + + default: + return -1; // Unsupported definition type, return an error. + } + + // "All glyphs not assigned to a class fall into class 0". (OpenType spec) + return 0; +} + +// Define to STBTT_assert(x) if you want to break on unimplemented formats. +#define STBTT_GPOS_TODO_assert(x) + +static stbtt_int32 stbtt__GetGlyphGPOSInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) +{ + stbtt_uint16 lookupListOffset; + stbtt_uint8 *lookupList; + stbtt_uint16 lookupCount; + stbtt_uint8 *data; + stbtt_int32 i, sti; + + if (!info->gpos) return 0; + + data = info->data + info->gpos; + + if (ttUSHORT(data+0) != 1) return 0; // Major version 1 + if (ttUSHORT(data+2) != 0) return 0; // Minor version 0 + + lookupListOffset = ttUSHORT(data+8); + lookupList = data + lookupListOffset; + lookupCount = ttUSHORT(lookupList); + + for (i=0; i= pairSetCount) return 0; + + needle=glyph2; + r=pairValueCount-1; + l=0; + + // Binary search. + while (l <= r) { + stbtt_uint16 secondGlyph; + stbtt_uint8 *pairValue; + m = (l + r) >> 1; + pairValue = pairValueArray + (2 + valueRecordPairSizeInBytes) * m; + secondGlyph = ttUSHORT(pairValue); + straw = secondGlyph; + if (needle < straw) + r = m - 1; + else if (needle > straw) + l = m + 1; + else { + stbtt_int16 xAdvance = ttSHORT(pairValue + 2); + return xAdvance; + } + } + } else + return 0; + break; + } + + case 2: { + stbtt_uint16 valueFormat1 = ttUSHORT(table + 4); + stbtt_uint16 valueFormat2 = ttUSHORT(table + 6); + if (valueFormat1 == 4 && valueFormat2 == 0) { // Support more formats? + stbtt_uint16 classDef1Offset = ttUSHORT(table + 8); + stbtt_uint16 classDef2Offset = ttUSHORT(table + 10); + int glyph1class = stbtt__GetGlyphClass(table + classDef1Offset, glyph1); + int glyph2class = stbtt__GetGlyphClass(table + classDef2Offset, glyph2); + + stbtt_uint16 class1Count = ttUSHORT(table + 12); + stbtt_uint16 class2Count = ttUSHORT(table + 14); + stbtt_uint8 *class1Records, *class2Records; + stbtt_int16 xAdvance; + + if (glyph1class < 0 || glyph1class >= class1Count) return 0; // malformed + if (glyph2class < 0 || glyph2class >= class2Count) return 0; // malformed + + class1Records = table + 16; + class2Records = class1Records + 2 * (glyph1class * class2Count); + xAdvance = ttSHORT(class2Records + 2 * glyph2class); + return xAdvance; + } else + return 0; + break; + } + + default: + return 0; // Unsupported position format + } + } + } + + return 0; +} + +STBTT_DEF int stbtt_GetGlyphKernAdvance(const stbtt_fontinfo *info, int g1, int g2) +{ + int xAdvance = 0; + + if (info->gpos) + xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); + else if (info->kern) + xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); + + return xAdvance; +} + +STBTT_DEF int stbtt_GetCodepointKernAdvance(const stbtt_fontinfo *info, int ch1, int ch2) +{ + if (!info->kern && !info->gpos) // if no kerning table, don't waste time looking up both codepoint->glyphs + return 0; + return stbtt_GetGlyphKernAdvance(info, stbtt_FindGlyphIndex(info,ch1), stbtt_FindGlyphIndex(info,ch2)); +} + +STBTT_DEF void stbtt_GetCodepointHMetrics(const stbtt_fontinfo *info, int codepoint, int *advanceWidth, int *leftSideBearing) +{ + stbtt_GetGlyphHMetrics(info, stbtt_FindGlyphIndex(info,codepoint), advanceWidth, leftSideBearing); +} + +STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, int *descent, int *lineGap) +{ + if (ascent ) *ascent = ttSHORT(info->data+info->hhea + 4); + if (descent) *descent = ttSHORT(info->data+info->hhea + 6); + if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); +} + +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + +STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) +{ + *x0 = ttSHORT(info->data + info->head + 36); + *y0 = ttSHORT(info->data + info->head + 38); + *x1 = ttSHORT(info->data + info->head + 40); + *y1 = ttSHORT(info->data + info->head + 42); +} + +STBTT_DEF float stbtt_ScaleForPixelHeight(const stbtt_fontinfo *info, float height) +{ + int fheight = ttSHORT(info->data + info->hhea + 4) - ttSHORT(info->data + info->hhea + 6); + return (float) height / fheight; +} + +STBTT_DEF float stbtt_ScaleForMappingEmToPixels(const stbtt_fontinfo *info, float pixels) +{ + int unitsPerEm = ttUSHORT(info->data + info->head + 18); + return pixels / unitsPerEm; +} + +STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *v) +{ + STBTT_free(v, info->userdata); +} + +STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) +{ + int i; + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); + + int numEntries = ttUSHORT(svg_doc_list); + stbtt_uint8 *svg_docs = svg_doc_list + 2; + + for(i=0; i= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) + return svg_doc; + } + return 0; +} + +STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) +{ + stbtt_uint8 *data = info->data; + stbtt_uint8 *svg_doc; + + if (info->svg == 0) + return 0; + + svg_doc = stbtt_FindSVGDoc(info, gl); + if (svg_doc != NULL) { + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); + return ttULONG(svg_doc + 8); + } else { + return 0; + } +} + +STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) +{ + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); +} + +////////////////////////////////////////////////////////////////////////////// +// +// antialiasing software rasterizer +// + +STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + int x0=0,y0=0,x1,y1; // =0 suppresses compiler warning + if (!stbtt_GetGlyphBox(font, glyph, &x0,&y0,&x1,&y1)) { + // e.g. space character + if (ix0) *ix0 = 0; + if (iy0) *iy0 = 0; + if (ix1) *ix1 = 0; + if (iy1) *iy1 = 0; + } else { + // move to integral bboxes (treating pixels as little squares, what pixels get touched)? + if (ix0) *ix0 = STBTT_ifloor( x0 * scale_x + shift_x); + if (iy0) *iy0 = STBTT_ifloor(-y1 * scale_y + shift_y); + if (ix1) *ix1 = STBTT_iceil ( x1 * scale_x + shift_x); + if (iy1) *iy1 = STBTT_iceil (-y0 * scale_y + shift_y); + } +} + +STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, glyph, scale_x, scale_y,0.0f,0.0f, ix0, iy0, ix1, iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBoxSubpixel(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetGlyphBitmapBoxSubpixel(font, stbtt_FindGlyphIndex(font,codepoint), scale_x, scale_y,shift_x,shift_y, ix0,iy0,ix1,iy1); +} + +STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1) +{ + stbtt_GetCodepointBitmapBoxSubpixel(font, codepoint, scale_x, scale_y,0.0f,0.0f, ix0,iy0,ix1,iy1); +} + +////////////////////////////////////////////////////////////////////////////// +// +// Rasterizer + +typedef struct stbtt__hheap_chunk +{ + struct stbtt__hheap_chunk *next; +} stbtt__hheap_chunk; + +typedef struct stbtt__hheap +{ + struct stbtt__hheap_chunk *head; + void *first_free; + int num_remaining_in_head_chunk; +} stbtt__hheap; + +static void *stbtt__hheap_alloc(stbtt__hheap *hh, size_t size, void *userdata) +{ + if (hh->first_free) { + void *p = hh->first_free; + hh->first_free = * (void **) p; + return p; + } else { + if (hh->num_remaining_in_head_chunk == 0) { + int count = (size < 32 ? 2000 : size < 128 ? 800 : 100); + stbtt__hheap_chunk *c = (stbtt__hheap_chunk *) STBTT_malloc(sizeof(stbtt__hheap_chunk) + size * count, userdata); + if (c == NULL) + return NULL; + c->next = hh->head; + hh->head = c; + hh->num_remaining_in_head_chunk = count; + } + --hh->num_remaining_in_head_chunk; + return (char *) (hh->head) + sizeof(stbtt__hheap_chunk) + size * hh->num_remaining_in_head_chunk; + } +} + +static void stbtt__hheap_free(stbtt__hheap *hh, void *p) +{ + *(void **) p = hh->first_free; + hh->first_free = p; +} + +static void stbtt__hheap_cleanup(stbtt__hheap *hh, void *userdata) +{ + stbtt__hheap_chunk *c = hh->head; + while (c) { + stbtt__hheap_chunk *n = c->next; + STBTT_free(c, userdata); + c = n; + } +} + +typedef struct stbtt__edge { + float x0,y0, x1,y1; + int invert; +} stbtt__edge; + + +typedef struct stbtt__active_edge +{ + struct stbtt__active_edge *next; + #if STBTT_RASTERIZER_VERSION==1 + int x,dx; + float ey; + int direction; + #elif STBTT_RASTERIZER_VERSION==2 + float fx,fdx,fdy; + float direction; + float sy; + float ey; + #else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" + #endif +} stbtt__active_edge; + +#if STBTT_RASTERIZER_VERSION == 1 +#define STBTT_FIXSHIFT 10 +#define STBTT_FIX (1 << STBTT_FIXSHIFT) +#define STBTT_FIXMASK (STBTT_FIX-1) + +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + if (!z) return z; + + // round dx down to avoid overshooting + if (dxdy < 0) + z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); + else + z->dx = STBTT_ifloor(STBTT_FIX * dxdy); + + z->x = STBTT_ifloor(STBTT_FIX * e->x0 + z->dx * (start_point - e->y0)); // use z->dx so when we offset later it's by the same amount + z->x -= off_x * STBTT_FIX; + + z->ey = e->y1; + z->next = 0; + z->direction = e->invert ? 1 : -1; + return z; +} +#elif STBTT_RASTERIZER_VERSION == 2 +static stbtt__active_edge *stbtt__new_active(stbtt__hheap *hh, stbtt__edge *e, int off_x, float start_point, void *userdata) +{ + stbtt__active_edge *z = (stbtt__active_edge *) stbtt__hheap_alloc(hh, sizeof(*z), userdata); + float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); + STBTT_assert(z != NULL); + //STBTT_assert(e->y0 <= start_point); + if (!z) return z; + z->fdx = dxdy; + z->fdy = dxdy != 0.0f ? (1.0f/dxdy) : 0.0f; + z->fx = e->x0 + dxdy * (start_point - e->y0); + z->fx -= off_x; + z->direction = e->invert ? 1.0f : -1.0f; + z->sy = e->y0; + z->ey = e->y1; + z->next = 0; + return z; +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#if STBTT_RASTERIZER_VERSION == 1 +// note: this routine clips fills that extend off the edges... ideally this +// wouldn't happen, but it could happen if the truetype glyph bounding boxes +// are wrong, or if the user supplies a too-small bitmap +static void stbtt__fill_active_edges(unsigned char *scanline, int len, stbtt__active_edge *e, int max_weight) +{ + // non-zero winding fill + int x0=0, w=0; + + while (e) { + if (w == 0) { + // if we're currently at zero, we need to record the edge start point + x0 = e->x; w += e->direction; + } else { + int x1 = e->x; w += e->direction; + // if we went to zero, we need to draw + if (w == 0) { + int i = x0 >> STBTT_FIXSHIFT; + int j = x1 >> STBTT_FIXSHIFT; + + if (i < len && j >= 0) { + if (i == j) { + // x0,x1 are the same pixel, so compute combined coverage + scanline[i] = scanline[i] + (stbtt_uint8) ((x1 - x0) * max_weight >> STBTT_FIXSHIFT); + } else { + if (i >= 0) // add antialiasing for x0 + scanline[i] = scanline[i] + (stbtt_uint8) (((STBTT_FIX - (x0 & STBTT_FIXMASK)) * max_weight) >> STBTT_FIXSHIFT); + else + i = -1; // clip + + if (j < len) // add antialiasing for x1 + scanline[j] = scanline[j] + (stbtt_uint8) (((x1 & STBTT_FIXMASK) * max_weight) >> STBTT_FIXSHIFT); + else + j = len; // clip + + for (++i; i < j; ++i) // fill pixels between x0 and x1 + scanline[i] = scanline[i] + (stbtt_uint8) max_weight; + } + } + } + } + + e = e->next; + } +} + +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0; + int max_weight = (255 / vsubsample); // weight per vertical scanline + int s; // vertical subsample index + unsigned char scanline_data[512], *scanline; + + if (result->w > 512) + scanline = (unsigned char *) STBTT_malloc(result->w, userdata); + else + scanline = scanline_data; + + y = off_y * vsubsample; + e[n].y0 = (off_y + result->h) * (float) vsubsample + 1; + + while (j < result->h) { + STBTT_memset(scanline, 0, result->w); + for (s=0; s < vsubsample; ++s) { + // find center of pixel for this scanline + float scan_y = y + 0.5f; + stbtt__active_edge **step = &active; + + // update all active edges; + // remove all active edges that terminate before the center of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + z->x += z->dx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + } + + // resort the list if needed + for(;;) { + int changed=0; + step = &active; + while (*step && (*step)->next) { + if ((*step)->x > (*step)->next->x) { + stbtt__active_edge *t = *step; + stbtt__active_edge *q = t->next; + + t->next = q->next; + q->next = t; + *step = q; + changed = 1; + } + step = &(*step)->next; + } + if (!changed) break; + } + + // insert all edges that start before the center of this scanline -- omit ones that also end on this scanline + while (e->y0 <= scan_y) { + if (e->y1 > scan_y) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y, userdata); + if (z != NULL) { + // find insertion point + if (active == NULL) + active = z; + else if (z->x < active->x) { + // insert at front + z->next = active; + active = z; + } else { + // find thing to insert AFTER + stbtt__active_edge *p = active; + while (p->next && p->next->x < z->x) + p = p->next; + // at this point, p->next->x is NOT < z->x + z->next = p->next; + p->next = z; + } + } + } + ++e; + } + + // now process all active edges in XOR fashion + if (active) + stbtt__fill_active_edges(scanline, result->w, active, max_weight); + + ++y; + } + STBTT_memcpy(result->pixels + j * result->stride, scanline, result->w); + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} + +#elif STBTT_RASTERIZER_VERSION == 2 + +// the edge passed in here does not cross the vertical line at x or the vertical line at x+1 +// (i.e. it has already been clipped to those) +static void stbtt__handle_clipped_edge(float *scanline, int x, stbtt__active_edge *e, float x0, float y0, float x1, float y1) +{ + if (y0 == y1) return; + STBTT_assert(y0 < y1); + STBTT_assert(e->sy <= e->ey); + if (y0 > e->ey) return; + if (y1 < e->sy) return; + if (y0 < e->sy) { + x0 += (x1-x0) * (e->sy - y0) / (y1-y0); + y0 = e->sy; + } + if (y1 > e->ey) { + x1 += (x1-x0) * (e->ey - y1) / (y1-y0); + y1 = e->ey; + } + + if (x0 == x) + STBTT_assert(x1 <= x+1); + else if (x0 == x+1) + STBTT_assert(x1 >= x); + else if (x0 <= x) + STBTT_assert(x1 <= x); + else if (x0 >= x+1) + STBTT_assert(x1 >= x+1); + else + STBTT_assert(x1 >= x && x1 <= x+1); + + if (x0 <= x && x1 <= x) + scanline[x] += e->direction * (y1-y0); + else if (x0 >= x+1 && x1 >= x+1) + ; + else { + STBTT_assert(x0 >= x && x0 <= x+1 && x1 >= x && x1 <= x+1); + scanline[x] += e->direction * (y1-y0) * (1-((x0-x)+(x1-x))/2); // coverage = 1 - average x position + } +} + +static float stbtt__sized_trapezoid_area(float height, float top_width, float bottom_width) +{ + STBTT_assert(top_width >= 0); + STBTT_assert(bottom_width >= 0); + return (top_width + bottom_width) / 2.0f * height; +} + +static float stbtt__position_trapezoid_area(float height, float tx0, float tx1, float bx0, float bx1) +{ + return stbtt__sized_trapezoid_area(height, tx1 - tx0, bx1 - bx0); +} + +static float stbtt__sized_triangle_area(float height, float width) +{ + return height * width / 2; +} + +static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, int len, stbtt__active_edge *e, float y_top) +{ + float y_bottom = y_top+1; + + while (e) { + // brute force every pixel + + // compute intersection points with top & bottom + STBTT_assert(e->ey >= y_top); + + if (e->fdx == 0) { + float x0 = e->fx; + if (x0 < len) { + if (x0 >= 0) { + stbtt__handle_clipped_edge(scanline,(int) x0,e, x0,y_top, x0,y_bottom); + stbtt__handle_clipped_edge(scanline_fill-1,(int) x0+1,e, x0,y_top, x0,y_bottom); + } else { + stbtt__handle_clipped_edge(scanline_fill-1,0,e, x0,y_top, x0,y_bottom); + } + } + } else { + float x0 = e->fx; + float dx = e->fdx; + float xb = x0 + dx; + float x_top, x_bottom; + float sy0,sy1; + float dy = e->fdy; + STBTT_assert(e->sy <= y_bottom && e->ey >= y_top); + + // compute endpoints of line segment clipped to this scanline (if the + // line segment starts on this scanline. x0 is the intersection of the + // line with y_top, but that may be off the line segment. + if (e->sy > y_top) { + x_top = x0 + dx * (e->sy - y_top); + sy0 = e->sy; + } else { + x_top = x0; + sy0 = y_top; + } + if (e->ey < y_bottom) { + x_bottom = x0 + dx * (e->ey - y_top); + sy1 = e->ey; + } else { + x_bottom = xb; + sy1 = y_bottom; + } + + if (x_top >= 0 && x_bottom >= 0 && x_top < len && x_bottom < len) { + // from here on, we don't have to range check x values + + if ((int) x_top == (int) x_bottom) { + float height; + // simple case, only spans one pixel + int x = (int) x_top; + height = (sy1 - sy0) * e->direction; + STBTT_assert(x >= 0 && x < len); + scanline[x] += stbtt__position_trapezoid_area(height, x_top, x+1.0f, x_bottom, x+1.0f); + scanline_fill[x] += height; // everything right of this pixel is filled + } else { + int x,x1,x2; + float y_crossing, y_final, step, sign, area; + // covers 2+ pixels + if (x_top > x_bottom) { + // flip scanline vertically; signed area is the same + float t; + sy0 = y_bottom - (sy0 - y_top); + sy1 = y_bottom - (sy1 - y_top); + t = sy0, sy0 = sy1, sy1 = t; + t = x_bottom, x_bottom = x_top, x_top = t; + dx = -dx; + dy = -dy; + t = x0, x0 = xb, xb = t; + } + STBTT_assert(dy >= 0); + STBTT_assert(dx >= 0); + + x1 = (int) x_top; + x2 = (int) x_bottom; + // compute intersection with y axis at x1+1 + y_crossing = y_top + dy * (x1+1 - x0); + + // compute intersection with y axis at x2 + y_final = y_top + dy * (x2 - x0); + + // x1 x_top x2 x_bottom + // y_top +------|-----+------------+------------+--------|---+------------+ + // | | | | | | + // | | | | | | + // sy0 | Txxxxx|............|............|............|............| + // y_crossing | *xxxxx.......|............|............|............| + // | | xxxxx..|............|............|............| + // | | /- xx*xxxx........|............|............| + // | | dy < | xxxxxx..|............|............| + // y_final | | \- | xx*xxx.........|............| + // sy1 | | | | xxxxxB...|............| + // | | | | | | + // | | | | | | + // y_bottom +------------+------------+------------+------------+------------+ + // + // goal is to measure the area covered by '.' in each pixel + + // if x2 is right at the right edge of x1, y_crossing can blow up, github #1057 + // @TODO: maybe test against sy1 rather than y_bottom? + if (y_crossing > y_bottom) + y_crossing = y_bottom; + + sign = e->direction; + + // area of the rectangle covered from sy0..y_crossing + area = sign * (y_crossing-sy0); + + // area of the triangle (x_top,sy0), (x1+1,sy0), (x1+1,y_crossing) + scanline[x1] += stbtt__sized_triangle_area(area, x1+1 - x_top); + + // check if final y_crossing is blown up; no test case for this + if (y_final > y_bottom) { + y_final = y_bottom; + dy = (y_final - y_crossing ) / (x2 - (x1+1)); // if denom=0, y_final = y_crossing, so y_final <= y_bottom + } + + // in second pixel, area covered by line segment found in first pixel + // is always a rectangle 1 wide * the height of that line segment; this + // is exactly what the variable 'area' stores. it also gets a contribution + // from the line segment within it. the THIRD pixel will get the first + // pixel's rectangle contribution, the second pixel's rectangle contribution, + // and its own contribution. the 'own contribution' is the same in every pixel except + // the leftmost and rightmost, a trapezoid that slides down in each pixel. + // the second pixel's contribution to the third pixel will be the + // rectangle 1 wide times the height change in the second pixel, which is dy. + + step = sign * dy * 1; // dy is dy/dx, change in y for every 1 change in x, + // which multiplied by 1-pixel-width is how much pixel area changes for each step in x + // so the area advances by 'step' every time + + for (x = x1+1; x < x2; ++x) { + scanline[x] += area + step/2; // area of trapezoid is 1*step/2 + area += step; + } + STBTT_assert(STBTT_fabs(area) <= 1.01f); // accumulated error from area += step unless we round step down + STBTT_assert(sy1 > y_final-0.01f); + + // area covered in the last pixel is the rectangle from all the pixels to the left, + // plus the trapezoid filled by the line segment in this pixel all the way to the right edge + scanline[x2] += area + sign * stbtt__position_trapezoid_area(sy1-y_final, (float) x2, x2+1.0f, x_bottom, x2+1.0f); + + // the rest of the line is filled based on the total height of the line segment in this pixel + scanline_fill[x2] += sign * (sy1-sy0); + } + } else { + // if edge goes outside of box we're drawing, we require + // clipping logic. since this does not match the intended use + // of this library, we use a different, very slow brute + // force implementation + // note though that this does happen some of the time because + // x_top and x_bottom can be extrapolated at the top & bottom of + // the shape and actually lie outside the bounding box + int x; + for (x=0; x < len; ++x) { + // cases: + // + // there can be up to two intersections with the pixel. any intersection + // with left or right edges can be handled by splitting into two (or three) + // regions. intersections with top & bottom do not necessitate case-wise logic. + // + // the old way of doing this found the intersections with the left & right edges, + // then used some simple logic to produce up to three segments in sorted order + // from top-to-bottom. however, this had a problem: if an x edge was epsilon + // across the x border, then the corresponding y position might not be distinct + // from the other y segment, and it might ignored as an empty segment. to avoid + // that, we need to explicitly produce segments based on x positions. + + // rename variables to clearly-defined pairs + float y0 = y_top; + float x1 = (float) (x); + float x2 = (float) (x+1); + float x3 = xb; + float y3 = y_bottom; + + // x = e->x + e->dx * (y-y_top) + // (y-y_top) = (x - e->x) / e->dx + // y = (x - e->x) / e->dx + y_top + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; + + if (x0 < x1 && x3 > x2) { // three segments descending down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x1 && x0 > x2) { // three segments descending down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x1 && x3 > x1) { // two segments across x, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x3 < x1 && x0 > x1) { // two segments across x, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); + stbtt__handle_clipped_edge(scanline,x,e, x1,y1, x3,y3); + } else if (x0 < x2 && x3 > x2) { // two segments across x+1, down-right + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else if (x3 < x2 && x0 > x2) { // two segments across x+1, down-left + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x2,y2); + stbtt__handle_clipped_edge(scanline,x,e, x2,y2, x3,y3); + } else { // one segment + stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x3,y3); + } + } + } + } + e = e->next; + } +} + +// directly AA rasterize edges w/o supersampling +static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int n, int vsubsample, int off_x, int off_y, void *userdata) +{ + stbtt__hheap hh = { 0, 0, 0 }; + stbtt__active_edge *active = NULL; + int y,j=0, i; + float scanline_data[129], *scanline, *scanline2; + + STBTT__NOTUSED(vsubsample); + + if (result->w > 64) + scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); + else + scanline = scanline_data; + + scanline2 = scanline + result->w; + + y = off_y; + e[n].y0 = (float) (off_y + result->h) + 1; + + while (j < result->h) { + // find center of pixel for this scanline + float scan_y_top = y + 0.0f; + float scan_y_bottom = y + 1.0f; + stbtt__active_edge **step = &active; + + STBTT_memset(scanline , 0, result->w*sizeof(scanline[0])); + STBTT_memset(scanline2, 0, (result->w+1)*sizeof(scanline[0])); + + // update all active edges; + // remove all active edges that terminate before the top of this scanline + while (*step) { + stbtt__active_edge * z = *step; + if (z->ey <= scan_y_top) { + *step = z->next; // delete from list + STBTT_assert(z->direction); + z->direction = 0; + stbtt__hheap_free(&hh, z); + } else { + step = &((*step)->next); // advance through list + } + } + + // insert all edges that start before the bottom of this scanline + while (e->y0 <= scan_y_bottom) { + if (e->y0 != e->y1) { + stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); + if (z != NULL) { + if (j == 0 && off_y != 0) { + if (z->ey < scan_y_top) { + // this can happen due to subpixel positioning and some kind of fp rounding error i think + z->ey = scan_y_top; + } + } + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds + // insert at front + z->next = active; + active = z; + } + } + ++e; + } + + // now process all active edges + if (active) + stbtt__fill_active_edges_new(scanline, scanline2+1, result->w, active, scan_y_top); + + { + float sum = 0; + for (i=0; i < result->w; ++i) { + float k; + int m; + sum += scanline2[i]; + k = scanline[i] + sum; + k = (float) STBTT_fabs(k)*255 + 0.5f; + m = (int) k; + if (m > 255) m = 255; + result->pixels[j*result->stride + i] = (unsigned char) m; + } + } + // advance all the edges + step = &active; + while (*step) { + stbtt__active_edge *z = *step; + z->fx += z->fdx; // advance to position for current scanline + step = &((*step)->next); // advance through list + } + + ++y; + ++j; + } + + stbtt__hheap_cleanup(&hh, userdata); + + if (scanline != scanline_data) + STBTT_free(scanline, userdata); +} +#else +#error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + +#define STBTT__COMPARE(a,b) ((a)->y0 < (b)->y0) + +static void stbtt__sort_edges_ins_sort(stbtt__edge *p, int n) +{ + int i,j; + for (i=1; i < n; ++i) { + stbtt__edge t = p[i], *a = &t; + j = i; + while (j > 0) { + stbtt__edge *b = &p[j-1]; + int c = STBTT__COMPARE(a,b); + if (!c) break; + p[j] = p[j-1]; + --j; + } + if (i != j) + p[j] = t; + } +} + +static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) +{ + /* threshold for transitioning to insertion sort */ + while (n > 12) { + stbtt__edge t; + int c01,c12,c,m,i,j; + + /* compute median of three */ + m = n >> 1; + c01 = STBTT__COMPARE(&p[0],&p[m]); + c12 = STBTT__COMPARE(&p[m],&p[n-1]); + /* if 0 >= mid >= end, or 0 < mid < end, then use mid */ + if (c01 != c12) { + /* otherwise, we'll need to swap something else to middle */ + int z; + c = STBTT__COMPARE(&p[0],&p[n-1]); + /* 0>mid && midn => n; 0 0 */ + /* 0n: 0>n => 0; 0 n */ + z = (c == c12) ? 0 : n-1; + t = p[z]; + p[z] = p[m]; + p[m] = t; + } + /* now p[m] is the median-of-three */ + /* swap it to the beginning so it won't move around */ + t = p[0]; + p[0] = p[m]; + p[m] = t; + + /* partition loop */ + i=1; + j=n-1; + for(;;) { + /* handling of equality is crucial here */ + /* for sentinels & efficiency with duplicates */ + for (;;++i) { + if (!STBTT__COMPARE(&p[i], &p[0])) break; + } + for (;;--j) { + if (!STBTT__COMPARE(&p[0], &p[j])) break; + } + /* make sure we haven't crossed */ + if (i >= j) break; + t = p[i]; + p[i] = p[j]; + p[j] = t; + + ++i; + --j; + } + /* recurse on smaller side, iterate on larger */ + if (j < (n-i)) { + stbtt__sort_edges_quicksort(p,j); + p = p+i; + n = n-i; + } else { + stbtt__sort_edges_quicksort(p+i, n-i); + n = j; + } + } +} + +static void stbtt__sort_edges(stbtt__edge *p, int n) +{ + stbtt__sort_edges_quicksort(p, n); + stbtt__sort_edges_ins_sort(p, n); +} + +typedef struct +{ + float x,y; +} stbtt__point; + +static void stbtt__rasterize(stbtt__bitmap *result, stbtt__point *pts, int *wcount, int windings, float scale_x, float scale_y, float shift_x, float shift_y, int off_x, int off_y, int invert, void *userdata) +{ + float y_scale_inv = invert ? -scale_y : scale_y; + stbtt__edge *e; + int n,i,j,k,m; +#if STBTT_RASTERIZER_VERSION == 1 + int vsubsample = result->h < 8 ? 15 : 5; +#elif STBTT_RASTERIZER_VERSION == 2 + int vsubsample = 1; +#else + #error "Unrecognized value of STBTT_RASTERIZER_VERSION" +#endif + // vsubsample should divide 255 evenly; otherwise we won't reach full opacity + + // now we have to blow out the windings into explicit edge lists + n = 0; + for (i=0; i < windings; ++i) + n += wcount[i]; + + e = (stbtt__edge *) STBTT_malloc(sizeof(*e) * (n+1), userdata); // add an extra one as a sentinel + if (e == 0) return; + n = 0; + + m=0; + for (i=0; i < windings; ++i) { + stbtt__point *p = pts + m; + m += wcount[i]; + j = wcount[i]-1; + for (k=0; k < wcount[i]; j=k++) { + int a=k,b=j; + // skip the edge if horizontal + if (p[j].y == p[k].y) + continue; + // add edge from j to k to the list + e[n].invert = 0; + if (invert ? p[j].y > p[k].y : p[j].y < p[k].y) { + e[n].invert = 1; + a=j,b=k; + } + e[n].x0 = p[a].x * scale_x + shift_x; + e[n].y0 = (p[a].y * y_scale_inv + shift_y) * vsubsample; + e[n].x1 = p[b].x * scale_x + shift_x; + e[n].y1 = (p[b].y * y_scale_inv + shift_y) * vsubsample; + ++n; + } + } + + // now sort the edges by their highest point (should snap to integer, and then by x) + //STBTT_sort(e, n, sizeof(e[0]), stbtt__edge_compare); + stbtt__sort_edges(e, n); + + // now, traverse the scanlines and find the intersections on each scanline, use xor winding rule + stbtt__rasterize_sorted_edges(result, e, n, vsubsample, off_x, off_y, userdata); + + STBTT_free(e, userdata); +} + +static void stbtt__add_point(stbtt__point *points, int n, float x, float y) +{ + if (!points) return; // during first pass, it's unallocated + points[n].x = x; + points[n].y = y; +} + +// tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching +static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) +{ + // midpoint + float mx = (x0 + 2*x1 + x2)/4; + float my = (y0 + 2*y1 + y2)/4; + // versus directly drawn line + float dx = (x0+x2)/2 - mx; + float dy = (y0+y2)/2 - my; + if (n > 16) // 65536 segments on one curve better be enough! + return 1; + if (dx*dx+dy*dy > objspace_flatness_squared) { // half-pixel error allowed... need to be smaller if AA + stbtt__tesselate_curve(points, num_points, x0,y0, (x0+x1)/2.0f,(y0+y1)/2.0f, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_curve(points, num_points, mx,my, (x1+x2)/2.0f,(y1+y2)/2.0f, x2,y2, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x2,y2); + *num_points = *num_points+1; + } + return 1; +} + +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + +// returns number of contours +static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) +{ + stbtt__point *points=0; + int num_points=0; + + float objspace_flatness_squared = objspace_flatness * objspace_flatness; + int i,n=0,start=0, pass; + + // count how many "moves" there are to get the contour count + for (i=0; i < num_verts; ++i) + if (vertices[i].type == STBTT_vmove) + ++n; + + *num_contours = n; + if (n == 0) return 0; + + *contour_lengths = (int *) STBTT_malloc(sizeof(**contour_lengths) * n, userdata); + + if (*contour_lengths == 0) { + *num_contours = 0; + return 0; + } + + // make two passes through the points so we don't need to realloc + for (pass=0; pass < 2; ++pass) { + float x=0,y=0; + if (pass == 1) { + points = (stbtt__point *) STBTT_malloc(num_points * sizeof(points[0]), userdata); + if (points == NULL) goto error; + } + num_points = 0; + n= -1; + for (i=0; i < num_verts; ++i) { + switch (vertices[i].type) { + case STBTT_vmove: + // start the next contour + if (n >= 0) + (*contour_lengths)[n] = num_points - start; + ++n; + start = num_points; + + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x,y); + break; + case STBTT_vline: + x = vertices[i].x, y = vertices[i].y; + stbtt__add_point(points, num_points++, x, y); + break; + case STBTT_vcurve: + stbtt__tesselate_curve(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; + } + } + (*contour_lengths)[n] = num_points - start; + } + + return points; +error: + STBTT_free(points, userdata); + STBTT_free(*contour_lengths, userdata); + *contour_lengths = 0; + *num_contours = 0; + return NULL; +} + +STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, float flatness_in_pixels, stbtt_vertex *vertices, int num_verts, float scale_x, float scale_y, float shift_x, float shift_y, int x_off, int y_off, int invert, void *userdata) +{ + float scale = scale_x > scale_y ? scale_y : scale_x; + int winding_count = 0; + int *winding_lengths = NULL; + stbtt__point *windings = stbtt_FlattenCurves(vertices, num_verts, flatness_in_pixels / scale, &winding_lengths, &winding_count, userdata); + if (windings) { + stbtt__rasterize(result, windings, winding_lengths, winding_count, scale_x, scale_y, shift_x, shift_y, x_off, y_off, invert, userdata); + STBTT_free(winding_lengths, userdata); + STBTT_free(windings, userdata); + } +} + +STBTT_DEF void stbtt_FreeBitmap(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + int ix0,iy0,ix1,iy1; + stbtt__bitmap gbm; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,&ix1,&iy1); + + // now we get the size + gbm.w = (ix1 - ix0); + gbm.h = (iy1 - iy0); + gbm.pixels = NULL; // in case we error + + if (width ) *width = gbm.w; + if (height) *height = gbm.h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + if (gbm.w && gbm.h) { + gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); + if (gbm.pixels) { + gbm.stride = gbm.w; + + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0, iy0, 1, info->userdata); + } + } + STBTT_free(vertices, info->userdata); + return gbm.pixels; +} + +STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y, 0.0f, 0.0f, glyph, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph) +{ + int ix0,iy0; + stbtt_vertex *vertices; + int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); + stbtt__bitmap gbm; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); + gbm.pixels = output; + gbm.w = out_w; + gbm.h = out_h; + gbm.stride = out_stride; + + if (gbm.w && gbm.h) + stbtt_Rasterize(&gbm, 0.35f, vertices, num_verts, scale_x, scale_y, shift_x, shift_y, ix0,iy0, 1, info->userdata); + + STBTT_free(vertices, info->userdata); +} + +STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, glyph); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixelPrefilter(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, oversample_x, oversample_y, sub_x, sub_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint) +{ + stbtt_MakeGlyphBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, shift_x, shift_y, stbtt_FindGlyphIndex(info,codepoint)); +} + +STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); +} + +STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) +{ + stbtt_MakeCodepointBitmapSubpixel(info, output, out_w, out_h, out_stride, scale_x, scale_y, 0.0f,0.0f, codepoint); +} + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-CRAPPY packing to keep source code small + +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) + float pixel_height, // height of font in pixels + unsigned char *pixels, int pw, int ph, // bitmap to be filled in + int first_char, int num_chars, // characters to bake + stbtt_bakedchar *chardata) +{ + float scale; + int x,y,bottom_y, i; + stbtt_fontinfo f; + f.userdata = NULL; + if (!stbtt_InitFont(&f, data, offset)) + return -1; + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + x=y=1; + bottom_y = 1; + + scale = stbtt_ScaleForPixelHeight(&f, pixel_height); + + for (i=0; i < num_chars; ++i) { + int advance, lsb, x0,y0,x1,y1,gw,gh; + int g = stbtt_FindGlyphIndex(&f, first_char + i); + stbtt_GetGlyphHMetrics(&f, g, &advance, &lsb); + stbtt_GetGlyphBitmapBox(&f, g, scale,scale, &x0,&y0,&x1,&y1); + gw = x1-x0; + gh = y1-y0; + if (x + gw + 1 >= pw) + y = bottom_y, x = 1; // advance to next row + if (y + gh + 1 >= ph) // check if it fits vertically AFTER potentially moving to next row + return -i; + STBTT_assert(x+gw < pw); + STBTT_assert(y+gh < ph); + stbtt_MakeGlyphBitmap(&f, pixels+x+y*pw, gw,gh,pw, scale,scale, g); + chardata[i].x0 = (stbtt_int16) x; + chardata[i].y0 = (stbtt_int16) y; + chardata[i].x1 = (stbtt_int16) (x + gw); + chardata[i].y1 = (stbtt_int16) (y + gh); + chardata[i].xadvance = scale * advance; + chardata[i].xoff = (float) x0; + chardata[i].yoff = (float) y0; + x = x + gw + 1; + if (y+gh+1 > bottom_y) + bottom_y = y+gh+1; + } + return bottom_y; +} + +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +{ + float d3d_bias = opengl_fillrule ? 0 : -0.5f; + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_bakedchar *b = chardata + char_index; + int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); + int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); + + q->x0 = round_x + d3d_bias; + q->y0 = round_y + d3d_bias; + q->x1 = round_x + b->x1 - b->x0 + d3d_bias; + q->y1 = round_y + b->y1 - b->y0 + d3d_bias; + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// rectangle packing replacement routines if you don't have stb_rect_pack.h +// + +#ifndef STB_RECT_PACK_VERSION + +typedef int stbrp_coord; + +//////////////////////////////////////////////////////////////////////////////////// +// // +// // +// COMPILER WARNING ?!?!? // +// // +// // +// if you get a compile warning due to these symbols being defined more than // +// once, move #include "stb_rect_pack.h" before #include "stb_truetype.h" // +// // +//////////////////////////////////////////////////////////////////////////////////// + +typedef struct +{ + int width,height; + int x,y,bottom_y; +} stbrp_context; + +typedef struct +{ + unsigned char x; +} stbrp_node; + +struct stbrp_rect +{ + stbrp_coord x,y; + int id,w,h,was_packed; +}; + +static void stbrp_init_target(stbrp_context *con, int pw, int ph, stbrp_node *nodes, int num_nodes) +{ + con->width = pw; + con->height = ph; + con->x = 0; + con->y = 0; + con->bottom_y = 0; + STBTT__NOTUSED(nodes); + STBTT__NOTUSED(num_nodes); +} + +static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) +{ + int i; + for (i=0; i < num_rects; ++i) { + if (con->x + rects[i].w > con->width) { + con->x = 0; + con->y = con->bottom_y; + } + if (con->y + rects[i].h > con->height) + break; + rects[i].x = con->x; + rects[i].y = con->y; + rects[i].was_packed = 1; + con->x += rects[i].w; + if (con->y + rects[i].h > con->bottom_y) + con->bottom_y = con->y + rects[i].h; + } + for ( ; i < num_rects; ++i) + rects[i].was_packed = 0; +} +#endif + +////////////////////////////////////////////////////////////////////////////// +// +// bitmap baking +// +// This is SUPER-AWESOME (tm Ryan Gordon) packing using stb_rect_pack.h. If +// stb_rect_pack.h isn't available, it uses the BakeFontBitmap strategy. + +STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int pw, int ph, int stride_in_bytes, int padding, void *alloc_context) +{ + stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context) ,alloc_context); + int num_nodes = pw - padding; + stbrp_node *nodes = (stbrp_node *) STBTT_malloc(sizeof(*nodes ) * num_nodes,alloc_context); + + if (context == NULL || nodes == NULL) { + if (context != NULL) STBTT_free(context, alloc_context); + if (nodes != NULL) STBTT_free(nodes , alloc_context); + return 0; + } + + spc->user_allocator_context = alloc_context; + spc->width = pw; + spc->height = ph; + spc->pixels = pixels; + spc->pack_info = context; + spc->nodes = nodes; + spc->padding = padding; + spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; + spc->h_oversample = 1; + spc->v_oversample = 1; + spc->skip_missing = 0; + + stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); + + if (pixels) + STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels + + return 1; +} + +STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc) +{ + STBTT_free(spc->nodes , spc->user_allocator_context); + STBTT_free(spc->pack_info, spc->user_allocator_context); +} + +STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h_oversample, unsigned int v_oversample) +{ + STBTT_assert(h_oversample <= STBTT_MAX_OVERSAMPLE); + STBTT_assert(v_oversample <= STBTT_MAX_OVERSAMPLE); + if (h_oversample <= STBTT_MAX_OVERSAMPLE) + spc->h_oversample = h_oversample; + if (v_oversample <= STBTT_MAX_OVERSAMPLE) + spc->v_oversample = v_oversample; +} + +STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) +{ + spc->skip_missing = skip; +} + +#define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) + +static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_w = w - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < h; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_w; ++i) { + total += pixels[i] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i]; + pixels[i] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < w; ++i) { + STBTT_assert(pixels[i] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i] = (unsigned char) (total / kernel_width); + } + + pixels += stride_in_bytes; + } +} + +static void stbtt__v_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) +{ + unsigned char buffer[STBTT_MAX_OVERSAMPLE]; + int safe_h = h - kernel_width; + int j; + STBTT_memset(buffer, 0, STBTT_MAX_OVERSAMPLE); // suppress bogus warning from VS2013 -analyze + for (j=0; j < w; ++j) { + int i; + unsigned int total; + STBTT_memset(buffer, 0, kernel_width); + + total = 0; + + // make kernel_width a constant in common cases so compiler can optimize out the divide + switch (kernel_width) { + case 2: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 2); + } + break; + case 3: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 3); + } + break; + case 4: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 4); + } + break; + case 5: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / 5); + } + break; + default: + for (i=0; i <= safe_h; ++i) { + total += pixels[i*stride_in_bytes] - buffer[i & STBTT__OVER_MASK]; + buffer[(i+kernel_width) & STBTT__OVER_MASK] = pixels[i*stride_in_bytes]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + break; + } + + for (; i < h; ++i) { + STBTT_assert(pixels[i*stride_in_bytes] == 0); + total -= buffer[i & STBTT__OVER_MASK]; + pixels[i*stride_in_bytes] = (unsigned char) (total / kernel_width); + } + + pixels += 1; + } +} + +static float stbtt__oversample_shift(int oversample) +{ + if (!oversample) + return 0.0f; + + // The prefilter is a box filter of width "oversample", + // which shifts phase by (oversample - 1)/2 pixels in + // oversampled space. We want to shift in the opposite + // direction to counter this. + return (float)-(oversample - 1) / (2.0f * (float)oversample); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k; + int missing_glyph_added = 0; + + k=0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + ranges[i].h_oversample = (unsigned char) spc->h_oversample; + ranges[i].v_oversample = (unsigned char) spc->v_oversample; + for (j=0; j < ranges[i].num_chars; ++j) { + int x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { + rects[k].w = rects[k].h = 0; + } else { + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + &x0,&y0,&x1,&y1); + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); + if (glyph == 0) + missing_glyph_added = 1; + } + ++k; + } + } + + return k; +} + +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + +// rects array must be big enough to accommodate all characters in the given ranges +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +{ + int i,j,k, missing_glyph = -1, return_value = 1; + + // save current values + int old_h_over = spc->h_oversample; + int old_v_over = spc->v_oversample; + + k = 0; + for (i=0; i < num_ranges; ++i) { + float fh = ranges[i].font_size; + float scale = fh > 0 ? stbtt_ScaleForPixelHeight(info, fh) : stbtt_ScaleForMappingEmToPixels(info, -fh); + float recip_h,recip_v,sub_x,sub_y; + spc->h_oversample = ranges[i].h_oversample; + spc->v_oversample = ranges[i].v_oversample; + recip_h = 1.0f / spc->h_oversample; + recip_v = 1.0f / spc->v_oversample; + sub_x = stbtt__oversample_shift(spc->h_oversample); + sub_y = stbtt__oversample_shift(spc->v_oversample); + for (j=0; j < ranges[i].num_chars; ++j) { + stbrp_rect *r = &rects[k]; + if (r->was_packed && r->w != 0 && r->h != 0) { + stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; + int advance, lsb, x0,y0,x1,y1; + int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; + int glyph = stbtt_FindGlyphIndex(info, codepoint); + stbrp_coord pad = (stbrp_coord) spc->padding; + + // pad on left and top + r->x += pad; + r->y += pad; + r->w -= pad; + r->h -= pad; + stbtt_GetGlyphHMetrics(info, glyph, &advance, &lsb); + stbtt_GetGlyphBitmapBox(info, glyph, + scale * spc->h_oversample, + scale * spc->v_oversample, + &x0,&y0,&x1,&y1); + stbtt_MakeGlyphBitmapSubpixel(info, + spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w - spc->h_oversample+1, + r->h - spc->v_oversample+1, + spc->stride_in_bytes, + scale * spc->h_oversample, + scale * spc->v_oversample, + 0,0, + glyph); + + if (spc->h_oversample > 1) + stbtt__h_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->h_oversample); + + if (spc->v_oversample > 1) + stbtt__v_prefilter(spc->pixels + r->x + r->y*spc->stride_in_bytes, + r->w, r->h, spc->stride_in_bytes, + spc->v_oversample); + + bc->x0 = (stbtt_int16) r->x; + bc->y0 = (stbtt_int16) r->y; + bc->x1 = (stbtt_int16) (r->x + r->w); + bc->y1 = (stbtt_int16) (r->y + r->h); + bc->xadvance = scale * advance; + bc->xoff = (float) x0 * recip_h + sub_x; + bc->yoff = (float) y0 * recip_v + sub_y; + bc->xoff2 = (x0 + r->w) * recip_h + sub_x; + bc->yoff2 = (y0 + r->h) * recip_v + sub_y; + + if (glyph == 0) + missing_glyph = j; + } else if (spc->skip_missing) { + return_value = 0; + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; + } else { + return_value = 0; // if any fail, report failure + } + + ++k; + } + } + + // restore original values + spc->h_oversample = old_h_over; + spc->v_oversample = old_v_over; + + return return_value; +} + +STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects) +{ + stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); +} + +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +{ + stbtt_fontinfo info; + int i,j,n, return_value = 1; + //stbrp_context *context = (stbrp_context *) spc->pack_info; + stbrp_rect *rects; + + // flag all characters as NOT packed + for (i=0; i < num_ranges; ++i) + for (j=0; j < ranges[i].num_chars; ++j) + ranges[i].chardata_for_range[j].x0 = + ranges[i].chardata_for_range[j].y0 = + ranges[i].chardata_for_range[j].x1 = + ranges[i].chardata_for_range[j].y1 = 0; + + n = 0; + for (i=0; i < num_ranges; ++i) + n += ranges[i].num_chars; + + rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); + if (rects == NULL) + return 0; + + info.userdata = spc->user_allocator_context; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index)); + + n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); + + stbtt_PackFontRangesPackRects(spc, rects, n); + + return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); + + STBTT_free(rects, spc->user_allocator_context); + return return_value; +} + +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, + int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) +{ + stbtt_pack_range range; + range.first_unicode_codepoint_in_range = first_unicode_codepoint_in_range; + range.array_of_unicode_codepoints = NULL; + range.num_chars = num_chars_in_range; + range.chardata_for_range = chardata_for_range; + range.font_size = font_size; + return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); +} + +STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) +{ + int i_ascent, i_descent, i_lineGap; + float scale; + stbtt_fontinfo info; + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); + *ascent = (float) i_ascent * scale; + *descent = (float) i_descent * scale; + *lineGap = (float) i_lineGap * scale; +} + +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +{ + float ipw = 1.0f / pw, iph = 1.0f / ph; + const stbtt_packedchar *b = chardata + char_index; + + if (align_to_integer) { + float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); + float y = (float) STBTT_ifloor((*ypos + b->yoff) + 0.5f); + q->x0 = x; + q->y0 = y; + q->x1 = x + b->xoff2 - b->xoff; + q->y1 = y + b->yoff2 - b->yoff; + } else { + q->x0 = *xpos + b->xoff; + q->y0 = *ypos + b->yoff; + q->x1 = *xpos + b->xoff2; + q->y1 = *ypos + b->yoff2; + } + + q->s0 = b->x0 * ipw; + q->t0 = b->y0 * iph; + q->s1 = b->x1 * ipw; + q->t1 = b->y1 * iph; + + *xpos += b->xadvance; +} + +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) STBTT_sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + // make sure y never passes through a vertex of the shape + y_frac = (float) STBTT_fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + + orig[0] = x; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + a*x^2 + b*x + c = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + if (scale == 0) return NULL; + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + if (verts[i].type == STBTT_vline && precompute[i] != 0.0f) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + float dist,dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3] = {0.f,0.f,0.f}; + float px,py,t,it,dist2; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} + +////////////////////////////////////////////////////////////////////////////// +// +// font name matching -- recommended not to use this +// + +// check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) +{ + stbtt_int32 i=0; + + // convert utf16 to utf8 and compare the results while converting + while (len2) { + stbtt_uint16 ch = s2[0]*256 + s2[1]; + if (ch < 0x80) { + if (i >= len1) return -1; + if (s1[i++] != ch) return -1; + } else if (ch < 0x800) { + if (i+1 >= len1) return -1; + if (s1[i++] != 0xc0 + (ch >> 6)) return -1; + if (s1[i++] != 0x80 + (ch & 0x3f)) return -1; + } else if (ch >= 0xd800 && ch < 0xdc00) { + stbtt_uint32 c; + stbtt_uint16 ch2 = s2[2]*256 + s2[3]; + if (i+3 >= len1) return -1; + c = ((ch - 0xd800) << 10) + (ch2 - 0xdc00) + 0x10000; + if (s1[i++] != 0xf0 + (c >> 18)) return -1; + if (s1[i++] != 0x80 + ((c >> 12) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((c ) & 0x3f)) return -1; + s2 += 2; // plus another 2 below + len2 -= 2; + } else if (ch >= 0xdc00 && ch < 0xe000) { + return -1; + } else { + if (i+2 >= len1) return -1; + if (s1[i++] != 0xe0 + (ch >> 12)) return -1; + if (s1[i++] != 0x80 + ((ch >> 6) & 0x3f)) return -1; + if (s1[i++] != 0x80 + ((ch ) & 0x3f)) return -1; + } + s2 += 2; + len2 -= 2; + } + return i; +} + +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) +{ + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); +} + +// returns results in whatever encoding you request... but note that 2-byte encodings +// will be BIG-ENDIAN... use stbtt_CompareUTF8toUTF16_bigendian() to compare +STBTT_DEF const char *stbtt_GetFontNameString(const stbtt_fontinfo *font, int *length, int platformID, int encodingID, int languageID, int nameID) +{ + stbtt_int32 i,count,stringOffset; + stbtt_uint8 *fc = font->data; + stbtt_uint32 offset = font->fontstart; + stbtt_uint32 nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return NULL; + + count = ttUSHORT(fc+nm+2); + stringOffset = nm + ttUSHORT(fc+nm+4); + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + if (platformID == ttUSHORT(fc+loc+0) && encodingID == ttUSHORT(fc+loc+2) + && languageID == ttUSHORT(fc+loc+4) && nameID == ttUSHORT(fc+loc+6)) { + *length = ttUSHORT(fc+loc+8); + return (const char *) (fc+stringOffset+ttUSHORT(fc+loc+10)); + } + } + return NULL; +} + +static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, stbtt_int32 nlen, stbtt_int32 target_id, stbtt_int32 next_id) +{ + stbtt_int32 i; + stbtt_int32 count = ttUSHORT(fc+nm+2); + stbtt_int32 stringOffset = nm + ttUSHORT(fc+nm+4); + + for (i=0; i < count; ++i) { + stbtt_uint32 loc = nm + 6 + 12 * i; + stbtt_int32 id = ttUSHORT(fc+loc+6); + if (id == target_id) { + // find the encoding + stbtt_int32 platform = ttUSHORT(fc+loc+0), encoding = ttUSHORT(fc+loc+2), language = ttUSHORT(fc+loc+4); + + // is this a Unicode encoding? + if (platform == 0 || (platform == 3 && encoding == 1) || (platform == 3 && encoding == 10)) { + stbtt_int32 slen = ttUSHORT(fc+loc+8); + stbtt_int32 off = ttUSHORT(fc+loc+10); + + // check if there's a prefix match + stbtt_int32 matchlen = stbtt__CompareUTF8toUTF16_bigendian_prefix(name, nlen, fc+stringOffset+off,slen); + if (matchlen >= 0) { + // check for target_id+1 immediately following, with same encoding & language + if (i+1 < count && ttUSHORT(fc+loc+12+6) == next_id && ttUSHORT(fc+loc+12) == platform && ttUSHORT(fc+loc+12+2) == encoding && ttUSHORT(fc+loc+12+4) == language) { + slen = ttUSHORT(fc+loc+12+8); + off = ttUSHORT(fc+loc+12+10); + if (slen == 0) { + if (matchlen == nlen) + return 1; + } else if (matchlen < nlen && name[matchlen] == ' ') { + ++matchlen; + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + return 1; + } + } else { + // if nothing immediately following + if (matchlen == nlen) + return 1; + } + } + } + + // @TODO handle other encodings + } + } + return 0; +} + +static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *name, stbtt_int32 flags) +{ + stbtt_int32 nlen = (stbtt_int32) STBTT_strlen((char *) name); + stbtt_uint32 nm,hd; + if (!stbtt__isfont(fc+offset)) return 0; + + // check italics/bold/underline flags in macStyle... + if (flags) { + hd = stbtt__find_table(fc, offset, "head"); + if ((ttUSHORT(fc+hd+44) & 7) != (flags & 7)) return 0; + } + + nm = stbtt__find_table(fc, offset, "name"); + if (!nm) return 0; + + if (flags) { + // if we checked the macStyle flags, then just check the family and ignore the subfamily + if (stbtt__matchpair(fc, nm, name, nlen, 16, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, -1)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } else { + if (stbtt__matchpair(fc, nm, name, nlen, 16, 17)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 1, 2)) return 1; + if (stbtt__matchpair(fc, nm, name, nlen, 3, -1)) return 1; + } + + return 0; +} + +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) +{ + stbtt_int32 i; + for (i=0;;++i) { + stbtt_int32 off = stbtt_GetFontOffsetForIndex(font_collection, i); + if (off < 0) return off; + if (stbtt__matches((stbtt_uint8 *) font_collection, off, (stbtt_uint8*) name_utf8, flags)) + return off; + } +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + +#endif // STB_TRUETYPE_IMPLEMENTATION + + +// FULL VERSION HISTORY +// +// 1.25 (2021-07-11) many fixes +// 1.24 (2020-02-05) fix warning +// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) +// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined +// 1.21 (2019-02-25) fix warning +// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() +// 1.19 (2018-02-11) OpenType GPOS kerning (horizontal only), STBTT_fmod +// 1.18 (2018-01-29) add missing function +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef +// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges +// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges +// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; +// allow PackFontRanges to pack and render in separate phases; +// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); +// fixed an assert() bug in the new rasterizer +// replace assert() with STBTT_assert() in new rasterizer +// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) +// also more precise AA rasterizer, except if shapes overlap +// remove need for STBTT_sort +// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC +// 1.04 (2015-04-15) typo in example +// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes +// 1.02 (2014-12-10) fix various warnings & compile issues w/ stb_rect_pack, C++ +// 1.01 (2014-12-08) fix subpixel position when oversampling to exactly match +// non-oversampled; STBTT_POINT_SIZE for packed case only +// 1.00 (2014-12-06) add new PackBegin etc. API, w/ support for oversampling +// 0.99 (2014-09-18) fix multiple bugs with subpixel rendering (ryg) +// 0.9 (2014-08-07) support certain mac/iOS fonts without an MS platformID +// 0.8b (2014-07-07) fix a warning +// 0.8 (2014-05-25) fix a few more warnings +// 0.7 (2013-09-25) bugfix: subpixel glyph bug fixed in 0.5 had come back +// 0.6c (2012-07-24) improve documentation +// 0.6b (2012-07-20) fix a few more warnings +// 0.6 (2012-07-17) fix warnings; added stbtt_ScaleForMappingEmToPixels, +// stbtt_GetFontBoundingBox, stbtt_IsGlyphEmpty +// 0.5 (2011-12-09) bugfixes: +// subpixel glyph renderer computed wrong bounding box +// first vertex of shape can be off-curve (FreeSans) +// 0.4b (2011-12-03) fixed an error in the font baking example +// 0.4 (2011-12-01) kerning, subpixel rendering (tor) +// bugfixes for: +// codepoint-to-glyph conversion using table fmt=12 +// codepoint-to-glyph conversion using table fmt=4 +// stbtt_GetBakedQuad with non-square texture (Zer) +// updated Hello World! sample to use kerning and subpixel +// fixed some warnings +// 0.3 (2009-06-24) cmap fmt=12, compound shapes (MM) +// userdata, malloc-from-userdata, non-zero fill (stb) +// 0.2 (2009-03-11) Fix unsigned/signed char warnings +// 0.1 (2009-03-09) First public release +// + +/* +------------------------------------------------------------------------------ +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. +------------------------------------------------------------------------------ +*/ \ No newline at end of file diff --git a/thirdparty/stb/src/zpl/zpl.h b/thirdparty/stb/src/zpl/zpl.h new file mode 100644 index 0000000..d90720a --- /dev/null +++ b/thirdparty/stb/src/zpl/zpl.h @@ -0,0 +1,19055 @@ +/** + zpl - Pushing the boundaries of simplicity. + +Usage: +# define ZPL_IMPLEMENTATION exactly in ONE source file right BEFORE including the library, like: + +# define ZPL_IMPLEMENTATION +# include "zpl.h" + + You can also use a lightweight version of zpl by using ZPL_NANO, like: + +# define ZPL_IMPLEMENTATION +# define ZPL_NANO +# include "zpl.h" + + There is also a distribution that provides only the essential modules, you can enable it by defining ZPL_PICO. + Currently, the distro offers: preprocessor helpers, debug module, memory API (except vm) and collections. + Some of these modules used to depend on zpl_printf, but they use the standard library if the distro is enabled now. + +# define ZPL_IMPLEMENTATION +# define ZPL_PICO +# include "zpl.h" + +Options: + ZPL_EXPOSE_TYPES - exposes all zpl defined types to the global namespace. This means type such as `zpl_u32` is now available as `u32` globally. + ZPL_DEFINE_NULL_MACRO - to let zpl define what NULL stands for in case it is undefined. + ZPL_NO_MATH_H - disables the use of math.h library and replaces it with custom routines or SIMD. + ZPL_HEAP_ANALYSIS - enables heap allocator analysis tools + ZPL_PARSER_DISABLE_ANALYSIS - disables the extra parsing logic that would collect more information about node's formatting and structure. + this is useful in scenarios where a raw parsing performance is preferred over a more complex analysis. + It is not recommended to serialise data back since we lack the extra information about the original source document. + +GitHub: + https://github.com/zpl-c/zpl + +Version History: + 19.5.0 - math: implemented hypot(x) function (Arin-Grigoras) + 19.4.1 - file: zpl_file_read_contents now ignores folders. + 19.4.0 - json: introduce support for ZPL_JSON_INDENT_STYLE_COMPACT output (rheatley-pervasid) + - fix SJSON value parse not detecting EOF correctly when analysing delimiter used. + example: "foo=123,bar=456" would produce 1 extra garbage field. + 19.3.1 - adt: zpl_adt_parse_number exit early on false hex value assumption + 19.3.0 - socket: new socket api cross-platform layer + 19.2.0 - file: add macros for standard i/o wrappers (ZPL_STDIO_IN, ...) + 19.1.1 - thread: return error values for POSIX calls + 19.1.0 - parser: introduce a new URI parser module + 19.0.4 - fix: zpl_buffer_copy_init missing macros (thanks Ed_ on Discord) + 19.0.3 - fix: zpl_str_skip_literal reading before start of string (mbenniston) + 19.0.2 - fixed ZPL_ISIZE_MIN and MAX for 32-bit architectures (mrossetti) + 19.0.1 - Fixed zpl_array_fill ZPL_ASSERT off-by-one error + 19.0.0 - Check all results of zpl_alloc() when using JSON parser/writer (rheatley-pervasid) + + 18.1.5 - set parent to parsed JSON nodes (fixed) + - fix zpl_json/csv_write_string off-by-one issue + 18.1.4 - fix zpl_random_gen_isize/zpl_random_range_isize 32bit overflow + 18.1.3 - set parent to parsed JSON nodes + 18.1.2 - fix zpl sort procs + 18.1.1 - updated table _clear method + 18.1.0 - added table _clear method + 18.0.4 - fix memory arena alignment & added tests + 18.0.3 - fix emscripten support + 18.0.2 - fix global-buffer-overflow in print module + - raise ZPL_PRINTF_MAXLEN to 64kb + 18.0.1 - fix ADT parser wrongly assuming that an IP address is a real number + 18.0.0 - removed coroutines module + - removed timer module + - rename zpl_adt_get -> zpl_adt_query + + 17.0.0 - ADT API changes + zpl_adt_inset_* -> zpl_adt_append_* + zpl_adt_node now holds a parent field, methods no longer require a pointer to the parent + methods are now documented + - add zpl_thread_init_nowait (gaopeng) + + 16.1.1 - fix scientific notation parsing + 16.1.0 - introduce ZPL_PARSER_DISABLE_ANALYSIS that disables extra parsing capabilities to offer better raw performance + at a cost of lack of node metadata. + 16.0.0 - introduce a new zpl_adt_query method for flexible data retrieval + "a/b/c" navigates through objects "a" and "b" to get to "c" + "arr/[foo=123]/bar" iterates over "arr" to find any object with param "foo" that matches the value "123", then gets its field called "bar" + "arr/3" retrieves the 4th element in "arr" + "arr/[apple]" retrieves the first element of value "apple" in "arr" + - fix memory leak when parsing a json array (gaopeng) + - add zpl_strntok (gaopeng) + - add zpl_semaphore_trywait (gaopeng) + + 15.0.3 - fix zpl_sign call in math failing to compile + on macos devices + 15.0.2 - zpl_sign0 was introduced + 15.0.1 - hashtable performance improvements + - zpl_sign(0) returns 0 + 15.0.0 - Rework zpl ring buffer + - various code improvements + + 14.1.7 - fix zpl_random_range_i64 + - set thread's is_running before we start a thread + 14.1.6 - remove windows.h dependency for header part + 14.1.5 - fix array append_at + 14.1.4 - Fix win32 missing CRITICAL_SECTION definition if + - ZPL_NO_WINDOWS_H is defined + 14.1.0 - add hashtable map_mut method + 14.0.1 - fix zpl_array_remove_at boundary bug + 14.0.0 - heap memory allocator analysis + + 13.4.1 - adt optimizations + 13.4.0 - new adt manipulation methods + 13.3.3 - fix zpl_str_skip_literal bug + 13.3.2 - escape strings in parser output + 13.3.1 - number parsing improvements + 13.3.0 - csv parse numbers + 13.2.0 - hashtable _map function + 13.1.5 - ZPL_DEBUG_TRAP for tcc + 13.1.4 - potential csv ub fix + 13.1.3 - tcc support improvements + 13.1.2 - fix ast -> adt filename + 13.1.1 - fix emscripten support + 13.1.0 - abstract data tree naming update + 13.0.0 - text parsers refactor + + 12.8.0 - zpl_opts improvements + 12.7.0 - math improvements + 12.6.2 - remove register usage (BeastLe9enD) + 12.6.1 - improve tar null padding code + 12.6.0 - introduce zpl_align_forward_u64/i64 + 12.5.1 - small type casting fixes + 12.5.0 - add zpl_asprintf + 12.4.0 - zpl_printf improvements + 12.3.2 - allow zpl_path_dirlist to include symlinks, but don't enter them + 12.3.1 - avoid symbolic link cycle in zpl_path_dirlist + 12.3.0 - add TAR archiving support + 12.2.1 - fix zpl_random_gen_f64 + 12.2.0 - Add zpl_array_fill and zpl_array_appendv_at + 12.1.0 - Add rectangle partitioning + 12.0.1 - Optimize zpl_strlen + 12.0.0 - JSON API revamp + improvements + + 11.3.0 - JSON zpl_json_str_to_flt + cleanup + 11.2.5 - fix small atomics typo + 11.2.4 - JSON rewrite core parser + 11.2.2 - JSON rewrite comment handling + 11.2.1 - JSON zero-initialise node + 11.2.0 - JSON API improvements + 11.1.2 - Improve atomics + 11.1.1 - Fix zpl_json_write_string providing incorrect length + 11.1.0 - Introduce new ZPL_PICO distro + 11.0.11 - remove stdatomic.h include + 11.0.10 - get rid of c11 atomics lib + 11.0.9 - stringlib uses ZPL_PRINTF_MAXLEN now + - zpl_printf family is now thread-safe + 11.0.7 - Add ZPL_PRINTF_MAXLEN + 11.0.6 - Fix zpl_printf left padding bug + 11.0.4 - Disable ZPL_NO_MATH_H on TinyC + 11.0.3 - Added support for TinyC compiler + 11.0.2 - Fixes for Apple M1 chip + 11.0.0 - New jobs system + - Rewrite the timer module + - zpl_ring rework + + 10.13.0 - Initial ARM threading support + 10.12.1 - Fix missing zpL_alloc_str + 10.12.0 - Add zpl_crc64 + 10.11.1 - Fix zpl_time_utc_ms on 32-bit OSes + 10.11.0 - Added zpl_file_stream_buf + 10.10.3 - Math type-punning fixes + 10.10.1 - Fix memory writing issue + new write-only in-situ flag + 10.10.0 - Implement memory streaming API + 10.9.1 - Support ARMv6, ARMv7 and ARMv8-a builds + 10.9.0 - Improve the time API + 10.8.3 - zpl_file_close tempfile Windows fixes + 10.8.2 - zpl_file_temp disallow some operations + 10.8.1 - zpl_file_temp Windows fixes + 10.8.0 - Implemented zpl_json_write_string + 10.7.1 - Fix zpl_file_temp platform bug + 10.7.0 - Add zpl_file_write_contents + 10.6.6 - Fix type mismatch in Jobs system + 10.6.0 - Remove event system + 10.5.8 - Remove zpl__memcpy_4byte + 10.5.7 - zpl_file_new is now OS-agnostic constructor + 10.5.6 - Fix coroutine creation + 10.5.5 - Jobs system uses zpl_f32 for priority setting + 10.5.4 - zpl_buffer_free no longer takes the 2nd argument (allocator) + 10.5.3 - Removed crc64 and annotated some hashing methods + 10.5.2 - Don't expose ZPL types anymore + 10.5.1 - Fixed zpl_rdtsc for Emscripten + 10.5.0 - Changed casts to memcopy in random methods, added embed cmd + 10.4.1 - Jobs system now enqueues jobs with def priority of 1.0 + 10.4.0 - [META] version bump + 10.3.0 - Pool allocator now supports zpl_free_all + 10.2.0 - [META] version bump + 10.1.0 - Additional math methods (thanks to funZX and msmshazan) + 10.0.15 - WIP Emscripten fixes + 10.0.14 - FreeBSD support + 10.0.13 - OpenBSD support + 10.0.12 - Cygwin fixes + 10.0.11 - Tweak module dependencies + 10.0.10 - Fix zero-allocation regression in filesystem module + 10.0.9 - Fix multi-compilation unit builds + 10.0.8 - Fix zpl_printf "%0d" format specifier + 10.0.4 - Flush tester output to fix ordering + 10.0.3 - Fix ZPL_STATIC_ASSERT under MSVC + 10.0.0 - Major overhaul of the library + + 9.8.10 - JSON fix array-based documents with objects + 9.8.9 - JSON document structured as array now properly recognizes the root object as array. + 9.8.8 - Fixed an incorrect parsing of empty array nodes. + 9.8.7 - Improve FreeBSD support + 9.8.6 - WIP: Handle inlined methods properly + 9.8.5 - Fix incorrect usage of EOF and opts dependency on JSON5 module's methods + 9.8.4 - Fix MSVC ZPL_NO_MATH_H code branch using incorrect methods internally + 9.8.3 - Fix MinGW GCC related issue with zpl_printf %lld format + 9.8.2 - Fix VS C4190 issue + 9.8.1 - Fix several C++ type casting quirks + 9.8.0 - Incorporated OpenGL into ZPL core as an optional module + 9.7.0 - Added co-routine module + 9.6.0 - Added process module for creation and manipulation + 9.5.2 - zpl_printf family now prints (null) on NULL string arguments + 9.5.1 - Fixed JSON5 real number export support + indentation fixes + 9.5.0 - Added base64 encode/decode methods + 9.4.10- Small enum style changes + 9.4.9 - Remove #undef for cast and hard_cast (sorry) + 9.4.8 - Fix quote-less JSON node name resolution + 9.4.7 - Additional change to the code + 9.4.6 - Fix issue where zpl_json_find would have false match on substrings + 9.4.5 - Mistakes were made, fixed compilation errors + 9.4.3 - Fix old API shenanigans + 9.4.2 - Fix small API typos + 9.4.1 - Reordered JSON5 constants to integrate better with conditions + 9.4.0 - JSON5 API changes made to zpl_json_find + 9.3.0 - Change how zpl uses basic types internally + 9.2.0 - Directory listing was added. Check dirlist_api.c test for more info + 9.1.1 - Fix WIN32_LEAN_AND_MEAN redefinition properly + 9.1.0 - get_env rework and fixes + 9.0.3 - Small fixes and removals + 9.0.0 - New documentation format, removed deprecated code, changed styles + + 8.14.1 - Fix string library + 8.14.0 - Added zpl_re_match_all + 8.13.0 - Update system command API + 8.12.6 - Fix warning in CLI options parser + 8.12.5 - Support parametric options preceding positionals + 8.12.4 - Fixed opts positionals ordering + 8.12.3 - Fixed incorrect handling of flags preceding positionals + 8.12.2 - JSON parsing remark added + 8.12.1 - Fixed a lot of important stuff + 8.12.0 - Added helper constructors for containers + 8.11.2 - Fix bug in opts module + 8.11.1 - Small code improvements + 8.11.0 - Ported regex processor from https://github.com/gingerBill/gb/ and applied fixes on top of it + 8.10.2 - Fix zpl_strtok + 8.10.1 - Replace zpl_strchr by zpl_char_last_occurence + 8.10.0 - Added zpl_strchr + 8.9.0 - API improvements for JSON5 parser + 8.8.4 - Add support for SJSON formatting http://bitsquid.blogspot.com/2009/10/simplified-json-notation.html + + 6.8.3 - JSON5 exp fix + 6.8.2 - Bugfixes applied from gb + 6.8.1 - Performance improvements for JSON5 parser + 6.8.0 - zpl.h is now generated by build.py + 6.7.0 - Several fixes and added switches + 6.6.0 - Several significant changes made to the repository + 6.5.0 - Ported platform layer from https://github.com/gingerBill/gb/ + 6.4.1 - Use zpl_strlen in zpl_strdup + 6.4.0 - Deprecated zpl_buffer_free and added zpl_array_end, zpl_buffer_end + 6.3.0 - Added zpl_strdup + 6.2.1 - Remove math redundancies + 6.2.0 - Integrated zpl_math.h into zpl.h + 6.1.1 - Added direct.h include for win c++ dir methods + 6.1.0 - Added zpl_path_mkdir, zpl_path_rmdir, and few new zplFileErrors + 6.0.4 - More MSVC(++) satisfaction by fixing warnings + 6.0.3 - Satisfy MSVC by fixing a warning + 6.0.2 - Fixed warnings for json5 i64 printfs + 6.0.1 - Fixed warnings for particual win compiler in dirlist method + 6.0.0 - New build, include/ was renamed to code/ + + 5.8.3 - Naming fixes + 5.8.2 - Job system now supports prioritized tasks + 5.8.1 - Renames zpl_pad to zpl_ring + 5.8.0 - Added instantiated scratch pad (circular buffer) + 5.7.2 - Added Windows support for zpl_path_dirlist + 5.7.1 - Fixed few things in job system + macOS support for zpl_path_dirlist + 5.7.0 - Added a job system (zpl_thread_pool) + 5.6.5 - Fixes extra error cases for zpl_opts when input is: + - missing a value for an option, + - having an extra value for a flag (e.g. --enable-log shouldn't get a value.) + 5.6.4 - Several tweaks to the zpl_opts API + 5.6.3 - Added support for flags without values + 5.6.2 - Improve error handling for zpl_opts + 5.6.1 - Added support for strings with spaces in zpl_opts + 5.6.0 - Added zpl_opts for CLI argument parsing + 5.5.1 - Fixed time method for win + 5.5.0 - Integrate JSON5 writer into the core + 5.4.0 - Improved storage support for numbers in JSON5 parser + 5.3.0 - Integrated zpl_json into ZPL + 5.2.0 - Added zpl_string_sprintf + 5.1.1 - Added zpl_system_command_nores for output-less execution + 5.1.0 - Added event handler + 5.0.4 - Fix alias for zpl_list + 5.0.3 - Finalizing syntax changes + 5.0.2 - Fix segfault when using zpl_stack_memory + 5.0.1 - Small code improvements + 5.0.0 - Project structure changes + + 4.7.2 - Got rid of size arg for zpl_str_split_lines + 4.7.1 - Added an example + 4.7.0 - Added zpl_path_dirlist + 4.6.1 - zpl_memcopy x86 patch from upstream + 4.6.0 - Added few string-related functions + 4.5.9 - Error fixes + 4.5.8 - Warning fixes + 4.5.7 - Fixed timer loops. zpl_time* related functions work with seconds now + 4.5.6 - Fixed zpl_time_now() for Windows and Linux + 4.5.5 - Small cosmetic changes + 4.5.4 - Fixed issue when zpl_list_add would break the links + - when adding a new item between nodes + 4.5.3 - Fixed malformed enum values + 4.5.1 - Fixed some warnings + 4.5.0 - Added zpl_array_append_at + 4.4.0 - Added zpl_array_back, zpl_array_front + 4.3.0 - Added zpl_list + 4.2.0 - Added zpl_system_command_str + 4.1.2 - GG, fixed small compilation error + 4.1.1 - Fixed possible security issue in zpl_system_command + 4.1.0 - Added zpl_string_make_reserve and small fixes + 4.0.2 - Warning fix for _LARGEFILE64_SOURCE + 4.0.1 - include stdlib.h for getenv (temp) + 4.0.0 - ARM support, coding style changes and various improvements + + 3.4.1 - zpl_memcopy now uses memcpy for ARM arch-family + 3.4.0 - Removed obsolete code + 3.3.4 - Added Travis CI config + 3.3.3 - Small macro formatting changes + ZPL_SYSTEM_IOS + 3.3.2 - Fixes for android arm + 3.3.1 - Fixed some type cast warnings + 3.3.0 - Added Android support + 3.1.5 - Renamed userptr to user_data in timer + 3.1.4 - Fix for zpl_buffer not allocating correctly + 3.1.2 - Small fix in zpl_memcompare + 3.1.1 - Added char* conversion for data field in zpl_array_header + 3.1.0 - Added data field to zpl_array_header + 3.0.7 - Added timer userptr as argument to callback + 3.0.6 - Small changes + 3.0.5 - Fixed compilation for emscripten + 3.0.4 - Small fixes for tiny cpp warnings + 3.0.3 - Small fixes for various cpp warnings and errors + 3.0.2 - Fixed linux part, and removed trailing spaces + 3.0.1 - Small bugfix in zpl_file_open + 3.0.0 - Added several fixes and features + + 2.4.0 - Added remove to hash table + 2.3.3 - Removed redundant code + 2.3.2 - Eliminated extra warnings + 2.3.1 - Warning hunt + 2.3.0 - Added the ability to copy array/buffer and fixed bug in hash table. + 2.2.1 - Used tmpfile() for Windows + 2.2.0 - Added zpl_file_temp + 2.1.1 - Very small fix (forgive me) + 2.1.0 - Added the ability to resize bitstream + 2.0.8 - Small adjustments + 2.0.7 - MinGW related fixes + 2.0.0 - New NPM based version + + 1.2.2 - Small fix + 1.2.1 - Macro fixes + 1.2.0 - Added zpl_async macro + 1.1.0 - Added timer feature + 1.0.0 - Initial version + + +License: + This Software is dual licensed under the following licenses: + + Unlicense + 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. + + For more information, please refer to + + BSD 3-Clause + Copyright (c) 2016-2021 Dominik Madarász. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + 3. Neither the name of the copyright holder nor the names of its contributors + may be used to endorse or promote products derived from this software without + specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef ZPL_H +#define ZPL_H + +#define ZPL_VERSION_MAJOR 19 +#define ZPL_VERSION_MINOR 5 +#define ZPL_VERSION_PATCH 0 +#define ZPL_VERSION_PRE "" + + // file: zpl_hedley.h + + /* Hedley - https://nemequ.github.io/hedley + * Created by Evan Nemerson + * + * To the extent possible under law, the author(s) have dedicated all + * copyright and related and neighboring rights to this software to + * the public domain worldwide. This software is distributed without + * any warranty. + * + * For details, see . + * SPDX-License-Identifier: CC0-1.0 + */ + + #if !defined(ZPL_HEDLEY_VERSION) || (ZPL_HEDLEY_VERSION < 12) + #if defined(ZPL_HEDLEY_VERSION) + # undef ZPL_HEDLEY_VERSION + #endif + #define ZPL_HEDLEY_VERSION 12 + + #if defined(ZPL_STRINGIFY_EX) + # undef ZPL_STRINGIFY_EX + #endif + #define ZPL_STRINGIFY_EX(x) #x + + #if defined(ZPL_STRINGIFY) + # undef ZPL_STRINGIFY + #endif + #define ZPL_STRINGIFY(x) ZPL_STRINGIFY_EX(x) + + #if defined(ZPL_CONCAT_EX) + # undef ZPL_CONCAT_EX + #endif + #define ZPL_CONCAT_EX(a,b) a##b + + #if defined(ZPL_CONCAT) + # undef ZPL_CONCAT + #endif + #define ZPL_CONCAT(a,b) ZPL_CONCAT_EX(a,b) + + #if defined(ZPL_VERSION_ENCODE) + # undef ZPL_VERSION_ENCODE + #endif + #define ZPL_VERSION_ENCODE(major,minor,patch) (((major) * 1000000) + ((minor) * 1000) + (patch)) + + #if defined(ZPL_VERSION_DECODE_MAJOR) + # undef ZPL_VERSION_DECODE_MAJOR + #endif + #define ZPL_VERSION_DECODE_MAJOR(version) ((version) / 1000000) + + #if defined(ZPL_VERSION_DECODE_MINOR) + # undef ZPL_VERSION_DECODE_MINOR + #endif + #define ZPL_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000) + + #if defined(ZPL_VERSION_DECODE_PATCH) + # undef ZPL_VERSION_DECODE_PATCH + #endif + #define ZPL_VERSION_DECODE_PATCH(version) ((version) % 1000) + + #if defined(ZPL_VERSION_CHECK) + # undef ZPL_VERSION_CHECK + #endif + #define ZPL_VERSION_CHECK(major,minor,patch) (ZPL_VERSION_ENCODE(major,minor,patch) <= ZPL_VERSION) + + #if defined(ZPL_GNUC_VERSION) + # undef ZPL_GNUC_VERSION + #endif + #if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__) + # define ZPL_GNUC_VERSION ZPL_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) + #elif defined(__GNUC__) + # define ZPL_GNUC_VERSION ZPL_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0) + #endif + + #if defined(ZPL_GNUC_VERSION_CHECK) + # undef ZPL_GNUC_VERSION_CHECK + #endif + #if defined(ZPL_GNUC_VERSION) + # define ZPL_GNUC_VERSION_CHECK(major,minor,patch) (ZPL_GNUC_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_GNUC_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_MSVC_VERSION) + # undef ZPL_MSVC_VERSION + #endif + #if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000) + # define ZPL_MSVC_VERSION ZPL_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100) + #elif defined(_MSC_FULL_VER) + # define ZPL_MSVC_VERSION ZPL_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10) + #elif defined(_MSC_VER) + # define ZPL_MSVC_VERSION ZPL_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0) + #endif + + #if defined(ZPL_MSVC_VERSION_CHECK) + # undef ZPL_MSVC_VERSION_CHECK + #endif + #if !defined(_MSC_VER) + # define ZPL_MSVC_VERSION_CHECK(major,minor,patch) (0) + #elif defined(_MSC_VER) && (_MSC_VER >= 1400) + # define ZPL_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch))) + #elif defined(_MSC_VER) && (_MSC_VER >= 1200) + # define ZPL_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch))) + #else + # define ZPL_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor))) + #endif + + #if defined(ZPL_INTEL_VERSION) + # undef ZPL_INTEL_VERSION + #endif + #if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE) + # define ZPL_INTEL_VERSION ZPL_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE) + #elif defined(__INTEL_COMPILER) + # define ZPL_INTEL_VERSION ZPL_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0) + #endif + + #if defined(ZPL_INTEL_VERSION_CHECK) + # undef ZPL_INTEL_VERSION_CHECK + #endif + #if defined(ZPL_INTEL_VERSION) + # define ZPL_INTEL_VERSION_CHECK(major,minor,patch) (ZPL_INTEL_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_INTEL_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_PGI_VERSION) + # undef ZPL_PGI_VERSION + #endif + #if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__) + # define ZPL_PGI_VERSION ZPL_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__) + #endif + + #if defined(ZPL_PGI_VERSION_CHECK) + # undef ZPL_PGI_VERSION_CHECK + #endif + #if defined(ZPL_PGI_VERSION) + # define ZPL_PGI_VERSION_CHECK(major,minor,patch) (ZPL_PGI_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_PGI_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_SUNPRO_VERSION) + # undef ZPL_SUNPRO_VERSION + #endif + #if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000) + # define ZPL_SUNPRO_VERSION ZPL_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10) + #elif defined(__SUNPRO_C) + # define ZPL_SUNPRO_VERSION ZPL_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf) + #elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000) + # define ZPL_SUNPRO_VERSION ZPL_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10) + #elif defined(__SUNPRO_CC) + # define ZPL_SUNPRO_VERSION ZPL_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf) + #endif + + #if defined(ZPL_SUNPRO_VERSION_CHECK) + # undef ZPL_SUNPRO_VERSION_CHECK + #endif + #if defined(ZPL_SUNPRO_VERSION) + # define ZPL_SUNPRO_VERSION_CHECK(major,minor,patch) (ZPL_SUNPRO_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_SUNPRO_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_EMSCRIPTEN_VERSION) + # undef ZPL_EMSCRIPTEN_VERSION + #endif + #if defined(__EMSCRIPTEN__) + # define ZPL_EMSCRIPTEN_VERSION ZPL_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__) + #endif + + #if defined(ZPL_EMSCRIPTEN_VERSION_CHECK) + # undef ZPL_EMSCRIPTEN_VERSION_CHECK + #endif + #if defined(ZPL_EMSCRIPTEN_VERSION) + # define ZPL_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (ZPL_EMSCRIPTEN_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_ARM_VERSION) + # undef ZPL_ARM_VERSION + #endif + #if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION) + # define ZPL_ARM_VERSION ZPL_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100) + #elif defined(__CC_ARM) && defined(__ARMCC_VERSION) + # define ZPL_ARM_VERSION ZPL_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100) + #endif + + #if defined(ZPL_ARM_VERSION_CHECK) + # undef ZPL_ARM_VERSION_CHECK + #endif + #if defined(ZPL_ARM_VERSION) + # define ZPL_ARM_VERSION_CHECK(major,minor,patch) (ZPL_ARM_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_ARM_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_IBM_VERSION) + # undef ZPL_IBM_VERSION + #endif + #if defined(__ibmxl__) + # define ZPL_IBM_VERSION ZPL_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__) + #elif defined(__xlC__) && defined(__xlC_ver__) + # define ZPL_IBM_VERSION ZPL_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff) + #elif defined(__xlC__) + # define ZPL_IBM_VERSION ZPL_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0) + #endif + + #if defined(ZPL_IBM_VERSION_CHECK) + # undef ZPL_IBM_VERSION_CHECK + #endif + #if defined(ZPL_IBM_VERSION) + # define ZPL_IBM_VERSION_CHECK(major,minor,patch) (ZPL_IBM_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_IBM_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_VERSION) + # undef ZPL_TI_VERSION + #endif + #if \ + defined(__TI_COMPILER_VERSION__) && \ + ( \ + defined(__TMS470__) || defined(__TI_ARM__) || \ + defined(__MSP430__) || \ + defined(__TMS320C2000__) \ + ) + # if (__TI_COMPILER_VERSION__ >= 16000000) + # define ZPL_TI_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + # endif + #endif + + #if defined(ZPL_TI_VERSION_CHECK) + # undef ZPL_TI_VERSION_CHECK + #endif + #if defined(ZPL_TI_VERSION) + # define ZPL_TI_VERSION_CHECK(major,minor,patch) (ZPL_TI_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_CL2000_VERSION) + # undef ZPL_TI_CL2000_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C2000__) + # define ZPL_TI_CL2000_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_CL2000_VERSION_CHECK) + # undef ZPL_TI_CL2000_VERSION_CHECK + #endif + #if defined(ZPL_TI_CL2000_VERSION) + # define ZPL_TI_CL2000_VERSION_CHECK(major,minor,patch) (ZPL_TI_CL2000_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_CL2000_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_CL430_VERSION) + # undef ZPL_TI_CL430_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && defined(__MSP430__) + # define ZPL_TI_CL430_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_CL430_VERSION_CHECK) + # undef ZPL_TI_CL430_VERSION_CHECK + #endif + #if defined(ZPL_TI_CL430_VERSION) + # define ZPL_TI_CL430_VERSION_CHECK(major,minor,patch) (ZPL_TI_CL430_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_CL430_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_ARMCL_VERSION) + # undef ZPL_TI_ARMCL_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && (defined(__TMS470__) || defined(__TI_ARM__)) + # define ZPL_TI_ARMCL_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_ARMCL_VERSION_CHECK) + # undef ZPL_TI_ARMCL_VERSION_CHECK + #endif + #if defined(ZPL_TI_ARMCL_VERSION) + # define ZPL_TI_ARMCL_VERSION_CHECK(major,minor,patch) (ZPL_TI_ARMCL_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_ARMCL_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_CL6X_VERSION) + # undef ZPL_TI_CL6X_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && defined(__TMS320C6X__) + # define ZPL_TI_CL6X_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_CL6X_VERSION_CHECK) + # undef ZPL_TI_CL6X_VERSION_CHECK + #endif + #if defined(ZPL_TI_CL6X_VERSION) + # define ZPL_TI_CL6X_VERSION_CHECK(major,minor,patch) (ZPL_TI_CL6X_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_CL6X_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_CL7X_VERSION) + # undef ZPL_TI_CL7X_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && defined(__C7000__) + # define ZPL_TI_CL7X_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_CL7X_VERSION_CHECK) + # undef ZPL_TI_CL7X_VERSION_CHECK + #endif + #if defined(ZPL_TI_CL7X_VERSION) + # define ZPL_TI_CL7X_VERSION_CHECK(major,minor,patch) (ZPL_TI_CL7X_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_CL7X_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TI_CLPRU_VERSION) + # undef ZPL_TI_CLPRU_VERSION + #endif + #if defined(__TI_COMPILER_VERSION__) && defined(__PRU__) + # define ZPL_TI_CLPRU_VERSION ZPL_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000)) + #endif + + #if defined(ZPL_TI_CLPRU_VERSION_CHECK) + # undef ZPL_TI_CLPRU_VERSION_CHECK + #endif + #if defined(ZPL_TI_CLPRU_VERSION) + # define ZPL_TI_CLPRU_VERSION_CHECK(major,minor,patch) (ZPL_TI_CLPRU_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TI_CLPRU_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_CRAY_VERSION) + # undef ZPL_CRAY_VERSION + #endif + #if defined(_CRAYC) + # if defined(_RELEASE_PATCHLEVEL) + # define ZPL_CRAY_VERSION ZPL_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL) + # else + # define ZPL_CRAY_VERSION ZPL_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0) + # endif + #endif + + #if defined(ZPL_CRAY_VERSION_CHECK) + # undef ZPL_CRAY_VERSION_CHECK + #endif + #if defined(ZPL_CRAY_VERSION) + # define ZPL_CRAY_VERSION_CHECK(major,minor,patch) (ZPL_CRAY_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_CRAY_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_IAR_VERSION) + # undef ZPL_IAR_VERSION + #endif + #if defined(__IAR_SYSTEMS_ICC__) + # if __VER__ > 1000 + # define ZPL_IAR_VERSION ZPL_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000)) + # else + # define ZPL_IAR_VERSION ZPL_VERSION_ENCODE(VER / 100, __VER__ % 100, 0) + # endif + #endif + + #if defined(ZPL_IAR_VERSION_CHECK) + # undef ZPL_IAR_VERSION_CHECK + #endif + #if defined(ZPL_IAR_VERSION) + # define ZPL_IAR_VERSION_CHECK(major,minor,patch) (ZPL_IAR_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_IAR_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_TINYC_VERSION) + # undef ZPL_TINYC_VERSION + #endif + #if defined(__TINYC__) + # define ZPL_TINYC_VERSION ZPL_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100) + #endif + + #if defined(ZPL_TINYC_VERSION_CHECK) + # undef ZPL_TINYC_VERSION_CHECK + #endif + #if defined(ZPL_TINYC_VERSION) + # define ZPL_TINYC_VERSION_CHECK(major,minor,patch) (ZPL_TINYC_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_TINYC_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_DMC_VERSION) + # undef ZPL_DMC_VERSION + #endif + #if defined(__DMC__) + # define ZPL_DMC_VERSION ZPL_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf) + #endif + + #if defined(ZPL_DMC_VERSION_CHECK) + # undef ZPL_DMC_VERSION_CHECK + #endif + #if defined(ZPL_DMC_VERSION) + # define ZPL_DMC_VERSION_CHECK(major,minor,patch) (ZPL_DMC_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_DMC_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_COMPCERT_VERSION) + # undef ZPL_COMPCERT_VERSION + #endif + #if defined(__COMPCERT_VERSION__) + # define ZPL_COMPCERT_VERSION ZPL_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100) + #endif + + #if defined(ZPL_COMPCERT_VERSION_CHECK) + # undef ZPL_COMPCERT_VERSION_CHECK + #endif + #if defined(ZPL_COMPCERT_VERSION) + # define ZPL_COMPCERT_VERSION_CHECK(major,minor,patch) (ZPL_COMPCERT_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_COMPCERT_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_PELLES_VERSION) + # undef ZPL_PELLES_VERSION + #endif + #if defined(__POCC__) + # define ZPL_PELLES_VERSION ZPL_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0) + #endif + + #if defined(ZPL_PELLES_VERSION_CHECK) + # undef ZPL_PELLES_VERSION_CHECK + #endif + #if defined(ZPL_PELLES_VERSION) + # define ZPL_PELLES_VERSION_CHECK(major,minor,patch) (ZPL_PELLES_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_PELLES_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_GCC_VERSION) + # undef ZPL_GCC_VERSION + #endif + #if \ + defined(ZPL_GNUC_VERSION) && \ + !defined(__clang__) && \ + !defined(ZPL_INTEL_VERSION) && \ + !defined(ZPL_PGI_VERSION) && \ + !defined(ZPL_ARM_VERSION) && \ + !defined(ZPL_TI_VERSION) && \ + !defined(ZPL_TI_ARMCL_VERSION) && \ + !defined(ZPL_TI_CL430_VERSION) && \ + !defined(ZPL_TI_CL2000_VERSION) && \ + !defined(ZPL_TI_CL6X_VERSION) && \ + !defined(ZPL_TI_CL7X_VERSION) && \ + !defined(ZPL_TI_CLPRU_VERSION) && \ + !defined(__COMPCERT__) + # define ZPL_GCC_VERSION ZPL_GNUC_VERSION + #endif + + #if defined(ZPL_GCC_VERSION_CHECK) + # undef ZPL_GCC_VERSION_CHECK + #endif + #if defined(ZPL_GCC_VERSION) + # define ZPL_GCC_VERSION_CHECK(major,minor,patch) (ZPL_GCC_VERSION >= ZPL_VERSION_ENCODE(major, minor, patch)) + #else + # define ZPL_GCC_VERSION_CHECK(major,minor,patch) (0) + #endif + + #if defined(ZPL_HAS_ATTRIBUTE) + # undef ZPL_HAS_ATTRIBUTE + #endif + #if defined(__has_attribute) + # define ZPL_HAS_ATTRIBUTE(attribute) __has_attribute(attribute) + #else + # define ZPL_HAS_ATTRIBUTE(attribute) (0) + #endif + + #if defined(ZPL_GNUC_HAS_ATTRIBUTE) + # undef ZPL_GNUC_HAS_ATTRIBUTE + #endif + #if defined(__has_attribute) + # define ZPL_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) + #else + # define ZPL_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_ATTRIBUTE) + # undef ZPL_GCC_HAS_ATTRIBUTE + #endif + #if defined(__has_attribute) + # define ZPL_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute) + #else + # define ZPL_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_CPP_ATTRIBUTE) + # undef ZPL_HAS_CPP_ATTRIBUTE + #endif + #if \ + defined(__has_cpp_attribute) && \ + defined(__cplusplus) && \ + (!defined(ZPL_SUNPRO_VERSION) || ZPL_SUNPRO_VERSION_CHECK(5,15,0)) + # define ZPL_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute) + #else + # define ZPL_HAS_CPP_ATTRIBUTE(attribute) (0) + #endif + + #if defined(ZPL_HAS_CPP_ATTRIBUTE_NS) + # undef ZPL_HAS_CPP_ATTRIBUTE_NS + #endif + #if !defined(__cplusplus) || !defined(__has_cpp_attribute) + # define ZPL_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) + #elif \ + !defined(ZPL_PGI_VERSION) && \ + !defined(ZPL_IAR_VERSION) && \ + (!defined(ZPL_SUNPRO_VERSION) || ZPL_SUNPRO_VERSION_CHECK(5,15,0)) && \ + (!defined(ZPL_MSVC_VERSION) || ZPL_MSVC_VERSION_CHECK(19,20,0)) + # define ZPL_HAS_CPP_ATTRIBUTE_NS(ns,attribute) ZPL_HAS_CPP_ATTRIBUTE(ns::attribute) + #else + # define ZPL_HAS_CPP_ATTRIBUTE_NS(ns,attribute) (0) + #endif + + #if defined(ZPL_GNUC_HAS_CPP_ATTRIBUTE) + # undef ZPL_GNUC_HAS_CPP_ATTRIBUTE + #endif + #if defined(__has_cpp_attribute) && defined(__cplusplus) + # define ZPL_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) + #else + # define ZPL_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_CPP_ATTRIBUTE) + # undef ZPL_GCC_HAS_CPP_ATTRIBUTE + #endif + #if defined(__has_cpp_attribute) && defined(__cplusplus) + # define ZPL_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute) + #else + # define ZPL_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_BUILTIN) + # undef ZPL_HAS_BUILTIN + #endif + #if defined(__has_builtin) + # define ZPL_HAS_BUILTIN(builtin) __has_builtin(builtin) + #else + # define ZPL_HAS_BUILTIN(builtin) (0) + #endif + + #if defined(ZPL_GNUC_HAS_BUILTIN) + # undef ZPL_GNUC_HAS_BUILTIN + #endif + #if defined(__has_builtin) + # define ZPL_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) + #else + # define ZPL_GNUC_HAS_BUILTIN(builtin,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_BUILTIN) + # undef ZPL_GCC_HAS_BUILTIN + #endif + #if defined(__has_builtin) + # define ZPL_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin) + #else + # define ZPL_GCC_HAS_BUILTIN(builtin,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_FEATURE) + # undef ZPL_HAS_FEATURE + #endif + #if defined(__has_feature) + # define ZPL_HAS_FEATURE(feature) __has_feature(feature) + #else + # define ZPL_HAS_FEATURE(feature) (0) + #endif + + #if defined(ZPL_GNUC_HAS_FEATURE) + # undef ZPL_GNUC_HAS_FEATURE + #endif + #if defined(__has_feature) + # define ZPL_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) + #else + # define ZPL_GNUC_HAS_FEATURE(feature,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_FEATURE) + # undef ZPL_GCC_HAS_FEATURE + #endif + #if defined(__has_feature) + # define ZPL_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature) + #else + # define ZPL_GCC_HAS_FEATURE(feature,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_EXTENSION) + # undef ZPL_HAS_EXTENSION + #endif + #if defined(__has_extension) + # define ZPL_HAS_EXTENSION(extension) __has_extension(extension) + #else + # define ZPL_HAS_EXTENSION(extension) (0) + #endif + + #if defined(ZPL_GNUC_HAS_EXTENSION) + # undef ZPL_GNUC_HAS_EXTENSION + #endif + #if defined(__has_extension) + # define ZPL_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) + #else + # define ZPL_GNUC_HAS_EXTENSION(extension,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_EXTENSION) + # undef ZPL_GCC_HAS_EXTENSION + #endif + #if defined(__has_extension) + # define ZPL_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension) + #else + # define ZPL_GCC_HAS_EXTENSION(extension,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_DECLSPEC_ATTRIBUTE) + # undef ZPL_HAS_DECLSPEC_ATTRIBUTE + #endif + #if defined(__has_declspec_attribute) + # define ZPL_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute) + #else + # define ZPL_HAS_DECLSPEC_ATTRIBUTE(attribute) (0) + #endif + + #if defined(ZPL_GNUC_HAS_DECLSPEC_ATTRIBUTE) + # undef ZPL_GNUC_HAS_DECLSPEC_ATTRIBUTE + #endif + #if defined(__has_declspec_attribute) + # define ZPL_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) + #else + # define ZPL_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_DECLSPEC_ATTRIBUTE) + # undef ZPL_GCC_HAS_DECLSPEC_ATTRIBUTE + #endif + #if defined(__has_declspec_attribute) + # define ZPL_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute) + #else + # define ZPL_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_HAS_WARNING) + # undef ZPL_HAS_WARNING + #endif + #if defined(__has_warning) + # define ZPL_HAS_WARNING(warning) __has_warning(warning) + #else + # define ZPL_HAS_WARNING(warning) (0) + #endif + + #if defined(ZPL_GNUC_HAS_WARNING) + # undef ZPL_GNUC_HAS_WARNING + #endif + #if defined(__has_warning) + # define ZPL_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) + #else + # define ZPL_GNUC_HAS_WARNING(warning,major,minor,patch) ZPL_GNUC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_GCC_HAS_WARNING) + # undef ZPL_GCC_HAS_WARNING + #endif + #if defined(__has_warning) + # define ZPL_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning) + #else + # define ZPL_GCC_HAS_WARNING(warning,major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + /* ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ is for + ZPL INTERNAL USE ONLY. API subject to change without notice. */ + #if defined(ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + # undef ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_ + #endif + #if defined(__cplusplus) + # if ZPL_HAS_WARNING("-Wc++98-compat") + # if ZPL_HAS_WARNING("-Wc++17-extensions") + # define ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + _Pragma("clang diagnostic ignored \"-Wc++17-extensions\"") \ + xpr \ + ZPL_DIAGNOSTIC_POP + # else + # define ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(xpr) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wc++98-compat\"") \ + xpr \ + ZPL_DIAGNOSTIC_POP + # endif + # endif + #endif + #if !defined(ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_) + # define ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(x) x + #endif + + #if defined(ZPL_CONST_CAST) + # undef ZPL_CONST_CAST + #endif + #if defined(__cplusplus) + # define ZPL_CONST_CAST(T, expr) (const_cast(expr)) + #elif \ + ZPL_HAS_WARNING("-Wcast-qual") || \ + ZPL_GCC_VERSION_CHECK(4,6,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_CONST_CAST(T, expr) (__extension__ ({ \ + ZPL_DIAGNOSTIC_PUSH \ + ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL \ + ((T) (expr)); \ + ZPL_DIAGNOSTIC_POP \ + })) + #else + # define ZPL_CONST_CAST(T, expr) ((T) (expr)) + #endif + + #if defined(ZPL_REINTERPRET_CAST) + # undef ZPL_REINTERPRET_CAST + #endif + #if defined(__cplusplus) + # define ZPL_REINTERPRET_CAST(T, expr) (reinterpret_cast(expr)) + #else + # define ZPL_REINTERPRET_CAST(T, expr) ((T) (expr)) + #endif + + #if defined(ZPL_STATIC_CAST) + # undef ZPL_STATIC_CAST + #endif + #if defined(__cplusplus) + # define ZPL_STATIC_CAST(T, expr) (static_cast(expr)) + #else + # define ZPL_STATIC_CAST(T, expr) ((T) (expr)) + #endif + + #if defined(ZPL_CPP_CAST) + # undef ZPL_CPP_CAST + #endif + #if defined(__cplusplus) + # if ZPL_HAS_WARNING("-Wold-style-cast") + # define ZPL_CPP_CAST(T, expr) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \ + ((T) (expr)) \ + ZPL_DIAGNOSTIC_POP + # elif ZPL_IAR_VERSION_CHECK(8,3,0) + # define ZPL_CPP_CAST(T, expr) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("diag_suppress=Pe137") \ + ZPL_DIAGNOSTIC_POP \ + # else + # define ZPL_CPP_CAST(T, expr) ((T) (expr)) + # endif + #else + # define ZPL_CPP_CAST(T, expr) (expr) + #endif + + #if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + defined(__clang__) || \ + ZPL_GCC_VERSION_CHECK(3,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_IAR_VERSION_CHECK(8,0,0) || \ + ZPL_PGI_VERSION_CHECK(18,4,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + ZPL_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + ZPL_TI_CL430_VERSION_CHECK(2,0,1) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,1,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,0,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + ZPL_CRAY_VERSION_CHECK(5,0,0) || \ + ZPL_TINYC_VERSION_CHECK(0,9,17) || \ + ZPL_SUNPRO_VERSION_CHECK(8,0,0) || \ + (ZPL_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR)) + # define ZPL_PRAGMA(value) _Pragma(#value) + #elif ZPL_MSVC_VERSION_CHECK(15,0,0) + # define ZPL_PRAGMA(value) __pragma(value) + #else + # define ZPL_PRAGMA(value) + #endif + + #if defined(ZPL_DIAGNOSTIC_PUSH) + # undef ZPL_DIAGNOSTIC_PUSH + #endif + #if defined(ZPL_DIAGNOSTIC_PUSH_WARNLEVEL) + # undef ZPL_DIAGNOSTIC_PUSH_WARNLEVEL + #endif + #if defined(ZPL_DIAGNOSTIC_POP) + # undef ZPL_DIAGNOSTIC_POP + #endif + #if defined(__clang__) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push") + # define ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(x) + # define ZPL_DIAGNOSTIC_POP _Pragma("clang diagnostic pop") + #elif ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("warning(push)") + # define ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(x) + # define ZPL_DIAGNOSTIC_POP _Pragma("warning(pop)") + #elif ZPL_GCC_VERSION_CHECK(4,6,0) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push") + # define ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(x) + # define ZPL_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop") + #elif ZPL_MSVC_VERSION_CHECK(15,0,0) + # define ZPL_DIAGNOSTIC_PUSH __pragma(warning(push)) + # define ZPL_DIAGNOSTIC_PUSH_WARNLEVEL(x) __pragma(warning(push, x)) + # define ZPL_DIAGNOSTIC_POP __pragma(warning(pop)) + #elif ZPL_ARM_VERSION_CHECK(5,6,0) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("push") + # define ZPL_DIAGNOSTIC_POP _Pragma("pop") + #elif \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + ZPL_TI_CL430_VERSION_CHECK(4,4,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,1,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("diag_push") + # define ZPL_DIAGNOSTIC_POP _Pragma("diag_pop") + #elif ZPL_PELLES_VERSION_CHECK(2,90,0) + # define ZPL_DIAGNOSTIC_PUSH _Pragma("warning(push)") + # define ZPL_DIAGNOSTIC_POP _Pragma("warning(pop)") + #else + # define ZPL_DIAGNOSTIC_PUSH + # define ZPL_DIAGNOSTIC_POP + #endif + + #if defined(ZPL_DIAGNOSTIC_DISABLE_DEPRECATED) + # undef ZPL_DIAGNOSTIC_DISABLE_DEPRECATED + #endif + #if ZPL_HAS_WARNING("-Wdeprecated-declarations") + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"") + #elif ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)") + #elif ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444") + #elif ZPL_GCC_VERSION_CHECK(4,3,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + #elif ZPL_MSVC_VERSION_CHECK(15,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996)) + #elif \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718") + #elif ZPL_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)") + #elif ZPL_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)") + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215") + #elif ZPL_PELLES_VERSION_CHECK(2,90,0) + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)") + #else + # define ZPL_DIAGNOSTIC_DISABLE_DEPRECATED + #endif + + #if defined(ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS) + # undef ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS + #endif + #if ZPL_HAS_WARNING("-Wunknown-pragmas") + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"") + #elif ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)") + #elif ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675") + #elif ZPL_GCC_VERSION_CHECK(4,3,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"") + #elif ZPL_MSVC_VERSION_CHECK(15,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068)) + #elif \ + ZPL_TI_VERSION_CHECK(16,9,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,0,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,3,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") + #elif ZPL_TI_CL6X_VERSION_CHECK(8,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163") + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161") + #else + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS + #endif + + #if defined(ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES) + # undef ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES + #endif + #if ZPL_HAS_WARNING("-Wunknown-attributes") + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("clang diagnostic ignored \"-Wunknown-attributes\"") + #elif ZPL_GCC_VERSION_CHECK(4,6,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") + #elif ZPL_INTEL_VERSION_CHECK(17,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("warning(disable:1292)") + #elif ZPL_MSVC_VERSION_CHECK(19,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES __pragma(warning(disable:5030)) + #elif ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1097") + #elif ZPL_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("error_messages(off,attrskipunsup)") + #elif \ + ZPL_TI_VERSION_CHECK(18,1,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,3,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress 1173") + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES _Pragma("diag_suppress=Pe1097") + #else + # define ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_CPP_ATTRIBUTES + #endif + + #if defined(ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL) + # undef ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL + #endif + #if ZPL_HAS_WARNING("-Wcast-qual") + # define ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"") + #elif ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)") + #elif ZPL_GCC_VERSION_CHECK(3,0,0) + # define ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"") + #else + # define ZPL_DIAGNOSTIC_DISABLE_CAST_QUAL + #endif + + #if defined(ZPL_DEPRECATED) + # undef ZPL_DEPRECATED + #endif + #if defined(ZPL_DEPRECATED_FOR) + # undef ZPL_DEPRECATED_FOR + #endif + #if defined(__cplusplus) && (__cplusplus >= 201402L) + # define ZPL_DEPRECATED(since) ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since)]]) + # define ZPL_DEPRECATED_FOR(since, replacement) ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[deprecated("Since " #since "; use " #replacement)]]) + #elif \ + ZPL_HAS_EXTENSION(attribute_deprecated_with_message) || \ + ZPL_GCC_VERSION_CHECK(4,5,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(5,6,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,13,0) || \ + ZPL_PGI_VERSION_CHECK(17,10,0) || \ + ZPL_TI_VERSION_CHECK(18,1,0) || \ + ZPL_TI_ARMCL_VERSION_CHECK(18,1,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,3,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,3,0) + # define ZPL_DEPRECATED(since) __attribute__((__deprecated__("Since " #since))) + # define ZPL_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement))) + #elif \ + ZPL_HAS_ATTRIBUTE(deprecated) || \ + ZPL_GCC_VERSION_CHECK(3,1,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_DEPRECATED(since) __attribute__((__deprecated__)) + # define ZPL_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__)) + #elif ZPL_MSVC_VERSION_CHECK(14,0,0) + # define ZPL_DEPRECATED(since) __declspec(deprecated("Since " # since)) + # define ZPL_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement)) + #elif \ + ZPL_MSVC_VERSION_CHECK(13,10,0) || \ + ZPL_PELLES_VERSION_CHECK(6,50,0) + # define ZPL_DEPRECATED(since) __declspec(deprecated) + # define ZPL_DEPRECATED_FOR(since, replacement) __declspec(deprecated) + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_DEPRECATED(since) _Pragma("deprecated") + # define ZPL_DEPRECATED_FOR(since, replacement) _Pragma("deprecated") + #else + # define ZPL_DEPRECATED(since) + # define ZPL_DEPRECATED_FOR(since, replacement) + #endif + + #if defined(ZPL_UNAVAILABLE) + # undef ZPL_UNAVAILABLE + #endif + #if \ + ZPL_HAS_ATTRIBUTE(warning) || \ + ZPL_GCC_VERSION_CHECK(4,3,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since))) + #else + # define ZPL_UNAVAILABLE(available_since) + #endif + + #if defined(ZPL_WARN_UNUSED_RESULT) + # undef ZPL_WARN_UNUSED_RESULT + #endif + #if defined(ZPL_WARN_UNUSED_RESULT_MSG) + # undef ZPL_WARN_UNUSED_RESULT_MSG + #endif + #if (ZPL_HAS_CPP_ATTRIBUTE(nodiscard) >= 201907L) + # define ZPL_WARN_UNUSED_RESULT ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + # define ZPL_WARN_UNUSED_RESULT_MSG(msg) ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard(msg)]]) + #elif ZPL_HAS_CPP_ATTRIBUTE(nodiscard) + # define ZPL_WARN_UNUSED_RESULT ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + # define ZPL_WARN_UNUSED_RESULT_MSG(msg) ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[nodiscard]]) + #elif \ + ZPL_HAS_ATTRIBUTE(warn_unused_result) || \ + ZPL_GCC_VERSION_CHECK(3,4,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + (ZPL_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__)) + # define ZPL_WARN_UNUSED_RESULT_MSG(msg) __attribute__((__warn_unused_result__)) + #elif defined(_Check_return_) /* SAL */ + # define ZPL_WARN_UNUSED_RESULT _Check_return_ + # define ZPL_WARN_UNUSED_RESULT_MSG(msg) _Check_return_ + #else + # define ZPL_WARN_UNUSED_RESULT + # define ZPL_WARN_UNUSED_RESULT_MSG(msg) + #endif + + #if defined(ZPL_SENTINEL) + # undef ZPL_SENTINEL + #endif + #if \ + ZPL_HAS_ATTRIBUTE(sentinel) || \ + ZPL_GCC_VERSION_CHECK(4,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(5,4,0) + # define ZPL_SENTINEL(position) __attribute__((__sentinel__(position))) + #else + # define ZPL_SENTINEL(position) + #endif + + #if defined(ZPL_NO_RETURN) + # undef ZPL_NO_RETURN + #endif + #if ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_NO_RETURN __noreturn + #elif ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_NO_RETURN __attribute__((__noreturn__)) + #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + # define ZPL_NO_RETURN _Noreturn + #elif defined(__cplusplus) && (__cplusplus >= 201103L) + # define ZPL_NO_RETURN ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[noreturn]]) + #elif \ + ZPL_HAS_ATTRIBUTE(noreturn) || \ + ZPL_GCC_VERSION_CHECK(3,2,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_NO_RETURN __attribute__((__noreturn__)) + #elif ZPL_SUNPRO_VERSION_CHECK(5,10,0) + # define ZPL_NO_RETURN _Pragma("does_not_return") + #elif ZPL_MSVC_VERSION_CHECK(13,10,0) + # define ZPL_NO_RETURN __declspec(noreturn) + #elif ZPL_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + # define ZPL_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;") + #elif ZPL_COMPCERT_VERSION_CHECK(3,2,0) + # define ZPL_NO_RETURN __attribute((noreturn)) + #elif ZPL_PELLES_VERSION_CHECK(9,0,0) + # define ZPL_NO_RETURN __declspec(noreturn) + #else + # define ZPL_NO_RETURN + #endif + + #if defined(ZPL_NO_ESCAPE) + # undef ZPL_NO_ESCAPE + #endif + #if ZPL_HAS_ATTRIBUTE(noescape) + # define ZPL_NO_ESCAPE __attribute__((__noescape__)) + #else + # define ZPL_NO_ESCAPE + #endif + + #if defined(ZPL_UNREACHABLE) + # undef ZPL_UNREACHABLE + #endif + #if defined(ZPL_UNREACHABLE_RETURN) + # undef ZPL_UNREACHABLE_RETURN + #endif + #if defined(ZPL_ASSUME) + # undef ZPL_ASSUME + #endif + #if \ + ZPL_MSVC_VERSION_CHECK(13,10,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_ASSUME(expr) __assume(expr) + #elif ZPL_HAS_BUILTIN(__builtin_assume) + # define ZPL_ASSUME(expr) __builtin_assume(expr) + #elif \ + ZPL_TI_CL2000_VERSION_CHECK(6,2,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(4,0,0) + # if defined(__cplusplus) + # define ZPL_ASSUME(expr) std::_nassert(expr) + # else + # define ZPL_ASSUME(expr) _nassert(expr) + # endif + #endif + #if \ + (ZPL_HAS_BUILTIN(__builtin_unreachable) && (!defined(ZPL_ARM_VERSION))) || \ + ZPL_GCC_VERSION_CHECK(4,5,0) || \ + ZPL_PGI_VERSION_CHECK(18,10,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_IBM_VERSION_CHECK(13,1,5) + # define ZPL_UNREACHABLE() __builtin_unreachable() + #elif defined(ZPL_ASSUME) + # define ZPL_UNREACHABLE() ZPL_ASSUME(0) + #endif + #if !defined(ZPL_ASSUME) + # if defined(ZPL_UNREACHABLE) + # define ZPL_ASSUME(expr) ZPL_STATIC_CAST(void, ((expr) ? 1 : (ZPL_UNREACHABLE(), 1))) + # else + # define ZPL_ASSUME(expr) ZPL_STATIC_CAST(void, expr) + # endif + #endif + #if defined(ZPL_UNREACHABLE) + # if \ + ZPL_TI_CL2000_VERSION_CHECK(6,2,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(4,0,0) + # define ZPL_UNREACHABLE_RETURN(value) return (ZPL_STATIC_CAST(void, ZPL_ASSUME(0)), (value)) + # else + # define ZPL_UNREACHABLE_RETURN(value) ZPL_UNREACHABLE() + # endif + #else + # define ZPL_UNREACHABLE_RETURN(value) return (value) + #endif + #if !defined(ZPL_UNREACHABLE) + # define ZPL_UNREACHABLE() ZPL_ASSUME(0) + #endif + + ZPL_DIAGNOSTIC_PUSH + #if ZPL_HAS_WARNING("-Wpedantic") + # pragma clang diagnostic ignored "-Wpedantic" + #endif + #if ZPL_HAS_WARNING("-Wc++98-compat-pedantic") && defined(__cplusplus) + # pragma clang diagnostic ignored "-Wc++98-compat-pedantic" + #endif + #if ZPL_GCC_HAS_WARNING("-Wvariadic-macros",4,0,0) + # if defined(__clang__) + # pragma clang diagnostic ignored "-Wvariadic-macros" + # elif defined(ZPL_GCC_VERSION) + # pragma GCC diagnostic ignored "-Wvariadic-macros" + # endif + #endif + #if defined(ZPL_NON_NULL) + # undef ZPL_NON_NULL + #endif + #if \ + ZPL_HAS_ATTRIBUTE(nonnull) || \ + ZPL_GCC_VERSION_CHECK(3,3,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) + # define ZPL_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__))) + #else + # define ZPL_NON_NULL(...) + #endif + ZPL_DIAGNOSTIC_POP + + #if defined(ZPL_PRINTF_FORMAT) + # undef ZPL_PRINTF_FORMAT + #endif + #if defined(__MINGW32__) && ZPL_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO) + # define ZPL_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check))) + #elif defined(__MINGW32__) && ZPL_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO) + # define ZPL_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check))) + #elif \ + ZPL_HAS_ATTRIBUTE(format) || \ + ZPL_GCC_VERSION_CHECK(3,1,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(5,6,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check))) + #elif ZPL_PELLES_VERSION_CHECK(6,0,0) + # define ZPL_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check)) + #else + # define ZPL_PRINTF_FORMAT(string_idx,first_to_check) + #endif + + #if defined(ZPL_CONSTEXPR) + # undef ZPL_CONSTEXPR + #endif + #if defined(__cplusplus) + # if __cplusplus >= 201103L + # define ZPL_CONSTEXPR ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(constexpr) + # endif + #endif + #if !defined(ZPL_CONSTEXPR) + # define ZPL_CONSTEXPR + #endif + + #if defined(ZPL_PREDICT) + # undef ZPL_PREDICT + #endif + #if defined(ZPL_LIKELY) + # undef ZPL_LIKELY + #endif + #if defined(ZPL_UNLIKELY) + # undef ZPL_UNLIKELY + #endif + #if defined(ZPL_UNPREDICTABLE) + # undef ZPL_UNPREDICTABLE + #endif + #if ZPL_HAS_BUILTIN(__builtin_unpredictable) + # define ZPL_UNPREDICTABLE(expr) __builtin_unpredictable((expr)) + #endif + #if \ + ZPL_HAS_BUILTIN(__builtin_expect_with_probability) || \ + ZPL_GCC_VERSION_CHECK(9,0,0) + # define ZPL_PREDICT(expr, value, probability) __builtin_expect_with_probability( (expr), (value), (probability)) + # define ZPL_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1 , (probability)) + # define ZPL_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0 , (probability)) + # define ZPL_LIKELY(expr) __builtin_expect (!!(expr), 1 ) + # define ZPL_UNLIKELY(expr) __builtin_expect (!!(expr), 0 ) + #elif \ + ZPL_HAS_BUILTIN(__builtin_expect) || \ + ZPL_GCC_VERSION_CHECK(3,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + (ZPL_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + ZPL_TI_ARMCL_VERSION_CHECK(4,7,0) || \ + ZPL_TI_CL430_VERSION_CHECK(3,1,0) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,1,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(6,1,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + ZPL_TINYC_VERSION_CHECK(0,9,27) || \ + ZPL_CRAY_VERSION_CHECK(8,1,0) + # define ZPL_PREDICT(expr, expected, probability) \ + (((probability) >= 0.9) ? __builtin_expect((expr), (expected)) : (ZPL_STATIC_CAST(void, expected), (expr))) + # define ZPL_PREDICT_TRUE(expr, probability) \ + (__extension__ ({ \ + double zpl_probability_ = (probability); \ + ((zpl_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((zpl_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \ + })) + # define ZPL_PREDICT_FALSE(expr, probability) \ + (__extension__ ({ \ + double zpl_probability_ = (probability); \ + ((zpl_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((zpl_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \ + })) + # define ZPL_LIKELY(expr) __builtin_expect(!!(expr), 1) + # define ZPL_UNLIKELY(expr) __builtin_expect(!!(expr), 0) + #else + # define ZPL_PREDICT(expr, expected, probability) (ZPL_STATIC_CAST(void, expected), (expr)) + # define ZPL_PREDICT_TRUE(expr, probability) (!!(expr)) + # define ZPL_PREDICT_FALSE(expr, probability) (!!(expr)) + # define ZPL_LIKELY(expr) (!!(expr)) + # define ZPL_UNLIKELY(expr) (!!(expr)) + #endif + #if !defined(ZPL_UNPREDICTABLE) + # define ZPL_UNPREDICTABLE(expr) ZPL_PREDICT(expr, 1, 0.5) + #endif + + #if defined(ZPL_MALLOC) + # undef ZPL_MALLOC + #endif + #if \ + ZPL_HAS_ATTRIBUTE(malloc) || \ + ZPL_GCC_VERSION_CHECK(3,1,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(12,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_MALLOC __attribute__((__malloc__)) + #elif ZPL_SUNPRO_VERSION_CHECK(5,10,0) + # define ZPL_MALLOC _Pragma("returns_new_memory") + #elif ZPL_MSVC_VERSION_CHECK(14, 0, 0) + # define ZPL_MALLOC __declspec(restrict) + #else + # define ZPL_MALLOC + #endif + + #if defined(ZPL_PURE) + # undef ZPL_PURE + #endif + #if \ + ZPL_HAS_ATTRIBUTE(pure) || \ + ZPL_GCC_VERSION_CHECK(2,96,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_PURE __attribute__((__pure__)) + #elif ZPL_SUNPRO_VERSION_CHECK(5,10,0) + # define ZPL_PURE _Pragma("does_not_write_global_data") + #elif defined(__cplusplus) && \ + ( \ + ZPL_TI_CL430_VERSION_CHECK(2,0,1) || \ + ZPL_TI_CL6X_VERSION_CHECK(4,0,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) \ + ) + # define ZPL_PURE _Pragma("FUNC_IS_PURE;") + #else + # define ZPL_PURE + #endif + + #if defined(ZPL_CONST) + # undef ZPL_CONST + #endif + #if \ + ZPL_HAS_ATTRIBUTE(const) || \ + ZPL_GCC_VERSION_CHECK(2,5,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) || \ + ZPL_PGI_VERSION_CHECK(17,10,0) + # define ZPL_CONST __attribute__((__const__)) + #elif \ + ZPL_SUNPRO_VERSION_CHECK(5,10,0) + # define ZPL_CONST _Pragma("no_side_effect") + #else + # define ZPL_CONST ZPL_PURE + #endif + + #if defined(ZPL_RESTRICT) + # undef ZPL_RESTRICT + #endif + #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus) + # define ZPL_RESTRICT restrict + #elif \ + ZPL_GCC_VERSION_CHECK(3,1,0) || \ + ZPL_MSVC_VERSION_CHECK(14,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_PGI_VERSION_CHECK(17,10,0) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,2,4) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,1,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + (ZPL_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \ + ZPL_IAR_VERSION_CHECK(8,0,0) || \ + defined(__clang__) + # define ZPL_RESTRICT __restrict + #elif ZPL_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus) + # define ZPL_RESTRICT _Restrict + #else + # define ZPL_RESTRICT + #endif + + #if defined(ZPL_INLINE) + # undef ZPL_INLINE + #endif + #if \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \ + (defined(__cplusplus) && (__cplusplus >= 199711L)) + # define ZPL_INLINE inline + #elif \ + defined(ZPL_GCC_VERSION) || \ + ZPL_ARM_VERSION_CHECK(6,2,0) + # define ZPL_INLINE __inline__ + #elif \ + ZPL_MSVC_VERSION_CHECK(12,0,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,1,0) || \ + ZPL_TI_CL430_VERSION_CHECK(3,1,0) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,2,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(8,0,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_INLINE __inline + #else + # define ZPL_INLINE + #endif + + #if defined(ZPL_ALWAYS_INLINE) + # undef ZPL_ALWAYS_INLINE + #endif + #if \ + ZPL_HAS_ATTRIBUTE(always_inline) || \ + ZPL_GCC_VERSION_CHECK(4,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_ALWAYS_INLINE __attribute__((__always_inline__)) ZPL_INLINE + #elif ZPL_MSVC_VERSION_CHECK(12,0,0) + # define ZPL_ALWAYS_INLINE __forceinline + #elif defined(__cplusplus) && \ + ( \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(6,1,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) \ + ) + # define ZPL_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;") + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_ALWAYS_INLINE _Pragma("inline=forced") + #else + # define ZPL_ALWAYS_INLINE ZPL_INLINE + #endif + + #undef ZPL_ALWAYS_INLINE + #define ZPL_ALWAYS_INLINE ZPL_INLINE + + #if defined(ZPL_NEVER_INLINE) + # undef ZPL_NEVER_INLINE + #endif + #if \ + ZPL_HAS_ATTRIBUTE(noinline) || \ + ZPL_GCC_VERSION_CHECK(4,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(10,1,0) || \ + ZPL_TI_VERSION_CHECK(15,12,0) || \ + (ZPL_TI_ARMCL_VERSION_CHECK(4,8,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_ARMCL_VERSION_CHECK(5,2,0) || \ + (ZPL_TI_CL2000_VERSION_CHECK(6,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL2000_VERSION_CHECK(6,4,0) || \ + (ZPL_TI_CL430_VERSION_CHECK(4,0,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL430_VERSION_CHECK(4,3,0) || \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) || \ + ZPL_TI_CL7X_VERSION_CHECK(1,2,0) || \ + ZPL_TI_CLPRU_VERSION_CHECK(2,1,0) + # define ZPL_NEVER_INLINE __attribute__((__noinline__)) + #elif ZPL_MSVC_VERSION_CHECK(13,10,0) + # define ZPL_NEVER_INLINE __declspec(noinline) + #elif ZPL_PGI_VERSION_CHECK(10,2,0) + # define ZPL_NEVER_INLINE _Pragma("noinline") + #elif ZPL_TI_CL6X_VERSION_CHECK(6,0,0) && defined(__cplusplus) + # define ZPL_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;") + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_NEVER_INLINE _Pragma("inline=never") + #elif ZPL_COMPCERT_VERSION_CHECK(3,2,0) + # define ZPL_NEVER_INLINE __attribute((noinline)) + #elif ZPL_PELLES_VERSION_CHECK(9,0,0) + # define ZPL_NEVER_INLINE __declspec(noinline) + #else + # define ZPL_NEVER_INLINE + #endif + + #if defined(ZPL_PRIVATE) + # undef ZPL_PRIVATE + #endif + #if defined(ZPL_PUBLIC) + # undef ZPL_PUBLIC + #endif + #if defined(ZPL_IMPORT) + # undef ZPL_IMPORT + #endif + #if defined(_WIN32) || defined(__CYGWIN__) + # define ZPL_PRIVATE + # define ZPL_PUBLIC __declspec(dllexport) + # define ZPL_IMPORT __declspec(dllimport) + #else + # if \ + ZPL_HAS_ATTRIBUTE(visibility) || \ + ZPL_GCC_VERSION_CHECK(3,3,0) || \ + ZPL_SUNPRO_VERSION_CHECK(5,11,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(13,1,0) || \ + ( \ + defined(__TI_EABI__) && \ + ( \ + (ZPL_TI_CL6X_VERSION_CHECK(7,2,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \ + ZPL_TI_CL6X_VERSION_CHECK(7,5,0) \ + ) \ + ) + # define ZPL_PRIVATE __attribute__((__visibility__("hidden"))) + # define ZPL_PUBLIC __attribute__((__visibility__("default"))) + # else + # define ZPL_PRIVATE + # define ZPL_PUBLIC + # endif + # define ZPL_IMPORT extern + #endif + + #if defined(ZPL_NO_THROW) + # undef ZPL_NO_THROW + #endif + #if \ + ZPL_HAS_ATTRIBUTE(nothrow) || \ + ZPL_GCC_VERSION_CHECK(3,3,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_NO_THROW __attribute__((__nothrow__)) + #elif \ + ZPL_MSVC_VERSION_CHECK(13,1,0) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) + # define ZPL_NO_THROW __declspec(nothrow) + #else + # define ZPL_NO_THROW + #endif + + #if defined(ZPL_FALL_THROUGH) + # undef ZPL_FALL_THROUGH + #endif + #if ZPL_GNUC_HAS_ATTRIBUTE(fallthrough,7,0,0) && !defined(ZPL_PGI_VERSION) + # define ZPL_FALL_THROUGH __attribute__((__fallthrough__)) + #elif ZPL_HAS_CPP_ATTRIBUTE_NS(clang,fallthrough) + # define ZPL_FALL_THROUGH ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[clang::fallthrough]]) + #elif ZPL_HAS_CPP_ATTRIBUTE(fallthrough) + # define ZPL_FALL_THROUGH ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_([[fallthrough]]) + #elif defined(__fallthrough) /* SAL */ + # define ZPL_FALL_THROUGH __fallthrough + #else + # define ZPL_FALL_THROUGH + #endif + + #if defined(ZPL_RETURNS_NON_NULL) + # undef ZPL_RETURNS_NON_NULL + #endif + #if \ + ZPL_HAS_ATTRIBUTE(returns_nonnull) || \ + ZPL_GCC_VERSION_CHECK(4,9,0) + # define ZPL_RETURNS_NON_NULL __attribute__((__returns_nonnull__)) + #elif defined(_Ret_notnull_) /* SAL */ + # define ZPL_RETURNS_NON_NULL _Ret_notnull_ + #else + # define ZPL_RETURNS_NON_NULL + #endif + + #if defined(ZPL_ARRAY_PARAM) + # undef ZPL_ARRAY_PARAM + #endif + #if \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \ + !defined(__STDC_NO_VLA__) && \ + !defined(__cplusplus) && \ + !defined(ZPL_PGI_VERSION) && \ + !defined(ZPL_TINYC_VERSION) + # define ZPL_ARRAY_PARAM(name) (name) + #else + # define ZPL_ARRAY_PARAM(name) + #endif + + #if defined(ZPL_IS_CONSTANT) + # undef ZPL_IS_CONSTANT + #endif + #if defined(ZPL_REQUIRE_CONSTEXPR) + # undef ZPL_REQUIRE_CONSTEXPR + #endif + /* ZPL_IS_CONSTEXPR_ is for + ZPL INTERNAL USE ONLY. API subject to change without notice. */ + #if defined(ZPL_IS_CONSTEXPR_) + # undef ZPL_IS_CONSTEXPR_ + #endif + #if \ + ZPL_HAS_BUILTIN(__builtin_constant_p) || \ + ZPL_GCC_VERSION_CHECK(3,4,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_TINYC_VERSION_CHECK(0,9,19) || \ + ZPL_ARM_VERSION_CHECK(4,1,0) || \ + ZPL_IBM_VERSION_CHECK(13,1,0) || \ + ZPL_TI_CL6X_VERSION_CHECK(6,1,0) || \ + (ZPL_SUNPRO_VERSION_CHECK(5,10,0) && !defined(__cplusplus)) || \ + ZPL_CRAY_VERSION_CHECK(8,1,0) + # define ZPL_IS_CONSTANT(expr) __builtin_constant_p(expr) + #endif + #if !defined(__cplusplus) + # if \ + ZPL_HAS_BUILTIN(__builtin_types_compatible_p) || \ + ZPL_GCC_VERSION_CHECK(3,4,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + ZPL_IBM_VERSION_CHECK(13,1,0) || \ + ZPL_CRAY_VERSION_CHECK(8,1,0) || \ + ZPL_ARM_VERSION_CHECK(5,4,0) || \ + ZPL_TINYC_VERSION_CHECK(0,9,24) + # if defined(__INTPTR_TYPE__) + # define ZPL_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*) + # else + # include + # define ZPL_IS_CONSTEXPR_(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*) + # endif + # elif \ + ( \ + defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && \ + !defined(ZPL_SUNPRO_VERSION) && \ + !defined(ZPL_PGI_VERSION) && \ + !defined(ZPL_IAR_VERSION)) || \ + ZPL_HAS_EXTENSION(c_generic_selections) || \ + ZPL_GCC_VERSION_CHECK(4,9,0) || \ + ZPL_INTEL_VERSION_CHECK(17,0,0) || \ + ZPL_IBM_VERSION_CHECK(12,1,0) || \ + ZPL_ARM_VERSION_CHECK(5,3,0) + # if defined(__INTPTR_TYPE__) + # define ZPL_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0) + # else + # include + # define ZPL_IS_CONSTEXPR_(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0) + # endif + # elif \ + defined(ZPL_GCC_VERSION) || \ + defined(ZPL_INTEL_VERSION) || \ + defined(ZPL_TINYC_VERSION) || \ + defined(ZPL_TI_ARMCL_VERSION) || \ + ZPL_TI_CL430_VERSION_CHECK(18,12,0) || \ + defined(ZPL_TI_CL2000_VERSION) || \ + defined(ZPL_TI_CL6X_VERSION) || \ + defined(ZPL_TI_CL7X_VERSION) || \ + defined(ZPL_TI_CLPRU_VERSION) || \ + defined(__clang__) + # define ZPL_IS_CONSTEXPR_(expr) ( \ + sizeof(void) != \ + sizeof(*( \ + 1 ? \ + ((void*) ((expr) * 0L) ) : \ + ((struct { char v[sizeof(void) * 2]; } *) 1) \ + ) \ + ) \ + ) + # endif + #endif + #if defined(ZPL_IS_CONSTEXPR_) + # if !defined(ZPL_IS_CONSTANT) + # define ZPL_IS_CONSTANT(expr) ZPL_IS_CONSTEXPR_(expr) + # endif + # define ZPL_REQUIRE_CONSTEXPR(expr) (ZPL_IS_CONSTEXPR_(expr) ? (expr) : (-1)) + #else + # if !defined(ZPL_IS_CONSTANT) + # define ZPL_IS_CONSTANT(expr) (0) + # endif + # define ZPL_REQUIRE_CONSTEXPR(expr) (expr) + #endif + + #if defined(ZPL_BEGIN_C_DECLS) + # undef ZPL_BEGIN_C_DECLS + #endif + #if defined(ZPL_END_C_DECLS) + # undef ZPL_END_C_DECLS + #endif + #if defined(ZPL_C_DECL) + # undef ZPL_C_DECL + #endif + #if defined(__cplusplus) + # define ZPL_BEGIN_C_DECLS extern "C" { + # define ZPL_END_C_DECLS } + # define ZPL_C_DECL extern "C" + #else + # define ZPL_BEGIN_C_DECLS + # define ZPL_END_C_DECLS + # define ZPL_C_DECL + #endif + + #if defined(ZPL_STATIC_ASSERT) + # undef ZPL_STATIC_ASSERT + #endif + #if \ + !defined(__cplusplus) && ( \ + (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \ + ZPL_HAS_FEATURE(c_static_assert) || \ + ZPL_GCC_VERSION_CHECK(6,0,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) || \ + defined(_Static_assert) \ + ) + # define ZPL_STATIC_ASSERT(expr, message) _Static_assert(expr, message) + #elif \ + (defined(__cplusplus) && (__cplusplus >= 201103L)) || \ + ZPL_MSVC_VERSION_CHECK(16,0,0) + # define ZPL_STATIC_ASSERT(expr, message) ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(static_assert(expr, message)) + #else + # define ZPL_STATIC_ASSERT3(cond, msg) typedef char static_assertion_##msg[(!!(cond)) * 2 - 1] + # define ZPL_STATIC_ASSERT2(cond, line) ZPL_STATIC_ASSERT3(cond, static_assertion_at_line_##line) + # define ZPL_STATIC_ASSERT1(cond, line) ZPL_STATIC_ASSERT2(cond, line) + # define ZPL_STATIC_ASSERT(cond, unused) ZPL_STATIC_ASSERT1(cond, __LINE__) + #endif + + #if defined(ZPL_NULL) + # undef ZPL_NULL + #endif + #if defined(__cplusplus) + # if __cplusplus >= 201103L + # define ZPL_NULL ZPL_DIAGNOSTIC_DISABLE_CPP98_COMPAT_WRAP_(nullptr) + # elif defined(NULL) + # define ZPL_NULL NULL + # else + # define ZPL_NULL ZPL_STATIC_CAST(void*, 0) + # endif + #elif defined(NULL) + # define ZPL_NULL NULL + #else + # define ZPL_NULL ((void*) 0) + #endif + + #if defined(ZPL_MESSAGE) + # undef ZPL_MESSAGE + #endif + #if ZPL_HAS_WARNING("-Wunknown-pragmas") + # define ZPL_MESSAGE(msg) \ + ZPL_DIAGNOSTIC_PUSH \ + ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + ZPL_PRAGMA(message msg) \ + ZPL_DIAGNOSTIC_POP + #elif \ + ZPL_GCC_VERSION_CHECK(4,4,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_MESSAGE(msg) ZPL_PRAGMA(message msg) + #elif ZPL_CRAY_VERSION_CHECK(5,0,0) + # define ZPL_MESSAGE(msg) ZPL_PRAGMA(_CRI message msg) + #elif ZPL_IAR_VERSION_CHECK(8,0,0) + # define ZPL_MESSAGE(msg) ZPL_PRAGMA(message(msg)) + #elif ZPL_PELLES_VERSION_CHECK(2,0,0) + # define ZPL_MESSAGE(msg) ZPL_PRAGMA(message(msg)) + #else + # define ZPL_MESSAGE(msg) + #endif + + #if defined(ZPL_WARNING) + # undef ZPL_WARNING + #endif + #if ZPL_HAS_WARNING("-Wunknown-pragmas") + # define ZPL_WARNING(msg) \ + ZPL_DIAGNOSTIC_PUSH \ + ZPL_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \ + ZPL_PRAGMA(clang warning msg) \ + ZPL_DIAGNOSTIC_POP + #elif \ + ZPL_GCC_VERSION_CHECK(4,8,0) || \ + ZPL_PGI_VERSION_CHECK(18,4,0) || \ + ZPL_INTEL_VERSION_CHECK(13,0,0) + # define ZPL_WARNING(msg) ZPL_PRAGMA(GCC warning msg) + #elif ZPL_MSVC_VERSION_CHECK(15,0,0) + # define ZPL_WARNING(msg) ZPL_PRAGMA(message(msg)) + #else + # define ZPL_WARNING(msg) ZPL_MESSAGE(msg) + #endif + + #if defined(ZPL_REQUIRE) + # undef ZPL_REQUIRE + #endif + #if defined(ZPL_REQUIRE_MSG) + # undef ZPL_REQUIRE_MSG + #endif + #if ZPL_HAS_ATTRIBUTE(diagnose_if) + # if ZPL_HAS_WARNING("-Wgcc-compat") + # define ZPL_REQUIRE(expr) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), #expr, "error"))) \ + ZPL_DIAGNOSTIC_POP + # define ZPL_REQUIRE_MSG(expr,msg) \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((diagnose_if(!(expr), msg, "error"))) \ + ZPL_DIAGNOSTIC_POP + # else + # define ZPL_REQUIRE(expr) __attribute__((diagnose_if(!(expr), #expr, "error"))) + # define ZPL_REQUIRE_MSG(expr,msg) __attribute__((diagnose_if(!(expr), msg, "error"))) + # endif + #else + # define ZPL_REQUIRE(expr) + # define ZPL_REQUIRE_MSG(expr,msg) + #endif + + #if defined(ZPL_FLAGS) + # undef ZPL_FLAGS + #endif + #if ZPL_HAS_ATTRIBUTE(flag_enum) + # define ZPL_FLAGS __attribute__((__flag_enum__)) + #endif + + #if defined(ZPL_FLAGS_CAST) + # undef ZPL_FLAGS_CAST + #endif + #if ZPL_INTEL_VERSION_CHECK(19,0,0) + # define ZPL_FLAGS_CAST(T, expr) (__extension__ ({ \ + ZPL_DIAGNOSTIC_PUSH \ + _Pragma("warning(disable:188)") \ + ((T) (expr)); \ + ZPL_DIAGNOSTIC_POP \ + })) + #else + # define ZPL_FLAGS_CAST(T, expr) ZPL_STATIC_CAST(T, expr) + #endif + + #if defined(ZPL_EMPTY_BASES) + # undef ZPL_EMPTY_BASES + #endif + #if ZPL_MSVC_VERSION_CHECK(19,0,23918) && !ZPL_MSVC_VERSION_CHECK(20,0,0) + # define ZPL_EMPTY_BASES __declspec(empty_bases) + #else + # define ZPL_EMPTY_BASES + #endif + + /* Remaining macros are deprecated. */ + + #if defined(ZPL_GCC_NOT_CLANG_VERSION_CHECK) + # undef ZPL_GCC_NOT_CLANG_VERSION_CHECK + #endif + #if defined(__clang__) + # define ZPL_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0) + #else + # define ZPL_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) ZPL_GCC_VERSION_CHECK(major,minor,patch) + #endif + + #if defined(ZPL_CLANG_HAS_ATTRIBUTE) + # undef ZPL_CLANG_HAS_ATTRIBUTE + #endif + #define ZPL_CLANG_HAS_ATTRIBUTE(attribute) ZPL_HAS_ATTRIBUTE(attribute) + + #if defined(ZPL_CLANG_HAS_CPP_ATTRIBUTE) + # undef ZPL_CLANG_HAS_CPP_ATTRIBUTE + #endif + #define ZPL_CLANG_HAS_CPP_ATTRIBUTE(attribute) ZPL_HAS_CPP_ATTRIBUTE(attribute) + + #if defined(ZPL_CLANG_HAS_BUILTIN) + # undef ZPL_CLANG_HAS_BUILTIN + #endif + #define ZPL_CLANG_HAS_BUILTIN(builtin) ZPL_HAS_BUILTIN(builtin) + + #if defined(ZPL_CLANG_HAS_FEATURE) + # undef ZPL_CLANG_HAS_FEATURE + #endif + #define ZPL_CLANG_HAS_FEATURE(feature) ZPL_HAS_FEATURE(feature) + + #if defined(ZPL_CLANG_HAS_EXTENSION) + # undef ZPL_CLANG_HAS_EXTENSION + #endif + #define ZPL_CLANG_HAS_EXTENSION(extension) ZPL_HAS_EXTENSION(extension) + + #if defined(ZPL_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE) + # undef ZPL_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE + #endif + #define ZPL_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) ZPL_HAS_DECLSPEC_ATTRIBUTE(attribute) + + #if defined(ZPL_CLANG_HAS_WARNING) + # undef ZPL_CLANG_HAS_WARNING + #endif + #define ZPL_CLANG_HAS_WARNING(warning) ZPL_HAS_WARNING(warning) + + #endif /* !defined(ZPL_HEDLEY_VERSION) || (ZPL_HEDLEY_VERSION < X) */ + +#define ZPL_VERSION ZPL_VERSION_ENCODE(ZPL_VERSION_MAJOR, ZPL_VERSION_MINOR, ZPL_VERSION_PATCH) + +#ifdef ZPL_IMPL +# ifndef ZPL_IMPLEMENTATION +# define ZPL_IMPLEMENTATION +# endif +#endif + +#if defined(__cplusplus) && !defined(ZPL_EXTERN) +# define ZPL_EXTERN extern "C" +#else +# define ZPL_EXTERN extern +#endif + +#ifndef ZPL_DEF +# if defined(ZPL_SHARED_LIB) +# ifdef ZPL_IMPLEMENTATION +# define ZPL_DEF ZPL_PUBLIC +# else +# define ZPL_DEF ZPL_IMPORT +# endif +# elif defined(ZPL_STATIC_LIB) +# ifdef ZPL_IMPLEMENTATION +# define ZPL_DEF +# else +# define ZPL_DEF ZPL_EXTERN +# endif +# elif defined(ZPL_STATIC) +# define ZPL_DEF static +# else +# define ZPL_DEF ZPL_EXTERN +# endif +#endif + +#ifndef ZPL_DEF_INLINE +# if defined(ZPL_STATIC) +# define ZPL_DEF_INLINE +# define ZPL_IMPL_INLINE +# else +# define ZPL_DEF_INLINE static +# define ZPL_IMPL_INLINE static inline +# endif +#endif + +/* builtin overrides */ +#if defined(__TINYC__) || defined(__EMSCRIPTEN__) +# if defined(ZPL_ENFORCE_THREADING) +# define ZPL_ENABLE_THREADING +# else +# define ZPL_DISABLE_THREADING +# endif +#endif + +/* Distributions */ +#ifndef ZPL_CUSTOM_MODULES + /* default distribution */ +# define ZPL_MODULE_ESSENTIALS +# define ZPL_MODULE_CORE +# define ZPL_MODULE_TIMER +# define ZPL_MODULE_HASHING +# define ZPL_MODULE_REGEX +# define ZPL_MODULE_EVENT +# define ZPL_MODULE_DLL +# define ZPL_MODULE_OPTS +# define ZPL_MODULE_PROCESS +# define ZPL_MODULE_MATH +# define ZPL_MODULE_THREADING +# define ZPL_MODULE_JOBS +# define ZPL_MODULE_PARSER +# define ZPL_MODULE_SOCKET + + /* zpl nano distribution */ +# if defined(ZPL_NANO) || defined(ZPL_PICO) +# undef ZPL_MODULE_TIMER +# undef ZPL_MODULE_HASHING +# undef ZPL_MODULE_REGEX +# undef ZPL_MODULE_EVENT +# undef ZPL_MODULE_DLL +# undef ZPL_MODULE_OPTS +# undef ZPL_MODULE_PROCESS +# undef ZPL_MODULE_MATH +# undef ZPL_MODULE_THREADING +# undef ZPL_MODULE_JOBS +# undef ZPL_MODULE_PARSER +# undef ZPL_MODULE_SOCKET +# endif + +# if defined(ZPL_PICO) +# undef ZPL_MODULE_CORE +# endif + + /* module enabling overrides */ +# if defined(ZPL_ENABLE_CORE) && !defined(ZPL_MODULE_CORE) +# define ZPL_MODULE_CORE +# endif +# if defined(ZPL_ENABLE_HASHING) && !defined(ZPL_MODULE_HASHING) +# define ZPL_MODULE_HASHING +# endif +# if defined(ZPL_ENABLE_REGEX) && !defined(ZPL_MODULE_REGEX) +# define ZPL_MODULE_REGEX +# endif +# if defined(ZPL_ENABLE_DLL) && !defined(ZPL_MODULE_DLL) +# define ZPL_MODULE_DLL +# endif +# if defined(ZPL_ENABLE_OPTS) && !defined(ZPL_MODULE_OPTS) +# define ZPL_MODULE_OPTS +# endif +# if defined(ZPL_ENABLE_PROCESS) && !defined(ZPL_MODULE_PROCESS) +# define ZPL_MODULE_PROCESS +# endif +# if defined(ZPL_ENABLE_MATH) && !defined(ZPL_MODULE_MATH) +# define ZPL_MODULE_MATH +# endif +# if defined(ZPL_ENABLE_THREADING) && !defined(ZPL_MODULE_THREADING) +# define ZPL_MODULE_THREADING +# endif +# if defined(ZPL_ENABLE_JOBS) && !defined(ZPL_MODULE_JOBS) +# ifndef ZPL_MODULE_THREADING +# define ZPL_MODULE_THREADING /* dependency */ +# endif +# define ZPL_MODULE_JOBS +# endif +# if defined(ZPL_ENABLE_PARSER) && !defined(ZPL_MODULE_PARSER) +# define ZPL_MODULE_PARSER +# endif +# if defined(ZPL_ENABLE_SOCKET) && !defined(ZPL_MODULE_SOCKET) +# define ZPL_MODULE_SOCKET +# endif + + /* module disabling overrides */ +# if defined(ZPL_DISABLE_CORE) && defined(ZPL_MODULE_CORE) +# undef ZPL_MODULE_CORE +# endif +# if defined(ZPL_DISABLE_HASHING) && defined(ZPL_MODULE_HASHING) +# undef ZPL_MODULE_HASHING +# endif +# if defined(ZPL_DISABLE_REGEX) && defined(ZPL_MODULE_REGEX) +# undef ZPL_MODULE_REGEX +# endif +# if defined(ZPL_DISABLE_DLL) && defined(ZPL_MODULE_DLL) +# undef ZPL_MODULE_DLL +# endif +# if defined(ZPL_DISABLE_OPTS) && defined(ZPL_MODULE_OPTS) +# undef ZPL_MODULE_OPTS +# endif +# if defined(ZPL_DISABLE_PROCESS) && defined(ZPL_MODULE_PROCESS) +# undef ZPL_MODULE_PROCESS +# endif +# if defined(ZPL_DISABLE_MATH) && defined(ZPL_MODULE_MATH) +# undef ZPL_MODULE_MATH +# endif +# if defined(ZPL_DISABLE_THREADING) && defined(ZPL_MODULE_THREADING) +# ifdef ZPL_MODULE_JOBS +# undef ZPL_MODULE_JOBS /* user */ +# endif +# undef ZPL_MODULE_THREADING +# endif +# if defined(ZPL_DISABLE_JOBS) && defined(ZPL_MODULE_JOBS) +# undef ZPL_MODULE_JOBS +# endif +# if defined(ZPL_DISABLE_PARSER) && defined(ZPL_MODULE_PARSER) +# undef ZPL_MODULE_PARSER +# endif +# if defined(ZPL_DISABLE_SOCKET) && defined(ZPL_MODULE_SOCKET) +# undef ZPL_MODULE_SOCKET +# endif +#endif + +#if defined(__GCC__) || defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4201) +# pragma warning(disable : 4127) // Conditional expression is constant +#endif + +/* general purpose includes */ + + // file: header/core/system.h + + + ZPL_BEGIN_C_DECLS + + /* Platform architecture */ + + #if defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__64BIT__) || defined(__powerpc64__) || \ + defined(__ppc64__) || defined(__aarch64__) + # ifndef ZPL_ARCH_64_BIT + # define ZPL_ARCH_64_BIT 1 + # endif + #else + # ifndef ZPL_ARCH_32_BIT + # define ZPL_ARCH_32_BIT 1 + # endif + #endif + + /* Platform endiannes */ + + #ifndef ZPL_ENDIAN_ORDER + # define ZPL_ENDIAN_ORDER + # define ZPL_IS_BIG_ENDIAN (!*(zpl_u8 *)&(zpl_u16){ 1 }) + # define ZPL_IS_LITTLE_ENDIAN (!ZPL_IS_BIG_ENDIAN) + #endif + + /* Platform OS */ + + #if defined(_WIN32) || defined(_WIN64) + # ifndef ZPL_SYSTEM_WINDOWS + # define ZPL_SYSTEM_WINDOWS 1 + # endif + #elif defined(__APPLE__) && defined(__MACH__) + # ifndef ZPL_SYSTEM_OSX + # define ZPL_SYSTEM_OSX 1 + # endif + # ifndef ZPL_SYSTEM_MACOS + # define ZPL_SYSTEM_MACOS 1 + # endif + # include + # if TARGET_IPHONE_SIMULATOR == 1 || TARGET_OS_IPHONE == 1 + # ifndef ZPL_SYSTEM_IOS + # define ZPL_SYSTEM_IOS 1 + # endif + # endif + #elif defined(__unix__) + # ifndef ZPL_SYSTEM_UNIX + # define ZPL_SYSTEM_UNIX 1 + # endif + # if defined(ANDROID) || defined(__ANDROID__) + # ifndef ZPL_SYSTEM_ANDROID + # define ZPL_SYSTEM_ANDROID 1 + # endif + # ifndef ZPL_SYSTEM_LINUX + # define ZPL_SYSTEM_LINUX 1 + # endif + # elif defined(__linux__) + # ifndef ZPL_SYSTEM_LINUX + # define ZPL_SYSTEM_LINUX 1 + # endif + # elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) + # ifndef ZPL_SYSTEM_FREEBSD + # define ZPL_SYSTEM_FREEBSD 1 + # endif + # elif defined(__OpenBSD__) + # ifndef ZPL_SYSTEM_OPENBSD + # define ZPL_SYSTEM_OPENBSD 1 + # endif + # elif defined(__EMSCRIPTEN__) + # ifndef ZPL_SYSTEM_EMSCRIPTEN + # define ZPL_SYSTEM_EMSCRIPTEN 1 + # endif + # elif defined(__CYGWIN__) + # ifndef ZPL_SYSTEM_CYGWIN + # define ZPL_SYSTEM_CYGWIN 1 + # endif + # else + # error This UNIX operating system is not supported + # endif + #else + # error This operating system is not supported + #endif + + /* Platform compiler */ + + #if defined(_MSC_VER) + # define ZPL_COMPILER_MSVC 1 + #elif defined(__GNUC__) + # define ZPL_COMPILER_GCC 1 + #elif defined(__clang__) + # define ZPL_COMPILER_CLANG 1 + #elif defined(__MINGW32__) + # define ZPL_COMPILER_MINGW 1 + #elif defined(__TINYC__) + # define ZPL_COMPILER_TINYC 1 + #else + # error Unknown compiler + #endif + + /* Platform CPU */ + + #if defined(__arm__) || defined(__aarch64__) || defined(__ARM_ARCH) + # ifndef ZPL_CPU_ARM + # define ZPL_CPU_ARM 1 + # endif + # ifndef ZPL_CACHE_LINE_SIZE + # define ZPL_CACHE_LINE_SIZE 64 + # endif + #elif defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__x86_64__) || defined(ZPL_SYSTEM_EMSCRIPTEN) + # ifndef ZPL_CPU_X86 + # define ZPL_CPU_X86 1 + # endif + # ifndef ZPL_CACHE_LINE_SIZE + # define ZPL_CACHE_LINE_SIZE 64 + # endif + #elif defined(_M_PPC) || defined(__powerpc__) || defined(__powerpc64__) + # ifndef ZPL_CPU_PPC + # define ZPL_CPU_PPC 1 + # endif + # ifndef ZPL_CACHE_LINE_SIZE + # define ZPL_CACHE_LINE_SIZE 128 + # endif + #elif defined(__MIPSEL__) || defined(__mips_isa_rev) + # ifndef ZPL_CPU_MIPS + # define ZPL_CPU_MIPS 1 + # endif + # ifndef ZPL_CACHE_LINE_SIZE + # define ZPL_CACHE_LINE_SIZE 64 + # endif + #else + # error Unknown CPU Type + #endif + + // TODO(ZaKlaus): Find a better way to get this flag in MinGW. + #if (defined(ZPL_COMPILER_GCC) && !defined(WC_ERR_INVALID_CHARS)) || defined(ZPL_COMPILER_TINYC) + # define WC_ERR_INVALID_CHARS 0x0080 + #endif + + #if defined(ZPL_COMPILER_GCC) && defined(ZPL_SYSTEM_WINDOWS) + # ifndef ZPL_COMPILER_MINGW + # define ZPL_COMPILER_MINGW // assume we use mingw as a compiler + # endif + #endif + + #if defined(ZPL_SYSTEM_UNIX) + # ifndef _GNU_SOURCE + # define _GNU_SOURCE + # endif + + # ifndef _LARGEFILE64_SOURCE + # define _LARGEFILE64_SOURCE + # endif + #endif + + #if ZPL_GNUC_VERSION_CHECK(3, 3, 0) + # define ZPL_INFINITY (__builtin_inff()) + # define ZPL_NAN (__builtin_nanf("")) + #elif defined(ZPL_COMPILER_MSVC) + + # if !defined(ZPL__HACK_INFINITY) + typedef union zpl__msvc_inf_hack { + unsigned __int8 bytes[4]; + float value; + } zpl__msvc_inf_hack; + static union zpl__msvc_inf_hack ZPL__INFINITY_HACK = {{0x00, 0x00, 0x80, 0x7F}}; + # define ZPL__HACK_INFINITY (ZPL__INFINITY_HACK.value) + # endif + + # define ZPL_INFINITY (ZPL__HACK_INFINITY) + # define ZPL_NAN (0) + #else + # define ZPL_INFINITY (1e10000f) + # define ZPL_NAN (0.0f / 0.0f) + #endif + + ZPL_END_C_DECLS + +#include +#include + +#if defined(ZPL_SYSTEM_WINDOWS) +# include +#endif + + // file: header/essentials/types.h + + + ZPL_BEGIN_C_DECLS + + /* Basic types */ + + #if defined(ZPL_COMPILER_MSVC) + # if _MSC_VER < 1300 + typedef unsigned char zpl_u8; + typedef signed char zpl_i8; + typedef unsigned short zpl_u16; + typedef signed short zpl_i16; + typedef unsigned int zpl_u32; + typedef signed int zpl_i32; + # else + typedef unsigned __int8 zpl_u8; + typedef signed __int8 zpl_i8; + typedef unsigned __int16 zpl_u16; + typedef signed __int16 zpl_i16; + typedef unsigned __int32 zpl_u32; + typedef signed __int32 zpl_i32; + # endif + typedef unsigned __int64 zpl_u64; + typedef signed __int64 zpl_i64; + #else + # include + + typedef uint8_t zpl_u8; + typedef int8_t zpl_i8; + typedef uint16_t zpl_u16; + typedef int16_t zpl_i16; + typedef uint32_t zpl_u32; + typedef int32_t zpl_i32; + typedef uint64_t zpl_u64; + typedef int64_t zpl_i64; + #endif + + ZPL_STATIC_ASSERT(sizeof(zpl_u8) == sizeof(zpl_i8), "sizeof(zpl_u8) != sizeof(zpl_i8)"); + ZPL_STATIC_ASSERT(sizeof(zpl_u16) == sizeof(zpl_i16), "sizeof(zpl_u16) != sizeof(zpl_i16)"); + ZPL_STATIC_ASSERT(sizeof(zpl_u32) == sizeof(zpl_i32), "sizeof(zpl_u32) != sizeof(zpl_i32)"); + ZPL_STATIC_ASSERT(sizeof(zpl_u64) == sizeof(zpl_i64), "sizeof(zpl_u64) != sizeof(zpl_i64)"); + + ZPL_STATIC_ASSERT(sizeof(zpl_u8) == 1, "sizeof(zpl_u8) != 1"); + ZPL_STATIC_ASSERT(sizeof(zpl_u16) == 2, "sizeof(zpl_u16) != 2"); + ZPL_STATIC_ASSERT(sizeof(zpl_u32) == 4, "sizeof(zpl_u32) != 4"); + ZPL_STATIC_ASSERT(sizeof(zpl_u64) == 8, "sizeof(zpl_u64) != 8"); + + typedef size_t zpl_usize; + typedef ptrdiff_t zpl_isize; + + ZPL_STATIC_ASSERT(sizeof(zpl_usize) == sizeof(zpl_isize), "sizeof(zpl_usize) != sizeof(zpl_isize)"); + + // NOTE: (u)zpl_intptr is only here for semantic reasons really as this library will only support 32/64 bit OSes. + #if defined(_WIN64) + typedef signed __int64 zpl_intptr; + typedef unsigned __int64 zpl_uintptr; + #elif defined(_WIN32) + // NOTE; To mark types changing their size, e.g. zpl_intptr + # ifndef _W64 + # if !defined(__midl) && (defined(_X86_) || defined(_M_IX86)) && _MSC_VER >= 1300 + # define _W64 __w64 + # else + # define _W64 + # endif + # endif + typedef _W64 signed int zpl_intptr; + typedef _W64 unsigned int zpl_uintptr; + #else + typedef uintptr_t zpl_uintptr; + typedef intptr_t zpl_intptr; + #endif + + ZPL_STATIC_ASSERT(sizeof(zpl_uintptr) == sizeof(zpl_intptr), "sizeof(zpl_uintptr) != sizeof(zpl_intptr)"); + + typedef float zpl_f32; + typedef double zpl_f64; + + ZPL_STATIC_ASSERT(sizeof(zpl_f32) == 4, "sizeof(zpl_f32) != 4"); + ZPL_STATIC_ASSERT(sizeof(zpl_f64) == 8, "sizeof(zpl_f64) != 8"); + + typedef zpl_i32 zpl_rune; // NOTE: Unicode codepoint + typedef zpl_i32 zpl_char32; + #define ZPL_RUNE_INVALID cast(zpl_rune)(0xfffd) + #define ZPL_RUNE_MAX cast(zpl_rune)(0x0010ffff) + #define ZPL_RUNE_BOM cast(zpl_rune)(0xfeff) + #define ZPL_RUNE_EOF cast(zpl_rune)(-1) + + typedef zpl_i8 zpl_b8; + typedef zpl_i16 zpl_b16; + typedef zpl_i32 zpl_b32; + + #if !defined(__cplusplus) + # if (defined(_MSC_VER) && _MSC_VER < 1800) || (!defined(_MSC_VER) && !defined(__STDC_VERSION__)) + # ifndef true + # define true(0 == 0) + # endif + # ifndef false + # define false(0 != 0) + # endif + + typedef zpl_b8 bool; + # else + # include + # endif + #endif + + #ifndef ZPL_U8_MIN + # define ZPL_U8_MIN 0u + # define ZPL_U8_MAX 0xffu + # define ZPL_I8_MIN (-0x7f - 1) + # define ZPL_I8_MAX 0x7f + + # define ZPL_U16_MIN 0u + # define ZPL_U16_MAX 0xffffu + # define ZPL_I16_MIN (-0x7fff - 1) + # define ZPL_I16_MAX 0x7fff + + # define ZPL_U32_MIN 0u + # define ZPL_U32_MAX 0xffffffffu + # define ZPL_I32_MIN (-0x7fffffff - 1) + # define ZPL_I32_MAX 0x7fffffff + + # define ZPL_U64_MIN 0ull + # define ZPL_U64_MAX 0xffffffffffffffffull + # define ZPL_I64_MIN (-0x7fffffffffffffffll - 1) + # define ZPL_I64_MAX 0x7fffffffffffffffll + + # if defined(ZPL_ARCH_32_BIT) + # define ZPL_USIZE_MIN ZPL_U32_MIN + # define ZPL_USIZE_MAX ZPL_U32_MAX + # define ZPL_ISIZE_MIN ZPL_I32_MIN + # define ZPL_ISIZE_MAX ZPL_I32_MAX + # elif defined(ZPL_ARCH_64_BIT) + # define ZPL_USIZE_MIN ZPL_U64_MIN + # define ZPL_USIZE_MAX ZPL_U64_MAX + # define ZPL_ISIZE_MIN ZPL_I64_MIN + # define ZPL_ISIZE_MAX ZPL_I64_MAX + # else + # error Unknown architecture size. This library only supports 32 bit and 64 bit architectures. + # endif + + # define ZPL_F32_MIN 1.17549435e-38f + # define ZPL_F32_MAX 3.40282347e+38f + + # define ZPL_F64_MIN 2.2250738585072014e-308 + # define ZPL_F64_MAX 1.7976931348623157e+308 + #endif + + #ifdef ZPL_DEFINE_NULL_MACRO + # ifndef NULL + # define NULL ZPL_NULL + # endif + #endif + + ZPL_END_C_DECLS + // file: header/essentials/helpers.h + + /* Various macro based helpers */ + + ZPL_BEGIN_C_DECLS + + #ifndef cast + # define cast(Type) (Type) + #endif + + #ifndef zpl_size_of + # define zpl_size_of(x) (zpl_isize)(sizeof(x)) + #endif + + #ifndef zpl_count_of + # define zpl_count_of(x) ((zpl_size_of(x) / zpl_size_of(0 [x])) / ((zpl_isize)(!(zpl_size_of(x) % zpl_size_of(0 [x]))))) + #endif + + #ifndef zpl_offset_of + #if defined(_MSC_VER) || defined(ZPL_COMPILER_TINYC) + # define zpl_offset_of(Type, element) ((zpl_isize) & (((Type *)0)->element)) + #else + # define zpl_offset_of(Type, element) __builtin_offsetof(Type, element) + #endif + #endif + + #if defined(__cplusplus) + # ifndef zpl_align_of + # if __cplusplus >= 201103L + # define zpl_align_of(Type) (zpl_isize)alignof(Type) + # else + extern "C++" { + template struct zpl_alignment_trick { + char c; + T member; + }; + } + # define zpl_align_of(Type) zpl_offset_of(zpl_alignment_trick, member) + # endif + # endif + #else + # ifndef zpl_align_of + # define zpl_align_of(Type) \ + zpl_offset_of( \ + struct { \ + char c; \ + Type member; \ + }, \ + member) + # endif + #endif + + #ifndef zpl_swap + # define zpl_swap(Type, a, b) \ + do { \ + Type tmp = (a); \ + (a) = (b); \ + (b) = tmp; \ + } while (0) + #endif + + + + #ifndef zpl_global + # define zpl_global static // Global variables + #endif + + #ifndef zpl_internal + # define zpl_internal static // Internal linkage + #endif + + #ifndef zpl_local_persist + # define zpl_local_persist static // Local Persisting variables + #endif + + #ifndef zpl_unused + # if defined(_MSC_VER) + # define zpl_unused(x) (__pragma(warning(suppress : 4100))(x)) + # elif defined(__GCC__) + # define zpl_unused(x) __attribute__((__unused__))(x) + # else + # define zpl_unused(x) ((void)(zpl_size_of(x))) + # endif + #endif + + + #ifndef ZPL_JOIN_MACROS + # define ZPL_JOIN_MACROS + + # define ZPL_JOIN2 ZPL_CONCAT + # define ZPL_JOIN3(a, b, c) ZPL_JOIN2(ZPL_JOIN2(a, b), c) + # define ZPL_JOIN4(a, b, c, d) ZPL_JOIN2(ZPL_JOIN2(ZPL_JOIN2(a, b), c), d) + #endif + + #ifndef ZPL_BIT + # define ZPL_BIT(x) (1 << (x)) + #endif + + #ifndef zpl_min + # define zpl_min(a, b) ((a) < (b) ? (a) : (b)) + #endif + + #ifndef zpl_max + # define zpl_max(a, b) ((a) > (b) ? (a) : (b)) + #endif + + #ifndef zpl_min3 + # define zpl_min3(a, b, c) zpl_min(zpl_min(a, b), c) + #endif + + #ifndef zpl_max3 + # define zpl_max3(a, b, c) zpl_max(zpl_max(a, b), c) + #endif + + #ifndef zpl_clamp + # define zpl_clamp(x, lower, upper) zpl_min(zpl_max((x), (lower)), (upper)) + #endif + + #ifndef zpl_clamp01 + # define zpl_clamp01(x) zpl_clamp((x), 0, 1) + #endif + + #ifndef zpl_is_between + # define zpl_is_between(x, lower, upper) (((lower) <= (x)) && ((x) <= (upper))) + #endif + + #ifndef zpl_is_between_limit + # define zpl_is_between_limit(x, lower, upper) (((lower) <= (x)) && ((x) < (upper))) + #endif + + #ifndef zpl_step + #define zpl_step(x,y) (((x)/(y))*(y)) + #endif + + #ifndef zpl_abs + # define zpl_abs(x) ((x) < 0 ? -(x) : (x)) + #endif + + #ifndef ZPL_MASK_SET + # define ZPL_MASK_SET(var, set, mask) \ + do { \ + if (set) \ + (var) |= (mask); \ + else \ + (var) &= ~(mask); \ + } while (0) + #endif + + // Multiline string literals in C99! + #ifndef ZPL_MULTILINE + # define ZPL_MULTILINE(...) #__VA_ARGS__ + #endif + + ZPL_END_C_DECLS + +#if defined(ZPL_MODULE_ESSENTIALS) + // file: header/essentials/debug.h + + /* Debugging stuff */ + + ZPL_BEGIN_C_DECLS + + #ifndef ZPL_DEBUG_TRAP + # if defined(_MSC_VER) + # if _MSC_VER < 1300 + # define ZPL_DEBUG_TRAP( ) __asm int 3 /* Trap to debugger! */ + # else + # define ZPL_DEBUG_TRAP( ) __debugbreak( ) + # endif + # elif defined(ZPL_COMPILER_TINYC) + # define ZPL_DEBUG_TRAP( ) zpl_exit(1) + # else + # define ZPL_DEBUG_TRAP( ) __builtin_trap( ) + # endif + #endif + + #ifndef ZPL_ASSERT_MSG + # define ZPL_ASSERT_MSG(cond, msg, ...) \ + do { \ + if (!(cond)) { \ + zpl_assert_handler(#cond, __FILE__, cast(zpl_i64) __LINE__, msg, ##__VA_ARGS__); \ + ZPL_DEBUG_TRAP( ); \ + } \ + } while (0) + #endif + + #ifndef ZPL_ASSERT + # define ZPL_ASSERT(cond) ZPL_ASSERT_MSG(cond, NULL) + #endif + + #ifndef ZPL_ASSERT_NOT_NULL + # define ZPL_ASSERT_NOT_NULL(ptr) ZPL_ASSERT_MSG((ptr) != NULL, #ptr " must not be NULL") + #endif + + // NOTE: Things that shouldn't happen with a message! + #ifndef ZPL_PANIC + # define ZPL_PANIC(msg, ...) ZPL_ASSERT_MSG(0, msg, ##__VA_ARGS__) + #endif + + #ifndef ZPL_NOT_IMPLEMENTED + # define ZPL_NOT_IMPLEMENTED ZPL_PANIC("not implemented") + #endif + + /* Functions */ + + ZPL_DEF void zpl_assert_handler(char const *condition, char const *file, zpl_i32 line, char const *msg, ...); + ZPL_DEF zpl_i32 zpl_assert_crash(char const *condition); + ZPL_DEF void zpl_exit(zpl_u32 code); + + ZPL_END_C_DECLS + // file: header/essentials/memory.h + + /** @file mem.c + @brief Memory manipulation and helpers. + @defgroup memman Memory management + + Consists of pointer arithmetic methods, virtual memory management and custom memory allocators. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + //! Checks if value is power of 2. + ZPL_DEF_INLINE zpl_b32 zpl_is_power_of_two(zpl_isize x); + + //! Aligns address to specified alignment. + ZPL_DEF_INLINE void *zpl_align_forward(void *ptr, zpl_isize alignment); + + //! Aligns value to a specified alignment. + ZPL_DEF_INLINE zpl_i64 zpl_align_forward_i64(zpl_i64 value, zpl_isize alignment); + + //! Aligns value to a specified alignment. + ZPL_DEF_INLINE zpl_u64 zpl_align_forward_u64(zpl_u64 value, zpl_usize alignment); + + //! Moves pointer forward by bytes. + ZPL_DEF_INLINE void *zpl_pointer_add(void *ptr, zpl_isize bytes); + + //! Moves pointer backward by bytes. + ZPL_DEF_INLINE void *zpl_pointer_sub(void *ptr, zpl_isize bytes); + + //! Moves pointer forward by bytes. + ZPL_DEF_INLINE void const *zpl_pointer_add_const(void const *ptr, zpl_isize bytes); + + //! Moves pointer backward by bytes. + ZPL_DEF_INLINE void const *zpl_pointer_sub_const(void const *ptr, zpl_isize bytes); + + //! Calculates difference between two addresses. + ZPL_DEF_INLINE zpl_isize zpl_pointer_diff(void const *begin, void const *end); + + #define zpl_ptr_add zpl_pointer_add + #define zpl_ptr_sub zpl_pointer_sub + #define zpl_ptr_add_const zpl_pointer_add_const + #define zpl_ptr_sub_const zpl_pointer_sub_const + #define zpl_ptr_diff zpl_pointer_diff + + //! Clears up memory at location by specified size. + + //! @param ptr Memory location to clear up. + //! @param size The size to clear up with. + ZPL_DEF_INLINE void zpl_zero_size(void *ptr, zpl_isize size); + + #ifndef zpl_zero_item + //! Clears up an item. + #define zpl_zero_item(t) zpl_zero_size((t), zpl_size_of(*(t))) // NOTE: Pass pointer of struct + + //! Clears up an array. + #define zpl_zero_array(a, count) zpl_zero_size((a), zpl_size_of(*(a)) * count) + #endif + + //! Copy memory from source to destination. + ZPL_DEF_INLINE void *zpl_memmove(void *dest, void const *source, zpl_isize size); + + //! Set constant value at memory location with specified size. + ZPL_DEF_INLINE void *zpl_memset(void *data, zpl_u8 byte_value, zpl_isize size); + + //! Compare two memory locations with specified size. + ZPL_DEF_INLINE zpl_i32 zpl_memcompare(void const *s1, void const *s2, zpl_isize size); + + //! Swap memory contents between 2 locations with size. + ZPL_DEF void zpl_memswap(void *i, void *j, zpl_isize size); + + //! Search for a constant value within the size limit at memory location. + ZPL_DEF void const *zpl_memchr(void const *data, zpl_u8 byte_value, zpl_isize size); + + //! Search for a constant value within the size limit at memory location in backwards. + ZPL_DEF void const *zpl_memrchr(void const *data, zpl_u8 byte_value, zpl_isize size); + + //! Copy non-overlapping memory from source to destination. + ZPL_DEF void *zpl_memcopy(void *dest, void const *source, zpl_isize size); + + #ifndef zpl_memcopy_array + + //! Copy non-overlapping array. + #define zpl_memcopy_array(dst, src, count) zpl_memcopy((dst), (src), zpl_size_of(*(dst)) * (count)) + #endif + + //! Copy an array. + #ifndef zpl_memmove_array + #define zpl_memmove_array(dst, src, count) zpl_memmove((dst), (src), zpl_size_of(*(dst)) * (count)) + #endif + + #ifndef ZPL_BIT_CAST + #define ZPL_BIT_CAST(dest, source) \ + do { \ + ZPL_STATIC_ASSERT(zpl_size_of(*(dest)) <= zpl_size_of(source), "zpl_size_of(*(dest)) !<= zpl_size_of(source)");\ + zpl_memcopy((dest), &(source), zpl_size_of(*dest)); \ + } while (0) + #endif + + #ifndef zpl_kilobytes + #define zpl_kilobytes(x) ((x) * (zpl_i64)(1024)) + #define zpl_megabytes(x) (zpl_kilobytes(x) * (zpl_i64)(1024)) + #define zpl_gigabytes(x) (zpl_megabytes(x) * (zpl_i64)(1024)) + #define zpl_terabytes(x) (zpl_gigabytes(x) * (zpl_i64)(1024)) + #endif + + + /* inlines */ + + #define ZPL__ONES (cast(zpl_usize) - 1 / ZPL_U8_MAX) + #define ZPL__HIGHS (ZPL__ONES * (ZPL_U8_MAX / 2 + 1)) + #define ZPL__HAS_ZERO(x) (((x)-ZPL__ONES) & ~(x)&ZPL__HIGHS) + + ZPL_IMPL_INLINE void *zpl_align_forward(void *ptr, zpl_isize alignment) { + zpl_uintptr p; + + ZPL_ASSERT(zpl_is_power_of_two(alignment)); + + p = cast(zpl_uintptr) ptr; + return cast(void *)((p + (alignment - 1)) & ~(alignment - 1)); + } + + ZPL_IMPL_INLINE zpl_i64 zpl_align_forward_i64(zpl_i64 value, zpl_isize alignment) { + return value + (alignment - value % alignment) % alignment; + } + + ZPL_IMPL_INLINE zpl_u64 zpl_align_forward_u64(zpl_u64 value, zpl_usize alignment) { + return value + (alignment - value % alignment) % alignment; + } + + ZPL_IMPL_INLINE void *zpl_pointer_add(void *ptr, zpl_isize bytes) { return cast(void *)(cast(zpl_u8 *) ptr + bytes); } + ZPL_IMPL_INLINE void *zpl_pointer_sub(void *ptr, zpl_isize bytes) { return cast(void *)(cast(zpl_u8 *) ptr - bytes); } + ZPL_IMPL_INLINE void const *zpl_pointer_add_const(void const *ptr, zpl_isize bytes) { + return cast(void const *)(cast(zpl_u8 const *) ptr + bytes); + } + ZPL_IMPL_INLINE void const *zpl_pointer_sub_const(void const *ptr, zpl_isize bytes) { + return cast(void const *)(cast(zpl_u8 const *) ptr - bytes); + } + ZPL_IMPL_INLINE zpl_isize zpl_pointer_diff(void const *begin, void const *end) { + return cast(zpl_isize)(cast(zpl_u8 const *) end - cast(zpl_u8 const *) begin); + } + + ZPL_IMPL_INLINE void zpl_zero_size(void *ptr, zpl_isize size) { zpl_memset(ptr, 0, size); } + + #if defined(_MSC_VER) && !defined(__clang__) + #pragma intrinsic(__movsb) + #endif + + ZPL_IMPL_INLINE void *zpl_memmove(void *dest, void const *source, zpl_isize n) { + if (dest == NULL) { return NULL; } + + zpl_u8 *d = cast(zpl_u8 *) dest; + zpl_u8 const *s = cast(zpl_u8 const *) source; + + if (d == s) return d; + if (s + n <= d || d + n <= s) // NOTE: Non-overlapping + return zpl_memcopy(d, s, n); + + if (d < s) { + if (cast(zpl_uintptr) s % zpl_size_of(zpl_isize) == cast(zpl_uintptr) d % zpl_size_of(zpl_isize)) { + while (cast(zpl_uintptr) d % zpl_size_of(zpl_isize)) { + if (!n--) return dest; + *d++ = *s++; + } + while (n >= zpl_size_of(zpl_isize)) { + *cast(zpl_isize *) d = *cast(zpl_isize *) s; + n -= zpl_size_of(zpl_isize); + d += zpl_size_of(zpl_isize); + s += zpl_size_of(zpl_isize); + } + } + for (; n; n--) *d++ = *s++; + } else { + if ((cast(zpl_uintptr) s % zpl_size_of(zpl_isize)) == (cast(zpl_uintptr) d % zpl_size_of(zpl_isize))) { + while (cast(zpl_uintptr)(d + n) % zpl_size_of(zpl_isize)) { + if (!n--) return dest; + d[n] = s[n]; + } + while (n >= zpl_size_of(zpl_isize)) { + n -= zpl_size_of(zpl_isize); + *cast(zpl_isize *)(d + n) = *cast(zpl_isize *)(s + n); + } + } + while (n) n--, d[n] = s[n]; + } + + return dest; + } + + ZPL_IMPL_INLINE void *zpl_memset(void *dest, zpl_u8 c, zpl_isize n) { + if (dest == NULL) { return NULL; } + + zpl_u8 *s = cast(zpl_u8 *) dest; + zpl_isize k; + zpl_u32 c32 = ((zpl_u32)-1) / 255 * c; + + if (n == 0) return dest; + s[0] = s[n - 1] = c; + if (n < 3) return dest; + s[1] = s[n - 2] = c; + s[2] = s[n - 3] = c; + if (n < 7) return dest; + s[3] = s[n - 4] = c; + if (n < 9) return dest; + + k = -cast(zpl_intptr) s & 3; + s += k; + n -= k; + n &= -4; + + *cast(zpl_u32 *)(s + 0) = c32; + *cast(zpl_u32 *)(s + n - 4) = c32; + if (n < 9) return dest; + *cast(zpl_u32 *)(s + 4) = c32; + *cast(zpl_u32 *)(s + 8) = c32; + *cast(zpl_u32 *)(s + n - 12) = c32; + *cast(zpl_u32 *)(s + n - 8) = c32; + if (n < 25) return dest; + *cast(zpl_u32 *)(s + 12) = c32; + *cast(zpl_u32 *)(s + 16) = c32; + *cast(zpl_u32 *)(s + 20) = c32; + *cast(zpl_u32 *)(s + 24) = c32; + *cast(zpl_u32 *)(s + n - 28) = c32; + *cast(zpl_u32 *)(s + n - 24) = c32; + *cast(zpl_u32 *)(s + n - 20) = c32; + *cast(zpl_u32 *)(s + n - 16) = c32; + + k = 24 + (cast(zpl_uintptr) s & 4); + s += k; + n -= k; + + { + zpl_u64 c64 = (cast(zpl_u64) c32 << 32) | c32; + while (n > 31) { + *cast(zpl_u64 *)(s + 0) = c64; + *cast(zpl_u64 *)(s + 8) = c64; + *cast(zpl_u64 *)(s + 16) = c64; + *cast(zpl_u64 *)(s + 24) = c64; + + n -= 32; + s += 32; + } + } + + return dest; + } + + ZPL_IMPL_INLINE zpl_i32 zpl_memcompare(void const *s1, void const *s2, zpl_isize size) { + zpl_u8 const *s1p8 = cast(zpl_u8 const *) s1; + zpl_u8 const *s2p8 = cast(zpl_u8 const *) s2; + + if (s1 == NULL || s2 == NULL) { return 0; } + + while (size--) { + zpl_isize d; + if ((d = (*s1p8++ - *s2p8++)) != 0) return cast(zpl_i32) d; + } + return 0; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_is_power_of_two(zpl_isize x) { + if (x <= 0) return false; + return !(x & (x - 1)); + } + + ZPL_END_C_DECLS + // file: header/essentials/memory_custom.h + + //////////////////////////////////////////////////////////////// + // + // Custom Allocation + // + // + + ZPL_BEGIN_C_DECLS + + typedef enum zpl_alloc_type { + ZPL_ALLOCATION_ALLOC, + ZPL_ALLOCATION_FREE, + ZPL_ALLOCATION_FREE_ALL, + ZPL_ALLOCATION_RESIZE, + } zpl_alloc_type; + + // NOTE: This is useful so you can define an allocator of the same type and parameters + #define ZPL_ALLOCATOR_PROC(name) \ + void *name(void *allocator_data, zpl_alloc_type type, zpl_isize size, zpl_isize alignment, void *old_memory, \ + zpl_isize old_size, zpl_u64 flags) + typedef ZPL_ALLOCATOR_PROC(zpl_allocator_proc); + + + typedef struct zpl_allocator { + zpl_allocator_proc *proc; + void *data; + } zpl_allocator; + + typedef enum zpl_alloc_flag { + ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO = ZPL_BIT(0), + } zpl_alloc_flag; + + #ifndef ZPL_DEFAULT_MEMORY_ALIGNMENT + #define ZPL_DEFAULT_MEMORY_ALIGNMENT (2 * zpl_size_of(void *)) + #endif + + #ifndef ZPL_DEFAULT_ALLOCATOR_FLAGS + #define ZPL_DEFAULT_ALLOCATOR_FLAGS (ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) + #endif + + //! Allocate memory with specified alignment. + ZPL_DEF_INLINE void *zpl_alloc_align(zpl_allocator a, zpl_isize size, zpl_isize alignment); + + //! Allocate memory with default alignment. + ZPL_DEF_INLINE void *zpl_alloc(zpl_allocator a, zpl_isize size); + + //! Free allocated memory. + ZPL_DEF_INLINE void zpl_free(zpl_allocator a, void *ptr); + + //! Free all memory allocated by an allocator. + ZPL_DEF_INLINE void zpl_free_all(zpl_allocator a); + + //! Resize an allocated memory. + ZPL_DEF_INLINE void *zpl_resize(zpl_allocator a, void *ptr, zpl_isize old_size, zpl_isize new_size); + + //! Resize an allocated memory with specified alignment. + ZPL_DEF_INLINE void *zpl_resize_align(zpl_allocator a, void *ptr, zpl_isize old_size, zpl_isize new_size, zpl_isize alignment); + + //! Allocate memory and copy data into it. + ZPL_DEF_INLINE void *zpl_alloc_copy(zpl_allocator a, void const *src, zpl_isize size); + + //! Allocate memory with specified alignment and copy data into it. + ZPL_DEF_INLINE void *zpl_alloc_copy_align(zpl_allocator a, void const *src, zpl_isize size, zpl_isize alignment); + + //! Allocate memory for null-terminated C-String. + ZPL_DEF char *zpl_alloc_str(zpl_allocator a, char const *str); + + //! Allocate memory for C-String with specified size. + ZPL_DEF_INLINE char *zpl_alloc_str_len(zpl_allocator a, char const *str, zpl_isize len); + + #ifndef zpl_alloc_item + + //! Allocate memory for an item. + #define zpl_alloc_item(allocator_, Type) (Type *)zpl_alloc(allocator_, zpl_size_of(Type)) + + //! Allocate memory for an array of items. + #define zpl_alloc_array(allocator_, Type, count) (Type *)zpl_alloc(allocator_, zpl_size_of(Type) * (count)) + #endif + + /* heap memory analysis tools */ + /* define ZPL_HEAP_ANALYSIS to enable this feature */ + /* call zpl_heap_stats_init at the beginning of the entry point */ + /* you can call zpl_heap_stats_check near the end of the execution to validate any possible leaks */ + ZPL_DEF void zpl_heap_stats_init(void); + ZPL_DEF zpl_isize zpl_heap_stats_used_memory(void); + ZPL_DEF zpl_isize zpl_heap_stats_alloc_count(void); + ZPL_DEF void zpl_heap_stats_check(void); + + //! Allocate/Resize memory using default options. + + //! Use this if you don't need a "fancy" resize allocation + ZPL_DEF_INLINE void *zpl_default_resize_align(zpl_allocator a, void *ptr, zpl_isize old_size, zpl_isize new_size, zpl_isize alignment); + + //! The heap allocator backed by operating system's memory manager. + ZPL_DEF_INLINE zpl_allocator zpl_heap_allocator(void); + ZPL_DEF ZPL_ALLOCATOR_PROC(zpl_heap_allocator_proc); + + #ifndef zpl_malloc + + //! Helper to allocate memory using heap allocator. + #define zpl_malloc(sz) zpl_alloc(zpl_heap_allocator( ), sz) + + //! Helper to free memory allocated by heap allocator. + #define zpl_mfree(ptr) zpl_free(zpl_heap_allocator( ), ptr) + + //! Alias to heap allocator. + #define zpl_heap zpl_heap_allocator + #endif + + // + // Arena Allocator + // + + typedef struct zpl_arena { + zpl_allocator backing; + void *physical_start; + zpl_isize total_size; + zpl_isize total_allocated; + zpl_isize temp_count; + } zpl_arena; + + //! Initialize memory arena from existing memory region. + ZPL_DEF_INLINE void zpl_arena_init_from_memory(zpl_arena *arena, void *start, zpl_isize size); + + //! Initialize memory arena using existing memory allocator. + ZPL_DEF_INLINE void zpl_arena_init_from_allocator(zpl_arena *arena, zpl_allocator backing, zpl_isize size); + + //! Initialize memory arena within an existing parent memory arena. + ZPL_DEF_INLINE void zpl_arena_init_sub(zpl_arena *arena, zpl_arena *parent_arena, zpl_isize size); + + //! Release the memory used by memory arena. + ZPL_DEF_INLINE void zpl_arena_free(zpl_arena *arena); + + + //! Retrieve memory arena's aligned allocation address. + ZPL_DEF_INLINE zpl_isize zpl_arena_alignment_of(zpl_arena *arena, zpl_isize alignment); + + //! Retrieve memory arena's remaining size. + ZPL_DEF_INLINE zpl_isize zpl_arena_size_remaining(zpl_arena *arena, zpl_isize alignment); + + //! Check whether memory arena has any temporary snapshots. + ZPL_DEF_INLINE void zpl_arena_check(zpl_arena *arena); + + //! Allocation Types: alloc, free_all, resize + ZPL_DEF_INLINE zpl_allocator zpl_arena_allocator(zpl_arena *arena); + ZPL_DEF ZPL_ALLOCATOR_PROC(zpl_arena_allocator_proc); + + + typedef struct zpl_arena_snapshot { + zpl_arena *arena; + zpl_isize original_count; + } zpl_arena_snapshot; + + //! Capture a snapshot of used memory in a memory arena. + ZPL_DEF_INLINE zpl_arena_snapshot zpl_arena_snapshot_begin(zpl_arena *arena); + + //! Reset memory arena's usage by a captured snapshot. + ZPL_DEF_INLINE void zpl_arena_snapshot_end(zpl_arena_snapshot tmp_mem); + + // + // Pool Allocator + // + + + typedef struct zpl_pool { + zpl_allocator backing; + void *physical_start; + void *free_list; + zpl_isize block_size; + zpl_isize block_align; + zpl_isize total_size; + zpl_isize num_blocks; + } zpl_pool; + + + //! Initialize pool allocator. + ZPL_DEF_INLINE void zpl_pool_init(zpl_pool *pool, zpl_allocator backing, zpl_isize num_blocks, zpl_isize block_size); + + //! Initialize pool allocator with specific block alignment. + ZPL_DEF void zpl_pool_init_align(zpl_pool *pool, zpl_allocator backing, zpl_isize num_blocks, zpl_isize block_size, + zpl_isize block_align); + + //! Release the resources used by pool allocator. + ZPL_DEF_INLINE void zpl_pool_free(zpl_pool *pool); + + //! Allocation Types: alloc, free + ZPL_DEF_INLINE zpl_allocator zpl_pool_allocator(zpl_pool *pool); + ZPL_DEF ZPL_ALLOCATOR_PROC(zpl_pool_allocator_proc); + + // + // Scratch Memory Allocator - Ring Buffer Based Arena + // + + typedef struct zpl_allocation_header_ev { + zpl_isize size; + } zpl_allocation_header_ev; + + ZPL_DEF_INLINE zpl_allocation_header_ev *zpl_allocation_header(void *data); + ZPL_DEF_INLINE void zpl_allocation_header_fill(zpl_allocation_header_ev *header, void *data, zpl_isize size); + + #if defined(ZPL_ARCH_32_BIT) + #define ZPL_ISIZE_HIGH_BIT 0x80000000 + #elif defined(ZPL_ARCH_64_BIT) + #define ZPL_ISIZE_HIGH_BIT 0x8000000000000000ll + #else + #error + #endif + + typedef struct zpl_scratch_memory { + void *physical_start; + zpl_isize total_size; + void *alloc_point; + void *free_point; + } zpl_scratch_memory; + + //! Initialize ring buffer arena. + ZPL_DEF void zpl_scratch_memory_init(zpl_scratch_memory *s, void *start, zpl_isize size); + + //! Check whether ring buffer arena is in use. + ZPL_DEF zpl_b32 zpl_scratch_memory_is_in_use(zpl_scratch_memory *s, void *ptr); + + //! Allocation Types: alloc, free, free_all, resize + ZPL_DEF zpl_allocator zpl_scratch_allocator(zpl_scratch_memory *s); + ZPL_DEF ZPL_ALLOCATOR_PROC(zpl_scratch_allocator_proc); + + // + // Stack Memory Allocator + // + + + typedef struct zpl_stack_memory { + zpl_allocator backing; + + void *physical_start; + zpl_usize total_size; + zpl_usize allocated; + } zpl_stack_memory; + + //! Initialize stack allocator from existing memory. + ZPL_DEF_INLINE void zpl_stack_memory_init_from_memory(zpl_stack_memory *s, void *start, zpl_isize size); + + //! Initialize stack allocator using existing memory allocator. + ZPL_DEF_INLINE void zpl_stack_memory_init(zpl_stack_memory *s, zpl_allocator backing, zpl_isize size); + + //! Check whether stack allocator is in use. + ZPL_DEF_INLINE zpl_b32 zpl_stack_memory_is_in_use(zpl_stack_memory *s, void *ptr); + + //! Release the resources used by stack allocator. + ZPL_DEF_INLINE void zpl_stack_memory_free(zpl_stack_memory *s); + + //! Allocation Types: alloc, free, free_all + ZPL_DEF_INLINE zpl_allocator zpl_stack_allocator(zpl_stack_memory *s); + ZPL_DEF ZPL_ALLOCATOR_PROC(zpl_stack_allocator_proc); + + /* inlines */ + + ZPL_IMPL_INLINE void *zpl_alloc_align(zpl_allocator a, zpl_isize size, zpl_isize alignment) { + return a.proc(a.data, ZPL_ALLOCATION_ALLOC, size, alignment, NULL, 0, ZPL_DEFAULT_ALLOCATOR_FLAGS); + } + ZPL_IMPL_INLINE void *zpl_alloc(zpl_allocator a, zpl_isize size) { + return zpl_alloc_align(a, size, ZPL_DEFAULT_MEMORY_ALIGNMENT); + } + ZPL_IMPL_INLINE void zpl_free(zpl_allocator a, void *ptr) { + if (ptr != NULL) a.proc(a.data, ZPL_ALLOCATION_FREE, 0, 0, ptr, 0, ZPL_DEFAULT_ALLOCATOR_FLAGS); + } + ZPL_IMPL_INLINE void zpl_free_all(zpl_allocator a) { + a.proc(a.data, ZPL_ALLOCATION_FREE_ALL, 0, 0, NULL, 0, ZPL_DEFAULT_ALLOCATOR_FLAGS); + } + ZPL_IMPL_INLINE void *zpl_resize(zpl_allocator a, void *ptr, zpl_isize old_size, zpl_isize new_size) { + return zpl_resize_align(a, ptr, old_size, new_size, ZPL_DEFAULT_MEMORY_ALIGNMENT); + } + ZPL_IMPL_INLINE void *zpl_resize_align(zpl_allocator a, void *ptr, zpl_isize old_size, zpl_isize new_size, zpl_isize alignment) { + return a.proc(a.data, ZPL_ALLOCATION_RESIZE, new_size, alignment, ptr, old_size, ZPL_DEFAULT_ALLOCATOR_FLAGS); + } + + ZPL_IMPL_INLINE void *zpl_alloc_copy(zpl_allocator a, void const *src, zpl_isize size) { + return zpl_memcopy(zpl_alloc(a, size), src, size); + } + ZPL_IMPL_INLINE void *zpl_alloc_copy_align(zpl_allocator a, void const *src, zpl_isize size, zpl_isize alignment) { + return zpl_memcopy(zpl_alloc_align(a, size, alignment), src, size); + } + + ZPL_IMPL_INLINE char *zpl_alloc_str_len(zpl_allocator a, char const *str, zpl_isize len) { + char *result; + result = cast(char *) zpl_alloc(a, len + 1); + zpl_memmove(result, str, len); + result[len] = '\0'; + return result; + } + + ZPL_IMPL_INLINE void *zpl_default_resize_align(zpl_allocator a, void *old_memory, zpl_isize old_size, zpl_isize new_size, + zpl_isize alignment) { + if (!old_memory) return zpl_alloc_align(a, new_size, alignment); + + if (new_size == 0) { + zpl_free(a, old_memory); + return NULL; + } + + if (new_size < old_size) new_size = old_size; + + if (old_size == new_size) { + return old_memory; + } else { + void *new_memory = zpl_alloc_align(a, new_size, alignment); + if (!new_memory) return NULL; + zpl_memmove(new_memory, old_memory, zpl_min(new_size, old_size)); + zpl_free(a, old_memory); + return new_memory; + } + } + + // + // Heap Allocator + // + + ZPL_IMPL_INLINE zpl_allocator zpl_heap_allocator(void) { + zpl_allocator a; + a.proc = zpl_heap_allocator_proc; + a.data = NULL; + return a; + } + + // + // Arena Allocator + // + + ZPL_IMPL_INLINE void zpl_arena_init_from_memory(zpl_arena *arena, void *start, zpl_isize size) { + arena->backing.proc = NULL; + arena->backing.data = NULL; + arena->physical_start = start; + arena->total_size = size; + arena->total_allocated = 0; + arena->temp_count = 0; + } + + ZPL_IMPL_INLINE void zpl_arena_init_from_allocator(zpl_arena *arena, zpl_allocator backing, zpl_isize size) { + arena->backing = backing; + arena->physical_start = zpl_alloc(backing, size); // NOTE: Uses default alignment + arena->total_size = size; + arena->total_allocated = 0; + arena->temp_count = 0; + } + + ZPL_IMPL_INLINE void zpl_arena_init_sub(zpl_arena *arena, zpl_arena *parent_arena, zpl_isize size) { + zpl_arena_init_from_allocator(arena, zpl_arena_allocator(parent_arena), size); + } + + ZPL_IMPL_INLINE void zpl_arena_free(zpl_arena *arena) { + if (arena->backing.proc) { + zpl_free(arena->backing, arena->physical_start); + arena->physical_start = NULL; + } + } + + ZPL_IMPL_INLINE zpl_isize zpl_arena_alignment_of(zpl_arena *arena, zpl_isize alignment) { + zpl_isize alignment_offset, result_pointer, mask; + ZPL_ASSERT(zpl_is_power_of_two(alignment)); + + alignment_offset = 0; + result_pointer = cast(zpl_isize) arena->physical_start + arena->total_allocated; + mask = alignment - 1; + if (result_pointer & mask) alignment_offset = alignment - (result_pointer & mask); + + return alignment_offset; + } + + ZPL_IMPL_INLINE zpl_isize zpl_arena_size_remaining(zpl_arena *arena, zpl_isize alignment) { + zpl_isize result = arena->total_size - (arena->total_allocated + zpl_arena_alignment_of(arena, alignment)); + return result; + } + + ZPL_IMPL_INLINE void zpl_arena_check(zpl_arena *arena) { ZPL_ASSERT(arena->temp_count == 0); } + + ZPL_IMPL_INLINE zpl_allocator zpl_arena_allocator(zpl_arena *arena) { + zpl_allocator allocator; + allocator.proc = zpl_arena_allocator_proc; + allocator.data = arena; + return allocator; + } + + ZPL_IMPL_INLINE zpl_arena_snapshot zpl_arena_snapshot_begin(zpl_arena *arena) { + zpl_arena_snapshot tmp; + tmp.arena = arena; + tmp.original_count = arena->total_allocated; + arena->temp_count++; + return tmp; + } + + ZPL_IMPL_INLINE void zpl_arena_snapshot_end(zpl_arena_snapshot tmp) { + ZPL_ASSERT(tmp.arena->total_allocated >= tmp.original_count); + ZPL_ASSERT(tmp.arena->temp_count > 0); + tmp.arena->total_allocated = tmp.original_count; + tmp.arena->temp_count--; + } + + // + // Pool Allocator + // + + ZPL_IMPL_INLINE void zpl_pool_init(zpl_pool *pool, zpl_allocator backing, zpl_isize num_blocks, zpl_isize block_size) { + zpl_pool_init_align(pool, backing, num_blocks, block_size, ZPL_DEFAULT_MEMORY_ALIGNMENT); + } + + ZPL_IMPL_INLINE void zpl_pool_free(zpl_pool *pool) { + if (pool->backing.proc) { zpl_free(pool->backing, pool->physical_start); } + } + + ZPL_IMPL_INLINE zpl_allocator zpl_pool_allocator(zpl_pool *pool) { + zpl_allocator allocator; + allocator.proc = zpl_pool_allocator_proc; + allocator.data = pool; + return allocator; + } + + ZPL_IMPL_INLINE zpl_allocation_header_ev *zpl_allocation_header(void *data) { + zpl_isize *p = cast(zpl_isize *) data; + while (p[-1] == cast(zpl_isize)(-1)) p--; + return cast(zpl_allocation_header_ev *) p - 1; + } + + ZPL_IMPL_INLINE void zpl_allocation_header_fill(zpl_allocation_header_ev *header, void *data, zpl_isize size) { + zpl_isize *ptr; + header->size = size; + ptr = cast(zpl_isize *)(header + 1); + while (cast(void *) ptr < data) *ptr++ = cast(zpl_isize)(-1); + } + + // + // Stack Memory Allocator + // + + #define ZPL_STACK_ALLOC_OFFSET sizeof(zpl_u64) + ZPL_STATIC_ASSERT(ZPL_STACK_ALLOC_OFFSET == 8, "ZPL_STACK_ALLOC_OFFSET != 8"); + + ZPL_IMPL_INLINE void zpl_stack_memory_init_from_memory(zpl_stack_memory *s, void *start, zpl_isize size) { + s->physical_start = start; + s->total_size = size; + s->allocated = 0; + } + + ZPL_IMPL_INLINE void zpl_stack_memory_init(zpl_stack_memory *s, zpl_allocator backing, zpl_isize size) { + s->backing = backing; + s->physical_start = zpl_alloc(backing, size); + s->total_size = size; + s->allocated = 0; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_stack_memory_is_in_use(zpl_stack_memory *s, void *ptr) { + if (s->allocated == 0) return false; + + if (ptr > s->physical_start && ptr < zpl_pointer_add(s->physical_start, s->total_size)) { return true; } + + return false; + } + + ZPL_IMPL_INLINE void zpl_stack_memory_free(zpl_stack_memory *s) { + if (s->backing.proc) { + zpl_free(s->backing, s->physical_start); + s->physical_start = NULL; + } + } + + ZPL_IMPL_INLINE zpl_allocator zpl_stack_allocator(zpl_stack_memory *s) { + zpl_allocator a; + a.proc = zpl_stack_allocator_proc; + a.data = s; + return a; + } + + ZPL_END_C_DECLS + // file: header/essentials/collections/array.h + + //////////////////////////////////////////////////////////////// + // + // Dynamic Array (POD Types) + // + // zpl_array(Type) works like zpl_string or zpl_buffer where the actual type is just a pointer to the first + // element. + // + // Available Procedures for zpl_array(Type) + // zpl_array_init + // zpl_array_free + // zpl_array_set_capacity + // zpl_array_grow + // zpl_array_append + // zpl_array_appendv + // zpl_array_pop + // zpl_array_clear + // zpl_array_back + // zpl_array_front + // zpl_array_resize + // zpl_array_reserve + // + + #if 0 // Example + void foo(void) { + zpl_isize i; + int test_values[] = {4, 2, 1, 7}; + zpl_allocator a = zpl_heap_allocator(); + zpl_array(int) items; + + zpl_array_init(items, a); + + zpl_array_append(items, 1); + zpl_array_append(items, 4); + zpl_array_append(items, 9); + zpl_array_append(items, 16); + + items[1] = 3; // Manually set value + // NOTE: No array bounds checking + + for (i = 0; i < items.count; i++) + zpl_printf("%d\n", items[i]); + // 1 + // 3 + // 9 + // 16 + + zpl_array_clear(items); + + zpl_array_appendv(items, test_values, zpl_count_of(test_values)); + for (i = 0; i < items.count; i++) + zpl_printf("%d\n", items[i]); + // 4 + // 2 + // 1 + // 7 + + zpl_array_free(items); + } + #endif + + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_array_header { + zpl_isize elem_size; + zpl_isize count; + zpl_isize capacity; + zpl_allocator allocator; + } zpl_array_header; + + #define zpl_array(Type) Type * + + #define zpl_array_make(Type, Name, allocator) Type *Name; zpl_array_init(Name, allocator) + + #ifndef ZPL_ARRAY_GROW_FORMULA + #define ZPL_ARRAY_GROW_FORMULA(x) (2 * (x) + 8) + #endif + + ZPL_STATIC_ASSERT(ZPL_ARRAY_GROW_FORMULA(0) > 0, "ZPL_ARRAY_GROW_FORMULA(0) <= 0"); + + #define ZPL_ARRAY_HEADER(x) (cast(zpl_array_header *)(x) - 1) + #define zpl_array_allocator(x) (ZPL_ARRAY_HEADER(x)->allocator) + #define zpl_array_elem_size(x) (ZPL_ARRAY_HEADER(x)->elem_size) + #define zpl_array_count(x) (ZPL_ARRAY_HEADER(x)->count) + #define zpl_array_capacity(x) (ZPL_ARRAY_HEADER(x)->capacity) + #define zpl_array_end(x) (x + (zpl_array_count(x) - 1)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_init_reserve(void **zpl__array_, zpl_allocator allocator_, zpl_isize elem_size, zpl_isize cap) { + zpl_array_header *zpl__ah = + cast(zpl_array_header *) zpl_alloc(allocator_, zpl_size_of(zpl_array_header) + elem_size * cap); + if (!zpl__ah) return false; + zpl__ah->allocator = allocator_; + zpl__ah->elem_size = elem_size; + zpl__ah->count = 0; + zpl__ah->capacity = cap; + *zpl__array_ = cast(void *)(zpl__ah + 1); + return true; + } + + #define zpl_array_init_reserve(x, allocator_, cap) zpl__array_init_reserve(cast(void **) & (x), allocator_, zpl_size_of(*(x)), (cap)) + + // NOTE: Give it an initial default capacity + #define zpl_array_init(x, allocator) zpl_array_init_reserve(x, allocator, ZPL_ARRAY_GROW_FORMULA(0)) + + #define zpl_array_free(x) \ + do { \ + if (x) { \ + zpl_array_header *zpl__ah = ZPL_ARRAY_HEADER(x); \ + zpl_free(zpl__ah->allocator, zpl__ah); \ + } \ + } while (0) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_set_capacity(void **array, zpl_isize capacity) { + zpl_array_header *h = ZPL_ARRAY_HEADER(*array); + if (capacity == h->capacity) return true; + if (capacity < h->count) h->count = capacity; + zpl_isize size = zpl_size_of(zpl_array_header) + h->elem_size * capacity; + zpl_array_header *nh = cast(zpl_array_header *) zpl_alloc(h->allocator, size); + if (!nh) return false; + zpl_memmove(nh, h, zpl_size_of(zpl_array_header) + h->elem_size * h->count); + nh->allocator = h->allocator; + nh->elem_size = h->elem_size; + nh->count = h->count; + nh->capacity = capacity; + zpl_free(h->allocator, h); + *array = nh + 1; + return true; + } + + #define zpl_array_set_capacity(x, capacity) zpl__array_set_capacity(cast(void **) & (x), (capacity)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_grow(void **x, zpl_isize min_capacity) { + zpl_isize new_capacity = ZPL_ARRAY_GROW_FORMULA(zpl_array_capacity(*x)); + if (new_capacity < min_capacity) new_capacity = min_capacity; + return zpl__array_set_capacity(x, new_capacity); + } + + #define zpl_array_grow(x, min_capacity) zpl__array_grow(cast(void **) & (x), (min_capacity)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_append_helper(void **x) { + if (zpl_array_capacity(*x) < zpl_array_count(*x) + 1) { + if (!zpl__array_grow(x, 0)) return false; + } + return true; + } + + #define zpl_array_append(x, item) (zpl__array_append_helper(cast(void **) & (x)) && (((x)[zpl_array_count(x)++] = (item)), true)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_append_at_helper(void **x, zpl_isize ind) { + if (ind >= zpl_array_count(*x)) ind = zpl_array_count(*x) - 1; + if (ind < 0) ind = 0; + if (zpl_array_capacity(*x) < zpl_array_count(*x) + 1) { + if (!zpl__array_grow(x, 0)) return false; + } + zpl_i8 *s = (cast(zpl_i8*)*x) + ind*zpl_array_elem_size(*x); + zpl_memmove(s + zpl_array_elem_size(*x), s, zpl_array_elem_size(*x) * (zpl_array_count(*x) - ind)); + return true; + } + + #define zpl_array_append_at(x, item, ind) (zpl__array_append_at_helper(cast(void **) & (x), (ind)) && (((x)[ind] = (item)), zpl_array_count(x)++, true)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_appendv(void **x, void *items, zpl_isize item_size, zpl_isize item_count) { + ZPL_ASSERT(item_size == zpl_array_elem_size(*x)); + if (zpl_array_capacity(*x) < zpl_array_count(*x) + item_count) { + if (!zpl__array_grow(x, zpl_array_count(*x) + item_count)) return false; + } + zpl_memcopy((cast(zpl_i8*)*x) + zpl_array_count(*x)*zpl_array_elem_size(*x), items, zpl_array_elem_size(*x) * item_count); + zpl_array_count(*x) += item_count; + return true; + } + + #define zpl_array_appendv(x, items, item_count) zpl__array_appendv(cast(void **) & (x), (items), zpl_size_of((items)[0]), (item_count)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_appendv_at(void **x, void *items, zpl_isize item_size, zpl_isize item_count, zpl_isize ind) { + if (ind >= zpl_array_count(*x)) return zpl__array_appendv(x, items, item_size, item_count); + ZPL_ASSERT(item_size == zpl_array_elem_size(*x)); + if (zpl_array_capacity(*x) < zpl_array_count(*x) + item_count) { + if (!zpl__array_grow(x, zpl_array_count(*x) + item_count)) return false; + } + zpl_memmove((cast(zpl_i8*)*x) + (ind + item_count)*zpl_array_elem_size(*x), + (cast(zpl_i8*)*x) + ind*zpl_array_elem_size(*x), zpl_array_elem_size(*x) * (zpl_array_count(*x) - ind)); + zpl_memcopy((cast(zpl_i8*)*x) + ind*zpl_array_elem_size(*x), items, zpl_array_elem_size(*x) * item_count); + zpl_array_count(*x) += item_count; + return true; + } + + #define zpl_array_appendv_at(x, items, item_count, ind) zpl__array_appendv_at(cast(void **) & (x), (items), zpl_size_of((items)[0]), (item_count), (ind)) + + #define zpl_array_fill(x, begin, end, value) \ + do { \ + ZPL_ASSERT((begin) >= 0 && (end) <= zpl_array_count(x)); \ + ZPL_ASSERT(zpl_size_of(value) == zpl_size_of((x)[0])); \ + for (zpl_isize i = (begin); i < (end); i++) { x[i] = value; } \ + } while (0) + + #define zpl_array_remove_at(x, index) \ + do { \ + zpl_array_header *zpl__ah = ZPL_ARRAY_HEADER(x); \ + ZPL_ASSERT(index < zpl__ah->count); \ + zpl_memmove(x + index, x + index + 1, zpl_size_of(x[0]) * (zpl__ah->count - index - 1)); \ + --zpl__ah->count; \ + } while (0) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_copy_init(void **y, void **x) { + if (!zpl__array_init_reserve(y, zpl_array_allocator(*x), zpl_array_elem_size(*x), zpl_array_capacity(*x))) + return false; + zpl_memcopy(*y, *x, zpl_array_capacity(*x) * zpl_array_elem_size(*x)); + zpl_array_count(*y) = zpl_array_count(*x); + return true; + } + + #define zpl_array_copy_init(y, x) zpl__array_copy_init(cast(void **) & (y), cast(void **) & (x)) + + #define zpl_array_pop(x) \ + do { \ + ZPL_ASSERT(ZPL_ARRAY_HEADER(x)->count > 0); \ + ZPL_ARRAY_HEADER(x)->count--; \ + } while (0) + #define zpl_array_back(x) x[ZPL_ARRAY_HEADER(x)->count - 1] + #define zpl_array_front(x) x[0] + #define zpl_array_clear(x) \ + do { ZPL_ARRAY_HEADER(x)->count = 0; } while (0) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_resize(void **x, zpl_isize new_count) { + if (ZPL_ARRAY_HEADER(*x)->capacity < new_count) { + if (!zpl__array_grow(x, new_count)) return false; + } + ZPL_ARRAY_HEADER(*x)->count = new_count; + return true; + } + + #define zpl_array_resize(x, new_count) zpl__array_resize(cast(void **) & (x), (new_count)) + + ZPL_IMPL_INLINE zpl_b8 zpl__array_reserve(void **x, zpl_isize new_capacity) { + if (ZPL_ARRAY_HEADER(*x)->capacity < new_capacity) return zpl__array_set_capacity(x, new_capacity); + return true; + } + + #define zpl_array_reserve(x, new_capacity) zpl__array_reserve(cast(void **) & (x), (new_capacity)) + + ZPL_END_C_DECLS + // file: header/essentials/collections/buffer.h + + //////////////////////////////////////////////////////////////// + // + // Fixed Capacity Buffer (POD Types) + // + // + // zpl_buffer(Type) works like zpl_string or zpl_array where the actual type is just a pointer to the first + // element. + // + // Available Procedures for zpl_buffer(Type) + // zpl_buffer_init + // zpl_buffer_free + // zpl_buffer_append + // zpl_buffer_appendv + // zpl_buffer_pop + // zpl_buffer_clear + + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_buffer_header { + zpl_allocator backing; + zpl_isize count; + zpl_isize capacity; + } zpl_buffer_header; + + #define zpl_buffer(Type) Type * + + #define zpl_buffer_make(Type, Name, allocator, cap) Type *Name; zpl_buffer_init(Name, allocator, cap) + + #define ZPL_BUFFER_HEADER(x) (cast(zpl_buffer_header *)(x) - 1) + #define zpl_buffer_count(x) (ZPL_BUFFER_HEADER(x)->count) + #define zpl_buffer_capacity(x) (ZPL_BUFFER_HEADER(x)->capacity) + #define zpl_buffer_end(x) (x + (zpl_buffer_count(x) - 1)) + #define zpl_buffer_allocator(x) (ZPL_BUFFER_HEADER(x)->backing) + + #define zpl_buffer_init(x, allocator, cap) \ + do { \ + void **nx = cast(void **) & (x); \ + zpl_buffer_header *zpl__bh = \ + cast(zpl_buffer_header *) zpl_alloc((allocator), sizeof(zpl_buffer_header) + (cap)*zpl_size_of(*(x))); \ + zpl__bh->backing = allocator; \ + zpl__bh->count = 0; \ + zpl__bh->capacity = cap; \ + *nx = cast(void *)(zpl__bh + 1); \ + } while (0) + + #define zpl_buffer_free(x) (zpl_free(ZPL_BUFFER_HEADER(x)->backing, ZPL_BUFFER_HEADER(x))) + + #define zpl_buffer_append(x, item) \ + do { (x)[zpl_buffer_count(x)++] = (item); } while (0) + + #define zpl_buffer_appendv(x, items, item_count) \ + do { \ + ZPL_ASSERT(zpl_size_of(*(items)) == zpl_size_of(*(x))); \ + ZPL_ASSERT(zpl_buffer_count(x) + item_count <= zpl_buffer_capacity(x)); \ + zpl_memcopy(&(x)[zpl_buffer_count(x)], (items), zpl_size_of(*(x)) * (item_count)); \ + zpl_buffer_count(x) += (item_count); \ + } while (0) + + #define zpl_buffer_copy_init(y, x) \ + do { \ + zpl_buffer_init(y, zpl_buffer_allocator(x), zpl_buffer_capacity(x)); \ + zpl_memcopy(y, x, zpl_buffer_capacity(x) * zpl_size_of(*x)); \ + zpl_buffer_count(y) = zpl_buffer_count(x); \ + } while (0) + + #define zpl_buffer_pop(x) \ + do { \ + ZPL_ASSERT(zpl_buffer_count(x) > 0); \ + zpl_buffer_count(x)--; \ + } while (0) + #define zpl_buffer_clear(x) \ + do { zpl_buffer_count(x) = 0; } while (0) + + ZPL_END_C_DECLS + // file: header/essentials/collections/list.h + + //////////////////////////////////////////////////////////////// + // + // Linked List + // + // zpl_list encapsulates pointer to data and points to the next and the previous element in the list. + // + // Available Procedures for zpl_list + // zpl_list_init + // zpl_list_add + // zpl_list_remove + + + ZPL_BEGIN_C_DECLS + + #if 0 + #define ZPL_IMPLEMENTATION + #include "zpl.h" + int main(void) + { + zpl_list s, *head, *cursor; + zpl_list_init(&s, "it is optional to call init: "); + head = cursor = &s; + + // since we can construct an element implicitly this way + // the second field gets overwritten once we add it to a list. + zpl_list a = {"hello"}; + cursor = zpl_list_add(cursor, &a); + + zpl_list b = {"world"}; + cursor = zpl_list_add(cursor, &b); + + zpl_list c = {"!!! OK"}; + cursor = zpl_list_add(cursor, &c); + + for (zpl_list *l=head; l; l=l->next) { + zpl_printf("%s ", cast(char *)l->ptr); + } + zpl_printf("\n"); + + return 0; + } + #endif + + + typedef struct zpl__list { + void const *ptr; + struct zpl__list *next, *prev; + } zpl_list; + + ZPL_DEF_INLINE void zpl_list_init(zpl_list *list, void const *ptr); + ZPL_DEF_INLINE zpl_list *zpl_list_add(zpl_list *list, zpl_list *item); + + // NOTE(zaklaus): Returns a pointer to the next node (or NULL if the removed node has no trailing node.) + ZPL_DEF_INLINE zpl_list *zpl_list_remove(zpl_list *list); + + + ZPL_IMPL_INLINE void zpl_list_init(zpl_list *list, void const *ptr) { + zpl_list list_ = { 0 }; + *list = list_; + list->ptr = ptr; + } + + ZPL_IMPL_INLINE zpl_list *zpl_list_add(zpl_list *list, zpl_list *item) { + item->next = NULL; + + if (list->next) { item->next = list->next; } + + list->next = item; + item->prev = list; + return item; + } + + ZPL_IMPL_INLINE zpl_list *zpl_list_remove(zpl_list *list) { + if (list->prev) { list->prev->next = list->next; } + + return list->next; + } + + ZPL_END_C_DECLS + // file: header/essentials/collections/ring.h + + //////////////////////////////////////////////////////////////// + // + // Instantiated Circular buffer + // + + /* + Buffer type and function declaration, call: ZPL_RING_DECLARE(PREFIX, FUNC, VALUE) + Buffer function definitions, call: ZPL_RING_DEFINE(PREFIX, FUNC, VALUE) + + PREFIX - a prefix for function prototypes e.g. extern, static, etc. + FUNC - the name will prefix function names + VALUE - the type of the value to be stored + + funcname_init(VALUE * pad, zpl_allocator a, zpl_isize max_size) + funcname_free(VALUE * pad) + funcname_full(VALUE * pad) + funcname_empty(VALUE * pad) + funcname_append(VALUE * pad, type data) + funcname_append_array(VALUE * pad, zpl_array(type) data) + funcname_get(VALUE * pad) + funcname_get_array(VALUE * pad, zpl_usize max_size, zpl_allocator a) + */ + ZPL_BEGIN_C_DECLS + + #define ZPL_RING(PREFIX, FUNC, VALUE) \ + ZPL_RING_DECLARE(PREFIX, FUNC, VALUE); \ + ZPL_RING_DEFINE(FUNC, VALUE); + + #define ZPL_RING_DECLARE(prefix,func,type) \ + typedef struct { \ + zpl_allocator backing; \ + zpl_buffer(type) buf; \ + zpl_usize head, tail; \ + zpl_usize capacity; \ + } ZPL_JOIN2(func, type); \ + \ + prefix void ZPL_JOIN2(func, init)(ZPL_JOIN2(func, type) * pad, zpl_allocator a, zpl_isize max_size); \ + prefix void ZPL_JOIN2(func, free)(ZPL_JOIN2(func, type) * pad); \ + prefix zpl_b32 ZPL_JOIN2(func, full)(ZPL_JOIN2(func, type) * pad); \ + prefix zpl_b32 ZPL_JOIN2(func, empty)(ZPL_JOIN2(func, type) * pad); \ + prefix void ZPL_JOIN2(func, append)(ZPL_JOIN2(func, type) * pad, type data); \ + prefix void ZPL_JOIN2(func, append_array)(ZPL_JOIN2(func, type) * pad, zpl_array(type) data); \ + prefix type *ZPL_JOIN2(func, get)(ZPL_JOIN2(func, type) * pad); \ + prefix zpl_array(type) \ + ZPL_JOIN2(func, get_array)(ZPL_JOIN2(func, type) * pad, zpl_usize max_size, zpl_allocator a); + + #define ZPL_RING_DEFINE(func,type) \ + void ZPL_JOIN2(func, init)(ZPL_JOIN2(func, type) * pad, zpl_allocator a, zpl_isize max_size) { \ + ZPL_JOIN2(func, type) pad_ = { 0 }; \ + *pad = pad_; \ + \ + pad->backing = a; \ + zpl_buffer_init(pad->buf, a, max_size + 1); \ + pad->capacity = max_size + 1; \ + pad->head = pad->tail = 0; \ + } \ + void ZPL_JOIN2(func, free)(ZPL_JOIN2(func, type) * pad) { \ + zpl_buffer_free(pad->buf); \ + } \ + \ + zpl_b32 ZPL_JOIN2(func, full)(ZPL_JOIN2(func, type) * pad) { \ + return ((pad->head + 1) % pad->capacity) == pad->tail; \ + } \ + \ + zpl_b32 ZPL_JOIN2(func, empty)(ZPL_JOIN2(func, type) * pad) { return pad->head == pad->tail; } \ + \ + void ZPL_JOIN2(func, append)(ZPL_JOIN2(func, type) * pad, type data) { \ + pad->buf[pad->head] = data; \ + pad->head = (pad->head + 1) % pad->capacity; \ + \ + if (pad->head == pad->tail) { pad->tail = (pad->tail + 1) % pad->capacity; } \ + } \ + \ + void ZPL_JOIN2(func, append_array)(ZPL_JOIN2(func, type) * pad, zpl_array(type) data) { \ + zpl_usize c = zpl_array_count(data); \ + for (zpl_usize i = 0; i < c; ++i) { ZPL_JOIN2(func, append)(pad, data[i]); } \ + } \ + \ + type *ZPL_JOIN2(func, get)(ZPL_JOIN2(func, type) * pad) { \ + if (ZPL_JOIN2(func, empty)(pad)) { return NULL; } \ + \ + type *data = &pad->buf[pad->tail]; \ + pad->tail = (pad->tail + 1) % pad->capacity; \ + \ + return data; \ + } \ + \ + zpl_array(type) \ + ZPL_JOIN2(func, get_array)(ZPL_JOIN2(func, type) * pad, zpl_usize max_size, zpl_allocator a) { \ + zpl_array(type) vals = 0; \ + zpl_array_init(vals, a); \ + while (--max_size && !ZPL_JOIN2(func, empty)(pad)) { \ + zpl_array_append(vals, *ZPL_JOIN2(func, get)(pad)); \ + } \ + return vals; \ + } + + ZPL_END_C_DECLS + // file: header/essentials/collections/hashtable.h + + /** @file hashtable.c + @brief Instantiated hash table + @defgroup hashtable Instantiated hash table + + + This is an attempt to implement a templated hash table + NOTE: The key is always a zpl_u64 for simplicity and you will _probably_ _never_ need anything bigger. + + Hash table type and function declaration, call: ZPL_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE) + Hash table function definitions, call: ZPL_TABLE_DEFINE(NAME, FUNC, VALUE) + + PREFIX - a prefix for function prototypes e.g. extern, static, etc. + NAME - Name of the Hash Table + FUNC - the name will prefix function names + VALUE - the type of the value to be stored + + tablename_init(NAME * h, zpl_allocator a); + tablename_destroy(NAME * h); + tablename_get(NAME * h, zpl_u64 key); + tablename_set(NAME * h, zpl_u64 key, VALUE value); + tablename_grow(NAME * h); + tablename_map(NAME * h, void (*map_proc)(zpl_u64 key, VALUE value)) + tablename_map_mut(NAME * h, void (*map_proc)(zpl_u64 key, VALUE * value)) + tablename_rehash(NAME * h, zpl_isize new_count); + tablename_remove(NAME * h, zpl_u64 key); + + @{ + */ + + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_hash_table_find_result { + zpl_isize hash_index; + zpl_isize entry_prev; + zpl_isize entry_index; + } zpl_hash_table_find_result; + + /** + * Combined macro for a quick delcaration + definition + */ + + #define ZPL_TABLE(PREFIX, NAME, FUNC, VALUE) \ + ZPL_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE); \ + ZPL_TABLE_DEFINE(NAME, FUNC, VALUE); + + /** + * Table delcaration macro that generates the interface + */ + + #define ZPL_TABLE_DECLARE(PREFIX, NAME, FUNC, VALUE) \ + typedef struct ZPL_JOIN2(NAME, Entry) { \ + zpl_u64 key; \ + zpl_isize next; \ + VALUE value; \ + } ZPL_JOIN2(NAME, Entry); \ + \ + typedef struct NAME { \ + zpl_array(zpl_isize) hashes; \ + zpl_array(ZPL_JOIN2(NAME, Entry)) entries; \ + } NAME; \ + \ + PREFIX void ZPL_JOIN2(FUNC, init) (NAME *h, zpl_allocator a); \ + PREFIX void ZPL_JOIN2(FUNC, destroy) (NAME *h); \ + PREFIX void ZPL_JOIN2(FUNC, clear) (NAME *h); \ + PREFIX VALUE *ZPL_JOIN2(FUNC, get) (NAME *h, zpl_u64 key); \ + PREFIX zpl_isize ZPL_JOIN2(FUNC, slot) (NAME *h, zpl_u64 key); \ + PREFIX void ZPL_JOIN2(FUNC, set) (NAME *h, zpl_u64 key, VALUE value); \ + PREFIX void ZPL_JOIN2(FUNC, grow) (NAME *h); \ + PREFIX void ZPL_JOIN2(FUNC, rehash) (NAME *h, zpl_isize new_count); \ + PREFIX void ZPL_JOIN2(FUNC, rehash_fast) (NAME *h); \ + PREFIX void ZPL_JOIN2(FUNC, map) (NAME *h, void (*map_proc) (zpl_u64 key, VALUE value)); \ + PREFIX void ZPL_JOIN2(FUNC, map_mut) (NAME *h, void (*map_proc) (zpl_u64 key, VALUE * value)); \ + PREFIX void ZPL_JOIN2(FUNC, remove) (NAME *h, zpl_u64 key); \ + PREFIX void ZPL_JOIN2(FUNC, remove_entry) (NAME *h, zpl_isize idx); + + /** + * Table definition interfaces that generates the implementation + */ + + #define ZPL_TABLE_DEFINE(NAME, FUNC, VALUE) \ + void ZPL_JOIN2(FUNC, init)(NAME * h, zpl_allocator a) { \ + zpl_array_init(h->hashes, a); \ + zpl_array_init(h->entries, a); \ + } \ + \ + void ZPL_JOIN2(FUNC, destroy)(NAME * h) { \ + if (h->entries) zpl_array_free(h->entries); \ + if (h->hashes) zpl_array_free(h->hashes); \ + } \ + \ + void ZPL_JOIN2(FUNC, clear)(NAME * h) { \ + for (int i = 0; i < zpl_array_count(h->hashes); i++) h->hashes[i] = -1; \ + zpl_array_clear(h->entries); \ + } \ + \ + zpl_isize ZPL_JOIN2(FUNC, slot)(NAME * h, zpl_u64 key) { \ + for (zpl_isize i = 0; i < zpl_array_count(h->entries); i++) { \ + if (h->entries[i].key == key) { \ + return i; \ + } \ + } \ + return -1; \ + } \ + \ + zpl_internal zpl_isize ZPL_JOIN2(FUNC, _add_entry)(NAME * h, zpl_u64 key) { \ + zpl_isize index; \ + ZPL_JOIN2(NAME, Entry) e = { 0 }; \ + e.key = key; \ + e.next = -1; \ + index = zpl_array_count(h->entries); \ + zpl_array_append(h->entries, e); \ + return index; \ + } \ + \ + zpl_internal zpl_hash_table_find_result ZPL_JOIN2(FUNC, _find)(NAME * h, zpl_u64 key) { \ + zpl_hash_table_find_result r = { -1, -1, -1 }; \ + if (zpl_array_count(h->hashes) > 0) { \ + r.hash_index = key % zpl_array_count(h->hashes); \ + r.entry_index = h->hashes[r.hash_index]; \ + while (r.entry_index >= 0) { \ + if (h->entries[r.entry_index].key == key) return r; \ + r.entry_prev = r.entry_index; \ + r.entry_index = h->entries[r.entry_index].next; \ + } \ + } \ + return r; \ + } \ + \ + zpl_internal zpl_b32 ZPL_JOIN2(FUNC, _full)(NAME * h) { \ + return 0.75f * zpl_array_count(h->hashes) < zpl_array_count(h->entries); \ + } \ + \ + void ZPL_JOIN2(FUNC, grow)(NAME * h) { \ + zpl_isize new_count = ZPL_ARRAY_GROW_FORMULA(zpl_array_count(h->entries)); \ + ZPL_JOIN2(FUNC, rehash)(h, new_count); \ + } \ + \ + void ZPL_JOIN2(FUNC, rehash)(NAME * h, zpl_isize new_count) { \ + zpl_isize i, j; \ + NAME nh = { 0 }; \ + ZPL_JOIN2(FUNC, init)(&nh, zpl_array_allocator(h->hashes)); \ + zpl_array_resize(nh.hashes, new_count); \ + zpl_array_reserve(nh.entries, zpl_array_count(h->entries)); \ + for (i = 0; i < new_count; i++) nh.hashes[i] = -1; \ + for (i = 0; i < zpl_array_count(h->entries); i++) { \ + ZPL_JOIN2(NAME, Entry) * e; \ + zpl_hash_table_find_result fr; \ + if (zpl_array_count(nh.hashes) == 0) ZPL_JOIN2(FUNC, grow)(&nh); \ + e = &h->entries[i]; \ + fr = ZPL_JOIN2(FUNC, _find)(&nh, e->key); \ + j = ZPL_JOIN2(FUNC, _add_entry)(&nh, e->key); \ + if (fr.entry_prev < 0) \ + nh.hashes[fr.hash_index] = j; \ + else \ + nh.entries[fr.entry_prev].next = j; \ + nh.entries[j].next = fr.entry_index; \ + nh.entries[j].value = e->value; \ + } \ + ZPL_JOIN2(FUNC, destroy)(h); \ + h->hashes = nh.hashes; \ + h->entries = nh.entries; \ + } \ + \ + void ZPL_JOIN2(FUNC, rehash_fast)(NAME * h) { \ + zpl_isize i; \ + for (i = 0; i < zpl_array_count(h->entries); i++) h->entries[i].next = -1; \ + for (i = 0; i < zpl_array_count(h->hashes); i++) h->hashes[i] = -1; \ + for (i = 0; i < zpl_array_count(h->entries); i++) { \ + ZPL_JOIN2(NAME, Entry) * e; \ + zpl_hash_table_find_result fr; \ + e = &h->entries[i]; \ + fr = ZPL_JOIN2(FUNC, _find)(h, e->key); \ + if (fr.entry_prev < 0) \ + h->hashes[fr.hash_index] = i; \ + else \ + h->entries[fr.entry_prev].next = i; \ + } \ + } \ + \ + VALUE *ZPL_JOIN2(FUNC, get)(NAME * h, zpl_u64 key) { \ + zpl_isize index = ZPL_JOIN2(FUNC, _find)(h, key).entry_index; \ + if (index >= 0) return &h->entries[index].value; \ + return NULL; \ + } \ + \ + void ZPL_JOIN2(FUNC, remove)(NAME * h, zpl_u64 key) { \ + zpl_hash_table_find_result fr = ZPL_JOIN2(FUNC, _find)(h, key); \ + if (fr.entry_index >= 0) { \ + zpl_array_remove_at(h->entries, fr.entry_index); \ + ZPL_JOIN2(FUNC, rehash_fast)(h); \ + } \ + } \ + \ + void ZPL_JOIN2(FUNC, remove_entry)(NAME * h, zpl_isize idx) { \ + zpl_array_remove_at(h->entries, idx); \ + } \ + \ + void ZPL_JOIN2(FUNC, map)(NAME * h, void (*map_proc)(zpl_u64 key, VALUE value)) { \ + ZPL_ASSERT_NOT_NULL(h); \ + ZPL_ASSERT_NOT_NULL(map_proc); \ + for (zpl_isize i = 0; i < zpl_array_count(h->entries); ++i) { \ + map_proc(h->entries[i].key, h->entries[i].value); \ + } \ + } \ + \ + void ZPL_JOIN2(FUNC, map_mut)(NAME * h, void (*map_proc)(zpl_u64 key, VALUE * value)) { \ + ZPL_ASSERT_NOT_NULL(h); \ + ZPL_ASSERT_NOT_NULL(map_proc); \ + for (zpl_isize i = 0; i < zpl_array_count(h->entries); ++i) { \ + map_proc(h->entries[i].key, &h->entries[i].value); \ + } \ + } \ + \ + void ZPL_JOIN2(FUNC, set)(NAME * h, zpl_u64 key, VALUE value) { \ + zpl_isize index; \ + zpl_hash_table_find_result fr; \ + if (zpl_array_count(h->hashes) == 0) ZPL_JOIN2(FUNC, grow)(h); \ + fr = ZPL_JOIN2(FUNC, _find)(h, key); \ + if (fr.entry_index >= 0) { \ + index = fr.entry_index; \ + } else { \ + index = ZPL_JOIN2(FUNC, _add_entry)(h, key); \ + if (fr.entry_prev >= 0) { \ + h->entries[fr.entry_prev].next = index; \ + } else { \ + h->hashes[fr.hash_index] = index; \ + } \ + } \ + h->entries[index].value = value; \ + if (ZPL_JOIN2(FUNC, _full)(h)) ZPL_JOIN2(FUNC, grow)(h); \ + } + + //! @} + + ZPL_END_C_DECLS +# if defined(ZPL_MODULE_CORE) + // file: header/core/memory_virtual.h + + + //////////////////////////////////////////////////////////////// + // + // Virtual Memory + // + // + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_virtual_memory { + void *data; + zpl_isize size; + } zpl_virtual_memory; + + //! Initialize virtual memory from existing data. + ZPL_DEF zpl_virtual_memory zpl_vm(void *data, zpl_isize size); + + //! Allocate virtual memory at address with size. + + //! @param addr The starting address of the region to reserve. If NULL, it lets operating system to decide where to allocate it. + //! @param size The size to serve. + ZPL_DEF zpl_virtual_memory zpl_vm_alloc(void *addr, zpl_isize size); + + //! Release the virtual memory. + ZPL_DEF zpl_b32 zpl_vm_free(zpl_virtual_memory vm); + + //! Trim virtual memory. + ZPL_DEF zpl_virtual_memory zpl_vm_trim(zpl_virtual_memory vm, zpl_isize lead_size, zpl_isize size); + + //! Purge virtual memory. + ZPL_DEF zpl_b32 zpl_vm_purge(zpl_virtual_memory vm); + + //! Retrieve VM's page size and alignment. + ZPL_DEF zpl_isize zpl_virtual_memory_page_size(zpl_isize *alignment_out); + + ZPL_END_C_DECLS + // file: header/core/string.h + + /** @file string.c + @brief String operations and library + @defgroup string String library + + Offers methods for c-string manipulation, but also a string library based on gb_string, which is c-string friendly. + + @{ + */ + + //////////////////////////////////////////////////////////////// + // + // Char Functions + // + // + + + ZPL_BEGIN_C_DECLS + + ZPL_DEF_INLINE char zpl_char_to_lower(char c); + ZPL_DEF_INLINE char zpl_char_to_upper(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_space(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_digit(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_hex_digit(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_alpha(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_alphanumeric(char c); + ZPL_DEF_INLINE zpl_i32 zpl_digit_to_int(char c); + ZPL_DEF_INLINE zpl_i32 zpl_hex_digit_to_int(char c); + ZPL_DEF_INLINE zpl_u8 zpl_char_to_hex_digit(char c); + ZPL_DEF_INLINE zpl_b32 zpl_char_is_control(char c); + + // NOTE: ASCII only + ZPL_DEF_INLINE void zpl_str_to_lower(char *str); + ZPL_DEF_INLINE void zpl_str_to_upper(char *str); + + ZPL_DEF_INLINE char const *zpl_str_trim(char const *str, zpl_b32 catch_newline); + ZPL_DEF_INLINE char const *zpl_str_skip(char const *str, char c); + ZPL_DEF_INLINE char const *zpl_str_skip_any(char const *str, char const*char_list); + ZPL_DEF_INLINE char const *zpl_str_skip_literal(char const *str, char c); + ZPL_DEF_INLINE char const *zpl_str_control_skip(char const *str, char c); + + ZPL_DEF_INLINE zpl_isize zpl_strlen(const char *str); + ZPL_DEF_INLINE zpl_isize zpl_strnlen(const char *str, zpl_isize max_len); + ZPL_DEF_INLINE zpl_i32 zpl_strcmp(const char *s1, const char *s2); + ZPL_DEF_INLINE zpl_i32 zpl_strncmp(const char *s1, const char *s2, zpl_isize len); + ZPL_DEF_INLINE char *zpl_strcpy(char *dest, const char *source); + ZPL_DEF_INLINE char *zpl_strcat(char *dest, const char *source); + ZPL_DEF_INLINE char *zpl_strncpy(char *dest, const char *source, zpl_isize len); + ZPL_DEF_INLINE zpl_isize zpl_strlcpy(char *dest, const char *source, zpl_isize len); + ZPL_DEF_INLINE char *zpl_strrev(char *str); // NOTE: ASCII only + ZPL_DEF_INLINE const char *zpl_strtok(char *output, const char *src, const char *delimit); + ZPL_DEF_INLINE const char *zpl_strntok(char *output, zpl_isize len, const char *src, const char *delimit); + + ZPL_DEF_INLINE char *zpl_strdup(zpl_allocator a, char *src, zpl_isize max_len); + ZPL_DEF_INLINE char **zpl_str_split_lines(zpl_allocator alloc, char *source, zpl_b32 strip_whitespace); + + #define zpl_str_expand(str) str, zpl_strlen(str) + #define zpl_str_advance_while(str, cond) \ + do { \ + ++str; \ + } while ((cond)); + + ZPL_DEF_INLINE zpl_b32 zpl_str_has_prefix(const char *str, const char *prefix); + ZPL_DEF_INLINE zpl_b32 zpl_str_has_suffix(const char *str, const char *suffix); + + ZPL_DEF_INLINE const char *zpl_char_first_occurence(const char *str, char c); + ZPL_DEF_INLINE const char *zpl_char_last_occurence(const char *str, char c); + #define zpl_strchr zpl_char_first_occurence + + ZPL_DEF_INLINE void zpl_str_concat(char *dest, zpl_isize dest_len, const char *src_a, zpl_isize src_a_len, const char *src_b, zpl_isize src_b_len); + + ZPL_DEF zpl_u64 zpl_str_to_u64(const char *str, char **end_ptr, zpl_i32 base); + ZPL_DEF zpl_i64 zpl_str_to_i64(const char *str, char **end_ptr, zpl_i32 base); + ZPL_DEF zpl_f64 zpl_str_to_f64(const char *str, char **end_ptr); + ZPL_DEF void zpl_i64_to_str(zpl_i64 value, char *string, zpl_i32 base); + ZPL_DEF void zpl_u64_to_str(zpl_u64 value, char *string, zpl_i32 base); + + ZPL_DEF_INLINE zpl_f32 zpl_str_to_f32(const char *str, char **end_ptr); + + //////////////////////////////////////////////////////////////// + // + // UTF-8 Handling + // + // + + // NOTE: Does not check if utf-8 string is valid + ZPL_IMPL_INLINE zpl_isize zpl_utf8_strlen(zpl_u8 const *str); + ZPL_IMPL_INLINE zpl_isize zpl_utf8_strnlen(zpl_u8 const *str, zpl_isize max_len); + + // NOTE: Windows doesn't handle 8 bit filenames well + ZPL_DEF zpl_u16 *zpl_utf8_to_ucs2(zpl_u16 *buffer, zpl_isize len, zpl_u8 const *str); + ZPL_DEF zpl_u8 *zpl_ucs2_to_utf8(zpl_u8 *buffer, zpl_isize len, zpl_u16 const *str); + ZPL_DEF zpl_u16 *zpl_utf8_to_ucs2_buf(zpl_u8 const *str); // NOTE: Uses locally persisting buffer + ZPL_DEF zpl_u8 *zpl_ucs2_to_utf8_buf(zpl_u16 const *str); // NOTE: Uses locally persisting buffer + + // NOTE: Returns size of codepoint in bytes + ZPL_DEF zpl_isize zpl_utf8_decode(zpl_u8 const *str, zpl_isize str_len, zpl_rune *codepoint); + ZPL_DEF zpl_isize zpl_utf8_codepoint_size(zpl_u8 const *str, zpl_isize str_len); + ZPL_DEF zpl_isize zpl_utf8_encode_rune(zpl_u8 buf[4], zpl_rune r); + + /* inlines */ + + ZPL_IMPL_INLINE char zpl_char_to_lower(char c) { + if (c >= 'A' && c <= 'Z') return 'a' + (c - 'A'); + return c; + } + + ZPL_IMPL_INLINE char zpl_char_to_upper(char c) { + if (c >= 'a' && c <= 'z') return 'A' + (c - 'a'); + return c; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_space(char c) { + if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f' || c == '\v') return true; + return false; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_digit(char c) { + if (c >= '0' && c <= '9') return true; + return false; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_hex_digit(char c) { + if (zpl_char_is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')) return true; + return false; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_alpha(char c) { + if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) return true; + return false; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_alphanumeric(char c) { return zpl_char_is_alpha(c) || zpl_char_is_digit(c); } + + ZPL_IMPL_INLINE zpl_i32 zpl_digit_to_int(char c) { return zpl_char_is_digit(c) ? c - '0' : c - 'W'; } + + ZPL_IMPL_INLINE zpl_i32 zpl_hex_digit_to_int(char c) { + if (zpl_char_is_digit(c)) + return zpl_digit_to_int(c); + else if (zpl_is_between(c, 'a', 'f')) + return c - 'a' + 10; + else if (zpl_is_between(c, 'A', 'F')) + return c - 'A' + 10; + return -1; + } + + ZPL_IMPL_INLINE zpl_u8 zpl_char_to_hex_digit(char c) { + if (c >= '0' && c <= '9') + return (zpl_u8)(c - '0'); + if (c >= 'a' && c <= 'f') + return (zpl_u8)(c - 'a'); + if (c >= 'A' && c <= 'F') + return (zpl_u8)(c - 'A'); + return 0; + } + + ZPL_IMPL_INLINE void zpl_str_to_lower(char *str) { + if (!str) return; + while (*str) { + *str = zpl_char_to_lower(*str); + str++; + } + } + + ZPL_IMPL_INLINE void zpl_str_to_upper(char *str) { + if (!str) return; + while (*str) { + *str = zpl_char_to_upper(*str); + str++; + } + } + + ZPL_IMPL_INLINE zpl_isize zpl_strlen(const char *str) { + if (str == NULL) { return 0; } + const char *p = str; + while (*str) str++; + return str-p; + } + + ZPL_IMPL_INLINE zpl_isize zpl_strnlen(const char *str, zpl_isize max_len) { + const char *end = cast(const char *) zpl_memchr(str, 0, max_len); + if (end) return end - str; + return max_len; + } + + ZPL_IMPL_INLINE zpl_isize zpl_utf8_strlen(zpl_u8 const *str) { + zpl_isize count = 0; + for (; *str; count++) { + zpl_u8 c = *str; + zpl_isize inc = 0; + if (c < 0x80) + inc = 1; + else if ((c & 0xe0) == 0xc0) + inc = 2; + else if ((c & 0xf0) == 0xe0) + inc = 3; + else if ((c & 0xf8) == 0xf0) + inc = 4; + else + return -1; + + str += inc; + } + return count; + } + + ZPL_IMPL_INLINE zpl_isize zpl_utf8_strnlen(zpl_u8 const *str, zpl_isize max_len) { + zpl_isize count = 0; + for (; *str && max_len > 0; count++) { + zpl_u8 c = *str; + zpl_isize inc = 0; + if (c < 0x80) + inc = 1; + else if ((c & 0xe0) == 0xc0) + inc = 2; + else if ((c & 0xf0) == 0xe0) + inc = 3; + else if ((c & 0xf8) == 0xf0) + inc = 4; + else + return -1; + + str += inc; + max_len -= inc; + } + return count; + } + + ZPL_IMPL_INLINE zpl_i32 zpl_strcmp(const char *s1, const char *s2) { + while (*s1 && (*s1 == *s2)) { s1++, s2++; } + return *(zpl_u8 *)s1 - *(zpl_u8 *)s2; + } + + ZPL_IMPL_INLINE char *zpl_strcpy(char *dest, const char *source) { + ZPL_ASSERT_NOT_NULL(dest); + if (source) { + char *str = dest; + while (*source) *str++ = *source++; + } + return dest; + } + + ZPL_IMPL_INLINE char *zpl_strcat(char *dest, const char *source) { + ZPL_ASSERT_NOT_NULL(dest); + if (source) { + char *str = dest; + while (*str) ++str; + while (*source) *str++ = *source++; + } + return dest; + } + + ZPL_IMPL_INLINE char *zpl_strncpy(char *dest, const char *source, zpl_isize len) { + ZPL_ASSERT_NOT_NULL(dest); + if (source) { + char *str = dest; + while (len > 0 && *source) { + *str++ = *source++; + len--; + } + while (len > 0) { + *str++ = '\0'; + len--; + } + } + return dest; + } + + ZPL_IMPL_INLINE zpl_isize zpl_strlcpy(char *dest, const char *source, zpl_isize len) { + zpl_isize result = 0; + ZPL_ASSERT_NOT_NULL(dest); + if (source) { + const char *source_start = source; + char *str = dest; + while (len > 0 && *source) { + *str++ = *source++; + len--; + } + while (len > 0) { + *str++ = '\0'; + len--; + } + + result = source - source_start; + } + return result; + } + + ZPL_IMPL_INLINE char *zpl_strrev(char *str) { + zpl_isize len = zpl_strlen(str); + char *a = str + 0; + char *b = str + len - 1; + len /= 2; + while (len--) { + zpl_swap(char, *a, *b); + a++, b--; + } + return str; + } + + ZPL_IMPL_INLINE zpl_i32 zpl_strncmp(const char *s1, const char *s2, zpl_isize len) { + for (; len > 0; s1++, s2++, len--) { + if (*s1 != *s2) + return ((s1 < s2) ? -1 : +1); + else if (*s1 == '\0') + return 0; + } + return 0; + } + + ZPL_IMPL_INLINE const char *zpl_strtok(char *output, const char *src, const char *delimit) { + while (*src && zpl_char_first_occurence(delimit, *src) == NULL) *output++ = *src++; + + *output = 0; + return *src ? src + 1 : src; + } + + ZPL_IMPL_INLINE const char *zpl_strntok(char *output, zpl_isize len, const char *src, const char *delimit) { + ZPL_ASSERT(len > 0); + *(output+len-1) = 0; + while (*src && zpl_char_first_occurence(delimit, *src) == NULL && len > 0) { + *output++ = *src++; + len --; + } + + if (len > 0) + *output = 0; + return *src ? src + 1 : src; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_char_is_control(char c) { + return !!zpl_strchr("\"\\/bfnrt", c); + } + + ZPL_IMPL_INLINE zpl_b32 zpl__is_special_char(char c) { return !!zpl_strchr("<>:/", c); } + ZPL_IMPL_INLINE zpl_b32 zpl__is_assign_char(char c) { return !!zpl_strchr(":=|", c); } + ZPL_IMPL_INLINE zpl_b32 zpl__is_delim_char(char c) { return !!zpl_strchr(",|\n", c); } + + + ZPL_IMPL_INLINE char const *zpl_str_control_skip(char const *str, char c) { + while ((*str && *str != c) || (*(str - 1) == '\\' && *str == c && zpl_char_is_control(c))) { ++str; } + + return str; + } + + + ZPL_IMPL_INLINE zpl_b32 zpl_str_has_prefix(const char *str, const char *prefix) { + while (*prefix) { + if (*str++ != *prefix++) return false; + } + return true; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_str_has_suffix(const char *str, const char *suffix) { + zpl_isize i = zpl_strlen(str); + zpl_isize j = zpl_strlen(suffix); + if (j <= i) return zpl_strcmp(str + i - j, suffix) == 0; + return false; + } + + ZPL_IMPL_INLINE const char *zpl_char_first_occurence(const char *s, char c) { + char ch = c; + for (; *s != ch; s++) { + if (*s == '\0') return NULL; + } + return s; + } + + ZPL_IMPL_INLINE const char *zpl_char_last_occurence(const char *s, char c) { + char *result = (char*)NULL; + do { + if (*s == c) result = (char *)s; + } while (*s++); + + return result; + } + + ZPL_IMPL_INLINE char const *zpl_str_trim(char const *str, zpl_b32 catch_newline) + { + while (*str && zpl_char_is_space(*str) && (!catch_newline || (catch_newline && *str != '\n'))) { ++str; } + return str; + } + + ZPL_IMPL_INLINE char const *zpl_str_skip(char const *str, char c) { + while (*str && *str != c) { ++str; } + return str; + } + + ZPL_IMPL_INLINE char const *zpl_str_skip_any(char const *str, char const*char_list) { + char const *closest_ptr = cast(char const *) zpl_ptr_add((void*)str, zpl_strlen(str)); + zpl_isize char_list_count = zpl_strlen(char_list); + for (zpl_isize i = 0; i < char_list_count; i++) { + char const *p = zpl_str_skip(str, char_list[i]); + closest_ptr = zpl_min(closest_ptr, p); + } + return closest_ptr; + } + + ZPL_IMPL_INLINE char const *zpl_str_skip_literal(char const *str, char c) { + if (*str == '\0' || *str == c) + return str; + str++; + + while ((*str && *str != c) || (*str == c && *(str-1) == '\\')) { ++str; } + return str; + } + + ZPL_IMPL_INLINE void zpl_str_concat(char *dest, zpl_isize dest_len, const char *src_a, zpl_isize src_a_len, const char *src_b, + zpl_isize src_b_len) { + ZPL_ASSERT(dest_len >= src_a_len + src_b_len + 1); + if (dest) { + zpl_memcopy(dest, src_a, src_a_len); + zpl_memcopy(dest + src_a_len, src_b, src_b_len); + dest[src_a_len + src_b_len] = '\0'; + } + } + + ZPL_IMPL_INLINE zpl_f32 zpl_str_to_f32(const char *str, char **end_ptr) { + zpl_f64 f = zpl_str_to_f64(str, end_ptr); + zpl_f32 r = cast(zpl_f32) f; + return r; + } + + ZPL_IMPL_INLINE char *zpl_strdup(zpl_allocator a, char *src, zpl_isize max_len) { + ZPL_ASSERT_NOT_NULL(src); + zpl_isize len = zpl_strlen(src); + char *dest = cast(char *) zpl_alloc(a, max_len); + zpl_memset(dest + len, 0, max_len - len); + zpl_strncpy(dest, src, max_len); + + return dest; + } + + ZPL_IMPL_INLINE char **zpl_str_split_lines(zpl_allocator alloc, char *source, zpl_b32 strip_whitespace) { + char **lines = NULL, *p = source, *pd = p; + zpl_array_init(lines, alloc); + + while (*p) { + if (*pd == '\n') { + *pd = 0; + if (*(pd - 1) == '\r') *(pd - 1) = 0; + if (strip_whitespace && (pd - p) == 0) { + p = pd + 1; + continue; + } + zpl_array_append(lines, p); + p = pd + 1; + } + ++pd; + } + return lines; + } + + ZPL_END_C_DECLS + // file: header/core/stringlib.h + + + ZPL_BEGIN_C_DECLS + + typedef char *zpl_string; + + typedef struct zpl_string_header { + zpl_allocator allocator; + zpl_isize length; + zpl_isize capacity; + } zpl_string_header; + + #define ZPL_STRING_HEADER(str) (cast(zpl_string_header *)(str) - 1) + + ZPL_DEF zpl_string zpl_string_make_reserve(zpl_allocator a, zpl_isize capacity); + ZPL_DEF zpl_string zpl_string_make_length(zpl_allocator a, void const *str, zpl_isize num_bytes); + ZPL_DEF zpl_string zpl_string_sprintf(zpl_allocator a, char *buf, zpl_isize num_bytes, const char *fmt, ...); + ZPL_DEF zpl_string zpl_string_sprintf_buf(zpl_allocator a, const char *fmt, ...); // NOTE: Uses locally persistent buffer + ZPL_DEF zpl_string zpl_string_append_length(zpl_string str, void const *other, zpl_isize num_bytes); + ZPL_DEF zpl_string zpl_string_appendc(zpl_string str, const char *other); + ZPL_DEF zpl_string zpl_string_join(zpl_allocator a, const char **parts, zpl_isize count, const char *glue); + ZPL_DEF zpl_string zpl_string_set(zpl_string str, const char *cstr); + ZPL_DEF zpl_string zpl_string_make_space_for(zpl_string str, zpl_isize add_len); + ZPL_DEF zpl_isize zpl_string_allocation_size(zpl_string const str); + ZPL_DEF zpl_b32 zpl_string_are_equal(zpl_string const lhs, zpl_string const rhs); + ZPL_DEF zpl_string zpl_string_trim(zpl_string str, const char *cut_set); + ZPL_DEF zpl_string zpl_string_append_rune(zpl_string str, zpl_rune r); + ZPL_DEF zpl_string zpl_string_append_fmt(zpl_string str, const char *fmt, ...); + + ZPL_DEF_INLINE zpl_string zpl_string_make(zpl_allocator a, const char *str); + ZPL_DEF_INLINE void zpl_string_free(zpl_string str); + ZPL_DEF_INLINE void zpl_string_clear(zpl_string str); + ZPL_DEF_INLINE zpl_string zpl_string_duplicate(zpl_allocator a, zpl_string const str); + ZPL_DEF_INLINE zpl_isize zpl_string_length(zpl_string const str); + ZPL_DEF_INLINE zpl_isize zpl_string_capacity(zpl_string const str); + ZPL_DEF_INLINE zpl_isize zpl_string_available_space(zpl_string const str); + ZPL_DEF_INLINE zpl_string zpl_string_append(zpl_string str, zpl_string const other); + ZPL_DEF_INLINE zpl_string zpl_string_trim_space(zpl_string str); // Whitespace ` \t\r\n\v\f` + ZPL_DEF_INLINE void zpl__set_string_length(zpl_string str, zpl_isize len); + ZPL_DEF_INLINE void zpl__set_string_capacity(zpl_string str, zpl_isize cap); + + ZPL_IMPL_INLINE void zpl__set_string_length(zpl_string str, zpl_isize len) { ZPL_STRING_HEADER(str)->length = len; } + ZPL_IMPL_INLINE void zpl__set_string_capacity(zpl_string str, zpl_isize cap) { ZPL_STRING_HEADER(str)->capacity = cap; } + ZPL_IMPL_INLINE zpl_string zpl_string_make(zpl_allocator a, const char *str) { + zpl_isize len = str ? zpl_strlen(str) : 0; + return zpl_string_make_length(a, str, len); + } + + ZPL_IMPL_INLINE void zpl_string_free(zpl_string str) { + if (str) { + zpl_string_header *header = ZPL_STRING_HEADER(str); + zpl_free(header->allocator, header); + } + } + + ZPL_IMPL_INLINE zpl_string zpl_string_duplicate(zpl_allocator a, zpl_string const str) { + return zpl_string_make_length(a, str, zpl_string_length(str)); + } + + ZPL_IMPL_INLINE zpl_isize zpl_string_length(zpl_string const str) { return ZPL_STRING_HEADER(str)->length; } + ZPL_IMPL_INLINE zpl_isize zpl_string_capacity(zpl_string const str) { return ZPL_STRING_HEADER(str)->capacity; } + + ZPL_IMPL_INLINE zpl_isize zpl_string_available_space(zpl_string const str) { + zpl_string_header *h = ZPL_STRING_HEADER(str); + if (h->capacity > h->length) return h->capacity - h->length; + return 0; + } + + ZPL_IMPL_INLINE void zpl_string_clear(zpl_string str) { + zpl__set_string_length(str, 0); + str[0] = '\0'; + } + + ZPL_IMPL_INLINE zpl_string zpl_string_append(zpl_string str, zpl_string const other) { + return zpl_string_append_length(str, other, zpl_string_length(other)); + } + + ZPL_IMPL_INLINE zpl_string zpl_string_trim_space(zpl_string str) { return zpl_string_trim(str, " \t\r\n\v\f"); } + + + ZPL_END_C_DECLS + // file: header/core/file.h + + /** @file file.c + @brief File handling + @defgroup fileio File handling + + File I/O operations as well as path and folder structure manipulation methods. With threading enabled, it also offers async read/write methods. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef zpl_u32 zpl_file_mode; + + typedef enum zpl_file_mode_flag { + ZPL_FILE_MODE_READ = ZPL_BIT(0), + ZPL_FILE_MODE_WRITE = ZPL_BIT(1), + ZPL_FILE_MODE_APPEND = ZPL_BIT(2), + ZPL_FILE_MODE_RW = ZPL_BIT(3), + ZPL_FILE_MODES = ZPL_FILE_MODE_READ | ZPL_FILE_MODE_WRITE | ZPL_FILE_MODE_APPEND | ZPL_FILE_MODE_RW, + } zpl_file_mode_flag; + + // NOTE: Only used internally and for the file operations + typedef enum zpl_seek_whence_type { + ZPL_SEEK_WHENCE_BEGIN = 0, + ZPL_SEEK_WHENCE_CURRENT = 1, + ZPL_SEEK_WHENCE_END = 2, + } zpl_seek_whence_type; + + typedef enum zpl_file_error { + ZPL_FILE_ERROR_NONE, + ZPL_FILE_ERROR_INVALID, + ZPL_FILE_ERROR_INVALID_FILENAME, + ZPL_FILE_ERROR_EXISTS, + ZPL_FILE_ERROR_NOT_EXISTS, + ZPL_FILE_ERROR_PERMISSION, + ZPL_FILE_ERROR_TRUNCATION_FAILURE, + ZPL_FILE_ERROR_NOT_EMPTY, + ZPL_FILE_ERROR_NAME_TOO_LONG, + ZPL_FILE_ERROR_UNKNOWN, + } zpl_file_error; + + typedef union zpl_file_descriptor { + void *p; + zpl_intptr i; + zpl_uintptr u; + } zpl_file_descriptor; + + typedef struct zpl_file_operations zpl_file_operations; + + #define ZPL_FILE_OPEN_PROC(name) zpl_file_error name(zpl_file_descriptor *fd, zpl_file_operations *ops, zpl_file_mode mode, char const *filename) + #define ZPL_FILE_READ_AT_PROC(name) zpl_b32 name(zpl_file_descriptor fd, void *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_read, zpl_b32 stop_at_newline) + #define ZPL_FILE_WRITE_AT_PROC(name) zpl_b32 name(zpl_file_descriptor fd, void const *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_written) + #define ZPL_FILE_SEEK_PROC(name) zpl_b32 name(zpl_file_descriptor fd, zpl_i64 offset, zpl_seek_whence_type whence, zpl_i64 *new_offset) + #define ZPL_FILE_CLOSE_PROC(name) void name(zpl_file_descriptor fd) + + typedef ZPL_FILE_OPEN_PROC(zpl_file_open_proc); + typedef ZPL_FILE_READ_AT_PROC(zpl_file_read_proc); + typedef ZPL_FILE_WRITE_AT_PROC(zpl_file_write_proc); + typedef ZPL_FILE_SEEK_PROC(zpl_file_seek_proc); + typedef ZPL_FILE_CLOSE_PROC(zpl_file_close_proc); + + struct zpl_file_operations { + zpl_file_read_proc *read_at; + zpl_file_write_proc *write_at; + zpl_file_seek_proc *seek; + zpl_file_close_proc *close; + }; + + extern zpl_file_operations const zpl_default_file_operations; + + typedef zpl_u64 zpl_file_time; + typedef enum zpl_dir_type { + ZPL_DIR_TYPE_FILE, + ZPL_DIR_TYPE_FOLDER, + ZPL_DIR_TYPE_UNKNOWN, + } zpl_dir_type; + + struct zpl_dir_info; + + typedef struct zpl_dir_entry { + char const *filename; + struct zpl_dir_info *dir_info; + zpl_u8 type; + } zpl_dir_entry; + + typedef struct zpl_dir_info { + char const *fullpath; + zpl_dir_entry *entries; // zpl_array + + // Internals + char **filenames; // zpl_array + zpl_string buf; + } zpl_dir_info; + + typedef struct zpl_file { + zpl_file_operations ops; + zpl_file_descriptor fd; + zpl_b32 is_temp; + + char const *filename; + zpl_file_time last_write_time; + zpl_dir_entry *dir; + } zpl_file; + + typedef enum zpl_file_standard_type { + ZPL_FILE_STANDARD_INPUT, + ZPL_FILE_STANDARD_OUTPUT, + ZPL_FILE_STANDARD_ERROR, + + ZPL_FILE_STANDARD_COUNT, + } zpl_file_standard_type; + + #define ZPL_STDIO_IN zpl_file_get_standard(ZPL_FILE_STANDARD_INPUT) + #define ZPL_STDIO_OUT zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT) + #define ZPL_STDIO_ERR zpl_file_get_standard(ZPL_FILE_STANDARD_ERROR) + + /** + * Get standard file I/O. + * @param std Check zpl_file_standard_type + * @return File handle to standard I/O + */ + ZPL_DEF zpl_file *zpl_file_get_standard(zpl_file_standard_type std); + + /** + * Connects a system handle to a zpl file. + * @param file Pointer to zpl file + * @param handle Low-level OS handle to connect + */ + ZPL_DEF void zpl_file_connect_handle(zpl_file *file, void *handle); + + /** + * Creates a new file + * @param file + * @param filename + */ + ZPL_DEF zpl_file_error zpl_file_create(zpl_file *file, char const *filename); + + /** + * Opens a file + * @param file + * @param filename + */ + ZPL_DEF zpl_file_error zpl_file_open(zpl_file *file, char const *filename); + + /** + * Opens a file using a specified mode + * @param file + * @param mode Access mode to use + * @param filename + */ + ZPL_DEF zpl_file_error zpl_file_open_mode(zpl_file *file, zpl_file_mode mode, char const *filename); + + /** + * Constructs a new file from data + * @param file + * @param fd Low-level file descriptor to use + * @param ops File operations to rely upon + * @param filename + */ + ZPL_DEF zpl_file_error zpl_file_new(zpl_file *file, zpl_file_descriptor fd, zpl_file_operations ops, char const *filename); + + /** + * Returns a size of the file + * @param file + * @return File size + */ + ZPL_DEF zpl_i64 zpl_file_size(zpl_file *file); + + /** + * Returns the currently opened file's name + * @param file + */ + ZPL_DEF char const *zpl_file_name(zpl_file *file); + + /** + * Truncates the file by a specified size + * @param file + * @param size Size to truncate + */ + ZPL_DEF zpl_file_error zpl_file_truncate(zpl_file *file, zpl_i64 size); + + /** + * Checks whether a file's been changed since the last check + * @param file + */ + ZPL_DEF zpl_b32 zpl_file_has_changed(zpl_file *file); + + /** + * Retrieves a directory listing relative to the file + * @param file + */ + ZPL_DEF void zpl_file_dirinfo_refresh(zpl_file *file); + + /** + * Creates a temporary file + * @param file + */ + ZPL_DEF zpl_file_error zpl_file_temp(zpl_file *file); + + /** + * Closes the file + * @param file + */ + ZPL_DEF zpl_file_error zpl_file_close(zpl_file *file); + + /** + * Reads file safely + * @param file + * @param buffer Buffer to read to + * @param size Size to read + * @param offset Offset to read from + * @param bytes_read How much data we've actually read + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_read_at_check(zpl_file *file, void *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_read); + + /** + * Writes to file safely + * @param file + * @param buffer Buffer to read from + * @param size Size to write + * @param offset Offset to write to + * @param bytes_written How much data we've actually written + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_write_at_check(zpl_file *file, void const *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_written); + + + /** + * Reads file at a specific offset + * @param file + * @param buffer Buffer to read to + * @param size Size to read + * @param offset Offset to read from + * @param bytes_read How much data we've actually read + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_read_at(zpl_file *file, void *buffer, zpl_isize size, zpl_i64 offset); + + /** + * Writes to file at a specific offset + * @param file + * @param buffer Buffer to read from + * @param size Size to write + * @param offset Offset to write to + * @param bytes_written How much data we've actually written + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_write_at(zpl_file *file, void const *buffer, zpl_isize size, zpl_i64 offset); + + /** + * Seeks the file cursor from the beginning of file to a specific position + * @param file + * @param offset Offset to seek to + */ + ZPL_DEF_INLINE zpl_i64 zpl_file_seek(zpl_file *file, zpl_i64 offset); + + /** + * Seeks the file cursor to the end of the file + * @param file + */ + ZPL_DEF_INLINE zpl_i64 zpl_file_seek_to_end(zpl_file *file); + + /** + * Skips N bytes at the current position + * @param file + * @param bytes Bytes to skip + */ + ZPL_DEF_INLINE zpl_i64 zpl_file_skip(zpl_file *file, zpl_i64 bytes); // NOTE: Skips a certain amount of bytes + + /** + * Returns the length from the beginning of the file we've read so far + * @param file + * @return Our current position in file + */ + ZPL_DEF_INLINE zpl_i64 zpl_file_tell(zpl_file *file); + + /** + * Reads from a file + * @param file + * @param buffer Buffer to read to + * @param size Size to read + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_read(zpl_file *file, void *buffer, zpl_isize size); + + /** + * Writes to a file + * @param file + * @param buffer Buffer to read from + * @param size Size to read + */ + ZPL_DEF_INLINE zpl_b32 zpl_file_write(zpl_file *file, void const *buffer, zpl_isize size); + + + typedef struct zpl_file_contents { + zpl_allocator allocator; + void *data; + zpl_isize size; + } zpl_file_contents; + + /** + * Reads the whole file contents + * @param a Allocator to use + * @param zero_terminate End the read data with null terminator + * @param filepath Path to the file + * @return File contents data + */ + ZPL_DEF zpl_file_contents zpl_file_read_contents(zpl_allocator a, zpl_b32 zero_terminate, char const *filepath); + + /** + * Frees the file content data previously read + * @param fc + */ + ZPL_DEF void zpl_file_free_contents(zpl_file_contents *fc); + + /** + * Writes content to a file + */ + ZPL_DEF zpl_b32 zpl_file_write_contents(char const* filepath, void const* buffer, zpl_isize size, zpl_file_error* err); + + /** + * Reads the file as array of lines + * + * Make sure you free both the returned buffer and the lines (zpl_array) + * @param alloc Allocator to use + * @param lines Reference to zpl_array container we store lines to + * @param filename Path to the file + * @param strip_whitespace Strip whitespace when we split to lines? + * @return File content we've read itself + */ + ZPL_DEF char *zpl_file_read_lines(zpl_allocator alloc, zpl_array(char *)*lines, char const *filename, zpl_b32 strip_whitespace); + + //! @} + + /* inlines */ + + + ZPL_IMPL_INLINE zpl_b32 zpl_file_read_at_check(zpl_file *f, void *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_read) { + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + return f->ops.read_at(f->fd, buffer, size, offset, bytes_read, false); + } + + ZPL_IMPL_INLINE zpl_b32 zpl_file_write_at_check(zpl_file *f, void const *buffer, zpl_isize size, zpl_i64 offset, zpl_isize *bytes_written) { + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + return f->ops.write_at(f->fd, buffer, size, offset, bytes_written); + } + + ZPL_IMPL_INLINE zpl_b32 zpl_file_read_at(zpl_file *f, void *buffer, zpl_isize size, zpl_i64 offset) { + return zpl_file_read_at_check(f, buffer, size, offset, NULL); + } + + ZPL_IMPL_INLINE zpl_b32 zpl_file_write_at(zpl_file *f, void const *buffer, zpl_isize size, zpl_i64 offset) { + return zpl_file_write_at_check(f, buffer, size, offset, NULL); + } + + ZPL_IMPL_INLINE zpl_i64 zpl_file_seek(zpl_file *f, zpl_i64 offset) { + zpl_i64 new_offset = 0; + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + f->ops.seek(f->fd, offset, ZPL_SEEK_WHENCE_BEGIN, &new_offset); + return new_offset; + } + + ZPL_IMPL_INLINE zpl_i64 zpl_file_seek_to_end(zpl_file *f) { + zpl_i64 new_offset = 0; + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + f->ops.seek(f->fd, 0, ZPL_SEEK_WHENCE_END, &new_offset); + return new_offset; + } + + // NOTE: Skips a certain amount of bytes + ZPL_IMPL_INLINE zpl_i64 zpl_file_skip(zpl_file *f, zpl_i64 bytes) { + zpl_i64 new_offset = 0; + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + f->ops.seek(f->fd, bytes, ZPL_SEEK_WHENCE_CURRENT, &new_offset); + return new_offset; + } + + ZPL_IMPL_INLINE zpl_i64 zpl_file_tell(zpl_file *f) { + zpl_i64 new_offset = 0; + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + f->ops.seek(f->fd, 0, ZPL_SEEK_WHENCE_CURRENT, &new_offset); + return new_offset; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_file_read(zpl_file *f, void *buffer, zpl_isize size) { + zpl_i64 cur_offset = zpl_file_tell(f); + zpl_b32 result = zpl_file_read_at(f, buffer, size, zpl_file_tell(f)); + zpl_file_seek(f, cur_offset + size); + return result; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_file_write(zpl_file *f, void const *buffer, zpl_isize size) { + zpl_i64 cur_offset = zpl_file_tell(f); + zpl_b32 result = zpl_file_write_at(f, buffer, size, zpl_file_tell(f)); + zpl_file_seek(f, cur_offset + size); + return result; + } + + ZPL_END_C_DECLS + // file: header/core/file_stream.h + + /** @file file_stream.c + @brief File stream + @defgroup fileio File stream + + File streaming operations on memory. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef enum { + /* Allows us to write to the buffer directly. Beware: you can not append a new data! */ + ZPL_FILE_STREAM_WRITABLE = ZPL_BIT(0), + + /* Clones the input buffer so you can write (zpl_file_write*) data into it. */ + /* Since we work with a clone, the buffer size can dynamically grow as well. */ + ZPL_FILE_STREAM_CLONE_WRITABLE = ZPL_BIT(1), + } zpl_file_stream_flags; + + /** + * Opens a new memory stream + * @param file + * @param allocator + */ + ZPL_DEF zpl_b8 zpl_file_stream_new(zpl_file* file, zpl_allocator allocator); + + /** + * Opens a memory stream over an existing buffer + * @param file + * @param allocator + * @param buffer Memory to create stream from + * @param size Buffer's size + * @param flags + */ + ZPL_DEF zpl_b8 zpl_file_stream_open(zpl_file* file, zpl_allocator allocator, zpl_u8 *buffer, zpl_isize size, zpl_file_stream_flags flags); + + /** + * Retrieves the stream's underlying buffer and buffer size. + * @param file memory stream + * @param size (Optional) buffer size + */ + ZPL_DEF zpl_u8 *zpl_file_stream_buf(zpl_file* file, zpl_isize *size); + + extern zpl_file_operations const zpl_memory_file_operations; + + //! @} + + ZPL_END_C_DECLS + // file: header/core/file_misc.h + + + ZPL_BEGIN_C_DECLS + + #ifndef ZPL_PATH_SEPARATOR + # if defined(ZPL_SYSTEM_WINDOWS) + # define ZPL_PATH_SEPARATOR '\\' + # else + # define ZPL_PATH_SEPARATOR '/' + # endif + #endif + + #ifndef ZPL_MAX_PATH + # if defined(ZPL_SYSTEM_WINDOWS) + # define ZPL_MAX_PATH MAX_PATH + # elif defined(ZPL_SYSTEM_UNIX) && !defined(ZPL_SYSTEM_EMSCRIPTEN) + # define ZPL_MAX_PATH PATH_MAX + # else + # define ZPL_MAX_PATH 4096 + # endif + #endif + + /** + * Checks if file/directory exists + * @param filepath + */ + ZPL_DEF zpl_b32 zpl_fs_exists(char const *filepath); + + /** + * Retrieves node's type (file, folder, ...) + * @param path + */ + ZPL_DEF zpl_u8 zpl_fs_get_type(char const *path); + + /** + * Retrieves file's last write time + * @param filepath + */ + ZPL_DEF zpl_file_time zpl_fs_last_write_time(char const *filepath); + + /** + * Copies the file to a directory + * @param existing_filename + * @param new_filename + * @param fail_if_exists + */ + ZPL_DEF zpl_b32 zpl_fs_copy(char const *existing_filename, char const *new_filename, zpl_b32 fail_if_exists); + + /** + * Moves the file to a directory + * @param existing_filename + * @param new_filename + */ + ZPL_DEF zpl_b32 zpl_fs_move(char const *existing_filename, char const *new_filename); + + /** + * Removes a file from a directory + * @param filename + */ + ZPL_DEF zpl_b32 zpl_fs_remove(char const *filename); + + ZPL_DEF_INLINE zpl_b32 zpl_path_is_absolute(char const *path); + ZPL_DEF_INLINE zpl_b32 zpl_path_is_relative(char const *path); + ZPL_DEF_INLINE zpl_b32 zpl_path_is_root(char const *path); + + ZPL_DEF_INLINE char const *zpl_path_base_name(char const *path); + ZPL_DEF_INLINE char const *zpl_path_extension(char const *path); + + ZPL_DEF void zpl_path_fix_slashes(char *path); + + ZPL_DEF zpl_file_error zpl_path_mkdir(char const *path, zpl_i32 mode); + ZPL_DEF zpl_isize zpl_path_mkdir_recursive(char const *path, zpl_i32 mode); + ZPL_DEF zpl_file_error zpl_path_rmdir(char const *path); + + ZPL_DEF char *zpl_path_get_full_name(zpl_allocator a, char const *path); + + /** + * Returns file paths terminated by newline (\n) + * @param alloc [description] + * @param dirname [description] + * @param recurse [description] + * @return [description] + */ + ZPL_DEF /*zpl_string*/char * zpl_path_dirlist(zpl_allocator alloc, char const *dirname, zpl_b32 recurse); + + /** + * Initialize dirinfo from specified path + * @param dir [description] + * @param path [description] + */ + ZPL_DEF void zpl_dirinfo_init(zpl_dir_info *dir, char const *path); + ZPL_DEF void zpl_dirinfo_free(zpl_dir_info *dir); + + /** + * Analyze the entry's dirinfo + * @param dir_entry [description] + */ + ZPL_DEF void zpl_dirinfo_step(zpl_dir_entry *dir_entry); + + + /* inlines */ + + ZPL_IMPL_INLINE zpl_b32 zpl_path_is_absolute(char const *path) { + zpl_b32 result = false; + ZPL_ASSERT_NOT_NULL(path); + #if defined(ZPL_SYSTEM_WINDOWS) + result = (zpl_strlen(path) > 2) && zpl_char_is_alpha(path[0]) && (path[1] == ':' && path[2] == ZPL_PATH_SEPARATOR); + #else + result = (zpl_strlen(path) > 0 && path[0] == ZPL_PATH_SEPARATOR); + #endif + return result; + } + + ZPL_IMPL_INLINE zpl_b32 zpl_path_is_relative(char const *path) { return !zpl_path_is_absolute(path); } + + ZPL_IMPL_INLINE zpl_b32 zpl_path_is_root(char const *path) { + zpl_b32 result = false; + ZPL_ASSERT_NOT_NULL(path); + #if defined(ZPL_SYSTEM_WINDOWS) + result = zpl_path_is_absolute(path) && (zpl_strlen(path) == 3); + #else + result = zpl_path_is_absolute(path) && (zpl_strlen(path) == 1); + #endif + return result; + } + + ZPL_IMPL_INLINE char const *zpl_path_base_name(char const *path) { + char const *ls; + ZPL_ASSERT_NOT_NULL(path); + zpl_path_fix_slashes((char *)path); + ls = zpl_char_last_occurence(path, ZPL_PATH_SEPARATOR); + return (ls == NULL) ? path : ls + 1; + } + + ZPL_IMPL_INLINE char const *zpl_path_extension(char const *path) { + char const *ld; + ZPL_ASSERT_NOT_NULL(path); + ld = zpl_char_last_occurence(path, '.'); + return (ld == NULL) ? NULL : ld + 1; + } + + ZPL_END_C_DECLS + // file: header/core/file_tar.h + + /** @file file_tar.c + @brief Tar archiving module + @defgroup fileio Tar module + + Allows to easily pack/unpack files. + Based on: https://github.com/rxi/microtar/ + + Disclaimer: The pack method does not support file permissions nor GID/UID information. Only regular files are supported. + Use zpl_tar_pack_dir to pack an entire directory recursively. Empty folders are ignored. + + @{ + */ + + + ZPL_BEGIN_C_DECLS + + typedef enum { + ZPL_TAR_ERROR_NONE, + ZPL_TAR_ERROR_INTERRUPTED, + ZPL_TAR_ERROR_IO_ERROR, + ZPL_TAR_ERROR_BAD_CHECKSUM, + ZPL_TAR_ERROR_FILE_NOT_FOUND, + ZPL_TAR_ERROR_INVALID_INPUT, + } zpl_tar_errors; + + typedef enum { + ZPL_TAR_TYPE_REGULAR = '0', + ZPL_TAR_TYPE_LINK = '1', + ZPL_TAR_TYPE_SYMBOL = '2', + ZPL_TAR_TYPE_CHR = '3', + ZPL_TAR_TYPE_BLK = '4', + ZPL_TAR_TYPE_DIR = '5', + ZPL_TAR_TYPE_FIFO = '6' + } zpl_tar_file_type; + + typedef struct { + char type; + char *path; + zpl_i64 offset; + zpl_i64 length; + zpl_isize error; + } zpl_tar_record; + + #define ZPL_TAR_UNPACK_PROC(name) zpl_isize name(zpl_file *archive, zpl_tar_record *file, void* user_data) + typedef ZPL_TAR_UNPACK_PROC(zpl_tar_unpack_proc); + + /** + * @brief Packs a list of files + * Packs a list of provided files. Note that this method only supports regular files + * and does not provide extended info such as GID/UID or permissions. + * @param archive archive we pack files into + * @param paths list of files + * @param paths_len number of files provided + * @return error + */ + ZPL_DEF zpl_isize zpl_tar_pack(zpl_file *archive, char const **paths, zpl_isize paths_len); + + /** + * @brief Packs an entire directory + * Packs an entire directory of files recursively. + * @param archive archive we pack files to + * @param path folder to pack + * @param alloc memory allocator to use (ex. zpl_heap()) + * @return error + */ + ZPL_DEF zpl_isize zpl_tar_pack_dir(zpl_file *archive, char const *path, zpl_allocator alloc); + + /** + * @brief Unpacks an existing archive + * Unpacks an existing archive. Users provide a callback in which information about file is provided. + * Library does not unpack files to the filesystem nor reads any file data. + * @param archive archive we unpack files from + * @param unpack_proc callback we call per each file parsed + * @param user_data user provided data + * @return error + */ + ZPL_DEF zpl_isize zpl_tar_unpack(zpl_file *archive, zpl_tar_unpack_proc *unpack_proc, void *user_data); + + /** + * @brief Unpacks an existing archive into directory + * Unpacks an existing archive into directory. The folder structure will be re-created automatically. + * @param archive archive we unpack files from + * @param dest directory to unpack files to + * @return error + */ + ZPL_DEF_INLINE zpl_isize zpl_tar_unpack_dir(zpl_file *archive, char const *dest); + + ZPL_DEF ZPL_TAR_UNPACK_PROC(zpl_tar_default_list_file); + ZPL_DEF ZPL_TAR_UNPACK_PROC(zpl_tar_default_unpack_file); + + //! @} + + ZPL_IMPL_INLINE zpl_isize zpl_tar_unpack_dir(zpl_file *archive, char const *dest) { + return zpl_tar_unpack(archive, zpl_tar_default_unpack_file, cast(void*)dest); + } + + ZPL_END_C_DECLS + // file: header/core/print.h + + /** @file print.c + @brief Printing methods + @defgroup print Printing methods + + Various printing methods. + @{ + */ + + ZPL_BEGIN_C_DECLS + + #ifndef ZPL_PRINTF_MAXLEN + #define ZPL_PRINTF_MAXLEN 65536 + #endif + + ZPL_DEF zpl_isize zpl_printf(char const *fmt, ...); + ZPL_DEF zpl_isize zpl_printf_va(char const *fmt, va_list va); + ZPL_DEF zpl_isize zpl_printf_err(char const *fmt, ...); + ZPL_DEF zpl_isize zpl_printf_err_va(char const *fmt, va_list va); + ZPL_DEF zpl_isize zpl_fprintf(zpl_file *f, char const *fmt, ...); + ZPL_DEF zpl_isize zpl_fprintf_va(zpl_file *f, char const *fmt, va_list va); + + // NOTE: A locally persisting buffer is used internally + ZPL_DEF char *zpl_bprintf(char const *fmt, ...); + + // NOTE: A locally persisting buffer is used internally + ZPL_DEF char *zpl_bprintf_va(char const *fmt, va_list va); + + ZPL_DEF zpl_isize zpl_asprintf(zpl_allocator allocator, char **buffer, char const *fmt, ...); + ZPL_DEF zpl_isize zpl_asprintf_va(zpl_allocator allocator, char **buffer, char const *fmt, va_list va); + + ZPL_DEF zpl_isize zpl_snprintf(char *str, zpl_isize n, char const *fmt, ...); + ZPL_DEF zpl_isize zpl_snprintf_va(char *str, zpl_isize n, char const *fmt, va_list va); + + ZPL_END_C_DECLS + // file: header/core/time.h + + /** @file time.c + @brief Time helper methods. + @defgroup time Time helpers + + Helper methods for retrieving the current time in many forms under different precisions. It also offers a simple to use timer library. + + @{ + */ + + + ZPL_BEGIN_C_DECLS + + //! Return CPU timestamp. + ZPL_DEF zpl_u64 zpl_rdtsc(void); + + //! Return relative time (in seconds) since the application start. + ZPL_DEF zpl_f64 zpl_time_rel(void); + + //! Return relative time since the application start. + ZPL_DEF zpl_u64 zpl_time_rel_ms(void); + + //! Return time (in seconds) since 1601-01-01 UTC. + ZPL_DEF zpl_f64 zpl_time_utc(void); + + //! Return time since 1601-01-01 UTC. + ZPL_DEF zpl_u64 zpl_time_utc_ms(void); + + //! Return local system time since 1601-01-01 + ZPL_DEF zpl_u64 zpl_time_tz_ms(void); + + //! Return local system time in seconds since 1601-01-01 + ZPL_DEF zpl_f64 zpl_time_tz(void); + + //! Convert Win32 epoch (1601-01-01 UTC) to UNIX (1970-01-01 UTC) + ZPL_DEF_INLINE zpl_u64 zpl_time_win32_to_unix(zpl_u64 ms); + + //! Convert UNIX (1970-01-01 UTC) to Win32 epoch (1601-01-01 UTC) + ZPL_DEF_INLINE zpl_u64 zpl_time_unix_to_win32(zpl_u64 ms); + + //! Sleep for specified number of milliseconds. + ZPL_DEF void zpl_sleep_ms(zpl_u32 ms); + + //! Sleep for specified number of seconds. + ZPL_DEF_INLINE void zpl_sleep(zpl_f32 s); + + // Deprecated methods + ZPL_DEPRECATED_FOR(10.9.0, zpl_time_rel) + ZPL_DEF_INLINE zpl_f64 zpl_time_now(void); + + ZPL_DEPRECATED_FOR(10.9.0, zpl_time_utc) + ZPL_DEF_INLINE zpl_f64 zpl_utc_time_now(void); + + + #ifndef ZPL__UNIX_TO_WIN32_EPOCH + #define ZPL__UNIX_TO_WIN32_EPOCH 11644473600000ull + #endif + + ZPL_IMPL_INLINE zpl_u64 zpl_time_win32_to_unix(zpl_u64 ms) { + return ms - ZPL__UNIX_TO_WIN32_EPOCH; + } + + ZPL_IMPL_INLINE zpl_u64 zpl_time_unix_to_win32(zpl_u64 ms) { + return ms + ZPL__UNIX_TO_WIN32_EPOCH; + } + + ZPL_IMPL_INLINE void zpl_sleep(zpl_f32 s) { + zpl_sleep_ms((zpl_u32)(s * 1000)); + } + + ZPL_IMPL_INLINE zpl_f64 zpl_time_now() { + return zpl_time_rel(); + } + + ZPL_IMPL_INLINE zpl_f64 zpl_utc_time_now() { + return zpl_time_utc(); + } + + ZPL_END_C_DECLS + // file: header/core/random.h + + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_random { + zpl_u32 offsets[8]; + zpl_u32 value; + } zpl_random; + + // NOTE: Generates from numerous sources to produce a decent pseudo-random seed + ZPL_DEF void zpl_random_init(zpl_random *r); + ZPL_DEF zpl_u32 zpl_random_gen_u32(zpl_random *r); + ZPL_DEF zpl_u32 zpl_random_gen_u32_unique(zpl_random *r); + ZPL_DEF zpl_u64 zpl_random_gen_u64(zpl_random *r); // NOTE: (zpl_random_gen_u32() << 32) | zpl_random_gen_u32() + ZPL_DEF zpl_isize zpl_random_gen_isize(zpl_random *r); + ZPL_DEF zpl_i64 zpl_random_range_i64(zpl_random *r, zpl_i64 lower_inc, zpl_i64 higher_inc); + ZPL_DEF zpl_isize zpl_random_range_isize(zpl_random *r, zpl_isize lower_inc, zpl_isize higher_inc); + ZPL_DEF zpl_f64 zpl_random_range_f64(zpl_random *r, zpl_f64 lower_inc, zpl_f64 higher_inc); + + ZPL_END_C_DECLS + // file: header/core/misc.h + + /** @file misc.c + @brief Various other stuff + @defgroup misc Various other stuff + + Methods that don't belong anywhere but are still very useful in many occasions. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + ZPL_DEF void zpl_yield(void); + + //! Returns allocated buffer + ZPL_DEF const char *zpl_get_env(const char *name); + ZPL_DEF const char *zpl_get_env_buf(const char *name); + ZPL_DEF zpl_string zpl_get_env_str(const char *name); + ZPL_DEF void zpl_set_env(const char *name, const char *value); + ZPL_DEF void zpl_unset_env(const char *name); + + ZPL_DEF zpl_u32 zpl_system_command(const char *command, zpl_usize buffer_len, char *buffer); + ZPL_DEF zpl_string zpl_system_command_str(const char *command, zpl_allocator backing); + + ZPL_DEF_INLINE zpl_u16 zpl_endian_swap16(zpl_u16 i); + ZPL_DEF_INLINE zpl_u32 zpl_endian_swap32(zpl_u32 i); + ZPL_DEF_INLINE zpl_u64 zpl_endian_swap64(zpl_u64 i); + + ZPL_DEF_INLINE zpl_isize zpl_count_set_bits(zpl_u64 mask); + + //! @} + //$$ + + ZPL_IMPL_INLINE zpl_u16 zpl_endian_swap16(zpl_u16 i) { + return (i>>8) | (i<<8); + } + + ZPL_IMPL_INLINE zpl_u32 zpl_endian_swap32(zpl_u32 i) { + return (i>>24) |(i<<24) | + ((i&0x00ff0000u)>>8) | ((i&0x0000ff00u)<<8); + } + + ZPL_IMPL_INLINE zpl_u64 zpl_endian_swap64(zpl_u64 i) { + return (i>>56) | (i<<56) | + ((i&0x00ff000000000000ull)>>40) | ((i&0x000000000000ff00ull)<<40) | + ((i&0x0000ff0000000000ull)>>24) | ((i&0x0000000000ff0000ull)<<24) | + ((i&0x000000ff00000000ull)>>8) | ((i&0x00000000ff000000ull)<<8); + } + + ZPL_IMPL_INLINE zpl_i32 zpl_next_pow2(zpl_i32 x) { + x--; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; + } + + ZPL_IMPL_INLINE void zpl_bit_set(zpl_u32* x, zpl_u32 bit) { *x = *x | (1 << bit); } + ZPL_IMPL_INLINE zpl_b8 zpl_bit_get(zpl_u32 x, zpl_u32 bit) { return (x & (1 << bit)); } + ZPL_IMPL_INLINE void zpl_bit_reset(zpl_u32* x, zpl_u32 bit) { *x = *x & ~(1 << bit); } + + ZPL_IMPL_INLINE zpl_isize zpl_count_set_bits(zpl_u64 mask) { + zpl_isize count = 0; + while (mask) { + count += (mask & 1); + mask >>= 1; + } + return count; + } + + ZPL_END_C_DECLS + // file: header/core/sort.h + + /** @file sort.c + @brief Sorting and searching methods. + @defgroup sort Sorting and searching + + Methods for sorting arrays using either Quick/Merge-sort combo or Radix sort. It also contains simple implementation of binary search, as well as an easy to use API to define your own comparators. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + #define ZPL_COMPARE_PROC(name) int name(void const *a, void const *b) + typedef ZPL_COMPARE_PROC(zpl_compare_proc); + + #define ZPL_COMPARE_PROC_PTR(def) ZPL_COMPARE_PROC((*def)) + + // Procedure pointers + // NOTE: The offset parameter specifies the offset in the structure + // e.g. zpl_i32_cmp(zpl_offset_of(Thing, value)) + // Use 0 if it's just the type instead. + + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_i16_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_u8_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_i32_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_i64_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_isize_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_str_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_f32_cmp(zpl_isize offset)); + ZPL_DEF ZPL_COMPARE_PROC_PTR(zpl_f64_cmp(zpl_isize offset)); + + // TODO: Better sorting algorithms + + //! Sorts an array. + + //! Uses quick sort for large arrays but insertion sort for small ones. + #define zpl_sort_array(array, count, compare_proc) zpl_sort(array, count, zpl_size_of(*(array)), compare_proc) + + //! Perform sorting operation on a memory location with a specified item count and size. + ZPL_DEF void zpl_sort(void *base, zpl_isize count, zpl_isize size, zpl_compare_proc compare_proc); + + // NOTE: the count of temp == count of items + #define zpl_radix_sort(Type) zpl_radix_sort_##Type + #define ZPL_RADIX_SORT_PROC(Type) void zpl_radix_sort(Type)(zpl_##Type * items, zpl_##Type * temp, zpl_isize count) + + ZPL_DEF ZPL_RADIX_SORT_PROC(u8); + ZPL_DEF ZPL_RADIX_SORT_PROC(u16); + ZPL_DEF ZPL_RADIX_SORT_PROC(u32); + ZPL_DEF ZPL_RADIX_SORT_PROC(u64); + + //! Performs binary search on an array. + + //! Returns index or -1 if not found + #define zpl_binary_search_array(array, count, key, compare_proc) \ + zpl_binary_search(array, count, zpl_size_of(*(array)), key, compare_proc) + + //! Performs binary search on a memory location with specified item count and size. + ZPL_DEF_INLINE zpl_isize zpl_binary_search(void const *base, zpl_isize count, zpl_isize size, void const *key, + zpl_compare_proc compare_proc); + + #define zpl_shuffle_array(array, count) zpl_shuffle(array, count, zpl_size_of(*(array))) + + //! Shuffles a memory. + ZPL_DEF void zpl_shuffle(void *base, zpl_isize count, zpl_isize size); + + #define zpl_reverse_array(array, count) zpl_reverse(array, count, zpl_size_of(*(array))) + + //! Reverses memory's contents + ZPL_DEF void zpl_reverse(void *base, zpl_isize count, zpl_isize size); + + //! @} + + + ZPL_IMPL_INLINE zpl_isize zpl_binary_search(void const *base, zpl_isize count, zpl_isize size, void const *key, + zpl_compare_proc compare_proc) { + zpl_isize start = 0; + zpl_isize end = count; + + while (start < end) { + zpl_isize mid = start + (end - start) / 2; + zpl_isize result = compare_proc(key, cast(zpl_u8 *) base + mid * size); + if (result < 0) + end = mid; + else if (result > 0) + start = mid + 1; + else + return mid; + } + + return -1; + } + + ZPL_END_C_DECLS +# endif +#endif + +#if defined(ZPL_MODULE_HASHING) + // file: header/hashing.h + + /** @file hashing.c + @brief Hashing and Checksum Functions + @defgroup hashing Hashing and Checksum Functions + + Several hashing methods used by zpl internally but possibly useful outside of it. Contains: adler32, crc32/64, fnv32/64/a and murmur32/64 + + @{ + */ + + + ZPL_BEGIN_C_DECLS + + ZPL_DEF zpl_u32 zpl_adler32(void const *data, zpl_isize len); + + ZPL_DEF zpl_u32 zpl_crc32(void const *data, zpl_isize len); + ZPL_DEF zpl_u64 zpl_crc64(void const *data, zpl_isize len); + + // These use FNV-1 algorithm + ZPL_DEF zpl_u32 zpl_fnv32(void const *data, zpl_isize len); + ZPL_DEF zpl_u64 zpl_fnv64(void const *data, zpl_isize len); + ZPL_DEF zpl_u32 zpl_fnv32a(void const *data, zpl_isize len); + ZPL_DEF zpl_u64 zpl_fnv64a(void const *data, zpl_isize len); + + ZPL_DEF zpl_u8 *zpl_base64_encode(zpl_allocator a, void const *data, zpl_isize len); + ZPL_DEF zpl_u8 *zpl_base64_decode(zpl_allocator a, void const *data, zpl_isize len); + + //! Based on MurmurHash3 + ZPL_DEF zpl_u32 zpl_murmur32_seed(void const *data, zpl_isize len, zpl_u32 seed); + + //! Based on MurmurHash2 + ZPL_DEF zpl_u64 zpl_murmur64_seed(void const *data, zpl_isize len, zpl_u64 seed); + + //! Default seed of 0x9747b28c + ZPL_DEF_INLINE zpl_u32 zpl_murmur32(void const *data, zpl_isize len); + + //! Default seed of 0x9747b28c + ZPL_DEF_INLINE zpl_u64 zpl_murmur64(void const *data, zpl_isize len); + + //! @} + + ZPL_IMPL_INLINE zpl_u32 zpl_murmur32(void const *data, zpl_isize len) { return zpl_murmur32_seed(data, len, 0x9747b28c); } + ZPL_IMPL_INLINE zpl_u64 zpl_murmur64(void const *data, zpl_isize len) { return zpl_murmur64_seed(data, len, 0x9747b28c); } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_REGEX) + // file: header/regex.h + + /** @file regex.c + @brief Regular expressions parser. + @defgroup regex Regex processor + + Port of gb_regex with several bugfixes applied. This is a simple regex library and is fast to perform. + + Supported Matching: + @n ^ - Beginning of string + @n $ - End of string + @n . - Match one (anything) + @n | - Branch (or) + @n () - Capturing group + @n [] - Any character included in set + @n [^] - Any character excluded from set + @n + - One or more (greedy) + @n +? - One or more (non-greedy) + @n * - Zero or more (greedy) + @n *? - Zero or more (non-greedy) + @n ? - Zero or once + @n [BACKSLASH]XX - Hex decimal digit (must be 2 digits) + @n [BACKSLASH]meta - Meta character + @n [BACKSLASH]s - Whitespace + @n [BACKSLASH]S - Not whitespace + @n [BACKSLASH]d - Digit + @n [BACKSLASH]D - Not digit + @n [BACKSLASH]a - Alphabetic character + @n [BACKSLASH]l - Lower case letter + @n [BACKSLASH]u - Upper case letter + @n [BACKSLASH]w - Word + @n [BACKSLASH]W - Not word + @n [BACKSLASH]x - Hex Digit + @n [BACKSLASH]p - Printable ASCII character + @n --Whitespace-- + @n [BACKSLASH]t - Tab + @n [BACKSLASH]n - New line + @n [BACKSLASH]r - Return carriage + @n [BACKSLASH]v - Vertical Tab + @n [BACKSLASH]f - Form feed + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_re { + zpl_allocator backing; + zpl_isize capture_count; + char *buf; + zpl_isize buf_len, buf_cap; + zpl_b32 can_realloc; + } zpl_re; + + typedef struct zpl_re_capture { + char const *str; + zpl_isize len; + } zpl_re_capture; + + #define zplRegexError zpl_regex_error + typedef enum zpl_regex_error { + ZPL_RE_ERROR_NONE, + ZPL_RE_ERROR_NO_MATCH, + ZPL_RE_ERROR_TOO_LONG, + ZPL_RE_ERROR_MISMATCHED_CAPTURES, + ZPL_RE_ERROR_MISMATCHED_BLOCKS, + ZPL_RE_ERROR_BRANCH_FAILURE, + ZPL_RE_ERROR_INVALID_QUANTIFIER, + ZPL_RE_ERROR_INTERNAL_FAILURE, + } zpl_regex_error; + + //! Compile regex pattern. + ZPL_DEF zpl_regex_error zpl_re_compile(zpl_re *re, zpl_allocator backing, char const *pattern, zpl_isize pattern_len); + + //! Compile regex pattern using a buffer. + ZPL_DEF zpl_regex_error zpl_re_compile_from_buffer(zpl_re *re, char const *pattern, zpl_isize pattern_len, void *buffer, zpl_isize buffer_len); + + //! Destroy regex object. + ZPL_DEF void zpl_re_destroy(zpl_re *re); + + //! Retrieve number of retrievable captures. + ZPL_DEF zpl_isize zpl_re_capture_count(zpl_re *re); + + //! Match input string and output captures of the occurence. + ZPL_DEF zpl_b32 zpl_re_match(zpl_re *re, char const *str, zpl_isize str_len, zpl_re_capture *captures, zpl_isize max_capture_count, zpl_isize *offset); + + //! Match all occurences in an input string and output them into captures. Array of captures is allocated on the heap and needs to be freed afterwards. + ZPL_DEF zpl_b32 zpl_re_match_all(zpl_re *re, char const *str, zpl_isize str_len, zpl_isize max_capture_count, zpl_re_capture **out_captures); + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_DLL) + // file: header/dll.h + + /** @file dll.c + @brief DLL Handling + @defgroup dll DLL handling + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef void *zpl_dll_handle; + typedef void (*zpl_dll_proc)(void); + + ZPL_DEF zpl_dll_handle zpl_dll_load(char const *filepath); + ZPL_DEF void zpl_dll_unload(zpl_dll_handle dll); + ZPL_DEF zpl_dll_proc zpl_dll_proc_address(zpl_dll_handle dll, char const *proc_name); + + //! @} + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_OPTS) + // file: header/opts.h + + /** @file opts.c + @brief CLI options processor + @defgroup cli CLI options processor + + Opts is a CLI options parser, it can parse flags, switches and arguments from command line + and offers an easy way to express input errors as well as the ability to display help screen. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef enum { + ZPL_OPTS_STRING, + ZPL_OPTS_FLOAT, + ZPL_OPTS_FLAG, + ZPL_OPTS_INT, + } zpl_opts_types; + + typedef struct { + char const *name, *lname, *desc; + zpl_u8 type; + zpl_b32 met, pos; + + //! values + union { + zpl_string text; + zpl_i64 integer; + zpl_f64 real; + }; + } zpl_opts_entry; + + typedef enum { + ZPL_OPTS_ERR_VALUE, + ZPL_OPTS_ERR_OPTION, + ZPL_OPTS_ERR_EXTRA_VALUE, + ZPL_OPTS_ERR_MISSING_VALUE, + } zpl_opts_err_type; + + typedef struct { + char *val; + zpl_u8 type; + } zpl_opts_err; + + typedef struct { + zpl_allocator alloc; + zpl_opts_entry *entries; ///< zpl_array + zpl_opts_err *errors; ///< zpl_array + zpl_opts_entry **positioned; ///< zpl_array + char const *appname; + } zpl_opts; + + //! Initializes options parser. + + //! Initializes CLI options parser using specified memory allocator and provided application name. + //! @param opts Options parser to initialize. + //! @param allocator Memory allocator to use. (ex. zpl_heap()) + //! @param app Application name displayed in help screen. + ZPL_DEF void zpl_opts_init(zpl_opts *opts, zpl_allocator allocator, char const *app); + + //! Releases the resources used by options parser. + ZPL_DEF void zpl_opts_free(zpl_opts *opts); + + //! Registers an option. + + //! Registers an option with its short and long name, specifies option's type and its description. + //! @param opts Options parser to add to. + //! @param lname Shorter name of option. (ex. "f") + //! @param name Full name of option. (ex. "foo") Note that rest of the module uses longer names to manipulate opts. + //! @param desc Description shown in the help screen. + //! @param type Option's type (see zpl_opts_types) + //! @see zpl_opts_types + ZPL_DEF void zpl_opts_add(zpl_opts *opts, char const *name, char const *lname, const char *desc, zpl_u8 type); + + //! Registers option as positional. + + //! Registers added option as positional, so that we can pass it anonymously. Arguments are expected on the command input in the same order they were registered as. + //! @param opts + //! @param name Name of already registered option. + ZPL_DEF void zpl_opts_positional_add(zpl_opts *opts, char const *name); + + //! Compiles CLI arguments. + + // This method takes CLI arguments as input and processes them based on rules that were set up. + //! @param opts + //! @param argc Argument count in an array. + //! @param argv Array of arguments. + ZPL_DEF zpl_b32 zpl_opts_compile(zpl_opts *opts, int argc, char **argv); + + //! Prints out help screen. + + //! Prints out help screen with example usage of application as well as with all the flags available. + ZPL_DEF void zpl_opts_print_help(zpl_opts *opts); + + //! Prints out parsing errors. + + //! Prints out possible errors caused by CLI input. + ZPL_DEF void zpl_opts_print_errors(zpl_opts *opts); + + //! Fetches a string from an option. + + //! @param opts + //! @param name Name of an option. + //! @param fallback Fallback string we return if option wasn't found. + ZPL_DEF zpl_string zpl_opts_string(zpl_opts *opts, char const *name, char const *fallback); + + //! Fetches a real number from an option. + + //! @param opts + //! @param name Name of an option. + //! @param fallback Fallback real number we return if option was not found. + ZPL_DEF zpl_f64 zpl_opts_real(zpl_opts *opts, char const *name, zpl_f64 fallback); + + //! Fetches an integer number from an option. + + //! @param opts + //! @param name Name of an option. + //! @param fallback Fallback integer number we return if option was not found. + ZPL_DEF zpl_i64 zpl_opts_integer(zpl_opts *opts, char const *name, zpl_i64 fallback); + + //! Checks whether an option was used. + + //! @param opts + //! @param name Name of an option. + ZPL_DEF zpl_b32 zpl_opts_has_arg(zpl_opts *opts, char const *name); + + //! Checks whether all positionals have been passed in. + ZPL_DEF zpl_b32 zpl_opts_positionals_filled(zpl_opts *opts); + + //! @} + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_PROCESS) + // file: header/process.h + + /** @file process.c + @brief Process creation and manipulation methods + @defgroup process Process creation and manipulation methods + + Gives you the ability to create a new process, wait for it to end or terminate it. + It also exposes standard I/O with configurable options. + + @{ + */ + + ZPL_BEGIN_C_DECLS + // TODO(zaklaus): Add Linux support + + typedef enum { + ZPL_PR_OPTS_COMBINE_STD_OUTPUT = ZPL_BIT(1), + ZPL_PR_OPTS_INHERIT_ENV = ZPL_BIT(2), + ZPL_PR_OPTS_CUSTOM_ENV = ZPL_BIT(3), + } zpl_pr_opts; + + typedef struct { + zpl_file in, out, err; + void *f_stdin, *f_stdout, *f_stderr; + #ifdef ZPL_SYSTEM_WINDOWS + void *win32_handle; + #else + // todo + #endif + } zpl_pr; + + typedef struct { + char *con_title; + char *workdir; + + zpl_isize env_count; + char **env; // format: "var=name" + + zpl_u32 posx, posy; + zpl_u32 resx, resy; + zpl_u32 bufx, bufy; + zpl_u32 fill_attr; + zpl_u32 flags; + zpl_b32 show_window; + } zpl_pr_si; + + ZPL_DEF zpl_i32 zpl_pr_create(zpl_pr *process, const char **args, zpl_isize argc, zpl_pr_si si, zpl_pr_opts options); + ZPL_DEF void zpl_pr_destroy(zpl_pr *process); + ZPL_DEF void zpl_pr_terminate(zpl_pr *process, zpl_i32 err_code); + ZPL_DEF zpl_i32 zpl_pr_join(zpl_pr *process); + + //! @} + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_MATH) + // file: header/math.h + + /** @file math.c + @brief Math operations + @defgroup math Math operations + + OpenGL gamedev friendly library for math. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef union zpl_vec2 { + struct { + zpl_f32 x, y; + }; + struct { + zpl_f32 s, t; + }; + zpl_f32 e[2]; + } zpl_vec2; + + typedef union zpl_vec3 { + struct { + zpl_f32 x, y, z; + }; + struct { + zpl_f32 r, g, b; + }; + struct { + zpl_f32 s, t, p; + }; + + zpl_vec2 xy; + zpl_vec2 st; + zpl_f32 e[3]; + } zpl_vec3; + + typedef union zpl_vec4 { + struct { + zpl_f32 x, y, z, w; + }; + struct { + zpl_f32 r, g, b, a; + }; + struct { + zpl_f32 s, t, p, q; + }; + struct { + zpl_vec2 xy, zw; + }; + struct { + zpl_vec2 st, pq; + }; + zpl_vec3 xyz; + zpl_vec3 rgb; + zpl_f32 e[4]; + } zpl_vec4; + + typedef union zpl_mat2 { + struct { + zpl_vec2 x, y; + }; + zpl_vec2 col[2]; + zpl_f32 e[4]; + } zpl_mat2; + + typedef union zpl_mat3 { + struct { + zpl_vec3 x, y, z; + }; + zpl_vec3 col[3]; + zpl_f32 e[9]; + } zpl_mat3; + + typedef union zpl_mat4 { + struct { + zpl_vec4 x, y, z, w; + }; + zpl_vec4 col[4]; + zpl_f32 e[16]; + } zpl_mat4; + + typedef union zpl_quat { + struct { + zpl_f32 x, y, z, w; + }; + zpl_vec4 xyzw; + zpl_vec3 xyz; + zpl_f32 e[4]; + } zpl_quat; + + typedef union zpl_plane { + struct { + zpl_f32 a, b, c, d; + }; + zpl_vec4 xyzw; + zpl_vec3 n; + zpl_f32 e[4]; + } zpl_plane; + + typedef struct zpl_frustum { + zpl_plane x1; + zpl_plane x2; + zpl_plane y1; + zpl_plane y2; + zpl_plane z1; + zpl_plane z2; + } zpl_frustum; + + typedef zpl_f32 zpl_float2[2]; + typedef zpl_f32 zpl_float3[3]; + typedef zpl_f32 zpl_float4[4]; + + typedef struct zpl_rect2 { + zpl_vec2 pos, dim; + } zpl_rect2; + typedef struct zpl_rect3 { + zpl_vec3 pos, dim; + } zpl_rect3; + + typedef struct zpl_aabb2 { + zpl_vec2 min, max; + } zpl_aabb2; + typedef struct zpl_aabb3 { + zpl_vec3 min, max; + } zpl_aabb3; + + typedef short zpl_half; + + #ifndef ZPL_CONSTANTS + #define ZPL_CONSTANTS + #define ZPL_EPSILON 1.19209290e-7f + #define ZPL_ZERO 0.0f + #define ZPL_ONE 1.0f + #define ZPL_TWO_THIRDS 0.666666666666666666666666666666666666667f + + #define ZPL_TAU 6.28318530717958647692528676655900576f + #define ZPL_PI 3.14159265358979323846264338327950288f + #define ZPL_ONE_OVER_TAU 0.636619772367581343075535053490057448f + #define ZPL_ONE_OVER_PI 0.159154943091895335768883763372514362f + + #define ZPL_TAU_OVER_2 3.14159265358979323846264338327950288f + #define ZPL_TAU_OVER_4 1.570796326794896619231321691639751442f + #define ZPL_TAU_OVER_8 0.785398163397448309615660845819875721f + + #define ZPL_E 2.71828182845904523536f + #define ZPL_SQRT_TWO 1.41421356237309504880168872420969808f + #define ZPL_SQRT_THREE 1.73205080756887729352744634150587236f + #define ZPL_SQRT_FIVE 2.23606797749978969640917366873127623f + + #define ZPL_LOG_TWO 0.693147180559945309417232121458176568f + #define ZPL_LOG_TEN 2.30258509299404568401799145468436421f + #endif // ZPL_CONSTANTS + + #ifndef zpl_square + #define zpl_square(x) ((x) * (x)) + #endif + + #ifndef zpl_cube + #define zpl_cube(x) ((x) * (x) * (x)) + #endif + + #ifndef zpl_sign + #define zpl_sign(x) ((x) >= 0.0f ? 1.0f : -1.0f) + #endif + + #ifndef zpl_sign0 + #define zpl_sign0(x) ((x == 0.0f) ? 0.0f : ((x) >= 0.0f ? 1.0f : -1.0f)) + #endif + + ZPL_DEF zpl_f32 zpl_to_radians(zpl_f32 degrees); + ZPL_DEF zpl_f32 zpl_to_degrees(zpl_f32 radians); + + /* NOTE: Because to interpolate angles */ + ZPL_DEF zpl_f32 zpl_angle_diff(zpl_f32 radians_a, zpl_f32 radians_b); + + ZPL_DEF zpl_f32 zpl_copy_sign(zpl_f32 x, zpl_f32 y); + ZPL_DEF zpl_f32 zpl_remainder(zpl_f32 x, zpl_f32 y); + ZPL_DEF zpl_f32 zpl_mod(zpl_f32 x, zpl_f32 y); + ZPL_DEF zpl_f64 zpl_copy_sign64(zpl_f64 x, zpl_f64 y); + ZPL_DEF zpl_f64 zpl_floor64(zpl_f64 x); + ZPL_DEF zpl_f64 zpl_ceil64(zpl_f64 x); + ZPL_DEF zpl_f64 zpl_round64(zpl_f64 x); + ZPL_DEF zpl_f64 zpl_remainder64(zpl_f64 x, zpl_f64 y); + ZPL_DEF zpl_f64 zpl_abs64(zpl_f64 x); + ZPL_DEF zpl_f64 zpl_sign64(zpl_f64 x); + ZPL_DEF zpl_f64 zpl_mod64(zpl_f64 x, zpl_f64 y); + ZPL_DEF zpl_f32 zpl_sqrt(zpl_f32 a); + ZPL_DEF zpl_f32 zpl_rsqrt(zpl_f32 a); + ZPL_DEF zpl_f32 zpl_quake_rsqrt(zpl_f32 a); /* NOTE: It's probably better to use 1.0f/zpl_sqrt(a) + * And for simd, there is usually isqrt functions too! + */ + ZPL_DEF zpl_f32 zpl_hypot(zpl_f32 x, zpl_f32 y); + ZPL_DEF zpl_f32 zpl_sin(zpl_f32 radians); + ZPL_DEF zpl_f32 zpl_cos(zpl_f32 radians); + ZPL_DEF zpl_f32 zpl_tan(zpl_f32 radians); + ZPL_DEF zpl_f32 zpl_arcsin(zpl_f32 a); + ZPL_DEF zpl_f32 zpl_arccos(zpl_f32 a); + ZPL_DEF zpl_f32 zpl_arctan(zpl_f32 a); + ZPL_DEF zpl_f32 zpl_arctan2(zpl_f32 y, zpl_f32 x); + + ZPL_DEF zpl_f32 zpl_exp(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_exp2(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_log(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_log2(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_fast_exp(zpl_f32 x); /* NOTE: Only valid from -1 <= x <= +1 */ + ZPL_DEF zpl_f32 zpl_fast_exp2(zpl_f32 x); /* NOTE: Only valid from -1 <= x <= +1 */ + ZPL_DEF zpl_f32 zpl_pow(zpl_f32 x, zpl_f32 y); /* x^y */ + + ZPL_DEF zpl_f32 zpl_round(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_floor(zpl_f32 x); + ZPL_DEF zpl_f32 zpl_ceil(zpl_f32 x); + + ZPL_DEF zpl_f32 zpl_half_to_float(zpl_half value); + ZPL_DEF zpl_half zpl_float_to_half(zpl_f32 value); + + ZPL_DEF zpl_vec2 zpl_vec2f_zero(void); + ZPL_DEF zpl_vec2 zpl_vec2f(zpl_f32 x, zpl_f32 y); + ZPL_DEF zpl_vec2 zpl_vec2fv(zpl_f32 x[2]); + + ZPL_DEF zpl_vec3 zpl_vec3f_zero(void); + ZPL_DEF zpl_vec3 zpl_vec3f(zpl_f32 x, zpl_f32 y, zpl_f32 z); + ZPL_DEF zpl_vec3 zpl_vec3fv(zpl_f32 x[3]); + + ZPL_DEF zpl_vec4 zpl_vec4f_zero(void); + ZPL_DEF zpl_vec4 zpl_vec4f(zpl_f32 x, zpl_f32 y, zpl_f32 z, zpl_f32 w); + ZPL_DEF zpl_vec4 zpl_vec4fv(zpl_f32 x[4]); + + ZPL_DEF zpl_f32 zpl_vec2_max(zpl_vec2 v); + ZPL_DEF zpl_f32 zpl_vec2_side(zpl_vec2 p, zpl_vec2 q, zpl_vec2 r); + ZPL_DEF void zpl_vec2_add(zpl_vec2 *d, zpl_vec2 v0, zpl_vec2 v1); + ZPL_DEF void zpl_vec2_sub(zpl_vec2 *d, zpl_vec2 v0, zpl_vec2 v1); + ZPL_DEF void zpl_vec2_mul(zpl_vec2 *d, zpl_vec2 v, zpl_f32 s); + ZPL_DEF void zpl_vec2_div(zpl_vec2 *d, zpl_vec2 v, zpl_f32 s); + + ZPL_DEF zpl_f32 zpl_vec3_max(zpl_vec3 v); + ZPL_DEF void zpl_vec3_add(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1); + ZPL_DEF void zpl_vec3_sub(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1); + ZPL_DEF void zpl_vec3_mul(zpl_vec3 *d, zpl_vec3 v, zpl_f32 s); + ZPL_DEF void zpl_vec3_div(zpl_vec3 *d, zpl_vec3 v, zpl_f32 s); + + ZPL_DEF void zpl_vec4_add(zpl_vec4 *d, zpl_vec4 v0, zpl_vec4 v1); + ZPL_DEF void zpl_vec4_sub(zpl_vec4 *d, zpl_vec4 v0, zpl_vec4 v1); + ZPL_DEF void zpl_vec4_mul(zpl_vec4 *d, zpl_vec4 v, zpl_f32 s); + ZPL_DEF void zpl_vec4_div(zpl_vec4 *d, zpl_vec4 v, zpl_f32 s); + + ZPL_DEF void zpl_vec2_addeq(zpl_vec2 *d, zpl_vec2 v); + ZPL_DEF void zpl_vec2_subeq(zpl_vec2 *d, zpl_vec2 v); + ZPL_DEF void zpl_vec2_muleq(zpl_vec2 *d, zpl_f32 s); + ZPL_DEF void zpl_vec2_diveq(zpl_vec2 *d, zpl_f32 s); + + ZPL_DEF void zpl_vec3_addeq(zpl_vec3 *d, zpl_vec3 v); + ZPL_DEF void zpl_vec3_subeq(zpl_vec3 *d, zpl_vec3 v); + ZPL_DEF void zpl_vec3_muleq(zpl_vec3 *d, zpl_f32 s); + ZPL_DEF void zpl_vec3_diveq(zpl_vec3 *d, zpl_f32 s); + + ZPL_DEF void zpl_vec4_addeq(zpl_vec4 *d, zpl_vec4 v); + ZPL_DEF void zpl_vec4_subeq(zpl_vec4 *d, zpl_vec4 v); + ZPL_DEF void zpl_vec4_muleq(zpl_vec4 *d, zpl_f32 s); + ZPL_DEF void zpl_vec4_diveq(zpl_vec4 *d, zpl_f32 s); + + ZPL_DEF zpl_f32 zpl_vec2_dot(zpl_vec2 v0, zpl_vec2 v1); + ZPL_DEF zpl_f32 zpl_vec3_dot(zpl_vec3 v0, zpl_vec3 v1); + ZPL_DEF zpl_f32 zpl_vec4_dot(zpl_vec4 v0, zpl_vec4 v1); + + ZPL_DEF void zpl_vec2_cross(zpl_f32 *d, zpl_vec2 v0, zpl_vec2 v1); + ZPL_DEF void zpl_vec3_cross(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1); + + ZPL_DEF zpl_f32 zpl_vec2_mag2(zpl_vec2 v); + ZPL_DEF zpl_f32 zpl_vec3_mag2(zpl_vec3 v); + ZPL_DEF zpl_f32 zpl_vec4_mag2(zpl_vec4 v); + + ZPL_DEF zpl_f32 zpl_vec2_mag(zpl_vec2 v); + ZPL_DEF zpl_f32 zpl_vec3_mag(zpl_vec3 v); + ZPL_DEF zpl_f32 zpl_vec4_mag(zpl_vec4 v); + + ZPL_DEF void zpl_vec2_norm(zpl_vec2 *d, zpl_vec2 v); + ZPL_DEF void zpl_vec3_norm(zpl_vec3 *d, zpl_vec3 v); + ZPL_DEF void zpl_vec4_norm(zpl_vec4 *d, zpl_vec4 v); + + ZPL_DEF void zpl_vec2_norm0(zpl_vec2 *d, zpl_vec2 v); + ZPL_DEF void zpl_vec3_norm0(zpl_vec3 *d, zpl_vec3 v); + ZPL_DEF void zpl_vec4_norm0(zpl_vec4 *d, zpl_vec4 v); + + ZPL_DEF void zpl_vec2_reflect(zpl_vec2 *d, zpl_vec2 i, zpl_vec2 n); + ZPL_DEF void zpl_vec3_reflect(zpl_vec3 *d, zpl_vec3 i, zpl_vec3 n); + ZPL_DEF void zpl_vec2_refract(zpl_vec2 *d, zpl_vec2 i, zpl_vec2 n, zpl_f32 eta); + ZPL_DEF void zpl_vec3_refract(zpl_vec3 *d, zpl_vec3 i, zpl_vec3 n, zpl_f32 eta); + + ZPL_DEF zpl_f32 zpl_vec2_aspect_ratio(zpl_vec2 v); + + ZPL_DEF void zpl_mat2_identity(zpl_mat2 *m); + ZPL_DEF void zpl_float22_identity(zpl_f32 m[2][2]); + + ZPL_DEF void zpl_mat2_transpose(zpl_mat2 *m); + ZPL_DEF void zpl_mat2_mul(zpl_mat2 *out, zpl_mat2 *m1, zpl_mat2 *m2); + ZPL_DEF void zpl_mat2_mul_vec2(zpl_vec2 *out, zpl_mat2 *m, zpl_vec2 in); + ZPL_DEF void zpl_mat2_inverse(zpl_mat2 *out, zpl_mat2 *in); + ZPL_DEF zpl_f32 zpl_mat2_determinate(zpl_mat2 *m); + + ZPL_DEF zpl_mat2 *zpl_mat2_v(zpl_vec2 m[2]); + ZPL_DEF zpl_mat2 *zpl_mat2_f(zpl_f32 m[2][2]); + ZPL_DEF zpl_float2 *zpl_float22_m(zpl_mat2 *m); + ZPL_DEF zpl_float2 *zpl_float22_v(zpl_vec2 m[2]); + ZPL_DEF zpl_float2 *zpl_float22_4(zpl_f32 m[4]); + + ZPL_DEF void zpl_float22_transpose(zpl_f32 (*vec)[2]); + ZPL_DEF void zpl_float22_mul(zpl_f32 (*out)[2], zpl_f32 (*mat1)[2], zpl_f32 (*mat2)[2]); + ZPL_DEF void zpl_float22_mul_vec2(zpl_vec2 *out, zpl_f32 m[2][2], zpl_vec2 in); + + ZPL_DEF void zpl_mat3_identity(zpl_mat3 *m); + ZPL_DEF void zpl_float33_identity(zpl_f32 m[3][3]); + + ZPL_DEF void zpl_mat3_transpose(zpl_mat3 *m); + ZPL_DEF void zpl_mat3_mul(zpl_mat3 *out, zpl_mat3 *m1, zpl_mat3 *m2); + ZPL_DEF void zpl_mat3_mul_vec3(zpl_vec3 *out, zpl_mat3 *m, zpl_vec3 in); + ZPL_DEF void zpl_mat3_inverse(zpl_mat3 *out, zpl_mat3 *in); + ZPL_DEF zpl_f32 zpl_mat3_determinate(zpl_mat3 *m); + + ZPL_DEF zpl_mat3 *zpl_mat3_v(zpl_vec3 m[3]); + ZPL_DEF zpl_mat3 *zpl_mat3_f(zpl_f32 m[3][3]); + + ZPL_DEF zpl_float3 *zpl_float33_m(zpl_mat3 *m); + ZPL_DEF zpl_float3 *zpl_float33_v(zpl_vec3 m[3]); + ZPL_DEF zpl_float3 *zpl_float33_9(zpl_f32 m[9]); + + ZPL_DEF void zpl_float33_transpose(zpl_f32 (*vec)[3]); + ZPL_DEF void zpl_float33_mul(zpl_f32 (*out)[3], zpl_f32 (*mat1)[3], zpl_f32 (*mat2)[3]); + ZPL_DEF void zpl_float33_mul_vec3(zpl_vec3 *out, zpl_f32 m[3][3], zpl_vec3 in); + + ZPL_DEF void zpl_mat4_identity(zpl_mat4 *m); + ZPL_DEF void zpl_float44_identity(zpl_f32 m[4][4]); + ZPL_DEF void zpl_mat4_copy(zpl_mat4* out, zpl_mat4* m); + + ZPL_DEF void zpl_mat4_transpose(zpl_mat4 *m); + ZPL_DEF void zpl_mat4_mul(zpl_mat4 *out, zpl_mat4 *m1, zpl_mat4 *m2); + ZPL_DEF void zpl_mat4_mul_vec4(zpl_vec4 *out, zpl_mat4 *m, zpl_vec4 in); + ZPL_DEF void zpl_mat4_inverse(zpl_mat4 *out, zpl_mat4 *in); + + ZPL_DEF zpl_mat4 *zpl_mat4_v(zpl_vec4 m[4]); + ZPL_DEF zpl_mat4 *zpl_mat4_f(zpl_f32 m[4][4]); + + ZPL_DEF zpl_float4 *zpl_float44_m(zpl_mat4 *m); + ZPL_DEF zpl_float4 *zpl_float44_v(zpl_vec4 m[4]); + ZPL_DEF zpl_float4 *zpl_float44_16(zpl_f32 m[16]); + + ZPL_DEF void zpl_float44_transpose(zpl_f32 (*vec)[4]); + ZPL_DEF void zpl_float44_mul(zpl_f32 (*out)[4], zpl_f32 (*mat1)[4], zpl_f32 (*mat2)[4]); + ZPL_DEF void zpl_float44_mul_vec4(zpl_vec4 *out, zpl_f32 m[4][4], zpl_vec4 in); + + ZPL_DEF void zpl_mat4_axis_angle(zpl_mat4* out, zpl_vec3 v, zpl_f32 angle_radians); + ZPL_DEF void zpl_mat4_to_translate(zpl_mat4* out, zpl_vec3 v); + ZPL_DEF void zpl_mat4_to_rotate(zpl_mat4* out, zpl_vec3 v, zpl_f32 angle_radians); + ZPL_DEF void zpl_mat4_to_scale(zpl_mat4* out, zpl_vec3 v); + ZPL_DEF void zpl_mat4_to_scalef(zpl_mat4* out, zpl_f32 s); + ZPL_DEF void zpl_mat4_translate(zpl_mat4* out, zpl_vec3 v); + ZPL_DEF void zpl_mat4_rotate(zpl_mat4* out, zpl_vec3 v, zpl_f32 angle_radians); + ZPL_DEF void zpl_mat4_scale(zpl_mat4* out, zpl_vec3 v); + ZPL_DEF void zpl_mat4_scalef(zpl_mat4 *out, zpl_f32 s); + ZPL_DEF void zpl_mat4_ortho2d(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top); + ZPL_DEF void zpl_mat4_ortho3d(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top, zpl_f32 z_near, zpl_f32 z_far); + ZPL_DEF void zpl_mat4_perspective(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near, zpl_f32 z_far); + ZPL_DEF void zpl_mat4_infinite_perspective(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near); + + ZPL_DEF void zpl_mat4_ortho2d_dx(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top); + ZPL_DEF void zpl_mat4_ortho3d_dx(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top, zpl_f32 z_near, zpl_f32 z_far); + ZPL_DEF void zpl_mat4_perspective_dx(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near, zpl_f32 z_far); + ZPL_DEF void zpl_mat4_infinite_perspective_dx(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near); + + ZPL_DEF void zpl_mat4_look_at(zpl_mat4 *out, zpl_vec3 eye, zpl_vec3 centre, zpl_vec3 up); + + ZPL_DEF void zpl_mat4_look_at_lh(zpl_mat4 *out, zpl_vec3 eye, zpl_vec3 centre, zpl_vec3 up); + + ZPL_DEF zpl_quat zpl_quatf(zpl_f32 x, zpl_f32 y, zpl_f32 z, zpl_f32 w); + ZPL_DEF zpl_quat zpl_quatfv(zpl_f32 e[4]); + ZPL_DEF zpl_quat zpl_quat_axis_angle(zpl_vec3 axis, zpl_f32 angle_radians); + ZPL_DEF zpl_quat zpl_quat_euler_angles(zpl_f32 pitch, zpl_f32 yaw, zpl_f32 roll); + ZPL_DEF zpl_quat zpl_quat_identity(void); + + ZPL_DEF void zpl_quat_add(zpl_quat *d, zpl_quat q0, zpl_quat q1); + ZPL_DEF void zpl_quat_sub(zpl_quat *d, zpl_quat q0, zpl_quat q1); + ZPL_DEF void zpl_quat_mul(zpl_quat *d, zpl_quat q0, zpl_quat q1); + ZPL_DEF void zpl_quat_div(zpl_quat *d, zpl_quat q0, zpl_quat q1); + + ZPL_DEF void zpl_quat_mulf(zpl_quat *d, zpl_quat q, zpl_f32 s); + ZPL_DEF void zpl_quat_divf(zpl_quat *d, zpl_quat q, zpl_f32 s); + + ZPL_DEF void zpl_quat_addeq(zpl_quat *d, zpl_quat q); + ZPL_DEF void zpl_quat_subeq(zpl_quat *d, zpl_quat q); + ZPL_DEF void zpl_quat_muleq(zpl_quat *d, zpl_quat q); + ZPL_DEF void zpl_quat_diveq(zpl_quat *d, zpl_quat q); + + ZPL_DEF void zpl_quat_muleqf(zpl_quat *d, zpl_f32 s); + ZPL_DEF void zpl_quat_diveqf(zpl_quat *d, zpl_f32 s); + + ZPL_DEF zpl_f32 zpl_quat_dot(zpl_quat q0, zpl_quat q1); + ZPL_DEF zpl_f32 zpl_quat_mag(zpl_quat q); + + ZPL_DEF void zpl_quat_norm(zpl_quat *d, zpl_quat q); + ZPL_DEF void zpl_quat_conj(zpl_quat *d, zpl_quat q); + ZPL_DEF void zpl_quat_inverse(zpl_quat *d, zpl_quat q); + + ZPL_DEF void zpl_quat_axis(zpl_vec3 *axis, zpl_quat q); + ZPL_DEF zpl_f32 zpl_quat_angle(zpl_quat q); + + ZPL_DEF zpl_f32 zpl_quat_pitch(zpl_quat q); + ZPL_DEF zpl_f32 zpl_quat_yaw(zpl_quat q); + ZPL_DEF zpl_f32 zpl_quat_roll(zpl_quat q); + + /* NOTE: Rotate v by q */ + ZPL_DEF void zpl_quat_rotate_vec3(zpl_vec3 *d, zpl_quat q, zpl_vec3 v); + ZPL_DEF void zpl_mat4_from_quat(zpl_mat4 *out, zpl_quat q); + ZPL_DEF void zpl_quat_from_mat4(zpl_quat *out, zpl_mat4 *m); + + /* Plane math. */ + ZPL_DEF zpl_f32 zpl_plane_distance(zpl_plane* p, zpl_vec3 v); + + /* Frustum culling. */ + ZPL_DEF void zpl_frustum_create(zpl_frustum* out, zpl_mat4* camera, zpl_mat4* proj); + ZPL_DEF zpl_b8 zpl_frustum_sphere_inside(zpl_frustum* frustum, zpl_vec3 center, zpl_f32 radius); + ZPL_DEF zpl_b8 zpl_frustum_point_inside(zpl_frustum* frustum, zpl_vec3 point); + ZPL_DEF zpl_b8 zpl_frustum_box_inside(zpl_frustum* frustum, zpl_aabb3 box); + + /* Interpolations */ + ZPL_DEF zpl_f32 zpl_lerp(zpl_f32 a, zpl_f32 b, zpl_f32 t); + ZPL_DEF zpl_f32 zpl_unlerp(zpl_f32 t, zpl_f32 a, zpl_f32 b); + ZPL_DEF zpl_f32 zpl_smooth_step(zpl_f32 a, zpl_f32 b, zpl_f32 t); + ZPL_DEF zpl_f32 zpl_smoother_step(zpl_f32 a, zpl_f32 b, zpl_f32 t); + + ZPL_DEF void zpl_vec2_lerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 b, zpl_f32 t); + ZPL_DEF void zpl_vec3_lerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 b, zpl_f32 t); + ZPL_DEF void zpl_vec4_lerp(zpl_vec4 *d, zpl_vec4 a, zpl_vec4 b, zpl_f32 t); + + ZPL_DEF void zpl_vec2_cslerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 v0, zpl_vec2 b, zpl_vec2 v1, zpl_f32 t); + ZPL_DEF void zpl_vec3_cslerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 v0, zpl_vec3 b, zpl_vec3 v1, zpl_f32 t); + ZPL_DEF void zpl_vec2_dcslerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 v0, zpl_vec2 b, zpl_vec2 v1, zpl_f32 t); + ZPL_DEF void zpl_vec3_dcslerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 v0, zpl_vec3 b, zpl_vec3 v1, zpl_f32 t); + + ZPL_DEF void zpl_quat_lerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t); + ZPL_DEF void zpl_quat_nlerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t); + ZPL_DEF void zpl_quat_slerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t); + ZPL_DEF void zpl_quat_nquad(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t); + ZPL_DEF void zpl_quat_squad(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t); + ZPL_DEF void zpl_quat_slerp_approx(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t); + ZPL_DEF void zpl_quat_squad_approx(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t); + + /* rects */ + ZPL_DEF zpl_rect2 zpl_rect2f(zpl_vec2 pos, zpl_vec2 dim); + ZPL_DEF zpl_rect3 zpl_rect3f(zpl_vec3 pos, zpl_vec3 dim); + + ZPL_DEF zpl_aabb2 zpl_aabb2f(zpl_f32 minx, zpl_f32 miny, zpl_f32 maxx, zpl_f32 maxy); + ZPL_DEF zpl_aabb3 zpl_aabb3f(zpl_f32 minx, zpl_f32 miny, zpl_f32 minz, zpl_f32 maxx, zpl_f32 maxy, zpl_f32 maxz); + + ZPL_DEF zpl_aabb2 zpl_aabb2_rect2(zpl_rect2 a); + ZPL_DEF zpl_aabb3 zpl_aabb3_rect3(zpl_rect3 a); + ZPL_DEF zpl_rect2 zpl_rect2_aabb2(zpl_aabb2 a); + ZPL_DEF zpl_rect3 zpl_rect3_aabb3(zpl_aabb3 a); + + ZPL_DEF int zpl_rect2_contains(zpl_rect2 a, zpl_f32 x, zpl_f32 y); + ZPL_DEF int zpl_rect2_contains_vec2(zpl_rect2 a, zpl_vec2 p); + ZPL_DEF int zpl_rect2_intersects(zpl_rect2 a, zpl_rect2 b); + ZPL_DEF int zpl_rect2_intersection_result(zpl_rect2 a, zpl_rect2 b, zpl_rect2 *intersection); + ZPL_DEF int zpl_aabb2_contains(zpl_aabb2 a, zpl_f32 x, zpl_f32 y); + ZPL_DEF int zpl_aabb3_contains(zpl_aabb3 a, zpl_f32 x, zpl_f32 y, zpl_f32 z); + + /* rectangle partitioning: based on https://halt.software/dead-simple-layouts/ */ + ZPL_DEF zpl_aabb2 zpl_aabb2_cut_left(zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_cut_right(zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_cut_top(zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_cut_bottom(zpl_aabb2 *a, zpl_f32 b); + + ZPL_DEF zpl_aabb2 zpl_aabb2_get_left(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_get_right(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_get_top(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_get_bottom(const zpl_aabb2 *a, zpl_f32 b); + + ZPL_DEF zpl_aabb2 zpl_aabb2_add_left(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_add_right(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_add_top(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_add_bottom(const zpl_aabb2 *a, zpl_f32 b); + + ZPL_DEF zpl_aabb2 zpl_aabb2_contract(const zpl_aabb2 *a, zpl_f32 b); + ZPL_DEF zpl_aabb2 zpl_aabb2_expand(const zpl_aabb2 *a, zpl_f32 b); + + //! @} + ZPL_END_C_DECLS + #if defined(__cplusplus) + ZPL_INLINE bool operator==(zpl_vec2 a, zpl_vec2 b) { return (a.x == b.x) && (a.y == b.y); } + ZPL_INLINE bool operator!=(zpl_vec2 a, zpl_vec2 b) { return !operator==(a, b); } + + ZPL_INLINE zpl_vec2 operator+(zpl_vec2 a) { return a; } + ZPL_INLINE zpl_vec2 operator-(zpl_vec2 a) { zpl_vec2 r = {-a.x, -a.y}; return r; } + + ZPL_INLINE zpl_vec2 operator+(zpl_vec2 a, zpl_vec2 b) { zpl_vec2 r; zpl_vec2_add(&r, a, b); return r; } + ZPL_INLINE zpl_vec2 operator-(zpl_vec2 a, zpl_vec2 b) { zpl_vec2 r; zpl_vec2_sub(&r, a, b); return r; } + + ZPL_INLINE zpl_vec2 operator*(zpl_vec2 a, float scalar) { zpl_vec2 r; zpl_vec2_mul(&r, a, scalar); return r; } + ZPL_INLINE zpl_vec2 operator*(float scalar, zpl_vec2 a) { return operator*(a, scalar); } + + ZPL_INLINE zpl_vec2 operator/(zpl_vec2 a, float scalar) { return operator*(a, 1.0f/scalar); } + + /* Hadamard Product */ + ZPL_INLINE zpl_vec2 operator*(zpl_vec2 a, zpl_vec2 b) { zpl_vec2 r = {a.x*b.x, a.y*b.y}; return r; } + ZPL_INLINE zpl_vec2 operator/(zpl_vec2 a, zpl_vec2 b) { zpl_vec2 r = {a.x/b.x, a.y/b.y}; return r; } + + ZPL_INLINE zpl_vec2 &operator+=(zpl_vec2 &a, zpl_vec2 b) { return (a = a + b); } + ZPL_INLINE zpl_vec2 &operator-=(zpl_vec2 &a, zpl_vec2 b) { return (a = a - b); } + ZPL_INLINE zpl_vec2 &operator*=(zpl_vec2 &a, float scalar) { return (a = a * scalar); } + ZPL_INLINE zpl_vec2 &operator/=(zpl_vec2 &a, float scalar) { return (a = a / scalar); } + + + ZPL_INLINE bool operator==(zpl_vec3 a, zpl_vec3 b) { return (a.x == b.x) && (a.y == b.y) && (a.z == b.z); } + ZPL_INLINE bool operator!=(zpl_vec3 a, zpl_vec3 b) { return !operator==(a, b); } + + ZPL_INLINE zpl_vec3 operator+(zpl_vec3 a) { return a; } + ZPL_INLINE zpl_vec3 operator-(zpl_vec3 a) { zpl_vec3 r = {-a.x, -a.y, -a.z}; return r; } + + ZPL_INLINE zpl_vec3 operator+(zpl_vec3 a, zpl_vec3 b) { zpl_vec3 r; zpl_vec3_add(&r, a, b); return r; } + ZPL_INLINE zpl_vec3 operator-(zpl_vec3 a, zpl_vec3 b) { zpl_vec3 r; zpl_vec3_sub(&r, a, b); return r; } + + ZPL_INLINE zpl_vec3 operator*(zpl_vec3 a, float scalar) { zpl_vec3 r; zpl_vec3_mul(&r, a, scalar); return r; } + ZPL_INLINE zpl_vec3 operator*(float scalar, zpl_vec3 a) { return operator*(a, scalar); } + + ZPL_INLINE zpl_vec3 operator/(zpl_vec3 a, float scalar) { return operator*(a, 1.0f/scalar); } + + /* Hadamard Product */ + ZPL_INLINE zpl_vec3 operator*(zpl_vec3 a, zpl_vec3 b) { zpl_vec3 r = {a.x*b.x, a.y*b.y, a.z*b.z}; return r; } + ZPL_INLINE zpl_vec3 operator/(zpl_vec3 a, zpl_vec3 b) { zpl_vec3 r = {a.x/b.x, a.y/b.y, a.z/b.z}; return r; } + + ZPL_INLINE zpl_vec3 &operator+=(zpl_vec3 &a, zpl_vec3 b) { return (a = a + b); } + ZPL_INLINE zpl_vec3 &operator-=(zpl_vec3 &a, zpl_vec3 b) { return (a = a - b); } + ZPL_INLINE zpl_vec3 &operator*=(zpl_vec3 &a, float scalar) { return (a = a * scalar); } + ZPL_INLINE zpl_vec3 &operator/=(zpl_vec3 &a, float scalar) { return (a = a / scalar); } + + + ZPL_INLINE bool operator==(zpl_vec4 a, zpl_vec4 b) { return (a.x == b.x) && (a.y == b.y) && (a.z == b.z) && (a.w == b.w); } + ZPL_INLINE bool operator!=(zpl_vec4 a, zpl_vec4 b) { return !operator==(a, b); } + + ZPL_INLINE zpl_vec4 operator+(zpl_vec4 a) { return a; } + ZPL_INLINE zpl_vec4 operator-(zpl_vec4 a) { zpl_vec4 r = {-a.x, -a.y, -a.z, -a.w}; return r; } + + ZPL_INLINE zpl_vec4 operator+(zpl_vec4 a, zpl_vec4 b) { zpl_vec4 r; zpl_vec4_add(&r, a, b); return r; } + ZPL_INLINE zpl_vec4 operator-(zpl_vec4 a, zpl_vec4 b) { zpl_vec4 r; zpl_vec4_sub(&r, a, b); return r; } + + ZPL_INLINE zpl_vec4 operator*(zpl_vec4 a, float scalar) { zpl_vec4 r; zpl_vec4_mul(&r, a, scalar); return r; } + ZPL_INLINE zpl_vec4 operator*(float scalar, zpl_vec4 a) { return operator*(a, scalar); } + + ZPL_INLINE zpl_vec4 operator/(zpl_vec4 a, float scalar) { return operator*(a, 1.0f/scalar); } + + /* Hadamard Product */ + ZPL_INLINE zpl_vec4 operator*(zpl_vec4 a, zpl_vec4 b) { zpl_vec4 r = {a.x*b.x, a.y*b.y, a.z*b.z, a.w*b.w}; return r; } + ZPL_INLINE zpl_vec4 operator/(zpl_vec4 a, zpl_vec4 b) { zpl_vec4 r = {a.x/b.x, a.y/b.y, a.z/b.z, a.w/b.w}; return r; } + + ZPL_INLINE zpl_vec4 &operator+=(zpl_vec4 &a, zpl_vec4 b) { return (a = a + b); } + ZPL_INLINE zpl_vec4 &operator-=(zpl_vec4 &a, zpl_vec4 b) { return (a = a - b); } + ZPL_INLINE zpl_vec4 &operator*=(zpl_vec4 &a, float scalar) { return (a = a * scalar); } + ZPL_INLINE zpl_vec4 &operator/=(zpl_vec4 &a, float scalar) { return (a = a / scalar); } + + + ZPL_INLINE zpl_mat2 operator+(zpl_mat2 const &a, zpl_mat2 const &b) { + int i, j; + zpl_mat2 r = {0}; + for (j = 0; j < 2; j++) { + for (i = 0; i < 2; i++) + r.e[2*j+i] = a.e[2*j+i] + b.e[2*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat2 operator-(zpl_mat2 const &a, zpl_mat2 const &b) { + int i, j; + zpl_mat2 r = {0}; + for (j = 0; j < 2; j++) { + for (i = 0; i < 2; i++) + r.e[2*j+i] = a.e[2*j+i] - b.e[2*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat2 operator*(zpl_mat2 const &a, zpl_mat2 const &b) { zpl_mat2 r; zpl_mat2_mul(&r, (zpl_mat2 *)&a, (zpl_mat2 *)&b); return r; } + ZPL_INLINE zpl_vec2 operator*(zpl_mat2 const &a, zpl_vec2 v) { zpl_vec2 r; zpl_mat2_mul_vec2(&r, (zpl_mat2 *)&a, v); return r; } + ZPL_INLINE zpl_mat2 operator*(zpl_mat2 const &a, float scalar) { + zpl_mat2 r = {0}; + int i; + for (i = 0; i < 2*2; i++) r.e[i] = a.e[i] * scalar; + return r; + } + ZPL_INLINE zpl_mat2 operator*(float scalar, zpl_mat2 const &a) { return operator*(a, scalar); } + ZPL_INLINE zpl_mat2 operator/(zpl_mat2 const &a, float scalar) { return operator*(a, 1.0f/scalar); } + + ZPL_INLINE zpl_mat2& operator+=(zpl_mat2& a, zpl_mat2 const &b) { return (a = a + b); } + ZPL_INLINE zpl_mat2& operator-=(zpl_mat2& a, zpl_mat2 const &b) { return (a = a - b); } + ZPL_INLINE zpl_mat2& operator*=(zpl_mat2& a, zpl_mat2 const &b) { return (a = a * b); } + + + + ZPL_INLINE zpl_mat3 operator+(zpl_mat3 const &a, zpl_mat3 const &b) { + int i, j; + zpl_mat3 r = {0}; + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) + r.e[3*j+i] = a.e[3*j+i] + b.e[3*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat3 operator-(zpl_mat3 const &a, zpl_mat3 const &b) { + int i, j; + zpl_mat3 r = {0}; + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) + r.e[3*j+i] = a.e[3*j+i] - b.e[3*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat3 operator*(zpl_mat3 const &a, zpl_mat3 const &b) { zpl_mat3 r; zpl_mat3_mul(&r, (zpl_mat3 *)&a, (zpl_mat3 *)&b); return r; } + ZPL_INLINE zpl_vec3 operator*(zpl_mat3 const &a, zpl_vec3 v) { zpl_vec3 r; zpl_mat3_mul_vec3(&r, (zpl_mat3 *)&a, v); return r; } + ZPL_INLINE zpl_mat3 operator*(zpl_mat3 const &a, float scalar) { + zpl_mat3 r = {0}; + int i; + for (i = 0; i < 3*3; i++) r.e[i] = a.e[i] * scalar; + return r; + } + ZPL_INLINE zpl_mat3 operator*(float scalar, zpl_mat3 const &a) { return operator*(a, scalar); } + ZPL_INLINE zpl_mat3 operator/(zpl_mat3 const &a, float scalar) { return operator*(a, 1.0f/scalar); } + + ZPL_INLINE zpl_mat3& operator+=(zpl_mat3& a, zpl_mat3 const &b) { return (a = a + b); } + ZPL_INLINE zpl_mat3& operator-=(zpl_mat3& a, zpl_mat3 const &b) { return (a = a - b); } + ZPL_INLINE zpl_mat3& operator*=(zpl_mat3& a, zpl_mat3 const &b) { return (a = a * b); } + + + + ZPL_INLINE zpl_mat4 operator+(zpl_mat4 const &a, zpl_mat4 const &b) { + int i, j; + zpl_mat4 r = {0}; + for (j = 0; j < 4; j++) { + for (i = 0; i < 4; i++) + r.e[4*j+i] = a.e[4*j+i] + b.e[4*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat4 operator-(zpl_mat4 const &a, zpl_mat4 const &b) { + int i, j; + zpl_mat4 r = {0}; + for (j = 0; j < 4; j++) { + for (i = 0; i < 4; i++) + r.e[4*j+i] = a.e[4*j+i] - b.e[4*j+i]; + } + return r; + } + + ZPL_INLINE zpl_mat4 operator*(zpl_mat4 const &a, zpl_mat4 const &b) { zpl_mat4 r; zpl_mat4_mul(&r, (zpl_mat4 *)&a, (zpl_mat4 *)&b); return r; } + ZPL_INLINE zpl_vec4 operator*(zpl_mat4 const &a, zpl_vec4 v) { zpl_vec4 r; zpl_mat4_mul_vec4(&r, (zpl_mat4 *)&a, v); return r; } + ZPL_INLINE zpl_mat4 operator*(zpl_mat4 const &a, float scalar) { + zpl_mat4 r = {0}; + int i; + for (i = 0; i < 4*4; i++) r.e[i] = a.e[i] * scalar; + return r; + } + ZPL_INLINE zpl_mat4 operator*(float scalar, zpl_mat4 const &a) { return operator*(a, scalar); } + ZPL_INLINE zpl_mat4 operator/(zpl_mat4 const &a, float scalar) { return operator*(a, 1.0f/scalar); } + + ZPL_INLINE zpl_mat4& operator+=(zpl_mat4 &a, zpl_mat4 const &b) { return (a = a + b); } + ZPL_INLINE zpl_mat4& operator-=(zpl_mat4 &a, zpl_mat4 const &b) { return (a = a - b); } + ZPL_INLINE zpl_mat4& operator*=(zpl_mat4 &a, zpl_mat4 const &b) { return (a = a * b); } + + + + ZPL_INLINE bool operator==(zpl_quat a, zpl_quat b) { return a.xyzw == b.xyzw; } + ZPL_INLINE bool operator!=(zpl_quat a, zpl_quat b) { return !operator==(a, b); } + + ZPL_INLINE zpl_quat operator+(zpl_quat q) { return q; } + ZPL_INLINE zpl_quat operator-(zpl_quat q) { return zpl_quatf(-q.x, -q.y, -q.z, -q.w); } + + ZPL_INLINE zpl_quat operator+(zpl_quat a, zpl_quat b) { zpl_quat r; zpl_quat_add(&r, a, b); return r; } + ZPL_INLINE zpl_quat operator-(zpl_quat a, zpl_quat b) { zpl_quat r; zpl_quat_sub(&r, a, b); return r; } + + ZPL_INLINE zpl_quat operator*(zpl_quat a, zpl_quat b) { zpl_quat r; zpl_quat_mul(&r, a, b); return r; } + ZPL_INLINE zpl_quat operator*(zpl_quat q, float s) { zpl_quat r; zpl_quat_mulf(&r, q, s); return r; } + ZPL_INLINE zpl_quat operator*(float s, zpl_quat q) { return operator*(q, s); } + ZPL_INLINE zpl_quat operator/(zpl_quat q, float s) { zpl_quat r; zpl_quat_divf(&r, q, s); return r; } + + ZPL_INLINE zpl_quat &operator+=(zpl_quat &a, zpl_quat b) { zpl_quat_addeq(&a, b); return a; } + ZPL_INLINE zpl_quat &operator-=(zpl_quat &a, zpl_quat b) { zpl_quat_subeq(&a, b); return a; } + ZPL_INLINE zpl_quat &operator*=(zpl_quat &a, zpl_quat b) { zpl_quat_muleq(&a, b); return a; } + ZPL_INLINE zpl_quat &operator/=(zpl_quat &a, zpl_quat b) { zpl_quat_diveq(&a, b); return a; } + + ZPL_INLINE zpl_quat &operator*=(zpl_quat &a, float b) { zpl_quat_muleqf(&a, b); return a; } + ZPL_INLINE zpl_quat &operator/=(zpl_quat &a, float b) { zpl_quat_diveqf(&a, b); return a; } + + /* Rotate v by a */ + ZPL_INLINE zpl_vec3 operator*(zpl_quat q, zpl_vec3 v) { zpl_vec3 r; zpl_quat_rotate_vec3(&r, q, v); return r; } + #endif +#endif + +#if defined(ZPL_MODULE_PARSER) + // file: header/adt.h + + ZPL_BEGIN_C_DECLS + + typedef enum zpl_adt_type { + ZPL_ADT_TYPE_UNINITIALISED, /* node was not initialised, this is a programming error! */ + ZPL_ADT_TYPE_ARRAY, + ZPL_ADT_TYPE_OBJECT, + ZPL_ADT_TYPE_STRING, + ZPL_ADT_TYPE_MULTISTRING, + ZPL_ADT_TYPE_INTEGER, + ZPL_ADT_TYPE_REAL, + } zpl_adt_type; + + typedef enum zpl_adt_props { + ZPL_ADT_PROPS_NONE, + ZPL_ADT_PROPS_NAN, + ZPL_ADT_PROPS_NAN_NEG, + ZPL_ADT_PROPS_INFINITY, + ZPL_ADT_PROPS_INFINITY_NEG, + ZPL_ADT_PROPS_FALSE, + ZPL_ADT_PROPS_TRUE, + ZPL_ADT_PROPS_NULL, + ZPL_ADT_PROPS_IS_EXP, + ZPL_ADT_PROPS_IS_HEX, + + // Used internally so that people can fill in real numbers they plan to write. + ZPL_ADT_PROPS_IS_PARSED_REAL, + } zpl_adt_props; + + typedef enum zpl_adt_naming_style { + ZPL_ADT_NAME_STYLE_DOUBLE_QUOTE, + ZPL_ADT_NAME_STYLE_SINGLE_QUOTE, + ZPL_ADT_NAME_STYLE_NO_QUOTES, + } zpl_adt_naming_style; + + typedef enum zpl_adt_assign_style { + ZPL_ADT_ASSIGN_STYLE_COLON, + ZPL_ADT_ASSIGN_STYLE_EQUALS, + ZPL_ADT_ASSIGN_STYLE_LINE, + } zpl_adt_assign_style; + + typedef enum zpl_adt_delim_style { + ZPL_ADT_DELIM_STYLE_COMMA, + ZPL_ADT_DELIM_STYLE_LINE, + ZPL_ADT_DELIM_STYLE_NEWLINE, + } zpl_adt_delim_style; + + typedef enum zpl_adt_error { + ZPL_ADT_ERROR_NONE, + ZPL_ADT_ERROR_INTERNAL, + ZPL_ADT_ERROR_ALREADY_CONVERTED, + ZPL_ADT_ERROR_INVALID_TYPE, + ZPL_ADT_ERROR_OUT_OF_MEMORY, + } zpl_adt_error; + + typedef struct zpl_adt_node { + char const *name; + struct zpl_adt_node *parent; + + /* properties */ + zpl_u8 type :4; + zpl_u8 props :4; + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + zpl_u8 cfg_mode :1; + zpl_u8 name_style :2; + zpl_u8 assign_style:2; + zpl_u8 delim_style :2; + zpl_u8 delim_line_width :4; + zpl_u8 assign_line_width:4; + #endif + + /* adt data */ + union { + char const *string; + struct zpl_adt_node *nodes; ///< zpl_array + struct { + union { + zpl_f64 real; + zpl_i64 integer; + }; + + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + /* number analysis */ + zpl_i32 base; + zpl_i32 base2; + zpl_u8 base2_offset:4; + zpl_i8 exp :4; + zpl_u8 neg_zero :1; + zpl_u8 lead_digit:1; + #endif + }; + }; + } zpl_adt_node; + + /* ADT NODE LIMITS + * delimiter and assignment segment width is limited to 128 whitespace symbols each. + * real number limits decimal position to 128 places. + * real number exponent is limited to 64 digits. + */ + + /** + * @brief Initialise an ADT object or array + * + * @param node + * @param backing Memory allocator used for descendants + * @param name Node's name + * @param is_array + * @return error code + */ + ZPL_DEF zpl_u8 zpl_adt_make_branch(zpl_adt_node *node, zpl_allocator backing, char const *name, zpl_b32 is_array); + + /** + * @brief Destroy an ADT branch and its descendants + * + * @param node + * @return error code + */ + ZPL_DEF zpl_u8 zpl_adt_destroy_branch(zpl_adt_node *node); + + /** + * @brief Initialise an ADT leaf + * + * @param node + * @param name Node's name + * @param type Node's type (use zpl_adt_make_branch for container nodes) + * @return error code + */ + ZPL_DEF zpl_u8 zpl_adt_make_leaf(zpl_adt_node *node, char const *name, zpl_u8 type); + + + /** + * @brief Fetch a node using provided URI string. + * + * This method uses a basic syntax to fetch a node from the ADT. The following features are available + * to retrieve the data: + * + * - "a/b/c" navigates through objects "a" and "b" to get to "c" + * - "arr/[foo=123]/bar" iterates over "arr" to find any object with param "foo" that matches the value "123", then gets its field called "bar" + * - "arr/3" retrieves the 4th element in "arr" + * - "arr/[apple]" retrieves the first element of value "apple" in "arr" + * + * @param node ADT node + * @param uri Locator string as described above + * @return zpl_adt_node* + * + * @see code/apps/examples/json_get.c + */ + ZPL_DEF zpl_adt_node *zpl_adt_query(zpl_adt_node *node, char const *uri); + + /** + * @brief Find a field node within an object by the given name. + * + * @param node + * @param name + * @param deep_search Perform search recursively + * @return zpl_adt_node * node + */ + ZPL_DEF zpl_adt_node *zpl_adt_find(zpl_adt_node *node, char const *name, zpl_b32 deep_search); + + /** + * @brief Allocate an unitialised node within a container at a specified index. + * + * @param parent + * @param index + * @return zpl_adt_node * node + */ + ZPL_DEF zpl_adt_node *zpl_adt_alloc_at(zpl_adt_node *parent, zpl_isize index); + + /** + * @brief Allocate an unitialised node within a container. + * + * @param parent + * @return zpl_adt_node * node + */ + ZPL_DEF zpl_adt_node *zpl_adt_alloc(zpl_adt_node *parent); + + /** + * @brief Move an existing node to a new container at a specified index. + * + * @param node + * @param new_parent + * @param index + * @return zpl_adt_node * node + */ + ZPL_DEF zpl_adt_node *zpl_adt_move_node_at(zpl_adt_node *node, zpl_adt_node *new_parent, zpl_isize index); + + /** + * @brief Move an existing node to a new container. + * + * @param node + * @param new_parent + * @return zpl_adt_node * node + */ + ZPL_DEF zpl_adt_node *zpl_adt_move_node(zpl_adt_node *node, zpl_adt_node *new_parent); + + /** + * @brief Swap two nodes. + * + * @param node + * @param other_node + * @return + */ + ZPL_DEF void zpl_adt_swap_nodes(zpl_adt_node *node, zpl_adt_node *other_node); + + /** + * @brief Remove node from container. + * + * @param node + * @return + */ + ZPL_DEF void zpl_adt_remove_node(zpl_adt_node *node); + + /** + * @brief Initialise a node as an object + * + * @param obj + * @param name + * @param backing + * @return + */ + ZPL_DEF zpl_b8 zpl_adt_set_obj(zpl_adt_node *obj, char const *name, zpl_allocator backing); + + /** + * @brief Initialise a node as an array + * + * @param obj + * @param name + * @param backing + * @return + */ + ZPL_DEF zpl_b8 zpl_adt_set_arr(zpl_adt_node *obj, char const *name, zpl_allocator backing); + + /** + * @brief Initialise a node as a string + * + * @param obj + * @param name + * @param value + * @return + */ + ZPL_DEF zpl_b8 zpl_adt_set_str(zpl_adt_node *obj, char const *name, char const *value); + + /** + * @brief Initialise a node as a float + * + * @param obj + * @param name + * @param value + * @return + */ + ZPL_DEF zpl_b8 zpl_adt_set_flt(zpl_adt_node *obj, char const *name, zpl_f64 value); + + /** + * @brief Initialise a node as a signed integer + * + * @param obj + * @param name + * @param value + * @return + */ + ZPL_DEF zpl_b8 zpl_adt_set_int(zpl_adt_node *obj, char const *name, zpl_i64 value); + + /** + * @brief Append a new node to a container as an object + * + * @param parent + * @param name + * @return* + */ + ZPL_DEF zpl_adt_node *zpl_adt_append_obj(zpl_adt_node *parent, char const *name); + + /** + * @brief Append a new node to a container as an array + * + * @param parent + * @param name + * @return* + */ + ZPL_DEF zpl_adt_node *zpl_adt_append_arr(zpl_adt_node *parent, char const *name); + + /** + * @brief Append a new node to a container as a string + * + * @param parent + * @param name + * @param value + * @return* + */ + ZPL_DEF zpl_adt_node *zpl_adt_append_str(zpl_adt_node *parent, char const *name, char const *value); + + /** + * @brief Append a new node to a container as a float + * + * @param parent + * @param name + * @param value + * @return* + */ + ZPL_DEF zpl_adt_node *zpl_adt_append_flt(zpl_adt_node *parent, char const *name, zpl_f64 value); + + /** + * @brief Append a new node to a container as a signed integer + * + * @param parent + * @param name + * @param value + * @return* + */ + ZPL_DEF zpl_adt_node *zpl_adt_append_int(zpl_adt_node *parent, char const *name, zpl_i64 value); + + /* parser helpers */ + + /** + * @brief Parses a text and stores the result into an unitialised node. + * + * @param node + * @param base + * @return* + */ + ZPL_DEF char *zpl_adt_parse_number(zpl_adt_node *node, char* base); + + /** + * @brief Parses a text and stores the result into an unitialised node. + * This function expects the entire input to be a number. + * + * @param node + * @param base + * @return* + */ + ZPL_DEF char *zpl_adt_parse_number_strict(zpl_adt_node *node, char* base_str); + + /** + * @brief Parses and converts an existing string node into a number. + * + * @param node + * @return + */ + ZPL_DEF zpl_adt_error zpl_adt_str_to_number(zpl_adt_node *node); + + /** + * @brief Parses and converts an existing string node into a number. + * This function expects the entire input to be a number. + * + * @param node + * @return + */ + ZPL_DEF zpl_adt_error zpl_adt_str_to_number_strict(zpl_adt_node *node); + + /** + * @brief Prints a number into a file stream. + * + * The provided file handle can also be a memory mapped stream. + * + * @see zpl_file_stream_new + * @param file + * @param node + * @return + */ + ZPL_DEF zpl_adt_error zpl_adt_print_number(zpl_file *file, zpl_adt_node *node); + + /** + * @brief Prints a string into a file stream. + * + * The provided file handle can also be a memory mapped stream. + * + * @see zpl_file_stream_new + * @param file + * @param node + * @param escaped_chars + * @param escape_symbol + * @return + */ + ZPL_DEF zpl_adt_error zpl_adt_print_string(zpl_file *file, zpl_adt_node *node, char const *escaped_chars, char const *escape_symbol); + + /* extensions */ + + #if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L + #define zpl_adt_append(parent, name, value) _Generic((value), \ + char*: zpl_adt_append_str, \ + char const*: zpl_adt_append_str, \ + zpl_f64: zpl_adt_append_flt, \ + default: zpl_adt_append_int)(parent, name, value) + #define zpl_adt_set(obj, name, value) _Generic((value), \ + char*: zpl_adt_set_str, \ + char const*: zpl_adt_set_str, \ + zpl_f64: zpl_adt_set_flt, \ + default: zpl_adt_set_int)(obj, name, value) + #endif + + /* deprecated */ + + ZPL_DEPRECATED_FOR(18.0.0, zpl_adt_query) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_get(zpl_adt_node *node, char const *uri) { + return zpl_adt_query(node, uri); + } + + ZPL_DEPRECATED_FOR(13.3.0, zpl_adt_str_to_number) + ZPL_IMPL_INLINE void zpl_adt_str_to_flt(zpl_adt_node *node) { + (void)zpl_adt_str_to_number(node); + } + + ZPL_DEPRECATED_FOR(17.0.0, zpl_adt_append_obj) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_inset_obj(zpl_adt_node *parent, char const *name) { + return zpl_adt_append_obj(parent, name); + } + + ZPL_DEPRECATED_FOR(17.0.0, zpl_adt_append_arr) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_inset_arr(zpl_adt_node *parent, char const *name) { + return zpl_adt_append_arr(parent, name); + } + + ZPL_DEPRECATED_FOR(17.0.0, zpl_adt_append_str) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_inset_str(zpl_adt_node *parent, char const *name, char const *value) { + return zpl_adt_append_str(parent, name, value); + } + + ZPL_DEPRECATED_FOR(17.0.0, zpl_adt_append_flt) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_inset_flt(zpl_adt_node *parent, char const *name, zpl_f64 value) { + return zpl_adt_append_flt(parent, name, value); + } + + ZPL_DEPRECATED_FOR(17.0.0, zpl_adt_append_int) + ZPL_IMPL_INLINE zpl_adt_node *zpl_adt_inset_int(zpl_adt_node *parent, char const *name, zpl_i64 value) { + return zpl_adt_append_int(parent, name, value); + } + + ZPL_END_C_DECLS + + /* parsers */ + // file: header/parsers/json.h + + + ZPL_BEGIN_C_DECLS + + typedef enum zpl_json_error { + ZPL_JSON_ERROR_NONE, + ZPL_JSON_ERROR_INTERNAL, + ZPL_JSON_ERROR_INVALID_NAME, + ZPL_JSON_ERROR_INVALID_VALUE, + ZPL_JSON_ERROR_INVALID_ASSIGNMENT, + ZPL_JSON_ERROR_UNKNOWN_KEYWORD, + ZPL_JSON_ERROR_ARRAY_LEFT_OPEN, + ZPL_JSON_ERROR_OBJECT_END_PAIR_MISMATCHED, + ZPL_JSON_ERROR_OUT_OF_MEMORY, + } zpl_json_error; + + typedef enum zpl_json_indent_style { + ZPL_JSON_INDENT_STYLE_COMPACT = -1000, + } zpl_json_indent_style; + + typedef zpl_adt_node zpl_json_object; + + ZPL_DEF zpl_u8 zpl_json_parse(zpl_json_object *root, char *text, zpl_allocator allocator); + ZPL_DEF void zpl_json_free(zpl_json_object *obj); + ZPL_DEF zpl_b8 zpl_json_write(zpl_file *file, zpl_json_object *obj, zpl_isize indent); + ZPL_DEF zpl_string zpl_json_write_string(zpl_allocator a, zpl_json_object *obj, zpl_isize indent); + + ZPL_END_C_DECLS + // file: header/parsers/csv.h + + + ZPL_BEGIN_C_DECLS + + typedef enum zpl_csv_error { + ZPL_CSV_ERROR_NONE, + ZPL_CSV_ERROR_INTERNAL, + ZPL_CSV_ERROR_UNEXPECTED_END_OF_INPUT, + ZPL_CSV_ERROR_MISMATCHED_ROWS, + } zpl_csv_error; + + typedef zpl_adt_node zpl_csv_object; + + ZPL_DEF_INLINE zpl_u8 zpl_csv_parse(zpl_csv_object *root, char *text, zpl_allocator allocator, zpl_b32 has_header); + ZPL_DEF zpl_u8 zpl_csv_parse_delimiter(zpl_csv_object *root, char *text, zpl_allocator allocator, zpl_b32 has_header, char delim); + ZPL_DEF void zpl_csv_free(zpl_csv_object *obj); + + ZPL_DEF_INLINE void zpl_csv_write(zpl_file *file, zpl_csv_object *obj); + ZPL_DEF_INLINE zpl_string zpl_csv_write_string(zpl_allocator a, zpl_csv_object *obj); + ZPL_DEF void zpl_csv_write_delimiter(zpl_file *file, zpl_csv_object *obj, char delim); + ZPL_DEF zpl_string zpl_csv_write_string_delimiter(zpl_allocator a, zpl_csv_object *obj, char delim); + + /* inline */ + + ZPL_IMPL_INLINE zpl_u8 zpl_csv_parse(zpl_csv_object *root, char *text, zpl_allocator allocator, zpl_b32 has_header) { + return zpl_csv_parse_delimiter(root, text, allocator, has_header, ','); + } + + ZPL_IMPL_INLINE void zpl_csv_write(zpl_file *file, zpl_csv_object *obj) { + zpl_csv_write_delimiter(file, obj, ','); + } + + ZPL_IMPL_INLINE zpl_string zpl_csv_write_string(zpl_allocator a, zpl_csv_object *obj) { + return zpl_csv_write_string_delimiter(a, obj, ','); + } + + + ZPL_END_C_DECLS + // file: header/parsers/uri.h + + + ZPL_BEGIN_C_DECLS + + typedef zpl_adt_node zpl_uri_object; + + typedef enum zpl_uri_error { + ZPL_URI_ERROR_NONE, + ZPL_URI_ERROR_INTERNAL, + } zpl_uri_error; + + ZPL_DEF zpl_u8 zpl_uri_init(zpl_adt_node *root, char *origin, zpl_allocator a); + ZPL_DEF zpl_u8 zpl_uri_parse(zpl_adt_node *root, char *text, zpl_allocator a); + ZPL_DEF void zpl_uri_write(zpl_file *f, zpl_adt_node *obj); + ZPL_DEF zpl_string zpl_uri_write_string(zpl_allocator a, zpl_adt_node *obj); + ZPL_DEF void zpl_uri_free(zpl_adt_node *obj); + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_SOCKET) + // file: header/socket.h + + ZPL_BEGIN_C_DECLS + + typedef int zpl_socket; + + typedef struct zpl_socket_addr { + char data[128]; + } zpl_socket_addr; + + typedef enum zpl_socket_protocol { + ZPL_SOCKET_TCP = 0, + ZPL_SOCKET_UDP = 1, + } zpl_socket_protocol; + + typedef enum zpl_socket_mode { + ZPL_SOCKET_BIND = 0, + ZPL_SOCKET_CONNECT = 1, + } zpl_socket_mode; + + typedef enum zpl_socket_flags { + ZPL_SOCKET_DEFAULT = 0, + ZPL_SOCKET_NON_BLOCKING = 0x01, + ZPL_SOCKET_NO_DELAY = 0x02, + } zpl_socket_flags; + + /** + * Initializes socket functionality + * @return 0 on success + */ + ZPL_DEF zpl_i32 zpl_socket_init(void); + + /** + * Creates a new socket configured according to the given parameters + * @param protocol Protocol of the socket, either ZPL_SOCKET_TCP or ZPL_SOCKET_UDP for TCP or UDP respectively + * @param mode Mode of the socket (bind or connect), either ZPL_SOCKET_BIND or ZPL_SOCKET_CONNECT + * @param flags Configuration flags, either ZPL_SOCKET_DEFAULT or a bitwise combination of flags + * @param host Host/address as a string, can be IPv4, IPv6, etc... + * @param service Service/port as a string, e.g. "1728" or "http" + * @return socket handle, or -1 on failure + */ + ZPL_DEF zpl_socket zpl_socket_create(zpl_i32 protocol, zpl_i32 mode, char flags, const char *host, const char *service); + + /** + * Closes the given socket + * @param socket Socket handle + */ + ZPL_DEF void zpl_socket_close(zpl_socket socket); + + /** + * Terminates socket functionality + */ + ZPL_DEF void zpl_socket_terminate(void); + + /** + * Configures the given socket (must be ZPL_SOCKET_TCP + ZPL_SOCKET_BIND) to listen for new connections with the given backlog + * @param socket Socket handle + * @param backlog Size of the backlog + * @return 0 on success, non-zero on failure + */ + ZPL_DEF zpl_i32 zpl_socket_listen(zpl_socket socket, zpl_i32 backlog); + + /** + * Uses the given socket (must be zpl_socket_listen) to accept a new incoming connection, optionally returning its address + * @param socket Socket handle + * @param addr Pointer to zpl_socket_addr to store the address + * @return a socket handle for the new connection, or -1 on failure (e.g. if there are no new connections) + */ + ZPL_DEF zpl_socket zpl_socket_accept(zpl_socket socket, zpl_socket_addr *addr); + + /** + * Writes the address the given socket is bound to into the given address pointer, useful when automatically assigning a port + * @param socket Socket handle + * @param addr Pointer to zpl_socket_addr to store the address + * @return 0 on success, non-zero on failure + */ + ZPL_DEF zpl_i32 zpl_socket_get_address(zpl_socket socket, zpl_socket_addr *addr); + + /** + * Writes the host/address and service/port of the given address into given buffers (pointer + size), one buffer may be NULL + * @param addr Pointer to zpl_socket_addr containing the address + * @param host Buffer to store the host/address string + * @param host_size Size of the host buffer + * @param service Buffer to store the service/port string + * @param service_size Size of the service buffer + * @return 0 on success, non-zero on failure + */ + ZPL_DEF zpl_i32 zpl_socket_get_address_info(zpl_socket_addr *addr, char *host, zpl_i32 host_size, char *service, zpl_i32 service_size); + + /** + * Uses the given socket (either ZPL_SOCKET_CONNECT or returned by zpl_socket_accept) to send the given data + * @param socket Socket handle + * @param data Pointer to the data to be sent + * @param size Size of the data + * @return how much data was actually sent (may be less than data size), or -1 on failure + */ + ZPL_DEF zpl_i32 zpl_socket_send(zpl_socket socket, const char *data, zpl_i32 size); + + /** + * Receives data using the given socket (either ZPL_SOCKET_CONNECT or returned by zpl_socket_accept) into the given buffer + * @param socket Socket handle + * @param buffer Pointer to the buffer to receive the data + * @param size Size of the buffer + * @return the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive) + */ + ZPL_DEF zpl_i32 zpl_socket_receive(zpl_socket socket, char *buffer, zpl_i32 size); + + /** + * Uses the given socket to send the given data to the given zpl_socket_addr + * @param socket Socket handle + * @param addr Pointer to zpl_socket_addr containing the address to send the data to + * @param data Pointer to the data to be sent + * @param size Size of the data + * @return how much data was actually sent (may be less than data size), or -1 on failure + */ + ZPL_DEF zpl_i32 zpl_socket_send_to(zpl_socket socket, zpl_socket_addr *addr, const char *data, zpl_i32 size); + + /** + * Receives data using the given socket into the given buffer, optionally returning the sender's address + * @param socket Socket handle + * @param addr Pointer to zpl_socket_addr to store the sender's address + * @param buffer Pointer to the buffer to receive the data + * @param size Size of the buffer + * @return the number of bytes received, 0 on orderly shutdown, or -1 on failure (e.g. no data to receive) + */ + ZPL_DEF zpl_i32 zpl_socket_receive_from(zpl_socket socket, zpl_socket_addr *addr, char *buffer, zpl_i32 size); + + /** + * Waits either until the given socket has new data to receive or the given time (in seconds) has passed + * @param socket Socket handle + * @param time Time to wait in seconds + * @return 1 if new data is available, 0 if timeout was reached, and -1 on error + */ + ZPL_DEF zpl_i32 zpl_socket_select(zpl_socket socket, zpl_f64 time); + + /** + * Waits either until a socket in the given list has new data to receive or the given time (in seconds) has passed + * @param sockets Array of socket handles + * @param count Number of sockets in the array + * @param time Time to wait in seconds + * @return 1 or more if new data is available, 0 if timeout was reached, and -1 on error + */ + ZPL_DEF zpl_i32 zpl_socket_multi_select(zpl_socket *sockets, zpl_i32 count, zpl_f64 time); + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_THREADING) +# if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_MACOS) +# include +# endif + +# if !defined(zpl_thread_local) +# if defined(_MSC_VER) && _MSC_VER >= 1300 +# define zpl_thread_local __declspec(thread) +# elif defined(__GNUC__) +# define zpl_thread_local __thread +# elif defined(__TINYC__) +# define zpl_thread_local +# else +# define zpl_thread_local thread_local +# endif +# endif + + // file: header/threading/atomic.h + + // Atomics + + // TODO: Be specific with memory order? + // e.g. relaxed, acquire, release, acquire_release + + #if !defined(__STDC_NO_ATOMICS__) && !defined(__cplusplus) && !defined(ZPL_COMPILER_MSVC) && !defined(ZPL_COMPILER_TINYC) + # define zpl_atomic(X) volatile _Atomic(X) + #else + // TODO: Fix once C++ guys bother to add C atomics to std. + //# include + # define zpl_atomic(X) volatile X /*std::atomic*/ + #endif + + #if defined(__STDC_NO_ATOMICS__) || defined(__cplusplus) || defined(ZPL_COMPILER_MSVC) + #define zpl_atomicarg(X) volatile X + #else + #define zpl_atomicarg(X) X + #endif + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_COMPILER_MSVC) + typedef struct zpl_atomic32 { zpl_atomic(zpl_i32) value; } zpl_atomic32; + typedef struct zpl_atomic64 { zpl_atomic(zpl_i64) value; } zpl_atomic64; + typedef struct zpl_atomic_ptr { zpl_atomic(void*) value; } zpl_atomic_ptr; + #else + # if defined(ZPL_ARCH_32_BIT) + # define ZPL_ATOMIC_PTR_ALIGNMENT 4 + # elif defined(ZPL_ARCH_64_BIT) + # define ZPL_ATOMIC_PTR_ALIGNMENT 8 + # else + # error Unknown architecture + # endif + + typedef struct zpl_atomic32 { zpl_atomic(zpl_i32) value; } __attribute__ ((aligned(4))) zpl_atomic32; + typedef struct zpl_atomic64 { zpl_atomic(zpl_i64) value; } __attribute__ ((aligned(8))) zpl_atomic64; + typedef struct zpl_atomic_ptr { zpl_atomic(void*) value; } __attribute__ ((aligned(ZPL_ATOMIC_PTR_ALIGNMENT))) zpl_atomic_ptr; + #endif + + ZPL_DEF zpl_i32 zpl_atomic32_load (zpl_atomic32 const *a); + ZPL_DEF void zpl_atomic32_store (zpl_atomic32 *a, zpl_atomicarg(zpl_i32) value); + ZPL_DEF zpl_i32 zpl_atomic32_compare_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) expected, zpl_atomicarg(zpl_i32) desired); + ZPL_DEF zpl_i32 zpl_atomic32_exchange (zpl_atomic32 *a, zpl_atomicarg(zpl_i32) desired); + ZPL_DEF zpl_i32 zpl_atomic32_fetch_add (zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand); + ZPL_DEF zpl_i32 zpl_atomic32_fetch_and (zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand); + ZPL_DEF zpl_i32 zpl_atomic32_fetch_or (zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand); + ZPL_DEF zpl_b32 zpl_atomic32_spin_lock (zpl_atomic32 *a, zpl_isize time_out); // NOTE: time_out = -1 as default + ZPL_DEF void zpl_atomic32_spin_unlock (zpl_atomic32 *a); + ZPL_DEF zpl_b32 zpl_atomic32_try_acquire_lock(zpl_atomic32 *a); + + + ZPL_DEF zpl_i64 zpl_atomic64_load (zpl_atomic64 const *a); + ZPL_DEF void zpl_atomic64_store (zpl_atomic64 *a, zpl_atomicarg(zpl_i64) value); + ZPL_DEF zpl_i64 zpl_atomic64_compare_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) expected, zpl_atomicarg(zpl_i64) desired); + ZPL_DEF zpl_i64 zpl_atomic64_exchange (zpl_atomic64 *a, zpl_atomicarg(zpl_i64) desired); + ZPL_DEF zpl_i64 zpl_atomic64_fetch_add (zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand); + ZPL_DEF zpl_i64 zpl_atomic64_fetch_and (zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand); + ZPL_DEF zpl_i64 zpl_atomic64_fetch_or (zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand); + ZPL_DEF zpl_b32 zpl_atomic64_spin_lock (zpl_atomic64 *a, zpl_isize time_out); // NOTE: time_out = -1 as default + ZPL_DEF void zpl_atomic64_spin_unlock (zpl_atomic64 *a); + ZPL_DEF zpl_b32 zpl_atomic64_try_acquire_lock(zpl_atomic64 *a); + + + ZPL_DEF void *zpl_atomic_ptr_load (zpl_atomic_ptr const *a); + ZPL_DEF void zpl_atomic_ptr_store (zpl_atomic_ptr *a, zpl_atomicarg(void *)value); + ZPL_DEF void *zpl_atomic_ptr_compare_exchange(zpl_atomic_ptr *a, zpl_atomicarg(void *)expected, zpl_atomicarg(void *)desired); + ZPL_DEF void *zpl_atomic_ptr_exchange (zpl_atomic_ptr *a, zpl_atomicarg(void *)desired); + ZPL_DEF void *zpl_atomic_ptr_fetch_add (zpl_atomic_ptr *a, zpl_atomicarg(void *)operand); + ZPL_DEF void *zpl_atomic_ptr_fetch_and (zpl_atomic_ptr *a, zpl_atomicarg(void *)operand); + ZPL_DEF void *zpl_atomic_ptr_fetch_or (zpl_atomic_ptr *a, zpl_atomicarg(void *)operand); + ZPL_DEF zpl_b32 zpl_atomic_ptr_spin_lock (zpl_atomic_ptr *a, zpl_isize time_out); // NOTE: time_out = -1 as default + ZPL_DEF void zpl_atomic_ptr_spin_unlock (zpl_atomic_ptr *a); + ZPL_DEF zpl_b32 zpl_atomic_ptr_try_acquire_lock(zpl_atomic_ptr *a); + + ZPL_END_C_DECLS + // file: header/threading/fence.h + + // Fences + + ZPL_BEGIN_C_DECLS + + ZPL_DEF void zpl_yield_thread(void); + ZPL_DEF void zpl_mfence (void); + ZPL_DEF void zpl_sfence (void); + ZPL_DEF void zpl_lfence (void); + + ZPL_END_C_DECLS + // file: header/threading/sem.h + + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_SYSTEM_MACOS) + # include + #elif defined(ZPL_SYSTEM_UNIX) + # include + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) + typedef struct zpl_semaphore { void *win32_handle; } zpl_semaphore; + #elif defined(ZPL_SYSTEM_MACOS) + typedef struct zpl_semaphore { semaphore_t osx_handle; } zpl_semaphore; + #elif defined(ZPL_SYSTEM_UNIX) + typedef struct zpl_semaphore { sem_t unix_handle; } zpl_semaphore; + #else + # error + #endif + + ZPL_DEF void zpl_semaphore_init (zpl_semaphore *s); + ZPL_DEF void zpl_semaphore_destroy(zpl_semaphore *s); + ZPL_DEF void zpl_semaphore_post (zpl_semaphore *s, zpl_i32 count); + ZPL_DEF void zpl_semaphore_release(zpl_semaphore *s); // NOTE: zpl_semaphore_post(s, 1) + ZPL_DEF void zpl_semaphore_wait (zpl_semaphore *s); + ZPL_DEF zpl_i32 zpl_semaphore_trywait(zpl_semaphore *s); + + ZPL_END_C_DECLS + // file: header/threading/mutex.h + + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_mutex { + #if defined(ZPL_SYSTEM_WINDOWS) + zpl_u64 win32_critical_section[sizeof(zpl_usize) / 2 + 1]; + #else + pthread_mutex_t pthread_mutex; + #endif + } zpl_mutex; + + ZPL_DEF zpl_b32 zpl_mutex_init (zpl_mutex *m); + ZPL_DEF zpl_b32 zpl_mutex_destroy (zpl_mutex *m); + ZPL_DEF void zpl_mutex_lock (zpl_mutex *m); + ZPL_DEF zpl_b32 zpl_mutex_try_lock(zpl_mutex *m); + ZPL_DEF void zpl_mutex_unlock (zpl_mutex *m); + + ZPL_END_C_DECLS + // file: header/threading/thread.h + + #ifdef ZPL_EDITOR + #include + #else + struct zpl_thread; + #endif + + ZPL_BEGIN_C_DECLS + + typedef zpl_isize (*zpl_thread_proc)(struct zpl_thread *thread); + + typedef struct zpl_thread { + #if defined(ZPL_SYSTEM_WINDOWS) + void * win32_handle; + #else + pthread_t posix_handle; + #endif + + zpl_thread_proc proc; + void * user_data; + zpl_isize user_index; + zpl_isize return_value; + + zpl_semaphore semaphore; + zpl_isize stack_size; + zpl_b32 is_running; + zpl_b32 nowait; + } zpl_thread; + + ZPL_DEF void zpl_thread_init (zpl_thread *t); + ZPL_DEF void zpl_thread_init_nowait (zpl_thread *t); + ZPL_DEF void zpl_thread_destroy (zpl_thread *t); + ZPL_DEF zpl_b32 zpl_thread_start (zpl_thread *t, zpl_thread_proc proc, void *data); + ZPL_DEF zpl_b32 zpl_thread_start_with_stack(zpl_thread *t, zpl_thread_proc proc, void *data, zpl_isize stack_size); + ZPL_DEF void zpl_thread_join (zpl_thread *t); + ZPL_DEF zpl_b32 zpl_thread_is_running (zpl_thread const *t); + ZPL_DEF zpl_u32 zpl_thread_current_id (void); + ZPL_DEF void zpl_thread_set_name (zpl_thread *t, char const *name); + + ZPL_END_C_DECLS + // file: header/threading/sync.h + + // NOTE: Thread Merge Operation + // Based on Sean Barrett's stb_sync + + ZPL_BEGIN_C_DECLS + + typedef struct zpl_sync { + zpl_i32 target; // Target Number of threads + zpl_i32 current; // Threads to hit + zpl_i32 waiting; // Threads waiting + + zpl_mutex start; + zpl_mutex mutex; + zpl_semaphore release; + } zpl_sync; + + ZPL_DEF void zpl_sync_init (zpl_sync *s); + ZPL_DEF void zpl_sync_destroy (zpl_sync *s); + ZPL_DEF void zpl_sync_set_target (zpl_sync *s, zpl_i32 count); + ZPL_DEF void zpl_sync_release (zpl_sync *s); + ZPL_DEF zpl_i32 zpl_sync_reach (zpl_sync *s); + ZPL_DEF void zpl_sync_reach_and_wait(zpl_sync *s); + + ZPL_END_C_DECLS + // file: header/threading/affinity.h + + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_SYSTEM_WINDOWS) || defined (ZPL_SYSTEM_CYGWIN) + + typedef struct zpl_affinity { + zpl_b32 is_accurate; + zpl_isize core_count; + zpl_isize thread_count; + + # define ZPL_WIN32_MAX_THREADS (8 * zpl_size_of(zpl_usize)) + zpl_usize core_masks[ZPL_WIN32_MAX_THREADS]; + } zpl_affinity; + + #elif defined(ZPL_SYSTEM_OSX) + + typedef struct zpl_affinity { + zpl_b32 is_accurate; + zpl_isize core_count; + zpl_isize thread_count; + zpl_isize threads_per_core; + } zpl_affinity; + + #elif defined(ZPL_SYSTEM_LINUX) || defined(ZPL_SYSTEM_FREEBSD) || defined(ZPL_SYSTEM_EMSCRIPTEN) || defined(ZPL_SYSTEM_OPENBSD) + + typedef struct zpl_affinity { + zpl_b32 is_accurate; + zpl_isize core_count; + zpl_isize thread_count; + zpl_isize threads_per_core; + } zpl_affinity; + + #else + # error TODO: Unknown system + #endif + + ZPL_DEF void zpl_affinity_init (zpl_affinity *a); + ZPL_DEF void zpl_affinity_destroy(zpl_affinity *a); + ZPL_DEF zpl_b32 zpl_affinity_set (zpl_affinity *a, zpl_isize core, zpl_isize thread); + ZPL_DEF zpl_isize zpl_affinity_thread_count_for_core(zpl_affinity *a, zpl_isize core); + + ZPL_END_C_DECLS + +# if defined(ZPL_MODULE_JOBS) + // file: header/jobs.h + + /** @file threadpool.c + @brief Job system + @defgroup jobs Job system + + This job system follows thread pool pattern to minimize the costs of thread initialization. + It reuses fixed number of threads to process variable number of jobs. + + @{ + */ + + ZPL_BEGIN_C_DECLS + + typedef void (*zpl_jobs_proc)(void *data); + + #define ZPL_INVALID_JOB ZPL_U32_MAX + + #ifndef ZPL_JOBS_MAX_QUEUE + #define ZPL_JOBS_MAX_QUEUE 100 + #endif + + #ifdef ZPL_JOBS_ENABLE_DEBUG + #define ZPL_JOBS_DEBUG + #endif + + typedef enum { + ZPL_JOBS_STATUS_READY, + ZPL_JOBS_STATUS_BUSY, + ZPL_JOBS_STATUS_WAITING, + ZPL_JOBS_STATUS_TERM, + } zpl_jobs_status; + + typedef enum { + ZPL_JOBS_PRIORITY_REALTIME, + ZPL_JOBS_PRIORITY_HIGH, + ZPL_JOBS_PRIORITY_NORMAL, + ZPL_JOBS_PRIORITY_LOW, + ZPL_JOBS_PRIORITY_IDLE, + ZPL_JOBS_MAX_PRIORITIES, + } zpl_jobs_priority; + + typedef struct { + zpl_jobs_proc proc; + void *data; + } zpl_thread_job; + + ZPL_RING_DECLARE(extern, zpl__jobs_ring_, zpl_thread_job); + + typedef struct { + zpl_thread thread; + zpl_atomic32 status; + zpl_thread_job job; + #ifdef ZPL_JOBS_DEBUG + zpl_u32 hits; + zpl_u32 idle; + #endif + } zpl_thread_worker; + + typedef struct { + zpl__jobs_ring_zpl_thread_job jobs; ///< zpl_ring + zpl_u32 chance; + #ifdef ZPL_JOBS_DEBUG + zpl_u32 hits; + #endif + } zpl_thread_queue; + + typedef struct { + zpl_allocator alloc; + zpl_u32 max_threads, max_jobs, counter; + zpl_thread_worker *workers; ///< zpl_buffer + zpl_thread_queue queues[ZPL_JOBS_MAX_PRIORITIES]; + } zpl_jobs_system; + + //! Initialize thread pool with specified amount of fixed threads. + ZPL_DEF void zpl_jobs_init(zpl_jobs_system *pool, zpl_allocator a, zpl_u32 max_threads); + + //! Initialize thread pool with specified amount of fixed threads and custom job limit. + ZPL_DEF void zpl_jobs_init_with_limit(zpl_jobs_system *pool, zpl_allocator a, zpl_u32 max_threads, zpl_u32 max_jobs); + + //! Release the resources use by thread pool. + ZPL_DEF void zpl_jobs_free(zpl_jobs_system *pool); + + //! Enqueue a job with specified data and custom priority. + ZPL_DEF zpl_b32 zpl_jobs_enqueue_with_priority(zpl_jobs_system *pool, zpl_jobs_proc proc, void *data, zpl_jobs_priority priority); + + //! Enqueue a job with specified data. + ZPL_DEF zpl_b32 zpl_jobs_enqueue(zpl_jobs_system *pool, zpl_jobs_proc proc, void *data); + + //! Check if the work queue is empty. + ZPL_DEF zpl_b32 zpl_jobs_empty(zpl_jobs_system *pool, zpl_jobs_priority priority); + + ZPL_DEF zpl_b32 zpl_jobs_empty_all(zpl_jobs_system *pool); + ZPL_DEF zpl_b32 zpl_jobs_full_all(zpl_jobs_system *pool); + + //! Check if the work queue is full. + ZPL_DEF zpl_b32 zpl_jobs_full(zpl_jobs_system *pool, zpl_jobs_priority priority); + + //! Check if all workers are done. + ZPL_DEF zpl_b32 zpl_jobs_done(zpl_jobs_system *pool); + + //! Process all jobs and check all threads. Should be called by Main Thread in a tight loop. + ZPL_DEF zpl_b32 zpl_jobs_process(zpl_jobs_system *pool); + + ZPL_END_C_DECLS +# endif +#else +# if !defined(zpl_thread_local) +# define zpl_thread_local +# endif +#endif + +#if defined(ZPL_COMPILER_MSVC) +# pragma warning(pop) +#endif + +#if defined(__GCC__) || defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic pop +#endif + +#if defined(ZPL_IMPLEMENTATION) && !defined(ZPL_IMPLEMENTATION_DONE) +#define ZPL_IMPLEMENTATION_DONE + +#if defined(__GCC__) || defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wattributes" +# pragma GCC diagnostic ignored "-Wunused-value" +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wwrite-strings" +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wdeprecated-declarations" +# pragma GCC diagnostic ignored "-Wmissing-braces" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +# pragma GCC diagnostic ignored "-Wignored-qualifiers" +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable : 4201) +# pragma warning(disable : 4996) // Disable deprecated POSIX functions warning +# pragma warning(disable : 4127) // Conditional expression is constant +# pragma warning(disable : 4204) // non-constant field initializer +# pragma warning(disable : 4756) // -INFINITY +# pragma warning(disable : 4056) // -INFINITY +# pragma warning(disable : 4702) // unreachable code +#endif + +/* general purpose includes */ + +#include + +// NOTE: Ensure we use standard methods for these calls if we use ZPL_PICO +#if !defined(ZPL_PICO_CUSTOM_ROUTINES) +# if !defined(ZPL_MODULE_CORE) +# define zpl__strlen strlen +# define zpl__printf_err(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__) +# define zpl__printf_err_va(fmt, va) vfprintf(stderr, fmt, va) +# else +# define zpl__strlen zpl_strlen +# define zpl__printf_err(fmt, ...) zpl_printf_err(fmt, __VA_ARGS__) +# define zpl__printf_err_va(fmt, va) zpl_printf_err_va(fmt, va) +# endif +#endif + +#include + +#if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_MACOS) +# include +#elif defined(ZPL_SYSTEM_WINDOWS) +# if !defined(ZPL_NO_WINDOWS_H) +# ifndef WIN32_LEAN_AND_MEAN +# ifndef NOMINMAX +# define NOMINMAX +# endif + +# define WIN32_LEAN_AND_MEAN +# define WIN32_MEAN_AND_LEAN +# define VC_EXTRALEAN +# endif +# include +# undef NOMINMAX +# undef WIN32_LEAN_AND_MEAN +# undef WIN32_MEAN_AND_LEAN +# undef VC_EXTRALEAN +# endif +#endif + +#if defined(ZPL_MODULE_ESSENTIALS) + // file: source/essentials/debug.c + + + ZPL_BEGIN_C_DECLS + + void zpl_assert_handler(char const *condition, char const *file, zpl_i32 line, char const *msg, ...) { + zpl__printf_err("%s:(%d): Assert Failure: ", file, line); + + if (condition) zpl__printf_err("`%s` ", condition); + + if (msg) { + va_list va; + va_start(va, msg); + zpl__printf_err_va(msg, va); + va_end(va); + } + + zpl__printf_err("%s", "\n"); + } + + zpl_i32 zpl_assert_crash(char const *condition) { + ZPL_PANIC(condition); + return 0; + } + + #if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_MACOS) + # include + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) + void zpl_exit(zpl_u32 code) { ExitProcess(code); } + #else + # include + void zpl_exit(zpl_u32 code) { exit(code); } + #endif + + ZPL_END_C_DECLS + // file: source/essentials/memory.c + + + #include + + ZPL_BEGIN_C_DECLS + + + void zpl_memswap(void *i, void *j, zpl_isize size) { + if (i == j) return; + + if (size == 4) { + zpl_swap(zpl_u32, *cast(zpl_u32 *) i, *cast(zpl_u32 *) j); + } else if (size == 8) { + zpl_swap(zpl_u64, *cast(zpl_u64 *) i, *cast(zpl_u64 *) j); + } else if (size < 8) { + zpl_u8 *a = cast(zpl_u8 *) i; + zpl_u8 *b = cast(zpl_u8 *) j; + if (a != b) { + while (size--) { zpl_swap(zpl_u8, *a++, *b++); } + } + } else { + char buffer[256]; + + while (size > zpl_size_of(buffer)) { + zpl_memswap(i, j, zpl_size_of(buffer)); + i = zpl_pointer_add(i, zpl_size_of(buffer)); + j = zpl_pointer_add(j, zpl_size_of(buffer)); + size -= zpl_size_of(buffer); + } + + zpl_memcopy(buffer, i, size); + zpl_memcopy(i, j, size); + zpl_memcopy(j, buffer, size); + } + } + + void const *zpl_memchr(void const *data, zpl_u8 c, zpl_isize n) { + zpl_u8 const *s = cast(zpl_u8 const *) data; + while ((cast(zpl_uintptr) s & (sizeof(zpl_usize) - 1)) && n && *s != c) { + s++; + n--; + } + if (n && *s != c) { + zpl_isize const *w; + zpl_isize k = ZPL__ONES * c; + w = cast(zpl_isize const *) s; + while (n >= zpl_size_of(zpl_isize) && !ZPL__HAS_ZERO(*w ^ k)) { + w++; + n -= zpl_size_of(zpl_isize); + } + s = cast(zpl_u8 const *) w; + while (n && *s != c) { + s++; + n--; + } + } + + return n ? cast(void const *) s : NULL; + } + + void const *zpl_memrchr(void const *data, zpl_u8 c, zpl_isize n) { + zpl_u8 const *s = cast(zpl_u8 const *) data; + while (n--) { + if (s[n] == c) return cast(void const *)(s + n); + } + return NULL; + } + + void *zpl_memcopy(void *dest, void const *source, zpl_isize n) { + if (dest == NULL) { return NULL; } + + return memcpy(dest, source, n); + + // TODO: Re-work the whole method + #if 0 + #if defined(_MSC_VER) + __movsb(cast(zpl_u8 *) dest, cast(zpl_u8 *) source, n); + #elif defined(ZPL_CPU_X86) && !defined(ZPL_SYSTEM_EMSCRIPTEN) + zpl_u8 *__dest8 = cast(zpl_u8 *) dest; + zpl_u8 *__source8 = cast(zpl_u8 *) source; + __asm__ __volatile__("rep movsb" : "+D"(__dest8), "+S"(__source8), "+c"(n) : : "memory"); + #elif defined(ZPL_CPU_ARM) + return memcpy(dest, source, n); + #else + zpl_u8 *d = cast(zpl_u8 *) dest; + zpl_u8 const *s = cast(zpl_u8 const *) source; + zpl_u32 w, x; + + for (; cast(zpl_uintptr) s % 4 && n; n--) *d++ = *s++; + + if (cast(zpl_uintptr) d % 4 == 0) { + for (; n >= 16; s += 16, d += 16, n -= 16) { + *cast(zpl_u32 *)(d + 0) = *cast(zpl_u32 *)(s + 0); + *cast(zpl_u32 *)(d + 4) = *cast(zpl_u32 *)(s + 4); + *cast(zpl_u32 *)(d + 8) = *cast(zpl_u32 *)(s + 8); + *cast(zpl_u32 *)(d + 12) = *cast(zpl_u32 *)(s + 12); + } + if (n & 8) { + *cast(zpl_u32 *)(d + 0) = *cast(zpl_u32 *)(s + 0); + *cast(zpl_u32 *)(d + 4) = *cast(zpl_u32 *)(s + 4); + d += 8; + s += 8; + } + if (n & 4) { + *cast(zpl_u32 *)(d + 0) = *cast(zpl_u32 *)(s + 0); + d += 4; + s += 4; + } + if (n & 2) { + *d++ = *s++; + *d++ = *s++; + } + if (n & 1) { *d = *s; } + return dest; + } + + if (n >= 32) { + #if __BYTE_ORDER == __BIG_ENDIAN + #define LS << + #define RS >> + #else + #define LS >> + #define RS << + #endif + switch (cast(zpl_uintptr) d % 4) { + case 1: { + w = *cast(zpl_u32 *) s; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + n -= 3; + while (n > 16) { + x = *cast(zpl_u32 *)(s + 1); + *cast(zpl_u32 *)(d + 0) = (w LS 24) | (x RS 8); + w = *cast(zpl_u32 *)(s + 5); + *cast(zpl_u32 *)(d + 4) = (x LS 24) | (w RS 8); + x = *cast(zpl_u32 *)(s + 9); + *cast(zpl_u32 *)(d + 8) = (w LS 24) | (x RS 8); + w = *cast(zpl_u32 *)(s + 13); + *cast(zpl_u32 *)(d + 12) = (x LS 24) | (w RS 8); + + s += 16; + d += 16; + n -= 16; + } + } break; + case 2: { + w = *cast(zpl_u32 *) s; + *d++ = *s++; + *d++ = *s++; + n -= 2; + while (n > 17) { + x = *cast(zpl_u32 *)(s + 2); + *cast(zpl_u32 *)(d + 0) = (w LS 16) | (x RS 16); + w = *cast(zpl_u32 *)(s + 6); + *cast(zpl_u32 *)(d + 4) = (x LS 16) | (w RS 16); + x = *cast(zpl_u32 *)(s + 10); + *cast(zpl_u32 *)(d + 8) = (w LS 16) | (x RS 16); + w = *cast(zpl_u32 *)(s + 14); + *cast(zpl_u32 *)(d + 12) = (x LS 16) | (w RS 16); + + s += 16; + d += 16; + n -= 16; + } + } break; + case 3: { + w = *cast(zpl_u32 *) s; + *d++ = *s++; + n -= 1; + while (n > 18) { + x = *cast(zpl_u32 *)(s + 3); + *cast(zpl_u32 *)(d + 0) = (w LS 8) | (x RS 24); + w = *cast(zpl_u32 *)(s + 7); + *cast(zpl_u32 *)(d + 4) = (x LS 8) | (w RS 24); + x = *cast(zpl_u32 *)(s + 11); + *cast(zpl_u32 *)(d + 8) = (w LS 8) | (x RS 24); + w = *cast(zpl_u32 *)(s + 15); + *cast(zpl_u32 *)(d + 12) = (x LS 8) | (w RS 24); + + s += 16; + d += 16; + n -= 16; + } + } break; + default: break; // NOTE: Do nowt! + } + #undef LS + #undef RS + if (n & 16) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + } + if (n & 8) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + } + if (n & 4) { + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + *d++ = *s++; + } + if (n & 2) { + *d++ = *s++; + *d++ = *s++; + } + if (n & 1) { *d = *s; } + } + + #endif + #endif + + return dest; + } + + ZPL_END_C_DECLS + // file: source/essentials/memory_custom.c + + + #ifndef _IOSC11_SOURCE + #define _IOSC11_SOURCE + #endif + + #include + + #if defined(ZPL_SYSTEM_WINDOWS) + # include + #endif + + // include errno.h for MinGW + #if defined(ZPL_COMPILER_GCC) || (defined(ZPL_COMPILER_TINYC) && defined(ZPL_SYSTEM_WINDOWS)) + # include + #endif + + #if defined(ZPL_COMPILER_MINGW) + # ifdef __MINGW32__ + # define _aligned_malloc __mingw_aligned_malloc + # define _aligned_free __mingw_aligned_free + # endif //MINGW + #endif + + ZPL_BEGIN_C_DECLS + + char *zpl_alloc_str(zpl_allocator a, char const *str) { + return zpl_alloc_str_len(a, str, zpl__strlen(str)); + } + + //////////////////////////////////////////////////////////////// + // + // Custom Allocation + // + // + + // + // Heap Allocator + // + + #define ZPL_HEAP_STATS_MAGIC 0xDEADC0DE + + typedef struct zpl__heap_stats { + zpl_u32 magic; + zpl_isize used_memory; + zpl_isize alloc_count; + } zpl__heap_stats; + + zpl_global zpl__heap_stats zpl__heap_stats_info; + + void zpl_heap_stats_init(void) { + zpl_zero_item(&zpl__heap_stats_info); + zpl__heap_stats_info.magic = ZPL_HEAP_STATS_MAGIC; + } + zpl_isize zpl_heap_stats_used_memory(void) { + ZPL_ASSERT_MSG(zpl__heap_stats_info.magic == ZPL_HEAP_STATS_MAGIC, "zpl_heap_stats is not initialised yet, call zpl_heap_stats_init first!"); + return zpl__heap_stats_info.used_memory; + } + zpl_isize zpl_heap_stats_alloc_count(void) { + ZPL_ASSERT_MSG(zpl__heap_stats_info.magic == ZPL_HEAP_STATS_MAGIC, "zpl_heap_stats is not initialised yet, call zpl_heap_stats_init first!"); + return zpl__heap_stats_info.alloc_count; + } + void zpl_heap_stats_check(void) { + ZPL_ASSERT_MSG(zpl__heap_stats_info.magic == ZPL_HEAP_STATS_MAGIC, "zpl_heap_stats is not initialised yet, call zpl_heap_stats_init first!"); + ZPL_ASSERT(zpl__heap_stats_info.used_memory == 0); + ZPL_ASSERT(zpl__heap_stats_info.alloc_count == 0); + } + + typedef struct zpl__heap_alloc_info { + zpl_isize size; + void *physical_start; + } zpl__heap_alloc_info; + + ZPL_ALLOCATOR_PROC(zpl_heap_allocator_proc) { + void *ptr = NULL; + zpl_unused(allocator_data); + zpl_unused(old_size); + if (!alignment) alignment = ZPL_DEFAULT_MEMORY_ALIGNMENT; + + # ifdef ZPL_HEAP_ANALYSIS + zpl_isize alloc_info_size = zpl_size_of(zpl__heap_alloc_info); + zpl_isize alloc_info_remainder = (alloc_info_size % alignment); + zpl_isize track_size = zpl_max(alloc_info_size, alignment) + alloc_info_remainder; + switch (type) { + case ZPL_ALLOCATION_FREE: { + if (!old_memory) break; + zpl__heap_alloc_info *alloc_info = cast(zpl__heap_alloc_info *)old_memory - 1; + zpl__heap_stats_info.used_memory -= alloc_info->size; + zpl__heap_stats_info.alloc_count--; + old_memory = alloc_info->physical_start; + } break; + case ZPL_ALLOCATION_ALLOC: { + size += track_size; + } break; + default: break; + } + # endif + + switch (type) { + #if defined(ZPL_COMPILER_MSVC) || (defined(ZPL_COMPILER_GCC) && defined(ZPL_SYSTEM_WINDOWS)) || (defined(ZPL_COMPILER_TINYC) && defined(ZPL_SYSTEM_WINDOWS)) + case ZPL_ALLOCATION_ALLOC: + ptr = _aligned_malloc(size, alignment); + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) zpl_zero_size(ptr, size); + break; + case ZPL_ALLOCATION_FREE: _aligned_free(old_memory); break; + case ZPL_ALLOCATION_RESIZE: { + zpl_allocator a = zpl_heap_allocator(); + ptr = zpl_default_resize_align(a, old_memory, old_size, size, alignment); + } break; + + #elif defined(ZPL_SYSTEM_LINUX) && !defined(ZPL_CPU_ARM) && !defined(ZPL_COMPILER_TINYC) + case ZPL_ALLOCATION_ALLOC: { + ptr = aligned_alloc(alignment, (size + alignment - 1) & ~(alignment - 1)); + + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) { zpl_zero_size(ptr, size); } + } break; + + case ZPL_ALLOCATION_FREE: { + free(old_memory); + } break; + + case ZPL_ALLOCATION_RESIZE: { + zpl_allocator a = zpl_heap_allocator(); + ptr = zpl_default_resize_align(a, old_memory, old_size, size, alignment); + } break; + #else + case ZPL_ALLOCATION_ALLOC: { + posix_memalign(&ptr, alignment, size); + + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) { zpl_zero_size(ptr, size); } + } break; + + case ZPL_ALLOCATION_FREE: { + free(old_memory); + } break; + + case ZPL_ALLOCATION_RESIZE: { + zpl_allocator a = zpl_heap_allocator( ); + ptr = zpl_default_resize_align(a, old_memory, old_size, size, alignment); + } break; + #endif + + case ZPL_ALLOCATION_FREE_ALL: break; + } + + # ifdef ZPL_HEAP_ANALYSIS + if (type == ZPL_ALLOCATION_ALLOC) { + zpl__heap_alloc_info *alloc_info = cast(zpl__heap_alloc_info *)(cast(char *)ptr + alloc_info_remainder); + zpl_zero_item(alloc_info); + alloc_info->size = size - track_size; + alloc_info->physical_start = ptr; + ptr = cast(void*)(alloc_info + 1); + zpl__heap_stats_info.used_memory += alloc_info->size; + zpl__heap_stats_info.alloc_count++; + } + # endif + + return ptr; + } + + // + // Arena Allocator + // + + ZPL_ALLOCATOR_PROC(zpl_arena_allocator_proc) { + zpl_arena *arena = cast(zpl_arena *) allocator_data; + void *ptr = NULL; + + zpl_unused(old_size); + + switch (type) { + case ZPL_ALLOCATION_ALLOC: { + void *end = zpl_pointer_add(arena->physical_start, arena->total_allocated); + zpl_isize total_size = zpl_align_forward_i64(size, alignment); + + // NOTE: Out of memory + if (arena->total_allocated + total_size > cast(zpl_isize) arena->total_size) { + return NULL; + } + + ptr = zpl_align_forward(end, alignment); + arena->total_allocated += total_size; + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) zpl_zero_size(ptr, size); + } break; + + case ZPL_ALLOCATION_FREE: + // NOTE: Free all at once + // Use Temp_Arena_Memory if you want to free a block + break; + + case ZPL_ALLOCATION_FREE_ALL: arena->total_allocated = 0; break; + + case ZPL_ALLOCATION_RESIZE: { + // TODO: Check if ptr is on top of stack and just extend + zpl_allocator a = zpl_arena_allocator(arena); + ptr = zpl_default_resize_align(a, old_memory, old_size, size, alignment); + } break; + } + return ptr; + } + + // + // Pool Allocator + // + + void zpl_pool_init_align(zpl_pool *pool, zpl_allocator backing, zpl_isize num_blocks, zpl_isize block_size, zpl_isize block_align) { + zpl_isize actual_block_size, pool_size, block_index; + void *data, *curr; + zpl_uintptr *end; + + zpl_zero_item(pool); + + pool->backing = backing; + pool->block_size = block_size; + pool->block_align = block_align; + pool->num_blocks = num_blocks; + + actual_block_size = block_size + block_align; + pool_size = num_blocks * actual_block_size; + + data = zpl_alloc_align(backing, pool_size, block_align); + + // NOTE: Init intrusive freelist + curr = data; + for (block_index = 0; block_index < num_blocks - 1; block_index++) { + zpl_uintptr *next = cast(zpl_uintptr *) curr; + *next = cast(zpl_uintptr) curr + actual_block_size; + curr = zpl_pointer_add(curr, actual_block_size); + } + + end = cast(zpl_uintptr *) curr; + *end = cast(zpl_uintptr) NULL; + + pool->physical_start = data; + pool->free_list = data; + } + + ZPL_ALLOCATOR_PROC(zpl_pool_allocator_proc) { + zpl_pool *pool = cast(zpl_pool *) allocator_data; + void *ptr = NULL; + + zpl_unused(old_size); + + switch (type) { + case ZPL_ALLOCATION_ALLOC: { + zpl_uintptr next_free; + ZPL_ASSERT(size == pool->block_size); + ZPL_ASSERT(alignment == pool->block_align); + ZPL_ASSERT(pool->free_list != NULL); + + next_free = *cast(zpl_uintptr *) pool->free_list; + ptr = pool->free_list; + pool->free_list = cast(void *) next_free; + pool->total_size += pool->block_size; + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) zpl_zero_size(ptr, size); + } break; + + case ZPL_ALLOCATION_FREE: { + zpl_uintptr *next; + if (old_memory == NULL) return NULL; + + next = cast(zpl_uintptr *) old_memory; + *next = cast(zpl_uintptr) pool->free_list; + pool->free_list = old_memory; + pool->total_size -= pool->block_size; + } break; + + case ZPL_ALLOCATION_FREE_ALL: { + zpl_isize actual_block_size, block_index; + void *curr; + zpl_uintptr *end; + + actual_block_size = pool->block_size + pool->block_align; + pool->total_size = 0; + + // NOTE: Init intrusive freelist + curr = pool->physical_start; + for (block_index = 0; block_index < pool->num_blocks - 1; block_index++) { + zpl_uintptr *next = cast(zpl_uintptr *) curr; + *next = cast(zpl_uintptr) curr + actual_block_size; + curr = zpl_pointer_add(curr, actual_block_size); + } + + end = cast(zpl_uintptr *) curr; + *end = cast(zpl_uintptr) NULL; + pool->free_list = pool->physical_start; + } break; + + case ZPL_ALLOCATION_RESIZE: + // NOTE: Cannot resize + ZPL_PANIC("You cannot resize something allocated by with a pool."); + break; + } + + return ptr; + } + + + // + // Scratch Memory Allocator + // + + void zpl_scratch_memory_init(zpl_scratch_memory *s, void *start, zpl_isize size) { + s->physical_start = start; + s->total_size = size; + s->alloc_point = start; + s->free_point = start; + } + + zpl_b32 zpl_scratch_memory_is_in_use(zpl_scratch_memory *s, void *ptr) { + if (s->free_point == s->alloc_point) return false; + if (s->alloc_point > s->free_point) return ptr >= s->free_point && ptr < s->alloc_point; + return ptr >= s->free_point || ptr < s->alloc_point; + } + + zpl_allocator zpl_scratch_allocator(zpl_scratch_memory *s) { + zpl_allocator a; + a.proc = zpl_scratch_allocator_proc; + a.data = s; + return a; + } + + ZPL_ALLOCATOR_PROC(zpl_scratch_allocator_proc) { + zpl_scratch_memory *s = cast(zpl_scratch_memory *) allocator_data; + void *ptr = NULL; + ZPL_ASSERT_NOT_NULL(s); + + switch (type) { + case ZPL_ALLOCATION_ALLOC: { + void *pt = s->alloc_point; + zpl_allocation_header_ev *header = cast(zpl_allocation_header_ev *) pt; + void *data = zpl_align_forward(header + 1, alignment); + void *end = zpl_pointer_add(s->physical_start, s->total_size); + + ZPL_ASSERT(alignment % 4 == 0); + size = ((size + 3) / 4) * 4; + pt = zpl_pointer_add(pt, size); + + // NOTE: Wrap around + if (pt > end) { + header->size = zpl_pointer_diff(header, end) | ZPL_ISIZE_HIGH_BIT; + pt = s->physical_start; + header = cast(zpl_allocation_header_ev *) pt; + data = zpl_align_forward(header + 1, alignment); + pt = zpl_pointer_add(pt, size); + } + + if (!zpl_scratch_memory_is_in_use(s, pt)) { + zpl_allocation_header_fill(header, pt, zpl_pointer_diff(header, pt)); + s->alloc_point = cast(zpl_u8 *) pt; + ptr = data; + } + + if (flags & ZPL_ALLOCATOR_FLAG_CLEAR_TO_ZERO) zpl_zero_size(ptr, size); + } break; + + case ZPL_ALLOCATION_FREE: { + if (old_memory) { + void *end = zpl_pointer_add(s->physical_start, s->total_size); + if (old_memory < s->physical_start || old_memory >= end) { + ZPL_ASSERT(false); + } else { + // NOTE: Mark as free + zpl_allocation_header_ev *h = zpl_allocation_header(old_memory); + ZPL_ASSERT((h->size & ZPL_ISIZE_HIGH_BIT) == 0); + h->size = h->size | ZPL_ISIZE_HIGH_BIT; + + while (s->free_point != s->alloc_point) { + zpl_allocation_header_ev *header = cast(zpl_allocation_header_ev *) s->free_point; + if ((header->size & ZPL_ISIZE_HIGH_BIT) == 0) break; + + s->free_point = zpl_pointer_add(s->free_point, h->size & (~ZPL_ISIZE_HIGH_BIT)); + if (s->free_point == end) s->free_point = s->physical_start; + } + } + } + } break; + + case ZPL_ALLOCATION_FREE_ALL: + s->alloc_point = s->physical_start; + s->free_point = s->physical_start; + break; + + case ZPL_ALLOCATION_RESIZE: + ptr = zpl_default_resize_align(zpl_scratch_allocator(s), old_memory, old_size, size, alignment); + break; + } + + return ptr; + } + + // + // Stack Memory Allocator + // + ZPL_ALLOCATOR_PROC(zpl_stack_allocator_proc) { + zpl_stack_memory *s = cast(zpl_stack_memory *) allocator_data; + void *ptr = NULL; + ZPL_ASSERT_NOT_NULL(s); + zpl_unused(old_size); + zpl_unused(flags); + + switch (type) { + case ZPL_ALLOCATION_ALLOC: { + size += ZPL_STACK_ALLOC_OFFSET; + zpl_u64 alloc_offset = s->allocated; + + void *curr = + cast(zpl_u64 *) zpl_align_forward(cast(zpl_u64 *) zpl_pointer_add(s->physical_start, s->allocated), alignment); + + if (cast(zpl_u64 *) zpl_pointer_add(curr, size) > cast(zpl_u64 *) zpl_pointer_add(s->physical_start, s->total_size)) { + if (s->backing.proc) { + void *old_start = s->physical_start; + s->physical_start = + zpl_resize_align(s->backing, s->physical_start, s->total_size, s->total_size + size, alignment); + curr = cast(zpl_u64 *) + zpl_align_forward(cast(zpl_u64 *) zpl_pointer_add(s->physical_start, s->allocated), alignment); + s->total_size = zpl_pointer_diff(old_start, s->physical_start); + } else { + ZPL_PANIC("Can not resize stack's memory! Allocator not defined!"); + } + } + + s->allocated = zpl_pointer_diff(s->physical_start, curr) + size; + + *(zpl_u64 *)curr = alloc_offset; + curr = zpl_pointer_add(curr, ZPL_STACK_ALLOC_OFFSET); + + ptr = curr; + } break; + + case ZPL_ALLOCATION_FREE: { + if (old_memory) { + void *curr = old_memory; + curr = zpl_pointer_sub(curr, ZPL_STACK_ALLOC_OFFSET); + + zpl_u64 alloc_offset = *(zpl_u64 *)curr; + s->allocated = (zpl_usize)alloc_offset; + } + } break; + + case ZPL_ALLOCATION_FREE_ALL: { + s->allocated = 0; + } break; + + case ZPL_ALLOCATION_RESIZE: { + ZPL_PANIC("You cannot resize something allocated by a stack."); + } break; + } + return ptr; + } + + ZPL_END_C_DECLS +# if defined(ZPL_MODULE_CORE) + // file: source/core/memory_virtual.c + + //////////////////////////////////////////////////////////////// + // + // Virtual Memory + // + // + + ZPL_BEGIN_C_DECLS + + zpl_virtual_memory zpl_vm(void *data, zpl_isize size) { + zpl_virtual_memory vm; + vm.data = data; + vm.size = size; + return vm; + } + + #if defined(ZPL_SYSTEM_WINDOWS) + zpl_virtual_memory zpl_vm_alloc(void *addr, zpl_isize size) { + zpl_virtual_memory vm; + ZPL_ASSERT(size > 0); + vm.data = VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); + vm.size = size; + return vm; + } + + zpl_b32 zpl_vm_free(zpl_virtual_memory vm) { + MEMORY_BASIC_INFORMATION info; + while (vm.size > 0) { + if (VirtualQuery(vm.data, &info, zpl_size_of(info)) == 0) return false; + if (info.BaseAddress != vm.data || info.AllocationBase != vm.data || info.State != MEM_COMMIT || + info.RegionSize > cast(zpl_usize) vm.size) { + return false; + } + if (VirtualFree(vm.data, 0, MEM_RELEASE) == 0) return false; + vm.data = zpl_pointer_add(vm.data, info.RegionSize); + vm.size -= info.RegionSize; + } + return true; + } + + zpl_virtual_memory zpl_vm_trim(zpl_virtual_memory vm, zpl_isize lead_size, zpl_isize size) { + zpl_virtual_memory new_vm = { 0 }; + void *ptr; + ZPL_ASSERT(vm.size >= lead_size + size); + + ptr = zpl_pointer_add(vm.data, lead_size); + + zpl_vm_free(vm); + new_vm = zpl_vm_alloc(ptr, size); + if (new_vm.data == ptr) return new_vm; + if (new_vm.data) zpl_vm_free(new_vm); + return new_vm; + } + + zpl_b32 zpl_vm_purge(zpl_virtual_memory vm) { + VirtualAlloc(vm.data, vm.size, MEM_RESET, PAGE_READWRITE); + // NOTE: Can this really fail? + return true; + } + + zpl_isize zpl_virtual_memory_page_size(zpl_isize *alignment_out) { + SYSTEM_INFO info; + GetSystemInfo(&info); + if (alignment_out) *alignment_out = info.dwAllocationGranularity; + return info.dwPageSize; + } + + #else + # include + + # ifndef MAP_ANONYMOUS + # define MAP_ANONYMOUS MAP_ANON + # endif + + zpl_virtual_memory zpl_vm_alloc(void *addr, zpl_isize size) { + zpl_virtual_memory vm; + ZPL_ASSERT(size > 0); + vm.data = mmap(addr, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + vm.size = size; + return vm; + } + + zpl_b32 zpl_vm_free(zpl_virtual_memory vm) { + munmap(vm.data, vm.size); + return true; + } + + zpl_virtual_memory zpl_vm_trim(zpl_virtual_memory vm, zpl_isize lead_size, zpl_isize size) { + void *ptr; + zpl_isize trail_size; + ZPL_ASSERT(vm.size >= lead_size + size); + + ptr = zpl_pointer_add(vm.data, lead_size); + trail_size = vm.size - lead_size - size; + + if (lead_size != 0) zpl_vm_free(zpl_vm(vm.data, lead_size)); + if (trail_size != 0) zpl_vm_free(zpl_vm(ptr, trail_size)); + return zpl_vm(ptr, size); + } + + zpl_b32 zpl_vm_purge(zpl_virtual_memory vm) { + int err = madvise(vm.data, vm.size, MADV_DONTNEED); + return err != 0; + } + + zpl_isize zpl_virtual_memory_page_size(zpl_isize *alignment_out) { + // TODO: Is this always true? + zpl_isize result = cast(zpl_isize) sysconf(_SC_PAGE_SIZE); + if (alignment_out) *alignment_out = result; + return result; + } + + #endif + + ZPL_END_C_DECLS + // file: source/core/string.c + + //////////////////////////////////////////////////////////////// + // + // Char things + // + // + + ZPL_BEGIN_C_DECLS + + zpl_internal zpl_isize zpl__scan_zpl_i64(const char *text, zpl_i32 base, zpl_i64 *value) { + const char *text_begin = text; + zpl_i64 result = 0; + zpl_b32 negative = false; + + if (*text == '-') { + negative = true; + text++; + } + + if (base == 16 && zpl_strncmp(text, "0x", 2) == 0) text += 2; + + for (;;) { + zpl_i64 v; + if (zpl_char_is_digit(*text)) + v = *text - '0'; + else if (base == 16 && zpl_char_is_hex_digit(*text)) + v = zpl_hex_digit_to_int(*text); + else + break; + + result *= base; + result += v; + text++; + } + + if (value) { + if (negative) result = -result; + *value = result; + } + + return (text - text_begin); + } + + zpl_internal zpl_isize zpl__scan_zpl_u64(const char *text, zpl_i32 base, zpl_u64 *value) { + const char *text_begin = text; + zpl_u64 result = 0; + + if (base == 16 && zpl_strncmp(text, "0x", 2) == 0) text += 2; + + for (;;) { + zpl_u64 v; + if (zpl_char_is_digit(*text)) + v = *text - '0'; + else if (base == 16 && zpl_char_is_hex_digit(*text)) + v = zpl_hex_digit_to_int(*text); + else { + break; + } + + result *= base; + result += v; + text++; + } + + if (value) *value = result; + + return (text - text_begin); + } + + // TODO: Make better + zpl_u64 zpl_str_to_u64(const char *str, char **end_ptr, zpl_i32 base) { + zpl_isize len; + zpl_u64 value = 0; + + if (!base) { + if ((zpl_strlen(str) > 2) && (zpl_strncmp(str, "0x", 2) == 0)) + base = 16; + else + base = 10; + } + + len = zpl__scan_zpl_u64(str, base, &value); + if (end_ptr) *end_ptr = (char *)str + len; + return value; + } + + zpl_i64 zpl_str_to_i64(const char *str, char **end_ptr, zpl_i32 base) { + zpl_isize len; + zpl_i64 value; + + if (!base) { + if ((zpl_strlen(str) > 2) && (zpl_strncmp(str, "0x", 2) == 0)) + base = 16; + else + base = 10; + } + + len = zpl__scan_zpl_i64(str, base, &value); + if (end_ptr) *end_ptr = (char *)str + len; + return value; + } + + // TODO: Are these good enough for characters? + zpl_global const char zpl__num_to_char_table[] = "0123456789" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "@$"; + + void zpl_i64_to_str(zpl_i64 value, char *string, zpl_i32 base) { + char *buf = string; + zpl_b32 negative = false; + zpl_u64 v; + + if (value < 0) { + negative = true; + value = -value; + } + + v = cast(zpl_u64) value; + if (v != 0) { + while (v > 0) { + *buf++ = zpl__num_to_char_table[v % base]; + v /= base; + } + } else { + *buf++ = '0'; + } + if (negative) *buf++ = '-'; + *buf = '\0'; + zpl_strrev(string); + } + + void zpl_u64_to_str(zpl_u64 value, char *string, zpl_i32 base) { + char *buf = string; + + if (value) { + while (value > 0) { + *buf++ = zpl__num_to_char_table[value % base]; + value /= base; + } + } else { + *buf++ = '0'; + } + *buf = '\0'; + + zpl_strrev(string); + } + + zpl_f64 zpl_str_to_f64(const char *str, char **end_ptr) { + zpl_f64 result, value, sign, scale; + zpl_i32 frac; + + while (zpl_char_is_space(*str)) { str++; } + + sign = 1.0; + if (*str == '-') { + sign = -1.0; + str++; + } else if (*str == '+') { + str++; + } + + for (value = 0.0; zpl_char_is_digit(*str); str++) { value = value * 10.0 + (*str - '0'); } + + if (*str == '.') { + zpl_f64 pow10 = 10.0; + str++; + while (zpl_char_is_digit(*str)) { + value += (*str - '0') / pow10; + pow10 *= 10.0; + str++; + } + } + + frac = 0; + scale = 1.0; + if ((*str == 'e') || (*str == 'E')) { + zpl_u32 exp; + + str++; + if (*str == '-') { + frac = 1; + str++; + } else if (*str == '+') { + str++; + } + + for (exp = 0; zpl_char_is_digit(*str); str++) { exp = exp * 10 + (*str - '0'); } + if (exp > 308) exp = 308; + + while (exp >= 50) { + scale *= 1e50; + exp -= 50; + } + while (exp >= 8) { + scale *= 1e8; + exp -= 8; + } + while (exp > 0) { + scale *= 10.0; + exp -= 1; + } + } + + result = sign * (frac ? (value / scale) : (value * scale)); + + if (end_ptr) *end_ptr = cast(char *) str; + + return result; + } + + + + //////////////////////////////////////////////////////////////// + // + // Windows UTF-8 Handling + // + // + + zpl_u16 *zpl_utf8_to_ucs2(zpl_u16 *buffer, zpl_isize len, zpl_u8 const *str) { + zpl_rune c; + zpl_isize i = 0; + len--; + while (*str) { + if (i >= len) return NULL; + if (!(*str & 0x80)) { + buffer[i++] = *str++; + } else if ((*str & 0xe0) == 0xc0) { + if (*str < 0xc2) return NULL; + c = (*str++ & 0x1f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + buffer[i++] = cast(zpl_u16)(c + (*str++ & 0x3f)); + } else if ((*str & 0xf0) == 0xe0) { + if (*str == 0xe0 && (str[1] < 0xa0 || str[1] > 0xbf)) return NULL; + if (*str == 0xed && str[1] > 0x9f) // str[1] < 0x80 is checked below + return NULL; + c = (*str++ & 0x0f) << 12; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + buffer[i++] = cast(zpl_u16)(c + (*str++ & 0x3f)); + } else if ((*str & 0xf8) == 0xf0) { + if (*str > 0xf4) return NULL; + if (*str == 0xf0 && (str[1] < 0x90 || str[1] > 0xbf)) return NULL; + if (*str == 0xf4 && str[1] > 0x8f) // str[1] < 0x80 is checked below + return NULL; + c = (*str++ & 0x07) << 18; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 12; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f) << 6; + if ((*str & 0xc0) != 0x80) return NULL; + c += (*str++ & 0x3f); + // UTF-8 encodings of values used in surrogate pairs are invalid + if ((c & 0xfffff800) == 0xd800) return NULL; + if (c >= 0x10000) { + c -= 0x10000; + if (i + 2 > len) return NULL; + buffer[i++] = 0xd800 | (0x3ff & (c >> 10)); + buffer[i++] = 0xdc00 | (0x3ff & (c)); + } + } else { + return NULL; + } + } + buffer[i] = 0; + return buffer; + } + + zpl_u8 *zpl_ucs2_to_utf8(zpl_u8 *buffer, zpl_isize len, zpl_u16 const *str) { + zpl_isize i = 0; + len--; + while (*str) { + if (*str < 0x80) { + if (i + 1 > len) return NULL; + buffer[i++] = (char)*str++; + } else if (*str < 0x800) { + if (i + 2 > len) return NULL; + buffer[i++] = cast(char)(0xc0 + (*str >> 6)); + buffer[i++] = cast(char)(0x80 + (*str & 0x3f)); + str += 1; + } else if (*str >= 0xd800 && *str < 0xdc00) { + zpl_rune c; + if (i + 4 > len) return NULL; + c = ((str[0] - 0xd800) << 10) + ((str[1]) - 0xdc00) + 0x10000; + buffer[i++] = cast(char)(0xf0 + (c >> 18)); + buffer[i++] = cast(char)(0x80 + ((c >> 12) & 0x3f)); + buffer[i++] = cast(char)(0x80 + ((c >> 6) & 0x3f)); + buffer[i++] = cast(char)(0x80 + ((c)&0x3f)); + str += 2; + } else if (*str >= 0xdc00 && *str < 0xe000) { + return NULL; + } else { + if (i + 3 > len) return NULL; + buffer[i++] = 0xe0 + (*str >> 12); + buffer[i++] = 0x80 + ((*str >> 6) & 0x3f); + buffer[i++] = 0x80 + ((*str) & 0x3f); + str += 1; + } + } + buffer[i] = 0; + return buffer; + } + + zpl_u16 *zpl_utf8_to_ucs2_buf(zpl_u8 const *str) { // NOTE: Uses locally persisting buffer + zpl_local_persist zpl_u16 buf[4096]; + return zpl_utf8_to_ucs2(buf, zpl_count_of(buf), str); + } + + zpl_u8 *zpl_ucs2_to_utf8_buf(zpl_u16 const *str) { // NOTE: Uses locally persisting buffer + zpl_local_persist zpl_u8 buf[4096]; + return zpl_ucs2_to_utf8(buf, zpl_count_of(buf), str); + } + + zpl_global zpl_u8 const zpl__utf8_first[256] = { + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x00-0x0F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x10-0x1F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x20-0x2F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x30-0x3F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x40-0x4F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x50-0x5F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x60-0x6F + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, // 0x70-0x7F + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x80-0x8F + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0x90-0x9F + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xA0-0xAF + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xB0-0xBF + 0xf1, 0xf1, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xC0-0xCF + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, // 0xD0-0xDF + 0x13, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x23, 0x03, 0x03, // 0xE0-0xEF + 0x34, 0x04, 0x04, 0x04, 0x44, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, // 0xF0-0xFF + }; + + + typedef struct zpl_utf8_accept_range { + zpl_u8 lo, hi; + } zpl_utf8_accept_range; + + zpl_global zpl_utf8_accept_range const zpl__utf8_accept_ranges[] = { + { 0x80, 0xbf }, { 0xa0, 0xbf }, { 0x80, 0x9f }, { 0x90, 0xbf }, { 0x80, 0x8f }, + }; + + zpl_isize zpl_utf8_decode(zpl_u8 const *str, zpl_isize str_len, zpl_rune *codepoint_out) { + + zpl_isize width = 0; + zpl_rune codepoint = ZPL_RUNE_INVALID; + + if (str_len > 0) { + zpl_u8 s0 = str[0]; + zpl_u8 x = zpl__utf8_first[s0], sz; + zpl_u8 b1, b2, b3; + zpl_utf8_accept_range accept; + if (x >= 0xf0) { + zpl_rune mask = (cast(zpl_rune) x << 31) >> 31; + codepoint = (cast(zpl_rune) s0 & (~mask)) | (ZPL_RUNE_INVALID & mask); + width = 1; + goto end; + } + if (s0 < 0x80) { + codepoint = s0; + width = 1; + goto end; + } + + sz = x & 7; + accept = zpl__utf8_accept_ranges[x >> 4]; + if (str_len < sz) goto invalid_codepoint; + + b1 = str[1]; + if (b1 < accept.lo || accept.hi < b1) goto invalid_codepoint; + + if (sz == 2) { + codepoint = (cast(zpl_rune) s0 & 0x1f) << 6 | (cast(zpl_rune) b1 & 0x3f); + width = 2; + goto end; + } + + b2 = str[2]; + if (!zpl_is_between(b2, 0x80, 0xbf)) goto invalid_codepoint; + + if (sz == 3) { + codepoint = (cast(zpl_rune) s0 & 0x1f) << 12 | (cast(zpl_rune) b1 & 0x3f) << 6 | (cast(zpl_rune) b2 & 0x3f); + width = 3; + goto end; + } + + b3 = str[3]; + if (!zpl_is_between(b3, 0x80, 0xbf)) goto invalid_codepoint; + + codepoint = (cast(zpl_rune) s0 & 0x07) << 18 | (cast(zpl_rune) b1 & 0x3f) << 12 | (cast(zpl_rune) b2 & 0x3f) << 6 | + (cast(zpl_rune) b3 & 0x3f); + width = 4; + goto end; + + invalid_codepoint: + codepoint = ZPL_RUNE_INVALID; + width = 1; + } + + end: + if (codepoint_out) *codepoint_out = codepoint; + return width; + } + + zpl_isize zpl_utf8_codepoint_size(zpl_u8 const *str, zpl_isize str_len) { + zpl_isize i = 0; + for (; i < str_len && str[i]; i++) { + if ((str[i] & 0xc0) != 0x80) break; + } + return i + 1; + } + + zpl_isize zpl_utf8_encode_rune(zpl_u8 buf[4], zpl_rune r) { + zpl_u32 i = cast(zpl_u32) r; + zpl_u8 mask = 0x3f; + if (i <= (1 << 7) - 1) { + buf[0] = cast(zpl_u8) r; + return 1; + } + if (i <= (1 << 11) - 1) { + buf[0] = 0xc0 | cast(zpl_u8)(r >> 6); + buf[1] = 0x80 | (cast(zpl_u8)(r) & mask); + return 2; + } + + // Invalid or Surrogate range + if (i > ZPL_RUNE_MAX || zpl_is_between(i, 0xd800, 0xdfff)) { + r = ZPL_RUNE_INVALID; + + buf[0] = 0xe0 | cast(zpl_u8)(r >> 12); + buf[1] = 0x80 | (cast(zpl_u8)(r >> 6) & mask); + buf[2] = 0x80 | (cast(zpl_u8)(r) & mask); + return 3; + } + + if (i <= (1 << 16) - 1) { + buf[0] = 0xe0 | cast(zpl_u8)(r >> 12); + buf[1] = 0x80 | (cast(zpl_u8)(r >> 6) & mask); + buf[2] = 0x80 | (cast(zpl_u8)(r) & mask); + return 3; + } + + buf[0] = 0xf0 | cast(zpl_u8)(r >> 18); + buf[1] = 0x80 | (cast(zpl_u8)(r >> 12) & mask); + buf[2] = 0x80 | (cast(zpl_u8)(r >> 6) & mask); + buf[3] = 0x80 | (cast(zpl_u8)(r) & mask); + return 4; + } + + ZPL_END_C_DECLS + // file: source/core/stringlib.c + + + ZPL_BEGIN_C_DECLS + + zpl_string zpl_string_make_reserve(zpl_allocator a, zpl_isize capacity) { + zpl_isize header_size = zpl_size_of(zpl_string_header); + void *ptr = zpl_alloc(a, header_size + capacity + 1); + + zpl_string str; + zpl_string_header *header; + + if (ptr == NULL) return NULL; + zpl_zero_size(ptr, header_size + capacity + 1); + + str = cast(char *) ptr + header_size; + header = ZPL_STRING_HEADER(str); + header->allocator = a; + header->length = 0; + header->capacity = capacity; + str[capacity] = '\0'; + + return str; + } + + + zpl_string zpl_string_make_length(zpl_allocator a, void const *init_str, zpl_isize num_bytes) { + zpl_isize header_size = zpl_size_of(zpl_string_header); + void *ptr = zpl_alloc(a, header_size + num_bytes + 1); + + zpl_string str; + zpl_string_header *header; + + if (ptr == NULL) return NULL; + if (!init_str) zpl_zero_size(ptr, header_size + num_bytes + 1); + + str = cast(char *) ptr + header_size; + header = ZPL_STRING_HEADER(str); + header->allocator = a; + header->length = num_bytes; + header->capacity = num_bytes; + if (num_bytes && init_str) zpl_memcopy(str, init_str, num_bytes); + str[num_bytes] = '\0'; + + return str; + } + + zpl_string zpl_string_sprintf_buf(zpl_allocator a, const char *fmt, ...) { + zpl_local_persist zpl_thread_local char buf[ZPL_PRINTF_MAXLEN] = { 0 }; + va_list va; + va_start(va, fmt); + zpl_snprintf_va(buf, ZPL_PRINTF_MAXLEN, fmt, va); + va_end(va); + + return zpl_string_make(a, buf); + } + + zpl_string zpl_string_sprintf(zpl_allocator a, char *buf, zpl_isize num_bytes, const char *fmt, ...) { + va_list va; + va_start(va, fmt); + zpl_snprintf_va(buf, num_bytes, fmt, va); + va_end(va); + + return zpl_string_make(a, buf); + } + + zpl_string zpl_string_append_length(zpl_string str, void const *other, zpl_isize other_len) { + if (other_len > 0) { + zpl_isize curr_len = zpl_string_length(str); + + str = zpl_string_make_space_for(str, other_len); + if (str == NULL) return NULL; + + zpl_memcopy(str + curr_len, other, other_len); + str[curr_len + other_len] = '\0'; + zpl__set_string_length(str, curr_len + other_len); + } + return str; + } + + ZPL_ALWAYS_INLINE zpl_string zpl_string_appendc(zpl_string str, const char *other) { + return zpl_string_append_length(str, other, zpl_strlen(other)); + } + + ZPL_ALWAYS_INLINE zpl_string zpl_string_join(zpl_allocator a, const char **parts, zpl_isize count, const char *glue) { + zpl_string ret; + zpl_isize i; + + ret = zpl_string_make(a, NULL); + + for (i=0; i= add_len) { + return str; + } else { + zpl_isize new_len, old_size, new_size; + void *ptr, *new_ptr; + zpl_allocator a = ZPL_STRING_HEADER(str)->allocator; + zpl_string_header *header; + + new_len = zpl_string_length(str) + add_len; + ptr = ZPL_STRING_HEADER(str); + old_size = zpl_size_of(zpl_string_header) + zpl_string_length(str) + 1; + new_size = zpl_size_of(zpl_string_header) + new_len + 1; + + new_ptr = zpl_resize(a, ptr, old_size, new_size); + if (new_ptr == NULL) return NULL; + + header = cast(zpl_string_header *) new_ptr; + header->allocator = a; + + str = cast(zpl_string)(header + 1); + zpl__set_string_capacity(str, new_len); + + return str; + } + } + + zpl_isize zpl_string_allocation_size(zpl_string const str) { + zpl_isize cap = zpl_string_capacity(str); + return zpl_size_of(zpl_string_header) + cap; + } + + zpl_b32 zpl_string_are_equal(zpl_string const lhs, zpl_string const rhs) { + zpl_isize lhs_len, rhs_len, i; + lhs_len = zpl_string_length(lhs); + rhs_len = zpl_string_length(rhs); + if (lhs_len != rhs_len) return false; + + for (i = 0; i < lhs_len; i++) { + if (lhs[i] != rhs[i]) return false; + } + + return true; + } + + zpl_string zpl_string_trim(zpl_string str, const char *cut_set) { + char *start, *end, *start_pos, *end_pos; + zpl_isize len; + + start_pos = start = str; + end_pos = end = str + zpl_string_length(str) - 1; + + while (start_pos <= end && zpl_char_first_occurence(cut_set, *start_pos)) start_pos++; + while (end_pos > start_pos && zpl_char_first_occurence(cut_set, *end_pos)) end_pos--; + + len = cast(zpl_isize)((start_pos > end_pos) ? 0 : ((end_pos - start_pos) + 1)); + + if (str != start_pos) zpl_memmove(str, start_pos, len); + str[len] = '\0'; + + zpl__set_string_length(str, len); + + return str; + } + + zpl_string zpl_string_append_rune(zpl_string str, zpl_rune r) { + if (r >= 0) { + zpl_u8 buf[8] = { 0 }; + zpl_isize len = zpl_utf8_encode_rune(buf, r); + return zpl_string_append_length(str, buf, len); + } + + return str; + } + + zpl_string zpl_string_append_fmt(zpl_string str, const char *fmt, ...) { + zpl_isize res; + char buf[ZPL_PRINTF_MAXLEN] = { 0 }; + va_list va; + va_start(va, fmt); + res = zpl_snprintf_va(buf, zpl_count_of(buf) - 1, fmt, va) - 1; + va_end(va); + return zpl_string_append_length(str, buf, res); + } + + ZPL_END_C_DECLS + // file: source/core/file.c + + + //////////////////////////////////////////////////////////////// + // + // File Handling + // + // + #include + + #ifdef ZPL_SYSTEM_MACOS + # include + #endif + + #ifdef ZPL_SYSTEM_CYGWIN + # include + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) && !defined(ZPL_COMPILER_GCC) + #include + #endif + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_SYSTEM_WINDOWS) || defined (ZPL_SYSTEM_CYGWIN) + + zpl_internal wchar_t *zpl__alloc_utf8_to_ucs2(zpl_allocator a, char const *text, zpl_isize *w_len_) { + wchar_t *w_text = NULL; + zpl_isize len = 0, w_len = 0, w_len1 = 0; + if (text == NULL) { + if (w_len_) *w_len_ = w_len; + return NULL; + } + len = zpl_strlen(text); + if (len == 0) { + if (w_len_) *w_len_ = w_len; + return NULL; + } + w_len = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, cast(int) len, NULL, 0); + if (w_len == 0) { + if (w_len_) *w_len_ = w_len; + return NULL; + } + w_text = zpl_alloc_array(a, wchar_t, w_len + 1); + w_len1 = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, text, cast(int) len, w_text, cast(int) w_len); + if (w_len1 == 0) { + zpl_free(a, w_text); + if (w_len_) *w_len_ = 0; + return NULL; + } + w_text[w_len] = 0; + if (w_len_) *w_len_ = w_len; + return w_text; + } + + zpl_internal ZPL_FILE_SEEK_PROC(zpl__win32_file_seek) { + LARGE_INTEGER li_offset; + li_offset.QuadPart = offset; + if (!SetFilePointerEx(fd.p, li_offset, &li_offset, whence)) { return false; } + + if (new_offset) *new_offset = li_offset.QuadPart; + return true; + } + + zpl_internal ZPL_FILE_READ_AT_PROC(zpl__win32_file_read) { + zpl_unused(stop_at_newline); + zpl_b32 result = false; + zpl__win32_file_seek(fd, offset, ZPL_SEEK_WHENCE_BEGIN, NULL); + DWORD size_ = cast(DWORD)(size > ZPL_I32_MAX ? ZPL_I32_MAX : size); + DWORD bytes_read_; + if (ReadFile(fd.p, buffer, size_, &bytes_read_, NULL)) { + if (bytes_read) *bytes_read = bytes_read_; + result = true; + } + + return result; + } + + zpl_internal ZPL_FILE_WRITE_AT_PROC(zpl__win32_file_write) { + DWORD size_ = cast(DWORD)(size > ZPL_I32_MAX ? ZPL_I32_MAX : size); + DWORD bytes_written_; + zpl__win32_file_seek(fd, offset, ZPL_SEEK_WHENCE_BEGIN, NULL); + if (WriteFile(fd.p, buffer, size_, &bytes_written_, NULL)) { + if (bytes_written) *bytes_written = bytes_written_; + return true; + } + return false; + } + + zpl_internal ZPL_FILE_CLOSE_PROC(zpl__win32_file_close) { CloseHandle(fd.p); } + + zpl_file_operations const zpl_default_file_operations = { zpl__win32_file_read, zpl__win32_file_write, + zpl__win32_file_seek, zpl__win32_file_close }; + + ZPL_NEVER_INLINE ZPL_FILE_OPEN_PROC(zpl__win32_file_open) { + DWORD desired_access; + DWORD creation_disposition; + void *handle; + wchar_t *w_text; + + switch (mode & ZPL_FILE_MODES) { + case ZPL_FILE_MODE_READ: + desired_access = GENERIC_READ; + creation_disposition = OPEN_EXISTING; + break; + case ZPL_FILE_MODE_WRITE: + desired_access = GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + break; + case ZPL_FILE_MODE_APPEND: + desired_access = GENERIC_WRITE; + creation_disposition = OPEN_ALWAYS; + break; + case ZPL_FILE_MODE_READ | ZPL_FILE_MODE_RW: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_EXISTING; + break; + case ZPL_FILE_MODE_WRITE | ZPL_FILE_MODE_RW: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = CREATE_ALWAYS; + break; + case ZPL_FILE_MODE_APPEND | ZPL_FILE_MODE_RW: + desired_access = GENERIC_READ | GENERIC_WRITE; + creation_disposition = OPEN_ALWAYS; + break; + default: ZPL_PANIC("Invalid file mode"); return ZPL_FILE_ERROR_INVALID; + } + + w_text = zpl__alloc_utf8_to_ucs2(zpl_heap_allocator( ), filename, NULL); + handle = CreateFileW(w_text, desired_access, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, creation_disposition, + FILE_ATTRIBUTE_NORMAL, NULL); + + zpl_free(zpl_heap_allocator( ), w_text); + + if (handle == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError( ); + switch (err) { + case ERROR_FILE_NOT_FOUND: return ZPL_FILE_ERROR_NOT_EXISTS; + case ERROR_FILE_EXISTS: return ZPL_FILE_ERROR_EXISTS; + case ERROR_ALREADY_EXISTS: return ZPL_FILE_ERROR_EXISTS; + case ERROR_ACCESS_DENIED: return ZPL_FILE_ERROR_PERMISSION; + } + return ZPL_FILE_ERROR_INVALID; + } + + if (mode & ZPL_FILE_MODE_APPEND) { + LARGE_INTEGER offset = { 0 }; + if (!SetFilePointerEx(handle, offset, NULL, ZPL_SEEK_WHENCE_END)) { + CloseHandle(handle); + return ZPL_FILE_ERROR_INVALID; + } + } + + fd->p = handle; + *ops = zpl_default_file_operations; + return ZPL_FILE_ERROR_NONE; + } + + #else // POSIX + # include + + zpl_internal ZPL_FILE_SEEK_PROC(zpl__posix_file_seek) { + # if defined(ZPL_SYSTEM_OSX) + zpl_i64 res = lseek(fd.i, offset, whence); + # else // TODO(ZaKlaus): @fixme lseek64 + zpl_i64 res = lseek(fd.i, offset, whence); + # endif + if (res < 0) return false; + if (new_offset) *new_offset = res; + return true; + } + + zpl_internal ZPL_FILE_READ_AT_PROC(zpl__posix_file_read) { + zpl_unused(stop_at_newline); + zpl_isize res = pread(fd.i, buffer, size, offset); + if (res < 0) return false; + if (bytes_read) *bytes_read = res; + return true; + } + + zpl_internal ZPL_FILE_WRITE_AT_PROC(zpl__posix_file_write) { + zpl_isize res; + zpl_i64 curr_offset = 0; + zpl__posix_file_seek(fd, 0, ZPL_SEEK_WHENCE_CURRENT, &curr_offset); + if (curr_offset == offset) { + // NOTE: Writing to stdout et al. doesn't like pwrite for numerous reasons + res = write(cast(int) fd.i, buffer, size); + } else { + res = pwrite(cast(int) fd.i, buffer, size, offset); + } + if (res < 0) return false; + if (bytes_written) *bytes_written = res; + return true; + } + + zpl_internal ZPL_FILE_CLOSE_PROC(zpl__posix_file_close) { close(fd.i); } + + zpl_file_operations const zpl_default_file_operations = { zpl__posix_file_read, zpl__posix_file_write, + zpl__posix_file_seek, zpl__posix_file_close }; + + ZPL_NEVER_INLINE ZPL_FILE_OPEN_PROC(zpl__posix_file_open) { + zpl_i32 os_mode; + switch (mode & ZPL_FILE_MODES) { + case ZPL_FILE_MODE_READ: os_mode = O_RDONLY; break; + case ZPL_FILE_MODE_WRITE: os_mode = O_WRONLY | O_CREAT | O_TRUNC; break; + case ZPL_FILE_MODE_APPEND: os_mode = O_WRONLY | O_APPEND | O_CREAT; break; + case ZPL_FILE_MODE_READ | ZPL_FILE_MODE_RW: os_mode = O_RDWR; break; + case ZPL_FILE_MODE_WRITE | ZPL_FILE_MODE_RW: os_mode = O_RDWR | O_CREAT | O_TRUNC; break; + case ZPL_FILE_MODE_APPEND | ZPL_FILE_MODE_RW: os_mode = O_RDWR | O_APPEND | O_CREAT; break; + default: ZPL_PANIC("Invalid file mode"); return ZPL_FILE_ERROR_INVALID; + } + + fd->i = open(filename, os_mode, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); + if (fd->i < 0) { + // TODO: More file errors + return ZPL_FILE_ERROR_INVALID; + } + + *ops = zpl_default_file_operations; + return ZPL_FILE_ERROR_NONE; + } + + #endif + + zpl_file_error zpl_file_new(zpl_file *f, zpl_file_descriptor fd, zpl_file_operations ops, char const *filename) { + zpl_file_error err = ZPL_FILE_ERROR_NONE; + zpl_isize len = zpl_strlen(filename); + + f->ops = ops; + f->fd = fd; + f->dir = NULL; + f->last_write_time = 0; + f->filename = zpl_alloc_array(zpl_heap_allocator( ), char, len + 1); + zpl_memcopy(cast(char *) f->filename, cast(char *) filename, len + 1); + + return err; + } + + zpl_file_error zpl_file_open_mode(zpl_file *f, zpl_file_mode mode, char const *filename) { + zpl_file file_ = {0}; + *f = file_; + zpl_file_error err; + #if defined(ZPL_SYSTEM_WINDOWS) || defined(ZPL_SYSTEM_CYGWIN) + err = zpl__win32_file_open(&f->fd, &f->ops, mode, filename); + #else + err = zpl__posix_file_open(&f->fd, &f->ops, mode, filename); + #endif + if (err == ZPL_FILE_ERROR_NONE) return zpl_file_new(f, f->fd, f->ops, filename); + return err; + } + + zpl_internal void zpl__dirinfo_free_entry(zpl_dir_entry *entry); + + zpl_file_error zpl_file_close(zpl_file *f) { + if (!f) return ZPL_FILE_ERROR_INVALID; + + if (f->filename) zpl_free(zpl_heap_allocator( ), cast(char *) f->filename); + + #if defined(ZPL_SYSTEM_WINDOWS) + if (f->fd.p == INVALID_HANDLE_VALUE) return ZPL_FILE_ERROR_INVALID; + #else + if (f->fd.i < 0) return ZPL_FILE_ERROR_INVALID; + #endif + + if (f->is_temp) + { + f->ops.close(f->fd); + return ZPL_FILE_ERROR_NONE; + } + + if (!f->ops.read_at) f->ops = zpl_default_file_operations; + f->ops.close(f->fd); + + if (f->dir) { + zpl__dirinfo_free_entry(f->dir); + zpl_mfree(f->dir); + f->dir = NULL; + } + + return ZPL_FILE_ERROR_NONE; + } + + + zpl_file_error zpl_file_create(zpl_file *f, char const *filename) { + return zpl_file_open_mode(f, ZPL_FILE_MODE_WRITE | ZPL_FILE_MODE_RW, filename); + } + + zpl_file_error zpl_file_open(zpl_file *f, char const *filename) { + return zpl_file_open_mode(f, ZPL_FILE_MODE_READ, filename); + } + + char const *zpl_file_name(zpl_file *f) { return f->filename ? f->filename : ""; } + + zpl_b32 zpl_file_has_changed(zpl_file *f) { + if (f->is_temp) + return false; + zpl_b32 result = false; + zpl_file_time last_write_time = zpl_fs_last_write_time(f->filename); + if (f->last_write_time != last_write_time) { + result = true; + f->last_write_time = last_write_time; + } + return result; + } + + // TODO: Is this a bad idea? + zpl_global zpl_b32 zpl__std_file_set = false; + zpl_global zpl_file zpl__std_files[ZPL_FILE_STANDARD_COUNT] = { { 0 } }; + + #if defined(ZPL_SYSTEM_WINDOWS) || defined(ZPL_SYSTEM_CYGWIN) + + zpl_file *zpl_file_get_standard(zpl_file_standard_type std) { + if (!zpl__std_file_set) { + #define ZPL__SET_STD_FILE(type, v) \ + zpl__std_files[type].fd.p = v; \ + zpl__std_files[type].ops = zpl_default_file_operations + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_INPUT, GetStdHandle(STD_INPUT_HANDLE)); + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_OUTPUT, GetStdHandle(STD_OUTPUT_HANDLE)); + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_ERROR, GetStdHandle(STD_ERROR_HANDLE)); + #undef ZPL__SET_STD_FILE + zpl__std_file_set = true; + } + return &zpl__std_files[std]; + } + + void zpl_file_connect_handle(zpl_file *file, void *handle) { + ZPL_ASSERT_NOT_NULL(file); + ZPL_ASSERT_NOT_NULL(handle); + + if (file->is_temp) + return; + + zpl_zero_item(file); + + file->fd.p = handle; + file->ops = zpl_default_file_operations; + } + + zpl_file_error zpl_file_truncate(zpl_file *f, zpl_i64 size) { + zpl_file_error err = ZPL_FILE_ERROR_NONE; + zpl_i64 prev_offset = zpl_file_tell(f); + zpl_file_seek(f, size); + if (!SetEndOfFile(f)) err = ZPL_FILE_ERROR_TRUNCATION_FAILURE; + zpl_file_seek(f, prev_offset); + return err; + } + + zpl_b32 zpl_fs_exists(char const *name) { + WIN32_FIND_DATAW data; + wchar_t *w_text; + void *handle; + zpl_b32 found = false; + zpl_allocator a = zpl_heap_allocator( ); + + w_text = zpl__alloc_utf8_to_ucs2(a, name, NULL); + if (w_text == NULL) { return false; } + handle = FindFirstFileW(w_text, &data); + zpl_free(a, w_text); + found = handle != INVALID_HANDLE_VALUE; + if (found) FindClose(handle); + return found; + } + + #else // POSIX + + zpl_file *zpl_file_get_standard(zpl_file_standard_type std) { + if (!zpl__std_file_set) { + #define ZPL__SET_STD_FILE(type, v) \ + zpl__std_files[type].fd.i = v; \ + zpl__std_files[type].ops = zpl_default_file_operations + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_INPUT, 0); + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_OUTPUT, 1); + ZPL__SET_STD_FILE(ZPL_FILE_STANDARD_ERROR, 2); + #undef ZPL__SET_STD_FILE + zpl__std_file_set = true; + } + return &zpl__std_files[std]; + } + + zpl_file_error zpl_file_truncate(zpl_file *f, zpl_i64 size) { + zpl_file_error err = ZPL_FILE_ERROR_NONE; + int i = ftruncate(f->fd.i, size); + if (i != 0) err = ZPL_FILE_ERROR_TRUNCATION_FAILURE; + return err; + } + + zpl_b32 zpl_fs_exists(char const *name) { return access(name, F_OK) != -1; } + + #endif + + zpl_i64 zpl_file_size(zpl_file *f) { + zpl_i64 size = 0; + zpl_i64 prev_offset = zpl_file_tell(f); + zpl_file_seek_to_end(f); + size = zpl_file_tell(f); + zpl_file_seek(f, prev_offset); + return size; + } + + zpl_file_error zpl_file_temp(zpl_file *file) { + zpl_zero_item(file); + FILE *fd = NULL; + + #if (defined(ZPL_SYSTEM_WINDOWS) && !defined(ZPL_SYSTEM_TINYC)) && !defined(ZPL_COMPILER_GCC) + errno_t errcode = tmpfile_s(&fd); + + if (errcode != 0) { + fd = NULL; + } + #else + fd = tmpfile(); + #endif + + if (fd == NULL) { return ZPL_FILE_ERROR_INVALID; } + + #if defined(ZPL_SYSTEM_WINDOWS) && !defined(ZPL_COMPILER_GCC) + file->fd.i = _get_osfhandle(_fileno(fd)); + #else + file->fd.i = fileno(fd); + #endif + file->ops = zpl_default_file_operations; + file->is_temp = true; + return ZPL_FILE_ERROR_NONE; + } + + zpl_file_contents zpl_file_read_contents(zpl_allocator a, zpl_b32 zero_terminate, char const *filepath) { + zpl_file_contents result = { 0 }; + zpl_file file = { 0 }; + + result.allocator = a; + + zpl_u8 entry_type = zpl_fs_get_type(filepath); + + /* ignore folders */ + if (entry_type == ZPL_DIR_TYPE_FOLDER) { + return result; + } + + if (zpl_file_open(&file, filepath) == ZPL_FILE_ERROR_NONE) { + zpl_isize file_size = cast(zpl_isize) zpl_file_size(&file); + if (file_size > 0) { + result.data = zpl_alloc(a, zero_terminate ? file_size + 1 : file_size); + result.size = file_size; + zpl_file_read_at(&file, result.data, result.size, 0); + if (zero_terminate) { + zpl_u8 *str = cast(zpl_u8 *) result.data; + str[file_size] = '\0'; + } + } + zpl_file_close(&file); + } + + return result; + } + + void zpl_file_free_contents(zpl_file_contents *fc) { + ZPL_ASSERT_NOT_NULL(fc->data); + zpl_free(fc->allocator, fc->data); + fc->data = NULL; + fc->size = 0; + } + + zpl_b32 zpl_file_write_contents(char const* filepath, void const* buffer, zpl_isize size, zpl_file_error* err) { + zpl_file f = { 0 }; + zpl_file_error open_err; + zpl_b32 write_ok; + open_err = zpl_file_open_mode(&f, ZPL_FILE_MODE_WRITE, filepath); + + if (open_err != ZPL_FILE_ERROR_NONE) + { + if (err) + *err = open_err; + + return false; + } + + write_ok = zpl_file_write(&f, buffer, size); + zpl_file_close(&f); + return write_ok; + } + + char *zpl_file_read_lines(zpl_allocator alloc, zpl_array(char *)*lines, char const *filename, zpl_b32 strip_whitespace) { + zpl_file f = { 0 }; + zpl_file_open(&f, filename); + zpl_isize fsize = (zpl_isize)zpl_file_size(&f); + + char *contents = (char *)zpl_alloc(alloc, fsize + 1); + zpl_file_read(&f, contents, fsize); + contents[fsize] = 0; + *lines = zpl_str_split_lines(alloc, contents, strip_whitespace); + zpl_file_close(&f); + + return contents; + } + + #if !defined(_WINDOWS_) && defined(ZPL_SYSTEM_WINDOWS) + ZPL_IMPORT DWORD WINAPI GetFullPathNameA(char const *lpFileName, DWORD nBufferLength, char *lpBuffer, char **lpFilePart); + ZPL_IMPORT DWORD WINAPI GetFullPathNameW(wchar_t const *lpFileName, DWORD nBufferLength, wchar_t *lpBuffer, wchar_t **lpFilePart); + #endif + + ZPL_END_C_DECLS + // file: source/core/file_stream.c + + + //////////////////////////////////////////////////////////////// + // + // Memory streaming + // + // + + ZPL_BEGIN_C_DECLS + + typedef struct { + zpl_u8 magic; + zpl_u8 *buf; //< zpl_array OR plain buffer if we can't write + zpl_isize cursor; + zpl_allocator alloc; + + zpl_file_stream_flags flags; + zpl_isize cap; + } zpl__memory_fd; + + #define ZPL__FILE_STREAM_FD_MAGIC 37 + + ZPL_DEF_INLINE zpl_file_descriptor zpl__file_stream_fd_make(zpl__memory_fd* d); + ZPL_DEF_INLINE zpl__memory_fd *zpl__file_stream_from_fd(zpl_file_descriptor fd); + + ZPL_IMPL_INLINE zpl_file_descriptor zpl__file_stream_fd_make(zpl__memory_fd* d) { + zpl_file_descriptor fd = {0}; + fd.p = (void*)d; + return fd; + } + + ZPL_IMPL_INLINE zpl__memory_fd *zpl__file_stream_from_fd(zpl_file_descriptor fd) { + zpl__memory_fd *d = (zpl__memory_fd*)fd.p; + ZPL_ASSERT(d->magic == ZPL__FILE_STREAM_FD_MAGIC); + return d; + } + + zpl_b8 zpl_file_stream_new(zpl_file* file, zpl_allocator allocator) { + ZPL_ASSERT_NOT_NULL(file); + zpl__memory_fd *d = (zpl__memory_fd*)zpl_alloc(allocator, zpl_size_of(zpl__memory_fd)); + if (!d) return false; + zpl_zero_item(file); + d->magic = ZPL__FILE_STREAM_FD_MAGIC; + d->alloc = allocator; + d->flags = ZPL_FILE_STREAM_CLONE_WRITABLE; + d->cap = 0; + if (!zpl_array_init(d->buf, allocator)) return false; + file->ops = zpl_memory_file_operations; + file->fd = zpl__file_stream_fd_make(d); + file->dir = NULL; + file->last_write_time = 0; + file->filename = NULL; + file->is_temp = true; + return true; + } + zpl_b8 zpl_file_stream_open(zpl_file* file, zpl_allocator allocator, zpl_u8 *buffer, zpl_isize size, zpl_file_stream_flags flags) { + ZPL_ASSERT_NOT_NULL(file); + zpl__memory_fd *d = (zpl__memory_fd*)zpl_alloc(allocator, zpl_size_of(zpl__memory_fd)); + if (!d) return false; + zpl_zero_item(file); + d->magic = ZPL__FILE_STREAM_FD_MAGIC; + d->alloc = allocator; + d->flags = flags; + if (d->flags & ZPL_FILE_STREAM_CLONE_WRITABLE) { + if (!zpl_array_init_reserve(d->buf, allocator, size)) return false; + zpl_memcopy(d->buf, buffer, size); + d->cap = zpl_array_count(d->buf) = size; + } else { + d->buf = buffer; + d->cap = size; + } + file->ops = zpl_memory_file_operations; + file->fd = zpl__file_stream_fd_make(d); + file->dir = NULL; + file->last_write_time = 0; + file->filename = NULL; + file->is_temp = true; + return true; + } + + zpl_u8 *zpl_file_stream_buf(zpl_file* file, zpl_isize *size) { + ZPL_ASSERT_NOT_NULL(file); + zpl__memory_fd *d = zpl__file_stream_from_fd(file->fd); + if (size) *size = d->cap; + return d->buf; + } + + zpl_internal ZPL_FILE_SEEK_PROC(zpl__memory_file_seek) { + zpl__memory_fd *d = zpl__file_stream_from_fd(fd); + zpl_isize buflen = d->cap; + + if (whence == ZPL_SEEK_WHENCE_BEGIN) + d->cursor = 0; + else if (whence == ZPL_SEEK_WHENCE_END) + d->cursor = buflen; + + d->cursor = zpl_max(0, zpl_clamp(d->cursor + offset, 0, buflen)); + if (new_offset) *new_offset = d->cursor; + return true; + } + + zpl_internal ZPL_FILE_READ_AT_PROC(zpl__memory_file_read) { + zpl_unused(stop_at_newline); + zpl__memory_fd *d = zpl__file_stream_from_fd(fd); + zpl_memcopy(buffer, d->buf + offset, size); + if (bytes_read) *bytes_read = size; + return true; + } + + zpl_internal ZPL_FILE_WRITE_AT_PROC(zpl__memory_file_write) { + zpl__memory_fd *d = zpl__file_stream_from_fd(fd); + if (!(d->flags & (ZPL_FILE_STREAM_CLONE_WRITABLE|ZPL_FILE_STREAM_WRITABLE))) + return false; + zpl_isize buflen = d->cap; + zpl_isize extralen = zpl_max(0, size-(buflen-offset)); + zpl_isize rwlen = size-extralen; + zpl_isize new_cap = buflen+extralen; + if (d->flags & ZPL_FILE_STREAM_CLONE_WRITABLE) { + if(zpl_array_capacity(d->buf) < new_cap) { + if (!zpl_array_grow(d->buf, (zpl_i64)(new_cap))) return false; + } + } + zpl_memcopy(d->buf + offset, buffer, rwlen); + + if ((d->flags & ZPL_FILE_STREAM_CLONE_WRITABLE) && extralen > 0) { + zpl_memcopy(d->buf + offset + rwlen, zpl_ptr_add_const(buffer, rwlen), extralen); + d->cap = zpl_array_count(d->buf) = new_cap; + } else { + extralen = 0; + } + + if (bytes_written) *bytes_written = (rwlen+extralen); + return true; + } + + zpl_internal ZPL_FILE_CLOSE_PROC(zpl__memory_file_close) { + zpl__memory_fd *d = zpl__file_stream_from_fd(fd); + zpl_allocator alloc = d->alloc; + if (d->flags & ZPL_FILE_STREAM_CLONE_WRITABLE) + zpl_array_free(d->buf); + zpl_free(alloc, d); + } + + zpl_file_operations const zpl_memory_file_operations = { zpl__memory_file_read, zpl__memory_file_write, + zpl__memory_file_seek, zpl__memory_file_close }; + + ZPL_END_C_DECLS + // file: source/core/file_misc.c + + + #if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_MACOS) + # include + #endif + + #if defined(ZPL_SYSTEM_UNIX) && !defined(ZPL_SYSTEM_FREEBSD) && !defined(ZPL_SYSTEM_OPENBSD) && !defined(ZPL_SYSTEM_CYGWIN) && !defined(ZPL_SYSTEM_EMSCRIPTEN) + # include + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) + # include + # include + #endif + + #if defined(ZPL_SYSTEM_CYGWIN) + # include + # include + # include + #endif + + ZPL_BEGIN_C_DECLS + + + #if defined(ZPL_SYSTEM_WINDOWS) || defined(ZPL_SYSTEM_CYGWIN) + zpl_file_time zpl_fs_last_write_time(char const *filepath) { + ULARGE_INTEGER li = { 0 }; + FILETIME last_write_time = { 0 }; + WIN32_FILE_ATTRIBUTE_DATA data = { 0 }; + zpl_allocator a = zpl_heap_allocator( ); + + wchar_t *w_text = zpl__alloc_utf8_to_ucs2(a, filepath, NULL); + if (w_text == NULL) { return 0; } + if (GetFileAttributesExW(w_text, GetFileExInfoStandard, &data)) last_write_time = data.ftLastWriteTime; + + zpl_free(a, w_text); + + li.LowPart = last_write_time.dwLowDateTime; + li.HighPart = last_write_time.dwHighDateTime; + return cast(zpl_file_time) li.QuadPart; + } + + zpl_b32 zpl_fs_copy(char const *existing_filename, char const *new_filename, zpl_b32 fail_if_exists) { + zpl_b32 result = false; + zpl_allocator a = zpl_heap_allocator( ); + + wchar_t *w_old = zpl__alloc_utf8_to_ucs2(a, existing_filename, NULL); + if (w_old == NULL) { return false; } + + wchar_t *w_new = zpl__alloc_utf8_to_ucs2(a, new_filename, NULL); + if (w_new != NULL) { result = CopyFileW(w_old, w_new, fail_if_exists); } + + zpl_free(a, w_old); + zpl_free(a, w_new); + return result; + } + + zpl_b32 zpl_fs_move(char const *existing_filename, char const *new_filename) { + zpl_b32 result = false; + zpl_allocator a = zpl_heap_allocator( ); + + wchar_t *w_old = zpl__alloc_utf8_to_ucs2(a, existing_filename, NULL); + if (w_old == NULL) { return false; } + + wchar_t *w_new = zpl__alloc_utf8_to_ucs2(a, new_filename, NULL); + if (w_new != NULL) { result = MoveFileW(w_old, w_new); } + + zpl_free(a, w_old); + zpl_free(a, w_new); + return result; + } + + zpl_b32 zpl_fs_remove(char const *filename) { + zpl_b32 result = false; + zpl_allocator a = zpl_heap_allocator( ); + + wchar_t *w_filename = zpl__alloc_utf8_to_ucs2(a, filename, NULL); + if (w_filename == NULL) { return false; } + + result = DeleteFileW(w_filename); + + zpl_free(a, w_filename); + return result; + } + + #else + + zpl_file_time zpl_fs_last_write_time(char const *filepath) { + time_t result = 0; + struct stat file_stat; + + if (stat(filepath, &file_stat)) result = file_stat.st_mtime; + + return cast(zpl_file_time) result; + } + + # if defined(ZPL_SYSTEM_FREEBSD) + # include + # include + # include + # endif + + + zpl_b32 zpl_fs_copy(char const *existing_filename, char const *new_filename, zpl_b32 fail_if_exists) { + zpl_unused(fail_if_exists); + # if defined(ZPL_SYSTEM_OSX) + return copyfile(existing_filename, new_filename, NULL, COPYFILE_DATA) == 0; + # elif defined(ZPL_SYSTEM_OPENBSD) + ZPL_NOT_IMPLEMENTED; + return 0; + # elif defined(ZPL_SYSTEM_EMSCRIPTEN) + ZPL_NOT_IMPLEMENTED; + return 0; + # else + int existing_fd = open(existing_filename, O_RDONLY, 0); + struct stat stat_existing; + fstat(existing_fd, &stat_existing); + + zpl_isize size; + int new_fd = open(new_filename, O_WRONLY | O_CREAT, stat_existing.st_mode); + + # if defined(ZPL_SYSTEM_FREEBSD) + size = sendfile(new_fd, existing_fd, 0, stat_existing.st_size, NULL, 0, 0); + # else + size = sendfile(new_fd, existing_fd, 0, stat_existing.st_size); + # endif + + close(new_fd); + close(existing_fd); + + return size == stat_existing.st_size; + # endif + } + + zpl_b32 zpl_fs_move(char const *existing_filename, char const *new_filename) { + if (link(existing_filename, new_filename) == 0) { return (unlink(existing_filename) != -1); } + return false; + } + + zpl_b32 zpl_fs_remove(char const *filename) { + # if defined(ZPL_SYSTEM_OSX) || defined(ZPL_SYSTEM_EMSCRIPTEN) + return (unlink(filename) != -1); + # else + return (remove(filename) == 0); + # endif + } + + #endif + + char *zpl_path_get_full_name(zpl_allocator a, char const *path) { + #if defined(ZPL_SYSTEM_WINDOWS) + wchar_t *w_path = NULL; + wchar_t *w_fullpath = NULL; + zpl_isize w_len = 0; + zpl_isize new_len = 0; + zpl_isize new_len1 = 0; + char *new_path = 0; + + w_path = zpl__alloc_utf8_to_ucs2(zpl_heap_allocator( ), path, NULL); + if (w_path == NULL) { return NULL; } + + w_len = GetFullPathNameW(w_path, 0, NULL, NULL); + if (w_len == 0) { return NULL; } + + w_fullpath = zpl_alloc_array(zpl_heap_allocator( ), wchar_t, w_len + 1); + GetFullPathNameW(w_path, cast(int) w_len, w_fullpath, NULL); + w_fullpath[w_len] = 0; + + zpl_free(zpl_heap_allocator( ), w_path); + + new_len = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w_fullpath, cast(int) w_len, NULL, 0, NULL, NULL); + + if (new_len == 0) { + zpl_free(zpl_heap_allocator( ), w_fullpath); + return NULL; + } + + new_path = zpl_alloc_array(a, char, new_len); + new_len1 = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, w_fullpath, cast(int) w_len, new_path, + cast(int) new_len, NULL, NULL); + + if (new_len1 == 0) { + zpl_free(zpl_heap_allocator( ), w_fullpath); + zpl_free(a, new_path); + return NULL; + } + + new_path[new_len] = 0; + return new_path; + #else + char *p, *result, *fullpath = NULL; + zpl_isize len; + p = realpath(path, NULL); + fullpath = p; + if (p == NULL) { + // NOTE(bill): File does not exist + fullpath = cast(char *) path; + } + + len = zpl_strlen(fullpath); + + result = zpl_alloc_array(a, char, len + 1); + zpl_memmove(result, fullpath, len); + result[len] = 0; + zpl_free(a, p); + + return result; + #endif + } + + zpl_file_error zpl_path_mkdir(char const *path, zpl_i32 mode) { + zpl_i32 error = 0; + #if defined(ZPL_SYSTEM_WINDOWS) + error = _wmkdir((const wchar_t *)zpl_utf8_to_ucs2_buf((const zpl_u8 *)path)); + #else + error = mkdir(path, (mode_t)mode); + #endif + + if (error == 0) { return ZPL_FILE_ERROR_NONE; } + + switch (errno) { + case EPERM: + case EACCES: return ZPL_FILE_ERROR_PERMISSION; + case EEXIST: return ZPL_FILE_ERROR_EXISTS; + case ENAMETOOLONG: return ZPL_FILE_ERROR_NAME_TOO_LONG; + } + + return ZPL_FILE_ERROR_UNKNOWN; + } + + zpl_isize zpl_path_mkdir_recursive(char const *path, zpl_i32 mode) { + char tmp[ZPL_MAX_PATH] = {0}; + char *p = 0; + zpl_isize len = zpl_strlen(path); + + if (len > zpl_size_of(tmp)-1) { + return -1; + } + zpl_strcpy(tmp, path); + zpl_path_fix_slashes(tmp); + for (p = tmp + 1; *p; p++) { + if (*p == ZPL_PATH_SEPARATOR) { + *p = 0; + zpl_path_mkdir(tmp, mode); + *p = ZPL_PATH_SEPARATOR; + } + } + zpl_path_mkdir(tmp, mode); + return 0; + } + + zpl_file_error zpl_path_rmdir(char const *path) { + zpl_i32 error = 0; + #if defined(ZPL_SYSTEM_WINDOWS) + error = _wrmdir((const wchar_t *)zpl_utf8_to_ucs2_buf((const zpl_u8 *)path)); + #else + error = rmdir(path); + #endif + + if (error == 0) { return ZPL_FILE_ERROR_NONE; } + + switch (errno) { + case EPERM: + case EACCES: return ZPL_FILE_ERROR_PERMISSION; + case ENOENT: return ZPL_FILE_ERROR_NOT_EXISTS; + case ENOTEMPTY: return ZPL_FILE_ERROR_NOT_EMPTY; + case ENAMETOOLONG: return ZPL_FILE_ERROR_NAME_TOO_LONG; + } + + return ZPL_FILE_ERROR_UNKNOWN; + } + + void zpl__file_direntry(zpl_allocator alloc, char const *dirname, zpl_string *output, zpl_b32 recurse) { + #if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_OSX) + DIR *d, *cd; + struct dirent *dir; + d = opendir(dirname); + + if (d) { + while ((dir = readdir(d))) { + if (dir == 0) break; + if (!zpl_strncmp(dir->d_name, "..", 2)) continue; + if (dir->d_name[0] == '.' && dir->d_name[1] == 0) continue; + + zpl_string dirpath = zpl_string_make(alloc, dirname); + dirpath = zpl_string_appendc(dirpath, "/"); + dirpath = zpl_string_appendc(dirpath, dir->d_name); + + *output = zpl_string_appendc(*output, dirpath); + *output = zpl_string_appendc(*output, "\n"); + + if (recurse && (cd = opendir(dirpath)) != NULL && dir->d_type == DT_DIR) { zpl__file_direntry(alloc, dirpath, output, recurse); } + zpl_string_free(dirpath); + } + } + #elif defined(ZPL_SYSTEM_WINDOWS) + zpl_usize length = zpl_strlen(dirname); + struct _wfinddata_t data; + zpl_intptr findhandle; + + char directory[MAX_PATH] = { 0 }; + zpl_strncpy(directory, dirname, length); + + // keeping it native + for (zpl_usize i = 0; i < length; i++) { + if (directory[i] == '/') directory[i] = '\\'; + } + + // remove trailing slashses + if (directory[length - 1] == '\\') { directory[length - 1] = '\0'; } + + // attach search pattern + zpl_string findpath = zpl_string_make(alloc, directory); + findpath = zpl_string_appendc(findpath, "\\"); + findpath = zpl_string_appendc(findpath, "*"); + + findhandle = _wfindfirst((const wchar_t *)zpl_utf8_to_ucs2_buf((const zpl_u8 *)findpath), &data); + zpl_string_free(findpath); + + if (findhandle != -1) { + do { + char *filename = (char *)zpl_ucs2_to_utf8_buf((const zpl_u16 *)data.name); + if (!zpl_strncmp(filename, "..", 2)) continue; + if (filename[0] == '.' && filename[1] == 0) continue; + + zpl_string dirpath = zpl_string_make(alloc, directory); + dirpath = zpl_string_appendc(dirpath, "\\"); + dirpath = zpl_string_appendc(dirpath, filename); + DWORD attrs = GetFileAttributesW((const wchar_t *)zpl_utf8_to_ucs2_buf((const zpl_u8 *)dirpath)); + + *output = zpl_string_appendc(*output, dirpath); + *output = zpl_string_appendc(*output, "\n"); + + if (recurse && (data.attrib & _A_SUBDIR) && !(attrs & FILE_ATTRIBUTE_REPARSE_POINT)) { zpl__file_direntry(alloc, dirpath, output, recurse); } + + zpl_string_free(dirpath); + } while (_wfindnext(findhandle, &data) != -1); + _findclose(findhandle); + } + #else + // TODO: Implement other OSes + #endif + } + + zpl_string zpl_path_dirlist(zpl_allocator alloc, char const *dirname, zpl_b32 recurse) { + zpl_string buf = zpl_string_make_reserve(alloc, 4); + zpl__file_direntry(alloc, dirname, &buf, recurse); + return buf; + } + + void zpl_dirinfo_init(zpl_dir_info *dir, char const *path) { + ZPL_ASSERT_NOT_NULL(dir); + + zpl_dir_info dir_ = {0}; + *dir = dir_; + dir->fullpath = (char const*)zpl_malloc(zpl_strlen(path)); + zpl_strcpy((char *)dir->fullpath, path); + + + zpl_string dirlist = zpl_path_dirlist(zpl_heap(), path, false); + char **files=zpl_str_split_lines(zpl_heap(), dirlist, false); + dir->filenames = files; + dir->buf = dirlist; + + zpl_array_init(dir->entries, zpl_heap()); + + for (zpl_i32 i=0; ientries, entry); + } + } + + zpl_internal void zpl__dirinfo_free_entry(zpl_dir_entry *entry) { + if (entry->dir_info) { + zpl_dirinfo_free(entry->dir_info); + zpl_mfree(entry->dir_info); + entry->dir_info = NULL; + } + } + + void zpl_dirinfo_free(zpl_dir_info *dir) { + ZPL_ASSERT_NOT_NULL(dir); + + for (zpl_isize i = 0; i < zpl_array_count(dir->entries); ++i) { + zpl__dirinfo_free_entry(dir->entries + i); + } + + zpl_array_free(dir->entries); + zpl_array_free(dir->filenames); + zpl_string_free(dir->buf); + zpl_mfree((void *)dir->fullpath); + } + + + zpl_u8 zpl_fs_get_type(char const *path) { + #ifdef ZPL_SYSTEM_WINDOWS + DWORD attrs = GetFileAttributesW((const wchar_t *)zpl_utf8_to_ucs2_buf((const zpl_u8 *)path)); + + if (attrs == INVALID_FILE_ATTRIBUTES) { + return ZPL_DIR_TYPE_UNKNOWN; + } + + if (attrs & FILE_ATTRIBUTE_DIRECTORY) + return ZPL_DIR_TYPE_FOLDER; + else + return ZPL_DIR_TYPE_FILE; + + #else + struct stat s; + if( stat(path,&s) == 0 ) + { + if(s.st_mode & S_IFDIR) + return ZPL_DIR_TYPE_FOLDER; + else + return ZPL_DIR_TYPE_FILE; + } + #endif + + return ZPL_DIR_TYPE_UNKNOWN; + } + + void zpl_dirinfo_step(zpl_dir_entry *entry) { + if (entry->dir_info) { + zpl__dirinfo_free_entry(entry); + } + + entry->dir_info = (zpl_dir_info *)zpl_malloc(sizeof(zpl_dir_info)); + zpl_dir_info dir_ = {0}; + *entry->dir_info = dir_; + + zpl_local_persist char buf[128] = {0}; + char const *path = entry->filename; + + if (entry->type != ZPL_DIR_TYPE_FOLDER) { + zpl_path_fix_slashes((char *)path); + char const* slash = zpl_char_last_occurence(path, ZPL_PATH_SEPARATOR); + zpl_strncpy(buf, path, slash-path); + path = buf; + } + + zpl_dirinfo_init(entry->dir_info, path); + } + + void zpl_file_dirinfo_refresh(zpl_file *file) { + if (file->is_temp) + return; + + if (file->dir) { + zpl__dirinfo_free_entry(file->dir); + zpl_mfree(file->dir); + file->dir = NULL; + } + + file->dir = (zpl_dir_entry *)zpl_malloc(sizeof(zpl_dir_entry)); + zpl_dir_entry dir_ = {0}; + *file->dir = dir_; + file->dir->filename = file->filename; + file->dir->type = ZPL_DIR_TYPE_FILE; + + zpl_dirinfo_step(file->dir); + } + + void zpl_path_fix_slashes(char *path) { + #ifdef ZPL_SYSTEM_WINDOWS + char *p = path; + + while (*p != '\0') { + if (*p == '/') + *p = '\\'; + + ++p; + } + #endif + } + + ZPL_END_C_DECLS + // file: source/core/file_tar.c + + + typedef struct { + char name[100]; + char mode[8]; + char owner[8]; + char group[8]; + char size[12]; + char mtime[12]; + char checksum[8]; + char type; + char linkname[100]; + char _padding[255]; + } zpl__tar_header; + + zpl_internal zpl_usize zpl__tar_checksum(zpl__tar_header *hr) { + zpl_usize i; + zpl_usize res = 256; + zpl_u8 *p = cast(zpl_u8*)(hr); + for (i = 0; i < cast(zpl_usize)zpl_offset_of(zpl__tar_header, checksum); i++) + res += p[i]; + for (i = cast(zpl_usize)zpl_offset_of(zpl__tar_header, type); i < cast(zpl_usize)zpl_size_of(zpl__tar_header); i++) + res += p[i]; + return res; + } + + zpl_internal zpl_b32 zpl__tar_write_null(zpl_file *archive, zpl_isize cnt) { + char *out = zpl_bprintf("%*r", cnt, '\0'); + if (!zpl_file_write(archive, out, cnt)) + return 0; + return 1; + } + + zpl_isize zpl_tar_pack(zpl_file *archive, char const **paths, zpl_isize paths_len) { + ZPL_ASSERT_NOT_NULL(archive); + ZPL_ASSERT_NOT_NULL(paths); + + for (zpl_isize i = 0; i < paths_len; i++) { + ZPL_ASSERT_NOT_NULL(paths[i]); + zpl__tar_header hr = {0}; + zpl_file file; + zpl_file_error ferr = zpl_file_open_mode(&file, ZPL_FILE_MODE_READ, paths[i]); + if (ferr == ZPL_FILE_ERROR_NOT_EXISTS) { + return -(ZPL_TAR_ERROR_FILE_NOT_FOUND); + } else if (ferr != ZPL_FILE_ERROR_NONE) { + return -(ZPL_TAR_ERROR_IO_ERROR); + } + + zpl_i64 file_size = zpl_file_size(&file); + zpl_snprintf(hr.name, 12, "%s", paths[i]); + zpl_snprintf(hr.size, 12, "%o", file_size); + zpl_snprintf(hr.mode, 8, "%o", 0664); + zpl_snprintf(hr.mtime, 12, "%o", zpl_fs_last_write_time(paths[i])); + hr.type = ZPL_TAR_TYPE_REGULAR; + zpl_snprintf(hr.checksum, 8, "%o", zpl__tar_checksum(&hr)); + + zpl_file_write(archive, cast(void*)(&hr), zpl_size_of(zpl__tar_header)); + + // write data + { + zpl_i64 remaining_data = file_size; + zpl_i64 total_data = zpl_align_forward_i64(remaining_data, 512); + zpl_i64 padding = (total_data-file_size); + char buf[4096] = {0}; + zpl_i64 pos = 0; + zpl_isize bytes_read = 0; + do { + if (!zpl_file_read_at_check(&file, buf, 4096, pos, &bytes_read)) { + zpl_file_close(&file); + return -(ZPL_TAR_ERROR_IO_ERROR); + } else if (bytes_read == 0) { + break; + } + + zpl_file_write(archive, buf, bytes_read); + pos += bytes_read; + remaining_data -= bytes_read; + } + while (remaining_data > 0); + + if (padding > 0) { + if (!zpl__tar_write_null(archive, padding)) { + zpl_file_close(&file); + return -(ZPL_TAR_ERROR_IO_ERROR); + } + } + } + + zpl_file_close(&file); + } + + if (!zpl__tar_write_null(archive, zpl_size_of(zpl__tar_header) * 2)) { + return -(ZPL_TAR_ERROR_IO_ERROR); + } + + return 0; + } + + zpl_isize zpl_tar_pack_dir(zpl_file *archive, char const *path, zpl_allocator alloc) { + zpl_string filelst = zpl_path_dirlist(alloc, path, true); + char const **files = cast(char const**)zpl_str_split_lines(alloc, filelst, false); + zpl_isize err = zpl_tar_pack(archive, files, zpl_array_count(files)); + zpl_string_free(filelst); + zpl_array_free(files); + return err; + } + + zpl_isize zpl_tar_unpack(zpl_file *archive, zpl_tar_unpack_proc *unpack_proc, void *user_data) { + ZPL_ASSERT_NOT_NULL(archive); + ZPL_ASSERT_NOT_NULL(unpack_proc); + + zpl_i64 pos = zpl_file_tell(archive); + zpl__tar_header hr = {0}; + zpl_isize err = ZPL_TAR_ERROR_NONE; + + do { + if (!zpl_file_read(archive, cast(void*)&hr, zpl_size_of(hr))) { + err = ZPL_TAR_ERROR_IO_ERROR; + break; + } + else if (*hr.checksum == 0) { + break; + } + pos = zpl_file_tell(archive); + + zpl_tar_record rec = {0}; + rec.type = hr.type; + rec.path = hr.name; + rec.offset = pos; + rec.length = zpl_str_to_i64(hr.size, 0, 8); + rec.error = ZPL_TAR_ERROR_NONE; + + zpl_usize checksum1 = cast(zpl_usize)(zpl_str_to_i64(hr.checksum, 0, 8)); + zpl_usize checksum2 = zpl__tar_checksum(&hr); + rec.error = (checksum1 != checksum2) ? cast(zpl_isize)ZPL_TAR_ERROR_BAD_CHECKSUM : rec.error; + + rec.error = unpack_proc(archive, &rec, user_data); + + if (rec.error > 0) { + err = ZPL_TAR_ERROR_INTERRUPTED; + break; + } + + /* tar rounds files to 512 byte boundary */ + zpl_file_seek(archive, pos + zpl_align_forward_i64(rec.length, 512)); + } + while(err == ZPL_TAR_ERROR_NONE); + + return -(err); + } + + ZPL_TAR_UNPACK_PROC(zpl_tar_default_list_file) { + (void)archive; + (void)user_data; + if (file->error != ZPL_TAR_ERROR_NONE) + return 0; /* skip file */ + + if (file->type != ZPL_TAR_TYPE_REGULAR) + return 0; /* we only care about regular files */ + + /* proceed as usual */ + zpl_printf("name: %s, offset: %d, length: %d\n", file->path, file->offset, file->length); + return 0; + } + + ZPL_TAR_UNPACK_PROC(zpl_tar_default_unpack_file) { + if (file->error != ZPL_TAR_ERROR_NONE) + return 0; /* skip file */ + + if (file->type != ZPL_TAR_TYPE_REGULAR) + return 0; /* we only care about regular files */ + + if (!zpl_strncmp(file->path, "..", 2)) + return 0; + + char tmp[ZPL_MAX_PATH] = {0}; + char *base_path = cast(char*)user_data; + zpl_isize base_len = zpl_strlen(base_path); + zpl_isize len = zpl_strlen(file->path); + ZPL_ASSERT(base_len+len-2 < ZPL_MAX_PATH); /* todo: account for missing leading path sep */ + + zpl_strcpy(tmp, base_path); + zpl_path_fix_slashes(tmp); /* todo: need to do twice as base_path is checked before concat */ + + if (*tmp && tmp[base_len-1] != ZPL_PATH_SEPARATOR) { + char sep[2] = {ZPL_PATH_SEPARATOR, 0}; + zpl_strcat(tmp, sep); + } + zpl_strcat(tmp, file->path); + zpl_path_fix_slashes(tmp); + + const char *last_slash = zpl_char_last_occurence(tmp, ZPL_PATH_SEPARATOR); + + if (last_slash) { + zpl_isize i = cast(zpl_isize)(last_slash-tmp); + tmp[i] = 0; + zpl_path_mkdir_recursive(tmp, 0755); + tmp[i] = ZPL_PATH_SEPARATOR; + } + + zpl_file f; + zpl_file_create(&f, tmp); + { + char buf[4096] = {0}; + zpl_isize remaining_data = file->length; + zpl_isize bytes_read = 0; + zpl_i64 pos = file->offset; + do { + if (!zpl_file_read_at_check(archive, buf, zpl_min(4096, remaining_data), pos, &bytes_read)) { + zpl_file_close(&f); + return 1; + } else if (bytes_read == 0) { + break; + } + + zpl_file_write(&f, buf, bytes_read); + pos += bytes_read; + remaining_data -= bytes_read; + } + while (remaining_data > 0); + } + zpl_file_close(&f); + return 0; + } + // file: source/core/print.c + + + ZPL_BEGIN_C_DECLS + + zpl_isize zpl_printf_va(char const *fmt, va_list va) { + return zpl_fprintf_va(zpl_file_get_standard(ZPL_FILE_STANDARD_OUTPUT), fmt, va); + } + + zpl_isize zpl_printf_err_va(char const *fmt, va_list va) { + return zpl_fprintf_va(zpl_file_get_standard(ZPL_FILE_STANDARD_ERROR), fmt, va); + } + + zpl_isize zpl_fprintf_va(struct zpl_file *f, char const *fmt, va_list va) { + zpl_local_persist zpl_thread_local char buf[ZPL_PRINTF_MAXLEN]; + zpl_isize len = zpl_snprintf_va(buf, zpl_size_of(buf), fmt, va); + zpl_b32 res = zpl_file_write(f, buf, len - 1); // NOTE: prevent extra whitespace + return res ? len : -1; + } + + char *zpl_bprintf_va(char const *fmt, va_list va) { + zpl_local_persist zpl_thread_local char buffer[ZPL_PRINTF_MAXLEN]; + zpl_snprintf_va(buffer, zpl_size_of(buffer), fmt, va); + return buffer; + } + + zpl_isize zpl_asprintf_va(zpl_allocator allocator, char **buffer, char const *fmt, va_list va) { + zpl_local_persist zpl_thread_local char tmp[ZPL_PRINTF_MAXLEN]; + ZPL_ASSERT_NOT_NULL(buffer); + zpl_isize res; + res = zpl_snprintf_va(tmp, zpl_size_of(tmp), fmt, va); + *buffer = zpl_alloc_str(allocator, tmp); + return res; + } + + zpl_isize zpl_printf(char const *fmt, ...) { + zpl_isize res; + va_list va; + va_start(va, fmt); + res = zpl_printf_va(fmt, va); + va_end(va); + return res; + } + + zpl_isize zpl_printf_err(char const *fmt, ...) { + zpl_isize res; + va_list va; + va_start(va, fmt); + res = zpl_printf_err_va(fmt, va); + va_end(va); + return res; + } + + zpl_isize zpl_fprintf(struct zpl_file *f, char const *fmt, ...) { + zpl_isize res; + va_list va; + va_start(va, fmt); + res = zpl_fprintf_va(f, fmt, va); + va_end(va); + return res; + } + + char *zpl_bprintf(char const *fmt, ...) { + va_list va; + char *str; + va_start(va, fmt); + str = zpl_bprintf_va(fmt, va); + va_end(va); + return str; + } + + zpl_isize zpl_asprintf(zpl_allocator allocator, char **buffer, char const *fmt, ...) { + zpl_isize res; + va_list va; + va_start(va, fmt); + res = zpl_asprintf_va(allocator, buffer, fmt, va); + va_end(va); + return res; + } + + zpl_isize zpl_snprintf(char *str, zpl_isize n, char const *fmt, ...) { + zpl_isize res; + va_list va; + va_start(va, fmt); + res = zpl_snprintf_va(str, n, fmt, va); + va_end(va); + return res; + } + + + enum { + ZPL_FMT_MINUS = ZPL_BIT(0), + ZPL_FMT_PLUS = ZPL_BIT(1), + ZPL_FMT_ALT = ZPL_BIT(2), + ZPL_FMT_SPACE = ZPL_BIT(3), + ZPL_FMT_ZERO = ZPL_BIT(4), + + ZPL_FMT_CHAR = ZPL_BIT(5), + ZPL_FMT_SHORT = ZPL_BIT(6), + ZPL_FMT_INT = ZPL_BIT(7), + ZPL_FMT_LONG = ZPL_BIT(8), + ZPL_FMT_LLONG = ZPL_BIT(9), + ZPL_FMT_SIZE = ZPL_BIT(10), + ZPL_FMT_INTPTR = ZPL_BIT(11), + + ZPL_FMT_UNSIGNED = ZPL_BIT(12), + ZPL_FMT_LOWER = ZPL_BIT(13), + ZPL_FMT_UPPER = ZPL_BIT(14), + ZPL_FMT_WIDTH = ZPL_BIT(15), + + ZPL_FMT_DONE = ZPL_BIT(30), + + ZPL_FMT_INTS = + ZPL_FMT_CHAR | ZPL_FMT_SHORT | ZPL_FMT_INT | + ZPL_FMT_LONG | ZPL_FMT_LLONG | ZPL_FMT_SIZE | ZPL_FMT_INTPTR + }; + + typedef struct { + zpl_i32 base; + zpl_i32 flags; + zpl_i32 width; + zpl_i32 precision; + } zpl__format_info; + + zpl_internal zpl_isize zpl__print_string(char *text, zpl_isize max_len, zpl__format_info *info, char const *str) { + zpl_isize res = 0, len = 0; + zpl_isize remaining = max_len; + char *begin = text; + + if (str == NULL && max_len >= 6) { + res += zpl_strlcpy(text, "(null)", 6); + return res; + } + + if (info && info->precision >= 0) + len = zpl_strnlen(str, info->precision); + else + len = zpl_strlen(str); + + if (info && (info->width == 0 && info->flags & ZPL_FMT_WIDTH)) { + return res; + } + + if (info && (info->width == 0 || info->flags & ZPL_FMT_MINUS)) { + if (info->precision > 0) len = info->precision < len ? info->precision : len; + if (res+len > max_len) return res; + res += zpl_strlcpy(text, str, len); + text += res; + + if (info->width > res) { + zpl_isize padding = info->width - len; + + char pad = (info->flags & ZPL_FMT_ZERO) ? '0' : ' '; + while (padding-- > 0 && remaining-- > 0) *text++ = pad, res++; + } + } else { + if (info && (info->width > res)) { + zpl_isize padding = info->width - len; + char pad = (info->flags & ZPL_FMT_ZERO) ? '0' : ' '; + while (padding-- > 0 && remaining-- > 0) *text++ = pad, res++; + } + + if (res+len > max_len) return res; + res += zpl_strlcpy(text, str, len); + } + + if (info) { + if (info->flags & ZPL_FMT_UPPER) + zpl_str_to_upper(begin); + else if (info->flags & ZPL_FMT_LOWER) + zpl_str_to_lower(begin); + } + + return res; + } + + zpl_internal zpl_isize zpl__print_char(char *text, zpl_isize max_len, zpl__format_info *info, char arg) { + char str[2] = ""; + str[0] = arg; + return zpl__print_string(text, max_len, info, str); + } + + zpl_internal zpl_isize zpl__print_repeated_char(char *text, zpl_isize max_len, zpl__format_info *info, char arg) { + zpl_isize res = 0; + zpl_i32 rem = (info) ? (info->width > 0) ? info->width : 1 : 1; + res = rem; + while (rem-- > 0) *text++ = arg; + + return res; + } + + zpl_internal zpl_isize zpl__print_i64(char *text, zpl_isize max_len, zpl__format_info *info, zpl_i64 value) { + char num[130]; + zpl_i64_to_str(value, num, info ? info->base : 10); + return zpl__print_string(text, max_len, info, num); + } + + zpl_internal zpl_isize zpl__print_u64(char *text, zpl_isize max_len, zpl__format_info *info, zpl_u64 value) { + char num[130]; + zpl_u64_to_str(value, num, info ? info->base : 10); + return zpl__print_string(text, max_len, info, num); + } + + zpl_internal zpl_isize zpl__print_f64(char *text, zpl_isize max_len, zpl__format_info *info, zpl_b32 is_hexadecimal, zpl_f64 arg) { + // TODO: Handle exponent notation + zpl_isize width, len, remaining = max_len; + char *text_begin = text; + + if (arg) { + zpl_u64 value; + if (arg < 0) { + if (remaining > 1) *text = '-', remaining--; + text++; + arg = -arg; + } else if (info->flags & ZPL_FMT_MINUS) { + if (remaining > 1) *text = '+', remaining--; + text++; + } + + value = cast(zpl_u64) arg; + len = zpl__print_u64(text, remaining, NULL, value); + text += len; + + if (len >= remaining) + remaining = zpl_min(remaining, 1); + else + remaining -= len; + arg -= value; + + if (info->precision < 0) info->precision = 6; + + if ((info->flags & ZPL_FMT_ALT) || info->precision > 0) { + zpl_i64 mult = 10; + if (remaining > 1) *text = '.', remaining--; + text++; + while (info->precision-- > 0) { + value = cast(zpl_u64)(arg * mult); + len = zpl__print_u64(text, remaining, NULL, value); + text += len; + if (len >= remaining) + remaining = zpl_min(remaining, 1); + else + remaining -= len; + arg -= cast(zpl_f64) value / mult; + mult *= 10; + } + } + } else { + if (remaining > 1) *text = '0', remaining--; + text++; + if (info->flags & ZPL_FMT_ALT) { + if (remaining > 1) *text = '.', remaining--; + text++; + } + } + + width = info->width - (text - text_begin); + if (width > 0) { + char fill = (info->flags & ZPL_FMT_ZERO) ? '0' : ' '; + char *end = text + remaining - 1; + len = (text - text_begin); + + for (len = (text - text_begin); len--;) { + if ((text_begin + len + width) < end) *(text_begin + len + width) = *(text_begin + len); + } + + len = width; + text += len; + if (len >= remaining) + remaining = zpl_min(remaining, 1); + else + remaining -= len; + + while (len--) { + if (text_begin + len < end) text_begin[len] = fill; + } + } + + return (text - text_begin); + } + + ZPL_NEVER_INLINE zpl_isize zpl_snprintf_va(char *text, zpl_isize max_len, char const *fmt, va_list va) { + char const *text_begin = text; + zpl_isize remaining = max_len, res; + + while (*fmt) { + zpl__format_info info = { 0 }; + zpl_isize len = 0; + info.precision = -1; + + while (*fmt && *fmt != '%' && remaining) *text++ = *fmt++; + + if (*fmt == '%') { + do { + switch (*++fmt) { + case '-': {info.flags |= ZPL_FMT_MINUS; break;} + case '+': {info.flags |= ZPL_FMT_PLUS; break;} + case '#': {info.flags |= ZPL_FMT_ALT; break;} + case ' ': {info.flags |= ZPL_FMT_SPACE; break;} + case '0': {info.flags |= (ZPL_FMT_ZERO|ZPL_FMT_WIDTH); break;} + default: {info.flags |= ZPL_FMT_DONE; break;} + } + } while (!(info.flags & ZPL_FMT_DONE)); + } + + // NOTE: Optional Width + if (*fmt == '*') { + int width = va_arg(va, int); + if (width < 0) { + info.flags |= ZPL_FMT_MINUS; + info.width = -width; + } else { + info.width = width; + } + info.flags |= ZPL_FMT_WIDTH; + fmt++; + } else { + info.width = cast(zpl_i32) zpl_str_to_i64(fmt, cast(char **) & fmt, 10); + if (info.width != 0) { + info.flags |= ZPL_FMT_WIDTH; + } + } + + // NOTE: Optional Precision + if (*fmt == '.') { + fmt++; + if (*fmt == '*') { + info.precision = va_arg(va, int); + fmt++; + } else { + info.precision = cast(zpl_i32) zpl_str_to_i64(fmt, cast(char **) & fmt, 10); + } + info.flags &= ~ZPL_FMT_ZERO; + } + + switch (*fmt++) { + case 'h': + if (*fmt == 'h') { // hh => char + info.flags |= ZPL_FMT_CHAR; + fmt++; + } else { // h => short + info.flags |= ZPL_FMT_SHORT; + } + break; + + case 'l': + if (*fmt == 'l') { // ll => long long + info.flags |= ZPL_FMT_LLONG; + fmt++; + } else { // l => long + info.flags |= ZPL_FMT_LONG; + } + break; + + break; + + case 'z': // NOTE: zpl_usize + info.flags |= ZPL_FMT_UNSIGNED; + // fallthrough + case 't': // NOTE: zpl_isize + info.flags |= ZPL_FMT_SIZE; + break; + + default: fmt--; break; + } + + switch (*fmt) { + case 'u': + info.flags |= ZPL_FMT_UNSIGNED; + // fallthrough + case 'd': + case 'i': info.base = 10; break; + + case 'o': info.base = 8; break; + + case 'x': + info.base = 16; + info.flags |= (ZPL_FMT_UNSIGNED | ZPL_FMT_LOWER); + break; + + case 'X': + info.base = 16; + info.flags |= (ZPL_FMT_UNSIGNED | ZPL_FMT_UPPER); + break; + + case 'f': + case 'F': + case 'g': + case 'G': len = zpl__print_f64(text, remaining, &info, 0, va_arg(va, zpl_f64)); break; + + case 'a': + case 'A': + len = zpl__print_f64(text, remaining, &info, 1, va_arg(va, zpl_f64)); break; + + case 'c': len = zpl__print_char(text, remaining, &info, cast(char) va_arg(va, int)); break; + + case 's': len = zpl__print_string(text, remaining, &info, va_arg(va, char *)); break; + + case 'r': len = zpl__print_repeated_char(text, remaining, &info, va_arg(va, int)); break; + + case 'p': + info.base = 16; + info.flags |= (ZPL_FMT_LOWER | ZPL_FMT_UNSIGNED | ZPL_FMT_ALT | ZPL_FMT_INTPTR); + break; + + case '%': len = zpl__print_char(text, remaining, &info, '%'); break; + + default: fmt--; break; + } + + fmt++; + + if (info.base != 0) { + if (info.flags & ZPL_FMT_UNSIGNED) { + zpl_u64 value = 0; + switch (info.flags & ZPL_FMT_INTS) { + case ZPL_FMT_CHAR: value = cast(zpl_u64) cast(zpl_u8) va_arg(va, int); break; + case ZPL_FMT_SHORT: value = cast(zpl_u64) cast(zpl_u16) va_arg(va, int); break; + case ZPL_FMT_LONG: value = cast(zpl_u64) va_arg(va, unsigned long); break; + case ZPL_FMT_LLONG: value = cast(zpl_u64) va_arg(va, unsigned long long); break; + case ZPL_FMT_SIZE: value = cast(zpl_u64) va_arg(va, zpl_usize); break; + case ZPL_FMT_INTPTR: value = cast(zpl_u64) va_arg(va, zpl_uintptr); break; + default: value = cast(zpl_u64) va_arg(va, unsigned int); break; + } + + len = zpl__print_u64(text, remaining, &info, value); + + } else { + zpl_i64 value = 0; + switch (info.flags & ZPL_FMT_INTS) { + case ZPL_FMT_CHAR: value = cast(zpl_i64) cast(zpl_i8) va_arg(va, int); break; + case ZPL_FMT_SHORT: value = cast(zpl_i64) cast(zpl_i16) va_arg(va, int); break; + case ZPL_FMT_LONG: value = cast(zpl_i64) va_arg(va, long); break; + case ZPL_FMT_LLONG: value = cast(zpl_i64) va_arg(va, long long); break; + case ZPL_FMT_SIZE: value = cast(zpl_i64) va_arg(va, zpl_usize); break; + case ZPL_FMT_INTPTR: value = cast(zpl_i64) va_arg(va, zpl_uintptr); break; + default: value = cast(zpl_i64) va_arg(va, int); break; + } + + len = zpl__print_i64(text, remaining, &info, value); + } + } + + text += len; + if (len >= remaining) + remaining = zpl_min(remaining, 1); + else + remaining -= len; + } + + *text++ = '\0'; + res = (text - text_begin); + return (res >= max_len || res < 0) ? -1 : res; + } + + ZPL_END_C_DECLS + // file: source/core/time.c + + + #if defined(ZPL_SYSTEM_MACOS) || ZPL_SYSTEM_UNIX + # include + # include + #endif + + #if defined(ZPL_SYSTEM_MACOS) + # include + # include + # include + #endif + + #if defined(ZPL_SYSTEM_EMSCRIPTEN) + # include + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) + # include + #endif + + ZPL_BEGIN_C_DECLS + + //! @} + //$$ + //////////////////////////////////////////////////////////////// + // + // Time + // + // + + #if defined(ZPL_COMPILER_MSVC) && !defined(__clang__) + zpl_u64 zpl_rdtsc(void) { return __rdtsc( ); } + #elif defined(__i386__) + zpl_u64 zpl_rdtsc(void) { + zpl_u64 x; + __asm__ volatile(".byte 0x0f, 0x31" : "=A"(x)); + return x; + } + #elif defined(__x86_64__) + zpl_u64 zpl_rdtsc(void) { + zpl_u32 hi, lo; + __asm__ __volatile__("rdtsc" : "=a"(lo), "=d"(hi)); + return (cast(zpl_u64) lo) | ((cast(zpl_u64) hi) << 32); + } + #elif defined(__powerpc__) + zpl_u64 zpl_rdtsc(void) { + zpl_u64 result = 0; + zpl_u32 upper, lower, tmp; + __asm__ volatile("0: \n" + "\tmftbu %0 \n" + "\tmftb %1 \n" + "\tmftbu %2 \n" + "\tcmpw %2,%0 \n" + "\tbne 0b \n" + : "=r"(upper), "=r"(lower), "=r"(tmp)); + result = upper; + result = result << 32; + result = result | lower; + + return result; + } + #elif defined(ZPL_SYSTEM_EMSCRIPTEN) + zpl_u64 zpl_rdtsc(void) { + return (zpl_u64)(emscripten_get_now() * 1e+6); + } + #elif defined(ZPL_CPU_ARM) && !defined(ZPL_COMPILER_TINYC) + zpl_u64 zpl_rdtsc(void) { + # if defined(__aarch64__) + int64_t r = 0; + asm volatile("mrs %0, cntvct_el0" : "=r"(r)); + # elif (__ARM_ARCH >= 6) + uint32_t r = 0; + uint32_t pmccntr; + uint32_t pmuseren; + uint32_t pmcntenset; + + // Read the user mode perf monitor counter access permissions. + asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren)); + if (pmuseren & 1) { // Allows reading perfmon counters for user mode code. + asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset)); + if (pmcntenset & 0x80000000ul) { // Is it counting? + asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr)); + // The counter is set up to count every 64th cycle + return ((int64_t)pmccntr) * 64; // Should optimize to << 6 + } + } + # else + # error "No suitable method for zpl_rdtsc for this cpu type" + # endif + + return r; + } + #else + zpl_u64 zpl_rdtsc(void) { + ZPL_PANIC("zpl_rdtsc is not supported on this particular setup"); + return -0; + } + #endif + + #if defined(ZPL_SYSTEM_WINDOWS) || defined(ZPL_SYSTEM_CYGWIN) + + zpl_u64 zpl_time_rel_ms(void) { + zpl_local_persist LARGE_INTEGER win32_perf_count_freq = { 0 }; + zpl_u64 result; + LARGE_INTEGER counter; + zpl_local_persist LARGE_INTEGER win32_perf_counter = { 0 }; + if (!win32_perf_count_freq.QuadPart) { + QueryPerformanceFrequency(&win32_perf_count_freq); + ZPL_ASSERT(win32_perf_count_freq.QuadPart != 0); + QueryPerformanceCounter(&win32_perf_counter); + } + + QueryPerformanceCounter(&counter); + + result = (counter.QuadPart - win32_perf_counter.QuadPart) * 1000 / (win32_perf_count_freq.QuadPart); + return result; + } + + zpl_u64 zpl_time_utc_ms(void) { + FILETIME ft; + ULARGE_INTEGER li; + + GetSystemTimeAsFileTime(&ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + + return li.QuadPart / 1000; + } + + zpl_u64 zpl_time_tz_ms(void) { + FILETIME ft; + SYSTEMTIME st, lst; + ULARGE_INTEGER li; + + GetSystemTime(&st); + SystemTimeToTzSpecificLocalTime(NULL, &st, &lst); + SystemTimeToFileTime(&lst, &ft); + li.LowPart = ft.dwLowDateTime; + li.HighPart = ft.dwHighDateTime; + + return li.QuadPart / 1000; + } + + void zpl_sleep_ms(zpl_u32 ms) { Sleep(ms); } + + #else + + # if defined(ZPL_SYSTEM_LINUX) || defined(ZPL_SYSTEM_FREEBSD) || defined(ZPL_SYSTEM_OPENBSD) || defined(ZPL_SYSTEM_EMSCRIPTEN) + zpl_u64 zpl__unix_gettime(void) { + struct timespec t; + zpl_u64 result; + + clock_gettime(1 /*CLOCK_MONOTONIC*/, &t); + result = 1000 * t.tv_sec + 1.0e-6 * t.tv_nsec; + return result; + } + # endif + + zpl_u64 zpl_time_rel_ms(void) { + # if defined(ZPL_SYSTEM_OSX) + zpl_u64 result; + + zpl_local_persist zpl_u64 timebase = 0; + zpl_local_persist zpl_u64 timestart = 0; + + if (!timestart) { + mach_timebase_info_data_t tb = { 0 }; + mach_timebase_info(&tb); + timebase = tb.numer; + timebase /= tb.denom; + timestart = mach_absolute_time(); + } + + // NOTE: mach_absolute_time() returns things in nanoseconds + result = 1.0e-6 * (mach_absolute_time() - timestart) * timebase; + return result; + # else + zpl_local_persist zpl_u64 unix_timestart = 0.0; + + if (!unix_timestart) { unix_timestart = zpl__unix_gettime( ); } + + zpl_u64 now = zpl__unix_gettime( ); + + return (now - unix_timestart); + # endif + } + + zpl_u64 zpl_time_utc_ms(void) { + struct timespec t; + # if defined(ZPL_SYSTEM_OSX) + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self( ), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self( ), cclock); + t.tv_sec = mts.tv_sec; + t.tv_nsec = mts.tv_nsec; + # else + clock_gettime(0 /*CLOCK_REALTIME*/, &t); + # endif + return ((zpl_u64)t.tv_sec * 1000 + t.tv_nsec * 1e-6 + ZPL__UNIX_TO_WIN32_EPOCH); + } + + void zpl_sleep_ms(zpl_u32 ms) { + struct timespec req = { cast(time_t)(ms * 1e-3), cast(long)((ms % 1000) * 1e6) }; + struct timespec rem = { 0, 0 }; + nanosleep(&req, &rem); + } + + zpl_u64 zpl_time_tz_ms(void) { + struct tm t; + zpl_u64 result = zpl_time_utc_ms() - ZPL__UNIX_TO_WIN32_EPOCH; + zpl_u16 ms = result % 1000; + result *= 1e-3; + localtime_r((const time_t*)&result, &t); + result = (zpl_u64)mktime(&t); + return (result - timezone + t.tm_isdst * 3600) * 1000 + ms + ZPL__UNIX_TO_WIN32_EPOCH; + } + #endif + + zpl_f64 zpl_time_rel(void) { + return (zpl_f64)(zpl_time_rel_ms() * 1e-3); + } + + zpl_f64 zpl_time_utc(void) { + return (zpl_f64)(zpl_time_utc_ms() * 1e-3); + } + + zpl_f64 zpl_time_tz(void) { + return (zpl_f64)(zpl_time_tz_ms() * 1e-3); + } + + + + ZPL_END_C_DECLS + // file: source/core/random.c + + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_MODULE_THREADING) + zpl_global zpl_atomic32 zpl__random_shared_counter = {0}; + #else + zpl_global zpl_i32 zpl__random_shared_counter = 0; + #endif + + zpl_internal zpl_u32 zpl__get_noise_from_time(void) { + zpl_u32 accum = 0; + zpl_f64 start, remaining, end, curr = 0; + zpl_u64 interval = 100000ll; + + start = zpl_time_rel(); + remaining = (interval - cast(zpl_u64)(interval*start)%interval) / cast(zpl_f64)interval; + end = start + remaining; + + do { + curr = zpl_time_rel(); + accum += cast(zpl_u32)curr; + } while (curr >= end); + return accum; + } + + // NOTE: Partly from http://preshing.com/20121224/how-to-generate-a-sequence-of-unique-random-integers/ + // But the generation is even more random-er-est + + zpl_internal ZPL_ALWAYS_INLINE zpl_u32 zpl__permute_qpr(zpl_u32 x) { + zpl_local_persist zpl_u32 const prime = 4294967291; // 2^32 - 5 + if (x >= prime) { + return x; + } else { + zpl_u32 residue = cast(zpl_u32)(cast(zpl_u64) x * x) % prime; + if (x <= prime / 2) + return residue; + else + return prime - residue; + } + } + + zpl_internal ZPL_ALWAYS_INLINE zpl_u32 zpl__permute_with_offset(zpl_u32 x, zpl_u32 offset) { + return (zpl__permute_qpr(x) + offset) ^ 0x5bf03635; + } + + + void zpl_random_init(zpl_random *r) { + zpl_u64 time, tick; + zpl_isize i, j; + zpl_u32 x = 0; + r->value = 0; + + r->offsets[0] = zpl__get_noise_from_time(); + #ifdef ZPL_MODULE_THREADING + r->offsets[1] = zpl_atomic32_fetch_add(&zpl__random_shared_counter, 1); + r->offsets[2] = zpl_thread_current_id(); + r->offsets[3] = zpl_thread_current_id() * 3 + 1; + #else + r->offsets[1] = zpl__random_shared_counter++; + r->offsets[2] = 0; + r->offsets[3] = 1; + #endif + time = zpl_time_tz_ms(); + r->offsets[4] = cast(zpl_u32)(time >> 32); + r->offsets[5] = cast(zpl_u32)time; + r->offsets[6] = zpl__get_noise_from_time(); + tick = zpl_rdtsc(); + r->offsets[7] = cast(zpl_u32)(tick ^ (tick >> 32)); + + for (j = 0; j < 4; j++) { + for (i = 0; i < zpl_count_of(r->offsets); i++) { + r->offsets[i] = x = zpl__permute_with_offset(x, r->offsets[i]); + } + } + } + + zpl_u32 zpl_random_gen_u32(zpl_random *r) { + zpl_u32 x = r->value; + zpl_u32 carry = 1; + zpl_isize i; + for (i = 0; i < zpl_count_of(r->offsets); i++) { + x = zpl__permute_with_offset(x, r->offsets[i]); + if (carry > 0) { + carry = ++r->offsets[i] ? 0 : 1; + } + } + + r->value = x; + return x; + } + + zpl_u32 zpl_random_gen_u32_unique(zpl_random *r) { + zpl_u32 x = r->value; + zpl_isize i; + r->value++; + for (i = 0; i < zpl_count_of(r->offsets); i++) { + x = zpl__permute_with_offset(x, r->offsets[i]); + } + + return x; + } + + zpl_u64 zpl_random_gen_u64(zpl_random *r) { + return ((cast(zpl_u64)zpl_random_gen_u32(r)) << 32) | zpl_random_gen_u32(r); + } + + + zpl_isize zpl_random_gen_isize(zpl_random *r) { + #if defined(ZPL_ARCH_32_BIT) + zpl_u32 u = zpl_random_gen_u32(r); + #else + zpl_u64 u = zpl_random_gen_u64(r); + #endif + zpl_isize i; + zpl_memcopy(&i, &u, zpl_size_of(u)); + return i; + } + + + zpl_i64 zpl_random_range_i64(zpl_random *r, zpl_i64 lower_inc, zpl_i64 higher_inc) { + zpl_u64 u = zpl_random_gen_u64(r); + zpl_i64 diff = higher_inc-lower_inc+1; + u %= diff; + zpl_i64 i; + zpl_memcopy(&i, &u, zpl_size_of(u)); + i += lower_inc; + return i; + } + + zpl_isize zpl_random_range_isize(zpl_random *r, zpl_isize lower_inc, zpl_isize higher_inc) { + #if defined(ZPL_ARCH_32_BIT) + zpl_u32 u = zpl_random_gen_u32(r); + #else + zpl_u64 u = zpl_random_gen_u64(r); + #endif + zpl_isize diff = higher_inc-lower_inc+1; + u %= diff; + zpl_isize i; + zpl_memcopy(&i, &u, zpl_size_of(u)); + i += lower_inc; + return i; + } + + ZPL_ALWAYS_INLINE zpl_f64 zpl__random_copy_sign64(zpl_f64 x, zpl_f64 y) { + zpl_i64 ix=0, iy=0; + zpl_memcopy(&ix, &x, zpl_size_of(zpl_i64)); + zpl_memcopy(&iy, &y, zpl_size_of(zpl_i64)); + + ix &= 0x7fffffffffffffff; + ix |= iy & 0x8000000000000000; + + zpl_f64 r = 0.0; + zpl_memcopy(&r, &ix, zpl_size_of(zpl_f64)); + return r; + } + + zpl_f64 zpl_random_range_f64(zpl_random *r, zpl_f64 lower_inc, zpl_f64 higher_inc) { + zpl_f64 f = cast(zpl_f64)zpl_random_gen_u64(r) / cast(zpl_f64)ZPL_U64_MAX; + zpl_f64 diff = higher_inc-lower_inc; + + f *= diff; + f += lower_inc; + return f; + } + + ZPL_END_C_DECLS + // file: source/core/misc.c + + + ZPL_BEGIN_C_DECLS + + void zpl_yield(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + Sleep(0); + # else + sched_yield(); + # endif + } + + const char *zpl_get_env(const char *name) { + char *buffer = NULL; + const char *ptr = zpl_get_env_buf(name); + + if (ptr == NULL) { + return NULL; + } + + zpl_isize ptr_size = zpl_strlen(ptr); + buffer = (char *)zpl_malloc(ptr_size * sizeof(char)+1); + zpl_memcopy((char *)buffer, ptr, ptr_size+1); + return buffer; + } + + const char *zpl_get_env_buf(const char *name) { + # ifdef ZPL_SYSTEM_WINDOWS + zpl_local_persist wchar_t wbuffer[32767] = {0}; + zpl_local_persist char buffer[32767] = {0}; + + if (!GetEnvironmentVariableW( + cast(LPCWSTR)zpl_utf8_to_ucs2_buf(cast(const zpl_u8 *)name), + cast(LPWSTR)wbuffer, 32767)) { + return NULL; + } + + zpl_ucs2_to_utf8(cast(zpl_u8*)buffer, 32767, cast(const zpl_u16*)wbuffer); + + return (const char *)buffer; + # else + return (const char *)getenv(name); + # endif + } + + zpl_string zpl_get_env_str(const char *name) { + const char *buf = zpl_get_env_buf(name); + + if (buf == NULL) { + return NULL; + } + + zpl_string str = zpl_string_make(zpl_heap(), buf); + return str; + } + + void zpl_set_env(const char *name, const char *value) { + # if defined(ZPL_SYSTEM_WINDOWS) + SetEnvironmentVariableA(name, value); + # else + setenv(name, value, 1); + # endif + } + + void zpl_unset_env(const char *name) { + # if defined(ZPL_SYSTEM_WINDOWS) + SetEnvironmentVariableA(name, NULL); + # else + unsetenv(name); + # endif + } + + #if !defined(ZPL_SYSTEM_WINDOWS) + extern char **environ; + #endif + + zpl_u32 zpl_system_command(const char *command, zpl_usize buffer_len, char *buffer) { + # if defined(ZPL_SYSTEM_EMSCRIPTEN) + ZPL_PANIC("zpl_system_command not supported"); + # else + + # if defined(ZPL_SYSTEM_WINDOWS) + FILE *handle = _popen(command, "r"); + # else + FILE *handle = popen(command, "r"); + # endif + + if(!handle) return 0; + + int c; + zpl_usize i=0; + while ((c = getc(handle)) != EOF && i++ < buffer_len) { + *buffer++ = c; + } + + # if defined(ZPL_SYSTEM_WINDOWS) + _pclose(handle); + # else + pclose(handle); + # endif + + # endif + + return 1; + } + + zpl_string zpl_system_command_str(const char *command, zpl_allocator backing) { + # if defined(ZPL_SYSTEM_EMSCRIPTEN) + ZPL_PANIC("zpl_system_command not supported"); + # else + + # if defined(ZPL_SYSTEM_WINDOWS) + FILE *handle = _popen(command, "r"); + # else + FILE *handle = popen(command, "r"); + # endif + + if(!handle) return NULL; + + zpl_string output = zpl_string_make_reserve(backing, 4); + + int c; + while ((c = getc(handle)) != EOF) { + char ins[2] = {(char)c,0}; + output = zpl_string_appendc(output, ins); + } + + # if defined(ZPL_SYSTEM_WINDOWS) + _pclose(handle); + # else + pclose(handle); + # endif + return output; + # endif + return NULL; + } + + ZPL_END_C_DECLS + // file: source/core/sort.c + + + ZPL_BEGIN_C_DECLS + + #define ZPL__COMPARE_PROC(Type) \ + zpl_global zpl_isize Type##__cmp_offset; \ + ZPL_COMPARE_PROC(Type##__cmp) { \ + Type const p = *cast(Type const *) zpl_pointer_add_const(a, Type##__cmp_offset); \ + Type const q = *cast(Type const *) zpl_pointer_add_const(b, Type##__cmp_offset); \ + return p < q ? -1 : p > q; \ + } \ + ZPL_COMPARE_PROC_PTR(Type##_cmp(zpl_isize offset)) { \ + Type##__cmp_offset = offset; \ + return &Type##__cmp; \ + } + + ZPL__COMPARE_PROC(zpl_u8); + ZPL__COMPARE_PROC(zpl_i16); + ZPL__COMPARE_PROC(zpl_i32); + ZPL__COMPARE_PROC(zpl_i64); + ZPL__COMPARE_PROC(zpl_isize); + ZPL__COMPARE_PROC(zpl_f32); + ZPL__COMPARE_PROC(zpl_f64); + + // NOTE: str_cmp is special as it requires a funny type and funny comparison + zpl_global zpl_isize zpl__str_cmp_offset; + ZPL_COMPARE_PROC(zpl__str_cmp) { + char const *p = *cast(char const **) zpl_pointer_add_const(a, zpl__str_cmp_offset); + char const *q = *cast(char const **) zpl_pointer_add_const(b, zpl__str_cmp_offset); + return zpl_strcmp(p, q); + } + ZPL_COMPARE_PROC_PTR(zpl_str_cmp(zpl_isize offset)) { + zpl__str_cmp_offset = offset; + return &zpl__str_cmp; + } + + #undef ZPL__COMPARE_PROC + + // TODO: Make user definable? + #define ZPL__SORT_STACK_SIZE 64 + #define zpl__SORT_INSERT_SORT_TRESHOLD 8 + + #define ZPL__SORT_PUSH(_base, _limit) \ + do { \ + stack_ptr[0] = (_base); \ + stack_ptr[1] = (_limit); \ + stack_ptr += 2; \ + } while (0) + + #define ZPL__SORT_POP(_base, _limit) \ + do { \ + stack_ptr -= 2; \ + (_base) = stack_ptr[0]; \ + (_limit) = stack_ptr[1]; \ + } while (0) + + void zpl_sort(void *base_, zpl_isize count, zpl_isize size, zpl_compare_proc cmp) { + zpl_u8 *i, *j; + zpl_u8 *base = cast(zpl_u8 *) base_; + zpl_u8 *limit = base + count * size; + zpl_isize threshold = zpl__SORT_INSERT_SORT_TRESHOLD * size; + + // NOTE: Prepare the stack + zpl_u8 *stack[ZPL__SORT_STACK_SIZE] = { 0 }; + zpl_u8 **stack_ptr = stack; + + for (;;) { + if ((limit - base) > threshold) { + // NOTE: Quick sort + i = base + size; + j = limit - size; + + zpl_memswap(((limit - base) / size / 2) * size + base, base, size); + if (cmp(i, j) > 0) zpl_memswap(i, j, size); + if (cmp(base, j) > 0) zpl_memswap(base, j, size); + if (cmp(i, base) > 0) zpl_memswap(i, base, size); + + for (;;) { + do + i += size; + while (cmp(i, base) < 0); + do + j -= size; + while (cmp(j, base) > 0); + if (i > j) break; + zpl_memswap(i, j, size); + } + + zpl_memswap(base, j, size); + + if (j - base > limit - i) { + ZPL__SORT_PUSH(base, j); + base = i; + } else { + ZPL__SORT_PUSH(i, limit); + limit = j; + } + } else { + // NOTE: Insertion sort + for (j = base, i = j + size; i < limit; j = i, i += size) { + for (; cmp(j, j + size) > 0; j -= size) { + zpl_memswap(j, j + size, size); + if (j == base) break; + } + } + + if (stack_ptr == stack) break; // NOTE: Sorting is done! + ZPL__SORT_POP(base, limit); + } + } + } + + #undef ZPL__SORT_PUSH + #undef ZPL__SORT_POP + + #define ZPL_RADIX_SORT_PROC_GEN(Type) \ + ZPL_RADIX_SORT_PROC(Type) { \ + zpl_##Type *source = items; \ + zpl_##Type *dest = temp; \ + zpl_isize byte_index, i, byte_max = 8 * zpl_size_of(zpl_##Type); \ + for (byte_index = 0; byte_index < byte_max; byte_index += 8) { \ + zpl_isize offsets[256] = { 0 }; \ + zpl_isize total = 0; \ + /* NOTE: First pass - count how many of each key */ \ + for (i = 0; i < count; i++) { \ + zpl_##Type radix_value = source[i]; \ + zpl_##Type radix_piece = (radix_value >> byte_index) & 0xff; \ + offsets[radix_piece]++; \ + } \ + /* NOTE: Change counts to offsets */ \ + for (i = 0; i < zpl_count_of(offsets); i++) { \ + zpl_isize skcount = offsets[i]; \ + offsets[i] = total; \ + total += skcount; \ + } \ + /* NOTE: Second pass - place elements into the right location */ \ + for (i = 0; i < count; i++) { \ + zpl_##Type radix_value = source[i]; \ + zpl_##Type radix_piece = (radix_value >> byte_index) & 0xff; \ + dest[offsets[radix_piece]++] = source[i]; \ + } \ + zpl_swap(zpl_##Type *, source, dest); \ + } \ + } + + ZPL_RADIX_SORT_PROC_GEN(u8); + ZPL_RADIX_SORT_PROC_GEN(u16); + ZPL_RADIX_SORT_PROC_GEN(u32); + ZPL_RADIX_SORT_PROC_GEN(u64); + + void zpl_shuffle(void *base, zpl_isize count, zpl_isize size) { + zpl_u8 *a; + zpl_isize i, j; + zpl_random random; + zpl_random_init(&random); + + a = cast(zpl_u8 *) base + (count - 1) * size; + for (i = count; i > 1; i--) { + j = zpl_random_gen_isize(&random) % i; + zpl_memswap(a, cast(zpl_u8 *) base + j * size, size); + a -= size; + } + } + + void zpl_reverse(void *base, zpl_isize count, zpl_isize size) { + zpl_isize i, j = count - 1; + for (i = 0; i < j; i++, j++) zpl_memswap(cast(zpl_u8 *) base + i * size, cast(zpl_u8 *) base + j * size, size); + } + + ZPL_END_C_DECLS +# endif +#endif + +#if defined(ZPL_MODULE_HASHING) + // file: source/hashing.c + + //////////////////////////////////////////////////////////////// + // + // Hashing functions + // + // + + ZPL_BEGIN_C_DECLS + + zpl_u32 zpl_adler32(void const *data, zpl_isize len) { + zpl_u32 const MOD_ALDER = 65521; + zpl_u32 a = 1, b = 0; + zpl_isize i, block_len; + zpl_u8 const *bytes = cast(zpl_u8 const *) data; + + block_len = len % 5552; + + while (len) { + for (i = 0; i + 7 < block_len; i += 8) { + a += bytes[0], b += a; + a += bytes[1], b += a; + a += bytes[2], b += a; + a += bytes[3], b += a; + a += bytes[4], b += a; + a += bytes[5], b += a; + a += bytes[6], b += a; + a += bytes[7], b += a; + + bytes += 8; + } + for (; i < block_len; i++) a += *bytes++, b += a; + + a %= MOD_ALDER, b %= MOD_ALDER; + len -= block_len; + block_len = 5552; + } + + return (b << 16) | a; + } + + zpl_global zpl_u32 const zpl__crc32_table[256] = { + 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x0edb8832, + 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2, + 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, 0xfd62f97a, + 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, + 0x45df5c75, 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, + 0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, + 0xb6662d3d, 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, + 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, + 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, + 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, 0x4db26158, 0x3ab551ce, 0xa3bc0074, + 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, + 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, + 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, + 0x73dc1683, 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, + 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, 0xfed41b76, + 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e, + 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, + 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, + 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, + 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, + 0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, + 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, + 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, + 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, 0xcabac28a, 0x53b39330, + 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, + 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, + }; + + zpl_global zpl_u64 const zpl__crc64_table[256] = { + 0x0000000000000000ull, 0x7ad870c830358979ull, 0xf5b0e190606b12f2ull, 0x8f689158505e9b8bull, 0xc038e5739841b68full, 0xbae095bba8743ff6ull, + 0x358804e3f82aa47dull, 0x4f50742bc81f2d04ull, 0xab28ecb46814fe75ull, 0xd1f09c7c5821770cull, 0x5e980d24087fec87ull, 0x24407dec384a65feull, + 0x6b1009c7f05548faull, 0x11c8790fc060c183ull, 0x9ea0e857903e5a08ull, 0xe478989fa00bd371ull, 0x7d08ff3b88be6f81ull, 0x07d08ff3b88be6f8ull, + 0x88b81eabe8d57d73ull, 0xf2606e63d8e0f40aull, 0xbd301a4810ffd90eull, 0xc7e86a8020ca5077ull, 0x4880fbd87094cbfcull, 0x32588b1040a14285ull, + 0xd620138fe0aa91f4ull, 0xacf86347d09f188dull, 0x2390f21f80c18306ull, 0x594882d7b0f40a7full, 0x1618f6fc78eb277bull, 0x6cc0863448deae02ull, + 0xe3a8176c18803589ull, 0x997067a428b5bcf0ull, 0xfa11fe77117cdf02ull, 0x80c98ebf2149567bull, + 0x0fa11fe77117cdf0ull, 0x75796f2f41224489ull, 0x3a291b04893d698dull, 0x40f16bccb908e0f4ull, 0xcf99fa94e9567b7full, 0xb5418a5cd963f206ull, + 0x513912c379682177ull, 0x2be1620b495da80eull, 0xa489f35319033385ull, 0xde51839b2936bafcull, 0x9101f7b0e12997f8ull, 0xebd98778d11c1e81ull, + 0x64b116208142850aull, 0x1e6966e8b1770c73ull, 0x8719014c99c2b083ull, 0xfdc17184a9f739faull, 0x72a9e0dcf9a9a271ull, 0x08719014c99c2b08ull, + 0x4721e43f0183060cull, 0x3df994f731b68f75ull, 0xb29105af61e814feull, 0xc849756751dd9d87ull, 0x2c31edf8f1d64ef6ull, 0x56e99d30c1e3c78full, + 0xd9810c6891bd5c04ull, 0xa3597ca0a188d57dull, 0xec09088b6997f879ull, 0x96d1784359a27100ull, 0x19b9e91b09fcea8bull, 0x636199d339c963f2ull, + 0xdf7adabd7a6e2d6full, 0xa5a2aa754a5ba416ull, 0x2aca3b2d1a053f9dull, 0x50124be52a30b6e4ull, 0x1f423fcee22f9be0ull, 0x659a4f06d21a1299ull, + 0xeaf2de5e82448912ull, 0x902aae96b271006bull, 0x74523609127ad31aull, 0x0e8a46c1224f5a63ull, 0x81e2d7997211c1e8ull, 0xfb3aa75142244891ull, + 0xb46ad37a8a3b6595ull, 0xceb2a3b2ba0eececull, 0x41da32eaea507767ull, 0x3b024222da65fe1eull, 0xa2722586f2d042eeull, 0xd8aa554ec2e5cb97ull, + 0x57c2c41692bb501cull, 0x2d1ab4dea28ed965ull, 0x624ac0f56a91f461ull, 0x1892b03d5aa47d18ull, 0x97fa21650afae693ull, 0xed2251ad3acf6feaull, + 0x095ac9329ac4bc9bull, 0x7382b9faaaf135e2ull, 0xfcea28a2faafae69ull, 0x8632586aca9a2710ull, 0xc9622c4102850a14ull, 0xb3ba5c8932b0836dull, + 0x3cd2cdd162ee18e6ull, 0x460abd1952db919full, 0x256b24ca6b12f26dull, 0x5fb354025b277b14ull, 0xd0dbc55a0b79e09full, 0xaa03b5923b4c69e6ull, + 0xe553c1b9f35344e2ull, 0x9f8bb171c366cd9bull, 0x10e3202993385610ull, 0x6a3b50e1a30ddf69ull, 0x8e43c87e03060c18ull, 0xf49bb8b633338561ull, + 0x7bf329ee636d1eeaull, 0x012b592653589793ull, 0x4e7b2d0d9b47ba97ull, 0x34a35dc5ab7233eeull, 0xbbcbcc9dfb2ca865ull, 0xc113bc55cb19211cull, + 0x5863dbf1e3ac9decull, 0x22bbab39d3991495ull, 0xadd33a6183c78f1eull, 0xd70b4aa9b3f20667ull, 0x985b3e827bed2b63ull, 0xe2834e4a4bd8a21aull, + 0x6debdf121b863991ull, 0x1733afda2bb3b0e8ull, 0xf34b37458bb86399ull, 0x8993478dbb8deae0ull, 0x06fbd6d5ebd3716bull, 0x7c23a61ddbe6f812ull, + 0x3373d23613f9d516ull, 0x49aba2fe23cc5c6full, 0xc6c333a67392c7e4ull, 0xbc1b436e43a74e9dull, 0x95ac9329ac4bc9b5ull, 0xef74e3e19c7e40ccull, + 0x601c72b9cc20db47ull, 0x1ac40271fc15523eull, 0x5594765a340a7f3aull, 0x2f4c0692043ff643ull, 0xa02497ca54616dc8ull, 0xdafce7026454e4b1ull, + 0x3e847f9dc45f37c0ull, 0x445c0f55f46abeb9ull, 0xcb349e0da4342532ull, 0xb1eceec59401ac4bull, 0xfebc9aee5c1e814full, 0x8464ea266c2b0836ull, + 0x0b0c7b7e3c7593bdull, 0x71d40bb60c401ac4ull, 0xe8a46c1224f5a634ull, 0x927c1cda14c02f4dull, 0x1d148d82449eb4c6ull, 0x67ccfd4a74ab3dbfull, + 0x289c8961bcb410bbull, 0x5244f9a98c8199c2ull, 0xdd2c68f1dcdf0249ull, 0xa7f41839ecea8b30ull, 0x438c80a64ce15841ull, 0x3954f06e7cd4d138ull, + 0xb63c61362c8a4ab3ull, 0xcce411fe1cbfc3caull, 0x83b465d5d4a0eeceull, 0xf96c151de49567b7ull, 0x76048445b4cbfc3cull, 0x0cdcf48d84fe7545ull, + 0x6fbd6d5ebd3716b7ull, 0x15651d968d029fceull, 0x9a0d8ccedd5c0445ull, 0xe0d5fc06ed698d3cull, 0xaf85882d2576a038ull, 0xd55df8e515432941ull, + 0x5a3569bd451db2caull, 0x20ed197575283bb3ull, 0xc49581ead523e8c2ull, 0xbe4df122e51661bbull, 0x3125607ab548fa30ull, 0x4bfd10b2857d7349ull, + 0x04ad64994d625e4dull, 0x7e7514517d57d734ull, 0xf11d85092d094cbfull, 0x8bc5f5c11d3cc5c6ull, 0x12b5926535897936ull, 0x686de2ad05bcf04full, + 0xe70573f555e26bc4ull, 0x9ddd033d65d7e2bdull, 0xd28d7716adc8cfb9ull, 0xa85507de9dfd46c0ull, 0x273d9686cda3dd4bull, 0x5de5e64efd965432ull, + 0xb99d7ed15d9d8743ull, 0xc3450e196da80e3aull, 0x4c2d9f413df695b1ull, 0x36f5ef890dc31cc8ull, 0x79a59ba2c5dc31ccull, 0x037deb6af5e9b8b5ull, + 0x8c157a32a5b7233eull, 0xf6cd0afa9582aa47ull, 0x4ad64994d625e4daull, 0x300e395ce6106da3ull, 0xbf66a804b64ef628ull, 0xc5bed8cc867b7f51ull, + 0x8aeeace74e645255ull, 0xf036dc2f7e51db2cull, 0x7f5e4d772e0f40a7ull, 0x05863dbf1e3ac9deull, 0xe1fea520be311aafull, 0x9b26d5e88e0493d6ull, + 0x144e44b0de5a085dull, 0x6e963478ee6f8124ull, 0x21c640532670ac20ull, 0x5b1e309b16452559ull, 0xd476a1c3461bbed2ull, 0xaeaed10b762e37abull, + 0x37deb6af5e9b8b5bull, 0x4d06c6676eae0222ull, 0xc26e573f3ef099a9ull, 0xb8b627f70ec510d0ull, 0xf7e653dcc6da3dd4ull, 0x8d3e2314f6efb4adull, + 0x0256b24ca6b12f26ull, 0x788ec2849684a65full, 0x9cf65a1b368f752eull, 0xe62e2ad306bafc57ull, 0x6946bb8b56e467dcull, 0x139ecb4366d1eea5ull, + 0x5ccebf68aecec3a1ull, 0x2616cfa09efb4ad8ull, 0xa97e5ef8cea5d153ull, 0xd3a62e30fe90582aull, 0xb0c7b7e3c7593bd8ull, 0xca1fc72bf76cb2a1ull, + 0x45775673a732292aull, 0x3faf26bb9707a053ull, 0x70ff52905f188d57ull, 0x0a2722586f2d042eull, 0x854fb3003f739fa5ull, 0xff97c3c80f4616dcull, + 0x1bef5b57af4dc5adull, 0x61372b9f9f784cd4ull, 0xee5fbac7cf26d75full, 0x9487ca0fff135e26ull, 0xdbd7be24370c7322ull, 0xa10fceec0739fa5bull, + 0x2e675fb4576761d0ull, 0x54bf2f7c6752e8a9ull, 0xcdcf48d84fe75459ull, 0xb71738107fd2dd20ull, 0x387fa9482f8c46abull, 0x42a7d9801fb9cfd2ull, + 0x0df7adabd7a6e2d6ull, 0x772fdd63e7936bafull, 0xf8474c3bb7cdf024ull, 0x829f3cf387f8795dull, 0x66e7a46c27f3aa2cull, 0x1c3fd4a417c62355ull, + 0x935745fc4798b8deull, 0xe98f353477ad31a7ull, 0xa6df411fbfb21ca3ull, 0xdc0731d78f8795daull, 0x536fa08fdfd90e51ull, 0x29b7d047efec8728ull, + }; + + zpl_u32 zpl_crc32(void const *data, zpl_isize len) { + zpl_isize remaining; + zpl_u32 result = ~(cast(zpl_u32) 0); + zpl_u8 const *c = cast(zpl_u8 const *) data; + for (remaining = len; remaining--; c++) result = (result >> 8) ^ (zpl__crc32_table[(result ^ *c) & 0xff]); + return ~result; + } + + zpl_u64 zpl_crc64(void const *data, zpl_isize len) { + zpl_isize remaining; + zpl_u64 result = (cast(zpl_u64)0); + zpl_u8 const *c = cast(zpl_u8 const *) data; + for (remaining = len; remaining--; c++) result = (result >> 8) ^ (zpl__crc64_table[(result ^ *c) & 0xff]); + return result; + } + + zpl_u32 zpl_fnv32(void const *data, zpl_isize len) { + zpl_isize i; + zpl_u32 h = 0x811c9dc5; + zpl_u8 const *c = cast(zpl_u8 const *) data; + + for (i = 0; i < len; i++) h = (h * 0x01000193) ^ c[i]; + + return h; + } + + zpl_u64 zpl_fnv64(void const *data, zpl_isize len) { + zpl_isize i; + zpl_u64 h = 0xcbf29ce484222325ull; + zpl_u8 const *c = cast(zpl_u8 const *) data; + + for (i = 0; i < len; i++) h = (h * 0x100000001b3ll) ^ c[i]; + + return h; + } + + zpl_u32 zpl_fnv32a(void const *data, zpl_isize len) { + zpl_isize i; + zpl_u32 h = 0x811c9dc5; + zpl_u8 const *c = cast(zpl_u8 const *) data; + + for (i = 0; i < len; i++) h = (h ^ c[i]) * 0x01000193; + + return h; + } + + zpl_u64 zpl_fnv64a(void const *data, zpl_isize len) { + zpl_isize i; + zpl_u64 h = 0xcbf29ce484222325ull; + zpl_u8 const *c = cast(zpl_u8 const *) data; + + for (i = 0; i < len; i++) h = (h ^ c[i]) * 0x100000001b3ll; + + return h; + } + + // base64 implementation based on https://nachtimwald.com/2017/11/18/base64-encode-and-decode-in-c/ + // + zpl_global zpl_u8 zpl__base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + + + /* generated table based on: */ + #if 0 + void zpl__base64_decode_table() { + zpl_i32 inv[80]; + zpl_isize i; + + zpl_memset(inv, -1, zpl_size_of(inv)); + + for (i=0; i < zpl_size_of(zpl__base64_chars)-1; i++) { + inv[zpl__base64_chars[i]-43] = i; + } + } + #endif + /* === */ + zpl_global zpl_i32 zpl__base64_dec_table[] = { + 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, + 59, 60, 61, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, + 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, + 43, 44, 45, 46, 47, 48, 49, 50, 51 }; + + zpl_isize zpl__base64_encoded_size(zpl_isize len) { + zpl_isize ret = len; + + if (len % 3 != 0) { + ret += 3 - (len % 3); + } + + ret /= 3; + ret *= 4; + + return ret; + } + + zpl_isize zpl__base64_decoded_size(void const *data) { + zpl_isize len, ret, i; + const zpl_u8 *s = cast(const zpl_u8 *)data; + + if (s == NULL) { + return 0; + } + + len = zpl_strlen(cast(const char*)s); + ret = len / 4 * 3; + + for (i=len; i-- > 0;) { + if (s[i] == '=') { + ret--; + } else { + break; + } + } + + return ret; + } + + zpl_b32 zpl__base64_valid_char(zpl_u8 c) { + if (c >= '0' && c <= '9') + return true; + if (c >= 'A' && c <= 'Z') + return true; + if (c >= 'a' && c <= 'z') + return true; + if (c == '+' || c == '/' || c == '=') + return true; + + return false; + } + + zpl_u8 *zpl_base64_encode(zpl_allocator a, void const *data, zpl_isize len) { + const zpl_u8 *s = cast(const zpl_u8*)data; + zpl_u8 *ret = NULL; + zpl_isize enc_len, i, j, v; + + if (data == NULL || len == 0) { + return NULL; + } + + enc_len = zpl__base64_encoded_size(len); + ret = cast(zpl_u8 *)zpl_alloc(a, enc_len+1); + ret[enc_len] = 0; + + for (i=0, j=0; i < len; i+=3, j+=4) { + v = s[i]; + v = (i+1 < len) ? (v << 8 | s[i+1]) : (v << 8); + v = (i+2 < len) ? (v << 8 | s[i+2]) : (v << 8); + + ret[j] = zpl__base64_chars[(v >> 18) & 0x3F]; + ret[j+1] = zpl__base64_chars[(v >> 12) & 0x3F]; + + if (i+1 < len) + ret[j+2] = zpl__base64_chars[(v >> 6) & 0x3F]; + + else ret[j+2] = '='; + + if (i+2 < len) + ret[j+3] = zpl__base64_chars[v & 0x3F]; + + else ret[j+3] = '='; + + } + + return ret; + } + + zpl_u8 *zpl_base64_decode(zpl_allocator a, void const *data, zpl_isize len) { + const zpl_u8 *s = cast(const zpl_u8*)data; + zpl_u8 *ret = NULL; + zpl_isize alen, i, j, v; + + if (data == NULL) { + return NULL; + } + + alen = zpl__base64_decoded_size(s); + ret = cast(zpl_u8 *)zpl_alloc(a, alen+1); + + ZPL_ASSERT_NOT_NULL(ret); + + ret[alen] = 0; + + for (i=0; i> 16) & 0xFF; + + if (s[i+2] != '=') + ret[j+1] = (v >> 8) & 0xFF; + + if (s[i+3] != '=') + ret[j+2] = v & 0xFF; + } + + return ret; + } + + zpl_u32 zpl_murmur32_seed(void const *data, zpl_isize len, zpl_u32 seed) { + zpl_u32 const c1 = 0xcc9e2d51; + zpl_u32 const c2 = 0x1b873593; + zpl_u32 const r1 = 15; + zpl_u32 const r2 = 13; + zpl_u32 const m = 5; + zpl_u32 const n = 0xe6546b64; + + zpl_isize i, nblocks = len / 4; + zpl_u32 hash = seed, k1 = 0; + zpl_u32 const *blocks = cast(zpl_u32 const *) data; + zpl_u8 const *tail = cast(zpl_u8 const *)(data) + nblocks * 4; + + for (i = 0; i < nblocks; i++) { + zpl_u32 k = blocks[i]; + k *= c1; + k = (k << r1) | (k >> (32 - r1)); + k *= c2; + + hash ^= k; + hash = ((hash << r2) | (hash >> (32 - r2))) * m + n; + } + + switch (len & 3) { + case 3: k1 ^= tail[2] << 16; + case 2: k1 ^= tail[1] << 8; + case 1: + k1 ^= tail[0]; + + k1 *= c1; + k1 = (k1 << r1) | (k1 >> (32 - r1)); + k1 *= c2; + hash ^= k1; + } + + hash ^= len; + hash ^= (hash >> 16); + hash *= 0x85ebca6b; + hash ^= (hash >> 13); + hash *= 0xc2b2ae35; + hash ^= (hash >> 16); + + return hash; + } + + zpl_u64 zpl_murmur64_seed(void const *data_, zpl_isize len, zpl_u64 seed) { + zpl_u64 const m = 0xc6a4a7935bd1e995ULL; + zpl_i32 const r = 47; + + zpl_u64 h = seed ^ (len * m); + + zpl_u64 const *data = cast(zpl_u64 const *) data_; + zpl_u8 const *data2 = cast(zpl_u8 const *) data_; + zpl_u64 const *end = data + (len / 8); + + while (data != end) { + zpl_u64 k = *data++; + + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + } + + switch (len & 7) { + case 7: h ^= cast(zpl_u64)(data2[6]) << 48; + case 6: h ^= cast(zpl_u64)(data2[5]) << 40; + case 5: h ^= cast(zpl_u64)(data2[4]) << 32; + case 4: h ^= cast(zpl_u64)(data2[3]) << 24; + case 3: h ^= cast(zpl_u64)(data2[2]) << 16; + case 2: h ^= cast(zpl_u64)(data2[1]) << 8; + case 1: h ^= cast(zpl_u64)(data2[0]); + h *= m; + }; + + h ^= h >> r; + h *= m; + h ^= h >> r; + + return h; + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_REGEX) + // file: source/regex.c + + + ZPL_BEGIN_C_DECLS + + typedef enum zplreOp { + ZPL_RE_OP_BEGIN_CAPTURE, + ZPL_RE_OP_END_CAPTURE, + + ZPL_RE_OP_BEGINNING_OF_LINE, + ZPL_RE_OP_END_OF_LINE, + + ZPL_RE_OP_EXACT_MATCH, + ZPL_RE_OP_META_MATCH, + + ZPL_RE_OP_ANY, + ZPL_RE_OP_ANY_OF, + ZPL_RE_OP_ANY_BUT, + + ZPL_RE_OP_ZERO_OR_MORE, + ZPL_RE_OP_ONE_OR_MORE, + ZPL_RE_OP_ZERO_OR_MORE_SHORTEST, + ZPL_RE_OP_ONE_OR_MORE_SHORTEST, + ZPL_RE_OP_ZERO_OR_ONE, + + ZPL_RE_OP_BRANCH_START, + ZPL_RE_OP_BRANCH_END + } zplreOp; + + typedef enum zplreCode { + ZPL_RE_CODE_NULL = 0x0000, + ZPL_RE_CODE_WHITESPACE = 0x0100, + ZPL_RE_CODE_NOT_WHITESPACE = 0x0200, + ZPL_RE_CODE_DIGIT = 0x0300, + ZPL_RE_CODE_NOT_DIGIT = 0x0400, + ZPL_RE_CODE_ALPHA = 0x0500, + ZPL_RE_CODE_LOWER = 0x0600, + ZPL_RE_CODE_UPPER = 0x0700, + ZPL_RE_CODE_WORD = 0x0800, + ZPL_RE_CODE_NOT_WORD = 0x0900, + + ZPL_RE_CODE_XDIGIT = 0x0a00, + ZPL_RE_CODE_PRINTABLE = 0x0b00, + } zplreCode; + + typedef struct { + zpl_isize op, offset; + } zpl_re_ctx; + + enum { + ZPL_RE__NO_MATCH = -1, + ZPL_RE__INTERNAL_FAILURE = -2, + }; + + static char const ZPL_RE__META_CHARS[] = "^$()[].*+?|\\"; + static char const ZPL_RE__WHITESPACE[] = " \r\t\n\v\f"; + #define ZPL_RE__LITERAL(str) (str), zpl_size_of(str)-1 + + static zpl_re_ctx zpl_re__exec_single(zpl_re *re, zpl_isize op, char const *str, zpl_isize str_len, zpl_isize offset, zpl_re_capture *captures, zpl_isize max_capture_count); + static zpl_re_ctx zpl_re__exec(zpl_re *re, zpl_isize op, char const *str, zpl_isize str_len, zpl_isize offset, zpl_re_capture *captures, zpl_isize max_capture_count); + + static zpl_re_ctx zpl_re__ctx_no_match(zpl_isize op) { + zpl_re_ctx c; + c.op = op; + c.offset = ZPL_RE__NO_MATCH; + return c; + } + + static zpl_re_ctx zpl_re__ctx_internal_failure(zpl_isize op) { + zpl_re_ctx c; + c.op = op; + c.offset = ZPL_RE__INTERNAL_FAILURE; + return c; + } + + static zpl_u8 zpl_re__hex(char const *s) { + return ((zpl_char_to_hex_digit(*s) << 4) & 0xf0) | (zpl_char_to_hex_digit(*(s+1)) & 0x0f); + } + + static zpl_isize zpl_re__strfind(char const *s, zpl_isize len, char c, zpl_isize offset) { + if (offset < len) { + char const *found = (char const *)zpl_memchr(s+offset, c, len-offset); + if (found) + return found - s; + } + + return -1; + } + + static zpl_b32 zpl_re__match_escape(char c, int code) { + switch (code) { + case ZPL_RE_CODE_NULL: return c == 0; + case ZPL_RE_CODE_WHITESPACE: return zpl_re__strfind(ZPL_RE__LITERAL(ZPL_RE__WHITESPACE), c, 0) >= 0; + case ZPL_RE_CODE_NOT_WHITESPACE: return zpl_re__strfind(ZPL_RE__LITERAL(ZPL_RE__WHITESPACE), c, 0) < 0; + case ZPL_RE_CODE_DIGIT: return (c >= '0' && c <= '9'); + case ZPL_RE_CODE_NOT_DIGIT: return !(c >= '0' && c <= '9'); + case ZPL_RE_CODE_ALPHA: return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); + case ZPL_RE_CODE_LOWER: return (c >= 'a' && c <= 'z'); + case ZPL_RE_CODE_UPPER: return (c >= 'A' && c <= 'Z'); + + /* TODO(bill): Make better? */ + case ZPL_RE_CODE_WORD: return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'; + case ZPL_RE_CODE_NOT_WORD: return !((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '_'); + + /* TODO(bill): Maybe replace with between tests? */ + case ZPL_RE_CODE_XDIGIT: return zpl_re__strfind(ZPL_RE__LITERAL("0123456789ABCDEFabcdef"), c, 0) >= 0; + case ZPL_RE_CODE_PRINTABLE: return c >= 0x20 && c <= 0x7e; + default: break; + } + + return 0; + } + + static zpl_re_ctx zpl_re__consume(zpl_re *re, zpl_isize op, char const *str, zpl_isize str_len, zpl_isize offset, zpl_re_capture *captures, zpl_isize max_capture_count, zpl_b32 is_greedy) + { + zpl_re_ctx c, best_c, next_c; + + c.op = op; + c.offset = offset; + + best_c.op = ZPL_RE__NO_MATCH; + best_c.offset = offset; + + for (;;) { + c = zpl_re__exec_single(re, op, str, str_len, c.offset, 0, 0); + if (c.offset > str_len || c.offset == -1) break; + if (c.op >= re->buf_len) return c; + + next_c = zpl_re__exec(re, c.op, str, str_len, c.offset, captures, max_capture_count); + if (next_c.offset <= str_len) { + if (captures) + zpl_re__exec(re, c.op, str, str_len, c.offset, captures, max_capture_count); + + best_c = next_c; + if (!is_greedy) break; + } + + if (best_c.op > re->buf_len) + best_c.op = c.op; + + } + + return best_c; + } + + static zpl_re_ctx zpl_re__exec_single(zpl_re *re, zpl_isize op, char const *str, zpl_isize str_len, zpl_isize offset, zpl_re_capture *captures, zpl_isize max_capture_count) { + zpl_re_ctx ctx; + zpl_isize buffer_len; + zpl_isize match_len; + zpl_isize next_op; + zpl_isize skip; + + switch (re->buf[op++]) { + case ZPL_RE_OP_BEGIN_CAPTURE: { + zpl_u8 capture = re->buf[op++]; + if (captures && (capture < max_capture_count)) + captures[capture].str = str + offset; + } break; + + case ZPL_RE_OP_END_CAPTURE: { + zpl_u8 capture = re->buf[op++]; + if (captures && (capture < max_capture_count)) + captures[capture].len = (str + offset) - captures[capture].str; + } break; + + case ZPL_RE_OP_BEGINNING_OF_LINE: { + if (offset != 0) + return zpl_re__ctx_no_match(op); + } break; + + case ZPL_RE_OP_END_OF_LINE: { + if (offset != str_len) + return zpl_re__ctx_no_match(op); + } break; + + case ZPL_RE_OP_BRANCH_START: { + skip = re->buf[op++]; + ctx = zpl_re__exec(re, op, str, str_len, offset, captures, max_capture_count); + if (ctx.offset <= str_len) { + offset = ctx.offset; + op = ctx.op; + } else { + ctx = zpl_re__exec(re, op + skip, str, str_len, offset, captures, max_capture_count); + offset = ctx.offset; + op = ctx.op; + } + } break; + + case ZPL_RE_OP_BRANCH_END: { + skip = re->buf[op++]; + op += skip; + } break; + + case ZPL_RE_OP_ANY: { + if (offset < str_len) { + offset++; + break; + } + return zpl_re__ctx_no_match(op); + } break; + + case ZPL_RE_OP_ANY_OF: { + zpl_isize i; + char cin = str[offset]; + buffer_len = re->buf[op++]; + + if (offset >= str_len) + return zpl_re__ctx_no_match(op + buffer_len); + + for (i = 0; i < buffer_len; i++) { + char cmatch = (char)re->buf[op+i]; + if (!cmatch) { + i++; + if (zpl_re__match_escape(cin, re->buf[op+i] << 8)) + break; + } else if (cin == cmatch) { + break; + } + } + + if (i == buffer_len) + return zpl_re__ctx_no_match(op + buffer_len); + + offset++; + op += buffer_len; + } break; + + case ZPL_RE_OP_ANY_BUT: { + zpl_isize i; + char cin = str[offset]; + buffer_len = re->buf[op++]; + + if (offset >= str_len) + return zpl_re__ctx_no_match(op + buffer_len); + + for (i = 0; i < buffer_len; i++) { + char cmatch = (char)re->buf[op + i]; + if (!cmatch) { + i++; + if (zpl_re__match_escape(cin, re->buf[op+i] << 8)) + return zpl_re__ctx_no_match(op + buffer_len); + } else if (cin == cmatch) { + return zpl_re__ctx_no_match(op + buffer_len); + } + } + + offset++; + op += buffer_len; + } break; + + case ZPL_RE_OP_EXACT_MATCH: { + match_len = re->buf[op++]; + + if ((match_len > (str_len - offset)) || + zpl_strncmp(str+offset, (const char*)re->buf + op, match_len) != 0) + return zpl_re__ctx_no_match(op + match_len); + + op += match_len; + offset += match_len; + } break; + + case ZPL_RE_OP_META_MATCH: { + char cin = (char)re->buf[op++]; + char cmatch = str[offset++]; + + if (!cin) { + if (zpl_re__match_escape(cmatch, re->buf[op++] << 8)) + break; + } + else if (cin == cmatch) break; + + return zpl_re__ctx_no_match(op); + } break; + + case ZPL_RE_OP_ZERO_OR_MORE: { + ctx = zpl_re__consume(re, op, str, str_len, offset, captures, max_capture_count, 1); + offset = ctx.offset; + op = ctx.op; + } break; + + case ZPL_RE_OP_ONE_OR_MORE: { + ctx = zpl_re__exec_single(re, op, str, str_len, offset, captures, max_capture_count); + + if (ctx.offset > str_len) + return ctx; + + ctx = zpl_re__consume(re, op, str, str_len, offset, captures, max_capture_count, 1); + offset = ctx.offset; + op = ctx.op; + } break; + + case ZPL_RE_OP_ZERO_OR_MORE_SHORTEST: { + ctx = zpl_re__consume(re, op, str, str_len, offset, captures, max_capture_count, 0); + offset = ctx.offset; + op = ctx.op; + } break; + + case ZPL_RE_OP_ONE_OR_MORE_SHORTEST: { + ctx = zpl_re__exec_single(re, op, str, str_len, offset, captures, max_capture_count); + + if (ctx.offset > str_len) + return ctx; + + ctx = zpl_re__consume(re, op, str, str_len, offset, captures, max_capture_count, 0); + offset = ctx.offset; + op = ctx.op; + } break; + + case ZPL_RE_OP_ZERO_OR_ONE: { + ctx = zpl_re__exec_single(re, op, str, str_len, offset, captures, max_capture_count); + + if (ctx.offset <= str_len) { + zpl_re_ctx possible_ctx = zpl_re__exec(re, ctx.op, str, str_len, ctx.offset, captures, max_capture_count); + + if (possible_ctx.offset <= str_len) { + op = possible_ctx.op; + offset = possible_ctx.offset; + break; + } + } + + next_op = ctx.op; + ctx = zpl_re__exec(re, next_op, str, str_len, offset, captures, max_capture_count); + + if (ctx.offset <= str_len) { + op = ctx.op; + offset = ctx.offset; + break; + } + return zpl_re__ctx_no_match(op); + } break; + + default: { + return zpl_re__ctx_internal_failure(op); + } break; + } + + ctx.op = op; + ctx.offset = offset; + + return ctx; + } + + static zpl_re_ctx zpl_re__exec(zpl_re *re, zpl_isize op, char const *str, zpl_isize str_len, zpl_isize offset, zpl_re_capture *captures, zpl_isize max_capture_count) { + zpl_re_ctx c; + c.op = op; + c.offset = offset; + + while (c.op < re->buf_len) { + c = zpl_re__exec_single(re, c.op, str, str_len, c.offset, captures, max_capture_count); + + if (c.offset > str_len || c.offset == -1) + break; + } + + return c; + } + + static zpl_regex_error zpl_re__emit_ops(zpl_re *re, zpl_isize op_count, ...) { + va_list va; + + if (re->buf_len + op_count > re->buf_cap) { + if (!re->can_realloc) { + return ZPL_RE_ERROR_TOO_LONG; + } + else { + zpl_isize new_cap = (re->buf_cap*2) + op_count; + re->buf = (char *)zpl_resize(re->backing, re->buf, re->buf_cap, new_cap); + re->buf_cap = new_cap; + } + } + + va_start(va, op_count); + for (zpl_isize i = 0; i < op_count; i++) + { + zpl_i32 v = va_arg(va, zpl_i32); + if (v > 256) + return ZPL_RE_ERROR_TOO_LONG; + re->buf[re->buf_len++] = (char)v; + } + va_end(va); + + return ZPL_RE_ERROR_NONE; + } + + static zpl_regex_error zpl_re__emit_ops_buffer(zpl_re *re, zpl_isize op_count, char const *buffer) { + if (re->buf_len + op_count > re->buf_cap) { + if (!re->can_realloc) { + return ZPL_RE_ERROR_TOO_LONG; + } + else { + zpl_isize new_cap = (re->buf_cap*2) + op_count; + re->buf = (char *)zpl_resize(re->backing, re->buf, re->buf_cap, new_cap); + re->buf_cap = new_cap; + } + } + + for (zpl_isize i = 0; i < op_count; i++) + { + re->buf[re->buf_len++] = buffer[i]; + } + + return ZPL_RE_ERROR_NONE; + } + + static int zpl_re__encode_escape(char code) { + switch (code) { + default: break; /* NOTE(bill): It's a normal character */ + + /* TODO(bill): Are there anymore? */ + case 't': return '\t'; + case 'n': return '\n'; + case 'r': return '\r'; + case 'f': return '\f'; + case 'v': return '\v'; + + case '0': return ZPL_RE_CODE_NULL; + + case 's': return ZPL_RE_CODE_WHITESPACE; + case 'S': return ZPL_RE_CODE_NOT_WHITESPACE; + + case 'd': return ZPL_RE_CODE_DIGIT; + case 'D': return ZPL_RE_CODE_NOT_DIGIT; + + case 'a': return ZPL_RE_CODE_ALPHA; + case 'l': return ZPL_RE_CODE_LOWER; + case 'u': return ZPL_RE_CODE_UPPER; + + case 'w': return ZPL_RE_CODE_WORD; + case 'W': return ZPL_RE_CODE_NOT_WORD; + + case 'x': return ZPL_RE_CODE_XDIGIT; + case 'p': return ZPL_RE_CODE_PRINTABLE; + } + return code; + } + + static zpl_regex_error zpl_re__parse_group(zpl_re *re, char const *pattern, zpl_isize len, zpl_isize offset, zpl_isize *new_offset) { + zpl_regex_error err = ZPL_RE_ERROR_NONE; + char buffer[256] = {0}; + zpl_isize buffer_len = 0, buffer_cap = zpl_size_of(buffer); + zpl_b32 closed = 0; + zplreOp op = ZPL_RE_OP_ANY_OF; + + if (pattern[offset] == '^') { + offset++; + op = ZPL_RE_OP_ANY_BUT; + } + + while(!closed && + err == ZPL_RE_ERROR_NONE && + offset < len) + { + if (pattern[offset] == ']') { + err = zpl_re__emit_ops(re, 2, (zpl_i32)op, (zpl_i32)buffer_len); + if (err) break; + + err = zpl_re__emit_ops_buffer(re, buffer_len, (const char*)buffer); + if (err) break; + offset++; + closed = 1; + break; + } + + if (buffer_len >= buffer_cap) + return ZPL_RE_ERROR_TOO_LONG; + + if (pattern[offset] == '\\') { + offset++; + + if ((offset + 1 < len) && zpl_char_is_hex_digit(*(pattern+offset))) { + buffer[buffer_len++] = zpl_re__hex((pattern+offset)); + offset++; + } + else if (offset < len) { + zpl_i32 code = zpl_re__encode_escape(pattern[offset]); + + if (!code || code > 0xff) { + buffer[buffer_len++] = 0; + + if (buffer_len >= buffer_cap) + return ZPL_RE_ERROR_TOO_LONG; + + buffer[buffer_len++] = (code >> 8) & 0xff; + } + else { + buffer[buffer_len++] = code & 0xff; + } + } + } + else { + buffer[buffer_len++] = (unsigned char)pattern[offset]; + } + + offset++; + } + + if (err) return err; + if (!closed) return ZPL_RE_ERROR_MISMATCHED_BLOCKS; + if (new_offset) *new_offset = offset; + return ZPL_RE_ERROR_NONE; + } + + static zpl_regex_error zpl_re__compile_quantifier(zpl_re *re, zpl_isize last_buf_len, unsigned char quantifier) { + zpl_regex_error err; + zpl_isize move_size; + + if ((re->buf[last_buf_len] == ZPL_RE_OP_EXACT_MATCH) && + (re->buf[last_buf_len+1] > 1)) + { + unsigned char last_char = re->buf[re->buf_len-1]; + + re->buf[last_buf_len+1]--; + re->buf_len--; + err = zpl_re__emit_ops(re, 4, (zpl_i32)quantifier, (zpl_i32)ZPL_RE_OP_EXACT_MATCH, 1, (zpl_i32)last_char); + if (err) return err; + return ZPL_RE_ERROR_NONE; + } + + move_size = re->buf_len - last_buf_len + 1; + + err = zpl_re__emit_ops(re, 1, 0); + if (err) return err; + + zpl_memmove(re->buf+last_buf_len+1, re->buf+last_buf_len, move_size); + re->buf[last_buf_len] = quantifier; + + return ZPL_RE_ERROR_NONE; + } + + static zpl_regex_error zpl_re__parse(zpl_re *re, char const *pattern, zpl_isize len, zpl_isize offset, zpl_isize level, zpl_isize *new_offset) { + zpl_regex_error err = ZPL_RE_ERROR_NONE; + zpl_isize last_buf_len = re->buf_len; + zpl_isize branch_begin = re->buf_len; + zpl_isize branch_op = -1; + + while (offset < len) { + switch (pattern[offset++]) { + case '^': { + err = zpl_re__emit_ops(re, 1, ZPL_RE_OP_BEGINNING_OF_LINE); + if (err) return err; + } break; + + case '$': { + err = zpl_re__emit_ops(re, 1, ZPL_RE_OP_END_OF_LINE); + if (err) return err; + } break; + + case '(': { + zpl_isize capture = re->capture_count++; + last_buf_len = re->buf_len; + err = zpl_re__emit_ops(re, 2, ZPL_RE_OP_BEGIN_CAPTURE, (zpl_i32)capture); + if (err) return err; + + err = zpl_re__parse(re, pattern, len, offset, level+1, &offset); + + if ((offset > len) || (pattern[offset-1] != ')')) + return ZPL_RE_ERROR_MISMATCHED_CAPTURES; + + err = zpl_re__emit_ops(re, 2, ZPL_RE_OP_END_CAPTURE, (zpl_i32)capture); + if (err) return err; + } break; + + case ')': { + if (branch_op != -1) + re->buf[branch_op + 1] = (unsigned char)(re->buf_len - (branch_op+2)); + + if (level == 0) + return ZPL_RE_ERROR_MISMATCHED_CAPTURES; + + if (new_offset) *new_offset = offset; + return ZPL_RE_ERROR_NONE; + } break; + + case '[': { + last_buf_len = re->buf_len; + err = zpl_re__parse_group(re, pattern, len, offset, &offset); + if (offset > len) + return err; + } break; + + /* NOTE(bill): Branching magic! */ + case '|': { + if (branch_begin >= re->buf_len) { + return ZPL_RE_ERROR_BRANCH_FAILURE; + } else { + zpl_isize size = re->buf_len - branch_begin; + err = zpl_re__emit_ops(re, 4, 0, 0, ZPL_RE_OP_BRANCH_END, 0); + if (err) return err; + + zpl_memmove(re->buf + branch_begin + 2, re->buf + branch_begin, size); + re->buf[branch_begin] = ZPL_RE_OP_BRANCH_START; + re->buf[branch_begin+1] = (size+2) & 0xff; + branch_op = re->buf_len-2; + } + } break; + + case '.': { + last_buf_len = re->buf_len; + err = zpl_re__emit_ops(re, 1, ZPL_RE_OP_ANY); + if (err) return err; + } break; + + case '*': + case '+': + { + unsigned char quantifier = ZPL_RE_OP_ONE_OR_MORE; + if (pattern[offset-1] == '*') + quantifier = ZPL_RE_OP_ZERO_OR_MORE; + + if (last_buf_len >= re->buf_len) + return ZPL_RE_ERROR_INVALID_QUANTIFIER; + if ((re->buf[last_buf_len] < ZPL_RE_OP_EXACT_MATCH) || + (re->buf[last_buf_len] > ZPL_RE_OP_ANY_BUT)) + return ZPL_RE_ERROR_INVALID_QUANTIFIER; + + if ((offset < len) && (pattern[offset] == '?')) { + quantifier = ZPL_RE_OP_ONE_OR_MORE_SHORTEST; + offset++; + } + + err = zpl_re__compile_quantifier(re, last_buf_len, quantifier); + if (err) return err; + } break; + + case '?': { + if (last_buf_len >= re->buf_len) + return ZPL_RE_ERROR_INVALID_QUANTIFIER; + if ((re->buf[last_buf_len] < ZPL_RE_OP_EXACT_MATCH) || + (re->buf[last_buf_len] > ZPL_RE_OP_ANY_BUT)) + return ZPL_RE_ERROR_INVALID_QUANTIFIER; + + err = zpl_re__compile_quantifier(re, last_buf_len, + (unsigned char)ZPL_RE_OP_ZERO_OR_ONE); + if (err) return err; + } break; + + case '\\': { + last_buf_len = re->buf_len; + if ((offset+1 < len) && zpl_char_is_hex_digit(*(pattern+offset))) { + unsigned char hex_value = zpl_re__hex((pattern+offset)); + offset += 2; + err = zpl_re__emit_ops(re, 2, ZPL_RE_OP_META_MATCH, (int)hex_value); + if (err) return err; + } else if (offset < len) { + int code = zpl_re__encode_escape(pattern[offset++]); + if (!code || (code > 0xff)) { + err = zpl_re__emit_ops(re, 3, ZPL_RE_OP_META_MATCH, 0, (int)((code >> 8) & 0xff)); + if (err) return err; + } else { + err = zpl_re__emit_ops(re, 2, ZPL_RE_OP_META_MATCH, (int)code); + if (err) return err; + } + } + } break; + + /* NOTE(bill): Exact match */ + default: { + char const *match_start; + zpl_isize size = 0; + offset--; + match_start = pattern+offset; + while ((offset < len) && + (zpl_re__strfind(ZPL_RE__LITERAL(ZPL_RE__META_CHARS), pattern[offset], 0) < 0)) { + size++, offset++; + } + + last_buf_len = re->buf_len; + err = zpl_re__emit_ops(re, 2, ZPL_RE_OP_EXACT_MATCH, (int)size); + if (err) return err; + err = zpl_re__emit_ops_buffer(re, size, (char const *)match_start); + if (err) return err; + } break; + } + } + + if (new_offset) *new_offset = offset; + return ZPL_RE_ERROR_NONE; + } + + zpl_regex_error zpl_re_compile_from_buffer(zpl_re *re, char const *pattern, zpl_isize pattern_len, void *buffer, zpl_isize buffer_len) { + zpl_regex_error err; + re->capture_count = 0; + re->buf = (char *)buffer; + re->buf_len = 0; + re->buf_cap = re->buf_len; + re->can_realloc = 0; + + err = zpl_re__parse(re, pattern, pattern_len, 0, 0, 0); + return err; + } + + zpl_regex_error zpl_re_compile(zpl_re *re, zpl_allocator backing, char const *pattern, zpl_isize pattern_len) { + zpl_regex_error err; + zpl_isize cap = pattern_len+128; + zpl_isize offset = 0; + + re->backing = backing; + re->capture_count = 0; + re->buf = (char *)zpl_alloc(backing, cap); + re->buf_len = 0; + re->buf_cap = cap; + re->can_realloc = 1; + + err = zpl_re__parse(re, pattern, pattern_len, 0, 0, &offset); + + if (offset != pattern_len) + zpl_free(backing, re->buf); + + return err; + } + + zpl_isize zpl_re_capture_count(zpl_re *re) { return re->capture_count; } + + zpl_b32 zpl_re_match(zpl_re *re, char const *str, zpl_isize len, zpl_re_capture *captures, zpl_isize max_capture_count, zpl_isize *offset) { + if (re && re->buf_len > 0) { + if (re->buf[0] == ZPL_RE_OP_BEGINNING_OF_LINE) { + zpl_re_ctx c = zpl_re__exec(re, 0, str, len, 0, captures, max_capture_count); + if (c.offset >= 0 && c.offset <= len) { if (offset) *offset = c.offset; return 1; }; + if (c.offset == ZPL_RE__INTERNAL_FAILURE) return 0; + } else { + zpl_isize i; + for (i = 0; i < len; i++) { + zpl_re_ctx c = zpl_re__exec(re, 0, str, len, i, captures, max_capture_count); + if (c.offset >= 0 && c.offset <= len) { if (offset) *offset = c.offset; return 1; }; + if (c.offset == ZPL_RE__INTERNAL_FAILURE) return 0; + } + } + return 0; + } + return 1; + } + + + zpl_b32 zpl_re_match_all(zpl_re *re, char const *str, zpl_isize str_len, zpl_isize max_capture_count, + zpl_re_capture **out_captures) + { + char *end = (char *)str + str_len; + char *p = (char *)str; + + zpl_buffer_make(zpl_re_capture, cps, zpl_heap(), max_capture_count); + + zpl_isize offset = 0; + + while (p < end) + { + zpl_b32 ok = zpl_re_match(re, p, end - p, cps, max_capture_count, &offset); + if (!ok) { + zpl_buffer_free(cps); + return false; + } + + p += offset; + + for (zpl_isize i = 0; i < max_capture_count; i++) { + zpl_array_append(*out_captures, cps[i]); + } + } + + zpl_buffer_free(cps); + + return true; + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_DLL) + // file: source/dll.c + + + #if defined(ZPL_SYSTEM_UNIX) || defined(ZPL_SYSTEM_MACOS) + # include + #endif + + ZPL_BEGIN_C_DECLS + + //////////////////////////////////////////////////////////////// + // + // DLL Handling + // + // + + #if defined(ZPL_SYSTEM_WINDOWS) + zpl_dll_handle zpl_dll_load(char const *filepath) { + return cast(zpl_dll_handle) LoadLibraryA(filepath); + } + + void zpl_dll_unload(zpl_dll_handle dll) { + FreeLibrary(cast(HMODULE) dll); + } + + zpl_dll_proc zpl_dll_proc_address(zpl_dll_handle dll, char const *proc_name) { + return cast(zpl_dll_proc) GetProcAddress(cast(HMODULE) dll, proc_name); + } + + #else // POSIX + + zpl_dll_handle zpl_dll_load(char const *filepath) { + return cast(zpl_dll_handle) dlopen(filepath, RTLD_LAZY | RTLD_GLOBAL); + } + + void zpl_dll_unload(zpl_dll_handle dll) { + dlclose(dll); + } + + zpl_dll_proc zpl_dll_proc_address(zpl_dll_handle dll, char const *proc_name) { + return cast(zpl_dll_proc) dlsym(dll, proc_name); + } + + #endif + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_OPTS) + // file: source/opts.c + + //////////////////////////////////////////////////////////////// + // + // CLI Options + // + // + + ZPL_BEGIN_C_DECLS + + void zpl_opts_init(zpl_opts *opts, zpl_allocator a, char const *app) { + zpl_opts opts_ = { 0 }; + *opts = opts_; + opts->alloc = a; + opts->appname = app; + + zpl_array_init(opts->entries, a); + zpl_array_init(opts->positioned, a); + zpl_array_init(opts->errors, a); + } + + void zpl_opts_free(zpl_opts *opts) { + for (zpl_i32 i = 0; i < zpl_array_count(opts->entries); ++i) { + zpl_opts_entry *e = opts->entries + i; + if (e->type == ZPL_OPTS_STRING) { + zpl_string_free(e->text); + } + } + + zpl_array_free(opts->entries); + zpl_array_free(opts->positioned); + zpl_array_free(opts->errors); + } + + void zpl_opts_add(zpl_opts *opts, char const *name, char const *lname, const char *desc, zpl_u8 type) { + zpl_opts_entry e = { 0 }; + + e.name = name; + e.lname = lname; + e.desc = desc; + e.type = type; + e.met = false; + e.pos = false; + + zpl_array_append(opts->entries, e); + } + + zpl_opts_entry *zpl__opts_find(zpl_opts *opts, char const *name, zpl_usize len, zpl_b32 longname) { + zpl_opts_entry *e = 0; + + for (int i = 0; i < zpl_array_count(opts->entries); ++i) { + e = opts->entries + i; + char const *n = (longname ? e->lname : e->name); + if(!n) continue; + + if (zpl_strnlen(name, len) == zpl_strlen(n) && !zpl_strncmp(n, name, len)) { return e; } + } + + return NULL; + } + + void zpl_opts_positional_add(zpl_opts *opts, char const *name) { + zpl_opts_entry *e = zpl__opts_find(opts, name, zpl_strlen(name), true); + + if (e) { + e->pos = true; + zpl_array_append_at(opts->positioned, e, 0); + } + } + + zpl_b32 zpl_opts_positionals_filled(zpl_opts *opts) { return zpl_array_count(opts->positioned) == 0; } + + zpl_string zpl_opts_string(zpl_opts *opts, char const *name, char const *fallback) { + zpl_opts_entry *e = zpl__opts_find(opts, name, zpl_strlen(name), true); + + return (char *)((e && e->met) ? e->text : fallback); + } + + zpl_f64 zpl_opts_real(zpl_opts *opts, char const *name, zpl_f64 fallback) { + zpl_opts_entry *e = zpl__opts_find(opts, name, zpl_strlen(name), true); + + return (e && e->met) ? e->real : fallback; + } + + zpl_i64 zpl_opts_integer(zpl_opts *opts, char const *name, zpl_i64 fallback) { + zpl_opts_entry *e = zpl__opts_find(opts, name, zpl_strlen(name), true); + + return (e && e->met) ? e->integer : fallback; + } + + void zpl__opts_set_value(zpl_opts *opts, zpl_opts_entry *t, char *b) { + t->met = true; + + switch (t->type) { + case ZPL_OPTS_STRING: { + t->text = zpl_string_make(opts->alloc, b); + } break; + + case ZPL_OPTS_FLOAT: { + t->real = zpl_str_to_f64(b, NULL); + } break; + + case ZPL_OPTS_INT: { + t->integer = zpl_str_to_i64(b, NULL, 10); + } break; + } + + for (zpl_isize i=0; i < zpl_array_count(opts->positioned); i++) { + if (!zpl_strcmp(opts->positioned[i]->lname, t->lname)) { + zpl_array_remove_at(opts->positioned, i); + break; + } + } + } + + zpl_b32 zpl_opts_has_arg(zpl_opts *opts, char const *name) { + zpl_opts_entry *e = zpl__opts_find(opts, name, zpl_strlen(name), true); + + if (e) { return e->met; } + + return false; + } + + void zpl_opts_print_help(zpl_opts *opts) { + zpl_printf("USAGE: %s", opts->appname); + + for (zpl_isize i = zpl_array_count(opts->entries); i >= 0; --i) { + zpl_opts_entry *e = opts->entries + i; + + if (e->pos == (zpl_b32) true) { zpl_printf(" [%s]", e->lname); } + } + + zpl_printf("\nOPTIONS:\n"); + + for (zpl_isize i = 0; i < zpl_array_count(opts->entries); ++i) { + zpl_opts_entry *e = opts->entries + i; + + if(e->name) { + if(e->lname) { zpl_printf("\t-%s, --%s: %s\n", e->name, e->lname, e->desc); } + else { zpl_printf("\t-%s: %s\n", e->name, e->desc); } + } else { zpl_printf("\t--%s: %s\n", e->lname, e->desc); } + } + } + + void zpl_opts_print_errors(zpl_opts *opts) { + for (int i = 0; i < zpl_array_count(opts->errors); ++i) { + zpl_opts_err *err = (opts->errors + i); + + zpl_printf("ERROR: "); + + switch (err->type) { + case ZPL_OPTS_ERR_OPTION: zpl_printf("Invalid option \"%s\"", err->val); break; + + case ZPL_OPTS_ERR_VALUE: zpl_printf("Invalid value \"%s\"", err->val); break; + + case ZPL_OPTS_ERR_MISSING_VALUE: zpl_printf("Missing value for option \"%s\"", err->val); break; + + case ZPL_OPTS_ERR_EXTRA_VALUE: zpl_printf("Extra value for option \"%s\"", err->val); break; + } + + zpl_printf("\n"); + } + } + + void zpl__opts_push_error(zpl_opts *opts, char *b, zpl_u8 errtype) { + zpl_opts_err err = { 0 }; + err.val = b; + err.type = errtype; + zpl_array_append(opts->errors, err); + } + + zpl_b32 zpl_opts_compile(zpl_opts *opts, int argc, char **argv) { + zpl_b32 had_errors = false; + for (int i = 1; i < argc; ++i) { + char *p = argv[i]; + + if (*p) { + p = cast(char *)zpl_str_trim(p, false); + if (*p == '-') { + zpl_opts_entry *t = 0; + zpl_b32 checkln = false; + if (*(p + 1) == '-') { + checkln = true; + ++p; + } + + char *b = p + 1, *e = b; + + while (zpl_char_is_alphanumeric(*e) || *e == '-' || *e == '_') { ++e; } + + t = zpl__opts_find(opts, b, (e - b), checkln); + + if (t) { + char *ob = b; + b = e; + + /**/ if (*e == '=') { + if (t->type == ZPL_OPTS_FLAG) { + *e = '\0'; + zpl__opts_push_error(opts, ob, ZPL_OPTS_ERR_EXTRA_VALUE); + had_errors = true; + continue; + } + + b = e = e + 1; + } else if (*e == '\0') { + char *sp = argv[i+1]; + + if (sp && *sp != '-' && (zpl_array_count(opts->positioned) < 1 || t->type != ZPL_OPTS_FLAG)) { + if (t->type == ZPL_OPTS_FLAG) { + zpl__opts_push_error(opts, b, ZPL_OPTS_ERR_EXTRA_VALUE); + had_errors = true; + continue; + } + + p = sp; + b = e = sp; + ++i; + } else { + if (t->type != ZPL_OPTS_FLAG) { + zpl__opts_push_error(opts, ob, ZPL_OPTS_ERR_MISSING_VALUE); + had_errors = true; + continue; + } + t->met = true; + continue; + } + } + + e = cast(char *)zpl_str_control_skip(e, '\0'); + zpl__opts_set_value(opts, t, b); + } else { + zpl__opts_push_error(opts, b, ZPL_OPTS_ERR_OPTION); + had_errors = true; + } + } else if (zpl_array_count(opts->positioned)) { + zpl_opts_entry *l = zpl_array_back(opts->positioned); + zpl_array_pop(opts->positioned); + zpl__opts_set_value(opts, l, p); + } else { + zpl__opts_push_error(opts, p, ZPL_OPTS_ERR_VALUE); + had_errors = true; + } + } + } + return !had_errors; + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_PROCESS) + // file: source/process.c + + //////////////////////////////////////////////////////////////// + // + // Process creation and manipulation methods + // + // + + ZPL_BEGIN_C_DECLS + + static ZPL_ALWAYS_INLINE void zpl__pr_close_file_handle(zpl_file *f) { + ZPL_ASSERT_NOT_NULL(f); + f->fd.p = NULL; + } + + static ZPL_ALWAYS_INLINE void zpl__pr_close_file_handles(zpl_pr *process) { + ZPL_ASSERT_NOT_NULL(process); + + zpl__pr_close_file_handle(&process->in); + zpl__pr_close_file_handle(&process->out); + zpl__pr_close_file_handle(&process->err); + + process->f_stdin = process->f_stdout = process->f_stderr = NULL; + + #ifdef ZPL_SYSTEM_WINDOWS + process->win32_handle = NULL; + #else + ZPL_NOT_IMPLEMENTED; + #endif + } + + enum { + ZPL_PR_HANDLE_MODE_READ, + ZPL_PR_HANDLE_MODE_WRITE, + ZPL_PR_HANDLE_MODES, + }; + + void *zpl__pr_open_handle(zpl_u8 type, const char *mode, void **handle) { + #ifdef ZPL_SYSTEM_WINDOWS + void *pipes[ZPL_PR_HANDLE_MODES]; + zpl_i32 fd; + + const zpl_u32 flag_inherit = 0x00000001; + SECURITY_ATTRIBUTES sa = {zpl_size_of(sa), 0, 1}; + + if (!CreatePipe(&pipes[0], &pipes[1], cast(LPSECURITY_ATTRIBUTES)&sa, 0)) { + return NULL; + } + + if (!SetHandleInformation(pipes[type], flag_inherit, 0)) { + return NULL; + } + + fd = _open_osfhandle(cast(zpl_intptr)pipes[type], 0); + + if (fd != -1) { + *handle = pipes[1-type]; + return _fdopen(fd, mode); + } + + return NULL; + #else + ZPL_NOT_IMPLEMENTED; + return NULL; + #endif + } + + zpl_i32 zpl_pr_create(zpl_pr *process, const char **args, zpl_isize argc, zpl_pr_si si, zpl_pr_opts options) { + ZPL_ASSERT_NOT_NULL(process); + zpl_zero_item(process); + + #ifdef ZPL_SYSTEM_WINDOWS + zpl_string cli, env; + zpl_b32 c_env=false; + STARTUPINFOW psi = {0}; + PROCESS_INFORMATION pi = {0}; + zpl_i32 err_code = 0; + zpl_allocator a = zpl_heap(); + const zpl_u32 use_std_handles = 0x00000100; + + psi.cb = zpl_size_of(psi); + psi.dwFlags = use_std_handles | si.flags; + + if (options & ZPL_PR_OPTS_CUSTOM_ENV) { + env = zpl_string_join(zpl_heap(), cast(const char**)si.env, si.env_count, "\0\0"); + env = zpl_string_appendc(env, "\0"); + c_env = true; + } + else if (!(options & ZPL_PR_OPTS_INHERIT_ENV)) { + env = (zpl_string)"\0\0\0\0"; + } else { + env = (zpl_string)NULL; + } + + process->f_stdin = zpl__pr_open_handle(ZPL_PR_HANDLE_MODE_WRITE, "wb", &psi.hStdInput); + process->f_stdout = zpl__pr_open_handle(ZPL_PR_HANDLE_MODE_READ, "rb", &psi.hStdOutput); + + if (options & ZPL_PR_OPTS_COMBINE_STD_OUTPUT) { + process->f_stderr = process->f_stdout; + psi.hStdError = psi.hStdOutput; + } else { + process->f_stderr = zpl__pr_open_handle(ZPL_PR_HANDLE_MODE_READ, "rb", &psi.hStdError); + } + + cli = zpl_string_join(zpl_heap(), args, argc, " "); + + psi.dwX = si.posx; + psi.dwY = si.posy; + psi.dwXSize = si.resx; + psi.dwYSize = si.resy; + psi.dwXCountChars = si.bufx; + psi.dwYCountChars = si.bufy; + psi.dwFillAttribute = si.fill_attr; + psi.wShowWindow = si.show_window; + + wchar_t *w_cli = zpl__alloc_utf8_to_ucs2(a, cli, NULL); + wchar_t *w_workdir = zpl__alloc_utf8_to_ucs2(a, si.workdir, NULL); + + if (!CreateProcessW( + NULL, + w_cli, + NULL, + NULL, + 1, + 0, + env, + w_workdir, + cast(LPSTARTUPINFOW)&psi, + cast(LPPROCESS_INFORMATION)&pi + )) { + err_code = -1; + goto pr_free_data; + } + + process->win32_handle = pi.hProcess; + CloseHandle(pi.hThread); + + zpl_file_connect_handle(&process->in, process->f_stdin); + zpl_file_connect_handle(&process->out, process->f_stdout); + zpl_file_connect_handle(&process->err, process->f_stderr); + + pr_free_data: + zpl_string_free(cli); + zpl_free(a, w_cli); + zpl_free(a, w_workdir); + + if (c_env) + zpl_string_free(env); + + return err_code; + + #else + ZPL_NOT_IMPLEMENTED; + return -1; + #endif + } + + + zpl_i32 zpl_pr_join(zpl_pr *process) { + zpl_i32 ret_code; + + ZPL_ASSERT_NOT_NULL(process); + + #ifdef ZPL_SYSTEM_WINDOWS + if (process->f_stdin) { + fclose(cast(FILE *)process->f_stdin); + } + + WaitForSingleObject(process->win32_handle, INFINITE); + + if (!GetExitCodeProcess(process->win32_handle, cast(LPDWORD)&ret_code)) { + zpl_pr_destroy(process); + return -1; + } + + zpl_pr_destroy(process); + + return ret_code; + #else + ZPL_NOT_IMPLEMENTED; + ret_code = -1; + return ret_code; + #endif + } + + void zpl_pr_destroy(zpl_pr *process) { + ZPL_ASSERT_NOT_NULL(process); + + #ifdef ZPL_SYSTEM_WINDOWS + if (process->f_stdin) { + fclose(cast(FILE *)process->f_stdin); + } + + fclose(cast(FILE *)process->f_stdout); + + if (process->f_stderr != process->f_stdout) { + fclose(cast(FILE *)process->f_stderr); + } + + CloseHandle(process->win32_handle); + + zpl__pr_close_file_handles(process); + #else + ZPL_NOT_IMPLEMENTED; + #endif + } + + void zpl_pr_terminate(zpl_pr *process, zpl_i32 err_code) { + ZPL_ASSERT_NOT_NULL(process); + + #ifdef ZPL_SYSTEM_WINDOWS + TerminateProcess(process->win32_handle, cast(UINT)err_code); + zpl_pr_destroy(process); + #else + ZPL_NOT_IMPLEMENTED; + #endif + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_MATH) + // file: source/math.c + + + #if defined(ZPL_COMPILER_TINYC) && defined(ZPL_NO_MATH_H) + #undef ZPL_NO_MATH_H + #endif + + #if !defined(ZPL_NO_MATH_H) + # include + #endif + + ZPL_BEGIN_C_DECLS + + //////////////////////////////////////////////////////////////// + // + // Math + // + + zpl_f32 zpl_to_radians(zpl_f32 degrees) { return degrees * ZPL_TAU / 360.0f; } + zpl_f32 zpl_to_degrees(zpl_f32 radians) { return radians * 360.0f / ZPL_TAU; } + + zpl_f32 zpl_angle_diff(zpl_f32 radians_a, zpl_f32 radians_b) { + zpl_f32 delta = zpl_mod(radians_b - radians_a, ZPL_TAU); + delta = zpl_mod(delta + 1.5f * ZPL_TAU, ZPL_TAU); + delta -= 0.5f * ZPL_TAU; + return delta; + } + + zpl_f32 zpl_copy_sign(zpl_f32 x, zpl_f32 y) { + zpl_i32 ix, iy; + zpl_f32 r; + zpl_memcopy(&ix, &x, zpl_size_of(x)); + zpl_memcopy(&iy, &y, zpl_size_of(y)); + + ix &= 0x7fffffff; + ix |= iy & 0x80000000; + zpl_memcopy(&r, &ix, zpl_size_of(ix)); + return r; + } + + zpl_f32 zpl_remainder(zpl_f32 x, zpl_f32 y) { return x - (zpl_round(x / y) * y); } + + zpl_f32 zpl_mod(zpl_f32 x, zpl_f32 y) { + zpl_f32 result; + y = zpl_abs(y); + result = zpl_remainder(zpl_abs(x), y); + if (zpl_sign(result) > 0.0f) result += y; + return zpl_copy_sign(result, x); + } + + zpl_f64 zpl_copy_sign64(zpl_f64 x, zpl_f64 y) { + zpl_i64 ix, iy; + zpl_f64 r; + zpl_memcopy(&ix, &x, zpl_size_of(x)); + zpl_memcopy(&iy, &y, zpl_size_of(y)); + + ix &= 0x7fffffffffffffff; + ix |= iy & 0x8000000000000000; + zpl_memcopy(&r, &ix, zpl_size_of(ix)); + return r; + } + + zpl_f64 zpl_floor64(zpl_f64 x) { return cast(zpl_f64)((x >= 0.0) ? cast(zpl_i64) x : cast(zpl_i64)(x - 0.9999999999999999)); } + zpl_f64 zpl_ceil64(zpl_f64 x) { return cast(zpl_f64)((x < 0) ? cast(zpl_i64) x : (cast(zpl_i64) x) + 1); } + zpl_f64 zpl_round64(zpl_f64 x) { return cast(zpl_f64)((x >= 0.0) ? zpl_floor64(x + 0.5) : zpl_ceil64(x - 0.5)); } + zpl_f64 zpl_remainder64(zpl_f64 x, zpl_f64 y) { return x - (zpl_round64(x / y) * y); } + zpl_f64 zpl_abs64(zpl_f64 x) { return x < 0 ? -x : x; } + zpl_f64 zpl_sign64(zpl_f64 x) { return x < 0 ? -1.0 : +1.0; } + + zpl_f64 zpl_mod64(zpl_f64 x, zpl_f64 y) { + zpl_f64 result; + y = zpl_abs64(y); + result = zpl_remainder64(zpl_abs64(x), y); + if (zpl_sign64(result)) result += y; + return zpl_copy_sign64(result, x); + } + + zpl_f32 zpl_quake_rsqrt(zpl_f32 a) { + union { + int i; + zpl_f32 f; + } t; + zpl_f32 x2; + zpl_f32 const three_halfs = 1.5f; + + x2 = a * 0.5f; + t.f = a; + t.i = 0x5f375a86 - (t.i >> 1); /* What the fuck? */ + t.f = t.f * (three_halfs - (x2 * t.f * t.f)); /* 1st iteration */ + t.f = t.f * (three_halfs - (x2 * t.f * t.f)); /* 2nd iteration, this can be removed */ + + return t.f; + } + + #if defined(ZPL_NO_MATH_H) + # if defined(_MSC_VER) + + zpl_f32 zpl_rsqrt(zpl_f32 a) { return _mm_cvtss_f32(_mm_rsqrt_ss(_mm_set_ss(a))); } + zpl_f32 zpl_sqrt(zpl_f32 a) { return _mm_cvtss_f32(_mm_sqrt_ss(_mm_set_ss(a))); }; + + zpl_f32 zpl_sin(zpl_f32 a) { + static zpl_f32 const a0 = +1.91059300966915117e-31f; + static zpl_f32 const a1 = +1.00086760103908896f; + static zpl_f32 const a2 = -1.21276126894734565e-2f; + static zpl_f32 const a3 = -1.38078780785773762e-1f; + static zpl_f32 const a4 = -2.67353392911981221e-2f; + static zpl_f32 const a5 = +2.08026600266304389e-2f; + static zpl_f32 const a6 = -3.03996055049204407e-3f; + static zpl_f32 const a7 = +1.38235642404333740e-4f; + return a0 + a * (a1 + a * (a2 + a * (a3 + a * (a4 + a * (a5 + a * (a6 + a * a7)))))); + } + + zpl_f32 zpl_cos(zpl_f32 a) { + static zpl_f32 const a0 = +1.00238601909309722f; + static zpl_f32 const a1 = -3.81919947353040024e-2f; + static zpl_f32 const a2 = -3.94382342128062756e-1f; + static zpl_f32 const a3 = -1.18134036025221444e-1f; + static zpl_f32 const a4 = +1.07123798512170878e-1f; + static zpl_f32 const a5 = -1.86637164165180873e-2f; + static zpl_f32 const a6 = +9.90140908664079833e-4f; + static zpl_f32 const a7 = -5.23022132118824778e-14f; + return a0 + a * (a1 + a * (a2 + a * (a3 + a * (a4 + a * (a5 + a * (a6 + a * a7)))))); + } + + zpl_f32 zpl_tan(zpl_f32 radians) { + zpl_f32 rr = radians * radians; + zpl_f32 a = 9.5168091e-03f; + a *= rr; + a += 2.900525e-03f; + a *= rr; + a += 2.45650893e-02f; + a *= rr; + a += 5.33740603e-02f; + a *= rr; + a += 1.333923995e-01f; + a *= rr; + a += 3.333314036e-01f; + a *= rr; + a += 1.0f; + a *= radians; + return a; + } + + zpl_f32 zpl_arcsin(zpl_f32 a) { return zpl_arctan2(a, zpl_sqrt((1.0f + a) * (1.0f - a))); } + zpl_f32 zpl_arccos(zpl_f32 a) { return zpl_arctan2(zpl_sqrt((1.0f + a) * (1.0f - a)), a); } + + zpl_f32 zpl_arctan(zpl_f32 a) { + zpl_f32 u = a * a; + zpl_f32 u2 = u * u; + zpl_f32 u3 = u2 * u; + zpl_f32 u4 = u3 * u; + zpl_f32 f = 1.0f + 0.33288950512027f * u - 0.08467922817644f * u2 + 0.03252232640125f * u3 - 0.00749305860992f * u4; + return a / f; + } + + zpl_f32 zpl_arctan2(zpl_f32 y, zpl_f32 x) { + if (zpl_abs(x) > zpl_abs(y)) { + zpl_f32 a = zpl_arctan(y / x); + if (x > 0.0f) + return a; + else + return y > 0.0f ? a + ZPL_TAU_OVER_2 : a - ZPL_TAU_OVER_2; + } else { + zpl_f32 a = zpl_arctan(x / y); + if (x > 0.0f) + return y > 0.0f ? ZPL_TAU_OVER_4 - a : -ZPL_TAU_OVER_4 - a; + else + return y > 0.0f ? ZPL_TAU_OVER_4 + a : -ZPL_TAU_OVER_4 + a; + } + } + + zpl_f32 zpl_exp(zpl_f32 a) { + union { + zpl_f32 f; + int i; + } u, v; + u.i = (int)(6051102 * a + 1056478197); + v.i = (int)(1056478197 - 6051102 * a); + return u.f / v.f; + } + + zpl_f32 zpl_log(zpl_f32 a) { + union { + zpl_f32 f; + int i; + } u = { a }; + return (u.i - 1064866805) * 8.262958405176314e-8f; /* 1 / 12102203.0; */ + } + + zpl_f32 zpl_pow(zpl_f32 a, zpl_f32 b) { + int flipped = 0, e; + zpl_f32 f, r = 1.0f; + if (b < 0) { + flipped = 1; + b = -b; + } + + e = (int)b; + f = zpl_exp(b - e); + + while (e) { + if (e & 1) r *= a; + a *= a; + e >>= 1; + } + + r *= f; + return flipped ? 1.0f / r : r; + } + # else + + zpl_f32 zpl_rsqrt(zpl_f32 a) { return 1.0f / __builtin_sqrt(a); } + zpl_f32 zpl_sqrt(zpl_f32 a) { return __builtin_sqrt(a); } + zpl_f32 zpl_sin(zpl_f32 radians) { return __builtin_sinf(radians); } + zpl_f32 zpl_cos(zpl_f32 radians) { return __builtin_cosf(radians); } + zpl_f32 zpl_tan(zpl_f32 radians) { return __builtin_tanf(radians); } + zpl_f32 zpl_arcsin(zpl_f32 a) { return __builtin_asinf(a); } + zpl_f32 zpl_arccos(zpl_f32 a) { return __builtin_acosf(a); } + zpl_f32 zpl_arctan(zpl_f32 a) { return __builtin_atanf(a); } + zpl_f32 zpl_arctan2(zpl_f32 y, zpl_f32 x) { return __builtin_atan2f(y, x); } + + zpl_f32 zpl_exp(zpl_f32 x) { return __builtin_expf(x); } + zpl_f32 zpl_log(zpl_f32 x) { return __builtin_logf(x); } + + // TODO: Should this be zpl_exp(y * zpl_log(x)) ??? + zpl_f32 zpl_pow(zpl_f32 x, zpl_f32 y) { return __builtin_powf(x, y); } + zpl_f32 zpl_hypot(zpl_f32 x, zpl_f32 y){ return __builtin_sqrt(zpl_square(x) + zpl_square(y)); } + + # endif + #else + zpl_f32 zpl_rsqrt(zpl_f32 a) { return 1.0f / sqrtf(a); } + zpl_f32 zpl_sqrt(zpl_f32 a) { return sqrtf(a); }; + zpl_f32 zpl_sin(zpl_f32 radians) { return sinf(radians); }; + zpl_f32 zpl_cos(zpl_f32 radians) { return cosf(radians); }; + zpl_f32 zpl_tan(zpl_f32 radians) { return tanf(radians); }; + zpl_f32 zpl_arcsin(zpl_f32 a) { return asinf(a); }; + zpl_f32 zpl_arccos(zpl_f32 a) { return acosf(a); }; + zpl_f32 zpl_arctan(zpl_f32 a) { return atanf(a); }; + zpl_f32 zpl_arctan2(zpl_f32 y, zpl_f32 x) { return atan2f(y, x); }; + + zpl_f32 zpl_exp(zpl_f32 x) { return expf(x); } + zpl_f32 zpl_log(zpl_f32 x) { return logf(x); } + zpl_f32 zpl_pow(zpl_f32 x, zpl_f32 y) { return powf(x, y); } + zpl_f32 zpl_hypot(zpl_f32 x, zpl_f32 y) { return sqrtf(zpl_square(x) + zpl_square(y)); } + #endif + + zpl_f32 zpl_exp2(zpl_f32 x) { return zpl_exp(ZPL_LOG_TWO * x); } + zpl_f32 zpl_log2(zpl_f32 x) { return zpl_log(x) / ZPL_LOG_TWO; } + + zpl_f32 zpl_fast_exp(zpl_f32 x) { + /* NOTE: Only works in the range -1 <= x <= +1 */ + zpl_f32 e = 1.0f + x * (1.0f + x * 0.5f * (1.0f + x * 0.3333333333f * (1.0f + x * 0.25f * (1.0f + x * 0.2f)))); + return e; + } + + zpl_f32 zpl_fast_exp2(zpl_f32 x) { return zpl_fast_exp(ZPL_LOG_TWO * x); } + + zpl_f32 zpl_round(zpl_f32 x) { return (float)((x >= 0.0f) ? zpl_floor(x + 0.5f) : zpl_ceil(x - 0.5f)); } + zpl_f32 zpl_floor(zpl_f32 x) { return (float)((x >= 0.0f) ? (int)x : (int)(x - 0.9999999999999999f)); } + zpl_f32 zpl_ceil(zpl_f32 x) { return (float)((x < 0.0f) ? (int)x : ((int)x) + 1); } + + zpl_f32 zpl_half_to_float(zpl_half value) { + union { + unsigned int i; + zpl_f32 f; + } result; + int s = (value >> 15) & 0x001; + int e = (value >> 10) & 0x01f; + int m = value & 0x3ff; + + if (e == 0) { + if (m == 0) { + /* Plus or minus zero */ + result.i = (unsigned int)(s << 31); + return result.f; + } else { + /* Denormalized number */ + while (!(m & 0x00000400)) { + m <<= 1; + e -= 1; + } + + e += 1; + m &= ~0x00000400; + } + } else if (e == 31) { + if (m == 0) { + /* Positive or negative infinity */ + result.i = (unsigned int)((s << 31) | 0x7f800000); + return result.f; + } else { + /* Nan */ + result.i = (unsigned int)((s << 31) | 0x7f800000 | (m << 13)); + return result.f; + } + } + + e = e + (127 - 15); + m = m << 13; + + result.i = (unsigned int)((s << 31) | (e << 23) | m); + return result.f; + } + + zpl_half zpl_float_to_half(zpl_f32 value) { + union { + unsigned int i; + zpl_f32 f; + } v; + int i, s, e, m; + + v.f = value; + i = (int)v.i; + + s = (i >> 16) & 0x00008000; + e = ((i >> 23) & 0x000000ff) - (127 - 15); + m = i & 0x007fffff; + + if (e <= 0) { + if (e < -10) return (zpl_half)s; + m = (m | 0x00800000) >> (1 - e); + + if (m & 0x00001000) m += 0x00002000; + + return (zpl_half)(s | (m >> 13)); + } else if (e == 0xff - (127 - 15)) { + if (m == 0) { + return (zpl_half)(s | 0x7c00); /* NOTE: infinity */ + } else { + /* NOTE: NAN */ + m >>= 13; + return (zpl_half)(s | 0x7c00 | m | (m == 0)); + } + } else { + if (m & 0x00001000) { + m += 0x00002000; + if (m & 0x00800000) { + m = 0; + e += 1; + } + } + + if (e > 30) { + zpl_f32 volatile f = 1e12f; + int j; + for (j = 0; j < 10; j++) f *= f; /* NOTE: Cause overflow */ + + return (zpl_half)(s | 0x7c00); + } + + return (zpl_half)(s | (e << 10) | (m >> 13)); + } + } + + #define ZPL_VEC2_2OP(a, c, post) \ + a->x = c.x post; \ + a->y = c.y post; + + #define ZPL_VEC2_3OP(a, b, op, c, post) \ + a->x = b.x op c.x post; \ + a->y = b.y op c.y post; + + #define ZPL_VEC3_2OP(a, c, post) \ + a->x = c.x post; \ + a->y = c.y post; \ + a->z = c.z post; + + #define ZPL_VEC3_3OP(a, b, op, c, post) \ + a->x = b.x op c.x post; \ + a->y = b.y op c.y post; \ + a->z = b.z op c.z post; + + #define ZPL_VEC4_2OP(a, c, post) \ + a->x = c.x post; \ + a->y = c.y post; \ + a->z = c.z post; \ + a->w = c.w post; + + #define ZPL_VEC4_3OP(a, b, op, c, post) \ + a->x = b.x op c.x post; \ + a->y = b.y op c.y post; \ + a->z = b.z op c.z post; \ + a->w = b.w op c.w post; + + zpl_vec2 zpl_vec2f_zero(void) { + zpl_vec2 v = { 0, 0 }; + return v; + } + zpl_vec2 zpl_vec2f(zpl_f32 x, zpl_f32 y) { + zpl_vec2 v; + v.x = x; + v.y = y; + return v; + } + zpl_vec2 zpl_vec2fv(zpl_f32 x[2]) { + zpl_vec2 v; + v.x = x[0]; + v.y = x[1]; + return v; + } + + zpl_vec3 zpl_vec3f_zero(void) { + zpl_vec3 v = { 0, 0, 0 }; + return v; + } + zpl_vec3 zpl_vec3f(zpl_f32 x, zpl_f32 y, zpl_f32 z) { + zpl_vec3 v; + v.x = x; + v.y = y; + v.z = z; + return v; + } + zpl_vec3 zpl_vec3fv(zpl_f32 x[3]) { + zpl_vec3 v; + v.x = x[0]; + v.y = x[1]; + v.z = x[2]; + return v; + } + + zpl_vec4 zpl_vec4f_zero(void) { + zpl_vec4 v = { 0, 0, 0, 0 }; + return v; + } + zpl_vec4 zpl_vec4f(zpl_f32 x, zpl_f32 y, zpl_f32 z, zpl_f32 w) { + zpl_vec4 v; + v.x = x; + v.y = y; + v.z = z; + v.w = w; + return v; + } + zpl_vec4 zpl_vec4fv(zpl_f32 x[4]) { + zpl_vec4 v; + v.x = x[0]; + v.y = x[1]; + v.z = x[2]; + v.w = x[3]; + return v; + } + + zpl_f32 zpl_vec2_max(zpl_vec2 v) { return zpl_max(v.x, v.y); } + zpl_f32 zpl_vec2_side(zpl_vec2 p, zpl_vec2 q, zpl_vec2 r) { return ((q.x - p.x) * (r.y - p.y) - (r.x - p.x) * (q.y - p.y)); } + + void zpl_vec2_add(zpl_vec2 *d, zpl_vec2 v0, zpl_vec2 v1) { ZPL_VEC2_3OP(d, v0, +, v1, +0); } + void zpl_vec2_sub(zpl_vec2 *d, zpl_vec2 v0, zpl_vec2 v1) { ZPL_VEC2_3OP(d, v0, -, v1, +0); } + void zpl_vec2_mul(zpl_vec2 *d, zpl_vec2 v, zpl_f32 s) { ZPL_VEC2_2OP(d, v, *s); } + void zpl_vec2_div(zpl_vec2 *d, zpl_vec2 v, zpl_f32 s) { ZPL_VEC2_2OP(d, v, / s); } + + zpl_f32 zpl_vec3_max(zpl_vec3 v) { return zpl_max3(v.x, v.y, v.z); } + + void zpl_vec3_add(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1) { ZPL_VEC3_3OP(d, v0, +, v1, +0); } + void zpl_vec3_sub(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1) { ZPL_VEC3_3OP(d, v0, -, v1, +0); } + void zpl_vec3_mul(zpl_vec3 *d, zpl_vec3 v, zpl_f32 s) { ZPL_VEC3_2OP(d, v, *s); } + void zpl_vec3_div(zpl_vec3 *d, zpl_vec3 v, zpl_f32 s) { ZPL_VEC3_2OP(d, v, / s); } + + void zpl_vec4_add(zpl_vec4 *d, zpl_vec4 v0, zpl_vec4 v1) { ZPL_VEC4_3OP(d, v0, +, v1, +0); } + void zpl_vec4_sub(zpl_vec4 *d, zpl_vec4 v0, zpl_vec4 v1) { ZPL_VEC4_3OP(d, v0, -, v1, +0); } + void zpl_vec4_mul(zpl_vec4 *d, zpl_vec4 v, zpl_f32 s) { ZPL_VEC4_2OP(d, v, *s); } + void zpl_vec4_div(zpl_vec4 *d, zpl_vec4 v, zpl_f32 s) { ZPL_VEC4_2OP(d, v, / s); } + + void zpl_vec2_addeq(zpl_vec2 *d, zpl_vec2 v) { ZPL_VEC2_3OP(d, (*d), +, v, +0); } + void zpl_vec2_subeq(zpl_vec2 *d, zpl_vec2 v) { ZPL_VEC2_3OP(d, (*d), -, v, +0); } + void zpl_vec2_muleq(zpl_vec2 *d, zpl_f32 s) { ZPL_VEC2_2OP(d, (*d), *s); } + void zpl_vec2_diveq(zpl_vec2 *d, zpl_f32 s) { ZPL_VEC2_2OP(d, (*d), / s); } + + void zpl_vec3_addeq(zpl_vec3 *d, zpl_vec3 v) { ZPL_VEC3_3OP(d, (*d), +, v, +0); } + void zpl_vec3_subeq(zpl_vec3 *d, zpl_vec3 v) { ZPL_VEC3_3OP(d, (*d), -, v, +0); } + void zpl_vec3_muleq(zpl_vec3 *d, zpl_f32 s) { ZPL_VEC3_2OP(d, (*d), *s); } + void zpl_vec3_diveq(zpl_vec3 *d, zpl_f32 s) { ZPL_VEC3_2OP(d, (*d), / s); } + + void zpl_vec4_addeq(zpl_vec4 *d, zpl_vec4 v) { ZPL_VEC4_3OP(d, (*d), +, v, +0); } + void zpl_vec4_subeq(zpl_vec4 *d, zpl_vec4 v) { ZPL_VEC4_3OP(d, (*d), -, v, +0); } + void zpl_vec4_muleq(zpl_vec4 *d, zpl_f32 s) { ZPL_VEC4_2OP(d, (*d), *s); } + void zpl_vec4_diveq(zpl_vec4 *d, zpl_f32 s) { ZPL_VEC4_2OP(d, (*d), / s); } + + #undef ZPL_VEC2_2OP + #undef ZPL_VEC2_3OP + #undef ZPL_VEC3_3OP + #undef ZPL_VEC3_2OP + #undef ZPL_VEC4_2OP + #undef ZPL_VEC4_3OP + + zpl_f32 zpl_vec2_dot(zpl_vec2 v0, zpl_vec2 v1) { return v0.x * v1.x + v0.y * v1.y; } + zpl_f32 zpl_vec3_dot(zpl_vec3 v0, zpl_vec3 v1) { return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z; } + zpl_f32 zpl_vec4_dot(zpl_vec4 v0, zpl_vec4 v1) { return v0.x * v1.x + v0.y * v1.y + v0.z * v1.z + v0.w * v1.w; } + + void zpl_vec2_cross(zpl_f32 *d, zpl_vec2 v0, zpl_vec2 v1) { *d = v0.x * v1.y - v1.x * v0.y; } + void zpl_vec3_cross(zpl_vec3 *d, zpl_vec3 v0, zpl_vec3 v1) { + d->x = v0.y * v1.z - v0.z * v1.y; + d->y = v0.z * v1.x - v0.x * v1.z; + d->z = v0.x * v1.y - v0.y * v1.x; + } + + zpl_f32 zpl_vec2_mag2(zpl_vec2 v) { return zpl_vec2_dot(v, v); } + zpl_f32 zpl_vec3_mag2(zpl_vec3 v) { return zpl_vec3_dot(v, v); } + zpl_f32 zpl_vec4_mag2(zpl_vec4 v) { return zpl_vec4_dot(v, v); } + + /* TODO: Create custom sqrt function */ + zpl_f32 zpl_vec2_mag(zpl_vec2 v) { return zpl_sqrt(zpl_vec2_dot(v, v)); } + zpl_f32 zpl_vec3_mag(zpl_vec3 v) { return zpl_sqrt(zpl_vec3_dot(v, v)); } + zpl_f32 zpl_vec4_mag(zpl_vec4 v) { return zpl_sqrt(zpl_vec4_dot(v, v)); } + + void zpl_vec2_norm(zpl_vec2 *d, zpl_vec2 v) { + zpl_f32 inv_mag = zpl_rsqrt(zpl_vec2_dot(v, v)); + zpl_vec2_mul(d, v, inv_mag); + } + void zpl_vec3_norm(zpl_vec3 *d, zpl_vec3 v) { + zpl_f32 inv_mag = zpl_rsqrt(zpl_vec3_dot(v, v)); + zpl_vec3_mul(d, v, inv_mag); + } + void zpl_vec4_norm(zpl_vec4 *d, zpl_vec4 v) { + zpl_f32 inv_mag = zpl_rsqrt(zpl_vec4_dot(v, v)); + zpl_vec4_mul(d, v, inv_mag); + } + + void zpl_vec2_norm0(zpl_vec2 *d, zpl_vec2 v) { + zpl_f32 mag = zpl_vec2_mag(v); + if (mag > 0) + zpl_vec2_div(d, v, mag); + else + *d = zpl_vec2f_zero( ); + } + void zpl_vec3_norm0(zpl_vec3 *d, zpl_vec3 v) { + zpl_f32 mag = zpl_vec3_mag(v); + if (mag > 0) + zpl_vec3_div(d, v, mag); + else + *d = zpl_vec3f_zero( ); + } + void zpl_vec4_norm0(zpl_vec4 *d, zpl_vec4 v) { + zpl_f32 mag = zpl_vec4_mag(v); + if (mag > 0) + zpl_vec4_div(d, v, mag); + else + *d = zpl_vec4f_zero( ); + } + + void zpl_vec2_reflect(zpl_vec2 *d, zpl_vec2 i, zpl_vec2 n) { + zpl_vec2 b = n; + zpl_vec2_muleq(&b, 2.0f * zpl_vec2_dot(n, i)); + zpl_vec2_sub(d, i, b); + } + + void zpl_vec3_reflect(zpl_vec3 *d, zpl_vec3 i, zpl_vec3 n) { + zpl_vec3 b = n; + zpl_vec3_muleq(&b, 2.0f * zpl_vec3_dot(n, i)); + zpl_vec3_sub(d, i, b); + } + + void zpl_vec2_refract(zpl_vec2 *d, zpl_vec2 i, zpl_vec2 n, zpl_f32 eta) { + zpl_vec2 a, b; + zpl_f32 dv, k; + + dv = zpl_vec2_dot(n, i); + k = 1.0f - eta * eta * (1.0f - dv * dv); + zpl_vec2_mul(&a, i, eta); + zpl_vec2_mul(&b, n, eta * dv * zpl_sqrt(k)); + zpl_vec2_sub(d, a, b); + zpl_vec2_muleq(d, (float)(k >= 0.0f)); + } + + void zpl_vec3_refract(zpl_vec3 *d, zpl_vec3 i, zpl_vec3 n, zpl_f32 eta) { + zpl_vec3 a, b; + zpl_f32 dv, k; + + dv = zpl_vec3_dot(n, i); + k = 1.0f - eta * eta * (1.0f - dv * dv); + zpl_vec3_mul(&a, i, eta); + zpl_vec3_mul(&b, n, eta * dv * zpl_sqrt(k)); + zpl_vec3_sub(d, a, b); + zpl_vec3_muleq(d, (float)(k >= 0.0f)); + } + + zpl_f32 zpl_vec2_aspect_ratio(zpl_vec2 v) { return (v.y < 0.0001f) ? 0.0f : v.x / v.y; } + + void zpl_mat2_transpose(zpl_mat2 *m) { zpl_float22_transpose(zpl_float22_m(m)); } + void zpl_mat2_identity(zpl_mat2 *m) { zpl_float22_identity(zpl_float22_m(m)); } + void zpl_mat2_mul(zpl_mat2 *out, zpl_mat2 *m1, zpl_mat2 *m2) { + zpl_float22_mul(zpl_float22_m(out), zpl_float22_m(m1), zpl_float22_m(m2)); + } + + void zpl_float22_identity(zpl_f32 m[2][2]) { + m[0][0] = 1; + m[0][1] = 0; + m[1][0] = 0; + m[1][1] = 1; + } + + void zpl_mat2_copy(zpl_mat2* out, zpl_mat2* m) { + zpl_memcopy(out, m, sizeof(zpl_mat3)); + } + + void zpl_mat2_mul_vec2(zpl_vec2 *out, zpl_mat2 *m, zpl_vec2 in) { zpl_float22_mul_vec2(out, zpl_float22_m(m), in); } + + zpl_mat2 *zpl_mat2_v(zpl_vec2 m[2]) { return (zpl_mat2 *)m; } + zpl_mat2 *zpl_mat2_f(zpl_f32 m[2][2]) { return (zpl_mat2 *)m; } + + zpl_float2 *zpl_float22_m(zpl_mat2 *m) { return (zpl_float2 *)m; } + zpl_float2 *zpl_float22_v(zpl_vec2 m[2]) { return (zpl_float2 *)m; } + zpl_float2 *zpl_float22_4(zpl_f32 m[4]) { return (zpl_float2 *)m; } + + void zpl_float22_transpose(zpl_f32 (*vec)[2]) { + int i, j; + for (j = 0; j < 2; j++) { + for (i = j + 1; i < 2; i++) { + zpl_f32 t = vec[i][j]; + vec[i][j] = vec[j][i]; + vec[j][i] = t; + } + } + } + + void zpl_float22_mul(zpl_f32 (*out)[2], zpl_f32 (*mat1)[2], zpl_f32 (*mat2)[2]) { + int i, j; + zpl_f32 temp1[2][2], temp2[2][2]; + if (mat1 == out) { + zpl_memcopy(temp1, mat1, sizeof(temp1)); + mat1 = temp1; + } + if (mat2 == out) { + zpl_memcopy(temp2, mat2, sizeof(temp2)); + mat2 = temp2; + } + for (j = 0; j < 2; j++) { + for (i = 0; i < 2; i++) { out[j][i] = mat1[0][i] * mat2[j][0] + mat1[1][i] * mat2[j][1]; } + } + } + + void zpl_float22_mul_vec2(zpl_vec2 *out, zpl_f32 m[2][2], zpl_vec2 v) { + out->x = m[0][0] * v.x + m[0][1] * v.y; + out->y = m[1][0] * v.x + m[1][1] * v.y; + } + + zpl_f32 zpl_mat2_determinate(zpl_mat2 *m) { + zpl_float2 *e = zpl_float22_m(m); + return e[0][0] * e[1][1] - e[1][0] * e[0][1]; + } + + void zpl_mat2_inverse(zpl_mat2 *out, zpl_mat2 *in) { + zpl_float2 *o = zpl_float22_m(out); + zpl_float2 *i = zpl_float22_m(in); + + zpl_f32 ood = 1.0f / zpl_mat2_determinate(in); + + o[0][0] = +i[1][1] * ood; + o[0][1] = -i[0][1] * ood; + o[1][0] = -i[1][0] * ood; + o[1][1] = +i[0][0] * ood; + } + + void zpl_mat3_transpose(zpl_mat3 *m) { zpl_float33_transpose(zpl_float33_m(m)); } + void zpl_mat3_identity(zpl_mat3 *m) { zpl_float33_identity(zpl_float33_m(m)); } + + void zpl_mat3_copy(zpl_mat3* out, zpl_mat3* m) { + zpl_memcopy(out, m, sizeof(zpl_mat3)); + } + + void zpl_mat3_mul(zpl_mat3 *out, zpl_mat3 *m1, zpl_mat3 *m2) { + zpl_float33_mul(zpl_float33_m(out), zpl_float33_m(m1), zpl_float33_m(m2)); + } + + void zpl_float33_identity(zpl_f32 m[3][3]) { + m[0][0] = 1; + m[0][1] = 0; + m[0][2] = 0; + m[1][0] = 0; + m[1][1] = 1; + m[1][2] = 0; + m[2][0] = 0; + m[2][1] = 0; + m[2][2] = 1; + } + + void zpl_mat3_mul_vec3(zpl_vec3 *out, zpl_mat3 *m, zpl_vec3 in) { zpl_float33_mul_vec3(out, zpl_float33_m(m), in); } + + zpl_mat3 *zpl_mat3_v(zpl_vec3 m[3]) { return (zpl_mat3 *)m; } + zpl_mat3 *zpl_mat3_f(zpl_f32 m[3][3]) { return (zpl_mat3 *)m; } + + zpl_float3 *zpl_float33_m(zpl_mat3 *m) { return (zpl_float3 *)m; } + zpl_float3 *zpl_float33_v(zpl_vec3 m[3]) { return (zpl_float3 *)m; } + zpl_float3 *zpl_float33_9(zpl_f32 m[9]) { return (zpl_float3 *)m; } + + void zpl_float33_transpose(zpl_f32 (*vec)[3]) { + int i, j; + for (j = 0; j < 3; j++) { + for (i = j + 1; i < 3; i++) { + zpl_f32 t = vec[i][j]; + vec[i][j] = vec[j][i]; + vec[j][i] = t; + } + } + } + + void zpl_float33_mul(zpl_f32 (*out)[3], zpl_f32 (*mat1)[3], zpl_f32 (*mat2)[3]) { + int i, j; + zpl_f32 temp1[3][3], temp2[3][3]; + if (mat1 == out) { + zpl_memcopy(temp1, mat1, sizeof(temp1)); + mat1 = temp1; + } + if (mat2 == out) { + zpl_memcopy(temp2, mat2, sizeof(temp2)); + mat2 = temp2; + } + for (j = 0; j < 3; j++) { + for (i = 0; i < 3; i++) { + out[j][i] = mat1[0][i] * mat2[j][0] + mat1[1][i] * mat2[j][1] + mat1[2][i] * mat2[j][2]; + } + } + } + + void zpl_float33_mul_vec3(zpl_vec3 *out, zpl_f32 m[3][3], zpl_vec3 v) { + out->x = m[0][0] * v.x + m[0][1] * v.y + m[0][2] * v.z; + out->y = m[1][0] * v.x + m[1][1] * v.y + m[1][2] * v.z; + out->z = m[2][0] * v.x + m[2][1] * v.y + m[2][2] * v.z; + } + + zpl_f32 zpl_mat3_determinate(zpl_mat3 *m) { + zpl_float3 *e = zpl_float33_m(m); + zpl_f32 d = + +e[0][0] * (e[1][1] * e[2][2] - e[1][2] * e[2][1]) + -e[0][1] * (e[1][0] * e[2][2] - e[1][2] * e[2][0]) + +e[0][2] * (e[1][0] * e[2][1] - e[1][1] * e[2][0]); + return d; + } + + void zpl_mat3_inverse(zpl_mat3 *out, zpl_mat3 *in) { + zpl_float3 *o = zpl_float33_m(out); + zpl_float3 *i = zpl_float33_m(in); + + zpl_f32 ood = 1.0f / zpl_mat3_determinate(in); + + o[0][0] = +(i[1][1] * i[2][2] - i[2][1] * i[1][2]) * ood; + o[0][1] = -(i[1][0] * i[2][2] - i[2][0] * i[1][2]) * ood; + o[0][2] = +(i[1][0] * i[2][1] - i[2][0] * i[1][1]) * ood; + o[1][0] = -(i[0][1] * i[2][2] - i[2][1] * i[0][2]) * ood; + o[1][1] = +(i[0][0] * i[2][2] - i[2][0] * i[0][2]) * ood; + o[1][2] = -(i[0][0] * i[2][1] - i[2][0] * i[0][1]) * ood; + o[2][0] = +(i[0][1] * i[1][2] - i[1][1] * i[0][2]) * ood; + o[2][1] = -(i[0][0] * i[1][2] - i[1][0] * i[0][2]) * ood; + o[2][2] = +(i[0][0] * i[1][1] - i[1][0] * i[0][1]) * ood; + } + + void zpl_mat4_transpose(zpl_mat4 *m) { zpl_float44_transpose(zpl_float44_m(m)); } + void zpl_mat4_identity(zpl_mat4 *m) { zpl_float44_identity(zpl_float44_m(m)); } + + void zpl_mat4_copy(zpl_mat4* out, zpl_mat4* m) { + zpl_memcopy(out, m, sizeof(zpl_mat4)); + } + + + void zpl_mat4_mul(zpl_mat4 *out, zpl_mat4 *m1, zpl_mat4 *m2) { + zpl_float44_mul(zpl_float44_m(out), zpl_float44_m(m1), zpl_float44_m(m2)); + } + + void zpl_float44_identity(zpl_f32 m[4][4]) { + m[0][0] = 1; + m[0][1] = 0; + m[0][2] = 0; + m[0][3] = 0; + m[1][0] = 0; + m[1][1] = 1; + m[1][2] = 0; + m[1][3] = 0; + m[2][0] = 0; + m[2][1] = 0; + m[2][2] = 1; + m[2][3] = 0; + m[3][0] = 0; + m[3][1] = 0; + m[3][2] = 0; + m[3][3] = 1; + } + + void zpl_mat4_mul_vec4(zpl_vec4 *out, zpl_mat4 *m, zpl_vec4 in) { zpl_float44_mul_vec4(out, zpl_float44_m(m), in); } + + zpl_mat4 *zpl_mat4_v(zpl_vec4 m[4]) { return (zpl_mat4 *)m; } + zpl_mat4 *zpl_mat4_f(zpl_f32 m[4][4]) { return (zpl_mat4 *)m; } + + zpl_float4 *zpl_float44_m(zpl_mat4 *m) { return (zpl_float4 *)m; } + zpl_float4 *zpl_float44_v(zpl_vec4 m[4]) { return (zpl_float4 *)m; } + zpl_float4 *zpl_float44_16(zpl_f32 m[16]) { return (zpl_float4 *)m; } + + void zpl_float44_transpose(zpl_f32 (*vec)[4]) { + zpl_f32 tmp; + tmp = vec[1][0]; + vec[1][0] = vec[0][1]; + vec[0][1] = tmp; + tmp = vec[2][0]; + vec[2][0] = vec[0][2]; + vec[0][2] = tmp; + tmp = vec[3][0]; + vec[3][0] = vec[0][3]; + vec[0][3] = tmp; + tmp = vec[2][1]; + vec[2][1] = vec[1][2]; + vec[1][2] = tmp; + tmp = vec[3][1]; + vec[3][1] = vec[1][3]; + vec[1][3] = tmp; + tmp = vec[3][2]; + vec[3][2] = vec[2][3]; + vec[2][3] = tmp; + } + + void zpl_float44_mul(zpl_f32 (*out)[4], zpl_f32 (*mat1)[4], zpl_f32 (*mat2)[4]) { + int i, j; + zpl_f32 temp1[4][4], temp2[4][4]; + if (mat1 == out) { + zpl_memcopy(temp1, mat1, sizeof(temp1)); + mat1 = temp1; + } + if (mat2 == out) { + zpl_memcopy(temp2, mat2, sizeof(temp2)); + mat2 = temp2; + } + for (j = 0; j < 4; j++) { + for (i = 0; i < 4; i++) { + out[j][i] = + mat1[0][i] * mat2[j][0] + mat1[1][i] * mat2[j][1] + +mat1[2][i] * mat2[j][2] + mat1[3][i] * mat2[j][3]; + } + } + } + + void zpl_float44_mul_vec4(zpl_vec4 *out, zpl_f32 m[4][4], zpl_vec4 v) { + out->x = m[0][0] * v.x + m[1][0] * v.y + m[2][0] * v.z + m[3][0] * v.w; + out->y = m[0][1] * v.x + m[1][1] * v.y + m[2][1] * v.z + m[3][1] * v.w; + out->z = m[0][2] * v.x + m[1][2] * v.y + m[2][2] * v.z + m[3][2] * v.w; + out->w = m[0][3] * v.x + m[1][3] * v.y + m[2][3] * v.z + m[3][3] * v.w; + } + + void zpl_mat4_inverse(zpl_mat4 *out, zpl_mat4 *in) { + zpl_float4 *o = zpl_float44_m(out); + zpl_float4 *m = zpl_float44_m(in); + + zpl_f32 ood; + + zpl_f32 sf00 = m[2][2] * m[3][3] - m[3][2] * m[2][3]; + zpl_f32 sf01 = m[2][1] * m[3][3] - m[3][1] * m[2][3]; + zpl_f32 sf02 = m[2][1] * m[3][2] - m[3][1] * m[2][2]; + zpl_f32 sf03 = m[2][0] * m[3][3] - m[3][0] * m[2][3]; + zpl_f32 sf04 = m[2][0] * m[3][2] - m[3][0] * m[2][2]; + zpl_f32 sf05 = m[2][0] * m[3][1] - m[3][0] * m[2][1]; + zpl_f32 sf06 = m[1][2] * m[3][3] - m[3][2] * m[1][3]; + zpl_f32 sf07 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + zpl_f32 sf08 = m[1][1] * m[3][2] - m[3][1] * m[1][2]; + zpl_f32 sf09 = m[1][0] * m[3][3] - m[3][0] * m[1][3]; + zpl_f32 sf10 = m[1][0] * m[3][2] - m[3][0] * m[1][2]; + zpl_f32 sf11 = m[1][1] * m[3][3] - m[3][1] * m[1][3]; + zpl_f32 sf12 = m[1][0] * m[3][1] - m[3][0] * m[1][1]; + zpl_f32 sf13 = m[1][2] * m[2][3] - m[2][2] * m[1][3]; + zpl_f32 sf14 = m[1][1] * m[2][3] - m[2][1] * m[1][3]; + zpl_f32 sf15 = m[1][1] * m[2][2] - m[2][1] * m[1][2]; + zpl_f32 sf16 = m[1][0] * m[2][3] - m[2][0] * m[1][3]; + zpl_f32 sf17 = m[1][0] * m[2][2] - m[2][0] * m[1][2]; + zpl_f32 sf18 = m[1][0] * m[2][1] - m[2][0] * m[1][1]; + + o[0][0] = +(m[1][1] * sf00 - m[1][2] * sf01 + m[1][3] * sf02); + o[1][0] = -(m[1][0] * sf00 - m[1][2] * sf03 + m[1][3] * sf04); + o[2][0] = +(m[1][0] * sf01 - m[1][1] * sf03 + m[1][3] * sf05); + o[3][0] = -(m[1][0] * sf02 - m[1][1] * sf04 + m[1][2] * sf05); + + o[0][1] = -(m[0][1] * sf00 - m[0][2] * sf01 + m[0][3] * sf02); + o[1][1] = +(m[0][0] * sf00 - m[0][2] * sf03 + m[0][3] * sf04); + o[2][1] = -(m[0][0] * sf01 - m[0][1] * sf03 + m[0][3] * sf05); + o[3][1] = +(m[0][0] * sf02 - m[0][1] * sf04 + m[0][2] * sf05); + + o[0][2] = +(m[0][1] * sf06 - m[0][2] * sf07 + m[0][3] * sf08); + o[1][2] = -(m[0][0] * sf06 - m[0][2] * sf09 + m[0][3] * sf10); + o[2][2] = +(m[0][0] * sf11 - m[0][1] * sf09 + m[0][3] * sf12); + o[3][2] = -(m[0][0] * sf08 - m[0][1] * sf10 + m[0][2] * sf12); + + o[0][3] = -(m[0][1] * sf13 - m[0][2] * sf14 + m[0][3] * sf15); + o[1][3] = +(m[0][0] * sf13 - m[0][2] * sf16 + m[0][3] * sf17); + o[2][3] = -(m[0][0] * sf14 - m[0][1] * sf16 + m[0][3] * sf18); + o[3][3] = +(m[0][0] * sf15 - m[0][1] * sf17 + m[0][2] * sf18); + + ood = 1.0f / (m[0][0] * o[0][0] + m[0][1] * o[1][0] + m[0][2] * o[2][0] + m[0][3] * o[3][0]); + + o[0][0] *= ood; o[1][0] *= ood; o[2][0] *= ood; o[3][0] *= ood; + o[0][1] *= ood; o[1][1] *= ood; o[2][1] *= ood; o[3][1] *= ood; + o[0][2] *= ood; o[1][2] *= ood; o[2][2] *= ood; o[3][2] *= ood; + o[0][3] *= ood; o[1][3] *= ood; o[2][3] *= ood; o[3][3] *= ood; + } + + void zpl_mat4_axis_angle(zpl_mat4 *out, zpl_vec3 v, zpl_f32 angle_radians) { + zpl_f32 c, s; + zpl_vec3 axis, t; + zpl_float4 *rot; + + c = zpl_cos(angle_radians); + s = zpl_sin(angle_radians); + + zpl_vec3_norm(&axis, v); + zpl_vec3_mul(&t, axis, 1.0f - c); + + zpl_mat4_identity(out); + rot = zpl_float44_m(out); + + rot[0][0] = c + t.x * axis.x; + rot[0][1] = 0 + t.x * axis.y + s * axis.z; + rot[0][2] = 0 + t.x * axis.z - s * axis.y; + rot[0][3] = 0; + + rot[1][0] = 0 + t.y * axis.x - s * axis.z; + rot[1][1] = c + t.y * axis.y; + rot[1][2] = 0 + t.y * axis.z + s * axis.x; + rot[1][3] = 0; + + rot[2][0] = 0 + t.z * axis.x + s * axis.y; + rot[2][1] = 0 + t.z * axis.y - s * axis.x; + rot[2][2] = c + t.z * axis.z; + rot[2][3] = 0; + } + + void zpl_mat4_to_translate(zpl_mat4* out, zpl_vec3 v) { + zpl_mat4_identity(out); + out->col[3].xyz = v; + } + + void zpl_mat4_to_rotate(zpl_mat4* out, zpl_vec3 v, zpl_f32 angle_radians) { + zpl_mat4_axis_angle(out, v, angle_radians); + } + + void zpl_mat4_to_scale(zpl_mat4* out, zpl_vec3 v) { + zpl_mat4_identity(out); + out->col[0].x = v.x; + out->col[1].y = v.y; + out->col[2].z = v.z; + } + void zpl_mat4_to_scalef(zpl_mat4* out, zpl_f32 s) { + zpl_mat4_identity(out); + out->col[0].x = s; + out->col[1].y = s; + out->col[2].z = s; + } + + void zpl_mat4_translate(zpl_mat4* m, zpl_vec3 v) { + zpl_mat4 mm; + zpl_mat4_to_translate(&mm, v); + zpl_mat4_mul(m, m, &mm); + } + + void zpl_mat4_rotate(zpl_mat4* m, zpl_vec3 v, zpl_f32 angle_radians) { + zpl_mat4 mm; + zpl_mat4_axis_angle(&mm,v, angle_radians); + zpl_mat4_mul(m, m, &mm); + } + + void zpl_mat4_scale(zpl_mat4* m, zpl_vec3 v) { + zpl_mat4 mm; + zpl_mat4_to_scale(&mm, v); + zpl_mat4_mul(m, m, &mm); + } + + void zpl_mat4_scalef(zpl_mat4* m, zpl_f32 s) { + zpl_mat4 mm; + zpl_mat4_to_scalef(&mm, s); + zpl_mat4_mul(m, m, &mm); + } + + void zpl_mat4_ortho2d(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top) { + zpl_float4 *m; + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = 2.0f / (right - left); + m[1][1] = 2.0f / (top - bottom); + m[2][2] = -1.0f; + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + } + + void zpl_mat4_ortho3d(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top, zpl_f32 z_near, zpl_f32 z_far) { + zpl_float4 *m; + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = +2.0f / (right - left); + m[1][1] = +2.0f / (top - bottom); + m[2][2] = -2.0f / (z_far - z_near); + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + m[3][2] = -(z_far + z_near) / (z_far - z_near); + } + + void zpl_mat4_perspective(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near, zpl_f32 z_far) { + zpl_f32 tan_half_fovy = zpl_tan(0.5f * fovy); + zpl_mat4 zero_mat = { 0 }; + zpl_float4 *m = zpl_float44_m(out); + *out = zero_mat; + + m[0][0] = 1.0f / (aspect * tan_half_fovy); + m[1][1] = 1.0f / (tan_half_fovy); + m[2][2] = -(z_far + z_near) / (z_far - z_near); + m[2][3] = -1.0f; + m[3][2] = -2.0f * z_far * z_near / (z_far - z_near); + } + + void zpl_mat4_infinite_perspective(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near) { + zpl_f32 range = zpl_tan(0.5f * fovy) * z_near; + zpl_f32 left = -range * aspect; + zpl_f32 right = range * aspect; + zpl_f32 bottom = -range; + zpl_f32 top = range; + zpl_mat4 zero_mat = { 0 }; + zpl_float4 *m = zpl_float44_m(out); + *out = zero_mat; + + m[0][0] = (2.0f * z_near) / (right - left); + m[1][1] = (2.0f * z_near) / (top - bottom); + m[2][2] = -1.0f; + m[2][3] = -1.0f; + m[3][2] = -2.0f * z_near; + } + + void zpl_mat4_ortho2d_dx(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top) { + zpl_float4 *m; + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = 2.0f / (right - left); + m[1][1] = 2.0f / (top - bottom); + m[2][2] = -1.0f; + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + } + + void zpl_mat4_ortho3d_dx(zpl_mat4 *out, zpl_f32 left, zpl_f32 right, zpl_f32 bottom, zpl_f32 top, zpl_f32 z_near, zpl_f32 z_far) { + zpl_float4 *m; + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = +2.0f / (right - left); + m[1][1] = +2.0f / (top - bottom); + m[2][2] = -1.0f / (z_far - z_near); + m[3][0] = -(right + left) / (right - left); + m[3][1] = -(top + bottom) / (top - bottom); + m[3][2] = -( z_near) / (z_far - z_near); + } + + void zpl_mat4_perspective_dx(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near, zpl_f32 z_far) { + zpl_f32 tan_half_fovy = zpl_tan(0.5f * fovy); + zpl_mat4 zero_mat = { 0 }; + zpl_float4 *m = zpl_float44_m(out); + *out = zero_mat; + + m[0][0] = 1.0f / (aspect * tan_half_fovy); + m[1][1] = 1.0f / (tan_half_fovy); + m[2][2] = -(z_far ) / (z_far - z_near); + m[2][3] = -1.0f; + m[3][2] = - z_near / (z_far - z_near); + } + + void zpl_mat4_infinite_perspective_dx(zpl_mat4 *out, zpl_f32 fovy, zpl_f32 aspect, zpl_f32 z_near) { + zpl_f32 tan_half_fovy = zpl_tan(0.5f * fovy); + zpl_mat4 zero_mat = { 0 }; + zpl_float4 *m = zpl_float44_m(out); + *out = zero_mat; + + m[0][0] = 1.0f / (aspect * tan_half_fovy); + m[1][1] = 1.0f / (tan_half_fovy); + m[2][2] = -1.0f; + m[2][3] = -1.0f; + m[3][2] = - z_near; + } + + + + void zpl_mat4_look_at(zpl_mat4 *out, zpl_vec3 eye, zpl_vec3 centre, zpl_vec3 up) { + zpl_vec3 f, s, u; + zpl_float4 *m; + + zpl_vec3_sub(&f, centre, eye); + zpl_vec3_norm(&f, f); + + zpl_vec3_cross(&s, f, up); + zpl_vec3_norm(&s, s); + + zpl_vec3_cross(&u, s, f); + + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = +s.x; + m[1][0] = +s.y; + m[2][0] = +s.z; + + m[0][1] = +u.x; + m[1][1] = +u.y; + m[2][1] = +u.z; + + m[0][2] = -f.x; + m[1][2] = -f.y; + m[2][2] = -f.z; + + m[3][0] = -zpl_vec3_dot(s, eye); + m[3][1] = -zpl_vec3_dot(u, eye); + m[3][2] = +zpl_vec3_dot(f, eye); + } + + void zpl_mat4_look_at_lh(zpl_mat4 *out, zpl_vec3 eye, zpl_vec3 centre, zpl_vec3 up) { + zpl_vec3 f, s, u; + zpl_float4 *m; + + zpl_vec3_sub(&f, centre, eye); + zpl_vec3_norm(&f, f); + + zpl_vec3_cross(&s, up, f); + zpl_vec3_norm(&s, s); + + zpl_vec3_cross(&u, f, s); + + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = +s.x; + m[1][0] = +s.y; + m[2][0] = +s.z; + + m[0][1] = +u.x; + m[1][1] = +u.y; + m[2][1] = +u.z; + + m[0][2] = +f.x; + m[1][2] = +f.y; + m[2][2] = +f.z; + + m[3][0] = -zpl_vec3_dot(s, eye); + m[3][1] = -zpl_vec3_dot(u, eye); + m[3][2] = -zpl_vec3_dot(f, eye); + } + + zpl_quat zpl_quatf(zpl_f32 x, zpl_f32 y, zpl_f32 z, zpl_f32 w) { + zpl_quat q; + q.x = x; + q.y = y; + q.z = z; + q.w = w; + return q; + } + zpl_quat zpl_quatfv(zpl_f32 e[4]) { + zpl_quat q; + q.x = e[0]; + q.y = e[1]; + q.z = e[2]; + q.w = e[3]; + return q; + } + + zpl_quat zpl_quat_axis_angle(zpl_vec3 axis, zpl_f32 angle_radians) { + zpl_quat q; + zpl_vec3_norm(&q.xyz, axis); + zpl_vec3_muleq(&q.xyz, zpl_sin(0.5f * angle_radians)); + q.w = zpl_cos(0.5f * angle_radians); + return q; + } + + zpl_quat zpl_quat_euler_angles(zpl_f32 pitch, zpl_f32 yaw, zpl_f32 roll) { + /* TODO: Do without multiplication, i.e. make it faster */ + zpl_quat q, p, y, r; + p = zpl_quat_axis_angle(zpl_vec3f(1, 0, 0), pitch); + y = zpl_quat_axis_angle(zpl_vec3f(0, 1, 0), yaw); + r = zpl_quat_axis_angle(zpl_vec3f(0, 0, 1), roll); + + zpl_quat_mul(&q, y, p); + zpl_quat_muleq(&q, r); + + return q; + } + + zpl_quat zpl_quat_identity(void) { + zpl_quat q = { 0, 0, 0, 1 }; + return q; + } + + void zpl_quat_add(zpl_quat *d, zpl_quat q0, zpl_quat q1) { zpl_vec4_add(&d->xyzw, q0.xyzw, q1.xyzw); } + void zpl_quat_sub(zpl_quat *d, zpl_quat q0, zpl_quat q1) { zpl_vec4_sub(&d->xyzw, q0.xyzw, q1.xyzw); } + + void zpl_quat_mul(zpl_quat *d, zpl_quat q0, zpl_quat q1) { + d->x = q0.w * q1.x + q0.x * q1.w + q0.y * q1.z - q0.z * q1.y; + d->y = q0.w * q1.y - q0.x * q1.z + q0.y * q1.w + q0.z * q1.x; + d->z = q0.w * q1.z + q0.x * q1.y - q0.y * q1.x + q0.z * q1.w; + d->w = q0.w * q1.w - q0.x * q1.x - q0.y * q1.y - q0.z * q1.z; + } + + void zpl_quat_div(zpl_quat *d, zpl_quat q0, zpl_quat q1) { + zpl_quat iq1; + zpl_quat_inverse(&iq1, q1); + zpl_quat_mul(d, q0, iq1); + } + + void zpl_quat_mulf(zpl_quat *d, zpl_quat q0, zpl_f32 s) { zpl_vec4_mul(&d->xyzw, q0.xyzw, s); } + void zpl_quat_divf(zpl_quat *d, zpl_quat q0, zpl_f32 s) { zpl_vec4_div(&d->xyzw, q0.xyzw, s); } + + void zpl_quat_addeq(zpl_quat *d, zpl_quat q) { zpl_vec4_addeq(&d->xyzw, q.xyzw); } + void zpl_quat_subeq(zpl_quat *d, zpl_quat q) { zpl_vec4_subeq(&d->xyzw, q.xyzw); } + void zpl_quat_muleq(zpl_quat *d, zpl_quat q) { zpl_quat_mul(d, *d, q); } + void zpl_quat_diveq(zpl_quat *d, zpl_quat q) { zpl_quat_div(d, *d, q); } + + void zpl_quat_muleqf(zpl_quat *d, zpl_f32 s) { zpl_vec4_muleq(&d->xyzw, s); } + void zpl_quat_diveqf(zpl_quat *d, zpl_f32 s) { zpl_vec4_diveq(&d->xyzw, s); } + + zpl_f32 zpl_quat_dot(zpl_quat q0, zpl_quat q1) { + zpl_f32 r = zpl_vec3_dot(q0.xyz, q1.xyz) + q0.w * q1.w; + return r; + } + zpl_f32 zpl_quat_mag(zpl_quat q) { + zpl_f32 r = zpl_sqrt(zpl_quat_dot(q, q)); + return r; + } + + void zpl_quat_norm(zpl_quat *d, zpl_quat q) { zpl_quat_divf(d, q, zpl_quat_mag(q)); } + + void zpl_quat_conj(zpl_quat *d, zpl_quat q) { + d->xyz = zpl_vec3f(-q.x, -q.y, -q.z); + d->w = q.w; + } + void zpl_quat_inverse(zpl_quat *d, zpl_quat q) { + zpl_quat_conj(d, q); + zpl_quat_diveqf(d, zpl_quat_dot(q, q)); + } + + void zpl_quat_axis(zpl_vec3 *axis, zpl_quat q) { + zpl_quat n; + zpl_quat_norm(&n, q); + zpl_vec3_div(axis, n.xyz, zpl_sin(zpl_arccos(q.w))); + } + + zpl_f32 zpl_quat_angle(zpl_quat q) { + zpl_f32 mag = zpl_quat_mag(q); + zpl_f32 c = q.w * (1.0f / mag); + zpl_f32 angle = 2.0f * zpl_arccos(c); + return angle; + } + + zpl_f32 zpl_quat_roll(zpl_quat q) { + return zpl_arctan2(2.0f * q.x * q.y + q.z * q.w, q.x * q.x + q.w * q.w - q.y * q.y - q.z * q.z); + } + zpl_f32 zpl_quat_pitch(zpl_quat q) { + return zpl_arctan2(2.0f * q.y * q.z + q.w * q.x, q.w * q.w - q.x * q.x - q.y * q.y + q.z * q.z); + } + zpl_f32 zpl_quat_yaw(zpl_quat q) { return zpl_arcsin(-2.0f * (q.x * q.z - q.w * q.y)); } + + void zpl_quat_rotate_vec3(zpl_vec3 *d, zpl_quat q, zpl_vec3 v) { + /* zpl_vec3 t = 2.0f * cross(q.xyz, v); + * *d = q.w*t + v + cross(q.xyz, t); + */ + zpl_vec3 t, p; + zpl_vec3_cross(&t, q.xyz, v); + zpl_vec3_muleq(&t, 2.0f); + + zpl_vec3_cross(&p, q.xyz, t); + + zpl_vec3_mul(d, t, q.w); + zpl_vec3_addeq(d, v); + zpl_vec3_addeq(d, p); + } + + void zpl_mat4_from_quat(zpl_mat4 *out, zpl_quat q) { + zpl_float4 *m; + zpl_quat a; + zpl_f32 xx, yy, zz, xy, xz, yz, wx, wy, wz; + + zpl_quat_norm(&a, q); + xx = a.x * a.x; + yy = a.y * a.y; + zz = a.z * a.z; + xy = a.x * a.y; + xz = a.x * a.z; + yz = a.y * a.z; + wx = a.w * a.x; + wy = a.w * a.y; + wz = a.w * a.z; + + zpl_mat4_identity(out); + m = zpl_float44_m(out); + + m[0][0] = 1.0f - 2.0f * (yy + zz); + m[0][1] = 2.0f * (xy + wz); + m[0][2] = 2.0f * (xz - wy); + + m[1][0] = 2.0f * (xy - wz); + m[1][1] = 1.0f - 2.0f * (xx + zz); + m[1][2] = 2.0f * (yz + wx); + + m[2][0] = 2.0f * (xz + wy); + m[2][1] = 2.0f * (yz - wx); + m[2][2] = 1.0f - 2.0f * (xx + yy); + } + + void zpl_quat_from_mat4(zpl_quat *out, zpl_mat4 *mat) { + zpl_float4 *m; + zpl_f32 four_x_squared_minus_1, four_y_squared_minus_1, four_z_squared_minus_1, four_w_squared_minus_1, + four_biggest_squared_minus_1; + int biggest_index = 0; + zpl_f32 biggest_value, mult; + + m = zpl_float44_m(mat); + + four_x_squared_minus_1 = m[0][0] - m[1][1] - m[2][2]; + four_y_squared_minus_1 = m[1][1] - m[0][0] - m[2][2]; + four_z_squared_minus_1 = m[2][2] - m[0][0] - m[1][1]; + four_w_squared_minus_1 = m[0][0] + m[1][1] + m[2][2]; + + four_biggest_squared_minus_1 = four_w_squared_minus_1; + if (four_x_squared_minus_1 > four_biggest_squared_minus_1) { + four_biggest_squared_minus_1 = four_x_squared_minus_1; + biggest_index = 1; + } + if (four_y_squared_minus_1 > four_biggest_squared_minus_1) { + four_biggest_squared_minus_1 = four_y_squared_minus_1; + biggest_index = 2; + } + if (four_z_squared_minus_1 > four_biggest_squared_minus_1) { + four_biggest_squared_minus_1 = four_z_squared_minus_1; + biggest_index = 3; + } + + biggest_value = zpl_sqrt(four_biggest_squared_minus_1 + 1.0f) * 0.5f; + mult = 0.25f / biggest_value; + + switch (biggest_index) { + case 0: + out->w = biggest_value; + out->x = (m[1][2] - m[2][1]) * mult; + out->y = (m[2][0] - m[0][2]) * mult; + out->z = (m[0][1] - m[1][0]) * mult; + break; + case 1: + out->w = (m[1][2] - m[2][1]) * mult; + out->x = biggest_value; + out->y = (m[0][1] + m[1][0]) * mult; + out->z = (m[2][0] + m[0][2]) * mult; + break; + case 2: + out->w = (m[2][0] - m[0][2]) * mult; + out->x = (m[0][1] + m[1][0]) * mult; + out->y = biggest_value; + out->z = (m[1][2] + m[2][1]) * mult; + break; + case 3: + out->w = (m[0][1] - m[1][0]) * mult; + out->x = (m[2][0] + m[0][2]) * mult; + out->y = (m[1][2] + m[2][1]) * mult; + out->z = biggest_value; + break; + } + } + + zpl_f32 zpl_plane_distance(zpl_plane* p, zpl_vec3 v) { + return (p->a * v.x + p->b * v.y + p->c * v.z + p->d); + } + + void zpl_frustum_create(zpl_frustum* out, zpl_mat4* camera, zpl_mat4* proj) { + zpl_mat4 pv; + + zpl_mat4_mul(&pv, camera, proj); + + zpl_plane* fp = 0; + zpl_f32 rmag; + + fp = &out->x1; + fp->a = pv.x.w + pv.x.x; + fp->b = pv.y.w + pv.x.y; + fp->c = pv.z.w + pv.x.z; + fp->d = pv.w.w + pv.x.w; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag; + + fp = &out->x2; + + fp->a = pv.x.w - pv.x.x; + fp->b = pv.y.w - pv.x.y; + fp->c = pv.z.w - pv.x.z; + fp->d = pv.w.w - pv.x.w; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag; + + fp = &out->y1; + + fp->a = pv.x.w - pv.y.x; + fp->b = pv.y.w - pv.y.y; + fp->c = pv.z.w - pv.y.w; + fp->d = pv.w.w - pv.y.z; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag; + + fp = &out->y2; + + fp->a = pv.x.w + pv.y.x; + fp->b = pv.y.w + pv.y.y; + fp->c = pv.z.w + pv.y.z; + fp->d = pv.w.w + pv.y.w; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag;; + + fp = &out->z1; + + fp->a = pv.x.w + pv.z.x; + fp->b = pv.y.w + pv.z.y; + fp->c = pv.z.w + pv.z.z; + fp->d = pv.w.w + pv.z.w; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag; + + fp = &out->z2; + + fp->a = pv.x.w - pv.z.x; + fp->b = pv.y.w - pv.z.y; + fp->c = pv.z.w - pv.z.z; + fp->d = pv.w.w - pv.z.w; + + rmag = zpl_rsqrt(zpl_square(fp->a) + zpl_square(fp->b) + zpl_square(fp->c)); + + fp->a *= rmag; + fp->b *= rmag; + fp->c *= rmag; + fp->d *= rmag; + } + + zpl_b8 zpl_frustum_sphere_inside(zpl_frustum* frustum, zpl_vec3 center, zpl_f32 radius) { + if (zpl_plane_distance(&frustum->x1, center) <= -radius) return 0; + if (zpl_plane_distance(&frustum->x2, center) <= -radius) return 0; + if (zpl_plane_distance(&frustum->y1, center) <= -radius) return 0; + if (zpl_plane_distance(&frustum->y2, center) <= -radius) return 0; + if (zpl_plane_distance(&frustum->z1, center) <= -radius) return 0; + if (zpl_plane_distance(&frustum->z2, center) <= -radius) return 0; + + return 1; + } + + zpl_b8 zpl_frustum_point_inside(zpl_frustum* frustum, zpl_vec3 point) { + return zpl_frustum_sphere_inside(frustum, point, 0.0f); + } + + zpl_b8 zpl_frustum_box_inside(zpl_frustum* frustum, zpl_aabb3 aabb) { + zpl_vec3 box, center; + zpl_vec3 v, b; + zpl_vec3_sub(&box, aabb.max, aabb.min); + zpl_vec3_diveq(&box, 2.0f); + zpl_vec3_add(¢er, aabb.min, box); + + b = zpl_vec3f(-box.x, -box.y, -box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(+box.x, -box.y, -box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(-box.x, +box.y, -box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(+box.x, +box.y, -box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(+box.x, +box.y, +box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(-box.x, +box.y, +box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(-box.x, -box.y, +box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + b = zpl_vec3f(+box.x, -box.y, +box.z); + zpl_vec3_add(&v, b, center); + + if (zpl_frustum_point_inside(frustum, v)) return 1; + + return 0; + } + + zpl_f32 zpl_lerp(zpl_f32 a, zpl_f32 b, zpl_f32 t) { return a * (1.0f - t) + b * t; } + zpl_f32 zpl_unlerp(zpl_f32 t, zpl_f32 a, zpl_f32 b) { return (t - a) / (b - a); } + zpl_f32 zpl_smooth_step(zpl_f32 a, zpl_f32 b, zpl_f32 t) { + zpl_f32 x = (t - a) / (b - a); + return x * x * (3.0f - 2.0f * x); + } + zpl_f32 zpl_smoother_step(zpl_f32 a, zpl_f32 b, zpl_f32 t) { + zpl_f32 x = (t - a) / (b - a); + return x * x * x * (x * (6.0f * x - 15.0f) + 10.0f); + } + + #define ZPL_VEC_LERPN(N, d, a, b, t) \ + zpl_vec##N db; \ + zpl_vec##N##_sub(&db, b, a); \ + zpl_vec##N##_muleq(&db, t); \ + zpl_vec##N##_add(d, a, db) + + void zpl_vec2_lerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 b, zpl_f32 t) { ZPL_VEC_LERPN(2, d, a, b, t); } + void zpl_vec3_lerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 b, zpl_f32 t) { ZPL_VEC_LERPN(3, d, a, b, t); } + void zpl_vec4_lerp(zpl_vec4 *d, zpl_vec4 a, zpl_vec4 b, zpl_f32 t) { ZPL_VEC_LERPN(4, d, a, b, t); } + + #undef ZPL_VEC_LERPN + + void zpl_vec2_cslerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 v0, zpl_vec2 b, zpl_vec2 v1, zpl_f32 t) { + zpl_f32 t2 = t * t; + zpl_f32 ti = (t - 1); + zpl_f32 ti2 = ti * ti; + + zpl_f32 h00 = (1 + 2 * t) * ti2; + zpl_f32 h10 = t * ti2; + zpl_f32 h01 = t2 * (3 - 2 * t); + zpl_f32 h11 = t2 * ti; + + d->x = h00 * a.x + h10 * v0.x + h01 * b.x + h11 * v1.x; + d->y = h00 * a.y + h10 * v0.y + h01 * b.y + h11 * v1.y; + } + + void zpl_vec3_cslerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 v0, zpl_vec3 b, zpl_vec3 v1, zpl_f32 t) { + zpl_f32 t2 = t * t; + zpl_f32 ti = (t - 1); + zpl_f32 ti2 = ti * ti; + + zpl_f32 h00 = (1 + 2 * t) * ti2; + zpl_f32 h10 = t * ti2; + zpl_f32 h01 = t2 * (3 - 2 * t); + zpl_f32 h11 = t2 * ti; + + d->x = h00 * a.x + h10 * v0.x + h01 * b.x + h11 * v1.x; + d->y = h00 * a.y + h10 * v0.y + h01 * b.y + h11 * v1.y; + d->z = h00 * a.z + h10 * v0.z + h01 * b.z + h11 * v1.z; + } + + void zpl_vec2_dcslerp(zpl_vec2 *d, zpl_vec2 a, zpl_vec2 v0, zpl_vec2 b, zpl_vec2 v1, zpl_f32 t) { + zpl_f32 t2 = t * t; + + zpl_f32 dh00 = 6 * t2 - 6 * t; + zpl_f32 dh10 = 3 * t2 - 4 * t + 1; + zpl_f32 dh01 = -6 * t2 + 6 * t; + zpl_f32 dh11 = 3 * t2 - 2 * t; + + d->x = dh00 * a.x + dh10 * v0.x + dh01 * b.x + dh11 * v1.x; + d->y = dh00 * a.y + dh10 * v0.y + dh01 * b.y + dh11 * v1.y; + } + + void zpl_vec3_dcslerp(zpl_vec3 *d, zpl_vec3 a, zpl_vec3 v0, zpl_vec3 b, zpl_vec3 v1, zpl_f32 t) { + zpl_f32 t2 = t * t; + + zpl_f32 dh00 = 6 * t2 - 6 * t; + zpl_f32 dh10 = 3 * t2 - 4 * t + 1; + zpl_f32 dh01 = -6 * t2 + 6 * t; + zpl_f32 dh11 = 3 * t2 - 2 * t; + + d->x = dh00 * a.x + dh10 * v0.x + dh01 * b.x + dh11 * v1.x; + d->y = dh00 * a.y + dh10 * v0.y + dh01 * b.y + dh11 * v1.y; + d->z = dh00 * a.z + dh10 * v0.z + dh01 * b.z + dh11 * v1.z; + } + + void zpl_quat_lerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t) { zpl_vec4_lerp(&d->xyzw, a.xyzw, b.xyzw, t); } + void zpl_quat_nlerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t) { + zpl_quat_lerp(d, a, b, t); + zpl_quat_norm(d, *d); + } + + void zpl_quat_slerp(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t) { + zpl_quat x, y, z; + zpl_f32 cos_theta, angle; + zpl_f32 s1, s0, is; + + z = b; + cos_theta = zpl_quat_dot(a, b); + + if (cos_theta < 0.0f) { + z = zpl_quatf(-b.x, -b.y, -b.z, -b.w); + cos_theta = -cos_theta; + } + + if (cos_theta > 1.0f) { + /* NOTE: Use lerp not nlerp as it's not a real angle or they are not normalized */ + zpl_quat_lerp(d, a, b, t); + } + + angle = zpl_arccos(cos_theta); + + s1 = zpl_sin((1.0f - t) * angle); + s0 = zpl_sin(t * angle); + is = 1.0f / zpl_sin(angle); + zpl_quat_mulf(&x, a, s1); + zpl_quat_mulf(&y, z, s0); + zpl_quat_add(d, x, y); + zpl_quat_muleqf(d, is); + } + + void zpl_quat_slerp_approx(zpl_quat *d, zpl_quat a, zpl_quat b, zpl_f32 t) { + /* NOTE: Derived by taylor expanding the geometric interpolation equation + * Even works okay for nearly anti-parallel versors!!! + */ + /* NOTE: Extra interations cannot be used as they require angle^4 which is not worth it to approximate */ + zpl_f32 tp = t + (1.0f - zpl_quat_dot(a, b)) / 3.0f * t * (-2.0f * t * t + 3.0f * t - 1.0f); + zpl_quat_nlerp(d, a, b, tp); + } + + void zpl_quat_nquad(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t) { + zpl_quat x, y; + zpl_quat_nlerp(&x, p, q, t); + zpl_quat_nlerp(&y, a, b, t); + zpl_quat_nlerp(d, x, y, 2.0f * t * (1.0f - t)); + } + + void zpl_quat_squad(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t) { + zpl_quat x, y; + zpl_quat_slerp(&x, p, q, t); + zpl_quat_slerp(&y, a, b, t); + zpl_quat_slerp(d, x, y, 2.0f * t * (1.0f - t)); + } + + void zpl_quat_squad_approx(zpl_quat *d, zpl_quat p, zpl_quat a, zpl_quat b, zpl_quat q, zpl_f32 t) { + zpl_quat x, y; + zpl_quat_slerp_approx(&x, p, q, t); + zpl_quat_slerp_approx(&y, a, b, t); + zpl_quat_slerp_approx(d, x, y, 2.0f * t * (1.0f - t)); + } + + zpl_rect2 zpl_rect2f(zpl_vec2 pos, zpl_vec2 dim) { + zpl_rect2 r; + r.pos = pos; + r.dim = dim; + return r; + } + + zpl_rect3 zpl_rect3f(zpl_vec3 pos, zpl_vec3 dim) { + zpl_rect3 r; + r.pos = pos; + r.dim = dim; + return r; + } + + zpl_aabb2 zpl_aabb2f(zpl_f32 minx, zpl_f32 miny, zpl_f32 maxx, zpl_f32 maxy) { + zpl_aabb2 r; + r.min = zpl_vec2f(minx, miny); + r.max = zpl_vec2f(maxx, maxy); + return r; + } + zpl_aabb3 zpl_aabb3f(zpl_f32 minx, zpl_f32 miny, zpl_f32 minz, zpl_f32 maxx, zpl_f32 maxy, zpl_f32 maxz) { + zpl_aabb3 r; + r.min = zpl_vec3f(minx, miny, minz); + r.max = zpl_vec3f(maxx, maxy, maxz); + return r; + } + + zpl_aabb2 zpl_aabb2_rect2(zpl_rect2 a) { + zpl_aabb2 r; + r.min = a.pos; + zpl_vec2_add(&r.max, a.pos, a.dim); + return r; + } + zpl_aabb3 zpl_aabb3_rect3(zpl_rect3 a) { + zpl_aabb3 r; + r.min = a.pos; + zpl_vec3_add(&r.max, a.pos, a.dim); + return r; + } + + zpl_rect2 zpl_rect2_aabb2(zpl_aabb2 a) { + zpl_rect2 r; + r.pos = a.min; + zpl_vec2_sub(&r.dim, a.max, a.min); + return r; + } + zpl_rect3 zpl_rect3_aabb3(zpl_aabb3 a) { + zpl_rect3 r; + r.pos = a.min; + zpl_vec3_sub(&r.dim, a.max, a.min); + return r; + } + + int zpl_rect2_contains(zpl_rect2 a, zpl_f32 x, zpl_f32 y) { + zpl_f32 min_x = zpl_min(a.pos.x, a.pos.x + a.dim.x); + zpl_f32 max_x = zpl_max(a.pos.x, a.pos.x + a.dim.x); + zpl_f32 min_y = zpl_min(a.pos.y, a.pos.y + a.dim.y); + zpl_f32 max_y = zpl_max(a.pos.y, a.pos.y + a.dim.y); + int result = (x >= min_x) & (x < max_x) & (y >= min_y) & (y < max_y); + return result; + } + + int zpl_rect2_contains_vec2(zpl_rect2 a, zpl_vec2 p) { return zpl_rect2_contains(a, p.x, p.y); } + + int zpl_rect2_intersects(zpl_rect2 a, zpl_rect2 b) { + zpl_rect2 r = { 0 }; + return zpl_rect2_intersection_result(a, b, &r); + } + + int zpl_rect2_intersection_result(zpl_rect2 a, zpl_rect2 b, zpl_rect2 *intersection) { + zpl_f32 a_min_x = zpl_min(a.pos.x, a.pos.x + a.dim.x); + zpl_f32 a_max_x = zpl_max(a.pos.x, a.pos.x + a.dim.x); + zpl_f32 a_min_y = zpl_min(a.pos.y, a.pos.y + a.dim.y); + zpl_f32 a_max_y = zpl_max(a.pos.y, a.pos.y + a.dim.y); + + zpl_f32 b_min_x = zpl_min(b.pos.x, b.pos.x + b.dim.x); + zpl_f32 b_max_x = zpl_max(b.pos.x, b.pos.x + b.dim.x); + zpl_f32 b_min_y = zpl_min(b.pos.y, b.pos.y + b.dim.y); + zpl_f32 b_max_y = zpl_max(b.pos.y, b.pos.y + b.dim.y); + + zpl_f32 x0 = zpl_max(a_min_x, b_min_x); + zpl_f32 y0 = zpl_max(a_min_y, b_min_y); + zpl_f32 x1 = zpl_min(a_max_x, b_max_x); + zpl_f32 y1 = zpl_min(a_max_y, b_max_y); + + if ((x0 < x1) && (y0 < y1)) { + zpl_rect2 r = zpl_rect2f(zpl_vec2f(x0, y0), zpl_vec2f(x1 - x0, y1 - y0)); + *intersection = r; + return 1; + } else { + zpl_rect2 r = { 0 }; + *intersection = r; + return 0; + } + } + + int zpl_aabb2_contains(zpl_aabb2 a, zpl_f32 x, zpl_f32 y) { + return (zpl_is_between_limit(x, a.min.x, a.max.x) && zpl_is_between_limit(y, a.min.y, a.max.y)); + } + + int zpl_aabb3_contains(zpl_aabb3 a, zpl_f32 x, zpl_f32 y, zpl_f32 z) { + return (zpl_is_between_limit(x, a.min.x, a.max.x) && zpl_is_between_limit(y, a.min.y, a.max.y) && zpl_is_between_limit(z, a.min.z, a.max.z)); + } + + zpl_aabb2 zpl_aabb2_cut_left(zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 minx = a->min.x; + a->min.x = zpl_min(a->max.x, a->min.x + b); + return zpl_aabb2f(minx, a->min.y, a->min.x, a->max.y); + } + zpl_aabb2 zpl_aabb2_cut_right(zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 maxx = a->max.x; + a->max.x = zpl_max(a->min.x, a->max.x - b); + return zpl_aabb2f(a->max.x, a->min.y, maxx, a->max.y); + } + zpl_aabb2 zpl_aabb2_cut_top(zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 miny = a->min.y; + a->min.y = zpl_min(a->max.y, a->min.y + b); + return zpl_aabb2f(a->min.x, miny, a->max.x, a->min.y); + } + zpl_aabb2 zpl_aabb2_cut_bottom(zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 maxy = a->max.y; + a->max.y = zpl_max(a->min.y, a->max.y - b); + return zpl_aabb2f(a->min.x, a->max.y, a->max.x, maxy); + } + + zpl_aabb2 zpl_aabb2_get_left(const zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 minx = a->min.x; + zpl_f32 aminx = zpl_min(a->max.x, a->min.x + b); + return zpl_aabb2f(minx, a->min.y, aminx, a->max.y); + } + zpl_aabb2 zpl_aabb2_get_right(const zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 maxx = a->max.x; + zpl_f32 amaxx = zpl_max(a->min.x, a->max.x - b); + return zpl_aabb2f(amaxx, a->min.y, maxx, a->max.y); + } + zpl_aabb2 zpl_aabb2_get_top(const zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 miny = a->min.y; + zpl_f32 aminy = zpl_min(a->max.y, a->min.y + b); + return zpl_aabb2f(a->min.x, miny, a->max.x, aminy); + } + zpl_aabb2 zpl_aabb2_get_bottom(const zpl_aabb2 *a, zpl_f32 b) { + zpl_f32 maxy = a->max.y; + zpl_f32 amaxy = zpl_max(a->min.y, a->max.y - b); + return zpl_aabb2f(a->min.x, amaxy, a->max.x, maxy); + } + + zpl_aabb2 zpl_aabb2_add_left(const zpl_aabb2 *a, zpl_f32 b) { + return zpl_aabb2f(a->min.x-b, a->min.y, a->min.x, a->max.y); + } + zpl_aabb2 zpl_aabb2_add_right(const zpl_aabb2 *a, zpl_f32 b) { + return zpl_aabb2f(a->max.x, a->min.y, a->max.x+b, a->max.y); + } + zpl_aabb2 zpl_aabb2_add_top(const zpl_aabb2 *a, zpl_f32 b) { + return zpl_aabb2f(a->min.x, a->min.y-b, a->max.x, a->min.y); + } + zpl_aabb2 zpl_aabb2_add_bottom(const zpl_aabb2 *a, zpl_f32 b) { + return zpl_aabb2f(a->min.x, a->max.y, a->max.x, a->max.y+b); + } + + zpl_aabb2 zpl_aabb2_contract(const zpl_aabb2 *a, zpl_f32 b) { + zpl_aabb2 r = *a; + zpl_vec2 vb = zpl_vec2f(b, b); + zpl_vec2_addeq(&r.min, vb); + zpl_vec2_subeq(&r.max, vb); + + if (zpl_vec2_mag2(r.min) > zpl_vec2_mag2(r.max)) { + return zpl_aabb2f(0,0,0,0); + } + return r; + } + zpl_aabb2 zpl_aabb2_expand(const zpl_aabb2 *a, zpl_f32 b) { + return zpl_aabb2_contract(a, -b); + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_THREADING) + // file: source/threading/fence.c + + + ZPL_BEGIN_C_DECLS + + #if defined(_MSC_VER) + /* Microsoft C/C++-compatible compiler */ + # include + #elif defined(__GNUC__) && (defined(__x86_64__) || defined(__i386__)) + /* GCC-compatible compiler, targeting x86/x86-64 */ + # include + #elif defined(__GNUC__) && defined(__ARM_NEON__) + /* GCC-compatible compiler, targeting ARM with NEON */ + # include + #elif defined(__GNUC__) && defined(__IWMMXT__) + /* GCC-compatible compiler, targeting ARM with WMMX */ + # include + #elif (defined(__GNUC__) || defined(__xlC__)) && (defined(__VEC__) || defined(__ALTIVEC__)) + /* XLC or GCC-compatible compiler, targeting PowerPC with VMX/VSX */ + # include + #elif defined(__GNUC__) && defined(__SPE__) + /* GCC-compatible compiler, targeting PowerPC with SPE */ + # include + #endif + + void zpl_yield_thread(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + _mm_pause(); + # elif defined(ZPL_SYSTEM_OSX) || defined(ZPL_COMPILER_TINYC) + __asm__ volatile ("" : : : "memory"); + # elif defined(ZPL_CPU_X86) + _mm_pause(); + # endif + } + + void zpl_mfence(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + _ReadWriteBarrier(); + # elif defined(ZPL_COMPILER_TINYC) + __asm__ volatile ("" : : : "memory"); + # elif defined(ZPL_SYSTEM_OSX) + __sync_synchronize(); + # elif defined(ZPL_CPU_X86) + _mm_mfence(); + # endif + } + + void zpl_sfence(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + _WriteBarrier(); + # elif defined(ZPL_SYSTEM_OSX) || defined(ZPL_COMPILER_TINYC) + __asm__ volatile ("" : : : "memory"); + # elif defined(ZPL_CPU_X86) + _mm_sfence(); + # endif + } + + void zpl_lfence(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + _ReadBarrier(); + # elif defined(ZPL_SYSTEM_OSX) || defined(ZPL_COMPILER_TINYC) + __asm__ volatile ("" : : : "memory"); + # elif defined(ZPL_CPU_X86) + _mm_lfence(); + # endif + } + + ZPL_END_C_DECLS + // file: source/threading/atomic.c + + + ZPL_BEGIN_C_DECLS + + //////////////////////////////////////////////////////////////// + // + // Concurrency + // + // + // IMPORTANT TODO: Use compiler intrinsics for the atomics + + #if defined(ZPL_COMPILER_MSVC) && !defined(ZPL_COMPILER_CLANG) + zpl_i32 zpl_atomic32_load (zpl_atomic32 const *a) { return a->value; } + void zpl_atomic32_store(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) value) { a->value = value; } + + zpl_i32 zpl_atomic32_compare_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) expected, zpl_atomicarg(zpl_i32) desired) { + return _InterlockedCompareExchange(cast(long *)a, desired, expected); + } + zpl_i32 zpl_atomic32_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) desired) { + return _InterlockedExchange(cast(long *)a, desired); + } + zpl_i32 zpl_atomic32_fetch_add(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return _InterlockedExchangeAdd(cast(long *)a, operand); + } + zpl_i32 zpl_atomic32_fetch_and(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return _InterlockedAnd(cast(long *)a, operand); + } + zpl_i32 zpl_atomic32_fetch_or(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return _InterlockedOr(cast(long *)a, operand); + } + + zpl_i64 zpl_atomic64_load(zpl_atomic64 const *a) { + # if defined(ZPL_ARCH_64_BIT) + return a->value; + # elif ZPL_CPU_X86 + // NOTE: The most compatible way to get an atomic 64-bit load on x86 is with cmpxchg8b + zpl_atomicarg(zpl_i64) result; + __asm { + mov esi, a; + mov ebx, eax; + mov ecx, edx; + lock cmpxchg8b [esi]; + mov dword ptr result, eax; + mov dword ptr result[4], edx; + } + return result; + # else + # error TODO: atomics for this CPU + # endif + } + + void zpl_atomic64_store(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) value) { + # if defined(ZPL_ARCH_64_BIT) + a->value = value; + # elif ZPL_CPU_X86 + // NOTE: The most compatible way to get an atomic 64-bit store on x86 is with cmpxchg8b + __asm { + mov esi, a; + mov ebx, dword ptr value; + mov ecx, dword ptr value[4]; + retry: + cmpxchg8b [esi]; + jne retry; + } + # else + # error TODO: atomics for this CPU + # endif + } + + zpl_i64 zpl_atomic64_compare_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) expected, zpl_atomicarg(zpl_i64) desired) { + return _InterlockedCompareExchange64(cast(zpl_atomicarg(zpl_i64) *)a, desired, expected); + } + + zpl_i64 zpl_atomic64_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) desired) { + # if defined(ZPL_ARCH_64_BIT) + return _InterlockedExchange64(cast(zpl_atomicarg(zpl_i64) *)a, desired); + # elif ZPL_CPU_X86 + zpl_atomicarg(zpl_i64) expected = a->value; + for (;;) { + zpl_atomicarg(zpl_i64) original = _InterlockedCompareExchange64(cast(zpl_atomicarg(zpl_i64) *)a, desired, expected); + if (original == expected) + return original; + expected = original; + } + # else + # error TODO: atomics for this CPU + # endif + } + + zpl_i64 zpl_atomic64_fetch_add(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + return _InterlockedExchangeAdd64(cast(zpl_atomicarg(zpl_i64) *)a, operand); + # elif ZPL_CPU_X86 + zpl_atomicarg(zpl_i64) expected = a->value; + for (;;) { + zpl_atomicarg(zpl_i64) original = _InterlockedCompareExchange64(cast(zpl_atomicarg(zpl_i64) *)a, expected + operand, expected); + if (original == expected) + return original; + expected = original; + } + # else + # error TODO: atomics for this CPU + # endif + } + + zpl_i64 zpl_atomic64_fetch_and(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + return _InterlockedAnd64(cast(zpl_atomicarg(zpl_i64) *)a, operand); + # elif ZPL_CPU_X86 + zpl_atomicarg(zpl_i64) expected = a->value; + for (;;) { + zpl_atomicarg(zpl_i64) original = _InterlockedCompareExchange64(cast(zpl_atomicarg(zpl_i64) *)a, expected & operand, expected); + if (original == expected) + return original; + expected = original; + } + # else + # error TODO: atomics for this CPU + # endif + } + + zpl_i64 zpl_atomic64_fetch_or(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + return _InterlockedOr64(cast(zpl_atomicarg(zpl_i64) *)a, operand); + # elif ZPL_CPU_X86 + zpl_atomicarg(zpl_i64) expected = a->value; + for (;;) { + zpl_atomicarg(zpl_i64) original = _InterlockedCompareExchange64(cast(zpl_atomicarg(zpl_i64) *)a, expected | operand, expected); + if (original == expected) + return original; + expected = original; + } + # else + # error TODO: atomics for this CPU + # endif + } + + #elif defined(ZPL_CPU_X86) + + zpl_i32 zpl_atomic32_load (zpl_atomic32 const *a) { return a->value; } + void zpl_atomic32_store(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) value) { a->value = value; } + + zpl_i32 zpl_atomic32_compare_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) expected, zpl_atomicarg(zpl_i32) desired) { + zpl_atomicarg(zpl_i32) original; + __asm__( + "lock; cmpxchgl %2, %1" + : "=a"(original), "+m"(a->value) + : "q"(desired), "0"(expected) + ); + return original; + } + + zpl_i32 zpl_atomic32_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) desired) { + // NOTE: No lock prefix is necessary for xchgl + zpl_atomicarg(zpl_i32) original; + __asm__( + "xchgl %0, %1" + : "=r"(original), "+m"(a->value) + : "0"(desired) + ); + return original; + } + + zpl_i32 zpl_atomic32_fetch_add(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + zpl_atomicarg(zpl_i32) original; + __asm__( + "lock; xaddl %0, %1" + : "=r"(original), "+m"(a->value) + : "0"(operand) + ); + return original; + } + + zpl_i32 zpl_atomic32_fetch_and(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + zpl_atomicarg(zpl_i32) original; + zpl_atomicarg(zpl_i32) tmp; + __asm__( + "1: movl %1, %0\n" + " movl %0, %2\n" + " andl %3, %2\n" + " lock; cmpxchgl %2, %1\n" + " jne 1b" + : "=&a"(original), "+m"(a->value), "=&r"(tmp) + : "r"(operand) + ); + return original; + } + + zpl_i32 zpl_atomic32_fetch_or(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + zpl_atomicarg(zpl_i32) original; + zpl_atomicarg(zpl_i32) temp; + __asm__( + "1: movl %1, %0\n" + " movl %0, %2\n" + " orl %3, %2\n" + " lock; cmpxchgl %2, %1\n" + " jne 1b" + : "=&a"(original), "+m"(a->value), "=&r"(temp) + : "r"(operand) + ); + return original; + } + + + zpl_i64 zpl_atomic64_load(zpl_atomic64 const *a) { + # if defined(ZPL_ARCH_64_BIT) + return a->value; + # else + zpl_atomicarg(zpl_i64) original; + __asm__( + "movl %%ebx, %%eax\n" + "movl %%ecx, %%edx\n" + "lock; cmpxchg8b %1" + : "=&A"(original) + : "m"(a->value) + ); + return original; + # endif + } + + void zpl_atomic64_store(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) value) { + # if defined(ZPL_ARCH_64_BIT) + a->value = value; + # else + zpl_atomicarg(zpl_i64) expected = a->value; + __asm__( + "1: cmpxchg8b %0\n" + " jne 1b" + : "=m"(a->value) + : "b"((zpl_atomicarg(zpl_i32))value), "c"((zpl_atomicarg(zpl_i32))(value >> 32)), "A"(expected) + ); + # endif + } + + zpl_i64 zpl_atomic64_compare_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) expected, zpl_atomicarg(zpl_i64) desired) { + # if defined(ZPL_ARCH_64_BIT) + zpl_atomicarg(zpl_i64) original; + __asm__( + "lock; cmpxchgq %2, %1" + : "=a"(original), "+m"(a->value) + : "q"(desired), "0"(expected) + ); + return original; + # else + zpl_atomicarg(zpl_i64) original; + __asm__( + "lock; cmpxchg8b %1" + : "=A"(original), "+m"(a->value) + : "b"((zpl_atomicarg(zpl_i32))desired), "c"((zpl_atomicarg(zpl_i32))(desired >> 32)), "0"(expected) + ); + return original; + # endif + } + + zpl_i64 zpl_atomic64_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) desired) { + # if defined(ZPL_ARCH_64_BIT) + zpl_atomicarg(zpl_i64) original; + __asm__( + "xchgq %0, %1" + : "=r"(original), "+m"(a->value) + : "0"(desired) + ); + return original; + # else + zpl_atomicarg(zpl_i64) original = a->value; + for (;;) { + zpl_atomicarg(zpl_i64) previous = zpl_atomic64_compare_exchange(a, original, desired); + if (original == previous) + return original; + original = previous; + } + # endif + } + + zpl_i64 zpl_atomic64_fetch_add(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + zpl_atomicarg(zpl_i64) original; + __asm__( + "lock; xaddq %0, %1" + : "=r"(original), "+m"(a->value) + : "0"(operand) + ); + return original; + # else + for (;;) { + zpl_atomicarg(zpl_i64) original = a->value; + if (zpl_atomic64_compare_exchange(a, original, original + operand) == original) + return original; + } + # endif + } + + zpl_i64 zpl_atomic64_fetch_and(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + zpl_atomicarg(zpl_i64) original; + zpl_atomicarg(zpl_i64) tmp; + __asm__( + "1: movq %1, %0\n" + " movq %0, %2\n" + " andq %3, %2\n" + " lock; cmpxchgq %2, %1\n" + " jne 1b" + : "=&a"(original), "+m"(a->value), "=&r"(tmp) + : "r"(operand) + ); + return original; + # else + for (;;) { + zpl_atomicarg(zpl_i64) original = a->value; + if (zpl_atomic64_compare_exchange(a, original, original & operand) == original) + return original; + } + # endif + } + + zpl_i64 zpl_atomic64_fetch_or(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + # if defined(ZPL_ARCH_64_BIT) + zpl_atomicarg(zpl_i64) original; + zpl_atomicarg(zpl_i64) temp; + __asm__( + "1: movq %1, %0\n" + " movq %0, %2\n" + " orq %3, %2\n" + " lock; cmpxchgq %2, %1\n" + " jne 1b" + : "=&a"(original), "+m"(a->value), "=&r"(temp) + : "r"(operand) + ); + return original; + # else + for (;;) { + zpl_atomicarg(zpl_i64) original = a->value; + if (zpl_atomic64_compare_exchange(a, original, original | operand) == original) + return original; + } + # endif + } + + #elif !defined(ZPL_COMPILER_MSVC) + zpl_i32 zpl_atomic32_load (zpl_atomic32 const *a) { + return __atomic_load_n((zpl_i32*)&a->value, __ATOMIC_SEQ_CST); + } + void zpl_atomic32_store(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) value) { + __atomic_store((zpl_i32*)&a->value, (zpl_i32*)&value, __ATOMIC_SEQ_CST); + } + + zpl_i32 zpl_atomic32_compare_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) expected, zpl_atomicarg(zpl_i32) desired) { + return __atomic_compare_exchange_n((zpl_i32*)&a->value, (zpl_i32*)&expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + } + + zpl_i32 zpl_atomic32_exchange(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) desired) { + return __atomic_exchange_n((zpl_i32*)&a->value, desired, __ATOMIC_SEQ_CST); + } + + zpl_i32 zpl_atomic32_fetch_add(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return __atomic_fetch_add((zpl_i32*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + zpl_i32 zpl_atomic32_fetch_and(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return __atomic_fetch_and((zpl_i32*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + zpl_i32 zpl_atomic32_fetch_or(zpl_atomic32 *a, zpl_atomicarg(zpl_i32) operand) { + return __atomic_fetch_or((zpl_i32*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_load(zpl_atomic64 const *a) { + return __atomic_load_n((zpl_i64*)&a->value, __ATOMIC_SEQ_CST); + } + + void zpl_atomic64_store(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) value) { + __atomic_store((zpl_i64*)&a->value, (zpl_i64*)&value, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_compare_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) expected, zpl_atomicarg(zpl_i64) desired) { + return __atomic_compare_exchange_n((zpl_i64*)&a->value, (zpl_i64*)&expected, desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_exchange(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) desired) { + return __atomic_exchange_n((zpl_i64*)&a->value, desired, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_fetch_add(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + return __atomic_fetch_add((zpl_i64*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_fetch_and(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + return __atomic_fetch_and((zpl_i64*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + zpl_i64 zpl_atomic64_fetch_or(zpl_atomic64 *a, zpl_atomicarg(zpl_i64) operand) { + return __atomic_fetch_or((zpl_i64*)&a->value, operand, __ATOMIC_SEQ_CST); + } + + #else + # error TODO: Implement Atomics for this CPU + #endif + + + + zpl_b32 zpl_atomic32_spin_lock(zpl_atomic32 *a, zpl_isize time_out) { + zpl_atomicarg(zpl_i32) old_value = zpl_atomic32_compare_exchange(a, 1, 0); + zpl_i32 counter = 0; + while (old_value != 0 && (time_out < 0 || counter++ < time_out)) { + zpl_yield_thread(); + old_value = zpl_atomic32_compare_exchange(a, 1, 0); + zpl_mfence(); + } + return old_value == 0; + } + + void zpl_atomic32_spin_unlock(zpl_atomic32 *a) { + zpl_atomic32_store(a, 0); + zpl_mfence(); + } + + zpl_b32 zpl_atomic64_spin_lock(zpl_atomic64 *a, zpl_isize time_out) { + zpl_atomicarg(zpl_i64) old_value = zpl_atomic64_compare_exchange(a, 1, 0); + zpl_atomicarg(zpl_i64) counter = 0; + while (old_value != 0 && (time_out < 0 || counter++ < time_out)) { + zpl_yield_thread(); + old_value = zpl_atomic64_compare_exchange(a, 1, 0); + zpl_mfence(); + } + return old_value == 0; + } + + void zpl_atomic64_spin_unlock(zpl_atomic64 *a) { + zpl_atomic64_store(a, 0); + zpl_mfence(); + } + + zpl_b32 zpl_atomic32_try_acquire_lock(zpl_atomic32 *a) { + zpl_atomicarg(zpl_i32) old_value; + zpl_yield_thread(); + old_value = zpl_atomic32_compare_exchange(a, 1, 0); + zpl_mfence(); + return old_value == 0; + } + + zpl_b32 zpl_atomic64_try_acquire_lock(zpl_atomic64 *a) { + zpl_atomicarg(zpl_i64) old_value; + zpl_yield_thread(); + old_value = zpl_atomic64_compare_exchange(a, 1, 0); + zpl_mfence(); + return old_value == 0; + } + + + + #if defined(ZPL_ARCH_32_BIT) + + void* zpl_atomic_ptr_load(zpl_atomic_ptr const *a) { + return (void *)cast(zpl_intptr)zpl_atomic32_load(cast(zpl_atomic32 const *)a); + } + void zpl_atomic_ptr_store(zpl_atomic_ptr *a, zpl_atomicarg(void *)value) { + zpl_atomic32_store(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)value); + } + void* zpl_atomic_ptr_compare_exchange(zpl_atomic_ptr *a, zpl_atomicarg(void *)expected, zpl_atomicarg(void *)desired) { + return (void *)cast(zpl_intptr)zpl_atomic32_compare_exchange(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)expected, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)desired); + } + void* zpl_atomic_ptr_exchange(zpl_atomic_ptr *a, zpl_atomicarg(void *)desired) { + return (void *)cast(zpl_intptr)zpl_atomic32_exchange(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)desired); + } + void* zpl_atomic_ptr_fetch_add(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic32_fetch_add(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)operand); + } + void* zpl_atomic_ptr_fetch_and(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic32_fetch_and(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)operand); + } + void* zpl_atomic_ptr_fetch_or(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic32_fetch_or(cast(zpl_atomic32 *)a, cast(zpl_atomicarg(zpl_i32))cast(zpl_intptr)operand); + } + zpl_b32 zpl_atomic_ptr_spin_lock(zpl_atomic_ptr *a, zpl_isize time_out) { + return zpl_atomic32_spin_lock(cast(zpl_atomic32 *)a, time_out); + } + void zpl_atomic_ptr_spin_unlock(zpl_atomic_ptr *a) { + zpl_atomic32_spin_unlock(cast(zpl_atomic32 *)a); + } + zpl_b32 zpl_atomic_ptr_try_acquire_lock(zpl_atomic_ptr *a) { + return zpl_atomic32_try_acquire_lock(cast(zpl_atomic32 *)a); + } + + #elif defined(ZPL_ARCH_64_BIT) + + void* zpl_atomic_ptr_load(zpl_atomic_ptr const *a) { + return (void *)cast(zpl_intptr)zpl_atomic64_load(cast(zpl_atomic64 const *)a); + } + void zpl_atomic_ptr_store(zpl_atomic_ptr *a, zpl_atomicarg(void *)value) { + zpl_atomic64_store(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)value); + } + void* zpl_atomic_ptr_compare_exchange(zpl_atomic_ptr *a, zpl_atomicarg(void *)expected, zpl_atomicarg(void *)desired) { + return (void *)cast(zpl_intptr)zpl_atomic64_compare_exchange(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)expected, cast(zpl_i64)cast(zpl_intptr)desired); + } + void* zpl_atomic_ptr_exchange(zpl_atomic_ptr *a, zpl_atomicarg(void *)desired) { + return (void *)cast(zpl_intptr)zpl_atomic64_exchange(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)desired); + } + void* zpl_atomic_ptr_fetch_add(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic64_fetch_add(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)operand); + } + void* zpl_atomic_ptr_fetch_and(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic64_fetch_and(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)operand); + } + void* zpl_atomic_ptr_fetch_or(zpl_atomic_ptr *a, zpl_atomicarg(void *)operand) { + return (void *)cast(zpl_intptr)zpl_atomic64_fetch_or(cast(zpl_atomic64 *)a, cast(zpl_i64)cast(zpl_intptr)operand); + } + zpl_b32 zpl_atomic_ptr_spin_lock(zpl_atomic_ptr *a, zpl_isize time_out) { + return zpl_atomic64_spin_lock(cast(zpl_atomic64 *)a, time_out); + } + void zpl_atomic_ptr_spin_unlock(zpl_atomic_ptr *a) { + zpl_atomic64_spin_unlock(cast(zpl_atomic64 *)a); + } + zpl_b32 zpl_atomic_ptr_try_acquire_lock(zpl_atomic_ptr *a) { + return zpl_atomic64_try_acquire_lock(cast(zpl_atomic64 *)a); + } + + #endif + + ZPL_END_C_DECLS + // file: source/threading/sem.c + + + ZPL_BEGIN_C_DECLS + + void zpl_semaphore_release(zpl_semaphore *s) { zpl_semaphore_post(s, 1); } + + #if defined(ZPL_SYSTEM_WINDOWS) + + void zpl_semaphore_init (zpl_semaphore *s) { s->win32_handle = CreateSemaphoreA(NULL, 0, ZPL_I32_MAX, NULL); } + void zpl_semaphore_destroy(zpl_semaphore *s) { CloseHandle(s->win32_handle); } + void zpl_semaphore_post (zpl_semaphore *s, zpl_i32 count) { ReleaseSemaphore(s->win32_handle, count, NULL); } + void zpl_semaphore_wait (zpl_semaphore *s) { WaitForSingleObject(s->win32_handle, INFINITE); } + zpl_i32 zpl_semaphore_trywait(zpl_semaphore *s) { int r = WaitForSingleObject(s->win32_handle, 0); return r; } + + #elif defined(ZPL_SYSTEM_OSX) + + void zpl_semaphore_init (zpl_semaphore *s) { semaphore_create(mach_task_self(), &s->osx_handle, SYNC_POLICY_FIFO, 0); } + void zpl_semaphore_destroy(zpl_semaphore *s) { semaphore_destroy(mach_task_self(), s->osx_handle); } + void zpl_semaphore_post (zpl_semaphore *s, zpl_i32 count) { while (count --> 0) semaphore_signal(s->osx_handle); } + void zpl_semaphore_wait (zpl_semaphore *s) { semaphore_wait(s->osx_handle); } + zpl_i32 zpl_semaphore_trywait(zpl_semaphore *s) { mach_timespec_t t; t.tv_sec = t.tv_nsec = 0; kern_return_t r = semaphore_timedwait(s->osx_handle, t); return r; } + + #elif defined(ZPL_SYSTEM_UNIX) + + void zpl_semaphore_init (zpl_semaphore *s) { sem_init(&s->unix_handle, 0, 0); } + void zpl_semaphore_destroy(zpl_semaphore *s) { sem_destroy(&s->unix_handle); } + void zpl_semaphore_post (zpl_semaphore *s, zpl_i32 count) { while (count --> 0) sem_post(&s->unix_handle); } + void zpl_semaphore_wait (zpl_semaphore *s) { int i; do { i = sem_wait(&s->unix_handle); } while (i == -1 && errno == EINTR); } + zpl_i32 zpl_semaphore_trywait(zpl_semaphore *s) { int r = sem_trywait(&s->unix_handle); return r; } + + #else + # error Semaphores for this OS are not implemented + #endif + + ZPL_END_C_DECLS + // file: source/threading/mutex.c + + + ZPL_BEGIN_C_DECLS + + zpl_b32 zpl_mutex_init(zpl_mutex *m) { + # if defined(ZPL_SYSTEM_WINDOWS) + InitializeCriticalSection((CRITICAL_SECTION*)m->win32_critical_section); + return 1; + # else + return pthread_mutex_init(&m->pthread_mutex, NULL); + # endif + } + + zpl_b32 zpl_mutex_destroy(zpl_mutex *m) { + # if defined(ZPL_SYSTEM_WINDOWS) + DeleteCriticalSection((CRITICAL_SECTION*)m->win32_critical_section); + return 1; + # else + return pthread_mutex_destroy(&m->pthread_mutex); + # endif + } + + void zpl_mutex_lock(zpl_mutex *m) { + # if defined(ZPL_SYSTEM_WINDOWS) + EnterCriticalSection((CRITICAL_SECTION*)m->win32_critical_section); + # else + pthread_mutex_lock(&m->pthread_mutex); + # endif + } + + zpl_b32 zpl_mutex_try_lock(zpl_mutex *m) { + # if defined(ZPL_SYSTEM_WINDOWS) + return TryEnterCriticalSection((CRITICAL_SECTION*)m->win32_critical_section); + # else + return pthread_mutex_trylock(&m->pthread_mutex); + # endif + } + + void zpl_mutex_unlock(zpl_mutex *m) { + # if defined(ZPL_SYSTEM_WINDOWS) + LeaveCriticalSection((CRITICAL_SECTION*)m->win32_critical_section); + # else + pthread_mutex_unlock(&m->pthread_mutex); + # endif + } + + ZPL_END_C_DECLS + // file: source/threading/thread.c + + + ZPL_BEGIN_C_DECLS + + zpl_b32 zpl_thread_is_running(zpl_thread const *t) { return t->is_running != 0; } + + void zpl_thread_init_nowait(zpl_thread *t) { + zpl_zero_item(t); + + # if defined(ZPL_SYSTEM_WINDOWS) + t->win32_handle = INVALID_HANDLE_VALUE; + # endif + + t->nowait = true; + } + + void zpl_thread_init(zpl_thread *t) { + zpl_thread_init_nowait(t); + + t->nowait = false; + zpl_semaphore_init(&t->semaphore); + } + + void zpl_thread_destroy(zpl_thread *t) { + # if defined(ZPL_SYSTEM_WINDOWS) + if (t->win32_handle != INVALID_HANDLE_VALUE) + zpl_thread_join(t); + # else + if (t->posix_handle) + zpl_thread_join(t); + # endif + if (!t->nowait) + zpl_semaphore_destroy(&t->semaphore); + } + + static void zpl__thread_run(zpl_thread *t) { + if (!t->nowait) + zpl_semaphore_release(&t->semaphore); + t->return_value = t->proc(t); + } + + #if defined(ZPL_SYSTEM_WINDOWS) + static DWORD __stdcall zpl__thread_proc(void *arg) { + zpl_thread *t = cast(zpl_thread *)arg; + t->is_running = true; + zpl__thread_run(t); + t->is_running = false; + return 0; + } + #else + static void *zpl__thread_proc(void *arg) { + zpl_thread *t = cast(zpl_thread *)arg; + t->is_running = true; + zpl__thread_run(t); + t->is_running = false; + return NULL; + } + #endif + + zpl_b32 zpl_thread_start(zpl_thread *t, zpl_thread_proc proc, void *user_data) { + return zpl_thread_start_with_stack(t, proc, user_data, 0); + } + + zpl_b32 zpl_thread_start_with_stack(zpl_thread *t, zpl_thread_proc proc, void *user_data, zpl_isize stack_size) { + ZPL_ASSERT(!t->is_running); + ZPL_ASSERT(proc != NULL); + t->proc = proc; + t->user_data = user_data; + t->stack_size = stack_size; + + # if defined(ZPL_SYSTEM_WINDOWS) + t->win32_handle = CreateThread(NULL, stack_size, zpl__thread_proc, t, 0, NULL); + ZPL_ASSERT_MSG(t->win32_handle != NULL, "CreateThread: GetLastError"); + # else + { + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); + if (stack_size != 0) + pthread_attr_setstacksize(&attr, stack_size); + zpl_b32 res = pthread_create(&t->posix_handle, &attr, zpl__thread_proc, t); + if (res != 0) { + return res; + } + pthread_attr_destroy(&attr); + } + # endif + if (!t->nowait) + zpl_semaphore_wait(&t->semaphore); + return 1; + } + + void zpl_thread_join(zpl_thread *t) { + # if defined(ZPL_SYSTEM_WINDOWS) + WaitForSingleObject(t->win32_handle, INFINITE); + CloseHandle(t->win32_handle); + t->win32_handle = INVALID_HANDLE_VALUE; + # else + pthread_join(t->posix_handle, NULL); + t->posix_handle = 0; + # endif + } + + zpl_u32 zpl_thread_current_id(void) { + zpl_u32 thread_id; + # if defined(ZPL_SYSTEM_WINDOWS) + # if defined(ZPL_ARCH_32_BIT) && defined(ZPL_CPU_X86) + thread_id = (cast(zpl_u32 *)__readfsdword(24))[9]; + # elif defined(ZPL_ARCH_64_BIT) && defined(ZPL_CPU_X86) + thread_id = (cast(zpl_u32 *)__readgsqword(48))[18]; + # else + thread_id = GetCurrentThreadId(); + # endif + + # elif defined(ZPL_SYSTEM_OSX) && defined(ZPL_ARCH_64_BIT) + thread_id = pthread_mach_thread_np(pthread_self()); + # elif defined(ZPL_ARCH_32_BIT) && defined(ZPL_CPU_X86) + __asm__("mov %%gs:0x08,%0" : "=r"(thread_id)); + # elif defined(ZPL_ARCH_64_BIT) && defined(ZPL_CPU_X86) + __asm__("mov %%fs:0x10,%0" : "=r"(thread_id)); + # elif defined(__ARM_ARCH) + thread_id = pthread_self(); + # else + # error Unsupported architecture for zpl_thread_current_id() + # endif + + return thread_id; + } + + void zpl_thread_set_name(zpl_thread *t, char const *name) { + # if defined(ZPL_COMPILER_MSVC) + # pragma pack(push, 8) + typedef struct { + DWORD type; + char const *name; + DWORD id; + DWORD flags; + } zplprivThreadName; + # pragma pack(pop) + + zplprivThreadName tn; + tn.type = 0x1000; + tn.name = name; + tn.id = GetThreadId(cast(HANDLE)t->win32_handle); + tn.flags = 0; + + __try { + RaiseException(0x406d1388, 0, zpl_size_of(tn)/4, cast(ULONG_PTR *)&tn); + } __except(1 /*EXCEPTION_EXECUTE_HANDLER*/) { + } + + # elif defined(ZPL_SYSTEM_WINDOWS) && !defined(ZPL_COMPILER_MSVC) + zpl_unused(t); + zpl_unused(name); + // IMPORTANT TODO: Set thread name for GCC/Clang on windows + return; + # elif defined(ZPL_SYSTEM_OSX) + // TODO: Test if this works + pthread_setname_np(name); + # else + zpl_unused(t); + zpl_unused(name); + // TODO: Test if this works + // pthread_set_name_np(t->posix_handle, name); + # endif + } + + ZPL_END_C_DECLS + // file: source/threading/sync.c + + + ZPL_BEGIN_C_DECLS + + void zpl_sync_init(zpl_sync *s) { + zpl_zero_item(s); + zpl_mutex_init(&s->mutex); + zpl_mutex_init(&s->start); + zpl_semaphore_init(&s->release); + } + + void zpl_sync_destroy(zpl_sync *s) { + if (s->waiting) { + ZPL_PANIC("Cannot destroy while threads are waiting!"); + } + + zpl_mutex_destroy(&s->mutex); + zpl_mutex_destroy(&s->start); + zpl_semaphore_destroy(&s->release); + } + + void zpl_sync_set_target(zpl_sync *s, zpl_i32 count) { + zpl_mutex_lock(&s->start); + + zpl_mutex_lock(&s->mutex); + ZPL_ASSERT(s->target == 0); + s->target = count; + s->current = 0; + s->waiting = 0; + zpl_mutex_unlock(&s->mutex); + } + + void zpl_sync_release(zpl_sync *s) { + if (s->waiting) { + zpl_semaphore_release(&s->release); + } else { + s->target = 0; + zpl_mutex_unlock(&s->start); + } + } + + zpl_i32 zpl_sync_reach(zpl_sync *s) { + zpl_i32 n; + zpl_mutex_lock(&s->mutex); + ZPL_ASSERT(s->current < s->target); + n = ++s->current; // NOTE: Record this value to avoid possible race if `return s->current` was done + if (s->current == s->target) + zpl_sync_release(s); + zpl_mutex_unlock(&s->mutex); + return n; + } + + void zpl_sync_reach_and_wait(zpl_sync *s) { + zpl_mutex_lock(&s->mutex); + ZPL_ASSERT(s->current < s->target); + s->current++; + if (s->current == s->target) { + zpl_sync_release(s); + zpl_mutex_unlock(&s->mutex); + } else { + s->waiting++; // NOTE: Waiting, so one more waiter + zpl_mutex_unlock(&s->mutex); // NOTE: Release the mutex to other threads + zpl_semaphore_wait(&s->release); // NOTE: Wait for merge completion + zpl_mutex_lock(&s->mutex); // NOTE: On merge completion, lock mutex + s->waiting--; // NOTE: Done waiting + zpl_sync_release(s); // NOTE: Restart the next waiter + zpl_mutex_unlock(&s->mutex); + } + } + + ZPL_END_C_DECLS + // file: source/threading/affinity.c + + + #if defined(ZPL_SYSTEM_MACOS) + # include + #endif + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_SYSTEM_WINDOWS) || defined(ZPL_SYSTEM_CYGWIN) + + void zpl_affinity_init(zpl_affinity *a) { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION *start_processor_info = NULL; + DWORD length = 0; + zpl_b32 result = GetLogicalProcessorInformation(NULL, &length); + + zpl_zero_item(a); + + if (!result && GetLastError() == 122l /*ERROR_INSUFFICIENT_BUFFER*/ && length > 0) { + start_processor_info = cast(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *)zpl_alloc(zpl_heap_allocator(), length); + result = GetLogicalProcessorInformation(start_processor_info, &length); + if (result) { + SYSTEM_LOGICAL_PROCESSOR_INFORMATION *end_processor_info, *processor_info; + + a->is_accurate = true; + a->core_count = 0; + a->thread_count = 0; + end_processor_info = cast(SYSTEM_LOGICAL_PROCESSOR_INFORMATION *)zpl_pointer_add(start_processor_info, length); + + for (processor_info = start_processor_info; + processor_info < end_processor_info; + processor_info++) { + if (processor_info->Relationship == RelationProcessorCore) { + zpl_isize thread = zpl_count_set_bits(processor_info->ProcessorMask); + if (thread == 0) { + a->is_accurate = false; + } else if (a->thread_count + thread > ZPL_WIN32_MAX_THREADS) { + a->is_accurate = false; + } else { + ZPL_ASSERT(a->core_count <= a->thread_count && + a->thread_count < ZPL_WIN32_MAX_THREADS); + a->core_masks[a->core_count++] = processor_info->ProcessorMask; + a->thread_count += thread; + } + } + } + } + + zpl_free(zpl_heap_allocator(), start_processor_info); + } + + ZPL_ASSERT(a->core_count <= a->thread_count); + if (a->thread_count == 0) { + a->is_accurate = false; + a->core_count = 1; + a->thread_count = 1; + a->core_masks[0] = 1; + } + + } + + void zpl_affinity_destroy(zpl_affinity *a) { + zpl_unused(a); + } + + zpl_b32 zpl_affinity_set(zpl_affinity *a, zpl_isize core, zpl_isize thread) { + zpl_usize available_mask, check_mask = 1; + ZPL_ASSERT(thread < zpl_affinity_thread_count_for_core(a, core)); + + available_mask = a->core_masks[core]; + for (;;) { + if ((available_mask & check_mask) != 0) { + if (thread-- == 0) { + zpl_usize result = SetThreadAffinityMask(GetCurrentThread(), check_mask); + return result != 0; + } + } + check_mask <<= 1; // NOTE: Onto the next bit + } + } + + zpl_isize zpl_affinity_thread_count_for_core(zpl_affinity *a, zpl_isize core) { + ZPL_ASSERT(core >= 0 && core < a->core_count); + return zpl_count_set_bits(a->core_masks[core]); + } + + #elif defined(ZPL_SYSTEM_MACOS) + void zpl_affinity_init(zpl_affinity *a) { + zpl_usize count, count_size = zpl_size_of(count); + + a->is_accurate = false; + a->thread_count = 1; + a->core_count = 1; + a->threads_per_core = 1; + + if (sysctlbyname("hw.logicalcpu", &count, &count_size, NULL, 0) == 0) { + if (count > 0) { + a->thread_count = count; + // Get # of physical cores + if (sysctlbyname("hw.physicalcpu", &count, &count_size, NULL, 0) == 0) { + if (count > 0) { + a->core_count = count; + a->threads_per_core = a->thread_count / count; + if (a->threads_per_core < 1) + a->threads_per_core = 1; + else + a->is_accurate = true; + } + } + } + } + + } + + void zpl_affinity_destroy(zpl_affinity *a) { + zpl_unused(a); + } + + zpl_b32 zpl_affinity_set(zpl_affinity *a, zpl_isize core, zpl_isize thread_index) { + zpl_isize index; + thread_t thread; + thread_affinity_policy_data_t info; + kern_return_t result; + + ZPL_ASSERT(core < a->core_count); + ZPL_ASSERT(thread_index < a->threads_per_core); + + index = core * a->threads_per_core + thread_index; + thread = mach_thread_self(); + info.affinity_tag = cast(integer_t)index; + result = thread_policy_set(thread, THREAD_AFFINITY_POLICY, cast(thread_policy_t)&info, THREAD_AFFINITY_POLICY_COUNT); + return result == KERN_SUCCESS; + } + + zpl_isize zpl_affinity_thread_count_for_core(zpl_affinity *a, zpl_isize core) { + ZPL_ASSERT(core >= 0 && core < a->core_count); + return a->threads_per_core; + } + + #elif defined(ZPL_SYSTEM_LINUX) || defined(ZPL_SYSTEM_FREEBSD) || defined(ZPL_SYSTEM_OPENBSD) + void zpl_affinity_init(zpl_affinity *a) { + a->core_count = sysconf(_SC_NPROCESSORS_ONLN); + a->threads_per_core = 1; + + a->is_accurate = a->core_count > 0; + a->core_count = a->is_accurate ? a->core_count : 1; + a->thread_count = a->core_count; + } + + void zpl_affinity_destroy(zpl_affinity *a) { + zpl_unused(a); + } + + zpl_b32 zpl_affinity_set(zpl_affinity * a, zpl_isize core, zpl_isize thread_index) { + zpl_unused(a); + zpl_unused(core); + zpl_unused(thread_index); + return true; + } + + zpl_isize zpl_affinity_thread_count_for_core(zpl_affinity *a, zpl_isize core) { + ZPL_ASSERT(0 <= core && core < a->core_count); + return a->threads_per_core; + } + + #elif defined(ZPL_SYSTEM_EMSCRIPTEN) + # error No affinity implementation for Emscripten + #else + # error TODO: Unknown system + #endif + + ZPL_END_C_DECLS + +# if defined(ZPL_MODULE_JOBS) + // file: source/jobs.c + + /////////////////////////////////////////////////////////////// + // + // Thread Pool + // + + ZPL_BEGIN_C_DECLS + + ZPL_RING_DEFINE(zpl__jobs_ring_, zpl_thread_job); + + zpl_global const zpl_u32 zpl__jobs_chances[ZPL_JOBS_MAX_PRIORITIES] = { + 2, 3, 5, 7, 11 + }; + + zpl_isize zpl__jobs_entry(struct zpl_thread *thread) { + zpl_thread_worker *tw = (zpl_thread_worker *)thread->user_data; + + for (;;) { + zpl_u32 status = zpl_atomic32_load(&tw->status); + + switch (status) { + case ZPL_JOBS_STATUS_READY: { + zpl_atomic32_store(&tw->status, ZPL_JOBS_STATUS_BUSY); + tw->job.proc(tw->job.data); + zpl_atomic32_compare_exchange(&tw->status, ZPL_JOBS_STATUS_BUSY, ZPL_JOBS_STATUS_WAITING); + + # ifdef ZPL_JOBS_DEBUG + ++tw->hits; + # endif + } break; + + case ZPL_JOBS_STATUS_WAITING: { + # ifdef ZPL_JOBS_DEBUG + ++tw->idle; + # endif + zpl_yield(); + } break; + + case ZPL_JOBS_STATUS_TERM: { + return 0; + } break; + } + } + + return 0; + } + + void zpl_jobs_init(zpl_jobs_system *pool, zpl_allocator a, zpl_u32 max_threads) { + zpl_jobs_init_with_limit(pool, a, max_threads, ZPL_JOBS_MAX_QUEUE); + } + + void zpl_jobs_init_with_limit(zpl_jobs_system *pool, zpl_allocator a, zpl_u32 max_threads, zpl_u32 max_jobs) { + zpl_jobs_system pool_ = { 0 }; + *pool = pool_; + + pool->alloc = a; + pool->max_threads = max_threads; + pool->max_jobs = max_jobs; + pool->counter = 0; + + zpl_buffer_init(pool->workers, a, max_threads); + + for (zpl_usize i = 0; i < ZPL_JOBS_MAX_PRIORITIES; ++i) { + zpl_thread_queue *q = &pool->queues[i]; + zpl__jobs_ring_init(&q->jobs, a, max_jobs); + q->chance = zpl__jobs_chances[i]; + } + + for (zpl_usize i = 0; i < max_threads; ++i) { + zpl_thread_worker worker_ = { 0 }; + zpl_thread_worker *tw = pool->workers + i; + *tw = worker_; + + zpl_thread_init(&tw->thread); + zpl_atomic32_store(&tw->status, ZPL_JOBS_STATUS_WAITING); + zpl_thread_start(&tw->thread, zpl__jobs_entry, (void *)tw); + } + } + + void zpl_jobs_free(zpl_jobs_system *pool) { + for (zpl_usize i = 0; i < pool->max_threads; ++i) { + zpl_thread_worker *tw = pool->workers + i; + + zpl_atomic32_store(&tw->status, ZPL_JOBS_STATUS_TERM); + zpl_thread_destroy(&tw->thread); + } + + zpl_buffer_free(pool->workers); + + for (zpl_usize i = 0; i < ZPL_JOBS_MAX_PRIORITIES; ++i) { + zpl_thread_queue *q = &pool->queues[i]; + zpl__jobs_ring_free(&q->jobs); + } + } + + zpl_b32 zpl_jobs_enqueue_with_priority(zpl_jobs_system *pool, zpl_jobs_proc proc, void *data, zpl_jobs_priority priority) { + ZPL_ASSERT(priority >= 0 && priority < ZPL_JOBS_MAX_PRIORITIES); + ZPL_ASSERT_NOT_NULL(proc); + zpl_thread_job job = {0}; + job.proc = proc; + job.data = data; + + if (!zpl_jobs_full(pool, priority)) { + zpl__jobs_ring_append(&pool->queues[priority].jobs, job); + return true; + } + return false; + } + + zpl_b32 zpl_jobs_enqueue(zpl_jobs_system *pool, zpl_jobs_proc proc, void *data) { + return zpl_jobs_enqueue_with_priority(pool, proc, data, ZPL_JOBS_PRIORITY_NORMAL); + } + + zpl_b32 zpl_jobs_empty(zpl_jobs_system *pool, zpl_jobs_priority priority) { + ZPL_ASSERT(priority >= 0 && priority < ZPL_JOBS_MAX_PRIORITIES); + return zpl__jobs_ring_empty(&pool->queues[priority].jobs); + } + + zpl_b32 zpl_jobs_full(zpl_jobs_system *pool, zpl_jobs_priority priority) { + ZPL_ASSERT(priority >= 0 && priority < ZPL_JOBS_MAX_PRIORITIES); + return zpl__jobs_ring_full(&pool->queues[priority].jobs); + } + + zpl_b32 zpl_jobs_done(zpl_jobs_system *pool) { + for (zpl_usize i = 0; i < pool->max_threads; ++i) { + zpl_thread_worker *tw = pool->workers + i; + if (zpl_atomic32_load(&tw->status) != ZPL_JOBS_STATUS_WAITING) { + return false; + } + } + + return zpl_jobs_empty_all(pool); + } + + zpl_b32 zpl_jobs_empty_all(zpl_jobs_system *pool) { + for (zpl_usize i = 0; i < ZPL_JOBS_MAX_PRIORITIES; ++i) { + if (!zpl_jobs_empty(pool, (zpl_jobs_priority)i)) { + return false; + } + } + return true; + } + + zpl_b32 zpl_jobs_full_all(zpl_jobs_system *pool) { + for (zpl_usize i = 0; i < ZPL_JOBS_MAX_PRIORITIES; ++i) { + if (!zpl_jobs_full(pool, (zpl_jobs_priority)i)) { + return false; + } + } + return true; + } + + zpl_b32 zpl_jobs_process(zpl_jobs_system *pool) { + if (zpl_jobs_empty_all(pool)) { + return false; + } + // NOTE: Process the jobs + for (zpl_usize i = 0; i < pool->max_threads; ++i) { + zpl_thread_worker *tw = pool->workers + i; + zpl_u32 status = zpl_atomic32_load(&tw->status); + zpl_b32 last_empty = false; + + if (status == ZPL_JOBS_STATUS_WAITING) { + for (zpl_usize j = 0; j < ZPL_JOBS_MAX_PRIORITIES; ++j) { + zpl_thread_queue *q = &pool->queues[j]; + if (zpl_jobs_empty(pool, (zpl_jobs_priority)j)) { + last_empty = (j+1 == ZPL_JOBS_MAX_PRIORITIES); + continue; + } + if (!last_empty && ((pool->counter++ % q->chance) != 0)) { + continue; + } + + last_empty = false; + tw->job = *zpl__jobs_ring_get(&q->jobs); + zpl_atomic32_store(&tw->status, ZPL_JOBS_STATUS_READY); + # ifdef ZPL_JOBS_DEBUG + ++q->hits; + # endif + break; + } + } + } + + return true; + } + + ZPL_END_C_DECLS +# endif +#endif + +#if defined(ZPL_MODULE_PARSER) + // file: source/adt.c + + ZPL_BEGIN_C_DECLS + + #define zpl__adt_fprintf(s_, fmt_, ...) \ + do { \ + if (zpl_fprintf(s_, fmt_, ##__VA_ARGS__) < 0) return ZPL_ADT_ERROR_OUT_OF_MEMORY; \ + } while (0) + + zpl_u8 zpl_adt_make_branch(zpl_adt_node *node, zpl_allocator backing, char const *name, zpl_b32 is_array) { + zpl_u8 type = ZPL_ADT_TYPE_OBJECT; + if (is_array) { + type = ZPL_ADT_TYPE_ARRAY; + } + zpl_adt_node *parent = node->parent; + zpl_zero_item(node); + node->type = type; + node->name = name; + node->parent = parent; + if (!zpl_array_init(node->nodes, backing)) + return ZPL_ADT_ERROR_OUT_OF_MEMORY; + return 0; + } + + zpl_u8 zpl_adt_destroy_branch(zpl_adt_node *node) { + ZPL_ASSERT_NOT_NULL(node); + if ((node->type == ZPL_ADT_TYPE_OBJECT || node->type == ZPL_ADT_TYPE_ARRAY) && node->nodes) { + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); ++i) { zpl_adt_destroy_branch(node->nodes + i); } + + zpl_array_free(node->nodes); + } + return 0; + } + + zpl_u8 zpl_adt_make_leaf(zpl_adt_node *node, char const *name, zpl_u8 type) { + ZPL_ASSERT(type != ZPL_ADT_TYPE_OBJECT && type != ZPL_ADT_TYPE_ARRAY); + zpl_adt_node *parent = node->parent; + zpl_zero_item(node); + node->type = type; + node->name = name; + node->parent = parent; + return 0; + } + + zpl_adt_node *zpl_adt_find(zpl_adt_node *node, char const *name, zpl_b32 deep_search) { + if (node->type != ZPL_ADT_TYPE_OBJECT) { + return NULL; + } + + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); i++) { + if (!zpl_strcmp(node->nodes[i].name, name)) { + return (node->nodes + i); + } + } + + if (deep_search) { + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); i++) { + zpl_adt_node *res = zpl_adt_find(node->nodes + i, name, deep_search); + + if (res != NULL) + return res; + } + } + + return NULL; + } + + zpl_internal zpl_adt_node *zpl__adt_get_value(zpl_adt_node *node, char const *value) { + switch (node->type) { + case ZPL_ADT_TYPE_MULTISTRING: + case ZPL_ADT_TYPE_STRING: { + if (node->string && !zpl_strcmp(node->string, value)) { + return node; + } + } break; + case ZPL_ADT_TYPE_INTEGER: + case ZPL_ADT_TYPE_REAL: { + char back[4096]={0}; + zpl_file tmp; + + /* allocate a file descriptor for a memory-mapped number to string conversion, input source buffer is not cloned, however. */ + zpl_file_stream_open(&tmp, zpl_heap(), (zpl_u8*)back, zpl_size_of(back), ZPL_FILE_STREAM_WRITABLE); + zpl_adt_print_number(&tmp, node); + + zpl_isize fsize=0; + zpl_u8* buf = zpl_file_stream_buf(&tmp, &fsize); + + if (!zpl_strcmp((char const *)buf, value)) { + zpl_file_close(&tmp); + return node; + } + + zpl_file_close(&tmp); + } break; + default: break; /* node doesn't support value based lookup */ + } + + return NULL; + } + + zpl_internal zpl_adt_node *zpl__adt_get_field(zpl_adt_node *node, char *name, char *value) { + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); i++) { + if (!zpl_strcmp(node->nodes[i].name, name)) { + zpl_adt_node *child = &node->nodes[i]; + if (zpl__adt_get_value(child, value)) { + return node; /* this object does contain a field of a specified value! */ + } + } + } + + return NULL; + } + + zpl_adt_node *zpl_adt_query(zpl_adt_node *node, char const *uri) { + ZPL_ASSERT_NOT_NULL(uri); + + if (*uri == '/') { + uri++; + } + + if (*uri == 0) { + return node; + } + + if (!node || (node->type != ZPL_ADT_TYPE_OBJECT && node->type != ZPL_ADT_TYPE_ARRAY)) { + return NULL; + } + + #if defined ZPL_ADT_URI_DEBUG || 0 + zpl_printf("uri: %s\n", uri); + #endif + + char *p=(char*)uri, *b=p, *e=p; + zpl_adt_node *found_node=NULL; + + b = p; + p = e = (char*)zpl_str_skip(p, '/'); + char *buf = zpl_bprintf("%.*s", (int)(e - b), b); + + /* handle field value lookup */ + if (*b == '[') { + char *l_p=buf+1,*l_b=l_p,*l_e=l_p,*l_b2=l_p,*l_e2=l_p; + l_e = (char*)zpl_str_skip(l_p, '='); + l_e2 = (char*)zpl_str_skip(l_p, ']'); + + if ((!*l_e && node->type != ZPL_ADT_TYPE_ARRAY) || !*l_e2) { + ZPL_ASSERT_MSG(0, "Invalid field value lookup"); + return NULL; + } + + *l_e2 = 0; + + /* [field=value] */ + if (*l_e) { + *l_e = 0; + l_b2 = l_e+1; + + /* run a value comparison against our own fields */ + if (node->type == ZPL_ADT_TYPE_OBJECT) { + found_node = zpl__adt_get_field(node, l_b, l_b2); + } + + /* run a value comparison against any child that is an object node */ + else if (node->type == ZPL_ADT_TYPE_ARRAY) { + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); i++) { + zpl_adt_node *child = &node->nodes[i]; + if (child->type != ZPL_ADT_TYPE_OBJECT) { + continue; + } + + found_node = zpl__adt_get_field(child, l_b, l_b2); + + if (found_node) + break; + } + } + } + /* [value] */ + else { + for (zpl_isize i = 0; i < zpl_array_count(node->nodes); i++) { + zpl_adt_node *child = &node->nodes[i]; + if (zpl__adt_get_value(child, l_b2)) { + found_node = child; + break; /* we found a matching value in array, ignore the rest of it */ + } + } + } + + /* go deeper if uri continues */ + if (*e) { + return zpl_adt_query(found_node, e+1); + } + } + /* handle field name lookup */ + else if (node->type == ZPL_ADT_TYPE_OBJECT) { + found_node = zpl_adt_find(node, buf, false); + + /* go deeper if uri continues */ + if (*e) { + return zpl_adt_query(found_node, e+1); + } + } + /* handle array index lookup */ + else { + zpl_isize idx = (zpl_isize)zpl_str_to_i64(buf, NULL, 10); + if (idx >= 0 && idx < zpl_array_count(node->nodes)) { + found_node = &node->nodes[idx]; + + /* go deeper if uri continues */ + if (*e) { + return zpl_adt_query(found_node, e+1); + } + } + } + + return found_node; + } + + zpl_adt_node *zpl_adt_alloc_at(zpl_adt_node *parent, zpl_isize index) { + if (!parent || (parent->type != ZPL_ADT_TYPE_OBJECT && parent->type != ZPL_ADT_TYPE_ARRAY)) { + return NULL; + } + + if (!parent->nodes) + return NULL; + + if (index < 0 || index > zpl_array_count(parent->nodes)) + return NULL; + + zpl_adt_node o = {0}; + o.parent = parent; + if (!zpl_array_append_at(parent->nodes, o, index)) + return NULL; + + return parent->nodes + index; + } + + zpl_adt_node *zpl_adt_alloc(zpl_adt_node *parent) { + if (!parent || (parent->type != ZPL_ADT_TYPE_OBJECT && parent->type != ZPL_ADT_TYPE_ARRAY)) { + return NULL; + } + + if (!parent->nodes) + return NULL; + + return zpl_adt_alloc_at(parent, zpl_array_count(parent->nodes)); + } + + + zpl_b8 zpl_adt_set_obj(zpl_adt_node *obj, char const *name, zpl_allocator backing) { + return zpl_adt_make_branch(obj, backing, name, 0); + } + zpl_b8 zpl_adt_set_arr(zpl_adt_node *obj, char const *name, zpl_allocator backing) { + return zpl_adt_make_branch(obj, backing, name, 1); + } + zpl_b8 zpl_adt_set_str(zpl_adt_node *obj, char const *name, char const *value) { + zpl_adt_make_leaf(obj, name, ZPL_ADT_TYPE_STRING); + obj->string = value; + return true; + } + zpl_b8 zpl_adt_set_flt(zpl_adt_node *obj, char const *name, zpl_f64 value) { + zpl_adt_make_leaf(obj, name, ZPL_ADT_TYPE_REAL); + obj->real = value; + return true; + } + zpl_b8 zpl_adt_set_int(zpl_adt_node *obj, char const *name, zpl_i64 value) { + zpl_adt_make_leaf(obj, name, ZPL_ADT_TYPE_INTEGER); + obj->integer = value; + return true; + } + + zpl_adt_node *zpl_adt_move_node_at(zpl_adt_node *node, zpl_adt_node *new_parent, zpl_isize index) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(new_parent); + zpl_adt_node *old_parent = node->parent; + zpl_adt_node *new_node = zpl_adt_alloc_at(new_parent, index); + *new_node = *node; + new_node->parent = new_parent; + if (old_parent) { + zpl_adt_remove_node(node); + } + return new_node; + } + + zpl_adt_node *zpl_adt_move_node(zpl_adt_node *node, zpl_adt_node *new_parent) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(new_parent); + ZPL_ASSERT(new_parent->type == ZPL_ADT_TYPE_ARRAY || new_parent->type == ZPL_ADT_TYPE_OBJECT); + return zpl_adt_move_node_at(node, new_parent, zpl_array_count(new_parent->nodes)); + } + + void zpl_adt_swap_nodes(zpl_adt_node *node, zpl_adt_node *other_node) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(other_node); + zpl_adt_node *parent = node->parent; + zpl_adt_node *other_parent = other_node->parent; + zpl_isize index = (zpl_pointer_diff(parent->nodes, node) / zpl_size_of(zpl_adt_node)); + zpl_isize index2 = (zpl_pointer_diff(other_parent->nodes, other_node) / zpl_size_of(zpl_adt_node)); + zpl_adt_node temp = parent->nodes[index]; + temp.parent = other_parent; + other_parent->nodes[index2].parent = parent; + parent->nodes[index] = other_parent->nodes[index2]; + other_parent->nodes[index2] = temp; + } + + void zpl_adt_remove_node(zpl_adt_node *node) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(node->parent); + zpl_adt_node *parent = node->parent; + zpl_isize index = (zpl_pointer_diff(parent->nodes, node) / zpl_size_of(zpl_adt_node)); + zpl_array_remove_at(parent->nodes, index); + } + + + zpl_adt_node *zpl_adt_append_obj(zpl_adt_node *parent, char const *name) { + zpl_adt_node *o = zpl_adt_alloc(parent); + if (!o) return NULL; + if (zpl_adt_set_obj(o, name, ZPL_ARRAY_HEADER(parent->nodes)->allocator)) { + zpl_adt_remove_node(o); + return NULL; + } + return o; + } + zpl_adt_node *zpl_adt_append_arr(zpl_adt_node *parent, char const *name) { + zpl_adt_node *o = zpl_adt_alloc(parent); + if (!o) return NULL; + if (zpl_adt_set_arr(o, name, ZPL_ARRAY_HEADER(parent->nodes)->allocator)) { + zpl_adt_remove_node(o); + return NULL; + } + return o; + } + zpl_adt_node *zpl_adt_append_str(zpl_adt_node *parent, char const *name, char const *value) { + zpl_adt_node *o = zpl_adt_alloc(parent); + if (!o) return NULL; + zpl_adt_set_str(o, name, value); + return o; + } + zpl_adt_node *zpl_adt_append_flt(zpl_adt_node *parent, char const *name, zpl_f64 value) { + zpl_adt_node *o = zpl_adt_alloc(parent); + if (!o) return NULL; + zpl_adt_set_flt(o, name, value); + return o; + } + zpl_adt_node *zpl_adt_append_int(zpl_adt_node *parent, char const *name, zpl_i64 value) { + zpl_adt_node *o = zpl_adt_alloc(parent); + if (!o) return NULL; + zpl_adt_set_int(o, name, value); + return o; + } + + /* parser helpers */ + char *zpl_adt_parse_number_strict(zpl_adt_node *node, char* base_str) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(base_str); + char *p = base_str, *e = p; + + while (*e) + ++e; + + while (*p && (zpl_strchr("eE.+-", *p) || zpl_char_is_hex_digit(*p))) { + ++p; + } + + if (p >= e) { + return zpl_adt_parse_number(node, base_str); + } + + return base_str; + } + + char *zpl_adt_parse_number(zpl_adt_node *node, char* base_str) { + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(base_str); + char *p = base_str, *e = p; + + zpl_i32 base=0; + zpl_i32 base2=0; + zpl_u8 base2_offset=0; + zpl_i8 exp=0,orig_exp=0; + zpl_u8 neg_zero=0; + zpl_u8 lead_digit=0; + zpl_u8 node_type=0; + zpl_u8 node_props=0; + + /* skip false positives and special cases */ + if (!!zpl_strchr("eE", *p) || (!!zpl_strchr(".+-", *p) && !zpl_char_is_hex_digit(*(p+1)) && *(p+1) != '.')) { + return ++base_str; + } + + node_type = ZPL_ADT_TYPE_INTEGER; + neg_zero = false; + + zpl_isize ib = 0; + char buf[48] = { 0 }; + + if (*e == '+') + ++e; + else if (*e == '-') { + buf[ib++] = *e++; + } + + if (*e == '.') { + node_type = ZPL_ADT_TYPE_REAL; + node_props = ZPL_ADT_PROPS_IS_PARSED_REAL; + lead_digit = false; + buf[ib++] = '0'; + do { + buf[ib++] = *e; + } while (zpl_char_is_digit(*++e)); + } else { + if (!zpl_strncmp(e, "0x", 2) || !zpl_strncmp(e, "0X", 2)) { node_props = ZPL_ADT_PROPS_IS_HEX; } + + /* bail if ZPL_ADT_PROPS_IS_HEX is unset but we get 'x' on input */ + if (zpl_char_to_lower(*e) == 'x' && (node_props != ZPL_ADT_PROPS_IS_HEX)) { + return ++base_str; + } + + while (zpl_char_is_hex_digit(*e) || zpl_char_to_lower(*e) == 'x') { buf[ib++] = *e++; } + + if (*e == '.') { + node_type = ZPL_ADT_TYPE_REAL; + lead_digit = true; + zpl_u32 step = 0; + + do { + buf[ib++] = *e; + ++step; + } while (zpl_char_is_digit(*++e)); + + if (step < 2) { buf[ib++] = '0'; } + } + } + + /* check if we have a dot here, this is a false positive (IP address, ...) */ + if (*e == '.') { + return ++base_str; + } + + zpl_f32 eb = 10; + char expbuf[6] = { 0 }; + zpl_isize expi = 0; + + if (*e && !!zpl_strchr("eE", *e)) { + ++e; + if (*e == '+' || *e == '-' || zpl_char_is_digit(*e)) { + if (*e == '-') { eb = 0.1f; } + if (!zpl_char_is_digit(*e)) { ++e; } + while (zpl_char_is_digit(*e)) { expbuf[expi++] = *e++; } + } + + orig_exp = exp = (zpl_u8)zpl_str_to_i64(expbuf, NULL, 10); + } + + if (node_type == ZPL_ADT_TYPE_INTEGER) { + node->integer = zpl_str_to_i64(buf, 0, 0); + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + /* special case: negative zero */ + if (node->integer == 0 && buf[0] == '-') { + neg_zero = true; + } + #endif + while (orig_exp-- > 0) { node->integer *= (zpl_i64)eb; } + } else { + node->real = zpl_str_to_f64(buf, 0); + + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + char *q = buf, *base_string = q, *base_string2 = q; + base_string = cast(char *)zpl_str_skip(base_string, '.'); + *base_string = '\0'; + base_string2 = base_string + 1; + char *base_string_off = base_string2; + while (*base_string_off++ == '0') base2_offset++; + + base = (zpl_i32)zpl_str_to_i64(q, 0, 0); + base2 = (zpl_i32)zpl_str_to_i64(base_string2, 0, 0); + if (exp) { + exp = exp * (!(eb == 10.0f) ? -1 : 1); + node_props = ZPL_ADT_PROPS_IS_EXP; + } + + /* special case: negative zero */ + if (base == 0 && buf[0] == '-') { + neg_zero = true; + } + #endif + while (orig_exp-- > 0) { node->real *= eb; } + } + + node->type = node_type; + node->props = node_props; + + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + node->base = base; + node->base2 = base2; + node->base2_offset = base2_offset; + node->exp = exp; + node->neg_zero = neg_zero; + node->lead_digit = lead_digit; + #else + zpl_unused(base); + zpl_unused(base2); + zpl_unused(base2_offset); + zpl_unused(exp); + zpl_unused(neg_zero); + zpl_unused(lead_digit); + #endif + return e; + } + + zpl_adt_error zpl_adt_print_number(zpl_file *file, zpl_adt_node *node) { + ZPL_ASSERT_NOT_NULL(file); + ZPL_ASSERT_NOT_NULL(node); + if (node->type != ZPL_ADT_TYPE_INTEGER && node->type != ZPL_ADT_TYPE_REAL) { + return ZPL_ADT_ERROR_INVALID_TYPE; + } + + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (node->neg_zero) { + zpl__adt_fprintf(file, "-"); + } + #endif + + switch (node->type) { + case ZPL_ADT_TYPE_INTEGER: { + if (node->props == ZPL_ADT_PROPS_IS_HEX) { + zpl__adt_fprintf(file, "0x%llx", (long long)node->integer); + } else { + zpl__adt_fprintf(file, "%lld", (long long)node->integer); + } + } break; + + case ZPL_ADT_TYPE_REAL: { + if (node->props == ZPL_ADT_PROPS_NAN) { + zpl__adt_fprintf(file, "NaN"); + } else if (node->props == ZPL_ADT_PROPS_NAN_NEG) { + zpl__adt_fprintf(file, "-NaN"); + } else if (node->props == ZPL_ADT_PROPS_INFINITY) { + zpl__adt_fprintf(file, "Infinity"); + } else if (node->props == ZPL_ADT_PROPS_INFINITY_NEG) { + zpl__adt_fprintf(file, "-Infinity"); + } else if (node->props == ZPL_ADT_PROPS_TRUE) { + zpl__adt_fprintf(file, "true"); + } else if (node->props == ZPL_ADT_PROPS_FALSE) { + zpl__adt_fprintf(file, "false"); + } else if (node->props == ZPL_ADT_PROPS_NULL) { + zpl__adt_fprintf(file, "null"); + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + } else if (node->props == ZPL_ADT_PROPS_IS_EXP) { + zpl__adt_fprintf(file, "%lld.%0*d%llde%lld", (long long)node->base, node->base2_offset, 0, (long long)node->base2, (long long)node->exp); + } else if (node->props == ZPL_ADT_PROPS_IS_PARSED_REAL) { + if (!node->lead_digit) + zpl__adt_fprintf(file, ".%0*d%lld", node->base2_offset, 0, (long long)node->base2); + else + zpl__adt_fprintf(file, "%lld.%0*d%lld", (long long int)node->base2_offset, 0, (int)node->base, (long long)node->base2); + #endif + } else { + zpl__adt_fprintf(file, "%f", node->real); + } + } break; + } + + return ZPL_ADT_ERROR_NONE; + } + + zpl_adt_error zpl_adt_print_string(zpl_file *file, zpl_adt_node *node, char const *escaped_chars, char const *escape_symbol) { + ZPL_ASSERT_NOT_NULL(file); + ZPL_ASSERT_NOT_NULL(node); + ZPL_ASSERT_NOT_NULL(escaped_chars); + if (node->type != ZPL_ADT_TYPE_STRING && node->type != ZPL_ADT_TYPE_MULTISTRING) { + return ZPL_ADT_ERROR_INVALID_TYPE; + } + + /* escape string */ + char const* p = node->string, *b = p; + + if (!p) + return ZPL_ADT_ERROR_NONE; + + do { + p = zpl_str_skip_any(p, escaped_chars); + zpl__adt_fprintf(file, "%.*s", zpl_ptr_diff(b, p), b); + if (*p && !!zpl_strchr(escaped_chars, *p)) { + zpl__adt_fprintf(file, "%s%c", escape_symbol, *p); + p++; + } + b = p; + } while (*p); + + return ZPL_ADT_ERROR_NONE; + } + + zpl_adt_error zpl_adt_str_to_number(zpl_adt_node *node) { + ZPL_ASSERT(node); + + if (node->type == ZPL_ADT_TYPE_REAL || node->type == ZPL_ADT_TYPE_INTEGER) return ZPL_ADT_ERROR_ALREADY_CONVERTED; /* this is already converted/parsed */ + if (node->type != ZPL_ADT_TYPE_STRING && node->type != ZPL_ADT_TYPE_MULTISTRING) { + return ZPL_ADT_ERROR_INVALID_TYPE; + } + + zpl_adt_parse_number(node, (char *)node->string); + + return ZPL_ADT_ERROR_NONE; + } + + zpl_adt_error zpl_adt_str_to_number_strict(zpl_adt_node *node) { + ZPL_ASSERT(node); + + if (node->type == ZPL_ADT_TYPE_REAL || node->type == ZPL_ADT_TYPE_INTEGER) return ZPL_ADT_ERROR_ALREADY_CONVERTED; /* this is already converted/parsed */ + if (node->type != ZPL_ADT_TYPE_STRING && node->type != ZPL_ADT_TYPE_MULTISTRING) { + return ZPL_ADT_ERROR_INVALID_TYPE; + } + + zpl_adt_parse_number_strict(node, (char *)node->string); + + return ZPL_ADT_ERROR_NONE; + } + + #undef zpl__adt_fprintf + + ZPL_END_C_DECLS + + /* parsers */ + // file: source/parsers/json.c + + //////////////////////////////////////////////////////////////// + // + // JSON5 Parser + // + // + + + #ifdef ZPL_JSON_DEBUG + #define ZPL_JSON_ASSERT(msg) ZPL_PANIC(msg) + #else + #define ZPL_JSON_ASSERT(msg) + #endif + + ZPL_BEGIN_C_DECLS + + char *zpl__json_parse_object(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code); + char *zpl__json_parse_array(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code); + char *zpl__json_parse_value(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code); + char *zpl__json_parse_name(zpl_adt_node *obj, char *base, zpl_u8 *err_code); + char *zpl__json_trim(char *base, zpl_b32 catch_newline); + zpl_b8 zpl__json_write_value(zpl_file *f, zpl_adt_node *o, zpl_adt_node *t, zpl_isize indent, zpl_b32 is_inline, zpl_b32 is_last); + + #define zpl__json_fprintf(s_, fmt_, ...) \ + do { \ + if (zpl_fprintf(s_, fmt_, ##__VA_ARGS__) < 0) return false; \ + } while (0) + + #define zpl___ind(x) if (x > 0) zpl__json_fprintf(f, "%*r", x, ' '); + + zpl_u8 zpl_json_parse(zpl_adt_node *root, char *text, zpl_allocator a) { + zpl_u8 err_code = ZPL_JSON_ERROR_NONE; + ZPL_ASSERT(root); + ZPL_ASSERT(text); + zpl_zero_item(root); + text = zpl__json_trim(text, true); + + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (!zpl_strchr("{[", *text)) { + root->cfg_mode = true; + } + #endif + + zpl__json_parse_object(root, text, a, &err_code); + return err_code; + } + + void zpl_json_free(zpl_adt_node *obj) { + zpl_adt_destroy_branch(obj); + } + + zpl_string zpl_json_write_string(zpl_allocator a, zpl_adt_node *obj, zpl_isize indent) { + zpl_file tmp; + if (!zpl_file_stream_new(&tmp, a)) + return NULL; + if (!zpl_json_write(&tmp, obj, indent)) + return NULL; + zpl_isize fsize; + zpl_u8* buf = zpl_file_stream_buf(&tmp, &fsize); + zpl_string output = zpl_string_make_length(a, (char *)buf, fsize); + zpl_file_close(&tmp); + return output; + } + + /* private */ + + #define zpl__json_append_node(x, item)\ + do {\ + if (!zpl_array_append(x, item)) {\ + *err_code = ZPL_JSON_ERROR_OUT_OF_MEMORY;\ + return NULL;\ + }\ + if (item.type == ZPL_ADT_TYPE_OBJECT || item.type == ZPL_ADT_TYPE_ARRAY) {\ + for (zpl_isize i = 0; i < zpl_array_count(item.nodes); i++)\ + item.nodes[i].parent = zpl_array_end(x);\ + }\ + } while (0); + + static ZPL_ALWAYS_INLINE zpl_b32 zpl__json_is_assign_char(char c) { return !!zpl_strchr(":=|", c); } + static ZPL_ALWAYS_INLINE zpl_b32 zpl__json_is_delim_char(char c) { return !!zpl_strchr(",|\n", c); } + static ZPL_ALWAYS_INLINE const char *zpl__json_string_space(zpl_isize indent) { return indent == ZPL_JSON_INDENT_STYLE_COMPACT ? "" : " "; } + static ZPL_ALWAYS_INLINE const char *zpl__json_string_eol(zpl_adt_node *o, zpl_isize indent) { + zpl_b8 force_new_line = false; + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + force_new_line = (o->delim_style != ZPL_ADT_DELIM_STYLE_COMMA); + #endif + return !force_new_line && indent == ZPL_JSON_INDENT_STYLE_COMPACT ? "" : "\n"; + } + ZPL_DEF_INLINE zpl_b32 zpl__json_validate_name(char const *str, char *err); + + #define jx(x) !zpl_char_is_hex_digit(str[x]) + ZPL_IMPL_INLINE zpl_b32 zpl__json_validate_name(char const *str, char *err) { + while (*str) { + /* todo: refactor name validation. */ + if ((str[0] == '\\' && !zpl_char_is_control(str[1])) && + (str[0] == '\\' && jx(1) && jx(2) && jx(3) && jx(4))) { + if (err) *err = *str; + return false; + } + + ++str; + } + + return true; + } + #undef jx + + char *zpl__json_parse_array(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code) { + ZPL_ASSERT(obj && base); + char *p = base; + + obj->type = ZPL_ADT_TYPE_ARRAY; + if (!zpl_array_init(obj->nodes, a)) { + *err_code = ZPL_JSON_ERROR_OUT_OF_MEMORY; + return NULL; + } + + while (*p) { + p = zpl__json_trim(p, false); + + if (*p == ']') { + return p; + } + + zpl_adt_node elem = { 0 }; + p = zpl__json_parse_value(&elem, p, a, err_code); + + if (*err_code != ZPL_JSON_ERROR_NONE) { return NULL; } + + zpl__json_append_node(obj->nodes, elem); + + p = zpl__json_trim(p, false); + + if (*p == ',') { + ++p; + continue; + } else { + if (*p != ']') { + ZPL_JSON_ASSERT("end of array unfulfilled"); + *err_code = ZPL_JSON_ERROR_ARRAY_LEFT_OPEN; + return NULL; + } + return p; + } + } + + *err_code = ZPL_JSON_ERROR_INTERNAL; + return NULL; + } + + char *zpl__json_parse_value(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code) { + ZPL_ASSERT(obj && base); + char *p = base, *b = p, *e = p; + + /* handle quoted strings */ + if (!!zpl_strchr("`\"'", *p)) { + char c = *p; + obj->type = (c == '`') ? ZPL_ADT_TYPE_MULTISTRING : ZPL_ADT_TYPE_STRING; + b = e = p + 1; + obj->string = b; + e = cast(char *)zpl_str_skip_literal(e, c); + *e = '\0', p = e + 1; + } else if (zpl_char_is_alpha(*p) || (*p == '-' && !zpl_char_is_digit(*(p + 1)))) { + /* handle constants */ + if (zpl_str_has_prefix(p, "true")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->props = ZPL_ADT_PROPS_TRUE; + obj->real = 1; + p += 4; + } else if (zpl_str_has_prefix(p, "false")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->props = ZPL_ADT_PROPS_FALSE; + obj->real = 0; + p += 5; + } else if (zpl_str_has_prefix(p, "null")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->props = ZPL_ADT_PROPS_NULL; + obj->real = 0; + p += 4; + } else if (zpl_str_has_prefix(p, "Infinity")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->real = ZPL_INFINITY; + obj->props = ZPL_ADT_PROPS_INFINITY; + p += 8; + } else if (zpl_str_has_prefix(p, "-Infinity")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->real = -ZPL_INFINITY; + obj->props = ZPL_ADT_PROPS_INFINITY_NEG; + p += 9; + } else if (zpl_str_has_prefix(p, "NaN")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->real = ZPL_NAN; + obj->props = ZPL_ADT_PROPS_NAN; + p += 3; + } else if (zpl_str_has_prefix(p, "-NaN")) { + obj->type = ZPL_ADT_TYPE_REAL; + obj->real = -ZPL_NAN; + obj->props = ZPL_ADT_PROPS_NAN_NEG; + p += 4; + } else { + ZPL_JSON_ASSERT("unknown keyword"); + *err_code = ZPL_JSON_ERROR_UNKNOWN_KEYWORD; + return NULL; + } + } else if (zpl_char_is_digit(*p) || *p == '+' || *p == '-' || *p == '.') { + /* handle numbers */ + /* defer operation to our helper method. */ + p = zpl_adt_parse_number(obj, p); + } else if (!!zpl_strchr("[{", *p)) { + /* handle compound objects */ + p = zpl__json_parse_object(obj, p, a, err_code); + ++p; + } + + return p; + } + + char *zpl__json_parse_object(zpl_adt_node *obj, char *base, zpl_allocator a, zpl_u8 *err_code) { + ZPL_ASSERT(obj && base); + char *p = base; + + p = zpl__json_trim(p, false); + /**/ if (*p == '{') { ++p; } + else if (*p == '[') { /* special case for when we call this func on an array. */ + ++p; + obj->type = ZPL_ADT_TYPE_ARRAY; + return zpl__json_parse_array(obj, p, a, err_code); + } + + if (!zpl_array_init(obj->nodes, a)) { + *err_code = ZPL_JSON_ERROR_OUT_OF_MEMORY; + return NULL; + } + obj->type = ZPL_ADT_TYPE_OBJECT; + + do { + zpl_adt_node node = { 0 }; + p = zpl__json_trim(p, false); + if (*p == '}' && obj->type == ZPL_ADT_TYPE_OBJECT) return p; + else if (*p == ']' && obj->type == ZPL_ADT_TYPE_ARRAY) return p; + else if (!!zpl_strchr("}]", *p)) { + ZPL_JSON_ASSERT("mismatched end pair"); + *err_code = ZPL_JSON_ERROR_OBJECT_END_PAIR_MISMATCHED; + return NULL; + } + + /* First, we parse the key, then we proceed to the value itself. */ + p = zpl__json_parse_name(&node, p, err_code); + if (err_code && *err_code != ZPL_JSON_ERROR_NONE) { return NULL; } + p = zpl__json_trim(p + 1, false); + p = zpl__json_parse_value(&node, p, a, err_code); + if (err_code && *err_code != ZPL_JSON_ERROR_NONE) { return NULL; } + + zpl__json_append_node(obj->nodes, node); + + char *end_p = p; zpl_unused(end_p); + p = zpl__json_trim(p, true); + + /* this code analyses the keyvalue pair delimiter used in the packet. */ + if (zpl__json_is_delim_char(*p)) { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + zpl_adt_node *n = zpl_array_end(obj->nodes); + n->delim_style = ZPL_ADT_DELIM_STYLE_COMMA; + + if (*p == '\n') + n->delim_style = ZPL_ADT_DELIM_STYLE_NEWLINE; + else if (*p == '|') { + n->delim_style = ZPL_ADT_DELIM_STYLE_LINE; + n->delim_line_width = cast(zpl_u8)(p-end_p); + } + #endif + if (*p == 0) { + return p; + } + ++p; + } + p = zpl__json_trim(p, false); + } while (*p); + return p; + } + + char *zpl__json_parse_name(zpl_adt_node *node, char *base, zpl_u8 *err_code) { + char *p = base, *b = p, *e = p; + zpl_u8 name_style=0; + + if (*p == '"' || *p == '\'' || zpl_char_is_alpha(*p) || *p == '_' || *p == '$') { + if (*p == '"' || *p == '\'') { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (*p == '"') { + node->name_style = ZPL_ADT_NAME_STYLE_DOUBLE_QUOTE; + } else if (*p == '\'') { + node->name_style = ZPL_ADT_NAME_STYLE_SINGLE_QUOTE; + } + #endif + char c = *p; + b = ++p; + e = cast(char *)zpl_str_control_skip(b, c); + node->name = b; + + /* we can safely null-terminate here, since "e" points to the quote pair end. */ + *e++ = '\0'; + } + else { + b = e = p; + zpl_str_advance_while(e, *e && (zpl_char_is_alphanumeric(*e) || *e == '_') && !zpl_char_is_space(*e) && !zpl__json_is_assign_char(*e)); + node->name = b; + name_style = ZPL_ADT_NAME_STYLE_NO_QUOTES; + /* we defer null-termination as it can potentially wipe our assign char as well. */ + } + + char *assign_p = e; zpl_unused(assign_p); + p = zpl__json_trim(e, false); + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + node->assign_line_width = cast(zpl_u8)(p-assign_p); + #endif + + if (*p && !zpl__json_is_assign_char(*p)) { + ZPL_JSON_ASSERT("invalid assignment"); + *err_code = ZPL_JSON_ERROR_INVALID_ASSIGNMENT; + return NULL; + } + else + { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (*p == '=') + node->assign_style = ZPL_ADT_ASSIGN_STYLE_EQUALS; + else if (*p == '|') + node->assign_style = ZPL_ADT_ASSIGN_STYLE_LINE; + else node->assign_style = ZPL_ADT_ASSIGN_STYLE_COLON; + #endif + } + + /* since we already know the assign style, we can cut it here for unquoted names */ + if (name_style == ZPL_ADT_NAME_STYLE_NO_QUOTES && *e) { + *e = '\0'; + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + node->name_style = name_style; + #endif + } + } + + if (node->name && !zpl__json_validate_name(node->name, NULL)) { + ZPL_JSON_ASSERT("invalid name"); + *err_code = ZPL_JSON_ERROR_INVALID_NAME; + return NULL; + } + + return p; + } + + char *zpl__json_trim(char *base, zpl_b32 catch_newline) { + ZPL_ASSERT_NOT_NULL(base); + char *p = base; + do { + if (zpl_str_has_prefix(p, "//")) { + const char *e = zpl_str_skip(p, '\n'); + p += (e-p); + } + else if (zpl_str_has_prefix(p, "/*")) { + const char *e = zpl_str_skip(p+2, '*'); + if (*e && *(e+1) == '/') { + e+=2; /* advance past end comment block */ + p += (e-p); + } + } + else if (*p == '\n' && catch_newline) { + return p; + } + else if (!zpl_char_is_space(*p)) { + return p; + } + } while (*p++); + return NULL; + } + + zpl_b8 zpl_json_write(zpl_file *f, zpl_adt_node *o, zpl_isize indent) { + if (!o) + return true; + + ZPL_ASSERT(o->type == ZPL_ADT_TYPE_OBJECT || o->type == ZPL_ADT_TYPE_ARRAY); + + zpl___ind(indent - 4); + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (!o->cfg_mode) + #else + if (1) + #endif + zpl__json_fprintf(f, "%c%s", o->type == ZPL_ADT_TYPE_OBJECT ? '{' : '[', zpl__json_string_eol(o, indent)); + else + { + indent -= 4; + } + + if (o->nodes) { + zpl_isize cnt = zpl_array_count(o->nodes); + + for (int i = 0; i < cnt; ++i) { + if (!zpl__json_write_value(f, o->nodes + i, o, indent, false, !(i < cnt - 1))) return false; + } + } + + zpl___ind(indent); + + if (indent > 0) { + zpl__json_fprintf(f, "%c", o->type == ZPL_ADT_TYPE_OBJECT ? '}' : ']'); + } else { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (!o->cfg_mode) + #endif + zpl__json_fprintf(f, "%c%s", o->type == ZPL_ADT_TYPE_OBJECT ? '}' : ']', zpl__json_string_eol(o, indent)); + } + + return true; + } + + zpl_b8 zpl__json_write_value(zpl_file *f, zpl_adt_node *o, zpl_adt_node *t, zpl_isize indent, zpl_b32 is_inline, zpl_b32 is_last) { + zpl_adt_node *node = o; + if (indent != ZPL_JSON_INDENT_STYLE_COMPACT) indent += 4; + + if (!is_inline) { + zpl___ind(indent); + + if (t->type != ZPL_ADT_TYPE_ARRAY) { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + switch (node->name_style) { + case ZPL_ADT_NAME_STYLE_DOUBLE_QUOTE: { + zpl__json_fprintf(f, "\"%s\"", node->name); + } break; + + case ZPL_ADT_NAME_STYLE_SINGLE_QUOTE: { + zpl__json_fprintf(f, "\'%s\'", node->name); + } break; + + case ZPL_ADT_NAME_STYLE_NO_QUOTES: { + zpl__json_fprintf(f, "%s", node->name); + } break; + } + + if (o->assign_style == ZPL_ADT_ASSIGN_STYLE_COLON) + zpl__json_fprintf(f, ":%s", zpl__json_string_space(indent)); + else { + if (indent != ZPL_JSON_INDENT_STYLE_COMPACT) + zpl___ind(zpl_max(o->assign_line_width, 1)); + + if (o->assign_style == ZPL_ADT_ASSIGN_STYLE_EQUALS) + zpl__json_fprintf(f, "=%s", zpl__json_string_space(indent)); + else if (o->assign_style == ZPL_ADT_ASSIGN_STYLE_LINE) { + zpl__json_fprintf(f, "|%s", zpl__json_string_space(indent)); + } + } + #else + zpl__json_fprintf(f, "\"%s\":%s", node->name, zpl__json_string_space(indent)); + #endif + } + } + + switch (node->type) { + case ZPL_ADT_TYPE_STRING: { + zpl__json_fprintf(f, "\""); + if (zpl_adt_print_string(f, node, "\"", "\\")) return false; + zpl__json_fprintf(f, "\""); + } break; + + case ZPL_ADT_TYPE_MULTISTRING: { + zpl__json_fprintf(f, "`"); + if (zpl_adt_print_string(f, node, "`", "\\")) return false; + zpl__json_fprintf(f, "`"); + } break; + + case ZPL_ADT_TYPE_ARRAY: { + zpl__json_fprintf(f, "["); + zpl_isize elemn = zpl_array_count(node->nodes); + for (int j = 0; j < elemn; ++j) { + zpl_isize ind = ((node->nodes + j)->type == ZPL_ADT_TYPE_OBJECT || (node->nodes + j)->type == ZPL_ADT_TYPE_ARRAY) ? 0 : -4; + if (!zpl__json_write_value(f, node->nodes + j, o, indent == ZPL_JSON_INDENT_STYLE_COMPACT ? indent : ind, true, true)) return false; + + if (j < elemn - 1) { zpl__json_fprintf(f, ", "); } + } + zpl__json_fprintf(f, "]"); + } break; + + case ZPL_ADT_TYPE_REAL: + case ZPL_ADT_TYPE_INTEGER: { + if (zpl_adt_print_number(f, node)) return false; + } break; + + case ZPL_ADT_TYPE_OBJECT: { + if (!zpl_json_write(f, node, indent)) return false; + } break; + } + + if (!is_inline) { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + if (o->delim_style != ZPL_ADT_DELIM_STYLE_COMMA) { + if (o->delim_style == ZPL_ADT_DELIM_STYLE_NEWLINE) + zpl__json_fprintf(f, "\n"); + else if (o->delim_style == ZPL_ADT_DELIM_STYLE_LINE) { + zpl___ind(o->delim_line_width); + zpl__json_fprintf(f, "|\n"); + } + } + else { + if (!is_last) { + zpl__json_fprintf(f, ",%s", zpl__json_string_eol(o, indent)); + } else { + zpl__json_fprintf(f, "%s", zpl__json_string_eol(o, indent)); + } + } + #else + if (!is_last) { + zpl__json_fprintf(f, ",%s", zpl__json_string_eol(o, indent)); + } else { + zpl__json_fprintf(f, "%s", zpl__json_string_eol(o, indent)); + } + #endif + } + + return true; + } + + #undef zpl__json_fprintf + #undef zpl___ind + #undef zpl__json_append_node + + ZPL_END_C_DECLS + // file: source/parsers/csv.c + + + #ifdef ZPL_CSV_DEBUG + #define ZPL_CSV_ASSERT(msg) ZPL_PANIC(msg) + #else + #define ZPL_CSV_ASSERT(msg) + #endif + + + ZPL_BEGIN_C_DECLS + + zpl_u8 zpl_csv_parse_delimiter(zpl_csv_object *root, char *text, zpl_allocator allocator, zpl_b32 has_header, char delim) { + zpl_csv_error err = ZPL_CSV_ERROR_NONE; + ZPL_ASSERT_NOT_NULL(root); + ZPL_ASSERT_NOT_NULL(text); + zpl_zero_item(root); + zpl_adt_make_branch(root, allocator, NULL, has_header ? false : true); + char *p = text, *b = p, *e = p; + zpl_isize colc = 0, total_colc = 0; + + do { + char d = 0; + p = cast(char *)zpl_str_trim(p, false); + if (*p == 0) break; + zpl_adt_node row_item = {0}; + row_item.type = ZPL_ADT_TYPE_STRING; + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + row_item.name_style = ZPL_ADT_NAME_STYLE_NO_QUOTES; + #endif + + /* handle string literals */ + if (*p == '"') { + p = b = e = p+1; + row_item.string = b; + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + row_item.name_style = ZPL_ADT_NAME_STYLE_DOUBLE_QUOTE; + #endif + do { + e = cast(char *)zpl_str_skip(e, '"'); + if (*e && *(e+1) == '"') { + e += 2; + } + else break; + } while (*e); + if (*e == 0) { + ZPL_CSV_ASSERT("unmatched quoted string"); + err = ZPL_CSV_ERROR_UNEXPECTED_END_OF_INPUT; + return err; + } + *e = 0; + p = cast(char *)zpl_str_trim(e+1, true); + d = *p; + + /* unescape escaped quotes (so that unescaped text escapes :) */ + { + char *ep = b; + do { + if (*ep == '"' && *(ep+1) == '"') { + zpl_memmove(ep, ep+1, zpl_strlen(ep)); + } + ep++; + } while (*ep); + } + } + else if (*p == delim) { + d = *p; + row_item.string = ""; + } + else if (*p) { + /* regular data */ + b = e = p; + row_item.string = b; + do { + e++; + } while (*e && *e != delim && *e != '\n'); + if (*e) { + p = cast(char *)zpl_str_trim(e, true); + while (zpl_char_is_space(*(e-1))) { e--; } + d = *p; + *e = 0; + } + else { + d = 0; + p = e; + } + + /* check if number and process if so */ + zpl_b32 skip_number = false; + char *num_p = b; + do { + if (!zpl_char_is_hex_digit(*num_p) && (!zpl_strchr("+-.eExX", *num_p))) { + skip_number = true; + break; + } + } while (*num_p++); + + if (!skip_number) { + zpl_adt_str_to_number(&row_item); + } + } + + if (colc >= zpl_array_count(root->nodes)) { + zpl_adt_append_arr(root, NULL); + } + + zpl_array_append(root->nodes[colc].nodes, row_item); + + if (d == delim) { + colc++; + p++; + } + else if (d == '\n' || d == 0) { + /* check if number of rows is not mismatched */ + if (total_colc < colc) total_colc = colc; + else if (total_colc != colc) { + ZPL_CSV_ASSERT("mismatched rows"); + err = ZPL_CSV_ERROR_MISMATCHED_ROWS; + return err; + } + colc = 0; + if (d != 0) p++; + } + } while(*p); + + if (zpl_array_count(root->nodes) == 0) { + ZPL_CSV_ASSERT("unexpected end of input. stream is empty."); + err = ZPL_CSV_ERROR_UNEXPECTED_END_OF_INPUT; + return err; + } + + /* consider first row as a header. */ + if (has_header) { + for (zpl_isize i = 0; i < zpl_array_count(root->nodes); i++) { + zpl_csv_object *col = root->nodes + i; + zpl_csv_object *hdr = col->nodes; + col->name = hdr->string; + zpl_array_remove_at(col->nodes, 0); + } + } + + return err; + } + void zpl_csv_free(zpl_csv_object *obj) { + zpl_adt_destroy_branch(obj); + } + + void zpl__csv_write_record(zpl_file *file, zpl_csv_object *node) { + switch (node->type) { + case ZPL_ADT_TYPE_STRING: { + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + switch (node->name_style) { + case ZPL_ADT_NAME_STYLE_DOUBLE_QUOTE: { + zpl_fprintf(file, "\""); + zpl_adt_print_string(file, node, "\"", "\""); + zpl_fprintf(file, "\""); + } break; + + case ZPL_ADT_NAME_STYLE_NO_QUOTES: { + #endif + zpl_fprintf(file, "%s", node->string); + #ifndef ZPL_PARSER_DISABLE_ANALYSIS + } break; + } + #endif + } break; + + case ZPL_ADT_TYPE_REAL: + case ZPL_ADT_TYPE_INTEGER: { + zpl_adt_print_number(file, node); + } break; + } + } + + void zpl__csv_write_header(zpl_file *file, zpl_csv_object *header) { + zpl_csv_object temp = *header; + temp.string = temp.name; + temp.type = ZPL_ADT_TYPE_STRING; + zpl__csv_write_record(file, &temp); + } + + void zpl_csv_write_delimiter(zpl_file *file, zpl_csv_object *obj, char delimiter) { + ZPL_ASSERT_NOT_NULL(file); + ZPL_ASSERT_NOT_NULL(obj); + ZPL_ASSERT(obj->nodes); + zpl_isize cols = zpl_array_count(obj->nodes); + if (cols == 0) return; + + zpl_isize rows = zpl_array_count(obj->nodes[0].nodes); + if (rows == 0) return; + + zpl_b32 has_headers = obj->nodes[0].name != NULL; + + if (has_headers) { + for (zpl_isize i = 0; i < cols; i++) { + zpl__csv_write_header(file, &obj->nodes[i]); + if (i+1 != cols) { + zpl_fprintf(file, "%c", delimiter); + } + } + zpl_fprintf(file, "\n"); + } + + for (zpl_isize r = 0; r < rows; r++) { + for (zpl_isize i = 0; i < cols; i++) { + zpl__csv_write_record(file, &obj->nodes[i].nodes[r]); + if (i+1 != cols) { + zpl_fprintf(file, "%c", delimiter); + } + } + zpl_fprintf(file, "\n"); + } + } + + zpl_string zpl_csv_write_string_delimiter(zpl_allocator a, zpl_csv_object *obj, char delimiter) { + zpl_file tmp; + zpl_file_stream_new(&tmp, a); + zpl_csv_write_delimiter(&tmp, obj, delimiter); + zpl_isize fsize; + zpl_u8* buf = zpl_file_stream_buf(&tmp, &fsize); + zpl_string output = zpl_string_make_length(a, (char *)buf, fsize); + zpl_file_close(&tmp); + return output; + } + + ZPL_END_C_DECLS + // file: source/parsers/uri.c + + ZPL_BEGIN_C_DECLS + + void zpl__uri_decode_str(char *text) { + char buf[ZPL_PRINTF_MAXLEN] = {0}, *p = buf; + char *tp = text; + + char ch = -1; + while (*tp) { + ch = *tp++; + + if (ch != '%') { + if (ch == '+') { + *(p++) = ' '; + } else { + *(p++) = ch; + } + } else { + char hex[3] = {0}; + hex[0] = tp[0]; + hex[1] = tp[1]; + char b = (char)zpl_str_to_i64(hex, NULL, 16); + *(p++) = b; + tp += 2; + } + } + + zpl_strcpy(text, buf); + text[p-buf] = 0; + } + + zpl_u8 zpl_uri_init(zpl_adt_node *root, char *origin, zpl_allocator a) { + zpl_u8 err_code = ZPL_URI_ERROR_NONE; + ZPL_ASSERT_NOT_NULL(root); + zpl_zero_item(root); + zpl_adt_set_obj(root, NULL, a); + if (origin) + zpl_adt_append_str(root, "__zpl_origin__", origin); + return err_code; + } + + zpl_u8 zpl_uri_parse(zpl_adt_node *root, char *text, zpl_allocator a) { + zpl_u8 err_code = ZPL_URI_ERROR_NONE; + ZPL_ASSERT_NOT_NULL(root); + ZPL_ASSERT_NOT_NULL(text); + zpl_zero_item(root); + text = (char *)zpl_str_trim(text, false); + + zpl_adt_set_obj(root, NULL, a); + + char *p = text, *b = p; + + if (*p == 0) { + return err_code; + } + + // NOTE(zaklaus): grab URI origin + if (*p != '?') { + while (*p && *p != '?' && !zpl_char_is_space(*p)) {++p;} + char c = *p; + *p = 0; + + zpl_adt_append_str(root, "__zpl_origin__", b); + + if (!c) { + // NOTE(zaklaus): URI has no query params, bail + return err_code; + } + } + + b = ++p; + + // NOTE(zaklaus): extract query params + while (*p && !zpl_char_is_space(*p)) { + // NOTE(zaklaus): get param name + b = p; + while (*p && (*p != '&' && *p != '?' && *p != '=' && !zpl_char_is_space(*p))) { ++p; } + char c = *p; + *p = 0; + + char *field_name = b; + char *field_value = ""; + zpl__uri_decode_str(field_name); + + if (c == '=') { + // NOTE(zaklaus): read param value + ++p; + b = p; + while (*p && (*p != '&' && *p != '?' && !zpl_char_is_space(*p))) { ++p; } + c = *p; + *p = 0; + + field_value = b; + zpl__uri_decode_str(field_value); + zpl_adt_node *obj = zpl_adt_append_str(root, field_name, field_value); + zpl_adt_str_to_number_strict(obj); + } else { + zpl_adt_node *obj = zpl_adt_append_flt(root, field_name, 1); + obj->props = ZPL_ADT_PROPS_TRUE; + } + + + if (!c) { + break; + } + + ++p; + } + + return err_code; + } + + zpl_adt_error zpl__uri_print_str(zpl_file *file, const char *text) { + ZPL_ASSERT_NOT_NULL(file); + + if (!text) { + return ZPL_ADT_ERROR_NONE; + } + + const char *p = text; + char buf[10] = {0}; + zpl_u8 ch; + + while (*p) { + ch = (zpl_u8) *p++; + if (ch == ' ') { + zpl_fprintf(file, "%s", "+"); + } else if (zpl_char_is_alphanumeric(ch) || zpl_strchr("-_.~", ch)) { + zpl_fprintf(file, "%c", ch); + } else { + zpl_snprintf(buf, zpl_size_of(buf), "%02X", ch); + zpl_fprintf(file, "%c%s", '%', buf); + } + } + + return ZPL_ADT_ERROR_NONE; + } + + void zpl_uri_write(zpl_file *f, zpl_adt_node *obj) { + ZPL_ASSERT_NOT_NULL(f); + ZPL_ASSERT_NOT_NULL(obj); + ZPL_ASSERT(obj->type == ZPL_ADT_TYPE_OBJECT); + + zpl_adt_node *origin = NULL; + + // NOTE(zaklaus): write URI origin if available + { + origin = zpl_adt_query(obj, "__zpl_origin__"); + if (origin) { + zpl_fprintf(f, origin->string); + } + } + + // NOTE(zaklaus): write params + if (zpl_array_count(obj->nodes) > 0) + zpl_fprintf(f, "%s", "?"); + + for (zpl_isize i = 0; i < zpl_array_count(obj->nodes); i++) { + zpl_adt_node *n = (obj->nodes+i); + if (origin == n) continue; + + zpl__uri_print_str(f, n->name); + + if (n->type == ZPL_ADT_TYPE_STRING) { + zpl_fprintf(f, "%c", '='); + zpl__uri_print_str(f, n->string); + } else if (n->type == ZPL_ADT_TYPE_INTEGER || n->type == ZPL_ADT_TYPE_REAL) { + if (n->props != ZPL_ADT_PROPS_TRUE) { + zpl_fprintf(f, "%c", '='); + // TODO: ensure the output is URI-encoded + zpl_adt_print_number(f, n); + } + } + + if (i+1 < zpl_array_count(obj->nodes)) { + zpl_fprintf(f, "%s", "&"); + } + } + } + + void zpl_uri_free(zpl_adt_node *obj) { + zpl_adt_destroy_branch(obj); + } + + zpl_string zpl_uri_write_string(zpl_allocator a, zpl_adt_node *obj) { + zpl_file tmp; + zpl_file_stream_new(&tmp, a); + zpl_uri_write(&tmp, obj); + zpl_isize fsize; + zpl_u8* buf = zpl_file_stream_buf(&tmp, &fsize); + zpl_string output = zpl_string_make_length(a, (char *)buf, fsize); + zpl_file_close(&tmp); + return output; + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_MODULE_SOCKET) + // file: source/socket.c + + ZPL_BEGIN_C_DECLS + + #if defined(ZPL_SYSTEM_WINDOWS) + # include + # pragma comment(lib, "ws2_32.lib") + typedef int socklen_t; + #else //unix + # include + # include + # include + # include + #ifndef TCP_NODELAY + # include + # include + # endif + #endif + + ZPL_DEF zpl_socket zpl_socket_init(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + WSADATA winsock_data = {0}; + return WSAStartup(MAKEWORD(2, 2), &winsock_data) != NO_ERROR; + # endif + return 0; + } + + ZPL_DEF zpl_socket zpl_socket_create(zpl_i32 protocol, zpl_i32 mode, char flags, const char *host, const char *service) { + struct addrinfo *result, hints = { + (mode == ZPL_SOCKET_BIND) ? AI_PASSIVE : 0, + AF_UNSPEC, + (protocol == ZPL_SOCKET_UDP) ? SOCK_DGRAM : SOCK_STREAM, + 0, 0, 0, 0, 0 + }; + + if (getaddrinfo(host, service, &hints, &result) != 0) { + return -1; + } + # if defined(ZPL_SYSTEM_WINDOWS) + zpl_socket sock = (zpl_socket)socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (sock == INVALID_SOCKET) { + freeaddrinfo(result); + return -1; + } + if (sock > INT_MAX) { + closesocket(sock); + freeaddrinfo(result); + return -1; + } + # else + zpl_socket sock = socket(result->ai_family, result->ai_socktype, result->ai_protocol); + if (sock == -1) { + freeaddrinfo(result); + return -1; + } + # endif + + if (result->ai_family == AF_INET6) { + zpl_i32 no = 0; + setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&no, sizeof(no)); + } + + if (protocol == ZPL_SOCKET_TCP) { + int nodelay = (flags & ZPL_SOCKET_NO_DELAY); + setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&nodelay, sizeof(nodelay)); + } + + if (mode == ZPL_SOCKET_BIND) { + if (bind(sock, result->ai_addr, (int)result->ai_addrlen) != 0) { + freeaddrinfo(result); + return -1; + } + } + + if (flags & ZPL_SOCKET_NON_BLOCKING) { + # if defined(ZPL_SYSTEM_WINDOWS) + DWORD non_blocking = 1; + if (ioctlsocket(sock, FIONBIO, &non_blocking)) { + freeaddrinfo(result); + return -1; + } + # else + if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1) { + freeaddrinfo(result); + return -1; + } + # endif + } + + if (mode == ZPL_SOCKET_CONNECT) { + if (connect(sock, result->ai_addr, (int)result->ai_addrlen) != 0 && !(flags & ZPL_SOCKET_NON_BLOCKING)) { + freeaddrinfo(result); + return -1; + } + } + + freeaddrinfo(result); + return sock; + } + + ZPL_DEF void zpl_socket_close(zpl_socket socket) { + # if defined(ZPL_SYSTEM_WINDOWS) + closesocket(socket); + # else + close(socket); + # endif + } + + ZPL_DEF void zpl_socket_terminate(void) { + # if defined(ZPL_SYSTEM_WINDOWS) + WSACleanup(); + # endif + } + + ZPL_DEF zpl_i32 zpl_socket_listen(zpl_socket socket, zpl_i32 backlog) { + return listen(socket, backlog); + } + + ZPL_DEF zpl_socket zpl_socket_accept(zpl_socket socket, zpl_socket_addr *addr) { + # if defined(ZPL_SYSTEM_WINDOWS) + int len = sizeof(*addr); + zpl_socket sock = (zpl_socket)accept(socket, (struct sockaddr *)addr, &len); + if (sock == INVALID_SOCKET) { + return -1; + } + if (sock > INT_MAX) { + closesocket(sock); + return -1; + } + # else + socklen_t len = sizeof(*addr); + zpl_socket sock = accept(socket, (struct sockaddr *)addr, &len); + if (sock == -1) { + return -1; + } + # endif + return sock; + } + + ZPL_DEF zpl_i32 zpl_socket_get_address(zpl_socket socket, zpl_socket_addr *addr) { + return getsockname(socket, (struct sockaddr *)addr, (socklen_t *)sizeof(*addr)); + } + + ZPL_DEF zpl_i32 zpl_socket_get_address_info(zpl_socket_addr *addr, char *host, zpl_i32 host_size, char *service, zpl_i32 service_size) { + return getnameinfo((struct sockaddr *)addr, (socklen_t)sizeof(*addr), host, host_size, service, service_size, 0); + } + + ZPL_DEF zpl_i32 zpl_socket_send(zpl_socket socket, const char *data, zpl_i32 size) { + return send(socket, data, size, 0); + } + + ZPL_DEF zpl_i32 zpl_socket_receive(zpl_socket socket, char *buffer, zpl_i32 size) { + return recv(socket, buffer, size, 0); + } + + ZPL_DEF zpl_i32 zpl_socket_send_to(zpl_socket socket, zpl_socket_addr *addr, const char *data, zpl_i32 size) { + return sendto(socket, data, size, 0, (struct sockaddr *)addr, (socklen_t)sizeof(*addr)); + } + + ZPL_DEF zpl_i32 zpl_socket_receive_from(zpl_socket socket, zpl_socket_addr *addr, char *buffer, zpl_i32 size) { + return recvfrom(socket, buffer, size, 0, (struct sockaddr *)addr, (socklen_t *)sizeof(*addr)); + } + + ZPL_DEF zpl_i32 zpl_socket_select(zpl_socket socket, zpl_f64 time) { + fd_set fds; + struct timeval tv; + + FD_ZERO(&fds); + if (socket > -1) FD_SET(socket, &fds); + + tv.tv_sec = (long)time; + tv.tv_usec = (long)((time - tv.tv_sec) * 1000000); + + return select(socket + 1, &fds, 0, 0, &tv); + } + + ZPL_DEF zpl_i32 zpl_socket_multi_select(zpl_socket *sockets, zpl_i32 count, zpl_f64 time) { + fd_set fds; + struct timeval tv; + zpl_i32 i, max = -1; + + FD_ZERO(&fds); + for (i = 0; i < count; ++i) { + if (sockets[i] > max) max = sockets[i]; + if (sockets[i] > -1) FD_SET(sockets[i], &fds); + } + + tv.tv_sec = (long)time; + tv.tv_usec = (long)((time - tv.tv_sec) * 1000000); + + return select(max + 1, &fds, 0, 0, &tv); + } + + ZPL_END_C_DECLS +#endif + +#if defined(ZPL_COMPILER_MSVC) +# pragma warning(pop) +#endif + +#if defined(__GCC__) || defined(__GNUC__) || defined(__clang__) +# pragma GCC diagnostic pop +#endif + +#endif // ZPL_IMPLEMENTATION + +#if !defined(ZPL_PICO_CUSTOM_ROUTINES) +# undef zpl__printf_err +# undef zpl__printf_err_va +# undef zpl__strlen +#endif + +#if defined(ZPL_EXPOSE_TYPES) + typedef zpl_u8 u8; + typedef zpl_i8 i8; + typedef zpl_u16 u16; + typedef zpl_i16 i16; + typedef zpl_u32 u32; + typedef zpl_i32 i32; + typedef zpl_u64 u64; + typedef zpl_i64 i64; + typedef zpl_b8 b8; + typedef zpl_b16 b16; + typedef zpl_b32 b32; + typedef zpl_f32 f32; + typedef zpl_f64 f64; + typedef zpl_rune rune; + typedef zpl_usize usize; + typedef zpl_isize isize; + typedef zpl_uintptr uintptr; + typedef zpl_intptr intptr; +#endif // ZPL_EXPOSE_TYPES + +#endif // ZPL_H + +// TOC: +// zpl.h +// zpl_hedley.h +// header/opts.h +// header/essentials/helpers.h +// header/essentials/memory.h +// header/essentials/memory_custom.h +// header/essentials/types.h +// header/essentials/collections/buffer.h +// header/essentials/collections/list.h +// header/essentials/collections/hashtable.h +// header/essentials/collections/ring.h +// header/essentials/collections/array.h +// header/essentials/debug.h +// header/process.h +// header/threading/fence.h +// header/threading/mutex.h +// header/threading/sync.h +// header/threading/affinity.h +// header/threading/atomic.h +// header/threading/thread.h +// header/threading/sem.h +// header/math.h +// header/jobs.h +// header/parsers/json.h +// header/parsers/csv.h +// header/parsers/uri.h +// header/dll.h +// header/adt.h +// header/core/file_tar.h +// header/core/memory_virtual.h +// header/core/random.h +// header/core/file_stream.h +// header/core/string.h +// header/core/misc.h +// header/core/file.h +// header/core/stringlib.h +// header/core/sort.h +// header/core/print.h +// header/core/system.h +// header/core/file_misc.h +// header/core/time.h +// header/hashing.h +// header/regex.h +// header/socket.h +// source/hashing.c +// source/adt.c +// source/process.c +// source/essentials/debug.c +// source/essentials/memory_custom.c +// source/essentials/memory.c +// source/dll.c +// source/regex.c +// source/threading/mutex.c +// source/threading/affinity.c +// source/threading/atomic.c +// source/threading/sync.c +// source/threading/thread.c +// source/threading/fence.c +// source/threading/sem.c +// source/parsers/csv.c +// source/parsers/uri.c +// source/parsers/json.c +// source/jobs.c +// source/core/file_stream.c +// source/core/stringlib.c +// source/core/misc.c +// source/core/file_misc.c +// source/core/file.c +// source/core/memory_virtual.c +// source/core/print.c +// source/core/time.c +// source/core/string.c +// source/core/random.c +// source/core/sort.c +// source/core/file_tar.c +// source/opts.c +// source/math.c +// source/socket.c diff --git a/thirdparty/stb/truetype/stb_truetype.odin b/thirdparty/stb/truetype/stb_truetype.odin new file mode 100644 index 0000000..59bbcc4 --- /dev/null +++ b/thirdparty/stb/truetype/stb_truetype.odin @@ -0,0 +1,660 @@ +package stb_truetype + +import c "core:c" +import stbrp "vendor:stb/rect_pack" + +@(private) +LIB :: ( + "../lib/stb_truetype.lib" when ODIN_OS == .Windows + else "../lib/stb_truetype.a" when ODIN_OS == .Linux + else "../lib/darwin/stb_truetype.a" when ODIN_OS == .Darwin + else "../lib/stb_truetype_wasm.o" when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 + else "" +) + +when LIB != "" { + when !#exists(LIB) { + #panic("Could not find the compiled STB libraries, they can be compiled by running `make -C \"" + ODIN_ROOT + "vendor/stb/src\"`") + } +} + +when ODIN_ARCH == .wasm32 || ODIN_ARCH == .wasm64p32 { + foreign import stbtt "../lib/stb_truetype_wasm.o" +} else when LIB != "" { + foreign import stbtt { LIB } +} else { + foreign import stbtt "system:stb_truetype" +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// +//// +//// INTERFACE +//// +//// + +#assert(size_of(c.int) == size_of(rune)) +#assert(size_of(c.int) == size_of(b32)) + +//----------------------------------------------------------------------------- +// CUSTOM: ODIN COMPATIBLE ALLOCATOR +//----------------------------------------------------------------------------- + +zpl_allocator_type :: enum(i32) { + Alloc, + Free, + FreeAll, + Resize, +} + +zpl_allocator_proc :: #type proc(allocator_data: rawptr, type: zpl_allocator_type, + size: c.ssize_t, alignment: c.ssize_t, + old_memory: rawptr, old_size: c.ssize_t, + flags : c.ulonglong +) -> rawptr + +zpl_allocator :: struct { + procedure: zpl_allocator_proc, + data: rawptr, +} + +@(default_calling_convention="c", link_prefix="stbtt_") +foreign stbtt { + SetAllocator :: proc(allocator : zpl_allocator) --- +} + +//----------------------------------------------------------------------------- +// END CUSTOM: ODIN COMPATIBLE ALLOCATOR +//----------------------------------------------------------------------------- + +////////////////////////////////////////////////////////////////////////////// +// +// 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, + skip_missing: b32, + 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, #by_ptr 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, #by_ptr 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, gpos, svg: 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) -> c.int --- + + // 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(#by_ptr 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(#by_ptr 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(#by_ptr 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(#by_ptr 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(#by_ptr info: fontinfo, typoAscent, typoDescent, typoLineGap: ^c.int) -> b32 --- + + // the bounding box around all possible characters + GetFontBoundingBox :: proc(#by_ptr 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(#by_ptr info: fontinfo, codepoint: rune, advanceWidth, leftSideBearing: ^c.int) --- + + // an additional amount to add to the 'advance' value between ch1 and ch2 + GetCodepointKernAdvance :: proc(#by_ptr info: fontinfo, ch1, ch2: rune) -> (advance: c.int) --- + + // Gets the bounding box of the visible part of the glyph, in unscaled coordinates + GetCodepointBox :: proc(#by_ptr 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(#by_ptr info: fontinfo, glyph_index: c.int, advanceWidth, leftSideBearing: ^c.int) --- + GetGlyphKernAdvance :: proc(#by_ptr info: fontinfo, glyph1, glyph2: c.int) -> c.int --- + GetGlyphBox :: proc(#by_ptr 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(#by_ptr info: fontinfo) -> c.int --- + GetKerningTable :: proc(#by_ptr 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(#by_ptr 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(#by_ptr info: fontinfo, unicode_codepoint: rune, vertices: ^[^]vertex) -> c.int --- + GetGlyphShape :: proc(#by_ptr info: fontinfo, glyph_index: c.int, vertices: ^[^]vertex) -> c.int --- + + // frees the data allocated above + FreeShape :: proc(#by_ptr info: fontinfo, vertices: [^]vertex) --- + + // fills svg with the character's SVG data. + // returns data size or 0 if SVG not found. + FindSVGDoc :: proc(#by_ptr info: fontinfo, gl: b32) -> [^]byte --- + GetCodepointSVG :: proc(#by_ptr info: fontinfo, unicode_codepoint: rune, svg: ^cstring) -> c.int --- + GetGlyphSVG :: proc(#by_ptr 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(#by_ptr 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(#by_ptr 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(#by_ptr 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(#by_ptr 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(#by_ptr 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(#by_ptr 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(#by_ptr 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(#by_ptr info: fontinfo, scale_x, scale_y: f32, glyph: c.int, width, height, xoff, yoff: ^c.int) -> [^]byte --- + GetGlyphBitmapSubpixel :: proc(#by_ptr info: fontinfo, scale_x, scale_y, shift_x, shift_y: f32, glyph: c.int, width, height, xoff, yoff: ^c.int) -> [^]byte --- + MakeGlyphBitmap :: proc(#by_ptr info: fontinfo, output: [^]byte, out_w, out_h, out_stride: c.int, scale_x, scale_y: f32, glyph: c.int) --- + MakeGlyphBitmapSubpixel :: proc(#by_ptr 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(#by_ptr 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(#by_ptr font: fontinfo, glyph: c.int, scale_x, scale_y: f32, ix0, iy0, ix1, iy1: ^c.int) --- + GetGlyphBitmapBoxSubpixel :: proc(#by_ptr 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(#by_ptr info: fontinfo, scale: f32, glyph, padding: c.int, onedge_value: u8, pixel_dist_scale: f32, width, height, xoff, yoff: ^c.int) -> [^]byte --- + GetCodepointSDF :: proc(#by_ptr 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(#by_ptr 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, +} diff --git a/thirdparty/stb/truetype/stb_truetype_wasm.odin b/thirdparty/stb/truetype/stb_truetype_wasm.odin new file mode 100644 index 0000000..f362390 --- /dev/null +++ b/thirdparty/stb/truetype/stb_truetype_wasm.odin @@ -0,0 +1,4 @@ +#+build wasm32, wasm64p32 +package stb_truetype + +@(require) import _ "vendor:libc" diff --git a/vefontcache/.freetype_wip.odin b/vefontcache/.freetype_wip.odin new file mode 100644 index 0000000..d6306a4 --- /dev/null +++ b/vefontcache/.freetype_wip.odin @@ -0,0 +1,163 @@ +package vefontcache + +when false { +// TODO(Ed): Freetype support + +// TODO(Ed): glyph triangulation cannot be handled in a 'font parser' abstraction. Just going to have explicit procedures to grab info neatly... +cache_glyph_freetype :: proc(ctx: ^Context, font: Font_ID, glyph_index: Glyph, entry: ^Entry, bounds_0, bounds_1: Vec2, scale, translate: Vec2) -> b32 +{ + draw_filled_path_freetype :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex, + scale := Vec2 { 1, 1 }, + translate := Vec2 { 0, 0 }, + debug_print_verbose : b32 = false + ) + { + if debug_print_verbose { + log("outline_path:") + for point in path { + vec := point.pos * scale + translate + logf(" %0.2f %0.2f", vec.x, vec.y ) + } + } + + v_offset := cast(u32) len(draw_list.vertices) + for point in path + { + transformed_point := Vertex { + pos = point.pos * scale + translate, + u = 0, + v = 0 + } + append( & draw_list.vertices, transformed_point ) + } + + if len(path) > 2 + { + indices := & draw_list.indices + for index : u32 = 1; index < cast(u32) len(path) - 1; index += 1 { + to_add := [3]u32 { + v_offset, + v_offset + index, + v_offset + index + 1 + } + append( indices, ..to_add[:] ) + } + + // Close the path by connecting the last vertex to the first two + to_add := [3]u32 { + v_offset, + v_offset + cast(u32)(len(path) - 1), + v_offset + 1 + } + append( indices, ..to_add[:] ) + } + } + + if glyph_index == Glyph(0) { + return false + } + + face := entry.parser_info.freetype_info + error := freetype.load_glyph(face, u32(glyph_index), {.No_Bitmap, .No_Scale}) + if error != .Ok { + return false + } + + glyph := face.glyph + if glyph.format != .Outline { + return false + } + + outline := &glyph.outline + if outline.n_points == 0 { + return false + } + + draw := Draw_Call_Default + draw.pass = Frame_Buffer_Pass.Glyph + draw.start_index = cast(u32) len(ctx.draw_list.indices) + + contours := slice.from_ptr(cast( [^]i16) outline.contours, int(outline.n_contours)) + points := slice.from_ptr(cast( [^]freetype.Vector) outline.points, int(outline.n_points)) + tags := slice.from_ptr(cast( [^]u8) outline.tags, int(outline.n_points)) + + path := &ctx.temp_path + clear(path) + + outside := Vec2{ bounds_0.x - 21, bounds_0.y - 33 } + + start_index: int = 0 + for contour_index in 0 ..< int(outline.n_contours) + { + end_index := int(contours[contour_index]) + 1 + prev_point : Vec2 + first_point : Vec2 + + for idx := start_index; idx < end_index; idx += 1 + { + current_pos := Vec2 { f32( points[idx].x ), f32( points[idx].y ) } + if ( tags[idx] & 1 ) == 0 + { + // If current point is off-curve + if (idx == start_index || (tags[ idx - 1 ] & 1) != 0) + { + // current is the first or following an on-curve point + prev_point = current_pos + } + else + { + // current and previous are off-curve, calculate midpoint + midpoint := (prev_point + current_pos) * 0.5 + append( path, Vertex { pos = midpoint } ) // Add midpoint as on-curve point + if idx < end_index - 1 + { + // perform interp from prev_point to current_pos via midpoint + step := 1.0 / entry.curve_quality + for alpha : f32 = 0.0; alpha <= 1.0; alpha += step + { + bezier_point := eval_point_on_bezier3( prev_point, midpoint, current_pos, alpha ) + append( path, Vertex{ pos = bezier_point } ) + } + } + + prev_point = current_pos + } + } + else + { + if idx == start_index { + first_point = current_pos + } + if prev_point != (Vec2{}) { + // there was an off-curve point before this + append(path, Vertex{ pos = prev_point}) // Ensure previous off-curve is handled + } + append(path, Vertex{ pos = current_pos}) + prev_point = {} + } + } + + // ensure the contour is closed + if path[0].pos != path[ len(path) - 1 ].pos { + append(path, Vertex{pos = path[0].pos}) + } + draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate) + // draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate, ctx.debug_print_verbose) + clear(path) + start_index = end_index + } + + if len(path) > 0 { + // draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose) + draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate) + } + + draw.end_index = cast(u32) len(ctx.draw_list.indices) + if draw.end_index > draw.start_index { + append( & ctx.draw_list.calls, draw) + } + + return true +} + +} diff --git a/vefontcache/LRU.odin b/vefontcache/LRU.odin index 3682ccf..dab054e 100644 --- a/vefontcache/LRU.odin +++ b/vefontcache/LRU.odin @@ -1,22 +1,34 @@ package vefontcache -/* -The choice was made to keep the LRU cache implementation as close to the original as possible. +/* Note(Ed): + Original implementation has been changed moderately. + Notably the LRU is now type generic for its key value. + This was done to profile between using u64, u32, and u16. + + What ended up happening was using u32 for both the atlas and the shape cache + yielded a several ms save for processing thousands of draw text calls. + + There was an attempt at an optimization pass but the directives done here (other than force_inline) + are marginal changes at best. */ -import "base:runtime" +// 16-bit hashing was attempted, however it seems to get collisions with djb8_hash_16 + +LRU_Fail_Mask_16 :: 0xFFFF +LRU_Fail_Mask_32 :: 0xFFFFFFFF +LRU_Fail_Mask_64 :: 0xFFFFFFFFFFFFFFFF Pool_ListIter :: i32 -Pool_ListValue :: u64 -Pool_List_Item :: struct { +Pool_List_Item :: struct( $V_Type : typeid ) #packed { +// Pool_List_Item :: struct( $V_Type : typeid ) { prev : Pool_ListIter, next : Pool_ListIter, - value : Pool_ListValue, + value : V_Type, } -Pool_List :: struct { - items : [dynamic]Pool_List_Item, +Pool_List :: struct( $V_Type : typeid) { + items : [dynamic]Pool_List_Item(V_Type), free_list : [dynamic]Pool_ListIter, front : Pool_ListIter, back : Pool_ListIter, @@ -25,144 +37,144 @@ Pool_List :: struct { dbg_name : string, } -pool_list_init :: proc( pool : ^Pool_List, capacity : i32, dbg_name : string = "" ) +pool_list_init :: proc( pool : ^Pool_List($V_Type), capacity : i32, dbg_name : string = "" ) { error : Allocator_Error - pool.items, error = make( [dynamic]Pool_List_Item, int(capacity) ) - assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate items array") + pool.items, error = make( [dynamic]Pool_List_Item(V_Type), int(capacity) ) + assert( error == .None, "VEFontCache.pool_list_inits: Failed to allocate items array") resize( & pool.items, capacity ) pool.free_list, error = make( [dynamic]Pool_ListIter, len = 0, cap = int(capacity) ) - assert( error == .None, "VEFontCache.pool_list_init : Failed to allocate free_list array") + assert( error == .None, "VEFontCache.pool_list_init: Failed to allocate free_list array") resize( & pool.free_list, capacity ) pool.capacity = capacity pool.dbg_name = dbg_name - using pool - for id in 0 ..< capacity { - free_list[id] = i32(id) - items[id] = { + for id in 0 ..< pool.capacity { + pool.free_list[id] = Pool_ListIter(id) + pool.items[id] = { prev = -1, next = -1, } } - front = -1 - back = -1 + pool.front = -1 + pool.back = -1 } -pool_list_free :: proc( pool : ^Pool_List ) { +pool_list_free :: proc( pool : ^Pool_List($V_Type) ) { delete( pool.items) delete( pool.free_list) } -pool_list_reload :: proc( pool : ^Pool_List, allocator : Allocator ) { +pool_list_reload :: proc( pool : ^Pool_List($V_Type), allocator : Allocator ) { reload_array( & pool.items, allocator ) reload_array( & pool.free_list, allocator ) } -pool_list_clear :: proc( pool: ^Pool_List ) { - using pool - clear(& items) - clear(& free_list) +pool_list_clear :: proc( pool: ^Pool_List($V_Type) ) +{ + clear(& pool.items) + clear(& pool.free_list) resize( & pool.items, cap(pool.items) ) resize( & pool.free_list, cap(pool.free_list) ) - for id in 0 ..< capacity { - free_list[id] = i32(id) - items[id] = { + for id in 0 ..< pool.capacity { + pool.free_list[id] = Pool_ListIter(id) + pool.items[id] = { prev = -1, next = -1, } } - front = -1 - back = -1 - size = 0 + pool.front = -1 + pool.back = -1 + pool.size = 0 } -pool_list_push_front :: proc( pool : ^Pool_List, value : Pool_ListValue ) +@(optimization_mode="favor_size") +pool_list_push_front :: proc( pool : ^Pool_List($V_Type), value : V_Type ) #no_bounds_check { - using pool - if size >= capacity do return + if pool.size >= pool.capacity do return - length := len(free_list) + length := len(pool.free_list) assert( length > 0 ) - assert( length == int(capacity - size) ) - - id := free_list[ len(free_list) - 1 ] - if pool.dbg_name != "" { - logf("pool_list: back %v", id) - } - pop( & free_list ) - items[ id ].prev = -1 - items[ id ].next = front - items[ id ].value = value - if pool.dbg_name != "" { - logf("pool_list: pushed %v into id %v", value, id) - } - - if front != -1 do items[ front ].prev = id - if back == -1 do back = id - front = id - size += 1 + assert( length == int(pool.capacity - pool.size) ) + + id := pool.free_list[ len(pool.free_list) - 1 ] + // if pool.dbg_name != "" { + // logf("pool_list: back %v", id) + // } + pop( & pool.free_list ) + pool.items[ id ].prev = -1 + pool.items[ id ].next = pool.front + pool.items[ id ].value = value + // if pool.dbg_name != "" { + // logf("pool_list: pushed %v into id %v", value, id) + // } + + if pool.front != -1 do pool.items[ pool.front ].prev = id + if pool.back == -1 do pool.back = id + pool.front = id + pool.size += 1 } -pool_list_erase :: proc( pool : ^Pool_List, iter : Pool_ListIter ) +@(optimization_mode="favor_size") +pool_list_erase :: proc( pool : ^Pool_List($V_Type), iter : Pool_ListIter ) #no_bounds_check { - using pool - if size <= 0 do return - assert( iter >= 0 && iter < i32(capacity) ) - assert( len(free_list) == int(capacity - size) ) + if pool.size <= 0 do return + assert( iter >= 0 && iter < Pool_ListIter(pool.capacity) ) + assert( len(pool.free_list) == int(pool.capacity - pool.size) ) - iter_node := & items[ iter ] + iter_node := & pool.items[ iter ] prev := iter_node.prev next := iter_node.next - if iter_node.prev != -1 do items[ prev ].next = iter_node.next - if iter_node.next != -1 do items[ next ].prev = iter_node.prev + if iter_node.prev != -1 do pool.items[ prev ].next = iter_node.next + if iter_node.next != -1 do pool.items[ next ].prev = iter_node.prev - if front == iter do front = iter_node.next - if back == iter do back = iter_node.prev + if pool.front == iter do pool.front = iter_node.next + if pool.back == iter do pool.back = iter_node.prev iter_node.prev = -1 iter_node.next = -1 iter_node.value = 0 - append( & free_list, iter ) + append( & pool.free_list, iter ) - size -= 1 - if size == 0 { - back = -1 - front = -1 + pool.size -= 1 + if pool.size == 0 { + pool.back = -1 + pool.front = -1 } } -pool_list_move_to_front :: #force_inline proc( pool : ^Pool_List, iter : Pool_ListIter ) +@(optimization_mode="favor_size") +pool_list_move_to_front :: proc "contextless" ( pool : ^Pool_List($V_Type), iter : Pool_ListIter ) #no_bounds_check { - using pool - - if front == iter do return + if pool.front == iter do return - item := & items[iter] - if item.prev != -1 do items[ item.prev ].next = item.next - if item.next != -1 do items[ item.next ].prev = item.prev - if back == iter do back = item.prev + item := & pool.items[iter] + if item.prev != -1 do pool.items[ item.prev ].next = item.next + if item.next != -1 do pool.items[ item.next ].prev = item.prev + if pool.back == iter do pool.back = item.prev - item.prev = -1 - item.next = front - items[ front ].prev = iter - front = iter + item.prev = -1 + item.next = pool.front + pool.items[ pool.front ].prev = iter + pool.front = iter } -pool_list_peek_back :: #force_inline proc ( pool : ^Pool_List ) -> Pool_ListValue { +@(optimization_mode="favor_size") +pool_list_peek_back :: #force_inline proc ( pool : Pool_List($V_Type) ) -> V_Type #no_bounds_check { assert( pool.back != - 1 ) value := pool.items[ pool.back ].value return value } -pool_list_pop_back :: #force_inline proc( pool : ^Pool_List ) -> Pool_ListValue { +@(optimization_mode="favor_size") +pool_list_pop_back :: #force_inline proc( pool : ^Pool_List($V_Type) ) -> V_Type #no_bounds_check { if pool.size <= 0 do return 0 assert( pool.back != -1 ) @@ -171,69 +183,69 @@ pool_list_pop_back :: #force_inline proc( pool : ^Pool_List ) -> Pool_ListValue return value } -LRU_Link :: struct { - pad_top : u64, - +LRU_Link :: struct #packed { value : i32, ptr : Pool_ListIter, - - pad_bottom : u64, } -LRU_Cache :: struct { +LRU_Cache :: struct( $Key_Type : typeid ) { capacity : i32, num : i32, - table : map[u64]LRU_Link, - key_queue : Pool_List, + table : map[Key_Type]LRU_Link, + key_queue : Pool_List(Key_Type), } -lru_init :: proc( cache : ^LRU_Cache, capacity : i32, dbg_name : string = "" ) { +lru_init :: proc( cache : ^LRU_Cache($Key_Type), capacity : i32, dbg_name : string = "" ) { error : Allocator_Error cache.capacity = capacity - cache.table, error = make( map[u64]LRU_Link, uint(capacity) ) + cache.table, error = make( map[Key_Type]LRU_Link, uint(capacity) ) assert( error == .None, "VEFontCache.lru_init : Failed to allocate cache's table") pool_list_init( & cache.key_queue, capacity, dbg_name = dbg_name ) -} +} -lru_free :: proc( cache : ^LRU_Cache ) { +lru_free :: proc( cache : ^LRU_Cache($Key_Type) ) { pool_list_free( & cache.key_queue ) delete( cache.table ) } -lru_reload :: #force_inline proc( cache : ^LRU_Cache, allocator : Allocator ) { +lru_reload :: #force_inline proc( cache : ^LRU_Cache($Key_Type), allocator : Allocator ) { reload_map( & cache.table, allocator ) pool_list_reload( & cache.key_queue, allocator ) } -lru_clear :: proc ( cache : ^LRU_Cache ) { +lru_clear :: proc ( cache : ^LRU_Cache($Key_Type) ) { pool_list_clear( & cache.key_queue ) clear(& cache.table) cache.num = 0 } -lru_find :: #force_inline proc "contextless" ( cache : ^LRU_Cache, key : u64, must_find := false ) -> (LRU_Link, bool) { +@(optimization_mode="favor_size") +lru_find :: #force_inline proc "contextless" ( cache : LRU_Cache($Key_Type), key : Key_Type, must_find := false ) -> (LRU_Link, bool) #no_bounds_check { link, success := cache.table[key] return link, success } -lru_get :: #force_inline proc( cache: ^LRU_Cache, key : u64 ) -> i32 { +@(optimization_mode="favor_size") +lru_get :: #force_inline proc ( cache: ^LRU_Cache($Key_Type), key : Key_Type ) -> i32 #no_bounds_check { if link, ok := &cache.table[ key ]; ok { - pool_list_move_to_front(&cache.key_queue, link.ptr) - return link.value + pool_list_move_to_front(&cache.key_queue, link.ptr) + return link.value } return -1 } -lru_get_next_evicted :: #force_inline proc ( cache : ^LRU_Cache ) -> u64 { +@(optimization_mode="favor_size") +lru_get_next_evicted :: #force_inline proc ( cache : LRU_Cache($Key_Type) ) -> Key_Type #no_bounds_check { if cache.key_queue.size >= cache.capacity { - evict := pool_list_peek_back( & cache.key_queue ) + evict := pool_list_peek_back( cache.key_queue ) return evict } - return 0xFFFFFFFFFFFFFFFF + return ~Key_Type(0) } -lru_peek :: #force_inline proc ( cache : ^LRU_Cache, key : u64, must_find := false ) -> i32 { +@(optimization_mode="favor_size") +lru_peek :: #force_inline proc "contextless" ( cache : LRU_Cache($Key_Type), key : Key_Type, must_find := false ) -> i32 #no_bounds_check { iter, success := lru_find( cache, key, must_find ) if success == false { return -1 @@ -241,8 +253,10 @@ lru_peek :: #force_inline proc ( cache : ^LRU_Cache, key : u64, must_find := fal return iter.value } -lru_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u64 +@(optimization_mode="favor_size") +lru_put :: proc( cache : ^LRU_Cache($Key_Type), key : Key_Type, value : i32 ) -> Key_Type #no_bounds_check { + // profile(#procedure) if link, ok := & cache.table[ key ]; ok { pool_list_move_to_front( & cache.key_queue, link.ptr ) link.value = value @@ -265,8 +279,8 @@ lru_put :: #force_inline proc( cache : ^LRU_Cache, key : u64, value : i32 ) -> u return evict } -lru_refresh :: proc( cache : ^LRU_Cache, key : u64 ) { - link, success := lru_find( cache, key ) +lru_refresh :: proc( cache : ^LRU_Cache($Key_Type), key : Key_Type ) { + link, success := lru_find( cache ^, key ) pool_list_erase( & cache.key_queue, link.ptr ) pool_list_push_front( & cache.key_queue, key ) link.ptr = cache.key_queue.front diff --git a/vefontcache/atlas.odin b/vefontcache/atlas.odin index 33a2ceb..6a0213e 100644 --- a/vefontcache/atlas.odin +++ b/vefontcache/atlas.odin @@ -1,127 +1,126 @@ package vefontcache +// There are only 4 actual regions of the atlas. E represents the atlas_decide_region detecting an oversized glyph. +// Note(Ed): None should never really occur anymore. So its safe to most likely add an assert when its detected. Atlas_Region_Kind :: enum u8 { None = 0x00, - A = 0x41, - B = 0x42, - C = 0x43, - D = 0x44, - E = 0x45, + A = 0x01, + B = 0x02, + C = 0x03, + D = 0x04, + E = 0x05, Ignore = 0xFF, // ve_fontcache_cache_glyph_to_atlas uses a -1 value in clear draw call } -Atlas_Region :: struct { - state : LRU_Cache, +Atlas_Key :: u32 - width : i32, - height : i32, +// TODO(Ed) It might perform better with a tailored made hashtable implementation for the LRU_Cache or dedicated array struct/procs for the Atlas. +/* Essentially a sub-atlas of the atlas. There is a state cache per region that tracks the glyph inventory (what slot they occupy). + Unlike the shape cache this one's fixed capacity (natrually) and the next avail slot is tracked. +*/ +Atlas_Region :: struct { + state : LRU_Cache(Atlas_Key), size : Vec2i, capacity : Vec2i, offset : Vec2i, + slot_size : Vec2i, + next_idx : i32, } -Atlas :: struct { - width : i32, - height : i32, - - glyph_padding : i32, // Padding to add to bounds__scaled for choosing which atlas region. - glyph_over_scalar : f32, // Scalar to apply to bounds__scaled for choosing which atlas region. +/* There are four regions each succeeding region holds larger sized slots. + The generator pipeline for draw lists utilizes the regions array for info lookup. + Note(Ed): + Padding can techncially be larger than 1, however recently I haven't had any artififact issues... + size_multiplier usage isn't fully resolved. Intent was to further setup over_sampling or just having + a more massive cache for content that used more than the usual common glyphs. +*/ +Atlas :: struct { region_a : Atlas_Region, region_b : Atlas_Region, region_c : Atlas_Region, region_d : Atlas_Region, -} -atlas_bbox :: proc( atlas : ^Atlas, region : Atlas_Region_Kind, local_idx : i32 ) -> (position, size: Vec2) -{ - switch region - { - case .A: - size.x = f32(atlas.region_a.width) - size.y = f32(atlas.region_a.height) - - position.x = cast(f32) (( local_idx % atlas.region_a.capacity.x ) * atlas.region_a.width) - position.y = cast(f32) (( local_idx / atlas.region_a.capacity.x ) * atlas.region_a.height) - - position.x += f32(atlas.region_a.offset.x) - position.y += f32(atlas.region_a.offset.y) - - case .B: - size.x = f32(atlas.region_b.width) - size.y = f32(atlas.region_b.height) + regions : [5] ^Atlas_Region, - position.x = cast(f32) (( local_idx % atlas.region_b.capacity.x ) * atlas.region_b.width) - position.y = cast(f32) (( local_idx / atlas.region_b.capacity.x ) * atlas.region_b.height) + glyph_padding : f32, // Padding to add to bounds__scaled for choosing which atlas region. + size_multiplier : f32, // Grows all text by this multiple. - position.x += f32(atlas.region_b.offset.x) - position.y += f32(atlas.region_b.offset.y) + size : Vec2i, +} - case .C: - size.x = f32(atlas.region_c.width) - size.y = f32(atlas.region_c.height) +// Hahser for the atlas. +@(optimization_mode="favor_size") +atlas_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, px_size : f32, glyph_index : Glyph ) -> (lru_code : Atlas_Key) { + // lru_code = u32(glyph_index) + ( ( 0x10000 * u32(font) ) & 0xFFFF0000 ) + font := font + glyph_index := glyph_index + px_size := px_size + djb8_hash( & lru_code, to_bytes( & font) ) + djb8_hash( & lru_code, to_bytes( & glyph_index ) ) + djb8_hash( & lru_code, to_bytes( & px_size ) ) + return +} - position.x = cast(f32) (( local_idx % atlas.region_c.capacity.x ) * atlas.region_c.width) - position.y = cast(f32) (( local_idx / atlas.region_c.capacity.x ) * atlas.region_c.height) +@(optimization_mode="favor_size") +atlas_region_bbox :: #force_inline proc( region : Atlas_Region, local_idx : i32 ) -> (position, size: Vec2) +{ + size = vec2(region.slot_size) - position.x += f32(atlas.region_c.offset.x) - position.y += f32(atlas.region_c.offset.y) + position.x = cast(f32) (( local_idx % region.capacity.x ) * region.slot_size.x) + position.y = cast(f32) (( local_idx / region.capacity.x ) * region.slot_size.y) - case .D: - size.x = f32(atlas.region_d.width) - size.y = f32(atlas.region_d.height) + position.x += f32(region.offset.x) + position.y += f32(region.offset.y) + return +} - position.x = cast(f32) (( local_idx % atlas.region_d.capacity.x ) * atlas.region_d.width) - position.y = cast(f32) (( local_idx / atlas.region_d.capacity.x ) * atlas.region_d.height) +@(optimization_mode="favor_size") +atlas_decide_region :: #force_inline proc "contextless" (atlas : Atlas, glyph_buffer_size : Vec2, bounds_size_scaled : Vec2 ) -> (region_kind : Atlas_Region_Kind) +{ + // profile(#procedure) + glyph_padding_dbl := atlas.glyph_padding * 2 + padded_bounds := bounds_size_scaled + glyph_padding_dbl - position.x += f32(atlas.region_d.offset.x) - position.y += f32(atlas.region_d.offset.y) + for kind in 1 ..= 4 do if + padded_bounds.x <= f32(atlas.regions[kind].slot_size.x) && + padded_bounds.y <= f32(atlas.regions[kind].slot_size.y) + { + return cast(Atlas_Region_Kind) kind + } - case .Ignore, .None, .E: + if padded_bounds.x <= glyph_buffer_size.x && padded_bounds.y <= glyph_buffer_size.y{ + return .E } - return + return .None } -decide_codepoint_region :: proc(ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> (region_kind : Atlas_Region_Kind, region : ^Atlas_Region, over_sample : Vec2) +// Grab an atlas LRU cache slot. +@(optimization_mode="favor_size") +atlas_reserve_slot :: #force_inline proc ( region : ^Atlas_Region, lru_code : Atlas_Key ) -> (atlas_index : i32) { - if parser_is_glyph_empty(&entry.parser_info, glyph_index) { - return .None, nil, {} + if region.next_idx < region.state.capacity + { + evicted := lru_put( & region.state, lru_code, region.next_idx ) + atlas_index = region.next_idx + region.next_idx += 1 + assert( evicted == lru_code ) } + else + { + next_evict_codepoint := lru_get_next_evicted( region.state ) + assert( next_evict_codepoint != LRU_Fail_Mask_16) - bounds_0, bounds_1 := parser_get_glyph_box(&entry.parser_info, glyph_index) - bounds_width := f32(bounds_1.x - bounds_0.x) - bounds_height := f32(bounds_1.y - bounds_0.y) - - atlas := & ctx.atlas - glyph_buffer := & ctx.glyph_buffer - glyph_padding := f32( atlas.glyph_padding ) * 2 - - bounds_width_scaled := i32(bounds_width * entry.size_scale * atlas.glyph_over_scalar + glyph_padding) - bounds_height_scaled := i32(bounds_height * entry.size_scale * atlas.glyph_over_scalar + glyph_padding) + atlas_index = lru_peek( region.state, next_evict_codepoint, must_find = true ) + assert( atlas_index != -1 ) - // Use a lookup table for faster region selection - region_lookup := [4]struct { kind: Atlas_Region_Kind, region: ^Atlas_Region } { - { .A, & atlas.region_a }, - { .B, & atlas.region_b }, - { .C, & atlas.region_c }, - { .D, & atlas.region_d }, + evicted := lru_put( & region.state, lru_code, atlas_index ) + assert( evicted == next_evict_codepoint ) } - for region in region_lookup do if bounds_width_scaled <= region.region.width && bounds_height_scaled <= region.region.height { - return region.kind, region.region, glyph_buffer.over_sample - } - - if bounds_width_scaled <= glyph_buffer.width \ - && bounds_height_scaled <= glyph_buffer.height { - over_sample = \ - bounds_width_scaled <= glyph_buffer.width / 2 && - bounds_height_scaled <= glyph_buffer.height / 2 ? \ - {2.0, 2.0} \ - : {1.0, 1.0} - return .E, nil, over_sample - } - return .None, nil, {} + assert( lru_get( & region.state, lru_code ) != - 1 ) + return } diff --git a/vefontcache/draw.odin b/vefontcache/draw.odin index 0a75744..80bd3f5 100644 --- a/vefontcache/draw.odin +++ b/vefontcache/draw.odin @@ -1,20 +1,52 @@ package vefontcache -import "thirdparty:freetype" -import "core:slice" +/* + Note(Ed): This may be seperated in the future into another file dedending on how much is involved with supportin ear-clipping triangulation. +*/ + +// import "thirdparty:freetype" + +Glyph_Trianglation_Method :: enum(i32) { + Ear_Clipping, + Triangle_Fanning, +} Vertex :: struct { pos : Vec2, u, v : f32, } +Glyph_Bounds_Mat :: matrix[2, 2] f32 + +Glyph_Draw_Quad :: struct { + dst_pos : Vec2, + dst_scale : Vec2, + src_pos : Vec2, + src_scale : Vec2, +} + +// This is used by generate_shape_draw_list & batch_generate_glyphs_draw_list +// to track relevant glyph data in soa format for pipelined processing +Glyph_Pack_Entry :: struct #packed { + position : Vec2, + atlas_index : i32, + region_pos : Vec2, + region_size : Vec2, + over_sample : Vec2, // Only used for oversized glyphs + shape : Parser_Glyph_Shape, + draw_transform : Transform, + draw_quad : Glyph_Draw_Quad, + buffer_x : f32, + flush_glyph_buffer : b8, +} + Draw_Call :: struct { pass : Frame_Buffer_Pass, start_index : u32, end_index : u32, clear_before_draw : b32, region : Atlas_Region_Kind, - colour : Colour, + colour : RGBAN, } Draw_Call_Default :: Draw_Call { @@ -32,32 +64,50 @@ Draw_List :: struct { calls : [dynamic]Draw_Call, } -// TODO(Ed): This was a rough translation of the raw values the orignal was using, need to give better names... Frame_Buffer_Pass :: enum u32 { None = 0, - Glyph = 1, - Atlas = 2, - Target = 3, - Target_Uncached = 4, + Glyph = 1, // Operations on glyph buffer render target + Atlas = 2, // Operations on atlas render target + Target = 3, // Operations on user's end-destination render target using atlas + Target_Uncached = 4, // Operations on user's end-destination render target using glyph buffer } -Glyph_Draw_Buffer :: struct { - over_sample : Vec2, - batch : i32, - width : i32, - height : i32, - draw_padding : i32, +Glyph_Batch_Cache :: struct { + table : map[Atlas_Key]b8, + num : i32, + cap : i32, +} - batch_x : i32, +// The general tracker for a generator pipeline +Glyph_Draw_Buffer :: struct{ + over_sample : Vec2, + size : Vec2i, + draw_padding : f32, + snap_glyph_height : f32, + + allocated_x : i32, // Space used (horizontally) within the glyph buffer clear_draw_list : Draw_List, draw_list : Draw_List, + + batch_cache : Glyph_Batch_Cache, + shape_gen_scratch : [dynamic]Vertex, // Used during triangulating a glyph into a mesh. + + glyph_pack : #soa[dynamic]Glyph_Pack_Entry, + oversized : [dynamic]i32, + to_cache : [dynamic]i32, + cached : [dynamic]i32, } -blit_quad :: proc( draw_list : ^Draw_List, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1}, uv0 : Vec2 = {0, 0}, uv1 : Vec2 = {1, 1} ) +// Contructs a quad mesh for bliting a texture from source render target (src uv0 & 1) to the destination render target (p0, p1) +@(optimization_mode="favor_size") +blit_quad :: #force_inline proc ( draw_list : ^Draw_List, + p0 : Vec2 = {0, 0}, + p1 : Vec2 = {1, 1}, + uv0 : Vec2 = {0, 0}, + uv1 : Vec2 = {1, 1} +) { // profile(#procedure) - // logf("Blitting: xy0: %0.2f, %0.2f xy1: %0.2f, %0.2f uv0: %0.2f, %0.2f uv1: %0.2f, %0.2f", - // p0.x, p0.y, p1.x, p1.y, uv0.x, uv0.y, uv1.x, uv1.y); v_offset := cast(u32) len(draw_list.vertices) quadv : [4]Vertex = { @@ -88,196 +138,68 @@ blit_quad :: proc( draw_list : ^Draw_List, p0 : Vec2 = {0, 0}, p1 : Vec2 = {1, 1 return } -// TODO(Ed): glyph caching cannot be handled in a 'font parser' abstraction. Just going to have explicit procedures to grab info neatly... -cache_glyph_freetype :: proc(ctx: ^Context, font: Font_ID, glyph_index: Glyph, entry: ^Entry, bounds_0, bounds_1: Vec2, scale, translate: Vec2) -> b32 +// Constructs a triangle fan mesh to fill a shape using the provided path outside_point represents the center point of the fan. +@(optimization_mode="favor_size") +fill_path_via_fan_triangulation :: proc( draw_list : ^Draw_List, + outside_point : Vec2, + path : []Vertex, + scale := Vec2 { 1, 1 }, + translate := Vec2 { 0, 0 } +) #no_bounds_check { - draw_filled_path_freetype :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex, - scale := Vec2 { 1, 1 }, - translate := Vec2 { 0, 0 }, - debug_print_verbose : b32 = false - ) - { - if debug_print_verbose { - log("outline_path:") - for point in path { - vec := point.pos * scale + translate - logf(" %0.2f %0.2f", vec.x, vec.y ) - } - } - - v_offset := cast(u32) len(draw_list.vertices) - for point in path - { - transformed_point := Vertex { - pos = point.pos * scale + translate, - u = 0, - v = 0 - } - append( & draw_list.vertices, transformed_point ) - } - - if len(path) > 2 - { - indices := & draw_list.indices - for index : u32 = 1; index < cast(u32) len(path) - 1; index += 1 { - to_add := [3]u32 { - v_offset, - v_offset + index, - v_offset + index + 1 - } - append( indices, ..to_add[:] ) - } - - // Close the path by connecting the last vertex to the first two - to_add := [3]u32 { - v_offset, - v_offset + cast(u32)(len(path) - 1), - v_offset + 1 - } - append( indices, ..to_add[:] ) - } - } - - if glyph_index == Glyph(0) { - return false - } - - face := entry.parser_info.freetype_info - error := freetype.load_glyph(face, u32(glyph_index), {.No_Bitmap, .No_Scale}) - if error != .Ok { - return false - } - - glyph := face.glyph - if glyph.format != .Outline { - return false - } - - outline := &glyph.outline - if outline.n_points == 0 { - return false + // profile(#procedure) + v_offset := cast(u32) len(draw_list.vertices) + for point in path { + point := point + point.pos = point.pos * scale + translate + append( & draw_list.vertices, point ) } - draw := Draw_Call_Default - draw.pass = Frame_Buffer_Pass.Glyph - draw.start_index = cast(u32) len(ctx.draw_list.indices) - - contours := slice.from_ptr(cast( [^]i16) outline.contours, int(outline.n_contours)) - points := slice.from_ptr(cast( [^]freetype.Vector) outline.points, int(outline.n_points)) - tags := slice.from_ptr(cast( [^]u8) outline.tags, int(outline.n_points)) - - path := &ctx.temp_path - clear(path) - - outside := Vec2{ bounds_0.x - 21, bounds_0.y - 33 } - - start_index: int = 0 - for contour_index in 0 ..< int(outline.n_contours) + outside_vertex := cast(u32) len(draw_list.vertices) { - end_index := int(contours[contour_index]) + 1 - prev_point : Vec2 - first_point : Vec2 - - for idx := start_index; idx < end_index; idx += 1 - { - current_pos := Vec2 { f32( points[idx].x ), f32( points[idx].y ) } - if ( tags[idx] & 1 ) == 0 - { - // If current point is off-curve - if (idx == start_index || (tags[ idx - 1 ] & 1) != 0) - { - // current is the first or following an on-curve point - prev_point = current_pos - } - else - { - // current and previous are off-curve, calculate midpoint - midpoint := (prev_point + current_pos) * 0.5 - append( path, Vertex { pos = midpoint } ) // Add midpoint as on-curve point - if idx < end_index - 1 - { - // perform interp from prev_point to current_pos via midpoint - step := 1.0 / entry.curve_quality - for alpha : f32 = 0.0; alpha <= 1.0; alpha += step - { - bezier_point := eval_point_on_bezier3( prev_point, midpoint, current_pos, alpha ) - append( path, Vertex{ pos = bezier_point } ) - } - } - - prev_point = current_pos - } - } - else - { - if idx == start_index { - first_point = current_pos - } - if prev_point != (Vec2{}) { - // there was an off-curve point before this - append(path, Vertex{ pos = prev_point}) // Ensure previous off-curve is handled - } - append(path, Vertex{ pos = current_pos}) - prev_point = {} - } - } - - // ensure the contour is closed - if path[0].pos != path[ len(path) - 1 ].pos { - append(path, Vertex{pos = path[0].pos}) + vertex := Vertex { + pos = outside_point * scale + translate, + u = 0, + v = 0, } - draw_filled_path(&ctx.draw_list, bounds_0, path[:], scale, translate, ctx.debug_print_verbose) - clear(path) - start_index = end_index - } - - if len(path) > 0 { - draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose) + append( & draw_list.vertices, vertex ) } - draw.end_index = cast(u32) len(ctx.draw_list.indices) - if draw.end_index > draw.start_index { - append( & ctx.draw_list.calls, draw) + for index : u32 = 1; index < cast(u32) len(path); index += 1 { + indices := & draw_list.indices + to_add := [3]u32 { + outside_vertex, + v_offset + index - 1, + v_offset + index + } + append( indices, ..to_add[:] ) } - - return true } -// TODO(Ed): Is it better to cache the glyph vertices for when it must be re-drawn (directly or two atlas)? -cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry : ^Entry, bounds_0, bounds_1 : Vec2, scale, translate : Vec2) -> b32 +// Glyph triangulation generator +@(optimization_mode="favor_size") +generate_glyph_pass_draw_list :: proc(draw_list : ^Draw_List, path : ^[dynamic]Vertex, + glyph_shape : Parser_Glyph_Shape, + curve_quality : f32, + bounds : Range2, + translate, scale : Vec2 +) #no_bounds_check { - // profile(#procedure) - if glyph_index == Glyph(0) { - return false - } - - // Glyph shape handling are not abstractable between freetype and stb_truetype - if entry.parser_info.kind == .Freetype { - result := cache_glyph_freetype( ctx, font, glyph_index, entry, bounds_0, bounds_1, scale, translate ) - return result - } - - shape, error := parser_get_glyph_shape(&entry.parser_info, glyph_index) - assert(error == .None) - if len(shape) == 0 { - return false - } - - outside := Vec2{bounds_0.x - 21, bounds_0.y - 33} + profile(#procedure) + outside := Vec2{bounds.p0.x - 21, bounds.p0.y - 33} draw := Draw_Call_Default draw.pass = Frame_Buffer_Pass.Glyph - draw.start_index = u32(len(ctx.draw_list.indices)) + draw.start_index = u32(len(draw_list.indices)) - path := &ctx.temp_path clear(path) - step := 1.0 / entry.curve_quality - for edge in shape do #partial switch edge.type + step := 1.0 / curve_quality + for edge, index in glyph_shape do #partial switch edge.type { case .Move: if len(path) > 0 { - draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose) + fill_path_via_fan_triangulation( draw_list, outside, path[:], scale, translate) clear(path) } fallthrough @@ -291,7 +213,7 @@ cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry : p1 := Vec2{ f32(edge.contour_x0), f32(edge.contour_y0) } p2 := Vec2{ f32(edge.x), f32(edge.y) } - for index : f32 = 1; index <= entry.curve_quality; index += 1 { + for index : f32 = 1; index <= curve_quality; index += 1 { alpha := index * step append( path, Vertex { pos = eval_point_on_bezier3(p0, p1, p2, alpha) } ) } @@ -303,439 +225,573 @@ cache_glyph :: proc(ctx : ^Context, font : Font_ID, glyph_index : Glyph, entry : p2 := Vec2{ f32(edge.contour_x1), f32(edge.contour_y1) } p3 := Vec2{ f32(edge.x), f32(edge.y) } - for index : f32 = 1; index <= entry.curve_quality; index += 1 { + for index : f32 = 1; index <= curve_quality; index += 1 { alpha := index * step append( path, Vertex { pos = eval_point_on_bezier4(p0, p1, p2, p3, alpha) } ) } } if len(path) > 0 { - draw_filled_path(&ctx.draw_list, outside, path[:], scale, translate, ctx.debug_print_verbose) + fill_path_via_fan_triangulation(draw_list, outside, path[:], scale, translate) } - draw.end_index = u32(len(ctx.draw_list.indices)) + draw.end_index = u32(len(draw_list.indices)) if draw.end_index > draw.start_index { - append( & ctx.draw_list.calls, draw) + append( & draw_list.calls, draw) } - - parser_free_shape(&entry.parser_info, shape) - return true } -/* - Called by: - * can_batch_glyph : If it determines that the glyph was not detected and we haven't reached capacity in the atlas - * draw_text_shape : Glyph -*/ -cache_glyph_to_atlas :: proc( ctx : ^Context, - font : Font_ID, - glyph_index : Glyph, - lru_code : u64, - atlas_index : i32, - entry : ^Entry, - region_kind : Atlas_Region_Kind, - region : ^Atlas_Region, - over_sample : Vec2 ) +// Just a warpper of generate_shape_draw_list for handling an array of shapes +generate_shapes_draw_list :: #force_inline proc ( ctx : ^Context, + font : Font_ID, + colour : RGBAN, + entry : Entry, + px_size : f32, + font_scale : f32, + position : Vec2, + scale : Vec2, + shapes : []Shaped_Text +) { - // profile(#procedure) - - // Get hb_font text metrics. These are unscaled! - bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index ) - vbounds_0 := vec2(bounds_0) - vbounds_1 := vec2(bounds_1) - bounds_size := vbounds_1 - vbounds_0 - - // E region is special case and not cached to atlas. - if region_kind == .None || region_kind == .E do return - - // Grab an atlas LRU cache slot. - atlas_index := atlas_index - if atlas_index == -1 - { - if region.next_idx < region.state.capacity - { - evicted := lru_put( & region.state, lru_code, i32(region.next_idx) ) - atlas_index = i32(region.next_idx) - region.next_idx += 1 - assert( evicted == lru_code ) - } - else - { - next_evict_codepoint := lru_get_next_evicted( & region.state ) - assert( next_evict_codepoint != 0xFFFFFFFFFFFFFFFF ) - - atlas_index = lru_peek( & region.state, next_evict_codepoint, must_find = true ) - assert( atlas_index != -1 ) - - evicted := lru_put( & region.state, lru_code, atlas_index ) - assert( evicted == next_evict_codepoint ) - } - - assert( lru_get( & region.state, lru_code ) != - 1 ) + assert(len(shapes) > 0) + for shape in shapes { + ctx.cursor_pos = {} + ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, ctx.px_scalar, + colour, + entry, + px_size, + font_scale, + position, + scale, + ) } +} - atlas := & ctx.atlas - glyph_buffer := & ctx.glyph_buffer - atlas_size := Vec2 { f32(atlas.width), f32(atlas.height) } - glyph_buffer_size := Vec2 { f32(glyph_buffer.width), f32(glyph_buffer.height) } - glyph_padding := cast(f32) atlas.glyph_padding +/* Generator pipeline for shapes + This procedure has no awareness of layers. That should be handled by a higher-order codepath. - if ctx.debug_print + Pipleine order: + * Resolve the glyph's position offset from the target position + * Segregate the glyphs into three slices: oversized, to_cache, cached. + * If oversized is not necessary for your use case and your hitting a bottleneck, omit it with setting ENABLE_OVERSIZED_GLYPHS to false. + * The segregation will not allow slices to exceed the batch_cache capacity of the glyph_buffer (configurable within startup params) + * When The capacity is reached batch_generate_glyphs_draw_list will be called which will do futher compute and then finally draw_list generation. + * This may perform better with smaller shapes vs larger shapes, but having more shapes has a cache lookup penatly (if done per frame) so keep that in mind. +*/ +generate_shape_draw_list :: proc( draw_list : ^Draw_List, shape : Shaped_Text, + atlas : ^Atlas, + glyph_buffer : ^Glyph_Draw_Buffer, + px_scalar : f32, + + colour : RGBAN, + entry : Entry, + px_size : f32, + font_scale : f32, + + target_position : Vec2, + target_scale : Vec2, +) -> (cursor_pos : Vec2) #no_bounds_check +{ + profile(#procedure) + + mark_glyph_seen :: #force_inline proc "contextless" ( cache : ^Glyph_Batch_Cache, lru_code : Atlas_Key ) { + cache.table[lru_code] = true + cache.num += 1 + } + reset_batch :: #force_inline proc( cache : ^Glyph_Batch_Cache ) { + clear_map( & cache.table ) + cache.num = 0 + } + + atlas_glyph_pad := atlas.glyph_padding + atlas_size := vec2(atlas.size) + glyph_buffer_size := vec2(glyph_buffer.size) + + // Make sure the packs are large enough for the shape + glyph_pack := & glyph_buffer.glyph_pack + oversized := & glyph_buffer.oversized + to_cache := & glyph_buffer.to_cache + cached := & glyph_buffer.cached + resize_soa_non_zero(glyph_pack, len(shape.visible)) + + profile_begin("batching & segregating glyphs") + // We do any reservation up front as appending to the array's will not check. + reserve(oversized, len(shape.visible)) + reserve(to_cache, len(shape.visible)) + reserve(cached, len(shape.visible)) + clear(oversized) + clear(to_cache) + clear(cached) + reset_batch( & glyph_buffer.batch_cache) + + append_sub_pack :: #force_inline proc ( pack : ^[dynamic]i32, entry : i32 ) { - @static debug_total_cached : i32 = 0 - logf("glyph %v%v( %v ) caching to atlas region %v at idx %d. %d total glyphs cached.\n", - i32(glyph_index), rune(glyph_index), cast(rune) region_kind, atlas_index, debug_total_cached) - debug_total_cached += 1 + raw := cast(^Raw_Dynamic_Array) pack + raw.len += 1 + pack[len(pack) - 1] = entry } + sub_slice :: #force_inline proc "contextless" ( pack : ^[dynamic]i32) -> []i32 { return pack[:] } - // Draw oversized glyph to glyph render target (FBO) - glyph_draw_scale := over_sample * entry.size_scale - glyph_draw_translate := -1 * vbounds_0 * glyph_draw_scale + vec2( glyph_padding ) - - // Allocate a glyph glyph render target region (FBO) - gwidth_scaled_px := bounds_size.x * glyph_draw_scale.x + over_sample.x * glyph_padding + 1.0 - if i32(f32(glyph_buffer.batch_x) + gwidth_scaled_px) >= i32(glyph_buffer.width) { - flush_glyph_buffer_to_atlas( ctx ) - } - - // Calculate the src and destination regions - slot_position, slot_size := atlas_bbox( atlas, region_kind, atlas_index ) - - dst_glyph_position := slot_position - dst_glyph_size := (bounds_size * entry.size_scale + glyph_padding) - dst_size := (slot_size) - screenspace_x_form( & dst_glyph_position, & dst_glyph_size, atlas_size ) - screenspace_x_form( & slot_position, & dst_size, atlas_size ) - - src_position := Vec2 { f32(glyph_buffer.batch_x), 0 } - src_size := (bounds_size * glyph_draw_scale + over_sample * glyph_padding) - textspace_x_form( & src_position, & src_size, glyph_buffer_size ) - - // Advance glyph_update_batch_x and calculate final glyph drawing transform - glyph_draw_translate.x = (glyph_draw_translate.x + f32(glyph_buffer.batch_x)) - glyph_buffer.batch_x += i32(gwidth_scaled_px) - screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_size ) - - clear_target_region : Draw_Call - { - using clear_target_region - pass = .Atlas - region = .Ignore - start_index = cast(u32) len(glyph_buffer.clear_draw_list.indices) - - blit_quad( & glyph_buffer.clear_draw_list, - slot_position, slot_position + dst_size, - { 1.0, 1.0 }, { 1.0, 1.0 } ) - - end_index = cast(u32) len(glyph_buffer.clear_draw_list.indices) + profile_begin("translate") + for & glyph, index in glyph_pack { + // Throughout the draw list generation vis_id will need to be used over index as + // not all glyphs or positions for the shape are visibly rendered. + vis_id := shape.visible[index] + glyph.position = target_position + (shape.position[vis_id]) * target_scale } + profile_end() - blit_to_atlas : Draw_Call + for & glyph, index in glyph_pack { - using blit_to_atlas - pass = .Atlas - region = .None - start_index = cast(u32) len(glyph_buffer.draw_list.indices) - - blit_quad( & glyph_buffer.draw_list, - dst_glyph_position, slot_position + dst_glyph_size, - src_position, src_position + src_size ) - - end_index = cast(u32) len(glyph_buffer.draw_list.indices) - } - - append( & glyph_buffer.clear_draw_list.calls, clear_target_region ) - append( & glyph_buffer.draw_list.calls, blit_to_atlas ) - - // Render glyph to glyph render target (FBO) - cache_glyph( ctx, font, glyph_index, entry, vbounds_0, vbounds_1, glyph_draw_scale, glyph_draw_translate ) -} + // atlas_lru_code, region_kind, and bounds are all 1:1 with shape.visible + atlas_key := shape.atlas_lru_code[index] + region_kind := shape.region_kind[index] + bounds := shape.bounds[index] + bounds_size_scaled := size(bounds) * font_scale + + assert(region_kind != .None, "FAILED TO ASSGIN REGION") + when ENABLE_OVERSIZED_GLYPHS + { + if region_kind == .E + { + glyph.over_sample = \ + bounds_size_scaled.x <= glyph_buffer_size.x / 2 && + bounds_size_scaled.y <= glyph_buffer_size.y / 2 ? \ + {2.0, 2.0} \ + : {1.0, 1.0} + append_sub_pack(oversized, cast(i32) index) + continue + } + } + + glyph.over_sample = glyph_buffer.over_sample + region := atlas.regions[region_kind] + glyph.atlas_index = lru_get( & region.state, atlas_key ) -// If the glyuph is found in the atlas, nothing occurs, otherwise, the glyph call is setup to catch it to the atlas -check_glyph_in_atlas :: #force_inline proc( ctx : ^Context, font : Font_ID, entry : ^Entry, glyph_index : Glyph, - lru_code : u64, - atlas_index : i32, - region_kind : Atlas_Region_Kind, - region : ^Atlas_Region, - over_sample : Vec2 -) -> b32 -{ - // profile(#procedure) - assert( glyph_index != -1 ) + // Glyphs are prepared in batches based on the capacity of the batch cache. + Prepare_For_Batch: + { + pack := cached - // E region can't batch - if region_kind == .E || region_kind == .None do return false - if ctx.temp_codepoint_seen_num > 1024 do return false - // TODO(Ed): Why 1024? + found_take_slow_path : b8 + success : bool - if atlas_index == - 1 - { - if region.next_idx > region.state.capacity { - // We will evict LRU. We must predict which LRU will get evicted, and if it's something we've seen then we need to take slowpath and flush batch. - next_evict_codepoint := lru_get_next_evicted( & region.state ) - seen, success := ctx.temp_codepoint_seen[next_evict_codepoint] - assert(success != false) - - if (seen) { - return false + // Determine if we hit the limit for this batch. + if glyph.atlas_index == - 1 + { + // Check to see if we reached capacity for the atlas + if region.next_idx > region.state.capacity + { + // We will evict LRU. We must predict which LRU will get evicted, and if it's something we've seen then we need to take slowpath and flush batch. + next_evict_glyph := lru_get_next_evicted( region.state ) + found_take_slow_path, success = glyph_buffer.batch_cache.table[next_evict_glyph] + assert(success != false) + // TODO(Ed): This might not be needed with the new pipeline/batching + if (found_take_slow_path) { + break Prepare_For_Batch + } + } + // profile_begin("glyph needs caching") + glyph.atlas_index = atlas_reserve_slot(region, atlas_key) + pack = to_cache + // profile_end() } + // profile("append cached") + glyph.region_pos, glyph.region_size = atlas_region_bbox(region ^, glyph.atlas_index) + mark_glyph_seen(& glyph_buffer.batch_cache, atlas_key) + append_sub_pack(pack, cast(i32) index) + // TODO(Ed): This might not be needed with the new pipeline/batching + // if (found_take_slow_path) { + // break Prepare_For_Batch + // } + if glyph_buffer.batch_cache.num >= glyph_buffer.batch_cache.cap do break Prepare_For_Batch + continue } - cache_glyph_to_atlas( ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample ) - } - - assert( lru_get( & region.state, lru_code ) != -1 ) - mark_batch_codepoint_seen( ctx, lru_code) - return true + // Batch has been prepared for a set of glyphs time to generate glyphs. + batch_generate_glyphs_draw_list( draw_list, shape, glyph_pack, sub_slice(cached), sub_slice(to_cache), sub_slice(oversized), + atlas, + glyph_buffer, + atlas_size, + glyph_buffer_size, + entry, + colour, + font_scale, + target_scale + ) + + reset_batch( & glyph_buffer.batch_cache) + clear(oversized) + clear(to_cache) + clear(cached) + } + profile_end() + + if len(oversized) > 0 || glyph_buffer.batch_cache.num > 0 + { + // Last batch pass + batch_generate_glyphs_draw_list( draw_list, shape, glyph_pack, sub_slice(cached), sub_slice(to_cache), sub_slice(oversized), + atlas, + glyph_buffer, + atlas_size, + glyph_buffer_size, + entry, + colour, + font_scale, + target_scale, + ) + } + + cursor_pos = target_position + shape.end_cursor_pos * target_scale + return } -// ve_fontcache_clear_Draw_List -clear_draw_list :: #force_inline proc ( draw_list : ^Draw_List ) { - clear( & draw_list.calls ) - clear( & draw_list.indices ) - clear( & draw_list.vertices ) -} +/* + The glyphs types have been segregated by this point into a batch slice of indices to the glyph_pack + The transform and draw quads are computed first (getting the math done in one spot as possible) + Some of the math from to_cache pass for glyph generation was not moved over (it could be but I'm not sure its worth it) -directly_draw_massive_glyph :: proc( ctx : ^Context, - entry : ^Entry, - glyph : Glyph, - bounds_0, bounds_1 : Vec2, - bounds_size : Vec2, - over_sample, position, scale : Vec2 ) -{ - // profile(#procedure) - flush_glyph_buffer_to_atlas( ctx ) + Order : Oversized first, then to_cache, then cached. + Important: These slices store ids for glyph_pack which matches shape.visible in index. + shape.position and shape.glyph DO NOT. + + There are only two places this matters for: getting glyph shapes when doing glyph pass generation for oversized and to_cache iterations. - glyph_padding := f32(ctx.atlas.glyph_padding) - glyph_buffer_size := Vec2 { f32(ctx.glyph_buffer.width), f32(ctx.glyph_buffer.height) } + Oversized and to_cache will both enqueue operations for rendering glyphs to the glyph buffer render target. + The compute section will have operations regarding how many glyphs they may alloate before a flush must occur. + A flush will force one of the following: + * Oversized will have a draw call setup to blit directly from the glyph buffer to the target. + * to_cache will blit the glyphs rendered from the buffer to the atlas. +*/ +@(optimization_mode = "favor_size") +batch_generate_glyphs_draw_list :: proc ( draw_list : ^Draw_List, + shape : Shaped_Text, + glyph_pack : ^#soa[dynamic]Glyph_Pack_Entry, + cached : []i32, + to_cache : []i32, + oversized : []i32, + + atlas : ^Atlas, + glyph_buffer : ^Glyph_Draw_Buffer, + atlas_size : Vec2, + glyph_buffer_size : Vec2, + + entry : Entry, + colour : RGBAN, + font_scale : Vec2, + target_scale : Vec2, +) #no_bounds_check +{ + profile(#procedure) + colour := colour - // Draw un-antialiased glyph to update FBO. - glyph_draw_scale := over_sample * entry.size_scale - glyph_draw_translate := -1 * bounds_0 * glyph_draw_scale + vec2_from_scalar(glyph_padding) - screenspace_x_form( & glyph_draw_translate, & glyph_draw_scale, glyph_buffer_size ) + profile_begin("glyph transform & draw quads compute") + for id, index in cached + { + // Quad to for drawing atlas slot to target + glyph := & glyph_pack[id] + bounds := shape.bounds[id] + bounds_scaled := mul(bounds, font_scale) + glyph_scale := size(bounds_scaled) + atlas.glyph_padding + + quad := & glyph.draw_quad + quad.dst_pos = glyph.position + (bounds_scaled.p0) * target_scale + quad.dst_scale = (glyph_scale) * target_scale + quad.src_scale = (glyph_scale) + quad.src_pos = (glyph.region_pos) + to_target_space( & quad.src_pos, & quad.src_scale, atlas_size ) + } + for id, index in to_cache + { + glyph := & glyph_pack[id] + bounds := shape.bounds[id] + bounds_scaled := mul(bounds, font_scale) + glyph_scale := size(bounds_scaled) + glyph_buffer.draw_padding - cache_glyph( ctx, entry.id, glyph, entry, bounds_0, bounds_1, glyph_draw_scale, glyph_draw_translate ) + f32_allocated_x := cast(f32) glyph_buffer.allocated_x - glyph_padding_dbl := glyph_padding * 2 - bounds_scaled := bounds_size * entry.size_scale + // Resolve how much space this glyph will allocate in the buffer + buffer_size := glyph_scale * glyph_buffer.over_sample + // Allocate a glyph glyph render target region (FBO) + to_allocate_x := buffer_size.x + 4.0 - // Figure out the source rect. - glyph_position := Vec2 {} - glyph_size := vec2(glyph_padding) - glyph_dst_size := glyph_size + bounds_scaled - glyph_size += bounds_scaled * over_sample + // If allocation would exceed buffer's bounds the buffer must be flush before this glyph can be rendered. + glyph.flush_glyph_buffer = i32(f32_allocated_x + to_allocate_x) >= i32(glyph_buffer_size.x) + glyph.buffer_x = f32_allocated_x * f32( i32( ! glyph.flush_glyph_buffer ) ) - // Figure out the destination rect. - bounds_0_scaled := (bounds_0 * entry.size_scale) - dst := position + scale * bounds_0_scaled - glyph_padding * scale - dst_size := glyph_dst_size * scale - textspace_x_form( & glyph_position, & glyph_size, glyph_buffer_size ) + // The glyph buffer space transform for generate_glyph_pass_draw_list + draw_transform := & glyph.draw_transform + draw_transform.scale = font_scale * glyph_buffer.over_sample + draw_transform.pos = -1 * (bounds.p0) * draw_transform.scale + glyph_buffer.draw_padding + draw_transform.pos.x += glyph.buffer_x + to_glyph_buffer_space( & draw_transform.pos, & draw_transform.scale, glyph_buffer_size ) - // Add the glyph Draw_Call. - calls : [2]Draw_Call + // Allocate the space + glyph_buffer.allocated_x += i32(to_allocate_x) - draw_to_target := & calls[0] - { - using draw_to_target - pass = .Target_Uncached - colour = ctx.colour - start_index = u32(len(ctx.draw_list.indices)) + // Quad to for drawing atlas slot to target (used in generate_cached_draw_list) + draw_quad := & glyph.draw_quad - blit_quad( & ctx.draw_list, - dst, dst + dst_size, - glyph_position, glyph_position + glyph_size ) + // Destination (draw_list's target image) + draw_quad.dst_pos = glyph.position + (bounds_scaled.p0) * target_scale + draw_quad.dst_scale = (glyph_scale) * target_scale - end_index = u32(len(ctx.draw_list.indices)) + // UV Coordinates for sampling the atlas + draw_quad.src_scale = (glyph_scale) + draw_quad.src_pos = (glyph.region_pos) + to_target_space( & draw_quad.src_pos, & draw_quad.src_scale, atlas_size ) } - - clear_glyph_update := & calls[1] + when ENABLE_OVERSIZED_GLYPHS do for id, index in oversized { - // Clear glyph render target (FBO) - clear_glyph_update.pass = .Glyph - clear_glyph_update.start_index = 0 - clear_glyph_update.end_index = 0 - clear_glyph_update.clear_before_draw = true - } - append( & ctx.draw_list.calls, ..calls[:] ) -} - -// Constructs a triangle fan to fill a shape using the provided path -// outside_point represents the center point of the fan. -// -// Note(Original Author): -// WARNING: doesn't actually append Draw_Call; caller is responsible for actually appending the Draw_Call. -// ve_fontcache_draw_filled_path -draw_filled_path :: proc( draw_list : ^Draw_List, outside_point : Vec2, path : []Vertex, - scale := Vec2 { 1, 1 }, - translate := Vec2 { 0, 0 }, - debug_print_verbose : b32 = false -) -{ - if debug_print_verbose + glyph_padding := vec2(glyph_buffer.draw_padding) + glyph := & glyph_pack[id] + bounds := shape.bounds[id] + bounds_scaled := mul(bounds, font_scale) + bounds_size_scaled := size(bounds_scaled) + + f32_allocated_x := cast(f32) glyph_buffer.allocated_x + // Resolve how much space this glyph will allocate in the buffer + buffer_size := (bounds_size_scaled + glyph_padding) * glyph.over_sample + + // Allocate a glyph glyph render target region (FBO) + to_allocate_x := buffer_size.x + 2.0 + glyph_buffer.allocated_x += i32(to_allocate_x) + + // If allocation would exceed buffer's bounds the buffer must be flush before this glyph can be rendered. + glyph.flush_glyph_buffer = i32(f32_allocated_x + to_allocate_x) >= i32(glyph_buffer_size.x) + glyph.buffer_x = f32_allocated_x * f32( i32( ! glyph.flush_glyph_buffer ) ) + + // Quad to for drawing atlas slot to target + draw_quad := & glyph.draw_quad + + // Target position (draw_list's target image) + draw_quad.dst_pos = glyph.position + (bounds_scaled.p0 - glyph_padding) * target_scale + draw_quad.dst_scale = (bounds_size_scaled + glyph_padding) * target_scale + + // The glyph buffer space transform for generate_glyph_pass_draw_list + draw_transform := & glyph.draw_transform + draw_transform.scale = font_scale * glyph.over_sample + draw_transform.pos = -1 * bounds.p0 * draw_transform.scale + vec2(atlas.glyph_padding) + draw_transform.pos.x += glyph.buffer_x + to_glyph_buffer_space( & draw_transform.pos, & draw_transform.scale, glyph_buffer_size ) + + + draw_quad.src_pos = Vec2 { glyph.buffer_x, 0 } + draw_quad.src_scale = bounds_size_scaled * glyph.over_sample + glyph_padding + to_target_space( & draw_quad.src_pos, & draw_quad.src_scale, glyph_buffer_size ) + } + profile_end() + + profile_begin("gen oversized glyphs draw_list") + when ENABLE_OVERSIZED_GLYPHS do if len(oversized) > 0 { - log("outline_path:") - for point in path { - vec := point.pos * scale + translate - logf(" %0.2f %0.2f", vec.x, vec.y ) + when ENABLE_DRAW_TYPE_VISUALIZATION { + colour.r = 1.0 + colour.g = 1.0 + colour.b = 0.0 } - } + for pack_id, index in oversized { + vis_id := shape.visible[pack_id] + error : Allocator_Error + glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[vis_id]) + assert(error == .None) + assert(glyph_pack[pack_id].shape != nil) + } + for id, index in oversized + { + glyph := & glyph_pack[id] + bounds := shape.bounds[id] + if glyph.flush_glyph_buffer do flush_glyph_buffer_draw_list(draw_list, + & glyph_buffer.draw_list, + & glyph_buffer.clear_draw_list, + & glyph_buffer.allocated_x + ) + + generate_glyph_pass_draw_list( draw_list, & glyph_buffer.shape_gen_scratch, + glyph_pack[id].shape, + entry.curve_quality, + bounds, + glyph_pack[id].draw_transform.pos, + glyph_pack[id].draw_transform.scale + ) + + target_quad := & glyph_pack[id].draw_quad + + draw_to_target : Draw_Call + { + draw_to_target.pass = .Target_Uncached + draw_to_target.colour = colour + draw_to_target.start_index = u32(len(draw_list.indices)) - v_offset := cast(u32) len(draw_list.vertices) - for point in path { - point := point - point.pos = point.pos * scale + translate - append( & draw_list.vertices, point ) - } + blit_quad( draw_list, + target_quad.dst_pos, target_quad.dst_pos + target_quad.dst_scale, + target_quad.src_pos, target_quad.src_pos + target_quad.src_scale ) - outside_vertex := cast(u32) len(draw_list.vertices) - { - vertex := Vertex { - pos = outside_point * scale + translate, - u = 0, - v = 0, + draw_to_target.end_index = u32(len(draw_list.indices)) + } + append( & draw_list.calls, draw_to_target ) } - append( & draw_list.vertices, vertex ) - } - for index : u32 = 1; index < cast(u32) len(path); index += 1 { - indices := & draw_list.indices - to_add := [3]u32 { - outside_vertex, - v_offset + index - 1, - v_offset + index + flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.allocated_x) + for pack_id, index in oversized { + assert(glyph_pack[pack_id].shape != nil) + parser_free_shape(entry.parser_info, glyph_pack[pack_id].shape) } - append( indices, ..to_add[:] ) } -} - -draw_text_batch :: proc(ctx: ^Context, entry: ^Entry, shaped: ^Shaped_Text, - batch_start_idx, batch_end_idx : i32, - position, scale : Vec2, - snap_width, snap_height : f32 ) -{ - flush_glyph_buffer_to_atlas(ctx) - - atlas := & ctx.atlas - atlas_size := Vec2{ f32(atlas.width), f32(atlas.height) } - glyph_padding := f32(atlas.glyph_padding) + profile_end() - for index := batch_start_idx; index < batch_end_idx; index += 1 + @(optimization_mode = "favor_size") + generate_blit_from_atlas_draw_list :: #force_inline proc (draw_list : ^Draw_List, glyph_pack : #soa[]Glyph_Pack_Entry, sub_pack : []i32, colour : RGBAN ) { - glyph_index := shaped.glyphs[index] - - if glyph_index == 0 || parser_is_glyph_empty( & entry.parser_info, glyph_index) do continue - - region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index ) - lru_code := font_glyph_lru_code( entry.id, glyph_index ) - atlas_index := region_kind != .E ? lru_get( & region.state, lru_code ) : -1 - bounds_0, bounds_1 := parser_get_glyph_box( & entry.parser_info, glyph_index ) - vbounds_0 := vec2(bounds_0) - vbounds_1 := vec2(bounds_1) - bounds_size := vbounds_1 - vbounds_0 - - shaped_position := shaped.positions[index] - glyph_translate := position + (shaped_position) * scale - - if region_kind == .E - { - directly_draw_massive_glyph(ctx, entry, glyph_index, - vbounds_0, vbounds_1, - bounds_size, - over_sample, glyph_translate, scale ) - } - else if atlas_index != -1 + profile(#procedure) + call := Draw_Call_Default + call.pass = .Target + call.colour = colour + for id, index in sub_pack { - // Draw cached glyph - slot_position, _ := atlas_bbox( atlas, region_kind, atlas_index ) - glyph_scale := bounds_size * entry.size_scale + glyph_padding - bounds_0_scaled := ceil(vbounds_0 * entry.size_scale - 0.5 ) - dst := glyph_translate + bounds_0_scaled * scale - dst_scale := glyph_scale * scale - textspace_x_form( & slot_position, & glyph_scale, atlas_size ) - - call := Draw_Call_Default - call.pass = .Target - call.colour = ctx.colour - call.start_index = u32(len(ctx.draw_list.indices)) - - blit_quad(&ctx.draw_list, - dst, dst + dst_scale, - slot_position, slot_position + glyph_scale ) - - call.end_index = u32(len(ctx.draw_list.indices)) - append(&ctx.draw_list.calls, call) + // profile("glyph") + call.start_index = u32(len(draw_list.indices)) + + quad := glyph_pack[id].draw_quad + blit_quad(draw_list, + quad.dst_pos, quad.dst_pos + quad.dst_scale, + quad.src_pos, quad.src_pos + quad.src_scale + ) + call.end_index = u32(len(draw_list.indices)) + append(& draw_list.calls, call) } } -} -// Helper for draw_text, all raw text content should be confirmed to be either formatting or visible shapes before getting cached. -draw_text_shape :: proc( ctx : ^Context, - font : Font_ID, - entry : ^Entry, - shaped : ^Shaped_Text, - position, scale : Vec2, - snap_width, snap_height : f32 -) -> (cursor_pos : Vec2) -{ - // profile(#procedure) - batch_start_idx : i32 = 0 - for index : i32 = 0; index < cast(i32) len(shaped.glyphs); index += 1 + profile_begin("to_cache: caching to atlas") + if len(to_cache) > 0 { - glyph_index := shaped.glyphs[ index ] - if is_empty( ctx, entry, glyph_index ) do continue - - region_kind, region, over_sample := decide_codepoint_region( ctx, entry, glyph_index ) - lru_code := font_glyph_lru_code(entry.id, glyph_index) - atlas_index := cast(i32) -1 - - if region_kind != .E do atlas_index = lru_get( & region.state, lru_code ) - if check_glyph_in_atlas( ctx, font, entry, glyph_index, lru_code, atlas_index, region_kind, region, over_sample ) do continue - - // We can no longer directly append the shape as it has missing glyphs in the atlas + for pack_id, index in to_cache { + vis_id := shape.visible[pack_id] + error : Allocator_Error + glyph_pack[pack_id].shape, error = parser_get_glyph_shape(entry.parser_info, shape.glyph[vis_id]) + assert(error == .None) + assert(glyph_pack[pack_id].shape != nil) + } - // First batch the other cached glyphs - // flush_glyph_buffer_to_atlas(ctx) - draw_text_batch( ctx, entry, shaped, batch_start_idx, index, position, scale, snap_width, snap_height ) - reset_batch_codepoint_state( ctx ) + for id, index in to_cache + { + // profile("glyph") + glyph := & glyph_pack[id] + bounds := shape.bounds[id] + bounds_scaled := mul(bounds, font_scale) + bounds_size_scaled := size(bounds_scaled) + + if glyph.flush_glyph_buffer do flush_glyph_buffer_draw_list( draw_list, + & glyph_buffer.draw_list, + & glyph_buffer.clear_draw_list, + & glyph_buffer.allocated_x + ) + + dst_region_pos := glyph.region_pos + dst_region_size := glyph.region_size + to_glyph_buffer_space( & dst_region_pos, & dst_region_size, atlas_size ) + + clear_target_region : Draw_Call + clear_target_region.pass = .Atlas + clear_target_region.region = .Ignore + clear_target_region.start_index = cast(u32) len(glyph_buffer.clear_draw_list.indices) + blit_quad( & glyph_buffer.clear_draw_list, + dst_region_pos, dst_region_pos + dst_region_size, + { 1.0, 1.0 }, { 1.0, 1.0 } + ) + clear_target_region.end_index = cast(u32) len(glyph_buffer.clear_draw_list.indices) + + dst_glyph_pos := glyph.region_pos + dst_glyph_size := bounds_size_scaled + atlas.glyph_padding + dst_glyph_size.x = ceil(dst_glyph_size.x) + dst_glyph_size.y = max(dst_glyph_size.y, ceil(dst_glyph_size.y) * glyph_buffer.snap_glyph_height) // Note(Ed): Seems to improve hinting + to_glyph_buffer_space( & dst_glyph_pos, & dst_glyph_size, atlas_size ) + + src_position := Vec2 { glyph.buffer_x, 0 } + src_size := (bounds_size_scaled + atlas.glyph_padding) * glyph_buffer.over_sample + src_size.x = ceil(src_size.x) + src_size.y = max(src_size.y, ceil(src_size.y) * glyph_buffer.snap_glyph_height) // Note(Ed): Seems to improve hinting + to_target_space( & src_position, & src_size, glyph_buffer_size ) + + blit_to_atlas : Draw_Call + blit_to_atlas.pass = .Atlas + blit_to_atlas.region = .None + blit_to_atlas.start_index = cast(u32) len(glyph_buffer.draw_list.indices) + blit_quad( & glyph_buffer.draw_list, + dst_glyph_pos, dst_glyph_pos + dst_glyph_size, + src_position, src_position + src_size ) + blit_to_atlas.end_index = cast(u32) len(glyph_buffer.draw_list.indices) + + append( & glyph_buffer.clear_draw_list.calls, clear_target_region ) + append( & glyph_buffer.draw_list.calls, blit_to_atlas ) + + // Render glyph to glyph render target (FBO) + generate_glyph_pass_draw_list( draw_list, & glyph_buffer.shape_gen_scratch, + glyph.shape, + entry.curve_quality, + bounds, + glyph.draw_transform.pos, + glyph.draw_transform.scale + ) + } - cache_glyph_to_atlas( ctx, font, glyph_index, lru_code, atlas_index, entry, region_kind, region, over_sample ) - mark_batch_codepoint_seen( ctx, lru_code) - batch_start_idx = index + flush_glyph_buffer_draw_list(draw_list, & glyph_buffer.draw_list, & glyph_buffer.clear_draw_list, & glyph_buffer.allocated_x) + for pack_id, index in to_cache { + assert(glyph_pack[pack_id].shape != nil) + parser_free_shape(entry.parser_info, glyph_pack[pack_id].shape) + } + + profile_begin("gen_cached_draw_list: to_cache") + when ENABLE_DRAW_TYPE_VISUALIZATION { + colour.r = 1.0 + colour.g = 0.0 + colour.b = 0.0 + } + generate_blit_from_atlas_draw_list( draw_list, glyph_pack[:], to_cache, colour ) + profile_end() } + profile_end() - draw_text_batch( ctx, entry, shaped, batch_start_idx, cast(i32) len(shaped.glyphs), position, scale, snap_width , snap_height ) - reset_batch_codepoint_state( ctx ) - - cursor_pos = position + shaped.end_cursor_pos * scale - return + profile_begin("gen_cached_draw_list: cached") + when ENABLE_DRAW_TYPE_VISUALIZATION { + colour.r = 0.5 + colour.g = 0.5 + colour.b = 0.5 + } + generate_blit_from_atlas_draw_list( draw_list, glyph_pack[:], cached, colour ) + profile_end() } -flush_glyph_buffer_to_atlas :: proc( ctx : ^Context ) +// Flush the content of the glyph_buffers draw lists to the main draw list +flush_glyph_buffer_draw_list :: proc( #no_alias draw_list, glyph_buffer_draw_list, glyph_buffer_clear_draw_list : ^Draw_List, allocated_x : ^i32 ) { - // profile(#procedure) - // Flush Draw_Calls to draw list - merge_draw_list( & ctx.draw_list, & ctx.glyph_buffer.clear_draw_list ) - merge_draw_list( & ctx.draw_list, & ctx.glyph_buffer.draw_list) - clear_draw_list( & ctx.glyph_buffer.draw_list ) - clear_draw_list( & ctx.glyph_buffer.clear_draw_list ) - - // Clear glyph render target (FBO) - if ctx.glyph_buffer.batch_x != 0 - { - call := Draw_Call_Default - call.pass = .Glyph - call.start_index = 0 - call.end_index = 0 - call.clear_before_draw = true - append( & ctx.draw_list.calls, call ) - ctx.glyph_buffer.batch_x = 0 - } + profile(#procedure) + merge_draw_list( draw_list, glyph_buffer_clear_draw_list ) + merge_draw_list( draw_list, glyph_buffer_draw_list) + clear_draw_list( glyph_buffer_draw_list ) + clear_draw_list( glyph_buffer_clear_draw_list ) + + call := Draw_Call_Default + call.pass = .Glyph + call.start_index = 0 + call.end_index = 0 + call.clear_before_draw = true + append( & draw_list.calls, call ) + (allocated_x ^) = 0 } -// ve_fontcache_merge_Draw_List -merge_draw_list :: proc( dst, src : ^Draw_List ) +@(optimization_mode="favor_size") +clear_draw_list :: #force_inline proc ( draw_list : ^Draw_List ) { + clear( & draw_list.calls ) + clear( & draw_list.indices ) + clear( & draw_list.vertices ) +} + +// Helper used by flush_glyph_buffer_draw_list. Used to append all the content from the src draw list o the destination. +@(optimization_mode="favor_size") +merge_draw_list :: proc ( #no_alias dst, src : ^Draw_List ) #no_bounds_check { - // profile(#procedure) + profile(#procedure) error : Allocator_Error v_offset := cast(u32) len( dst.vertices ) @@ -744,13 +800,13 @@ merge_draw_list :: proc( dst, src : ^Draw_List ) assert( error == .None ) i_offset := cast(u32) len(dst.indices) - for index : int = 0; index < len(src.indices); index += 1 { + for index : i32 = 0; index < cast(i32) len(src.indices); index += 1 { ignored : int ignored, error = append( & dst.indices, src.indices[index] + v_offset ) assert( error == .None ) } - for index : int = 0; index < len(src.calls); index += 1 { + for index : i32 = 0; index < cast(i32) len(src.calls); index += 1 { src_call := src.calls[ index ] src_call.start_index += i_offset src_call.end_index += i_offset @@ -759,9 +815,11 @@ merge_draw_list :: proc( dst, src : ^Draw_List ) } } -optimize_draw_list :: proc(draw_list: ^Draw_List, call_offset: int) +// Naive implmentation to merge passes that are equivalent and the following to be merged (b for can_merge_draw_calls) doesn't have a clear todo. +// Its intended for optimiztion passes to occur on a per-layer basis. +optimize_draw_list :: proc (draw_list: ^Draw_List, call_offset: int) #no_bounds_check { - // profile(#procedure) + profile(#procedure) assert(draw_list != nil) can_merge_draw_calls :: #force_inline proc "contextless" ( a, b : ^Draw_Call ) -> bool { diff --git a/vefontcache/misc.odin b/vefontcache/misc.odin index e34fbf0..39f847b 100644 --- a/vefontcache/misc.odin +++ b/vefontcache/misc.odin @@ -1,16 +1,56 @@ package vefontcache -import "base:runtime" +/* + Didn't want to splinter this into more files.. + Just a bunch of utilities. +*/ + import "core:simd" -import "core:math" import core_log "core:log" -Colour :: [4]f32 +peek_array :: #force_inline proc "contextless" ( self : [dynamic]$Type ) -> Type { + return self[ len(self) - 1 ] +} + +reload_array :: #force_inline proc( self : ^[dynamic]$Type, allocator : Allocator ) { + raw := transmute( ^Raw_Dynamic_Array) self + raw.allocator = allocator +} + +reload_array_soa :: #force_inline proc( self : ^#soa[dynamic]$Type, allocator : Allocator ) { + raw := raw_soa_footer(self) + raw.allocator = allocator +} + +reload_map :: #force_inline proc( self : ^map [$KeyType] $EntryType, allocator : Allocator ) { + raw := transmute( ^Raw_Map) self + raw.allocator = allocator +} + +to_bytes :: #force_inline proc "contextless" ( typed_data : ^$Type ) -> []byte { return slice_ptr( transmute(^byte) typed_data, size_of(Type) ) } + +@(optimization_mode="favor_size") +djb8_hash :: #force_inline proc "contextless" ( hash : ^$Type, bytes : []byte ) { for value in bytes do (hash^) = (( (hash^) << 8) + (hash^) ) + Type(value) } + +RGBA8 :: [4]u8 +RGBAN :: [4]f32 Vec2 :: [2]f32 Vec2i :: [2]i32 Vec2_64 :: [2]f64 +Transform :: struct { + pos : Vec2, + scale : Vec2, +} + +Range2 :: struct { + p0, p1 : Vec2, +} + +mul_range2_vec2 :: #force_inline proc "contextless" ( range : Range2, v : Vec2 ) -> Range2 { return { range.p0 * v, range.p1 * v } } +size_range2 :: #force_inline proc "contextless" ( range : Range2 ) -> Vec2 { return range.p1 - range.p0 } + vec2_from_scalar :: #force_inline proc "contextless" ( scalar : f32 ) -> Vec2 { return { scalar, scalar }} vec2_64_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2_64 { return { f64(v2.x), f64(v2.y) }} vec2_from_vec2i :: #force_inline proc "contextless" ( v2i : Vec2i ) -> Vec2 { return { f32(v2i.x), f32(v2i.y) }} @@ -19,7 +59,7 @@ vec2i_from_vec2 :: #force_inline proc "contextless" ( v2 : Vec2 ) -> Vec2 @(require_results) ceil_vec2 :: proc "contextless" ( v : Vec2 ) -> Vec2 { return { ceil_f32(v.x), ceil_f32(v.y) } } @(require_results) floor_vec2 :: proc "contextless" ( v : Vec2 ) -> Vec2 { return { floor_f32(v.x), floor_f32(v.y) } } -// This buffer is used below excluisvely to prevent any allocator recusion when verbose logging from allocators. +// This buffer is used below excluisvely to prevent any allocator recursion when verbose logging from allocators. // This means a single line is limited to 4k buffer // Logger_Allocator_Buffer : [4 * Kilobyte]u8 @@ -39,91 +79,29 @@ logf :: proc( fmt : string, args : ..any, level := core_log.Level.Info, loc := core_log.logf( level, fmt, ..args, location = loc ) } -reload_array :: proc( self : ^[dynamic]$Type, allocator : Allocator ) { - raw := transmute( ^runtime.Raw_Dynamic_Array) self - raw.allocator = allocator -} - -reload_map :: proc( self : ^map [$KeyType] $EntryType, allocator : Allocator ) { - raw := transmute( ^runtime.Raw_Map) self - raw.allocator = allocator -} - -font_glyph_lru_code :: #force_inline proc "contextless" ( font : Font_ID, glyph_index : Glyph ) -> (lru_code : u64) { - lru_code = u64(glyph_index) + ( ( 0x100000000 * u64(font) ) & 0xFFFFFFFF00000000 ) - return -} - -is_empty :: #force_inline proc ( ctx : ^Context, entry : ^Entry, glyph_index : Glyph ) -> b32 -{ - if glyph_index == 0 do return true - if parser_is_glyph_empty( & entry.parser_info, glyph_index ) do return true - return false -} - -mark_batch_codepoint_seen :: #force_inline proc ( ctx : ^Context, lru_code : u64 ) { - ctx.temp_codepoint_seen[lru_code] = true - ctx.temp_codepoint_seen_num += 1 -} - -reset_batch_codepoint_state :: #force_inline proc( ctx : ^Context ) { - clear_map( & ctx.temp_codepoint_seen ) - ctx.temp_codepoint_seen_num = 0 -} - -USE_F64_PRECISION_ON_X_FORM_OPS :: false - -screenspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 ) +@(optimization_mode="favor_size") +to_glyph_buffer_space :: #force_inline proc "contextless" ( #no_alias position, scale : ^Vec2, size : Vec2 ) { - when USE_F64_PRECISION_ON_X_FORM_OPS - { - pos_64 := vec2_64_from_vec2(position^) - scale_64 := vec2_64_from_vec2(scale^) - - quotient : Vec2_64 = 1.0 / vec2_64(size) - pos_64 = pos_64 * quotient * 2.0 - 1.0 - scale_64 = scale_64 * quotient * 2.0 + pos := position^ + scale_32 := scale^ - (position^) = { f32(pos_64.x), f32(pos_64.y) } - (scale^) = { f32(scale_64.x), f32(scale_64.y) } - } - else - { - pos := position^ - scale_32 := scale^ - - quotient : Vec2 = 1.0 / size - pos = pos * quotient * 2.0 - 1.0 - scale_32 = scale_32 * quotient * 2.0 + quotient : Vec2 = 1.0 / size + pos = pos * quotient * 2.0 - 1.0 + scale_32 = scale_32 * quotient * 2.0 - (position^) = pos - (scale^) = scale_32 - } + (position^) = pos + (scale^) = scale_32 } -textspace_x_form :: #force_inline proc "contextless" ( position, scale : ^Vec2, size : Vec2 ) +@(optimization_mode="favor_size") +to_target_space :: #force_inline proc "contextless" ( #no_alias position, scale : ^Vec2, size : Vec2 ) { - when USE_F64_PRECISION_ON_X_FORM_OPS - { - pos_64 := vec2_64_from_vec2(position^) - scale_64 := vec2_64_from_vec2(scale^) - - quotient : Vec2_64 = 1.0 / vec2_64(size) - pos_64 *= quotient - scale_64 *= quotient - - (position^) = { f32(pos_64.x), f32(pos_64.y) } - (scale^) = { f32(scale_64.x), f32(scale_64.y) } - } - else - { - quotient : Vec2 = 1.0 / size - (position^) *= quotient - (scale^) *= quotient - } + quotient : Vec2 = 1.0 / size + (position^) *= quotient + (scale^) *= quotient } -USE_MANUAL_SIMD_FOR_BEZIER_OPS :: false +USE_MANUAL_SIMD_FOR_BEZIER_OPS :: true when ! USE_MANUAL_SIMD_FOR_BEZIER_OPS { @@ -132,11 +110,6 @@ when ! USE_MANUAL_SIMD_FOR_BEZIER_OPS // ve_fontcache_eval_bezier (quadratic) eval_point_on_bezier3 :: #force_inline proc "contextless" ( p0, p1, p2 : Vec2, alpha : f32 ) -> Vec2 { - // p0 := vec2_64(p0) - // p1 := vec2_64(p1) - // p2 := vec2_64(p2) - // alpha := f64(alpha) - weight_start := (1 - alpha) * (1 - alpha) weight_control := 2.0 * (1 - alpha) * alpha weight_end := alpha * alpha @@ -154,12 +127,6 @@ when ! USE_MANUAL_SIMD_FOR_BEZIER_OPS // ve_fontcache_eval_bezier (cubic) eval_point_on_bezier4 :: #force_inline proc "contextless" ( p0, p1, p2, p3 : Vec2, alpha : f32 ) -> Vec2 { - // p0 := vec2_64(p0) - // p1 := vec2_64(p1) - // p2 := vec2_64(p2) - // p3 := vec2_64(p3) - // alpha := f64(alpha) - weight_start := (1 - alpha) * (1 - alpha) * (1 - alpha) weight_c_a := 3 * (1 - alpha) * (1 - alpha) * alpha weight_c_b := 3 * (1 - alpha) * alpha * alpha @@ -178,14 +145,17 @@ else { Vec2_SIMD :: simd.f32x4 + @(optimization_mode="favor_size") vec2_to_simd :: #force_inline proc "contextless" (v: Vec2) -> Vec2_SIMD { return Vec2_SIMD{v.x, v.y, 0, 0} } + @(optimization_mode="favor_size") simd_to_vec2 :: #force_inline proc "contextless" (v: Vec2_SIMD) -> Vec2 { return Vec2{ simd.extract(v, 0), simd.extract(v, 1) } } + @(optimization_mode="favor_size") eval_point_on_bezier3 :: #force_inline proc "contextless" (p0, p1, p2: Vec2, alpha: f32) -> Vec2 { simd_p0 := vec2_to_simd(p0) @@ -209,6 +179,7 @@ else return simd_to_vec2(result) } + @(optimization_mode="favor_size") eval_point_on_bezier4 :: #force_inline proc "contextless" (p0, p1, p2, p3: Vec2, alpha: f32) -> Vec2 { simd_p0 := vec2_to_simd(p0) diff --git a/vefontcache/parser.odin b/vefontcache/parser.odin index 2bb0b6f..631f02a 100644 --- a/vefontcache/parser.odin +++ b/vefontcache/parser.odin @@ -2,31 +2,34 @@ package vefontcache /* Notes: +This is a minimal wrapper I originally did incase a font parser other than stb_truetype is introduced in the future. +Otherwise, its essentially 1:1 with it. -Freetype will do memory allocations and has an interface the user can implement. -That interface is not exposed from this parser but could be added to parser_init. +Freetype isn't really supported and its not a high priority. +~~Freetype will do memory allocations and has an interface the user can implement.~~ +~~That interface is not exposed from this parser but could be added to parser_init.~~ -STB_Truetype has macros for its allocation unfortuantely +STB_Truetype: +* Added ability to set the stb_truetype allocator for STBTT_MALLOC and STBTT_FREE. +* Changed procedure signatures to pass the font_info struct by immutable ptr (#by_ptr) + when the C equivalent has their parameter as `const*`. */ -import "base:runtime" import "core:c" -import "core:math" -import "core:slice" -import stbtt "vendor:stb/truetype" -import freetype "thirdparty:freetype" +import stbtt "thirdparty:stb/truetype" +// import freetype "thirdparty:freetype" Parser_Kind :: enum u32 { STB_TrueType, - Freetype, + Freetype, // Currently not implemented. } Parser_Font_Info :: struct { label : string, kind : Parser_Kind, using _ : struct #raw_union { - stbtt_info : stbtt.fontinfo, - freetype_info : freetype.Face + stbtt_info : stbtt.fontinfo, + // freetype_info : freetype.Face }, data : []byte, } @@ -51,47 +54,55 @@ Parser_Glyph_Vertex :: struct { Parser_Glyph_Shape :: [dynamic]Parser_Glyph_Vertex Parser_Context :: struct { - kind : Parser_Kind, - ft_library : freetype.Library, + lib_backing : Allocator, + kind : Parser_Kind, + // ft_library : freetype.Library, +} + +parser_stbtt_allocator_proc :: proc( + allocator_data : rawptr, + type : stbtt.zpl_allocator_type, + size : c.ssize_t, + alignment : c.ssize_t, + old_memory : rawptr, + old_size : c.ssize_t, + flags : c.ulonglong +) -> rawptr +{ + allocator := transmute(^Allocator) allocator_data + result, error := allocator.procedure( allocator.data, cast(Allocator_Mode) type, cast(int) size, cast(int) alignment, old_memory, cast(int) old_size ) + assert(error == .None) + + if type == .Alloc || type == .Resize { + raw := transmute(Raw_Slice) result + // assert(raw.len > 0, "Allocation is 0 bytes?") + return transmute(rawptr) raw.data + } + else do return nil } -parser_init :: proc( ctx : ^Parser_Context, kind : Parser_Kind ) +parser_init :: proc( ctx : ^Parser_Context, kind : Parser_Kind, allocator := context.allocator ) { - switch kind - { - case .Freetype: - result := freetype.init_free_type( & ctx.ft_library ) - assert( result == freetype.Error.Ok, "VEFontCache.parser_init: Failed to initialize freetype" ) - - case .STB_TrueType: - // Do nothing intentional - } + ctx.kind = kind + ctx.lib_backing = allocator - ctx.kind = kind + stbtt_allocator := stbtt.zpl_allocator { parser_stbtt_allocator_proc, & ctx.lib_backing } + stbtt.SetAllocator( stbtt_allocator ) +} + +parser_reload :: proc( ctx : ^Parser_Context, allocator := context.allocator) { + ctx.lib_backing = allocator + stbtt_allocator := stbtt.zpl_allocator { parser_stbtt_allocator_proc, & ctx.lib_backing } + stbtt.SetAllocator( stbtt_allocator ) } parser_shutdown :: proc( ctx : ^Parser_Context ) { - // TODO(Ed): Implement + // Note: Not necesssary for stb_truetype } -parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte ) -> (font : Parser_Font_Info) +parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte ) -> (font : Parser_Font_Info, error : b32) { - switch ctx.kind - { - case .Freetype: - when ODIN_OS == .Windows { - error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i32) len(data), 0, & font.freetype_info ) - if error != .Ok do return - } - else when ODIN_OS == .Linux { - error := freetype.new_memory_face( ctx.ft_library, raw_data(data), cast(i64) len(data), 0, & font.freetype_info ) - if error != .Ok do return - } - - case .STB_TrueType: - success := stbtt.InitFont( & font.stbtt_info, raw_data(data), 0 ) - if ! success do return - } + error = ! stbtt.InitFont( & font.stbtt_info, raw_data(data), 0 ) font.label = label font.data = data @@ -101,242 +112,87 @@ parser_load_font :: proc( ctx : ^Parser_Context, label : string, data : []byte ) parser_unload_font :: proc( font : ^Parser_Font_Info ) { - switch font.kind { - case .Freetype: - error := freetype.done_face( font.freetype_info ) - assert( error == .Ok, "VEFontCache.parser_unload_font: Failed to unload freetype face" ) - - case .STB_TrueType: - // Do Nothing - } + // case .STB_TrueType: + // Do Nothing } -parser_find_glyph_index :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph) +parser_find_glyph_index :: #force_inline proc "contextless" ( font : Parser_Font_Info, codepoint : rune ) -> (glyph_index : Glyph) { - switch font.kind - { - case .Freetype: - when ODIN_OS == .Windows { - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) - } - else when ODIN_OS == .Linux { - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) - } - return - - case .STB_TrueType: - glyph_index = transmute(Glyph) stbtt.FindGlyphIndex( & font.stbtt_info, codepoint ) - return - } - return Glyph(-1) + glyph_index = transmute(Glyph) stbtt.FindGlyphIndex( font.stbtt_info, codepoint ) + return } -parser_free_shape :: proc( font : ^Parser_Font_Info, shape : Parser_Glyph_Shape ) +parser_free_shape :: #force_inline proc( font : Parser_Font_Info, shape : Parser_Glyph_Shape ) { - switch font.kind - { - case .Freetype: - delete(shape) - - case .STB_TrueType: - stbtt.FreeShape( & font.stbtt_info, transmute( [^]stbtt.vertex) raw_data(shape) ) - } + shape := shape + shape_raw := transmute( ^Raw_Dynamic_Array) & shape + stbtt.FreeShape( font.stbtt_info, transmute( [^]stbtt.vertex) shape_raw.data ) } -parser_get_codepoint_horizontal_metrics :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, codepoint : rune ) -> ( advance, to_left_side_glyph : i32 ) +parser_get_codepoint_horizontal_metrics :: #force_inline proc "contextless" ( font : Parser_Font_Info, codepoint : rune ) -> ( advance, to_left_side_glyph : i32 ) { - switch font.kind - { - case .Freetype: - glyph_index : Glyph - when ODIN_OS == .Windows { - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) - } - else when ODIN_OS == .Linux { - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) - } - - if glyph_index != 0 - { - freetype.load_glyph( font.freetype_info, c.uint(codepoint), { .No_Bitmap, .No_Hinting, .No_Scale } ) - advance = i32(font.freetype_info.glyph.advance.x) >> 6 - to_left_side_glyph = i32(font.freetype_info.glyph.metrics.hori_bearing_x) >> 6 - } - else - { - advance = 0 - to_left_side_glyph = 0 - } - - case .STB_TrueType: - stbtt.GetCodepointHMetrics( & font.stbtt_info, codepoint, & advance, & to_left_side_glyph ) - } + stbtt.GetCodepointHMetrics( font.stbtt_info, codepoint, & advance, & to_left_side_glyph ) return } -parser_get_codepoint_kern_advance :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, prev_codepoint, codepoint : rune ) -> i32 +parser_get_codepoint_kern_advance :: #force_inline proc "contextless" ( font : Parser_Font_Info, prev_codepoint, codepoint : rune ) -> i32 { - switch font.kind - { - case .Freetype: - prev_glyph_index : Glyph - glyph_index : Glyph - when ODIN_OS == .Windows { - prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) prev_codepoint ) - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, transmute(u32) codepoint ) - } - else when ODIN_OS == .Linux { - prev_glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) prev_codepoint ) - glyph_index = transmute(Glyph) freetype.get_char_index( font.freetype_info, cast(u64) codepoint ) - } - - if prev_glyph_index != 0 && glyph_index != 0 - { - kerning : freetype.Vector - font.freetype_info.driver.clazz.get_kerning( font.freetype_info, transmute(u32) prev_codepoint, transmute(u32) codepoint, & kerning ) - } - - case .STB_TrueType: - kern := stbtt.GetCodepointKernAdvance( & font.stbtt_info, prev_codepoint, codepoint ) - return kern - } - return -1 + kern := stbtt.GetCodepointKernAdvance( font.stbtt_info, prev_codepoint, codepoint ) + return kern } -parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : ^Parser_Font_Info ) -> (ascent, descent, line_gap : i32 ) +parser_get_font_vertical_metrics :: #force_inline proc "contextless" ( font : Parser_Font_Info ) -> (ascent, descent, line_gap : i32 ) { - switch font.kind - { - case .Freetype: - info := font.freetype_info - ascent = i32(info.ascender) - descent = i32(info.descender) - line_gap = i32(info.height) - (ascent - descent) - - case .STB_TrueType: - stbtt.GetFontVMetrics( & font.stbtt_info, & ascent, & descent, & line_gap ) - } + stbtt.GetFontVMetrics( font.stbtt_info, & ascent, & descent, & line_gap ) return } -parser_get_glyph_box :: #force_inline proc ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (bounds_0, bounds_1 : Vec2i) +parser_get_bounds :: #force_inline proc "contextless" ( font : Parser_Font_Info, glyph_index : Glyph ) -> (bounds : Range2) { - switch font.kind - { - case .Freetype: - freetype.load_glyph( font.freetype_info, c.uint(glyph_index), { .No_Bitmap, .No_Hinting, .No_Scale } ) + // profile(#procedure) + bounds_0, bounds_1 : Vec2i - metrics := font.freetype_info.glyph.metrics + x0, y0, x1, y1 : i32 + success := cast(bool) stbtt.GetGlyphBox( font.stbtt_info, i32(glyph_index), & x0, & y0, & x1, & y1 ) - bounds_0 = {i32(metrics.hori_bearing_x), i32(metrics.hori_bearing_y - metrics.height)} - bounds_1 = {i32(metrics.hori_bearing_x + metrics.width), i32(metrics.hori_bearing_y)} - - case .STB_TrueType: - x0, y0, x1, y1 : i32 - success := cast(bool) stbtt.GetGlyphBox( & font.stbtt_info, i32(glyph_index), & x0, & y0, & x1, & y1 ) - assert( success ) - - bounds_0 = { i32(x0), i32(y0) } - bounds_1 = { i32(x1), i32(y1) } - } + bounds_0 = { x0, y0 } + bounds_1 = { x1, y1 } + bounds = { vec2(bounds_0), vec2(bounds_1) } return } -parser_get_glyph_shape :: proc( font : ^Parser_Font_Info, glyph_index : Glyph ) -> (shape : Parser_Glyph_Shape, error : Allocator_Error) +parser_get_glyph_shape :: #force_inline proc ( font : Parser_Font_Info, glyph_index : Glyph ) -> (shape : Parser_Glyph_Shape, error : Allocator_Error) { - switch font.kind - { - case .Freetype: - // TODO(Ed): Don't do this, going a completely different route for handling shapes. - // This abstraction fails to be time-saving or performant. - - case .STB_TrueType: - stb_shape : [^]stbtt.vertex - nverts := stbtt.GetGlyphShape( & font.stbtt_info, cast(i32) glyph_index, & stb_shape ) - - shape_raw := transmute( ^runtime.Raw_Dynamic_Array) & shape - shape_raw.data = stb_shape - shape_raw.len = int(nverts) - shape_raw.cap = int(nverts) - shape_raw.allocator = runtime.nil_allocator() - error = Allocator_Error.None - return - } - + stb_shape : [^]stbtt.vertex + nverts := stbtt.GetGlyphShape( font.stbtt_info, cast(i32) glyph_index, & stb_shape ) + + shape_raw := transmute( ^Raw_Dynamic_Array) & shape + shape_raw.data = stb_shape + shape_raw.len = int(nverts) + shape_raw.cap = int(nverts) + shape_raw.allocator = nil_allocator() + error = Allocator_Error.None return } -parser_is_glyph_empty :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, glyph_index : Glyph ) -> b32 +parser_is_glyph_empty :: #force_inline proc "contextless" ( font : Parser_Font_Info, glyph_index : Glyph ) -> b32 { - switch font.kind - { - case .Freetype: - error := freetype.load_glyph( font.freetype_info, cast(u32) glyph_index, { .No_Bitmap, .No_Hinting, .No_Scale } ) - if error == .Ok - { - if font.freetype_info.glyph.format == .Outline { - return font.freetype_info.glyph.outline.n_points == 0 - } - else if font.freetype_info.glyph.format == .Bitmap { - return font.freetype_info.glyph.bitmap.width == 0 && font.freetype_info.glyph.bitmap.rows == 0; - } - } - return false - - case .STB_TrueType: - return stbtt.IsGlyphEmpty( & font.stbtt_info, cast(c.int) glyph_index ) - } - return false + return stbtt.IsGlyphEmpty( font.stbtt_info, cast(c.int) glyph_index ) } -parser_scale :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, size : f32 ) -> f32 +parser_scale :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32 { - size_scale := size < 0.0 ? \ - parser_scale_for_pixel_height( font, -size ) \ - : parser_scale_for_mapping_em_to_pixels( font, size ) - // size_scale = 1.0 + // profile(#procedure) + size_scale := size > 0.0 ? parser_scale_for_mapping_em_to_pixels( font, size ) : parser_scale_for_pixel_height( font, -size ) return size_scale } -parser_scale_for_pixel_height :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, size : f32 ) -> f32 +parser_scale_for_pixel_height :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32 { - switch font.kind { - case .Freetype: - freetype.set_pixel_sizes( font.freetype_info, 0, cast(u32) size ) - size_scale := size / cast(f32)font.freetype_info.units_per_em - return size_scale - - case.STB_TrueType: - return stbtt.ScaleForPixelHeight( & font.stbtt_info, size ) - } - return 0 + return stbtt.ScaleForPixelHeight( font.stbtt_info, size ) } -parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font : ^Parser_Font_Info, size : f32 ) -> f32 +parser_scale_for_mapping_em_to_pixels :: #force_inline proc "contextless" ( font : Parser_Font_Info, size : f32 ) -> f32 { - switch font.kind { - case .Freetype: - Inches_To_CM :: cast(f32) 2.54 - Points_Per_CM :: cast(f32) 28.3465 - CM_Per_Point :: cast(f32) 1.0 / DPT_DPCM - CM_Per_Pixel :: cast(f32) 1.0 / DPT_PPCM - DPT_DPCM :: cast(f32) 72.0 * Inches_To_CM // 182.88 points/dots per cm - DPT_PPCM :: cast(f32) 96.0 * Inches_To_CM // 243.84 pixels per cm - DPT_DPI :: cast(f32) 72.0 - - // TODO(Ed): Don't assume the dots or pixels per inch. - system_dpi :: DPT_DPI - - FT_Font_Size_Point_Unit :: 1.0 / 64.0 - FT_Point_10 :: 64.0 - - points_per_em := (size / system_dpi ) * DPT_DPI - freetype.set_char_size( font.freetype_info, 0, cast(freetype.F26Dot6) f32(points_per_em * FT_Point_10), cast(u32) DPT_DPI, cast(u32) DPT_DPI ) - size_scale := f32(f64(size) / cast(f64) font.freetype_info.units_per_em) - return size_scale - - case .STB_TrueType: - return stbtt.ScaleForMappingEmToPixels( & font.stbtt_info, size ) - } - return 0 + return stbtt.ScaleForMappingEmToPixels( font.stbtt_info, size ) } diff --git a/vefontcache/mappings.odin b/vefontcache/pkg_mapping.odin similarity index 63% rename from vefontcache/mappings.odin rename to vefontcache/pkg_mapping.odin index f6afc8d..acf9be7 100644 --- a/vefontcache/mappings.odin +++ b/vefontcache/pkg_mapping.odin @@ -1,7 +1,15 @@ package vefontcache +import "base:builtin" + resize_soa_non_zero :: non_zero_resize_soa +import "base:runtime" + Raw_Dynamic_Array :: runtime.Raw_Dynamic_Array + Raw_Map :: runtime.Raw_Map + Raw_Slice :: runtime.Raw_Slice + raw_soa_footer :: runtime.raw_soa_footer + nil_allocator :: runtime.nil_allocator import "core:hash" - fnv64a :: hash.fnv64a + ginger16 :: hash.ginger16 import "core:math" ceil_f16 :: math.ceil_f16 ceil_f16le :: math.ceil_f16le @@ -29,11 +37,13 @@ import "core:mem" Allocator :: mem.Allocator Allocator_Error :: mem.Allocator_Error + Allocator_Mode :: mem.Allocator_Mode Arena :: mem.Arena arena_allocator :: mem.arena_allocator arena_init :: mem.arena_init import "core:slice" +import "core:unicode" //#region("Proc overload mappings") @@ -43,6 +53,10 @@ append :: proc { append_elem_string, } +append_soa :: proc { + append_soa_elem, +} + ceil :: proc { math.ceil_f16, math.ceil_f16le, @@ -58,8 +72,8 @@ ceil :: proc { } clear :: proc { - clear_dynamic_array, - clear_map, + builtin.clear_dynamic_array, + builtin.clear_map, } floor :: proc { @@ -80,16 +94,43 @@ fill :: proc { slice.fill, } +max :: proc { + linalg.max_single, + linalg.max_double, +} + make :: proc { - make_dynamic_array, - make_dynamic_array_len, - make_dynamic_array_len_cap, - make_map, - make_map_cap, + builtin.make_dynamic_array, + builtin.make_dynamic_array_len, + builtin.make_dynamic_array_len_cap, + builtin.make_slice, + builtin.make_map, + builtin.make_map_cap, +} + +make_soa :: proc { + builtin.make_soa_dynamic_array_len_cap, + builtin.make_soa_slice, +} + +mul :: proc { + mul_range2_vec2, +} + +peek :: proc { + peek_array, } resize :: proc { - resize_dynamic_array, + builtin.resize_dynamic_array, +} + +round :: proc { + math.round_f32, +} + +size :: proc { + size_range2, } vec2 :: proc { diff --git a/vefontcache/profiling.odin b/vefontcache/profiling.odin new file mode 100644 index 0000000..5d832f5 --- /dev/null +++ b/vefontcache/profiling.odin @@ -0,0 +1,17 @@ +package vefontcache + +// Add profiling hookup here + +// import "" + +@(deferred_none = profile_end, disabled = DISABLE_PROFILING) +profile :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { +} + +@(disabled = DISABLE_PROFILING) +profile_begin :: #force_inline proc "contextless" ( name : string, loc := #caller_location ) { +} + +@(disabled = DISABLE_PROFILING) +profile_end :: #force_inline proc "contextless" () { +} diff --git a/vefontcache/shaped_text.odin b/vefontcache/shaped_text.odin deleted file mode 100644 index d23436e..0000000 --- a/vefontcache/shaped_text.odin +++ /dev/null @@ -1,131 +0,0 @@ -package vefontcache - -Shaped_Text :: struct { - glyphs : [dynamic]Glyph, - positions : [dynamic]Vec2, - end_cursor_pos : Vec2, - size : Vec2, -} - -Shaped_Text_Cache :: struct { - storage : [dynamic]Shaped_Text, - state : LRU_Cache, - next_cache_id : i32, -} - -shape_lru_hash :: #force_inline proc "contextless" ( hash : ^u64, bytes : []byte ) { - for value in bytes { - (hash^) = (( (hash^) << 8) + (hash^) ) + u64(value) - } -} - -shape_text_cached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry ) -> ^Shaped_Text -{ - // profile(#procedure) - font := font - font_bytes := slice_ptr( transmute(^byte) & font, size_of(Font_ID) ) - text_bytes := transmute( []byte) text_utf8 - - lru_code : u64 - shape_lru_hash( & lru_code, font_bytes ) - shape_lru_hash( & lru_code, text_bytes ) - - shape_cache := & ctx.shape_cache - state := & ctx.shape_cache.state - - shape_cache_idx := lru_get( state, lru_code ) - if shape_cache_idx == -1 - { - if shape_cache.next_cache_id < i32(state.capacity) { - shape_cache_idx = shape_cache.next_cache_id - shape_cache.next_cache_id += 1 - evicted := lru_put( state, lru_code, shape_cache_idx ) - } - else - { - next_evict_idx := lru_get_next_evicted( state ) - assert( next_evict_idx != 0xFFFFFFFFFFFFFFFF ) - - shape_cache_idx = lru_peek( state, next_evict_idx, must_find = true ) - assert( shape_cache_idx != - 1 ) - - lru_put( state, lru_code, shape_cache_idx ) - } - - shape_entry := & shape_cache.storage[ shape_cache_idx ] - shape_text_uncached( ctx, font, text_utf8, entry, shape_entry ) - } - - return & shape_cache.storage[ shape_cache_idx ] -} - -shape_text_uncached :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, entry : ^Entry, output : ^Shaped_Text ) -{ - // profile(#procedure) - assert( ctx != nil ) - assert( font >= 0 && int(font) < len(ctx.entries) ) - - clear( & output.glyphs ) - clear( & output.positions ) - - ascent_i32, descent_i32, line_gap_i32 := parser_get_font_vertical_metrics( & entry.parser_info ) - ascent := f32(ascent_i32) - descent := f32(descent_i32) - line_gap := f32(line_gap_i32) - line_height := (ascent - descent + line_gap) * entry.size_scale - - if ctx.use_advanced_shaper - { - shaper_shape_from_text( & ctx.shaper_ctx, & entry.shaper_info, output, text_utf8, ascent_i32, descent_i32, line_gap_i32, entry.size, entry.size_scale ) - return - } - else - { - // Note(Original Author): - // We use our own fallback dumbass text shaping. - // WARNING: PLEASE USE HARFBUZZ. GOOD TEXT SHAPING IS IMPORTANT FOR INTERNATIONALISATION. - - line_count : int = 1 - max_line_width : f32 = 0 - position : Vec2 - - prev_codepoint : rune - for codepoint in text_utf8 - { - if prev_codepoint > 0 { - kern := parser_get_codepoint_kern_advance( & entry.parser_info, prev_codepoint, codepoint ) - position.x += f32(kern) * entry.size_scale - } - if codepoint == '\n' - { - line_count += 1 - max_line_width = max(max_line_width, position.x) - position.x = 0.0 - position.y -= line_height - position.y = position.y - prev_codepoint = rune(0) - continue - } - if abs( entry.size ) <= ctx.shaper_ctx.adv_snap_small_font_threshold { - position.x = ceil(position.x) - } - - append( & output.glyphs, parser_find_glyph_index( & entry.parser_info, codepoint )) - advance, _ := parser_get_codepoint_horizontal_metrics( & entry.parser_info, codepoint ) - - append( & output.positions, Vec2 { - ceil(position.x), - ceil(position.y) - }) - - position.x += f32(advance) * entry.size_scale - prev_codepoint = codepoint - } - - output.end_cursor_pos = position - max_line_width = max(max_line_width, position.x) - - output.size.x = max_line_width - output.size.y = f32(line_count) * line_height - } -} diff --git a/vefontcache/shaper.odin b/vefontcache/shaper.odin index 309c4ec..f609b4d 100644 --- a/vefontcache/shaper.odin +++ b/vefontcache/shaper.odin @@ -1,16 +1,69 @@ package vefontcache /* -Note(Ed): The only reason I didn't directly use harfbuzz is because hamza exists and seems to be under active development as an alternative. +Note(Ed): The only reason I didn't directly use harfbuzz is: +https://github.com/saidwho12/hamza +and seems to be under active development as an alternative. */ import "core:c" import "thirdparty:harfbuzz" +Shape_Key :: u32 + +/* A text whose codepoints have had their relevant glyphs and + associated data resolved for processing in a draw list generation stage. + Traditionally a shape only refers to resolving which glyph and + its position should be used for rendering. + + For this library's case it also resolves any content that does not have to be done + on a per-frame basis for draw list generation: + * atlas lru codes + * glyph bounds and scale + * atlas region the glyph is associated with. + + Ideally the user should resolve this shape once and cache/store it on their side. + They have the best ability to avoid costly lookups. +*/ +Shaped_Text :: struct #packed { + glyph : [dynamic]Glyph, + position : [dynamic]Vec2, + visible : [dynamic]i32, + atlas_lru_code : [dynamic]Atlas_Key, + region_kind : [dynamic]Atlas_Region_Kind, + bounds : [dynamic]Range2, + end_cursor_pos : Vec2, + size : Vec2, + font : Font_ID, + px_size : f32, +} + +// Ease of use cache, can handle thousands of lookups per frame with ease. +// TODO(Ed) It might perform better with a tailored made hashtable implementation for the LRU_Cache or dedicated array struct/procs for the Shaped_Text. +Shaped_Text_Cache :: struct { + storage : [dynamic]Shaped_Text, + state : LRU_Cache(Shape_Key), + next_cache_id : i32, +} + +// Used by shaper_shape_text_cached, allows user to specify their own proc at compile-time without having to rewrite the caching implementation. +Shaper_Shape_Text_Uncached_Proc :: #type proc( ctx : ^Shaper_Context, + atlas : Atlas, + glyph_buffer_size : Vec2, + font : Font_ID, + entry : Entry, + font_px_Size : f32, + font_scale : f32, + text_utf8 : string, + output : ^Shaped_Text +) + +// Note(Ed): Not used.. Shaper_Kind :: enum { - Naive = 0, + Latin = 0, Harfbuzz = 1, } +// Not much here other than just keep track of a harfbuzz var and deciding to keep runtime config here used by the shapers. Shaper_Context :: struct { hb_buffer : harfbuzz.Buffer, @@ -18,6 +71,7 @@ Shaper_Context :: struct { adv_snap_small_font_threshold : f32, } +// Only used with harbuzz for now. Resolved during load_font for a font Entry. Shaper_Info :: struct { blob : harfbuzz.Blob, face : harfbuzz.Face, @@ -30,112 +84,147 @@ shaper_init :: proc( ctx : ^Shaper_Context ) assert( ctx.hb_buffer != nil, "VEFontCache.shaper_init: Failed to create harfbuzz buffer") } -shaper_shutdown :: proc( ctx : ^Shaper_Context ) +shaper_shutdown :: proc( ctx : ^Shaper_Context ) { if ctx.hb_buffer != nil { harfbuzz.buffer_destroy( ctx.hb_buffer ) } } -shaper_load_font :: proc( ctx : ^Shaper_Context, label : string, data : []byte, user_data : rawptr ) -> (info : Shaper_Info) +shaper_load_font :: #force_inline proc( ctx : ^Shaper_Context, label : string, data : []byte, user_data : rawptr = nil ) -> (info : Shaper_Info) { - using info - blob = harfbuzz.blob_create( raw_data(data), cast(c.uint) len(data), harfbuzz.Memory_Mode.READONLY, user_data, nil ) - face = harfbuzz.face_create( blob, 0 ) - font = harfbuzz.font_create( face ) + info.blob = harfbuzz.blob_create( raw_data(data), cast(c.uint) len(data), harfbuzz.Memory_Mode.READONLY, user_data, nil ) + info.face = harfbuzz.face_create( info.blob, 0 ) + info.font = harfbuzz.font_create( info.face ) return } -shaper_unload_font :: proc( ctx : ^Shaper_Info ) +shaper_unload_font :: #force_inline proc( info : ^Shaper_Info ) { - using ctx - if blob != nil do harfbuzz.font_destroy( font ) - if face != nil do harfbuzz.face_destroy( face ) - if blob != nil do harfbuzz.blob_destroy( blob ) + if info.font != nil do harfbuzz.font_destroy( info.font ) + if info.face != nil do harfbuzz.face_destroy( info.face ) + if info.blob != nil do harfbuzz.blob_destroy( info.blob ) } -shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, output :^Shaped_Text, text_utf8 : string, - ascent, descent, line_gap : i32, size, size_scale : f32 ) +// TODO(Ed): Allow the user to override snap_glyph_position of the shaper context on a per-call basis (as a param) +// Recommended shaper. Very performant. +// TODO(Ed): Would be nice to properly support vertical shaping, right now its strictly just horizontal... +@(optimization_mode="favor_size") +shaper_shape_harfbuzz :: proc( ctx : ^Shaper_Context, + atlas : Atlas, + glyph_buffer_size : Vec2, + font : Font_ID, + entry : Entry, + font_px_size : f32, + font_scale : f32, + text_utf8 : string, + output : ^Shaped_Text +) { - // profile(#procedure) + profile(#procedure) + assert( ctx != nil ) + + clear( & output.glyph ) + clear( & output.position ) + clear( & output.visible ) + current_script := harfbuzz.Script.UNKNOWN hb_ucfunc := harfbuzz.unicode_funcs_get_default() harfbuzz.buffer_clear_contents( ctx.hb_buffer ) - assert( info.font != nil ) - - ascent := f32(ascent) - descent := f32(descent) - line_gap := f32(line_gap) + ascent := entry.ascent + descent := entry.descent + line_gap := entry.line_gap + max_line_width := f32(0) line_count := 1 - line_height := ((ascent - descent + line_gap) * size_scale) + line_height := ((ascent - descent + line_gap) * font_scale) + + position : Vec2 + + @(optimization_mode="favor_size") + shape_run :: proc( output : ^Shaped_Text, + entry : Entry, + buffer : harfbuzz.Buffer, + script : harfbuzz.Script, + + position : ^Vec2, + max_line_width : ^f32, + line_count : ^int, + + font_px_size : f32, + font_scale : f32, - position, vertical_position : f32 - shape_run :: proc( buffer : harfbuzz.Buffer, script : harfbuzz.Script, font : harfbuzz.Font, output : ^Shaped_Text, - position, vertical_position, max_line_width: ^f32, line_count: ^int, - ascent, descent, line_gap, size, size_scale: f32, - snap_shape_pos : b32, adv_snap_small_font_threshold : f32 ) + snap_shape_pos : b32, + adv_snap_small_font_threshold : f32 + ) { - // Set script and direction. We use the system's default langauge. - // script = HB_SCRIPT_LATIN - harfbuzz.buffer_set_script( buffer, script ) + profile(#procedure) + harfbuzz.buffer_set_script ( buffer, script ) harfbuzz.buffer_set_direction( buffer, harfbuzz.script_get_horizontal_direction( script )) - harfbuzz.buffer_set_language( buffer, harfbuzz.language_get_default() ) + harfbuzz.buffer_set_language ( buffer, harfbuzz.language_get_default() ) // Perform the actual shaping of this run using HarfBuzz. harfbuzz.buffer_set_content_type( buffer, harfbuzz.Buffer_Content_Type.UNICODE ) - harfbuzz.shape( font, buffer, nil, 0 ) + harfbuzz.shape( entry.shaper_info.font, buffer, nil, 0 ) // Loop over glyphs and append to output buffer. glyph_count : u32 glyph_infos := harfbuzz.buffer_get_glyph_infos( buffer, & glyph_count ) glyph_positions := harfbuzz.buffer_get_glyph_positions( buffer, & glyph_count ) - line_height := (ascent - descent + line_gap) * size_scale + line_height := (entry.ascent - entry.descent + entry.line_gap) * font_scale + last_cluster := u32(0) for index : i32; index < i32(glyph_count); index += 1 { - hb_glyph := glyph_infos[ index ] + hb_glyph := glyph_infos [ index ] hb_gposition := glyph_positions[ index ] - glyph_id := cast(Glyph) hb_glyph.codepoint + glyph := cast(Glyph) hb_glyph.codepoint if hb_glyph.cluster > 0 { - (max_line_width^) = max( max_line_width^, position^ ) - (position^) = 0.0 - (vertical_position^) -= line_height - (vertical_position^) = floor(vertical_position^ + 0.5) - (line_count^) += 1 + (max_line_width^) = max( max_line_width^, position.x ) + position.x = 0.0 + position.y -= line_height + position.y = floor(position.y) + (line_count^) += 1 + + last_cluster = hb_glyph.cluster continue } - if abs( size ) <= adv_snap_small_font_threshold - { + if abs( font_px_size ) <= adv_snap_small_font_threshold { (position^) = ceil( position^ ) } - append( & output.glyphs, glyph_id ) - - pos := position^ - v_pos := vertical_position^ - offset_x := f32(hb_gposition.x_offset) * size_scale - offset_y := f32(hb_gposition.y_offset) * size_scale - pos += offset_x - v_pos += offset_y + glyph_pos := position^ + offset := Vec2 { f32(hb_gposition.x_offset), f32(hb_gposition.y_offset) } * font_scale + glyph_pos += offset if snap_shape_pos { - pos = ceil(pos) - v_pos = ceil(v_pos) + glyph_pos = ceil(glyph_pos) + } + + advance := Vec2 { + f32(hb_gposition.x_advance) * font_scale, + f32(hb_gposition.y_advance) * font_scale } - append( & output.positions, Vec2 {pos, v_pos}) + (position^) += advance + (max_line_width^) = max(max_line_width^, position.x) + + // We track all glyphs so that user can use the shape for navigation purposes. + append( & output.glyph, glyph ) + append( & output.position, glyph_pos) - (position^) += f32(hb_gposition.x_advance) * size_scale - (vertical_position^) += f32(hb_gposition.y_advance) * size_scale - (max_line_width^) = max(max_line_width^, position^) + // We don't accept all glyphs for rendering, harfbuzz preserves positions of non-visible codepoints (as .notdef glyphs) + // We also double check to make sure the glyph isn't detected for drawing by the parser. + visible_glyph := glyph != 0 && ! parser_is_glyph_empty(entry.parser_info, glyph) + if visible_glyph { + append( & output.visible, cast(i32) len(output.glyph) - 1 ) + } } - output.end_cursor_pos.x = position^ - output.end_cursor_pos.y = vertical_position^ + output.end_cursor_pos = position^ harfbuzz.buffer_clear_contents( buffer ) } @@ -152,34 +241,246 @@ shaper_shape_from_text :: proc( ctx : ^Shaper_Context, info : ^Shaper_Info, outp // Can we continue the current run? ScriptKind :: harfbuzz.Script - special_script : b32 = script == ScriptKind.UNKNOWN || script == ScriptKind.INHERITED || script == ScriptKind.COMMON - if special_script || script == current_script || byte_offset == 0 { + // These scripts don't break runs because they don't represent script transitions - they adapt to their context. + // Maintaining the current shaping run for these scripts ensures correct processing of marks, numbers, + // and punctuation within the primary text flow. + is_neutral_script := script == ScriptKind.UNKNOWN || script == ScriptKind.INHERITED || script == ScriptKind.COMMON + + // Essentially if the script is neutral, or the same as current, + // or this is the first codepoint: add it to the buffer and continue the loop. + if is_neutral_script \ + || script == current_script \ + || byte_offset == 0 + { harfbuzz.buffer_add( ctx.hb_buffer, hb_codepoint, codepoint == '\n' ? 1 : 0 ) - current_script = special_script ? current_script : script + current_script = is_neutral_script ? current_script : script continue } - // End current run since we've encountered a script change. - shape_run( - ctx.hb_buffer, current_script, info.font, output, - & position, & vertical_position, & max_line_width, & line_count, - ascent, descent, line_gap, size, size_scale, - ctx.snap_glyph_position, ctx.adv_snap_small_font_threshold - ) + // End current run since we've encountred a significant script change. + shape_run( output, + entry, + ctx.hb_buffer, + current_script, + & position, + & max_line_width, + & line_count, + font_px_size, + font_scale, + ctx.snap_glyph_position, + ctx.adv_snap_small_font_threshold + ) harfbuzz.buffer_add( ctx.hb_buffer, hb_codepoint, codepoint == '\n' ? 1 : 0 ) current_script = script } // End the last run if needed - shape_run( - ctx.hb_buffer, current_script, info.font, output, - & position, & vertical_position, & max_line_width, & line_count, - ascent, descent, line_gap, size, size_scale, - ctx.snap_glyph_position, ctx.adv_snap_small_font_threshold - ) + shape_run( output, + entry, + ctx.hb_buffer, + current_script, + & position, + & max_line_width, + & line_count, + font_px_size, + font_scale, + ctx.snap_glyph_position, + ctx.adv_snap_small_font_threshold + ) // Set the final size output.size.x = max_line_width output.size.y = f32(line_count) * line_height + + // Resolve each glyphs: bounds, atlas lru, and the atlas region as we have everything we need now. + + resize( & output.atlas_lru_code, len(output.visible) ) + resize( & output.region_kind, len(output.visible) ) + resize( & output.bounds, len(output.visible) ) + + profile_begin("atlas_lru_code") + for vis_id, index in output.visible { + glyph_id := output.glyph[vis_id] + output.atlas_lru_code[index] = atlas_glyph_lru_code(entry.id, font_px_size, glyph_id) + // atlas_lru_code is 1:1 with visible index + } + profile_end() + + profile_begin("bounds & region") + for vis_id, index in output.visible { + glyph_id := output.glyph[vis_id] + bounds := & output.bounds[index] + (bounds ^) = parser_get_bounds( entry.parser_info, glyph_id ) + bounds_size_scaled := (bounds.p1 - bounds.p0) * font_scale + output.region_kind[index] = atlas_decide_region( atlas, glyph_buffer_size, bounds_size_scaled ) + // bounds & region_kind are 1:1 with visible index + } + profile_end() + + output.font = font + output.px_size = font_px_size + return +} + +// TODO(Ed): Allow the user to override snap_glyph_position of the shaper context on a per-call basis (as an param) +// Basic western alphabet based shaping. Not that much faster than harfbuzz if at all. +shaper_shape_text_latin :: proc( ctx : ^Shaper_Context, + atlas : Atlas, + glyph_buffer_size : Vec2, + font : Font_ID, + entry : Entry, + font_px_size : f32, + font_scale : f32, + text_utf8 : string, + output : ^Shaped_Text +) +{ + profile(#procedure) + assert( ctx != nil ) + + clear( & output.glyph ) + clear( & output.position ) + clear( & output.visible ) + + line_height := (entry.ascent - entry.descent + entry.line_gap) * font_scale + + line_count : int = 1 + max_line_width : f32 = 0 + position : Vec2 + + prev_codepoint : rune + for codepoint, index in text_utf8 + { + if prev_codepoint > 0 { + kern := parser_get_codepoint_kern_advance( entry.parser_info, prev_codepoint, codepoint ) + position.x += f32(kern) * font_scale + } + if codepoint == '\n' + { + line_count += 1 + max_line_width = max(max_line_width, position.x) + position.x = 0.0 + position.y -= line_height + position.y = position.y + prev_codepoint = rune(0) + continue + } + if abs( font_px_size ) <= ctx.adv_snap_small_font_threshold { + position.x = ceil(position.x) + } + + glyph_index := parser_find_glyph_index( entry.parser_info, codepoint ) + is_glyph_empty := parser_is_glyph_empty( entry.parser_info, glyph_index ) + + if ctx.snap_glyph_position { + position.x = ceil(position.x) + position.y = ceil(position.y) + } + append( & output.glyph, glyph_index) + append( & output.position, position) + + if ! is_glyph_empty { + append( & output.visible, cast(i32) len(output.glyph) - 1 ) + } + + advance, _ := parser_get_codepoint_horizontal_metrics( entry.parser_info, codepoint ) + position.x += f32(advance) * font_scale + prev_codepoint = codepoint + } + + output.end_cursor_pos = position + max_line_width = max(max_line_width, position.x) + + output.size.x = max_line_width + output.size.y = f32(line_count) * line_height + + // Resolve each glyphs: bounds, atlas lru, and the atlas region as we have everything we need now. + + resize( & output.atlas_lru_code, len(output.glyph) ) + resize( & output.region_kind, len(output.glyph) ) + resize( & output.bounds, len(output.glyph) ) + + profile_begin("atlas_lru_code") + for vis_id, index in output.visible { + glyph_id := output.glyph[vis_id] + output.atlas_lru_code[index] = atlas_glyph_lru_code(entry.id, font_px_size, glyph_id) + // atlas_lru_code is 1:1 with visible index + } + profile_end() + + profile_begin("bounds & region") + for vis_id, index in output.visible { + glyph_id := output.glyph[vis_id] + bounds := & output.bounds[index] + (bounds ^) = parser_get_bounds( entry.parser_info, glyph_id ) + bounds_size_scaled := (bounds.p1 - bounds.p0) * font_scale + output.region_kind[index] = atlas_decide_region( atlas, glyph_buffer_size, bounds_size_scaled ) + // bounds & region_kind are 1:1 with visible index + } + profile_end() + + output.font = font + output.px_size = font_px_size +} + +// Shapes are tracked by the library's context using the shape cache +// and the key is resolved using the font, the desired pixel size, and the text bytes to be shaped. +// Thus this procedures cost will be proporitonal to how much text it has to sift through. +// djb8_hash is used as its been pretty good for thousands of hashed lines that around 6-250 charactes long +// (and its very fast). +@(optimization_mode="favor_size") +shaper_shape_text_cached :: proc( text_utf8 : string, + ctx : ^Shaper_Context, + shape_cache : ^Shaped_Text_Cache, + atlas : Atlas, + glyph_buffer_size : Vec2, + font : Font_ID, + entry : Entry, + font_px_size : f32, + font_scale : f32, + shape_text_uncached : $Shaper_Shape_Text_Uncached_Proc +) -> (shaped_text : Shaped_Text) +{ + profile(#procedure) + font := font + font_px_size := font_px_size + font_bytes := to_bytes( & font ) + size_bytes := to_bytes( & font_px_size ) + text_bytes := transmute( []byte) text_utf8 + + lru_code : Shape_Key + djb8_hash( & lru_code, font_bytes ) + djb8_hash( & lru_code, size_bytes ) + djb8_hash( & lru_code, text_bytes ) + + state := & shape_cache.state + + shape_cache_idx := lru_get( state, lru_code ) + if shape_cache_idx == -1 + { + if shape_cache.next_cache_id < i32(state.capacity){ + shape_cache_idx = shape_cache.next_cache_id + shape_cache.next_cache_id += 1 + evicted := lru_put( state, lru_code, shape_cache_idx ) + } + else + { + next_evict_idx := lru_get_next_evicted( state ^ ) + assert( next_evict_idx != LRU_Fail_Mask_32 ) + + shape_cache_idx = lru_peek( state ^, next_evict_idx, must_find = true ) + assert( shape_cache_idx != - 1 ) + + lru_put( state, lru_code, shape_cache_idx ) + } + + storage_entry := & shape_cache.storage[ shape_cache_idx ] + shape_text_uncached( ctx, atlas, glyph_buffer_size, font, entry, font_px_size, font_scale, text_utf8, storage_entry ) + + shaped_text = storage_entry ^ + return + } + + shaped_text = shape_cache.storage[ shape_cache_idx ] return } diff --git a/vefontcache/vefontcache.odin b/vefontcache/vefontcache.odin index 8ca238c..2cfd37d 100644 --- a/vefontcache/vefontcache.odin +++ b/vefontcache/vefontcache.odin @@ -1,285 +1,381 @@ /* -A port of (https://github.com/hypernewbie/VEFontCache) to Odin. - See: https://github.com/Ed94/VEFontCache-Odin */ package vefontcache -import "base:runtime" +// See: mappings.odin for profiling hookup +DISABLE_PROFILING :: true +ENABLE_OVERSIZED_GLYPHS :: true +// White: Cached Hit, Red: Cache Miss, Yellow: Oversized (Will override user's colors enabled) +ENABLE_DRAW_TYPE_VISUALIZATION :: false -Font_ID :: distinct i64 +Font_ID :: distinct i16 Glyph :: distinct i32 +Load_Font_Error :: enum(i32) { + None, + Parser_Failed, +} + Entry :: struct { parser_info : Parser_Font_Info, shaper_info : Shaper_Info, id : Font_ID, used : b32, curve_quality : f32, - size : f32, - size_scale : f32, + + ascent : f32, + descent : f32, + line_gap : f32, } Entry_Default :: Entry { - id = 0, - used = false, - size = 24.0, - size_scale = 1.0, + id = 0, + used = false, + curve_quality = 3, +} + +// Ease of use encapsulation of common fields for a canvas space +VPZ_Transform :: struct { + view : Vec2, + position : Vec2, + zoom : f32, +} + +Scope_Stack :: struct { + font : [dynamic]Font_ID, + font_size : [dynamic]f32, + colour : [dynamic]RGBAN, + view : [dynamic]Vec2, + position : [dynamic]Vec2, + scale : [dynamic]Vec2, + zoom : [dynamic]f32, } Context :: struct { backing : Allocator, - parser_ctx : Parser_Context, - shaper_ctx : Shaper_Context, + parser_ctx : Parser_Context, // Glyph parser state + shaper_ctx : Shaper_Context, // Text shaper state + // The managed font instances entries : [dynamic]Entry, - temp_path : [dynamic]Vertex, - temp_codepoint_seen : map[u64]bool, - temp_codepoint_seen_num : int, + // TODO(Ed): Review these when preparing to handle lifting of working context to a thread context. + glyph_buffer : Glyph_Draw_Buffer, // -> draw.odin + atlas : Atlas, // -> atlas.odin + shape_cache : Shaped_Text_Cache, // -> shaper.doin + draw_list : Draw_List, // -> draw.odin - snap_width : f32, - snap_height : f32, + batch_shapes_buffer : [dynamic]Shaped_Text, // Used for the procs that batch a layer of text. - colour : Colour, - cursor_pos : Vec2, - - draw_layer : struct { + // Tracks the offsets for the current layer in a draw_list + draw_layer : struct { vertices_offset : int, indices_offset : int, calls_offset : int, }, + // See: get_draw_list_layer & flush_draw_list_layer - draw_list : Draw_List, - atlas : Atlas, - glyph_buffer : Glyph_Draw_Buffer, - shape_cache : Shaped_Text_Cache, + // Note(Ed): Not really used anymore. + // debug_print : b32, + // debug_print_verbose : b32, - default_curve_quality : i32, - use_advanced_shaper : b32, + // Will enforce even px_size when drawing. + even_size_only : f32, - debug_print : b32, - debug_print_verbose : b32, -} + // Whether or not to snap positioning to the pixel of the view + // Helps with hinting + snap_to_view_extent : b32, + stack : Scope_Stack, -Init_Atlas_Region_Params :: struct { - width : u32, - height : u32, + cursor_pos : Vec2, + // Will apply a boost scalar (1.0 + alpha sharpen) to the colour's alpha which provides some sharpening of the edges. + // Has a boldening side-effect. If overblown will look smeared. + alpha_sharpen : f32, + // Used by draw interface to super-scale the text by + // upscaling px_size with px_scalar and then down-scaling + // the draw_list result by the same amount. + px_scalar : f32, // Improves hinting, positioning, etc. Can make zoomed out text too jagged. + zoom_px_interval : f32, // When using zoom, the size can be locked to to this interval (fixes text width jitter) + + default_curve_quality : i32, } -Init_Atlas_Params :: struct { - width : u32, - height : u32, - glyph_padding : u32, // Padding to add to bounds__scaled for choosing which atlas region. - glyph_over_scalar : f32, // Scalar to apply to bounds__scaled for choosing which atlas region. +//#region("Init Params") - region_a : Init_Atlas_Region_Params, - region_b : Init_Atlas_Region_Params, - region_c : Init_Atlas_Region_Params, - region_d : Init_Atlas_Region_Params, +Init_Atlas_Params :: struct { + size_multiplier : u32, // How much to scale the the atlas size to. (Affects everything, the base is 4096 x 2048 and everything follows from there) + glyph_padding : u32, // Padding to add to bounds__scaled for choosing which atlas region. } Init_Atlas_Params_Default :: Init_Atlas_Params { - width = 4096, - height = 2048, - glyph_padding = 1, - glyph_over_scalar = 1, - - region_a = { - width = 32, - height = 32, - }, - region_b = { - width = 32, - height = 64, - }, - region_c = { - width = 64, - height = 64, - }, - region_d = { - width = 128, - height = 128, - } + size_multiplier = 1, + glyph_padding = 1, } Init_Glyph_Draw_Params :: struct { - over_sample : Vec2, - buffer_batch : u32, - draw_padding : u32, + // During the draw list generation stage when blitting to atlas, the quad wil be ceil()'d to the closest pixel. + snap_glyph_height : b32, + // Intended to be x16 (4x4) super-sampling from the glyph buffer to the atlas. + // Oversized glyphs don't use this and instead do 2x or 1x depending on how massive they are. + over_sample : u32, + // Best to just keep this the same as glyph_padding for the atlas.. + draw_padding : u32, + shape_gen_scratch_reserve : u32, + // How many region.D glyphs can be drawn to the glyph render target buffer at once (worst case scenario) + buffer_glyph_limit : u32, + // How many glyphs can at maximimum be proccessed at once by batch_generate_glyphs_draw_list + batch_glyph_limit : u32, } Init_Glyph_Draw_Params_Default :: Init_Glyph_Draw_Params { - over_sample = Vec2 { 4, 4 }, - buffer_batch = 4, - draw_padding = Init_Atlas_Params_Default.glyph_padding, + snap_glyph_height = true, + over_sample = 4, + draw_padding = Init_Atlas_Params_Default.glyph_padding, + shape_gen_scratch_reserve = 512, + buffer_glyph_limit = 16, + batch_glyph_limit = 256, } Init_Shaper_Params :: struct { - use_advanced_text_shaper : b32, + // Forces a glyph position to align to a pixel (make sure to use snap_to_view_extent with this or else it won't be preserveds) snap_glyph_position : b32, + // Will use more signficant advance during shaping for fonts + // Note(Ed): Thinking of removing, doesn't look good often and its an extra condition in the hot-loop. adv_snap_small_font_threshold : u32, } Init_Shaper_Params_Default :: Init_Shaper_Params { - use_advanced_text_shaper = true, snap_glyph_position = true, adv_snap_small_font_threshold = 0, } Init_Shape_Cache_Params :: struct { - capacity : u32, - reserve_length : u32, + // Note(Ed): This should mostly just be given the worst-case capacity and reserve at the same time. + // If memory is a concern it can easily be 256 - 2k if not much text is going to be rendered often. + // Shapes should really not exceed 1024 glyphs.. + capacity : u32, + reserve : u32, } Init_Shape_Cache_Params_Default :: Init_Shape_Cache_Params { - capacity = 8 * 1024, - reserve_length = 256, + capacity = 10 * 1024, + reserve = 128, } +//#endregion("Init Params") + //#region("lifetime") // ve_fontcache_init -startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType, +startup :: proc( ctx : ^Context, parser_kind : Parser_Kind = .STB_TrueType, // Note(Ed): Only sbt_truetype supported for now. allocator := context.allocator, atlas_params := Init_Atlas_Params_Default, glyph_draw_params := Init_Glyph_Draw_Params_Default, shape_cache_params := Init_Shape_Cache_Params_Default, shaper_params := Init_Shaper_Params_Default, + alpha_sharpen : f32 = 0.35, + px_scalar : f32 = 1.6, + zoom_px_interval : i32 = 2, + + // Curve quality to use for a font when unspecified, + // Affects step size for bezier curve passes in generate_glyph_pass_draw_list default_curve_quality : u32 = 3, - entires_reserve : u32 = 512, - temp_path_reserve : u32 = 1024, - temp_codepoint_seen_reserve : u32 = 2048, + entires_reserve : u32 = 256, + scope_stack_reserve : u32 = 32, ) { assert( ctx != nil, "Must provide a valid context" ) - using ctx ctx.backing = allocator context.allocator = ctx.backing - use_advanced_shaper = shaper_params.use_advanced_text_shaper + ctx.alpha_sharpen = alpha_sharpen + ctx.px_scalar = px_scalar + ctx.zoom_px_interval = f32(zoom_px_interval) + + shaper_ctx := & ctx.shaper_ctx shaper_ctx.adv_snap_small_font_threshold = f32(shaper_params.adv_snap_small_font_threshold) shaper_ctx.snap_glyph_position = shaper_params.snap_glyph_position - if default_curve_quality == 0 { - default_curve_quality = 3 - } - ctx.default_curve_quality = default_curve_quality + ctx.default_curve_quality = default_curve_quality == 0 ? 3 : i32(default_curve_quality) error : Allocator_Error - entries, error = make( [dynamic]Entry, len = 0, cap = entires_reserve ) + ctx.entries, error = make( [dynamic]Entry, len = 0, cap = entires_reserve ) assert(error == .None, "VEFontCache.init : Failed to allocate entries") - temp_path, error = make( [dynamic]Vertex, len = 0, cap = temp_path_reserve ) - assert(error == .None, "VEFontCache.init : Failed to allocate temp_path") - - temp_codepoint_seen, error = make( map[u64]bool, uint(temp_codepoint_seen_reserve) ) - assert(error == .None, "VEFontCache.init : Failed to allocate temp_path") - - draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = 4 * Kilobyte ) + ctx.draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = 8 * Kilobyte ) assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.vertices") - draw_list.indices, error = make( [dynamic]u32, len = 0, cap = 8 * Kilobyte ) + ctx.draw_list.indices, error = make( [dynamic]u32, len = 0, cap = 16 * Kilobyte ) assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.indices") - draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = 512 ) + ctx.draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = Kilobyte ) assert(error == .None, "VEFontCache.init : Failed to allocate draw_list.calls") - - init_atlas_region :: proc( region : ^Atlas_Region, params : Init_Atlas_Params, region_params : Init_Atlas_Region_Params, factor : Vec2i, expected_cap : i32 ) + + atlas := & ctx.atlas + Atlas_Setup: { - using region - - next_idx = 0; - width = i32(region_params.width) - height = i32(region_params.height) - size = { - i32(params.width) / factor.x, - i32(params.height) / factor.y, + atlas.size_multiplier = f32(atlas_params.size_multiplier) + + atlas_size := Vec2i { 4096, 2048 } * i32(atlas.size_multiplier) + slot_region_a := Vec2i { 32, 32 } * i32(atlas.size_multiplier) + slot_region_b := Vec2i { 32, 64 } * i32(atlas.size_multiplier) + slot_region_c := Vec2i { 64, 64 } * i32(atlas.size_multiplier) + slot_region_d := Vec2i { 128, 128 } * i32(atlas.size_multiplier) + + init_atlas_region :: proc( region : ^Atlas_Region, atlas_size, slot_size : Vec2i, factor : Vec2i ) + { + region.next_idx = 0; + region.slot_size = slot_size + region.size = atlas_size / factor + region.capacity = region.size / region.slot_size + + error : Allocator_Error + lru_init( & region.state, region.capacity.x * region.capacity.y ) } - capacity = { - size.x / i32(width), - size.y / i32(height), + init_atlas_region( & atlas.region_a, atlas_size, slot_region_a, { 4, 2}) + init_atlas_region( & atlas.region_b, atlas_size, slot_region_b, { 4, 2}) + init_atlas_region( & atlas.region_c, atlas_size, slot_region_c, { 4, 1}) + init_atlas_region( & atlas.region_d, atlas_size, slot_region_d, { 2, 1}) + + atlas.size = atlas_size + atlas.glyph_padding = f32(atlas_params.glyph_padding) + + atlas.region_a.offset = {0, 0} + atlas.region_b.offset.x = 0 + atlas.region_b.offset.y = atlas.region_a.size.y + atlas.region_c.offset.x = atlas.region_a.size.x + atlas.region_c.offset.y = 0 + atlas.region_d.offset.x = atlas.size.x / 2 + atlas.region_d.offset.y = 0 + + atlas.regions = { + nil, + & atlas.region_a, + & atlas.region_b, + & atlas.region_c, + & atlas.region_d, } - assert( capacity.x * capacity.y == expected_cap ) - - error : Allocator_Error - lru_init( & state, capacity.x * capacity.y ) } - init_atlas_region( & atlas.region_a, atlas_params, atlas_params.region_a, { 4, 2}, 1024 ) - init_atlas_region( & atlas.region_b, atlas_params, atlas_params.region_b, { 4, 2}, 512 ) - init_atlas_region( & atlas.region_c, atlas_params, atlas_params.region_c, { 4, 1}, 512 ) - init_atlas_region( & atlas.region_d, atlas_params, atlas_params.region_d, { 2, 1}, 256 ) - - atlas.width = i32(atlas_params.width) - atlas.height = i32(atlas_params.height) - atlas.glyph_padding = i32(atlas_params.glyph_padding) - atlas.glyph_over_scalar = atlas_params.glyph_over_scalar - - atlas.region_a.offset = {0, 0} - atlas.region_b.offset.x = 0 - atlas.region_b.offset.y = atlas.region_a.size.y - atlas.region_c.offset.x = atlas.region_a.size.x - atlas.region_c.offset.y = 0 - atlas.region_d.offset.x = atlas.width / 2 - atlas.region_d.offset.y = 0 - - lru_init( & shape_cache.state, i32(shape_cache_params.capacity) ) - - shape_cache.storage, error = make( [dynamic]Shaped_Text, shape_cache_params.capacity ) - assert(error == .None, "VEFontCache.init : Failed to allocate shape_cache.storage") - - for idx : u32 = 0; idx < shape_cache_params.capacity; idx += 1 { - stroage_entry := & shape_cache.storage[idx] - using stroage_entry - glyphs, error = make( [dynamic]Glyph, len = 0, cap = shape_cache_params.reserve_length ) - assert( error == .None, "VEFontCache.init : Failed to allocate glyphs array for shape cache storage" ) - - positions, error = make( [dynamic]Vec2, len = 0, cap = shape_cache_params.reserve_length ) - assert( error == .None, "VEFontCache.init : Failed to allocate positions array for shape cache storage" ) - - draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) - assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) - - draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 6 ) - assert( error == .None, "VEFontCache.init : Failed to allocate indices array for draw_list" ) - - draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 ) - assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" ) + + Shape_Cache_Setup: + { + shape_cache := & ctx.shape_cache + lru_init( & shape_cache.state, i32(shape_cache_params.capacity) ) + + shape_cache.storage, error = make( [dynamic]Shaped_Text, shape_cache_params.capacity ) + assert(error == .None, "VEFontCache.init : Failed to allocate shape_cache.storage") + + for idx : u32 = 0; idx < shape_cache_params.capacity; idx += 1 + { + storage_entry := & shape_cache.storage[idx] + + storage_entry.glyph, error = make( [dynamic]Glyph, len = 0, cap = shape_cache_params.reserve ) + assert( error == .None, "VEFontCache.init : Failed to allocate glyphs array for shape cache storage" ) + + storage_entry.position, error = make( [dynamic]Vec2, len = 0, cap = shape_cache_params.reserve ) + assert( error == .None, "VEFontCache.init : Failed to allocate positions array for shape cache storage" ) + + storage_entry.visible, error = make( [dynamic]i32, len = 0, cap = shape_cache_params.reserve ) + assert( error == .None, "VEFontCache.init : Failed to allocate visible array for shape cache storage" ) + + storage_entry.atlas_lru_code, error = make( [dynamic]Atlas_Key, len = 0, cap = shape_cache_params.reserve ) + assert( error == .None, "VEFontCache.init : Failed to allocate atlas_lru_code array for shape cache storage" ) + + storage_entry.region_kind, error = make( [dynamic]Atlas_Region_Kind, len = 0, cap = shape_cache_params.reserve ) + assert( error == .None, "VEFontCache.init : Failed to allocate region_kind array for shape cache storage" ) + + storage_entry.bounds, error = make( [dynamic]Range2, len = 0, cap = shape_cache_params.reserve ) + assert( error == .None, "VEFontCache.init : Failed to allocate bounds array for shape cache storage" ) + } } - // Note(From original author): We can actually go over VE_FONTCACHE_GLYPHDRAW_BUFFER_BATCH batches due to smart packing! + Glyph_Buffer_Setup: { - using glyph_buffer - over_sample = glyph_draw_params.over_sample - batch = cast(i32) glyph_draw_params.buffer_batch - width = atlas.region_d.width * i32(over_sample.x) * batch - height = atlas.region_d.height * i32(over_sample.y) - draw_padding = cast(i32) glyph_draw_params.draw_padding + glyph_buffer := & ctx.glyph_buffer + glyph_buffer.snap_glyph_height = cast(f32) i32(glyph_draw_params.snap_glyph_height) + glyph_buffer.over_sample = { f32(glyph_draw_params.over_sample), f32(glyph_draw_params.over_sample) } + glyph_buffer.size.x = atlas.region_d.slot_size.x * i32(glyph_buffer.over_sample.x) * i32(glyph_draw_params.buffer_glyph_limit) + glyph_buffer.size.y = atlas.region_d.slot_size.y * i32(glyph_buffer.over_sample.y) + glyph_buffer.draw_padding = cast(f32) glyph_draw_params.draw_padding - draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) - assert( error == .None, "VEFontCache.init : Failed to allocate calls for draw_list" ) + glyph_buffer.draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = 8 * Kilobyte ) + assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for glyph_buffer.draw_list" ) - draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 6 ) - assert( error == .None, "VEFontCache.init : Failed to allocate indices array for draw_list" ) + glyph_buffer.draw_list.indices, error = make( [dynamic]u32, len = 0, cap = 16 * Kilobyte ) + assert( error == .None, "VEFontCache.init : Failed to allocate indices for glyph_buffer.draw_list" ) - draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 ) - assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for draw_list" ) + glyph_buffer.draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = Kilobyte ) + assert( error == .None, "VEFontCache.init : Failed to allocate calls for glyph_buffer.draw_list" ) - clear_draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = glyph_draw_params.buffer_batch * 2 ) - assert( error == .None, "VEFontCache.init : Failed to allocate calls for calls for clear_draw_list" ) + glyph_buffer.clear_draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = 2 * Kilobyte ) + assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" ) - clear_draw_list.indices, error = make( [dynamic]u32, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 ) + glyph_buffer.clear_draw_list.indices, error = make( [dynamic]u32, len = 0, cap = 4 * Kilobyte ) assert( error == .None, "VEFontCache.init : Failed to allocate calls for indices array for clear_draw_list" ) - clear_draw_list.vertices, error = make( [dynamic]Vertex, len = 0, cap = glyph_draw_params.buffer_batch * 2 * 4 ) - assert( error == .None, "VEFontCache.init : Failed to allocate vertices array for clear_draw_list" ) + glyph_buffer.clear_draw_list.calls, error = make( [dynamic]Draw_Call, len = 0, cap = Kilobyte ) + assert( error == .None, "VEFontCache.init : Failed to allocate calls for calls for clear_draw_list" ) + + glyph_buffer.shape_gen_scratch, error = make( [dynamic]Vertex, len = 0, cap = glyph_draw_params.shape_gen_scratch_reserve ) + assert(error == .None, "VEFontCache.init : Failed to allocate shape_gen_scratch") + + batch_cache := & glyph_buffer.batch_cache + batch_cache.cap = i32(glyph_draw_params.batch_glyph_limit) + batch_cache.num = 0 + batch_cache.table, error = make( map[Atlas_Key]b8, uint(glyph_draw_params.batch_glyph_limit) ) + assert(error == .None, "VEFontCache.init : Failed to allocate batch_cache") + + glyph_buffer.glyph_pack,error = make_soa( #soa[dynamic]Glyph_Pack_Entry, length = 0, capacity = uint(shape_cache_params.reserve) ) + glyph_buffer.oversized, error = make( [dynamic]i32, len = 0, cap = uint(shape_cache_params.reserve) ) + glyph_buffer.to_cache, error = make( [dynamic]i32, len = 0, cap = uint(shape_cache_params.reserve) ) + glyph_buffer.cached, error = make( [dynamic]i32, len = 0, cap = uint(shape_cache_params.reserve) ) + } + + parser_init( & ctx.parser_ctx, parser_kind ) + shaper_init( & ctx.shaper_ctx ) + + // Scoping Stack + { + stack := & ctx.stack + + error : Allocator_Error + stack.font, error = make([dynamic]Font_ID, len = 0, cap = scope_stack_reserve) + assert(error == .None, "VEFontCache.init : Failed to allocate stack.font") + + stack.font_size, error = make([dynamic]f32, len = 0, cap = scope_stack_reserve) + assert(error == .None, "VEFontCache.init : Failed to allocate stack.font_size") + + stack.font_size, error = make([dynamic]f32, len = 0, cap = scope_stack_reserve) + assert(error == .None, "VEFontCache.init : Failed to allocate stack.font_size") + + stack.colour, error = make([dynamic]RGBAN, len = 0, cap = scope_stack_reserve) + assert(error == .None, "VEFontCache.init : Failed to allocate stack.colour") + + stack.view, error = make([dynamic]Vec2, len = 0, cap = scope_stack_reserve) + assert(error == .None, "VEFontCache.init : Failed to allocate stack.view") + + stack.position, error = make([dynamic]Vec2, len = 0, cap = scope_stack_reserve) + assert(error == .None, "VEFontCache.init : Failed to allocate stack.position") + + stack.scale, error = make([dynamic]Vec2, len = 0, cap = scope_stack_reserve) + assert(error == .None, "VEFontCache.init : Failed to allocate stack.scale") + + stack.zoom, error = make([dynamic]f32, len = 0, cap = scope_stack_reserve) + assert(error == .None, "VEFontCache.init : Failed to allocate stack.zoom") } - parser_init( & parser_ctx, parser_kind ) - shaper_init( & shaper_ctx ) + // Set the default stack values + // Will be popped on shutdown + push_colour(ctx, {1, 1, 1, 1}) + push_font_size(ctx, 36) + push_view(ctx, { 0, 0 }) + push_position(ctx, {0, 0}) + push_scale(ctx, 1.0) + push_zoom(ctx, 1.0) } hot_reload :: proc( ctx : ^Context, allocator : Allocator ) @@ -287,15 +383,29 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator ) assert( ctx != nil ) ctx.backing = allocator context.allocator = ctx.backing - using ctx - reload_array( & entries, allocator ) - reload_array( & temp_path, allocator ) - reload_map( & ctx.temp_codepoint_seen, allocator ) + atlas := & ctx.atlas + glyph_buffer := & ctx.glyph_buffer + shape_cache := & ctx.shape_cache + draw_list := & ctx.draw_list - reload_array( & draw_list.vertices, allocator) - reload_array( & draw_list.indices, allocator ) - reload_array( & draw_list.calls, allocator ) + reload_array( & ctx.entries, allocator ) + + reload_array( & glyph_buffer.draw_list.calls, allocator ) + reload_array( & glyph_buffer.draw_list.indices, allocator ) + reload_array( & glyph_buffer.draw_list.vertices, allocator ) + + reload_array( & glyph_buffer.clear_draw_list.calls, allocator ) + reload_array( & glyph_buffer.clear_draw_list.indices, allocator ) + reload_array( & glyph_buffer.clear_draw_list.vertices, allocator ) + + reload_map( & glyph_buffer.batch_cache.table, allocator ) + reload_array( & glyph_buffer.shape_gen_scratch, allocator ) + + reload_array_soa( & glyph_buffer.glyph_pack, allocator ) + reload_array( & glyph_buffer.oversized, allocator ) + reload_array( & glyph_buffer.to_cache, allocator ) + reload_array( & glyph_buffer.cached, allocator ) lru_reload( & atlas.region_a.state, allocator) lru_reload( & atlas.region_b.state, allocator) @@ -304,42 +414,72 @@ hot_reload :: proc( ctx : ^Context, allocator : Allocator ) lru_reload( & shape_cache.state, allocator ) for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 { - stroage_entry := & shape_cache.storage[idx] - using stroage_entry - - reload_array( & glyphs, allocator ) - reload_array( & positions, allocator ) + storage_entry := & shape_cache.storage[idx] + reload_array( & storage_entry.glyph, allocator) + reload_array( & storage_entry.position, allocator) + reload_array( & storage_entry.visible, allocator) + reload_array( & storage_entry.atlas_lru_code, allocator) + reload_array( & storage_entry.region_kind, allocator) + reload_array( & storage_entry.bounds, allocator) } + reload_array( & shape_cache.storage, allocator ) + + reload_array( & draw_list.vertices, allocator) + reload_array( & draw_list.indices, allocator) + reload_array( & draw_list.calls, allocator) - reload_array( & glyph_buffer.draw_list.calls, allocator ) - reload_array( & glyph_buffer.draw_list.indices, allocator ) - reload_array( & glyph_buffer.draw_list.vertices, allocator ) + parser_reload(& ctx.parser_ctx, allocator) - reload_array( & glyph_buffer.clear_draw_list.calls, allocator ) - reload_array( & glyph_buffer.clear_draw_list.indices, allocator ) - reload_array( & glyph_buffer.clear_draw_list.vertices, allocator ) - - reload_array( & shape_cache.storage, allocator ) + // Scope Stack + { + stack := & ctx.stack + reload_array(& stack.font, allocator) + reload_array(& stack.font_size, allocator) + reload_array(& stack.colour, allocator) + reload_array(& stack.view, allocator) + reload_array(& stack.position, allocator) + reload_array(& stack.scale, allocator) + reload_array(& stack.zoom, allocator) + } } -// ve_foncache_shutdown shutdown :: proc( ctx : ^Context ) { assert( ctx != nil ) context.allocator = ctx.backing - using ctx - for & entry in entries { + atlas := & ctx.atlas + glyph_buffer := & ctx.glyph_buffer + shape_cache := & ctx.shape_cache + draw_list := & ctx.draw_list + + pop_colour(ctx) + pop_font_size(ctx) + pop_view(ctx) + pop_position(ctx) + pop_scale(ctx) + pop_zoom(ctx) + + for & entry in ctx.entries { unload_font( ctx, entry.id ) } + delete( ctx.entries ) + + delete( glyph_buffer.draw_list.vertices ) + delete( glyph_buffer.draw_list.indices ) + delete( glyph_buffer.draw_list.calls ) - delete( entries ) - delete( temp_path ) - delete( temp_codepoint_seen ) + delete( glyph_buffer.clear_draw_list.vertices ) + delete( glyph_buffer.clear_draw_list.indices ) + delete( glyph_buffer.clear_draw_list.calls ) - delete( draw_list.vertices ) - delete( draw_list.indices ) - delete( draw_list.calls ) + delete( glyph_buffer.batch_cache.table ) + delete( glyph_buffer.shape_gen_scratch ) + + delete_soa( glyph_buffer.glyph_pack) + delete( glyph_buffer.oversized) + delete( glyph_buffer.to_cache) + delete( glyph_buffer.cached) lru_free( & atlas.region_a.state ) lru_free( & atlas.region_b.state ) @@ -347,63 +487,82 @@ shutdown :: proc( ctx : ^Context ) lru_free( & atlas.region_d.state ) for idx : i32 = 0; idx < i32(len(shape_cache.storage)); idx += 1 { - stroage_entry := & shape_cache.storage[idx] - using stroage_entry - - delete( glyphs ) - delete( positions ) + storage_entry := & shape_cache.storage[idx] + delete( storage_entry.glyph ) + delete( storage_entry.position ) + delete( storage_entry.visible ) + delete( storage_entry.atlas_lru_code) + delete( storage_entry.region_kind) + delete( storage_entry.bounds) } lru_free( & shape_cache.state ) + + delete( draw_list.vertices ) + delete( draw_list.indices ) + delete( draw_list.calls ) - delete( glyph_buffer.draw_list.vertices ) - delete( glyph_buffer.draw_list.indices ) - delete( glyph_buffer.draw_list.calls ) - - delete( glyph_buffer.clear_draw_list.vertices ) - delete( glyph_buffer.clear_draw_list.indices ) - delete( glyph_buffer.clear_draw_list.calls ) + shaper_shutdown( & ctx.shaper_ctx ) + parser_shutdown( & ctx.parser_ctx ) - shaper_shutdown( & shaper_ctx ) - parser_shutdown( & parser_ctx ) + // Scope Stack + { + stack := & ctx.stack + delete(stack.font) + delete(stack.font_size) + delete(stack.colour) + delete(stack.view) + delete(stack.position) + delete(stack.scale) + delete(stack.zoom) + } } -// ve_fontcache_load -load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, glyph_curve_quality : u32 = 0 ) -> (font_id : Font_ID) +load_font :: proc( ctx : ^Context, label : string, data : []byte, glyph_curve_quality : u32 = 0 ) -> (font_id : Font_ID, error : Load_Font_Error) { + profile(#procedure) assert( ctx != nil ) assert( len(data) > 0 ) - using ctx - context.allocator = backing + context.allocator = ctx.backing - id : i32 = -1 + entries := & ctx.entries + + id : Font_ID = -1 for index : i32 = 0; index < i32(len(entries)); index += 1 { if entries[index].used do continue - id = index + id = Font_ID(index) break } if id == -1 { - append_elem( & entries, Entry {}) - id = cast(i32) len(entries) - 1 + append_elem( entries, Entry {}) + id = cast(Font_ID) len(entries) - 1 } - assert( id >= 0 && id < i32(len(entries)) ) + assert( id >= 0 && id < Font_ID(len(entries)) ) entry := & entries[ id ] { - using entry - used = true - - parser_info = parser_load_font( & parser_ctx, label, data ) - shaper_info = shaper_load_font( & shaper_ctx, label, data, transmute(rawptr) id ) + entry.used = true + + profile_begin("calling loaders") + parser_error : b32 + entry.parser_info, parser_error = parser_load_font( & ctx.parser_ctx, label, data ) + if parser_error { + error = .Parser_Failed + return + } + entry.shaper_info = shaper_load_font( & ctx.shaper_ctx, label, data ) + profile_end() - size = size_px - size_scale = parser_scale( & parser_info, size ) + ascent, descent, line_gap := parser_get_font_vertical_metrics(entry.parser_info) + entry.ascent = f32(ascent) + entry.descent = f32(descent) + entry.line_gap = f32(line_gap) if glyph_curve_quality == 0 { - curve_quality = f32(ctx.default_curve_quality) + entry.curve_quality = f32(ctx.default_curve_quality) } else { - curve_quality = f32(glyph_curve_quality) + entry.curve_quality = f32(glyph_curve_quality) } } entry.id = Font_ID(id) @@ -413,73 +572,547 @@ load_font :: proc( ctx : ^Context, label : string, data : []byte, size_px : f32, return } -// ve_fontcache_unload unload_font :: proc( ctx : ^Context, font : Font_ID ) { assert( ctx != nil ) assert( font >= 0 && int(font) < len(ctx.entries) ) context.allocator = ctx.backing - using ctx entry := & ctx.entries[ font ] entry.used = false - parser_unload_font( & entry.parser_info ) shaper_unload_font( & entry.shaper_info ) + parser_unload_font( & entry.parser_info ) +} + +// Can be used with hot-reload +clear_atlas_region_caches :: proc(ctx : ^Context) +{ + lru_clear(& ctx.atlas.region_a.state) + lru_clear(& ctx.atlas.region_b.state) + lru_clear(& ctx.atlas.region_c.state) + lru_clear(& ctx.atlas.region_d.state) + + ctx.atlas.region_a.next_idx = 0 + ctx.atlas.region_b.next_idx = 0 + ctx.atlas.region_c.next_idx = 0 + ctx.atlas.region_d.next_idx = 0 +} + +// Can be used with hot-reload +clear_shape_cache :: proc (ctx : ^Context) +{ + lru_clear(& ctx.shape_cache.state) + for idx : i32 = 0; idx < cast(i32) cap(ctx.shape_cache.storage); idx += 1 { + storage_entry := & ctx.shape_cache.storage[idx] + storage_entry.end_cursor_pos = {} + storage_entry.size = {} + clear(& storage_entry.glyph) + clear(& storage_entry.position) + } + ctx.shape_cache.next_cache_id = 0 } //#endregion("lifetime") -//#region("drawing") +//#region("shaping") + +// For high performance, the user should track the shapes and use the draw list interface on shapes. +// Doing so avoids cache lookups. + +shape_text :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, text_utf8 : string, + shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz +) -> Shaped_Text +{ + profile(#procedure) + assert( len(text_utf8) > 0 ) + entry := ctx.entries[ font ] + + target_px_size := px_size * ctx.px_scalar + target_font_scale := parser_scale( entry.parser_info, target_px_size ) + + return shaper_shape_text_cached( text_utf8, + & ctx.shaper_ctx, + & ctx.shape_cache, + ctx.atlas, + vec2(ctx.glyph_buffer.size), + font, + entry, + target_px_size, + target_font_scale, + shaper_proc + ) +} + +// User handled shaped text. Will not be cached +shape_text_uncached :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size: f32, text_utf8 : string, shape : ^Shaped_Text, + shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz +) +{ + profile(#procedure) + assert( len(text_utf8) > 0 ) + entry := ctx.entries[ font ] + + target_px_size := px_size * ctx.px_scalar + target_font_scale := parser_scale( entry.parser_info, target_px_size ) + + shaper_proc(& ctx.shaper_ctx, + ctx.atlas, + vec2(ctx.glyph_buffer.size), + font, + entry, + target_px_size, + target_font_scale, + text_utf8, + shape + ) + return +} + +//#endregion("shaping") + +//#region("draw_list generation") + +/* The most basic interface-level draw shape procedure. + Context's stack is not used. Only modifications for alpha sharpen and px_scalar are applied. + view, position, and scale are expected to be in unsigned normalized space: + +| +----------------------------------+ (1.0, 1.0) +| | | +| | | +| | Glyph Quad | +| | +--------+ < scale.y | +| | | ** | * | | +| | | * * | **** | | +| | | **** | * * | | +| | | * * | **** | | +| | +--------+--------+.... | +| | position ^ ^ scale.x | +| | | +| | | +| | | +| (0.0, 0.0) +----------------------------------+ + + • position: Anchor point in normalized space (where the bottom-right vertex of the first glyph quad will be positioned) + <-> scale : Scale the glyph beyond its default scaling from its px_size. +*/ +@(optimization_mode="favor_size") +draw_shape_normalized_space :: #force_inline proc( ctx : ^Context, + colour : RGBAN, + position : Vec2, + scale : Vec2, + shape : Shaped_Text +) +{ + profile(#procedure) + assert( ctx != nil ) -// ve_fontcache_configure_snap -configure_snap :: #force_inline proc( ctx : ^Context, snap_width, snap_height : u32 ) { + entry := ctx.entries[ shape.font ] + + should_alpha_sharpen := cast(f32) cast(i32) (colour.a >= 1.0) + adjusted_colour := colour + adjusted_colour.a += ctx.alpha_sharpen * should_alpha_sharpen + + target_px_size := shape.px_size + target_scale := scale * (1 / ctx.px_scalar) + target_font_scale := parser_scale( entry.parser_info, shape.px_size ) + + ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, + ctx.px_scalar, + adjusted_colour, + entry, + target_px_size, + target_font_scale, + position, + target_scale, + ) +} + +/* Non-scoping context. The most basic interface-level draw shape procedure (everything else is quality of life warppers). + + Context's stack is not used. Only modifications for alpha sharpen and px_scalar are applied. + view, position, and scale are expected to be in unsigned normalized space: + +| +----------------------------------+ (1.0, 1.0) +| | | +| | | +| | Glyph Quad | +| | +---------+ < scale.y | +| | | ** | * | | +| | | * * | **** | | +| | | **** | * * | | +| | | * * | **** | | +| | +--------+--------+.... | +| | position ^ ^ scale.x | +| | | +| | | +| | | +| (0.0, 0.0) +----------------------------------+ + + • position: Anchor point in normalized space (where the bottom-right vertex of the first glyph quad will be positioned) + <-> scale : Scale the glyph beyond its default scaling from its px_size. +*/ +@(optimization_mode = "favor_size") +draw_text_normalized_space :: proc( ctx : ^Context, + font : Font_ID, + px_size : f32, + colour : RGBAN, + position : Vec2, + scale : Vec2, + text_utf8 : string, + shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz +) +{ + profile(#procedure) assert( ctx != nil ) - ctx.snap_width = f32(snap_width) - ctx.snap_height = f32(snap_height) + assert( font >= 0 && int(font) < len(ctx.entries) ) + assert( len(text_utf8) > 0 ) + + ctx.cursor_pos = {} + entry := ctx.entries[ font ] + + should_alpha_sharpen := cast(f32) cast(i32) (colour.a >= 1.0) + adjusted_colour := colour + adjusted_colour.a += ctx.alpha_sharpen * should_alpha_sharpen + + // Does nothing when px_scalar is 1.0 + target_px_size := px_size * ctx.px_scalar + target_scale := scale * (1 / ctx.px_scalar) + target_font_scale := parser_scale( entry.parser_info, target_px_size ) + + shape := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, ctx.atlas, vec2(ctx.glyph_buffer.size), + font, + entry, + target_px_size, + target_font_scale, + shaper_proc + ) + ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, + ctx.px_scalar, + adjusted_colour, + entry, + target_px_size, + target_font_scale, + position, + target_scale, + ) } -get_cursor_pos :: #force_inline proc( ctx : ^Context ) -> Vec2 { assert(ctx != nil); return ctx.cursor_pos } -set_colour :: #force_inline proc( ctx : ^Context, colour : Colour ) { assert(ctx != nil); ctx.colour = colour } +/* Equivalent to draw_text_shape_normalized_space, however the coordinate space is expected to be relative to the view. + view, position, and scale are expected to be in unsigned view space: + +| +----------------------------------+ (view.x, view.y) +| | | +| | | +| | Glyph Quad | +| | +---------+ < scale.y | +| | | ** | * | | +| | | * * | **** | | +| | | **** | * * | | +| | | * * | **** | | +| | +--------+--------+.... | +| | position ^ ^ scale.x | +| | | +| | | +| | | +| (0.0, 0.0) +----------------------------------+ + + □ view : The coordinate space is scaled to the view. Positions will be snapped to it. + • position: Anchor point in normalized space (where the bottom-right vertex of the first glyph quad will be positioned) + <-> scale : Scale the glyph beyond its default scaling from its px_size. + zoom : Will affect the scale similar to how the zoom on a canvas would behave. +*/ +// @(optimization_mode="favor_size") +draw_shape_view_space :: #force_inline proc( ctx : ^Context, + colour : RGBAN, + view : Vec2, + position : Vec2, + scale : Vec2, + zoom : f32, + shape : Shaped_Text +) +{ + profile(#procedure) + assert( ctx != nil ) + // TODO(Ed): This should be taken from the shape instead (you cannot use a different font with a shape) + assert( ctx.px_scalar > 0.0 ) + + entry := ctx.entries[ shape.font ] + + should_alpha_sharpen := cast(f32) cast(i32) (colour.a >= 1.0) + adjusted_colour := colour + adjusted_colour.a += ctx.alpha_sharpen * should_alpha_sharpen + + px_scalar_quotient := (1 / ctx.px_scalar) + px_size := shape.px_size * px_scalar_quotient + + resolved_size, zoom_scale := resolve_zoom_size_scale( zoom, px_size, scale, ctx.zoom_px_interval, 2, 999.0, view ) + target_position, norm_scale := get_normalized_position_scale( position, zoom_scale, view ) + + // Does nothing if px_scalar is 1.0 + target_px_size := resolved_size * ctx.px_scalar + target_scale := norm_scale * px_scalar_quotient + target_font_scale := parser_scale( entry.parser_info, target_px_size ) + + ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, + ctx.px_scalar, + adjusted_colour, + entry, + target_px_size, + target_font_scale, + target_position, + target_scale, + ) +} -draw_text :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string, position, scale : Vec2 ) -> b32 +/* Equivalent to draw_text_shape_normalized_space, however the coordinate space is expected to be relative to the view. + view, position, and scale are expected to be in unsigned view space: + +| +----------------------------------+ (view.x, view.y) +| | | +| | | +| | Glyph Quad | +| | +---------+ < scale.y | +| | | ** | * | | +| | | * * | **** | | +| | | **** | * * | | +| | | * * | **** | | +| | +--------+--------+.... | +| | position ^ ^ scale.x | +| | | +| | | +| | | +| (0.0, 0.0) +----------------------------------+ + + □ view : The coordinate space is scaled to the view. Positions will be snapped to it. + • position: Anchor point in normalized space (where the bottom-right vertex of the first glyph quad will be positioned) + <-> scale : Scale the glyph beyond its default scaling from its px_size. + zoom : Will affect the scale similar to how the zoom on a canvas would behave. +*/ +// @(optimization_mode = "favor_size") +draw_text_view_space :: proc(ctx : ^Context, + font : Font_ID, + px_size : f32, + colour : RGBAN, + view : Vec2, + position : Vec2, + scale : Vec2, + zoom : f32, + text_utf8 : string, + shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz +) { - // profile(#procedure) + profile(#procedure) assert( ctx != nil ) assert( font >= 0 && int(font) < len(ctx.entries) ) + assert( len(text_utf8) > 0 ) + assert( ctx.px_scalar > 0.0 ) ctx.cursor_pos = {} + entry := ctx.entries[ font ] + + should_alpha_sharpen := cast(f32) cast(i32) (colour.a >= 1.0) + adjusted_colour := colour + adjusted_colour.a += ctx.alpha_sharpen * should_alpha_sharpen + + resolved_size, zoom_scale := resolve_zoom_size_scale( zoom, px_size, scale, ctx.zoom_px_interval, 2, 999.0, view ) + target_position, norm_scale := get_normalized_position_scale( position, zoom_scale, view ) + + // Does nothing if px_scalar is 1.0 + target_px_size := resolved_size * ctx.px_scalar + target_scale := norm_scale * (1 / ctx.px_scalar) + target_font_scale := parser_scale( entry.parser_info, target_px_size ) + + shape := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, ctx.atlas, vec2(ctx.glyph_buffer.size), + font, + entry, + target_px_size, + target_font_scale, + shaper_proc + ) + ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, + ctx.px_scalar, + adjusted_colour, + entry, + target_px_size, + target_font_scale, + target_position, + target_scale, + ) +} - position := position - if ctx.snap_width > 0 do position.x = ceil(position.x * ctx.snap_width ) / ctx.snap_width - if ctx.snap_height > 0 do position.y = ceil(position.y * ctx.snap_height) / ctx.snap_height +/* Uses the ctx.stack, position and scale are relative to the position and scale on the stack. + +absolute_position := peek(stack.position) + position +absolute_scale := peek(stack.scale ) * scale + +| +-----------------------------------+ (view.x, view.y) +| | | +| | | +| | Glyph Quad absolute | +| | +---------+ < scale.y | +| | | ** | * | | +| | | * * | **** | | +| | | **** | * * | | +| | | * * | **** | | +| | +---------+--------+.... | +| | absolute ^ ^ absolute | +| | position scale.x | +| | | +| | | +| | | +| (0.0, 0.0) +-----------------------------------+ +*/ +// @(optimization_mode = "favor_size") +draw_shape :: proc( ctx : ^Context, position, scale : Vec2, shape : Shaped_Text ) +{ + profile(#procedure) + assert( ctx != nil ) + assert( ctx.px_scalar > 0.0 ) - entry := & ctx.entries[ font ] + stack := & ctx.stack + assert(len(stack.view) > 0) + assert(len(stack.colour) > 0) + assert(len(stack.position) > 0) + assert(len(stack.scale) > 0) + assert(len(stack.zoom) > 0) - ChunkType :: enum u32 { Visible, Formatting } - chunk_kind : ChunkType - chunk_start : int = 0 - chunk_end : int = 0 + // TODO(Ed): This should be taken from the shape instead (you cannot use a different font with a shape) + font := peek(stack.font) + assert( font >= 0 &&int(font) < len(ctx.entries) ) - text_utf8_bytes := transmute([]u8) text_utf8 - text_chunk : string + view := peek(stack.view); - text_chunk = transmute(string) text_utf8_bytes[ : ] - if len(text_chunk) > 0 { - shaped := shape_text_cached( ctx, font, text_chunk, entry ) - ctx.cursor_pos = draw_text_shape( ctx, font, entry, shaped, position, scale, ctx.snap_width, ctx.snap_height ) - } - return true + ctx.cursor_pos = {} + entry := ctx.entries[ font ] + + colour := peek(stack.colour) + should_alpha_sharpen := cast(f32) cast(i32) (colour.a >= 1.0) + adjusted_colour := colour + adjusted_colour.a += ctx.alpha_sharpen * should_alpha_sharpen + + px_scalar_quotient := 1 / ctx.px_scalar + + px_size := shape.px_size * px_scalar_quotient + zoom := peek(stack.zoom) + + resolved_size, zoom_scale := resolve_zoom_size_scale( zoom, px_size, scale, ctx.zoom_px_interval, 2, 999.0, view ) + + absolute_position := peek(stack.position) + position + absolute_scale := peek(stack.scale) * zoom_scale + + target_position, norm_scale := get_normalized_position_scale( absolute_position, absolute_scale, view ) + + // Does nothing when px_scalar is 1.0 + target_px_size := resolved_size * ctx.px_scalar + target_scale := norm_scale * px_scalar_quotient + target_font_scale := parser_scale( entry.parser_info, target_px_size ) + + ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, + ctx.px_scalar, + adjusted_colour, + entry, + target_px_size, + target_font_scale, + target_position, + target_scale, + ) } -// ve_fontcache_Draw_List -get_draw_list :: proc( ctx : ^Context, optimize_before_returning := true ) -> ^Draw_List { +/* Uses the ctx.stack, position and scale are relative to the position and scale on the stack. + +absolute_position := peek(stack.position) + position +absolute_scale := peek(stack.scale ) * scale + +| +-----------------------------------+ (view.x, view.y) +| | | +| | | +| | Glyph Quad absolute | +| | +---------+ < scale.y | +| | | ** | * | | +| | | * * | **** | | +| | | **** | * * | | +| | | * * | **** | | +| | +---------+--------+.... | +| | absolute ^ ^ absolute | +| | position scale.x | +| | | +| | | +| | | +| (0.0, 0.0) +-----------------------------------+ +*/ +// @(optimization_mode = "favor_size") +draw_text :: proc( ctx : ^Context, position, scale : Vec2, text_utf8 : string, + shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz +) +{ + profile(#procedure) + assert( ctx != nil ) + assert( len(text_utf8) > 0 ) + assert( ctx.px_scalar > 0.0 ) + + stack := & ctx.stack + assert(len(stack.font) > 0) + assert(len(stack.font_size) > 0) + assert(len(stack.colour) > 0) + assert(len(stack.view) > 0) + assert(len(stack.position) > 0) + assert(len(stack.scale) > 0) + assert(len(stack.zoom) > 0) + + font := peek(stack.font) + assert( font >= 0 &&int(font) < len(ctx.entries) ) + + view := peek(stack.view); + + ctx.cursor_pos = {} + entry := ctx.entries[ font ] + + colour := peek(stack.colour) + should_alpha_sharpen := cast(f32) cast(i32) (colour.a >= 1.0) + adjusted_colour := colour + adjusted_colour.a += ctx.alpha_sharpen * should_alpha_sharpen + + px_size := peek(stack.font_size) + zoom := peek(stack.zoom) + + resolved_size, zoom_scale := resolve_zoom_size_scale( zoom, px_size, scale, ctx.zoom_px_interval, 2, 999.0, view ) + + absolute_position := peek(stack.position) + position + absolute_scale := peek(stack.scale) * scale + + target_position, norm_scale := get_normalized_position_scale( absolute_position, absolute_scale, view ) + + // Does nothing when px_scalar is 1.0 + target_px_size := resolved_size * ctx.px_scalar + target_scale := norm_scale * (1 / ctx.px_scalar) + target_font_scale := parser_scale( entry.parser_info, target_px_size ) + + shape := shaper_shape_text_cached( text_utf8, & ctx.shaper_ctx, & ctx.shape_cache, ctx.atlas, vec2(ctx.glyph_buffer.size), + font, + entry, + target_px_size, + target_font_scale, + shaper_proc + ) + ctx.cursor_pos = generate_shape_draw_list( & ctx.draw_list, shape, & ctx.atlas, & ctx.glyph_buffer, + ctx.px_scalar, + adjusted_colour, + entry, + target_px_size, + target_font_scale, + target_position, + target_scale, + ) +} + +get_draw_list :: #force_inline proc( ctx : ^Context, optimize_before_returning := true ) -> ^Draw_List { assert( ctx != nil ) if optimize_before_returning do optimize_draw_list( & ctx.draw_list, 0 ) return & ctx.draw_list } -get_draw_list_layer :: proc( ctx : ^Context, optimize_before_returning := true ) -> (vertices : []Vertex, indices : []u32, calls : []Draw_Call) { +get_draw_list_layer :: #force_inline proc( ctx : ^Context, optimize_before_returning := true ) -> (vertices : []Vertex, indices : []u32, calls : []Draw_Call) { assert( ctx != nil ) if optimize_before_returning do optimize_draw_list( & ctx.draw_list, ctx.draw_layer.calls_offset ) vertices = ctx.draw_list.vertices[ ctx.draw_layer.vertices_offset : ] @@ -488,85 +1121,241 @@ get_draw_list_layer :: proc( ctx : ^Context, optimize_before_returning := true ) return } -// ve_fontcache_flush_Draw_List -flush_draw_list :: proc( ctx : ^Context ) { +flush_draw_list :: #force_inline proc( ctx : ^Context ) { assert( ctx != nil ) - using ctx - clear_draw_list( & draw_list ) - draw_layer.vertices_offset = 0 - draw_layer.indices_offset = 0 - draw_layer.calls_offset = 0 + clear_draw_list( & ctx.draw_list ) + ctx.draw_layer.vertices_offset = 0 + ctx.draw_layer.indices_offset = 0 + ctx.draw_layer.calls_offset = 0 } -flush_draw_list_layer :: proc( ctx : ^Context ) { +flush_draw_list_layer :: #force_inline proc( ctx : ^Context ) { assert( ctx != nil ) - using ctx - draw_layer.vertices_offset = len(draw_list.vertices) - draw_layer.indices_offset = len(draw_list.indices) - draw_layer.calls_offset = len(draw_list.calls) + ctx.draw_layer.vertices_offset = len(ctx.draw_list.vertices) + ctx.draw_layer.indices_offset = len(ctx.draw_list.indices) + ctx.draw_layer.calls_offset = len(ctx.draw_list.calls) } -//#endregion("drawing") +//#endregion("draw_list generation") //#region("metrics") -measure_text_size :: proc( ctx : ^Context, font : Font_ID, text_utf8 : string ) -> (measured : Vec2) +// The metrics follow the convention for providing their values unscaled from ctx.px_scalar +// Where its assumed when utilizing the draw_list generators or shaping procedures that the shape will be affected by it so it must be handled. +// If px_scalar is 1.0 no effect is done and its just redundant ops. + +measure_shape_size :: #force_inline proc( ctx : Context, shape : Shaped_Text ) -> (measured : Vec2) { + measured = shape.size * (1 / ctx.px_scalar) + return +} + +// Don't use this if you already have the shape instead use measure_shape_size +measure_text_size :: #force_inline proc( ctx : ^Context, font : Font_ID, px_size : f32, text_utf8 : string, + shaper_proc : $Shaper_Shape_Text_Uncached_Proc = shaper_shape_harfbuzz +) -> (measured : Vec2) { // profile(#procedure) assert( ctx != nil ) assert( font >= 0 && int(font) < len(ctx.entries) ) - entry := &ctx.entries[font] - shaped := shape_text_cached(ctx, font, text_utf8, entry) - return shaped.size + entry := ctx.entries[font] + + target_scale := 1 / ctx.px_scalar + target_px_size := px_size * ctx.px_scalar + target_font_scale := parser_scale( entry.parser_info, target_px_size ) + + shaped := shaper_shape_text_cached( text_utf8, + & ctx.shaper_ctx, + & ctx.shape_cache, + ctx.atlas, + vec2(ctx.glyph_buffer.size), + font, + entry, + target_px_size, + target_font_scale, + shaper_proc + ) + return shaped.size * target_scale } -get_font_vertical_metrics :: #force_inline proc ( ctx : ^Context, font : Font_ID ) -> ( ascent, descent, line_gap : f32 ) +get_font_vertical_metrics :: #force_inline proc ( ctx : Context, font : Font_ID, px_size : f32 ) -> ( ascent, descent, line_gap : f32 ) { - assert( ctx != nil ) assert( font >= 0 && int(font) < len(ctx.entries) ) - entry := & ctx.entries[ font ] - ascent_i32, descent_i32, line_gap_i32 := parser_get_font_vertical_metrics( & entry.parser_info ) + entry := ctx.entries[ font ] + + font_scale := parser_scale( entry.parser_info, px_size ) - ascent = (f32(ascent_i32) * entry.size_scale) - descent = (f32(descent_i32) * entry.size_scale) - line_gap = (f32(line_gap_i32) * entry.size_scale) + ascent = font_scale * entry.ascent + descent = font_scale * entry.descent + line_gap = font_scale * entry.line_gap return } //#endregion("metrics") -// Can be used with hot-reload -clear_atlas_region_caches :: proc(ctx : ^Context) +//#region("miscellaneous") + +get_font_entry :: #force_inline proc "contextless" ( ctx : ^Context, font : Font_ID ) -> Entry { + return ctx.entries[font] +} + +get_cursor_pos :: #force_inline proc "contextless" ( ctx : Context ) -> Vec2 { return ctx.cursor_pos } + +// Will normalize the value of the position and scale based on the provided view. +// Position will also be snapped to the nearest pixel via ceil. +// (Does nothing if view is 1 or 0) +get_normalized_position_scale :: #force_inline proc "contextless" ( position, scale, view : Vec2 ) -> (position_norm, scale_norm : Vec2) { - lru_clear(& ctx.atlas.region_a.state) - lru_clear(& ctx.atlas.region_b.state) - lru_clear(& ctx.atlas.region_c.state) - lru_clear(& ctx.atlas.region_d.state) + should_snap := cast(f32) i32(view.x > 0 && view.y > 0) + view_quotient := 1 / Vec2 { max(view.x, 1), max(view.y, 1) } - ctx.atlas.region_a.next_idx = 0 - ctx.atlas.region_b.next_idx = 0 - ctx.atlas.region_c.next_idx = 0 - ctx.atlas.region_d.next_idx = 0 + position_snapped := ceil(position) * view_quotient * should_snap + position_norm = position * view_quotient + + position_norm = max(position_snapped, position_norm) + scale_norm = scale * view_quotient + return } -// Can be used with hot-reload -clear_shape_cache :: proc (ctx : ^Context) +// Used to constrain the px_size used in draw calls. +resolve_draw_px_size :: #force_inline proc "contextless" ( px_size, interval, min, max : f32 ) -> (resolved_size : f32) { + interval_quotient := 1.0 / f32(interval) + interval_size := round(px_size * interval_quotient) * interval + resolved_size = clamp( interval_size, min, max ) + return +} + +// Provides a way to get a "zoom" on the font size and scale, similar conceptually to a canvas UX zoom +// Does nothing when zoom is 1.0 +resolve_zoom_size_scale :: #force_inline proc "contextless" ( + zoom, px_size : f32, scale : Vec2, interval, min, max : f32, clamp_scale : Vec2 +) -> (resolved_size : f32, zoom_scale : Vec2) { - using ctx - lru_clear(& shape_cache.state) - for idx : i32 = 0; idx < cast(i32) cap(shape_cache.storage); idx += 1 - { - stroage_entry := & shape_cache.storage[idx] - using stroage_entry - end_cursor_pos = {} - size = {} - clear(& glyphs) - clear(& positions) - clear(& draw_list.calls) - clear(& draw_list.indices) - clear(& draw_list.vertices) - } - ctx.shape_cache.next_cache_id = 0 + zoom_px_size := px_size * zoom + resolved_size = resolve_draw_px_size( zoom_px_size, interval, min, max ) + zoom_diff_scalar := 1 + (zoom_px_size - resolved_size) * (1 / resolved_size) + zoom_scale = zoom_diff_scalar * scale + zoom_scale.x = clamp(zoom_scale.x, 0, clamp_scale.x) + zoom_scale.y = clamp(zoom_scale.y, 0, clamp_scale.y) + return +} + +resolve_px_scalar_size :: #force_inline proc "contextless" ( parser_info : Parser_Font_Info, px_size, px_scalar : f32, norm_scale : Vec2 +) -> (target_px_size, target_font_scale : f32, target_scale : Vec2 ) +{ + target_px_size = px_size * px_scalar + target_scale = norm_scale * (1 / px_scalar) + target_font_scale = parser_scale( parser_info, target_px_size ) + return +} + +snap_normalized_position_to_view :: #force_inline proc "contextless" ( position, view : Vec2 ) -> (position_snapped : Vec2) +{ + should_snap := cast(f32) i32(view.x > 0 && view.y > 0) + view_quotient := 1 / Vec2 { max(view.x, 1), max(view.y, 1) } + + position_snapped = ceil(position * view) * view_quotient * should_snap + position_snapped = max(position, position_snapped) + return +} + +set_alpha_scalar :: #force_inline proc( ctx : ^Context, scalar : f32 ) { assert(ctx != nil); ctx.alpha_sharpen = scalar } +set_px_scalar :: #force_inline proc( ctx : ^Context, scalar : f32 ) { assert(ctx != nil); ctx.px_scalar = scalar } +set_zoom_px_interval :: #force_inline proc( ctx : ^Context, interval : i32 ) { assert(ctx != nil); ctx.zoom_px_interval = f32(interval) } + +// During a shaping pass on text, will snap each glyph's position via ceil. +set_snap_glyph_shape_position :: #force_inline proc( ctx : ^Context, should_snap : b32 ) { + assert(ctx != nil) + ctx.shaper_ctx.snap_glyph_position = should_snap } + +// During to_cache pass within batch_generate_glyphs_draw_list, will snap the quad's size using ceil. +set_snap_glyph_render_height :: #force_inline proc( ctx : ^Context, should_snap : b32 ) { + assert(ctx != nil) + ctx.glyph_buffer.snap_glyph_height = cast(f32) i32(should_snap) +} + +//#endregion("misc") + +//#region("scope stack") + +/* Scope stacking ease of use interface. + +View: Extents in 2D for the relative space the the text is being drawn within. +Used with snap_to_view_extent to enforce position snapping. + +Position: Used with a draw procedure that uses relative positioning will offset the incoming position by the given amount. +Scale : Used with a draw procedure that uses relative scaling, will scale the procedures incoming scale by the given amount. +Zoom : Used with a draw procedure that uses scaling via zoom, will scale the procedure's incoming font size & scale based on an 'canvas' camera's notion of it. +*/ + +@(deferred_in = auto_pop_font) +scope_font :: #force_inline proc( ctx : ^Context, font : Font_ID ) { assert(ctx != nil); append(& ctx.stack.font, font ) } +push_font :: #force_inline proc( ctx : ^Context, font : Font_ID ) { assert(ctx != nil); append(& ctx.stack.font, font ) } +pop_font :: #force_inline proc( ctx : ^Context ) { assert(ctx != nil); pop(& ctx.stack.font) } +auto_pop_font :: #force_inline proc( ctx : ^Context, font : Font_ID ) { assert(ctx != nil); pop(& ctx.stack.font) } + +@(deferred_in = auto_pop_font_size) +scope_font_size :: #force_inline proc( ctx : ^Context, px_size : f32 ) { assert(ctx != nil); append(& ctx.stack.font_size, px_size) } +push_font_size :: #force_inline proc( ctx : ^Context, px_size : f32 ) { assert(ctx != nil); append(& ctx.stack.font_size, px_size) } +pop_font_size :: #force_inline proc( ctx : ^Context ) { assert(ctx != nil); pop(& ctx.stack.font_size) } +auto_pop_font_size :: #force_inline proc( ctx : ^Context, px_size : f32 ) { assert(ctx != nil); pop(& ctx.stack.font_size) } + +@(deferred_in = auto_pop_colour ) +scope_colour :: #force_inline proc( ctx : ^Context, colour : RGBAN ) { assert(ctx != nil); append(& ctx.stack.colour, colour) } +push_colour :: #force_inline proc( ctx : ^Context, colour : RGBAN ) { assert(ctx != nil); append(& ctx.stack.colour, colour) } +pop_colour :: #force_inline proc( ctx : ^Context ) { assert(ctx != nil); pop(& ctx.stack.colour) } +auto_pop_colour :: #force_inline proc( ctx : ^Context, colour : RGBAN ) { assert(ctx != nil); pop(& ctx.stack.colour) } + +@(deferred_in = auto_pop_view) +scope_view :: #force_inline proc( ctx : ^Context, view : Vec2 ) { assert(ctx != nil); append(& ctx.stack.view, view) } +push_view :: #force_inline proc( ctx : ^Context, view : Vec2 ) { assert(ctx != nil); append(& ctx.stack.view, view) } +pop_view :: #force_inline proc( ctx : ^Context ) { assert(ctx != nil); pop(& ctx.stack.view) } +auto_pop_view :: #force_inline proc( ctx : ^Context, view : Vec2 ) { assert(ctx != nil); pop(& ctx.stack.view) } + +@(deferred_in = auto_pop_position) +scope_position :: #force_inline proc( ctx : ^Context, position : Vec2 ) { assert(ctx != nil); append(& ctx.stack.position, position ) } +push_position :: #force_inline proc( ctx : ^Context, position : Vec2 ) { assert(ctx != nil); append(& ctx.stack.position, position ) } +pop_position :: #force_inline proc( ctx : ^Context ) { assert(ctx != nil); pop( & ctx.stack.position) } +auto_pop_position :: #force_inline proc( ctx : ^Context, view : Vec2 ) { assert(ctx != nil); pop( & ctx.stack.position) } + +@(deferred_in = auto_pop_scale) +scope_scale :: #force_inline proc( ctx : ^Context, scale : Vec2 ) { assert(ctx != nil); append(& ctx.stack.scale, scale ) } +push_scale :: #force_inline proc( ctx : ^Context, scale : Vec2 ) { assert(ctx != nil); append(& ctx.stack.scale, scale ) } +pop_scale :: #force_inline proc( ctx : ^Context, ) { assert(ctx != nil); pop(& ctx.stack.scale) } +auto_pop_scale :: #force_inline proc( ctx : ^Context, scale : Vec2 ) { assert(ctx != nil); pop(& ctx.stack.scale) } + +@(deferred_in = auto_pop_zoom ) +scope_zoom :: #force_inline proc( ctx : ^Context, zoom : f32 ) { append(& ctx.stack.zoom, zoom ) } +push_zoom :: #force_inline proc( ctx : ^Context, zoom : f32 ) { append(& ctx.stack.zoom, zoom) } +pop_zoom :: #force_inline proc( ctx : ^Context ) { pop(& ctx.stack.zoom) } +auto_pop_zoom :: #force_inline proc( ctx : ^Context, zoom : f32 ) { pop(& ctx.stack.zoom) } + +@(deferred_in = auto_pop_vpz) +scope_vpz :: #force_inline proc( ctx : ^Context, camera : VPZ_Transform ) { + assert(ctx != nil) + append(& ctx.stack.view, camera.view ) + append(& ctx.stack.position, camera.position ) + append(& ctx.stack.zoom, camera.zoom ) +} +push_vpz :: #force_inline proc( ctx : ^Context, camera : VPZ_Transform ) { + assert(ctx != nil) + append(& ctx.stack.view, camera.view ) + append(& ctx.stack.position, camera.position ) + append(& ctx.stack.zoom, camera.zoom ) +} +pop_vpz :: #force_inline proc( ctx : ^Context ) { + assert(ctx != nil) + pop(& ctx.stack.view ) + pop(& ctx.stack.position) + pop(& ctx.stack.zoom ) +} +auto_pop_vpz :: #force_inline proc( ctx : ^Context, camera : VPZ_Transform ) { + assert(ctx != nil) + pop(& ctx.stack.view ) + pop(& ctx.stack.position) + pop(& ctx.stack.zoom ) +} + +//#endregion("scope stack")