Jump to content

Recommended Posts

Posted (edited)

Hi,

While playing around with GDIPlus I was wondering how to save a non transperent image as a png with transparency.

Pretty sure it's been done before , but thought I'd post an unpolished/uncommented/no error checking example anyway :D

#include <GDIPlus.au3>

Global $sRegPath, $sImageIn, $sImageOut

$sRegPath = "HKLM\SOFTWARE\AutoIt v3\AutoIt"
If StringInStr("X64IA64", @OSArch) Then $sRegPath = StringReplace($sRegPath, "SOFTWARE", "SOFTWARE\Wow6432Node")

$sImageIn = RegRead($sRegPath, "InstallDir") & "\Examples\GUI\logo4.gif"

$sImageOut = @ScriptDir & "\Trans_logo4.png"

_ImageToTransPNG($sImageIn, $sImageOut)

If FileExists($sImageOut) Then ShellExecute($sImageOut)


Func _ImageToTransPNG($sInFile, $sOutFile, $iXPixel = 0, $iYPixel = 0)
    Local $hImage, $iW, $iH, $iFirstPixel, $iTransPixel, $tBitmapData, $iStride, $iScan0, $iX, $iY, $tPixel, $iPixel
    Local $v_BufferA, $AllPixels, $sREResult1, $sPix
    _GDIPlus_Startup()

    $hImage = _GDIPlus_ImageLoadFromFile($sInFile)
    $iW = _GDIPlus_ImageGetWidth($hImage)
    $iH = _GDIPlus_ImageGetHeight($hImage)

    ;=> Start Work araound For XP, GDIPBitmapLockBits() seem to hard crash autoit When using images that are less then 24bpp
    ; If your using Vista or Newer OS then this won't be called or needed.
    If StringInStr('"WIN_2003","WIN_XP","WIN_2000"', @OSVersion) Then
        Local $aRet, $hBmp, $hBitmap, $hGraphic
        $aRet = _GDIPlus_ImageGetPixelFormat($hImage)
        If Int(StringRegExpReplace($aRet[1], "\D+", "")) < 24 Then
            $hBmp = _WinAPI_CreateBitmap($iW, $iH, 1, 32)
            $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hBmp)
            _WinAPI_DeleteObject($hBmp)
            $hGraphic = _GDIPlus_ImageGetGraphicsContext($hBitmap)
            _GDIPlus_GraphicsDrawImage($hGraphic, $hImage, 0, 0)
            _GDIPlus_GraphicsDispose($hGraphic)
            _GDIPlus_ImageDispose($hImage)          
            $hImage = _GDIPlus_BitmapCloneArea($hBitmap, 0, 0, $iW, $iH, $GDIP_PXF32ARGB)
            _GDIPlus_BitmapDispose($hBitmap)
        EndIf
    EndIf
    ;=> End Work around

    $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW, $iH, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
    $iStride = DllStructGetData($tBitmapData, "stride")
    $iScan0 = DllStructGetData($tBitmapData, "Scan0")

    ; Get Pixel colour to be transparent at coordinates ($iXPixel, $iYPixel).
    $tPixel = DllStructCreate("int", $iScan0 + ($iYPixel * $iStride) + ($iXPixel * 4))
    $iFirstPixel = DllStructGetData($tPixel, 1)
    $iTransPixel = BitAND($iFirstPixel, 0x00FFFFFF)


    $iFirstPixel = StringRegExpReplace(Hex($iFirstPixel, 8), "(.{2})(.{2})(.{2})(.{2})", "\4\3\2\1")
    ConsoleWrite($iFirstPixel & @LF)
    $iTransPixel = StringTrimRight($iFirstPixel, 2) & "00"
    $v_BufferA = DllStructCreate("byte[" & $iH * $iW * 4 & "]", $iScan0) ; Create DLL structure for all pixels
    $AllPixels = DllStructGetData($v_BufferA, 1)
    $sREResult1 = StringRegExpReplace(StringTrimLeft($AllPixels, 2), "(.{8})", "\1 ")
    $sPix = "0x" & StringStripWS(StringRegExpReplace($sREResult1, "(" & $iFirstPixel & ")", $iTransPixel), 8)
    $AllPixels = DllStructSetData($v_BufferA, 1, $sPix)

    _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
    _GDIPlus_ImageSaveToFileEx($hImage, $sOutFile, _GDIPlus_EncodersGetCLSID("PNG"))
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_Shutdown()
EndFunc   ;==>_ImageToTransPNG
It appaers that <24bpp images and _GDIPlus_BitmapLockBits() in XP crash AutoIt when calling $GDIP_ILMWRITE.

As a dirty work around I wrote in a check that should resolve it.

Cheers

Edit: Malkey did a nice example, posted his code, as it's faster and functional.

Nice one Malkey :D

Edited by smashly
Posted

Probably this is causing the crash (line 34-40).

...
    For $iX = 0 To $iW - 1
        For $iY = 0 To $iH - 1
            $tPixel = DllStructCreate("int", $iScan0 + ($iY * $iStride) + ($iX * 4))
            $iPixel = DllStructGetData($tPixel, 1)
            If $iPixel = $iFirstPixel Then DllStructSetData($tPixel, 1, $iTransPixel)
        Next
    Next
...

UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Posted

Probably this is causing the crash (line 34-40).

...
    For $iX = 0 To $iW - 1
        For $iY = 0 To $iH - 1
            $tPixel = DllStructCreate("int", $iScan0 + ($iY * $iStride) + ($iX * 4))
            $iPixel = DllStructGetData($tPixel, 1)
            If $iPixel = $iFirstPixel Then DllStructSetData($tPixel, 1, $iTransPixel)
        Next
    Next
...

UEZ

Nope that's not it at all...

I'm using win 7 x 64 and I don't have a problem with the code..

XP x86 that's another kettle of fish..

FFS...

I found where it errors..

$tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW, $iH, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)

returns @error 2 under xp ... wtf..

But that only happens when using a gif file..

@error 2 = InvalidParameter

2 x wtf...

Yet I load a jpg, png, bmp as an input file and xp works fine o_0

There is nothing wrong with the params..

I just checked MSDN and it even says that the $iFormat in _GDIPlus_BitmapLockBits() function does not have to be the same format as the image as your locking.

Glad I moved on to win 7 x64 as I'm sorta tired of battling with xp x86 and it's quirks with autoit..

All I can suggest if you'd like to see it work is try the function with different input image format.. eg: png, jpg, bmp..

Cheers

Posted

Worked out why it was crashing in xp..

It appaers that <24bpp images and _GDIPlus_BitmapLockBits() in XP crash AutoIt when calling $GDIP_ILMWRITE.

Added dirty work around when the code is run on XP.

Cheers

Posted

Some possible alternatives for your consideration.

1/ Within the load Gif file in xp work around, this example uses _GDIPlus_BitmapCloneArea() instead of saving the gif image as a png file to disk.

2/ Removed _GDIPlus_BitmapGetPixel(). All pixels are already accessible when _GDIPlus_BitmapLockBits() is called.

3/ Instead of the two nested For- Next statements, StringRegExpReplace() is used which appears to be about three times faster.

;
#include <GDIPlus.au3>

Global $sRegPath, $sImageIn, $sImageOut

$sRegPath = "HKLM\SOFTWARE\AutoIt v3\AutoIt"
If StringInStr("X64IA64", @OSArch) Then $sRegPath = StringReplace($sRegPath, "SOFTWARE", "SOFTWARE\Wow6432Node")

$sImageIn = RegRead($sRegPath, "InstallDir") & "\Examples\GUI\logo4.gif"

$sImageOut = @ScriptDir & "\Trans_logo4.png"

_ImageToTransPNG($sImageIn, $sImageOut)

If FileExists($sImageOut) Then ShellExecute($sImageOut)


Func _ImageToTransPNG($sInFile, $sOutFile, $iXPixel = 0, $iYPixel = 0)
    Local $hImage, $iW, $iH, $iFirstPixel, $iTransPixel, $tBitmapData, $iStride, $iScan0, $iX, $iY, $tPixel, $iPixel
    Local $sXP_24bpp = @ScriptDir & "\XP_24bpp.png"

    _GDIPlus_Startup()

    $hImage = _GDIPlus_ImageLoadFromFile($sInFile)
    $iW = _GDIPlus_ImageGetWidth($hImage)
    $iH = _GDIPlus_ImageGetHeight($hImage)

    ;=> Start Work araound For XP, GDIPBitmapLockBits() seem to hard crash autoit When using images that are less then 24bpp
    ; If your using Vista or Newer OS then this won't be called or needed.
    If StringInStr("WIN_2003,WIN_XP,WIN_2000", @OSVersion) Then
        Local $aRet, $hBmp, $hBitmap, $hGraphic
        $aRet = _GDIPlus_ImageGetPixelFormat($hImage)
        If Int(StringRegExpReplace($aRet[1], "\D+", "")) < 24 Then
            $hBmp = _WinAPI_CreateBitmap($iW, $iH, 1, 32)
            $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($hBmp)
            _WinAPI_DeleteObject($hBmp)
            $hGraphic = _GDIPlus_ImageGetGraphicsContext($hBitmap)
            _GDIPlus_GraphicsDrawImage($hGraphic, $hImage, 0, 0)
            _GDIPlus_ImageDispose($hImage)
            _GDIPlus_GraphicsDispose($hGraphic)
            $hImage = _GDIPlus_BitmapCloneArea($hBitmap, 0, 0, $iW, $iH, $GDIP_PXF32ARGB)
            _GDIPlus_BitmapDispose($hBitmap)
        EndIf
    EndIf
    ;=> End Work around

    $tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW, $iH, $GDIP_ILMWRITE, $GDIP_PXF32ARGB)
    $iStride = DllStructGetData($tBitmapData, "stride")
    $iScan0 = DllStructGetData($tBitmapData, "Scan0")

    ; Get Pixel colour to be transparent at coordinates ($iXPixel, $iYPixel).
    $tPixel = DllStructCreate("int", $iScan0 + ($iYPixel * $iStride) + ($iXPixel * 4))
    $iFirstPixel = DllStructGetData($tPixel, 1)
    $iTransPixel = BitAND($iFirstPixel, 0x00FFFFFF)
    Local $begin = TimerInit()

    #cs
    For $iX = 0 To $iW - 1
        For $iY = 0 To $iH - 1
            $tPixel = DllStructCreate("int", $iScan0 + ($iY * $iStride) + ($iX * 4))
            $iPixel = DllStructGetData($tPixel, 1)
            If $iPixel = $iFirstPixel Then DllStructSetData($tPixel, 1, $iTransPixel)
        Next
    Next
    #ce
    ;#cs
    $iFirstPixel = StringRegExpReplace(Hex($iFirstPixel, 8), "(.{2})(.{2})(.{2})(.{2})", "\4\3\2\1")
    $iTransPixel = StringTrimRight($iFirstPixel, 2) & "00"
    Local $v_BufferA = DllStructCreate("byte[" & $iH * $iW * 4 & "]", $iScan0) ; Create DLL structure for all pixels
    Local $AllPixels = DllStructGetData($v_BufferA, 1)
    Local $sREResult1 = StringRegExpReplace(StringTrimLeft($AllPixels, 2), "(.{8})", "\1 ")
    Local $sPix = "0x" & StringStripWS(StringRegExpReplace($sREResult1, "(" & $iFirstPixel & ")", $iTransPixel), 8)
    $AllPixels = DllStructSetData($v_BufferA, 1, $sPix)
    ;#ce
    _GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
    ConsoleWrite("Time: " & TimerDiff($begin) & @CRLF)

    _GDIPlus_ImageSaveToFileEx($hImage, $sOutFile, _GDIPlus_EncodersGetCLSID("PNG"))

    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_Shutdown()
    If FileExists($sXP_24bpp) Then FileDelete($sXP_24bpp)
EndFunc   ;==>_ImageToTransPNG
;
  • 1 month later...
  • 5 years later...

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
  • Recently Browsing   0 members

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