LoadImage() with a transparent color

Share your advanced PureBasic knowledge/code with the community.
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

LoadImage() with a transparent color

Post by Keya »

Similar to LoadImage(), but lets you specify an RGB color to use as transparent - all pixels of that color will then be set to Alpha channel value = 0, or more specifically, RGBA(0,0,0, 0)

This means you can provide transparency to formats such as tiny 8-bit BMP which doesn't natively support transparency. Can be used as a smaller alternative to the PNG decoder to provide transparency, although it only provides full transparency (like GIF), not variable gradient levels like PNG.

For flexibility you give it an existing imageID (eg from a LoadImage() or CreateImage()) rather than an image filename. The resulting image will of course be 32-bit, but the original image can be of any bit depth.

Supports everything: x86 + x64, Windows + Linux + OSX, and both RGB + BGR byte orders.
Ascii+Unicode both supported/irrelevant. Normal and ReversedY image row orders both supported/irrelevant.
Fast in-memory transform (no slow Point/Plot()'s) with purely 32-bit ops; no slow 8's.

Code: Select all

EnableExplicit

Procedure ByteSwap32(addrLong)
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
    !mov rax, [p.v_addrLong]
    !mov ecx, [rax]
    !bswap ecx
    !mov [rax], ecx
  CompilerElse
    !mov eax, [p.v_addrLong]
    !mov ecx, [eax]
    !bswap ecx
    !mov [eax], ecx
  CompilerEndIf
EndProcedure

Procedure.i LoadImageTransparent (hLoadImg, TransparentColorRGB.l)   ;returns new image handle
  Protected width,height,pitch,imgbase, x,y, hImg32, *RGBA.Long
  width = ImageWidth(hLoadImg)
  height = ImageHeight(hLoadImg)
  hImg32 = CreateImage(#PB_Any, width, height, 32)
  If hImg32 = 0: ProcedureReturn 0: EndIf  
  If StartDrawing(ImageOutput(hImg32)) = 0
    FreeImage(hImg32):   ProcedureReturn 0
  EndIf  
  If DrawingBufferPixelFormat() & #PB_PixelFormat_32Bits_RGB
    TransparentColorRGB | $FF000000    ;Linux & OSX
  Else
    ByteSwap32(@TransparentColorRGB)   ;Windows
    TransparentColorRGB = (TransparentColorRGB >> 8) | $FF000000
  EndIf  
  DrawingMode(#PB_2DDrawing_Default)
  DrawImage(ImageID(hLoadImg),0,0,width,height)
  pitch = DrawingBufferPitch()
  imgbase = DrawingBuffer()
  width-1: height-1  ;to address from base 0
  For y = 0 To height
    *RGBA = imgbase + (y * pitch)
    For x = 0 To width
      If *RGBA\l = TransparentColorRGB
        *RGBA\l = 0  ;= RGBA(0,0,0, 0) ;set Alpha channel to 0 (fully transparent)
      EndIf
      *RGBA+4
    Next x
  Next y
  StopDrawing()  
  ProcedureReturn hImg32
EndProcedure


;### EXAMPLE ###########################################

Procedure TestImage()
  Protected hImg = CreateImage(#PB_Any, 100,100, 24)
  If StartDrawing(ImageOutput(hImg))
    Box(0,0,50,50, RGB(200,20,10))     ;this Red box should show as transparent
    Box(20,20,50,50, RGB(30,40,210))    
  EndIf
  StopDrawing()
  ProcedureReturn hImg
EndProcedure

Define hLoadImg = TestImage()
;Define hLoadImg = LoadImage(#PB_Any, "c:\test.bmp")   ;eg. a tiny 8bit bmp that doesnt natively support transparency

Define TransparentColor.l = RGB(200,20,10)
Define hTransparentImg = LoadImageTransparent(hLoadImg, TransparentColor)
If hTransparentImg = 0
  MessageRequester("Error","Couldnt convert bmp"):    End
EndIf

FreeImage(hLoadImg)

OpenWindow(0,0,0,500,500,"Transparent Drawing",#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ImageGadget(0,0,0,400,400, ImageID(hTransparentImg))
Repeat:Until WaitWindowEvent()=#PB_Event_CloseWindow
This alternative version sets a color to transparent in an EXISTING IMAGE rather than creating a new one. The existing image must be 32bit (the procedure verifies this with a call to ImageDepth):

Code: Select all

Procedure SetImageTransparent (hImg32, TransparentColorRGB.l)    ;returns 1 on success, 0 on error/fail
  Protected width,height,pitch,imgbase, x,y, *RGBA.Long
  If ImageDepth(hImg32) <> 32: ProcedureReturn 0: Endif
  width = ImageWidth(hImg32)
  height = ImageHeight(hImg32)
  If StartDrawing(ImageOutput(hImg32)) = 0
    ProcedureReturn 0
  EndIf  
  If DrawingBufferPixelFormat() & #PB_PixelFormat_32Bits_RGB
    TransparentColorRGB | $FF000000    ;Linux & OSX
  Else
    ByteSwap32(@TransparentColorRGB)   ;Windows
    TransparentColorRGB = (TransparentColorRGB >> 8) | $FF000000
  EndIf  
  DrawingMode(#PB_2DDrawing_Default)
  pitch = DrawingBufferPitch()
  imgbase = DrawingBuffer()  
  width-1: height-1
  For y = 0 To height
    *RGBA = imgbase + (y * pitch)
    For x = 0 To width
      If *RGBA\l = TransparentColorRGB
        *RGBA\l = 0  ;= RGBA(0,0,0, 0) ;set Alpha channel to 0 (fully transparent)
      EndIf
      *RGBA+4
    Next x
  Next y
  StopDrawing()  
  ProcedureReturn 1 ;SUCCESS
EndProcedure
User avatar
minimy
Enthusiast
Enthusiast
Posts: 349
Joined: Mon Jul 08, 2013 8:43 pm

Re: LoadImage() with a transparent color

Post by minimy »

Good Job! This can be interesting to make cartoon look or comic effects. :D
Thank for share!
If translation=Error: reply="Sorry, Im Spanish": Endif
SeregaZ
Enthusiast
Enthusiast
Posts: 619
Joined: Fri Feb 20, 2009 9:24 am
Location: Almaty (Kazakhstan. not Borat, but Triple G)
Contact:

Re: LoadImage() with a transparent color

Post by SeregaZ »

i am create 32 bit image. paint some. then use SetImageTransparent for that image. next i want to repaint that image. i am make box(0, 0, to size of image) for fill old painting, them make some new paint, then use SetImageTransparent again... and it makes strange effect. first time it will shows some data, third time that image will full transparent. it need every time make free old transparent image, then create new one?
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5353
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: LoadImage() with a transparent color

Post by Kwai chang caine »

Works great :D
Thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Dude
Addict
Addict
Posts: 1907
Joined: Mon Feb 16, 2015 2:49 pm

Re: LoadImage() with a transparent color

Post by Dude »

Alternative for transparent BMP images on Windows only (no conversion or swapping needed):

Code: Select all

; Note 1: It's a lot simpler just to use transparent PNG images! :)
; Note 2: Palette index 1 of the BMP file is the transparency color.

OpenWindow(0,200,200,320,120,"test",#PB_Window_SystemMenu)

hbitmap=LoadImage_(0,"C:\Image.bmp",#IMAGE_BITMAP,0,0,#LR_LOADFROMFILE|#LR_LOADTRANSPARENT|#LR_LOADMAP3DCOLORS)

ImageGadget(0,10,10,300,100,hbitmap)

Repeat : Until WaitWindowEvent()=#PB_Event_CloseWindow
Post Reply