0

As part of a larger tool I'm working displaying pieces of a bitmap in a picturebox. In my current test case, I want to take a 200x200 section of the bitmap and display it in a 200x200 picturebox (clientsize area). The code runs, but I find I'm actually getting 600x600 pixels from the bitmap in the picturebox. This is all running on a 4K monitor, and I suspect some dpi-based scaling is the culprit. I will want to use that scaling to allow zoom, but I can't see why it's happening at all right now.

Here's the code - the src_rect and dest_rect are identical, both 200x200, and this variant of DrawImage shouldn't be scaling on its own:

Public Overrides Sub Draw_Data(Pbox As PictureBox, Src_Rect As Rectangle, Dst_Rect As Rectangle)
    ''Pbox is the destination picturbox:
    Dim bm As New Bitmap(Pbox.ClientSize.Width, Pbox.ClientSize.Height)
    Dim gr As Graphics = Graphics.FromImage(bm) 'So GR relates to drawing destination.

    gr.PixelOffsetMode = Drawing2D.PixelOffsetMode.HighQuality
    gr.SmoothingMode = Drawing2D.SmoothingMode.None
    gr.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic 

    '' Bitmap is the underlying data, a subset of which should be shown:
    gr.DrawImage(Bitmap, Dst_Rect, Src_Rect, GraphicsUnit.Pixel)
    Pbox.Image = bm
End Sub

I've played with the various gr.* settings, as well as the picturebox properties, trying pretty much anything reasonable, but I always get the same result. If it's an issue of bitmap "resolution", that's odd because the originating bitmap is (at least to me, conceptually) just an array of pixels. It would seem that a mapping of a 200x200 subset of that to a 200x200 bitmap used in the picturebox would be one-to-one. Does anyone see what I might be missing? If there's some scaling I have to apply, I can certainly do it, but I'd have to have some way of at least measuring the kind of weird scaling that's going on before I can compensate for it.

  • [How to configure an app to run correctly on a machine with a high DPI setting (e.g. 150%)?](https://stackoverflow.com/a/13228495/7444103) -- [High DPI support in Windows Forms](https://learn.microsoft.com/en-us/dotnet/framework/winforms/high-dpi-support-in-windows-forms#configuring-your-windows-forms-app-for-high-dpi-support). Reason (read the notes): [Image is not drawn at the correct spot](https://stackoverflow.com/a/51456467/7444103) – Jimi May 02 '20 at 12:34
  • I've read about the high DPI settings, etc., think I've done all of that, will double check. However, I'm not sure how that problem explains the behavior I'm seeing - and if it is, I'd like to be able to programmatically check for it. I can see how the DPI issues might change the displayed size of the picturebox, but not how they would turn an extracted 200x200 array of pixels into something different. – Bryan A. Bentz May 02 '20 at 13:20
  • I believe you will need to make the section have the same display resolution criteria as the source bitmap to get the effect you want. Add this after declaring/initializing 'bm': `bm.SetResolution(Bitmap.HorizontalResolution, Bitmap.VerticalResolution)` – TnTinMn May 02 '20 at 13:25
  • I made a number of stabs at the high-DPI settings as Jimi suggested; weirdly, things are better, but rather than getting 600x600 mapped into 200x200, I now get 300x300 mapped into 200x200. So I think that's the crux of the issue, but not sure how to robustly measure/fix it. TnTinMn, I tried the SetResolution you suggested, it had no effect (I'd thought that had a solid shot at fixing it). – Bryan A. Bentz May 02 '20 at 13:52
  • The Bitmap you're drawing is not the *same bunch of bytes*. You're interpolating bitmapdata in a device context. The DPI setting is taken into consideration when the bitmapdata is rendered in a device context. When two DCs have different DPI settings, the result depends on how the interpolation function evaluates the difference. It may also applies when you present a Bitmap using an existing Control (which has its own DC). You can redefine the source Bitmap DPI setting to the current `DeviceDpi` and generate a new Bitmap with the same DPI scale before drawing a section of the original. – Jimi May 02 '20 at 14:06
  • If your application is not DpiAware, you assume 96 DPI (unless - Windows 10 new exploits - the System gets nosy) and work it all out using this value. Since your app will probably need to be DPI aware instead, you need to consider both the current DPI settings and Bitmaps setting when drawing on different Device Contexts. If you use PhotoShop, when you change the DPI value of an Image, PS also redefines (scales) the Width and Height values, since the DPI value is one of the factors that describe how the Bitmap is rendered in the current context. – Jimi May 02 '20 at 14:12
  • Jimi, I'm beginning to get it. I did try: Dim gr As Graphics = Graphics.FromImage(bm) Bitmap.SetResolution(gr.DpiX, gr.DpiY) thinking that might make sure that the bitmap and graphics class instance had the same resolution. I hadn't thought of creating an entirely new bitmap. In brief, I've tried to set the resolution of everything involved to the same values, without much luck yet. Maybe just using the DPI resolution of the graphics instance and the bitmap will tell me the scaling value that will be applied; I can then back that out using the source/destination rectangle sizes. – Bryan A. Bentz May 02 '20 at 14:29
  • Jimi, it is a DPIAware application. Is there any decent high-level way to get the device resolution, for each Device Context? I just started a trial, trying to grab the device context for the picturebox and determine its resolution, feels like I'm back in VB6, loading gdi32.dll, etc. Maybe that approach still works, just feels aesthetically "off" (especially on a 64-bit machine). I'll do a bit of searching to see if there's a more 'modern' way to get Device Context information. (For some reason my VS 'help'/examples are all C#, so I'm also mentally translating all examples...) – Bryan A. Bentz May 02 '20 at 15:17
  • You need to prefix a nickname with `@` to ping someone. I came here by chance. -- As already mentioned, `DeviceDpi` (property of the Form class) gives you the current DPI setting (if the app is DpiAware). Many other sources (I won't mention these in comments) can return the same value(s) -- The Visual Studio `Help` (F1), sends you to the MSDN Docs: in the upper band, you can find the language selector. – Jimi May 02 '20 at 20:08
  • The typical method to determine DPI is to create a Graphics for a control (Control.CreateGraphics) and check its Dpi(X/Y) properties. Also, the Graphics object created from a Bitmap will have its DPI based on its Horizontal/Vertical resolution. You can also try the Control.DeviceDpi property, but my observation is that this returns the raw physical device DPI and does not account for the Windows scale factor. – TnTinMn May 03 '20 at 02:25
  • You have indicated that the application DPI aware; however have you changed the form's AutoScaleMode property from the designer default of Font? Until you resolve your issues, I would recommend using Inherit or None; this will prevent any form scaling until you get settled. Later, you can change it to DPI. AutoScaleMode's purpose to maintain _as-designed size_ when the code runs under different DPI's. – TnTinMn May 03 '20 at 02:25
  • @Jimi, TnTinMn, problem solved, thank you both for all your help. The solution was really weird too - it had to do with how I was reading in the image, the resolution was a default and not the real one. Once fixed, the above DPI logic works fine. I could only track this down by examining the DPI res at every step, and it became clear the issue was in the base image data I had in hand. – Bryan A. Bentz May 04 '20 at 15:04
  • That was the reason why I posted those links in the very first comment :). But, of course, testing it yourself is better. – Jimi May 04 '20 at 15:08
  • @Jimi, I'm still not sure how the bitmap could be internally inconsistent; I read it in as an image, then convert to bitmap. The original image data has a resolution of 300 dpi, the resulting bitmap reported 96 (iirc), but seemed somehow to also 'know' it had a higher res internally - it was that discrepancy which was the issue. The display form I build on the fly, I just wanted to show M by N pixels, didn't care how big or tiny that autosize picturebox ended up - so at least conceptually bm resolution shouldn't matter. (Ultimately the displayed data will be computed, not a bitmap). – Bryan A. Bentz May 04 '20 at 15:34

0 Answers0