Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qgrayraster.c
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2000-2016 by David Turner, Robert Wilhelm, and Werner Lemberg.
3// SPDX-License-Identifier: FTL OR GPL-2.0-only
4
5/***************************************************************************/
6/* */
7/* qgrayraster.c, derived from ftgrays.c */
8/* */
9/* A new `perfect' anti-aliasing renderer (body). */
10/* */
11/* Copyright 2000-2016 by */
12/* David Turner, Robert Wilhelm, and Werner Lemberg. */
13/* */
14/* This file is part of the FreeType project, and may only be used, */
15/* modified, and distributed under the terms of the FreeType project */
16/* license, ../../3rdparty/freetype/docs/FTL.TXT. By continuing to use, */
17/* modify, or distribute this file you indicate that you have read */
18/* the license and understand and accept it fully. */
19/* */
20/***************************************************************************/
21
22 /*************************************************************************/
23 /* */
24 /* This file can be compiled without the rest of the FreeType engine, by */
25 /* defining the _STANDALONE_ macro when compiling it. You also need to */
26 /* put the files `ftgrays.h' and `ftimage.h' into the current */
27 /* compilation directory. Typically, you could do something like */
28 /* */
29 /* - copy `src/smooth/ftgrays.c' (this file) to your current directory */
30 /* */
31 /* - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the */
32 /* same directory */
33 /* */
34 /* - compile `ftgrays' with the _STANDALONE_ macro defined, as in */
35 /* */
36 /* cc -c -D_STANDALONE_ ftgrays.c */
37 /* */
38 /* The renderer can be initialized with a call to */
39 /* `qt_ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated */
40 /* with a call to `qt_ft_gray_raster.raster_render'. */
41 /* */
42 /* See the comments and documentation in the file `ftimage.h' for more */
43 /* details on how the raster works. */
44 /* */
45 /*************************************************************************/
46
47 /*************************************************************************/
48 /* */
49 /* This is a new anti-aliasing scan-converter for FreeType 2. The */
50 /* algorithm used here is _very_ different from the one in the standard */
51 /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */
52 /* coverage of the outline on each pixel cell. */
53 /* */
54 /* It is based on ideas that I initially found in Raph Levien's */
55 /* excellent LibArt graphics library (see http://www.levien.com/libart */
56 /* for more information, though the web pages do not tell anything */
57 /* about the renderer; you'll have to dive into the source code to */
58 /* understand how it works). */
59 /* */
60 /* Note, however, that this is a _very_ different implementation */
61 /* compared to Raph's. Coverage information is stored in a very */
62 /* different way, and I don't use sorted vector paths. Also, it doesn't */
63 /* use floating point values. */
64 /* */
65 /* This renderer has the following advantages: */
66 /* */
67 /* - It doesn't need an intermediate bitmap. Instead, one can supply a */
68 /* callback function that will be called by the renderer to draw gray */
69 /* spans on any target surface. You can thus do direct composition on */
70 /* any kind of bitmap, provided that you give the renderer the right */
71 /* callback. */
72 /* */
73 /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */
74 /* each pixel cell. */
75 /* */
76 /* - It performs a single pass on the outline (the `standard' FT2 */
77 /* renderer makes two passes). */
78 /* */
79 /* - It can easily be modified to render to _any_ number of gray levels */
80 /* cheaply. */
81 /* */
82 /* - For small (< 20) pixel sizes, it is faster than the standard */
83 /* renderer. */
84 /* */
85 /*************************************************************************/
86
87 /*************************************************************************/
88 /* */
89 /* The macro QT_FT_COMPONENT is used in trace mode. It is an implicit */
90 /* parameter of the QT_FT_TRACE() and QT_FT_ERROR() macros, used to print/log */
91 /* messages during execution. */
92 /* */
93#undef QT_FT_COMPONENT
94#define QT_FT_COMPONENT trace_smooth
95
96
97/* Auxiliary macros for token concatenation. */
98#define QT_FT_ERR_XCAT( x, y ) x ## y
99#define QT_FT_ERR_CAT( x, y ) QT_FT_ERR_XCAT( x, y )
100
101#define QT_FT_BEGIN_STMNT do {
102#define QT_FT_END_STMNT } while ( 0 )
103
104#define QT_FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) )
105#define QT_FT_ABS( a ) ( (a) < 0 ? -(a) : (a) )
106
107
108/*
109 * Approximate sqrt(x*x+y*y) using the `alpha max plus beta min'
110 * algorithm. We use alpha = 1, beta = 3/8, giving us results with a
111 * largest error less than 7% compared to the exact value.
112 */
113#define QT_FT_HYPOT( x, y )
114 ( x = QT_FT_ABS( x ),
115 y = QT_FT_ABS( y ),
116 x > y ? x + ( 3 * y >> 3 )
117 : y + ( 3 * x >> 3 ) )
118
119#define ErrRaster_MemoryOverflow -4
120
121#if defined(VXWORKS)
122# include <vxWorksCommon.h> /* needed for setjmp.h */
123#endif
124#include <string.h> /* for qt_ft_memcpy() */
125#include <setjmp.h>
126#include <limits.h>
127
128#define QT_FT_UINT_MAX UINT_MAX
129
130#define qt_ft_memset memset
131
132#define qt_ft_setjmp setjmp
133#define qt_ft_longjmp longjmp
134#define qt_ft_jmp_buf jmp_buf
135
136#include <stddef.h>
138
139#define ErrRaster_Invalid_Mode -2
140#define ErrRaster_Invalid_Outline -1
141#define ErrRaster_Invalid_Argument -3
142#define ErrRaster_Memory_Overflow -4
143#define ErrRaster_OutOfMemory -6
144
145#define QT_FT_BEGIN_HEADER
146#define QT_FT_END_HEADER
147
148#include <private/qrasterdefs_p.h>
149#include <private/qgrayraster_p.h>
150
151#include <qcompilerdetection.h>
152#include <qtconfigmacros.h>
153
154#include <stdlib.h>
155#include <stdio.h>
156#include <assert.h>
157
158#define QT_FT_UNUSED( x ) (void) x
159
160#define QT_FT_TRACE5( x ) do { } while ( 0 ) /* nothing */
161#define QT_FT_TRACE7( x ) do { } while ( 0 ) /* nothing */
162#define QT_FT_ERROR( x ) do { } while ( 0 ) /* nothing */
163#define QT_FT_THROW( e ) QT_FT_ERR_CAT( ErrRaster_, e )
164
165#ifndef QT_FT_MEM_SET
166#define QT_FT_MEM_SET( d, s, c ) qt_ft_memset( d, s, c )
167#endif
168
169#ifndef QT_FT_MEM_ZERO
170#define QT_FT_MEM_ZERO( dest, count ) QT_FT_MEM_SET( dest, 0, count )
171#endif
172
173
174#define RAS_ARG PWorker worker
175#define RAS_ARG_ PWorker worker,
176
177#define RAS_VAR worker
178#define RAS_VAR_ worker,
179
180#define ras (*worker)
181
182 /* must be at least 6 bits! */
183#define PIXEL_BITS 8
184
185#define ONE_PIXEL ( 1L << PIXEL_BITS )
186#define TRUNC( x ) (TCoord)( (x) >> PIXEL_BITS )
187#define FRACT( x ) (TCoord)( (x) & ( ONE_PIXEL - 1 ) )
188
189#if PIXEL_BITS >= 6
190#define UPSCALE( x ) ( (x) * ( ONE_PIXEL >> 6 ) )
191#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) )
192#else
193#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) )
194#define DOWNSCALE( x ) ( (x) * ( 64 >> PIXEL_BITS ) )
195#endif
196
197/* Compute `dividend / divisor' and return both its quotient and */
198/* remainder, cast to a specific type. This macro also ensures that */
199/* the remainder is always positive. */
200#define QT_FT_DIV_MOD( type, dividend, divisor, quotient, remainder ) QT_FT_BEGIN_STMNT
201
202 (quotient) = (type)( (dividend) / (divisor) );
203 (remainder) = (type)( (dividend) % (divisor) );
204 if ( (remainder) < 0 )
205 {
206 (quotient)--;
207 (remainder) += (type)(divisor);
208 } QT_FT_END_STMNT
209
210
211 /* These macros speed up repetitive divisions by replacing them */
212 /* with multiplications and right shifts. */
213#define QT_FT_UDIVPREP( b )
214 long b ## _r = (long)( ULONG_MAX >> PIXEL_BITS ) / ( b )
215#define QT_FT_UDIV( a, b )
216 ( ( (unsigned long)( a ) * (unsigned long)( b ## _r ) ) >>
217 ( sizeof( long ) * CHAR_BIT - PIXEL_BITS ) )
218
219
220 /*************************************************************************/
221 /* */
222 /* TYPE DEFINITIONS */
223 /* */
224
225 /* don't change the following types to QT_FT_Int or QT_FT_Pos, since we might */
226 /* need to define them to "float" or "double" when experimenting with */
227 /* new algorithms */
228
229 typedef long TCoord; /* integer scanline/pixel coordinate */
230 typedef long TPos; /* sub-pixel coordinate */
231 typedef long TArea ; /* cell areas, coordinate products */
232
233 /* maximal number of gray spans in a call to the span callback */
234#define QT_FT_MAX_GRAY_SPANS 256
235
236
237 typedef struct TCell_* PCell;
238
239 typedef struct TCell_
240 {
241 int x;
242 int cover;
245
246 } TCell;
247
248
289
290
291 typedef struct TRaster_
292 {
293 void* buffer;
297 void* memory;
298 PWorker worker;
299
300 } TRaster, *PRaster;
301
302 int QT_MANGLE_NAMESPACE(q_gray_rendered_spans)(TRaster *raster)
303 {
304 if ( raster && raster->worker )
305 return raster->worker->skip_spans > 0 ? 0 : -raster->worker->skip_spans;
306 return 0;
307 }
308
309 /*************************************************************************/
310 /* */
311 /* Initialize the cells table. */
312 /* */
313 static void
315 long byte_size )
316 {
317 ras.buffer = buffer;
318 ras.buffer_size = byte_size;
319
320 ras.ycells = (PCell*) buffer;
321 ras.cells = NULL;
322 ras.max_cells = 0;
323 ras.num_cells = 0;
324 ras.area = 0;
325 ras.cover = 0;
326 ras.invalid = 1;
327 }
328
329
330 /*************************************************************************/
331 /* */
332 /* Compute the outline bounding box. */
333 /* */
334 static void
336 {
337 QT_FT_Outline* outline = &ras.outline;
338 QT_FT_Vector* vec = outline->points;
339 QT_FT_Vector* limit = vec + outline->n_points;
340
341
342 if ( outline->n_points <= 0 )
343 {
346 return;
347 }
348
349 ras.min_ex = ras.max_ex = vec->x;
350 ras.min_ey = ras.max_ey = vec->y;
351
352 vec++;
353
354 for ( ; vec < limit; vec++ )
355 {
356 TPos x = vec->x;
357 TPos y = vec->y;
358
359
360 if ( x < ras.min_ex ) ras.min_ex = x;
361 if ( x > ras.max_ex ) ras.max_ex = x;
362 if ( y < ras.min_ey ) ras.min_ey = y;
363 if ( y > ras.max_ey ) ras.max_ey = y;
364 }
365
366 /* truncate the bounding box to integer pixels */
367 ras.min_ex = ras.min_ex >> 6;
368 ras.min_ey = ras.min_ey >> 6;
369 ras.max_ex = ( ras.max_ex + 63 ) >> 6;
370 ras.max_ey = ( ras.max_ey + 63 ) >> 6;
371 }
372
373
374 /*************************************************************************/
375 /* */
376 /* Record the current cell in the table. */
377 /* */
378 static PCell
380 {
381 PCell *pcell, cell;
382 TPos x = ras.ex;
383
384
385 if ( x > ras.count_ex )
386 x = ras.count_ex;
387
388 pcell = &ras.ycells[ras.ey];
389 for (;;)
390 {
391 cell = *pcell;
392 if ( cell == NULL || cell->x > x )
393 break;
394
395 if ( cell->x == x )
396 goto Exit;
397
398 pcell = &cell->next;
399 }
400
403
404 cell = ras.cells + ras.num_cells++;
405 cell->x = x;
406 cell->area = 0;
407 cell->cover = 0;
408
409 cell->next = *pcell;
410 *pcell = cell;
411
412 Exit:
413 return cell;
414 }
415
416
417 static void
419 {
420 if ( ras.area | ras.cover )
421 {
423
424
425 cell->area += ras.area;
426 cell->cover += ras.cover;
427 }
428 }
429
430
431 /*************************************************************************/
432 /* */
433 /* Set the current cell to a new position. */
434 /* */
435 static void
437 TCoord ey )
438 {
439 /* Move the cell pointer to a new position. We set the `invalid' */
440 /* flag to indicate that the cell isn't part of those we're interested */
441 /* in during the render phase. This means that: */
442 /* */
443 /* . the new vertical position must be within min_ey..max_ey-1. */
444 /* . the new horizontal position must be strictly less than max_ex */
445 /* */
446 /* Note that if a cell is to the left of the clipping region, it is */
447 /* actually set to the (min_ex-1) horizontal position. */
448
449 /* All cells that are on the left of the clipping region go to the */
450 /* min_ex - 1 horizontal position. */
451 ey -= ras.min_ey;
452
453 if ( ex > ras.max_ex )
454 ex = ras.max_ex;
455
456 ex -= ras.min_ex;
457 if ( ex < 0 )
458 ex = -1;
459
460 /* are we moving to a different cell ? */
461 if ( ex != ras.ex || ey != ras.ey )
462 {
463 /* record the current one if it is valid */
464 if ( !ras.invalid )
466
467 ras.area = 0;
468 ras.cover = 0;
469 ras.ex = ex;
470 ras.ey = ey;
471 }
472
473 ras.invalid = ( (unsigned int)ey >= (unsigned int)ras.count_ey ||
474 ex >= ras.count_ex );
475 }
476
477
478 /*************************************************************************/
479 /* */
480 /* Start a new contour at a given cell. */
481 /* */
482 static void
484 TCoord ey )
485 {
486 if ( ex > ras.max_ex )
487 ex = (TCoord)( ras.max_ex );
488
489 if ( ex < ras.min_ex )
490 ex = (TCoord)( ras.min_ex - 1 );
491
492 ras.area = 0;
493 ras.cover = 0;
494 ras.ex = ex - ras.min_ex;
495 ras.ey = ey - ras.min_ey;
496 ras.invalid = 0;
497
499 }
500
501// The new render-line implementation is not yet used
502#if 1
503
504 /*************************************************************************/
505 /* */
506 /* Render a scanline as one or more cells. */
507 /* */
508 static void
510 TPos x1,
511 TCoord y1,
512 TPos x2,
513 TCoord y2 )
514 {
515 TCoord ex1, ex2, fx1, fx2, first, dy, delta, mod;
516 TPos p, dx;
517 int incr;
518
519
520 ex1 = TRUNC( x1 );
521 ex2 = TRUNC( x2 );
522
523 /* trivial case. Happens often */
524 if ( y1 == y2 )
525 {
527 return;
528 }
529
530 fx1 = FRACT( x1 );
531 fx2 = FRACT( x2 );
532
533 /* everything is located in a single cell. That is easy! */
534 /* */
535 if ( ex1 == ex2 )
536 goto End;
537
538 /* ok, we'll have to render a run of adjacent cells on the same */
539 /* scanline... */
540 /* */
541 dx = x2 - x1;
542 dy = y2 - y1;
543
544 if ( dx > 0 )
545 {
546 p = ( ONE_PIXEL - fx1 ) * dy;
547 first = ONE_PIXEL;
548 incr = 1;
549 } else {
550 p = fx1 * dy;
551 first = 0;
552 incr = -1;
553 dx = -dx;
554 }
555
556 QT_FT_DIV_MOD( TCoord, p, dx, delta, mod );
557
558 ras.area += (TArea)( fx1 + first ) * delta;
559 ras.cover += delta;
560 y1 += delta;
561 ex1 += incr;
563
564 if ( ex1 != ex2 )
565 {
566 TCoord lift, rem;
567
568
569 p = ONE_PIXEL * dy;
570 QT_FT_DIV_MOD( TCoord, p, dx, lift, rem );
571
572 do
573 {
574 delta = lift;
575 mod += rem;
576 if ( mod >= (TCoord)dx )
577 {
578 mod -= (TCoord)dx;
579 delta++;
580 }
581
582 ras.area += (TArea)( ONE_PIXEL * delta );
583 ras.cover += delta;
584 y1 += delta;
585 ex1 += incr;
587 } while ( ex1 != ex2 );
588 }
589 fx1 = ONE_PIXEL - first;
590
591 End:
592 dy = y2 - y1;
593
594 ras.area += (TArea)( ( fx1 + fx2 ) * dy );
595 ras.cover += dy;
596 }
597
598
599 /*************************************************************************/
600 /* */
601 /* Render a given line as a series of scanlines. */
602 /* */
603 static void
605 TPos to_y )
606 {
607 TCoord ey1, ey2, fy1, fy2, first, delta, mod;
608 TPos p, dx, dy, x, x2;
609 int incr;
610
611 ey1 = TRUNC( ras.y );
612 ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */
613
614 /* perform vertical clipping */
615 if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) ||
616 ( ey1 < ras.min_ey && ey2 < ras.min_ey ) )
617 goto End;
618
619 fy1 = FRACT( ras.y );
620 fy2 = FRACT( to_y );
621
622 /* everything is on a single scanline */
623 if ( ey1 == ey2 )
624 {
625 gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 );
626 goto End;
627 }
628
629 dx = to_x - ras.x;
630 dy = to_y - ras.y;
631
632 /* vertical line - avoid calling gray_render_scanline */
633 if ( dx == 0 )
634 {
635 TCoord ex = TRUNC( ras.x );
636 TCoord two_fx = FRACT( ras.x ) << 1;
637 TPos area, max_ey1;
638
639
640 if ( dy > 0)
641 {
642 first = ONE_PIXEL;
643 }
644 else
645 {
646 first = 0;
647 }
648
649 delta = first - fy1;
650 ras.area += (TArea)two_fx * delta;
651 ras.cover += delta;
652
653 delta = first + first - ONE_PIXEL;
654 area = (TArea)two_fx * delta;
655 max_ey1 = ras.count_ey + ras.min_ey;
656 if (dy < 0) {
657 if (ey1 > max_ey1) {
658 ey1 = (max_ey1 > ey2) ? max_ey1 : ey2;
659 gray_set_cell( &ras, ex, ey1 );
660 } else {
661 ey1--;
662 gray_set_cell( &ras, ex, ey1 );
663 }
664 while ( ey1 > ey2 && ey1 >= ras.min_ey)
665 {
666 ras.area += area;
667 ras.cover += delta;
668 ey1--;
669
670 gray_set_cell( &ras, ex, ey1 );
671 }
672 if (ey1 != ey2) {
673 ey1 = ey2;
674 gray_set_cell( &ras, ex, ey1 );
675 }
676 } else {
677 if (ey1 < ras.min_ey) {
678 ey1 = (ras.min_ey < ey2) ? ras.min_ey : ey2;
679 gray_set_cell( &ras, ex, ey1 );
680 } else {
681 ey1++;
682 gray_set_cell( &ras, ex, ey1 );
683 }
684 while ( ey1 < ey2 && ey1 < max_ey1)
685 {
686 ras.area += area;
687 ras.cover += delta;
688 ey1++;
689
690 gray_set_cell( &ras, ex, ey1 );
691 }
692 if (ey1 != ey2) {
693 ey1 = ey2;
694 gray_set_cell( &ras, ex, ey1 );
695 }
696 }
697
698 delta = (int)( fy2 - ONE_PIXEL + first );
699 ras.area += (TArea)two_fx * delta;
700 ras.cover += delta;
701
702 goto End;
703 }
704
705 /* ok, we have to render several scanlines */
706 if ( dy > 0)
707 {
708 p = ( ONE_PIXEL - fy1 ) * dx;
709 first = ONE_PIXEL;
710 incr = 1;
711 }
712 else
713 {
714 p = fy1 * dx;
715 first = 0;
716 incr = -1;
717 dy = -dy;
718 }
719
720 /* the fractional part of x-delta is mod/dy. It is essential to */
721 /* keep track of its accumulation for accurate rendering. */
722 QT_FT_DIV_MOD( TCoord, p, dy, delta, mod );
723
724 x = ras.x + delta;
726
727 ey1 += incr;
729
730 if ( ey1 != ey2 )
731 {
732 TCoord lift, rem;
733
734
735 p = ONE_PIXEL * dx;
736 QT_FT_DIV_MOD( TCoord, p, dy, lift, rem );
737
738 do
739 {
740 delta = lift;
741 mod += rem;
742 if ( mod >= (TCoord)dy )
743 {
744 mod -= (TCoord)dy;
745 delta++;
746 }
747
748 x2 = x + delta;
750 x, ONE_PIXEL - first,
751 x2, first );
752 x = x2;
753
754 ey1 += incr;
756 } while ( ey1 != ey2 );
757 }
758
760 x, ONE_PIXEL - first,
761 to_x, fy2 );
762
763 End:
764 ras.x = to_x;
765 ras.y = to_y;
766 }
767
768
769#else
770
771 /*************************************************************************/
772 /* */
773 /* Render a straight line across multiple cells in any direction. */
774 /* */
775 static void
776 gray_render_line( RAS_ARG_ TPos to_x,
777 TPos to_y )
778 {
779 TPos dx, dy, fx1, fy1, fx2, fy2;
780 TCoord ex1, ex2, ey1, ey2;
781
782
783 ex1 = TRUNC( ras.x );
784 ex2 = TRUNC( to_x );
785 ey1 = TRUNC( ras.y );
786 ey2 = TRUNC( to_y );
787
788 /* perform vertical clipping */
789 if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) ||
790 ( ey1 < ras.min_ey && ey2 < ras.min_ey ) )
791 goto End;
792
793 dx = to_x - ras.x;
794 dy = to_y - ras.y;
795
796 fx1 = FRACT( ras.x );
797 fy1 = FRACT( ras.y );
798
799 if ( ex1 == ex2 && ey1 == ey2 ) /* inside one cell */
800 ;
801 else if ( dy == 0 ) /* ex1 != ex2 */ /* any horizontal line */
802 {
803 ex1 = ex2;
804 gray_set_cell( RAS_VAR_ ex1, ey1 );
805 }
806 else if ( dx == 0 )
807 {
808 if ( dy > 0 ) /* vertical line up */
809 do
810 {
811 fy2 = ONE_PIXEL;
812 ras.cover += ( fy2 - fy1 );
813 ras.area += ( fy2 - fy1 ) * fx1 * 2;
814 fy1 = 0;
815 ey1++;
816 gray_set_cell( RAS_VAR_ ex1, ey1 );
817 } while ( ey1 != ey2 );
818 else /* vertical line down */
819 do
820 {
821 fy2 = 0;
822 ras.cover += ( fy2 - fy1 );
823 ras.area += ( fy2 - fy1 ) * fx1 * 2;
824 fy1 = ONE_PIXEL;
825 ey1--;
826 gray_set_cell( RAS_VAR_ ex1, ey1 );
827 } while ( ey1 != ey2 );
828 }
829 else /* any other line */
830 {
831 TArea prod = dx * fy1 - dy * fx1;
832 QT_FT_UDIVPREP( dx );
833 QT_FT_UDIVPREP( dy );
834
835
836 /* The fundamental value `prod' determines which side and the */
837 /* exact coordinate where the line exits current cell. It is */
838 /* also easily updated when moving from one cell to the next. */
839 do
840 {
841 if ( prod <= 0 &&
842 prod - dx * ONE_PIXEL > 0 ) /* left */
843 {
844 fx2 = 0;
845 fy2 = (TPos)QT_FT_UDIV( -prod, -dx );
846 prod -= dy * ONE_PIXEL;
847 ras.cover += ( fy2 - fy1 );
848 ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
849 fx1 = ONE_PIXEL;
850 fy1 = fy2;
851 ex1--;
852 }
853 else if ( prod - dx * ONE_PIXEL <= 0 &&
854 prod - dx * ONE_PIXEL + dy * ONE_PIXEL > 0 ) /* up */
855 {
856 prod -= dx * ONE_PIXEL;
857 fx2 = (TPos)QT_FT_UDIV( -prod, dy );
858 fy2 = ONE_PIXEL;
859 ras.cover += ( fy2 - fy1 );
860 ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
861 fx1 = fx2;
862 fy1 = 0;
863 ey1++;
864 }
865 else if ( prod - dx * ONE_PIXEL + dy * ONE_PIXEL <= 0 &&
866 prod + dy * ONE_PIXEL >= 0 ) /* right */
867 {
868 prod += dy * ONE_PIXEL;
869 fx2 = ONE_PIXEL;
870 fy2 = (TPos)QT_FT_UDIV( prod, dx );
871 ras.cover += ( fy2 - fy1 );
872 ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
873 fx1 = 0;
874 fy1 = fy2;
875 ex1++;
876 }
877 else /* ( prod + dy * ONE_PIXEL < 0 &&
878 prod > 0 ) down */
879 {
880 fx2 = (TPos)QT_FT_UDIV( prod, -dy );
881 fy2 = 0;
882 prod += dx * ONE_PIXEL;
883 ras.cover += ( fy2 - fy1 );
884 ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
885 fx1 = fx2;
886 fy1 = ONE_PIXEL;
887 ey1--;
888 }
889
890 gray_set_cell( RAS_VAR_ ex1, ey1 );
891 } while ( ex1 != ex2 || ey1 != ey2 );
892 }
893
894 fx2 = FRACT( to_x );
895 fy2 = FRACT( to_y );
896
897 ras.cover += ( fy2 - fy1 );
898 ras.area += ( fy2 - fy1 ) * ( fx1 + fx2 );
899
900 End:
901 ras.x = to_x;
902 ras.y = to_y;
903 }
904
905#endif
906
907 static void
908 gray_split_conic( QT_FT_Vector* base )
909 {
910 TPos a, b;
911
912
913 base[4].x = base[2].x;
914 b = base[1].x;
915 a = base[3].x = ( base[2].x + b ) / 2;
916 b = base[1].x = ( base[0].x + b ) / 2;
917 base[2].x = ( a + b ) / 2;
918
919 base[4].y = base[2].y;
920 b = base[1].y;
921 a = base[3].y = ( base[2].y + b ) / 2;
922 b = base[1].y = ( base[0].y + b ) / 2;
923 base[2].y = ( a + b ) / 2;
924 }
925
926
927 static void
928 gray_render_conic( RAS_ARG_ const QT_FT_Vector* control,
929 const QT_FT_Vector* to )
930 {
931 QT_FT_Vector bez_stack[16 * 2 + 1]; /* enough to accommodate bisections */
932 QT_FT_Vector* arc = bez_stack;
933 TPos dx, dy;
934 int draw, split;
935
936
937 arc[0].x = UPSCALE( to->x );
938 arc[0].y = UPSCALE( to->y );
939 arc[1].x = UPSCALE( control->x );
940 arc[1].y = UPSCALE( control->y );
941 arc[2].x = ras.x;
942 arc[2].y = ras.y;
943
944 /* short-cut the arc that crosses the current band */
945 if ( ( TRUNC( arc[0].y ) >= ras.max_ey &&
946 TRUNC( arc[1].y ) >= ras.max_ey &&
947 TRUNC( arc[2].y ) >= ras.max_ey ) ||
948 ( TRUNC( arc[0].y ) < ras.min_ey &&
949 TRUNC( arc[1].y ) < ras.min_ey &&
950 TRUNC( arc[2].y ) < ras.min_ey ) )
951 {
952 ras.x = arc[0].x;
953 ras.y = arc[0].y;
954 return;
955 }
956
957 dx = QT_FT_ABS( arc[2].x + arc[0].x - 2 * arc[1].x );
958 dy = QT_FT_ABS( arc[2].y + arc[0].y - 2 * arc[1].y );
959 if ( dx < dy )
960 dx = dy;
961
962 /* We can calculate the number of necessary bisections because */
963 /* each bisection predictably reduces deviation exactly 4-fold. */
964 /* Even 32-bit deviation would vanish after 16 bisections. */
965 draw = 1;
966 while ( dx > ONE_PIXEL / 4 )
967 {
968 dx >>= 2;
969 draw <<= 1;
970 }
971
972 /* We use decrement counter to count the total number of segments */
973 /* to draw starting from 2^level. Before each draw we split as */
974 /* many times as there are trailing zeros in the counter. */
975 do
976 {
977 split = 1;
978 while ( ( draw & split ) == 0 )
979 {
980 gray_split_conic( arc );
981 arc += 2;
982 split <<= 1;
983 }
984
985 gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
986 arc -= 2;
987
988 } while ( --draw );
989 }
990
991
992 static void
993 gray_split_cubic( QT_FT_Vector* base )
994 {
995 TPos a, b, c, d;
996
997
998 base[6].x = base[3].x;
999 c = base[1].x;
1000 d = base[2].x;
1001 base[1].x = a = ( base[0].x + c ) / 2;
1002 base[5].x = b = ( base[3].x + d ) / 2;
1003 c = ( c + d ) / 2;
1004 base[2].x = a = ( a + c ) / 2;
1005 base[4].x = b = ( b + c ) / 2;
1006 base[3].x = ( a + b ) / 2;
1007
1008 base[6].y = base[3].y;
1009 c = base[1].y;
1010 d = base[2].y;
1011 base[1].y = a = ( base[0].y + c ) / 2;
1012 base[5].y = b = ( base[3].y + d ) / 2;
1013 c = ( c + d ) / 2;
1014 base[2].y = a = ( a + c ) / 2;
1015 base[4].y = b = ( b + c ) / 2;
1016 base[3].y = ( a + b ) / 2;
1017 }
1018
1019
1020 static void
1021 gray_render_cubic( RAS_ARG_ const QT_FT_Vector* control1,
1022 const QT_FT_Vector* control2,
1023 const QT_FT_Vector* to )
1024 {
1025 QT_FT_Vector bez_stack[16 * 3 + 1]; /* enough to accommodate bisections */
1026 QT_FT_Vector* arc = bez_stack;
1027 TPos dx, dy, dx_, dy_;
1028 TPos dx1, dy1, dx2, dy2;
1029 TPos L, s, s_limit;
1030
1031
1032 arc[0].x = UPSCALE( to->x );
1033 arc[0].y = UPSCALE( to->y );
1034 arc[1].x = UPSCALE( control2->x );
1035 arc[1].y = UPSCALE( control2->y );
1036 arc[2].x = UPSCALE( control1->x );
1037 arc[2].y = UPSCALE( control1->y );
1038 arc[3].x = ras.x;
1039 arc[3].y = ras.y;
1040
1041 /* short-cut the arc that crosses the current band */
1042 if ( ( TRUNC( arc[0].y ) >= ras.max_ey &&
1043 TRUNC( arc[1].y ) >= ras.max_ey &&
1044 TRUNC( arc[2].y ) >= ras.max_ey &&
1045 TRUNC( arc[3].y ) >= ras.max_ey ) ||
1046 ( TRUNC( arc[0].y ) < ras.min_ey &&
1047 TRUNC( arc[1].y ) < ras.min_ey &&
1048 TRUNC( arc[2].y ) < ras.min_ey &&
1049 TRUNC( arc[3].y ) < ras.min_ey ) )
1050 {
1051 ras.x = arc[0].x;
1052 ras.y = arc[0].y;
1053 return;
1054 }
1055
1056 for (;;)
1057 {
1058 /* Decide whether to split or draw. See `Rapid Termination */
1059 /* Evaluation for Recursive Subdivision of Bezier Curves' by Thomas */
1060 /* F. Hain, at */
1061 /* http://www.cis.southalabama.edu/~hain/general/Publications/Bezier/Camera-ready%20CISST02%202.pdf */
1062
1063
1064 /* dx and dy are x and y components of the P0-P3 chord vector. */
1065 dx = dx_ = arc[3].x - arc[0].x;
1066 dy = dy_ = arc[3].y - arc[0].y;
1067
1068 L = QT_FT_HYPOT( dx_, dy_ );
1069
1070 /* Avoid possible arithmetic overflow below by splitting. */
1071 if ( L >= (1 << 23) )
1072 goto Split;
1073
1074 /* Max deviation may be as much as (s/L) * 3/4 (if Hain's v = 1). */
1075 s_limit = L * (TPos)( ONE_PIXEL / 6 );
1076
1077 /* s is L * the perpendicular distance from P1 to the line P0-P3. */
1078 dx1 = arc[1].x - arc[0].x;
1079 dy1 = arc[1].y - arc[0].y;
1080 s = QT_FT_ABS( dy * dx1 - dx * dy1 );
1081
1082 if ( s > s_limit )
1083 goto Split;
1084
1085 /* s is L * the perpendicular distance from P2 to the line P0-P3. */
1086 dx2 = arc[2].x - arc[0].x;
1087 dy2 = arc[2].y - arc[0].y;
1088 s = QT_FT_ABS( dy * dx2 - dx * dy2 );
1089
1090 if ( s > s_limit )
1091 goto Split;
1092
1093 /* Split super curvy segments where the off points are so far
1094 from the chord that the angles P0-P1-P3 or P0-P2-P3 become
1095 acute as detected by appropriate dot products. */
1096 if ( dx1 * ( dx1 - dx ) + dy1 * ( dy1 - dy ) > 0 ||
1097 dx2 * ( dx2 - dx ) + dy2 * ( dy2 - dy ) > 0 )
1098 goto Split;
1099
1100 gray_render_line( RAS_VAR_ arc[0].x, arc[0].y );
1101
1102 if ( arc == bez_stack )
1103 return;
1104
1105 arc -= 3;
1106 continue;
1107
1108 Split:
1109 gray_split_cubic( arc );
1110 arc += 3;
1111 }
1112 }
1113
1114
1115
1116 static int
1117 gray_move_to( const QT_FT_Vector* to,
1118 PWorker worker )
1119 {
1120 TPos x, y;
1121
1122
1123 /* record current cell, if any */
1124 if ( !ras.invalid )
1125 gray_record_cell( worker );
1126
1127 /* start to a new position */
1128 x = UPSCALE( to->x );
1129 y = UPSCALE( to->y );
1130
1131 gray_start_cell( worker, TRUNC( x ), TRUNC( y ) );
1132
1133 ras.x = x;
1134 ras.y = y;
1135 return 0;
1136 }
1137
1138 static void
1140 const QT_FT_Span* spans,
1141 PWorker worker )
1142 {
1143 unsigned char* p;
1144 QT_FT_Bitmap* map = &worker->target;
1145
1146 for ( ; count > 0; count--, spans++ )
1147 {
1148 unsigned char coverage = spans->coverage;
1149
1150 /* first of all, compute the scanline offset */
1151 p = (unsigned char*)map->buffer - spans->y * map->pitch;
1152 if ( map->pitch >= 0 )
1153 p += ( map->rows - 1 ) * (unsigned int)map->pitch;
1154
1155
1156 if ( coverage )
1157 {
1158 unsigned char* q = p + spans->x;
1159
1160
1161 /* For small-spans it is faster to do it by ourselves than
1162 * calling `memset'. This is mainly due to the cost of the
1163 * function call.
1164 */
1165 switch ( spans->len )
1166 {
1167 case 7: *q++ = coverage; Q_FALLTHROUGH();
1168 case 6: *q++ = coverage; Q_FALLTHROUGH();
1169 case 5: *q++ = coverage; Q_FALLTHROUGH();
1170 case 4: *q++ = coverage; Q_FALLTHROUGH();
1171 case 3: *q++ = coverage; Q_FALLTHROUGH();
1172 case 2: *q++ = coverage; Q_FALLTHROUGH();
1173 case 1: *q = coverage; Q_FALLTHROUGH();
1174 case 0: break;
1175 default:
1176 QT_FT_MEM_SET( q, coverage, spans->len );
1177 }
1178 }
1179 }
1180 }
1181
1182
1183 static void
1185 TCoord y,
1186 TPos area,
1187 int acount )
1188 {
1189 int coverage;
1190
1191
1192 /* compute the coverage line's coverage, depending on the */
1193 /* outline fill rule */
1194 /* */
1195 /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
1196 /* */
1197 coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) );
1198 /* use range 0..256 */
1199 if ( coverage < 0 )
1200 coverage = -coverage;
1201
1202 if ( ras.outline.flags & QT_FT_OUTLINE_EVEN_ODD_FILL )
1203 {
1204 coverage &= 511;
1205
1206 if ( coverage > 256 )
1207 coverage = 512 - coverage;
1208 else if ( coverage == 256 )
1209 coverage = 255;
1210 }
1211 else
1212 {
1213 /* normal non-zero winding rule */
1214 if ( coverage >= 256 )
1215 coverage = 255;
1216 }
1217
1218 y += (TCoord)ras.min_ey;
1219 x += (TCoord)ras.min_ex;
1220
1221 /* QT_FT_Span.x is an int, so limit our coordinates appropriately */
1222 if ( x >= (1 << 23) )
1223 x = (1 << 23) - 1;
1224
1225 /* QT_FT_Span.y is an int, so limit our coordinates appropriately */
1226 if ( y >= (1 << 23) )
1227 y = (1 << 23) - 1;
1228
1229 if ( coverage )
1230 {
1231 QT_FT_Span* span;
1232 int count;
1233 int skip;
1234
1235
1236 /* see whether we can add this span to the current list */
1237 count = ras.num_gray_spans;
1238 span = ras.gray_spans + count - 1;
1239 if ( count > 0 &&
1240 span->y == y &&
1241 span->x + span->len == x &&
1242 span->coverage == coverage )
1243 {
1244 span->len = span->len + acount;
1245 return;
1246 }
1247
1248 if ( count >= QT_FT_MAX_GRAY_SPANS )
1249 {
1250 if ( ras.render_span && count > ras.skip_spans )
1251 {
1252 skip = ras.skip_spans > 0 ? ras.skip_spans : 0;
1253 ras.render_span( ras.num_gray_spans - skip,
1254 ras.gray_spans + skip,
1256 }
1257
1259
1260 /* ras.render_span( span->y, ras.gray_spans, count ); */
1261
1262#ifdef DEBUG_GRAYS
1263
1264 if ( 1 )
1265 {
1266 int n;
1267
1268
1269 fprintf( stderr, "y=%3d ", y );
1270 span = ras.gray_spans;
1271 for ( n = 0; n < count; n++, span++ )
1272 fprintf( stderr, "[%d..%d]:%02x ",
1273 span->x, span->x + span->len - 1, span->coverage );
1274 fprintf( stderr, "\n" );
1275 }
1276
1277#endif /* DEBUG_GRAYS */
1278
1280
1281 span = ras.gray_spans;
1282 }
1283 else
1284 span++;
1285
1286 /* add a gray span to the current list */
1287 span->x = x;
1288 span->len = acount;
1289 span->y = y;
1290 span->coverage = (unsigned char)coverage;
1291
1293 }
1294 }
1295
1296
1297#ifdef DEBUG_GRAYS
1298
1299 /* to be called while in the debugger */
1300 gray_dump_cells( RAS_ARG )
1301 {
1302 int yindex;
1303
1304
1305 for ( yindex = 0; yindex < ras.ycount; yindex++ )
1306 {
1307 PCell cell;
1308
1309
1310 printf( "%3d:", yindex );
1311
1312 for ( cell = ras.ycells[yindex]; cell != NULL; cell = cell->next )
1313 printf( " (%3d, c:%4d, a:%6d)", cell->x, cell->cover, cell->area );
1314 printf( "\n" );
1315 }
1316 }
1317
1318#endif /* DEBUG_GRAYS */
1319
1320
1321 static void
1322 gray_sweep( RAS_ARG_ const QT_FT_Bitmap* target )
1323 {
1324 int yindex;
1325
1326 QT_FT_UNUSED( target );
1327
1328
1329 if ( ras.num_cells == 0 )
1330 return;
1331
1332 QT_FT_TRACE7(( "gray_sweep: start\n" ));
1333
1334 for ( yindex = 0; yindex < ras.ycount; yindex++ )
1335 {
1336 PCell cell = ras.ycells[yindex];
1337 TCoord cover = 0;
1338 TCoord x = 0;
1339
1340
1341 for ( ; cell != NULL; cell = cell->next )
1342 {
1343 TArea area;
1344
1345
1346 if ( cell->x > x && cover != 0 )
1347 gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
1348 cell->x - x );
1349
1350 cover += cell->cover;
1351 area = cover * ( ONE_PIXEL * 2 ) - cell->area;
1352
1353 if ( area != 0 && cell->x >= 0 )
1354 gray_hline( RAS_VAR_ cell->x, yindex, area, 1 );
1355
1356 x = cell->x + 1;
1357 }
1358
1359 if ( ras.count_ex > x && cover != 0 )
1360 gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
1361 ras.count_ex - x );
1362 }
1363
1364 QT_FT_TRACE7(( "gray_sweep: end\n" ));
1365 }
1366
1367 /*************************************************************************/
1368 /* */
1369 /* The following function should only compile in stand_alone mode, */
1370 /* i.e., when building this component without the rest of FreeType. */
1371 /* */
1372 /*************************************************************************/
1373
1374 /*************************************************************************/
1375 /* */
1376 /* <Function> */
1377 /* QT_FT_Outline_Decompose */
1378 /* */
1379 /* <Description> */
1380 /* Walks over an outline's structure to decompose it into individual */
1381 /* segments and Bezier arcs. This function is also able to emit */
1382 /* `move to' and `close to' operations to indicate the start and end */
1383 /* of new contours in the outline. */
1384 /* */
1385 /* <Input> */
1386 /* outline :: A pointer to the source target. */
1387 /* */
1388 /* user :: A typeless pointer which is passed to each */
1389 /* emitter during the decomposition. It can be */
1390 /* used to store the state during the */
1391 /* decomposition. */
1392 /* */
1393 /* <Return> */
1394 /* Error code. 0 means success. */
1395 /* */
1396 static
1397 int QT_FT_Outline_Decompose( const QT_FT_Outline* outline,
1398 void* user )
1399 {
1400#undef SCALED
1401#define SCALED( x ) (x)
1402
1403 QT_FT_Vector v_last;
1404 QT_FT_Vector v_control;
1405 QT_FT_Vector v_start;
1406
1407 QT_FT_Vector* point;
1408 QT_FT_Vector* limit;
1409 char* tags;
1410
1411 int n; /* index of contour in outline */
1412 int first; /* index of first point in contour */
1413 int error;
1414 char tag; /* current point's state */
1415
1416 if ( !outline )
1418
1419 first = 0;
1420
1421 for ( n = 0; n < outline->n_contours; n++ )
1422 {
1423 int last; /* index of last point in contour */
1424
1425
1426 last = outline->contours[n];
1427 if ( last < 0 )
1428 goto Invalid_Outline;
1429 limit = outline->points + last;
1430
1431 v_start = outline->points[first];
1432 v_start.x = SCALED( v_start.x );
1433 v_start.y = SCALED( v_start.y );
1434
1435 v_last = outline->points[last];
1436 v_last.x = SCALED( v_last.x );
1437 v_last.y = SCALED( v_last.y );
1438
1439 v_control = v_start;
1440
1441 point = outline->points + first;
1442 tags = outline->tags + first;
1443 tag = QT_FT_CURVE_TAG( tags[0] );
1444
1445 /* A contour cannot start with a cubic control point! */
1446 if ( tag == QT_FT_CURVE_TAG_CUBIC )
1447 goto Invalid_Outline;
1448
1449 /* check first point to determine origin */
1450 if ( tag == QT_FT_CURVE_TAG_CONIC )
1451 {
1452 /* first point is conic control. Yes, this happens. */
1453 if ( QT_FT_CURVE_TAG( outline->tags[last] ) == QT_FT_CURVE_TAG_ON )
1454 {
1455 /* start at last point if it is on the curve */
1456 v_start = v_last;
1457 limit--;
1458 }
1459 else
1460 {
1461 /* if both first and last points are conic, */
1462 /* start at their middle and record its position */
1463 /* for closure */
1464 v_start.x = ( v_start.x + v_last.x ) / 2;
1465 v_start.y = ( v_start.y + v_last.y ) / 2;
1466
1467 v_last = v_start;
1468 }
1469 point--;
1470 tags--;
1471 }
1472
1473 QT_FT_TRACE5(( " move to (%.2f, %.2f)\n",
1474 v_start.x / 64.0, v_start.y / 64.0 ));
1475 error = gray_move_to( &v_start, user );
1476 if ( error )
1477 goto Exit;
1478
1479 while ( point < limit )
1480 {
1481 point++;
1482 tags++;
1483
1484 tag = QT_FT_CURVE_TAG( tags[0] );
1485 switch ( tag )
1486 {
1487 case QT_FT_CURVE_TAG_ON: /* emit a single line_to */
1488 {
1489 QT_FT_Vector vec;
1490
1491
1492 vec.x = SCALED( point->x );
1493 vec.y = SCALED( point->y );
1494
1495 QT_FT_TRACE5(( " line to (%.2f, %.2f)\n",
1496 vec.x / 64.0, vec.y / 64.0 ));
1497 gray_render_line(user, UPSCALE(vec.x), UPSCALE(vec.y));
1498 continue;
1499 }
1500
1501 case QT_FT_CURVE_TAG_CONIC: /* consume conic arcs */
1502 {
1503 v_control.x = SCALED( point->x );
1504 v_control.y = SCALED( point->y );
1505
1506 Do_Conic:
1507 if ( point < limit )
1508 {
1509 QT_FT_Vector vec;
1510 QT_FT_Vector v_middle;
1511
1512
1513 point++;
1514 tags++;
1515 tag = QT_FT_CURVE_TAG( tags[0] );
1516
1517 vec.x = SCALED( point->x );
1518 vec.y = SCALED( point->y );
1519
1520 if ( tag == QT_FT_CURVE_TAG_ON )
1521 {
1522 QT_FT_TRACE5(( " conic to (%.2f, %.2f)"
1523 " with control (%.2f, %.2f)\n",
1524 vec.x / 64.0, vec.y / 64.0,
1525 v_control.x / 64.0, v_control.y / 64.0 ));
1526 gray_render_conic(user, &v_control, &vec);
1527 continue;
1528 }
1529
1530 if ( tag != QT_FT_CURVE_TAG_CONIC )
1531 goto Invalid_Outline;
1532
1533 v_middle.x = ( v_control.x + vec.x ) / 2;
1534 v_middle.y = ( v_control.y + vec.y ) / 2;
1535
1536 QT_FT_TRACE5(( " conic to (%.2f, %.2f)"
1537 " with control (%.2f, %.2f)\n",
1538 v_middle.x / 64.0, v_middle.y / 64.0,
1539 v_control.x / 64.0, v_control.y / 64.0 ));
1540 gray_render_conic(user, &v_control, &v_middle);
1541
1542 v_control = vec;
1543 goto Do_Conic;
1544 }
1545
1546 QT_FT_TRACE5(( " conic to (%.2f, %.2f)"
1547 " with control (%.2f, %.2f)\n",
1548 v_start.x / 64.0, v_start.y / 64.0,
1549 v_control.x / 64.0, v_control.y / 64.0 ));
1550 gray_render_conic(user, &v_control, &v_start);
1551 goto Close;
1552 }
1553
1554 default: /* QT_FT_CURVE_TAG_CUBIC */
1555 {
1556 QT_FT_Vector vec1, vec2;
1557
1558
1559 if ( point + 1 > limit ||
1560 QT_FT_CURVE_TAG( tags[1] ) != QT_FT_CURVE_TAG_CUBIC )
1561 goto Invalid_Outline;
1562
1563 point += 2;
1564 tags += 2;
1565
1566 vec1.x = SCALED( point[-2].x );
1567 vec1.y = SCALED( point[-2].y );
1568
1569 vec2.x = SCALED( point[-1].x );
1570 vec2.y = SCALED( point[-1].y );
1571
1572 if ( point <= limit )
1573 {
1574 QT_FT_Vector vec;
1575
1576
1577 vec.x = SCALED( point->x );
1578 vec.y = SCALED( point->y );
1579
1580 QT_FT_TRACE5(( " cubic to (%.2f, %.2f)"
1581 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
1582 vec.x / 64.0, vec.y / 64.0,
1583 vec1.x / 64.0, vec1.y / 64.0,
1584 vec2.x / 64.0, vec2.y / 64.0 ));
1585 gray_render_cubic(user, &vec1, &vec2, &vec);
1586 continue;
1587 }
1588
1589 QT_FT_TRACE5(( " cubic to (%.2f, %.2f)"
1590 " with controls (%.2f, %.2f) and (%.2f, %.2f)\n",
1591 v_start.x / 64.0, v_start.y / 64.0,
1592 vec1.x / 64.0, vec1.y / 64.0,
1593 vec2.x / 64.0, vec2.y / 64.0 ));
1594 gray_render_cubic(user, &vec1, &vec2, &v_start);
1595 goto Close;
1596 }
1597 }
1598 }
1599
1600 /* close the contour with a line segment */
1601 QT_FT_TRACE5(( " line to (%.2f, %.2f)\n",
1602 v_start.x / 64.0, v_start.y / 64.0 ));
1603 gray_render_line(user, UPSCALE(v_start.x), UPSCALE(v_start.y));
1604
1605 Close:
1606 first = last + 1;
1607 }
1608
1609 QT_FT_TRACE5(( "FT_Outline_Decompose: Done\n", n ));
1610 return 0;
1611
1612 Exit:
1613 QT_FT_TRACE5(( "FT_Outline_Decompose: Error %d\n", error ));
1614 return error;
1615
1616 Invalid_Outline:
1618 }
1619
1620 typedef struct TBand_
1621 {
1623
1624 } TBand;
1625
1626 static int
1628 {
1629 volatile int error = 0;
1630
1631 if ( qt_ft_setjmp( ras.jump_buffer ) == 0 )
1632 {
1633 error = QT_FT_Outline_Decompose( &ras.outline, &ras );
1634 if ( !ras.invalid )
1636 }
1637 else
1638 {
1640 }
1641
1642 return error;
1643 }
1644
1645
1646 static int
1648 {
1649 TBand bands[40];
1650 TBand* volatile band;
1651 int volatile n, num_bands;
1652 TPos volatile min, max, max_y;
1653 QT_FT_BBox* clip;
1654 int skip;
1655
1657
1658 /* Set up state in the raster object */
1660
1661 /* clip to target bitmap, exit if nothing to do */
1662 clip = &ras.clip_box;
1663
1664 if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax ||
1665 ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax )
1666 return 0;
1667
1668 if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin;
1669 if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin;
1670
1671 if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax;
1672 if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax;
1673
1676
1677 /* set up vertical bands */
1678 num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size );
1679 if ( num_bands == 0 )
1680 num_bands = 1;
1681 if ( num_bands >= 39 )
1682 num_bands = 39;
1683
1684 ras.band_shoot = 0;
1685
1686 min = ras.min_ey;
1687 max_y = ras.max_ey;
1688
1689 for ( n = 0; n < num_bands; n++, min = max )
1690 {
1691 max = min + ras.band_size;
1692 if ( n == num_bands - 1 || max > max_y )
1693 max = max_y;
1694
1695 bands[0].min = min;
1696 bands[0].max = max;
1697 band = bands;
1698
1699 while ( band >= bands )
1700 {
1701 TPos bottom, top, middle;
1702 int error;
1703
1704 {
1705 PCell cells_max;
1706 int yindex;
1707 int cell_start, cell_end, cell_mod;
1708
1709
1711 ras.ycount = band->max - band->min;
1712
1713 cell_start = sizeof ( PCell ) * ras.ycount;
1714 cell_mod = cell_start % sizeof ( TCell );
1715 if ( cell_mod > 0 )
1716 cell_start += sizeof ( TCell ) - cell_mod;
1717
1718 cell_end = ras.buffer_size;
1719 cell_end -= cell_end % sizeof( TCell );
1720
1721 cells_max = (PCell)( (char*)ras.buffer + cell_end );
1722 ras.cells = (PCell)( (char*)ras.buffer + cell_start );
1723 if ( ras.cells >= cells_max )
1724 goto ReduceBands;
1725
1726 ras.max_cells = (int)(cells_max - ras.cells);
1727 if ( ras.max_cells < 2 )
1728 goto ReduceBands;
1729
1730 for ( yindex = 0; yindex < ras.ycount; yindex++ )
1731 ras.ycells[yindex] = NULL;
1732 }
1733
1734 ras.num_cells = 0;
1735 ras.invalid = 1;
1736 ras.min_ey = band->min;
1737 ras.max_ey = band->max;
1738 ras.count_ey = band->max - band->min;
1739
1741
1742 if ( !error )
1743 {
1744 gray_sweep( RAS_VAR_ &ras.target );
1745 band--;
1746 continue;
1747 }
1748 else if ( error != ErrRaster_Memory_Overflow )
1749 return 1;
1750
1751 ReduceBands:
1752 /* render pool overflow; we will reduce the render band by half */
1753 bottom = band->min;
1754 top = band->max;
1755 middle = bottom + ( ( top - bottom ) >> 1 );
1756
1757 /* This is too complex for a single scanline; there must */
1758 /* be some problems. */
1759 if ( middle == bottom )
1760 {
1761#ifdef DEBUG_GRAYS
1762 fprintf( stderr, "Rotten glyph!\n" );
1763#endif
1764 return ErrRaster_OutOfMemory;
1765 }
1766
1767 if ( bottom-top >= ras.band_size )
1769
1770 band[1].min = bottom;
1771 band[1].max = middle;
1772 band[0].min = middle;
1773 band[0].max = top;
1774 band++;
1775 }
1776 }
1777
1778 if ( ras.render_span && ras.num_gray_spans > ras.skip_spans )
1779 {
1780 skip = ras.skip_spans > 0 ? ras.skip_spans : 0;
1781 ras.render_span( ras.num_gray_spans - skip,
1782 ras.gray_spans + skip,
1784 }
1785
1787
1788 if ( ras.band_shoot > 8 && ras.band_size > 16 )
1790
1791 return 0;
1792 }
1793
1794
1795 static int
1796 gray_raster_render( QT_FT_Raster raster,
1797 const QT_FT_Raster_Params* params )
1798 {
1799 const QT_FT_Outline* outline = (const QT_FT_Outline*)params->source;
1800 const QT_FT_Bitmap* target_map = params->target;
1801 PWorker worker;
1802
1803
1804 if ( !raster || !raster->buffer || !raster->buffer_size )
1806
1807 /* Should always be non-null, it is set by raster_reset() which is always */
1808 /* called with a non-null pool, and a pool_size >= MINIMUM_POOL_SIZE. */
1809 assert(raster->worker);
1810
1811 raster->worker->skip_spans = params->skip_spans;
1812
1813 /* If raster object and raster buffer are allocated, but */
1814 /* raster size isn't of the minimum size, indicate out of */
1815 /* memory. */
1816 if (raster->buffer_allocated_size < MINIMUM_POOL_SIZE )
1817 return ErrRaster_OutOfMemory;
1818
1819 if ( !outline )
1821
1822 /* return immediately if the outline is empty */
1823 if ( outline->n_points == 0 || outline->n_contours <= 0 )
1824 return 0;
1825
1826 if ( !outline->contours || !outline->points )
1828
1829 if ( outline->n_points !=
1830 outline->contours[outline->n_contours - 1] + 1 )
1832
1833 worker = raster->worker;
1834
1835 /* if direct mode is not set, we must have a target bitmap */
1836 if ( ( params->flags & QT_FT_RASTER_FLAG_DIRECT ) == 0 )
1837 {
1838 if ( !target_map )
1840
1841 /* nothing to do */
1842 if ( !target_map->width || !target_map->rows )
1843 return 0;
1844
1845 if ( !target_map->buffer )
1847 }
1848
1849 /* this version does not support monochrome rendering */
1850 if ( !( params->flags & QT_FT_RASTER_FLAG_AA ) )
1852
1853 /* compute clipping box */
1854 if ( ( params->flags & QT_FT_RASTER_FLAG_DIRECT ) == 0 )
1855 {
1856 /* compute clip box from target pixmap */
1857 ras.clip_box.xMin = 0;
1858 ras.clip_box.yMin = 0;
1859 ras.clip_box.xMax = target_map->width;
1860 ras.clip_box.yMax = target_map->rows;
1861 }
1862 else if ( params->flags & QT_FT_RASTER_FLAG_CLIP )
1863 {
1864 ras.clip_box = params->clip_box;
1865 }
1866 else
1867 {
1868 ras.clip_box.xMin = -(1 << 23);
1869 ras.clip_box.yMin = -(1 << 23);
1870 ras.clip_box.xMax = (1 << 23) - 1;
1871 ras.clip_box.yMax = (1 << 23) - 1;
1872 }
1873
1874 gray_init_cells( worker, raster->buffer, raster->buffer_size );
1875
1876 ras.outline = *outline;
1877 ras.num_cells = 0;
1878 ras.invalid = 1;
1879 ras.band_size = raster->band_size;
1880
1881 if ( target_map )
1882 ras.target = *target_map;
1883
1884 ras.render_span = (QT_FT_Raster_Span_Func)gray_render_span;
1886
1887 if ( params->flags & QT_FT_RASTER_FLAG_DIRECT )
1888 {
1889 ras.render_span = (QT_FT_Raster_Span_Func)params->gray_spans;
1890 ras.render_span_data = params->user;
1891 }
1892
1893 return gray_convert_glyph( worker );
1894 }
1895
1896
1897 /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
1898 /**** a static object. *****/
1899
1900 static int
1901 gray_raster_new( QT_FT_Raster* araster )
1902 {
1903 *araster = malloc(sizeof(TRaster));
1904 if (!*araster) {
1905 *araster = 0;
1907 }
1908 QT_FT_MEM_ZERO(*araster, sizeof(TRaster));
1909
1910 return 0;
1911 }
1912
1913
1914 static void
1915 gray_raster_done( QT_FT_Raster raster )
1916 {
1917 free(raster);
1918 }
1919
1920
1921 static void
1922 gray_raster_reset( QT_FT_Raster raster,
1923 char* pool_base,
1924 long pool_size )
1925 {
1926 PRaster rast = (PRaster)raster;
1927
1928 if ( raster )
1929 {
1930 if ( pool_base && ( pool_size >= MINIMUM_POOL_SIZE ) )
1931 {
1932 PWorker worker = (PWorker)pool_base;
1933
1934
1935 rast->worker = worker;
1936 rast->buffer = pool_base +
1937 ( ( sizeof ( TWorker ) + sizeof ( TCell ) - 1 ) &
1938 ~( sizeof ( TCell ) - 1 ) );
1939 rast->buffer_size = (long)( ( pool_base + pool_size ) -
1940 (char*)rast->buffer ) &
1941 ~( sizeof ( TCell ) - 1 );
1942 rast->band_size = (int)( rast->buffer_size /
1943 ( sizeof ( TCell ) * 8 ) );
1944 }
1945 else if ( pool_base)
1946 { /* Case when there is a raster pool allocated, but it */
1947 /* doesn't have the minimum size (and so memory will be reallocated) */
1948 rast->buffer = pool_base;
1949 rast->worker = NULL;
1950 rast->buffer_size = pool_size;
1951 }
1952 else
1953 {
1954 rast->buffer = NULL;
1955 rast->buffer_size = 0;
1956 rast->worker = NULL;
1957 }
1958 rast->buffer_allocated_size = pool_size;
1959 }
1960 }
1961
1962 const QT_FT_Raster_Funcs QT_MANGLE_NAMESPACE(qt_ft_grays_raster) =
1963 {
1964 QT_FT_GLYPH_FORMAT_OUTLINE,
1965
1966 (QT_FT_Raster_New_Func) gray_raster_new,
1967 (QT_FT_Raster_Reset_Func) gray_raster_reset,
1968 (QT_FT_Raster_Set_Mode_Func)0,
1969 (QT_FT_Raster_Render_Func) gray_raster_render,
1970 (QT_FT_Raster_Done_Func) gray_raster_done
1971 };
1972
1973/* END */
#define assert
#define Q_FALLTHROUGH()
#define QT_FT_MAX_GRAY_SPANS
static int gray_move_to(const QT_FT_Vector *to, PWorker worker)
#define qt_ft_memset
#define qt_ft_setjmp
static void gray_record_cell(RAS_ARG)
#define RAS_ARG
#define QT_FT_MEM_ZERO(dest, count)
#define UPSCALE(x)
#define ErrRaster_Memory_Overflow
static PCell gray_find_cell(RAS_ARG)
struct TCell_ * PCell
long TPos
static void gray_raster_done(QT_FT_Raster raster)
#define TRUNC(x)
static void gray_render_line(RAS_ARG_ TPos to_x, TPos to_y)
#define QT_FT_UNUSED(x)
static int gray_convert_glyph_inner(RAS_ARG)
static void gray_start_cell(RAS_ARG_ TCoord ex, TCoord ey)
#define QT_FT_TRACE5(x)
static void gray_split_conic(QT_FT_Vector *base)
#define ErrRaster_Invalid_Argument
static int gray_raster_new(QT_FT_Raster *araster)
static int QT_FT_Outline_Decompose(const QT_FT_Outline *outline, void *user)
#define QT_FT_DIV_MOD(type, dividend, divisor, quotient, remainder)
#define QT_FT_HYPOT(x, y)
static void gray_render_cubic(RAS_ARG_ const QT_FT_Vector *control1, const QT_FT_Vector *control2, const QT_FT_Vector *to)
#define RAS_VAR
static int gray_convert_glyph(RAS_ARG)
ptrdiff_t QT_FT_PtrDist
#define QT_FT_ERR_XCAT(x, y)
Definition qgrayraster.c:98
static int gray_raster_render(QT_FT_Raster raster, const QT_FT_Raster_Params *params)
#define QT_FT_TRACE7(x)
#define RAS_VAR_
#define PIXEL_BITS
#define FRACT(x)
#define ErrRaster_Invalid_Mode
#define qt_ft_jmp_buf
#define qt_ft_longjmp
#define ras
static void gray_raster_reset(QT_FT_Raster raster, char *pool_base, long pool_size)
static void gray_compute_cbox(RAS_ARG)
#define RAS_ARG_
#define SCALED(x)
long TArea
static void gray_set_cell(RAS_ARG_ TCoord ex, TCoord ey)
#define ErrRaster_OutOfMemory
#define QT_FT_ERR_CAT(x, y)
Definition qgrayraster.c:99
#define QT_FT_MEM_SET(d, s, c)
static void gray_init_cells(RAS_ARG_ void *buffer, long byte_size)
static void gray_render_conic(RAS_ARG_ const QT_FT_Vector *control, const QT_FT_Vector *to)
static void gray_sweep(RAS_ARG_ const QT_FT_Bitmap *target)
static void gray_render_scanline(RAS_ARG_ TCoord ey, TPos x1, TCoord y1, TPos x2, TCoord y2)
static void gray_render_span(int count, const QT_FT_Span *spans, PWorker worker)
#define ErrRaster_Invalid_Outline
long TCoord
#define QT_FT_ABS(a)
#define ONE_PIXEL
static void gray_split_cubic(QT_FT_Vector *base)
static void gray_hline(RAS_ARG_ TCoord x, TCoord y, TPos area, int acount)
#define QT_MANGLE_NAMESPACE(name)
PCell next
int cover
TArea area
long buffer_size
void * buffer
PWorker worker
void * memory
int band_size
long buffer_allocated_size
TPos max_ex
TArea area
qt_ft_jmp_buf jump_buffer
PCell cells
QT_FT_PtrDist max_cells
int band_shoot
QT_FT_Raster_Span_Func render_span
void * buffer
TCoord ex
TPos ycount
TPos max_ey
QT_FT_BBox clip_box
int skip_spans
QT_FT_Span gray_spans[QT_FT_MAX_GRAY_SPANS]
TPos min_ey
long buffer_size
TPos count_ey
QT_FT_PtrDist num_cells
TPos min_ex
void * render_span_data
PCell * ycells
TCoord ey
int num_gray_spans
QT_FT_Outline outline
QT_FT_Bitmap target
int band_size
TPos count_ex