2011-11-27 106 views
4

此代码生成以下图像。为什么所有的坐标和大小都很奇怪?

DrawingVisual visual = new DrawingVisual(); 
DrawingContext ctx = visual.RenderOpen(); 

FormattedText txt = new FormattedText("45", CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Verdana"), 100, Brushes.Red); 
ctx.DrawRectangle(Brushes.White, new Pen(Brushes.White, 10), new System.Windows.Rect(0, 0, 400, 400)); 
ctx.DrawText(txt, new System.Windows.Point((300 - txt.Width)/2, 10)); 
ctx.Close(); 

RenderTargetBitmap bity = new RenderTargetBitmap(300, 300, 40, 40, PixelFormats.Default); 
bity.Render(visual); 
BitmapFrame frame = BitmapFrame.Create(bity); 
JpegBitmapEncoder encoder = new JpegBitmapEncoder(); 
encoder.Frames.Add(frame); 
MemoryStream ms = new MemoryStream(); 
encoder.Save(ms); 

test image

如果位图是300×300,为什么不白色矩形(0,0,400,400)仅占据它的一小部分? 为什么不是文字居中?

我甚至不确定谷歌有什么条款。我寻求智慧。

+0

尝试使用ActualWidth而不是宽度 – Tigran

+0

@Tigran,这可能会解决问题,但它不会传达任何理解*为什么会发生这种情况。 – Amy

+0

@lnuyasha:宽度是尺寸声明,ActualWidth是实际在您的机器的显示器上呈现的尺寸。 – Tigran

回答

4

注:除了提供给我原来的答复

对于新手赏金后加入这一点,有没有必要400x400背景矩形,因为您只渲染300x300位图,所以这里是第一次更改:

ctx.DrawRectangle(Brushes.White, new Pen(Brushes.White, 10), new System.Windows.Rect(0, 0, 300, 300)); 

随着硫当地的变化,产量将完全相同,但它简化了解释。

在可能和合乎逻辑的情况下,WPF使用DIP(设备无关像素)作为度量单位而不是像素。当你这样做:

<Rectangle Width="100" Height="100"/> 

您不一定有Rectangle是100x100的物理像素结束。如果您的设备每个物理英寸具有更多(或更少)不超过96个像素,那么您将以不同数量的物理像素结束。我猜,每英寸96像素是行业标准。像智能手机和平板电脑这样的现代设备每个物理英寸的像素数量要多得多如果WPF使用物理像素作为度量单位,那么上述Rectangle会在这样的设备上变得更小。

现在,为了渲染位图(或者JPEG,PNG,GIF等),必须使用设备依赖像素,因为它是栅格化格式(而不是矢量格式)。这就是您在调用RenderTargetBitmap构造函数时指定的内容。您要告诉它,您希望得到的位图的DPI为40,物理像素为300x300。由于源的DPI为96(假设您的显示器为行业标准)并且目标的DPI为40,它必须缩小来源适合目标。因此,效果是渲染位图中缩小的图像。

现在你想真的想要做的是确保源DPI和目标DPI匹配。这并不像硬编码96那么简单,因为正如所讨论的那样,这只是一个标准 - 实际上源可能具有或多或少的DPI。不幸的是,WPF没有提供一个很好的获取DPI的方法,这在我看来是荒谬的。然而,你可以做一点的P/Invoke的获得它:

public int Dpi 
{ 
    get 
    { 
     if (this.dpi == 0) 
     { 
      var desktopHwnd = new HandleRef(null, IntPtr.Zero); 
      var desktopDC = new HandleRef(null, SafeNativeMethods.GetDC(desktopHwnd)); 

      this.dpi = SafeNativeMethods.GetDeviceCaps(desktopDC, 88 /*LOGPIXELSX*/); 

      if (SafeNativeMethods.ReleaseDC(desktopHwnd, desktopDC) != 1 /* OK */) 
      { 
       // log error 
      } 
     } 

     return this.dpi; 
    } 
} 

private static class SafeNativeMethods 
{ 
    [DllImport("User32.dll")] 
    public static extern IntPtr GetDC(HandleRef hWnd); 

    [DllImport("User32.dll")] 
    public static extern int ReleaseDC(HandleRef hWnd, HandleRef hDC); 

    [DllImport("GDI32.dll")] 
    public static extern int GetDeviceCaps(HandleRef hDC, int nIndex); 
} 

所以,现在你可以改变的代码来此相关线路:

RenderTargetBitmap bity = new RenderTargetBitmap(300, 300, this.Dpi, this.Dpi, PixelFormats.Default); 

且不论它会工作的设备你正在运行。你总是会得到一个300x300像素的位图,并且源代码将始终完全填充它。

+0

非常好,谢谢。赏金颁发。 – Amy

3

你指定的40 DPI时,您希望96:

RenderTargetBitmap bity = new RenderTargetBitmap(300, 300, 96, 96, PixelFormats.Default); 
+0

我想,真的,我正在寻找的答案是*为什么*我需要使用96而不是40. – Amy

相关问题