]> git.saurik.com Git - wxWidgets.git/blob - src/freetype/raster1/ftraster.c
wxSystemSettings class API face lift: better names for its methods
[wxWidgets.git] / src / freetype / raster1 / ftraster.c
1 /***************************************************************************/
2 /* */
3 /* ftraster.c */
4 /* */
5 /* The FreeType glyph rasterizer (body). */
6 /* */
7 /* Copyright 1996-2000 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 /* */
20 /* This is a rewrite of the FreeType 1.x scan-line converter */
21 /* */
22 /*************************************************************************/
23
24
25 #include "ftraster.h"
26 #include <freetype/internal/ftcalc.h> /* for FT_MulDiv() only */
27
28
29 /*************************************************************************/
30 /* */
31 /* A simple technical note on how the raster works */
32 /* ----------------------------------------------- */
33 /* */
34 /* Converting an outline into a bitmap is achieved in several steps: */
35 /* */
36 /* 1 - Decomposing the outline into successive `profiles'. Each */
37 /* profile is simply an array of scanline intersections on a given */
38 /* dimension. A profile's main attributes are */
39 /* */
40 /* o its scanline position boundaries, i.e. `Ymin' and `Ymax'. */
41 /* */
42 /* o an array of intersection coordinates for each scanline */
43 /* between `Ymin' and `Ymax'. */
44 /* */
45 /* o a direction, indicating whether it was built going `up' or */
46 /* `down', as this is very important for filling rules. */
47 /* */
48 /* 2 - Sweeping the target map's scanlines in order to compute segment */
49 /* `spans' which are then filled. Additionally, this pass */
50 /* performs drop-out control. */
51 /* */
52 /* The outline data is parsed during step 1 only. The profiles are */
53 /* built from the bottom of the render pool, used as a stack. The */
54 /* following graphics shows the profile list under construction: */
55 /* */
56 /* ____________________________________________________________ _ _ */
57 /* | | | | | */
58 /* | profile | coordinates for | profile | coordinates for |--> */
59 /* | 1 | profile 1 | 2 | profile 2 |--> */
60 /* |_________|___________________|_________|_________________|__ _ _ */
61 /* */
62 /* ^ ^ */
63 /* | | */
64 /* start of render pool top */
65 /* */
66 /* The top of the profile stack is kept in the `top' variable. */
67 /* */
68 /* As you can see, a profile record is pushed on top of the render */
69 /* pool, which is then followed by its coordinates/intersections. If */
70 /* a change of direction is detected in the outline, a new profile is */
71 /* generated until the end of the outline. */
72 /* */
73 /* Note that when all profiles have been generated, the function */
74 /* Finalize_Profile_Table() is used to record, for each profile, its */
75 /* bottom-most scanline as well as the scanline above its upmost */
76 /* boundary. These positions are called `y-turns' because they (sort */
77 /* of) correspond to local extrema. They are stored in a sorted list */
78 /* built from the top of the render pool as a downwards stack: */
79 /* */
80 /* _ _ _______________________________________ */
81 /* | | */
82 /* <--| sorted list of | */
83 /* <--| extrema scanlines | */
84 /* _ _ __________________|____________________| */
85 /* */
86 /* ^ ^ */
87 /* | | */
88 /* maxBuff sizeBuff = end of pool */
89 /* */
90 /* This list is later used during the sweep phase in order to */
91 /* optimize performance (see technical note on the sweep below). */
92 /* */
93 /* Of course, the raster detects whether the two stacks collide and */
94 /* handles the situation propertly. */
95 /* */
96 /*************************************************************************/
97
98
99 /*************************************************************************/
100 /*************************************************************************/
101 /** **/
102 /** CONFIGURATION MACROS **/
103 /** **/
104 /*************************************************************************/
105 /*************************************************************************/
106
107 /* define DEBUG_RASTER if you want to compile a debugging version */
108 #define xxxDEBUG_RASTER
109
110 /* The default render pool size in bytes */
111 #define RASTER_RENDER_POOL 8192
112
113 /* undefine FT_RASTER_OPTION_ANTI_ALIASING if you do not want to support */
114 /* 5-levels anti-aliasing */
115 #ifdef FT_CONFIG_OPTION_5_GRAY_LEVELS
116 #define FT_RASTER_OPTION_ANTI_ALIASING
117 #endif
118
119 /* The size of the two-lines intermediate bitmap used */
120 /* for anti-aliasing, in bytes. */
121 #define RASTER_GRAY_LINES 2048
122
123
124 /*************************************************************************/
125 /*************************************************************************/
126 /** **/
127 /** OTHER MACROS (do not change) **/
128 /** **/
129 /*************************************************************************/
130 /*************************************************************************/
131
132 /*************************************************************************/
133 /* */
134 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
135 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
136 /* messages during execution. */
137 /* */
138 #undef FT_COMPONENT
139 #define FT_COMPONENT trace_raster
140
141
142 #ifdef _STANDALONE_
143
144
145 /* This macro is used to indicate that a function parameter is unused. */
146 /* Its purpose is simply to reduce compiler warnings. Note also that */
147 /* simply defining it as `(void)x' doesn't avoid warnings with certain */
148 /* ANSI compilers (e.g. LCC). */
149 #define FT_UNUSED( x ) (x) = (x)
150
151 /* Disable the tracing mechanism for simplicity -- developers can */
152 /* activate it easily by redefining these two macros. */
153 #ifndef FT_ERROR
154 #define FT_ERROR( x ) do ; while ( 0 ) /* nothing */
155 #endif
156
157 #ifndef FT_TRACE
158 #define FT_TRACE( x ) do ; while ( 0 ) /* nothing */
159 #endif
160
161 #define Raster_Err_None 0
162 #define Raster_Err_Not_Ini -1
163 #define Raster_Err_Overflow -2
164 #define Raster_Err_Neg_Height -3
165 #define Raster_Err_Invalid -4
166 #define Raster_Err_Unsupported -5
167
168
169 #else /* _STANDALONE_ */
170
171
172 #include <freetype/internal/ftobjs.h>
173 #include <freetype/internal/ftdebug.h> /* for FT_TRACE() and FT_ERROR() */
174
175 #define Raster_Err_None FT_Err_Ok
176 #define Raster_Err_Not_Ini FT_Err_Raster_Uninitialized
177 #define Raster_Err_Overflow FT_Err_Raster_Overflow
178 #define Raster_Err_Neg_Height FT_Err_Raster_Negative_Height
179 #define Raster_Err_Invalid FT_Err_Invalid_Outline
180 #define Raster_Err_Unsupported FT_Err_Unimplemented_Feature
181
182
183 #endif /* _STANDALONE_ */
184
185
186 /* FMulDiv means `Fast MulDiv'; it is used in case where `b' is */
187 /* typically a small value and the result of a*b is known to fit into */
188 /* 32 bits. */
189 #define FMulDiv( a, b, c ) ( (a) * (b) / (c) )
190
191 /* On the other hand, SMulDiv means `Slow MulDiv', and is used typically */
192 /* for clipping computations. It simply uses the FT_MulDiv() function */
193 /* defined in `ftcalc.h'. */
194 #define SMulDiv FT_MulDiv
195
196 /* The rasterizer is a very general purpose component; please leave */
197 /* the following redefinitions there (you never know your target */
198 /* environment). */
199
200 #ifndef TRUE
201 #define TRUE 1
202 #endif
203
204 #ifndef FALSE
205 #define FALSE 0
206 #endif
207
208 #ifndef NULL
209 #define NULL (void*)0
210 #endif
211
212 #ifndef SUCCESS
213 #define SUCCESS 0
214 #endif
215
216 #ifndef FAILURE
217 #define FAILURE 1
218 #endif
219
220
221 #define MaxBezier 32 /* The maximum number of stacked Bezier curves. */
222 /* Setting this constant to more than 32 is a */
223 /* pure waste of space. */
224
225 #define Pixel_Bits 6 /* fractional bits of *input* coordinates */
226
227
228 /*************************************************************************/
229 /*************************************************************************/
230 /** **/
231 /** SIMPLE TYPE DECLARATIONS **/
232 /** **/
233 /*************************************************************************/
234 /*************************************************************************/
235
236 typedef int Int;
237 typedef unsigned int UInt;
238 typedef short Short;
239 typedef unsigned short UShort, *PUShort;
240 typedef long Long, *PLong;
241 typedef unsigned long ULong;
242
243 typedef unsigned char Byte, *PByte;
244 typedef char Bool;
245
246 typedef struct TPoint_
247 {
248 Long x;
249 Long y;
250
251 } TPoint;
252
253
254 typedef enum TFlow_
255 {
256 Flow_None = 0,
257 Flow_Up = 1,
258 Flow_Down = -1
259
260 } TFlow;
261
262
263 /* States of each line, arc, and profile */
264 typedef enum TStates_
265 {
266 Unknown,
267 Ascending,
268 Descending,
269 Flat
270
271 } TStates;
272
273
274 typedef struct TProfile_ TProfile;
275 typedef TProfile* PProfile;
276
277 struct TProfile_
278 {
279 FT_F26Dot6 X; /* current coordinate during sweep */
280 PProfile link; /* link to next profile - various purpose */
281 PLong offset; /* start of profile's data in render pool */
282 Int flow; /* Profile orientation: Asc/Descending */
283 Long height; /* profile's height in scanlines */
284 Long start; /* profile's starting scanline */
285
286 UShort countL; /* number of lines to step before this */
287 /* profile becomes drawable */
288
289 PProfile next; /* next profile in same contour, used */
290 /* during drop-out control */
291 };
292
293 typedef PProfile TProfileList;
294 typedef PProfile* PProfileList;
295
296
297 /* Simple record used to implement a stack of bands, required */
298 /* by the sub-banding mechanism */
299 typedef struct TBand_
300 {
301 Short y_min; /* band's minimum */
302 Short y_max; /* band's maximum */
303
304 } TBand;
305
306
307 #define AlignProfileSize \
308 ( ( sizeof ( TProfile ) + sizeof ( long ) - 1 ) / sizeof ( long ) )
309
310
311 #ifdef TT_STATIC_RASTER
312
313
314 #define RAS_ARGS /* void */
315 #define RAS_ARG /* void */
316
317 #define RAS_VARS /* void */
318 #define RAS_VAR /* void */
319
320 #define FT_UNUSED_RASTER do ; while ( 0 )
321
322
323 #else /* TT_STATIC_RASTER */
324
325
326 #define RAS_ARGS TRaster_Instance* raster,
327 #define RAS_ARG TRaster_Instance* raster
328
329 #define RAS_VARS raster,
330 #define RAS_VAR raster
331
332 #define FT_UNUSED_RASTER FT_UNUSED( raster )
333
334
335 #endif /* TT_STATIC_RASTER */
336
337
338 typedef struct TRaster_Instance_ TRaster_Instance;
339
340
341 /* prototypes used for sweep function dispatch */
342 typedef void Function_Sweep_Init( RAS_ARGS Short* min,
343 Short* max );
344
345 typedef void Function_Sweep_Span( RAS_ARGS Short y,
346 FT_F26Dot6 x1,
347 FT_F26Dot6 x2,
348 PProfile left,
349 PProfile right );
350
351 typedef void Function_Sweep_Step( RAS_ARG );
352
353
354 /* NOTE: These operations are only valid on 2's complement processors */
355
356 #define FLOOR( x ) ( (x) & -ras.precision )
357 #define CEILING( x ) ( ( (x) + ras.precision - 1 ) & -ras.precision )
358 #define TRUNC( x ) ( (signed long)(x) >> ras.precision_bits )
359 #define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
360 #define SCALED( x ) ( ( (x) << ras.scale_shift ) - ras.precision_half )
361
362 /* Note that I have moved the location of some fields in the */
363 /* structure to ensure that the most used variables are used */
364 /* at the top. Thus, their offset can be coded with less */
365 /* opcodes, and it results in a smaller executable. */
366
367 struct TRaster_Instance_
368 {
369 Int precision_bits; /* precision related variables */
370 Int precision;
371 Int precision_half;
372 Long precision_mask;
373 Int precision_shift;
374 Int precision_step;
375 Int precision_jitter;
376
377 Int scale_shift; /* == precision_shift for bitmaps */
378 /* == precision_shift+1 for pixmaps */
379
380 PLong buff; /* The profiles buffer */
381 PLong sizeBuff; /* Render pool size */
382 PLong maxBuff; /* Profiles buffer size */
383 PLong top; /* Current cursor in buffer */
384
385 FT_Error error;
386
387 Int numTurns; /* number of Y-turns in outline */
388
389 TPoint* arc; /* current Bezier arc pointer */
390
391 UShort bWidth; /* target bitmap width */
392 PByte bTarget; /* target bitmap buffer */
393 PByte gTarget; /* target pixmap buffer */
394
395 Long lastX, lastY, minY, maxY;
396
397 UShort num_Profs; /* current number of profiles */
398
399 Bool fresh; /* signals a fresh new profile which */
400 /* 'start' field must be completed */
401 Bool joint; /* signals that the last arc ended */
402 /* exactly on a scanline. Allows */
403 /* removal of doublets */
404 PProfile cProfile; /* current profile */
405 PProfile fProfile; /* head of linked list of profiles */
406 PProfile gProfile; /* contour's first profile in case */
407 /* of impact */
408
409 TStates state; /* rendering state */
410
411 FT_Bitmap target; /* description of target bit/pixmap */
412 FT_Outline outline;
413
414 Long traceOfs; /* current offset in target bitmap */
415 Long traceG; /* current offset in target pixmap */
416
417 Short traceIncr; /* sweep's increment in target bitmap */
418
419 Short gray_min_x; /* current min x during gray rendering */
420 Short gray_max_x; /* current max x during gray rendering */
421
422 /* dispatch variables */
423
424 Function_Sweep_Init* Proc_Sweep_Init;
425 Function_Sweep_Span* Proc_Sweep_Span;
426 Function_Sweep_Span* Proc_Sweep_Drop;
427 Function_Sweep_Step* Proc_Sweep_Step;
428
429 Byte dropOutControl; /* current drop_out control method */
430
431 Bool second_pass; /* indicates wether a horizontal pass */
432 /* should be performed to control */
433 /* drop-out accurately when calling */
434 /* Render_Glyph. Note that there is */
435 /* no horizontal pass during gray */
436 /* rendering. */
437
438 TPoint arcs[2 * MaxBezier + 1]; /* The Bezier stack */
439
440 TBand band_stack[16]; /* band stack used for sub-banding */
441 Int band_top; /* band stack top */
442
443 Int count_table[256]; /* Look-up table used to quickly count */
444 /* set bits in a gray 2x2 cell */
445
446 void* memory;
447
448 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
449
450 Byte grays[5]; /* Palette of gray levels used for */
451 /* render. */
452
453 Byte gray_lines[RASTER_GRAY_LINES];
454 /* Intermediate table used to render the */
455 /* graylevels pixmaps. */
456 /* gray_lines is a buffer holding two */
457 /* monochrome scanlines */
458
459 Short gray_width; /* width in bytes of one monochrome */
460 /* intermediate scanline of gray_lines. */
461 /* Each gray pixel takes 2 bits long there */
462
463 /* The gray_lines must hold 2 lines, thus with size */
464 /* in bytes of at least `gray_width*2'. */
465
466 #endif /* FT_RASTER_ANTI_ALIASING */
467
468 #if 0
469 PByte flags; /* current flags table */
470 PUShort outs; /* current outlines table */
471 FT_Vector* coords;
472
473 UShort nPoints; /* number of points in current glyph */
474 Short nContours; /* number of contours in current glyph */
475 #endif
476
477 };
478
479
480 #ifdef FT_CONFIG_OPTION_STATIC_RASTER
481
482 static TRaster_Instance cur_ras;
483 #define ras cur_ras
484
485 #else
486
487 #define ras (*raster)
488
489 #endif /* FT_CONFIG_OPTION_STATIC_RASTER */
490
491
492 /*************************************************************************/
493 /*************************************************************************/
494 /** **/
495 /** PROFILES COMPUTATION **/
496 /** **/
497 /*************************************************************************/
498 /*************************************************************************/
499
500
501 /*************************************************************************/
502 /* */
503 /* <Function> */
504 /* Set_High_Precision */
505 /* */
506 /* <Description> */
507 /* Sets precision variables according to param flag. */
508 /* */
509 /* <Input> */
510 /* High :: Set to True for high precision (typically for ppem < 18), */
511 /* false otherwise. */
512 /* */
513 static
514 void Set_High_Precision( RAS_ARGS Int High )
515 {
516 if ( High )
517 {
518 ras.precision_bits = 10;
519 ras.precision_step = 128;
520 ras.precision_jitter = 24;
521 }
522 else
523 {
524 ras.precision_bits = 6;
525 ras.precision_step = 32;
526 ras.precision_jitter = 2;
527 }
528
529 FT_TRACE6(( "Set_High_Precision(%s)\n", High ? "true" : "false" ));
530
531 ras.precision = 1L << ras.precision_bits;
532 ras.precision_half = ras.precision / 2;
533 ras.precision_shift = ras.precision_bits - Pixel_Bits;
534 ras.precision_mask = -ras.precision;
535 }
536
537
538 /*************************************************************************/
539 /* */
540 /* <Function> */
541 /* New_Profile */
542 /* */
543 /* <Description> */
544 /* Creates a new profile in the render pool. */
545 /* */
546 /* <Input> */
547 /* aState :: The state/orientation of the new profile. */
548 /* */
549 /* <Return> */
550 /* SUCCESS on success. FAILURE in case of overflow or of incoherent */
551 /* profile. */
552 /* */
553 static
554 Bool New_Profile( RAS_ARGS TStates aState )
555 {
556 if ( !ras.fProfile )
557 {
558 ras.cProfile = (PProfile)ras.top;
559 ras.fProfile = ras.cProfile;
560 ras.top += AlignProfileSize;
561 }
562
563 if ( ras.top >= ras.maxBuff )
564 {
565 ras.error = Raster_Err_Overflow;
566 return FAILURE;
567 }
568
569 switch ( aState )
570 {
571 case Ascending:
572 ras.cProfile->flow = Flow_Up;
573 FT_TRACE6(( "New ascending profile = %lx\n", (long)ras.cProfile ));
574 break;
575
576 case Descending:
577 ras.cProfile->flow = Flow_Down;
578 FT_TRACE6(( "New descending profile = %lx\n", (long)ras.cProfile ));
579 break;
580
581 default:
582 FT_ERROR(( "New_Profile: invalid profile direction!\n" ));
583 ras.error = Raster_Err_Invalid;
584 return FAILURE;
585 }
586
587 ras.cProfile->start = 0;
588 ras.cProfile->height = 0;
589 ras.cProfile->offset = ras.top;
590 ras.cProfile->link = (PProfile)0;
591 ras.cProfile->next = (PProfile)0;
592
593 if ( !ras.gProfile )
594 ras.gProfile = ras.cProfile;
595
596 ras.state = aState;
597 ras.fresh = TRUE;
598 ras.joint = FALSE;
599
600 return SUCCESS;
601 }
602
603
604 /*************************************************************************/
605 /* */
606 /* <Function> */
607 /* End_Profile */
608 /* */
609 /* <Description> */
610 /* Finalizes the current profile. */
611 /* */
612 /* <Return> */
613 /* SUCCESS on success. FAILURE in case of overflow or incoherency. */
614 /* */
615 static
616 Bool End_Profile( RAS_ARG )
617 {
618 Long h;
619 PProfile oldProfile;
620
621
622 h = ras.top - ras.cProfile->offset;
623
624 if ( h < 0 )
625 {
626 FT_ERROR(( "End_Profile: negative height encountered!\n" ));
627 ras.error = Raster_Err_Neg_Height;
628 return FAILURE;
629 }
630
631 if ( h > 0 )
632 {
633 FT_TRACE6(( "Ending profile %lx, start = %ld, height = %ld\n",
634 (long)ras.cProfile, ras.cProfile->start, h ));
635
636 oldProfile = ras.cProfile;
637 ras.cProfile->height = h;
638 ras.cProfile = (PProfile)ras.top;
639
640 ras.top += AlignProfileSize;
641
642 ras.cProfile->height = 0;
643 ras.cProfile->offset = ras.top;
644 oldProfile->next = ras.cProfile;
645 ras.num_Profs++;
646 }
647
648 if ( ras.top >= ras.maxBuff )
649 {
650 FT_TRACE1(( "overflow in End_Profile\n" ));
651 ras.error = Raster_Err_Overflow;
652 return FAILURE;
653 }
654
655 ras.joint = FALSE;
656
657 return SUCCESS;
658 }
659
660
661 /*************************************************************************/
662 /* */
663 /* <Function> */
664 /* Insert_Y_Turn */
665 /* */
666 /* <Description> */
667 /* Inserts a salient into the sorted list placed on top of the render */
668 /* pool. */
669 /* */
670 /* <Input> */
671 /* New y scanline position. */
672 /* */
673 /* <Return> */
674 /* SUCCESS on success. FAILURE in case of overflow. */
675 /* */
676 static
677 Bool Insert_Y_Turn( RAS_ARGS Int y )
678 {
679 PLong y_turns;
680 Int y2, n;
681
682
683 n = ras.numTurns - 1;
684 y_turns = ras.sizeBuff - ras.numTurns;
685
686 /* look for first y value that is <= */
687 while ( n >= 0 && y < y_turns[n] )
688 n--;
689
690 /* if it is <, simply insert it, ignore if == */
691 if ( n >= 0 && y > y_turns[n] )
692 while ( n >= 0 )
693 {
694 y2 = y_turns[n];
695 y_turns[n] = y;
696 y = y2;
697 n--;
698 }
699
700 if ( n < 0 )
701 {
702 if ( ras.maxBuff <= ras.top )
703 {
704 ras.error = Raster_Err_Overflow;
705 return FAILURE;
706 }
707 ras.maxBuff--;
708 ras.numTurns++;
709 ras.sizeBuff[-ras.numTurns] = y;
710 }
711
712 return SUCCESS;
713 }
714
715
716 /*************************************************************************/
717 /* */
718 /* <Function> */
719 /* Finalize_Profile_Table */
720 /* */
721 /* <Description> */
722 /* Adjusts all links in the profiles list. */
723 /* */
724 /* <Return> */
725 /* SUCCESS on success. FAILURE in case of overflow. */
726 /* */
727 static
728 Bool Finalize_Profile_Table( RAS_ARG )
729 {
730 Int bottom, top;
731 UShort n;
732 PProfile p;
733
734
735 n = ras.num_Profs;
736
737 if ( n > 1 )
738 {
739 p = ras.fProfile;
740 while ( n > 0 )
741 {
742 if ( n > 1 )
743 p->link = (PProfile)( p->offset + p->height );
744 else
745 p->link = NULL;
746
747 switch ( p->flow )
748 {
749 case Flow_Down:
750 bottom = p->start - p->height+1;
751 top = p->start;
752 p->start = bottom;
753 p->offset += p->height - 1;
754 break;
755
756 case Flow_Up:
757 default:
758 bottom = p->start;
759 top = p->start + p->height - 1;
760 }
761
762 if ( Insert_Y_Turn( RAS_VARS bottom ) ||
763 Insert_Y_Turn( RAS_VARS top + 1 ) )
764 return FAILURE;
765
766 p = p->link;
767 n--;
768 }
769 }
770 else
771 ras.fProfile = NULL;
772
773 return SUCCESS;
774 }
775
776
777 /*************************************************************************/
778 /* */
779 /* <Function> */
780 /* Split_Conic */
781 /* */
782 /* <Description> */
783 /* Subdivides one conic Bezier into two joint sub-arcs in the Bezier */
784 /* stack. */
785 /* */
786 /* <Input> */
787 /* None (subdivided Bezier is taken from the top of the stack). */
788 /* */
789 /* <Note> */
790 /* This routine is the `beef' of this component. It is _the_ inner */
791 /* loop that should be optimized to hell to get the best performance. */
792 /* */
793 static
794 void Split_Conic( TPoint* base )
795 {
796 Long a, b;
797
798
799 base[4].x = base[2].x;
800 b = base[1].x;
801 a = base[3].x = ( base[2].x + b ) / 2;
802 b = base[1].x = ( base[0].x + b ) / 2;
803 base[2].x = ( a + b ) / 2;
804
805 base[4].y = base[2].y;
806 b = base[1].y;
807 a = base[3].y = ( base[2].y + b ) / 2;
808 b = base[1].y = ( base[0].y + b ) / 2;
809 base[2].y = ( a + b ) / 2;
810
811 /* hand optimized. gcc doesn't seem to be too good at common */
812 /* expression substitution and instruction scheduling ;-) */
813 }
814
815
816 /*************************************************************************/
817 /* */
818 /* <Function> */
819 /* Split_Cubic */
820 /* */
821 /* <Description> */
822 /* Subdivides a third-order Bezier arc into two joint sub-arcs in the */
823 /* Bezier stack. */
824 /* */
825 /* <Note> */
826 /* This routine is the `beef' of the component. It is one of _the_ */
827 /* inner loops that should be optimized like hell to get the best */
828 /* performance. */
829 /* */
830 static
831 void Split_Cubic( TPoint* base )
832 {
833 Long a, b, c, d;
834
835
836 base[6].x = base[3].x;
837 c = base[1].x;
838 d = base[2].x;
839 base[1].x = a = ( base[0].x + c + 1 ) >> 1;
840 base[5].x = b = ( base[3].x + d + 1 ) >> 1;
841 c = ( c + d + 1 ) >> 1;
842 base[2].x = a = ( a + c + 1 ) >> 1;
843 base[4].x = b = ( b + c + 1 ) >> 1;
844 base[3].x = ( a + b + 1 ) >> 1;
845
846 base[6].y = base[3].y;
847 c = base[1].y;
848 d = base[2].y;
849 base[1].y = a = ( base[0].y + c + 1 ) >> 1;
850 base[5].y = b = ( base[3].y + d + 1 ) >> 1;
851 c = ( c + d + 1 ) >> 1;
852 base[2].y = a = ( a + c + 1 ) >> 1;
853 base[4].y = b = ( b + c + 1 ) >> 1;
854 base[3].y = ( a + b + 1 ) >> 1;
855 }
856
857
858 /*************************************************************************/
859 /* */
860 /* <Function> */
861 /* Line_Up */
862 /* */
863 /* <Description> */
864 /* Computes the x-coordinates of an ascending line segment and stores */
865 /* them in the render pool. */
866 /* */
867 /* <Input> */
868 /* x1 :: The x-coordinate of the segment's start point. */
869 /* */
870 /* y1 :: The y-coordinate of the segment's start point. */
871 /* */
872 /* x2 :: The x-coordinate of the segment's end point. */
873 /* */
874 /* y2 :: The y-coordinate of the segment's end point. */
875 /* */
876 /* miny :: A lower vertical clipping bound value. */
877 /* */
878 /* maxy :: An upper vertical clipping bound value. */
879 /* */
880 /* <Return> */
881 /* SUCCESS on success, FAILURE on render pool overflow. */
882 /* */
883 static
884 Bool Line_Up( RAS_ARGS Long x1,
885 Long y1,
886 Long x2,
887 Long y2,
888 Long miny,
889 Long maxy )
890 {
891 Long Dx, Dy;
892 Int e1, e2, f1, f2, size; /* XXX: is `Short' sufficient? */
893 Long Ix, Rx, Ax;
894
895 PLong top;
896
897
898 Dx = x2 - x1;
899 Dy = y2 - y1;
900
901 if ( Dy <= 0 || y2 < miny || y1 > maxy )
902 return SUCCESS;
903
904 if ( y1 < miny )
905 {
906 /* Take care: miny-y1 can be a very large value; we use */
907 /* a slow MulDiv function to avoid clipping bugs */
908 x1 += SMulDiv( Dx, miny - y1, Dy );
909 e1 = TRUNC( miny );
910 f1 = 0;
911 }
912 else
913 {
914 e1 = TRUNC( y1 );
915 f1 = FRAC( y1 );
916 }
917
918 if ( y2 > maxy )
919 {
920 /* x2 += FMulDiv( Dx, maxy - y2, Dy ); UNNECESSARY */
921 e2 = TRUNC( maxy );
922 f2 = 0;
923 }
924 else
925 {
926 e2 = TRUNC( y2 );
927 f2 = FRAC( y2 );
928 }
929
930 if ( f1 > 0 )
931 {
932 if ( e1 == e2 )
933 return SUCCESS;
934 else
935 {
936 x1 += FMulDiv( Dx, ras.precision - f1, Dy );
937 e1 += 1;
938 }
939 }
940 else
941 if ( ras.joint )
942 {
943 ras.top--;
944 ras.joint = FALSE;
945 }
946
947 ras.joint = ( f2 == 0 );
948
949 if ( ras.fresh )
950 {
951 ras.cProfile->start = e1;
952 ras.fresh = FALSE;
953 }
954
955 size = e2 - e1 + 1;
956 if ( ras.top + size >= ras.maxBuff )
957 {
958 ras.error = Raster_Err_Overflow;
959 return FAILURE;
960 }
961
962 if ( Dx > 0 )
963 {
964 Ix = ( ras.precision * Dx ) / Dy;
965 Rx = ( ras.precision * Dx ) % Dy;
966 Dx = 1;
967 }
968 else
969 {
970 Ix = -( ( ras.precision * -Dx ) / Dy );
971 Rx = ( ras.precision * -Dx ) % Dy;
972 Dx = -1;
973 }
974
975 Ax = -Dy;
976 top = ras.top;
977
978 while ( size > 0 )
979 {
980 *top++ = x1;
981
982 x1 += Ix;
983 Ax += Rx;
984 if ( Ax >= 0 )
985 {
986 Ax -= Dy;
987 x1 += Dx;
988 }
989 size--;
990 }
991
992 ras.top = top;
993 return SUCCESS;
994 }
995
996
997 /*************************************************************************/
998 /* */
999 /* <Function> */
1000 /* Line_Down */
1001 /* */
1002 /* <Description> */
1003 /* Computes the x-coordinates of an descending line segment and */
1004 /* stores them in the render pool. */
1005 /* */
1006 /* <Input> */
1007 /* x1 :: The x-coordinate of the segment's start point. */
1008 /* */
1009 /* y1 :: The y-coordinate of the segment's start point. */
1010 /* */
1011 /* x2 :: The x-coordinate of the segment's end point. */
1012 /* */
1013 /* y2 :: The y-coordinate of the segment's end point. */
1014 /* */
1015 /* miny :: A lower vertical clipping bound value. */
1016 /* */
1017 /* maxy :: An upper vertical clipping bound value. */
1018 /* */
1019 /* <Return> */
1020 /* SUCCESS on success, FAILURE on render pool overflow. */
1021 /* */
1022 static
1023 Bool Line_Down( RAS_ARGS Long x1,
1024 Long y1,
1025 Long x2,
1026 Long y2,
1027 Long miny,
1028 Long maxy )
1029 {
1030 Bool result, fresh;
1031
1032
1033 fresh = ras.fresh;
1034
1035 result = Line_Up( RAS_VARS x1, -y1, x2, -y2, -maxy, -miny );
1036
1037 if ( fresh && !ras.fresh )
1038 ras.cProfile->start = -ras.cProfile->start;
1039
1040 return result;
1041 }
1042
1043
1044 /* A function type describing the functions used to split Bezier arcs */
1045 typedef void (*TSplitter)( TPoint* base );
1046
1047
1048 /*************************************************************************/
1049 /* */
1050 /* <Function> */
1051 /* Bezier_Up */
1052 /* */
1053 /* <Description> */
1054 /* Computes the x-coordinates of an ascending Bezier arc and stores */
1055 /* them in the render pool. */
1056 /* */
1057 /* <Input> */
1058 /* degree :: The degree of the Bezier arc (either 2 or 3). */
1059 /* */
1060 /* splitter :: The function to split Bezier arcs. */
1061 /* */
1062 /* miny :: A lower vertical clipping bound value. */
1063 /* */
1064 /* maxy :: An upper vertical clipping bound value. */
1065 /* */
1066 /* <Return> */
1067 /* SUCCESS on success, FAILURE on render pool overflow. */
1068 /* */
1069 static
1070 Bool Bezier_Up( RAS_ARGS Int degree,
1071 TSplitter splitter,
1072 Long miny,
1073 Long maxy )
1074 {
1075 Long y1, y2, e, e2, e0;
1076 Short f1;
1077
1078 TPoint* arc;
1079 TPoint* start_arc;
1080
1081 PLong top;
1082
1083
1084 arc = ras.arc;
1085 y1 = arc[degree].y;
1086 y2 = arc[0].y;
1087 top = ras.top;
1088
1089 if ( y2 < miny || y1 > maxy )
1090 goto Fin;
1091
1092 e2 = FLOOR( y2 );
1093
1094 if ( e2 > maxy )
1095 e2 = maxy;
1096
1097 e0 = miny;
1098
1099 if ( y1 < miny )
1100 e = miny;
1101 else
1102 {
1103 e = CEILING( y1 );
1104 f1 = FRAC( y1 );
1105 e0 = e;
1106
1107 if ( f1 == 0 )
1108 {
1109 if ( ras.joint )
1110 {
1111 top--;
1112 ras.joint = FALSE;
1113 }
1114
1115 *top++ = arc[degree].x;
1116
1117 e += ras.precision;
1118 }
1119 }
1120
1121 if ( ras.fresh )
1122 {
1123 ras.cProfile->start = TRUNC( e0 );
1124 ras.fresh = FALSE;
1125 }
1126
1127 if ( e2 < e )
1128 goto Fin;
1129
1130 if ( ( top + TRUNC( e2 - e ) + 1 ) >= ras.maxBuff )
1131 {
1132 ras.top = top;
1133 ras.error = Raster_Err_Overflow;
1134 return FAILURE;
1135 }
1136
1137 start_arc = arc;
1138
1139 while ( arc >= start_arc && e <= e2 )
1140 {
1141 ras.joint = FALSE;
1142
1143 y2 = arc[0].y;
1144
1145 if ( y2 > e )
1146 {
1147 y1 = arc[degree].y;
1148 if ( y2 - y1 >= ras.precision_step )
1149 {
1150 splitter( arc );
1151 arc += degree;
1152 }
1153 else
1154 {
1155 *top++ = arc[degree].x + FMulDiv( arc[0].x-arc[degree].x,
1156 e - y1, y2 - y1 );
1157 arc -= degree;
1158 e += ras.precision;
1159 }
1160 }
1161 else
1162 {
1163 if ( y2 == e )
1164 {
1165 ras.joint = TRUE;
1166 *top++ = arc[0].x;
1167
1168 e += ras.precision;
1169 }
1170 arc -= degree;
1171 }
1172 }
1173
1174 Fin:
1175 ras.top = top;
1176 ras.arc -= degree;
1177 return SUCCESS;
1178 }
1179
1180
1181 /*************************************************************************/
1182 /* */
1183 /* <Function> */
1184 /* Bezier_Down */
1185 /* */
1186 /* <Description> */
1187 /* Computes the x-coordinates of an descending Bezier arc and stores */
1188 /* them in the render pool. */
1189 /* */
1190 /* <Input> */
1191 /* degree :: The degree of the Bezier arc (either 2 or 3). */
1192 /* */
1193 /* splitter :: The function to split Bezier arcs. */
1194 /* */
1195 /* miny :: A lower vertical clipping bound value. */
1196 /* */
1197 /* maxy :: An upper vertical clipping bound value. */
1198 /* */
1199 /* <Return> */
1200 /* SUCCESS on success, FAILURE on render pool overflow. */
1201 /* */
1202 static
1203 Bool Bezier_Down( RAS_ARGS Int degree,
1204 TSplitter splitter,
1205 Long miny,
1206 Long maxy )
1207 {
1208 TPoint* arc = ras.arc;
1209 Bool result, fresh;
1210
1211
1212 arc[0].y = -arc[0].y;
1213 arc[1].y = -arc[1].y;
1214 arc[2].y = -arc[2].y;
1215 if ( degree > 2 )
1216 arc[3].y = -arc[3].y;
1217
1218 fresh = ras.fresh;
1219
1220 result = Bezier_Up( RAS_VARS degree, splitter, -maxy, -miny );
1221
1222 if ( fresh && !ras.fresh )
1223 ras.cProfile->start = -ras.cProfile->start;
1224
1225 arc[0].y = -arc[0].y;
1226 return result;
1227 }
1228
1229
1230 /*************************************************************************/
1231 /* */
1232 /* <Function> */
1233 /* Line_To */
1234 /* */
1235 /* <Description> */
1236 /* Injects a new line segment and adjusts Profiles list. */
1237 /* */
1238 /* <Input> */
1239 /* x :: The x-coordinate of the segment's end point (its start point */
1240 /* is stored in `LastX'). */
1241 /* */
1242 /* y :: The y-coordinate of the segment's end point (its start point */
1243 /* is stored in `LastY'). */
1244 /* */
1245 /* <Return> */
1246 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1247 /* profile. */
1248 /* */
1249 static
1250 Bool Line_To( RAS_ARGS Long x,
1251 Long y )
1252 {
1253 /* First, detect a change of direction */
1254
1255 switch ( ras.state )
1256 {
1257 case Unknown:
1258 if ( y > ras.lastY )
1259 {
1260 if ( New_Profile( RAS_VARS Ascending ) )
1261 return FAILURE;
1262 }
1263 else
1264 {
1265 if ( y < ras.lastY )
1266 if ( New_Profile( RAS_VARS Descending ) )
1267 return FAILURE;
1268 }
1269 break;
1270
1271 case Ascending:
1272 if ( y < ras.lastY )
1273 {
1274 if ( End_Profile( RAS_VAR ) ||
1275 New_Profile( RAS_VARS Descending ) )
1276 return FAILURE;
1277 }
1278 break;
1279
1280 case Descending:
1281 if ( y > ras.lastY )
1282 {
1283 if ( End_Profile( RAS_VAR ) ||
1284 New_Profile( RAS_VARS Ascending ) )
1285 return FAILURE;
1286 }
1287 break;
1288
1289 default:
1290 ;
1291 }
1292
1293 /* Then compute the lines */
1294
1295 switch ( ras.state )
1296 {
1297 case Ascending:
1298 if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
1299 x, y, ras.minY, ras.maxY ) )
1300 return FAILURE;
1301 break;
1302
1303 case Descending:
1304 if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
1305 x, y, ras.minY, ras.maxY ) )
1306 return FAILURE;
1307 break;
1308
1309 default:
1310 ;
1311 }
1312
1313 ras.lastX = x;
1314 ras.lastY = y;
1315
1316 return SUCCESS;
1317 }
1318
1319
1320 /*************************************************************************/
1321 /* */
1322 /* <Function> */
1323 /* Conic_To */
1324 /* */
1325 /* <Description> */
1326 /* Injects a new conic arc and adjusts the profile list. */
1327 /* */
1328 /* <Input> */
1329 /* cx :: The x-coordinate of the arc's new control point. */
1330 /* */
1331 /* cy :: The y-coordinate of the arc's new control point. */
1332 /* */
1333 /* x :: The x-coordinate of the arc's end point (its start point is */
1334 /* stored in `LastX'). */
1335 /* */
1336 /* y :: The y-coordinate of the arc's end point (its start point is */
1337 /* stored in `LastY'). */
1338 /* */
1339 /* <Return> */
1340 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1341 /* profile. */
1342 /* */
1343 static
1344 Bool Conic_To( RAS_ARGS Long cx,
1345 Long cy,
1346 Long x,
1347 Long y )
1348 {
1349 Long y1, y2, y3, x3, ymin, ymax;
1350 TStates state_bez;
1351
1352
1353 ras.arc = ras.arcs;
1354 ras.arc[2].x = ras.lastX;
1355 ras.arc[2].y = ras.lastY;
1356 ras.arc[1].x = cx; ras.arc[1].y = cy;
1357 ras.arc[0].x = x; ras.arc[0].y = y;
1358
1359 do
1360 {
1361 y1 = ras.arc[2].y;
1362 y2 = ras.arc[1].y;
1363 y3 = ras.arc[0].y;
1364 x3 = ras.arc[0].x;
1365
1366 /* first, categorize the Bezier arc */
1367
1368 if ( y1 <= y3 )
1369 {
1370 ymin = y1;
1371 ymax = y3;
1372 }
1373 else
1374 {
1375 ymin = y3;
1376 ymax = y1;
1377 }
1378
1379 if ( y2 < ymin || y2 > ymax )
1380 {
1381 /* this arc has no given direction, split it! */
1382 Split_Conic( ras.arc );
1383 ras.arc += 2;
1384 }
1385 else if ( y1 == y3 )
1386 {
1387 /* this arc is flat, ignore it and pop it from the Bezier stack */
1388 ras.arc -= 2;
1389 }
1390 else
1391 {
1392 /* the arc is y-monotonous, either ascending or descending */
1393 /* detect a change of direction */
1394 state_bez = y1 < y3 ? Ascending : Descending;
1395 if ( ras.state != state_bez )
1396 {
1397 /* finalize current profile if any */
1398 if ( ras.state != Unknown &&
1399 End_Profile( RAS_VAR ) )
1400 goto Fail;
1401
1402 /* create a new profile */
1403 if ( New_Profile( RAS_VARS state_bez ) )
1404 goto Fail;
1405 }
1406
1407 /* now call the appropriate routine */
1408 if ( state_bez == Ascending )
1409 {
1410 if ( Bezier_Up( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1411 goto Fail;
1412 }
1413 else
1414 if ( Bezier_Down( RAS_VARS 2, Split_Conic, ras.minY, ras.maxY ) )
1415 goto Fail;
1416 }
1417
1418 } while ( ras.arc >= ras.arcs );
1419
1420 ras.lastX = x3;
1421 ras.lastY = y3;
1422
1423 return SUCCESS;
1424
1425 Fail:
1426 return FAILURE;
1427 }
1428
1429
1430 /*************************************************************************/
1431 /* */
1432 /* <Function> */
1433 /* Cubic_To */
1434 /* */
1435 /* <Description> */
1436 /* Injects a new cubic arc and adjusts the profile list. */
1437 /* */
1438 /* <Input> */
1439 /* cx1 :: The x-coordinate of the arc's first new control point. */
1440 /* */
1441 /* cy1 :: The y-coordinate of the arc's first new control point. */
1442 /* */
1443 /* cx2 :: The x-coordinate of the arc's second new control point. */
1444 /* */
1445 /* cy2 :: The y-coordinate of the arc's second new control point. */
1446 /* */
1447 /* x :: The x-coordinate of the arc's end point (its start point is */
1448 /* stored in `LastX'). */
1449 /* */
1450 /* y :: The y-coordinate of the arc's end point (its start point is */
1451 /* stored in `LastY'). */
1452 /* */
1453 /* <Return> */
1454 /* SUCCESS on success, FAILURE on render pool overflow or incorrect */
1455 /* profile. */
1456 /* */
1457 static
1458 Bool Cubic_To( RAS_ARGS Long cx1,
1459 Long cy1,
1460 Long cx2,
1461 Long cy2,
1462 Long x,
1463 Long y )
1464 {
1465 Long y1, y2, y3, y4, x4, ymin1, ymax1, ymin2, ymax2;
1466 TStates state_bez;
1467
1468
1469 ras.arc = ras.arcs;
1470 ras.arc[3].x = ras.lastX;
1471 ras.arc[3].y = ras.lastY;
1472 ras.arc[2].x = cx1; ras.arc[2].y = cy1;
1473 ras.arc[1].x = cx2; ras.arc[1].y = cy2;
1474 ras.arc[0].x = x; ras.arc[0].y = y;
1475
1476 do
1477 {
1478 y1 = ras.arc[3].y;
1479 y2 = ras.arc[2].y;
1480 y3 = ras.arc[1].y;
1481 y4 = ras.arc[0].y;
1482 x4 = ras.arc[0].x;
1483
1484 /* first, categorize the Bezier arc */
1485
1486 if ( y1 <= y4 )
1487 {
1488 ymin1 = y1;
1489 ymax1 = y4;
1490 }
1491 else
1492 {
1493 ymin1 = y4;
1494 ymax1 = y1;
1495 }
1496
1497 if ( y2 <= y3 )
1498 {
1499 ymin2 = y2;
1500 ymax2 = y3;
1501 }
1502 else
1503 {
1504 ymin2 = y3;
1505 ymax2 = y2;
1506 }
1507
1508 if ( ymin2 < ymin1 || ymax2 > ymax1 )
1509 {
1510 /* this arc has no given direction, split it! */
1511 Split_Cubic( ras.arc );
1512 ras.arc += 3;
1513 }
1514 else if ( y1 == y4 )
1515 {
1516 /* this arc is flat, ignore it and pop it from the Bezier stack */
1517 ras.arc -= 3;
1518 }
1519 else
1520 {
1521 state_bez = ( y1 <= y4 ) ? Ascending : Descending;
1522
1523 /* detect a change of direction */
1524 if ( ras.state != state_bez )
1525 {
1526 if ( ras.state != Unknown &&
1527 End_Profile( RAS_VAR ) )
1528 goto Fail;
1529
1530 if ( New_Profile( RAS_VARS state_bez ) )
1531 goto Fail;
1532 }
1533
1534 /* compute intersections */
1535 if ( state_bez == Ascending )
1536 {
1537 if ( Bezier_Up( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1538 goto Fail;
1539 }
1540 else
1541 if ( Bezier_Down( RAS_VARS 3, Split_Cubic, ras.minY, ras.maxY ) )
1542 goto Fail;
1543 }
1544
1545 } while ( ras.arc >= ras.arcs );
1546
1547 ras.lastX = x4;
1548 ras.lastY = y4;
1549
1550 return SUCCESS;
1551
1552 Fail:
1553 return FAILURE;
1554 }
1555
1556
1557 #undef SWAP_
1558 #define SWAP_( x, y ) do \
1559 { \
1560 Long swap = x; \
1561 \
1562 \
1563 x = y; \
1564 y = swap; \
1565 } while ( 0 )
1566
1567
1568 /*************************************************************************/
1569 /* */
1570 /* <Function> */
1571 /* Decompose_Curve */
1572 /* */
1573 /* <Description> */
1574 /* Scans the outline arays in order to emit individual segments and */
1575 /* Beziers by calling Line_To() and Bezier_To(). It handles all */
1576 /* weird cases, like when the first point is off the curve, or when */
1577 /* there are simply no `on' points in the contour! */
1578 /* */
1579 /* <Input> */
1580 /* first :: The index of the first point in the contour. */
1581 /* */
1582 /* last :: The index of the last point in the contour. */
1583 /* */
1584 /* flipped :: If set, flip the direction of the curve. */
1585 /* */
1586 /* <Return> */
1587 /* SUCCESS on success, FAILURE on error. */
1588 /* */
1589 static
1590 Bool Decompose_Curve( RAS_ARGS UShort first,
1591 UShort last,
1592 int flipped )
1593 {
1594 FT_Vector v_last;
1595 FT_Vector v_control;
1596 FT_Vector v_start;
1597
1598 FT_Vector* points;
1599 FT_Vector* point;
1600 FT_Vector* limit;
1601 char* tags;
1602
1603 char tag; /* current point's state */
1604
1605
1606 points = ras.outline.points;
1607 limit = points + last;
1608
1609 v_start.x = SCALED( points[first].x );
1610 v_start.y = SCALED( points[first].y );
1611 v_last.x = SCALED( points[last].x );
1612 v_last.y = SCALED( points[last].y );
1613
1614 if ( flipped )
1615 {
1616 SWAP_( v_start.x, v_start.y );
1617 SWAP_( v_last.x, v_last.y );
1618 }
1619
1620 v_control = v_start;
1621
1622 point = points + first;
1623 tags = ras.outline.tags + first;
1624 tag = FT_CURVE_TAG( tags[0] );
1625
1626 /* A contour cannot start with a cubic control point! */
1627 if ( tag == FT_Curve_Tag_Cubic )
1628 goto Invalid_Outline;
1629
1630 /* check first point to determine origin */
1631 if ( tag == FT_Curve_Tag_Conic )
1632 {
1633 /* first point is conic control. Yes, this happens. */
1634 if ( FT_CURVE_TAG( ras.outline.tags[last] ) == FT_Curve_Tag_On )
1635 {
1636 /* start at last point if it is on the curve */
1637 v_start = v_last;
1638 limit--;
1639 }
1640 else
1641 {
1642 /* if both first and last points are conic, */
1643 /* start at their middle and record its position */
1644 /* for closure */
1645 v_start.x = ( v_start.x + v_last.x ) / 2;
1646 v_start.y = ( v_start.y + v_last.y ) / 2;
1647
1648 v_last = v_start;
1649 }
1650 point--;
1651 tags--;
1652 }
1653
1654 ras.lastX = v_start.x;
1655 ras.lastY = v_start.y;
1656
1657 while ( point < limit )
1658 {
1659 point++;
1660 tags++;
1661
1662 tag = FT_CURVE_TAG( tags[0] );
1663
1664 switch ( tag )
1665 {
1666 case FT_Curve_Tag_On: /* emit a single line_to */
1667 {
1668 Long x, y;
1669
1670
1671 x = SCALED( point->x );
1672 y = SCALED( point->y );
1673 if ( flipped )
1674 SWAP_( x, y );
1675
1676 if ( Line_To( RAS_VARS x, y ) )
1677 goto Fail;
1678 continue;
1679 }
1680
1681 case FT_Curve_Tag_Conic: /* consume conic arcs */
1682 v_control.x = SCALED( point[0].x );
1683 v_control.y = SCALED( point[0].y );
1684
1685 if ( flipped )
1686 SWAP_( v_control.x, v_control.y );
1687
1688 Do_Conic:
1689 if ( point < limit )
1690 {
1691 FT_Vector v_middle;
1692 Long x, y;
1693
1694
1695 point++;
1696 tags++;
1697 tag = FT_CURVE_TAG( tags[0] );
1698
1699 x = SCALED( point[0].x );
1700 y = SCALED( point[0].y );
1701
1702 if ( flipped )
1703 SWAP_( x, y );
1704
1705 if ( tag == FT_Curve_Tag_On )
1706 {
1707 if ( Conic_To( RAS_VARS v_control.x, v_control.y, x, y ) )
1708 goto Fail;
1709 continue;
1710 }
1711
1712 if ( tag != FT_Curve_Tag_Conic )
1713 goto Invalid_Outline;
1714
1715 v_middle.x = ( v_control.x + x ) / 2;
1716 v_middle.y = ( v_control.y + y ) / 2;
1717
1718 if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1719 v_middle.x, v_middle.y ) )
1720 goto Fail;
1721
1722 v_control.x = x;
1723 v_control.y = y;
1724
1725 goto Do_Conic;
1726 }
1727
1728 if ( Conic_To( RAS_VARS v_control.x, v_control.y,
1729 v_start.x, v_start.y ) )
1730 goto Fail;
1731
1732 goto Close;
1733
1734 default: /* FT_Curve_Tag_Cubic */
1735 {
1736 Long x1, y1, x2, y2, x3, y3;
1737
1738
1739 if ( point + 1 > limit ||
1740 FT_CURVE_TAG( tags[1] ) != FT_Curve_Tag_Cubic )
1741 goto Invalid_Outline;
1742
1743 point += 2;
1744 tags += 2;
1745
1746 x1 = SCALED( point[-2].x );
1747 y1 = SCALED( point[-2].y );
1748 x2 = SCALED( point[-1].x );
1749 y2 = SCALED( point[-1].y );
1750 x3 = SCALED( point[ 0].x );
1751 y3 = SCALED( point[ 0].y );
1752
1753 if ( flipped )
1754 {
1755 SWAP_( x1, y1 );
1756 SWAP_( x2, y2 );
1757 SWAP_( x3, y3 );
1758 }
1759
1760 if ( point <= limit )
1761 {
1762 if ( Cubic_To( RAS_VARS x1, y1, x2, y2, x3, y3 ) )
1763 goto Fail;
1764 continue;
1765 }
1766
1767 if ( Cubic_To( RAS_VARS x1, y1, x2, y2, v_start.x, v_start.y ) )
1768 goto Fail;
1769 goto Close;
1770 }
1771 }
1772 }
1773
1774 /* close the contour with a line segment */
1775 if ( Line_To( RAS_VARS v_start.x, v_start.y ) )
1776 goto Fail;
1777
1778 Close:
1779 return SUCCESS;
1780
1781 Invalid_Outline:
1782 ras.error = Raster_Err_Invalid;
1783
1784 Fail:
1785 return FAILURE;
1786 }
1787
1788
1789 /*************************************************************************/
1790 /* */
1791 /* <Function> */
1792 /* Convert_Glyph */
1793 /* */
1794 /* <Description> */
1795 /* Converts a glyph into a series of segments and arcs and makes a */
1796 /* profiles list with them. */
1797 /* */
1798 /* <Input> */
1799 /* flipped :: If set, flip the direction of curve. */
1800 /* */
1801 /* <Return> */
1802 /* SUCCESS on success, FAILURE if any error was encountered during */
1803 /* rendering. */
1804 /* */
1805 static
1806 Bool Convert_Glyph( RAS_ARGS int flipped )
1807 {
1808 Short i;
1809 UShort start;
1810
1811 PProfile lastProfile;
1812
1813
1814 ras.fProfile = NULL;
1815 ras.joint = FALSE;
1816 ras.fresh = FALSE;
1817
1818 ras.maxBuff = ras.sizeBuff - AlignProfileSize;
1819
1820 ras.numTurns = 0;
1821
1822 ras.cProfile = (PProfile)ras.top;
1823 ras.cProfile->offset = ras.top;
1824 ras.num_Profs = 0;
1825
1826 start = 0;
1827
1828 for ( i = 0; i < ras.outline.n_contours; i++ )
1829 {
1830 ras.state = Unknown;
1831 ras.gProfile = NULL;
1832
1833 if ( Decompose_Curve( RAS_VARS start, ras.outline.contours[i], flipped ) )
1834 return FAILURE;
1835
1836 start = ras.outline.contours[i] + 1;
1837
1838 /* We must now see whether the extreme arcs join or not */
1839 if ( FRAC( ras.lastY ) == 0 &&
1840 ras.lastY >= ras.minY &&
1841 ras.lastY <= ras.maxY )
1842 if ( ras.gProfile && ras.gProfile->flow == ras.cProfile->flow )
1843 ras.top--;
1844 /* Note that ras.gProfile can be nil if the contour was too small */
1845 /* to be drawn. */
1846
1847 lastProfile = ras.cProfile;
1848 if ( End_Profile( RAS_VAR ) )
1849 return FAILURE;
1850
1851 /* close the `next profile in contour' linked list */
1852 if ( ras.gProfile )
1853 lastProfile->next = ras.gProfile;
1854 }
1855
1856 if ( Finalize_Profile_Table( RAS_VAR ) )
1857 return FAILURE;
1858
1859 return ( ras.top < ras.maxBuff ? SUCCESS : FAILURE );
1860 }
1861
1862
1863 /*************************************************************************/
1864 /*************************************************************************/
1865 /** **/
1866 /** SCAN-LINE SWEEPS AND DRAWING **/
1867 /** **/
1868 /*************************************************************************/
1869 /*************************************************************************/
1870
1871
1872 /*************************************************************************/
1873 /* */
1874 /* Init_Linked */
1875 /* */
1876 /* Initializes an empty linked list. */
1877 /* */
1878 static
1879 void Init_Linked( TProfileList* l )
1880 {
1881 *l = NULL;
1882 }
1883
1884
1885 /*************************************************************************/
1886 /* */
1887 /* InsNew */
1888 /* */
1889 /* Inserts a new profile in a linked list. */
1890 /* */
1891 static
1892 void InsNew( PProfileList list,
1893 PProfile profile )
1894 {
1895 PProfile *old, current;
1896 Long x;
1897
1898
1899 old = list;
1900 current = *old;
1901 x = profile->X;
1902
1903 while ( current )
1904 {
1905 if ( x < current->X )
1906 break;
1907 old = &current->link;
1908 current = *old;
1909 }
1910
1911 profile->link = current;
1912 *old = profile;
1913 }
1914
1915
1916 /*************************************************************************/
1917 /* */
1918 /* DelOld */
1919 /* */
1920 /* Removes an old profile from a linked list. */
1921 /* */
1922 static
1923 void DelOld( PProfileList list,
1924 PProfile profile )
1925 {
1926 PProfile *old, current;
1927
1928
1929 old = list;
1930 current = *old;
1931
1932 while ( current )
1933 {
1934 if ( current == profile )
1935 {
1936 *old = current->link;
1937 return;
1938 }
1939
1940 old = &current->link;
1941 current = *old;
1942 }
1943
1944 /* we should never get there, unless the profile was not part of */
1945 /* the list. */
1946 }
1947
1948
1949 /*************************************************************************/
1950 /* */
1951 /* Update */
1952 /* */
1953 /* Update all X offsets of a drawing list. */
1954 /* */
1955 static
1956 void Update( PProfile first )
1957 {
1958 PProfile current = first;
1959
1960
1961 while ( current )
1962 {
1963 current->X = *current->offset;
1964 current->offset += current->flow;
1965 current->height--;
1966 current = current->link;
1967 }
1968 }
1969
1970
1971 /*************************************************************************/
1972 /* */
1973 /* Sort */
1974 /* */
1975 /* Sorts a trace list. In 95%, the list is already sorted. We need */
1976 /* an algorithm which is fast in this case. Bubble sort is enough */
1977 /* and simple. */
1978 /* */
1979 static
1980 void Sort( PProfileList list )
1981 {
1982 PProfile *old, current, next;
1983
1984
1985 /* First, set the new X coordinate of each profile */
1986 Update( *list );
1987
1988 /* Then sort them */
1989 old = list;
1990 current = *old;
1991
1992 if ( !current )
1993 return;
1994
1995 next = current->link;
1996
1997 while ( next )
1998 {
1999 if ( current->X <= next->X )
2000 {
2001 old = &current->link;
2002 current = *old;
2003
2004 if ( !current )
2005 return;
2006 }
2007 else
2008 {
2009 *old = next;
2010 current->link = next->link;
2011 next->link = current;
2012
2013 old = list;
2014 current = *old;
2015 }
2016
2017 next = current->link;
2018 }
2019 }
2020
2021
2022 /*************************************************************************/
2023 /* */
2024 /* Vertical Sweep Procedure Set */
2025 /* */
2026 /* These four routines are used during the vertical black/white sweep */
2027 /* phase by the generic Draw_Sweep() function. */
2028 /* */
2029 /*************************************************************************/
2030
2031 static
2032 void Vertical_Sweep_Init( RAS_ARGS Short* min,
2033 Short* max )
2034 {
2035 Long pitch = ras.target.pitch;
2036
2037 FT_UNUSED( max );
2038
2039
2040 ras.traceIncr = (Short)-pitch;
2041 ras.traceOfs = -*min * pitch;
2042 if ( pitch > 0 )
2043 ras.traceOfs += ( ras.target.rows - 1 ) * pitch;
2044
2045 ras.gray_min_x = 0;
2046 ras.gray_max_x = 0;
2047 }
2048
2049
2050 static
2051 void Vertical_Sweep_Span( RAS_ARGS Short y,
2052 FT_F26Dot6 x1,
2053 FT_F26Dot6 x2,
2054 PProfile left,
2055 PProfile right )
2056 {
2057 Long e1, e2;
2058 Short c1, c2;
2059 Byte f1, f2;
2060 Byte* target;
2061
2062 FT_UNUSED( y );
2063 FT_UNUSED( left );
2064 FT_UNUSED( right );
2065
2066
2067 /* Drop-out control */
2068
2069 e1 = TRUNC( CEILING( x1 ) );
2070
2071 if ( x2 - x1 - ras.precision <= ras.precision_jitter )
2072 e2 = e1;
2073 else
2074 e2 = TRUNC( FLOOR( x2 ) );
2075
2076 if ( e2 >= 0 && e1 < ras.bWidth )
2077 {
2078 if ( e1 < 0 )
2079 e1 = 0;
2080 if ( e2 >= ras.bWidth )
2081 e2 = ras.bWidth - 1;
2082
2083 c1 = (Short)( e1 >> 3 );
2084 c2 = (Short)( e2 >> 3 );
2085
2086 f1 = (unsigned char)0xFF >> ( e1 & 7 );
2087 f2 = ~( (unsigned char)0x7F >> ( e2 & 7 ) );
2088
2089 if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
2090 if ( ras.gray_max_x < c2 ) ras.gray_max_x = c2;
2091
2092 target = ras.bTarget + ras.traceOfs + c1;
2093 c2 -= c1;
2094
2095 if ( c2 > 0 )
2096 {
2097 target[0] |= f1;
2098
2099 /* memset() is slower than the following code on many platforms. */
2100 /* This is due to the fact that, in the vast majority of cases, */
2101 /* the span length in bytes is relatively small. */
2102 c2--;
2103 while ( c2 > 0 )
2104 {
2105 *(++target) = 0xFF;
2106 c2--;
2107 }
2108 target[1] |= f2;
2109 }
2110 else
2111 *target |= ( f1 & f2 );
2112 }
2113 }
2114
2115
2116 static
2117 void Vertical_Sweep_Drop( RAS_ARGS Short y,
2118 FT_F26Dot6 x1,
2119 FT_F26Dot6 x2,
2120 PProfile left,
2121 PProfile right )
2122 {
2123 Long e1, e2;
2124 Short c1, f1;
2125
2126
2127 /* Drop-out control */
2128
2129 e1 = CEILING( x1 );
2130 e2 = FLOOR ( x2 );
2131
2132 if ( e1 > e2 )
2133 {
2134 if ( e1 == e2 + ras.precision )
2135 {
2136 switch ( ras.dropOutControl )
2137 {
2138 case 1:
2139 e1 = e2;
2140 break;
2141
2142 case 4:
2143 e1 = CEILING( (x1 + x2 + 1) / 2 );
2144 break;
2145
2146 case 2:
2147 case 5:
2148 /* Drop-out Control Rule #4 */
2149
2150 /* The spec is not very clear regarding rule #4. It */
2151 /* presents a method that is way too costly to implement */
2152 /* while the general idea seems to get rid of `stubs'. */
2153 /* */
2154 /* Here, we only get rid of stubs recognized if: */
2155 /* */
2156 /* upper stub: */
2157 /* */
2158 /* - P_Left and P_Right are in the same contour */
2159 /* - P_Right is the successor of P_Left in that contour */
2160 /* - y is the top of P_Left and P_Right */
2161 /* */
2162 /* lower stub: */
2163 /* */
2164 /* - P_Left and P_Right are in the same contour */
2165 /* - P_Left is the successor of P_Right in that contour */
2166 /* - y is the bottom of P_Left */
2167 /* */
2168
2169 /* FIXXXME: uncommenting this line solves the disappearing */
2170 /* bit problem in the `7' of verdana 10pts, but */
2171 /* makes a new one in the `C' of arial 14pts */
2172
2173 #if 0
2174 if ( x2 - x1 < ras.precision_half )
2175 #endif
2176 {
2177 /* upper stub test */
2178 if ( left->next == right && left->height <= 0 )
2179 return;
2180
2181 /* lower stub test */
2182 if ( right->next == left && left->start == y )
2183 return;
2184 }
2185
2186 /* check that the rightmost pixel isn't set */
2187
2188 e1 = TRUNC( e1 );
2189
2190 c1 = (Short)( e1 >> 3 );
2191 f1 = e1 & 7;
2192
2193 if ( e1 >= 0 && e1 < ras.bWidth &&
2194 ras.bTarget[ras.traceOfs + c1] & ( 0x80 >> f1 ) )
2195 return;
2196
2197 if ( ras.dropOutControl == 2 )
2198 e1 = e2;
2199 else
2200 e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
2201
2202 break;
2203
2204 default:
2205 return; /* unsupported mode */
2206 }
2207 }
2208 else
2209 return;
2210 }
2211
2212 e1 = TRUNC( e1 );
2213
2214 if ( e1 >= 0 && e1 < ras.bWidth )
2215 {
2216 c1 = (Short)( e1 >> 3 );
2217 f1 = e1 & 7;
2218
2219 if ( ras.gray_min_x > c1 ) ras.gray_min_x = c1;
2220 if ( ras.gray_max_x < c1 ) ras.gray_max_x = c1;
2221
2222 ras.bTarget[ras.traceOfs + c1] |= (char)( 0x80 >> f1 );
2223 }
2224 }
2225
2226
2227 static
2228 void Vertical_Sweep_Step( RAS_ARG )
2229 {
2230 ras.traceOfs += ras.traceIncr;
2231 }
2232
2233
2234 /***********************************************************************/
2235 /* */
2236 /* Horizontal Sweep Procedure Set */
2237 /* */
2238 /* These four routines are used during the horizontal black/white */
2239 /* sweep phase by the generic Draw_Sweep() function. */
2240 /* */
2241 /***********************************************************************/
2242
2243 static
2244 void Horizontal_Sweep_Init( RAS_ARGS Short* min,
2245 Short* max )
2246 {
2247 /* nothing, really */
2248 FT_UNUSED( raster );
2249 FT_UNUSED( min );
2250 FT_UNUSED( max );
2251 }
2252
2253
2254 static
2255 void Horizontal_Sweep_Span( RAS_ARGS Short y,
2256 FT_F26Dot6 x1,
2257 FT_F26Dot6 x2,
2258 PProfile left,
2259 PProfile right )
2260 {
2261 Long e1, e2;
2262 PByte bits;
2263 Byte f1;
2264
2265 FT_UNUSED( left );
2266 FT_UNUSED( right );
2267
2268
2269 if ( x2 - x1 < ras.precision )
2270 {
2271 e1 = CEILING( x1 );
2272 e2 = FLOOR ( x2 );
2273
2274 if ( e1 == e2 )
2275 {
2276 bits = ras.bTarget + ( y >> 3 );
2277 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2278
2279 e1 = TRUNC( e1 );
2280
2281 if ( e1 >= 0 && e1 < ras.target.rows )
2282 {
2283 PByte p;
2284
2285
2286 p = bits - e1*ras.target.pitch;
2287 if ( ras.target.pitch > 0 )
2288 p += ( ras.target.rows - 1 ) * ras.target.pitch;
2289
2290 p[0] |= f1;
2291 }
2292 }
2293 }
2294 }
2295
2296
2297 static
2298 void Horizontal_Sweep_Drop( RAS_ARGS Short y,
2299 FT_F26Dot6 x1,
2300 FT_F26Dot6 x2,
2301 PProfile left,
2302 PProfile right )
2303 {
2304 Long e1, e2;
2305 PByte bits;
2306 Byte f1;
2307
2308
2309 /* During the horizontal sweep, we only take care of drop-outs */
2310
2311 e1 = CEILING( x1 );
2312 e2 = FLOOR ( x2 );
2313
2314 if ( e1 > e2 )
2315 {
2316 if ( e1 == e2 + ras.precision )
2317 {
2318 switch ( ras.dropOutControl )
2319 {
2320 case 1:
2321 e1 = e2;
2322 break;
2323
2324 case 4:
2325 e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
2326 break;
2327
2328 case 2:
2329 case 5:
2330
2331 /* Drop-out Control Rule #4 */
2332
2333 /* The spec is not very clear regarding rule #4. It */
2334 /* presents a method that is way too costly to implement */
2335 /* while the general idea seems to get rid of `stubs'. */
2336 /* */
2337
2338 /* rightmost stub test */
2339 if ( left->next == right && left->height <= 0 )
2340 return;
2341
2342 /* leftmost stub test */
2343 if ( right->next == left && left->start == y )
2344 return;
2345
2346 /* check that the rightmost pixel isn't set */
2347
2348 e1 = TRUNC( e1 );
2349
2350 bits = ras.bTarget + ( y >> 3 );
2351 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2352
2353 bits -= e1 * ras.target.pitch;
2354 if ( ras.target.pitch > 0 )
2355 bits += ( ras.target.rows - 1 ) * ras.target.pitch;
2356
2357 if ( e1 >= 0 &&
2358 e1 < ras.target.rows &&
2359 *bits & f1 )
2360 return;
2361
2362 if ( ras.dropOutControl == 2 )
2363 e1 = e2;
2364 else
2365 e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
2366
2367 break;
2368
2369 default:
2370 return; /* unsupported mode */
2371 }
2372 }
2373 else
2374 return;
2375 }
2376
2377 bits = ras.bTarget + ( y >> 3 );
2378 f1 = (Byte)( 0x80 >> ( y & 7 ) );
2379
2380 e1 = TRUNC( e1 );
2381
2382 if ( e1 >= 0 && e1 < ras.target.rows )
2383 {
2384 bits -= e1 * ras.target.pitch;
2385 if ( ras.target.pitch > 0 )
2386 bits += ( ras.target.rows - 1 ) * ras.target.pitch;
2387
2388 bits[0] |= f1;
2389 }
2390 }
2391
2392
2393 static
2394 void Horizontal_Sweep_Step( RAS_ARG )
2395 {
2396 /* Nothing, really */
2397 FT_UNUSED( raster );
2398 }
2399
2400
2401 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
2402
2403
2404 /*************************************************************************/
2405 /* */
2406 /* Vertical Gray Sweep Procedure Set */
2407 /* */
2408 /* These two routines are used during the vertical gray-levels sweep */
2409 /* phase by the generic Draw_Sweep() function. */
2410 /* */
2411 /* NOTES */
2412 /* */
2413 /* - The target pixmap's width *must* be a multiple of 4. */
2414 /* */
2415 /* - You have to use the function Vertical_Sweep_Span() for the gray */
2416 /* span call. */
2417 /* */
2418 /*************************************************************************/
2419
2420 static
2421 void Vertical_Gray_Sweep_Init( RAS_ARGS Short* min,
2422 Short* max )
2423 {
2424 Long pitch, byte_len;
2425
2426
2427 *min = *min & -2;
2428 *max = ( *max + 3 ) & -2;
2429
2430 ras.traceOfs = 0;
2431 pitch = ras.target.pitch;
2432 byte_len = -pitch;
2433 ras.traceIncr = (Short)byte_len;
2434 ras.traceG = ( *min / 2 ) * byte_len;
2435
2436 if ( pitch > 0 )
2437 {
2438 ras.traceG += ( ras.target.rows - 1 ) * pitch;
2439 byte_len = -byte_len;
2440 }
2441
2442 ras.gray_min_x = (Short)byte_len;
2443 ras.gray_max_x = -(Short)byte_len;
2444 }
2445
2446
2447 static
2448 void Vertical_Gray_Sweep_Step( RAS_ARG )
2449 {
2450 Int c1, c2;
2451 PByte pix, bit, bit2;
2452 Int* count = ras.count_table;
2453 Byte* grays;
2454
2455
2456 ras.traceOfs += ras.gray_width;
2457
2458 if ( ras.traceOfs > ras.gray_width )
2459 {
2460 pix = ras.gTarget + ras.traceG + ras.gray_min_x * 4;
2461 grays = ras.grays;
2462
2463 if ( ras.gray_max_x >= 0 )
2464 {
2465 Long last_pixel = ras.target.width - 1;
2466 Int last_cell = last_pixel >> 2;
2467 Int last_bit = last_pixel & 3;
2468 Bool over = 0;
2469
2470
2471 if ( ras.gray_max_x >= last_cell && last_bit != 3 )
2472 {
2473 ras.gray_max_x = last_cell - 1;
2474 over = 1;
2475 }
2476
2477 if ( ras.gray_min_x < 0 )
2478 ras.gray_min_x = 0;
2479
2480 bit = ras.bTarget + ras.gray_min_x;
2481 bit2 = bit + ras.gray_width;
2482
2483 c1 = ras.gray_max_x - ras.gray_min_x;
2484
2485 while ( c1 >= 0 )
2486 {
2487 c2 = count[*bit] + count[*bit2];
2488
2489 if ( c2 )
2490 {
2491 pix[0] = grays[(c2 >> 12) & 0x000F];
2492 pix[1] = grays[(c2 >> 8 ) & 0x000F];
2493 pix[2] = grays[(c2 >> 4 ) & 0x000F];
2494 pix[3] = grays[ c2 & 0x000F];
2495
2496 *bit = 0;
2497 *bit2 = 0;
2498 }
2499
2500 bit++;
2501 bit2++;
2502 pix += 4;
2503 c1--;
2504 }
2505
2506 if ( over )
2507 {
2508 c2 = count[*bit] + count[*bit2];
2509 if ( c2 )
2510 {
2511 switch ( last_bit )
2512 {
2513 case 2:
2514 pix[2] = grays[(c2 >> 4 ) & 0x000F];
2515 case 1:
2516 pix[1] = grays[(c2 >> 8 ) & 0x000F];
2517 default:
2518 pix[0] = grays[(c2 >> 12) & 0x000F];
2519 }
2520
2521 *bit = 0;
2522 *bit2 = 0;
2523 }
2524 }
2525 }
2526
2527 ras.traceOfs = 0;
2528 ras.traceG += ras.traceIncr;
2529
2530 ras.gray_min_x = 32000;
2531 ras.gray_max_x = -32000;
2532 }
2533 }
2534
2535
2536 static
2537 void Horizontal_Gray_Sweep_Span( RAS_ARGS Short y,
2538 FT_F26Dot6 x1,
2539 FT_F26Dot6 x2,
2540 PProfile left,
2541 PProfile right )
2542 {
2543 /* nothing, really */
2544 FT_UNUSED( raster );
2545 FT_UNUSED( y );
2546 FT_UNUSED( x1 );
2547 FT_UNUSED( x2 );
2548 FT_UNUSED( left );
2549 FT_UNUSED( right );
2550 }
2551
2552
2553 static
2554 void Horizontal_Gray_Sweep_Drop( RAS_ARGS Short y,
2555 FT_F26Dot6 x1,
2556 FT_F26Dot6 x2,
2557 PProfile left,
2558 PProfile right )
2559 {
2560 Long e1, e2;
2561 PByte pixel;
2562 Byte color;
2563
2564
2565 /* During the horizontal sweep, we only take care of drop-outs */
2566 e1 = CEILING( x1 );
2567 e2 = FLOOR ( x2 );
2568
2569 if ( e1 > e2 )
2570 {
2571 if ( e1 == e2 + ras.precision )
2572 {
2573 switch ( ras.dropOutControl )
2574 {
2575 case 1:
2576 e1 = e2;
2577 break;
2578
2579 case 4:
2580 e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
2581 break;
2582
2583 case 2:
2584 case 5:
2585
2586 /* Drop-out Control Rule #4 */
2587
2588 /* The spec is not very clear regarding rule #4. It */
2589 /* presents a method that is way too costly to implement */
2590 /* while the general idea seems to get rid of `stubs'. */
2591 /* */
2592
2593 /* rightmost stub test */
2594 if ( left->next == right && left->height <= 0 )
2595 return;
2596
2597 /* leftmost stub test */
2598 if ( right->next == left && left->start == y )
2599 return;
2600
2601 if ( ras.dropOutControl == 2 )
2602 e1 = e2;
2603 else
2604 e1 = CEILING( ( x1 + x2 + 1 ) / 2 );
2605
2606 break;
2607
2608 default:
2609 return; /* unsupported mode */
2610 }
2611 }
2612 else
2613 return;
2614 }
2615
2616 if ( e1 >= 0 )
2617 {
2618 if ( x2 - x1 >= ras.precision_half )
2619 color = ras.grays[2];
2620 else
2621 color = ras.grays[1];
2622
2623 e1 = TRUNC( e1 ) / 2;
2624 if ( e1 < ras.target.rows )
2625 {
2626 pixel = ras.gTarget - e1 * ras.target.pitch + y / 2;
2627 if ( ras.target.pitch > 0 )
2628 pixel += ( ras.target.rows - 1 ) * ras.target.pitch;
2629
2630 if ( pixel[0] == ras.grays[0] )
2631 pixel[0] = color;
2632 }
2633 }
2634 }
2635
2636
2637 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
2638
2639
2640 /*************************************************************************/
2641 /* */
2642 /* Generic Sweep Drawing routine */
2643 /* */
2644 /*************************************************************************/
2645
2646 static
2647 Bool Draw_Sweep( RAS_ARG )
2648 {
2649 Short y, y_change, y_height;
2650
2651 PProfile P, Q, P_Left, P_Right;
2652
2653 Short min_Y, max_Y, top, bottom, dropouts;
2654
2655 Long x1, x2, xs, e1, e2;
2656
2657 TProfileList wait;
2658 TProfileList draw_left, draw_right;
2659
2660
2661 /* Init empty linked lists */
2662
2663 Init_Linked( &wait );
2664
2665 Init_Linked( &draw_left );
2666 Init_Linked( &draw_right );
2667
2668 /* first, compute min and max Y */
2669
2670 P = ras.fProfile;
2671 max_Y = (Short)TRUNC( ras.minY );
2672 min_Y = (Short)TRUNC( ras.maxY );
2673
2674 while ( P )
2675 {
2676 Q = P->link;
2677
2678 bottom = (Short)P->start;
2679 top = (Short)P->start + P->height - 1;
2680
2681 if ( min_Y > bottom ) min_Y = bottom;
2682 if ( max_Y < top ) max_Y = top;
2683
2684 P->X = 0;
2685 InsNew( &wait, P );
2686
2687 P = Q;
2688 }
2689
2690 /* Check the Y-turns */
2691 if ( ras.numTurns == 0 )
2692 {
2693 ras.error = Raster_Err_Invalid;
2694 return FAILURE;
2695 }
2696
2697 /* Now inits the sweep */
2698
2699 ras.Proc_Sweep_Init( RAS_VARS &min_Y, &max_Y );
2700
2701 /* Then compute the distance of each profile from min_Y */
2702
2703 P = wait;
2704
2705 while ( P )
2706 {
2707 P->countL = P->start - min_Y;
2708 P = P->link;
2709 }
2710
2711 /* Let's go */
2712
2713 y = min_Y;
2714 y_height = 0;
2715
2716 if ( ras.numTurns > 0 &&
2717 ras.sizeBuff[-ras.numTurns] == min_Y )
2718 ras.numTurns--;
2719
2720 while ( ras.numTurns > 0 )
2721 {
2722 /* look in the wait list for new activations */
2723
2724 P = wait;
2725
2726 while ( P )
2727 {
2728 Q = P->link;
2729 P->countL -= y_height;
2730 if ( P->countL == 0 )
2731 {
2732 DelOld( &wait, P );
2733
2734 switch ( P->flow )
2735 {
2736 case Flow_Up:
2737 InsNew( &draw_left, P );
2738 break;
2739
2740 case Flow_Down:
2741 InsNew( &draw_right, P );
2742 break;
2743 }
2744 }
2745
2746 P = Q;
2747 }
2748
2749 /* Sort the drawing lists */
2750
2751 Sort( &draw_left );
2752 Sort( &draw_right );
2753
2754 y_change = (Short)ras.sizeBuff[-ras.numTurns--];
2755 y_height = y_change - y;
2756
2757 while ( y < y_change )
2758 {
2759 /* Let's trace */
2760
2761 dropouts = 0;
2762
2763 P_Left = draw_left;
2764 P_Right = draw_right;
2765
2766 while ( P_Left )
2767 {
2768 x1 = P_Left ->X;
2769 x2 = P_Right->X;
2770
2771 if ( x1 > x2 )
2772 {
2773 xs = x1;
2774 x1 = x2;
2775 x2 = xs;
2776 }
2777
2778 if ( x2 - x1 <= ras.precision )
2779 {
2780 e1 = FLOOR( x1 );
2781 e2 = CEILING( x2 );
2782
2783 if ( ras.dropOutControl != 0 &&
2784 ( e1 > e2 || e2 == e1 + ras.precision ) )
2785 {
2786 /* a drop out was detected */
2787
2788 P_Left ->X = x1;
2789 P_Right->X = x2;
2790
2791 /* mark profile for drop-out processing */
2792 P_Left->countL = 1;
2793 dropouts++;
2794
2795 goto Skip_To_Next;
2796 }
2797 }
2798
2799 ras.Proc_Sweep_Span( RAS_VARS y, x1, x2, P_Left, P_Right );
2800
2801 Skip_To_Next:
2802
2803 P_Left = P_Left->link;
2804 P_Right = P_Right->link;
2805 }
2806
2807 /* now perform the dropouts _after_ the span drawing -- */
2808 /* drop-outs processing has been moved out of the loop */
2809 /* for performance tuning */
2810 if ( dropouts > 0 )
2811 goto Scan_DropOuts;
2812
2813 Next_Line:
2814
2815 ras.Proc_Sweep_Step( RAS_VAR );
2816
2817 y++;
2818
2819 if ( y < y_change )
2820 {
2821 Sort( &draw_left );
2822 Sort( &draw_right );
2823 }
2824 }
2825
2826 /* Now finalize the profiles that needs it */
2827
2828 {
2829 PProfile Q, P;
2830
2831
2832 P = draw_left;
2833 while ( P )
2834 {
2835 Q = P->link;
2836 if ( P->height == 0 )
2837 DelOld( &draw_left, P );
2838 P = Q;
2839 }
2840 }
2841
2842 {
2843 PProfile Q, P = draw_right;
2844
2845
2846 while ( P )
2847 {
2848 Q = P->link;
2849 if ( P->height == 0 )
2850 DelOld( &draw_right, P );
2851 P = Q;
2852 }
2853 }
2854 }
2855
2856 /* for gray-scaling, flushes the bitmap scanline cache */
2857 while ( y <= max_Y )
2858 {
2859 ras.Proc_Sweep_Step( RAS_VAR );
2860 y++;
2861 }
2862
2863 return SUCCESS;
2864
2865 Scan_DropOuts:
2866
2867 P_Left = draw_left;
2868 P_Right = draw_right;
2869
2870 while ( P_Left )
2871 {
2872 if ( P_Left->countL )
2873 {
2874 P_Left->countL = 0;
2875 #if 0
2876 dropouts--; /* -- this is useful when debugging only */
2877 #endif
2878 ras.Proc_Sweep_Drop( RAS_VARS y,
2879 P_Left->X,
2880 P_Right->X,
2881 P_Left,
2882 P_Right );
2883 }
2884
2885 P_Left = P_Left->link;
2886 P_Right = P_Right->link;
2887 }
2888
2889 goto Next_Line;
2890 }
2891
2892
2893 /*************************************************************************/
2894 /* */
2895 /* <Function> */
2896 /* Render_Single_Pass */
2897 /* */
2898 /* <Description> */
2899 /* Performs one sweep with sub-banding. */
2900 /* */
2901 /* <Input> */
2902 /* flipped :: If set, flip the direction of the outline. */
2903 /* */
2904 /* <Return> */
2905 /* Renderer error code. */
2906 /* */
2907 static
2908 int Render_Single_Pass( RAS_ARGS Bool flipped )
2909 {
2910 Short i, j, k;
2911
2912
2913 while ( ras.band_top >= 0 )
2914 {
2915 ras.maxY = (Long)ras.band_stack[ras.band_top].y_max * ras.precision;
2916 ras.minY = (Long)ras.band_stack[ras.band_top].y_min * ras.precision;
2917
2918 ras.top = ras.buff;
2919
2920 ras.error = Raster_Err_None;
2921
2922 if ( Convert_Glyph( RAS_VARS flipped ) )
2923 {
2924 if ( ras.error != Raster_Err_Overflow )
2925 return FAILURE;
2926
2927 ras.error = Raster_Err_None;
2928
2929 /* sub-banding */
2930
2931 #ifdef DEBUG_RASTER
2932 ClearBand( RAS_VARS TRUNC( ras.minY ), TRUNC( ras.maxY ) );
2933 #endif
2934
2935 i = ras.band_stack[ras.band_top].y_min;
2936 j = ras.band_stack[ras.band_top].y_max;
2937
2938 k = ( i + j ) / 2;
2939
2940 if ( ras.band_top >= 7 || k < i )
2941 {
2942 ras.band_top = 0;
2943 ras.error = Raster_Err_Invalid;
2944
2945 return ras.error;
2946 }
2947
2948 ras.band_stack[ras.band_top + 1].y_min = k;
2949 ras.band_stack[ras.band_top + 1].y_max = j;
2950
2951 ras.band_stack[ras.band_top].y_max = k - 1;
2952
2953 ras.band_top++;
2954 }
2955 else
2956 {
2957 if ( ras.fProfile )
2958 if ( Draw_Sweep( RAS_VAR ) )
2959 return ras.error;
2960 ras.band_top--;
2961 }
2962 }
2963
2964 return SUCCESS;
2965 }
2966
2967
2968 /*************************************************************************/
2969 /* */
2970 /* <Function> */
2971 /* Render_Glyph */
2972 /* */
2973 /* <Description> */
2974 /* Renders a glyph in a bitmap. Sub-banding if needed. */
2975 /* */
2976 /* <Return> */
2977 /* FreeType error code. 0 means success. */
2978 /* */
2979 /* XXX Fixme: ftraster's error codes don't harmonize with FT2's ones! */
2980 /* */
2981 LOCAL_FUNC
2982 FT_Error Render_Glyph( RAS_ARG )
2983 {
2984 FT_Error error;
2985
2986
2987 Set_High_Precision( RAS_VARS ras.outline.flags &
2988 ft_outline_high_precision );
2989 ras.scale_shift = ras.precision_shift;
2990 ras.dropOutControl = 2;
2991 ras.second_pass = !( ras.outline.flags & ft_outline_single_pass );
2992
2993 /* Vertical Sweep */
2994 ras.Proc_Sweep_Init = Vertical_Sweep_Init;
2995 ras.Proc_Sweep_Span = Vertical_Sweep_Span;
2996 ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
2997 ras.Proc_Sweep_Step = Vertical_Sweep_Step;
2998
2999 ras.band_top = 0;
3000 ras.band_stack[0].y_min = 0;
3001 ras.band_stack[0].y_max = ras.target.rows - 1;
3002
3003 ras.bWidth = ras.target.width;
3004 ras.bTarget = (Byte*)ras.target.buffer;
3005
3006 if ( ( error = Render_Single_Pass( RAS_VARS 0 ) ) != 0 )
3007 return error;
3008
3009 /* Horizontal Sweep */
3010 if ( ras.second_pass && ras.dropOutControl != 0 )
3011 {
3012 ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3013 ras.Proc_Sweep_Span = Horizontal_Sweep_Span;
3014 ras.Proc_Sweep_Drop = Horizontal_Sweep_Drop;
3015 ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3016
3017 ras.band_top = 0;
3018 ras.band_stack[0].y_min = 0;
3019 ras.band_stack[0].y_max = ras.target.width - 1;
3020
3021 if ( ( error = Render_Single_Pass( RAS_VARS 1 ) ) != 0 )
3022 return error;
3023 }
3024
3025 return FT_Err_Ok;
3026 }
3027
3028
3029 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3030
3031
3032 /*************************************************************************/
3033 /* */
3034 /* <Function> */
3035 /* Render_Gray_Glyph */
3036 /* */
3037 /* <Description> */
3038 /* Renders a glyph with grayscaling. Sub-banding if needed. */
3039 /* */
3040 /* <Return> */
3041 /* FreeType error code. 0 means success. */
3042 /* */
3043 LOCAL_FUNC
3044 FT_Error Render_Gray_Glyph( RAS_ARG )
3045 {
3046 Long pixel_width;
3047 FT_Error error;
3048
3049
3050 Set_High_Precision( RAS_VARS ras.outline.flags &
3051 ft_outline_high_precision );
3052 ras.scale_shift = ras.precision_shift + 1;
3053 ras.dropOutControl = 2;
3054 ras.second_pass = !( ras.outline.flags & ft_outline_single_pass );
3055
3056 /* Vertical Sweep */
3057
3058 ras.band_top = 0;
3059 ras.band_stack[0].y_min = 0;
3060 ras.band_stack[0].y_max = 2 * ras.target.rows - 1;
3061
3062 ras.bWidth = ras.gray_width;
3063 pixel_width = 2 * ( ( ras.target.width + 3 ) >> 2 );
3064
3065 if ( ras.bWidth > pixel_width )
3066 ras.bWidth = pixel_width;
3067
3068 ras.bWidth = ras.bWidth * 8;
3069 ras.bTarget = (Byte*)ras.gray_lines;
3070 ras.gTarget = (Byte*)ras.target.buffer;
3071
3072 ras.Proc_Sweep_Init = Vertical_Gray_Sweep_Init;
3073 ras.Proc_Sweep_Span = Vertical_Sweep_Span;
3074 ras.Proc_Sweep_Drop = Vertical_Sweep_Drop;
3075 ras.Proc_Sweep_Step = Vertical_Gray_Sweep_Step;
3076
3077 error = Render_Single_Pass( RAS_VARS 0 );
3078 if ( error )
3079 return error;
3080
3081 /* Horizontal Sweep */
3082 if ( ras.second_pass && ras.dropOutControl != 0 )
3083 {
3084 ras.Proc_Sweep_Init = Horizontal_Sweep_Init;
3085 ras.Proc_Sweep_Span = Horizontal_Gray_Sweep_Span;
3086 ras.Proc_Sweep_Drop = Horizontal_Gray_Sweep_Drop;
3087 ras.Proc_Sweep_Step = Horizontal_Sweep_Step;
3088
3089 ras.band_top = 0;
3090 ras.band_stack[0].y_min = 0;
3091 ras.band_stack[0].y_max = ras.target.width * 2 - 1;
3092
3093 error = Render_Single_Pass( RAS_VARS 1 );
3094 if ( error )
3095 return error;
3096 }
3097
3098 return FT_Err_Ok;
3099 }
3100
3101 #else /* FT_RASTER_OPTION_ANTI_ALIASING */
3102
3103 LOCAL_FUNC
3104 FT_Error Render_Gray_Glyph( RAS_ARG )
3105 {
3106 FT_UNUSED_RASTER;
3107
3108 return FT_Err_Cannot_Render_Glyph;
3109 }
3110
3111 #endif /* FT_RASTER_OPTION_ANTI_ALIASING */
3112
3113
3114 static
3115 void ft_black_init( TRaster_Instance* raster )
3116 {
3117 FT_UInt n;
3118 FT_ULong c;
3119
3120
3121 /* setup count table */
3122 for ( n = 0; n < 256; n++ )
3123 {
3124 c = ( n & 0x55 ) + ( ( n & 0xAA ) >> 1 );
3125
3126 c = ( ( c << 6 ) & 0x3000 ) |
3127 ( ( c << 4 ) & 0x0300 ) |
3128 ( ( c << 2 ) & 0x0030 ) |
3129 (c & 0x0003 );
3130
3131 raster->count_table[n] = c;
3132 }
3133
3134 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3135
3136 /* set default 5-levels gray palette */
3137 for ( n = 0; n < 5; n++ )
3138 raster->grays[n] = n * 255 / 4;
3139
3140 raster->gray_width = RASTER_GRAY_LINES / 2;
3141
3142 #endif
3143 }
3144
3145
3146 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
3147 /**** a static object. *****/
3148
3149
3150 #ifdef _STANDALONE_
3151
3152
3153 static
3154 int ft_black_new( void* memory,
3155 FT_Raster *araster )
3156 {
3157 static FT_RasterRec_ the_raster;
3158
3159
3160 *araster = &the_raster;
3161 memset( &the_raster, sizeof ( the_raster ), 0 );
3162 ft_black_init( &the_raster );
3163
3164 return 0;
3165 }
3166
3167
3168 static
3169 void ft_black_done( FT_Raster raster )
3170 {
3171 /* nothing */
3172 raster->init = 0;
3173 }
3174
3175
3176 #else /* _STANDALONE_ */
3177
3178
3179 static
3180 int ft_black_new( FT_Memory memory,
3181 TRaster_Instance** araster )
3182 {
3183 FT_Error error;
3184 TRaster_Instance* raster;
3185
3186
3187 *araster = 0;
3188 if ( !ALLOC( raster, sizeof ( *raster ) ) )
3189 {
3190 raster->memory = memory;
3191 ft_black_init( raster );
3192
3193 *araster = raster;
3194 }
3195
3196 return error;
3197 }
3198
3199
3200 static
3201 void ft_black_done( TRaster_Instance* raster )
3202 {
3203 FT_Memory memory = (FT_Memory)raster->memory;
3204 FREE( raster );
3205 }
3206
3207
3208 #endif /* _STANDALONE_ */
3209
3210
3211 static
3212 void ft_black_reset( TRaster_Instance* raster,
3213 const char* pool_base,
3214 long pool_size )
3215 {
3216 if ( raster && pool_base && pool_size >= 4096 )
3217 {
3218 /* save the pool */
3219 raster->buff = (PLong)pool_base;
3220 raster->sizeBuff = raster->buff + pool_size / sizeof ( Long );
3221 }
3222 }
3223
3224
3225 static
3226 void ft_black_set_mode( TRaster_Instance* raster,
3227 unsigned long mode,
3228 const char* palette )
3229 {
3230 #ifdef FT_RASTER_OPTION_ANTI_ALIASING
3231
3232 if ( mode == FT_MAKE_TAG( 'p', 'a', 'l', '5' ) )
3233 {
3234 /* set 5-levels gray palette */
3235 raster->grays[0] = palette[0];
3236 raster->grays[1] = palette[1];
3237 raster->grays[2] = palette[2];
3238 raster->grays[3] = palette[3];
3239 raster->grays[4] = palette[4];
3240 }
3241
3242 #else
3243
3244 FT_UNUSED( raster );
3245 FT_UNUSED( mode );
3246 FT_UNUSED( palette );
3247
3248 #endif
3249 }
3250
3251
3252 static
3253 int ft_black_render( TRaster_Instance* raster,
3254 FT_Raster_Params* params )
3255 {
3256 FT_Outline* outline = (FT_Outline*)params->source;
3257 FT_Bitmap* target_map = params->target;
3258
3259
3260 if ( !raster || !raster->buff || !raster->sizeBuff )
3261 return Raster_Err_Not_Ini;
3262
3263 if ( !outline || !outline->contours || !outline->points )
3264 return Raster_Err_Invalid;
3265
3266 /* return immediately if the outline is empty */
3267 if ( outline->n_points == 0 || outline->n_contours <= 0 )
3268 return Raster_Err_None;
3269
3270 if ( outline->n_points != outline->contours[outline->n_contours - 1] + 1 )
3271 return Raster_Err_Invalid;
3272
3273 if ( !target_map || !target_map->buffer )
3274 return Raster_Err_Invalid;
3275
3276 /* this version of the raster does not support direct rendering, sorry */
3277 if ( params->flags & ft_raster_flag_direct )
3278 return Raster_Err_Unsupported;
3279
3280 ras.outline = *outline;
3281 ras.target = *target_map;
3282
3283 return ( ( params->flags & ft_raster_flag_aa )
3284 ? Render_Gray_Glyph( raster )
3285 : Render_Glyph( raster ) );
3286 }
3287
3288
3289 FT_Raster_Funcs ft_standard_raster =
3290 {
3291 ft_glyph_format_outline,
3292 (FT_Raster_New_Func) ft_black_new,
3293 (FT_Raster_Reset_Func) ft_black_reset,
3294 (FT_Raster_Set_Mode_Func)ft_black_set_mode,
3295 (FT_Raster_Render_Func) ft_black_render,
3296 (FT_Raster_Done_Func) ft_black_done
3297 };
3298
3299
3300 /* END */