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
nimda
  • Members
  • 4368 posts
  • Last active: Aug 09 2015 02:36 AM
  • Joined: 26 Dec 2010
Can I suggest improvements to the tutorials?
things like this:

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

... mean nothing to me.
And things like this:

; We do not need SmoothingMode as we did in previous examples for drawing an image
; Instead we must set InterpolationMode. This specifies how a file will be resized (the quality of the resize)
; Interpolation mode has been set to HighQualityBicubic = 7
Gdip_SetInterpolationMode(G, 7)

... I have no idea how to figure out.

Therefore, I'm unable to do anything not covered in examples. Also, in this thread I found out I'm supposed to use DrawImage for re-sizing pictures, but a search in the docs for 'resize' or 're-size' yields nothing, so I have no idea how to figure that out either :(

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

Thanks for your suggestions. I know it's not ideal, but I think most things are covered in the tutorials and so you just have to read thoroughly through them

; DrawImage will draw the bitmap we took from the file into the graphics of the bitmap we created
; We are wanting to draw the entire image, but at half its size
; Coordinates are therefore taken from (0,0) of the source bitmap and also into the destination bitmap
; The source height and width are specified, and also the destination width and height (half the original)
; Gdip_DrawImage(pGraphics, pBitmap, dx, dy, dw, dh, sx, sy, sw, sh, Matrix)
; d is for destination and s is for source. We will not talk about the matrix yet (this is for changing colours when drawing)
Gdip_DrawImage(G, pBitmap, 0, 0, Width//2, Height//2, 0, 0, Width, Height)

I didn't create the help file, so you might want to talk with Lucid_Method to update it.
My advice regarding device contexts, bitmap handles, bitmap pointers, graphics contexts, device independent bitmaps etc is.....if you are unsure about them, then don't change them and copy them directly from the tutorials as you are unlikely to need to modify their behaviour.

I think the same with interpolation and smoothing...just always use the same as the tutorials. If you get to the point where you understand completely the last 2 tutorials and SAIRA then you'll know everything that is possible and how to go about finding out an answer....and if you ever get stuck....just ask :)

infogulch
  • Moderators
  • 717 posts
  • Last active: Jul 31 2014 08:27 PM
  • Joined: 27 Mar 2008
Here's a really nice GDI tutorial: Bitmap Basics - A GDI tutorial

The language is in C++ but the preface on the tutorials are really nice and informative from a beginners perspective and starts with general concepts and motivations around gdi. If you're willing to read into the c++ some and understand how it would translate to ahk it would probably give one a good understanding of how GDIP works. :)

tic
  • Members
  • 1934 posts
  • Last active: May 30 2018 08:13 PM
  • Joined: 22 Apr 2007
Some updates to the library...now version 1.44

Added
Gdip_BitmapSetResolution(pBitmap, dpix, dpiy)

Modified
Gdip_CloneBrush(pBrush)
to not use unsafe code

Added
Gdip_GetImageDimensions(pBitmap, ByRef Width, ByRef Height)
instead of
Gdip_GetDimensions(pBitmap, ByRef Width, ByRef Height)
although left in to not break existing scripts

Added:
Gdip_PixelateBitmap(pBitmap, ByRef pBitmapOut, BlockSize)
First machine code function added to the library. It has been removed from tutorial 12. Here are the comments for reference:

Gdip_PixelateBitmap(pBitmap, ByRef pBitmapOut, BlockSize)
{
	; PixelateBitmap is created as static so that it isn't created each time
	; It is the memory location that our machine code is stored
	; We put the machine code into the memory location the first time the function is called
	static PixelateBitmap
	if !PixelateBitmap
	{
		MCode_PixelateBitmap := "83EC388B4424485355568B74245C99F7FE8B5C244C8B6C2448578BF88BCA894C241C897C243485FF0F8E2E0300008B44245"
		. "499F7FE897C24448944242833C089542418894424308944242CEB038D490033FF397C2428897C24380F8E750100008BCE0FAFCE894C24408DA4240000"
		. "000033C03BF08944241089442460894424580F8E8A0000008B5C242C8D4D028BD52BD183C203895424208D3CBB0FAFFE8BD52BD142895424248BD52BD"
		. "103F9897C24148974243C8BCF8BFE8DA424000000008B5C24200FB61C0B03C30FB619015C24588B5C24240FB61C0B015C24600FB61C11015C241083C1"
		. "0483EF0175D38B7C2414037C245C836C243C01897C241475B58B7C24388B6C244C8B5C24508B4C244099F7F9894424148B44245899F7F9894424588B4"
		. "4246099F7F9894424608B44241099F7F98944241085F60F8E820000008D4B028BC32BC18D68038B44242C8D04B80FAFC68BD32BD142895424248BD32B"
		. "D103C18944243C89742420EB038D49008BC88BFE0FB64424148B5C24248804290FB644245888010FB644246088040B0FB644241088040A83C10483EF0"
		. "175D58B44243C0344245C836C2420018944243C75BE8B4C24408B5C24508B6C244C8B7C2438473B7C2428897C24380F8C9FFEFFFF8B4C241C33D23954"
		. "24180F846401000033C03BF2895424108954246089542458895424148944243C0F8E82000000EB0233D2395424187E6F8B4C243003C80FAF4C245C8B4"
		. "424280FAFC68D550203CA8D0C818BC52BC283C003894424208BC52BC2408BFD2BFA8B54241889442424895424408B4424200FB614080FB60101542414"
		. "8B542424014424580FB6040A0FB61439014424600154241083C104836C24400175CF8B44243C403BC68944243C7C808B4C24188B4424140FAFCE99F7F"
		. "9894424148B44245899F7F9894424588B44246099F7F9894424608B44241099F7F98944241033C08944243C85F60F8E7F000000837C2418007E6F8B4C"
		. "243003C80FAF4C245C8B4424280FAFC68D530203CA8D0C818BC32BC283C003894424208BC32BC2408BFB2BFA8B54241889442424895424400FB644241"
		. "48B5424208804110FB64424580FB654246088018B4424248814010FB654241088143983C104836C24400175CF8B44243C403BC68944243C7C818B4C24"
		. "1C8B44245C0144242C01742430836C2444010F85F4FCFFFF8B44245499F7FE895424188944242885C00F8E890100008BF90FAFFE33D2897C243C89542"
		. "45489442438EB0233D233C03BCA89542410895424608954245889542414894424400F8E840000003BF27E738B4C24340FAFCE03C80FAF4C245C034C24"
		. "548D55028BC52BC283C003894424208BC52BC2408BFD03CA894424242BFA89742444908B5424200FB6040A0FB611014424148B442424015424580FB61"
		. "4080FB6040F015424600144241083C104836C24440175CF8B4424408B7C243C8B4C241C33D2403BC1894424400F8C7CFFFFFF8B44241499F7FF894424"
		. "148B44245899F7FF894424588B44246099F7FF894424608B44241099F7FF8944241033C08944244085C90F8E8000000085F67E738B4C24340FAFCE03C"
		. "80FAF4C245C034C24548D53028BC32BC283C003894424208BC32BC2408BFB03CA894424242BFA897424448D49000FB65424148B4424208814010FB654"
		. "24580FB644246088118B5424248804110FB644241088043983C104836C24440175CF8B4424408B7C243C8B4C241C403BC1894424407C808D04B500000"
		. "00001442454836C2438010F858CFEFFFF33D233C03BCA89542410895424608954245889542414894424440F8E9A000000EB048BFF33D2395424180F8E"
		. "7D0000008B4C24340FAFCE03C80FAF4C245C8B4424280FAFC68D550203CA8D0C818BC52BC283C003894424208BC52BC240894424248BC52BC28B54241"
		. "8895424548DA424000000008B5424200FB6140A015424140FB611015424588B5424240FB6140A015424600FB614010154241083C104836C24540175CF"
		. "8B4424448B4C241C403BC1894424440F8C6AFFFFFF0FAF4C24188B44241499F7F9894424148B44245899F7F9894424588B44246099F7F9894424608B4"
		. "4241099F7F98944241033C03944241C894424540F8E7B0000008B7C241885FF7E688B4C24340FAFCE03C80FAF4C245C8B4424280FAFC68D530203CA8D"
		. "0C818BC32BC283C003894424208BC32BC2408BEB894424242BEA0FB65424148B4424208814010FB65424580FB644246088118B5424248804110FB6442"
		. "41088042983C10483EF0175D18B442454403B44241C894424547C855F5E5D33C05B83C438C3"
		VarSetCapacity(PixelateBitmap, StrLen(MCode_PixelateBitmap)//2)
		Loop % StrLen(MCode_PixelateBitmap)//2		;%
			NumPut("0x" SubStr(MCode_PixelateBitmap, (2*A_Index)-1, 2), PixelateBitmap, A_Index-1, "char")
	}
	
	; We need to get the width and height of the input bitmap to know the area we are going to lock
	; Also we must check that the input and output bitmaps are the same dimensions or else the c++ function will fail
	Width := Gdip_GetImageWidth(pBitmap), Height := Gdip_GetImageHeight(pBitmap)
	if (Width != Gdip_GetImageWidth(pBitmapOut) || Height != Gdip_GetImageHeight(pBitmapOut))
		return -1
	; Ensure the pixel size won't be greater than the actual dimensions of the image
	if (BlockSize > Width || BlockSize > Height)
		return -2
	
	; Gdip_LockBits(pBitmap, x, y, w, h, ByRef Stride, ByRef Scan0, ByRef BitmapData, LockMode = 3, PixelFormat = 0x26200a)
	; If we lock the bits of both bitmaps then we can have direct access to pixels and modify them
	; We pass the bitmap we wish to lock, the x,y,w,h of the area we wish to lock on that bitmap
	; We then pass the ByRef paramters for Stride, Scan0, BitmapData
	; Stride is the extra padding to the right of every row of pixels
	; Scan0 is the location in memory where the pixel data is located
	; BitmapData is only used with Gdip_UnlockBits to free up memory
	; We could change the lockmode, but it can be left at ReadWrite = 3
	; The PixelFormat by default will be Format32bppArgb = 0x26200a so that we have ARGB channels
	E1 := Gdip_LockBits(pBitmap, 0, 0, Width, Height, Stride1, Scan01, BitmapData1)
	E2 := Gdip_LockBits(pBitmapOut, 0, 0, Width, Height, Stride2, Scan02, BitmapData2)
	; Return an error if lockbits failed for whatever reason
	if (E1 || E2)
		return -3
	
	; We can now call our machine code function. Instead of the dll we wish to call, we instead use the memory location of our machine code
	; The parameters of our machine code are:
	; int PixelateBitmap(InputScan0, OutputScan0, Width, Height, Stride, BlockSize)
	E := DllCall(&PixelateBitmap, "uint", Scan01, "uint", Scan02, "int", Width, "int", Height, "int", Stride1, "int", BlockSize)
	
	; We must unlock the bitmaps, otherwise we will not be able to use them later on
	; A call to Gdip_LockBits establishes a temporary buffer that you can use to read or write pixel data in a specified format
	; After you write to the temporary buffer, a call to Gdip_UnlockBits copies the pixel data in the buffer to the Bitmap object
	Gdip_UnlockBits(pBitmap, BitmapData1), Gdip_UnlockBits(pBitmapOut, BitmapData2)
	return 0
}

Let me know if anyone has any problems. Thanks

  • Guests
  • Last active:
  • Joined: --

The library works on a x64 bit computer. I use it, and it works perfectly.

It works with the x86 build of AHK on a x64 bit machine but not with the x64bit build. I wish it did.

TH
  • Members
  • 76 posts
  • Last active: Jan 15 2012 06:22 PM
  • Joined: 28 Oct 2010
How do I move the shapes individually?

; gdi+ ahk tutorial 2 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, but just the outlines of these shapes

#SingleInstance, Force
#NoEnv
SetBatchLines, -1

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

initgdi:
; 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 := A_ScreenWidth, Height := A_ScreenHeight
; 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, 5: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs
; Show the window
Gui, 5: Show, NA
GUI_ID := WinExist()
; Set it transparent and make it click-through.
;~ WinSet , Transparent, 200         , % "ahk_id " . GUI_ID ; Change the numerical value for opaqueness amount.
WinSet, ExStyle    , ^0x00000020, % "ahk_id " . GUI_ID ; Leave this value alone.


; 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)
;~ ; Create a fully opaque red pen (ARGB = Transparency, red, green, blue) of width 3 (the thickness the pen will draw at) to draw a circle
;~  pPen := Gdip_CreatePen(0xffff0000, 3)
;~ ; Draw an ellipse into the graphics of the bitmap (this being only the outline of the shape) using the pen created
;~ ; This pen has a width of 3, and is drawing from coordinates (100,50) an ellipse of 200x300
;~ Gdip_DrawEllipse(G, pPen, 100, 50, 200, 300)
;~ Gdip_DrawEllipse(G, pPen, 100, 50, 1, 1)
;~ ; Delete the pen as it is no longer needed and wastes memory
;~ Gdip_DeletePen(pPen)

; Create a slightly transparent (66) blue pen (ARGB = Transparency, red, green, blue) to draw a rectangle
; This pen is wider than the last one, with a thickness of 10
pPen1 := Gdip_BrushCreateSolid(0xFFFF0000)
pPen2 := Gdip_BrushCreateSolid(0xFF009900)
pPen3 := Gdip_BrushCreateSolid(0xFF0000ff)
pPen4 := Gdip_BrushCreateSolid(0xFFff00ff)
pPen5 := Gdip_BrushCreateSolid(0xFF333333)
;~ pBrush := Gdip_BrushCreateSolid(0x660000ff)
; Draw a rectangle onto the graphics of the bitmap using the pen just created
; Draws the rectangle from coordinates (250,80) a rectangle of 300x200 and outline width of 10 (specified when creating the pen)
Gdip_FillRectangle(G, pPen1, 200, 200, 50, 50)
Gdip_FillRectangle(G, pPen2, 200, 250, 50, 50)
Gdip_FillRectangle(G, pPen3, 250, 250, 100, 50)
Gdip_FillRectangle(G, pPen4, 200, 300, 100, 100)
Gdip_FillRectangle(G, pPen5, 300, 300, 50, 100)
;~ Gdip_FillRectangle(G, pBrush, 250, 80, 300, 200)
;Gdip_DrawLine(G,pPen,100,200,200,300)
;~ Gdip_DrawLines(G,pPen,"100,200|200,300|450,350|400,500")
;DrawAxes()
;Gdip_DrawLines(G,pPen,"500,600|700,800|800,900")
;MsgBox, drawing %width% %Height% %G% %pPen% %hwnd1% %hdc%

; 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)
Return

;#######################################################################
closegdi:
; Delete the brush as it is no longer needed and wastes memory
Gdip_DeletePen(pPen)
; 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)
; gdi+ may now be shutdown on exiting the program
Gdip_Shutdown(pToken)
Gui, 5:Destroy
Return

$Esc::
Exit:
Gosub closegdi
ExitApp
Return

Changing "UpdateLayeredWindow(hwnd1, hdc, 0, 0, Width, Height)" moves the all the shapes

Frankie
  • Members
  • 2930 posts
  • Last active: Feb 05 2015 02:49 PM
  • Joined: 02 Nov 2008
You need to clear the graphics first. Then redraw the shapes in their new positions.
aboutscriptappsscripts
Request Video Tutorials Here or View Current Tutorials on YouTube
Any code ⇈ above ⇈ requires AutoHotkey_L to run

jethrow
  • Moderators
  • 2854 posts
  • Last active: May 17 2017 01:57 AM
  • Joined: 24 May 2009

It works with the x86 build of AHK on a x64 bit machine but not with the x64bit build. I wish it did.

What all would it take to make it compatible with 64-bit AHK? Wouldn't it just be changing the appropriate 4s to A_PtrSize?

fincs
  • Moderators
  • 1662 posts
  • Last active:
  • Joined: 05 May 2007
... And changing all "UInt" pointers to "Ptr", revising struct alignement + offsets of fields, etc etc etc.

fincs
  • Moderators
  • 1662 posts
  • Last active:
  • Joined: 05 May 2007
Ok, I've bitten the bullet and made this library compatible with 64-bit AutoHotkey myself. It can be found here.
Notes:[*:3l58btnf]It extensively uses the Ptr datatype and the A_PtrSize variable.
[*:3l58btnf]I've replaced all the ANSI-->Unicode business with the right (wstr) DllCall datatype.Due to the changes above, the library is no longer compatible with AutoHotkey Basic. I suggest tic to keep the current one for Basic users (they still exist?! :lol:) and require AutoHotkey_L from now on. Having workarounds all over the place for Basic support or maintaining two versions of the code would make the library unmaintainable.
Here are some useful notes for writing 64-bit compatible code:
[*:3l58btnf]The right DllCall pointer datatype is Ptr. This includes handles such as HWNDs, HBITMAPs, HDCs, etc. Note that the default DllCall return value is still int, so make sure to specify Ptr if the function you're calling returns a pointer!
[*:3l58btnf]The default NumGet/NumPut datatype is UPtr (unsigned pointer-sized integer), as opposed to UInt. Therefore, each time you want to retrive/store a DWORD you must manually specify UInt.
[*:3l58btnf]Pointers in structures occupy 8 bytes as opposed to 4 (use A_PtrSize), and are always 8-byte aligned (their offset must be evenly divisible by 8). For this to happen, sometimes there are some padding bytes before the pointer that are usually not present in 32-bit code. If a structure uses pointers and it's used inside another structure, the nested structure is also required to be 8-byte aligned, again with the padding.
[*:3l58btnf]MCodes must be recompiled for x64, and you must call VirtualProtect on the variable data because DEP is always on.
So far, all (non-BRA) examples are working perfectly :D

EDIT: Oh, and I forgot to mention, it's a very nicely structured and documented library. Congrats :)

Drugwash
  • Members
  • 1078 posts
  • Last active: May 24 2016 04:20 PM
  • Joined: 07 Sep 2008

Basic users (they still exist?! :lol: )

There is at least one: me. There may be others too, although they may not be posting all around the forum board.

Generic message to all script posters: :!:

Please, if you already have AHK Basic-compatible scripts posted, mark them as such. If you plan to update them and break compatibility, please keep the latest Basic-compatible version available, either in the forums or archived in your autohotkey.net repository.

Since AHK Basic is the only chance at programming for Win9x users these days, please do not take it away from us.

(AHK 1.0.48.05 and Win98SE) forever | My scripts are here


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

Issue: Gdip_PixelateBitmap() can't be ported unless tic releases the source code of its embedded MCode.

So far, all (non-BRA) examples are working perfectly :D

EDIT: Oh, and I forgot to mention, it's a very nicely structured and documented library. Congrats :)


Thanks fincs and all. I really appreciate anyone putting in the work to help out
I posted the machine code for pixelation a bunch of page back if you want to recompile it

<!-- m -->http://www.autohotke... ... 687#385687<!-- m -->

  • Guests
  • Last active:
  • Joined: --
Thanks a lot, fincs.

Please, if you already have AHK Basic-compatible scripts posted, mark them as such. If you plan to update them and break compatibility, please keep the latest Basic-compatible version available, either in the forums or archived in your autohotkey.net repository.

If you post scripts that are non-compatible with AHKL 64bit, please mark them as such.

Since AHK Basic is the only chance at programming for Win9x users these days, please do not take it away from us.

Posting a 64 bit compatible script/library does not mean taking away AHK Basic from you. Your claim sounds like as if the forum members had to take care of your every little troubles. Please do not make the innovators feel guilty about it. They should not feel painful reluctance by developing something new. I use Windows 7 64 bit and the AHKL 64 bit version. I must use AHKL because my language uses Unicode characters. And if I say, "Please do not take away AHKL from us", how does it sound to you. (you don't have to answer)

Drugwash
  • Members
  • 1078 posts
  • Last active: May 24 2016 04:20 PM
  • Joined: 07 Sep 2008
I was only referring to authors that may upgrade their scripts to AHK_L (or anything non-ANSI) without leaving in place the previous Basic versions (if they ever existed). I have no beef with upgrading AHK itself or the scripts, as long as the old ones are not being sweeped from the face of the Internet.

As for my scripts, I have just (hopefully) finished marking them as AHK Basic compatible. Please check, to make sure.

(AHK 1.0.48.05 and Win98SE) forever | My scripts are here


  • Guests
  • Last active:
  • Joined: --

I was only referring to authors that may upgrade their scripts to AHK_L (or anything non-ANSI) without leaving in place the previous Basic versions (if they ever existed).

You wrote:

Generic message to all script posters: :!:

As for my scripts, I have just (hopefully) finished marking them as AHK Basic compatible. Please check, to make sure.

AHK Basic compatible does not mean AHKL 64 bit incompatible.