Jump to content

[GDI+] Recolor icon background and foreground


Recommended Posts

Hi all,

I'm attempting to recolor both the foreground (single color) and background (transparency) of an icon. Currently I'm using an updated _SetBKIcon script

Func _SetBkIcon($ControlID, $iBackground, $sIcon, $iIndex, $iWidth, $iHeight)

    Local Static $STM_SETIMAGE = 0x0172
    Local $hDC, $hBackDC, $hBackSv, $hBitmap, $hImage, $hIcon, $hBkIcon

    $hIcon = _WinAPI_ShellExtractIcon($sIcon, $iIndex, $iWidth, $iHeight)

    $hDC = _WinAPI_GetDC(0)
    $hBackDC = _WinAPI_CreateCompatibleDC($hDC)
    $hBitmap = _WinAPI_CreateSolidBitmap(0, $iBackground, $iWidth, $iHeight)
    $hBackSv = _WinAPI_SelectObject($hBackDC, $hBitmap)
    _WinAPI_DrawIconEx($hBackDC, 0, 0, $hIcon, 0, 0, 0, 0, $DI_NORMAL)

    $hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
    $hBkIcon = DllCall($__g_hGDIPDll, 'int', 'GdipCreateHICONFromBitmap', 'hWnd', $hImage, 'int*', 0)
    $hBkIcon = $hBkIcon[2]
    _GDIPlus_ImageDispose($hImage)

    GUICtrlSendMsg($ControlID, $STM_SETIMAGE, $IMAGE_ICON, _WinAPI_CopyIcon($hBkIcon))
    _WinAPI_RedrawWindow(GUICtrlGetHandle($ControlID))

    _WinAPI_SelectObject($hBackDC, $hBackSv)
    _WinAPI_DeleteDC($hBackDC)
    _WinAPI_ReleaseDC(0, $hDC)
    _WinAPI_DeleteObject($hBkIcon)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteObject($hIcon)

    Return SetError(0, 0, 1)
EndFunc   ;==>_SetBkIcon

and I see some GDI functions for recoloring but I don't see anywhere that they can be used. However, most of the recoloring uses 

_GDIPlus_ImageAttributesCreate()

However, this isn't used in the existing function, so I don't see how it can be merged in. I think this is because the icon is just copied onto the existing specified color background in _SetBKIcon. I think it might be better to use

_GDIPlus_ImageAttributesSetRemapTable()

to change transparency in an icon to a specific color and the color of the icon to a different color, however it uses

_GDIPlus_GraphicsCreateFromHWND()

which specifies in documentation that it needs a Window Handle, not a control handle.

 

Any advice is appreciated!

Edited by rcmaehl

My UDFs are generally for me. If they aren't updated for a while, it means I'm not using them myself. As soon as I start using them again, they'll get updated.

My Projects

WhyNotWin11
Cisco FinesseGithubIRC UDFWindowEx UDF

 

Link to post
Share on other sites
Posted (edited)

I've attempted to implement part of @UEZ's GDI+ Color Transformer v0.9.7 build 2016-04-23. The recoloring works within his executable, however I can't seem to reproduce the effect. My code is below:

#include <GDIPlus.au3>
#include <WinAPISysWin.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

#include "ResourcesEX.au3"

Global Const $GDIP_COLORCURVEEFFECT = "{DD6A0022-58E4-4a67-9D9B-D48EB881A53D}"
Global Const $tagCOLORCURVEEFFECTPARAMS = "int type;int channel;int value"

Global Enum $iAdjustExposure = 0, $iAdjustDensity, $iAdjustContrast, $iAdjustHighlight, $iAdjustShadow, _ ;http://msdn.microsoft.com/en-us/library/windows/desktop/ms534098(v=vs.85).aspx
        $iAdjustMidtone, $iAdjustWhiteSaturation, $iAdjustBlackSaturation
Global Enum $iCurveChannelAll = 0, $iCurveChannelRed, $iCurveChannelGreen, $iCurveChannelBlue ;http://msdn.microsoft.com/en-us/library/windows/desktop/ms534100(v=vs.85).aspx
Global $tColorCurve = DllStructCreate($tagCOLORCURVEEFFECTPARAMS), $iType = $iAdjustExposure, $iChannel = $iCurveChannelAll

Example_1()

;######################################################################################################################################
; #FUNCTION# ====================================================================================================================
; Name ..........: _GDIPlus_GraphicsGetDPIRatio
; Description ...:
; Syntax ........: _GDIPlus_GraphicsGetDPIRatio([$iDPIDef = 96])
; Parameters ....: $iDPIDef             - [optional] An integer value. Default is 96.
; Return values .: None
; Author ........: UEZ
; Modified ......:
; Remarks .......:
; Related .......:
; Link ..........: http://www.autoitscript.com/forum/topic/159612-dpi-resolution-problem/?hl=%2Bdpi#entry1158317
; Example .......: No
; ===============================================================================================================================
Func _GDIPlus_GraphicsGetDPIRatio($iDPIDef = 96)
    _GDIPlus_Startup()
    Local $hGfx = _GDIPlus_GraphicsCreateFromHWND(0)
    If @error Then Return SetError(1, @extended, 0)
    #forcedef $__g_hGDIPDll

    Local $aResult = DllCall($__g_hGDIPDll, "int", "GdipGetDpiX", "handle", $hGfx, "float*", 0)

    If @error Then Return SetError(2, @extended, 0)
    Local $iDPI = $aResult[2]
    Local $aResults[2] = [$iDPIDef / $iDPI, $iDPI / $iDPIDef]
    _GDIPlus_GraphicsDispose($hGfx)
    _GDIPlus_Shutdown()
    Return $aResults
EndFunc   ;==>_GDIPlus_GraphicsGetDPIRatio

Func _GDIPlus_ColorCurve($tColorCurve, $iType, $iChannel, $iValue)
    If Not IsDllStruct($tColorCurve) Then Return SetError(1, @error, 0)
    DllStructSetData($tColorCurve, "type", $iType)
    DllStructSetData($tColorCurve, "channel", $iChannel)
    DllStructSetData($tColorCurve, "value", $iValue)
    Local $pEffect = _GDIPlus_EffectCreate($GDIP_COLORCURVEEFFECT)
    If @error Then Return SetError(2, @error, 0)
    _GDIPlus_EffectsSetParameters($pEffect, $tColorCurve)
    If @error Then Return SetError(3, @error, 0)
    Return $pEffect
EndFunc   ;==>_GDIPlus_ColorCurve

Func _GDIPlus_EffectsSetParameters($pEffectObject, $tEffectParameters, $iSizeAdj = 1)
    Local $aSize = DllCall($__g_hGDIPDll, "uint", "GdipGetEffectParameterSize", "ptr", $pEffectObject, "uint*", 0)
    Local $iSize = $aSize[2] * $iSizeAdj
    Local $aResult = DllCall($__g_hGDIPDll, "uint", "GdipSetEffectParameters", "ptr", $pEffectObject, "struct*", $tEffectParameters, "uint", $iSize)
    If @error Then Return SetError(@error, @extended, 0)
    Return SetError($aResult[0], 0, $aResult[3])
EndFunc   ;==>_GDIPlus_EffectsSetParameters

Func _SetBkIcon($ControlID, $iBackground, $iForeground, $sIcon, $iIndex, $iWidth, $iHeight)

    Local Static $STM_SETIMAGE = 0x0172
    Local $hDC, $hBackDC, $hBackSv, $hBitmap, $hImage, $hIcon, $hBkIcon
    Local $hEffect

    $hIcon = _WinAPI_ShellExtractIcon($sIcon, $iIndex, $iWidth, $iHeight)

    $hDC = _WinAPI_GetDC(0)
    $hBackDC = _WinAPI_CreateCompatibleDC($hDC)
    $hBitmap = _WinAPI_CreateSolidBitmap(0, $iBackground, $iWidth, $iHeight)
    $hBackSv = _WinAPI_SelectObject($hBackDC, $hBitmap)
    _WinAPI_DrawIconEx($hBackDC, 0, 0, $hIcon, 0, 0, 0, 0, $DI_NORMAL)

    $hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBitmap)
    $hBkIcon = DllCall($__g_hGDIPDll, 'int', 'GdipCreateHICONFromBitmap', 'hWnd', $hImage, 'int*', 0)
    $hBkIcon = $hBkIcon[2]

    Local $sForeground = Hex($iForeground)
    Local $iRed = Dec(StringRight(StringLeft($sForeground, 4), 2))
    Local $iGreen = Dec(StringRight(StringLeft($sForeground, 6), 2))
    Local $iBlue = Dec(StringRight(StringLeft($sForeground, 8), 2))

    $hEffect = _GDIPlus_ColorCurve($tColorCurve, $iType, $iCurveChannelAll, 255)

    _GDIPlus_EffectDispose($hEffect)

    $hEffect = _GDIPlus_ColorCurve($tColorCurve, $iType, $iCurveChannelRed, $iRed)

    _GDIPlus_EffectDispose($hEffect)

    $hEffect = _GDIPlus_ColorCurve($tColorCurve, $iType, $iCurveChannelGreen, $iGreen)

    _GDIPlus_EffectDispose($hEffect)

    $hEffect = _GDIPlus_ColorCurve($tColorCurve, $iType, $iCurveChannelBlue, $iBlue)

    _GDIPlus_EffectDispose($hEffect)

    _GDIPlus_ImageDispose($hImage)

    GUICtrlSendMsg($ControlID, $STM_SETIMAGE, $IMAGE_ICON, _WinAPI_CopyIcon($hBkIcon))
    _WinAPI_RedrawWindow(GUICtrlGetHandle($ControlID))

    _WinAPI_SelectObject($hBackDC, $hBackSv)
    _WinAPI_DeleteDC($hBackDC)
    _WinAPI_ReleaseDC(0, $hDC)
    _WinAPI_DeleteObject($hBkIcon)
    _WinAPI_DeleteObject($hBitmap)
    _WinAPI_DeleteObject($hIcon)

    Return SetError(0, 0, 1)
EndFunc   ;==>_SetBkIcon

Func Example_1()
    Local $hGui = GUICreate("GDI+", 200, 50)
    GUISetState(@SW_SHOW)

    _GDIPlus_Startup()

    GUICtrlCreateIcon("", -1, 10, 10, 32, 32)
    _SetBkIcon(-1, 0x000000, 0xFFFFFF, @ScriptDir & "\git.ico", -1, 32, 32)

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    GUIDelete($hGui)
EndFunc   ;==>Example_1

I'm 200% sure this is user error.

ResourcesEx.au3 git.ico

Edited by rcmaehl

My UDFs are generally for me. If they aren't updated for a while, it means I'm not using them myself. As soon as I start using them again, they'll get updated.

My Projects

WhyNotWin11
Cisco FinesseGithubIRC UDFWindowEx UDF

 

Link to post
Share on other sites

As far as I can remember the recoloring aka HUE modification works only for color images. Your icon is nearly a b/w image.

In this case you can do something like this here:

#include <GDIPlus.au3>
#include <WinAPISysWin.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Example_1()

Func _SetBkIcon($ControlID, $iForeground_old, $iForeground_new, $iBackground, $sIcon, $iIndex, $iWidth, $iHeight)

    Local Static $STM_SETIMAGE = 0x0172
    Local $hBitmap, $hImage, $hIcon, $hBkIcon, $hGfx

    $hIcon = _WinAPI_ShellExtractIcon($sIcon, $iIndex, $iWidth, $iHeight)
    $hImage = _GDIPlus_BitmapCreateFromHICON($hIcon)

    Local $aRemapTable[2][2]
    $aRemapTable[0][0] = 1
    $aRemapTable[1][0] = $iForeground_old
    $aRemapTable[1][1] = $iForeground_new

    Local $hImgAttr = _GDIPlus_ImageAttributesCreate()
    _GDIPlus_ImageAttributesSetRemapTable($hImgAttr, $aRemapTable)
    Local $iW = _GDIPlus_ImageGetWidth($hImage), $iH = _GDIPlus_ImageGetHeight($hImage)
    $hBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    $hGfx = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    _GDIPlus_GraphicsClear($hGfx, $iBackground)
    _GDIPlus_GraphicsDrawImageRectRect($hGfx, $hImage, 0, 0, $iW, $iH, 0, 0, $iW, $iH, $hImgAttr)

    $hBkIcon = _GDIPlus_HICONCreateFromBitmap($hBitmap)
    GUICtrlSendMsg($ControlID, $STM_SETIMAGE, $IMAGE_ICON, _WinAPI_CopyIcon($hBkIcon))
    _WinAPI_RedrawWindow(GUICtrlGetHandle($ControlID))

    _WinAPI_DeleteObject($hIcon)
    _WinAPI_DeleteObject($hBkIcon)
    _GDIPlus_GraphicsDispose($hGfx)
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_ImageDispose($hBitmap)
    _GDIPlus_ImageAttributesDispose($hImgAttr)
    Return SetError(0, 0, 1)
EndFunc   ;==>_SetBkIcon

Func Example_1()
    Local $hGui = GUICreate("GDI+", 200, 50)
    GUISetState(@SW_SHOW)

    _GDIPlus_Startup()

    GUICtrlCreateIcon("", -1, 10, 10, 32, 32)
    _SetBkIcon(-1, 0xFF000000, 0xFFFFFFFF, 0xFF000000, @ScriptDir & "\git.ico", -1, 32, 32)

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    GUIDelete($hGui)
EndFunc   ;==>Example_1

It will change the foreground color from black to white and set the background color to black.

 

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!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Link to post
Share on other sites
Posted (edited)
6 hours ago, UEZ said:

As far as I can remember the recoloring aka HUE modification works only for color images. Your icon is nearly a b/w image.

In this case you can do something like this here:

It will change the foreground color from black to white and set the background color to black.

Works good. Thanks! Anti-aliasing in the icons is missing them up a bit but I should be able to fix that using by adjusting the remap table.

Edited by rcmaehl

My UDFs are generally for me. If they aren't updated for a while, it means I'm not using them myself. As soon as I start using them again, they'll get updated.

My Projects

WhyNotWin11
Cisco FinesseGithubIRC UDFWindowEx UDF

 

Link to post
Share on other sites
Posted (edited)

I'm been unable to resolve the issue with anti-aliasing, the images are always going to be originally grayscale so I removed foreground_old, but I can't seem to get the aliased images to adjust. I tried defining all gray scale and adjusting the new color accordingly but that didn't work. I've also tried defining all transparency levels and the issue persists. I'm thinking it's an issue elsewhere as skipping the remap table causes the same issue.

#include <Array.au3>
#include <GDIPlus.au3>
#include <WinAPISysWin.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Example_1()

Func _SetBkIcon($ControlID, $iForeground, $iBackground, $sIcon, $iIndex, $iWidth, $iHeight)

    Local Static $STM_SETIMAGE = 0x0172
    Local $hBitmap, $hImage, $hIcon, $hBkIcon, $hGfx

    $hIcon = _WinAPI_ShellExtractIcon($sIcon, $iIndex, $iWidth, $iHeight)
    $hImage = _GDIPlus_BitmapCreateFromHICON($hIcon)

    If $iForeground <> 0 Then ; Skip Recoloring Foreground if Black
        Local Static $aRemapTable[65537][2] ; Cache Remap Table
        If Not $aRemapTable <> "" Then
            $aRemapTable[0][0] = 65536
            For $iTransparency = 0 To 255 Step 1
                For $iShade = 1 To 256 Step 1
                    $aRemapTable[(255 * $iTransparency) + $iShade][0] = "0x" & Hex($iTransparency) & Hex($iShade - 1, 2) & Hex($iShade - 1, 2) & Hex($iShade - 1, 2)
                    $aRemapTable[(255 * $iTransparency) + $iShade][1] = "0x" & Hex($iTransparency) & Hex(Int($iForeground * ((255 - ($iShade - 1)) / 255)), 6)
                Next
            Next
        EndIf
    EndIf

    Local $hImgAttr = _GDIPlus_ImageAttributesCreate()
    If $iForeground <> 0 Then _GDIPlus_ImageAttributesSetRemapTable($hImgAttr, $aRemapTable)
    Local $iW = _GDIPlus_ImageGetWidth($hImage), $iH = _GDIPlus_ImageGetHeight($hImage)
    $hBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH)
    $hGfx = _GDIPlus_ImageGetGraphicsContext($hBitmap)
    _GDIPlus_GraphicsClear($hGfx, $iBackground)
    _GDIPlus_GraphicsDrawImageRectRect($hGfx, $hImage, 0, 0, $iW, $iH, 0, 0, $iW, $iH, $hImgAttr)

    $hBkIcon = _GDIPlus_HICONCreateFromBitmap($hBitmap)
    GUICtrlSendMsg($ControlID, $STM_SETIMAGE, $IMAGE_ICON, _WinAPI_CopyIcon($hBkIcon))
    _WinAPI_RedrawWindow(GUICtrlGetHandle($ControlID))

    _WinAPI_DeleteObject($hIcon)
    _WinAPI_DeleteObject($hBkIcon)
    _GDIPlus_GraphicsDispose($hGfx)
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_ImageDispose($hBitmap)
    _GDIPlus_ImageAttributesDispose($hImgAttr)
    Return SetError(0, 0, 1)
EndFunc   ;==>_SetBkIcon

Func Example_1()
    Local $hGui = GUICreate("GDI+", 768, 256)
    GUISetState(@SW_SHOW)

    _GDIPlus_Startup()

    ; Original Icon
    GUICtrlCreateIcon(@ScriptDir & "\git.ico", -1, 0, 0, 256, 256)

    ; Only Recolor Background
    GUICtrlCreateIcon("", -1, 256, 0, 256, 256)
    _SetBkIcon(-1, 0x000000, 0xFFFFFFFF, @ScriptDir & "\git.ico", -1, 256, 256)

    ; Recolor Foreground and Background
    GUICtrlCreateIcon("", -1, 512, 0, 256, 256)
    _SetBkIcon(-1, 0xFF0000, 0xFF000000, @ScriptDir & "\git.ico", -1, 256, 256)

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    GUIDelete($hGui)
EndFunc   ;==>Example_1

 

Edited by rcmaehl

My UDFs are generally for me. If they aren't updated for a while, it means I'm not using them myself. As soon as I start using them again, they'll get updated.

My Projects

WhyNotWin11
Cisco FinesseGithubIRC UDFWindowEx UDF

 

Link to post
Share on other sites

I found another way to change the color without loosing proper anti-aliasing. For this solution GDI+ v1.1 (Win7+) is required.

#include <Array.au3>
#include <GDIPlus.au3>
#include <WinAPISysWin.au3>
#include <WinAPIShellEx.au3>
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>

Example_1()

Func _SetBkIcon($ControlID, $iForeground, $iBackground, $sIcon, $iIndex, $iWidth, $iHeight)

    Local Static $STM_SETIMAGE = 0x0172
    Local $hBitmap, $hGfx, $hImage, $hIcon, $hBkIcon

    $hIcon = _WinAPI_ShellExtractIcon($sIcon, $iIndex, $iWidth, $iHeight)
    $hImage = _GDIPlus_BitmapCreateFromHICON32($hIcon)

    Local $r = BitShift(BitAND($iForeground, 0xFF0000), 16), $g = BitShift(BitAND($iForeground, 0xFF00), 8), $b = BitAND($iForeground, 0xFF)
    Local $hEffect = _GDIPlus_EffectCreateColorCurve($GDIP_AdjustExposure, $GDIP_CurveChannelRed, $r) ;GDI+ v1.1 is needed -> Win7+
    _GDIPlus_BitmapApplyEffect($hImage, $hEffect)
    _GDIPlus_EffectDispose($hEffect)
    $hEffect = _GDIPlus_EffectCreateColorCurve($GDIP_AdjustExposure, $GDIP_CurveChannelGreen, $g)
    _GDIPlus_BitmapApplyEffect($hImage, $hEffect)
    _GDIPlus_EffectDispose($hEffect)
    $hEffect = _GDIPlus_EffectCreateColorCurve($GDIP_AdjustExposure, $GDIP_CurveChannelBlue, $b)
    _GDIPlus_BitmapApplyEffect($hImage, $hEffect)
    _GDIPlus_EffectDispose($hEffect)

    If $iBackground Then
        $hBitmap = _GDIPlus_BitmapCreateFromScan0($iWidth, $iHeight)
        $hGfx = _GDIPlus_ImageGetGraphicsContext($hBitmap)
        _GDIPlus_GraphicsClear($hGfx, $iBackground)
        _GDIPlus_GraphicsDrawImageRect($hGfx, $hImage, 0, 0, $iWidth, $iHeight)
        _GDIPlus_ImageDispose($hImage)
        $hImage = _GDIPlus_ImageClone($hBitmap)
        _GDIPlus_GraphicsDispose($hGfx)
        _GDIPlus_ImageDispose($hBitmap)
    EndIf

    $hBkIcon = _GDIPlus_HICONCreateFromBitmap($hImage)
    GUICtrlSendMsg($ControlID, $STM_SETIMAGE, $IMAGE_ICON, $hBkIcon)
    _WinAPI_RedrawWindow(GUICtrlGetHandle($ControlID))

    _WinAPI_DeleteObject($hIcon)
    _WinAPI_DeleteObject($hBkIcon)
    _GDIPlus_ImageDispose($hImage)
    Return SetError(0, 0, 1)
EndFunc   ;==>_SetBkIcon

Func Example_1()
    Local $hGui = GUICreate("GDI+", 512, 256)
    GUISetState(@SW_SHOW)

    If Not _GDIPlus_Startup() Or @extended < 6 Then
        MsgBox($MB_SYSTEMMODAL, "ERROR", "GDIPlus.dll v1.1 not available")
        Return
    EndIf

    ; Original Icon
    GUICtrlCreateIcon(@ScriptDir & "\git.ico", -1, 0, 0, 256, 256)

    ; Only Recolor Background
    GUICtrlCreateIcon("", -1, 256, 0, 256, 256)
    _SetBkIcon(-1, 0xFFABCDEF, 0xFFFFFF00, @ScriptDir & "\git.ico", -1, 256, 256)

    Do
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    GUIDelete($hGui)
EndFunc   ;==>Example_1

 

Works best with monochrome like images.

 

Btw, when you use 

$hImage = _GDIPlus_BitmapCreateFromHICON32($hIcon)

in your example it will work, too.

Edited by 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!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

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

    No registered users viewing this page.

×
×
  • Create New...