-/***************************************************************************/
-/* */
-/* ftgrays.c */
-/* */
-/* A new `perfect' anti-aliasing renderer (body). */
-/* */
-/* Copyright 2000-2001, 2002, 2003, 2005, 2006, 2007 by */
-/* David Turner, Robert Wilhelm, and Werner Lemberg. */
-/* */
-/* This file is part of the FreeType project, and may only be used, */
-/* modified, and distributed under the terms of the FreeType project */
-/* license, LICENSE.TXT. By continuing to use, modify, or distribute */
-/* this file you indicate that you have read the license and */
-/* understand and accept it fully. */
-/* */
-/***************************************************************************/
-
- /*************************************************************************/
- /* */
- /* This file can be compiled without the rest of the FreeType engine, by */
- /* defining the _STANDALONE_ macro when compiling it. You also need to */
- /* put the files `ftgrays.h' and `ftimage.h' into the current */
- /* compilation directory. Typically, you could do something like */
- /* */
- /* - copy `src/smooth/ftgrays.c' (this file) to your current directory */
- /* */
- /* - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the */
- /* same directory */
- /* */
- /* - compile `ftgrays' with the _STANDALONE_ macro defined, as in */
- /* */
- /* cc -c -D_STANDALONE_ ftgrays.c */
- /* */
- /* The renderer can be initialized with a call to */
- /* `ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated */
- /* with a call to `ft_gray_raster.raster_render'. */
- /* */
- /* See the comments and documentation in the file `ftimage.h' for more */
- /* details on how the raster works. */
- /* */
- /*************************************************************************/
-
- /*************************************************************************/
- /* */
- /* This is a new anti-aliasing scan-converter for FreeType 2. The */
- /* algorithm used here is _very_ different from the one in the standard */
- /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */
- /* coverage of the outline on each pixel cell. */
- /* */
- /* It is based on ideas that I initially found in Raph Levien's */
- /* excellent LibArt graphics library (see http://www.levien.com/libart */
- /* for more information, though the web pages do not tell anything */
- /* about the renderer; you'll have to dive into the source code to */
- /* understand how it works). */
- /* */
- /* Note, however, that this is a _very_ different implementation */
- /* compared to Raph's. Coverage information is stored in a very */
- /* different way, and I don't use sorted vector paths. Also, it doesn't */
- /* use floating point values. */
- /* */
- /* This renderer has the following advantages: */
- /* */
- /* - It doesn't need an intermediate bitmap. Instead, one can supply a */
- /* callback function that will be called by the renderer to draw gray */
- /* spans on any target surface. You can thus do direct composition on */
- /* any kind of bitmap, provided that you give the renderer the right */
- /* callback. */
- /* */
- /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */
- /* each pixel cell. */
- /* */
- /* - It performs a single pass on the outline (the `standard' FT2 */
- /* renderer makes two passes). */
- /* */
- /* - It can easily be modified to render to _any_ number of gray levels */
- /* cheaply. */
- /* */
- /* - For small (< 20) pixel sizes, it is faster than the standard */
- /* renderer. */
- /* */
- /*************************************************************************/
-
-
- /*************************************************************************/
- /* */
- /* The macro FT_COMPONENT is used in trace mode. It is an implicit */
- /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */
- /* messages during execution. */
- /* */
-#undef FT_COMPONENT
-#define FT_COMPONENT trace_smooth
-
-
-
-
-#ifdef _STANDALONE_
-
-#include <string.h> /* for ft_memcpy() */
-#include <setjmp.h>
-#include <limits.h>
-#define FT_UINT_MAX UINT_MAX
-
-#define ft_memset memset
-
-#define ft_setjmp setjmp
-#define ft_longjmp longjmp
-#define ft_jmp_buf jmp_buf
-
-
-#define ErrRaster_Invalid_Mode -2
-#define ErrRaster_Invalid_Outline -1
-#define ErrRaster_Invalid_Argument -3
-#define ErrRaster_Memory_Overflow -4
-
-#define FT_BEGIN_HEADER
-#define FT_END_HEADER
-
-#include "ftimage.h"
-#include "ftgrays.h"
-
- /* This macro is used to indicate that a function parameter is unused. */
- /* Its purpose is simply to reduce compiler warnings. Note also that */
- /* simply defining it as `(void)x' doesn't avoid warnings with certain */
- /* ANSI compilers (e.g. LCC). */
-#define FT_UNUSED( x ) (x) = (x)
-
- /* Disable the tracing mechanism for simplicity -- developers can */
- /* activate it easily by redefining these two macros. */
-#ifndef FT_ERROR
-#define FT_ERROR( x ) do ; while ( 0 ) /* nothing */
-#endif
-
-#ifndef FT_TRACE
-#define FT_TRACE( x ) do ; while ( 0 ) /* nothing */
-#endif
-
-#else /* !_STANDALONE_ */
-
-#include <ft2build.h>
-#include "ftgrays.h"
-#include FT_INTERNAL_OBJECTS_H
-#include FT_INTERNAL_DEBUG_H
-#include FT_OUTLINE_H
-
-#include "ftsmerrs.h"
-
-#define ErrRaster_Invalid_Mode Smooth_Err_Cannot_Render_Glyph
-#define ErrRaster_Invalid_Outline Smooth_Err_Invalid_Outline
-#define ErrRaster_Memory_Overflow Smooth_Err_Out_Of_Memory
-#define ErrRaster_Invalid_Argument Smooth_Err_Bad_Argument
-
-#endif /* !_STANDALONE_ */
-
-
-#ifndef FT_MEM_SET
-#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )
-#endif
-
-#ifndef FT_MEM_ZERO
-#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )
-#endif
-
- /* define this to dump debugging information */
-#define xxxDEBUG_GRAYS
-
-
- /* as usual, for the speed hungry :-) */
-
-#ifndef FT_STATIC_RASTER
-
-
-#define RAS_ARG PWorker worker
-#define RAS_ARG_ PWorker worker,
-
-#define RAS_VAR worker
-#define RAS_VAR_ worker,
-
-#define ras (*worker)
-
-
-#else /* FT_STATIC_RASTER */
-
-
-#define RAS_ARG /* empty */
-#define RAS_ARG_ /* empty */
-#define RAS_VAR /* empty */
-#define RAS_VAR_ /* empty */
-
- static TWorker ras;
-
-
-#endif /* FT_STATIC_RASTER */
-
-
- /* must be at least 6 bits! */
-#define PIXEL_BITS 8
-
-#define ONE_PIXEL ( 1L << PIXEL_BITS )
-#define PIXEL_MASK ( -1L << PIXEL_BITS )
-#define TRUNC( x ) ( (TCoord)( (x) >> PIXEL_BITS ) )
-#define SUBPIXELS( x ) ( (TPos)(x) << PIXEL_BITS )
-#define FLOOR( x ) ( (x) & -ONE_PIXEL )
-#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL )
-#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL )
-
-#if PIXEL_BITS >= 6
-#define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) )
-#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) )
-#else
-#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) )
-#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) )
-#endif
-
-
- /*************************************************************************/
- /* */
- /* TYPE DEFINITIONS */
- /* */
-
- /* don't change the following types to FT_Int or FT_Pos, since we might */
- /* need to define them to "float" or "double" when experimenting with */
- /* new algorithms */
-
- typedef int TCoord; /* integer scanline/pixel coordinate */
- typedef long TPos; /* sub-pixel coordinate */
-
- /* determine the type used to store cell areas. This normally takes at */
- /* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */
- /* `long' instead of `int', otherwise bad things happen */
-
-#if PIXEL_BITS <= 7
-
- typedef int TArea;
-
-#else /* PIXEL_BITS >= 8 */
-
- /* approximately determine the size of integers using an ANSI-C header */
-#if FT_UINT_MAX == 0xFFFFU
- typedef long TArea;
-#else
- typedef int TArea;
-#endif
-
-#endif /* PIXEL_BITS >= 8 */
-
-
- /* maximal number of gray spans in a call to the span callback */
-#define FT_MAX_GRAY_SPANS 32
-
-
- typedef struct TCell_* PCell;
-
- typedef struct TCell_
- {
- int x;
- int cover;
- TArea area;
- PCell next;
-
- } TCell;
-
-
- typedef struct TWorker_
- {
- TCoord ex, ey;
- TPos min_ex, max_ex;
- TPos min_ey, max_ey;
- TPos count_ex, count_ey;
-
- TArea area;
- int cover;
- int invalid;
-
- PCell cells;
- int max_cells;
- int num_cells;
-
- TCoord cx, cy;
- TPos x, y;
-
- TPos last_ey;
-
- FT_Vector bez_stack[32 * 3 + 1];
- int lev_stack[32];
-
- FT_Outline outline;
- FT_Bitmap target;
- FT_BBox clip_box;
-
- FT_Span gray_spans[FT_MAX_GRAY_SPANS];
- int num_gray_spans;
-
- FT_Raster_Span_Func render_span;
- void* render_span_data;
- int span_y;
-
- int band_size;
- int band_shoot;
- int conic_level;
- int cubic_level;
-
- ft_jmp_buf jump_buffer;
-
- void* buffer;
- long buffer_size;
-
- PCell* ycells;
- int ycount;
-
- } TWorker, *PWorker;
-
-
- typedef struct TRaster_
- {
- void* buffer;
- long buffer_size;
- int band_size;
- void* memory;
- PWorker worker;
-
- } TRaster, *PRaster;
-
-
-
- /*************************************************************************/
- /* */
- /* Initialize the cells table. */
- /* */
- static void
- gray_init_cells( RAS_ARG_ void* buffer,
- long byte_size )
- {
- ras.buffer = buffer;
- ras.buffer_size = byte_size;
-
- ras.ycells = (PCell*) buffer;
- ras.cells = NULL;
- ras.max_cells = 0;
- ras.num_cells = 0;
- ras.area = 0;
- ras.cover = 0;
- ras.invalid = 1;
- }
-
-
- /*************************************************************************/
- /* */
- /* Compute the outline bounding box. */
- /* */
- static void
- gray_compute_cbox( RAS_ARG )
- {
- FT_Outline* outline = &ras.outline;
- FT_Vector* vec = outline->points;
- FT_Vector* limit = vec + outline->n_points;
-
-
- if ( outline->n_points <= 0 )
- {
- ras.min_ex = ras.max_ex = 0;
- ras.min_ey = ras.max_ey = 0;
- return;
- }
-
- ras.min_ex = ras.max_ex = vec->x;
- ras.min_ey = ras.max_ey = vec->y;
-
- vec++;
-
- for ( ; vec < limit; vec++ )
- {
- TPos x = vec->x;
- TPos y = vec->y;
-
-
- if ( x < ras.min_ex ) ras.min_ex = x;
- if ( x > ras.max_ex ) ras.max_ex = x;
- if ( y < ras.min_ey ) ras.min_ey = y;
- if ( y > ras.max_ey ) ras.max_ey = y;
- }
-
- /* truncate the bounding box to integer pixels */
- ras.min_ex = ras.min_ex >> 6;
- ras.min_ey = ras.min_ey >> 6;
- ras.max_ex = ( ras.max_ex + 63 ) >> 6;
- ras.max_ey = ( ras.max_ey + 63 ) >> 6;
- }
-
-
- /*************************************************************************/
- /* */
- /* Record the current cell in the table. */
- /* */
- static PCell
- gray_find_cell( RAS_ARG )
- {
- PCell *pcell, cell;
- int x = ras.ex;
-
-
- if ( x > ras.max_ex )
- x = ras.max_ex;
-
- pcell = &ras.ycells[ras.ey];
- for (;;)
- {
- cell = *pcell;
- if ( cell == NULL || cell->x > x )
- break;
-
- if ( cell->x == x )
- goto Exit;
-
- pcell = &cell->next;
- }
-
- if ( ras.num_cells >= ras.max_cells )
- ft_longjmp( ras.jump_buffer, 1 );
-
- cell = ras.cells + ras.num_cells++;
- cell->x = x;
- cell->area = 0;
- cell->cover = 0;
-
- cell->next = *pcell;
- *pcell = cell;
-
- Exit:
- return cell;
- }
-
-
- static void
- gray_record_cell( RAS_ARG )
- {
- if ( !ras.invalid && ( ras.area | ras.cover ) )
- {
- PCell cell = gray_find_cell( RAS_VAR );
-
-
- cell->area += ras.area;
- cell->cover += ras.cover;
- }
- }
-
-
- /*************************************************************************/
- /* */
- /* Set the current cell to a new position. */
- /* */
- static void
- gray_set_cell( RAS_ARG_ TCoord ex,
- TCoord ey )
- {
- /* Move the cell pointer to a new position. We set the `invalid' */
- /* flag to indicate that the cell isn't part of those we're interested */
- /* in during the render phase. This means that: */
- /* */
- /* . the new vertical position must be within min_ey..max_ey-1. */
- /* . the new horizontal position must be strictly less than max_ex */
- /* */
- /* Note that if a cell is to the left of the clipping region, it is */
- /* actually set to the (min_ex-1) horizontal position. */
-
- /* All cells that are on the left of the clipping region go to the */
- /* min_ex - 1 horizontal position. */
- ey -= ras.min_ey;
-
- if ( ex > ras.max_ex )
- ex = ras.max_ex;
-
- ex -= ras.min_ex;
- if ( ex < 0 )
- ex = -1;
-
- /* are we moving to a different cell ? */
- if ( ex != ras.ex || ey != ras.ey )
- {
- /* record the current one if it is valid */
- if ( !ras.invalid )
- gray_record_cell( RAS_VAR );
-
- ras.area = 0;
- ras.cover = 0;
- }
-
- ras.ex = ex;
- ras.ey = ey;
- ras.invalid = ( (unsigned)ey >= (unsigned)ras.count_ey ||
- ex >= ras.count_ex );
- }
-
-
- /*************************************************************************/
- /* */
- /* Start a new contour at a given cell. */
- /* */
- static void
- gray_start_cell( RAS_ARG_ TCoord ex,
- TCoord ey )
- {
- if ( ex > ras.max_ex )
- ex = (TCoord)( ras.max_ex );
-
- if ( ex < ras.min_ex )
- ex = (TCoord)( ras.min_ex - 1 );
-
- ras.area = 0;
- ras.cover = 0;
- ras.ex = ex - ras.min_ex;
- ras.ey = ey - ras.min_ey;
- ras.last_ey = SUBPIXELS( ey );
- ras.invalid = 0;
-
- gray_set_cell( RAS_VAR_ ex, ey );
- }
-
-
- /*************************************************************************/
- /* */
- /* Render a scanline as one or more cells. */
- /* */
- static void
- gray_render_scanline( RAS_ARG_ TCoord ey,
- TPos x1,
- TCoord y1,
- TPos x2,
- TCoord y2 )
- {
- TCoord ex1, ex2, fx1, fx2, delta;
- long p, first, dx;
- int incr, lift, mod, rem;
-
-
- dx = x2 - x1;
-
- ex1 = TRUNC( x1 );
- ex2 = TRUNC( x2 );
- fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) );
- fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) );
-
- /* trivial case. Happens often */
- if ( y1 == y2 )
- {
- gray_set_cell( RAS_VAR_ ex2, ey );
- return;
- }
-
- /* everything is located in a single cell. That is easy! */
- /* */
- if ( ex1 == ex2 )
- {
- delta = y2 - y1;
- ras.area += (TArea)( fx1 + fx2 ) * delta;
- ras.cover += delta;
- return;
- }
-
- /* ok, we'll have to render a run of adjacent cells on the same */
- /* scanline... */
- /* */
- p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 );
- first = ONE_PIXEL;
- incr = 1;
-
- if ( dx < 0 )
- {
- p = fx1 * ( y2 - y1 );
- first = 0;
- incr = -1;
- dx = -dx;
- }
-
- delta = (TCoord)( p / dx );
- mod = (TCoord)( p % dx );
- if ( mod < 0 )
- {
- delta--;
- mod += (TCoord)dx;
- }
-
- ras.area += (TArea)( fx1 + first ) * delta;
- ras.cover += delta;
-
- ex1 += incr;
- gray_set_cell( RAS_VAR_ ex1, ey );
- y1 += delta;
-
- if ( ex1 != ex2 )
- {
- p = ONE_PIXEL * ( y2 - y1 + delta );
- lift = (TCoord)( p / dx );
- rem = (TCoord)( p % dx );
- if ( rem < 0 )
- {
- lift--;
- rem += (TCoord)dx;
- }
-
- mod -= (int)dx;
-
- while ( ex1 != ex2 )
- {
- delta = lift;
- mod += rem;
- if ( mod >= 0 )
- {
- mod -= (TCoord)dx;
- delta++;
- }
-
- ras.area += (TArea)ONE_PIXEL * delta;
- ras.cover += delta;
- y1 += delta;
- ex1 += incr;
- gray_set_cell( RAS_VAR_ ex1, ey );
- }
- }
-
- delta = y2 - y1;
- ras.area += (TArea)( fx2 + ONE_PIXEL - first ) * delta;
- ras.cover += delta;
- }
-
-
- /*************************************************************************/
- /* */
- /* Render a given line as a series of scanlines. */
- /* */
- static void
- gray_render_line( RAS_ARG_ TPos to_x,
- TPos to_y )
- {
- TCoord ey1, ey2, fy1, fy2;
- TPos dx, dy, x, x2;
- long p, first;
- int delta, rem, mod, lift, incr;
-
-
- ey1 = TRUNC( ras.last_ey );
- ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */
- fy1 = (TCoord)( ras.y - ras.last_ey );
- fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) );
-
- dx = to_x - ras.x;
- dy = to_y - ras.y;
-
- /* XXX: we should do something about the trivial case where dx == 0, */
- /* as it happens very often! */
-
- /* perform vertical clipping */
- {
- TCoord min, max;
-
-
- min = ey1;
- max = ey2;
- if ( ey1 > ey2 )
- {
- min = ey2;
- max = ey1;
- }
- if ( min >= ras.max_ey || max < ras.min_ey )
- goto End;
- }
-
- /* everything is on a single scanline */
- if ( ey1 == ey2 )
- {
- gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 );
- goto End;
- }
-
- /* vertical line - avoid calling gray_render_scanline */
- incr = 1;
-
- if ( dx == 0 )
- {
- TCoord ex = TRUNC( ras.x );
- TCoord two_fx = (TCoord)( ( ras.x - SUBPIXELS( ex ) ) << 1 );
- TPos area;
-
-
- first = ONE_PIXEL;
- if ( dy < 0 )
- {
- first = 0;
- incr = -1;
- }
-
- delta = (int)( first - fy1 );
- ras.area += (TArea)two_fx * delta;
- ras.cover += delta;
- ey1 += incr;
-
- gray_set_cell( &ras, ex, ey1 );
-
- delta = (int)( first + first - ONE_PIXEL );
- area = (TArea)two_fx * delta;
- while ( ey1 != ey2 )
- {
- ras.area += area;
- ras.cover += delta;
- ey1 += incr;
-
- gray_set_cell( &ras, ex, ey1 );
- }
-
- delta = (int)( fy2 - ONE_PIXEL + first );
- ras.area += (TArea)two_fx * delta;
- ras.cover += delta;
-
- goto End;
- }
-
- /* ok, we have to render several scanlines */
- p = ( ONE_PIXEL - fy1 ) * dx;
- first = ONE_PIXEL;
- incr = 1;
-
- if ( dy < 0 )
- {
- p = fy1 * dx;
- first = 0;
- incr = -1;
- dy = -dy;
- }
-
- delta = (int)( p / dy );
- mod = (int)( p % dy );
- if ( mod < 0 )
- {
- delta--;
- mod += (TCoord)dy;
- }
-
- x = ras.x + delta;
- gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, (TCoord)first );
-
- ey1 += incr;
- gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );
-
- if ( ey1 != ey2 )
- {
- p = ONE_PIXEL * dx;
- lift = (int)( p / dy );
- rem = (int)( p % dy );
- if ( rem < 0 )
- {
- lift--;
- rem += (int)dy;
- }
- mod -= (int)dy;
-
- while ( ey1 != ey2 )
- {
- delta = lift;
- mod += rem;
- if ( mod >= 0 )
- {
- mod -= (int)dy;
- delta++;
- }
-
- x2 = x + delta;
- gray_render_scanline( RAS_VAR_ ey1, x,
- (TCoord)( ONE_PIXEL - first ), x2,
- (TCoord)first );
- x = x2;
-
- ey1 += incr;
- gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );
- }
- }
-
- gray_render_scanline( RAS_VAR_ ey1, x,
- (TCoord)( ONE_PIXEL - first ), to_x,
- fy2 );
-
- End:
- ras.x = to_x;
- ras.y = to_y;
- ras.last_ey = SUBPIXELS( ey2 );
- }
-
-
- static void
- gray_split_conic( FT_Vector* base )
- {
- TPos a, b;
-
-
- base[4].x = base[2].x;
- b = base[1].x;
- a = base[3].x = ( base[2].x + b ) / 2;
- b = base[1].x = ( base[0].x + b ) / 2;
- base[2].x = ( a + b ) / 2;
-
- base[4].y = base[2].y;
- b = base[1].y;
- a = base[3].y = ( base[2].y + b ) / 2;
- b = base[1].y = ( base[0].y + b ) / 2;
- base[2].y = ( a + b ) / 2;
- }
-
-
- static void
- gray_render_conic( RAS_ARG_ const FT_Vector* control,
- const FT_Vector* to )
- {
- TPos dx, dy;
- int top, level;
- int* levels;
- FT_Vector* arc;
-
-
- dx = DOWNSCALE( ras.x ) + to->x - ( control->x << 1 );
- if ( dx < 0 )
- dx = -dx;
- dy = DOWNSCALE( ras.y ) + to->y - ( control->y << 1 );
- if ( dy < 0 )
- dy = -dy;
- if ( dx < dy )
- dx = dy;
-
- level = 1;
- dx = dx / ras.conic_level;
- while ( dx > 0 )
- {
- dx >>= 2;
- level++;
- }
-
- /* a shortcut to speed things up */
- if ( level <= 1 )
- {
- /* we compute the mid-point directly in order to avoid */
- /* calling gray_split_conic() */
- TPos to_x, to_y, mid_x, mid_y;
-
-
- to_x = UPSCALE( to->x );
- to_y = UPSCALE( to->y );
- mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4;
- mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4;
-
- gray_render_line( RAS_VAR_ mid_x, mid_y );
- gray_render_line( RAS_VAR_ to_x, to_y );
-
- return;
- }
-
- arc = ras.bez_stack;
- levels = ras.lev_stack;
- top = 0;
- levels[0] = level;
-
- arc[0].x = UPSCALE( to->x );
- arc[0].y = UPSCALE( to->y );
- arc[1].x = UPSCALE( control->x );
- arc[1].y = UPSCALE( control->y );
- arc[2].x = ras.x;
- arc[2].y = ras.y;
-
- while ( top >= 0 )
- {
- level = levels[top];
- if ( level > 1 )
- {
- /* check that the arc crosses the current band */
- TPos min, max, y;
-
-
- min = max = arc[0].y;
-
- y = arc[1].y;
- if ( y < min ) min = y;
- if ( y > max ) max = y;
-
- y = arc[2].y;
- if ( y < min ) min = y;
- if ( y > max ) max = y;
-
- if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey )
- goto Draw;
-
- gray_split_conic( arc );
- arc += 2;
- top++;
- levels[top] = levels[top - 1] = level - 1;
- continue;
- }
-
- Draw:
- {
- TPos to_x, to_y, mid_x, mid_y;
-
-
- to_x = arc[0].x;
- to_y = arc[0].y;
- mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4;
- mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4;
-
- gray_render_line( RAS_VAR_ mid_x, mid_y );
- gray_render_line( RAS_VAR_ to_x, to_y );
-
- top--;
- arc -= 2;
- }
- }
-
- return;
- }
-
-
- static void
- gray_split_cubic( FT_Vector* base )
- {
- TPos a, b, c, d;
-
-
- base[6].x = base[3].x;
- c = base[1].x;
- d = base[2].x;
- base[1].x = a = ( base[0].x + c ) / 2;
- base[5].x = b = ( base[3].x + d ) / 2;
- c = ( c + d ) / 2;
- base[2].x = a = ( a + c ) / 2;
- base[4].x = b = ( b + c ) / 2;
- base[3].x = ( a + b ) / 2;
-
- base[6].y = base[3].y;
- c = base[1].y;
- d = base[2].y;
- base[1].y = a = ( base[0].y + c ) / 2;
- base[5].y = b = ( base[3].y + d ) / 2;
- c = ( c + d ) / 2;
- base[2].y = a = ( a + c ) / 2;
- base[4].y = b = ( b + c ) / 2;
- base[3].y = ( a + b ) / 2;
- }
-
-
- static void
- gray_render_cubic( RAS_ARG_ const FT_Vector* control1,
- const FT_Vector* control2,
- const FT_Vector* to )
- {
- TPos dx, dy, da, db;
- int top, level;
- int* levels;
- FT_Vector* arc;
-
-
- dx = DOWNSCALE( ras.x ) + to->x - ( control1->x << 1 );
- if ( dx < 0 )
- dx = -dx;
- dy = DOWNSCALE( ras.y ) + to->y - ( control1->y << 1 );
- if ( dy < 0 )
- dy = -dy;
- if ( dx < dy )
- dx = dy;
- da = dx;
-
- dx = DOWNSCALE( ras.x ) + to->x - 3 * ( control1->x + control2->x );
- if ( dx < 0 )
- dx = -dx;
- dy = DOWNSCALE( ras.y ) + to->y - 3 * ( control1->x + control2->y );
- if ( dy < 0 )
- dy = -dy;
- if ( dx < dy )
- dx = dy;
- db = dx;
-
- level = 1;
- da = da / ras.cubic_level;
- db = db / ras.conic_level;
- while ( da > 0 || db > 0 )
- {
- da >>= 2;
- db >>= 3;
- level++;
- }
-
- if ( level <= 1 )
- {
- TPos to_x, to_y, mid_x, mid_y;
-
-
- to_x = UPSCALE( to->x );
- to_y = UPSCALE( to->y );
- mid_x = ( ras.x + to_x +
- 3 * UPSCALE( control1->x + control2->x ) ) / 8;
- mid_y = ( ras.y + to_y +
- 3 * UPSCALE( control1->y + control2->y ) ) / 8;
-
- gray_render_line( RAS_VAR_ mid_x, mid_y );
- gray_render_line( RAS_VAR_ to_x, to_y );
- return;
- }
-
- arc = ras.bez_stack;
- arc[0].x = UPSCALE( to->x );
- arc[0].y = UPSCALE( to->y );
- arc[1].x = UPSCALE( control2->x );
- arc[1].y = UPSCALE( control2->y );
- arc[2].x = UPSCALE( control1->x );
- arc[2].y = UPSCALE( control1->y );
- arc[3].x = ras.x;
- arc[3].y = ras.y;
-
- levels = ras.lev_stack;
- top = 0;
- levels[0] = level;
-
- while ( top >= 0 )
- {
- level = levels[top];
- if ( level > 1 )
- {
- /* check that the arc crosses the current band */
- TPos min, max, y;
-
-
- min = max = arc[0].y;
- y = arc[1].y;
- if ( y < min ) min = y;
- if ( y > max ) max = y;
- y = arc[2].y;
- if ( y < min ) min = y;
- if ( y > max ) max = y;
- y = arc[3].y;
- if ( y < min ) min = y;
- if ( y > max ) max = y;
- if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )
- goto Draw;
- gray_split_cubic( arc );
- arc += 3;
- top ++;
- levels[top] = levels[top - 1] = level - 1;
- continue;
- }
-
- Draw:
- {
- TPos to_x, to_y, mid_x, mid_y;
-
-
- to_x = arc[0].x;
- to_y = arc[0].y;
- mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8;
- mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8;
-
- gray_render_line( RAS_VAR_ mid_x, mid_y );
- gray_render_line( RAS_VAR_ to_x, to_y );
- top --;
- arc -= 3;
- }
- }
-
- return;
- }
-
-
-
- static int
- gray_move_to( const FT_Vector* to,
- PWorker worker )
- {
- TPos x, y;
-
-
- /* record current cell, if any */
- gray_record_cell( worker );
-
- /* start to a new position */
- x = UPSCALE( to->x );
- y = UPSCALE( to->y );
-
- gray_start_cell( worker, TRUNC( x ), TRUNC( y ) );
-
- worker->x = x;
- worker->y = y;
- return 0;
- }
-
-
- static int
- gray_line_to( const FT_Vector* to,
- PWorker worker )
- {
- gray_render_line( worker, UPSCALE( to->x ), UPSCALE( to->y ) );
- return 0;
- }
-
-
- static int
- gray_conic_to( const FT_Vector* control,
- const FT_Vector* to,
- PWorker worker )
- {
- gray_render_conic( worker, control, to );
- return 0;
- }
-
-
- static int
- gray_cubic_to( const FT_Vector* control1,
- const FT_Vector* control2,
- const FT_Vector* to,
- PWorker worker )
- {
- gray_render_cubic( worker, control1, control2, to );
- return 0;
- }
-
-
- static void
- gray_render_span( int y,
- int count,
- const FT_Span* spans,
- PWorker worker )
- {
- unsigned char* p;
- FT_Bitmap* map = &worker->target;
-
-
- /* first of all, compute the scanline offset */
- p = (unsigned char*)map->buffer - y * map->pitch;
- if ( map->pitch >= 0 )
- p += ( map->rows - 1 ) * map->pitch;
-
- for ( ; count > 0; count--, spans++ )
- {
- unsigned char coverage = spans->coverage;
-
-
- if ( coverage )
- {
- /* For small-spans it is faster to do it by ourselves than
- * calling `memset'. This is mainly due to the cost of the
- * function call.
- */
- if ( spans->len >= 8 )
- FT_MEM_SET( p + spans->x, (unsigned char)coverage, spans->len );
- else
- {
- unsigned char* q = p + spans->x;
-
-
- switch ( spans->len )
- {
- case 7: *q++ = (unsigned char)coverage;
- case 6: *q++ = (unsigned char)coverage;
- case 5: *q++ = (unsigned char)coverage;
- case 4: *q++ = (unsigned char)coverage;
- case 3: *q++ = (unsigned char)coverage;
- case 2: *q++ = (unsigned char)coverage;
- case 1: *q = (unsigned char)coverage;
- default:
- ;
- }
- }
- }
- }
- }
-
-
- static void
- gray_hline( RAS_ARG_ TCoord x,
- TCoord y,
- TPos area,
- int acount )
- {
- FT_Span* span;
- int count;
- int coverage;
-
-
- /* compute the coverage line's coverage, depending on the */
- /* outline fill rule */
- /* */
- /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */
- /* */
- coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) );
- /* use range 0..256 */
- if ( coverage < 0 )
- coverage = -coverage;
-
- if ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL )
- {
- coverage &= 511;
-
- if ( coverage > 256 )
- coverage = 512 - coverage;
- else if ( coverage == 256 )
- coverage = 255;
- }
- else
- {
- /* normal non-zero winding rule */
- if ( coverage >= 256 )
- coverage = 255;
- }
-
- y += (TCoord)ras.min_ey;
- x += (TCoord)ras.min_ex;
-
- /* FT_Span.x is a 16-bit short, so limit our coordinates appropriately */
- if ( x >= 32768 )
- x = 32767;
-
- if ( coverage )
- {
- /* see whether we can add this span to the current list */
- count = ras.num_gray_spans;
- span = ras.gray_spans + count - 1;
- if ( count > 0 &&
- ras.span_y == y &&
- (int)span->x + span->len == (int)x &&
- span->coverage == coverage )
- {
- span->len = (unsigned short)( span->len + acount );
- return;
- }
-
- if ( ras.span_y != y || count >= FT_MAX_GRAY_SPANS )
- {
- if ( ras.render_span && count > 0 )
- ras.render_span( ras.span_y, count, ras.gray_spans,
- ras.render_span_data );
- /* ras.render_span( span->y, ras.gray_spans, count ); */
-
-#ifdef DEBUG_GRAYS
-
- if ( ras.span_y >= 0 )
- {
- int n;
-
-
- fprintf( stderr, "y=%3d ", ras.span_y );
- span = ras.gray_spans;
- for ( n = 0; n < count; n++, span++ )
- fprintf( stderr, "[%d..%d]:%02x ",
- span->x, span->x + span->len - 1, span->coverage );
- fprintf( stderr, "\n" );
- }
-
-#endif /* DEBUG_GRAYS */
-
- ras.num_gray_spans = 0;
- ras.span_y = y;
-
- count = 0;
- span = ras.gray_spans;
- }
- else
- span++;
-
- /* add a gray span to the current list */
- span->x = (short)x;
- span->len = (unsigned short)acount;
- span->coverage = (unsigned char)coverage;
-
- ras.num_gray_spans++;
- }
- }
-
-
-#ifdef DEBUG_GRAYS
-
- /* to be called while in the debugger */
- gray_dump_cells( RAS_ARG )
- {
- int yindex;
-
-
- for ( yindex = 0; yindex < ras.ycount; yindex++ )
- {
- PCell cell;
-
-
- printf( "%3d:", yindex );
-
- for ( cell = ras.ycells[yindex]; cell != NULL; cell = cell->next )
- printf( " (%3d, c:%4d, a:%6d)", cell->x, cell->cover, cell->area );
- printf( "\n" );
- }
- }
-
-#endif /* DEBUG_GRAYS */
-
-
- static void
- gray_sweep( RAS_ARG_ const FT_Bitmap* target )
- {
- int yindex;
-
- FT_UNUSED( target );
-
-
- if ( ras.num_cells == 0 )
- return;
-
- ras.num_gray_spans = 0;
-
- for ( yindex = 0; yindex < ras.ycount; yindex++ )
- {
- PCell cell = ras.ycells[yindex];
- TCoord cover = 0;
- TCoord x = 0;
-
-
- for ( ; cell != NULL; cell = cell->next )
- {
- TArea area;
-
-
- if ( cell->x > x && cover != 0 )
- gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
- cell->x - x );
-
- cover += cell->cover;
- area = cover * ( ONE_PIXEL * 2 ) - cell->area;
-
- if ( area != 0 && cell->x >= 0 )
- gray_hline( RAS_VAR_ cell->x, yindex, area, 1 );
-
- x = cell->x + 1;
- }
-
- if ( cover != 0 )
- gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),
- ras.count_ex - x );
- }
-
- if ( ras.render_span && ras.num_gray_spans > 0 )
- ras.render_span( ras.span_y, ras.num_gray_spans,
- ras.gray_spans, ras.render_span_data );
- }
-
-
-#ifdef _STANDALONE_
-
- /*************************************************************************/
- /* */
- /* The following function should only compile in stand_alone mode, */
- /* i.e., when building this component without the rest of FreeType. */
- /* */
- /*************************************************************************/
-
- /*************************************************************************/
- /* */
- /* <Function> */
- /* FT_Outline_Decompose */
- /* */
- /* <Description> */
- /* Walks over an outline's structure to decompose it into individual */
- /* segments and Bezier arcs. This function is also able to emit */
- /* `move to' and `close to' operations to indicate the start and end */
- /* of new contours in the outline. */
- /* */
- /* <Input> */
- /* outline :: A pointer to the source target. */
- /* */
- /* func_interface :: A table of `emitters', i.e,. function pointers */
- /* called during decomposition to indicate path */
- /* operations. */
- /* */
- /* user :: A typeless pointer which is passed to each */
- /* emitter during the decomposition. It can be */
- /* used to store the state during the */
- /* decomposition. */
- /* */
- /* <Return> */
- /* Error code. 0 means success. */
- /* */
- static
- int FT_Outline_Decompose( const FT_Outline* outline,
- const FT_Outline_Funcs* func_interface,
- void* user )
- {
-#undef SCALED
-#if 0
-#define SCALED( x ) ( ( (x) << shift ) - delta )
-#else
-#define SCALED( x ) (x)
-#endif
-
- FT_Vector v_last;
- FT_Vector v_control;
- FT_Vector v_start;
-
- FT_Vector* point;
- FT_Vector* limit;
- char* tags;
-
- int n; /* index of contour in outline */
- int first; /* index of first point in contour */
- int error;
- char tag; /* current point's state */
-
-#if 0
- int shift = func_interface->shift;
- TPos delta = func_interface->delta;
-#endif
-
-
- first = 0;
-
- for ( n = 0; n < outline->n_contours; n++ )
- {
- int last; /* index of last point in contour */
-
-
- last = outline->contours[n];
- limit = outline->points + last;
-
- v_start = outline->points[first];
- v_last = outline->points[last];
-
- v_start.x = SCALED( v_start.x );
- v_start.y = SCALED( v_start.y );
-
- v_last.x = SCALED( v_last.x );
- v_last.y = SCALED( v_last.y );
-
- v_control = v_start;
-
- point = outline->points + first;
- tags = outline->tags + first;
- tag = FT_CURVE_TAG( tags[0] );
-
- /* A contour cannot start with a cubic control point! */
- if ( tag == FT_CURVE_TAG_CUBIC )
- goto Invalid_Outline;
-
- /* check first point to determine origin */
- if ( tag == FT_CURVE_TAG_CONIC )
- {
- /* first point is conic control. Yes, this happens. */
- if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )
- {
- /* start at last point if it is on the curve */
- v_start = v_last;
- limit--;
- }
- else
- {
- /* if both first and last points are conic, */
- /* start at their middle and record its position */
- /* for closure */
- v_start.x = ( v_start.x + v_last.x ) / 2;
- v_start.y = ( v_start.y + v_last.y ) / 2;
-
- v_last = v_start;
- }
- point--;
- tags--;
- }
-
- error = func_interface->move_to( &v_start, user );
- if ( error )
- goto Exit;
-
- while ( point < limit )
- {
- point++;
- tags++;
-
- tag = FT_CURVE_TAG( tags[0] );
- switch ( tag )
- {
- case FT_CURVE_TAG_ON: /* emit a single line_to */
- {
- FT_Vector vec;
-
-
- vec.x = SCALED( point->x );
- vec.y = SCALED( point->y );
-
- error = func_interface->line_to( &vec, user );
- if ( error )
- goto Exit;
- continue;
- }
-
- case FT_CURVE_TAG_CONIC: /* consume conic arcs */
- {
- v_control.x = SCALED( point->x );
- v_control.y = SCALED( point->y );
-
- Do_Conic:
- if ( point < limit )
- {
- FT_Vector vec;
- FT_Vector v_middle;
-
-
- point++;
- tags++;
- tag = FT_CURVE_TAG( tags[0] );
-
- vec.x = SCALED( point->x );
- vec.y = SCALED( point->y );
-
- if ( tag == FT_CURVE_TAG_ON )
- {
- error = func_interface->conic_to( &v_control, &vec,
- user );
- if ( error )
- goto Exit;
- continue;
- }
-
- if ( tag != FT_CURVE_TAG_CONIC )
- goto Invalid_Outline;
-
- v_middle.x = ( v_control.x + vec.x ) / 2;
- v_middle.y = ( v_control.y + vec.y ) / 2;
-
- error = func_interface->conic_to( &v_control, &v_middle,
- user );
- if ( error )
- goto Exit;
-
- v_control = vec;
- goto Do_Conic;
- }
-
- error = func_interface->conic_to( &v_control, &v_start,
- user );
- goto Close;
- }
-
- default: /* FT_CURVE_TAG_CUBIC */
- {
- FT_Vector vec1, vec2;
-
-
- if ( point + 1 > limit ||
- FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )
- goto Invalid_Outline;
-
- point += 2;
- tags += 2;
-
- vec1.x = SCALED( point[-2].x );
- vec1.y = SCALED( point[-2].y );
-
- vec2.x = SCALED( point[-1].x );
- vec2.y = SCALED( point[-1].y );
-
- if ( point <= limit )
- {
- FT_Vector vec;
-
-
- vec.x = SCALED( point->x );
- vec.y = SCALED( point->y );
-
- error = func_interface->cubic_to( &vec1, &vec2, &vec, user );
- if ( error )
- goto Exit;
- continue;
- }
-
- error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );
- goto Close;
- }
- }
- }
-
- /* close the contour with a line segment */
- error = func_interface->line_to( &v_start, user );
-
- Close:
- if ( error )
- goto Exit;
-
- first = last + 1;
- }
-
- return 0;
-
- Exit:
- return error;
-
- Invalid_Outline:
- return ErrRaster_Invalid_Outline;
- }
-
-#endif /* _STANDALONE_ */
-
-
- typedef struct TBand_
- {
- TPos min, max;
-
- } TBand;
-
-
- static int
- gray_convert_glyph_inner( RAS_ARG )
- {
- static
- const FT_Outline_Funcs func_interface =
- {
- (FT_Outline_MoveTo_Func) gray_move_to,
- (FT_Outline_LineTo_Func) gray_line_to,
- (FT_Outline_ConicTo_Func)gray_conic_to,
- (FT_Outline_CubicTo_Func)gray_cubic_to,
- 0,
- 0
- };
-
- volatile int error = 0;
-
- if ( ft_setjmp( ras.jump_buffer ) == 0 )
- {
- error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras );
- gray_record_cell( RAS_VAR );
- }
- else
- {
- error = ErrRaster_Memory_Overflow;
- }
-
- return error;
- }
-
-
- static int
- gray_convert_glyph( RAS_ARG )
- {
- TBand bands[40];
- TBand* volatile band;
- int volatile n, num_bands;
- TPos volatile min, max, max_y;
- FT_BBox* clip;
-
-
- /* Set up state in the raster object */
- gray_compute_cbox( RAS_VAR );
-
- /* clip to target bitmap, exit if nothing to do */
- clip = &ras.clip_box;
-
- if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax ||
- ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax )
- return 0;
-
- if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin;
- if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin;
-
- if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax;
- if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax;
-
- ras.count_ex = ras.max_ex - ras.min_ex;
- ras.count_ey = ras.max_ey - ras.min_ey;
-
- /* simple heuristic used to speed-up the bezier decomposition -- see */
- /* the code in gray_render_conic() and gray_render_cubic() for more */
- /* details */
- ras.conic_level = 32;
- ras.cubic_level = 16;
-
- {
- int level = 0;
-
-
- if ( ras.count_ex > 24 || ras.count_ey > 24 )
- level++;
- if ( ras.count_ex > 120 || ras.count_ey > 120 )
- level++;
-
- ras.conic_level <<= level;
- ras.cubic_level <<= level;
- }
-
- /* setup vertical bands */
- num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size );
- if ( num_bands == 0 ) num_bands = 1;
- if ( num_bands >= 39 ) num_bands = 39;
-
- ras.band_shoot = 0;
-
- min = ras.min_ey;
- max_y = ras.max_ey;
-
- for ( n = 0; n < num_bands; n++, min = max )
- {
- max = min + ras.band_size;
- if ( n == num_bands - 1 || max > max_y )
- max = max_y;
-
- bands[0].min = min;
- bands[0].max = max;
- band = bands;
-
- while ( band >= bands )
- {
- TPos bottom, top, middle;
- int error;
-
- {
- PCell cells_max;
- int yindex;
- long cell_start, cell_end, cell_mod;
-
-
- ras.ycells = (PCell*)ras.buffer;
- ras.ycount = band->max - band->min;
-
- cell_start = sizeof ( PCell ) * ras.ycount;
- cell_mod = cell_start % sizeof ( TCell );
- if ( cell_mod > 0 )
- cell_start += sizeof ( TCell ) - cell_mod;
-
- cell_end = ras.buffer_size;
- cell_end -= cell_end % sizeof( TCell );
-
- cells_max = (PCell)( (char*)ras.buffer + cell_end );
- ras.cells = (PCell)( (char*)ras.buffer + cell_start );
- if ( ras.cells >= cells_max )
- goto ReduceBands;
-
- ras.max_cells = cells_max - ras.cells;
- if ( ras.max_cells < 2 )
- goto ReduceBands;
-
- for ( yindex = 0; yindex < ras.ycount; yindex++ )
- ras.ycells[yindex] = NULL;
- }
-
- ras.num_cells = 0;
- ras.invalid = 1;
- ras.min_ey = band->min;
- ras.max_ey = band->max;
- ras.count_ey = band->max - band->min;
-
- error = gray_convert_glyph_inner( RAS_VAR );
-
- if ( !error )
- {
- gray_sweep( RAS_VAR_ &ras.target );
- band--;
- continue;
- }
- else if ( error != ErrRaster_Memory_Overflow )
- return 1;
-
- ReduceBands:
- /* render pool overflow; we will reduce the render band by half */
- bottom = band->min;
- top = band->max;
- middle = bottom + ( ( top - bottom ) >> 1 );
-
- /* This is too complex for a single scanline; there must */
- /* be some problems. */
- if ( middle == bottom )
- {
-#ifdef DEBUG_GRAYS
- fprintf( stderr, "Rotten glyph!\n" );
-#endif
- return 1;
- }
-
- if ( bottom-top >= ras.band_size )
- ras.band_shoot++;
-
- band[1].min = bottom;
- band[1].max = middle;
- band[0].min = middle;
- band[0].max = top;
- band++;
- }
- }
-
- if ( ras.band_shoot > 8 && ras.band_size > 16 )
- ras.band_size = ras.band_size / 2;
-
- return 0;
- }
-
-
- static int
- gray_raster_render( PRaster raster,
- const FT_Raster_Params* params )
- {
- const FT_Outline* outline = (const FT_Outline*)params->source;
- const FT_Bitmap* target_map = params->target;
- PWorker worker;
-
-
- if ( !raster || !raster->buffer || !raster->buffer_size )
- return ErrRaster_Invalid_Argument;
-
- /* return immediately if the outline is empty */
- if ( outline->n_points == 0 || outline->n_contours <= 0 )
- return 0;
-
- if ( !outline || !outline->contours || !outline->points )
- return ErrRaster_Invalid_Outline;
-
- if ( outline->n_points !=
- outline->contours[outline->n_contours - 1] + 1 )
- return ErrRaster_Invalid_Outline;
-
- worker = raster->worker;
-
- /* if direct mode is not set, we must have a target bitmap */
- if ( ( params->flags & FT_RASTER_FLAG_DIRECT ) == 0 )
- {
- if ( !target_map )
- return ErrRaster_Invalid_Argument;
-
- /* nothing to do */
- if ( !target_map->width || !target_map->rows )
- return 0;
-
- if ( !target_map->buffer )
- return ErrRaster_Invalid_Argument;
- }
-
- /* this version does not support monochrome rendering */
- if ( !( params->flags & FT_RASTER_FLAG_AA ) )
- return ErrRaster_Invalid_Mode;
-
- /* compute clipping box */
- if ( ( params->flags & FT_RASTER_FLAG_DIRECT ) == 0 )
- {
- /* compute clip box from target pixmap */
- ras.clip_box.xMin = 0;
- ras.clip_box.yMin = 0;
- ras.clip_box.xMax = target_map->width;
- ras.clip_box.yMax = target_map->rows;
- }
- else if ( params->flags & FT_RASTER_FLAG_CLIP )
- {
- ras.clip_box = params->clip_box;
- }
- else
- {
- ras.clip_box.xMin = -32768L;
- ras.clip_box.yMin = -32768L;
- ras.clip_box.xMax = 32767L;
- ras.clip_box.yMax = 32767L;
- }
-
- gray_init_cells( worker, raster->buffer, raster->buffer_size );
-
- ras.outline = *outline;
- ras.num_cells = 0;
- ras.invalid = 1;
- ras.band_size = raster->band_size;
- ras.num_gray_spans = 0;
-
- if ( target_map )
- ras.target = *target_map;
-
- ras.render_span = (FT_Raster_Span_Func)gray_render_span;
- ras.render_span_data = &ras;
-
- if ( params->flags & FT_RASTER_FLAG_DIRECT )
- {
- ras.render_span = (FT_Raster_Span_Func)params->gray_spans;
- ras.render_span_data = params->user;
- }
-
- return gray_convert_glyph( worker );
- }
-
-
- /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/
- /**** a static object. *****/
-
-#ifdef _STANDALONE_
-
- static int
- gray_raster_new( void* memory,
- FT_Raster* araster )
- {
- static TRaster the_raster;
-
- FT_UNUSED( memory );
-
-
- *araster = (FT_Raster)&the_raster;
- FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );
-
- return 0;
- }
-
-
- static void
- gray_raster_done( FT_Raster raster )
- {
- /* nothing */
- FT_UNUSED( raster );
- }
-
-#else /* _STANDALONE_ */
-
- static int
- gray_raster_new( FT_Memory memory,
- FT_Raster* araster )
- {
- FT_Error error;
- PRaster raster;
-
-
- *araster = 0;
- if ( !FT_ALLOC( raster, sizeof ( TRaster ) ) )
- {
- raster->memory = memory;
- *araster = (FT_Raster)raster;
- }
-
- return error;
- }
-
-
- static void
- gray_raster_done( FT_Raster raster )
- {
- FT_Memory memory = (FT_Memory)((PRaster)raster)->memory;
-
-
- FT_FREE( raster );
- }
-
-#endif /* _STANDALONE_ */
-
-
- static void
- gray_raster_reset( FT_Raster raster,
- char* pool_base,
- long pool_size )
- {
- PRaster rast = (PRaster)raster;
-
-
- if ( raster )
- {
- if ( pool_base && pool_size >= (long)sizeof ( TWorker ) + 2048 )
- {
- PWorker worker = (PWorker)pool_base;
-
-
- rast->worker = worker;
- rast->buffer = pool_base +
- ( ( sizeof ( TWorker ) + sizeof ( TCell ) - 1 ) &
- ~( sizeof ( TCell ) - 1 ) );
- rast->buffer_size = (long)( ( pool_base + pool_size ) -
- (char*)rast->buffer ) &
- ~( sizeof ( TCell ) - 1 );
- rast->band_size = (int)( rast->buffer_size /
- ( sizeof ( TCell ) * 8 ) );
- }
- else
- {
- rast->buffer = NULL;
- rast->buffer_size = 0;
- rast->worker = NULL;
- }
- }
- }
-
-
- const FT_Raster_Funcs ft_grays_raster =
- {
- FT_GLYPH_FORMAT_OUTLINE,
-
- (FT_Raster_New_Func) gray_raster_new,
- (FT_Raster_Reset_Func) gray_raster_reset,
- (FT_Raster_Set_Mode_Func)0,
- (FT_Raster_Render_Func) gray_raster_render,
- (FT_Raster_Done_Func) gray_raster_done
- };
-
-
-/* END */
+/***************************************************************************/\r
+/* */\r
+/* ftgrays.c */\r
+/* */\r
+/* A new `perfect' anti-aliasing renderer (body). */\r
+/* */\r
+/* Copyright 2000-2001, 2002, 2003, 2005, 2006, 2007 by */\r
+/* David Turner, Robert Wilhelm, and Werner Lemberg. */\r
+/* */\r
+/* This file is part of the FreeType project, and may only be used, */\r
+/* modified, and distributed under the terms of the FreeType project */\r
+/* license, LICENSE.TXT. By continuing to use, modify, or distribute */\r
+/* this file you indicate that you have read the license and */\r
+/* understand and accept it fully. */\r
+/* */\r
+/***************************************************************************/\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* This file can be compiled without the rest of the FreeType engine, by */\r
+ /* defining the _STANDALONE_ macro when compiling it. You also need to */\r
+ /* put the files `ftgrays.h' and `ftimage.h' into the current */\r
+ /* compilation directory. Typically, you could do something like */\r
+ /* */\r
+ /* - copy `src/smooth/ftgrays.c' (this file) to your current directory */\r
+ /* */\r
+ /* - copy `include/freetype/ftimage.h' and `src/smooth/ftgrays.h' to the */\r
+ /* same directory */\r
+ /* */\r
+ /* - compile `ftgrays' with the _STANDALONE_ macro defined, as in */\r
+ /* */\r
+ /* cc -c -D_STANDALONE_ ftgrays.c */\r
+ /* */\r
+ /* The renderer can be initialized with a call to */\r
+ /* `ft_gray_raster.raster_new'; an anti-aliased bitmap can be generated */\r
+ /* with a call to `ft_gray_raster.raster_render'. */\r
+ /* */\r
+ /* See the comments and documentation in the file `ftimage.h' for more */\r
+ /* details on how the raster works. */\r
+ /* */\r
+ /*************************************************************************/\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* This is a new anti-aliasing scan-converter for FreeType 2. The */\r
+ /* algorithm used here is _very_ different from the one in the standard */\r
+ /* `ftraster' module. Actually, `ftgrays' computes the _exact_ */\r
+ /* coverage of the outline on each pixel cell. */\r
+ /* */\r
+ /* It is based on ideas that I initially found in Raph Levien's */\r
+ /* excellent LibArt graphics library (see http://www.levien.com/libart */\r
+ /* for more information, though the web pages do not tell anything */\r
+ /* about the renderer; you'll have to dive into the source code to */\r
+ /* understand how it works). */\r
+ /* */\r
+ /* Note, however, that this is a _very_ different implementation */\r
+ /* compared to Raph's. Coverage information is stored in a very */\r
+ /* different way, and I don't use sorted vector paths. Also, it doesn't */\r
+ /* use floating point values. */\r
+ /* */\r
+ /* This renderer has the following advantages: */\r
+ /* */\r
+ /* - It doesn't need an intermediate bitmap. Instead, one can supply a */\r
+ /* callback function that will be called by the renderer to draw gray */\r
+ /* spans on any target surface. You can thus do direct composition on */\r
+ /* any kind of bitmap, provided that you give the renderer the right */\r
+ /* callback. */\r
+ /* */\r
+ /* - A perfect anti-aliaser, i.e., it computes the _exact_ coverage on */\r
+ /* each pixel cell. */\r
+ /* */\r
+ /* - It performs a single pass on the outline (the `standard' FT2 */\r
+ /* renderer makes two passes). */\r
+ /* */\r
+ /* - It can easily be modified to render to _any_ number of gray levels */\r
+ /* cheaply. */\r
+ /* */\r
+ /* - For small (< 20) pixel sizes, it is faster than the standard */\r
+ /* renderer. */\r
+ /* */\r
+ /*************************************************************************/\r
+\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */\r
+ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */\r
+ /* messages during execution. */\r
+ /* */\r
+#undef FT_COMPONENT\r
+#define FT_COMPONENT trace_smooth\r
+\r
+\r
+\r
+\r
+#ifdef _STANDALONE_\r
+\r
+#include <string.h> /* for ft_memcpy() */\r
+#include <setjmp.h>\r
+#include <limits.h>\r
+#define FT_UINT_MAX UINT_MAX\r
+\r
+#define ft_memset memset\r
+\r
+#define ft_setjmp setjmp\r
+#define ft_longjmp longjmp\r
+#define ft_jmp_buf jmp_buf\r
+\r
+\r
+#define ErrRaster_Invalid_Mode -2\r
+#define ErrRaster_Invalid_Outline -1\r
+#define ErrRaster_Invalid_Argument -3\r
+#define ErrRaster_Memory_Overflow -4\r
+\r
+#define FT_BEGIN_HEADER\r
+#define FT_END_HEADER\r
+\r
+#include "ftimage.h"\r
+#include "ftgrays.h"\r
+\r
+ /* This macro is used to indicate that a function parameter is unused. */\r
+ /* Its purpose is simply to reduce compiler warnings. Note also that */\r
+ /* simply defining it as `(void)x' doesn't avoid warnings with certain */\r
+ /* ANSI compilers (e.g. LCC). */\r
+#define FT_UNUSED( x ) (x) = (x)\r
+\r
+ /* Disable the tracing mechanism for simplicity -- developers can */\r
+ /* activate it easily by redefining these two macros. */\r
+#ifndef FT_ERROR\r
+#define FT_ERROR( x ) do ; while ( 0 ) /* nothing */\r
+#endif\r
+\r
+#ifndef FT_TRACE\r
+#define FT_TRACE( x ) do ; while ( 0 ) /* nothing */\r
+#endif\r
+\r
+#else /* !_STANDALONE_ */\r
+\r
+#include <ft2build.h>\r
+#include "ftgrays.h"\r
+#include FT_INTERNAL_OBJECTS_H\r
+#include FT_INTERNAL_DEBUG_H\r
+#include FT_OUTLINE_H\r
+\r
+#include "ftsmerrs.h"\r
+\r
+#define ErrRaster_Invalid_Mode Smooth_Err_Cannot_Render_Glyph\r
+#define ErrRaster_Invalid_Outline Smooth_Err_Invalid_Outline\r
+#define ErrRaster_Memory_Overflow Smooth_Err_Out_Of_Memory\r
+#define ErrRaster_Invalid_Argument Smooth_Err_Bad_Argument\r
+\r
+#endif /* !_STANDALONE_ */\r
+\r
+\r
+#ifndef FT_MEM_SET\r
+#define FT_MEM_SET( d, s, c ) ft_memset( d, s, c )\r
+#endif\r
+\r
+#ifndef FT_MEM_ZERO\r
+#define FT_MEM_ZERO( dest, count ) FT_MEM_SET( dest, 0, count )\r
+#endif\r
+\r
+ /* define this to dump debugging information */\r
+#define xxxDEBUG_GRAYS\r
+\r
+\r
+ /* as usual, for the speed hungry :-) */\r
+\r
+#ifndef FT_STATIC_RASTER\r
+\r
+\r
+#define RAS_ARG PWorker worker\r
+#define RAS_ARG_ PWorker worker,\r
+\r
+#define RAS_VAR worker\r
+#define RAS_VAR_ worker,\r
+\r
+#define ras (*worker)\r
+\r
+\r
+#else /* FT_STATIC_RASTER */\r
+\r
+\r
+#define RAS_ARG /* empty */\r
+#define RAS_ARG_ /* empty */\r
+#define RAS_VAR /* empty */\r
+#define RAS_VAR_ /* empty */\r
+\r
+ static TWorker ras;\r
+\r
+\r
+#endif /* FT_STATIC_RASTER */\r
+\r
+\r
+ /* must be at least 6 bits! */\r
+#define PIXEL_BITS 8\r
+\r
+#define ONE_PIXEL ( 1L << PIXEL_BITS )\r
+#define PIXEL_MASK ( -1L << PIXEL_BITS )\r
+#define TRUNC( x ) ( (TCoord)( (x) >> PIXEL_BITS ) )\r
+#define SUBPIXELS( x ) ( (TPos)(x) << PIXEL_BITS )\r
+#define FLOOR( x ) ( (x) & -ONE_PIXEL )\r
+#define CEILING( x ) ( ( (x) + ONE_PIXEL - 1 ) & -ONE_PIXEL )\r
+#define ROUND( x ) ( ( (x) + ONE_PIXEL / 2 ) & -ONE_PIXEL )\r
+\r
+#if PIXEL_BITS >= 6\r
+#define UPSCALE( x ) ( (x) << ( PIXEL_BITS - 6 ) )\r
+#define DOWNSCALE( x ) ( (x) >> ( PIXEL_BITS - 6 ) )\r
+#else\r
+#define UPSCALE( x ) ( (x) >> ( 6 - PIXEL_BITS ) )\r
+#define DOWNSCALE( x ) ( (x) << ( 6 - PIXEL_BITS ) )\r
+#endif\r
+\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* TYPE DEFINITIONS */\r
+ /* */\r
+\r
+ /* don't change the following types to FT_Int or FT_Pos, since we might */\r
+ /* need to define them to "float" or "double" when experimenting with */\r
+ /* new algorithms */\r
+\r
+ typedef int TCoord; /* integer scanline/pixel coordinate */\r
+ typedef long TPos; /* sub-pixel coordinate */\r
+\r
+ /* determine the type used to store cell areas. This normally takes at */\r
+ /* least PIXEL_BITS*2 + 1 bits. On 16-bit systems, we need to use */\r
+ /* `long' instead of `int', otherwise bad things happen */\r
+\r
+#if PIXEL_BITS <= 7\r
+\r
+ typedef int TArea;\r
+\r
+#else /* PIXEL_BITS >= 8 */\r
+\r
+ /* approximately determine the size of integers using an ANSI-C header */\r
+#if FT_UINT_MAX == 0xFFFFU\r
+ typedef long TArea;\r
+#else\r
+ typedef int TArea;\r
+#endif\r
+\r
+#endif /* PIXEL_BITS >= 8 */\r
+\r
+\r
+ /* maximal number of gray spans in a call to the span callback */\r
+#define FT_MAX_GRAY_SPANS 32\r
+\r
+\r
+ typedef struct TCell_* PCell;\r
+\r
+ typedef struct TCell_\r
+ {\r
+ int x;\r
+ int cover;\r
+ TArea area;\r
+ PCell next;\r
+\r
+ } TCell;\r
+\r
+\r
+ typedef struct TWorker_\r
+ {\r
+ TCoord ex, ey;\r
+ TPos min_ex, max_ex;\r
+ TPos min_ey, max_ey;\r
+ TPos count_ex, count_ey;\r
+\r
+ TArea area;\r
+ int cover;\r
+ int invalid;\r
+\r
+ PCell cells;\r
+ int max_cells;\r
+ int num_cells;\r
+\r
+ TCoord cx, cy;\r
+ TPos x, y;\r
+\r
+ TPos last_ey;\r
+\r
+ FT_Vector bez_stack[32 * 3 + 1];\r
+ int lev_stack[32];\r
+\r
+ FT_Outline outline;\r
+ FT_Bitmap target;\r
+ FT_BBox clip_box;\r
+\r
+ FT_Span gray_spans[FT_MAX_GRAY_SPANS];\r
+ int num_gray_spans;\r
+\r
+ FT_Raster_Span_Func render_span;\r
+ void* render_span_data;\r
+ int span_y;\r
+\r
+ int band_size;\r
+ int band_shoot;\r
+ int conic_level;\r
+ int cubic_level;\r
+\r
+ ft_jmp_buf jump_buffer;\r
+\r
+ void* buffer;\r
+ long buffer_size;\r
+\r
+ PCell* ycells;\r
+ int ycount;\r
+\r
+ } TWorker, *PWorker;\r
+\r
+\r
+ typedef struct TRaster_\r
+ {\r
+ void* buffer;\r
+ long buffer_size;\r
+ int band_size;\r
+ void* memory;\r
+ PWorker worker;\r
+\r
+ } TRaster, *PRaster;\r
+\r
+\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* Initialize the cells table. */\r
+ /* */\r
+ static void\r
+ gray_init_cells( RAS_ARG_ void* buffer,\r
+ long byte_size )\r
+ {\r
+ ras.buffer = buffer;\r
+ ras.buffer_size = byte_size;\r
+\r
+ ras.ycells = (PCell*) buffer;\r
+ ras.cells = NULL;\r
+ ras.max_cells = 0;\r
+ ras.num_cells = 0;\r
+ ras.area = 0;\r
+ ras.cover = 0;\r
+ ras.invalid = 1;\r
+ }\r
+\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* Compute the outline bounding box. */\r
+ /* */\r
+ static void\r
+ gray_compute_cbox( RAS_ARG )\r
+ {\r
+ FT_Outline* outline = &ras.outline;\r
+ FT_Vector* vec = outline->points;\r
+ FT_Vector* limit = vec + outline->n_points;\r
+\r
+\r
+ if ( outline->n_points <= 0 )\r
+ {\r
+ ras.min_ex = ras.max_ex = 0;\r
+ ras.min_ey = ras.max_ey = 0;\r
+ return;\r
+ }\r
+\r
+ ras.min_ex = ras.max_ex = vec->x;\r
+ ras.min_ey = ras.max_ey = vec->y;\r
+\r
+ vec++;\r
+\r
+ for ( ; vec < limit; vec++ )\r
+ {\r
+ TPos x = vec->x;\r
+ TPos y = vec->y;\r
+\r
+\r
+ if ( x < ras.min_ex ) ras.min_ex = x;\r
+ if ( x > ras.max_ex ) ras.max_ex = x;\r
+ if ( y < ras.min_ey ) ras.min_ey = y;\r
+ if ( y > ras.max_ey ) ras.max_ey = y;\r
+ }\r
+\r
+ /* truncate the bounding box to integer pixels */\r
+ ras.min_ex = ras.min_ex >> 6;\r
+ ras.min_ey = ras.min_ey >> 6;\r
+ ras.max_ex = ( ras.max_ex + 63 ) >> 6;\r
+ ras.max_ey = ( ras.max_ey + 63 ) >> 6;\r
+ }\r
+\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* Record the current cell in the table. */\r
+ /* */\r
+ static PCell\r
+ gray_find_cell( RAS_ARG )\r
+ {\r
+ PCell *pcell, cell;\r
+ int x = ras.ex;\r
+\r
+\r
+ if ( x > ras.max_ex )\r
+ x = ras.max_ex;\r
+\r
+ pcell = &ras.ycells[ras.ey];\r
+ for (;;)\r
+ {\r
+ cell = *pcell;\r
+ if ( cell == NULL || cell->x > x )\r
+ break;\r
+\r
+ if ( cell->x == x )\r
+ goto Exit;\r
+\r
+ pcell = &cell->next;\r
+ }\r
+\r
+ if ( ras.num_cells >= ras.max_cells )\r
+ ft_longjmp( ras.jump_buffer, 1 );\r
+\r
+ cell = ras.cells + ras.num_cells++;\r
+ cell->x = x;\r
+ cell->area = 0;\r
+ cell->cover = 0;\r
+\r
+ cell->next = *pcell;\r
+ *pcell = cell;\r
+\r
+ Exit:\r
+ return cell;\r
+ }\r
+\r
+\r
+ static void\r
+ gray_record_cell( RAS_ARG )\r
+ {\r
+ if ( !ras.invalid && ( ras.area | ras.cover ) )\r
+ {\r
+ PCell cell = gray_find_cell( RAS_VAR );\r
+\r
+\r
+ cell->area += ras.area;\r
+ cell->cover += ras.cover;\r
+ }\r
+ }\r
+\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* Set the current cell to a new position. */\r
+ /* */\r
+ static void\r
+ gray_set_cell( RAS_ARG_ TCoord ex,\r
+ TCoord ey )\r
+ {\r
+ /* Move the cell pointer to a new position. We set the `invalid' */\r
+ /* flag to indicate that the cell isn't part of those we're interested */\r
+ /* in during the render phase. This means that: */\r
+ /* */\r
+ /* . the new vertical position must be within min_ey..max_ey-1. */\r
+ /* . the new horizontal position must be strictly less than max_ex */\r
+ /* */\r
+ /* Note that if a cell is to the left of the clipping region, it is */\r
+ /* actually set to the (min_ex-1) horizontal position. */\r
+\r
+ /* All cells that are on the left of the clipping region go to the */\r
+ /* min_ex - 1 horizontal position. */\r
+ ey -= ras.min_ey;\r
+\r
+ if ( ex > ras.max_ex )\r
+ ex = ras.max_ex;\r
+\r
+ ex -= ras.min_ex;\r
+ if ( ex < 0 )\r
+ ex = -1;\r
+\r
+ /* are we moving to a different cell ? */\r
+ if ( ex != ras.ex || ey != ras.ey )\r
+ {\r
+ /* record the current one if it is valid */\r
+ if ( !ras.invalid )\r
+ gray_record_cell( RAS_VAR );\r
+\r
+ ras.area = 0;\r
+ ras.cover = 0;\r
+ }\r
+\r
+ ras.ex = ex;\r
+ ras.ey = ey;\r
+ ras.invalid = ( (unsigned)ey >= (unsigned)ras.count_ey ||\r
+ ex >= ras.count_ex );\r
+ }\r
+\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* Start a new contour at a given cell. */\r
+ /* */\r
+ static void\r
+ gray_start_cell( RAS_ARG_ TCoord ex,\r
+ TCoord ey )\r
+ {\r
+ if ( ex > ras.max_ex )\r
+ ex = (TCoord)( ras.max_ex );\r
+\r
+ if ( ex < ras.min_ex )\r
+ ex = (TCoord)( ras.min_ex - 1 );\r
+\r
+ ras.area = 0;\r
+ ras.cover = 0;\r
+ ras.ex = ex - ras.min_ex;\r
+ ras.ey = ey - ras.min_ey;\r
+ ras.last_ey = SUBPIXELS( ey );\r
+ ras.invalid = 0;\r
+\r
+ gray_set_cell( RAS_VAR_ ex, ey );\r
+ }\r
+\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* Render a scanline as one or more cells. */\r
+ /* */\r
+ static void\r
+ gray_render_scanline( RAS_ARG_ TCoord ey,\r
+ TPos x1,\r
+ TCoord y1,\r
+ TPos x2,\r
+ TCoord y2 )\r
+ {\r
+ TCoord ex1, ex2, fx1, fx2, delta;\r
+ long p, first, dx;\r
+ int incr, lift, mod, rem;\r
+\r
+\r
+ dx = x2 - x1;\r
+\r
+ ex1 = TRUNC( x1 );\r
+ ex2 = TRUNC( x2 );\r
+ fx1 = (TCoord)( x1 - SUBPIXELS( ex1 ) );\r
+ fx2 = (TCoord)( x2 - SUBPIXELS( ex2 ) );\r
+\r
+ /* trivial case. Happens often */\r
+ if ( y1 == y2 )\r
+ {\r
+ gray_set_cell( RAS_VAR_ ex2, ey );\r
+ return;\r
+ }\r
+\r
+ /* everything is located in a single cell. That is easy! */\r
+ /* */\r
+ if ( ex1 == ex2 )\r
+ {\r
+ delta = y2 - y1;\r
+ ras.area += (TArea)( fx1 + fx2 ) * delta;\r
+ ras.cover += delta;\r
+ return;\r
+ }\r
+\r
+ /* ok, we'll have to render a run of adjacent cells on the same */\r
+ /* scanline... */\r
+ /* */\r
+ p = ( ONE_PIXEL - fx1 ) * ( y2 - y1 );\r
+ first = ONE_PIXEL;\r
+ incr = 1;\r
+\r
+ if ( dx < 0 )\r
+ {\r
+ p = fx1 * ( y2 - y1 );\r
+ first = 0;\r
+ incr = -1;\r
+ dx = -dx;\r
+ }\r
+\r
+ delta = (TCoord)( p / dx );\r
+ mod = (TCoord)( p % dx );\r
+ if ( mod < 0 )\r
+ {\r
+ delta--;\r
+ mod += (TCoord)dx;\r
+ }\r
+\r
+ ras.area += (TArea)( fx1 + first ) * delta;\r
+ ras.cover += delta;\r
+\r
+ ex1 += incr;\r
+ gray_set_cell( RAS_VAR_ ex1, ey );\r
+ y1 += delta;\r
+\r
+ if ( ex1 != ex2 )\r
+ {\r
+ p = ONE_PIXEL * ( y2 - y1 + delta );\r
+ lift = (TCoord)( p / dx );\r
+ rem = (TCoord)( p % dx );\r
+ if ( rem < 0 )\r
+ {\r
+ lift--;\r
+ rem += (TCoord)dx;\r
+ }\r
+\r
+ mod -= (int)dx;\r
+\r
+ while ( ex1 != ex2 )\r
+ {\r
+ delta = lift;\r
+ mod += rem;\r
+ if ( mod >= 0 )\r
+ {\r
+ mod -= (TCoord)dx;\r
+ delta++;\r
+ }\r
+\r
+ ras.area += (TArea)ONE_PIXEL * delta;\r
+ ras.cover += delta;\r
+ y1 += delta;\r
+ ex1 += incr;\r
+ gray_set_cell( RAS_VAR_ ex1, ey );\r
+ }\r
+ }\r
+\r
+ delta = y2 - y1;\r
+ ras.area += (TArea)( fx2 + ONE_PIXEL - first ) * delta;\r
+ ras.cover += delta;\r
+ }\r
+\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* Render a given line as a series of scanlines. */\r
+ /* */\r
+ static void\r
+ gray_render_line( RAS_ARG_ TPos to_x,\r
+ TPos to_y )\r
+ {\r
+ TCoord ey1, ey2, fy1, fy2;\r
+ TPos dx, dy, x, x2;\r
+ long p, first;\r
+ int delta, rem, mod, lift, incr;\r
+\r
+\r
+ ey1 = TRUNC( ras.last_ey );\r
+ ey2 = TRUNC( to_y ); /* if (ey2 >= ras.max_ey) ey2 = ras.max_ey-1; */\r
+ fy1 = (TCoord)( ras.y - ras.last_ey );\r
+ fy2 = (TCoord)( to_y - SUBPIXELS( ey2 ) );\r
+\r
+ dx = to_x - ras.x;\r
+ dy = to_y - ras.y;\r
+\r
+ /* XXX: we should do something about the trivial case where dx == 0, */\r
+ /* as it happens very often! */\r
+\r
+ /* perform vertical clipping */\r
+ {\r
+ TCoord min, max;\r
+\r
+\r
+ min = ey1;\r
+ max = ey2;\r
+ if ( ey1 > ey2 )\r
+ {\r
+ min = ey2;\r
+ max = ey1;\r
+ }\r
+ if ( min >= ras.max_ey || max < ras.min_ey )\r
+ goto End;\r
+ }\r
+\r
+ /* everything is on a single scanline */\r
+ if ( ey1 == ey2 )\r
+ {\r
+ gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, to_x, fy2 );\r
+ goto End;\r
+ }\r
+\r
+ /* vertical line - avoid calling gray_render_scanline */\r
+ incr = 1;\r
+\r
+ if ( dx == 0 )\r
+ {\r
+ TCoord ex = TRUNC( ras.x );\r
+ TCoord two_fx = (TCoord)( ( ras.x - SUBPIXELS( ex ) ) << 1 );\r
+ TPos area;\r
+\r
+\r
+ first = ONE_PIXEL;\r
+ if ( dy < 0 )\r
+ {\r
+ first = 0;\r
+ incr = -1;\r
+ }\r
+\r
+ delta = (int)( first - fy1 );\r
+ ras.area += (TArea)two_fx * delta;\r
+ ras.cover += delta;\r
+ ey1 += incr;\r
+\r
+ gray_set_cell( &ras, ex, ey1 );\r
+\r
+ delta = (int)( first + first - ONE_PIXEL );\r
+ area = (TArea)two_fx * delta;\r
+ while ( ey1 != ey2 )\r
+ {\r
+ ras.area += area;\r
+ ras.cover += delta;\r
+ ey1 += incr;\r
+\r
+ gray_set_cell( &ras, ex, ey1 );\r
+ }\r
+\r
+ delta = (int)( fy2 - ONE_PIXEL + first );\r
+ ras.area += (TArea)two_fx * delta;\r
+ ras.cover += delta;\r
+\r
+ goto End;\r
+ }\r
+\r
+ /* ok, we have to render several scanlines */\r
+ p = ( ONE_PIXEL - fy1 ) * dx;\r
+ first = ONE_PIXEL;\r
+ incr = 1;\r
+\r
+ if ( dy < 0 )\r
+ {\r
+ p = fy1 * dx;\r
+ first = 0;\r
+ incr = -1;\r
+ dy = -dy;\r
+ }\r
+\r
+ delta = (int)( p / dy );\r
+ mod = (int)( p % dy );\r
+ if ( mod < 0 )\r
+ {\r
+ delta--;\r
+ mod += (TCoord)dy;\r
+ }\r
+\r
+ x = ras.x + delta;\r
+ gray_render_scanline( RAS_VAR_ ey1, ras.x, fy1, x, (TCoord)first );\r
+\r
+ ey1 += incr;\r
+ gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );\r
+\r
+ if ( ey1 != ey2 )\r
+ {\r
+ p = ONE_PIXEL * dx;\r
+ lift = (int)( p / dy );\r
+ rem = (int)( p % dy );\r
+ if ( rem < 0 )\r
+ {\r
+ lift--;\r
+ rem += (int)dy;\r
+ }\r
+ mod -= (int)dy;\r
+\r
+ while ( ey1 != ey2 )\r
+ {\r
+ delta = lift;\r
+ mod += rem;\r
+ if ( mod >= 0 )\r
+ {\r
+ mod -= (int)dy;\r
+ delta++;\r
+ }\r
+\r
+ x2 = x + delta;\r
+ gray_render_scanline( RAS_VAR_ ey1, x,\r
+ (TCoord)( ONE_PIXEL - first ), x2,\r
+ (TCoord)first );\r
+ x = x2;\r
+\r
+ ey1 += incr;\r
+ gray_set_cell( RAS_VAR_ TRUNC( x ), ey1 );\r
+ }\r
+ }\r
+\r
+ gray_render_scanline( RAS_VAR_ ey1, x,\r
+ (TCoord)( ONE_PIXEL - first ), to_x,\r
+ fy2 );\r
+\r
+ End:\r
+ ras.x = to_x;\r
+ ras.y = to_y;\r
+ ras.last_ey = SUBPIXELS( ey2 );\r
+ }\r
+\r
+\r
+ static void\r
+ gray_split_conic( FT_Vector* base )\r
+ {\r
+ TPos a, b;\r
+\r
+\r
+ base[4].x = base[2].x;\r
+ b = base[1].x;\r
+ a = base[3].x = ( base[2].x + b ) / 2;\r
+ b = base[1].x = ( base[0].x + b ) / 2;\r
+ base[2].x = ( a + b ) / 2;\r
+\r
+ base[4].y = base[2].y;\r
+ b = base[1].y;\r
+ a = base[3].y = ( base[2].y + b ) / 2;\r
+ b = base[1].y = ( base[0].y + b ) / 2;\r
+ base[2].y = ( a + b ) / 2;\r
+ }\r
+\r
+\r
+ static void\r
+ gray_render_conic( RAS_ARG_ const FT_Vector* control,\r
+ const FT_Vector* to )\r
+ {\r
+ TPos dx, dy;\r
+ int top, level;\r
+ int* levels;\r
+ FT_Vector* arc;\r
+\r
+\r
+ dx = DOWNSCALE( ras.x ) + to->x - ( control->x << 1 );\r
+ if ( dx < 0 )\r
+ dx = -dx;\r
+ dy = DOWNSCALE( ras.y ) + to->y - ( control->y << 1 );\r
+ if ( dy < 0 )\r
+ dy = -dy;\r
+ if ( dx < dy )\r
+ dx = dy;\r
+\r
+ level = 1;\r
+ dx = dx / ras.conic_level;\r
+ while ( dx > 0 )\r
+ {\r
+ dx >>= 2;\r
+ level++;\r
+ }\r
+\r
+ /* a shortcut to speed things up */\r
+ if ( level <= 1 )\r
+ {\r
+ /* we compute the mid-point directly in order to avoid */\r
+ /* calling gray_split_conic() */\r
+ TPos to_x, to_y, mid_x, mid_y;\r
+\r
+\r
+ to_x = UPSCALE( to->x );\r
+ to_y = UPSCALE( to->y );\r
+ mid_x = ( ras.x + to_x + 2 * UPSCALE( control->x ) ) / 4;\r
+ mid_y = ( ras.y + to_y + 2 * UPSCALE( control->y ) ) / 4;\r
+\r
+ gray_render_line( RAS_VAR_ mid_x, mid_y );\r
+ gray_render_line( RAS_VAR_ to_x, to_y );\r
+\r
+ return;\r
+ }\r
+\r
+ arc = ras.bez_stack;\r
+ levels = ras.lev_stack;\r
+ top = 0;\r
+ levels[0] = level;\r
+\r
+ arc[0].x = UPSCALE( to->x );\r
+ arc[0].y = UPSCALE( to->y );\r
+ arc[1].x = UPSCALE( control->x );\r
+ arc[1].y = UPSCALE( control->y );\r
+ arc[2].x = ras.x;\r
+ arc[2].y = ras.y;\r
+\r
+ while ( top >= 0 )\r
+ {\r
+ level = levels[top];\r
+ if ( level > 1 )\r
+ {\r
+ /* check that the arc crosses the current band */\r
+ TPos min, max, y;\r
+\r
+\r
+ min = max = arc[0].y;\r
+\r
+ y = arc[1].y;\r
+ if ( y < min ) min = y;\r
+ if ( y > max ) max = y;\r
+\r
+ y = arc[2].y;\r
+ if ( y < min ) min = y;\r
+ if ( y > max ) max = y;\r
+\r
+ if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < ras.min_ey )\r
+ goto Draw;\r
+\r
+ gray_split_conic( arc );\r
+ arc += 2;\r
+ top++;\r
+ levels[top] = levels[top - 1] = level - 1;\r
+ continue;\r
+ }\r
+\r
+ Draw:\r
+ {\r
+ TPos to_x, to_y, mid_x, mid_y;\r
+\r
+\r
+ to_x = arc[0].x;\r
+ to_y = arc[0].y;\r
+ mid_x = ( ras.x + to_x + 2 * arc[1].x ) / 4;\r
+ mid_y = ( ras.y + to_y + 2 * arc[1].y ) / 4;\r
+\r
+ gray_render_line( RAS_VAR_ mid_x, mid_y );\r
+ gray_render_line( RAS_VAR_ to_x, to_y );\r
+\r
+ top--;\r
+ arc -= 2;\r
+ }\r
+ }\r
+\r
+ return;\r
+ }\r
+\r
+\r
+ static void\r
+ gray_split_cubic( FT_Vector* base )\r
+ {\r
+ TPos a, b, c, d;\r
+\r
+\r
+ base[6].x = base[3].x;\r
+ c = base[1].x;\r
+ d = base[2].x;\r
+ base[1].x = a = ( base[0].x + c ) / 2;\r
+ base[5].x = b = ( base[3].x + d ) / 2;\r
+ c = ( c + d ) / 2;\r
+ base[2].x = a = ( a + c ) / 2;\r
+ base[4].x = b = ( b + c ) / 2;\r
+ base[3].x = ( a + b ) / 2;\r
+\r
+ base[6].y = base[3].y;\r
+ c = base[1].y;\r
+ d = base[2].y;\r
+ base[1].y = a = ( base[0].y + c ) / 2;\r
+ base[5].y = b = ( base[3].y + d ) / 2;\r
+ c = ( c + d ) / 2;\r
+ base[2].y = a = ( a + c ) / 2;\r
+ base[4].y = b = ( b + c ) / 2;\r
+ base[3].y = ( a + b ) / 2;\r
+ }\r
+\r
+\r
+ static void\r
+ gray_render_cubic( RAS_ARG_ const FT_Vector* control1,\r
+ const FT_Vector* control2,\r
+ const FT_Vector* to )\r
+ {\r
+ TPos dx, dy, da, db;\r
+ int top, level;\r
+ int* levels;\r
+ FT_Vector* arc;\r
+\r
+\r
+ dx = DOWNSCALE( ras.x ) + to->x - ( control1->x << 1 );\r
+ if ( dx < 0 )\r
+ dx = -dx;\r
+ dy = DOWNSCALE( ras.y ) + to->y - ( control1->y << 1 );\r
+ if ( dy < 0 )\r
+ dy = -dy;\r
+ if ( dx < dy )\r
+ dx = dy;\r
+ da = dx;\r
+\r
+ dx = DOWNSCALE( ras.x ) + to->x - 3 * ( control1->x + control2->x );\r
+ if ( dx < 0 )\r
+ dx = -dx;\r
+ dy = DOWNSCALE( ras.y ) + to->y - 3 * ( control1->x + control2->y );\r
+ if ( dy < 0 )\r
+ dy = -dy;\r
+ if ( dx < dy )\r
+ dx = dy;\r
+ db = dx;\r
+\r
+ level = 1;\r
+ da = da / ras.cubic_level;\r
+ db = db / ras.conic_level;\r
+ while ( da > 0 || db > 0 )\r
+ {\r
+ da >>= 2;\r
+ db >>= 3;\r
+ level++;\r
+ }\r
+\r
+ if ( level <= 1 )\r
+ {\r
+ TPos to_x, to_y, mid_x, mid_y;\r
+\r
+\r
+ to_x = UPSCALE( to->x );\r
+ to_y = UPSCALE( to->y );\r
+ mid_x = ( ras.x + to_x +\r
+ 3 * UPSCALE( control1->x + control2->x ) ) / 8;\r
+ mid_y = ( ras.y + to_y +\r
+ 3 * UPSCALE( control1->y + control2->y ) ) / 8;\r
+\r
+ gray_render_line( RAS_VAR_ mid_x, mid_y );\r
+ gray_render_line( RAS_VAR_ to_x, to_y );\r
+ return;\r
+ }\r
+\r
+ arc = ras.bez_stack;\r
+ arc[0].x = UPSCALE( to->x );\r
+ arc[0].y = UPSCALE( to->y );\r
+ arc[1].x = UPSCALE( control2->x );\r
+ arc[1].y = UPSCALE( control2->y );\r
+ arc[2].x = UPSCALE( control1->x );\r
+ arc[2].y = UPSCALE( control1->y );\r
+ arc[3].x = ras.x;\r
+ arc[3].y = ras.y;\r
+\r
+ levels = ras.lev_stack;\r
+ top = 0;\r
+ levels[0] = level;\r
+\r
+ while ( top >= 0 )\r
+ {\r
+ level = levels[top];\r
+ if ( level > 1 )\r
+ {\r
+ /* check that the arc crosses the current band */\r
+ TPos min, max, y;\r
+\r
+\r
+ min = max = arc[0].y;\r
+ y = arc[1].y;\r
+ if ( y < min ) min = y;\r
+ if ( y > max ) max = y;\r
+ y = arc[2].y;\r
+ if ( y < min ) min = y;\r
+ if ( y > max ) max = y;\r
+ y = arc[3].y;\r
+ if ( y < min ) min = y;\r
+ if ( y > max ) max = y;\r
+ if ( TRUNC( min ) >= ras.max_ey || TRUNC( max ) < 0 )\r
+ goto Draw;\r
+ gray_split_cubic( arc );\r
+ arc += 3;\r
+ top ++;\r
+ levels[top] = levels[top - 1] = level - 1;\r
+ continue;\r
+ }\r
+\r
+ Draw:\r
+ {\r
+ TPos to_x, to_y, mid_x, mid_y;\r
+\r
+\r
+ to_x = arc[0].x;\r
+ to_y = arc[0].y;\r
+ mid_x = ( ras.x + to_x + 3 * ( arc[1].x + arc[2].x ) ) / 8;\r
+ mid_y = ( ras.y + to_y + 3 * ( arc[1].y + arc[2].y ) ) / 8;\r
+\r
+ gray_render_line( RAS_VAR_ mid_x, mid_y );\r
+ gray_render_line( RAS_VAR_ to_x, to_y );\r
+ top --;\r
+ arc -= 3;\r
+ }\r
+ }\r
+\r
+ return;\r
+ }\r
+\r
+\r
+\r
+ static int\r
+ gray_move_to( const FT_Vector* to,\r
+ PWorker worker )\r
+ {\r
+ TPos x, y;\r
+\r
+\r
+ /* record current cell, if any */\r
+ gray_record_cell( worker );\r
+\r
+ /* start to a new position */\r
+ x = UPSCALE( to->x );\r
+ y = UPSCALE( to->y );\r
+\r
+ gray_start_cell( worker, TRUNC( x ), TRUNC( y ) );\r
+\r
+ worker->x = x;\r
+ worker->y = y;\r
+ return 0;\r
+ }\r
+\r
+\r
+ static int\r
+ gray_line_to( const FT_Vector* to,\r
+ PWorker worker )\r
+ {\r
+ gray_render_line( worker, UPSCALE( to->x ), UPSCALE( to->y ) );\r
+ return 0;\r
+ }\r
+\r
+\r
+ static int\r
+ gray_conic_to( const FT_Vector* control,\r
+ const FT_Vector* to,\r
+ PWorker worker )\r
+ {\r
+ gray_render_conic( worker, control, to );\r
+ return 0;\r
+ }\r
+\r
+\r
+ static int\r
+ gray_cubic_to( const FT_Vector* control1,\r
+ const FT_Vector* control2,\r
+ const FT_Vector* to,\r
+ PWorker worker )\r
+ {\r
+ gray_render_cubic( worker, control1, control2, to );\r
+ return 0;\r
+ }\r
+\r
+\r
+ static void\r
+ gray_render_span( int y,\r
+ int count,\r
+ const FT_Span* spans,\r
+ PWorker worker )\r
+ {\r
+ unsigned char* p;\r
+ FT_Bitmap* map = &worker->target;\r
+\r
+\r
+ /* first of all, compute the scanline offset */\r
+ p = (unsigned char*)map->buffer - y * map->pitch;\r
+ if ( map->pitch >= 0 )\r
+ p += ( map->rows - 1 ) * map->pitch;\r
+\r
+ for ( ; count > 0; count--, spans++ )\r
+ {\r
+ unsigned char coverage = spans->coverage;\r
+\r
+\r
+ if ( coverage )\r
+ {\r
+ /* For small-spans it is faster to do it by ourselves than\r
+ * calling `memset'. This is mainly due to the cost of the\r
+ * function call.\r
+ */\r
+ if ( spans->len >= 8 )\r
+ FT_MEM_SET( p + spans->x, (unsigned char)coverage, spans->len );\r
+ else\r
+ {\r
+ unsigned char* q = p + spans->x;\r
+\r
+\r
+ switch ( spans->len )\r
+ {\r
+ case 7: *q++ = (unsigned char)coverage;\r
+ case 6: *q++ = (unsigned char)coverage;\r
+ case 5: *q++ = (unsigned char)coverage;\r
+ case 4: *q++ = (unsigned char)coverage;\r
+ case 3: *q++ = (unsigned char)coverage;\r
+ case 2: *q++ = (unsigned char)coverage;\r
+ case 1: *q = (unsigned char)coverage;\r
+ default:\r
+ ;\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ static void\r
+ gray_hline( RAS_ARG_ TCoord x,\r
+ TCoord y,\r
+ TPos area,\r
+ int acount )\r
+ {\r
+ FT_Span* span;\r
+ int count;\r
+ int coverage;\r
+\r
+\r
+ /* compute the coverage line's coverage, depending on the */\r
+ /* outline fill rule */\r
+ /* */\r
+ /* the coverage percentage is area/(PIXEL_BITS*PIXEL_BITS*2) */\r
+ /* */\r
+ coverage = (int)( area >> ( PIXEL_BITS * 2 + 1 - 8 ) );\r
+ /* use range 0..256 */\r
+ if ( coverage < 0 )\r
+ coverage = -coverage;\r
+\r
+ if ( ras.outline.flags & FT_OUTLINE_EVEN_ODD_FILL )\r
+ {\r
+ coverage &= 511;\r
+\r
+ if ( coverage > 256 )\r
+ coverage = 512 - coverage;\r
+ else if ( coverage == 256 )\r
+ coverage = 255;\r
+ }\r
+ else\r
+ {\r
+ /* normal non-zero winding rule */\r
+ if ( coverage >= 256 )\r
+ coverage = 255;\r
+ }\r
+\r
+ y += (TCoord)ras.min_ey;\r
+ x += (TCoord)ras.min_ex;\r
+\r
+ /* FT_Span.x is a 16-bit short, so limit our coordinates appropriately */\r
+ if ( x >= 32768 )\r
+ x = 32767;\r
+\r
+ if ( coverage )\r
+ {\r
+ /* see whether we can add this span to the current list */\r
+ count = ras.num_gray_spans;\r
+ span = ras.gray_spans + count - 1;\r
+ if ( count > 0 &&\r
+ ras.span_y == y &&\r
+ (int)span->x + span->len == (int)x &&\r
+ span->coverage == coverage )\r
+ {\r
+ span->len = (unsigned short)( span->len + acount );\r
+ return;\r
+ }\r
+\r
+ if ( ras.span_y != y || count >= FT_MAX_GRAY_SPANS )\r
+ {\r
+ if ( ras.render_span && count > 0 )\r
+ ras.render_span( ras.span_y, count, ras.gray_spans,\r
+ ras.render_span_data );\r
+ /* ras.render_span( span->y, ras.gray_spans, count ); */\r
+\r
+#ifdef DEBUG_GRAYS\r
+\r
+ if ( ras.span_y >= 0 )\r
+ {\r
+ int n;\r
+\r
+\r
+ fprintf( stderr, "y=%3d ", ras.span_y );\r
+ span = ras.gray_spans;\r
+ for ( n = 0; n < count; n++, span++ )\r
+ fprintf( stderr, "[%d..%d]:%02x ",\r
+ span->x, span->x + span->len - 1, span->coverage );\r
+ fprintf( stderr, "\n" );\r
+ }\r
+\r
+#endif /* DEBUG_GRAYS */\r
+\r
+ ras.num_gray_spans = 0;\r
+ ras.span_y = y;\r
+\r
+ count = 0;\r
+ span = ras.gray_spans;\r
+ }\r
+ else\r
+ span++;\r
+\r
+ /* add a gray span to the current list */\r
+ span->x = (short)x;\r
+ span->len = (unsigned short)acount;\r
+ span->coverage = (unsigned char)coverage;\r
+\r
+ ras.num_gray_spans++;\r
+ }\r
+ }\r
+\r
+\r
+#ifdef DEBUG_GRAYS\r
+\r
+ /* to be called while in the debugger */\r
+ gray_dump_cells( RAS_ARG )\r
+ {\r
+ int yindex;\r
+\r
+\r
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )\r
+ {\r
+ PCell cell;\r
+\r
+\r
+ printf( "%3d:", yindex );\r
+\r
+ for ( cell = ras.ycells[yindex]; cell != NULL; cell = cell->next )\r
+ printf( " (%3d, c:%4d, a:%6d)", cell->x, cell->cover, cell->area );\r
+ printf( "\n" );\r
+ }\r
+ }\r
+\r
+#endif /* DEBUG_GRAYS */\r
+\r
+\r
+ static void\r
+ gray_sweep( RAS_ARG_ const FT_Bitmap* target )\r
+ {\r
+ int yindex;\r
+\r
+ FT_UNUSED( target );\r
+\r
+\r
+ if ( ras.num_cells == 0 )\r
+ return;\r
+\r
+ ras.num_gray_spans = 0;\r
+\r
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )\r
+ {\r
+ PCell cell = ras.ycells[yindex];\r
+ TCoord cover = 0;\r
+ TCoord x = 0;\r
+\r
+\r
+ for ( ; cell != NULL; cell = cell->next )\r
+ {\r
+ TArea area;\r
+\r
+\r
+ if ( cell->x > x && cover != 0 )\r
+ gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),\r
+ cell->x - x );\r
+\r
+ cover += cell->cover;\r
+ area = cover * ( ONE_PIXEL * 2 ) - cell->area;\r
+\r
+ if ( area != 0 && cell->x >= 0 )\r
+ gray_hline( RAS_VAR_ cell->x, yindex, area, 1 );\r
+\r
+ x = cell->x + 1;\r
+ }\r
+\r
+ if ( cover != 0 )\r
+ gray_hline( RAS_VAR_ x, yindex, cover * ( ONE_PIXEL * 2 ),\r
+ ras.count_ex - x );\r
+ }\r
+\r
+ if ( ras.render_span && ras.num_gray_spans > 0 )\r
+ ras.render_span( ras.span_y, ras.num_gray_spans,\r
+ ras.gray_spans, ras.render_span_data );\r
+ }\r
+\r
+\r
+#ifdef _STANDALONE_\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* The following function should only compile in stand_alone mode, */\r
+ /* i.e., when building this component without the rest of FreeType. */\r
+ /* */\r
+ /*************************************************************************/\r
+\r
+ /*************************************************************************/\r
+ /* */\r
+ /* <Function> */\r
+ /* FT_Outline_Decompose */\r
+ /* */\r
+ /* <Description> */\r
+ /* Walks over an outline's structure to decompose it into individual */\r
+ /* segments and Bezier arcs. This function is also able to emit */\r
+ /* `move to' and `close to' operations to indicate the start and end */\r
+ /* of new contours in the outline. */\r
+ /* */\r
+ /* <Input> */\r
+ /* outline :: A pointer to the source target. */\r
+ /* */\r
+ /* func_interface :: A table of `emitters', i.e,. function pointers */\r
+ /* called during decomposition to indicate path */\r
+ /* operations. */\r
+ /* */\r
+ /* user :: A typeless pointer which is passed to each */\r
+ /* emitter during the decomposition. It can be */\r
+ /* used to store the state during the */\r
+ /* decomposition. */\r
+ /* */\r
+ /* <Return> */\r
+ /* Error code. 0 means success. */\r
+ /* */\r
+ static\r
+ int FT_Outline_Decompose( const FT_Outline* outline,\r
+ const FT_Outline_Funcs* func_interface,\r
+ void* user )\r
+ {\r
+#undef SCALED\r
+#if 0\r
+#define SCALED( x ) ( ( (x) << shift ) - delta )\r
+#else\r
+#define SCALED( x ) (x)\r
+#endif\r
+\r
+ FT_Vector v_last;\r
+ FT_Vector v_control;\r
+ FT_Vector v_start;\r
+\r
+ FT_Vector* point;\r
+ FT_Vector* limit;\r
+ char* tags;\r
+\r
+ int n; /* index of contour in outline */\r
+ int first; /* index of first point in contour */\r
+ int error;\r
+ char tag; /* current point's state */\r
+\r
+#if 0\r
+ int shift = func_interface->shift;\r
+ TPos delta = func_interface->delta;\r
+#endif\r
+\r
+\r
+ first = 0;\r
+\r
+ for ( n = 0; n < outline->n_contours; n++ )\r
+ {\r
+ int last; /* index of last point in contour */\r
+\r
+\r
+ last = outline->contours[n];\r
+ limit = outline->points + last;\r
+\r
+ v_start = outline->points[first];\r
+ v_last = outline->points[last];\r
+\r
+ v_start.x = SCALED( v_start.x );\r
+ v_start.y = SCALED( v_start.y );\r
+\r
+ v_last.x = SCALED( v_last.x );\r
+ v_last.y = SCALED( v_last.y );\r
+\r
+ v_control = v_start;\r
+\r
+ point = outline->points + first;\r
+ tags = outline->tags + first;\r
+ tag = FT_CURVE_TAG( tags[0] );\r
+\r
+ /* A contour cannot start with a cubic control point! */\r
+ if ( tag == FT_CURVE_TAG_CUBIC )\r
+ goto Invalid_Outline;\r
+\r
+ /* check first point to determine origin */\r
+ if ( tag == FT_CURVE_TAG_CONIC )\r
+ {\r
+ /* first point is conic control. Yes, this happens. */\r
+ if ( FT_CURVE_TAG( outline->tags[last] ) == FT_CURVE_TAG_ON )\r
+ {\r
+ /* start at last point if it is on the curve */\r
+ v_start = v_last;\r
+ limit--;\r
+ }\r
+ else\r
+ {\r
+ /* if both first and last points are conic, */\r
+ /* start at their middle and record its position */\r
+ /* for closure */\r
+ v_start.x = ( v_start.x + v_last.x ) / 2;\r
+ v_start.y = ( v_start.y + v_last.y ) / 2;\r
+\r
+ v_last = v_start;\r
+ }\r
+ point--;\r
+ tags--;\r
+ }\r
+\r
+ error = func_interface->move_to( &v_start, user );\r
+ if ( error )\r
+ goto Exit;\r
+\r
+ while ( point < limit )\r
+ {\r
+ point++;\r
+ tags++;\r
+\r
+ tag = FT_CURVE_TAG( tags[0] );\r
+ switch ( tag )\r
+ {\r
+ case FT_CURVE_TAG_ON: /* emit a single line_to */\r
+ {\r
+ FT_Vector vec;\r
+\r
+\r
+ vec.x = SCALED( point->x );\r
+ vec.y = SCALED( point->y );\r
+\r
+ error = func_interface->line_to( &vec, user );\r
+ if ( error )\r
+ goto Exit;\r
+ continue;\r
+ }\r
+\r
+ case FT_CURVE_TAG_CONIC: /* consume conic arcs */\r
+ {\r
+ v_control.x = SCALED( point->x );\r
+ v_control.y = SCALED( point->y );\r
+\r
+ Do_Conic:\r
+ if ( point < limit )\r
+ {\r
+ FT_Vector vec;\r
+ FT_Vector v_middle;\r
+\r
+\r
+ point++;\r
+ tags++;\r
+ tag = FT_CURVE_TAG( tags[0] );\r
+\r
+ vec.x = SCALED( point->x );\r
+ vec.y = SCALED( point->y );\r
+\r
+ if ( tag == FT_CURVE_TAG_ON )\r
+ {\r
+ error = func_interface->conic_to( &v_control, &vec,\r
+ user );\r
+ if ( error )\r
+ goto Exit;\r
+ continue;\r
+ }\r
+\r
+ if ( tag != FT_CURVE_TAG_CONIC )\r
+ goto Invalid_Outline;\r
+\r
+ v_middle.x = ( v_control.x + vec.x ) / 2;\r
+ v_middle.y = ( v_control.y + vec.y ) / 2;\r
+\r
+ error = func_interface->conic_to( &v_control, &v_middle,\r
+ user );\r
+ if ( error )\r
+ goto Exit;\r
+\r
+ v_control = vec;\r
+ goto Do_Conic;\r
+ }\r
+\r
+ error = func_interface->conic_to( &v_control, &v_start,\r
+ user );\r
+ goto Close;\r
+ }\r
+\r
+ default: /* FT_CURVE_TAG_CUBIC */\r
+ {\r
+ FT_Vector vec1, vec2;\r
+\r
+\r
+ if ( point + 1 > limit ||\r
+ FT_CURVE_TAG( tags[1] ) != FT_CURVE_TAG_CUBIC )\r
+ goto Invalid_Outline;\r
+\r
+ point += 2;\r
+ tags += 2;\r
+\r
+ vec1.x = SCALED( point[-2].x );\r
+ vec1.y = SCALED( point[-2].y );\r
+\r
+ vec2.x = SCALED( point[-1].x );\r
+ vec2.y = SCALED( point[-1].y );\r
+\r
+ if ( point <= limit )\r
+ {\r
+ FT_Vector vec;\r
+\r
+\r
+ vec.x = SCALED( point->x );\r
+ vec.y = SCALED( point->y );\r
+\r
+ error = func_interface->cubic_to( &vec1, &vec2, &vec, user );\r
+ if ( error )\r
+ goto Exit;\r
+ continue;\r
+ }\r
+\r
+ error = func_interface->cubic_to( &vec1, &vec2, &v_start, user );\r
+ goto Close;\r
+ }\r
+ }\r
+ }\r
+\r
+ /* close the contour with a line segment */\r
+ error = func_interface->line_to( &v_start, user );\r
+\r
+ Close:\r
+ if ( error )\r
+ goto Exit;\r
+\r
+ first = last + 1;\r
+ }\r
+\r
+ return 0;\r
+\r
+ Exit:\r
+ return error;\r
+\r
+ Invalid_Outline:\r
+ return ErrRaster_Invalid_Outline;\r
+ }\r
+\r
+#endif /* _STANDALONE_ */\r
+\r
+\r
+ typedef struct TBand_\r
+ {\r
+ TPos min, max;\r
+\r
+ } TBand;\r
+\r
+\r
+ static int\r
+ gray_convert_glyph_inner( RAS_ARG )\r
+ {\r
+ static\r
+ const FT_Outline_Funcs func_interface =\r
+ {\r
+ (FT_Outline_MoveTo_Func) gray_move_to,\r
+ (FT_Outline_LineTo_Func) gray_line_to,\r
+ (FT_Outline_ConicTo_Func)gray_conic_to,\r
+ (FT_Outline_CubicTo_Func)gray_cubic_to,\r
+ 0,\r
+ 0\r
+ };\r
+\r
+ volatile int error = 0;\r
+\r
+ if ( ft_setjmp( ras.jump_buffer ) == 0 )\r
+ {\r
+ error = FT_Outline_Decompose( &ras.outline, &func_interface, &ras );\r
+ gray_record_cell( RAS_VAR );\r
+ }\r
+ else\r
+ {\r
+ error = ErrRaster_Memory_Overflow;\r
+ }\r
+\r
+ return error;\r
+ }\r
+\r
+\r
+ static int\r
+ gray_convert_glyph( RAS_ARG )\r
+ {\r
+ TBand bands[40];\r
+ TBand* volatile band;\r
+ int volatile n, num_bands;\r
+ TPos volatile min, max, max_y;\r
+ FT_BBox* clip;\r
+\r
+\r
+ /* Set up state in the raster object */\r
+ gray_compute_cbox( RAS_VAR );\r
+\r
+ /* clip to target bitmap, exit if nothing to do */\r
+ clip = &ras.clip_box;\r
+\r
+ if ( ras.max_ex <= clip->xMin || ras.min_ex >= clip->xMax ||\r
+ ras.max_ey <= clip->yMin || ras.min_ey >= clip->yMax )\r
+ return 0;\r
+\r
+ if ( ras.min_ex < clip->xMin ) ras.min_ex = clip->xMin;\r
+ if ( ras.min_ey < clip->yMin ) ras.min_ey = clip->yMin;\r
+\r
+ if ( ras.max_ex > clip->xMax ) ras.max_ex = clip->xMax;\r
+ if ( ras.max_ey > clip->yMax ) ras.max_ey = clip->yMax;\r
+\r
+ ras.count_ex = ras.max_ex - ras.min_ex;\r
+ ras.count_ey = ras.max_ey - ras.min_ey;\r
+\r
+ /* simple heuristic used to speed-up the bezier decomposition -- see */\r
+ /* the code in gray_render_conic() and gray_render_cubic() for more */\r
+ /* details */\r
+ ras.conic_level = 32;\r
+ ras.cubic_level = 16;\r
+\r
+ {\r
+ int level = 0;\r
+\r
+\r
+ if ( ras.count_ex > 24 || ras.count_ey > 24 )\r
+ level++;\r
+ if ( ras.count_ex > 120 || ras.count_ey > 120 )\r
+ level++;\r
+\r
+ ras.conic_level <<= level;\r
+ ras.cubic_level <<= level;\r
+ }\r
+\r
+ /* setup vertical bands */\r
+ num_bands = (int)( ( ras.max_ey - ras.min_ey ) / ras.band_size );\r
+ if ( num_bands == 0 ) num_bands = 1;\r
+ if ( num_bands >= 39 ) num_bands = 39;\r
+\r
+ ras.band_shoot = 0;\r
+\r
+ min = ras.min_ey;\r
+ max_y = ras.max_ey;\r
+\r
+ for ( n = 0; n < num_bands; n++, min = max )\r
+ {\r
+ max = min + ras.band_size;\r
+ if ( n == num_bands - 1 || max > max_y )\r
+ max = max_y;\r
+\r
+ bands[0].min = min;\r
+ bands[0].max = max;\r
+ band = bands;\r
+\r
+ while ( band >= bands )\r
+ {\r
+ TPos bottom, top, middle;\r
+ int error;\r
+\r
+ {\r
+ PCell cells_max;\r
+ int yindex;\r
+ long cell_start, cell_end, cell_mod;\r
+\r
+\r
+ ras.ycells = (PCell*)ras.buffer;\r
+ ras.ycount = band->max - band->min;\r
+\r
+ cell_start = sizeof ( PCell ) * ras.ycount;\r
+ cell_mod = cell_start % sizeof ( TCell );\r
+ if ( cell_mod > 0 )\r
+ cell_start += sizeof ( TCell ) - cell_mod;\r
+\r
+ cell_end = ras.buffer_size;\r
+ cell_end -= cell_end % sizeof( TCell );\r
+\r
+ cells_max = (PCell)( (char*)ras.buffer + cell_end );\r
+ ras.cells = (PCell)( (char*)ras.buffer + cell_start );\r
+ if ( ras.cells >= cells_max )\r
+ goto ReduceBands;\r
+\r
+ ras.max_cells = cells_max - ras.cells;\r
+ if ( ras.max_cells < 2 )\r
+ goto ReduceBands;\r
+\r
+ for ( yindex = 0; yindex < ras.ycount; yindex++ )\r
+ ras.ycells[yindex] = NULL;\r
+ }\r
+\r
+ ras.num_cells = 0;\r
+ ras.invalid = 1;\r
+ ras.min_ey = band->min;\r
+ ras.max_ey = band->max;\r
+ ras.count_ey = band->max - band->min;\r
+\r
+ error = gray_convert_glyph_inner( RAS_VAR );\r
+\r
+ if ( !error )\r
+ {\r
+ gray_sweep( RAS_VAR_ &ras.target );\r
+ band--;\r
+ continue;\r
+ }\r
+ else if ( error != ErrRaster_Memory_Overflow )\r
+ return 1;\r
+\r
+ ReduceBands:\r
+ /* render pool overflow; we will reduce the render band by half */\r
+ bottom = band->min;\r
+ top = band->max;\r
+ middle = bottom + ( ( top - bottom ) >> 1 );\r
+\r
+ /* This is too complex for a single scanline; there must */\r
+ /* be some problems. */\r
+ if ( middle == bottom )\r
+ {\r
+#ifdef DEBUG_GRAYS\r
+ fprintf( stderr, "Rotten glyph!\n" );\r
+#endif\r
+ return 1;\r
+ }\r
+\r
+ if ( bottom-top >= ras.band_size )\r
+ ras.band_shoot++;\r
+\r
+ band[1].min = bottom;\r
+ band[1].max = middle;\r
+ band[0].min = middle;\r
+ band[0].max = top;\r
+ band++;\r
+ }\r
+ }\r
+\r
+ if ( ras.band_shoot > 8 && ras.band_size > 16 )\r
+ ras.band_size = ras.band_size / 2;\r
+\r
+ return 0;\r
+ }\r
+\r
+\r
+ static int\r
+ gray_raster_render( PRaster raster,\r
+ const FT_Raster_Params* params )\r
+ {\r
+ const FT_Outline* outline = (const FT_Outline*)params->source;\r
+ const FT_Bitmap* target_map = params->target;\r
+ PWorker worker;\r
+\r
+\r
+ if ( !raster || !raster->buffer || !raster->buffer_size )\r
+ return ErrRaster_Invalid_Argument;\r
+\r
+ /* return immediately if the outline is empty */\r
+ if ( outline->n_points == 0 || outline->n_contours <= 0 )\r
+ return 0;\r
+\r
+ if ( !outline || !outline->contours || !outline->points )\r
+ return ErrRaster_Invalid_Outline;\r
+\r
+ if ( outline->n_points !=\r
+ outline->contours[outline->n_contours - 1] + 1 )\r
+ return ErrRaster_Invalid_Outline;\r
+\r
+ worker = raster->worker;\r
+\r
+ /* if direct mode is not set, we must have a target bitmap */\r
+ if ( ( params->flags & FT_RASTER_FLAG_DIRECT ) == 0 )\r
+ {\r
+ if ( !target_map )\r
+ return ErrRaster_Invalid_Argument;\r
+\r
+ /* nothing to do */\r
+ if ( !target_map->width || !target_map->rows )\r
+ return 0;\r
+\r
+ if ( !target_map->buffer )\r
+ return ErrRaster_Invalid_Argument;\r
+ }\r
+\r
+ /* this version does not support monochrome rendering */\r
+ if ( !( params->flags & FT_RASTER_FLAG_AA ) )\r
+ return ErrRaster_Invalid_Mode;\r
+\r
+ /* compute clipping box */\r
+ if ( ( params->flags & FT_RASTER_FLAG_DIRECT ) == 0 )\r
+ {\r
+ /* compute clip box from target pixmap */\r
+ ras.clip_box.xMin = 0;\r
+ ras.clip_box.yMin = 0;\r
+ ras.clip_box.xMax = target_map->width;\r
+ ras.clip_box.yMax = target_map->rows;\r
+ }\r
+ else if ( params->flags & FT_RASTER_FLAG_CLIP )\r
+ {\r
+ ras.clip_box = params->clip_box;\r
+ }\r
+ else\r
+ {\r
+ ras.clip_box.xMin = -32768L;\r
+ ras.clip_box.yMin = -32768L;\r
+ ras.clip_box.xMax = 32767L;\r
+ ras.clip_box.yMax = 32767L;\r
+ }\r
+\r
+ gray_init_cells( worker, raster->buffer, raster->buffer_size );\r
+\r
+ ras.outline = *outline;\r
+ ras.num_cells = 0;\r
+ ras.invalid = 1;\r
+ ras.band_size = raster->band_size;\r
+ ras.num_gray_spans = 0;\r
+\r
+ if ( target_map )\r
+ ras.target = *target_map;\r
+\r
+ ras.render_span = (FT_Raster_Span_Func)gray_render_span;\r
+ ras.render_span_data = &ras;\r
+\r
+ if ( params->flags & FT_RASTER_FLAG_DIRECT )\r
+ {\r
+ ras.render_span = (FT_Raster_Span_Func)params->gray_spans;\r
+ ras.render_span_data = params->user;\r
+ }\r
+\r
+ return gray_convert_glyph( worker );\r
+ }\r
+\r
+\r
+ /**** RASTER OBJECT CREATION: In standalone mode, we simply use *****/\r
+ /**** a static object. *****/\r
+\r
+#ifdef _STANDALONE_\r
+\r
+ static int\r
+ gray_raster_new( void* memory,\r
+ FT_Raster* araster )\r
+ {\r
+ static TRaster the_raster;\r
+\r
+ FT_UNUSED( memory );\r
+\r
+\r
+ *araster = (FT_Raster)&the_raster;\r
+ FT_MEM_ZERO( &the_raster, sizeof ( the_raster ) );\r
+\r
+ return 0;\r
+ }\r
+\r
+\r
+ static void\r
+ gray_raster_done( FT_Raster raster )\r
+ {\r
+ /* nothing */\r
+ FT_UNUSED( raster );\r
+ }\r
+\r
+#else /* _STANDALONE_ */\r
+\r
+ static int\r
+ gray_raster_new( FT_Memory memory,\r
+ FT_Raster* araster )\r
+ {\r
+ FT_Error error;\r
+ PRaster raster;\r
+\r
+\r
+ *araster = 0;\r
+ if ( !FT_ALLOC( raster, sizeof ( TRaster ) ) )\r
+ {\r
+ raster->memory = memory;\r
+ *araster = (FT_Raster)raster;\r
+ }\r
+\r
+ return error;\r
+ }\r
+\r
+\r
+ static void\r
+ gray_raster_done( FT_Raster raster )\r
+ {\r
+ FT_Memory memory = (FT_Memory)((PRaster)raster)->memory;\r
+\r
+\r
+ FT_FREE( raster );\r
+ }\r
+\r
+#endif /* _STANDALONE_ */\r
+\r
+\r
+ static void\r
+ gray_raster_reset( FT_Raster raster,\r
+ char* pool_base,\r
+ long pool_size )\r
+ {\r
+ PRaster rast = (PRaster)raster;\r
+\r
+\r
+ if ( raster )\r
+ {\r
+ if ( pool_base && pool_size >= (long)sizeof ( TWorker ) + 2048 )\r
+ {\r
+ PWorker worker = (PWorker)pool_base;\r
+\r
+\r
+ rast->worker = worker;\r
+ rast->buffer = pool_base +\r
+ ( ( sizeof ( TWorker ) + sizeof ( TCell ) - 1 ) &\r
+ ~( sizeof ( TCell ) - 1 ) );\r
+ rast->buffer_size = (long)( ( pool_base + pool_size ) -\r
+ (char*)rast->buffer ) &\r
+ ~( sizeof ( TCell ) - 1 );\r
+ rast->band_size = (int)( rast->buffer_size /\r
+ ( sizeof ( TCell ) * 8 ) );\r
+ }\r
+ else\r
+ {\r
+ rast->buffer = NULL;\r
+ rast->buffer_size = 0;\r
+ rast->worker = NULL;\r
+ }\r
+ }\r
+ }\r
+\r
+\r
+ const FT_Raster_Funcs ft_grays_raster =\r
+ {\r
+ FT_GLYPH_FORMAT_OUTLINE,\r
+\r
+ (FT_Raster_New_Func) gray_raster_new,\r
+ (FT_Raster_Reset_Func) gray_raster_reset,\r
+ (FT_Raster_Set_Mode_Func)0,\r
+ (FT_Raster_Render_Func) gray_raster_render,\r
+ (FT_Raster_Done_Func) gray_raster_done\r
+ };\r
+\r
+\r
+/* END */\r