font.c (9612B)
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 /** @file font.c 31 * 32 * Fairly primitive font handling, just enough to emulate the old API. 33 * 34 * Hinting and Font Size 35 * 36 * The old API does not create fonts explicitly, it just renders them 37 * as needed. That works fine for unhinted fonts, but for hinted fonts we 38 * care about font size. 39 * 40 * Since we now *can* do hinted fonts, we should do. Regenerating the 41 * fonts each time becomes quite slow, so we maintain a cache of fonts. 42 * 43 * For the typical applications which use graphics_x this is fine, but 44 * won't work well if lots of fonts sizes are used. 45 * 46 * Unicode 47 * 48 * This API doesn't support unicode at all at present, nor UTF-8. 49 */ 50 51 #include <fcntl.h> 52 #include <stdio.h> 53 #include <unistd.h> 54 #include <sys/types.h> 55 56 #include "graphics_x_private.h" 57 #include "vgft.h" 58 59 #define VMCS_INSTALL_PREFIX "" 60 61 /** The one and only (default) font we support for now. 62 */ 63 static struct 64 { 65 const char *file; 66 void *mem; 67 size_t len; 68 } default_font = { "VeraMono.ttf" }; 69 70 /** An entry in our list of fonts 71 */ 72 typedef struct gx_font_cache_entry_t 73 { 74 struct gx_font_cache_entry_t *next; 75 VGFT_FONT_T font; 76 uint32_t ptsize; /** size in points, 26.6 */ 77 } gx_font_cache_entry_t; 78 79 static char fname[128]; 80 static int inited; 81 static gx_font_cache_entry_t *fonts; 82 83 static VGFT_FONT_T *find_font(const char *text, uint32_t text_size); 84 85 VCOS_STATUS_T gx_priv_font_init(const char *font_dir) 86 { 87 VCOS_STATUS_T ret; 88 size_t len; 89 int rc; 90 if (vgft_init()) 91 { 92 ret = VCOS_ENOMEM; 93 goto fail_init; 94 } 95 96 int fd = -1; 97 // search for the font 98 sprintf(fname, "%s/%s", font_dir, default_font.file); 99 fd = open(fname, O_RDONLY); 100 101 if (fd < 0) 102 { 103 GX_ERROR("Could not open font file '%s'", default_font.file); 104 ret = VCOS_ENOENT; 105 goto fail_open; 106 } 107 108 len = lseek(fd, 0, SEEK_END); 109 lseek(fd, 0, SEEK_SET); 110 111 default_font.mem = vcos_malloc(len, default_font.file); 112 if (!default_font.mem) 113 { 114 GX_ERROR("No memory for font %s", fname); 115 ret = VCOS_ENOMEM; 116 goto fail_mem; 117 } 118 119 rc = read(fd, default_font.mem, len); 120 if (rc != len) 121 { 122 GX_ERROR("Could not read font %s", fname); 123 ret = VCOS_EINVAL; 124 goto fail_rd; 125 } 126 default_font.len = len; 127 close(fd); 128 129 GX_TRACE("Opened font file '%s'", fname); 130 131 inited = 1; 132 return VCOS_SUCCESS; 133 134 fail_rd: 135 vcos_free(default_font.mem); 136 fail_mem: 137 if (fd >= 0) close(fd); 138 fail_open: 139 vgft_term(); 140 fail_init: 141 return ret; 142 } 143 144 void gx_priv_font_term(void) 145 { 146 gx_font_cache_flush(); 147 vgft_term(); 148 vcos_free(default_font.mem); 149 } 150 151 /** Render text. 152 * 153 * FIXME: Not at all optimal - re-renders each time. 154 * FIXME: Not UTF-8 aware 155 * FIXME: better caching 156 */ 157 VCOS_STATUS_T gx_priv_render_text( GX_DISPLAY_T *disp, 158 GRAPHICS_RESOURCE_HANDLE res, 159 int32_t x, 160 int32_t y, 161 uint32_t width, 162 uint32_t height, 163 uint32_t fg_colour, 164 uint32_t bg_colour, 165 const char *text, 166 uint32_t text_length, 167 uint32_t text_size ) 168 { 169 VGfloat vg_colour[4]; 170 VGFT_FONT_T *font; 171 VGPaint fg; 172 GX_CLIENT_STATE_T save; 173 VCOS_STATUS_T status = VCOS_SUCCESS; 174 int clip = 1; 175 176 vcos_demand(inited); // has gx_font_init() been called? 177 178 gx_priv_save(&save, res); 179 180 if (width == GRAPHICS_RESOURCE_WIDTH && 181 height == GRAPHICS_RESOURCE_HEIGHT) 182 { 183 clip = 0; 184 } 185 186 width = (width == GRAPHICS_RESOURCE_WIDTH) ? res->width : width; 187 height = (height == GRAPHICS_RESOURCE_HEIGHT) ? res->height : height; 188 font = find_font(text, text_size); 189 if (!font) 190 { 191 status = VCOS_ENOMEM; 192 goto finish; 193 } 194 195 // setup the clipping rectangle 196 if (clip) 197 { 198 VGint coords[] = {x,y,width,height}; 199 vgSeti(VG_SCISSORING, VG_TRUE); 200 vgSetiv(VG_SCISSOR_RECTS, 4, coords); 201 } 202 203 // setup the background colour if needed 204 if (bg_colour != GRAPHICS_TRANSPARENT_COLOUR) 205 { 206 int err; 207 VGfloat rendered_w, rendered_h; 208 VGfloat vg_bg_colour[4]; 209 210 // setup the background colour... 211 gx_priv_colour_to_paint(bg_colour, vg_bg_colour); 212 vgSetfv(VG_CLEAR_COLOR, 4, vg_bg_colour); 213 214 // fill in a rectangle... 215 vgft_get_text_extents(font, text, text_length, (VGfloat)x, (VGfloat)y, &rendered_w, &rendered_h); 216 217 if ( ( 0 < (VGint)rendered_w ) && ( 0 < (VGint)rendered_h ) ) 218 { 219 // Have to compensate for the messed up y position of multiline text. 220 VGfloat offset = vgft_first_line_y_offset(font); 221 int32_t bottom = y + offset - rendered_h; 222 223 vgClear(x, bottom, (VGint)rendered_w, (VGint)rendered_h); 224 err = vgGetError(); 225 if (err) 226 { 227 GX_LOG("Error %d clearing bg text %d %d %g %g", 228 err, x, y, rendered_w, rendered_h); 229 vcos_assert(0); 230 } // if 231 } // if 232 } // if 233 // setup the foreground colour 234 fg = vgCreatePaint(); 235 if (!fg) 236 { 237 status = VCOS_ENOMEM; 238 goto finish; 239 } 240 241 // draw the foreground text 242 vgSetParameteri(fg, VG_PAINT_TYPE, VG_PAINT_TYPE_COLOR); 243 gx_priv_colour_to_paint(fg_colour, vg_colour); 244 vgSetParameterfv(fg, VG_PAINT_COLOR, 4, vg_colour); 245 vgSetPaint(fg, VG_FILL_PATH); 246 247 vgft_font_draw(font, (VGfloat)x, (VGfloat)y, text, text_length, VG_FILL_PATH); 248 249 vgDestroyPaint(fg); 250 251 vcos_assert(vgGetError() == 0); 252 vgSeti(VG_SCISSORING, VG_FALSE); 253 254 finish: 255 gx_priv_restore(&save); 256 257 return status; 258 } 259 260 261 /** Find a font in our cache, or create a new entry in the cache. 262 * 263 * Very primitive at present. 264 */ 265 static VGFT_FONT_T *find_font(const char *text, uint32_t text_size) 266 { 267 int ptsize, dpi_x = 0, dpi_y = 0; 268 VCOS_STATUS_T status; 269 gx_font_cache_entry_t *font; 270 271 ptsize = text_size << 6; // freetype takes size in points, in 26.6 format. 272 273 for (font = fonts; font; font = font->next) 274 { 275 if (font->ptsize == ptsize) 276 return &font->font; 277 } 278 279 font = vcos_malloc(sizeof(*font), "font"); 280 if (!font) 281 return NULL; 282 283 font->ptsize = ptsize; 284 285 status = vgft_font_init(&font->font); 286 if (status != VCOS_SUCCESS) 287 { 288 vcos_free(font); 289 return NULL; 290 } 291 292 // load the font 293 status = vgft_font_load_mem(&font->font, default_font.mem, default_font.len); 294 if (status != VCOS_SUCCESS) 295 { 296 GX_LOG("Could not load font from memory: %d", status); 297 vgft_font_term(&font->font); 298 vcos_free(font); 299 return NULL; 300 } 301 302 status = vgft_font_convert_glyphs(&font->font, ptsize, dpi_x, dpi_y); 303 if (status != VCOS_SUCCESS) 304 { 305 GX_LOG("Could not convert font '%s' at size %d", fname, ptsize); 306 vgft_font_term(&font->font); 307 vcos_free(font); 308 return NULL; 309 } 310 311 font->next = fonts; 312 fonts = font; 313 314 return &font->font; 315 } 316 317 void gx_font_cache_flush(void) 318 { 319 while (fonts != NULL) 320 { 321 struct gx_font_cache_entry_t *next = fonts->next; 322 vgft_font_term(&fonts->font); 323 vcos_free(fonts); 324 fonts = next; 325 } 326 } 327 328 int32_t graphics_resource_text_dimensions_ext(GRAPHICS_RESOURCE_HANDLE res, 329 const char *text, 330 const uint32_t text_length, 331 uint32_t *width, 332 uint32_t *height, 333 const uint32_t text_size ) 334 { 335 GX_CLIENT_STATE_T save; 336 VGfloat w, h; 337 int ret = -1; 338 339 gx_priv_save(&save, res); 340 341 VGFT_FONT_T *font = find_font(text, text_size); 342 if (!font) 343 goto finish; 344 345 346 vgft_get_text_extents(font, text, text_length, 0.0, 0.0, &w, &h); 347 *width = w; 348 *height = h; 349 ret = 0; 350 351 finish: 352 gx_priv_restore(&save); 353 return ret; 354 } 355