blob: 62d601997cf252268d60c4b5aa0f2acf43e6ba43 [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 Lemberg1f7f0e82001-06-06 17:30:41 +0000125
Werner Lemberg63408a12000-12-13 23:44:37 +0000126#include <ft2build.h>
David Turner8d3a4012001-03-20 11:14:24 +0000127#include "ftgrays.h"
Werner Lemberg63408a12000-12-13 23:44:37 +0000128#include FT_INTERNAL_OBJECTS_H
129#include FT_INTERNAL_DEBUG_H
130#include FT_OUTLINE_H
David Turner5ae1bad2000-06-27 23:18:39 +0000131
Werner Lemberg1f7f0e82001-06-06 17:30:41 +0000132#include "ftsmerrs.h"
133
134#define ErrRaster_Invalid_Mode Smooth_Err_Cannot_Render_Glyph
135#define ErrRaster_Invalid_Outline Smooth_Err_Invalid_Outline
David Turner5ae1bad2000-06-27 23:18:39 +0000136
137
138#endif /* _STANDALONE_ */
139
140
141 /* define this to dump debugging information */
142#define xxxDEBUG_GRAYS
143
144 /* as usual, for the speed hungry :-) */
145
146#ifndef FT_STATIC_RASTER
147
148
149#define RAS_ARG PRaster raster
150#define RAS_ARG_ PRaster raster,
151
152#define RAS_VAR raster
153#define RAS_VAR_ raster,
154
155#define ras (*raster)
156
157
158#else /* FT_STATIC_RASTER */
159
160
161#define RAS_ARG /* empty */
162#define RAS_ARG_ /* empty */
163#define RAS_VAR /* empty */
164#define RAS_VAR_ /* empty */
165
166 static TRaster ras;
167
168
169#endif /* FT_STATIC_RASTER */
170
171
172 /* must be at least 6 bits! */
173#define PIXEL_BITS 8
174
175#define ONE_PIXEL ( 1L << PIXEL_BITS )
176#define PIXEL_MASK ( -1L << PIXEL_BITS )
177#define TRUNC( x ) ( (x) >> PIXEL_BITS )
178#define SUBPIXELS( x ) ( (x) << PIXEL_BITS )
179#define FLOOR( x ) ( (x) & -ONE_PIXEL )
180#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL )
181#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL )
182
183#if PIXEL_BITS >= 6
184#define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) )
185#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) )
186#else
187#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) )
188#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) )
189#endif
190
191 /* Define this if you want to use a more compact storage scheme. This */
192 /* increases the number of cells available in the render pool but slows */
193 /* down the rendering a bit. It is useful if you have a really tiny */
194 /* render pool. */
195#define xxxGRAYS_COMPACT
196
197
198 /*************************************************************************/
199 /* */
200 /* TYPE DEFINITIONS */
201 /* */
202 typedef int TScan; /* integer scanline/pixel coordinate */
203 typedef long TPos; /* sub-pixel coordinate */
204
205 /* maximal number of gray spans in a call to the span callback */
206#define FT_MAX_GRAY_SPANS 32
207
208
209#ifdef GRAYS_COMPACT
210
211 typedef struct TCell_
212 {
213 short x : 14;
214 short y : 14;
215 int cover : PIXEL_BITS + 2;
216 int area : PIXEL_BITS * 2 + 2;
217
218 } TCell, *PCell;
219
220#else /* GRAYS_COMPACT */
221
222 typedef struct TCell_
223 {
224 TScan x;
225 TScan y;
226 int cover;
227 int area;
228
229 } TCell, *PCell;
230
231#endif /* GRAYS_COMPACT */
232
233
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000234 typedef struct TRaster_
David Turner5ae1bad2000-06-27 23:18:39 +0000235 {
236 PCell cells;
237 int max_cells;
238 int num_cells;
239
240 TScan min_ex, max_ex;
241 TScan min_ey, max_ey;
242
243 int area;
244 int cover;
245 int invalid;
246
247 TScan ex, ey;
248 TScan cx, cy;
249 TPos x, y;
250
251 TScan last_ey;
252
Werner Lemberg37802732001-04-26 13:34:36 +0000253 FT_Vector bez_stack[32 * 3 + 1];
David Turner5ae1bad2000-06-27 23:18:39 +0000254 int lev_stack[32];
255
256 FT_Outline outline;
257 FT_Bitmap target;
David Turner859a18a2000-12-14 18:50:40 +0000258 FT_BBox clip_box;
David Turner5ae1bad2000-06-27 23:18:39 +0000259
260 FT_Span gray_spans[FT_MAX_GRAY_SPANS];
261 int num_gray_spans;
262
263 FT_Raster_Span_Func render_span;
264 void* render_span_data;
265 int span_y;
266
267 int band_size;
268 int band_shoot;
269 int conic_level;
270 int cubic_level;
271
272 void* memory;
273
274 } TRaster, *PRaster;
275
276
277 /*************************************************************************/
278 /* */
279 /* Initialize the cells table. */
280 /* */
Werner Lemberg52005c32001-06-27 23:25:46 +0000281 static void
282 init_cells( RAS_ARG_ void* buffer,
283 long byte_size )
David Turner5ae1bad2000-06-27 23:18:39 +0000284 {
285 ras.cells = (PCell)buffer;
286 ras.max_cells = byte_size / sizeof ( TCell );
287 ras.num_cells = 0;
288 ras.area = 0;
289 ras.cover = 0;
290 ras.invalid = 1;
291 }
292
293
294 /*************************************************************************/
295 /* */
296 /* Compute the outline bounding box. */
297 /* */
Werner Lemberg52005c32001-06-27 23:25:46 +0000298 static void
299 compute_cbox( RAS_ARG_ FT_Outline* outline )
David Turner5ae1bad2000-06-27 23:18:39 +0000300 {
301 FT_Vector* vec = outline->points;
302 FT_Vector* limit = vec + outline->n_points;
303
304
305 if ( outline->n_points <= 0 )
306 {
307 ras.min_ex = ras.max_ex = 0;
308 ras.min_ey = ras.max_ey = 0;
309 return;
310 }
311
312 ras.min_ex = ras.max_ex = vec->x;
313 ras.min_ey = ras.max_ey = vec->y;
314
315 vec++;
316
317 for ( ; vec < limit; vec++ )
318 {
319 TPos x = vec->x;
320 TPos y = vec->y;
321
322
323 if ( x < ras.min_ex ) ras.min_ex = x;
324 if ( x > ras.max_ex ) ras.max_ex = x;
325 if ( y < ras.min_ey ) ras.min_ey = y;
326 if ( y > ras.max_ey ) ras.max_ey = y;
327 }
328
329 /* truncate the bounding box to integer pixels */
330 ras.min_ex = ras.min_ex >> 6;
331 ras.min_ey = ras.min_ey >> 6;
332 ras.max_ex = ( ras.max_ex + 63 ) >> 6;
333 ras.max_ey = ( ras.max_ey + 63 ) >> 6;
334 }
335
336
337 /*************************************************************************/
338 /* */
339 /* Record the current cell in the table. */
340 /* */
Werner Lemberg52005c32001-06-27 23:25:46 +0000341 static int
342 record_cell( RAS_ARG )
David Turner5ae1bad2000-06-27 23:18:39 +0000343 {
344 PCell cell;
345
346
347 if ( !ras.invalid && ( ras.area | ras.cover ) )
348 {
349 if ( ras.num_cells >= ras.max_cells )
350 return 1;
351
352 cell = ras.cells + ras.num_cells++;
353 cell->x = ras.ex - ras.min_ex;
354 cell->y = ras.ey - ras.min_ey;
355 cell->area = ras.area;
356 cell->cover = ras.cover;
357 }
358
359 return 0;
360 }
361
362
363 /*************************************************************************/
364 /* */
365 /* Set the current cell to a new position. */
366 /* */
Werner Lemberg52005c32001-06-27 23:25:46 +0000367 static int
368 set_cell( RAS_ARG_ TScan ex,
369 TScan ey )
David Turner5ae1bad2000-06-27 23:18:39 +0000370 {
371 int invalid, record, clean;
372
373
374 /* Move the cell pointer to a new position. We set the `invalid' */
375 /* flag to indicate that the cell isn't part of those we're interested */
376 /* in during the render phase. This means that: */
377 /* */
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000378 /* . the new vertical position must be within min_ey..max_ey-1. */
David Turner5ae1bad2000-06-27 23:18:39 +0000379 /* . the new horizontal position must be strictly less than max_ex */
380 /* */
381 /* Note that if a cell is to the left of the clipping region, it is */
382 /* actually set to the (min_ex-1) horizontal position. */
383
384 record = 0;
385 clean = 1;
386
387 invalid = ( ey < ras.min_ey || ey >= ras.max_ey || ex >= ras.max_ex );
388 if ( !invalid )
389 {
390 /* All cells that are on the left of the clipping region go to the */
391 /* min_ex - 1 horizontal position. */
392 if ( ex < ras.min_ex )
393 ex = ras.min_ex - 1;
394
395 /* if our position is new, then record the previous cell */
396 if ( ex != ras.ex || ey != ras.ey )
397 record = 1;
398 else
399 clean = ras.invalid; /* do not clean if we didn't move from */
400 /* a valid cell */
401 }
402
403 /* record the previous cell if needed (i.e., if we changed the cell */
404 /* position, of changed the `invalid' flag) */
405 if ( ( ras.invalid != invalid || record ) && record_cell( RAS_VAR ) )
406 return 1;
407
408 if ( clean )
409 {
410 ras.area = 0;
411 ras.cover = 0;
412 }
413
414 ras.invalid = invalid;
415 ras.ex = ex;
416 ras.ey = ey;
417 return 0;
418 }
419
420
421 /*************************************************************************/
422 /* */
423 /* Start a new contour at a given cell. */
424 /* */
Werner Lemberg52005c32001-06-27 23:25:46 +0000425 static void
426 start_cell( RAS_ARG_ TScan ex,
427 TScan ey )
David Turner5ae1bad2000-06-27 23:18:39 +0000428 {
429 if ( ex < ras.min_ex )
430 ex = ras.min_ex - 1;
431
432 ras.area = 0;
433 ras.cover = 0;
434 ras.ex = ex;
435 ras.ey = ey;
436 ras.last_ey = SUBPIXELS( ey );
437 ras.invalid = 0;
438
439 (void)set_cell( RAS_VAR_ ex, ey );
440 }
441
442
443 /*************************************************************************/
444 /* */
445 /* Render a scanline as one or more cells. */
446 /* */
Werner Lemberg52005c32001-06-27 23:25:46 +0000447 static int
448 render_scanline( RAS_ARG_ TScan ey,
449 TPos x1,
450 TScan y1,
451 TPos x2,
452 TScan y2 )
David Turner5ae1bad2000-06-27 23:18:39 +0000453 {
454 TScan ex1, ex2, fx1, fx2, delta;
455 long p, first, dx;
456 int incr, lift, mod, rem;
457
458
459 dx = x2 - x1;
460
461 ex1 = TRUNC( x1 ); /* if (ex1 >= ras.max_ex) ex1 = ras.max_ex-1; */
462 ex2 = TRUNC( x2 ); /* if (ex2 >= ras.max_ex) ex2 = ras.max_ex-1; */
463 fx1 = x1 - SUBPIXELS( ex1 );
464 fx2 = x2 - SUBPIXELS( ex2 );
465
466 /* trivial case. Happens often */
467 if ( y1 == y2 )
468 return set_cell( RAS_VAR_ ex2, ey );
469
470 /* everything is located in a single cell. That is easy! */
471 /* */
472 if ( ex1 == ex2 )
473 {
474 delta = y2 - y1;
475 ras.area += ( fx1 + fx2 ) * delta;
476 ras.cover += delta;
477 return 0;
478 }
479
480 /* ok, we'll have to render a run of adjacent cells on the same */
481 /* scanline... */
482 /* */
483 p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 );
484 first = ONE_PIXEL;
485 incr = 1;
486
487 if ( dx < 0 )
488 {
489 p = fx1 * ( y2 - y1 );
490 first = 0;
491 incr = -1;
492 dx = -dx;
493 }
494
495 delta = p / dx;
496 mod = p % dx;
497 if ( mod < 0 )
498 {
499 delta--;
500 mod += dx;
501 }
502
503 ras.area += ( fx1 + first ) * delta;
504 ras.cover += delta;
505
506 ex1 += incr;
507 if ( set_cell( RAS_VAR_ ex1, ey ) )
508 goto Error;
509 y1 += delta;
510
511 if ( ex1 != ex2 )
512 {
513 p = ONE_PIXEL * ( y2 - y1 );
514 lift = p / dx;
515 rem = p % dx;
516 if ( rem < 0 )
517 {
518 lift--;
519 rem += dx;
520 }
521
522 mod -= dx;
523
524 while ( ex1 != ex2 )
525 {
526 delta = lift;
527 mod += rem;
528 if ( mod >= 0 )
529 {
530 mod -= dx;
531 delta++;
532 }
533
534 ras.area += ONE_PIXEL * delta;
535 ras.cover += delta;
536 y1 += delta;
537 ex1 += incr;
538 if ( set_cell( RAS_VAR_ ex1, ey ) )
539 goto Error;
540 }
541 }
542
543 delta = y2 - y1;
544 ras.area += ( fx2 + ONE_PIXEL - first ) * delta;
545 ras.cover += delta;
546
547 return 0;
548
549 Error:
550 return 1;
551 }
552
553
554 /*************************************************************************/
555 /* */
556 /* Render a given line as a series of scanlines. */
557 /* */
Werner Lemberg52005c32001-06-27 23:25:46 +0000558 static int
559 render_line( RAS_ARG_ TPos to_x,
560 TPos to_y )
David Turner5ae1bad2000-06-27 23:18:39 +0000561 {
562 TScan ey1, ey2, fy1, fy2;
563 TPos dx, dy, x, x2;
564 int p, rem, mod, lift, delta, first, incr;
565
566
567 ey1 = TRUNC( ras.last_ey );
568 ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */
569 fy1 = ras.y - ras.last_ey;
570 fy2 = to_y - SUBPIXELS( ey2 );
571
572 dx = to_x - ras.x;
573 dy = to_y - ras.y;
574
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000575 /* XXX: we should do something about the trivial case where dx == 0, */
576 /* as it happens very often! */
David Turner5ae1bad2000-06-27 23:18:39 +0000577
578 /* perform vertical clipping */
579 {
580 TScan min, max;
581
582
583 min = ey1;
584 max = ey2;
585 if ( ey1 > ey2 )
586 {
587 min = ey2;
588 max = ey1;
589 }
590 if ( min >= ras.max_ey || max < ras.min_ey )
591 goto End;
592 }
593
594 /* everything is on a single scanline */
595 if ( ey1 == ey2 )
596 {
597 if ( render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 ) )
598 goto Error;
599 goto End;
600 }
601
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000602 /* ok, we have to render several scanlines */
David Turner5ae1bad2000-06-27 23:18:39 +0000603 p = ( ONE_PIXEL - fy1 ) * dx;
604 first = ONE_PIXEL;
605 incr = 1;
606
607 if ( dy < 0 )
608 {
609 p = fy1 * dx;
610 first = 0;
611 incr = -1;
612 dy = -dy;
613 }
614
615 delta = p / dy;
616 mod = p % dy;
617 if ( mod < 0 )
618 {
619 delta--;
620 mod += dy;
621 }
622
623 x = ras.x + delta;
624 if ( render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, first ) )
625 goto Error;
626
627 ey1 += incr;
628 if ( set_cell( RAS_VAR_ TRUNC( x ), ey1 ) )
629 goto Error;
630
631 if ( ey1 != ey2 )
632 {
633 p = ONE_PIXEL * dx;
634 lift = p / dy;
635 rem = p % dy;
636 if ( rem < 0 )
637 {
638 lift--;
639 rem += dy;
640 }
641 mod -= dy;
642
643 while ( ey1 != ey2 )
644 {
645 delta = lift;
646 mod += rem;
647 if ( mod >= 0 )
648 {
649 mod -= dy;
650 delta++;
651 }
652
653 x2 = x + delta;
654 if ( render_scanline( RAS_VAR_ ey1,
655 x, ONE_PIXEL - first, x2, first ) )
656 goto Error;
657 x = x2;
658 ey1 += incr;
659 if ( set_cell( RAS_VAR_ TRUNC( x ), ey1 ) )
660 goto Error;
661 }
662 }
663
664 if ( render_scanline( RAS_VAR_ ey1,
665 x, ONE_PIXEL - first, to_x, fy2 ) )
666 goto Error;
667
668 End:
669 ras.x = to_x;
670 ras.y = to_y;
671 ras.last_ey = SUBPIXELS( ey2 );
672
673 return 0;
674
675 Error:
676 return 1;
677 }
678
679
Werner Lemberg52005c32001-06-27 23:25:46 +0000680 static void
681 split_conic( FT_Vector* base )
David Turner5ae1bad2000-06-27 23:18:39 +0000682 {
683 TPos a, b;
684
685
686 base[4].x = base[2].x;
687 b = base[1].x;
688 a = base[3].x = ( base[2].x + b ) / 2;
689 b = base[1].x = ( base[0].x + b ) / 2;
690 base[2].x = ( a + b ) / 2;
691
692 base[4].y = base[2].y;
693 b = base[1].y;
694 a = base[3].y = ( base[2].y + b ) / 2;
695 b = base[1].y = ( base[0].y + b ) / 2;
696 base[2].y = ( a + b ) / 2;
697 }
698
699
Werner Lemberg52005c32001-06-27 23:25:46 +0000700 static int
701 render_conic( RAS_ARG_ FT_Vector* control,
702 FT_Vector* to )
David Turner5ae1bad2000-06-27 23:18:39 +0000703 {
704 TPos dx, dy;
705 int top, level;
706 int* levels;
707 FT_Vector* arc;
708
709
710 dx = DOWNSCALE( ras.x ) + to->x - ( control->x << 1 );
711 if ( dx < 0 )
712 dx = -dx;
713 dy = DOWNSCALE( ras.y ) + to->y - ( control->y << 1 );
714 if ( dy < 0 )
715 dy = -dy;
716 if ( dx < dy )
717 dx = dy;
718
719 level = 1;
720 dx = dx / ras.conic_level;
721 while ( dx > 0 )
722 {
David Turnera8194a92000-09-02 00:20:42 +0000723 dx >>= 2;
David Turner5ae1bad2000-06-27 23:18:39 +0000724 level++;
725 }
726
727 /* a shortcut to speed things up */
728 if ( level <= 1 )
729 {
730 /* we compute the mid-point directly in order to avoid */
731 /* calling split_conic() */
732 TPos to_x, to_y, mid_x, mid_y;
733
734
735 to_x = UPSCALE( to->x );
736 to_y = UPSCALE( to->y );
737 mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4;
738 mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4;
739
740 return render_line( RAS_VAR_ mid_x, mid_y ) ||
741 render_line( RAS_VAR_ to_x, to_y );
742 }
743
744 arc = ras.bez_stack;
745 levels = ras.lev_stack;
746 top = 0;
747 levels[0] = level;
748
749 arc[0].x = UPSCALE( to->x );
750 arc[0].y = UPSCALE( to->y );
751 arc[1].x = UPSCALE( control->x );
752 arc[1].y = UPSCALE( control->y );
753 arc[2].x = ras.x;
754 arc[2].y = ras.y;
755
756 while ( top >= 0 )
757 {
758 level = levels[top];
759 if ( level > 1 )
760 {
761 /* check that the arc crosses the current band */
762 TPos min, max, y;
763
764
765 min = max = arc[0].y;
766
767 y = arc[1].y;
768 if ( y < min ) min = y;
769 if ( y > max ) max = y;
770
771 y = arc[2].y;
772 if ( y < min ) min = y;
773 if ( y > max ) max = y;
774
775 if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
776 goto Draw;
777
778 split_conic( arc );
779 arc += 2;
780 top++;
781 levels[top] = levels[top - 1] = level - 1;
782 continue;
783 }
784
785 Draw:
786 {
787 TPos to_x, to_y, mid_x, mid_y;
788
789
790 to_x = arc[0].x;
791 to_y = arc[0].y;
792 mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4;
793 mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4;
794
795 if ( render_line( RAS_VAR_ mid_x, mid_y ) ||
796 render_line( RAS_VAR_ to_x, to_y ) )
797 return 1;
798
799 top--;
800 arc -= 2;
801 }
802 }
803 return 0;
804 }
805
806
Werner Lemberg52005c32001-06-27 23:25:46 +0000807 static void
808 split_cubic( FT_Vector* base )
David Turner5ae1bad2000-06-27 23:18:39 +0000809 {
810 TPos a, b, c, d;
811
812
813 base[6].x = base[3].x;
814 c = base[1].x;
815 d = base[2].x;
816 base[1].x = a = ( base[0].x + c ) / 2;
817 base[5].x = b = ( base[3].x + d ) / 2;
818 c = ( c + d ) / 2;
819 base[2].x = a = ( a + c ) / 2;
820 base[4].x = b = ( b + c ) / 2;
821 base[3].x = ( a + b ) / 2;
822
823 base[6].y = base[3].y;
824 c = base[1].y;
825 d = base[2].y;
826 base[1].y = a = ( base[0].y + c ) / 2;
827 base[5].y = b = ( base[3].y + d ) / 2;
828 c = ( c + d ) / 2;
829 base[2].y = a = ( a + c ) / 2;
830 base[4].y = b = ( b + c ) / 2;
831 base[3].y = ( a + b ) / 2;
832 }
833
834
Werner Lemberg52005c32001-06-27 23:25:46 +0000835 static int
836 render_cubic( RAS_ARG_ FT_Vector* control1,
837 FT_Vector* control2,
838 FT_Vector* to )
David Turner5ae1bad2000-06-27 23:18:39 +0000839 {
840 TPos dx, dy, da, db;
841 int top, level;
842 int* levels;
843 FT_Vector* arc;
844
845
846 dx = DOWNSCALE( ras.x ) + to->x - ( control1->x << 1 );
847 if ( dx < 0 )
848 dx = -dx;
849 dy = DOWNSCALE( ras.y ) + to->y - ( control1->y << 1 );
850 if ( dy < 0 )
851 dy = -dy;
852 if ( dx < dy )
853 dx = dy;
854 da = dx;
855
856 dx = DOWNSCALE( ras.x ) + to->x - 3 * ( control1->x + control2->x );
857 if ( dx < 0 )
858 dx = -dx;
859 dy = DOWNSCALE( ras.y ) + to->y - 3 * ( control1->x + control2->y );
860 if ( dy < 0 )
861 dy = -dy;
862 if ( dx < dy )
863 dx = dy;
864 db = dx;
865
866 level = 1;
867 da = da / ras.cubic_level;
868 db = db / ras.conic_level;
869 while ( da > 0 || db > 0 )
870 {
David Turnera8194a92000-09-02 00:20:42 +0000871 da >>= 2;
872 db >>= 3;
David Turner5ae1bad2000-06-27 23:18:39 +0000873 level++;
874 }
875
876 if ( level <= 1 )
877 {
878 TPos to_x, to_y, mid_x, mid_y;
879
880
881 to_x = UPSCALE( to->x );
882 to_y = UPSCALE( to->y );
883 mid_x = ( ras.x + to_x +
884 3 * UPSCALE( control1->x + control2->x ) ) / 8;
885 mid_y = ( ras.y + to_y +
886 3 * UPSCALE( control1->y + control2->y ) ) / 8;
887
888 return render_line( RAS_VAR_ mid_x, mid_y ) ||
889 render_line( RAS_VAR_ to_x, to_y );
890 }
891
892 arc = ras.bez_stack;
893 arc[0].x = UPSCALE( to->x );
894 arc[0].y = UPSCALE( to->y );
895 arc[1].x = UPSCALE( control2->x );
896 arc[1].y = UPSCALE( control2->y );
897 arc[2].x = UPSCALE( control1->x );
898 arc[2].y = UPSCALE( control1->y );
899 arc[3].x = ras.x;
900 arc[3].y = ras.y;
901
902 levels = ras.lev_stack;
903 top = 0;
904 levels[0] = level;
905
906 while ( top >= 0 )
907 {
908 level = levels[top];
909 if ( level > 1 )
910 {
911 /* check that the arc crosses the current band */
912 TPos min, max, y;
913
914
915 min = max = arc[0].y;
916 y = arc[1].y;
917 if ( y < min ) min = y;
918 if ( y > max ) max = y;
919 y = arc[2].y;
920 if ( y < min ) min = y;
921 if ( y > max ) max = y;
922 y = arc[3].y;
923 if ( y < min ) min = y;
924 if ( y > max ) max = y;
925 if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
926 goto Draw;
927 split_cubic( arc );
928 arc += 3;
929 top ++;
930 levels[top] = levels[top - 1] = level - 1;
931 continue;
932 }
933
934 Draw:
935 {
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000936 TPos to_x, to_y, mid_x, mid_y;
David Turner5ae1bad2000-06-27 23:18:39 +0000937
938
939 to_x = arc[0].x;
940 to_y = arc[0].y;
941 mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8;
942 mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8;
943
944 if ( render_line( RAS_VAR_ mid_x, mid_y ) ||
945 render_line( RAS_VAR_ to_x, to_y ) )
946 return 1;
947 top --;
948 arc -= 3;
949 }
950 }
951 return 0;
952 }
953
954
955 /* a macro comparing two cell pointers. Returns true if a <= b. */
956#if 1
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000957
David Turner5ae1bad2000-06-27 23:18:39 +0000958#define PACK( a ) ( ( (long)(a)->y << 16 ) + (a)->x )
959#define LESS_THAN( a, b ) ( PACK( a ) < PACK( b ) )
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000960
David Turner5ae1bad2000-06-27 23:18:39 +0000961#else /* 1 */
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000962
David Turner5ae1bad2000-06-27 23:18:39 +0000963#define LESS_THAN( a, b ) ( (a)->y < (b)->y || \
964 ( (a)->y == (b)->y && (a)->x < (b)->x ) )
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000965
David Turner5ae1bad2000-06-27 23:18:39 +0000966#endif /* 1 */
967
968#define SWAP_CELLS( a, b, temp ) do \
969 { \
970 temp = *(a); \
971 *(a) = *(b); \
972 *(b) = temp; \
973 } while ( 0 )
974#define DEBUG_SORT
975#define QUICK_SORT
976
977#ifdef SHELL_SORT
978
Werner Lembergfbeb41d2000-07-02 00:27:53 +0000979 /* a simple shell sort algorithm that works directly on our */
980 /* cells table */
Werner Lemberg52005c32001-06-27 23:25:46 +0000981 static void
982 shell_sort ( PCell cells,
983 int count )
David Turner5ae1bad2000-06-27 23:18:39 +0000984 {
985 PCell i, j, limit = cells + count;
986 TCell temp;
987 int gap;
988
989
990 /* compute initial gap */
991 for ( gap = 0; ++gap < count; gap *= 3 )
992 ;
993
994 while ( gap /= 3 )
995 {
996 for ( i = cells + gap; i < limit; i++ )
997 {
998 for ( j = i - gap; ; j -= gap )
999 {
1000 PCell k = j + gap;
1001
1002
1003 if ( LESS_THAN( j, k ) )
1004 break;
1005
1006 SWAP_CELLS( j, k, temp );
1007
1008 if ( j < cells + gap )
1009 break;
1010 }
1011 }
1012 }
1013 }
1014
1015#endif /* SHELL_SORT */
1016
1017
1018#ifdef QUICK_SORT
1019
1020 /* This is a non-recursive quicksort that directly process our cells */
1021 /* array. It should be faster than calling the stdlib qsort(), and we */
1022 /* can even tailor our insertion threshold... */
1023
1024#define QSORT_THRESHOLD 9 /* below this size, a sub-array will be sorted */
Werner Lembergfbeb41d2000-07-02 00:27:53 +00001025 /* through a normal insertion sort */
David Turner5ae1bad2000-06-27 23:18:39 +00001026
Werner Lemberg52005c32001-06-27 23:25:46 +00001027 static void
1028 quick_sort( PCell cells,
1029 int count )
David Turner5ae1bad2000-06-27 23:18:39 +00001030 {
1031 PCell stack[40]; /* should be enough ;-) */
1032 PCell* top; /* top of stack */
1033 PCell base, limit;
1034 TCell temp;
1035
1036
1037 limit = cells + count;
1038 base = cells;
1039 top = stack;
1040
1041 for (;;)
1042 {
Werner Lemberg914b2892001-03-10 17:07:42 +00001043 int len = (int)( limit - base );
David Turner5ae1bad2000-06-27 23:18:39 +00001044 PCell i, j, pivot;
1045
1046
1047 if ( len > QSORT_THRESHOLD )
1048 {
1049 /* we use base + len/2 as the pivot */
1050 pivot = base + len / 2;
1051 SWAP_CELLS( base, pivot, temp );
1052
1053 i = base + 1;
1054 j = limit - 1;
1055
1056 /* now ensure that *i <= *base <= *j */
1057 if ( LESS_THAN( j, i ) )
1058 SWAP_CELLS( i, j, temp );
1059
1060 if ( LESS_THAN( base, i ) )
1061 SWAP_CELLS( base, i, temp );
1062
1063 if ( LESS_THAN( j, base ) )
1064 SWAP_CELLS( base, j, temp );
1065
1066 for (;;)
1067 {
1068 do i++; while ( LESS_THAN( i, base ) );
1069 do j--; while ( LESS_THAN( base, j ) );
1070
1071 if ( i > j )
1072 break;
1073
1074 SWAP_CELLS( i, j, temp );
1075 }
1076
1077 SWAP_CELLS( base, j, temp );
1078
1079 /* now, push the largest sub-array */
1080 if ( j - base > limit - i )
1081 {
1082 top[0] = base;
1083 top[1] = j;
1084 base = i;
1085 }
1086 else
1087 {
1088 top[0] = i;
1089 top[1] = limit;
1090 limit = j;
1091 }
1092 top += 2;
1093 }
1094 else
1095 {
1096 /* the sub-array is small, perform insertion sort */
1097 j = base;
1098 i = j + 1;
1099
1100 for ( ; i < limit; j = i, i++ )
1101 {
1102 for ( ; LESS_THAN( j + 1, j ); j-- )
1103 {
1104 SWAP_CELLS( j + 1, j, temp );
1105 if ( j == base )
1106 break;
1107 }
1108 }
1109 if ( top > stack )
1110 {
1111 top -= 2;
1112 base = top[0];
1113 limit = top[1];
1114 }
1115 else
1116 break;
1117 }
1118 }
1119 }
1120
1121#endif /* QUICK_SORT */
1122
1123
1124#ifdef DEBUG_GRAYS
1125#ifdef DEBUG_SORT
1126
Werner Lemberg52005c32001-06-27 23:25:46 +00001127 static int
1128 check_sort( PCell cells,
1129 int count )
David Turner5ae1bad2000-06-27 23:18:39 +00001130 {
1131 PCell p, q;
1132
1133
1134 for ( p = cells + count - 2; p >= cells; p-- )
1135 {
1136 q = p + 1;
1137 if ( !LESS_THAN( p, q ) )
1138 return 0;
1139 }
1140 return 1;
1141 }
1142
1143#endif /* DEBUG_SORT */
1144#endif /* DEBUG_GRAYS */
1145
1146
Werner Lemberg52005c32001-06-27 23:25:46 +00001147 static int
1148 Move_To( FT_Vector* to,
1149 FT_Raster raster )
David Turner5ae1bad2000-06-27 23:18:39 +00001150 {
1151 TPos x, y;
1152
1153
1154 /* record current cell, if any */
1155 record_cell( (PRaster)raster );
1156
1157 /* start to a new position */
1158 x = UPSCALE( to->x );
1159 y = UPSCALE( to->y );
1160 start_cell( (PRaster)raster, TRUNC( x ), TRUNC( y ) );
1161 ((PRaster)raster)->x = x;
1162 ((PRaster)raster)->y = y;
1163 return 0;
1164 }
1165
1166
Werner Lemberg52005c32001-06-27 23:25:46 +00001167 static int
1168 Line_To( FT_Vector* to,
1169 FT_Raster raster )
David Turner5ae1bad2000-06-27 23:18:39 +00001170 {
1171 return render_line( (PRaster)raster,
1172 UPSCALE( to->x ), UPSCALE( to->y ) );
1173 }
1174
1175
Werner Lemberg52005c32001-06-27 23:25:46 +00001176 static int
1177 Conic_To( FT_Vector* control,
1178 FT_Vector* to,
1179 FT_Raster raster )
David Turner5ae1bad2000-06-27 23:18:39 +00001180 {
1181 return render_conic( (PRaster)raster, control, to );
1182 }
1183
1184
Werner Lemberg52005c32001-06-27 23:25:46 +00001185 static int
1186 Cubic_To( FT_Vector* control1,
1187 FT_Vector* control2,
1188 FT_Vector* to,
1189 FT_Raster raster )
David Turner5ae1bad2000-06-27 23:18:39 +00001190 {
1191 return render_cubic( (PRaster)raster, control1, control2, to );
1192 }
1193
1194
Werner Lemberg52005c32001-06-27 23:25:46 +00001195 static void
1196 grays_render_span( int y,
1197 int count,
1198 FT_Span* spans,
1199 PRaster raster )
David Turner5ae1bad2000-06-27 23:18:39 +00001200 {
1201 unsigned char* p;
1202 FT_Bitmap* map = &raster->target;
1203
1204
1205 /* first of all, compute the scanline offset */
1206 p = (unsigned char*)map->buffer - y * map->pitch;
1207 if ( map->pitch >= 0 )
1208 p += ( map->rows - 1 ) * map->pitch;
1209
1210 for ( ; count > 0; count--, spans++ )
1211 {
1212 if ( spans->coverage )
1213#if 1
1214 memset( p + spans->x, (unsigned char)spans->coverage, spans->len );
1215#else /* 1 */
1216 {
1217 q = p + spans->x;
1218 limit = q + spans->len;
1219 for ( ; q < limit; q++ )
1220 q[0] = (unsigned char)spans->coverage;
1221 }
1222#endif /* 1 */
1223 }
1224 }
1225
1226
1227#ifdef DEBUG_GRAYS
1228
1229#include <stdio.h>
1230
Werner Lemberg52005c32001-06-27 23:25:46 +00001231 static void
1232 dump_cells( RAS_ARG )
David Turner5ae1bad2000-06-27 23:18:39 +00001233 {
1234 PCell cell, limit;
1235 int y = -1;
1236
1237
1238 cell = ras.cells;
1239 limit = cell + ras.num_cells;
1240
1241 for ( ; cell < limit; cell++ )
1242 {
1243 if ( cell->y != y )
1244 {
1245 fprintf( stderr, "\n%2d: ", cell->y );
1246 y = cell->y;
1247 }
1248 fprintf( stderr, "[%d %d %d]",
1249 cell->x, cell->area, cell->cover );
1250 }
1251 fprintf(stderr, "\n" );
1252 }
1253
1254#endif /* DEBUG_GRAYS */
1255
1256
Werner Lemberg52005c32001-06-27 23:25:46 +00001257 static void
1258 grays_hline( RAS_ARG_ TScan x,
1259 TScan y,
1260 TPos area,
1261 int acount )
David Turner5ae1bad2000-06-27 23:18:39 +00001262 {
1263 FT_Span* span;
1264 int count;
1265 int coverage;
1266
1267
1268 /* compute the coverage line's coverage, depending on the */
1269 /* outline fill rule */
1270 /* */
1271 /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
1272 /* */
1273 coverage = area >> ( PIXEL_BITS * 2 + 1 - 8); /* use range 0..256 */
1274
1275 if ( ras.outline.flags & ft_outline_even_odd_fill )
1276 {
1277 if ( coverage < 0 )
1278 coverage = -coverage;
1279
1280 while ( coverage >= 512 )
1281 coverage -= 512;
1282
1283 if ( coverage > 256 )
1284 coverage = 512 - coverage;
1285 else if ( coverage == 256 )
1286 coverage = 255;
1287 }
1288 else
1289 {
1290 /* normal non-zero winding rule */
1291 if ( coverage < 0 )
1292 coverage = -coverage;
1293
1294 if ( coverage >= 256 )
1295 coverage = 255;
1296 }
1297
1298 y += ras.min_ey;
1299 x += ras.min_ex;
1300
1301 if ( coverage )
1302 {
1303 /* see if we can add this span to the current list */
1304 count = ras.num_gray_spans;
1305 span = ras.gray_spans + count - 1;
1306 if ( count > 0 &&
1307 ras.span_y == y &&
1308 (int)span->x + span->len == (int)x &&
1309 span->coverage == coverage )
1310 {
Werner Lemberg8eb03532001-06-19 23:03:41 +00001311 span->len = (unsigned short)( span->len + acount );
David Turner5ae1bad2000-06-27 23:18:39 +00001312 return;
1313 }
1314
1315 if ( ras.span_y != y || count >= FT_MAX_GRAY_SPANS )
1316 {
David Turnerb71c6af2000-09-17 17:17:16 +00001317 if ( ras.render_span && count > 0 )
David Turner5ae1bad2000-06-27 23:18:39 +00001318 ras.render_span( ras.span_y, count, ras.gray_spans,
1319 ras.render_span_data );
1320 /* ras.render_span( span->y, ras.gray_spans, count ); */
1321
1322#ifdef DEBUG_GRAYS
1323
1324 if ( ras.span_y >= 0 )
1325 {
1326 int n;
1327
1328
1329 fprintf( stderr, "y=%3d ", ras.span_y );
1330 span = ras.gray_spans;
1331 for ( n = 0; n < count; n++, span++ )
1332 fprintf( stderr, "[%d..%d]:%02x ",
1333 span->x, span->x + span->len - 1, span->coverage );
1334 fprintf( stderr, "\n" );
1335 }
1336
1337#endif /* DEBUG_GRAYS */
1338
1339 ras.num_gray_spans = 0;
1340 ras.span_y = y;
1341
1342 count = 0;
1343 span = ras.gray_spans;
1344 }
1345 else
1346 span++;
1347
1348 /* add a gray span to the current list */
1349 span->x = (short)x;
1350 span->len = (unsigned short)acount;
1351 span->coverage = (unsigned char)coverage;
1352 ras.num_gray_spans++;
1353 }
1354 }
1355
1356
Werner Lemberg52005c32001-06-27 23:25:46 +00001357 static void
1358 grays_sweep( RAS_ARG_ FT_Bitmap* target )
David Turner5ae1bad2000-06-27 23:18:39 +00001359 {
1360 TScan x, y, cover, area;
1361 PCell start, cur, limit;
1362
David Turnerc6a92202000-07-04 18:12:13 +00001363 FT_UNUSED( target );
David Turner5ae1bad2000-06-27 23:18:39 +00001364
David Turner5aeaad62001-02-23 17:47:41 +00001365 if ( ras.num_cells == 0 )
1366 return;
1367
David Turner5ae1bad2000-06-27 23:18:39 +00001368 cur = ras.cells;
1369 limit = cur + ras.num_cells;
1370
1371 cover = 0;
1372 ras.span_y = -1;
1373 ras.num_gray_spans = 0;
1374
1375 for (;;)
1376 {
1377 start = cur;
1378 y = start->y;
1379 x = start->x;
1380
1381 area = start->area;
1382 cover += start->cover;
1383
1384 /* accumulate all start cells */
1385 for (;;)
1386 {
1387 ++cur;
1388 if ( cur >= limit || cur->y != start->y || cur->x != start->x )
1389 break;
1390
1391 area += cur->area;
1392 cover += cur->cover;
1393 }
1394
1395 /* if the start cell has a non-null area, we must draw an */
1396 /* individual gray pixel there */
1397 if ( area && x >= 0 )
1398 {
1399 grays_hline( RAS_VAR_ x, y, cover * ( ONE_PIXEL * 2 ) - area, 1 );
1400 x++;
1401 }
1402
1403 if ( x < 0 )
1404 x = 0;
1405
1406 if ( cur < limit && start->y == cur->y )
1407 {
1408 /* draw a gray span between the start cell and the current one */
1409 if ( cur->x > x )
1410 grays_hline( RAS_VAR_ x, y,
1411 cover * ( ONE_PIXEL * 2 ), cur->x - x );
1412 }
1413 else
1414 {
1415 /* draw a gray span until the end of the clipping region */
1416 if ( cover && x < ras.max_ex - ras.min_ex )
1417 grays_hline( RAS_VAR_ x, y,
1418 cover * ( ONE_PIXEL * 2 ),
1419 ras.max_ex - x - ras.min_ex );
1420 cover = 0;
1421 }
1422
1423 if ( cur >= limit )
1424 break;
1425 }
1426
1427 if ( ras.render_span && ras.num_gray_spans > 0 )
1428 ras.render_span( ras.span_y, ras.num_gray_spans,
1429 ras.gray_spans, ras.render_span_data );
1430
1431#ifdef DEBUG_GRAYS
1432
1433 {
1434 int n;
1435 FT_Span* span;
1436
1437
1438 fprintf( stderr, "y=%3d ", ras.span_y );
1439 span = ras.gray_spans;
1440 for ( n = 0; n < ras.num_gray_spans; n++, span++ )
1441 fprintf( stderr, "[%d..%d]:%02x ",
1442 span->x, span->x + span->len - 1, span->coverage );
1443 fprintf( stderr, "\n" );
1444 }
1445
1446#endif /* DEBUG_GRAYS */
1447
1448 }
1449
1450
1451#ifdef _STANDALONE_
1452
1453 /*************************************************************************/
1454 /* */
1455 /* The following function should only compile in stand_alone mode, */
1456 /* i.e., when building this component without the rest of FreeType. */
1457 /* */
1458 /*************************************************************************/
1459
1460 /*************************************************************************/
1461 /* */
1462 /* <Function> */
1463 /* FT_Outline_Decompose */
1464 /* */
1465 /* <Description> */
1466 /* Walks over an outline's structure to decompose it into individual */
1467 /* segments and Bezier arcs. This function is also able to emit */
1468 /* `move to' and `close to' operations to indicate the start and end */
1469 /* of new contours in the outline. */
1470 /* */
1471 /* <Input> */
1472 /* outline :: A pointer to the source target. */
1473 /* */
1474 /* interface :: A table of `emitters', i.e,. function pointers called */
1475 /* during decomposition to indicate path operations. */
1476 /* */
1477 /* user :: A typeless pointer which is passed to each emitter */
1478 /* during the decomposition. It can be used to store */
1479 /* the state during the decomposition. */
1480 /* */
1481 /* <Return> */
1482 /* Error code. 0 means sucess. */
1483 /* */
Werner Lemberg52005c32001-06-27 23:25:46 +00001484 static int
1485 FT_Outline_Decompose( FT_Outline* outline,
1486 const FT_Outline_Funcs* interface,
1487 void* user )
David Turner5ae1bad2000-06-27 23:18:39 +00001488 {
1489#undef SCALED
1490#define SCALED( x ) ( ( (x) << shift ) - delta )
1491
1492 FT_Vector v_last;
1493 FT_Vector v_control;
1494 FT_Vector v_start;
1495
1496 FT_Vector* point;
1497 FT_Vector* limit;
1498 char* tags;
1499
1500 int n; /* index of contour in outline */
1501 int first; /* index of first point in contour */
1502 int error;
1503 char tag; /* current point's state */
1504
1505 int shift = interface->shift;
1506 FT_Pos delta = interface->delta;
1507
1508
1509 first = 0;
1510
1511 for ( n = 0; n < outline->n_contours; n++ )
1512 {
1513 int last; /* index of last point in contour */
1514
1515
1516 last = outline->contours[n];
1517 limit = outline->points + last;
1518
1519 v_start = outline->points[first];
1520 v_last = outline->points[last];
1521
1522 v_start.x = SCALED( v_start.x ); v_start.y = SCALED( v_start.y );
1523 v_last.x = SCALED( v_last.x ); v_last.y = SCALED( v_last.y );
1524
1525 v_control = v_start;
1526
1527 point = outline->points + first;
1528 tags = outline->tags + first;
1529 tag = FT_CURVE_TAG( tags[0] );
1530
1531 /* A contour cannot start with a cubic control point! */
1532 if ( tag == FT_Curve_Tag_Cubic )
1533 goto Invalid_Outline;
1534
1535 /* check first point to determine origin */
1536 if ( tag == FT_Curve_Tag_Conic )
1537 {
1538 /* first point is conic control. Yes, this happens. */
1539 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_Curve_Tag_On )
1540 {
1541 /* start at last point if it is on the curve */
1542 v_start = v_last;
1543 limit--;
1544 }
1545 else
1546 {
1547 /* if both first and last points are conic, */
1548 /* start at their middle and record its position */
1549 /* for closure */
1550 v_start.x = ( v_start.x + v_last.x ) / 2;
1551 v_start.y = ( v_start.y + v_last.y ) / 2;
1552
1553 v_last = v_start;
1554 }
1555 point--;
1556 tags--;
1557 }
1558
1559 error = interface->move_to( &v_start, user );
1560 if ( error )
1561 goto Exit;
1562
1563 while ( point < limit )
1564 {
1565 point++;
1566 tags++;
1567
1568 tag = FT_CURVE_TAG( tags[0] );
1569 switch ( tag )
1570 {
1571 case FT_Curve_Tag_On: /* emit a single line_to */
1572 {
1573 FT_Vector vec;
1574
1575
1576 vec.x = SCALED( point->x );
1577 vec.y = SCALED( point->y );
1578
1579 error = interface->line_to( &vec, user );
1580 if ( error )
1581 goto Exit;
1582 continue;
1583 }
1584
1585 case FT_Curve_Tag_Conic: /* consume conic arcs */
1586 {
1587 v_control.x = SCALED( point->x );
1588 v_control.y = SCALED( point->y );
1589
1590 Do_Conic:
1591 if ( point < limit )
1592 {
1593 FT_Vector vec;
1594 FT_Vector v_middle;
1595
1596
1597 point++;
1598 tags++;
1599 tag = FT_CURVE_TAG( tags[0] );
1600
1601 vec.x = SCALED( point->x );
1602 vec.y = SCALED( point->y );
1603
1604 if ( tag == FT_Curve_Tag_On )
1605 {
1606 error = interface->conic_to( &v_control, &vec, user );
1607 if ( error )
1608 goto Exit;
1609 continue;
1610 }
1611
1612 if ( tag != FT_Curve_Tag_Conic )
1613 goto Invalid_Outline;
1614
1615 v_middle.x = ( v_control.x + vec.x ) / 2;
1616 v_middle.y = ( v_control.y + vec.y ) / 2;
1617
1618 error = interface->conic_to( &v_control, &v_middle, user );
1619 if ( error )
1620 goto Exit;
1621
1622 v_control = vec;
1623 goto Do_Conic;
1624 }
1625
1626 error = interface->conic_to( &v_control, &v_start, user );
1627 goto Close;
1628 }
1629
1630 default: /* FT_Curve_Tag_Cubic */
1631 {
1632 FT_Vector vec1, vec2;
1633
1634
1635 if ( point + 1 > limit ||
1636 FT_CURVE_TAG( tags[1] ) != FT_Curve_Tag_Cubic )
1637 goto Invalid_Outline;
1638
1639 point += 2;
1640 tags += 2;
1641
1642 vec1.x = SCALED( point[-2].x ); vec1.y = SCALED( point[-2].y );
1643 vec2.x = SCALED( point[-1].x ); vec2.y = SCALED( point[-1].y );
1644
1645 if ( point <= limit )
1646 {
1647 FT_Vector vec;
1648
1649
1650 vec.x = SCALED( point->x );
1651 vec.y = SCALED( point->y );
1652
1653 error = interface->cubic_to( &vec1, &vec2, &vec, user );
1654 if ( error )
1655 goto Exit;
1656 continue;
1657 }
1658
1659 error = interface->cubic_to( &vec1, &vec2, &v_start, user );
1660 goto Close;
1661 }
1662 }
1663 }
1664
1665 /* close the contour with a line segment */
1666 error = interface->line_to( &v_start, user );
1667
1668 Close:
1669 if ( error )
1670 goto Exit;
1671
1672 first = last + 1;
1673 }
1674
1675 return 0;
1676
1677 Exit:
1678 return error;
1679
1680 Invalid_Outline:
1681 return ErrRaster_Invalid_Outline;
1682 }
1683
1684#endif /* _STANDALONE_ */
1685
1686
1687 typedef struct TBand_
1688 {
1689 FT_Pos min, max;
1690
1691 } TBand;
1692
1693
Werner Lemberg52005c32001-06-27 23:25:46 +00001694 static int
1695 grays_convert_glyph( RAS_ARG_ FT_Outline* outline )
David Turner5ae1bad2000-06-27 23:18:39 +00001696 {
1697 static
David Turnerdc26e7b2000-10-23 22:46:56 +00001698 const FT_Outline_Funcs interface =
David Turner5ae1bad2000-06-27 23:18:39 +00001699 {
Werner Lembergfbeb41d2000-07-02 00:27:53 +00001700 (FT_Outline_MoveTo_Func) Move_To,
1701 (FT_Outline_LineTo_Func) Line_To,
David Turner5ae1bad2000-06-27 23:18:39 +00001702 (FT_Outline_ConicTo_Func)Conic_To,
1703 (FT_Outline_CubicTo_Func)Cubic_To,
1704 0,
1705 0
1706 };
1707
Werner Lemberg594f0c92000-12-20 22:09:41 +00001708 TBand bands[40], *band;
1709 int n, num_bands;
1710 TPos min, max, max_y;
1711 FT_BBox* clip;
David Turner5ae1bad2000-06-27 23:18:39 +00001712
1713
1714 /* Set up state in the raster object */
1715 compute_cbox( RAS_VAR_ outline );
1716
1717 /* clip to target bitmap, exit if nothing to do */
David Turner859a18a2000-12-14 18:50:40 +00001718 clip = &ras.clip_box;
1719
1720 if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax ||
1721 ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax )
David Turner5ae1bad2000-06-27 23:18:39 +00001722 return 0;
1723
David Turner859a18a2000-12-14 18:50:40 +00001724 if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin;
1725 if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin;
David Turner5ae1bad2000-06-27 23:18:39 +00001726
David Turner859a18a2000-12-14 18:50:40 +00001727 if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax;
1728 if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax;
David Turner5ae1bad2000-06-27 23:18:39 +00001729
Werner Lembergfbeb41d2000-07-02 00:27:53 +00001730 /* simple heuristic used to speed-up the bezier decomposition -- see */
1731 /* the code in render_conic() and render_cubic() for more details */
David Turner5ae1bad2000-06-27 23:18:39 +00001732 ras.conic_level = 32;
1733 ras.cubic_level = 16;
1734
1735 {
1736 int level = 0;
1737
1738
1739 if ( ras.max_ex > 24 || ras.max_ey > 24 )
1740 level++;
1741 if ( ras.max_ex > 120 || ras.max_ey > 120 )
Werner Lembergf9b03752000-09-11 22:50:13 +00001742 level++;
David Turner5ae1bad2000-06-27 23:18:39 +00001743
1744 ras.conic_level <<= level;
1745 ras.cubic_level <<= level;
1746 }
1747
1748 /* setup vertical bands */
1749 num_bands = ( ras.max_ey - ras.min_ey ) / ras.band_size;
1750 if ( num_bands == 0 ) num_bands = 1;
1751 if ( num_bands >= 39 ) num_bands = 39;
1752
1753 ras.band_shoot = 0;
1754
1755 min = ras.min_ey;
1756 max_y = ras.max_ey;
1757
1758 for ( n = 0; n < num_bands; n++, min = max )
1759 {
1760 max = min + ras.band_size;
1761 if ( n == num_bands - 1 || max > max_y )
1762 max = max_y;
1763
1764 bands[0].min = min;
1765 bands[0].max = max;
1766 band = bands;
1767
1768 while ( band >= bands )
1769 {
1770 FT_Pos bottom, top, middle;
1771 int error;
1772
1773
1774 ras.num_cells = 0;
1775 ras.invalid = 1;
1776 ras.min_ey = band->min;
1777 ras.max_ey = band->max;
1778
1779 error = FT_Outline_Decompose( outline, &interface, &ras ) ||
1780 record_cell( RAS_VAR );
1781
1782 if ( !error )
1783 {
1784#ifdef SHELL_SORT
1785 shell_sort( ras.cells, ras.num_cells );
1786#else
1787 quick_sort( ras.cells, ras.num_cells );
1788#endif
1789
1790#ifdef DEBUG_GRAYS
1791 check_sort( ras.cells, ras.num_cells );
1792 dump_cells( RAS_VAR );
1793#endif
1794
1795 grays_sweep( RAS_VAR_ &ras.target );
1796 band--;
1797 continue;
1798 }
1799
1800 /* render pool overflow, we will reduce the render band by half */
1801 bottom = band->min;
1802 top = band->max;
1803 middle = bottom + ( ( top - bottom ) >> 1 );
1804
1805 /* waoow! This is too complex for a single scanline, something */
1806 /* must be really rotten here! */
1807 if ( middle == bottom )
1808 {
1809#ifdef DEBUG_GRAYS
1810 fprintf( stderr, "Rotten glyph!\n" );
1811#endif
1812 return 1;
1813 }
1814
1815 if ( bottom-top >= ras.band_size )
1816 ras.band_shoot++;
1817
1818 band[1].min = bottom;
1819 band[1].max = middle;
1820 band[0].min = middle;
1821 band[0].max = top;
1822 band++;
1823 }
1824 }
1825
1826 if ( ras.band_shoot > 8 && ras.band_size > 16 )
1827 ras.band_size = ras.band_size / 2;
1828
1829 return 0;
1830 }
1831
1832
Werner Lemberg52005c32001-06-27 23:25:46 +00001833 extern int
1834 grays_raster_render( PRaster raster,
1835 FT_Raster_Params* params )
David Turner5ae1bad2000-06-27 23:18:39 +00001836 {
1837 FT_Outline* outline = (FT_Outline*)params->source;
1838 FT_Bitmap* target_map = params->target;
1839
1840
1841 if ( !raster || !raster->cells || !raster->max_cells )
1842 return -1;
1843
1844 /* return immediately if the outline is empty */
1845 if ( outline->n_points == 0 || outline->n_contours <= 0 )
1846 return 0;
1847
1848 if ( !outline || !outline->contours || !outline->points )
1849 return ErrRaster_Invalid_Outline;
1850
1851 if ( outline->n_points !=
1852 outline->contours[outline->n_contours - 1] + 1 )
1853 return ErrRaster_Invalid_Outline;
1854
David Turner78dd7102000-10-03 19:13:11 +00001855 /* if direct mode is not set, we must have a target bitmap */
Werner Lemberg6fbe4db2000-10-05 04:53:31 +00001856 if ( ( params->flags & ft_raster_flag_direct ) == 0 &&
1857 ( !target_map || !target_map->buffer ) )
David Turner5ae1bad2000-06-27 23:18:39 +00001858 return -1;
1859
David Turner78dd7102000-10-03 19:13:11 +00001860 /* this version does not support monochrome rendering */
Werner Lemberg6fbe4db2000-10-05 04:53:31 +00001861 if ( !( params->flags & ft_raster_flag_aa ) )
David Turner5ae1bad2000-06-27 23:18:39 +00001862 return ErrRaster_Invalid_Mode;
1863
David Turner859a18a2000-12-14 18:50:40 +00001864 /* compute clipping box */
Werner Lemberg594f0c92000-12-20 22:09:41 +00001865 if ( ( params->flags & ft_raster_flag_direct ) == 0 )
David Turner859a18a2000-12-14 18:50:40 +00001866 {
1867 /* compute clip box from target pixmap */
1868 ras.clip_box.xMin = 0;
1869 ras.clip_box.yMin = 0;
1870 ras.clip_box.xMax = target_map->width;
1871 ras.clip_box.yMax = target_map->rows;
1872 }
1873 else if ( params->flags & ft_raster_flag_clip )
1874 {
1875 ras.clip_box = params->clip_box;
1876 }
1877 else
1878 {
Werner Lembergcf24d512001-06-18 14:23:45 +00001879 ras.clip_box.xMin = -32768L;
1880 ras.clip_box.yMin = -32768L;
1881 ras.clip_box.xMax = 32767L;
1882 ras.clip_box.yMax = 32767L;
David Turner859a18a2000-12-14 18:50:40 +00001883 }
1884
David Turner5ae1bad2000-06-27 23:18:39 +00001885 ras.outline = *outline;
David Turner5ae1bad2000-06-27 23:18:39 +00001886 ras.num_cells = 0;
1887 ras.invalid = 1;
1888
Werner Lemberg6fbe4db2000-10-05 04:53:31 +00001889 if ( target_map )
David Turner78dd7102000-10-03 19:13:11 +00001890 ras.target = *target_map;
David Turner78dd7102000-10-03 19:13:11 +00001891
David Turner5ae1bad2000-06-27 23:18:39 +00001892 ras.render_span = (FT_Raster_Span_Func)grays_render_span;
1893 ras.render_span_data = &ras;
1894
1895 if ( params->flags & ft_raster_flag_direct )
1896 {
1897 ras.render_span = (FT_Raster_Span_Func)params->gray_spans;
1898 ras.render_span_data = params->user;
1899 }
1900
1901 return grays_convert_glyph( (PRaster)raster, outline );
1902 }
1903
1904
1905 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
1906 /**** a static object. *****/
1907
1908#ifdef _STANDALONE_
1909
Werner Lemberg52005c32001-06-27 23:25:46 +00001910 static int
1911 grays_raster_new( void* memory,
1912 FT_Raster* araster )
David Turner5ae1bad2000-06-27 23:18:39 +00001913 {
1914 static TRaster the_raster;
1915
David Turnerc6a92202000-07-04 18:12:13 +00001916 FT_UNUSED( memory );
David Turner5ae1bad2000-06-27 23:18:39 +00001917
1918
1919 *araster = (FT_Raster)&the_raster;
1920 memset( &the_raster, 0, sizeof ( the_raster ) );
1921
1922 return 0;
1923 }
1924
1925
Werner Lemberg52005c32001-06-27 23:25:46 +00001926 static void
1927 grays_raster_done( FT_Raster raster )
David Turner5ae1bad2000-06-27 23:18:39 +00001928 {
1929 /* nothing */
David Turnerc6a92202000-07-04 18:12:13 +00001930 FT_UNUSED( raster );
David Turner5ae1bad2000-06-27 23:18:39 +00001931 }
1932
1933#else /* _STANDALONE_ */
1934
Werner Lemberg52005c32001-06-27 23:25:46 +00001935 static int
1936 grays_raster_new( FT_Memory memory,
1937 FT_Raster* araster )
David Turner5ae1bad2000-06-27 23:18:39 +00001938 {
1939 FT_Error error;
1940 PRaster raster;
1941
1942
1943 *araster = 0;
1944 if ( !ALLOC( raster, sizeof ( TRaster ) ) )
1945 {
1946 raster->memory = memory;
1947 *araster = (FT_Raster)raster;
1948 }
1949
1950 return error;
1951 }
1952
1953
Werner Lemberg52005c32001-06-27 23:25:46 +00001954 static void
1955 grays_raster_done( FT_Raster raster )
David Turner5ae1bad2000-06-27 23:18:39 +00001956 {
1957 FT_Memory memory = (FT_Memory)((PRaster)raster)->memory;
1958
1959
1960 FREE( raster );
1961 }
1962
1963#endif /* _STANDALONE_ */
1964
1965
Werner Lemberg52005c32001-06-27 23:25:46 +00001966 static void
1967 grays_raster_reset( FT_Raster raster,
1968 const char* pool_base,
1969 long pool_size )
David Turner5ae1bad2000-06-27 23:18:39 +00001970 {
1971 PRaster rast = (PRaster)raster;
1972
1973
1974 if ( raster && pool_base && pool_size >= 4096 )
1975 init_cells( rast, (char*)pool_base, pool_size );
1976
1977 rast->band_size = ( pool_size / sizeof ( TCell ) ) / 8;
1978 }
1979
1980
David Turnerdc26e7b2000-10-23 22:46:56 +00001981 const FT_Raster_Funcs ft_grays_raster =
David Turner5ae1bad2000-06-27 23:18:39 +00001982 {
1983 ft_glyph_format_outline,
1984
1985 (FT_Raster_New_Func) grays_raster_new,
1986 (FT_Raster_Reset_Func) grays_raster_reset,
1987 (FT_Raster_Set_Mode_Func)0,
1988 (FT_Raster_Render_Func) grays_raster_render,
1989 (FT_Raster_Done_Func) grays_raster_done
1990 };
1991
1992
1993/* END */