View Issue Details

IDProjectCategoryView StatusLast Update
0037033LazarusLazUtilspublic2020-05-11 09:42
Reportercircular Assigned ToJuha Manninen  
PrioritynormalSeverityminorReproducibilityhave not tried
Status resolvedResolutionfixed 
Product Version2.0.8 
Summary0037033: LazFreeType graphics state initialization
DescriptionWhen using letters with circumflex such as "â" with the font Liberation Serif, the interpreter makes an error and the glyph is not rendered.

I believe the problem is that the graphics state (GS) is not initialized for each subglyphs. The documentation of FreeType is not really clear on this topic, saying that the GS must be initialized for each glyph, not specifying if it concerns subglyphs, i.e. glyphs that make up another composite glyph.

In this case, the hint program for the circumflex does not initialize the rp2 register, so I suppose it expects it to be zero as it is normally after the initialization.
Steps To ReproduceRun attached test project. It should display:
"Various accents á, à, â and ä"
Additional InformationTo fix the problem, the following lines in "ttgload.pas"

           if instance^.GS.instruct_control and 2 <> 0 then
             exec^.GS := Default_GraphicsState
           else
             exec^.GS := instance^.GS;

must be moved just before calling Load_Simple_Glyph
TagsNo tags attached.
Fixed in Revisionr63128, r63134
LazTarget-
Widgetset
Attached Files

Activities

circular

2020-05-07 09:17

developer  

testfreetype3.zip (159,109 bytes)

Juha Manninen

2020-05-09 10:15

developer   ~0122680

Last edited: 2020-05-09 10:16

View 3 revisions

The test program creates a range check error in function TFreeTypeGlyph.GetBoundsWithOffset.
Tested on Linux with QT5 bindings.
Please create a patch for the fix.

---
#0 SYSTEM_$$_HANDLEERRORADDRFRAME$LONGINT$POINTER$POINTER at :0
0000001 SYSTEM_$$_HANDLEERRORADDRFRAMEIND$LONGINT$POINTER$POINTER at :0
0000002 GETBOUNDSWITHOFFSET(0x7fffffffb9c0, 6.21585871e-39, -nan(0x7fb97c)) at easylazfreetype.pas:769
0000003 GETBOUNDSWITHOFFSET(0x7ffff7fb6c40, 115.5, 126) at easylazfreetype.pas:769
0000004 RENDERDIRECTLY(0x7ffff7fb6c40, 0x7fffeb55ccc0, 115.5, 126, {LEFT = 0, TOP = 0, RIGHT = 320, BOTTOM = 240, TOPLEFT = {X = 0, Y = 0}, BOTTOMRIGHT = {X = 320, Y = 240}, VECTOR = {0, 0, 320, 240}}, {Proc = {procedure (POINTER, LONGINT, LONGINT, LONGINT, POINTER)} 0x7fffffffbaa8, Self = 0x7ffff7fb6840}, GRQHIGHQUALITY, false) at easylazfreetype.pas:804
0000005 RENDERDIRECTLY(0x7ffff7fb6c40, 115.5, 126, {LEFT = 0, TOP = 0, RIGHT = 320, BOTTOM = 240, TOPLEFT = {X = 0, Y = 0}, BOTTOMRIGHT = {X = 320, Y = 240}, VECTOR = {0, 0, 320, 240}}, {Proc = {procedure (POINTER, LONGINT, LONGINT, LONGINT, POINTER)} 0x7fffffffbb50, Self = 0x7ffff7fb6840}, GRQHIGHQUALITY, false) at easylazfreetype.pas:785
0000006 RENDERTEXT(0x7fffeb56c040, 0x8b8c18 'Various accents '0000195#161', '0000195#160', '0000195..., 115.5, 125.865532, {LEFT = 0, TOP = 0, RIGHT = 320, BOTTOM = 240, TOPLEFT = {X = 0, Y = 0}, BOTTOMRIGHT = {X = 320, Y = 240}, VECTOR = {0, 0, 320, 240}}, {Proc = {procedure (POINTER, LONGINT, LONGINT, LONGINT, POINTER)} 0x7fffffffbca0, Self = 0x7ffff7fb6840}) at easylazfreetype.pas:1512
0000007 DRAWTEXT(0x7ffff7fb6840, 0x8b8c18 'Various accents '0000195#161', '0000195#160', '0000195..., 0x7fffeb56c040, 57.5, 125.865532, {RED = 0, GREEN = 0, BLUE = 0, ALPHA = 65535}) at lazfreetypefpimagedrawer.pas:451
0000008 FORMPAINT(0x7ffff2df9910, 0x7ffff2df9910) at umain.pas:59
0000009 PAINT(0x7ffff2df9910) at include/customcontrol.inc:59
0000010 PAINTWINDOW(0x7ffff2df9910, 140737141688448) at include/customform.inc:1102
0000011 PAINTHANDLER(0x7ffff2df9910, {MSG = 15, UNUSEDMSG = 0, DC = 140737141688448, PAINTSTRUCT = 0x7ffff7f9e360, RESULT = 0}) at include/wincontrol.inc:4857
0000012 WMPAINT(0x7ffff2df9910, {MSG = 15, UNUSEDMSG = 0, DC = 140737141688448, PAINTSTRUCT = 0x7ffff7f9e360, RESULT = 0}) at include/wincontrol.inc:6845
0000013 WMPAINT(0x7ffff2df9910, {MSG = 15, UNUSEDMSG = 0, DC = 140737141688448, PAINTSTRUCT = 0x7ffff7f9e360, RESULT = 0}) at include/customcontrol.inc:98
0000014 SYSTEM$_$TOBJECT_$__$$_DISPATCH$formal at :0

circular

2020-05-09 15:59

developer   ~0122682

I don't know why you get this error.

Here is the patch, that may fix it incidentally.

As well in the patch:
- removed redundant assignment to ins^.GS in TTObjs line 1725 (assignment already done in Context_Save)
- replaced code page 850 superscript 2 character by "**2"
graphic_state.diff (1,897 bytes)   
Index: components/lazutils/ttgload.pas
===================================================================
--- components/lazutils/ttgload.pas	(révision 63126)
+++ components/lazutils/ttgload.pas	(copie de travail)
@@ -745,10 +745,6 @@
      end;
 
    Context_Load( exec, instance );
-   if instance^.GS.instruct_control and 2 <> 0 then
-     exec^.GS := Default_GraphicsState
-   else
-     exec^.GS := instance^.GS;
 
    glyph^.outline.high_precision := ( instance^.metrics.y_ppem < 24 );
 
@@ -925,6 +921,11 @@
              if load_top > 0 then
                new_flags := new_flags and not TT_Load_Debug;
 
+           if instance^.GS.instruct_control and 2 <> 0 then
+             exec^.GS := Default_GraphicsState
+           else
+             exec^.GS := instance^.GS;
+
            if Load_Simple_Glyph(
                        ftstream,
                        exec,
Index: components/lazutils/ttobjs.pas
===================================================================
--- components/lazutils/ttobjs.pas	(révision 63126)
+++ components/lazutils/ttobjs.pas	(copie de travail)
@@ -335,7 +335,7 @@
   (*                                                                     *)
   (*     - if projVector is horizontal, ratio = x_ratio = 1.0            *)
   (*     - if projVector is vertical,   ratop = y_ratio                  *)
-  (*     - else, ratio = sqrt( (proj.x*x_ratio)�+(proj.y*y_ratio)� )     *)
+  (*     - else, ratio = sqrt( (proj.x*x_ratio)**2+(proj.y*y_ratio)**2 ) *)
   (*                                                                     *)
   (*   reading a cvt value returns      ratio*cvt[index]                 *)
   (*   writing a cvt value in pixels    cvt[index]/ratio                 *)
@@ -1722,7 +1722,6 @@
         ( (not debug) and Run_Ins( @exec^ ) ) then
        goto Fin;
 
-   ins^.GS        := exec^.GS;
    Instance_Reset := Success;
 
  Fin:
graphic_state.diff (1,897 bytes)   

Juha Manninen

2020-05-09 20:51

developer   ~0122684

Last edited: 2020-05-09 21:23

View 3 revisions

> I don't know why you get this error.

Maybe because I build the whole IDE + its packages with all debug flags including -Cr (range check).
I recommend you do the same thing. Select "Debug IDE" profile from the Configure Build Lazarus window.
Note, packages like LazUtils are compiled with the IDE's build flags because they have $(IDEBuildOptions) macro.
You should build test programs with debug flags, too. Create a "Debug" BuildMode for that. There is a button for Debug and Release modes.

> Here is the patch, that may fix it incidentally.

I applied it in r63128. I believe it is OK but it is not related to the range error.
In r63130 I removed {$hints off}. The hints correctly tell you that variable "metrics" is not initialized in 3 places, including TFreeTypeGlyph.GetBoundsWithOffset.
It is passed as a var parameter for TT_Get_Glyph_Metrics() which does not always set its values. Either initialize it before calling TT_Get_Glyph_Metrics() or make it an out parameter in TT_Get_Glyph_Metrics() and set it always there.
In my tests the values in gmetrics.bbox were either MaxInt or -MaxInt. Tested with Linux 64bit FPC trunk from few weeks ago.

[Edit] Actually TT_Get_Glyph_Metrics sets values from glyph^ and returns TT_Err_Ok.
  gmetrics.bbox := glyph^.metrics.bbox;
The wrong bbox values must come from there.

circular

2020-05-10 18:49

developer   ~0122702

I was going to submit a patch to fix the "generic" variable name. I see that you fixed it already.

Compiling in debug mode raised the error indeed. I fixed the calls on both side to avoid any surprise.

Though the error was still happening. The problem is that the bounding box of space " " was not computed correctly. The function TT_Get_Outline_BBox that computes the bounding box sets the minimum and maximum to big numbers at the beginning, but if there are no points, those artificial values are kept. So I fixed that and now it seems fine.
bounding_box_space.diff (5,317 bytes)   
Index: components/lazutils/easylazfreetype.pas
===================================================================
--- components/lazutils/easylazfreetype.pas	(révision 63132)
+++ components/lazutils/easylazfreetype.pas	(copie de travail)
@@ -747,10 +747,12 @@
 var
   metrics: TT_Glyph_Metrics;
 begin
-  TT_Get_Glyph_Metrics(FGlyphData, metrics);
-  with metrics.bbox do
-    result := rect(IncludeFullGrainMin(xMin,64) div 64,IncludeFullGrainMin(-yMax,64) div 64,
-       (IncludeFullGrainMax(xMax,64)+1) div 64,(IncludeFullGrainMax(-yMin,64)+1) div 64);
+  if TT_Get_Glyph_Metrics(FGlyphData, metrics) = TT_Err_Ok then
+    with metrics.bbox do
+      result := rect(IncludeFullGrainMin(xMin,64) div 64,IncludeFullGrainMin(-yMax,64) div 64,
+         (IncludeFullGrainMax(xMax,64)+1) div 64,(IncludeFullGrainMax(-yMin,64)+1) div 64)
+  else
+      result := TRect.Empty;
 end;
 
 function TFreeTypeGlyph.GetAdvance: single;
@@ -757,8 +759,10 @@
 var
   metrics: TT_Glyph_Metrics;
 begin
-  TT_Get_Glyph_Metrics(FGlyphData, metrics);
-  result := metrics.advance/64;
+  if TT_Get_Glyph_Metrics(FGlyphData, metrics) = TT_Err_Ok then
+    result := metrics.advance/64
+  else
+    result := 0;
 end;
 
 function TFreeTypeGlyph.GetBoundsWithOffset(x, y: single): TRect;
@@ -765,12 +769,15 @@
 var
   metrics: TT_Glyph_Metrics;
 begin
-  TT_Get_Glyph_Metrics(FGlyphData, metrics);
-  with metrics.bbox do
-    result := rect(IncludeFullGrainMin(xMin+round(x*64), 64) div 64,
-                   IncludeFullGrainMin(-yMax+round(y*64),64) div 64,
-                  (IncludeFullGrainMax(xMax+round(x*64), 64)+1) div 64,
-                  (IncludeFullGrainMax(-yMin+round(y*64),64)+1) div 64);
+  if TT_Get_Glyph_Metrics(FGlyphData, metrics) = TT_Err_Ok then
+  begin
+    with metrics.bbox do
+      result := rect(IncludeFullGrainMin(xMin+round(x*64),64) div 64,
+                     IncludeFullGrainMin(-yMax+round(y*64),64) div 64,
+                    (IncludeFullGrainMax(xMax+round(x*64),64)+1) div 64,
+                    (IncludeFullGrainMax(-yMin+round(y*64),64)+1) div 64);
+  end else
+    result := TRect.Empty;
 end;
 
 constructor TFreeTypeGlyph.Create(AFont: TFreeTypeFont; AIndex: integer);
Index: components/lazutils/lazfreetype.pas
===================================================================
--- components/lazutils/lazfreetype.pas	(révision 63132)
+++ components/lazutils/lazfreetype.pas	(copie de travail)
@@ -254,13 +254,13 @@
   (*  Get a glyph's metrics                                        *)
   (*                                                               *)
   function TT_Get_Glyph_Metrics( _glyph       : TT_Glyph;
-                                 var gmetrics : TT_Glyph_Metrics ) : TT_Error;
+                                 out gmetrics : TT_Glyph_Metrics ) : TT_Error;
 
   (*****************************************************************)
   (*  Get a glyph's big metrics                                    *)
   (*                                                               *)
   function TT_Get_Glyph_Big_Metrics( _glyph       : TT_Glyph;
-                                     var gmetrics : TT_Big_Glyph_Metrics
+                                     out gmetrics : TT_Big_Glyph_Metrics
                                    ) : TT_Error;
 
   (*****************************************************************)
@@ -1027,7 +1027,7 @@
   (*                                                               *)
   (*                                                               *)
   function TT_Get_Glyph_Metrics( _glyph       : TT_Glyph;
-                                 var gmetrics : TT_Glyph_Metrics ) : TT_Error;
+                                 out gmetrics : TT_Glyph_Metrics ) : TT_Error;
   var
     glyph : PGlyph;
   begin
@@ -1041,7 +1041,10 @@
         TT_Get_Glyph_Metrics := TT_Err_Ok;
       end
     else
-      TT_Get_Glyph_Metrics := TT_Err_Invalid_Glyph_Handle;
+      begin
+        fillchar(gmetrics, sizeof(gmetrics), 0);
+        TT_Get_Glyph_Metrics := TT_Err_Invalid_Glyph_Handle;
+      end;
   end;
 
   (*****************************************************************)
@@ -1048,7 +1051,7 @@
   (*  Get a glyph's big metrics                                    *)
   (*                                                               *)
   function TT_Get_Glyph_Big_Metrics( _glyph       : TT_Glyph;
-                                     var gmetrics : TT_Big_Glyph_Metrics
+                                     out gmetrics : TT_Big_Glyph_Metrics
                                    ) : TT_Error;
   var
     glyph : PGlyph;
@@ -1060,7 +1063,10 @@
         TT_Get_Glyph_Big_Metrics := TT_Err_Ok;
       end
     else
-      TT_Get_Glyph_Big_Metrics := TT_Err_Invalid_Glyph_Handle;
+      begin
+        fillchar(gmetrics, sizeof(gmetrics), 0);
+        TT_Get_Glyph_Big_Metrics := TT_Err_Invalid_Glyph_Handle;
+      end;
   end;
 
   (*****************************************************************)
@@ -1345,8 +1351,17 @@
     n          : Int;
   begin
 
+    if out.n_points-nbPhantomPoints <= 0 then
     with bbox do
     begin
+      xMin := 0;
+      xMax := 0;
+      yMin := 0;
+      yMax := 0;
+    end
+    else
+    with bbox do
+    begin
       xMin := $7FFFFFFF;
       xMax := -$80000000;
       yMin := $7FFFFFFF;
bounding_box_space.diff (5,317 bytes)   

Juha Manninen

2020-05-10 20:56

developer   ~0122703

Applied, thanks.
Looks good now! :)

circular

2020-05-11 09:42

developer   ~0122710

We're getting there :)

Issue History

Date Modified Username Field Change
2020-05-07 09:17 circular New Issue
2020-05-07 09:17 circular File Added: testfreetype3.zip
2020-05-09 10:15 Juha Manninen Note Added: 0122680
2020-05-09 10:16 Juha Manninen Note Edited: 0122680 View Revisions
2020-05-09 10:16 Juha Manninen Note Edited: 0122680 View Revisions
2020-05-09 15:59 circular Note Added: 0122682
2020-05-09 15:59 circular File Added: graphic_state.diff
2020-05-09 19:46 Juha Manninen Assigned To => Juha Manninen
2020-05-09 19:46 Juha Manninen Status new => assigned
2020-05-09 20:51 Juha Manninen Fixed in Revision => r63128
2020-05-09 20:51 Juha Manninen LazTarget => -
2020-05-09 20:51 Juha Manninen Note Added: 0122684
2020-05-09 20:56 Juha Manninen Note Edited: 0122684 View Revisions
2020-05-09 21:08 Juha Manninen Status assigned => feedback
2020-05-09 21:23 Juha Manninen Note Edited: 0122684 View Revisions
2020-05-10 18:49 circular Note Added: 0122702
2020-05-10 18:49 circular File Added: bounding_box_space.diff
2020-05-10 18:49 circular Status feedback => assigned
2020-05-10 20:56 Juha Manninen Status assigned => resolved
2020-05-10 20:56 Juha Manninen Resolution open => fixed
2020-05-10 20:56 Juha Manninen Fixed in Revision r63128 => r63128, r63134
2020-05-10 20:56 Juha Manninen Note Added: 0122703
2020-05-11 09:42 circular Note Added: 0122710