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
  • Guests
  • Last active:
  • Joined: --
Thank you ! :)

Please may I enquire, what is big difference between GDI & GDI+? I am guessing hBitmap and pBitmap are nearly same data bandwidth size... but one is higher quality? I don't understand.

but Thank you, it worked!

tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
Due to an interesting request by infogulch:

Get avg color of portion of the screen

A small gui will appear in the centre of the screen. You can then drag a selection of the screen using ctrl and lbutton. it will then update the gui with the average of the colours almost instantly. Obviously not all of the code below is needed, but i just wanted to make it look nice and easy to use

Ensure you have version 1.17 from the 1st post

#SingleInstance, Force
#NoEnv
DetectHiddenWindows, On
CoordMode, Mouse, Screen
SetBatchLines, -1
SetWinDelay, 0
SetWorkingDir %A_ScriptDir%

OnExit, Exit

; Uncomment if Gdip.ahk is not in your standard library
;#Include, Gdip.ahk

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

pBrush := Gdip_BrushCreateSolid(0x3300ff00), pPen := Gdip_CreatePen(0xbbff2233, 1)

Gui, 1: +AlwaysOnTop -Caption +ToolWindow +Border
Gui, 1: Margin, 0, 0
Gui, 1: Add, Picture, x0 y0 w50 h50 0xE hwndPic
Gui, 1: Show, AutoSize
Hotkey, ^LButton, Drag, On
Return

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

Drag:
Gui, 1: Submit, NoHide
MouseGetPos, x1, y1
Gui, 2: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
hwnd2 := WinExist()
Gui, 2: Show, NA
Loop
{
	Sleep, 50
	If !GetKeyState("LButton", "P")
	Break
	MouseGetPos, x2, y2
	If (x2 = Oldx2) && (y2 = Oldy2)
	Continue
	
	If (x2 >= x1) && (y2 <= y1)
	x := x1, y := y2, w := x2-x1, h := y1-y2
	Else If (x2 >= x1) && (y2 >= y1)
	x := x1, y := y1, w := x2-x1, h := y2-y1
	Else If (x2 <= x1) && (y2 >= y1)
	x := x2, y := y1, w := x1-x2, h := y2-y1
	Else If (x2 <= x1) && (y2 <= y1)
	x := x2, y := y2, w := x1-x2, h := y1-y2
	
	Oldx2 := x2, Oldy2 := y2
	
	hbm := CreateDIBSection(w, h), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm), G := Gdip_GraphicsFromHDC(hdc)
	Gdip_FillRectangle(G, pBrush, 0, 0, w, h)
	Gdip_DrawRectangle(G, pPen, 0, 0, w-1, h-1)
	UpdateLayeredWindow(hwnd2, hdc, x, y, w, h)
	Gdip_DeleteGraphics(G), SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
}
Gui, 2: Destroy

pBitmap1 := Gdip_BitmapFromScreen(x "|" y "|" w "|" h)
pBitmap2 := Gdip_CreateBitmap(1, 1), G2 := Gdip_GraphicsFromImage(pBitmap2)
pBitmap3 := Gdip_CreateBitmap(50, 50), G3 := Gdip_GraphicsFromImage(pBitmap3)

Gdip_DrawImage(G2, pBitmap1, 0, 0, 1, 1, 0, 0, 50, 50)
Gdip_FillRectangle(G3, pBrush2 := Gdip_BrushCreateSolid(Gdip_GetPixel(pBitmap2, 0, 0)), 0, 0, 50, 50)

hBitmap := Gdip_CreateHBITMAPFromBitmap(pBitmap3)
SetImage(Pic, hBitmap)

Gdip_DeleteBrush(pBrush2)
Gdip_DisposeImage(pBitmap1), Gdip_DisposeImage(pBitmap2), Gdip_DisposeImage(pBitmap3)
Gdip_DeleteGraphics(G2), Gdip_DeleteGraphics(G3)
DeleteObject(hBitmap)
Return

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

Exit:
Gdip_DeleteBrush(pBrush), Gdip_DeletePen(pPen)
Gdip_Shutdown(pToken)
ExitApp
Return


skrommel
  • Members
  • 193 posts
  • Last active: Jun 07 2010 08:30 AM
  • Joined: 30 Jul 2004
@tic:

but with 3 extra lines (one of them used to dispose of the pBitmap created), allowing us to save a gdi bitmap to disk

Why can't I use your code to save a transparent PNG?

I have no problem saving a transparent PNG when it's created using pBitmap:=Gdip_CreateBitmap(width,height)

:) Skrommel

tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
I would suggest that if you are wanting to work with a gdi+ bitmap the entire time, and want to use it to update a gui and save it to disk, then follow this method:

#SingleInstance, Force
#NoEnv
DetectHiddenWindows, On
CoordMode, Mouse, Screen
SetBatchLines, -1
SetWinDelay, 0
SetWorkingDir %A_ScriptDir%

; Uncomment if Gdip.ahk is not in your standard library
;#Include, Gdip.ahk

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

Width := 200, Height := 200

Gui, 1: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
Gui, 1: Show, NA
hwnd1 := WinExist()

pBitmap := Gdip_CreateBitmap(Width, Height), Gp := Gdip_GraphicsFromImage(pBitmap)

pBrush := Gdip_BrushCreateSolid(0x77ff0000)
Gdip_FillRectangle(Gp, pBrush, Width//4, Height//4, Width//2, Height//2)

hbm := CreateDIBSection(Width, Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm), Gh := Gdip_GraphicsFromHDC(hdc)
Gdip_DrawImage(Gh, pBitmap, 0, 0, Width, Height)

UpdateLayeredWindow(hwnd1, hdc, (A_ScreenWidth-Width)//2, (A_ScreenHeight-Height)//2, Width, Height)
Gdip_SaveBitmapToFile(pBitmap, "file.png")

Gdip_DeleteBrush(pBrush)
Gdip_DisposeImage(pBitmap)
SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc)
Gdip_DeleteGraphics(Gp), Gdip_DeleteGraphics(Gh)
Gdip_Shutdown(pToken)
Return

So work with a gdi+ bitmap (pBitmap) the entire time, and then whenever you want to update your gui with whatever you've been doing then just create a gdi bitmap:

hbm := CreateDIBSection(Width, Height), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm), Gh := Gdip_GraphicsFromHDC(hdc)

and then draw your gdi+ bitmap into the gdi bitmap:

Gdip_DrawImage(Gh, pBitmap, 0, 0, Width, Height)

You will then obviously have to dispose of all the extra stuff you created.

But the above example shows how we can use a bitmap with a transparent background and use it to update a gui and save it to disk

Hope that helps

skrommel
  • Members
  • 193 posts
  • Last active: Jun 07 2010 08:30 AM
  • Joined: 30 Jul 2004
:( I've tried similar code, but the problem is speed. It copies and redraws the entire screen instead of just the few changed pixels, so in a drawing app you'll end up with "square" circles and slow response.

I've found no way of just copying and updating the changed area, so my final solution was painting everything twice, once on the screen and once on the bitmap. :)

Is there no way to convert a DC bitmap into a transparent one, or loading a transparent bitmap into a DC?

Skrommel

Sunrise
  • Guests
  • Last active:
  • Joined: --
Tic, I want to draw a complex Filled shape with several sides and straight angles. It has no curves. Could you pls start me on some path to understanding how best to draw something simple with multiple sides and angles? One or two demonstrations would be so excellent to get me on my Web feet, if possible :D

hey, I have really enjoyed working thru your tutorials to understand concepts, and use of the gdip commands :D This was something I couldn't quite ascertain how to do yet, but would like to :(

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

I've found no way of just copying and updating the changed area, so my final solution was painting everything twice, once on the screen and once on the bitmap. :)

Have you tried painting everything onto the bitmap, then painting the bitmap onto the screen? As an added benefit, the image is presented to the screen in a single drawing operation. This usually eliminates any flicker that may be present.

If the screen is represented by a handle to a device context (hdc), you can use Gdip_GraphicsFromHDC to retrieve a GDI+ Graphics object. The Graphics object should be deleted with Gdip_DeleteGraphics before using any GDI functions with the original hdc.

There are also functions which tic has not yet wrapped:
; Get a device context from a Graphics object:
DllCall("gdiplus\GdipGetDC", "uint", pGraphics, "uint*", hdc)
; Release the device context before using the Graphics object again:
DllCall("gdiplus\GdipReleaseDC", "uint", pGraphics, "uint", hdc)

; Get a Graphics object from a window handle:
DllCall("gdiplus\GdipCreateFromHWND", hwnd, "uint*", pGraphics)


tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
Skrommel:
I think you are going about it the wrong way. If you are wanting to make a drawing application then you would use a gdi bitmap to let the user draw onto, and update the window with this, and then when they go to save what they have drawn, simpley convert it to a gdi+ bitmap. I do not believe it is necessary to get the graphics of the window. When I get time I'll write a quick start to a drawing app that allows a couple of drawing functions and can have layers if you wish to demonstrate the way I would do it, but I'll have to see when I get some time.

Sunrise:
To fill polygons you would use Gdip_FillPolygon() and you pass the points on the polygon as "x1,y1|x2,y2|x3,y3......"
So a square would look like:

Gdip_FillPolygon(pGraphics, pBrush, "0,0|100,0|100,100|0,100|0,0")

That last coordinate is optional to close the polygon. I will start on a helper function if you wish to allow quick and easy construction of uniform polygons.

Taking the 1st example I have added a simple function to construct these uniform polygons as demonstrated here:

; gdi+ ahk tutorial 1 written by tic (Tariq Porter)
; Requires Gdip.ahk either in your Lib folder as standard library or using #Include
;
; Tutorial to draw a single ellipse and rectangle to the screen

#SingleInstance, Force
#NoEnv
SetBatchLines, -1

; Uncomment if Gdip.ahk is not in your standard library
#Include, Gdip.ahk

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

; Set the width and height we want as our drawing area, to draw everything in. This will be the dimensions of our bitmap
Width := 800, Height := 800

; 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, 1: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs

; Show the window
Gui, 1: Show, NA

; Get a handle to this window we have created in order to update it later
hwnd1 := WinExist()

; Create a gdi bitmap with width and height of what we are going to draw into it. This is the entire drawing area for everything
hbm := CreateDIBSection(Width, Height)

; 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
G := Gdip_GraphicsFromHDC(hdc)

; Set the smoothing mode to antialias = 4 to make shapes appear smother (only used for vector drawing and filling)
Gdip_SetSmoothingMode(G, 4)


pBrush := Gdip_BrushCreateSolid(0x660000ff)
Gdip_FillUniformPolygon(G, pBrush, 9, 100, 100, 50)
Gdip_DeleteBrush(pBrush)


; Update the specified window we have created (hwnd1) with a handle to our bitmap (hdc), specifying the x,y,w,h we want it positioned on our screen
; So this will position our gui at (0,0) with the Width and Height specified earlier
UpdateLayeredWindow(hwnd1, hdc, 0, 0, Width, Height)


; Select the object back into the hdc
SelectObject(hdc, obm)

; Now the bitmap may be deleted
DeleteObject(hbm)

; Also the device context related to the bitmap may be deleted
DeleteDC(hdc)

; The graphics may now be deleted
Gdip_DeleteGraphics(G)
Return

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

Gdip_FillUniformPolygon(pGraphics, pBrush, Sides, x, y, Length, FillMode=0)
{
	a := 0, Points := x "," y "|"
	Loop, %Sides%
	{
		x += Length*Cos(a), y += Length*Sin(a)
		Points .= x "," y "|"
		a += ((4*ATan(1))/180)*(360/Sides)
	}
	StringTrimRight, Points, Points, 1	
	Gdip_FillPolygon(pGraphics, pBrush, Points, FillMode)
}

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

Exit:
; gdi+ may now be shutdown on exiting the program
Gdip_Shutdown(pToken)
ExitApp
Return

I have not added Gdip_FillUniformPolygon() to the library as I would prefer it to be in the form of:

Gdip_FillUniformPolygon(pGraphics, pBrush, Sides, x, y, w, h, FillMode=0)

so rather than specifying the length of each side, you could specify the width and height of the entire polygon, but this will be a bit tricky, so will do it when I have more time. Try the above example and ask if you have any other problems

skrommel
  • Members
  • 193 posts
  • Last active: Jun 07 2010 08:30 AM
  • Joined: 30 Jul 2004
:)
@ Lexikos: I can't get Gdip_GraphicsFromHDC to create a transparent bitmap.

@ tic: I've posted the code at http://www.donationc...19781#msg119781

Skrommel

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006

@ Lexicos: I can't get Gdip_GraphicsFromHDC to create a transparent bitmap.

Of course: Gdip_GraphicsFromHDC has nothing to do with creating a bitmap. If you had already created a GDI+ bitmap and wanted to draw it onto the screen, you could use Gdip_GraphicsFromHDC to create a Graphics object to wrap the screen hdc. Whether or not the bitmap is transparent is irrelevant. If you create a 32-bit ARGB GDI+ bitmap, it should be transparent by default. It only becomes opaque when you draw an opaque image onto it or fill it with an opaque colour. For instance, in your script posted at donationcoder.com:
hBrush:= Gdip_BrushCreateSolid("0xFF" background) ; <- This is an opaque/solid brush.
Gdip_FillRectangle(pGraphic3, hBrush, 0, 0, width3, height3)
Gdip_DeleteBrush(hBrush)


skrommel
  • Members
  • 193 posts
  • Last active: Jun 07 2010 08:30 AM
  • Joined: 30 Jul 2004
Lexikos wrote:

Gdip_GraphicsFromHDC has nothing to do with creating a bitmap


:) Sorry, I ment Gdip_GraphicsFromHDC can't be used as a basis for saving a transparent png file, it gets a black backround.

Skrommel

Lexikos
  • Administrators
  • 9844 posts
  • AutoHotkey Foundation
  • Last active:
  • Joined: 17 Oct 2006
Gdip_GraphicsFromHDC is used to get a GDI+ Graphics object for drawing onto a GDI device context. It does not modify the underlying object of the device context (which may or may not be a bitmap), so it will only "get a black background" if the underlying bitmap already had a black background.

I suggest there is some other issue.

thirtydot
  • Members
  • 3 posts
  • Last active: Sep 01 2008 12:25 PM
  • Joined: 24 Jul 2008
When saving a .jpg using Gdip_SaveBitmapToFile, is it possible to change the output quality? (0-100 in photoshop, for example)


edit:
I found out it's possible.
From Gdip.ahk
E := DllCall("gdiplus\GdipSaveImageToFile", "UInt", pBitmap, "UInt", &wOutput, "UInt", pCodec, "UInt", 0)
There's an additional argument that this can be called with, which can set the jpg quality, however I'm unsure how to build the "tParams" argument.

http://bb4w.wikispac...ng a JPEG image

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

I think you are going about the problem in slightly the wrong way, and don't think those functions you are adding are necessary. At the weekend I'll write a quick app to show how I think it should be done

thirtydot:

I have not added that yet, but I'll make a modification to that function and release an update to Gdip in the next couple of days

thirtydot
  • Members
  • 3 posts
  • Last active: Sep 01 2008 12:25 PM
  • Joined: 24 Jul 2008
Oh, thanks :)