Load/Resize png image and show in userform
<p>I created an userform in Powerpoint (2016) which allows the user to select some pictures with a filedialog. The filenames are stored in a listbox and the absolute paths in a collection. After an entry becomes selected, the picture should be displayed in the userform (like a preview). Therefore I use a frame control.</p>

<p>Because most of the pictures are png's, I use the GDI libraries to load the file as a bitmap and convert it to an IPicture object. Afterwards I just have to set the IPicture object to the Picture property of the control. These works fine and the picture is displayed correctly.</p>

<p>My problem is that the images have a large resolution (e.g. 1600x1200) and I want to resize the images. My first approach was to set the PictureSizeMode, but the PNG becomes unreadable. It seems that the <strong>PictureSizeModeZoom</strong> is distorting the image. The same effect happens if i use <strong>fmPicturesizeModeClip</strong> and reduce the zoom value.</p>

<p>After searching the web I found some posts about the <strong>render</strong> method (<a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms682202(v=vs.85).aspx" rel="nofollow noreferrer">IPicture::Render (MSDN)</a>). I used a spy program to determine the handle of my frame and then rendered the image with a lower resolution. Unfortunately the picture is still unreadable and looks the same as with the other approaches.</p>

<p>The images contain some measure plots with description labels and legends on it. At least the description header should be readable. If I change the size with paint and load the image afterwards to the userform it looks exactly like I want, but this is not a solution for me!</p>

<p>Is there any other way to reduce the size of an image with VBA and keep it readable?</p>

<p>Thanks for any help!</p>

<p><strong>Update</strong>
Here are two png version of the <a href="http://www.mathworks.com/matlabcentral/answers/uploaded_files/31714/Test1.png" rel="nofollow noreferrer">linked picture</a> i found on google. The first one has the size 1920x921 (reduced automatically from frame control), the second one 400x192 (reduced with paint). Both are loaded to a frame with the size 400x192.</p>

<p><a href="https://i.stack.imgur.com/Y2MFk.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/Y2MFk.png" alt="reduced automatically"></a></p>

<p><a href="https://i.stack.imgur.com/MnfsB.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/MnfsB.png" alt="reduced before"></a></p>

<p>The following code declares all libraries.</p>

<pre><code>'@author    Stephen Bullen, Rob Bovey
'@url       http://www.rondebruin.nl/win/s2/win009.htm

'Declare a UDT to store a GUID for the IPicture OLE Interface
Private Type GUID
    Data1 As Long
    Data2 As Integer
    Data3 As Integer
    Data4(0 To 7) As Byte
End Type

#If VBA7 Then
    'Declare a UDT to store the bitmap information
    Private Type PICTDESC
        Size As Long
        Type As Long
        hPic As LongPtr
        hPal As LongPtr
    End Type

    'Declare a UDT to store the GDI+ Startup information
    Private Type GdiplusStartupInput
        GdiplusVersion As Long
        DebugEventCallback As LongPtr
        SuppressBackgroundThread As Long
        SuppressExternalCodecs As Long
    End Type

    'Windows API calls into the GDI+ library
    Private Declare PtrSafe Function GdiplusStartup Lib "GDIPlus" (token As LongPtr, inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As LongPtr = 0) As Long
    Private Declare PtrSafe Function GdipCreateBitmapFromFile Lib "GDIPlus" (ByVal filename As LongPtr, bitmap As LongPtr) As Long
    Private Declare PtrSafe Function GdipCreateHBITMAPFromBitmap Lib "GDIPlus" (ByVal bitmap As LongPtr, hbmReturn As LongPtr, ByVal background As LongPtr) As Long
    Private Declare PtrSafe Function GdipDisposeImage Lib "GDIPlus" (ByVal image As LongPtr) As Long
    Private Declare PtrSafe Sub GdiplusShutdown Lib "GDIPlus" (ByVal token As LongPtr)
    Private Declare PtrSafe Function OleCreatePictureIndirect Lib "oleaut32" (PicDesc As PICTDESC, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
#Else
    'Declare a UDT to store the bitmap information
    Private Type PICTDESC
        Size As Long
        Type As Long
        hPic As Long
        hPal As Long
    End Type

    'Declare a UDT to store the GDI+ Startup information
    Private Type GdiplusStartupInput
        GdiplusVersion As Long
        DebugEventCallback As Long
        SuppressBackgroundThread As Long
        SuppressExternalCodecs As Long
    End Type

    'Windows API calls into the GDI+ library
    Private Declare Function GdiplusStartup Lib "GDIPlus" (token As Long, inputbuf As GdiplusStartupInput, Optional ByVal outputbuf As Long = 0) As Long
    Private Declare Function GdipCreateBitmapFromFile Lib "GDIPlus" (ByVal filename As Long, bitmap As Long) As Long
    Private Declare Function GdipCreateHBITMAPFromBitmap Lib "GDIPlus" (ByVal bitmap As Long, hbmReturn As Long, ByVal background As Long) As Long
    Private Declare Function GdipDisposeImage Lib "GDIPlus" (ByVal image As Long) As Long
    Private Declare Sub GdiplusShutdown Lib "GDIPlus" (ByVal token As Long)
    Private Declare Function OleCreatePictureIndirect Lib "oleaut32" (PicDesc As PICTDESC, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
#End If
</code></pre>

<p>And here the methods:</p>

<pre><code>'@brief     Showing the image with the given path
Private Sub showImage(ByVal path As String)
    LoadPictureGDI Frame1, path
End Sub

' Procedure:    LoadPictureGDI
' Purpose:      Loads an image using GDI+
' Returns:      The image as an IPicture Object
Public Sub LoadPictureGDI(ByVal c As Object, ByVal sFilename As String)

    Dim uGdiInput As GdiplusStartupInput
    Dim lResult As Long
#If VBA7 Then
    Dim hGdiPlus As LongPtr
    Dim hGdiImage As LongPtr
    Dim hBitmap As LongPtr
#Else
    Dim hGdiPlus As Long
    Dim hGdiImage As Long
    Dim hBitmap As Long
#End If

    'Initialize GDI+
    uGdiInput.GdiplusVersion = 1
    lResult = GdiplusStartup(hGdiPlus, uGdiInput)

    If lResult = 0 Then

        'Load the image
        lResult = GdipCreateBitmapFromFile(StrPtr(sFilename), hGdiImage)

        If lResult = 0 Then

            'Create a bitmap handle from the GDI image
            lResult = GdipCreateHBITMAPFromBitmap(hGdiImage, hBitmap, 0)

            'Create the IPicture object from the bitmap handle
            'and show it in the frame.
            Set c.Picture = CreateIPicture(hBitmap)

            'Tidy up
            GdipDisposeImage hGdiImage
        End If

        'Shutdown GDI+
        GdiplusShutdown hGdiPlus
    End If
End Sub


' Procedure:    CreateIPicture
' Purpose:      Converts a image handle into an IPicture object.
' Returns:      The IPicture object
#If VBA7 Then
Private Function CreateIPicture(ByVal hPic As LongPtr) As IPicture
#Else
Private Function CreateIPicture(ByVal hPic As Long) As IPicture
#End If
    Dim lResult As Long
    Dim uPicInfo As PICTDESC
    Dim IID_IDispatch As GUID
    Dim IPic As IPicture

    'OLE Picture types
    Const PICTYPE_BITMAP = 1

    ' Create the Interface GUID (for the IPicture interface)
    With IID_IDispatch
        .Data1 = &amp;H7BF80980
        .Data2 = &amp;HBF32
        .Data3 = &amp;H101A
        .Data4(0) = &amp;H8B
        .Data4(1) = &amp;HBB
        .Data4(2) = &amp;H0
        .Data4(3) = &amp;HAA
        .Data4(4) = &amp;H0
        .Data4(5) = &amp;H30
        .Data4(6) = &amp;HC
        .Data4(7) = &amp;HAB
    End With

    ' Fill uPicInfo with necessary parts.
    With uPicInfo
        .Size = Len(uPicInfo)
        .Type = PICTYPE_BITMAP
        .hPic = hPic
        .hPal = 0
    End With

    ' Create the Picture object.
    lResult = OleCreatePictureIndirect(uPicInfo, IID_IDispatch, True, IPic)

    ' Return the new Picture object.
    Set CreateIPicture = IPic

End Function
</code></pre>