[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[freetype2] gsoc2023-ftbench 9dd15018c: ftbench demo with seperate makef
From: |
Werner Lemberg |
Subject: |
[freetype2] gsoc2023-ftbench 9dd15018c: ftbench demo with seperate makefile |
Date: |
Thu, 25 May 2023 06:48:38 -0400 (EDT) |
branch: gsoc2023-ftbench
commit 9dd15018c26e25886dfb58ffa04886d89ee3c245
Author: goksu <25721443+goeksu@users.noreply.github.com>
Commit: goksu <25721443+goeksu@users.noreply.github.com>
ftbench demo with seperate makefile
---
.gitignore | 1 +
src/tools/ftbench/Makefile | 40 +
src/tools/ftbench/fonts/Arial_subset.ttf | Bin 0 -> 48720 bytes
src/tools/ftbench/fonts/Roboto_subset.ttf | Bin 0 -> 12732 bytes
src/tools/ftbench/fonts/Tahoma_subset.ttf | Bin 0 -> 37120 bytes
src/tools/ftbench/fonts/TimesNewRoman_subset.ttf | Bin 0 -> 63212 bytes
src/tools/ftbench/fonts/Verdana_subset.ttf | Bin 0 -> 31488 bytes
src/tools/ftbench/fonts/sub.sh | 15 +
src/tools/ftbench/ftbench.c | 1613 ++++++++++++++++++++++
src/tools/ftbench/mlgetopt.h | 44 +
10 files changed, 1713 insertions(+)
diff --git a/.gitignore b/.gitignore
index e4f1510bf..b94251c8a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,4 @@ src/dlg/dlg.c
subprojects/*
!subprojects/*.wrap
/tests/data/*
+.DS_Store
diff --git a/src/tools/ftbench/Makefile b/src/tools/ftbench/Makefile
new file mode 100644
index 000000000..a8211ebd5
--- /dev/null
+++ b/src/tools/ftbench/Makefile
@@ -0,0 +1,40 @@
+# Variables
+FTBENCH_SRC = ftbench.c
+FTBENCH_BIN = bench.out
+FTBENCH_FLAGS = $(shell pkg-config --cflags freetype2) -lfreetype
+FONTS = $(wildcard fonts/*.ttf)
+BASELINES = $(addprefix baselines/, $(notdir $(FONTS)))
+BENCHMARKS = $(addprefix benchmarks/, $(notdir $(FONTS)))
+
+# Default target
+all: $(FTBENCH_BIN)
+
+# Build ftbench
+$(FTBENCH_BIN): $(FTBENCH_SRC)
+ gcc $(FTBENCH_FLAGS) $(FTBENCH_SRC) -o $(FTBENCH_BIN)
+
+# Create directories for baselines and benchmarks
+baselines/ benchmarks/:
+ mkdir -p $@
+
+# Create a baseline
+.PHONY: baseline
+baseline: $(FTBENCH_BIN) baselines/
+ $(foreach font, $(FONTS), \
+ ./$(FTBENCH_BIN) $(font) > baselines/$(notdir $(font)).txt; \
+ )
+
+# Benchmark and compare to baseline
+.PHONY: benchmark
+benchmark: $(FTBENCH_BIN) benchmarks/
+ $(foreach font, $(FONTS), \
+ ./$(FTBENCH_BIN) $(font) > benchmarks/$(notdir $(font)).txt; \
+ )
+ $(foreach font, $(FONTS), \
+ diff baselines/$(notdir $(font)).txt benchmarks/$(notdir
$(font)).txt; \
+ )
+
+.PHONY: clean
+clean:
+ rm -f $(FTBENCH_BIN)
+ rm -rf baselines/ benchmarks/
diff --git a/src/tools/ftbench/fonts/Arial_subset.ttf
b/src/tools/ftbench/fonts/Arial_subset.ttf
new file mode 100644
index 000000000..be51bfaca
Binary files /dev/null and b/src/tools/ftbench/fonts/Arial_subset.ttf differ
diff --git a/src/tools/ftbench/fonts/Roboto_subset.ttf
b/src/tools/ftbench/fonts/Roboto_subset.ttf
new file mode 100644
index 000000000..430b08072
Binary files /dev/null and b/src/tools/ftbench/fonts/Roboto_subset.ttf differ
diff --git a/src/tools/ftbench/fonts/Tahoma_subset.ttf
b/src/tools/ftbench/fonts/Tahoma_subset.ttf
new file mode 100644
index 000000000..5d493a8ec
Binary files /dev/null and b/src/tools/ftbench/fonts/Tahoma_subset.ttf differ
diff --git a/src/tools/ftbench/fonts/TimesNewRoman_subset.ttf
b/src/tools/ftbench/fonts/TimesNewRoman_subset.ttf
new file mode 100644
index 000000000..911dac542
Binary files /dev/null and b/src/tools/ftbench/fonts/TimesNewRoman_subset.ttf
differ
diff --git a/src/tools/ftbench/fonts/Verdana_subset.ttf
b/src/tools/ftbench/fonts/Verdana_subset.ttf
new file mode 100644
index 000000000..0d9a47f76
Binary files /dev/null and b/src/tools/ftbench/fonts/Verdana_subset.ttf differ
diff --git a/src/tools/ftbench/fonts/sub.sh b/src/tools/ftbench/fonts/sub.sh
new file mode 100644
index 000000000..3b114cce8
--- /dev/null
+++ b/src/tools/ftbench/fonts/sub.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# Define the Unicode range
+unicodes="U+0021-007E"
+
+# Loop over all .ttf files in the current directory
+for fontfile in *.ttf
+do
+ # Generate the output filename
+ output="${fontfile%.ttf}_subset.ttf"
+
+ # Run the pyftsubset command
+ pyftsubset "$fontfile" --unicodes=$unicodes --output-file="$output"
+done
+
diff --git a/src/tools/ftbench/ftbench.c b/src/tools/ftbench/ftbench.c
new file mode 100644
index 000000000..2f0f2a4e6
--- /dev/null
+++ b/src/tools/ftbench/ftbench.c
@@ -0,0 +1,1613 @@
+/****************************************************************************/
+/* */
+/* The FreeType project -- a free and portable quality TrueType renderer. */
+/* */
+/* Copyright (C) 2002-2023 by */
+/* D. Turner, R.Wilhelm, and W. Lemberg */
+/* */
+/* ftbench: bench some common FreeType call paths */
+/* */
+/****************************************************************************/
+
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* we want to use extensions to `time.h' if available */
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <time.h>
+
+#include <ft2build.h>
+#include <freetype/freetype.h>
+
+#include <freetype/ftadvanc.h>
+#include <freetype/ftbbox.h>
+#include <freetype/ftcache.h>
+#include <freetype/ftdriver.h>
+#include <freetype/ftglyph.h>
+#include <freetype/ftlcdfil.h>
+#include <freetype/ftmm.h>
+#include <freetype/ftmodapi.h>
+#include <freetype/ftoutln.h>
+#include <freetype/ftstroke.h>
+#include <freetype/ftsynth.h>
+
+#define MAX_MM_AXES 16
+
+#ifdef UNIX
+#include <unistd.h>
+#else
+#include "mlgetopt.h"
+#endif
+
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+/* Specify the timer: QPC for accurate wall time, GPT for user-mode time. */
+/* Otherwise, QPCT cycles are measured accurately but with huge overhead. */
+#define QPC
+
+#ifdef QPC
+ double interval;
+#endif
+
+#endif
+
+
+ typedef struct btimer_t_ {
+ double t0;
+ double total;
+
+ } btimer_t;
+
+
+ typedef int
+ (*bcall_t)( btimer_t* timer,
+ FT_Face face,
+ void* user_data );
+
+
+ typedef struct btest_t_ {
+ const char* title;
+ bcall_t bench;
+ int cache_first;
+ void* user_data;
+
+ } btest_t;
+
+
+ typedef struct bcharset_t_
+ {
+ FT_Int size;
+ FT_ULong* code;
+
+ } bcharset_t;
+
+
+ static FT_Error
+ get_face( FT_Face* face );
+
+
+ /*
+ * Globals
+ */
+
+#define CACHE_SIZE 1024
+#define BENCH_TIME 2.0
+#define FACE_SIZE 10
+
+
+ static FT_Library lib;
+ static FTC_Manager cache_man;
+ static FTC_CMapCache cmap_cache;
+ static FTC_ImageCache image_cache;
+ static FTC_SBitCache sbit_cache;
+ static FTC_ImageTypeRec font_type;
+
+ static FT_MM_Var* multimaster = NULL;
+ static FT_Fixed design_pos [MAX_MM_AXES];
+ static FT_Fixed requested_pos[MAX_MM_AXES];
+ static unsigned int requested_cnt = 0;
+ static unsigned int used_num_axes = 0;
+
+ enum {
+ FT_BENCH_LOAD_GLYPH,
+ FT_BENCH_LOAD_ADVANCES,
+ FT_BENCH_RENDER,
+ FT_BENCH_GET_GLYPH,
+ FT_BENCH_CMAP,
+ FT_BENCH_CMAP_ITER,
+ FT_BENCH_NEW_FACE,
+ FT_BENCH_EMBOLDEN,
+ FT_BENCH_STROKE,
+ FT_BENCH_GET_BBOX,
+ FT_BENCH_GET_CBOX,
+ FT_BENCH_NEW_FACE_AND_LOAD_GLYPH,
+ N_FT_BENCH
+ };
+
+
+ static const char* bench_desc[] =
+ {
+ "load a glyph (FT_Load_Glyph)",
+ "load advance widths (FT_Get_Advances)",
+ "render a glyph (FT_Render_Glyph)",
+ "load a glyph (FT_Get_Glyph)",
+ "get glyph indices (FT_Get_Char_Index)",
+ "iterate CMap (FT_Get_{First,Next}_Char)",
+ "open a new face (FT_New_Face)",
+ "embolden (FT_GlyphSlot_Embolden)",
+ "stroke (FT_Glyph_Stroke)",
+ "get glyph bbox (FT_Outline_Get_BBox)",
+ "get glyph cbox (FT_Glyph_Get_CBox)",
+
+ "open face and load glyphs",
+ NULL
+ };
+
+
+ static int preload;
+ static char* filename;
+
+ static int first_index = 0;
+ static int last_index = INT_MAX;
+ static int incr_index = 1;
+
+ static int cmap_index = -1;
+
+#define FOREACH( i ) for ( i = first_index ; \
+ ( first_index <= i && i <= last_index ) || \
+ ( first_index >= i && i >= last_index ) ; \
+ i += incr_index )
+
+ static FT_Render_Mode render_mode = FT_RENDER_MODE_NORMAL;
+ static FT_Int32 load_flags = FT_LOAD_DEFAULT;
+
+ static unsigned int tt_interpreter_versions[2];
+ static int num_tt_interpreter_versions;
+ static unsigned int dflt_tt_interpreter_version;
+
+ static unsigned int ps_hinting_engines[2];
+ static int num_ps_hinting_engines;
+ static unsigned int dflt_ps_hinting_engine;
+
+ static char ps_hinting_engine_names[2][10] = { "freetype",
+ "adobe" };
+
+
+ /*
+ * Face requester for cache testing
+ */
+
+ static FT_Error
+ face_requester( FTC_FaceID face_id,
+ FT_Library library,
+ FT_Pointer request_data,
+ FT_Face* aface )
+ {
+ FT_UNUSED( face_id );
+ FT_UNUSED( library );
+ FT_UNUSED( request_data );
+
+ return get_face( aface );
+ }
+
+
+ /*
+ * timer in milliseconds
+ */
+
+ static double
+ get_time( void )
+ {
+ /* NOTE: When building with the Mingw64 toolchain, `_POSIX_TIMERS` is
+ * defined, but function `clock_gettime` is not. Ensure that the
+ * `_WIN32` specific timer code appears first here.
+ */
+#if defined _WIN32
+
+#ifdef QPC
+ LARGE_INTEGER ticks;
+
+
+ QueryPerformanceCounter( &ticks );
+
+ return interval * ticks.QuadPart;
+
+#elif defined GPT
+ FILETIME start, end, kern, user;
+
+
+ GetProcessTimes( GetCurrentProcess(), &start, &end, &kern, &user );
+
+ return 0.1 * user.dwLowDateTime + 429496729.6 * user.dwHighDateTime;
+
+#else
+ ULONG64 cycles;
+
+
+ QueryProcessCycleTime( GetCurrentProcess(), &cycles );
+
+ return 1e-3 * cycles; /* at 1GHz */
+
+#endif
+
+#elif defined _POSIX_TIMERS && _POSIX_TIMERS > 0
+ struct timespec tv;
+
+
+#ifdef _POSIX_CPUTIME
+ clock_gettime( CLOCK_PROCESS_CPUTIME_ID, &tv );
+#else
+ clock_gettime( CLOCK_REALTIME, &tv );
+#endif /* _POSIX_CPUTIME */
+
+ return 1E6 * (double)tv.tv_sec + 1E-3 * (double)tv.tv_nsec;
+
+#else
+ /* clock() accuracy has improved since glibc 2.18 */
+ return 1E6 * (double)clock() / (double)CLOCKS_PER_SEC;
+#endif /* _POSIX_TIMERS */
+ }
+
+#define TIMER_START( timer ) ( timer )->t0 = get_time()
+#define TIMER_STOP( timer ) ( timer )->total += get_time() - ( timer )->t0
+#define TIMER_GET( timer ) ( timer )->total
+#define TIMER_RESET( timer ) ( timer )->total = 0
+
+
+ /*
+ * Bench code
+ */
+
+ static void
+ benchmark( FT_Face face,
+ btest_t* test,
+ int max_iter,
+ double max_time )
+ {
+ int n, done;
+ btimer_t timer, elapsed;
+
+
+ if ( test->cache_first )
+ {
+ TIMER_RESET( &timer );
+ test->bench( &timer, face, test->user_data );
+ }
+
+ printf( " %-25s ", test->title );
+ fflush( stdout );
+
+ TIMER_RESET( &timer );
+ TIMER_RESET( &elapsed );
+
+ for ( n = 0, done = 0; !max_iter || n < max_iter; n++ )
+ {
+ TIMER_START( &elapsed );
+
+ done += test->bench( &timer, face, test->user_data );
+
+ TIMER_STOP( &elapsed );
+
+ if ( TIMER_GET( &elapsed ) > 1E6 * max_time )
+ break;
+ }
+
+ if ( done )
+ printf( "%10.3f us/op %10d done\n",
+ TIMER_GET( &timer ) / (double)done, done );
+ else
+ printf( "no error-free calls\n" );
+ }
+
+
+ /*
+ * Various tests
+ */
+
+ static int
+ test_load( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ int i, done = 0;
+
+ FT_UNUSED( user_data );
+
+
+ TIMER_START( timer );
+
+ FOREACH( i )
+ {
+ if ( !FT_Load_Glyph( face, (FT_UInt)i, load_flags ) )
+ done++;
+ }
+
+ TIMER_STOP( timer );
+
+ return done;
+ }
+
+
+ static int
+ test_load_advances( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ int done = 0;
+ FT_Fixed* advances;
+ FT_ULong flags = *((FT_ULong*)user_data);
+ FT_Int start, count;
+
+
+ if ( incr_index > 0 )
+ {
+ start = first_index;
+ count = last_index - first_index + 1;
+ }
+ else
+ {
+ start = last_index;
+ count = first_index - last_index + 1;
+ }
+
+ advances = (FT_Fixed *)calloc( sizeof ( FT_Fixed ), (size_t)count );
+
+ TIMER_START( timer );
+
+ FT_Get_Advances( face,
+ (FT_UInt)start, (FT_UInt)count,
+ (FT_Int32)flags, advances );
+ done += (int)count;
+
+ TIMER_STOP( timer );
+
+ free( advances );
+
+ return done;
+ }
+
+
+ static int
+ test_render( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ int i, done = 0;
+
+ FT_UNUSED( user_data );
+
+
+ FOREACH( i )
+ {
+ if ( FT_Load_Glyph( face, (FT_UInt)i, load_flags ) )
+ continue;
+
+ TIMER_START( timer );
+ if ( !FT_Render_Glyph( face->glyph, render_mode ) )
+ done++;
+ TIMER_STOP( timer );
+ }
+
+ return done;
+ }
+
+
+ static int
+ test_embolden( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ int i, done = 0;
+
+ FT_UNUSED( user_data );
+
+
+ FOREACH( i )
+ {
+ if ( FT_Load_Glyph( face, (FT_UInt)i, load_flags ) )
+ continue;
+
+ TIMER_START( timer );
+ FT_GlyphSlot_Embolden( face->glyph );
+ done++;
+ TIMER_STOP( timer );
+ }
+
+ return done;
+ }
+
+
+ static int
+ test_stroke( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ FT_Glyph glyph;
+ FT_Stroker stroker;
+
+ int i, done = 0;
+
+ FT_UNUSED( user_data );
+
+
+ FT_Stroker_New( lib, &stroker );
+ FT_Stroker_Set( stroker, face->size->metrics.y_ppem,
+ FT_STROKER_LINECAP_ROUND,
+ FT_STROKER_LINEJOIN_ROUND,
+ 0 );
+
+ FOREACH( i )
+ {
+ if ( FT_Load_Glyph( face, (FT_UInt)i, load_flags ) ||
+ face->glyph->format != FT_GLYPH_FORMAT_OUTLINE ||
+ FT_Get_Glyph( face->glyph, &glyph ) )
+ continue;
+
+ TIMER_START( timer );
+ FT_Glyph_Stroke( &glyph, stroker, 1 );
+ TIMER_STOP( timer );
+
+ FT_Done_Glyph( glyph );
+ done++;
+ }
+
+ FT_Stroker_Done( stroker );
+
+ return done;
+ }
+
+
+ static int
+ test_get_glyph( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ FT_Glyph glyph;
+
+ int i, done = 0;
+
+ FT_UNUSED( user_data );
+
+
+ FOREACH( i )
+ {
+ if ( FT_Load_Glyph( face, (FT_UInt)i, load_flags ) )
+ continue;
+
+ TIMER_START( timer );
+ if ( !FT_Get_Glyph( face->glyph, &glyph ) )
+ {
+ FT_Done_Glyph( glyph );
+ done++;
+ }
+ TIMER_STOP( timer );
+ }
+
+ return done;
+ }
+
+
+ static int
+ test_get_cbox( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ FT_Glyph glyph;
+ FT_BBox bbox;
+
+ int i, done = 0;
+
+ FT_UNUSED( user_data );
+
+
+ FOREACH( i )
+ {
+ if ( FT_Load_Glyph( face, (FT_UInt)i, load_flags ) )
+ continue;
+
+ if ( FT_Get_Glyph( face->glyph, &glyph ) )
+ continue;
+
+ TIMER_START( timer );
+ FT_Glyph_Get_CBox( glyph, FT_GLYPH_BBOX_PIXELS, &bbox );
+ TIMER_STOP( timer );
+
+ FT_Done_Glyph( glyph );
+ done++;
+ }
+
+ return done;
+ }
+
+
+ static int
+ test_get_bbox( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ FT_BBox bbox;
+
+ int i, done = 0;
+
+ FT_UNUSED( user_data );
+
+
+ FOREACH( i )
+ {
+ if ( FT_Load_Glyph( face, (FT_UInt)i, load_flags ) )
+ continue;
+
+ TIMER_START( timer );
+ FT_Outline_Get_BBox( &face->glyph->outline, &bbox );
+ TIMER_STOP( timer );
+
+ done++;
+ }
+
+ return done;
+ }
+
+
+ static int
+ test_get_char_index( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ bcharset_t* charset = (bcharset_t*)user_data;
+ int i, done = 0;
+
+
+ TIMER_START( timer );
+
+ for ( i = 0; i < charset->size; i++ )
+ {
+ if ( FT_Get_Char_Index(face, charset->code[i]) )
+ done++;
+ }
+
+ TIMER_STOP( timer );
+
+ return done;
+ }
+
+
+ static int
+ test_cmap_cache( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ bcharset_t* charset = (bcharset_t*)user_data;
+ int i, done = 0;
+
+ FT_UNUSED( face );
+
+
+ TIMER_START( timer );
+
+ for ( i = 0; i < charset->size; i++ )
+ {
+ if ( FTC_CMapCache_Lookup( cmap_cache,
+ font_type.face_id,
+ cmap_index,
+ charset->code[i] ) )
+ done++;
+ }
+
+ TIMER_STOP( timer );
+
+ return done;
+ }
+
+
+ static int
+ test_image_cache( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ FT_Glyph glyph;
+
+ int i, done = 0;
+
+ FT_UNUSED( face );
+ FT_UNUSED( user_data );
+
+
+ TIMER_START( timer );
+
+ FOREACH( i )
+ {
+ if ( !FTC_ImageCache_Lookup( image_cache,
+ &font_type,
+ (FT_UInt)i,
+ &glyph,
+ NULL ) )
+ done++;
+ }
+
+ TIMER_STOP( timer );
+
+ return done;
+ }
+
+
+ static int
+ test_sbit_cache( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ FTC_SBit glyph;
+
+ int i, done = 0;
+
+ FT_UNUSED( face );
+ FT_UNUSED( user_data );
+
+
+ TIMER_START( timer );
+
+ FOREACH( i )
+ {
+ if ( !FTC_SBitCache_Lookup( sbit_cache,
+ &font_type,
+ (FT_UInt)i,
+ &glyph,
+ NULL ) )
+ done++;
+ }
+
+ TIMER_STOP( timer );
+
+ return done;
+ }
+
+
+ static int
+ test_cmap_iter( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ FT_UInt idx;
+ FT_ULong charcode;
+ int done;
+
+ FT_UNUSED( user_data );
+
+
+ TIMER_START( timer );
+
+ charcode = FT_Get_First_Char( face, &idx );
+ done = ( idx != 0 );
+
+ while ( idx != 0 )
+ charcode = FT_Get_Next_Char( face, charcode, &idx );
+
+ TIMER_STOP( timer );
+
+ return done;
+ }
+
+
+ static int
+ test_new_face( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ FT_Face bench_face;
+
+ FT_UNUSED( face );
+ FT_UNUSED( user_data );
+
+
+ TIMER_START( timer );
+
+ if ( !get_face( &bench_face ) )
+ FT_Done_Face( bench_face );
+
+ TIMER_STOP( timer );
+
+ return 1;
+ }
+
+
+ static int
+ test_new_face_and_load_glyph( btimer_t* timer,
+ FT_Face face,
+ void* user_data )
+ {
+ FT_Face bench_face;
+
+ int i, done = 0;
+
+ FT_UNUSED( face );
+ FT_UNUSED( user_data );
+
+
+ TIMER_START( timer );
+
+ if ( !get_face( &bench_face ) )
+ {
+ FOREACH( i )
+ {
+ if ( !FT_Load_Glyph( bench_face, (FT_UInt)i, load_flags ) )
+ done++;
+ }
+
+ FT_Done_Face( bench_face );
+ }
+
+ TIMER_STOP( timer );
+
+ return done;
+ }
+
+
+ /*
+ * main
+ */
+
+ static void
+ get_charset( FT_Face face,
+ bcharset_t* charset )
+ {
+ FT_ULong charcode;
+ int i = 0;
+
+
+ charset->code = (FT_ULong*)calloc( (size_t)face->num_glyphs,
+ sizeof ( FT_ULong ) );
+ if ( !charset->code )
+ return;
+
+ if ( face->charmap )
+ {
+ FT_UInt idx;
+
+
+ charcode = FT_Get_First_Char( face, &idx );
+
+ /* certain fonts contain a broken charmap that will map character */
+ /* codes to out-of-bounds glyph indices. Take care of that here. */
+ /* */
+ while ( idx && i < face->num_glyphs )
+ {
+ FT_Int gindex = (FT_Int)idx;
+
+
+ if ( ( first_index <= gindex && gindex <= last_index ) ||
+ ( first_index >= gindex && gindex >= last_index ) )
+ charset->code[i++] = charcode;
+ charcode = FT_Get_Next_Char( face, charcode, &idx );
+ }
+ }
+ else
+ {
+ int j;
+
+
+ /* no charmap, do an identity mapping */
+ FOREACH( j )
+ charset->code[i++] = (FT_ULong)j;
+ }
+
+ charset->size = i;
+ }
+
+
+ static void
+ header( FT_Face face )
+ {
+ const FT_String* target =
+ render_mode == FT_RENDER_MODE_NORMAL ? "normal" :
+ render_mode == FT_RENDER_MODE_LIGHT ? "light" :
+ render_mode == FT_RENDER_MODE_MONO ? "mono" :
+ render_mode == FT_RENDER_MODE_LCD ? "lcd" :
+ render_mode == FT_RENDER_MODE_LCD_V ? "lcd-v" :
+ render_mode == FT_RENDER_MODE_SDF ? "sdf" : "";
+ const FT_String* module_name = FT_FACE_DRIVER_NAME( face );
+ const FT_String* hinting_engine = "";
+ FT_UInt prop;
+
+
+ if ( !FT_IS_SCALABLE( face ) )
+ hinting_engine = "bitmap";
+
+ else if ( load_flags & FT_LOAD_NO_SCALE )
+ hinting_engine = "unscaled";
+
+ else if ( load_flags & FT_LOAD_NO_HINTING )
+ hinting_engine = "unhinted";
+
+ else if ( render_mode == FT_RENDER_MODE_LIGHT )
+ hinting_engine = "auto";
+
+ else if ( load_flags == FT_LOAD_FORCE_AUTOHINT )
+ hinting_engine = "auto";
+
+ else if ( !FT_Property_Get( lib, module_name,
+ "interpreter-version", &prop ) )
+ {
+ switch ( prop )
+ {
+ case TT_INTERPRETER_VERSION_35:
+ hinting_engine = "v35";
+ break;
+ case TT_INTERPRETER_VERSION_40:
+ hinting_engine = "v40";
+ break;
+ }
+ }
+
+ else if ( !FT_Property_Get( lib, module_name,
+ "hinting-engine", &prop ) )
+ {
+ switch ( prop )
+ {
+ case FT_HINTING_FREETYPE:
+ hinting_engine = "FT";
+ break;
+ case FT_HINTING_ADOBE:
+ hinting_engine = "Adobe";
+ break;
+ }
+ }
+
+ printf( "\n"
+ "family: %s\n"
+ " style: %s\n"
+ "driver: %s %s\n"
+ "target: %s\n"
+ " flags: 0x%X\n"
+ " cmap: %d\n"
+ "glyphs: %ld\n",
+ face->family_name,
+ face->style_name,
+ module_name, hinting_engine,
+ target,
+ load_flags,
+ FT_Get_Charmap_Index( face->charmap ),
+ face->num_glyphs );
+ }
+
+
+ static FT_Error
+ get_face( FT_Face* face )
+ {
+ static unsigned char* memory_file = NULL;
+ static size_t memory_size;
+ int face_index = 0;
+ FT_Error error;
+
+
+ if ( preload )
+ {
+ if ( !memory_file )
+ {
+ FILE* file = fopen( filename, "rb" );
+
+
+ if ( file == NULL )
+ {
+ fprintf( stderr, "couldn't find or open `%s'\n", filename );
+
+ return 1;
+ }
+
+ fseek( file, 0, SEEK_END );
+ memory_size = (size_t)ftell( file );
+ fseek( file, 0, SEEK_SET );
+
+ memory_file = (FT_Byte*)malloc( memory_size );
+ if ( memory_file == NULL )
+ {
+ fprintf( stderr,
+ "couldn't allocate memory to pre-load font file\n" );
+
+ return 1;
+ }
+
+ if ( !fread( memory_file, memory_size, 1, file ) )
+ {
+ fprintf( stderr, "read error\n" );
+ free( memory_file );
+ memory_file = NULL;
+
+ return 1;
+ }
+ }
+
+ error = FT_New_Memory_Face( lib,
+ memory_file,
+ (FT_Long)memory_size,
+ face_index,
+ face );
+ }
+ else
+ error = FT_New_Face( lib, filename, face_index, face );
+
+ if ( error )
+ fprintf( stderr, "couldn't load font resource\n");
+
+ /* Set up MM_Var. */
+ if ( requested_cnt != 0 )
+ {
+ unsigned int n;
+
+
+ error = FT_Get_MM_Var( *face, &multimaster );
+ if ( error )
+ {
+ fprintf( stderr, "couldn't load MultiMaster info\n" );
+ return error;
+ }
+
+ used_num_axes = multimaster->num_axis;
+
+ for ( n = 0; n < used_num_axes; n++ )
+ {
+ FT_Var_Axis* a = multimaster->axis + n;
+
+
+ design_pos[n] = n < requested_cnt ? requested_pos[n] : a->def;
+
+ if ( design_pos[n] < a->minimum )
+ design_pos[n] = a->minimum;
+ else if ( design_pos[n] > a->maximum )
+ design_pos[n] = a->maximum;
+
+ if ( !FT_IS_SFNT( *face ) )
+ design_pos[n] = FT_RoundFix( design_pos[n] );
+ }
+
+ error = FT_Set_Var_Design_Coordinates( *face,
+ used_num_axes,
+ design_pos );
+ if ( error )
+ {
+ fprintf( stderr, "couldn't set design coordinates\n" );
+ return error;
+ }
+
+ FT_Done_MM_Var( lib, multimaster );
+ }
+
+ return error;
+ }
+
+
+ static void
+ usage( void )
+ {
+ int i;
+ char interpreter_versions[32];
+ char hinting_engines[32];
+
+
+ /* we expect that at least one interpreter version is available */
+ if ( num_tt_interpreter_versions == 1 )
+ snprintf( interpreter_versions, sizeof ( interpreter_versions ),
+ "%u",
+ tt_interpreter_versions[0]);
+ else
+ snprintf( interpreter_versions, sizeof ( interpreter_versions ),
+ "%u and %u",
+ tt_interpreter_versions[0],
+ tt_interpreter_versions[1] );
+
+ /* we expect that at least one hinting engine is available */
+ if ( num_ps_hinting_engines == 1 )
+ snprintf( hinting_engines, sizeof ( hinting_engines ),
+ "`%s'",
+ ps_hinting_engine_names[ps_hinting_engines[0]] );
+ else
+ snprintf( hinting_engines, sizeof ( hinting_engines ),
+ "`%s' and `%s'",
+ ps_hinting_engine_names[ps_hinting_engines[0]],
+ ps_hinting_engine_names[ps_hinting_engines[1]] );
+
+
+ fprintf( stderr,
+ "\n"
+ "ftbench: run FreeType benchmarks\n"
+ "--------------------------------\n"
+ "\n"
+ "Usage: ftbench [options] fontname\n"
+ "\n"
+ " -C Compare with cached version (if available).\n"
+ " -c N Use at most N iterations for each test\n"
+ " (0 means time limited).\n"
+ " -e E Set specific charmap index E.\n"
+ " -f L Use hex number L as load flags (see `FT_LOAD_XXX').\n"
+ " -H NAME Use PS hinting engine NAME.\n"
+ " Available versions are %s; default is `%s'.\n"
+ " -I VER Use TT interpreter version VER.\n"
+ " Available versions are %s; default is version %u.\n"
+ " -i I-J Forward or reverse range of glyph indices to use\n"
+ " (default is from 0 to the number of glyphs minus one).\n"
+ " -l N Set LCD filter to N\n"
+ " 0: none, 1: default, 2: light, 16: legacy\n"
+ " -m M Set maximum cache size to M KiByte (default is %d).\n",
+ hinting_engines,
+ ps_hinting_engine_names[dflt_ps_hinting_engine],
+ interpreter_versions,
+ dflt_tt_interpreter_version,
+ CACHE_SIZE );
+ fprintf( stderr,
+ " -p Preload font file in memory.\n"
+ " -r N Set render mode to N\n"
+ " 0: normal, 1: light, 2: mono, 3: LCD, 4: LCD vertical\n"
+ " (default is 0).\n"
+ " -s S Use S ppem as face size (default is %dppem).\n"
+ " If set to zero, don't call FT_Set_Pixel_Sizes.\n"
+ " Use value 0 with option `-f 1' or something similar to\n"
+ " load the glyphs unscaled, otherwise errors will show up.\n",
+ FACE_SIZE );
+ fprintf( stderr,
+ " -t T Use at most T seconds per bench (default is %.0f).\n"
+ "\n"
+ " -b tests Perform chosen tests (default is all):\n",
+ BENCH_TIME );
+
+ for ( i = 0; i < N_FT_BENCH; i++ )
+ {
+ if ( !bench_desc[i] )
+ break;
+
+ fprintf( stderr,
+ " %c %s\n", 'a' + i, bench_desc[i] );
+ }
+
+ fprintf( stderr,
+ "\n"
+ " -v Show version.\n"
+ "\n" );
+
+ exit( 1 );
+ }
+
+
+#define TEST( x ) ( !test_string || strchr( test_string, (x) ) )
+
+
+ static void
+ parse_design_coords( char *s )
+ {
+ for ( requested_cnt = 0;
+ requested_cnt < MAX_MM_AXES && *s;
+ requested_cnt++ )
+ {
+ requested_pos[requested_cnt] = (FT_Fixed)( strtod( s, &s ) * 65536.0 );
+
+ while ( *s==' ' )
+ ++s;
+ }
+ }
+
+
+ int
+ main( int argc,
+ char** argv )
+ {
+ FT_Face face;
+ FT_Error error;
+
+ unsigned long max_bytes = CACHE_SIZE * 1024;
+ char* test_string = NULL;
+ unsigned int size = FACE_SIZE;
+ int max_iter = 0;
+ double max_time = BENCH_TIME;
+ int j;
+
+ unsigned int versions[2] = { TT_INTERPRETER_VERSION_35,
+ TT_INTERPRETER_VERSION_40 };
+ unsigned int engines[2] = { FT_HINTING_FREETYPE,
+ FT_HINTING_ADOBE };
+ int version;
+ char *engine;
+
+#if defined _WIN32 && defined QPC
+ LARGE_INTEGER freq;
+
+ QueryPerformanceFrequency( &freq );
+ interval = 1e6 / freq.QuadPart;
+#endif
+
+
+ if ( FT_Init_FreeType( &lib ) )
+ {
+ fprintf( stderr, "could not initialize font library\n" );
+
+ return 1;
+ }
+
+
+ /* collect all available versions, then set again the default */
+ FT_Property_Get( lib,
+ "truetype",
+ "interpreter-version", &dflt_tt_interpreter_version );
+ for ( j = 0; j < 2; j++ )
+ {
+ error = FT_Property_Set( lib,
+ "truetype",
+ "interpreter-version", &versions[j] );
+ if ( !error )
+ tt_interpreter_versions[num_tt_interpreter_versions++] = versions[j];
+ }
+ FT_Property_Set( lib,
+ "truetype",
+ "interpreter-version", &dflt_tt_interpreter_version );
+
+ FT_Property_Get( lib,
+ "cff",
+ "hinting-engine", &dflt_ps_hinting_engine );
+ for ( j = 0; j < 2; j++ )
+ {
+ error = FT_Property_Set( lib,
+ "cff",
+ "hinting-engine", &engines[j] );
+ if ( !error )
+ ps_hinting_engines[num_ps_hinting_engines++] = engines[j];
+ }
+ FT_Property_Set( lib,
+ "cff",
+ "hinting-engine", &dflt_ps_hinting_engine );
+ FT_Property_Set( lib,
+ "type1",
+ "hinting-engine", &dflt_ps_hinting_engine );
+ FT_Property_Set( lib,
+ "t1cid",
+ "hinting-engine", &dflt_ps_hinting_engine );
+
+
+ version = (int)dflt_tt_interpreter_version;
+ engine = ps_hinting_engine_names[dflt_ps_hinting_engine];
+
+ while ( 1 )
+ {
+ int opt;
+
+
+ opt = getopt( argc, argv, "a:b:Cc:e:f:H:I:i:l:m:pr:s:t:v" );
+
+ if ( opt == -1 )
+ break;
+
+ switch ( opt )
+ {
+ case 'a':
+ parse_design_coords( optarg );
+ break;
+
+ case 'b':
+ test_string = optarg;
+ break;
+
+ case 'C':
+ FTC_Manager_New( lib,
+ 0, 0, max_bytes,
+ face_requester,
+ NULL,
+ &cache_man );
+ break;
+
+ case 'c':
+ max_iter = atoi( optarg );
+ if ( max_iter < 0 )
+ max_iter = -max_iter;
+ break;
+
+ case 'e':
+ cmap_index = atoi( optarg );
+ break;
+
+ case 'f':
+ load_flags = strtol( optarg, NULL, 16 );
+ break;
+
+ case 'H':
+ engine = optarg;
+
+ for ( j = 0; j < num_ps_hinting_engines; j++ )
+ {
+ if ( !strcmp( engine, ps_hinting_engine_names[j] ) )
+ {
+ FT_Property_Set( lib,
+ "cff",
+ "hinting-engine", &j );
+ FT_Property_Set( lib,
+ "type1",
+ "hinting-engine", &j );
+ FT_Property_Set( lib,
+ "t1cid",
+ "hinting-engine", &j );
+ break;
+ }
+ }
+
+ if ( j == num_ps_hinting_engines )
+ fprintf( stderr,
+ "warning: couldn't set hinting engine\n" );
+ break;
+
+ case 'I':
+ version = atoi( optarg );
+
+ for ( j = 0; j < num_tt_interpreter_versions; j++ )
+ {
+ if ( version == (int)tt_interpreter_versions[j] )
+ {
+ FT_Property_Set( lib,
+ "truetype",
+ "interpreter-version", &version );
+ break;
+ }
+ }
+
+ if ( j == num_tt_interpreter_versions )
+ fprintf( stderr,
+ "warning: couldn't set TT interpreter version\n" );
+ break;
+
+ case 'i':
+ {
+ int fi, li;
+
+ if ( sscanf( optarg, "%i%*[,:-]%i", &fi, &li ) == 2 )
+ {
+ first_index = fi < 0 ? 0 : fi;
+ last_index = li < 0 ? 0 : li;
+ }
+ }
+ break;
+
+ case 'l':
+ {
+ int filter = atoi( optarg );
+
+
+ switch ( filter )
+ {
+ case FT_LCD_FILTER_NONE:
+ case FT_LCD_FILTER_DEFAULT:
+ case FT_LCD_FILTER_LIGHT:
+ case FT_LCD_FILTER_LEGACY1:
+ case FT_LCD_FILTER_LEGACY:
+ FT_Library_SetLcdFilter( lib, (FT_LcdFilter)filter );
+ }
+ }
+ break;
+
+ case 'm':
+ {
+ int mb = atoi( optarg );
+
+
+ if ( mb > 0 )
+ max_bytes = (unsigned int)mb * 1024;
+ }
+ break;
+
+ case 'p':
+ preload = 1;
+ break;
+
+ case 'r':
+ {
+ int rm = atoi( optarg );
+
+
+ if ( rm < 0 || rm >= FT_RENDER_MODE_MAX )
+ render_mode = FT_RENDER_MODE_NORMAL;
+ else
+ render_mode = (FT_Render_Mode)rm;
+ }
+ break;
+
+ case 's':
+ {
+ int sz = atoi( optarg );
+
+
+ /* value 0 is special */
+ if ( sz < 0 )
+ size = 1;
+ else
+ size = (unsigned int)sz;
+ }
+ break;
+
+ case 't':
+ max_time = atof( optarg );
+ if ( max_time < 0 )
+ max_time = -max_time;
+ break;
+
+ case 'v':
+ {
+ FT_Int major, minor, patch;
+
+
+ FT_Library_Version( lib, &major, &minor, &patch );
+
+ printf( "ftbench (FreeType) %d.%d", major, minor );
+ if ( patch )
+ printf( ".%d", patch );
+ printf( "\n" );
+ exit( 0 );
+ }
+ /* break; */
+
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if ( argc != 1 )
+ usage();
+
+ filename = *argv;
+
+ if ( get_face( &face ) )
+ goto Exit;
+
+ j = printf( "\n"
+ "ftbench results for font `%s'\n",
+ filename ) - 2;
+ while ( j-- )
+ putchar( '-' );
+ putchar( '\n' );
+
+ if ( cmap_index >= face->num_charmaps )
+ cmap_index = -1;
+ if ( cmap_index >= 0 )
+ face->charmap = face->charmaps[cmap_index];
+
+ /* sync target and mode */
+ load_flags |= FT_LOAD_TARGET_( render_mode );
+ render_mode = (FT_Render_Mode)( ( load_flags & 0xF0000 ) >> 16 );
+
+ header( face );
+
+ if ( !face->num_glyphs )
+ goto Exit;
+
+ if ( first_index >= face->num_glyphs )
+ first_index = face->num_glyphs - 1;
+ if ( last_index >= face->num_glyphs )
+ last_index = face->num_glyphs - 1;
+ incr_index = last_index > first_index ? 1 : -1;
+
+ if ( size )
+ {
+ if ( FT_IS_SCALABLE( face ) )
+ {
+ if ( FT_Set_Pixel_Sizes( face, size, size ) )
+ {
+ fprintf( stderr, "failed to set pixel size to %u\n", size );
+
+ return 1;
+ }
+ }
+ else
+ {
+ size = (unsigned int)face->available_sizes[0].size >> 6;
+ fprintf( stderr,
+ "using size of first bitmap strike (%upx)\n", size );
+ FT_Select_Size( face, 0 );
+ }
+ }
+
+ if ( cache_man )
+ {
+ font_type.face_id = (FTC_FaceID)1;
+ font_type.width = size;
+ font_type.height = size;
+ font_type.flags = load_flags;
+ }
+
+ printf( "\n"
+ "font preloading into memory: %s\n"
+ "maximum cache size: %lu KiByte\n",
+ preload ? "yes" : face->stream->base ? "mapped" : "no",
+ max_bytes / 1024 );
+
+ printf( "\n"
+ "testing glyph indices from %d to %d at %u ppem\n"
+ "number of seconds for each test: %s%g\n",
+ first_index, last_index, size,
+ max_iter ? "at most " : "", max_time );
+ if ( max_iter )
+ printf( "number of iterations for each test: at most %d\n",
+ max_iter );
+
+ printf( "\n"
+ "executing tests:\n" );
+
+ for ( j = 0; j < N_FT_BENCH; j++ )
+ {
+ btest_t test;
+ FT_ULong flags;
+
+
+ if ( !TEST( 'a' + j ) )
+ continue;
+
+ test.title = NULL;
+ test.bench = NULL;
+ test.cache_first = 0;
+ test.user_data = NULL;
+
+ switch ( j )
+ {
+ case FT_BENCH_LOAD_GLYPH:
+ test.title = "Load";
+ test.bench = test_load;
+ benchmark( face, &test, max_iter, max_time );
+
+ if ( cache_man )
+ {
+ test.cache_first = 1;
+
+ if ( !FTC_ImageCache_New( cache_man, &image_cache ) )
+ {
+ test.title = "Load (image cached)";
+ test.bench = test_image_cache;
+ benchmark( face, &test, max_iter, max_time );
+ }
+
+ if ( !FTC_SBitCache_New( cache_man, &sbit_cache ) )
+ {
+ test.title = "Load (sbit cached)";
+ test.bench = test_sbit_cache;
+ if ( size )
+ benchmark( face, &test, max_iter, max_time );
+ else
+ printf( " %-25s disabled (size = 0)\n", test.title );
+ }
+ }
+ break;
+
+ case FT_BENCH_LOAD_ADVANCES:
+ test.user_data = &flags;
+
+ test.title = "Load_Advances (Normal)";
+ test.bench = test_load_advances;
+ flags = FT_LOAD_DEFAULT;
+ benchmark( face, &test, max_iter, max_time );
+
+ test.title = "Load_Advances (Fast)";
+ test.bench = test_load_advances;
+ flags = FT_LOAD_TARGET_LIGHT;
+ benchmark( face, &test, max_iter, max_time );
+
+ test.title = "Load_Advances (Unscaled)";
+ test.bench = test_load_advances;
+ flags = FT_LOAD_NO_SCALE;
+ benchmark( face, &test, max_iter, max_time );
+ break;
+
+ case FT_BENCH_RENDER:
+ test.title = "Render";
+ test.bench = test_render;
+ if ( size )
+ benchmark( face, &test, max_iter, max_time );
+ else
+ printf( " %-25s disabled (size = 0)\n", test.title );
+ break;
+
+ case FT_BENCH_GET_GLYPH:
+ test.title = "Get_Glyph";
+ test.bench = test_get_glyph;
+ benchmark( face, &test, max_iter, max_time );
+ break;
+
+ case FT_BENCH_GET_CBOX:
+ test.title = "Get_CBox";
+ test.bench = test_get_cbox;
+ benchmark( face, &test, max_iter, max_time );
+ break;
+
+ case FT_BENCH_GET_BBOX:
+ test.title = "Get_BBox";
+ test.bench = test_get_bbox;
+ {
+ FT_Matrix rot30 = { 0xDDB4, -0x8000, 0x8000, 0xDDB4 };
+
+
+ /* rotate outlines by 30 degrees so that CBox and BBox differ */
+ FT_Set_Transform( face, &rot30, NULL );
+ benchmark( face, &test, max_iter, max_time );
+ FT_Set_Transform( face, NULL, NULL );
+ }
+ break;
+
+ case FT_BENCH_CMAP:
+ {
+ bcharset_t charset;
+
+
+ get_charset( face, &charset );
+ if ( charset.code )
+ {
+ test.user_data = (void*)&charset;
+
+
+ test.title = "Get_Char_Index";
+ test.bench = test_get_char_index;
+
+ benchmark( face, &test, max_iter, max_time );
+
+ if ( cache_man &&
+ !FTC_CMapCache_New( cache_man, &cmap_cache ) )
+ {
+ test.cache_first = 1;
+
+ test.title = "Get_Char_Index (cached)";
+ test.bench = test_cmap_cache;
+ benchmark( face, &test, max_iter, max_time );
+ }
+
+ free( charset.code );
+ }
+ }
+ break;
+
+ case FT_BENCH_CMAP_ITER:
+ test.title = "Iterate CMap";
+ test.bench = test_cmap_iter;
+ benchmark( face, &test, max_iter, max_time );
+ break;
+
+ case FT_BENCH_NEW_FACE:
+ test.title = "New_Face";
+ test.bench = test_new_face;
+ benchmark( face, &test, max_iter, max_time );
+ break;
+
+ case FT_BENCH_EMBOLDEN:
+ test.title = "Embolden";
+ test.bench = test_embolden;
+ if ( size )
+ benchmark( face, &test, max_iter, max_time );
+ else
+ printf( " %-25s disabled (size = 0)\n", test.title );
+ break;
+
+ case FT_BENCH_STROKE:
+ test.title = "Stroke";
+ test.bench = test_stroke;
+ if ( size )
+ benchmark( face, &test, max_iter, max_time );
+ else
+ printf( " %-25s disabled (size = 0)\n", test.title );
+ break;
+
+ case FT_BENCH_NEW_FACE_AND_LOAD_GLYPH:
+ test.title = "New_Face & load glyph(s)";
+ test.bench = test_new_face_and_load_glyph;
+ benchmark( face, &test, max_iter, max_time );
+ break;
+ }
+ }
+
+ if ( cache_man )
+ FTC_Manager_Done( cache_man );
+
+ Exit:
+ /* releases any remaining FT_Face object too */
+ FT_Done_FreeType( lib );
+
+ return 0;
+ }
+
+
+/* End */
diff --git a/src/tools/ftbench/mlgetopt.h b/src/tools/ftbench/mlgetopt.h
new file mode 100644
index 000000000..ce981b1c6
--- /dev/null
+++ b/src/tools/ftbench/mlgetopt.h
@@ -0,0 +1,44 @@
+/*
+ * This is a cheap replacement for getopt() because that routine is not
+ * available on some platforms and behaves differently on other platforms.
+ *
+ * This code is hereby expressly placed in the public domain.
+ * mleisher@crl.nmsu.edu (Mark Leisher)
+ * 10 October 1997
+ */
+
+#ifndef MLGETOPT_H_
+#define MLGETOPT_H_
+
+#ifdef VMS
+#include <stdio.h>
+#define getopt local_getopt
+#define optind local_optind
+#define opterr local_opterr
+#define optarg local_optarg
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+ extern int opterr;
+ extern int optind;
+ extern char* optarg;
+
+ extern int getopt(
+#ifdef __STDC__
+ int argc,
+ char* const* argv,
+ const char* pattern
+#endif
+ );
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* MLGETOPT_H_ */
+
+
+/* End */
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [freetype2] gsoc2023-ftbench 9dd15018c: ftbench demo with seperate makefile,
Werner Lemberg <=