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