ColorMatrix filter

Share your advanced PureBasic knowledge/code with the community.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

ColorMatrix filter

Post by wilbert »

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

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
Example

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
Last edited by wilbert on Sat Aug 10, 2013 7:15 am, edited 11 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: ColorMatrix filter

Post by davido »

@Wilbert,

Very nice - impressive. Thank you for sharing. :D
DE AA EB
Little John
Addict
Addict
Posts: 4791
Joined: Thu Jun 07, 2007 3:25 pm
Location: Berlin, Germany

Re: ColorMatrix filter

Post by Little John »

Very nice indeed. :-)
Thanks for sharing!
User avatar
STARGÅTE
Addict
Addict
Posts: 2235
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: ColorMatrix filter

Post by STARGÅTE »

CM_Apply() is a powerfull function, thx for sharing.
You can combine this function with the CustomFilterCallback() instead of the For-Loop with Plot and Point
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ColorMatrix filter

Post by wilbert »

STARGÅTE wrote:CM_Apply() is a powerfull function, thx for sharing.
You can combine this function with the CustomFilterCallback() instead of the For-Loop with Plot and Point
Thanks for the feedback :)

I thought CustomFilterCallback() only worked for lines and boxes etc.
After some experimenting, I got it to work. It's a lot faster :D

Code: Select all

Global 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 

Procedure FilterCallback(x, y, SourceColor, TargetColor)
  ProcedureReturn CM_Apply(TargetColor, @sepia)
EndProcedure

UseJPEGImageDecoder()
LoadImage(0, "MyImage.jpg")

StartDrawing(ImageOutput(0))

w = OutputWidth()
h = OutputHeight()

DrawingMode(#PB_2DDrawing_CustomFilter)
CustomFilterCallback(@FilterCallback())
Box(0, 0, w, h)

StopDrawing()

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
Last edited by wilbert on Tue Aug 06, 2013 12:05 pm, edited 1 time in total.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
STARGÅTE
Addict
Addict
Posts: 2235
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: ColorMatrix filter

Post by STARGÅTE »

It works with all drawing functions (DrawImage, DrawText, ...)
(And it works also with gradient functions)

Hier a example:

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_GetMatrix(*Filter.CM_Filter, *Matrix.CM_Matrix)
  ; Get the matrix of a filter
  Protected row, col, *M.CM_Matrix = (*Filter + 15) & -16
  For row = 0 To 3
    For col = 0 To 4
      *Matrix\m[row * 5 + col] = *M\m[col * 4 + row]
    Next
  Next
EndProcedure

Procedure CM_GetIdentityMatrix(*Matrix.CM_Matrix)
  ; Get an identity matrix
  Protected row, col
  For row = 0 To 3
    For col = 0 To 4
      *Matrix\m[row * 5 + col] = Bool(row = col)
    Next
  Next
EndProcedure

Procedure CM_SetMatrix(*Filter.CM_Filter, *Matrix.CM_Matrix = 0)
  ; Set the matrix of a filter
  Protected 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
        *M\m[col * 4 + row] = Bool(row = col)
      EndIf
    Next
  Next
EndProcedure

Procedure CM_ConcatMatrix(*Filter.CM_Filter, *Matrix.CM_Matrix)
  ; Concatenates the matrix
  Protected 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.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_AdjustBrightness(*Filter.CM_Filter, value.f)
  ; Adjust the brightness of a filter (-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_AdjustContrast(*Filter.CM_Filter, value.f)
  ; Adjust the contrast of a filter (-100 - 100)
  Protected m.CM_Matrix, s.f, o.f
  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

DataSection
  ; Some filter matrices
  grayscale:
  Data.f 0.33,0.59,0.11,0,0
  Data.f 0.33,0.59,0.11,0,0
  Data.f 0.33,0.59,0.11,0,0
  Data.f 0,0,0,1.0,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



OpenWindow(0, 0, 0, 500, 500, "", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
CanvasGadget(0, 0, 0, 500, 500)

Procedure CustomCallback(X, Y, SourceColor, TargetColor)
   Shared *CurrentFilter
   ProcedureReturn AlphaBlend(CM_Apply(SourceColor, *CurrentFilter), TargetColor) ; Alpha blend for alpha suppoert
EndProcedure


Procedure DrawingFilter(*Filter.CM_Filter)
   Shared *CurrentFilter
   *CurrentFilter = *Filter
   DrawingMode(#PB_2DDrawing_CustomFilter)
   CustomFilterCallback(@CustomCallback())
EndProcedure   


sepia.CM_Filter
CM_SetMatrix(@sepia, ?sepia); Setup a sepia filter
invert.CM_Filter
CM_SetMatrix(invert, ?invert); Setup a invert filter

UsePNGImageDecoder()
CatchImage(0, ?Image)
StartDrawing(CanvasOutput(0))
   DrawAlphaImage(ImageID(0), 0, 0)
   DrawingFilter(@sepia)
   DrawImage(ImageID(0), 50, 0)
   DrawImage(ImageID(0), 50, 150, 64, 256)
   DrawingFilter(@invert)
   DrawImage(ImageID(0), 100, 0)
StopDrawing()

Repeat
Until WaitWindowEvent() = #PB_Event_CloseWindow


DataSection
   Image:
   Data.q $0A1A0A0D474E5089,$524448490D000000,$8000000020000000,$6B666A0000000608,$475273010000001A
   Data.q $0000E91CCEAE0042,$DA78544144490020,$EF99555D98699BED,$A9539AC67DEF6B7F,$0612194648CA6A4A
   Data.q $8D283994063CC399,$32A222A2F415B7D2,$9C57DABC55A55F68,$2655CB602BD016D0,$4C21242991080301
   Data.q $3CD4AA92A4810819,$7EEB5EF3D9F33A9E,$DDBC51229392BB38,$CE7679FBD0FE9F7D,$7AEF5FFBD67D9DA9
   Data.q $4E25DFBFC12EF5E7,$4948973A68C72AFE,$472C11F02A271A1D,$4031106BA0040281,$7F8CB306F9AC8C68
   Data.q $0A5AB85B94ED4103,$31D1231132B0502E,$9EAC69B34D1D1663,$34743E04398CC74E,$4D0516893AAAA1CD
   Data.q $56ECFFAF966E5817,$DFCB19439DEBDC36,$6E747CE55F15CB36,$CB3170BA1DA715F2,$D634A58EE2C51E17
   Data.q $52F66321EC513336,$9BEB833FCE19C7B4,$4364A02543EA5BBC,$F3CA3D21BAFA124D,$680141FF80725E99
   Data.q $CCBFC1A427C2BE2B,$2C09C4709F8D68EF,$66DC81932814785B,$084EEEC0CFDD4BDE,$3015C0A001504940
   Data.q $A69ADA351BD30130,$3C430A107C6292E1,$271001AFF7016406,$37987F157A799E7C,$82C39C572B61E66B
   Data.q $C0DAF23667F445F6,$1902ADA8148DF41D,$096CE132A940AE80,$3FCE2AACF660C7BA,$17BB1930D67228FE
   Data.q $39E51E778394A0A1,$CABFACE2FE3006C0,$CFB392F9FE09A297,$3CFD3BB347A5D8E6,$7E8996EB8FDD77CF
   Data.q $E691FEF53642F1A8,$82EEB61F7C0A9046,$D9217D9F5724DABA,$48AE65B5E95D3B60,$FF7009403BF2F430
   Data.q $A3343C2DC57D0012,$5412ECB87F92FAA9,$BDB6C67F81FACF24,$327CABC736437B32,$96C0E38949351A13
   Data.q $B6D69B384F47C14E,$FCAA7BE10F723B38,$70FC77B106FF2CAE,$F46DCA72200C0E91,$6795AACB5674BE1E
   Data.q $8C9D13A31D6267B2,$964ACD996353408E,$64E8BC3E1EC5733F,$DAE7A82F197A6CD6,$327796B23E4E3F73
   Data.q $14009390484994EF,$214F3614C88C1204,$A4B61F3F85B8AC6E,$D19DE72F67A3E5AD,$992C2522742D1947
   Data.q $40164215A3632271,$ABF1C0CC4313B8AA,$8C7B54607FC2FEB9,$F9456070B41DE761,$35D50065C4CB251F
   Data.q $4A8FC9311CD5E470,$D6D9FF997F27E80E,$4CE94BD9C1715896,$C3FCA0A858E5873D,$F58D1A13F20686FA
   Data.q $7657920A274B2F72,$E32D817D178DC33F,$12035108D361B1F7,$811221C589C1B2C9,$78B3F292E67964AA
   Data.q $005EDE012F00273D,$174DCD5629A2CE0B,$897B0DE93D28739E,$B33752990FA400AB,$6D4344DA9E31969A
   Data.q $3F13AAC177DB5679,$9CBBCD4714F060C9,$5236F4B4662F17B3,$A55200A81DCDC62A,$01A2EFBE08F6F26E
   Data.q $7E4E121C23D06AC9,$74BCDC61C67D002F,$F69669883EBE0371,$597A974EEC865CCE,$FA89B1BD4F097ED8
   Data.q $9739CC4703D1D86A,$9BD2B6A66F0CB6A6,$49A1C865FF57BADF,$412648281EFC9959,$206527F724F4728F
   Data.q $32510590F2B945AE,$693ECCB9006421A5,$F3759989568A1297,$38DBB5C166C85C08,$345BF33B79E3B8EF
   Data.q $AA6814203A03B659,$5AAACB29710549FC,$09522062C22D0095,$7C6D6B2666342620,$F6B2C76AA8F2CCBF
   Data.q $A8B20E6B200A9D09,$1751F670235D3163,$24D2CA0826AFE830,$AA928D26BC2E8526,$F5154FB6801504D0
   Data.q $FA44D75C0034214A,$6F3F923B6A2FB665,$9C1865BEFB56601A,$99A458886AA30378,$24DBD36BB2AF035D
   Data.q $88112304CAD0C0AB,$F7F2D1620D71211A,$7EECAC2B1542BC44,$04D0D97E0404205A,$DCF1AA1108F4443E
   Data.q $EF2B7B519B385A22,$B5662EED3F7FC2F4,$50A4E4A36C2E1827,$4A429B928A4E4A0A,$81480CDC94133928
   Data.q $E113995CDD316769,$3016597BF7A7ED32,$717E5E3C46609E9A,$DDE58EDA4B2EFF2C,$8E023D006A69E7A1
   Data.q $FB8578938ADD8101,$D67DA9CACFBBDEB6,$FB94A30A54F0D2AE,$B020A21BF78848D6,$0C0174B42C7C6779
   Data.q $1D18538ED0E3DE66,$82EDB60EA441A903,$E8D3CCCE9A7C56AB,$2854F0526706BD8A,$C1200CA55002A27C
   Data.q $83F2F002A6700076,$D0A1103CB86461FC,$E8A12EB6161EACE5,$D7502712B304BA42,$B972965834A26298
   Data.q $0A3BF538200796EF,$B97F0BAC0B705972,$313B3CC3F6F38017,$B3690FA03852A501,$F099DA92388E638B
   Data.q $69B317A9C5709BE6,$F12298ACCD7B97F4,$343909B807576C93,$DEA9EBF1EF63013E,$0E0C3F86760A05D0
   Data.q $A98C3A682B477170,$B9583AED64E8B0B7,$DBC937F4F57E72EC,$A1E2C608FB35D50E,$0357D7958D1057DD
   Data.q $164C80AF59FD91A2,$A986C4FF3B5D22AC,$7BE11EDB4A0280F1,$C5886CF678B9D355,$D4E45F59AC8BC640
   Data.q $6744FF19ED719705,$9025289DA55264FA,$482678E3F6632032,$82A46CF5C41ACE45,$5F66FB39EB8A39BC
   Data.q $7C73D76046867216,$2AC8CEA9BC868C29,$B2CF1AEC9AE8328E,$D51C0B6B7810EDA0,$D83F396638CE31FC
   Data.q $E42C2B2107D704B8,$7793D770AC874B14,$6BB9BBA40F7BBC48,$754D4480C35B1370,$91CBF0F6CA5001C7
   Data.q $E730F9AACF02F2D1,$20977FF4AF2C7B1C,$2695F6B156B0EB28,$8472A1553E585457,$FE7EEB7DAC6373C4
   Data.q $120281FD9C989805,$4CF291A2138DB6AF,$5C46F7A6B3A61D96,$6EA15933F288614F,$884DE693802FED4A
   Data.q $90A0C7460644E7F2,$046045F31FBADD62,$4E078CA8B6AD2B30,$23A2E42D664799BD,$DA825981D6B7838E
   Data.q $41A68481CA27885A,$EF12FEFE5C287555,$9059637F962A5079,$FF0377A7B16B81CD,$DAE18303D615E7AB
   Data.q $F0B7A9C684B207FB,$D878FCB45C05ACF0,$EBFF62F0B7BA01B4,$DA2A8C2B387ADC3C,$1042AD38F4DA3122
   Data.q $B745BED670F41F7F,$CBF7636218543367,$B642A406314A5CBC,$E338C61C9F12A697,$1D96738438EEACEB
   Data.q $DDAC2DAE87AD6F0B,$38C821F2065F1770,$92F2A0C453A20310,$F0CDBC31405B3AA1,$F1038FDCB38A776F
   Data.q $DAE855F9B00623C0,$1FA046E2EE1B2BC2,$0FD4FBA123048428,$6E96708395527C42,$870354F5920FCB78
   Data.q $AF237C9E3625FC9E,$FAE3BF79D3024701,$46600685C844B90F,$FEBD681CC8146CF8,$F22684CEB15000A3
   Data.q $C0C9F6BAFFF10BFF,$4A458EAF875DEF17,$BFBB84BECF1734D0,$AFABEAE754C1F4F0,$6BFD937E6FCDFFAE
   Data.q $27CE4FDD73A3F6CF,$9C557AA3290C7F12,$093E9FC7E35EF5C0,$E7AF26A97E1B96A0,$F14479DF39A88D76
   Data.q $5375F5E171B67BB9,$735C162BAAF8FA07,$35FA25A2983F35CD,$62BAAF8F83F35CD7,$F8EBEBC2F4D4FBC1
   Data.q $7AA8F1DCE6DB3DFC,$4BF0DC3A5EC8D76E,$F8468B4C406AD405,$6100657CDF34B6F5,$C971DCA5EB79D924
   Data.q $FAE59B1E97468709,$FDFC1EB4E9593EBB,$6261A125C9422343,$FF7F0792E4AF586F,$FB3FF5E7797EC8D0
   Data.q $D1EE7CBF67977324,$7C13D012F9BCEE87,$B65D2DBD7E0195F3,$E86C14A1003310EB,$794677C1E294A2CF
   Data.q $DA7016CBE5CF387D,$EDF7CBEDFEAE3682,$5F4FE3F28FDEE1BE,$1C5368191D1A1A11,$C37DFEF7D33E1199
   Data.q $FDF17E9D6C2D0FBD,$B3BE6ADA96953B1F,$19D74449B4AE806E,$01A1048EBA68B82C,$A5E9EF379B9BE62A
   Data.q $782C19D51921163A,$CBC6BEB2618DE8FC,$98BE2F0FAACF9FD7,$3E4E06890BE81E9B,$E432B8AF2BAAFAB3
   Data.q $E4E95C5795D5768C,$81E9B8868043AB33,$CF9AF17C5E8E2BBE,$5E95EB7C54C864AC,$D11123433116B27A
   Data.q $033737D443DDDCD9,$D4FD9F47FB1C034A,$1E27973C6B3EDEF6,$358D519234444831,$9767E793DEAC727B
   Data.q $BFD16BCE4795C49D,$FA789F07801E9BF2,$A21BE848FF4464E1,$FA789F0791FE88C9,$62B9E9BF2BE30061
   Data.q $0F3C7C5D1F96FCF9,$A409C6F7B1E31F6E,$CF9631A68604D0C4,$D9F47FB2C06FBDB3,$63518C3A168E014F
   Data.q $0D4463239DEEEBB8,$FA20CA3711AE889D,$2F4FCE3EDDBF0E0A,$BF46B7E7CB55913D,$85E04152807BE0FA
   Data.q $1F9BD1577E12223E,$707F5AF365F2EF7C,$593D9879BDDC71D1,$009A04D7474C6487,$DE19118C8F76084D
   Data.q $7AB42D4D7168777D,$8B0532A7E6F76099,$B3FBBA35D111AE88,$80FDE4188C9140C8,$B9E392F0FCD33A87
   Data.q $F81E9BD16DF9C8FC,$8E6E6E68DF34C969,$46F9A64A47373734,$F365F2F4FC0FCBF4,$F6EBFC71D1707EDA
   Data.q $2BA0613AA29E0A31,$5F208420404C65C8,$DCD26A7E6F7609B2,$99DD5C6D63920A4F,$A094A04D346AF339
   Data.q $6B69467B3BC636A1,$C93C9469B690E1AA,$B247B9C7FE195D93,$F65E2D29C2BCDC6D,$5B1E801DF45F1F77
   Data.q $73B6F0B1DD9A2E74,$A920529A6770F397,$154919A70D74184D,$BCD2B30B43AC6968,$9C9939DD5C6E6165
   Data.q $939A34D0DCBE9568,$66396B9ADA0D3411,$7B6DE5E6D3391B71,$E1DA6B62A5307430,$C2D36BBD809C5727
   Data.q $D9C6EF7774DDEEEE,$2D913F2F8FE169B5,$F978D0DC414B0729,$6D85F2CB96983D9D,$FAECC10BA157D634
   Data.q $C32BAAE951AE8638,$A10852813A686972,$EE2FC73A63C49081,$58BB2DD9E4573C60,$7A6DA06B5D05B73E
   Data.q $03B7DB6D9264C915,$A908F1E8C5D2D870,$C606F79A78FF4994,$4C51A2AA6CF17E38,$B7904D74020827CF
   Data.q $55FDF84E4FA1747D,$BD39639D07AEA8B2,$81021A04211AA1B5,$CA1E67F32D7A63A2,$5E21ECEBAC43DDEB
   Data.q $F57E3734A9A3287D,$CCE8FBBEF6FC00AB,$355D6B43DCED5E4A,$3A61E7A638709E57,$35D0C09AE8109A01
   Data.q $301C16BAA279D27A,$FB5C3FBAD2FC063B,$87343E93ABF2245C,$E6D2F042E84DED25,$26ECD0F9C5A69A7E
   Data.q $B972A81B8EDF9D8E,$9EBAE58BE7C8D74D,$402F87D9C92BE1AE,$04CF5EF029983147,$35D05A8BE1CBA285
   Data.q $4433DEDE09B1C18D,$2BEEE95B5C3F95F1,$19D17BEF67A99801,$3EFEB75585EAB5DA,$0B6581A5A0B08D19
   Data.q $3DB6C2F22D9A3067,$2D470D5A69A03204,$00B2DB0AB275405E,$77EB15F022AAAAA8,$B4A23093CE9432F6
   Data.q $0C068F54DED2D7D4,$FFEC601AEA6C0A65,$A11BFD6DB0AAB1DC,$B6148C03800FBFEA,$D727EF7B523D7FAD
   Data.q $344D6DC5FEE17D57,$EE7C3927D73418D4,$B11520415F0A900C,$E3C788032EDB1C44,$9B2CADD988E481B8
   Data.q $04E0107E052042DD,$E6443DDDBC97DF12,$F5FEB6D8593E7BA9,$34615001039DFF50,$178DABC4FF5E9234
   Data.q $026853EBFE2F967C,$AF69444EA9AB6B9A,$84DCDAD28B1A25AF,$3B08F520397BA232,$14A6883433D9C706
   Data.q $9D3067DF6C59E33D,$FF5E90D64E680847,$F9BA54E26261ABC4,$A69F3F8DF17084FD,$97F5CD21F77B98CF
   Data.q $A157D76622C67ADE,$3D1A1861EF206284,$C7644BCCEC090759,$0E96BA800E763821,$9202EB5670D63696
   Data.q $A68A0628EA968D11,$F7C7387DB04201A4,$F63E299C39D2BC66,$4A8194AAADE72DCE,$F3AD17CEF23D1DDF
   Data.q $3D2670B99DAAD43E,$4E869225C38D8705,$3804190D0BA1A224,$23EF67F908AB6152,$A8B30D3C2CB3B631
   Data.q $D74C4F85C433FF39,$A4A01FAED0F2F4D0,$567545D4B5D9B4E4,$9B13C31D85E21F6F,$59E3B2FBCB7A74BE
   Data.q $ECDA135D76B7FFE7,$792A19E40FD67AE6,$5322603F282225D4,$AB6A7C0FC7933062,$C46FAEC5696DCC13
   Data.q $6485BF3192CB2DEE,$A934CE4E3433D054,$2AB6A1AFAD68C68A,$ECCF37A179D51E81,$73B6B1A65B0B28EA
   Data.q $00BDB4D30AA811B4,$EF3F6CDBFFD3DF44,$554902B1331DDE1A,$A697DED8311120DC,$D0C5CF8799C63EB1
   Data.q $ECD881C147AE4A75,$39FA80027D5F1425,$AA76DCD4706A884B,$4D286BA5CB250613,$81915B8B659A5F77
   Data.q $4B49B5D493672BA2,$D4FC080A032A9D18,$B5E68B89559DE3D0,$1E2706BA1A686137,$7674D740D26D46D6
   Data.q $FE70B0CBECE8660D,$3942A5D8C5F68C0C,$EF5E022E8A5F6CA8,$4E5CE8719C8AEF2E,$BA6723CB7916623B
   Data.q $1FEF6CEBA186BA18,$5531995E3D0D4FC3,$6D6C8FCBE0DA75C6,$922555579763ED3D,$3A1A882E8628F1D5
   Data.q $14C93B37A9E5C725,$3A0CC5881B9A3A6D,$9A6A087B867027FB,$E6317519943CCD29,$DB0BCD5F52D03534
   Data.q $D13A1745923BD0F6,$C62533C64BEF81A8,$EFF0BD6E1B5226C7,$40A53E911AC9F181,$E3C2E496F8ECBCA1
   Data.q $5E754589C554D556,$17639A68F747DDDF,$3A224469CCCE62CE,$2F5D63235219035D,$C6F6DAEF1FF5F3FC
   Data.q $48815CEAB8CFDB6F,$DA8B3A19894554D6,$3472867B3A6BA3A7,$D3867A5C97C92435,$60615DFEEF9BF7BC
   Data.q $28015355E9308ED7,$2FB5D62AEC0C2BC0,$4136B6B93B6FA934,$1A03D535667AB4C7,$303FD12E9230178E
   Data.q $F4065CA70DC2F136,$4D53D5FE9E7B1300,$964B163468FAAF27,$A86A68468B101C67,$7D18A6F67602F2C6
   Data.q $EAF9FF1F3A3BC5EB,$4BD2609681FEF670,$319AAEE9AE6FC379,$1D16CE4F5A6B28B7,$9AC8992FAE18C992
   Data.q $E4EE7CD6A58DF6F4,$EDDA0CC2C7C4848F,$D5693F4CCFD7E67E,$9E061BA93468F547,$30289B19A7F57494
   Data.q $E6E2FD3777CD71B0,$6B6A920E415AA7FB,$F2D6731DB4B49AA1,$78964EDFBDF33E63,$95D8F6BC9F65E055
   Data.q $89EB5930F0B09EC0,$DAF3DC8FDCF51984,$6F078387D48F4253,$B358DF06ABE6E2FC,$01AFF2148B0E1819
   Data.q $2016809A00910498,$9C42981801512E19,$336C709A81DA2029,$B2C1E42A161E3C24,$A47890D16DA583E3
   Data.q $104AF1003A59A0A2,$5A0E2085EE7A1555,$46FC3FFC4BAFF921,$8566E01DF8D2DB9F,$67B7BE0037262042
   Data.q $F7F914DBF635F528,$AEA07D3AD7991007,$10943CFB6679A9B1,$0D8EC895698B8E78,$FB54C1EEB1C558E2
   Data.q $D67D00137F91905F,$8D1A39B808EF868C,$30217B257DA55B61,$BF7C4A91F4943122,$81C4E507DB7D346F
   Data.q $1EE9EF9ADAF3E800,$477E7D1062B6BB1C,$D0D65E671A1AEFF3,$CB2528E9B4ED4CD8,$A4C9EB3088405A65
   Data.q $D23C8C4CC8C6A4C8,$300FEFE565763FD5,$2E6F6FCF540141E6,$BDBB2A70D87BB698,$9AE899326E7EA9AD
   Data.q $CA211C68D10871D1,$4E6579E01041741B,$5021075D74112247,$31BBFDA336BAE152,$CB4F7678E0FE6B4D
   Data.q $5D6DD1E3250FFAF4,$9BA0F30161356303,$3C64E6D4A93100D7,$992266724D7D7CFA,$DCB7CDC2D69EAAE8
   Data.q $7C865CB908D8D8CC,$DE378DC13D3A473E,$413D3A40DE378DC0,$363630CD97239F3E,$16B4F54CDCB7CDC2
   Data.q $66724D7D5744C912,$4A79B52A5E7D1E32,$FD534B7D57A0E351,$D3ADD9AB37B4E6D7,$0BF9BCD54BC592D5
   Data.q $D4C791F87A94A054,$D54BC5E2D48B0582,$76CA7AED94F3399C,$F178B53CCE67355D,$23F0F522C160B552
   Data.q $02FE6F352940A98F,$BA75BB3552C964B5,$BFEA9A5AACDED39A,$B3038819107FD406,$5AFDF0237F4CB5A6
   Data.q $083AE78BAE780419,$9EF8109A06188821,$181316208594E387,$0147DF3C29520891,$EBA68A24488C5891
   Data.q $018EA70F4F9EBAE8,$005FB60E6AB726FA,$6A97E85E470EDDE0,$F795578832F27F97,$59225106F2F1A26F
   Data.q $37978F101227DFAE,$28054A134C1B6C8A,$F96284CB7B75CBE5,$3847FB50BF8FCDEF,$3EF3A72075D01BF2
   Data.q $87F3B82E77019F5D,$FC6AF3F853177378,$58A236C34D021EBE,$1A66AF06B27604D1,$E99829CC700BFABB
   Data.q $CC09E98A0989A534,$A4EAEBC1932F1674,$C1644F23BC7AC6F4,$29F0ACEE0332779D,$6AD34583AE200507
   Data.q $F67EC5CEE7AA5BAB,$E2AEDCB86BB52E71,$E36DBE2E380CDEB6,$12CB938B215E169A,$F879E7859678F214
   Data.q $E19BD33E3DF0806E,$FB3F60E339C551C4,$BAB6AD32E7735538,$02A7D97534583AE5,$78E2B59FBF810FD0
   Data.q $9851F6F7765EFE9C,$C21659F479EE86D1,$F209152A85969071,$B69116651E7CFC05,$A5CF06CF48D8E045
   Data.q $3578C180A98972DB,$2B1D2F7F6E3F40C1,$D041A9FBF810E78E,$E8E5AB386AB10054,$4D8DED1AF5BE28A3
   Data.q $F7D1A1DB1914D2CC,$162BC4598112AD70,$9F208E42BC390BF0,$B4C08B4C08952823,$5C7593E916872D71
   Data.q $5E9B8A5119E0EAE6,$A6B386A8A3E8F5A7,$D2C3568C2154E2AA,$681D6878997305C6,$3204AA4415AF3F06
   Data.q $2223E781268B70AE,$B9F8F3E7E3A1811E,$2DB2807E08B17E1C,$11220D3AB3F16D94,$7777D0AB2941A2BC
   Data.q $30D2C35431D978D2,$6B6ACDA0054D2856,$06383684F888A0BA,$8A1F3D212C5F89A6,$44BE2200B1043126
   Data.q $F0D7409711F862F7,$C450B204CC82050B,$A42DB0839E220208,$F9E165B62698E169,$680D134C0942911E
   Data.q $EF40F15AB7BA6B6C,$AF31801A406AF8D0,$FD9B3389A919E9AD,$42D37C4C97E03880,$F01383674D112E7A
   Data.q $E2C4D4634444DF43,$5781B8B2C13EA17C,$379E428852A7E028,$0B4C648BA7EF9F6D,$1B7D638F0D5B8B6C
   Data.q $08992827C0FC2819,$79D2E29AA5C4D340,$4F3F1C1EEE7F4D6D,$7B964B3388429203,$4AF01709F0BA44FB
   Data.q $62B44C9413AEA425,$84258AB1668DAF89,$3491FDF02854A259,$8F8DE3CC5F8F3E7A,$2AC19A31DC0A15A0
   Data.q $E5F12CCB136C3096,$E0450323EF861CD1,$FE74F9EB996678F9,$1338842EDCEDA613,$125B181AE8801A48
   Data.q $E044B30203C447AA,$6594916651206449,$1123D177C20105A0,$058B21669F419A4A,$BCF18E7CB461E1DC
   Data.q $B2CF071DF0F03C4B,$448B51A7BF454828,$64A701212CDF1279,$EFC28E6FD92D1D6F,$26E860EAD1846100
   Data.q $4104024D33C07DEB,$3EFBC875DD017120,$60C46BE7A102246E,$E5C8C21A1EC058A0,$806421E7B63E8AF0
   Data.q $7D14919A09A920A7,$4A5193992BC08E89,$1BAD52C9F58CCC2A,$BE39D099E026DB86,$D850320D4D463763
   Data.q $98C949E04453FC01,$53C9DDB9162C536D,$3EBECC79F2F166CC,$106E16F8B702B94A,$D808E3F1AFF72B90
   Data.q $E30D4D7A140822BE,$B9488ACE8253763B,$8CEF00A0C27EA070,$F08BE1F670BDDCEC,$39782138E0A50083
   Data.q $C009C2845A12830A,$0CE5F370082A0192,$124315452F0158F9,$206C200855B034DA,$6A677805277B791B
   Data.q $3DC02FB910D53240,$64311825F041BEA9,$52524D01F8142650,$D08502F12056240C,$2F82B3E178548104
   Data.q $9008200FDF129269,$6FAA497C0C460041,$60F8ACC99EE01790,$54CBE4A3B0359C00,$0BC30EB6BD187FBA
   Data.q $31F28A8C70653BF8,$7E0497C092F9A065,$4CF87E01F8105080,$51561F06531F25F3,$D6D7A017BA0652AE
   Data.q $0D6A65F4C61FE991,$03E0054E1B9A98EC,$46EDEF7DDBE0B785,$A820F4A2458C2237,$81900FC20423D146
   Data.q $6D65FA4B2CE5FA44,$50423D01F8448191,$8DD047A18107A285,$2B7C0B66EEEF8308,$F98050012FFF775C
   Data.q $BB473B7AE4BA784B,$353007318D1999FA,$4810454AE152B98D,$209207E12EDCA0FC,$A696152B98A95DBC
   Data.q $7D5D6338F1901731,$A999E02FEEF79E98,$78D004888B0703BD,$7E2B9CBF17F55D58,$EB8043710D43766D
   Data.q $E8A8A57B10700811,$100688EF6D0DAA4B,$93D14B1772ABA143,$ADB4C66F7C8B660C,$DEE2786462FC4D69
   Data.q $BDAD6A0DA07842A9,$D6C9F8993302F1A5,$F0A97459E3A59C64,$DE190A5020A38135,$CF85428D07615281
   Data.q $2B91EDA282AE053C,$6A3645D32DBBF25A,$62757D41710B8CCC,$81C2E4AADF305B34,$0BAFBFAB6B1B4400
   Data.q $B15FAEBA572FB731,$030A50333BD750FE,$2D02A4377AFB5281,$FAE0A2A4A9F84E24,$747B55C36F9F1D64
   Data.q $458AAF1E72DF79ED,$FBCC14C9CC42164E,$B7E6EABC9CA8560F,$5CE953F305336737,$9DF0FBF9ACF87D71
   Data.q $0D34C46869293826,$A55E942F0F708843,$EEB0A325F4C4A470,$B240D888AC35B816,$479A86A6A1F12AB6
   Data.q $07846B261AC606BB,$0A23ED3198E880E2,$2F850C057F654918,$4F3D5846ED606B9B,$EA40CDB3903D7AAA
   Data.q $BB7CA94903312076,$09F1B85DFB3D523D,$A039018F01187DF8,$5AD12AB6A17E78E8,$83DDEB25F2F7155B
   Data.q $2E4322C2EA074071,$BFFC2257CA151B54,$342B9BB2A467E169,$AC155A7E1D2039B1,$DC84D436A4D56116
   Data.q $54244068038A30A4,$419F64E60900EF4F,$AC1F76432C2A00FC,$FFD8E196C70F40FF,$9C407D6B7F262A39
   Data.q $AEEEA1348B39C676,$B8E38064A15EAEAE,$396B5B9F2149A658,$B7F9DF6C763B75DE,$DE9D105F3D6FEECC
   Data.q $D71D3ECF73BDFB32,$E4B264C82666C9B1,$DDDE2B1353C65972,$08FF9C7970BDFBB1,$B6EEE5618F1B880E
   Data.q $152AC26EA918FE2B,$071C71B1D71B6DB0,$6CD5E704D650F6CB,$E507D4F1AF07F0BD,$17C2FB9F5CAD7EC2
   Data.q $445A9A3F27EBB9EF,$3C626BF382B8FF10,$D4974C758B49AB3A,$74B8635189CC61C5,$72ACCC984E69EA17
   Data.q $B1BE8D31DE825AC8,$4CB5ADFA5C3829C0,$D5004A8D1F78C2ED,$C0C0C0C2BAF6BA9F,$EA93DBD4FD3D3FB2
   Data.q $7E25A3A87853ADF7,$0C81A18E988F87F1,$E7E8AA8198A89E2D,$14D6FAF097C084D2,$AE65F1FE1FF6C3FD
   Data.q $C3CAFE7B9C42B508,$BB9EF2FCB7DBDBDB,$CB9620652A2A16F7,$5618BFD5D5D4B6E6,$0B45AEE0AF6FFD47
   Data.q $2F3C7CFC715E2E1A,$CA2A5917F749DBD8,$AFB593359DE7CBCD,$EBEBB9956DFC5D76,$5FEAEAEA301EB96B
   Data.q $7DE8ECC25B7365CC,$A61BC7E1E1918200,$ABB0AF47E5737E35,$E572B3CF1B660983,$B218E2FC6686A885
   Data.q $2C23EFDA800FB238,$BA8C5AF17CAD8556,$03F0CC94B0D39CB9,$B515D719E1795DD7,$71FE9D2649A65869
   Data.q $8F1622412E5712E5,$751247F160B21C77,$EBF4ED7D129D67CD,$6A09D5903C0CE497,$23113E75BC495F0E
   Data.q $217261CAE8151220,$64F0F1CF0C450A14,$E28FEBF49FB4731C,$E35C38FE0AF26D67,$5A972B05F87CE4BF
   Data.q $EEDB6D27CED76BB3,$E4AB26007EE52D0F,$79EAF2567BCCC08D,$786EC86B1BFDA1B3,$618FCBC27F5BD484
   Data.q $CD35188D62A07434,$E01FBE0E2EA74D48,$1FAA7604D690C7E6,$4F1A5CEA3E699BAA,$A3F58B395FAD8CBA
   Data.q $4E1BEE1F1CB3FD70,$F274A6298DCD5654,$2EC03C7034D289F0,$312EB214E9F87F52,$8F0DD1853C4E3A34
   Data.q $A20C89EC45F9BC33,$68D3B0FEA06DA80A,$25197FAEF4B3EE3C,$E388A7074ECB8D25,$2AF0021C34CD3DED
   Data.q $5803B9EC01C6E6AB,$CF48134B2C607282,$980C804A51A52BC1,$B5A524DC1445E271,$E349592D540F8AFF
   Data.q $48138AF00C836771,$370C48F4294A08A9,$4B607CEE590F29EC,$A4395BE2173E51A8,$CAC084A40559E2E7
   Data.q $2FFFCA642DC08420,$528409CDF0642BC1,$23CAD0A9B97CA578,$53817DCBFC8128FD,$A182EABA5F176801
   Data.q $281C3A7A8427C289,$CCCF2CDB7C5C0C87,$0A040B33C08423CE,$11E56040AB5C0082,$39DBE04484BB5C28
   Data.q $7D1EBC36104A3F34,$E07DAC696F3F59F2,$2A0972BABA3F8F8F,$6CFF5ECF99F1AA5D,$AA5D8D16227F1E3C
   Data.q $52BC194AF0C91F28,$3B024559E22ACF0E,$C2D96544CBE7604F,$CF7C9F3E19015678,$20087E0CA5787333
   Data.q $F89F458F1A5E891A,$2BC4E56CF75F4F87,$0D0E6762C48C9039,$6A5D5DCEC77DB8ED,$928D335C12F80F39
   Data.q $E767852812659E01,$6024ABA7CB140E42,$132D70A01CECF115,$178E9A6832B3C089,$D09FADDDEBB01280
   Data.q $1BE9A9BD3F982E16,$D3F1002A7E4C2EE1,$C1CB574D37B9DD1F,$5AE015285295E057,$70081CA5B892A445
   Data.q $02CCA3BF2ABE02CD,$35290E5E5045998E,$A4CBCBD74D0A52BC,$C0C8F8EB60647C60,$F54ABBF8BED6EB9B
   Data.q $C2F74D32BBB5AFDF,$CF125287295E5193,$41080BB7C2940875,$156043B6A82877F8,$2BC094A2EBA5C9CA
   Data.q $8F0DEE4F0D34D1E5,$48ADC0040FCB4BF6,$5173C06508A5AB1C,$4E78124A90D1224A,$4FA1E265128838F9
   Data.q $1CCA254801073C0B,$D57B57C34D345DF0,$286AABF8DD6CAFD0,$56888A30A77546C3,$21046CF7D3B32921
   Data.q $B95EF81284065080,$98EF8E8046952C04,$25BF8ED1DFB4AF28,$F491DFD196C35222,$A2AC6910198A6A3B
   Data.q $24C11EE19127A683,$879E04149A090207,$CEC393AE1E38E010,$4679482BE0529096,$07DE6F6C0E5A597F
   Data.q $E8E6D5F9CFD394EA,$E263D89825831D49,$588A96625F7C6C96,$965C1E532B5C41B9,$D95832B5C0A5F587
   Data.q $66D16EFCB162CC41,$9C8ADE1F0526DBE7,$40EAFCE7E9CA71D4,$3BCF73BB69DF7E13,$9B8CC2E264AAC9FF
   Data.q $8C1CDEC6CF27AE7C,$B5CF02BD0372A80D,$1AB2F1998A298BB0,$7C298BB1C7A6282A,$3153BBBCD70822D7
   Data.q $73E4DDA6361E4366,$FE779EE785C4C955,$9FA7100254415393,$193CF24489A9D671,$6FB7D3D2FABD2FF8
   Data.q $600BBA07619EE018,$1CF026CF1679E1FB,$5D26433C3201A864,$1F09F3E151B3C087,$BE367B8871B0B5E2
   Data.q $4894BFEF81EDF4F4,$AD4EB38CFD3BCF24,$389932612CFDC354,$DF5667A7F28D1A25,$22B1DAC7D759EC04
   Data.q $C1E130DBF425E64F,$A670C8BAE04BA0D0,$03832EBBE006507E,$21EAAA023200852E,$EC0B59DB13E4F0EB
   Data.q $A3468A6FAB4FF679,$0950B92A53899324,$4689171D564C9220,$91B5DF8C05367526,$14E404F15E2D67AF
   Data.q $E5CAA2DBAC521B6C,$8ADBFAF4875EC13B,$20EA6A06AD0A231D,$B87AF9AB2DCAF26D,$6484689152029D71
   Data.q $01FD837DCAB8EAB2,$1A1ADAA4A31D5554,$0AF87F860F8C2F25,$AE5684DB97B57F9F,$200A977272D1FA2C
   Data.q $B5D532872884C1E5,$376DC9DE7FAD83E5,$807BD96C57CF91FF,$E63AAAAC9ADAA4A5,$83205D58F1E200A0
   Data.q $BD5EB420BB722BB7,$45619EDB91E0EF4F,$B4BF47CA09DF866F,$B01347477944A818,$7E2B30F75DDF9C7B
   Data.q $93B7F0DE804799BF,$3C78EDE0C81F9FF1,$14A05F3F60095756,$BD6CA697B973E66F,$797F85036B3661FA
   Data.q $9F22773FBB803B95,$C33BA1252AC8DF99,$D6E1845F96B87FEF,$7CBC826299D84878,$4A92FD5D5D52F72E
   Data.q $712BC36B098CAC21,$444942000000C873,$3DC81C2B04065441,$1B727778AF1B8648,$A07A043E73AE1B2E
   Data.q $DE2A266F1868DB6D,$807C14BE11800FDD,$66470A8920498386,$3D0886C9E2FD95FF,$0BFF8211C73FFF1C
   Data.q $3A30AE0C28706B8C,$444E454900000000,$00000000826042AE
EndDataSection
edit: AlphaBlend in custom filter callback
edit2: DrawImage with an other scale.
edit3: bug fix
Last edited by STARGÅTE on Tue Aug 06, 2013 10:51 pm, edited 1 time in total.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ColorMatrix filter

Post by wilbert »

STARGÅTE wrote:It works with all drawing functions (DrawImage, DrawText, ...)
(And it works also with gradient functions)

Hier a example:
A very nice example Stargate 8) :D

On Windows your example works fine, on OS X it doesn't.
There seems to be a bug that prevents a custom filter callback to work when drawing an image on OS X.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: ColorMatrix filter

Post by BasicallyPure »

Is there something wrong here with grayscale?
Black & white works but with grayscale I get nothing.

Code: Select all

;no longer relevant so code deleted
BP
Last edited by BasicallyPure on Fri Aug 09, 2013 11:59 pm, edited 1 time in total.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
User avatar
STARGÅTE
Addict
Addict
Posts: 2235
Joined: Thu Jan 10, 2008 1:30 pm
Location: Germany, Glienicke
Contact:

Re: ColorMatrix filter

Post by STARGÅTE »

my mistake, i miss the 1 in the alpha-cannel in the grayscale filter:

Code: Select all

DataSection
  ; Some filter matrices
  grayscale:
  Data.f 0.33,0.59,0.11,0,0
  Data.f 0.33,0.59,0.11,0,0
  Data.f 0.33,0.59,0.11,0,0
  Data.f 0,0,0,1.0,0 ;<<< HERE 
  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
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Lizard - Script language for symbolic calculations and moreTypeface - Sprite-based font include/module
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: ColorMatrix filter

Post by BasicallyPure »

Thanks Stargate.

BP.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ColorMatrix filter

Post by wilbert »

I updated my first post to a separate include file and an example.
I also added saturation adjustment. It makes it possible to create contrast, brightness and saturation sliders and create a filter based on that.

Simple example (without sliders)

Code: Select all

EnableExplicit

XIncludeFile "ColorMatrix.pbi"

Define.i w, h, max_x, max_y, x, y, ColorCorrection.CM_Filter

CM_SetMatrix(@ColorCorrection); Reset the filter matrix
CM_AdjustContrast(@ColorCorrection, -50)
CM_AdjustBrightness(@ColorCorrection, 80)
CM_AdjustSaturation(@ColorCorrection, -70)

UseJPEGImageDecoder()
LoadImage(0, "MyImage.jpg")

StartDrawing(ImageOutput(0))

w = OutputWidth()
h = OutputHeight()
max_x = w - 1
max_y = h - 1

For y = 0 To max_y
  For x = 0 To max_x
    Plot(x, y, CM_Apply(Point(x, y), @ColorCorrection))
  Next
Next

StopDrawing()

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
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: ColorMatrix filter

Post by BasicallyPure »

I've been working on a program that uses Wilbert's ColorMatrix.pbi include file.
I don't know how to make the adjustments work properly using a slider for brightness, contrast, and saturation.
Here is the code I have so far.

edit: 8/8/2013
code updated.
I'm getting there, I just need to sleep for a while.
It mostly works, you just have to select revert before doing an adjustment.

edit: 8/9/2013
code updated.
now brightness, contrast, and saturation are working properly.
My next task is to add 'save as' to the menu options.

edit: 8/9/2013
code simplified and removed unused variables.

BP.

Code: Select all

;- demo for Wilbert's ColorMatrix Filter
;
; by BasicallyPure
; 8/9/2013

IncludeFile "ColorMatrix.pbi"
; forum topic: http://www.purebasic.fr/english/viewtopic.php?f=12&t=55804

EnableExplicit

Enumeration ; popup menu items
   #load
   #revert
   #grayscale
   #sepia
   #invert
   #BlackAndWhite
   #brightness
   #contrast
   #saturation
   #Quit
EndEnumeration

#imgMain = 0
#imgAdjust = 1

Procedure Apply_Filter(menuChoice.i, adjVal.i = 0)
   Static Filter.CM_Filter
   
   Select menuChoice
      Case #grayscale     : CM_SetMatrix(@Filter, ?grayscale)
      Case #sepia         : CM_SetMatrix(@Filter, ?sepia)
      Case #invert        : CM_SetMatrix(@Filter, ?invert)
      Case #BlackAndWhite : CM_SetMatrix(@Filter, ?black_and_white)
      Case #brightness    : CM_SetMatrix(@Filter) : CM_AdjustBrightness(@Filter, adjVal)
      Case #contrast      : CM_SetMatrix(@Filter) : CM_AdjustContrast(@Filter, adjVal)
      Case #saturation    : CM_SetMatrix(@Filter) : CM_AdjustSaturation(@Filter, adjVal)
      Default : ProcedureReturn 0
   EndSelect
   
   CM_ApplyToImage(#imgMain, @Filter)
   SetGadgetState(0, ImageID(#imgMain))
   ProcedureReturn 1
EndProcedure

Procedure.s Load_Image()
   Protected path.s
   path = OpenFileRequester("select source image", GetHomeDirectory() + "\My Pictures\",
          "*.jpg,*.png,*.bmp|*.jpg;*.png;*.bmp",0)
   If path = "" ; error
   ElseIf LoadImage(#imgMain, path) = 0 : path = ""
   EndIf
   
   If path = ""
      MessageRequester("Error!", "No image was loaded.")
   EndIf
   
   ProcedureReturn path
EndProcedure

Procedure Adjust(menuChoice.i)
   Protected flags.i, title.s, min.i, max.i
   
   Select menuChoice
      Case #brightness : title = "brightness = " : min = -255 : max = 255
      Case #contrast   : title = "contrast = "   : min = -100 : max = 100
      Case #saturation : title = "saturation = " : min = -100 : max = 100
      Default : ProcedureReturn 0
   EndSelect
   
   flags = #PB_Window_WindowCentered | #PB_Window_SystemMenu
   OpenWindow(1, 0, 0, 400, 30, title+"0", flags, WindowID(0))
   TrackBarGadget(1, 5, 5, 390, 30, 0, max-min)
   SetGadgetState(1, (max-min)/2)
   
   CopyImage(#imgMain, #imgAdjust)
   Repeat
      Select WaitWindowEvent()
         Case #PB_Event_CloseWindow : CloseWindow(1) : Break
         Case #PB_Event_Gadget : SetWindowTitle(1, title + Str(GetGadgetState(1)+min))
         Case #WM_LBUTTONUP
            If GetActiveGadget() = 1
               CopyImage(#imgAdjust, #imgMain)
               Apply_Filter(menuChoice, GetGadgetState(1)+min)
            EndIf
      EndSelect
   ForEver
   
EndProcedure

Define.i w = 640, h = 480, menuChoice
Define.s path

UseJPEGImageDecoder()
UsePNGImageDecoder()
CreateImage(#imgMain, w, h)

If OpenWindow(0, 0, 0, w, h, "ColorMatrix Filter Demo | right click for menu")
   CreatePopupMenu(0)
      MenuItem(#load, "load image")
      MenuItem(#revert, "revert")
   MenuBar()
      MenuItem(#grayscale, "grayscale")
      MenuItem(#sepia, "sepia")
      MenuItem(#invert, "invert")
      MenuItem(#BlackAndWhite, "black and white")
   MenuBar()
      OpenSubMenu("adjust")
         MenuItem(#brightness, "brightness")
         MenuItem(#contrast, "contrast")
         MenuItem(#saturation, "saturation")
      CloseSubMenu()
   MenuBar()
      MenuItem(#Quit, "Quit")
   
   ImageGadget(0, 0, 0, w, h, ImageID(#imgMain))
   
   Repeat ; event loop
      Select WaitWindowEvent()
         Case #PB_Event_CloseWindow
            If EventWindow() = 1
               CloseWindow(1)
            Else
               Break
            EndIf
         Case #PB_Event_Gadget
            If EventType() = #PB_EventType_RightClick
               DisplayPopupMenu(0, WindowID(0))
            EndIf
         Case #PB_Event_Menu : menuChoice = EventMenu()
            Select menuChoice
               Case #load ; load new image
                  path = Load_Image()
                  If path
                     w = ImageWidth(#imgMain) : h = ImageHeight(#imgMain)
                     ResizeWindow(0, 0, 0, w, h)
                     SetGadgetState(0, ImageID(#imgMain))
                  EndIf
               Case #revert
                  If path
                     LoadImage(#imgMain, path)
                     SetGadgetState(0, ImageID(#imgMain))
                  EndIf
               Case #grayscale  To #BlackAndWhite : Apply_Filter(menuChoice)
               Case #brightness To #saturation    : Adjust(menuChoice)
               Case #Quit : Break
            EndSelect
            
      EndSelect
   ForEver
EndIf
Last edited by BasicallyPure on Sat Aug 10, 2013 3:25 am, edited 3 times in total.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
davido
Addict
Addict
Posts: 1890
Joined: Fri Nov 09, 2012 11:04 pm
Location: Uttoxeter, UK

Re: ColorMatrix filter

Post by davido »

Another nice piece of work.
Thank you for sharing. :D

Is there a reason why you reload the file on revert, rather than copy an already loaded image?
DE AA EB
User avatar
BasicallyPure
Enthusiast
Enthusiast
Posts: 539
Joined: Thu Mar 24, 2011 12:40 am
Location: Iowa, USA

Re: ColorMatrix filter

Post by BasicallyPure »

Hi Davido,
davido wrote:Is there a reason why you reload the file on revert, rather than copy an already loaded image?
No 'good' reason for that.
I still consider this a work in progress and it wasn't at the top of my priority list.
What I really want is a way to change brightness, contrast, and saturation without applying one of the other filter effects as well.
I'm hoping that Wilbert can help with that.

BP.
BasicallyPure
Until you know everything you know nothing, all you have is what you believe.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: ColorMatrix filter

Post by wilbert »

I updated my first post.
The include file contains a procedure now CM_ApplyToImage to apply a filter directly to an image.
I added an extra asm routine for that also. With the new procedure, if the image is already in 32 bit format, it works directly on the drawing buffer.
In this case the processing time is about 1/3 of what it was before.

As for the brightness, contrast, and saturation without applying one of the other filter effects, you can do it like this.

Code: Select all

Define Sepia.CM_Filter, ColorCorrection.CM_Filter, CC_Matrix.CM_Matrix

CM_SetMatrix(@ColorCorrection); Reset the filter matrix
CM_AdjustContrast(@ColorCorrection, -50)
CM_AdjustBrightness(@ColorCorrection, 80)
CM_AdjustSaturation(@ColorCorrection, -70)

; use the ColorCorrection filter after this

; to combine with another filter

CM_SetMatrix(@sepia, ?sepia); Setup a sepia filter
CM_GetMatrix(@ColorCorrection, @CC_Matrix)
CM_ConcatMatrix(@sepia, @CC_Matrix); Combine with the color correction matrix
Windows (x64)
Raspberry Pi OS (Arm64)
Post Reply