Getting a bmp file into a RichTextBox: resizing without losing quality + dealing with windows scaling

178 Views Asked by At

Goal:

My goal is to get a picture (a bmp file) into a RichTextBox, at a size I choose, say 400 pixels high, and width scaled to match, without it being ruined by Windows Scaling (125%, 150%, 200% etc.), or losing quality.

Here's my code:

<DllImport("user32.dll", EntryPoint:="OpenClipboard")> Private Shared Function OpenClipboard(ByVal hWnd As IntPtr) As Boolean
End Function
<DllImport("user32.dll", EntryPoint:="EmptyClipboard")> Private Shared Function EmptyClipboard() As Boolean
End Function
<DllImport("user32.dll", EntryPoint:="SetClipboardData")> Private Shared Function SetClipboardData(ByVal uFormat As Integer, ByVal hWnd As IntPtr) As IntPtr
End Function
<DllImport("user32.dll", EntryPoint:="CloseClipboard")> Private Shared Function CloseClipboard() As Boolean
End Function
<DllImport("gdi32.dll", EntryPoint:="CopyEnhMetaFileA")> Private Shared Function CopyEnhMetaFile(ByVal hemfSrc As IntPtr, ByVal hNULL As IntPtr) As IntPtr
End Function
<DllImport("gdi32.dll", EntryPoint:="DeleteEnhMetaFile")> Private Shared Function DeleteEnhMetaFile(ByVal hemfSrc As IntPtr) As Boolean
End Function

Private Sub PasteMeta(imgPathName As String)
    'Create a bitmap of the image you want to draw to the metafile
    Dim bm As New Bitmap(imgPathName)

    'Create a graphics object of the Form and get its hdc handle
    Dim me_grx As Graphics = Me.CreateGraphics()
    Dim me_hdc As IntPtr = me_grx.GetHdc()
    Dim scale = 400 / bm.Height
    'Create a new metafile the same size as the image and create a graphics object for it
    Dim emf As New Imaging.Metafile("Tmp.emf", me_hdc, New Rectangle(0, 0, bm.Width * scale, 400), Imaging.MetafileFrameUnit.Point)
    Dim emf_gr As Graphics = Graphics.FromImage(emf)


    emf_gr.DrawImage(bm, 0, 0, CInt(bm.Width * scale) * _windowsScaling, CInt(bm.Height * scale) * _windowsScaling)

    'Dispose the bitmap and the metafile graphics object
    emf_gr.Dispose()
    bm.Dispose()

    PutEnhMetafileOnClipboard(Me.Handle, emf)
    'Paste the new metafile in the RichTextBox
    JournalRichTextBox.Paste()
    'Dispose the rest of the objects we created
    me_grx.ReleaseHdc(me_hdc)
    me_grx.Dispose()
    emf.Dispose()
End Sub

Private Shared Function PutEnhMetafileOnClipboard(ByVal hWnd As IntPtr, ByVal mf As Imaging.Metafile) As Boolean
    Dim bResult As New Boolean()
    bResult = False
    Dim hEMF, hEMF2 As IntPtr
    hEMF = mf.GetHenhmetafile() ' invalidates mf
    If Not hEMF.Equals(New IntPtr(0)) Then
        hEMF2 = CopyEnhMetaFile(hEMF, New IntPtr(0))
        If Not hEMF2.Equals(New IntPtr(0)) Then
            If OpenClipboard(hWnd) Then
                If EmptyClipboard() Then
                    Dim hRes As IntPtr
                    hRes = SetClipboardData(14, hEMF2)    ' 14 == CF_ENHMETAFILE
                    bResult = hRes.Equals(hEMF2)
                    CloseClipboard()
                End If
            End If
        End If
        DeleteEnhMetaFile(hEMF)
    End If
    Return bResult
End Function

Here's what happens to the RTB when I run the sub: https://i.stack.imgur.com/QaC3b.png

Background and what I've tried:

My first go was to just use the bitmap size property, but that really ruins the quality - it's completely useless.

Then I found this magical way that sorted the quality. To be brief, it's the top answer here: https://social.msdn.microsoft.com/Forums/vstudio/en-US/355bfc59-cd0a-4e81-984e-9f066fd3a897/richtextbox-images-quality-loss?forum=vbgeneral

In summary, it makes a metafile and graphic, does the resizing with it and puts it into the clipboard and then pastes that into the richtextbox.

I got that working nicely. However, I have my 4K screen scaled at 200% and that means that the picture is the right size but the metafile it is put in is twice the size.

I have spent about 4 hours googling and trying everything I could find to work around this. No matter what I do, the DPI I get for the form, or controls within it, is always 96, so I can't use one of the DpiX solutions.

If I add:

<DllImport("User32.dll")>
Private Shared Sub SetProcessDPIAware()
End Sub

Public Sub New()
    SetProcessDPIAware()
    InitializeComponent()
End Sub

It just makes the form unscaled (some buttons and labels still are scaled though - weird!)

Trying to override the scalecontrol event always returns a factor.X, factor.Y = 1, never anything else.

What I'd like if possible:

Please note that the BEST solution would be to just get a number for the scale factor. 125% = 1.25, 150% = 1.5 etc. If I have that number, I can just multiply the dimensions of the graphic by it and it fits perfectly (for example)

I think my main problem is I've got this working using too much code that I just don't understand. I would really appreciate you could bare that in mind when answering, I might not understand your answer if it's giving me too much doubt-benefit. I think the solution might be here: https://msdn.microsoft.com/en-us/library/cc250585.aspx

But that's way over my head at the mo. So any help or advice would be REALLY appreciated.

FYI I am targeting 3.5 and would like to stick to that if possible.

0

There are 0 best solutions below