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