Page 1 of 2

[GDI]How to create a bitmap with opacity?

Posted: 10 Jun 2021, 19:43
by pv007

Code: Select all

CreateBITMAP(Width, Height, Radius, Color) {
   	BkColor := 0xFFF
   	BkColor .= Color
   	FileAppend, BkColor: %BkColor% `n,*

	pBitmap := Gdip_CreateBitmap( Width, Height ) 
  	G := Gdip_GraphicsFromImage( pBitmap ) 
	Gdip_SetSmoothingMode( G , 4 )
	Brush := Gdip_BrushCreateSolid( BkColor )
   

   	Gdip_FillRoundedRectangle(G, Brush, 0, 0, Width, Height, Radious)
   	Gdip_DeleteBrush( Brush )
	Gdip_DeleteGraphics( G )
	return pBitmap
}
I have found this function here at the forum to create a pBitmap with GDI.

Would like to ask, how to add a new option to it "opacity"?

I don't know if opacity is the right word, I mean a result similar to this effect in Photoshop:

A 70x70 red picture:
T1.png
T1.png (249 Bytes) Viewed 1985 times
Now with 50% opacity:
T2.png
T2.png (251 Bytes) Viewed 1985 times


The option I refer to, in photoshop:
Spoiler
If there's any way to set opacity/transparency in a already existing hBitmap also would help

Re: [GDI]How to create a bitmap with opacity?

Posted: 10 Jun 2021, 23:55
by teadrinker
A couple of examples:

Code: Select all

pToken := Gdip_Startup()

hbm := CreateHBitmap(w := 300, h := 220, 0x77FF0000)

hdc := CreateCompatibleDC()
obj := SelectObject(hdc, hbm)
Gui, -Caption +E0x80000 +ToolWindow +AlwaysOnTop +hwndhGui
Gui, Show, NA
UpdateLayeredWindow(hGui, hdc, 500, 400, w, h)

SelectObject(hdc, obj)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_Shutdown(pToken)

CreateHBitmap(width, height, color) {
   hBitmap := CreateDIBSection(width, height,,, pBits)
   Loop % width*height
      NumPut(color, pBits + (A_Index - 1)*4, "UInt")
   Return hBitmap
}

Code: Select all

pToken := Gdip_Startup()

pBitmap := CreateBitmap(0x77FF0000, w := 300, h := 220, 15)

hbm := CreateDIBSection(w, h)
hdc := CreateCompatibleDC()
obj := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)
Gdip_DrawImage(G, pBitmap, 0, 0, w, h)
Gui, -Caption +E0x80000 +ToolWindow +AlwaysOnTop +hwndhGui
Gui, Show, NA
UpdateLayeredWindow(hGui, hdc, 500, 400, w, h)

SelectObject(hdc, obj)
DeleteObject(hbm)
DeleteDC(hdc)
Gdip_DeleteGraphics(G)
Gdip_DisposeImage(pBitmap)
Gdip_Shutdown(pToken)

CreateBitmap(color, w, h, r := 0) {
   pBitmap := Gdip_CreateBitmap(w, h)
   G := Gdip_GraphicsFromImage(pBitmap)
   pBrush := Gdip_BrushCreateSolid(color)
   Gdip_SetSmoothingMode(G, AntiAlias := 4)
   Gdip_FillRoundedRectangle(G, pBrush, 0, 0, w - !!r, h - !!r, r)
   DeleteObject(pBrush)
   Gdip_DeleteGraphics(G)
   Return pBitmap
}

Re: [GDI]How to create a bitmap with opacity?

Posted: 11 Jun 2021, 00:57
by pv007
@teadrinker please an example not using layered window

I tried:

Code: Select all

color := 0x199FF0000
pBitmap := CreateBitmap(color, w := 300, h := 220, 0)

HB := Gdip_CreateHBITMAPFromBitmap(pBitmap)

Gui, Font, s16
Gui, Add, Text,,TEXT TEXT TEXT TEXT
Gui, Add, Picture, x0 y0 +BackgroundTrans,% "HBITMAP:" . HB
Gui, Show, w300 h220, TEST

Gui, 2:Show, w300 h220,

pBitmap2 := CreateBitmap(color, w := 300, h := 220, 0)

hbm := CreateDIBSection(w, h)
hdc := CreateCompatibleDC()
obj := SelectObject(hdc, hbm)
G := Gdip_GraphicsFromHDC(hdc)
Gdip_DrawImage(G, pBitmap2, 0, 0, w, h)

Gui, 3: +hwndhGui +E0x80000
Gui, 3:Show
UpdateLayeredWindow(hGui, hdc, 500, 400, w, h)
But the color looks different than the color in the layered window, also how do you specify no transparency in the color parameter?
0x99FF0000

99 is the transparency level, right? how do I tell it no transparency in case of need?
0x100FF0000 gives a color totally different
Spoiler

Re: [GDI]How to create a bitmap with opacity?

Posted: 11 Jun 2021, 01:57
by boiler
pv007 wrote: But the color looks different than the color in the layered window
You used a transparency (actually opacity) of 0x99, while teadrinker used 0x77, so I don’t know why you would expect them to look the same.

pv007 wrote: also how do you specify no transparency in the color parameter?
0x99FF0000

99 is the transparency level, right? how do I tell it no transparency in case of need?
0x100FF0000 gives a color totally diferent
99 (as specified here) is the alpha channel, which is where you indicate transparency. It is more correctly viewed as opacity because higher numbers are more opaque. 100 is not full opacity/no transparency. These are hexadecimal numbers, and the max is 0xFF (255 in decimal), not 100. No transparency/full opacity for this color is 0xFFFF0000.

Full opacity is 0xFF (255 decimal). Full transparency is 0x00 (0). 50% transparency is 0x80 (128). What you specified with 0x99 (153 decimal) is 60% opacity or 40% transparency.

Re: [GDI]How to create a bitmap with opacity?

Posted: 11 Jun 2021, 02:28
by pv007
Hello Boiler, thanks for the explanation about the alpha channel!

When i said looks different, i was referring to the color in the GUI and the layered window, this colors:
Spoiler

Re: [GDI]How to create a bitmap with opacity?

Posted: 11 Jun 2021, 05:02
by just me

Re: [GDI]How to create a bitmap with opacity?

Posted: 11 Jun 2021, 08:59
by teadrinker
pv007 wrote: i was referring to the color in the GUI and the layered window
The color in the GUI looks differently since the GUI has its own color that shines through the semitransparent Picture.

Re: [GDI]How to create a bitmap with opacity?

Posted: 12 Jun 2021, 22:17
by pv007
@just me and when you already have an existing bitmap, how do you load it into a gui and apply transparency on it? (No layered window please)

@teadrinker can you provide me another example of instead creating a new bitmap, applying transparency to a existing one?

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 03:06
by teadrinker
As I understand, transparency can be added only if HBitmap is 32 bits per pixel:

Code: Select all

SetBatchLines, -1
imgWithoutTransparency := A_WinDir . "\Web\Wallpaper\Windows\img0.jpg"
opacity := 0x77

hBitmap := LoadPicture(imgWithoutTransparency, "GDI+ w300 h-1")
SetTransparency(hBitmap, opacity)

hBitmapWithoutTransparency := LoadPicture(imgWithoutTransparency, "GDI+ w300 h-1")
Gui, Color, White
Gui, Add, Text,, Background
Gui, Add, Pic, xp yp +BackgroundTrans, HBITMAP: %hBitmap%
Gui, Add, Text,, This text won't be visible
Gui, Add, Pic, xp yp +BackgroundTrans, HBITMAP: %hBitmapWithoutTransparency%
Gui, Show
Return

GuiClose:
   ExitApp
   
SetTransparency(hBitmap, opacity) {
   static DIB_RGB_COLORS := 0
   VarSetCapacity(BITMAP, byteCount := 4*4 + A_PtrSize*2, 0)
   DllCall("GetObject", "Ptr", hBitmap, "Int", byteCount, "Ptr", &BITMAP)
   width := NumGet(BITMAP, 4, "UInt"), height := NumGet(BITMAP, 8, "UInt")
   bpp := NumGet(BITMAP, 18, "UShort")
   if (bpp != 32)
      throw "Bits per pixel: " . bpp . ", must be 32"
   
   if pBits := NumGet(BITMAP, 4*4 + A_PtrSize) {
      Loop % width * height
         NumPut(opacity, pBits + (A_Index - 1)*4 + 3, "UChar")
   }
   else {
      VarSetCapacity(BITMAPINFO, size := 4*10, 0)
      NumPut(size, BITMAPINFO)
      NumPut(width, BITMAPINFO, 4), NumPut(-height, BITMAPINFO, 8)
      NumPut(1, BITMAPINFO, 12), NumPut(bpp, BITMAPINFO, 14)
      VarSetCapacity(Bits, width * 4 * height, 0)
      hDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
      hObj := DllCall("SelectObject", "Ptr", hDC, "Ptr", hBitmap)
      DllCall("GetDIBits", "Ptr", hDC, "Ptr", hBitmap, "UInt", 0, "UInt", height, "Ptr", &Bits, "Ptr", &BITMAPINFO, "UInt", DIB_RGB_COLORS)
      Loop % width * height
         NumPut(opacity, &Bits + (A_Index - 1)*4 + 3, "UChar")
      DllCall("SetDIBits", "Ptr", hDC, "Ptr", hBitmap, "UInt", 0, "UInt", height, "Ptr", &Bits, "Ptr", &BITMAPINFO, "UInt", DIB_RGB_COLORS)
      DllCall("SelectObject", "Ptr", hDC, "Ptr", hObj)
      DllCall("DeleteDC", "Ptr", hDC)
   }
}

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 03:38
by pv007
Hi Teadrinker, i'm testing the script, is it really applying "transparency"? i can see the text beneath the picture, but it looks strange, looks like it's only changing the picture brightness

Re: [GDI]How to create a bitmap with opacity?  Topic is solved

Posted: 13 Jun 2021, 05:52
by SKAN
Hi @teadrinker

I'd assume LoadPicture(..., "GDI+") returns PARGB bitmap (@lexikos could confirm).
You can't set constant Alpha value in a PARGB without adjusting RGB values accordingly, I suppose.
 
image.png
image.png (4.78 KiB) Viewed 1763 times
 

Code: Select all

#NoEnv
#Warn
#SingleInstance, Force
SetWorkingDir, %A_ScriptDir%

If ! FileExist("T1.png")
     UrlDownloadToFile, https://www.autohotkey.com/boards/download/file.php?id=13665, T1.png

If ! FileExist("T2.png")
     UrlDownloadToFile, https://www.autohotkey.com/boards/download/file.php?id=13666, T2.png

Gui, New, -MinimizeBox, Opacity
Gui, Color, White
Gui, Margin, 20, 20
Gui, Add, Picture,xm, T1.png
Gui, Add, Text, xm y+0, Background
Gui, Add, Picture, xp+32 BackgroundTrans, T2.png

Gui, Add, Picture,xm, T1.png
Gui, Add, Text, xm y+0, Background

hBM := LoadPicture("T1.png", "GDI+")
SetTransparency(hBM, 127)
Gui, Add, Picture,xp+32 BackgroundTrans, HBITMAP:%hBM%
Gui, Show



SetTransparency(hBitmap, opacity) {
   static DIB_RGB_COLORS := 0
   VarSetCapacity(BITMAP, byteCount := 4*4 + A_PtrSize*2, 0)
   DllCall("GetObject", "Ptr", hBitmap, "Int", byteCount, "Ptr", &BITMAP)
   width := NumGet(BITMAP, 4, "UInt"), height := NumGet(BITMAP, 8, "UInt")
   bpp := NumGet(BITMAP, 18, "UShort")
   if (bpp != 32)
      throw "Bits per pixel: " . bpp . ", must be 32"

   if pBits := NumGet(BITMAP, 4*4 + A_PtrSize) {
      Loop % width * height
         NumPut(opacity, pBits + (A_Index - 1)*4 + 3, "UChar")
   }
   else {
      VarSetCapacity(BITMAPINFO, size := 4*10, 0)
      NumPut(size, BITMAPINFO)
      NumPut(width, BITMAPINFO, 4), NumPut(-height, BITMAPINFO, 8)
      NumPut(1, BITMAPINFO, 12), NumPut(bpp, BITMAPINFO, 14)
      VarSetCapacity(Bits, width * 4 * height, 0)
      hDC := DllCall("CreateCompatibleDC", "Ptr", 0, "Ptr")
      hObj := DllCall("SelectObject", "Ptr", hDC, "Ptr", hBitmap)
      DllCall("GetDIBits", "Ptr", hDC, "Ptr", hBitmap, "UInt", 0, "UInt", height, "Ptr", &Bits, "Ptr", &BITMAPINFO, "UInt", DIB_RGB_COLORS)
      Loop % width * height
         NumPut(opacity, &Bits + (A_Index - 1)*4 + 3, "UChar")
      DllCall("SetDIBits", "Ptr", hDC, "Ptr", hBitmap, "UInt", 0, "UInt", height, "Ptr", &Bits, "Ptr", &BITMAPINFO, "UInt", DIB_RGB_COLORS)
      DllCall("SelectObject", "Ptr", hDC, "Ptr", hObj)
      DllCall("DeleteDC", "Ptr", hDC)
   }
}
 

@pv007 : My Imagen() will do this if you pass a colormatrix in options.
I will try to implement this as a built-in feature. (Edit: Done)

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 06:35
by lexikos
The bitmap returned by LoadPicture with GDI+ is either the one created by GdipCreateHBITMAPFromBitmap, or a copy created with CopyImage. I would guess that any alpha channel is not retained, based on the presence of a "background color" parameter. I think the parameters and return value are only documented for the C++ wrapper, and even there the details are sparse.

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 08:16
by teadrinker
SKAN wrote: You can't set constant Alpha value in a pARGB without adjusting RGB values accordingly, I suppose.
But I see correct RGB values:

Code: Select all

If ! FileExist("T1.png")
     UrlDownloadToFile, https://www.autohotkey.com/boards/download/file.php?id=13665, T1.png

hBitmap := LoadPicture("T1.png", "GDI+")

VarSetCapacity(BITMAP, byteCount := 4*4 + A_PtrSize*2, 0)
DllCall("GetObject", "Ptr", hBitmap, "Int", byteCount, "Ptr", &BITMAP)
width := NumGet(BITMAP, 4, "UInt"), height := NumGet(BITMAP, 8, "UInt")
bpp := NumGet(BITMAP, 18, "UShort")
if (bpp != 32)
   throw "Bits per pixel: " . bpp . ", must be 32"

if pBits := NumGet(BITMAP, 4*4 + A_PtrSize) {
   Loop % width * height {
      MsgBox, % "red: "   . Format("{:#x}", NumGet(pBits + (A_Index - 1)*4 + 2, "UChar")) . "`n"
              . "green: " . Format("{:#x}", NumGet(pBits + (A_Index - 1)*4 + 1, "UChar")) . "`n"
              . "blue: "  . Format("{:#x}", NumGet(pBits + (A_Index - 1)*4 + 0, "UChar"))
   }
}
In the format Format32bppPArgb R, G, B are premultiplied, the formula is:

Code: Select all

factor := alpha/0xFF
PARGB := alpha << 24 | R*factor << 16 | G*factor << 8 | B*factor
If alpha is 0xff, factor is 1.
Am I wrong? @lexikos

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 09:11
by teadrinker
Got it, I had to change RGB values depending on the pixel format. There should be a universal way for all 32-bit formats. I'll think about it.

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 10:35
by SKAN
lexikos wrote:
13 Jun 2021, 06:35
The bitmap returned by LoadPicture with GDI+ is either the one created by GdipCreateHBITMAPFromBitmap, or a copy created with CopyImage. I would guess that any alpha channel is not retained, based on the presence of a "background color" parameter.
As far as I've tested LoadPicture with GDI+ always returns an alpha bitmap.
Transparency/Alpha transparency (if any) is well preserved.
Please clarify whether LoadPicture() returned alpha bitmap is in ARGB or PARGB pixel format. Thanks.

Forum doesn't allow .bmp file so i have zipped mono.bmp ( 653x200 1bpp bitmap ).
You may use the following script to test it.

Code: Select all

#NoEnv
#Warn
#SingleInstance, Force
SetWorkingDir, %A_ScriptDir%

hbm := LoadPicture("mono.bmp", "GDI+")
MsgBox % IsAlphaBitmap(hbm) ; True

IsAlphaBitmap(sBM) {                                        ; v0.95 by SKAN on D39C/D39O @ tiny.cc/t80920
Local
  VarSetCapacity(BITMAP, 32, 0)
  Res := DllCall("GetObject", "Ptr",sBM, "Int",A_PtrSize=8? 32:24, "Ptr",&BITMAP)
  Bpp := NumGet(BITMAP,18,"UShort")
  If (Res=0|| Bpp<32)
    Return 0

  W := NumGet(BITMAP,04,"UInt"), Plane := NumGet(BITMAP,16,"UShort"),  WBytes := NumGet(BITMAP,12,"UInt")
  H := NumGet(BITMAP,08,"UInt"), Bytes := WBytes*H,                    HalfSz := Bytes/2
  VarSetCapacity(BITMAPINFO, 40, 0), pBits:=0
  NumPut(Bpp,NumPut(Plane,NumPut(0-H,NumPut(W,NumPut(40,BITMAPINFO,"Int"),"Int"),"Int"),"Short"),"Short")
  tBM := DllCall("CreateDIBSection"
               , "Ptr",0, "Ptr",&BITMAPINFO, "Int",0, "PtrP",pBits, "Ptr",0, "Int", 0, "Ptr")
  tDC := DllCall("CreateCompatibleDC", "Ptr",0, "Ptr"),  DllCall("SaveDC", "Ptr",tDC)
  sDC := DllCall("CreateCompatibleDC", "Ptr",0, "Ptr"),  DllCall("SaveDC", "Ptr",sDC)
  DllCall("SelectObject", "Ptr",tDC, "Ptr",tBM)
  DllCall("SelectObject", "Ptr",sDC, "Ptr",sBM)
  DllCall("GdiAlphaBlend", "Ptr",tDC, "Int",0, "Int",0, "Int",W, "Int",H
                         , "Ptr",sDC, "Int",0, "Int",0, "Int",W, "Int",H, "Int",0x01FF0000)
  DllCall("RestoreDC", "Ptr",sDC, "Int",-1),             DllCall("DeleteDC", "Ptr",sDC)
  DllCall("RestoreDC", "Ptr",tDC, "Int",-1),             DllCall("DeleteDC", "Ptr",tDC)
  IsAplha := NumGet(pBits+0,"UInt") ? 1
          :  !(DllCall("ntdll\RtlCompareMemory", "Ptr",pBits, "Ptr",pBits+1, "Ptr",Bytes-1)=Bytes-1)
  DllCall("DeleteObject", "Ptr",tBM)
Return IsAplha
}

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 13:04
by teadrinker
SKAN wrote: My Imagen() will do this if you pass a colormatrix in options.
I will try to implement this as a built-in feature. (Edit: Done)
Even from hBitmap?

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 13:18
by SKAN
teadrinker wrote:
13 Jun 2021, 13:04
SKAN wrote: My Imagen() will do this if you pass a colormatrix in options.
I will try to implement this as a built-in feature. (Edit: Done)
Even from hBitmap?
Yes. filename can be "HBITMAP:" . hBM .. or "HBITMAP:*" . hBM if you want Imagen() to use a copy of hBitmap
.. either case it will delete the hBitmap you pass to it.

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 14:32
by teadrinker
Yes, it works, great! :)

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 15:07
by SKAN
teadrinker wrote:
13 Jun 2021, 14:32
Yes, it works, great! :)
:thumbup:

PS:
I had incorrectly uploaded my work folder instead of release folder.
If there is a Desktop_SlideShow.ahk in contents don't try it. It wasn't well tested and still in development.

Re: [GDI]How to create a bitmap with opacity?

Posted: 13 Jun 2021, 22:22
by lexikos
SKAN wrote:
13 Jun 2021, 10:35
Please clarify whether LoadPicture() returned alpha bitmap is in ARGB or PARGB pixel format.
I don't even know what you're talking about.

It's in whatever format GdipCreateHBITMAPFromBitmap creates it in.