Improve scan conversion rules 4 and 6.

Two new constraints are introduced to better identify a `stub' -- a
concept which is only vaguely described in the OpenType
specification.  The old code was too rigorous and suppressed more
pixel than it should.

  . The intersection of the two profiles with the scanline is less
    than a half pixel.  Code related to this was already present in
    the sources but has been commented out.

  . The endpoint of the original contour forming a profile has a
    distance (`overshoot') less than half a pixel to the scanline.

Note that the two additional conditions fix almost all differences
to the Windows rasterizer, but some problematic cases remain.

* src/raster/ftraster.c (Overshoot_Top, Overshoot_Bottom): New
macros for the `flags' field in the `TProfile' structure.
(IS_BOTTOM_OVERSHOOT, IS_TOP_OVERSHOOT): New macros.
(New_Profile, End_Profile): Pass overshoot flag as an argument and
set it accordingly.
Update callers.
(Vertical_Sweep_Drop, Horizontal_Sweep_Drop): Implement the two new
constraints.
This commit is contained in:
Werner Lemberg 2009-06-16 15:14:21 +02:00
parent 0409ef3268
commit 42206ad86a
2 changed files with 151 additions and 65 deletions

View File

@ -1,3 +1,31 @@
2009-06-16 Werner Lemberg <wl@gnu.org>
Improve scan conversion rules 4 and 6.
Two new constraints are introduced to better identify a `stub' -- a
concept which is only vaguely described in the OpenType
specification. The old code was too rigorous and suppressed more
pixel than it should.
. The intersection of the two profiles with the scanline is less
than a half pixel. Code related to this was already present in
the sources but has been commented out.
. The endpoint of the original contour forming a profile has a
distance (`overshoot') less than half a pixel to the scanline.
Note that the two additional conditions fix almost all differences
to the Windows rasterizer, but some problematic cases remain.
* src/raster/ftraster.c (Overshoot_Top, Overshoot_Bottom): New
macros for the `flags' field in the `TProfile' structure.
(IS_BOTTOM_OVERSHOOT, IS_TOP_OVERSHOOT): New macros.
(New_Profile, End_Profile): Pass overshoot flag as an argument and
set it accordingly.
Update callers.
(Vertical_Sweep_Drop, Horizontal_Sweep_Drop): Implement the two new
constraints.
2009-06-11 Werner Lemberg <wl@gnu.org>
Increase precision for B/W rasterizer.

View File

@ -305,7 +305,10 @@
} TPoint;
#define Flow_Up 0x1
/* values for the `flags' bit field */
#define Flow_Up 0x1
#define Overshoot_Top 0x2
#define Overshoot_Bottom 0x4
/* States of each line, arc, and profile */
@ -324,18 +327,19 @@
struct TProfile_
{
FT_F26Dot6 X; /* current coordinate during sweep */
PProfile link; /* link to next profile (various purposes) */
PLong offset; /* start of profile's data in render pool */
unsigned flags; /* Bit 0: profile orientation: up/down */
long height; /* profile's height in scanlines */
long start; /* profile's starting scanline */
FT_F26Dot6 X; /* current coordinate during sweep */
PProfile link; /* link to next profile (various purposes) */
PLong offset; /* start of profile's data in render pool */
unsigned flags; /* Bit 0: profile orientation (up/down) */
/* Bit 1, 2: profile overshoot (top/bottom) */
long height; /* profile's height in scanlines */
long start; /* profile's starting scanline */
unsigned countL; /* number of lines to step before this */
/* profile becomes drawable */
unsigned countL; /* number of lines to step before this */
/* profile becomes drawable */
PProfile next; /* next profile in same contour, used */
/* during drop-out control */
PProfile next; /* next profile in same contour, used */
/* during drop-out control */
};
typedef PProfile TProfileList;
@ -410,6 +414,9 @@
#define FRAC( x ) ( (x) & ( ras.precision - 1 ) )
#define SCALED( x ) ( ( (x) << ras.scale_shift ) - ras.precision_half )
#define IS_BOTTOM_OVERSHOOT( x ) ( CEILING( x ) - x >= ras.precision_half )
#define IS_TOP_OVERSHOOT( x ) ( x - FLOOR( x ) >= ras.precision_half )
/* Note that I have moved the location of some fields in the */
/* structure to ensure that the most used variables are used */
/* at the top. Thus, their offset can be coded with less */
@ -617,14 +624,18 @@
/* Create a new profile in the render pool. */
/* */
/* <Input> */
/* aState :: The state/orientation of the new profile. */
/* aState :: The state/orientation of the new profile. */
/* */
/* overshoot :: Whether the profile's unrounded start position */
/* differs by at least a half pixel. */
/* */
/* <Return> */
/* SUCCESS on success. FAILURE in case of overflow or of incoherent */
/* profile. */
/* */
static Bool
New_Profile( RAS_ARGS TStates aState )
New_Profile( RAS_ARGS TStates aState,
Bool overshoot )
{
if ( !ras.fProfile )
{
@ -639,15 +650,26 @@
return FAILURE;
}
ras.cProfile->flags = 0;
ras.cProfile->start = 0;
ras.cProfile->height = 0;
ras.cProfile->offset = ras.top;
ras.cProfile->link = (PProfile)0;
ras.cProfile->next = (PProfile)0;
switch ( aState )
{
case Ascending_State:
ras.cProfile->flags |= Flow_Up;
if ( overshoot )
ras.cProfile->flags |= Overshoot_Bottom;
FT_TRACE6(( "New ascending profile = %lx\n", (long)ras.cProfile ));
break;
case Descending_State:
ras.cProfile->flags &= ~Flow_Up;
if ( overshoot )
ras.cProfile->flags |= Overshoot_Top;
FT_TRACE6(( "New descending profile = %lx\n", (long)ras.cProfile ));
break;
@ -657,12 +679,6 @@
return FAILURE;
}
ras.cProfile->start = 0;
ras.cProfile->height = 0;
ras.cProfile->offset = ras.top;
ras.cProfile->link = (PProfile)0;
ras.cProfile->next = (PProfile)0;
if ( !ras.gProfile )
ras.gProfile = ras.cProfile;
@ -682,11 +698,15 @@
/* <Description> */
/* Finalize the current profile. */
/* */
/* <Input> */
/* overshoot :: Whether the profile's unrounded end position differs */
/* by at least a half pixel. */
/* */
/* <Return> */
/* SUCCESS on success. FAILURE in case of overflow or incoherency. */
/* */
static Bool
End_Profile( RAS_ARG )
End_Profile( RAS_ARGS Bool overshoot )
{
Long h;
PProfile oldProfile;
@ -706,15 +726,24 @@
FT_TRACE6(( "Ending profile %lx, start = %ld, height = %ld\n",
(long)ras.cProfile, ras.cProfile->start, h ));
oldProfile = ras.cProfile;
ras.cProfile->height = h;
ras.cProfile = (PProfile)ras.top;
if ( overshoot )
{
if ( ras.cProfile->flags & Flow_Up )
ras.cProfile->flags |= Overshoot_Top;
else
ras.cProfile->flags |= Overshoot_Bottom;
}
ras.top += AlignProfileSize;
oldProfile = ras.cProfile;
ras.cProfile = (PProfile)ras.top;
ras.top += AlignProfileSize;
ras.cProfile->height = 0;
ras.cProfile->offset = ras.top;
oldProfile->next = ras.cProfile;
oldProfile->next = ras.cProfile;
ras.num_Profs++;
}
@ -1328,13 +1357,15 @@
case Unknown_State:
if ( y > ras.lastY )
{
if ( New_Profile( RAS_VARS Ascending_State ) )
if ( New_Profile( RAS_VARS Ascending_State,
IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
return FAILURE;
}
else
{
if ( y < ras.lastY )
if ( New_Profile( RAS_VARS Descending_State ) )
if ( New_Profile( RAS_VARS Descending_State,
IS_TOP_OVERSHOOT( ras.lastY ) ) )
return FAILURE;
}
break;
@ -1342,8 +1373,9 @@
case Ascending_State:
if ( y < ras.lastY )
{
if ( End_Profile( RAS_VAR ) ||
New_Profile( RAS_VARS Descending_State ) )
if ( End_Profile( RAS_VARS IS_TOP_OVERSHOOT( ras.lastY ) ) ||
New_Profile( RAS_VARS Descending_State,
IS_TOP_OVERSHOOT( ras.lastY ) ) )
return FAILURE;
}
break;
@ -1351,8 +1383,9 @@
case Descending_State:
if ( y > ras.lastY )
{
if ( End_Profile( RAS_VAR ) ||
New_Profile( RAS_VARS Ascending_State ) )
if ( End_Profile( RAS_VARS IS_BOTTOM_OVERSHOOT( ras.lastY ) ) ||
New_Profile( RAS_VARS Ascending_State,
IS_BOTTOM_OVERSHOOT( ras.lastY ) ) )
return FAILURE;
}
break;
@ -1367,13 +1400,13 @@
{
case Ascending_State:
if ( Line_Up( RAS_VARS ras.lastX, ras.lastY,
x, y, ras.minY, ras.maxY ) )
x, y, ras.minY, ras.maxY ) )
return FAILURE;
break;
case Descending_State:
if ( Line_Down( RAS_VARS ras.lastX, ras.lastY,
x, y, ras.minY, ras.maxY ) )
x, y, ras.minY, ras.maxY ) )
return FAILURE;
break;
@ -1467,13 +1500,17 @@
state_bez = y1 < y3 ? Ascending_State : Descending_State;
if ( ras.state != state_bez )
{
Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
: IS_TOP_OVERSHOOT( y1 );
/* finalize current profile if any */
if ( ras.state != Unknown_State &&
End_Profile( RAS_VAR ) )
End_Profile( RAS_VARS o ) )
goto Fail;
/* create a new profile */
if ( New_Profile( RAS_VARS state_bez ) )
if ( New_Profile( RAS_VARS state_bez, o ) )
goto Fail;
}
@ -1599,11 +1636,16 @@
/* detect a change of direction */
if ( ras.state != state_bez )
{
Bool o = state_bez == Ascending_State ? IS_BOTTOM_OVERSHOOT( y1 )
: IS_TOP_OVERSHOOT( y1 );
/* finalize current profile if any */
if ( ras.state != Unknown_State &&
End_Profile( RAS_VAR ) )
End_Profile( RAS_VARS o ) )
goto Fail;
if ( New_Profile( RAS_VARS state_bez ) )
if ( New_Profile( RAS_VARS state_bez, o ) )
goto Fail;
}
@ -1903,12 +1945,15 @@
for ( i = 0; i < ras.outline.n_contours; i++ )
{
Bool o;
ras.state = Unknown_State;
ras.gProfile = NULL;
if ( Decompose_Curve( RAS_VARS (unsigned short)start,
ras.outline.contours[i],
flipped ) )
ras.outline.contours[i],
flipped ) )
return FAILURE;
start = ras.outline.contours[i] + 1;
@ -1917,15 +1962,19 @@
if ( FRAC( ras.lastY ) == 0 &&
ras.lastY >= ras.minY &&
ras.lastY <= ras.maxY )
if ( ras.gProfile &&
( ras.gProfile->flags & Flow_Up ) ==
( ras.cProfile->flags & Flow_Up ) )
if ( ras.gProfile &&
( ras.gProfile->flags & Flow_Up ) ==
( ras.cProfile->flags & Flow_Up ) )
ras.top--;
/* Note that ras.gProfile can be nil if the contour was too small */
/* to be drawn. */
lastProfile = ras.cProfile;
if ( End_Profile( RAS_VAR ) )
if ( ras.cProfile->flags & Flow_Up )
o = IS_TOP_OVERSHOOT( ras.lastY );
else
o = IS_BOTTOM_OVERSHOOT( ras.lastY );
if ( End_Profile( RAS_VARS o ) )
return FAILURE;
/* close the `next profile in contour' linked list */
@ -2237,11 +2286,10 @@
/* Drop-out Control Rules #4 and #6 */
/* The spec is not very clear regarding those rules. It */
/* presents a method that is way too costly to implement */
/* while the general idea seems to get rid of `stubs'. */
/* The specification neither provides an exact definition */
/* of a `stub' nor gives exact rules to exclude them. */
/* */
/* Here, we only get rid of stubs recognized if: */
/* Here the constraints we use to recognize a stub. */
/* */
/* upper stub: */
/* */
@ -2255,22 +2303,26 @@
/* - P_Left is the successor of P_Right in that contour */
/* - y is the bottom of P_Left */
/* */
/* We draw a stub if the following constraints are met. */
/* */
/* - for an upper or lower stub, there is top or bottom */
/* overshoot, respectively */
/* - the covered interval is greater or equal to a half */
/* pixel */
/* FIXXXME: uncommenting this line solves the disappearing */
/* bit problem in the `7' of verdana 10pts, but */
/* makes a new one in the `C' of arial 14pts */
#if 0
if ( x2 - x1 < ras.precision_half )
#endif
{
/* upper stub test */
if ( left->next == right && left->height <= 0 )
return;
/* upper stub test */
if ( left->next == right &&
left->height <= 0 &&
!( left->flags & Overshoot_Top &&
x2 - x1 >= ras.precision_half ) )
return;
/* lower stub test */
if ( right->next == left && left->start == y )
return;
}
/* lower stub test */
if ( right->next == left &&
left->start == y &&
!( left->flags & Overshoot_Bottom &&
x2 - x1 >= ras.precision_half ) )
return;
if ( ras.dropOutControl == 1 )
pxl = e2;
@ -2432,11 +2484,17 @@
/* see Vertical_Sweep_Drop for details */
/* rightmost stub test */
if ( left->next == right && left->height <= 0 )
if ( left->next == right &&
left->height <= 0 &&
!( left->flags & Overshoot_Top &&
x2 - x1 >= ras.precision_half ) )
return;
/* leftmost stub test */
if ( right->next == left && left->start == y )
if ( right->next == left &&
left->start == y &&
!( left->flags & Overshoot_Bottom &&
x2 - x1 >= ras.precision_half ) )
return;
if ( ras.dropOutControl == 1 )
@ -3064,7 +3122,7 @@
Set_High_Precision( RAS_VARS ras.outline.flags &
FT_OUTLINE_HIGH_PRECISION );
FT_OUTLINE_HIGH_PRECISION );
ras.scale_shift = ras.precision_shift;
if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )
@ -3140,7 +3198,7 @@
Set_High_Precision( RAS_VARS ras.outline.flags &
FT_OUTLINE_HIGH_PRECISION );
FT_OUTLINE_HIGH_PRECISION );
ras.scale_shift = ras.precision_shift + 1;
if ( ras.outline.flags & FT_OUTLINE_IGNORE_DROPOUTS )