none
Image.PhysicalDimension プロパティと文字表示について RRS feed

  • 質問

  • はじめまして。

    Visual Basic 2010を使った画像作成処理で少々困っている事が有るのでご相談させてください。

    拡張メタファイルの画像を作成する処理を作成しているのですが、

    一部のPCだけ文字が縦横比を引き延ばした表示になる現象が出ています。

    調査のために描画処理中のパラメータ値を取得してもらったところ、

    Image.PhysicalDimension プロパティから出力サイズをプログラム中で

    計測する箇所があり、問題が出ているPCは正方形のイメージでプロパティ値を

    取得しているのに幅、高さの比率が5:6や3:4で出ていました。

    120DPIのディスプレイでも同じサイズで作成するためにImage.PhysicalDimension プロパティを

    利用してサイズを調整した結果、ワールド座標の縦横比が1:1で無くなるために文字が引き延ばされる様です。

    文字の縦横比の異常はこの値に比例しているので、このImage.PhysicalDimension プロパティの

    異常値の原因を知りたいのですが、プロパティの詳細を知らないため行き詰まっています。

    1.このような現象がでるPCでも拡張メタファイルで文字の縦横比を正しく表示する手順や方法は有りますか?

    2.Image.PhysicalDimension プロパティの縦横比異常はディスプレイの設定等が関係しているのでしょうか?

      この問題はレジストリ等の変更で修正することは可能でしょうか?

    2014年12月25日 0:54

回答

  • ディスプレイの DC を利用して生成した拡張メタファイルの DPI の変更はできないようですね...

    このため、描画時に拡張メタファイルの DPI を無視して、描画するときに描画する環境の DC の DPI を元に描画するようにするしかないかもしれません。

    このプログラムの目的は拡張メタファイルを作成し、他のアプリでも縦横比を正しく描画させたいという事でしょうか?

    もし、作成中のアプリで拡張メタファイルの作成も描画も行うのであれば、拡張メタファイルの作成時には DPI を無視して描画を行うようにし、描画時にのみディスプレイ DC の DPI 比を元に描画する方法での対応はどうでしょうか?

    • 回答としてマーク 広い空 2015年1月21日 2:10
    2015年1月14日 1:33

すべての返信

  • ディスプレイの持つ値によります。
    # 1ドットのサイズが縦横で異なるディスプレイもあります。

    下記のページを参考にできると思います。

    画像のdpi(画像解像度)を取得/設定するには?[C#、VB] - @IT
     <http://www.atmarkit.co.jp/fdotnet/dotnettips/961dpiresolution/dpiresolution.html>

    2014年12月25日 9:19
  • 情報ありがとうございます。

    この画像解像度は拡張メタファイルのようなベクタ形式の画像でも有効なのでしょうか。

    明日から休みに入るため、検証出来るソースに触れず返信が遅くなる見込みですが、

    なるべく期間中に検証をしてみようと思います。


    2014年12月26日 7:12
  • 拡張メタファイルを DC に描画するときに、縦長や横長になってはまったことがあります。
    ベクタ画像をビットマップに変換するときにおかしくなっていたようです。

    2014年12月26日 7:15
  • 状況のご説明ありがとうございました。

    拡張メタファイルこちらはビットマップに変換する事はしておらず最初から最後まで拡張メタファイルで扱っております。

    残念ながら、拡張メタファイルでは参考URLのHorizontalResolution、およびVerticalResolutionプロパティは使用できないプロパティの様で、取得しようとするとエラーが発生しました。これらの値は解決手段としては使用できなかったようです。

    現状では引き延ばした表示になる時の値が取得出来るため、拡張メタファイルの描画処理で文字の縦横比を調整できないか描画手順を確認中ですが、プログラムでは文字縦横比変更の成功実績がありません。

    Pathを使用する方法は小さなフォントを画面表示する際に文字がつぶれて見えない事から、手法的に対象外としております。

    こちら問題について別の投稿に分けた方が良い場合は御教授ください。

    2015年1月5日 3:26
  • 拡張メタファイルは Metafile クラスで扱うと HorizontalResolution, VerticalResolution が取得できるかと思います。

    Metafile metafile = new Metafile("file.emf");
    int h = metafile.VerticalResolution;

    # C# で失礼...

    # 昔やったときは、最終的にビットマップに変換したのですが、
    # そこに至った経過が思い出せませんでした...

    2015年1月6日 8:03
  • これはすでに作成済みのファイルならサイズが取得出来るということでしょうか?

    こちらはプログラム内で拡張メタファイルを作成し、動的に描画する処理でのサイズ調整となります。

    動的にイメージを作成するためか、この処理内では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


    2015年1月7日 0:55
  • Metafile の生成方法によっては取得できないんですね...

    提示いただいたソースを見ると DC を元に Metafile を生成しているようですので、
    マシンの DC の情報を取得しても等価な処理は可能に思います。

    Dim LOGPIXELSX As Integer = 88
    Dim LOGPIXELSY As Integer = 90
    GetDeviceCaps(h, LOGPIXELSX)
    GetDeviceCaps(h, LOGPIXELSY)

    取得できた値を HorizontalResolution や VerticalResolution の
    変わりに利用してみてはどうでしょうか?

    ちなみに、動的に描画するのであれば、
    Paint イベントハンドラでも処理可能に思いますが、
    対応できない部分があるということでしょうか?

    2015年1月7日 8:33
  • DCの取得元で値が変わるかもしれないということでしょうか。

    現在の処理はVB6のソースを参考につくりかえた物で、DCそのものについて根本的な事を

    認識せずに作っていたので、GetDCに0を指定する方法は知りませんでした。

    問題対応するにもフォントの縦横比を変える処理順序を忘れており、頭を抱えてたのですが、

    DCを変えて値が変わるか調査する価値は有りそうです。あと、HorizontalResolution等の設定が可能か。

    この値は取得できなかたっため、設定はまだ試していません。


    本件の処理は画像の作成・取り出しが目的なので、画像の作成処理は別イベントで行っています。

    PaintイベントはPictureBoxの自動処理に任せてしまうのでここのGraphicsオブジェクトは使わないです。


    2015年1月8日 0:36
  • HorizontalResolution や VerticalResolution と等価な値として
    GetDeviceCaps(h, LOGPIXELSX), GetDeviceCaps(h, LOGPIXELSY) が
    使えないかと思ったりしました。
    # 今は手元に縦横でDPIの異なるディスプレイがないため確認できてませんが...

    この値を使って縦横比が 1:1 となるように描画位置を計算しながら
    描画することで対応ができるのではないかと思います。

    これらの値は DC に依存しますが、
    ディスプレイの DC はディスプレイのハードによって変わります。
    このため、描画時の環境に応じて描画位置を変更することにより
    どの環境でも正しい縦横比で描画できます。

    • 回答の候補に設定 星 睦美 2015年1月9日 7:43
    2015年1月8日 6:19
  • アドバイスありがとうございます。デバイス値の取得を試行してきましたが、

    サンプルソースでPhysicalDimensionを取得してScaleTransformを設定しているので、

    先日のサンプルソースを元にScaleTransformのパラメータで縦横比調節を試しましたが、文字の縦横比が

    拡張メタファイルの場合に延ばせない事に気がつきました。

    文字の引き延ばしが出来ない限り、アドバイスで値の取得が可能になっても根本的な解決にならなかった様です。

    VB6ではLogFontを使って作成出来たのですが、VB.NETでLogFontによるフォント作成は困難で技術的に実現しておりません。

    サンプル等が有ればと思いますが、最初の投稿から話が変わってしまっています。別スレッドで質問をした方が良いでしょうか?

    2015年1月13日 5:44
  • フォーラム オペレーターの星 睦美です。
    こぐま さん、回答ありがとうございます。

    広い空 さん、こんにちは。
    新しいスレッドを作成していただくほうがユーザーが質問に回答しやすいと思いますので、必要であればこちらのスレッドのURL をリンクして新しい質問としてご投稿ください。

    よろしければ今回の質問に役立った返信に投稿者から[回答としてマーク] いただければ幸いです。

    フォーラム オペレーター 星 睦美 - MSDN Community Support

    2015年1月13日 5:55
  • ディスプレイの DC を利用して生成した拡張メタファイルの DPI の変更はできないようですね...

    このため、描画時に拡張メタファイルの DPI を無視して、描画するときに描画する環境の DC の DPI を元に描画するようにするしかないかもしれません。

    このプログラムの目的は拡張メタファイルを作成し、他のアプリでも縦横比を正しく描画させたいという事でしょうか?

    もし、作成中のアプリで拡張メタファイルの作成も描画も行うのであれば、拡張メタファイルの作成時には DPI を無視して描画を行うようにし、描画時にのみディスプレイ DC の DPI 比を元に描画する方法での対応はどうでしょうか?

    • 回答としてマーク 広い空 2015年1月21日 2:10
    2015年1月14日 1:33
  • アドバイスありがとうございました。

    別の調査でFont.FromLogfontによるフォント作成方法が分かりましたが、

    その場合もフォント幅が破棄されてしまう事を確認し、その旨を処理の関係者に報告致しました。

    その後、問題が発生するPCの動作環境をリサーチして、仮想環境(用語に詳しくないため、表現づらいのですが)が

    影響している様なので、その観点で環境確認をお願いしたという次第です。

    全体的なやり取りで、拡張メタファイルの特性が確認する事が出来たため、

    こぐまさんのアドバイスを回答としてマークさせていただきました。

    2015年1月21日 2:22