Render PNG alpha-layer correctly

Just starting out? Need help? Post your questions and find answers here.
ctg
User
User
Posts: 21
Joined: Mon Sep 15, 2008 1:43 am

Render PNG alpha-layer correctly

Post by ctg »

G'day. I have a question that has taken a while to ask, as I have been trying to figure it out myself and by searching on the forums.

I am trying to render a PNG image on a window, which has other controls on it. Below is the standard code built using PureFORM:

Code: Select all

;{- Enumerations / DataSections
;{ Windows
Enumeration
  #Window_0
EndEnumeration
;}
;{ Gadgets
Enumeration
  #Image_0
  #Button_1
EndEnumeration
;}
;{ Images
Enumeration
  #Image_Image_0
EndEnumeration
;}
;{ Included Images
DataSection
  Image_Image_0:
  IncludeBinary "imagefilename"
EndDataSection
;}
;{ Image Plugins
UsePNGImageDecoder()
;}
Define.l Event, EventWindow, EventGadget, EventType, EventMenu
;}
Procedure OpenWindow_Window_0()
  If OpenWindow(#Window_0, 415, 196, 414, 364, "Window_0", #PB_Window_SystemMenu|#PB_Window_SizeGadget|#PB_Window_MinimizeGadget|#PB_Window_TitleBar)
    If CreateGadgetList(WindowID(#Window_0))
      ImageGadget(#Image_0, 15, 15, 384, 291, CatchImage(#Image_Image_0, ?Image_Image_0), #PB_Image_Border)
      ButtonGadget(#Button_1, 310, 325, 85, 25, "Close")
    EndIf
  EndIf
EndProcedure

OpenWindow_Window_0()

;{- Event loop
Repeat
  Event = WaitWindowEvent()
  Select Event
    ; ///////////////////
    Case #PB_Event_Gadget
      EventGadget = EventGadget()
      EventType = EventType()
      If EventGadget = #Image_0
      ElseIf EventGadget = #Button_1
        CloseWindow(#Window_0)
        Break
      EndIf
    ; ////////////////////////
    Case #PB_Event_CloseWindow
      EventWindow = EventWindow()
      If EventWindow = #Window_0
        CloseWindow(#Window_0)
        Break
      EndIf
  EndSelect
ForEver
;
;}
Now, I have read that you can use a new windoiw to render it in a strange fashion. This, however, sits in its own window. Below is the link to the topic of interest and the code I have seen to be [somewhat] useful:

http://purebasic.fr/english/viewtopic.php?t=32298

...and the code I have translated into procedures [to work with my existing code]:

Code: Select all

Global str_logofilename$

Procedure StringToBStr (str_logofilename$)
  Protected Unicode$ = Space(Len(str_logofilename$)* 2 + 2)
  Protected bstr_string.l
  PokeS(@Unicode$, str_logofilename$, -1, #PB_Unicode)
  bstr_string = SysAllocString_(@Unicode$)
  ProcedureReturn bstr_string
EndProcedure

ProcedureDLL LoadDecodedImage(str_logofilename$)

  CompilerIf Defined(GdiplusStartupInput, #PB_Structure) = 0
    Structure GdiplusStartupInput
      GdiPlusVersion.l
      *DebugEventCallback.Debug_Event
      SuppressBackgroundThread.l
      SuppressExternalCodecs.l
    EndStructure
  CompilerEndIf 
 
  OpenLibrary(0, "gdiplus.dll")
   
  input.GdiplusStartupInput
  input\GdiPlusVersion = 1
 
  CallFunction(0, "GdiplusStartup", @*token, @input, #Null)
  CallFunction(0, "GdipCreateBitmapFromFile", StringToBStr(str_logofilename$), @*image)
  CallFunction(0, "GdipGetImageWidth", *image, @Width.l)
  CallFunction(0, "GdipGetImageHeight", *image, @Height.l)
  CallFunction(0, "GdipCreateHBITMAPFromBitmap", *image, @imageid.l, -1)
  CallFunction(0, "GdipDisposeImage", *image)
  CallFunction(0, "GdiplusShutdown", *token)
  CloseLibrary(0)
  
  return_imagenumber = CreateImage(#PB_Any, width, height, 32)
  StartDrawing(ImageOutput(return_imagenumber))
    DrawAlphaImage(imageid,0,0)
  StopDrawing()
 
  ProcedureReturn return_imagenumber
EndProcedure

Procedure LoadLogo()
img_logo = LoadDecodedImage("imagefilename")

OpenWindow(#Window_Logo,int_mainwindowposx,int_mainwindowpos,300,300,"",#PB_Window_ScreenCentered|#PB_Window_BorderLess|#PB_Window_Invisible)
SetWindowLong_(WindowID(#Window_Logo),#GWL_EXSTYLE,GetWindowLong_(WindowID(#Window_Logo),#GWL_EXSTYLE)|#WS_EX_LAYERED)
CreateGadgetList(WindowID(#Window_Logo))

  hDC = StartDrawing(ImageOutput(img_logo))
    sz.SIZE
    sz\cx = ImageWidth(img_logo)
    sz\cy = ImageHeight(img_logo)
    ContextOffset.POINT
    BlendMode.BLENDFUNCTION
    BlendMode\SourceConstantAlpha = 255
    BlendMode\AlphaFormat = 1
    UpdateLayeredWindow_(WindowID(#Window_Logo), 0, 0, @sz, hDC, @ContextOffset, 0, @BlendMode, 2)
  StopDrawing()

ImageGadget(0,0,0,0,0,ImageID(img_logo))
HideWindow(#Window_Logo,0)

Repeat
  Event = WaitWindowEvent()
  Delay(10)
Until Event = #PB_Event_CloseWindow
End
EndProcedure
I don't see an easy way of integrating the image into my existing window [which has controls] or adding it as a hovering window [and receiving co-ords from a tight loop loop which grabs them].

Messy.

Any ideas/suggestions? Thanks :)

Sample apha-layer image URL: http://www.w3.org/Graphics/PNG/alphatest.png
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

Could you tell me a bit about what the image is for? That is, when and where do you want it to appear and for how long?
BERESHEIT
ctg
User
User
Posts: 21
Joined: Mon Sep 15, 2008 1:43 am

Post by ctg »

netmaestro wrote:Could you tell me a bit about what the image is for? That is, when and where do you want it to appear and for how long?
Sure. I need the image to appear on a window, permanently [as a standard Purebasic image would typically be], along with other controls such as "OK", "Cancel" and a few checkboxes. [similar to the first post in this thread, but with the alpha-layer rendered correctly]:

http://purebasic.fr/english/viewtopic.php?t=32298

If you need more details please let me know ;)

Cheers.
User avatar
netmaestro
PureBasic Bullfrog
PureBasic Bullfrog
Posts: 8451
Joined: Wed Jul 06, 2005 5:42 am
Location: Fort Nelson, BC, Canada

Post by netmaestro »

OK, we might as well dive in and start somewhere. Adjust the string "alphatest.png" in the datasection to point to your image and try this:

Code: Select all

; Yet another useless program by netmaestro
; ..because there will never be enough useless programs

UsePNGImageDecoder()
Declare WindowProc(hwnd, msg, wparam, lparam)

Global image = CatchImage(#PB_Any, ?img, ?imgend-?img)
Global base  = CreateImage(#PB_Any, ImageWidth(image),ImageHeight(image),#PB_Image_DisplayFormat)

OpenWindow(0,0,0,640,480,"",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
SetWindowCallback(@WindowProc())
CreateGadgetList(WindowID(0))
EditorGadget(0,10,10,200,300)
SetGadgetText(0,"Editor")
ButtonGadget(1, 10,320,200,20,"Button")

Repeat
  EventID = WaitWindowEvent()
Until EventID = #PB_Event_CloseWindow

DataSection
  img: IncludeBinary("alphatest.png") : imgend:
EndDataSection

Procedure WindowProc(hwnd, msg, wparam, lparam)
  result=#PB_ProcessPureBasicEvents
  Select msg
    Case #WM_PAINT
      hdc = BeginPaint_(hwnd, ps.PAINTSTRUCT)
      dcOut = StartDrawing(ImageOutput(base))
        ; take a snapshot of the window background where the image will show
        BitBlt_(dcOut, 0,0,ImageWidth(base),ImageHeight(base),hdc,240,10,#SRCCOPY)
        ; draw the alpha image onto it
        DrawAlphaImage(ImageID(image),0,0)
      StopDrawing()
      dcIn = StartDrawing(ImageOutput(base))
        ; render background & image together on the window
        BitBlt_(hdc, 240,10,ImageWidth(base),ImageHeight(base),dcIn,0,0,#SRCCOPY)
      StopDrawing()
      EndPaint_(hwnd, ps)
      result=0
  EndSelect
  ProcedureReturn result
EndProcedure
Merging the alpha image together with the window background prior to rendering is necessary to avoid the ugly result of successive repaints drawing the alpha image over top of previous renderings of itself. By happy coincidence, using this technique the alpha image will also show correctly on a window with a custom window color or pattern brush background as well as a window with a normal background.
BERESHEIT
ctg
User
User
Posts: 21
Joined: Mon Sep 15, 2008 1:43 am

Post by ctg »

That's excellent, thanks mate :D

I'll definitely add this to the codebase for future reference ;) [Maybe it could be put as a code example in PureArchiv? Just an idea]

Your help is awesome. Cheers :P
Post Reply