[原創] 改善 Firemonkey Canvas 幾何繪圖質量問題(移動平台)


說明:

  Fiiremonkey 的跨平台能力,大家有目共睹(一碼同介面跨四平台),唯獨移動平台在幾何繪圖方面,質量始終不盡人意,我也曾試着去修正(如:修正曲線平滑問題),也曾找過第三方案(如:AggPas),但都不完美,我一直在想,移動平台有這么強的繪圖能力及質量(Android & iOS),如果能直接拿來用,不是很好?為什么 Firemonkey 要自己重寫?

  目前網上許多針對此問題的改善方案,但多以控件形式,且需依各平台的繪圖函數來繪圖,或僅提供固定的幾何圖形控件,如果能延用現有的 TCanvas.Draw????? 繪圖代碼,只要加入幾行代碼(或編譯開關),就能達到原生繪圖的效果,豈不是很理想,於是開是構想這種方案的可行性,最后證實是可行的。

  要先說明的是,這里提供的只是一種改善方案,而不是修正,最終還是希望能受到 EMB 官方的關注,由官方來改進這個問題。

 

用法及效果:

FMX 的繪圖方法:

procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
var Rect, DesRect: TRectF;
begin
  Rect := PaintBox1.LocalRect;
  Canvas.Stroke.Thickness := 10;
  Canvas.Stroke.Kind := TBrushKind.Solid;
  Canvas.Stroke.Dash := TStrokeDash.DashDotDot;

  DesRect := Rect;
  InflateRect(DesRect, -(Canvas.Stroke.Thickness / 2), -(Canvas.Stroke.Thickness / 2)); // 線在區內
  Canvas.FillRect(DesRect, 30, 30, AllCorners, 1);
  Canvas.DrawRect(DesRect, 30, 30, AllCorners, 1);
end;
說明 Android iOS Windows macOS

上面的代碼,使用 FMX 繪圖方法

(移動平台是有問題的)

下面的代碼,使用原生繪圖方法

(四個平台全部相同了)

下面改成原生繪圖方案,只要在原有絵圖代碼前後加入二行代碼(原繪圖代碼不用更動)

{$i NativeDraw.inc}
uses FMX.Graphics.Native;

procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
var Rect, DesRect: TRectF;
begin
  Rect := PaintBox1.LocalRect;
  Canvas.Stroke.Thickness := 10;
  Canvas.Stroke.Kind := TBrushKind.Solid;
  Canvas.Stroke.Dash := TStrokeDash.DashDotDot;

  {$IFDEF UseNativeDraw}Canvas.NativeDraw(Rect, procedure begin {$ENDIF} // 原生繪圖 by Aone

  DesRect := Rect;
  InflateRect(DesRect, -(Canvas.Stroke.Thickness / 2), -(Canvas.Stroke.Thickness / 2)); // 線在區內
  Canvas.FillRect(DesRect, 30, 30, AllCorners, 1);
  Canvas.DrawRect(DesRect, 30, 30, AllCorners, 1);

  {$IFDEF UseNativeDraw}end);{$ENDIF} // 原生繪圖 by Aone
end;

 

運作原理:

  • 它的運作原理很簡單,就是先配置一個原生的繪圖區域 Bitmap,再將幾何圖形畫在這個區域里,最后將這個區域里的內容,轉回 TBitmap,再顯示到 Canvas 里
  • 利用 Delphi Pascal Helper 語言特性,針對 TCanvas 來做擴展,如此才能達到不改動原有的繪圖代碼
  • 目前這個作法,相同的繪圖代碼,可以跨四個平台:
    • Android, iOS:使用原生繪圖 TCanvasHelper 方法

    • Windows, masOS:使用原來 TCanvas 方法(這二個平台本來就沒有質量問題,所以不用重寫)
  • 我們無法觸及 FMX 的最核心,只能利用這種 Bitmap 方式來變通,要知道這種方式可能帶來的問題(記憶體及效能),才不致錯用

  • 注意:如果引用了 FMX.Graphics.Native 就必需使用下面的方式來寫繪圖代碼,否則請不要引用:
    {$i NativeDraw.inc}
    uses FMX.Graphics.Native;
    
    {$IFDEF UseNativeDraw}Canvas.NativeDraw(Rect, procedure begin {$ENDIF} // 原生繪圖 by Aone
    
    // 繪圖代碼
    
    {$IFDEF UseNativeDraw}end);{$ENDIF} // 原生繪圖 by Aone

 

調用原生繪圖的 TCanvas 函數:(僅有下面函數有質量問題)

DrawLine 畫線 
FillRect 圓距區 
DrawRect 圓距框線 
FillPath 路徑區 
DrawPath 路徑框線 
FillEllipse 橢圓區 
DrawEllipse 橢圓框線
FillArc 孤線區
DrawArc 孤框線 
FillPolygon 多邊形區 
DrawPolygon 多邊形框線 
IntersectClipRect 相交剪裁區
ExcludeClipRect 其它剪裁區

 

TBrush 塗刷 & TStrokeBrush 線刷:

  • 支持所有塗刷特性,除了 Bitmap 塗色尚未支持(若有此需求,可自行修改源碼,加入此特性)
  • FMX 在移動平台是不支持畫線加漸層塗色,使用原生繪圖,已經可以輕松實現(支持 Linear 線漸層及 Radial 圓漸層效果):
    原生 FMX
  • procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas);
    var Rect, DesRect: TRectF;
    begin
      Rect := PaintBox1.LocalRect;
      Canvas.Stroke.Thickness := 10;
      Canvas.Stroke.Kind := TBrushKind.Gradient;
      Canvas.Stroke.Dash := TStrokeDash.DashDotDot;
      Canvas.Stroke.Gradient.Color := TAlphaColorRec.Blue;
      Canvas.Stroke.Gradient.Color1 := TAlphaColorRec.Gold;
    
      {$IFDEF UseNativeDraw}Canvas.NativeDraw(Rect, procedure begin {$ENDIF} // 原生繪圖 by Aone
    
      DesRect := Rect;
      InflateRect(DesRect, -(Canvas.Stroke.Thickness / 2), -(Canvas.Stroke.Thickness / 2)); // 線在區內
      Canvas.FillRect(DesRect, 30, 30, AllCorners, 1);
      Canvas.DrawRect(DesRect, 30, 30, AllCorners, 1);
    
      {$IFDEF UseNativeDraw}end);{$ENDIF} // 原生繪圖 by Aone
    end;

 

已知問題:

  • 真機 iOS 64bit 無法顯示虛線(虛擬機沒問題)
  • 未完成的函數功能及特性,歡迎大家一起完善

 

文件下載:

 

 2017.06.22 新增 TestArc Demo(已更新到 GitHub):

參考資料:

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM