ColorMatrix filter
Posted: Tue Aug 06, 2013 7:31 am
A color matrix filter (requires SSE2).
The 4 x 5 matrix works the same as the matrix from the ColorMatrixFilter class from the Adobe Flash Player.
ColorMatrix.pbi
Example
The 4 x 5 matrix works the same as the matrix from the ColorMatrixFilter class from the Adobe Flash Player.
ColorMatrix.pbi
Code: Select all
; *** Color Matrix Filter ***
; Min CPU requirement : SSE2 (Pentium 4 or Athlon 64)
Structure CM_Matrix
m.f[20]
EndStructure
Structure CM_Filter
mem.b[96]
EndStructure
Procedure CM_GetIdentityMatrix(*Matrix.CM_Matrix)
; Get an identity matrix
Protected.i row, col
For row = 0 To 3
For col = 0 To 4
If row = col
*Matrix\m[row * 5 + col] = 1
Else
*Matrix\m[row * 5 + col] = 0
EndIf
Next
Next
EndProcedure
Procedure CM_GetMatrix(*Filter.CM_Filter, *Matrix.CM_Matrix)
; Get the matrix of a filter
Protected.i row, col, *M.CM_Matrix = (*Filter + 15) & -16
If *Filter\mem[95]
For row = 0 To 3
For col = 0 To 4
*Matrix\m[row * 5 + col] = *M\m[col * 4 + row]
Next
Next
Else
CM_GetIdentityMatrix(*Matrix)
EndIf
EndProcedure
Procedure CM_SetMatrix(*Filter.CM_Filter, *Matrix.CM_Matrix = 0)
; Set the matrix of a filter
Protected.i row, col, *M.CM_Matrix = (*Filter + 15) & -16
For row = 0 To 3
For col = 0 To 4
If *Matrix
*M\m[col * 4 + row] = *Matrix\m[row * 5 + col]
Else
If row = col
*M\m[col * 4 + row] = 1
Else
*M\m[col * 4 + row] = 0
EndIf
EndIf
Next
Next
*Filter\mem[95] = 1; maxtrix is set flag
EndProcedure
Procedure CM_ConcatMatrix(*Filter.CM_Filter, *Matrix.CM_Matrix)
; Concatenates the matrix
Protected.i i, row, col, current.CM_Matrix, new.CM_MAtrix
CM_GetMatrix(*Filter, @current)
For row = 0 To 3
For col = 0 To 4
new\m[i + col] = *Matrix\m[i] * current\m[col] +
*Matrix\m[i + 1] * current\m[col + 5] +
*Matrix\m[i + 2] * current\m[col + 10] +
*Matrix\m[i + 3] * current\m[col + 15]
If col = 4 : new\m[i + 4] + *Matrix\m[i + 4] : EndIf
Next
i + 5
Next
CM_SetMatrix(*Filter, @new)
EndProcedure
Procedure CM_ApplyToBuffer(*PixelBuf32, *Filter.CM_Filter, NumPixels, ByteOrder = 0)
; Byte order 0 : RGBA
; Byte order 1 : BGRA
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
CompilerIf #PB_Compiler_OS = #PB_OS_Windows
; xmm6 and xmm7 need to be preserved on Windows x64
Dim xmm_storage.q(3)
!mov rax, [p.a_xmm_storage]
!movdqu [rax], xmm6
!movdqu [rax + 16], xmm7
CompilerEndIf
!mov rdx, [p.p_Filter]
!add rdx, 15
!and rdx, -16
!movaps xmm0, [rdx]
!movaps xmm1, [rdx + 16]
!movaps xmm2, [rdx + 32]
!movaps xmm3, [rdx + 48]
!movaps xmm4, [rdx + 64]
!mov rdx, [p.p_PixelBuf32]
CompilerElse
!mov edx, [p.p_Filter]
!add edx, 15
!and edx, -16
!movaps xmm0, [edx]
!movaps xmm1, [edx + 16]
!movaps xmm2, [edx + 32]
!movaps xmm3, [edx + 48]
!movaps xmm4, [edx + 64]
!mov edx, [p.p_PixelBuf32]
CompilerEndIf
!mov ecx, [p.v_ByteOrder]
!shr ecx, 1
!mov ecx, [p.v_NumPixels]
!jnc cm_apply_loop
!pshufd xmm0, xmm0, 11000110b
!pshufd xmm1, xmm1, 11000110b
!pshufd xmm2, xmm2, 11000110b
!pshufd xmm3, xmm3, 11000110b
!pshufd xmm4, xmm4, 11000110b
!cm_apply_loop:
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!movd xmm5, [rdx]
CompilerElse
!movd xmm5, [edx]
CompilerEndIf
!pxor xmm6, xmm6
!punpcklbw xmm5, xmm6
!punpcklwd xmm5, xmm6
!cvtdq2ps xmm5, xmm5
!pshufd xmm6, xmm5, 00000000b
!mulps xmm6, xmm0
!addps xmm6, xmm4
!pshufd xmm7, xmm5, 01010101b
!mulps xmm7, xmm1
!addps xmm6, xmm7
!pshufd xmm7, xmm5, 10101010b
!mulps xmm7, xmm2
!addps xmm6, xmm7
!pshufd xmm7, xmm5, 11111111b
!mulps xmm7, xmm3
!addps xmm6, xmm7
!cvtps2dq xmm5, xmm6
!packssdw xmm5, xmm5
!packuswb xmm5, xmm5
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!movd [rdx], xmm5
!add rdx, 4
CompilerElse
!movd [edx], xmm5
!add edx, 4
CompilerEndIf
!dec ecx
!jnz cm_apply_loop
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64 And #PB_Compiler_OS = #PB_OS_Windows
!movdqu xmm6, [rax]
!movdqu xmm7, [rax + 16]
CompilerEndIf
EndProcedure
Procedure CM_ApplyToImage(Image, *Filter.CM_Filter)
Protected.i w, x, y, max_x, max_y
Protected.i addr, pitch, byte_order
If StartDrawing(ImageOutput(Image))
w = OutputWidth()
max_y = OutputHeight() - 1
If DrawingBufferPixelFormat() & $60; 32 bit buffer ?
addr = DrawingBuffer()
pitch = DrawingBufferPitch()
byte_order = DrawingBufferPixelFormat() >> 6 & 1
For y = 0 To max_y
CM_ApplyToBuffer(addr, *Filter, w, byte_order)
addr + pitch
Next
Else
max_x = w - 1
Dim Buffer.l(max_x)
DrawingMode(#PB_2DDrawing_AllChannels)
For y = 0 To max_y
For x = 0 To max_x
Buffer(x) = Point(x, y)
Next
CM_ApplyToBuffer(@Buffer(), *Filter, w)
For x = 0 To max_x
Plot(x, y, Buffer(x))
Next
Next
EndIf
StopDrawing()
EndIf
EndProcedure
Procedure.l CM_Apply(Color.l, *Filter.CM_Filter)
; Apply the filter to a color
!pxor xmm1, xmm1
!movd xmm0, [p.v_Color]
!punpcklbw xmm0, xmm1
!punpcklwd xmm0, xmm1
!cvtdq2ps xmm0, xmm0
!pshufd xmm1, xmm0, 01010101b
!pshufd xmm2, xmm0, 10101010b
!pshufd xmm3, xmm0, 11111111b
!pshufd xmm0, xmm0, 00000000b
CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
!mov rdx, [p.p_Filter]
!add rdx, 15
!and rdx, -16
!mulps xmm0, [rdx]
!mulps xmm1, [rdx + 16]
!mulps xmm2, [rdx + 32]
!mulps xmm3, [rdx + 48]
!addps xmm0, [rdx + 64]
CompilerElse
!mov edx, [p.p_Filter]
!add edx, 15
!and edx, -16
!mulps xmm0, [edx]
!mulps xmm1, [edx + 16]
!mulps xmm2, [edx + 32]
!mulps xmm3, [edx + 48]
!addps xmm0, [edx + 64]
CompilerEndIf
!addps xmm0, xmm1
!addps xmm0, xmm2
!addps xmm0, xmm3
!cvtps2dq xmm0, xmm0
!packssdw xmm0, xmm0
!packuswb xmm0, xmm0
!movd eax, xmm0
ProcedureReturn
EndProcedure
Procedure CM_AdjustContrast(*Filter.CM_Filter, value.f)
; Adjust contrast (-100 - 100)
Protected.f s, o, m.CM_Matrix
If value
s = value / 100 + 1
o = 128 * (1 - s)
CM_GetIdentityMatrix(@m)
m\m[0] = s : m\m[4] = o
m\m[6] = s : m\m[9] = o
m\m[12] = s : m\m[14] = o
CM_ConcatMatrix(*Filter, @m)
EndIf
EndProcedure
Procedure CM_AdjustBrightness(*Filter.CM_Filter, value.f)
; Adjust brightness (-255 - 255)
Protected m.CM_Matrix
If value
CM_GetIdentityMatrix(@m)
m\m[4] = value
m\m[9] = value
m\m[14] = value
CM_ConcatMatrix(*Filter, @m)
EndIf
EndProcedure
Procedure CM_AdjustSaturation(*Filter.CM_Filter, value.f)
; Adjust saturation (-100 - 100)
Protected.f v, r, g, b, m.CM_Matrix
If value
v = -value / 100
r = 0.299 * v
g = 0.587 * v
b = 0.114 * v
v = 1 - v
CM_GetIdentityMatrix(@m)
m\m[0] = r + v : m\m[1] = g : m\m[2] = b
m\m[5] = r : m\m[6] = g + v : m\m[7] = b
m\m[10] = r : m\m[11] = g : m\m[12] = b + v
CM_ConcatMatrix(*Filter, @m)
EndIf
EndProcedure
DataSection
; Some filter matrices
grayscale:
Data.f 0.299,0.587,0.114,0,0
Data.f 0.299,0.587,0.114,0,0
Data.f 0.299,0.587,0.114,0,0
Data.f 0,0,0,1,0
sepia:
Data.f 0.393,0.769,0.189,0,0
Data.f 0.349,0.686,0.168,0,0
Data.f 0.272,0.534,0.131,0,0
Data.f 0,0,0,1,0
invert:
Data.f -1,0,0,0,255
Data.f 0,-1,0,0,255
Data.f 0,0,-1,0,255
Data.f 0,0,0,1,0
black_and_white:
Data.f 1.5,1.5,1.5,0,-255
Data.f 1.5,1.5,1.5,0,-255
Data.f 1.5,1.5,1.5,0,-255
Data.f 0,0,0,1,0
EndDataSection
Code: Select all
EnableExplicit
XIncludeFile "ColorMatrix.pbi"
Define.i w, h, sepia.CM_Filter
CM_SetMatrix(@sepia, ?sepia); Setup a sepia filter
CM_AdjustContrast(@sepia, -50); Decrease the contrast
CM_AdjustBrightness(@sepia, -20); Make it a bit darker
UseJPEGImageDecoder()
LoadImage(0, "MyImage.jpg")
w = ImageWidth(0)
h = ImageHeight(0)
CM_ApplyToImage(0, @sepia)
If OpenWindow(0, 0, 0, w, h, "ColorMatrix Filter", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
ImageGadget(0, 0, 0, w, h, ImageID(0))
Repeat : Until WaitWindowEvent() = #PB_Event_CloseWindow
EndIf