Page 1 of 1

GDI+ Gdip_AlphaMask() not preserving png transparency

Posted: 19 Mar 2018, 07:25
by theimmersion
Firstly, is Tic still active and is GDI+ lib being maintained? I noticed Gdip_AlphaMask() function is missing in Gdip.ahk and i dont know why.
Its a very useful function.
Secondly, the function works great but theres a problem with it, the transparency from a png image becomes white. It supposed to just cut off edges or make it round if a solid image is being rendered or if the image is too large.
Is there a way around this or am i doing something wrong?

Heres an example:

Code: Select all

SendMode Input
SetWorkingDir %A_ScriptDir%
#SingleInstance, Force
SetBatchLines, -1

; Comment out if in your standard lib.
#Include, %A_ScriptDir%\Gdip_All.ahk

If !pToken := Gdip_Startup() {
	MsgBox, 48, Winamp Widget: gdiplus error!, Gdiplus failed to start. Please ensure you have the proper gdiplus on your system.`n`nWinamp Widget will now exit.
OnExit, Exit

ImageW := 100 ; End image width. Here you specify how large the image is going to be displayed (Gui width essentially).
ImageH := 100 ; End image height. Here you specify how large the image is going to be displayed (Gui width essentially).
Padding := 50 ; In percent! Padding 0 - 100, 0 = cubic, 100 = round (is making cubes rounder called padding?)
AspectRatioType := 2 ; 0 = Resize freely, 1 = by largest dimension between width and height, 2 = by smallest dimension between width and height.

	hbm := CreateDIBSection(ImageW, ImageH)
	hdc := CreateCompatibleDC()
	obm := SelectObject(hdc, hbm)
	G := Gdip_GraphicsFromHDC(hdc)
	Gdip_SetSmoothingMode(G, 4)

	Gui, Base: -Caption +E0x80000 +OwnDialogs ;+Owner
	Gui, Base: +LastFound +AlwaysOnTop
	LastParentHwnd := hwndBase := WinExist()
	Gui, Base: Show, NA
	pBrush := Gdip_BrushCreateSolid(0x0100ff00)
	Gdip_FillRectangle(G, pBrush, 0, 0, ImageW, ImageH)
	UpdateLayeredWindow(hwndBase, hdc, A_ScreenWidth-ImageW-200, 200, ImageW, ImageH)
	Gui, 1: -Caption +E0x80000 +OwnDialogs ;+Owner
	Gui, 1: +LastFound +AlwaysOnTop
	Gui, 1: +E0x10
	Gui, 1: +Parent%LastParentHwnd%
	hwnd1 := WinExist()
	Gui, 1: Show, NA
	pBrush := Gdip_BrushCreateSolid(0xff00ff00)
	Gdip_FillRectangle(G, pBrush, 0, 0, ImageW, ImageH)
	UpdateLayeredWindow(hwnd1, hdc, 0, 0, ImageW, ImageH)
	OnMessage(0x201, "WM_LBUTTONDOWN")

	Image := A_GuiEvent
	pBitmap := Gdip_CreateBitmapFromFile(Image), Gdip_GetDimensions(pBitmap, W, H)
	ImgW := ImageW
	ImgH := ImageH
	Resize(ImgW, ImgH, W, H, AspectRatioType)
	pBitmapMask := Gdip_CreateBitmap(W, H), G2 := Gdip_GraphicsFromImage(pBitmapMask), Gdip_SetSmoothingMode(G2, 4),Gdip_SetInterpolationMode(G2, 7)
	pBrush := Gdip_BrushCreateSolid(0xff00ff00)
	CanvasW := W
	CanvasH := H
	CanvasOffsetX := 0
	CanvasOffsetY := 0
	if (CanvasW > CanvasH) {
		CanvasW := CanvasH
		CanvasOffsetX := (W/2)-(H/2)
		NewPadding := ((CanvasW/2)/100) * Clamp(Padding,0,100)
	} else {
		CanvasH := CanvasW
		CanvasOffsetY := (W/2)-(H/2)
		NewPadding := ((CanvasH/2)/100) * Clamp(Padding,0,100)
	Gdip_FillRoundedRectangle(G2, pBrush, CanvasOffsetX, CanvasOffsetY, CanvasW, CanvasH, NewPadding)
	Gdip_DeleteBrush(pBrush), Gdip_DeleteGraphics(G2)
	pBitmapNew := Gdip_AlphaMask(pBitmap, pBitmapMask, 0, 0, 0)
	DrawW := (ImageW/2)-ImgW/2
	DrawH := (ImageH/2)-ImgH/2
	Gdip_DrawImage(G, pBitmapNew, DrawW, DrawH, ImgW, ImgH, 0, 0, W, H)
	UpdateLayeredWindow(hwnd1, hdc, 0, 0, ImageW, ImageH)
	Gdip_DisposeImage(pBitmapNew), Gdip_DisposeImage(pBitmap), Gdip_DisposeImage(pBitmapMask)


	SelectObject(hdc, obm)

	PostMessage, 0xA1, 2

; Resize by theimmersion
Resize(byref NewW, byref NewH, OrigW, OrigH, aspectRatio=0) {
	if (aspectRatio = 1 or aspectRatio = 2) {
		if NewW and NewH {
			if aspectRatio = 1
				(OrigW-NewW) > (OrigH-NewH) ? NewH := round(OrigH/OrigW*NewW) : NewW := round(OrigW/OrigH*NewH)
			else if aspectRatio = 2
				(OrigW-NewW) < (OrigH-NewH) ? NewH := round(OrigH/OrigW*NewW) : NewW := round(OrigW/OrigH*NewH)
	NewW := NewW = "" ? round(OrigW/OrigH*NewH) : NewW
	NewH := NewH = "" ? round(OrigH/OrigW*NewW) : NewH

; Clamp by theimmersion
Clamp(ByRef Val, Min, Max) {
	If (Val < Min)
		Val := Min
	If (Val > Max)
		Val := Max
	return Val

; by Tic.
  static e := {1:4, 2:1}, c := (A_PtrSize=8) ? "x64" : "x86"
  if (!regexmatch(mcode, "^([0-9]+),(" c ":|.*?," c ":)([^,]+)", m))
  if (!DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", 0, "uint*", s, "ptr", 0, "ptr", 0))
  p := DllCall("GlobalAlloc", "uint", 0, "ptr", s, "ptr")
  if (c="x64")
    DllCall("VirtualProtect", "ptr", p, "ptr", s, "uint", 0x40, "uint*", op)
  if (DllCall("crypt32\CryptStringToBinary", "str", m3, "uint", 0, "uint", e[m1], "ptr", p, "uint*", s, "ptr", 0, "ptr", 0))
    return p
  DllCall("GlobalFree", "ptr", p)

Gdip_AlphaMask(pBitmap, pBitmapMask, x, y, invert=0) {
    static _AlphaMask := MCode("
(LTrim Join

    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, "cdecl int")
    Gdip_UnlockBits(pBitmap, BitmapData1), Gdip_UnlockBits(pBitmapMask, BitmapData2), Gdip_UnlockBits(pBitmapNew, BitmapData3)

    return (E = "") ? -3 : pBitmapNew
You can just drag and drop some .png image that has transparency onto it.
The rendering and everything else relevant to Gdip_AlphaMask() function is in GuiDropFiles: subroutine and the function itself of course.

Setting x and/or y (i assume offsets) in Gdip_AlphaMask() to anything other than 0 crashes AHK v1.1. :(

Re: GDI+ Gdip_AlphaMask() not preserving png transparency

Posted: 20 Mar 2018, 05:06
by theimmersion
Wonder if it could be possible to detect if an image has transparency and if so, create a solid image from any pixels from that image.
Add the usual solid for roundness as well to it and then apply the alpha mask on the final image.
That way, you could get a round profile image and its original transparency.

Re: GDI+ Gdip_AlphaMask() not preserving png transparency

Posted: 20 Mar 2018, 05:29
by noname
Maybe you could create a suitable path and the use "Gdip_SetClipPath(pGraphics, Path, CombineMode=0)" before drawing the image.Transparency is kept but getting the path correct need some serious thinking...........

There is an example by nnnik but it did not work when i tried it (the clipped corners where not correct) but i guess it is worth a try :) if you really need it.

nnnik's code:

Re: GDI+ Gdip_AlphaMask() not preserving png transparency

Posted: 20 Mar 2018, 06:26
by nnnik
GDIp i still being maintained - or rather he is currently rewriting it using objects.
guest2345 is maintaining an version that works for both AHK_L and V2 since you are calling your #include gdip_all.ahk you should be using it.

Gdip_Alpha Mask is not part of the original Gdip implementation because it is an addition that tic made by himself.
It seems like he only wants to keep the things that are closely related to the original function in gdip.ahk
While the main library is still in active maintenance I'm not so sure about any of the additions or if anybody even has the source for that machine code anymore.

Re: GDI+ Gdip_AlphaMask() not preserving png transparency

Posted: 20 Mar 2018, 07:00
by noname
LinearSpoon has compiled it to 64bit and you also source code here:

Re: GDI+ Gdip_AlphaMask() not preserving png transparency

Posted: 20 Mar 2018, 07:10
by theimmersion
I hope when Tic does a rewrite, hell add some missing functions. Also hope he could somehow make some stuff easier to do.
I also just notice that Gdip_AlphaMask() is a bit finicky. The crop cuts out a few bits more then it should. What ever i try, it does not seem to crop it right.
Like 1-2 pixels get chopped off more on the bottom and right part of the image. The idea is to have a function collection for image manipulation. You give the image and specify what should be done and get the result in an easier way. Essentially trying to wrap up original functions into fewer ones. Everything works except Gdip_AlphaMask(). Ill figure something out i hope.

Re: GDI+ Gdip_AlphaMask() not preserving png transparency

Posted: 25 Mar 2018, 09:15
by theimmersion
Im experimenting with nnnik's code you posted, noname.
Seems like its closer to what i need but has one drawback currently. The edges are rough. No smoothing going on but it seems it does exactly what i need.
Ill try to make a nicer wrapper and try figure out the anti-aliasing (i guess?).

For direct reference @ by nnnik

Code: Select all

#Include <Gdip_All>

roundSize := 100

pToken  := Gdip_Startup()
pBitmap := Gdip_CreateBitmapFromFile( "test.jpg" )
pTarget := Gdip_CreateBitmap( w := Gdip_getImageWidth( pBitmap ), h := Gdip_getImageHeight( pBitmap ) )
pGraphics := Gdip_GraphicsFromImage( pTarget )

pPath   := Gdip_CreatePath()

Gdip_AddPathEllipse( pPath, 0, 0, roundSize * 2, roundSize * 2 )
Gdip_AddPathEllipse( pPath, 0, h - roundSize * 2, roundSize * 2, roundSize * 2 )
Gdip_AddPathEllipse( pPath, w - roundSize * 2, 0, roundSize * 2, roundSize * 2 )
Gdip_AddPathEllipse( pPath, w - roundSize * 2, h - roundSize * 2, roundSize * 2, roundSize * 2 )

Gdip_SetClipRect( pGraphics, roundSize, 0, w - roundSize * 2, h )
Gdip_SetClipRect( pGraphics, 0, roundSize, w, h - roundSize * 2, 2 )
Gdip_SetClipPath( pGraphics, pPath, 2 )

Gdip_DrawImage( pGraphics, pBitmap )
Gdip_SaveBitmapToFile( pTarget, "out2.png" )

Gdip_DeletePath( pPath )
Gdip_DeleteGraphics( pGraphics )
Gdip_DisposeImage( pBitmap )
Gdip_DisposeImage( pTarget )