diff --git a/font.js b/font.js index 9dc8f3f..bc00f64 100644 --- a/font.js +++ b/font.js @@ -63,7 +63,7 @@ class FontRenderer { resource: sampler, }, { binding: 1, - resource: texture, + resource: texture.createView(), }, { binding: 2, resource: { buffer: glyphBuffer }, @@ -244,6 +244,9 @@ class FontRenderer { depthCompare: "always", format: "depth24plus", }, + multisample: { + count: 4, + } }); ////////////////////////////////////////////////////////////////////// @@ -292,11 +295,11 @@ class FontRenderer { this.sliderArray[4] = configuration.distanceThreshold; this.sliderArray[5] = configuration.distanceScale; - this.sliderArray[6] = configuration.distanceOffset; + //this.sliderArray[6] = configuration.distanceOffset; this.sliderArray[8] = configuration.outlineThreshold; this.sliderArray[9] = configuration.outlineScale; - this.sliderArray[10] = configuration.outlineOffset; + //this.sliderArray[10] = configuration.outlineOffset; this.sliderArray[12] = configuration.colorMix; this.sliderArray[13] = configuration.colorScale; diff --git a/font.wgsl b/font.wgsl index ed8098f..bf8637a 100644 --- a/font.wgsl +++ b/font.wgsl @@ -52,7 +52,8 @@ struct VertexInput { struct VertexOutput { @builtin(position) position: vec4f, - @location(0) texture: vec2f, + @location(0) @interpolate(linear, sample) texture: vec2f, + //@location(0) texture: vec2f, @location(1) @interpolate(flat) glyphIndex: u32, @location(2) foo: vec4f, }; @@ -102,6 +103,17 @@ fn palette(t: f32) -> vec4f return vec4f(color, 1); } +fn aliasedLinearSaturation(distance: f32, threshold: f32, scale: f32) -> f32 +{ + let distanceFromEdge = threshold - distance; + let dx = dpdx(distanceFromEdge); + let dy = dpdy(distanceFromEdge); + let gradientLength = length(vec2f(dx, dy)); + let thresholdWidth = scale * gradientLength; + let aliased = saturate((distanceFromEdge / thresholdWidth) + 0.5); + return aliased; +} + @fragment fn fragmentMain(input: VertexOutput) -> @location(0) vec4f { @@ -111,6 +123,22 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f let base = textureSample(fontTexture, linearSampler, input.texture * s + p); let distance = base.x; + let insideT = aliasedLinearSaturation(distance, + config.distanceThreshold, + config.distanceScale); + + let outlineT = aliasedLinearSaturation(distance, + config.outlineThreshold, + config.outlineScale); + + let insideColor = vec4f(1, 1, 1, 1); + let outlineColor = vec4f(0, 0, 0, 1); + let outsideColor = vec4f(0, 0, 0, 0); + + let outline = mix(outlineColor, outsideColor, outlineT); + return mix(insideColor, outline, insideT); + + /* let insideColorW = vec4f(1, 1, 1, 1); let insideColorP = palette(distance * config.colorScale + config.colorOffset); let insideColor = mix(insideColorW, insideColorP, config.colorMix); @@ -124,4 +152,5 @@ fn fragmentMain(input: VertexOutput) -> @location(0) vec4f config.distanceThreshold - config.outlineThreshold, config.outlineScale, config.outlineOffset); return color; + */ } diff --git a/fontSliders.js b/fontSliders.js index c0683ee..a99ddc1 100644 --- a/fontSliders.js +++ b/fontSliders.js @@ -2,23 +2,34 @@ 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), + const sliders = { + scale: new Slider("scale", 0.0, 0.01), + translateX: new Slider("translate x", -1, 1), + translateY: new Slider("translate y", -1, 1), - distanceThreshold: new Slider("dist. threshold", 0.0, 1.0), - distanceScale: new Slider("distance scale", 0.0, 200.0), - distanceOffset: new Slider("distance offset", -20.0, 20.0), + distanceThreshold: new Slider("dist. threshold", 0.25, 0.75), + distanceScale: new Slider("distance scale", 0.0, 2.0), + //distanceOffset: new Slider("distance offset", -20.0, 20.0), outlineThreshold: new Slider("outline threshold", 0.0, 1.0), - outlineScale: new Slider("outline scale", 0.0, 200.0), - outlineOffset: new Slider("outline offset", -20.0, 20.0), + outlineScale: new Slider("outline scale", 0.0, 50.0), + //outlineOffset: new Slider("outline offset", -20.0, 20.0), colorMix: new Slider("color mix", 0.0, 1.0), colorScale: new Slider("color scale", 0.0, 10.0), colorOffset: new Slider("color offset", 0.0, 3.1415), }; + + /* + const defaults = {"color scale":"148","bone length":"32","outline scale":"37","dist. threshold":"562","velocity acc.":"385","translate x":"464","outline threshold":"433","translate y":"495","bone count":"40","outline offset":"482","scale":"229","outline. threshold":"512","color offset":"118","distance offset":"676","threshold offset":"265","velocity damp.":"796","distance scale":"404","threshold scale":"739","color mix":"0"}; + for (let slider of Object.values(sliders)) { + if (slider.label in defaults) { + slider.input.value = defaults[slider.label]; + } + } + */ + + return sliders; } export { makeSliders }; diff --git a/index.js b/index.js index 289862c..275920e 100644 --- a/index.js +++ b/index.js @@ -59,6 +59,7 @@ function createDepthTexture() { size: [canvas.width, canvas.height], format: 'depth24plus', usage: GPUTextureUsage.RENDER_ATTACHMENT, + sampleCount: 4, }); } @@ -73,7 +74,7 @@ const pitchValue = document.getElementById("pitch"); function recreateDepth(force) { - if (canvas.clientWidth !== canvas.width || canvas.clientWidth !== canvas.height || force === true) { + if (force === true || canvas.clientWidth !== canvas.width || canvas.clientWidth !== canvas.height) { canvas.width = canvas.clientWidth; canvas.height = canvas.clientHeight; @@ -244,6 +245,8 @@ canvas.addEventListener("mousemove", onMouseMove); var canRender = false; var frameNumber = 0; +var multisampleTexture = undefined; + function render2() { if (canRender === true) { @@ -252,14 +255,30 @@ function render2() canRender = false; recreateDepth(false); - const colorView = context.getCurrentTexture().createView(); + const canvasTexture = context.getCurrentTexture(); + if ((multisampleTexture === undefined) || + (multisampleTexture.width !== canvasTexture.width) || + (multisampleTexture.height !== canvasTexture.height)) { + if ((multisampleTexture !== undefined)) { + multisampleTexture.destroy(); + } + + multisampleTexture = device.createTexture({ + format: canvasTexture.format, + usage: GPUTextureUsage.RENDER_ATTACHMENT, + size: [canvasTexture.width, canvasTexture.height], + sampleCount: 4, + }); + } updateView(); const encoder = device.createCommandEncoder(); const renderPass = encoder.beginRenderPass({ colorAttachments: [{ - view: colorView, + view: multisampleTexture.createView(), + //view: canvasTexture.createView(), + resolveTarget: canvasTexture.createView(), loadOp: "clear", clearValue: { r: 0.2, g: 0.2, b: 0.4, a: 1 }, storeOp: "store", diff --git a/slider.js b/slider.js index 1d9be89..7765518 100644 --- a/slider.js +++ b/slider.js @@ -5,8 +5,6 @@ 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) { @@ -41,8 +39,6 @@ class Slider { 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(); diff --git a/src/font_layout/font_layout.cpp b/src/font_layout/font_layout.cpp index 6757a56..a6da1cc 100644 --- a/src/font_layout/font_layout.cpp +++ b/src/font_layout/font_layout.cpp @@ -6,6 +6,8 @@ static inline size_t texture_offset(font * font) return (sizeof (struct font)) + (sizeof (struct glyph)) * font->glyph_count; } +extern "C" void log(int); + void FontLayout::init(size_t buffer, int layout_buffer_length) { this->font = reinterpret_cast(buffer + 0); @@ -18,12 +20,14 @@ void FontLayout::init(size_t buffer, int layout_buffer_length) this->layout_buffer = New(layout_buffer_length); this->glyph_buffer = New(this->font->glyph_count); + int o = 0; + log(o); for (uint16_t i = 0; i < this->font->glyph_count; i++) { 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].texPosition.x = float(texture.x - o) / float(this->font->texture_width); + this->glyph_buffer[i].texPosition.y = float(texture.y - o) / float(this->font->texture_height); + this->glyph_buffer[i].texSize.x = float(texture.width - o) / float(this->font->texture_width); + this->glyph_buffer[i].texSize.y = float(texture.height - o) / float(this->font->texture_height); this->glyph_buffer[i].size.x = float(texture.width); this->glyph_buffer[i].size.y = float(texture.height); } diff --git a/tools/ttf_outline/Makefile b/tools/ttf_outline/Makefile index 3dd7b79..f13b6ea 100644 --- a/tools/ttf_outline/Makefile +++ b/tools/ttf_outline/Makefile @@ -1,8 +1,13 @@ -CFLAGS = -Og -g -gdwarf-4 -Wall -Wextra -Wno-error -Wfatal-errors +OPT = -flto -O3 -fno-stack-protector + +CFLAGS = -g -gdwarf-4 -Wall -Wextra -Wno-error -Wfatal-errors CFLAGS += -Wno-error=unused-parameter CFLAGS += -Wno-error=unused-variable CFLAGS += -Wno-error=unused-but-set-variable CFLAGS += -I../../include +CFLAGS += $(OPT) + +LDFLAGS += $(OPT) CXXFLAGS = -std=c++23 diff --git a/tools/ttf_outline/ttf_2d_pack.cpp b/tools/ttf_outline/ttf_2d_pack.cpp index 50378b3..7d83c8a 100644 --- a/tools/ttf_outline/ttf_2d_pack.cpp +++ b/tools/ttf_outline/ttf_2d_pack.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include "ttf_2d_pack.h" @@ -9,9 +11,9 @@ struct size { uint16_t height; }; -constexpr struct size max_texture = {1024, 1024}; +constexpr struct size max_texture = {4096, 4096}; -inline bool area_valid(const uint8_t texture[max_texture.height][max_texture.width], +inline bool area_valid(const uint8_t * texture, const uint32_t x_offset, const uint32_t y_offset, const struct rect& rect, @@ -22,7 +24,7 @@ inline bool area_valid(const uint8_t texture[max_texture.height][max_texture.wid uint32_t x = x_offset + xi; uint32_t y = y_offset + yi; - if (texture[y][x] != 0) + if (texture[y * max_texture.width + x] != 0) return false; if (x >= window.width || y >= window.height) @@ -77,10 +79,12 @@ constexpr inline int log2(uint32_t n) case 256: return 8; case 512: return 9; case 1024: return 10; + case 2048: return 11; + case 4096: return 12; } } -static void pack_into(uint8_t texture[max_texture.height][max_texture.width], +static void pack_into(uint8_t * texture, size & window, rect & rect) { @@ -112,7 +116,7 @@ static void pack_into(uint8_t texture[max_texture.height][max_texture.width], uint32_t x = x_offset + xi; uint32_t y = y_offset + yi; - texture[y][x] = 1; + texture[y * max_texture.width + x] = 1; } } @@ -145,7 +149,8 @@ void insertion_sort(T * arr, int len) window_curve_ix pack_all(struct rect * rects, const uint32_t num_rects) { - uint8_t texture[max_texture.height][max_texture.width] = { 0 }; + uint8_t * texture = (uint8_t *)malloc(max_texture.height * max_texture.width); + memset(texture, 0x00, max_texture.height * max_texture.width); size window = {1, 1}; // sort all rectangles by size @@ -155,5 +160,7 @@ window_curve_ix pack_all(struct rect * rects, const uint32_t num_rects) pack_into(texture, window, rects[i]); } + free(texture); + return {window.width, window.height}; } diff --git a/tools/ttf_outline/ttf_outline.cpp b/tools/ttf_outline/ttf_outline.cpp index 33a361f..fa7db94 100644 --- a/tools/ttf_outline/ttf_outline.cpp +++ b/tools/ttf_outline/ttf_outline.cpp @@ -13,7 +13,7 @@ std::endian _target_endian; -constexpr uint32_t max_texture_dim = 1024; +constexpr uint32_t max_texture_dim = 4096; constexpr uint32_t max_texture_size = max_texture_dim * max_texture_dim; template< class T > @@ -287,7 +287,7 @@ int main(int argc, char *argv[]) uint32_t num_glyphs = (end - start) + 1; glyph glyphs[num_glyphs]; - uint8_t texture[max_texture_size]; + uint8_t * texture = (uint8_t *)malloc(max_texture_size); memset(texture, 0x00, max_texture_size); auto window_curve_ix = load_all_positions(face, start, end, glyphs, texture);