blob: 6ac2608f7bcdf016aed9290b83fcdfc03b7a6df2 [file] [log] [blame]
David Turner5ae1bad2000-06-27 23:18:39 +00001/***************************************************************************/
2/* */
3/* ftgrays.c */
4/* */
5/* A new `perfect' anti-aliasing renderer (body). */
6/* */
7/* Copyright 2000 by */
8/* David Turner, Robert Wilhelm, and Werner Lemberg. */
9/* */
10/* This file is part of the FreeType project, and may only be used, */
11/* modified, and distributed under the terms of the FreeType project */
12/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
13/* this file you indicate that you have read the license and */
14/* understand and accept it fully. */
15/* */
16/***************************************************************************/
17
18 /*************************************************************************/
19 /* */
20 /* This file can be compiled without the rest of the FreeType engine, */
21 /* by defining the _STANDALONE_ macro when compiling it. You also need */
22 /* to put the files `ftgrays.h' and `ftimage.h' into the current */
23 /* compilation directory. Typically, you could do something like */
24 /* */
25 /* - copy `src/base/ftgrays.c' to your current directory */
26 /* */
27 /* - copy `include/freetype/ftimage.h' and */
28 /* `include/freetype/ftgrays.h' to the same directory */
29 /* */
30 /* - compile `ftgrays' with the _STANDALONE_ macro defined, as in */
31 /* */
32 /* cc -c -D_STANDALONE_ ftgrays.c */
33 /* */
34 /* The renderer can be initialized with a call to */
35 /* `ft_grays_raster.grays_raster_new'; an anti-aliased bitmap can be */
36 /* generated with a call to `ft_grays_raster.grays_raster_render'. */
37 /* */
38 /* See the comments and documentation in the file `ftimage.h' for */
39 /* more details on how the raster works. */
40 /* */
41 /*************************************************************************/
42
43 /*************************************************************************/
44 /* */
45 /* This is a new anti-aliasing scan-converter for FreeType 2. The */
46 /* algorithm used here is _very_ different from the one in the standard */
47 /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */
48 /* coverage of the outline on each pixel cell. */
49 /* */
50 /* It is based on ideas that I initially found in Raph Levien's */
51 /* excellent LibArt graphics library (see http://www.levien.com/libart */
52 /* for more information, though the web pages do not tell anything */
53 /* about the renderer; you'll have to dive into the source code to */
54 /* understand how it works). */
55 /* */
56 /* Note, however, that this is a _very_ different implementation */
Werner Lembergfbeb41d2000-07-02 00:27:53 +000057 /* compared to Raph's. Coverage information is stored in a very */
58 /* different way, and I don't use sorted vector paths. Also, it */
59 /* doesn't use floating point values. */
David Turner5ae1bad2000-06-27 23:18:39 +000060 /* */
61 /* This renderer has the following advantages: */
62 /* */
63 /* - It doesn't need an intermediate bitmap. Instead, one can supply */
64 /* a callback function that will be called by the renderer to draw */
65 /* gray spans on any target surface. You can thus do direct */
66 /* composition on any kind of bitmap, provided that you give the */
67 /* renderer the right callback. */
68 /* */
69 /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */
70 /* each pixel cell */
71 /* */
72 /* - It performs a single pass on the outline (the `standard' FT2 */
73 /* renderer makes two passes). */
74 /* */
75 /* - It can easily be modified to render to _any_ number of gray levels */
76 /* cheaply. */
77 /* */
78 /* - For small (< 20) pixel sizes, it is faster than the standard */
79 /* renderer. */
80 /* */
81 /*************************************************************************/
82
83
84#include <string.h> /* for memcpy() */
85
86
87 /*************************************************************************/
88 /* */
89 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
90 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
91 /* messages during execution. */
92 /* */
93#undef FT_COMPONENT
94#define FT_COMPONENT trace_aaraster
95
96
David Turner5ae1bad2000-06-27 23:18:39 +000097#ifdef _STANDALONE_
98
Werner Lembergb48a6092000-07-09 19:15:30 +000099
David Turner5ae1bad2000-06-27 23:18:39 +0000100#define ErrRaster_Invalid_Mode -2
Werner Lembergb48a6092000-07-09 19:15:30 +0000101#define ErrRaster_Invalid_Outline -1
David Turner5ae1bad2000-06-27 23:18:39 +0000102
103#include "ftimage.h"
104#include "ftgrays.h"
105
106 /* This macro is used to indicate that a function parameter is unused. */
107 /* Its purpose is simply to reduce compiler warnings. Note also that */
108 /* simply defining it as `(void)x' doesn't avoid warnings with certain */
109 /* ANSI compilers (e.g. LCC). */
David Turnerc6a92202000-07-04 18:12:13 +0000110#define FT_UNUSED( x ) (x) = (x)
David Turner5ae1bad2000-06-27 23:18:39 +0000111
112 /* Disable the tracing mechanism for simplicity -- developers can */
113 /* activate it easily by redefining these two macros. */
114#ifndef FT_ERROR
115#define FT_ERROR( x ) do ; while ( 0 ) /* nothing */
116#endif
117
118#ifndef FT_TRACE
119#define FT_TRACE( x ) do ; while ( 0 ) /* nothing */
120#endif
121
122
123#else /* _STANDALONE_ */
124
Werner Lemberg63408a12000-12-13 23:44:37 +0000125#include <ft2build.h>
David Turner8d3a4012001-03-20 11:14:24 +0000126#include "ftgrays.h"
Werner Lemberg63408a12000-12-13 23:44:37 +0000127#include FT_INTERNAL_OBJECTS_H
128#include FT_INTERNAL_DEBUG_H
129#include FT_OUTLINE_H
David Turner5ae1bad2000-06-27 23:18:39 +0000130
131#define ErrRaster_Invalid_Mode FT_Err_Cannot_Render_Glyph
Werner Lembergb48a6092000-07-09 19:15:30 +0000132#define ErrRaster_Invalid_Outline FT_Err_Invalid_Outline
David Turner5ae1bad2000-06-27 23:18:39 +0000133
134
135#endif /* _STANDALONE_ */
136
137
138 /* define this to dump debugging information */
139#define xxxDEBUG_GRAYS
140
141 /* as usual, for the speed hungry :-) */
142
143#ifndef FT_STATIC_RASTER
144
145
146#define RAS_ARG PRaster raster
147#define RAS_ARG_ PRaster raster,
148
149#define RAS_VAR raster
150#define RAS_VAR_ raster,
151
152#define ras (*raster)
153
154
155#else /* FT_STATIC_RASTER */
156
157
158#define RAS_ARG /* empty */
159#define RAS_ARG_ /* empty */
160#define RAS_VAR /* empty */
161#define RAS_VAR_ /* empty */
162
163 static TRaster ras;
164
165
166#endif /* FT_STATIC_RASTER */
167
168
169 /* must be at least 6 bits! */
170#define PIXEL_BITS 8
171
172#define ONE_PIXEL ( 1L << PIXEL_BITS )
173#define PIXEL_MASK ( -1L << PIXEL_BITS )
174#define TRUNC( x ) ( (x) >> PIXEL_BITS )
175#define SUBPIXELS( x ) ( (x) << PIXEL_BITS )
176#define FLOOR( x ) ( (x) & -ONE_PIXEL )
177#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL )
178#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL )
179
180#if PIXEL_BITS >= 6
181#define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) )
182#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) )
183#else
184#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) )
185#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) )
186#endif
187
188 /* Define this if you want to use a more compact storage scheme. This */
189 /* increases the number of cells available in the render pool but slows */
190 /* down the rendering a bit. It is useful if you have a really tiny */
191 /* render pool. */
192#define xxxGRAYS_COMPACT
193
194
195 /*************************************************************************/
196 /* */
197 /* TYPE DEFINITIONS */
198 /* */
199 typedef int TScan; /* integer scanline/pixel coordinate */
200 typedef long TPos; /* sub-pixel coordinate */
201
202 /* maximal number of gray spans in a call to the span callback */
203#define FT_MAX_GRAY_SPANS 32
204
205
206#ifdef GRAYS_COMPACT
207
208 typedef struct TCell_
209 {
210 short x : 14;
211 short y : 14;
212 int cover : PIXEL_BITS + 2;
213 int area : PIXEL_BITS * 2 + 2;
214
215 } TCell, *PCell;
216
217#else /* GRAYS_COMPACT */
218
219 typedef struct TCell_
220 {
221 TScan x;
222 TScan y;
223 int cover;
224 int area;
225
226 } TCell, *PCell;
227
228#endif /* GRAYS_COMPACT */
229
230
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000231 typedef struct TRaster_
David Turner5ae1bad2000-06-27 23:18:39 +0000232 {
233 PCell cells;
234 int max_cells;
235 int num_cells;
236
237 TScan min_ex, max_ex;
238 TScan min_ey, max_ey;
239
240 int area;
241 int cover;
242 int invalid;
243
244 TScan ex, ey;
245 TScan cx, cy;
246 TPos x, y;
247
248 TScan last_ey;
249
250 FT_Vector bez_stack[32 * 3];
251 int lev_stack[32];
252
253 FT_Outline outline;
254 FT_Bitmap target;
David Turner859a18a2000-12-14 18:50:40 +0000255 FT_BBox clip_box;
David Turner5ae1bad2000-06-27 23:18:39 +0000256
257 FT_Span gray_spans[FT_MAX_GRAY_SPANS];
258 int num_gray_spans;
259
260 FT_Raster_Span_Func render_span;
261 void* render_span_data;
262 int span_y;
263
264 int band_size;
265 int band_shoot;
266 int conic_level;
267 int cubic_level;
268
269 void* memory;
270
271 } TRaster, *PRaster;
272
273
274 /*************************************************************************/
275 /* */
276 /* Initialize the cells table. */
277 /* */
278 static
279 void init_cells( RAS_ARG_ void* buffer,
280 long byte_size )
281 {
282 ras.cells = (PCell)buffer;
283 ras.max_cells = byte_size / sizeof ( TCell );
284 ras.num_cells = 0;
285 ras.area = 0;
286 ras.cover = 0;
287 ras.invalid = 1;
288 }
289
290
291 /*************************************************************************/
292 /* */
293 /* Compute the outline bounding box. */
294 /* */
295 static
296 void compute_cbox( RAS_ARG_ FT_Outline* outline )
297 {
298 FT_Vector* vec = outline->points;
299 FT_Vector* limit = vec + outline->n_points;
300
301
302 if ( outline->n_points <= 0 )
303 {
304 ras.min_ex = ras.max_ex = 0;
305 ras.min_ey = ras.max_ey = 0;
306 return;
307 }
308
309 ras.min_ex = ras.max_ex = vec->x;
310 ras.min_ey = ras.max_ey = vec->y;
311
312 vec++;
313
314 for ( ; vec < limit; vec++ )
315 {
316 TPos x = vec->x;
317 TPos y = vec->y;
318
319
320 if ( x < ras.min_ex ) ras.min_ex = x;
321 if ( x > ras.max_ex ) ras.max_ex = x;
322 if ( y < ras.min_ey ) ras.min_ey = y;
323 if ( y > ras.max_ey ) ras.max_ey = y;
324 }
325
326 /* truncate the bounding box to integer pixels */
327 ras.min_ex = ras.min_ex >> 6;
328 ras.min_ey = ras.min_ey >> 6;
329 ras.max_ex = ( ras.max_ex + 63 ) >> 6;
330 ras.max_ey = ( ras.max_ey + 63 ) >> 6;
331 }
332
333
334 /*************************************************************************/
335 /* */
336 /* Record the current cell in the table. */
337 /* */
338 static
339 int record_cell( RAS_ARG )
340 {
341 PCell cell;
342
343
344 if ( !ras.invalid && ( ras.area | ras.cover ) )
345 {
346 if ( ras.num_cells >= ras.max_cells )
347 return 1;
348
349 cell = ras.cells + ras.num_cells++;
350 cell->x = ras.ex - ras.min_ex;
351 cell->y = ras.ey - ras.min_ey;
352 cell->area = ras.area;
353 cell->cover = ras.cover;
354 }
355
356 return 0;
357 }
358
359
360 /*************************************************************************/
361 /* */
362 /* Set the current cell to a new position. */
363 /* */
364 static
365 int set_cell( RAS_ARG_ TScan ex,
366 TScan ey )
367 {
368 int invalid, record, clean;
369
370
371 /* Move the cell pointer to a new position. We set the `invalid' */
372 /* flag to indicate that the cell isn't part of those we're interested */
373 /* in during the render phase. This means that: */
374 /* */
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000375 /* . the new vertical position must be within min_ey..max_ey-1. */
David Turner5ae1bad2000-06-27 23:18:39 +0000376 /* . the new horizontal position must be strictly less than max_ex */
377 /* */
378 /* Note that if a cell is to the left of the clipping region, it is */
379 /* actually set to the (min_ex-1) horizontal position. */
380
381 record = 0;
382 clean = 1;
383
384 invalid = ( ey < ras.min_ey || ey >= ras.max_ey || ex >= ras.max_ex );
385 if ( !invalid )
386 {
387 /* All cells that are on the left of the clipping region go to the */
388 /* min_ex - 1 horizontal position. */
389 if ( ex < ras.min_ex )
390 ex = ras.min_ex - 1;
391
392 /* if our position is new, then record the previous cell */
393 if ( ex != ras.ex || ey != ras.ey )
394 record = 1;
395 else
396 clean = ras.invalid; /* do not clean if we didn't move from */
397 /* a valid cell */
398 }
399
400 /* record the previous cell if needed (i.e., if we changed the cell */
401 /* position, of changed the `invalid' flag) */
402 if ( ( ras.invalid != invalid || record ) && record_cell( RAS_VAR ) )
403 return 1;
404
405 if ( clean )
406 {
407 ras.area = 0;
408 ras.cover = 0;
409 }
410
411 ras.invalid = invalid;
412 ras.ex = ex;
413 ras.ey = ey;
414 return 0;
415 }
416
417
418 /*************************************************************************/
419 /* */
420 /* Start a new contour at a given cell. */
421 /* */
422 static
423 void start_cell( RAS_ARG_ TScan ex,
424 TScan ey )
425 {
426 if ( ex < ras.min_ex )
427 ex = ras.min_ex - 1;
428
429 ras.area = 0;
430 ras.cover = 0;
431 ras.ex = ex;
432 ras.ey = ey;
433 ras.last_ey = SUBPIXELS( ey );
434 ras.invalid = 0;
435
436 (void)set_cell( RAS_VAR_ ex, ey );
437 }
438
439
440 /*************************************************************************/
441 /* */
442 /* Render a scanline as one or more cells. */
443 /* */
444 static
445 int render_scanline( RAS_ARG_ TScan ey,
446 TPos x1,
447 TScan y1,
448 TPos x2,
449 TScan y2 )
450 {
451 TScan ex1, ex2, fx1, fx2, delta;
452 long p, first, dx;
453 int incr, lift, mod, rem;
454
455
456 dx = x2 - x1;
457
458 ex1 = TRUNC( x1 ); /* if (ex1 >= ras.max_ex) ex1 = ras.max_ex-1; */
459 ex2 = TRUNC( x2 ); /* if (ex2 >= ras.max_ex) ex2 = ras.max_ex-1; */
460 fx1 = x1 - SUBPIXELS( ex1 );
461 fx2 = x2 - SUBPIXELS( ex2 );
462
463 /* trivial case. Happens often */
464 if ( y1 == y2 )
465 return set_cell( RAS_VAR_ ex2, ey );
466
467 /* everything is located in a single cell. That is easy! */
468 /* */
469 if ( ex1 == ex2 )
470 {
471 delta = y2 - y1;
472 ras.area += ( fx1 + fx2 ) * delta;
473 ras.cover += delta;
474 return 0;
475 }
476
477 /* ok, we'll have to render a run of adjacent cells on the same */
478 /* scanline... */
479 /* */
480 p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 );
481 first = ONE_PIXEL;
482 incr = 1;
483
484 if ( dx < 0 )
485 {
486 p = fx1 * ( y2 - y1 );
487 first = 0;
488 incr = -1;
489 dx = -dx;
490 }
491
492 delta = p / dx;
493 mod = p % dx;
494 if ( mod < 0 )
495 {
496 delta--;
497 mod += dx;
498 }
499
500 ras.area += ( fx1 + first ) * delta;
501 ras.cover += delta;
502
503 ex1 += incr;
504 if ( set_cell( RAS_VAR_ ex1, ey ) )
505 goto Error;
506 y1 += delta;
507
508 if ( ex1 != ex2 )
509 {
510 p = ONE_PIXEL * ( y2 - y1 );
511 lift = p / dx;
512 rem = p % dx;
513 if ( rem < 0 )
514 {
515 lift--;
516 rem += dx;
517 }
518
519 mod -= dx;
520
521 while ( ex1 != ex2 )
522 {
523 delta = lift;
524 mod += rem;
525 if ( mod >= 0 )
526 {
527 mod -= dx;
528 delta++;
529 }
530
531 ras.area += ONE_PIXEL * delta;
532 ras.cover += delta;
533 y1 += delta;
534 ex1 += incr;
535 if ( set_cell( RAS_VAR_ ex1, ey ) )
536 goto Error;
537 }
538 }
539
540 delta = y2 - y1;
541 ras.area += ( fx2 + ONE_PIXEL - first ) * delta;
542 ras.cover += delta;
543
544 return 0;
545
546 Error:
547 return 1;
548 }
549
550
551 /*************************************************************************/
552 /* */
553 /* Render a given line as a series of scanlines. */
554 /* */
555 static
556 int render_line( RAS_ARG_ TPos to_x,
557 TPos to_y )
558 {
559 TScan ey1, ey2, fy1, fy2;
560 TPos dx, dy, x, x2;
561 int p, rem, mod, lift, delta, first, incr;
562
563
564 ey1 = TRUNC( ras.last_ey );
565 ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */
566 fy1 = ras.y - ras.last_ey;
567 fy2 = to_y - SUBPIXELS( ey2 );
568
569 dx = to_x - ras.x;
570 dy = to_y - ras.y;
571
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000572 /* XXX: we should do something about the trivial case where dx == 0, */
573 /* as it happens very often! */
David Turner5ae1bad2000-06-27 23:18:39 +0000574
575 /* perform vertical clipping */
576 {
577 TScan min, max;
578
579
580 min = ey1;
581 max = ey2;
582 if ( ey1 > ey2 )
583 {
584 min = ey2;
585 max = ey1;
586 }
587 if ( min >= ras.max_ey || max < ras.min_ey )
588 goto End;
589 }
590
591 /* everything is on a single scanline */
592 if ( ey1 == ey2 )
593 {
594 if ( render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 ) )
595 goto Error;
596 goto End;
597 }
598
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000599 /* ok, we have to render several scanlines */
David Turner5ae1bad2000-06-27 23:18:39 +0000600 p = ( ONE_PIXEL - fy1 ) * dx;
601 first = ONE_PIXEL;
602 incr = 1;
603
604 if ( dy < 0 )
605 {
606 p = fy1 * dx;
607 first = 0;
608 incr = -1;
609 dy = -dy;
610 }
611
612 delta = p / dy;
613 mod = p % dy;
614 if ( mod < 0 )
615 {
616 delta--;
617 mod += dy;
618 }
619
620 x = ras.x + delta;
621 if ( render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, first ) )
622 goto Error;
623
624 ey1 += incr;
625 if ( set_cell( RAS_VAR_ TRUNC( x ), ey1 ) )
626 goto Error;
627
628 if ( ey1 != ey2 )
629 {
630 p = ONE_PIXEL * dx;
631 lift = p / dy;
632 rem = p % dy;
633 if ( rem < 0 )
634 {
635 lift--;
636 rem += dy;
637 }
638 mod -= dy;
639
640 while ( ey1 != ey2 )
641 {
642 delta = lift;
643 mod += rem;
644 if ( mod >= 0 )
645 {
646 mod -= dy;
647 delta++;
648 }
649
650 x2 = x + delta;
651 if ( render_scanline( RAS_VAR_ ey1,
652 x, ONE_PIXEL - first, x2, first ) )
653 goto Error;
654 x = x2;
655 ey1 += incr;
656 if ( set_cell( RAS_VAR_ TRUNC( x ), ey1 ) )
657 goto Error;
658 }
659 }
660
661 if ( render_scanline( RAS_VAR_ ey1,
662 x, ONE_PIXEL - first, to_x, fy2 ) )
663 goto Error;
664
665 End:
666 ras.x = to_x;
667 ras.y = to_y;
668 ras.last_ey = SUBPIXELS( ey2 );
669
670 return 0;
671
672 Error:
673 return 1;
674 }
675
676
677 static
678 void split_conic( FT_Vector* base )
679 {
680 TPos a, b;
681
682
683 base[4].x = base[2].x;
684 b = base[1].x;
685 a = base[3].x = ( base[2].x + b ) / 2;
686 b = base[1].x = ( base[0].x + b ) / 2;
687 base[2].x = ( a + b ) / 2;
688
689 base[4].y = base[2].y;
690 b = base[1].y;
691 a = base[3].y = ( base[2].y + b ) / 2;
692 b = base[1].y = ( base[0].y + b ) / 2;
693 base[2].y = ( a + b ) / 2;
694 }
695
696
697 static
698 int render_conic( RAS_ARG_ FT_Vector* control,
699 FT_Vector* to )
700 {
701 TPos dx, dy;
702 int top, level;
703 int* levels;
704 FT_Vector* arc;
705
706
707 dx = DOWNSCALE( ras.x ) + to->x - ( control->x << 1 );
708 if ( dx < 0 )
709 dx = -dx;
710 dy = DOWNSCALE( ras.y ) + to->y - ( control->y << 1 );
711 if ( dy < 0 )
712 dy = -dy;
713 if ( dx < dy )
714 dx = dy;
715
716 level = 1;
717 dx = dx / ras.conic_level;
718 while ( dx > 0 )
719 {
David Turnera8194a92000-09-02 00:20:42 +0000720 dx >>= 2;
David Turner5ae1bad2000-06-27 23:18:39 +0000721 level++;
722 }
723
724 /* a shortcut to speed things up */
725 if ( level <= 1 )
726 {
727 /* we compute the mid-point directly in order to avoid */
728 /* calling split_conic() */
729 TPos to_x, to_y, mid_x, mid_y;
730
731
732 to_x = UPSCALE( to->x );
733 to_y = UPSCALE( to->y );
734 mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4;
735 mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4;
736
737 return render_line( RAS_VAR_ mid_x, mid_y ) ||
738 render_line( RAS_VAR_ to_x, to_y );
739 }
740
741 arc = ras.bez_stack;
742 levels = ras.lev_stack;
743 top = 0;
744 levels[0] = level;
745
746 arc[0].x = UPSCALE( to->x );
747 arc[0].y = UPSCALE( to->y );
748 arc[1].x = UPSCALE( control->x );
749 arc[1].y = UPSCALE( control->y );
750 arc[2].x = ras.x;
751 arc[2].y = ras.y;
752
753 while ( top >= 0 )
754 {
755 level = levels[top];
756 if ( level > 1 )
757 {
758 /* check that the arc crosses the current band */
759 TPos min, max, y;
760
761
762 min = max = arc[0].y;
763
764 y = arc[1].y;
765 if ( y < min ) min = y;
766 if ( y > max ) max = y;
767
768 y = arc[2].y;
769 if ( y < min ) min = y;
770 if ( y > max ) max = y;
771
772 if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
773 goto Draw;
774
775 split_conic( arc );
776 arc += 2;
777 top++;
778 levels[top] = levels[top - 1] = level - 1;
779 continue;
780 }
781
782 Draw:
783 {
784 TPos to_x, to_y, mid_x, mid_y;
785
786
787 to_x = arc[0].x;
788 to_y = arc[0].y;
789 mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4;
790 mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4;
791
792 if ( render_line( RAS_VAR_ mid_x, mid_y ) ||
793 render_line( RAS_VAR_ to_x, to_y ) )
794 return 1;
795
796 top--;
797 arc -= 2;
798 }
799 }
800 return 0;
801 }
802
803
804 static
805 void split_cubic( FT_Vector* base )
806 {
807 TPos a, b, c, d;
808
809
810 base[6].x = base[3].x;
811 c = base[1].x;
812 d = base[2].x;
813 base[1].x = a = ( base[0].x + c ) / 2;
814 base[5].x = b = ( base[3].x + d ) / 2;
815 c = ( c + d ) / 2;
816 base[2].x = a = ( a + c ) / 2;
817 base[4].x = b = ( b + c ) / 2;
818 base[3].x = ( a + b ) / 2;
819
820 base[6].y = base[3].y;
821 c = base[1].y;
822 d = base[2].y;
823 base[1].y = a = ( base[0].y + c ) / 2;
824 base[5].y = b = ( base[3].y + d ) / 2;
825 c = ( c + d ) / 2;
826 base[2].y = a = ( a + c ) / 2;
827 base[4].y = b = ( b + c ) / 2;
828 base[3].y = ( a + b ) / 2;
829 }
830
831
832 static
833 int render_cubic( RAS_ARG_ FT_Vector* control1,
834 FT_Vector* control2,
835 FT_Vector* to )
836 {
837 TPos dx, dy, da, db;
838 int top, level;
839 int* levels;
840 FT_Vector* arc;
841
842
843 dx = DOWNSCALE( ras.x ) + to->x - ( control1->x << 1 );
844 if ( dx < 0 )
845 dx = -dx;
846 dy = DOWNSCALE( ras.y ) + to->y - ( control1->y << 1 );
847 if ( dy < 0 )
848 dy = -dy;
849 if ( dx < dy )
850 dx = dy;
851 da = dx;
852
853 dx = DOWNSCALE( ras.x ) + to->x - 3 * ( control1->x + control2->x );
854 if ( dx < 0 )
855 dx = -dx;
856 dy = DOWNSCALE( ras.y ) + to->y - 3 * ( control1->x + control2->y );
857 if ( dy < 0 )
858 dy = -dy;
859 if ( dx < dy )
860 dx = dy;
861 db = dx;
862
863 level = 1;
864 da = da / ras.cubic_level;
865 db = db / ras.conic_level;
866 while ( da > 0 || db > 0 )
867 {
David Turnera8194a92000-09-02 00:20:42 +0000868 da >>= 2;
869 db >>= 3;
David Turner5ae1bad2000-06-27 23:18:39 +0000870 level++;
871 }
872
873 if ( level <= 1 )
874 {
875 TPos to_x, to_y, mid_x, mid_y;
876
877
878 to_x = UPSCALE( to->x );
879 to_y = UPSCALE( to->y );
880 mid_x = ( ras.x + to_x +
881 3 * UPSCALE( control1->x + control2->x ) ) / 8;
882 mid_y = ( ras.y + to_y +
883 3 * UPSCALE( control1->y + control2->y ) ) / 8;
884
885 return render_line( RAS_VAR_ mid_x, mid_y ) ||
886 render_line( RAS_VAR_ to_x, to_y );
887 }
888
889 arc = ras.bez_stack;
890 arc[0].x = UPSCALE( to->x );
891 arc[0].y = UPSCALE( to->y );
892 arc[1].x = UPSCALE( control2->x );
893 arc[1].y = UPSCALE( control2->y );
894 arc[2].x = UPSCALE( control1->x );
895 arc[2].y = UPSCALE( control1->y );
896 arc[3].x = ras.x;
897 arc[3].y = ras.y;
898
899 levels = ras.lev_stack;
900 top = 0;
901 levels[0] = level;
902
903 while ( top >= 0 )
904 {
905 level = levels[top];
906 if ( level > 1 )
907 {
908 /* check that the arc crosses the current band */
909 TPos min, max, y;
910
911
912 min = max = arc[0].y;
913 y = arc[1].y;
914 if ( y < min ) min = y;
915 if ( y > max ) max = y;
916 y = arc[2].y;
917 if ( y < min ) min = y;
918 if ( y > max ) max = y;
919 y = arc[3].y;
920 if ( y < min ) min = y;
921 if ( y > max ) max = y;
922 if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
923 goto Draw;
924 split_cubic( arc );
925 arc += 3;
926 top ++;
927 levels[top] = levels[top - 1] = level - 1;
928 continue;
929 }
930
931 Draw:
932 {
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000933 TPos to_x, to_y, mid_x, mid_y;
David Turner5ae1bad2000-06-27 23:18:39 +0000934
935
936 to_x = arc[0].x;
937 to_y = arc[0].y;
938 mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8;
939 mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8;
940
941 if ( render_line( RAS_VAR_ mid_x, mid_y ) ||
942 render_line( RAS_VAR_ to_x, to_y ) )
943 return 1;
944 top --;
945 arc -= 3;
946 }
947 }
948 return 0;
949 }
950
951
952 /* a macro comparing two cell pointers. Returns true if a <= b. */
953#if 1
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000954
David Turner5ae1bad2000-06-27 23:18:39 +0000955#define PACK( a ) ( ( (long)(a)->y << 16 ) + (a)->x )
956#define LESS_THAN( a, b ) ( PACK( a ) < PACK( b ) )
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000957
David Turner5ae1bad2000-06-27 23:18:39 +0000958#else /* 1 */
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000959
David Turner5ae1bad2000-06-27 23:18:39 +0000960#define LESS_THAN( a, b ) ( (a)->y < (b)->y || \
961 ( (a)->y == (b)->y && (a)->x < (b)->x ) )
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000962
David Turner5ae1bad2000-06-27 23:18:39 +0000963#endif /* 1 */
964
965#define SWAP_CELLS( a, b, temp ) do \
966 { \
967 temp = *(a); \
968 *(a) = *(b); \
969 *(b) = temp; \
970 } while ( 0 )
971#define DEBUG_SORT
972#define QUICK_SORT
973
974#ifdef SHELL_SORT
975
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000976 /* a simple shell sort algorithm that works directly on our */
977 /* cells table */
David Turner5ae1bad2000-06-27 23:18:39 +0000978 static
979 void shell_sort ( PCell cells,
980 int count )
981 {
982 PCell i, j, limit = cells + count;
983 TCell temp;
984 int gap;
985
986
987 /* compute initial gap */
988 for ( gap = 0; ++gap < count; gap *= 3 )
989 ;
990
991 while ( gap /= 3 )
992 {
993 for ( i = cells + gap; i < limit; i++ )
994 {
995 for ( j = i - gap; ; j -= gap )
996 {
997 PCell k = j + gap;
998
999
1000 if ( LESS_THAN( j, k ) )
1001 break;
1002
1003 SWAP_CELLS( j, k, temp );
1004
1005 if ( j < cells + gap )
1006 break;
1007 }
1008 }
1009 }
1010 }
1011
1012#endif /* SHELL_SORT */
1013
1014
1015#ifdef QUICK_SORT
1016
1017 /* This is a non-recursive quicksort that directly process our cells */
1018 /* array. It should be faster than calling the stdlib qsort(), and we */
1019 /* can even tailor our insertion threshold... */
1020
1021#define QSORT_THRESHOLD 9 /* below this size, a sub-array will be sorted */
Werner Lembergfbeb41d2000-07-02 00:27:53 +00001022 /* through a normal insertion sort */
David Turner5ae1bad2000-06-27 23:18:39 +00001023
1024 static
1025 void quick_sort( PCell cells,
1026 int count )
1027 {
1028 PCell stack[40]; /* should be enough ;-) */
1029 PCell* top; /* top of stack */
1030 PCell base, limit;
1031 TCell temp;
1032
1033
1034 limit = cells + count;
1035 base = cells;
1036 top = stack;
1037
1038 for (;;)
1039 {
Werner Lemberg914b2892001-03-10 17:07:42 +00001040 int len = (int)( limit - base );
David Turner5ae1bad2000-06-27 23:18:39 +00001041 PCell i, j, pivot;
1042
1043
1044 if ( len > QSORT_THRESHOLD )
1045 {
1046 /* we use base + len/2 as the pivot */
1047 pivot = base + len / 2;
1048 SWAP_CELLS( base, pivot, temp );
1049
1050 i = base + 1;
1051 j = limit - 1;
1052
1053 /* now ensure that *i <= *base <= *j */
1054 if ( LESS_THAN( j, i ) )
1055 SWAP_CELLS( i, j, temp );
1056
1057 if ( LESS_THAN( base, i ) )
1058 SWAP_CELLS( base, i, temp );
1059
1060 if ( LESS_THAN( j, base ) )
1061 SWAP_CELLS( base, j, temp );
1062
1063 for (;;)
1064 {
1065 do i++; while ( LESS_THAN( i, base ) );
1066 do j--; while ( LESS_THAN( base, j ) );
1067
1068 if ( i > j )
1069 break;
1070
1071 SWAP_CELLS( i, j, temp );
1072 }
1073
1074 SWAP_CELLS( base, j, temp );
1075
1076 /* now, push the largest sub-array */
1077 if ( j - base > limit - i )
1078 {
1079 top[0] = base;
1080 top[1] = j;
1081 base = i;
1082 }
1083 else
1084 {
1085 top[0] = i;
1086 top[1] = limit;
1087 limit = j;
1088 }
1089 top += 2;
1090 }
1091 else
1092 {
1093 /* the sub-array is small, perform insertion sort */
1094 j = base;
1095 i = j + 1;
1096
1097 for ( ; i < limit; j = i, i++ )
1098 {
1099 for ( ; LESS_THAN( j + 1, j ); j-- )
1100 {
1101 SWAP_CELLS( j + 1, j, temp );
1102 if ( j == base )
1103 break;
1104 }
1105 }
1106 if ( top > stack )
1107 {
1108 top -= 2;
1109 base = top[0];
1110 limit = top[1];
1111 }
1112 else
1113 break;
1114 }
1115 }
1116 }
1117
1118#endif /* QUICK_SORT */
1119
1120
1121#ifdef DEBUG_GRAYS
1122#ifdef DEBUG_SORT
1123
1124 static
1125 int check_sort( PCell cells,
1126 int count )
1127 {
1128 PCell p, q;
1129
1130
1131 for ( p = cells + count - 2; p >= cells; p-- )
1132 {
1133 q = p + 1;
1134 if ( !LESS_THAN( p, q ) )
1135 return 0;
1136 }
1137 return 1;
1138 }
1139
1140#endif /* DEBUG_SORT */
1141#endif /* DEBUG_GRAYS */
1142
1143
1144 static
1145 int Move_To( FT_Vector* to,
1146 FT_Raster raster )
1147 {
1148 TPos x, y;
1149
1150
1151 /* record current cell, if any */
1152 record_cell( (PRaster)raster );
1153
1154 /* start to a new position */
1155 x = UPSCALE( to->x );
1156 y = UPSCALE( to->y );
1157 start_cell( (PRaster)raster, TRUNC( x ), TRUNC( y ) );
1158 ((PRaster)raster)->x = x;
1159 ((PRaster)raster)->y = y;
1160 return 0;
1161 }
1162
1163
1164 static
1165 int Line_To( FT_Vector* to,
1166 FT_Raster raster )
1167 {
1168 return render_line( (PRaster)raster,
1169 UPSCALE( to->x ), UPSCALE( to->y ) );
1170 }
1171
1172
1173 static
1174 int Conic_To( FT_Vector* control,
1175 FT_Vector* to,
1176 FT_Raster raster )
1177 {
1178 return render_conic( (PRaster)raster, control, to );
1179 }
1180
1181
1182 static
1183 int Cubic_To( FT_Vector* control1,
1184 FT_Vector* control2,
1185 FT_Vector* to,
1186 FT_Raster raster )
1187 {
1188 return render_cubic( (PRaster)raster, control1, control2, to );
1189 }
1190
1191
1192 static
1193 void grays_render_span( int y,
1194 int count,
1195 FT_Span* spans,
1196 PRaster raster )
1197 {
1198 unsigned char* p;
1199 FT_Bitmap* map = &raster->target;
1200
1201
1202 /* first of all, compute the scanline offset */
1203 p = (unsigned char*)map->buffer - y * map->pitch;
1204 if ( map->pitch >= 0 )
1205 p += ( map->rows - 1 ) * map->pitch;
1206
1207 for ( ; count > 0; count--, spans++ )
1208 {
1209 if ( spans->coverage )
1210#if 1
1211 memset( p + spans->x, (unsigned char)spans->coverage, spans->len );
1212#else /* 1 */
1213 {
1214 q = p + spans->x;
1215 limit = q + spans->len;
1216 for ( ; q < limit; q++ )
1217 q[0] = (unsigned char)spans->coverage;
1218 }
1219#endif /* 1 */
1220 }
1221 }
1222
1223
1224#ifdef DEBUG_GRAYS
1225
1226#include <stdio.h>
1227
1228 static
1229 void dump_cells( RAS_ARG )
1230 {
1231 PCell cell, limit;
1232 int y = -1;
1233
1234
1235 cell = ras.cells;
1236 limit = cell + ras.num_cells;
1237
1238 for ( ; cell < limit; cell++ )
1239 {
1240 if ( cell->y != y )
1241 {
1242 fprintf( stderr, "\n%2d: ", cell->y );
1243 y = cell->y;
1244 }
1245 fprintf( stderr, "[%d %d %d]",
1246 cell->x, cell->area, cell->cover );
1247 }
1248 fprintf(stderr, "\n" );
1249 }
1250
1251#endif /* DEBUG_GRAYS */
1252
1253
1254 static
1255 void grays_hline( RAS_ARG_ TScan x,
1256 TScan y,
1257 TPos area,
1258 int acount )
1259 {
1260 FT_Span* span;
1261 int count;
1262 int coverage;
1263
1264
1265 /* compute the coverage line's coverage, depending on the */
1266 /* outline fill rule */
1267 /* */
1268 /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
1269 /* */
1270 coverage = area >> ( PIXEL_BITS * 2 + 1 - 8); /* use range 0..256 */
1271
1272 if ( ras.outline.flags & ft_outline_even_odd_fill )
1273 {
1274 if ( coverage < 0 )
1275 coverage = -coverage;
1276
1277 while ( coverage >= 512 )
1278 coverage -= 512;
1279
1280 if ( coverage > 256 )
1281 coverage = 512 - coverage;
1282 else if ( coverage == 256 )
1283 coverage = 255;
1284 }
1285 else
1286 {
1287 /* normal non-zero winding rule */
1288 if ( coverage < 0 )
1289 coverage = -coverage;
1290
1291 if ( coverage >= 256 )
1292 coverage = 255;
1293 }
1294
1295 y += ras.min_ey;
1296 x += ras.min_ex;
1297
1298 if ( coverage )
1299 {
1300 /* see if we can add this span to the current list */
1301 count = ras.num_gray_spans;
1302 span = ras.gray_spans + count - 1;
1303 if ( count > 0 &&
1304 ras.span_y == y &&
1305 (int)span->x + span->len == (int)x &&
1306 span->coverage == coverage )
1307 {
1308 span->len += acount;
1309 return;
1310 }
1311
1312 if ( ras.span_y != y || count >= FT_MAX_GRAY_SPANS )
1313 {
David Turnerb71c6af2000-09-17 17:17:16 +00001314 if ( ras.render_span && count > 0 )
David Turner5ae1bad2000-06-27 23:18:39 +00001315 ras.render_span( ras.span_y, count, ras.gray_spans,
1316 ras.render_span_data );
1317 /* ras.render_span( span->y, ras.gray_spans, count ); */
1318
1319#ifdef DEBUG_GRAYS
1320
1321 if ( ras.span_y >= 0 )
1322 {
1323 int n;
1324
1325
1326 fprintf( stderr, "y=%3d ", ras.span_y );
1327 span = ras.gray_spans;
1328 for ( n = 0; n < count; n++, span++ )
1329 fprintf( stderr, "[%d..%d]:%02x ",
1330 span->x, span->x + span->len - 1, span->coverage );
1331 fprintf( stderr, "\n" );
1332 }
1333
1334#endif /* DEBUG_GRAYS */
1335
1336 ras.num_gray_spans = 0;
1337 ras.span_y = y;
1338
1339 count = 0;
1340 span = ras.gray_spans;
1341 }
1342 else
1343 span++;
1344
1345 /* add a gray span to the current list */
1346 span->x = (short)x;
1347 span->len = (unsigned short)acount;
1348 span->coverage = (unsigned char)coverage;
1349 ras.num_gray_spans++;
1350 }
1351 }
1352
1353
1354 static
1355 void grays_sweep( RAS_ARG_ FT_Bitmap* target )
1356 {
1357 TScan x, y, cover, area;
1358 PCell start, cur, limit;
1359
David Turnerc6a92202000-07-04 18:12:13 +00001360 FT_UNUSED( target );
David Turner5ae1bad2000-06-27 23:18:39 +00001361
David Turner5aeaad62001-02-23 17:47:41 +00001362 if ( ras.num_cells == 0 )
1363 return;
1364
David Turner5ae1bad2000-06-27 23:18:39 +00001365 cur = ras.cells;
1366 limit = cur + ras.num_cells;
1367
1368 cover = 0;
1369 ras.span_y = -1;
1370 ras.num_gray_spans = 0;
1371
1372 for (;;)
1373 {
1374 start = cur;
1375 y = start->y;
1376 x = start->x;
1377
1378 area = start->area;
1379 cover += start->cover;
1380
1381 /* accumulate all start cells */
1382 for (;;)
1383 {
1384 ++cur;
1385 if ( cur >= limit || cur->y != start->y || cur->x != start->x )
1386 break;
1387
1388 area += cur->area;
1389 cover += cur->cover;
1390 }
1391
1392 /* if the start cell has a non-null area, we must draw an */
1393 /* individual gray pixel there */
1394 if ( area && x >= 0 )
1395 {
1396 grays_hline( RAS_VAR_ x, y, cover * ( ONE_PIXEL * 2 ) - area, 1 );
1397 x++;
1398 }
1399
1400 if ( x < 0 )
1401 x = 0;
1402
1403 if ( cur < limit && start->y == cur->y )
1404 {
1405 /* draw a gray span between the start cell and the current one */
1406 if ( cur->x > x )
1407 grays_hline( RAS_VAR_ x, y,
1408 cover * ( ONE_PIXEL * 2 ), cur->x - x );
1409 }
1410 else
1411 {
1412 /* draw a gray span until the end of the clipping region */
1413 if ( cover && x < ras.max_ex - ras.min_ex )
1414 grays_hline( RAS_VAR_ x, y,
1415 cover * ( ONE_PIXEL * 2 ),
1416 ras.max_ex - x - ras.min_ex );
1417 cover = 0;
1418 }
1419
1420 if ( cur >= limit )
1421 break;
1422 }
1423
1424 if ( ras.render_span && ras.num_gray_spans > 0 )
1425 ras.render_span( ras.span_y, ras.num_gray_spans,
1426 ras.gray_spans, ras.render_span_data );
1427
1428#ifdef DEBUG_GRAYS
1429
1430 {
1431 int n;
1432 FT_Span* span;
1433
1434
1435 fprintf( stderr, "y=%3d ", ras.span_y );
1436 span = ras.gray_spans;
1437 for ( n = 0; n < ras.num_gray_spans; n++, span++ )
1438 fprintf( stderr, "[%d..%d]:%02x ",
1439 span->x, span->x + span->len - 1, span->coverage );
1440 fprintf( stderr, "\n" );
1441 }
1442
1443#endif /* DEBUG_GRAYS */
1444
1445 }
1446
1447
1448#ifdef _STANDALONE_
1449
1450 /*************************************************************************/
1451 /* */
1452 /* The following function should only compile in stand_alone mode, */
1453 /* i.e., when building this component without the rest of FreeType. */
1454 /* */
1455 /*************************************************************************/
1456
1457 /*************************************************************************/
1458 /* */
1459 /* <Function> */
1460 /* FT_Outline_Decompose */
1461 /* */
1462 /* <Description> */
1463 /* Walks over an outline's structure to decompose it into individual */
1464 /* segments and Bezier arcs. This function is also able to emit */
1465 /* `move to' and `close to' operations to indicate the start and end */
1466 /* of new contours in the outline. */
1467 /* */
1468 /* <Input> */
1469 /* outline :: A pointer to the source target. */
1470 /* */
1471 /* interface :: A table of `emitters', i.e,. function pointers called */
1472 /* during decomposition to indicate path operations. */
1473 /* */
1474 /* user :: A typeless pointer which is passed to each emitter */
1475 /* during the decomposition. It can be used to store */
1476 /* the state during the decomposition. */
1477 /* */
1478 /* <Return> */
1479 /* Error code. 0 means sucess. */
1480 /* */
1481 static
David Turnerdc26e7b2000-10-23 22:46:56 +00001482 int FT_Outline_Decompose( FT_Outline* outline,
1483 const FT_Outline_Funcs* interface,
1484 void* user )
David Turner5ae1bad2000-06-27 23:18:39 +00001485 {
1486#undef SCALED
1487#define SCALED( x ) ( ( (x) << shift ) - delta )
1488
1489 FT_Vector v_last;
1490 FT_Vector v_control;
1491 FT_Vector v_start;
1492
1493 FT_Vector* point;
1494 FT_Vector* limit;
1495 char* tags;
1496
1497 int n; /* index of contour in outline */
1498 int first; /* index of first point in contour */
1499 int error;
1500 char tag; /* current point's state */
1501
1502 int shift = interface->shift;
1503 FT_Pos delta = interface->delta;
1504
1505
1506 first = 0;
1507
1508 for ( n = 0; n < outline->n_contours; n++ )
1509 {
1510 int last; /* index of last point in contour */
1511
1512
1513 last = outline->contours[n];
1514 limit = outline->points + last;
1515
1516 v_start = outline->points[first];
1517 v_last = outline->points[last];
1518
1519 v_start.x = SCALED( v_start.x ); v_start.y = SCALED( v_start.y );
1520 v_last.x = SCALED( v_last.x ); v_last.y = SCALED( v_last.y );
1521
1522 v_control = v_start;
1523
1524 point = outline->points + first;
1525 tags = outline->tags + first;
1526 tag = FT_CURVE_TAG( tags[0] );
1527
1528 /* A contour cannot start with a cubic control point! */
1529 if ( tag == FT_Curve_Tag_Cubic )
1530 goto Invalid_Outline;
1531
1532 /* check first point to determine origin */
1533 if ( tag == FT_Curve_Tag_Conic )
1534 {
1535 /* first point is conic control. Yes, this happens. */
1536 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_Curve_Tag_On )
1537 {
1538 /* start at last point if it is on the curve */
1539 v_start = v_last;
1540 limit--;
1541 }
1542 else
1543 {
1544 /* if both first and last points are conic, */
1545 /* start at their middle and record its position */
1546 /* for closure */
1547 v_start.x = ( v_start.x + v_last.x ) / 2;
1548 v_start.y = ( v_start.y + v_last.y ) / 2;
1549
1550 v_last = v_start;
1551 }
1552 point--;
1553 tags--;
1554 }
1555
1556 error = interface->move_to( &v_start, user );
1557 if ( error )
1558 goto Exit;
1559
1560 while ( point < limit )
1561 {
1562 point++;
1563 tags++;
1564
1565 tag = FT_CURVE_TAG( tags[0] );
1566 switch ( tag )
1567 {
1568 case FT_Curve_Tag_On: /* emit a single line_to */
1569 {
1570 FT_Vector vec;
1571
1572
1573 vec.x = SCALED( point->x );
1574 vec.y = SCALED( point->y );
1575
1576 error = interface->line_to( &vec, user );
1577 if ( error )
1578 goto Exit;
1579 continue;
1580 }
1581
1582 case FT_Curve_Tag_Conic: /* consume conic arcs */
1583 {
1584 v_control.x = SCALED( point->x );
1585 v_control.y = SCALED( point->y );
1586
1587 Do_Conic:
1588 if ( point < limit )
1589 {
1590 FT_Vector vec;
1591 FT_Vector v_middle;
1592
1593
1594 point++;
1595 tags++;
1596 tag = FT_CURVE_TAG( tags[0] );
1597
1598 vec.x = SCALED( point->x );
1599 vec.y = SCALED( point->y );
1600
1601 if ( tag == FT_Curve_Tag_On )
1602 {
1603 error = interface->conic_to( &v_control, &vec, user );
1604 if ( error )
1605 goto Exit;
1606 continue;
1607 }
1608
1609 if ( tag != FT_Curve_Tag_Conic )
1610 goto Invalid_Outline;
1611
1612 v_middle.x = ( v_control.x + vec.x ) / 2;
1613 v_middle.y = ( v_control.y + vec.y ) / 2;
1614
1615 error = interface->conic_to( &v_control, &v_middle, user );
1616 if ( error )
1617 goto Exit;
1618
1619 v_control = vec;
1620 goto Do_Conic;
1621 }
1622
1623 error = interface->conic_to( &v_control, &v_start, user );
1624 goto Close;
1625 }
1626
1627 default: /* FT_Curve_Tag_Cubic */
1628 {
1629 FT_Vector vec1, vec2;
1630
1631
1632 if ( point + 1 > limit ||
1633 FT_CURVE_TAG( tags[1] ) != FT_Curve_Tag_Cubic )
1634 goto Invalid_Outline;
1635
1636 point += 2;
1637 tags += 2;
1638
1639 vec1.x = SCALED( point[-2].x ); vec1.y = SCALED( point[-2].y );
1640 vec2.x = SCALED( point[-1].x ); vec2.y = SCALED( point[-1].y );
1641
1642 if ( point <= limit )
1643 {
1644 FT_Vector vec;
1645
1646
1647 vec.x = SCALED( point->x );
1648 vec.y = SCALED( point->y );
1649
1650 error = interface->cubic_to( &vec1, &vec2, &vec, user );
1651 if ( error )
1652 goto Exit;
1653 continue;
1654 }
1655
1656 error = interface->cubic_to( &vec1, &vec2, &v_start, user );
1657 goto Close;
1658 }
1659 }
1660 }
1661
1662 /* close the contour with a line segment */
1663 error = interface->line_to( &v_start, user );
1664
1665 Close:
1666 if ( error )
1667 goto Exit;
1668
1669 first = last + 1;
1670 }
1671
1672 return 0;
1673
1674 Exit:
1675 return error;
1676
1677 Invalid_Outline:
1678 return ErrRaster_Invalid_Outline;
1679 }
1680
1681#endif /* _STANDALONE_ */
1682
1683
1684 typedef struct TBand_
1685 {
1686 FT_Pos min, max;
1687
1688 } TBand;
1689
1690
1691 static
1692 int grays_convert_glyph( RAS_ARG_ FT_Outline* outline )
1693 {
1694 static
David Turnerdc26e7b2000-10-23 22:46:56 +00001695 const FT_Outline_Funcs interface =
David Turner5ae1bad2000-06-27 23:18:39 +00001696 {
Werner Lembergfbeb41d2000-07-02 00:27:53 +00001697 (FT_Outline_MoveTo_Func) Move_To,
1698 (FT_Outline_LineTo_Func) Line_To,
David Turner5ae1bad2000-06-27 23:18:39 +00001699 (FT_Outline_ConicTo_Func)Conic_To,
1700 (FT_Outline_CubicTo_Func)Cubic_To,
1701 0,
1702 0
1703 };
1704
Werner Lemberg594f0c92000-12-20 22:09:41 +00001705 TBand bands[40], *band;
1706 int n, num_bands;
1707 TPos min, max, max_y;
1708 FT_BBox* clip;
David Turner5ae1bad2000-06-27 23:18:39 +00001709
1710
1711 /* Set up state in the raster object */
1712 compute_cbox( RAS_VAR_ outline );
1713
1714 /* clip to target bitmap, exit if nothing to do */
David Turner859a18a2000-12-14 18:50:40 +00001715 clip = &ras.clip_box;
1716
1717 if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax ||
1718 ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax )
David Turner5ae1bad2000-06-27 23:18:39 +00001719 return 0;
1720
David Turner859a18a2000-12-14 18:50:40 +00001721 if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin;
1722 if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin;
David Turner5ae1bad2000-06-27 23:18:39 +00001723
David Turner859a18a2000-12-14 18:50:40 +00001724 if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax;
1725 if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax;
David Turner5ae1bad2000-06-27 23:18:39 +00001726
Werner Lembergfbeb41d2000-07-02 00:27:53 +00001727 /* simple heuristic used to speed-up the bezier decomposition -- see */
1728 /* the code in render_conic() and render_cubic() for more details */
David Turner5ae1bad2000-06-27 23:18:39 +00001729 ras.conic_level = 32;
1730 ras.cubic_level = 16;
1731
1732 {
1733 int level = 0;
1734
1735
1736 if ( ras.max_ex > 24 || ras.max_ey > 24 )
1737 level++;
1738 if ( ras.max_ex > 120 || ras.max_ey > 120 )
Werner Lembergf9b03752000-09-11 22:50:13 +00001739 level++;
David Turner5ae1bad2000-06-27 23:18:39 +00001740
1741 ras.conic_level <<= level;
1742 ras.cubic_level <<= level;
1743 }
1744
1745 /* setup vertical bands */
1746 num_bands = ( ras.max_ey - ras.min_ey ) / ras.band_size;
1747 if ( num_bands == 0 ) num_bands = 1;
1748 if ( num_bands >= 39 ) num_bands = 39;
1749
1750 ras.band_shoot = 0;
1751
1752 min = ras.min_ey;
1753 max_y = ras.max_ey;
1754
1755 for ( n = 0; n < num_bands; n++, min = max )
1756 {
1757 max = min + ras.band_size;
1758 if ( n == num_bands - 1 || max > max_y )
1759 max = max_y;
1760
1761 bands[0].min = min;
1762 bands[0].max = max;
1763 band = bands;
1764
1765 while ( band >= bands )
1766 {
1767 FT_Pos bottom, top, middle;
1768 int error;
1769
1770
1771 ras.num_cells = 0;
1772 ras.invalid = 1;
1773 ras.min_ey = band->min;
1774 ras.max_ey = band->max;
1775
1776 error = FT_Outline_Decompose( outline, &interface, &ras ) ||
1777 record_cell( RAS_VAR );
1778
1779 if ( !error )
1780 {
1781#ifdef SHELL_SORT
1782 shell_sort( ras.cells, ras.num_cells );
1783#else
1784 quick_sort( ras.cells, ras.num_cells );
1785#endif
1786
1787#ifdef DEBUG_GRAYS
1788 check_sort( ras.cells, ras.num_cells );
1789 dump_cells( RAS_VAR );
1790#endif
1791
1792 grays_sweep( RAS_VAR_ &ras.target );
1793 band--;
1794 continue;
1795 }
1796
1797 /* render pool overflow, we will reduce the render band by half */
1798 bottom = band->min;
1799 top = band->max;
1800 middle = bottom + ( ( top - bottom ) >> 1 );
1801
1802 /* waoow! This is too complex for a single scanline, something */
1803 /* must be really rotten here! */
1804 if ( middle == bottom )
1805 {
1806#ifdef DEBUG_GRAYS
1807 fprintf( stderr, "Rotten glyph!\n" );
1808#endif
1809 return 1;
1810 }
1811
1812 if ( bottom-top >= ras.band_size )
1813 ras.band_shoot++;
1814
1815 band[1].min = bottom;
1816 band[1].max = middle;
1817 band[0].min = middle;
1818 band[0].max = top;
1819 band++;
1820 }
1821 }
1822
1823 if ( ras.band_shoot > 8 && ras.band_size > 16 )
1824 ras.band_size = ras.band_size / 2;
1825
1826 return 0;
1827 }
1828
1829
1830 extern
1831 int grays_raster_render( PRaster raster,
1832 FT_Raster_Params* params )
1833 {
1834 FT_Outline* outline = (FT_Outline*)params->source;
1835 FT_Bitmap* target_map = params->target;
1836
1837
1838 if ( !raster || !raster->cells || !raster->max_cells )
1839 return -1;
1840
1841 /* return immediately if the outline is empty */
1842 if ( outline->n_points == 0 || outline->n_contours <= 0 )
1843 return 0;
1844
1845 if ( !outline || !outline->contours || !outline->points )
1846 return ErrRaster_Invalid_Outline;
1847
1848 if ( outline->n_points !=
1849 outline->contours[outline->n_contours - 1] + 1 )
1850 return ErrRaster_Invalid_Outline;
1851
David Turner78dd7102000-10-03 19:13:11 +00001852 /* if direct mode is not set, we must have a target bitmap */
Werner Lemberg6fbe4db2000-10-05 04:53:31 +00001853 if ( ( params->flags & ft_raster_flag_direct ) == 0 &&
1854 ( !target_map || !target_map->buffer ) )
David Turner5ae1bad2000-06-27 23:18:39 +00001855 return -1;
1856
David Turner78dd7102000-10-03 19:13:11 +00001857 /* this version does not support monochrome rendering */
Werner Lemberg6fbe4db2000-10-05 04:53:31 +00001858 if ( !( params->flags & ft_raster_flag_aa ) )
David Turner5ae1bad2000-06-27 23:18:39 +00001859 return ErrRaster_Invalid_Mode;
1860
David Turner859a18a2000-12-14 18:50:40 +00001861 /* compute clipping box */
Werner Lemberg594f0c92000-12-20 22:09:41 +00001862 if ( ( params->flags & ft_raster_flag_direct ) == 0 )
David Turner859a18a2000-12-14 18:50:40 +00001863 {
1864 /* compute clip box from target pixmap */
1865 ras.clip_box.xMin = 0;
1866 ras.clip_box.yMin = 0;
1867 ras.clip_box.xMax = target_map->width;
1868 ras.clip_box.yMax = target_map->rows;
1869 }
1870 else if ( params->flags & ft_raster_flag_clip )
1871 {
1872 ras.clip_box = params->clip_box;
1873 }
1874 else
1875 {
1876 ras.clip_box.xMin = -32768;
1877 ras.clip_box.yMin = -32768;
1878 ras.clip_box.xMax = 32767;
1879 ras.clip_box.yMax = 32767;
1880 }
1881
David Turner5ae1bad2000-06-27 23:18:39 +00001882 ras.outline = *outline;
David Turner5ae1bad2000-06-27 23:18:39 +00001883 ras.num_cells = 0;
1884 ras.invalid = 1;
1885
Werner Lemberg6fbe4db2000-10-05 04:53:31 +00001886 if ( target_map )
David Turner78dd7102000-10-03 19:13:11 +00001887 ras.target = *target_map;
David Turner78dd7102000-10-03 19:13:11 +00001888
David Turner5ae1bad2000-06-27 23:18:39 +00001889 ras.render_span = (FT_Raster_Span_Func)grays_render_span;
1890 ras.render_span_data = &ras;
1891
1892 if ( params->flags & ft_raster_flag_direct )
1893 {
1894 ras.render_span = (FT_Raster_Span_Func)params->gray_spans;
1895 ras.render_span_data = params->user;
1896 }
1897
1898 return grays_convert_glyph( (PRaster)raster, outline );
1899 }
1900
1901
1902 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
1903 /**** a static object. *****/
1904
1905#ifdef _STANDALONE_
1906
1907 static
1908 int grays_raster_new( void* memory,
1909 FT_Raster* araster )
1910 {
1911 static TRaster the_raster;
1912
David Turnerc6a92202000-07-04 18:12:13 +00001913 FT_UNUSED( memory );
David Turner5ae1bad2000-06-27 23:18:39 +00001914
1915
1916 *araster = (FT_Raster)&the_raster;
1917 memset( &the_raster, 0, sizeof ( the_raster ) );
1918
1919 return 0;
1920 }
1921
1922
1923 static
1924 void grays_raster_done( FT_Raster raster )
1925 {
1926 /* nothing */
David Turnerc6a92202000-07-04 18:12:13 +00001927 FT_UNUSED( raster );
David Turner5ae1bad2000-06-27 23:18:39 +00001928 }
1929
1930#else /* _STANDALONE_ */
1931
1932 static
1933 int grays_raster_new( FT_Memory memory,
1934 FT_Raster* araster )
1935 {
1936 FT_Error error;
1937 PRaster raster;
1938
1939
1940 *araster = 0;
1941 if ( !ALLOC( raster, sizeof ( TRaster ) ) )
1942 {
1943 raster->memory = memory;
1944 *araster = (FT_Raster)raster;
1945 }
1946
1947 return error;
1948 }
1949
1950
1951 static
1952 void grays_raster_done( FT_Raster raster )
1953 {
1954 FT_Memory memory = (FT_Memory)((PRaster)raster)->memory;
1955
1956
1957 FREE( raster );
1958 }
1959
1960#endif /* _STANDALONE_ */
1961
1962
1963 static
1964 void grays_raster_reset( FT_Raster raster,
1965 const char* pool_base,
1966 long pool_size )
1967 {
1968 PRaster rast = (PRaster)raster;
1969
1970
1971 if ( raster && pool_base && pool_size >= 4096 )
1972 init_cells( rast, (char*)pool_base, pool_size );
1973
1974 rast->band_size = ( pool_size / sizeof ( TCell ) ) / 8;
1975 }
1976
1977
David Turnerdc26e7b2000-10-23 22:46:56 +00001978 const FT_Raster_Funcs ft_grays_raster =
David Turner5ae1bad2000-06-27 23:18:39 +00001979 {
1980 ft_glyph_format_outline,
1981
1982 (FT_Raster_New_Func) grays_raster_new,
1983 (FT_Raster_Reset_Func) grays_raster_reset,
1984 (FT_Raster_Set_Mode_Func)0,
1985 (FT_Raster_Render_Func) grays_raster_render,
1986 (FT_Raster_Done_Func) grays_raster_done
1987 };
1988
1989
1990/* END */