Thank you, Blackholyman.
I had to think about this myself. I feel so stupid now.
Hello, first of all: Thanks to tic for this great library and all the others that help to improve it!
Now my issue: Suppose I have an image with a non-transparent part in the middle surrounded by transparency. How do I get the dimensions of the non-transparent part only (Gdip_GetImageDimensions gives you the dimensions of the whole image).
I tried the functions Gdip_CreateHBITMAPFromBitmap(pBitmap, Background=0xffffffff) but that only fills transparent areas with the color specified.
Do I have to loop through all pixels and find the non-transparent pixels with GetPixel, then crop, then get dimensions? That would be really slow I suppose.
Or is there a function in GDI that I could call with DllCall() that can crop/erase transparent parts? I searched the MS Docs but couldn't find an appropriate function.
Maybe using a Matrix? You see, I am lost... :-) Some help please.
Hi Frisko
The fastest option really will just be to use machine code. It only needs to scan one line to find the first pixel from the left and right. This could be just called once to find the answer, but it would be so quick that you may as well have a reusable function to find separately the first non-transparent x from left or right
I don't have visual studio C++ installed any longer so can't compile this, and so is untested:
int Gdip_FirstOpaqueX(unsigned char * bitmap, int w, int d) { if (d) { for (int x = 0; x < w; ++x) { if (bitmap[3+(4*x)] != 0) return x; } } else { for (int x = w; x > 0; --x) { if (bitmap[3+(4*x)] != 0) return x; } } return -1; }
And then could be used in a standard Gdip machine code function with something like
x1 := DllCall(&_FirstOpaqueX, "ptr", Scan0, "int", w, "int", 0) x2 := DllCall(&_FirstOpaqueX, "ptr", Scan0, "int", w, "int", 1) opaqueWidth := x2 - x1
Let me know if you have problems using this and can direct you how to compile it (The easiest would be to install VS C++ and then just run the MCode Creator on the forums)
Hey tic,
thanks for your help. I took this approach and here is my solution for anybody who needs it :-)
The function finds the left/top/right and bottommost pixel that is not the color specified. The result is a rectangle with X1,Y1 and X2,Y2. The function then creates a new bitmap of this area and returns it. Basically it cuts all colored/transparent space that surrounds a solid shape the bounds of a shape. What you get is the solid shape shape cutted to its rectangular bound. Then you can get the dimensions, save it to file or whatever you want.
Tested with shape rectangle, square, circle, triangle, ellipsis and polygon on transparent, black, red, green and blue background.
;##################################################################################### ; Function Gdip_CutBitmap ; Description This function cuts colored/transparent space that surrounds the bounds of a shape ; ; pBitmap Pointer to a bitmap ; ARGB The background color to cut. Default = 0x00ffffff (Full transparent) ; Dispose Whether to dispose pBitmap or not. Default = 1 (Dispose) ; ; return Pointer to a gdi+ bitmap Gdip_CutBitmap(pBitmap, ARGB=0x00ffffff, Dispose=1) { bmpWidth := Gdip_GetImageWidth(pBitmap) bmpHeight:= Gdip_GetImageHeight(pBitmap) Gdip_LockBits(pBitmap, 0, 0, bmpWidth, bmpHeight, Stride, Scan0, BitmapData, 1) MCode_ImageCut := " (LTrim Join 2,x86:VVdWU4PsOItEJFyLTCRgi1wkZMdEJCwAAAAAiceJwohEJCfB7xiB4gAA/wCJfCQo iceLRCRsweoQwe8IhcmJVCQEiXwkFIlEJDAPjpoAAACLfCRoiVwkZJCNtCYAAAAA i1QkZIXSflyLRCQwMdKJFCQPtkgDD7ZYAg+2aAEPtjCFyXQxieo4VCQUD5XCOFwk BInVD5XDCd2J8zhcJCeJ6w+VwgjaD4VvAgAAO0wkKA+FZQIAAIMEJAEB+Is0JDt0 JGR1rYtEJEyLAIXAdRiDRCQsAYNEJDAEi0QkLDtEJGAPhXr///+LXCRki0QkbIXb x0QkLAAAAACJRCQwD46bAAAAi2wkYIlcJGSNtgAAAACF7X5gi0QkMDHSiRQkjXYA D7ZIAw+2WAIPtngBD7Ywhcl0M4n6OFQkFA+VwjhcJASJ1w+VwwnfifM4XCQnD5XD idqJ+wjaD4XNAQAAO0wkKA+FwwEAAIMEJAGDwASLDCQ56XWsi0QkUIsAhcB1G4NE JCwBi0wkaItEJCwBTCQwO0QkZA+Fd////4tcJGSLRCRgg+gBD4inAAAAi3wkYIlc JGSJfCQwi3wkbI0Eh4t8JGiJRCQsi0QkZIXAfmaLRCQsMdKJFCSNdgCNvCcAAAAA D7ZIAw+2WAIPtmgBD7Ywhcl0MYnqOFQkFA+VwjhcJASJ1Q+VwwndifM4XCQnieoP lcMI0w+F7wAAADtMJCgPheUAAACDBCQBAfiLNCQ7dCRkda2LRCRUiwCFwHUQg2wk LASDbCQwAQ+FeP///4tcJGSJ2IPoAQ+IlwAAAA+vRCRoi3wkaItsJGCJXCQwA0Qk bPffiXwkNIlEJCyQjXQmAIXtflaLRCQsMdKJFCSNdgAPtkgDD7ZYAg+2eAEPtjCF yXQpifo4VCQUD5XCOFwkBInXD5XDCd+J8zhcJCeJ+g+VwwjTdTU7TCQodS+DBCQB g8AEizwkOe91totEJFiLAIXAdQ+LfCQ0AXwkLINsJDABdY2DxDhbXl9dw4t8JDCL TCRYifiJOevXi0wkMIt0JFSJyIkO6R////+LTCRMi0QkLIkB6aH9//+LfCRQi0Qk LIkH6UL+//+QkJCQkJCQkA==, x64:QVdBVkFVQVRVV1ZTSIPsGESLlCSAAAAARTH2TGOsJJgAAABIiVQkaIuUJIgAAABM iUwkeEyJRCRwRInQRYnTRInVwegIQYHjAAD/AMHtGInDSIuEJKAAAABBwesQhdJE i4wkkAAAAEyNeAN+dg8fAEWFyX5WTIn4MdJmDx9EAABED7YAD7Zw/0QPtmD+D7Z4 /UWFwHQqRDjjQQ+VxEE480APlcZECeZBOPpAD5XHQAj+D4VMAgAAQTnoD4VDAgAA g8IBTAHoRDnKdbWLAYXAdRJBg8YBSYPHBEQ7tCSIAAAAdY1FhckPji0CAABIi4Qk oAAAAExjvCSYAAAARTHkTI1oA4uEJIgAAABMiTwkTIt8JGiD6AFMjTSFBAAAAIlE JAyLhCSIAAAAhcB+S0uNPC5MiegPthAPtkj/D7Zw/kQPtkD9hdJ0J0A480APlcZB OMsPlcEJ8UU4wkEPlcBECMEPhacBAAA56g+FnwEAAEiDwARIOcd1vEGLB4XAdQ1B g8QBTAMsJEU5zHWWi0QkDIXAD4iRAAAASIu8JKAAAADB4AJMY6QkmAAAAEiYRIus JIgAAABMjXQHA0iLfCRwkEWFyX5UTInwMclmDx9EAAAPthBED7ZA/0QPtnj+D7Zw /YXSdClEOPtBD5XHRTjDQQ+VwEUJ+EE48kAPlcZBCPAPhfIAAAA56g+F6gAAAIPB AUwB4EQ5yXW3iweFwHUKSYPuBEGD7QF1l0SJyIPoAQ+IqQAAAA+vhCSYAAAASIu8 JKAAAABEi6wkmAAAAESLvCSIAAAAQffdTWPtSJhMiSwkTItsJHhMjWQHA4tEJAxM jTSFBAAAAGYPH4QAAAAAAEWF/35HS408JkyJ4A8fQAAPthAPtkj/D7Zw/kQPtkD9 hdJ0H0A480APlcZBOMsPlcEJ8UU4wkEPlcBECMF1MDnqdSxIg8AESDn4dcRBi0UA hcB1CkwDJCRBg+kBdaJIg8QYW15fXUFcQV1BXkFfw0SJyEWJTQDr2ESJ6ESJL+kY ////RIkxRInw6b/9//9FiSdEieDpYv7//4uEJIgAAACD6AGJRCQM6WD+//+QkJCQ )" GetImageRect := MCode(MCode_ImageCut) x1 := 0, y1 := 0, x2 := 0, y2 := 0 DllCall(GetImageRect, "int*",x1, "int*",y1, "int*",x2, "int*",y2 , "int",ARGB, "int",bmpWidth, "int",bmpHeight, "int",Stride, "Ptr",Scan0, "cdecl") ;MsgBox X1: %x1% Y1: %y1% , X2: %x2% Y2: %y2% Gdip_UnlockBits(pBitmap, BitmapData) dw := x2 - x1, dh := y2 - y1 pBitmap2 := Gdip_CreateBitmap(dw, dh) G2 := Gdip_GraphicsFromImage(pBitmap2), Gdip_SetSmoothingMode(G2, 4), Gdip_SetInterpolationMode(G2, 7) Gdip_DrawImage(G2, pBitmap, 0, 0, dw, dh, x1, y1, dw, dh) Gdip_DeleteGraphics(G2) if Dispose Gdip_DisposeImage(pBitmap) return pBitmap2 }
Usage:
pBitmap1 := Gdip_CreateBitmapFromFile(imgPath) pBitmap2 := Gdip_CutBitmap(pBitmap1) /* Do something with pBitmap2 */ Gdip_DisposeImage(pBitmap2)
You'll need a MCode Function. I am using Bentschi's (thanks Bentschi!)
EDIT: Here is the C-Code the MCode is made from:
void Gdip_CutBitmap(int *Leftmost, int *Topmost, int *Rightmost, int *Bottommost, int c, int w, int h, int Stride, unsigned char *Scan0) { struct ARGB { unsigned char A; unsigned char R; unsigned char G; unsigned char B; } color; color.A = (0xff000000 & c) >> 24; color.R = (0x00ff0000 & c) >> 16; color.G = (0x0000ff00 & c) >> 8; color.B = 0x000000ff & c; int x, y, o, A, R, G, B; // Leftmost pixel (X1) that is not the color described in the argument for (x = 0; x < w; ++x) { for (y = 0; y < h; ++y) { o = (4*x)+(y*Stride); A = Scan0[3+o]; R = Scan0[2+o]; G = Scan0[1+o]; B = Scan0[o]; if (A != 0 && (color.A != A || color.R != R || color.G != G || color.B != B)) { Leftmost[0] = x; break; } } if (Leftmost[0]) break; } // Topmost (Y1) for (y = 0; y < h; ++y) { for (x = 0; x < w; ++x) { o = (4*x)+(y*Stride); A = Scan0[3+o]; R = Scan0[2+o]; G = Scan0[1+o]; B = Scan0[o]; if (A != 0 && (color.A != A || color.R != R || color.G != G || color.B != B)) { Topmost[0] = y; break; } } if (Topmost[0]) break; } // Rightmost (X2) for (x = w - 1; x >= 0; --x) { for (y = 0; y < h; ++y) { o = (4*x)+(y*Stride); A = Scan0[3+o]; R = Scan0[2+o]; G = Scan0[1+o]; B = Scan0[o]; if (A != 0 && (color.A != A || color.R != R || color.G != G || color.B != B)) { Rightmost[0] = x + 1; break; } } if (Rightmost[0]) break; } // Bottommost (Y2) for (y = h - 1; y >= 0; --y) { for (x = 0; x < w; ++x) { o = (4*x)+(y*Stride); A = Scan0[3+o]; R = Scan0[2+o]; G = Scan0[1+o]; B = Scan0[o]; if (A != 0 && (color.A != A || color.R != R || color.G != G || color.B != B)) { Bottommost[0] = y + 1; break; } } if (Bottommost[0]) break; } }
Woow ...! Thanks a lot Frisko and Tic it enables me to try out a project i had i mind
winXP and ahk unicode
Nice work Frisko.
Could you also post your C++ in your post. It makes it easier for anyone to modify if their use-case is slightly different.
Nice work Frisko.
Could you also post your C++ in your post. It makes it easier for anyone to modify if their use-case is slightly different.
I updated my post.
Btw: The purpose of the function is not to extract/clip/mask a shape. The description was a bit confusing, so I changed it accordingly.
Have Win 8.1 x64.
Basic Autohotkey 1.0.48.5 (32-bit)
Trying to capture an area of a screen.
Using Gdip_ALL.ahk
(; Gdip standard library v1.45 by tic (Tariq Porter) 07/09/11; Modifed by Rseding91 using fincs 64 bit compatible Gdip library 5/1/2013 ; Supports: Basic, _L ANSi, _L Unicode x86 and _L Unicode x64 ; ; Updated 2/20/2014 - fixed Gdip_CreateRegion() and Gdip_GetClipRegion() on AHK Unicode x86 ; Updated 5/13/2013 - fixed Gdip_SetBitmapToClipboard() on AHK Unicode x64 ; )
Can't seem to be able to save my screenshots.
pointer_to_a_bitmap := Gdip_BitmapFromScreen(0)
results in 0
Any ideas on what's going on?
Thanks
ok - seems like I figured out my problem - I had to do
gdipToken := Gdip_Startup()
Hi guys,
yesterday, I updated my AutoHotKey from 1.1.10.01 to 1.1.19.03 on my Win7 64bit machine. I had been using this script http://www.autohotke...-time/?p=662909for quite a while without problems. Unfortunately it stopped working after the update. To be exact, it's still working in the background, but the clock just doesn't appear on the screen any more. This is why I suspect that something is broken on the graphical side. That script was not written by me originally, I just added some non-graphics-related features, so I don't have much experience about how to go around with Gdip_All.ahk. Anyway, it was working fine with v1.1.10.01, and it is not working now. Btw, I also updated Gdip_All.ahk from the version at https://ahknet.autoh...ll/Gdip_All.ahk to the version shared by Blackholyman on Dropbox, which was updated last on 2/20/2014. Do you have any ideas, what could be the reason? I also checked the recent changes in AutoHotKey, but I couldn't find the relevant piece.
Your help is much appreciated.
Csaba
As mentioned previously, support for legacy AHK will soon be dropped from the Gdip library as all active development has moved to AHK v1.1
The Gdip library is being completely overhauled and can be viewed on the new forums and all links will be removed from these old forums in the coming months. All examples will be completely rewritten for the new library and new controls and methods will be created as well as completely new examples.
Development can be viewed on the new forums:
ahkscript.org/boards/viewtopic.php?t=6517
Thanks for that amazing library! I use it in AutoHotFlow.
Hi there,
I was wondering if you can help me, i find gdip quite confusing as i am not a proficient programmer.
I am trying to crop an image i am searching with, can i use GDIP for that?
I had in mind something like this:
1) i use testimage.png, and would like to search the screen for a percentage of the horizontal of that image.
So if the testimage.png is 10x100, would like to be able to crop the image horizontally, say 60%, so i am only using the first 60 pixels in my imagesearch.
I presume i would need to save the saved testimage.png as a temporary image, example, temporarytestimage.png.
The cut off the last number of horizontal pixels, using gdip? So if i wanted to search only 60% i would cut the image by the last 40 pixals.
Then use the new temporarytestimage.png in the imagesearch
Then delete afterwards.
Is all my ramblings possible with GDIP?
Kindest Regards
Surreall