Sign in to follow this  
Followers 0
smashly

image to transparent png

10 posts in this topic

#1 ·  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

Share this post


Link to post
Share on other sites



Thanks for sharing Mr. GDI+ ;-) but AutoIt is crashing when running the code.

No em in the console window!

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!
¯\_(ツ)_/¯

Share this post


Link to post
Share on other sites

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!
¯\_(ツ)_/¯

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Yes, with another image it worked properly!

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!
¯\_(ツ)_/¯

Share this post


Link to post
Share on other sites

Works ok on x86 Win7. Cool example :D

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

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
;

Share this post


Link to post
Share on other sites

Looking good, gentlemen!

Now who wants to bring it on home with "_ExtractIconToTransparentPng.au3"?

Homer Simpson voice: "Hauuughhhhh iconnn to pnggggggg"

Share this post


Link to post
Share on other sites

#10 ·  Posted (edited)

:thumbsup:

Thanks for your code.

Good job.

Edited by Xwolf1

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