Jump to content

Sky Slate Blueberry Blackcurrant Watermelon Strawberry Orange Banana Apple Emerald Chocolate
Photo

GDI+ standard library 1.45 by tic


  • Please log in to reply
1385 replies to this topic
toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
Hi tic,

I have read binary data from a file. Internally I store it in a var in Hex format.
When I write this data to a binary file with BinWrite() by Laszlo, the file is ok and from there I can use your GDI+ Lib to CreateBitmapFromFile(). But for this I need this temporary binary file. How can I create a Bitmap directly from the hex or binary data stored in the variable of my script? Is it possible?

Thanks a lot for your advice.
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

Smurth
  • Members
  • 120 posts
  • Last active: Feb 23 2014 09:58 PM
  • Joined: 13 Dec 2006
Hi all GDI+ lovers,

I've just discovered an easy way to add blur behind a layered window (Vista/Win7):
VarSetCapacity(struct, 3, 0)
NumPut(1,struct,0,"UInt")
NumPut(0x1,struct,1,"UInt")
DllCall("dwmapi.dll\DwmEnableBlurBehindWindow", "UInt", hwnd,"UInt",&struct)

Unfortunately, it doesn't work - in my case, at least - for the first created window. The following windows have blur...

tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
Yep.....no problem sir toralf. Just look at Gdip_BitmapFromBRA, or more specifically the end of it:

hData := DllCall("GlobalAlloc", "UInt", 2, "UInt", FileInfo2)
	pData := DllCall("GlobalLock", "UInt", hData)
	DllCall("RtlMoveMemory", "UInt", pData, "UInt", &BRAFromMemIn+Info2+FileInfo1, "UInt", FileInfo2)
	DllCall("GlobalUnlock", "UInt", hData)
	DllCall("ole32\CreateStreamOnHGlobal", "UInt", hData, "Int", 1, "UInt*", pStream)
	DllCall("gdiplus\GdipCreateBitmapFromStream", "UInt", pStream, "UInt*", pBitmap)
	DllCall(NumGet(NumGet(1*pStream)+8), "UInt", pStream)
	return pBitmap

where FileInfo2 is the size in bytes of the image and &BRAFromMemIn+Info2+FileInfo1 is the address in memory of the beginning of the image.

You might want to take a look at the as of yet unreleased BRA library a couple of posts back. I am really sorry about this anyone that cares! I am overworked! I will try and release it properly soon

toralf
  • Moderators
  • 4035 posts
  • Last active: Aug 20 2014 04:23 PM
  • Joined: 31 Jan 2005
Thanks tic,

But I couldn't get it working. Hence, I posted in the Ask for help section: <!-- w -->www.autohotkey.com/forum/topic54465.html<!-- w -->
Ciao
toralf
 
I use the latest AHK version (1.1.15+)
Please ask questions in forum on ahkscript.org. Why?
For online reference please use these Docs.

  • Guests
  • Last active:
  • Joined: --
How can be defined a point of rotation?
See this hand clock:

Posted Image

In this script, the rotating point is in the middle of png, so I must chage it somewhere else ofcourse.
How :?: I can't get it work...


#SingleInstance, Force
#NoEnv
SetBatchLines, -1

; Create a layered window (+E0x80000 : must be used for UpdateLayeredWindow to work!) that is always on top (+AlwaysOnTop), has no taskbar entry or caption
; This will be used as the 2nd gui so that we can show our image on it
Gui, 2: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
Gui, 2: Show, NA
hwnd2 := WinExist()
OnMessage(0x201, "WM_LBUTTONDOWN")

;_______________________________________________________________________________________________



#Include, Gdip.ahk

If !pToken := Gdip_Startup()
{
	MsgBox, 48, gdiplus error!, Gdiplus failed to start. Please ensure you have gdiplus on your system
	ExitApp
}

if !FileExist("hand.png")
	UrlDownloadToFile, http://img11.imageshack.us/img11/5531/image9yq.png, hand.png

file := A_ScriptDir . "\hand.png"

Loop,1000
{
	Angle:=A_Index
	Gosub go
}
return




;___________________________GDI+ important stuff__________________________________________________
Return

;#####################################################################

Go:

; If the file in the edit field is not a valid image then return
If !pBitmap := Gdip_CreateBitmapFromFile(File)
Return

; We should get the width and height of the image, in case it is too big for the screen then we can resize it to fit nicely
OriginalWidth := Gdip_GetImageWidth(pBitmap), OriginalHeight := Gdip_GetImageHeight(pBitmap)
Ratio := OriginalWidth/OriginalHeight

; If the image has a width larger than 1/2 of the width of the screen or height larger than 1/2 the screen, then we will resize it to be half of the screen
If (OriginalWidth >= A_ScreenWidth//2) || (OriginalHeight >= A_ScreenHeight//2)
{
	If (OriginalWidth >= OriginalHeight)
	Width := A_ScreenWidth//2, Height := Width*(1/Ratio)
	Else
	Height := A_ScreenHeight//2, Width := Height*Ratio
}
Else
Width := OriginalWidth, Height := OriginalHeight

; Width and Height now contain the new dimensions the image on screen will be

; When rotating a square image, then the bitmap or canvas will need to be bigger as you can imagine that once rotated then a triangle will be wider than a square
; We need to know the new dimensions of the image
; With Gdip_GetRotatedDimensions we can plug in the width and height of the image, and the angle it is to be rotated by
; The last 2 parameters are the variables in which tio store the new width and height of the rotated image
; RWidth and RHeight now contain the dimensions of the rotated image
Gdip_GetRotatedDimensions(Width, Height, Angle, RWidth, RHeight)

; We rotate an image about the top left corner of the image, however this will result in the image moving off the canvas
; We can use Gdip_GetRotatedTranslation to find how much the image should be 'shifted' by in the x and y coordinates in order for it to be back on the canvas
; As with the above function, we plug in the width, height and angle to rotate by
; The function will then make the last 2 parameters the x and y translation (this is the distance in pixels the image must be shifted by)
; xTranslation and yTranslation now contain the distance to shift the image by
Gdip_GetRotatedTranslation(Width, Height, Angle, xTranslation, yTranslation)

; We will now create a gdi bitmap to display the rotated image on the screen (as mentioned previously we must use a gdi bitmap to display things on the screen)
hbm := CreateDIBSection(RWidth, RHeight)

; Get a device context compatible with the screen
hdc := CreateCompatibleDC()

; Select the bitmap into the device context
obm := SelectObject(hdc, hbm)

; Get a pointer to the graphics of the bitmap, for use with drawing functions,
; and set the InterpolationMode to HighQualityBicubic = 7 so that when resizing the image still looks good
G := Gdip_GraphicsFromHDC(hdc), Gdip_SetInterpolationMode(G, 7)

; We can now shift our graphics or 'canvas' using the values found with Gdip_GetRotatedTranslation so that the image will be drawn on the canvas
Gdip_TranslateWorldTransform(G, xTranslation, yTranslation)

; We can also rotate the graphics by the angle we desire
Gdip_RotateWorldTransform(G, Angle)

; If we wish to flip the image horizontally, then we supply Gdip_ScaleWorldTransform(G, x, y) with a negative x transform
; We multiply the image by the x and y transform. So multiplying a direction by -1 will flip it in that direction and 1 will do nothing
; We must then shift the graphics again to ensure the image will be within the 'canvas'
; You can see that if we wish to flip vertically we supply a negative y transform
If Horizontal
Gdip_ScaleWorldTransform(G, -1, 1), Gdip_TranslateWorldTransform(G, -Width, 0)
If Vertical
Gdip_ScaleWorldTransform(G, 1, -1), Gdip_TranslateWorldTransform(G, 0, -Height)


; As you will already know....we must draw the image onto the graphics. We want to draw from the top left coordinates of the image (0, 0) to the top left of the graphics (0, 0)
; We are drawing from the orginal image size to the new size (this may not be different if the image was not larger than half the screen)
Gdip_DrawImage(G, pBitmap, 0, 0, Width, Height, 0, 0, OriginalWidth, OriginalHeight)

; Even though this is not necessary in this scenario, you should always reset the transforms set on the graphics. This will remove any of the rotations
Gdip_ResetWorldTransform(G)

; We will update the hwnd  with the hdc of our gdi bitmap. We are drawing it at the new width and height and in the centre of the screen
UpdateLayeredWindow(hwnd2, hdc, (A_ScreenWidth-RWidth)//2, (A_ScreenHeight-RHeight)//2, RWidth, RHeight)

; As always we will dispose of everything we created
; So we select the object back into the hdc, the delete the bitmap and hdc
SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
; We will then dispose of the graphics and bitmap we created
Gdip_DeleteGraphics(G), Gdip_DisposeImage(pBitmap)
Return

;#####################################################################
WM_LBUTTONDOWN(){
	If (A_Gui = 2)
	PostMessage, 0xA1, 2
}
;#####################################################################

Esc::
GuiClose:
Exit:
Gdip_Shutdown(pToken)
ExitApp
Return


  • Guests
  • Last active:
  • Joined: --
I know this can be done by changing x and y position of UpdateLayeredWindow (x:=(A_ScreenWidth-RWidth)//2 and y:= (A_ScreenHeight-RHeight)//2), but I dont know the formula. Please, help if you can..

In this line:

; We will update the hwnd with the hdc of our gdi bitmap. We are drawing it at the new width and height and in the centre of the screen
UpdateLayeredWindow(hwnd2, hdc,(A_ScreenWidth-RWidth)//2, (A_ScreenHeight-RHeight)//2, RWidth, RHeight)



  • Guests
  • Last active:
  • Joined: --
Or, more complicated:

"GDI+ uses matrix transformations for that purpose, so if you want to rotate around a point other than (0,0), that is a combination of a translation and a rotation (http://en.wikipedia....ormation_matrix), i.e.

graphics.TranslateTransform( 0 - (rc.right - rc.left)/2 , 0 - (rc.bottom - rc.top)/2, MatrixOrderPrepend);

graphics.RotateTransform(some_angle,MatrixOrderPrepend);

And it is the nature of a matrix transformation that it is applied to the whole image, so if you only want to use a part of a DC, you have to "cut" that part for this purpose.
"



Razer
  • Members
  • 10 posts
  • Last active: Mar 11 2010 01:59 PM
  • Joined: 15 Feb 2010
Hi Tic,

Thx for your great library, it was really helpful to me.

I have a question about performance and optimization.

Let's say I have a static graphic ( a png picture for example ) and some dynamic text to show above the picture.
So there are two layers and if I'm not wrong, I can't show a classic gui window above a gdip window.

I have tried two ways :

1/ One single gui window with the two layers updated each time the text needs to be changed.

2/ Two gui windows, one with the png background and one with the text.

Both solution are working great with a simple example like this, but when you add more dynamic layers I think it will be better to use the fastest one.

How would you do this ?

Razer

Zaelia
  • Members
  • 754 posts
  • Last active: Jan 17 2015 02:38 AM
  • Joined: 31 Oct 2008
I use the second method, how do you first method?

About method #2, I use GUI +owner and transparency, but we are limited to 1 main gui and 49 gui layers, CPU consommation is good but if you are on vista it's dwm.exe that use CPU not your script , and guicontrol, move, mycontrol is very fast without redraw...

tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
Hi Razer

Im honestly very glad the library has been helpful to you

If you are able, would you be able to post what code you have or an example of what you are currently doing and I will try and get some time and show you how I would do it

Guest

You should really sign up if you havent already

Ok, you are exactly right. the easiest way to achive this would be to create a modified Gdip_GetRotatedTranslation but have the byref outputs as xPos and yPos to be used with UpdateLayeredWindow, instead of the amount to shift by. I will try and get round to this as well, but you might wanna give the whole trig thing a bash yourself with that, or if anyone else can help out, that would be great!

Razer
  • Members
  • 10 posts
  • Last active: Mar 11 2010 01:59 PM
  • Joined: 15 Feb 2010
Thx Tic,

Here is an example of the 2nd method :

; --------------------------------------------------------------------------------------------------
; GENERAL SETTINGS
; --------------------------------------------------------------------------------------------------
#noenv
setbatchlines -1
listlines Off

#singleinstance force
#include Gdip.ahk

settimer UpdateLabel, 200

onexit ExitLabel


; --------------------------------------------------------------------------------------------------
; DEFINITIONS
; --------------------------------------------------------------------------------------------------
GUI_BG := 10
GUI_FG := 20

WIN_BG := "WIN_BACKGROUND"
WIN_FG := "WIN_FOREGROUND"

WIN_ID_BG := "Background"
WIN_ID_FG := "Foreground"

skin_file = %A_ScriptDir%\Skin.png


initialize()

; --------------------------------------------------------------------------------------------------
; FONCTIONS
; --------------------------------------------------------------------------------------------------
initialize()
{
	global
	local retour := 0
	
	If !pToken := Gdip_Startup()
	{
		msgbox 48, initialize : Gdiplus failed to start. Please ensure you have gdiplus on your system
		ExitApp
	}
	
	init_background()
	init_foreground()
	
	return retour
}

init_background()
{
	global
	local retour := 0
	
	; Create a layered window (+E0x80000 : must be used for UpdateLayeredWindow to work!) that is always on top (+AlwaysOnTop), has no taskbar entry or caption
	gui %GUI_BG%: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs

	; Show the window
	gui %GUI_BG%:show, NA, %WIN_BG%
	
	; Get a handle to this window we have created in order to update it later
	WIN_ID_BG := WinExist()
	
	update_layer( WIN_ID_BG )
	
	return retour
}

init_foreground()
{
	global
	local retour := 0
	
	; Create a layered window (+E0x80000 : must be used for UpdateLayeredWindow to work!) that is always on top (+AlwaysOnTop), has no taskbar entry or caption
	gui %GUI_FG%: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs

	; Show the window
	gui %GUI_FG%:show, NA, %WIN_FG%

	; Get a handle to this window we have created in order to update it later
	WIN_ID_FG := WinExist()
	
	update_layer( WIN_ID_FG )
	
	return retour
}

update_layer( layer_name )
{
	global
	local retour := 0
	
	font := ""
	options = ""
	current_time := ""
	
	; ---------------------------------
	; Loading of the background picture
	; ---------------------------------
	if ( layer_name == WIN_ID_BG )
	{
		p_bitmap := Gdip_CreateBitmapFromFile( skin_file )

		If !p_bitmap
		{
			MsgBox, 48, update_layer : Could not load the image <%skin_file%>
			ExitApp
		}
	}
	
	; ----------------------------------
	; Creation of the graphic ressources
	; ----------------------------------
	hbm := CreateDIBSection(300, 150)
	hdc := CreateCompatibleDC()
	obm := SelectObject(hdc, hbm)
	p_graphic := Gdip_GraphicsFromHDC(hdc)
	Gdip_SetInterpolationMode(p_graphic, 7)
	
	; -------------------------------
	; Update of the background window
	; -------------------------------
	if ( layer_name == WIN_ID_BG )
	{
		Gdip_DrawImage(p_graphic, p_bitmap, 0, 0, 300, 150, 0, 0, 300, 150)
		UpdateLayeredWindow(WIN_ID_BG, hdc, 0, 0, 300, 150)
	}
	
	; -------------------------------
	; Update of the foreground window
	; -------------------------------
	if ( layer_name == WIN_ID_FG )
	{
		font := "Arial"
		options = x0 y75 w300 h0 cFF000000 Center Normal r4 s18
		
		formattime current_time,, HH:mm:ss
		
		Gdip_TextToGraphics(p_graphic, current_time, options, font)
		
		UpdateLayeredWindow(WIN_ID_FG, hdc, 0, 0, 300, 150)
	}
	
	; -------------------------
	; Delete graphic ressources
	; -------------------------
	SelectObject(hdc, obm)
	DeleteObject(hbm)
	DeleteDC(hdc)
	Gdip_DeleteGraphics(p_graphic)
	
	if ( layer_name == WIN_ID_BG )
	{
		Gdip_DisposeImage(p_bitmap)
	}
	
	return retour
}

; --------------------------------------------------------------------------------------------------
; LABELS
; --------------------------------------------------------------------------------------------------
UpdateLabel:
	update_layer( WIN_ID_FG )
return

ExitLabel:
	Gdip_Shutdown(pToken)
	ExitApp
return

Before running the script don't forget to download this picture :

Posted Image

It's a very basic example, just a png background and the current time.

My question is still the same, is there a better (faster) way to do this?

Zaelia :
The first method would have been to have a single window with the picture and the text updating together. I think it is not the best solution because when you work with bigger pictures and with multiple pictures into the same window it is useless to waste memory and cpu updating static objects. Maybe I'm totally wrong, so if anyone have a better solution than the two I've presented, feel free to post.

Razer.

tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
For the example you posted, it would be best to just use one gui. when it gets more complex, then you can use clipping regions to only update the regions that you need to

Also, it may help you to look at example 11 as it is very similar to what you are writing

you will need:

Download: BRA Library

Although it is still incomplete :(

Razer
  • Members
  • 10 posts
  • Last active: Mar 11 2010 01:59 PM
  • Joined: 15 Feb 2010
Ok, thx for your advice Tic.

I've read your tutorial 11 and I'll try your region clipping method with a single gui to see if it's possible to use it with my script. I really hope so.

It would be great because I'm developping a google quick search bar like tool and right now I have 1 main gui window and 4 others which I have to move together when dragging.
It works quite well but it would be much easier and smoother to have everything running into a single gui.

Here you can find some screen and explanation of what I'm working on :

http://executor.21.f...topic.php?t=716

I think when I will have something good enough I'll post my tool here.

Anyway, I have two other problems I haven't solved, maybe someone has already figured these out?

1/ How to have an animation gif bitmap playing with gdip library?
I thought using Gdip_CreateBitmapFromFile, Gdip_DrawImage and UpdateLayeredWindow would work but it doesn't seem to.
BRA files could be a solution, I will test it later if nothing about the gif comes out.

2/ Since it's not possible to have a classic gui input box onto a mutli layered window, I had to simulate one for my tool.
I'm using a Gdip_TextToGraphics and scanning keystrokes to update this text. Porblem is when the input is too big the text is returning to the line.
Is it posible to have the text to be clipped at the left side like a classic input box?

Razer.

Zaelia
  • Members
  • 754 posts
  • Last active: Jan 17 2015 02:38 AM
  • Joined: 31 Oct 2008
Sorry to bother you...
What is the fastest method for draw ? gdidrawimage, bitblt or setimage ? I do not know which to use for a very fast update (30fps w640 h480) ...

tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
theyre all very different things. For ahk the best way is to just drawimage with clipping regions....for c# which is a lot faster, then lockbits and looping is faster