Page 2 of 2

Re: Emulating Photoshop's blending modes

Posted: Sat Jul 23, 2011 9:54 am
by [blendman]
Hi

Here is another way to do blendmode with imagegadget or canvasgadget :

Code: Select all

;{ infos
; test blendmode by blendman 07/2011
; pb : 4.60
;}

;{ variables & globales
Enumeration
  #fond ; the "background"
  #layer1
  #alpha_layer1
  #tempo ; temporary image
  #result
  #imagefinal ; final image
EndEnumeration

Global mode.b = 1
;}

;{ init
If UseJPEGImageDecoder() =0 Or UsePNGImageDecoder() =0
  End
EndIf
;}

;{ declare
Declare affiche_img() ; affiche = show in french
Declare bm_add(x, y, SourceColor, TargetColor)
Declare bm_multiply(x, y, SourceColor, TargetColor)
Declare bm_overlay(x, y, SourceColor, TargetColor)
Declare bm_screen(x, y, SourceColor, TargetColor)
Declare Changemode()
;}

;{ open window & create image
If OpenWindow(0, 0, 0, 400, 200, "2DDrawing Example - Blendmode test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

  ;{ menu
  If CreateMenu(0,WindowID(0))
    MenuTitle("BlendMode")
    MenuItem(1,"Next BlendMode"+Chr(9)+"+")
    MenuItem(2,"Previous BlendMode"+Chr(9)+"-")
  EndIf
  ;}

  ;{ images
  LoadImage(#fond, #PB_Compiler_Home + "examples/sources/data/clouds.jpg") ; the "clouds" image for the background layer
   
  ; the layer1 is a simple red circle
  CreateImage(#layer1,400,200,32)
  StartDrawing(ImageOutput(#layer1))
  DrawingMode(#PB_2DDrawing_AlphaChannel)   
  Box(0,0,400,200,RGBA(0,0,0,0))
  DrawingMode(#PB_2DDrawing_AlphaBlend)   
  Circle(300, 100, 100,RGBA(255,0,0,255))
  StopDrawing()
 
  CopyImage(#layer1,#alpha_layer1)
 
  ; copyfor the temporary image needed for the alpha channel
  CreateImage(#tempo,400,200,32)
  StartDrawing(ImageOutput(#tempo))
  DrawingMode(#PB_2DDrawing_AlphaChannel)   
  Box(0,0,400,200,RGBA(0,0,0,0))
  StopDrawing()
   
  If CreateImage(#imagefinal, 400, 200,32)     
    affiche_Img()   
  EndIf
  ;}

  ;{ shortcut
  AddKeyboardShortcut(0,#PB_Shortcut_Add,1)
  AddKeyboardShortcut(0,#PB_Shortcut_Subtract,2)
  ;}
Else
  MessageRequester("Error", "Can't open a window")
  End
EndIf
;}

Repeat   
  Changemode()
Until Event = #PB_Event_CloseWindow

;{ procedure
Procedure  affiche_img()
  ; calcul of the blendmode, only on the alpha not transparent, on the layer1
  If StartDrawing(ImageOutput(#tempo))   
    ; Background image, we can use a transprent image if we want
    DrawingMode(#PB_2DDrawing_AlphaBlend)     
    DrawImage(ImageID(#fond), 0, 0, 400, 200)
    ; than, we draw the image on the top (the layer1), with the blendmode choosen
    If mode =0 ; normal
      DrawingMode(#PB_2DDrawing_Default)
    ElseIf mode <>0     
      DrawingMode(#PB_2DDrawing_CustomFilter)
      If mode = 1 ; add
        CustomFilterCallback(@bm_add())
      ElseIf mode = 2 ; multiply
        CustomFilterCallback(@bm_multiply())
      ElseIf mode = 3 ; overlay
        CustomFilterCallback(@bm_overlay())
      ElseIf mode = 4 ; screen
        CustomFilterCallback(@bm_screen())       
      EndIf
    EndIf   
    DrawAlphaImage(ImageID(#layer1),0,0) 
    DrawingMode(#PB_2DDrawing_AlphaChannel)   
    DrawAlphaImage(ImageID(#alpha_layer1),0,0)
    StopDrawing()
  EndIf
 
  ; then, we have the result, the final image
  If StartDrawing(ImageOutput(#imagefinal))   
    DrawImage(ImageID(#fond), 0, 0, 400, 200) ;  background image
    DrawingMode(#PB_2DDrawing_AlphaBlend)
    DrawAlphaImage(ImageID(#tempo),0,0)
    StopDrawing()
  EndIf
  ImageGadget(0, 0, 0, 400, 200, ImageID(#imagefinal))
     
  Select mode
    Case 0
      SetWindowTitle(0,"Mode : Normal ")
    Case 1
      SetWindowTitle(0,"Mode : Add")
    Case 2
      SetWindowTitle(0,"Mode : Multiply")
    Case 3
      SetWindowTitle(0,"Mode : Overlay")
    Case 4
      SetWindowTitle(0,"Mode : Screen")
  EndSelect   
EndProcedure

Procedure.l min(a.l,b.l)
  If a>b
    ProcedureReturn b
  EndIf
  ProcedureReturn a
EndProcedure

Procedure bm_screen(x, y, SourceColor, TargetColor)
  ; color = 255 - ( ( 255 - bottom ) * ( 255 - top ) ) / 255
  red = 255 -((255-Red(SourceColor))*(255-Red(TargetColor)))/255
  green = 255 -((255-Green(SourceColor))*(255-Green(TargetColor)))/255
  blue = 255 -((255-Blue(SourceColor))*(255-Blue(TargetColor)))/255   
  ProcedureReturn RGBA(red,green,blue, Alpha(TargetColor))
EndProcedure

Procedure bm_add(x, y, SourceColor, TargetColor)
  ; color = top >= 255? 255 : min(bottom * 255 / (255 - top), 255)
  If  Red(TargetColor) >= 255
    Red.l= 255
  Else
    result.l= 255 - Red(SourceColor)
    If result >0     
      Red =min(Red(TargetColor) * 255 / (result), 255)
    ElseIf result =0
      red=min(Red(TargetColor) * 255, 255)
    EndIf   
  EndIf

  If  Blue(TargetColor) >= 255
    blue = 255
  Else
    result= 255 - Blue(SourceColor)
    If result >0
      blue=min(Blue(TargetColor) * 255 / (result), 255)
    ElseIf result =0
      blue = min(Blue(TargetColor) * 255, 255)     
    EndIf   
  EndIf

  If  Green(TargetColor) >= 255
    Green = 255
  Else
    result= 255 - Green(SourceColor)
    If result >0
      Green =min(Green(TargetColor) * 255 / (result), 255)
    ElseIf result =0
      green =min(Green(TargetColor) * 255, 255)
    EndIf   
  EndIf
   
  ProcedureReturn RGBA(red, green, blue, Alpha(TargetColor))
EndProcedure

Procedure bm_multiply(x, y, SourceColor, TargetColor)
  ProcedureReturn RGBA((Red(SourceColor)*Red(TargetColor))/255,(Green(SourceColor)*Green(TargetColor))/255,(Blue(SourceColor)*Blue(TargetColor))/255, Alpha(TargetColor)*Alpha(TargetColor)/255)
EndProcedure

Macro overlay(color_source,color_targ, colo)
  If  color_source <128
    colo = ( 2 * color_source*color_targ ) / 255
  Else
    colo =255 - ( 2 * ( 255 - color_source ) * ( 255 - color_targ  ) / 255 )
  EndIf
EndMacro

Procedure bm_overlay(x, y, SourceColor, TargetColor)
  ; color = bottom < 128 ? ( 2 * bottom * top ) / 255 : 255 - ( 2 * ( 255 - bottom ) * ( 255 - top ) / 255 )
  overlay(Red(TargetColor),Red(SourceColor), red)
  overlay(Blue(TargetColor),Blue(SourceColor), blue)
  overlay(Green(TargetColor),Green(SourceColor), green)

  ProcedureReturn RGBA(red, green, blue, Alpha(TargetColor))
EndProcedure

Procedure Changemode()
  Event = WaitWindowEvent()
  Select event
    Case #PB_Event_CloseWindow
      End

    Case #PB_Event_Menu
      Select EventMenu()
        Case 1
          If mode<4
            mode +1
          Else
            mode = 0
          EndIf
          affiche_img()
         
        Case 2
          If mode>0
            mode -1
          Else
            mode = 4
          EndIf
          affiche_img()         
      EndSelect     
  EndSelect
EndProcedure
;}
As you can see, The alpha channel is used for the overlay mode or the other mode, because wa have to use it to not have the rest of the image bleck or white (if the transparency is rgba(0,0,0,0) or rgba(255,255,255,0) for the alpha channel.

I hope this could be usefull for you ;).

It could be optimised I think, but it's a good start too :).