Why are all coordinates and sizes weird?

The following image appeared in this code.

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

If the bitmap is 300x300, why , does the white rectangle (0, 0, 400, 400) occupy only a small part? Why does not match the text?

I'm not even sure what the conditions are for Google. I seek wisdom.

+4
source share
2 answers

NOTE: adding this after the proposed reward in addition to my original answer

For starters, there is no need for a 400x400 background rectangle because you only show a 300x300 bitmap, so here is the first change:

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

With this change in place, the conclusion will be exactly the same, but it will simplify the explanation.

Where possible and logical, WPF uses DIP (device-independent pixels) as units, not pixels. When you do this:

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

Ultimately, you will not have a Rectangle 100x100 physical pixels. If your device has more (or less) 96 pixels per physical inch, then in the end you will have a different number of physical pixels. I think 96 ppi is a kind of industry standard. Modern devices, such as smartphones and tablets, have much more pixels per physical inch. If WPF used physical pixels as a unit of measurement, then the aforementioned Rectangle will display less on such a device.

Now for rendering a bitmap (or JPEG, PNG, GIF, etc.), you must use pixels using the device, since this is a rasterized format (not a vector format). And this is what you specify when calling the RenderTargetBitmap constructor. You say you want the resulting bitmap to be 300x300 physical pixels with DPI 40. Since the source has DPI 96 (assuming your monitor is an industry standard) and the target has DPI 40, it must compress the source to set the target . Therefore, the effect is a shrunken image in your bitmap rendering.

Now, what you really want to do is make sure the source DPI and destination DPI are the same. This is not as simple as hard coding 96, because, as discussed, this is just a standard - the source can actually have more or less DPI than that. Unfortunately, WPF does not provide a good way to get DPI, which, in my opinion, is ridiculous. However, you can do a little p / invoke to get it:

 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); } 

So now you can change the corresponding line of code:

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

And it will work regardless of the device on which you work. You will always have a raster image whose size is 300 × 300, and the source will always fill it accurately.

+4
source

You specified 40 DPI when you need 96:

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

Source: https://habr.com/ru/post/1383324/


All Articles