Jump to content

Better thumbnail creation


TimRude
 Share

Recommended Posts

I'm creating thumbnails of image files for populating an ImageList control to be used with a ListView. The image files may be of various types (i.e. jpg, png, gif, bmp, etc.) and of varying sizes.

My ImageList thumbnail size is 160W x 100H.

Presently, I'm creating the GUI, ListView, and thumbnails using this:

#include <GUIConstantsEx.au3>
#include <GuiImageList.au3>
#include <GuiListView.au3>
#include <WindowsConstants.au3>
#include <Array.au3>
#include <File.au3>
#include <GDIPlus.au3>

; Create GUI with a ListView control
Global $hGUI = GUICreate("ListView", 700, 420, -1, -1, $WS_OVERLAPPEDWINDOW)
Global $hListView = _GUICtrlListView_Create($hGUI, "", 70, 0, 630, 420, BitOR($LVS_ICON, $LVS_SINGLESEL, $LVS_SHOWSELALWAYS, $LVS_SORTASCENDING))
GUISetState(@SW_SHOW, $hGUI)

; Create and load ImageList control with thumbnails
_GDIPlus_Startup()
Global $sPath = StringReplace(@AutoItExe, "autoit3.exe", "Examples\GUI")
Local $aFile = _FileListToArray($sPath, Default, $FLTA_FILES)
If @error = 0 Then
    ; found some files
    Global $SizeX = 160 ; width of each thumbnail
    Global $SizeY = 100 ; height of each thumbnail
    Global $hImgLst = _GUIImageList_Create($SizeX, $SizeY) ; create an imagelist control
    Global $aImgFiles[1] ; create an array to hold the names of images we load
    For $i = 1 To $aFile[0]
        Local $hImage = _GDIPlus_ImageLoadFromFile($sPath & "\" & $aFile[$i]) ; try to load the image from the file
        If $hImage <> 0 Then
            Local $hImageScaled = _GDIPlus_ImageResize($hImage, $SizeX, $SizeY) ; resize the image to fit the thumbnail (doesn't maintain aspect ratio)
            Local $Bmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImageScaled) ; create a handle to the bitmap object
            Local $iIndex = _GUIImageList_Add($hImgLst, $Bmp) ; add the bitmap to the ImageList and note the index number
            _GDIPlus_ImageDispose($hImage) ; clean up
            _GDIPlus_ImageDispose($hImageScaled) ; clean up
            _WinAPI_DeleteObject($Bmp) ; clean up
            If $iIndex <> -1 Then
                ReDim $aImgFiles[$iIndex + 1] ; make room in the array for the file name of the image just loaded
                $aImgFiles[$iIndex] = $aFile[$i] ; store the file name of the image just loaded
            EndIf
        EndIf
    Next
    _GUICtrlListView_SetImageList($hListView, $hImgLst, 0) ; assign the ImageList to the ListView
    ; Add the thumbnails as items to the ListView, and use the file names as captions
    For $i = 0 To UBound($aImgFiles) - 1
        _GUICtrlListView_AddItem($hListView, $aImgFiles[$i], $i)
    Next
EndIf

Do
    Local $Msg = GUIGetMsg()
Until $Msg = $GUI_EVENT_CLOSE

_GDIPlus_Shutdown()
GUIDelete()
Exit

This does size the images to fit my thumbnail size, but it doesn't maintain the aspect ratio of the images. I end up with something that looks like this screenshot.

Thumbnails.png

What I want to create is a function to take the $hImage I've read from the file and rather than just running it through _GDIPlus_ImageResize, I want to size the image to the largest size that will fit in my thumbnail box (160x100) while maintaining the aspect ratio of the image. This will ordinarily result in a resized image that has one dimension the full size of the thumbnail's width or height, but with the other dimension smaller (such as 160 x 75, or 120 x 100). When that happens, I want to center the resized image in the thumbnail area on a black background. Like these two samples I've mocked up of a couple of the thumbnails.

Desired.png

Now before anyone points it out, I've already tried the _GDIPlus_ImageGetThumbnail function but that often ends up truncating part of the image in the thumbnail, so it's a no-go.

I can easily figure out what size to resize the image to while maintaining its aspect ratio, using a bit of code like this:

Local $iWidth = 160
Local $iHeight = 100
Local $aImgDim = _GDIPlus_ImageGetDimension($hImage)

Local $fRatioImage = $aImgDim[0] / $aImgDim[1]
Local $fRatioThumb = $iWidth / $iHeight

Switch True
    Case $fRatioThumb > $fRatioImage
        $iWidth = $iHeight * $fRatioImage
    Case $fRatioThumb < $fRatioImage
        $iHeight = $iWidth / $fRatioImage
EndSwitch

Local $hResizedImage = _GDIPlus_ImageResize($hImage, $iWidth, $iHeight)

I just don't understand enough about the GDIPlus functions to take that resized image and center it it over a black background of 160x100 and then combine them into a new 160x100 image that I can then convert to a bitmap object and throw into my ImageList.

Help?

Thumbnails.png

Desired.png

Edited by TimRude
Move the pictures inline with the text for clarity
Link to comment
Share on other sites

#include <GUIConstantsEx.au3>
#include <GuiImageList.au3>
#include <GuiListView.au3>
#include <WindowsConstants.au3>
#include <Array.au3>
#include <File.au3>
#include <GDIPlus.au3>

; Create GUI with a ListView control
Global $hGUI = GUICreate("ListView", 700, 420, -1, -1, $WS_OVERLAPPEDWINDOW)
Global $hListView = _GUICtrlListView_Create($hGUI, "", 70, 0, 630, 420, BitOR($LVS_ICON, $LVS_SINGLESEL, $LVS_SHOWSELALWAYS, $LVS_SORTASCENDING))
GUISetState(@SW_SHOW, $hGUI)

; Create and load ImageList control with thumbnails
_GDIPlus_Startup()
Global $sPath = StringReplace(@AutoItExe, "autoit3.exe", "Examples\GUI")
Local $aFile = _FileListToArray($sPath, Default, $FLTA_FILES)
If @error = 0 Then
    ; found some files
    Global $SizeX = 160 ; width of each thumbnail
    Global $SizeY = 100 ; height of each thumbnail
    Global $hImgLst = _GUIImageList_Create($SizeX, $SizeY) ; create an imagelist control
    Global $aImgFiles[1] ; create an array to hold the names of images we load

    Global $_dHash_hBitmap_SmallScale = _GDIPlus_BitmapCreateFromScan0($SizeX, $SizeY)
    Global $_dHash_hBitmap_SmallScale_Graphics = _GDIPlus_ImageGetGraphicsContext($_dHash_hBitmap_SmallScale)
    _GDIPlus_GraphicsSetInterpolationMode($_dHash_hBitmap_SmallScale_Graphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
    Global $hBrush = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)

    For $i = 1 To $aFile[0]

        $sFile = $sPath & "\" & $aFile[$i]
        Switch StringRight($sFile, 4)
            Case ".png", ".jpg", ".gif"
            Case Else
                ContinueLoop
        EndSwitch

        Local $hImage = _GDIPlus_ImageLoadFromFile($sFile) ; try to load the image from the file
        ; ShellExecute($sPath & "\" & $aFile[$i])
        If $hImage <> 0 Then

            $iImage_iWidth = _GDIPlus_ImageGetWidth($hImage)
            $iImage_iHeight = _GDIPlus_ImageGetHeight($hImage)

            If $iImage_iWidth > Round($iImage_iHeight * ($SizeX / $SizeY)) Then
                ConsoleWrite("+ ")
                $iImage_Resized_Width = $SizeX
                $iImage_Offset_X = 0
                $iImage_Resized_Height = Round($iImage_iHeight / ($SizeX / $SizeY))
                $iImage_Offset_Y = Round(($SizeY - $iImage_Resized_Height) / 2)
            Else
                ConsoleWrite("- ")
                $iImage_Resized_Width = Round($SizeX / ($SizeX / $SizeY))
                $iImage_Offset_X = Round(($SizeX - $iImage_Resized_Width) / 2)
                $iImage_Resized_Height = $SizeY
                $iImage_Offset_Y = 0
            EndIf

            ConsoleWrite($aFile[$i] & @TAB & @TAB & $iImage_Resized_Width & "x" & $iImage_Resized_Height & @TAB & $iImage_Offset_X & "x" & $iImage_Offset_Y & @CRLF)

            Local $hImageScaled = _GDIPlus_ImageResize($hImage, $iImage_Resized_Width, $iImage_Resized_Height) ; resize the image to fit the thumbnail (doesn't maintain aspect ratio)

            _GDIPlus_ImageDispose($hImage) ; clean up

            _GDIPlus_GraphicsFillRect($_dHash_hBitmap_SmallScale_Graphics, 0, 0, $SizeX, $SizeY, $hBrush) ; reset background to black
            _GDIPlus_GraphicsDrawImageRect($_dHash_hBitmap_SmallScale_Graphics, $hImageScaled, $iImage_Offset_X, $iImage_Offset_Y, $iImage_Resized_Width, $iImage_Resized_Height) ; draw loaded file to existing buffer
            _GDIPlus_ImageDispose($hImageScaled)

            Local $Bmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($_dHash_hBitmap_SmallScale) ; create a handle to the bitmap object
            Local $iIndex = _GUIImageList_Add($hImgLst, $Bmp) ; add the bitmap to the ImageList and note the index number

            _WinAPI_DeleteObject($Bmp) ; clean up
            If $iIndex <> -1 Then
                ReDim $aImgFiles[$iIndex + 1] ; make room in the array for the file name of the image just loaded
                $aImgFiles[$iIndex] = $aFile[$i] ; store the file name of the image just loaded
            EndIf
        EndIf
    Next

    _GDIPlus_GraphicsDispose($_dHash_hBitmap_SmallScale_Graphics)
    _GDIPlus_BitmapDispose($_dHash_hBitmap_SmallScale)
    _GDIPlus_BrushDispose($hBrush)

    _GUICtrlListView_SetImageList($hListView, $hImgLst, 0) ; assign the ImageList to the ListView
    ; Add the thumbnails as items to the ListView, and use the file names as captions
    For $i = 0 To UBound($aImgFiles) - 1
        _GUICtrlListView_AddItem($hListView, $aImgFiles[$i], $i)
    Next
EndIf

Do
    Local $Msg = GUIGetMsg()
Until $Msg = $GUI_EVENT_CLOSE

_GDIPlus_Shutdown()
GUIDelete()
Exit

 

Link to comment
Share on other sites

@pixelsearch Yes. I guess it's because the Torus.png file has a transparent background.

If I want the Torus.png to look like my mock-up sample, I have to edit the .png with something like IrfanView and save it with a white background and no transparency.

With transparency off and with the .png saved with a white background, it looks like this:

image.png.51ce31b1a067089c94ce5a1b237542a8.png

I would prefer to have all .png's (or .gifs) that are loaded in the thumbnails to automatically be treated as if they had a white background wherever there's transparency in the image, with the black background showing up only wherever the resized image is smaller than the thumbnail. Do you know how to make that happen without having to edit the files ahead of time?

This app's final purpose is to be an image viewer that displays a selected image full-screen on a large secondary monitor (TV). On the TV, the image will be displayed as large as possible while maintaining the aspect ratio, and any part of the TV display not covered by the image will be black. (Kind of like 'letterboxing' on old movies played on an HD TV.) So I want the thumbnails to be a preview of what it will look like on the TV.

Edited by TimRude
Link to comment
Share on other sites

It works... with a funny script. The background is light grey because it's the color I choosed on my computer. On yours it should be white.

#include <File.au3>
#include <GUIConstantsEx.au3>
#include <GuiImageList.au3>
#include <GuiListView.au3>
#include <GDIPlus.au3>
#include <WindowsConstants.au3>

; Create GUI with a ListView control
Global $hGUI = GUICreate("ListView", 700, 420, -1, -1, $WS_OVERLAPPEDWINDOW)
Global $hListView = _GUICtrlListView_Create($hGUI, "", 70, 0, 630, 420, _
    BitOR($LVS_ICON, $LVS_SINGLESEL, $LVS_SHOWSELALWAYS, $LVS_SORTASCENDING))
GUISetState(@SW_SHOW, $hGUI)

; Create and load ImageList control with thumbnails
_GDIPlus_Startup()
Global $sPath = StringReplace(@AutoItExe, "autoit3.exe", "Examples\GUI")
Local $aFile = _FileListToArray($sPath, Default, $FLTA_FILES)

If @error = 0 Then
    ; found some files
    Global $SizeX = 160 ; width of each thumbnail
    Global $SizeY = 100 ; height of each thumbnail
    Global $fRatioThumb = $SizeX / $SizeY
    Global $hImgLst = _GUIImageList_Create($SizeX, $SizeY) ; create an imagelist control
    Global $aImgFiles[1] ; create an array to hold the names of images we load

    ; create GUI2 visible... under the screen down left corner
    Local $hGUI2 = GUICreate("", $SizeX, $SizeY, 0, @DesktopHeight, $WS_POPUP)
    GUISetState(@SW_SHOW, $hGUI2) ; mandatory line or nothing will work

    For $i = 1 To $aFile[0]
        $sFile = $sPath & "\" & $aFile[$i]
        Switch StringRight($sFile, 4)
            Case ".png", ".jpg", ".gif"
            Case Else
                ContinueLoop
        EndSwitch

        Local $hImage = _GDIPlus_ImageLoadFromFile($sFile) ; try to load the image from the file
        If $hImage <> 0 Then
            $iImage_iWidth = _GDIPlus_ImageGetWidth($hImage)
            $iImage_iHeight = _GDIPlus_ImageGetHeight($hImage)
            $fRatioImage = $iImage_iWidth / $iImage_iHeight

            Switch True
                Case $fRatioThumb < $fRatioImage
                    ConsoleWrite("+ ")
                    $iImage_Resized_Width = $SizeX
                    $iImage_Offset_X = 0
                    $iImage_Resized_Height = Round($iImage_Resized_Width / $fRatioImage)
                    $iImage_Offset_Y = Floor(($SizeY - $iImage_Resized_Height) / 2)
                Case $fRatioThumb > $fRatioImage
                    ConsoleWrite("- ")
                    $iImage_Resized_Height = $SizeY
                    $iImage_Offset_Y = 0
                    $iImage_Resized_Width = Round($iImage_Resized_Height * $fRatioImage)
                    $iImage_Offset_X = Floor(($SizeX - $iImage_Resized_Width) / 2)
                Case Else
                    ConsoleWrite("= ")
                    $iImage_Resized_Width = $SizeX
                    $iImage_Offset_X = 0
                    $iImage_Resized_Height = $SizeY
                    $iImage_Offset_Y = 0
            EndSwitch

            ConsoleWrite($aFile[$i] & @TAB & @TAB & $iImage_Resized_Width & "x" & $iImage_Resized_Height & @TAB & $iImage_Offset_X & "x" & $iImage_Offset_Y & @CRLF)

            Local $hImageScaled = _GDIPlus_ImageResize($hImage, $iImage_Resized_Width, $iImage_Resized_Height) ; resize the image (maintain aspect ratio)
            _GDIPlus_ImageDispose($hImage)

            Local $idPic = GUICtrlCreatePic("", $iImage_Offset_X, $iImage_Offset_Y, $iImage_Resized_Width, $iImage_Resized_Height)
            Local $hBmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImageScaled)
            _WinAPI_DeleteObject(GUICtrlSendMsg($idPic, 0x0172, 0, $hBmp)) ; STM_SETIMAGE = 0x0172, $IMAGE_BITMAP = 0
            _WinAPI_DeleteObject($hBmp)
            _GDIPlus_ImageDispose($hImageScaled)

            Local $idLabel, $idLabel2
            If $iImage_Resized_Width <> $SizeX Then
                $idLabel = GuiCtrlCreateLabel("", 0, 0, $iImage_Offset_X, $SizeY)
                GUICtrlSetBkColor(-1, 0x000000) ; black label (left side of pic)
                $idLabel2 = GuiCtrlCreateLabel("", $iImage_Offset_X + $iImage_Resized_Width, 0, $iImage_Offset_X, $SizeY)
                GUICtrlSetBkColor(-1, 0x000000) ; black label (right side of pic)
            ElseIf $iImage_Resized_Height <> $SizeY Then
                $idLabel = GuiCtrlCreateLabel("", 0, 0, $SizeX, $iImage_Offset_Y)
                GUICtrlSetBkColor(-1, 0x000000) ; black label (above pic)
                $idLabel2 = GuiCtrlCreateLabel("", 0, $iImage_Offset_Y + $iImage_Resized_Height, $SizeX, $iImage_Offset_Y)
                GUICtrlSetBkColor(-1, 0x000000) ; black label (under pic)
            EndIf

            Local $hBmp2 = _WinCapture($hGUI2, $SizeX, $SizeY)  ; capture the client area of hGUI2 (off the screen)
            Local $iIndex = _GUIImageList_Add($hImgLst, $hBmp2) ; add the bitmap to the ImageList and note the index number
            _WinAPI_DeleteObject($hBmp2)

            GUICtrlDelete($idPic)
            GUICtrlDelete($idLabel)
            GUICtrlDelete($idLabel2)

            If $iIndex <> -1 Then
                ReDim $aImgFiles[$iIndex + 1] ; make room in the array for the file name of the image just loaded
                $aImgFiles[$iIndex] = $aFile[$i] ; store the file name of the image just loaded
            EndIf
        EndIf
    Next

    _GUICtrlListView_SetImageList($hListView, $hImgLst, 0) ; assign the ImageList to the ListView
    ; Add the thumbnails as items to the ListView, and use the file names as captions
    For $i = 0 To UBound($aImgFiles) - 1
        _GUICtrlListView_AddItem($hListView, $aImgFiles[$i], $i)
    Next

    GUIDelete($hGUI2)
EndIf

Do
Until GUIGetMsg() = $GUI_EVENT_CLOSE

_GDIPlus_Shutdown()
GUIDelete($hGUI)

;========================================
Func _WinCapture($hWnd, $iWidth, $iHeight)
    Local $hSrcDC, $hDestDC, $hBmp

    $hSrcDC = _WinAPI_GetDC($hWnd)
    $hBmp = _WinAPI_CreateCompatibleBitmap($hSrcDC, $iWidth, $iHeight)

    $hDestDC = _WinAPI_CreateCompatibleDC($hSrcDC)
    _WinAPI_SelectObject($hDestDC, $hBmp)

    _WinAPI_PrintWindow($hWnd, $hDestDC, True) ; True = only the client area
    _WinAPI_BitBlt($hDestDC, 0, 0, $iWidth, $iHeight, $hSrcDC, 0, 0, $SRCCOPY)

    _WinAPI_DeleteDC($hDestDC)
    _WinAPI_ReleaseDC($hWnd, $hSrcDC)

    Return $hBmp
EndFunc   ;==>_WinCapture

 

1357003615_gui2offthescreen2.jpg.b1a6a5a8e76d6d7a09a97fe74a50879c.jpg

Updates :

* Nov  9, 2022 : Create GUI2 off the screen in one pass.

* Nov 10, 2022 : Add a 3rd parameter True to _WinAPI_PrintWindow() to capture only the client area (though GUI2 got only a $WS_POPUP style, then it makes no difference in this case)

* Nov 11, 2022 : Use TimRude's calculation (from his last script) to keep thumbnail ratio always = original image ratio. The vertical black bars will then have a different width, depending on the original images.

Edited by pixelsearch
updates (listed above)
Link to comment
Share on other sites

Here's my take, adjust the brushes white, green and red as desired (I know green and red should be black 🙂, but with green and red the example is better and you see what's going on).

#include <GUIConstantsEx.au3>
#include <GuiImageList.au3>
#include <GuiListView.au3>
#include <WindowsConstants.au3>
#include <Array.au3>
#include <File.au3>
#include <GDIPlus.au3>

; Create GUI with a ListView control
Global $hGUI = GUICreate("ListView", 700, 420, -1, -1, $WS_OVERLAPPEDWINDOW)
Global $hListView = _GUICtrlListView_Create($hGUI, "", 70, 0, 630, 420, BitOR($LVS_ICON, $LVS_SINGLESEL, $LVS_SHOWSELALWAYS, $LVS_SORTASCENDING))
GUISetState(@SW_SHOW, $hGUI)

; Create and load ImageList control with thumbnails
_GDIPlus_Startup()
Global $sPath = StringReplace(@AutoItExe, "autoit3.exe", "Examples\GUI")
Local $aFile = _FileListToArray($sPath, Default, $FLTA_FILES)
If @error = 0 Then
    ; found some files
    Global $SizeX = 160 ; width of each thumbnail
    Global $SizeY = 100 ; height of each thumbnail
    Global $hImgLst = _GUIImageList_Create($SizeX, $SizeY) ; create an imagelist control
    Global $aImgFiles[1] ; create an array to hold the names of images we load

    Global $_dHash_hBitmap_SmallScale = _GDIPlus_BitmapCreateFromScan0($SizeX, $SizeY)
    Global $_dHash_hBitmap_SmallScale_Graphics = _GDIPlus_ImageGetGraphicsContext($_dHash_hBitmap_SmallScale)

    Global $hBrush_White = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)
    Global $hBrush_Green = _GDIPlus_BrushCreateSolid(0xFF00FF00)
    Global $hBrush_Red = _GDIPlus_BrushCreateSolid(0xFFFF0000)

    For $i = 1 To $aFile[0]

        $sFile = $sPath & "\" & $aFile[$i]
        Switch StringRight($sFile, 4)
            Case ".png", ".jpg", ".gif"
            Case Else
                ContinueLoop
        EndSwitch

        Local $hImage = _GDIPlus_ImageLoadFromFile($sFile) ; try to load the image from the file
        ; ShellExecute($sPath & "\" & $aFile[$i])
        If $hImage <> 0 Then

            $iImage_iWidth = _GDIPlus_ImageGetWidth($hImage)
            $iImage_iHeight = _GDIPlus_ImageGetHeight($hImage)

            If $iImage_iWidth > Round($iImage_iHeight * ($SizeX / $SizeY)) Then
                ConsoleWrite("+ ")
                $iImage_Resized_Width = $SizeX
                $iImage_Offset_X = 0
                $iImage_Resized_Height = Round($iImage_iHeight / ($SizeX / $SizeY))
                $iImage_Offset_Y = Round(($SizeY - $iImage_Resized_Height) / 2)
            Else
                ConsoleWrite("- ")
                $iImage_Resized_Width = Round($SizeX / ($SizeX / $SizeY))
                $iImage_Offset_X = Round(($SizeX - $iImage_Resized_Width) / 2)
                $iImage_Resized_Height = $SizeY
                $iImage_Offset_Y = 0
            EndIf

            ConsoleWrite($aFile[$i] & @TAB & @TAB & $iImage_Resized_Width & "x" & $iImage_Resized_Height & @TAB & $iImage_Offset_X & "x" & $iImage_Offset_Y & @CRLF)

            $hGraphics = _GDIPlus_ImageGetGraphicsContext($hImage)
            _GDIPlus_GraphicsSetInterpolationMode($hGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)

            Local $hImageScaled = _GDIPlus_ImageResize($hImage, $iImage_Resized_Width, $iImage_Resized_Height)

            _GDIPlus_GraphicsDispose($hGraphics)
            _GDIPlus_ImageDispose($hImage) ; clean up

            _GDIPlus_GraphicsFillRect($_dHash_hBitmap_SmallScale_Graphics, 0, 0, $SizeX, $SizeY, $hBrush_White)

            If $iImage_Offset_X Then
                ConsoleWrite("a" & @CRLF)
                _GDIPlus_GraphicsFillRect($_dHash_hBitmap_SmallScale_Graphics, 0, 0, $iImage_Offset_X, $SizeY, $hBrush_Green)
                ConsoleWrite("0x0 - " & $iImage_Offset_X & "x" & $SizeY & @CRLF)
                _GDIPlus_GraphicsFillRect($_dHash_hBitmap_SmallScale_Graphics, $iImage_Resized_Height + $iImage_Offset_X, 0, $iImage_Offset_X, $SizeY, $hBrush_Red)
                ConsoleWrite($iImage_Resized_Height + $iImage_Offset_X & "x0 - " & $iImage_Offset_X & "x" & $SizeY & @CRLF)
            Else
                ConsoleWrite("b" & @CRLF)
                _GDIPlus_GraphicsFillRect($_dHash_hBitmap_SmallScale_Graphics, 0, 0, $SizeX, $iImage_Offset_Y, $hBrush_Green)
                ConsoleWrite("0x0 - " & $SizeX & "x" & $iImage_Offset_Y & @CRLF)
                _GDIPlus_GraphicsFillRect($_dHash_hBitmap_SmallScale_Graphics, 0, $iImage_Resized_Height + $iImage_Offset_Y, $SizeX, $iImage_Offset_Y, $hBrush_Red)
                ConsoleWrite("0x" & $iImage_Resized_Height + $iImage_Offset_Y & " - " & $SizeX & "x" & $iImage_Offset_Y & @CRLF)
            EndIf

            _GDIPlus_GraphicsDrawImageRect($_dHash_hBitmap_SmallScale_Graphics, $hImageScaled, $iImage_Offset_X, $iImage_Offset_Y, $iImage_Resized_Width, $iImage_Resized_Height) ; draw loaded file to existing buffer

            _GDIPlus_ImageDispose($hImageScaled)

            Local $Bmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($_dHash_hBitmap_SmallScale) ; create a handle to the bitmap object
            Local $iIndex = _GUIImageList_Add($hImgLst, $Bmp) ; add the bitmap to the ImageList and note the index number

            _WinAPI_DeleteObject($Bmp) ; clean up
            If $iIndex <> -1 Then
                ReDim $aImgFiles[$iIndex + 1] ; make room in the array for the file name of the image just loaded
                $aImgFiles[$iIndex] = $aFile[$i] ; store the file name of the image just loaded
            EndIf
        EndIf
    Next

    _GDIPlus_GraphicsDispose($_dHash_hBitmap_SmallScale_Graphics)
    _GDIPlus_BitmapDispose($_dHash_hBitmap_SmallScale)
    _GDIPlus_BrushDispose($hBrush_White)
    _GDIPlus_BrushDispose($hBrush_Green)
    _GDIPlus_BrushDispose($hBrush_Red)

    _GUICtrlListView_SetImageList($hListView, $hImgLst, 0) ; assign the ImageList to the ListView
    ; Add the thumbnails as items to the ListView, and use the file names as captions
    For $i = 0 To UBound($aImgFiles) - 1
        _GUICtrlListView_AddItem($hListView, $aImgFiles[$i], $i)
    Next
EndIf

Do
    Local $Msg = GUIGetMsg()
Until $Msg = $GUI_EVENT_CLOSE

_GDIPlus_Shutdown()
GUIDelete()
Exit

 

Edited by KaFu
Link to comment
Share on other sites

After examining both approaches, I ended up going with a modified version of KaFu's method.

@pixelsearch Your method was quite interesting, but in the end it seemed that since GDIPlus was needed for some of the process, it would be better to use it all the way through. But thanks for posting your sample! There are some useful tidbits in there for another project some day.

@KaFu I worked my way through your GDIPlus calls and read the docs on them and definitely learned something in the process. So thanks for that!

I extracted the code for building the thumbnail bitmap and put it into a function. The function lets you specify the filename to read, the size of the thumbnail, and the colors desired for the image background as well as the overflow area background.

Here's what I ended up with:

#include <GUIConstantsEx.au3>
#include <GuiImageList.au3>
#include <GuiListView.au3>
#include <WindowsConstants.au3>
#include <Array.au3>
#include <File.au3>
#include <GDIPlus.au3>

; Create GUI with a ListView control
Global $hGUI = GUICreate("ListView", 700, 420, -1, -1, $WS_OVERLAPPEDWINDOW)
Global $hListView = _GUICtrlListView_Create($hGUI, "", 70, 0, 630, 420, BitOR($LVS_ICON, $LVS_SINGLESEL, $LVS_SHOWSELALWAYS, $LVS_SORTASCENDING))
GUISetState(@SW_SHOW, $hGUI)

; Create and load ImageList control with thumbnails
_GDIPlus_Startup()
Global $sPath = StringReplace(@AutoItExe, "autoit3.exe", "Examples\GUI")
;Global $sPath = StringReplace(@AutoItExe, "autoit3.exe", "Examples\Helpfile\Extras")
Local $aFile = _FileListToArray($sPath, Default, $FLTA_FILES)
If @error = 0 Then
    ; found some files
    Global $SizeX = 160 ; width of each thumbnail
    Global $SizeY = 100 ; height of each thumbnail
    Global $hImgLst = _GUIImageList_Create($SizeX, $SizeY) ; create an imagelist control
    Global $aImgFiles[1] ; create an array to hold the names of images we load

    For $i = 1 To $aFile[0]
        $sFile = $sPath & "\" & $aFile[$i]
        Local $hBmp = _MakeThumbnailBitmapFromFile($sFile, $SizeX, $SizeY)
        If $hBmp <> 0 Then
            Local $iIndex = _GUIImageList_Add($hImgLst, $hBmp) ; add the bitmap to the ImageList and note the index number
            _WinAPI_DeleteObject($hBmp) ; clean up
            If $iIndex <> -1 Then
                ReDim $aImgFiles[$iIndex + 1] ; make room in the array for the file name of the image just loaded
                $aImgFiles[$iIndex] = $aFile[$i] ; store the file name of the image just loaded
            EndIf
        EndIf
    Next

    _GUICtrlListView_SetImageList($hListView, $hImgLst, 0) ; assign the ImageList to the ListView
    ; Add the thumbnails as items to the ListView, and use the file names as captions
    For $i = 0 To UBound($aImgFiles) - 1
        _GUICtrlListView_AddItem($hListView, $aImgFiles[$i], $i)
    Next
EndIf

Do
    Local $Msg = GUIGetMsg()
Until $Msg = $GUI_EVENT_CLOSE

_GDIPlus_Shutdown()
GUIDelete()
Exit

Func _MakeThumbnailBitmapFromFile($sFile, $iThumbWidth = 160, $iThumbHeight = 100, $iARGB_Transparent = 0xFFFFFFFF, $iARGB_Background = 0xFF000000)

    ; Parameters:
    ;    $sFile - name of image file (including path) to load
    ;    $iThumbWidth - width of thumbnail (optional, default = 160)
    ;    $iThumbHeight - height of thumbnail (optional, default = 100)
    ;    $iARGB_Transparent - color for transparent areas of image files (PNG, GIF, ICO) (optional, default = 0xFFFFFFFF = white)
    ;    $iARGB_Background - color of background areas not covered by image (optional, default = 0xFF000000 = black)

    ; If successful, returns handle to a bitmap that can be assigned to the thumbnail image
    ; If not successful, returns 0

    ; Sample Use:
    ;
    ;  #include <GDIPlus.au3>
    ;  _GDIPlus_Startup() ; needed before any _GDIPlus functions are used
    ;  ...
    ;  $hBmp = _MakeThumbnailBitmapFromFile("path/filename") ; using default size and colors
    ;  If $hBmp <> 0 Then
    ;      $iIndex = _GUIImageList_Add($hImageList, $hBmp) ; assign bitmap to imagelist
    ;      _WinAPI_DeleteObject($hBmp) ; <-- be sure to remember to do this cleanup!
    ;  EndIf
    ;  ...
    ;  _GDIPlus_Shutdown() ; at end of script when finished with all _GDIPlus functions

    Local $hImage, $hGraphics, $hImageScaled, $hBmp
    Local $_dHash_hBitmap_SmallScale, $_dHash_hBitmap_SmallScale_Graphics
    Local $hBrush_Back, $hBrush_Trans
    Local $fRatioImage, $fRatioThumb
    Local $iImage_Width, $iImage_Height, $iImage_Resized_Height, $iImage_Resized_Width
    Local $iImage_Offset_X, $iImage_Offset_Y

    ; Try to load the image from the file
    $hImage = _GDIPlus_ImageLoadFromFile($sFile)

    If $hImage <> 0 Then    ; Successfully read the image file, process it

        ; Resize the image to fit within the thumbnail while maintaining its aspect ratio
        $iImage_Width = _GDIPlus_ImageGetWidth($hImage)
        $iImage_Height = _GDIPlus_ImageGetHeight($hImage)
        $fRatioImage = $iImage_Width / $iImage_Height
        $fRatioThumb = $iThumbWidth / $iThumbHeight
        Switch True
            Case $fRatioThumb > $fRatioImage
                $iImage_Resized_Height = $iThumbHeight
                $iImage_Resized_Width = Round($iImage_Resized_Height * $fRatioImage)
                $iImage_Offset_X = Floor(($iThumbWidth - $iImage_Resized_Width) / 2)
                $iImage_Offset_Y = 0
            Case $fRatioThumb < $fRatioImage
                $iImage_Resized_Width = $iThumbWidth
                $iImage_Resized_Height = Round($iImage_Resized_Width / $fRatioImage)
                $iImage_Offset_X = 0
                $iImage_Offset_Y = Floor(($iThumbHeight - $iImage_Resized_Height) / 2)
            Case Else
                $iImage_Resized_Width = $iThumbWidth
                $iImage_Resized_Height = $iThumbHeight
                $iImage_Offset_X = 0
                $iImage_Offset_Y = 0
        EndSwitch

        Local $sDrive, $sDir, $sFileName, $sExtension
        _PathSplit($sFile, $sDrive, $sDir, $sFileName, $sExtension)
        ConsoleWrite($sFileName & $sExtension & @TAB & _
            "Orig " & $iImage_Width & " x " & $iImage_Height & @TAB & _
            "New " & $iImage_Resized_Width & " x " & $iImage_Resized_Height & @TAB & _
            "Offsets = " & $iImage_Offset_X & " X , " & $iImage_Offset_Y & " Y" & @CRLF)

        $hGraphics = _GDIPlus_ImageGetGraphicsContext($hImage)
        _GDIPlus_GraphicsSetInterpolationMode($hGraphics, $GDIP_INTERPOLATIONMODE_HIGHQUALITYBICUBIC)
        $hImageScaled = _GDIPlus_ImageResize($hImage, $iImage_Resized_Width, $iImage_Resized_Height)

        _GDIPlus_GraphicsDispose($hGraphics) ; clean up
        _GDIPlus_ImageDispose($hImage) ; clean up

        ; Create a buffer to build the thumbnail image in
        $_dHash_hBitmap_SmallScale = _GDIPlus_BitmapCreateFromScan0($iThumbWidth, $iThumbHeight)
        $_dHash_hBitmap_SmallScale_Graphics = _GDIPlus_ImageGetGraphicsContext($_dHash_hBitmap_SmallScale)

        ; Add the background and transparent area colors to the appropriate areas of the buffer
        $hBrush_Back = _GDIPlus_BrushCreateSolid($iARGB_Background)
        $hBrush_Trans = _GDIPlus_BrushCreateSolid($iARGB_Transparent)
        _GDIPlus_GraphicsFillRect($_dHash_hBitmap_SmallScale_Graphics, 0, 0, $iThumbWidth, $iThumbHeight, $hBrush_Back) ; background outside of image
        _GDIPlus_GraphicsFillRect($_dHash_hBitmap_SmallScale_Graphics, $iImage_Offset_X, $iImage_Offset_Y, $iImage_Resized_Width, $iImage_Resized_Height, $hBrush_Trans) ; area where the resized image will be

        ; Add the resized image to the buffer and then create a handle to the bitmap object
        _GDIPlus_GraphicsDrawImageRect($_dHash_hBitmap_SmallScale_Graphics, $hImageScaled, $iImage_Offset_X, $iImage_Offset_Y, $iImage_Resized_Width, $iImage_Resized_Height)
        $hBmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($_dHash_hBitmap_SmallScale)

        _GDIPlus_BrushDispose($hBrush_Back) ; clean up
        _GDIPlus_BrushDispose($hBrush_Trans) ; clean up
        _GDIPlus_ImageDispose($hImageScaled) ; clean up
        _GDIPlus_BitmapDispose($_dHash_hBitmap_SmallScale) ; clean up
        _GDIPlus_GraphicsDispose($_dHash_hBitmap_SmallScale_Graphics) ; clean up

        ; Return the bitmap handle (be sure to delete it with _WinAPI_DeleteObject after using it)
        Return $hBmp
    Else
        Return 0 ; failed
    EndIf

EndFunc

I modified a few things from KaFu's sample:

For one, I eliminated checking for only certain file extensions. Instead I try to load every file in the folder. For files that aren't compatible image files, the _GDIPlus_ImageLoadFromFile function just returns a 0 and I skip that file and move on.

Another change was in determining the size of the resized image. The posted sample wasn't quite getting the aspect ratio right, especially for images that are wider than they are tall.

I also simplified the drawing of the backgrounds by first painting the entire buffer with the black background, then dropping the white background over it where the image goes, and then dropping on the image. That was easier than painting the black bars one at a time, and takes care of any rounding discrepancies better.

There's another set of sample images in the C:\Program Files (x86)\AutoIt3\Examples\Helpfile\Extras folder that you can view by uncommenting the $sPath assignment line in the code and it shows how the routine also handles .bmp, .ico, and .emf files nicely, besides the the .gif, .png, and .jpg files.

image.png.ebd81dced459e358c9db10311e21262c.png

image.png.20440122cd1489e196d20929dd654b48.png

Edited by TimRude
fixed a typo
Link to comment
Share on other sites

19 hours ago, TimRude said:

There are some useful tidbits in there

Thanks for the kind words. Glad you made it by choosing Kafu's style, which is certainly more appropriate as it's plain GDI+

I never took time to study GDI+ "graphics, pens & brushes" (until now) so I tried it in another way, which succeeded and I was the first to be surprised with this "off-the-screen popup GUI and its pic control"

Thanks to @Bert (MVP) because his simple sentence in this thread started it all. In his own words :

The only way I know to do it is have the window visible, but moved off the screen.

Immediately after, in the same thread, James & wraithdu (2 other MVP's) reworked an old script found on the Forum and that was a big help too, plus Kafu's code above. We're lucky that this forum was (and gladly still is) full of resourceful MVP's !

Anyway, I'll keep on updating my script above, for example today :
* major change : take care of images who don't need to be resized at all.

@KaFu do you think this major change should also be applied to your script ?
Please look at the attached pic, which is exactly 160x100 pixels, then see what happens when it's placed in the working folder, then the ratio will be lost as the resized pic will be 100x100

1191760131_Kafusoutput.png.c5e65061b9989caef3f3003300a6676c.png

@TimRude great job on the msoobe.jpg, where the ratio 1.33 is kept after resizing, which ends in a resized pic of 133x100 (instead of 100x100 as in our scripts), this is your output :

1196978531_TimRudesoutput.png.71aa574109496b8eb86b9ad4a6d8050f.png

Thinking of it, maybe Kafu did want the vertical bars to always have the same width in his calculation ?
But imho, if the horizontal bars are allowed to have a different height (depending on original pics) then shouldn't it be the same behavior for the vertical bars and their different widths ?

I'm attaching below the original blue pic 160x100, in case you need to test it.

test.jpg.9050d325451a65bb6a0ba5e0b53b6a44.jpg

Edit: new change in my script above (Nov 11, 2022) :
Use TimRude's calculation (from his last script) to keep thumbnail ratio always = original image ratio. The vertical black bars will then have a different width, depending on the original images.

Edited by pixelsearch
added an Edit comment
Link to comment
Share on other sites

@pixelsearch The app I'm writing is actually a re-write of an app I wrote in VB6 a number of years ago that I now wanted to tweak a little. Unfortunately, due to a crashed HDD and a lax backup routine, I lost the source code to the VB app and therefore cannot tweak it. So I decided to recreated it in AutoIt this time.

As I recall, in the VB app I used a similar approach to yours to create the thumbnails. VB has image-box and picture-box controls and using a combination of a couple of them as hidden controls on the form, I composited the thumbnail with the black bars where I wanted it. But the VB picture-box control also exposes it's internal picture object that can be used to assign it directly to the ImageList, so no screen-capture hoop jumping was needed. But the basic logic was very similar.

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...