1 | /**************************************************************************** |
2 | * |
3 | * pshrec.c |
4 | * |
5 | * FreeType PostScript hints recorder (body). |
6 | * |
7 | * Copyright (C) 2001-2019 by |
8 | * David Turner, Robert Wilhelm, and Werner Lemberg. |
9 | * |
10 | * This file is part of the FreeType project, and may only be used, |
11 | * modified, and distributed under the terms of the FreeType project |
12 | * license, LICENSE.TXT. By continuing to use, modify, or distribute |
13 | * this file you indicate that you have read the license and |
14 | * understand and accept it fully. |
15 | * |
16 | */ |
17 | |
18 | |
19 | #include <ft2build.h> |
20 | #include FT_FREETYPE_H |
21 | #include FT_INTERNAL_OBJECTS_H |
22 | #include FT_INTERNAL_DEBUG_H |
23 | #include FT_INTERNAL_CALC_H |
24 | |
25 | #include "pshrec.h" |
26 | #include "pshalgo.h" |
27 | |
28 | #include "pshnterr.h" |
29 | |
30 | #undef FT_COMPONENT |
31 | #define FT_COMPONENT pshrec |
32 | |
33 | #ifdef DEBUG_HINTER |
34 | PS_Hints ps_debug_hints = NULL; |
35 | int ps_debug_no_horz_hints = 0; |
36 | int ps_debug_no_vert_hints = 0; |
37 | #endif |
38 | |
39 | |
40 | /*************************************************************************/ |
41 | /*************************************************************************/ |
42 | /***** *****/ |
43 | /***** PS_HINT MANAGEMENT *****/ |
44 | /***** *****/ |
45 | /*************************************************************************/ |
46 | /*************************************************************************/ |
47 | |
48 | /* destroy hints table */ |
49 | static void |
50 | ps_hint_table_done( PS_Hint_Table table, |
51 | FT_Memory memory ) |
52 | { |
53 | FT_FREE( table->hints ); |
54 | table->num_hints = 0; |
55 | table->max_hints = 0; |
56 | } |
57 | |
58 | |
59 | /* ensure that a table can contain "count" elements */ |
60 | static FT_Error |
61 | ps_hint_table_ensure( PS_Hint_Table table, |
62 | FT_UInt count, |
63 | FT_Memory memory ) |
64 | { |
65 | FT_UInt old_max = table->max_hints; |
66 | FT_UInt new_max = count; |
67 | FT_Error error = FT_Err_Ok; |
68 | |
69 | |
70 | if ( new_max > old_max ) |
71 | { |
72 | /* try to grow the table */ |
73 | new_max = FT_PAD_CEIL( new_max, 8 ); |
74 | if ( !FT_RENEW_ARRAY( table->hints, old_max, new_max ) ) |
75 | table->max_hints = new_max; |
76 | } |
77 | return error; |
78 | } |
79 | |
80 | |
81 | static FT_Error |
82 | ps_hint_table_alloc( PS_Hint_Table table, |
83 | FT_Memory memory, |
84 | PS_Hint *ahint ) |
85 | { |
86 | FT_Error error = FT_Err_Ok; |
87 | FT_UInt count; |
88 | PS_Hint hint = NULL; |
89 | |
90 | |
91 | count = table->num_hints; |
92 | count++; |
93 | |
94 | if ( count >= table->max_hints ) |
95 | { |
96 | error = ps_hint_table_ensure( table, count, memory ); |
97 | if ( error ) |
98 | goto Exit; |
99 | } |
100 | |
101 | hint = table->hints + count - 1; |
102 | hint->pos = 0; |
103 | hint->len = 0; |
104 | hint->flags = 0; |
105 | |
106 | table->num_hints = count; |
107 | |
108 | Exit: |
109 | *ahint = hint; |
110 | return error; |
111 | } |
112 | |
113 | |
114 | /*************************************************************************/ |
115 | /*************************************************************************/ |
116 | /***** *****/ |
117 | /***** PS_MASK MANAGEMENT *****/ |
118 | /***** *****/ |
119 | /*************************************************************************/ |
120 | /*************************************************************************/ |
121 | |
122 | /* destroy mask */ |
123 | static void |
124 | ps_mask_done( PS_Mask mask, |
125 | FT_Memory memory ) |
126 | { |
127 | FT_FREE( mask->bytes ); |
128 | mask->num_bits = 0; |
129 | mask->max_bits = 0; |
130 | mask->end_point = 0; |
131 | } |
132 | |
133 | |
134 | /* ensure that a mask can contain "count" bits */ |
135 | static FT_Error |
136 | ps_mask_ensure( PS_Mask mask, |
137 | FT_UInt count, |
138 | FT_Memory memory ) |
139 | { |
140 | FT_UInt old_max = ( mask->max_bits + 7 ) >> 3; |
141 | FT_UInt new_max = ( count + 7 ) >> 3; |
142 | FT_Error error = FT_Err_Ok; |
143 | |
144 | |
145 | if ( new_max > old_max ) |
146 | { |
147 | new_max = FT_PAD_CEIL( new_max, 8 ); |
148 | if ( !FT_RENEW_ARRAY( mask->bytes, old_max, new_max ) ) |
149 | mask->max_bits = new_max * 8; |
150 | } |
151 | return error; |
152 | } |
153 | |
154 | |
155 | /* test a bit value in a given mask */ |
156 | static FT_Int |
157 | ps_mask_test_bit( PS_Mask mask, |
158 | FT_Int idx ) |
159 | { |
160 | if ( (FT_UInt)idx >= mask->num_bits ) |
161 | return 0; |
162 | |
163 | return mask->bytes[idx >> 3] & ( 0x80 >> ( idx & 7 ) ); |
164 | } |
165 | |
166 | |
167 | /* clear a given bit */ |
168 | static void |
169 | ps_mask_clear_bit( PS_Mask mask, |
170 | FT_UInt idx ) |
171 | { |
172 | FT_Byte* p; |
173 | |
174 | |
175 | if ( idx >= mask->num_bits ) |
176 | return; |
177 | |
178 | p = mask->bytes + ( idx >> 3 ); |
179 | p[0] = (FT_Byte)( p[0] & ~( 0x80 >> ( idx & 7 ) ) ); |
180 | } |
181 | |
182 | |
183 | /* set a given bit, possibly grow the mask */ |
184 | static FT_Error |
185 | ps_mask_set_bit( PS_Mask mask, |
186 | FT_UInt idx, |
187 | FT_Memory memory ) |
188 | { |
189 | FT_Error error = FT_Err_Ok; |
190 | FT_Byte* p; |
191 | |
192 | |
193 | if ( idx >= mask->num_bits ) |
194 | { |
195 | error = ps_mask_ensure( mask, idx + 1, memory ); |
196 | if ( error ) |
197 | goto Exit; |
198 | |
199 | mask->num_bits = idx + 1; |
200 | } |
201 | |
202 | p = mask->bytes + ( idx >> 3 ); |
203 | p[0] = (FT_Byte)( p[0] | ( 0x80 >> ( idx & 7 ) ) ); |
204 | |
205 | Exit: |
206 | return error; |
207 | } |
208 | |
209 | |
210 | /* destroy mask table */ |
211 | static void |
212 | ps_mask_table_done( PS_Mask_Table table, |
213 | FT_Memory memory ) |
214 | { |
215 | FT_UInt count = table->max_masks; |
216 | PS_Mask mask = table->masks; |
217 | |
218 | |
219 | for ( ; count > 0; count--, mask++ ) |
220 | ps_mask_done( mask, memory ); |
221 | |
222 | FT_FREE( table->masks ); |
223 | table->num_masks = 0; |
224 | table->max_masks = 0; |
225 | } |
226 | |
227 | |
228 | /* ensure that a mask table can contain "count" masks */ |
229 | static FT_Error |
230 | ps_mask_table_ensure( PS_Mask_Table table, |
231 | FT_UInt count, |
232 | FT_Memory memory ) |
233 | { |
234 | FT_UInt old_max = table->max_masks; |
235 | FT_UInt new_max = count; |
236 | FT_Error error = FT_Err_Ok; |
237 | |
238 | |
239 | if ( new_max > old_max ) |
240 | { |
241 | new_max = FT_PAD_CEIL( new_max, 8 ); |
242 | if ( !FT_RENEW_ARRAY( table->masks, old_max, new_max ) ) |
243 | table->max_masks = new_max; |
244 | } |
245 | return error; |
246 | } |
247 | |
248 | |
249 | /* allocate a new mask in a table */ |
250 | static FT_Error |
251 | ps_mask_table_alloc( PS_Mask_Table table, |
252 | FT_Memory memory, |
253 | PS_Mask *amask ) |
254 | { |
255 | FT_UInt count; |
256 | FT_Error error = FT_Err_Ok; |
257 | PS_Mask mask = NULL; |
258 | |
259 | |
260 | count = table->num_masks; |
261 | count++; |
262 | |
263 | if ( count > table->max_masks ) |
264 | { |
265 | error = ps_mask_table_ensure( table, count, memory ); |
266 | if ( error ) |
267 | goto Exit; |
268 | } |
269 | |
270 | mask = table->masks + count - 1; |
271 | mask->num_bits = 0; |
272 | mask->end_point = 0; |
273 | table->num_masks = count; |
274 | |
275 | Exit: |
276 | *amask = mask; |
277 | return error; |
278 | } |
279 | |
280 | |
281 | /* return last hint mask in a table, create one if the table is empty */ |
282 | static FT_Error |
283 | ps_mask_table_last( PS_Mask_Table table, |
284 | FT_Memory memory, |
285 | PS_Mask *amask ) |
286 | { |
287 | FT_Error error = FT_Err_Ok; |
288 | FT_UInt count; |
289 | PS_Mask mask; |
290 | |
291 | |
292 | count = table->num_masks; |
293 | if ( count == 0 ) |
294 | { |
295 | error = ps_mask_table_alloc( table, memory, &mask ); |
296 | if ( error ) |
297 | goto Exit; |
298 | } |
299 | else |
300 | mask = table->masks + count - 1; |
301 | |
302 | Exit: |
303 | *amask = mask; |
304 | return error; |
305 | } |
306 | |
307 | |
308 | /* set a new mask to a given bit range */ |
309 | static FT_Error |
310 | ps_mask_table_set_bits( PS_Mask_Table table, |
311 | const FT_Byte* source, |
312 | FT_UInt bit_pos, |
313 | FT_UInt bit_count, |
314 | FT_Memory memory ) |
315 | { |
316 | FT_Error error; |
317 | PS_Mask mask; |
318 | |
319 | |
320 | error = ps_mask_table_last( table, memory, &mask ); |
321 | if ( error ) |
322 | goto Exit; |
323 | |
324 | error = ps_mask_ensure( mask, bit_count, memory ); |
325 | if ( error ) |
326 | goto Exit; |
327 | |
328 | mask->num_bits = bit_count; |
329 | |
330 | /* now, copy bits */ |
331 | { |
332 | FT_Byte* read = (FT_Byte*)source + ( bit_pos >> 3 ); |
333 | FT_Int rmask = 0x80 >> ( bit_pos & 7 ); |
334 | FT_Byte* write = mask->bytes; |
335 | FT_Int wmask = 0x80; |
336 | FT_Int val; |
337 | |
338 | |
339 | for ( ; bit_count > 0; bit_count-- ) |
340 | { |
341 | val = write[0] & ~wmask; |
342 | |
343 | if ( read[0] & rmask ) |
344 | val |= wmask; |
345 | |
346 | write[0] = (FT_Byte)val; |
347 | |
348 | rmask >>= 1; |
349 | if ( rmask == 0 ) |
350 | { |
351 | read++; |
352 | rmask = 0x80; |
353 | } |
354 | |
355 | wmask >>= 1; |
356 | if ( wmask == 0 ) |
357 | { |
358 | write++; |
359 | wmask = 0x80; |
360 | } |
361 | } |
362 | } |
363 | |
364 | Exit: |
365 | return error; |
366 | } |
367 | |
368 | |
369 | /* test whether two masks in a table intersect */ |
370 | static FT_Int |
371 | ps_mask_table_test_intersect( PS_Mask_Table table, |
372 | FT_UInt index1, |
373 | FT_UInt index2 ) |
374 | { |
375 | PS_Mask mask1 = table->masks + index1; |
376 | PS_Mask mask2 = table->masks + index2; |
377 | FT_Byte* p1 = mask1->bytes; |
378 | FT_Byte* p2 = mask2->bytes; |
379 | FT_UInt count1 = mask1->num_bits; |
380 | FT_UInt count2 = mask2->num_bits; |
381 | FT_UInt count; |
382 | |
383 | |
384 | count = FT_MIN( count1, count2 ); |
385 | for ( ; count >= 8; count -= 8 ) |
386 | { |
387 | if ( p1[0] & p2[0] ) |
388 | return 1; |
389 | |
390 | p1++; |
391 | p2++; |
392 | } |
393 | |
394 | if ( count == 0 ) |
395 | return 0; |
396 | |
397 | return ( p1[0] & p2[0] ) & ~( 0xFF >> count ); |
398 | } |
399 | |
400 | |
401 | /* merge two masks, used by ps_mask_table_merge_all */ |
402 | static FT_Error |
403 | ps_mask_table_merge( PS_Mask_Table table, |
404 | FT_UInt index1, |
405 | FT_UInt index2, |
406 | FT_Memory memory ) |
407 | { |
408 | FT_Error error = FT_Err_Ok; |
409 | |
410 | |
411 | /* swap index1 and index2 so that index1 < index2 */ |
412 | if ( index1 > index2 ) |
413 | { |
414 | FT_UInt temp; |
415 | |
416 | |
417 | temp = index1; |
418 | index1 = index2; |
419 | index2 = temp; |
420 | } |
421 | |
422 | if ( index1 < index2 && index2 < table->num_masks ) |
423 | { |
424 | /* we need to merge the bitsets of index1 and index2 with a */ |
425 | /* simple union */ |
426 | PS_Mask mask1 = table->masks + index1; |
427 | PS_Mask mask2 = table->masks + index2; |
428 | FT_UInt count1 = mask1->num_bits; |
429 | FT_UInt count2 = mask2->num_bits; |
430 | FT_Int delta; |
431 | |
432 | |
433 | if ( count2 > 0 ) |
434 | { |
435 | FT_UInt pos; |
436 | FT_Byte* read; |
437 | FT_Byte* write; |
438 | |
439 | |
440 | /* if "count2" is greater than "count1", we need to grow the */ |
441 | /* first bitset, and clear the highest bits */ |
442 | if ( count2 > count1 ) |
443 | { |
444 | error = ps_mask_ensure( mask1, count2, memory ); |
445 | if ( error ) |
446 | goto Exit; |
447 | |
448 | for ( pos = count1; pos < count2; pos++ ) |
449 | ps_mask_clear_bit( mask1, pos ); |
450 | } |
451 | |
452 | /* merge (unite) the bitsets */ |
453 | read = mask2->bytes; |
454 | write = mask1->bytes; |
455 | pos = ( count2 + 7 ) >> 3; |
456 | |
457 | for ( ; pos > 0; pos-- ) |
458 | { |
459 | write[0] = (FT_Byte)( write[0] | read[0] ); |
460 | write++; |
461 | read++; |
462 | } |
463 | } |
464 | |
465 | /* Now, remove "mask2" from the list. We need to keep the masks */ |
466 | /* sorted in order of importance, so move table elements. */ |
467 | mask2->num_bits = 0; |
468 | mask2->end_point = 0; |
469 | |
470 | /* number of masks to move */ |
471 | delta = (FT_Int)( table->num_masks - 1 - index2 ); |
472 | if ( delta > 0 ) |
473 | { |
474 | /* move to end of table for reuse */ |
475 | PS_MaskRec dummy = *mask2; |
476 | |
477 | |
478 | ft_memmove( mask2, |
479 | mask2 + 1, |
480 | (FT_UInt)delta * sizeof ( PS_MaskRec ) ); |
481 | |
482 | mask2[delta] = dummy; |
483 | } |
484 | |
485 | table->num_masks--; |
486 | } |
487 | else |
488 | FT_TRACE0(( "ps_mask_table_merge: ignoring invalid indices (%d,%d)\n" , |
489 | index1, index2 )); |
490 | |
491 | Exit: |
492 | return error; |
493 | } |
494 | |
495 | |
496 | /* Try to merge all masks in a given table. This is used to merge */ |
497 | /* all counter masks into independent counter "paths". */ |
498 | /* */ |
499 | static FT_Error |
500 | ps_mask_table_merge_all( PS_Mask_Table table, |
501 | FT_Memory memory ) |
502 | { |
503 | FT_Int index1, index2; |
504 | FT_Error error = FT_Err_Ok; |
505 | |
506 | |
507 | /* both loops go down to 0, thus FT_Int for index1 and index2 */ |
508 | for ( index1 = (FT_Int)table->num_masks - 1; index1 > 0; index1-- ) |
509 | { |
510 | for ( index2 = index1 - 1; index2 >= 0; index2-- ) |
511 | { |
512 | if ( ps_mask_table_test_intersect( table, |
513 | (FT_UInt)index1, |
514 | (FT_UInt)index2 ) ) |
515 | { |
516 | error = ps_mask_table_merge( table, |
517 | (FT_UInt)index2, |
518 | (FT_UInt)index1, |
519 | memory ); |
520 | if ( error ) |
521 | goto Exit; |
522 | |
523 | break; |
524 | } |
525 | } |
526 | } |
527 | |
528 | Exit: |
529 | return error; |
530 | } |
531 | |
532 | |
533 | /*************************************************************************/ |
534 | /*************************************************************************/ |
535 | /***** *****/ |
536 | /***** PS_DIMENSION MANAGEMENT *****/ |
537 | /***** *****/ |
538 | /*************************************************************************/ |
539 | /*************************************************************************/ |
540 | |
541 | |
542 | /* finalize a given dimension */ |
543 | static void |
544 | ps_dimension_done( PS_Dimension dimension, |
545 | FT_Memory memory ) |
546 | { |
547 | ps_mask_table_done( &dimension->counters, memory ); |
548 | ps_mask_table_done( &dimension->masks, memory ); |
549 | ps_hint_table_done( &dimension->hints, memory ); |
550 | } |
551 | |
552 | |
553 | /* initialize a given dimension */ |
554 | static void |
555 | ps_dimension_init( PS_Dimension dimension ) |
556 | { |
557 | dimension->hints.num_hints = 0; |
558 | dimension->masks.num_masks = 0; |
559 | dimension->counters.num_masks = 0; |
560 | } |
561 | |
562 | |
563 | #if 0 |
564 | |
565 | /* set a bit at a given index in the current hint mask */ |
566 | static FT_Error |
567 | ps_dimension_set_mask_bit( PS_Dimension dim, |
568 | FT_UInt idx, |
569 | FT_Memory memory ) |
570 | { |
571 | PS_Mask mask; |
572 | FT_Error error = FT_Err_Ok; |
573 | |
574 | |
575 | /* get last hint mask */ |
576 | error = ps_mask_table_last( &dim->masks, memory, &mask ); |
577 | if ( error ) |
578 | goto Exit; |
579 | |
580 | error = ps_mask_set_bit( mask, idx, memory ); |
581 | |
582 | Exit: |
583 | return error; |
584 | } |
585 | |
586 | #endif |
587 | |
588 | /* set the end point in a mask, called from "End" & "Reset" methods */ |
589 | static void |
590 | ps_dimension_end_mask( PS_Dimension dim, |
591 | FT_UInt end_point ) |
592 | { |
593 | FT_UInt count = dim->masks.num_masks; |
594 | |
595 | |
596 | if ( count > 0 ) |
597 | { |
598 | PS_Mask mask = dim->masks.masks + count - 1; |
599 | |
600 | |
601 | mask->end_point = end_point; |
602 | } |
603 | } |
604 | |
605 | |
606 | /* set the end point in the current mask, then create a new empty one */ |
607 | /* (called by "Reset" method) */ |
608 | static FT_Error |
609 | ps_dimension_reset_mask( PS_Dimension dim, |
610 | FT_UInt end_point, |
611 | FT_Memory memory ) |
612 | { |
613 | PS_Mask mask; |
614 | |
615 | |
616 | /* end current mask */ |
617 | ps_dimension_end_mask( dim, end_point ); |
618 | |
619 | /* allocate new one */ |
620 | return ps_mask_table_alloc( &dim->masks, memory, &mask ); |
621 | } |
622 | |
623 | |
624 | /* set a new mask, called from the "T2Stem" method */ |
625 | static FT_Error |
626 | ps_dimension_set_mask_bits( PS_Dimension dim, |
627 | const FT_Byte* source, |
628 | FT_UInt source_pos, |
629 | FT_UInt source_bits, |
630 | FT_UInt end_point, |
631 | FT_Memory memory ) |
632 | { |
633 | FT_Error error; |
634 | |
635 | |
636 | /* reset current mask, if any */ |
637 | error = ps_dimension_reset_mask( dim, end_point, memory ); |
638 | if ( error ) |
639 | goto Exit; |
640 | |
641 | /* set bits in new mask */ |
642 | error = ps_mask_table_set_bits( &dim->masks, source, |
643 | source_pos, source_bits, memory ); |
644 | |
645 | Exit: |
646 | return error; |
647 | } |
648 | |
649 | |
650 | /* add a new single stem (called from "T1Stem" method) */ |
651 | static FT_Error |
652 | ps_dimension_add_t1stem( PS_Dimension dim, |
653 | FT_Int pos, |
654 | FT_Int len, |
655 | FT_Memory memory, |
656 | FT_Int *aindex ) |
657 | { |
658 | FT_Error error = FT_Err_Ok; |
659 | FT_UInt flags = 0; |
660 | |
661 | |
662 | /* detect ghost stem */ |
663 | if ( len < 0 ) |
664 | { |
665 | flags |= PS_HINT_FLAG_GHOST; |
666 | if ( len == -21 ) |
667 | { |
668 | flags |= PS_HINT_FLAG_BOTTOM; |
669 | pos = ADD_INT( pos, len ); |
670 | } |
671 | len = 0; |
672 | } |
673 | |
674 | if ( aindex ) |
675 | *aindex = -1; |
676 | |
677 | /* now, lookup stem in the current hints table */ |
678 | { |
679 | PS_Mask mask; |
680 | FT_UInt idx; |
681 | FT_UInt max = dim->hints.num_hints; |
682 | PS_Hint hint = dim->hints.hints; |
683 | |
684 | |
685 | for ( idx = 0; idx < max; idx++, hint++ ) |
686 | { |
687 | if ( hint->pos == pos && hint->len == len ) |
688 | break; |
689 | } |
690 | |
691 | /* we need to create a new hint in the table */ |
692 | if ( idx >= max ) |
693 | { |
694 | error = ps_hint_table_alloc( &dim->hints, memory, &hint ); |
695 | if ( error ) |
696 | goto Exit; |
697 | |
698 | hint->pos = pos; |
699 | hint->len = len; |
700 | hint->flags = flags; |
701 | } |
702 | |
703 | /* now, store the hint in the current mask */ |
704 | error = ps_mask_table_last( &dim->masks, memory, &mask ); |
705 | if ( error ) |
706 | goto Exit; |
707 | |
708 | error = ps_mask_set_bit( mask, idx, memory ); |
709 | if ( error ) |
710 | goto Exit; |
711 | |
712 | if ( aindex ) |
713 | *aindex = (FT_Int)idx; |
714 | } |
715 | |
716 | Exit: |
717 | return error; |
718 | } |
719 | |
720 | |
721 | /* add a "hstem3/vstem3" counter to our dimension table */ |
722 | static FT_Error |
723 | ps_dimension_add_counter( PS_Dimension dim, |
724 | FT_Int hint1, |
725 | FT_Int hint2, |
726 | FT_Int hint3, |
727 | FT_Memory memory ) |
728 | { |
729 | FT_Error error = FT_Err_Ok; |
730 | FT_UInt count = dim->counters.num_masks; |
731 | PS_Mask counter = dim->counters.masks; |
732 | |
733 | |
734 | /* try to find an existing counter mask that already uses */ |
735 | /* one of these stems here */ |
736 | for ( ; count > 0; count--, counter++ ) |
737 | { |
738 | if ( ps_mask_test_bit( counter, hint1 ) || |
739 | ps_mask_test_bit( counter, hint2 ) || |
740 | ps_mask_test_bit( counter, hint3 ) ) |
741 | break; |
742 | } |
743 | |
744 | /* create a new counter when needed */ |
745 | if ( count == 0 ) |
746 | { |
747 | error = ps_mask_table_alloc( &dim->counters, memory, &counter ); |
748 | if ( error ) |
749 | goto Exit; |
750 | } |
751 | |
752 | /* now, set the bits for our hints in the counter mask */ |
753 | if ( hint1 >= 0 ) |
754 | { |
755 | error = ps_mask_set_bit( counter, (FT_UInt)hint1, memory ); |
756 | if ( error ) |
757 | goto Exit; |
758 | } |
759 | |
760 | if ( hint2 >= 0 ) |
761 | { |
762 | error = ps_mask_set_bit( counter, (FT_UInt)hint2, memory ); |
763 | if ( error ) |
764 | goto Exit; |
765 | } |
766 | |
767 | if ( hint3 >= 0 ) |
768 | { |
769 | error = ps_mask_set_bit( counter, (FT_UInt)hint3, memory ); |
770 | if ( error ) |
771 | goto Exit; |
772 | } |
773 | |
774 | Exit: |
775 | return error; |
776 | } |
777 | |
778 | |
779 | /* end of recording session for a given dimension */ |
780 | static FT_Error |
781 | ps_dimension_end( PS_Dimension dim, |
782 | FT_UInt end_point, |
783 | FT_Memory memory ) |
784 | { |
785 | /* end hint mask table */ |
786 | ps_dimension_end_mask( dim, end_point ); |
787 | |
788 | /* merge all counter masks into independent "paths" */ |
789 | return ps_mask_table_merge_all( &dim->counters, memory ); |
790 | } |
791 | |
792 | |
793 | /*************************************************************************/ |
794 | /*************************************************************************/ |
795 | /***** *****/ |
796 | /***** PS_RECORDER MANAGEMENT *****/ |
797 | /***** *****/ |
798 | /*************************************************************************/ |
799 | /*************************************************************************/ |
800 | |
801 | |
802 | /* destroy hints */ |
803 | FT_LOCAL( void ) |
804 | ps_hints_done( PS_Hints hints ) |
805 | { |
806 | FT_Memory memory = hints->memory; |
807 | |
808 | |
809 | ps_dimension_done( &hints->dimension[0], memory ); |
810 | ps_dimension_done( &hints->dimension[1], memory ); |
811 | |
812 | hints->error = FT_Err_Ok; |
813 | hints->memory = NULL; |
814 | } |
815 | |
816 | |
817 | FT_LOCAL( void ) |
818 | ps_hints_init( PS_Hints hints, |
819 | FT_Memory memory ) |
820 | { |
821 | FT_ZERO( hints ); |
822 | hints->memory = memory; |
823 | } |
824 | |
825 | |
826 | /* initialize a hints for a new session */ |
827 | static void |
828 | ps_hints_open( PS_Hints hints, |
829 | PS_Hint_Type hint_type ) |
830 | { |
831 | hints->error = FT_Err_Ok; |
832 | hints->hint_type = hint_type; |
833 | |
834 | ps_dimension_init( &hints->dimension[0] ); |
835 | ps_dimension_init( &hints->dimension[1] ); |
836 | } |
837 | |
838 | |
839 | /* add one or more stems to the current hints table */ |
840 | static void |
841 | ps_hints_stem( PS_Hints hints, |
842 | FT_UInt dimension, |
843 | FT_Int count, |
844 | FT_Long* stems ) |
845 | { |
846 | PS_Dimension dim; |
847 | |
848 | |
849 | if ( hints->error ) |
850 | return; |
851 | |
852 | /* limit "dimension" to 0..1 */ |
853 | if ( dimension > 1 ) |
854 | { |
855 | FT_TRACE0(( "ps_hints_stem: invalid dimension (%d) used\n" , |
856 | dimension )); |
857 | dimension = ( dimension != 0 ); |
858 | } |
859 | |
860 | /* record the stems in the current hints/masks table */ |
861 | /* (Type 1 & 2's `hstem' or `vstem' operators) */ |
862 | dim = &hints->dimension[dimension]; |
863 | |
864 | for ( ; count > 0; count--, stems += 2 ) |
865 | { |
866 | FT_Error error; |
867 | FT_Memory memory = hints->memory; |
868 | |
869 | |
870 | error = ps_dimension_add_t1stem( dim, |
871 | (FT_Int)stems[0], |
872 | (FT_Int)stems[1], |
873 | memory, |
874 | NULL ); |
875 | if ( error ) |
876 | { |
877 | FT_ERROR(( "ps_hints_stem: could not add stem" |
878 | " (%d,%d) to hints table\n" , stems[0], stems[1] )); |
879 | |
880 | hints->error = error; |
881 | return; |
882 | } |
883 | } |
884 | } |
885 | |
886 | |
887 | /* add one Type1 counter stem to the current hints table */ |
888 | static void |
889 | ps_hints_t1stem3( PS_Hints hints, |
890 | FT_UInt dimension, |
891 | FT_Fixed* stems ) |
892 | { |
893 | FT_Error error = FT_Err_Ok; |
894 | |
895 | |
896 | if ( !hints->error ) |
897 | { |
898 | PS_Dimension dim; |
899 | FT_Memory memory = hints->memory; |
900 | FT_Int count; |
901 | FT_Int idx[3]; |
902 | |
903 | |
904 | /* limit "dimension" to 0..1 */ |
905 | if ( dimension > 1 ) |
906 | { |
907 | FT_TRACE0(( "ps_hints_t1stem3: invalid dimension (%d) used\n" , |
908 | dimension )); |
909 | dimension = ( dimension != 0 ); |
910 | } |
911 | |
912 | dim = &hints->dimension[dimension]; |
913 | |
914 | /* there must be 6 elements in the 'stem' array */ |
915 | if ( hints->hint_type == PS_HINT_TYPE_1 ) |
916 | { |
917 | /* add the three stems to our hints/masks table */ |
918 | for ( count = 0; count < 3; count++, stems += 2 ) |
919 | { |
920 | error = ps_dimension_add_t1stem( dim, |
921 | (FT_Int)FIXED_TO_INT( stems[0] ), |
922 | (FT_Int)FIXED_TO_INT( stems[1] ), |
923 | memory, &idx[count] ); |
924 | if ( error ) |
925 | goto Fail; |
926 | } |
927 | |
928 | /* now, add the hints to the counters table */ |
929 | error = ps_dimension_add_counter( dim, idx[0], idx[1], idx[2], |
930 | memory ); |
931 | if ( error ) |
932 | goto Fail; |
933 | } |
934 | else |
935 | { |
936 | FT_ERROR(( "ps_hints_t1stem3: called with invalid hint type\n" )); |
937 | error = FT_THROW( Invalid_Argument ); |
938 | goto Fail; |
939 | } |
940 | } |
941 | |
942 | return; |
943 | |
944 | Fail: |
945 | FT_ERROR(( "ps_hints_t1stem3: could not add counter stems to table\n" )); |
946 | hints->error = error; |
947 | } |
948 | |
949 | |
950 | /* reset hints (only with Type 1 hints) */ |
951 | static void |
952 | ps_hints_t1reset( PS_Hints hints, |
953 | FT_UInt end_point ) |
954 | { |
955 | FT_Error error = FT_Err_Ok; |
956 | |
957 | |
958 | if ( !hints->error ) |
959 | { |
960 | FT_Memory memory = hints->memory; |
961 | |
962 | |
963 | if ( hints->hint_type == PS_HINT_TYPE_1 ) |
964 | { |
965 | error = ps_dimension_reset_mask( &hints->dimension[0], |
966 | end_point, memory ); |
967 | if ( error ) |
968 | goto Fail; |
969 | |
970 | error = ps_dimension_reset_mask( &hints->dimension[1], |
971 | end_point, memory ); |
972 | if ( error ) |
973 | goto Fail; |
974 | } |
975 | else |
976 | { |
977 | /* invalid hint type */ |
978 | error = FT_THROW( Invalid_Argument ); |
979 | goto Fail; |
980 | } |
981 | } |
982 | return; |
983 | |
984 | Fail: |
985 | hints->error = error; |
986 | } |
987 | |
988 | |
989 | /* Type2 "hintmask" operator, add a new hintmask to each direction */ |
990 | static void |
991 | ps_hints_t2mask( PS_Hints hints, |
992 | FT_UInt end_point, |
993 | FT_UInt bit_count, |
994 | const FT_Byte* bytes ) |
995 | { |
996 | FT_Error error; |
997 | |
998 | |
999 | if ( !hints->error ) |
1000 | { |
1001 | PS_Dimension dim = hints->dimension; |
1002 | FT_Memory memory = hints->memory; |
1003 | FT_UInt count1 = dim[0].hints.num_hints; |
1004 | FT_UInt count2 = dim[1].hints.num_hints; |
1005 | |
1006 | |
1007 | /* check bit count; must be equal to current total hint count */ |
1008 | if ( bit_count != count1 + count2 ) |
1009 | { |
1010 | FT_TRACE0(( "ps_hints_t2mask:" |
1011 | " called with invalid bitcount %d (instead of %d)\n" , |
1012 | bit_count, count1 + count2 )); |
1013 | |
1014 | /* simply ignore the operator */ |
1015 | return; |
1016 | } |
1017 | |
1018 | /* set-up new horizontal and vertical hint mask now */ |
1019 | error = ps_dimension_set_mask_bits( &dim[0], bytes, count2, count1, |
1020 | end_point, memory ); |
1021 | if ( error ) |
1022 | goto Fail; |
1023 | |
1024 | error = ps_dimension_set_mask_bits( &dim[1], bytes, 0, count2, |
1025 | end_point, memory ); |
1026 | if ( error ) |
1027 | goto Fail; |
1028 | } |
1029 | return; |
1030 | |
1031 | Fail: |
1032 | hints->error = error; |
1033 | } |
1034 | |
1035 | |
1036 | static void |
1037 | ps_hints_t2counter( PS_Hints hints, |
1038 | FT_UInt bit_count, |
1039 | const FT_Byte* bytes ) |
1040 | { |
1041 | FT_Error error; |
1042 | |
1043 | |
1044 | if ( !hints->error ) |
1045 | { |
1046 | PS_Dimension dim = hints->dimension; |
1047 | FT_Memory memory = hints->memory; |
1048 | FT_UInt count1 = dim[0].hints.num_hints; |
1049 | FT_UInt count2 = dim[1].hints.num_hints; |
1050 | |
1051 | |
1052 | /* check bit count, must be equal to current total hint count */ |
1053 | if ( bit_count != count1 + count2 ) |
1054 | { |
1055 | FT_TRACE0(( "ps_hints_t2counter:" |
1056 | " called with invalid bitcount %d (instead of %d)\n" , |
1057 | bit_count, count1 + count2 )); |
1058 | |
1059 | /* simply ignore the operator */ |
1060 | return; |
1061 | } |
1062 | |
1063 | /* set-up new horizontal and vertical hint mask now */ |
1064 | error = ps_dimension_set_mask_bits( &dim[0], bytes, 0, count1, |
1065 | 0, memory ); |
1066 | if ( error ) |
1067 | goto Fail; |
1068 | |
1069 | error = ps_dimension_set_mask_bits( &dim[1], bytes, count1, count2, |
1070 | 0, memory ); |
1071 | if ( error ) |
1072 | goto Fail; |
1073 | } |
1074 | return; |
1075 | |
1076 | Fail: |
1077 | hints->error = error; |
1078 | } |
1079 | |
1080 | |
1081 | /* end recording session */ |
1082 | static FT_Error |
1083 | ps_hints_close( PS_Hints hints, |
1084 | FT_UInt end_point ) |
1085 | { |
1086 | FT_Error error; |
1087 | |
1088 | |
1089 | error = hints->error; |
1090 | if ( !error ) |
1091 | { |
1092 | FT_Memory memory = hints->memory; |
1093 | PS_Dimension dim = hints->dimension; |
1094 | |
1095 | |
1096 | error = ps_dimension_end( &dim[0], end_point, memory ); |
1097 | if ( !error ) |
1098 | { |
1099 | error = ps_dimension_end( &dim[1], end_point, memory ); |
1100 | } |
1101 | } |
1102 | |
1103 | #ifdef DEBUG_HINTER |
1104 | if ( !error ) |
1105 | ps_debug_hints = hints; |
1106 | #endif |
1107 | return error; |
1108 | } |
1109 | |
1110 | |
1111 | /*************************************************************************/ |
1112 | /*************************************************************************/ |
1113 | /***** *****/ |
1114 | /***** TYPE 1 HINTS RECORDING INTERFACE *****/ |
1115 | /***** *****/ |
1116 | /*************************************************************************/ |
1117 | /*************************************************************************/ |
1118 | |
1119 | static void |
1120 | t1_hints_open( T1_Hints hints ) |
1121 | { |
1122 | ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_1 ); |
1123 | } |
1124 | |
1125 | static void |
1126 | t1_hints_stem( T1_Hints hints, |
1127 | FT_UInt dimension, |
1128 | FT_Fixed* coords ) |
1129 | { |
1130 | FT_Pos stems[2]; |
1131 | |
1132 | |
1133 | stems[0] = FIXED_TO_INT( coords[0] ); |
1134 | stems[1] = FIXED_TO_INT( coords[1] ); |
1135 | |
1136 | ps_hints_stem( (PS_Hints)hints, dimension, 1, stems ); |
1137 | } |
1138 | |
1139 | |
1140 | FT_LOCAL_DEF( void ) |
1141 | t1_hints_funcs_init( T1_Hints_FuncsRec* funcs ) |
1142 | { |
1143 | FT_ZERO( funcs ); |
1144 | |
1145 | funcs->open = (T1_Hints_OpenFunc) t1_hints_open; |
1146 | funcs->close = (T1_Hints_CloseFunc) ps_hints_close; |
1147 | funcs->stem = (T1_Hints_SetStemFunc) t1_hints_stem; |
1148 | funcs->stem3 = (T1_Hints_SetStem3Func)ps_hints_t1stem3; |
1149 | funcs->reset = (T1_Hints_ResetFunc) ps_hints_t1reset; |
1150 | funcs->apply = (T1_Hints_ApplyFunc) ps_hints_apply; |
1151 | } |
1152 | |
1153 | |
1154 | /*************************************************************************/ |
1155 | /*************************************************************************/ |
1156 | /***** *****/ |
1157 | /***** TYPE 2 HINTS RECORDING INTERFACE *****/ |
1158 | /***** *****/ |
1159 | /*************************************************************************/ |
1160 | /*************************************************************************/ |
1161 | |
1162 | static void |
1163 | t2_hints_open( T2_Hints hints ) |
1164 | { |
1165 | ps_hints_open( (PS_Hints)hints, PS_HINT_TYPE_2 ); |
1166 | } |
1167 | |
1168 | |
1169 | static void |
1170 | t2_hints_stems( T2_Hints hints, |
1171 | FT_UInt dimension, |
1172 | FT_Int count, |
1173 | FT_Fixed* coords ) |
1174 | { |
1175 | FT_Pos stems[32], y; |
1176 | FT_Int total = count, n; |
1177 | |
1178 | |
1179 | y = 0; |
1180 | while ( total > 0 ) |
1181 | { |
1182 | /* determine number of stems to write */ |
1183 | count = total; |
1184 | if ( count > 16 ) |
1185 | count = 16; |
1186 | |
1187 | /* compute integer stem positions in font units */ |
1188 | for ( n = 0; n < count * 2; n++ ) |
1189 | { |
1190 | y = ADD_LONG( y, coords[n] ); |
1191 | stems[n] = FIXED_TO_INT( y ); |
1192 | } |
1193 | |
1194 | /* compute lengths */ |
1195 | for ( n = 0; n < count * 2; n += 2 ) |
1196 | stems[n + 1] = stems[n + 1] - stems[n]; |
1197 | |
1198 | /* add them to the current dimension */ |
1199 | ps_hints_stem( (PS_Hints)hints, dimension, count, stems ); |
1200 | |
1201 | total -= count; |
1202 | } |
1203 | } |
1204 | |
1205 | |
1206 | FT_LOCAL_DEF( void ) |
1207 | t2_hints_funcs_init( T2_Hints_FuncsRec* funcs ) |
1208 | { |
1209 | FT_ZERO( funcs ); |
1210 | |
1211 | funcs->open = (T2_Hints_OpenFunc) t2_hints_open; |
1212 | funcs->close = (T2_Hints_CloseFunc) ps_hints_close; |
1213 | funcs->stems = (T2_Hints_StemsFunc) t2_hints_stems; |
1214 | funcs->hintmask= (T2_Hints_MaskFunc) ps_hints_t2mask; |
1215 | funcs->counter = (T2_Hints_CounterFunc)ps_hints_t2counter; |
1216 | funcs->apply = (T2_Hints_ApplyFunc) ps_hints_apply; |
1217 | } |
1218 | |
1219 | |
1220 | /* END */ |
1221 | |