Merge branch 'flow/m3te/P4.3' into m3te-plan
This commit is contained in:
173
board_view.sx
Normal file
173
board_view.sx
Normal file
@@ -0,0 +1,173 @@
|
||||
// BoardView (P4.3) — render the seeded match-3 board with real gem sprites.
|
||||
//
|
||||
// Modeled on game/chess/board_view.sx: a `View` that lays out an 8×8 grid and
|
||||
// draws tiles/sprites through RenderContext.add_image / add_image_uv, sampling
|
||||
// the gem sprite sheet by UV column. The background image fills the whole view;
|
||||
// the grid is a centered square inside the safe-area inset.
|
||||
#import "modules/std.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/opengl.sx";
|
||||
#import "modules/stb.sx";
|
||||
#import "modules/gpu/types.sx";
|
||||
#import "modules/gpu/api.sx";
|
||||
#import "modules/ui/types.sx";
|
||||
#import "modules/ui/render.sx";
|
||||
#import "modules/ui/events.sx";
|
||||
#import "modules/ui/view.sx";
|
||||
#import "board.sx";
|
||||
|
||||
// Fraction of a cell each gem occupies; the remainder is margin so a gem sits
|
||||
// inside its cell tile rather than touching the tile's edges.
|
||||
GEM_FILL_FRAC :f32: 0.84;
|
||||
|
||||
// UV sub-rect of one gem column, spanning the sheet's full height.
|
||||
GemUV :: struct {
|
||||
uv_min: Point;
|
||||
uv_max: Point;
|
||||
}
|
||||
|
||||
// Loads and holds the three board textures (background, cell tile, gem sheet)
|
||||
// and maps a gem index to its column UV. Modeled on chess's ChessPieces.
|
||||
BoardAssets :: struct {
|
||||
bg_tex: u32;
|
||||
cell_tex: u32;
|
||||
gems_tex: u32;
|
||||
cell_u: f32;
|
||||
loaded: bool;
|
||||
|
||||
init :: (self: *BoardAssets) {
|
||||
self.bg_tex = 0;
|
||||
self.cell_tex = 0;
|
||||
self.gems_tex = 0;
|
||||
// gems.png is GEM_COUNT columns wide and one row tall, so a gem's UV
|
||||
// column IS its gem index (0=red … 5=purple); cell_u is one column wide.
|
||||
self.cell_u = 1.0 / cast(f32) GEM_COUNT;
|
||||
self.loaded = false;
|
||||
}
|
||||
|
||||
load :: (self: *BoardAssets, gpu: ?GPU) {
|
||||
self.bg_tex = load_texture("assets/board/background.png", gpu);
|
||||
self.cell_tex = load_texture("assets/board/cell.png", gpu);
|
||||
self.gems_tex = load_texture("assets/gems/gems.png", gpu);
|
||||
self.loaded = self.bg_tex != 0 and self.cell_tex != 0 and self.gems_tex != 0;
|
||||
}
|
||||
|
||||
gem_uv :: (self: *BoardAssets, index: s64) -> GemUV {
|
||||
u0 : f32 = xx index * self.cell_u;
|
||||
GemUV.{
|
||||
uv_min = Point.{ x = u0, y = 0.0 },
|
||||
uv_max = Point.{ x = u0 + self.cell_u, y = 1.0 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Decode an RGBA image and upload it as a texture, returning the handle (0 on
|
||||
// failure). When a GPU backend is bound (iOS Metal) it owns the upload; the
|
||||
// desktop GL path falls back to a plain GL_TEXTURE_2D.
|
||||
load_texture :: (path: [:0]u8, gpu: ?GPU) -> u32 {
|
||||
w : s32 = 0;
|
||||
h : s32 = 0;
|
||||
ch : s32 = 0;
|
||||
pixels := stbi_load(path, @w, @h, @ch, 4);
|
||||
if pixels == null {
|
||||
out("WARNING: could not load texture: ");
|
||||
out(path);
|
||||
out("\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
tex : u32 = 0;
|
||||
if gpu != null {
|
||||
tex = gpu.create_texture(w, h, .rgba8, xx pixels);
|
||||
} else {
|
||||
glGenTextures(1, @tex);
|
||||
glBindTexture(GL_TEXTURE_2D, tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, xx GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, xx GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, xx GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, xx GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, xx GL_CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
stbi_image_free(pixels);
|
||||
return tex;
|
||||
}
|
||||
|
||||
BoardView :: struct {
|
||||
board: *Board;
|
||||
assets: *BoardAssets;
|
||||
safe: EdgeInsets;
|
||||
|
||||
cell_size: f32;
|
||||
origin: Point;
|
||||
|
||||
// Center a square 8×8 grid inside the safe-area-inset region of `frame`.
|
||||
compute_layout :: (self: *BoardView, frame: Frame) {
|
||||
avail := frame.inset(self.safe);
|
||||
cols : f32 = xx BOARD_COLS;
|
||||
board_dim := min(avail.size.width, avail.size.height);
|
||||
self.cell_size = board_dim / cols;
|
||||
total := self.cell_size * cols;
|
||||
self.origin = Point.{
|
||||
x = avail.origin.x + (avail.size.width - total) * 0.5,
|
||||
y = avail.origin.y + (avail.size.height - total) * 0.5
|
||||
};
|
||||
}
|
||||
|
||||
cell_frame :: (self: *BoardView, col: s64, row: s64) -> Frame {
|
||||
Frame.make(
|
||||
self.origin.x + xx col * self.cell_size,
|
||||
self.origin.y + xx row * self.cell_size,
|
||||
self.cell_size,
|
||||
self.cell_size
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl View for BoardView {
|
||||
size_that_fits :: (self: *BoardView, proposal: ProposedSize) -> Size {
|
||||
Size.{ width = proposal.width ?? 0.0, height = proposal.height ?? 0.0 }
|
||||
}
|
||||
|
||||
layout :: (self: *BoardView, bounds: Frame) {
|
||||
self.compute_layout(bounds);
|
||||
}
|
||||
|
||||
render :: (self: *BoardView, ctx: *RenderContext, frame: Frame) {
|
||||
self.compute_layout(frame);
|
||||
|
||||
// 1. Background image fills the whole view, behind the grid.
|
||||
if self.assets.bg_tex != 0 {
|
||||
ctx.add_image(frame, self.assets.bg_tex);
|
||||
}
|
||||
|
||||
// 2. One cell tile per board cell, then its gem sampled by index column.
|
||||
gem_inset := self.cell_size * (1.0 - GEM_FILL_FRAC) * 0.5;
|
||||
gem_dim := self.cell_size * GEM_FILL_FRAC;
|
||||
for 0..BOARD_ROWS: (row) {
|
||||
for 0..BOARD_COLS: (col) {
|
||||
cf := self.cell_frame(col, row);
|
||||
|
||||
if self.assets.cell_tex != 0 {
|
||||
ctx.add_image(cf, self.assets.cell_tex);
|
||||
}
|
||||
|
||||
g := self.board.at(col, row);
|
||||
if g != .empty and self.assets.gems_tex != 0 {
|
||||
uv := self.assets.gem_uv(cast(s64) g);
|
||||
gf := Frame.make(
|
||||
cf.origin.x + gem_inset,
|
||||
cf.origin.y + gem_inset,
|
||||
gem_dim,
|
||||
gem_dim
|
||||
);
|
||||
ctx.add_image_uv(gf, self.assets.gems_tex, uv.uv_min, uv.uv_max);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handle_event :: (self: *BoardView, event: *Event, frame: Frame) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
BIN
goldens/p4_board.png
Normal file
BIN
goldens/p4_board.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 MiB |
171
main.sx
171
main.sx
@@ -3,113 +3,60 @@
|
||||
#import "modules/compiler.sx";
|
||||
#import "modules/opengl.sx";
|
||||
#import "modules/sdl3.sx";
|
||||
#import "modules/math";
|
||||
#import "modules/stb.sx";
|
||||
#import "modules/stb_truetype.sx";
|
||||
#import "modules/gpu/api.sx";
|
||||
#import "modules/gpu/types.sx";
|
||||
#import "modules/gpu/metal.sx";
|
||||
#import "modules/ui";
|
||||
#import "modules/platform/api.sx";
|
||||
#import "modules/platform/sdl3.sx";
|
||||
#import "modules/platform/uikit.sx";
|
||||
#import "board.sx";
|
||||
#import "board_view.sx";
|
||||
|
||||
#run configure_build();
|
||||
|
||||
// Fixed seed for the rendered board — the same seed tests/board_init.sx locks
|
||||
// as a snapshot, so the on-screen layout matches that golden gem-for-gem.
|
||||
BOARD_SEED :: 1337;
|
||||
|
||||
g_plat : Platform = ---;
|
||||
g_pipeline : *UIPipeline = ---;
|
||||
g_viewport_w : f32 = 800.0;
|
||||
g_viewport_h : f32 = 600.0;
|
||||
g_safe_insets : EdgeInsets = .{};
|
||||
|
||||
// iOS-only concrete handles kept alongside the boxed `g_plat` so the frame
|
||||
// loop can reach the CAMetalLayer pointer / pixel dims without going through
|
||||
// the protocol box.
|
||||
// iOS-only concrete handles kept alongside the boxed `g_plat` so the frame loop
|
||||
// can reach the CAMetalLayer pointer / pixel dims without going through the
|
||||
// protocol box.
|
||||
g_uikit_plat : *UIKitPlatform = null;
|
||||
g_metal_gpu : *MetalGPU = null;
|
||||
|
||||
// ── Render-path proof (P0.2) ─────────────────────────────────────────────
|
||||
// Clear to a solid blue, then draw one centered orange quad covering the
|
||||
// central 50%×50% of the drawable. Geometry is in NDC ([-0.5, 0.5]²) so the
|
||||
// quad stays screen-size independent across simulator devices, which keeps
|
||||
// the screenshot golden unambiguous. This is the GPU protocol's
|
||||
// clear+quad path: an MSL pipeline state plus a 6-vertex (2-triangle)
|
||||
// buffer, created lazily once the MTLDevice exists.
|
||||
g_quad_shader : ShaderHandle = 0;
|
||||
g_quad_vbuf : BufferHandle = 0;
|
||||
// The pure-sx model (board.sx) and its sprites, seeded once in main() and
|
||||
// rendered every frame. Heap-allocated so the view holds stable pointers to
|
||||
// the mutable state across frames.
|
||||
g_board : *Board = null;
|
||||
g_assets : *BoardAssets = null;
|
||||
|
||||
// ── Input-path proof (P0.3) ──────────────────────────────────────────────
|
||||
// A tap toggles the quad between two distinct colors, proving a real touch
|
||||
// reaches sx and changes the rendered frame. UIKit touchesBegan is mapped to
|
||||
// a `mouse_down` Event in uikit.sx; the frame loop's poll flips
|
||||
// `g_quad_flipped` and marks `g_quad_dirty`, which re-uploads the matching
|
||||
// vertex colors before the next draw.
|
||||
g_quad_flipped : bool = false;
|
||||
g_quad_dirty : bool = true;
|
||||
|
||||
BG_CLEAR :: ClearColor.{ r = 0.10, g = 0.20, b = 0.55, a = 1.0 };
|
||||
|
||||
// Vertex layout matches QUAD_MSL's `Vertex`: packed_float2 pos +
|
||||
// packed_float4 color = 24 bytes. `packed_*` avoids the 16-byte alignment
|
||||
// padding a plain `float4` would force. 6 vertices = 2 triangles. Two
|
||||
// arrays share the same geometry and differ only in color so the buffer
|
||||
// re-upload is a flat memcpy of the active palette.
|
||||
QUAD_VERTS_ORANGE : [36]f32 = .[
|
||||
-0.5, 0.5, 1.0, 0.6, 0.0, 1.0,
|
||||
0.5, 0.5, 1.0, 0.6, 0.0, 1.0,
|
||||
-0.5, -0.5, 1.0, 0.6, 0.0, 1.0,
|
||||
0.5, 0.5, 1.0, 0.6, 0.0, 1.0,
|
||||
0.5, -0.5, 1.0, 0.6, 0.0, 1.0,
|
||||
-0.5, -0.5, 1.0, 0.6, 0.0, 1.0,
|
||||
];
|
||||
|
||||
QUAD_VERTS_GREEN : [36]f32 = .[
|
||||
-0.5, 0.5, 0.15, 0.85, 0.35, 1.0,
|
||||
0.5, 0.5, 0.15, 0.85, 0.35, 1.0,
|
||||
-0.5, -0.5, 0.15, 0.85, 0.35, 1.0,
|
||||
0.5, 0.5, 0.15, 0.85, 0.35, 1.0,
|
||||
0.5, -0.5, 0.15, 0.85, 0.35, 1.0,
|
||||
-0.5, -0.5, 0.15, 0.85, 0.35, 1.0,
|
||||
];
|
||||
|
||||
// Pass-through shader: the vertex stage emits NDC positions directly (no
|
||||
// projection), the fragment stage returns the interpolated vertex color.
|
||||
// Entry-point names vmain / fmain are what MetalGPU.create_shader looks up.
|
||||
QUAD_MSL :: #string MSL
|
||||
#include <metal_stdlib>
|
||||
using namespace metal;
|
||||
|
||||
struct Vertex {
|
||||
packed_float2 pos;
|
||||
packed_float4 color;
|
||||
};
|
||||
|
||||
struct RasterizerData {
|
||||
float4 position [[position]];
|
||||
float4 color;
|
||||
};
|
||||
|
||||
vertex RasterizerData vmain(uint vid [[vertex_id]],
|
||||
constant Vertex* vertices [[buffer(0)]]) {
|
||||
RasterizerData out;
|
||||
out.position = float4(vertices[vid].pos, 0.0, 1.0);
|
||||
out.color = float4(vertices[vid].color);
|
||||
return out;
|
||||
// Rebuilt each frame inside the pipeline's arena; carries the current safe-area
|
||||
// insets so the grid stays inside the notch / home-indicator region.
|
||||
build_ui :: () -> View {
|
||||
BoardView.{ board = g_board, assets = g_assets, safe = g_safe_insets }
|
||||
}
|
||||
|
||||
fragment float4 fmain(RasterizerData in [[stage_in]]) {
|
||||
return in.color;
|
||||
}
|
||||
MSL;
|
||||
|
||||
frame :: () {
|
||||
fc := g_plat.begin_frame();
|
||||
g_viewport_w = fc.viewport_w;
|
||||
g_viewport_h = fc.viewport_h;
|
||||
g_safe_insets = g_plat.safe_insets();
|
||||
|
||||
if fc.viewport_w != g_pipeline.screen_width or fc.viewport_h != g_pipeline.screen_height {
|
||||
g_pipeline.resize(fc.viewport_w, fc.viewport_h);
|
||||
}
|
||||
|
||||
for g_plat.poll_events(): (*ev) {
|
||||
if ev == {
|
||||
// Flip on the press only. A tap also produces mouse_up
|
||||
// (touchesEnded); toggling on both would net to no change.
|
||||
case .mouse_down: {
|
||||
g_quad_flipped = !g_quad_flipped;
|
||||
g_quad_dirty = true;
|
||||
}
|
||||
}
|
||||
inline if OS != .ios {
|
||||
if ev == {
|
||||
case .key_up: (e) {
|
||||
@@ -117,13 +64,14 @@ frame :: () {
|
||||
}
|
||||
}
|
||||
}
|
||||
g_pipeline.dispatch_event(ev);
|
||||
}
|
||||
|
||||
inline if OS == .ios {
|
||||
// Lazy-attach Metal once -[SxAppDelegate didFinishLaunching:] has
|
||||
// installed the SxMetalView and its bounds have been measured; both
|
||||
// can lag the first CADisplayLink tick, and a zero-sized drawable
|
||||
// aborts via XPC.
|
||||
// installed the SxMetalView and its bounds have been measured; both can
|
||||
// lag the first CADisplayLink tick, and a zero-sized drawable aborts
|
||||
// via XPC.
|
||||
if g_uikit_plat.gl_layer == null { return; }
|
||||
if g_uikit_plat.pixel_w <= 0 or g_uikit_plat.pixel_h <= 0 { return; }
|
||||
if g_metal_gpu.layer == null {
|
||||
@@ -131,31 +79,15 @@ frame :: () {
|
||||
} else if g_metal_gpu.pixel_w != g_uikit_plat.pixel_w or g_metal_gpu.pixel_h != g_uikit_plat.pixel_h {
|
||||
g_metal_gpu.resize(g_uikit_plat.pixel_w, g_uikit_plat.pixel_h);
|
||||
}
|
||||
// Compile the quad pipeline + upload its vertices once. The MTLDevice
|
||||
// was created eagerly in main(), so both only need a valid device.
|
||||
if g_quad_shader == 0 {
|
||||
g_quad_shader = g_metal_gpu.create_shader(QUAD_MSL, "");
|
||||
if g_quad_shader == 0 { return; }
|
||||
}
|
||||
if g_quad_vbuf == 0 {
|
||||
g_quad_vbuf = g_metal_gpu.create_buffer(size_of([36]f32));
|
||||
if g_quad_vbuf == 0 { return; }
|
||||
}
|
||||
if g_quad_dirty {
|
||||
verts := if g_quad_flipped then @QUAD_VERTS_GREEN else @QUAD_VERTS_ORANGE;
|
||||
g_metal_gpu.update_buffer(g_quad_vbuf, xx verts, size_of([36]f32));
|
||||
g_quad_dirty = false;
|
||||
}
|
||||
|
||||
if !g_metal_gpu.begin_frame(BG_CLEAR) { return; }
|
||||
g_metal_gpu.set_shader(g_quad_shader);
|
||||
g_metal_gpu.set_vertex_buffer(g_quad_vbuf);
|
||||
g_metal_gpu.draw_triangles(0, 6);
|
||||
clear : ClearColor = .{ r = 0.05, g = 0.06, b = 0.10, a = 1.0 };
|
||||
if !g_metal_gpu.begin_frame(clear) { return; }
|
||||
g_pipeline.tick();
|
||||
g_metal_gpu.end_frame(fc.target_present_time);
|
||||
} else {
|
||||
glViewport(0, 0, fc.pixel_w, fc.pixel_h);
|
||||
glClearColor(0.10, 0.20, 0.55, 1.0);
|
||||
glClearColor(0.05, 0.06, 0.10, 1.0);
|
||||
glClear(GL_COLOR_BUFFER_BIT);
|
||||
g_pipeline.tick();
|
||||
}
|
||||
g_plat.end_frame();
|
||||
}
|
||||
@@ -168,9 +100,10 @@ main :: () -> void {
|
||||
g_plat = xx u;
|
||||
g_uikit_plat = u;
|
||||
|
||||
// The CAMetalLayer doesn't exist until didFinishLaunching: runs after
|
||||
// we return into UIApplicationMain, so attach lazily on the first
|
||||
// frame. init(null, 0, 0) only needs the MTLDevice.
|
||||
// The CAMetalLayer doesn't exist until didFinishLaunching: runs after we
|
||||
// return into UIApplicationMain, so attach lazily on the first frame.
|
||||
// init(null, 0, 0) only needs the MTLDevice, which is enough for the
|
||||
// texture uploads below.
|
||||
g_metal_gpu = xx context.allocator.alloc(size_of(MetalGPU));
|
||||
// alloc returns uninitialized memory; struct field defaults are NOT
|
||||
// applied, so List caps/lens would be garbage without this memset.
|
||||
@@ -185,6 +118,26 @@ main :: () -> void {
|
||||
fc := g_plat.begin_frame();
|
||||
g_viewport_w = fc.viewport_w;
|
||||
g_viewport_h = fc.viewport_h;
|
||||
g_safe_insets = g_plat.safe_insets();
|
||||
|
||||
g_pipeline = xx context.allocator.alloc(size_of(UIPipeline));
|
||||
// Same alloc caveat as above: zero so the optional `gpu` reads as null on
|
||||
// the desktop path (where set_gpu is not called) and the Lists start empty.
|
||||
memset(xx g_pipeline, 0, size_of(UIPipeline));
|
||||
inline if OS == .ios {
|
||||
g_pipeline.set_gpu(xx g_metal_gpu);
|
||||
}
|
||||
g_pipeline.init(fc.viewport_w, fc.viewport_h);
|
||||
g_pipeline.init_font("assets/fonts/default.ttf", 32.0, fc.dpi_scale);
|
||||
|
||||
g_board = xx context.allocator.alloc(size_of(Board));
|
||||
g_board.init(BOARD_SEED);
|
||||
|
||||
g_assets = xx context.allocator.alloc(size_of(BoardAssets));
|
||||
g_assets.init();
|
||||
g_assets.load(g_pipeline.gpu);
|
||||
|
||||
g_pipeline.set_body(closure(build_ui));
|
||||
|
||||
g_plat.run_frame_loop(closure(frame));
|
||||
g_plat.shutdown();
|
||||
|
||||
55
vendors/file_utils/file_utils.c
vendored
Normal file
55
vendors/file_utils/file_utils.c
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __ANDROID__
|
||||
#include <android/asset_manager.h>
|
||||
|
||||
// Caller-installed AAssetManager pointer. Chess's android_main extracts
|
||||
// it from `app->activity->assetManager` (via sx-side platform module's
|
||||
// `g_android_asset_manager` global) and feeds it here once at startup.
|
||||
// Until the setter has been called, Android falls through to fopen —
|
||||
// gives a predictable "file not found" rather than a NULL-deref.
|
||||
static AAssetManager* g_aam = NULL;
|
||||
|
||||
void sx_android_set_asset_manager(void* m) {
|
||||
g_aam = (AAssetManager*)m;
|
||||
}
|
||||
#endif
|
||||
|
||||
unsigned char* read_file_bytes(const char* path, int* out_size) {
|
||||
#ifdef __ANDROID__
|
||||
if (g_aam != NULL) {
|
||||
// AAssetManager paths are relative to the APK's `assets/`
|
||||
// directory. Strip a leading "assets/" so callers can use the
|
||||
// same paths across iOS/macOS/Android (those platforms read
|
||||
// assets via `assets/...` rooted in the bundle or CWD).
|
||||
const char* lookup = path;
|
||||
if (strncmp(path, "assets/", 7) == 0) {
|
||||
lookup = path + 7;
|
||||
}
|
||||
AAsset* a = AAssetManager_open(g_aam, lookup, AASSET_MODE_BUFFER);
|
||||
if (a != NULL) {
|
||||
off_t n = AAsset_getLength(a);
|
||||
*out_size = (int)n;
|
||||
unsigned char* buf = (unsigned char*)malloc((size_t)n);
|
||||
if (buf != NULL) {
|
||||
memcpy(buf, AAsset_getBuffer(a), (size_t)n);
|
||||
}
|
||||
AAsset_close(a);
|
||||
return buf;
|
||||
}
|
||||
// Falls through to fopen — useful when assets land in the data
|
||||
// dir via extraction or app updates.
|
||||
}
|
||||
#endif
|
||||
FILE* f = fopen(path, "rb");
|
||||
if (!f) return 0;
|
||||
fseek(f, 0, SEEK_END);
|
||||
*out_size = (int)ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
unsigned char* buf = (unsigned char*)malloc(*out_size);
|
||||
fread(buf, 1, *out_size, f);
|
||||
fclose(f);
|
||||
return buf;
|
||||
}
|
||||
13
vendors/file_utils/file_utils.h
vendored
Normal file
13
vendors/file_utils/file_utils.h
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef FILE_UTILS_H
|
||||
#define FILE_UTILS_H
|
||||
|
||||
unsigned char* read_file_bytes(const char* path, int* out_size);
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// Install the AAssetManager that `read_file_bytes` consults for paths
|
||||
// rooted inside the APK. Caller is responsible for passing the manager
|
||||
// from `ANativeActivity->assetManager` before any read_file_bytes call.
|
||||
void sx_android_set_asset_manager(void* m);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
19
vendors/kb_text_shape/kb/LICENSE
vendored
Normal file
19
vendors/kb_text_shape/kb/LICENSE
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
zlib License
|
||||
|
||||
(C) Copyright 2024-2025 Jimmy Lefevre
|
||||
|
||||
This software is provided 'as-is', without any express or implied
|
||||
warranty. In no event will the authors be held liable for any damages
|
||||
arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it
|
||||
freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not
|
||||
claim that you wrote the original software. If you use this software
|
||||
in a product, an acknowledgment in the product documentation would be
|
||||
appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be
|
||||
misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
30737
vendors/kb_text_shape/kb/kb_text_shape.h
vendored
Normal file
30737
vendors/kb_text_shape/kb/kb_text_shape.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
vendors/kb_text_shape/kb_text_shape_impl.c
vendored
Normal file
2
vendors/kb_text_shape/kb_text_shape_impl.c
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#define KB_TEXT_SHAPE_IMPLEMENTATION
|
||||
#include "kb/kb_text_shape.h"
|
||||
15
vendors/kb_text_shape/kbts_api.h
vendored
Normal file
15
vendors/kb_text_shape/kbts_api.h
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
// Minimal API declarations for SX import.
|
||||
// Only the functions/types we actually use — avoids parsing the full 30k-line header.
|
||||
|
||||
typedef struct kbts_shape_context kbts_shape_context;
|
||||
typedef struct kbts_font kbts_font;
|
||||
|
||||
kbts_shape_context *kbts_CreateShapeContext(void *Allocator, void *AllocatorData);
|
||||
void kbts_DestroyShapeContext(kbts_shape_context *Context);
|
||||
kbts_font *kbts_ShapePushFontFromMemory(kbts_shape_context *Context, void *Memory, int Size, int FontIndex);
|
||||
void kbts_GetFontInfo2(kbts_font *Font, void *Info);
|
||||
void kbts_ShapeBegin(kbts_shape_context *Context, unsigned int ParagraphDirection, unsigned int Language);
|
||||
void kbts_ShapeUtf8(kbts_shape_context *Context, const char *Utf8, int Length, unsigned int UserIdGenerationMode);
|
||||
void kbts_ShapeEnd(kbts_shape_context *Context);
|
||||
int kbts_ShapeRun(kbts_shape_context *Context, void *Run);
|
||||
int kbts_GlyphIteratorNext(void *It, void **Glyph);
|
||||
7988
vendors/stb_image/stb_image.h
vendored
Normal file
7988
vendors/stb_image/stb_image.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
vendors/stb_image/stb_image_impl.c
vendored
Normal file
2
vendors/stb_image/stb_image_impl.c
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
1724
vendors/stb_image/stb_image_write.h
vendored
Normal file
1724
vendors/stb_image/stb_image_write.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
vendors/stb_image/stb_image_write_impl.c
vendored
Normal file
2
vendors/stb_image/stb_image_write_impl.c
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||
#include "stb_image_write.h"
|
||||
5079
vendors/stb_truetype/stb_truetype.h
vendored
Normal file
5079
vendors/stb_truetype/stb_truetype.h
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
vendors/stb_truetype/stb_truetype_impl.c
vendored
Normal file
2
vendors/stb_truetype/stb_truetype_impl.c
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#include "stb_truetype.h"
|
||||
Reference in New Issue
Block a user