The easiest way to use is Colorize::ColorizeImage(0, $0080c0, Colorize::#Multiply)
The effect of the Colorize::#Overlay mode is similar to what is called Duotone.
ColorizeImageSMH allows you to set custom colors for shadow, midtone and highlight.
It's slower but gives you a lot more creative options. Two examples
Colorize::ColorizeImageSMH(0, $800000, $907040, $a0e080)
Colorize::ColorizeImageSMH(0, $800000, $ff80ff, $ffffff)
It is strongly recommended to use 32 bit images since they are processed much faster as 24 bit images.
Code: Select all
; Colorize module by Wilbert
; Latest update Feb 6, 2016
; SSE2 required
; Coefficients used for luma conversion: 0.2126, 0.7152, and 0.0722 (BT.709)
DeclareModule Colorize
Enumeration
#Multiply
#Overlay
#Screen
EndEnumeration
Declare ColorizeImage(Image, Color.l = $ffffff, BlendMode = #Multiply)
Declare ColorizeImageSMH(Image, Shadow = $000000, Midtone = $808080, Highlight = $ffffff)
Declare BMMultiply (*PixArrayIn, *PixArrayOut, PixCount.l , Color.l = $ffffffff, SwapRB = #False)
Declare BMOverlay (*PixArrayIn, *PixArrayOut, PixCount.l , Color.l = $ff808080, SwapRB = #False)
Declare BMScreen (*PixArrayIn, *PixArrayOut, PixCount.l , Color.l = $ff000000, SwapRB = #False)
Declare SMH(*PixArrayIn, *PixArrayOut, PixCount, Shadow = $000000, Midtone = $808080, Highlight = $ffffff, SwapRB = #False)
EndDeclareModule
Module Colorize
DisableDebugger
EnableExplicit
EnableASM
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
Macro rax : eax : EndMacro
Macro rdx : edx : EndMacro
CompilerEndIf
Procedure ColorizeImage(Image, Color.l = $ffffff, BlendMode = #Multiply)
Protected.i x, y, w, h, *db, pf, srb
If IsImage(Image)
Color | $ff000000
w = ImageWidth(Image)
h = ImageHeight(Image)
StartDrawing(ImageOutput(Image))
pf = DrawingBufferPixelFormat()
If pf & (#PB_PixelFormat_32Bits_RGB | #PB_PixelFormat_32Bits_BGR)
; process pixel buffer
If pf & #PB_PixelFormat_32Bits_BGR
srb = #True
EndIf
*db = DrawingBuffer()
Select BlendMode
Case #Overlay : BMOverlay (*db, *db, h * DrawingBufferPitch() >> 2, Color, srb)
Case #Screen : BMScreen (*db, *db, h * DrawingBufferPitch() >> 2, Color, srb)
Default : BMMultiply (*db, *db, h * DrawingBufferPitch() >> 2, Color, srb)
EndSelect
Else
DrawingMode(#PB_2DDrawing_AllChannels)
Protected Dim PixelLine.l(w - 1)
While y < h
; read a line of pixels
x = 0
While x < w
PixelLine(x) = Point(x, y)
x + 1
Wend
; process a line of pixels
Select BlendMode
Case #Overlay : BMOverlay (@PixelLine(), @PixelLine(), w, Color)
Case #Screen : BMScreen (@PixelLine(), @PixelLine(), w, Color)
Default : BMMultiply (@PixelLine(), @PixelLine(), w, Color)
EndSelect
; write a line of pixels
x = 0
While x < w
Plot(x, y, PixelLine(x))
x + 1
Wend
y + 1
Wend
EndIf
StopDrawing()
EndIf
EndProcedure
Procedure ColorizeImageSMH(Image, Shadow = $000000, Midtone = $808080, Highlight = $ffffff)
Protected.i x, y, w, h, *db, pf, srb
If IsImage(Image)
w = ImageWidth(Image)
h = ImageHeight(Image)
StartDrawing(ImageOutput(Image))
pf = DrawingBufferPixelFormat()
If pf & (#PB_PixelFormat_32Bits_RGB | #PB_PixelFormat_32Bits_BGR)
; process pixel buffer
If pf & #PB_PixelFormat_32Bits_BGR
srb = #True
EndIf
*db = DrawingBuffer()
SMH(*db, *db, h * DrawingBufferPitch() >> 2, Shadow, Midtone, Highlight, srb)
Else
DrawingMode(#PB_2DDrawing_AllChannels)
Protected Dim PixelLine.l(w - 1)
While y < h
; read a line of pixels
x = 0
While x < w
PixelLine(x) = Point(x, y)
x + 1
Wend
; process a line of pixels
SMH(@PixelLine(), @PixelLine(), w, Shadow, Midtone, Highlight)
; write a line of pixels
x = 0
While x < w
Plot(x, y, PixelLine(x))
x + 1
Wend
y + 1
Wend
EndIf
StopDrawing()
EndIf
EndProcedure
Procedure BMMultiply(*PixArrayIn, *PixArrayOut, PixCount.l , Color.l = $ffffffff, SwapRB = #False)
; load pixel array pointers and count
mov rax, *PixArrayIn
mov rdx, *PixArrayOut
mov ecx, PixCount
; load luma constant and color
movd xmm2, Color
!movq xmm1, [colorize.l_mluma_rgba]
!punpcklbw xmm2, xmm2
; swap red and blue channels if requested
bt SwapRB, 0
!jnc colorize.l_mloop
!pshuflw xmm1, xmm1, 11000110b
!pshuflw xmm2, xmm2, 11000110b
; main loop
!colorize.l_mloop:
; load pixel color
movd xmm0, [rax]
!punpcklbw xmm0, xmm0
; convert pixel color to luma
!pmulhuw xmm0, xmm1
!pshuflw xmm3, xmm0, 11010010b
!paddw xmm0, xmm3
!pshuflw xmm3, xmm3, 11010010b
!paddw xmm0, xmm3
; multiply with color
!pmulhuw xmm0, xmm2
; convert back to 32 bit color value
!psrlw xmm0, 8
!packuswb xmm0, xmm0
movd [rdx], xmm0
add rax, 4
add rdx, 4
!sub ecx, 1
!jnz colorize.l_mloop
ProcedureReturn
!colorize.l_mluma_rgba: dq 0x5555127cb717366d ; aaaa-bbbb-gggg-rrrr
EndProcedure
Procedure BMOverlay(*PixArrayIn, *PixArrayOut, PixCount.l , Color.l = $ff808080, SwapRB = #False)
; load pixel array pointers and count
mov rax, *PixArrayIn
mov rdx, *PixArrayOut
mov ecx, PixCount
; load luma constant and color
movd xmm2, Color
!movq xmm1, [colorize.l_oluma_rgba]
!punpcklbw xmm2, xmm2
; swap red and blue channels if requested
bt SwapRB, 0
!jnc colorize.l_oloop
!pshuflw xmm1, xmm1, 11000110b
!pshuflw xmm2, xmm2, 11000110b
; main loop
!colorize.l_oloop:
; load pixel color
movd xmm0, [rax]
!punpcklbw xmm0, xmm0
; convert pixel color to luma
!pmulhuw xmm0, xmm1
!pshuflw xmm3, xmm0, 11010010b
!paddw xmm0, xmm3
!pshuflw xmm3, xmm3, 11010010b
!paddw xmm0, xmm3
; create inversion mask
!pxor xmm5, xmm5
!pcmpgtw xmm5, xmm0
; invert color and luma if needed
!movdqa xmm3, xmm2
!pxor xmm0, xmm5
!pxor xmm3, xmm5
; luma * 2
!paddw xmm0, xmm0
; multiply
!pmulhuw xmm0, xmm3
; invert back result if needed
!pxor xmm0, xmm5
; convert back to 32 bit color value
!psrlw xmm0, 8
!packuswb xmm0, xmm0
movd [rdx], xmm0
add rax, 4
add rdx, 4
!sub ecx, 1
!jnz colorize.l_oloop
ProcedureReturn
!colorize.l_oluma_rgba: dq 0x2aaa127cb717366d ; aaaa-bbbb-gggg-rrrr
EndProcedure
Procedure BMScreen(*PixArrayIn, *PixArrayOut, PixCount.l , Color.l = $ff000000, SwapRB = #False)
; load pixel array pointers and count
mov rax, *PixArrayIn
mov rdx, *PixArrayOut
mov ecx, PixCount
; load luma constant and color
movd xmm2, Color
!movq xmm1, [colorize.l_sluma_rgba]
!punpcklbw xmm2, xmm2
; create inversion mask
!pcmpeqw xmm5, xmm5
!psrlq xmm5, 16
; invert color
!pxor xmm2, xmm5
; swap red and blue channels if requested
bt SwapRB, 0
!jnc colorize.l_sloop
!pshuflw xmm1, xmm1, 11000110b
!pshuflw xmm2, xmm2, 11000110b
; main loop
!colorize.l_sloop:
; load pixel color
movd xmm0, [rax]
!punpcklbw xmm0, xmm0
; convert pixel color to luma
!pmulhuw xmm0, xmm1
!pshuflw xmm3, xmm0, 11010010b
!paddw xmm0, xmm3
!pshuflw xmm3, xmm3, 11010010b
!paddw xmm0, xmm3
; invert luma
!pxor xmm0, xmm5
; multiply with inverted color
!pmulhuw xmm0, xmm2
; invert back to 32 bit color value
!pxor xmm0, xmm5
!psrlw xmm0, 8
!packuswb xmm0, xmm0
movd [rdx], xmm0
add rax, 4
add rdx, 4
!sub ecx, 1
!jnz colorize.l_sloop
ProcedureReturn
!colorize.l_sluma_rgba: dq 0x5555127cb717366d ; aaaa-bbbb-gggg-rrrr
EndProcedure
Procedure SMH(*PixArrayIn, *PixArrayOut, PixCount, Shadow = $000000, Midtone = $808080, Highlight = $ffffff, SwapRB = #False)
CompilerIf #PB_Compiler_OS = #PB_OS_Windows And #PB_Compiler_Processor = #PB_Processor_x64
!movups [rsp - 16], xmm6 ; backup xmm6 (required for Win64)
CompilerEndIf
; load pixel array pointers and count
mov rax, *PixArrayIn
mov rdx, *PixArrayOut
!mov ecx, [p.v_PixCount]
; load luma constant and color
!movq xmm1, [colorize.l_smhluma_rgba]
!movd xmm2, [p.v_Shadow]
!movd xmm3, [p.v_Midtone]
!movd xmm4, [p.v_Highlight]
!pcmpeqw xmm5, xmm5
!psllq xmm5, 48
!punpcklbw xmm2, xmm2
!punpcklbw xmm3, xmm3
!punpcklbw xmm4, xmm4
!por xmm2, xmm5
!por xmm3, xmm5
!por xmm4, xmm5
!pxor xmm4, xmm2
!pshuflw xmm6, xmm5, 00111111b
; swap red and blue channels if requested
bt SwapRB, 0
!jnc colorize.l_smhloop
!pshuflw xmm1, xmm1, 11000110b
!pshuflw xmm2, xmm2, 11000110b
!pshuflw xmm3, xmm3, 11000110b
!pshuflw xmm4, xmm4, 11000110b
; main loop
!colorize.l_smhloop:
; load pixel color
movd xmm0, [rax]
!punpcklbw xmm0, xmm0
; convert pixel color to luma
!pmulhuw xmm0, xmm1
!pshuflw xmm5, xmm0, 11010010b
!paddw xmm0, xmm5
!pshuflw xmm5, xmm5, 11010010b
!paddw xmm0, xmm5
; create mask
!pxor xmm5, xmm5
!pcmpgtw xmm5, xmm0
; change luma range
!paddw xmm0, xmm0
!pxor xmm0, xmm5
!pxor xmm0, xmm6
; select shadow or highlight color
!pand xmm5, xmm4
!pxor xmm5, xmm2
; blend with mid color
!pmulhuw xmm5, xmm0
!pxor xmm0, xmm6
!pmulhuw xmm0, xmm3
!paddw xmm0, xmm5
; convert back to 32 bit color value
!psrlw xmm0, 8
!packuswb xmm0, xmm0
movd [rdx], xmm0
add rax, 4
add rdx, 4
!sub ecx, 1
!jnz colorize.l_smhloop
CompilerIf #PB_Compiler_OS = #PB_OS_Windows And #PB_Compiler_Processor = #PB_Processor_x64
!movups xmm6, [rsp - 16]
CompilerEndIf
ProcedureReturn
!colorize.l_smhluma_rgba: dq 0x1555127cb717366d ; aaaa-bbbb-gggg-rrrr
EndProcedure
EndModule