eliminate use vars
[imager.git] / fontft1.c
CommitLineData
92bda632 1#include "imager.h"
fa16b6c6 2#include "imrender.h"
02d1d628
AMH
3
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <fcntl.h>
7
8#include <stdio.h>
9#include <stdlib.h>
10
f75c1aeb 11
02d1d628
AMH
12/*
13=head1 NAME
14
f2da6da9 15fontft1.c - Freetype 1.x font driver for Imager
02d1d628
AMH
16
17=head1 SYNOPSIS
18
02d1d628 19 handle = i_tt_new(path_to_ttf);
eeaa33fd 20 rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8);
02d1d628
AMH
21 i_tt_destroy(handle);
22
23 // and much more
24
25=head1 DESCRIPTION
26
f2da6da9
TC
27fontft1.c implements font creation, rendering, bounding box functions and
28more for Imager using Freetype 1.x.
29
30In general this driver should be ignored in favour of the FT2 driver.
02d1d628
AMH
31
32=head1 FUNCTION REFERENCE
33
34Some of these functions are internal.
35
b8c2033e 36=over
02d1d628
AMH
37
38=cut
39
40*/
41
02d1d628 42
4f68b48f 43/* Truetype font support */
d93d5c10 44/* These are enabled by default when configuring Freetype 1.x
3799c4d1
TC
45 I haven't a clue how to reliably detect it at compile time.
46
47 We need a compilation probe in Makefile.PL
48*/
49#define FTXPOST 1
d93d5c10 50#define FTXERR18 1
3799c4d1 51
4f68b48f
TC
52#include <freetype.h>
53#define TT_CHC 5
02d1d628 54
3799c4d1
TC
55#ifdef FTXPOST
56#include <ftxpost.h>
57#endif
58
d93d5c10
TC
59#ifdef FTXERR18
60#include <ftxerr18.h>
61#endif
62
a90b253d
TC
63/* some versions of FT1.x don't seem to define this - it's font defined
64 so it won't change */
65#ifndef TT_MS_LANGID_ENGLISH_GENERAL
66#define TT_MS_LANGID_ENGLISH_GENERAL 0x0409
67#endif
68
eee347a1
TC
69static im_slot_t slot = -1;
70
4f68b48f
TC
71/* convert a code point into an index in the glyph cache */
72#define TT_HASH(x) ((x) & 0xFF)
02d1d628 73
eee347a1
TC
74typedef struct {
75 int initialized;
76 TT_Engine engine;
77} i_tt_engine;
78
4f68b48f
TC
79typedef struct i_glyph_entry_ {
80 TT_Glyph glyph;
81 unsigned long ch;
82} i_tt_glyph_entry;
02d1d628 83
4f68b48f 84#define TT_NOCHAR (~0UL)
02d1d628 85
4f68b48f
TC
86struct TT_Instancehandle_ {
87 TT_Instance instance;
88 TT_Instance_Metrics imetrics;
89 TT_Glyph_Metrics gmetrics[256];
90 i_tt_glyph_entry glyphs[256];
91 int smooth;
4f68b48f 92 int order;
8d14daab 93 i_img_dim ptsize;
4f68b48f 94};
02d1d628 95
4f68b48f 96typedef struct TT_Instancehandle_ TT_Instancehandle;
02d1d628 97
4f68b48f
TC
98struct TT_Fonthandle_ {
99 TT_Face face;
100 TT_Face_Properties properties;
101 TT_Instancehandle instanceh[TT_CHC];
102 TT_CharMap char_map;
3799c4d1
TC
103#ifdef FTXPOST
104 int loaded_names;
105 TT_Error load_cond;
106#endif
4f68b48f 107};
02d1d628
AMH
108
109/* Defines */
110
111#define USTRCT(x) ((x).z)
112#define TT_VALID( handle ) ( ( handle ).z != NULL )
113
d93d5c10 114static void i_tt_push_error(TT_Error rc);
eee347a1 115static void i_tt_uninit(void *);
02d1d628
AMH
116
117/* Prototypes */
118
8d14daab
TC
119static int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth );
120static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth );
02d1d628
AMH
121static void i_tt_done_raster_map( TT_Raster_Map *bit );
122static void i_tt_clear_raster_map( TT_Raster_Map* bit );
8d14daab 123static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off );
4f68b48f
TC
124static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j );
125static void
126i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
127 TT_Raster_Map *bit, TT_Raster_Map *small_bit,
8d14daab 128 i_img_dim x_off, i_img_dim y_off, int smooth );
4f68b48f
TC
129static int
130i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
8d14daab 131 TT_Raster_Map *small_bit, i_img_dim cords[6],
718b8c97 132 char const* txt, size_t len, int smooth, int utf8 );
8d14daab
TC
133static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, const i_color *cl, int smooth );
134static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, i_img_dim xb, i_img_dim yb, int channel, int smooth );
4f68b48f 135static int
8d14daab
TC
136i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, i_img_dim cords[6],
137 double points, char const* txt, size_t len, int smooth, int utf8 );
138static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[6], int utf8 );
02d1d628
AMH
139
140
141/* static globals needed */
142
02d1d628
AMH
143static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */
144static int LTT_hinted = 1; /* FIXME: this too */
145
146
147/*
148 * FreeType interface
149 */
150
eee347a1
TC
151void
152i_tt_start(void) {
eee347a1
TC
153 if (slot == -1)
154 slot = im_context_slot_new(i_tt_uninit);
155}
156
02d1d628
AMH
157
158/*
159=item init_tt()
160
eee347a1 161Initializes the freetype font rendering engine (if needed)
02d1d628
AMH
162
163=cut
164*/
165
eee347a1 166static i_tt_engine *
d1555273 167i_init_tt(void) {
02d1d628 168 TT_Error error;
eee347a1 169 im_context_t ctx = im_get_context();
95b2bff4 170 TT_Byte palette[] = { 0, 64, 127, 191, 255 };
eee347a1 171 i_tt_engine *result = im_context_slot_get(ctx, slot);
95b2bff4 172
d1555273
TC
173 i_clear_error();
174
eee347a1
TC
175 if (result == NULL) {
176 result = mymalloc(sizeof(i_tt_engine));
177 memset(result, 0, sizeof(*result));
178 im_context_slot_set(ctx, slot, result);
179 mm_log((1, "allocated FT1 state %p\n", result));
180 }
181
02d1d628 182 mm_log((1,"init_tt()\n"));
eee347a1
TC
183
184 if (result->initialized)
185 return result;
186
187 error = TT_Init_FreeType( &result->engine );
02d1d628 188 if ( error ){
8d14daab
TC
189 mm_log((1,"Initialization of freetype failed, code = 0x%x\n",
190 (unsigned)error));
d1555273
TC
191 i_tt_push_error(error);
192 i_push_error(0, "Could not initialize freetype 1.x");
eee347a1 193 return NULL;
02d1d628 194 }
3799c4d1
TC
195
196#ifdef FTXPOST
eee347a1 197 error = TT_Init_Post_Extension( result->engine );
3799c4d1 198 if (error) {
8d14daab
TC
199 mm_log((1, "Initialization of Post extension failed = 0x%x\n",
200 (unsigned)error));
d1555273
TC
201
202 i_tt_push_error(error);
203 i_push_error(0, "Could not initialize FT 1.x POST extension");
eee347a1 204 return NULL;
3799c4d1
TC
205 }
206#endif
207
eee347a1 208 error = TT_Set_Raster_Gray_Palette(result->engine, palette);
95b2bff4 209 if (error) {
8d14daab
TC
210 mm_log((1, "Initialization of gray levels failed = 0x%x\n",
211 (unsigned)error));
d1555273
TC
212 i_tt_push_error(error);
213 i_push_error(0, "Could not initialize FT 1.x POST extension");
eee347a1 214 return NULL;
95b2bff4
TC
215 }
216
eee347a1
TC
217 mm_log((1, "initialized FT1 state %p\n", result));
218
219 result->initialized = 1;
db65c8df 220
eee347a1 221 return result;
02d1d628
AMH
222}
223
eee347a1
TC
224static void
225i_tt_uninit(void *p) {
226 i_tt_engine *tteng = p;
227
228 if (tteng->initialized) {
229 mm_log((1, "finalizing FT1 state %p\n", tteng));
230 TT_Done_FreeType(tteng->engine);
231 }
232 mm_log((1, "freeing FT1 state %p\n", tteng));
233 myfree(tteng);
234}
02d1d628
AMH
235
236/*
237=item i_tt_get_instance(handle, points, smooth)
238
239Finds a points+smooth instance or if one doesn't exist in the cache
240allocates room and returns its cache entry
241
242 fontname - path to the font to load
243 handle - handle to the font.
244 points - points of the requested font
245 smooth - boolean (True: antialias on, False: antialias is off)
246
247=cut
248*/
249
250static
251int
8d14daab 252i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) {
02d1d628
AMH
253 int i,idx;
254 TT_Error error;
255
8d14daab
TC
256 mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n",
257 handle, i_DFc(points), smooth));
02d1d628
AMH
258
259 if (smooth == -1) { /* Smooth doesn't matter for this search */
4f68b48f
TC
260 for(i=0;i<TT_CHC;i++) {
261 if (handle->instanceh[i].ptsize==points) {
262 mm_log((1,"i_tt_get_instance: in cache - (non selective smoothing search) returning %d\n",i));
263 return i;
264 }
02d1d628
AMH
265 }
266 smooth=1; /* We will be adding a font - add it as smooth then */
267 } else { /* Smooth doesn't matter for this search */
4f68b48f
TC
268 for(i=0;i<TT_CHC;i++) {
269 if (handle->instanceh[i].ptsize == points
270 && handle->instanceh[i].smooth == smooth) {
271 mm_log((1,"i_tt_get_instance: in cache returning %d\n",i));
272 return i;
273 }
02d1d628
AMH
274 }
275 }
276
277 /* Found the instance in the cache - return the cache index */
278
4f68b48f
TC
279 for(idx=0;idx<TT_CHC;idx++) {
280 if (!(handle->instanceh[idx].order)) break; /* find the lru item */
281 }
02d1d628
AMH
282
283 mm_log((1,"i_tt_get_instance: lru item is %d\n",idx));
8d14daab 284 mm_log((1,"i_tt_get_instance: lru pointer %p\n",
4f68b48f 285 USTRCT(handle->instanceh[idx].instance) ));
02d1d628
AMH
286
287 if ( USTRCT(handle->instanceh[idx].instance) ) {
288 mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx));
93d0372f 289
4f68b48f 290 /* Free cached glyphs */
93d0372f 291 for(i=0;i<256;i++)
4f68b48f
TC
292 if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) )
293 TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph );
93d0372f 294
4f68b48f
TC
295 for(i=0;i<256;i++) {
296 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
297 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
298 }
299
300 /* Free instance if needed */
301 TT_Done_Instance( handle->instanceh[idx].instance );
02d1d628
AMH
302 }
303
304 /* create and initialize instance */
305 /* FIXME: probably a memory leak on fail */
306
307 (void) (( error = TT_New_Instance( handle->face, &handle->instanceh[idx].instance ) ) ||
308 ( error = TT_Set_Instance_Resolutions( handle->instanceh[idx].instance, LTT_dpi, LTT_dpi ) ) ||
309 ( error = TT_Set_Instance_CharSize( handle->instanceh[idx].instance, points*64 ) ) );
310
311 if ( error ) {
8d14daab
TC
312 mm_log((1, "Could not create and initialize instance: error %x.\n",
313 (unsigned)error ));
02d1d628
AMH
314 return -1;
315 }
316
317 /* Now that the instance should the inplace we need to lower all of the
318 ru counts and put `this' one with the highest entry */
319
320 for(i=0;i<TT_CHC;i++) handle->instanceh[i].order--;
321
322 handle->instanceh[idx].order=TT_CHC-1;
323 handle->instanceh[idx].ptsize=points;
324 handle->instanceh[idx].smooth=smooth;
325 TT_Get_Instance_Metrics( handle->instanceh[idx].instance, &(handle->instanceh[idx].imetrics) );
326
4f68b48f
TC
327 /* Zero the memory for the glyph storage so they are not thought as
328 cached if they haven't been cached since this new font was loaded */
02d1d628 329
4f68b48f
TC
330 for(i=0;i<256;i++) {
331 handle->instanceh[idx].glyphs[i].ch = TT_NOCHAR;
332 USTRCT(handle->instanceh[idx].glyphs[i].glyph)=NULL;
333 }
02d1d628
AMH
334
335 return idx;
336}
337
338
339/*
340=item i_tt_new(fontname)
341
342Creates a new font handle object, finds a character map and initialise the
343the font handle's cache
344
345 fontname - path to the font to load
346
347=cut
348*/
349
350TT_Fonthandle*
97ac0a96 351i_tt_new(const char *fontname) {
02d1d628
AMH
352 TT_Error error;
353 TT_Fonthandle *handle;
354 unsigned short i,n;
355 unsigned short platform,encoding;
eee347a1 356 i_tt_engine *tteng;
d93d5c10 357
eee347a1 358 if ((tteng = i_init_tt()) == NULL) {
d1555273
TC
359 i_push_error(0, "Could not initialize FT1 engine");
360 return NULL;
361 }
362
d93d5c10 363 i_clear_error();
02d1d628
AMH
364
365 mm_log((1,"i_tt_new(fontname '%s')\n",fontname));
366
367 /* allocate memory for the structure */
368
f0960b14 369 handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */
02d1d628
AMH
370
371 /* load the typeface */
eee347a1 372 error = TT_Open_Face( tteng->engine, fontname, &handle->face );
02d1d628 373 if ( error ) {
3515e65f 374 myfree(handle);
4f68b48f
TC
375 if ( error == TT_Err_Could_Not_Open_File ) {
376 mm_log((1, "Could not find/open %s.\n", fontname ));
377 }
378 else {
379 mm_log((1, "Error while opening %s, error code = 0x%x.\n",fontname,
8d14daab 380 (unsigned)error ));
4f68b48f 381 }
d93d5c10 382 i_tt_push_error(error);
02d1d628
AMH
383 return NULL;
384 }
385
386 TT_Get_Face_Properties( handle->face, &(handle->properties) );
4f68b48f 387
02d1d628 388 /* First, look for a Unicode charmap */
02d1d628
AMH
389 n = handle->properties.num_CharMaps;
390 USTRCT( handle->char_map )=NULL; /* Invalidate character map */
391
392 for ( i = 0; i < n; i++ ) {
393 TT_Get_CharMap_ID( handle->face, i, &platform, &encoding );
4f68b48f
TC
394 if ( (platform == 3 && encoding == 1 )
395 || (platform == 0 && encoding == 0 ) ) {
396 mm_log((2,"i_tt_new - found char map platform %u encoding %u\n",
397 platform, encoding));
02d1d628
AMH
398 TT_Get_CharMap( handle->face, i, &(handle->char_map) );
399 break;
400 }
401 }
27e79497
TC
402 if (!USTRCT(handle->char_map) && n != 0) {
403 /* just use the first one */
404 TT_Get_CharMap( handle->face, 0, &(handle->char_map));
405 }
02d1d628
AMH
406
407 /* Zero the pointsizes - and ordering */
408
409 for(i=0;i<TT_CHC;i++) {
410 USTRCT(handle->instanceh[i].instance)=NULL;
411 handle->instanceh[i].order=i;
412 handle->instanceh[i].ptsize=0;
413 handle->instanceh[i].smooth=-1;
414 }
415
3799c4d1
TC
416#ifdef FTXPOST
417 handle->loaded_names = 0;
418#endif
419
8d14daab 420 mm_log((1,"i_tt_new <- %p\n",handle));
02d1d628
AMH
421 return handle;
422}
423
424
425
426/*
427