Jump to content

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

GDI+, Cutting, Anti-aliasing



  • Please log in to reply
12 replies to this topic
Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009

DRKNd8y.jpg
I can cut image as I want, but the problem is that the inner border is not anti-aliased (like the outer border).
I tried 2 different methods, but no success. What am I doing wrong? Do I have to call Gdip_LockBits() and Gdip_UnlockBits()? How?

How to make anti-aliased inner border?
Thank you in advance.

; Press F1, F2, F3 ...


;===Auto-execute========================================================================
;===Create LayeredWindow===
#NoEnv
w := 350, h := 350
OnExit, ExitSub
Gui 1: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs +HwndhGui
Gui 1: Show, NA
OnMessage(0x201, "WM_LBUTTONDOWN")
pToken := Gdip_Startup()
hbm := CreateDIBSection(w, h), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc), Gdip_SetSmoothingMode(G, 4)
pBrush1 := Gdip_CreateLineBrushFromRect(0, 0, w, h, 0xaa777777, 0xff111111)  ; for background
Gdip_FillEllipse(G, pBrush1, 1, 1, w-2, h-2)  ; fill background
UpdateLayeredWindow(hGui, hdc, (A_ScreenWidth-w)//2, (A_ScreenHeight-h)//2, w, h)

;===Create path which will later be used for cutting the image===
Offsets := "-32:-90|18:-61|18:-3|-32:26|-82:-3|-82:-61|-32:-143|24:-128|64:-88|79:-32|64:24|24:64|-32:79|-88:64|-128:24|-143:-32|-128:-88|-88:-128" ; Offsets are relative to the center of image
pPath := Gdip_CreatePath(1)                     ; BrushMode: Winding = 1
Loop, parse, Offsets, |
{
   StringSplit, coordinate, A_LoopField, :  
   Gdip_AddPathEllipse(pPath, w/2+coordinate1, w/2+coordinate2, 64, 64)
}
return


;===Hotkeys=============================================================================
F1::    ; First cutting method: Set clip to path, and then clear that area in Graphics
Gdip_GraphicsClear(G)
Gdip_FillEllipse(G, pBrush1, 1, 1, w-2, h-2)    ; fill background
Gdip_SetClipPath(G, pPath, 0)           ; CombineMode: Replace = 0, Intersect = 1, Union = 2, Xor = 3, Exclude = 4, Complement = 5
Gdip_GraphicsClear(G)                   ; clear just that area in Graphics
Gdip_ResetClip(G)                       ; now we can draw on entire Graphics again
UpdateLayeredWindow(hGui, hdc)
return

F2::    ; Second cutting method: Overwrite path with transparent brush
Gdip_GraphicsClear(G)
Gdip_FillEllipse(G, pBrush1, 1, 1, w-2, h-2)    ; fill background
pBrush2 := Gdip_BrushCreateSolid("0x00000000")
Gdip_SetCompositingMode(G, 1)       ; 0 = blended, 1 = overwrite
Gdip_FillPath(G, pBrush2, pPath)    ; overwrite with transparent brush
Gdip_SetCompositingMode(G, 0)       ; back to default mode; blended
Gdip_DeleteBrush(pBrush2)
UpdateLayeredWindow(hGui, hdc)
return

F3::    ; Back to initial state (image is not cut)
Gdip_GraphicsClear(G)
Gdip_FillEllipse(G, pBrush1, 1, 1, w-2, h-2)    ; fill background
UpdateLayeredWindow(hGui, hdc)
return

Esc::ExitApp


;===Subroutines=========================================================================
ExitSub:
Gdip_DeleteBrush(pBrush1), Gdip_DeletePath(pPath)
SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc), Gdip_DeleteGraphics(G)
Gdip_Shutdown(pToken)
ExitApp


;===Functions===========================================================================
#Include Gdip.ahk    ; by Tic	www.autohotkey.com/community/viewtopic.php?f=2&t=32238

WM_LBUTTONDOWN() {
   PostMessage, 0xA1, 2
}

My Website • Recommended: AutoHotkey Unicode 32-bit • Join DropBox, Copy


noname
  • Members
  • 650 posts
  • Last active:
  • Joined: 12 Nov 2011

I had a similar problem to create a circular avatar image,the only solution i could find was using a function made by Tic to use an " alpha mask".

 

This is an example ( it will download an image from dropbox as demo)

 

You can find the original function somewhere in the 88+pages here : http://www.autohotke...topic32238.html :/


setworkingdir %a_scriptdir%

onexit bye

url=https://dl.dropboxusercontent.com/u/14147708/a.png
ifnotexist, %a_scriptdir%\a.png
URLDownloadToFile, %url%,%a_scriptdir%\a.png

pToken := Gdip_Startup()
Gui, 2:  -Caption +E0x80000 +LastFound +OwnDialogs +Owner +hwndhwnd +alwaysontop
Gui, 2: Show, NA ,dialog

sFile=%a_scriptdir%\a.png
oFile=%a_scriptdir%\avatar.png

pBitmap := Gdip_CreateBitmapFromFile(sFile)
Gdip_GetDimensions(pBitmap, w, h)

hbm := CreateDIBSection(w,h)
hdc := CreateCompatibleDC()
obm := SelectObject(hdc, hbm)
pGraphics:=Gdip_GraphicsFromHDC(hdc)

pBitmapMask := Gdip_CreateBitmap(w, h), G2 := Gdip_GraphicsFromImage(pBitmapMask)
Gdip_SetSmoothingMode(G2, 4)

pBrush := Gdip_BrushCreateSolid(0xff00ff00)
Gdip_FillEllipse(G2, pBrush, 0, 0, w, h)
Gdip_DeleteBrush(pBrush)

pBitmapNew := Gdip_AlphaMask(pBitmap, pBitmapMask, 0, 0)
Gdip_DrawImage(pGraphics, pBitmapNew, 0,0,w,h)

Gdip_SaveBitmapToFile(pBitmapNew, oFile)
UpdateLayeredWindow(hwnd, hdc,(a_screenwidth-w)//2,(a_screenheight-h)//2,w,h)


return

esc::
bye:
Gdip_DisposeImage(pBitmap), Gdip_DisposeImage(pBitmapMask), Gdip_DisposeImage(pBitmapNew)
SelectObject(hdc, obm)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_DeleteGraphics(pGraphics)
Gdip_Shutdown(pToken)
exitapp

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

Gdip_AlphaMask(pBitmap, pBitmapMask, x, y)
{
	static _AlphaMask
	if !_AlphaMask
	{
		MCode_AlphaMask := "8B4424209983E2038D0C028B4424249983E20303C28B54241CC1F902C1F80285D27E7303C003C08944241C8D048D000000000FA"
		. "F4C242C034C2428538B5C240C55568B742424894424308B442418578D3C888954243085F67E2A8B5424142B54241C8BCF8BC38B2C0A332883C00481E"
		. "5FFFFFF003368FC83C1044E8969FC75E68B742428037C2434035C242CFF4C243075C45F5E5D5B33C0C3"

		VarSetCapacity(_AlphaMask, StrLen(MCode_AlphaMask)//2)
		Loop % StrLen(MCode_AlphaMask)//2      ;%
			NumPut("0x" SubStr(MCode_AlphaMask, (2*A_Index)-1, 2), _AlphaMask, A_Index-1, "char")
	}
	Gdip_GetDimensions(pBitmap, w1, h1), Gdip_GetDimensions(pBitmapMask, w2, h2)
	pBitmapNew := Gdip_CreateBitmap(w1, h1)
	if !pBitmapNew
		return -1

	E1 := Gdip_LockBits(pBitmap, 0, 0, w1, h1, Stride1, Scan01, BitmapData1)
	E2 := Gdip_LockBits(pBitmapMask, 0, 0, w2, h2, Stride2, Scan02, BitmapData2)
	E3 := Gdip_LockBits(pBitmapNew, 0, 0, w1, h1, Stride3, Scan03, BitmapData3)
	if (E1 || E2 || E3)
		return -2

	E := DllCall(&_AlphaMask, "ptr", Scan01, "ptr", Scan02, "ptr", Scan03, "int", w1, "int", h1, "int", w2, "int", h2, "int", Stride1, "int", Stride2, "int", x, "int", y)
	
	Gdip_UnlockBits(pBitmap, BitmapData1), Gdip_UnlockBits(pBitmapMask, BitmapData2), Gdip_UnlockBits(pBitmapNew, BitmapData3)
	return (E = "") ? -3 : pBitmapNew
}

winXP  and ahk unicode


Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009

Thank you for reply! :)

I studied example and was experimenting a little bit. As I need to work with paths, for experiment, I replaced this part

Gdip_FillEllipse(G2, pBrush, 0, 0, w, h)

with this

pPath := Gdip_CreatePath()
Gdip_AddPathEllipse(pPath, 0, 0, w, h)
Gdip_FillPath(G2, pBrush, pPath)
Gdip_DeletePath(pPath)

I was thinking I'll have the same result as in original example, because my [Path+Ellipse+Fill] should be equal to [Ellipse+Fill] as it is written in example.
I was wrong, it doesn't work... What am I doing wrong?


My Website • Recommended: AutoHotkey Unicode 32-bit • Join DropBox, Copy


noname
  • Members
  • 650 posts
  • Last active:
  • Joined: 12 Nov 2011

I tried path before i found Tic's function and could not make it work.

 

This is the original link to Tic's post maybe it is interesting to read his comments.

 

Can you not create a mask without using "Path" ?

 

http://www.autohotke...-73#entry535199


winXP  and ahk unicode


Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009

Hahaha... Here's why Path functions didn't work; Path is environment variable! Using #NoEnv solves the problem.

Give me a moment...


My Website • Recommended: AutoHotkey Unicode 32-bit • Join DropBox, Copy


Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009
I'm getting closer; here's what I got
sFile=%a_scriptdir%\a.png
url=https://dl.dropboxusercontent.com/u/14147708/a.png


#NoEnv
onexit bye
OnMessage(0x201, "WM_LBUTTONDOWN")
ifnotexist, % sFile
	URLDownloadToFile, % url, % sFile

pToken := Gdip_Startup()
Gui, 2:  -Caption +E0x80000 +LastFound +OwnDialogs +Owner +hwndhwnd +alwaysontop
Gui, 2: Show, NA ,dialog
pBitmap := Gdip_CreateBitmapFromFile(sFile), Gdip_GetDimensions(pBitmap, w, h)
hbm := CreateDIBSection(w,h), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm), pGraphics:=Gdip_GraphicsFromHDC(hdc)


pBitmapMask := Gdip_CreateBitmap(w, h), G2 := Gdip_GraphicsFromImage(pBitmapMask)
Gdip_SetSmoothingMode(G2, 4)
pBrush := Gdip_BrushCreateSolid(0xff00ff00)


;###############################################################################
;=== This block replaces original Gdip_FillEllipse(G2, pBrush, 0, 0, w, h) =====
pPath := Gdip_CreatePath()
Offsets := "-32:-90|18:-61|18:-3|-32:26|-82:-3|-82:-61|-32:-143|24:-128|64:-88|79:-32|64:24|24:64|-32:79|-88:64|-128:24|-143:-32|-128:-88|-88:-128" ; Offsets are relative to the center of image
pPath := Gdip_CreatePath(1)                     ; BrushMode: Winding = 1
Loop, parse, Offsets, |
{
   StringSplit, coordinate, A_LoopField, :  
   Gdip_AddPathEllipse(pPath, w/2+coordinate1, w/2+coordinate2, 64, 64)
}

;===Uncomment line below to create Image B ===
;Gdip_SetPathFillMode(pPath, 0), Gdip_AddPathRectangle(pPath, 0, 0, w, h)

Gdip_FillPath(G2, pBrush, pPath)
Gdip_DeletePath(pPath)
;===============================================================================
;###############################################################################


Gdip_DeleteBrush(pBrush)

pBitmapNew := Gdip_AlphaMask(pBitmap, pBitmapMask, 0, 0)
Gdip_DrawImage(pGraphics, pBitmapNew, 0,0,w,h)

UpdateLayeredWindow(hwnd, hdc,(a_screenwidth-w)//2,(a_screenheight-h)//2,w,h)
return

esc::
bye:
Gdip_DisposeImage(pBitmap), Gdip_DisposeImage(pBitmapMask), Gdip_DisposeImage(pBitmapNew)
SelectObject(hdc, obm)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_DeleteGraphics(pGraphics)
Gdip_Shutdown(pToken)
exitapp


;===Functions===========================================================================
#Include Gdip.ahk	; by Tic	www.autohotkey.com/community/viewtopic.php?f=2&t=32238

Gdip_AlphaMask(pBitmap, pBitmapMask, x, y) {	; by Tic
	static _AlphaMask
	if !_AlphaMask
	{
		MCode_AlphaMask := "8B4424209983E2038D0C028B4424249983E20303C28B54241CC1F902C1F80285D27E7303C003C08944241C8D048D000000000FA"
		. "F4C242C034C2428538B5C240C55568B742424894424308B442418578D3C888954243085F67E2A8B5424142B54241C8BCF8BC38B2C0A332883C00481E"
		. "5FFFFFF003368FC83C1044E8969FC75E68B742428037C2434035C242CFF4C243075C45F5E5D5B33C0C3"

		VarSetCapacity(_AlphaMask, StrLen(MCode_AlphaMask)//2)
		Loop % StrLen(MCode_AlphaMask)//2      ;%
			NumPut("0x" SubStr(MCode_AlphaMask, (2*A_Index)-1, 2), _AlphaMask, A_Index-1, "char")
	}
	Gdip_GetDimensions(pBitmap, w1, h1), Gdip_GetDimensions(pBitmapMask, w2, h2)
	pBitmapNew := Gdip_CreateBitmap(w1, h1)
	if !pBitmapNew
		return -1

	E1 := Gdip_LockBits(pBitmap, 0, 0, w1, h1, Stride1, Scan01, BitmapData1)
	E2 := Gdip_LockBits(pBitmapMask, 0, 0, w2, h2, Stride2, Scan02, BitmapData2)
	E3 := Gdip_LockBits(pBitmapNew, 0, 0, w1, h1, Stride3, Scan03, BitmapData3)
	if (E1 || E2 || E3)
		return -2

	E := DllCall(&_AlphaMask, "ptr", Scan01, "ptr", Scan02, "ptr", Scan03, "int", w1, "int", h1, "int", w2, "int", h2, "int", Stride1, "int", Stride2, "int", x, "int", y)
	
	Gdip_UnlockBits(pBitmap, BitmapData1), Gdip_UnlockBits(pBitmapMask, BitmapData2), Gdip_UnlockBits(pBitmapNew, BitmapData3)
	return (E = "") ? -3 : pBitmapNew
}


Gdip_AddPathRectangle(Path, x, y, w, h) {	; added by Learning one
	return DllCall("gdiplus\GdipAddPathRectangle", A_PtrSize ? "UPtr" : "UInt", Path, "float", x, "float", y, "float", w, "float", h)
}

Gdip_SetPathFillMode(Path, FillMode) {	; added by Learning one
	return DllCall("gdiplus\GdipSetPathFillMode", A_PtrSize ? "UPtr" : "UInt", Path, "int", FillMode)
}

WM_LBUTTONDOWN() {
   PostMessage, 0xA1, 2
}
a585OWT.jpg

What I get is image A - it is cropped, but in inverted way. :( I need sort of "Invert path" function. Like "Invert selection" in image editing programs.

Was experimenting with changing Fill mode to Alternate and adding Rectangle to that path in hope I'll get Invert path / Invert selection effect - But I get image B :(
To get Image B, uncomment the following line in the code ;Gdip_SetPathFillMode(pPath, 0), Gdip_AddPathRectangle(pPath, 0, 0, w, h)

What I would like to have is image C.
Should I use Regions instead of Paths to form my "cutter shape"?

My Website • Recommended: AutoHotkey Unicode 32-bit • Join DropBox, Copy


Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009

Should I use Regions instead of Paths to form my "cutter shape"?

I tried working with Regions, wraped some additional Region functions, but I have the same problem: I can cut one region with another (XOR), but again, there's no-antialiasing... :(

Here's the code:

;===Auto-execute========================================================================
#NoEnv
w := 350, h := 350
Gui 1: -Caption +E0x80000 +LastFound +AlwaysOnTop +ToolWindow +OwnDialogs +HwndhGui
Gui 1: Show, NA
OnMessage(0x201, "WM_LBUTTONDOWN")
pToken := Gdip_Startup()
hbm := CreateDIBSection(w, h), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc), Gdip_SetSmoothingMode(G, 4)


;=== Create path which will be cut ===
pPath1 := Gdip_CreatePath()
Gdip_AddPathEllipse(pPath1, 1, 1, w-2, h-2)

;=== Create path which is "the cutting shape" ===
Offsets := "-32:-90|18:-61|18:-3|-32:26|-82:-3|-82:-61|-32:-143|24:-128|64:-88|79:-32|64:24|24:64|-32:79|-88:64|-128:24|-143:-32|-128:-88|-88:-128" ; Offsets are relative to the center of image
pPath2 := Gdip_CreatePath(1)                     ; BrushMode: Winding = 1
Loop, parse, Offsets, |
{
   StringSplit, coordinate, A_LoopField, :  
   Gdip_AddPathEllipse(pPath2, w/2+coordinate1, w/2+coordinate2, 64, 64)
}

;=== Create, intersect and fill region ===
pRegion1 := Gdip_CreateRegionPath(pPath1)		; Creates a region that is defined by a Path1
pRegion2 := Gdip_CreateRegionPath(pPath2)		; Creates a region that is defined by a Path2
Gdip_CombineRegionRegion(pRegion1, pRegion2, 3)	; Updates first region to the portion of itself that intersects second region. 3 = CombineModeXor
pBrush := Gdip_CreateLineBrushFromRect(0, 0, w, h, 0xaa777777, 0xff111111)  ; create filling brush
Gdip_FillRegion(G, pBrush, pRegion1)			; fill intersected region


;=== Update, Delete ===
UpdateLayeredWindow(hGui, hdc, (A_ScreenWidth-w)//2, (A_ScreenHeight-h)//2, w, h)
Gdip_DeleteBrush(pBrush)
Gdip_DeletePath(pPath1), Gdip_DeletePath(pPath2)
Gdip_DeleteRegion(pRegion1), Gdip_DeleteRegion(pRegion2)
SelectObject(hdc, obm), DeleteObject(hbm), DeleteDC(hdc), Gdip_DeleteGraphics(G)
Gdip_Shutdown(pToken)
return


;===Hotkeys=============================================================================
Esc::ExitApp


;===Functions===========================================================================
#Include Gdip.ahk	; by Tic	www.autohotkey.com/community/viewtopic.php?f=2&t=32238

Gdip_CombineRegionRegion(Region, Region2, CombineMode) {	; Updates this region to the portion of itself that intersects another region. Added by Learning one
	Ptr := A_PtrSize ? "UPtr" : "UInt"	
	return DllCall("gdiplus\GdipCombineRegionRegion", Ptr, Region, Ptr, Region2, "int", CombineMode)
}

Gdip_CreateRegionPath(Path) {	; Creates a region that is defined by a GraphicsPath.  Added by Learning one
	Ptr := A_PtrSize ? "UPtr" : "UInt"	
	DllCall("gdiplus\GdipCreateRegionPath", Ptr, Path, "UInt*", Region)
	return Region
}

WM_LBUTTONDOWN() {
   PostMessage, 0xA1, 2
}

Here's the result :(
DRKNd8y.jpg


My Website • Recommended: AutoHotkey Unicode 32-bit • Join DropBox, Copy


noname
  • Members
  • 650 posts
  • Last active:
  • Joined: 12 Nov 2011

I tried inverting the mask but this also leads to non anti-aliased result.The only crude option i see is to create a magnified image and then resize the result in the hope the smoothing will take care of the edges.

But it is to late to try ..... :(

 

Hopefully Tic has some ideas :)


winXP  and ahk unicode


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

Made a very quick change to Gdip_AlphaMask

pToken := Gdip_Startup()
pBitmap := Gdip_CreateBitmapFromFile("MJ.jpg")
Gdip_GetDimensions(pBitmap, w, h)

pBitmapMask := Gdip_CreateBitmap(w, h), G2 := Gdip_GraphicsFromImage(pBitmapMask)
Gdip_SetSmoothingMode(G2, 4)

pBrush := Gdip_BrushCreateSolid(0xff00ff00)
Gdip_FillEllipse(G2, pBrush, 0, 0, w, h)
Gdip_DeleteBrush(pBrush)

pBitmapNew := Gdip_AlphaMask(pBitmap, pBitmapMask, 0, 0)
Gdip_SaveBitmapToFile(pBitmapNew, "Circle.png")
Gdip_DisposeImage(pBitmapNew)

pBitmapNew := Gdip_AlphaMask(pBitmap, pBitmapMask, 0, 0, 1)
Gdip_SaveBitmapToFile(pBitmapNew, "CircleInverted.png")
Gdip_DisposeImage(pBitmapNew)

Gdip_DisposeImage(pBitmap), Gdip_DisposeImage(pBitmapMask)
Gdip_Shutdown(pToken)
return

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

Gdip_AlphaMask(pBitmap, pBitmapMask, x, y, invert=0)
{
    static _AlphaMask
    if !_AlphaMask
    {
        MCode_AlphaMask := "518B4424249983E20303C28BC88B442428995383E20303C28B5424245556C1F902C1F802837C24400057757E85D20F8E0E01000"
        . "08B5C241C8B74242C03C003C0894424388D048D000000000FAF4C2440034C243C894424348B4424208D3C888954244485F67E2C8B5424182B5424208"
        . "BCF8BC38B2C0A332883C00481E5FFFFFF003368FC83C10483EE018969FC75E48B74242C037C2434035C2438836C24440175C15F5E5D33C05B59C385D"
        . "20F8E900000008B5C241C8B74242C03C003C0894424388D048D000000000FAF4C2440034C243C894424348B442420895C24448D3C888954241085F67"
        . "E428B5424182B5424208BC78BCBEB098DA424000000008BFF8B1981E3000000FFBD000000FF2BEB8B1C1081E3FFFFFF000BEB892883C10483C00483E"
        . "E0175D98B74242C8B5C2444035C2438037C2434836C241001895C244475A35F5E5D33C05B59C3"

        VarSetCapacity(_AlphaMask, StrLen(MCode_AlphaMask)//2)
        Loop % StrLen(MCode_AlphaMask)//2      ;%
            NumPut("0x" SubStr(MCode_AlphaMask, (2*A_Index)-1, 2), _AlphaMask, A_Index-1, "char")
    }
    Gdip_GetDimensions(pBitmap, w1, h1), Gdip_GetDimensions(pBitmapMask, w2, h2)
    pBitmapNew := Gdip_CreateBitmap(w1, h1)
    if !pBitmapNew
        return -1

    E1 := Gdip_LockBits(pBitmap, 0, 0, w1, h1, Stride1, Scan01, BitmapData1)
    E2 := Gdip_LockBits(pBitmapMask, 0, 0, w2, h2, Stride2, Scan02, BitmapData2)
    E3 := Gdip_LockBits(pBitmapNew, 0, 0, w1, h1, Stride3, Scan03, BitmapData3)
    if (E1 || E2 || E3)
        return -2

    E := DllCall(&_AlphaMask, "ptr", Scan01, "ptr", Scan02, "ptr", Scan03, "int", w1, "int", h1, "int", w2, "int", h2, "int", Stride1, "int", Stride2, "int", x, "int", y, "int", invert)
    
    Gdip_UnlockBits(pBitmap, BitmapData1), Gdip_UnlockBits(pBitmapMask, BitmapData2), Gdip_UnlockBits(pBitmapNew, BitmapData3)
    return (E = "") ? -3 : pBitmapNew
}
int Gdip_AlphaMask(unsigned int * Bitmap, unsigned int * BitmapMask, unsigned int * BitmapNew, int w1, int h1, int w2, int h2, int Stride1, int Stride2, int sx, int sy, int invert)
{
    int o1 = Stride1/4, o2 = Stride2/4;
    if (!invert)
    {
        for (int y = 0; y < h2; ++y)
        {
            for (int x = 0; x < w2; ++x)
            {
                BitmapNew[(x+sx)+(y+sy)*o1] = (BitmapMask[x+(y*o2)] & 0xff000000) | (Bitmap[(x+sx)+(y+sy)*o1] & 0x00ffffff);
            }
        }
    }
    else
    {
        for (int y = 0; y < h2; ++y)
        {
            for (int x = 0; x < w2; ++x)
            {
                BitmapNew[(x+sx)+(y+sy)*o1] = (0xff000000 - (BitmapMask[x+(y*o2)] & 0xff000000)) | (Bitmap[(x+sx)+(y+sy)*o1] & 0x00ffffff);
            }
        }
    }
    return 0;
}


RHCP
  • Members
  • 1228 posts
  • Last active: Apr 08 2017 06:17 PM
  • Joined: 29 May 2006

Is this interpretation of the above code correct?

You draw onto a picture which doesn't have an alpha channel using a brush which does. Then you use Gdip_AlphaMask to hide any pixel which doesn't have an alpha component.

pBrush := Gdip_BrushCreateSolid(0xff00ff00)

0xff00ff00. The first FF sets the transparency/alpha component,  but what purpose does the second FF serve?

 

And standard .bmp and .jpg files do not have an alpha component?

 

Thanks.



Learning one
  • Members
  • 1483 posts
  • Last active: Jan 02 2016 02:30 PM
  • Joined: 04 Apr 2009

Thank you so much Tic! :) B) B) B) B) B)
That additional Invert parameter is great! Solved! Example with Ellipse and GraphicsPath:



#NoEnv
TestMode := 2       ; 1 = Tic's original example with Ellipse, 2 = Learning one's example with GraphicsPath
ImageFullPath := A_ScriptDir "\MJ.jpg"

pToken := Gdip_Startup()
pBitmap := Gdip_CreateBitmapFromFile(ImageFullPath), Gdip_GetDimensions(pBitmap, w, h)
pBitmapMask := Gdip_CreateBitmap(w, h), G2 := Gdip_GraphicsFromImage(pBitmapMask), Gdip_SetSmoothingMode(G2, 4)

pBrush := Gdip_BrushCreateSolid(0xff00ff00)
if (TestMode = 1) {     ; Tic's original example with Ellipse
    Gdip_FillEllipse(G2, pBrush, 0, 0, w, h) 
}
else if (TestMode = 2) {    ; Learning one's example with GraphicsPath
    Offsets := "-32:-90|18:-61|18:-3|-32:26|-82:-3|-82:-61|-32:-143|24:-128|64:-88|79:-32|64:24|24:64|-32:79|-88:64|-128:24|-143:-32|-128:-88|-88:-128" ; Offsets are relative to the center of image
    pPath := Gdip_CreatePath(1)                     ; BrushMode: Winding = 1
    Loop, parse, Offsets, |
    {
       StringSplit, coordinate, A_LoopField, :  
       Gdip_AddPathEllipse(pPath, w/2+coordinate1, w/2+coordinate2, 64, 64)
    }
    Gdip_FillPath(G2, pBrush, pPath)
    Gdip_DeletePath(pPath)
}
Gdip_DeleteBrush(pBrush), Gdip_DeleteGraphics(G2)


pBitmapNew := Gdip_AlphaMask(pBitmap, pBitmapMask, 0, 0)
Gdip_SaveBitmapToFile(pBitmapNew, A_ScriptDir "\Circle.png")
Gdip_DisposeImage(pBitmapNew)

pBitmapNew := Gdip_AlphaMask(pBitmap, pBitmapMask, 0, 0, 1)
Gdip_SaveBitmapToFile(pBitmapNew, A_ScriptDir "\CircleInverted.png")
Gdip_DisposeImage(pBitmapNew)

Gdip_DisposeImage(pBitmap), Gdip_DisposeImage(pBitmapMask)
Gdip_Shutdown(pToken)

Run % A_ScriptDir "\Circle.png"
Run % A_ScriptDir "\CircleInverted.png"
ExitApp

;#######################################################################
#Include Gdip.ahk	; by Tic	www.autohotkey.com/community/viewtopic.php?f=2&t=32238

Gdip_AlphaMask(pBitmap, pBitmapMask, x, y, invert=0) {    ; by Tic. http://www.autohotkey.com/board/topic/103475-gdi-cutting-anti-aliasing/#entry638772
    static _AlphaMask
    if !_AlphaMask
    {
        MCode_AlphaMask := "518B4424249983E20303C28BC88B442428995383E20303C28B5424245556C1F902C1F802837C24400057757E85D20F8E0E01000"
        . "08B5C241C8B74242C03C003C0894424388D048D000000000FAF4C2440034C243C894424348B4424208D3C888954244485F67E2C8B5424182B5424208"
        . "BCF8BC38B2C0A332883C00481E5FFFFFF003368FC83C10483EE018969FC75E48B74242C037C2434035C2438836C24440175C15F5E5D33C05B59C385D"
        . "20F8E900000008B5C241C8B74242C03C003C0894424388D048D000000000FAF4C2440034C243C894424348B442420895C24448D3C888954241085F67"
        . "E428B5424182B5424208BC78BCBEB098DA424000000008BFF8B1981E3000000FFBD000000FF2BEB8B1C1081E3FFFFFF000BEB892883C10483C00483E"
        . "E0175D98B74242C8B5C2444035C2438037C2434836C241001895C244475A35F5E5D33C05B59C3"

        VarSetCapacity(_AlphaMask, StrLen(MCode_AlphaMask)//2)
        Loop % StrLen(MCode_AlphaMask)//2      ;%
            NumPut("0x" SubStr(MCode_AlphaMask, (2*A_Index)-1, 2), _AlphaMask, A_Index-1, "char")
    }
    Gdip_GetDimensions(pBitmap, w1, h1), Gdip_GetDimensions(pBitmapMask, w2, h2)
    pBitmapNew := Gdip_CreateBitmap(w1, h1)
    if !pBitmapNew
        return -1

    E1 := Gdip_LockBits(pBitmap, 0, 0, w1, h1, Stride1, Scan01, BitmapData1)
    E2 := Gdip_LockBits(pBitmapMask, 0, 0, w2, h2, Stride2, Scan02, BitmapData2)
    E3 := Gdip_LockBits(pBitmapNew, 0, 0, w1, h1, Stride3, Scan03, BitmapData3)
    if (E1 || E2 || E3)
        return -2

    E := DllCall(&_AlphaMask, "ptr", Scan01, "ptr", Scan02, "ptr", Scan03, "int", w1, "int", h1, "int", w2, "int", h2, "int", Stride1, "int", Stride2, "int", x, "int", y, "int", invert)
    
    Gdip_UnlockBits(pBitmap, BitmapData1), Gdip_UnlockBits(pBitmapMask, BitmapData2), Gdip_UnlockBits(pBitmapNew, BitmapData3)
    return (E = "") ? -3 : pBitmapNew
}

Result:
unDw2lQ.png


My Website • Recommended: AutoHotkey Unicode 32-bit • Join DropBox, Copy


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

Is this interpretation of the above code correct?

You draw onto a picture which doesn't have an alpha channel using a brush which does. Then you use Gdip_AlphaMask to hide any pixel which doesn't have an alpha component.

pBrush := Gdip_BrushCreateSolid(0xff00ff00)

0xff00ff00. The first FF sets the transparency/alpha component,  but what purpose does the second FF serve?

 

And standard .bmp and .jpg files do not have an alpha component?

 

Thanks.

 

We are drawing onto a new bitmap with pBrush with a brush that has the transparencies we want (So in this case circles with anti-aliased edges), and then we pass that bitmap into Gdip_AlphaMask along with the bitmap we want to cut out of, and it returns a new bitmap created from the information on both bitmaps.

 

I just made the brush arbitrarily green because it makes it easy to see the ARGB channels, having ff00ff00



RHCP
  • Members
  • 1228 posts
  • Last active: Apr 08 2017 06:17 PM
  • Joined: 29 May 2006

Thanks, that makes perfect sense. Somehow I didn't realise Gdip_AlphaMask() was taking two pBitmaps and one was acting as the mask - Haven't had my coffee yet :)

 

And thank you for creating such powerful library!