corgano

Converting image to a defined pallet of colors

7 posts in this topic

#1 ·  Posted

What I am trying to accomplish is to simplify a .PNG image and convert it to use only a specified pallet of colors. Eg if I have a pallet of 0xFF0000, 0x00FF00, 0x0000FF, it would change every pixel in the image to one of those three colors by whichever color is closest.

 

The simplest method I have tried is simply converting the image to a 16 bit bitmap, and that does simplify the pallet of the image, but I want to be able to specify what colors it specifies to. The other problem is the resulting saved .BMP file is much larger (file size) than the original, and I don't know if it's possible to make a 16 bit jpg.

 

It looks like _GDIPlus_BitmapConvertFormat is the solution but the help file doesn't go into how to use the pallet and I couldn't find an example that did what I needed. Can anyone help me?


0x616e2069646561206973206c696b652061206d616e20776974686f7574206120626f64792c20746f206669676874206f6e6520697320746f206e657665722077696e2e2e2e2e

Share this post


Link to post
Share on other sites



#2 ·  Posted (edited)

Something like this here?

 

GDI+ v1.1 required -> Windows 7+

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


Global $sFile = FileOpenDialog("Selecet an image", "", "Images (*.jpg;*.png;*.bmp)")
If @error Then Exit MsgBox(16, "Error", "You must select an image!", 30)

_GDIPlus_Startup()

$hImage = _GDIPlus_ImageLoadFromFile($sFile)
$iW = _GDIPlus_ImageGetWidth($hImage)
$iH = _GDIPlus_ImageGetHeight($hImage)
$hGUI = GUICreate("GDI+ v1.1 needed", $iW * 2 + 30, $iH + 20)
GUISetState()

$hDC = _WinAPI_GetDC($hGUI)

$hGraphics1 = _GDIPlus_GraphicsCreateFromHWND($hGUI)
_GDIPlus_GraphicsDrawImageRect($hGraphics1, $hImage, 10, 10, $iW, $iH)
_GDIPlus_GraphicsDispose($hGraphics1)
_WinAPI_ReleaseDC($hGUI, $hDC)

$iColors = 4

Local $tPalette = _GDIPlus_PaletteInitialize(256, $GDIP_PaletteTypeFixedHalftone256, $iColors, False, $hImage)
ConsoleWrite(@error & @CRLF)
$tPalette.ARGB((1)) = 0xFF0000FF
$tPalette.ARGB((2)) = 0xFF00FF00
$tPalette.ARGB((3)) = 0xFFFF0000
$tPalette.ARGB((4)) = 0xFF000000

_GDIPlus_BitmapConvertFormat($hImage, $GDIP_PXF04INDEXED, $GDIP_DitherTypeDualSpiral8x8, $GDIP_PaletteTypeFixedHalftone256, $tPalette)
DllCall($__g_hGDIPDll, "int", "GdipSetImagePalette", "handle", $hImage, "struct*", $tPalette)

$hDC = _WinAPI_GetDC($hGUI)
$hGraphics2 = _GDIPlus_GraphicsCreateFromHDC($hDC)

_GDIPlus_GraphicsDrawImageRect($hGraphics2, $hImage, $iW + 20, 10, $iW, $iH)

Do
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            _WinAPI_ReleaseDC($hGUI, $hDC)
            _GDIPlus_GraphicsDispose($hGraphics2)
            _GDIPlus_ImageDispose($hImage)
            _GDIPlus_Shutdown()
            GUIDelete()
            Exit
    EndSwitch
Until False

 

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

Share this post


Link to post
Share on other sites

#3 ·  Posted

I knew it would be UEZ to respond. You really are our local GDI expert.

That is really cool and i think we're on the right track, but I'm not looking to use various dots to make up the color, I want to make each area the solid color it's closest to in the pallet. For instance a4Dnd88.png turns into  CoGRTDb.png but the result I'm looking for is  COOWRLe.png

 


0x616e2069646561206973206c696b652061206d616e20776974686f7574206120626f64792c20746f206669676874206f6e6520697320746f206e657665722077696e2e2e2e2e

Share this post


Link to post
Share on other sites

#4 ·  Posted

Try this.

#include <Color.au3>
#include <GuiConstantsEx.au3>
#include <GDIPlus.au3>
#include <Misc.au3>
#include <ScreenCapture.au3>

Opt('MustDeclareVars', 1)

Global $width, $height, $hGUI1, $hGUI2, $hImage, $hImage2, $hGraphic1, $hGraphic2, $iX, $iY, $sImageIn

_Main()

Func _Main()
    ; Capture top left corner of the screen
    ;$sImageIn = "GDIPlus_Image.png"
    ;_ScreenCapture_Capture($sImageIn, 0, 0, 400, 400)
    ;Or

    ;$sImageIn = FileOpenDialog("First image", "", "All images (*.jpg;*.png;*.gif;*.bmp;)")
    ;If $sImageIn = "" Then Exit
    ;ConsoleWrite($sImageIn & @LF)
    ;Or

    InetGet("https://i.imgur.com/a4Dnd88.png", @ScriptDir & "\a4Dnd88.png")
    $sImageIn = @ScriptDir & "\a4Dnd88.png"
    ;or

    ;InetGet("http://img11.nnm.ru/a/1/5/b/0/1dba2209d5a133d9e84431a5587.jpg","FireWorksSydney.jpg")
    ;$sImageIn = "FireWorksSydney.jpg"

    ; Create a GUI for the original image
    $hGUI1 = GUICreate("Original", 400, 400, 0, 0)
    GUISetState()

    ; Create a GUI for the zoomed image
    $hGUI2 = GUICreate("Ctrl + Left click to save image", 400, 400, 0, 400)
    GUISetState()
    GUIRegisterMsg(0xF, "MY_PAINT")

    ; Initialize GDI+ library and load image
    _GDIPlus_Startup()
    $hImage = _GDIPlus_ImageLoadFromFile($sImageIn)
    $iX = _GDIPlus_ImageGetWidth($hImage)
    $iY = _GDIPlus_ImageGetHeight($hImage)
    ConsoleWrite($iX & " x " & $iY & @CRLF)

    ; Draw original image
    $hGraphic1 = _GDIPlus_GraphicsCreateFromHWND($hGUI1)
    _GDIPlus_GraphicsDrawImageRectRect($hGraphic1, $hImage, 0, 0, $iX, $iY, 0, 0, 400, 400)

    $hImage2 = _ImageColorFunc($hImage, "CF") ; <======= Use function CF() on each pixel.

    ; Draw returned image from _ImageColorFunc
    $hGraphic2 = _GDIPlus_GraphicsCreateFromHWND($hGUI2)
    _GDIPlus_GraphicsDrawImageRectRect($hGraphic2, $hImage2, 0, 0, $iX, $iY, 0, 0, 400, 400)

    ; Loop until user exits
    Do
        If _IsPressed("01") And _IsPressed("11") Then ; Left click + Ctrl key to save image.
            Do
            Until Not _IsPressed("01")
            Local $PathFile = FileSaveDialog("Choose a name.", @ScriptDir & "\", "Images (*.bmp;*.jpg;*.png;*.gif)|All files (*.*)", 16, "ColourModify.png")
            If Not @error Then
                _GDIPlus_ImageSaveToFile($hImage2, $PathFile)
                ;ShellExecute($PathFile)
            EndIf
        EndIf
        Sleep(10)
    Until GUIGetMsg() = $GUI_EVENT_CLOSE

    ; Release resources
    _GDIPlus_GraphicsDispose($hGraphic1)
    _GDIPlus_GraphicsDispose($hGraphic2)
    _GDIPlus_ImageDispose($hImage)
    _GDIPlus_ImageDispose($hImage2)
    _GDIPlus_Shutdown()
EndFunc   ;==>_Main

; ========= _ImageColorFunc($hImage, $ColorFunc) =====================
;Paramerers:-
; $hImage   - Handle to a Bitmap object
; $ColorFunc - A function that will be applied to every pixel in image.
;
Func _ImageColorFunc($hImage, $ColorFunc)
    Local $Reslt, $stride, $format, $Scan0, $iIW, $iIH, $hBitmap1
    Local $v_BufferA, $AllPixels, $sREResult1, $sResult

    $iIW = _GDIPlus_ImageGetWidth($hImage)
    $iIH = _GDIPlus_ImageGetHeight($hImage)
    ;ConsoleWrite($iIW & "  " & $iIH & @LF)
    $hBitmap1 = _GDIPlus_BitmapCloneArea($hImage, 0, 0, $iIW, $iIH, $GDIP_PXF32ARGB)

    ; Locks a portion of a bitmap for reading or writing
    $Reslt = _GDIPlus_BitmapLockBits($hBitmap1, 0, 0, $iIW, $iIH, BitOR($GDIP_ILMREAD, $GDIP_ILMWRITE), $GDIP_PXF32ARGB)

    ;Get the returned values of _GDIPlus_BitmapLockBits ()
    $width = DllStructGetData($Reslt, "width")
    $height = DllStructGetData($Reslt, "height")
    $stride = DllStructGetData($Reslt, "stride")
    $format = DllStructGetData($Reslt, "format")
    $Scan0 = DllStructGetData($Reslt, "Scan0")

    $v_BufferA = DllStructCreate("byte[" & $height * $width * 4 & "]", $Scan0) ; Create DLL structure for all pixels
    $AllPixels = DllStructGetData($v_BufferA, 1)
    ;ConsoleWrite("$AllPixels, raw data, first few colours = " & StringRegExpReplace($AllPixels, "(.{98})(.*)", "\1") & @CRLF)

    ; Searches on this string - $sREResult1 whch has the prefix "0x"  removed and a space put between pixels 8 characters long.
    $sREResult1 = StringRegExpReplace(StringTrimLeft($AllPixels, 2), "(.{8})", "\1 ")
    ;ConsoleWrite("$sREResult1 first few colours = " & StringRegExpReplace($sREResult1, "(.{81})(.*)", "\1") & @CRLF)

    ; Create an executable string with each pixel as a parameter of $ColorFunc function and remove all spaces.
    $sResult = StringStripWS(StringRegExpReplace($sREResult1, "(.{8}) ", $ColorFunc & '("\1")&'), 8)
    ;ConsoleWrite("$sREResult1 first few colours = " & StringRegExpReplace($sResult, "(.{81})(.*)", "\1") & @CRLF)

    ; Remove trailing "&", execute string, $sResult, and, add a leading, "0x.
    $sResult = "0x" & Execute(StringTrimRight($sResult, 1))
    ;ConsoleWrite("$sREResult1 first 9 colours = " & StringRegExpReplace($sResult, "(.{81})(.*)", "\1") & @CRLF)

    DllStructSetData($v_BufferA, 1, $sResult)
    _GDIPlus_BitmapUnlockBits($hBitmap1, $Reslt) ; Releases the locked region

    Return $hBitmap1
EndFunc   ;==>_ImageColorFunc

Func MY_PAINT($hWnd, $msg, $wParam, $lParam)
    If $hWnd = $hGUI1 Then _GDIPlus_GraphicsDrawImageRectRect($hGraphic1, $hImage, 0, 0, $iX, $iY, 0, 0, 400, 400) ; _GDIPlus_GraphicsDrawImageRect($hGraphic1, $hImage, 0, 0, $width, $height)
    If $hWnd = $hGUI2 Then _GDIPlus_GraphicsDrawImageRectRect($hGraphic2, $hImage2, 0, 0, $iX, $iY, 0, 0, 400, 400) ; _GDIPlus_GraphicsDrawImageRect($hGraphic2, $hImage2, 0, 0, $width, $height)
    Return $GUI_RUNDEFMSG
EndFunc   ;==>MY_PAINT

; The function used as a parameter in _ImageColorFunc() function.
Func CF($iColor)
    Local $aPalet = [0x0000FF, 0x00FF00, 0xFF0000, 0x000000], $iCol
    $iColor = "0x" & StringRegExpReplace($iColor, "(.{2})(.{2})(.{2})(.{2})", "$3$2$1") ; convert to AARRGGBB
    If _ColorGetRed($iColor) >= _ColorGetGreen($iColor) And _ColorGetRed($iColor) >= _ColorGetBlue($iColor) Then
        $iCol = $aPalet[0]
        If _ColorGetRed($iColor) <= 0x80 Then $iCol = $aPalet[3]
    EndIf
    If _ColorGetGreen($iColor) >= _ColorGetRed($iColor) And _ColorGetGreen($iColor) >= _ColorGetBlue($iColor) Then
        $iCol = $aPalet[1]
        If _ColorGetGreen($iColor) <= 0x80 Then $iCol = $aPalet[3]
    EndIf
    If _ColorGetBlue($iColor) >= _ColorGetRed($iColor) And _ColorGetBlue($iColor) >= _ColorGetGreen($iColor) Then
        $iCol = $aPalet[2]
        If _ColorGetBlue($iColor) <= 0x80 Then $iCol = $aPalet[3]
    EndIf
    Return Hex($iCol, 6) & "FF"
EndFunc   ;==>CF

 

Share this post


Link to post
Share on other sites

#5 ·  Posted

@corgano you have to be more precise what you want.

If you disable dithering in the code from post #2 you will get very similar results:

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


Global $sFile = FileOpenDialog("Selecet an image", "", "Images (*.jpg;*.png;*.bmp)")
If @error Then Exit MsgBox(16, "Error", "You must select an image!", 30)

_GDIPlus_Startup()

Global $hImage = _GDIPlus_ImageLoadFromFile($sFile)
Global $iW = _GDIPlus_ImageGetWidth($hImage)
Global $iH = _GDIPlus_ImageGetHeight($hImage)
Global $hGUI = GUICreate("GDI+ v1.1 needed", $iW * 2 + 30, $iH + 20)
GUISetState()


Global $hGraphics1 = _GDIPlus_GraphicsCreateFromHWND($hGUI)
_GDIPlus_GraphicsDrawImageRect($hGraphics1, $hImage, 10, 10, $iW, $iH)
_GDIPlus_GraphicsDispose($hGraphics1)

Global $iColors = 4

Global $tPalette = _GDIPlus_PaletteInitialize(256, $GDIP_PaletteTypeOptimal, $iColors, False, $hImage)
$tPalette.ARGB((1)) = 0xFF0000FF
$tPalette.ARGB((2)) = 0xFF00FF00
$tPalette.ARGB((3)) = 0xFFFF0000
$tPalette.ARGB((4)) = 0xFF000000

_GDIPlus_BitmapConvertFormat($hImage, $GDIP_PXF04INDEXED, $GDIP_DitherTypeSolid, $GDIP_PaletteTypeOptimal, $tPalette)
DllCall($__g_hGDIPDll, "int", "GdipSetImagePalette", "handle", $hImage, "struct*", $tPalette)

Global $hGraphics2 = _GDIPlus_GraphicsCreateFromHWND($hGUI)

_GDIPlus_GraphicsDrawImageRect($hGraphics2, $hImage, $iW + 20, 10, $iW, $iH)

Do
    Switch GUIGetMsg()
        Case $GUI_EVENT_CLOSE
            _GDIPlus_GraphicsDispose($hGraphics1)
            _GDIPlus_GraphicsDispose($hGraphics2)
            _GDIPlus_ImageDispose($hImage)
            _GDIPlus_Shutdown()
            GUIDelete()
            Exit
    EndSwitch
Until False

 

@Malkey nice to see you posting GDI+ stuff again. I learned so much from your posts! :)


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

#6 ·  Posted

How about  _GDIPlus_ImageAttributesSetRemapTable ?

Global $aRemapTable[5][2]
$aRemapTable[0][0] = 4
$aRemapTable[1][0] = 0xFFD3B4BA
$aRemapTable[1][1] = 0xFFFF0000
$aRemapTable[2][0] = 0xFFB5D7B0
$aRemapTable[2][1] = 0xFF00FF00
$aRemapTable[3][0] = 0xFFACAFDB
$aRemapTable[3][1] = 0xFF0000FF
$aRemapTable[4][0] = 0xFF646464
$aRemapTable[4][1] = 0xFF000000

 

Or simply fill the area

#include <GDIPlus.au3>

_GDIPlus_Startup ()

$hImage = _GDIPlus_ImageLoadFromFile("a4Dnd88.png")
$hBmp = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
_GDIPlus_ImageDispose($hImage)

$hCDC = _WinAPI_CreateCompatibleDC(0)
$hOldBmp = _WinAPI_SelectObject($hCDC, $hBmp)

$hBrush = _WinAPI_GetStockObject($DC_BRUSH)
$hOldBrush = _WinAPI_SelectObject($hCDC, $hBrush)

_WinAPI_SetDCBrushColor($hCDC, 0xFF0000)
_WinAPI_ExtFloodFill($hCDC, 0, 0, _WinAPI_GetPixel($hCDC, 0, 0), 1)
_WinAPI_SetDCBrushColor($hCDC, 0x00FF00)
_WinAPI_ExtFloodFill($hCDC, 100, 0, _WinAPI_GetPixel($hCDC, 100, 0), 1)
_WinAPI_SetDCBrushColor($hCDC, 0x0000FF)
_WinAPI_ExtFloodFill($hCDC, 0, 100, _WinAPI_GetPixel($hCDC, 0, 100), 1)
_WinAPI_SetDCBrushColor($hCDC, 0x000000)
_WinAPI_ExtFloodFill($hCDC, 100, 100, _WinAPI_GetPixel($hCDC, 100, 100), 1)

$hImage = _GDIPlus_BitmapCreateFromHBITMAP($hBmp)
_GDIPlus_ImageSaveToFile($hImage, "result.png")
_GDIPlus_ImageDispose($hImage)

_WinAPI_SelectObject($hCDC, $hOldBrush)
_WinAPI_DeleteObject($hBrush)
_WinAPI_SelectObject($hCDC, $hOldBmp)
_WinAPI_DeleteObject($hBmp)
_WinAPI_DeleteDC($hCDC)

_GDIPlus_ShutDown()

 

Share this post


Link to post
Share on other sites

#7 ·  Posted

@InnI the magic key word is here color quantization. Probably corgano's example is just a simple one for demonstration purposes.


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

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

  • Similar Content

    • SimTheNo1
      By SimTheNo1
      I am busy with building a solution for change monitoring of VOIP call program and to be properly automated means among others need for some limited OCR functionality. Current works of others are way too much overkill for this case what makes the need to build it myself. But to do it properly I significantly have to increase my knowledge about digital graphics management.
      For now I already have discovered here and there some mind blowing  Autoit miracles what can be achieved with Windows own possibilities to manipulate that what is output to the monitor. And as far I can judge there are 2 options to process graphics without use of any external libraries like ImageMagick, FreeImage and so on. These are:
       WinAPI  GDIPlus It is for me quite obvious to have various holes in general understanding of graphics and it is once more very clear what advantages gives consistent general study in 1 or another official institutions like University.  Cause there you are introduced into certain domain of knowledge in a way which usually has been perfected over long period of time. So you are not overloaded by stuff which requires a certain amount of information to be initially clear for you. For example, before starting to solve physics you first learn to read, count and so on and then move to subjects like physics.
      Though in my case opportunity to study in such educational system I had only for 8 years, from my 7th to 15th year of age in the country that was falling apart now Ukraine but used to be USSR, was all what it was. After have immigrated to Netherlands possibilities to study further haven't occur. And this therefor causes often various implications when going deep in that or another field of practical knowledge acquiring for any needed physical result, like programming to perform enormous amount of tasks. In this particular case automating VOIP call program.
      Anyway, right now I think the best direction to move is to concentrate on as basic as possible image management and if someone would maybe explain in general what is a pixel will definitely help. Particularly I am very curious about how to do picture manipulations in Autoit. Especially would help a lot to produce eventually following functions:
      createImage($imageFileName, $width, $height, $color) readImagePixel($imageFileName, $x, $y)  writeImagePixel($imageFileName, $x, $y) I do not know exactly how image is handled in computer but preferably above mentioned functions should deal with so far possible origin of graphics creation on computer. But nevertheless I definitely would love to hear any proposition for solution.
      The problem with explaining screenshots:
       VOIPConnect full Window  Part of Window with control to monitor for changes  Exact location of area where actual changes occur and have to be processed  It comes down to a rectangle of approximately 51 pixel wide and 7 pixel high. In fact if I get to learn as far as to be able exactly read, write and compare 2 images consistently across different computers I could narrow down then even further the area to watch as little as a square of 2-5 pixels wide. 
      To finish here is last detail about particularly no need for ultra fast solution at all. This because it is needed only once when it is first run on a new computer and to have to wait few minutes while it is being set instead of just a few seconds make no sence.
      This is it and what I too think to do beside this very particular case is to purify out beautiful generic Autoit functions for core image manipulation by using WinAPI or/and GDIPlus. 
    • timmalos
      By timmalos
      Hello all . i'm trying to build a tool to generate some pictures/graphs automatically based on some variables. I have completed the first part and I'm struggling now on what I thought would be the easiest part :
      In my tool there is a "preview" button, which helps to display on a new children GUI the generated picture. What I do in simplified :
      Global $g_hGraphics = _GDIPlus_GraphicsCreateFromHWND($g_hGUI) ;Then, I'll add multiple objects to this graphic, for example $Temp = _GDIPlus_ImageLoadFromFile($temp[1]) _GDIPlus_GraphicsDrawImage($g_hGraphics,$temp)  
      I'm then able to have a preview GUI showing all my elements perfectly. This works but now I want to be able to really generate it and have a ".png" file written to disk.
      What I tried without success:
      $g_hBitmap = _GDIPlus_BitmapCreateFromGraphics($previewWidth, $previewHeight, $g_hGraphics) _GDIPlus_ImageSaveToFile($g_hBitmap,$OUTPUTDIR & "\generated\test-"&@HOUR&@MIN&@SEC&".png") But what I write to file is a transparent PNG without anything.
      I'm pretty sure I'm missing something obvious there, I need your help
       
       
      NB : If this change your answer, my goal will be to generate transparent png, meaning I'll add multiple objects/pictures/texts on a transparent png background and if there are some "transparent holes" I need them to be transparent in the generated png.
       
      Thanks a lot for any help !
      Tim
    • timmalos
      By timmalos
      Hello all.
      I'm trying to do something simple, but I can't manage to find where to start since I can have multiple possibilities and not sure what is the best one :
      Taking screen capture (I can do that) On this capture, I'll look for 10 particular regions that will give me 10 images of 40px * 40px (I can do that) Then what I want is for each of these regions, find on my library of ~140 images which one is the closest one to my region (= the match)  
      What do you think is the best approach ? Ican easily do step 1 and 2, however for the third step :
      I can't use memcmp because they won't be exactly the same (original library can differ a very little bit from what I'll get from my screen capture) There is no text, only drawing Should I do a pixelSum of what I get on screen and do the same via GDIPlus for each of my images in the library?  
      Thanks a lot,
      Tim
       
    • GtaSpider
      By GtaSpider
      Hey everyone,
      When I try to add blur to an Image with _GDIPlus_EffectCreateBlur and _GDIPlus_BitmapApplyEffect the blur is only horizontal, not horizontal and vertical, if the blur radius is <= 20. 
      Easy to test with the Image I attached and the default example from the helpfile of 
      _GDIPlus_BitmapApplyEffect The second Image I attached is the result and as you can see the cross is only blurred in one direction.  Does anyone have a clue why this happens and furthermore a solution for this problem? (I could just rotate the picture but then it would be twice as time consuming...)
      Thanks in advance,
      Spider
      p.s.: Running Win 10 Pro with all updates


    • timmy2
      By timmy2
      The following demo script, drawn largely from examples by @UEZ, lets me display colored text over a transparent background using my choice of font, type size, etc.  It is a single function (out of many) in the project I'm scripting, but it is crucial to my goal of displaying messages in an interesting way (IMHO).  
      (For the purpose of this forum post it displays a single string variable; my actual project script reads a number of  possible messages from a text file into an array and then chooses the messages to be displayed based on stuff that happens in the course of someone using my program.)
      Note that I am seeking help only to overcome my inability to figure out how to display this text atop a PNG graphic, and make the result a draggable object.
       
      #cs Display a message one character at a time with the goal of adding a PNG graphic overlay #ce ;~ #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 #include <GDIPlus.au3> #include <GUIConstantsEx.au3> #include <GUIConstants.au3> Global $sMessage Global $iString = 0 Global $aInfo Global Const $SC_DRAGMOVE = 0xF012 HotKeySet("{Esc}","Close") ; in my actual script I read in a file containing all the text to be displayed Global $sText = _ "Lorem ipsum dolor sit amet, consectetur" & @CRLF & _ "elit, sed do eiusmod tempor incididunt ut" & @CRLF & _ @CRLF & _ "Et dolore magna aliqua. Ut enim ad minim," & @CRLF & _ "quis nostrud exercitation ullamco laboris nisi" & @CRLF & _ "aliquip ex ea commodo consequat." & @CRLF & _ @CRLF & _ "Duis aute irure dolor in reprehenderit in" & @CRLF & _ "voluptate velit esse cillum dolore eu fugiat" & @CRLF & _ "nulla pariatur." & @CRLF ; Create GUI Global $hGUI = GUICreate("", 450, 340, -1, -1, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOPMOST)) GUISetBkColor(0xFF000000) ; will change background color, which UEZ doesn't use in his example, but then he doesn't want a BG GUISetState(@SW_SHOW) ; Draw a string _GDIPlus_Startup() Global $hBrush = _GDIPlus_BrushCreateSolid(0xFF99ff33) Global $hFormat = _GDIPlus_StringFormatCreate() Global $hFamily = _GDIPlus_FontFamilyCreate("VT323") Global $hFont = _GDIPlus_FontCreate($hFamily, 14, 0) Global $tLayout = _GDIPlus_RectFCreate(20, 20, 0, 0) Global $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI) Global $hBitmap = _GDIPlus_BitmapCreateFromScan0(420, 300) Global $hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap) _GDIPlus_GraphicsSetTextRenderingHint($hBuffer, 4) _GDIPlus_GraphicsSetSmoothingMode($hBuffer, 4) GUIRegisterMsg($WM_LBUTTONDOWN, "_WM_LBUTTONDOWN") For $iString = 1 to StringLen($sText) $sMessage = StringLeft($sText,$iString) $aInfo = _GDIPlus_GraphicsMeasureString($hGraphic, $sMessage, $hFont, $tLayout, $hFormat) _GDIPlus_GraphicsClear($hBuffer, 0x000000) _GDIPlus_GraphicsDrawStringEx($hBuffer, $sMessage, $hFont, $aInfo[0], $hFormat, $hBrush) $hHBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap) _WinAPI_BitmapDisplayTransparentInGUI($hHBitmap, $hGUI) _WinAPI_DeleteObject($hHBitmap) Next Do Until GUIGetMsg() = $GUI_EVENT_CLOSE Close() ; Clean up resources (my best guess at what needs closing/shutting down) Func Close() _GDIPlus_FontDispose($hFont) _GDIPlus_FontFamilyDispose($hFamily) _GDIPlus_StringFormatDispose($hFormat) _GDIPlus_BrushDispose($hBrush) _GDIPlus_GraphicsDispose($hGraphic) _GDIPlus_ImageDispose($hBitmap) _GDIPlus_Shutdown() Exit EndFunc ;==>Close Func _WinAPI_BitmapDisplayTransparentInGUI(ByRef $hHBitmap, ByRef $hGUI, $iOpacity = 0xFF, $bReleaseGDI = True) If Not BitAND(GUIGetStyle($hGUI)[1], $WS_EX_LAYERED) = $WS_EX_LAYERED Then Return SetError(1, 0, 0) Local $tDim = DllStructCreate($tagBITMAP) If Not _WinAPI_GetObject($hHBitmap, DllStructGetSize($tDim), DllStructGetPtr($tDim)) Then Return SetError(2, 0, 0) Local $tSize = DllStructCreate($tagSIZE), $tSource = DllStructCreate($tagPOINT), $tBlend = DllStructCreate($tagBLENDFUNCTION) Local Const $hScrDC = _WinAPI_GetDC(0), $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC), $hOld = _WinAPI_SelectObject($hMemDC, $hHBitmap) $tSize.X = $tDim.bmWidth $tSize.Y = $tDim.bmHeight $tBlend.Alpha = $iOpacity $tBlend.Format = 1 _WinAPI_UpdateLayeredWindow($hGUI, $hScrDC, 0, DllStructGetPtr($tSize), $hMemDC, DllStructGetPtr($tSource), 0, DllStructGetPtr($tBlend), $ULW_ALPHA) _WinAPI_ReleaseDC(0, $hScrDC) _WinAPI_SelectObject($hMemDC, $hOld) _WinAPI_DeleteDC($hMemDC) If $bReleaseGDI Then _WinAPI_DeleteObject($hHBitmap) Return True EndFunc Func _WM_LBUTTONDOWN($hWnd, $iMsg, $wParam, $lParam) Switch $hWnd Case $hGUI _SendMessage($hWnd, $WM_SYSCOMMAND, $SC_DRAGMOVE, 0) EndSwitch EndFunc ;==>_WM_LBUTTONDOWN  
      The above script lets me push the text out to the screen, but I want to display the text atop a PNG graphic, which is an imagined piece of hand-held test equipment that I'll refer to here as the Veeblefetzer.
      Here's a half-size animated gif of the desired result.

       
      Note that initially I used a different script to stream the text out atop a built-in black background rectangle.  I figured I would place the Veeblefetzer graphic above the text -- with the center "display area" of the Veeblefetzer cut out (transparent).  
      Then UEZ's example script came along. The benefit of using text that has a transparent BG, and placing it in a layer above the Veeblefetzer, is this lets me include reflections in the Veeblefetzer's display.
      Now to the challenges I'm ill-equipped to figure out.  
      The function I use to display the Veeblefetzer is below. It, too, is drawn from some of examples by @UEZ.
      There are actually two functions below, one I use to display the PNG graphic, and one that can be used to fade the graphic in or out.  I include it here only because it might be applicable to my ancillary goal of needing to clear the text on the handheld display before streaming the next message onto the Veeblefetzer's display.  I realize that the fade in/out function is for graphic objects, so it may not be applicable to text. 
       
      ; Display the image at center unless otherwise specified. If Fader will not be used then $iAlpha's default of 0xFF is good. ; If Fader will be used then start with $iAlpha equal to zero. Option to make this image "Always on top" is also available. Func DisplayImage($sFile, $iPosX = -1, $iPosY = -1, $iAlpha = 0xFF, $bTopmost = True) Local Const $hBmp_Background = _GDIPlus_BitmapCreateFromFile($sFile) ;load the image If @error Then Return SetError(1, 0, 0) ;image cannot be loaded Local Const $iW = _GDIPlus_ImageGetWidth($hBmp_Background), $iH = _GDIPlus_ImageGetHeight($hBmp_Background) ;get the dimension of the background image Local Const $hGUI = GUICreate("", $iW, $iH, $iPosX, $iPosY, $WS_POPUP, BitOR($WS_EX_LAYERED, $WS_EX_TOPMOST * $bTopmost, $WS_EX_TOOLWINDOW)) ;create GUI with appropriate styles and extented style (borderless transparent GUI) GUICtrlCreateLabel("", 0, 0, $iW, $iH, Default, $GUI_WS_EX_PARENTDRAG) ;create a hidden label for GUI dragging GUISetState(@SW_SHOW, $hGUI) ;show GUI Local Const $hBitmap = _GDIPlus_BitmapCreateFromScan0($iW, $iH) ;define an empty bitmap where all the gfx stuff will copied to Local Const $hGfx = _GDIPlus_ImageGetGraphicsContext($hBitmap) ;get the context to the bitmap to be able to copy / draw to the bitmap _GDIPlus_GraphicsDrawImageRect($hGfx, $hBmp_Background, 0, 0, $iW, $iH) ;draw background image to the empty bitmap ;display GDI+ with transparency on desktop Local Const $hHBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap) ;convert GDI+ image to GDI to display it on the screen using GDI functions Local Const $hScrDC = _WinAPI_GetDC($hGUI) ;get the device context (dc) handle of the GUI Local Const $hMemDC = _WinAPI_CreateCompatibleDC($hScrDC) ;create a compatible dc handle Local Const $hOld = _WinAPI_SelectObject($hMemDC, $hHBitmap) ;selects the GDI bitmap object into the specified device context Local Const $tSize = DllStructCreate($tagSIZE) ;create a $tagSIZE struct (x = width, y = height) DllStructSetData($tSize, "X", $iW) ;set data for width DllStructSetData($tSize, "Y", $iH) ;set data for height Local $tSource = DllStructCreate($tagPOINT) ;create a $tagPOINT struct (x = x position, y = y position) Local $tBlend = DllStructCreate($tagBLENDFUNCTION) ;create $tagBLENDFUNCTION struct -> see help file for more info DllStructSetData($tBlend, "Alpha", $iAlpha) ;set the alpha channel of the GUI -> 255 = opaque, 0 = transparent DllStructSetData($tBlend, "Format", 1) ;set the format to 1 -> bitmap has alpha channels DllCall("user32.dll", "bool", "UpdateLayeredWindow", "hwnd", $hGUI, "handle", $hScrDC, "ptr", 0, "struct*", $tSize, "handle", $hMemDC, "struct*", $tSource, "dword", 0, "struct*", $tBlend, "dword", $ULW_ALPHA) ;display bitmap on screen ;release resources otherwise memory will filled up (memory leak) _GDIPlus_GraphicsDispose($hGfx) _GDIPlus_BitmapDispose($hBitmap) Local $aResource[7] = [$hGUI, _ ;GUI handle $hScrDC, _ ;device context (dc) handle of the GUI (GDI) $hHBitmap, _ ;GDI bitmap handle of the displayed image $hMemDC, _ ;compatible dc handle from $hScrDC $tBlend, _ ;tBlend struct (source blend oper., flags, alpha value, format) -> see help file for description $tSize, _ ;tSize struct (width and height of the window / image) $tSource] ;tSource struct for x / y position of the GUI Return $aResource ;returns the handles to release it later or to use it for fading effects EndFunc ;==>DisplayImage ; This function releases the resources of a specific image and closes it Func ReleaseResources(ByRef $aResource) If Not IsArray($aResource) Then Return SetError(1, 0, 0) If UBound($aResource) <> 7 Then Return SetError(2, 0, 0) _WinAPI_ReleaseDC($aResource[0], $aResource[1]) _WinAPI_DeleteDC($aResource[3]) _WinAPI_DeleteObject($aResource[2]) GUIDelete($aResource[0]) EndFunc ;==>ReleaseResources ; To fade in an image leave $bIn at default (True); to fade out specify False. ; There are also options to set the ending transparency level (0 transparent, 255 opaque), speed and delay. ; If you change the speed or delay be sure to specify $bIn and $iTrans, otherwise you'll wonder why the fade-in or out isn't what you hoped for. ; The default values are specified in the function below. Func _Fader($res1, $bIn = True, $iTrans = 255, $speed = 3, $delay = 10 ) If Not IsArray($res1) Then Return SetError(1, 0, 0) If UBound($res1) <> 7 Then Return SetError(2, 0, 0) Switch $bIn Case True For $a = 0 To $iTrans Step $speed DllStructSetData($res1[4], "Alpha", $a) DllCall("user32.dll", "bool", "UpdateLayeredWindow", "hwnd", $res1[0], "handle", $res1[1], "ptr", 0, "struct*", $res1[5], "handle", $res1[3], "struct*", $res1[6], "dword", 0, "struct*", $res1[4], "dword", $ULW_ALPHA) ;display bitmap on screen Sleep($delay) Next Case Else For $a = $iTrans To 0 Step -$speed DllStructSetData($res1[4], "Alpha", $a) DllCall("user32.dll", "bool", "UpdateLayeredWindow", "hwnd", $res1[0], "handle", $res1[1], "ptr", 0, "struct*", $res1[5], "handle", $res1[3], "struct*", $res1[6], "dword", 0, "struct*", $res1[4], "dword", $ULW_ALPHA) ;display bitmap on screen Sleep($delay) Next EndSwitch EndFunc ;==>_FadeIn  
      Is anyone willing to step in and show me how to use the text display function along with the PNG display function, so that the resulting Veeblefetzer will be a single draggable object on screen?
      I'm equally baffled about how to properly clear off one message before displaying the next.
      The reason?   I have only the most rudimentary grasp of the commands within -- or the relationships between -- what seem like three distinct sections common to each of UEZ's scripts:
      Initialization (which defines a number of attributes, some of which are obvious to me, for text in this case)
      _GDIPlus_Startup() Global $hBrush = _GDIPlus_BrushCreateSolid(0xFF99ff33) Global $hFormat = _GDIPlus_StringFormatCreate() Global $hFamily = _GDIPlus_FontFamilyCreate("VT323") Global $hFont = _GDIPlus_FontCreate($hFamily, 14, 0) Global $tLayout = _GDIPlus_RectFCreate(20, 20, 0, 0) Global $hGraphic = _GDIPlus_GraphicsCreateFromHWND($hGUI) Global $hBitmap = _GDIPlus_BitmapCreateFromScan0(420, 300) Global $hBuffer = _GDIPlus_ImageGetGraphicsContext($hBitmap) _GDIPlus_GraphicsSetTextRenderingHint($hBuffer, 4) _GDIPlus_GraphicsSetSmoothingMode($hBuffer, 4)  
      Delivery... (apparently what's needed to display the text)
      $aInfo = _GDIPlus_GraphicsMeasureString($hGraphic, $sMessage, $hFont, $tLayout, $hFormat) _GDIPlus_GraphicsClear($hBuffer, 0x000000) _GDIPlus_GraphicsDrawStringEx($hBuffer, $sMessage, $hFont, $aInfo[0], $hFormat, $hBrush) $hHBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hBitmap) _WinAPI_BitmapDisplayTransparentInGUI($hHBitmap, $hGUI) _WinAPI_DeleteObject($hHBitmap) Even at my primitive level of understanding I have figured out that you can reuse the above "delivery" section to display other strings before proceeding to the next section.
       
      Cleanup... (apparently needed to release resources, which to me is like closing a previously opened file: it's part of the process so I'll do it):
      _GDIPlus_FontDispose($hFont) _GDIPlus_FontFamilyDispose($hFamily) _GDIPlus_StringFormatDispose($hFormat) _GDIPlus_BrushDispose($hBrush) _GDIPlus_GraphicsDispose($hGraphic) _GDIPlus_ImageDispose($hBitmap) _GDIPlus_Shutdown()  
      (You're getting an inside look at a monkey brain at work here!)
      UEZ's script to display the PNG graphic follows a similar pattern. What I cannot figure out is how to merge the two scripts, while avoiding redundancy and conflicts. To clear the text between messages must I release any resources, and if so: which ones? AND, how do I group the PNG graphic with the text so the resulting "object" can be moved around on the screen?