1

フォーマットされたテキストを画面に表示しようとしています。最初に非常に単純なHTMLテキストが解析され(b、u、iのようなタグがあります)、次に各文字がCanvas.TextOut関数を使用して適切な位置とフォントでレンダリングされます。

私が最初に気付いたのは、キャンバス上のすべての個別の文字のレンダリングがかなり遅いということです。文全体のレンダリングははるかに高速です。キャンバスが強制的に再描画されたとき、フォームが画面上で移動したときは明らかです。

1つの解決策は、文字を均等なフォントでクラスター化し、一度にレンダリングすることです。しかし、フォーマットが豊富な場合は、あまり役に立ちません。さらに、文字は個別のエンティティである必要があります。これは、任意の方法でレンダリングできます。たとえば、テキストの配置をサポートするWinAPIはありません。taJustifyまたはブロック書き込み...

もう1つのアプローチは、ビットマップでレンダリングするかClipRect、TCanvasのプロパティを賢く使用することです(まだ試していません)。

とにかく、同じフォーマットのテキストがTRichEditに表示される場合、再描画操作による時間のペナルティはありません。もう1つの簡単な例は、すべての主要なブラウザです。これは、大量のフォーマットされたテキストを表示するのに問題はありません...私と同じように各文字をレンダリングしますが、より効率的に表示しますか?私は知らない。

それで、アプリケーションを高速化するためのレシピを知っていますか(フォーマットされたテキストレンダリング?)。

あなたのアイデアに感謝します...

サンプルコード:(TFormをできるだけ大きくし、マウスでつかんで画面の下にドラッグします。上に移動すると、「ジャンピー」な動きが表示されます)

procedure TForm1.FormPaint(Sender: TObject);
var i, w, h, j:integer;
    s:string;
    switch:Boolean;
begin
   w:=0;
   h:=0;
   s:='';
   for j:=0 to 5 do
       for i:=65 to 90 do s:=s + Char(i);

   switch:=False; // set true to see the difference

   if switch then
     begin
     for j:=0 to 70 do begin
         for i := 1 to Length(s) do
         begin
         Form1.Canvas.TextOut(50+ w,h +70 , s[i]);
         w:=w +  Form1.Canvas.TextWidth(s[i]);
         end;
         w:=0;
         h:=h+15;
         end;
     end
    else
      begin
      for j:=0 to 70 do begin
       Form1.Canvas.TextOut(50+ w,h +70 , s);
       w:=w +  Form1.Canvas.TextWidth(s);  // not optimalized just for comparison
       w:=0;                               // not optimalized just for comparison
       h:=h+15;
       end;
      end;
end;
4

3 に答える 3

4

AQTimeなどのプロファイラーを使用して、コードが実際に時間を費やしている場所を見つけます。TextOut()最も時間がかかるのはそれ自体ではない可能性があります。一度に1文字ずつインデックスを作成し、String各文字をTextOut()とに渡しますTextWidth()。これらのメソッドはどちらもCharパラメータを入力として受け入れず、代わりに入力のみを受け取るため、RTLは、ソースの長さに応じて、メモリ内のString多くの一時的なものを割り当てて解放するために労力を費やしています。私はそのようなキルループのパフォーマンスを見てきました。StringString

于 2012-09-26T18:33:18.060 に答える
2

ちらつきを回避し、最高のパフォーマンスを実現し、高度なテキストレンダリング機能(カーニングなど)をすべて備えているため、答えは一時的なビットマップを使用することです。

Windowsではテキストの描画は非常に高速ですが、事前に計算されたビットマップの表示ははるかに高速になります。

レイアウトを分割して、テキストの表示されている部分のみをレンダリングできます。または、各ボックスの幅にキャッシュを使用して、テキストをテキストの「ボックス」に分割してみてください(優れたTeXエンジンと同じように)。ただし、Windows自体がそのようなキャッシュを実行するため、コード全体の適切なプロファイリングを介して、実際のボトルネックが見つかった場合にのみ、このような手法を使用してください。

車輪の再発明をしないでください。実際のコンテンツでは、たとえば言語とレイアウト(アラビア語と英語など)を混在させる場合など、テキストのレンダリングが想像よりもはるかに複雑であることがわかります。このような複雑な作業については、 Windows( UniScribe APIなど)に依存することをお勧めします。オープンソースのPDFエンジンを作成したときは、可能な限り再利用しました

たとえば、FireMonkeyは車輪の再発明に苦しんでおり、複雑なテキストコンテンツをレンダリングすると失敗します。したがって、既存のAPIを使用することが私見の最善の方法です...

于 2012-09-26T19:20:01.993 に答える
1

私のPCでは、ビットマップにレンダリングしてからそれをキャンバスに描画すると、約2倍の速度になります。さて、遅いバージョンは2倍速くなります。高速バージョンは同じままです。

動作する可能性のある別の最適化。文字幅を事前に計算して配列にすることもできるため、canvas.TextWidth()を頻繁に呼び出す必要はありません。

このような変数を保持します

widths:array[char] of byte;

このように記入してください:

for c := low(widths) to high(widths) do
  widths[c] := Canvas.TextWidth(char(c));

この65536要素配列の入力には時間がかかるため、65..90要素配列を作成し、unicode-supportを削除する方がよいでしょう。

もう1つ..Winapi.Windows.TextOut()の呼び出しは、canvas.TextOut()よりも高速です。

あなたは実際にそれでたくさん勝つことができます。

    Winapi.Windows.TextOut(bmp.Canvas.Handle, w, h, @s[i], 1);

コードの変更バージョン:

// set up of off-screen bitmap.. needs to be resized when the form resizes. 
procedure TForm1.FormCreate(Sender: TObject);
begin
  bmp := TBitmap.Create;
  bmp.SetSize(width,height);
end;

これは

procedure TForm36.PaintIt2;
var h,i,j,w: Integer; s: string;
begin
  w := 0;  h := 0;  s := '';

  for j := 0 to 5 do
    for i := 65 to 90 do
      s := s + Char(i);

  bmp.Canvas.Brush.Color := Color;
  bmp.Canvas.FillRect(bmp.Canvas.ClipRect);
  if Checkbox1.Checked then
  begin
    for j := 0 to 70 do
    begin
      for i := 1 to Length(s) do
      begin
        Winapi.Windows.TextOut(bmp.Canvas.Handle, w, h, @s[i], 1);
        w := w + widths[s[i]];
      end;
      w := 0; h := h + 15;
    end;
  end
  else
    for j := 0 to 70 do
    begin
      bmp.Canvas.TextOut(w, h, s);
      w := 0; h := h + 15;
    end;
  canvas.Draw(0,0,bmp);
end;

私はこの手順でパフォーマンスの時間を計りました:

procedure TForm1.Button2Click(Sender: TObject);
var i : Integer; const iterations=300;
begin
  with TStopwatch.StartNew do
  begin
    for I := 1 to iterations do
      PaintIt2;
    Caption := IntToStr(Elapsed.Ticks div iterations);
  end;
end;

最後の注意:

cleartype /アンチエイリアシングを無効にしてみましたが、奇妙なことに、レンダリングが2倍遅くなります。これが私がアンチエイリアシングをオフにした方法です:

  tagLOGFONT: TLogFont;

  GetObject(
    bmp.Canvas.Font.Handle,
    SizeOf(TLogFont),
    @tagLOGFONT);
  tagLOGFONT.lfQuality  := NONANTIALIASED_QUALITY;
  bmp.Canvas.Font.Handle := CreateFontIndirect(tagLOGFONT);
于 2012-09-26T19:32:27.147 に答える