]> git.sesse.net Git - vlc/blob - modules/gui/skins2/src/ft2_font.cpp
skins2: fix compiler warnings (missing ())
[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 <errno.h>
26 #include "ft2_font.hpp"
27 #include "ft2_bitmap.hpp"
28 #include "ft2_err.h"
29 #include "../utils/ustring.hpp"
30
31 #ifdef HAVE_FRIBIDI
32 #include <fribidi/fribidi.h>
33 #endif
34
35
36 FT2Font::FT2Font( intf_thread_t *pIntf, const string &rName, int size ):
37     GenericFont( pIntf ), m_name( rName ), m_buffer( NULL ), m_size( size ),
38     m_lib( NULL ), m_face( NULL )
39 {
40 }
41
42
43 FT2Font::~FT2Font()
44 {
45     GlyphMap_t::iterator iter;
46     for( iter = m_glyphCache.begin(); iter != m_glyphCache.end(); ++iter )
47         FT_Done_Glyph( (*iter).second.m_glyph );
48     if( m_face ) FT_Done_Face( m_face );
49     if( m_lib )  FT_Done_FreeType( m_lib );
50     delete[] m_buffer;
51 }
52
53
54 bool FT2Font::init()
55 {
56     unsigned err;
57
58     if( ( err = FT_Init_FreeType( &m_lib ) ) )
59     {
60         msg_Err( getIntf(), "failed to initialize freetype (%s)",
61                  ft2_strerror( err ) );
62         return false;
63     }
64
65     FILE *file = fopen( m_name.c_str(), "rb" );
66     if( !file )
67     {
68         msg_Dbg( getIntf(), "failed to open font %s (%s)",
69                  m_name.c_str(), strerror(errno) );
70         return false;
71     }
72     msg_Dbg( getIntf(), "loading font %s", m_name.c_str() );
73
74     fseek( file, 0, SEEK_END );
75     long size = ftell( file );
76     rewind( file );
77
78     if( -1==size )
79     {
80         msg_Dbg( getIntf(), "fseek loading font %s (%s)",
81                  m_name.c_str(), strerror(errno) );
82         fclose( file );
83         return false;
84     }
85
86     m_buffer = new (std::nothrow) char[size];
87     if( !m_buffer )
88     {
89         fclose( file );
90         return false;
91     }
92
93     if( fread( m_buffer, size, 1, file ) != 1 )
94     {
95         msg_Err( getIntf(), "unexpected result for read" );
96         fclose( file );
97         return false;
98     }
99     fclose( file );
100
101     err = FT_New_Memory_Face( m_lib, (const FT_Byte*)m_buffer, size, 0,
102                               &m_face );
103     if ( err == FT_Err_Unknown_File_Format )
104     {
105         msg_Err( getIntf(), "unsupported font format (%s)", m_name.c_str() );
106         return false;
107     }
108     else if ( err )
109     {
110         msg_Err( getIntf(), "error opening font %s (%s)",
111                  m_name.c_str(), ft2_strerror(err) );
112         return false;
113     }
114
115     // Select the charset
116     if( ( err = FT_Select_Charmap( m_face, ft_encoding_unicode ) ) )
117     {
118         msg_Err( getIntf(), "font %s has no UNICODE table (%s)",
119                  m_name.c_str(), ft2_strerror(err) );
120         return false;
121     }
122
123     // Set the pixel size
124     if( ( err = FT_Set_Pixel_Sizes( m_face, 0, m_size ) ) )
125     {
126         msg_Warn( getIntf(), "cannot set a pixel size of %d for %s (%s)",
127                   m_size, m_name.c_str(), ft2_strerror(err) );
128     }
129
130     // Get the font metrucs
131     m_height = m_face->size->metrics.height >> 6;
132     m_ascender = m_face->size->metrics.ascender >> 6;
133     m_descender = m_face->size->metrics.descender >> 6;
134
135     return true;
136 }
137
138
139 GenericBitmap *FT2Font::drawString( const UString &rString, uint32_t color,
140                                     int maxWidth ) const
141 {
142     uint32_t code;
143     int n;
144     int penX = 0;
145     int width1 = 0, width2 = 0;
146     int yMin = 0, yMax = 0;
147     uint32_t *pString = (uint32_t*)rString.u_str();
148
149     // Check if freetype has been initialized
150     if( !m_face )
151     {
152         return NULL;
153     }
154
155     // Get the length of the string
156     int len = rString.length();
157
158     // Use fribidi if available
159 #ifdef HAVE_FRIBIDI
160     uint32_t *pFribidiString = NULL;
161     if( len > 0 )
162     {
163         pFribidiString = new uint32_t[len+1];
164         FriBidiCharType baseDir = FRIBIDI_TYPE_ON;
165         fribidi_log2vis( (FriBidiChar*)pString, len, &baseDir,
166                          (FriBidiChar*)pFribidiString, 0, 0, 0 );
167         pString = pFribidiString;
168     }
169 #endif
170
171     // Array of glyph bitmaps and position
172     FT_BitmapGlyphRec **glyphs = new FT_BitmapGlyphRec*[len];
173     int *pos = new int[len];
174
175     // Does the font support kerning ?
176     FT_Bool useKerning = FT_HAS_KERNING( m_face );
177     int previous = 0;
178
179     // Index of the last glyph when the text is truncated with trailing ...
180     int maxIndex = 0;
181     // Position of the first trailing dot
182     int firstDotX = 0;
183     /// Get the dot glyph
184     Glyph_t &dotGlyph = getGlyph( '.' );
185
186     // First, render all the glyphs
187     for( n = 0; n < len; n++ )
188     {
189         code = *(pString++);
190         // Get the glyph for this character
191         Glyph_t &glyph = getGlyph( code );
192         glyphs[n] = (FT_BitmapGlyphRec*)(glyph.m_glyph);
193
194         // Retrieve kerning distance and move pen position
195         if( useKerning && previous && glyph.m_index )
196         {
197             FT_Vector delta;
198             FT_Get_Kerning( m_face, previous, glyph.m_index,
199                             ft_kerning_default, &delta );
200             penX += delta.x >> 6;
201         }
202
203         pos[n] = penX;
204         width1 = penX + glyph.m_size.xMax - glyph.m_size.xMin;
205         yMin = __MIN( yMin, glyph.m_size.yMin );
206         yMax = __MAX( yMax, glyph.m_size.yMax );
207
208         // Next position
209         penX += glyph.m_advance;
210
211         // Save glyph index
212         previous = glyph.m_index;
213
214         if( maxWidth != -1 )
215         {
216             // Check if the truncated text with the '...' fit in the maxWidth
217             int curX = penX;
218             if( useKerning )
219             {
220                 FT_Vector delta;
221                 FT_Get_Kerning( m_face, glyph.m_index, dotGlyph.m_index,
222                                 ft_kerning_default, &delta );
223                 curX += delta.x >> 6;
224             }
225             int dotWidth = 2 * dotGlyph.m_advance +
226                 dotGlyph.m_size.xMax - dotGlyph.m_size.xMin;
227             if( curX + dotWidth < maxWidth )
228             {
229                 width2 = curX + dotWidth;
230                 maxIndex++;
231                 firstDotX = curX;
232             }
233         }
234         else
235         {
236             // No check
237             width2 = width1;
238             maxIndex++;
239         }
240
241         // Stop here if the text is too large
242         if( maxWidth != -1 && width1 > maxWidth )
243         {
244             break;
245         }
246     }
247
248 #ifdef HAVE_FRIBIDI
249     if( len > 0 )
250     {
251         delete[] pFribidiString;
252     }
253 #endif
254
255     // Adjust the size for vertical padding
256     yMax = __MAX( yMax, m_ascender );
257     yMin = __MIN( yMin, m_descender );
258
259     // Create the bitmap
260     FT2Bitmap *pBmp = new FT2Bitmap( getIntf(), __MIN( width1, width2 ),
261                                      yMax - yMin );
262
263     // Draw the glyphs on the bitmap
264     for( n = 0; n < maxIndex; n++ )
265     {
266         FT_BitmapGlyphRec *pBmpGlyph = (FT_BitmapGlyphRec*)glyphs[n];
267         // Draw the glyph on the bitmap
268         pBmp->draw( pBmpGlyph->bitmap, pos[n], yMax - pBmpGlyph->top, color );
269     }
270     // Draw the trailing dots if the text is truncated
271     if( maxIndex < len )
272     {
273         int penX = firstDotX;
274         FT_BitmapGlyphRec *pBmpGlyph = (FT_BitmapGlyphRec*)dotGlyph.m_glyph;
275         for( n = 0; n < 3; n++ )
276         {
277             // Draw the glyph on the bitmap
278             pBmp->draw( pBmpGlyph->bitmap, penX, yMax - pBmpGlyph->top,
279                         color );
280             penX += dotGlyph.m_advance;
281         }
282     }
283
284     delete [] glyphs;
285     delete [] pos;
286
287     return pBmp;
288 }
289
290
291 FT2Font::Glyph_t &FT2Font::getGlyph( uint32_t code ) const
292 {
293     // Try to find the glyph in the cache
294     GlyphMap_t::iterator iter = m_glyphCache.find( code );
295     if( iter != m_glyphCache.end() )
296     {
297         return (*iter).second;
298     }
299     else
300     {
301         // Add a new glyph in the cache
302         Glyph_t &glyph = m_glyphCache[code];
303
304         // Load and render the glyph
305         glyph.m_index = FT_Get_Char_Index( m_face, code );
306         FT_Load_Glyph( m_face, glyph.m_index, FT_LOAD_DEFAULT );
307         FT_Get_Glyph( m_face->glyph, &glyph.m_glyph );
308         FT_Glyph_Get_CBox( glyph.m_glyph, ft_glyph_bbox_pixels,
309                            &glyph.m_size );
310         glyph.m_advance = m_face->glyph->advance.x >> 6;
311         FT_Glyph_To_Bitmap( &glyph.m_glyph, ft_render_mode_normal, NULL, 1 );
312         return glyph;
313     }
314 }
315