root/trunk/orca/gl/ttf_load.c

Revision 108, 16.4 kB (checked in by krobillard, 3 years ago)

Orca-GL should now build on Windows.

Line 
1/*============================================================================
2//
3// Load FontResource using Freetype 2.
4//
5//==========================================================================*/
6
7
8/*
9  TODO:
10   * Automatically compute best image size.
11   * Pack glyphs more efficiently.
12   * Speed up txf_kerning().
13*/
14
15
16#include <assert.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <ft2build.h>
21#include FT_FREETYPE_H
22#include "os.h"
23#include "gx.h"
24#include "res_font.h"
25
26
27#define KERN      1
28//#define DUMP      1
29
30#define FT_PIXELS(x)  (x >> 6)
31
32
33//----------------------------------------------------------------------------
34
35
36void txf_init( TexFont* txf, int w, int h )
37{
38    txf->tex_width  = w;
39    txf->tex_height = h;
40    txf->tgi        = 0;
41    txf->table      = 0;
42    txf->kerning    = 0;
43}
44
45
46void txf_free( TexFont* txf )
47{
48#define FREE(n) if(n) { memFree(n); n=0; }
49
50    FREE( txf->tgi )
51    FREE( txf->table )
52    FREE( txf->kerning )
53}
54
55
56void txf_setGlyphCount( TexFont* txf, int n )
57{
58    if( txf->tgi )
59        memFree( txf->tgi );
60    txf->tgi   = (TexGlyphInfo*) memAlloc( sizeof(TexGlyphInfo) * n );
61    txf->num_glyphs = n;
62}
63
64
65/**
66  Build lookup table.
67*/
68void txf_buildTable( TexFont* txf )
69{
70    int min_glyph;
71    int max_glyph;
72    TexGlyphInfo* it;
73    TexGlyphInfo* end;
74
75    assert( txf->num_glyphs );
76
77    // Find high & low glyph.
78    min_glyph = txf->tgi[0].c;
79    max_glyph = txf->tgi[0].c;
80
81    it  = txf->tgi;
82    end = it + txf->num_glyphs;
83
84    while( it != end )
85    {
86        if( it->c < min_glyph )
87            min_glyph = it->c;
88        if( it->c > max_glyph )
89            max_glyph = it->c;
90        ++it;
91    }
92
93    txf->min_glyph  = min_glyph;
94    txf->table_size = max_glyph - min_glyph + 1;
95
96    // Allocate table.
97    if( txf->table )
98        memFree( txf->table );
99    txf->table = (TexGlyphInfo**)
100                 memAlloc( sizeof(TexGlyphInfo*) * txf->table_size );
101
102    // Fill table.
103    memSet( txf->table, 0, sizeof(TexGlyphInfo*) * txf->table_size );
104
105    it = txf->tgi;
106    while( it != end )
107    {
108        txf->table[ it->c - txf->min_glyph ] = it;
109        ++it;
110    }
111}
112
113
114/**
115  Returns the TexGlyphInfo for a glyph or zero if it is not available.
116  Automatically substitutes uppercase letters with lowercase if
117  uppercase not available (and vice versa).
118*/
119TexGlyphInfo* txf_glyph( const TexFont* txf, int c )
120{
121    TexGlyphInfo* tgi;
122    int max_glyph = txf->min_glyph + txf->table_size;
123
124    if( (c >= txf->min_glyph) && (c < max_glyph) )
125    {
126        tgi = txf->table[c - txf->min_glyph];
127        if( tgi )
128            return tgi;
129
130        if( (c >= 'a') && (c <= 'z') )
131        {
132            c -= 'a' - 'A';     // toupper
133
134            if( (c >= txf->min_glyph) && (c < max_glyph) )
135            {
136                return txf->table[c - txf->min_glyph];
137            }
138        }
139        else if( (c >= 'A') && (c <= 'Z') )
140        {
141            c += 'a' - 'A';     // tolower
142
143            if( (c >= txf->min_glyph) && (c < max_glyph) )
144            {
145                return txf->table[c - txf->min_glyph];
146            }
147        }
148    }
149
150    return 0;
151}
152
153
154int txf_kerning( const TexFont* txf, const TexGlyphInfo* left,
155                                     const TexGlyphInfo* right )
156{
157#ifdef KERN
158    unsigned char* table;
159    if( left->kern_index > -1 )
160    {
161        table = txf->kerning + left->kern_index;
162        while( *table )
163        {
164            /*
165            // If table sorted...
166            if( right->c < *table )
167                break;
168            */
169            if( right->c == *table )
170                return ((char*) table)[1];
171            ++table;
172        }
173    }
174#else
175    (void) txf;
176    (void) left;
177    (void) right;
178#endif
179    return 0;
180}
181
182
183//----------------------------------------------------------------------------
184
185
186#if DUMP
187void dumpCharMaps( FT_Face face )
188{
189    FT_CharMap charmap;
190    int n;
191
192    printf( "  CharMaps %d [\n", face->num_charmaps );
193    for( n = 0; n < face->num_charmaps; n++ )
194    {
195        long enc;
196        charmap = face->charmaps[ n ];
197        enc = charmap->encoding;
198        printf( "    %lx (%c%c%c%c)\n", enc,
199                 (char) ((enc >> 24) & 0xff),
200                 (char) ((enc >> 16) & 0xff),
201                 (char) ((enc >> 8) & 0xff),
202                 (char) (enc & 0xff) );
203    }
204    printf( "  ]\n" );
205}
206#endif
207
208
209void blitGlyphToBitmap( FT_Bitmap* src, FT_Bitmap* dst, int x, int y )
210{
211    unsigned char* s;
212    unsigned char* d;
213    unsigned char* dend;
214    int r;
215
216    s = src->buffer;
217    d = dst->buffer + (y * dst->pitch) + x;
218    dend = dst->buffer + (dst->rows * dst->pitch);
219
220    r = src->rows;
221    while( r && (d < dend) )
222    {
223        memcpy( d, s, src->width );
224        s += src->pitch;
225        d += dst->pitch;
226        r--;
227    }
228}
229
230
231static FT_Error renderGlyph( FT_Bitmap* img, FT_GlyphSlot glyph,
232                             int x_offset, int y_offset, int antialias )
233{
234    /* first, render the glyph into an intermediate buffer */
235    if( glyph->format != ft_glyph_format_bitmap )
236    {
237        FT_Error error = FT_Render_Glyph( glyph,
238                antialias ? ft_render_mode_normal : ft_render_mode_mono );
239        if( error )
240            return error;
241    }
242 
243#if 0
244    printf( "\nKR target offset %dx%d\n", x_offset, y_offset );
245    printf( "KR bitmap left/top %d %d\n", glyph->bitmap_left, glyph->bitmap_top );
246    printf( "KR metrics %ldx%ld %ldx%ld %ld\n",
247            FT_PIXELS(glyph->metrics.width),
248            FT_PIXELS(glyph->metrics.height),
249            FT_PIXELS(glyph->metrics.horiBearingX),
250            FT_PIXELS(glyph->metrics.horiBearingY),
251            FT_PIXELS(glyph->metrics.horiAdvance) );
252#endif
253 
254    // Then, blit the image to the target surface
255    // Ajusting by bitmap_top to pretty up the vertical alignment.
256    blitGlyphToBitmap( &glyph->bitmap, img,
257                       x_offset /*+ glyph->bitmap_left*/,
258                       y_offset - glyph->bitmap_top );
259
260    return 0;
261}
262
263
264#ifdef KERN
265/*
266   Returns index into table or -1 if there is no kerning for left_glyph.
267*/
268static int loadKerning( OArray* table, FT_Face face, FT_UInt left_glyph,
269                        const char* codes )
270{
271    FT_Vector kern;
272    FT_UInt right_glyph;
273    const char* ci;
274    char* entry;
275    int start = table->used;
276
277    ci = codes;
278    for( ci = codes; *ci != '\0'; ++ci )
279    {
280        right_glyph = FT_Get_Char_Index( face, *ci );
281        if( right_glyph == 0 )
282            continue;
283
284        FT_Get_Kerning( face, left_glyph, right_glyph,
285                        FT_KERNING_DEFAULT,
286                        //FT_KERNING_UNFITTED, FT_KERNING_UNSCALED
287                        &kern );
288        if( kern.x || kern.y )
289        {
290          //printf(" %c %ld %ld\n", *ci, FT_PIXELS(kern.x), FT_PIXELS(kern.y));
291
292            orArrayReserve( table, 1, table->used + 3 );
293            entry = table->charArray + table->used;
294            table->used += 2;
295
296            // NOTE: kern.y is ignored since is it almost always zero.
297            entry[0] = *ci;
298            entry[1] = FT_PIXELS(kern.x);
299        }
300    }
301
302    if( table->used > start )
303    {
304        // Terminate entries.
305        table->charArray[ table->used ] = 0;
306        ++table->used;
307
308        return start;
309    }
310
311    return -1;
312}
313#endif
314
315
316/**
317  Returns number of glyphs added or zero if fails.
318*/
319int txf_build( TexFont* txf, const char* file,
320               const char* codes, FT_Bitmap* img, int psize, int gap,
321               int asBitmap )
322{
323    FT_Library library;
324    FT_Face face;
325    FT_Error error;
326    FT_GlyphSlot glyph;
327    FT_Size size;
328    FT_F26Dot6 start_x;
329    FT_F26Dot6 step_y;
330    FT_F26Dot6 x, y;
331#ifdef KERN
332    OArray kern;
333#endif
334    const char* ci;
335    int count = 0;
336    int fail = 0;
337
338
339    error = FT_Init_FreeType( &library );
340    if( error )
341    {
342        return 0;
343    }
344
345    error = FT_New_Face( library, file, 0, &face );
346    if( error )
347    {
348        return 0;
349    }
350
351
352#if DUMP
353#define EM_PIX(n)   n,FT_PIXELS(n)
354        printf( "FT_Face [\n" );
355        printf( "  family_name:  \"%s\"\n", face->family_name );
356        printf( "  style_name:   \"%s\"\n", face->style_name );
357        printf( "  num_glyphs:   %ld\n",    face->num_glyphs );
358        printf( "  units_per_EM: %d (%d)\n", EM_PIX(face->units_per_EM) );
359        printf( "  ascender:     %d (%d)\n", EM_PIX(face->ascender) );
360        printf( "  descender:    %d (%d)\n", EM_PIX(face->descender) );
361        printf( "  height:       %d (%d)\n", EM_PIX(face->height) );
362        dumpCharMaps( face );
363        printf( "]\n" );
364#endif
365
366
367    error = FT_Set_Pixel_Sizes( face, psize, psize );
368    if( error )
369    {
370        return 0;
371    }
372
373#ifdef KERN
374    orArrayInit( &kern, 1, 0 );
375#endif
376
377    glyph = face->glyph;
378    size  = face->size;
379
380
381    txf_setGlyphCount( txf, face->num_glyphs );
382    //txf.max_ascent  = size->metrics.y_ppem;
383    txf->max_ascent  =  face->ascender  * psize / face->units_per_EM;
384    txf->max_descent = -face->descender * psize / face->units_per_EM;
385
386
387    /* Clear bitmap */
388    memSet( img->buffer, 0, img->rows * img->pitch );
389
390
391    step_y = size->metrics.y_ppem + gap;
392
393    start_x = gap;
394    x = start_x;
395    y = step_y;
396
397    ci = codes;
398    for( ci = codes; *ci != '\0'; ++ci )
399    {
400        int glyph_index = FT_Get_Char_Index( face, *ci );
401        if( glyph_index == 0 )
402        {
403            printf( "Code 0x%x (%c) is undefined\n", (int) *ci, (char) *ci );
404            continue;
405        }
406
407        error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );
408        if( ! error )
409        {
410            TexGlyphInfo* tgi;
411            int nextX = x + FT_PIXELS(glyph->metrics.width) + gap;
412
413            if( nextX > img->width )
414            {
415                x  = start_x;
416                y += step_y;
417
418                //if( (y + step_y) >= img->rows )
419                if( y >= img->rows )
420                {
421                    fprintf(stderr, "** Texture too small\n");
422                    break;
423                }
424
425                nextX = x + FT_PIXELS(glyph->metrics.width) + gap;
426            }
427
428            renderGlyph( img, glyph, x, y, ! asBitmap );
429
430            tgi = txf->tgi + count;
431            count++;
432
433            tgi->c       = *ci;
434            tgi->width   = FT_PIXELS(glyph->metrics.width);
435            tgi->height  = FT_PIXELS(glyph->metrics.height);
436            tgi->xoffset = FT_PIXELS(glyph->metrics.horiBearingX);
437            tgi->yoffset = FT_PIXELS(glyph->metrics.horiBearingY) - tgi->height;
438            tgi->advance = FT_PIXELS(glyph->metrics.horiAdvance);
439            tgi->x       = x /*+ tgi->xoffset*/;
440            tgi->y       = img->rows - y + tgi->yoffset;
441            tgi->kern_index = -1;
442
443#ifdef KERN
444            if( FT_HAS_KERNING( face ) )
445            {
446                //printf( "KR Kern %c\n", *ci );
447                tgi->kern_index = loadKerning(&kern, face, glyph_index, codes);
448            }
449#endif
450
451            x = nextX;
452        }
453        else
454        {
455            fail++;
456        }
457    }
458
459    if( fail )
460        printf( "Failed to load %d glyphs.\n", fail );
461
462#ifdef KERN
463    if( kern.used )
464    {
465        if( txf->kerning )
466            memFree( txf->kerning );
467        txf->kerning = memAlloc( kern.used );
468        memCpy( txf->kerning, kern.charArray, kern.used );
469    }
470    orArrayFree( &kern );
471#endif
472
473    FT_Done_Face( face );
474    FT_Done_FreeType( library );
475
476    return count;
477}
478
479
480//----------------------------------------------------------------------------
481
482
483static GLuint buildFontTexture( OImage* img )
484{
485    GLuint id;
486    GLenum format;
487    GLint  comp;
488
489    switch( img->format )
490    {
491        case OR_IMG_GRAY:
492            //format = GL_LUMINANCE;
493            //comp   = 1;
494            format = GL_LUMINANCE_ALPHA;
495            comp   = GL_LUMINANCE_ALPHA;
496            break;
497
498        case OR_IMG_RGB:
499            format = GL_RGB;
500            comp   = 3;
501            break;
502
503        case OR_IMG_RGBA:
504            format = GL_RGBA;
505            comp   = 4;
506            break;
507
508        default:
509            return 0;
510    }
511
512    glGenTextures( 1, &id );
513    glBindTexture( GL_TEXTURE_2D, id );
514
515    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
516    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
517
518    if( format == GL_LUMINANCE_ALPHA )
519    {
520        int i;
521        int tsize = img->width * img->height;
522        GLubyte* buf = (GLubyte*) memAlloc( 2 * tsize );
523        GLubyte* src = (GLubyte*) (img + 1);
524        GLubyte* dst = buf;
525
526        for( i = 0; i < tsize; i++ )
527        {
528            *dst++ = *src;
529            *dst++ = *src++;
530        }
531
532        glTexImage2D( GL_TEXTURE_2D, 0, comp, img->width, img->height, 0,
533                      format, GL_UNSIGNED_BYTE, buf );
534
535        memFree( buf );
536    }
537    else
538    {
539        glTexImage2D( GL_TEXTURE_2D, 0, comp, img->width, img->height, 0,
540                      format, GL_UNSIGNED_BYTE, img + 1 );
541    }
542
543
544    glTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
545
546    return id;
547}
548
549
550FontResource* createFont()
551{
552    FontResource* fr = memAlloc( sizeof(FontResource) );
553    if( fr )
554    {
555        fr->res.resType = RES_TYPE_FONT;
556        //fr->name    = 0;
557        fr->imgHold = OR_INVALID_HOLD;
558        fr->glTexId = 0;
559        txf_init( &fr->tfont, 256, 256 );
560    }
561    return fr;
562}
563
564
565void destroyFont( Resource* res )
566{
567    FontResource* fr = (FontResource*) res;
568    if( res )
569    {
570        //if( fr->name )
571        //    memFree( fr->name );
572
573        if( orHoldIsValid(fr->imgHold) )
574            orRelease( fr->imgHold );
575
576        if( fr->glTexId )
577            glDeleteTextures( 1, &fr->glTexId );
578
579        txf_free( &fr->tfont );
580
581        memFree( res );
582    }
583}
584
585
586#if 0
587static char* intToStr00( char* cp, int n )
588{
589    int d;
590    if( n > 99 )
591    {
592        d = n / 100;
593        *cp++ = d + '0';
594        n -= d * 100;
595    }
596    if( n > 9 )
597    {
598        d = n / 10;
599        *cp++ = d + '0';
600        n -= d * 10;
601    }
602    *cp++ = n + '0';
603    return cp;
604}
605#endif
606
607
608static char _default_codes[] = " ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz?.;,!*:\"/+-|'@#$%^&<>()[]{}_";
609
610
611#define REF_GLYPHS  a1 + 3
612#define REF_CODES   a1 + 4
613#define REF_DIM     a1 + 5
614#define REF_SIZE    a1 + 6
615
616/*
617  make-texfont: native [
618      name    [string! issue!]
619      file    [file!]
620      pntsize [integer!]
621      /glyphs codes [string!]
622      /dim    size  [integer! pair!]
623  ]
624*/
625OR_NATIVE_PUB( makeTexfontNative )
626{
627    OValue* arg;
628    OString* str;
629    OBinary* bin;
630    FT_Bitmap fbm;
631    TexFont* tf;
632    FontResource* res;
633    int psize;
634    const char* codes = _default_codes;
635
636
637    res = createFont();
638    tf  = &res->tfont;
639
640    psize = orInt(a1 + 2);
641    if( psize < 1 )
642        psize = 20;
643    else if( psize > 100 )
644        psize = 100;
645
646    arg = REF_GLYPHS;
647    if( orRefineSet(arg) )
648    {
649        ++arg;
650        str = orSTRING(arg);
651        orTermCStr( str );
652        codes = orStrChars( str, arg );
653    }
654
655    arg = REF_DIM;
656    if( orRefineSet(arg) )
657    {
658        ++arg;
659        if( arg->type == OT_INTEGER )
660        {
661            tf->tex_width  = orInt(arg);
662            tf->tex_height = orInt(arg);
663        }
664        else
665        {
666            tf->tex_width  = arg->pair[0];
667            tf->tex_height = arg->pair[1];
668        }
669    }
670
671    bin = orMakeImage( OR_IMG_GRAY, tf->tex_width, tf->tex_height );
672    if( bin->buf )
673    {
674        OValue* a2 = a1 + 1;
675        char* ffile;
676
677        res->imgHold = orHold( OT_BINARY, orBinaryN(bin) );
678
679        str = orSTRING(a2);
680        orTermCStr( str );
681        ffile = orStrChars( str, a2 );
682
683        fbm.width  = tf->tex_width;
684        fbm.rows   = tf->tex_height;
685        fbm.pitch  = fbm.width;
686        fbm.buffer = (unsigned char*) orImagePixels(bin);
687
688        tf->num_glyphs = txf_build( tf, ffile, codes, &fbm, psize, 1, 0 );
689        if( tf->num_glyphs )
690        {
691            txf_buildTable( &res->tfont );
692
693            res->glTexId = buildFontTexture( (OImage*) bin->buf );
694#if 0
695            {
696            char* cp;
697            int len;
698            uint32_t key;
699            len = (str->charArray + str->used) - ffile;
700            res->name = cp = memAlloc( len + 6 );
701            memCpy( res->name, ffile, len );
702            cp += len;
703            *cp++ = '.';
704            cp = intToStr00( cp, psize );
705            *cp = '\0';
706
707            key = resd_hash( res->name, cp - res->name );
708            resd_add( &gEnv.resd, &res->res, key );
709            }
710#else
711            resd_add( &gEnv.resd, &res->res, gxHashString( a1 ) );
712#endif
713
714            gxResultRES( res )
715            return;
716        }
717        else
718        {
719            orError( "No glyphs found in font" );
720        }
721    }
722
723    destroyFont( &res->res );
724}
725
726
727/*EOF*/
Note: See TracBrowser for help on using the browser.