tarina

git clone https://git.tarina.org/tarina
Log | Files | Refs | README | LICENSE

vgft.c (13046B)


      1 /*
      2 Copyright (c) 2012, Broadcom Europe Ltd
      3 All rights reserved.
      4 
      5 Redistribution and use in source and binary forms, with or without
      6 modification, are permitted provided that the following conditions are met:
      7     * Redistributions of source code must retain the above copyright
      8       notice, this list of conditions and the following disclaimer.
      9     * Redistributions in binary form must reproduce the above copyright
     10       notice, this list of conditions and the following disclaimer in the
     11       documentation and/or other materials provided with the distribution.
     12     * Neither the name of the copyright holder nor the
     13       names of its contributors may be used to endorse or promote products
     14       derived from this software without specific prior written permission.
     15 
     16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
     17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY
     20 DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
     25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26 */
     27 
     28 // Font handling for graphicsx
     29 
     30 #include <assert.h>
     31 #include <stdlib.h>
     32 
     33 #include "graphics_x_private.h"
     34 #include "vgft.h"
     35 
     36 static FT_Library lib;
     37 
     38 int vgft_init(void)
     39 {
     40    if (FT_Init_FreeType(&lib) == 0)
     41       return 0;
     42    else
     43    {
     44       return -1;
     45    }
     46 }
     47 
     48 void vgft_term(void)
     49 {
     50    FT_Done_FreeType(lib);
     51 }
     52 
     53 #define SEGMENTS_COUNT_MAX 256
     54 #define COORDS_COUNT_MAX 1024
     55 
     56 static VGuint segments_count;
     57 static VGubyte segments[SEGMENTS_COUNT_MAX];
     58 static VGuint coords_count;
     59 static VGfloat coords[COORDS_COUNT_MAX];
     60 
     61 static VGfloat float_from_26_6(FT_Pos x)
     62 {
     63    return (VGfloat)x / 64.0f;
     64 }
     65 
     66 static void convert_contour(const FT_Vector *points, const char *tags, short points_count)
     67 {
     68    int first_coords = coords_count;
     69 
     70    int first = 1;
     71    char last_tag = 0;
     72    int c = 0;
     73 
     74    for (; points_count != 0; ++points, ++tags, --points_count) {
     75       ++c;
     76 
     77       char tag = *tags;
     78       if (first) {
     79          assert(tag & 0x1);
     80          assert(c==1); c=0;
     81          segments[segments_count++] = VG_MOVE_TO;
     82          first = 0;
     83       } else if (tag & 0x1) {
     84          /* on curve */
     85 
     86          if (last_tag & 0x1) {
     87             /* last point was also on -- line */
     88             assert(c==1); c=0;
     89             segments[segments_count++] = VG_LINE_TO;
     90          } else {
     91             /* last point was off -- quad or cubic */
     92             if (last_tag & 0x2) {
     93                /* cubic */
     94                assert(c==3); c=0;
     95                segments[segments_count++] = VG_CUBIC_TO;
     96             } else {
     97                /* quad */
     98                assert(c==2); c=0;
     99                segments[segments_count++] = VG_QUAD_TO;
    100             }
    101          }
    102       } else {
    103          /* off curve */
    104 
    105          if (tag & 0x2) {
    106             /* cubic */
    107 
    108             assert((last_tag & 0x1) || (last_tag & 0x2)); /* last either on or off and cubic */
    109          } else {
    110             /* quad */
    111 
    112             if (!(last_tag & 0x1)) {
    113                /* last was also off curve */
    114 
    115                assert(!(last_tag & 0x2)); /* must be quad */
    116 
    117                /* add on point half-way between */
    118                assert(c==2); c=1;
    119                segments[segments_count++] = VG_QUAD_TO;
    120                VGfloat x = (coords[coords_count - 2] + float_from_26_6(points->x)) * 0.5f;
    121                VGfloat y = (coords[coords_count - 1] + float_from_26_6(points->y)) * 0.5f;
    122                coords[coords_count++] = x;
    123                coords[coords_count++] = y;
    124             }
    125          }
    126       }
    127       last_tag = tag;
    128 
    129       coords[coords_count++] = float_from_26_6(points->x);
    130       coords[coords_count++] = float_from_26_6(points->y);
    131    }
    132 
    133    if (last_tag & 0x1) {
    134       /* last point was also on -- line (implicit with close path) */
    135       assert(c==0);
    136    } else {
    137       ++c;
    138 
    139       /* last point was off -- quad or cubic */
    140       if (last_tag & 0x2) {
    141          /* cubic */
    142          assert(c==3); c=0;
    143          segments[segments_count++] = VG_CUBIC_TO;
    144       } else {
    145          /* quad */
    146          assert(c==2); c=0;
    147          segments[segments_count++] = VG_QUAD_TO;
    148       }
    149 
    150       coords[coords_count++] = coords[first_coords + 0];
    151       coords[coords_count++] = coords[first_coords + 1];
    152    }
    153 
    154    segments[segments_count++] = VG_CLOSE_PATH;
    155 }
    156 
    157 static void convert_outline(const FT_Vector *points, const char *tags, const short *contours, short contours_count, short points_count)
    158 {
    159    segments_count = 0;
    160    coords_count = 0;
    161 
    162    short last_contour = 0;
    163    for (; contours_count != 0; ++contours, --contours_count) {
    164       short contour = *contours + 1;
    165       convert_contour(points + last_contour, tags + last_contour, contour - last_contour);
    166       last_contour = contour;
    167    }
    168    assert(last_contour == points_count);
    169 
    170    assert(segments_count <= SEGMENTS_COUNT_MAX); /* oops... we overwrote some memory */
    171    assert(coords_count <= COORDS_COUNT_MAX);
    172 }
    173 
    174 VCOS_STATUS_T vgft_font_init(VGFT_FONT_T *font)
    175 {
    176    font->ft_face = NULL;
    177    font->vg_font = vgCreateFont(0);
    178    if (font->vg_font == VG_INVALID_HANDLE)
    179    {
    180       return VCOS_ENOMEM;
    181    }
    182    return VCOS_SUCCESS;
    183 }
    184 
    185 VCOS_STATUS_T vgft_font_load_mem(VGFT_FONT_T *font, void *mem, size_t len)
    186 {
    187    if (FT_New_Memory_Face(lib, mem, len, 0, &font->ft_face))
    188    {
    189       return VCOS_EINVAL;
    190    }
    191    return VCOS_SUCCESS;
    192 }
    193 
    194 VCOS_STATUS_T vgft_font_load_file(VGFT_FONT_T *font, const char *file)
    195 {
    196    if (FT_New_Face(lib, file, 0, &font->ft_face)) {
    197       return VCOS_EINVAL;
    198    }
    199    return VCOS_SUCCESS;
    200 }
    201 
    202 VCOS_STATUS_T vgft_font_convert_glyphs(VGFT_FONT_T *font, unsigned int char_height, unsigned int dpi_x, unsigned int dpi_y)
    203 {
    204    FT_UInt glyph_index;
    205    FT_ULong ch;
    206 
    207    if (FT_Set_Char_Size(font->ft_face, 0, char_height, dpi_x, dpi_y))
    208    {
    209       FT_Done_Face(font->ft_face);
    210       vgDestroyFont(font->vg_font);
    211       return VCOS_EINVAL;
    212    }
    213 
    214    ch = FT_Get_First_Char(font->ft_face, &glyph_index);
    215 
    216    while (ch != 0)
    217    {
    218       if (FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT)) {
    219          FT_Done_Face(font->ft_face);
    220          vgDestroyFont(font->vg_font);
    221          return VCOS_ENOMEM;
    222       }
    223 
    224       VGPath vg_path;
    225       FT_Outline *outline = &font->ft_face->glyph->outline;
    226       if (outline->n_contours != 0) {
    227          vg_path = vgCreatePath(VG_PATH_FORMAT_STANDARD, VG_PATH_DATATYPE_F, 1.0f, 0.0f, 0, 0, VG_PATH_CAPABILITY_ALL);
    228          assert(vg_path != VG_INVALID_HANDLE);
    229 
    230          convert_outline(outline->points, outline->tags, outline->contours, outline->n_contours, outline->n_points);
    231          vgAppendPathData(vg_path, segments_count, segments, coords);
    232       } else {
    233          vg_path = VG_INVALID_HANDLE;
    234       }
    235 
    236       VGfloat origin[] = { 0.0f, 0.0f };
    237       VGfloat escapement[] = { float_from_26_6(font->ft_face->glyph->advance.x), float_from_26_6(font->ft_face->glyph->advance.y) };
    238       vgSetGlyphToPath(font->vg_font, glyph_index, vg_path, VG_FALSE, origin, escapement);
    239 
    240       if (vg_path != VG_INVALID_HANDLE) {
    241          vgDestroyPath(vg_path);
    242       }
    243       ch = FT_Get_Next_Char(font->ft_face, ch, &glyph_index);
    244    }
    245 
    246    return VCOS_SUCCESS;
    247 }
    248 
    249 void vgft_font_term(VGFT_FONT_T *font)
    250 {
    251    if (font->ft_face)
    252       FT_Done_Face(font->ft_face);
    253    if (font->vg_font)
    254       vgDestroyFont(font->vg_font);
    255    memset(font, 0, sizeof(*font));
    256 }
    257 
    258 
    259 #define CHAR_COUNT_MAX 200
    260 static VGuint glyph_indices[CHAR_COUNT_MAX];
    261 static VGfloat adjustments_x[CHAR_COUNT_MAX];
    262 static VGfloat adjustments_y[CHAR_COUNT_MAX];
    263 
    264 // Draws the first char_count characters from text, with adjustments, starting 
    265 // from the current origin.  The peek argument indicates whether to peek ahead 
    266 // and get a final adjustment based on the next character past char_count, or 
    267 // else just assume that this is the end of the text and add no final 
    268 // adjustment.
    269 
    270 static void draw_chars(VGFT_FONT_T *font, const char *text, int char_count, VGbitfield paint_modes, int peek) {
    271    // Put in first character
    272    glyph_indices[0] = FT_Get_Char_Index(font->ft_face, text[0]);
    273    int prev_glyph_index = glyph_indices[0];
    274 
    275    // Calculate glyph_indices and adjustments
    276    int i;
    277    FT_Vector kern;
    278    for (i = 1; i != char_count; ++i) {
    279       int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]);
    280       if (!glyph_index) { return; }
    281       glyph_indices[i] = glyph_index;
    282 
    283       if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0);
    284       adjustments_x[i - 1] = float_from_26_6(kern.x);
    285       adjustments_y[i - 1] = float_from_26_6(kern.y);
    286 
    287       prev_glyph_index = glyph_index;
    288    }
    289 
    290    // Get the last adjustment?
    291    if (peek) {
    292       int peek_glyph_index = FT_Get_Char_Index(font->ft_face, text[i]);
    293       if (!peek_glyph_index) { return; }
    294       if (FT_Get_Kerning(font->ft_face, prev_glyph_index, peek_glyph_index, FT_KERNING_DEFAULT, &kern)) assert(0);
    295       adjustments_x[char_count - 1] = float_from_26_6(kern.x);
    296       adjustments_y[char_count - 1] = float_from_26_6(kern.y);
    297    } else {
    298       adjustments_x[char_count - 1] = 0.0f;
    299       adjustments_y[char_count - 1] = 0.0f;
    300    }
    301 
    302    vgDrawGlyphs(font->vg_font, char_count, glyph_indices, adjustments_x, adjustments_y, paint_modes, VG_FALSE);
    303 }
    304 
    305 // Goes to the x,y position and draws arbitrary number of characters, draws 
    306 // iteratively if the char_count exceeds the max buffer size given above.
    307 
    308 static void draw_line(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, int char_count, VGbitfield paint_modes) {
    309    if (char_count == 0) return;
    310 
    311    // Set origin to requested x,y
    312    VGfloat glor[] = { x, y };
    313    vgSetfv(VG_GLYPH_ORIGIN, 2, glor);
    314 
    315    // Draw the characters in blocks to reuse buffer memory
    316    const char *curr_text = text;
    317    int chars_left = char_count;
    318    while (chars_left > CHAR_COUNT_MAX) {
    319       draw_chars(font, curr_text, CHAR_COUNT_MAX, paint_modes, 1);
    320       chars_left -= CHAR_COUNT_MAX;
    321       curr_text += CHAR_COUNT_MAX;
    322    }
    323 
    324    // Draw the last block
    325    draw_chars(font, curr_text, chars_left, paint_modes, 0);
    326 }
    327 
    328 void vgft_font_draw(VGFT_FONT_T *font, VGfloat x, VGfloat y, const char *text, unsigned text_length, VGbitfield paint_modes)
    329 {
    330    VGfloat descent = float_from_26_6(font->ft_face->size->metrics.descender);
    331    int last_draw = 0;
    332    int i = 0;
    333    y -= descent;
    334    for (;;) {
    335       int last = !text[i] || (text_length && i==text_length);
    336 
    337       if ((text[i] == '\n') || last)
    338       {
    339          draw_line(font, x, y, text + last_draw, i - last_draw, paint_modes);
    340          last_draw = i+1;
    341          y -= float_from_26_6(font->ft_face->size->metrics.height);
    342       }
    343       if (last)
    344       {
    345          break;
    346       }
    347       ++i;
    348    }
    349 }
    350 
    351 // Get text extents for a single line
    352 
    353 static void line_extents(VGFT_FONT_T *font, VGfloat *x, VGfloat *y, const char *text, int chars_count)
    354 {
    355    int i;
    356    int prev_glyph_index = 0;
    357    if (chars_count == 0) return;
    358 
    359    for (i=0; i < chars_count; i++)
    360    {
    361       int glyph_index = FT_Get_Char_Index(font->ft_face, text[i]);
    362       if (!glyph_index) return;
    363 
    364       if (i != 0)
    365       {
    366          FT_Vector kern;
    367          if (FT_Get_Kerning(font->ft_face, prev_glyph_index, glyph_index,
    368                             FT_KERNING_DEFAULT, &kern))
    369          {
    370             assert(0);
    371          }
    372          *x += float_from_26_6(kern.x);
    373          *y += float_from_26_6(kern.y);
    374       }
    375       FT_Load_Glyph(font->ft_face, glyph_index, FT_LOAD_DEFAULT);
    376       *x += float_from_26_6(font->ft_face->glyph->advance.x);
    377 
    378       prev_glyph_index = glyph_index;
    379    }
    380 }
    381 
    382 // Text extents for some ASCII text.
    383 //
    384 // Use text_length if non-zero, otherwise look for trailing '\0'.
    385 
    386 void vgft_get_text_extents(VGFT_FONT_T *font,
    387                            const char *text,
    388                            unsigned text_length,
    389                            VGfloat unused0, VGfloat unused1,
    390                            VGfloat *w, VGfloat *h) {
    391    int last_draw = 0;
    392    VGfloat max_x = 0;
    393    VGfloat y = 0;
    394 
    395    int i, last;
    396    for (i = 0, last = 0; !last; ++i) {
    397       last = !text[i] || (text_length && i==text_length);
    398       if ((text[i] == '\n') || last) {
    399          VGfloat x = 0;
    400          line_extents(font, &x, &y, text + last_draw, i - last_draw);
    401          last_draw = i + 1;
    402          y -= float_from_26_6(font->ft_face->size->metrics.height);
    403          if (x > max_x) max_x = x;
    404       }
    405    }
    406    *w = max_x;
    407    *h = -y;
    408 }
    409 
    410 // Get y offset for first line; mitigates issue of start y being middle of block
    411 // for multiline renders by vgft_font_draw.  Currently simple, may be worth
    412 // adding y kerning?
    413 
    414 VGfloat vgft_first_line_y_offset(VGFT_FONT_T *font) {
    415    return float_from_26_6(font->ft_face->size->metrics.height);
    416 }