例として。 シールの編集者によって開発されました。 サブジェクト領域の詳細のため、作業は「微視的」フォントで実行されます。そのサイズは分数で非常に小さくなります。 スケールなしではできません。 ただし、最大スケールですべてのテキストが適切に設定されていて、位置合わせが完了し、すべてが美しい場合、「通常の」スケールに戻ると、すべての書式設定が「飛ぶ」ことができます。
大規模な場合、右側のロゴは見栄えがします。 ズームアウトのプロセスでは、状況が定期的に発生します。これは左の図に示されています-碑文「スプロール」です。
碑文は2つの部分で構成されています。 左側にあるように、単一の全体のように見えるマージされたテキストがあります。 しかし、碑文間をズームアウトすると、ギャップが顕著に現れます。
このようなプロジェクトのスケール機能は非常に重要です。 また、彼らが大規模に行ったことは、どのような規模でも見られるはずです。 許容できない「小さな」シフトやエラーはありません。
テストアプリケーション
スケールの方法をテストするために、小さなアプリケーションを作成します。 ソースコードはアーカイブに記載されています。
- 上部にはコントロールのあるパネルがあります。 ドロップダウンリストにスケールのあるスライダーとスケーリング方法の選択を含める;
- すべてのスケール関数は次のタイプです。
TxDrawZoomFunc = function (ACanvas : TCanvas; // ARect : TRect; // AZoom, ASize : double;// , AText : string // ) : boolean; //
- 関数は、文字列リストの名前で登録されます。 ドロップダウンリストに表示されるのは彼です。GDrawZoomFuncList:Tstrings = nil;
- テキストがプルされているかどうか、およびどこまでプルされているかを確認するために、スケール依存のグリッドを描画します。
- テキストとともに、「計算された」長方形が描画されます。これは、通常のフォントサイズのテキストの領域として計算され、スケールが乗算されます。
//****************************************************************************** // //****************************************************************************** function DrawZoomCalcRect (ACanvas : TCanvas; ARect : TRect; AZoom, ASize : double; AText : string) : TRect; var siz : TSize; begin //-- , --------------------------------- ACanvas.Font.Height := -trunc(ASize * ACanvas.Font.PixelsPerInch/72); //-- ----------- GetTextExtentPoint32(ACanvas.Handle,PWideChar(AText),Length(AText), siz); //---------------------------------------------------------------------------- // , , // //---------------------------------------------------------------------------- result := ARect; result.Right := result.Left + round (AZoom * siz.Width); result.Bottom := result.Top + round (AZoom * siz.Height); end;
- すべてのスケールメソッドは、グローバル変数GDiffWidth:extendedを計算します。 これは、計算された幅と結果の幅の比率です。 テスト結果の分析に必要です。
いくつかの補助機能が使用されます。
//****************************************************************************** // //****************************************************************************** function WidthRect (ARect : TRect) : Integer; begin result := ARect.Right - ARect.Left; end; function HeightRect (ARect : TRect) : Integer; begin result := ARect.Bottom - ARect.Top; end; //****************************************************************************** // //****************************************************************************** function CheckParamsValid (ACanvas : TCanvas; ARect : TRect; AObject : TObject; AObjChecked : boolean = true) : boolean; begin result := (ACanvas <> nil) and ((not AObjChecked) or (AObject <> nil)) and (WidthRect (ARect) > 0) and (HeightRect (ARect)>0); end; //****************************************************************************** // ARect //****************************************************************************** function CreateBmpRect (ARect : TRect) : TBitmap; begin result := TBitmap.Create; result.Width := abs (WidthRect (ARect)); result.Height := abs (HeightRect (ARect)); end;
方法1「額に」 分数フォントサイズ
問題を「真正面から」解決する場合、これは道を譲ります:スケールに応じてフォントの高さを変更します。 これには、Font.Heightなどのパラメーターが適しています。 これはピクセル単位のフォントの高さであり、物事の論理によると、これはスムーズなズームにつながるはずです。
Font.Height=−trunc(AZoom∗ASize∗Font.PixelsPerInch/72);
どこで:
- ASize-小数にできるフォントサイズ
- AZoom-スケール。
式はどこから来たのですか。 Windowsのフォントの高さはポイントで表されますが、これはタイポグラフィに由来します。
1インチ= 25.4 mm = 72ポイント
したがって、最初のズーム機能は次のとおりです。
//****************************************************************************** // " " // 1 = 25.4 = 72 //****************************************************************************** function DrawZoomTextSimple (ACanvas : TCanvas; ARect : TRect; AZoom, ASize : double; AText : string) : boolean; var rct : TRect; begin result := CheckParamsValid(ACanvas,ARect,nil,false) and (AText<>''); if not result then exit; rct := DrawZoomCalcRect(ACanvas, ARect, AZoom, ASize, AText); with Acanvas do begin Pen.Color := clGreen; Pen.Width := 1; Rectangle(rct); Font.Height := -trunc(AZoom * ASize * Font.PixelsPerInch / 72); TextOut (ARect.Left, ARect.Top, AText); GDiffWidth := WidthRect(rct) / TextWidth(AText); end; end;
結果は図に表示されます。
左側のデュースがエッジのある灰色の線上に明確に配置されている場合、右側の図のスケールがわずかに変化すると、灰色の線は中央のデュースと交差します。
結果は不十分です。
方法2「世界座標」SetGraphicsMode
ノミをxで押し込めないことは明らかです。 Windowsが提供するツールを使用する必要があります。
function SetGraphicsMode(hdc: HDC; iMode: Integer): Integer;
- DCデバイスコンテキストハンドル。
- iModeグラフィックモードを指定します。 このパラメーターは、次の値のいずれかです。
GM_COMPATIBLE:16ビットWindowsと互換性のあるグラフィックモードを設定します。 これがデフォルトのモードです。
GM_ADVANCED:ワールドスペースの変換を可能にする拡張グラフィックモードをインストールします。 特に、スケール変換はこのモードで使用できます。 ここで使用しています。
操作アルゴリズムは次のとおりです。
- DCをGM_ADVANCEDに設定します。
- TXForm構造体(実際は行列です)のフィールドを初期化します。 変換は、次の式に従って実行されます。
x ′ = x ∗ e M 11 + y ∗ e M 21 + e D x
y ′ = x ∗ e M 12 + y ∗ e M 22 + e D y
ご覧のとおり、スケールを実装するために、eM11およびeM22フィールドに関心があります。 - 変換マトリックスの割り当て:SetWorldTransform(DC、xFrm);
- スケールに関係なく、「通常の」サイズで「通常の」座標にテキストを描画します。
- 変換を元の状態に戻します。
- 前のモードに戻ります。
2番目のズーム機能は次のとおりです。
//****************************************************************************** // SetGraphicsMode (GM_ADVANCED) // //****************************************************************************** function DrawZoomTextWorldMode(ACanvas : TCanvas; ARect : TRect; AZoom, ASize : double; AText : string) : boolean; var rct : TRect; oldM : integer; xFrm : TXForm; begin result := CheckParamsValid(ACanvas,ARect,nil,false) and (AText <> ''); if not result then exit; //-- , =1 ------------- rct := DrawZoomCalcRect(ACanvas,ARect,1,ASize,AText); //-- "" ---------------------- oldM := SetGraphicsMode(ACanvas.Handle, GM_ADVANCED); try //-- ------------------------------------------------------ FillChar(xFrm,SizeOf(xFrm),0); //-- ------------------------------------- // x' = x * eM11 + y * eM21 + eDx // y' = x * eM12 + y * eM22 + eDy xFrm.eM11 := AZoom; xFrm.eM22 := AZoom; //-- -------------------------------------- SetWorldTransform(ACanvas.Handle, xFrm); //-- , --------------------- with Acanvas do begin Pen.Color := clRed; Pen.Width := 1; Rectangle (rct); TextOut (rct.Left, rct.Top, AText); //-- / -------- GDiffWidth := WidthRect(rct)/TextWidth(AText); end; finally //-- -------------------------------- xFrm.eM11 := 1; xFrm.eM22 := 1; SetWorldTransform(ACanvas.Handle, xFrm); //-- --------------------------------------------- SetGraphicsMode(ACanvas.Handle, oldM); end; end;
結果は次のとおりです。
状況は前の状況と似ています。 左側では、デュースはセルの灰色の境界線の間に快適に配置され、右側ではセルラインが交差します。 つまり 彼らは大規模に「引っ張る」ことを取り除きませんでした。
ただし、肯定的な側面もあります。スケールを気にせずに描画できます。 つまり、スケールに関係なく、非常に不均衡にクールな何かを描画する非常に大きな関数があります。 呼び出す前に変換行列を割り当てることができ、それによってスケーリングする能力が得られます。 この場合、eDxおよびeDyパラメーターを使用して、変位も取得します。
線の太さも縮尺によって変わることに注意してください。 追加の利点と変換はトピック外です。
その間、望ましい結果は達成されていません。
スケール方法3 SetMapMode / MM_ISOTROPIC
SetGraphicsMode(GM_ADVANCED)メソッド2を使用してWindowsを使用して座標を変換しても、そこで終了しません。 以下の関数の束を考慮してください。
function SetMapMode(DC: HDC; p2: Integer): Integer; function SetWindowExtEx(DC: HDC; XExt, YExt: Integer; Size: PSize): BOOL; function SetViewportExtEx(DC: HDC; XExt, YExt: Integer; Size: PSize): BOOL;
SetMapMode関数は、選択されたデバイスコンテキストがピクセルを別のものと見なすよう強制します。 ピクセルが実際に0.001インチであると仮定します。 次の値を取ることができるパラメーターp2に依存します。
- MM_ISOTROPIC-両方の軸で同じスケールの任意のスケーリング。 スケール係数は、以下で説明するSetWindowExtとSetViewportExtのペアによって設定されます。
- MM_ANISOTROPIC-各軸に沿った任意のスケーリング。 スケール係数は、以下で説明するSetWindowExtとSetViewportExtのペアによって設定されます。
- MM_HIENGLISH-0.001インチ。 Xは左から右、Yは下から上。
- MM_LOENGLISH-0.01インチ。 Xは左から右、Yは下から上。
- MM_HIMETRIC-0.01ミリメートル。 Xは左から右、Yは下から上。
- MM_LOMETRIC-0.1ミリメートル。 Xは左から右、Yは下から上。
- MM_TEXT-ピクセルごと。 Xは左から右、Yは下から上。
- MM_TWIPS-1/20ポイント。 (ポイント= 1インチ/ 72、したがってtwip = 1インチ/ 1440)。 Xは左から右、Yは下から上。
「左から右にX、下から上にY」という語句の意味は何ですか。 これは、Xの座標は非常に普通ですが、Y座標は負であることを意味します。 つまり、長方形(10,10,1000,1000)に楕円を描きたい場合、追加の変換なしでそれを見るには、楕円(10、-10,1000、-1000)を書く必要があります。
しかし、スケールに興味があります。 さらに、最も一般的で、すべての軸で同じです。 したがって、p2 = MM_ISOTROPICを使用します。
モードを設定した後、スケール係数を設定する必要があります。 これは、SetWindowExtEx / SetViewportExtEx関数のペアによって行われます
- 出力論理ウィンドウの設定
SetWindowExtEx(DC、論理幅、論理高さ、nil); - 実際の出力ウィンドウを設定する
SetViewportExtEx(DC、実幅、実高、nil);
これで、画面の実際の幅(高さ)、形状、ペイントボックス、任意の長方形などのスケールができました。 特定の論理的な幅(高さ)に対応します。たとえば、プリンターの用紙、または縮尺で表示する必要のある大きな画像の幅などです。
スケールファクターは、F =(実数値)/(論理値)です。
なぜなら スケールは両方の軸で同じである必要があり、Windowsは最小の比率を選択します。
論理量とは何ですか。 特定の画像を反映したい場合は、その幅と高さになります。実際のサイズは、反映したいピクセル単位の領域です。
変換関数は次のとおりです。
x '= x * F
y '= y * F
したがって、幅の実際の値:ズーム*幅と高さ:ズーム*高さ。
3番目のズーム機能は次のようになります。
//****************************************************************************** // : SetMapMode/SetWindowExtEx/SetViewportExtEx //****************************************************************************** function DrawZoomTextMapMode (ACanvas : TCanvas; ARect : TRect; AZoom, ASize : double; AText : string) : boolean; var DC : HDC; rct : TRect; Old : integer; w,h : Integer; begin result := CheckParamsValid(ACanvas,ARect,nil,false); if not result then exit; //-- , ---- rct := DrawZoomCalcRect(ACanvas,ARect,1,ASize,AText) and (AText <> ''); //-- ----------------------------- DC := ACanvas.Handle; w := WidthRect(ARect); h := heightRect(ARect); //-- MM_ISOTROPIC X Y //-- (.. ) Old := SetMapMode(DC, MM_ISOTROPIC); //-- ---------------------- SetWindowExtEx(DC, w, h, nil); //-- ------------------------ SetViewportExtEx(DC, round(AZoom*W), round(AZoom*H), nil); //-- ------------------------------------------------- try with ACanvas do begin Pen.Color := clPurple; Pen.Width := 1; Rectangle(rct); TextOut (ARect.Left, ARect.Top, AText); GDiffWidth := WidthRect(rct)/TextWidth(AText); end; finally SetMapMode(DC, Old); end; end;
ただし、結果はまだ満足のいくものではありません。
この状況は、前の2つの状況とまったく同じです。
この方法の利点は方法2と似ています。「描画」関数を作成する際にスケールについて心配する必要はありませんが、スケールを移動する方法はありません。 運動の変容は純粋に手作業です。
一般に、この関数は変換によってシャープ化されません。 この何かの単位で何かを表示するのに適しています。 これは、ユニットの1つの言語から画面表現の言語への一種の「翻訳者」です。
方法4 "インチ" SetMapMode / MM_HIENGLISH
しかし、別のオプションを試してください。 方法3では、SetMapMode関数について詳しく説明します。 メートル法からスクリーンへの変換のフラグを含めることが言及されました。 インチ座標系で作業してみましょう。 ミリ単位ではない理由-追加の変換を避けるため。 結局のところ、まだ最初はまだいくつかのインチインジケーターがあります。 なぜ25.4で追加するのか(方法1を参照)。
何が促した。 それでも、0.001インチの値は非常に小さな離散です。 もしもし?
4番目のスケーリング関数は次のとおりです。
//****************************************************************************** // SetMapMode/SetWindowExtEx/SetViewportExtEx // MM_HIENGLISH - 0.001 . //****************************************************************************** function DrawZoomTextMapModeHIENGLISH(ACanvas : TCanvas; ARect : TRect; AZoom, ASize : double; AText : string) : boolean; var DC : HDC; Old: integer; pnt : TPoint; rct : TRect; siz : TSize; tmp : Integer; begin result := CheckParamsValid(ACanvas,ARect,nil,false) and (AText <> ''); if not result then exit; //-- , --------- ACanvas.Font.Height := -trunc(ASize * ACanvas.Font.PixelsPerInch / 72); tmp := ACanvas.Font.Height; DC := ACanvas.Handle; //-- ------------------------ pnt.X := GetDeviceCaps(DC,LogPixelsX); //-- -------------------------- pnt.Y := GetDeviceCaps(DC,LogPixelsY); //-- (0.001 )----------------------------------- GetTextExtentPoint32(DC,PWideChar(AText),Length(AText), siz); rct.Top := -round(1000* AZoom * ARect.Top / pnt.Y); rct.Left := round(1000* AZoom * ARect.Left / pnt.X); rct.Right := rct.Left + round(1000* AZoom * siz.Width / pnt.X); rct.Bottom := rct.Top - round(1000* AZoom * siz.Height / pnt.Y); ACanvas.Font.Height := -round(rct.Bottom-rct.Top) ; Old := SetMapMode(DC, MM_HIENGLISH); try with Acanvas do begin Pen.Color := clTeal; Pen.Width := 1; Rectangle (rct); TextOut (rct.Left, rct.Top, AText); GDiffWidth := WidthRect(rct) / TextWidth(AText); end; finally SetMapMode(DC, Old); ACanvas.Font.Height := tmp; end; end;
残念ながら、結果は以前のものより良くありません:
方法5「文字ごとのレンダリング」
以前のすべてのメソッドでは、TLogFontの整数部分のように感じられます。 lfHeightは寿命を著しく損ない、特定のスケールへの「微調整」を許可しません。 ええと...それが分数だった場合...まあ、さて、問題を別の方法で解決してみましょう。
主なアイデアは次のとおりです。テキストのすべての文字を通過させるには、文字が表示されるX軸に沿って先頭をカウントします。 変換係数は、計算された幅と実数の比率として最初に計算されます。
//****************************************************************************** // //****************************************************************************** function DrawZoomTextChar(ACanvas : TCanvas; ARect : TRect; AZoom, ASize : double; AText : string) : boolean; var rct : TRect; fct : double; i : Integer; w : Integer; begin result := CheckParamsValid(ACanvas,ARect,nil,false) and (AText <> ''); if not result then exit; //-- , ---------- rct := DrawZoomCalcRect(ACanvas,ARect,AZoom,ASize,AText); try with ACanvas do begin Pen.Color := clMaroon; Pen.Width := 1; Rectangle(rct); GDiffWidth := WidthRect (rct); //-- ---------------------------------------------- Font.Height := -trunc(AZoom * ASize * Font.PixelsPerInch/72); //-- "" ---------------------------- fct := WidthRect (rct)/TextWidth(AText); //-- , , w := 0; for i := 1 to Length(AText) do begin TextOut (rct.Left, rct.Top, AText[i]); w := w + TextWidth(AText[i]); //-- ----- rct.Left := round (ARect.Left + w * fct); end; GDiffWidth := GDiffWidth / (rct.Left-ARect.Left); end; except result := false; end; end;
驚くべきことに、それは動作します:
デュースはラインにしっかりとくっついており、どんなスケールでもそれを残しません。
最初の成功したスケーリング方法。 目標は達成されましたが、より良い解決策が欲しいです。
方法6「ビットマップバッファー」
以前の方法は、各文字のレンダリングの開始をシフトすることにより、事前に計算された必要なサイズに文字ごとに「適合する」というものでした。 しかし、ビットマップに基づいてすべて同じことが行われた場合はどうなりますか?
これは、テキストが最初に中間ビットマップ上に所定の縮尺で描かれるというものです。 これを「戦闘」マトリックスと呼びます。 次に、計算された値に応じて、サイズが設定された別のビットマップ行列へのストレッチコピーがあります。 その後、「透明な」コピーが「作業中の」キャンバスにコピーされます。
機能テキスト:
//****************************************************************************** // TBitmap StretchDraw //****************************************************************************** function DrawZoomTextBitmap(ACanvas : TCanvas; ARect : TRect; AZoom, ASize : double; AText : string) : boolean; var rct: TRect; val: TRect; siz: TSize; bmp: TBitmap; // - "" dst: TBitmap; // -stretch begin result := CheckParamsValid(ACanvas,ARect,nil,false) and (AText <> ''); if not result then exit; //-- , ---------- rct := DrawZoomCalcRect(Acanvas,Arect,AZoom,ASize,AText); //-- ----------------------------- ACanvas.Font.Height := -trunc(AZoom * ASize * ACanvas.Font.PixelsPerInch / 72); GetTextExtentPoint32(ACanvas.Handle,PWideChar(AText),Length(AText), siz); val := ARect; val.Right := val.Left + siz.Width; val.Bottom := val.Top + siz.Height; //-- -, ----------------------------------- bmp := CreateBMPRect (val);// , "" try with bmp.Canvas do begin Font.Assign(ACanvas.Font); Brush.Color := clWhite; TextOut(0,0,AText); end; //-- ---------------------------------- dst := CreateBmpRect(rct); //-- / "" , dst.Canvas.StretchDraw(dst.Canvas.ClipRect,bmp); //-- --------------------------------------- dst.TransparentColor := clWhite; dst.Transparent := true; with ACanvas do begin Pen.Color := clBlue; Pen.Width := 1; Rectangle(rct); ACanvas.Draw(rct.Left,rct.Top,dst); end; GDiffWidth := WidthRect(rct) / dst.Width; finally if dst <> nil then dst.Free; bmp.Free; end; end;
そして、この方法もうまく機能します:
テキストはそのセルに付着しているように見えました。 非常にスムーズなスケーリング。
2番目に成功したスケーリング方法。 目標は達成されましたが、さらに良いソリューションが欲しいです。 最後の2つの方法は、リソースを集中的に使用します。 これは直接感じられます。
方法7「GDI +」フォントサイズの拡大縮小
そこで、GDI +を使用したスケーリングやテキスト出力など、独自の正確で壮大なツールを開発しました。
コメントする特別なものはありません。 主なことは、スケールに応じてフォントサイズを変更することです。 アンチエイリアス(TextRenderingHintAntiAlias)を使用して、GDI +を使用したテキスト出力。 他のすべてはソースから非常に明確です:
//****************************************************************************** // GDI+ //****************************************************************************** function DrawZoomTextGDIPlus(ACanvas : TCanvas; ARect : TRect; AZoom, ASize : double; AText : string) : boolean; var clr : TColor; grp : TGPGraphics; brh : TGPSolidBrush; nam : TGPFontFamily; fsl : FontStyle; src : TGPRectF; fnt : TGPFont; begin result := CheckParamsValid(ACanvas,ARect,nil,false) and (AText<>''); if not result then exit; ACanvas.Font.Height := -trunc(AZoom * ASize * ACanvas.Font.PixelsPerInch / 72); grp := TGPGraphics.Create(ACanvas.Handle); try with ACanvas do begin clr := Font.Color; //-- --------------------------------------------- nam := TGPFontFamily.Create(Font.Name); //-- --------------------------------------------- fsl := FontStyleRegular; if fsBold in Font.Style then fsl := fsl + FontStyleBold; if fsItalic in Font.Style then fsl := fsl + FontStyleItalic; if fsUnderline in Font.Style then fsl := fsl + FontStyleUnderline; if fsStrikeOut in Font.Style then fsl := fsl + FontStyleStrikeout; //-- "" ---- grp.SetTextRenderingHint(TextRenderingHintAntiAlias); //-- , ------------------------------- brh := TGPSolidBrush.Create(MakeColor(GetRValue(clr), GetGValue(clr), GetBValue(clr))); //-- , "" ---------------------- Fnt := TGPFont.Create(nam, ASize * Font.PixelsPerInch / 72, fsl, UnitPixel); //-- "" ------------------------------- grp.MeasureString(AText,-1,fnt,MakePoint(ARect.Left*1.0, ARect.Top*1.0),src); //-- "" ------------------------------- Pen.Color := clNavy; pen.Width := 1; Rectangle (round(src.X),round(src.Y), round(src.X + AZoom*src.Width), round(src.Y + AZoom*src.Height)); //-- , ------------------- GDiffWidth := AZoom*src.Width; Fnt.Free; //-- ------------------------------------- Fnt := TGPFont.Create(nam, AZoom * ASize * Font.PixelsPerInch / 72, fsl, UnitPixel); grp.SetTextRenderingHint(TextRenderingHintAntiAlias); grp.DrawString(AText, -1, Fnt, MakePoint(ARect.Left*1.0, ARect.Top*1.0), brh); //-- ------------------ grp.MeasureString(AText,-1,fnt,MakePoint(ARect.Left*1.0, ARect.Top*1.0),src); GDiffWidth := GDiffWidth / src.Width; end; except result := false; end; Fnt.free; brh.free; nam.free; grp.free; end;
結果は当然すべての期待を上回りました。スクリーンショットは提供しません これらは、最後の2つの方法の上記に似ています。実行可能ファイルを実行することで、GDI +のパワーをさらに感じます。
方法8「GDI +」スケール変換
そして再び、GDI +。しかし、今回はスケール変換を使用します。つまり「通常の」サイズでテキストを描画すると、GDI +エンジンがそのスケーリングを処理します。変換は、ScaleTransform(AZoom、AZoom)を呼び出すことにより実行されます。
//****************************************************************************** // GDI+ //****************************************************************************** function DrawZoomTextGDIPlusScale(ACanvas : TCanvas; ARect : TRect; AZoom, ASize : double; AText : string) : boolean; var clr : TColor; grp : TGPGraphics; brh : TGPSolidBrush; nam : TGPFontFamily; fsl : FontStyle; src : TGPRectF; fnt : TGPFont; pnt : TGPPointF; begin result := CheckParamsValid(ACanvas,ARect,nil,false) and (AText<>''); if not result then exit; grp := TGPGraphics.Create(ACanvas.Handle); try with ACanvas do begin clr := Font.Color; pnt := MakePoint(ARect.Left*1.0, ARect.Top*1.0); //-- --------------------------------------------- nam := TGPFontFamily.Create(Font.Name); //-- --------------------------------------------- fsl := FontStyleRegular; if fsBold in Font.Style then fsl := fsl + FontStyleBold; if fsItalic in Font.Style then fsl := fsl + FontStyleItalic; if fsUnderline in Font.Style then fsl := fsl + FontStyleUnderline; if fsStrikeOut in Font.Style then fsl := fsl + FontStyleStrikeout; //-- "" ---- grp.SetTextRenderingHint(TextRenderingHintAntiAlias); //-- , ------------------------------- brh := TGPSolidBrush.Create(MakeColor(GetRValue(clr), GetGValue(clr), GetBValue(clr))); //-- , "" ---------------------- Fnt := TGPFont.Create(nam, ASize * Font.PixelsPerInch / 72, fsl, UnitPixel); //-- "" ------------------------------- grp.MeasureString(AText,-1,fnt,pnt,src); //-- "" ------------------------------- Pen.Color := $00BC6C01; pen.Width := 1; Rectangle (round(AZoom*src.X),round(AZoom*src.Y), round(AZoom*(src.X + src.Width)), round(AZoom*(src.Y + src.Height))); //-- ---------------------------------- grp.ScaleTransform(AZoom,AZoom); grp.DrawString(AText, -1, Fnt, pnt, brh); GDiffWidth := 1; end; except result := false; end; Fnt.free; brh.free; nam.free; grp.free; end;
上記のすべての最良の結果。
試験結果
テストプログラムでは、[開始]ボタンをクリックして統計の収集を開始できます。提示されたすべての方法の順次検索は、プログラムで可能なすべてのスケールで行われます。作業が終了すると、次のグラフが表示されます
。最初の列はミリ秒単位の平均描画時間です。 2番目は、実際の値からの計算値の相対偏差です。簡単に言えば、最初の列-操作にかかる時間の短さ、2番目の列-スケーリング結果の高さです。
ご覧のとおり、メソッドは2つのグループに分けられます-最初の4つ-スケールの結果が不十分で、2つ目の4-スケーリングが成功します。
奇妙ですが、速度の面で最も不器用な最初の方法は、敗者のグループでその派手な兄弟よりも良い結果を示しました。確かに、彼の計算値からの偏差は最大です。
傑出した勝者は、スケール変換を伴う方法8「GDI +」です。
したがって、個別の関数としてGDI +でテキストのレンダリングを作成します。
指定された角度の回転とアンチエイリアシングによるテキストの滑らかなスケーリングの機能
//****************************************************************************** // GDI+ //****************************************************************************** function DrawGDIPlusText (ACanvas : TCanvas; ARect : TRect; Angle, ASize : double; AText : string; AZoom : double = 1) : boolean; var clr : TColor; grp : TGPGraphics; brh : TGPSolidBrush; nam : TGPFontFamily; fsl : FontStyle; fnt : TGPFont; pnt : TGPPointF; begin result := CheckParamsValid(ACanvas,ARect,nil,false) and (AText<>''); if not result then exit; grp := TGPGraphics.Create(ACanvas.Handle); try with ACanvas do begin clr := Font.Color; //-- --------------------------------------------- nam := TGPFontFamily.Create(Font.Name); //-- --------------------------------------------- fsl := FontStyleRegular; if fsBold in Font.Style then fsl := fsl + FontStyleBold; if fsItalic in Font.Style then fsl := fsl + FontStyleItalic; if fsUnderline in Font.Style then fsl := fsl + FontStyleUnderline; if fsStrikeOut in Font.Style then fsl := fsl + FontStyleStrikeout; //-- , ------------------------------- brh := TGPSolidBrush.Create(MakeColor(GetRValue(clr),GetGValue(clr),GetBValue(clr))); //-- , "" ---------------------- Fnt := TGPFont.Create(nam, ASize * Font.PixelsPerInch / 72, fsl, UnitPixel); //-- "" ---- grp.SetTextRenderingHint(TextRenderingHintAntiAlias); //-- -------------------------------------- pnt := MakePoint(ARect.Left*1.0, ARect.Top*1.0); //-- , , grp.TranslateTransform(pnt.X,pnt.y); //-- , ------------------ if Angle <> 0 then begin //-- ---------------------------------- grp.RotateTransform(Angle); end; //-- "" ------------------- pnt := MakePoint(0.0,0.0); //-- , ------------------ if AZoom <> 1 then begin grp.ScaleTransform(AZoom,AZoom); end; //-- ------------------------------------- grp.DrawString(AText, -1, Fnt, pnt, brh); end; except result := false; end; Fnt.free; brh.free; nam.free; grp.free; end;
短い結論とコメント
- : SetMapMode
SetWindowOrgEx – .
SetViewportOrgEx – .
, SetViewportOrgEx (DC,100,100,nil), (100,100) TextOut(0,0,'Center here') (100,100); - GDI+ TranslateTransform (. DrawGDIPlusText).
, «» . - , . , – . - , SetGraphicsMode. , . , , , , . .
- SetGraphicsMode TXForm.
- //, , , , GDI+, GDI+? – , , , . , .
- GDI+ VCL . TCanvas TCanvas, Windows API GDI, GDI+.
- GDI GDI+. GDI+ GDI+. つまり , , , SetGraphicsMode, GDI+.
最新のステートメントのデモンストレーションとして、統計出力関数は、DrawGDIPlusText関数を使用して、角度と角度を含むテキストの出力とスケールとオフセットを担当するSetGraphicsModeを使用して記述されます。
次の形式で:
type TFmMain = class(TForm) … private FList : TxZoomStatList; // (utlZoomStat) FListPoint : TPoint; FMouseDown : boolean; FMousePoint: TPoint; FProcessing : boolean; … End; procedure TFmMain.pbMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin FMouseDown := (Button = mbLeft) and //-- - ----------------- (ComboBox1.ItemIndex=ComboBox1.Items.Count-1); if FMouseDown then begin //-- , ---------------- FMousePoint := Point(X,Y); //-- --------------------------------- FListPoint := Point(FList.OffX, FList.OffY); end; end; procedure TFmMain.pbMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin if FMouseDown then begin //-- ------------------------------------------ FList.OffX := FListPoint.X + X-FMousePoint.X; FList.OffY := FListPoint.Y + Y-FMousePoint.Y; //-- ----------------------------------------------- pbPaint(Sender); end; end; procedure TFmMain.pbMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin //-- --------------------- FMouseDown := false; end;
統計クラスの説明
type //****************************************************************************** // //****************************************************************************** PxZoomStat = ^TxZoomStat; TxZoomStat = packed record FIndex : Integer; FColor : TColor; FName : string; FCount : Integer; FTime : extended; FDiff : extended; FTimeC : extended; FDiffC : extended; FTimeR : TRect; FDiffR : TRect; end; TxZoomStatList = class private FOffX : Integer; FOffY : Integer; FList : TList; FGDIPlus : boolean; function GetCount : Integer; function GetItem (Index : Integer) : PxZoomStat; public Constructor Create; virtual; Destructor Destroy; override; function Add (AIndex : Integer; AName : string; ATime, ADiff : Extended) : Integer; overload; function Add (AIndex : Integer; ATime, ADiff : Extended) : PxZoomStat; overload; procedure Delete (Index : Integer); procedure Clear; property Count : Integer read GetCount; property Items[Index : Integer] : PxZoomStat read GetItem; default; //-------------------------------------------------------------------------- property GDIPlus : boolean read FGDIPlus write FGDIPlus; property OffX : Integer read FOffX write FOffX; property OffY : Integer read FOffY write FOffY; end;
統計を描画します。DrawZoomStatList:
//****************************************************************************** // // . , // . , . // // SetGraphicsMode(DC, GM_ADVANCED); //****************************************************************************** function DrawZoomStatList(ACanvas : TCanvas; ARect : TRect; AZoom, ASize : double; AText : string) : boolean; var lst : TxZoomStatList; // ( utlZoomStat) rct : TRect; val : TRect; str : string; i : Integer; p : PxZoomStat; wBar : Integer; //------------------------------------------------------------------------------ maxTime : Extended; maxDiff : Extended; minTime : Extended; minDiff : Extended; wTime : Extended; wDiff : Extended; //-- ------------------------------------------------------------------- DC : HDC; fnt : hFont; tmp : hFont; //-------------------------------------- oldM : integer; xFrm : TXForm; begin lst := xGZoomList(false); result := CheckParamsValid(ACanvas,ARect,lst,true); if not result then exit; DC := ACanvas.Handle; maxTime :=-1; maxDiff :=-1; minTime := MaxInt; minDiff := MaxInt; for i := 0 to lst.Count-1 do begin p := lst[i]; if (p = nil) or (p^.FCount = 0) then continue; p^.FTimeC := p^.FTime / p^.FCount; p^.FDiffC := p^.FDiff / p^.FCount; if p^.FTimeC > maxTime then maxTime := p^.FTimeC; if p^.FTimeC < minTime then minTime := p^.FTimeC; if p^.FDiffC > maxDiff then maxDiff := p^.FDiffC; if p^.FDiffC < minDiff then minDiff := p^.FDiffC; end; wTime := (maxTime - minTime) * 0.1; minTime := minTime - wTime; maxTime := maxTime + wTime; wDiff := (maxDiff - minDiff) * 0.1; minDiff := minDiff - wDiff; maxDiff := maxDiff + wDiff; with ACanvas do begin Font.Height := -trunc(ASize * Font.PixelsPerInch/72); wBar := TextWidth('F=0000.00000') div 2; // end; //-- ----------------------------- oldM := SetGraphicsMode(DC, GM_ADVANCED); //-- ------------------------------------------------------ FillChar(xFrm,SizeOf(xFrm),0); //-- ------------------------------------- xFrm.eM11 := AZoom; // , =1 xFrm.eM22 := AZoom; // , =1 xFrm.eDx := lst.FOffX; // X, xFrm.eDy := lst.FOffY; // Y, //-- -------------------------------------- SetWorldTransform(DC, xFrm); rct := ARect; rct.Top := rct.Top + 10; rct.Bottom := rct.Top + round ( ASize * 190/6.5); // if wTime <> 0 then wTime := (rct.Bottom - rct.Top) / (minTime - maxTime); if wDiff <> 0 then wDiff := (rct.Bottom - rct.Top) / (minDiff - maxDiff); try with ACanvas do begin val := rct; val.Left := val.Left + wBar; val.Right := val.Left + wBar; Pen.Width := 1; for i := 0 to lst.Count-1 do begin p := lst[i]; if (p = nil) or (p^.FCount = 0) then continue; Pen.Color := Darker(p^.FColor,10); //-- ------------------------------- OffsetRect (val,wBar,0); Brush.Color := Lighter(p^.FColor,50); val.Top := val.Bottom-round (wTime*(minTime-p^.FTimeC)); Rectangle(val); p^.FTimeR := val; //-- -------------------------- OffsetRect (val,wBar,0); Brush.Color := Lighter(p^.FColor,10); val.Top := val.Bottom-round (wDiff*(minDiff-p^.FDiffC)); Rectangle(val); p^.FDiffR := val; OffsetRect (val,wBar,0); end; for i := 0 to lst.Count-1 do begin p := lst[i]; if (p = nil) or (p^.FCount = 0) then continue; Brush.Style := bsClear; Font.Color := Darker(p^.FColor,10); val := p^.FTimeR; str := 't='+FormatFLoat('#0.000#',p^.FTimeC); OffsetRect(val,-1,HeightRect(val)+2); if lst.GDIPlus then DrawGDIPlusText (ACanvas, val, 0, ASize, str) else TextOut (val.Left,val.Top,str); Font.Color := Darker(p^.FColor,30); val := p^.FDiffR; str := 'f='+FormatFLoat('#0.000#',p^.FDiffC); OffsetRect(val,1,-TextHeight(str)-2); if lst.GDIPlus then DrawGDIPlusText (ACanvas, val, 0, ASize, str) else TextOut (val.Left, val.Top,str); val := p^.FDiffR; str := p^.FName; val.Top := val.Bottom+TextHeight(str)+2; val.Bottom := ARect.Bottom; if lst.GDIPlus then DrawGDIPlusText (ACanvas, val, 30, ASize, str) else begin fnt := CreateRotatedFont(Font, -30); tmp := SelectObject(DC,fnt); try TextOut (val.Left,val.Top, str); finally SelectObject(DC, tmp); DeleteObject(fnt); end; end; end; end; finally xFrm.eM11 := 1; xFrm.eM22 := 1; xFrm.eDx := 0; xFrm.eDy := 0; SetWorldTransform(DC, xFrm); //-- --------------------------------------------- SetGraphicsMode(DC, oldM); end; end;
ダウンロード:Delphi XE 7ソース(70 Kb)