diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml index 11fe3ca6..a1061d50 100644 --- a/.github/workflows/linux-build.yml +++ b/.github/workflows/linux-build.yml @@ -8,35 +8,12 @@ on: jobs: build-test: - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v2 - - name: Cache Harfbuzz - uses: actions/cache@v3 - with: - path: harfbuzz-7.3.0 - key: ${{ runner.os }}-build + - uses: actions/checkout@v4 - name: Install build dependencies - run: sudo apt install -y curl gcc g++ libfreetype6-dev libglib2.0-dev libcairo2-dev make cmake automake autoconf libtool libharfbuzz-dev meson freeglut3-dev libglew-dev - - name: Download a recent harfbuzz archive - # TODO: Ubuntu 22.04 has harfbuzz < 4.0.0. Ubuntu 24.04 will have harfbuzz >= 4.0.0. - # The harfbuzz build will not be required when Github updates the runners. - run: curl -L https://github.com/harfbuzz/harfbuzz/releases/download/7.3.0/harfbuzz-7.3.0.tar.xz -O - - name: Decompress harfbuzz archive - run: tar xvf harfbuzz-7.3.0.tar.xz - - name: Configure harfbuzz build - run: cd harfbuzz-7.3.0; meson setup build - - name: Build harfbuzz - run: cd harfbuzz-7.3.0; meson compile -C build - - name: Install harfbuzz - run: cd harfbuzz-7.3.0; sudo meson install -C build - - name: Configure glyphy - run: ./autogen.sh - - name: Make glyphy - run: make + run: sudo apt update && sudo apt install -y gcc g++ libfreetype6-dev libharfbuzz-dev meson freeglut3-dev libglew-dev - name: Meson setup run: meson setup build - name: Meson compile run: meson compile -C build - - name: Run Validator against default font - run: ./demo/glyphy-validate ./demo/default-font.ttf diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..8e912c2e --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +build/ +*.o +*~ +.*.sw[nop] +*-glsl.h +default-font.h +default-text.h diff --git a/COPYING b/COPYING index 3218c75b..5eaef2ef 100644 --- a/COPYING +++ b/COPYING @@ -2,6 +2,7 @@ GLyphy is licensed under the so-called "Old MIT" license, since HarfBuzz has standardized around this license: Copyright 2012 The GLyphy Project Authors +Copyright 2017 by Eric Lengyel Permission is hereby granted, without written agreement and without license or royalty fees, to use, copy, modify, and distribute this diff --git a/Makefile.am b/Makefile.am deleted file mode 100644 index c1021cfe..00000000 --- a/Makefile.am +++ /dev/null @@ -1,30 +0,0 @@ -NULL = - -EXTRA_DIST = \ - autogen.sh \ - ac_define_dir.m4 \ - $(NULL) - -MAINTAINERCLEANFILES = \ - $(srcdir)/INSTALL \ - $(srcdir)/aclocal.m4 \ - $(srcdir)/autoscan.log \ - $(srcdir)/compile \ - $(srcdir)/config.guess \ - $(srcdir)/config.h.in \ - $(srcdir)/config.sub \ - $(srcdir)/configure.scan \ - $(srcdir)/depcomp \ - $(srcdir)/install-sh \ - $(srcdir)/ltmain.sh \ - $(srcdir)/missing \ - $(srcdir)/mkinstalldirs \ - $(srcdir)/ChangeLog \ - `find "$(srcdir)" -type f -name Makefile.in -print` - -SUBDIRS = src demo - -pkgconfigdir = $(libdir)/pkgconfig -pkgconfig_DATA = glyphy.pc - --include $(top_srcdir)/git.mk diff --git a/NEWS b/NEWS deleted file mode 100644 index 243fa3e7..00000000 --- a/NEWS +++ /dev/null @@ -1 +0,0 @@ -Initial import. diff --git a/README.md b/README.md index 3029e950..4155ac07 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,42 @@ -GLyphy is a signed-distance-field (SDF) text renderer using OpenGL ES2 shading language. +GLyphy is a GPU text renderer that renders glyph outlines directly, +using the [Slug algorithm](https://jcgt.org/published/0006/02/02/) +by Eric Lengyel for robust winding number calculation. -The main difference between GLyphy and other SDF-based OpenGL renderers is that most other projects sample the SDF into a texture. This has all the usual problems that sampling has. Ie. it distorts the outline and is low quality. +GLyphy works with quadratic Bezier curves (TrueType outlines) and +produces pixel-perfect rendering at any scale with proper antialiasing. -GLyphy instead represents the SDF using actual vectors submitted to the GPU. This results in very high quality rendering, though at a much higher runtime cost. +## Building -See this video for insight to how GLyphy works: +Requires: meson, OpenGL 3.3+, FreeType, HarfBuzz, GLUT, GLEW. -http://vimeo.com/behdad/glyphy +```sh +meson setup build +meson compile -C build +build/demo/glyphy-demo +``` -Dicussions happen on: +## How it works -https://groups.google.com/forum/#!forum/glyphy +Each glyph's outline is encoded into a compact blob stored in a GPU +buffer texture. The fragment shader computes a winding number by +casting horizontal and vertical rays against the quadratic Bezier +curves, using Lengyel's equivalence class algorithm for numerical +robustness. Curves are organized into bands for efficient early exit. ----------------------------------------------------------------------- +The vertex shader performs dynamic dilation to ensure edge pixels +are always shaded for antialiasing. -On GNOME3 and possibly other systems, if the vsync extension is not working (ie. pressing `v` in the demo doesn't have any effect), try running with `vblank_mode=0` env var. +Unlike Slug's reference vertex path, GLyphy computes dilation from +the projective Jacobian of NDC with respect to glyph-plane position +while using its existing quad vertex format, so the half-pixel +expansion remains perspective-aware. -### Compilation instructions on Mac OS X +## Notes -1. Install Xcode and command line tools (as of Xcode 4.3.x, from -   within `Preferences` -> `Downloads`). -2. Install [MacPorts](https://www.macports.org/install.php). -3. `sudo port install automake autoconf libtool pkgconfig freetype` -4. `./autogen.sh` -5. `make` +On GNOME3 and possibly other systems, if the vsync extension is not +working (pressing `v` in the demo has no effect), try running with +`vblank_mode=0`. -### Compilation instructions on Windows +## License -See [appveyor.yml](https://github.com/behdad/glyphy/blob/master/appveyor.yml), basically first get [vcpkg](https://github.com/Microsoft/vcpkg) and install `glew`, `freetype` and `freeglut` on it, then open win32\glyphy.sln -with Visual Studio. - -### Compilation instructions for emscripten - -Assuming you have installed emscripten and have its tools on path, - -1. `NOCONFIGURE=1 ./autogen.sh` -2. `CPPFLAGS='-s USE_FREETYPE=1' LDFLAGS='-s USE_FREETYPE=1' emconfigure ./configure` -3. `make EXEEXT=.html GL_LIBS= GLUT_LIBS=` -4. The result will be located on `demo/.libs/glyphy-demo.html` (not `demo/glyphy-demo.html`) +GLyphy is licensed under the Apache License, Version 2.0. diff --git a/ac_define_dir.m4 b/ac_define_dir.m4 deleted file mode 100644 index d98be2f9..00000000 --- a/ac_define_dir.m4 +++ /dev/null @@ -1,27 +0,0 @@ -dnl Taken from the autoconf archive (www.gnu.org) -dnl -dnl @synopsis AC_DEFINE_DIR(VARNAME, DIR [, DESCRIPTION]) -dnl -dnl This macro _AC_DEFINEs VARNAME to the expansion of the DIR -dnl variable, taking care of fixing up ${prefix} and such. -dnl -dnl Note that the 3 argument form is only supported with autoconf 2.13 and -dnl later (i.e. only where _AC_DEFINE supports 3 arguments). -dnl -dnl Examples: -dnl -dnl AC_DEFINE_DIR(DATADIR, datadir) -dnl AC_DEFINE_DIR(PROG_PATH, bindir, [Location of installed binaries]) -dnl -dnl @version $Id: ac_define_dir.m4,v 1.1 2002/12/31 02:15:21 heiko Exp $ -dnl @author Guido Draheim , original by Alexandre Oliva - -AC_DEFUN([AC_DEFINE_DIR], [ - test "x$prefix" = xNONE && prefix="$ac_default_prefix" - test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' - ac_define_dir=`eval echo [$]$2` - ac_define_dir=`eval echo [$]ac_define_dir` - ifelse($3, , - AC_DEFINE_UNQUOTED($1, "$ac_define_dir"), - AC_DEFINE_UNQUOTED($1, "$ac_define_dir", $3)) -]) diff --git a/acinclude.m4 b/acinclude.m4 deleted file mode 100644 index 86f674fd..00000000 --- a/acinclude.m4 +++ /dev/null @@ -1 +0,0 @@ -sinclude(ac_define_dir.m4) diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 78e62c6c..00000000 --- a/appveyor.yml +++ /dev/null @@ -1,11 +0,0 @@ -install: - - set PATH=C:\Program Files (x86)\MSBuild\14.0\Bin;%PATH% - - git clone https://github.com/Microsoft/vcpkg - - cd vcpkg - - powershell -exec bypass scripts\bootstrap.ps1 - - vcpkg install glew freetype freeglut - - vcpkg integrate install - - cd .. - -build_script: - - msbuild win32\glyphy.sln \ No newline at end of file diff --git a/autogen.sh b/autogen.sh deleted file mode 100755 index 842f1b17..00000000 --- a/autogen.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/bin/sh -# Run this to generate all the initial makefiles, etc. - -test -n "$srcdir" || srcdir=`dirname "$0"` -test -n "$srcdir" || srcdir=. - -olddir=`pwd` -cd $srcdir - -echo -n "checking for pkg-config... " -which pkg-config || { - echo "*** No pkg-config found, please install it ***" - exit 1 -} - -echo -n "checking for autoreconf... " -which autoreconf || { - echo "*** No autoreconf found, please install it ***" - exit 1 -} - -touch ChangeLog - -echo "running autoreconf --force --install --verbose" -autoreconf --force --install --verbose || exit $? - -cd $olddir -echo "running configure $@" -test -n "$NOCONFIGURE" || "$srcdir/configure" "$@" diff --git a/bench/bench-encode.cc b/bench/bench-encode.cc new file mode 100644 index 00000000..a336de47 --- /dev/null +++ b/bench/bench-encode.cc @@ -0,0 +1,235 @@ +/* + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. + * + */ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +struct bench_stats_t { + uint64_t glyphs; + uint64_t non_empty_glyphs; + uint64_t curves; + uint64_t blob_bytes; + uint64_t outline_ns; + uint64_t encode_ns; + uint64_t wall_ns; +}; + +static void +die (const char *message) +{ + fprintf (stderr, "%s\n", message); + exit (1); +} + +static void +usage (const char *argv0) +{ + fprintf (stderr, + "Usage: %s [-r repeats] fontfile\n" + "\n" + "Encode all glyphs in a font and report outline and blob timings.\n" + "Texture upload is not measured.\n", + argv0); +} + +static bool +parse_uint (const char *arg, unsigned int *value) +{ + char *end = NULL; + unsigned long parsed = strtoul (arg, &end, 10); + + if (!arg[0] || !end || *end || parsed > UINT_MAX) + return false; + + *value = (unsigned int) parsed; + return true; +} + +static glyphy_bool_t +accumulate_curve (const glyphy_curve_t *curve, + void *user_data) +{ + std::vector *curves = (std::vector *) user_data; + curves->push_back (*curve); + return true; +} + +static double +ns_to_ms (uint64_t ns) +{ + return ns / 1000000.; +} + +static double +ns_to_us_per_glyph (uint64_t ns, uint64_t glyphs) +{ + return glyphs ? (double) ns / glyphs / 1000. : 0.; +} + +static double +glyphs_per_second (uint64_t glyphs, uint64_t ns) +{ + return ns ? glyphs * 1000000000. / ns : 0.; +} + +static double +megabytes_per_second (uint64_t bytes, uint64_t ns) +{ + return ns ? bytes * 1000000000. / ns / (1024. * 1024.) : 0.; +} + +static bench_stats_t +benchmark_font (hb_face_t *face, + unsigned int repeats) +{ + bench_stats_t stats = {}; + hb_font_t *font = hb_font_create (face); + glyphy_encoder_t *encoder = glyphy_encoder_create (); + glyphy_curve_accumulator_t *acc = glyphy_curve_accumulator_create (); + std::vector curves; + std::vector scratch_buffer (1u << 20); + unsigned int glyph_count = hb_face_get_glyph_count (face); + + if (!glyph_count) + die ("Font has no glyphs"); + + curves.reserve (1024); + + typedef std::chrono::steady_clock clock; + clock::time_point wall_start = clock::now (); + + for (unsigned int repeat = 0; repeat < repeats; repeat++) { + for (unsigned int glyph_index = 0; glyph_index < glyph_count; glyph_index++) { + unsigned int output_len = 0; + glyphy_extents_t extents; + + curves.clear (); + glyphy_curve_accumulator_reset (acc); + glyphy_curve_accumulator_set_callback (acc, accumulate_curve, &curves); + + clock::time_point outline_start = clock::now (); + glyphy_harfbuzz(font_get_glyph_shape) (font, glyph_index, acc); + clock::time_point outline_end = clock::now (); + + if (!glyphy_curve_accumulator_successful (acc)) { + char message[128]; + snprintf (message, sizeof (message), + "Failed accumulating curves for glyph %u", glyph_index); + die (message); + } + + clock::time_point encode_start = clock::now (); + if (!glyphy_encoder_encode (encoder, + curves.empty () ? NULL : &curves[0], + curves.size (), + scratch_buffer.data (), + scratch_buffer.size (), + &output_len, + &extents)) { + char message[128]; + snprintf (message, sizeof (message), + "Failed encoding blob for glyph %u", glyph_index); + die (message); + } + clock::time_point encode_end = clock::now (); + + stats.glyphs++; + if (!glyphy_extents_is_empty (&extents)) + stats.non_empty_glyphs++; + stats.curves += glyphy_curve_accumulator_get_num_curves (acc); + stats.blob_bytes += (uint64_t) output_len * sizeof (glyphy_texel_t); + stats.outline_ns += std::chrono::duration_cast (outline_end - outline_start).count (); + stats.encode_ns += std::chrono::duration_cast (encode_end - encode_start).count (); + } + } + + stats.wall_ns = std::chrono::duration_cast (clock::now () - wall_start).count (); + + glyphy_encoder_destroy (encoder); + glyphy_curve_accumulator_destroy (acc); + hb_font_destroy (font); + + return stats; +} + +int +main (int argc, char **argv) +{ + const char *font_path = NULL; + unsigned int repeats = 1; + + for (int i = 1; i < argc; i++) { + if (!strcmp (argv[i], "-h") || !strcmp (argv[i], "--help")) { + usage (argv[0]); + return 0; + } + if (!strcmp (argv[i], "-r") || !strcmp (argv[i], "--repeats")) { + if (++i >= argc || !parse_uint (argv[i], &repeats) || !repeats) { + usage (argv[0]); + return 1; + } + continue; + } + if (argv[i][0] == '-') { + usage (argv[0]); + return 1; + } + if (font_path) { + usage (argv[0]); + return 1; + } + font_path = argv[i]; + } + + if (!font_path) { + usage (argv[0]); + return 1; + } + + hb_blob_t *blob = hb_blob_create_from_file_or_fail (font_path); + if (!blob) + die ("Failed to open font file"); + + hb_face_t *face = hb_face_create (blob, 0); + bench_stats_t stats = benchmark_font (face, repeats); + unsigned int glyph_count = hb_face_get_glyph_count (face); + double avg_curves = stats.glyphs ? (double) stats.curves / stats.glyphs : 0.; + double avg_blob_kb = stats.glyphs ? stats.blob_bytes / 1024. / stats.glyphs : 0.; + + printf ("font: %s\n", font_path); + printf ("glyphs: %u x %u repeats = %" PRIu64 " processed (%" PRIu64 " non-empty)\n", + glyph_count, repeats, stats.glyphs, stats.non_empty_glyphs); + printf ("total blob size: %" PRIu64 " bytes (%.2fkb)\n", + stats.blob_bytes, + stats.blob_bytes / 1024.); + printf ("avg curves per glyph: %.2f\n", avg_curves); + printf ("avg blob size per glyph: %.2fkb\n", avg_blob_kb); + printf ("outline: %8.3fms total, %.3fus/glyph, %.0f glyphs/s\n", + ns_to_ms (stats.outline_ns), + ns_to_us_per_glyph (stats.outline_ns, stats.glyphs), + glyphs_per_second (stats.glyphs, stats.outline_ns)); + printf ("encode: %8.3fms total, %.3fus/glyph, %.0f glyphs/s, %.2f MiB/s\n", + ns_to_ms (stats.encode_ns), + ns_to_us_per_glyph (stats.encode_ns, stats.glyphs), + glyphs_per_second (stats.glyphs, stats.encode_ns), + megabytes_per_second (stats.blob_bytes, stats.encode_ns)); + printf ("wall: %8.3fms total (outline + encode + loop overhead)\n", + ns_to_ms (stats.wall_ns)); + + hb_face_destroy (face); + hb_blob_destroy (blob); + + return 0; +} diff --git a/bench/meson.build b/bench/meson.build new file mode 100644 index 00000000..de4944eb --- /dev/null +++ b/bench/meson.build @@ -0,0 +1,6 @@ +bench_encode = executable('bench-encode', + 'bench-encode.cc', + include_directories: [confinc, srcinc], + dependencies: [harfbuzz_dep], + link_with: [libglyphy], + install: false) diff --git a/configure.ac b/configure.ac deleted file mode 100644 index 982d0efb..00000000 --- a/configure.ac +++ /dev/null @@ -1,107 +0,0 @@ -AC_PREREQ([2.64]) -AC_INIT([glyphy], - [0.2.0], - [http://code.google.com/p/glyphy/issues/list], - [glyphy], - [http://code.google.com/p/glyphy/]) - -AC_CONFIG_SRCDIR([glyphy.pc.in]) -AC_CONFIG_HEADERS([config.h]) - -AM_INIT_AUTOMAKE([1.11.1 foreign dist-bzip2 no-dist-gzip -Wall]) -AM_SILENT_RULES([yes]) - -AC_CANONICAL_HOST - -android=false -case $host in - *-android*) android=true;; -esac -AM_CONDITIONAL(ANDROID, $android) - -# Initialize libtool -LT_PREREQ([2.2]) -LT_INIT([disable-static]) - -# TODO flesh this out -GLYPHY_LIBTOOL_VERSION_INFO="0:0:0" -AC_SUBST(GLYPHY_LIBTOOL_VERSION_INFO) - -# Check for programs -AC_PROG_CC -AM_PROG_CC_C_O -AC_PROG_CXX -PKG_PROG_PKG_CONFIG - -dnl ========================================================================== - -dnl syntax: pkgname, symbol, framework -AC_DEFUN([GLYPHY_CHECK_PACKAGE], -[ - m4_define([pkg], [$1]) - m4_define([_symbol], [$2]) - m4_define([_framework], [$3]) - m4_define([_more], [$4]) - m4_define([PKG], [m4_translit(pkg,[-a-z.+],[_A-Z_])]) - - HAVE_[]PKG=false - - if test -n "_framework"; then - case "$host" in - *-darwin*) - AC_MSG_CHECKING([for ]PKG) - HAVE_[]PKG=true - PKG[]_LIBS="-framework _framework" - AC_MSG_RESULT($PKG[]_LIBS) - break; - ;; - esac - fi - - if ! $HAVE_[]PKG; then - PKG_CHECK_MODULES(PKG, pkg, [HAVE_]PKG[=true], [HAVE_]PKG[=false]) - fi - - m4_foreach_w(lib, [m4_translit(PKG,[A-Z],[a-z]) PKG _more], [ - if ! $HAVE_[]PKG; then - AC_CHECK_LIB(lib, _symbol, [ - HAVE_]PKG[=true - ]PKG[_LIBS=-l]lib[ - ]) - fi - ]) - if [$HAVE_]PKG; then AC_DEFINE([HAVE_]PKG, 1, [Have ]pkg[ library]) fi - AM_CONDITIONAL([HAVE_]PKG, [$HAVE_]PKG) -]) - -GLYPHY_CHECK_PACKAGE(freetype2, FT_Init_FreeType) - -PKG_CHECK_MODULES(HARFBUZZ, harfbuzz >= 4.0.0, HAVE_HARFBUZZ=true, HAVE_HARFBUZZ=false) -AM_CONDITIONAL(HAVE_HARFBUZZ, $HAVE_HARFBUZZ) - -if $android; then - GLYPHY_CHECK_PACKAGE(gl, glCreateShader,, GLESv2) - GLYPHY_CHECK_PACKAGE(glew, glewInit) - GLYPHY_CHECK_PACKAGE(glut, glutInit,, freeglut-gles2) - GLUT_CFLAGS=-DFREEGLUT_GLES2 - AC_SUBST(GLUT_CFLAGS) - AC_DEFINE([__ANDROID__], 1, [On Android]) -else - GLYPHY_CHECK_PACKAGE(gl, glCreateShader, OpenGL) - GLYPHY_CHECK_PACKAGE(glew, glewInit) - GLYPHY_CHECK_PACKAGE(glut, glutInit, GLUT) -fi - -dnl =========================================================================== - -AC_DEFINE_DIR([PKGDATADIR], [$pkgdatadir], [Define to the directory containing package data.]) - -AC_CONFIG_FILES([ -glyphy.pc -Makefile -src/Makefile -demo/Makefile -demo/android/Makefile -]) - -AC_OUTPUT diff --git a/demo/Makefile.am b/demo/Makefile.am deleted file mode 100644 index c611be3d..00000000 --- a/demo/Makefile.am +++ /dev/null @@ -1,105 +0,0 @@ -NULL = -SUBDIRS = -EXTRA_DIST = -CLEANFILES = -DISTCLEANFILES = -MAINTAINERCLEANFILES = -BUILT_SOURCES = -noinst_PROGRAMS = - -BUILT_SOURCES += default-text.h default-font.h -EXTRA_DIST += default-text.txt default-font.ttf -default-text.h: default-text.txt $(top_srcdir)/src/stringize - $(AM_V_GEN) $(top_srcdir)/src/stringize "static const char default_text[]" $< > $@ -default-font.h: default-font.ttf - $(AM_V_GEN) { \ - echo "static const unsigned char default_font[] = {"; \ - hexdump -v -e '"x" 1/1 "%02X" " "' < $< | fmt | sed 's/ *x/\\x/g;s/^/"/;s/$$/"/'; \ - echo '};'; \ - } > $@ - -if HAVE_HARFBUZZ -if HAVE_GL -if HAVE_GLUT - -if ANDROID -SUBDIRS += android -else -noinst_PROGRAMS += glyphy-demo -glyphy_demo_CPPFLAGS = \ - -I $(top_srcdir)/src \ - $(HARFBUZZ_CFLAGS) \ - $(GL_CFLAGS) \ - $(GLEW_CFLAGS) \ - $(GLUT_CFLAGS) \ - $(NULL) -glyphy_demo_LDADD = \ - $(top_builddir)/src/libglyphy.la \ - -lm \ - $(HARFBUZZ_LIBS) \ - $(GL_LIBS) \ - $(GLEW_LIBS) \ - $(GLUT_LIBS) \ - $(NULL) -glyphy_demo_SOURCES = \ - default-font.h \ - default-text.h \ - demo-atlas.h \ - demo-atlas.cc \ - demo-buffer.h \ - demo-buffer.cc \ - demo-common.h \ - demo-font.h \ - demo-font.cc \ - demo-glstate.h \ - demo-glstate.cc \ - demo-shader.h \ - demo-shader.cc \ - demo-view.h \ - demo-view.cc \ - glyphy-demo.cc \ - matrix4x4.h \ - matrix4x4.c \ - trackball.h \ - trackball.c \ - $(SHADERHEADERS) \ - $(NULL) -endif -SHADERS = \ - demo-atlas.glsl \ - demo-fshader.glsl \ - demo-vshader.glsl \ - $(NULL) -SHADERHEADERS = $(patsubst %.glsl,%-glsl.h, $(SHADERS)) -BUILT_SOURCES += $(SHADERHEADERS) -EXTRA_DIST += $(SHADERS) - -%-glsl.h: %.glsl $(top_srcdir)/src/stringize - $(AM_V_GEN) $(top_srcdir)/src/stringize "static const char *`echo "$<" | \ - sed 's@.*/@@;s/[-.]/_/g'`" "$<" > "$@.tmp" && \ - mv "$@.tmp" "$@" || ($(RM) "$@.tmp"; false) - -endif -endif -endif - - -if HAVE_FREETYPE2 - -noinst_PROGRAMS += glyphy-validate -glyphy_validate_CPPFLAGS = \ - -I $(top_srcdir)/src \ - $(FREETYPE2_CFLAGS) \ - $(NULL) -glyphy_validate_LDADD = \ - $(top_builddir)/src/libglyphy.la \ - $(FREETYPE2_LIBS) \ - $(NULL) -glyphy_validate_SOURCES = \ - glyphy-validate.cc \ - $(NULL) - -endif - - --include $(top_srcdir)/git.mk diff --git a/demo/android/AndroidManifest.xml b/demo/android/AndroidManifest.xml deleted file mode 100644 index 62934bbe..00000000 --- a/demo/android/AndroidManifest.xml +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff --git a/demo/android/Makefile.am b/demo/android/Makefile.am deleted file mode 100644 index f584851f..00000000 --- a/demo/android/Makefile.am +++ /dev/null @@ -1,15 +0,0 @@ -if ANDROID - -all: - NDK_PROJECT_PATH=$(srcdir) ndk-build NDK_DEBUG=1 - ant -buildfile $(srcdir)/build.xml debug - -clean-local: - NDK_PROJECT_PATH=$(srcdir) ndk-build clean - ant -buildfile $(srcdir)/build.xml clean - -endif - -GITIGNOREFILES = bin libs obj - --include $(top_srcdir)/git.mk diff --git a/demo/android/README b/demo/android/README deleted file mode 100644 index a8b1fbc9..00000000 --- a/demo/android/README +++ /dev/null @@ -1,57 +0,0 @@ -To build GLyphy Demo app on Android, follow these steps: - -- Download and install Android SDK and NDK, for Android API version 18 or later. - -- Install a "standalone" toolchain with the NDK, and set PLATFORM_PREFIX env - var to point to it. You can do this by running, eg: - - ndk/build/tools/make-standalone-toolchain.sh \ - --platform=android-18 \ - --install-dir=/prefix - - Adjust platform and install-dir. - -- Make sure the cross-compile tools from PLATFORM_PREFIX are in the path. - Ie. that you can run arm-linux-androideabi-gcc. - -- Configure and install FreeType. Within the FreeType tarball run: - - ./configure --host=arm-linux-androideabi --prefix=$PLATFORM_PREFIX --without-png - make install - -- Configure and install freeglut-gles2. We want the pre-3.0 version of freeglut - which has native Android support. Get from SVN and run: - - cmake -D CMAKE_TOOLCHAIN_FILE=android_toolchain.cmake \ - -D CMAKE_INSTALL_PREFIX=$PLATFORM_PREFIX \ - -D CMAKE_BUILD_TYPE=Debug \ - -D FREEGLUT_GLES2=ON \ - -D FREEGLUT_BUILD_DEMOS=NO \ - .. - make install - -- Configure GLyphy: - - ./configure \ - --host=arm-linux-androideabi \ - --prefix=$PLATFORM_PREFIX \ - --enable-static \ - PKG_CONFIG_LIBDIR=$PLATFORM_PREFIX/lib/pkgconfig - -- Make and install the GLyphy library (but not the demos): - - make -C src install - -- Add a local.properties file to demo/android, with: - - sdk.dir=/path/to/sdk - - By default GLyphy Demo app builds against android-18 target. If you want - to override that, you can add a target=android-XX line to the above file. - -- Finally, make the demo app: - - make -C demo - -- If all goes well, you should now have demo/android/bin/GLyphyDemo-debug.apk - diff --git a/demo/android/build.xml b/demo/android/build.xml deleted file mode 100644 index b5142b7d..00000000 --- a/demo/android/build.xml +++ /dev/null @@ -1,92 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/demo/android/jni/Android.mk b/demo/android/jni/Android.mk deleted file mode 100644 index 7f5ab98e..00000000 --- a/demo/android/jni/Android.mk +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright (C) 2010 The Android Open Source Project -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_MODULE := freetype -LOCAL_SRC_FILES := $(PLATFORM_PREFIX)/lib/libfreetype.a -include $(PREBUILT_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := glyphy -LOCAL_SRC_FILES := $(PLATFORM_PREFIX)/lib/libglyphy.a -include $(PREBUILT_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := glut -LOCAL_SRC_FILES := $(PLATFORM_PREFIX)/lib/libfreeglut-gles2.a -include $(PREBUILT_STATIC_LIBRARY) - -include $(CLEAR_VARS) -LOCAL_MODULE := glyphy-demo -LOCAL_CPP_EXTENSION := .cc -LOCAL_SRC_FILES := \ - ../../matrix4x4.c \ - ../../trackball.c \ - ../../demo-atlas.cc \ - ../../demo-buffer.cc \ - ../../demo-font.cc \ - ../../demo-glstate.cc \ - ../../demo-shader.cc \ - ../../demo-view.cc \ - ../../glyphy-demo.cc \ - $(NULL) -LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv2 -lz -LOCAL_WHOLE_STATIC_LIBRARIES := gnustl_static freetype glyphy glut -include $(BUILD_SHARED_LIBRARY) - -$(call import-module,android/native_app_glue) diff --git a/demo/android/jni/Application.mk b/demo/android/jni/Application.mk deleted file mode 100644 index af5560ec..00000000 --- a/demo/android/jni/Application.mk +++ /dev/null @@ -1,11 +0,0 @@ -PLATFORM_PREFIX := /home/behdad/.local/arm-linux-androideabi - -APP_STL := gnustl_static - -APP_CPPFLAGS := \ - -I$(PLATFORM_PREFIX)/include/ \ - -I$(PLATFORM_PREFIX)/include/freetype2 \ - -I$(PLATFORM_PREFIX)/include/glyphy \ - -DDEFAULT_FONT='"/system/fonts/Roboto-Regular.ttf"' \ - -DFREEGLUT_GLES2 \ - $(NULL) diff --git a/demo/android/project.properties b/demo/android/project.properties deleted file mode 100644 index ce39f2d0..00000000 --- a/demo/android/project.properties +++ /dev/null @@ -1,14 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system edit -# "ant.properties", and override values to adapt the script to your -# project structure. -# -# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): -#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - -# Project target. -target=android-18 diff --git a/demo/android/res/values/strings.xml b/demo/android/res/values/strings.xml deleted file mode 100644 index c5c76902..00000000 --- a/demo/android/res/values/strings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - GLyphy Demo - diff --git a/demo/default-text.h b/demo/default-text.h deleted file mode 100644 index b28e1286..00000000 --- a/demo/default-text.h +++ /dev/null @@ -1,71 +0,0 @@ -static const char default_text[] = -"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam tincidunt nibh quis semper malesuada. Mauris sit amet metus sed tellus gravida\n" -"maximus sit amet a enim. Morbi et lorem congue, aliquam lorem vitae, condimentum erat. Mauris aliquet, sapien consequat blandit mattis, velit ante\n" -"molestie mi, ac condimentum justo leo sed odio. Curabitur at suscipit quam. Ut ac convallis ante, at sollicitudin sapien. Nulla pellentesque felis\n" -"id mi blandit dictum. Phasellus ultrices, odio non volutpat tincidunt, neque quam tristique lacus, nec gravida nulla ante id risus. Nulla sit amet\n" -"bibendum lectus, sed bibendum lectus. Vivamus ultrices metus sit amet sapien posuere volutpat. Suspendisse luctus non mauris nec iaculis. Duis\n" -"mattis enim libero, ac malesuada tortor gravida tempor. Cras sagittis felis at sollicitudin fermentum. Duis et ipsum bibendum, viverra felis quis,\n" -"consectetur lacus. Donec vulputate risus imperdiet, tincidunt purus nec, vestibulum lorem. Morbi iaculis tincidunt rutrum. Duis sit amet nulla\n" -"ut lectus efficitur suscipit. Curabitur urna turpis, congue lacinia varius vitae, interdum vel dolor. Vestibulum sit amet suscipit arcu, sit amet\n" -"tincidunt ipsum. Maecenas feugiat ante vel fermentum viverra. Sed aliquam sem ac quam bibendum, sit amet fringilla augue pharetra. Morbi scelerisque\n" -"tempus purus, interdum tempor est pulvinar bibendum. Duis tincidunt dictum ante vel sodales. Fusce quis cursus metus. Pellentesque mi mauris,\n" -"tincidunt ut orci ut, interdum dapibus dolor. Aliquam blandit, nisl et rhoncus laoreet, tellus nulla blandit tellus, sit amet cursus magna enim nec\n" -"ante. Integer venenatis a est sed hendrerit. Proin id porttitor turpis, aliquam tempus ex. Morbi tristique, felis ut aliquet luctus, orci tortor\n" -"sodales sem, vel imperdiet justo tortor sit amet arcu. Quisque ipsum sem, lacinia in fermentum eu, maximus in lectus. Pellentesque quis scelerisque\n" -"neque, eget ultricies ligula. Etiam accumsan orci mi, ut blandit nibh viverra quis. Pellentesque tincidunt auctor dictum. Integer tincidunt neque\n" -"at enim ultrices, in accumsan augue elementum. Sed placerat eros leo, ac venenatis metus dignissim vel. Suspendisse mauris sem, pulvinar id purus\n" -"non, hendrerit pretium sem. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Nulla accumsan massa id nulla\n" -"viverra semper. Aliquam sit amet neque vitae diam rhoncus feugiat. Etiam tellus purus, rhoncus id enim a, fermentum volutpat neque. Vestibulum\n" -"tortor augue, dapibus at varius sed, varius at nisl. Ut pharetra elementum varius. Nulla molestie, urna sit amet euismod venenatis, nunc magna\n" -"bibendum augue, vitae elementum augue elit eget urna. Morbi a leo at sapien tempor varius sit amet venenatis metus. Sed ac pellentesque turpis,\n" -"quis malesuada ligula. Cras quis ex mattis, ultricies nibh ac, vestibulum velit. Sed sed sagittis libero, at consectetur massa. Cras quis risus\n" -"nunc. Curabitur vitae odio laoreet, sodales sem eu, fermentum risus. Quisque laoreet felis eu mattis laoreet. Phasellus auctor velit ut varius\n" -"accumsan. Sed molestie bibendum mi varius auctor. Vestibulum iaculis, lacus ut mattis convallis, turpis purus cursus nibh, nec ullamcorper dolor nibh\n" -"eget nunc. Nam lacinia justo ac vestibulum lacinia. Nulla vestibulum eu urna a sodales. Aliquam blandit congue lacus vitae ornare. Nulla bibendum\n" -"vehicula tortor eu vestibulum. Mauris nec pretium est. Pellentesque faucibus quam et est sollicitudin, quis condimentum justo placerat. Etiam urna\n" -"elit, porttitor sodales dui congue, maximus sodales dolor. Proin lacinia diam quis libero molestie viverra. Pellentesque tortor mauris, sodales\n" -"vel luctus non, euismod eu risus. Nulla sed condimentum ex. Morbi ac mi quis felis laoreet mattis. Fusce nec quam orci. Morbi pretium quis risus\n" -"a tincidunt. Phasellus accumsan suscipit nisi sed viverra. Vestibulum eu vulputate lorem. Nullam fermentum turpis eget nulla rutrum egestas. Sed\n" -"tortor nisi, consectetur a semper ut, lacinia in lectus. Phasellus finibus at diam nec interdum. Nunc mollis congue enim, et tempor neque dapibus\n" -"quis. Nunc a egestas quam, vitae tempor mi. Sed at lobortis neque. Pellentesque habitant morbi tristique senectus et netus et malesuada fames\n" -"ac turpis egestas. Etiam blandit mi sit amet ipsum accumsan, eget euismod nulla facilisis. Morbi imperdiet ex massa, in porttitor nibh posuere\n" -"ut. Curabitur bibendum lorem id ultricies placerat. Aenean tincidunt malesuada mauris, sit amet rhoncus odio ornare ac. Morbi venenatis turpis\n" -"non nulla tincidunt, bibendum tincidunt quam ultricies. Donec dictum, lorem eget commodo malesuada, risus nisi ornare augue, feugiat accumsan\n" -"dui massa eu ipsum. Mauris aliquam sapien id felis faucibus ornare. Nullam eleifend felis vel metus rutrum mollis. Quisque posuere eros vitae\n" -"imperdiet dapibus. Suspendisse quis est laoreet, faucibus lectus at, mattis est. Vestibulum nec neque ut ante auctor luctus. Nunc posuere vulputate\n" -"egestas. Quisque sit amet sodales erat, ac suscipit lorem. Ut auctor dapibus efficitur. Phasellus tincidunt cursus neque in ultrices. Sed pretium\n" -"viverra felis vitae aliquet. Vivamus at quam libero. Phasellus mattis orci ut egestas finibus. Suspendisse mattis maximus consequat. Ut id aliquam\n" -"lectus. Maecenas ut libero in nulla tempus accumsan at at tellus. Quisque a nibh quis ligula pharetra vehicula id et ante. Pellentesque et nisl in\n" -"lectus commodo sollicitudin non quis lectus. Vestibulum varius leo at diam fringilla, nec vulputate diam pellentesque. Curabitur id nisl in nibh\n" -"sollicitudin vestibulum non sit amet odio. Nunc pulvinar lectus ac est sagittis mollis. Nunc vel consectetur nulla, sit amet eleifend elit. Etiam\n" -"volutpat, ligula id cursus posuere, est lacus sagittis purus, scelerisque tempus sem lectus tempor nisi. Curabitur scelerisque leo et neque\n" -"ultricies semper. Sed in efficitur urna. Aliquam tempus cursus tortor. Sed feugiat mi elit, id ullamcorper nulla ullamcorper vitae. Suspendisse\n" -"rutrum ornare orci, eget facilisis nisi ornare et. Aenean sed leo vitae lorem congue lobortis. Nunc vel dui condimentum, sagittis sapien ac,\n" -"ultricies odio. Suspendisse elementum iaculis commodo. Phasellus pellentesque purus nec nisl pretium eleifend. Suspendisse a imperdiet urna. Fusce\n" -"eleifend et nunc at elementum. Morbi imperdiet varius mattis. Cras pharetra urna nulla, gravida sodales orci blandit et. Nulla dignissim eleifend\n" -"tempus. Vivamus placerat tristique purus et facilisis. In eget maximus urna. Mauris ut commodo elit. Aliquam erat volutpat. Donec efficitur augue\n" -"vitae nisi suscipit, vel aliquet sem posuere. Ut placerat ullamcorper lorem, eget consequat nunc ultricies nec. Nulla scelerisque ex non velit\n" -"efficitur imperdiet. Aliquam faucibus augue vel rhoncus feugiat. Aenean hendrerit nunc sem, ac hendrerit tortor fringilla sed. Morbi lorem massa,\n" -"dictum at maximus vitae, dictum et mi. Nullam ante elit, ultrices tincidunt tempor quis, aliquam ut lacus. Donec ut aliquet purus. Sed dolor mi,\n" -"rhoncus auctor tristique ac, lobortis non urna. Sed tempor leo ut finibus mattis. Aenean fermentum augue eget nisi aliquet feugiat. Donec convallis\n" -"laoreet nulla id malesuada. Vestibulum in congue enim, non sagittis massa. Ut et euismod nibh, eu euismod leo. Class aptent taciti sociosqu ad litora\n" -"torquent per conubia nostra, per inceptos himenaeos. Cras sodales purus nec ligula egestas cursus. Sed gravida, elit non eleifend semper, ex erat\n" -"ultricies augue, nec suscipit velit metus in augue. Nulla dapibus, ante a aliquam cursus, mi purus lobortis purus, sit amet feugiat neque sapien\n" -"at mauris. Vivamus mattis sagittis purus, quis viverra dolor mollis nec. Pellentesque habitant morbi tristique senectus et netus et malesuada fames\n" -"ac turpis egestas. Fusce in semper justo, eget mattis magna. Donec libero augue, aliquam eget mi at, laoreet sodales metus. Etiam gravida, libero\n" -"eu pellentesque imperdiet, nulla dolor scelerisque tortor, vitae placerat est risus non metus. Etiam vel laoreet est. Ut posuere turpis facilisis,\n" -"faucibus magna nec, ornare augue. Pellentesque auctor feugiat ornare. Etiam vulputate vitae odio et lacinia. Aenean semper purus vitae erat tincidunt\n" -"volutpat. Sed et laoreet nulla, eu condimentum arcu. Maecenas magna arcu, condimentum ut elementum vitae, laoreet sed justo. Maecenas bibendum\n" -"lacus vel blandit tincidunt. Praesent mattis, eros eget auctor malesuada, felis ligula egestas eros, at lacinia dolor lacus sed arcu. Praesent\n" -"lectus lacus, rhoncus ultricies facilisis vel, tristique pellentesque eros. Donec et justo placerat, finibus lectus id, mattis augue. Nam\n" -"consequat, felis a commodo varius, odio erat viverra urna, mollis sodales nibh lectus non turpis. Maecenas quis egestas risus, eget lobortis\n" -"dui. Sed pellentesque vitae neque non condimentum. Proin eu maximus magna, ac posuere libero. Maecenas porta interdum metus eu hendrerit. Integer\n" -"ut blandit risus. Maecenas facilisis consectetur dolor a mattis. Morbi elementum tincidunt turpis, vel tempus est mattis vel. Morbi et ultrices\n" -"velit, at dapibus neque. Vestibulum dictum in mi et rhoncus. In congue, elit vel ultricies maximus, arcu augue venenatis neque, sed imperdiet purus\n" -"leo et risus. Quisque tincidunt sapien felis, vitae elementum erat aliquet in. Curabitur mauris nibh, dictum sollicitudin dolor ut, accumsan luctus\n" -"nulla. Aenean quis diam quam. Etiam viverra odio at felis tincidunt laoreet. Proin euismod non lectus sit amet lacinia. Proin aliquet mauris et dui\n" -"consectetur sagittis. Ut tempus fringilla odio, vitae scelerisque neque efficitur ut. Quisque a nulla pulvinar, varius dolor sed, eleifend magna.\n" -"Vivamus eu neque pulvinar, accumsan odio venenatis, mollis leo. Morbi aliquet ac dolor sed lobortis. Maecenas urna sapien, volutpat non sagittis in,\n" -"tempor eget enim. Interdum et malesuada fames ac ante ipsum primis in faucibus. Sed mollis porta lectus, id lobortis metus elementum vitae. Nullam\n" -"non nulla sed risus rhoncus tempor. Nullam id quam hendrerit, dictum purus ut, maximus ipsum. Cras auct\n" -; diff --git a/demo/demo-atlas.cc b/demo/demo-atlas.cc index d84da068..1e88c4c9 100644 --- a/demo/demo-atlas.cc +++ b/demo/demo-atlas.cc @@ -1,19 +1,6 @@ /* - * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod */ #ifdef HAVE_CONFIG_H @@ -28,41 +15,32 @@ struct demo_atlas_t { GLuint tex_unit; GLuint tex_name; - GLuint tex_w; - GLuint tex_h; - GLuint item_w; - GLuint item_h_q; /* height quantum */ - GLuint cursor_x; - GLuint cursor_y; + GLuint buf_name; + GLuint capacity; + GLuint cursor; }; +static void +demo_atlas_bind_texture (demo_atlas_t *at); + demo_atlas_t * -demo_atlas_create (unsigned int w, - unsigned int h, - unsigned int item_w, - unsigned int item_h_quantum) +demo_atlas_create (unsigned int capacity) { - TRACE(); - demo_atlas_t *at = (demo_atlas_t *) calloc (1, sizeof (demo_atlas_t)); at->refcount = 1; glGetIntegerv (GL_ACTIVE_TEXTURE, (GLint *) &at->tex_unit); + glGenBuffers (1, &at->buf_name); glGenTextures (1, &at->tex_name); - at->tex_w = w; - at->tex_h = h; - at->item_w = item_w; - at->item_h_q = item_h_quantum; - at->cursor_x = 0; - at->cursor_y = 0; - - demo_atlas_bind_texture (at); + at->capacity = capacity; + at->cursor = 0; - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glBindBuffer (GL_TEXTURE_BUFFER, at->buf_name); + glBufferData (GL_TEXTURE_BUFFER, capacity * sizeof (glyphy_texel_t), NULL, GL_STATIC_DRAW); - gl(TexImage2D) (GL_TEXTURE_2D, 0, GL_RGBA, at->tex_w, at->tex_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + demo_atlas_bind_texture (at); + gl(TexBuffer) (GL_TEXTURE_BUFFER, GL_RGBA16I, at->buf_name); return at; } @@ -81,14 +59,15 @@ demo_atlas_destroy (demo_atlas_t *at) return; glDeleteTextures (1, &at->tex_name); + glDeleteBuffers (1, &at->buf_name); free (at); } -void +static void demo_atlas_bind_texture (demo_atlas_t *at) { glActiveTexture (at->tex_unit); - glBindTexture (GL_TEXTURE_2D, at->tex_name); + glBindTexture (GL_TEXTURE_BUFFER, at->tex_name); } void @@ -97,48 +76,31 @@ demo_atlas_set_uniforms (demo_atlas_t *at) GLuint program; glGetIntegerv (GL_CURRENT_PROGRAM, (GLint *) &program); - glUniform4i (glGetUniformLocation (program, "u_atlas_info"), - at->tex_w, at->tex_h, at->item_w, at->item_h_q); - glUniform1i (glGetUniformLocation (program, "u_atlas_tex"), at->tex_unit - GL_TEXTURE0); + glUniform1i (glGetUniformLocation (program, "u_atlas"), at->tex_unit - GL_TEXTURE0); } -void -demo_atlas_alloc (demo_atlas_t *at, - glyphy_rgba_t *data, - unsigned int len, - unsigned int *px, - unsigned int *py) +unsigned int +demo_atlas_alloc (demo_atlas_t *at, + glyphy_texel_t *data, + unsigned int len) { - GLuint w, h, x, y; - - w = at->item_w; - h = (len + w - 1) / w; - - if (at->cursor_y + h > at->tex_h) { - /* Go to next column */ - at->cursor_x += at->item_w; - at->cursor_y = 0; - } - - if (at->cursor_x + w <= at->tex_w && - at->cursor_y + h <= at->tex_h) - { - x = at->cursor_x; - y = at->cursor_y; - at->cursor_y += (h + at->item_h_q - 1) & ~(at->item_h_q - 1); - } else + if (at->cursor + len > at->capacity) die ("Ran out of atlas memory"); - demo_atlas_bind_texture (at); - if (w * h == len) - gl(TexSubImage2D) (GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data); - else { - gl(TexSubImage2D) (GL_TEXTURE_2D, 0, x, y, w, h - 1, GL_RGBA, GL_UNSIGNED_BYTE, data); - /* Upload the last row separately */ - gl(TexSubImage2D) (GL_TEXTURE_2D, 0, x, y + h - 1, len - (w * (h - 1)), 1, GL_RGBA, GL_UNSIGNED_BYTE, - data + w * (h - 1)); - } - - *px = x / at->item_w; - *py = y / at->item_h_q; + unsigned int offset = at->cursor; + at->cursor += len; + + glBindBuffer (GL_TEXTURE_BUFFER, at->buf_name); + glBufferSubData (GL_TEXTURE_BUFFER, + offset * sizeof (glyphy_texel_t), + len * sizeof (glyphy_texel_t), + data); + + return offset; +} + +unsigned int +demo_atlas_get_used (demo_atlas_t *at) +{ + return at->cursor; } diff --git a/demo/demo-atlas.glsl b/demo/demo-atlas.glsl deleted file mode 100644 index ec6bc1af..00000000 --- a/demo/demo-atlas.glsl +++ /dev/null @@ -1,16 +0,0 @@ -uniform sampler2D u_atlas_tex; -uniform ivec4 u_atlas_info; - -#define GLYPHY_TEXTURE1D_EXTRA_DECLS , sampler2D _tex, ivec4 _atlas_info, ivec2 _atlas_pos -#define GLYPHY_TEXTURE1D_EXTRA_ARGS , _tex, _atlas_info, _atlas_pos -#define GLYPHY_DEMO_EXTRA_ARGS , u_atlas_tex, u_atlas_info, gi.atlas_pos - -vec4 -glyphy_texture1D_func (int offset GLYPHY_TEXTURE1D_EXTRA_DECLS) -{ - ivec2 item_geom = _atlas_info.zw; - vec2 pos = (vec2 (_atlas_pos.xy * item_geom + - ivec2 (mod (float (offset), float (item_geom.x)), offset / item_geom.x)) + - + vec2 (.5, .5)) / vec2(_atlas_info.xy); - return texture2D (_tex, pos); -} diff --git a/demo/demo-atlas.h b/demo/demo-atlas.h index 729403f4..cea75d1a 100644 --- a/demo/demo-atlas.h +++ b/demo/demo-atlas.h @@ -1,19 +1,6 @@ /* - * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju */ #ifndef DEMO_ATLAS_H @@ -25,10 +12,7 @@ typedef struct demo_atlas_t demo_atlas_t; demo_atlas_t * -demo_atlas_create (unsigned int w, - unsigned int h, - unsigned int item_w, - unsigned int item_h_quantum); +demo_atlas_create (unsigned int capacity); demo_atlas_t * demo_atlas_reference (demo_atlas_t *at); @@ -37,15 +21,14 @@ void demo_atlas_destroy (demo_atlas_t *at); -void -demo_atlas_alloc (demo_atlas_t *at, - glyphy_rgba_t *data, - unsigned int len, - unsigned int *px, - unsigned int *py); +/* Returns the 1D offset where the data was placed. */ +unsigned int +demo_atlas_alloc (demo_atlas_t *at, + glyphy_texel_t *data, + unsigned int len); -void -demo_atlas_bind_texture (demo_atlas_t *at); +unsigned int +demo_atlas_get_used (demo_atlas_t *at); void demo_atlas_set_uniforms (demo_atlas_t *at); diff --git a/demo/demo-buffer.cc b/demo/demo-buffer.cc index dcbb5ff1..83a7fd73 100644 --- a/demo/demo-buffer.cc +++ b/demo/demo-buffer.cc @@ -1,19 +1,7 @@ /* * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod */ #ifdef HAVE_CONFIG_H @@ -23,13 +11,12 @@ #include "demo-buffer.h" struct demo_buffer_t { - unsigned int refcount; - glyphy_point_t cursor; std::vector *vertices; glyphy_extents_t ink_extents; glyphy_extents_t logical_extents; bool dirty; + GLuint vao_name; GLuint buf_name; }; @@ -37,9 +24,9 @@ demo_buffer_t * demo_buffer_create (void) { demo_buffer_t *buffer = (demo_buffer_t *) calloc (1, sizeof (demo_buffer_t)); - buffer->refcount = 1; buffer->vertices = new std::vector; + glGenVertexArrays (1, &buffer->vao_name); glGenBuffers (1, &buffer->buf_name); demo_buffer_clear (buffer); @@ -47,19 +34,13 @@ demo_buffer_create (void) return buffer; } -demo_buffer_t * -demo_buffer_reference (demo_buffer_t *buffer) -{ - if (buffer) buffer->refcount++; - return buffer; -} - void demo_buffer_destroy (demo_buffer_t *buffer) { - if (!buffer || --buffer->refcount) + if (!buffer) return; + glDeleteVertexArrays (1, &buffer->vao_name); glDeleteBuffers (1, &buffer->buf_name); delete buffer->vertices; free (buffer); @@ -93,13 +74,6 @@ demo_buffer_move_to (demo_buffer_t *buffer, buffer->cursor = *p; } -void -demo_buffer_current_point (demo_buffer_t *buffer, - glyphy_point_t *p) -{ - *p = buffer->cursor; -} - void demo_buffer_add_text (demo_buffer_t *buffer, const char *utf8, @@ -111,7 +85,7 @@ demo_buffer_add_text (demo_buffer_t *buffer, hb_buffer_t *hb_buffer = hb_buffer_create (); glyphy_point_t top_left = buffer->cursor; - buffer->cursor.y += font_size /* * font->ascent */; + buffer->cursor.y += font_size; double scale = font_size / hb_face_get_upem (hb_face); while (utf8) @@ -132,7 +106,6 @@ demo_buffer_add_text (demo_buffer_t *buffer, glyph_info_t gi; demo_font_lookup_glyph (font, glyph_index, &gi); - /* Update ink extents */ glyphy_extents_t ink_extents; glyphy_point_t position = buffer->cursor; position.x += scale * pos[i].x_offset; @@ -140,19 +113,17 @@ demo_buffer_add_text (demo_buffer_t *buffer, demo_shader_add_glyph_vertices (position, font_size, &gi, buffer->vertices, &ink_extents); glyphy_extents_extend (&buffer->ink_extents, &ink_extents); - /* Update logical extents */ glyphy_point_t corner; corner.x = buffer->cursor.x; corner.y = buffer->cursor.y - font_size; glyphy_extents_add (&buffer->logical_extents, &corner); - corner.x = buffer->cursor.x + font_size * gi.advance; + corner.x = buffer->cursor.x + scale * gi.advance; corner.y = buffer->cursor.y; glyphy_extents_add (&buffer->logical_extents, &corner); buffer->cursor.x += scale * pos[i].x_advance; } - if (end) { buffer->cursor.y += font_size; buffer->cursor.x = top_left.x; @@ -170,14 +141,54 @@ demo_buffer_draw (demo_buffer_t *buffer) { GLint program; glGetIntegerv (GL_CURRENT_PROGRAM, &program); - GLuint a_glyph_vertex_loc = glGetAttribLocation (program, "a_glyph_vertex"); + + glBindVertexArray (buffer->vao_name); glBindBuffer (GL_ARRAY_BUFFER, buffer->buf_name); if (buffer->dirty) { - glBufferData (GL_ARRAY_BUFFER, sizeof (glyph_vertex_t) * buffer->vertices->size (), (const char *) &(*buffer->vertices)[0], GL_STATIC_DRAW); + glBufferData (GL_ARRAY_BUFFER, + sizeof (glyph_vertex_t) * buffer->vertices->size (), + (const char *) &(*buffer->vertices)[0], GL_STATIC_DRAW); buffer->dirty = false; } - glEnableVertexAttribArray (a_glyph_vertex_loc); - glVertexAttribPointer (a_glyph_vertex_loc, 4, GL_FLOAT, GL_FALSE, sizeof (glyph_vertex_t), 0); + + GLsizei stride = sizeof (glyph_vertex_t); + + /* a_position: vec2 at offset 0 */ + GLint loc_pos = glGetAttribLocation (program, "a_position"); + glEnableVertexAttribArray (loc_pos); + glVertexAttribPointer (loc_pos, 2, GL_FLOAT, GL_FALSE, stride, + (const void *) offsetof (glyph_vertex_t, x)); + + /* a_texcoord: vec2 at offset 8 */ + GLint loc_tex = glGetAttribLocation (program, "a_texcoord"); + glEnableVertexAttribArray (loc_tex); + glVertexAttribPointer (loc_tex, 2, GL_FLOAT, GL_FALSE, stride, + (const void *) offsetof (glyph_vertex_t, tx)); + + /* a_corner: vec2 */ + GLint loc_corner = glGetAttribLocation (program, "a_corner"); + glEnableVertexAttribArray (loc_corner); + glVertexAttribPointer (loc_corner, 2, GL_FLOAT, GL_FALSE, stride, + (const void *) offsetof (glyph_vertex_t, cx)); + + /* a_texPerPos: vec2 */ + GLint loc_tpp = glGetAttribLocation (program, "a_texPerPos"); + glEnableVertexAttribArray (loc_tpp); + glVertexAttribPointer (loc_tpp, 2, GL_FLOAT, GL_FALSE, stride, + (const void *) offsetof (glyph_vertex_t, tpx)); + + /* a_glyphLoc: uint */ + GLint loc_glyph = glGetAttribLocation (program, "a_glyphLoc"); + glEnableVertexAttribArray (loc_glyph); + glVertexAttribIPointer (loc_glyph, 1, GL_UNSIGNED_INT, stride, + (const void *) offsetof (glyph_vertex_t, atlas_offset)); + glDrawArrays (GL_TRIANGLES, 0, buffer->vertices->size ()); - glDisableVertexAttribArray (a_glyph_vertex_loc); + + glDisableVertexAttribArray (loc_pos); + glDisableVertexAttribArray (loc_tex); + glDisableVertexAttribArray (loc_corner); + glDisableVertexAttribArray (loc_tpp); + glDisableVertexAttribArray (loc_glyph); + glBindVertexArray (0); } diff --git a/demo/demo-buffer.h b/demo/demo-buffer.h index 2948a7ff..bab9b647 100644 --- a/demo/demo-buffer.h +++ b/demo/demo-buffer.h @@ -1,18 +1,6 @@ /* * Copyright 2012 Google, Inc. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * * Google Author(s): Behdad Esfahbod */ @@ -28,9 +16,6 @@ typedef struct demo_buffer_t demo_buffer_t; demo_buffer_t * demo_buffer_create (void); -demo_buffer_t * -demo_buffer_reference (demo_buffer_t *buffer); - void demo_buffer_destroy (demo_buffer_t *buffer); @@ -47,10 +32,6 @@ void demo_buffer_move_to (demo_buffer_t *buffer, const glyphy_point_t *p); -void -demo_buffer_current_point (demo_buffer_t *buffer, - glyphy_point_t *p); - void demo_buffer_add_text (demo_buffer_t *buffer, const char *utf8, diff --git a/demo/demo-common.h b/demo/demo-common.h index 1e178746..d5692e75 100644 --- a/demo/demo-common.h +++ b/demo/demo-common.h @@ -1,18 +1,6 @@ /* * Copyright 2012 Google, Inc. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * * Google Author(s): Behdad Esfahbod, Maysum Panju */ @@ -32,89 +20,31 @@ #include #include -/* Tailor config for various platforms. */ - -#ifdef EMSCRIPTEN -/* https://github.com/kripken/emscripten/issues/340 */ -# undef HAVE_GLEW - /* WebGL shaders are ES2 */ -# define GL_ES_VERSION_2_0 1 -#endif - -#if defined(__ANDROID__) -# define HAVE_GLES2 1 -# define HAVE_GLUT 1 -#endif - #ifdef _WIN32 # define HAVE_GL 1 # define HAVE_GLEW 1 -# define HAVE_GLUT 1 +# define HAVE_GLFW 1 # define HAVE_FREETYPE2 1 #endif -/* Get Glew out of the way. */ -#ifdef HAVE_GLEW -# include -#else -# define GLEW_OK 0 - static inline int glewInit (void) { return GLEW_OK; } - static inline int glewIsSupported (const char *s) - { return 0 == strcmp ("GL_VERSION_2_0", s); } -#endif /* HAVE_GLEW */ +#include #ifdef __APPLE__ # define GL_SILENCE_DEPRECATION #endif -/* WTF this block?! */ -#if defined(HAVE_GLES2) -# include -#elif defined(HAVE_GL) -# ifndef HAVE_GLEW -# define GL_GLEXT_PROTOTYPES 1 -# if defined(__APPLE__) -# include -# else -# include -# endif -# endif +#if defined(HAVE_GL) # if defined(__APPLE__) # include -# else -# ifdef HAVE_GLEW -# ifdef _WIN32 -# include -# else -# include -# endif -# endif # endif #endif /* HAVE_GL */ -/* Finally, Glut. */ -#ifdef HAVE_GLUT -# if defined(__APPLE__) -# include -# else -# include -# endif -#endif +#include - - -/* Logging. */ -#ifdef __ANDROID__ -# include -# define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "glyphy-demo", __VA_ARGS__)) -# define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "glyphy-demo", __VA_ARGS__)) -# define LOGE(...) ((void)__android_log_print(ANDROID_LOG_ERROR, "glyphy-demo", __VA_ARGS__)) -#else /* !__ANDROID__ */ -# define LOGI(...) ((void) fprintf (stdout, __VA_ARGS__)) -# define LOGW(...) ((void) fprintf (stderr, __VA_ARGS__)) -# define LOGE(...) ((void) fprintf (stderr, __VA_ARGS__), abort ()) -#endif +#define LOGI(...) ((void) fprintf (stdout, __VA_ARGS__)) +#define LOGW(...) ((void) fprintf (stderr, __VA_ARGS__)) +#define LOGE(...) ((void) fprintf (stderr, __VA_ARGS__), abort ()) @@ -124,11 +54,6 @@ #define ARRAY_LEN(Array) (sizeof (Array) / sizeof (*Array)) -#define MIN_FONT_SIZE 14 -#define GRID_SIZE 20 /* Per EM */ -#define TOLERANCE (1./1024) -#define ENLIGHTEN_MAX .01 /* Per EM */ -#define EMBOLDEN_MAX .024 /* Per EM */ #define gl(name) \ @@ -153,25 +78,4 @@ T clamp (T v, T m, T M) return v < m ? m : v > M ? M : v; } - -#if defined(_MSC_VER) -#define DEMO_FUNC __FUNCSIG__ -#else -#define DEMO_FUNC __func__ -#endif - -struct auto_trace_t -{ - auto_trace_t (const char *func_) : func (func_) - { printf ("Enter: %s\n", func); } - - ~auto_trace_t (void) - { printf ("Leave: %s\n", func); } - - private: - const char * const func; -}; - -#define TRACE() auto_trace_t trace(DEMO_FUNC) - #endif /* DEMO_COMMON_H */ diff --git a/demo/demo-font.cc b/demo/demo-font.cc index a1faeef5..dc0cf530 100644 --- a/demo/demo-font.cc +++ b/demo/demo-font.cc @@ -1,19 +1,6 @@ /* - * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod */ #ifdef HAVE_CONFIG_H @@ -30,19 +17,16 @@ typedef std::map glyph_cache_t; struct demo_font_t { - unsigned int refcount; - hb_face_t *face; hb_font_t *font; glyph_cache_t *glyph_cache; demo_atlas_t *atlas; - glyphy_arc_accumulator_t *acc; + glyphy_encoder_t *encoder; + glyphy_curve_accumulator_t *acc; + std::vector *scratch_buffer; - /* stats */ unsigned int num_glyphs; - double sum_error; - unsigned int sum_endpoints; - double sum_fetch; + unsigned int sum_curves; unsigned int sum_bytes; }; @@ -51,38 +35,28 @@ demo_font_create (hb_face_t *face, demo_atlas_t *atlas) { demo_font_t *font = (demo_font_t *) calloc (1, sizeof (demo_font_t)); - font->refcount = 1; font->face = hb_face_reference (face); font->font = hb_font_create (face); font->glyph_cache = new glyph_cache_t (); font->atlas = demo_atlas_reference (atlas); - font->acc = glyphy_arc_accumulator_create (); - - font->num_glyphs = 0; - font->sum_error = 0; - font->sum_endpoints = 0; - font->sum_fetch = 0; - font->sum_bytes = 0; + font->encoder = glyphy_encoder_create (); + font->acc = glyphy_curve_accumulator_create (); + font->scratch_buffer = new std::vector (16384); return font; } -demo_font_t * -demo_font_reference (demo_font_t *font) -{ - if (font) font->refcount++; - return font; -} - void demo_font_destroy (demo_font_t *font) { - if (!font || --font->refcount) + if (!font) return; - glyphy_arc_accumulator_destroy (font->acc); + glyphy_encoder_destroy (font->encoder); + glyphy_curve_accumulator_destroy (font->acc); demo_atlas_destroy (font->atlas); + delete font->scratch_buffer; delete font->glyph_cache; hb_font_destroy (font->font); hb_face_destroy (font->face); @@ -102,99 +76,47 @@ demo_font_get_font (demo_font_t *font) return font->font; } -demo_atlas_t * -demo_font_get_atlas (demo_font_t *font) -{ - return font->atlas; -} - static glyphy_bool_t -accumulate_endpoint (glyphy_arc_endpoint_t *endpoint, - std::vector *endpoints) +accumulate_curve (const glyphy_curve_t *curve, + std::vector *curves) { - endpoints->push_back (*endpoint); + curves->push_back (*curve); return true; } static void -encode_ft_glyph (demo_font_t *font, - unsigned int glyph_index, - double tolerance_per_em, - glyphy_rgba_t *buffer, - unsigned int buffer_len, - unsigned int *output_len, - unsigned int *nominal_width, - unsigned int *nominal_height, - glyphy_extents_t *extents, - double *advance) +encode_glyph (demo_font_t *font, + unsigned int glyph_index, + glyphy_texel_t *buffer, + unsigned int buffer_len, + unsigned int *output_len, + glyphy_extents_t *extents, + double *advance) { -/* Used for testing only */ -#define SCALE (1. * (1 << 0)) - - unsigned int upem = hb_face_get_upem (font->face); - double tolerance = upem * tolerance_per_em; /* in font design units */ - double faraway = double (upem) / (MIN_FONT_SIZE * M_SQRT2); - double unit_size = double (upem) / GRID_SIZE; - double enlighten_max = double (upem) * ENLIGHTEN_MAX; - double embolden_max = double (upem) * EMBOLDEN_MAX; - std::vector endpoints; - - glyphy_arc_accumulator_reset (font->acc); - glyphy_arc_accumulator_set_tolerance (font->acc, tolerance); - glyphy_arc_accumulator_set_callback (font->acc, - (glyphy_arc_endpoint_accumulator_callback_t) accumulate_endpoint, - &endpoints); + std::vector curves; + + glyphy_curve_accumulator_reset (font->acc); + glyphy_curve_accumulator_set_callback (font->acc, + (glyphy_curve_accumulator_callback_t) accumulate_curve, + &curves); glyphy_harfbuzz(font_get_glyph_shape) (font->font, glyph_index, font->acc); - if (!glyphy_arc_accumulator_successful (font->acc)) - die ("Failed encoding arcs"); - - assert (glyphy_arc_accumulator_get_error (font->acc) <= tolerance); - - if (endpoints.size ()) - glyphy_outline_winding_from_even_odd (&endpoints[0], endpoints.size (), false); - - if (SCALE != 1.) - for (unsigned int i = 0; i < endpoints.size (); i++) - { - endpoints[i].p.x /= SCALE; - endpoints[i].p.y /= SCALE; - } - - double avg_fetch_achieved; - if (!glyphy_arc_list_encode_blob2 (endpoints.size () ? &endpoints[0] : NULL, endpoints.size (), - buffer, - buffer_len, - faraway / SCALE, - unit_size / SCALE, - enlighten_max / SCALE, - embolden_max / SCALE, - &avg_fetch_achieved, - output_len, - nominal_width, - nominal_height, - extents)) - die ("Failed encoding arcs"); - - glyphy_extents_scale (extents, 1. / upem, 1. / upem); - glyphy_extents_scale (extents, SCALE, SCALE); - - *advance = hb_font_get_glyph_h_advance (font->font, glyph_index) / (double) upem; - - if (0) - LOGI ("gid%3u: endpoints%3d; err%3g%%; tex fetch%4.1f; mem%4.1fkb\n", - glyph_index, - (unsigned int) glyphy_arc_accumulator_get_num_endpoints (font->acc), - round (100 * glyphy_arc_accumulator_get_error (font->acc) / tolerance), - avg_fetch_achieved, - (*output_len * sizeof (glyphy_rgba_t)) / 1024.); + if (!glyphy_curve_accumulator_successful (font->acc)) + die ("Failed accumulating curves"); + + if (!glyphy_encoder_encode (font->encoder, + curves.size () ? &curves[0] : NULL, curves.size (), + buffer, buffer_len, + output_len, + extents)) + die ("Failed encoding blob"); + + *advance = hb_font_get_glyph_h_advance (font->font, glyph_index); font->num_glyphs++; - font->sum_error += glyphy_arc_accumulator_get_error (font->acc) / tolerance; - font->sum_endpoints += glyphy_arc_accumulator_get_num_endpoints (font->acc); - font->sum_fetch += avg_fetch_achieved; - font->sum_bytes += (*output_len * sizeof (glyphy_rgba_t)); + font->sum_curves += glyphy_curve_accumulator_get_num_curves (font->acc); + font->sum_bytes += (*output_len * sizeof (glyphy_texel_t)); } static void @@ -202,23 +124,21 @@ _demo_font_upload_glyph (demo_font_t *font, unsigned int glyph_index, glyph_info_t *glyph_info) { - glyphy_rgba_t buffer[4096 * 16]; unsigned int output_len; - encode_ft_glyph (font, - glyph_index, - TOLERANCE, - buffer, ARRAY_LEN (buffer), - &output_len, - &glyph_info->nominal_w, - &glyph_info->nominal_h, - &glyph_info->extents, - &glyph_info->advance); + encode_glyph (font, + glyph_index, + font->scratch_buffer->data (), font->scratch_buffer->size (), + &output_len, + &glyph_info->extents, + &glyph_info->advance); + glyph_info->upem = hb_face_get_upem (font->face); glyph_info->is_empty = glyphy_extents_is_empty (&glyph_info->extents); if (!glyph_info->is_empty) - demo_atlas_alloc (font->atlas, buffer, output_len, - &glyph_info->atlas_x, &glyph_info->atlas_y); + glyph_info->atlas_offset = demo_atlas_alloc (font->atlas, + font->scratch_buffer->data (), + output_len); } void @@ -236,10 +156,14 @@ demo_font_lookup_glyph (demo_font_t *font, void demo_font_print_stats (demo_font_t *font) { - LOGI ("%3d glyphs; avg num endpoints%6.2f; avg error%5.1f%%; avg tex fetch%5.2f; avg %5.2fkb per glyph\n", + double atlas_used_kb = demo_atlas_get_used (font->atlas) * sizeof (glyphy_texel_t) / 1024.; + + if (!font->num_glyphs) + return; + + LOGI ("%3d glyphs; avg curves%6.2f; avg %5.2fkb per glyph; atlas used %5.2fkb\n", font->num_glyphs, - (double) font->sum_endpoints / font->num_glyphs, - 100. * font->sum_error / font->num_glyphs, - font->sum_fetch / font->num_glyphs, - font->sum_bytes / 1024. / font->num_glyphs); + (double) font->sum_curves / font->num_glyphs, + font->sum_bytes / 1024. / font->num_glyphs, + atlas_used_kb); } diff --git a/demo/demo-font.h b/demo/demo-font.h index 07327023..5528936c 100644 --- a/demo/demo-font.h +++ b/demo/demo-font.h @@ -1,19 +1,6 @@ /* - * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod */ #ifndef DEMO_FONT_H @@ -24,20 +11,12 @@ #include -#ifdef _WIN32 -#define DEFAULT_FONT "Calibri" -#undef near -#undef far -#endif - typedef struct { glyphy_extents_t extents; double advance; - glyphy_bool_t is_empty; /* has no outline; eg. space; don't draw it */ - unsigned int nominal_w; - unsigned int nominal_h; - unsigned int atlas_x; - unsigned int atlas_y; + glyphy_bool_t is_empty; + unsigned int upem; + unsigned int atlas_offset; } glyph_info_t; @@ -47,9 +26,6 @@ demo_font_t * demo_font_create (hb_face_t *face, demo_atlas_t *atlas); -demo_font_t * -demo_font_reference (demo_font_t *font); - void demo_font_destroy (demo_font_t *font); @@ -60,9 +36,6 @@ demo_font_get_face (demo_font_t *font); hb_font_t * demo_font_get_font (demo_font_t *font); -demo_atlas_t * -demo_font_get_atlas (demo_font_t *font); - void demo_font_lookup_glyph (demo_font_t *font, diff --git a/demo/demo-fragment.glsl b/demo/demo-fragment.glsl new file mode 100644 index 00000000..233bc347 --- /dev/null +++ b/demo/demo-fragment.glsl @@ -0,0 +1,11 @@ +in vec2 v_texcoord; +flat in uint v_glyphLoc; + +out vec4 fragColor; + +void main () +{ + float coverage = glyphy_render (v_texcoord, v_glyphLoc); + + fragColor = vec4 (0.0, 0.0, 0.0, coverage); +} diff --git a/demo/demo-fshader.glsl b/demo/demo-fshader.glsl deleted file mode 100644 index 9bb884fd..00000000 --- a/demo/demo-fshader.glsl +++ /dev/null @@ -1,85 +0,0 @@ -uniform float u_contrast; -uniform float u_gamma_adjust; -uniform float u_outline_thickness; -uniform bool u_outline; -uniform float u_boldness; -uniform bool u_debug; - -varying vec4 v_glyph; - - -#define SQRT2_2 0.70710678118654757 /* 1 / sqrt(2.) */ -#define SQRT2 1.4142135623730951 - -struct glyph_info_t { - ivec2 nominal_size; - ivec2 atlas_pos; -}; - -glyph_info_t -glyph_info_decode (vec4 v) -{ - glyph_info_t gi; - gi.nominal_size = (ivec2 (mod (v.zw, 256.)) + 2) / 4; - gi.atlas_pos = ivec2 (v_glyph.zw) / 256; - return gi; -} - - -float -antialias (float d) -{ - return smoothstep (-.75, +.75, d); -} - -void -main() -{ - vec2 p = v_glyph.xy; - glyph_info_t gi = glyph_info_decode (v_glyph); - - /* isotropic antialiasing */ - vec2 dpdx = dFdx (p); - vec2 dpdy = dFdy (p); - float m = length (vec2 (length (dpdx), length (dpdy))) * SQRT2_2; - - vec4 color = vec4 (0,0,0,1); - - float gsdist = glyphy_sdf (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS); - gsdist -= u_boldness; - float sdist = gsdist / m * u_contrast; - - if (!u_debug) { - if (u_outline) - sdist = abs (sdist) - u_outline_thickness * .5; - if (sdist > 1.) - discard; - float alpha = antialias (-sdist); - if (u_gamma_adjust != 1.) - alpha = pow (alpha, 1./u_gamma_adjust); - color = vec4 (color.rgb,color.a * alpha); - } else { - color = vec4 (0,0,0,0); - - // Color the inside of the glyph a light red - color += vec4 (.5,0,0,.5) * smoothstep (1., -1., sdist); - - float udist = abs (sdist); - float gudist = abs (gsdist); - // Color the outline red - color += vec4 (1,0,0,1) * smoothstep (2., 1., udist); - // Color the distance field in green - if (!glyphy_isinf (udist)) - color += vec4(0,.4,0,.4 - (abs(gsdist) / max(float(gi.nominal_size.x), float(gi.nominal_size.y))) * 4.); - - float pdist = glyphy_point_dist (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS); - // Color points green - color = mix (vec4 (0,1,0,.5), color, smoothstep (.05, .06, pdist)); - - glyphy_arc_list_t arc_list = glyphy_arc_list (p, gi.nominal_size GLYPHY_DEMO_EXTRA_ARGS); - // Color the number of endpoints per cell blue - color += vec4 (0,0,1,.4) * float(arc_list.num_endpoints) * 32./255.; - } - - gl_FragColor = color; -} diff --git a/demo/demo-glstate.cc b/demo/demo-glstate.cc index 459737bc..b6fc29c9 100644 --- a/demo/demo-glstate.cc +++ b/demo/demo-glstate.cc @@ -1,19 +1,6 @@ /* - * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski */ #ifdef HAVE_CONFIG_H @@ -23,52 +10,25 @@ #include "demo-glstate.h" struct demo_glstate_t { - unsigned int refcount; - GLuint program; demo_atlas_t *atlas; - - /* Uniforms */ - double u_debug; - double u_contrast; - double u_gamma_adjust; - double u_outline; - double u_outline_thickness; - double u_boldness; }; demo_glstate_t * demo_glstate_create (void) { - TRACE(); - demo_glstate_t *st = (demo_glstate_t *) calloc (1, sizeof (demo_glstate_t)); - st->refcount = 1; st->program = demo_shader_create_program (); - st->atlas = demo_atlas_create (2048, 1024, 64, 8); - - st->u_debug = false; - st->u_contrast = 1.0; - st->u_gamma_adjust = 1.0; - st->u_outline = false; - st->u_outline_thickness = 1.0; - st->u_boldness = 0.; + st->atlas = demo_atlas_create (1024 * 1024); return st; } -demo_glstate_t * -demo_glstate_reference (demo_glstate_t *st) -{ - if (st) st->refcount++; - return st; -} - void demo_glstate_destroy (demo_glstate_t *st) { - if (!st || --st->refcount) + if (!st) return; demo_atlas_destroy (st->atlas); @@ -77,18 +37,6 @@ demo_glstate_destroy (demo_glstate_t *st) free (st); } - -static void -set_uniform (GLuint program, const char *name, double *p, double value, double scale=1.0) -{ - *p = value; - glUniform1f (glGetUniformLocation (program, name), value * scale); - LOGI ("Setting %s to %g\n", name + 2, value); -} - -#define SET_UNIFORM(name, value) set_uniform (st->program, #name, &st->name, value) -#define SET_UNIFORM_SCALE(name, value, scale) set_uniform (st->program, #name, &st->name, value, scale) - void demo_glstate_setup (demo_glstate_t *st) { @@ -96,13 +44,6 @@ demo_glstate_setup (demo_glstate_t *st) demo_atlas_set_uniforms (st->atlas); - SET_UNIFORM (u_debug, st->u_debug); - SET_UNIFORM (u_contrast, st->u_contrast); - SET_UNIFORM (u_gamma_adjust, st->u_gamma_adjust); - SET_UNIFORM (u_outline, st->u_outline); - SET_UNIFORM (u_outline_thickness, st->u_outline_thickness); - SET_UNIFORM (u_boldness, st->u_boldness); - glEnable (GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } @@ -113,44 +54,13 @@ demo_glstate_get_atlas (demo_glstate_t *st) return st->atlas; } -void -demo_glstate_scale_gamma_adjust (demo_glstate_t *st, double factor) -{ - SET_UNIFORM (u_gamma_adjust, clamp (st->u_gamma_adjust * factor, .1, 10.)); -} - -void -demo_glstate_scale_contrast (demo_glstate_t *st, double factor) -{ - SET_UNIFORM (u_contrast, clamp (st->u_contrast * factor, .1, 10.)); -} - -void -demo_glstate_toggle_debug (demo_glstate_t *st) -{ - SET_UNIFORM (u_debug, 1 - st->u_debug); -} - void demo_glstate_set_matrix (demo_glstate_t *st, float mat[16]) { glUniformMatrix4fv (glGetUniformLocation (st->program, "u_matViewProjection"), 1, GL_FALSE, mat); -} -void -demo_glstate_toggle_outline (demo_glstate_t *st) -{ - SET_UNIFORM (u_outline, 1 - st->u_outline); -} - -void -demo_glstate_scale_outline_thickness (demo_glstate_t *st, double factor) -{ - SET_UNIFORM (u_outline_thickness, clamp (st->u_outline_thickness * factor, .5, 3.)); -} - -void -demo_glstate_adjust_boldness (demo_glstate_t *st, double adjustment) -{ - SET_UNIFORM_SCALE (u_boldness, clamp (st->u_boldness + adjustment, -ENLIGHTEN_MAX, EMBOLDEN_MAX), GRID_SIZE); + GLint viewport[4]; + glGetIntegerv (GL_VIEWPORT, viewport); + glUniform2f (glGetUniformLocation (st->program, "u_viewport"), + (float) viewport[2], (float) viewport[3]); } diff --git a/demo/demo-glstate.h b/demo/demo-glstate.h index 23899cda..6abdca70 100644 --- a/demo/demo-glstate.h +++ b/demo/demo-glstate.h @@ -1,19 +1,6 @@ /* - * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod */ #ifndef DEMO_GLSTATE_H @@ -30,9 +17,6 @@ typedef struct demo_glstate_t demo_glstate_t; demo_glstate_t * demo_glstate_create (void); -demo_glstate_t * -demo_glstate_reference (demo_glstate_t *st); - void demo_glstate_destroy (demo_glstate_t *st); @@ -43,26 +27,8 @@ demo_glstate_setup (demo_glstate_t *st); demo_atlas_t * demo_glstate_get_atlas (demo_glstate_t *st); -void -demo_glstate_scale_gamma_adjust (demo_glstate_t *st, double factor); - -void -demo_glstate_scale_contrast (demo_glstate_t *st, double factor); - -void -demo_glstate_toggle_debug (demo_glstate_t *st); - void demo_glstate_set_matrix (demo_glstate_t *st, float mat[16]); -void -demo_glstate_toggle_outline (demo_glstate_t *st); - -void -demo_glstate_scale_outline_thickness (demo_glstate_t *st, double factor); - -void -demo_glstate_adjust_boldness (demo_glstate_t *st, double adjustment); - #endif /* DEMO_GLSTATE_H */ diff --git a/demo/demo-shader.cc b/demo/demo-shader.cc index 9b821233..4142270e 100644 --- a/demo/demo-shader.cc +++ b/demo/demo-shader.cc @@ -1,19 +1,6 @@ /* - * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod */ #ifdef HAVE_CONFIG_H @@ -22,46 +9,9 @@ #include "demo-shader.h" -#include "demo-atlas-glsl.h" -#include "demo-vshader-glsl.h" -#include "demo-fshader-glsl.h" - - -static unsigned int -glyph_encode (unsigned int atlas_x , /* 7 bits */ - unsigned int atlas_y, /* 7 bits */ - unsigned int corner_x, /* 1 bit */ - unsigned int corner_y, /* 1 bit */ - unsigned int nominal_w, /* 6 bits */ - unsigned int nominal_h /* 6 bits */) -{ - assert (0 == (atlas_x & ~0x7F)); - assert (0 == (atlas_y & ~0x7F)); - assert (0 == (corner_x & ~1)); - assert (0 == (corner_y & ~1)); - assert (0 == (nominal_w & ~0x3F)); - assert (0 == (nominal_h & ~0x3F)); - - unsigned int x = (((atlas_x << 6) | nominal_w) << 1) | corner_x; - unsigned int y = (((atlas_y << 6) | nominal_h) << 1) | corner_y; +#include "demo-vertex-glsl.h" +#include "demo-fragment-glsl.h" - return (x << 16) | y; -} - -static void -glyph_vertex_encode (double x, double y, - unsigned int corner_x, unsigned int corner_y, - const glyph_info_t *gi, - glyph_vertex_t *v) -{ - unsigned int encoded = glyph_encode (gi->atlas_x, gi->atlas_y, - corner_x, corner_y, - gi->nominal_w, gi->nominal_h); - v->x = x; - v->y = y; - v->g16hi = encoded >> 16; - v->g16lo = encoded & 0xFFFF; -} void demo_shader_add_glyph_vertices (const glyphy_point_t &p, @@ -73,20 +23,31 @@ demo_shader_add_glyph_vertices (const glyphy_point_t &p, if (gi->is_empty) return; + /* Extents and texcoords are in font design units. + * Screen position uses font_size / upem as the scale. */ + double scale = font_size / gi->upem; + glyph_vertex_t v[4]; -#define ENCODE_CORNER(_cx, _cy) \ - do { \ - double _vx = p.x + font_size * ((1-_cx) * gi->extents.min_x + _cx * gi->extents.max_x); \ - double _vy = p.y - font_size * ((1-_cy) * gi->extents.min_y + _cy * gi->extents.max_y); \ - glyph_vertex_encode (_vx, _vy, _cx, _cy, gi, &v[_cx * 2 + _cy]); \ - } while (0) - ENCODE_CORNER (0, 0); - ENCODE_CORNER (0, 1); - ENCODE_CORNER (1, 0); - ENCODE_CORNER (1, 1); -#undef ENCODE_CORNER + for (int ci = 0; ci < 4; ci++) { + int cx = (ci >> 1) & 1; + int cy = ci & 1; + + double ex = (1 - cx) * gi->extents.min_x + cx * gi->extents.max_x; + double ey = (1 - cy) * gi->extents.min_y + cy * gi->extents.max_y; + + v[ci].x = (float) (p.x + scale * ex); + v[ci].y = (float) (p.y - scale * ey); + v[ci].tx = (float) ex; + v[ci].ty = (float) ey; + v[ci].cx = cx ? 1.f : -1.f; + v[ci].cy = cy ? 1.f : -1.f; /* top dilates up, bottom dilates down */ + v[ci].tpx = (float) (1.0 / scale); + v[ci].tpy = (float) (-1.0 / scale); + v[ci].atlas_offset = gi->atlas_offset; + } + /* Two triangles */ vertices->push_back (v[0]); vertices->push_back (v[1]); vertices->push_back (v[2]); @@ -97,23 +58,19 @@ demo_shader_add_glyph_vertices (const glyphy_point_t &p, if (extents) { glyphy_extents_clear (extents); - for (unsigned int i = 0; i < 4; i++) { - glyphy_point_t p = {v[i].x, v[i].y}; - glyphy_extents_add (extents, &p); + for (int i = 0; i < 4; i++) { + glyphy_point_t pt = {v[i].x, v[i].y}; + glyphy_extents_add (extents, &pt); } } } - - static GLuint compile_shader (GLenum type, GLsizei count, const GLchar** sources) { - TRACE(); - GLuint shader; GLint compiled; @@ -145,20 +102,18 @@ compile_shader (GLenum type, } static GLuint -link_program (GLuint vshader, - GLuint fshader) +link_program (GLuint vertex_shader, + GLuint fragment_shader) { - TRACE(); - GLuint program; GLint linked; program = glCreateProgram (); - glAttachShader (program, vshader); - glAttachShader (program, fshader); + glAttachShader (program, vertex_shader); + glAttachShader (program, fragment_shader); glLinkProgram (program); - glDeleteShader (vshader); - glDeleteShader (fshader); + glDeleteShader (vertex_shader); + glDeleteShader (fragment_shader); glGetProgramiv (program, GL_LINK_STATUS, &linked); if (!linked) { @@ -180,33 +135,23 @@ link_program (GLuint vshader, return program; } -#ifdef GL_ES_VERSION_2_0 -# define GLSL_HEADER_STRING \ - "#extension GL_OES_standard_derivatives : enable\n" \ - "precision highp float;\n" \ - "precision highp int;\n" -#else -# define GLSL_HEADER_STRING \ - "#version 110\n" -#endif - GLuint demo_shader_create_program (void) { - TRACE(); - - GLuint vshader, fshader, program; - const GLchar *vshader_sources[] = {GLSL_HEADER_STRING, - demo_vshader_glsl}; - vshader = compile_shader (GL_VERTEX_SHADER, ARRAY_LEN (vshader_sources), vshader_sources); - const GLchar *fshader_sources[] = {GLSL_HEADER_STRING, - demo_atlas_glsl, - glyphy_common_shader_source (), - "#define GLYPHY_SDF_PSEUDO_DISTANCE 1\n", - glyphy_sdf_shader_source (), - demo_fshader_glsl}; - fshader = compile_shader (GL_FRAGMENT_SHADER, ARRAY_LEN (fshader_sources), fshader_sources); - - program = link_program (vshader, fshader); + GLuint vertex_shader, fragment_shader, program; + const GLchar *vertex_shader_sources[] = {"#version 330\n", + glyphy_vertex_shader_source (), + demo_vertex_glsl}; + vertex_shader = compile_shader (GL_VERTEX_SHADER, + ARRAY_LEN (vertex_shader_sources), + vertex_shader_sources); + const GLchar *fragment_shader_sources[] = {"#version 330\n", + glyphy_fragment_shader_source (), + demo_fragment_glsl}; + fragment_shader = compile_shader (GL_FRAGMENT_SHADER, + ARRAY_LEN (fragment_shader_sources), + fragment_shader_sources); + + program = link_program (vertex_shader, fragment_shader); return program; } diff --git a/demo/demo-shader.h b/demo/demo-shader.h index dfa54809..a84b4e7e 100644 --- a/demo/demo-shader.h +++ b/demo/demo-shader.h @@ -1,19 +1,6 @@ /* - * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod */ #ifndef DEMO_SHADERS_H @@ -24,12 +11,21 @@ struct glyph_vertex_t { - /* Position */ + /* Object-space position */ GLfloat x; GLfloat y; - /* Glyph info */ - GLfloat g16hi; - GLfloat g16lo; + /* Em-space texture coordinates */ + GLfloat tx; + GLfloat ty; + /* Corner direction for dilation (-1 or +1 per axis) */ + GLfloat cx; + GLfloat cy; + /* Texcoord-per-position ratio (constant across glyph) */ + GLfloat tpx; + GLfloat tpy; + /* Atlas offset (constant across glyph) */ + GLuint atlas_offset; + GLuint _padding; }; void diff --git a/demo/demo-vertex.glsl b/demo/demo-vertex.glsl new file mode 100644 index 00000000..01d16f8d --- /dev/null +++ b/demo/demo-vertex.glsl @@ -0,0 +1,24 @@ +uniform mat4 u_matViewProjection; +uniform vec2 u_viewport; + +in vec2 a_position; +in vec2 a_texcoord; +in vec2 a_corner; +in vec2 a_texPerPos; +in uint a_glyphLoc; + +out vec2 v_texcoord; +flat out uint v_glyphLoc; + +void main () +{ + vec2 pos = a_position; + vec2 tex = a_texcoord; + + glyphy_dilate (pos, tex, a_corner, a_texPerPos, + u_matViewProjection, u_viewport); + + gl_Position = u_matViewProjection * vec4 (pos, 0.0, 1.0); + v_texcoord = tex; + v_glyphLoc = a_glyphLoc; +} diff --git a/demo/demo-view.cc b/demo/demo-view.cc index e4684306..9c51a190 100644 --- a/demo/demo-view.cc +++ b/demo/demo-view.cc @@ -1,19 +1,8 @@ /* + * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski */ #ifdef HAVE_CONFIG_H @@ -32,9 +21,8 @@ extern "C" { #endif struct demo_view_t { - unsigned int refcount; - demo_glstate_t *st; + GLFWwindow *window; /* Output */ GLint vsync; @@ -46,6 +34,7 @@ struct demo_view_t { int modifiers; bool dragged; bool click_handled; + double cursorx, cursory; double beginx, beginy; double lastx, lasty, lastt; double dx,dy, dt; @@ -59,12 +48,17 @@ struct demo_view_t { /* Animation */ float rot_axis[3]; - float rot_speed; + double rot_speed; bool animate; int num_frames; - long fps_start_time; - long last_frame_time; + double fps_start_time; + double last_frame_time; bool has_fps_timer; + double fps_timer_interval; + double fps_timer_last; + + /* Dirty flag for redraw */ + bool needs_redraw; /* Window geometry just before going fullscreen */ int x; @@ -73,46 +67,30 @@ struct demo_view_t { int height; }; -demo_view_t *static_vu; - demo_view_t * -demo_view_create (demo_glstate_t *st) +demo_view_create (demo_glstate_t *st, GLFWwindow *window) { - TRACE(); - demo_view_t *vu = (demo_view_t *) calloc (1, sizeof (demo_view_t)); - vu->refcount = 1; vu->st = st; + vu->window = window; + vu->needs_redraw = true; demo_view_reset (vu); - assert (!static_vu); - static_vu = vu; - - return vu; -} - -demo_view_t * -demo_view_reference (demo_view_t *vu) -{ - if (vu) vu->refcount++; return vu; } void demo_view_destroy (demo_view_t *vu) { - if (!vu || --vu->refcount) + if (!vu) return; - assert (static_vu == vu); - static_vu = NULL; - free (vu); } -#define ANIMATION_SPEED 1. /* Default speed, in radians second. */ +#define ANIMATION_SPEED 1. /* Default speed, in radians per second. */ void demo_view_reset (demo_view_t *vu) { @@ -121,47 +99,17 @@ demo_view_reset (demo_view_t *vu) vu->translate.x = vu->translate.y = 0; trackball (vu->quat , 0.0, 0.0, 0.0, 0.0); vset (vu->rot_axis, 0., 0., 1.); - vu->rot_speed = ANIMATION_SPEED / 1000.; + vu->rot_speed = ANIMATION_SPEED; + vu->needs_redraw = true; } -static void -demo_view_scale_gamma_adjust (demo_view_t *vu, double factor) -{ - demo_glstate_scale_gamma_adjust (vu->st, factor); -} - -static void -demo_view_scale_contrast (demo_view_t *vu, double factor) -{ - demo_glstate_scale_contrast (vu->st, factor); -} - static void demo_view_scale_perspective (demo_view_t *vu, double factor) { vu->perspective = clamp (vu->perspective * factor, .01, 100.); } -static void -demo_view_toggle_outline (demo_view_t *vu) -{ - demo_glstate_toggle_outline (vu->st); -} - -static void -demo_view_scale_outline_thickness (demo_view_t *vu, double factor) -{ - demo_glstate_scale_outline_thickness (vu->st, factor); -} - - -static void -demo_view_adjust_boldness (demo_view_t *vu, double factor) -{ - demo_glstate_adjust_boldness (vu->st, factor); -} - static void demo_view_scalex (demo_view_t *vu, double factor) { @@ -221,52 +169,11 @@ demo_view_apply_transform (demo_view_t *vu, float *mat) } -/* return current time in milli-seconds */ -static long +/* return current time in seconds */ +static double current_time (void) { - return glutGet (GLUT_ELAPSED_TIME); -} - -static void -next_frame (demo_view_t *vu) -{ - glutPostRedisplay (); -} - -static void -timed_step (int ms) -{ - demo_view_t *vu = static_vu; - if (vu->animate) { - glutTimerFunc (ms, timed_step, ms); - next_frame (vu); - } -} - -static void -idle_step (void) -{ - demo_view_t *vu = static_vu; - if (vu->animate) { - next_frame (vu); - } - else - glutIdleFunc (NULL); -} - -static void -print_fps (int ms) -{ - demo_view_t *vu = static_vu; - if (vu->animate) { - glutTimerFunc (ms, print_fps, ms); - long t = current_time (); - LOGI ("%gfps\n", vu->num_frames * 1000. / (t - vu->fps_start_time)); - vu->num_frames = 0; - vu->fps_start_time = t; - } else - vu->has_fps_timer = false; + return glfwGetTime (); } static void @@ -274,18 +181,16 @@ start_animation (demo_view_t *vu) { vu->num_frames = 0; vu->last_frame_time = vu->fps_start_time = current_time (); - //glutTimerFunc (1000/60, timed_step, 1000/60); - glutIdleFunc (idle_step); - if (!vu->has_fps_timer) { - vu->has_fps_timer = true; - glutTimerFunc (5000, print_fps, 5000); - } + vu->fps_timer_interval = 5.0; + vu->fps_timer_last = current_time (); + vu->has_fps_timer = true; } static void demo_view_toggle_animation (demo_view_t *vu) { vu->animate = !vu->animate; + LOGI ("Setting animation %s.\n", vu->animate ? "on" : "off"); if (vu->animate) start_animation (vu); } @@ -294,41 +199,41 @@ demo_view_toggle_animation (demo_view_t *vu) static void demo_view_toggle_vsync (demo_view_t *vu) { - vu->vsync = !vu->vsync; + GLint vsync = !vu->vsync; + glfwSwapInterval (vsync); + vu->vsync = vsync; LOGI ("Setting vsync %s.\n", vu->vsync ? "on" : "off"); -#if defined(__APPLE__) - CGLSetParameter(CGLGetCurrentContext(), kCGLCPSwapInterval, &vu->vsync); -#elif defined(__WGLEW__) - if (wglewIsSupported ("WGL_EXT_swap_control")) - wglSwapIntervalEXT (vu->vsync); - else - LOGW ("WGL_EXT_swal_control not supported; failed to set vsync\n"); -#elif defined(__GLXEW_H__) - if (glxewIsSupported ("GLX_SGI_swap_control")) - glXSwapIntervalSGI (vu->vsync); - else - LOGW ("GLX_SGI_swap_control not supported; failed to set vsync\n"); -#else - LOGW ("No vsync extension found; failed to set vsync\n"); -#endif } static void demo_view_toggle_srgb (demo_view_t *vu) { - vu->srgb = !vu->srgb; - LOGI ("Setting sRGB framebuffer %s.\n", vu->srgb ? "on" : "off"); -#if defined(GL_FRAMEBUFFER_SRGB) && defined(GL_FRAMEBUFFER_SRGB_CAPABLE_EXT) - GLboolean available = false; - if ((glewIsSupported ("GL_ARB_framebuffer_sRGB") || glewIsSupported ("GL_EXT_framebuffer_sRGB")) && - (glGetBooleanv (GL_FRAMEBUFFER_SRGB_CAPABLE_EXT, &available), available)) { + glyphy_bool_t srgb = !vu->srgb; +#if defined(GL_FRAMEBUFFER_SRGB) + while (glGetError () != GL_NO_ERROR) + ; + + if (srgb) + glEnable (GL_FRAMEBUFFER_SRGB); + else + glDisable (GL_FRAMEBUFFER_SRGB); + + if (glGetError () == GL_NO_ERROR) { + vu->srgb = srgb; + LOGI ("Setting sRGB framebuffer %s.\n", vu->srgb ? "on" : "off"); + } else { + /* Restore the previous state if the driver rejected the toggle. */ if (vu->srgb) glEnable (GL_FRAMEBUFFER_SRGB); else glDisable (GL_FRAMEBUFFER_SRGB); - } else + while (glGetError () != GL_NO_ERROR) + ; + LOGW ("Failed to set sRGB framebuffer state\n"); + } +#else + LOGW ("No sRGB framebuffer extension found; failed to set sRGB framebuffer\n"); #endif - LOGW ("No sRGB framebuffer extension found; failed to set sRGB framebuffer\n"); } static void @@ -336,91 +241,30 @@ demo_view_toggle_fullscreen (demo_view_t *vu) { vu->fullscreen = !vu->fullscreen; if (vu->fullscreen) { - vu->x = glutGet (GLUT_WINDOW_X); - vu->y = glutGet (GLUT_WINDOW_Y); - vu->width = glutGet (GLUT_WINDOW_WIDTH); - vu->height = glutGet (GLUT_WINDOW_HEIGHT); - glutFullScreen (); + glfwGetWindowPos (vu->window, &vu->x, &vu->y); + glfwGetWindowSize (vu->window, &vu->width, &vu->height); + GLFWmonitor *monitor = glfwGetPrimaryMonitor (); + const GLFWvidmode *mode = glfwGetVideoMode (monitor); + glfwSetWindowMonitor (vu->window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate); } else { - glutReshapeWindow (vu->width, vu->height); - glutPositionWindow (vu->x, vu->y); + glfwSetWindowMonitor (vu->window, NULL, vu->x, vu->y, vu->width, vu->height, 0); } } -static void -demo_view_toggle_debug (demo_view_t *vu) -{ - demo_glstate_toggle_debug (vu->st); -} void demo_view_reshape_func (demo_view_t *vu, int width, int height) { - glViewport (0, 0, width, height); - glutPostRedisplay (); + vu->needs_redraw = true; } #define STEP 1.05 void -demo_view_keyboard_func (demo_view_t *vu, unsigned char key, int x, int y) +demo_view_char_func (demo_view_t *vu, unsigned int codepoint) { - switch (key) + switch (codepoint) { - case '\033': - case 'q': - exit (0); - break; - - case ' ': - demo_view_toggle_animation (vu); - break; - case 'v': - demo_view_toggle_vsync (vu); - break; - - case 'f': - demo_view_toggle_fullscreen (vu); - break; - - case 'd': - demo_view_toggle_debug (vu); - break; - - case 'o': - demo_view_toggle_outline (vu); - break; - case 'p': - demo_view_scale_outline_thickness (vu, STEP); - break; - case 'i': - demo_view_scale_outline_thickness (vu, 1. / STEP); - break; - - case '0': - demo_view_adjust_boldness (vu, +.002); - break; - case '9': - demo_view_adjust_boldness (vu, -.002); - break; - - - case 'a': - demo_view_scale_contrast (vu, STEP); - break; - case 'z': - demo_view_scale_contrast (vu, 1. / STEP); - break; - case 'g': - demo_view_scale_gamma_adjust (vu, STEP); - break; - case 'b': - demo_view_scale_gamma_adjust (vu, 1. / STEP); - break; - case 'c': - demo_view_toggle_srgb (vu); - break; - case '=': demo_view_scale (vu, STEP, STEP); break; @@ -455,61 +299,89 @@ demo_view_keyboard_func (demo_view_t *vu, unsigned char key, int x, int y) demo_view_translate (vu, -.1, 0); break; - case 'r': - demo_view_reset (vu); - break; - default: return; } - glutPostRedisplay (); + vu->needs_redraw = true; } void -demo_view_special_func (demo_view_t *vu, int key, int x, int y) +demo_view_key_func (demo_view_t *vu, int key, int scancode, int action, int mods) { + if (action != GLFW_PRESS) + return; + switch (key) { - case GLUT_KEY_UP: + case GLFW_KEY_ESCAPE: + case GLFW_KEY_Q: + glfwSetWindowShouldClose (vu->window, GLFW_TRUE); + break; + + case GLFW_KEY_SPACE: + demo_view_toggle_animation (vu); + break; + case GLFW_KEY_SLASH: + if (mods & GLFW_MOD_SHIFT) + demo_view_print_help (vu); + break; + case GLFW_KEY_S: + demo_view_toggle_srgb (vu); + break; + case GLFW_KEY_V: + demo_view_toggle_vsync (vu); + break; + + case GLFW_KEY_F: + demo_view_toggle_fullscreen (vu); + break; + + case GLFW_KEY_R: + demo_view_reset (vu); + break; + + case GLFW_KEY_UP: demo_view_translate (vu, 0, -.1); break; - case GLUT_KEY_DOWN: + case GLFW_KEY_DOWN: demo_view_translate (vu, 0, +.1); break; - case GLUT_KEY_LEFT: + case GLFW_KEY_LEFT: demo_view_translate (vu, +.1, 0); break; - case GLUT_KEY_RIGHT: + case GLFW_KEY_RIGHT: demo_view_translate (vu, -.1, 0); break; default: return; } - glutPostRedisplay (); + vu->needs_redraw = true; } void -demo_view_mouse_func (demo_view_t *vu, int button, int state, int x, int y) +demo_view_mouse_func (demo_view_t *vu, int button, int action, int mods) { - if (state == GLUT_DOWN) { + if (action == GLFW_PRESS) { vu->buttons |= (1 << button); vu->click_handled = false; } else vu->buttons &= ~(1 << button); - vu->modifiers = glutGetModifiers (); + vu->modifiers = mods; + + double x = vu->cursorx, y = vu->cursory; switch (button) { - case GLUT_RIGHT_BUTTON: - switch (state) { - case GLUT_DOWN: + case GLFW_MOUSE_BUTTON_RIGHT: + switch (action) { + case GLFW_PRESS: if (vu->animate) { demo_view_toggle_animation (vu); vu->click_handled = true; } break; - case GLUT_UP: + case GLFW_RELEASE: if (!vu->animate) { if (!vu->dragged && !vu->click_handled) @@ -524,55 +396,53 @@ demo_view_mouse_func (demo_view_t *vu, int button, int state, int x, int y) break; } break; - -#if !defined(GLUT_WHEEL_UP) -#define GLUT_WHEEL_UP 3 -#define GLUT_WHEEL_DOWN 4 -#endif - - case GLUT_WHEEL_UP: - demo_view_scale (vu, STEP, STEP); - break; - - case GLUT_WHEEL_DOWN: - demo_view_scale (vu, 1. / STEP, 1. / STEP); - break; } vu->beginx = vu->lastx = x; vu->beginy = vu->lasty = y; vu->dragged = false; - glutPostRedisplay (); + vu->needs_redraw = true; +} + +void +demo_view_scroll_func (demo_view_t *vu, double xoffset, double yoffset) +{ + if (yoffset > 0) + demo_view_scale (vu, STEP, STEP); + else if (yoffset < 0) + demo_view_scale (vu, 1. / STEP, 1. / STEP); + vu->needs_redraw = true; } void -demo_view_motion_func (demo_view_t *vu, int x, int y) +demo_view_motion_func (demo_view_t *vu, double x, double y) { + vu->cursorx = x; + vu->cursory = y; + + if (!vu->buttons) + return; + vu->dragged = true; - int viewport[4]; - glGetIntegerv (GL_VIEWPORT, viewport); - GLuint width = viewport[2]; - GLuint height = viewport[3]; + /* Use window size, not framebuffer size, since GLFW cursor + * positions are in screen coordinates. */ + int width, height; + glfwGetWindowSize (vu->window, &width, &height); - if (vu->buttons & (1 << GLUT_LEFT_BUTTON)) + if (vu->buttons & (1 << GLFW_MOUSE_BUTTON_LEFT)) { - if (vu->modifiers & GLUT_ACTIVE_SHIFT) { - /* adjust contrast/gamma */ - demo_view_scale_gamma_adjust (vu, 1 - ((y - vu->lasty) / height)); - demo_view_scale_contrast (vu, 1 + ((x - vu->lastx) / width)); - } else { - /* translate */ + { demo_view_translate (vu, +2 * (x - vu->lastx) / width, -2 * (y - vu->lasty) / height); } } - if (vu->buttons & (1 << GLUT_RIGHT_BUTTON)) + if (vu->buttons & (1 << GLFW_MOUSE_BUTTON_RIGHT)) { - if (vu->modifiers & GLUT_ACTIVE_SHIFT) { + if (vu->modifiers & GLFW_MOD_SHIFT) { /* adjust perspective */ demo_view_scale_perspective (vu, 1 - ((y - vu->lasty) / height) * 5); } else { @@ -598,7 +468,7 @@ demo_view_motion_func (demo_view_t *vu, int x, int y) } } - if (vu->buttons & (1 << GLUT_MIDDLE_BUTTON)) + if (vu->buttons & (1 << GLFW_MOUSE_BUTTON_MIDDLE)) { /* scale */ double factor = 1 - ((y - vu->lasty) / height) * 5; @@ -613,18 +483,41 @@ demo_view_motion_func (demo_view_t *vu, int x, int y) vu->lasty = y; vu->lastt = current_time (); - glutPostRedisplay (); + vu->needs_redraw = true; } void demo_view_print_help (demo_view_t *vu) { - LOGI ("Welcome to GLyphy demo\n"); + (void) vu; + + LOGI ("GLyphy demo controls\n"); + LOGI ("Keyboard:\n"); + LOGI (" Esc, q Quit\n"); + LOGI (" Space Toggle animation\n"); + LOGI (" f Toggle fullscreen\n"); + LOGI (" s Toggle sRGB framebuffer\n"); + LOGI (" v Toggle vsync\n"); + LOGI (" =, - Zoom in or out\n"); + LOGI (" [, ] Stretch or shrink horizontally\n"); + LOGI (" {, } Stretch or shrink vertically\n"); + LOGI (" h j k l Pan\n"); + LOGI (" Arrow keys Pan\n"); + LOGI (" r Reset view\n"); + LOGI ("Mouse:\n"); + LOGI (" Left drag Pan\n"); + LOGI (" Middle drag Zoom\n"); + LOGI (" Wheel Zoom\n"); + LOGI (" Right drag Rotate\n"); + LOGI (" Shift + right drag Adjust perspective\n"); + LOGI (" Right drag and release Animate rotation\n"); + LOGI (" Right click Toggle animation\n"); + LOGI ("\n"); } static void -advance_frame (demo_view_t *vu, long dtime) +advance_frame (demo_view_t *vu, double dtime) { if (vu->animate) { float dquat[4]; @@ -637,14 +530,24 @@ advance_frame (demo_view_t *vu, long dtime) void demo_view_display (demo_view_t *vu, demo_buffer_t *buffer) { - long new_time = current_time (); + double new_time = current_time (); advance_frame (vu, new_time - vu->last_frame_time); vu->last_frame_time = new_time; - int viewport[4]; - glGetIntegerv (GL_VIEWPORT, viewport); - GLint width = viewport[2]; - GLint height = viewport[3]; + /* FPS reporting */ + if (vu->animate && vu->has_fps_timer) { + double now = current_time (); + if (now - vu->fps_timer_last >= vu->fps_timer_interval) { + LOGI ("%gfps\n", vu->num_frames / (now - vu->fps_start_time)); + vu->num_frames = 0; + vu->fps_start_time = now; + vu->fps_timer_last = now; + } + } + + int width, height; + glfwGetFramebufferSize (vu->window, &width, &height); + glViewport (0, 0, width, height); float mat[16]; @@ -671,7 +574,8 @@ demo_view_display (demo_view_t *vu, demo_buffer_t *buffer) demo_buffer_draw (buffer); - glutSwapBuffers (); + glfwSwapBuffers (vu->window); + vu->needs_redraw = false; } void @@ -683,3 +587,9 @@ demo_view_setup (demo_view_t *vu) demo_view_toggle_srgb (vu); demo_glstate_setup (vu->st); } + +bool +demo_view_should_redraw (demo_view_t *vu) +{ + return vu->needs_redraw || vu->animate; +} diff --git a/demo/demo-view.h b/demo/demo-view.h index 9619361d..c09791ae 100644 --- a/demo/demo-view.h +++ b/demo/demo-view.h @@ -1,18 +1,6 @@ /* * Copyright 2012 Google, Inc. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * * Google Author(s): Behdad Esfahbod */ @@ -26,10 +14,7 @@ typedef struct demo_view_t demo_view_t; demo_view_t * -demo_view_create (demo_glstate_t *st); - -demo_view_t * -demo_view_reference (demo_view_t *vu); +demo_view_create (demo_glstate_t *st, GLFWwindow *window); void demo_view_destroy (demo_view_t *vu); @@ -42,16 +27,19 @@ void demo_view_reshape_func (demo_view_t *vu, int width, int height); void -demo_view_keyboard_func (demo_view_t *vu, unsigned char key, int x, int y); +demo_view_key_func (demo_view_t *vu, int key, int scancode, int action, int mods); + +void +demo_view_char_func (demo_view_t *vu, unsigned int codepoint); void -demo_view_special_func (demo_view_t *view, int key, int x, int y); +demo_view_mouse_func (demo_view_t *vu, int button, int action, int mods); void -demo_view_mouse_func (demo_view_t *vu, int button, int state, int x, int y); +demo_view_scroll_func (demo_view_t *vu, double xoffset, double yoffset); void -demo_view_motion_func (demo_view_t *vu, int x, int y); +demo_view_motion_func (demo_view_t *vu, double x, double y); void demo_view_print_help (demo_view_t *vu); @@ -62,5 +50,8 @@ demo_view_display (demo_view_t *vu, demo_buffer_t *buffer); void demo_view_setup (demo_view_t *vu); +bool +demo_view_should_redraw (demo_view_t *vu); + #endif /* DEMO_VIEW_H */ diff --git a/demo/demo-vshader.glsl b/demo/demo-vshader.glsl deleted file mode 100644 index 90ac4cf6..00000000 --- a/demo/demo-vshader.glsl +++ /dev/null @@ -1,22 +0,0 @@ -uniform mat4 u_matViewProjection; - -attribute vec4 a_glyph_vertex; - -varying vec4 v_glyph; - -vec4 -glyph_vertex_transcode (vec2 v) -{ - ivec2 g = ivec2 (v); - ivec2 corner = ivec2 (mod (v, 2.)); - g /= 2; - ivec2 nominal_size = ivec2 (mod (vec2(g), 64.)); - return vec4 (corner * nominal_size, g * 4); -} - -void -main() -{ - gl_Position = u_matViewProjection * vec4 (a_glyph_vertex.xy, 0, 1); - v_glyph = glyph_vertex_transcode (a_glyph_vertex.zw); -} diff --git a/demo/glyphy-demo.cc b/demo/glyphy-demo.cc index 2ecd3e53..af471ea2 100644 --- a/demo/glyphy-demo.cc +++ b/demo/glyphy-demo.cc @@ -1,18 +1,6 @@ /* * Copyright 2012 Google, Inc. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski */ @@ -126,39 +114,39 @@ static int getopt(int argc, char *argv[], char *opts) #endif static void -reshape_func (int width, int height) +framebuffer_size_func (GLFWwindow *window, int width, int height) { demo_view_reshape_func (vu, width, height); } static void -keyboard_func (unsigned char key, int x, int y) +key_func (GLFWwindow *window, int key, int scancode, int action, int mods) { - demo_view_keyboard_func (vu, key, x, y); + demo_view_key_func (vu, key, scancode, action, mods); } static void -special_func (int key, int x, int y) +char_func (GLFWwindow *window, unsigned int codepoint) { - demo_view_special_func (vu, key, x, y); + demo_view_char_func (vu, codepoint); } static void -mouse_func (int button, int state, int x, int y) +mouse_func (GLFWwindow *window, int button, int action, int mods) { - demo_view_mouse_func (vu, button, state, x, y); + demo_view_mouse_func (vu, button, action, mods); } static void -motion_func (int x, int y) +scroll_func (GLFWwindow *window, double xoffset, double yoffset) { - demo_view_motion_func (vu, x, y); + demo_view_scroll_func (vu, xoffset, yoffset); } static void -display_func (void) +cursor_func (GLFWwindow *window, double x, double y) { - demo_view_display (vu, buffer); + demo_view_motion_func (vu, x, y); } static void @@ -179,14 +167,95 @@ show_usage(const char *path) " -f fontfile the font file (e.g. /Library/Fonts/Microsoft/Verdana.ttf)\n" "\n", name, name); + demo_view_print_help (NULL); + free(p); } +static bool +contains_case_insensitive (const char *haystack, const char *needle) +{ + if (!haystack || !needle) + return false; + + size_t needle_len = strlen (needle); + if (!needle_len) + return true; + + for (const char *h = haystack; *h; h++) { + size_t i = 0; + while (i < needle_len && + h[i] && + tolower ((unsigned char) h[i]) == tolower ((unsigned char) needle[i])) + i++; + if (i == needle_len) + return true; + } + + return false; +} + +static bool +running_on_gnome_desktop (void) +{ + const char *desktops[] = { + getenv ("XDG_CURRENT_DESKTOP"), + getenv ("XDG_SESSION_DESKTOP"), + getenv ("DESKTOP_SESSION"), + }; + + for (unsigned int i = 0; i < ARRAY_LEN (desktops); i++) { + if (contains_case_insensitive (desktops[i], "gnome")) + return true; + } + + return false; +} + +static bool +using_mesa_gl (void) +{ + const char *strings[] = { + (const char *) glGetString (GL_VENDOR), + (const char *) glGetString (GL_RENDERER), + (const char *) glGetString (GL_VERSION), + }; + + for (unsigned int i = 0; i < ARRAY_LEN (strings); i++) { + if (contains_case_insensitive (strings[i], "mesa")) + return true; + } + + return false; +} + +static void +warn_about_vsync_override (void) +{ + const char *vblank_mode = getenv ("vblank_mode"); + if (vblank_mode) { + LOGW ("Using vblank_mode=%s from the environment.\n", vblank_mode); + return; + } + + if (!running_on_gnome_desktop () || !using_mesa_gl ()) + return; + + LOGW ("GNOME/Mesa detected with vblank_mode unset.\n"); + LOGW ("If toggling vsync with `v` has no effect, restart with `vblank_mode=0`.\n"); +} + +static void +glfw_error_callback (int error, const char *description) +{ + LOGW ("GLFW error %d: %s\n", error, description); +} + int main (int argc, char** argv) { /* Process received parameters */ -# include "default-text.h" +# include "default-text.h" const char *text = NULL; const char *font_path = NULL; char arg; @@ -223,27 +292,54 @@ main (int argc, char** argv) return 1; } - /* Setup glut */ - glutInit (&argc, argv); - glutInitWindowSize (WINDOW_W, WINDOW_H); - glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB); - int window = glutCreateWindow ("GLyphy Demo"); - glutReshapeFunc (reshape_func); - glutDisplayFunc (display_func); - glutKeyboardFunc (keyboard_func); - glutSpecialFunc (special_func); - glutMouseFunc (mouse_func); - glutMotionFunc (motion_func); - - /* Setup glew */ - if (GLEW_OK != glewInit ()) - die ("Failed to initialize GL; something really broken"); - if (!glewIsSupported ("GL_VERSION_2_0")) - die ("OpenGL 2.0 not supported"); + /* Setup GLFW */ + glfwSetErrorCallback (glfw_error_callback); + if (!glfwInit ()) + die ("Failed to initialize GLFW"); + + glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); +#ifdef __APPLE__ + glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); +#endif + glfwWindowHint (GLFW_SRGB_CAPABLE, GLFW_TRUE); + + GLFWwindow *window = glfwCreateWindow (WINDOW_W, WINDOW_H, "GLyphy Demo", NULL, NULL); + if (!window) { + glfwTerminate (); + die ("Failed to create GLFW window"); + } + glfwMakeContextCurrent (window); + + glfwSetFramebufferSizeCallback (window, framebuffer_size_func); + glfwSetKeyCallback (window, key_func); + glfwSetCharCallback (window, char_func); + glfwSetMouseButtonCallback (window, mouse_func); + glfwSetScrollCallback (window, scroll_func); + glfwSetCursorPosCallback (window, cursor_func); + + /* Setup GLEW */ + glewExperimental = GL_TRUE; + glewInit (); + /* glewInit() generates GL_INVALID_ENUM in core profile; clear it. */ + while (glGetError () != GL_NO_ERROR) + ; + if (!glewIsSupported ("GL_VERSION_3_3")) + die ("OpenGL 3.3 not supported"); + + /* Set initial viewport from framebuffer size (may differ from window + * size on HiDPI displays). */ + { + int fb_width, fb_height; + glfwGetFramebufferSize (window, &fb_width, &fb_height); + glViewport (0, 0, fb_width, fb_height); + } st = demo_glstate_create (); - vu = demo_view_create (st); + vu = demo_view_create (st, window); demo_view_print_help (vu); + warn_about_vsync_override (); hb_blob_t *blob = NULL; if (font_path) @@ -274,7 +370,26 @@ main (int argc, char** argv) demo_font_print_stats (font); demo_view_setup (vu); - glutMainLoop (); + + /* Render initial frame, then + * process events so the Wayland compositor + * can configure the surface at the correct + * content scale, then render a + * second frame at the right resolution. */ + demo_view_display (vu, buffer); + glfwPollEvents (); + demo_view_display (vu, buffer); + + /* Main loop */ + while (!glfwWindowShouldClose (window)) + { + glfwPollEvents (); + + if (demo_view_should_redraw (vu)) + demo_view_display (vu, buffer); + else + glfwWaitEvents (); + } demo_buffer_destroy (buffer); demo_font_destroy (font); @@ -285,7 +400,8 @@ main (int argc, char** argv) demo_view_destroy (vu); demo_glstate_destroy (st); - glutDestroyWindow (window); + glfwDestroyWindow (window); + glfwTerminate (); return 0; } diff --git a/demo/glyphy-validate.cc b/demo/glyphy-validate.cc deleted file mode 100644 index b89bed61..00000000 --- a/demo/glyphy-validate.cc +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2012 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include -#include -#include - -#define TOLERANCE (1./2048) - -#include - -#include - -using namespace std; - -static inline void -die (const char *msg) -{ - fprintf (stderr, "%s\n", msg); - exit (1); -} - -static glyphy_bool_t -accumulate_endpoint (glyphy_arc_endpoint_t *endpoint, - vector *endpoints) -{ - endpoints->push_back (*endpoint); - return true; -} - -int -main (int argc, char** argv) -{ - bool verbose = false; - - if (argc > 1 && 0 == strcmp (argv[1], "--verbose")) { - verbose = true; - argc--; - argv++; - } - - if (argc == 1) { - fprintf (stderr, "Usage: %s FONT_FILE...\n", argv[0]); - exit (1); - } - - FT_Library ft_library; - FT_Init_FreeType (&ft_library); - - glyphy_arc_accumulator_t *acc = glyphy_arc_accumulator_create (); - - for (unsigned int arg = 1; (int) arg < argc; arg++) - { - const char *font_path = argv[arg]; - - unsigned int num_faces = 1; - for (unsigned int face_index = 0; face_index < num_faces; face_index++) - { - FT_Face ft_face = NULL; - FT_New_Face (ft_library, font_path, face_index, &ft_face); - if (!ft_face) - die ("Failed to open font file"); - /* FreeType's absurd. You have to open a ft_face to get the number of - * faces in the font file. */ - num_faces = ft_face->num_faces; - printf ("Opened %s face index %d. Has %d glyphs\n", - font_path, face_index, (int) ft_face->num_glyphs); - - for (unsigned int glyph_index = 0; glyph_index < ft_face->num_glyphs; glyph_index++) - { - char glyph_name[30]; - if (FT_Get_Glyph_Name (ft_face, glyph_index, glyph_name, sizeof (glyph_name))) - sprintf (glyph_name, "gid%u", glyph_index); - - printf ("Processing glyph %d (%s)\n", glyph_index, glyph_name); - - if (FT_Err_Ok != FT_Load_Glyph (ft_face, - glyph_index, - FT_LOAD_NO_BITMAP | - FT_LOAD_NO_HINTING | - FT_LOAD_NO_AUTOHINT | - FT_LOAD_NO_SCALE | - FT_LOAD_LINEAR_DESIGN | - FT_LOAD_IGNORE_TRANSFORM)) - die ("Failed loading FreeType glyph"); - - if (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE) - die ("FreeType loaded glyph format is not outline"); - - unsigned int upem = ft_face->units_per_EM; - double tolerance = upem * TOLERANCE; /* in font design units */ - vector endpoints; - - glyphy_arc_accumulator_reset (acc); - glyphy_arc_accumulator_set_tolerance (acc, tolerance); - glyphy_arc_accumulator_set_callback (acc, - (glyphy_arc_endpoint_accumulator_callback_t) accumulate_endpoint, - &endpoints); - - if (FT_Err_Ok != glyphy_freetype(outline_decompose) (&ft_face->glyph->outline, acc)) - die ("Failed converting glyph outline to arcs"); - - if (verbose) { - printf ("Arc list has %d endpoints\n", (int) endpoints.size ()); - for (unsigned int i = 0; i < endpoints.size (); i++) - printf ("Endpoint %d: p=(%g,%g),d=%g\n", i, endpoints[i].p.x, endpoints[i].p.y, endpoints[i].d); - } - - assert (glyphy_arc_accumulator_get_error (acc) <= tolerance); - -#if 0 - if (ft_face->glyph->outline.flags & FT_OUTLINE_EVEN_ODD_FILL) - glyphy_outline_winding_from_even_odd (&endpoints[0], endpoints.size (), false); -#endif - if (ft_face->glyph->outline.flags & FT_OUTLINE_REVERSE_FILL) - glyphy_outline_reverse (&endpoints[0], endpoints.size ()); - - if (glyphy_outline_winding_from_even_odd (&endpoints[0], endpoints.size (), false)) - { - fprintf (stderr, "ERROR: %s:%d: Glyph %d (%s) has contours with wrong direction\n", - font_path, face_index, glyph_index, glyph_name); - } - } - - FT_Done_Face (ft_face); - } - } - - glyphy_arc_accumulator_destroy (acc); - - FT_Done_FreeType (ft_library); - - return 0; -} diff --git a/demo/meson.build b/demo/meson.build index 47f0d0b5..f27509e3 100644 --- a/demo/meson.build +++ b/demo/meson.build @@ -20,9 +20,8 @@ demo_sources = [ ] demo_shaders = [ - [ 'demo-atlas.glsl', 'demo-atlas-glsl.h', 'demo_atlas_glsl' ], - [ 'demo-fshader.glsl', 'demo-fshader-glsl.h', 'demo_fshader_glsl' ], - [ 'demo-vshader.glsl', 'demo-vshader-glsl.h', 'demo_vshader_glsl' ], + [ 'demo-fragment.glsl', 'demo-fragment-glsl.h', 'demo_fragment_glsl' ], + [ 'demo-vertex.glsl', 'demo-vertex-glsl.h', 'demo_vertex_glsl' ], [ 'default-text.txt', 'default-text.h', 'default_text' ], ] @@ -50,6 +49,6 @@ demo_shader_sources += custom_target('default font', glyphy_demo = executable('glyphy-demo', demo_sources + demo_shader_sources, cpp_args: [], include_directories: [confinc, srcinc], - dependencies: [freetype_dep, harfbuzz_dep, gl_dep, glew_dep, glut_dep], + dependencies: [freetype_dep, harfbuzz_dep, gl_dep, glew_dep, glfw_dep], link_with: [libglyphy], install: true) diff --git a/git.mk b/git.mk deleted file mode 100644 index 315d06bc..00000000 --- a/git.mk +++ /dev/null @@ -1,285 +0,0 @@ -# git.mk -# -# Copyright 2009, Red Hat, Inc. -# Copyright 2010,2011,2012,2013 Behdad Esfahbod -# Written by Behdad Esfahbod -# -# Copying and distribution of this file, with or without modification, -# is permitted in any medium without royalty provided the copyright -# notice and this notice are preserved. -# -# The latest version of this file can be downloaded from: -# https://raw.github.com/behdad/git.mk/master/git.mk -# Bugs, etc, should be reported upstream at: -# https://github.com/behdad/git.mk -# -# To use in your project, import this file in your git repo's toplevel, -# then do "make -f git.mk". This modifies all Makefile.am files in -# your project to -include git.mk. Remember to add that line to new -# Makefile.am files you create in your project, or just rerun the -# "make -f git.mk". -# -# This enables automatic .gitignore generation. If you need to ignore -# more files, add them to the GITIGNOREFILES variable in your Makefile.am. -# But think twice before doing that. If a file has to be in .gitignore, -# chances are very high that it's a generated file and should be in one -# of MOSTLYCLEANFILES, CLEANFILES, DISTCLEANFILES, or MAINTAINERCLEANFILES. -# -# The only case that you need to manually add a file to GITIGNOREFILES is -# when remove files in one of mostlyclean-local, clean-local, distclean-local, -# or maintainer-clean-local make targets. -# -# Note that for files like editor backup, etc, there are better places to -# ignore them. See "man gitignore". -# -# If "make maintainer-clean" removes the files but they are not recognized -# by this script (that is, if "git status" shows untracked files still), send -# me the output of "git status" as well as your Makefile.am and Makefile for -# the directories involved and I'll diagnose. -# -# For a list of toplevel files that should be in MAINTAINERCLEANFILES, see -# Makefile.am.sample in the git.mk git repo. -# -# Don't EXTRA_DIST this file. It is supposed to only live in git clones, -# not tarballs. It serves no useful purpose in tarballs and clutters the -# build dir. -# -# This file knows how to handle autoconf, automake, libtool, gtk-doc, -# gnome-doc-utils, yelp.m4, mallard, intltool, gsettings, dejagnu. -# -# This makefile provides the following targets: -# -# - all: "make all" will build all gitignore files. -# - gitignore: makes all gitignore files in the current dir and subdirs. -# - .gitignore: make gitignore file for the current dir. -# - gitignore-recurse: makes all gitignore files in the subdirs. -# -# KNOWN ISSUES: -# -# - Recursive configure doesn't work as $(top_srcdir)/git.mk inside the -# submodule doesn't find us. If you have configure.{in,ac} files in -# subdirs, add a proxy git.mk file in those dirs that simply does: -# "include $(top_srcdir)/../git.mk". Add more ..'s to your taste. -# And add those files to git. See vte/gnome-pty-helper/git.mk for -# example. -# - - - -############################################################################### -# Variables user modules may want to add to toplevel MAINTAINERCLEANFILES: -############################################################################### - -# -# Most autotools-using modules should be fine including this variable in their -# toplevel MAINTAINERCLEANFILES: -GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \ - $(srcdir)/aclocal.m4 \ - $(srcdir)/ar-lib \ - $(srcdir)/autoscan.log \ - $(srcdir)/compile \ - $(srcdir)/config.guess \ - $(srcdir)/config.h.in \ - $(srcdir)/config.sub \ - $(srcdir)/configure.scan \ - $(srcdir)/depcomp \ - $(srcdir)/install-sh \ - $(srcdir)/ltmain.sh \ - $(srcdir)/missing \ - $(srcdir)/mkinstalldirs -# -# All modules should also be fine including the following variable, which -# removes automake-generated Makefile.in files: -GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \ - `$(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' $(srcdir)/configure.ac | \ - while read f; do \ - case $$f in Makefile|*/Makefile) \ - test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \ - done` -# -# Modules that use libtool /and/ use AC_CONFIG_MACRO_DIR([m4]) may also -# include this: -GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \ - $(srcdir)/m4/libtool.m4 \ - $(srcdir)/m4/ltoptions.m4 \ - $(srcdir)/m4/ltsugar.m4 \ - $(srcdir)/m4/ltversion.m4 \ - $(srcdir)/m4/lt~obsolete.m4 - - - -############################################################################### -# Default rule is to install ourselves in all Makefile.am files: -############################################################################### - -git-all: git-mk-install - -git-mk-install: - @echo "Installing git makefile" - @any_failed=; \ - find "`test -z "$(top_srcdir)" && echo . || echo "$(top_srcdir)"`" -name Makefile.am | while read x; do \ - if grep 'include .*/git.mk' $$x >/dev/null; then \ - echo "$$x already includes git.mk"; \ - else \ - failed=; \ - echo "Updating $$x"; \ - { cat $$x; \ - echo ''; \ - echo '-include $$(top_srcdir)/git.mk'; \ - } > $$x.tmp || failed=1; \ - if test x$$failed = x; then \ - mv $$x.tmp $$x || failed=1; \ - fi; \ - if test x$$failed = x; then : else \ - echo "Failed updating $$x"; >&2 \ - any_failed=1; \ - fi; \ - fi; done; test -z "$$any_failed" - -.PHONY: git-all git-mk-install - - - -############################################################################### -# Actual .gitignore generation: -############################################################################### - -$(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk - $(AM_V_GEN) \ - { \ - if test "x$(DOC_MODULE)" = x -o "x$(DOC_MAIN_SGML_FILE)" = x; then :; else \ - for x in \ - $(DOC_MODULE)-decl-list.txt \ - $(DOC_MODULE)-decl.txt \ - tmpl/$(DOC_MODULE)-unused.sgml \ - "tmpl/*.bak" \ - xml html \ - ; do echo "/$$x"; done; \ - fi; \ - if test "x$(DOC_MODULE)$(DOC_ID)" = x -o "x$(DOC_LINGUAS)" = x; then :; else \ - for lc in $(DOC_LINGUAS); do \ - for x in \ - $(if $(DOC_MODULE),$(DOC_MODULE).xml) \ - $(DOC_PAGES) \ - $(DOC_INCLUDES) \ - ; do echo "/$$lc/$$x"; done; \ - done; \ - for x in \ - $(_DOC_OMF_ALL) \ - $(_DOC_DSK_ALL) \ - $(_DOC_HTML_ALL) \ - $(_DOC_MOFILES) \ - $(DOC_H_FILE) \ - "*/.xml2po.mo" \ - "*/*.omf.out" \ - ; do echo /$$x; done; \ - fi; \ - if test "x$(HELP_ID)" = x -o "x$(HELP_LINGUAS)" = x; then :; else \ - for lc in $(HELP_LINGUAS); do \ - for x in \ - $(HELP_FILES) \ - "$$lc.stamp" \ - "$$lc.mo" \ - ; do echo "/$$lc/$$x"; done; \ - done; \ - fi; \ - if test "x$(gsettings_SCHEMAS)" = x; then :; else \ - for x in \ - $(gsettings_SCHEMAS:.xml=.valid) \ - $(gsettings__enum_file) \ - ; do echo "/$$x"; done; \ - fi; \ - if test -f $(srcdir)/po/Makefile.in.in; then \ - for x in \ - po/Makefile.in.in \ - po/Makefile.in \ - po/Makefile \ - po/POTFILES \ - po/stamp-it \ - po/.intltool-merge-cache \ - "po/*.gmo" \ - "po/*.mo" \ - po/$(GETTEXT_PACKAGE).pot \ - intltool-extract.in \ - intltool-merge.in \ - intltool-update.in \ - ; do echo "/$$x"; done; \ - fi; \ - if test -f $(srcdir)/configure; then \ - for x in \ - autom4te.cache \ - configure \ - config.h \ - stamp-h1 \ - libtool \ - config.lt \ - ; do echo "/$$x"; done; \ - fi; \ - if test "x$(DEJATOOL)" = x; then :; else \ - for x in \ - $(DEJATOOL) \ - ; do echo "/$$x.sum"; echo "/$$x.log"; done; \ - echo /site.exp; \ - fi; \ - if test "x$(am__dirstamp)" = x; then :; else \ - echo "$(am__dirstamp)"; \ - fi; \ - if test "x$(LTCOMPILE)" = x; then :; else \ - for x in \ - "*.lo" \ - ".libs" "_libs" \ - ; do echo "$$x"; done; \ - fi; \ - for x in \ - .gitignore \ - $(GITIGNOREFILES) \ - $(CLEANFILES) \ - $(PROGRAMS) $(check_PROGRAMS) $(EXTRA_PROGRAMS) \ - $(LIBRARIES) $(check_LIBRARIES) $(EXTRA_LIBRARIES) \ - $(LTLIBRARIES) $(check_LTLIBRARIES) $(EXTRA_LTLIBRARIES) \ - so_locations \ - $(MOSTLYCLEANFILES) \ - "*.$(OBJEXT)" \ - $(DISTCLEANFILES) \ - $(am__CONFIG_DISTCLEAN_FILES) \ - $(CONFIG_CLEAN_FILES) \ - TAGS ID GTAGS GRTAGS GSYMS GPATH tags \ - "*.tab.c" \ - $(MAINTAINERCLEANFILES) \ - $(BUILT_SOURCES) \ - $(DEPDIR) \ - Makefile \ - Makefile.in \ - "*.orig" \ - "*.rej" \ - "*.bak" \ - "*~" \ - ".*.sw[nop]" \ - ".dirstamp" \ - ; do echo "/$$x"; done; \ - } | \ - sed "s@^/`echo "$(srcdir)" | sed 's/\(.\)/[\1]/g'`/@/@" | \ - sed 's@/[.]/@/@g' | \ - LC_ALL=C sort | uniq > $@.tmp && \ - mv $@.tmp $@; - -all: $(srcdir)/.gitignore gitignore-recurse-maybe -gitignore: $(srcdir)/.gitignore gitignore-recurse - -gitignore-recurse-maybe: - @for subdir in $(DIST_SUBDIRS); do \ - case " $(SUBDIRS) " in \ - *" $$subdir "*) :;; \ - *) test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse-maybe || echo "Skipping $$subdir");; \ - esac; \ - done -gitignore-recurse: - @for subdir in $(DIST_SUBDIRS); do \ - test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) .gitignore gitignore-recurse || echo "Skipping $$subdir"); \ - done - -maintainer-clean: gitignore-clean -gitignore-clean: - -rm -f $(srcdir)/.gitignore - -.PHONY: gitignore-clean gitignore gitignore-recurse gitignore-recurse-maybe diff --git a/glyphy.pc.in b/glyphy.pc.in deleted file mode 100644 index 1867ecbd..00000000 --- a/glyphy.pc.in +++ /dev/null @@ -1,11 +0,0 @@ -prefix=@prefix@ -exec_prefix=@exec_prefix@ -libdir=@libdir@ -includedir=@includedir@ - -Name: GLyphy -Description: High-quality glyph rendering library using OpenGL ES2 shaders -URL: http://code.google.com/p/glyphy/ -Version: @PACKAGE_VERSION@ -Libs: -L${libdir} -lglyphy -Cflags: -I${includedir}/glyphy diff --git a/meson.build b/meson.build index fcc03208..39e6129f 100644 --- a/meson.build +++ b/meson.build @@ -4,7 +4,8 @@ project('glyphy', 'c', 'cpp', default_options: [ 'cpp_rtti=false', 'cpp_std=c++11', - 'wrap_mode=nofallback' + 'wrap_mode=nofallback', + 'buildtype=debugoptimized', ], ) @@ -60,9 +61,13 @@ if not freetype_dep.found() freetype_dep = dependency('Freetype', method: 'cmake', required: true) endif harfbuzz_dep = dependency('harfbuzz', version: '>= 4.0.0', required: true) -gl_dep = dependency('gl', required: true) -glew_dep = dependency('glew', required: false) -glut_dep = dependency('GLUT', required: get_option('demo').enabled()) +glew_dep = dependency('glew', required: get_option('demo').enabled()) +glfw_dep = dependency('glfw3', required: get_option('demo').enabled()) +if host_machine.system() == 'darwin' + gl_dep = dependency('appleframeworks', modules: ['OpenGL'], required: true) +else + gl_dep = dependency('gl', required: true) +endif prefix = get_option('prefix') libdir = join_paths(prefix, get_option('libdir')) @@ -82,8 +87,8 @@ endif if glew_dep.found() glyphy_conf.set('HAVE_GLEW', 1) endif -if glut_dep.found() - glyphy_conf.set('HAVE_GLUT', 1) +if glfw_dep.found() + glyphy_conf.set('HAVE_GLFW', 1) endif glyphy_conf.set_quoted('PKGDATADIR', pkgdatadir) @@ -91,6 +96,7 @@ configure_file(output: 'config.h', configuration: glyphy_conf) confinc = include_directories('.', 'src') subdir('src') +subdir('bench') if get_option('demo').enabled() subdir('demo') diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index 4b6bdb81..00000000 --- a/src/Makefile.am +++ /dev/null @@ -1,59 +0,0 @@ -NULL = -EXTRA_DIST = -CLEANFILES = -DISTCLEANFILES = -MAINTAINERCLEANFILES = -BUILT_SOURCES = - -lib_LTLIBRARIES = libglyphy.la -libglyphy_la_CPPFLAGS = \ - $(NULL) -libglyphy_la_LIBADD = \ - -lm \ - $(NULL) -libglyphy_la_LDFLAGS = \ - -version-info @GLYPHY_LIBTOOL_VERSION_INFO@ \ - $(NULL) -libglyphy_la_SOURCES = \ - glyphy-arc.cc \ - glyphy-arc-bezier.hh \ - glyphy-arcs.cc \ - glyphy-arcs-bezier.hh \ - glyphy-blob.cc \ - glyphy-common.hh \ - glyphy-extents.cc \ - glyphy-geometry.hh \ - glyphy-outline.cc \ - glyphy-sdf.cc \ - glyphy-shaders.cc \ - $(PUBLICHEADERS) \ - $(SHADERHEADERS) \ - $(NULL) -PUBLICHEADERS = \ - glyphy.h \ - glyphy-freetype.h \ - glyphy-harfbuzz.h \ - $(NULL) -SHADERS = \ - glyphy-common.glsl \ - glyphy-sdf.glsl \ - $(NULL) -SHADERHEADERS = $(patsubst %.glsl,%-glsl.h, $(SHADERS)) -BUILT_SOURCES += $(SHADERHEADERS) -EXTRA_DIST += $(SHADERS) - -pkginclude_HEADERS = \ - $(PUBLICHEADERS) \ - $(NULL) -pkgdata_DATA = \ - $(SHADERS) \ - $(NULL) - -EXTRA_DIST += stringize -%-glsl.h: %.glsl stringize - $(AM_V_GEN) $(srcdir)/stringize "static const char *`echo "$<" | \ - sed 's@.*/@@;s/[-.]/_/g'`" "$<" > "$@.tmp" && \ - mv "$@.tmp" "$@" || ($(RM) "$@.tmp"; false) - - --include $(top_srcdir)/git.mk diff --git a/src/glyphy-arc-bezier.hh b/src/glyphy-arc-bezier.hh deleted file mode 100644 index ec14660e..00000000 --- a/src/glyphy-arc-bezier.hh +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2012,2013 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju - */ - -#ifndef GLYPHY_ARC_BEZIER_HH -#define GLYPHY_ARC_BEZIER_HH - -#include "glyphy-common.hh" -#include "glyphy-geometry.hh" - -namespace GLyphy { -namespace ArcBezier { - -using namespace Geometry; - - -class MaxDeviationApproximatorExact -{ - public: - /* Returns 3 max(abs(d₀ t (1-t)² + d₁ t² (1-t)) for 0≤t≤1. */ - static double approximate_deviation (double d0, double d1) - { - double candidates[4] = {0,1}; - unsigned int num_candidates = 2; - if (d0 == d1) - candidates[num_candidates++] = .5; - else { - double delta = d0*d0 - d0*d1 + d1*d1; - double t2 = 1. / (3 * (d0 - d1)); - double t0 = (2 * d0 - d1) * t2; - if (delta == 0) - candidates[num_candidates++] = t0; - else if (delta > 0) { - /* This code can be optimized to avoid the sqrt if the solution - * is not feasible (ie. lies outside (0,1)). I have implemented - * that in cairo-spline.c:_cairo_spline_bound(). Can be reused - * here. - */ - double t1 = sqrt (delta) * t2; - candidates[num_candidates++] = t0 - t1; - candidates[num_candidates++] = t0 + t1; - } - } - - double e = 0; - for (unsigned int i = 0; i < num_candidates; i++) { - double t = candidates[i]; - double ee; - if (t < 0. || t > 1.) - continue; - ee = fabs (3 * t * (1-t) * (d0 * (1 - t) + d1 * t)); - e = std::max (e, ee); - } - - return e; - } -}; - - - -template -class ArcBezierErrorApproximatorBehdad -{ - public: - static double approximate_bezier_arc_error (const Bezier &b0, const Arc &a) - { - assert (b0.p0 == a.p0); - assert (b0.p3 == a.p1); - - double ea; - Bezier b1 = a.approximate_bezier (&ea); - - assert (b0.p0 == b1.p0); - assert (b0.p3 == b1.p3); - - Vector v0 = b1.p1 - b0.p1; - Vector v1 = b1.p2 - b0.p2; - - Vector b = (b0.p3 - b0.p0).normalized (); - v0 = v0.rebase (b); - v1 = v1.rebase (b); - - Vector v (MaxDeviationApproximator::approximate_deviation (v0.dx, v1.dx), - MaxDeviationApproximator::approximate_deviation (v0.dy, v1.dy)); - - /* Edge cases: If d*d is too close too large default to a weak bound. */ - if (a.d * a.d > 1. - 1e-4) - return ea + v.len (); - - /* If the wedge doesn't contain control points, default to weak bound. */ - if (!a.wedge_contains_point (b0.p1) || !a.wedge_contains_point (b0.p2)) - return ea + v.len (); - - /* If straight line, return the max ortho deviation. */ - if (fabs (a.d) < 1e-6) - return ea + v.dy; - - /* We made sure that fabs(a.d) < 1 */ - double tan_half_alpha = fabs (tan2atan (a.d)); - - double tan_v = v.dx / v.dy; - - double eb; - if (fabs (tan_v) <= tan_half_alpha) - return ea + v.len (); - - double c2 = (a.p1 - a.p0).len () * .5; - double r = a.radius (); - - eb = Vector (c2 + v.dx, c2 / tan_half_alpha + v.dy).len () - r; - assert (eb >= 0); - - return ea + eb; - } -}; - - - -template -class ArcBezierApproximatorMidpointSimple -{ - public: - static const Arc approximate_bezier_with_arc (const Bezier &b, double *error) - { - Arc a (b.p0, b.p3, b.midpoint (), false); - - *error = ArcBezierErrorApproximator::approximate_bezier_arc_error (b, a); - - return a; - } -}; - -template -class ArcBezierApproximatorMidpointTwoPart -{ - public: - static const Arc approximate_bezier_with_arc (const Bezier &b, double *error, double mid_t = .5) - { - Pair pair = b.split (mid_t); - Point m = pair.second.p0; - - Arc a0 (b.p0, m, b.p3, true); - Arc a1 (m, b.p3, b.p0, true); - - double e0 = ArcBezierErrorApproximator::approximate_bezier_arc_error (pair.first, a0); - double e1 = ArcBezierErrorApproximator::approximate_bezier_arc_error (pair.second, a1); - *error = std::max (e0, e1); - - return Arc (b.p0, b.p3, m, false); - } -}; - -template -class ArcBezierApproximatorQuantized -{ - public: - ArcBezierApproximatorQuantized (double _max_d = GLYPHY_INFINITY, unsigned int _d_bits = 0) : - max_d (_max_d), d_bits (_d_bits) {}; - - protected: - double max_d; - unsigned int d_bits; - - public: - const Arc approximate_bezier_with_arc (const Bezier &b, double *error) const - { - double mid_t = .5; - Arc a (b.p0, b.p3, b.point (mid_t), false); - Arc orig_a = a; - - if (isfinite (max_d)) { - assert (max_d >= 0); - if (fabs (a.d) > max_d) - a.d = a.d < 0 ? -max_d : max_d; - } - if (d_bits && max_d != 0) { - assert (isfinite (max_d)); - assert (fabs (a.d) <= max_d); - int mult = (1 << (d_bits - 1)) - 1; - int id = round (a.d / max_d * mult); - assert (-mult <= id && id <= mult); - a.d = id * max_d / mult; - assert (fabs (a.d) <= max_d); - } - - /* Error introduced by arc quantization */ - double ed = fabs (a.d - orig_a.d) * (a.p1 - a.p0).len () * .5; - - ArcBezierApproximatorMidpointTwoPart - ::approximate_bezier_with_arc (b, error, mid_t); - - if (ed) { - *error += ed; - - /* Try a simple one-arc approx which works with the quantized arc. - * May produce smaller error bound. */ - double e = ArcBezierErrorApproximator::approximate_bezier_arc_error (b, a); - if (e < *error) - *error = e; - } - - return a; - } -}; - -typedef MaxDeviationApproximatorExact MaxDeviationApproximatorDefault; -typedef ArcBezierErrorApproximatorBehdad ArcBezierErrorApproximatorDefault; -typedef ArcBezierApproximatorMidpointTwoPart ArcBezierApproximatorDefault; -typedef ArcBezierApproximatorQuantized ArcBezierApproximatorQuantizedDefault; - -} /* namespace ArcBezier */ -} /* namespace GLyphy */ - -#endif /* GLYPHY_ARC_BEZIER_HH */ diff --git a/src/glyphy-arc.cc b/src/glyphy-arc.cc deleted file mode 100644 index 650e2315..00000000 --- a/src/glyphy-arc.cc +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2012 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "glyphy-common.hh" -#include "glyphy-geometry.hh" -#include "glyphy-arc-bezier.hh" - -using namespace GLyphy::Geometry; -using namespace GLyphy::ArcBezier; - - - -/* - * Circular arcs - */ - - -/* Build from a conventional arc representation */ -void -glyphy_arc_from_conventional (const glyphy_point_t *center, - double radius, - double angle0, - double angle1, - glyphy_bool_t negative, - glyphy_arc_t *arc) -{ - *arc = Arc (*center, radius, angle0, angle1, negative); -}; - -/* Convert to a conventional arc representation */ -void -glyphy_arc_to_conventional (glyphy_arc_t arc, - glyphy_point_t *center /* may be NULL */, - double *radius /* may be NULL */, - double *angle0 /* may be NULL */, - double *angle1 /* may be NULL */, - glyphy_bool_t *negative /* may be NULL */) -{ - Arc a (arc); - if (radius) *radius = a.radius (); - if (center || angle0 || angle1) { - Point c = a.center (); - if (center) *center = c; - if (angle0) *angle0 = (a.p0 - c).angle (); - if (angle1) *angle1 = (a.p1 - c).angle (); - if (negative) *negative = a.d < 0; - } -} - -glyphy_bool_t -glyphy_arc_is_a_line (glyphy_arc_t arc) -{ - return arc.d == 0; -} - -void -glyphy_arc_extents (glyphy_arc_t arc, - glyphy_extents_t *extents) -{ - Arc(arc).extents (*extents); -} - - -/* - * Approximate single pieces of geometry to/from one arc - */ - - -void -glyphy_arc_from_line (const glyphy_point_t *p0, - const glyphy_point_t *p1, - glyphy_arc_t *arc) -{ - *arc = Arc (*p0, *p1, 0); -} - -void -glyphy_arc_from_conic (const glyphy_point_t *p0, - const glyphy_point_t *p1, - const glyphy_point_t *p2, - glyphy_arc_t *arc, - double *error) -{ - Point p1_ (Point (*p0).lerp (2/3., *p1)); - Point p2_ (Point (*p2).lerp (2/3., *p1)); - glyphy_arc_from_cubic (p0, - &p1_, - &p2_, - p2, - arc, - error); -} - -void -glyphy_arc_from_cubic (const glyphy_point_t *p0, - const glyphy_point_t *p1, - const glyphy_point_t *p2, - const glyphy_point_t *p3, - glyphy_arc_t *arc, - double *error) -{ - *arc = ArcBezierApproximatorDefault::approximate_bezier_with_arc (Bezier (*p0, *p1, *p2, *p3), error); -} - -void -glyphy_arc_to_cubic (const glyphy_arc_t *arc, - glyphy_point_t *p0, - glyphy_point_t *p1, - glyphy_point_t *p2, - glyphy_point_t *p3, - double *error) -{ - Bezier b = Arc (*arc).approximate_bezier (error); - *p0 = arc->p0; - *p1 = b.p1; - *p2 = b.p2; - *p3 = arc->p1; -} diff --git a/src/glyphy-arcs-bezier.hh b/src/glyphy-arcs-bezier.hh deleted file mode 100644 index c09479f7..00000000 --- a/src/glyphy-arcs-bezier.hh +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2012 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju - */ - -#ifndef GLYPHY_ARCS_BEZIER_HH -#define GLYPHY_ARCS_BEZIER_HH - -#include "glyphy-common.hh" -#include "glyphy-geometry.hh" -#include "glyphy-arc-bezier.hh" - -namespace GLyphy { -namespace ArcsBezier { - -using namespace Geometry; -using namespace ArcBezier; - -template -class ArcsBezierApproximatorSpringSystem -{ - static inline void calc_arcs (const Bezier &b, - const std::vector &t, - const ArcBezierApproximator &appx, - std::vector &e, - std::vector &arcs, - double &max_e, double &min_e) - { - unsigned int n = t.size () - 1; - e.resize (n); - arcs.clear (); - max_e = 0; - min_e = GLYPHY_INFINITY; - for (unsigned int i = 0; i < n; i++) - { - Bezier segment = b.segment (t[i], t[i + 1]); - arcs.push_back (appx.approximate_bezier_with_arc (segment, &e[i])); - - max_e = std::max (max_e, e[i]); - min_e = std::min (min_e, e[i]); - } - } - - static inline void jiggle (const Bezier &b, - const ArcBezierApproximator &appx, - std::vector &t, - std::vector &e, - std::vector &arcs, - double &max_e, double &min_e, - double tolerance, - unsigned int &n_jiggle) - { - unsigned int n = t.size () - 1; - double conditioner = tolerance * .01; - //fprintf (stderr, "candidate n %d max_e %g min_e %g\n", n, max_e, min_e); - unsigned int max_jiggle = log2 (n) + 1; - unsigned int s; - for (s = 0; s < max_jiggle; s++) - { - double total = 0; - for (unsigned int i = 0; i < n; i++) { - double l = t[i + 1] - t[i]; - double k_inv = l * pow (e[i] + conditioner, -.3); - total += k_inv; - e[i] = k_inv; - } - for (unsigned int i = 0; i < n; i++) { - double k_inv = e[i]; - double l = k_inv / total; - t[i + 1] = t[i] + l; - } - t[n] = 1.0; // Do this to get real 1.0, not .9999999999999998! - - calc_arcs (b, t, appx, e, arcs, max_e, min_e); - - //fprintf (stderr, "n %d jiggle %d max_e %g min_e %g\n", n, s, max_e, min_e); - - n_jiggle++; - if (max_e < tolerance || (2 * min_e - max_e > tolerance)) - break; - } - //if (s == max_jiggle) fprintf (stderr, "JIGGLE OVERFLOW n %d s %d\n", n, s); - } - - public: - static void approximate_bezier_with_arcs (const Bezier &b, - double tolerance, - const ArcBezierApproximator &appx, - std::vector &arcs, - double *perror, - unsigned int max_segments = 100) - { - /* Handle fully-degenerate cases. */ - Vector v1 (b.p1 - b.p0); - Vector v2 (b.p2 - b.p0); - Vector v3 (b.p3 - b.p0); - if (fabs (v1.cross(v2)) < GLYPHY_EPSILON && fabs (v2.cross(v3)) < GLYPHY_EPSILON) - { - /* Curve has no area. If endpoints are NOT the same, replace with single - * line segment. Otherwise fully skip. */ - arcs.clear (); - if (b.p0 != b.p1) - arcs.push_back (Arc (b.p0, b.p3, 0)); - return; - } - - std::vector t; - std::vector e; - double max_e = 0., min_e = 0.; - unsigned int n_jiggle = 0; - - /* Technically speaking we can bsearch for n. */ - for (unsigned int n = 1; n <= max_segments; n++) - { - t.resize (n + 1); - for (unsigned int i = 0; i < n; i++) - t[i] = double (i) / n; - t[n] = 1.0; // Do this out of the loop to get real 1.0, not .9999999999999998! - - calc_arcs (b, t, appx, e, arcs, max_e, min_e); - - for (unsigned int i = 0; i < n; i++) - if (e[i] <= tolerance) { - jiggle (b, appx, t, e, arcs, max_e, min_e, tolerance, n_jiggle); - break; - } - - if (max_e <= tolerance) - break; - } - if (perror) - *perror = max_e; - //fprintf (stderr, "n_jiggle %d\n", n_jiggle); - } -}; - -} /* namespace ArcsBezier */ -} /* namespace GLyphy */ - -#endif /* GLYPHY_ARCS_BEZIER_HH */ diff --git a/src/glyphy-arcs.cc b/src/glyphy-arcs.cc deleted file mode 100644 index 7479b7d0..00000000 --- a/src/glyphy-arcs.cc +++ /dev/null @@ -1,320 +0,0 @@ -/* - * Copyright 2012 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "glyphy-common.hh" -#include "glyphy-geometry.hh" -#include "glyphy-arcs-bezier.hh" - -using namespace GLyphy::Geometry; -using namespace GLyphy::ArcsBezier; - - - -/* - * Approximate outlines with multiple arcs - */ - - -struct glyphy_arc_accumulator_t { - unsigned int refcount; - - double tolerance; - double max_d; - unsigned int d_bits; - glyphy_arc_endpoint_accumulator_callback_t callback; - void *user_data; - - glyphy_point_t start_point; - glyphy_point_t current_point; - bool need_moveto; - unsigned int num_endpoints; - double max_error; - glyphy_bool_t success; -}; - - -glyphy_arc_accumulator_t * -glyphy_arc_accumulator_create (void) -{ - glyphy_arc_accumulator_t *acc = (glyphy_arc_accumulator_t *) calloc (1, sizeof (glyphy_arc_accumulator_t)); - acc->refcount = 1; - - acc->tolerance = 5e-4; - acc->max_d = GLYPHY_MAX_D; - acc->d_bits = 8; - acc->callback = NULL; - acc->user_data = NULL; - - glyphy_arc_accumulator_reset (acc); - - return acc; -} - -void -glyphy_arc_accumulator_reset (glyphy_arc_accumulator_t *acc) -{ - acc->start_point = acc->current_point = Point (0, 0); - acc->need_moveto = true; - acc->num_endpoints = 0; - acc->max_error = 0; - acc->success = true; -} - -void -glyphy_arc_accumulator_destroy (glyphy_arc_accumulator_t *acc) -{ - if (!acc || --acc->refcount) - return; - - free (acc); -} - -glyphy_arc_accumulator_t * -glyphy_arc_accumulator_reference (glyphy_arc_accumulator_t *acc) -{ - if (acc) - acc->refcount++; - return acc; -} - - -/* Configure acc */ - -void -glyphy_arc_accumulator_set_tolerance (glyphy_arc_accumulator_t *acc, - double tolerance) -{ - acc->tolerance = tolerance; -} - -double -glyphy_arc_accumulator_get_tolerance (glyphy_arc_accumulator_t *acc) -{ - return acc->tolerance; -} - -void -glyphy_arc_accumulator_set_callback (glyphy_arc_accumulator_t *acc, - glyphy_arc_endpoint_accumulator_callback_t callback, - void *user_data) -{ - acc->callback = callback; - acc->user_data = user_data; -} - -void -glyphy_arc_accumulator_get_callback (glyphy_arc_accumulator_t *acc, - glyphy_arc_endpoint_accumulator_callback_t *callback, - void **user_data) -{ - *callback = acc->callback; - *user_data = acc->user_data; -} - -void -glyphy_arc_accumulator_set_d_metrics (glyphy_arc_accumulator_t *acc, - double max_d, - double d_bits) -{ - acc->max_d = max_d; - acc->d_bits = d_bits; -} - -void -glyphy_arc_accumulator_get_d_metrics (glyphy_arc_accumulator_t *acc, - double *max_d, - double *d_bits) -{ - *max_d = acc->max_d; - *d_bits = acc->d_bits; -} - - -/* Accumulation results */ - -unsigned int -glyphy_arc_accumulator_get_num_endpoints (glyphy_arc_accumulator_t *acc) -{ - return acc->num_endpoints; -} - -double -glyphy_arc_accumulator_get_error (glyphy_arc_accumulator_t *acc) -{ - return acc->max_error; -} - -glyphy_bool_t -glyphy_arc_accumulator_successful (glyphy_arc_accumulator_t *acc) -{ - return acc->success; -} - - -/* Accumulate */ - -static void -emit (glyphy_arc_accumulator_t *acc, const Point &p, double d) -{ - glyphy_arc_endpoint_t endpoint = {p, d}; - acc->success = acc->success && acc->callback (&endpoint, acc->user_data); - if (acc->success) { - acc->num_endpoints++; - acc->current_point = p; - } -} - -static void -accumulate (glyphy_arc_accumulator_t *acc, const Point &p, double d) -{ - if (Point (acc->current_point) == p) - return; - if (d == GLYPHY_INFINITY) { - /* Emit moveto lazily, for cleaner outlines */ - acc->need_moveto = true; - acc->current_point = p; - return; - } - if (acc->need_moveto) { - emit (acc, acc->current_point, GLYPHY_INFINITY); - if (acc->success) { - acc->start_point = acc->current_point; - acc->need_moveto = false; - } - } - emit (acc, p, d); -} - -static void -move_to (glyphy_arc_accumulator_t *acc, const Point &p) -{ - if (!acc->num_endpoints || p != acc->current_point) - accumulate (acc, p, GLYPHY_INFINITY); -} - -static void -arc_to (glyphy_arc_accumulator_t *acc, const Point &p1, double d) -{ - accumulate (acc, p1, d); -} - -static void -bezier (glyphy_arc_accumulator_t *acc, const Bezier &b) -{ - double e = 0; - - std::vector arcs; - typedef ArcBezierApproximatorQuantizedDefault _ArcBezierApproximator; - _ArcBezierApproximator appx (acc->max_d, acc->d_bits); - ArcsBezierApproximatorSpringSystem<_ArcBezierApproximator> - ::approximate_bezier_with_arcs (b, acc->tolerance, appx, arcs, &e); - - acc->max_error = std::max (acc->max_error, e); - - move_to (acc, b.p0); - for (unsigned int i = 0; i < arcs.size (); i++) - arc_to (acc, arcs[i].p1, arcs[i].d); -} - -static void -close_path (glyphy_arc_accumulator_t *acc) -{ - if (!acc->need_moveto && Point (acc->current_point) != Point (acc->start_point)) - arc_to (acc, acc->start_point, 0); -} - -void -glyphy_arc_accumulator_move_to (glyphy_arc_accumulator_t *acc, - const glyphy_point_t *p0) -{ - move_to (acc, *p0); -} - -void -glyphy_arc_accumulator_line_to (glyphy_arc_accumulator_t *acc, - const glyphy_point_t *p1) -{ - arc_to (acc, *p1, 0); -} - -void -glyphy_arc_accumulator_conic_to (glyphy_arc_accumulator_t *acc, - const glyphy_point_t *p1, - const glyphy_point_t *p2) -{ - bezier (acc, Bezier (acc->current_point, - Point (acc->current_point).lerp (2/3., *p1), - Point (*p2).lerp (2/3., *p1), - *p2)); -} - -void -glyphy_arc_accumulator_cubic_to (glyphy_arc_accumulator_t *acc, - const glyphy_point_t *p1, - const glyphy_point_t *p2, - const glyphy_point_t *p3) -{ - bezier (acc, Bezier (acc->current_point, *p1, *p2, *p3)); -} - -void -glyphy_arc_accumulator_arc_to (glyphy_arc_accumulator_t *acc, - const glyphy_point_t *p1, - double d) -{ - arc_to (acc, *p1, d); -} - -void -glyphy_arc_accumulator_close_path (glyphy_arc_accumulator_t *acc) -{ - close_path (acc); -} - - - -/* - * Outline extents from arc list - */ - - -void -glyphy_arc_list_extents (const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - glyphy_extents_t *extents) -{ - Point p0 (0, 0); - glyphy_extents_clear (extents); - for (unsigned int i = 0; i < num_endpoints; i++) { - const glyphy_arc_endpoint_t &endpoint = endpoints[i]; - if (endpoint.d == GLYPHY_INFINITY) { - p0 = endpoint.p; - continue; - } - Arc arc (p0, endpoint.p, endpoint.d); - p0 = endpoint.p; - - glyphy_extents_t arc_extents; - arc.extents (arc_extents); - glyphy_extents_extend (extents, &arc_extents); - } -} diff --git a/src/glyphy-blob.cc b/src/glyphy-blob.cc deleted file mode 100644 index b10fae8e..00000000 --- a/src/glyphy-blob.cc +++ /dev/null @@ -1,367 +0,0 @@ -/* - * Copyright 2012 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include - -#include "glyphy-common.hh" -#include "glyphy-geometry.hh" - -#define MAX_GRID_SIZE 63 - -using namespace GLyphy::Geometry; - - -#define UPPER_BITS(v,bits,total_bits) ((v) >> ((total_bits) - (bits))) -#define LOWER_BITS(v,bits,total_bits) ((v) & ((1 << (bits)) - 1)) - -#define MAX_X 4095 -#define MAX_Y 4095 - -static inline glyphy_rgba_t -arc_endpoint_encode (unsigned int ix, unsigned int iy, double d) -{ - glyphy_rgba_t v; - - /* 12 bits for each of x and y, 8 bits for d */ - assert (ix <= MAX_X); - assert (iy <= MAX_Y); - unsigned int id; - if (isinf (d)) - id = 0; - else { - assert (fabs (d) <= GLYPHY_MAX_D); - id = 128 + lround (d * 127 / GLYPHY_MAX_D); - } - assert (id < 256); - - v.r = id; - v.g = LOWER_BITS (ix, 8, 12); - v.b = LOWER_BITS (iy, 8, 12); - v.a = ((ix >> 8) << 4) | (iy >> 8); - return v; -} - -static inline glyphy_rgba_t -arc_list_encode (unsigned int offset, unsigned int num_points, int side) -{ - glyphy_rgba_t v; - v.r = 0; // unused for arc-list encoding - v.g = UPPER_BITS (offset, 8, 16); - v.b = LOWER_BITS (offset, 8, 16); - v.a = LOWER_BITS (num_points, 8, 8); - if (side < 0 && !num_points) - v.a = 255; - return v; -} - -static inline glyphy_rgba_t -line_encode (const Line &line) -{ - Line l = line.normalized (); - double angle = l.n.angle (); - double distance = l.c; - - int ia = lround (-angle / M_PI * 0x7FFF); - unsigned int ua = ia + 0x8000; - assert (0 == (ua & ~0xFFFF)); - - int id = lround (distance * 0x1FFF); - unsigned int ud = id + 0x4000; - assert (0 == (ud & ~0x7FFF)); - - /* Marker for line-encoded */ - ud |= 0x8000; - - glyphy_rgba_t v; - v.r = ud >> 8; - v.g = ud & 0xFF; - v.b = ua >> 8; - v.a = ua & 0xFF; - return v; -} - - -/* Given a cell, fills the vector closest_arcs with arcs that may be closest to some point in the cell. - * Uses idea that all close arcs to cell must be ~close to center of cell. - */ -static void -closest_arcs_to_cell (Point c0, Point c1, /* corners */ - double faraway, - double enlighten_max, - double embolden_max, - const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - std::vector &near_endpoints, - int *side) -{ - // This can be improved: - double synth_max = std::max (enlighten_max, embolden_max); - faraway = std::max (faraway, synth_max); - - // Find distance between cell center - Point c = c0.midpoint (c1); - double min_dist = glyphy_sdf_from_arc_list (endpoints, num_endpoints, &c, NULL); - - *side = min_dist >= 0 ? +1 : -1; - min_dist = fabs (min_dist); - std::vector near_arcs; - - // If d is the distance from the center of the square to the nearest arc, then - // all nearest arcs to the square must be at most almost [d + half_diagonal] from the center. - double half_diagonal = (c - c0).len (); - double added = min_dist + half_diagonal + synth_max; - double radius_squared = added * added; - if (min_dist - half_diagonal <= faraway) { - Point p0 (0, 0); - for (unsigned int i = 0; i < num_endpoints; i++) { - const glyphy_arc_endpoint_t &endpoint = endpoints[i]; - if (endpoint.d == GLYPHY_INFINITY) { - p0 = endpoint.p; - continue; - } - Arc arc (p0, endpoint.p, endpoint.d); - p0 = endpoint.p; - - if (arc.squared_distance_to_point (c) <= radius_squared) - near_arcs.push_back (arc); - } - } - - Point p1 = Point (0, 0); - for (unsigned i = 0; i < near_arcs.size (); i++) - { - Arc arc = near_arcs[i]; - - if (i == 0 || p1 != arc.p0) { - glyphy_arc_endpoint_t endpoint = {arc.p0, GLYPHY_INFINITY}; - near_endpoints.push_back (endpoint); - p1 = arc.p0; - } - - glyphy_arc_endpoint_t endpoint = {arc.p1, arc.d}; - near_endpoints.push_back (endpoint); - p1 = arc.p1; - } -} - - -glyphy_bool_t -glyphy_arc_list_encode_blob (const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - glyphy_rgba_t *blob, - unsigned int blob_size, - double faraway, - double avg_fetch_desired, - double *avg_fetch_achieved, - unsigned int *output_len, - unsigned int *nominal_width, /* 8bit */ - unsigned int *nominal_height, /* 8bit */ - glyphy_extents_t *pextents) -{ - return - glyphy_arc_list_encode_blob2 (endpoints, - num_endpoints, - blob, - blob_size, - faraway, - faraway / M_SQRT2, - 0, - 0, - avg_fetch_achieved, - output_len, - nominal_width, /* 6bit */ - nominal_height, /* 6bit */ - pextents); -} - -GLYPHY_API glyphy_bool_t -glyphy_arc_list_encode_blob2 (const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - glyphy_rgba_t *blob, - unsigned int blob_size, - double faraway, - double grid_unit, - double enlighten_max, - double embolden_max, - double *avg_fetch_achieved, - unsigned int *output_len, - unsigned int *nominal_width, /* 6bit */ - unsigned int *nominal_height, /* 6bit */ - glyphy_extents_t *pextents) -{ - glyphy_extents_t extents; - glyphy_extents_clear (&extents); - - glyphy_arc_list_extents (endpoints, num_endpoints, &extents); - - if (glyphy_extents_is_empty (&extents)) { - *pextents = extents; - if (!blob_size) - return false; - *blob = arc_list_encode (0, 0, +1); - *avg_fetch_achieved = 1; - *output_len = 1; - *nominal_width = *nominal_height = 1; - return true; - } - - /* Add antialiasing padding */ - extents.min_x -= faraway + embolden_max; - extents.min_y -= faraway + embolden_max; - extents.max_x += faraway + embolden_max; - extents.max_y += faraway + embolden_max; - - double glyph_width = extents.max_x - extents.min_x; - double glyph_height = extents.max_y - extents.min_y; - double unit = std::max (glyph_width, glyph_height); - - unsigned int grid_w = std::min (MAX_GRID_SIZE, (int) std::ceil (glyph_width / grid_unit)); - unsigned int grid_h = std::min (MAX_GRID_SIZE, (int) std::ceil (glyph_height / grid_unit)); - - if (glyph_width > glyph_height) { - glyph_height = grid_h * unit / grid_w; - extents.max_y = extents.min_y + glyph_height; - } else { - glyph_width = grid_w * unit / grid_h; - extents.max_x = extents.min_x + glyph_width; - } - - double cell_unit = unit / std::max (grid_w, grid_h); - - std::vector tex_data; - std::vector near_endpoints; - - unsigned int header_length = grid_w * grid_h; - unsigned int offset = header_length; - tex_data.resize (header_length); - Point origin = Point (extents.min_x, extents.min_y); - unsigned int total_arcs = 0; - - for (unsigned int row = 0; row < grid_h; row++) - for (unsigned int col = 0; col < grid_w; col++) - { - Point cp0 = origin + Vector ((col + 0) * cell_unit, (row + 0) * cell_unit); - Point cp1 = origin + Vector ((col + 1) * cell_unit, (row + 1) * cell_unit); - near_endpoints.clear (); - - int side; - closest_arcs_to_cell (cp0, cp1, - faraway, - enlighten_max, - embolden_max, - endpoints, num_endpoints, - near_endpoints, - &side); - -#define QUANTIZE_X(X) (lround (MAX_X * ((X - extents.min_x) / glyph_width ))) -#define QUANTIZE_Y(Y) (lround (MAX_Y * ((Y - extents.min_y) / glyph_height))) -#define DEQUANTIZE_X(X) (double (X) / MAX_X * glyph_width + extents.min_x) -#define DEQUANTIZE_Y(Y) (double (Y) / MAX_Y * glyph_height + extents.min_y) -#define SNAP(P) (Point (DEQUANTIZE_X (QUANTIZE_X ((P).x)), DEQUANTIZE_Y (QUANTIZE_Y ((P).y)))) - - if (near_endpoints.size () == 2 && near_endpoints[1].d == 0) { - Point c (extents.min_x + glyph_width * .5, extents.min_y + glyph_height * .5); - Line line (SNAP (near_endpoints[0].p), SNAP (near_endpoints[1].p)); - line.c -= line.n * Vector (c); - line.c /= unit; - tex_data[row * grid_w + col] = line_encode (line); - continue; - } - - /* If the arclist is two arcs that can be combined in encoding if reordered, - * do that. */ - if (near_endpoints.size () == 4 && - isinf (near_endpoints[2].d) && - near_endpoints[0].p.x == near_endpoints[3].p.x && - near_endpoints[0].p.y == near_endpoints[3].p.y) - { - glyphy_arc_endpoint_t e0, e1, e2; - e0 = near_endpoints[2]; - e1 = near_endpoints[3]; - e2 = near_endpoints[1]; - near_endpoints.resize (0); - near_endpoints.push_back (e0); - near_endpoints.push_back (e1); - near_endpoints.push_back (e2); - } - - for (unsigned i = 0; i < near_endpoints.size (); i++) { - glyphy_arc_endpoint_t &endpoint = near_endpoints[i]; - tex_data.push_back (arc_endpoint_encode (QUANTIZE_X(endpoint.p.x), QUANTIZE_Y(endpoint.p.y), endpoint.d)); - } - - unsigned int current_endpoints = tex_data.size () - offset; - - if (current_endpoints) - { - /* See if we can fulfill this cell by using already-encoded arcs */ - const glyphy_rgba_t *needle = &tex_data[offset]; - unsigned int needle_len = current_endpoints; - const glyphy_rgba_t *haystack = &tex_data[header_length]; - unsigned int haystack_len = offset - header_length; - - bool found = false; - while (haystack_len >= needle_len) { - /* Trick: we don't care about first endpoint's d value, so skip one - * byte in comparison. This works because arc_encode() packs the - * d value in the first byte. */ - if (0 == memcmp (1 + (const char *) needle, - 1 + (const char *) haystack, - needle_len * sizeof (*needle) - 1)) { - found = true; - break; - } - haystack++; - haystack_len--; - } - if (found) { - unsigned int new_offset = haystack - &tex_data[0]; - tex_data.resize (offset); - haystack = needle = NULL; /* Invalidated by the resize. */ - offset = new_offset; - } - } - else - offset = 0; - - tex_data[row * grid_w + col] = arc_list_encode (offset, current_endpoints, side); - offset = tex_data.size (); - - total_arcs += current_endpoints; - } - - if (avg_fetch_achieved) - *avg_fetch_achieved = 1 + double (total_arcs) / (grid_w * grid_h); - - *pextents = extents; - - *output_len = tex_data.size (); - *nominal_width = grid_w; - *nominal_height = grid_h; - - if (tex_data.size () > blob_size) - return false; - - memcpy (blob, &tex_data[0], tex_data.size () * sizeof(tex_data[0])); - - return true; -} diff --git a/src/glyphy-common-glsl.h b/src/glyphy-common-glsl.h deleted file mode 100644 index 868177b8..00000000 --- a/src/glyphy-common-glsl.h +++ /dev/null @@ -1,224 +0,0 @@ -static const char *glyphy_common_glsl = -"/*\n" -" * Copyright 2012 Google, Inc. All Rights Reserved.\n" -" *\n" -" * Licensed under the Apache License, Version 2.0 (the \"License\");\n" -" * you may not use this file except in compliance with the License.\n" -" * You may obtain a copy of the License at\n" -" *\n" -" * http://www.apache.org/licenses/LICENSE-2.0\n" -" *\n" -" * Unless required by applicable law or agreed to in writing, software\n" -" * distributed under the License is distributed on an \"AS IS\" BASIS,\n" -" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -" * See the License for the specific language governing permissions and\n" -" * limitations under the License.\n" -" *\n" -" * Google Author(s): Behdad Esfahbod, Maysum Panju\n" -" */\n" -"\n" -"\n" -"#ifndef GLYPHY_INFINITY\n" -"# define GLYPHY_INFINITY 1e9\n" -"#endif\n" -"#ifndef GLYPHY_EPSILON\n" -"# define GLYPHY_EPSILON 1e-5\n" -"#endif\n" -"\n" -"#ifndef GLYPHY_RGBA\n" -"# ifdef GLYPHY_BGRA\n" -"# define GLYPHY_RGBA(v) glyphy_bgra (v)\n" -"# else\n" -"# define GLYPHY_RGBA(v) glyphy_rgba (v)\n" -"# endif\n" -"#endif\n" -"\n" -"vec4\n" -"glyphy_rgba (const vec4 v)\n" -"{\n" -" return v.rgba;\n" -"}\n" -"\n" -"vec4\n" -"glyphy_bgra (const vec4 v)\n" -"{\n" -" return v.bgra;\n" -"}\n" -"\n" -"\n" -"struct glyphy_arc_t {\n" -" vec2 p0;\n" -" vec2 p1;\n" -" float d;\n" -"};\n" -"\n" -"struct glyphy_arc_endpoint_t {\n" -" /* Second arc endpoint */\n" -" vec2 p;\n" -" /* Infinity if this endpoint does not form an arc with the previous\n" -" * endpoint. Ie. a \"move_to\". Test with glyphy_isinf().\n" -" * Arc depth otherwise. */\n" -" float d;\n" -"};\n" -"\n" -"struct glyphy_arc_list_t {\n" -" /* Number of endpoints in the list.\n" -" * Will be zero if we're far away inside or outside, in which case side is set.\n" -" * Will be -1 if this arc-list encodes a single line, in which case line_* are set. */\n" -" int num_endpoints;\n" -"\n" -" /* If num_endpoints is zero, this specifies whether we are inside (-1)\n" -" * or outside (+1). Otherwise we're unsure (0). */\n" -" int side;\n" -" /* Offset to the arc-endpoints from the beginning of the glyph blob */\n" -" int offset;\n" -"\n" -" /* A single line is all we care about. It's right here. */\n" -" float line_angle;\n" -" float line_distance; /* From nominal glyph center */\n" -"};\n" -"\n" -"bool\n" -"glyphy_isinf (const float v)\n" -"{\n" -" return abs (v) >= GLYPHY_INFINITY * .5;\n" -"}\n" -"\n" -"bool\n" -"glyphy_iszero (const float v)\n" -"{\n" -" return abs (v) <= GLYPHY_EPSILON * 2.;\n" -"}\n" -"\n" -"vec2\n" -"glyphy_ortho (const vec2 v)\n" -"{\n" -" return vec2 (-v.y, v.x);\n" -"}\n" -"\n" -"int\n" -"glyphy_float_to_byte (const float v)\n" -"{\n" -" return int (v * (256. - GLYPHY_EPSILON));\n" -"}\n" -"\n" -"ivec4\n" -"glyphy_vec4_to_bytes (const vec4 v)\n" -"{\n" -" return ivec4 (v * (256. - GLYPHY_EPSILON));\n" -"}\n" -"\n" -"ivec2\n" -"glyphy_float_to_two_nimbles (const float v)\n" -"{\n" -" int f = glyphy_float_to_byte (v);\n" -" return ivec2 (f / 16, int(mod (float(f), 16.)));\n" -"}\n" -"\n" -"/* returns tan (2 * atan (d)) */\n" -"float\n" -"glyphy_tan2atan (const float d)\n" -"{\n" -" return 2. * d / (1. - d * d);\n" -"}\n" -"\n" -"glyphy_arc_endpoint_t\n" -"glyphy_arc_endpoint_decode (const vec4 v, const ivec2 nominal_size)\n" -"{\n" -" vec2 p = (vec2 (glyphy_float_to_two_nimbles (v.a)) + v.gb) / 16.;\n" -" float d = v.r;\n" -" if (d == 0.)\n" -" d = GLYPHY_INFINITY;\n" -" else\n" -"#define GLYPHY_MAX_D .5\n" -" d = float(glyphy_float_to_byte (d) - 128) * GLYPHY_MAX_D / 127.;\n" -"#undef GLYPHY_MAX_D\n" -" return glyphy_arc_endpoint_t (p * vec2(nominal_size), d);\n" -"}\n" -"\n" -"vec2\n" -"glyphy_arc_center (const glyphy_arc_t a)\n" -"{\n" -" return mix (a.p0, a.p1, .5) +\n" -" glyphy_ortho (a.p1 - a.p0) / (2. * glyphy_tan2atan (a.d));\n" -"}\n" -"\n" -"bool\n" -"glyphy_arc_wedge_contains (const glyphy_arc_t a, const vec2 p)\n" -"{\n" -" float d2 = glyphy_tan2atan (a.d);\n" -" return dot (p - a.p0, (a.p1 - a.p0) * mat2(1, d2, -d2, 1)) >= 0. &&\n" -" dot (p - a.p1, (a.p1 - a.p0) * mat2(1, -d2, d2, 1)) <= 0.;\n" -"}\n" -"\n" -"float\n" -"glyphy_arc_wedge_signed_dist_shallow (const glyphy_arc_t a, const vec2 p)\n" -"{\n" -" vec2 v = normalize (a.p1 - a.p0);\n" -" float line_d = dot (p - a.p0, glyphy_ortho (v));\n" -" if (a.d == 0.)\n" -" return line_d;\n" -"\n" -" float d0 = dot ((p - a.p0), v);\n" -" if (d0 < 0.)\n" -" return sign (line_d) * distance (p, a.p0);\n" -" float d1 = dot ((a.p1 - p), v);\n" -" if (d1 < 0.)\n" -" return sign (line_d) * distance (p, a.p1);\n" -" float r = 2. * a.d * (d0 * d1) / (d0 + d1);\n" -" if (r * line_d > 0.)\n" -" return sign (line_d) * min (abs (line_d + r), min (distance (p, a.p0), distance (p, a.p1)));\n" -" return line_d + r;\n" -"}\n" -"\n" -"float\n" -"glyphy_arc_wedge_signed_dist (const glyphy_arc_t a, const vec2 p)\n" -"{\n" -" if (abs (a.d) <= .03)\n" -" return glyphy_arc_wedge_signed_dist_shallow (a, p);\n" -" vec2 c = glyphy_arc_center (a);\n" -" return sign (a.d) * (distance (a.p0, c) - distance (p, c));\n" -"}\n" -"\n" -"float\n" -"glyphy_arc_extended_dist (const glyphy_arc_t a, const vec2 p)\n" -"{\n" -" /* Note: this doesn't handle points inside the wedge. */\n" -" vec2 m = mix (a.p0, a.p1, .5);\n" -" float d2 = glyphy_tan2atan (a.d);\n" -" if (dot (p - m, a.p1 - m) < 0.)\n" -" return dot (p - a.p0, normalize ((a.p1 - a.p0) * mat2(+d2, -1, +1, +d2)));\n" -" else\n" -" return dot (p - a.p1, normalize ((a.p1 - a.p0) * mat2(-d2, -1, +1, -d2)));\n" -"}\n" -"\n" -"int\n" -"glyphy_arc_list_offset (const vec2 p, const ivec2 nominal_size)\n" -"{\n" -" ivec2 cell = ivec2 (clamp (floor (p), vec2 (0.,0.), vec2(nominal_size - 1)));\n" -" return cell.y * nominal_size.x + cell.x;\n" -"}\n" -"\n" -"glyphy_arc_list_t\n" -"glyphy_arc_list_decode (const vec4 v, const ivec2 nominal_size)\n" -"{\n" -" glyphy_arc_list_t l;\n" -" ivec4 iv = glyphy_vec4_to_bytes (v);\n" -" l.side = 0; /* unsure */\n" -" if (iv.r == 0) { /* arc-list encoded */\n" -" l.offset = (iv.g * 256) + iv.b;\n" -" l.num_endpoints = iv.a;\n" -" if (l.num_endpoints == 255) {\n" -" l.num_endpoints = 0;\n" -" l.side = -1;\n" -" } else if (l.num_endpoints == 0)\n" -" l.side = +1;\n" -" } else { /* single line encoded */\n" -" l.num_endpoints = -1;\n" -" l.line_distance = float(((iv.r - 128) * 256 + iv.g) - 0x4000) / float (0x1FFF)\n" -" * max (float (nominal_size.x), float (nominal_size.y));\n" -" l.line_angle = float(-((iv.b * 256 + iv.a) - 0x8000)) / float (0x7FFF) * 3.14159265358979;\n" -" }\n" -" return l;\n" -"}\n" -; diff --git a/src/glyphy-common.glsl b/src/glyphy-common.glsl deleted file mode 100644 index 792b1cff..00000000 --- a/src/glyphy-common.glsl +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright 2012 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju - */ - - -#ifndef GLYPHY_INFINITY -# define GLYPHY_INFINITY 1e9 -#endif -#ifndef GLYPHY_EPSILON -# define GLYPHY_EPSILON 1e-5 -#endif - -#ifndef GLYPHY_RGBA -# ifdef GLYPHY_BGRA -# define GLYPHY_RGBA(v) glyphy_bgra (v) -# else -# define GLYPHY_RGBA(v) glyphy_rgba (v) -# endif -#endif - -vec4 -glyphy_rgba (const vec4 v) -{ - return v.rgba; -} - -vec4 -glyphy_bgra (const vec4 v) -{ - return v.bgra; -} - - -struct glyphy_arc_t { - vec2 p0; - vec2 p1; - float d; -}; - -struct glyphy_arc_endpoint_t { - /* Second arc endpoint */ - vec2 p; - /* Infinity if this endpoint does not form an arc with the previous - * endpoint. Ie. a "move_to". Test with glyphy_isinf(). - * Arc depth otherwise. */ - float d; -}; - -struct glyphy_arc_list_t { - /* Number of endpoints in the list. - * Will be zero if we're far away inside or outside, in which case side is set. - * Will be -1 if this arc-list encodes a single line, in which case line_* are set. */ - int num_endpoints; - - /* If num_endpoints is zero, this specifies whether we are inside (-1) - * or outside (+1). Otherwise we're unsure (0). */ - int side; - /* Offset to the arc-endpoints from the beginning of the glyph blob */ - int offset; - - /* A single line is all we care about. It's right here. */ - float line_angle; - float line_distance; /* From nominal glyph center */ -}; - -bool -glyphy_isinf (const float v) -{ - return abs (v) >= GLYPHY_INFINITY * .5; -} - -bool -glyphy_iszero (const float v) -{ - return abs (v) <= GLYPHY_EPSILON * 2.; -} - -vec2 -glyphy_ortho (const vec2 v) -{ - return vec2 (-v.y, v.x); -} - -int -glyphy_float_to_byte (const float v) -{ - return int (v * (256. - GLYPHY_EPSILON)); -} - -ivec4 -glyphy_vec4_to_bytes (const vec4 v) -{ - return ivec4 (v * (256. - GLYPHY_EPSILON)); -} - -ivec2 -glyphy_float_to_two_nimbles (const float v) -{ - int f = glyphy_float_to_byte (v); - return ivec2 (f / 16, int(mod (float(f), 16.))); -} - -/* returns tan (2 * atan (d)) */ -float -glyphy_tan2atan (const float d) -{ - return 2. * d / (1. - d * d); -} - -glyphy_arc_endpoint_t -glyphy_arc_endpoint_decode (const vec4 v, const ivec2 nominal_size) -{ - vec2 p = (vec2 (glyphy_float_to_two_nimbles (v.a)) + v.gb) / 16.; - float d = v.r; - if (d == 0.) - d = GLYPHY_INFINITY; - else -#define GLYPHY_MAX_D .5 - d = float(glyphy_float_to_byte (d) - 128) * GLYPHY_MAX_D / 127.; -#undef GLYPHY_MAX_D - return glyphy_arc_endpoint_t (p * vec2(nominal_size), d); -} - -vec2 -glyphy_arc_center (const glyphy_arc_t a) -{ - return mix (a.p0, a.p1, .5) + - glyphy_ortho (a.p1 - a.p0) / (2. * glyphy_tan2atan (a.d)); -} - -bool -glyphy_arc_wedge_contains (const glyphy_arc_t a, const vec2 p) -{ - float d2 = glyphy_tan2atan (a.d); - return dot (p - a.p0, (a.p1 - a.p0) * mat2(1, d2, -d2, 1)) >= 0. && - dot (p - a.p1, (a.p1 - a.p0) * mat2(1, -d2, d2, 1)) <= 0.; -} - -float -glyphy_arc_wedge_signed_dist_shallow (const glyphy_arc_t a, const vec2 p) -{ - vec2 v = normalize (a.p1 - a.p0); - float line_d = dot (p - a.p0, glyphy_ortho (v)); - if (a.d == 0.) - return line_d; - - float d0 = dot ((p - a.p0), v); - if (d0 < 0.) - return sign (line_d) * distance (p, a.p0); - float d1 = dot ((a.p1 - p), v); - if (d1 < 0.) - return sign (line_d) * distance (p, a.p1); - float r = 2. * a.d * (d0 * d1) / (d0 + d1); - if (r * line_d > 0.) - return sign (line_d) * min (abs (line_d + r), min (distance (p, a.p0), distance (p, a.p1))); - return line_d + r; -} - -float -glyphy_arc_wedge_signed_dist (const glyphy_arc_t a, const vec2 p) -{ - if (abs (a.d) <= .03) - return glyphy_arc_wedge_signed_dist_shallow (a, p); - vec2 c = glyphy_arc_center (a); - return sign (a.d) * (distance (a.p0, c) - distance (p, c)); -} - -float -glyphy_arc_extended_dist (const glyphy_arc_t a, const vec2 p) -{ - /* Note: this doesn't handle points inside the wedge. */ - vec2 m = mix (a.p0, a.p1, .5); - float d2 = glyphy_tan2atan (a.d); - if (dot (p - m, a.p1 - m) < 0.) - return dot (p - a.p0, normalize ((a.p1 - a.p0) * mat2(+d2, -1, +1, +d2))); - else - return dot (p - a.p1, normalize ((a.p1 - a.p0) * mat2(-d2, -1, +1, -d2))); -} - -int -glyphy_arc_list_offset (const vec2 p, const ivec2 nominal_size) -{ - ivec2 cell = ivec2 (clamp (floor (p), vec2 (0.,0.), vec2(nominal_size - 1))); - return cell.y * nominal_size.x + cell.x; -} - -glyphy_arc_list_t -glyphy_arc_list_decode (const vec4 v, const ivec2 nominal_size) -{ - glyphy_arc_list_t l; - ivec4 iv = glyphy_vec4_to_bytes (v); - l.side = 0; /* unsure */ - if (iv.r == 0) { /* arc-list encoded */ - l.offset = (iv.g * 256) + iv.b; - l.num_endpoints = iv.a; - if (l.num_endpoints == 255) { - l.num_endpoints = 0; - l.side = -1; - } else if (l.num_endpoints == 0) - l.side = +1; - } else { /* single line encoded */ - l.num_endpoints = -1; - l.line_distance = float(((iv.r - 128) * 256 + iv.g) - 0x4000) / float (0x1FFF) - * max (float (nominal_size.x), float (nominal_size.y)); - l.line_angle = float(-((iv.b * 256 + iv.a) - 0x8000)) / float (0x7FFF) * 3.14159265358979; - } - return l; -} diff --git a/src/glyphy-common.hh b/src/glyphy-common.hh deleted file mode 100644 index 7b9c2e66..00000000 --- a/src/glyphy-common.hh +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright 2012 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju - */ - -#ifndef GLYPHY_COMMON_HH -#define GLYPHY_COMMON_HH - -#include - -#include -#include -#include -#include -#include -#include -#include - -#ifndef GLYPHY_EPSILON -# define GLYPHY_EPSILON 1e-5 -#endif -#ifndef GLYPHY_INFINITY -# define GLYPHY_INFINITY INFINITY -#endif - - -static inline bool -iszero (double v) -{ - return fabs (v) < 2 * GLYPHY_EPSILON; -} - - -#define GLYPHY_MAX_D .5 - -#undef ARRAY_LENGTH -#define ARRAY_LENGTH(__array) ((signed int) (sizeof (__array) / sizeof (__array[0]))) - -#define _ASSERT_STATIC1(_line, _cond) typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1] -#define _ASSERT_STATIC0(_line, _cond) _ASSERT_STATIC1 (_line, (_cond)) -#define ASSERT_STATIC(_cond) _ASSERT_STATIC0 (__LINE__, (_cond)) - -#ifdef __ANDROID__ -#define log2(x) (log(x) / log(2.0)) -#endif - -#endif /* GLYPHY_COMMON_HH */ diff --git a/src/glyphy-curves.cc b/src/glyphy-curves.cc new file mode 100644 index 00000000..3f1a4954 --- /dev/null +++ b/src/glyphy-curves.cc @@ -0,0 +1,195 @@ +/* + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glyphy.h" + +#include + + +/* + * Accumulate quadratic Bezier curves from glyph outlines + */ + + +struct glyphy_curve_accumulator_t { + unsigned int refcount; + + glyphy_curve_accumulator_callback_t callback; + void *user_data; + + glyphy_point_t start_point; + glyphy_point_t current_point; + bool need_moveto; + unsigned int num_curves; + glyphy_bool_t success; +}; + + +glyphy_curve_accumulator_t * +glyphy_curve_accumulator_create (void) +{ + glyphy_curve_accumulator_t *acc = (glyphy_curve_accumulator_t *) calloc (1, sizeof (glyphy_curve_accumulator_t)); + acc->refcount = 1; + + acc->callback = NULL; + acc->user_data = NULL; + + glyphy_curve_accumulator_reset (acc); + + return acc; +} + +void +glyphy_curve_accumulator_reset (glyphy_curve_accumulator_t *acc) +{ + acc->start_point = acc->current_point = {0, 0}; + acc->need_moveto = true; + acc->num_curves = 0; + acc->success = true; +} + +void +glyphy_curve_accumulator_destroy (glyphy_curve_accumulator_t *acc) +{ + if (!acc || --acc->refcount) + return; + + free (acc); +} + +glyphy_curve_accumulator_t * +glyphy_curve_accumulator_reference (glyphy_curve_accumulator_t *acc) +{ + if (acc) + acc->refcount++; + return acc; +} + + +/* Configure acc */ + +void +glyphy_curve_accumulator_set_callback (glyphy_curve_accumulator_t *acc, + glyphy_curve_accumulator_callback_t callback, + void *user_data) +{ + acc->callback = callback; + acc->user_data = user_data; +} + +void +glyphy_curve_accumulator_get_callback (glyphy_curve_accumulator_t *acc, + glyphy_curve_accumulator_callback_t *callback, + void **user_data) +{ + *callback = acc->callback; + *user_data = acc->user_data; +} + + +/* Accumulation results */ + +unsigned int +glyphy_curve_accumulator_get_num_curves (glyphy_curve_accumulator_t *acc) +{ + return acc->num_curves; +} + +glyphy_bool_t +glyphy_curve_accumulator_successful (glyphy_curve_accumulator_t *acc) +{ + return acc->success; +} + + +/* Accumulate */ + +static void +emit (glyphy_curve_accumulator_t *acc, const glyphy_curve_t *curve) +{ + acc->success = acc->success && acc->callback (curve, acc->user_data); + if (acc->success) { + acc->num_curves++; + acc->current_point = curve->p3; + } +} + +static void +emit_conic (glyphy_curve_accumulator_t *acc, + const glyphy_point_t *p2, + const glyphy_point_t *p3) +{ + if (acc->current_point.x == p3->x && acc->current_point.y == p3->y) + return; + + if (acc->need_moveto) { + acc->start_point = acc->current_point; + acc->need_moveto = false; + } + + glyphy_curve_t curve = {acc->current_point, *p2, *p3}; + emit (acc, &curve); +} + + +void +glyphy_curve_accumulator_move_to (glyphy_curve_accumulator_t *acc, + const glyphy_point_t *p0) +{ + acc->need_moveto = true; + acc->current_point = *p0; +} + +void +glyphy_curve_accumulator_line_to (glyphy_curve_accumulator_t *acc, + const glyphy_point_t *p1) +{ + /* Line as degenerate quadratic: p2 = p1 (start point). + * This gives a = p3 - p1 (never near zero for non-horizontal lines), + * b = 0, avoiding float32 precision issues in the GPU solver. */ + glyphy_point_t p0 = acc->current_point; + emit_conic (acc, &p0, p1); +} + +void +glyphy_curve_accumulator_conic_to (glyphy_curve_accumulator_t *acc, + const glyphy_point_t *p1, + const glyphy_point_t *p2) +{ + emit_conic (acc, p1, p2); +} + +void +glyphy_curve_accumulator_close_path (glyphy_curve_accumulator_t *acc) +{ + if (!acc->need_moveto && + (acc->current_point.x != acc->start_point.x || + acc->current_point.y != acc->start_point.y)) + glyphy_curve_accumulator_line_to (acc, &acc->start_point); +} + + +/* + * Curve list extents + */ + +void +glyphy_curve_list_extents (const glyphy_curve_t *curves, + unsigned int num_curves, + glyphy_extents_t *extents) +{ + glyphy_extents_clear (extents); + for (unsigned int i = 0; i < num_curves; i++) { + const glyphy_curve_t &c = curves[i]; + /* Conservative: use control point bounding box */ + glyphy_extents_add (extents, &c.p1); + glyphy_extents_add (extents, &c.p2); + glyphy_extents_add (extents, &c.p3); + } +} diff --git a/src/glyphy-encode.cc b/src/glyphy-encode.cc new file mode 100644 index 00000000..2f3fd69d --- /dev/null +++ b/src/glyphy-encode.cc @@ -0,0 +1,494 @@ +/* + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "glyphy.h" + +#include +#include +#include +#include +#include + + +/* + * Encode quadratic Bezier curves into a blob for GPU rendering. + * + * Blob layout (single RGBA16I texture region): + * + * [H-band headers (num_hbands texels)] + * [V-band headers (num_vbands texels)] + * [Curve index lists (variable)] + * [Curve data (2 texels per curve)] + * + * Band header texel: + * R = curve count + * G = offset to descending curve index list (from blob start) + * B = offset to ascending curve index list (from blob start) + * A = split value for symmetric optimization + * + * Curve index texel: + * R = offset to curve data (from blob start) + * G = reserved + * B = reserved + * A = reserved + * + * Curve data (2 consecutive texels): + * Texel 0: R=p1.x, G=p1.y, B=p2.x, A=p2.y (int16, em-space * UNITS_PER_EM_UNIT) + * Texel 1: R=p3.x, G=p3.y, B=0, A=0 + * + * All offsets are 1D from blob start. The shader converts to 2D atlas + * coordinates using the atlas width, similar to Slug's CalcBandLoc. + */ + + +static int16_t +quantize (double v) +{ + return (int16_t) round (v * GLYPHY_UNITS_PER_EM_UNIT); +} + +static bool +quantize_fits_i16 (double v) +{ + double q = round (v * GLYPHY_UNITS_PER_EM_UNIT); + return q >= std::numeric_limits::min () && + q <= std::numeric_limits::max (); +} + +typedef struct +{ + double min_x; + double max_x; + double min_y; + double max_y; + bool is_horizontal; + bool is_vertical; + int hband_lo; + int hband_hi; + int vband_lo; + int vband_hi; +} curve_info_t; + +static curve_info_t +curve_info (const glyphy_curve_t *c) +{ + curve_info_t info; + + info.min_x = std::min (std::min (c->p1.x, c->p2.x), c->p3.x); + info.max_x = std::max (std::max (c->p1.x, c->p2.x), c->p3.x); + info.min_y = std::min (std::min (c->p1.y, c->p2.y), c->p3.y); + info.max_y = std::max (std::max (c->p1.y, c->p2.y), c->p3.y); + info.is_horizontal = c->p1.y == c->p2.y && c->p2.y == c->p3.y; + info.is_vertical = c->p1.x == c->p2.x && c->p2.x == c->p3.x; + info.hband_lo = 0; + info.hband_hi = -1; + info.vband_lo = 0; + info.vband_hi = -1; + + return info; +} + +struct glyphy_encoder_t +{ + std::vector curve_infos; + std::vector hband_curve_counts; + std::vector vband_curve_counts; + std::vector hband_offsets; + std::vector vband_offsets; + std::vector hband_curves; + std::vector hband_curves_asc; + std::vector vband_curves; + std::vector vband_curves_asc; + std::vector hband_cursors; + std::vector vband_cursors; + std::vector curve_texel_offset; +}; + +glyphy_encoder_t * +glyphy_encoder_create (void) +{ + return new glyphy_encoder_t; +} + +void +glyphy_encoder_destroy (glyphy_encoder_t *encoder) +{ + delete encoder; +} + + +glyphy_bool_t +glyphy_encoder_encode (glyphy_encoder_t *encoder, + const glyphy_curve_t *curves, + unsigned int num_curves, + glyphy_texel_t *blob, + unsigned int blob_size, + unsigned int *output_len, + glyphy_extents_t *extents) +{ + if (!encoder) + return false; + + if (num_curves == 0) { + glyphy_extents_clear (extents); + *output_len = 0; + return true; + } + + std::vector &curve_infos = encoder->curve_infos; + curve_infos.resize (num_curves); + glyphy_extents_clear (extents); + for (unsigned int i = 0; i < num_curves; i++) { + curve_infos[i] = curve_info (&curves[i]); + + if (i == 0) { + extents->min_x = curve_infos[i].min_x; + extents->max_x = curve_infos[i].max_x; + extents->min_y = curve_infos[i].min_y; + extents->max_y = curve_infos[i].max_y; + } else { + extents->min_x = std::min (extents->min_x, curve_infos[i].min_x); + extents->max_x = std::max (extents->max_x, curve_infos[i].max_x); + extents->min_y = std::min (extents->min_y, curve_infos[i].min_y); + extents->max_y = std::max (extents->max_y, curve_infos[i].max_y); + } + } + + /* Choose number of bands (capped at 16 per Slug paper) */ + unsigned int num_hbands = std::min (num_curves, 16u); + unsigned int num_vbands = std::min (num_curves, 16u); + num_hbands = std::max (num_hbands, 1u); + num_vbands = std::max (num_vbands, 1u); + + double height = extents->max_y - extents->min_y; + double width = extents->max_x - extents->min_x; + + if (height <= 0) num_hbands = 1; + if (width <= 0) num_vbands = 1; + + double hband_size = height / num_hbands; + double vband_size = width / num_vbands; + + std::vector &hband_curve_counts = encoder->hband_curve_counts; + std::vector &vband_curve_counts = encoder->vband_curve_counts; + hband_curve_counts.assign (num_hbands, 0); + vband_curve_counts.assign (num_vbands, 0); + + for (unsigned int i = 0; i < num_curves; i++) { + curve_info_t &info = curve_infos[i]; + + if (!info.is_horizontal) { + if (height > 0) { + info.hband_lo = (int) floor ((info.min_y - extents->min_y) / hband_size); + info.hband_hi = (int) floor ((info.max_y - extents->min_y) / hband_size); + info.hband_lo = std::max (info.hband_lo, 0); + info.hband_hi = std::min (info.hband_hi, (int) num_hbands - 1); + for (int b = info.hband_lo; b <= info.hband_hi; b++) + hband_curve_counts[b]++; + } else { + info.hband_lo = 0; + info.hband_hi = 0; + hband_curve_counts[0]++; + } + } + + if (!info.is_vertical) { + if (width > 0) { + info.vband_lo = (int) floor ((info.min_x - extents->min_x) / vband_size); + info.vband_hi = (int) floor ((info.max_x - extents->min_x) / vband_size); + info.vband_lo = std::max (info.vband_lo, 0); + info.vband_hi = std::min (info.vband_hi, (int) num_vbands - 1); + for (int b = info.vband_lo; b <= info.vband_hi; b++) + vband_curve_counts[b]++; + } else { + info.vband_lo = 0; + info.vband_hi = 0; + vband_curve_counts[0]++; + } + } + } + + std::vector &hband_offsets = encoder->hband_offsets; + std::vector &vband_offsets = encoder->vband_offsets; + hband_offsets.resize (num_hbands); + vband_offsets.resize (num_vbands); + unsigned int total_hband_indices = 0; + unsigned int total_vband_indices = 0; + + for (unsigned int b = 0; b < num_hbands; b++) { + hband_offsets[b] = total_hband_indices; + total_hband_indices += hband_curve_counts[b]; + } + + for (unsigned int b = 0; b < num_vbands; b++) { + vband_offsets[b] = total_vband_indices; + total_vband_indices += vband_curve_counts[b]; + } + + /* Assign curves to bands */ + std::vector &hband_curves = encoder->hband_curves; + std::vector &hband_curves_asc = encoder->hband_curves_asc; + std::vector &vband_curves = encoder->vband_curves; + std::vector &vband_curves_asc = encoder->vband_curves_asc; + std::vector &hband_cursors = encoder->hband_cursors; + std::vector &vband_cursors = encoder->vband_cursors; + hband_curves.resize (total_hband_indices); + hband_curves_asc.resize (total_hband_indices); + vband_curves.resize (total_vband_indices); + vband_curves_asc.resize (total_vband_indices); + hband_cursors = hband_offsets; + vband_cursors = vband_offsets; + + for (unsigned int i = 0; i < num_curves; i++) { + const curve_info_t &info = curve_infos[i]; + + /* Horizontal lines never intersect horizontal rays; + * vertical lines never intersect vertical rays. */ + + for (int b = info.hband_lo; b <= info.hband_hi; b++) { + unsigned int index = hband_cursors[b]++; + hband_curves[index] = i; + hband_curves_asc[index] = i; + } + + for (int b = info.vband_lo; b <= info.vband_hi; b++) { + unsigned int index = vband_cursors[b]++; + vband_curves[index] = i; + vband_curves_asc[index] = i; + } + } + + /* Build two sort orders per band for symmetric optimization. + * Descending max: for rightward/upward ray (current behavior). + * Ascending min: for leftward/downward ray. */ + for (unsigned int b = 0; b < num_hbands; b++) { + unsigned int off = hband_offsets[b]; + unsigned int count = hband_curve_counts[b]; + std::sort (hband_curves.begin () + off, hband_curves.begin () + off + count, + [&] (unsigned int a, unsigned int b) { + return curve_infos[a].max_x > curve_infos[b].max_x; + }); + std::sort (hband_curves_asc.begin () + off, hband_curves_asc.begin () + off + count, + [&] (unsigned int a, unsigned int b) { + return curve_infos[a].min_x < curve_infos[b].min_x; + }); + } + + for (unsigned int b = 0; b < num_vbands; b++) { + unsigned int off = vband_offsets[b]; + unsigned int count = vband_curve_counts[b]; + std::sort (vband_curves.begin () + off, vband_curves.begin () + off + count, + [&] (unsigned int a, unsigned int b) { + return curve_infos[a].max_y > curve_infos[b].max_y; + }); + std::sort (vband_curves_asc.begin () + off, vband_curves_asc.begin () + off + count, + [&] (unsigned int a, unsigned int b) { + return curve_infos[a].min_y < curve_infos[b].min_y; + }); + } + + /* Compute sizes -- two index lists per band */ + unsigned int total_curve_indices = (total_hband_indices + total_vband_indices) * 2; + + unsigned int header_len = 2; /* blob header: extents + band counts */ + /* Compute curve data size with shared endpoints. + * Adjacent curves in a contour share p3/p1: N+1 texels per contour. + * Layout per contour: (p1,p2) (p3/p1,p2) ... (p3,0) + * The shader reads curveLoc and curveLoc+1, same as before. */ + unsigned int num_contour_breaks = 0; + for (unsigned int i = 0; i + 1 < num_curves; i++) + if (curves[i].p3.x != curves[i + 1].p1.x || + curves[i].p3.y != curves[i + 1].p1.y) + num_contour_breaks++; + + /* With sharing: num_curves + (num_contour_breaks + 1) texels + * (one extra texel per contour for the final p3). */ + unsigned int curve_data_len = num_curves + num_contour_breaks + 1; + + unsigned int band_headers_len = num_hbands + num_vbands; + unsigned int total_len = header_len + band_headers_len + total_curve_indices + curve_data_len; + + if (total_len > blob_size) + return false; + + /* Offsets and counts are stored in signed 16-bit lanes in the atlas. */ + if (total_len - 1 > (unsigned int) std::numeric_limits::max ()) + return false; + + if (!quantize_fits_i16 (extents->min_x) || + !quantize_fits_i16 (extents->min_y) || + !quantize_fits_i16 (extents->max_x) || + !quantize_fits_i16 (extents->max_y)) + return false; + + unsigned int curve_data_offset = header_len + band_headers_len + total_curve_indices; + + /* Pack blob header */ + blob[0].r = quantize (extents->min_x); + blob[0].g = quantize (extents->min_y); + blob[0].b = quantize (extents->max_x); + blob[0].a = quantize (extents->max_y); + blob[1].r = (int16_t) num_hbands; + blob[1].g = (int16_t) num_vbands; + blob[1].b = 0; + blob[1].a = 0; + + /* Pack curve data with shared endpoints. + * Build curve_texel_offset[i] = texel offset for curve i's first texel. */ + std::vector &curve_texel_offset = encoder->curve_texel_offset; + curve_texel_offset.resize (num_curves); + unsigned int texel = curve_data_offset; + + for (unsigned int i = 0; i < num_curves; i++) { + bool contour_start = (i == 0 || + curves[i - 1].p3.x != curves[i].p1.x || + curves[i - 1].p3.y != curves[i].p1.y); + + if (contour_start) { + curve_texel_offset[i] = texel; + /* First curve in contour: write (p1, p2) */ + blob[texel].r = quantize (curves[i].p1.x); + blob[texel].g = quantize (curves[i].p1.y); + blob[texel].b = quantize (curves[i].p2.x); + blob[texel].a = quantize (curves[i].p2.y); + texel++; + } else { + /* Non-start curve: p12 is in the previous texel (p3_prev, p2) */ + curve_texel_offset[i] = texel - 1; + } + + /* Write (p3, p2_next) or (p3, 0) if last in contour */ + bool has_next = (i + 1 < num_curves && + curves[i].p3.x == curves[i + 1].p1.x && + curves[i].p3.y == curves[i + 1].p1.y); + + blob[texel].r = quantize (curves[i].p3.x); + blob[texel].g = quantize (curves[i].p3.y); + if (has_next) { + blob[texel].b = quantize (curves[i + 1].p2.x); + blob[texel].a = quantize (curves[i + 1].p2.y); + } else { + blob[texel].b = 0; + blob[texel].a = 0; + } + texel++; + } + + /* Pack band headers and curve indices. + * Band header: (count, desc_offset, asc_offset, split_value) + * All offsets are relative to blob start. */ + unsigned int index_offset = header_len + band_headers_len; + + for (unsigned int b = 0; b < num_hbands; b++) { + /* Per-band split: find x that minimizes max(left_count, right_count). + * Curves with max_x >= split are processed by rightward ray. + * Curves with min_x <= split are processed by leftward ray. + * Descending sort is by max_x, so try split at each max_x boundary. */ + int16_t hband_split; + { + unsigned int off = hband_offsets[b]; + unsigned int n = hband_curve_counts[b]; + unsigned int best_worst = n; + double best_split = (extents->min_x + extents->max_x) * 0.5; + unsigned int left_count = n; + for (unsigned int ci = 0; ci < n; ci++) { + double split = curve_infos[hband_curves[off + ci]].max_x; + unsigned int right_count = ci + 1; /* curves with max_x >= split */ + while (left_count && + curve_infos[hband_curves_asc[off + left_count - 1]].min_x > split) + left_count--; + unsigned int worst = std::max (right_count, left_count); + if (worst < best_worst) { + best_worst = worst; + best_split = split; + } + } + hband_split = quantize (best_split); + } + unsigned int hdr = header_len + b; + unsigned int desc_off = index_offset; + + for (unsigned int ci = 0; ci < hband_curve_counts[b]; ci++) { + blob[index_offset].r = (int16_t) curve_texel_offset[hband_curves[hband_offsets[b] + ci]]; + blob[index_offset].g = 0; + blob[index_offset].b = 0; + blob[index_offset].a = 0; + index_offset++; + } + + unsigned int asc_off = index_offset; + + for (unsigned int ci = 0; ci < hband_curve_counts[b]; ci++) { + blob[index_offset].r = (int16_t) curve_texel_offset[hband_curves_asc[hband_offsets[b] + ci]]; + blob[index_offset].g = 0; + blob[index_offset].b = 0; + blob[index_offset].a = 0; + index_offset++; + } + + blob[hdr].r = (int16_t) hband_curve_counts[b]; + blob[hdr].g = (int16_t) desc_off; + blob[hdr].b = (int16_t) asc_off; + blob[hdr].a = hband_split; + } + + for (unsigned int b = 0; b < num_vbands; b++) { + int16_t vband_split; + { + unsigned int off = vband_offsets[b]; + unsigned int n = vband_curve_counts[b]; + unsigned int best_worst = n; + double best_split = (extents->min_y + extents->max_y) * 0.5; + unsigned int left_count = n; + for (unsigned int ci = 0; ci < n; ci++) { + double split = curve_infos[vband_curves[off + ci]].max_y; + unsigned int right_count = ci + 1; + while (left_count && + curve_infos[vband_curves_asc[off + left_count - 1]].min_y > split) + left_count--; + unsigned int worst = std::max (right_count, left_count); + if (worst < best_worst) { + best_worst = worst; + best_split = split; + } + } + vband_split = quantize (best_split); + } + + unsigned int hdr = header_len + num_hbands + b; + unsigned int desc_off = index_offset; + + for (unsigned int ci = 0; ci < vband_curve_counts[b]; ci++) { + blob[index_offset].r = (int16_t) curve_texel_offset[vband_curves[vband_offsets[b] + ci]]; + blob[index_offset].g = 0; + blob[index_offset].b = 0; + blob[index_offset].a = 0; + index_offset++; + } + + unsigned int asc_off = index_offset; + + for (unsigned int ci = 0; ci < vband_curve_counts[b]; ci++) { + blob[index_offset].r = (int16_t) curve_texel_offset[vband_curves_asc[vband_offsets[b] + ci]]; + blob[index_offset].g = 0; + blob[index_offset].b = 0; + blob[index_offset].a = 0; + index_offset++; + } + + blob[hdr].r = (int16_t) vband_curve_counts[b]; + blob[hdr].g = (int16_t) desc_off; + blob[hdr].b = (int16_t) asc_off; + blob[hdr].a = vband_split; + } + + *output_len = total_len; + + return true; +} diff --git a/src/glyphy-extents.cc b/src/glyphy-extents.cc index 89ebfe2b..e467459f 100644 --- a/src/glyphy-extents.cc +++ b/src/glyphy-extents.cc @@ -1,41 +1,33 @@ /* + * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod */ #ifdef HAVE_CONFIG_H #include #endif -#include "glyphy-common.hh" +#include "glyphy.h" + +#include +#include void glyphy_extents_clear (glyphy_extents_t *extents) { - extents->min_x = GLYPHY_INFINITY; - extents->min_y = GLYPHY_INFINITY; - extents->max_x = -GLYPHY_INFINITY; - extents->max_y = -GLYPHY_INFINITY; + extents->min_x = INFINITY; + extents->min_y = INFINITY; + extents->max_x = -INFINITY; + extents->max_y = -INFINITY; } glyphy_bool_t glyphy_extents_is_empty (const glyphy_extents_t *extents) { - return isinf (extents->min_x); + return std::isinf (extents->min_x); } void diff --git a/src/glyphy-fragment.glsl b/src/glyphy-fragment.glsl new file mode 100644 index 00000000..504efc2c --- /dev/null +++ b/src/glyphy-fragment.glsl @@ -0,0 +1,200 @@ +/* + * Copyright 2026 Behdad Esfahbod. + * Copyright 2017 Eric Lengyel. + * Based on the Slug algorithm by Eric Lengyel: + * https://github.com/EricLengyel/Slug + */ + + +/* Requires GLSL 3.30 */ + + +#ifndef GLYPHY_UNITS_PER_EM_UNIT +#define GLYPHY_UNITS_PER_EM_UNIT 4 +#endif + +#define GLYPHY_INV_UNITS float(1.0 / float(GLYPHY_UNITS_PER_EM_UNIT)) + + +uniform isamplerBuffer u_atlas; + + +uint glyphy_calc_root_code (float y1, float y2, float y3) +{ + uint i1 = floatBitsToUint (y1) >> 31U; + uint i2 = floatBitsToUint (y2) >> 30U; + uint i3 = floatBitsToUint (y3) >> 29U; + + uint shift = (i2 & 2U) | (i1 & ~2U); + shift = (i3 & 4U) | (shift & ~4U); + + return (0x2E74U >> shift) & 0x0101U; +} + +vec2 glyphy_solve_horiz_poly (vec4 p12, vec2 p3) +{ + vec2 a = p12.xy - p12.zw * 2.0 + p3; + vec2 b = p12.xy - p12.zw; + float ra = 1.0 / a.y; + float rb = 0.5 / b.y; + + float d = sqrt (max (b.y * b.y - a.y * p12.y, 0.0)); + float t1 = (b.y - d) * ra; + float t2 = (b.y + d) * ra; + + if (abs (a.y) < 1.0 / 65536.0) + t1 = t2 = p12.y * rb; + + return vec2 ((a.x * t1 - b.x * 2.0) * t1 + p12.x, + (a.x * t2 - b.x * 2.0) * t2 + p12.x); +} + +vec2 glyphy_solve_vert_poly (vec4 p12, vec2 p3) +{ + vec2 a = p12.xy - p12.zw * 2.0 + p3; + vec2 b = p12.xy - p12.zw; + float ra = 1.0 / a.x; + float rb = 0.5 / b.x; + + float d = sqrt (max (b.x * b.x - a.x * p12.x, 0.0)); + float t1 = (b.x - d) * ra; + float t2 = (b.x + d) * ra; + + if (abs (a.x) < 1.0 / 65536.0) + t1 = t2 = p12.x * rb; + + return vec2 ((a.y * t1 - b.y * 2.0) * t1 + p12.y, + (a.y * t2 - b.y * 2.0) * t2 + p12.y); +} + +float glyphy_calc_coverage (float xcov, float ycov, float xwgt, float ywgt) +{ + float coverage = max (abs (xcov * xwgt + ycov * ywgt) / + max (xwgt + ywgt, 1.0 / 65536.0), + min (abs (xcov), abs (ycov))); + + return clamp (coverage, 0.0, 1.0); +} + +float glyphy_render (vec2 renderCoord, uint glyphLoc_) +{ + vec2 emsPerPixel = fwidth (renderCoord); + vec2 pixelsPerEm = 1.0 / emsPerPixel; + + int glyphLoc = int (glyphLoc_); + + /* Read blob header */ + ivec4 header0 = texelFetch (u_atlas, glyphLoc); + ivec4 header1 = texelFetch (u_atlas, glyphLoc + 1); + vec4 ext = vec4 (header0) * GLYPHY_INV_UNITS; /* min_x, min_y, max_x, max_y */ + int numHBands = header1.r; + int numVBands = header1.g; + + /* Compute band transform from extents */ + vec2 extSize = ext.zw - ext.xy; /* (width, height) */ + vec2 bandScale = vec2 (float (numVBands), float (numHBands)) / max (extSize, vec2 (1.0 / 65536.0)); + vec2 bandOffset = -ext.xy * bandScale; + + ivec2 bandIndex = clamp (ivec2 (renderCoord * bandScale + bandOffset), + ivec2 (0, 0), + ivec2 (numVBands - 1, numHBands - 1)); + + /* Skip past header (2 texels) */ + int bandBase = glyphLoc + 2; + + float xcov = 0.0; + float xwgt = 0.0; + + ivec4 hbandData = texelFetch (u_atlas, bandBase + bandIndex.y); + int hCurveCount = hbandData.r; + /* Symmetric: choose rightward (desc) or leftward (asc) sort */ + float hSplit = float (hbandData.a) * GLYPHY_INV_UNITS; + bool hLeftRay = (renderCoord.x < hSplit); + int hDataOffset = hLeftRay ? hbandData.b : hbandData.g; + + for (int ci = 0; ci < hCurveCount; ci++) + { + int curveOffset = texelFetch (u_atlas, glyphLoc + hDataOffset + ci).r; + + ivec4 raw12 = texelFetch (u_atlas, glyphLoc + curveOffset); + ivec4 raw3 = texelFetch (u_atlas, glyphLoc + curveOffset + 1); + + vec4 p12 = vec4 (raw12) * GLYPHY_INV_UNITS - vec4 (renderCoord, renderCoord); + vec2 p3 = vec2 (raw3.rg) * GLYPHY_INV_UNITS - renderCoord; + + if (hLeftRay) { + if (min (min (p12.x, p12.z), p3.x) * pixelsPerEm.x > 0.5) break; + } else { + if (max (max (p12.x, p12.z), p3.x) * pixelsPerEm.x < -0.5) break; + } + + uint code = glyphy_calc_root_code (p12.y, p12.w, p3.y); + if (code != 0U) + { + vec2 r = glyphy_solve_horiz_poly (p12, p3) * pixelsPerEm.x; + /* For leftward ray: saturate(0.5 - r) counts coverage from the left */ + vec2 cov = hLeftRay ? clamp (vec2 (0.5) - r, 0.0, 1.0) + : clamp (r + vec2 (0.5), 0.0, 1.0); + + if ((code & 1U) != 0U) + { + xcov += cov.x; + xwgt = max (xwgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0)); + } + + if (code > 1U) + { + xcov -= cov.y; + xwgt = max (xwgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0)); + } + } + } + + float ycov = 0.0; + float ywgt = 0.0; + + ivec4 vbandData = texelFetch (u_atlas, bandBase + numHBands + bandIndex.x); + int vCurveCount = vbandData.r; + float vSplit = float (vbandData.a) * GLYPHY_INV_UNITS; + bool vLeftRay = (renderCoord.y < vSplit); + int vDataOffset = vLeftRay ? vbandData.b : vbandData.g; + + for (int ci = 0; ci < vCurveCount; ci++) + { + int curveOffset = texelFetch (u_atlas, glyphLoc + vDataOffset + ci).r; + + ivec4 raw12 = texelFetch (u_atlas, glyphLoc + curveOffset); + ivec4 raw3 = texelFetch (u_atlas, glyphLoc + curveOffset + 1); + + vec4 p12 = vec4 (raw12) * GLYPHY_INV_UNITS - vec4 (renderCoord, renderCoord); + vec2 p3 = vec2 (raw3.rg) * GLYPHY_INV_UNITS - renderCoord; + + if (vLeftRay) { + if (min (min (p12.y, p12.w), p3.y) * pixelsPerEm.y > 0.5) break; + } else { + if (max (max (p12.y, p12.w), p3.y) * pixelsPerEm.y < -0.5) break; + } + + uint code = glyphy_calc_root_code (p12.x, p12.z, p3.x); + if (code != 0U) + { + vec2 r = glyphy_solve_vert_poly (p12, p3) * pixelsPerEm.y; + vec2 cov = vLeftRay ? clamp (vec2 (0.5) - r, 0.0, 1.0) + : clamp (r + vec2 (0.5), 0.0, 1.0); + + if ((code & 1U) != 0U) + { + ycov -= cov.x; + ywgt = max (ywgt, clamp (1.0 - abs (r.x) * 2.0, 0.0, 1.0)); + } + + if (code > 1U) + { + ycov += cov.y; + ywgt = max (ywgt, clamp (1.0 - abs (r.y) * 2.0, 0.0, 1.0)); + } + } + } + + return glyphy_calc_coverage (xcov, ycov, xwgt, ywgt); +} diff --git a/src/glyphy-freetype.h b/src/glyphy-freetype.h index 94474ac6..9631ed5e 100644 --- a/src/glyphy-freetype.h +++ b/src/glyphy-freetype.h @@ -1,19 +1,8 @@ /* + * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju */ /* Intentionally doesn't have include guards */ @@ -25,6 +14,8 @@ extern "C" { #endif +#include +#include #include #include FT_FREETYPE_H #include FT_OUTLINE_H @@ -43,47 +34,46 @@ extern "C" { static int glyphy_freetype(move_to) (FT_Vector *to, - glyphy_arc_accumulator_t *acc) + glyphy_curve_accumulator_t *acc) { glyphy_point_t p1 = {(double) to->x, (double) to->y}; - glyphy_arc_accumulator_close_path (acc); - glyphy_arc_accumulator_move_to (acc, &p1); - return glyphy_arc_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; + glyphy_curve_accumulator_close_path (acc); + glyphy_curve_accumulator_move_to (acc, &p1); + return glyphy_curve_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; } static int glyphy_freetype(line_to) (FT_Vector *to, - glyphy_arc_accumulator_t *acc) + glyphy_curve_accumulator_t *acc) { glyphy_point_t p1 = {(double) to->x, (double) to->y}; - glyphy_arc_accumulator_line_to (acc, &p1); - return glyphy_arc_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; + glyphy_curve_accumulator_line_to (acc, &p1); + return glyphy_curve_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; } static int glyphy_freetype(conic_to) (FT_Vector *control, FT_Vector *to, - glyphy_arc_accumulator_t *acc) + glyphy_curve_accumulator_t *acc) { glyphy_point_t p1 = {(double) control->x, (double) control->y}; glyphy_point_t p2 = {(double) to->x, (double) to->y}; - glyphy_arc_accumulator_conic_to (acc, &p1, &p2); - return glyphy_arc_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; + glyphy_curve_accumulator_conic_to (acc, &p1, &p2); + return glyphy_curve_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; } static int glyphy_freetype(cubic_to) (FT_Vector *control1, FT_Vector *control2, FT_Vector *to, - glyphy_arc_accumulator_t *acc) + glyphy_curve_accumulator_t *acc) { - glyphy_point_t p1 = {(double) control1->x, (double) control1->y}; - glyphy_point_t p2 = {(double) control2->x, (double) control2->y}; - glyphy_point_t p3 = {(double) to->x, (double) to->y}; - glyphy_arc_accumulator_cubic_to (acc, &p1, &p2, &p3); - return glyphy_arc_accumulator_successful (acc) ? FT_Err_Ok : FT_Err_Out_Of_Memory; + (void) control1; (void) control2; + (void) to; (void) acc; + fprintf (stderr, "glyphy: cubic outlines are unsupported currently\n"); + abort (); } static FT_Error -glyphy_freetype(outline_decompose) (const FT_Outline *outline, - glyphy_arc_accumulator_t *acc) +glyphy_freetype(outline_decompose) (const FT_Outline *outline, + glyphy_curve_accumulator_t *acc) { const FT_Outline_Funcs outline_funcs = { (FT_Outline_MoveToFunc) glyphy_freetype(move_to), diff --git a/src/glyphy-geometry.hh b/src/glyphy-geometry.hh deleted file mode 100644 index 3c608562..00000000 --- a/src/glyphy-geometry.hh +++ /dev/null @@ -1,742 +0,0 @@ -/* - * Copyright 2012,2013 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju - */ - -#ifndef GLYPHY_GEOMETRY_HH -#define GLYPHY_GEOMETRY_HH - -#include "glyphy-common.hh" - -namespace GLyphy { -namespace Geometry { - -template struct Pair; -struct Vector; -struct SignedVector; -struct Point; -struct Line; -struct Segment; -struct Arc; -struct Bezier; - -/* returns tan (2 * atan (d)) */ -inline double tan2atan (double d) { return 2 * d / (1 - d*d); } - -/* returns sin (2 * atan (d)) */ -inline double sin2atan (double d) { return 2 * d / (1 + d*d); } - -/* returns cos (2 * atan (d)) */ -inline double cos2atan (double d) { return (1 - d*d) / (1 + d*d); } - -template -struct Pair { - typedef Type ElementType; - - inline Pair (const Type &first_, const Type &second_) : first (first_), second (second_) {} - - Type first, second; -}; - -struct Point : glyphy_point_t { - inline Point (double x_, double y_) { x = x_; y = y_; } - inline explicit Point (const Vector &v); - inline Point (const glyphy_point_t &p) { *(glyphy_point_t *)this = p; } - - inline bool operator == (const Point &p) const; - inline bool operator != (const Point &p) const; - inline Point& operator+= (const Vector &v); - inline Point& operator-= (const Vector &v); - inline const Point operator+ (const Vector &v) const; - inline const Point operator- (const Vector &v) const; - inline const Vector operator- (const Point &p) const; - inline const Point midpoint (const Point &p) const; - inline const Line bisector (const Point &p) const; - inline double distance_to_point (const Point &p) const; /* distance to point! */ - inline double squared_distance_to_point (const Point &p) const; /* square of distance to point! */ - - inline bool is_finite (void) const; - inline const Point lerp (const double &a, const Point &p) const; -}; - -struct Vector { - inline Vector (double dx_, double dy_) : dx (dx_), dy (dy_) {} - inline explicit Vector (const Point &p) : dx (p.x), dy (p.y) {} - - inline bool operator == (const Vector &v) const; - inline bool operator != (const Vector &v) const; - inline const Vector operator+ (void) const; - inline const Vector operator- (void) const; - inline Vector& operator+= (const Vector &v); - inline Vector& operator-= (const Vector &v); - inline Vector& operator*= (const double &s); - inline Vector& operator/= (const double &s); - inline const Vector operator+ (const Vector &v) const; - inline const Vector operator- (const Vector &v) const; - inline const Vector operator* (const double &s) const; - inline const Vector operator/ (const double &s) const; - inline double operator* (const Vector &v) const; /* dot product */ - inline const Point operator+ (const Point &p) const; - - inline bool is_nonzero (void) const; - inline double len (void) const; - inline double len2 (void) const; - inline const Vector normalized (void) const; - inline const Vector ortho (void) const; - inline const Vector normal (void) const; /* ortho().normalized() */ - inline double angle (void) const; - - inline double cross (const Vector &other) const; - inline const Vector rebase (const Vector &bx, const Vector &by) const; - inline const Vector rebase (const Vector &bx) const; - - double dx, dy; -}; - -struct SignedVector : Vector { - inline SignedVector (const Vector &v, bool negative_) : Vector (v), negative (negative_) {} - - inline bool operator == (const SignedVector &v) const; - inline bool operator != (const SignedVector &v) const; - inline const SignedVector operator- (void) const; - - bool negative; -}; - -struct Line { - inline Line (double a_, double b_, double c_) : n (a_, b_), c (c_) {} - inline Line (Vector n_, double c_) : n (n_), c (c_) {} - inline Line (const Point &p0, const Point &p1) : - n ((p1 - p0).ortho ()), c (n * Vector (p0)) {} - - inline const Point operator+ (const Line &l) const; /* line intersection! */ - inline const SignedVector operator- (const Point &p) const; /* shortest vector from point to line */ - - - inline const Line normalized (void) const; - inline const Vector normal (void) const; - - Vector n; /* line normal */ - double c; /* n.dx*x + n.dy*y = c */ -}; - -struct Segment { - inline Segment (const Point &p0_, const Point &p1_) : - p0 (p0_), p1 (p1_) {} - - inline const SignedVector operator- (const Point &p) const; /* shortest vector from point to ***line*** */ - inline double distance_to_point (const Point &p) const; /* shortest distance from point to segment */ - inline double squared_distance_to_point (const Point &p) const; /* shortest distance squared from point to segment */ - inline bool contains_in_span (const Point &p) const; /* is p in the stripe formed by sliding this segment? */ - inline double max_distance_to_arc (const Arc &a) const; - - - Point p0; - Point p1; -}; - - - -struct Arc { - inline Arc (const Point &p0_, const Point &p1_, const Point &pm, bool complement) : - p0 (p0_), p1 (p1_), - d (p0_ == pm || p1_ == pm ? 0 : - tan (((p1_-pm).angle () - (p0_-pm).angle ()) / 2 - (complement ? 0 : M_PI_2))) {} - inline Arc (const Point &p0_, const Point &p1_, const double &d_) : - p0 (p0_), p1 (p1_), d (d_) {} - inline Arc (const Point ¢er, double radius, const double &a0, const double &a1, bool complement) : - p0 (center + Vector (cos(a0),sin(a0)) * radius), - p1 (center + Vector (cos(a1),sin(a1)) * radius), - d (tan ((a1 - a0) / 4 - (complement ? 0 : M_PI_2))) {} - inline Arc (const glyphy_arc_t &a) : p0 (a.p0), p1 (a.p1), d (a.d) {} - inline operator glyphy_arc_t (void) const { glyphy_arc_t a = {p0, p1, d}; return a; } - - inline bool operator == (const Arc &a) const; - inline bool operator != (const Arc &a) const; - inline const SignedVector operator- (const Point &p) const; /* shortest vector from point to arc */ - - inline double radius (void) const; - inline const Point center (void) const; - inline const Pair tangents (void) const; - - inline Bezier approximate_bezier (double *error) const; - - inline bool wedge_contains_point (const Point &p) const; - inline double distance_to_point (const Point &p) const; - inline double squared_distance_to_point (const Point &p) const; - inline double extended_dist (const Point &p) const; - - inline void extents (glyphy_extents_t &extents) const; - - Point p0, p1; - double d; /* Depth */ -}; - -struct Bezier { - inline Bezier (const Point &p0_, const Point &p1_, - const Point &p2_, const Point &p3_) : - p0 (p0_), p1 (p1_), p2 (p2_), p3 (p3_) {} - - inline const Point point (const double &t) const; - inline const Point midpoint (void) const; - inline const Vector tangent (const double &t) const; - inline const Vector d_tangent (const double &t) const; - inline double curvature (const double &t) const; - inline const Pair split (const double &t) const; - inline const Pair halve (void) const; - inline const Bezier segment (const double &t0, const double &t1) const; - - Point p0, p1, p2, p3; -}; - - -/* Implementations */ - - -/* Point */ - -inline Point::Point (const Vector &v) { - x = v.dx; - y = v.dy; -} -inline bool Point::operator == (const Point &p) const { - return x == p.x && y == p.y; -} -inline bool Point::operator != (const Point &p) const { - return !(*this == p); -} -inline Point& Point::operator+= (const Vector &v) { - x += v.dx; - y += v.dy; - return *this; -} -inline Point& Point::operator-= (const Vector &v) { - x -= v.dx; - y -= v.dy; - return *this; -} -inline const Point Point::operator+ (const Vector &v) const { - return Point (*this) += v; -} -inline const Point Point::operator- (const Vector &v) const { - return Point (*this) -= v; -} -inline const Vector Point::operator- (const Point &p) const { - return Vector (x - p.x, y - p.y); -} - -inline const Point Point::midpoint (const Point &p) const { - return *this + (p - *this) / 2; -} -inline const Line Point::bisector (const Point &p) const { - Vector d = p - *this; - return Line (d.dx * 2, d.dy * 2, d * Vector (p) + d * Vector (*this)); -} - -inline double Point::distance_to_point (const Point &p) const { - return ((*this) - p).len (); -} - -inline double Point::squared_distance_to_point (const Point &p) const { - return ((*this) - p).len2 (); -} - -inline bool Point::is_finite (void) const { - return isfinite (x) && isfinite (y); -} -inline const Point Point::lerp (const double &a, const Point &p) const { - /* The following two cases are special-cased to get better floating - * point stability. We require that points that are the same be - * bit-equal. */ - if (a == 0) return *this; - if (a == 1.0) return p; - return Point ((1-a) * x + a * p.x, (1-a) * y + a * p.y); -} - - -/* Vector */ - -inline bool Vector::operator == (const Vector &v) const { - return dx == v.dx && dy == v.dy; -} -inline bool Vector::operator != (const Vector &v) const { - return !(*this == v); -} -inline const Vector Vector::operator+ (void) const { - return *this; -} -inline const Vector Vector::operator- (void) const { - return Vector (-dx, -dy); -} -inline Vector& Vector::operator+= (const Vector &v) { - dx += v.dx; - dy += v.dy; - return *this; -} -inline Vector& Vector::operator-= (const Vector &v) { - dx -= v.dx; - dy -= v.dy; - return *this; -} -inline Vector& Vector::operator*= (const double &s) { - dx *= s; - dy *= s; - return *this; -} -inline Vector& Vector::operator/= (const double &s) { - dx /= s; - dy /= s; - return *this; -} -inline const Vector Vector::operator+ (const Vector &v) const { - return Vector (*this) += v; -} -inline const Vector Vector::operator- (const Vector &v) const { - return Vector (*this) -= v; -} -inline const Vector Vector::operator* (const double &s) const { - return Vector (*this) *= s; -} -inline const Vector operator* (const double &s, const Vector &v) { - return v * s; -} -inline const Vector Vector::operator/ (const double &s) const { - return Vector (*this) /= s; -} -inline double Vector::operator* (const Vector &v) const { /* dot product */ - return dx * v.dx + dy * v.dy; -} -inline const Point Vector::operator+ (const Point &p) const { - return p + *this; -} - -inline bool Vector::is_nonzero (void) const { - return dx || dy; -} -inline double Vector::len (void) const { - return hypot (dx, dy); -} -inline double Vector::len2 (void) const { - return dx * dx + dy * dy; -} -inline const Vector Vector::normalized (void) const { - double d = len (); - return d ? *this / d : *this; -} -inline const Vector Vector::ortho (void) const { - return Vector (-dy, dx); -} -inline const Vector Vector::normal (void) const { - return ortho ().normalized (); -} -inline double Vector::angle (void) const { - return atan2 (dy, dx); -} - -inline double Vector::cross (const Vector &other) const { - return dx * other.dy - dy * other.dx; -} -inline const Vector Vector::rebase (const Vector &bx, - const Vector &by) const { - return Vector (*this * bx, *this * by); -} -inline const Vector Vector::rebase (const Vector &bx) const { - return rebase (bx, bx.ortho ()); -} - - -/* SignedVector */ - -inline bool SignedVector::operator == (const SignedVector &v) const { - return (const Vector &)(*this) == (const Vector &)(v) && negative == v.negative; -} -inline bool SignedVector::operator != (const SignedVector &v) const { - return !(*this == v); -} -inline const SignedVector SignedVector::operator- (void) const { - return SignedVector (-(const Vector &)(*this), !negative); -} - - -/* Line */ - -inline const Point Line::operator+ (const Line &l) const { - double det = n.dx * l.n.dy - n.dy * l.n.dx; - if (!det) - return Point (GLYPHY_INFINITY, GLYPHY_INFINITY); - return Point ((c * l.n.dy - n.dy * l.c) / det, - (n.dx * l.c - c * l.n.dx) / det); -} -inline const SignedVector Line::operator- (const Point &p) const { - double mag = -(n * Vector (p) - c) / n.len (); - return SignedVector (n.normalized () * mag, mag < 0); /******************************************************************************************* FIX. *************************************/ -} - -inline const SignedVector operator- (const Point &p, const Line &l) { - return -(l - p); -} - -inline const Line Line::normalized (void) const { - double d = n.len (); - return d ? Line (n / d, c / d) : *this; -} -inline const Vector Line::normal (void) const { - return n; -} - -/* Segment */ -inline const SignedVector Segment::operator- (const Point &p) const { - /* shortest vector from point to line */ - return p - Line (p1, p0); /************************************************************************************************** Should the order (p1, p0) depend on d?? ***********************/ -} - -/* Segment */ -inline bool Segment::contains_in_span (const Point &p) const { - if (p0 == p1) - return false; - - /* shortest vector from point to line */ - Line temp (p0, p1); - double mag = -(temp.n * Vector (p) - temp.c) / temp.n.len (); - Vector y (temp.n.normalized () * mag); - Point z = y + p; - - // Check if z is between p0 and p1. - - if (fabs (p1.y - p0.y) > fabs (p1.x - p0.x)) { - return ((z.y - p0.y > 0 && p1.y - p0.y > z.y - p0.y) || - (z.y - p0.y < 0 && p1.y - p0.y < z.y - p0.y)); - } - else { - return ((0 < z.x - p0.x && z.x - p0.x < p1.x - p0.x) || - (0 > z.x - p0.x && z.x - p0.x > p1.x - p0.x)); - } -} - -inline double Segment::distance_to_point (const Point &p) const { - if (p0 == p1) - return 0; - - // Check if z is between p0 and p1. - Line temp (p0, p1); - if (contains_in_span (p)) - return -(temp.n * Vector (p) - temp.c) / temp.n.len (); - - double dist_p_p0 = p.distance_to_point (p0); - double dist_p_p1 = p.distance_to_point (p1); - return (dist_p_p0 < dist_p_p1 ? dist_p_p0 : dist_p_p1) * (-(temp.n * Vector (p) - temp.c) < 0 ? -1 : 1); -} - - -inline double Segment::squared_distance_to_point (const Point &p) const { - if (p0 == p1) - return 0; - - // Check if z is between p0 and p1. - Line temp (p0, p1); - if (contains_in_span (p)) - return (temp.n * Vector (p) - temp.c) * (temp.n * Vector (p) - temp.c) / (temp.n * temp.n); - - double dist_p_p0 = p.squared_distance_to_point (p0); - double dist_p_p1 = p.squared_distance_to_point (p1); - return (dist_p_p0 < dist_p_p1 ? dist_p_p0 : dist_p_p1); -} - - -inline double Segment::max_distance_to_arc (const Arc &a) const { - double max_distance = fabs(a.distance_to_point(p0)) ; - return max_distance > fabs(a.distance_to_point(p1)) ? max_distance : fabs(a.distance_to_point(p1)) ; -} - - - -/* Arc */ - -inline bool Arc::operator == (const Arc &a) const { - return p0 == a.p0 && p1 == a.p1 && d == a.d; -} -inline bool Arc::operator != (const Arc &a) const { - return !(*this == a); -} - - -inline const SignedVector Arc::operator- (const Point &p) const { - - if (fabs(d) < 1e-5) { - Segment arc_segment (p0, p1); - return arc_segment - p; - } - if (wedge_contains_point (p)){ - Vector difference = (center () - p).normalized () * fabs (p.distance_to_point (center ()) - radius ()); - - return SignedVector (difference, ((p - center ()).len () < radius ()) ^ (d < 0)); - } - double d0 = p.squared_distance_to_point (p0); - double d1 = p.squared_distance_to_point (p1); - - Arc other_arc (p0, p1, (1.0 + d) / (1.0 - d)); /********************************* NOT Robust. But works? *****************/ - Vector normal = center () - (d0 < d1 ? p0 : p1) ; - - if (normal.len() == 0) - return SignedVector (Vector (0, 0), true); /************************************ Check sign of this S.D. *************/ - - return SignedVector (Line (normal.dx, normal.dy, normal * Vector ((d0 < d1 ? p0 : p1))) - p, !other_arc.wedge_contains_point(p)); -} - -inline const SignedVector operator- (const Point &p, const Arc &a) { - return -(a - p); -} - - - -inline double Arc::radius (void) const -{ - return fabs ((p1 - p0).len () / (2 * sin2atan (d))); -} - -inline const Point Arc::center (void) const -{ - return (p0.midpoint (p1)) + (p1 - p0).ortho () / (2 * tan2atan (d)); -} - -inline const Pair Arc::tangents (void) const -{ - Vector dp = (p1 - p0) * .5; - Vector pp = dp.ortho () * -sin2atan (d); - dp = dp * cos2atan (d); - return Pair (dp + pp, dp - pp); -} - - - -inline Bezier Arc::approximate_bezier (double *error) const -{ - Vector dp = p1 - p0; - Vector pp = dp.ortho (); - - if (error) - *error = dp.len () * pow (fabs (d), 5) / (54 * (1 + d*d)); - - dp *= ((1 - d*d) / 3); - pp *= (2 * d / 3); - - Point p0s = p0 + dp - pp; - Point p1s = p1 - dp - pp; - - return Bezier (p0, p0s, p1s, p1); -} - - -inline bool Arc::wedge_contains_point (const Point &p) const -{ - Pair t = tangents (); - if (fabs (d) <= 1) - return (p - p0) * t.first >= 0 && (p - p1) * t.second <= 0; - else - return (p - p0) * t.first >= 0 || (p - p1) * t.second <= 0; -} - - -/* Distance may not always be positive, but will be to an endpoint whenever necessary. */ -inline double Arc::distance_to_point (const Point &p) const { - if (fabs(d) < 1e-5) { - Segment arc_segment (p0, p1); - return arc_segment.distance_to_point (p); - } - - SignedVector difference = *this - p; - - if (wedge_contains_point (p) && fabs(d) > 1e-5) - return fabs (p.distance_to_point (center ()) - radius ()) * (difference.negative ? -1 : 1); - double d1 = p.squared_distance_to_point (p0); - double d2 = p.squared_distance_to_point (p1); - return (d1 < d2 ? sqrt(d1) : sqrt(d2)) * (difference.negative ? -1 : 1); -} - -/* Distance will be to an endpoint whenever necessary. */ -inline double Arc::squared_distance_to_point (const Point &p) const { - if (fabs(d) < 1e-5) { - Segment arc_segment (p0, p1); - return arc_segment.squared_distance_to_point (p); - } - - //SignedVector difference = *this - p; - - if (wedge_contains_point (p) && fabs(d) > 1e-5) { - double answer = p.distance_to_point (center ()) - radius (); - return answer * answer; - } - double d1 = p.squared_distance_to_point (p0); - double d2 = p.squared_distance_to_point (p1); - return (d1 < d2 ? d1 : d2); -} - -inline double Arc::extended_dist (const Point &p) const { - Point m = p0.lerp (.5, p1); - Vector dp = p1 - p0; - Vector pp = dp.ortho (); - float d2 = tan2atan (d); - if ((p - m) * (p1 - m) < 0) - return (p - p0) * (pp + dp * d2).normalized (); - else - return (p - p1) * (pp - dp * d2).normalized (); -} - -inline void Arc::extents (glyphy_extents_t &extents) const { - glyphy_extents_clear (&extents); - glyphy_extents_add (&extents, &p0); - glyphy_extents_add (&extents, &p1); - Point c = center (); - double r = radius (); - Point p[4] = {c + r * Vector (-1, 0), - c + r * Vector (+1, 0), - c + r * Vector ( 0, -1), - c + r * Vector ( 0, +1)}; - for (unsigned int i = 0; i < 4; i++) - if (wedge_contains_point (p[i])) - glyphy_extents_add (&extents, &p[i]); -} - - -/* Bezier */ - -inline const Point Bezier::point (const double &t) const { - Point p01 = p0.lerp (t, p1); - Point p12 = p1.lerp (t, p2); - Point p23 = p2.lerp (t, p3); - Point p012 = p01.lerp (t, p12); - Point p123 = p12.lerp (t, p23); - Point p0123 = p012.lerp (t, p123); - return p0123; -} - -inline const Point Bezier::midpoint (void) const -{ - Point p01 = p0.midpoint (p1); - Point p12 = p1.midpoint (p2); - Point p23 = p2.midpoint (p3); - Point p012 = p01.midpoint (p12); - Point p123 = p12.midpoint (p23); - Point p0123 = p012.midpoint (p123); - return p0123; -} - -inline const Vector Bezier::tangent (const double &t) const -{ - double t_2_0 = t * t; - double t_0_2 = (1 - t) * (1 - t); - - double _1__4t_1_0_3t_2_0 = 1 - 4 * t + 3 * t_2_0; - double _2t_1_0_3t_2_0 = 2 * t - 3 * t_2_0; - - return Vector (-3 * p0.x * t_0_2 - +3 * p1.x * _1__4t_1_0_3t_2_0 - +3 * p2.x * _2t_1_0_3t_2_0 - +3 * p3.x * t_2_0, - -3 * p0.y * t_0_2 - +3 * p1.y * _1__4t_1_0_3t_2_0 - +3 * p2.y * _2t_1_0_3t_2_0 - +3 * p3.y * t_2_0); -} - -inline const Vector Bezier::d_tangent (const double &t) const { - return Vector (6 * ((-p0.x + 3*p1.x - 3*p2.x + p3.x) * t + (p0.x - 2*p1.x + p2.x)), - 6 * ((-p0.y + 3*p1.y - 3*p2.y + p3.y) * t + (p0.y - 2*p1.y + p2.y))); -} - -inline double Bezier::curvature (const double &t) const { - Vector dpp = tangent (t).ortho (); - Vector ddp = d_tangent (t); - /* normal vector len squared */ - double len = dpp.len (); - double curvature = (dpp * ddp) / (len * len * len); - return curvature; -} - -inline const Pair Bezier::split (const double &t) const { - Point p01 = p0.lerp (t, p1); - Point p12 = p1.lerp (t, p2); - Point p23 = p2.lerp (t, p3); - Point p012 = p01.lerp (t, p12); - Point p123 = p12.lerp (t, p23); - Point p0123 = p012.lerp (t, p123); - return Pair (Bezier (p0, p01, p012, p0123), - Bezier (p0123, p123, p23, p3)); -} - -inline const Pair Bezier::halve (void) const -{ - Point p01 = p0.midpoint (p1); - Point p12 = p1.midpoint (p2); - Point p23 = p2.midpoint (p3); - Point p012 = p01.midpoint (p12); - Point p123 = p12.midpoint (p23); - Point p0123 = p012.midpoint (p123); - return Pair (Bezier (p0, p01, p012, p0123), - Bezier (p0123, p123, p23, p3)); -} - -inline const Bezier Bezier::segment (const double &t0, const double &t1) const -{ - Point p01 = p0.lerp (t0, p1); - Point p12 = p1.lerp (t0, p2); - Point p23 = p2.lerp (t0, p3); - Point p012 = p01.lerp (t0, p12); - Point p123 = p12.lerp (t0, p23); - Point p0123 = p012.lerp (t0, p123); - - Point q01 = p0.lerp (t1, p1); - Point q12 = p1.lerp (t1, p2); - Point q23 = p2.lerp (t1, p3); - Point q012 = q01.lerp (t1, q12); - Point q123 = q12.lerp (t1, q23); - Point q0123 = q012.lerp (t1, q123); - - return Bezier (p0123, - p0123 + (p123 - p0123) * ((t1 - t0) / (1 - t0)), - q0123 + (q012 - q0123) * ((t1 - t0) / t1), - q0123); -} - - -/* insertion operator */ - - -static inline std::ostream& operator<<(std::ostream& os, const Point& p) -{ - os << "Point(" << p.x << "," << p.y << ")"; - return os; -} -static inline std::ostream& operator<<(std::ostream& os, const Vector& v) -{ - os << "Vector(" << v.dx << "," << v.dy << ")"; - return os; -} -static inline std::ostream& operator<<(std::ostream& os, const Arc& a) -{ - os << "Arc(" << a.p0 << ", " << a.p1 << ", " << a.d << ")"; - return os; -} -static inline std::ostream& operator<<(std::ostream& os, const Bezier& b) -{ - os << "Bezier(" << b.p0 << ", " << b.p1 << ", " << b.p2 << ", " << b.p3 << ")"; - return os; -} - -} /* namespace Geometry */ -} /* namespace GLyphy */ - -#endif /* GLYPHY_GEOMETRY_HH */ diff --git a/src/glyphy-harfbuzz.h b/src/glyphy-harfbuzz.h index b2a8cfc4..b43f69f3 100644 --- a/src/glyphy-harfbuzz.h +++ b/src/glyphy-harfbuzz.h @@ -1,17 +1,6 @@ /* * Copyright 2022 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. */ /* Intentionally doesn't have include guards */ @@ -23,6 +12,8 @@ extern "C" { #endif +#include +#include #include @@ -39,30 +30,30 @@ extern "C" { static void glyphy_harfbuzz(move_to) (hb_draw_funcs_t *dfuncs, - glyphy_arc_accumulator_t *acc, + glyphy_curve_accumulator_t *acc, hb_draw_state_t *st, float to_x, float to_y, void *user_data) { glyphy_point_t p1 = {(double) to_x, (double) to_y}; - glyphy_arc_accumulator_close_path (acc); - glyphy_arc_accumulator_move_to (acc, &p1); + glyphy_curve_accumulator_close_path (acc); + glyphy_curve_accumulator_move_to (acc, &p1); } static void glyphy_harfbuzz(line_to) (hb_draw_funcs_t *dfuncs, - glyphy_arc_accumulator_t *acc, + glyphy_curve_accumulator_t *acc, hb_draw_state_t *st, float to_x, float to_y, void *user_data) { glyphy_point_t p1 = {(double) to_x, (double) to_y}; - glyphy_arc_accumulator_line_to (acc, &p1); + glyphy_curve_accumulator_line_to (acc, &p1); } static void glyphy_harfbuzz(quadratic_to) (hb_draw_funcs_t *dfuncs, - glyphy_arc_accumulator_t *acc, + glyphy_curve_accumulator_t *acc, hb_draw_state_t *st, float control_x, float control_y, float to_x, float to_y, @@ -70,22 +61,34 @@ glyphy_harfbuzz(quadratic_to) (hb_draw_funcs_t *dfuncs, { glyphy_point_t p1 = {(double) control_x, (double) control_y}; glyphy_point_t p2 = {(double) to_x, (double) to_y}; - glyphy_arc_accumulator_conic_to (acc, &p1, &p2); + glyphy_curve_accumulator_conic_to (acc, &p1, &p2); } static void glyphy_harfbuzz(cubic_to) (hb_draw_funcs_t *dfuncs, - glyphy_arc_accumulator_t *acc, + glyphy_curve_accumulator_t *acc, hb_draw_state_t *st, float control1_x, float control1_y, float control2_x, float control2_y, float to_x, float to_y, void *user_data) { - glyphy_point_t p1 = {(double) control1_x, (double) control1_y}; - glyphy_point_t p2 = {(double) control2_x, (double) control2_y}; - glyphy_point_t p3 = {(double) to_x, (double) to_y}; - glyphy_arc_accumulator_cubic_to (acc, &p1, &p2, &p3); + (void) dfuncs; (void) acc; (void) st; + (void) control1_x; (void) control1_y; + (void) control2_x; (void) control2_y; + (void) to_x; (void) to_y; + (void) user_data; + fprintf (stderr, "glyphy: cubic outlines are unsupported currently\n"); + abort (); +} + +static void +glyphy_harfbuzz(close_path) (hb_draw_funcs_t *dfuncs, + glyphy_curve_accumulator_t *acc, + hb_draw_state_t *st, + void *user_data) +{ + glyphy_curve_accumulator_close_path (acc); } static hb_draw_funcs_t * @@ -100,6 +103,7 @@ glyphy_harfbuzz(get_draw_funcs) (void) hb_draw_funcs_set_line_to_func (dfuncs, (hb_draw_line_to_func_t) glyphy_harfbuzz(line_to), NULL, NULL); hb_draw_funcs_set_quadratic_to_func (dfuncs, (hb_draw_quadratic_to_func_t) glyphy_harfbuzz(quadratic_to), NULL, NULL); hb_draw_funcs_set_cubic_to_func (dfuncs, (hb_draw_cubic_to_func_t) glyphy_harfbuzz(cubic_to), NULL, NULL); + hb_draw_funcs_set_close_path_func (dfuncs, (hb_draw_close_path_func_t) glyphy_harfbuzz(close_path), NULL, NULL); hb_draw_funcs_make_immutable (dfuncs); } @@ -109,9 +113,9 @@ glyphy_harfbuzz(get_draw_funcs) (void) static void glyphy_harfbuzz(font_get_glyph_shape) (hb_font_t *font, hb_codepoint_t glyph, - glyphy_arc_accumulator_t *acc) + glyphy_curve_accumulator_t *acc) { - hb_font_get_glyph_shape (font, glyph, glyphy_harfbuzz(get_draw_funcs) (), acc); + hb_font_draw_glyph (font, glyph, glyphy_harfbuzz(get_draw_funcs) (), acc); } #ifdef __cplusplus diff --git a/src/glyphy-outline.cc b/src/glyphy-outline.cc deleted file mode 100644 index 7fded288..00000000 --- a/src/glyphy-outline.cc +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright 2012 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "glyphy-common.hh" -#include "glyphy-geometry.hh" - -using namespace GLyphy::Geometry; - - -void -glyphy_outline_reverse (glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints) -{ - if (!num_endpoints) - return; - - // Shift the d's first - double d0 = endpoints[0].d; - for (unsigned int i = 0; i < num_endpoints - 1; i++) - endpoints[i].d = endpoints[i + 1].d == GLYPHY_INFINITY ? GLYPHY_INFINITY : -endpoints[i + 1].d; - endpoints[num_endpoints - 1].d = d0; - - // Reverse - for (unsigned int i = 0, j = num_endpoints - 1; i < j; i++, j--) { - glyphy_arc_endpoint_t t = endpoints[i]; - endpoints[i] = endpoints[j]; - endpoints[j] = t; - } -} - - -static bool -winding (const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints) -{ - /* - * Algorithm: - * - * - Approximate arcs with triangles passing through the mid- and end-points, - * - Calculate the area of the contour, - * - Return sign. - */ - - double area = 0; - for (unsigned int i = 1; i < num_endpoints; i++) - { - const glyphy_point_t &p0 = endpoints[i - 1].p; - const glyphy_point_t &p1 = endpoints[i].p; - double d = endpoints[i].d; - - assert (d != GLYPHY_INFINITY); - - area += Vector(p0).cross (Vector(p1)); - area -= .5 * d * (Point(p1) - Point(p0)).len2 (); - } - return area < 0; -} - - -static int -categorize (double v, double ref) -{ - return v < ref - GLYPHY_EPSILON ? -1 : v > ref + GLYPHY_EPSILON ? +1 : 0; -} - -static bool -is_zero (double v) -{ - return fabs (v) < GLYPHY_EPSILON; -} - -static bool -even_odd (const glyphy_arc_endpoint_t *c_endpoints, - unsigned int num_c_endpoints, - const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints) -{ - /* - * Algorithm: - * - * - For a point on the contour, draw a halfline in a direction - * (eg. decreasing x) to infinity, - * - Count how many times it crosses all other contours, - * - Pay special attention to points falling exactly on the halfline, - * specifically, they count as +.5 or -.5, depending the direction - * of crossing. - * - * All this counting is extremely tricky: - * - * - Floating point equality cannot be relied on here, - * - Lots of arc analysis needed, - * - Without having a point that we know falls /inside/ the contour, - * there are legitimate cases that we simply cannot handle using - * this algorithm. For example, imagine the following glyph shape: - * - * +---------+ - * | +-----+ | - * | \ / | - * | \ / | - * +----o----+ - * - * If the glyph is defined as two outlines, and when analysing the - * inner outline we happen to pick the point denoted by 'o' for - * analysis, there simply is no way to differentiate this case from - * the following case: - * - * +---------+ - * | | - * | | - * | | - * +----o----+ - * / \ - * / \ - * +-----+ - * - * However, in one, the triangle should be filled in, and in the other - * filled out. - * - * One way to work around this may be to do the analysis for all endpoints - * on the outline and take majority. But even that can fail in more - * extreme yet legitimate cases, such as this one: - * - * +--+--+ - * | / \ | - * |/ \| - * + + - * |\ /| - * | \ / | - * +--o--+ - * - * The only correct algorithm I can think of requires a point that falls - * fully inside the outline. While we can try finding such a point (not - * dissimilar to the winding algorithm), it's beyond what I'm willing to - * implement right now. - */ - - const Point p = c_endpoints[0].p; - - double count = 0; - Point p0 (0, 0); - for (unsigned int i = 0; i < num_endpoints; i++) { - const glyphy_arc_endpoint_t &endpoint = endpoints[i]; - if (endpoint.d == GLYPHY_INFINITY) { - p0 = endpoint.p; - continue; - } - Arc arc (p0, endpoint.p, endpoint.d); - p0 = endpoint.p; - - /* - * Skip our own contour - */ - if (&endpoint >= c_endpoints && &endpoint < c_endpoints + num_c_endpoints) - continue; - - /* End-point y's compared to the ref point; lt, eq, or gt */ - unsigned s0 = categorize (arc.p0.y, p.y); - unsigned s1 = categorize (arc.p1.y, p.y); - - if (is_zero (arc.d)) - { - /* Line */ - - if (!s0 || !s1) - { - /* - * Add +.5 / -.5 for each endpoint on the halfline, depending on - * crossing direction. - */ - Pair t = arc.tangents (); - if (!s0 && arc.p0.x < p.x + GLYPHY_EPSILON) - count += .5 * categorize (t.first.dy, 0); - if (!s1 && arc.p1.x < p.x + GLYPHY_EPSILON) - count += .5 * categorize (t.second.dy, 0); - continue; - } - - if (s0 == s1) - continue; // Segment fully above or below the halfline - - // Find x pos that the line segment would intersect the half-line. - double x = arc.p0.x + (arc.p1.x - arc.p0.x) * ((p.y - arc.p0.y) / (arc.p1.y - arc.p0.y)); - - if (x >= p.x - GLYPHY_EPSILON) - continue; // Does not intersect halfline - - count++; // Add one for full crossing - continue; - } - else - { - /* Arc */ - - if (!s0 || !s1) - { - /* - * Add +.5 / -.5 for each endpoint on the halfline, depending on - * crossing direction. - */ - Pair t = arc.tangents (); - - /* Arc-specific logic: - * If the tangent has dy==0, use the other endpoint's - * y value to decide which way the arc will be heading. - */ - if (is_zero (t.first.dy)) - t.first.dy = +categorize (arc.p1.y, p.y); - if (is_zero (t.second.dy)) - t.second.dy = -categorize (arc.p0.y, p.y); - - if (!s0 && arc.p0.x < p.x + GLYPHY_EPSILON) - count += .5 * categorize (t.first.dy, 0); - if (!s1 && arc.p1.x < p.x + GLYPHY_EPSILON) - count += .5 * categorize (t.second.dy, 0); - } - - Point c = arc.center (); - double r = arc.radius (); - if (c.x - r >= p.x) - continue; // No chance - /* Solve for arc crossing line with y = p.y */ - double dy = p.y - c.y; - double x2 = r * r - dy * dy; - if (x2 <= GLYPHY_EPSILON) - continue; // Negative delta, no crossing - double dx = sqrt (x2); - /* There's two candidate points on the arc with the same y as the - * ref point. */ - Point pp[2] = { Point (c.x - dx, p.y), - Point (c.x + dx, p.y) }; - -#define POINTS_EQ(a,b) (is_zero (a.x - b.x) && is_zero (a.y - b.y)) - for (unsigned int i = 0; i < ARRAY_LENGTH (pp); i++) - { - /* Make sure we don't double-count endpoints that fall on the - * halfline as we already accounted for those above */ - if (!POINTS_EQ (pp[i], arc.p0) && !POINTS_EQ (pp[i], arc.p1) && - pp[i].x < p.x - GLYPHY_EPSILON && arc.wedge_contains_point (pp[i])) - count++; // Add one for full crossing - } -#undef POINTS_EQ - } - } - - return !(int (floor (count)) & 1); -} - -static bool -process_contour (glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - const glyphy_arc_endpoint_t *all_endpoints, - unsigned int num_all_endpoints, - bool inverse) -{ - /* - * Algorithm: - * - * - Find the winding direction and even-odd number, - * - If the two disagree, reverse the contour, inplace. - */ - - if (!num_endpoints) - return false; - - if (num_endpoints < 3) { - abort (); // Don't expect this - return false; // Need at least two arcs - } - if (Point (endpoints[0].p) != Point (endpoints[num_endpoints-1].p)) { - abort (); // Don't expect this - return false; // Need a closed contour - } - - if (inverse ^ - winding (endpoints, num_endpoints) ^ - even_odd (endpoints, num_endpoints, all_endpoints, num_all_endpoints)) - { - glyphy_outline_reverse (endpoints, num_endpoints); - return true; - } - - return false; -} - -/* Returns true if outline was modified */ -glyphy_bool_t -glyphy_outline_winding_from_even_odd (glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - glyphy_bool_t inverse) -{ - /* - * Algorithm: - * - * - Process one contour at a time. - */ - - unsigned int start = 0; - bool ret = false; - for (unsigned int i = 1; i < num_endpoints; i++) { - const glyphy_arc_endpoint_t &endpoint = endpoints[i]; - if (endpoint.d == GLYPHY_INFINITY) { - ret = ret | process_contour (endpoints + start, i - start, endpoints, num_endpoints, bool (inverse)); - start = i; - } - } - ret = ret | process_contour (endpoints + start, num_endpoints - start, endpoints, num_endpoints, bool (inverse)); - return ret; -} diff --git a/src/glyphy-sdf-glsl.h b/src/glyphy-sdf-glsl.h deleted file mode 100644 index a86590de..00000000 --- a/src/glyphy-sdf-glsl.h +++ /dev/null @@ -1,155 +0,0 @@ -static const char *glyphy_sdf_glsl = -"/*\n" -" * Copyright 2012 Google, Inc. All Rights Reserved.\n" -" *\n" -" * Licensed under the Apache License, Version 2.0 (the \"License\");\n" -" * you may not use this file except in compliance with the License.\n" -" * You may obtain a copy of the License at\n" -" *\n" -" * http://www.apache.org/licenses/LICENSE-2.0\n" -" *\n" -" * Unless required by applicable law or agreed to in writing, software\n" -" * distributed under the License is distributed on an \"AS IS\" BASIS,\n" -" * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" -" * See the License for the specific language governing permissions and\n" -" * limitations under the License.\n" -" *\n" -" * Google Author(s): Behdad Esfahbod, Maysum Panju\n" -" */\n" -"\n" -"#ifndef GLYPHY_TEXTURE1D_FUNC\n" -"#define GLYPHY_TEXTURE1D_FUNC glyphy_texture1D_func\n" -"#endif\n" -"#ifndef GLYPHY_TEXTURE1D_EXTRA_DECLS\n" -"#define GLYPHY_TEXTURE1D_EXTRA_DECLS\n" -"#endif\n" -"#ifndef GLYPHY_TEXTURE1D_EXTRA_ARGS\n" -"#define GLYPHY_TEXTURE1D_EXTRA_ARGS\n" -"#endif\n" -"\n" -"#ifndef GLYPHY_SDF_TEXTURE1D_FUNC\n" -"#define GLYPHY_SDF_TEXTURE1D_FUNC GLYPHY_TEXTURE1D_FUNC\n" -"#endif\n" -"#ifndef GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS\n" -"#define GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS GLYPHY_TEXTURE1D_EXTRA_DECLS\n" -"#endif\n" -"#ifndef GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS\n" -"#define GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS GLYPHY_TEXTURE1D_EXTRA_ARGS\n" -"#endif\n" -"#ifndef GLYPHY_SDF_TEXTURE1D\n" -"#define GLYPHY_SDF_TEXTURE1D(offset) GLYPHY_RGBA(GLYPHY_SDF_TEXTURE1D_FUNC (offset GLYPHY_TEXTURE1D_EXTRA_ARGS))\n" -"#endif\n" -"\n" -"#ifndef GLYPHY_MAX_NUM_ENDPOINTS\n" -"#define GLYPHY_MAX_NUM_ENDPOINTS 32\n" -"#endif\n" -"\n" -"glyphy_arc_list_t\n" -"glyphy_arc_list (const vec2 p, const ivec2 nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS)\n" -"{\n" -" int cell_offset = glyphy_arc_list_offset (p, nominal_size);\n" -" vec4 arc_list_data = GLYPHY_SDF_TEXTURE1D (cell_offset);\n" -" return glyphy_arc_list_decode (arc_list_data, nominal_size);\n" -"}\n" -"\n" -"float\n" -"glyphy_sdf (const vec2 p, const ivec2 nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS)\n" -"{\n" -" glyphy_arc_list_t arc_list = glyphy_arc_list (p, nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS);\n" -"\n" -" /* Short-circuits */\n" -" if (arc_list.num_endpoints == 0) {\n" -" /* far-away cell */\n" -" return GLYPHY_INFINITY * float(arc_list.side);\n" -" } if (arc_list.num_endpoints == -1) {\n" -" /* single-line */\n" -" float angle = arc_list.line_angle;\n" -" vec2 n = vec2 (cos (angle), sin (angle));\n" -" return dot (p - (vec2(nominal_size) * .5), n) - arc_list.line_distance;\n" -" }\n" -"\n" -" float side = float(arc_list.side);\n" -" float min_dist = GLYPHY_INFINITY;\n" -" glyphy_arc_t closest_arc;\n" -"\n" -" glyphy_arc_endpoint_t endpoint = glyphy_arc_endpoint_decode (GLYPHY_SDF_TEXTURE1D (arc_list.offset), nominal_size);\n" -" vec2 pp = endpoint.p;\n" -" for (int i = 1; i < GLYPHY_MAX_NUM_ENDPOINTS; i++)\n" -" {\n" -" if (i >= arc_list.num_endpoints) {\n" -" break;\n" -" }\n" -" endpoint = glyphy_arc_endpoint_decode (GLYPHY_SDF_TEXTURE1D (arc_list.offset + i), nominal_size);\n" -" glyphy_arc_t a = glyphy_arc_t (pp, endpoint.p, endpoint.d);\n" -" if (glyphy_isinf (a.d)) {\n" -" pp = endpoint.p;\n" -" continue;\n" -" }\n" -" \n" -" if (glyphy_arc_wedge_contains (a, p))\n" -" {\n" -" float sdist = glyphy_arc_wedge_signed_dist (a, p);\n" -" float udist = abs (sdist) * (1. - GLYPHY_EPSILON);\n" -" if (udist <= min_dist) {\n" -" min_dist = udist;\n" -" side = sdist <= 0. ? -1. : +1.;\n" -" }\n" -" } else {\n" -" float udist = min (distance (p, a.p0), distance (p, a.p1));\n" -" if (udist < min_dist - GLYPHY_EPSILON) {\n" -" min_dist = udist;\n" -" side = 0.; /* unsure */\n" -" closest_arc = a;\n" -" } else if (side == 0. && udist - min_dist <= GLYPHY_EPSILON) {\n" -" /* If this new distance is the same as the current minimum,\n" -" * compare extended distances. Take the sign from the arc\n" -" * with larger extended distance. */\n" -" float old_ext_dist = glyphy_arc_extended_dist (closest_arc, p);\n" -" float new_ext_dist = glyphy_arc_extended_dist (a, p);\n" -"\n" -" float ext_dist = abs (new_ext_dist) <= abs (old_ext_dist) ?\n" -" old_ext_dist : new_ext_dist;\n" -"\n" -"#ifdef GLYPHY_SDF_PSEUDO_DISTANCE\n" -" /* For emboldening and stuff: */\n" -" min_dist = abs (ext_dist);\n" -"#endif\n" -" side = sign (ext_dist);\n" -" }\n" -" }\n" -" pp = endpoint.p;\n" -" }\n" -"\n" -" if (side == 0.) {\n" -" // Technically speaking this should not happen, but it does. So try to fix it.\n" -" float ext_dist = glyphy_arc_extended_dist (closest_arc, p);\n" -" side = sign (ext_dist);\n" -" }\n" -"\n" -" return min_dist * side;\n" -"}\n" -"\n" -"float\n" -"glyphy_point_dist (const vec2 p, const ivec2 nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS)\n" -"{\n" -" glyphy_arc_list_t arc_list = glyphy_arc_list (p, nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS);\n" -"\n" -" float side = float(arc_list.side);\n" -" float min_dist = GLYPHY_INFINITY;\n" -"\n" -" if (arc_list.num_endpoints == 0)\n" -" return min_dist;\n" -"\n" -" glyphy_arc_endpoint_t endpoint;\n" -" for (int i = 0; i < GLYPHY_MAX_NUM_ENDPOINTS; i++)\n" -" {\n" -" if (i >= arc_list.num_endpoints) {\n" -" break;\n" -" }\n" -" endpoint = glyphy_arc_endpoint_decode (GLYPHY_SDF_TEXTURE1D (arc_list.offset + i), nominal_size);\n" -" if (glyphy_isinf (endpoint.d)) continue;\n" -" min_dist = min (min_dist, distance (p, endpoint.p));\n" -" }\n" -" return min_dist;\n" -"}\n" -; diff --git a/src/glyphy-sdf.cc b/src/glyphy-sdf.cc deleted file mode 100644 index 7cdc31ce..00000000 --- a/src/glyphy-sdf.cc +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2012 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski - */ - -#ifdef HAVE_CONFIG_H -#include -#endif - -#include "glyphy-common.hh" -#include "glyphy-geometry.hh" - -using namespace GLyphy::Geometry; - -/* - * TODO - * - * Sync this with the shader sdf - */ - -double -glyphy_sdf_from_arc_list (const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - const glyphy_point_t *p, - glyphy_point_t *closest_p /* may be NULL; TBD not implemented yet */) -{ - Point c = *p; - Point p0 (0, 0); - Arc closest_arc (p0, p0, 0); - double min_dist = GLYPHY_INFINITY; - int side = 0; - for (unsigned int i = 0; i < num_endpoints; i++) { - const glyphy_arc_endpoint_t &endpoint = endpoints[i]; - if (endpoint.d == GLYPHY_INFINITY) { - p0 = endpoint.p; - continue; - } - Arc arc (p0, endpoint.p, endpoint.d); - p0 = endpoint.p; - - if (arc.wedge_contains_point (c)) { - double sdist = arc.distance_to_point (c); /* TODO This distance has the wrong sign. Fix */ - double udist = fabs (sdist) * (1 - GLYPHY_EPSILON); - if (udist <= min_dist) { - min_dist = udist; - side = sdist >= 0 ? -1 : +1; - } - } else { - double udist = std::min ((arc.p0 - c).len (), (arc.p1 - c).len ()); - if (udist < min_dist) { - min_dist = udist; - side = 0; /* unsure */ - closest_arc = arc; - } else if (side == 0 && udist == min_dist) { - /* If this new distance is the same as the current minimum, - * compare extended distances. Take the sign from the arc - * with larger extended distance. */ - double old_ext_dist = closest_arc.extended_dist (c); - double new_ext_dist = arc.extended_dist (c); - - double ext_dist = fabs (new_ext_dist) <= fabs (old_ext_dist) ? - old_ext_dist : new_ext_dist; - - /* For emboldening and stuff: */ - // min_dist = fabs (ext_dist); - side = ext_dist >= 0 ? +1 : -1; - } - } - } - - if (side == 0) { - // Technically speaking this should not happen, but it does. So try to fix it. - double ext_dist = closest_arc.extended_dist (c); - side = ext_dist >= 0 ? +1 : -1; - } - - return side * min_dist; -} diff --git a/src/glyphy-sdf.glsl b/src/glyphy-sdf.glsl deleted file mode 100644 index daad010a..00000000 --- a/src/glyphy-sdf.glsl +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright 2012 Google, Inc. All Rights Reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju - */ - -#ifndef GLYPHY_TEXTURE1D_FUNC -#define GLYPHY_TEXTURE1D_FUNC glyphy_texture1D_func -#endif -#ifndef GLYPHY_TEXTURE1D_EXTRA_DECLS -#define GLYPHY_TEXTURE1D_EXTRA_DECLS -#endif -#ifndef GLYPHY_TEXTURE1D_EXTRA_ARGS -#define GLYPHY_TEXTURE1D_EXTRA_ARGS -#endif - -#ifndef GLYPHY_SDF_TEXTURE1D_FUNC -#define GLYPHY_SDF_TEXTURE1D_FUNC GLYPHY_TEXTURE1D_FUNC -#endif -#ifndef GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS -#define GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS GLYPHY_TEXTURE1D_EXTRA_DECLS -#endif -#ifndef GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS -#define GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS GLYPHY_TEXTURE1D_EXTRA_ARGS -#endif -#ifndef GLYPHY_SDF_TEXTURE1D -#define GLYPHY_SDF_TEXTURE1D(offset) GLYPHY_RGBA(GLYPHY_SDF_TEXTURE1D_FUNC (offset GLYPHY_TEXTURE1D_EXTRA_ARGS)) -#endif - -#ifndef GLYPHY_MAX_NUM_ENDPOINTS -#define GLYPHY_MAX_NUM_ENDPOINTS 32 -#endif - -glyphy_arc_list_t -glyphy_arc_list (const vec2 p, const ivec2 nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS) -{ - int cell_offset = glyphy_arc_list_offset (p, nominal_size); - vec4 arc_list_data = GLYPHY_SDF_TEXTURE1D (cell_offset); - return glyphy_arc_list_decode (arc_list_data, nominal_size); -} - -float -glyphy_sdf (const vec2 p, const ivec2 nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS) -{ - glyphy_arc_list_t arc_list = glyphy_arc_list (p, nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS); - - /* Short-circuits */ - if (arc_list.num_endpoints == 0) { - /* far-away cell */ - return GLYPHY_INFINITY * float(arc_list.side); - } if (arc_list.num_endpoints == -1) { - /* single-line */ - float angle = arc_list.line_angle; - vec2 n = vec2 (cos (angle), sin (angle)); - return dot (p - (vec2(nominal_size) * .5), n) - arc_list.line_distance; - } - - float side = float(arc_list.side); - float min_dist = GLYPHY_INFINITY; - glyphy_arc_t closest_arc; - - glyphy_arc_endpoint_t endpoint = glyphy_arc_endpoint_decode (GLYPHY_SDF_TEXTURE1D (arc_list.offset), nominal_size); - vec2 pp = endpoint.p; - for (int i = 1; i < GLYPHY_MAX_NUM_ENDPOINTS; i++) - { - if (i >= arc_list.num_endpoints) { - break; - } - endpoint = glyphy_arc_endpoint_decode (GLYPHY_SDF_TEXTURE1D (arc_list.offset + i), nominal_size); - glyphy_arc_t a = glyphy_arc_t (pp, endpoint.p, endpoint.d); - if (glyphy_isinf (a.d)) { - pp = endpoint.p; - continue; - } - - if (glyphy_arc_wedge_contains (a, p)) - { - float sdist = glyphy_arc_wedge_signed_dist (a, p); - float udist = abs (sdist) * (1. - GLYPHY_EPSILON); - if (udist <= min_dist) { - min_dist = udist; - side = sdist <= 0. ? -1. : +1.; - } - } else { - float udist = min (distance (p, a.p0), distance (p, a.p1)); - if (udist < min_dist - GLYPHY_EPSILON) { - min_dist = udist; - side = 0.; /* unsure */ - closest_arc = a; - } else if (side == 0. && udist - min_dist <= GLYPHY_EPSILON) { - /* If this new distance is the same as the current minimum, - * compare extended distances. Take the sign from the arc - * with larger extended distance. */ - float old_ext_dist = glyphy_arc_extended_dist (closest_arc, p); - float new_ext_dist = glyphy_arc_extended_dist (a, p); - - float ext_dist = abs (new_ext_dist) <= abs (old_ext_dist) ? - old_ext_dist : new_ext_dist; - -#ifdef GLYPHY_SDF_PSEUDO_DISTANCE - /* For emboldening and stuff: */ - min_dist = abs (ext_dist); -#endif - side = sign (ext_dist); - } - } - pp = endpoint.p; - } - - if (side == 0.) { - // Technically speaking this should not happen, but it does. So try to fix it. - float ext_dist = glyphy_arc_extended_dist (closest_arc, p); - side = sign (ext_dist); - } - - return min_dist * side; -} - -float -glyphy_point_dist (const vec2 p, const ivec2 nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_DECLS) -{ - glyphy_arc_list_t arc_list = glyphy_arc_list (p, nominal_size GLYPHY_SDF_TEXTURE1D_EXTRA_ARGS); - - float side = float(arc_list.side); - float min_dist = GLYPHY_INFINITY; - - if (arc_list.num_endpoints == 0) - return min_dist; - - glyphy_arc_endpoint_t endpoint; - for (int i = 0; i < GLYPHY_MAX_NUM_ENDPOINTS; i++) - { - if (i >= arc_list.num_endpoints) { - break; - } - endpoint = glyphy_arc_endpoint_decode (GLYPHY_SDF_TEXTURE1D (arc_list.offset + i), nominal_size); - if (glyphy_isinf (endpoint.d)) continue; - min_dist = min (min_dist, distance (p, endpoint.p)); - } - return min_dist; -} diff --git a/src/glyphy-shaders.cc b/src/glyphy-shaders.cc index c06b73e1..dd9a7b38 100644 --- a/src/glyphy-shaders.cc +++ b/src/glyphy-shaders.cc @@ -1,39 +1,16 @@ /* - * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju, Wojciech Baranowski */ #ifdef HAVE_CONFIG_H #include #endif -#include "glyphy-common.hh" - -/* - * Shader source code - */ - -/* TODO path separator */ -#define SHADER_PATH(File) PKGDATADIR "/" File - -#include "glyphy-common-glsl.h" -#include "glyphy-sdf-glsl.h" +#include "glyphy.h" -const char * glyphy_common_shader_source (void) { return glyphy_common_glsl; } -const char * glyphy_sdf_shader_source (void) { return glyphy_sdf_glsl; } +#include "glyphy-fragment-glsl.h" +#include "glyphy-vertex-glsl.h" -const char * glyphy_common_shader_source_path (void) { return SHADER_PATH ("glyphy-common.glsl"); } -const char * glyphy_sdf_shader_source_path (void) { return SHADER_PATH ("glyphy-sdf.glsl"); } +const char * glyphy_fragment_shader_source (void) { return glyphy_fragment_glsl; } +const char * glyphy_vertex_shader_source (void) { return glyphy_vertex_glsl; } diff --git a/src/glyphy-vertex.glsl b/src/glyphy-vertex.glsl new file mode 100644 index 00000000..50f9bb92 --- /dev/null +++ b/src/glyphy-vertex.glsl @@ -0,0 +1,44 @@ +/* + * Copyright 2026 Behdad Esfahbod. + * Copyright 2017 Eric Lengyel. + * Based on the Slug algorithm by Eric Lengyel: + * https://github.com/EricLengyel/Slug + */ + + +/* Requires GLSL 3.30 */ + + +/* Dilate a glyph quad vertex by half a pixel on screen. + * + * position: object-space vertex position (modified in place) + * texcoord: em-space sample coordinates (modified in place) + * corner: corner direction, each component -1 or +1 + * texPerPos: ratio of texcoord units to position units (e.g. upem / font_size) + * mvp: model-view-projection matrix + * viewport: viewport size in pixels + */ +void glyphy_dilate (inout vec2 position, inout vec2 texcoord, + vec2 corner, vec2 texPerPos, + mat4 mvp, vec2 viewport) +{ + vec4 clipPos = mvp * vec4 (position, 0.0, 1.0); + + /* Dilate by half a pixel in NDC. Under perspective, ndc = clip.xy / clip.w, + * so the object-space delta must be computed from the projective Jacobian, + * not just the affine clip-space transform. */ + float invW = 1.0 / clipPos.w; + vec2 deltaNdc = corner / viewport; + + vec2 dNdcDx = (mvp[0].xy * clipPos.w - clipPos.xy * mvp[0].w) * (invW * invW); + vec2 dNdcDy = (mvp[1].xy * clipPos.w - clipPos.xy * mvp[1].w) * (invW * invW); + mat2 ndcJacobian = mat2 (dNdcDx, dNdcDy); + + float det = dNdcDx.x * dNdcDy.y - dNdcDx.y * dNdcDy.x; + vec2 dPos = abs (det) > 1.0 / 16777216.0 + ? inverse (ndcJacobian) * deltaNdc + : vec2 (0.0); + + position += dPos; + texcoord += dPos * texPerPos; +} diff --git a/src/glyphy.h b/src/glyphy.h index 8d35279c..e7cb33d2 100644 --- a/src/glyphy.h +++ b/src/glyphy.h @@ -1,19 +1,8 @@ /* + * Copyright 2012 Google, Inc. All Rights Reserved. + * Copyright 2026 Behdad Esfahbod. All Rights Reserved. * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - * Google Author(s): Behdad Esfahbod, Maysum Panju */ #ifndef GLYPHY_H @@ -83,289 +72,130 @@ glyphy_extents_scale (glyphy_extents_t *extents, /* - * Circular arcs + * Quadratic Bezier curves */ typedef struct { - glyphy_point_t p0; glyphy_point_t p1; - double d; -} glyphy_arc_t; - - -/* Build from a conventional arc representation */ -GLYPHY_API void -glyphy_arc_from_conventional (const glyphy_point_t *center, - double radius, - double angle0, - double angle1, - glyphy_bool_t negative, - glyphy_arc_t *arc); - -/* Convert to a conventional arc representation */ -GLYPHY_API void -glyphy_arc_to_conventional (glyphy_arc_t arc, - glyphy_point_t *center /* may be NULL */, - double *radius /* may be NULL */, - double *angle0 /* may be NULL */, - double *angle1 /* may be NULL */, - glyphy_bool_t *negative /* may be NULL */); - -GLYPHY_API glyphy_bool_t -glyphy_arc_is_a_line (glyphy_arc_t arc); - -GLYPHY_API void -glyphy_arc_extents (glyphy_arc_t arc, - glyphy_extents_t *extents); - - - -/* - * Approximate single pieces of geometry to/from one arc - */ - - -GLYPHY_API void -glyphy_arc_from_line (const glyphy_point_t *p0, - const glyphy_point_t *p1, - glyphy_arc_t *arc); - -GLYPHY_API void -glyphy_arc_from_conic (const glyphy_point_t *p0, - const glyphy_point_t *p1, - const glyphy_point_t *p2, - glyphy_arc_t *arc, - double *error); - -GLYPHY_API void -glyphy_arc_from_cubic (const glyphy_point_t *p0, - const glyphy_point_t *p1, - const glyphy_point_t *p2, - const glyphy_point_t *p3, - glyphy_arc_t *arc, - double *error); - -GLYPHY_API void -glyphy_arc_to_cubic (const glyphy_arc_t *arc, - glyphy_point_t *p0, - glyphy_point_t *p1, - glyphy_point_t *p2, - glyphy_point_t *p3, - double *error); - + glyphy_point_t p2; + glyphy_point_t p3; +} glyphy_curve_t; /* - * Approximate outlines with multiple arcs + * Accumulate quadratic Bezier curves from glyph outlines */ -typedef struct { - glyphy_point_t p; - double d; -} glyphy_arc_endpoint_t; - -typedef glyphy_bool_t (*glyphy_arc_endpoint_accumulator_callback_t) (glyphy_arc_endpoint_t *endpoint, - void *user_data); +typedef glyphy_bool_t (*glyphy_curve_accumulator_callback_t) (const glyphy_curve_t *curve, + void *user_data); +typedef struct glyphy_curve_accumulator_t glyphy_curve_accumulator_t; -typedef struct glyphy_arc_accumulator_t glyphy_arc_accumulator_t; - -GLYPHY_API glyphy_arc_accumulator_t * -glyphy_arc_accumulator_create (void); +GLYPHY_API glyphy_curve_accumulator_t * +glyphy_curve_accumulator_create (void); GLYPHY_API void -glyphy_arc_accumulator_destroy (glyphy_arc_accumulator_t *acc); - -GLYPHY_API glyphy_arc_accumulator_t * -glyphy_arc_accumulator_reference (glyphy_arc_accumulator_t *acc); +glyphy_curve_accumulator_destroy (glyphy_curve_accumulator_t *acc); +GLYPHY_API glyphy_curve_accumulator_t * +glyphy_curve_accumulator_reference (glyphy_curve_accumulator_t *acc); GLYPHY_API void -glyphy_arc_accumulator_reset (glyphy_arc_accumulator_t *acc); +glyphy_curve_accumulator_reset (glyphy_curve_accumulator_t *acc); /* Configure accumulator */ GLYPHY_API void -glyphy_arc_accumulator_set_tolerance (glyphy_arc_accumulator_t *acc, - double tolerance); - -GLYPHY_API double -glyphy_arc_accumulator_get_tolerance (glyphy_arc_accumulator_t *acc); - -GLYPHY_API void -glyphy_arc_accumulator_set_callback (glyphy_arc_accumulator_t *acc, - glyphy_arc_endpoint_accumulator_callback_t callback, - void *user_data); - -GLYPHY_API void -glyphy_arc_accumulator_get_callback (glyphy_arc_accumulator_t *acc, - glyphy_arc_endpoint_accumulator_callback_t *callback, - void **user_data); - -GLYPHY_API void -glyphy_arc_accumulator_set_d_metrics (glyphy_arc_accumulator_t *acc, - double max_d, - double d_bits); +glyphy_curve_accumulator_set_callback (glyphy_curve_accumulator_t *acc, + glyphy_curve_accumulator_callback_t callback, + void *user_data); GLYPHY_API void -glyphy_arc_accumulator_get_d_metrics (glyphy_arc_accumulator_t *acc, - double *max_d, - double *d_bits); +glyphy_curve_accumulator_get_callback (glyphy_curve_accumulator_t *acc, + glyphy_curve_accumulator_callback_t *callback, + void **user_data); /* Accumulation results */ GLYPHY_API unsigned int -glyphy_arc_accumulator_get_num_endpoints (glyphy_arc_accumulator_t *acc); - -GLYPHY_API double -glyphy_arc_accumulator_get_error (glyphy_arc_accumulator_t *acc); +glyphy_curve_accumulator_get_num_curves (glyphy_curve_accumulator_t *acc); GLYPHY_API glyphy_bool_t -glyphy_arc_accumulator_successful (glyphy_arc_accumulator_t *acc); +glyphy_curve_accumulator_successful (glyphy_curve_accumulator_t *acc); /* Accumulate */ GLYPHY_API void -glyphy_arc_accumulator_move_to (glyphy_arc_accumulator_t *acc, - const glyphy_point_t *p0); - -GLYPHY_API void -glyphy_arc_accumulator_line_to (glyphy_arc_accumulator_t *acc, - const glyphy_point_t *p1); - -GLYPHY_API void -glyphy_arc_accumulator_conic_to (glyphy_arc_accumulator_t *acc, - const glyphy_point_t *p1, - const glyphy_point_t *p2); +glyphy_curve_accumulator_move_to (glyphy_curve_accumulator_t *acc, + const glyphy_point_t *p0); GLYPHY_API void -glyphy_arc_accumulator_cubic_to (glyphy_arc_accumulator_t *acc, - const glyphy_point_t *p1, - const glyphy_point_t *p2, - const glyphy_point_t *p3); +glyphy_curve_accumulator_line_to (glyphy_curve_accumulator_t *acc, + const glyphy_point_t *p1); GLYPHY_API void -glyphy_arc_accumulator_arc_to (glyphy_arc_accumulator_t *acc, - const glyphy_point_t *p1, - double d); +glyphy_curve_accumulator_conic_to (glyphy_curve_accumulator_t *acc, + const glyphy_point_t *p1, + const glyphy_point_t *p2); GLYPHY_API void -glyphy_arc_accumulator_close_path (glyphy_arc_accumulator_t *acc); +glyphy_curve_accumulator_close_path (glyphy_curve_accumulator_t *acc); GLYPHY_API void -glyphy_arc_list_extents (const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - glyphy_extents_t *extents); +glyphy_curve_list_extents (const glyphy_curve_t *curves, + unsigned int num_curves, + glyphy_extents_t *extents); /* - * Modify outlines for proper consumption + * Encode curves into blob for GPU rendering */ -GLYPHY_API void -glyphy_outline_reverse (glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints); - -/* Returns true if outline was modified */ -GLYPHY_API glyphy_bool_t -glyphy_outline_winding_from_even_odd (glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - glyphy_bool_t inverse); - - - -/* - * Encode an arc outline into binary blob for fast SDF calculation - */ +#ifndef GLYPHY_UNITS_PER_EM_UNIT +#define GLYPHY_UNITS_PER_EM_UNIT 4 +#endif typedef struct { - unsigned char r; - unsigned char g; - unsigned char b; - unsigned char a; -} glyphy_rgba_t; - - -/* TODO make this callback-based also? */ -/* TODO rename to glyphy_blob_encode? */ -GLYPHY_API glyphy_bool_t -glyphy_arc_list_encode_blob (const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - glyphy_rgba_t *blob, - unsigned int blob_size, - double faraway, - double avg_fetch_desired, - double *avg_fetch_achieved, - unsigned int *output_len, - unsigned int *nominal_width, /* 6bit */ - unsigned int *nominal_height, /* 6bit */ - glyphy_extents_t *pextents); - -GLYPHY_API glyphy_bool_t -glyphy_arc_list_encode_blob2 (const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - glyphy_rgba_t *blob, - unsigned int blob_size, - double faraway, - double grid_unit, - double enlighten_max, - double embolden_max, - double *avg_fetch_achieved, - unsigned int *output_len, - unsigned int *nominal_width, /* 6bit */ - unsigned int *nominal_height, /* 6bit */ - glyphy_extents_t *pextents); - -/* TBD _decode_blob */ - - - -/* - * Calculate signed-distance-field from (encoded) arc list - */ - - -GLYPHY_API double -glyphy_sdf_from_arc_list (const glyphy_arc_endpoint_t *endpoints, - unsigned int num_endpoints, - const glyphy_point_t *p, - glyphy_point_t *closest_p /* may be NULL; TBD not implemented yet */); - -/* TBD */ -GLYPHY_API double -glyphy_sdf_from_blob (const glyphy_rgba_t *blob, - unsigned int nominal_width, - unsigned int nominal_height, - const glyphy_point_t *p, - glyphy_point_t *closest_p /* may be NULL; TBD not implemented yet */); - + short r; + short g; + short b; + short a; +} glyphy_texel_t; +typedef struct glyphy_encoder_t glyphy_encoder_t; /* * Shader source code */ - -/* TODO make this enum-based? */ - GLYPHY_API const char * -glyphy_common_shader_source (void); +glyphy_fragment_shader_source (void); GLYPHY_API const char * -glyphy_common_shader_source_path (void); +glyphy_vertex_shader_source (void); -GLYPHY_API const char * -glyphy_sdf_shader_source (void); -GLYPHY_API const char * -glyphy_sdf_shader_source_path (void); +GLYPHY_API glyphy_encoder_t * +glyphy_encoder_create (void); + +GLYPHY_API void +glyphy_encoder_destroy (glyphy_encoder_t *encoder); + +GLYPHY_API glyphy_bool_t +glyphy_encoder_encode (glyphy_encoder_t *encoder, + const glyphy_curve_t *curves, + unsigned int num_curves, + glyphy_texel_t *blob, + unsigned int blob_size, + unsigned int *output_len, + glyphy_extents_t *extents); #ifdef __cplusplus diff --git a/src/meson.build b/src/meson.build index 3e70655c..4ad1ae5a 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,14 +1,7 @@ glyphy_sources = [ - 'glyphy-arc.cc', - 'glyphy-arc-bezier.hh', - 'glyphy-arcs.cc', - 'glyphy-arcs-bezier.hh', - 'glyphy-blob.cc', - 'glyphy-common.hh', + 'glyphy-curves.cc', + 'glyphy-encode.cc', 'glyphy-extents.cc', - 'glyphy-geometry.hh', - 'glyphy-outline.cc', - 'glyphy-sdf.cc', 'glyphy-shaders.cc', ] @@ -19,8 +12,8 @@ glyphy_headers = [ ] glyphy_shaders = [ - [ 'glyphy-common.glsl', 'glyphy-common-glsl.h', 'glyphy_common_glsl' ], - [ 'glyphy-sdf.glsl', 'glyphy-sdf-glsl.h', 'glyphy_sdf_glsl' ], + [ 'glyphy-fragment.glsl', 'glyphy-fragment-glsl.h', 'glyphy_fragment_glsl' ], + [ 'glyphy-vertex.glsl', 'glyphy-vertex-glsl.h', 'glyphy_vertex_glsl' ], ] stringize = find_program('stringize') @@ -59,7 +52,7 @@ libglyphy_dep = declare_dependency( meson.override_dependency('glyphy', libglyphy_dep) libglyphy_pc = pkgmod.generate(libglyphy, - description: 'High-quality glyph rendering library using OpenGL ES2 shaders', + description: 'High-quality GPU glyph rendering library', subdirs: meson.project_name()) install_headers(glyphy_headers, subdir: meson.project_name()) diff --git a/win32/config.h b/win32/config.h deleted file mode 100644 index 66472a2b..00000000 --- a/win32/config.h +++ /dev/null @@ -1,86 +0,0 @@ -/* config.h. Pre-canned for VS. */ - -/* Default font file. */ -#define DEFAULT_FONT "Calibri" - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_DLFCN_H */ - -/* Have freetype2 library */ -#define HAVE_FREETYPE2 - -/* Have gl library */ -#define HAVE_GL 1 - -/* Have glew library */ -#define HAVE_GLEW 1 - -/* Have glut library */ -#define HAVE_GLUT 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_INTTYPES_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_MEMORY_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDINT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_STDLIB_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_STRINGS_H */ - -/* Define to 1 if you have the header file. */ -#define HAVE_STRING_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_STAT_H 1 - -/* Define to 1 if you have the header file. */ -#define HAVE_SYS_TYPES_H 1 - -/* Define to 1 if you have the header file. */ -/* #undef HAVE_UNISTD_H */ - -/* Define to the sub-directory in which libtool stores uninstalled libraries. - */ -#define LT_OBJDIR ".libs/" - -/* Define to 1 if your C compiler doesn't accept -c and -o together. */ -/* #undef NO_MINUS_C_MINUS_O */ - -/* Name of package */ -#define PACKAGE "glyphy" - -/* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "http://code.google.com/p/glyphy/issues/list" - -/* Define to the full name of this package. */ -#define PACKAGE_NAME "glyphy" - -/* Define to the full name and version of this package. */ -#define PACKAGE_STRING "glyphy 0.2.0" - -/* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "glyphy" - -/* Define to the home page for this package. */ -#define PACKAGE_URL "http://code.google.com/p/glyphy/" - -/* Define to the version of this package. */ -#define PACKAGE_VERSION "0.2.0" - -/* Define to the directory containing package data. */ -#define PKGDATADIR "foo" - -/* Define to 1 if you have the ANSI C header files. */ -/* #undef STDC_HEADERS */ - -/* Version number of package */ -#define VERSION "0.2.0" - -/* On Android */ -/* #undef __ANDROID__ */ diff --git a/win32/glyphy-demo.vcxproj b/win32/glyphy-demo.vcxproj deleted file mode 100644 index b5e80080..00000000 --- a/win32/glyphy-demo.vcxproj +++ /dev/null @@ -1,106 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - {0C6D51DA-FFD8-4835-A6DC-8FB6FAE65D35} - Win32Proj - glyphywindowsdemo - - - - Application - true - v140 - Unicode - - - Application - false - v140 - true - Unicode - - - - - - - - - - - - - true - - - false - - - - - - Level3 - Disabled - WIN32;_DEBUG;_CONSOLE;_LIB;NOMINMAX;_USE_MATH_DEFINES;%(PreprocessorDefinitions) - ..\win32;..\src;..\demo;%(AdditionalIncludeDirectories) - - - Console - true - %(AdditionalLibraryDirectories) - %(AdditionalDependencies) - - - cscript "$(MSBuildProjectDirectory)\stringize.js" "$(MSBuildProjectDirectory)\..\demo" - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions) - - - Console - true - true - true - - - cscript "$(MSBuildProjectDirectory)\stringize.js" "$(MSBuildProjectDirectory)\..\demo" - - - - - - - - - - - - - - - - {16458488-1c9d-4ef0-8311-a5e80ecd25d0} - - - - - - \ No newline at end of file diff --git a/win32/glyphy.sln b/win32/glyphy.sln deleted file mode 100644 index 344693bc..00000000 --- a/win32/glyphy.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glyphy-demo", "glyphy-demo.vcxproj", "{0C6D51DA-FFD8-4835-A6DC-8FB6FAE65D35}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "glyphy", "glyphy.vcxproj", "{16458488-1C9D-4EF0-8311-A5E80ECD25D0}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {0C6D51DA-FFD8-4835-A6DC-8FB6FAE65D35}.Debug|Win32.ActiveCfg = Debug|Win32 - {0C6D51DA-FFD8-4835-A6DC-8FB6FAE65D35}.Debug|Win32.Build.0 = Debug|Win32 - {0C6D51DA-FFD8-4835-A6DC-8FB6FAE65D35}.Release|Win32.ActiveCfg = Release|Win32 - {0C6D51DA-FFD8-4835-A6DC-8FB6FAE65D35}.Release|Win32.Build.0 = Release|Win32 - {16458488-1C9D-4EF0-8311-A5E80ECD25D0}.Debug|Win32.ActiveCfg = Debug|Win32 - {16458488-1C9D-4EF0-8311-A5E80ECD25D0}.Debug|Win32.Build.0 = Debug|Win32 - {16458488-1C9D-4EF0-8311-A5E80ECD25D0}.Release|Win32.ActiveCfg = Release|Win32 - {16458488-1C9D-4EF0-8311-A5E80ECD25D0}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal diff --git a/win32/glyphy.vcxproj b/win32/glyphy.vcxproj deleted file mode 100644 index 3a2e515a..00000000 --- a/win32/glyphy.vcxproj +++ /dev/null @@ -1,92 +0,0 @@ - - - - - Debug - Win32 - - - Release - Win32 - - - - - - - - - - - - - {16458488-1C9D-4EF0-8311-A5E80ECD25D0} - Win32Proj - glyphy - - - - StaticLibrary - true - v140 - Unicode - - - StaticLibrary - false - v140 - true - Unicode - - - - - - - - - - - - - - - - - Level3 - Disabled - WIN32;_DEBUG;_LIB;HAVE_CONFIG_H;_USE_MATH_DEFINES;%(PreprocessorDefinitions) - ..\win32;..\src;%(AdditionalIncludeDirectories) - - - Windows - true - - - cscript "$(MSBuildProjectDirectory)\stringize.js" "$(MSBuildProjectDirectory)\..\src" - - - - - Level3 - - - MaxSpeed - true - true - WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) - - - Windows - true - true - true - - - cscript "$(MSBuildProjectDirectory)\stringize.js" "$(MSBuildProjectDirectory)\..\src" - - - - - - \ No newline at end of file diff --git a/win32/stringize.js b/win32/stringize.js deleted file mode 100644 index de18eea6..00000000 --- a/win32/stringize.js +++ /dev/null @@ -1,20 +0,0 @@ -var fs = new ActiveXObject("Scripting.FileSystemObject"); - -var enumerator = new Enumerator(fs.GetFolder(WScript.Arguments.Item(0)).Files); -for (; !enumerator.atEnd(); enumerator.moveNext()) { - var filePath = enumerator.item() + ''; - // skip if it is not a glsl file - if (!/\.glsl$/.exec(filePath)) { continue; } - var file = fs.OpenTextFile(filePath); - var text = file.ReadAll().split('\n'); - file.Close(); - - var result = fs.CreateTextFile(filePath.replace(/\.glsl$/, '-glsl.h'), true); - result.WriteLine('static const char* ' + filePath.replace(/.*\\/, '').replace(/[-\.]/g, '_') + ' = '); - for (var i in text) { - var line = (text[i] + ''); - result.WriteLine('"' + line.replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '\\n"'); - } - result.WriteLine(';'); - result.Close(); -} \ No newline at end of file