1 /***************************************************************************/ 2 /* */ 3 /* ftgrays.c */ 4 /* */ 5 /* A new `perfect' anti-aliasing renderer (body). */ 6 /* */ 7 /* Copyright 2000-2018 by */ 8 /* David Turner, Robert Wilhelm, and Werner Lemberg. */ 9 /* */ 10 /* This file is part of the FreeType project, and may only be used, */ 11 /* modified, and distributed under the terms of the FreeType project */ 12 /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ 13 /* this file you indicate that you have read the license and */ 14 /* understand and accept it fully. */ 15 /* */ 16 /***************************************************************************/ 17 18 /*************************************************************************/ 19 /* */ 20 /* This file can be compiled without the rest of the FreeType engine, by */ 21 /* defining the STANDALONE_ macro when compiling it. You also need to */ 22 /* put the files `ftgrays.h' and `ftimage.h' into the current */ 23 /* compilation directory. Typically, you could do something like */ 24 /* */ 25 /* - copy `src/smooth/ftgrays.c' (this file) to your current directory */ 26 /* */ 27 /* - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the */ 28 /* same directory */ 29 /* */ 30 /* - compile `ftgrays' with the STANDALONE_ macro defined, as in */ 31 /* */ 32 /* cc -c -DSTANDALONE_ ftgrays.c */ 33 /* */ 34 /* The renderer can be initialized with a call to */ 35 /* `ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated */ 36 /* with a call to `ft_gray_raster.raster_render'. */ 37 /* */ 38 /* See the comments and documentation in the file `ftimage.h' for more */ 39 /* details on how the raster works. */ 40 /* */ 41 /*************************************************************************/ 42 43 /*************************************************************************/ 44 /* */ 45 /* This is a new anti-aliasing scan-converter for FreeType 2. The */ 46 /* algorithm used here is _very_ different from the one in the standard */ 47 /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */ 48 /* coverage of the outline on each pixel cell. */ 49 /* */ 50 /* It is based on ideas that I initially found in Raph Levien's */ 51 /* excellent LibArt graphics library (see http://www.levien.com/libart */ 52 /* for more information, though the web pages do not tell anything */ 53 /* about the renderer; you'll have to dive into the source code to */ 54 /* understand how it works). */ 55 /* */ 56 /* Note, however, that this is a _very_ different implementation */ 57 /* compared to Raph's. Coverage information is stored in a very */ 58 /* different way, and I don't use sorted vector paths. Also, it doesn't */ 59 /* use floating point values. */ 60 /* */ 61 /* This renderer has the following advantages: */ 62 /* */ 63 /* - It doesn't need an intermediate bitmap. Instead, one can supply a */ 64 /* callback function that will be called by the renderer to draw gray */ 65 /* spans on any target surface. You can thus do direct composition on */ 66 /* any kind of bitmap, provided that you give the renderer the right */ 67 /* callback. */ 68 /* */ 69 /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */ 70 /* each pixel cell. */ 71 /* */ 72 /* - It performs a single pass on the outline (the `standard' FT2 */ 73 /* renderer makes two passes). */ 74 /* */ 75 /* - It can easily be modified to render to _any_ number of gray levels */ 76 /* cheaply. */ 77 /* */ 78 /* - For small (< 20) pixel sizes, it is faster than the standard */ 79 /* renderer. */ 80 /* */ 81 /*************************************************************************/ 82 83 84 /*************************************************************************/ 85 /* */ 86 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ 87 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ 88 /* messages during execution. */ 89 /* */ 90 #undef FT_COMPONENT 91 #define FT_COMPONENT trace_smooth 92 93 94 #ifdef STANDALONE_ 95 96 97 /* The size in bytes of the render pool used by the scan-line converter */ 98 /* to do all of its work. */ 99 #define FT_RENDER_POOL_SIZE 16384L 100 101 102 /* Auxiliary macros for token concatenation. */ 103 #define FT_ERR_XCAT( x, y ) x ## y 104 #define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) 105 106 #define FT_BEGIN_STMNT do { 107 #define FT_END_STMNT } while ( 0 ) 108 109 #define FT_MIN( a, b ) ( (a) < (b) ? (a) : (b) ) 110 #define FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) ) 111 #define FT_ABS( a ) ( (a) < 0 ? -(a) : (a) ) 205 FT_UNUSED( error ); 206 FT_UNUSED( line ); 207 FT_UNUSED( file ); 208 209 return 0; 210 } 211 212 213 /* we don't handle tracing levels in stand-alone mode; */ 214 #ifndef FT_TRACE5 215 #define FT_TRACE5( varformat ) FT_Message varformat 216 #endif 217 #ifndef FT_TRACE7 218 #define FT_TRACE7( varformat ) FT_Message varformat 219 #endif 220 #ifndef FT_ERROR 221 #define FT_ERROR( varformat ) FT_Message varformat 222 #endif 223 224 #define FT_THROW( e ) \ 225 ( FT_Throw( FT_ERR_CAT( ErrRaster, e ), \ 226 __LINE__, \ 227 __FILE__ ) | \ 228 FT_ERR_CAT( ErrRaster, e ) ) 229 230 #else /* !FT_DEBUG_LEVEL_TRACE */ 231 232 #define FT_TRACE5( x ) do { } while ( 0 ) /* nothing */ 233 #define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ 234 #define FT_ERROR( x ) do { } while ( 0 ) /* nothing */ 235 #define FT_THROW( e ) FT_ERR_CAT( ErrRaster_, e ) 236 237 238 #endif /* !FT_DEBUG_LEVEL_TRACE */ 239 240 241 #define FT_DEFINE_OUTLINE_FUNCS( class_, \ 242 move_to_, line_to_, \ 243 conic_to_, cubic_to_, \ 244 shift_, delta_ ) \ 245 static const FT_Outline_Funcs class_ = \ 246 { \ 247 move_to_, \ 248 line_to_, \ 262 raster_new_, \ 263 raster_reset_, \ 264 raster_set_mode_, \ 265 raster_render_, \ 266 raster_done_ \ 267 }; 268 269 270 #else /* !STANDALONE_ */ 271 272 273 #include <ft2build.h> 274 #include "ftgrays.h" 275 #include FT_INTERNAL_OBJECTS_H 276 #include FT_INTERNAL_DEBUG_H 277 #include FT_INTERNAL_CALC_H 278 #include FT_OUTLINE_H 279 280 #include "ftsmerrs.h" 281 282 #include "ftspic.h" 283 284 #define Smooth_Err_Invalid_Mode Smooth_Err_Cannot_Render_Glyph 285 #define Smooth_Err_Memory_Overflow Smooth_Err_Out_Of_Memory 286 #define ErrRaster_Memory_Overflow Smooth_Err_Out_Of_Memory 287 288 289 #endif /* !STANDALONE_ */ 290 291 292 #ifndef FT_MEM_SET 293 #define FT_MEM_SET( d, s, c ) ft_memset( d, s, c ) 294 #endif 295 296 #ifndef FT_MEM_ZERO 297 #define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count ) 298 #endif 299 300 #ifndef FT_ZERO 301 #define FT_ZERO( p ) FT_MEM_ZERO( p, sizeof ( *(p) ) ) 302 #endif 303 378 (remainder) = (type)( (dividend) - (quotient) * (divisor) ); \ 379 if ( (remainder) < 0 ) \ 380 { \ 381 (quotient)--; \ 382 (remainder) += (type)(divisor); \ 383 } \ 384 FT_END_STMNT 385 #endif /* __arm__ */ 386 387 388 /* These macros speed up repetitive divisions by replacing them */ 389 /* with multiplications and right shifts. */ 390 #define FT_UDIVPREP( c, b ) \ 391 long b ## _r = c ? (long)( FT_ULONG_MAX >> PIXEL_BITS ) / ( b ) \ 392 : 0 393 #define FT_UDIV( a, b ) \ 394 ( ( (unsigned long)( a ) * (unsigned long)( b ## _r ) ) >> \ 395 ( sizeof( long ) * FT_CHAR_BIT - PIXEL_BITS ) ) 396 397 398 /*************************************************************************/ 399 /* */ 400 /* TYPE DEFINITIONS */ 401 /* */ 402 403 /* don't change the following types to FT_Int or FT_Pos, since we might */ 404 /* need to define them to "float" or "double" when experimenting with */ 405 /* new algorithms */ 406 407 typedef long TPos; /* subpixel coordinate */ 408 typedef int TCoord; /* integer scanline/pixel coordinate */ 409 typedef int TArea; /* cell areas, coordinate products */ 410 411 412 typedef struct TCell_* PCell; 413 414 typedef struct TCell_ 415 { 416 TCoord x; /* same with gray_TWorker.ex */ 417 TCoord cover; /* same with gray_TWorker.cover */ 418 TArea area; 419 PCell next; 420 421 } TCell; 499 int y; 500 501 502 for ( y = ras.min_ey; y < ras.max_ey; y++ ) 503 { 504 PCell cell = ras.ycells[y - ras.min_ey]; 505 506 507 printf( "%3d:", y ); 508 509 for ( ; cell != NULL; cell = cell->next ) 510 printf( " (%3d, c:%4d, a:%6d)", 511 cell->x, cell->cover, cell->area ); 512 printf( "\n" ); 513 } 514 } 515 516 #endif /* FT_DEBUG_LEVEL_TRACE */ 517 518 519 /*************************************************************************/ 520 /* */ 521 /* Record the current cell in the table. */ 522 /* */ 523 static void 524 gray_record_cell( RAS_ARG ) 525 { 526 PCell *pcell, cell; 527 TCoord x = ras.ex; 528 529 530 pcell = &ras.ycells[ras.ey - ras.min_ey]; 531 for (;;) 532 { 533 cell = *pcell; 534 if ( !cell || cell->x > x ) 535 break; 536 537 if ( cell->x == x ) 538 goto Found; 539 540 pcell = &cell->next; 541 } 542 544 ft_longjmp( ras.jump_buffer, 1 ); 545 546 /* insert new cell */ 547 cell = ras.cells + ras.num_cells++; 548 cell->x = x; 549 cell->area = ras.area; 550 cell->cover = ras.cover; 551 552 cell->next = *pcell; 553 *pcell = cell; 554 555 return; 556 557 Found: 558 /* update old cell */ 559 cell->area += ras.area; 560 cell->cover += ras.cover; 561 } 562 563 564 /*************************************************************************/ 565 /* */ 566 /* Set the current cell to a new position. */ 567 /* */ 568 static void 569 gray_set_cell( RAS_ARG_ TCoord ex, 570 TCoord ey ) 571 { 572 /* Move the cell pointer to a new position. We set the `invalid' */ 573 /* flag to indicate that the cell isn't part of those we're interested */ 574 /* in during the render phase. This means that: */ 575 /* */ 576 /* . the new vertical position must be within min_ey..max_ey-1. */ 577 /* . the new horizontal position must be strictly less than max_ex */ 578 /* */ 579 /* Note that if a cell is to the left of the clipping region, it is */ 580 /* actually set to the (min_ex-1) horizontal position. */ 581 582 if ( ex < ras.min_ex ) 583 ex = ras.min_ex - 1; 584 585 /* record the current one if it is valid and substantial */ 586 if ( !ras.invalid && ( ras.area || ras.cover ) ) 587 gray_record_cell( RAS_VAR ); 588 589 ras.area = 0; 590 ras.cover = 0; 591 ras.ex = ex; 592 ras.ey = ey; 593 594 ras.invalid = ( ey >= ras.max_ey || ey < ras.min_ey || 595 ex >= ras.max_ex ); 596 } 597 598 599 #ifndef FT_LONG64 600 601 /*************************************************************************/ 602 /* */ 603 /* Render a scanline as one or more cells. */ 604 /* */ 605 static void 606 gray_render_scanline( RAS_ARG_ TCoord ey, 607 TPos x1, 608 TCoord y1, 609 TPos x2, 610 TCoord y2 ) 611 { 612 TCoord ex1, ex2, fx1, fx2, first, dy, delta, mod; 613 TPos p, dx; 614 int incr; 615 616 617 ex1 = TRUNC( x1 ); 618 ex2 = TRUNC( x2 ); 619 620 /* trivial case. Happens often */ 621 if ( y1 == y2 ) 622 { 623 gray_set_cell( RAS_VAR_ ex2, ey ); 624 return; 679 } 680 681 ras.area += (TArea)( ONE_PIXEL * delta ); 682 ras.cover += delta; 683 y1 += delta; 684 ex1 += incr; 685 gray_set_cell( RAS_VAR_ ex1, ey ); 686 } while ( ex1 != ex2 ); 687 } 688 689 fx1 = ONE_PIXEL - first; 690 691 End: 692 dy = y2 - y1; 693 694 ras.area += (TArea)( ( fx1 + fx2 ) * dy ); 695 ras.cover += dy; 696 } 697 698 699 /*************************************************************************/ 700 /* */ 701 /* Render a given line as a series of scanlines. */ 702 /* */ 703 static void 704 gray_render_line( RAS_ARG_ TPos to_x, 705 TPos to_y ) 706 { 707 TCoord ey1, ey2, fy1, fy2, first, delta, mod; 708 TPos p, dx, dy, x, x2; 709 int incr; 710 711 712 ey1 = TRUNC( ras.y ); 713 ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */ 714 715 /* perform vertical clipping */ 716 if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || 717 ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) 718 goto End; 719 720 fy1 = (TCoord)( ras.y - SUBPIXELS( ey1 ) ); 721 fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) ); 722 820 x, ONE_PIXEL - first, 821 x2, first ); 822 x = x2; 823 824 ey1 += incr; 825 gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); 826 } while ( ey1 != ey2 ); 827 } 828 829 gray_render_scanline( RAS_VAR_ ey1, 830 x, ONE_PIXEL - first, 831 to_x, fy2 ); 832 833 End: 834 ras.x = to_x; 835 ras.y = to_y; 836 } 837 838 #else 839 840 /*************************************************************************/ 841 /* */ 842 /* Render a straight line across multiple cells in any direction. */ 843 /* */ 844 static void 845 gray_render_line( RAS_ARG_ TPos to_x, 846 TPos to_y ) 847 { 848 TPos dx, dy, fx1, fy1, fx2, fy2; 849 TCoord ex1, ex2, ey1, ey2; 850 851 852 ey1 = TRUNC( ras.y ); 853 ey2 = TRUNC( to_y ); 854 855 /* perform vertical clipping */ 856 if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || 857 ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) 858 goto End; 859 860 ex1 = TRUNC( ras.x ); 861 ex2 = TRUNC( to_x ); 862 863 fx1 = ras.x - SUBPIXELS( ex1 ); 1313 if ( cover != 0 && cell->x > x ) 1314 gray_hline( RAS_VAR_ x, y, cover, cell->x - x ); 1315 1316 cover += (TArea)cell->cover * ( ONE_PIXEL * 2 ); 1317 area = cover - cell->area; 1318 1319 if ( area != 0 && cell->x >= ras.min_ex ) 1320 gray_hline( RAS_VAR_ cell->x, y, area, 1 ); 1321 1322 x = cell->x + 1; 1323 } 1324 1325 if ( cover != 0 ) 1326 gray_hline( RAS_VAR_ x, y, cover, ras.max_ex - x ); 1327 } 1328 } 1329 1330 1331 #ifdef STANDALONE_ 1332 1333 /*************************************************************************/ 1334 /* */ 1335 /* The following functions should only compile in stand-alone mode, */ 1336 /* i.e., when building this component without the rest of FreeType. */ 1337 /* */ 1338 /*************************************************************************/ 1339 1340 /*************************************************************************/ 1341 /* */ 1342 /* <Function> */ 1343 /* FT_Outline_Decompose */ 1344 /* */ 1345 /* <Description> */ 1346 /* Walk over an outline's structure to decompose it into individual */ 1347 /* segments and Bézier arcs. This function is also able to emit */ 1348 /* `move to' and `close to' operations to indicate the start and end */ 1349 /* of new contours in the outline. */ 1350 /* */ 1351 /* <Input> */ 1352 /* outline :: A pointer to the source target. */ 1353 /* */ 1354 /* func_interface :: A table of `emitters', i.e., function pointers */ 1355 /* called during decomposition to indicate path */ 1356 /* operations. */ 1357 /* */ 1358 /* <InOut> */ 1359 /* user :: A typeless pointer which is passed to each */ 1360 /* emitter during the decomposition. It can be */ 1361 /* used to store the state during the */ 1362 /* decomposition. */ 1363 /* */ 1364 /* <Return> */ 1365 /* Error code. 0 means success. */ 1366 /* */ 1367 static int 1368 FT_Outline_Decompose( const FT_Outline* outline, 1369 const FT_Outline_Funcs* func_interface, 1370 void* user ) 1371 { 1372 #undef SCALED 1373 #define SCALED( x ) ( ( (x) << shift ) - delta ) 1374 1375 FT_Vector v_last; 1376 FT_Vector v_control; 1377 FT_Vector v_start; 1378 1379 FT_Vector* point; 1380 FT_Vector* limit; 1381 char* tags; 1382 1383 int error; 1384 1385 int n; /* index of contour in outline */ 1386 int first; /* index of first point in contour */ 1593 error = func_interface->line_to( &v_start, user ); 1594 1595 Close: 1596 if ( error ) 1597 goto Exit; 1598 1599 first = last + 1; 1600 } 1601 1602 FT_TRACE5(( "FT_Outline_Decompose: Done\n", n )); 1603 return 0; 1604 1605 Exit: 1606 FT_TRACE5(( "FT_Outline_Decompose: Error 0x%x\n", error )); 1607 return error; 1608 1609 Invalid_Outline: 1610 return FT_THROW( Invalid_Outline ); 1611 } 1612 1613 1614 /*************************************************************************/ 1615 /* */ 1616 /* <Function> */ 1617 /* FT_Outline_Get_CBox */ 1618 /* */ 1619 /* <Description> */ 1620 /* Return an outline's `control box'. The control box encloses all */ 1621 /* the outline's points, including Bézier control points. Though it */ 1622 /* coincides with the exact bounding box for most glyphs, it can be */ 1623 /* slightly larger in some situations (like when rotating an outline */ 1624 /* that contains Bézier outside arcs). */ 1625 /* */ 1626 /* Computing the control box is very fast, while getting the bounding */ 1627 /* box can take much more time as it needs to walk over all segments */ 1628 /* and arcs in the outline. To get the latter, you can use the */ 1629 /* `ftbbox' component, which is dedicated to this single task. */ 1630 /* */ 1631 /* <Input> */ 1632 /* outline :: A pointer to the source outline descriptor. */ 1633 /* */ 1634 /* <Output> */ 1635 /* acbox :: The outline's control box. */ 1636 /* */ 1637 /* <Note> */ 1638 /* See @FT_Glyph_Get_CBox for a discussion of tricky fonts. */ 1639 /* */ 1640 1641 static void 1642 FT_Outline_Get_CBox( const FT_Outline* outline, 1643 FT_BBox *acbox ) 1644 { 1645 TPos xMin, yMin, xMax, yMax; 1646 1647 1648 if ( outline && acbox ) 1649 { 1650 if ( outline->n_points == 0 ) 1651 { 1652 xMin = 0; 1653 yMin = 0; 1654 xMax = 0; 1655 yMax = 0; 1656 } 1657 else 1658 { 1659 FT_Vector* vec = outline->points; 1660 FT_Vector* limit = vec + outline->n_points; 1661 1662 1663 xMin = xMax = vec->x; 1664 yMin = yMax = vec->y; 1665 vec++; 1666 1667 for ( ; vec < limit; vec++ ) 1668 { 1669 TPos x, y; 1670 1671 1672 x = vec->x; 1673 if ( x < xMin ) xMin = x; 1674 if ( x > xMax ) xMax = x; 1675 1676 y = vec->y; 1677 if ( y < yMin ) yMin = y; 1678 if ( y > yMax ) yMax = y; 1679 } 1680 } 1681 acbox->xMin = xMin; 1682 acbox->xMax = xMax; 1683 acbox->yMin = yMin; 1684 acbox->yMax = yMax; 1685 } 1686 } 1687 1688 #endif /* STANDALONE_ */ 1689 1690 1691 FT_DEFINE_OUTLINE_FUNCS( 1692 func_interface, 1693 1694 (FT_Outline_MoveTo_Func) gray_move_to, /* move_to */ 1695 (FT_Outline_LineTo_Func) gray_line_to, /* line_to */ 1696 (FT_Outline_ConicTo_Func)gray_conic_to, /* conic_to */ 1697 (FT_Outline_CubicTo_Func)gray_cubic_to, /* cubic_to */ 1698 1699 0, /* shift */ 1700 0 /* delta */ 1701 ) 1702 1703 1704 static int 1705 gray_convert_glyph_inner( RAS_ARG ) 1706 { 1707 1708 volatile int error = 0; 1709 1710 #ifdef FT_CONFIG_OPTION_PIC 1711 FT_Outline_Funcs func_interface; 1712 Init_Class_func_interface(&func_interface); 1713 #endif 1714 1715 if ( ft_setjmp( ras.jump_buffer ) == 0 ) 1716 { 1717 error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras ); 1718 if ( !ras.invalid ) 1719 gray_record_cell( RAS_VAR ); 1720 1721 FT_TRACE7(( "band [%d..%d]: %d cell%s\n", 1722 ras.min_ey, 1723 ras.max_ey, 1724 ras.num_cells, 1725 ras.num_cells == 1 ? "" : "s" )); 1726 } 1727 else 1728 { 1729 error = FT_THROW( Memory_Overflow ); 1730 1731 FT_TRACE7(( "band [%d..%d]: to be bisected\n", 1732 ras.min_ey, ras.max_ey )); 1733 } 1734 1735 return error; 1736 } 1737 1738 1739 static int 1740 gray_convert_glyph( RAS_ARG ) 1741 { 1742 const TCoord yMin = ras.min_ey; 1743 const TCoord yMax = ras.max_ey; 1744 const TCoord xMin = ras.min_ex; 1745 const TCoord xMax = ras.max_ex; 1746 1747 TCell buffer[FT_MAX_GRAY_POOL]; 1748 size_t height = (size_t)( yMax - yMin ); 1749 size_t n = FT_MAX_GRAY_POOL / 8; 1750 TCoord y; 1751 TCoord bands[32]; /* enough to accommodate bisections */ 1752 TCoord* band; 1753 1754 1755 /* set up vertical bands */ 1756 if ( height > n ) 1757 { 1758 /* two divisions rounded up */ 1759 n = ( height + n - 1 ) / n; 1760 height = ( height + n - 1 ) / n; 1761 } 1762 1763 /* memory management */ 1764 n = ( height * sizeof ( PCell ) + sizeof ( TCell ) - 1 ) / sizeof ( TCell ); 1765 1766 ras.cells = buffer + n; 1767 ras.max_cells = (FT_PtrDist)( FT_MAX_GRAY_POOL - n ); 1768 ras.ycells = (PCell*)buffer; 1769 1770 for ( y = yMin; y < yMax; ) 1771 { 1772 ras.min_ey = y; 1773 y += height; 1774 ras.max_ey = FT_MIN( y, yMax ); 1775 1776 band = bands; 1777 band[1] = xMin; 1778 band[0] = xMax; 1779 1780 do 1781 { 1782 TCoord width = band[0] - band[1]; 1783 int error; 1784 1785 1786 FT_MEM_ZERO( ras.ycells, height * sizeof ( PCell ) ); 1787 1788 ras.num_cells = 0; 1789 ras.invalid = 1; 1790 ras.min_ex = band[1]; 1791 ras.max_ex = band[0]; 1792 1793 error = gray_convert_glyph_inner( RAS_VAR ); 1794 1795 if ( !error ) 1796 { 1797 gray_sweep( RAS_VAR ); 1798 band--; 1799 continue; 1800 } 1801 else if ( error != ErrRaster_Memory_Overflow ) 1802 return 1; 1803 1804 /* render pool overflow; we will reduce the render band by half */ 1805 width >>= 1; 1806 1807 /* this should never happen even with tiny rendering pool */ 1808 if ( width == 0 ) 1809 { 1810 FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" )); 1811 return 1; 1812 } 1813 1814 band++; 1815 band[1] = band[0]; 1816 band[0] += width; 1817 } while ( band >= bands ); 1818 } 1819 1820 return 0; 1821 } 1822 1823 1824 static int 1825 gray_raster_render( FT_Raster raster, 1826 const FT_Raster_Params* params ) 1827 { 1828 const FT_Outline* outline = (const FT_Outline*)params->source; 1829 const FT_Bitmap* target_map = params->target; 1830 FT_BBox cbox, clip; 1831 1832 #ifndef FT_STATIC_RASTER 1833 gray_TWorker worker[1]; 1834 #endif 1835 1836 1837 if ( !raster ) 1838 return FT_THROW( Invalid_Argument ); 1839 1840 /* this version does not support monochrome rendering */ 1841 if ( !( params->flags & FT_RASTER_FLAG_AA ) ) 1842 return FT_THROW( Invalid_Mode ); 1843 1844 if ( !outline ) 1845 return FT_THROW( Invalid_Outline ); 1846 1847 /* return immediately if the outline is empty */ 1848 if ( outline->n_points == 0 || outline->n_contours <= 0 ) 1849 return 0; 1850 1873 1874 /* nothing to do */ 1875 if ( !target_map->width || !target_map->rows ) 1876 return 0; 1877 1878 if ( !target_map->buffer ) 1879 return FT_THROW( Invalid_Argument ); 1880 1881 if ( target_map->pitch < 0 ) 1882 ras.target.origin = target_map->buffer; 1883 else 1884 ras.target.origin = target_map->buffer 1885 + ( target_map->rows - 1 ) * (unsigned int)target_map->pitch; 1886 1887 ras.target.pitch = target_map->pitch; 1888 1889 ras.render_span = (FT_Raster_Span_Func)NULL; 1890 ras.render_span_data = NULL; 1891 } 1892 1893 FT_Outline_Get_CBox( outline, &cbox ); 1894 1895 /* reject too large outline coordinates */ 1896 if ( cbox.xMin < -0x1000000L || cbox.xMax > 0x1000000L || 1897 cbox.yMin < -0x1000000L || cbox.yMax > 0x1000000L ) 1898 return FT_THROW( Invalid_Outline ); 1899 1900 /* truncate the bounding box to integer pixels */ 1901 cbox.xMin = cbox.xMin >> 6; 1902 cbox.yMin = cbox.yMin >> 6; 1903 cbox.xMax = ( cbox.xMax + 63 ) >> 6; 1904 cbox.yMax = ( cbox.yMax + 63 ) >> 6; 1905 1906 /* compute clipping box */ 1907 if ( !( params->flags & FT_RASTER_FLAG_DIRECT ) ) 1908 { 1909 /* compute clip box from target pixmap */ 1910 clip.xMin = 0; 1911 clip.yMin = 0; 1912 clip.xMax = (FT_Pos)target_map->width; 1913 clip.yMax = (FT_Pos)target_map->rows; 1914 } 1915 else if ( params->flags & FT_RASTER_FLAG_CLIP ) 1916 clip = params->clip_box; 1917 else 1918 { 1919 clip.xMin = -32768L; 1920 clip.yMin = -32768L; 1921 clip.xMax = 32767L; 1922 clip.yMax = 32767L; 1923 } 1924 1925 /* clip to target bitmap, exit if nothing to do */ 1926 ras.min_ex = FT_MAX( cbox.xMin, clip.xMin ); 1927 ras.min_ey = FT_MAX( cbox.yMin, clip.yMin ); 1928 ras.max_ex = FT_MIN( cbox.xMax, clip.xMax ); 1929 ras.max_ey = FT_MIN( cbox.yMax, clip.yMax ); 1930 1931 if ( ras.max_ex <= ras.min_ex || ras.max_ey <= ras.min_ey ) 1932 return 0; 1933 1934 return gray_convert_glyph( RAS_VAR ); 1935 } 1936 1937 1938 /**** RASTER OBJECT CREATION: In stand-alone mode, we simply use *****/ 1939 /**** a static object. *****/ 1940 1941 #ifdef STANDALONE_ 1942 1943 static int 1944 gray_raster_new( void* memory, 1945 FT_Raster* araster ) 1946 { 1947 static gray_TRaster the_raster; 1948 1949 FT_UNUSED( memory ); | 1 /**************************************************************************** 2 * 3 * ftgrays.c 4 * 5 * A new `perfect' anti-aliasing renderer (body). 6 * 7 * Copyright (C) 2000-2019 by 8 * David Turner, Robert Wilhelm, and Werner Lemberg. 9 * 10 * This file is part of the FreeType project, and may only be used, 11 * modified, and distributed under the terms of the FreeType project 12 * license, LICENSE.TXT. By continuing to use, modify, or distribute 13 * this file you indicate that you have read the license and 14 * understand and accept it fully. 15 * 16 */ 17 18 /************************************************************************** 19 * 20 * This file can be compiled without the rest of the FreeType engine, by 21 * defining the STANDALONE_ macro when compiling it. You also need to 22 * put the files `ftgrays.h' and `ftimage.h' into the current 23 * compilation directory. Typically, you could do something like 24 * 25 * - copy `src/smooth/ftgrays.c' (this file) to your current directory 26 * 27 * - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the 28 * same directory 29 * 30 * - compile `ftgrays' with the STANDALONE_ macro defined, as in 31 * 32 * cc -c -DSTANDALONE_ ftgrays.c 33 * 34 * The renderer can be initialized with a call to 35 * `ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated 36 * with a call to `ft_gray_raster.raster_render'. 37 * 38 * See the comments and documentation in the file `ftimage.h' for more 39 * details on how the raster works. 40 * 41 */ 42 43 /************************************************************************** 44 * 45 * This is a new anti-aliasing scan-converter for FreeType 2. The 46 * algorithm used here is _very_ different from the one in the standard 47 * `ftraster' module. Actually, `ftgrays' computes the _exact_ 48 * coverage of the outline on each pixel cell. 49 * 50 * It is based on ideas that I initially found in Raph Levien's 51 * excellent LibArt graphics library (see https://www.levien.com/libart 52 * for more information, though the web pages do not tell anything 53 * about the renderer; you'll have to dive into the source code to 54 * understand how it works). 55 * 56 * Note, however, that this is a _very_ different implementation 57 * compared to Raph's. Coverage information is stored in a very 58 * different way, and I don't use sorted vector paths. Also, it doesn't 59 * use floating point values. 60 * 61 * This renderer has the following advantages: 62 * 63 * - It doesn't need an intermediate bitmap. Instead, one can supply a 64 * callback function that will be called by the renderer to draw gray 65 * spans on any target surface. You can thus do direct composition on 66 * any kind of bitmap, provided that you give the renderer the right 67 * callback. 68 * 69 * - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on 70 * each pixel cell. 71 * 72 * - It performs a single pass on the outline (the `standard' FT2 73 * renderer makes two passes). 74 * 75 * - It can easily be modified to render to _any_ number of gray levels 76 * cheaply. 77 * 78 * - For small (< 20) pixel sizes, it is faster than the standard 79 * renderer. 80 * 81 */ 82 83 84 /************************************************************************** 85 * 86 * The macro FT_COMPONENT is used in trace mode. It is an implicit 87 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 88 * messages during execution. 89 */ 90 #undef FT_COMPONENT 91 #define FT_COMPONENT smooth 92 93 94 #ifdef STANDALONE_ 95 96 97 /* The size in bytes of the render pool used by the scan-line converter */ 98 /* to do all of its work. */ 99 #define FT_RENDER_POOL_SIZE 16384L 100 101 102 /* Auxiliary macros for token concatenation. */ 103 #define FT_ERR_XCAT( x, y ) x ## y 104 #define FT_ERR_CAT( x, y ) FT_ERR_XCAT( x, y ) 105 106 #define FT_BEGIN_STMNT do { 107 #define FT_END_STMNT } while ( 0 ) 108 109 #define FT_MIN( a, b ) ( (a) < (b) ? (a) : (b) ) 110 #define FT_MAX( a, b ) ( (a) > (b) ? (a) : (b) ) 111 #define FT_ABS( a ) ( (a) < 0 ? -(a) : (a) ) 205 FT_UNUSED( error ); 206 FT_UNUSED( line ); 207 FT_UNUSED( file ); 208 209 return 0; 210 } 211 212 213 /* we don't handle tracing levels in stand-alone mode; */ 214 #ifndef FT_TRACE5 215 #define FT_TRACE5( varformat ) FT_Message varformat 216 #endif 217 #ifndef FT_TRACE7 218 #define FT_TRACE7( varformat ) FT_Message varformat 219 #endif 220 #ifndef FT_ERROR 221 #define FT_ERROR( varformat ) FT_Message varformat 222 #endif 223 224 #define FT_THROW( e ) \ 225 ( FT_Throw( FT_ERR_CAT( ErrRaster_, e ), \ 226 __LINE__, \ 227 __FILE__ ) | \ 228 FT_ERR_CAT( ErrRaster_, e ) ) 229 230 #else /* !FT_DEBUG_LEVEL_TRACE */ 231 232 #define FT_TRACE5( x ) do { } while ( 0 ) /* nothing */ 233 #define FT_TRACE7( x ) do { } while ( 0 ) /* nothing */ 234 #define FT_ERROR( x ) do { } while ( 0 ) /* nothing */ 235 #define FT_THROW( e ) FT_ERR_CAT( ErrRaster_, e ) 236 237 238 #endif /* !FT_DEBUG_LEVEL_TRACE */ 239 240 241 #define FT_DEFINE_OUTLINE_FUNCS( class_, \ 242 move_to_, line_to_, \ 243 conic_to_, cubic_to_, \ 244 shift_, delta_ ) \ 245 static const FT_Outline_Funcs class_ = \ 246 { \ 247 move_to_, \ 248 line_to_, \ 262 raster_new_, \ 263 raster_reset_, \ 264 raster_set_mode_, \ 265 raster_render_, \ 266 raster_done_ \ 267 }; 268 269 270 #else /* !STANDALONE_ */ 271 272 273 #include <ft2build.h> 274 #include "ftgrays.h" 275 #include FT_INTERNAL_OBJECTS_H 276 #include FT_INTERNAL_DEBUG_H 277 #include FT_INTERNAL_CALC_H 278 #include FT_OUTLINE_H 279 280 #include "ftsmerrs.h" 281 282 #define Smooth_Err_Invalid_Mode Smooth_Err_Cannot_Render_Glyph 283 #define Smooth_Err_Memory_Overflow Smooth_Err_Out_Of_Memory 284 #define ErrRaster_Memory_Overflow Smooth_Err_Out_Of_Memory 285 286 287 #endif /* !STANDALONE_ */ 288 289 290 #ifndef FT_MEM_SET 291 #define FT_MEM_SET( d, s, c ) ft_memset( d, s, c ) 292 #endif 293 294 #ifndef FT_MEM_ZERO 295 #define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count ) 296 #endif 297 298 #ifndef FT_ZERO 299 #define FT_ZERO( p ) FT_MEM_ZERO( p, sizeof ( *(p) ) ) 300 #endif 301 376 (remainder) = (type)( (dividend) - (quotient) * (divisor) ); \ 377 if ( (remainder) < 0 ) \ 378 { \ 379 (quotient)--; \ 380 (remainder) += (type)(divisor); \ 381 } \ 382 FT_END_STMNT 383 #endif /* __arm__ */ 384 385 386 /* These macros speed up repetitive divisions by replacing them */ 387 /* with multiplications and right shifts. */ 388 #define FT_UDIVPREP( c, b ) \ 389 long b ## _r = c ? (long)( FT_ULONG_MAX >> PIXEL_BITS ) / ( b ) \ 390 : 0 391 #define FT_UDIV( a, b ) \ 392 ( ( (unsigned long)( a ) * (unsigned long)( b ## _r ) ) >> \ 393 ( sizeof( long ) * FT_CHAR_BIT - PIXEL_BITS ) ) 394 395 396 /************************************************************************** 397 * 398 * TYPE DEFINITIONS 399 */ 400 401 /* don't change the following types to FT_Int or FT_Pos, since we might */ 402 /* need to define them to "float" or "double" when experimenting with */ 403 /* new algorithms */ 404 405 typedef long TPos; /* subpixel coordinate */ 406 typedef int TCoord; /* integer scanline/pixel coordinate */ 407 typedef int TArea; /* cell areas, coordinate products */ 408 409 410 typedef struct TCell_* PCell; 411 412 typedef struct TCell_ 413 { 414 TCoord x; /* same with gray_TWorker.ex */ 415 TCoord cover; /* same with gray_TWorker.cover */ 416 TArea area; 417 PCell next; 418 419 } TCell; 497 int y; 498 499 500 for ( y = ras.min_ey; y < ras.max_ey; y++ ) 501 { 502 PCell cell = ras.ycells[y - ras.min_ey]; 503 504 505 printf( "%3d:", y ); 506 507 for ( ; cell != NULL; cell = cell->next ) 508 printf( " (%3d, c:%4d, a:%6d)", 509 cell->x, cell->cover, cell->area ); 510 printf( "\n" ); 511 } 512 } 513 514 #endif /* FT_DEBUG_LEVEL_TRACE */ 515 516 517 /************************************************************************** 518 * 519 * Record the current cell in the table. 520 */ 521 static void 522 gray_record_cell( RAS_ARG ) 523 { 524 PCell *pcell, cell; 525 TCoord x = ras.ex; 526 527 528 pcell = &ras.ycells[ras.ey - ras.min_ey]; 529 for (;;) 530 { 531 cell = *pcell; 532 if ( !cell || cell->x > x ) 533 break; 534 535 if ( cell->x == x ) 536 goto Found; 537 538 pcell = &cell->next; 539 } 540 542 ft_longjmp( ras.jump_buffer, 1 ); 543 544 /* insert new cell */ 545 cell = ras.cells + ras.num_cells++; 546 cell->x = x; 547 cell->area = ras.area; 548 cell->cover = ras.cover; 549 550 cell->next = *pcell; 551 *pcell = cell; 552 553 return; 554 555 Found: 556 /* update old cell */ 557 cell->area += ras.area; 558 cell->cover += ras.cover; 559 } 560 561 562 /************************************************************************** 563 * 564 * Set the current cell to a new position. 565 */ 566 static void 567 gray_set_cell( RAS_ARG_ TCoord ex, 568 TCoord ey ) 569 { 570 /* Move the cell pointer to a new position. We set the `invalid' */ 571 /* flag to indicate that the cell isn't part of those we're interested */ 572 /* in during the render phase. This means that: */ 573 /* */ 574 /* . the new vertical position must be within min_ey..max_ey-1. */ 575 /* . the new horizontal position must be strictly less than max_ex */ 576 /* */ 577 /* Note that if a cell is to the left of the clipping region, it is */ 578 /* actually set to the (min_ex-1) horizontal position. */ 579 580 if ( ex < ras.min_ex ) 581 ex = ras.min_ex - 1; 582 583 /* record the current one if it is valid and substantial */ 584 if ( !ras.invalid && ( ras.area || ras.cover ) ) 585 gray_record_cell( RAS_VAR ); 586 587 ras.area = 0; 588 ras.cover = 0; 589 ras.ex = ex; 590 ras.ey = ey; 591 592 ras.invalid = ( ey >= ras.max_ey || ey < ras.min_ey || 593 ex >= ras.max_ex ); 594 } 595 596 597 #ifndef FT_LONG64 598 599 /************************************************************************** 600 * 601 * Render a scanline as one or more cells. 602 */ 603 static void 604 gray_render_scanline( RAS_ARG_ TCoord ey, 605 TPos x1, 606 TCoord y1, 607 TPos x2, 608 TCoord y2 ) 609 { 610 TCoord ex1, ex2, fx1, fx2, first, dy, delta, mod; 611 TPos p, dx; 612 int incr; 613 614 615 ex1 = TRUNC( x1 ); 616 ex2 = TRUNC( x2 ); 617 618 /* trivial case. Happens often */ 619 if ( y1 == y2 ) 620 { 621 gray_set_cell( RAS_VAR_ ex2, ey ); 622 return; 677 } 678 679 ras.area += (TArea)( ONE_PIXEL * delta ); 680 ras.cover += delta; 681 y1 += delta; 682 ex1 += incr; 683 gray_set_cell( RAS_VAR_ ex1, ey ); 684 } while ( ex1 != ex2 ); 685 } 686 687 fx1 = ONE_PIXEL - first; 688 689 End: 690 dy = y2 - y1; 691 692 ras.area += (TArea)( ( fx1 + fx2 ) * dy ); 693 ras.cover += dy; 694 } 695 696 697 /************************************************************************** 698 * 699 * Render a given line as a series of scanlines. 700 */ 701 static void 702 gray_render_line( RAS_ARG_ TPos to_x, 703 TPos to_y ) 704 { 705 TCoord ey1, ey2, fy1, fy2, first, delta, mod; 706 TPos p, dx, dy, x, x2; 707 int incr; 708 709 710 ey1 = TRUNC( ras.y ); 711 ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */ 712 713 /* perform vertical clipping */ 714 if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || 715 ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) 716 goto End; 717 718 fy1 = (TCoord)( ras.y - SUBPIXELS( ey1 ) ); 719 fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) ); 720 818 x, ONE_PIXEL - first, 819 x2, first ); 820 x = x2; 821 822 ey1 += incr; 823 gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 ); 824 } while ( ey1 != ey2 ); 825 } 826 827 gray_render_scanline( RAS_VAR_ ey1, 828 x, ONE_PIXEL - first, 829 to_x, fy2 ); 830 831 End: 832 ras.x = to_x; 833 ras.y = to_y; 834 } 835 836 #else 837 838 /************************************************************************** 839 * 840 * Render a straight line across multiple cells in any direction. 841 */ 842 static void 843 gray_render_line( RAS_ARG_ TPos to_x, 844 TPos to_y ) 845 { 846 TPos dx, dy, fx1, fy1, fx2, fy2; 847 TCoord ex1, ex2, ey1, ey2; 848 849 850 ey1 = TRUNC( ras.y ); 851 ey2 = TRUNC( to_y ); 852 853 /* perform vertical clipping */ 854 if ( ( ey1 >= ras.max_ey && ey2 >= ras.max_ey ) || 855 ( ey1 < ras.min_ey && ey2 < ras.min_ey ) ) 856 goto End; 857 858 ex1 = TRUNC( ras.x ); 859 ex2 = TRUNC( to_x ); 860 861 fx1 = ras.x - SUBPIXELS( ex1 ); 1311 if ( cover != 0 && cell->x > x ) 1312 gray_hline( RAS_VAR_ x, y, cover, cell->x - x ); 1313 1314 cover += (TArea)cell->cover * ( ONE_PIXEL * 2 ); 1315 area = cover - cell->area; 1316 1317 if ( area != 0 && cell->x >= ras.min_ex ) 1318 gray_hline( RAS_VAR_ cell->x, y, area, 1 ); 1319 1320 x = cell->x + 1; 1321 } 1322 1323 if ( cover != 0 ) 1324 gray_hline( RAS_VAR_ x, y, cover, ras.max_ex - x ); 1325 } 1326 } 1327 1328 1329 #ifdef STANDALONE_ 1330 1331 /************************************************************************** 1332 * 1333 * The following functions should only compile in stand-alone mode, 1334 * i.e., when building this component without the rest of FreeType. 1335 * 1336 */ 1337 1338 /************************************************************************** 1339 * 1340 * @Function: 1341 * FT_Outline_Decompose 1342 * 1343 * @Description: 1344 * Walk over an outline's structure to decompose it into individual 1345 * segments and Bézier arcs. This function is also able to emit 1346 * `move to' and `close to' operations to indicate the start and end 1347 * of new contours in the outline. 1348 * 1349 * @Input: 1350 * outline :: 1351 * A pointer to the source target. 1352 * 1353 * func_interface :: 1354 * A table of `emitters', i.e., function pointers 1355 * called during decomposition to indicate path 1356 * operations. 1357 * 1358 * @InOut: 1359 * user :: 1360 * A typeless pointer which is passed to each 1361 * emitter during the decomposition. It can be 1362 * used to store the state during the 1363 * decomposition. 1364 * 1365 * @Return: 1366 * Error code. 0 means success. 1367 */ 1368 static int 1369 FT_Outline_Decompose( const FT_Outline* outline, 1370 const FT_Outline_Funcs* func_interface, 1371 void* user ) 1372 { 1373 #undef SCALED 1374 #define SCALED( x ) ( ( (x) << shift ) - delta ) 1375 1376 FT_Vector v_last; 1377 FT_Vector v_control; 1378 FT_Vector v_start; 1379 1380 FT_Vector* point; 1381 FT_Vector* limit; 1382 char* tags; 1383 1384 int error; 1385 1386 int n; /* index of contour in outline */ 1387 int first; /* index of first point in contour */ 1594 error = func_interface->line_to( &v_start, user ); 1595 1596 Close: 1597 if ( error ) 1598 goto Exit; 1599 1600 first = last + 1; 1601 } 1602 1603 FT_TRACE5(( "FT_Outline_Decompose: Done\n", n )); 1604 return 0; 1605 1606 Exit: 1607 FT_TRACE5(( "FT_Outline_Decompose: Error 0x%x\n", error )); 1608 return error; 1609 1610 Invalid_Outline: 1611 return FT_THROW( Invalid_Outline ); 1612 } 1613 1614 #endif /* STANDALONE_ */ 1615 1616 1617 FT_DEFINE_OUTLINE_FUNCS( 1618 func_interface, 1619 1620 (FT_Outline_MoveTo_Func) gray_move_to, /* move_to */ 1621 (FT_Outline_LineTo_Func) gray_line_to, /* line_to */ 1622 (FT_Outline_ConicTo_Func)gray_conic_to, /* conic_to */ 1623 (FT_Outline_CubicTo_Func)gray_cubic_to, /* cubic_to */ 1624 1625 0, /* shift */ 1626 0 /* delta */ 1627 ) 1628 1629 1630 static int 1631 gray_convert_glyph_inner( RAS_ARG, 1632 int continued ) 1633 { 1634 volatile int error = 0; 1635 1636 1637 if ( ft_setjmp( ras.jump_buffer ) == 0 ) 1638 { 1639 if ( continued ) 1640 FT_Trace_Disable(); 1641 error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras ); 1642 if ( continued ) 1643 FT_Trace_Enable(); 1644 1645 if ( !ras.invalid ) 1646 gray_record_cell( RAS_VAR ); 1647 1648 FT_TRACE7(( "band [%d..%d]: %d cell%s\n", 1649 ras.min_ey, 1650 ras.max_ey, 1651 ras.num_cells, 1652 ras.num_cells == 1 ? "" : "s" )); 1653 } 1654 else 1655 { 1656 error = FT_THROW( Memory_Overflow ); 1657 1658 FT_TRACE7(( "band [%d..%d]: to be bisected\n", 1659 ras.min_ey, ras.max_ey )); 1660 } 1661 1662 return error; 1663 } 1664 1665 1666 static int 1667 gray_convert_glyph( RAS_ARG ) 1668 { 1669 const TCoord yMin = ras.min_ey; 1670 const TCoord yMax = ras.max_ey; 1671 1672 TCell buffer[FT_MAX_GRAY_POOL]; 1673 size_t height = (size_t)( yMax - yMin ); 1674 size_t n = FT_MAX_GRAY_POOL / 8; 1675 TCoord y; 1676 TCoord bands[32]; /* enough to accommodate bisections */ 1677 TCoord* band; 1678 1679 int continued = 0; 1680 1681 1682 /* set up vertical bands */ 1683 if ( height > n ) 1684 { 1685 /* two divisions rounded up */ 1686 n = ( height + n - 1 ) / n; 1687 height = ( height + n - 1 ) / n; 1688 } 1689 1690 /* memory management */ 1691 n = ( height * sizeof ( PCell ) + sizeof ( TCell ) - 1 ) / sizeof ( TCell ); 1692 1693 ras.cells = buffer + n; 1694 ras.max_cells = (FT_PtrDist)( FT_MAX_GRAY_POOL - n ); 1695 ras.ycells = (PCell*)buffer; 1696 1697 for ( y = yMin; y < yMax; ) 1698 { 1699 ras.min_ey = y; 1700 y += height; 1701 ras.max_ey = FT_MIN( y, yMax ); 1702 1703 band = bands; 1704 band[1] = ras.min_ey; 1705 band[0] = ras.max_ey; 1706 1707 do 1708 { 1709 TCoord width = band[0] - band[1]; 1710 int error; 1711 1712 1713 FT_MEM_ZERO( ras.ycells, height * sizeof ( PCell ) ); 1714 1715 ras.num_cells = 0; 1716 ras.invalid = 1; 1717 ras.min_ey = band[1]; 1718 ras.max_ey = band[0]; 1719 1720 error = gray_convert_glyph_inner( RAS_VAR, continued ); 1721 continued = 1; 1722 1723 if ( !error ) 1724 { 1725 gray_sweep( RAS_VAR ); 1726 band--; 1727 continue; 1728 } 1729 else if ( error != ErrRaster_Memory_Overflow ) 1730 return 1; 1731 1732 /* render pool overflow; we will reduce the render band by half */ 1733 width >>= 1; 1734 1735 /* this should never happen even with tiny rendering pool */ 1736 if ( width == 0 ) 1737 { 1738 FT_TRACE7(( "gray_convert_glyph: rotten glyph\n" )); 1739 return 1; 1740 } 1741 1742 band++; 1743 band[1] = band[0]; 1744 band[0] += width; 1745 } while ( band >= bands ); 1746 } 1747 1748 return 0; 1749 } 1750 1751 1752 static int 1753 gray_raster_render( FT_Raster raster, 1754 const FT_Raster_Params* params ) 1755 { 1756 const FT_Outline* outline = (const FT_Outline*)params->source; 1757 const FT_Bitmap* target_map = params->target; 1758 FT_BBox clip; 1759 1760 #ifndef FT_STATIC_RASTER 1761 gray_TWorker worker[1]; 1762 #endif 1763 1764 1765 if ( !raster ) 1766 return FT_THROW( Invalid_Argument ); 1767 1768 /* this version does not support monochrome rendering */ 1769 if ( !( params->flags & FT_RASTER_FLAG_AA ) ) 1770 return FT_THROW( Invalid_Mode ); 1771 1772 if ( !outline ) 1773 return FT_THROW( Invalid_Outline ); 1774 1775 /* return immediately if the outline is empty */ 1776 if ( outline->n_points == 0 || outline->n_contours <= 0 ) 1777 return 0; 1778 1801 1802 /* nothing to do */ 1803 if ( !target_map->width || !target_map->rows ) 1804 return 0; 1805 1806 if ( !target_map->buffer ) 1807 return FT_THROW( Invalid_Argument ); 1808 1809 if ( target_map->pitch < 0 ) 1810 ras.target.origin = target_map->buffer; 1811 else 1812 ras.target.origin = target_map->buffer 1813 + ( target_map->rows - 1 ) * (unsigned int)target_map->pitch; 1814 1815 ras.target.pitch = target_map->pitch; 1816 1817 ras.render_span = (FT_Raster_Span_Func)NULL; 1818 ras.render_span_data = NULL; 1819 } 1820 1821 /* compute clipping box */ 1822 if ( params->flags & FT_RASTER_FLAG_DIRECT && 1823 params->flags & FT_RASTER_FLAG_CLIP ) 1824 clip = params->clip_box; 1825 else 1826 { 1827 /* compute clip box from target pixmap */ 1828 clip.xMin = 0; 1829 clip.yMin = 0; 1830 clip.xMax = (FT_Pos)target_map->width; 1831 clip.yMax = (FT_Pos)target_map->rows; 1832 } 1833 1834 /* clip to target bitmap, exit if nothing to do */ 1835 ras.min_ex = clip.xMin; 1836 ras.min_ey = clip.yMin; 1837 ras.max_ex = clip.xMax; 1838 ras.max_ey = clip.yMax; 1839 1840 if ( ras.max_ex <= ras.min_ex || ras.max_ey <= ras.min_ey ) 1841 return 0; 1842 1843 return gray_convert_glyph( RAS_VAR ); 1844 } 1845 1846 1847 /**** RASTER OBJECT CREATION: In stand-alone mode, we simply use *****/ 1848 /**** a static object. *****/ 1849 1850 #ifdef STANDALONE_ 1851 1852 static int 1853 gray_raster_new( void* memory, 1854 FT_Raster* araster ) 1855 { 1856 static gray_TRaster the_raster; 1857 1858 FT_UNUSED( memory ); |