Page 1 of 1

Creating and editing Icons on runtime...

Posted: Sun Sep 07, 2003 10:03 pm
by freak
Hi all,

Since it is not possible, to create or paint Icon with the PureBasic Image
commands, i created 2 little procedures to still make this possible.

Since it is not possible to draw directly on a Icon at all with Windows API,
my procedures just copy the icon data to usual PB Images, where you
can then modify them, and create a new Icon out of them. You can use the
same #Image number for the icon when you recreate it, so you don't
waste any recources. All stuff is freed correctly.

Use the SplittIcon() function, to modify an existing icon, by extracting the
2 different Images out of them, and later combine them again with
CreateIcon()

As shown below, CreateIcon() can also be used to create a new Icon out
out of 2 Images.

Here we go:

Code: Select all

; This Function creates 2 new Images out of the Icon.
; The Image specified in 'Image.l' will hold the Icon Image, and
; the one in 'Mask.l' will hold the Icon AND Mask. This is a Black&White
; Bitmap image, where all white parts are transparent in the Icon.
; All numbers here are PB #Image numbers.
Procedure SplittIcon(Icon.l, Image.l, Mask.l)
  UseImage(Icon)
  If CreateImage(Image, ImageWidth(), ImageHeight())
      DC.l = StartDrawing(ImageOutput())
      DrawIconEx_(DC, 0, 0, UseImage(Icon), 0, 0, 0, 0, 2)
    StopDrawing()
  EndIf
  
  If CreateImage(Mask, ImageWidth(), ImageHeight())
    DC.l = StartDrawing(ImageOutput())
      DrawIconEx_(DC, 0, 0, UseImage(Icon), 0, 0, 0, 0, 1)
    StopDrawing()
  EndIf
  UseImage(Image)
EndProcedure


; This Function creates a new Icon, out of the Images specified in Image and Mask.
; Both must be of the same size, and the Mask may only contain black and white.
Procedure.l CreateIcon(Icon.l, Image.l, Mask.l)
  Protected *Bitmap.LONG
  
  UseImage(Image)
  CreateImage(Icon, ImageWidth(), ImageHeight())  
  !extrn _PB_Image_CurrentObject
  !mov eax, [_PB_Image_CurrentObject]
  !mov [esp+12], eax
  
  DeleteObject_(*Bitmap\l)
  
  NewIcon.ICONINFO
  NewIcon\fIcon = #true
  NewIcon\hbmMask = UseImage(Mask)
  NewIcon\hbmColor = UseImage(Image)
  *Bitmap\l = CreateIconIndirect_(@NewIcon) 
  
  ProcedureReturn UseImage(Icon)
EndProcedure

And now, a little commented example:

Code: Select all

#Icon  = 0 
#Image = 1 
#Mask  = 2 

Font.l = LoadFont(0, "Times", 8)

If OpenWindow(0, 0, 0, 100, 50, #PB_Window_SystemMenu|#PB_Window_ScreenCentered, "Icon")
  If CreateGadgetList(WindowID())

    ; Create and draw the Icon Image:
    CreateImage(#Image, 16, 16)    
    StartDrawing(ImageOutput())      
      DrawingMode(1)
      FrontColor($FF, $00, $00)
      Locate(2, 2)
      DrawingFont(Font)
      DrawText("PB")      
    StopDrawing()
    
    ; See the result:
    ImageGadget(1, 20, 20, 32, 32, ImageID())
    
    ; Create and draw the mask. Everything that is white
    ; will be transparent, everything black will be displayed.
    ; so we just draw the same as above in black on a white background.
    CreateImage(#Mask, 16, 16)    
    StartDrawing(ImageOutput())
      Box(1, 1, 14, 14, $FFFFFF)
      FrontColor($00, $00, $00)      
      DrawingMode(1)      
      Locate(2, 2)
      DrawingFont(Font)
      DrawText("PB")   
    StopDrawing()
    
    ; Yet another test:
    ImageGadget(1, 60, 20, 32, 32, ImageID())
    
    ; Now we make our new #Icon out of these 2...
    CreateIcon(#Icon, #Image, #Mask)
    
    ; And place it in the systray.
    AddSysTrayIcon(0, WindowID(), ImageID())
    

    Repeat
    Until WaitWindowEvent() = #PB_EventCloseWindow
  EndIf
EndIf
End

At last, another small example, showing how to make a Progressbar-like
Icon in the systray:

Code: Select all

#Icon  = 0
#Image = 1
#Mask  = 2

If OpenWindow(0, 0, 0, 100, 50, #PB_Window_SystemMenu|#PB_Window_ScreenCentered, "Icon")
  If CreateGadgetList(WindowID())

    CreateImage(#Image, 16, 16)    
    StartDrawing(ImageOutput())      
      Box(1, 0, 14, 3, $0000FF) 
      Box(1, 4, 14, 3, $0000FF)
      Box(1, 8, 14, 3, $0000FF)
      Box(1, 12, 14, 3, $0000FF)
    StopDrawing()
    
    ImageGadget(1, 20, 20, 32, 32, ImageID())
    
    CreateImage(#Mask, 16, 16)
    ImageGadget(2, 60, 20, 32, 32, ImageID())
    CreateIcon(#Icon, #Image, #Mask)
    AddSysTrayIcon(0, WindowID(), ImageID())
    
    time = 0
    a = 0
    Repeat
      If time<GetTickCount_()-750
        UseImage(#Mask)
        StartDrawing(ImageOutput())
          Box(0, 0, 16, 16, $FFFFFF)
          Select a         
            Case 1: Box(0,11, 16, 16, 0)
            Case 2: Box(0, 7, 16, 16, 0)
            Case 3: Box(0, 3, 16, 16, 0)
            Case 4: Box(0, 0, 16, 16, 0)
            Default: a = 0
          EndSelect
        StopDrawing()
        SetGadgetState(2, ImageID())
        CreateIcon(#Icon, #Image, #Mask)
        ChangeSysTrayIcon(0, ImageID())
        
        a + 1
        time = GetTickCount_()
      EndIf
    
      event = WindowEvent()
      If event = #PB_EventCloseWindow
        End
      ElseIf event = 0
        Delay(1)
      EndIf
    ForEver
  EndIf
EndIf
End

That's it, hope it helps somebody...

Timo

Posted: Wed Sep 10, 2003 11:58 am
by TronDoc
very slick!

just notes to myself:
the first example needs
these defined:

#Icon = 0
#Image = 1
#Mask = 2

and I had to REM out the lines:
;DrawingFont(Font)
because I didn't figure out how
to get it to work with them in.

too cool! many thanks
for the code.

Joe

Posted: Wed Sep 10, 2003 12:31 pm
by freak
cut&paste error again :roll:

I forgot those at the beginning of the first example:

Code: Select all

#Icon  = 0
#Image = 1
#Mask  = 2

Font.l = LoadFont(0, "Times", 8)
Timo

Posted: Thu Sep 11, 2003 4:51 am
by TronDoc
:lol: thanks again! --jb

Posted: Sun Aug 13, 2006 5:46 am
by netmaestro
I was trying to use this in PB 4.0, but I was having trouble with the asm part which seems not to work any more. I confess I didn't really understand how it worked anyway, so I took it out and updated the proc to work without it. I don't know if this is any good or not, but here it is along with the second example to test:

Code: Select all

; This Function creates 2 new Images out of the Icon. 
; The Image specified in 'Image.l' will hold the Icon Image, and 
; the one in 'Mask.l' will hold the Icon AND Mask. This is a Black&White 
; Bitmap image, where all white parts are transparent in the Icon. 
; All numbers here are PB #Image numbers. 

Procedure SplittIcon(Icon.l, Image.l, Mask.l) 

  If CreateImage(Image, ImageWidth(Icon), ImageHeight(Icon)) 
      DC.l = StartDrawing(ImageOutput(Image)) 
      DrawIconEx_(DC, 0, 0, ImageID(Icon), 0, 0, 0, 0, 2) 
    StopDrawing() 
  EndIf 
  
  If CreateImage(Mask, ImageWidth(Mask), ImageHeight(Mask)) 
    DC.l = StartDrawing(ImageOutput(Mask)) 
      DrawIconEx_(DC, 0, 0, ImageID(Icon), 0, 0, 0, 0, 1) 
    StopDrawing() 
  EndIf 
 
EndProcedure 

; This Function creates a new Icon, out of the Images specified in Image and Mask. 
; Both must be of the same size, and the Mask may only contain black and white. 
Procedure.l CreateIcon(Image.l, Mask.l) 
  Static icon.l
  
  NewIcon.ICONINFO 
  NewIcon\fIcon = #True 
  NewIcon\hbmMask = ImageID(Mask) 
  NewIcon\hbmColor = ImageID(Image) 
  
  If icon : DestroyIcon_(icon) : EndIf
  icon = CreateIconIndirect_(@NewIcon) 
  
  ProcedureReturn icon
EndProcedure 
 
#Image = 1 
#Mask  = 2 

If OpenWindow(0, 0, 0, 100, 50, "Icon", #PB_Window_SystemMenu|#PB_Window_ScreenCentered) 
  If CreateGadgetList(WindowID(0)) 

    CreateImage(#Image, 16, 16)    
    StartDrawing(ImageOutput(#Image))      
      Box(1, 0,  14, 3, $0000FF) 
      Box(1, 4,  14, 3, $0000FF) 
      Box(1, 8,  14, 3, $0000FF) 
      Box(1, 12, 14, 3, $0000FF) 
    StopDrawing() 
    
    ImageGadget(1, 20, 20, 32, 32, ImageID(#Image)) 
    
    CreateImage(#Mask, 16, 16) 
    ImageGadget(2, 60, 20, 32, 32, ImageID(#Mask)) 
    Icon=CreateIcon(#Image, #Mask) 
    AddSysTrayIcon(0, WindowID(0), Icon) 
    
    time = 0 
    a = 0 
    Repeat 
      If time<GetTickCount_()-750 

        StartDrawing(ImageOutput(#Mask)) 
          Box(0, 0, 16, 16, $FFFFFF) 
          Select a          
            Case 1: Box(0,11, 16, 16, 0) 
            Case 2: Box(0, 7, 16, 16, 0) 
            Case 3: Box(0, 3, 16, 16, 0) 
            Case 4: Box(0, 0, 16, 16, 0) 
            Default: a = 0 
          EndSelect 
        StopDrawing() 
        SetGadgetState(2, ImageID(#Mask)) 
        icon = CreateIcon(#Image, #Mask) 
        ChangeSysTrayIcon(0, Icon) 
        
        a + 1 
        time = GetTickCount_() 
      EndIf 
    
      event = WindowEvent() 
      If event = #PB_Event_CloseWindow 
        End 
      ElseIf event = 0 
        Delay(1) 
      EndIf 
    ForEver 
  EndIf 
EndIf 
End 
The asm part seemed to somehow convert the PB image into a valid icon, which seems neat, but is there an advantage to the icon being a PB image with an image#? And, if there is, what would the PB 4 way of accomplishing that be?