blob: b894aa2072e1038e945ccb3bb79eebac484588f3 [file] [log] [blame]
#include <nirvana.h>
#include NV_VIEWPORT_H
#include <stdio.h>
#include <ft2build.h>
#include FT_FREETYPE_H
/* include FreeType internals to debug hints */
#include <../src/pshinter/pshrec.h>
#include <../src/pshinter/pshfit.h>
#include <time.h> /* for clock() */
/* SunOS 4.1.* does not define CLOCKS_PER_SEC, so include <sys/param.h> */
/* to get the HZ macro which is the equivalent. */
#if defined(__sun__) && !defined(SVR4) && !defined(__SVR4)
#include <sys/param.h>
#define CLOCKS_PER_SEC HZ
#endif
static int depth = 0;
static NV_Renderer renderer;
static NV_Painter painter;
static NV_Pixmap target;
static NV_Error error;
static NV_Memory memory;
static NVV_Display display;
static NVV_Surface surface;
static FT_Library freetype;
static FT_Face face;
static NV_Pos glyph_scale;
static NV_Pos glyph_org_x;
static NV_Pos glyph_org_y;
static NV_Transform glyph_transform; /* font units -> device pixels */
static NV_Transform size_transform; /* subpixels -> device pixels */
static NV_Scale grid_scale = 1.0;
static int glyph_index;
static int pixel_size = 12;
static int option_show_axis = 1;
static int option_show_dots = 1;
static int option_show_stroke = 1;
static int option_show_glyph = 1;
static int option_show_grid = 1;
static int option_show_em = 0;
static int option_show_ps_hints = 1;
static int option_show_horz_hints = 1;
static int option_show_vert_hints = 1;
static int option_hinting = 1;
static char temp_message[1024];
PS_Hints the_ps_hints = 0;
int ps_debug_no_horz_hints = 0;
int ps_debug_no_vert_hints = 0;
PSH_HintFunc ps_debug_hint_func = 0;
#define AXIS_COLOR 0xFFFF0000
#define GRID_COLOR 0xFFD0D0D0
#define ON_COLOR 0xFFFF2000
#define OFF_COLOR 0xFFFF0080
#define BACKGROUND_COLOR 0xFFFFFFFF
#define TEXT_COLOR 0xFF000000
#define EM_COLOR 0x80008000
#define GHOST_HINT_COLOR 0xE00000FF
#define STEM_HINT_COLOR 0xE02020FF
#define STEM_JOIN_COLOR 0xE020FF20
/* print message and abort program */
static void
Panic( const char* message )
{
fprintf( stderr, "PANIC: %s\n", message );
exit(1);
}
static void
reset_scale( NV_Scale scale )
{
/* compute font units -> grid pixels scale factor */
glyph_scale = target->width*0.75 / face->units_per_EM * scale;
/* setup font units -> grid pixels transform */
nv_transform_set_scale( &glyph_transform, glyph_scale, -glyph_scale );
glyph_org_x = glyph_transform.delta.x = target->width*0.125;
glyph_org_y = glyph_transform.delta.y = target->height*0.875;
/* setup subpixels -> grid pixels transform */
nv_transform_set_scale( &size_transform,
glyph_scale/nv_fromfixed(face->size->metrics.x_scale),
- glyph_scale/nv_fromfixed(face->size->metrics.y_scale) );
size_transform.delta = glyph_transform.delta;
}
static void
reset_size( int pixel_size, NV_Scale scale )
{
FT_Set_Pixel_Sizes( face, pixel_size, pixel_size );
reset_scale( scale );
}
static void
clear_background( void )
{
nv_pixmap_fill_rect( target, 0, 0, target->width, target->height,
BACKGROUND_COLOR );
}
static void
draw_grid( void )
{
int x = (int)glyph_org_x;
int y = (int)glyph_org_y;
/* draw grid */
if ( option_show_grid )
{
NV_Scale min, max, x, step;
/* draw vertical grid bars */
step = 64. * size_transform.matrix.xx;
if (step > 1.)
{
min = max = glyph_org_x;
while ( min - step >= 0 ) min -= step;
while ( max + step < target->width ) max += step;
for ( x = min; x <= max; x += step )
nv_pixmap_fill_rect( target, (NV_Int)(x+.5), 0,
1, target->height, GRID_COLOR );
}
/* draw horizontal grid bars */
step = -64. * size_transform.matrix.yy;
if (step > 1.)
{
min = max = glyph_org_y;
while ( min - step >= 0 ) min -= step;
while ( max + step < target->height ) max += step;
for ( x = min; x <= max; x += step )
nv_pixmap_fill_rect( target, 0, (NV_Int)(x+.5),
target->width, 1, GRID_COLOR );
}
}
/* draw axis */
if ( option_show_axis )
{
nv_pixmap_fill_rect( target, x, 0, 1, target->height, AXIS_COLOR );
nv_pixmap_fill_rect( target, 0, y, target->width, 1, AXIS_COLOR );
}
if ( option_show_em )
{
NV_Path path;
NV_Path stroke;
NV_UInt units = (NV_UInt)face->units_per_EM;
nv_path_new_rectangle( renderer, 0, 0, units, units, 0, 0, &path );
nv_path_transform( path, &glyph_transform );
nv_path_stroke( path, 1.5, nv_path_linecap_butt, nv_path_linejoin_miter,
4.0, &stroke );
nv_painter_set_color( painter, EM_COLOR, 256 );
nv_painter_fill_path( painter, NULL, 0, stroke );
nv_path_destroy( stroke );
nv_path_destroy( path );
}
}
static int pshint_cpos = 0;
static int pshint_vertical = -1;
static void
draw_ps_hint( PSH_Hint hint, FT_Bool vertical )
{
int x1, x2;
NV_Vector v;
if ( pshint_vertical != vertical )
{
if (vertical)
pshint_cpos = 40;
else
pshint_cpos = 10;
pshint_vertical = vertical;
}
if (vertical)
{
if ( !option_show_vert_hints )
return;
v.x = hint->cur_pos;
v.y = 0;
nv_vector_transform( &v, &size_transform );
x1 = (int)(v.x + 0.5);
v.x = hint->cur_pos + hint->cur_len;
v.y = 0;
nv_vector_transform( &v, &size_transform );
x2 = (int)(v.x + 0.5);
nv_pixmap_fill_rect( target, x1, 0, 1, target->height,
psh_hint_is_ghost(hint)
? GHOST_HINT_COLOR : STEM_HINT_COLOR );
if ( psh_hint_is_ghost(hint) )
{
x1 --;
x2 = x1 + 2;
}
else
nv_pixmap_fill_rect( target, x2, 0, 1, target->height,
psh_hint_is_ghost(hint)
? GHOST_HINT_COLOR : STEM_HINT_COLOR );
nv_pixmap_fill_rect( target, x1, pshint_cpos, x2+1-x1, 1,
STEM_JOIN_COLOR );
}
else
{
if (!option_show_horz_hints)
return;
v.y = hint->cur_pos;
v.x = 0;
nv_vector_transform( &v, &size_transform );
x1 = (int)(v.y + 0.5);
v.y = hint->cur_pos + hint->cur_len;
v.x = 0;
nv_vector_transform( &v, &size_transform );
x2 = (int)(v.y + 0.5);
nv_pixmap_fill_rect( target, 0, x1, target->width, 1,
psh_hint_is_ghost(hint)
? GHOST_HINT_COLOR : STEM_HINT_COLOR );
if ( psh_hint_is_ghost(hint) )
{
x1 --;
x2 = x1 + 2;
}
else
nv_pixmap_fill_rect( target, 0, x2, target->width, 1,
psh_hint_is_ghost(hint)
? GHOST_HINT_COLOR : STEM_HINT_COLOR );
nv_pixmap_fill_rect( target, pshint_cpos, x2, 1, x1+1-x2,
STEM_JOIN_COLOR );
}
printf( "[%7.3f %7.3f] %c\n", hint->cur_pos/64.0, (hint->cur_pos+hint->cur_len)/64.0, vertical ? 'v' : 'h' );
pshint_cpos += 10;
}
static void
draw_glyph( int glyph_index )
{
NV_Path path;
pshint_vertical = -1;
ps_debug_hint_func = option_show_ps_hints ? draw_ps_hint : 0;
error = FT_Load_Glyph( face, glyph_index, option_hinting
? FT_LOAD_NO_BITMAP
: FT_LOAD_NO_BITMAP | FT_LOAD_NO_HINTING );
if (error) Panic( "could not load glyph" );
if ( face->glyph->format != ft_glyph_format_outline )
Panic( "could not load glyph outline" );
error = nv_path_new_from_outline( renderer,
(NV_Outline*)&face->glyph->outline,
&size_transform,
&path );
if (error) Panic( "could not create glyph path" );
/* tracé du glyphe plein */
if ( option_show_glyph )
{
nv_painter_set_color ( painter, 0xFF404080, 128 );
nv_painter_fill_path ( painter, 0, 0, path );
}
if ( option_show_stroke )
{
NV_Path stroke;
error = nv_path_stroke( path, 0.6,
nv_path_linecap_butt,
nv_path_linejoin_miter,
1.0, &stroke );
if (error) Panic( "could not stroke glyph path" );
nv_painter_set_color ( painter, 0xFF000040, 256 );
nv_painter_fill_path ( painter, 0, 0, stroke );
nv_path_destroy( stroke );
}
/* tracé des points de controle */
if ( option_show_dots )
{
NV_Path plot;
NV_Outline out;
NV_Scale r = 2;
NV_Int n, first, last;
nv_path_new_circle( renderer, 0, 0, 2., &plot );
nv_path_get_outline( path, NULL, memory, &out );
first = 0;
for ( n = 0; n < out.n_contours; n++ )
{
int m;
NV_Transform trans;
NV_Color color;
NV_SubVector* vec;
last = out.contours[n];
for ( m = first; m <= last; m++ )
{
color = (out.tags[m] & FT_Curve_Tag_On)
? ON_COLOR
: OFF_COLOR;
vec = out.points + m;
nv_transform_set_translate( &trans, vec->x/64.0, vec->y/64.0 );
nv_painter_set_color( painter, color, 256 );
nv_painter_fill_path( painter, &trans, 0, plot );
}
first = last + 1;
}
nv_path_destroy( plot );
}
nv_path_destroy( path );
/* autre infos */
{
char temp[1024];
char temp2[64];
sprintf( temp, "font name : %s (%s)", face->family_name, face->style_name );
nv_pixmap_cell_text( target, 0, 0, temp, TEXT_COLOR );
FT_Get_Glyph_Name( face, glyph_index, temp2, 63 );
temp2[63] = 0;
sprintf( temp, "glyph %4d: %s", glyph_index, temp2 );
nv_pixmap_cell_text( target, 0, 8, temp, TEXT_COLOR );
if ( temp_message[0] )
{
nv_pixmap_cell_text( target, 0, 16, temp_message, TEXT_COLOR );
temp_message[0] = 0;
}
}
}
#if 0
static void
draw_ps_hints( void )
{
if ( option_show_ps_hints && the_ps_hints )
{
PS_Dimension dim;
PS_Hint hint;
NV_UInt count;
NV_Int cpos;
NV_Vector v;
/* draw vertical stems */
if ( option_show_vert_hints )
{
dim = &the_ps_hints->dimension[1];
hint = dim->hints.hints;
cpos = 40;
for ( count = dim->hints.num_hints; count > 0; count--, hint++ )
{
NV_Int x1, x2;
v.x = hint->pos;
v.y = 0;
nv_vector_transform( &v, &glyph_transform );
x1 = (int)(v.x+0.5);
x1 = glyph_org_x + hint->pos*glyph_scale;
nv_pixmap_fill_rect( target, x1, 0, 1, target->height,
ps_hint_is_ghost(hint)
? GHOST_HINT_COLOR : STEM_HINT_COLOR );
if ( !ps_hint_is_ghost(hint) )
{
v.x = hint->pos + hint->len;
v.y = 0;
nv_vector_transform( &v, &glyph_transform );
x2 = (int)(v.x+0.5);
x2 = glyph_org_x + (hint->pos + hint->len)*glyph_scale;
nv_pixmap_fill_rect( target, x2, 0, 1, target->height,
STEM_HINT_COLOR );
}
else
{
x1 -= 1;
x2 = x1 + 2;
}
nv_pixmap_fill_rect( target, x1, cpos, x2-x1+1, 1,
STEM_JOIN_COLOR );
cpos += 10;
}
}
/* draw horizontal stems */
if ( option_show_horz_hints )
{
dim = &the_ps_hints->dimension[0];
hint = dim->hints.hints;
cpos = 10;
for ( count = dim->hints.num_hints; count > 0; count--, hint++ )
{
NV_Int y1, y2;
v.x = 0;
v.y = hint->pos;
nv_vector_transform( &v, &glyph_transform );
y1 = (int)(v.y+0.5);
y1 = glyph_org_y - hint->pos*glyph_scale;
nv_pixmap_fill_rect( target, 0, y1, target->width, 1,
ps_hint_is_ghost(hint)
? GHOST_HINT_COLOR : STEM_HINT_COLOR );
if ( !ps_hint_is_ghost(hint) )
{
v.x = 0;
v.y = hint->pos + hint->len;
nv_vector_transform( &v, &glyph_transform );
y2 = (int)(v.y+0.5);
y2 = glyph_org_y - (hint->pos + hint->len)*glyph_scale;
nv_pixmap_fill_rect( target, 0, y2, target->width, 1,
STEM_HINT_COLOR );
}
else
{
y1 -= 1;
y2 = y1 + 2;
}
nv_pixmap_fill_rect( target, cpos, y2, 1, y1-y2+1,
STEM_JOIN_COLOR );
cpos += 10;
}
}
}
}
#endif
static void
handle_event( NVV_EventRec* ev )
{
switch (ev->key)
{
case NVV_Key_Left:
{
if ( glyph_index > 0 )
glyph_index--;
break;
}
case NVV_Key_Right:
{
if ( glyph_index+1 < face->num_glyphs )
glyph_index++;
break;
}
case NVV_KEY('x'):
{
option_show_axis = !option_show_axis;
break;
}
case NVV_KEY('s'):
{
option_show_stroke = !option_show_stroke;
break;
}
case NVV_KEY('g'):
{
option_show_glyph = !option_show_glyph;
break;
}
case NVV_KEY('d'):
{
option_show_dots = !option_show_dots;
break;
}
case NVV_KEY('e'):
{
option_show_em = !option_show_em;
break;
}
case NVV_KEY('+'):
{
grid_scale *= 1.2;
reset_scale( grid_scale );
break;
}
case NVV_KEY('-'):
{
if (grid_scale > 0.3)
{
grid_scale /= 1.2;
reset_scale( grid_scale );
}
break;
}
case NVV_Key_Up:
{
pixel_size++;
reset_size( pixel_size, grid_scale );
sprintf( temp_message, "pixel size = %d", pixel_size );
break;
}
case NVV_Key_Down:
{
if (pixel_size > 1)
{
pixel_size--;
reset_size( pixel_size, grid_scale );
sprintf( temp_message, "pixel size = %d", pixel_size );
}
break;
}
case NVV_KEY('z'):
{
ps_debug_no_vert_hints = !ps_debug_no_vert_hints;
sprintf( temp_message, "vertical hints processing is now %s",
ps_debug_no_vert_hints ? "off" : "on" );
break;
}
case NVV_KEY('a'):
{
ps_debug_no_horz_hints = !ps_debug_no_horz_hints;
sprintf( temp_message, "horizontal hints processing is now %s",
ps_debug_no_horz_hints ? "off" : "on" );
break;
}
case NVV_KEY('Z'):
{
option_show_vert_hints = !option_show_vert_hints;
sprintf( temp_message, "vertical hints display is now %s",
option_show_vert_hints ? "off" : "on" );
break;
}
case NVV_KEY('A'):
{
option_show_horz_hints = !option_show_horz_hints;
sprintf( temp_message, "horizontal hints display is now %s",
option_show_horz_hints ? "off" : "on" );
break;
}
case NVV_KEY('h'):
{
option_hinting = !option_hinting;
sprintf( temp_message, "hinting is now %s",
option_hinting ? "off" : "on" );
break;
}
}
}
int main( int argc, char** argv )
{
/* create library */
error = nv_renderer_new( 0, &renderer );
if (error) Panic( "could not create Nirvana renderer" );
memory = nv_renderer_get_memory( renderer );
error = nvv_display_new( renderer, &display );
if (error) Panic( "could not create display" );
error = nvv_surface_new( display, 400, 400, nv_pixmap_type_argb, &surface );
if (error) Panic( "could not create surface" );
target = nvv_surface_get_pixmap( surface );
error = nv_painter_new( renderer, &painter );
if (error) Panic( "could not create painter" );
nv_painter_set_target( painter, target );
clear_background();
error = FT_Init_FreeType( &freetype );
if (error) Panic( "could not initialise FreeType" );
error = FT_New_Face( freetype, "h:/fonts/cour.pfa", 0, &face );
if (error) Panic( "could not open font face" );
reset_size( pixel_size, grid_scale );
nvv_surface_set_title( surface, "FreeType Glyph Viewer" );
{
NVV_EventRec event;
glyph_index = 0;
for ( ;; )
{
clear_background();
draw_grid();
the_ps_hints = 0;
draw_glyph( glyph_index );
/* draw_ps_hints(); */
nvv_surface_refresh( surface, NULL );
nvv_surface_listen( surface, 0, &event );
if ( event.key == NVV_Key_Esc )
break;
handle_event( &event );
switch (event.key)
{
case NVV_Key_Esc:
goto Exit;
default:
;
}
}
}
Exit:
/* wait for escape */
/* destroy display (and surface) */
nvv_display_unref( display );
nv_renderer_unref( renderer );
return 0;
}