Sign in to follow this  
Followers 0
Viet

Problem when manipulating images with different DPI using GDIPlus

3 posts in this topic

#1 ·  Posted (edited)

I made this little piece of code to crop images into circle shape using GDIPlus. When I try it with 96 DPI images, it works great.

However, when the images are in 300 DPI, the results are incorrect (as you can see in the test image files >>> here). Here is the full script:

#include <GDIplus.au3>

clipCircle("96dpi.jpg", "96dpi_out.jpg")
clipCircle("300dpi.jpg", "300dpi_out.jpg")

Func clipCircle($sImageSource, $sImageDest)
    _GDIPlus_Startup()

    $hBitmapSource = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\" & $sImageSource)
    $hGraphicsSource = _GDIPlus_ImageGetGraphicsContext($hBitmapSource)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphicsSource, $GDIP_SMOOTHINGMODE_HIGHQUALITY)

    $iX = _GDIPlus_ImageGetWidth($hBitmapSource)
    $iY = _GDIPlus_ImageGetHeight($hBitmapSource)

;~  Create an empty bitmap
;~  This does not work with 300 DPI images
    $hBitmapDest = _GDIPlus_BitmapCreateFromScan0($iX, $iY)
    $hGraphicsDest = _GDIPlus_ImageGetGraphicsContext($hBitmapDest)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphicsDest, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
    _GDIPlus_GraphicsClear($hGraphicsDest, 0xFFFFFFFF)

    $hPath = _GDIPlus_PathCreate()
    _GDIPlus_PathAddEllipse($hPath, 0, 0, $iX, $iY)

    _GDIPlus_GraphicsSetClipPath($hGraphicsDest, $hPath, 0)
    _GDIPlus_GraphicsDrawImage($hGraphicsDest, $hBitmapSource, 0, 0)
    _GDIPlus_ImageSaveToFile($hBitmapDest, @ScriptDir & "\" & $sImageDest)

    _GDIPlus_GraphicsDispose($hGraphicsSource)
    _GDIPlus_BitmapDispose($hBitmapSource)
    _GDIPlus_GraphicsDispose($hGraphicsDest)
    _GDIPlus_BitmapDispose($hBitmapDest)
    _GDIPlus_Shutdown()
EndFunc

Func clipCircle2($sImageSource, $sImageDest)
    _GDIPlus_Startup()

    $hBitmapSource = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\" & $sImageSource)

    $iX = _GDIPlus_ImageGetWidth($hBitmapSource)
    $iY = _GDIPlus_ImageGetHeight($hBitmapSource)

;~  Clone the source bitmap
;~  This works regardless of the image's DPI
    $hBitmapDest = _GDIPlus_BitmapCloneArea($hBitmapSource, 0, 0, $iX, $iY)
    $hGraphicsDest = _GDIPlus_ImageGetGraphicsContext($hBitmapDest)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphicsDest, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
    _GDIPlus_GraphicsClear($hGraphicsDest, 0xFFFFFFFF)

    $hPath = _GDIPlus_PathCreate()
    _GDIPlus_PathAddEllipse($hPath, 0, 0, $iX, $iY)

    _GDIPlus_GraphicsSetClipPath($hGraphicsDest, $hPath, 0)
    _GDIPlus_GraphicsDrawImage($hGraphicsDest, $hBitmapSource, 0, 0)
    _GDIPlus_ImageSaveToFile($hBitmapDest, @ScriptDir & "\" & $sImageDest)

    _GDIPlus_BitmapDispose($hBitmapSource)
    _GDIPlus_GraphicsDispose($hGraphicsDest)
    _GDIPlus_BitmapDispose($hBitmapDest)
    _GDIPlus_Shutdown()
EndFunc

So what am I doing wrong? Thanks.

 

 

 

 

 

Edited by Viet

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

#include <GDIplus.au3>

clipCircle("96dpi.jpg", "96dpi_out.jpg")
clipCircle("300dpi.jpg", "300dpi_out.jpg")

clipCircle2("96dpi.jpg", "96dpi_out2.jpg")
clipCircle2("300dpi.jpg", "300dpi_out2.jpg")

Func clipCircle($sImageSource, $sImageDest)
    _GDIPlus_Startup()

    $hBitmapSource = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\" & $sImageSource)

    $hGraphicsSource = _GDIPlus_ImageGetGraphicsContext($hBitmapSource)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphicsSource, $GDIP_SMOOTHINGMODE_HIGHQUALITY)

    $iX = _GDIPlus_ImageGetWidth($hBitmapSource)
    $iY = _GDIPlus_ImageGetHeight($hBitmapSource)

;~  Create an empty bitmap
;~  This does not work with 300 DPI images
    $hBitmapDest = _GDIPlus_BitmapCreateFromScan0($iX, $iY)

    ; New line : Fix DPI =====================================================================
    _GDIPlus_BitmapSetResolution($hBitmapDest, _GDIPlus_ImageGetHorizontalResolution($hBitmapSource), _GDIPlus_ImageGetVerticalResolution($hBitmapSource))

    $hGraphicsDest = _GDIPlus_ImageGetGraphicsContext($hBitmapDest)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphicsDest, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
    _GDIPlus_GraphicsClear($hGraphicsDest, 0xFFFFFFFF)

    $hPath = _GDIPlus_PathCreate()
    _GDIPlus_PathAddEllipse($hPath, 0, 0, $iX, $iY)

    _GDIPlus_GraphicsSetClipPath($hGraphicsDest, $hPath, 0)
    _GDIPlus_GraphicsDrawImage($hGraphicsDest, $hBitmapSource, 0, 0)

    ; New line ===============================================================================
    ; Improve Output Quality
    Local $sImgCLSID = _GDIPlus_EncodersGetCLSID("jpg") ;create CLSID for a JPG image file type
    Local $tGUID = _WinAPI_GUIDFromString($sImgCLSID) ;convert CLSID GUID to binary form and returns $tagGUID structure
    Local $tParams = _GDIPlus_ParamInit(1) ;initialize an encoder parameter list and return $tagGDIPENCODERPARAMS structure
    Local $tData = DllStructCreate("int Quality") ;create struct to set JPG quality setting
    DllStructSetData($tData, "Quality", 100) ;quality 0-100 (0: lowest, 100: highest)
    Local $pData = DllStructGetPtr($tData) ;get pointer from quality struct
    _GDIPlus_ParamAdd($tParams, $GDIP_EPGQUALITY, 1, $GDIP_EPTLONG, $pData) ;add a value to an encoder parameter list

    ; New line ===============================================================================
    _GDIPlus_ImageSaveToFileEx($hBitmapDest, @ScriptDir & "\" & $sImageDest, $sImgCLSID, $tParams )

    _GDIPlus_GraphicsDispose($hGraphicsSource)
    _GDIPlus_BitmapDispose($hBitmapSource)
    _GDIPlus_GraphicsDispose($hGraphicsDest)
    _GDIPlus_BitmapDispose($hBitmapDest)
    _GDIPlus_Shutdown()
EndFunc

Func clipCircle2($sImageSource, $sImageDest)
    _GDIPlus_Startup()

    $hBitmapSource = _GDIPlus_BitmapCreateFromFile(@ScriptDir & "\" & $sImageSource)

    $iX = _GDIPlus_ImageGetWidth($hBitmapSource)
    $iY = _GDIPlus_ImageGetHeight($hBitmapSource)

;~  Clone the source bitmap
;~  This works regardless of the image's DPI
    $hBitmapDest = _GDIPlus_BitmapCloneArea($hBitmapSource, 0, 0, $iX, $iY)
    $hGraphicsDest = _GDIPlus_ImageGetGraphicsContext($hBitmapDest)
    _GDIPlus_GraphicsSetSmoothingMode($hGraphicsDest, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
    _GDIPlus_GraphicsClear($hGraphicsDest, 0xFFFFFFFF)

    $hPath = _GDIPlus_PathCreate()
    _GDIPlus_PathAddEllipse($hPath, 0, 0, $iX, $iY)

    _GDIPlus_GraphicsSetClipPath($hGraphicsDest, $hPath, 0)
    _GDIPlus_GraphicsDrawImage($hGraphicsDest, $hBitmapSource, 0, 0)

    ; New line ===============================================================================
    ; Improve Output Quality
    Local $sImgCLSID = _GDIPlus_EncodersGetCLSID("jpg") ;create CLSID for a JPG image file type
    Local $tGUID = _WinAPI_GUIDFromString($sImgCLSID) ;convert CLSID GUID to binary form and returns $tagGUID structure
    Local $tParams = _GDIPlus_ParamInit(1) ;initialize an encoder parameter list and return $tagGDIPENCODERPARAMS structure
    Local $tData = DllStructCreate("int Quality") ;create struct to set JPG quality setting
    DllStructSetData($tData, "Quality", 100) ;quality 0-100 (0: lowest, 100: highest)
    Local $pData = DllStructGetPtr($tData) ;get pointer from quality struct
    _GDIPlus_ParamAdd($tParams, $GDIP_EPGQUALITY, 1, $GDIP_EPTLONG, $pData) ;add a value to an encoder parameter list

    ; New line ===============================================================================
    _GDIPlus_ImageSaveToFileEx($hBitmapDest, @ScriptDir & "\" & $sImageDest, $sImgCLSID, $tParams )

    _GDIPlus_BitmapDispose($hBitmapSource)
    _GDIPlus_GraphicsDispose($hGraphicsDest)
    _GDIPlus_BitmapDispose($hBitmapDest)
    _GDIPlus_Shutdown()
EndFunc

; https://www.autoitscript.com/forum/topic/120163-set-pixels-per-inch-using-gdi/?do=findComment&comment=834906
; https://www.autoitscript.com/forum/topic/106021-gdipau3/
; #FUNCTION# ====================================================================================================================
; Name...........: _GDIPlus_BitmapSetResolution
; Description ...: Sets the resolution of this Bitmap object
; Syntax.........: _GDIPlus_BitmapSetResolution($hBitmap, $nDpiX, $nDpiY)
; Parameters ....: $hBitmap - Pointer to the Bitmap object
;   $nDpiX  - Value that specifies the horizontal resolution in dots per inch.
;   $nDpiX  - Value that specifies the vertical resolution in dots per inch.
; Return values .: Success  - True
;   Failure     - False and either:
;   |@error and @extended are set if DllCall failed
;   |$GDIP_STATUS contains a non zero value specifying the error code
; Remarks .......: None
; Related .......:
; Link ..........; @@MsdnLink@@ GdipBitmapSetResolution
; Example .......; Yes
; ===============================================================================================================================
Func _GDIPlus_BitmapSetResolution($hBitmap, $nDpiX, $nDpiY)
    Local $aResult = DllCall( $__g_hGDIPDll, "uint", "GdipBitmapSetResolution", "handle", $hBitmap, "float", $nDpiX, "float", $nDpiY)
    If @error Then Return SetError(@error, @extended, False)
    $GDIP_STATUS = $aResult[0]
    Return $aResult[0] = 0
EndFunc ;==>_GDIPlus_BitmapSetResolution

 

Edited by Synapsee

Share this post


Link to post
Share on other sites

Thank you Synapsee very much, not only the script is fixed, but it's also improved to set the quality of the output file :D I would like to thank UEZ and the author of the GDIP.au3 script too ;)

1 person likes this

Share this post


Link to post
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
Sign in to follow this  
Followers 0