Commit 72271140434028186a49a5dc5925f0727559e46f

Werner Lemberg 2009-06-18T15:42:52

Fix B/W rasterization of subglyphs with different drop-out modes. Normally, the SCANMODE instruction (if present) to set the drop-out mode in a TrueType font is located in the `prep' table only and thus valid for all glyphs. However, there are fonts like `pala.ttf' which additionally contain this instruction in the hinting code of some glyphs (but not all). As a result it can happen that a composite glyph needs multiple drop-out modes for its subglyphs since the rendering state gets reset for each subglyph. FreeType collects the hinted outlines from all subglyphs, then it sends the data to the rasterizer. It also sends the drop-out mode -- after hinting has been applied -- and here is the error: It sends the drop-out mode of the last subglyph only; drop-out modes of all other subglyphs are lost. This patch fixes the problem; it adds a second, alternative mechanism to pass the drop-out mode: For each contour, the rasterizer now checks the first `tags' array element. If bit 2 is set, bits 5-7 contain the contour's drop-out mode, overriding the global drop-out mode. * include/freetype/ftimage.h (FT_CURVE_TAG_HAS_SCANMODE): New macro. * src/truetype/ttgload.c (TT_Hint_Glyph): Store drop-out mode in `tags[0]'. * src/raster/ftraster.c (Flow_Up, Overshoot_Top, Overshoot_Bottom): Use bits 3-5 instead of 0-2. (New_Profile): Set the drop-out mode in the profile's `flags' field. (Decompose_Curve): Check `tags[0]' and set `dropOutControl' if necessary. (Vertical_Sweep_Drop, Horizontal_Sweep_Drop, Horizontal_Gray_Sweep_Drop, Draw_Sweep): Use the profile's drop-out mode.

diff --git a/ChangeLog b/ChangeLog
index 22b2a34..568f4d3 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,41 @@
+2009-06-18  Werner Lemberg  <wl@gnu.org>
+
+	Fix B/W rasterization of subglyphs with different drop-out modes.
+
+	Normally, the SCANMODE instruction (if present) to set the drop-out
+	mode in a TrueType font is located in the `prep' table only and thus
+	valid for all glyphs.  However, there are fonts like `pala.ttf'
+	which additionally contain this instruction in the hinting code of
+	some glyphs (but not all).  As a result it can happen that a
+	composite glyph needs multiple drop-out modes for its subglyphs
+	since the rendering state gets reset for each subglyph.
+
+	FreeType collects the hinted outlines from all subglyphs, then it
+	sends the data to the rasterizer.  It also sends the drop-out mode
+	-- after hinting has been applied -- and here is the error: It sends
+	the drop-out mode of the last subglyph only; drop-out modes of all
+	other subglyphs are lost.
+
+	This patch fixes the problem; it adds a second, alternative
+	mechanism to pass the drop-out mode: For each contour, the
+	rasterizer now checks the first `tags' array element.  If bit 2 is
+	set, bits 5-7 contain the contour's drop-out mode, overriding the
+	global drop-out mode.
+
+	* include/freetype/ftimage.h (FT_CURVE_TAG_HAS_SCANMODE): New macro.
+
+	* src/truetype/ttgload.c (TT_Hint_Glyph): Store drop-out mode in
+	`tags[0]'.
+
+	* src/raster/ftraster.c (Flow_Up, Overshoot_Top, Overshoot_Bottom):
+	Use bits 3-5 instead of 0-2.
+	(New_Profile): Set the drop-out mode in the profile's `flags' field.
+	(Decompose_Curve): Check `tags[0]' and set `dropOutControl' if
+	necessary.
+	(Vertical_Sweep_Drop, Horizontal_Sweep_Drop,
+	Horizontal_Gray_Sweep_Drop, Draw_Sweep): Use the profile's drop-out
+	mode.
+
 2009-06-16  Werner Lemberg  <wl@gnu.org>
 
 	Improve scan conversion rules 4 and 6.
diff --git a/include/freetype/ftimage.h b/include/freetype/ftimage.h
index 61882e0..171f1b3 100644
--- a/include/freetype/ftimage.h
+++ b/include/freetype/ftimage.h
@@ -318,14 +318,23 @@ FT_BEGIN_HEADER
   /*                  elements, giving the outline's point coordinates.    */
   /*                                                                       */
   /*    tags       :: A pointer to an array of `n_points' chars, giving    */
-  /*                  each outline point's type.  If bit~0 is unset, the   */
-  /*                  point is `off' the curve, i.e., a Bézier control     */
-  /*                  point, while it is `on' when set.                    */
+  /*                  each outline point's type.                           */
+  /*                                                                       */
+  /*                  If bit~0 is unset, the point is `off' the curve,     */
+  /*                  i.e., a Bézier control point, while it is `on' if    */
+  /*                  set.                                                 */
   /*                                                                       */
   /*                  Bit~1 is meaningful for `off' points only.  If set,  */
   /*                  it indicates a third-order Bézier arc control point; */
   /*                  and a second-order control point if unset.           */
   /*                                                                       */
+  /*                  If bit~2 is set, bits 5-7 contain the drop-out mode  */
+  /*                  (as defined in the OpenType specification; the value */
+  /*                  is the same as the argument to the SCANMODE          */
+  /*                  instruction).                                        */
+  /*                                                                       */
+  /*                  Bits 3 and~4 are reserved for internal purposes.     */
+  /*                                                                       */
   /*    contours   :: An array of `n_contours' shorts, giving the end      */
   /*                  point of each contour within the outline.  For       */
   /*                  example, the first contour is defined by the points  */
@@ -336,6 +345,12 @@ FT_BEGIN_HEADER
   /*                  and give hints to the scan-converter and hinter on   */
   /*                  how to convert/grid-fit it.  See @FT_OUTLINE_FLAGS.  */
   /*                                                                       */
+  /* <Note>                                                                */
+  /*    The B/W rasterizer only checks bit~2 in the `tags' array for the   */
+  /*    first point of each contour.  The drop-out mode as given with      */
+  /*    @FT_OUTLINE_IGNORE_DROPOUTS, @FT_OUTLINE_SMART_DROPOUTS, and       */
+  /*    @FT_OUTLINE_INCLUDE_STUBS in `flags' is then overridden.           */
+  /*                                                                       */
   typedef struct  FT_Outline_
   {
     short       n_contours;      /* number of contours in glyph        */
@@ -371,7 +386,7 @@ FT_BEGIN_HEADER
   /*    FT_OUTLINE_EVEN_ODD_FILL ::                                        */
   /*      By default, outlines are filled using the non-zero winding rule. */
   /*      If set to 1, the outline will be filled using the even-odd fill  */
-  /*      rule (only works with the smooth raster).                        */
+  /*      rule (only works with the smooth rasterizer).                    */
   /*                                                                       */
   /*    FT_OUTLINE_REVERSE_FILL ::                                         */
   /*      By default, outside contours of an outline are oriented in       */
@@ -384,15 +399,17 @@ FT_BEGIN_HEADER
   /*      By default, the scan converter will try to detect drop-outs in   */
   /*      an outline and correct the glyph bitmap to ensure consistent     */
   /*      shape continuity.  If set, this flag hints the scan-line         */
-  /*      converter to ignore such cases.                                  */
+  /*      converter to ignore such cases.  See below for more information. */
   /*                                                                       */
   /*    FT_OUTLINE_SMART_DROPOUTS ::                                       */
   /*      Select smart dropout control.  If unset, use simple dropout      */
-  /*      control.  Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set.         */
+  /*      control.  Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set.  See    */
+  /*      below for more information.                                      */
   /*                                                                       */
   /*    FT_OUTLINE_INCLUDE_STUBS ::                                        */
   /*      If set, turn pixels on for `stubs', otherwise exclude them.      */
-  /*      Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set.                   */
+  /*      Ignored if @FT_OUTLINE_IGNORE_DROPOUTS is set.  See below for    */
+  /*      more information.                                                */
   /*                                                                       */
   /*    FT_OUTLINE_HIGH_PRECISION ::                                       */
   /*      This flag indicates that the scan-line converter should try to   */
@@ -409,6 +426,13 @@ FT_BEGIN_HEADER
   /*      scan-converter.                                                  */
   /*                                                                       */
   /* <Note>                                                                */
+  /*    The flags @FT_OUTLINE_IGNORE_DROPOUTS, @FT_OUTLINE_SMART_DROPOUTS, */
+  /*    and @FT_OUTLINE_INCLUDE_STUBS are ignored by the smooth            */
+  /*    rasterizer.                                                        */
+  /*                                                                       */
+  /*    There exists a second mechanism to pass the drop-out mode to the   */
+  /*    B/W rasterizer; see the `tags' field in @FT_Outline.               */
+  /*                                                                       */
   /*    Please refer to the description of the `SCANTYPE' instruction in   */
   /*    the OpenType specification (in file `ttinst1.doc') how simple      */
   /*    drop-outs, smart drop-outs, and stubs are defined.                 */
@@ -455,15 +479,17 @@ FT_BEGIN_HEADER
 
 #define FT_CURVE_TAG( flag )  ( flag & 3 )
 
-#define FT_CURVE_TAG_ON           1
-#define FT_CURVE_TAG_CONIC        0
-#define FT_CURVE_TAG_CUBIC        2
+#define FT_CURVE_TAG_ON            1
+#define FT_CURVE_TAG_CONIC         0
+#define FT_CURVE_TAG_CUBIC         2
+
+#define FT_CURVE_TAG_HAS_SCANMODE  4
 
-#define FT_CURVE_TAG_TOUCH_X      8  /* reserved for the TrueType hinter */
-#define FT_CURVE_TAG_TOUCH_Y     16  /* reserved for the TrueType hinter */
+#define FT_CURVE_TAG_TOUCH_X       8  /* reserved for the TrueType hinter */
+#define FT_CURVE_TAG_TOUCH_Y      16  /* reserved for the TrueType hinter */
 
-#define FT_CURVE_TAG_TOUCH_BOTH  ( FT_CURVE_TAG_TOUCH_X | \
-                                   FT_CURVE_TAG_TOUCH_Y )
+#define FT_CURVE_TAG_TOUCH_BOTH    ( FT_CURVE_TAG_TOUCH_X | \
+                                     FT_CURVE_TAG_TOUCH_Y )
 
 #define FT_Curve_Tag_On       FT_CURVE_TAG_ON
 #define FT_Curve_Tag_Conic    FT_CURVE_TAG_CONIC
diff --git a/src/raster/ftraster.c b/src/raster/ftraster.c
index c8c13f2..4547dc9 100644
--- a/src/raster/ftraster.c
+++ b/src/raster/ftraster.c
@@ -306,9 +306,9 @@
 
 
   /* values for the `flags' bit field */
-#define Flow_Up           0x1
-#define Overshoot_Top     0x2
-#define Overshoot_Bottom  0x4
+#define Flow_Up           0x8
+#define Overshoot_Top     0x10
+#define Overshoot_Bottom  0x20
 
 
   /* States of each line, arc, and profile */
@@ -330,8 +330,10 @@
     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) */
+    unsigned    flags;       /* Bit 0-2: drop-out mode                   */
+                             /* Bit 3: profile orientation (up/down)     */
+                             /* Bit 4: is top profile?                   */
+                             /* Bit 5: is bottom profile?                */
     long        height;      /* profile's height in scanlines            */
     long        start;       /* profile's starting scanline              */
 
@@ -656,6 +658,7 @@
     ras.cProfile->offset = ras.top;
     ras.cProfile->link   = (PProfile)0;
     ras.cProfile->next   = (PProfile)0;
+    ras.cProfile->flags  = ras.dropOutControl;
 
     switch ( aState )
     {
@@ -1739,7 +1742,12 @@
 
     point = points + first;
     tags  = ras.outline.tags + first;
-    tag   = FT_CURVE_TAG( tags[0] );
+
+    /* set scan mode if necessary */
+    if ( tags[0] & FT_CURVE_TAG_HAS_SCANMODE )
+      ras.dropOutControl = (Byte)tags[0] >> 5;
+
+    tag = FT_CURVE_TAG( tags[0] );
 
     /* A contour cannot start with a cubic control point! */
     if ( tag == FT_CURVE_TAG_CUBIC )
@@ -2269,9 +2277,12 @@
 
     if ( e1 > e2 )
     {
+      Int  dropOutControl = left->flags & 7;
+
+
       if ( e1 == e2 + ras.precision )
       {
-        switch ( ras.dropOutControl )
+        switch ( dropOutControl )
         {
         case 0: /* simple drop-outs including stubs */
           pxl = e2;
@@ -2324,7 +2335,7 @@
                   x2 - x1 >= ras.precision_half  ) )
             return;
 
-          if ( ras.dropOutControl == 1 )
+          if ( dropOutControl == 1 )
             pxl = e2;
           else
             pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
@@ -2467,9 +2478,12 @@
 
     if ( e1 > e2 )
     {
+      Int  dropOutControl = left->flags & 7;
+
+
       if ( e1 == e2 + ras.precision )
       {
-        switch ( ras.dropOutControl )
+        switch ( dropOutControl )
         {
         case 0: /* simple drop-outs including stubs */
           pxl = e2;
@@ -2497,7 +2511,7 @@
                   x2 - x1 >= ras.precision_half  ) )
             return;
 
-          if ( ras.dropOutControl == 1 )
+          if ( dropOutControl == 1 )
             pxl = e2;
           else
             pxl = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
@@ -2723,9 +2737,12 @@
 
     if ( e1 > e2 )
     {
+      Int  dropOutControl = left->flags & 7;
+
+
       if ( e1 == e2 + ras.precision )
       {
-        switch ( ras.dropOutControl )
+        switch ( dropOutControl )
         {
         case 0: /* simple drop-outs including stubs */
           e1 = e2;
@@ -2747,7 +2764,7 @@
           if ( right->next == left && left->start == y )
             return;
 
-          if ( ras.dropOutControl == 1 )
+          if ( dropOutControl == 1 )
             e1 = e2;
           else
             e1 = FLOOR( ( x1 + x2 - 1 ) / 2 + ras.precision_half );
@@ -2928,7 +2945,10 @@
           {
             if ( e1 > e2 || e2 == e1 + ras.precision )
             {
-              if ( ras.dropOutControl != 2 )
+              Int  dropOutControl = P_Left->flags & 7;
+
+
+              if ( dropOutControl != 2 )
               {
                 /* a drop-out was detected */
 
diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c
index ad2b62e..38023b8 100644
--- a/src/truetype/ttgload.c
+++ b/src/truetype/ttgload.c
@@ -680,6 +680,9 @@
       FT_Bool   debug;
       FT_Error  error;
 
+      FT_GlyphLoader  gloader         = loader->gloader;
+      FT_Outline      current_outline = gloader->current.outline;
+
 
       error = TT_Set_CodeRange( loader->exec, tt_coderange_glyph,
                                 loader->exec->glyphIns, n_ins );
@@ -695,6 +698,10 @@
       error = TT_Run_Context( loader->exec, debug );
       if ( error && loader->exec->pedantic_hinting )
         return error;
+
+      /* store drop-out mode in bits 5-7; set bit 2 also as a marker */
+      current_outline.tags[0] |=
+        ( loader->exec->GS.scan_type << 5 ) | FT_CURVE_TAG_HAS_SCANMODE;
     }
 
 #endif