トップ回答者
Image.PhysicalDimension プロパティと文字表示について

質問
-
はじめまして。
Visual Basic 2010を使った画像作成処理で少々困っている事が有るのでご相談させてください。
拡張メタファイルの画像を作成する処理を作成しているのですが、
一部のPCだけ文字が縦横比を引き延ばした表示になる現象が出ています。
調査のために描画処理中のパラメータ値を取得してもらったところ、
Image.PhysicalDimension プロパティから出力サイズをプログラム中で
計測する箇所があり、問題が出ているPCは正方形のイメージでプロパティ値を
取得しているのに幅、高さの比率が5:6や3:4で出ていました。
120DPIのディスプレイでも同じサイズで作成するためにImage.PhysicalDimension プロパティを
利用してサイズを調整した結果、ワールド座標の縦横比が1:1で無くなるために文字が引き延ばされる様です。
文字の縦横比の異常はこの値に比例しているので、このImage.PhysicalDimension プロパティの
異常値の原因を知りたいのですが、プロパティの詳細を知らないため行き詰まっています。
1.このような現象がでるPCでも拡張メタファイルで文字の縦横比を正しく表示する手順や方法は有りますか?
2.Image.PhysicalDimension プロパティの縦横比異常はディスプレイの設定等が関係しているのでしょうか?
この問題はレジストリ等の変更で修正することは可能でしょうか?
回答
-
ディスプレイの DC を利用して生成した拡張メタファイルの DPI の変更はできないようですね...
このため、描画時に拡張メタファイルの DPI を無視して、描画するときに描画する環境の DC の DPI を元に描画するようにするしかないかもしれません。
このプログラムの目的は拡張メタファイルを作成し、他のアプリでも縦横比を正しく描画させたいという事でしょうか?
もし、作成中のアプリで拡張メタファイルの作成も描画も行うのであれば、拡張メタファイルの作成時には DPI を無視して描画を行うようにし、描画時にのみディスプレイ DC の DPI 比を元に描画する方法での対応はどうでしょうか?
- 回答としてマーク 広い空 2015年1月21日 2:10
すべての返信
-
状況のご説明ありがとうございました。
拡張メタファイルこちらはビットマップに変換する事はしておらず最初から最後まで拡張メタファイルで扱っております。
残念ながら、拡張メタファイルでは参考URLのHorizontalResolution、およびVerticalResolutionプロパティは使用できないプロパティの様で、取得しようとするとエラーが発生しました。これらの値は解決手段としては使用できなかったようです。
現状では引き延ばした表示になる時の値が取得出来るため、拡張メタファイルの描画処理で文字の縦横比を調整できないか描画手順を確認中ですが、プログラムでは文字縦横比変更の成功実績がありません。
Pathを使用する方法は小さなフォントを画面表示する際に文字がつぶれて見えない事から、手法的に対象外としております。
こちら問題について別の投稿に分けた方が良い場合は御教授ください。
-
これはすでに作成済みのファイルならサイズが取得出来るということでしょうか?
こちらはプログラム内で拡張メタファイルを作成し、動的に描画する処理でのサイズ調整となります。
動的にイメージを作成するためか、この処理内ではHorizontalResolutionやVerticalResolutionはエラーとなるようです。
VBになりますが、処理を集約したソースを添付致します。ソースの貼り付けに不慣れなため見苦しいですがその点はご容赦ください。
標準モジュールとフォームを1個ずつ、フォームにはPictureBoxとButtonを1個ずつ貼り付けます。
PictureBoxの名前をPicBox、Buttonの名前はBtnDrawと定義してお試しください。
’===API定義(モジュール)==================================== Imports System.Runtime.InteropServices Module DefineModule '----- Win32APIインポート定義 -------------------------- <DllImport("User32.dll", EntryPoint:="GetDC", SetLastError:=True, CharSet:=CharSet.Unicode, _ ExactSpelling:=True, CallingConvention:=CallingConvention.StdCall)> _ Public Function GetDC(ByVal hWnd As IntPtr) As IntPtr End Function <DllImport("user32.dll", EntryPoint:="ReleaseDC")> _ Public Function ReleaseDC(ByVal hwnd As IntPtr, ByVal hdc As IntPtr) As IntPtr End Function <DllImport("gdi32.dll")> _ Public Function GetDeviceCaps(ByVal hdc As IntPtr, ByVal nIndex As Int32) As Int32 End Function End Module ’===フォーム内処理==================================== Imports System.Drawing Imports System.Drawing.Imaging Imports System.Runtime.Serialization Imports System.Text Imports System.Drawing.Drawing2D.CoordinateSpace Imports System Imports System.Runtime.InteropServices Public Class MainForm Private Area As SizeF = New SizeF(2000, 2000) ' 領域サイズ Public Frame As SizeF = New SizeF(1800, 1800) ' 枠線のサイズ Private Sub BtnDraw_Click(sender As System.Object, e As System.EventArgs) Handles BtnDraw.Click Dim h As IntPtr = GetDC(PicBox.Handle) Dim rf As RectangleF Dim ptf(0) As PointF ' 座標変換用一時変数 Using mf2 As New Metafile(h, New RectangleF(New PointF(0, 0), Area), _ MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly) 'Dim mfv As Integer = mf2.VerticalResolution 'Dim mfh As Integer = mf2.HorizontalResolution ' GraphicsからWorld座標を定義して実サイズを取得する Using g As Graphics = Graphics.FromImage(mf2) g.PageUnit = GraphicsUnit.Millimeter ' 1ミリ単位 g.PageScale = 0.01F ' MM_HIMETRIC換算×論理座標係数 ptf(0) = mf2.PhysicalDimension.ToPointF ' デバイスサイズ取得 g.TransformPoints(Device, Page, ptf) ' 論理サイズに変換 End Using End Using ' 物理サイズから目的サイズのメタファイルを作るのに必要なRectangleFを割り出す rf = New RectangleF(0, 0, Area.Width * ptf(0).X, Area.Height * ptf(0).Y) Dim mf As New Metafile(h, rf, MetafileFrameUnit.GdiCompatible, EmfType.EmfOnly) ' 描画領域の作成と座標系の定義 Using g As Graphics = Graphics.FromImage(mf) g.PageUnit = GraphicsUnit.Millimeter ' 1ミリ単位 g.PageScale = 0.01F ' 描画スケール指定 g.TranslateTransform(1000, 1000) ' 原点位置の移動 Dim PS As New RectangleF(-Area.Width / 2, -Area.Height / 2, Area.Width, Area.Height) g.FillRectangle(Brushes.White, PS) ' 枠線と文字列の描画 Using p As New Pen(Color.Black, 50) Dim FRecT As New Rectangle(-Frame.Width / 2, -Frame.Height / 2, Frame.Width, Frame.Height) g.DrawRectangle(p, FRecT) ' 外枠の描画 End Using ' 文字配置、背景色を設定 Using fs As StringFormat = New StringFormat(StringFormatFlags.NoClip), _ hBrush As New SolidBrush(Color.Red), _ f As Font = New Font("MS P明朝", 300, FontStyle.Regular, GraphicsUnit.World, 128, False) g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias fs.Alignment = StringAlignment.Center g.ScaleTransform(1, 1) g.DrawString("テスト出力", f, hBrush, 0, 0, fs) g.ResetTransform() g.ScaleTransform(1, 1) End Using End Using ReleaseDC(PicBox.Handle, h) PicBox.Image = mf End Sub End Class
-
Metafile の生成方法によっては取得できないんですね...
提示いただいたソースを見ると DC を元に Metafile を生成しているようですので、
マシンの DC の情報を取得しても等価な処理は可能に思います。
Dim LOGPIXELSX As Integer = 88
Dim LOGPIXELSY As Integer = 90
GetDeviceCaps(h, LOGPIXELSX)
GetDeviceCaps(h, LOGPIXELSY)
取得できた値を HorizontalResolution や VerticalResolution の
変わりに利用してみてはどうでしょうか?
ちなみに、動的に描画するのであれば、
Paint イベントハンドラでも処理可能に思いますが、
対応できない部分があるということでしょうか? -
DCの取得元で値が変わるかもしれないということでしょうか。
現在の処理はVB6のソースを参考につくりかえた物で、DCそのものについて根本的な事を
認識せずに作っていたので、GetDCに0を指定する方法は知りませんでした。
問題対応するにもフォントの縦横比を変える処理順序を忘れており、頭を抱えてたのですが、
DCを変えて値が変わるか調査する価値は有りそうです。あと、HorizontalResolution等の設定が可能か。
この値は取得できなかたっため、設定はまだ試していません。
本件の処理は画像の作成・取り出しが目的なので、画像の作成処理は別イベントで行っています。
PaintイベントはPictureBoxの自動処理に任せてしまうのでここのGraphicsオブジェクトは使わないです。
-
HorizontalResolution や VerticalResolution と等価な値として
GetDeviceCaps(h, LOGPIXELSX), GetDeviceCaps(h, LOGPIXELSY) が
使えないかと思ったりしました。
# 今は手元に縦横でDPIの異なるディスプレイがないため確認できてませんが...
この値を使って縦横比が 1:1 となるように描画位置を計算しながら
描画することで対応ができるのではないかと思います。
これらの値は DC に依存しますが、
ディスプレイの DC はディスプレイのハードによって変わります。
このため、描画時の環境に応じて描画位置を変更することにより
どの環境でも正しい縦横比で描画できます。
- 回答の候補に設定 星 睦美 2015年1月9日 7:43
-
アドバイスありがとうございます。デバイス値の取得を試行してきましたが、
サンプルソースでPhysicalDimensionを取得してScaleTransformを設定しているので、
先日のサンプルソースを元にScaleTransformのパラメータで縦横比調節を試しましたが、文字の縦横比が
拡張メタファイルの場合に延ばせない事に気がつきました。
文字の引き延ばしが出来ない限り、アドバイスで値の取得が可能になっても根本的な解決にならなかった様です。
VB6ではLogFontを使って作成出来たのですが、VB.NETでLogFontによるフォント作成は困難で技術的に実現しておりません。
サンプル等が有ればと思いますが、最初の投稿から話が変わってしまっています。別スレッドで質問をした方が良いでしょうか?
-
ディスプレイの DC を利用して生成した拡張メタファイルの DPI の変更はできないようですね...
このため、描画時に拡張メタファイルの DPI を無視して、描画するときに描画する環境の DC の DPI を元に描画するようにするしかないかもしれません。
このプログラムの目的は拡張メタファイルを作成し、他のアプリでも縦横比を正しく描画させたいという事でしょうか?
もし、作成中のアプリで拡張メタファイルの作成も描画も行うのであれば、拡張メタファイルの作成時には DPI を無視して描画を行うようにし、描画時にのみディスプレイ DC の DPI 比を元に描画する方法での対応はどうでしょうか?
- 回答としてマーク 広い空 2015年1月21日 2:10