Added a 3D font loader that uses the Freetype2 library to
[blender.git] / source / blender / blenlib / intern / freetypefont.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
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.
18  *
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 Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is written by Rob Haarsma (phase)
24  * All rights reserved.
25  *
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL/BL DUAL LICENSE BLOCK *****
30  */
31
32 #ifdef WITH_FREETYPE2
33
34 #ifdef WIN32
35 #pragma warning (disable:4244)
36 #endif
37
38 #include <ft2build.h>
39 #include FT_FREETYPE_H
40 #include FT_GLYPH_H
41
42 #include "MEM_guardedalloc.h"
43
44 #include "BLI_vfontdata.h"
45 #include "BLI_blenlib.h"
46 #include "BLI_arithb.h"  
47
48 #include "BIF_toolbox.h"
49
50 #include "DNA_packedFile_types.h"
51 #include "DNA_curve_types.h"
52
53 #define myMIN_ASCII     32
54 #define myMAX_ASCII     126
55
56 // should come from arithb.c
57 #define MIN2(x,y)               ( (x)<(y) ? (x) : (y) )
58 #define MAX2(x,y)               ( (x)>(y) ? (x) : (y) )
59
60 /* local variables */
61 static FT_Library       library;
62 static FT_Error         err;
63
64 #if 0
65 // Freetype2 Outline struct
66
67 typedef struct  FT_Outline_
68   {
69     short       n_contours;      /* number of contours in glyph        */
70     short       n_points;        /* number of points in the glyph      */
71
72     FT_Vector*  points;          /* the outline's points               */
73     char*       tags;            /* the points flags                   */
74     short*      contours;        /* the contour end points             */
75
76     int         flags;           /* outline masks                      */
77
78   } FT_Outline;
79 #endif
80
81 /***//*
82 from: http://www.freetype.org/freetype2/docs/glyphs/glyphs-6.html#section-1
83
84 Vectorial representation of Freetype glyphs
85
86 The source format of outlines is a collection of closed paths called "contours". Each contour is
87 made of a series of line segments and bezier arcs. Depending on the file format, these can be
88 second-order or third-order polynomials. The former are also called quadratic or conic arcs, and
89 they come from the TrueType format. The latter are called cubic arcs and mostly come from the
90 Type1 format.
91
92 Each arc is described through a series of start, end and control points. Each point of the outline
93 has a specific tag which indicates wether it is used to describe a line segment or an arc.
94
95
96 The following rules are applied to decompose the contour's points into segments and arcs :
97
98 # two successive "on" points indicate a line segment joining them.
99
100 # one conic "off" point amidst two "on" points indicates a conic bezier arc, the "off" point being
101   the control point, and the "on" ones the start and end points.
102
103 # Two successive cubic "off" points amidst two "on" points indicate a cubic bezier arc. There must
104   be exactly two cubic control points and two on points for each cubic arc (using a single cubic 
105   "off" point between two "on" points is forbidden, for example).
106
107 # finally, two successive conic "off" points forces the rasterizer to create (during the scan-line
108   conversion process exclusively) a virtual "on" point amidst them, at their exact middle. This
109   greatly facilitates the definition of successive conic bezier arcs. Moreover, it's the way
110   outlines are described in the TrueType specification.
111
112 Note that it is possible to mix conic and cubic arcs in a single contour, even though no current
113 font driver produces such outlines.
114
115                                   *            # on      
116                                                * off
117                                __---__
118   #-__                      _--       -_
119       --__                _-            -
120           --__           #               \
121               --__                        #
122                   -#
123                            Two "on" points
124    Two "on" points       and one "conic" point
125                             between them
126
127
128
129                 *
130   #            __      Two "on" points with two "conic"
131    \          -  -     points between them. The point
132     \        /    \    marked '0' is the middle of the
133      -      0      \   "off" points, and is a 'virtual'
134       -_  _-       #   "on" point where the curve passes.
135         --             It does not appear in the point
136                        list.
137         *
138
139
140
141
142         *                # on
143                    *     * off
144          __---__
145       _--       -_
146     _-            -
147    #               \
148                     #
149
150      Two "on" points
151    and two "cubic" point
152       between them
153
154
155 Each glyph's original outline points are located on a grid of indivisible units. The points are stored
156 in the font file as 16-bit integer grid coordinates, with the grid origin's being at (0,0); they thus
157 range from -16384 to 16383.
158
159
160 Convert conic to bezier arcs:
161 Conic P0 P1 P2
162 Bezier B0 B1 B2 B3
163 B0=P0
164 B1=(P0+2*P1)/3
165 B2=(P2+2*P1)/3
166 B3=P2
167
168 *//****/
169
170 static VFontData *objfnt_to_ftvfontdata(PackedFile * pf)
171 {
172         // Blender
173         VFontData *vfd;
174         struct Nurb *nu;
175         struct BezTriple *bezt;
176
177         // Freetype2
178         FT_Face         face;
179         FT_GlyphSlot  glyph;
180         FT_UInt         glyph_index;
181         FT_Outline      ftoutline;
182
183         float scale= 1. / 1024.; //needs text_height from metrics to make a standard linedist
184         float dx, dy;
185         int i, j, k, l, m; /* uhoh, kiddie C loops */
186         /* i = characters, j = curves/contours, k = points, l = curvepoint, m = first point on curve */
187
188         // test is used for BIF_printf
189         char test[2];
190
191         
192         // load the freetype font
193         err = FT_New_Memory_Face( library,
194                                                 pf->data,
195                                                 pf->size,
196                                                 0,
197                                                 &face );
198
199         if(err) return NULL;
200
201         // allocate blender font
202         vfd= MEM_callocN(sizeof(*vfd), "FTVFontData");
203
204 //FT_Set_Charmap(face, ft_encoding_symbol);
205
206         // extract generic ascii character range (needs international support, dynamic loading of chars, etcetc)
207         for(i = myMIN_ASCII; i <= myMAX_ASCII; i++) {
208                 int  *npoints;  //total points of each contour
209                 int  *onpoints; //num points on curve
210
211                 test[0] = i;
212                 test[1] = '\0'; //to print character
213
214                 glyph_index = FT_Get_Char_Index( face, i );
215                 err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE);
216                 glyph = face->glyph;
217                 ftoutline = glyph->outline;
218
219                 vfd->width[i] = glyph->advance.x* scale;
220 //              BIF_printf("sx %d sy %d", glyph->advance.x, face->glyph->metrics->text_height);
221
222                 npoints = (int *)MEM_callocN((ftoutline.n_contours)* sizeof(int),"endpoints") ;
223                 onpoints = (int *)MEM_callocN((ftoutline.n_contours)* sizeof(int),"onpoints") ;
224
225                 // calculate total points of each contour
226                 for(j = 0; j < ftoutline.n_contours; j++) {
227                         if(j == 0)
228                                 npoints[j] = ftoutline.contours[j] + 1;
229                         else
230                                 npoints[j] = ftoutline.contours[j] - ftoutline.contours[j - 1];
231                 }
232
233                 // get number of on-curve points for beziertriples (including conic virtual on-points) 
234                 for(j = 0; j < ftoutline.n_contours; j++) {
235                         l = 0;
236                         for(k = 0; k < npoints[j]; k++) {
237                                 if(j > 0) l = k + ftoutline.contours[j - 1] + 1; else l = k;
238
239 //                              if(i == 67) BIF_printf("%d->%s : |k %2d|l %2d|t %2d|", i, test, k, l, ftoutline.n_points);
240
241                                 if(ftoutline.tags[l] == FT_Curve_Tag_On)
242                                         onpoints[j]++;
243
244                                 if(k < npoints[j] - 1 )
245                                         if( ftoutline.tags[l]   == FT_Curve_Tag_Conic &&
246                                                 ftoutline.tags[l+1] == FT_Curve_Tag_Conic)
247                                                 onpoints[j]++;
248                         }
249                 }
250
251                 //final contour loop, bezier & conic styles merged
252                 for(j = 0; j < ftoutline.n_contours; j++) {
253                         // add new curve
254                         nu  =  (Nurb*)MEM_callocN(sizeof(struct Nurb),"objfnt_nurb");
255                         bezt = (BezTriple*)MEM_callocN((onpoints[j])* sizeof(BezTriple),"objfnt_bezt") ;
256                         BLI_addtail(&vfd->nurbsbase[i], nu);
257                         nu->type= CU_BEZIER+CU_2D;
258                         nu->pntsu = onpoints[j];
259                         nu->resolu= 8;
260                         nu->flagu= 1;
261                         nu->bezt = bezt;
262
263                         //individual curve loop, start-end
264                         for(k = 0; k < npoints[j]; k++) {
265                                 if(j > 0) l = k + ftoutline.contours[j - 1] + 1; else l = k;
266                                 if(k == 0) m = l;
267                                 
268                                 //virtual conic on-curve points
269                                 if(k < npoints[j] - 1 )
270                                         if( ftoutline.tags[l] == FT_Curve_Tag_Conic && ftoutline.tags[l+1] == FT_Curve_Tag_Conic) {
271                                                 dx = (ftoutline.points[l].x + ftoutline.points[l+1].x)* scale / 2.0;
272                                                 dy = (ftoutline.points[l].y + ftoutline.points[l+1].y)* scale / 2.0;
273
274                                                 //left handle
275                                                 bezt->vec[0][0] = (dx + (2 * ftoutline.points[l].x)* scale) / 3.0;
276                                                 bezt->vec[0][1] = (dy + (2 * ftoutline.points[l].y)* scale) / 3.0;
277
278                                                 //midpoint (virtual on-curve point)
279                                                 bezt->vec[1][0] = (ftoutline.points[l].x + ftoutline.points[l+1].x)* scale / 2.0;
280                                                 bezt->vec[1][1] = (ftoutline.points[l].y + ftoutline.points[l+1].y)* scale / 2.0;
281
282                                                 //right handle
283                                                 bezt->vec[2][0] = (dx + (2 * ftoutline.points[l+1].x)* scale) / 3.0;
284                                                 bezt->vec[2][1] = (dy + (2 * ftoutline.points[l+1].y)* scale) / 3.0;
285
286                                                 bezt->h1= bezt->h2= HD_ALIGN;
287                                                 bezt++;
288                                         }
289
290                                 //on-curve points
291                                 if(ftoutline.tags[l] == FT_Curve_Tag_On) {
292                                         //left handle
293                                         if(k > 0) {
294                                                 if(ftoutline.tags[l - 1] == FT_Curve_Tag_Cubic) {
295                                                         bezt->vec[0][0] = ftoutline.points[l-1].x* scale;
296                                                         bezt->vec[0][1] = ftoutline.points[l-1].y* scale;
297                                                         bezt->h1= HD_FREE;
298                                                 } else if(ftoutline.tags[l - 1] == FT_Curve_Tag_Conic) {
299                                                         bezt->vec[0][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l - 1].x))* scale / 3.0;
300                                                         bezt->vec[0][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l - 1].y))* scale / 3.0;
301                                                         bezt->h1= HD_FREE;
302                                                 } else {
303                                                         bezt->vec[0][0] = ftoutline.points[l].x* scale - (ftoutline.points[l].x - ftoutline.points[l-1].x)* scale / 3.0;
304                                                         bezt->vec[0][1] = ftoutline.points[l].y* scale - (ftoutline.points[l].y - ftoutline.points[l-1].y)* scale / 3.0;
305                                                         bezt->h1= HD_VECT;
306                                                 }
307                                         } else { //first point on curve
308                                                 if(ftoutline.tags[ftoutline.contours[j]] == FT_Curve_Tag_Cubic) {
309                                                         bezt->vec[0][0] = ftoutline.points[ftoutline.contours[j]].x * scale;
310                                                         bezt->vec[0][1] = ftoutline.points[ftoutline.contours[j]].y * scale;
311                                                         bezt->h1= HD_FREE;
312                                                 } else if(ftoutline.tags[ftoutline.contours[j]] == FT_Curve_Tag_Conic) {
313                                                         bezt->vec[0][0] = (ftoutline.points[l].x + (2 * ftoutline.points[ftoutline.contours[j]].x))* scale / 3.0 ;
314                                                         bezt->vec[0][1] = (ftoutline.points[l].y + (2 * ftoutline.points[ftoutline.contours[j]].y))* scale / 3.0 ;
315                                                         bezt->h1= HD_FREE;
316                                                 } else {
317                                                         bezt->vec[0][0] = ftoutline.points[l].x* scale - (ftoutline.points[l].x - ftoutline.points[ftoutline.contours[j]].x)* scale / 3.0;
318                                                         bezt->vec[0][1] = ftoutline.points[l].y* scale - (ftoutline.points[l].y - ftoutline.points[ftoutline.contours[j]].y)* scale / 3.0;
319                                                         bezt->h1= HD_VECT;
320                                                 }
321                                         }
322
323                                         //midpoint (on-curve point)
324                                         bezt->vec[1][0] = ftoutline.points[l].x* scale;
325                                         bezt->vec[1][1] = ftoutline.points[l].y* scale;
326
327                                         //right handle
328                                         if(k < (npoints[j] - 1)) {
329                                                 if(ftoutline.tags[l+1] == FT_Curve_Tag_Cubic) {
330                                                         bezt->vec[2][0] = ftoutline.points[l+1].x* scale;
331                                                         bezt->vec[2][1] = ftoutline.points[l+1].y* scale;
332                                                         bezt->h2= HD_FREE;
333                                                 } else if(ftoutline.tags[l+1] == FT_Curve_Tag_Conic) {
334                                                         bezt->vec[2][0] = (ftoutline.points[l].x + (2 * ftoutline.points[l+1].x))* scale / 3.0;
335                                                         bezt->vec[2][1] = (ftoutline.points[l].y + (2 * ftoutline.points[l+1].y))* scale / 3.0;
336                                                         bezt->h2= HD_FREE;
337                                                 } else {
338                                                         bezt->vec[2][0] = ftoutline.points[l].x* scale - (ftoutline.points[l].x - ftoutline.points[l+1].x)* scale / 3.0;
339                                                         bezt->vec[2][1] = ftoutline.points[l].y* scale - (ftoutline.points[l].y - ftoutline.points[l+1].y)* scale / 3.0;
340                                                         bezt->h2= HD_VECT;
341                                                 }
342                                         } else { //last point on curve
343                                                 if(ftoutline.tags[m] == FT_Curve_Tag_Cubic) {
344 //                                                      okee("hhuh");
345                                                         bezt->vec[2][0] = ftoutline.points[m].x* scale;
346                                                         bezt->vec[2][1] = ftoutline.points[m].y* scale;
347                                                         bezt->h2= HD_FREE;
348                                                 } else if(ftoutline.tags[m] == FT_Curve_Tag_Conic) {
349                                                         bezt->vec[2][0] = (ftoutline.points[l].x + (2 * ftoutline.points[m].x))* scale / 3.0 ;
350                                                         bezt->vec[2][1] = (ftoutline.points[l].y + (2 * ftoutline.points[m].y))* scale / 3.0 ;
351                                                         bezt->h2= HD_FREE;
352                                                 } else {
353                                                         bezt->vec[2][0] = ftoutline.points[l].x* scale - (ftoutline.points[l].x - ftoutline.points[m].x)* scale / 3.0;
354                                                         bezt->vec[2][1] = ftoutline.points[l].y* scale - (ftoutline.points[l].y - ftoutline.points[m].y)* scale / 3.0;
355                                                         bezt->h2= HD_VECT;
356                                                 }
357                                         }
358
359                                         // get the handles that are aligned, tricky...
360                                         // DistVL2Dfl, check if the three beztriple points are on one line
361                                         // VecLenf, see if there's a distance between the three points
362                                         // VecLenf again, to check the angle between the handles 
363                                         // finally, check if one of them is a vector handle 
364                                         if((DistVL2Dfl(bezt->vec[0],bezt->vec[1],bezt->vec[2]) < 0.001) &&
365                                                 (VecLenf(bezt->vec[0], bezt->vec[1]) > 0.0001) &&
366                                                 (VecLenf(bezt->vec[1], bezt->vec[2]) > 0.0001) &&
367                                                 (VecLenf(bezt->vec[0], bezt->vec[2]) > 0.0002) &&
368                                                 (VecLenf(bezt->vec[0], bezt->vec[2]) > MAX2(VecLenf(bezt->vec[0], bezt->vec[1]), VecLenf(bezt->vec[1], bezt->vec[2]))) &&
369                                                 bezt->h1 != HD_VECT && bezt->h2 != HD_VECT)
370                                         {
371                                                 bezt->h1= bezt->h2= HD_ALIGN;
372                                         }
373                                         bezt++;
374                                 }
375                         }
376                 }
377
378                 if(npoints) MEM_freeN(npoints);
379                 if(onpoints) MEM_freeN(onpoints);
380         }
381         return vfd;
382 }
383
384
385 static int check_freetypefont(PackedFile * pf)
386 {
387         FT_Face                 face;
388         FT_GlyphSlot    glyph;
389         FT_UInt                 glyph_index;
390
391         int success = 0;
392
393         err = FT_New_Memory_Face( library,
394                                                         pf->data,
395                                                         pf->size,
396                                                         0,
397                                                         &face );
398         if(err) {
399                 success = 0;
400             error("This is not a valid font");
401         }
402         else {
403                 glyph_index = FT_Get_Char_Index( face, 'A' );
404                 err = FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_BITMAP );
405                 if(err) success = 0;
406                 else {
407                         glyph = face->glyph;
408                         if (glyph->format == ft_glyph_format_outline ) {
409                                 success = 1;
410                         } else {
411                                 error("Selected Font has no outline data");
412                                 success = 0;
413                         }
414                 }
415         }
416         
417         return success;
418 }
419
420
421 VFontData *BLI_vfontdata_from_freetypefont(PackedFile *pf)
422 {
423         VFontData *vfd= NULL;
424         int success = 0;
425
426         //init Freetype 
427         err = FT_Init_FreeType( &library);
428         if(err) {
429             error("Failed loading Freetype font library");
430                 return 0;
431         }
432
433         success = check_freetypefont(pf);
434         
435         if (success) {
436                 vfd= objfnt_to_ftvfontdata(pf);
437         }
438
439         //free Freetype
440         FT_Done_FreeType( library);
441         
442         return vfd;
443 }
444
445 #endif // WITH_FREETYPE2