]> git.sesse.net Git - vlc/blob - modules/gui/skins2/src/ft2_font.cpp
Remove useless test before free and delete.
[vlc] / modules / gui / skins2 / src / ft2_font.cpp
1 /*****************************************************************************
2  * ft2_font.cpp
3  *****************************************************************************
4  * Copyright (C) 2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Cyril Deguet     <asmax@via.ecp.fr>
8  *          Olivier Teulière <ipkiss@via.ecp.fr>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 #include "ft2_font.hpp"
26 #include "ft2_bitmap.hpp"
27 #include "../utils/ustring.hpp"
28
29 #ifdef HAVE_FRIBIDI
30 #include <fribidi/fribidi.h>
31 #endif
32
33
34 FT2Font::FT2Font( intf_thread_t *pIntf, const string &rName, int size ):
35     GenericFont( pIntf ), m_name( rName ), m_buffer( NULL ), m_size( size ),
36     m_lib( NULL ), m_face( NULL )
37 {
38 }
39
40
41 FT2Font::~FT2Font()
42 {
43     // Clear the glyph cache
44     GlyphMap_t::iterator iter;
45     for( iter = m_glyphCache.begin(); iter != m_glyphCache.end(); ++iter )
46     {
47         FT_Done_Glyph( (*iter).second.m_glyph );
48     }
49     if( m_face )
50     {
51         FT_Done_Face( m_face );
52     }
53     if( m_lib )
54     {
55         FT_Done_FreeType( m_lib );
56     }
57     free( m_buffer );
58 }
59
60
61 bool FT2Font::init()
62 {
63     int err;
64
65     // Initialize libfreetype
66     if( FT_Init_FreeType( &m_lib ) )
67     {
68         msg_Err( getIntf(), "failed to initialize freetype" );
69         return false;
70     }
71
72     // Open the font
73     FILE *file = fopen( m_name.c_str(), "rb" );
74     if( file )
75     {
76         msg_Dbg( getIntf(), "loading font %s", m_name.c_str() );
77     }
78     else
79     {
80         msg_Dbg( getIntf(), "unable to open the font %s", m_name.c_str() );
81         return false;
82     }
83     // Get the file size
84     fseek( file, 0, SEEK_END );
85     int size = ftell( file );
86     rewind( file );
87     // Allocate the buffer
88     m_buffer = malloc( size );
89     if( !m_buffer )
90     {
91         msg_Err( getIntf(), "not enough memory for the font %s",
92                  m_name.c_str() );
93         return false;
94     }
95     // Read the font data
96     fread( m_buffer, size, 1, file );
97     fclose( file );
98
99     // Load the font from the buffer
100     err = FT_New_Memory_Face( m_lib, (const FT_Byte*)m_buffer, size, 0,
101                               &m_face );
102     if ( err == FT_Err_Unknown_File_Format )
103     {
104         msg_Err( getIntf(), "unsupported font format (%s)", m_name.c_str() );
105         return false;
106     }
107     else if ( err )
108     {
109         msg_Err( getIntf(), "error opening font (%s)", m_name.c_str() );
110         return false;
111     }
112
113     // Select the charset
114     if( FT_Select_Charmap( m_face, ft_encoding_unicode ) )
115     {
116         msg_Err( getIntf(), "font has no UNICODE table (%s)", m_name.c_str() );
117         return false;
118     }
119
120     // Set the pixel size
121     if( FT_Set_Pixel_Sizes( m_face, 0, m_size ) )
122     {
123         msg_Warn( getIntf(), "cannot set a pixel size of %d (%s)", m_size,
124                   m_name.c_str() );
125     }
126
127     // Get the font metrucs
128     m_height = m_face->size->metrics.height >> 6;
129     m_ascender = m_face->size->metrics.ascender >> 6;
130     m_descender = m_face->size->metrics.descender >> 6;
131
132     return true;
133 }
134
135
136 GenericBitmap *FT2Font::drawString( const UString &rString, uint32_t color,
137                                     int maxWidth ) const
138 {
139     uint32_t code;
140     int n;
141     int penX = 0;
142     int width1 = 0, width2 = 0;
143     int yMin = 0, yMax = 0;
144     uint32_t *pString = (uint32_t*)rString.u_str();
145
146     // Check if freetype has been initialized
147     if( !m_face )
148     {
149         return NULL;
150     }
151
152     // Get the length of the string
153     int len = rString.length();
154
155     // Use fribidi if available
156 #ifdef HAVE_FRIBIDI
157     uint32_t *pFribidiString = NULL;
158     if( len > 0 )
159     {
160         pFribidiString = new uint32_t[len+1];
161         FriBidiCharType baseDir = FRIBIDI_TYPE_ON;
162         fribidi_log2vis( (FriBidiChar*)pString, len, &baseDir,
163                          (FriBidiChar*)pFribidiString, 0, 0, 0 );
164         pString = pFribidiString;
165     }
166 #endif
167
168     // Array of glyph bitmaps and position
169     FT_BitmapGlyphRec **glyphs = new FT_BitmapGlyphRec*[len];
170     int *pos = new int[len];
171
172     // Does the font support kerning ?
173     FT_Bool useKerning = FT_HAS_KERNING( m_face );
174     int previous = 0;
175
176     // Index of the last glyph when the text is truncated with trailing ...
177     int maxIndex = 0;
178     // Position of the first trailing dot
179     int firstDotX = 0;
180     /// Get the dot glyph
181     Glyph_t &dotGlyph = getGlyph( '.' );
182
183     // First, render all the glyphs
184     for( n = 0; n < len; n++ )
185     {
186         code = *(pString++);
187         // Get the glyph for this character
188         Glyph_t &glyph = getGlyph( code );
189         glyphs[n] = (FT_BitmapGlyphRec*)(glyph.m_glyph);
190
191         // Retrieve kerning distance and move pen position
192         if( useKerning && previous && glyph.m_index )
193         {
194             FT_Vector delta;
195             FT_Get_Kerning( m_face, previous, glyph.m_index,
196                             ft_kerning_default, &delta );
197             penX += delta.x >> 6;
198         }
199
200         pos[n] = penX;
201         width1 = penX + glyph.m_size.xMax - glyph.m_size.xMin;
202         yMin = __MIN( yMin, glyph.m_size.yMin );
203         yMax = __MAX( yMax, glyph.m_size.yMax );
204
205         // Next position
206         penX += glyph.m_advance;
207
208         // Save glyph index
209         previous = glyph.m_index;
210
211         if( maxWidth != -1 )
212         {
213             // Check if the truncated text with the '...' fit in the maxWidth
214             int curX = penX;
215             if( useKerning )
216             {
217                 FT_Vector delta;
218                 FT_Get_Kerning( m_face, glyph.m_index, dotGlyph.m_index,
219                                 ft_kerning_default, &delta );
220                 curX += delta.x >> 6;
221             }
222             int dotWidth = 2 * dotGlyph.m_advance +
223                 dotGlyph.m_size.xMax - dotGlyph.m_size.xMin;
224             if( curX + dotWidth < maxWidth )
225             {
226                 width2 = curX + dotWidth;
227                 maxIndex++;
228                 firstDotX = curX;
229             }
230         }
231         else
232         {
233             // No check
234             width2 = width1;
235             maxIndex++;
236         }
237
238         // Stop here if the text is too large
239         if( maxWidth != -1 && width1 > maxWidth )
240         {
241             break;
242         }
243     }
244
245 #ifdef HAVE_FRIBIDI
246     if( len > 0 )
247     {
248         delete[] pFribidiString;
249     }
250 #endif
251
252     // Adjust the size for vertical padding
253     yMax = __MAX( yMax, m_ascender );
254     yMin = __MIN( yMin, m_descender );
255
256     // Create the bitmap
257     FT2Bitmap *pBmp = new FT2Bitmap( getIntf(), __MIN( width1, width2 ),
258                                      yMax - yMin );
259
260     // Draw the glyphs on the bitmap
261     for( n = 0; n < maxIndex; n++ )
262     {
263         FT_BitmapGlyphRec *pBmpGlyph = (FT_BitmapGlyphRec*)glyphs[n];
264         // Draw the glyph on the bitmap
265         pBmp->draw( pBmpGlyph->bitmap, pos[n], yMax - pBmpGlyph->top, color );
266     }
267     // Draw the trailing dots if the text is truncated
268     if( maxIndex < len )
269     {
270         int penX = firstDotX;
271         FT_BitmapGlyphRec *pBmpGlyph = (FT_BitmapGlyphRec*)dotGlyph.m_glyph;
272         for( n = 0; n < 3; n++ )
273         {
274             // Draw the glyph on the bitmap
275             pBmp->draw( pBmpGlyph->bitmap, penX, yMax - pBmpGlyph->top,
276                         color );
277             penX += dotGlyph.m_advance;
278         }
279     }
280
281     delete [] glyphs;
282     delete [] pos;
283
284     return pBmp;
285 }
286
287
288 FT2Font::Glyph_t &FT2Font::getGlyph( uint32_t code ) const
289 {
290     // Try to find the glyph in the cache
291     GlyphMap_t::iterator iter = m_glyphCache.find( code );
292     if( iter != m_glyphCache.end() )
293     {
294         return (*iter).second;
295     }
296     else
297     {
298         // Add a new glyph in the cache
299         Glyph_t &glyph = m_glyphCache[code];
300
301         // Load and render the glyph
302         glyph.m_index = FT_Get_Char_Index( m_face, code );
303         FT_Load_Glyph( m_face, glyph.m_index, FT_LOAD_DEFAULT );
304         FT_Get_Glyph( m_face->glyph, &glyph.m_glyph );
305         FT_Glyph_Get_CBox( glyph.m_glyph, ft_glyph_bbox_pixels,
306                            &glyph.m_size );
307         glyph.m_advance = m_face->glyph->advance.x >> 6;
308         FT_Glyph_To_Bitmap( &glyph.m_glyph, ft_render_mode_normal, NULL, 1 );
309         return glyph;
310     }
311 }
312