1 /***************************************************************************/ |
|
2 /* */ |
|
3 /* ftgrays.c */ |
|
4 /* */ |
|
5 /* A new `perfect' anti-aliasing renderer (body). */ |
|
6 /* */ |
|
7 /* Copyright 2000-2003, 2005-2011 by */ |
|
8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ |
|
9 /* */ |
|
10 /* This file is part of the FreeType project, and may only be used, */ |
|
11 /* modified, and distributed under the terms of the FreeType project */ |
|
12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ |
|
13 /* this file you indicate that you have read the license and */ |
|
14 /* understand and accept it fully. */ |
|
15 /* */ |
|
16 /***************************************************************************/ |
|
17 |
|
18 /*************************************************************************/ |
|
19 /* */ |
|
20 /* This file can be compiled without the rest of the FreeType engine, by */ |
|
21 /* defining the _STANDALONE_ macro when compiling it. You also need to */ |
|
22 /* put the files `ftgrays.h' and `ftimage.h' into the current */ |
|
23 /* compilation directory. Typically, you could do something like */ |
|
24 /* */ |
|
25 /* - copy `src/smooth/ftgrays.c' (this file) to your current directory */ |
|
26 /* */ |
|
27 /* - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the */ |
|
28 /* 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_gray_raster.raster_new'; an anti-aliased bitmap can be generated */ |
|
36 /* with a call to `ft_gray_raster.raster_render'. */ |
|
37 /* */ |
|
38 /* See the comments and documentation in the file `ftimage.h' for more */ |
|
39 /* details on how the raster works. */ |
|
40 /* */ |
|
41 /*************************************************************************/ |
|
42 |
|
43 /*************************************************************************/ |
|
44 /* */ |
|
45 /* 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 */ |
|
57 /* compared to Raph's. Coverage information is stored in a very */ |
|
58 /* different way, and I don't use sorted vector paths. Also, it doesn't */ |
|
59 /* use floating point values. */ |
|
60 /* */ |
|
61 /* This renderer has the following advantages: */ |
|
62 /* */ |
|
63 /* - It doesn't need an intermediate bitmap. Instead, one can supply a */ |
|
64 /* callback function that will be called by the renderer to draw gray */ |
|
65 /* spans on any target surface. You can thus do direct composition on */ |
|
66 /* any kind of bitmap, provided that you give the renderer the right */ |
|
67 /* 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 /*************************************************************************/ |
|
85 /* */ |
|
86 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ |
|
87 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ |
|
88 /* messages during execution. */ |
|
89 /* */ |
|
90 #undef FT_COMPONENT |
|
91 #define FT_COMPONENT trace_smooth |
|
92 |
|
93 |
|
94 #ifdef _STANDALONE_ |
|
95 |
|
96 |
|
97 /* define this to dump debugging information */ |
|
98 /* #define FT_DEBUG_LEVEL_TRACE */ |
|
99 |
|
100 |
|
101 #ifdef FT_DEBUG_LEVEL_TRACE |
|
102 #include <stdio.h> |
|
103 #include <stdarg.h> |
|
104 #endif |
|
105 |
|
106 #include <stddef.h> |
|
107 #include <string.h> |
|
108 #include <setjmp.h> |
|
109 #include <limits.h> |
|
110 #define FT_UINT_MAX UINT_MAX |
|
111 #define FT_INT_MAX INT_MAX |
|
112 |
|
113 #define ft_memset memset |
|
114 |
|
115 #define ft_setjmp setjmp |
|
116 #define ft_longjmp longjmp |
|
117 #define ft_jmp_buf jmp_buf |
|
118 |
|
119 typedef ptrdiff_t FT_PtrDist; |
|
120 |
|
121 |
|
122 #define ErrRaster_Invalid_Mode -2 |
|
123 #define ErrRaster_Invalid_Outline -1 |
|
124 #define ErrRaster_Invalid_Argument -3 |
|
125 #define ErrRaster_Memory_Overflow -4 |
|
126 |
|
127 #define FT_BEGIN_HEADER |
|
128 #define FT_END_HEADER |
|
129 |
|
130 #include "ftimage.h" |
|
131 #include "ftgrays.h" |
|
132 |
|
133 |
|
134 /* This macro is used to indicate that a function parameter is unused. */ |
|
135 /* Its purpose is simply to reduce compiler warnings. Note also that */ |
|
136 /* simply defining it as `(void)x' doesn't avoid warnings with certain */ |
|
137 /* ANSI compilers (e.g. LCC). */ |
|
138 #define FT_UNUSED( x ) (x) = (x) |
|
139 |
|
140 |
|
141 /* we only use level 5 & 7 tracing messages; cf. ftdebug.h */ |
|
142 |
|
143 #ifdef FT_DEBUG_LEVEL_TRACE |
|
144 |
|
145 void |
|
146 FT_Message( const char* fmt, |
|
147 ... ) |
|
148 { |
|
149 va_list ap; |
|
150 |
|
151 |
|
152 va_start( ap, fmt ); |
|
153 vfprintf( stderr, fmt, ap ); |
|
154 va_end( ap ); |
|
155 } |
|
156 |
|
157 /* we don't handle tracing levels in stand-alone mode; */ |
|
158 #ifndef FT_TRACE5 |
|
159 #define FT_TRACE5( varformat ) FT_Message varformat |
|
160 #endif |
|
161 #ifndef FT_TRACE7 |
|
162 #define FT_TRACE7( varformat ) FT_Message varformat |
|
163 #endif |
|
164 #ifndef FT_ERROR |
|
165 #define FT_ERROR( varformat ) FT_Message varformat |
|
166 #endif |
|
167 |
|
168 #else /* !FT_DEBUG_LEVEL_TRACE */ |
|
169 |
|
170 #define FT_TRACE5( x ) do { } while ( 0 ) /* nothing */ |
|
171 #define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ |
|
172 #define FT_ERROR( x ) do { } while ( 0 ) /* nothing */ |
|
173 |
|
174 #endif /* !FT_DEBUG_LEVEL_TRACE */ |
|
175 |
|
176 |
|
177 #define FT_DEFINE_OUTLINE_FUNCS( class_, \ |
|
178 move_to_, line_to_, \ |
|
179 conic_to_, cubic_to_, \ |
|
180 shift_, delta_ ) \ |
|
181 static const FT_Outline_Funcs class_ = \ |
|
182 { \ |
|
183 move_to_, \ |
|
184 line_to_, \ |
|
185 conic_to_, \ |
|
186 cubic_to_, \ |
|
187 shift_, \ |
|
188 delta_ \ |
|
189 }; |
|
190 |
|
191 #define FT_DEFINE_RASTER_FUNCS( class_, glyph_format_, \ |
|
192 raster_new_, raster_reset_, \ |
|
193 raster_set_mode_, raster_render_, \ |
|
194 raster_done_ ) \ |
|
195 const FT_Raster_Funcs class_ = \ |
|
196 { \ |
|
197 glyph_format_, \ |
|
198 raster_new_, \ |
|
199 raster_reset_, \ |
|
200 raster_set_mode_, \ |
|
201 raster_render_, \ |
|
202 raster_done_ \ |
|
203 }; |
|
204 |
|
205 #else /* !_STANDALONE_ */ |
|
206 |
|
207 |
|
208 #include <ft2build.h> |
|
209 #include "ftgrays.h" |
|
210 #include FT_INTERNAL_OBJECTS_H |
|
211 #include FT_INTERNAL_DEBUG_H |
|
212 #include FT_OUTLINE_H |
|
213 |
|
214 #include "ftsmerrs.h" |
|
215 |
|
216 #include "ftspic.h" |
|
217 |
|
218 #define ErrRaster_Invalid_Mode Smooth_Err_Cannot_Render_Glyph |
|
219 #define ErrRaster_Invalid_Outline Smooth_Err_Invalid_Outline |
|
220 #define ErrRaster_Memory_Overflow Smooth_Err_Out_Of_Memory |
|
221 #define ErrRaster_Invalid_Argument Smooth_Err_Invalid_Argument |
|
222 |
|
223 #endif /* !_STANDALONE_ */ |
|
224 |
|
225 #ifndef FT_MEM_SET |
|
226 #define FT_MEM_SET( d, s, c ) ft_memset( d, s, c ) |
|
227 #endif |
|
228 |
|
229 #ifndef FT_MEM_ZERO |
|
230 #define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count ) |
|
231 #endif |
|
232 |
|
233 /* as usual, for the speed hungry :-) */ |
|
234 |
|
235 #ifndef FT_STATIC_RASTER |
|
236 |
|
237 #define RAS_ARG PWorker worker |
|
238 #define RAS_ARG_ PWorker worker, |
|
239 |
|
240 #define RAS_VAR worker |
|
241 #define RAS_VAR_ worker, |
|
242 |
|
243 #else /* FT_STATIC_RASTER */ |
|
244 |
|
245 #define RAS_ARG /* empty */ |
|
246 #define RAS_ARG_ /* empty */ |
|
247 #define RAS_VAR /* empty */ |
|
248 #define RAS_VAR_ /* empty */ |
|
249 |
|
250 #endif /* FT_STATIC_RASTER */ |
|
251 |
|
252 |
|
253 /* must be at least 6 bits! */ |
|
254 #define PIXEL_BITS 8 |
|
255 |
|
256 #define ONE_PIXEL ( 1L << PIXEL_BITS ) |
|
257 #define PIXEL_MASK ( -1L << PIXEL_BITS ) |
|
258 #define TRUNC( x ) ( (TCoord)( (x) >> PIXEL_BITS ) ) |
|
259 #define SUBPIXELS( x ) ( (TPos)(x) << PIXEL_BITS ) |
|
260 #define FLOOR( x ) ( (x) & -ONE_PIXEL ) |
|
261 #define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL ) |
|
262 #define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL ) |
|
263 |
|
264 #if PIXEL_BITS >= 6 |
|
265 #define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) ) |
|
266 #define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) ) |
|
267 #else |
|
268 #define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) ) |
|
269 #define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) ) |
|
270 #endif |
|
271 |
|
272 |
|
273 /*************************************************************************/ |
|
274 /* */ |
|
275 /* TYPE DEFINITIONS */ |
|
276 /* */ |
|
277 |
|
278 /* don't change the following types to FT_Int or FT_Pos, since we might */ |
|
279 /* need to define them to "float" or "double" when experimenting with */ |
|
280 /* new algorithms */ |
|
281 |
|
282 typedef long TCoord; /* integer scanline/pixel coordinate */ |
|
283 typedef long TPos; /* sub-pixel coordinate */ |
|
284 |
|
285 /* determine the type used to store cell areas. This normally takes at */ |
|
286 /* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */ |
|
287 /* `long' instead of `int', otherwise bad things happen */ |
|
288 |
|
289 #if PIXEL_BITS <= 7 |
|
290 |
|
291 typedef int TArea; |
|
292 |
|
293 #else /* PIXEL_BITS >= 8 */ |
|
294 |
|
295 /* approximately determine the size of integers using an ANSI-C header */ |
|
296 #if FT_UINT_MAX == 0xFFFFU |
|
297 typedef long TArea; |
|
298 #else |
|
299 typedef int TArea; |
|
300 #endif |
|
301 |
|
302 #endif /* PIXEL_BITS >= 8 */ |
|
303 |
|
304 |
|
305 /* maximal number of gray spans in a call to the span callback */ |
|
306 #define FT_MAX_GRAY_SPANS 32 |
|
307 |
|
308 |
|
309 typedef struct TCell_* PCell; |
|
310 |
|
311 typedef struct TCell_ |
|
312 { |
|
313 TPos x; /* same with TWorker.ex */ |
|
314 TCoord cover; /* same with TWorker.cover */ |
|
315 TArea area; |
|
316 PCell next; |
|
317 |
|
318 } TCell; |
|
319 |
|
320 |
|
321 typedef struct TWorker_ |
|
322 { |
|
323 TCoord ex, ey; |
|
324 TPos min_ex, max_ex; |
|
325 TPos min_ey, max_ey; |
|
326 TPos count_ex, count_ey; |
|
327 |
|
328 TArea area; |
|
329 TCoord cover; |
|
330 int invalid; |
|
331 |
|
332 PCell cells; |
|
333 FT_PtrDist max_cells; |
|
334 FT_PtrDist num_cells; |
|
335 |
|
336 TCoord cx, cy; |
|
337 TPos x, y; |
|
338 |
|
339 TPos last_ey; |
|
340 |
|
341 FT_Vector bez_stack[32 * 3 + 1]; |
|
342 int lev_stack[32]; |
|
343 |
|
344 FT_Outline outline; |
|
345 FT_Bitmap target; |
|
346 FT_BBox clip_box; |
|
347 |
|
348 FT_Span gray_spans[FT_MAX_GRAY_SPANS]; |
|
349 int num_gray_spans; |
|
350 |
|
351 FT_Raster_Span_Func render_span; |
|
352 void* render_span_data; |
|
353 int span_y; |
|
354 |
|
355 int band_size; |
|
356 int band_shoot; |
|
357 |
|
358 ft_jmp_buf jump_buffer; |
|
359 |
|
360 void* buffer; |
|
361 long buffer_size; |
|
362 |
|
363 PCell* ycells; |
|
364 TPos ycount; |
|
365 |
|
366 } TWorker, *PWorker; |
|
367 |
|
368 |
|
369 #ifndef FT_STATIC_RASTER |
|
370 #define ras (*worker) |
|
371 #else |
|
372 static TWorker ras; |
|
373 #endif |
|
374 |
|
375 |
|
376 typedef struct TRaster_ |
|
377 { |
|
378 void* buffer; |
|
379 long buffer_size; |
|
380 int band_size; |
|
381 void* memory; |
|
382 PWorker worker; |
|
383 |
|
384 } TRaster, *PRaster; |
|
385 |
|
386 |
|
387 |
|
388 /*************************************************************************/ |
|
389 /* */ |
|
390 /* Initialize the cells table. */ |
|
391 /* */ |
|
392 static void |
|
393 gray_init_cells( RAS_ARG_ void* buffer, |
|
394 long byte_size ) |
|
395 { |
|
396 ras.buffer = buffer; |
|
397 ras.buffer_size = byte_size; |
|
398 |
|
399 ras.ycells = (PCell*) buffer; |
|
400 ras.cells = NULL; |
|
401 ras.max_cells = 0; |
|
402 ras.num_cells = 0; |
|
403 ras.area = 0; |
|
404 ras.cover = 0; |
|
405 ras.invalid = 1; |
|
406 } |
|
407 |
|
408 |
|
409 /*************************************************************************/ |
|
410 /* */ |
|
411 /* Compute the outline bounding box. */ |
|
412 /* */ |
|
413 static void |
|
414 gray_compute_cbox( RAS_ARG ) |
|
415 { |
|
416 FT_Outline* outline = &ras.outline; |
|
417 FT_Vector* vec = outline->points; |
|
418 FT_Vector* limit = vec + outline->n_points; |
|
419 |
|
420 |
|
421 if ( outline->n_points <= 0 ) |
|
422 { |
|
423 ras.min_ex = ras.max_ex = 0; |
|
424 ras.min_ey = ras.max_ey = 0; |
|
425 return; |
|
426 } |
|
427 |
|
428 ras.min_ex = ras.max_ex = vec->x; |
|
429 ras.min_ey = ras.max_ey = vec->y; |
|
430 |
|
431 vec++; |
|
432 |
|
433 for ( ; vec < limit; vec++ ) |
|
434 { |
|
435 TPos x = vec->x; |
|
436 TPos y = vec->y; |
|
437 |
|
438 |
|
439 if ( x < ras.min_ex ) ras.min_ex = x; |
|
440 if ( x > ras.max_ex ) ras.max_ex = x; |
|
441 if ( y < ras.min_ey ) ras.min_ey = y; |
|
442 if ( y > ras.max_ey ) ras.max_ey = y; |
|
443 } |
|
444 |
|
445 /* truncate the bounding box to integer pixels */ |
|
446 ras.min_ex = ras.min_ex >> 6; |
|
447 ras.min_ey = ras.min_ey >> 6; |
|
448 ras.max_ex = ( ras.max_ex + 63 ) >> 6; |
|
449 ras.max_ey = ( ras.max_ey + 63 ) >> 6; |
|
450 } |
|
451 |
|
452 |
|
453 /*************************************************************************/ |
|
454 /* */ |
|
455 /* Record the current cell in the table. */ |
|
456 /* */ |
|
457 static PCell |
|
458 gray_find_cell( RAS_ARG ) |
|
459 { |
|
460 PCell *pcell, cell; |
|
461 TPos x = ras.ex; |
|
462 |
|
463 |
|
464 if ( x > ras.count_ex ) |
|
465 x = ras.count_ex; |
|
466 |
|
467 pcell = &ras.ycells[ras.ey]; |
|
468 for (;;) |
|
469 { |
|
470 cell = *pcell; |
|
471 if ( cell == NULL || cell->x > x ) |
|
472 break; |
|
473 |
|
474 if ( cell->x == x ) |
|
475 goto Exit; |
|
476 |
|
477 pcell = &cell->next; |
|
478 } |
|
479 |
|
480 if ( ras.num_cells >= ras.max_cells ) |
|
481 ft_longjmp( ras.jump_buffer, 1 ); |
|
482 |
|
483 cell = ras.cells + ras.num_cells++; |
|
484 cell->x = x; |
|
485 cell->area = 0; |
|
486 cell->cover = 0; |
|
487 |
|
488 cell->next = *pcell; |
|
489 *pcell = cell; |
|
490 |
|
491 Exit: |
|
492 return cell; |
|
493 } |
|
494 |
|
495 |
|
496 static void |
|
497 gray_record_cell( RAS_ARG ) |
|
498 { |
|
499 if ( !ras.invalid && ( ras.area | ras.cover ) ) |
|
500 { |
|
501 PCell cell = gray_find_cell( RAS_VAR ); |
|
502 |
|
503 |
|
504 cell->area += ras.area; |
|
505 cell->cover += ras.cover; |
|
506 } |
|
507 } |
|
508 |
|
509 |
|
510 /*************************************************************************/ |
|
511 /* */ |
|
512 /* Set the current cell to a new position. */ |
|
513 /* */ |
|
514 static void |
|
515 gray_set_cell( RAS_ARG_ TCoord ex, |
|
516 TCoord ey ) |
|
517 { |
|
518 /* Move the cell pointer to a new position. We set the `invalid' */ |
|
519 /* flag to indicate that the cell isn't part of those we're interested */ |
|
520 /* in during the render phase. This means that: */ |
|
521 /* */ |
|
522 /* . the new vertical position must be within min_ey..max_ey-1. */ |
|
523 /* . the new horizontal position must be strictly less than max_ex */ |
|
524 /* */ |
|
525 /* Note that if a cell is to the left of the clipping region, it is */ |
|
526 /* actually set to the (min_ex-1) horizontal position. */ |
|
527 |
|
528 /* All cells that are on the left of the clipping region go to the */ |
|
529 /* min_ex - 1 horizontal position. */ |
|
530 ey -= ras.min_ey; |
|
531 |
|
532 if ( ex > ras.max_ex ) |
|
533 ex = ras.max_ex; |
|
534 |
|
535 ex -= ras.min_ex; |
|
536 if ( ex < 0 ) |
|
537 ex = -1; |
|
538 |
|
539 /* are we moving to a different cell ? */ |
|
540 if ( ex != ras.ex || ey != ras.ey ) |
|
541 { |
|
542 /* record the current one if it is valid */ |
|
543 if ( !ras.invalid ) |
|
544 gray_record_cell( RAS_VAR ); |
|
545 |
|
546 ras.area = 0; |
|
547 ras.cover = 0; |
|
548 } |
|
549 |
|
550 ras.ex = ex; |
|
551 ras.ey = ey; |
|
552 ras.invalid = ( (unsigned)ey >= (unsigned)ras.count_ey || |
|
553 ex >= ras.count_ex ); |
|
554 } |
|
555 |
|
556 |
|
557 /*************************************************************************/ |
|
558 /* */ |
|
559 /* Start a new contour at a given cell. */ |
|
560 /* */ |
|
561 static void |
|
562 gray_start_cell( RAS_ARG_ TCoord ex, |
|
563 TCoord ey ) |
|
564 { |
|
565 if ( ex > ras.max_ex ) |
|
566 ex = (TCoord)( ras.max_ex ); |
|
567 |
|
568 if ( ex < ras.min_ex ) |
|
569 ex = (TCoord)( ras.min_ex - 1 ); |
|
570 |
|
571 ras.area = 0; |
|
572 ras.cover = 0; |
|
573 ras.ex = ex - ras.min_ex; |
|
574 ras.ey = ey - ras.min_ey; |
|
575 ras.last_ey = SUBPIXELS( ey ); |
|
576 ras.invalid = 0; |
|
577 |
|
578 gray_set_cell( RAS_VAR_ ex, ey ); |
|
579 } |
|
580 |
|
581 |
|
582 /*************************************************************************/ |
|
583 /* */ |
|
584 /* Render a scanline as one or more cells. */ |
|
585 /* */ |
|
586 static void |
|
587 gray_render_scanline( RAS_ARG_ TCoord ey, |
|
588 TPos x1, |
|
589 TCoord y1, |
|
590 TPos x2, |
|
591 TCoord y2 ) |
|
592 { |
|
593 TCoord ex1, ex2, fx1, fx2, delta, mod, lift, rem; |
|
594 long p, first, dx; |
|
595 int incr; |
|
596 |
|
597 |
|
598 dx = x2 - x1; |
|
599 |
|
600 ex1 = TRUNC( x1 ); |
|
601 ex2 = TRUNC( x2 ); |
|
602 fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) ); |
|
603 fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) ); |
|
604 |
|
605 /* trivial case. Happens often */ |
|
606 if ( y1 == y2 ) |
|
607 { |
|
608 gray_set_cell( RAS_VAR_ ex2, ey ); |
|
609 return; |
|
610 } |
|
611 |
|
612 /* everything is located in a single cell. That is easy! */ |
|
613 /* */ |
|
614 if ( ex1 == ex2 ) |
|
615 { |
|
616 delta = y2 - y1; |
|
617 ras.area += (TArea)(( fx1 + fx2 ) * delta); |
|
618 ras.cover += delta; |
|
619 return; |
|
620 } |
|
621 |
|
622 /* ok, we'll have to render a run of adjacent cells on the same */ |
|
623 /* scanline... */ |
|
624 /* */ |
|
625 p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 ); |
|
626 first = ONE_PIXEL; |
|
627 incr = 1; |
|
628 |
|
629 if ( dx < 0 ) |
|
630 { |
|
631 p = fx1 * ( y2 - y1 ); |
|
632 first = 0; |
|
633 incr = -1; |
|
634 dx = -dx; |
|
635 } |
|
636 |
|
637 delta = (TCoord)( p / dx ); |
|
638 mod = (TCoord)( p % dx ); |
|
639 if ( mod < 0 ) |
|
640 { |
|
641 delta--; |
|
642 mod += (TCoord)dx; |
|
643 } |
|
644 |
|
645 ras.area += (TArea)(( fx1 + first ) * delta); |
|
646 ras.cover += delta; |
|
647 |
|
648 ex1 += incr; |
|
649 gray_set_cell( RAS_VAR_ ex1, ey ); |
|
650 y1 += delta; |
|
651 |
|
652 if ( ex1 != ex2 ) |
|
653 { |
|
654 p = ONE_PIXEL * ( y2 - y1 + delta ); |
|
655 lift = (TCoord)( p / dx ); |
|
656 rem = (TCoord)( p % dx ); |
|
657 if ( rem < 0 ) |
|
658 { |
|
659 lift--; |
|
660 rem += (TCoord)dx; |
|
661 } |
|
662 |
|
663 mod -= (int)dx; |
|
664 |
|
665 while ( ex1 != ex2 ) |
|
666 { |
|
667 delta = lift; |
|
668 mod += rem; |
|
669 if ( mod >= 0 ) |
|
670 { |
|
671 mod -= (TCoord)dx; |
|
672 delta++; |
|
673 } |
|
674 |
|
675 ras.area += (TArea)(ONE_PIXEL * delta); |
|
676 ras.cover += delta; |
|
677 y1 += delta; |
|
678 ex1 += incr; |
|
679 gray_set_cell( RAS_VAR_ ex1, ey ); |
|
680 } |
|
681 } |
|
682 |
|
683 delta = y2 - y1; |
|
684 ras.area += (TArea)(( fx2 + ONE_PIXEL - first ) * delta); |
|
685 ras.cover += delta; |
|
686 } |
|
687 |
|
688 |
|
689 /*************************************************************************/ |
|
690 /* */ |
|
691 /* Render a given line as a series of scanlines. */ |
|
692 /* */ |
|
693 static void |
|
694 gray_render_line( RAS_ARG_ TPos to_x, |
|
695 TPos to_y ) |
|
696 { |
|
697 TCoord ey1, ey2, fy1, fy2, mod; |
|
698 TPos dx, dy, x, x2; |
|
699 long p, first; |
|
700 int delta, rem, lift, incr; |
|
701 |
|
702 |
|
703 ey1 = TRUNC( ras.last_ey ); |
|
704 ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */ |
|
705 fy1 = (TCoord)( ras.y - ras.last_ey ); |
|
706 fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) ); |
|
707 |
|
708 dx = to_x - ras.x; |
|
709 dy = to_y - ras.y; |
|
710 |
|
711 /* XXX: we should do something about the trivial case where dx == 0, */ |
|
712 /* as it happens very often! */ |
|
713 |
|
714 /* perform vertical clipping */ |
|
715 { |
|
716 TCoord min, max; |
|
717 |
|
718 |
|
719 min = ey1; |
|
720 max = ey2; |
|
721 if ( ey1 > ey2 ) |
|
722 { |
|
723 min = ey2; |
|
724 max = ey1; |
|
725 } |
|
726 if ( min >= ras.max_ey || max < ras.min_ey ) |
|
727 goto End; |
|
728 } |
|
729 |
|
730 /* everything is on a single scanline */ |
|
731 if ( ey1 == ey2 ) |
|
732 { |
|
733 gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 ); |
|
734 goto End; |
|
735 } |
|
736 |
|
737 /* vertical line - avoid calling gray_render_scanline */ |
|
738 incr = 1; |
|
739 |
|
740 if ( dx == 0 ) |
|
741 { |
|
742 TCoord ex = TRUNC( ras.x ); |
|
743 TCoord two_fx = (TCoord)( ( ras.x - SUBPIXELS( ex ) ) << 1 ); |
|
744 TArea area; |
|
745 |
|
746 |
|
747 first = ONE_PIXEL; |
|
748 if ( dy < 0 ) |
|
749 { |
|
750 first = 0; |
|
751 incr = -1; |
|
752 } |
|
753 |
|
754 delta = (int)( first - fy1 ); |
|
755 ras.area += (TArea)two_fx * delta; |
|
756 ras.cover += delta; |
|
757 ey1 += incr; |
|
758 |
|
759 gray_set_cell( RAS_VAR_ ex, ey1 ); |
|
760 |
|
761 delta = (int)( first + first - ONE_PIXEL ); |
|
762 area = (TArea)two_fx * delta; |
|
763 while ( ey1 != ey2 ) |
|
764 { |
|
765 ras.area += area; |
|
766 ras.cover += delta; |
|
767 ey1 += incr; |
|
768 |
|
769 gray_set_cell( RAS_VAR_ ex, ey1 ); |
|
770 } |
|
771 |
|
772 delta = (int)( fy2 - ONE_PIXEL + first ); |
|
773 ras.area += (TArea)two_fx * delta; |
|
774 ras.cover += delta; |
|
775 |
|
776 goto End; |
|
777 } |
|
778 |
|
779 /* ok, we have to render several scanlines */ |
|
780 p = ( ONE_PIXEL - fy1 ) * dx; |
|
781 first = ONE_PIXEL; |
|
782 incr = 1; |
|
783 |
|
784 if ( dy < 0 ) |
|
785 { |
|
786 p = fy1 * dx; |
|
787 first = 0; |
|
788 incr = -1; |
|
789 dy = -dy; |
|
790 } |
|
791 |
|
792 delta = (int)( p / dy ); |
|
793 mod = (int)( p % dy ); |
|
794 if ( mod < 0 ) |
|
795 { |
|
796 delta--; |
|
797 mod += (TCoord)dy; |
|
798 } |
|
799 |
|
800 x = ras.x + delta; |
|
801 gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, (TCoord)first ); |
|
802 |
|
803 ey1 += incr; |
|
804 gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); |
|
805 |
|
806 if ( ey1 != ey2 ) |
|
807 { |
|
808 p = ONE_PIXEL * dx; |
|
809 lift = (int)( p / dy ); |
|
810 rem = (int)( p % dy ); |
|
811 if ( rem < 0 ) |
|
812 { |
|
813 lift--; |
|
814 rem += (int)dy; |
|
815 } |
|
816 mod -= (int)dy; |
|
817 |
|
818 while ( ey1 != ey2 ) |
|
819 { |
|
820 delta = lift; |
|
821 mod += rem; |
|
822 if ( mod >= 0 ) |
|
823 { |
|
824 mod -= (int)dy; |
|
825 delta++; |
|
826 } |
|
827 |
|
828 x2 = x + delta; |
|
829 gray_render_scanline( RAS_VAR_ ey1, x, |
|
830 (TCoord)( ONE_PIXEL - first ), x2, |
|
831 (TCoord)first ); |
|
832 x = x2; |
|
833 |
|
834 ey1 += incr; |
|
835 gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); |
|
836 } |
|
837 } |
|
838 |
|
839 gray_render_scanline( RAS_VAR_ ey1, x, |
|
840 (TCoord)( ONE_PIXEL - first ), to_x, |
|
841 fy2 ); |
|
842 |
|
843 End: |
|
844 ras.x = to_x; |
|
845 ras.y = to_y; |
|
846 ras.last_ey = SUBPIXELS( ey2 ); |
|
847 } |
|
848 |
|
849 |
|
850 static void |
|
851 gray_split_conic( FT_Vector* base ) |
|
852 { |
|
853 TPos a, b; |
|
854 |
|
855 |
|
856 base[4].x = base[2].x; |
|
857 b = base[1].x; |
|
858 a = base[3].x = ( base[2].x + b ) / 2; |
|
859 b = base[1].x = ( base[0].x + b ) / 2; |
|
860 base[2].x = ( a + b ) / 2; |
|
861 |
|
862 base[4].y = base[2].y; |
|
863 b = base[1].y; |
|
864 a = base[3].y = ( base[2].y + b ) / 2; |
|
865 b = base[1].y = ( base[0].y + b ) / 2; |
|
866 base[2].y = ( a + b ) / 2; |
|
867 } |
|
868 |
|
869 |
|
870 static void |
|
871 gray_render_conic( RAS_ARG_ const FT_Vector* control, |
|
872 const FT_Vector* to ) |
|
873 { |
|
874 TPos dx, dy; |
|
875 int top, level; |
|
876 int* levels; |
|
877 FT_Vector* arc; |
|
878 |
|
879 |
|
880 arc = ras.bez_stack; |
|
881 arc[0].x = UPSCALE( to->x ); |
|
882 arc[0].y = UPSCALE( to->y ); |
|
883 arc[1].x = UPSCALE( control->x ); |
|
884 arc[1].y = UPSCALE( control->y ); |
|
885 arc[2].x = ras.x; |
|
886 arc[2].y = ras.y; |
|
887 |
|
888 dx = FT_ABS( arc[2].x + arc[0].x - 2 * arc[1].x ); |
|
889 dy = FT_ABS( arc[2].y + arc[0].y - 2 * arc[1].y ); |
|
890 if ( dx < dy ) |
|
891 dx = dy; |
|
892 |
|
893 level = 0; |
|
894 while ( dx > ONE_PIXEL / 6 ) |
|
895 { |
|
896 dx >>= 2; |
|
897 level++; |
|
898 } |
|
899 |
|
900 levels = ras.lev_stack; |
|
901 levels[0] = level; |
|
902 top = 0; |
|
903 |
|
904 do |
|
905 { |
|
906 level = levels[top]; |
|
907 if ( level > 1 ) |
|
908 { |
|
909 /* check that the arc crosses the current band */ |
|
910 TPos min, max, y; |
|
911 |
|
912 |
|
913 min = max = arc[0].y; |
|
914 |
|
915 y = arc[1].y; |
|
916 if ( y < min ) min = y; |
|
917 if ( y > max ) max = y; |
|
918 |
|
919 y = arc[2].y; |
|
920 if ( y < min ) min = y; |
|
921 if ( y > max ) max = y; |
|
922 |
|
923 if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey ) |
|
924 goto Draw; |
|
925 |
|
926 gray_split_conic( arc ); |
|
927 arc += 2; |
|
928 top++; |
|
929 levels[top] = levels[top - 1] = level - 1; |
|
930 continue; |
|
931 } |
|
932 |
|
933 Draw: |
|
934 gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); |
|
935 top--; |
|
936 arc -= 2; |
|
937 |
|
938 } while ( top >= 0 ); |
|
939 } |
|
940 |
|
941 |
|
942 static void |
|
943 gray_split_cubic( FT_Vector* base ) |
|
944 { |
|
945 TPos a, b, c, d; |
|
946 |
|
947 |
|
948 base[6].x = base[3].x; |
|
949 c = base[1].x; |
|
950 d = base[2].x; |
|
951 base[1].x = a = ( base[0].x + c ) / 2; |
|
952 base[5].x = b = ( base[3].x + d ) / 2; |
|
953 c = ( c + d ) / 2; |
|
954 base[2].x = a = ( a + c ) / 2; |
|
955 base[4].x = b = ( b + c ) / 2; |
|
956 base[3].x = ( a + b ) / 2; |
|
957 |
|
958 base[6].y = base[3].y; |
|
959 c = base[1].y; |
|
960 d = base[2].y; |
|
961 base[1].y = a = ( base[0].y + c ) / 2; |
|
962 base[5].y = b = ( base[3].y + d ) / 2; |
|
963 c = ( c + d ) / 2; |
|
964 base[2].y = a = ( a + c ) / 2; |
|
965 base[4].y = b = ( b + c ) / 2; |
|
966 base[3].y = ( a + b ) / 2; |
|
967 } |
|
968 |
|
969 |
|
970 static void |
|
971 gray_render_cubic( RAS_ARG_ const FT_Vector* control1, |
|
972 const FT_Vector* control2, |
|
973 const FT_Vector* to ) |
|
974 { |
|
975 FT_Vector* arc; |
|
976 |
|
977 |
|
978 arc = ras.bez_stack; |
|
979 arc[0].x = UPSCALE( to->x ); |
|
980 arc[0].y = UPSCALE( to->y ); |
|
981 arc[1].x = UPSCALE( control2->x ); |
|
982 arc[1].y = UPSCALE( control2->y ); |
|
983 arc[2].x = UPSCALE( control1->x ); |
|
984 arc[2].y = UPSCALE( control1->y ); |
|
985 arc[3].x = ras.x; |
|
986 arc[3].y = ras.y; |
|
987 |
|
988 for (;;) |
|
989 { |
|
990 /* Check that the arc crosses the current band. */ |
|
991 TPos min, max, y; |
|
992 |
|
993 |
|
994 min = max = arc[0].y; |
|
995 |
|
996 y = arc[1].y; |
|
997 if ( y < min ) |
|
998 min = y; |
|
999 if ( y > max ) |
|
1000 max = y; |
|
1001 |
|
1002 y = arc[2].y; |
|
1003 if ( y < min ) |
|
1004 min = y; |
|
1005 if ( y > max ) |
|
1006 max = y; |
|
1007 |
|
1008 y = arc[3].y; |
|
1009 if ( y < min ) |
|
1010 min = y; |
|
1011 if ( y > max ) |
|
1012 max = y; |
|
1013 |
|
1014 if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey ) |
|
1015 goto Draw; |
|
1016 |
|
1017 /* Decide whether to split or draw. See `Rapid Termination */ |
|
1018 /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */ |
|
1019 /* F. Hain, at */ |
|
1020 /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */ |
|
1021 |
|
1022 { |
|
1023 TPos dx, dy, dx_, dy_; |
|
1024 TPos dx1, dy1, dx2, dy2; |
|
1025 TPos L, s, s_limit; |
|
1026 |
|
1027 |
|
1028 /* dx and dy are x and y components of the P0-P3 chord vector. */ |
|
1029 dx = arc[3].x - arc[0].x; |
|
1030 dy = arc[3].y - arc[0].y; |
|
1031 |
|
1032 /* L is an (under)estimate of the Euclidean distance P0-P3. */ |
|
1033 /* */ |
|
1034 /* If dx >= dy, then r = sqrt(dx^2 + dy^2) can be overestimated */ |
|
1035 /* with least maximum error by */ |
|
1036 /* */ |
|
1037 /* r_upperbound = dx + (sqrt(2) - 1) * dy , */ |
|
1038 /* */ |
|
1039 /* where sqrt(2) - 1 can be (over)estimated by 107/256, giving an */ |
|
1040 /* error of no more than 8.4%. */ |
|
1041 /* */ |
|
1042 /* Similarly, some elementary calculus shows that r can be */ |
|
1043 /* underestimated with least maximum error by */ |
|
1044 /* */ |
|
1045 /* r_lowerbound = sqrt(2 + sqrt(2)) / 2 * dx */ |
|
1046 /* + sqrt(2 - sqrt(2)) / 2 * dy . */ |
|
1047 /* */ |
|
1048 /* 236/256 and 97/256 are (under)estimates of the two algebraic */ |
|
1049 /* numbers, giving an error of no more than 8.1%. */ |
|
1050 |
|
1051 dx_ = FT_ABS( dx ); |
|
1052 dy_ = FT_ABS( dy ); |
|
1053 |
|
1054 /* This is the same as */ |
|
1055 /* */ |
|
1056 /* L = ( 236 * FT_MAX( dx_, dy_ ) */ |
|
1057 /* + 97 * FT_MIN( dx_, dy_ ) ) >> 8; */ |
|
1058 L = ( dx_ > dy_ ? 236 * dx_ + 97 * dy_ |
|
1059 : 97 * dx_ + 236 * dy_ ) >> 8; |
|
1060 |
|
1061 /* Avoid possible arithmetic overflow below by splitting. */ |
|
1062 if ( L > 32767 ) |
|
1063 goto Split; |
|
1064 |
|
1065 /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */ |
|
1066 s_limit = L * (TPos)( ONE_PIXEL / 6 ); |
|
1067 |
|
1068 /* s is L * the perpendicular distance from P1 to the line P0-P3. */ |
|
1069 dx1 = arc[1].x - arc[0].x; |
|
1070 dy1 = arc[1].y - arc[0].y; |
|
1071 s = FT_ABS( dy * dx1 - dx * dy1 ); |
|
1072 |
|
1073 if ( s > s_limit ) |
|
1074 goto Split; |
|
1075 |
|
1076 /* s is L * the perpendicular distance from P2 to the line P0-P3. */ |
|
1077 dx2 = arc[2].x - arc[0].x; |
|
1078 dy2 = arc[2].y - arc[0].y; |
|
1079 s = FT_ABS( dy * dx2 - dx * dy2 ); |
|
1080 |
|
1081 if ( s > s_limit ) |
|
1082 goto Split; |
|
1083 |
|
1084 /* If P1 or P2 is outside P0-P3, split the curve. */ |
|
1085 if ( dy * dy1 + dx * dx1 < 0 || |
|
1086 dy * dy2 + dx * dx2 < 0 || |
|
1087 dy * (arc[3].y - arc[1].y) + dx * (arc[3].x - arc[1].x) < 0 || |
|
1088 dy * (arc[3].y - arc[2].y) + dx * (arc[3].x - arc[2].x) < 0 ) |
|
1089 goto Split; |
|
1090 |
|
1091 /* No reason to split. */ |
|
1092 goto Draw; |
|
1093 } |
|
1094 |
|
1095 Split: |
|
1096 gray_split_cubic( arc ); |
|
1097 arc += 3; |
|
1098 continue; |
|
1099 |
|
1100 Draw: |
|
1101 gray_render_line( RAS_VAR_ arc[0].x, arc[0].y ); |
|
1102 |
|
1103 if ( arc == ras.bez_stack ) |
|
1104 return; |
|
1105 |
|
1106 arc -= 3; |
|
1107 } |
|
1108 } |
|
1109 |
|
1110 |
|
1111 static int |
|
1112 gray_move_to( const FT_Vector* to, |
|
1113 PWorker worker ) |
|
1114 { |
|
1115 TPos x, y; |
|
1116 |
|
1117 |
|
1118 /* record current cell, if any */ |
|
1119 gray_record_cell( RAS_VAR ); |
|
1120 |
|
1121 /* start to a new position */ |
|
1122 x = UPSCALE( to->x ); |
|
1123 y = UPSCALE( to->y ); |
|
1124 |
|
1125 gray_start_cell( RAS_VAR_ TRUNC( x ), TRUNC( y ) ); |
|
1126 |
|
1127 worker->x = x; |
|
1128 worker->y = y; |
|
1129 return 0; |
|
1130 } |
|
1131 |
|
1132 |
|
1133 static int |
|
1134 gray_line_to( const FT_Vector* to, |
|
1135 PWorker worker ) |
|
1136 { |
|
1137 gray_render_line( RAS_VAR_ UPSCALE( to->x ), UPSCALE( to->y ) ); |
|
1138 return 0; |
|
1139 } |
|
1140 |
|
1141 |
|
1142 static int |
|
1143 gray_conic_to( const FT_Vector* control, |
|
1144 const FT_Vector* to, |
|
1145 PWorker worker ) |
|
1146 { |
|
1147 gray_render_conic( RAS_VAR_ control, to ); |
|
1148 return 0; |
|
1149 } |
|
1150 |
|
1151 |
|
1152 static int |
|
1153 gray_cubic_to( const FT_Vector* control1, |
|
1154 const FT_Vector* control2, |
|
1155 const FT_Vector* to, |
|
1156 PWorker worker ) |
|
1157 { |
|
1158 gray_render_cubic( RAS_VAR_ control1, control2, to ); |
|
1159 return 0; |
|
1160 } |
|
1161 |
|
1162 |
|
1163 static void |
|
1164 gray_render_span( int y, |
|
1165 int count, |
|
1166 const FT_Span* spans, |
|
1167 PWorker worker ) |
|
1168 { |
|
1169 unsigned char* p; |
|
1170 FT_Bitmap* map = &worker->target; |
|
1171 |
|
1172 |
|
1173 /* first of all, compute the scanline offset */ |
|
1174 p = (unsigned char*)map->buffer - y * map->pitch; |
|
1175 if ( map->pitch >= 0 ) |
|
1176 p += (unsigned)( ( map->rows - 1 ) * map->pitch ); |
|
1177 |
|
1178 for ( ; count > 0; count--, spans++ ) |
|
1179 { |
|
1180 unsigned char coverage = spans->coverage; |
|
1181 |
|
1182 |
|
1183 if ( coverage ) |
|
1184 { |
|
1185 /* For small-spans it is faster to do it by ourselves than |
|
1186 * calling `memset'. This is mainly due to the cost of the |
|
1187 * function call. |
|
1188 */ |
|
1189 if ( spans->len >= 8 ) |
|
1190 FT_MEM_SET( p + spans->x, (unsigned char)coverage, spans->len ); |
|
1191 else |
|
1192 { |
|
1193 unsigned char* q = p + spans->x; |
|
1194 |
|
1195 |
|
1196 switch ( spans->len ) |
|
1197 { |
|
1198 case 7: *q++ = (unsigned char)coverage; |
|
1199 case 6: *q++ = (unsigned char)coverage; |
|
1200 case 5: *q++ = (unsigned char)coverage; |
|
1201 case 4: *q++ = (unsigned char)coverage; |
|
1202 case 3: *q++ = (unsigned char)coverage; |
|
1203 case 2: *q++ = (unsigned char)coverage; |
|
1204 case 1: *q = (unsigned char)coverage; |
|
1205 default: |
|
1206 ; |
|
1207 } |
|
1208 } |
|
1209 } |
|
1210 } |
|
1211 } |
|
1212 |
|
1213 |
|
1214 static void |
|
1215 gray_hline( RAS_ARG_ TCoord x, |
|
1216 TCoord y, |
|
1217 TPos area, |
|
1218 TCoord acount ) |
|
1219 { |
|
1220 FT_Span* span; |
|
1221 int count; |
|
1222 int coverage; |
|
1223 |
|
1224 |
|
1225 /* compute the coverage line's coverage, depending on the */ |
|
1226 /* outline fill rule */ |
|
1227 /* */ |
|
1228 /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */ |
|
1229 /* */ |
|
1230 coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) ); |
|
1231 /* use range 0..256 */ |
|
1232 if ( coverage < 0 ) |
|
1233 coverage = -coverage; |
|
1234 |
|
1235 if ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL ) |
|
1236 { |
|
1237 coverage &= 511; |
|
1238 |
|
1239 if ( coverage > 256 ) |
|
1240 coverage = 512 - coverage; |
|
1241 else if ( coverage == 256 ) |
|
1242 coverage = 255; |
|
1243 } |
|
1244 else |
|
1245 { |
|
1246 /* normal non-zero winding rule */ |
|
1247 if ( coverage >= 256 ) |
|
1248 coverage = 255; |
|
1249 } |
|
1250 |
|
1251 y += (TCoord)ras.min_ey; |
|
1252 x += (TCoord)ras.min_ex; |
|
1253 |
|
1254 /* FT_Span.x is a 16-bit short, so limit our coordinates appropriately */ |
|
1255 if ( x >= 32767 ) |
|
1256 x = 32767; |
|
1257 |
|
1258 /* FT_Span.y is an integer, so limit our coordinates appropriately */ |
|
1259 if ( y >= FT_INT_MAX ) |
|
1260 y = FT_INT_MAX; |
|
1261 |
|
1262 if ( coverage ) |
|
1263 { |
|
1264 /* see whether we can add this span to the current list */ |
|
1265 count = ras.num_gray_spans; |
|
1266 span = ras.gray_spans + count - 1; |
|
1267 if ( count > 0 && |
|
1268 ras.span_y == y && |
|
1269 (int)span->x + span->len == (int)x && |
|
1270 span->coverage == coverage ) |
|
1271 { |
|
1272 span->len = (unsigned short)( span->len + acount ); |
|
1273 return; |
|
1274 } |
|
1275 |
|
1276 if ( ras.span_y != y || count >= FT_MAX_GRAY_SPANS ) |
|
1277 { |
|
1278 if ( ras.render_span && count > 0 ) |
|
1279 ras.render_span( ras.span_y, count, ras.gray_spans, |
|
1280 ras.render_span_data ); |
|
1281 |
|
1282 #ifdef FT_DEBUG_LEVEL_TRACE |
|
1283 |
|
1284 if ( count > 0 ) |
|
1285 { |
|
1286 int n; |
|
1287 |
|
1288 |
|
1289 FT_TRACE7(( "y = %3d ", ras.span_y )); |
|
1290 span = ras.gray_spans; |
|
1291 for ( n = 0; n < count; n++, span++ ) |
|
1292 FT_TRACE7(( "[%d..%d]:%02x ", |
|
1293 span->x, span->x + span->len - 1, span->coverage )); |
|
1294 FT_TRACE7(( "\n" )); |
|
1295 } |
|
1296 |
|
1297 #endif /* FT_DEBUG_LEVEL_TRACE */ |
|
1298 |
|
1299 ras.num_gray_spans = 0; |
|
1300 ras.span_y = (int)y; |
|
1301 |
|
1302 count = 0; |
|
1303 span = ras.gray_spans; |
|
1304 } |
|
1305 else |
|
1306 span++; |
|
1307 |
|
1308 /* add a gray span to the current list */ |
|
1309 span->x = (short)x; |
|
1310 span->len = (unsigned short)acount; |
|
1311 span->coverage = (unsigned char)coverage; |
|
1312 |
|
1313 ras.num_gray_spans++; |
|
1314 } |
|
1315 } |
|
1316 |
|
1317 |
|
1318 #ifdef FT_DEBUG_LEVEL_TRACE |
|
1319 |
|
1320 /* to be called while in the debugger -- */ |
|
1321 /* this function causes a compiler warning since it is unused otherwise */ |
|
1322 static void |
|
1323 gray_dump_cells( RAS_ARG ) |
|
1324 { |
|
1325 int yindex; |
|
1326 |
|
1327 |
|
1328 for ( yindex = 0; yindex < ras.ycount; yindex++ ) |
|
1329 { |
|
1330 PCell cell; |
|
1331 |
|
1332 |
|
1333 printf( "%3d:", yindex ); |
|
1334 |
|
1335 for ( cell = ras.ycells[yindex]; cell != NULL; cell = cell->next ) |
|
1336 printf( " (%3ld, c:%4ld, a:%6d)", cell->x, cell->cover, cell->area ); |
|
1337 printf( "\n" ); |
|
1338 } |
|
1339 } |
|
1340 |
|
1341 #endif /* FT_DEBUG_LEVEL_TRACE */ |
|
1342 |
|
1343 |
|
1344 static void |
|
1345 gray_sweep( RAS_ARG_ const FT_Bitmap* target ) |
|
1346 { |
|
1347 int yindex; |
|
1348 |
|
1349 FT_UNUSED( target ); |
|
1350 |
|
1351 |
|
1352 if ( ras.num_cells == 0 ) |
|
1353 return; |
|
1354 |
|
1355 ras.num_gray_spans = 0; |
|
1356 |
|
1357 FT_TRACE7(( "gray_sweep: start\n" )); |
|
1358 |
|
1359 for ( yindex = 0; yindex < ras.ycount; yindex++ ) |
|
1360 { |
|
1361 PCell cell = ras.ycells[yindex]; |
|
1362 TCoord cover = 0; |
|
1363 TCoord x = 0; |
|
1364 |
|
1365 |
|
1366 for ( ; cell != NULL; cell = cell->next ) |
|
1367 { |
|
1368 TPos area; |
|
1369 |
|
1370 |
|
1371 if ( cell->x > x && cover != 0 ) |
|
1372 gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ), |
|
1373 cell->x - x ); |
|
1374 |
|
1375 cover += cell->cover; |
|
1376 area = cover * ( ONE_PIXEL * 2 ) - cell->area; |
|
1377 |
|
1378 if ( area != 0 && cell->x >= 0 ) |
|
1379 gray_hline( RAS_VAR_ cell->x, yindex, area, 1 ); |
|
1380 |
|
1381 x = cell->x + 1; |
|
1382 } |
|
1383 |
|
1384 if ( cover != 0 ) |
|
1385 gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ), |
|
1386 ras.count_ex - x ); |
|
1387 } |
|
1388 |
|
1389 if ( ras.render_span && ras.num_gray_spans > 0 ) |
|
1390 ras.render_span( ras.span_y, ras.num_gray_spans, |
|
1391 ras.gray_spans, ras.render_span_data ); |
|
1392 |
|
1393 FT_TRACE7(( "gray_sweep: end\n" )); |
|
1394 } |
|
1395 |
|
1396 |
|
1397 #ifdef _STANDALONE_ |
|
1398 |
|
1399 /*************************************************************************/ |
|
1400 /* */ |
|
1401 /* The following function should only compile in stand-alone mode, */ |
|
1402 /* i.e., when building this component without the rest of FreeType. */ |
|
1403 /* */ |
|
1404 /*************************************************************************/ |
|
1405 |
|
1406 /*************************************************************************/ |
|
1407 /* */ |
|
1408 /* <Function> */ |
|
1409 /* FT_Outline_Decompose */ |
|
1410 /* */ |
|
1411 /* <Description> */ |
|
1412 /* Walk over an outline's structure to decompose it into individual */ |
|
1413 /* segments and Bézier arcs. This function is also able to emit */ |
|
1414 /* `move to' and `close to' operations to indicate the start and end */ |
|
1415 /* of new contours in the outline. */ |
|
1416 /* */ |
|
1417 /* <Input> */ |
|
1418 /* outline :: A pointer to the source target. */ |
|
1419 /* */ |
|
1420 /* func_interface :: A table of `emitters', i.e., function pointers */ |
|
1421 /* called during decomposition to indicate path */ |
|
1422 /* operations. */ |
|
1423 /* */ |
|
1424 /* <InOut> */ |
|
1425 /* user :: A typeless pointer which is passed to each */ |
|
1426 /* emitter during the decomposition. It can be */ |
|
1427 /* used to store the state during the */ |
|
1428 /* decomposition. */ |
|
1429 /* */ |
|
1430 /* <Return> */ |
|
1431 /* Error code. 0 means success. */ |
|
1432 /* */ |
|
1433 static int |
|
1434 FT_Outline_Decompose( const FT_Outline* outline, |
|
1435 const FT_Outline_Funcs* func_interface, |
|
1436 void* user ) |
|
1437 { |
|
1438 #undef SCALED |
|
1439 #define SCALED( x ) ( ( (x) << shift ) - delta ) |
|
1440 |
|
1441 FT_Vector v_last; |
|
1442 FT_Vector v_control; |
|
1443 FT_Vector v_start; |
|
1444 |
|
1445 FT_Vector* point; |
|
1446 FT_Vector* limit; |
|
1447 char* tags; |
|
1448 |
|
1449 int error; |
|
1450 |
|
1451 int n; /* index of contour in outline */ |
|
1452 int first; /* index of first point in contour */ |
|
1453 char tag; /* current point's state */ |
|
1454 |
|
1455 int shift; |
|
1456 TPos delta; |
|
1457 |
|
1458 |
|
1459 if ( !outline || !func_interface ) |
|
1460 return ErrRaster_Invalid_Argument; |
|
1461 |
|
1462 shift = func_interface->shift; |
|
1463 delta = func_interface->delta; |
|
1464 first = 0; |
|
1465 |
|
1466 for ( n = 0; n < outline->n_contours; n++ ) |
|
1467 { |
|
1468 int last; /* index of last point in contour */ |
|
1469 |
|
1470 |
|
1471 FT_TRACE5(( "FT_Outline_Decompose: Outline %d\n", n )); |
|
1472 |
|
1473 last = outline->contours[n]; |
|
1474 if ( last < 0 ) |
|
1475 goto Invalid_Outline; |
|
1476 limit = outline->points + last; |
|
1477 |
|
1478 v_start = outline->points[first]; |
|
1479 v_start.x = SCALED( v_start.x ); |
|
1480 v_start.y = SCALED( v_start.y ); |
|
1481 |
|
1482 v_last = outline->points[last]; |
|
1483 v_last.x = SCALED( v_last.x ); |
|
1484 v_last.y = SCALED( v_last.y ); |
|
1485 |
|
1486 v_control = v_start; |
|
1487 |
|
1488 point = outline->points + first; |
|
1489 tags = outline->tags + first; |
|
1490 tag = FT_CURVE_TAG( tags[0] ); |
|
1491 |
|
1492 /* A contour cannot start with a cubic control point! */ |
|
1493 if ( tag == FT_CURVE_TAG_CUBIC ) |
|
1494 goto Invalid_Outline; |
|
1495 |
|
1496 /* check first point to determine origin */ |
|
1497 if ( tag == FT_CURVE_TAG_CONIC ) |
|
1498 { |
|
1499 /* first point is conic control. Yes, this happens. */ |
|
1500 if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON ) |
|
1501 { |
|
1502 /* start at last point if it is on the curve */ |
|
1503 v_start = v_last; |
|
1504 limit--; |
|
1505 } |
|
1506 else |
|
1507 { |
|
1508 /* if both first and last points are conic, */ |
|
1509 /* start at their middle and record its position */ |
|
1510 /* for closure */ |
|
1511 v_start.x = ( v_start.x + v_last.x ) / 2; |
|
1512 v_start.y = ( v_start.y + v_last.y ) / 2; |
|
1513 |
|
1514 v_last = v_start; |
|
1515 } |
|
1516 point--; |
|
1517 tags--; |
|
1518 } |
|
1519 |
|
1520 FT_TRACE5(( " move to (%.2f, %.2f)\n", |
|
1521 v_start.x / 64.0, v_start.y / 64.0 )); |
|
1522 error = func_interface->move_to( &v_start, user ); |
|
1523 if ( error ) |
|
1524 goto Exit; |
|
1525 |
|
1526 while ( point < limit ) |
|
1527 { |
|
1528 point++; |
|
1529 tags++; |
|
1530 |
|
1531 tag = FT_CURVE_TAG( tags[0] ); |
|
1532 switch ( tag ) |
|
1533 { |
|
1534 case FT_CURVE_TAG_ON: /* emit a single line_to */ |
|
1535 { |
|
1536 FT_Vector vec; |
|
1537 |
|
1538 |
|
1539 vec.x = SCALED( point->x ); |
|
1540 vec.y = SCALED( point->y ); |
|
1541 |
|
1542 FT_TRACE5(( " line to (%.2f, %.2f)\n", |
|
1543 vec.x / 64.0, vec.y / 64.0 )); |
|
1544 error = func_interface->line_to( &vec, user ); |
|
1545 if ( error ) |
|
1546 goto Exit; |
|
1547 continue; |
|
1548 } |
|
1549 |
|
1550 case FT_CURVE_TAG_CONIC: /* consume conic arcs */ |
|
1551 v_control.x = SCALED( point->x ); |
|
1552 v_control.y = SCALED( point->y ); |
|
1553 |
|
1554 Do_Conic: |
|
1555 if ( point < limit ) |
|
1556 { |
|
1557 FT_Vector vec; |
|
1558 FT_Vector v_middle; |
|
1559 |
|
1560 |
|
1561 point++; |
|
1562 tags++; |
|
1563 tag = FT_CURVE_TAG( tags[0] ); |
|
1564 |
|
1565 vec.x = SCALED( point->x ); |
|
1566 vec.y = SCALED( point->y ); |
|
1567 |
|
1568 if ( tag == FT_CURVE_TAG_ON ) |
|
1569 { |
|
1570 FT_TRACE5(( " conic to (%.2f, %.2f)" |
|
1571 " with control (%.2f, %.2f)\n", |
|
1572 vec.x / 64.0, vec.y / 64.0, |
|
1573 v_control.x / 64.0, v_control.y / 64.0 )); |
|
1574 error = func_interface->conic_to( &v_control, &vec, user ); |
|
1575 if ( error ) |
|
1576 goto Exit; |
|
1577 continue; |
|
1578 } |
|
1579 |
|
1580 if ( tag != FT_CURVE_TAG_CONIC ) |
|
1581 goto Invalid_Outline; |
|
1582 |
|
1583 v_middle.x = ( v_control.x + vec.x ) / 2; |
|
1584 v_middle.y = ( v_control.y + vec.y ) / 2; |
|
1585 |
|
1586 FT_TRACE5(( " conic to (%.2f, %.2f)" |
|
1587 " with control (%.2f, %.2f)\n", |
|
1588 v_middle.x / 64.0, v_middle.y / 64.0, |
|
1589 v_control.x / 64.0, v_control.y / 64.0 )); |
|
1590 error = func_interface->conic_to( &v_control, &v_middle, user ); |
|
1591 if ( error ) |
|
1592 goto Exit; |
|
1593 |
|
1594 v_control = vec; |
|
1595 goto Do_Conic; |
|
1596 } |
|
1597 |
|
1598 FT_TRACE5(( " conic to (%.2f, %.2f)" |
|
1599 " with control (%.2f, %.2f)\n", |
|
1600 v_start.x / 64.0, v_start.y / 64.0, |
|
1601 v_control.x / 64.0, v_control.y / 64.0 )); |
|
1602 error = func_interface->conic_to( &v_control, &v_start, user ); |
|
1603 goto Close; |
|
1604 |
|
1605 default: /* FT_CURVE_TAG_CUBIC */ |
|
1606 { |
|
1607 FT_Vector vec1, vec2; |
|
1608 |
|
1609 |
|
1610 if ( point + 1 > limit || |
|
1611 FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC ) |
|
1612 goto Invalid_Outline; |
|
1613 |
|
1614 point += 2; |
|
1615 tags += 2; |
|
1616 |
|
1617 vec1.x = SCALED( point[-2].x ); |
|
1618 vec1.y = SCALED( point[-2].y ); |
|
1619 |
|
1620 vec2.x = SCALED( point[-1].x ); |
|
1621 vec2.y = SCALED( point[-1].y ); |
|
1622 |
|
1623 if ( point <= limit ) |
|
1624 { |
|
1625 FT_Vector vec; |
|
1626 |
|
1627 |
|
1628 vec.x = SCALED( point->x ); |
|
1629 vec.y = SCALED( point->y ); |
|
1630 |
|
1631 FT_TRACE5(( " cubic to (%.2f, %.2f)" |
|
1632 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", |
|
1633 vec.x / 64.0, vec.y / 64.0, |
|
1634 vec1.x / 64.0, vec1.y / 64.0, |
|
1635 vec2.x / 64.0, vec2.y / 64.0 )); |
|
1636 error = func_interface->cubic_to( &vec1, &vec2, &vec, user ); |
|
1637 if ( error ) |
|
1638 goto Exit; |
|
1639 continue; |
|
1640 } |
|
1641 |
|
1642 FT_TRACE5(( " cubic to (%.2f, %.2f)" |
|
1643 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n", |
|
1644 v_start.x / 64.0, v_start.y / 64.0, |
|
1645 vec1.x / 64.0, vec1.y / 64.0, |
|
1646 vec2.x / 64.0, vec2.y / 64.0 )); |
|
1647 error = func_interface->cubic_to( &vec1, &vec2, &v_start, user ); |
|
1648 goto Close; |
|
1649 } |
|
1650 } |
|
1651 } |
|
1652 |
|
1653 /* close the contour with a line segment */ |
|
1654 FT_TRACE5(( " line to (%.2f, %.2f)\n", |
|
1655 v_start.x / 64.0, v_start.y / 64.0 )); |
|
1656 error = func_interface->line_to( &v_start, user ); |
|
1657 |
|
1658 Close: |
|
1659 if ( error ) |
|
1660 goto Exit; |
|
1661 |
|
1662 first = last + 1; |
|
1663 } |
|
1664 |
|
1665 FT_TRACE5(( "FT_Outline_Decompose: Done\n", n )); |
|
1666 return 0; |
|
1667 |
|
1668 Exit: |
|
1669 FT_TRACE5(( "FT_Outline_Decompose: Error %d\n", error )); |
|
1670 return error; |
|
1671 |
|
1672 Invalid_Outline: |
|
1673 return ErrRaster_Invalid_Outline; |
|
1674 } |
|
1675 |
|
1676 #endif /* _STANDALONE_ */ |
|
1677 |
|
1678 |
|
1679 typedef struct TBand_ |
|
1680 { |
|
1681 TPos min, max; |
|
1682 |
|
1683 } TBand; |
|
1684 |
|
1685 FT_DEFINE_OUTLINE_FUNCS(func_interface, |
|
1686 (FT_Outline_MoveTo_Func) gray_move_to, |
|
1687 (FT_Outline_LineTo_Func) gray_line_to, |
|
1688 (FT_Outline_ConicTo_Func)gray_conic_to, |
|
1689 (FT_Outline_CubicTo_Func)gray_cubic_to, |
|
1690 0, |
|
1691 0 |
|
1692 ) |
|
1693 |
|
1694 static int |
|
1695 gray_convert_glyph_inner( RAS_ARG ) |
|
1696 { |
|
1697 |
|
1698 volatile int error = 0; |
|
1699 |
|
1700 #ifdef FT_CONFIG_OPTION_PIC |
|
1701 FT_Outline_Funcs func_interface; |
|
1702 Init_Class_func_interface(&func_interface); |
|
1703 #endif |
|
1704 |
|
1705 if ( ft_setjmp( ras.jump_buffer ) == 0 ) |
|
1706 { |
|
1707 error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras ); |
|
1708 gray_record_cell( RAS_VAR ); |
|
1709 } |
|
1710 else |
|
1711 error = ErrRaster_Memory_Overflow; |
|
1712 |
|
1713 return error; |
|
1714 } |
|
1715 |
|
1716 |
|
1717 static int |
|
1718 gray_convert_glyph( RAS_ARG ) |
|
1719 { |
|
1720 TBand bands[40]; |
|
1721 TBand* volatile band; |
|
1722 int volatile n, num_bands; |
|
1723 TPos volatile min, max, max_y; |
|
1724 FT_BBox* clip; |
|
1725 |
|
1726 |
|
1727 /* Set up state in the raster object */ |
|
1728 gray_compute_cbox( RAS_VAR ); |
|
1729 |
|
1730 /* clip to target bitmap, exit if nothing to do */ |
|
1731 clip = &ras.clip_box; |
|
1732 |
|
1733 if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax || |
|
1734 ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax ) |
|
1735 return 0; |
|
1736 |
|
1737 if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin; |
|
1738 if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin; |
|
1739 |
|
1740 if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax; |
|
1741 if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax; |
|
1742 |
|
1743 ras.count_ex = ras.max_ex - ras.min_ex; |
|
1744 ras.count_ey = ras.max_ey - ras.min_ey; |
|
1745 |
|
1746 /* set up vertical bands */ |
|
1747 num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size ); |
|
1748 if ( num_bands == 0 ) |
|
1749 num_bands = 1; |
|
1750 if ( num_bands >= 39 ) |
|
1751 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 TPos bottom, top, middle; |
|
1771 int error; |
|
1772 |
|
1773 { |
|
1774 PCell cells_max; |
|
1775 int yindex; |
|
1776 long cell_start, cell_end, cell_mod; |
|
1777 |
|
1778 |
|
1779 ras.ycells = (PCell*)ras.buffer; |
|
1780 ras.ycount = band->max - band->min; |
|
1781 |
|
1782 cell_start = sizeof ( PCell ) * ras.ycount; |
|
1783 cell_mod = cell_start % sizeof ( TCell ); |
|
1784 if ( cell_mod > 0 ) |
|
1785 cell_start += sizeof ( TCell ) - cell_mod; |
|
1786 |
|
1787 cell_end = ras.buffer_size; |
|
1788 cell_end -= cell_end % sizeof( TCell ); |
|
1789 |
|
1790 cells_max = (PCell)( (char*)ras.buffer + cell_end ); |
|
1791 ras.cells = (PCell)( (char*)ras.buffer + cell_start ); |
|
1792 if ( ras.cells >= cells_max ) |
|
1793 goto ReduceBands; |
|
1794 |
|
1795 ras.max_cells = cells_max - ras.cells; |
|
1796 if ( ras.max_cells < 2 ) |
|
1797 goto ReduceBands; |
|
1798 |
|
1799 for ( yindex = 0; yindex < ras.ycount; yindex++ ) |
|
1800 ras.ycells[yindex] = NULL; |
|
1801 } |
|
1802 |
|
1803 ras.num_cells = 0; |
|
1804 ras.invalid = 1; |
|
1805 ras.min_ey = band->min; |
|
1806 ras.max_ey = band->max; |
|
1807 ras.count_ey = band->max - band->min; |
|
1808 |
|
1809 error = gray_convert_glyph_inner( RAS_VAR ); |
|
1810 |
|
1811 if ( !error ) |
|
1812 { |
|
1813 gray_sweep( RAS_VAR_ &ras.target ); |
|
1814 band--; |
|
1815 continue; |
|
1816 } |
|
1817 else if ( error != ErrRaster_Memory_Overflow ) |
|
1818 return 1; |
|
1819 |
|
1820 ReduceBands: |
|
1821 /* render pool overflow; we will reduce the render band by half */ |
|
1822 bottom = band->min; |
|
1823 top = band->max; |
|
1824 middle = bottom + ( ( top - bottom ) >> 1 ); |
|
1825 |
|
1826 /* This is too complex for a single scanline; there must */ |
|
1827 /* be some problems. */ |
|
1828 if ( middle == bottom ) |
|
1829 { |
|
1830 #ifdef FT_DEBUG_LEVEL_TRACE |
|
1831 FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" )); |
|
1832 #endif |
|
1833 return 1; |
|
1834 } |
|
1835 |
|
1836 if ( bottom-top >= ras.band_size ) |
|
1837 ras.band_shoot++; |
|
1838 |
|
1839 band[1].min = bottom; |
|
1840 band[1].max = middle; |
|
1841 band[0].min = middle; |
|
1842 band[0].max = top; |
|
1843 band++; |
|
1844 } |
|
1845 } |
|
1846 |
|
1847 if ( ras.band_shoot > 8 && ras.band_size > 16 ) |
|
1848 ras.band_size = ras.band_size / 2; |
|
1849 |
|
1850 return 0; |
|
1851 } |
|
1852 |
|
1853 |
|
1854 static int |
|
1855 gray_raster_render( PRaster raster, |
|
1856 const FT_Raster_Params* params ) |
|
1857 { |
|
1858 const FT_Outline* outline = (const FT_Outline*)params->source; |
|
1859 const FT_Bitmap* target_map = params->target; |
|
1860 PWorker worker; |
|
1861 |
|
1862 |
|
1863 if ( !raster || !raster->buffer || !raster->buffer_size ) |
|
1864 return ErrRaster_Invalid_Argument; |
|
1865 |
|
1866 if ( !outline ) |
|
1867 return ErrRaster_Invalid_Outline; |
|
1868 |
|
1869 /* return immediately if the outline is empty */ |
|
1870 if ( outline->n_points == 0 || outline->n_contours <= 0 ) |
|
1871 return 0; |
|
1872 |
|
1873 if ( !outline->contours || !outline->points ) |
|
1874 return ErrRaster_Invalid_Outline; |
|
1875 |
|
1876 if ( outline->n_points != |
|
1877 outline->contours[outline->n_contours - 1] + 1 ) |
|
1878 return ErrRaster_Invalid_Outline; |
|
1879 |
|
1880 worker = raster->worker; |
|
1881 |
|
1882 /* if direct mode is not set, we must have a target bitmap */ |
|
1883 if ( !( params->flags & FT_RASTER_FLAG_DIRECT ) ) |
|
1884 { |
|
1885 if ( !target_map ) |
|
1886 return ErrRaster_Invalid_Argument; |
|
1887 |
|
1888 /* nothing to do */ |
|
1889 if ( !target_map->width || !target_map->rows ) |
|
1890 return 0; |
|
1891 |
|
1892 if ( !target_map->buffer ) |
|
1893 return ErrRaster_Invalid_Argument; |
|
1894 } |
|
1895 |
|
1896 /* this version does not support monochrome rendering */ |
|
1897 if ( !( params->flags & FT_RASTER_FLAG_AA ) ) |
|
1898 return ErrRaster_Invalid_Mode; |
|
1899 |
|
1900 /* compute clipping box */ |
|
1901 if ( !( params->flags & FT_RASTER_FLAG_DIRECT ) ) |
|
1902 { |
|
1903 /* compute clip box from target pixmap */ |
|
1904 ras.clip_box.xMin = 0; |
|
1905 ras.clip_box.yMin = 0; |
|
1906 ras.clip_box.xMax = target_map->width; |
|
1907 ras.clip_box.yMax = target_map->rows; |
|
1908 } |
|
1909 else if ( params->flags & FT_RASTER_FLAG_CLIP ) |
|
1910 ras.clip_box = params->clip_box; |
|
1911 else |
|
1912 { |
|
1913 ras.clip_box.xMin = -32768L; |
|
1914 ras.clip_box.yMin = -32768L; |
|
1915 ras.clip_box.xMax = 32767L; |
|
1916 ras.clip_box.yMax = 32767L; |
|
1917 } |
|
1918 |
|
1919 gray_init_cells( RAS_VAR_ raster->buffer, raster->buffer_size ); |
|
1920 |
|
1921 ras.outline = *outline; |
|
1922 ras.num_cells = 0; |
|
1923 ras.invalid = 1; |
|
1924 ras.band_size = raster->band_size; |
|
1925 ras.num_gray_spans = 0; |
|
1926 |
|
1927 if ( params->flags & FT_RASTER_FLAG_DIRECT ) |
|
1928 { |
|
1929 ras.render_span = (FT_Raster_Span_Func)params->gray_spans; |
|
1930 ras.render_span_data = params->user; |
|
1931 } |
|
1932 else |
|
1933 { |
|
1934 ras.target = *target_map; |
|
1935 ras.render_span = (FT_Raster_Span_Func)gray_render_span; |
|
1936 ras.render_span_data = &ras; |
|
1937 } |
|
1938 |
|
1939 return gray_convert_glyph( RAS_VAR ); |
|
1940 } |
|
1941 |
|
1942 |
|
1943 /**** RASTER OBJECT CREATION: In stand-alone mode, we simply use *****/ |
|
1944 /**** a static object. *****/ |
|
1945 |
|
1946 #ifdef _STANDALONE_ |
|
1947 |
|
1948 static int |
|
1949 gray_raster_new( void* memory, |
|
1950 FT_Raster* araster ) |
|
1951 { |
|
1952 static TRaster the_raster; |
|
1953 |
|
1954 FT_UNUSED( memory ); |
|
1955 |
|
1956 |
|
1957 *araster = (FT_Raster)&the_raster; |
|
1958 FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) ); |
|
1959 |
|
1960 return 0; |
|
1961 } |
|
1962 |
|
1963 |
|
1964 static void |
|
1965 gray_raster_done( FT_Raster raster ) |
|
1966 { |
|
1967 /* nothing */ |
|
1968 FT_UNUSED( raster ); |
|
1969 } |
|
1970 |
|
1971 #else /* !_STANDALONE_ */ |
|
1972 |
|
1973 static int |
|
1974 gray_raster_new( FT_Memory memory, |
|
1975 FT_Raster* araster ) |
|
1976 { |
|
1977 FT_Error error; |
|
1978 PRaster raster = NULL; |
|
1979 |
|
1980 |
|
1981 *araster = 0; |
|
1982 if ( !FT_ALLOC( raster, sizeof ( TRaster ) ) ) |
|
1983 { |
|
1984 raster->memory = memory; |
|
1985 *araster = (FT_Raster)raster; |
|
1986 } |
|
1987 |
|
1988 return error; |
|
1989 } |
|
1990 |
|
1991 |
|
1992 static void |
|
1993 gray_raster_done( FT_Raster raster ) |
|
1994 { |
|
1995 FT_Memory memory = (FT_Memory)((PRaster)raster)->memory; |
|
1996 |
|
1997 |
|
1998 FT_FREE( raster ); |
|
1999 } |
|
2000 |
|
2001 #endif /* !_STANDALONE_ */ |
|
2002 |
|
2003 |
|
2004 static void |
|
2005 gray_raster_reset( FT_Raster raster, |
|
2006 char* pool_base, |
|
2007 long pool_size ) |
|
2008 { |
|
2009 PRaster rast = (PRaster)raster; |
|
2010 |
|
2011 |
|
2012 if ( raster ) |
|
2013 { |
|
2014 if ( pool_base && pool_size >= (long)sizeof ( TWorker ) + 2048 ) |
|
2015 { |
|
2016 PWorker worker = (PWorker)pool_base; |
|
2017 |
|
2018 |
|
2019 rast->worker = worker; |
|
2020 rast->buffer = pool_base + |
|
2021 ( ( sizeof ( TWorker ) + sizeof ( TCell ) - 1 ) & |
|
2022 ~( sizeof ( TCell ) - 1 ) ); |
|
2023 rast->buffer_size = (long)( ( pool_base + pool_size ) - |
|
2024 (char*)rast->buffer ) & |
|
2025 ~( sizeof ( TCell ) - 1 ); |
|
2026 rast->band_size = (int)( rast->buffer_size / |
|
2027 ( sizeof ( TCell ) * 8 ) ); |
|
2028 } |
|
2029 else |
|
2030 { |
|
2031 rast->buffer = NULL; |
|
2032 rast->buffer_size = 0; |
|
2033 rast->worker = NULL; |
|
2034 } |
|
2035 } |
|
2036 } |
|
2037 |
|
2038 |
|
2039 FT_DEFINE_RASTER_FUNCS(ft_grays_raster, |
|
2040 FT_GLYPH_FORMAT_OUTLINE, |
|
2041 |
|
2042 (FT_Raster_New_Func) gray_raster_new, |
|
2043 (FT_Raster_Reset_Func) gray_raster_reset, |
|
2044 (FT_Raster_Set_Mode_Func)0, |
|
2045 (FT_Raster_Render_Func) gray_raster_render, |
|
2046 (FT_Raster_Done_Func) gray_raster_done |
|
2047 ) |
|
2048 |
|
2049 |
|
2050 /* END */ |
|
2051 |
|
2052 |
|
2053 /* Local Variables: */ |
|
2054 /* coding: utf-8 */ |
|
2055 /* End: */ |
|