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;
241 TArea area;
242 PCell next;
243
244 } TCell;
245
246
247 typedef struct TWorker_
248 {
249 TCoord ex, ey;
250 TPos min_ex, max_ex;
251 TPos min_ey, max_ey;
252 TPos count_ex, count_ey;
253
254 TArea area;
255 int cover;
256 int invalid;
257
258 PCell cells;
259 QT_FT_PtrDist max_cells;
260 QT_FT_PtrDist num_cells;
261
262 TPos x, y;
263
264 QT_FT_Outline outline;
265 QT_FT_Bitmap target;
266 QT_FT_BBox clip_box;
267
268 QT_FT_Span gray_spans[QT_FT_MAX_GRAY_SPANS];
269 int num_gray_spans;
270
271 QT_FT_Raster_Span_Func render_span;
272 void* render_span_data;
273
274 int band_size;
275 int band_shoot;
276
277 qt_ft_jmp_buf jump_buffer;
278
279 void* buffer;
280 long buffer_size;
281
282 PCell* ycells;
283 TPos ycount;
284
285 int skip_spans;
286 } TWorker, *PWorker;
287
288
289 typedef struct TRaster_
290 {
291 void* buffer;
292 long buffer_size;
293 long buffer_allocated_size;
294 int band_size;
295 void* memory;
296 PWorker worker;
297
298 } TRaster, *PRaster;
299
300 int q_gray_rendered_spans(TRaster *raster)
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
312 gray_init_cells( RAS_ARG_ void* buffer,
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
333 gray_compute_cbox( RAS_ARG )
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
377 gray_find_cell( RAS_ARG )
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, val: 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
416 gray_record_cell( RAS_ARG )
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
434 gray_set_cell( RAS_ARG_ TCoord ex,
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 )
463 gray_record_cell( RAS_VAR );
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
481 gray_start_cell( RAS_ARG_ TCoord ex,
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
507 gray_render_scanline( RAS_ARG_ TCoord ey,
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_ ex: 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;
545 first = ONE_PIXEL;
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_ ex: 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_ ex: 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
602 gray_render_line( RAS_ARG_ TPos to_x,
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_ ey: ey1, ras.x, y1: fy1, x2: to_x, y2: 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 {
640 first = ONE_PIXEL;
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( worker: &ras, ex, ey: ey1 );
658 } else {
659 ey1--;
660 gray_set_cell( worker: &ras, ex, ey: 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( worker: &ras, ex, ey: ey1 );
669 }
670 if (ey1 != ey2) {
671 ey1 = ey2;
672 gray_set_cell( worker: &ras, ex, ey: ey1 );
673 }
674 } else {
675 if (ey1 < ras.min_ey) {
676 ey1 = (ras.min_ey < ey2) ? ras.min_ey : ey2;
677 gray_set_cell( worker: &ras, ex, ey: ey1 );
678 } else {
679 ey1++;
680 gray_set_cell( worker: &ras, ex, ey: 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( worker: &ras, ex, ey: ey1 );
689 }
690 if (ey1 != ey2) {
691 ey1 = ey2;
692 gray_set_cell( worker: &ras, ex, ey: 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;
707 first = ONE_PIXEL;
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_ ey: ey1, ras.x, y1: fy1, x2: x, y2: (TCoord)first );
724
725 ey1 += incr;
726 gray_set_cell( RAS_VAR_ TRUNC( x ), ey: 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;
747 gray_render_scanline( RAS_VAR_ ey: ey1,
748 x1: x, ONE_PIXEL - first,
749 x2, y2: first );
750 x = x2;
751
752 ey1 += incr;
753 gray_set_cell( RAS_VAR_ TRUNC( x ), ey: ey1 );
754 } while ( ey1 != ey2 );
755 }
756
757 gray_render_scanline( RAS_VAR_ ey: ey1,
758 x1: x, ONE_PIXEL - first,
759 x2: to_x, y2: 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
774 gray_render_line( RAS_ARG_ TPos to_x,
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
906 gray_split_conic( QT_FT_Vector* base )
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
926 gray_render_conic( RAS_ARG_ const QT_FT_Vector* control,
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( base: arc );
979 arc += 2;
980 split <<= 1;
981 }
982
983 gray_render_line( RAS_VAR_ to_x: arc[0].x, to_y: arc[0].y );
984 arc -= 2;
985
986 } while ( --draw );
987 }
988
989
990 static void
991 gray_split_cubic( QT_FT_Vector* base )
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
1019 gray_render_cubic( RAS_ARG_ const QT_FT_Vector* control1,
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_ to_x: arc[0].x, to_y: arc[0].y );
1099
1100 if ( arc == bez_stack )
1101 return;
1102
1103 arc -= 3;
1104 continue;
1105
1106 Split:
1107 gray_split_cubic( base: arc );
1108 arc += 3;
1109 }
1110 }
1111
1112
1113
1114 static int
1115 gray_move_to( const QT_FT_Vector* to,
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
1137 gray_render_span( int count,
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
1182 gray_hline( RAS_ARG_ TCoord x,
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 {
1229 QT_FT_Span* span;
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
1320 gray_sweep( RAS_ARG_ const QT_FT_Bitmap* target )
1321 {
1322 int yindex;
1323
1324 QT_FT_UNUSED( target );
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, y: yindex, area: cover * ( ONE_PIXEL * 2 ),
1346 acount: 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_ x: cell->x, y: yindex, area, acount: 1 );
1353
1354 x = cell->x + 1;
1355 }
1356
1357 if ( ras.count_ex > x && cover != 0 )
1358 gray_hline( RAS_VAR_ x, y: yindex, area: 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
1395 int QT_FT_Outline_Decompose( const QT_FT_Outline* outline,
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;
1406 QT_FT_Vector* limit;
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 )
1415 return ErrRaster_Invalid_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( to: &v_start, worker: 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(worker: 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(worker: user, control: &v_control, to: &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(worker: user, control: &v_control, to: &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(worker: user, control: &v_control, to: &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 ||
1558 QT_FT_CURVE_TAG( tags[1] ) != QT_FT_CURVE_TAG_CUBIC )
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(worker: user, control1: &vec1, control2: &vec2, to: &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(worker: user, control1: &vec1, control2: &vec2, to: &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(worker: 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:
1615 return ErrRaster_Invalid_Outline;
1616 }
1617
1618 typedef struct TBand_
1619 {
1620 TPos min, max;
1621
1622 } TBand;
1623
1624 static int
1625 gray_convert_glyph_inner( RAS_ARG )
1626 {
1627 volatile int error = 0;
1628
1629 if ( qt_ft_setjmp( ras.jump_buffer ) == 0 )
1630 {
1631 error = QT_FT_Outline_Decompose( outline: &ras.outline, user: &ras );
1632 if ( !ras.invalid )
1633 gray_record_cell( RAS_VAR );
1634 }
1635 else
1636 {
1637 error = ErrRaster_Memory_Overflow;
1638 }
1639
1640 return error;
1641 }
1642
1643
1644 static int
1645 gray_convert_glyph( RAS_ARG )
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 */
1657 gray_compute_cbox( RAS_VAR );
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
1738 error = gray_convert_glyph_inner( RAS_VAR );
1739
1740 if ( !error )
1741 {
1742 gray_sweep( RAS_VAR_ target: &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
1794 gray_raster_render( QT_FT_Raster raster,
1795 const QT_FT_Raster_Params* params )
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 )
1803 return ErrRaster_Invalid_Argument;
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 )
1818 return ErrRaster_Invalid_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 )
1825 return ErrRaster_Invalid_Outline;
1826
1827 if ( outline->n_points !=
1828 outline->contours[outline->n_contours - 1] + 1 )
1829 return ErrRaster_Invalid_Outline;
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 )
1837 return ErrRaster_Invalid_Argument;
1838
1839 /* nothing to do */
1840 if ( !target_map->width || !target_map->rows )
1841 return 0;
1842
1843 if ( !target_map->buffer )
1844 return ErrRaster_Invalid_Argument;
1845 }
1846
1847 /* this version does not support monochrome rendering */
1848 if ( !( params->flags & QT_FT_RASTER_FLAG_AA ) )
1849 return ErrRaster_Invalid_Mode;
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, buffer: raster->buffer, byte_size: 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
1882 ras.render_span = (QT_FT_Raster_Span_Func)gray_render_span;
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
1899 gray_raster_new( QT_FT_Raster* araster )
1900 {
1901 *araster = malloc(size: sizeof(TRaster));
1902 if (!*araster) {
1903 *araster = 0;
1904 return ErrRaster_Memory_Overflow;
1905 }
1906 QT_FT_MEM_ZERO(*araster, sizeof(TRaster));
1907
1908 return 0;
1909 }
1910
1911
1912 static void
1913 gray_raster_done( QT_FT_Raster raster )
1914 {
1915 free(ptr: raster);
1916 }
1917
1918
1919 static void
1920 gray_raster_reset( QT_FT_Raster raster,
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
1960 const QT_FT_Raster_Funcs qt_ft_grays_raster =
1961 {
1962 QT_FT_GLYPH_FORMAT_OUTLINE,
1963
1964 (QT_FT_Raster_New_Func) gray_raster_new,
1965 (QT_FT_Raster_Reset_Func) gray_raster_reset,
1966 (QT_FT_Raster_Set_Mode_Func)0,
1967 (QT_FT_Raster_Render_Func) gray_raster_render,
1968 (QT_FT_Raster_Done_Func) gray_raster_done
1969 };
1970
1971/* END */
1972

source code of qtbase/src/gui/painting/qgrayraster.c