Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Algorithmus für Text zu Polygon/TPath (Vector) (https://www.delphipraxis.net/189802-algorithmus-fuer-text-zu-polygon-tpath-vector.html)

milos 24. Jul 2016 08:18

Algorithmus für Text zu Polygon/TPath (Vector)
 
Hi,

ich hätte gerne die Möglichkeit einen Text mit bestimmter Schriftart zu einem Polygon (oder vielleicht Delphi-Referenz durchsuchenTPath (Vector)?) zu konvertieren.
Ich dachte dabei, dass ich den Text (gross genug) auf ein TBitmap zeichnen lasse und das gezeichnete dann mit einem Algorithmus "scanne".

Gibt es da gute und schnelle Algorithmen die das auch schön hinbekommen? Wie macht man z.B. Buchstaben mit Leerräumen dazwischen? Da müsste ich doch mit mehreren Polygonen arbeiten oder?

Freundliche Grüsse

Uwe Raabe 24. Jul 2016 08:50

AW: Algorithmus für Text zu Polygon/TPath (Vector)
 
Wenn es für TrueType Fonts reicht, könnte das hier ein Anfang sein. Der Code ist zwar ungefähr so alt wie du, aber mit ein bisschen Refactoring und Anpassungen könnte das eine gute Basis sein.

milos 24. Jul 2016 11:24

AW: Algorithmus für Text zu Polygon/TPath (Vector)
 
Hallo Uwe,

Danke für den Link. Sitze leider gerade im Zug und kann erst morgen das ganze genauer in der IDE ansehen. Auch scheint mir der Code zu sehr von der Windows API abzuhängen. (z.B. GetGlyphOutline/GetGlyphOutlineW und diverse andere Klassen/Strukturen die wichtig zu sein scheinen) Das müsste jedoch auch auf Android laufen, wenn das schon implementiert werden soll.

Ich weiss wie gesagt leider nicht was da im Hintergrund passiert aber ich dachte zunächst an etwas wie den A*-Algorithmus. Bei dem kann ich jedoch keine Leerräume finden und das Ergebnis war mehr "Windings" als "Arial". :-D

PS: Die erste Version des Source Codes ist 2 Monate jünger als ich. :lol: Das ganze Pointergedöns hätte mir aber Angst eingejagt. :stupid:

Freundliche Grüsse

Uwe Raabe 24. Jul 2016 13:48

AW: Algorithmus für Text zu Polygon/TPath (Vector)
 
Im Wesentlichen interpretiert man hier die Vektordefinition des TrueType. Für andere Plattformen müsste man das aber noch selbst implementierten.

milos 25. Jul 2016 01:11

AW: Algorithmus für Text zu Polygon/TPath (Vector)
 
Hi,

habe vorhin angefangen das Ding einfach irgendwie zu implementieren, was (sehr unerwartet) sehr gut funktioniert hat.
Beim optimieren bin ich jedoch auf Delphi-Referenz durchsuchenFMX.TextLayout gestossen welche die Klasse Delphi-Referenz durchsuchenTTextLayout beinhaltet, die es anscheinend ermöglicht auf jeder Platform einen Text mit gewünschtem Font in ein TPathData umzuwandeln.

Beispielcode mit leerem Formular (Nur das OnPaint Event reicht zum testen):
Delphi-Quellcode:
procedure TForm1.FormPaint(Sender: TObject; Canvas: TCanvas;
  const [Ref] ARect: TRectF);
var
  LTextLayout : TTextLayout;
  LPathData : TPathData;
begin
  Canvas.Font.Family := 'Comic Sans MS';
  Canvas.Font.Size := 128;

  LTextLayout := TTextLayoutManager.DefaultTextLayout.Create;
  try
    LPathData := TPathData.Create;
    try
      LTextLayout.Text := '@';
      LTextLayout.Font := Canvas.Font;
      LTextLAyout.ConvertToPath(LPathData); // Magic

      Canvas.Fill.Color := $FF0070FF;
      Canvas.FillPath(LPathData,1);

      Canvas.Stroke.Color := $FF000000;
      Canvas.DrawPath(LPathData,1);
    finally
      LPathData.Free;
    end;
  finally
    LTextLayout.Free;
  end;
end;
Mehr infos: http://riversoftavg.com/blogs/index....text-with-fmx/

Ich hoffe das auch andere hiervon Gebrauch machen können.
Ich mag es wie Firemonkey schon sehr vieles von Haus aus mitbringt auch wen einiges ab und zu mal buggy ist aber seit ich für Freizeitprojekte Firemonkey benutze, schreibe ich viel weniger Nebensächlichen Code den man zwar immer braucht, aber auch immer wieder neu schreibt weil das meist nur einige Zeilen sind. Da bleibt mehr Zeit für das eigentliche Vorhaben ;)

Freundliche Grüsse

Rollo62 25. Jul 2016 13:38

AW: Algorithmus für Text zu Polygon/TPath (Vector)
 
Hallo Milos,

hier ist noch ein "grober" Versuch Text in einen Pfad zu rendern unter FMX.
Jedenfalls hatte ich immer Probleme mit dem Scaling untern verschiedenen Plattformen,
und auch davon einen Screenshot zu machen hatte diverse Probleme.
Mit viel manuellen Korrekturen läuft es, aber ob es auf allen Geräten gleich aussieht kann ich nicht garantieren.

Ich will den Text erst im 0,0 Punkt mittig zentrieren,
dann drehen und verschieben, und
an die finale Position positioinieren.
Das alles mit den Matrizen.

Vielleicht hilft dir das ja auch it´rgendwie weiter.

Delphi-Quellcode:
procedure TS4Shape.PaintToLayout_Text(Canvas: TCanvas; const ARect: TRectF);
var
  ptS, ptE, ptC: TPointF;
//  sAng: Single;
//  mtrx: TMatrix;
//  mtrxOfs: TMatrix;
//  sAngL: Single;
  F_Arm : Single;
  len  : Single;
//  mtrxTrn: TMatrix;
//  mtrxRot: TMatrix;
//  rcText: TRectF;
//  szBmp: TSizeF;
//  MState: TMatrix;
//  ptVP: TPointF;
//  sSclC: Single;
//  sSclCP: Single;
//  sSclP: Single;
//  sSclB: Single;
//  sSclSC: Single;
  pd: TPathData;
  ang: Single;
  rcImg: TRectF;
  lenOfs: Single;
  sHeight: Single;
  rcTxt: TRectF;
  sScale: Single;
  mat: TMatrix;
  sSclScrn: Single;
  sSclCan: Single;
  sSclBmp: Single;
  bOrientH: Boolean;
  FScrn: IFMXScreenService;
  sGetScrnScale: Single;
  sTxtFak: Single;
//  bIsTablet: Boolean;

begin

// New Approach 24.04.16

    if FShapes.IsSuppressText then
    begin
      Exit; // Don'T want Text here
    end;

    sGetScrnScale := 1.0;

    if TPlatformServices.Current.SupportsPlatformService( IFMXScreenService, FScrn) then
    begin
      sGetScrnScale := FScrn.GetScreenScale;

      FScrn := nil;
    end;


    // New Approach 24.04.16
    pd := TPathData.Create;

    try

        if (FText <> '')           and
           (FSelPtsTextIdx[0] >= 0) and
           (FSelPtsTextIdx[1] >= 0) then
        begin
          pd.Clear;

          rcImg := RectF(0, 0,
                         Min(Canvas.Width, Canvas.Height),
                         Max(Canvas.Width, Canvas.Height)
                        );

          if Canvas.TextToPath(pd,
                               rcImg,
//                               RectF(0,
//                                     0,
//                                     Min(Canvas.Width, Canvas.Height),
//                                     Max(Canvas.Width, Canvas.Height)),
                               FText,
                               True,
                               TTextAlign.Center,
                               TTextAlign.Leading
                              ) then
          begin

            ptS := FSelPtsList.Items[FSelPtsTextIdx[0]].Position.Point;
            ptE := FSelPtsList.Items[FSelPtsTextIdx[1]].Position.Point;

            len := ptS.Distance( ptE );

            if len >= 10 then
            begin

              // Retrieve ScreenOrient
              bOrientH := TS4View_Manager.Orientation_Current_IsPortrait;

              ang := ptS.Angle(    ptE ) + Pi; // As Radian

              ptC := (ptS + ptE) / 2;
              if FSelPtsList.Count >= 3 then
                FSelPtsList.Items[2].Position.Point := ptC
              else
                ptC := ptC;

  //            FLine.Center.Offset ang + Pi/2; // Turn 90°
//              if bOrientH then
                F_Arm  := ang - Pi * 0.5; // 90° direction angFac; // - for L-Arm + for R-Arm
//              else
//                F_Arm  := ang + Pi * 0.5; // 90° direction angFac; // - for L-Arm + for R-Arm
  //            len := TextSize * sSclB * sSclP * FShapes.ScaleShapes * 1.0;
  //          mtrxOfs := TMatrix.CreateTranslation(Cos(FL) * len, Sin(FL) * len);


//              rcImg := RectF(0, 0, Canvas.Width, Canvas.Height);



              pd.FitToRect( rcImg );
              lenOfs := pd.GetBounds.Height;



              sSclScrn := sGetScrnScale;
              sSclCan := Canvas.Scale;
              sSclBmp := FShapes.GetImgViewCtrl.Bitmap.BitmapScale;

              sHeight := rcImg.Width; // Min(Canvas.Width, Canvas.Height );
              sHeight := sHeight / 6;

//              sHeight := 40;
  {$IFDEF MSWINDOWS}
//              sHeight := 80;
  {$ENDIF MSWINDOWS}
  {$IFDEF MACOS}
//              sHeight := 80;
  {$ENDIF MACOS}
  {$IF DEFINED(IOS) or DEFINED(ANDROID)}

  {$IF   DEFINED(IOS)}
              sHeight := 40;
  {$ENDIF DEFINED(IOS)}
  {$IF   DEFINED(ANDROID)}
              sHeight := 20;
  {$ENDIF DEFINED(ANDROID)}


              if sSclScrn <= 2 then
              begin
                sHeight := sHeight * 1.5;
              end
              else
              begin
                sHeight := sHeight / 1.5;
              end;

              if not FShapes.IsInMakeScreenshot then
                sHeight := sHeight * sSclScrn;
  {$ENDIF DEFINED(IOS) or DEFINED(ANDROID)}


              rcTxt := RectF(0,0,
                             len    * sSclScrn,
                             sHeight * sSclScrn); // Height/Size of Text

              sTxtFak := 1.5; //!!! Hier ist der Faktor der die Größe bestimmt ??

              if FText.Length >= 3 then
              begin
                sTxtFak := sTxtFak * (Min(Max(FText.Length, 2), 10) / 6);  // Get TextLen dependend Factor
                lenOfs := lenOfs * 0.95;
              end
              else
              begin
                sTxtFak := sTxtFak * (Min(Max(FText.Length, 1), 10) / 7); // Get TextLen dependend Factor
                lenOfs := lenOfs * 0.80;
              end;

              rcImg.FitInto(rcTxt, sScale);
              if sScale > 0.0 then
                sScale := Max(0.1, 1 / sScale) * sTxtFak
              else
                sScale := Max(0.1, 1 / 1.0)   * sTxtFak;

              lenOfs := 1.05 * lenOfs * sScale;

              mat :=      TMatrix.Identity;
              // Center at 0/0
              mat := mat * TMatrix.CreateTranslation( -rcImg.Width /2,  // -Canvas.Width /2,
                                                      -rcImg.Height/2); // -Canvas.Height/2 );
              // Scale to TextRect
              mat := mat * TMatrix.CreateScaling(sScale, sScale);
              // Rotate to TextRect
              mat := mat * TMatrix.CreateRotation( ang ); // ang );

              // Translate OffCenter, to Upper Line
//              if bOrientH then
                mat := mat * TMatrix.CreateTranslation( Cos(F_Arm) * lenOfs,
                                                        Sin(F_Arm) * lenOfs);
//              else
//                mat := mat * TMatrix.CreateTranslation( Cos(F_Arm) * lenOfs * 4,
//                                                        Sin(F_Arm) * lenOfs * 4);

              // Translate to TextRect Center
              mat := mat * TMatrix.CreateTranslation( ptC.X, // + rcTxt.Width /2,
                                                      ptC.Y // + rcTxt.Height/2 );
                                                    );


              pd.ApplyMatrix( mat );

              Canvas.Stroke.Color := Canvas.Fill.Color;

              Canvas.FillPath( pd, 1);
              Canvas.DrawPath( pd, 1);

            end;

          end;


        end;


    finally

      pd.Free;

    end;

end;
Rollo


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:16 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz