f2c56515f5
routine that created nasty alignment artefacts. * src/pshinter/pshrec.c, tests/gview.c: debugging updates.. * src/smooth/ftgrays.c: de-activated experimental gamme support, apparently, "optimal" gamma tables depend on the monitor type, resolution and general karma, so it's better to compute them outside of the rasterizer itself..
1335 lines
35 KiB
C
1335 lines
35 KiB
C
#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/pshalgo1.h>
|
||
#include <../src/pshinter/pshalgo2.h>
|
||
|
||
#include <../src/autohint/ahtypes.h>
|
||
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
/***** *****/
|
||
/***** ROOT DEFINITIONS *****/
|
||
/***** *****/
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
|
||
|
||
#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 first_glyph = 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;
|
||
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
/***** *****/
|
||
/***** OPTIONS, COLORS and OTHERS *****/
|
||
/***** *****/
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
|
||
static int option_show_axis = 1;
|
||
static int option_show_dots = 1;
|
||
static int option_show_stroke = 0;
|
||
static int option_show_glyph = 1;
|
||
static int option_show_grid = 1;
|
||
static int option_show_em = 0;
|
||
static int option_show_smooth = 1;
|
||
static int option_show_blues = 0;
|
||
static int option_show_edges = 0;
|
||
static int option_show_segments = 1;
|
||
static int option_show_links = 1;
|
||
static int option_show_indices = 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];
|
||
|
||
static NV_Path symbol_dot = NULL;
|
||
static NV_Path symbol_circle = NULL;
|
||
static NV_Path symbol_square = NULL;
|
||
static NV_Path symbol_rect_h = NULL;
|
||
static NV_Path symbol_rect_v = NULL;
|
||
|
||
|
||
|
||
#define AXIS_COLOR 0xFFFF0000
|
||
#define GRID_COLOR 0xFFD0D0D0
|
||
#define ON_COLOR 0xFFFF2000
|
||
#define OFF_COLOR 0xFFFF0080
|
||
#define STRONG_COLOR 0xFF404040
|
||
#define INTERP_COLOR 0xFF206040
|
||
#define SMOOTH_COLOR 0xF000B040
|
||
#define BACKGROUND_COLOR 0xFFFFFFFF
|
||
#define TEXT_COLOR 0xFF000000
|
||
#define EM_COLOR 0x80008000
|
||
#define BLUES_TOP_COLOR 0x4000008F
|
||
#define BLUES_BOT_COLOR 0x40008F00
|
||
|
||
#define GHOST_HINT_COLOR 0xE00000FF
|
||
#define STEM_HINT_COLOR 0xE02020FF
|
||
#define STEM_JOIN_COLOR 0xE020FF20
|
||
|
||
#define EDGE_COLOR 0xF0704070
|
||
#define SEGMENT_COLOR 0xF0206040
|
||
#define LINK_COLOR 0xF0FFFF00
|
||
#define SERIF_LINK_COLOR 0xF0FF808F
|
||
|
||
/* print message and abort program */
|
||
static void
|
||
Panic( const char* message )
|
||
{
|
||
fprintf( stderr, "PANIC: %s\n", message );
|
||
exit(1);
|
||
}
|
||
|
||
|
||
static void
|
||
init_symbols( void )
|
||
{
|
||
nv_path_new_rectangle( renderer, -1, -1, 3, 3, 0, 0, &symbol_square );
|
||
nv_path_new_rectangle( renderer, -1, -6, 2, 12, 0, 0, &symbol_rect_v );
|
||
nv_path_new_rectangle( renderer, -6, -1, 12, 2, 0, 0, &symbol_rect_h );
|
||
nv_path_new_circle( renderer, 0, 0, 3., &symbol_dot );
|
||
nv_path_stroke( symbol_dot, 0.6,
|
||
nv_path_linecap_butt,
|
||
nv_path_linejoin_miter, 1.,
|
||
&symbol_circle );
|
||
|
||
nv_path_destroy( symbol_dot );
|
||
nv_path_new_circle( renderer, 0, 0, 2., &symbol_dot );
|
||
|
||
}
|
||
|
||
static void
|
||
done_symbols( void )
|
||
{
|
||
nv_path_destroy( symbol_circle );
|
||
nv_path_destroy( symbol_dot );
|
||
nv_path_destroy( symbol_rect_v );
|
||
nv_path_destroy( symbol_rect_h );
|
||
nv_path_destroy( symbol_square );
|
||
}
|
||
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
/***** *****/
|
||
/***** COMMON GRID DRAWING ROUTINES *****/
|
||
/***** *****/
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
|
||
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 );
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
/***** *****/
|
||
/***** POSTSCRIPT GLOBALS ROUTINES *****/
|
||
/***** *****/
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
|
||
#include <../src/pshinter/pshglob.h>
|
||
|
||
static void
|
||
draw_ps_blue_zones( void )
|
||
{
|
||
if ( option_show_blues && ps_debug_globals )
|
||
{
|
||
PSH_Blues blues = &ps_debug_globals->blues;
|
||
PSH_Blue_Table table;
|
||
NV_Vector v;
|
||
FT_Int y1, y2;
|
||
FT_UInt count;
|
||
PSH_Blue_Zone zone;
|
||
|
||
/* draw top zones */
|
||
table = &blues->normal_top;
|
||
count = table->count;
|
||
zone = table->zones;
|
||
|
||
for ( ; count > 0; count--, zone++ )
|
||
{
|
||
v.x = 0;
|
||
if ( !ps_debug_no_horz_hints )
|
||
{
|
||
v.y = zone->cur_ref + zone->cur_delta;
|
||
nv_vector_transform( &v, &size_transform );
|
||
}
|
||
else
|
||
{
|
||
v.y = zone->org_ref + zone->org_delta;
|
||
nv_vector_transform( &v, &glyph_transform );
|
||
}
|
||
y1 = (int)(v.y + 0.5);
|
||
|
||
v.x = 0;
|
||
if ( !ps_debug_no_horz_hints )
|
||
{
|
||
v.y = zone->cur_ref;
|
||
nv_vector_transform( &v, &size_transform );
|
||
}
|
||
else
|
||
{
|
||
v.y = zone->org_ref;
|
||
nv_vector_transform( &v, &glyph_transform );
|
||
}
|
||
y2 = (int)(v.y + 0.5);
|
||
|
||
nv_pixmap_fill_rect( target, 0, y1,
|
||
target->width, y2-y1+1,
|
||
BLUES_TOP_COLOR );
|
||
|
||
#if 0
|
||
printf( "top [%.3f %.3f]\n", zone->cur_bottom/64.0, zone->cur_top/64.0 );
|
||
#endif
|
||
}
|
||
|
||
|
||
/* draw bottom zones */
|
||
table = &blues->normal_bottom;
|
||
count = table->count;
|
||
zone = table->zones;
|
||
|
||
for ( ; count > 0; count--, zone++ )
|
||
{
|
||
v.x = 0;
|
||
v.y = zone->cur_ref;
|
||
nv_vector_transform( &v, &size_transform );
|
||
y1 = (int)(v.y + 0.5);
|
||
|
||
v.x = 0;
|
||
v.y = zone->cur_ref + zone->cur_delta;
|
||
nv_vector_transform( &v, &size_transform );
|
||
y2 = (int)(v.y + 0.5);
|
||
|
||
nv_pixmap_fill_rect( target, 0, y1,
|
||
target->width, y2-y1+1,
|
||
BLUES_BOT_COLOR );
|
||
|
||
#if 0
|
||
printf( "bot [%.3f %.3f]\n", zone->cur_bottom/64.0, zone->cur_top/64.0 );
|
||
#endif
|
||
}
|
||
}
|
||
}
|
||
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
/***** *****/
|
||
/***** POSTSCRIPT HINTER ALGORITHM 1 ROUTINES *****/
|
||
/***** *****/
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
|
||
#include <../src/pshinter/pshalgo1.h>
|
||
|
||
static int pshint_cpos = 0;
|
||
static int pshint_vertical = -1;
|
||
|
||
static void
|
||
draw_ps1_hint( PSH1_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,
|
||
psh1_hint_is_ghost(hint)
|
||
? GHOST_HINT_COLOR : STEM_HINT_COLOR );
|
||
|
||
if ( psh1_hint_is_ghost(hint) )
|
||
{
|
||
x1 --;
|
||
x2 = x1 + 2;
|
||
}
|
||
else
|
||
nv_pixmap_fill_rect( target, x2, 0, 1, target->height,
|
||
psh1_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,
|
||
psh1_hint_is_ghost(hint)
|
||
? GHOST_HINT_COLOR : STEM_HINT_COLOR );
|
||
|
||
if ( psh1_hint_is_ghost(hint) )
|
||
{
|
||
x1 --;
|
||
x2 = x1 + 2;
|
||
}
|
||
else
|
||
nv_pixmap_fill_rect( target, 0, x2, target->width, 1,
|
||
psh1_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 );
|
||
}
|
||
|
||
#if 0
|
||
printf( "[%7.3f %7.3f] %c\n", hint->cur_pos/64.0, (hint->cur_pos+hint->cur_len)/64.0, vertical ? 'v' : 'h' );
|
||
#endif
|
||
|
||
pshint_cpos += 10;
|
||
}
|
||
|
||
|
||
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
/***** *****/
|
||
/***** POSTSCRIPT HINTER ALGORITHM 2 ROUTINES *****/
|
||
/***** *****/
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
|
||
#include <../src/pshinter/pshalgo2.h>
|
||
|
||
static void
|
||
draw_ps2_hint( PSH2_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,
|
||
psh2_hint_is_ghost(hint)
|
||
? GHOST_HINT_COLOR : STEM_HINT_COLOR );
|
||
|
||
if ( psh2_hint_is_ghost(hint) )
|
||
{
|
||
x1 --;
|
||
x2 = x1 + 2;
|
||
}
|
||
else
|
||
nv_pixmap_fill_rect( target, x2, 0, 1, target->height,
|
||
psh2_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,
|
||
psh2_hint_is_ghost(hint)
|
||
? GHOST_HINT_COLOR : STEM_HINT_COLOR );
|
||
|
||
if ( psh2_hint_is_ghost(hint) )
|
||
{
|
||
x1 --;
|
||
x2 = x1 + 2;
|
||
}
|
||
else
|
||
nv_pixmap_fill_rect( target, 0, x2, target->width, 1,
|
||
psh2_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 );
|
||
}
|
||
|
||
#if 0
|
||
printf( "[%7.3f %7.3f] %c\n", hint->cur_pos/64.0, (hint->cur_pos+hint->cur_len)/64.0, vertical ? 'v' : 'h' );
|
||
#endif
|
||
|
||
pshint_cpos += 10;
|
||
}
|
||
|
||
|
||
static void
|
||
ps2_draw_control_points( void )
|
||
{
|
||
if ( ps2_debug_glyph )
|
||
{
|
||
PSH2_Glyph glyph = ps2_debug_glyph;
|
||
PSH2_Point point = glyph->points;
|
||
FT_UInt count = glyph->num_points;
|
||
NV_Transform transform, *trans = &transform;
|
||
NV_Path vert_rect;
|
||
NV_Path horz_rect;
|
||
NV_Path dot, circle;
|
||
|
||
for ( ; count > 0; count--, point++ )
|
||
{
|
||
NV_Vector vec;
|
||
|
||
vec.x = point->cur_x;
|
||
vec.y = point->cur_y;
|
||
nv_vector_transform( &vec, &size_transform );
|
||
|
||
nv_transform_set_translate( trans, vec.x, vec.y );
|
||
|
||
if ( option_show_smooth && !psh2_point_is_smooth(point) )
|
||
{
|
||
nv_painter_set_color( painter, SMOOTH_COLOR, 256 );
|
||
nv_painter_fill_path( painter, trans, 0, symbol_circle );
|
||
}
|
||
|
||
if (option_show_horz_hints)
|
||
{
|
||
if ( point->flags_y & PSH2_POINT_STRONG )
|
||
{
|
||
nv_painter_set_color( painter, STRONG_COLOR, 256 );
|
||
nv_painter_fill_path( painter, trans, 0, symbol_rect_h );
|
||
}
|
||
}
|
||
|
||
if (option_show_vert_hints)
|
||
{
|
||
if ( point->flags_x & PSH2_POINT_STRONG )
|
||
{
|
||
nv_painter_set_color( painter, STRONG_COLOR, 256 );
|
||
nv_painter_fill_path( painter, trans, 0, symbol_rect_v );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
static void
|
||
ps_print_hints( void )
|
||
{
|
||
if ( ps_debug_hints )
|
||
{
|
||
FT_Int dimension;
|
||
PSH_Dimension dim;
|
||
|
||
for ( dimension = 1; dimension >= 0; dimension-- )
|
||
{
|
||
PS_Dimension dim = &ps_debug_hints->dimension[ dimension ];
|
||
PS_Mask mask = dim->masks.masks;
|
||
FT_UInt count = dim->masks.num_masks;
|
||
|
||
printf( "%s hints -------------------------\n",
|
||
dimension ? "vertical" : "horizontal" );
|
||
|
||
for ( ; count > 0; count--, mask++ )
|
||
{
|
||
FT_UInt index;
|
||
|
||
printf( "mask -> %d\n", mask->end_point );
|
||
for ( index = 0; index < mask->num_bits; index++ )
|
||
{
|
||
if ( mask->bytes[ index >> 3 ] & (0x80 >> (index & 7)) )
|
||
{
|
||
PS_Hint hint = dim->hints.hints + index;
|
||
|
||
printf( "%c [%3d %3d (%4d)]\n", dimension ? "v" : "h",
|
||
hint->pos, hint->pos + hint->len, hint->len );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
/***** *****/
|
||
/***** AUTOHINTER DRAWING ROUTINES *****/
|
||
/***** *****/
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
|
||
static NV_Path
|
||
ah_link_path( NV_Vector* p1,
|
||
NV_Vector* p4,
|
||
NV_Bool vertical )
|
||
{
|
||
NV_PathWriter writer;
|
||
NV_Vector p2, p3;
|
||
NV_Path path, stroke;
|
||
|
||
if ( vertical )
|
||
{
|
||
p2.x = p4->x;
|
||
p2.y = p1->y;
|
||
|
||
p3.x = p1->x;
|
||
p3.y = p4->y;
|
||
}
|
||
else
|
||
{
|
||
p2.x = p1->x;
|
||
p2.y = p4->y;
|
||
|
||
p3.x = p4->x;
|
||
p3.y = p1->y;
|
||
}
|
||
|
||
nv_path_writer_new( renderer, &writer );
|
||
nv_path_writer_moveto( writer, p1 );
|
||
nv_path_writer_cubicto( writer, &p2, &p3, p4 );
|
||
nv_path_writer_end( writer );
|
||
|
||
path = nv_path_writer_get_path( writer );
|
||
nv_path_writer_destroy( writer );
|
||
|
||
nv_path_stroke( path, 1., nv_path_linecap_butt, nv_path_linejoin_round, 1., &stroke );
|
||
|
||
nv_path_destroy( path );
|
||
|
||
return stroke;
|
||
}
|
||
|
||
|
||
static void
|
||
ah_draw_smooth_points( void )
|
||
{
|
||
if ( ah_debug_hinter && option_show_smooth )
|
||
{
|
||
AH_Outline* glyph = ah_debug_hinter->glyph;
|
||
FT_UInt count = glyph->num_points;
|
||
AH_Point* point = glyph->points;
|
||
|
||
nv_painter_set_color( painter, SMOOTH_COLOR, 256 );
|
||
|
||
for ( ; count > 0; count--, point++ )
|
||
{
|
||
if ( !( point->flags & ah_flag_weak_interpolation ) )
|
||
{
|
||
NV_Transform transform, *trans = &transform;
|
||
NV_Vector vec;
|
||
|
||
vec.x = point->x - ah_debug_hinter->pp1.x;
|
||
vec.y = point->y;
|
||
nv_vector_transform( &vec, &size_transform );
|
||
|
||
nv_transform_set_translate( &transform, vec.x, vec.y );
|
||
nv_painter_fill_path( painter, trans, 0, symbol_circle );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
static void
|
||
ah_draw_edges( void )
|
||
{
|
||
if ( ah_debug_hinter )
|
||
{
|
||
AH_Outline* glyph = ah_debug_hinter->glyph;
|
||
FT_UInt count;
|
||
AH_Edge* edge;
|
||
FT_Pos pp1 = ah_debug_hinter->pp1.x;
|
||
|
||
nv_painter_set_color( painter, EDGE_COLOR, 256 );
|
||
|
||
if ( option_show_edges )
|
||
{
|
||
/* draw verticla edges */
|
||
if ( option_show_vert_hints )
|
||
{
|
||
count = glyph->num_vedges;
|
||
edge = glyph->vert_edges;
|
||
for ( ; count > 0; count--, edge++ )
|
||
{
|
||
NV_Vector vec;
|
||
NV_Pos x;
|
||
|
||
vec.x = edge->pos - pp1;
|
||
vec.y = 0;
|
||
|
||
nv_vector_transform( &vec, &size_transform );
|
||
x = (FT_Pos)( vec.x + 0.5 );
|
||
|
||
nv_pixmap_fill_rect( target, x, 0, 1, target->height, EDGE_COLOR );
|
||
}
|
||
}
|
||
|
||
/* draw horizontal edges */
|
||
if ( option_show_horz_hints )
|
||
{
|
||
count = glyph->num_hedges;
|
||
edge = glyph->horz_edges;
|
||
for ( ; count > 0; count--, edge++ )
|
||
{
|
||
NV_Vector vec;
|
||
NV_Pos x;
|
||
|
||
vec.x = 0;
|
||
vec.y = edge->pos;
|
||
|
||
nv_vector_transform( &vec, &size_transform );
|
||
x = (FT_Pos)( vec.y + 0.5 );
|
||
|
||
nv_pixmap_fill_rect( target, 0, x, target->width, 1, EDGE_COLOR );
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( option_show_segments )
|
||
{
|
||
/* draw vertical segments */
|
||
if ( option_show_vert_hints )
|
||
{
|
||
AH_Segment* seg = glyph->vert_segments;
|
||
FT_UInt count = glyph->num_vsegments;
|
||
|
||
for ( ; count > 0; count--, seg++ )
|
||
{
|
||
AH_Point *first, *last;
|
||
NV_Vector v1, v2;
|
||
NV_Pos y1, y2, x;
|
||
|
||
first = seg->first;
|
||
last = seg->last;
|
||
|
||
v1.x = v2.x = first->x - pp1;
|
||
|
||
if ( first->y <= last->y )
|
||
{
|
||
v1.y = first->y;
|
||
v2.y = last->y;
|
||
}
|
||
else
|
||
{
|
||
v1.y = last->y;
|
||
v2.y = first->y;
|
||
}
|
||
|
||
nv_vector_transform( &v1, &size_transform );
|
||
nv_vector_transform( &v2, &size_transform );
|
||
|
||
y1 = (NV_Pos)( v1.y + 0.5 );
|
||
y2 = (NV_Pos)( v2.y + 0.5 );
|
||
x = (NV_Pos)( v1.x + 0.5 );
|
||
|
||
nv_pixmap_fill_rect( target, x-1, y2, 3, ABS(y1-y2)+1, SEGMENT_COLOR );
|
||
}
|
||
}
|
||
|
||
/* draw horizontal segments */
|
||
if ( option_show_horz_hints )
|
||
{
|
||
AH_Segment* seg = glyph->horz_segments;
|
||
FT_UInt count = glyph->num_hsegments;
|
||
|
||
for ( ; count > 0; count--, seg++ )
|
||
{
|
||
AH_Point *first, *last;
|
||
NV_Vector v1, v2;
|
||
NV_Pos y1, y2, x;
|
||
|
||
first = seg->first;
|
||
last = seg->last;
|
||
|
||
v1.y = v2.y = first->y;
|
||
|
||
if ( first->x <= last->x )
|
||
{
|
||
v1.x = first->x - pp1;
|
||
v2.x = last->x - pp1;
|
||
}
|
||
else
|
||
{
|
||
v1.x = last->x - pp1;
|
||
v2.x = first->x - pp1;
|
||
}
|
||
|
||
nv_vector_transform( &v1, &size_transform );
|
||
nv_vector_transform( &v2, &size_transform );
|
||
|
||
y1 = (NV_Pos)( v1.x + 0.5 );
|
||
y2 = (NV_Pos)( v2.x + 0.5 );
|
||
x = (NV_Pos)( v1.y + 0.5 );
|
||
|
||
nv_pixmap_fill_rect( target, y1, x-1, ABS(y1-y2)+1, 3, SEGMENT_COLOR );
|
||
}
|
||
}
|
||
|
||
|
||
if ( option_show_vert_hints && option_show_links )
|
||
{
|
||
AH_Segment* seg = glyph->vert_segments;
|
||
FT_UInt count = glyph->num_vsegments;
|
||
|
||
for ( ; count > 0; count--, seg++ )
|
||
{
|
||
AH_Segment* seg2 = NULL;
|
||
NV_Path link;
|
||
NV_Vector v1, v2;
|
||
|
||
if ( seg->link )
|
||
{
|
||
if ( seg->link > seg )
|
||
seg2 = seg->link;
|
||
}
|
||
else if ( seg->serif )
|
||
seg2 = seg->serif;
|
||
|
||
if ( seg2 )
|
||
{
|
||
v1.x = seg->first->x - pp1;
|
||
v2.x = seg2->first->x - pp1;
|
||
v1.y = (seg->first->y + seg->last->y)/2;
|
||
v2.y = (seg2->first->y + seg2->last->y)/2;
|
||
|
||
link = ah_link_path( &v1, &v2, 1 );
|
||
|
||
nv_painter_set_color( painter, seg->serif ? SERIF_LINK_COLOR : LINK_COLOR, 256 );
|
||
nv_painter_fill_path( painter, &size_transform, 0, link );
|
||
|
||
nv_path_destroy( link );
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( option_show_horz_hints && option_show_links )
|
||
{
|
||
AH_Segment* seg = glyph->horz_segments;
|
||
FT_UInt count = glyph->num_hsegments;
|
||
|
||
for ( ; count > 0; count--, seg++ )
|
||
{
|
||
AH_Segment* seg2 = NULL;
|
||
NV_Path link;
|
||
NV_Vector v1, v2;
|
||
|
||
if ( seg->link )
|
||
{
|
||
if ( seg->link > seg )
|
||
seg2 = seg->link;
|
||
}
|
||
else if ( seg->serif )
|
||
seg2 = seg->serif;
|
||
|
||
if ( seg2 )
|
||
{
|
||
v1.y = seg->first->y;
|
||
v2.y = seg2->first->y;
|
||
v1.x = (seg->first->x + seg->last->x)/2 - pp1;
|
||
v2.x = (seg2->first->x + seg2->last->x)/2 - pp1;
|
||
|
||
link = ah_link_path( &v1, &v2, 0 );
|
||
|
||
nv_painter_set_color( painter, seg->serif ? SERIF_LINK_COLOR : LINK_COLOR, 256 );
|
||
nv_painter_fill_path( painter, &size_transform, 0, link );
|
||
|
||
nv_path_destroy( link );
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
/***** *****/
|
||
/***** MAIN LOOP(S) *****/
|
||
/***** *****/
|
||
/************************************************************************/
|
||
/************************************************************************/
|
||
|
||
static void
|
||
draw_glyph( int glyph_index )
|
||
{
|
||
NV_Path path;
|
||
|
||
pshint_vertical = -1;
|
||
|
||
ps1_debug_hint_func = option_show_ps_hints ? draw_ps1_hint : 0;
|
||
ps2_debug_hint_func = option_show_ps_hints ? draw_ps2_hint : 0;
|
||
|
||
ah_debug_hinter = NULL;
|
||
|
||
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<61> 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<61> 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_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, symbol_dot );
|
||
|
||
if ( option_show_indices )
|
||
{
|
||
char temp[5];
|
||
|
||
sprintf( temp, "%d", m );
|
||
nv_pixmap_cell_text( target, vec->x/64 + 4, vec->y/64 - 4,
|
||
temp, TEXT_COLOR );
|
||
}
|
||
}
|
||
|
||
first = last + 1;
|
||
}
|
||
}
|
||
|
||
ah_draw_smooth_points();
|
||
ah_draw_edges();
|
||
|
||
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;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
|
||
#define TOGGLE_OPTION(var,prefix) \
|
||
{ \
|
||
var = !var; \
|
||
sprintf( temp_message, prefix " is now %s", \
|
||
var ? "on" : "off" ); \
|
||
break; \
|
||
}
|
||
|
||
|
||
#define TOGGLE_OPTION_NEG(var,prefix) \
|
||
{ \
|
||
var = !var; \
|
||
sprintf( temp_message, prefix " is now %s", \
|
||
!var ? "on" : "off" ); \
|
||
break; \
|
||
}
|
||
|
||
|
||
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'):
|
||
TOGGLE_OPTION( option_show_axis, "grid axis display" )
|
||
|
||
case NVV_KEY('s'):
|
||
TOGGLE_OPTION( option_show_stroke, "glyph stroke display" )
|
||
|
||
case NVV_KEY('g'):
|
||
TOGGLE_OPTION( option_show_glyph, "glyph fill display" )
|
||
|
||
case NVV_KEY('d'):
|
||
TOGGLE_OPTION( option_show_dots, "control points display" )
|
||
|
||
case NVV_KEY('e'):
|
||
TOGGLE_OPTION( option_show_em, "EM square display" )
|
||
|
||
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'):
|
||
TOGGLE_OPTION_NEG( ps_debug_no_vert_hints, "vertical hints processing" )
|
||
|
||
case NVV_KEY('a'):
|
||
TOGGLE_OPTION_NEG( ps_debug_no_horz_hints, "horizontal hints processing" )
|
||
|
||
case NVV_KEY('Z'):
|
||
TOGGLE_OPTION( option_show_vert_hints, "vertical hints display" )
|
||
|
||
case NVV_KEY('A'):
|
||
TOGGLE_OPTION( option_show_horz_hints, "horizontal hints display" )
|
||
|
||
case NVV_KEY('S'):
|
||
TOGGLE_OPTION( option_show_smooth, "smooth points display" );
|
||
|
||
case NVV_KEY('i'):
|
||
TOGGLE_OPTION( option_show_indices, "point index display" );
|
||
|
||
case NVV_KEY('b'):
|
||
TOGGLE_OPTION( option_show_blues, "blue zones display" );
|
||
|
||
case NVV_KEY('h'):
|
||
TOGGLE_OPTION( option_hinting, "hinting" )
|
||
|
||
case NVV_KEY('H'):
|
||
ps_print_hints();
|
||
break;
|
||
|
||
default:
|
||
;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
static void
|
||
usage()
|
||
{
|
||
Panic( "no usage" );
|
||
}
|
||
|
||
|
||
#define OPTION1(n,code) \
|
||
case n : \
|
||
code \
|
||
argc--; \
|
||
argv++; \
|
||
break;
|
||
|
||
#define OPTION2(n,code) \
|
||
case n : \
|
||
code \
|
||
argc -= 2; \
|
||
argv += 2; \
|
||
break;
|
||
|
||
|
||
static void
|
||
parse_options( int* argc_p, char*** argv_p )
|
||
{
|
||
int argc = *argc_p;
|
||
char** argv = *argv_p;
|
||
|
||
while (argc > 2 && argv[1][0] == '-')
|
||
{
|
||
switch (argv[1][1])
|
||
{
|
||
OPTION2( 'f', first_glyph = atoi( argv[2] ); )
|
||
|
||
OPTION2( 's', pixel_size = atoi( argv[2] ); )
|
||
|
||
default:
|
||
usage();
|
||
}
|
||
}
|
||
|
||
*argc_p = argc;
|
||
*argv_p = argv;
|
||
}
|
||
|
||
|
||
|
||
int main( int argc, char** argv )
|
||
{
|
||
char* filename = "/winnt/fonts/arial.ttf";
|
||
|
||
parse_options( &argc, &argv );
|
||
|
||
if ( argc >= 2 )
|
||
filename = argv[1];
|
||
|
||
|
||
/* create library */
|
||
error = nv_renderer_new( 0, &renderer );
|
||
if (error) Panic( "could not create Nirvana renderer" );
|
||
|
||
memory = nv_renderer_get_memory( renderer );
|
||
init_symbols();
|
||
|
||
error = nvv_display_new( renderer, &display );
|
||
if (error) Panic( "could not create display" );
|
||
|
||
error = nvv_surface_new( display, 460, 460, 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, filename, 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 = first_glyph;
|
||
for ( ;; )
|
||
{
|
||
clear_background();
|
||
draw_grid();
|
||
|
||
ps_debug_hints = 0;
|
||
ah_debug_hinter = 0;
|
||
|
||
ah_debug_disable_vert = ps_debug_no_vert_hints;
|
||
ah_debug_disable_horz = ps_debug_no_horz_hints;
|
||
|
||
draw_ps_blue_zones();
|
||
draw_glyph( glyph_index );
|
||
ps2_draw_control_points();
|
||
|
||
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 );
|
||
|
||
done_symbols();
|
||
nv_renderer_unref( renderer );
|
||
|
||
return 0;
|
||
}
|