1 /*****************************************************************************
2 * freetype.c : Put text on the video, using freetype2
3 *****************************************************************************
4 * Copyright (C) 2002, 2003 VideoLAN
5 * $Id: freetype.c,v 1.18 2003/08/17 15:22:49 sigmunau Exp $
7 * Authors: Sigmund Augdal <sigmunau@idi.ntnu.no>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
27 #include <stdlib.h> /* malloc(), free() */
36 #include FT_FREETYPE_H
40 #define DEFAULT_FONT "/System/Library/Fonts/LucidaGrande.dfont"
41 #elif defined( SYS_BEOS )
42 #define DEFAULT_FONT "/boot/beos/etc/fonts/ttfonts/Swiss721.ttf"
44 #define DEFAULT_FONT ""
47 typedef struct line_desc_t line_desc_t;
49 /*****************************************************************************
51 *****************************************************************************/
52 static int Create ( vlc_object_t * );
53 static void Destroy ( vlc_object_t * );
55 static void Render ( vout_thread_t *, picture_t *,
56 const subpicture_t * );
57 static void RenderI420( vout_thread_t *, picture_t *,
58 const subpicture_t * );
59 static void RenderYUY2( vout_thread_t *, picture_t *,
60 const subpicture_t * );
61 static void RenderRV32( vout_thread_t *, picture_t *,
62 const subpicture_t * );
63 static int AddText ( vout_thread_t *, byte_t *, text_style_t *, int,
64 int, int, mtime_t, mtime_t );
65 static void FreeString( subpicture_t * );
67 static int GetUnicodeCharFromUTF8( byte_t ** );
69 static line_desc_t *NewLine( byte_t * );
71 /*****************************************************************************
73 *****************************************************************************/
74 #define FONT_TEXT N_("Font")
75 #define FONT_LONGTEXT N_("Filename of Font")
76 #define FONTSIZE_TEXT N_("Font size")
77 #define FONTSIZE_LONGTEXT N_("The size of the fonts used by the osd module" )
80 add_category_hint( N_("Fonts"), NULL, VLC_FALSE );
81 add_file( "freetype-font", DEFAULT_FONT, NULL, FONT_TEXT, FONT_LONGTEXT, VLC_FALSE );
82 add_integer( "freetype-fontsize", 16, NULL, FONTSIZE_TEXT, FONTSIZE_LONGTEXT, VLC_FALSE );
83 set_description( _("freetype2 font renderer") );
84 set_capability( "text renderer", 100 );
85 add_shortcut( "text" );
86 set_callbacks( Create, Destroy );
90 * Private data in a aubpicture. Describes a string.
92 struct subpicture_sys_t
99 /** The string associated with this subpicture */
101 line_desc_t *p_lines;
106 /** NULL-terminated list of glyphs making the string */
107 FT_BitmapGlyph *pp_glyphs;
108 /** list of relative positions for the glyphs */
109 FT_Vector *p_glyph_pos;
115 /*****************************************************************************
116 * text_remderer_sys_t: freetype local data
117 *****************************************************************************
118 * This structure is part of the video output thread descriptor.
119 * It describes the freetype specific properties of an output thread.
120 *****************************************************************************/
121 struct text_renderer_sys_t
123 FT_Library p_library; /* handle to library */
124 FT_Face p_face; /* handle to face object */
126 vlc_bool_t i_use_kerning;
127 uint8_t pi_gamma[256];
130 /*****************************************************************************
131 * Create: allocates osd-text video thread output method
132 *****************************************************************************
133 * This function allocates and initializes a Clone vout method.
134 *****************************************************************************/
135 #define gamma_value 2.0
136 static int Create( vlc_object_t *p_this )
138 vout_thread_t *p_vout = (vout_thread_t *)p_this;
141 double gamma_inv = 1.0f / gamma_value;
144 /* Allocate structure */
145 p_vout->p_text_renderer_data = malloc( sizeof( text_renderer_sys_t ) );
146 if( p_vout->p_text_renderer_data == NULL )
148 msg_Err( p_vout, "out of memory" );
152 for (i = 0; i < 256; i++) {
153 p_vout->p_text_renderer_data->pi_gamma[i] =
154 (uint8_t)( pow( (double)i / 255.0f, gamma_inv) * 255.0f );
157 var_Create( p_vout, "freetype-font", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
158 var_Create( p_vout, "freetype-fontsize",
159 VLC_VAR_INTEGER | VLC_VAR_DOINHERIT );
161 /* Look what method was requested */
162 var_Get( p_vout, "freetype-font", &val );
163 psz_fontfile = val.psz_string;
165 if( !psz_fontfile || !*psz_fontfile )
167 if( psz_fontfile ) free( psz_fontfile );
168 psz_fontfile = (char *)malloc( PATH_MAX + 1 );
170 GetWindowsDirectory( psz_fontfile, PATH_MAX + 1 );
171 strcat( psz_fontfile, "\\fonts\\arial.ttf" );
173 strcpy( psz_fontfile, DEFAULT_FONT );
175 msg_Err( p_vout, "user didn't specify a font" );
176 free( p_vout->p_text_renderer_data );
181 i_error = FT_Init_FreeType( &p_vout->p_text_renderer_data->p_library );
184 msg_Err( p_vout, "couldn't initialize freetype" );
185 free( p_vout->p_text_renderer_data );
189 i_error = FT_New_Face( p_vout->p_text_renderer_data->p_library,
190 psz_fontfile ? psz_fontfile : "", 0,
191 &p_vout->p_text_renderer_data->p_face );
192 if( i_error == FT_Err_Unknown_File_Format )
194 msg_Err( p_vout, "file %s have unknown format", psz_fontfile );
195 FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
196 free( p_vout->p_text_renderer_data );
197 if( psz_fontfile ) free( psz_fontfile );
202 msg_Err( p_vout, "failed to load font file %s", psz_fontfile );
203 FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
204 free( p_vout->p_text_renderer_data );
205 if( psz_fontfile ) free( psz_fontfile );
208 if( psz_fontfile ) free( psz_fontfile );
210 i_error = FT_Select_Charmap( p_vout->p_text_renderer_data->p_face,
211 ft_encoding_unicode );
214 msg_Err( p_vout, "Font has no unicode translation table" );
215 FT_Done_Face( p_vout->p_text_renderer_data->p_face );
216 FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
217 free( p_vout->p_text_renderer_data );
221 p_vout->p_text_renderer_data->i_use_kerning =
222 FT_HAS_KERNING(p_vout->p_text_renderer_data->p_face);
223 var_Get( p_vout, "freetype-fontsize", &val );
225 i_error = FT_Set_Pixel_Sizes( p_vout->p_text_renderer_data->p_face, 0,
229 msg_Err( p_vout, "couldn't set font size to %d", val.i_int );
230 free( p_vout->p_text_renderer_data );
233 p_vout->pf_add_string = AddText;
237 /*****************************************************************************
238 * Destroy: destroy Clone video thread output method
239 *****************************************************************************
240 * Clean up all data and library connections
241 *****************************************************************************/
242 static void Destroy( vlc_object_t *p_this )
244 vout_thread_t *p_vout = (vout_thread_t *)p_this;
245 FT_Done_Face( p_vout->p_text_renderer_data->p_face );
246 FT_Done_FreeType( p_vout->p_text_renderer_data->p_library );
247 free( p_vout->p_text_renderer_data );
250 /*****************************************************************************
251 * Render: place string in picture
252 *****************************************************************************
253 * This function merges the previously rendered freetype glyphs into a picture
254 *****************************************************************************/
255 static void Render( vout_thread_t *p_vout, picture_t *p_pic,
256 const subpicture_t *p_subpic )
258 switch( p_vout->output.i_chroma )
260 /* I420 target, no scaling */
261 case VLC_FOURCC('I','4','2','0'):
262 case VLC_FOURCC('I','Y','U','V'):
263 case VLC_FOURCC('Y','V','1','2'):
264 RenderI420( p_vout, p_pic, p_subpic );
267 /* RV16 target, scaling */
268 case VLC_FOURCC('R','V','1','6'):
269 RenderRV16( p_vout, p_pic, p_subpic );
272 /* RV32 target, scaling */
273 case VLC_FOURCC('R','V','2','4'):
274 case VLC_FOURCC('R','V','3','2'):
275 RenderRV32( p_vout, p_pic, p_subpic );
277 /* NVidia or BeOS overlay, no scaling */
278 case VLC_FOURCC('Y','U','Y','2'):
279 RenderYUY2( p_vout, p_pic, p_subpic );
283 msg_Err( p_vout, "unknown chroma, can't render SPU" );
289 * Draw a string on a i420 (or similar) picture
291 static void RenderI420( vout_thread_t *p_vout, picture_t *p_pic,
292 const subpicture_t *p_subpic )
294 subpicture_sys_t *p_string = p_subpic->p_sys;
295 int i_plane, x, y, pen_x, pen_y;
299 for( p_line = p_subpic->p_sys->p_lines; p_line != NULL; p_line = p_line->p_next )
301 for( i_plane = 0 ; i_plane < p_pic->i_planes ; i_plane++ )
304 int i_pitch = p_pic->p[ i_plane ].i_pitch;
306 p_in = p_pic->p[ i_plane ].p_pixels;
310 if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
312 pen_y = p_pic->p[ i_plane ].i_lines - p_string->i_height -
313 p_string->i_y_margin;
317 pen_y = p_string->i_y_margin;
319 pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
320 if ( p_string->i_flags & OSD_ALIGN_RIGHT )
322 pen_x = i_pitch - p_line->i_width
323 - p_string->i_x_margin;
325 else if ( p_string->i_flags & OSD_ALIGN_LEFT )
327 pen_x = p_string->i_x_margin;
331 pen_x = i_pitch / 2 - p_line->i_width / 2
332 + p_string->i_x_margin;
335 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
337 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
338 #define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
339 #define pixel p_in[ ( p_line->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pitch + x + pen_x + p_line->p_glyph_pos[ i ].x + p_glyph->left ]
340 for(y = 0; y < p_glyph->bitmap.rows; y++ )
342 for( x = 0; x < p_glyph->bitmap.width; x++ )
344 pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
345 ( 255 * alpha >> 8 );
354 if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
356 pen_y = p_pic->p[i_plane].i_lines - ( p_string->i_height>>1) -
357 (p_string->i_y_margin>>1);
361 pen_y = p_string->i_y_margin >> 1;
363 pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 7;
364 if ( p_string->i_flags & OSD_ALIGN_RIGHT )
366 pen_x = i_pitch - ( p_line->i_width >> 1 )
367 - ( p_string->i_x_margin >> 1 );
369 else if ( p_string->i_flags & OSD_ALIGN_LEFT )
371 pen_x = p_string->i_x_margin >> 1;
375 pen_x = i_pitch / 2 - p_line->i_width / 4
376 + p_string->i_x_margin / 2;
379 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
381 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
382 #define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ ( x + y * p_glyph->bitmap.width ) ] ]
383 #define pixel p_in[ ( ( p_line->p_glyph_pos[ i ].y >> 1 ) + pen_y + ( y >> 1 ) - ( p_glyph->top >> 1 ) ) * i_pitch + ( x >> 1 ) + pen_x + ( p_line->p_glyph_pos[ i ].x >> 1 ) + ( p_glyph->left >>1) ]
384 for( y = 0; y < p_glyph->bitmap.rows; y+=2 )
386 for( x = 0; x < p_glyph->bitmap.width; x+=2 )
388 pixel = ( ( pixel * ( 0xFF - alpha ) ) >> 8 ) +
389 ( 0x80 * alpha >> 8 );
401 * Draw a string on a YUY2 picture
403 static void RenderYUY2( vout_thread_t *p_vout, picture_t *p_pic,
404 const subpicture_t *p_subpic )
406 subpicture_sys_t *p_string = p_subpic->p_sys;
407 int x, y, pen_x, pen_y;
411 for( p_line = p_subpic->p_sys->p_lines; p_line != NULL;
412 p_line = p_line->p_next )
415 int i_pitch = p_pic->p[0].i_pitch;
417 p_in = p_pic->p[0].p_pixels;
419 if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
421 pen_y = p_pic->p[0].i_lines - p_string->i_height -
422 p_string->i_y_margin;
426 pen_y = p_string->i_y_margin;
428 pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
429 if ( p_string->i_flags & OSD_ALIGN_RIGHT )
431 pen_x = i_pitch - p_line->i_width
432 - p_string->i_x_margin;
434 else if ( p_string->i_flags & OSD_ALIGN_LEFT )
436 pen_x = p_string->i_x_margin;
440 pen_x = i_pitch / 2 - p_line->i_width / 2 + p_string->i_x_margin;
443 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
445 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
446 #define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
447 #define pixel p_in[ ( p_line->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pitch + 2 * ( x + pen_x + p_line->p_glyph_pos[ i ].x + p_glyph->left ) ]
448 for(y = 0; y < p_glyph->bitmap.rows; y++ )
450 for( x = 0; x < p_glyph->bitmap.width; x++ )
452 pixel = ( ( pixel * ( 255 - alpha ) ) >> 8 ) +
453 ( 255 * alpha >> 8 );
463 * Draw a string on a RV32 picture
465 static void RenderRV32( vout_thread_t *p_vout, picture_t *p_pic,
466 const subpicture_t *p_subpic )
468 subpicture_sys_t *p_string = p_subpic->p_sys;
469 int i_plane, x, y, pen_x, pen_y;
475 for( p_line = p_subpic->p_sys->p_lines; p_line != NULL; p_line = p_line->p_next )
478 int i_pitch = p_pic->p[ i_plane ].i_pitch;
480 p_in = p_pic->p[ i_plane ].p_pixels;
482 if ( p_string->i_flags & OSD_ALIGN_BOTTOM )
484 pen_y = p_pic->p[ i_plane ].i_lines - p_string->i_height -
485 p_string->i_y_margin;
489 pen_y = p_string->i_y_margin;
491 pen_y += p_vout->p_text_renderer_data->p_face->size->metrics.ascender >> 6;
492 if ( p_string->i_flags & OSD_ALIGN_RIGHT )
494 pen_x = i_pitch - p_line->i_width
495 - p_string->i_x_margin;
497 else if ( p_string->i_flags & OSD_ALIGN_LEFT )
499 pen_x = p_string->i_x_margin;
503 pen_x = i_pitch / 2 - p_line->i_width / 2
504 + p_string->i_x_margin;
507 for( i = 0; p_line->pp_glyphs[i] != NULL; i++ )
509 FT_BitmapGlyph p_glyph = p_line->pp_glyphs[ i ];
510 #define alpha p_vout->p_text_renderer_data->pi_gamma[ p_glyph->bitmap.buffer[ x + y * p_glyph->bitmap.width ] ]
511 #define pixel( c ) p_in[ ( p_line->p_glyph_pos[ i ].y + pen_y + y - p_glyph->top ) * i_pitch + ( x + pen_x + p_line->p_glyph_pos[ i ].x + p_glyph->left ) * 4 + c ]
512 for(y = 0; y < p_glyph->bitmap.rows; y++ )
514 for( x = 0; x < p_glyph->bitmap.width; x++ )
516 pixel( 0 ) = ( ( pixel( 0 ) * ( 255 - alpha ) ) >> 8 ) +
517 ( 255 * alpha >> 8 );
518 pixel( 1 ) = ( ( pixel( 1 ) * ( 255 - alpha ) ) >> 8 ) +
519 ( 255 * alpha >> 8 );
520 pixel( 2 ) = ( ( pixel( 2 ) * ( 255 - alpha ) ) >> 8 ) +
521 ( 255 * alpha >> 8 );
531 * This function receives a string and creates a subpicture for it. It
532 * also calculates the size needed for this string, and renders the
533 * needed glyphs into memory. It is used as pf_add_string callback in
534 * the vout method by this module
536 static int AddText ( vout_thread_t *p_vout, byte_t *psz_string,
537 text_style_t *p_style, int i_flags, int i_hmargin,
538 int i_vmargin, mtime_t i_start, mtime_t i_stop )
540 subpicture_sys_t *p_string;
541 int i, i_pen_y, i_pen_x, i_error, i_glyph_index, i_previous, i_char;
542 subpicture_t *p_subpic;
543 line_desc_t *p_line, *p_next;
551 if ( !psz_string || !*psz_string )
567 /* Create and initialize a subpicture */
568 p_subpic = vout_CreateSubPicture( p_vout, MEMORY_SUBPICTURE );
569 if ( p_subpic == NULL )
574 p_subpic->pf_render = Render;
575 p_subpic->pf_destroy = FreeString;
576 p_subpic->i_start = i_start;
577 p_subpic->i_stop = i_stop;
580 p_subpic->b_ephemer = VLC_TRUE;
584 p_subpic->b_ephemer = VLC_FALSE;
587 /* Create and initialize private data for the subpicture */
588 p_string = malloc( sizeof(subpicture_sys_t) );
589 if ( p_string == NULL )
591 msg_Err( p_vout, "Out of memory" );
594 p_subpic->p_sys = p_string;
595 p_string->i_flags = i_flags;
596 p_string->i_x_margin = i_hmargin;
597 p_string->i_y_margin = i_vmargin;
598 p_string->p_lines = 0;
599 p_string->psz_text = strdup( psz_string );
601 /* Calculate relative glyph positions and a bounding box for the
603 p_line = NewLine( psz_string );
606 msg_Err( p_vout, "Out of memory" );
609 p_string->p_lines = p_line;
615 #define face p_vout->p_text_renderer_data->p_face
616 #define glyph face->glyph
620 i_char = GetUnicodeCharFromUTF8( &psz_string );
622 if ( i_char == '\r' ) /* ignore CR chars wherever they may be */
627 if ( i_char == '\n' )
629 p_next = NewLine( psz_string );
632 msg_Err( p_vout, "Out of memory" );
635 p_line->p_next = p_next;
636 p_line->i_width = line.xMax;
637 p_line->i_height = face->size->metrics.height >> 6;
638 p_line->pp_glyphs[ i ] = NULL;
640 result.x = __MAX( result.x, line.xMax );
641 result.y += face->size->metrics.height >> 6;
647 i_pen_y += face->size->metrics.height >> 6;
648 msg_Dbg( p_vout, "Creating new line, i is %d", i );
653 i_glyph_index = FT_Get_Char_Index( face, i_char );
654 if ( p_vout->p_text_renderer_data->i_use_kerning && i_glyph_index
658 FT_Get_Kerning( face, i_previous, i_glyph_index,
659 ft_kerning_default, &delta );
660 i_pen_x += delta.x >> 6;
663 p_line->p_glyph_pos[ i ].x = i_pen_x;
664 p_line->p_glyph_pos[ i ].y = i_pen_y;
665 i_error = FT_Load_Glyph( face, i_glyph_index, FT_LOAD_DEFAULT );
668 msg_Err( p_vout, "FT_Load_Glyph returned %d", i_error );
671 i_error = FT_Get_Glyph( glyph, &tmp_glyph );
674 msg_Err( p_vout, "FT_Get_Glyph returned %d", i_error );
677 FT_Glyph_Get_CBox( tmp_glyph, ft_glyph_bbox_pixels, &glyph_size );
678 i_error = FT_Glyph_To_Bitmap( &tmp_glyph, ft_render_mode_normal,
680 if ( i_error ) continue;
681 p_line->pp_glyphs[ i ] = (FT_BitmapGlyph)tmp_glyph;
684 line.xMax = p_line->p_glyph_pos[i].x + glyph_size.xMax - glyph_size.xMin;
685 line.yMax = __MAX( line.yMax, glyph_size.yMax );
686 line.yMin = __MIN( line.yMin, glyph_size.yMin );
688 i_previous = i_glyph_index;
689 i_pen_x += glyph->advance.x >> 6;
692 p_line->i_width = line.xMax;
693 p_line->i_height = face->size->metrics.height >> 6;
694 p_line->pp_glyphs[ i ] = NULL;
695 result.x = __MAX( result.x, line.xMax );
696 result.y += line.yMax - line.yMin;
697 p_string->i_height = result.y;
698 p_string->i_width = result.x;
699 msg_Dbg( p_vout, "string height is %d, width is %d",
700 p_string->i_height, p_string->i_width );
701 msg_Dbg( p_vout, "adding string \"%s\" at (%d,%d) start_date "I64Fd
702 " end_date" I64Fd, p_string->psz_text, p_string->i_x_margin,
703 p_string->i_y_margin, i_start, i_stop );
704 vout_DisplaySubPicture( p_vout, p_subpic );
711 FreeString( p_subpic );
712 vout_DestroySubPicture( p_vout, p_subpic );
716 static void FreeString( subpicture_t *p_subpic )
719 subpicture_sys_t *p_string = p_subpic->p_sys;
720 line_desc_t *p_line, *p_next;
722 if( p_subpic->p_sys == NULL ) return;
724 for( p_line = p_string->p_lines; p_line != NULL; p_line = p_next )
726 p_next = p_line->p_next;
727 for( i = 0; p_line->pp_glyphs[ i ] != NULL; i++ )
729 FT_Done_Glyph( (FT_Glyph)p_line->pp_glyphs[ i ] );
731 free( p_line->pp_glyphs );
732 free( p_line->p_glyph_pos );
736 free( p_string->psz_text );
740 /* convert one or more utf8 bytes into a unicode character */
741 static int GetUnicodeCharFromUTF8( byte_t **ppsz_utf8_string )
743 int i_remaining_bytes, i_char = 0;
744 if( ( **ppsz_utf8_string & 0xFC ) == 0xFC )
746 i_char = **ppsz_utf8_string & 1;
747 i_remaining_bytes = 5;
749 else if( ( **ppsz_utf8_string & 0xF8 ) == 0xF8 )
751 i_char = **ppsz_utf8_string & 3;
752 i_remaining_bytes = 4;
754 else if( ( **ppsz_utf8_string & 0xF0 ) == 0xF0 )
756 i_char = **ppsz_utf8_string & 7;
757 i_remaining_bytes = 3;
759 else if( ( **ppsz_utf8_string & 0xE0 ) == 0xE0 )
761 i_char = **ppsz_utf8_string & 15;
762 i_remaining_bytes = 2;
764 else if( ( **ppsz_utf8_string & 0xC0 ) == 0xC0 )
766 i_char = **ppsz_utf8_string & 31;
767 i_remaining_bytes = 1;
771 i_char = **ppsz_utf8_string;
772 i_remaining_bytes = 0;
774 while( i_remaining_bytes )
776 (*ppsz_utf8_string)++;
778 i_char = ( i_char << 6 ) + ( **ppsz_utf8_string & 0x3F );
780 (*ppsz_utf8_string)++;
784 static line_desc_t *NewLine( byte_t *psz_string )
787 line_desc_t *p_line = malloc( sizeof(line_desc_t) );
792 p_line->i_height = 0;
794 p_line->p_next = NULL;
796 /* We don't use CountUtf8Characters() here because we are not acutally
797 * sure the string is utf8. Better be safe than sorry. */
798 i_count = strlen( psz_string );
800 p_line->pp_glyphs = malloc( sizeof(FT_BitmapGlyph)
802 if( p_line->pp_glyphs == NULL )
807 p_line->p_glyph_pos = malloc( sizeof( FT_Vector )
809 if( p_line->p_glyph_pos == NULL )
811 free( p_line->pp_glyphs );