Jump to content

How to make overlaid PNG have semi-transparent background?


qwert
 Share

Recommended Posts

The new View Image feature of this forum has given me an idea for a better way to overlay a PNG onto an external application. I'm familiar with using WinSetTrans to control an entire window, but is there a way to make only the background of a window semi-transparent, while other elements remain opaque? (like the displayed PNG and any buttons).

Link to comment
Share on other sites

There are many different flavors of that original idea floating around so search for it!

Thanks for the reference. I've downloaded and run the example. I agree that the alpha-channel and grey.gif combination can create amazing PNG-based effects. What I don't see is a way to use "normal PNGs" overlaid on a semitransparent background -- the effect this forum uses.

My application has a library of over 100 PNGs that I'd like to display with this technique. It's not practical for me to edit this effect into every PNG. I could put up the semi-transparent background in one window and then overlay the PNG in its own window, but I was hoping for a simpler method.

Thanks again for your help.

Link to comment
Share on other sites

Maybe it will help you.

Thanks for the additional examples. I've worked with them for hours now -- mainly trying different combinations of layered and child window settings. So far, I can't come up with one that works. The graphic is always semi-transparent, just like the background.

At this point, any hints as to the correct create sequence will be greatly appreciated. I guess my understanding of layered and child windows is pretty weak. Thanks for any help.

Link to comment
Share on other sites

#Include <GUIConstantsEx.au3>
#Include <Icons.au3>
#Include <WindowsConstants.au3>

Global Const $sPng = RegRead('HKLM\SOFTWARE\AutoIt v3\AutoIt', 'InstallDir') & '\Examples\GUI\Advanced\Images\Torus.png'

$hMain = GUICreate('Test', 250, 250, 400, 400)
$Button = GUICtrlCreateButton('OK', 93, 216, 70, 23)
GUISetState()

$hPopup = GUICreate('', 193, 184, 400 + 45 + 3, 400 + 28 + 29, BitOR($WS_DISABLED, $WS_POPUP), -1, $hMain)
$Pic = GUICtrlCreatePic('', 0, 0, 193, 184)
_SetImage(-1, $sPng)
GUISetState()

GUIRegisterMsg($WM_MOVE, "WM_MOVE")

$Timer = TimerInit()
$Trans = 255
$Direction  = -1
$Step = 5
$TimeOut = 30

Do
    If TimerDiff($Timer) > $TimeOut Then
        $Trans += $Step * $Direction
        If $Direction > 0 Then
            If $Trans > 255  Then
                $Trans = 255
                $Direction = -1
            EndIf
        Else
            If $Trans < 0  Then
                $Trans = 0
                $Direction = +1
            EndIf
        EndIf
        WinSetTrans($hPopup, '', $Trans)
        $Timer = TimerInit()
    EndIf
Until GUIGetMsg() = -3

Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    Switch $hWnd
        Case $hMain
            WinMove($hPopup, '', BitAND($lParam, 0xFFFF) + 45, BitShift($lParam, 16) + 28)
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

EDIT:

But I think easier to use few PNGs with different transparency.

Edited by Yashied
Link to comment
Share on other sites

#Include <GUIConstantsEx.au3>
#Include <Icons.au3>
#Include <WindowsConstants.au3>

Global Const $sPng = @ScriptDir & "\torus2.png"

$hMain = GUICreate('Test', 250, 250, 400, 400)
$Button = GUICtrlCreateButton('OK', 93, 216, 70, 23)
GUISetState()

$hPopup = GUICreate('', 193, 184, 400 + 45 + 3, 400 + 28 + 29, BitOR($WS_DISABLED, $WS_POPUP), -1, $hMain)
$Pic = GUICtrlCreatePic('', 0, 0, 193, 184)
_SetImage(-1, $sPng)
GUISetState()

GUIRegisterMsg($WM_MOVE, "WM_MOVE")

$Timer = TimerInit()
$Trans = 255
$Direction  = -1
$Step = 5
$TimeOut = 30

Do
    If TimerDiff($Timer) > $TimeOut Then
        $Trans += $Step * $Direction
        If $Direction > 0 Then
            If $Trans > 255  Then
                $Trans = 255
                $Direction = -1
            EndIf
        Else
            If $Trans < 0  Then
                $Trans = 0
                $Direction = +1
            EndIf
        EndIf
        WinSetTrans($hPopup, '', $Trans)
        $Timer = TimerInit()
    EndIf
Until GUIGetMsg() = -3

Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    Switch $hWnd
        Case $hMain
            WinMove($hPopup, '', BitAND($lParam, 0xFFFF) + 45, BitShift($lParam, 16) + 28)
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

EDIT:

But I think easier to use few PNGs with different transparency.

The problem with changing the transparency of the pic is that it destroys the parts which should be permanently transparent though that might not be a problem for qwert. You can see that if you set the parent gui to be transparent.

This is one way to avoid it but it's slow the way I've done it. It needs the image I've added afterwards as well because it relies on having the transparent bits totally transparent so it falls down there too.

#include <GuiConstants.au3>
 #include <windowsconstants.au3>
 #include "G:\icons\icons.au3"
 Global Const $WS_EX_COMPOSITED = 0x2000000
 Const $maxx = 2, $maxy = 2
 $fade = 0
 $way = 1
 HotKeySet("{ESC}", "QuitApp")
 
 Global $gw = 193, $gh = 184
 
 $hMain = GUICreate('Test', 250, 250, 400, 400)
 $Button = GUICtrlCreateButton('OK', 93, 216, 70, 23)
 GUISetState()
 
 $hPopup = GUICreate('', $gw,$gh, 400 + 45 + 3, 400 + 28 + 29, BitOR($WS_DISABLED, $WS_POPUP), -1, $hMain)
 
 
 Global Const $sPng = @ScriptDir & "\torus2.png"
 $Pic = GUICtrlCreatePic('', 0, 0, 193, 184)
 _SetImage(-1, $sPng)
 
 
 Opt("PixelCoordMode", 2)
 GUISetState()
 GUISetBkColor(0)
 Sleep(3000)
 setTrans($hPopup)
 WinSetTrans($hMain, "", 170)
 
 AdlibEnable("chase", 15)
 GUIRegisterMsg($WM_MOVE, "WM_MOVE")
 While 1
     $Msg = GUIGetMsg()
     Switch $Msg
 
         Case - 3
             Exit
     EndSwitch
 
 WEnd
 
 Func setTrans($hMaskWnd)
     Local $1, $j
     Local $aM_Mask = DllCall("gdi32.dll", "long", "CreateRectRgn", "long", 0, "long", 0, "long", 460, "long", 460)
 
     Local $rct = DllStructCreate("int;int;int;inr", $aM_Mask[0])
     Local $TestCol = PixelGetColor(1, 1);<----------------------------set coords for your test colour here
     ;anything on the window which is the same colour as $TestCol will become transparent
     Local $Startx = -1
     Local $Starty = -1
     Local $Endx = 0
     Local $Endy = 0
     Local $sections = 0
     For $i = 0 To $gw - 1;the window width
         For $j = 0 To $gh - 1;the window height
             If PixelGetColor($i, $j) = $TestCol Then;And $j < $gh
 
                 If $Startx = -1 Then;start a new region
                     $Startx = $i
                     $Starty = $j
                     $Endx = $i
                     $Endy = $j
                 Else;region already started so extend the end
                     $Endx = $i
                     $Endy = $j
                 EndIf
             Else;not testcol or at end of line
                 If $Startx <> -1 Then addRegion($aM_Mask, $Startx, $Starty, $Endx, $Endy,$sections)
 
                 $Startx = -1
                 $Starty = -1
             EndIf
         Next
         addRegion($aM_Mask, $Startx, $Starty, $Endx, $Endy,$sections)
 
         $Startx = -1
         $Starty = -1
     Next
     DllCall("user32.dll", "long", "SetWindowRgn", "hwnd", $hMaskWnd, "long", $aM_Mask[0], "int", 1)
 EndFunc   ;==>setTrans
 
 Func addRegion($aAllMask, $iTopx, $iTopy, $iBotx, $iBoty,ByREf $nc)
     Local $aPartMask = DllCall("gdi32.dll", "long", "CreateRectRgn", "long", $iTopx, "long", $iTopy, "long", $iBotx + 1, "long", $iBoty + 1)
     $nc += 1
    ; ConsoleWrite($nc & ', ')
     DllCall("gdi32.dll", "long", "CombineRgn", "long", $aAllMask[0], "long", $aPartMask[0], "long", $aAllMask[0], "int", 3)
 EndFunc   ;==>addRegion
 
 
 Func QuitApp()
     Exit
 EndFunc   ;==>QuitApp
 
 Func chase()
     $fade += $way
     If $fade > 253 Then $way = -1
     If $fade < 10 Then $way = 1
     WinSetTrans($hPopup, "", $fade)
     Return
 
 EndFunc   ;==>chase
 
 Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
     Switch $hWnd
         Case $hMain
             WinMove($hPopup, '', BitAND($lParam, 0xFFFF) + 45, BitShift($lParam, 16) + 28)
     EndSwitch
     Return $GUI_RUNDEFMSG
 EndFunc   ;==>WM_MOVE

post-3602-12473982712466_thumb.png

Edited by martin
Serial port communications UDF Includes functions for binary transmission and reception.printing UDF Useful for graphs, forms, labels, reports etc.Add User Call Tips to SciTE for functions in UDFs not included with AutoIt and for your own scripts.Functions with parameters in OnEvent mode and for Hot Keys One function replaces GuiSetOnEvent, GuiCtrlSetOnEvent and HotKeySet.UDF IsConnected2 for notification of status of connected state of many urls or IPs, without slowing the script.
Link to comment
Share on other sites

Well, martin. This can be done as follows.

#Include <GUIConstantsEx.au3>
#Include <WinAPI.au3>
#Include <WindowsConstants.au3>

Global Const $sPng = RegRead('HKLM\SOFTWARE\AutoIt v3\AutoIt', 'InstallDir') & '\Examples\GUI\Advanced\Images\Torus.png'

_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile($sPng)

$hMain = GUICreate('Test', 250, 250, 400, 400)
GUISetBkColor(0xF0B7FD)
$Button = GUICtrlCreateButton('OK', 93, 216, 70, 23)
GUISetState()

$hPopup = GUICreate('', 193, 184, 400 + 45 + 3, 400 + 28 + 29, BitOR($WS_DISABLED, $WS_POPUP), $WS_EX_LAYERED, $hMain)
GUISetState()

GUIRegisterMsg($WM_MOVE, "WM_MOVE")

$Timer = TimerInit()
$Trans = 255
$Direction  = -1
$Step = 5
$TimeOut = 30

Do
    If TimerDiff($Timer) > $TimeOut Then
        $Trans += $Step * $Direction
        If $Direction > 0 Then
            If $Trans > 255  Then
                $Trans = 255
                $Direction = -1
            EndIf
        Else
            If $Trans < 0  Then
                $Trans = 0
                $Direction = +1
            EndIf
        EndIf
        _SetBitmap($hPopup, $hImage, $Trans)
        $Timer = TimerInit()
    EndIf
Until GUIGetMsg() = -3

_GDIPlus_Shutdown()

Func _SetBitmap($hWnd, $hImage, $iOpacity)

    Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend

    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, 'X', _GDIPlus_ImageGetWidth($hImage))
    DllStructSetData($tSize, 'Y', _GDIPlus_ImageGetHeight($hImage))
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, 'Alpha', $iOpacity)
    DllStructSetData($tBlend, 'Format', 1)
    _WinAPI_UpdateLayeredWindow($hWnd, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)

    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteDC($hMemDC)

EndFunc   ;==>_SetBitmap

Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    Switch $hWnd
        Case $hMain
            WinMove($hPopup, '', BitAND($lParam, 0xFFFF) + 45, BitShift($lParam, 16) + 28)
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE
Edited by Yashied
Link to comment
Share on other sites

... though that might not be a problem for qwert.

"The problem for qwert" is that both of these examples go WAY beyond what I need. I have run both of them and they are impressive ... and will probably help many people. But what I need is far simpler. Mainly, I don't need to have the PNG to fade in. Just click on the image file below to see the effect.

Martin, I was able to replace the GUICreate in your example with the following to obtain the effect I need:

$hMain = GUICreate('Test', @DesktopWidth, @DesktopHeight-60, 0, 0, BitOR($WS_POPUP,$WS_BORDER))

GUISetBkColor(0x222222) ; will change background color

WinSetTrans ( $hMain, "", 128 )

Since this gives the proper background, isn't there a simple way to tell windows that the PNG I'm displaying on the semi-transparent background should be "normal"? Or is is necessary to use the _SetImage function to fade the image into view? This is where I'm lost as to how to proceed.

Thanks to you both for your help and examples.

post-29172-12474092628298_thumb.png

Link to comment
Share on other sites

#include <Icons.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <WinAPI.au3>
#include <WindowsConstants.au3>
_GDIPlus_Startup()

Dim $hGUI = GUICreate('', @DesktopWidth, @DesktopHeight, 0, 0, $WS_POPUP, BitOR($WS_EX_TOOLWINDOW, $WS_EX_TOPMOST))
Dim $hChildGUI
Dim $Picture
Dim $hGraphics, $hBrush, $hImage
Dim $iImageWidth, $iImageHeight

GUISetState()
WinSetTrans($hGUI, '', 220)

$hGraphics = _GDIPlus_GraphicsCreateFromHWND($hGUI)
$hBrush = _GDIPlus_BrushCreateSolid()
$hImage = _GDIPlus_ImageLoadFromFile(@ScriptDir & '\image.png')
$iImageWidth = _GDIPlus_ImageGetWidth($hImage)
$iImageHeight = _GDIPlus_ImageGetHeight($hImage)
_GDIPlus_GraphicsFillRect($hGraphics, 0, 0, @DesktopWidth, @DesktopHeight, $hBrush)

$hChildGUI = GUICreate('', $iImageWidth, $iImageHeight, @DesktopWidth/2-$iImageWidth/2, @DesktopHeight/2-$iImageHeight/2, BitOR($WS_DISABLED, $WS_POPUP), -1, $hGUI)
$Picture = GUICtrlCreatePic('', 0, 0, $iImageWidth, $iImageHeight)
_SetImage($Picture, @ScriptDir & '\image.png')
GUISetState()

Do
Until GUIGetMsg() = -3

_GDIPlus_BrushDispose($hBrush)
_GDIPlus_GraphicsDispose($hGraphics)
_GDIPlus_ImageDispose($hImage)
_GDIPlus_Shutdown()
Exit

Link to comment
Share on other sites

... isn't there a simple way to tell windows that the PNG I'm displaying on the semi-transparent background should be "normal"?

Thanks, Authenticity. Indeed, your script does achieve exactly the END result I need. Unfortunately, it introduces five or six "flicker artifacts" in creating the semi-transparent background -- which don't result in a very good visual experience. I suppose a hide window at the beginning could work around that issue -- but that would introduce even more delay in getting the PNG on the screen.

Given the complexity of the examples so far, am I correct in concluding that this isn't possible with a simple layered or child window? If it isn't, then my top alternative seems to be two separate windows. In other words, put up the semi-transparent screen ... and then put up the PNG on top of it. It's not ideal, but it would be fast and simple.

As I mentioned previously, I struggle to follow the reasoning behind the techniques presented. Any insight as to why this is a case of "it just takes what it takes" will be appreciated.

Link to comment
Share on other sites

Well, martin. This can be done as follows.

#Include <GUIConstantsEx.au3>
#Include <WinAPI.au3>
#Include <WindowsConstants.au3>

Global Const $sPng = RegRead('HKLM\SOFTWARE\AutoIt v3\AutoIt', 'InstallDir') & '\Examples\GUI\Advanced\Images\Torus.png'

_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile($sPng)

$hMain = GUICreate('Test', 250, 250, 400, 400)
GUISetBkColor(0xF0B7FD)
$Button = GUICtrlCreateButton('OK', 93, 216, 70, 23)
GUISetState()

$hPopup = GUICreate('', 193, 184, 400 + 45 + 3, 400 + 28 + 29, BitOR($WS_DISABLED, $WS_POPUP), $WS_EX_LAYERED, $hMain)
GUISetState()

GUIRegisterMsg($WM_MOVE, "WM_MOVE")

$Timer = TimerInit()
$Trans = 255
$Direction  = -1
$Step = 5
$TimeOut = 30

Do
    If TimerDiff($Timer) > $TimeOut Then
        $Trans += $Step * $Direction
        If $Direction > 0 Then
            If $Trans > 255  Then
                $Trans = 255
                $Direction = -1
            EndIf
        Else
            If $Trans < 0  Then
                $Trans = 0
                $Direction = +1
            EndIf
        EndIf
        _SetBitmap($hPopup, $hImage, $Trans)
        $Timer = TimerInit()
    EndIf
Until GUIGetMsg() = -3

_GDIPlus_Shutdown()

Func _SetBitmap($hWnd, $hImage, $iOpacity)

    Local $hScrDC, $hMemDC, $hBitmap, $hOld, $pSize, $tSize, $pSource, $tSource, $pBlend, $tBlend

    $hScrDC = _WinAPI_GetDC(0)
    $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC)
    $hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
    $hOld = _WinAPI_SelectObject($hMemDC, $hBitmap)
    $tSize = DllStructCreate($tagSIZE)
    $pSize = DllStructGetPtr($tSize)
    DllStructSetData($tSize, 'X', _GDIPlus_ImageGetWidth($hImage))
    DllStructSetData($tSize, 'Y', _GDIPlus_ImageGetHeight($hImage))
    $tSource = DllStructCreate($tagPOINT)
    $pSource = DllStructGetPtr($tSource)
    $tBlend = DllStructCreate($tagBLENDFUNCTION)
    $pBlend = DllStructGetPtr($tBlend)
    DllStructSetData($tBlend, 'Alpha', $iOpacity)
    DllStructSetData($tBlend, 'Format', 1)
    _WinAPI_UpdateLayeredWindow($hWnd, $hScrDC, 0, $pSize, $hMemDC, $pSource, 0, $pBlend, $ULW_ALPHA)

    _WinAPI_ReleaseDC(0, $hScrDC)
    _WinAPI_SelectObject($hMemDC, $hOld)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteDC($hMemDC)

EndFunc   ;==>_SetBitmap

Func WM_MOVE($hWnd, $iMsg, $wParam, $lParam)
    Switch $hWnd
        Case $hMain
            WinMove($hPopup, '', BitAND($lParam, 0xFFFF) + 45, BitShift($lParam, 16) + 28)
    EndSwitch
    Return $GUI_RUNDEFMSG
EndFunc   ;==>WM_MOVE

Wow! I didn't reralize _WinAPI_UpdateLayeredWindow could do that. Thanks Yashied.
Serial port communications UDF Includes functions for binary transmission and reception.printing UDF Useful for graphs, forms, labels, reports etc.Add User Call Tips to SciTE for functions in UDFs not included with AutoIt and for your own scripts.Functions with parameters in OnEvent mode and for Hot Keys One function replaces GuiSetOnEvent, GuiCtrlSetOnEvent and HotKeySet.UDF IsConnected2 for notification of status of connected state of many urls or IPs, without slowing the script.
Link to comment
Share on other sites

I hope it's faster and cleaner than before:

#include <GDIPlus.au3>
#include <Icons.au3>
#include <WindowsConstants.au3>
_GDIPlus_Startup()

Dim $hGUI, $hChildGUI
Dim $hImage, $iImageWidth, $iImageHeight
Dim $hBitmap


$hImage = _GDIPlus_BitmapCreateFromFile(@ScriptDir & '\image.png')
$iImageWidth = _GDIPlus_ImageGetWidth($hImage)
$iImageHeight = _GDIPlus_ImageGetHeight($hImage)
$hBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
_GDIPlus_ImageDispose($hImage)

$hGUI = GUICreate('Test', @DesktopWidth, @DesktopHeight, 0, 0, $WS_POPUP, $WS_EX_TOOLWINDOW)
WinSetTrans($hGUI, '', 220)
GUISetBkColor(0)
GUISetState()

$hChildGUI = GUICreate('', $iImageWidth, $iImageHeight, @DesktopWidth/2-$iImageWidth/2, @DesktopHeight/2-$iImageHeight/2, BitOR($WS_DISABLED, $WS_POPUP), -1, $hGUI)
$Picture = GUICtrlCreatePic('', 0, 0, $iImageWidth, $iImageHeight)
_SetHImage($Picture, $hBitmap)
GUISetState()

Do
Until GUIGetMsg() = -3

GUIDelete()
_WinAPI_DeleteObject($hBitmap)
_GDIPlus_Shutdown()

It's also much simpler than before, in my opinion at least.

Edit: Changed to use _SetHImage() instead.

Edited by Authenticity
Link to comment
Share on other sites

Authenticity, thanks. That's the result I was hoping for. I had just finished testing with one of my graphics when I noticed you had changed to pre-convert the PNG to a bitmap.

Before I make the corresponding changes, I'd like to understand the advantage of using _SetHImage. Is it simply faster? The only (very minor) shortfall of your previous method was a 50 millisecond flash of the child GUI's background before the image occupies the area -- and I was working with @SW_HIDE/SHOW to reduce even that.

Either way, this is going to be the basis for a

Super Duper General-Purpose Image Display Utility -- with the option of a semi-transparent background

Link to comment
Share on other sites

:) heh I see Yashied waiting to response to such targeting question. In general, the is a subtle difference of using _SetHImage() so it won't get loaded from file as it used by the _SetImage() function and is already used by the main script to get the image dimensions and stuff.
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...