sdf string drawing
This commit is contained in:
parent
71052ffb69
commit
997bfac193
75
font.js
75
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;
|
||||
}
|
||||
|
||||
|
||||
36
font.wgsl
36
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<uniform> config: Configuration;
|
||||
@group(0) @binding(0) var<uniform> view: View;
|
||||
|
||||
@group(1) @binding(0) var linearSampler: sampler;
|
||||
@group(1) @binding(1) var fontTexture: texture_2d<f32>;
|
||||
@group(1) @binding(2) var<storage> glyphBuffer: array<GlyphBuffer>;
|
||||
|
||||
@group(2) @binding(0) var<storage> layoutBuffer: array<LayoutBuffer>;
|
||||
@group(2) @binding(1) var<uniform> 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);
|
||||
}
|
||||
|
||||
12
fontSliders.js
Normal file
12
fontSliders.js
Normal file
@ -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 };
|
||||
@ -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;
|
||||
|
||||
82
index.js
82
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();
|
||||
|
||||
77
slider.js
Normal file
77
slider.js
Normal file
@ -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 };
|
||||
@ -51,6 +51,6 @@ extern "C" {
|
||||
|
||||
void font_layout__draw(FontLayout * font_layout)
|
||||
{
|
||||
font_layout->draw_string("@");
|
||||
font_layout->draw_string("asdf qwer");
|
||||
}
|
||||
};
|
||||
|
||||
@ -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<struct font *>(buffer + 0);
|
||||
this->glyphs = reinterpret_cast<struct glyph *>(buffer + (sizeof (font)));
|
||||
this->glyphs = reinterpret_cast<struct glyph *>(buffer + (sizeof (struct font)));
|
||||
|
||||
this->texture = reinterpret_cast<void *>(buffer + texture_offset(this->font));
|
||||
|
||||
@ -19,19 +19,25 @@ void FontLayout::init(size_t buffer, int layout_buffer_length)
|
||||
|
||||
this->glyph_buffer = New<GlyphBuffer>(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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -133,16 +133,40 @@ load_outline_char(const FT_Face face,
|
||||
}
|
||||
}
|
||||
|
||||
glyph_bitmap& bitmap = glyph->bitmap;
|
||||
bitmap.x = byteswap<uint16_t>(rect.x);
|
||||
bitmap.y = byteswap<uint16_t>(rect.y);
|
||||
bitmap.width = byteswap<uint16_t>(rect.width);
|
||||
bitmap.height = byteswap<uint16_t>(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<int32_t>(face->glyph->metrics.horiBearingX);
|
||||
metrics.horiBearingY = byteswap<int32_t>(face->glyph->metrics.horiBearingY);
|
||||
metrics.horiAdvance = byteswap<int32_t>(face->glyph->metrics.horiAdvance);
|
||||
{
|
||||
glyph_texture& texture = glyph->texture;
|
||||
texture.x = byteswap<uint16_t>(rect.x);
|
||||
texture.y = byteswap<uint16_t>(rect.y);
|
||||
texture.width = byteswap<uint16_t>(rect.width);
|
||||
texture.height = byteswap<uint16_t>(rect.height);
|
||||
|
||||
glyph_metrics& metrics = glyph->metrics;
|
||||
metrics.width = byteswap<int32_t>(face->glyph->metrics.width);
|
||||
metrics.height = byteswap<int32_t>(face->glyph->metrics.height);
|
||||
metrics.horiBearingX = byteswap<int32_t>(face->glyph->metrics.horiBearingX);
|
||||
metrics.horiBearingY = byteswap<int32_t>(face->glyph->metrics.horiBearingY);
|
||||
metrics.horiAdvance = byteswap<int32_t>(face->glyph->metrics.horiAdvance);
|
||||
|
||||
glyph_bitmap& bitmap = glyph->bitmap;
|
||||
bitmap.left = byteswap<int32_t>(face->glyph->bitmap_left);
|
||||
bitmap.top = byteswap<int32_t>(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<void*>(&font), (sizeof (font)), 1, out);
|
||||
fwrite(reinterpret_cast<void*>(&glyphs[0]), (sizeof (glyph)), num_glyphs, out);
|
||||
//fwrite(reinterpret_cast<void*>(&texture[0]), (sizeof (uint8_t)), texture_size, out);
|
||||
fwrite(reinterpret_cast<void*>(&texture[0]), (sizeof (uint8_t)), texture_size, out);
|
||||
|
||||
fclose(out);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user