From 997bfac1939c2d0de8b5fe942789098b3b492b6b Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Wed, 5 Aug 2026 18:50:04 -0500 Subject: [PATCH] sdf string drawing --- font.js | 75 ++++++++++++++++++++++------ font.wgsl | 36 +++++++++++--- fontSliders.js | 12 +++++ include/font.h | 19 +++++-- index.js | 82 ++----------------------------- slider.js | 77 +++++++++++++++++++++++++++++ src/font_layout/api.cpp | 2 +- src/font_layout/font_layout.cpp | 31 +++++++++--- src/font_layout/font_layout.h | 6 ++- tools/ttf_outline/ttf_outline.cpp | 47 +++++++++++++----- 10 files changed, 261 insertions(+), 126 deletions(-) create mode 100644 fontSliders.js create mode 100644 slider.js diff --git a/font.js b/font.js index caaa202..4696c58 100644 --- a/font.js +++ b/font.js @@ -1,4 +1,6 @@ import { getPath } from "./common.js"; +import { makeSliders } from "./fontSliders.js"; +import { updateSliders } from "./slider.js"; class FontRenderer { createBuffers(device) @@ -9,14 +11,14 @@ class FontRenderer { // glyph ////////////////////////////////////////////////////////////////////// - const glyphBufferSize = this.module.instance.exports.font_layout__glyph_buffer_size(this.fontLayoutAddress); + const glyphBufferSize = this.exports.font_layout__glyph_buffer_size(this.fontLayoutAddress); const glyphBuffer = device.createBuffer({ label: `glyph buffer`, size: glyphBufferSize, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, }); - const glyphBufferAddress = this.module.instance.exports.font_layout__glyph_buffer_address(this.fontLayoutAddress); + const glyphBufferAddress = this.exports.font_layout__glyph_buffer_address(this.fontLayoutAddress); device.queue.writeBuffer(glyphBuffer, 0, this.module.memory.buffer, glyphBufferAddress, glyphBufferSize); @@ -24,9 +26,9 @@ class FontRenderer { // texture ////////////////////////////////////////////////////////////////////// - const textureWidth = this.module.instance.exports.font_layout__texture_width(this.fontLayoutAddress); - const textureHeight = this.module.instance.exports.font_layout__texture_height(this.fontLayoutAddress); - const textureAddress = this.module.instance.exports.font_layout__texture_address(this.fontLayoutAddress); + const textureWidth = this.exports.font_layout__texture_width(this.fontLayoutAddress); + const textureHeight = this.exports.font_layout__texture_height(this.fontLayoutAddress); + const textureAddress = this.exports.font_layout__texture_address(this.fontLayoutAddress); //console.log(textureWidth, textureHeight, textureAddress); const texture = device.createTexture({ @@ -69,10 +71,12 @@ class FontRenderer { }); ////////////////////////////////////////////////////////////////////// - // layout + // per-frame (layout, configuration) ////////////////////////////////////////////////////////////////////// - const layoutBufferSize = this.module.instance.exports.font_layout__layout_buffer_size(this.fontLayoutAddress); + this.layoutBufferAddress = this.exports.font_layout__layout_buffer_address(this.fontLayoutAddress); + + const layoutBufferSize = this.exports.font_layout__layout_buffer_size(this.fontLayoutAddress); for (let i = 0; i < 2; i++) { const layoutBuffer = device.createBuffer({ label: `layout buffer ${i}`, @@ -80,17 +84,27 @@ class FontRenderer { usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, }); + const configurationBuffer = device.createBuffer({ + label: `configuration buffer ${i}`, + size: 4 * 4, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }); + const bindGroup = device.createBindGroup({ label: `font per-frame bind group ${i}`, layout: this.bindGroupLayout.perFrame, entries: [{ binding: 0, resource: { buffer: layoutBuffer }, + }, { + binding: 1, + resource: { buffer: configurationBuffer }, }] }); this.frames.push({ layoutBuffer: layoutBuffer, + configurationBuffer: configurationBuffer, bindGroup: bindGroup, }); } @@ -101,16 +115,17 @@ class FontRenderer { const label = "font"; this.module = module; + this.exports = module.instance.exports; ////////////////////////////////////////////////////////////////////// // font layout ////////////////////////////////////////////////////////////////////// - this.fontBufferAddress = this.module.instance.exports.mem_alloc(fontBufferSrc.byteLength); + this.fontBufferAddress = this.exports.mem_alloc(fontBufferSrc.byteLength); const fontBufferDst = new Uint8Array(this.module.memory.buffer, this.fontBufferAddress, fontBufferSrc.byteLength); fontBufferDst.set(new Uint8Array(fontBufferSrc)); this.maxGlyphs = 1024; - this.fontLayoutAddress = this.module.instance.exports.font_layout__create(this.fontBufferAddress, this.maxGlyphs); + this.fontLayoutAddress = this.exports.font_layout__create(this.fontBufferAddress, this.maxGlyphs); ////////////////////////////////////////////////////////////////////// // shader module @@ -167,7 +182,7 @@ class FontRenderer { texture: {}, }, { // glyph buffer binding: 2, - visibility: GPUShaderStage.FRAGMENT, + visibility: GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT, buffer: { type: "read-only-storage" } } ] @@ -178,12 +193,11 @@ class FontRenderer { binding: 0, visibility: GPUShaderStage.VERTEX, buffer: { type: "read-only-storage" } - } - /*, { // configuration + }, { // configuration binding: 1, - visibility: GPUShaderStage.FRAGMENT, + visibility: GPUShaderStage.VERTEX, buffer: { type: "uniform" } - }*/] + }] }), }; @@ -238,6 +252,33 @@ class FontRenderer { resource: { buffer: viewUniformBuffer }, }], }); + + ////////////////////////////////////////////////////////////////////// + // sliders + ////////////////////////////////////////////////////////////////////// + + this.sliders = makeSliders(); + this.sliderArray = new Float32Array(4); + } + + update(device, frameNumber) + { + this.instanceCount = this.exports.font_layout__layout_buffer_index(this.fontLayoutAddress); + const layoutBufferSize = this.instanceCount * 4 * 4; + + device.queue.writeBuffer(this.frames[frameNumber].layoutBuffer, 0, + this.module.memory.buffer, this.layoutBufferAddress, layoutBufferSize); + + ////////////////////////////////////////////////////////////////////// + // sliders + ////////////////////////////////////////////////////////////////////// + + const configuration = updateSliders(this.sliders); + this.sliderArray[0] = configuration.scale; + this.sliderArray[1] = configuration.translateX; + this.sliderArray[2] = configuration.translateY; + device.queue.writeBuffer(this.frames[frameNumber].configurationBuffer, 0, + this.sliderArray); } render(renderPass, frameNumber) @@ -247,7 +288,8 @@ class FontRenderer { renderPass.setBindGroup(0, this.viewBindGroup); renderPass.setBindGroup(1, this.perFontBindGroup); renderPass.setBindGroup(2, this.frames[frameNumber].bindGroup); - renderPass.drawIndexed(6); + + renderPass.drawIndexed(6, this.instanceCount); } } @@ -259,6 +301,9 @@ async function loadFont(device, canvasFormat, viewUniformBuffer, module) const liberationBuffer = await liberationResponse.arrayBuffer(); const renderer = new FontRenderer(device, canvasFormat, viewUniformBuffer, fontWgsl, module, liberationBuffer); + + module.instance.exports.font_layout__draw(renderer.fontLayoutAddress); + return renderer; } diff --git a/font.wgsl b/font.wgsl index 40b4d5b..13aff17 100644 --- a/font.wgsl +++ b/font.wgsl @@ -1,4 +1,4 @@ -struct Configuration { +struct View { viewProj: mat4x4f, lightViewProj: mat4x4f, lightPosition: vec4f, @@ -6,30 +6,42 @@ struct Configuration { aspect: f32, }; +struct Config { + scale: f32, + translateX: f32, + translateY: f32, +}; + struct GlyphBuffer { - position: vec2f, + texPosition: vec2f, + texSize: vec2f, size: vec2f, + unused: vec2f, }; struct LayoutBuffer { position: vec4f, // glyph_index w }; -@group(0) @binding(0) var config: Configuration; +@group(0) @binding(0) var view: View; @group(1) @binding(0) var linearSampler: sampler; @group(1) @binding(1) var fontTexture: texture_2d; @group(1) @binding(2) var glyphBuffer: array; @group(2) @binding(0) var layoutBuffer: array; +@group(2) @binding(1) var config: Config; struct VertexInput { @builtin(vertex_index) vertex_index: u32, + @builtin(instance_index) instance_index: u32, }; struct VertexOutput { @builtin(position) position: vec4f, @location(0) texture: vec2f, + @location(1) @interpolate(flat) glyphIndex: u32, + @location(2) foo: vec4f, }; const vertices = array( @@ -44,11 +56,17 @@ fn vertexMain(input: VertexInput) -> VertexOutput { let texture = vertices[input.vertex_index]; - let position = vec2f(texture.x, 1.0 - texture.y) * 2.0 - 1.0; + let position = vec2f(texture.x, 1.0 - texture.y); + + let glyphIndex = u32(layoutBuffer[input.instance_index].position.w); + let viewPosition = position * glyphBuffer[glyphIndex].size + layoutBuffer[input.instance_index].position.xy; + let screenPosition = viewPosition * vec2f(1 / view.aspect, 1) * config.scale + vec2f(config.translateX, config.translateY); var output: VertexOutput; - output.position = vec4f(position * 0.5, 0.0, 1.0); + output.position = vec4f(screenPosition, 0.0, 1.0); output.texture = texture; + output.glyphIndex = glyphIndex; + output.foo = vec4f(layoutBuffer[glyphIndex].position.xy, 0, 0); return output; } @@ -57,12 +75,16 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f { var color = vec3f(input.texture.xy, 0); - let p = glyphBuffer[66].position; - let s = glyphBuffer[66].size; + let p = glyphBuffer[input.glyphIndex].texPosition; + let s = glyphBuffer[input.glyphIndex].texSize; let base = textureSample(fontTexture, linearSampler, input.texture * s + p); let t = f32(base.x > 0.5); + if (base.x < 0.5) { + discard; + } + return vec4f(t, t, t, 1); } diff --git a/fontSliders.js b/fontSliders.js new file mode 100644 index 0000000..66a64fb --- /dev/null +++ b/fontSliders.js @@ -0,0 +1,12 @@ +import { Slider } from "./slider.js" + +function makeSliders() +{ + return { + scale: new Slider("scale", 0.0, 0.05), + translateX: new Slider("translate x", -10, 10), + translateY: new Slider("translate y", -10, 10), + }; +} + +export { makeSliders }; diff --git a/include/font.h b/include/font.h index 0aac575..d27b372 100644 --- a/include/font.h +++ b/include/font.h @@ -9,28 +9,37 @@ extern "C" { // metrics are 26.6 fixed point struct glyph_metrics { + int32_t width; + int32_t height; int32_t horiBearingX; int32_t horiBearingY; int32_t horiAdvance; } __attribute__ ((packed)); -static_assert((sizeof (struct glyph_metrics)) == ((sizeof (int32_t)) * 3)); +static_assert((sizeof (struct glyph_metrics)) == ((sizeof (int32_t)) * 5)); -struct glyph_bitmap { +struct glyph_texture { uint16_t x; uint16_t y; uint16_t width; uint16_t height; } __attribute__ ((packed)); -static_assert((sizeof (struct glyph_bitmap)) == ((sizeof (uint16_t)) * 4)); +static_assert((sizeof (struct glyph_texture)) == ((sizeof (uint16_t)) * 4)); + +struct glyph_bitmap { + int32_t left; + int32_t top; +} __attribute__ ((packed)); +static_assert((sizeof (struct glyph_bitmap)) == ((sizeof (uint32_t)) * 2)); struct glyph { - struct glyph_bitmap bitmap; + struct glyph_texture texture; struct glyph_metrics metrics; + struct glyph_bitmap bitmap; } __attribute__ ((packed)); -static_assert((sizeof (struct glyph)) == ((sizeof (struct glyph_bitmap)) + (sizeof (struct glyph_metrics)))); +static_assert((sizeof (struct glyph)) == ((sizeof (struct glyph_texture)) + (sizeof (struct glyph_metrics)) + (sizeof (struct glyph_bitmap)))); struct font { uint32_t first_char_code; diff --git a/index.js b/index.js index 2f3516a..89939b6 100644 --- a/index.js +++ b/index.js @@ -47,6 +47,7 @@ const module = await WebAssembly.instantiateStreaming(fetch("src/module.wasm"), env: { memory: memory, log: console.log, + log4: console.log, logStrInt: console.log, } }); @@ -195,73 +196,6 @@ function updateView() viewUniformBufferSize); } -const controlMain = document.getElementById("control-main"); - -function remap(low1, high1, low2, high2, value) -{ - return low2 + (value - low1) * (high2 - low2) / (high1 - low1); -} - -const sliderDefaults = {"bone length":"32","velocity acc.":"385","velocity damp.":"796","bone count":"40"}; - -class Slider { - constructor(label, low, high, max = 1024) - { - const labelE = document.createElement("span"); - labelE.className = "label"; - labelE.innerHTML = label; - - const valueE = document.createElement("div"); - valueE.className = "value"; - - const inputE = document.createElement("input"); - inputE.className = "range"; - inputE.min = 0; - inputE.max = max; - inputE.type = "range"; - const spanE = document.createElement("span"); - spanE.className = "range-span code"; - - valueE.appendChild(inputE); - valueE.appendChild(spanE); - - controlMain.appendChild(labelE); - controlMain.appendChild(valueE); - - this.label = label; - this.input = inputE; - this.span = spanE; - this.low = low; - this.high = high; - this.lastValue = undefined; - - const storageValue = localStorage.getItem(this.label); - if (storageValue !== null) { - this.input.value = storageValue; - } else if (this.label in sliderDefaults) { - this.input.value = sliderDefaults[this.label]; - } - this.input.value = sliderDefaults[this.label]; - - this.update(); - } - - update() - { - const rawValue = parseInt(this.input.value); - const value = remap(this.input.min, this.input.max, this.low, this.high, this.input.value); - if (rawValue !== this.lastValue) { - this.lastValue = rawValue; - localStorage.setItem(this.label, rawValue); - } - if (this.high > 10) { - this.span.innerHTML = value.toFixed(0); - } else { - this.span.innerHTML = value.toFixed(5); - } - return value; - } -} /* const flatSliders = { @@ -282,21 +216,14 @@ const flatSliders = { const mousePosition = new Float32Array([0, 0, 0, 0]); +/* const snakeSliders = { boneCount: new Slider("bone count", 0.0, 100.0, 100), boneLength: new Slider("bone length", 0.0, 0.5), velocityDamping: new Slider("velocity damp.", 0.0, 1.0), velocityAcceleration: new Slider("velocity acc.", 0.0, 1.0), }; - -function updateSliders(sliders) -{ - const values = {}; - for (let [key, value] of Object.entries(sliders)) { - values[key] = value.update(); - } - return values; -} +*/ function onClick(e) { @@ -320,7 +247,7 @@ function render2() { if (canRender === true) { //const configuration = updateSliders(flatSliders); - const configuration = updateSliders(snakeSliders); + //const configuration = updateSliders(snakeSliders); canRender = false; recreateDepth(false); @@ -351,6 +278,7 @@ function render2() //snakeRenderer.update(device, frameNumber, mousePosition, configuration); //snakeRenderer.render(renderPass, frameNumber); + fontRenderer.update(device, frameNumber); fontRenderer.render(renderPass, frameNumber); renderPass.end(); diff --git a/slider.js b/slider.js new file mode 100644 index 0000000..3bba2c8 --- /dev/null +++ b/slider.js @@ -0,0 +1,77 @@ +const controlMain = document.getElementById("control-main"); + +function remap(low1, high1, low2, high2, value) +{ + return low2 + (value - low1) * (high2 - low2) / (high1 - low1); +} + +const sliderDefaults = {"bone length":"32","velocity acc.":"385","velocity damp.":"796","bone count":"40"}; + +class Slider { + constructor(label, low, high, max = 1024) + { + const labelE = document.createElement("span"); + labelE.className = "label"; + labelE.innerHTML = label; + + const valueE = document.createElement("div"); + valueE.className = "value"; + + const inputE = document.createElement("input"); + inputE.className = "range"; + inputE.min = 0; + inputE.max = max; + inputE.type = "range"; + const spanE = document.createElement("span"); + spanE.className = "range-span code"; + + valueE.appendChild(inputE); + valueE.appendChild(spanE); + + controlMain.appendChild(labelE); + controlMain.appendChild(valueE); + + this.label = label; + this.input = inputE; + this.span = spanE; + this.low = low; + this.high = high; + this.lastValue = undefined; + + const storageValue = localStorage.getItem(this.label); + if (storageValue !== null) { + this.input.value = storageValue; + } else if (this.label in sliderDefaults) { + this.input.value = sliderDefaults[this.label]; + } + + this.update(); + } + + update() + { + const rawValue = parseInt(this.input.value); + const value = remap(this.input.min, this.input.max, this.low, this.high, this.input.value); + if (rawValue !== this.lastValue) { + this.lastValue = rawValue; + localStorage.setItem(this.label, rawValue); + } + if (this.high > 10) { + this.span.innerHTML = value.toFixed(0); + } else { + this.span.innerHTML = value.toFixed(4); + } + return value; + } +} + +function updateSliders(sliders) +{ + const values = {}; + for (let [key, value] of Object.entries(sliders)) { + values[key] = value.update(); + } + return values; +} + +export { Slider, updateSliders }; diff --git a/src/font_layout/api.cpp b/src/font_layout/api.cpp index 3bf8b03..db278a0 100644 --- a/src/font_layout/api.cpp +++ b/src/font_layout/api.cpp @@ -51,6 +51,6 @@ extern "C" { void font_layout__draw(FontLayout * font_layout) { - font_layout->draw_string("@"); + font_layout->draw_string("asdf qwer"); } }; diff --git a/src/font_layout/font_layout.cpp b/src/font_layout/font_layout.cpp index 99400c8..6757a56 100644 --- a/src/font_layout/font_layout.cpp +++ b/src/font_layout/font_layout.cpp @@ -9,7 +9,7 @@ static inline size_t texture_offset(font * font) void FontLayout::init(size_t buffer, int layout_buffer_length) { this->font = reinterpret_cast(buffer + 0); - this->glyphs = reinterpret_cast(buffer + (sizeof (font))); + this->glyphs = reinterpret_cast(buffer + (sizeof (struct font))); this->texture = reinterpret_cast(buffer + texture_offset(this->font)); @@ -19,19 +19,25 @@ void FontLayout::init(size_t buffer, int layout_buffer_length) this->glyph_buffer = New(this->font->glyph_count); for (uint16_t i = 0; i < this->font->glyph_count; i++) { - glyph_bitmap const & bitmap = this->glyphs[i].bitmap; - this->glyph_buffer[i].position.x = float(bitmap.x) / float(this->font->texture_width); - this->glyph_buffer[i].position.y = float(bitmap.y) / float(this->font->texture_height); - this->glyph_buffer[i].size.x = float(bitmap.width) / float(this->font->texture_width); - this->glyph_buffer[i].size.y = float(bitmap.height) / float(this->font->texture_height); + glyph_texture const & texture = this->glyphs[i].texture; + this->glyph_buffer[i].texPosition.x = float(texture.x) / float(this->font->texture_width); + this->glyph_buffer[i].texPosition.y = float(texture.y) / float(this->font->texture_height); + this->glyph_buffer[i].texSize.x = float(texture.width) / float(this->font->texture_width); + this->glyph_buffer[i].texSize.y = float(texture.height) / float(this->font->texture_height); + this->glyph_buffer[i].size.x = float(texture.width); + this->glyph_buffer[i].size.y = float(texture.height); } } void FontLayout::draw_string(char const * string) { int i = 0; + + int32_t x = 0; + int32_t y = 0; + while (true) { - char c = string[i]; + char c = string[i++]; if (c == 0) return; @@ -44,8 +50,17 @@ void FontLayout::draw_string(char const * string) if (layout_buffer_index >= layout_buffer_length) return; - layout_buffer[layout_buffer_index].position = XMFLOAT3(0, 0, 0); + int32_t bearingY = glyph.metrics.height - glyph.metrics.horiBearingY; + XMFLOAT3 position { + float(x + glyph.metrics.horiBearingX) * 0.015625f, + float(y - bearingY) * 0.015625f, + 0.0f, + }; + + layout_buffer[layout_buffer_index].position = position; layout_buffer[layout_buffer_index].glyph_index = char_index; layout_buffer_index += 1; + + x += glyph.metrics.horiAdvance; } } diff --git a/src/font_layout/font_layout.h b/src/font_layout/font_layout.h index 2a308fb..70fbb08 100644 --- a/src/font_layout/font_layout.h +++ b/src/font_layout/font_layout.h @@ -4,8 +4,10 @@ #include "directxmath/DirectXMath.h" struct GlyphBuffer { - XMFLOAT2 position; // in uv units - XMFLOAT2 size; // in uv units + XMFLOAT2 texPosition; // in uv units + XMFLOAT2 texSize; // in uv units + XMFLOAT2 size; // in arbitrary-scale units + XMFLOAT2 unused; }; struct LayoutBuffer { diff --git a/tools/ttf_outline/ttf_outline.cpp b/tools/ttf_outline/ttf_outline.cpp index 3d00522..33a361f 100644 --- a/tools/ttf_outline/ttf_outline.cpp +++ b/tools/ttf_outline/ttf_outline.cpp @@ -133,16 +133,40 @@ load_outline_char(const FT_Face face, } } - glyph_bitmap& bitmap = glyph->bitmap; - bitmap.x = byteswap(rect.x); - bitmap.y = byteswap(rect.y); - bitmap.width = byteswap(rect.width); - bitmap.height = byteswap(rect.height); + /* + if (char_code == 'q' || char_code == 'f' || char_code == 'w') { + std::cerr << char(char_code) << " metrics " + << (face->glyph->metrics.width >> 6) << ' ' << (face->glyph->metrics.height >> 6) + << " bitmap wr " + << face->glyph->bitmap.width << ' ' << face->glyph->bitmap.rows + << " slot lt " + << face->glyph->bitmap_left << ' ' << face->glyph->bitmap_top + << " bearingX " + << (face->glyph->metrics.horiBearingX >> 6) + << " bearingY " + << (face->glyph->metrics.horiBearingY >> 6) + << '\n'; + } + */ - glyph_metrics& metrics = glyph->metrics; - metrics.horiBearingX = byteswap(face->glyph->metrics.horiBearingX); - metrics.horiBearingY = byteswap(face->glyph->metrics.horiBearingY); - metrics.horiAdvance = byteswap(face->glyph->metrics.horiAdvance); + { + glyph_texture& texture = glyph->texture; + texture.x = byteswap(rect.x); + texture.y = byteswap(rect.y); + texture.width = byteswap(rect.width); + texture.height = byteswap(rect.height); + + glyph_metrics& metrics = glyph->metrics; + metrics.width = byteswap(face->glyph->metrics.width); + metrics.height = byteswap(face->glyph->metrics.height); + metrics.horiBearingX = byteswap(face->glyph->metrics.horiBearingX); + metrics.horiBearingY = byteswap(face->glyph->metrics.horiBearingY); + metrics.horiAdvance = byteswap(face->glyph->metrics.horiAdvance); + + glyph_bitmap& bitmap = glyph->bitmap; + bitmap.left = byteswap(face->glyph->bitmap_left); + bitmap.top = byteswap(face->glyph->bitmap_top); + } return 0; } @@ -168,8 +192,8 @@ load_all_positions(const FT_Face face, const uint32_t num_glyphs = (end - start) + 1; struct rect rects[num_glyphs]; - FT_Int32 load_flags = FT_LOAD_DEFAULT | FT_LOAD_TARGET_MODE(FT_RENDER_MODE_SDF) | FT_LOAD_RENDER; FT_Render_Mode render_mode = FT_RENDER_MODE_SDF; + FT_Int32 load_flags = FT_LOAD_DEFAULT | FT_LOAD_TARGET_MODE(render_mode) | FT_LOAD_RENDER; // first, load all rectangles for (uint32_t char_code = start; char_code <= end; char_code++) { @@ -284,6 +308,7 @@ int main(int argc, char *argv[]) std::cerr << "end: 0x" << std::hex << end << '\n'; std::cerr << "texture_width: " << std::dec << window_curve_ix.window.width << '\n'; std::cerr << "texture_height: " << std::dec << window_curve_ix.window.height << '\n'; + std::cerr << "texture offset: " << (sizeof (glyph)) * num_glyphs + (sizeof (font)) << '\n'; FILE * out = fopen(argv[output_file_path], "w"); if (out == NULL) { @@ -293,7 +318,7 @@ int main(int argc, char *argv[]) fwrite(reinterpret_cast(&font), (sizeof (font)), 1, out); fwrite(reinterpret_cast(&glyphs[0]), (sizeof (glyph)), num_glyphs, out); - //fwrite(reinterpret_cast(&texture[0]), (sizeof (uint8_t)), texture_size, out); + fwrite(reinterpret_cast(&texture[0]), (sizeof (uint8_t)), texture_size, out); fclose(out); }