]>
Commit | Line | Data |
---|---|---|
1 | #include "imager.h" | |
2 | #include "imrender.h" | |
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 | ||
11 | ||
12 | /* | |
13 | =head1 NAME | |
14 | ||
15 | fontft1.c - Freetype 1.x font driver for Imager | |
16 | ||
17 | =head1 SYNOPSIS | |
18 | ||
19 | handle = i_tt_new(path_to_ttf); | |
20 | rc = i_tt_bbox(handle, points, "foo", 3, int cords[6], utf8); | |
21 | i_tt_destroy(handle); | |
22 | ||
23 | // and much more | |
24 | ||
25 | =head1 DESCRIPTION | |
26 | ||
27 | fontft1.c implements font creation, rendering, bounding box functions and | |
28 | more for Imager using Freetype 1.x. | |
29 | ||
30 | In general this driver should be ignored in favour of the FT2 driver. | |
31 | ||
32 | =head1 FUNCTION REFERENCE | |
33 | ||
34 | Some of these functions are internal. | |
35 | ||
36 | =over | |
37 | ||
38 | =cut | |
39 | ||
40 | */ | |
41 | ||
42 | ||
43 | /* Truetype font support */ | |
44 | /* These are enabled by default when configuring Freetype 1.x | |
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 | |
50 | #define FTXERR18 1 | |
51 | ||
52 | #include <freetype.h> | |
53 | #define TT_CHC 5 | |
54 | ||
55 | #ifdef FTXPOST | |
56 | #include <ftxpost.h> | |
57 | #endif | |
58 | ||
59 | #ifdef FTXERR18 | |
60 | #include <ftxerr18.h> | |
61 | #endif | |
62 | ||
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 | ||
69 | static im_slot_t slot = -1; | |
70 | ||
71 | /* convert a code point into an index in the glyph cache */ | |
72 | #define TT_HASH(x) ((x) & 0xFF) | |
73 | ||
74 | typedef struct { | |
75 | int initialized; | |
76 | TT_Engine engine; | |
77 | } i_tt_engine; | |
78 | ||
79 | typedef struct i_glyph_entry_ { | |
80 | TT_Glyph glyph; | |
81 | unsigned long ch; | |
82 | } i_tt_glyph_entry; | |
83 | ||
84 | #define TT_NOCHAR (~0UL) | |
85 | ||
86 | struct 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; | |
92 | int order; | |
93 | i_img_dim ptsize; | |
94 | }; | |
95 | ||
96 | typedef struct TT_Instancehandle_ TT_Instancehandle; | |
97 | ||
98 | struct TT_Fonthandle_ { | |
99 | TT_Face face; | |
100 | TT_Face_Properties properties; | |
101 | TT_Instancehandle instanceh[TT_CHC]; | |
102 | TT_CharMap char_map; | |
103 | #ifdef FTXPOST | |
104 | int loaded_names; | |
105 | TT_Error load_cond; | |
106 | #endif | |
107 | }; | |
108 | ||
109 | /* Defines */ | |
110 | ||
111 | #define USTRCT(x) ((x).z) | |
112 | #define TT_VALID( handle ) ( ( handle ).z != NULL ) | |
113 | ||
114 | static void i_tt_push_error(TT_Error rc); | |
115 | static void i_tt_uninit(void *); | |
116 | ||
117 | /* Prototypes */ | |
118 | ||
119 | static int i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ); | |
120 | static void i_tt_init_raster_map( TT_Raster_Map* bit, i_img_dim width, i_img_dim height, int smooth ); | |
121 | static void i_tt_done_raster_map( TT_Raster_Map *bit ); | |
122 | static void i_tt_clear_raster_map( TT_Raster_Map* bit ); | |
123 | static void i_tt_blit_or( TT_Raster_Map *dst, TT_Raster_Map *src,i_img_dim x_off, i_img_dim y_off ); | |
124 | static int i_tt_get_glyph( TT_Fonthandle *handle, int inst, unsigned long j ); | |
125 | static void | |
126 | i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics, | |
127 | TT_Raster_Map *bit, TT_Raster_Map *small_bit, | |
128 | i_img_dim x_off, i_img_dim y_off, int smooth ); | |
129 | static int | |
130 | i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, | |
131 | TT_Raster_Map *small_bit, i_img_dim cords[6], | |
132 | char const* txt, size_t len, int smooth, int utf8 ); | |
133 | static 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 ); | |
134 | static 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 ); | |
135 | static int | |
136 | i_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 ); | |
138 | static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, i_img_dim cords[6], int utf8 ); | |
139 | ||
140 | ||
141 | /* static globals needed */ | |
142 | ||
143 | static int LTT_dpi = 72; /* FIXME: this ought to be a part of the call interface */ | |
144 | static int LTT_hinted = 1; /* FIXME: this too */ | |
145 | ||
146 | ||
147 | /* | |
148 | * FreeType interface | |
149 | */ | |
150 | ||
151 | void | |
152 | i_tt_start(void) { | |
153 | if (slot == -1) | |
154 | slot = im_context_slot_new(i_tt_uninit); | |
155 | } | |
156 | ||
157 | ||
158 | /* | |
159 | =item init_tt() | |
160 | ||
161 | Initializes the freetype font rendering engine (if needed) | |
162 | ||
163 | =cut | |
164 | */ | |
165 | ||
166 | static i_tt_engine * | |
167 | i_init_tt(void) { | |
168 | TT_Error error; | |
169 | im_context_t ctx = im_get_context(); | |
170 | TT_Byte palette[] = { 0, 64, 127, 191, 255 }; | |
171 | i_tt_engine *result = im_context_slot_get(ctx, slot); | |
172 | ||
173 | i_clear_error(); | |
174 | ||
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 | ||
182 | mm_log((1,"init_tt()\n")); | |
183 | ||
184 | if (result->initialized) | |
185 | return result; | |
186 | ||
187 | error = TT_Init_FreeType( &result->engine ); | |
188 | if ( error ){ | |
189 | mm_log((1,"Initialization of freetype failed, code = 0x%x\n", | |
190 | (unsigned)error)); | |
191 | i_tt_push_error(error); | |
192 | i_push_error(0, "Could not initialize freetype 1.x"); | |
193 | return NULL; | |
194 | } | |
195 | ||
196 | #ifdef FTXPOST | |
197 | error = TT_Init_Post_Extension( result->engine ); | |
198 | if (error) { | |
199 | mm_log((1, "Initialization of Post extension failed = 0x%x\n", | |
200 | (unsigned)error)); | |
201 | ||
202 | i_tt_push_error(error); | |
203 | i_push_error(0, "Could not initialize FT 1.x POST extension"); | |
204 | return NULL; | |
205 | } | |
206 | #endif | |
207 | ||
208 | error = TT_Set_Raster_Gray_Palette(result->engine, palette); | |
209 | if (error) { | |
210 | mm_log((1, "Initialization of gray levels failed = 0x%x\n", | |
211 | (unsigned)error)); | |
212 | i_tt_push_error(error); | |
213 | i_push_error(0, "Could not initialize FT 1.x POST extension"); | |
214 | return NULL; | |
215 | } | |
216 | ||
217 | mm_log((1, "initialized FT1 state %p\n", result)); | |
218 | ||
219 | result->initialized = 1; | |
220 | ||
221 | return result; | |
222 | } | |
223 | ||
224 | static void | |
225 | i_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 | } | |
235 | ||
236 | /* | |
237 | =item i_tt_get_instance(handle, points, smooth) | |
238 | ||
239 | Finds a points+smooth instance or if one doesn't exist in the cache | |
240 | allocates 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 | ||
250 | static | |
251 | int | |
252 | i_tt_get_instance( TT_Fonthandle *handle, i_img_dim points, int smooth ) { | |
253 | int i,idx; | |
254 | TT_Error error; | |
255 | ||
256 | mm_log((1,"i_tt_get_instance(handle %p, points %" i_DF ", smooth %d)\n", | |
257 | handle, i_DFc(points), smooth)); | |
258 | ||
259 | if (smooth == -1) { /* Smooth doesn't matter for this search */ | |
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 | } | |
265 | } | |
266 | smooth=1; /* We will be adding a font - add it as smooth then */ | |
267 | } else { /* Smooth doesn't matter for this search */ | |
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 | } | |
274 | } | |
275 | } | |
276 | ||
277 | /* Found the instance in the cache - return the cache index */ | |
278 | ||
279 | for(idx=0;idx<TT_CHC;idx++) { | |
280 | if (!(handle->instanceh[idx].order)) break; /* find the lru item */ | |
281 | } | |
282 | ||
283 | mm_log((1,"i_tt_get_instance: lru item is %d\n",idx)); | |
284 | mm_log((1,"i_tt_get_instance: lru pointer %p\n", | |
285 | USTRCT(handle->instanceh[idx].instance) )); | |
286 | ||
287 | if ( USTRCT(handle->instanceh[idx].instance) ) { | |
288 | mm_log((1,"i_tt_get_instance: freeing lru item from cache %d\n",idx)); | |
289 | ||
290 | /* Free cached glyphs */ | |
291 | for(i=0;i<256;i++) | |
292 | if ( USTRCT(handle->instanceh[idx].glyphs[i].glyph) ) | |
293 | TT_Done_Glyph( handle->instanceh[idx].glyphs[i].glyph ); | |
294 | ||
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 ); | |
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 ) { | |
312 | mm_log((1, "Could not create and initialize instance: error %x.\n", | |
313 | (unsigned)error )); | |
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 | ||
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 */ | |
329 | ||
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 | } | |
334 | ||
335 | return idx; | |
336 | } | |
337 | ||
338 | ||
339 | /* | |
340 | =item i_tt_new(fontname) | |
341 | ||
342 | Creates a new font handle object, finds a character map and initialise the | |
343 | the font handle's cache | |
344 | ||
345 | fontname - path to the font to load | |
346 | ||
347 | =cut | |
348 | */ | |
349 | ||
350 | TT_Fonthandle* | |
351 | i_tt_new(const char *fontname) { | |
352 | TT_Error error; | |
353 | TT_Fonthandle *handle; | |
354 | unsigned short i,n; | |
355 | unsigned short platform,encoding; | |
356 | i_tt_engine *tteng; | |
357 | ||
358 | if ((tteng = i_init_tt()) == NULL) { | |
359 | i_push_error(0, "Could not initialize FT1 engine"); | |
360 | return NULL; | |
361 | } | |
362 | ||
363 | i_clear_error(); | |
364 | ||
365 | mm_log((1,"i_tt_new(fontname '%s')\n",fontname)); | |
366 | ||
367 | /* allocate memory for the structure */ | |
368 | ||
369 | handle = mymalloc( sizeof(TT_Fonthandle) ); /* checked 5Nov05 tonyc */ | |
370 | ||
371 | /* load the typeface */ | |
372 | error = TT_Open_Face( tteng->engine, fontname, &handle->face ); | |
373 | if ( error ) { | |
374 | myfree(handle); | |
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, | |
380 | (unsigned)error )); | |
381 | } | |
382 | i_tt_push_error(error); | |
383 | return NULL; | |
384 | } | |
385 | ||
386 | TT_Get_Face_Properties( handle->face, &(handle->properties) ); | |
387 | ||
388 | /* First, look for a Unicode charmap */ | |
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 ); | |
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)); | |
398 | TT_Get_CharMap( handle->face, i, &(handle->char_map) ); | |
399 | break; | |
400 | } | |
401 | } | |
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 | } | |
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 | ||
416 | #ifdef FTXPOST | |
417 | handle->loaded_names = 0; | |
418 | #endif | |
419 | ||
420 | mm_log((1,"i_tt_new <- %p\n",handle)); | |
421 | return handle; | |
422 | } | |
423 | ||
424 | ||
425 | ||
426 | /* | |
427 |