2011-11-27 70 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

相關問題