filtre graphique

Programmation d'applications complexes
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

filtre graphique

Message par manababel »

Bonjour, après mes précédents posts sur le sujet, j’ai décidé de tout regrouper dans un seul post avec tous les filtres.

Le programme de démonstration vous permet de charger trois images : la source1, la source2 et le masque.
Pour pouvoir charger la source2 et le masque, vous devez d’abord charger la source1 (car toutes les images doivent être de la même taille).

Cliquez sur l’image (la miniature) avec laquelle vous souhaitez travailler.
appliquez le/les filtre(s) , puis "valider" (modifier le/la ) le résultat sur l’une des trois images.

Si vous ouvrez plusieurs filtres à la fois, ils seront appliqués dans l’ordre de leur ouverture.
Commencez avec des images de petite taille, surtout si vous ouvrez plusieurs filtres.

Dans les options du compilateur, activez « Create Threadsafe Executable ».
je vous conseille d'utiliser le compilateur "C Babkend", il peut etre 2 fois plus rapide

Voici la première partie, la suite suivra bientôt.

attention le programme filtre.pbi est modifié à chaque a ajout de filtre

filtre.pbi

Code : Tout sélectionner

UseGIFImageDecoder()
UseJPEG2000ImageDecoder()
UseJPEG2000ImageEncoder()
UseJPEGImageDecoder()
UseJPEGImageEncoder()
UsePNGImageDecoder()
UsePNGImageEncoder()
UseTGAImageDecoder()
UseTIFFImageDecoder()


DeclareModule filtres
  
  
  Enumeration
    #Filter_Type_Blur = 1
    #Filter_Type_Edge_Detection
    #Filter_Type_Color
    #Filter_Type_Dither
    #Filter_Type_FX
    #Filter_Type_Convolution
    #Filter_Type_Deform
    #Filter_Type_Color_Space
    #Filter_Type_mix
    #Filter_Type_autre
  EndEnumeration
  
  Enumeration
    #Filter_Blur_IIR = 1
    #Filter_Guillossien
    #Filter_Blur_box
    #Filter_RadialBlur_IIR
    #Filter_SpiralBlur_IIR
    #Filter_RadialBlur
    #Filter_MedianBlur
    #Filter_Bilateral
    #Filter_StackBlur
    #Filter_DepthAwareBlur
    #Filter_Edge_Aware
    #Filter_DirectionalBoxBlur
    #Filter_GaussianBlur_Conv
    #Filter_MotionBlur
    #Filter_AnisotropicBlur
    #Filter_KuwaharaBlur
    #Filter_PoissonDiskBlur
    #Filter_GuidedFilterColor
    #Filter_HeatDiffusionBlur
    #Filter_OpticalBlur
    
    #Filter_Roberts
    #Filter_Prewitt
    #Filter_sobel
    #Filter_sobel_4d
    #Filter_scharr
    #Filter_scharr_4d
    #Filter_kirsch
    #Filter_robinson
    #Filter_Laplacian
    #Filter_canny
    #Filter_FreiChen
    #Filter_LaplacianOfGaussian
    #Filter_DoG
    
    #Filter_AutoOtsuThreshold
    #Filter_FloydDither
    #Filter_StuckiDither
    #Filter_AtkinsonDither
    #Filter_BurkesDither
    #Filter_SierraLiteDither
    #Filter_SierraDither
    #Filter_JJNDither
    #Filter_RandomDither
    #Filter_BayerDither
    #Filter_ShiauFanDither
    #Filter_KiteDither
    #Filter_LiteDither
    
    #Filter_BlackAndWhite
    #Filter_grayscale
    #Filter_Posterize
    #Filter_Balance
    #Filter_Bend
    #Filter_Brightness
    #Filter_RaviverCouleurs
    #Filter_Saturation
    #Filter_Contrast
    #Filter_Gamma
    #Filter_Color
    #Filter_Color_hue
    #Filter_color_effect
    #Filter_Colorize
    #Filter_Hollow
    #Filter_SquareLaw_Lightening
    #Filter_Sepia
    #Filter_Exposure
    #Filter_Negatif
    #Filter_Teinte
    #Filter_Normalize_Color_Filter
    #Filter_ColorPermutation
    #Filter_Dichromatic
    #Filter_FalseColour
    #Filter_PencilImage
    
    #Filter_GlowEffect_IIR
    #Filter_Emboss
    #Filter_Fake_Hdr
    #Filter_pencil
    #Filter_CharcoalImage
    #Filter_RaysFilter
    #Filter_Histogram
    
    #Filter_Diffuse
    #Filter_Emboss_bump
    #Filter_Mosaic
    #Filter_HexMosaic
    #Filter_IrregularHexMosaic
    #Filter_Glitch
    #Filter_Kaleidoscope
    #Filter_FlowLiquify
    #Filter_DisplacementMap
    #Filter_Dilate
    #Filter_mettalic_effect
    
    #Filter_Convolution3x3
    
    #Filter_FlipH
    #Filter_FlipV
    #Filter_Rotate
    #Filter_PerspectiveSimple
    #Filter_Perspective
    #Filter_Translate
    #Filter_Spherize
    #Filter_Spiralize
    #Filter_Ellipse
    #Filter_Ripple
    #Filter_PinchBulge
    #Filter_WaveCircular
    #Filter_Lens
    #Filter_Tile
    #Filter_Perspective2
    #Filter_deform_Bend
    
    #Filter_RgbToYuv
    #Filter_YUVtoRGB
    #Filter_RGB_YUV_Modif
    #Filter_RGBtoYIQ
    #Filter_YIQtoRGB
    #Filter_RGB_YIQ_Modif
    #Filter_RGBtoLAB
    #Filter_RGB_LAB_Modif
    
    #Filtre2_additive
    #Filtre2_additive_inverted
    
    #Filtre2_alphablend
    #Filtre2_RMSColor
    #Filtre2_And
    #Filtre2_Average
    #Filtre2_LightBlend
    #Filtre2_IntensityBoost
    #Filtre2_BrushUp
    #Filtre2_Burn
    #Filtre2_SubtractiveDodge
    #Filtre2_ColorBurn
    #Filtre2_ColorDodge
    #Filtre2_Contrast
    #Filtre2_Cosine
    #Filtre2_CrossFading
    #Filtre2_InverseMultiply
    #Filtre2_Darken
    #Filtre2_SubtractiveBlend
    #Filtre2_Difference
    #Filtre2_Div
    #Filtre2_SoftAdd
    #Filtre2_SoftLightBoost
    #Filtre2_Exponentiale
    #Filtre2_Fade
    #Filtre2_Fence
    #Filtre2_Freeze
    #Filtre2_Glow
    #Filtre2_HardContrast
    #Filtre2_Hardlight
    #Filtre2_TanBlend
    #Filtre2_HardlTangent
    #Filtre2_Heat
    #Filtre2_InHale
    #Filtre2_Intensify
    #Filtre2_CosBlend
    #Filtre2_Interpolation
    #Filtre2_InvBurn
    #Filtre2_InvColorBurn
    #Filtre2_InvColorDodge
    #Filtre2_InvDodge
    #Filtre2_Lighten
    #Filtre2_LinearBurn
    #Filtre2_LinearLight
    #Filtre2_Logarithmic
    #Filtre2_Mean
    #Filtre2_ColorVivify
    #Filtre2_Multiply
    #Filtre2_Negation
    #Filtre2_PinLight
    #Filtre2_Or
    #Filtre2_Overlay
    #Filtre2_Pegtop_soft_light
    #Filtre2_quadritic
    #Filtre2_Screen
    #Filtre2_SoftColorBurn
    #Filtre2_SoftColorDodge
    #Filtre2_SoftLight
    #Filtre2_SoftOverlay
    #Filtre2_Stamp
    #Filtre2_Subtractive
    #Filtre2_Xor
    
  EndEnumeration
  
  Structure parametre
    source.i
    source2.i
    cible.i
    mask.i
    source_mask.i
    lg.l
    ht.l
    thread_max.l
    thread_pos.l
    addr.i[20] ; adreesse temporaire utiliser en interne pour les threads
    mask_type.l; definis le type de mask , binaire ou non
    option.f[20]
    convolution3.f[11]
    info_active.l
    typ.l
    name.s
    remarque.s
    info.s[20] 
    Array info_data.l(20,2)
  EndStructure
  Global param.parametre
  Global.parametre Dim dim_param(128) ; 128 thread max
  
  Global Dim tabfunc.i(999)
  
  Macro DeclareModule_filtresadd_function(MaFunction , pos = 0)
    If pos > 0 
      Declare MaFunction(*p)
      tabfunc(pos) = @MaFunction()
    EndIf
  EndMacro
  
  Declare Clear_Data_Filter(*param)
  Declare Load_Image_32(n,t$)
  
  DeclareModule_filtresadd_function(Blur_IIR , #Filter_Blur_IIR)
  DeclareModule_filtresadd_function(Guillossien , #Filter_Guillossien)
  DeclareModule_filtresadd_function(Blur_box , #Filter_Blur_box)
  DeclareModule_filtresadd_function(RadialBlur_IIR , #Filter_RadialBlur_IIR)
  DeclareModule_filtresadd_function(SpiralBlur_IIR , #Filter_SpiralBlur_IIR)
  DeclareModule_filtresadd_function(RadialBlur , #Filter_RadialBlur)
  DeclareModule_filtresadd_function(MedianBlur , #Filter_MedianBlur)
  DeclareModule_filtresadd_function(Bilateral , #Filter_Bilateral)
  DeclareModule_filtresadd_function(StackBlur , #Filter_StackBlur)
  DeclareModule_filtresadd_function(DepthAwareBlur , #Filter_DepthAwareBlur)
  DeclareModule_filtresadd_function(Edge_Aware , #Filter_Edge_Aware)
  DeclareModule_filtresadd_function(DirectionalBoxBlur , #Filter_DirectionalBoxBlur)
  DeclareModule_filtresadd_function(GaussianBlur_Conv , #Filter_GaussianBlur_Conv)
  DeclareModule_filtresadd_function(MotionBlur , #Filter_MotionBlur)
  DeclareModule_filtresadd_function(AnisotropicBlur , #Filter_AnisotropicBlur)
  DeclareModule_filtresadd_function(KuwaharaBlur , #Filter_KuwaharaBlur)
  DeclareModule_filtresadd_function(PoissonDiskBlur , #Filter_PoissonDiskBlur)
  DeclareModule_filtresadd_function(GuidedFilterColor , #Filter_GuidedFilterColor)
  DeclareModule_filtresadd_function(HeatDiffusionBlur , #Filter_HeatDiffusionBlur)
  DeclareModule_filtresadd_function(OpticalBlur , #Filter_OpticalBlur)
  DeclareModule_filtresadd_function(GuidedFilterColor , #Filter_GuidedFilterColor)
  
  DeclareModule_filtresadd_function(Roberts , #Filter_Roberts)
  DeclareModule_filtresadd_function(Prewitt , #Filter_Prewitt)
  DeclareModule_filtresadd_function(sobel , #Filter_sobel)
  DeclareModule_filtresadd_function(sobel_4d , #Filter_sobel_4d)
  DeclareModule_filtresadd_function(scharr , #Filter_scharr)
  DeclareModule_filtresadd_function(scharr_4d , #Filter_scharr_4d)
  DeclareModule_filtresadd_function(kirsch , #Filter_kirsch)
  DeclareModule_filtresadd_function(robinson , #Filter_robinson)
  DeclareModule_filtresadd_function(Laplacian , #Filter_Laplacian)
  DeclareModule_filtresadd_function(canny , #Filter_canny)
  DeclareModule_filtresadd_function(FreiChen , #Filter_FreiChen)
  DeclareModule_filtresadd_function(LaplacianOfGaussian , #Filter_LaplacianOfGaussian)
  DeclareModule_filtresadd_function(DoG , #Filter_DoG)
  
  DeclareModule_filtresadd_function(AutoOtsuThreshold , #Filter_AutoOtsuThreshold)
  DeclareModule_filtresadd_function(FloydDither , #Filter_FloydDither)
  DeclareModule_filtresadd_function(StuckiDither , #Filter_StuckiDither)
  DeclareModule_filtresadd_function(AtkinsonDither , #Filter_AtkinsonDither)
  DeclareModule_filtresadd_function(BurkesDither , #Filter_BurkesDither)
  DeclareModule_filtresadd_function(SierraLiteDither , #Filter_SierraLiteDither)
  DeclareModule_filtresadd_function(SierraDither , #Filter_SierraDither)
  DeclareModule_filtresadd_function(JJNDither , #Filter_JJNDither)
  DeclareModule_filtresadd_function(RandomDither , #Filter_RandomDither)
  DeclareModule_filtresadd_function(BayerDither , #Filter_BayerDither)
  DeclareModule_filtresadd_function(ShiauFanDither , #Filter_ShiauFanDither)
  DeclareModule_filtresadd_function(KiteDither , #Filter_KiteDither)
  DeclareModule_filtresadd_function(LiteDither , #Filter_LiteDither)
  
  DeclareModule_filtresadd_function(BlackAndWhite , #Filter_BlackAndWhite)
  DeclareModule_filtresadd_function(grayscale , #Filter_grayscale)
  DeclareModule_filtresadd_function(Posterize , #Filter_Posterize)
  DeclareModule_filtresadd_function(Balance , #Filter_Balance)
  DeclareModule_filtresadd_function(Bend , #Filter_Bend)
  DeclareModule_filtresadd_function(Brightness , #Filter_Brightness)
  DeclareModule_filtresadd_function(RaviverCouleurs , #Filter_RaviverCouleurs)
  DeclareModule_filtresadd_function(Saturation , #Filter_Saturation)
  DeclareModule_filtresadd_function(Contrast , #Filter_Contrast)
  DeclareModule_filtresadd_function(Gamma , #Filter_Gamma)
  DeclareModule_filtresadd_function(Color , #Filter_Color)
  DeclareModule_filtresadd_function(Color_hue , #Filter_Color_hue)
  DeclareModule_filtresadd_function(color_effect , #Filter_color_effect)
  DeclareModule_filtresadd_function(Colorize , #Filter_Colorize)
  DeclareModule_filtresadd_function(Hollow , #Filter_Hollow)
  DeclareModule_filtresadd_function(SquareLaw_Lightening , #Filter_SquareLaw_Lightening)
  DeclareModule_filtresadd_function(Sepia , #Filter_Sepia)
  DeclareModule_filtresadd_function(Exposure , #Filter_Exposure)
  DeclareModule_filtresadd_function(Negatif , #Filter_Negatif)
  DeclareModule_filtresadd_function(teinte , #Filter_Teinte)
  DeclareModule_filtresadd_function(Normalize_Color_Filter, #Filter_Normalize_Color_Filter)
  DeclareModule_filtresadd_function(ColorPermutation , #Filter_ColorPermutation)
  DeclareModule_filtresadd_function(Dichromatic , #Filter_Dichromatic)
  DeclareModule_filtresadd_function(FalseColour , #Filter_FalseColour)
  DeclareModule_filtresadd_function(PencilImage , #Filter_PencilImage)
  
  DeclareModule_filtresadd_function(GlowEffect_IIR , #Filter_GlowEffect_IIR)
  DeclareModule_filtresadd_function(Emboss , #Filter_Emboss)
  DeclareModule_filtresadd_function(FakeHDR , #Filter_Fake_Hdr)
  DeclareModule_filtresadd_function(pencil , #Filter_pencil)
  DeclareModule_filtresadd_function(CharcoalImage , #Filter_CharcoalImage)
  DeclareModule_filtresadd_function(Histogram , #Filter_Histogram)
  
  DeclareModule_filtresadd_function(Diffuse , #Filter_Diffuse)
  DeclareModule_filtresadd_function(Emboss_bump , #Filter_Emboss_bump)
  DeclareModule_filtresadd_function(Mosaic , #Filter_Mosaic)
  DeclareModule_filtresadd_function(HexMosaic , #Filter_HexMosaic)
  DeclareModule_filtresadd_function(IrregularHexMosaic , #Filter_IrregularHexMosaic)
  DeclareModule_filtresadd_function(Glitch , #Filter_Glitch)
  DeclareModule_filtresadd_function(Kaleidoscope , #Filter_Kaleidoscope)
  DeclareModule_filtresadd_function(FlowLiquify ,  #Filter_FlowLiquify)
  DeclareModule_filtresadd_function(DisplacementMap , #Filter_DisplacementMap)
  DeclareModule_filtresadd_function(Dilate , #Filter_Dilate)
  ;;DeclareModule_filtresadd_function(mettalic_effect , #Filter_mettalic_effect)
  
  ;DeclareModule_filtresadd_function(Convolution3x3,#Filter_Convolution3x3)
  
  DeclareModule_filtresadd_function(FlipH , #Filter_FlipH)
  DeclareModule_filtresadd_function(FlipV , #Filter_FlipV)
  DeclareModule_filtresadd_function(Rotate , #Filter_Rotate)
  DeclareModule_filtresadd_function(Perspective , #Filter_Perspective)
  DeclareModule_filtresadd_function(PerspectiveSimple , #Filter_PerspectiveSimple)
  DeclareModule_filtresadd_function(Translate , #Filter_Translate)
  DeclareModule_filtresadd_function(Spherize , #Filter_Spherize)
  DeclareModule_filtresadd_function(Spiralize , #Filter_Spiralize)
  DeclareModule_filtresadd_function(Ellipze , #Filter_Ellipse)
  DeclareModule_filtresadd_function(Ripple , #Filter_Ripple)
  DeclareModule_filtresadd_function(PinchBulge , #Filter_PinchBulge)
  DeclareModule_filtresadd_function(WaveCircular , #Filter_WaveCircular)
  DeclareModule_filtresadd_function(Lens , #Filter_Lens)
  DeclareModule_filtresadd_function(Tile , #Filter_Tile)
  DeclareModule_filtresadd_function(Perspective2 , #Filter_Perspective2)
  DeclareModule_filtresadd_function(deform_Bend , #Filter_deform_Bend)
  
  ;DeclareModule_filtresadd_function(RgbToYuv , #Filter_RgbToYuv)
  ;DeclareModule_filtresadd_function(YUVtoRGB , #Filter_YUVtoRGB)
  ;DeclareModule_filtresadd_function(RGB_YUV_Modif , #Filter_RGB_YUV_Modif)
  ;DeclareModule_filtresadd_function(RGBtoYIQ , #Filter_RGBtoYIQ)
  ;DeclareModule_filtresadd_function(YIQtoRGB , #Filter_YIQtoRGB)
  ;DeclareModule_filtresadd_function(RGB_YIQ_Modif , #Filter_RGB_YIQ_Modif)
  ;DeclareModule_filtresadd_function(RGBtoLAB , #Filter_RGBtoLAB)
  ;DeclareModule_filtresadd_function(RGB_LAB_Modif , #Filter_RGB_LAB_Modif)
  
  DeclareModule_filtresadd_function(Filtre2_additive , #Filtre2_additive)
  DeclareModule_filtresadd_function(Filtre2_additive_inverted , #Filtre2_additive_inverted)
  DeclareModule_filtresadd_function(Filtre2_alphablend , #Filtre2_alphablend)
  DeclareModule_filtresadd_function(Filtre2_RMSColor , #Filtre2_RMSColor)
  DeclareModule_filtresadd_function(Filtre2_And , #Filtre2_And)
  DeclareModule_filtresadd_function(Filtre2_Average , #Filtre2_Average)
  DeclareModule_filtresadd_function(Filtre2_LightBlend , #Filtre2_LightBlend)
  DeclareModule_filtresadd_function(Filtre2_IntensityBoost , #Filtre2_IntensityBoost)
  DeclareModule_filtresadd_function(Filtre2_BrushUp , #Filtre2_BrushUp)
  DeclareModule_filtresadd_function(Filtre2_Burn , #Filtre2_Burn)
  DeclareModule_filtresadd_function(Filtre2_SubtractiveDodge , #Filtre2_SubtractiveDodge)
  DeclareModule_filtresadd_function(Filtre2_ColorBurn , #Filtre2_ColorBurn)
  DeclareModule_filtresadd_function(Filtre2_ColorDodge , #Filtre2_ColorDodge)
  DeclareModule_filtresadd_function(Filtre2_Contrast , #Filtre2_Contrast)
  DeclareModule_filtresadd_function(Filtre2_Cosine , #Filtre2_Cosine)
  DeclareModule_filtresadd_function(Filtre2_CrossFading , #Filtre2_CrossFading)
  DeclareModule_filtresadd_function(Filtre2_InverseMultiply , #Filtre2_InverseMultiply)
  DeclareModule_filtresadd_function(Filtre2_Darken , #Filtre2_Darken)
  DeclareModule_filtresadd_function(Filtre2_SubtractiveBlend , #Filtre2_SubtractiveBlend)
  DeclareModule_filtresadd_function(Filtre2_Difference , #Filtre2_Difference)
  DeclareModule_filtresadd_function(Filtre2_Div , #Filtre2_Div)
  DeclareModule_filtresadd_function(Filtre2_SoftAdd , #Filtre2_SoftAdd)
  DeclareModule_filtresadd_function(Filtre2_SoftLightBoost , #Filtre2_SoftLightBoost)
  DeclareModule_filtresadd_function(Filtre2_Exponentiale , #Filtre2_Exponentiale)
  DeclareModule_filtresadd_function(Filtre2_Fade , #Filtre2_Fade)
  DeclareModule_filtresadd_function(Filtre2_Fence , #Filtre2_Fence)
  DeclareModule_filtresadd_function(Filtre2_Freeze , #Filtre2_Freeze)
  DeclareModule_filtresadd_function(Filtre2_Glow , #Filtre2_Glow)
  DeclareModule_filtresadd_function(Filtre2_HardContrast , #Filtre2_HardContrast)
  DeclareModule_filtresadd_function(Filtre2_Hardlight , #Filtre2_Hardlight)
  DeclareModule_filtresadd_function(Filtre2_TanBlend , #Filtre2_TanBlend)
  DeclareModule_filtresadd_function(Filtre2_HardlTangent , #Filtre2_HardlTangent)
  DeclareModule_filtresadd_function(Filtre2_Heat , #Filtre2_Heat)
  DeclareModule_filtresadd_function(Filtre2_InHale , #Filtre2_InHale)
  DeclareModule_filtresadd_function(Filtre2_Intensify , #Filtre2_Intensify)
  DeclareModule_filtresadd_function(Filtre2_CosBlend , #Filtre2_CosBlend)
  DeclareModule_filtresadd_function(Filtre2_Interpolation , #Filtre2_Interpolation)
  DeclareModule_filtresadd_function(Filtre2_InvBurn , #Filtre2_InvBurn)
  DeclareModule_filtresadd_function(Filtre2_InvColorBurn , #Filtre2_InvColorBurn)
  DeclareModule_filtresadd_function(Filtre2_InvColorDodge , #Filtre2_InvColorDodge)
  DeclareModule_filtresadd_function(Filtre2_InvDodge , #Filtre2_InvDodge)
  DeclareModule_filtresadd_function(Filtre2_Lighten , #Filtre2_Lighten)
  DeclareModule_filtresadd_function(Filtre2_LinearBurn , #Filtre2_LinearBurn)
  DeclareModule_filtresadd_function(Filtre2_LinearLight , #Filtre2_LinearLight)
  DeclareModule_filtresadd_function(Filtre2_Logarithmic , #Filtre2_Logarithmic)
  DeclareModule_filtresadd_function(Filtre2_Mean , #Filtre2_Mean)
  DeclareModule_filtresadd_function(Filtre2_ColorVivify , #Filtre2_ColorVivify)
  DeclareModule_filtresadd_function(Filtre2_Multiply , #Filtre2_Multiply)
  DeclareModule_filtresadd_function(Filtre2_Negation , #Filtre2_Negation)
  DeclareModule_filtresadd_function(Filtre2_PinLight , #Filtre2_PinLight)
  DeclareModule_filtresadd_function(Filtre2_Or , #Filtre2_Or)
  DeclareModule_filtresadd_function(Filtre2_Overlay , #Filtre2_Overlay)
  DeclareModule_filtresadd_function(Filtre2_Pegtop_soft_light , #Filtre2_Pegtop_soft_light)
  DeclareModule_filtresadd_function(Filtre2_quadritic , #Filtre2_quadritic)
  DeclareModule_filtresadd_function(Filtre2_Screen , #Filtre2_Screen)
  DeclareModule_filtresadd_function(Filtre2_SoftColorBurn , #Filtre2_SoftColorBurn)
  DeclareModule_filtresadd_function(Filtre2_SoftColorDodge , #Filtre2_SoftColorDodge)
  DeclareModule_filtresadd_function(Filtre2_SoftLight , #Filtre2_SoftLight)
  DeclareModule_filtresadd_function(Filtre2_SoftOverlay , #Filtre2_SoftOverlay)
  DeclareModule_filtresadd_function(Filtre2_Stamp , #Filtre2_Stamp)
  DeclareModule_filtresadd_function(Filtre2_Subtractive , #Filtre2_Subtractive)
  DeclareModule_filtresadd_function(Filtre2_Xor , #Filtre2_Xor)
  
EndDeclareModule






Module filtres
  
  Structure Pixel32
    l.l
  EndStructure
  
  ;--
  Macro clamp(c,a,b)
    If c < a : c = a : EndIf
    If c > b : c = b : EndIf
  EndMacro
  
  Macro clamp_rgb(r,g,b)
    If r < 0 : r = 0 : ElseIf r > 255 : r = 255 : EndIf
    If g < 0 : g = 0 : ElseIf g > 255 : g = 255 : EndIf
    If b < 0 : b = 0 : ElseIf b > 255 : b = 255 : EndIf
  EndMacro
  
  Macro clamp_argb(a,r,g,b)
    If a < 0 : a = 0 : ElseIf a > 255 : a = 255 : EndIf
    If r < 0 : r = 0 : ElseIf r > 255 : r = 255 : EndIf
    If g < 0 : g = 0 : ElseIf g > 255 : g = 255 : EndIf
    If b < 0 : b = 0 : ElseIf b > 255 : b = 255 : EndIf
  EndMacro
  
  ;--
  
  Macro seuil_rgb(seuil , r , g , b)
    If r < seuil : r = 0 : ElseIf r > 255 : r = 255 : EndIf
    If g < seuil : g = 0 : ElseIf g > 255 : g = 255 : EndIf
    If b < seuil : b = 0 : ElseIf b > 255 : b = 255 : EndIf
  EndMacro
  
  ;--
  
  Macro min(c,a,b)
    If a < b : c = a : Else : c = b : EndIf
  EndMacro  
  
  Macro max(c,a,b)
    If a > b : c = a : Else : c = b : EndIf
  EndMacro
  
  ;--  
  Macro min3(c, a, b, d)
    If a < b : c = a : Else : c = b : EndIf
    If d < c : c = d : EndIf
  EndMacro
  
  Macro max3(c, a, b, d)
    If a > b : c = a : Else : c = b : EndIf
    If d > c : c = d : EndIf
  EndMacro
  
  ;--
  Macro mib4(c, a, b, d, e)
    If a < b : c = a : Else : c = b : EndIf
    If d < c : c = d : EndIf
    If e < c : c = e : EndIf
  EndMacro
  
  Macro max4(c, a, b, d, e)
    If a > b : c = a : Else : c = b : EndIf
    If d > c : c = d : EndIf
    If e > c : c = e : EndIf
  EndMacro
  
  ;----------------------------------------------------------
  ; Macro pour lancer un traitement multi-thread
  Procedure MultiThread_MT(proc , opt = 0)
    Protected i
    Protected thread = CountCPUs(#PB_System_CPUs)
    clamp(thread, 1 , 128)
    
    If opt > 0 : clamp( opt , 1 , 128) : thread = opt : EndIf
    
    Protected Dim tr(thread)
    For i = 0 To thread - 1 : tr(i) = 0 : Next
    For i = 0 To thread - 1
      CopyStructure(@param, @dim_param(i), parametre)
      dim_param(i)\thread_pos = i
      dim_param(i)\thread_max = thread
      While tr(i) = 0 : tr(i) = CreateThread(proc, @dim_param(i)) : Wend
    Next
    For i = 0 To thread - 1 : If IsThread(tr(i)) > 0 : WaitThread(tr(i)) : EndIf : Next
    FreeArray(tr())
  EndProcedure
  
  ;----------------------------------------------------------
  
  Procedure.f max_2(a.f,b.f)
    If a>b 
      ProcedureReturn a
    Else
      ProcedureReturn b
    EndIf
  EndProcedure
  
  Procedure.f min_2(a.f,b.f)
    If a<b 
      ProcedureReturn a
    Else
      ProcedureReturn b
    EndIf
  EndProcedure
  
  
  Procedure.i Max_4(a.i, b.i, c.i, d.i)
    Protected maxValue = a
    If b > maxValue : maxValue = b : EndIf
    If c > maxValue : maxValue = c : EndIf
    If d > maxValue : maxValue = d : EndIf
    ProcedureReturn maxValue
  EndProcedure
  
  Procedure.i Max8(a.i, b.i, c.i, d.i, e.i, f.i, g.i, h.i)
    Protected maxValue = a
    If b > maxValue : maxValue = b : EndIf
    If c > maxValue : maxValue = c : EndIf
    If d > maxValue : maxValue = d : EndIf
    If e > maxValue : maxValue = e : EndIf
    If f > maxValue : maxValue = f : EndIf
    If g > maxValue : maxValue = g : EndIf
    If h > maxValue : maxValue = h : EndIf
    ProcedureReturn maxValue
  EndProcedure
  
  ;--
  
  Macro GetRGB(var,r,g,b)
    r = (var & $ff0000) >> 16
    g = (var & $00ff00) >> 8
    b = (var & $0000ff) 
  EndMacro 
  
  Macro GetARGB(var,a,r,g,b)
    a = (var & $ff000000) >> 24
    r = (var & $00ff0000) >> 16
    g = (var & $0000ff00) >> 8
    b = (var & $000000ff) 
  EndMacro
  
  ;--
  
  ;-- perlin noise
  ; Gradients de base pour Perlin (8 directions)
  Global Dim gradients(15, 1)
  
  Procedure Normalize(*x.Float, *y.Float)
    Protected len.f = Sqr(*x\f * *x\f + *y\f * *y\f)
    If len <> 0
      *x\f / len
      *y\f / len
    EndIf
  EndProcedure
  
  Procedure SetupGradients(mode)
    Select mode
      Case 0 ; Classique 8 directions
        gradients(0,0) = 1 : gradients(0,1) = 0
        gradients(1,0) = -1 : gradients(1,1) = 0
        gradients(2,0) = 0 : gradients(2,1) = 1
        gradients(3,0) = 0 : gradients(3,1) = -1
        gradients(4,0) = 1 : gradients(4,1) = 1
        gradients(5,0) = -1 : gradients(5,1) = 1
        gradients(6,0) = 1 : gradients(6,1) = -1
        gradients(7,0) = -1 : gradients(7,1) = -1
        
      Case 1 ; 16 directions radiales uniformes
        For i = 0 To 15
          gradients(i,0) = Cos(i * 2.0 * #PI / 16)
          gradients(i,1) = Sin(i * 2.0 * #PI / 16)
        Next
        
      Case 2 ; Vecteurs verticaux modifiés
        gradients(0,0) = 0 : gradients(0,1) = 1
        gradients(1,0) = 0 : gradients(1,1) = -1
        gradients(2,0) = 0.3 : gradients(2,1) = 1
        gradients(3,0) = -0.3 : gradients(3,1) = 1
        ;For i = 4 To 15
        ;gradients(i,0) = -1 : gradients(i,1) = 1
        ;Next
        
      Case 3
        ; croix 4 directions
        gradients(0,0)=1  : gradients(0,1)=0
        gradients(1,0)=-1 : gradients(1,1)=0
        gradients(2,0)=0  : gradients(2,1)=1
        gradients(3,0)=0  : gradients(3,1)=-1
        
      Case 4
        ; diagonales 4 directions
        gradients(0,0)=1  : gradients(0,1)=1
        gradients(1,0)=-1 : gradients(1,1)=1
        gradients(2,0)=1  : gradients(2,1)=-1
        gradients(3,0)=-1 : gradients(3,1)=-1
        
      Case 5 ; Aléatoires normalisés
        For i = 0 To 15
          gradients(i,0) = Random(200) / 100.0 - 1.0
          gradients(i,1) = Random(200) / 100.0 - 1.0
          Normalize(@gradients(i,0), @gradients(i,1))
        Next
    EndSelect
  EndProcedure
  
  Procedure.f Fade(t.f)
    ProcedureReturn t*t*t*(t*(t*6 - 15) + 10)
  EndProcedure
  
  Procedure.f Lerp(a.f, b.f, t.f)
    ProcedureReturn a + t * (b - a)
  EndProcedure
  
  Procedure.f DotGridGradient(ix, iy, x.f, y.f)
    Protected gradientIndex = ((ix * 1836311903) ! (iy * 2971215073)) & 7
    Protected gx.f = gradients(gradientIndex, 0)
    Protected gy.f = gradients(gradientIndex, 1)
    Protected dx.f = x - ix
    Protected dy.f = y - iy
    ProcedureReturn (dx * gx + dy * gy)
  EndProcedure
  
  Procedure.f PerlinNoise(x.f, y.f)
    Protected x0 = Int(x), x1 = x0 + 1
    Protected y0 = Int(y), y1 = y0 + 1
    Protected sx.f = Fade(x - x0)
    Protected sy.f = Fade(y - y0)
    Protected n0.f = DotGridGradient(x0, y0, x, y)
    Protected n1.f = DotGridGradient(x1, y0, x, y)
    Protected ix0.f = Lerp(n0, n1, sx)
    Protected n2.f = DotGridGradient(x0, y1, x, y)
    Protected n3.f = DotGridGradient(x1, y1, x, y)
    Protected ix1.f = Lerp(n2, n3, sx)
    ProcedureReturn Lerp(ix0, ix1, sy) * 0.5 + 0.5 ; normalisé [0,1]
  EndProcedure
  
  Procedure.f PerlinFractal(x.f, y.f, octaves=4, persistence.f=0.5)
    Protected total.f = 0.0
    Protected frequency.f = 1.0
    Protected amplitude.f = 1.0
    Protected maxAmplitude.f = 0.0
    For i = 0 To octaves - 1
      total + PerlinNoise(x * frequency, y * frequency) * amplitude
      maxAmplitude + amplitude
      amplitude * persistence
      frequency * 2.0
    Next
    ProcedureReturn total / maxAmplitude
  EndProcedure
  
  ;--
  Procedure BilinearSample(*src, lg, ht, x.f, y.f)
    Protected x0 = Int(x)
    Protected y0 = Int(y)
    Protected x1 = x0 + 1
    Protected y1 = y0 + 1
    If x1 >= lg : x1 = lg - 1 : EndIf
    If y1 >= ht : y1 = ht - 1 : EndIf
    Protected dx = x - x0
    Protected dy = y - y0
    
    Protected offset00 = (y0 * lg + x0) * 4
    Protected offset10 = (y0 * lg + x1) * 4
    Protected offset01 = (y1 * lg + x0) * 4
    Protected offset11 = (y1 * lg + x1) * 4
    
    Protected c00 = PeekL(*src + offset00)
    Protected c10 = PeekL(*src + offset10)
    Protected c01 = PeekL(*src + offset01)
    Protected c11 = PeekL(*src + offset11)
    
    ; Extraire composants ARGB
    Protected a00 = (c00 >> 24) & $FF
    Protected r00 = (c00 >> 16) & $FF
    Protected g00 = (c00 >> 8) & $FF
    Protected b00 = c00 & $FF
    
    Protected a10 = (c10 >> 24) & $FF
    Protected r10 = (c10 >> 16) & $FF
    Protected g10 = (c10 >> 8) & $FF
    Protected b10 = c10 & $FF
    
    Protected a01 = (c01 >> 24) & $FF
    Protected r01 = (c01 >> 16) & $FF
    Protected g01 = (c01 >> 8) & $FF
    Protected b01 = c01 & $FF
    
    Protected a11 = (c11 >> 24) & $FF
    Protected r11 = (c11 >> 16) & $FF
    Protected g11 = (c11 >> 8) & $FF
    Protected b11 = c11 & $FF
    
    ; Interpolation bilinéaire
    Protected a = a00 * (1-dx) * (1-dy) + a10 * dx * (1-dy) + a01 * (1-dx) * dy + a11 * dx * dy
    Protected r = r00 * (1-dx) * (1-dy) + r10 * dx * (1-dy) + r01 * (1-dx) * dy + r11 * dx * dy
    Protected g = g00 * (1-dx) * (1-dy) + g10 * dx * (1-dy) + g01 * (1-dx) * dy + g11 * dx * dy
    Protected b = b00 * (1-dx) * (1-dy) + b10 * dx * (1-dy) + b01 * (1-dx) * dy + b11 * dx * dy
    
    ProcedureReturn (Int(a) << 24) | (Int(r) << 16) | (Int(g) << 8) | Int(b)
  EndProcedure
  
  ;--
  Procedure Clear_Data_Filter(*p.parametre)
    *p\source = 0
    *p\cible = 0
    *p\mask = 0
    *p\lg.l = 0
    *p\ht.l = 0
    *p\thread_max = 0
    *p\thread_pos = 0
    *p\mask_type = 0
    *p\info_active = 0
    *p\typ = 0
    *p\name = ""
    *p\remarque = ""
    For i = 0 To 10
      *p\convolution3[i] = 0
      *p\addr[i] = 0
      *p\option[i] = 0
      *p\info[i] =""
      *p\info_data(i,0) = 0
      *p\info_data(i,1) = 0
      *p\info_data(i,2) = 0
    Next
  EndProcedure
  
  
  ;-------------------------------------------------------------------
  
  Procedure dither_grascale(*p.parametre)
    Protected *source = *p\source
    Protected *cible = *p\cible
    Protected total = *p\lg * *p\ht
    Protected *srcPixel.Pixel32, *dstPixel.Pixel32, r, g, b
    Protected startPos = (*p\thread_pos * total) / *p\thread_max
    Protected endPos   = ((*p\thread_pos + 1) * total) / *p\thread_max
    If endPos >= total : endPos = total - 1 :EndIf
    For i = startPos To endPos
      *srcPixel = *source + (i << 2)
      *dstPixel = *cible  + (i << 2)
      getrgb(*srcPixel\l , r , g , b)
      *dstPixel\l = ((r * 54 + g * 183 + b * 18) >> 8) * $10101
    Next
  EndProcedure
  
  Macro dither(name1 , name2)
    ; Affichage des informations de configuration si demandé
    If param\info_active
      param\typ = #Filter_Type_Dither
      param\name = name2
      param\remarque = "Attention, fonction non multithreadée"
      param\info[0] = "Nb de couleurs"
      param\info[1] = "Noir et blanc"
      param\info[2] = "Masque binaire"
      param\info_data(0,0) = 6 : param\info_data(0,1) = 64  : param\info_data(0,2) = 6 ; option[0] → niveaux
      param\info_data(1,0) = 0 : param\info_data(1,1) = 1  : param\info_data(1,2) = 0  ; [1] : N&B
      param\info_data(2,0) = 0 : param\info_data(2,1) = 2  : param\info_data(2,2) = 0  ; [2] : masque 
      ProcedureReturn
    EndIf
    
    Protected *source = *param\source
    Protected *cible  = *param\cible
    Protected *mask   = *param\mask
    Protected lg = *param\lg, ht = *param\ht
    Protected levels = *param\option[0]
    Protected i , var
    
    If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
    
    Protected thread = 1 ; CountCPUs(#PB_System_CPUs)
    Protected Dim tr(thread)

    ; Préparation image (gris ou copie)
    Protected *srcPixel.Pixel32, *dstPixel.Pixel32, r, g, b
    param\addr[0] = *source
    param\addr[1] = *cible
    If *param\option[1] : MultiThread_MT(@dither_grascale()) : Else : CopyMemory(*source, *cible, lg * ht * 4) : EndIf
    
    ; Table de quantification
    clamp(levels, 2,254)
    Protected *ndc = AllocateMemory(255)
    Protected Steping.f = 255.0 / (levels - 1)
    For i = 0 To 255
      var = i / Steping
      var = var * Steping
      PokeA(*ndc + i , var)
    Next
    
    *param\addr[2] = *ndc
    MultiThread_MT(name1) 
    If *param\mask And *param\option[2] : *param\mask_type = *param\option[2] - 1 : MultiThread_MT(@_mask()) : EndIf
    ; Libération mémoire
    FreeMemory(*ndc)
    FreeArray(tr())
  EndMacro
  
  ;-------------------------------------------------------------------
  Procedure _mask(*p.parametre)
    If *p\source_mask = 0 Or *p\cible = 0 Or *p\mask = 0 : ProcedureReturn : EndIf
    Protected i, a.l , r.l, g.l, b.l, a1.l , r1.l , g1.l , b1.l, maskVal.l
    ; Déclarations de pointeurs sur les buffers source, destination, et masque
    Protected *srcPixel.Pixel32
    Protected *dstPixel.Pixel32
    Protected *makPixel.Pixel32
    ; Nombre total de pixels
    Protected totalPixels = *p\lg * *p\ht
    ; Détermination de la plage de pixels à traiter par ce thread
    Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
    Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
    ; Boucle sur chaque pixel à traiter
    For i = startPos To endPos - 1
      ; Récupération des adresses des pixels source, destination et masque
      *srcPixel = *p\source_mask + (i << 2)
      *dstPixel = *p\cible + (i << 2)
      *makPixel = *p\mask   + (i << 2)
      ; Extraction de la valeur du masque (octet de poids faible = canal rouge)
      maskVal = *makPixel\l & $ff
      If *p\mask_type
        ; --- Mode 1 : MASQUE BINAIRE (seuil)
        ; Si valeur du masque < 127, on ignore la saturation → on recopie l’original
        If maskVal < 127 : *dstPixel\l = *srcPixel\l : EndIf
      Else
        ; --- Mode 0 : MASQUE PROGRESSIF (fusion douce)
        ; Mélange progressif entre l’image d’origine et l’image saturée
        ; Décomposition ARGB des pixels source (a1,r1,g1,b1) et destination (a,r,g,b)
        getargb(*srcPixel\l, a1, r1, g1, b1) ; image d'origine
        getargb(*dstPixel\l, a , r , g , b ) ; image saturée
                                             ; Mélange des composantes selon la valeur du masque (alpha blending inversé)
        a = ((a  * maskVal + a1 * (255 - maskVal)) >> 8)
        r = ((r  * maskVal + r1 * (255 - maskVal)) >> 8)
        g = ((g  * maskVal + g1 * (255 - maskVal)) >> 8)
        b = ((b  * maskVal + b1 * (255 - maskVal)) >> 8)
        ; Reconstruction du pixel final
        *dstPixel\l = (a << 24) + (r << 16) + (g << 8) + b
      EndIf
    Next
  EndProcedure
  
  ;-------------------------------------------------------------------
  Macro filter_start(name , opt)
    
    If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
    Protected *tempo = 0
    If *param\source <> *param\cible
      *param\addr[0] = *param\source
      *param\addr[1] = *param\cible
    Else
      *tempo = AllocateMemory(*param\lg * *param\ht * 4)
      If Not *tempo : ProcedureReturn : EndIf
      CopyMemory(*param\source , *tempo , *param\lg * *param\ht * 4)
      *param\addr[0] = *tempo
      *param\addr[1] = *param\cible    
    EndIf
    MultiThread_MT(name)
    If *param\mask And *param\option[opt] : *param\mask_type = *param\option[opt] - 1 : MultiThread_MT(@_mask()) : EndIf
    If *tempo : FreeMemory(*tempo) : EndIf
    
  EndMacro
  ;-------------------------------------------------------------------
  
  ; charge une image et la convertie en 32bit
  Procedure load_image_32(nom,file$)
    Protected nom_p.i , temps_p.i , x.l , y.l , r.l,g.l,b.l , i.l
    Protected lg.l , ht.l , depth.l , temps.i  , dif.l , dif1.l
    If file$ = "" : ProcedureReturn 0 : EndIf
    If Not ReadFile( 0, file$)  : ProcedureReturn 0 : Else : CloseFile(0) : EndIf
    LoadImage(nom,file$)
    If Not IsImage(nom) : ProcedureReturn 0 : EndIf
    StartDrawing(ImageOutput(nom))
    Depth = OutputDepth()
    StopDrawing()
    If Depth=24
      CopyImage(nom,temps)
      FreeImage(nom)
      StartDrawing(ImageOutput(temps))
      temps_p = DrawingBuffer()
      lg = ImageWidth(temps)
      ht = ImageHeight(temps)
      dif = DrawingBufferPitch() - (lg*3)
      StopDrawing()
      CreateImage(nom,lg,ht,32)
      StartDrawing(ImageOutput(nom))
      nom_p = DrawingBuffer()
      StopDrawing()
      For y=0 To ht-1
        For x=0 To lg-1
          i = ((y*lg)+x)*3
          r=PeekA(temps_p + i + 2 + dif1)
          g=PeekA(temps_p + i + 1 + dif1)
          b=PeekA(temps_p + i + 0 + dif1)
          PokeL(nom_p + ((y*lg)+x)*4 , r<<16 + g<<8 + b)
        Next
        dif1 = dif1 + dif
      Next
      FreeImage(temps) ; supprime l'image 24bits
    EndIf
    ProcedureReturn 1
  EndProcedure
  
  
  ;-------------------------------------------------------------------
  ;-- IncludeFile
  
  EnableExplicit 
  IncludePath "filtres\"
  
  XIncludeFile "blur.pbi"
  XIncludeFile "edge_detection.pbi"
  XIncludeFile "dither.pbi"
  XIncludeFile "couleur.pbi"
  XIncludeFile "deform.pbi"
  XIncludeFile "autre.pbi"
  XIncludeFile "fx.pbi"
  
  ;XIncludeFile "fx\mettalic_effect.pbi"
  
  ;XIncludeFile "Convolution\Convol3x3.pbi"
  
  ;XIncludeFile "Color_Space\RgbToYuv.pbi"
  ;XIncludeFile "Color_Space\YUVtoRGB.pbi"
  ;XIncludeFile "Color_Space\RGB_YUV_Modif.pbi"
  ;XIncludeFile "Color_Space\RGBtoYIQ.pbi"
  ;XIncludeFile "Color_Space\YIQtoRGB.pbi"
  ;XIncludeFile "Color_Space\RGB_YIQ_Modif.pbi"
  ;XIncludeFile "Color_Space\RGBtoLAB.pbi"
  ;XIncludeFile "Color_Space\RGB_LAB_Modif.pbi"
  
  XIncludeFile "mix.pbi"
EndModule

:arrow: Download de l'ensemble des codes https://github.com/pbcodex/Graphic-Filt ... s/main.zip
Dernière modification par manababel le mer. 17/sept./2025 18:01, modifié 8 fois.
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

la demo

Code : Tout sélectionner


IncludeFile "filtres.pbi"
UseModule filtres


;---------------------------------------------------------

#img1 = 1
#img2 = 2
#img3 = 3
#save = 4
#save2 = 5
#quit = 6
#mask_e = 7
#mask_d = 8
#mask_n = 9
#copy1 = 10
#copy2 = 11
#copy3 = 12

#source1 = 1
#source2 = 2
#mask = 3
#miniature = 4
#cible = 5
#Black_image = 6
#tempo = 7
#cible_aff = 8

Global image_selected = -1

mask_enable = 0

#filtre_pos = 1000
#filtre_windows_pos = #filtre_pos + 1000

Structure filtre
  name.s
  pos.i
  Array opt.s(9,9)
  Array c3.s(99)
EndStructure
Global NewList list_filtre.filtre()

Structure windows
  name.s
  id_fenetre.i
  id_filtre.i
  item.i
  opt.i[20]
EndStructure
Global NewList list_windows.windows()

Global lg , ht
Global tx , ty
Global scx.f , scy.f
Global px , py
Global imagetx , imagety
Global pym
Global lgi ,hti

Global windows_id
;----------


Procedure draw_miniature(image , pos)
  If Not IsImage(image) : ProcedureReturn : EndIf
  If IsImage(#miniature) : FreeImage(#miniature) : EndIf
  CopyImage(image,#miniature)
  ResizeImage(#miniature,tx,ty)
  StartDrawing(WindowOutput(0))
  DrawImage(ImageID(#miniature) , px , py + (pym * pos + 10) * scy)
  StopDrawing()
  FreeImage(#miniature)
EndProcedure

Procedure draw_miniature_selected()
  StartDrawing(WindowOutput(0))
  For i = 0 To 2
    x = px - 2
    y = (py + (pym * i + 10) * scy) - 2 
    If  i = image_selected : col = $ff00 : Else : col = $7f7f7f : EndIf
    Box(x , y , tx + 2, 2 , col)
    Box(x , y , 2 , ty + 2, col)
    Box(x + tx + 2 , y , 2 , ty + 2, col)
    Box(x , y + ty + 2 , tx + 2 , 2, col)
  Next
  
  var = image_selected + 1
  If IsImage(var)
    CopyImage(var , #cible_aff)
    ResizeImage(#cible_aff,imagetx * scx , imagety * scy , #PB_Image_Raw)
    DrawImage(ImageID(#cible_aff), (lg/10 + 5) * scx , py * scy)
    FreeImage(#cible_aff)
  EndIf
  
  StopDrawing()
EndProcedure

Procedure load_img(var)
  file$ = OpenFileRequester("Image","","",0)
  ;If LoadImage(var,file$) = 0
  If load_image_32(var,file$) = 1 
    If var = #source1 ; charge l'image 1
      lgi = ImageWidth(#source1)
      hti = ImageHeight(#source1)
      If IsImage(#source2)  : FreeImage(#source2) : draw_miniature(#black_image,1) : EndIf
      If IsImage(#mask) : FreeImage(#mask) : draw_miniature(#black_image,2) : EndIf
      If IsImage(#cible) : FreeImage(#cible) : EndIf; efface la cible
      CreateImage(#cible , lgi , hti , 32)
    Else
      ResizeImage(var,lgi ,hti)
    EndIf   
    draw_miniature(var,var-1)
  EndIf
EndProcedure

Procedure copy_image(var)
  If Not IsImage(var)
    If var = #source1 : ProcedureReturn 
    Else
      If Not IsImage(#source1) : ProcedureReturn : EndIf
      CopyImage(#source1,var)
    EndIf
  EndIf
  *source = 0
  *cible = 0
  If IsImage(var) And StartDrawing(ImageOutput(var)) : *source = DrawingBuffer() : StopDrawing() : EndIf
  If IsImage(#cible) And StartDrawing(ImageOutput(#cible)) : *cible = DrawingBuffer() : StopDrawing() : EndIf
  lg0 = ImageWidth(var)
  ht0 = ImageHeight(var)     
  If *source <> 0 And *cible <> 0
    CopyMemory(*cible , *source , lg0 * ht0 * 4)
    draw_miniature(var , var - 1)
  EndIf
EndProcedure

;----------

Procedure create_menu_filtre()
  
  MenuTitle("Filtre")
  mem = -1
  param\info_active = 1
  For i = 0 To 999
    If tabfunc(i) <> 0
      CallCFunctionFast(tabfunc(i),param)
      If param\typ <> mem
        mem = param\typ
        ;CloseSubMenu()
        Select param\typ
          Case #Filter_Type_Blur
            ;CloseSubMenu()
            OpenSubMenu("Blur")
          Case #Filter_Type_Edge_Detection
            CloseSubMenu()
            OpenSubMenu("Edge_Detection")
          Case #Filter_Type_Color
            CloseSubMenu()
            OpenSubMenu("Color")
          Case #Filter_Type_Dither
            CloseSubMenu()
            OpenSubMenu("Dither")
          Case #Filter_Type_FX
            CloseSubMenu()
            OpenSubMenu("FX")
          Case #Filter_Type_Convolution
            CloseSubMenu()
            OpenSubMenu("Convolution")
          Case #Filter_Type_Deform
            CloseSubMenu()
            OpenSubMenu("Deform")
          Case #Filter_Type_Color_Space
            CloseSubMenu()
            OpenSubMenu("Color_Space")  
          Case #Filter_Type_autre
            CloseSubMenu()
            OpenSubMenu("test")   
          Case #Filter_Type_mix
            CloseSubMenu()
            OpenSubMenu("Mix") 
          Default
            CloseSubMenu()
            OpenSubMenu("Autres")
        EndSelect
      EndIf
      MenuItem(i + #filtre_pos,Str(i) + " - " +param\name)
      
    EndIf
  Next
  param\info_active = 0
  
EndProcedure

;----------

Procedure open_windows(pos) 
  
  Dim w_info.s(20)
  Dim w_data.l(20,3)
  
  ; test si il y a deja des fentres ouvertes
  If ListSize(list_windows()) < 1 : windows_id = 1 : EndIf
  
  AddElement(list_windows())
  list_windows()\id_fenetre = windows_id
  list_windows()\id_filtre = pos ; numero du filtre
  windows_id + 1
  
  lgy = 60
  name$ = ""
  
  ;demande d'info au filtre
  Clear_Data_Filter(param) ; met a 0 tous les parametres de la structure des filtres
  param\info_active = 1
  If tabfunc(list_windows()\id_filtre) <> 0 : CallCFunctionFast(tabfunc(list_windows()\id_filtre),param) : EndIf ; recupere les paramtres par defaut du filtre
  If param\name <> "" 
    name$ = param\name 
    
    If LCase(Trim(param\name)) = "convolution3x3"
      lgy = 9 * 30
    Else
      
      For i = 0 To 19
        w_info(i) = param\info[i]
        If w_info(i) = "" : lgy = (i + 2) * 25: Break : EndIf
        w_data(i,0) = param\info_data(i,0)
        w_data(i,1) = param\info_data(i,1)
        w_data(i,2) = param\info_data(i,2)
        ;t$ = w_info(i) + " : " + Str(w_data(i,0)) + " : " + Str(w_data(i,1)) + " : " + Str(w_data(i,2)) : Debug t$
      Next
    EndIf
  EndIf
  param\info_active = 0
  
  list_windows()\name = "Window " + " " + Str(list_windows()\id_fenetre) + "     Filtre " + list_windows()\id_filtre + " :  " + name$
  
  If OpenWindow(list_windows()\id_fenetre, 0, 0, 500, lgy, list_windows()\name, #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    StickyWindow(list_windows()\id_fenetre,#True)
    
    If LCase(Trim(param\name)) = "convolution3x3"
      
      For y = 0 To 2
        For x = 0 To 2
          i = y * 3 + x 
          np = #filtre_windows_pos + (list_windows()\id_fenetre * 200) +  i 
          StringGadget(np , 10 + x * 45, 10 + y * 25 , 40 , 20 ,"")
        Next
      Next
      StringGadget(np + 1 , 10 + 45 , 10 + 75 , 40 , 20 ,"")
      StringGadget(np + 2 , 10 + 45 , 10 + 100 , 40 , 20 ,"")
      TextGadget(  np + 3 , 10      , 10 + 75 , 40 , 20 ,"DIV")
      TextGadget(  np + 4 , 10     , 10 + 100 , 40 , 20 ,"ADD")
      
      ComboBoxGadget(np + 5, 200, 10, 250, 25 ) 
      For i= 0 To 99
        param\info_active = i + 2
        CallCFunctionFast(tabfunc(list_windows()\id_fenetre),param)
        If param\name <> ""
          AddGadgetItem(np + 5 , -1 , Str(i + 1) + "___" + param\name)
        Else
          param\info_active = 0
          Break
        EndIf
      Next
      
    Else 
      
      pos = #filtre_windows_pos + (list_windows()\id_fenetre * 200)
      decal = 0
      If param\remarque <> ""
        decal = 1
        TextGadget( pos + 9 , 5 , 5 , 490 , 25 , param\remarque,#PB_Text_Center | #PB_Text_Border)
      EndIf
      
      For i = 0 To 19
        ; 200 = decalage entre chaque fenetre ; une fenetre peut contenir 20 gadgets
        ; 10 = decalage entre chaque option , un gadget peut avoir 10 options
        np = pos + ( i * 10 ) 
        posy = i + decal
        If w_info(i) <> ""
          If w_data(i,1) - w_data(i,0) = 1 ; 1 CheckBoxGadget
            CheckBoxGadget(np + 0, 5 , 5 + posy * 25, 100 , 25 , w_info(i) )
          Else ; 1 TrackBarGadget (1 ligne = 5 gadgets)
            TrackBarGadget(np + 0 , 150 , 5 + posy * 25 , 250 , 25 , w_data(i,0) , w_data(i,1) ) ; gadget
            SetGadgetState(np + 0 ,  w_data(i,2) )                                               ; met le TrackBarGadget a la valeur par defaut
            TextGadget(    np + 1,   5  , 5 + posy * 25 , 120 , 25 , w_info(i) )                 ; nom du gadget
            TextGadget(    np + 2,  115 , 5 + posy * 25 , 35 , 25 , Str(w_data(i,0))  ,#PB_Text_Right ) ; valeur min
            TextGadget(    np + 3, 405 , 5 + posy * 25 , 35 , 25 , Str(w_data(i,1)) )                   ; valeur max
            TextGadget(    np + 4, 455 , 5 + posy * 25 , 35 , 25 , Str(w_data(i,2)) )                   ; valeur selectionnée 
          EndIf
          param\option[i] = w_data(i,2)
          list_windows()\opt[i] = w_data(i,2)
        EndIf
      Next
    EndIf
  EndIf
  
  
  FreeArray(w_info())
  FreeArray(w_data())
EndProcedure


Procedure close_windows(id)
  ForEach list_windows()
    If list_windows()\id_fenetre = id
      DeleteElement(list_windows())
      Break
    EndIf
  Next
  CloseWindow(id)
EndProcedure


Procedure update_windows()
  id = GetActiveWindow()
  If id < 1 : ProcedureReturn 0 : EndIf
  ok = 0
  ForEach list_windows() : If list_windows()\id_fenetre = id : ok = 1 : Break : EndIf : Next
  If Not ok : ProcedureReturn 0 : EndIf
  ;id = list_windows()\id 
  ev = EventGadget()
  np = ((ev - #filtre_windows_pos ) - (id * 200))
  If np < 0 : ProcedureReturn 0 : EndIf
  ; restore toutes les donnees du fitre
  For i = 0 To 19 : param\option[i] = list_windows()\opt[i] : Next
  np = ((ev - #filtre_windows_pos ) - (id * 200)) / 10
  If np < 0 Or np > 19 : ProcedureReturn 0 : EndIf
  var = GetGadgetState(ev)
  If param\option[np] <> var
    list_windows()\opt[np] = var
    param\option[np] = var  
    If IsGadget(ev + 4) : SetGadgetText(ev + 4 ,Str(var)) : EndIf
    ProcedureReturn 1
  EndIf
  ProcedureReturn 0
EndProcedure

;----------
;-- programme

lg = 1600 
ht = 900 
lg = lg *  100 / DesktopUnscaledX(100)
ht = ht *  100 / DesktopUnscaledY(100)
scx = (100 / DesktopUnscaledX(100))
scy = (100 / DesktopUnscaledY(100))

If OpenWindow(0, 0, 0, lg, ht, "test_filtres", #PB_Window_SystemMenu | #PB_Window_ScreenCentered | #PB_Window_SizeGadget | #PB_Window_MinimizeGadget | #PB_Window_MaximizeGadget)
  
  CreateMenu(0, WindowID(0))
  MenuTitle("File")
  
  OpenSubMenu("Load")
  MenuItem( #img1, "Load Image 1")
  MenuItem( #img2, "Load Image 2")
  MenuItem( #img3, "Load mask")
  CloseSubMenu()
  
  OpenSubMenu("Save")   
  MenuItem( #save, "Save BMP")
  ;MenuItem( 3, "Save JPG")
  MenuItem( #save2, "Save Clipboard")
  CloseSubMenu()
  MenuBar()
  ;MenuTitle("Quit")
  MenuItem( #quit, "Quit")
  
  MenuTitle("Valider")
  MenuItem( #copy1 , "modifier la source 1")
  MenuItem( #copy2 , "modifier la source 2")
  MenuItem( #copy3 , "modifier le Mask")
  
  create_menu_filtre()
  
  px = 5
  py = 5
  tx = lg / 10.526
  ty = ht / 6.105
  pym = ht / 5.6
  imagetx = (lg - lg/20) - (20 + lg/20)
  imagety = ht-40
  FrameGadget(100, lg/10 + 5, py,  imagetx , imagety, "" )
  FrameGadget(101, px, py + pym * 3 ,  tx, ht - (py + pym * 3.2), "" )
  
  CreateImage(#Black_image,tx*scx,ty*scy)
  StartDrawing(ImageOutput(#Black_image))
  Box(0,0,tx*scx,ty*scy,0)
  StopDrawing()
  
  draw_miniature(#Black_image,0)
  draw_miniature(#Black_image,1)
  draw_miniature(#Black_image,2)
  
  ;-- boucle
  ;Repeat
    Repeat
      update = 0
      Event = WaitWindowEvent()
      
      If EventType() = #PB_EventType_LeftClick 
        ;position buggé en y
        x = WindowMouseX(0)
        y = WindowMouseY(0)
        x1 = px
        x2 = x1 + (tx * scx)
        If x >= x1 And x <= x2
          For i = 0 To 2
            y1 = (py + pym * i)
            y2 = y1 + (ty * scy)
            If y >= y1 And y <= y2
              image_selected = i
              draw_miniature_selected()
            EndIf
          Next
        EndIf
      EndIf
      
      Select Event
          
        Case #PB_Event_Menu
          var = EventMenu()
          Select var
              
            Case #img1
              load_img(#source1)      
            Case #img2
              If IsImage(#source1) : load_img(#source2) : EndIf
            Case #img3
              If IsImage(#source1) : load_img(#mask) : EndIf
              
            Case #filtre_pos To (#filtre_pos + 500)
              pos = (var - #filtre_pos)
              SelectElement(list_filtre(), pos)
              open_windows(pos)
              update0 = 1
              
            Case #copy1 : copy_image(#source1)
            Case #copy2 : copy_image(#source2)
            Case #copy3 : copy_image(#mask)
              
            Case #save
              nom$ = SaveFileRequester("Save BMP", "", "", 0)
              If nom$ <> "" : SaveImage(#source1, nom$+".bmp" ,#PB_ImagePlugin_BMP ) : EndIf
              
            Case #save2
              SetClipboardImage(#source1)
              
            Case #quit
              quit = 1
          EndSelect
          
          
        Case #PB_Event_CloseWindow
          evt1 = EventWindow()
          ;If evt1 = 0
            ;If IsImage(#cible) : FreeImage(#cible) : EndIf
            ;If IsImage(#Black_image) : FreeImage(#Black_image) : EndIf
            ;CloseWindow(0)
            ;End
          ;Else
            If Event = #PB_Event_CloseWindow And evt1 <> 0
              event = close_windows(evt1)
            EndIf
            ;Event = 0
          ;EndIf
          
      EndSelect
      
    ;Until Event = 0
    
    update = update_windows() 
    
    If (update = 1 Or update0 = 1) And IsImage(#source1) And ListSize(list_windows()) > 0
      
      param\source = 0 : param\source2 = 0 : param\cible = 0 : param\mask = 0
      *source1 = 0 : *source2 = 0 : *cible = 0 : *mask = 0 : *tempo = 0
      If IsImage(#tempo) : FreeImage(#tempo) : EndIf
      If IsImage(#source1) And StartDrawing(ImageOutput(#source1)) : *source1 = DrawingBuffer() : StopDrawing() : EndIf
      If IsImage(#source2) And StartDrawing(ImageOutput(#source2)) : *source2 = DrawingBuffer() : StopDrawing() : EndIf
      If IsImage(#cible) And StartDrawing(ImageOutput(#cible))     : *cible   = DrawingBuffer() : StopDrawing() : EndIf
      If IsImage(#mask) And StartDrawing(ImageOutput(#mask))       : *mask    = DrawingBuffer() : StopDrawing() : EndIf
      
      Select image_selected
        Case 0
          If *source1 : CopyImage(#source1 , #tempo) : EndIf
        Case 1
          If *source2 : CopyImage(#source2 , #tempo) : EndIf
        Case 2
          If *mask : CopyImage(#mask , #tempo) : EndIf
      EndSelect
      
      If IsImage(#tempo) And StartDrawing(ImageOutput(#tempo)) : *tempo = DrawingBuffer() : StopDrawing() : EndIf
      
      param\source = *tempo
      param\source2 = *source2
      param\cible = *cible
      param\mask = *mask
      param\source_mask = param\source
      param\lg = ImageWidth(#source1)
      param\ht = ImageHeight(#source1) 
      
      ForEach list_windows()
        
        For i = 0 To 19 : param\option[i] = list_windows()\opt[i] : Next
        
        If param\typ = #Filter_Type_mix And param\source2 <> 0 Or param\typ <> #Filter_Type_mix
          t = ElapsedMilliseconds()
          If tabfunc(list_windows()\id_filtre) <> 0 : CallCFunctionFast(tabfunc(list_windows()\id_filtre),param) : EndIf
          param\source = param\cible
          t = ElapsedMilliseconds() - t
        EndIf
      Next
      
      StartDrawing(WindowOutput(0))
      If IsImage(#cible)
        CopyImage(#cible , #cible_aff)
        ResizeImage(#cible_aff,imagetx * scx , imagety * scy , #PB_Image_Raw)
        DrawImage(ImageID(#cible_aff), (lg/10 + 5) * scx , py * scy)
        t3$ = "     temps = " +Str(t) + " ms"
        tile$ = list_windows()\name + t3$
        SetWindowTitle(list_windows()\id_fenetre,tile$)
        FreeImage(#cible_aff)
      EndIf
      StopDrawing()
      update = 0
      update0 = 0
      
      If IsImage(#tempo) : FreeImage(#tempo) : EndIf
    EndIf
    
  ;ForEver
  
  Until Event = #PB_Event_CloseWindow Or quit = 1
  If IsImage(#cible) : FreeImage(#cible) : EndIf
  If IsImage(#Black_image) : FreeImage(#Black_image) : EndIf
  CloseWindow(0)
  
EndIf


manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

le fichier est trop long

les filtres "blur" partie 1

blur.pbi

Code : Tout sélectionner

Macro Bilateral_DomainTransform1D_declare(length)
  Protected *buf = param\addr[0]         ; pointeur vers l'image source
  Protected *temp = param\addr[1]        ; pointeur vers le buffer temporaire
  Protected *expLUT = param\addr[2]      ; pointeur vers la table exponentielle (LUT)
  Protected Dim domain.f(length)          ; tableau du domaine cumulatif
  Protected Dim dc.f(length)              ; tableau des différences de couleur
  Protected Dim color_diff.f(length)      ; tableau pour les différences de couleur (non utilisé ici)
  Protected Dim dataR.f(length)           ; tableau pour les valeurs rouge
  Protected Dim dataG.f(length)           ; tableau pour les valeurs vert
  Protected Dim dataB.f(length)           ; tableau pour les valeurs bleu
  Protected i.i, idx.i                    ; indices pour les boucles et LUT
  Protected diff_d.f, alpha.f, frac.f, a0.f, a1.f ; variables pour le calcul du filtre
  Protected pixel0.i, pixel1.i, r0, g0, b0, r1, g1, b1 ; pixels et composantes
  Protected *scr1.pixel32                 ; pointeur pixel courant
  Protected *scr2.pixel32                 ; pointeur pixel voisin
  Protected *dst.pixel32                  ; pointeur destination (non utilisé)
EndMacro

Macro Bilateral_DomainTransform1D_end()
  FreeArray(domain())
  FreeArray(dc())
  FreeArray(color_diff())
  FreeArray(dataR())
  FreeArray(dataG())
  FreeArray(dataB())
EndMacro

Macro Bilateral_DomainTransform1D_sp0(op)
  clamp(diff_d , 0 , 255)                 ; limiter la distance dans le domaine
  idx  = Int(diff_d)                      ; indice entier pour la LUT
  frac = diff_d - idx                     ; fraction pour interpolation
  a0   = PeekF(*expLUT + idx * 4)        ; valeur LUT pour idx
  a1   = PeekF(*expLUT + (Bool(idx < 255) * (idx + 1) + Bool(idx >= 255) * 255) * 4) ; valeur LUT suivante
  alpha = a0 + frac * (a1 - a0)          ; interpolation linéaire
  dataR(i) = dataR(i) + alpha * (dataR(i op 1) - dataR(i)) ; mise à jour rouge
  dataG(i) = dataG(i) + alpha * (dataG(i op 1) - dataG(i)) ; mise à jour vert
  dataB(i) = dataB(i) + alpha * (dataB(i op 1) - dataB(i)) ; mise à jour bleu
EndMacro

Macro Bilateral_DomainTransform1D_sp1(v1)
  *scr1 = *source + (i * v1)             ; pointeur pixel courant
  *scr2 = *scr1 + v1                     ; pointeur pixel suivant
  GetRGB(*scr1\l , r0, g0, b0)           ; récupérer RGB du pixel courant
  GetRGB(*scr2\l , r1, g1, b1)           ; récupérer RGB du pixel suivant
  dataR(i) = r0 : dataG(i) = g0 : dataB(i) = b0 ; initialiser les tableaux de couleurs
  ; Calcul de la différence de couleur en luminance perceptuelle
  dc(i) = Sqr(0.3 * ((r1 - r0) * (r1 - r0)) + 0.59 * ((g1 - g0) * (g1 - g0)) + 0.11 * ((b1 - b0) * (b1 - b0)))
  If dc(i) > 255 : dc(i) = 255 : EndIf     ; limiter à 255
EndMacro

Procedure Bilateral_DomainTransform1D_X(*param.parametre )
  Protected length = *param\lg
  Bilateral_DomainTransform1D_declare(length)       ; déclaration variables locales
  Protected y, pos, start, stop
  start = (*param\thread_pos * *param\ht) / *param\thread_max  ; début de ligne pour ce thread
  stop  = ((*param\thread_pos + 1) * *param\ht) / *param\thread_max ; fin
  If stop > *param\ht : stop = *param\ht : EndIf
  For y = start To stop - 1
    pos = y * length * 4
    Protected *source = *buf + pos
    ; calcul des différences de couleur horizontales
    For i = 0 To length - 2 : Bilateral_DomainTransform1D_sp1(4) : Next
    ; dernier pixel de la ligne
    i = length - 1
    pixel0 = PeekL(*source + (length - 1) * 4)
    GetRGB(pixel0, r0, g0, b0)
    dataR(i) = r0 : dataG(i) = g0 : dataB(i) = b0
    ; calcul du domaine cumulatif
    domain(0) = 0
    For i = 1 To length - 1
      domain(i) = domain(i - 1) + 1.0 + (*param\option[4] * dc(i - 1)) ; distance pondérée
      If domain(i) < domain(i-1) : domain(i) = domain(i-1) : EndIf
    Next
    ; filtrage récursif avant-arrière
    For i = 1 To length - 1   : diff_d = domain(i) - domain(i - 1) : Bilateral_DomainTransform1D_sp0(-) : Next
    For i = length - 2 To 0 Step -1 : diff_d = domain(i + 1) - domain(i) : Bilateral_DomainTransform1D_sp0(+) : Next

    ; stockage final dans le buffer temporaire
    For i = 0 To length - 1
      r0 = dataR(i) : g0 = dataG(i) : b0 = dataB(i)
      clamp_rgb(r0, g0, b0)                  ; clamp entre 0-255
      PokeL(*temp + pos + i * 4, (r0 << 16) | (g0 << 8) | b0) ; stockage BGR
    Next
  Next
  Bilateral_DomainTransform1D_end()
EndProcedure

Procedure Bilateral_DomainTransform1D_Y(*param.parametre )
  Protected length = *param\ht
  Bilateral_DomainTransform1D_declare(length)
  Protected stride = *param\lg * 4           ; pas pour passer d'une ligne à l'autre
  Protected start, stop, x
  start = (*param\thread_pos * *param\lg) / *param\thread_max
  stop  = ((*param\thread_pos + 1) * *param\lg) / *param\thread_max
  If stop > *param\lg : stop = *param\lg : EndIf
  For x = start To stop - 1
    Protected *source = *buf + x * 4
    ; calcul des différences de couleur verticales
    For i = 0 To length - 2 : Bilateral_DomainTransform1D_sp1(stride) : Next
    ; dernier pixel de la colonne
    i = length - 1
    pixel0 = PeekL(*source + (length - 1) * stride)
    GetRGB(pixel0, r0, g0, b0)
    dataR(i) = r0 : dataG(i) = g0 : dataB(i) = b0
    ; calcul du domaine cumulatif vertical
    domain(0) = 0
    For i = 1 To length - 1
      domain(i) = domain(i - 1) + 1.0 + (*param\option[4] * dc(i - 1))
      If domain(i) < domain(i-1) : domain(i) = domain(i-1) : EndIf
    Next
    ; filtrage récursif
    For i = 1 To length - 1   : diff_d = domain(i) - domain(i - 1) : Bilateral_DomainTransform1D_sp0(-) : Next
    For i = length - 2 To 0 Step -1 : diff_d = domain(i + 1) - domain(i) : Bilateral_DomainTransform1D_sp0(+) : Next
    ; stockage final
    For i = 0 To length - 1
      r0 = dataR(i) : g0 = dataG(i) : b0 = dataB(i)
      clamp_rgb(r0, g0, b0)
      PokeL(*temp + x * 4 + i * stride, (r0 << 16) | (g0 << 8) | b0)
    Next
  Next
  Bilateral_DomainTransform1D_end()
EndProcedure

Procedure Bilateral(*param.parametre)
  ; informations pour l'interface utilisateur
  If *param\info_active
    *param\name = "Bilateral"
    *param\typ = #Filter_Type_Blur
    *param\remarque = "adoucie tout en conservant les contours nets"
    *param\info[0] = "nb de passes"
    *param\info[1] = "sigma espace"
    *param\info[2] = "sigma couleur"
    *param\info[3] = "Masque binaire"
    *param\info_data(0,0) = 1 : *param\info_data(0,1) = 5   : *param\info_data(0,2) = 2
    *param\info_data(1,0) = 1 : *param\info_data(1,1) = 100 : *param\info_data(1,2) = 40
    *param\info_data(2,0) = 1 : *param\info_data(2,1) = 100 : *param\info_data(2,2) = 30
    *param\info_data(3,0) = 0 : *param\info_data(3,1) = 2   : *param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  ; vérifier si les pointeurs source et cible sont valides
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  Protected pass          = *param\option[0]    ; nombre de passes
  Protected sigma_space.f = *param\option[1]    ; sigma spatial
  Protected sigma_color.f = *param\option[2]    ; sigma couleur
  Clamp(pass, 1, 5)
  Clamp(sigma_space, 1, 100)
  Clamp(sigma_color, 1, 255)
  ; ---- LUT exponentielle pour la couleur ----
  Protected *expLUT = AllocateMemory(256 * SizeOf(Float))
  If *expLUT = 0 : ProcedureReturn : EndIf 
  Protected d
  For d = 0 To 255 : PokeF(*expLUT + d * 4, Exp(-d / sigma_color)) : Next
  ; ---- Coefficient de domaine cumulatif ----
  *param\option[4] = sigma_space / sigma_color
  ; ---- Buffer temporaire pour stockage intermédiaire ----
  Protected *temp = AllocateMemory(*param\lg * *param\ht * 4)
  If *temp = 0 : FreeMemory(*expLUT) : ProcedureReturn : EndIf
  Protected *buf = *param\source
  *param\addr[2] = *expLUT  ; passer le pointeur LUT
  ; ---- Application du filtrage en passes ----
  For d = 0 To pass - 1
    *param\addr[0] = *buf
    *param\addr[1] = *temp
    MultiThread_MT(@Bilateral_DomainTransform1D_X())  ; filtrage horizontal
    *param\addr[0] = *temp
    *param\addr[1] = *param\cible
    MultiThread_MT(@Bilateral_DomainTransform1D_Y())  ; filtrage vertical
    *buf = *param\cible
  Next
  ; ---- Application du masque éventuel ----
  If *param\mask And *param\option[3] : *param\mask_type = *param\option[3] - 1 : MultiThread_MT(@_mask()) : EndIf
  ; ---- Libération de la mémoire ----
  FreeMemory(*expLUT)
  FreeMemory(*temp)
EndProcedure

;----------------

Macro BoxBlur_declare_variable(lenght)
  Protected *buf1 = *param\addr[0]       ; pointeur vers l'image source
  Protected *buf2 = *param\addr[1]       ; pointeur vers l'image destination
  Protected lg = *param\lg               ; largeur de l'image
  Protected ht = *param\ht               ; hauteur de l'image
  Protected blur = *param\option[5]      ; coefficient de normalisation du flou
  Protected a, r, g, b                   ; accumulation des composantes ARGB
  Protected a1, r1, g1, b1               ; composantes du pixel calculé
  Protected a2, r2, g2, b2               ; composantes du pixel suivant
  Protected x = 0, y = 0                 ; coordonnées dans l'image
  Protected index1, index2, color_32bits ; indices et couleur temporaire
  Protected start, stop, i               ; limites pour le multithreading
  Protected *scr1.Pixel32, *scr2.Pixel32, *scr3.Pixel32
  Protected *scr4.Pixel32, *scr5.Pixel32, *scr6.Pixel32 ; pointeurs temporaires
  Protected *dst.Pixel32                  ; pointeur vers pixel de sortie
  ; calcule la portion d'image à traiter pour chaque thread
  start = (lenght * *param\thread_pos) / *param\thread_max
  stop  = (lenght * (*param\thread_pos + 1)) / *param\thread_max
  If *param\thread_pos = (*param\thread_max - 1) : stop = lenght : EndIf
EndMacro

Macro BoxBlur_sp0(var) ; calcul du noyau
  a = 0 : r = 0 : g = 0 : b = 0
  For i = 0 To opt#var - 1
    *scr5 = *pz + (i << 2)                  ; récupération de l'index du pixel
    *scr6 = *ligne + (*scr5\l << 2)         ; adresse du pixel dans la ligne/colonne
    getargb(*scr6\l, a1, r1, g1, b1)       ; extrait ARGB du pixel
    a + a1 : r + r1 : g + g1 : b + b1      ; accumulation des composantes
  Next
  ; normalisation de la somme selon le facteur blur
  a1 = (a * blur) >> 16 : r1 = (r * blur) >> 16 : g1 = (g * blur) >> 16 : b1 = (b * blur) >> 16
EndMacro

Macro BoxBlur_sp1(var1, var2)
  *scr1 = *pz + (var1 << 2)               ; index du pixel sortant de la fenêtre
  *scr2 = *pz + ((var1 + var2) << 2)      ; index du pixel entrant dans la fenêtre
  *scr3 = *ligne + ((*scr1\l) << 2)       ; adresse du pixel sortant
  *scr4 = *ligne + ((*scr2\l) << 2)       ; adresse du pixel entrant
  getargb(*scr3\l, a1, r1, g1, b1)       ; extrait ARGB du pixel sortant
  getargb(*scr4\l, a2, r2, g2, b2)       ; extrait ARGB du pixel entrant
  a - a1 + a2 : r - r1 + r2 : g - g1 + g2 : b - b1 + b2 ; mise à jour de la somme
  ; calcul du pixel final normalisé
  a1 = (a * blur) >> 16 : r1 = (r * blur) >> 16 : g1 = (g * blur) >> 16 : b1 = (b * blur) >> 16
  *dst = *buf2 + (((lg * y) + x) << 2)   ; adresse du pixel de sortie
  *dst\l = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1 ; écriture ARGB
EndMacro

; Applique un flou horizontal
Procedure BoxBlur_X(*param.parametre) 
  BoxBlur_declare_variable(ht)
  Protected optx = *param\option[0]
  Protected *pz = *param\addr[2]
  optx = (optx * 2) + 1
  Protected *ligne = AllocateMemory(lg << 2, #PB_Memory_NoClear) 
  For y = start To stop - 1
    CopyMemory(*buf1 + ((lg * y) << 2), *ligne, lg << 2)
    BoxBlur_sp0(x) ; calcul du premier pixel
    *dst = *buf2 + (((lg * y) ) << 2)
    *dst\l = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1
    For x = 1 To (lg - 1)
      BoxBlur_sp1((x-1) , optx)
    Next
  Next
  FreeMemory(*ligne)
EndProcedure

; Applique un flou vertical
Procedure BoxBlur_Y(*param.parametre) 
  BoxBlur_declare_variable(lg)
  Protected opty = *param\option[1]
  Protected *pz = *param\addr[3]
  opty = (opty * 2) + 1
  Protected *ligne = AllocateMemory(ht << 2, #PB_Memory_NoClear)
  For x = start To stop - 1
    For y = 0 To ht - 1 : PokeL(*ligne + (y << 2), PeekL(*buf1 + (((lg * y) + x) << 2))) : Next
    BoxBlur_sp0(y) ; calcul du premier pixel
    *dst = *buf2 + (x << 2)
    *dst\l = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1
    For y = 1 To (ht - 1)
      BoxBlur_sp1((y-1) , opty)
    Next
  Next
  FreeMemory(*ligne)
EndProcedure

Procedure Blur_box( *param.parametre )
  ; Mode interface : renseigner les informations sur les options si demandé
  If param\info_active
    param\typ = #Filter_Type_Blur
    param\name = "Blur_box"
    param\remarque = "Blur Box bugger"
    param\info[0] = "Rayon X"           ; Rayon horizontal
    param\info[1] = "Rayon Y"           ; Rayon vertical
    param\info[2] = "Nombre de passe"   ; Nombre d’itérations du filtre
    param\info[3] = "bord"              ; Mode bord ou boucle
    param\info[4] = "Masque binaire"    ; Option masque binaire
    param\info_data(0,0) = 1 : param\info_data(0,1) = 100 : param\info_data(0,2) = 1
    param\info_data(1,0) = 1 : param\info_data(1,1) = 100 : param\info_data(1,2) = 1
    param\info_data(2,0) = 1 : param\info_data(2,1) = 3   : param\info_data(2,2) = 1
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1   : param\info_data(3,2) = 0
    param\info_data(4,0) = 0 : param\info_data(4,1) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  Protected i , boucle , e , ii , l , k
  Protected lg = *param\lg
  Protected ht = *param\ht
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  clamp(param\option[0] , 1 , 100)
  clamp(param\option[1] , 1 , 100)
  Protected optx = *param\option[0]
  Protected opty = *param\option[1]
  clamp(*param\option[2],1,3)
  Protected *tempo = AllocateMemory( lg * ht * 4 , #PB_Memory_NoClear) ; memoire tempon de l'image  
  If *tempo = 0 : ProcedureReturn : EndIf
  Protected *px = AllocateMemory((lg + 2 * (optx + 2)) * 4 , #PB_Memory_NoClear) ; pre-calcul des pixels en x pour gerer les bords de l'image
  Protected *py = AllocateMemory((ht + 2 * (opty + 2)) * 4 , #PB_Memory_NoClear) ; pre-calcul des pixels en y pour gerer les bords de l'image
  If *param\option[3]
    ; mode boucle : les pixels sortants "reviennent" à l'autre extrémité
    k = (optx + 1) * 0.5 : l = 2 * (optx + 1) : e = (lg - 1) - k : For i = 0 To (lg - 1) + l : PokeL(*px + (i << 2) , (i+e) % lg) : Next
    k = (opty + 1) * 0.5 : l = 2 * (opty + 1) : e = (ht - 1) - k : For i = 0 To (ht - 1) + l : PokeL(*py + (i << 2) , (i+e) % ht) : Next
  Else      
    ; mode bord : pixels répétés aux extrémités
    k = (optx + 1) * 0.5 : l = 2 * (optx + 1) : For i = 0 To lg + l : ii = i - k : If ii < 0 : ii = 0 : EndIf : If ii > (lg - 1) : ii = (lg - 1) : EndIf : PokeL(*px + (i << 2) , ii) : Next
    k = (opty + 1) * 0.5 : l = 2 * (opty + 1) : For i = 0 To ht + l : ii = i - k : If ii < 0 : ii = 0 : EndIf : If ii > (ht - 1) : ii = (ht - 1) : EndIf : PokeL(*py + (i << 2) , ii) : Next
  EndIf 
  *param\addr[2] = *px
  *param\addr[3] = *py
  Protected *buf = *param\source
  For boucle = 1 To *param\option[2] 
    param\addr[0] = *buf 
    param\addr[1] = *tempo
    param\option[5] = (65536 / (optx * 2 + 1)); facteur blur horizontal
    MultiThread_MT(@BoxBlur_X())
    param\addr[0] = *tempo 
    param\addr[1] = *param\cible
    param\option[5] = (65536 / (opty * 2 + 1)); facteur blur vertical
    MultiThread_MT(@BoxBlur_Y())
    *buf = *param\cible
  Next
  If *param\mask And *param\option[4] : *param\mask_type = *param\option[4] - 1 : MultiThread_MT(@_mask()) : EndIf
  FreeMemory(*px)
  FreeMemory(*py)
  FreeMemory(*tempo)
EndProcedure

;----------------

Macro MedianBlur_sp1(op)
  value = PeekL(*source + index)
  getargb(value,a,r,g,b)
  histA(a) op 1
  yl = (77 * R + 150 * G + 29 * B) >> 8
  histy(yl) op 1
EndMacro

Macro MedianBlur_sp2(var)
  sum = 0
  median#var = 0
  For i = 0 To 255
    sum + hist#var(i)
    If sum >= kernelArea_d2 : median#var = i : Break : EndIf
  Next
EndMacro

Procedure MedianBlur_sp(*param.parametre )
  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  If *source = 0 Or *cible = 0 : ProcedureReturn : EndIf
  Protected kernelSize = param\option[0]
  If kernelSize < 1 :  kernelSize = 1 : EndIf
  kernelSize = (kernelSize * 2) + 1
  Protected half = kernelSize / 2
  Protected kernelArea_d2 = kernelSize * kernelSize * 0.5
  Dim histA.l(255)
  Dim histy.l(255)
  Protected x, y, dx, dy, px, py, index
  Protected value, r.l, g.l, b.l, a.l , i , sum , yl 
  Protected medianA , medianY
  Protected oldX, newX
  Protected cb.l, cr.l 
  Protected maskVal , invMask , a1.l ,r1.l , g1.l , b1.l 
  Protected start = (*param\ht * *param\thread_pos) / *param\thread_max
  Protected stop  = (*param\ht * (*param\thread_pos + 1)) / *param\thread_max
  If *param\thread_pos = (*param\thread_max - 1) : stop = *param\ht : EndIf 
  ;For y = 0 To ht - 1
  For y = start To stop - 1    
    ; Réinitialiser histogrammes
    FillMemory(@histA(),256*4,0)
    FillMemory(@histy(),256*4,0)
    ; Fenêtre initiale (colonne x = 0)
    For dy = -half To half
      py = y + dy
      Clamp(py, 0, (ht - 1))
      For dx = -half To half
        px = dx
        Clamp(px , 0, lg - 1)
        index = (py * lg + px) * 4
        MedianBlur_sp1(+)
      Next
    Next
    ; Parcours horizontal
    For x = 0 To lg - 1
      ; Médiane des canaux
      MedianBlur_sp2(a)
      MedianBlur_sp2(y)
      index = (y * lg + x) * 4
      value = PeekL(*source + index)
      getargb(value,a,r,g,b)
      cb = ((-43 * r - 85 * g + 128 * b) >> 8)
      cr = ((128 * r - 107 * g - 21 * b) >> 8)
      r = medianY + ((358 * cr) >> 8)
      g = medianY - ((88 * cb + 183 * cr) >> 8)
      b = medianY + ((454 * cb) >> 8)
      clamp_rgb(r,g,b)
      PokeL(*cible + (y * lg + x) * 4 , (mediana << 24) | (r << 16) | (g << 8) | b )  
      ; Mise à jour glissante : retirer ancienne colonne / ajouter nouvelle
      If x < lg - 1
        oldX = x - half
        Clamp(oldX, 0, lg - 1)
        newX = x + half + 1
        Clamp(newX, 0, lg - 1)
        For dy = -half To half
          py = y + dy
          Clamp(py, 0, ht - 1)
          ; Retirer ancienne colonne
          index = (py * lg + oldX) * 4
          MedianBlur_sp1(-)
          ; Ajouter nouvelle colonne
          index = (py * lg + newX) * 4
          MedianBlur_sp1(+)
        Next
      EndIf
    Next
  Next
  FreeArray(histA())
  FreeArray(histy())
EndProcedure

Procedure MedianBlur(*param.parametre )
  If param\info_active
    param\typ = #Filter_Type_Blur
    param\name = "MedianBlur"
    param\remarque = ""
    param\info[0] = "Rayon"           ; Rayon horizontal
    param\info[1] = "Masque binaire"    ; Option masque binaire
    param\info_data(0,0) = 1 : param\info_data(0,1) = 100 : param\info_data(0,2) = 1
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2   : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  If param\option[0] < 1 : param\option[0] = 1 : EndIf
  filter_start(@MedianBlur_sp() , 1)
EndProcedure

;----------------

Procedure RadialBlur_MT(*param.parametre)

  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected Radius = *param\option[0]
  If Radius < 1 : Radius = 1 : EndIf
  Protected cx = (*param\option[1] * lg) / 100
  Protected cy = (*param\option[2] * ht) / 100
  Protected rmax = (*param\option[3] * Sqr(lg*lg+ht*ht) )/ 100
  If rmax < 1 : rmax = 1 : EndIf

  Protected rmax2 = rmax * rmax
  Protected scale = 65536
  Protected samp = scale / (Radius + 1)
  
  Protected *scr1.Pixel32
  Protected *dst.Pixel32
  
  Protected startY = (ht * *param\thread_pos) / *param\thread_max
  Protected stopY  = (ht * (*param\thread_pos + 1)) / *param\thread_max
  If *param\thread_pos = (*param\thread_max - 1) : stopY = ht : EndIf

  ; Pré-calcule rmax2 pour éviter conditions multiples
  Protected x, y, i, sx, sy
  Protected dx, dy, fx, fy
  Protected r1, g1, b1, r, g, b
  Protected color
  Protected dist, force

  For y = startY To stopY - 1
    Protected rowOffset = y * lg * 4
    dy = y - cy
    For x = 0 To lg - 1
      dx = x - cx
      dist = dx*dx + dy*dy

      Protected pixelOffset = rowOffset + x * 4

      If dist > rmax2
        ; Pixel hors zone : copie rapide pixel original
        *scr1 = *source + pixelOffset
        *dst = *cible + pixelOffset
        *dst\l = *scr1\l
        Continue
      EndIf

      ; Force (fixed point 16.16)
      force = ((rmax2 - dist) << 16) / rmax2
      If force < 0 : force = 0 : EndIf

      ; Pré-calcul des incréments en fixed-point
      Protected dxStep = ((cx - x) * samp)
      Protected dyStep = ((cy - y) * samp)
      fx = x * scale
      fy = y * scale

      r = 0 : g = 0 : b = 0

      For i = 0 To Radius
        sx = fx >> 16
        sy = fy >> 16
        If sx >= 0 And sx < lg And sy >= 0 And sy < ht
          *scr1 = *source + (sy * lg + sx) * 4
          getrgb(*scr1\l, r1, g1, b1)
          r + r1
          g + g1
          b + b1
        EndIf
        fx + dxStep
        fy + dyStep
      Next

      ; Calcul de la moyenne et application de la force
      ; Évite division flottante: calcule en int puis ajuste
      r = (r * samp * force) >> 32;/ (scale * scale)
      g = (g * samp * force) >> 32;/ (scale * scale)
      b = (b * samp * force) >> 32;/ (scale * scale)

      ; Lecture pixel original pour mix
      *scr1 = *source + pixelOffset
      getrgb(*scr1\l , r1, g1, b1)

      ; Mix approximatif avec le pixel original selon la force
      r = (r * force + r1 * (scale - force)) >> 16
      g = (g * force + g1 * (scale - force)) >> 16
      b = (b * force + b1 * (scale - force)) >> 16

      ; Clamp branchless possible ici
      clamp_rgb(r, g, b)
      
      *dst = *cible + pixelOffset
      *dst\l = (r << 16) | (g << 8) | b
    Next
  Next

EndProcedure


Procedure RadialBlur( *param.parametre )
  ; Mode interface : renseigner les informations sur les options si demandé
  If param\info_active
    param\name = "RadialBlur"
    param\remarque = "Radial Blur linéaire"
    param\info[0] = "échantillonnage"          
    param\info[1] = "Pos X"           
    param\info[2] = "Pos Y"          
    param\info[3] = "Rayon Max"   
    param\info[4] = "Masque binaire"    
    param\info_data(0,0) = 1 : param\info_data(0,1) = 50 : param\info_data(0,2) = 25
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 0 : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 0 : param\info_data(3,1) = 100 : param\info_data(3,2) = 50
    param\info_data(4,0) = 0 : param\info_data(4,1) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@RadialBlur_MT() , 4)
EndProcedure

;----------------

Procedure RadialBlur_IIR_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected Radius = *param\option[0]
  Protected cx = (*param\option[1] * lg) / 100
  Protected cy = (*param\option[2] * ht) / 100
  Protected pos , i , j
  Protected angle.f
  Protected cosA
  Protected sinA
  Protected maxRadius.f
  Protected r , g , b
  Protected firstPixel = #True
  Protected px , py
  Protected r1 ,g1 ,b1
  Protected Alpha , inv_Alpha
  Protected mul = 65536
  Protected mul2 = mul >> 1
  Protected quality = *param\option[3]
  Protected *scr.Pixel32
  Protected *dst.Pixel32
  Alpha = (Exp(-2.3 / (Radius + 1)))* mul
  inv_Alpha = mul - alpha
  maxRadius = Sqr(lg * lg + ht * ht)
  Protected tt = 360 * quality
  Protected thread_pos = *param\thread_pos
  Protected thread_max = *param\thread_max
  Protected startPos = (thread_pos * tt) / thread_max
  Protected endPos   = ((thread_pos + 1) * tt) / thread_max - 1
  ;For i = 0 To (360 * quality) - 1
  For i = startPos To endPos -1 
    cosA = PeekL(*param\addr[2] + i <<2)
    sinA = PeekL(*param\addr[3] + i <<2)
    ; Variables pour flou IIR
    r = 0 : g = 0 : b = 0
    firstPixel = #True
    For j = 0 To maxRadius
      ; Position en cartésien
      px = cx + (j * cosA) >> 16
      py = cy + (j * sinA) >> 16
      If px < 0 Or py < 0 Or px >= lg Or py >= ht : Continue : EndIf
      ; Lecture pixel depuis buffer source (nearest neighbor)
      pos = ((py) * lg + (px)) << 2
      *scr = *source + pos
      getrgb(*scr\l , r1 , g1 , b1)
      If firstPixel
        r = r1 * mul : g = g1 * mul : b = b1 * mul
        firstPixel = #False
      Else
        ; Application du flou IIR exponentiel
        r = (Alpha * r + inv_Alpha * (r1 * mul)) >> 16
        g = (Alpha * g + inv_Alpha * (g1 * mul)) >> 16
        b = (Alpha * b + inv_Alpha * (b1  *mul)) >> 16
      EndIf
      ; Écriture dans image temporaire
      r1 = (r + mul2) >> 16
      g1 = (g + mul2) >> 16
      b1 = (b + mul2) >> 16
      *dst = *cible + pos
      *dst\l = (r1 << 16) | (g1 << 8) | b1
    Next
  Next
EndProcedure

Procedure RadialBlur_IIR( *param.parametre )
  ; Mode interface : renseigner les informations sur les options si demandé
  If param\info_active
    param\typ = #Filter_Type_Blur
    param\name = "RadialBlur_IIR"
    param\remarque = ""
    param\info[0] = "Rayon"           ; Rayon horizontal
    param\info[1] = "pos X"       
    param\info[2] = "pos Y"  
    param\info[3] = "qualité" 
    param\info[4] = "Masque binaire"    ; Option masque binaire
    param\info_data(0,0) = 1 : param\info_data(0,1) = 1999 : param\info_data(0,2) = 100
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 0 : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 16 : param\info_data(3,1) = 256   : param\info_data(3,2) = 32
    param\info_data(4,0) = 0 : param\info_data(4,1) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  
  Protected total = *param\lg * *param\ht * 4
  If *param\source = *param\cible
    Protected *tempo = AllocateMemory(total)
    If Not *tempo : ProcedureReturn : EndIf
    CopyMemory(*param\source , *tempo , total)
    *param\addr[0] = *tempo
    *param\addr[1] = *param\cible    
  Else
    *param\addr[0] = *param\source
    *param\addr[1] = *param\cible
  EndIf
  
  Protected i , angle.f
  Protected quality = *param\option[3]
  Protected inv_quality.f = 1/quality
  Dim rc.l(360 * quality)
  Dim rs.l(360 * quality)
  For i = 0 To (360 * quality) - 1
    angle = Radian(i * inv_quality) 
    rc(i) = Cos(angle) * 65536
    rs(i) = Sin(angle) * 65536
  Next
  *param\addr[2] = @rc()
  *param\addr[3] = @rs()
  MultiThread_MT(@RadialBlur_IIR_MT())
  If *param\mask And *param\option[4] : *param\mask_type = *param\option[4] - 1 : MultiThread_MT(@_mask()) : EndIf
  FreeArray(rc())
  FreeArray(rs())
  If *tempo : FreeMemory(*tempo) : EndIf
EndProcedure

;----------------

Procedure SpiralBlur_IIR_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected Radius = *param\option[0]
  Protected cx.f = (*param\option[1] * lg) / 100
  Protected cy.f = (*param\option[2] * ht) / 100
  Protected force.i = *param\option[3]
  Protected quality = *param\option[4]
  Protected direction = *param\option[6]
  Protected pos, i, j
  
  direction = (direction * 2) - 1
  
  ; ACCUMULATEURS en 64-bit pour éviter overflow
  Protected r.q, g.q, b.q
  Protected firstPixel
  Protected px.f, py.f
  Protected col, r1.i, g1.i, b1.i

  ; Alpha en 64-bit aussi (sécurité)
  Protected Alpha.q, inv_Alpha.q
  Protected mul = 65536
  Protected mul2 = mul >> 1

  Protected maxRadiusInt.i
  maxRadiusInt = Max_4( Sqr(cx*cx + cy*cy), Sqr((lg - cx)*(lg - cx) + cy*cy), Sqr(cx*cx + (ht - cy)*(ht - cy)), Sqr((lg - cx)*(lg - cx) + (ht - cy)*(ht - cy)) )
  Protected activeRadius.f = (*param\option[5] * maxRadiusInt) / 100
  
  Protected angleCount = 360 * quality
  Protected forceMod = (force * direction) % angleCount   ; wrap optimisé
  If forceMod < 0 : forceMod + angleCount : EndIf

  ; garde de sécurité
  If angleCount <= 0 Or *source = 0 Or *cible = 0
    ProcedureReturn
  EndIf

  Alpha = Int(Exp(-2.3 / (Radius + 1)) * mul)
  inv_Alpha = mul - Alpha

  ; Utiliser .q pour stocker des adresses si PureBasic 64-bit
  Protected cosPtr = *param\addr[2]
  Protected sinPtr = *param\addr[3]
  
  Protected *scr.Pixel32
  Protected *dst.Pixel32
  
  If cosPtr = 0 Or sinPtr = 0 : ProcedureReturn : EndIf

  Protected angleStart = (*param\thread_pos * angleCount) / *param\thread_max
  Protected angleEnd = ((*param\thread_pos + 1) * angleCount) / *param\thread_max - 1

  For i = angleStart To angleEnd
    r = 0 : g = 0 : b = 0
    firstPixel = #True

    ; idx normalisé une seule fois
    Protected idx.i = i
    If idx >= angleCount
      idx = idx % angleCount
    EndIf

    For j = 0 To maxRadiusInt
      
      If j > 0
        idx + forceMod
        If idx >= angleCount
          idx - angleCount
        EndIf
      EndIf

      px = cx + j * PeekF(cosPtr + (idx << 2))
      py = cy + j * PeekF(sinPtr + (idx << 2))

      If px < 0 Or py < 0 Or px >= lg Or py >= ht : Continue : EndIf

      Protected ix.i = Int(px)
      Protected iy.i = Int(py)
      Protected rowBase.i = (iy * lg) << 2
      pos = rowBase + (ix << 2)

      ;col = PeekL(*source + pos)
      *scr = *source + pos
      getrgb(*scr\l , r1 , g1 , b1)
      
      If j < activeRadius 
      
      If firstPixel
        r = r1 << 16 : g = g1 << 16 : b = b1 << 16
        firstPixel = #False
      Else
        ; opérations en 64-bit (sécurité)
        r = (Alpha * r + inv_Alpha * (r1 << 16)) >> 16
        g = (Alpha * g + inv_Alpha * (g1 << 16)) >> 16
        b = (Alpha * b + inv_Alpha * (b1 << 16)) >> 16
      EndIf

      r1 = (r + mul2) >> 16
      g1 = (g + mul2) >> 16
      b1 = (b + mul2) >> 16
      
    EndIf
      
      ; clamp simple
      clamp_rgb(r1 , g1 , b1)
      *dst = *cible + pos
      *dst\l = (r1 << 16) | (g1 << 8) | b1
      ;PokeL(*cible + pos, (r1 << 16) | (g1 << 8) | b1)
    Next ; j
  Next ; i
EndProcedure


Procedure SpiralBlur_IIR( *param.parametre )
  ; Mode interface : renseigner les informations sur les options si demandé
  If param\info_active
    param\typ = #Filter_Type_Blur
    param\name = "SpiralBlur_IIR"
    param\remarque = "appliquer un filtre de flou en spirale"
    param\info[0] = "Rayon du filtre"          
    param\info[1] = "Pos X"           
    param\info[2] = "Pos Y"          
    param\info[3] = "Force de rotation"   
    param\info[4] = "Qualité" 
    param\info[5] = "Rayon actif"   
    param\info[6] = "sens"   
    param\info[7] = "Masque binaire"    
    param\info_data(0,0) = 1 : param\info_data(0,1) = 99 : param\info_data(0,2) = 50
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 0 : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 0 : param\info_data(3,1) = 100 : param\info_data(3,2) = 10
    param\info_data(4,0) = 16 : param\info_data(4,1) = 64   : param\info_data(4,2) = 32
    param\info_data(5,0) = 0 : param\info_data(5,1) = 100   : param\info_data(5,2) = 100
    param\info_data(6,0) = 0 : param\info_data(6,1) = 1   : param\info_data(6,2) = 0
    param\info_data(7,0) = 0 : param\info_data(7,1) = 2   : param\info_data(7,2) = 0
    ProcedureReturn
  EndIf
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  
  Protected total = *param\lg * *param\ht * 4
  If *param\source = *param\cible
    Protected *tempo = AllocateMemory(total)
    If Not *tempo : ProcedureReturn : EndIf
    CopyMemory(*param\source , *tempo , total)
    *param\addr[0] = *tempo
    *param\addr[1] = *param\cible    
  Else
    *param\addr[0] = *param\source
    *param\addr[1] = *param\cible
  EndIf
  
  Protected i , angle.f
  Protected quality = *param\option[4]
  Protected inv_quality.f = 1.0 / quality
  Protected angleCount = 360 * quality
  Dim cosTable.f(angleCount)
  Dim sinTable.f(angleCount) 
  For i = 0 To angleCount - 1
    angle = Radian(i * inv_quality)
    cosTable(i) = Cos(angle)
    sinTable(i) = Sin(angle)
  Next
  *param\addr[2] = @cosTable()
  *param\addr[3] = @sinTable()
  MultiThread_MT(@SpiralBlur_IIR_MT())
  If *param\mask And *param\option[7] : *param\mask_type = *param\option[7] - 1 : MultiThread_MT(@_mask()) : EndIf
  FreeArray(cosTable())
  FreeArray(sinTable())
  If *tempo : FreeMemory(*tempo) : EndIf
EndProcedure

;----------------

Procedure DepthAwareBlur_garyscale_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected *cible = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected total = lg * ht
  Protected *scr.pixel32                       
  Protected r ,g , b , gray , i
  Protected start = (*param\thread_pos * total) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * total) / *param\thread_max
  For i = start To stop - 1
    *scr = *source + (i << 2)
    getrgb( *scr\l , r , g , b)
    gray = (r * 1225 + g * 2405 + b * 466) >> 12
    PokeA(*cible + i , gray)
  Next
EndProcedure


Procedure GetGrayFromColor(col.l)
  Protected r, g, b
  getrgb(col, r, g, b)
  ProcedureReturn (r * 1225 + g * 2405 + b * 466) >> 12 ; pondération NTSC approx
EndProcedure

Procedure DepthAwareBlur_MT(*param.parametre)
  
  Protected *source = *param\addr[0]
  Protected *output = *param\addr[1]
  Protected *depthMap = *param\addr[2]
  Protected width   = *param\lg
  Protected height  = *param\ht
  
  Protected depthThreshold = *param\option[0]
  Protected radius         = *param\option[1]
  
  Protected x, y, dx, dy, r, g, b, count, col
  Protected r1 , g1 , b1
  Protected centerDepth, sampleDepth, centerGray, sampleGray, dr
  
  Protected start = (*param\thread_pos * height) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * height) / *param\thread_max
  
  For y = start To stop - 1
    For x = 0 To width - 1
      
      r = 0 : g = 0 : b = 0 : count = 0
      
      ; profondeur pixel central
      centerDepth = PeekA(*depthMap + (y * width + x))
      
      ; balayage voisinage
      For dy = -radius To radius
        Protected sy = y + dy
        If sy < 0 Or sy >= height : Continue : EndIf
        
        For dx = -radius To radius
          Protected sx = x + dx
          If sx < 0 Or sx >= width : Continue : EndIf
          
          sampleDepth = PeekA(*depthMap + (sy * width + sx))
          
          dr = Abs(sampleDepth - centerDepth)
          If dr > depthThreshold : Continue : EndIf
          
          ; couleur source
          col = PeekL(*source + (sy * width + sx) * 4)
          getrgb(col , r1 , g1 ,b1 )
          r + r1 : g + g1 : b + b1
          count + 1
        Next
      Next
      
      ; écriture pixel résultat
      If count > 0
        r / count : g / count : b / count
      EndIf
      PokeL(*output + (y * width + x) * 4, (r << 16) | (g << 8) | b)
      
    Next
  Next
EndProcedure

Procedure DepthAwareBlur(*param.parametre)
  If *param\info_active
    *param\name = "DepthAwareBlur"
    *param\typ = #Filter_Type_Blur
    *param\remarque = "adoucie tout en conservant les contours nets"
    *param\info[0] = "depthThreshold"
    *param\info[1] = "radius"
    *param\info[2] = "Masque binaire"
    *param\info_data(0,0) = 1 : *param\info_data(0,1) = 255 : *param\info_data(0,2) = 127
    *param\info_data(1,0) = 3 : *param\info_data(1,1) = 10  : *param\info_data(1,2) = 1
    *param\info_data(2,0) = 0 : *param\info_data(2,1) = 02   : *param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
 
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf

  Protected *tempo = AllocateMemory(*param\lg * *param\ht * 4)
  If Not *tempo : ProcedureReturn : EndIf
  CopyMemory(*param\source , *tempo , *param\lg * *param\ht * 4)
  
  Protected *depthMap = AllocateMemory(*param\lg * *param\ht)
  If Not *depthMap : ProcedureReturn : EndIf
  
  *param\addr[0] = *param\source
  *param\addr[1] = *depthMap
  MultiThread_MT(@DepthAwareBlur_garyscale_MT())
  

  *param\addr[0] = *tempo
  *param\addr[1] = *param\cible
  *param\addr[2] = *depthMap
  ; lancement multi-thread
  MultiThread_MT(@DepthAwareBlur_MT())
  
  ; application du masque si nécessaire
  If *param\mask And *param\option[2] : *param\mask_type = *param\option[2] - 1 : MultiThread_MT(@_mask()) : EndIf
  
  FreeMemory(*tempo)
  FreeMemory(*depthMap)
EndProcedure

;----------------

Procedure DirectionalBoxBlur_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected *output = *param\addr[1]
  Protected width  = *param\lg
  Protected height = *param\ht
  Protected angle.f  = *param\option[0] * #PI / 180.0
  Protected radius   = *param\option[1]  ; longueur du flou

  Protected dx.f = Cos(angle)
  Protected dy.f = Sin(angle)

  Protected x, y, i
  Protected sx.f, sy.f
  Protected rSum.f, gSum.f, bSum.f
  Protected r, g, b, count
  Protected col, r1, g1, b1

  Protected start = (*param\thread_pos * height) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * height) / *param\thread_max

  For y = start To stop - 1
    For x = 0 To width - 1
      rSum = 0 : gSum = 0 : bSum = 0 : count = 0

      For i = -radius To radius
        sx = x + i * dx
        sy = y + i * dy
        If sx < 0 Or sx >= width Or sy < 0 Or sy >= height : Continue : EndIf

        col = PeekL(*source + (Int(sy) * width + Int(sx)) * 4)
        getrgb(col, r1, g1, b1)
        rSum + r1 : gSum + g1 : bSum + b1
        count + 1
      Next

      If count > 0
        r = rSum / count
        g = gSum / count
        b = bSum / count
      EndIf

      PokeL(*output + (y * width + x) * 4, (Int(r) << 16) | (Int(g) << 8) | Int(b))
    Next
  Next
EndProcedure


Procedure DirectionalBoxBlur(*param.parametre)
  If *param\info_active
    *param\name = "DirectionalBoxBlur"
    *param\typ = #Filter_Type_Blur
    *param\remarque = "Flou directionnel approximatif"
    *param\info[0] = "Angle (°)"
    *param\info[1] = "Radius"
    *param\info_data(0,0) = 0   : *param\info_data(0,1) = 360 : *param\info_data(0,2) = 0
    *param\info_data(1,0) = 1   : *param\info_data(1,1) = 100  : *param\info_data(1,2) = 8
    *param\info_data(2,0) = 1   : *param\info_data(2,1) = 2   : *param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@DirectionalBoxBlur_MT() , 2)
EndProcedure

;----------------

Procedure Edge_Aware_LoadImageToFloatArrays_MT(*param.parametre)
  Protected *source = *param\source
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected total = lg * ht
  Protected *scr.pixel32
  Protected r, g, b, gray, i
  Protected start = (*param\thread_pos * total) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * total) / *param\thread_max
  
  For i = start To stop - 1
    *scr = *source + (i << 2)
    getrgb(*scr\l, r, g, b)
    gray = (r * 1225 + g * 2405 + b * 466) >> 12 ; 0..255 entier
    
    PokeF(*param\addr[0] + i * 4, r / 255.0)
    PokeF(*param\addr[1] + i * 4, g / 255.0)
    PokeF(*param\addr[2] + i * 4, b / 255.0)
    PokeF(*param\addr[3] + i * 4, gray / 255.0)
  Next
EndProcedure

Procedure Edge_Aware_FloatArraysToLoadImage_MT(*param.parametre)
  Protected *source = *param\source
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected total = lg * ht
  Protected *dst.pixel32
  Protected r, g, b, gray, i
  Protected start = (*param\thread_pos * total) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * total) / *param\thread_max
  
  For i = start To stop - 1
    *dst = *param\cible + (i << 2)
    r = PeekF(*param\addr[0] + i * 4) * 255
    g = PeekF(*param\addr[1] + i * 4) * 255
    b = PeekF(*param\addr[2] + i * 4) * 255
    clamp_rgb(r , g , b)
    *dst\l = (r << 16) | (g << 8) | b
  Next
EndProcedure

Procedure Edge_Aware_UpdateLuma(*param.parametre)
  Protected w = *param\lg
  Protected h = *param\ht
  Protected total = w * h, i
  Protected r.f, g.f, b.f
  
  Protected start = (*param\thread_pos * total) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * total) / *param\thread_max
  
  For i = start To stop - 1
    r = PeekF(*param\addr[0] + i * 4)
    g = PeekF(*param\addr[1] + i * 4)
    b = PeekF(*param\addr[2] + i * 4)
    PokeF(*param\addr[3] + i * 4, 0.299*r + 0.587*g + 0.114*b)
  Next
EndProcedure


Procedure Edge_Aware_RecursiveEdgeAware1D(*param.parametre)
  Protected sigma_s.f = *param\option[0]
  Protected sigma_r.f = *param\option[1]
  Protected sigmaH.f  = *param\option[5]      ; <— fourni par l’appelant
  Protected direction.i = *param\option[6]    ; 0 = horizontal, 1 = vertical
  
  Protected w = *param\lg
  Protected h = *param\ht
  Protected x, y, i, N, idx, prevIdx
  Protected scale.f = sigma_s / sigma_r
  Protected sq2.f = Sqr(2.0)
  Protected lines, lineN
  
  If direction = 0 : lineN = w : lines = h : Else : lineN = h : lines = w : EndIf
  
  ; Tampons temporaires par ligne
  Protected *a  = AllocateMemory(4 * lineN)
  Protected *tr = AllocateMemory(4 * lineN)
  Protected *tg = AllocateMemory(4 * lineN)
  Protected *tb = AllocateMemory(4 * lineN)
  
  Protected start = (*param\thread_pos * lines) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * lines) / *param\thread_max
  
  For i = start To stop - 1
    ; Charger la ligne et calculer a[j]
    For N = 0 To lineN - 1
      If direction = 0 : x = N : y = i : Else : x = i : y = N : EndIf
      idx = y * w + x
      
      PokeF(*tr + N * 4, PeekF(*param\addr[0] + idx * 4))
      PokeF(*tg + N * 4, PeekF(*param\addr[1] + idx * 4))
      PokeF(*tb + N * 4, PeekF(*param\addr[2] + idx * 4))
      
      If N = 0
        PokeF(*a + N * 4, 0.0)
      Else
        If direction = 0
          prevIdx = y * w + (x - 1)
        Else
          prevIdx = (y - 1) * w + x
        EndIf
        Protected gi.f  = PeekF(*param\addr[3] + idx     * 4)
        Protected gip.f = PeekF(*param\addr[3] + prevIdx * 4)
        Protected di.f  = 1.0 + scale * Abs(gi - gip)
        Protected aVal.f = Exp(-(sq2 * di) / sigmaH)
        PokeF(*a + N * 4, aVal)
      EndIf
    Next
    
    ; Gauche -> Droite
    For N = 1 To lineN - 1
      Protected aN.f = PeekF(*a + N * 4)
      PokeF(*tr + N * 4, PeekF(*tr + (N-1) * 4) + aN * (PeekF(*tr + N * 4) - PeekF(*tr + (N-1) * 4)))
      PokeF(*tg + N * 4, PeekF(*tg + (N-1) * 4) + aN * (PeekF(*tg + N * 4) - PeekF(*tg + (N-1) * 4)))
      PokeF(*tb + N * 4, PeekF(*tb + (N-1) * 4) + aN * (PeekF(*tb + N * 4) - PeekF(*tb + (N-1) * 4)))
    Next
    
    ; Droite -> Gauche
    For N = lineN - 2 To 0 Step -1
      Protected aNp1.f = PeekF(*a + (N+1) * 4)
      PokeF(*tr + N * 4, PeekF(*tr + (N+1) * 4) + aNp1 * (PeekF(*tr + N * 4) - PeekF(*tr + (N+1) * 4)))
      PokeF(*tg + N * 4, PeekF(*tg + (N+1) * 4) + aNp1 * (PeekF(*tg + N * 4) - PeekF(*tg + (N+1) * 4)))
      PokeF(*tb + N * 4, PeekF(*tb + (N+1) * 4) + aNp1 * (PeekF(*tb + N * 4) - PeekF(*tb + (N+1) * 4)))
    Next
    
    ; Écrire la ligne filtrée dans les buffers r/g/b
    For N = 0 To lineN - 1
      If direction = 0 : x = N : y = i : Else : x = i : y = N : EndIf
      idx = y * w + x
      Protected r.f = (PeekF(*tr + N * 4))
      Protected g.f = (PeekF(*tg + N * 4))
      Protected b.f = (PeekF(*tb + N * 4))
      clamp(r , 0 , 1)
      clamp(g , 0 , 1)
      clamp(b , 0 , 1)
      
      PokeF(*param\addr[0] + idx * 4, r)
      PokeF(*param\addr[1] + idx * 4, g)
      PokeF(*param\addr[2] + idx * 4, b)
    Next
  Next
  
  FreeMemory(*a)  : FreeMemory(*tr)
  FreeMemory(*tg) : FreeMemory(*tb)
EndProcedure

Procedure Edge_Aware(*param.parametre)
  If *param\info_active
    *param\name = "Edge_Aware"
    *param\typ = #Filter_Type_Blur
    *param\remarque = "lisser sans casser les bords"
    *param\info[0] = "flou large/fin"
    *param\info[1] = "flou contours"
    *param\info[2] = "nombre de passe"
    *param\info[3] = "fixe/line/expo"
    *param\info[4] = "Masque binaire"
    *param\info_data(0,0) = 1  : *param\info_data(0,1) = 128   : *param\info_data(0,2) = 32
    *param\info_data(1,0) = 1  : *param\info_data(1,1) = 1000 : *param\info_data(1,2) = 5
    *param\info_data(2,0) = 1  : *param\info_data(2,1) = 10    : *param\info_data(2,2) = 4
    *param\info_data(3,0) = 0  : *param\info_data(3,1) = 2     : *param\info_data(3,2) = 0
    *param\info_data(4,0) = 0  : *param\info_data(4,1) = 2     : *param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected size = lg * ht * 4
  Protected iters = *param\option[2]
  *param\addr[0] = AllocateMemory(size)
  *param\addr[1] = AllocateMemory(size)
  *param\addr[2] = AllocateMemory(size)
  *param\addr[3] = AllocateMemory(size)
  If *param\addr[0] = 0 Or *param\addr[1] = 0 Or *param\addr[2] = 0 Or *param\addr[3] = 0
    Debug "Erreur allocation mémoire"
    If *param\addr[0] : FreeMemory(*param\addr[0]) : EndIf
    If *param\addr[1] : FreeMemory(*param\addr[1]) : EndIf
    If *param\addr[2] : FreeMemory(*param\addr[2]) : EndIf
    If *param\addr[3] : FreeMemory(*param\addr[3]) : EndIf
    ProcedureReturn
  EndIf
  
  ; Charger en floats normalisés
  MultiThread_MT(@Edge_Aware_LoadImageToFloatArrays_MT())
  
  *param\option[1] = *param\option[1] / 100
  ; Itérations
  Protected i, K = *param\option[2]      ; iters
  Protected sigma_s.f = *param\option[0]
  Protected sigma_r.f = *param\option[1]
  Protected sigma_s_i.f
  
  For i = 0 To K - 1
    
    Select *param\option[3]
      Case 0 : sigma_s_i = sigma_s                    ; fixe
      Case 1 : sigma_s_i = sigma_s * (1.0 - i / (K-1)); linéaire
      Case 2 : sigma_s_i = sigma_s * Pow(0.5, i)      ; exponentiel (actuel)
    EndSelect
    
    Protected numerator.f = Pow(2.0, K - i - 1)
    Protected denom.f = Sqr(Pow(4.0, K) - 1.0)
    
    *param\option[5] = sigma_s_i * Sqr(3.0) * numerator / denom
    
    *param\option[6] = 0   ; horizontal
    MultiThread_MT(@Edge_Aware_RecursiveEdgeAware1D())
    MultiThread_MT(@Edge_Aware_UpdateLuma())
    
    *param\option[6] = 1   ; vertical
    MultiThread_MT(@Edge_Aware_RecursiveEdgeAware1D())
    MultiThread_MT(@Edge_Aware_UpdateLuma())
  Next
  

  MultiThread_MT(@Edge_Aware_FloatArraysToLoadImage_MT())
  
  ; Appliquer le masque si nécessaire
  If *param\mask And *param\option[4] : *param\mask_type = *param\option[4] - 1 : MultiThread_MT(@_mask()) : EndIf
  
  FreeMemory(*param\addr[0])
  FreeMemory(*param\addr[1])
  FreeMemory(*param\addr[2])
  FreeMemory(*param\addr[3])
  
EndProcedure

;----------------

Procedure StackBlur_Horizontal_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected *temp   = *param\addr[1]  ; image tampon
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected radiusX = *param\option[0]
  If radiusX <= 0 Or *source = 0 Or *temp = 0 : ProcedureReturn : EndIf
  Protected x , y , i
  Protected r , g , b
  
  Protected yStart = (*param\thread_pos * ht) / *param\thread_max
  Protected yEnd   = ((*param\thread_pos + 1) * ht) / *param\thread_max - 1
  Protected div = radiusX * 2 + 1
  Protected wm = lg - 1
  Protected *stack = AllocateMemory(div * 3 * SizeOf(Long))
  If *stack = 0 : ProcedureReturn : EndIf
  
   Protected *scr.Pixel32
   
  For y = yStart To yEnd
    Protected rSum, gSum, bSum
    rSum = 0
    gSum = 0
    bSum = 0

    For i = -radiusX To radiusX
      Protected px = i
      Clamp(px, 0, wm)
      *scr = *source + ((y * lg + px) << 2)
      getrgb(*scr\l , r , g , b )
      Protected idx = (i + radiusX) * 3
      PokeL(*stack + idx * 4, r)
      PokeL(*stack + (idx + 1) * 4, g)
      PokeL(*stack + (idx + 2) * 4, b)
      rSum + r : gSum + g : bSum + b
    Next

    For x = 0 To lg - 1
      Protected rAvg = rSum / div
      Protected gAvg = gSum / div
      Protected bAvg = bSum / div
      PokeL(*temp + ((y * lg + x) << 2), (rAvg << 16) | (gAvg << 8) | bAvg)

      Protected outIdx = ((x - radiusX + div) % div) * 3
      rSum - PeekL(*stack + outIdx * 4)
      gSum - PeekL(*stack + (outIdx + 1) * 4)
      bSum - PeekL(*stack + (outIdx + 2) * 4)

      Protected nextX = x + radiusX + 1
      Clamp(nextX, 0, wm)
      *scr = *source + ((y * lg + nextX) << 2)
      getrgb(*scr\l , r , g , b )
      Protected inIdx = ((x + radiusX + 1) % div) * 3
      PokeL(*stack + inIdx * 4, r)
      PokeL(*stack + (inIdx + 1) * 4, g)
      PokeL(*stack + (inIdx + 2) * 4, b)
      rSum + r : gSum + g : bSum + b
    Next
  Next

  FreeMemory(*stack)
EndProcedure

Procedure StackBlur_Vertical_MT(*param.parametre)
  Protected *temp   = *param\addr[0]  ; image temp
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected radiusY = *param\option[1]
  If radiusY <= 0 Or *temp = 0 Or *cible = 0 : ProcedureReturn : EndIf
  Protected x , y , i
  Protected r , g , b
  
  Protected xStart = (*param\thread_pos * lg) / *param\thread_max
  Protected xEnd   = ((*param\thread_pos + 1) * lg) / *param\thread_max - 1
  Protected div = radiusY * 2 + 1
  Protected hm = ht - 1
  Protected *stack = AllocateMemory(div * 3 * SizeOf(Long))
  If *stack = 0 : ProcedureReturn : EndIf
  
   Protected *scr.Pixel32
   
  For x = xStart To xEnd
    Protected rSum, gSum, bSum
    rSum = 0
    gSum = 0
    bSum = 0

    For i = -radiusY To radiusY
      Protected py = i
      Clamp(py, 0, hm)
      *scr = *temp + ((py * lg + x) << 2)
      getrgb(*scr\l , r , g , b )
      Protected idx = (i + radiusY) * 3
      PokeL(*stack + idx * 4, r)
      PokeL(*stack + (idx + 1) * 4, g)
      PokeL(*stack + (idx + 2) * 4, b)
      rSum + r : gSum + g : bSum + b
    Next

    For y = 0 To ht - 1
      Protected rAvg = rSum / div
      Protected gAvg = gSum / div
      Protected bAvg = bSum / div
      PokeL(*cible + ((y * lg + x) << 2), (rAvg << 16) | (gAvg << 8) | bAvg)

      Protected outIdx = ((y - radiusY + div) % div) * 3
      rSum - PeekL(*stack + outIdx * 4)
      gSum - PeekL(*stack + (outIdx + 1) * 4)
      bSum - PeekL(*stack + (outIdx + 2) * 4)

      Protected nextY = y + radiusY + 1
      Clamp(nextY, 0, hm)
      *scr = *temp + ((nextY * lg + x) << 2)
      getrgb(*scr\l , r , g , b )
      Protected inIdx = ((y + radiusY + 1) % div) * 3
      PokeL(*stack + inIdx * 4, r)
      PokeL(*stack + (inIdx + 1) * 4, g)
      PokeL(*stack + (inIdx + 2) * 4, b)
      rSum + r : gSum + g : bSum + b
    Next
  Next

  FreeMemory(*stack)
EndProcedure

Procedure StackBlur(*param.parametre)
  If *param\info_active
    *param\typ = #Filter_Type_Blur
    *param\name = "StackBlur"
    *param\remarque = "Flou rapide par empilement (2 passes)"
    *param\info[0] = "Radius X"
    *param\info[1] = "Radius Y"
    *param\info[2] = "Masque binaire"
    *param\info_data(0,0) = 1 : *param\info_data(0,1) = 100 : *param\info_data(0,2) = 5
    *param\info_data(1,0) = 1 : *param\info_data(1,1) = 100 : *param\info_data(1,2) = 5
    *param\info_data(2,0) = 0 : *param\info_data(2,1) = 2   : *param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  
  Protected *temp = AllocateMemory(*param\lg * *param\ht * 4)
  If *temp = 0 : ProcedureReturn : EndIf
  
  ; Passe 1 : Horizontal
  *param\addr[0] = *param\source
  *param\addr[1] = *temp
  MultiThread_MT(@StackBlur_Horizontal_MT())
  
  ; Passe 2 : Vertical
  *param\addr[0] = *temp
  *param\addr[1] = *param\cible
  ;*param\cible = *param\cible_final ; valeur initiale
  MultiThread_MT(@StackBlur_Vertical_MT())
  If *param\mask And *param\option[2] : *param\mask_type = *param\option[2] - 1 : MultiThread_MT(@_mask()) : EndIf
  FreeMemory(*temp)
  
EndProcedure
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

les filtres "blur" partie 2

blur.pbi

Code : Tout sélectionner

;----------------

Procedure blur_box_create_limit(lg, ht, rx, ry, loop)
    Protected i, ii, e
    clamp(rx, 1, 100)
    clamp(ry, 1, 100)
    Protected dx = lg - 1
    Protected dy = ht - 1
    If rx > dx : rx = dx : EndIf
    If ry > dy : ry = dy : EndIf
    Protected nrx = rx + 1
    Protected nry = ry + 1
    Protected sizeX = (lg + 2 * nrx) * 4
    Protected sizeY = (ht + 2 * nry) * 4
    ; Allocation d’un seul bloc
    Global *blur_box_limit = AllocateMemory(sizeX + sizeY)
    If *blur_box_limit = 0 : ProcedureReturn 0 : EndIf
    Global *blur_box_limit_x = *blur_box_limit
    Global *blur_box_limit_y = *blur_box_limit + sizeX
    ; Remplissage des tables
    If loop
      e = dx - nrx / 2 : For i = 0 To dx + 2 * nrx : PokeL(*blur_box_limit_x + i * 4, (i + e) % (dx + 1)) : Next
      e = dy - nry / 2 : For i = 0 To dy + 2 * nry : PokeL(*blur_box_limit_y + i * 4, (i + e) % (dy + 1)) : Next
    Else
      For i = 0 To dx + 2 * nrx : ii = i - 1 - nrx / 2 : If ii < 0 : ii = 0 : ElseIf ii > dx : ii = dx : EndIf : PokeL(*blur_box_limit_x + i * 4, ii) : Next
      For i = 0 To dy + 2 * nry : ii = i - 1 - nry / 2 : If ii < 0 : ii = 0 : ElseIf ii > dy : ii = dy : EndIf : PokeL(*blur_box_limit_y + i * 4, ii) : Next
    EndIf
    ProcedureReturn 1
  EndProcedure
  
  Procedure blur_box_free_limit()
    If *blur_box_limit
      FreeMemory(*blur_box_limit)
      *blur_box_limit      = 0
      *blur_box_limit_x    = 0
      *blur_box_limit_y    = 0
    EndIf
  EndProcedure
  
  Procedure blur_box_Guillossien_MT(*param.parametre)
    ; Déclarations de pointeurs pixel source/destination
    Protected *srcPixel1.Pixel32
    Protected *srcPixel2.Pixel32
    Protected *dstPixel.Pixel32
    ; Accumulateurs pour composantes ARGB
    Protected ax1, rx1, gx1, bx1
    Protected a1.l, r1.l, b1.l, g1.l
    Protected a2.l, r2.l, b2.l, g2.l
    ; Index temporaires
    Protected j, i, p1, p2
    ; Paramètres de l’image
    Protected lx = *blur_box_limit_x
    Protected ly = *blur_box_limit_y
    Protected lg = *param\lg
    Protected ht = *param\ht
    ; Paramètres du filtre
    Protected nrx = *param\option[0] + 1
    Protected nry = *param\option[1] + 1
    Protected div = Int($800000 / (nrx * nry))  ; Pow(2,23) = $800000
    Protected thread_pos = *param\thread_pos
    Protected thread_max = *param\thread_max
    Protected startPos = (thread_pos * ht) / thread_max
    Protected endPos   = ((thread_pos + 1) * ht) / thread_max - 1
    ; Buffers pour accumuler les sommes par colonne
    Protected Dim a.l(lg) , Dim r.l(lg) , Dim g.l(lg) , Dim b.l(lg)
    ; Initialisation des buffers
    FillMemory(@a(), lg * 4, 0) : FillMemory(@r(), lg * 4, 0) : FillMemory(@g(), lg * 4, 0) : FillMemory(@b(), lg * 4, 0)
    ; === Étape 1 : Accumule les lignes verticales pour démarrer ===
    For j = 0 To nry - 1
      p1 = PeekL(ly + (j + startPos) << 2)
      *srcPixel1 = *param\addr[1] + ((p1 * lg) << 2)
      For i = 0 To lg - 1
        getargb(*srcPixel1\l, a1, r1, g1, b1)
        a(i) + a1 : r(i) + r1 : g(i) + g1 : b(i) + b1
        *srcPixel1 + 4
      Next
    Next
    ; === Étape 2 : Application du filtre pour chaque ligne ===
    For j = startPos To endPos
      ; Mise à jour du buffer colonne (soustraction d’une ancienne ligne et ajout d’une nouvelle)
      p1 = PeekL(ly + (nry + j) << 2)
      p2 = PeekL(ly + (j << 2))
      *srcPixel1 = *param\addr[1] + (p1 * lg) << 2
      *srcPixel2 = *param\addr[1] + (p2 * lg) << 2
      For i = 0 To lg - 1
        getargb(*srcPixel1\l, a1, r1, g1, b1)
        getargb(*srcPixel2\l, a2, r2, g2, b2)
        a(i) + a1 - a2
        r(i) + r1 - r2
        g(i) + g1 - g2
        b(i) + b1 - b2
        *srcPixel1 + 4
        *srcPixel2 + 4
      Next
      ; Application du filtre horizontal
      ax1 = 0 : rx1 = 0 : gx1 = 0 : bx1 = 0
      For i = 0 To nrx - 1
        p1 = PeekL(lx + (i << 2))
        ax1 + a(p1)
        rx1 + r(p1)
        gx1 + g(p1)
        bx1 + b(p1)
      Next
      ; Boucle de sortie pour chaque pixel de la ligne
      For i = 0 To lg - 1
        p1 = PeekL(lx + (nrx + i) << 2)
        p2 = PeekL(lx + (i << 2))
        ax1 + a(p1) - a(p2)
        rx1 + r(p1) - r(p2)
        gx1 + g(p1) - g(p2)
        bx1 + b(p1) - b(p2)
        ; Calcul final avec facteur de division
        a1 = (ax1 * div) >> 23
        r1 = (rx1 * div) >> 23
        g1 = (gx1 * div) >> 23
        b1 = (bx1 * div) >> 23
        ; Écriture dans le buffer temporaire
        *dstPixel = *param\addr[0] + ((j * lg + i) << 2)
        *dstPixel\l = (a1 << 24) | (r1 << 16) | (g1 << 8) | b1
      Next
    Next
    ; Libération des tableaux
    FreeArray(a())
    FreeArray(r())
    FreeArray(g())
    FreeArray(b())
  EndProcedure
  
  Procedure blur_box_Guillossien(*param.parametre)
    *param\addr[0] = *param\source
    *param\addr[1] = *param\cible
    If *param\addr[0] = 0 Or *param\addr[1] = 0 : ProcedureReturn : EndIf
    clamp(*param\option[0], 1, 63)
    clamp(*param\option[1], 1, 63)
    clamp(*param\option[2], 1, 3)
    clamp(*param\option[3], 0, 1)
    clamp(*param\option[4], 0, 1)
    CopyMemory(*param\addr[0], *param\addr[1], *param\lg * *param\ht * 4)
    If blur_box_create_limit(*param\lg, *param\ht, *param\option[0], *param\option[1], *param\option[3])
      Protected *tempo = AllocateMemory(*param\lg * *param\ht * 4)
      If *tempo
        param\addr[0] = *tempo
        Protected passe = *param\option[2] - 1
        For passe = 0 To *param\option[2]
          MultiThread_MT(@blur_box_Guillossien_MT())
          CopyMemory(param\addr[0], param\addr[1], (*param\lg) * (*param\ht) * 4)
        Next
        FreeMemory(*tempo)
        If *param\mask And *param\option[4] : *param\mask_type = *param\option[4] - 1 : MultiThread_MT(@_mask()) : EndIf
      EndIf
      blur_box_free_limit()
    EndIf
  EndProcedure
  
Procedure Guillossien(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Blur
    param\name = "Guillossien"
    param\remarque = "Blur Box optimise"
    param\info[0] = "Rayon X"           ; Rayon horizontal
    param\info[1] = "Rayon Y"           ; Rayon vertical
    param\info[2] = "Nombre de passe"   ; Nombre d’itérations du filtre
    param\info[3] = "bord"              ; Mode bord ou boucle
    param\info[4] = "Masque binaire"    ; Option masque binaire
    param\info_data(0,0) = 1 : param\info_data(0,1) = 63 : param\info_data(0,2) = 1
    param\info_data(1,0) = 1 : param\info_data(1,1) = 63 : param\info_data(1,2) = 1
    param\info_data(2,0) = 1 : param\info_data(2,1) = 3   : param\info_data(2,2) = 1
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1   : param\info_data(3,2) = 0
    param\info_data(4,0) = 0 : param\info_data(4,1) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  blur_box_Guillossien(*param.parametre)
EndProcedure

;----------------

; ---------------------------------------------------
; Flou exponentiel (IIR) sur image ARGB32
; Implémentation 2 passes : horizontale + verticale
; Avec gestion multithread et passes multiples
; ---------------------------------------------------

; --- Extraction des canaux ARGB à partir d’un pixel 32 bits
; Chaque canal est décalé pour garder une précision sur 16 bits
Macro Blur_IIR_get_rgb_32(a,r,g,b)
  *pix32 = *dst32 + (pos * 4)        ; Position mémoire du pixel (ARGB32)
  a = (*pix32\l >> 16) & $ff00       ; Alpha
  r = (*pix32\l >>  8) & $ff00       ; Rouge
  g = (*pix32\l      ) & $ff00       ; Vert
  b = (*pix32\l <<  8) & $ff00       ; Bleu
EndMacro

; --- Mélange entre la valeur précédente et la valeur courante
; (Filtre IIR : valeur[n] = alpha*val[n] + (1-alpha)*val[n-1])
; Puis réécriture dans le pixel
Macro Blur_IIR_sp1_32()
  Blur_IIR_get_rgb_32(a1,r1,g1,b1)   ; Charger le pixel courant
  ; Mélange exponentiel
  a = (a * alpha + inv_alpha * a1) >> 8
  r = (r * alpha + inv_alpha * r1) >> 8
  g = (g * alpha + inv_alpha * g1) >> 8
  b = (b * alpha + inv_alpha * b1) >> 8
  ; Conversion en 8 bits par canal (avec arrondi)
  a1 = (a + 128 ) >> 8
  r1 = (r + 128 ) >> 8
  g1 = (g + 128 ) >> 8
  b1 = (b + 128 ) >> 8
  ; Reconstruction du pixel ARGB32
  *pix32\l = (a1 << 24) + (r1 << 16) + (g1 << 8) + b1
EndMacro

; --- Passe horizontale : balayage gauche→droite puis droite→gauche
Macro Blur_IIR_blurH()
  alpha = alphax : inv_alpha = inv_alphax 
  For y = start To stop - 1 
    pos = (y * w)                        ; Début de ligne
    mem = pos
    Blur_IIR_get_rgb_32(a, r, g, b)      ; Initialiser avec le 1er pixel
    ; Gauche → droite
    For x = 1 To w - 1 : pos = (mem + x) : Blur_IIR_sp1_32() : Next 
    ; Droite → gauche
    pos = (mem + (w - 1))
    Blur_IIR_get_rgb_32(a, r, g, b)
    For x = w - 2 To 0 Step -1 : pos = (y * w + x) : Blur_IIR_sp1_32() : Next
  Next
EndMacro

; --- Passe verticale : balayage haut→bas puis bas→haut
Macro Blur_IIR_blurV()
  alpha = alphay : inv_alpha = inv_alphay 
  For x = start To stop - 1 
    pos = x
    Blur_IIR_get_rgb_32(a, r, g, b)      ; Initialiser avec le 1er pixel
    ; Haut → bas
    For y = 1 To h - 1 : pos = (y * w + x) : Blur_IIR_sp1_32() : Next 
    ; Bas → haut
    pos = ((h - 1) * w + x)
    Blur_IIR_get_rgb_32(a, r, g, b)
    For y = h - 2 To 0 Step -1 : pos = (y * w + x) : Blur_IIR_sp1_32() : Next
  Next
EndMacro

; --- Initialisation commune à chaque passe (H ou V)
Macro Blur_IIR_sp_001(var,opt,opt2)
  Protected *cible  = *param\addr[1]           ; Image destination
  Protected w = *param\lg, h = *param\ht
  Protected a, r, g, b, a1, r1, g1.l, b1
  Protected alpha, inv_alpha, alphaX, inv_alphaX, alphaY, inv_alphaY
  Protected x, y, mem, start, stop, pos
  Protected *dst32.pixel32 = *cible
  Protected *pix32.pixel32
  ; Découpe du traitement en bandes selon le numéro de thread
  start = (var * *param\thread_pos) / *param\thread_max
  stop  = (var * (*param\thread_pos + 1)) / *param\thread_max
  If *param\thread_pos = (*param\thread_max - 1) : stop = var : EndIf
  ; Calcul des coefficients alpha pour le filtre IIR
  alpha#opt = Int((Exp(-2.3 / (*param\option[opt2] + 1.0))) * 256)
  inv_alpha#opt = 256 - alpha#opt
EndMacro

; --- Passe horizontale (appelée par un thread)
Procedure Blur_IIR_sp1(*param.parametre)
  Blur_IIR_sp_001(h, x , 0) ; Initialise pour horizontal
  Blur_IIR_blurh()          ; Exécute la passe horizontale
EndProcedure

; --- Passe verticale (appelée par un thread)
Procedure Blur_IIR_sp2(*param.parametre)
  Blur_IIR_sp_001(w, y , 1) ; Initialise pour vertical
  Blur_IIR_blurv()          ; Exécute la passe verticale
EndProcedure

; --- Gestion complète d’une séquence de flou
Procedure Blur_IIR_sp0(*param.parametre)
  *param\addr[0] = *param\source
  *param\addr[1] = *param\cible
  If *param\addr[0] = 0 Or *param\addr[1] = 0 : ProcedureReturn : EndIf
  clamp(*param\option[2], 1, 3) ; Nombre de passes limité à 1..3
  ; Copier l’image source → destination
  CopyMemory(*param\addr[0], *param\addr[1], (*param\lg * *param\ht * 4))
  Protected passe
  ; Boucle sur le nombre de passes
  For passe = 0 To *param\option[2] - 1
    MultiThread_MT(@Blur_IIR_sp1()) ; Passe horizontale multithreadée
    MultiThread_MT(@Blur_IIR_sp2()) ; Passe verticale multithreadée
  Next
  ; Application éventuelle d’un masque
  If *param\mask And *param\option[3] 
    *param\mask_type = *param\option[3] - 1
    MultiThread_MT(@_mask())
  EndIf
EndProcedure

; --- Interface avec le moteur de filtres
Procedure Blur_IIR(*param.parametre)
  If param\info_active
    ; Remplissage des infos du filtre (interface utilisateur)
    param\typ = #Filter_Type_Blur
    param\name = "Blur_IIR"
    param\remarque = "flou efficace et léger"
    param\info[0] = "Rayon X"        ; Rayon horizontal
    param\info[1] = "Rayon Y"        ; Rayon vertical
    param\info[2] = "Nombre de passe"; Nombre d’itérations
    param\info[3] = "Masque off/alpha/bin" ; Optionnel : appliquer un masque
    ; Valeurs min/max par option
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100 : param\info_data(0,2) = 10
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100 : param\info_data(1,2) = 10
    param\info_data(2,0) = 1 : param\info_data(2,1) = 3   : param\info_data(2,2) = 1
    param\info_data(3,0) = 0 : param\info_data(3,1) = 2   : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  ; Sinon → exécution réelle du filtre
  Blur_IIR_sp0(*param.parametre)
EndProcedure

;----------------

Procedure GaussianBlur_Conv_H_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *dst = *param\addr[1]
  Protected w = *param\lg
  Protected h = *param\ht
  Protected radius = *param\option[0]
  Protected thread_pos = *param\thread_pos
  Protected thread_max = *param\thread_max
  Protected yStart = (thread_pos * h) / thread_max
  Protected yEnd = ((thread_pos + 1) * h) / thread_max - 1
  Protected x, y, k, i, pos
  Protected r.f, g.f, b.f
  Protected r1, g1, b1, var.f
  Protected *srcPix.Pixel32, *dstPix.Pixel32
  Protected *kernel = *param\addr[2]
  Protected half = radius

  For y = yStart To yEnd
    For x = 0 To w - 1
      r = 0 : g = 0 : b = 0
      For k = -half To half
        i = x + k
        If i < 0 : i = 0 : ElseIf i >= w : i = w - 1 : EndIf
        pos = (y * w + i) << 2
        *srcPix = *src + pos
        getrgb(*srcPix\l, r1, g1, b1)
        var = PeekF(*kernel + (k + half) * SizeOf(Float))
        r + r1 * var
        g + g1 * var
        b + b1 * var
      Next
      pos = (y * w + x) << 2
      *dstPix = *dst + pos
      *dstPix\l = RGB(Int(r), Int(g), Int(b))
    Next
  Next
EndProcedure

Procedure GaussianBlur_Conv_V_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *dst = *param\addr[1]
  Protected w = *param\lg
  Protected h = *param\ht
  Protected radius = *param\option[0]
  Protected thread_pos = *param\thread_pos
  Protected thread_max = *param\thread_max
  Protected yStart = (thread_pos * h) / thread_max
  Protected yEnd = ((thread_pos + 1) * h) / thread_max - 1
  Protected x, y, k, i, pos
  Protected r.f, g.f, b.f
  Protected r1, g1, b1, var.f
  Protected *srcPix.Pixel32, *dstPix.Pixel32
  Protected *kernel = *param\addr[2]
  Protected half = radius

  For y = yStart To yEnd
    For x = 0 To w - 1
      r = 0 : g = 0 : b = 0
      For k = -half To half
        i = y + k
        If i < 0 : i = 0 : ElseIf i >= h : i = h - 1 : EndIf
        pos = (i * w + x) << 2
        *srcPix = *src + pos
        getrgb(*srcPix\l, r1, g1, b1)
        var = PeekF(*kernel + (k + half) * SizeOf(Float))
        r + r1 * var
        g + g1 * var
        b + b1 * var
      Next
      pos = (y * w + x) << 2
      *dstPix = *dst + pos
      *dstPix\l = RGB(Int(r), Int(g), Int(b))
    Next
  Next
EndProcedure

Procedure GaussianBlur_Conv(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Blur
    param\name = "GaussianBlur_Conv"
    param\remarque = "Gaussian Blur (convolution, séparable)"
    param\info[0] = "Rayon"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 50 : param\info_data(0,2) = 5
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf

  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf

  Protected total = *param\lg * *param\ht * 4
  Protected *tempo = AllocateMemory(total)
  If Not *tempo : ProcedureReturn : EndIf

  ; Générer le noyau
  Protected radius = *param\option[0]
  If radius < 1 : radius = 1 : EndIf
  Protected sigma.f = radius / 2.0
  Protected size = (radius * 2) + 1
  Protected *kernel = AllocateMemory(size * SizeOf(Float))
  If Not *kernel : FreeMemory(*tempo) : ProcedureReturn : EndIf

  Protected i, x
  Protected var.f, sum.f = 0.0
  For i = 0 To size - 1
    x = i - radius
    var = Exp(-x * x / (2 * sigma * sigma))
    PokeF(*kernel + i * SizeOf(Float), var)
    sum + var
  Next

  For i = 0 To size - 1
    var = PeekF(*kernel + i * SizeOf(Float))
    PokeF(*kernel + i * SizeOf(Float), var / sum)
  Next

  ; === Passe horizontale ===
  *param\addr[0] = *param\source  ; src
  *param\addr[1] = *tempo         ; dst temporaire
  *param\addr[2] = *kernel
  MultiThread_MT(@GaussianBlur_Conv_H_MT())

  ; === Passe verticale ===
  *param\addr[0] = *tempo         ; src temporaire
  *param\addr[1] = *param\cible   ; dst final
  MultiThread_MT(@GaussianBlur_Conv_V_MT())
  If *param\mask And *param\option[2] : *param\mask_type = *param\option[2] - 1 : MultiThread_MT(@_mask()) : EndIf
  ; Nettoyage
  FreeMemory(*tempo)
  FreeMemory(*kernel)
EndProcedure

;----------------

; ===== Motion Blur orienté (multithread) =====
Procedure MotionBlur_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *dst = *param\addr[1]
  Protected w = *param\lg
  Protected h = *param\ht
  Protected radius = *param\option[0]
  Protected angle.f = *param\option[1] * #PI / 180.0   ; option[1] = angle en degrés
  Protected thread_pos = *param\thread_pos
  Protected thread_max = *param\thread_max
  Protected yStart = (thread_pos * h) / thread_max
  Protected yEnd = ((thread_pos + 1) * h) / thread_max - 1
  If yStart > yEnd : ProcedureReturn : EndIf

  Protected x, y, k, xi, yi, pos
  Protected r.f, g.f, b.f
  Protected r1, g1, b1
  Protected *srcPix.Pixel32, *dstPix.Pixel32

  Protected dx.f = Cos(angle)
  Protected dy.f = Sin(angle)
  Protected size = (radius * 2) + 1
  Protected coeff.f = 1.0 / size

  For y = yStart To yEnd
    For x = 0 To w - 1
      r = 0 : g = 0 : b = 0
      For k = -radius To radius
        xi = Round(x + dx * k , #PB_Round_Nearest)
        yi = Round(y + dy * k , #PB_Round_Nearest)
        If xi < 0 : xi = 0 : ElseIf xi >= w : xi = w - 1 : EndIf
        If yi < 0 : yi = 0 : ElseIf yi >= h : yi = h - 1 : EndIf
        pos = (yi * w + xi) << 2
        *srcPix = *src + pos
        getrgb(*srcPix\l, r1, g1, b1)
        r + r1 * coeff
        g + g1 * coeff
        b + b1 * coeff
      Next
      pos = (y * w + x) << 2
      *dstPix = *dst + pos
      *dstPix\l = (Int(r) << 16) | (Int(g) << 8) | Int(b)
    Next
  Next
EndProcedure

; ===== Procédure principale Motion Blur orienté =====
Procedure MotionBlur(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Blur
    param\name = "MotionBlur"
    param\remarque = "Flou directionnel"
    param\info[0] = "Rayon"
    param\info[1] = "Angle"
    param\info[2] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 100 : param\info_data(0,2) = 10
    param\info_data(1,0) = 0 : param\info_data(1,1) = 360 : param\info_data(1,2) = 0
    param\info_data(2,0) = 0 : param\info_data(2,1) = 2 : param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@MotionBlur_MT() , 2)
EndProcedure

;----------------

; ===== Anisotropic Blur orienté (multithread) =====
Procedure AnisotropicBlur_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *dst = *param\addr[1]
  Protected w = *param\lg
  Protected h = *param\ht
  Protected radius = *param\option[0]   ; longueur du blur (axe principal)
  Protected angle.f = *param\option[1] * #PI / 180.0
  Protected thread_pos = *param\thread_pos
  Protected thread_max = *param\thread_max
  Protected yStart = (thread_pos * h) / thread_max
  Protected yEnd = ((thread_pos + 1) * h) / thread_max - 1
  If yStart > yEnd : ProcedureReturn : EndIf

  Protected dx.f = Cos(angle)
  Protected dy.f = Sin(angle)

  Protected x, y, k, xi, yi, pos
  Protected r.f, g.f, b.f
  Protected r1, g1, b1
  Protected *srcPix.Pixel32, *dstPix.Pixel32

  Protected steps = radius * 2 + 1
  Protected coeff.f = 1.0 / steps

  For y = yStart To yEnd
    For x = 0 To w - 1
      r = 0 : g = 0 : b = 0
      For k = -radius To radius
        xi = Round(x + dx * k, #PB_Round_Nearest)
        yi = Round(y + dy * k, #PB_Round_Nearest)

        If xi < 0 : xi = 0 : ElseIf xi >= w : xi = w - 1 : EndIf
        If yi < 0 : yi = 0 : ElseIf yi >= h : yi = h - 1 : EndIf

        pos = (yi * w + xi) << 2
        *srcPix = *src + pos
        getrgb(*srcPix\l, r1, g1, b1)

        r + r1
        g + g1
        b + b1
      Next

      r * coeff : g * coeff : b * coeff
      clamp_rgb(r,g,b)

      pos = (y * w + x) << 2
      *dstPix = *dst + pos
      *dstPix\l = (Int(r) << 16) | (Int(g) << 8) | Int(b)
    Next
  Next
EndProcedure


; ===== Procédure principale =====
Procedure AnisotropicBlur(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Blur
    param\name = "AnisotropicBlur"
    param\remarque = "Gaussian anisotrope orienté (ellipse pivotée) Flou directionnel"
    param\info[0] = "Rayon"
    param\info[1] = "Angle"
    param\info[2] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 50 : param\info_data(0,2) = 5
    param\info_data(1,0) = 1 : param\info_data(1,1) = 180 : param\info_data(1,2) = 5
    param\info_data(2,0) = 0 : param\info_data(2,1) = 2 : param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@AnisotropicBlur_MT() , 2)
EndProcedure

;----------------

Procedure KuwaharaBlur_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *dst = *param\addr[1]
  Protected w = *param\lg
  Protected h = *param\ht
  Protected radius = *param\option[0]
  Protected sharpness.f = *param\option[1]/100.0
  Protected thread_pos = *param\thread_pos
  Protected thread_max = *param\thread_max
  Protected yStart = (thread_pos * h) / thread_max
  Protected yEnd = ((thread_pos + 1) * h) / thread_max - 1

  Protected x, y, k, minIndex
  Protected r.f, g.f, b.f, v, minVar

  ; --- summed-area tables pré-calculées pour l'itération ---
  Protected Dim sumR.f(w*h-1)
  Protected Dim sumG.f(w*h-1)
  Protected Dim sumB.f(w*h-1)
  Protected Dim sumSq.f(w*h-1)
  Protected *srcPix.Pixel32
  Protected r1, g1, b1

  ; --- calcul des tables cumulées une seule fois ---
  For y = 0 To h-1
    For x = 0 To w-1
      *srcPix = *src + ((y*w+x)<<2)
      getrgb(*srcPix\l, r1, g1, b1)
      If x=0 And y=0
        sumR(y*w+x)=r1 : sumG(y*w+x)=g1 : sumB(y*w+x)=b1 : sumSq(y*w+x)=r1*r1+g1*g1+b1*b1
      ElseIf x=0
        sumR(y*w+x)=sumR((y-1)*w+x)+r1
        sumG(y*w+x)=sumG((y-1)*w+x)+g1
        sumB(y*w+x)=sumB((y-1)*w+x)+b1
        sumSq(y*w+x)=sumSq((y-1)*w+x)+r1*r1+g1*g1+b1*b1
      ElseIf y=0
        sumR(y*w+x)=sumR(y*w+x-1)+r1
        sumG(y*w+x)=sumG(y*w+x-1)+g1
        sumB(y*w+x)=sumB(y*w+x-1)+b1
        sumSq(y*w+x)=sumSq(y*w+x-1)+r1*r1+g1*g1+b1*b1
      Else
        sumR(y*w+x)=sumR(y*w+x-1)+sumR((y-1)*w+x)-sumR((y-1)*w+x-1)+r1
        sumG(y*w+x)=sumG(y*w+x-1)+sumG((y-1)*w+x)-sumG((y-1)*w+x-1)+g1
        sumB(y*w+x)=sumB(y*w+x-1)+sumB((y-1)*w+x)-sumB((y-1)*w+x-1)+b1
        sumSq(y*w+x)=sumSq(y*w+x-1)+sumSq((y-1)*w+x)-sumSq((y-1)*w+x-1)+r1*r1+g1*g1+b1*b1
      EndIf
    Next
  Next

  Dim quadrant.f(4*5-1)
  Protected *dstPix.Pixel32

  ; --- traitement pixel par pixel ---
  For y=yStart To yEnd
    For x=0 To w-1
      For k=0 To 4*5-1 : quadrant(k)=0 : Next

      For k=0 To 3
        Protected x0,y0,x1,y1,count.f
        Protected sR0,sR1,sR2,sR3,sG0,sG1,sG2,sG3,sB0,sB1,sB2,sB3,sS0,sS1,sS2,sS3.f

        Select k
          Case 0
            x0 = Max_2(x-radius,0) : y0 = Max_2(y-radius,0) : x1=x : y1=y
          Case 1
            x0=x : y0 = Max_2(y-radius,0) : x1=Min_2(x+radius,w-1) : y1=y
          Case 2
            x0 = Max_2(x-radius,0) : y0=y : x1=x : y1=Min_2(y+radius,h-1)
          Case 3
            x0=x : y0=y : x1=Min_2(x+radius,w-1) : y1=Min_2(y+radius,h-1)
        EndSelect

        count=(x1-x0+1)*(y1-y0+1)

        sR0=sumR(y1*w+x1) : sG0=sumG(y1*w+x1) : sB0=sumB(y1*w+x1) : sS0=sumSq(y1*w+x1)
        sR1=0 : sG1=0 : sB1=0 : sS1=0
        sR2=0 : sG2=0 : sB2=0 : sS2=0
        sR3=0 : sG3=0 : sB3=0 : sS3=0
        If y0>0 : sR1=sumR((y0-1)*w+x1) : sG1=sumG((y0-1)*w+x1) : sB1=sumB((y0-1)*w+x1) : sS1=sumSq((y0-1)*w+x1) : EndIf
        If x0>0 : sR2=sumR(y1*w+(x0-1)) : sG2=sumG(y1*w+(x0-1)) : sB2=sumB(y1*w+(x0-1)) : sS2=sumSq(y1*w+(x0-1)) : EndIf
        If x0>0 And y0>0 : sR3=sumR((y0-1)*w+(x0-1)) : sG3=sumG((y0-1)*w+(x0-1)) : sB3=sumB((y0-1)*w+(x0-1)) : sS3=sumSq((y0-1)*w+(x0-1)) : EndIf

        quadrant(k*5+0)=sR0-sR1-sR2+sR3
        quadrant(k*5+1)=sG0-sG1-sG2+sG3
        quadrant(k*5+2)=sB0-sB1-sB2+sB3
        quadrant(k*5+3)=sS0-sS1-sS2+sS3
        quadrant(k*5+4)=count
      Next

      ; calcul variance et choix du quadrant
      minIndex=0
      minVar=quadrant(0*5+3)/quadrant(0*5+4)-Pow((quadrant(0*5+0)+quadrant(0*5+1)+quadrant(0*5+2))/quadrant(0*5+4),2)
      For k=1 To 3
        v=quadrant(k*5+3)/quadrant(k*5+4)-Pow((quadrant(k*5+0)+quadrant(k*5+1)+quadrant(k*5+2))/quadrant(k*5+4),2)
        If v<minVar : minVar=v : minIndex=k : EndIf
      Next

      ; interpolation sharpness
      *srcPix = *src + ((y*w+x)<<2)
      getrgb(*srcPix\l, r1, g1, b1)
      r = ((quadrant(minIndex*5+0)/quadrant(minIndex*5+4))*sharpness + r1*(1-sharpness))
      g = ((quadrant(minIndex*5+1)/quadrant(minIndex*5+4))*sharpness + g1*(1-sharpness))
      b = ((quadrant(minIndex*5+2)/quadrant(minIndex*5+4))*sharpness + b1*(1-sharpness))
      clamp_rgb(r,g,b)

      *dstPix = *dst + ((y*w+x)<<2)
      *dstPix\l = (Int(r)<<16) | (Int(g)<<8) | Int(b)
    Next
  Next
EndProcedure

Procedure KuwaharaBlur(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Blur
    param\name = "KuwaharaBlurFast"
    param\remarque = "Kuwahara blur non linéaire optimisé"

    ; --- Paramètre 0 : Rayon ---
    param\info[0] = "Rayon"
    param\info[1] = "Netteté des bords"
    param\info[2] = "Itérations"
    param\info[3] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 50  : param\info_data(0,2) = 2
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 1 : param\info_data(2,1) = 5   : param\info_data(2,2) = 1
    param\info_data(3,0) = 0 : param\info_data(3,1) = 2   : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf

  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  
  Protected iterations = *param\option[2]
  
  Protected *tempo , tmpSrc , tmpDst , i
  tmpDst = *param\cible
  If *param\source = *param\cible
    *tempo = AllocateMemory(*param\lg * *param\ht * 4)
    If Not *tempo : ProcedureReturn : EndIf
    CopyMemory(*param\source , *tempo , *param\lg * *param\ht * 4)
    tmpSrc = *tempo 
  Else
    tmpSrc = *param\source
  EndIf
  
  For i = 1 To iterations
    *param\addr[0] = tmpSrc
    *param\addr[1] = tmpDst
    MultiThread_MT(@KuwaharaBlur_MT())
    
    ; swap pour la prochaine itération
    If i < iterations
      Swap tmpSrc, tmpDst
    EndIf
  Next
  If *param\mask And *param\option[2] : *param\mask_type = *param\option[2] - 1 : MultiThread_MT(@_mask()) : EndIf
  If *tempo : FreeMemory(*tempo) : EndIf
EndProcedure

;----------------

; ===== Poisson Disk Blur multithread avec itérations et sharpness =====
Procedure PoissonDiskBlur_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *dst = *param\addr[1]
  Protected w = *param\lg
  Protected h = *param\ht
  Protected radius.f = *param\option[0]
  Protected samples = *param\option[1]
  Protected sharpness.f = *param\option[2]/100.0
  Protected thread_pos = *param\thread_pos
  Protected thread_max = *param\thread_max
  Protected yStart = (thread_pos * h) / thread_max
  Protected yEnd = ((thread_pos + 1) * h) / thread_max - 1

  Protected x, y, s, xi, yi, pos
  Protected r.f, g.f, b.f, r1, g1, b1
  Protected *srcPix.Pixel32, *dstPix.Pixel32

  ; Initialisation du générateur aléatoire pour ce thread
  Random((thread_pos+1)*1000)

  For y = yStart To yEnd
    For x = 0 To w-1
      r = 0 : g = 0 : b = 0

      For s = 0 To samples-1
        ; angle aléatoire en radians
        Protected angle.f = Random(360)*#PI/180.0
        ; distance aléatoire en flottant
        Protected dist.f = Random(radius)

        xi = x + Cos(angle) * dist
        yi = y + Sin(angle) * dist

        Clamp(xi, 0, w-1)
        Clamp(yi, 0, h-1)

        pos = (Int(yi)*w + Int(xi))<<2
        *srcPix = *src + pos
        getrgb(*srcPix\l, r1, g1, b1)
        r + r1 : g + g1 : b + b1
      Next

      r / samples : g / samples : b / samples

      ; interpolation sharpness
      *srcPix = *src + ((y*w+x)<<2)
      getrgb(*srcPix\l, r1, g1, b1)
      r = r*sharpness + r1*(1-sharpness)
      g = g*sharpness + g1*(1-sharpness)
      b = b*sharpness + b1*(1-sharpness)
      clamp_rgb(r,g,b)

      pos = (y*w + x)<<2
      *dstPix = *dst + pos
      *dstPix\l = (Int(r)<<16) | (Int(g)<<8) | Int(b)
    Next
  Next
EndProcedure

; ===== Procédure principale =====
Procedure PoissonDiskBlur(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Blur
    param\name = "PoissonDiskBlur"
    param\remarque = "Flou Poisson Disk"

    param\info[0] = "Rayon"
    param\info[1] = "Échantillons"
    param\info[2] = "Force (sharpness)"
    param\info[3] = "Itérations"
    param\info[4] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 100 : param\info_data(0,2) = 5
    param\info_data(1,0) = 1 : param\info_data(1,1) = 64  : param\info_data(1,2) = 16
    param\info_data(2,0) = 0 : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 1 : param\info_data(3,1) = 10  : param\info_data(3,2) = 1
    param\info_data(4,0) = 0 : param\info_data(4,1) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf

  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf

  Protected iterations = *param\option[3]
  
  Protected *tempo , tmpSrc , tmpDst , i
  tmpDst = *param\cible
  If *param\source = *param\cible
    *tempo = AllocateMemory(*param\lg * *param\ht * 4)
    If Not *tempo : ProcedureReturn : EndIf
    CopyMemory(*param\source , *tempo , *param\lg * *param\ht * 4)
    tmpSrc = *tempo 
  Else
    tmpSrc = *param\source
  EndIf
  
  For i = 1 To iterations
    *param\addr[0] = tmpSrc
    *param\addr[1] = tmpDst
    MultiThread_MT(@PoissonDiskBlur_MT())

    ; swap pour la prochaine itération
    If i < iterations
      Swap tmpSrc, tmpDst
    EndIf
  Next
  
  If *param\mask And *param\option[4] : *param\mask_type = *param\option[4] - 1 : MultiThread_MT(@_mask()) : EndIf
  If *tempo : FreeMemory(*tempo) : EndIf
EndProcedure

;----------------

; ======================================================
; Guided Filter Couleur simplifié (auto-guided, intégrales)
; ======================================================

; --- Integral image pour un canal (entier) ---
Procedure ComputeIntegral(*src, *integral, lg, ht)
  Protected x, y, pos, val, top, left, topleft
  For y = 0 To ht - 1
    For x = 0 To lg - 1
      pos = (y * lg + x) * 4
      val = PeekL(*src + pos) & $FF
      top = 0 : left = 0 : topleft = 0
      If y>0 : top  = PeekL(*integral + ((y-1) * lg + x) *4 ) : EndIf
      If x>0 : left = PeekL(*integral + (y * lg + x - 1) *4 ) : EndIf
      If x>0 And y>0 : topleft = PeekL(*integral + ((y - 1)*lg + x - 1) * 4) : EndIf
      PokeL(*integral + pos, val + top + left - topleft)
    Next
  Next
EndProcedure

; --- Somme d'une fenêtre avec intégrale (entier) ---
Procedure.l BoxSum(*integral, lg, ht, x, y, r)
  Protected x0 = Max_2(0, x-r) - 1
  Protected y0 = Max_2(0, y-r) - 1
  Protected x1 = Min_2(lg-1, x+r)
  Protected y1 = Min_2(ht-1, y+r)
  Protected A.l=0, B.l=0, C.l=0, D.l=0
  If x0>=0 And y0>=0 : A = PeekL(*integral + (y0*lg+x0)*4) : EndIf
  If x0>=0           : B = PeekL(*integral + (y1*lg+x0)*4) : EndIf
  If y0>=0           : C = PeekL(*integral + (y0*lg+x1)*4) : EndIf
  D = PeekL(*integral + (y1*lg+x1)*4)
  ProcedureReturn D-B-C+A
EndProcedure

; --- Integral float pour I² ou a/b ---
Procedure ComputeIntegralFloat(*src, *integral, lg, ht)
  Protected x, y, pos
  Protected rowSum.f
  For y = 0 To ht-1
    rowSum = 0.0
    For x = 0 To lg-1
      pos = (y*lg + x)*4
      rowSum + PeekF(*src+pos)
      If y=0
        PokeF(*integral+pos, rowSum)
      Else
        PokeF(*integral+pos, rowSum + PeekF(*integral+pos-(lg<<2)))
      EndIf
    Next
  Next
EndProcedure

Procedure.f SumWindowFloat(*integral, lg, ht, x, y, r)
  Protected x0 = x-r-1, y0 = y-r-1, x1 = x+r, y1 = y+r
  If x0<0 : x0=-1 : EndIf
  If y0<0 : y0=-1 : EndIf
  If x1>lg-1 : x1=lg-1 : EndIf
  If y1>ht-1 : y1=ht-1 : EndIf
  Protected A.f=0.0, B.f=0.0, C.f=0.0, D.f=0.0
  If x0>=0 And y0>=0 : A=PeekF(*integral + (y0*lg+x0)*4) : EndIf
  If x0>=0           : B=PeekF(*integral + (y1*lg+x0)*4) : EndIf
  If y0>=0           : C=PeekF(*integral + (y0*lg+x1)*4) : EndIf
  D = PeekF(*integral + (y1*lg+x1)*4)
  ProcedureReturn D-B-C+A
EndProcedure


Macro GuidedFilterColor_SP1_MT(col1 , col2 , var)
      meanI  = BoxSum(*int#col1, lg, ht, x, y, radius)*invArea
      meanII = SumWindowFloat(*int#col2, lg, ht, x, y, radius)*invArea
      varI = meanII - meanI*meanI
      If varI < 0 : varI = 0 : EndIf
      a = varI / (varI + eps)
      b = meanI - a * meanI
      val = PeekL(*I_#col1 + pos) & $FF
      var = a * val + b
EndMacro

Procedure GuidedFilterColor_SP2_MT(*param.parametre)
  Protected lg  = *param\lg
  Protected ht  = *param\ht
  Protected thread_start = (*param\thread_pos * ht) / *param\thread_max
  Protected thread_stop = (((*param\thread_pos + 1) * ht) / *param\thread_max) - 1
  If thread_stop >= ht : thread_stop = ht - 1 : EndIf
  Protected *I_R = *param\addr[3]
  Protected *I_G = *param\addr[4]
  Protected *I_B = *param\addr[5]
  Protected *tmpR = *param\addr[12]
  Protected *tmpG = *param\addr[13]
  Protected *tmpB = *param\addr[14]
  Protected x , y , pos , var
  For y = thread_start To thread_stop
    For x = 0 To lg - 1
      pos = (y * lg + x) * 4
      var = PeekL(*I_R + pos) & $FF
      PokeF(*tmpR + pos, var * var)
      var = PeekL(*I_G + pos) & $FF
      PokeF(*tmpG + pos, var * var)
      var = PeekL(*I_B + pos) & $FF
      PokeF(*tmpB + pos, var * var)
    Next
  Next
EndProcedure


; --- Guided Filter couleur simplifié ---
Procedure GuidedFilterColor_MT(*param.parametre)
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected radius = *param\option[0]
  Protected eps.f = *param\option[1]
  Protected invArea.f = 1.0 / ((2 * radius + 1) * (2 * radius + 1))
  Protected x, y, pos
  Protected meanI.f, meanII.f, varI.f, a.f, b.f, q.f
  Protected rc, gc, bc
  Protected val.l , var

  ; Pointeurs
  Protected *I_R   = *param\addr[3], *I_G   = *param\addr[4] , *I_B   = *param\addr[5]
  Protected *intR  = *param\addr[6], *intG  = *param\addr[7] , *intB  = *param\addr[8]
  Protected *intRR = *param\addr[9], *intGG = *param\addr[10], *intBB = *param\addr[11]
  
    Protected thread_start = (*param\thread_pos * ht) / *param\thread_max
    Protected thread_stop = (((*param\thread_pos + 1) * ht) / *param\thread_max) - 1
    
  ; 3) calcul final q = a*I + b (auto-guided)
  For y = thread_start To thread_stop
    For x = 0 To lg - 1
      pos=(y * lg + x) * 4
      GuidedFilterColor_SP1_MT(r , rr , rc)
      GuidedFilterColor_SP1_MT(g , gg , gc)
      GuidedFilterColor_SP1_MT(b , bb , bc)
      clamp_rgb(rc , gc, bc)
      PokeL(*param\addr[1] + pos, (rc<<16)|(gc<<8)|bc)
    Next
  Next
EndProcedure

; --- Split canaux ---
Procedure GuidedFilterColor_SP0_MT(*param.parametre)
  Protected *source.Pixel32
  Protected total=*param\lg * *param\ht
  Protected start = (*param\thread_pos * total) / *param\thread_max
  Protected stop = (((*param\thread_pos + 1) * total) / *param\thread_max) - 1
  Protected i , pos , r , g , b
  For i = start To stop
    pos = i << 2
    *source =*param\addr[0] + pos
    getrgb( *source\l , r , g , b)
    PokeA(*param\addr[3] + pos , r)
    PokeA(*param\addr[4] + pos , g)
    PokeA(*param\addr[5] + pos , b)
  Next
EndProcedure

; --- Wrapper ---
Procedure GuidedFilterColor(*param.parametre)
  If *param\info_active
    *param\name="GuidedFilterColor"
    *param\typ=#Filter_Type_Blur
    *param\remarque="Guided Filter couleur (exact)"
    *param\info[0]="Radius"
    *param\info[1]="Epsilon"
    *param\info_data(0,0) = 1 : *param\info_data(0,1) = 50   : *param\info_data(0,2) = 4
    *param\info_data(1,0) = 1 : *param\info_data(1,1) = 1000 : *param\info_data(1,2) = 50
    ProcedureReturn
  EndIf
  
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  
  Protected lg = *param\lg
  Protected ht = *param\ht
  
  Protected size = lg * ht * 4
  Protected i , err = 0
  For i = 3 To 11 : *param\addr[i] = AllocateMemory(size) : If Not *param\addr[i] : err = 1 : EndIf : Next
  If err = 1 : For i = 3 To 11 : If *param\addr[i] : FreeMemory(*param\addr[i]) : EndIf : Next : ProcedureReturn : EndIf
  
  *param\addr[0] = *param\source
  MultiThread_MT(@GuidedFilterColor_SP0_MT())
  
  ; Pointeurs
  Protected *I_R   = *param\addr[3], *I_G   = *param\addr[4] , *I_B   = *param\addr[5]
  Protected *intR  = *param\addr[6], *intG  = *param\addr[7] , *intB  = *param\addr[8]
  Protected *intRR = *param\addr[9], *intGG = *param\addr[10], *intBB = *param\addr[11]
  
  ; 1) intégrales I
  ComputeIntegral(*I_R, *intR, lg, ht)
  ComputeIntegral(*I_G, *intG, lg, ht)
  ComputeIntegral(*I_B, *intB, lg, ht)
  
  ; 2) intégrales float I²  
  *param\addr[12] = AllocateMemory(size)
  *param\addr[13] = AllocateMemory(size)
  *param\addr[14] = AllocateMemory(size)
  MultiThread_MT(@GuidedFilterColor_SP2_MT())
  ComputeIntegralFloat(*param\addr[12], *intRR, lg, ht)
  ComputeIntegralFloat(*param\addr[13], *intGG, lg, ht)
  ComputeIntegralFloat(*param\addr[14], *intBB, lg, ht)
  FreeMemory(*param\addr[12])
  FreeMemory(*param\addr[13])
  FreeMemory(*param\addr[14])
  
  
  *param\addr[0] = *param\source 
  *param\addr[1] = *param\cible    
  MultiThread_MT(@GuidedFilterColor_MT())
  ;filter_start(@GuidedFilterColor_MT() , 2)
  
  For i = 3 To 11 : FreeMemory(*param\addr[i]) : Next
EndProcedure

;----------------

Macro HeatDiffusionAnisoBlur_sp1(var)
  cN = PeekF(*addr3 + Abs(N#var - var) * 4)
  cS = PeekF(*addr3 + Abs(S#var - var) * 4)
  cW = PeekF(*addr3 + Abs(W#var - var) * 4)
  cE = PeekF(*addr3 + Abs(E#var - var) * 4)
  var + lambda * (cN * (N#var - var) + cS * (S#var - var) + cW * (W#var - var) + cE * (E#var - var))
EndMacro

Procedure HeatDiffusionAnisoBlur(*param.parametre)
  Protected *source.Pixel32
  Protected *cible.Pixel32 
  Protected *addr0 = *param\addr[0]
  Protected *addr1 = *param\addr[1]
  Protected *addr3 = *param\addr[3]
  Protected lg  = *param\lg
  Protected ht = *param\ht
  Protected lambda.f = 0.2
  Protected k.f = 10.0
  If *param\option[1] > 0 : k = *param\option[1] : EndIf
  If *param\option[2] > 0 : lambda = *param\option[2]/100.0 : EndIf
  Protected x, y, r, g, b , pos , pos1
  Protected Nr , Ng ,Nb , Sr , Sg , Sb , Wr , Wg , Wb , Er , Eg , Eb 
  Protected cN.f, cS.f, cW.f, cE.f
  Protected startY = (*param\thread_pos * ht) / *param\thread_max
  Protected stopY  = ((*param\thread_pos + 1) * ht) / *param\thread_max - 1
  If stopY >= ht : stopY = ht - 1 : EndIf
  For y = startY To stopY
    For x = 0 To lg - 1
      pos = (y * lg + x) << 2
      pos1 = *addr0 + pos
      *source = pos1
      getrgb(*source\l, r, g, b)
      If y > 0 : *source = pos1 - lg * 4 : getrgb(*source\l, nr, ng, nb) : Else : nr = r : ng = g : nb =b : EndIf
      If y < ht - 1 : *source = pos1 + lg * 4 : getrgb(*source\l, sr, sg, sb) : Else : sr = r : sg = g : sb =b : EndIf
      If x > 0 : *source = pos1 - 4 : getrgb(*source\l, wr, wg, wb) : Else : wr = r : wg = g : wb =b : EndIf
      If x < lg - 1 : *source = pos1 + 4 : getrgb(*source\l, er, eg, eb) : Else : er = r : eg = g : eb =b : EndIf
      ; === DIFFUSION ANISOTROPE ===
      HeatDiffusionAnisoBlur_sp1(r)
      HeatDiffusionAnisoBlur_sp1(g)
      HeatDiffusionAnisoBlur_sp1(b)
      clamp_rgb(r, g, b)
      *cible = *addr1 + pos
      *cible\l = (r << 16) | (g << 8) | b
    Next
  Next
EndProcedure

Procedure HeatDiffusionBlur(*param.parametre)
  If *param\info_active
    *param\name = "HeatDiffusionAnisotropic"
    *param\typ = #Filter_Type_Blur
    *param\remarque = "Flou anisotrope (Perona-Malik)"
    *param\info[0] = "Iterations"
    *param\info[1] = "Contraste K"
    *param\info[2] = "Lambda (%)"
    *param\info[3] = "Masque binaire"
    *param\info_data(0,0) = 1 : *param\info_data(0,1) = 50  : *param\info_data(0,2) = 50
    *param\info_data(1,0) = 1 : *param\info_data(1,1) = 100 : *param\info_data(1,2) = 20
    *param\info_data(2,0) = 1 : *param\info_data(2,1) = 25  : *param\info_data(2,2) = 25
    *param\info_data(3,0) = 0 : *param\info_data(3,1) = 2   : *param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  Protected *tempo = AllocateMemory(*param\lg * *param\ht * 4)
  If Not *tempo : ProcedureReturn : EndIf
  *param\addr[3] = AllocateMemory(*param\lg * *param\ht * 4)
  If Not *param\addr[3] : ProcedureReturn : EndIf
  Protected i , var.f
  For i = 0 To 255
    var = Exp( - Pow(i / *param\option[1], 2))
    PokeF(*param\addr[3] + i * 4 , var)
  Next
  CopyMemory(*param\source, *tempo, *param\lg * *param\ht * 4)
  *param\addr[0] = *tempo
  *param\addr[1] = *param\cible
  For i = 1 To *param\option[0]
    MultiThread_MT(@HeatDiffusionAnisoBlur())
    Swap *param\addr[0], *param\addr[1]
  Next
  If *param\mask And *param\option[3] : *param\mask_type = *param\option[3] - 1 : MultiThread_MT(@_mask()) : EndIf
  If *tempo : FreeMemory(*tempo) : EndIf
  If *param\addr[3] : FreeMemory(*param\addr[3]) : EndIf
EndProcedure

;----------------

Procedure OpticalBlur_MT(*param.parametre)
  Protected *source.Pixel32
  Protected *cible.Pixel32 
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected radius = *param\option[0]
  Protected x , y , ix , iy
  Protected rSum , gSum , bSum
  Protected count
  Protected pos , r , g , b , r1 , g1 , b1
  Protected dx , dy
  Protected thread_startY = (*param\thread_pos * ht) / *param\thread_max
  Protected thread_stopY  = ((*param\thread_pos + 1) * ht) / *param\thread_max - 1
  If thread_stopY >= ht : thread_stopY = ht - 1 : EndIf
  For y = thread_startY To thread_stopY
    For x = 0 To lg - 1
      rSum=0 : gSum=0 : bSum=0 : count=0
      pos = (y * lg + x) * 4
      For iy = -radius To radius
        For ix = -radius To radius
          dx = ix : dy = iy
          If dx * dx + dy * dy <= radius * radius
            If (x + ix) >= 0 And (x + ix) < lg And (y + iy) >= 0 And (y + iy) < ht
              *source = *param\addr[0] + ((y + iy) * lg + (x + ix)) * 4
              getrgb(*source\l , r1 , g1 , b1)
              rSum + r1 : gSum + g1 : bSum + b1
              count + 1
            EndIf
          EndIf
        Next
      Next
      If count>0
        r = rSum / count
        g = gSum / count
        b = bSum / count
      Else
        *source = *param\addr[0] + pos
        getrgb(*source\l , r1 , g1 , b1)
      EndIf
      *cible = *param\addr[1] + pos
      *cible\l = (r << 16) | (g << 8) | b
    Next
  Next
EndProcedure

Procedure OpticalBlur(*param.parametre)
  If *param\info_active
    *param\name = "OpticalBlur"
    *param\typ = #Filter_Type_Blur
    *param\remarque = "Flou optique simulant un objectif"
    *param\info[0] = "Radius"
    *param\info[1] = "Nombre de passe"
    *param\info[2] = "Masque binaire"
    *param\info_data(0,0)=1 : *param\info_data(0,1)=10 : *param\info_data(0,2)=1
    *param\info_data(1,0)=1 : *param\info_data(1,1)=10 : *param\info_data(1,2)=1
    *param\info_data(2,0)=0 : *param\info_data(2,1)=2  : *param\info_data(2,2)=0
    ProcedureReturn
  EndIf
  
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  Protected *tempo = AllocateMemory(*param\lg * *param\ht * 4)
  If Not *tempo : ProcedureReturn : EndIf
  
  CopyMemory(*param\source, *tempo, *param\lg * *param\ht * 4)
  *param\addr[0] = *tempo
  *param\addr[1] = *param\cible
  Protected i
  For i = 1 To *param\option[1]
    MultiThread_MT(@OpticalBlur_MT())
    Swap *param\addr[0], *param\addr[1]
  Next
  If *param\mask And *param\option[2] : *param\mask_type = *param\option[2] - 1 : MultiThread_MT(@_mask()) : EndIf
  If *tempo : FreeMemory(*tempo) : EndIf
  
EndProcedure

;----------------
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

les detections de contour

edge_detection.pbi

Code : Tout sélectionner

Macro edge_detection_decalre()
  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected mul.f = *param\option[0]
  Protected toGray = *param\option[2]
  Protected inverse = *param\option[3]
  Protected seuillage = *param\option[4]
  clamp(mul, 0, 100)
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected startPos = (*param\thread_pos * (ht - 2)) / *param\thread_max
  Protected endPos   = ((*param\thread_pos + 1) * (ht - 2)) / *param\thread_max
  If startPos < 1 : startPos = 1 : EndIf
EndMacro

Macro edge_detection_option(ro , go , bo)
  r = ro * mul : g = go * mul : b = bo * mul
  clamp_rgb(r, g, b)
  If seuillage > 0 : seuil_rgb(seuillage , r , g , b) : EndIf
  If toGray : r = (r * 77 + g * 150 + b * 29) >> 8 : g = r : b = r : EndIf
  If inverse : r = 255 - r : g = 255 - g : b = 255 - b : EndIf
  *dstPixel = (*cible + (y * lg + x ) * 4)
  *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
EndMacro

Procedure canny_grayscale_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *dst  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected i , var , r , g , b
  
  Protected totalPixels = lg * ht
  Protected start = (*param\thread_pos * totalPixels) / *param\thread_max
  Protected stop = ((*param\thread_pos + 1) * totalPixels) / *param\thread_max
  
  For i = start To stop -1
    var = PeekL(*src + i * 4)   ; Lecture pixel source (32 bits)
    GetRGB(var, r, g, b) 
    PokeA(*dst + i , ((r * 77 + g * 150 + b * 29) >> 8) ) ; Stockage gris dans *dst (32 bits)
  Next
EndProcedure

Procedure FiltrageGaussien_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *dst  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected x, y, i, j, r, g, b, v, gray , idx , var
  Dim weights(8)
  weights(0) = 1 : weights(1) = 2 : weights(2) = 1
  weights(3) = 2 : weights(4) = 4 : weights(5) = 2
  weights(6) = 1 : weights(7) = 2 : weights(8) = 1
  
  Protected start = (*param\thread_pos * ht ) / *param\thread_max
  Protected stop = ((*param\thread_pos + 1) * ht ) / *param\thread_max
  If start < 1 : start = 1 : EndIf
  If stop > (ht - 2) : stop = (ht - 2) : EndIf
  For y = start To stop
    For x = 1 To lg - 2
      v = 0 : idx = 0
      For j = -1 To 1
        For i = -1 To 1
          var = PeekA(*src + ((y+j)*lg + (x+i)) ) 
          v + var * weights(idx)                  
          idx + 1
        Next
      Next
      v = v >> 4 ;
      If v > 255 : v = 255 : ElseIf v < 0 : v = 0 : EndIf
      PokeA(*dst + (y*lg + x), v )
    Next
  Next
EndProcedure

Procedure GradientSobel_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *mag  = *param\addr[1]
  Protected *dir  = *param\addr[2]
  Protected lg = *param\lg
  Protected ht = *param\ht
 
  Protected x, y, gx, gy, magnitude, angle
  Protected line0, line1, line2
  Protected idx0, idx1, idx2
  Dim line0(lg - 1)
  Dim line1(lg - 1)
  Dim line2(lg - 1)
  Protected start = (*param\thread_pos * ht ) / *param\thread_max
  Protected stop = ((*param\thread_pos + 1) * ht ) / *param\thread_max
  If start < 1 : start = 1 : EndIf
  If stop > (ht - 2) : stop = (ht - 2) : EndIf
  For y = start To stop
    For x = 0 To lg - 1
      line0(x) = PeekA(*src + ((y - 1) * lg + x) )
      line1(x) = PeekA(*src + (y * lg + x) )
      line2(x) = PeekA(*src + ((y + 1) * lg + x) )
    Next
    For x = 1 To lg - 2
      gx = -line0(x-1) + line0(x+1) - 2 * line1(x-1) + 2 * line1(x+1) - line2(x-1) + line2(x+1)
      gy = -line0(x-1) - 2 * line0(x) - line0(x+1) + line2(x-1) + 2 * line2(x) + line2(x+1)
      magnitude = Abs(gx) + Abs(gy)
      If magnitude > 255 : magnitude = 255 : EndIf
      If Abs(gx) > Abs(gy)
        If gx * gy >= 0
          angle = 0   
        Else
          angle = 3   
        EndIf
      Else
        If gx * gy >= 0
          angle = 1
        Else
          angle = 2 
        EndIf
      EndIf
      PokeA(*dir + y * lg + x, angle)
      PokeA(*mag + y * lg + x, magnitude)
      PokeA(*dir + y * lg + x, angle)
    Next
  Next
EndProcedure

Procedure sobel_direction_MT(*param.parametre)
  Protected *mag = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected x , y , var , pos
  Protected totalPixels = lg * ht
  Protected start = (*param\thread_pos * totalPixels) / *param\thread_max
  Protected stop = ((*param\thread_pos + 1) * totalPixels) / *param\thread_max
  For pos = start To stop - 1
    var = PeekA(*mag + pos)
    PokeL(*cible + pos << 2, var * $10101)
  Next
EndProcedure

Procedure SuppressionNonMaximale_MT(*param.parametre)
  Protected *mag = *param\addr[0]
  Protected *dir  = *param\addr[1]
  Protected *dst  = *param\addr[2]
  Protected lg = *param\lg
  Protected ht = *param\ht
  
  Protected x, y, angle, m, m1, m2
  Protected fx, fy
  
  Protected start = (*param\thread_pos * ht ) / *param\thread_max
  Protected stop = ((*param\thread_pos + 1) * ht ) / *param\thread_max
  If start < 1 : start = 1 : EndIf
  If stop > (ht - 2) : stop = (ht - 2) : EndIf

  For y = start To stop
    For x = 1 To lg - 2
      m = PeekA(*mag + y*lg + x)  
      angle = PeekA(*dir + y*lg + x)

      Select angle
        Case 0  
          fx = 1 : fy = 0
        Case 1  
          fx = 1 : fy = -1
        Case 2  
          fx = 0 : fy = -1
        Case 3  
          fx = -1 : fy = -1
        Default
          fx = 0 : fy = 0 
      EndSelect
      m1 = PeekA(*mag + (y+fy)*lg + (x+fx))
      m2 = PeekA(*mag + (y-fy)*lg + (x-fx))
      If m >= m1 And m >= m2
        PokeA(*dst + y*lg + x, m)
      Else
        PokeA(*dst + y*lg + x, 0)
      EndIf
    Next
  Next
EndProcedure

Procedure SeuillageDouble_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *dst = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected seuilFort = *param\option[5]
  Protected seuilFaible = *param\option[6]
  Protected totalPixels = lg * ht
  Protected start = (*param\thread_pos * totalPixels) / *param\thread_max
  Protected stop = ((*param\thread_pos + 1) * totalPixels) / *param\thread_max
  Protected x, y, var , i
  For i = start To stop -1
    var = PeekA(*src + i)
    If var >= seuilFort
      PokeA(*dst + i, 255)
    ElseIf var < seuilFaible
      PokeA(*dst + i, 0)
    Else
      PokeA(*dst + i, 128)
    EndIf
  Next
EndProcedure

Procedure Sobel_No_Hysteresis(*param.parametre)
  Protected *thresh = *param\addr[0]
  Protected *cible = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht 
  Protected x, y, var , i
  Protected start = (*param\thread_pos * ht ) / *param\thread_max
  Protected stop = ((*param\thread_pos + 1) * ht ) / *param\thread_max
  For y = start To stop - 1
    For x = 0 To lg - 1
      var = PeekA(*thresh + y*lg + x)
      If var = 255
        PokeL(*cible + (y*lg + x) * 4, $FFFFFF) 
      ElseIf var = 128
        PokeL(*cible + (y*lg + x) * 4, $808080)
      Else
        PokeL(*cible + (y*lg + x) * 4, 0) 
      EndIf
    Next
  Next
  
EndProcedure

Procedure Hysteresis_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *dst = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht 
  Protected x, y, dx, dy, found, var
  Protected start = (*param\thread_pos * ht ) / *param\thread_max
  Protected stop = ((*param\thread_pos + 1) * ht ) / *param\thread_max
  If start < 1 : start = 1 : EndIf
  If stop > (ht - 2) : stop = (ht - 2) : EndIf
  For y = start To stop
    For x = 1 To lg - 2
      var = PeekA(*src + y*lg + x)
      If var = 128
        found = 0
        For dy = -1 To 1
          For dx = -1 To 1
            If PeekA(*src + (y+dy)*lg + (x+dx)) = 255
              found = 1 : Break 2
            EndIf
          Next
        Next
        If found
          PokeL(*dst + (y*lg + x)*4, $FFFFFF)
        Else
          PokeL(*dst + (y*lg + x)*4, 0)
        EndIf
      ElseIf var = 255
        PokeL(*dst + (y*lg + x)*4, $FFFFFF)
      Else
        PokeL(*dst + (y*lg + x)*4, 0) 
      EndIf
    Next
  Next
EndProcedure

Procedure canny(*param.parametre)
  
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "canny"
    param\remarque = ""
    param\info[0] = "seuil_Fort"
    param\info[1] = "seuil_Faible"
    param\info[2] = "Sortie brute"
    param\info[3] = "hystérésis"
    param\info[4] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 255 : param\info_data(0,2) = 100
    param\info_data(1,0) = 0 : param\info_data(1,1) = 255 : param\info_data(1,2) = 50
    param\info_data(2,1) = 0 : param\info_data(2,1) = 1  : param\info_data(2,2) = 0
    param\info_data(3,1) = 0 : param\info_data(3,1) = 1  : param\info_data(3,2) = 0
    param\info_data(4,1) = 0 : param\info_data(4,1) = 2  : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  
  Protected *source = *param\source
  Protected *cible  = *param\cible
  Protected *mask   = *param\mask
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected  *gray , *blurred , *mag , *dir , *nms , *thresh , *final
  Protected val , x , y , i
  
  If *source = 0 Or *cible = 0 : ProcedureReturn : EndIf
  Protected thread = CountCPUs(#PB_System_CPUs)
  clamp(thread , 1 , 128)
  Protected Dim tr(thread)

  *gray      = AllocateMemory(lg * ht)  
  *blurred   = AllocateMemory(lg * ht)
  *mag       = AllocateMemory(lg * ht)
  *dir       = AllocateMemory(lg * ht)
  *nms       = AllocateMemory(lg * ht)
  *thresh    = AllocateMemory(lg * ht)
  *final     = AllocateMemory(lg * ht * 4)
  Protected seuilFort = param\option[0]
  Protected seuilFaible = param\option[1]
  Protected seuillage = param\option[2]
  Protected hysteresis = param\option[3]
  clamp(seuilFort , 1 ,255)
  clamp(seuilFaible , 1 ,255)

  *param\addr[0] = *source
  *param\addr[1] = *gray
  MultiThread_MT(@canny_grayscale_MT())

  *param\addr[0] = *gray
  *param\addr[1] = *blurred
  MultiThread_MT(@FiltrageGaussien_MT())

  *param\addr[0] = *blurred
  *param\addr[1] = *mag
  *param\addr[2] = *dir
  MultiThread_MT(@GradientSobel_MT())  
  
  If seuillage = 1
    *param\addr[0] = *mag
    *param\addr[1] = *cible
    MultiThread_MT(@sobel_direction_MT())
  Else
    *param\addr[0] = *mag
    *param\addr[1] = *dir
    *param\addr[2] = *nms
    MultiThread_MT(@SuppressionNonMaximale_MT())  

    *param\addr[0] = *nms
    *param\addr[1] = *thresh
    *param\option[5] = seuilFort
    *param\option[6] = seuilFaible
    MultiThread_MT(@SeuillageDouble_MT())  
    
    If hysteresis = 0
      *param\addr[0] = *thresh
      *param\addr[1] = *cible
      MultiThread_MT(@Sobel_No_Hysteresis()) 
    Else
      *param\addr[0] = *thresh
      *param\addr[1] = *cible
      MultiThread_MT(@Hysteresis_MT()) 
    EndIf
  EndIf
  If *param\mask And *param\option[4] : *param\mask_type = *param\option[4] - 1 : MultiThread_MT(@_mask()) : EndIf
  
  FreeMemory(*gray)
  FreeMemory(*blurred)
  FreeMemory(*mag)
  FreeMemory(*dir)
  FreeMemory(*nms)
  FreeMemory(*thresh)
  FreeMemory(*final)
  FreeArray(tr())
EndProcedure

;-------------------------

Procedure FreiChen_MT(*param.parametre)
  edge_detection_decalre()
  mul = mul * 0.05
  Protected Dim r3(9)
  Protected Dim g3(9)
  Protected Dim b3(9)
  Protected a, r, g, b
  Protected x, y, i, dir
  Protected valR, valG, valB
  Protected rMax, gMax, bMax
  ; Les masques sont normalisés, utilisant sqrt(2) ~ 1.4142
  Protected Dim mask(7, 8)
  DataSection
    FreiChen_data:
    Data.f  1,  1.4142,  1,     0,   0,   0,    -1, -1.4142, -1   
    Data.f  0,  1,       1.4142, -1,  0,  1.4142, -1, -1,      0 
    Data.f -1, 0,       1,     -1,  0,  1,      -1,  0,      1  
    Data.f -1, -1.4142, 0,     -1,  0,  1.4142, 0,   1.4142,  1  
    Data.f -1, -1.4142, -1,     0,  0,  0,      1,   1.4142,  1  
    Data.f  0, -1,     -1.4142, 1,  0, -1.4142, 1,   1,       0   
    Data.f  1,  0,     -1,      1,  0, -1,      1,   0,      -1  
    Data.f  1,  1,      0,      1,  0, -1,      0,  -1,      -1 
  EndDataSection
  Restore FreiChen_data
  For dir = 0 To 7 : For i = 0 To 8 : Read.f mask(dir, i) : Next : Next
  For y = startPos To endPos
    For x = 1 To lg - 2
      *srcPixel = (*source + ((y - 1) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(0), g3(0), b3(0)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(1), g3(1), b3(1)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(2), g3(2), b3(2))
      *srcPixel = (*source + (y * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(3), g3(3), b3(3)) : *srcPixel + 4
      getargb(*srcPixel\l , a, r3(4), g3(4), b3(4)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(5), g3(5), b3(5))
      *srcPixel = (*source + ((y + 1) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(6), g3(6), b3(6)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(7), g3(7), b3(7)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(8), g3(8), b3(8))
      
      rMax = 0 : gMax = 0 : bMax = 0
      For dir = 0 To 7
        valR = 0 : valG = 0 : valB = 0
        For i = 0 To 8
          valR + r3(i) * mask(dir, i)
          valG + g3(i) * mask(dir, i)
          valB + b3(i) * mask(dir, i)
        Next
        valR = Abs(valR)
        valG = Abs(valG)
        valB = Abs(valB)
        If valR > rMax : rMax = valR : EndIf
        If valG > gMax : gMax = valG : EndIf
        If valB > bMax : bMax = valB : EndIf
      Next
      edge_detection_option(rMax , gMax , bMax)
    Next
  Next
  FreeArray(r3()) : FreeArray(g3()) : FreeArray(b3())
EndProcedure

Procedure FreiChen(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "FreiChen"
    param\remarque = ""
    param\info[0] = "multiply"             
    param\info[1] = "ABS/SQR"            
    param\info[2] = "Noir et blanc"       
    param\info[3] = "inversion"   
    param\info[4] = "seuillage : 0 = off"
    param\info[5] = "Masque binaire"           
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100 : param\info_data(0,2) = 10 ;
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1   : param\info_data(1,2) = 0 
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1   : param\info_data(2,2) = 0 
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1   : param\info_data(3,2) = 0 
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255 : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@FreiChen_MT() , 5)
EndProcedure

;-------------------------

Procedure Kirsch_MT(*param.parametre)
  edge_detection_decalre()
  Protected i, j
  Protected rMax, gMax, bMax
  clamp(mul, 0, 100)
  mul = mul * 0.05
  Protected Dim r3(9)
  Protected Dim g3(9)
  Protected Dim b3(9)
  Protected a, r, g, b
  Protected x, y
  Protected dir, valR, valG, valB
  Protected Dim mask(7, 8)
  DataSection
    kirsch_data:
    Data.i 5, 5, 5, -3, 0, -3, -3, -3, -3  
    Data.i 5, 5, -3, 5, 0, -3, -3, -3, -3  
    Data.i -3, 5, 5, -3, 0, 5, -3, -3, -3  
    Data.i -3, -3, 5, -3, 0, 5, -3, -3, 5  
    Data.i -3, -3, -3, -3, 0, -3, 5, 5, 5   
    Data.i -3, -3, -3, -3, 0, -3, 5, 5, -3 
    Data.i -3, -3, -3, 5, 0, -3, 5, 5, -3  
    Data.i 5, -3, -3, 5, 0, -3, 5, -3, -3  
  EndDataSection
  
  Restore kirsch_data
  For dir = 0 To 7 : For i = 0 To 8 : Read.i mask(dir, i) : Next : Next
  For y = startPos To endPos
    For x = 1 To lg - 2
      *srcPixel = (*source + ((y - 1) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(0), g3(0), b3(0)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(1), g3(1), b3(1)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(2), g3(2), b3(2))
      *srcPixel = (*source + ((y) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(3), g3(3), b3(3)) : *srcPixel + 4
      getargb(*srcPixel\l , a, r3(4), g3(4), b3(4)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(5), g3(5), b3(5))
      *srcPixel = (*source + ((y + 1) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(6), g3(6), b3(6)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(7), g3(7), b3(7)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(8), g3(8), b3(8))
      rMax = 0 : gMax = 0 : bMax = 0
      For dir = 0 To 7
        valR = 0 : valG = 0 : valB = 0
        For i = 0 To 8
          valR + r3(i) * mask(dir, i)
          valG + g3(i) * mask(dir, i)
          valB + b3(i) * mask(dir, i)
        Next
        If valR > rMax : rMax = valR : EndIf
        If valG > gMax : gMax = valG : EndIf
        If valB > bMax : bMax = valB : EndIf
      Next
      edge_detection_option(Abs(rMax) , Abs(gMax) , Abs(bMax))
    Next
  Next
  FreeArray(r3()) : FreeArray(g3()) : FreeArray(b3())
EndProcedure

Procedure Kirsch(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "Kirsch"
    param\remarque = "Détection 8 directions (fort)"
    param\info[0] = "multiply"
    param\info[1] = "math"
    param\info[2] = "Noir et blanc"
    param\info[3] = "inversion"
    param\info[4] = "seuillage : 0 = off"
    param\info[5] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100 : param\info_data(0,2) = 10
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1 : param\info_data(1,2) = 0
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1   : param\info_data(2,2) = 0
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1   : param\info_data(3,2) = 0
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255 : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Kirsch_MT() , 5)
EndProcedure

;-------------------------

Procedure Laplacian_MT(*param.parametre)
  edge_detection_decalre()
  Protected mode = *param\option[1]
  mul = mul * 0.1
  Protected Dim r3(9)
  Protected Dim g3(9)
  Protected Dim b3(9)
  Protected a, r, g, b
  Protected x, y
  If endPos > ht - 2 : endPos = ht - 2 : EndIf
  For y = startPos To endPos
    For x = 1 To lg - 2
      *srcPixel = (*source + ((y - 1) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l, r3(0), g3(0), b3(0))
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l, r3(1), g3(1), b3(1))
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l, r3(2), g3(2), b3(2))
      *srcPixel = (*source + (y * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l, r3(3), g3(3), b3(3))
      *srcPixel = *srcPixel + 4
      getargb(*srcPixel\l, a, r3(4), g3(4), b3(4))
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l, r3(5), g3(5), b3(5))
      *srcPixel = (*source + ((y + 1) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l, r3(6), g3(6), b3(6))
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l, r3(7), g3(7), b3(7))
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l, r3(8), g3(8), b3(8))
      If mode = 0
        r = (r3(1) + r3(3) + r3(5) + r3(7)) - (4 * r3(4))
        g = (g3(1) + g3(3) + g3(5) + g3(7)) - (4 * g3(4))
        b = (b3(1) + b3(3) + b3(5) + b3(7)) - (4 * b3(4))
      Else
        r = (r3(0) + r3(1) + r3(2) + r3(3) + r3(5) + r3(6) + r3(7) + r3(8)) - (8 * r3(4))
        g = (g3(0) + g3(1) + g3(2) + g3(3) + g3(5) + g3(6) + g3(7) + g3(8)) - (8 * g3(4))
        b = (b3(0) + b3(1) + b3(2) + b3(3) + b3(5) + b3(6) + b3(7) + b3(8)) - (8 * b3(4))
      EndIf
      edge_detection_option(r , g , b)
    Next
  Next
EndProcedure

Procedure Laplacian(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "Laplacian"
    param\remarque = ""
    param\info[0] = "multiply"             
    param\info[1] = "mode"            
    param\info[2] = "Noir et blanc"       
    param\info[3] = "inversion"           
    param\info[4] = "seuillage : 0 = off"
    param\info[5] = "Masque binaire"          
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100  : param\info_data(0,2) = 10 ;
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1  : param\info_data(1,2) = 0 
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1  : param\info_data(2,2) = 0 
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1  : param\info_data(3,2) = 0 
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255 : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Laplacian_MT() , 5)
EndProcedure

;-------------------------

Procedure LaplacianOfGaussian_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected seuil = *param\option[0]
  Protected mul.f = *param\option[1]
  Protected maskSize = *param\option[2] 
  Protected sigma.f = *param\option[3] 
  Protected invese = *param\option[4]
  Protected toGray = *param\option[5]
  Protected seuillage = *param\option[6]
  maskSize = (maskSize * 2) + 1
  clamp(seuil, 0, 255)
  clamp(mul, 1, 100)
  clamp(sigma, 1, 100)
  sigma = sigma *0.01 + 0.1
  mul = mul * 0.1 + 1
  Protected offset = maskSize / 2
  Protected maskArea = maskSize * maskSize
  Dim logMask.l(maskArea - 1)
  Dim logMaskf.f(maskArea - 1) 
  Protected i, j, x, y, dx, dy, pos, r, g, b
  Protected cx = maskSize / 2
  Protected norm.f, value.f
  Protected sum.f = 0
  For y = 0 To maskSize - 1
    For x = 0 To maskSize - 1
      dx = x - cx
      dy = y - cx
      norm = (dx * dx + dy * dy) / (2 * sigma * sigma)
      value = -1 / (#PI * Pow(sigma, 4)) * (1 - norm) * Exp(-norm)
      sum = sum + value
      logMaskF(y * maskSize + x) = value
    Next
  Next
  For i = 0 To maskArea - 1 : logMask(i) = Int((logMaskF(i) - sum / maskArea) * mul) : Next
  Protected rf.f, gf.f, bf.f
  Protected rr, gg, bb, gray
  Protected startPos = offset + (*param\thread_pos * (ht - 2 * offset)) / *param\thread_max
  Protected endPos   = offset + ((*param\thread_pos + 1) * (ht - 2 * offset)) / *param\thread_max
  If startPos < offset : startPos = offset : EndIf
  If endPos > ht - offset : endPos = ht - offset : EndIf
  For y = startPos To endPos - 1
    For x = offset To lg - offset - 1
      rr = 0 : gg = 0 : bb = 0 : i = 0
      For dy = -offset To offset
        For dx = -offset To offset
          pos = PeekL(*source + ((y + dy) * lg + (x + dx)) * 4)
          GetRGB(pos, r, g, b)
          rr + r * logMask(i)
          gg + g * logMask(i)
          bb + b * logMask(i)
          i + 1
        Next
      Next
      If rr < 0 : rr = -rr : EndIf
      If gg < 0 : gg = -gg : EndIf
      If bb < 0 : bb = -bb : EndIf
      rr = rr >> 8 : gg = gg >> 8 : bb = bb >> 8
      clamp_rgb(rr, gg, bb) 
      If seuillage > 0 : seuil_rgb(seuillage , rr , gg , bb) : EndIf
      If toGray : gray = (rr * 77 + gg * 150 + bb * 29) >> 8 : rr = gray : gg = gray : bb = gray : EndIf
      If (rr + gg + bb) / 3 < seuil : rr = 0 : gg = 0 : bb = 0 : EndIf   
      If invese  : rr = 255 - rr : gg = 255 - gg : bb = 255 - bb : EndIf
      PokeL(*cible + (y * lg + x) * 4, rr << 16 + gg << 8 + bb)
    Next
  Next
  FreeArray(logMaskf())
  FreeArray(logMask())
EndProcedure

Procedure LaplacianOfGaussian(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "LaplacianOfGaussian"
    param\remarque = ""
    param\info[0] = "seuil"             
    param\info[1] = "multiply"                  
    param\info[2] = "maskSize"   
    param\info[3] = "sigma"
    param\info[4] = "inverse"
    param\info[5] = "togray" 
    param\info[6] = "seuillage : 0 = off"
    param\info[7] = "Masque binaire"           
    param\info_data(0,0) = 0 : param\info_data(0,1) = 255  : param\info_data(0,2) = 50
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100  : param\info_data(1,2) = 60 
    param\info_data(2,0) = 1 : param\info_data(2,1) = 5   : param\info_data(2,2) = 1
    param\info_data(3,0) = 1 : param\info_data(3,1) = 10  : param\info_data(3,2) = 3
    param\info_data(4,0) = 0 : param\info_data(4,1) = 1   : param\info_data(4,2) = 0 
    param\info_data(5,0) = 0 : param\info_data(5,1) = 1   : param\info_data(5,2) = 0
    param\info_data(6,0) = 0 : param\info_data(6,1) = 255 : param\info_data(6,2) = 0
    param\info_data(7,0) = 0 : param\info_data(7,1) = 2   : param\info_data(7,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@LaplacianOfGaussian_MT() , 7)
EndProcedure

;-------------------------

Procedure Prewitt_MT(*param.parametre)
  edge_detection_decalre()
  mul = mul * 0.05
  Protected Dim r3(9)
  Protected Dim g3(9)
  Protected Dim b3(9)
  Protected a, r, g, b
  Protected x, y, i, dir
  Protected valR, valG, valB
  Protected rMax, gMax, bMax
  Protected Dim mask(7, 8)
  DataSection
    Prewitt_data:
    Data.i   1,  1,  1,   0,  0,  0,  -1, -1, -1
    Data.i   0,  1,  1,  -1,  0,  1,  -1, -1,  0
    Data.i  -1,  0,  1,  -1,  0,  1,  -1,  0,  1 
    Data.i  -1, -1,  0,  -1,  0,  1,   0,  1,  1
    Data.i  -1, -1, -1,   0,  0,  0,   1,  1,  1 
    Data.i   0, -1, -1,   1,  0, -1,   1,  1,  0  
    Data.i   1,  0, -1,   1,  0, -1,   1,  0, -1
    Data.i   1,  1,  0,   1,  0, -1,   0, -1, -1
  EndDataSection
  Restore Prewitt_data
  For dir = 0 To 7 : For i = 0 To 8 : Read.i mask(dir, i) : Next : Next
  For y = startPos To endPos
    For x = 1 To lg - 2
      *srcPixel = (*source + ((y - 1) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(0), g3(0), b3(0)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(1), g3(1), b3(1)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(2), g3(2), b3(2))
      *srcPixel = (*source + (y * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(3), g3(3), b3(3)) : *srcPixel + 4
      getargb(*srcPixel\l , a, r3(4), g3(4), b3(4)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(5), g3(5), b3(5))
      *srcPixel = (*source + ((y + 1) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(6), g3(6), b3(6)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(7), g3(7), b3(7)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(8), g3(8), b3(8))
      rMax = 0 : gMax = 0 : bMax = 0
      For dir = 0 To 7
        valR = 0 : valG = 0 : valB = 0
        For i = 0 To 8
          valR + r3(i) * mask(dir, i)
          valG + g3(i) * mask(dir, i)
          valB + b3(i) * mask(dir, i)
        Next
        valR = Abs(valR)
        valG = Abs(valG)
        valB = Abs(valB)
        If valR > rMax : rMax = valR : EndIf
        If valG > gMax : gMax = valG : EndIf
        If valB > bMax : bMax = valB : EndIf
      Next
      edge_detection_option(rMax , gMax , bMax)
    Next
  Next
  FreeArray(r3()) : FreeArray(g3()) : FreeArray(b3())
EndProcedure

Procedure Prewitt(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "Prewitt"
    param\remarque = "Détection 8 directions"
    param\info[0] = "multiply"
    param\info[1] = "math"
    param\info[2] = "Noir et blanc"
    param\info[3] = "inversion"
    param\info[4] = "seuillage : 0 = off"
    param\info[5] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100 : param\info_data(0,2) = 10
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1 : param\info_data(1,2) = 0
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1   : param\info_data(2,2) = 0
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1   : param\info_data(3,2) = 0
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255 : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Prewitt_MT() , 5)
EndProcedure

;-------------------------

Procedure Roberts_RGBFromHSV(*r.Integer, *g.Integer, *b.Integer, h.f, s.f, v.f)
  Protected c.f = v * s
  Protected x.f = c * (1 - Abs(Mod(h / 60.0, 2) - 1))
  Protected m.f = v - c
  Protected r1.f, g1.f, b1.f
  Select Int(h / 60)
    Case 0 : r1 = c : g1 = x : b1 = 0
    Case 1 : r1 = x : g1 = c : b1 = 0
    Case 2 : r1 = 0 : g1 = c : b1 = x
    Case 3 : r1 = 0 : g1 = x : b1 = c
    Case 4 : r1 = x : g1 = 0 : b1 = c
    Default: r1 = c : g1 = 0 : b1 = x
  EndSelect
  *r\i = (r1 + m) * 255
  *g\i = (g1 + m) * 255
  *b\i = (b1 + m) * 255
EndProcedure


Procedure Roberts_MT(*param.parametre)
  edge_detection_decalre()
  Protected math = *param\option[1]
  Protected orientation = *param\option[5]
  Protected angle_add = *param\option[6]
  mul = mul * 0.05
  Protected a, r, g, b
  Protected r1, g1, b1, r2, g2, b2, r3, g3, b3, r4, g4, b4
  Protected gxR, gxG, gxB, gyR, gyG, gyB
  Protected valR, valG, valB , gx,gy, mag, angle.f
  Protected x, y
  For y = startPos To endPos - 1
    For x = 0 To lg - 2
      *srcPixel = (*source + (y * lg + x) * 4)
      getargb(*srcPixel\l, a, r1, g1, b1)
      *srcPixel = (*source + (y * lg + x + 1) * 4)
      getrgb(*srcPixel\l, r2, g2, b2)
      *srcPixel = (*source + ((y + 1) * lg + x) * 4)
      getrgb(*srcPixel\l, r3, g3, b3)
      *srcPixel = (*source + ((y + 1) * lg + x + 1) * 4)
      getrgb(*srcPixel\l, r4, g4, b4)
      gxR = r1 - r4
      gxG = g1 - g4
      gxB = b1 - b4
      gyR = r2 - r3
      gyG = g2 - g3
      gyB = b2 - b3
      If orientation
        gx = gxR + gxG + gxB
        gy = gyR + gyG + gyB
        mag   = Sqr(gx*gx + gy*gy) * mul
        angle = ATan2(gy, gx) * 180 / #PI
        If angle < 0 : angle + 360 : EndIf
        angle + angle_add
        If angle > 360 : angle - 360 : EndIf
        Roberts_RGBFromHSV(@valr, @valg, @valb, angle, 1.0, mag/255.0)
      Else
        If math
          valR = Abs(gxR) + Abs(gyR)
          valG = Abs(gxG) + Abs(gyG)
          valB = Abs(gxB) + Abs(gyB)
        Else
          valR = Sqr(gxR * gxR + gyR *gyR) 
          valG = Sqr(gxG * gxG + gyG *gyG) 
          valB = Sqr(gxB * gxB + gyB *gyB) 
        EndIf
      EndIf
      edge_detection_option(valR , valG , valB)
    Next
  Next
EndProcedure

Procedure Roberts(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "Roberts"
    param\remarque = "Détection 2 directions"
    param\info[0] = "multiply"
    param\info[1] = "math (ABS ou SQR)"
    param\info[2] = "Noir et blanc"
    param\info[3] = "inversion"
    param\info[4] = "seuillage : 0 = off"
    param\info[5] = "orientation"
    param\info[6] = "angle"
    param\info[7] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100 : param\info_data(0,2) = 10
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1 : param\info_data(1,2) = 0
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1   : param\info_data(2,2) = 0
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1   : param\info_data(3,2) = 0
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255   : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 1 : param\info_data(5,2) = 0
    param\info_data(6,0) = 0 : param\info_data(6,1) = 360 : param\info_data(6,2) = 0
    param\info_data(7,0) = 0 : param\info_data(7,1) = 2   : param\info_data(7,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Roberts_MT() , 7)
EndProcedure

;-------------------------

Procedure Robinson_MT(*param.parametre)
  edge_detection_decalre()
  mul = mul * 0.05
  Protected Dim r3(9)
  Protected Dim g3(9)
  Protected Dim b3(9)
  Protected a, r, g, b
  Protected x, y, i, dir
  Protected valR, valG, valB
  Protected rMax, gMax, bMax
  Protected Dim mask(7, 8)
  DataSection
    robinson_data:
    Data.i  1,  1,  1,  0,  0,  0, -1, -1, -1 
    Data.i  0,  1,  1, -1,  0,  1, -1, -1,  0  
    Data.i -1,  0,  1, -1,  0,  1, -1,  0,  1  
    Data.i -1, -1,  0, -1,  0,  1,  0,  1,  1 
    Data.i -1, -1, -1,  0,  0,  0,  1,  1,  1  
    Data.i  0, -1, -1,  1,  0, -1,  1,  1,  0  
    Data.i  1,  0, -1,  1,  0, -1,  1,  0, -1  
    Data.i  1,  1,  0,  1,  0, -1,  0, -1, -1  
  EndDataSection
  Restore robinson_data
  For dir = 0 To 7 : For i = 0 To 8 : Read.i mask(dir, i) : Next : Next
  For y = startPos To endPos
    For x = 1 To lg - 2
      *srcPixel = (*source + ((y - 1) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(0), g3(0), b3(0)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(1), g3(1), b3(1)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(2), g3(2), b3(2))
      *srcPixel = (*source + (y * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(3), g3(3), b3(3)) : *srcPixel + 4
      getargb(*srcPixel\l , a, r3(4), g3(4), b3(4)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(5), g3(5), b3(5))
      *srcPixel = (*source + ((y + 1) * lg + (x - 1)) * 4)
      getrgb(*srcPixel\l , r3(6), g3(6), b3(6)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(7), g3(7), b3(7)) : *srcPixel + 4
      getrgb(*srcPixel\l , r3(8), g3(8), b3(8))
      rMax = 0 : gMax = 0 : bMax = 0
      For dir = 0 To 7
        valR = 0 : valG = 0 : valB = 0
        For i = 0 To 8
          valR + r3(i) * mask(dir, i)
          valG + g3(i) * mask(dir, i)
          valB + b3(i) * mask(dir, i)
        Next
        valR = Abs(valR)
        valG = Abs(valG)
        valB = Abs(valB)
        If valR > rMax : rMax = valR : EndIf
        If valG > gMax : gMax = valG : EndIf
        If valB > bMax : bMax = valB : EndIf
      Next
      edge_detection_option(rMax , gMax , bMax)
    Next
  Next
  FreeArray(r3()) : FreeArray(g3()) : FreeArray(b3())
EndProcedure

Procedure Robinson(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "Robinson"
    param\remarque = "Détection 8 directions"
    param\info[0] = "multiply"
    param\info[1] = "math"
    param\info[2] = "Noir et blanc"
    param\info[3] = "inversion"
    param\info[4] = "seuillage : 0 = off"
    param\info[5] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100 : param\info_data(0,2) = 10
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1 : param\info_data(1,2) = 0
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1   : param\info_data(2,2) = 0
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1   : param\info_data(3,2) = 0
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255 : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Robinson_MT() , 5)
EndProcedure

;-------------------------

Procedure Scharr_MT(*param.parametre)
  edge_detection_decalre()
  Protected i, j
  Protected rGx, gGx, bGx, rGy, gGy, bGy
  Protected mat = *param\option[1]
  mul = mul * 0.05
  Protected Dim r3(9)
  Protected Dim g3(9)
  Protected Dim b3(9)
  Protected a, r, g, b
  Protected x, y
  Protected rx, gx, bx, ry, gy, by
  For y = startPos To endPos
    For x = 1 To lg - 2
      *srcPixel = (*source + ((y + -1) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(0) , g3(0) , b3(0) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(1) , g3(1) , b3(1) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(2) , g3(2) , b3(2) )
      *srcPixel = (*source + ((y + 0) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(3) , g3(3) , b3(3) )
      *srcPixel = *srcPixel + 4
      getargb(*srcPixel\l , a , r3(4) , g3(4) , b3(4) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(5) , g3(5) , b3(5) )
      *srcPixel = (*source + ((y + 1) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(6) , g3(6) , b3(6) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(7) , g3(7) , b3(7) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(8) , g3(8) , b3(8) )
      rx = (r3(2)*3 + r3(5)*10 + r3(8)*3) - (r3(0)*3 + r3(3)*10 + r3(6)*3)
      gx = (g3(2)*3 + g3(5)*10 + g3(8)*3) - (g3(0)*3 + g3(3)*10 + g3(6)*3)
      bx = (b3(2)*3 + b3(5)*10 + b3(8)*3) - (b3(0)*3 + b3(3)*10 + b3(6)*3)
      ry = (r3(0)*3 + r3(1)*10 + r3(2)*3) - (r3(6)*3 + r3(7)*10 + r3(8)*3)
      gy = (g3(0)*3 + g3(1)*10 + g3(2)*3) - (g3(6)*3 + g3(7)*10 + g3(8)*3)
      by = (b3(0)*3 + b3(1)*10 + b3(2)*3) - (b3(6)*3 + b3(7)*10 + b3(8)*3)
      If mat = 0
        r = Sqr(rx * rx + ry * ry)
        g = Sqr(gx * gx + gy * gy)
        b = Sqr(bx * bx + by * by)
      Else 
        r = ((Abs(rx) + Abs(ry)))
        g = ((Abs(gx) + Abs(gy)))
        b = ((Abs(bx) + Abs(by)))
      EndIf
      edge_detection_option(r , g , b)
    Next
  Next
  FreeArray(r3())
  FreeArray(g3())
  FreeArray(b3())
EndProcedure

Procedure Scharr(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "Scharr"
    param\remarque = ""
    param\info[0] = "multiply"             
    param\info[1] = "ABS/SQR"            
    param\info[2] = "Noir et blanc"       
    param\info[3] = "inversion"           
    param\info[4] = "seuillage : 0 = off"
    param\info[5] = "Masque binaire"        
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100  : param\info_data(0,2) = 10 ;
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1  : param\info_data(1,2) = 0 
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1  : param\info_data(2,2) = 0 
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1  : param\info_data(3,2) = 0 
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255 : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Scharr_MT() , 5)
EndProcedure

;-------------------------

Macro Scharr_4d_sp1(v0 , v1 , v2 , v3 , v4 , v5 , v6 )
  Protected r#v0 = r3(v1)*3 + r3(v2)*10 + r3(v3)*3 - (r3(v4)*3 + r3(v5)*10 + r3(v6)*3)
  Protected g#v0 = g3(v1)*3 + g3(v2)*10 + g3(v3)*3 - (g3(v4)*3 + g3(v5)*10 + g3(v6)*3)
  Protected b#v0 = b3(v1)*3 + b3(v2)*10 + b3(v3)*3 - (b3(v4)*3 + b3(v5)*10 + b3(v6)*3)
EndMacro

Macro Scharr_4d_sp2(v0)
  r#v0 = Abs(rx#v0) + Abs(ry#v0)
  g#v0 = Abs(gx#v0) + Abs(gy#v0)
  b#v0 = Abs(bx#v0) + Abs(by#v0)
EndMacro

Macro Scharr_4d_sp3(v0)
  r#v0 = Sqr(rx#v0 * rx#v0 + ry#v0 * ry#v0)
  g#v0 = Sqr(gx#v0 * gx#v0 + gy#v0 * gy#v0)
  b#v0 = Sqr(bx#v0 * bx#v0 + by#v0 * by#v0)
EndMacro

Procedure Scharr_4d_MT(*param.parametre)
  edge_detection_decalre()
  Protected mat = *param\option[1]
  Protected r0 , r45 , r90 , r135
  Protected g0 , g45 , g90 , g135
  Protected b0 , b45 , b90 , b135
  mul = mul * 0.1
  Protected x, y, i
  Protected a , r, g, b
  Protected Dim r3(9)
  Protected Dim g3(9)
  Protected Dim b3(9)
  For y = startPos To endPos
    For x = 1 To lg - 2
      *srcPixel = (*source + ((y + -1) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(0) , g3(0) , b3(0) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(1) , g3(1) , b3(1) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(2) , g3(2) , b3(2) )
      *srcPixel = (*source + ((y + 0) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(3) , g3(3) , b3(3) )
      *srcPixel = *srcPixel + 4
      getargb(*srcPixel\l , a , r3(4) , g3(4) , b3(4) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(5) , g3(5) , b3(5) )
      *srcPixel = (*source + ((y + 1) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(6) , g3(6) , b3(6) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(7) , g3(7) , b3(7) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(8) , g3(8) , b3(8) )
      Scharr_4d_sp1(x0 , 2 , 5 , 8 , 0 , 3 , 6)
      Scharr_4d_sp1(y0 , 0 , 1 , 2 , 6 , 7 , 8) 
      Scharr_4d_sp1(x45 , 0 , 1 , 2 , 6 , 7 , 8) 
      Scharr_4d_sp1(y45 , 2 , 5 , 8 , 0 , 3 , 6)
      Scharr_4d_sp1(x90 , 6 , 7 , 8 , 0 , 1 , 2)
      Scharr_4d_sp1(y90 , 0 , 3 , 6 , 2 , 5 , 6) 
      Scharr_4d_sp1(x135 , 6 , 3 , 0 , 8 , 5 , 2)
      Scharr_4d_sp1(y135 , 2 , 5 , 8 , 0 , 3 , 6) 
      If mat
        Scharr_4d_sp2(0) : Scharr_4d_sp2(45) : Scharr_4d_sp2(90) : Scharr_4d_sp2(135)
      Else
        Scharr_4d_sp3(0) : Scharr_4d_sp3(45) : Scharr_4d_sp3(90) : Scharr_4d_sp3(135)
      EndIf
      max4(r , r0 , r45 , r90 , r135)
      max4(g , g0 , g45 , g90 , g135)
      max4(b , b0 , b45 , b90 , b135)
      edge_detection_option(r , g , b)
    Next
  Next
  FreeArray(r3())
  FreeArray(g3())
  FreeArray(b3())
EndProcedure

Procedure Scharr_4d(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "Scharr 4 directions"
    param\remarque = ""
    param\info[0] = "multiply"             
    param\info[1] = "ABS/SQR"            
    param\info[2] = "Noir et blanc"       
    param\info[3] = "inversion"           
    param\info[4] = "seuillage : 0 = off"
    param\info[5] = "Masque binaire"           
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100  : param\info_data(0,2) = 10 ;
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1  : param\info_data(1,2) = 0 
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1  : param\info_data(2,2) = 0 
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1  : param\info_data(3,2) = 0 
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255 : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Scharr_4d_MT() , 5)
EndProcedure

;-------------------------

Procedure Sobel_MT(*param.parametre)
  edge_detection_decalre()
  Protected mat = *param\option[1]
  Protected i, j
  Protected rGx, gGx, bGx, rGy, gGy, bGy
  mul = mul * 0.05
  Protected Dim r3(9)
  Protected Dim g3(9)
  Protected Dim b3(9)
  Protected a, r, g, b
  Protected x, y
  Protected rx, gx, bx, ry, gy, by
  For y = startPos To endPos
    For x = 1 To lg - 2
      *srcPixel = (*source + ((y + -1) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(0) , g3(0) , b3(0) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(1) , g3(1) , b3(1) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(2) , g3(2) , b3(2) )
      *srcPixel = (*source + ((y + 0) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(3) , g3(3) , b3(3) )
      *srcPixel = *srcPixel + 4
      getargb(*srcPixel\l , a , r3(4) , g3(4) , b3(4) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(5) , g3(5) , b3(5) )
      *srcPixel = (*source + ((y + 1) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(6) , g3(6) , b3(6) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(7) , g3(7) , b3(7) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(8) , g3(8) , b3(8) )
      rx = (r3(2) + (r3(5) << 1) + r3(8)) - (r3(0) + (r3(3) << 1) + r3(6))
      gx = (g3(2) + (g3(5) << 1) + g3(8)) - (g3(0) + (g3(3) << 1) + g3(6))
      bx = (b3(2) + (b3(5) << 1) + b3(8)) - (b3(0) + (b3(3) << 1) + b3(6))
      ry = (r3(0) + (r3(1) << 1) + r3(2)) - (r3(6) + (r3(7) << 1) + r3(8))
      gy = (g3(0) + (g3(1) << 1) + g3(2)) - (g3(6) + (g3(7) << 1) + g3(8))
      by = (b3(0) + (b3(1) << 1) + b3(2)) - (b3(6) + (b3(7) << 1) + b3(8))
      If mat = 0
        r = Sqr(rx * rx + ry * ry) 
        g = Sqr(gx * gx + gy * gy) 
        b = Sqr(bx * bx + by * by) 
      Else 
        r = ((Abs(rx) + Abs(ry)) )
        g = ((Abs(gx) + Abs(gy)) )
        b = ((Abs(bx) + Abs(by)) )
      EndIf
      edge_detection_option(r , g , b)
    Next
  Next
  FreeArray(r3())
  FreeArray(g3())
  FreeArray(b3())
EndProcedure

Procedure Sobel(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "Sobel"
    param\remarque = ""
    param\info[0] = "multiply"             
    param\info[1] = "ABS/SQR"            
    param\info[2] = "Noir et blanc"       
    param\info[3] = "inversion"           
    param\info[4] = "seuillage : 0 = off"
    param\info[5] = "Masque binaire"             
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100  : param\info_data(0,2) = 10 ;
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1  : param\info_data(1,2) = 0 
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1  : param\info_data(2,2) = 0 
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1  : param\info_data(3,2) = 0 
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255 : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@sobel_MT() , 5)
EndProcedure

;-------------------------

Macro sobel_4d_sp1(v0 , v1 , v2 , v3 , v4 , v5 , v6 )
  Protected r#v0 = r3(v1) + 2 * r3(v2) + r3(v3) - (r3(v4) + 2 * r3(v5) + r3(v6))
  Protected g#v0 = g3(v1) + 2 * g3(v2) + g3(v3) - (g3(v4) + 2 * g3(v5) + g3(v6))
  Protected b#v0 = b3(v1) + 2 * b3(v2) + b3(v3) - (b3(v4) + 2 * b3(v5) + b3(v6))
EndMacro

Macro sobel_4d_sp2(v0)
  r#v0 = Abs(rx#v0) + Abs(ry#v0)
  g#v0 = Abs(gx#v0) + Abs(gy#v0)
  b#v0 = Abs(bx#v0) + Abs(by#v0)
EndMacro

Macro sobel_4d_sp3(v0)
  r#v0 = Sqr(rx#v0 * rx#v0 + ry#v0 * ry#v0)
  g#v0 = Sqr(gx#v0 * gx#v0 + gy#v0 * gy#v0)
  b#v0 = Sqr(bx#v0 * bx#v0 + by#v0 * by#v0)
EndMacro

Procedure sobel_4d_MT(*param.parametre)
  edge_detection_decalre()
  Protected mat = *param\option[1]
  Protected r0 , r45 , r90 , r135
  Protected g0 , g45 , g90 , g135
  Protected b0 , b45 , b90 , b135
  mul = mul * 0.1
  Protected x, y, i
  Protected a , r, g, b
  Protected Dim r3(9)
  Protected Dim g3(9)
  Protected Dim b3(9)
  For y = startPos To endPos
    For x = 1 To lg - 2
      *srcPixel = (*source + ((y + -1) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(0) , g3(0) , b3(0) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(1) , g3(1) , b3(1) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(2) , g3(2) , b3(2) )
      *srcPixel = (*source + ((y + 0) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(3) , g3(3) , b3(3) )
      *srcPixel = *srcPixel + 4
      getargb(*srcPixel\l , a , r3(4) , g3(4) , b3(4) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(5) , g3(5) , b3(5) )
      *srcPixel = (*source + ((y + 1) * lg + (x + -1)) * 4)
      getrgb(*srcPixel\l , r3(6) , g3(6) , b3(6) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(7) , g3(7) , b3(7) )
      *srcPixel = *srcPixel + 4
      getrgb(*srcPixel\l , r3(8) , g3(8) , b3(8) )
      sobel_4d_sp1(x0 , 2 , 5 , 8 , 0 , 3 , 6)
      sobel_4d_sp1(y0 , 0 , 1 , 2 , 6 , 7 , 8) 
      sobel_4d_sp1(x45 , 0 , 1 , 2 , 6 , 7 , 8) 
      sobel_4d_sp1(y45 , 2 , 5 , 8 , 0 , 3 , 6)
      sobel_4d_sp1(x90 , 6 , 7 , 8 , 0 , 1 , 2)
      sobel_4d_sp1(y90 , 0 , 3 , 6 , 2 , 5 , 6) 
      sobel_4d_sp1(x135 , 6 , 3 , 0 , 8 , 5 , 2)
      sobel_4d_sp1(y135 , 2 , 5 , 8 , 0 , 3 , 6) 
      If mat
        sobel_4d_sp2(0) : sobel_4d_sp2(45) : sobel_4d_sp2(90) : sobel_4d_sp2(135)
      Else
        sobel_4d_sp3(0) : sobel_4d_sp3(45) : sobel_4d_sp3(90) : sobel_4d_sp3(135)
      EndIf
      max4(r , r0 , r45 , r90 , r135)
      max4(g , g0 , g45 , g90 , g135)
      max4(b , b0 , b45 , b90 , b135)
      edge_detection_option(r , g , b)
    Next
  Next
  FreeArray(r3())
  FreeArray(g3())
  FreeArray(b3())
EndProcedure

Procedure Sobel_4d(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "Sobel 4 directions"
    param\remarque = ""
    param\info[0] = "multiply"             
    param\info[1] = "ABS/SQR"            
    param\info[2] = "Noir et blanc"       
    param\info[3] = "inversion"           
    param\info[4] = "seuillage : 0 = off"
    param\info[5] = "Masque binaire"          
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100  : param\info_data(0,2) = 10 ;
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1  : param\info_data(1,2) = 0 
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1  : param\info_data(2,2) = 0 
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1  : param\info_data(3,2) = 0 
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255 : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@sobel_4d_MT() , 5)
EndProcedure

;-------------------------

Procedure CreateGaussianKernel(*kernel, radius, sigma.f)
  Protected x, i, value.f, sum.f = 0.0
  If sigma <= 0.0 : sigma = 1.0 : EndIf
  If radius < 1 : radius = Int(sigma * 2) : EndIf
  Protected div.f = 2.0 * sigma * sigma
  For x = -radius To radius
    value = Exp(-(x * x) / div)
    PokeF(*kernel + (x + radius) * 4, value)
    sum = sum + value
  Next
  If sum = 0.0 : sum = 1.0 : EndIf
  For i = 0 To 2 * radius
    value = PeekF(*kernel + i * 4) / sum
    PokeF(*kernel + i * 4, value)
  Next
EndProcedure

Procedure GaussianBlur1D_MT(*param.parametre)
  Protected Dim kernel.f(64)
  
  Protected *src = *param\addr[2]  
  Protected *dst = *param\addr[3] 
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected sigma.f = *param\option[0]
  Protected radius = Int(sigma * 2)
  Protected horizontal = *param\option[9]
  clamp(radius , 1 , 32)
  CreateGaussianKernel(@kernel(), radius, sigma)
  Protected x, y, k, offset, idx
  Protected a , r , g, b
  Protected sumR.f, sumG.f, sumB.f
  Protected *srcPixel.Pixel32, *dstPixel.Pixel32
  Protected startPos = (*param\thread_pos * ht) / *param\thread_max
  Protected endPos   = ((*param\thread_pos + 1) * ht) / *param\thread_max
  If startPos < 0 : startPos = 0 : EndIf
  If endPos > ht : endPos = ht : EndIf
  For y = startPos To endPos - 1
    For x = 0 To lg - 1
      sumR = 0.0 : sumG = 0.0 : sumB = 0.0
      For k = -radius To radius
        If horizontal
          offset = x + k
          If offset < 0 : offset = 0
          ElseIf offset >= lg : offset = lg - 1
          EndIf
          idx = (y * lg + offset) * 4
        Else
          offset = y + k
          If offset < 0 : offset = 0
          ElseIf offset >= ht : offset = ht - 1
          EndIf
          idx = (offset * lg + x) * 4
        EndIf
        *srcPixel = *src + idx
        getargb(*srcPixel\l, a, r, g, b)
        sumR = sumR + r * kernel(k + radius)
        sumG = sumG + g * kernel(k + radius)
        sumB = sumB + b * kernel(k + radius)
      Next
      *dstPixel = *dst + (y * lg + x) * 4
      *dstPixel\l = (a << 24) | (Int(sumR + 0.5) << 16) | (Int(sumG + 0.5) << 8) | Int(sumB + 0.5)
    Next
  Next
  FreeArray(kernel())
EndProcedure

Procedure DoG_MT(*param.parametre)
  Protected *blur1 = *param\addr[2]
  Protected *blur2 = *param\addr[3]
  Protected *cible = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected math = *param\option[2]
  Protected toGray = *param\option[3]
  Protected inverse = *param\option[4]
  Protected seuillage = *param\option[5]
  Protected multiply = *param\option[6] + 10
  clamp(multiply, 0, 100)
  multiply = multiply * 0.05
  Protected x, y, a
  Protected r1, g1, b1, r2, g2, b2
  Protected *p1.Pixel32, *p2.Pixel32, *dst.Pixel32
  Protected startPos = (*param\thread_pos * ht) / *param\thread_max
  Protected endPos   = ((*param\thread_pos + 1) * ht) / *param\thread_max
  If startPos < 0 : startPos = 0 : EndIf
  If endPos > ht : endPos = ht : EndIf
  For y = startPos To endPos - 1
    For x = 0 To lg - 1
      *p1 = *blur1 + (y * lg + x) * 4
      *p2 = *blur2 + (y * lg + x) * 4
      getargb(*p1\l, a, r1, g1, b1)
      getrgb(*p2\l, r2, g2, b2)
      If Not math
        r1 = Abs(r1 - r2)
        g1 = Abs(g1 - g2)
        b1 = Abs(b1 - b2)
      Else
        r1 = Sqr(Abs(r1 * r1 - r2 * r2))
        g1 = Sqr(Abs(g1 * g1 - g2 * g2))
        b1 = Sqr(Abs(b1 * b1 - b2 * b2))
        clamp_rgb(r1 , g1 , b1)
      EndIf
      r1 * multiply : g1 * multiply : b1 * multiply
      
      If seuillage > 0 : seuil_rgb(seuillage , r1 , g1 , b1) : EndIf
      If toGray : r1 = (r1 * 77 + g1 * 150 + b1 * 29) >> 8 : g1 = r1 : b1 = r1 : EndIf
      If inverse : r1 = 255 - r1 : g1 = 255 - g1 : b1 = 255 - b1 : EndIf
      *dst = *cible + (y * lg + x) * 4
      *dst\l = (a << 24) | (Int(r1) << 16) | (Int(g1) << 8) | Int(b1)
    Next
  Next
EndProcedure

Procedure DoG(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_edge_detection
    param\name = "DoG (programme buggé avec les threads)"
    param\remarque = "Difference of Gaussian"
    param\info[0] = "sigma1"
    param\info[1] = "sigma2"
    param\info[2] = "math (ABS ou SQR)"
    param\info[3] = "Noir et blanc"
    param\info[4] = "inversion"
    param\info[5] = "seuillage : 0 = off"
    param\info[6] = "multiply"
    param\info[7] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 10 : param\info_data(0,2) = 1
    param\info_data(1,0) = 2 : param\info_data(1,1) = 20 : param\info_data(1,2) = 5
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1  : param\info_data(2,2) = 0
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1  : param\info_data(3,2) = 0
    param\info_data(4,0) = 0 : param\info_data(4,1) = 1  : param\info_data(4,2) = 0
    param\info_data(5,0) = 0 : param\info_data(5,1) = 255: param\info_data(5,2) = 0
    param\info_data(6,0) = 0 : param\info_data(6,1) = 100: param\info_data(6,2) = 10
    param\info_data(7,0) = 0 : param\info_data(7,1) = 2   : param\info_data(7,2) = 0
    ProcedureReturn
  EndIf
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  Protected *tempo = 0
  If *param\source = *param\cible
    *tempo = AllocateMemory(*param\lg * *param\ht * 4)
    If Not *tempo
      ProcedureReturn
    EndIf
    CopyMemory(*param\source, *tempo, *param\lg * *param\ht * 4)
    *param\addr[0] = *tempo
  Else
    *param\addr[0] = *param\source
  EndIf
  Protected *blur1 = AllocateMemory(*param\lg * *param\ht * 4)
  Protected *blur2 = AllocateMemory(*param\lg * *param\ht * 4)
  If Not *blur1 Or Not *blur2
    If *tempo : FreeMemory(*tempo) : EndIf
    If *blur1 : FreeMemory(*blur1) : EndIf
    If *blur2 : FreeMemory(*blur2) : EndIf
    ProcedureReturn
  EndIf
  *param\option[0] = *param\option[0] 
  *param\addr[2] = *param\addr[0]
  *param\addr[3] = *blur1
  *param\option[9] = #True
  MultiThread_MT(@GaussianBlur1D_MT())
  *param\addr[2] = *blur1
  *param\addr[3] = *blur1
  *param\option[9] = #False
  MultiThread_MT(@GaussianBlur1D_MT())
  *param\option[0] = *param\option[1] 
  *param\addr[2] = *param\addr[0]
  *param\addr[3] = *blur2
  *param\option[9] = #True
  MultiThread_MT(@GaussianBlur1D_MT())
  *param\addr[2] = *blur2
  *param\addr[3] = *blur2
  *param\option[9] = #False
  MultiThread_MT(@GaussianBlur1D_MT())
  *param\addr[2] = *blur1
  *param\addr[3] = *blur2
  *param\addr[1] = *param\cible
  MultiThread_MT(@DoG_MT())
  If *param\mask And *param\option[7] : *param\mask_type = *param\option[7] - 1 : MultiThread_MT(@_mask()) : EndIf
  If *tempo : FreeMemory(*tempo) : EndIf
  FreeMemory(*blur1)
  FreeMemory(*blur2)
EndProcedure
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

reduction de couleur type dither

dither.pbi

Code : Tout sélectionner

; ------------------------------------------------------------------------------
; Macro : Atkinson_sp
; Description :
;   Diffuse l’erreur de quantification vers un pixel voisin,
;   selon la matrice d'Atkinson (diviseur 8).
; Paramètres :
;   mul = coefficient de pondération (toujours 1 ici)
;   pos = décalage mémoire du pixel cible en octets (<< 2 déjà appliqué)
; ------------------------------------------------------------------------------
Macro Atkinson_sp(mul , pos)
  *dstPixel = *cible + pos                        ; Pointeur vers le pixel cible
  getrgb(*dstPixel\l , r , g , b)                 ; Lecture des composantes RGB
  r + (errR * mul) / 8                            ; Application de l’erreur pondérée
  g + (errG * mul) / 8
  b + (errB * mul) / 8
  clamp_RGB(r, g, b)                              ; Clamp pour rester entre 0 et 255
  *dstPixel\l = a + (r << 16) | (g << 8) | b      ; Réécriture du pixel (alpha préservé)
EndMacro

Procedure AtkinsonDither_MT(*param.parametre)
  Protected *cible  = *param\cible
  Protected lg = *param\lg, ht = *param\ht
  Protected x, y, i
  Protected oldR, oldG, oldB                      ; Couleurs originales
  Protected newR, newG, newB                      ; Couleurs quantifiées
  Protected errR, errG, errB                      ; Erreurs de quantification
  Protected a, r, g, b
  Protected *dstPixel.Pixel32
  Protected ndc = *param\addr[2]                  ; Table de quantification
  
  ; Définition des lignes à traiter (évite les 2 lignes du bas)
  Protected startPos = (*param\thread_pos * (ht - 3)) / *param\thread_max
  Protected endPos   = ((*param\thread_pos + 1) * (ht - 3)) / *param\thread_max
  
  ; Parcours des pixels, évite 2 colonnes à gauche et droite
  For y = startPos To endPos
    For x = 2 To lg - 3
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l , a, oldR , oldG , oldB)
      
      ; Quantification des composantes
      newR = PeekA(ndc + oldR) : newG = PeekA(ndc + oldG) : newB = PeekA(ndc + oldB)
      errR = oldR - newR : errG = oldG - newG : errB = oldB - newB
      
      ; Mise à jour du pixel
      a = a << 24
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB
      
      ; Diffusion de l’erreur selon la matrice Atkinson :
      ; Ligne actuelle
      Atkinson_sp(1,  (( y    * lg + x+1) << 2))    ; x+1
      Atkinson_sp(1,  (( y    * lg + x+2) << 2))    ; x+2
                                                    ; Ligne y+1
      Atkinson_sp(1,  (((y+1) * lg + x-1) << 2))    ; x-1
      Atkinson_sp(1,  (((y+1) * lg + x  ) << 2))    ; x
      Atkinson_sp(1,  (((y+1) * lg + x+1) << 2))    ; x+1
                                                    ; Ligne y+2
      Atkinson_sp(1,  (((y+2) * lg + x  ) << 2))    ; x
    Next
  Next
EndProcedure

Procedure AtkinsonDither(*param.parametre)
  dither(@AtkinsonDither_MT() , "AtkinsonDither")
EndProcedure

;------------------------

Procedure AutoOtsuThreshold_MT(*param.parametre)
  
  Protected *source = *param\source
  Protected *cible  = *param\cible
  Protected *mask   = *param\mask
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected tmax = lg * ht
  Protected i, x, y, r, g, b, lum, var, alpha ,t
  Protected threshold
  
  Dim histo(255)
  Dim buffer(tmax)
  
  ; === Étape 1 : Calcul histogramme de luminance ===
  For i = 0 To tmax - 1
    var = PeekL(*source + i * 4)
    r = (var >> 16) & $FF
    g = (var >> 8)  & $FF
    b = var & $FF
    lum = (r * 54 + g * 183 + b * 18) >> 8  ; Rec.709
    buffer(i) = lum
    histo(lum) + 1
  Next
  
  ; === Étape 2 : Calcul du seuil optimal (Otsu) ===
  Protected total = tmax
  Protected sumAll = 0
  For i = 0 To 255 : sumAll + i * histo(i) : Next
  
  Protected sum = 0, wB = 0, wF, mB.f, mF.f
  Protected maxVar.f = -1.0
  threshold = 0
  
  For t = 0 To 255
    wB + histo(t)
    If wB = 0 : Continue : EndIf
    wF = total - wB
    If wF = 0 : Break : EndIf
    
    sum + t * histo(t)
    mB = sum / wB
    mF = (sumAll - sum) / wF
    
    Protected varBetween.f = wB * wF * (mB - mF) * (mB - mF)
    If varBetween > maxVar
      maxVar = varBetween
      threshold = t
    EndIf
  Next
  
  ; === Étape 3 : Binarisation ===
  For i = 0 To tmax - 1
    If *mask
      alpha = PeekA(*mask + i * 4)
      If alpha < 128
        var = PeekL(*source + i * 4)
        PokeL(*cible + i * 4, var)
        Continue
      EndIf
    EndIf
    
    If buffer(i) > threshold
      PokeL(*cible + i * 4, $FFFFFF)
    Else
      PokeL(*cible + i * 4, 0)
    EndIf
  Next
  
EndProcedure

Procedure AutoOtsuThreshold(*param.parametre)
  ; Affichage des informations si demandé
  If param\info_active
    param\typ = #Filter_Type_Dither
    param\name = "AutoOtsuThreshold"
    param\remarque = "Attention , fonction non threadée"
    param\info[0] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 2  : param\info_data(0,2) = 0
    ProcedureReturn
  EndIf
  Protected *source = *param\source        ; Image source (lecture)
  Protected *cible  = *param\cible         ; Image cible (écriture)
  Protected *mask   = *param\mask          ; Masque éventuel (optionnel)
  Protected lg = *param\lg                 ; Largeur
  Protected ht = *param\ht                 ; Hauteur
  Protected levels = *param\option[0]      ; Niveaux de quantification (1-8)
  Protected i
  ; Vérification de la validité des images source et cible
  If *source = 0 Or *cible = 0 : ProcedureReturn : EndIf
  ; Détection du nombre de cœurs pour le traitement multi-threadé (limité de 1 à 128)
  Protected thread = 1 ; CountCPUs(#PB_System_CPUs)
  Protected Dim tr(thread) ; Tableau des threads (inutile ici car non utilisé ensuite)
                           ; Lancement du traitement Floyd-Steinberg en multi-thread
  MultiThread_MT(@AutoOtsuThreshold_MT())
  ; Si un masque est fourni, appliquer un post-traitement avec masque
  If *param\mask And *param\option[0] : *param\mask_type = *param\option[0] - 1 : MultiThread_MT(@_mask()) : EndIf
  ; Nettoyage mémoire (même si tr() n’est pas utilisé ici)
  FreeArray(tr())
EndProcedure

;------------------------

Macro Burkes_sp(mul , pos)
  *dstPixel = *cible + pos                        ; Pointeur vers le pixel cible
  getrgb(*dstPixel\l , r , g , b)                 ; Lecture des composantes RGB
  r + (errR * mul) >> 5                           ; Application de l’erreur pondérée (division par 32)
  g + (errG * mul) >> 5
  b + (errB * mul) >> 5
  clamp_RGB(r, g, b)                              ; Clamp pour rester entre 0 et 255
  *dstPixel\l = a + (r << 16) | (g << 8) | b      ; Écriture de la nouvelle couleur (alpha inchangé)
EndMacro

Procedure BurkesDither_MT(*param.parametre)
  Protected *cible  = *param\cible                ; Pointeur image destination
  Protected lg = *param\lg, ht = *param\ht        ; Dimensions de l’image
  Protected x, y, i
  Protected oldR, oldG, oldB                      ; Couleurs originales
  Protected newR, newG, newB                      ; Couleurs quantifiées
  Protected errR, errG, errB                      ; Erreur de quantification
  Protected a, r, g, b                            ; Composantes + alpha
  Protected *dstPixel.Pixel32
  Protected ndc = *param\addr[2]                  ; Table de quantification (non-dithered colors)
  
  ; Calcul de la plage de lignes à traiter (évite les bords du bas)
  Protected startPos = (*param\thread_pos * (ht - 2)) / *param\thread_max
  Protected endPos   = ((*param\thread_pos + 1) * (ht - 2)) / *param\thread_max
  
  ; Balayage de l’image, en évitant les 2 colonnes latérales
  For y = startPos To endPos
    For x = 2 To lg - 3
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l , a, oldR , oldG , oldB)
      
      ; Quantification des couleurs via table
      newR = PeekA(ndc + oldR) : newG = PeekA(ndc + oldG) : newB = PeekA(ndc + oldB)
      
      ; Calcul de l’erreur
      errR = oldR - newR : errG = oldG - newG : errB = oldB - newB
      
      ; Mise à jour du pixel courant
      a = a << 24
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB
      
      ; Diffusion de l’erreur selon matrice de Burkes :
      ; Ligne actuelle
      Burkes_sp(8,  (( y    * lg + x+1) << 2))    ; x+1
      Burkes_sp(4,  (( y    * lg + x+2) << 2))    ; x+2
                                                  ; Ligne suivante (y+1)
      Burkes_sp(2,  (((y+1) * lg + x-2) << 2))    ; x-2
      Burkes_sp(4,  (((y+1) * lg + x-1) << 2))    ; x-1
      Burkes_sp(8,  (((y+1) * lg + x  ) << 2))    ; x
      Burkes_sp(4,  (((y+1) * lg + x+1) << 2))    ; x+1
      Burkes_sp(2,  (((y+1) * lg + x+2) << 2))    ; x+2
    Next
  Next
EndProcedure

Procedure BurkesDither(*param.parametre)
  dither(@BurkesDither_MT() , "BurkesDither")
EndProcedure

;------------------------

Macro FloydDither_sp(mul , pos)
  *dstPixel = *cible + pos                         ; Pointeur vers le pixel cible
  getrgb(*dstPixel\l , r , g , b)                  ; Lecture des composantes RGB
  r + (errR * mul) >> 4                            ; Application de l’erreur pondérée (division par 16)
  g + (errG * mul) >> 4
  b + (errB * mul) >> 4
  clamp_RGB(r, g, b)                               ; Clamp pour éviter dépassement de 0-255
  *dstPixel\l = a + (r << 16) | (g << 8) | b       ; Écriture du pixel modifié (avec alpha conservé)
EndMacro

Procedure FloydDither_MT(*param.parametre)
  Protected *cible  = *param\cible        ; Image cible à traiter
  Protected lg = *param\lg                ; Largeur de l'image
  Protected ht = *param\ht                ; Hauteur de l'image
  Protected i, x, y
  Protected oldR, oldG, oldB              ; Valeurs RGB originales
  Protected newR, newG, newB              ; Valeurs RGB quantifiées
  Protected errR, errG, errB              ; Erreur entre original et quantifié
  Protected a, r, g, b                    ; Couleurs et alpha pour traitement
  Protected *dstPixel.Pixel32
  Protected ndc = *param\addr[2]
  ; Calcule les lignes à traiter pour ce thread
  Protected startPos = (*param\thread_pos * (ht - 2)) / *param\thread_max
  Protected endPos   = ((*param\thread_pos + 1) * (ht - 2)) / *param\thread_max
  ; Parcours ligne par ligne, pixel par pixel (sauf bords)
  For y = startPos To endPos
    For x = 1 To lg - 2
      i = y * lg + x
      *dstPixel = *cible + (y * lg + x) << 2
      getargb(*dstPixel\l , a, oldR , oldG , oldB)
      ; Quantification RGB à l'aide de la table
      newR = PeekA(ndc + oldR) : newG = PeekA(ndc + oldG) : newB = PeekA(ndc + oldB)
      errR = oldR - newR : errG = oldG - newG : errB = oldB - newB
      a = a << 24
      ; Mise à jour du pixel courant avec valeur quantifiée
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB
      ; Diffusion de l'erreur vers les 4 voisins selon Floyd-Steinberg
      FloydDither_sp( 7 , ((y * lg + (x + 1)) << 2) )         ; Droite
      FloydDither_sp( 3 , (((y + 1) * lg + (x - 1)) << 2) )   ; Bas-gauche
      FloydDither_sp( 5 , (((y + 1) * lg + x) << 2) )         ; Bas
      FloydDither_sp( 1 , (((y + 1) * lg + (x + 1)) << 2) )   ; Bas-droite
    Next
  Next
EndProcedure

Procedure FloydDither(*param.parametre)
  dither(@FloydDither_MT() , "FloydDither")
EndProcedure

;------------------------

Macro JJN_sp(mul , pos)
  *dstPixel = *cible + pos                        ; Pointeur vers le pixel cible
  getrgb(*dstPixel\l , r , g , b)                 ; Lecture des composantes RGB
  r + (errR * mul) / 48                           ; Application de l’erreur pondérée
  g + (errG * mul) / 48
  b + (errB * mul) / 48
  clamp_RGB(r, g, b)                              ; Clamp pour rester dans [0..255]
  *dstPixel\l = a + (r << 16) | (g << 8) | b      ; Réécriture du pixel (alpha préservé)
EndMacro

Procedure JJNDither_MT(*param.parametre)
  Protected *cible  = *param\cible
  Protected lg = *param\lg, ht = *param\ht
  Protected x, y, i
  Protected oldR, oldG, oldB                      ; Couleurs originales
  Protected newR, newG, newB                      ; Couleurs quantifiées
  Protected errR, errG, errB                      ; Erreurs de quantification
  Protected a, r, g, b
  Protected *dstPixel.Pixel32
  Protected ndc = *param\addr[2]                  ; Table de quantification
  
  ; Définition des lignes à traiter (évite les 2 lignes du bas)
  Protected startPos = (*param\thread_pos * (ht - 3)) / *param\thread_max
  Protected endPos   = ((*param\thread_pos + 1) * (ht - 3)) / *param\thread_max
  
  ; Parcours des pixels, évite 2 colonnes à gauche et droite
  For y = startPos To endPos
    For x = 2 To lg - 3
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l , a, oldR , oldG , oldB)
      
      ; Quantification des composantes
      newR = PeekA(ndc + oldR) : newG = PeekA(ndc + oldG) : newB = PeekA(ndc + oldB)
      errR = oldR - newR : errG = oldG - newG : errB = oldB - newB
      
      ; Mise à jour du pixel
      a = a << 24
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB
      
      ; Diffusion de l’erreur selon la matrice JJN (2 lignes)
      ; Ligne actuelle
      JJN_sp(7,  (( y    * lg + x+1) << 2))
      JJN_sp(5,  (( y    * lg + x+2) << 2))
      ; Ligne y+1
      JJN_sp(3,  (((y+1) * lg + x-2) << 2))
      JJN_sp(5,  (((y+1) * lg + x-1) << 2))
      JJN_sp(7,  (((y+1) * lg + x  ) << 2))
      JJN_sp(5,  (((y+1) * lg + x+1) << 2))
      JJN_sp(3,  (((y+1) * lg + x+2) << 2))
      ; Ligne y+2
      JJN_sp(1,  (((y+2) * lg + x-2) << 2))
      JJN_sp(3,  (((y+2) * lg + x-1) << 2))
      JJN_sp(5,  (((y+2) * lg + x  ) << 2))
      JJN_sp(3,  (((y+2) * lg + x+1) << 2))
      JJN_sp(1,  (((y+2) * lg + x+2) << 2))
    Next
  Next
EndProcedure

Procedure JJNDither(*param.parametre)
  dither(@JJNDither_MT() , "JJNDither")
EndProcedure

;------------------------

Procedure RandomDither_MT(*param.parametre)
  Protected *cible = *param\cible
  Protected lg = *param\lg, ht = *param\ht
  Protected x, y, i
  Protected oldR, oldG, oldB
  Protected newR, newG, newB
  Protected a, r, g, b
  Protected *dstPixel.Pixel32
  Protected ndc = *param\addr[2]
  
  ; Evite bordures (pas obligatoire ici mais mieux)
  Protected startPos = (*param\thread_pos * ht) / *param\thread_max
  Protected endPos = ((*param\thread_pos + 1) * ht) / *param\thread_max
  
  For y = startPos To endPos - 1
    For x = 0 To lg - 1
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l, a, oldR, oldG, oldB)
      
      ; Ajout bruit aléatoire dans [-0.5, +0.5] sur [0..255]
      ; On ajuste amplitude du bruit selon quantification
      Protected noiseR = Random(255) - 128
      Protected noiseG = Random(255) - 128
      Protected noiseB = Random(255) - 128
      
      ; Ajuste la valeur avec bruit avant quantification
      r = oldR + noiseR >> 3
      g = oldG + noiseG >> 3
      b = oldB + noiseB >> 3
      clamp_rgb(r,g,b)
      
      ; Quantification avec table ndc
      newR = PeekA(ndc + r)
      newG = PeekA(ndc + g)
      newB = PeekA(ndc + b)
      
      a = a << 24
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB
    Next
  Next
EndProcedure

Procedure RandomDither(*param.parametre)
  dither(@RandomDither_MT() , "RandomDither")
EndProcedure

;------------------------

Macro Sierra_sp(mul , pos)
  *dstPixel = *cible + pos                        ; Pointeur pixel cible
  getrgb(*dstPixel\l , r , g , b)                 ; Lecture composantes RGB
  r + (errR * mul) / 32                           ; Application erreur pondérée
  g + (errG * mul) / 32
  b + (errB * mul) / 32
  clamp_RGB(r, g, b)                             ; Clamp [0..255]
  *dstPixel\l = a + (r << 16) | (g << 8) | b     ; Réécriture pixel (alpha préservé)
EndMacro

Procedure SierraDither_MT(*param.parametre)
  Protected *cible = *param\cible
  Protected lg = *param\lg, ht = *param\ht
  Protected x, y, i
  Protected oldR, oldG, oldB
  Protected newR, newG, newB
  Protected errR, errG, errB
  Protected a, r, g, b
  Protected *dstPixel.Pixel32
  Protected ndc = *param\addr[2]
  
  ; Eviter bord bas et colonnes extrêmes (bordures)
  Protected startPos = (*param\thread_pos * (ht - 3)) / *param\thread_max
  Protected endPos = ((*param\thread_pos + 1) * (ht - 3)) / *param\thread_max
  
  For y = startPos To endPos
    For x = 2 To lg - 3
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l, a, oldR, oldG, oldB)
      
      ; Quantification des composantes
      newR = PeekA(ndc + oldR) : newG = PeekA(ndc + oldG) : newB = PeekA(ndc + oldB)
      
      ; Calcul erreur
      errR = oldR - newR : errG = oldG - newG : errB = oldB - newB
      
      ; Mise à jour pixel
      a = a << 24
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB
      
      ; Diffusion erreur selon matrice Sierra
      ; Ligne y
      Sierra_sp(5,  ((y    * lg + x+1) << 2))
      Sierra_sp(3,  ((y    * lg + x+2) << 2))
      ; Ligne y+1
      Sierra_sp(2,  (((y+1) * lg + x-2) << 2))
      Sierra_sp(4,  (((y+1) * lg + x-1) << 2))
      Sierra_sp(5,  (((y+1) * lg + x  ) << 2))
      Sierra_sp(4,  (((y+1) * lg + x+1) << 2))
      Sierra_sp(2,  (((y+1) * lg + x+2) << 2))
      ; Ligne y+2
      Sierra_sp(2,  (((y+2) * lg + x-1) << 2))
      Sierra_sp(3,  (((y+2) * lg + x  ) << 2))
      Sierra_sp(2,  (((y+2) * lg + x+1) << 2))
    Next
  Next
EndProcedure

Procedure SierraDither(*param.parametre)
  dither(@SierraDither_MT() , "SierraDither")
EndProcedure

;------------------------

Macro SierraLite_sp(mul , pos)
  *dstPixel = *cible + pos                        ; Pointeur vers le pixel cible
  getrgb(*dstPixel\l , r , g , b)                 ; Lecture RGB
  r + (errR * mul) / 16                           ; Application erreur pondérée
  g + (errG * mul) / 16
  b + (errB * mul) / 16
  clamp_RGB(r, g, b)                              ; Clamp [0..255]
  *dstPixel\l = a + (r << 16) | (g << 8) | b      ; Réécriture pixel (alpha conservé)
EndMacro

Procedure SierraLiteDither_MT(*param.parametre)
  Protected *cible = *param\cible
  Protected lg = *param\lg, ht = *param\ht
  Protected x, y, i
  Protected oldR, oldG, oldB
  Protected newR, newG, newB
  Protected errR, errG, errB
  Protected a, r, g, b
  Protected *dstPixel.Pixel32
  Protected ndc = *param\addr[2]  ; Table de quantification
  
  ; Définir plage lignes à traiter (éviter bord bas)
  Protected startPos = (*param\thread_pos * (ht - 2)) / *param\thread_max
  Protected endPos   = ((*param\thread_pos + 1) * (ht - 2)) / *param\thread_max
  
  ; Parcours pixels (éviter 2 colonnes bord gauche/droite)
  For y = startPos To endPos
    For x = 2 To lg - 3
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l, a, oldR, oldG, oldB)
      
      ; Quantification
      newR = PeekA(ndc + oldR) : newG = PeekA(ndc + oldG) : newB = PeekA(ndc + oldB)
      
      ; Calcul erreur
      errR = oldR - newR : errG = oldG - newG : errB = oldB - newB
      
      ; Mise à jour pixel
      a = a << 24
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB
      
      ; Diffusion erreur Sierra Lite
      SierraLite_sp(2, ((y    * lg + x+1) << 2))    ; x+1, y
      SierraLite_sp(1, ((y    * lg + x+2) << 2))    ; x+2, y
      SierraLite_sp(1, (((y+1) * lg + x-1) << 2))   ; x-1, y+1
      SierraLite_sp(1, (((y+1) * lg + x  ) << 2))   ; x, y+1
    Next
  Next
EndProcedure

Procedure SierraLiteDither(*param.parametre)
  dither(@SierraLiteDither_MT() , "SierraLiteDither")
EndProcedure

;------------------------

Macro Stucki_sp(mul , pos)
  *dstPixel = *cible + pos                           ; Pointeur vers le pixel voisin
  getrgb(*dstPixel\l , r , g , b)                    ; Lecture des composantes RGB
  r + (errR * mul) / 42                              ; Application de l’erreur pondérée
  g + (errG * mul) / 42
  b + (errB * mul) / 42
  clamp_RGB(r, g, b)                                 ; Clamp pour rester dans [0..255]
  *dstPixel\l = a + (r << 16) | (g << 8) | b         ; Écriture du pixel corrigé
EndMacro

Procedure StuckiDither_MT(*param.parametre)
  Protected *cible  = *param\cible        ; Image cible à traiter
  Protected lg = *param\lg                ; Largeur de l'image
  Protected ht = *param\ht                ; Hauteur de l'image
  Protected i, x, y
  Protected oldR, oldG, oldB              ; Composantes originales du pixel
  Protected newR, newG, newB              ; Composantes quantifiées
  Protected errR, errG, errB              ; Erreur de quantification
  Protected a, r, g, b                    ; Couleurs et alpha
  Protected *dstPixel.Pixel32
  Protected ndc = *param\addr[2]          ; Table de quantification
  
  ; Calcule la plage de lignes à traiter pour ce thread (évite les bords)
  Protected startPos = (*param\thread_pos * (ht - 3)) / *param\thread_max
  Protected endPos   = ((*param\thread_pos + 1) * (ht - 3)) / *param\thread_max
  
  ; Parcours ligne par ligne et pixel par pixel (en évitant les bords horizontaux)
  For y = startPos To endPos
    For x = 2 To lg - 3
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l , a, oldR , oldG , oldB)
      newR = PeekA(ndc + oldR) : newG = PeekA(ndc + oldG) : newB = PeekA(ndc + oldB)
      errR = oldR - newR : errG = oldG - newG : errB = oldB - newB
      a = a << 24
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB
      
      ; Diffusion de l'erreur vers les 12 voisins selon la matrice Stucki
      Stucki_sp(8,  ((y   * lg + x+1) << 2)) ; droite
      Stucki_sp(4,  ((y   * lg + x+2) << 2)) ; droite+1
      Stucki_sp(2,  (((y+1) * lg + x-2) << 2)) ; ligne+1 gauche+2
      Stucki_sp(4,  (((y+1) * lg + x-1) << 2)) ; ligne+1 gauche+1
      Stucki_sp(8,  (((y+1) * lg + x  ) << 2)) ; ligne+1 centre
      Stucki_sp(4,  (((y+1) * lg + x+1) << 2)) ; ligne+1 droite+1
      Stucki_sp(2,  (((y+1) * lg + x+2) << 2)) ; ligne+1 droite+2
      Stucki_sp(1,  (((y+2) * lg + x-2) << 2)) ; ligne+2 gauche+2
      Stucki_sp(2,  (((y+2) * lg + x-1) << 2)) ; ligne+2 gauche+1
      Stucki_sp(4,  (((y+2) * lg + x  ) << 2)) ; ligne+2 centre
      Stucki_sp(2,  (((y+2) * lg + x+1) << 2)) ; ligne+2 droite+1
      Stucki_sp(1,  (((y+2) * lg + x+2) << 2)) ; ligne+2 droite+2
    Next
  Next
EndProcedure

Procedure StuckiDither(*param.parametre)
  dither(@StuckiDither_MT() , "StuckiDither")
EndProcedure

;------------------------

Macro BayerThreshold(x, y, levels)
  Bayer4x4((y) & 3, (x) & 3) * 255 / (levels - 1)
EndMacro

Procedure BayerDither_MT(*param.parametre)
  
  Protected Dim Bayer4x4(3,3)
  Bayer4x4(0,0) =  0 : Bayer4x4(0,1) =  8 : Bayer4x4(0,2) =  2 : Bayer4x4(0,3) = 10
  Bayer4x4(1,0) = 12 : Bayer4x4(1,1) =  4 : Bayer4x4(1,2) = 14 : Bayer4x4(1,3) =  6
  Bayer4x4(2,0) =  3 : Bayer4x4(2,1) = 11 : Bayer4x4(2,2) =  1 : Bayer4x4(2,3) =  9
  Bayer4x4(3,0) = 15 : Bayer4x4(3,1) =  7 : Bayer4x4(3,2) = 13 : Bayer4x4(3,3) =  5
  
  Protected *cible = *param\cible
  Protected lg = *param\lg, ht = *param\ht
  Protected x, y, i
  Protected r, g, b, a, gray
  Protected *dstPixel.Pixel32
  Protected levels = *param\option[0]
  Protected *ndc = *param\addr[2]   ; LUT déjà préparée
  
  If levels < 2 : levels = 2 : EndIf
  Protected Steping.f = 255.0 / (levels - 1) ; taille d’un pas (float)
  Protected offset.f, rf.f, gf.f, bf.f
  
  For y = 0 To ht - 1
    For x = 0 To lg - 1
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l, a, r, g, b)
      
      ; offset normalisé = ((BayerValue + 0.5)/16 - 0.5) * Steping
      ; => range ≈ [-0.5*Steping, +0.5*Steping)
      offset = ((Bayer4x4((y) & 3, (x) & 3) + 0.5) * Steping / 16.0) - (Steping / 2.0)
      
      If *param\option[1] ; N&B : travailler sur la luminance
        gray = (r * 54 + g * 183 + b * 18) >> 8
        gray = gray + offset
        If gray < 0 : gray = 0 : EndIf
        If gray > 255 : gray = 255 : EndIf
        ; quantification via LUT
        r = PeekA(*ndc + Int(gray + 0.5))
        g = r
        b = r
      Else
        ; application par canal (offset identique pour les 3 canaux)
        rf = r + offset : gf = g + offset : bf = b + offset
        If rf < 0 : rf = 0 : EndIf
        If rf > 255 : rf = 255 : EndIf
        If gf < 0 : gf = 0 : EndIf
        If gf > 255 : gf = 255 : EndIf
        If bf < 0 : bf = 0 : EndIf
        If bf > 255 : bf = 255 : EndIf
        
        r = PeekA(*ndc + Int(rf + 0.5))
        g = PeekA(*ndc + Int(gf + 0.5))
        b = PeekA(*ndc + Int(bf + 0.5))
      EndIf
      
      a = a << 24
      *dstPixel\l = a | (r << 16) | (g << 8) | b
    Next
  Next
EndProcedure

Procedure BayerDither(*param.parametre)
  dither(@BayerDither_MT(), "BayerDither")
EndProcedure

;------------------------

Macro SF_sp(mul, xx, yy)
  If xx >= 0 And xx < lg And yy >= 0 And yy < ht
    *dstPixel = *cible + ((yy * lg + xx) << 2)
    getrgb(*dstPixel\l, r, g, b)
    r + (errR * mul) / 32
    g + (errG * mul) / 32
    b + (errB * mul) / 32
    clamp_RGB(r, g, b)
    *dstPixel\l = a + (r << 16) | (g << 8) | b
  EndIf
EndMacro

Procedure ShiauFanDither_MT(*param.parametre)
  Protected *cible = *param\cible
  Protected lg = *param\lg, ht = *param\ht
  Protected x, y, i
  Protected oldR, oldG, oldB
  Protected newR, newG, newB
  Protected errR, errG, errB
  Protected a, r, g, b
  Protected *dstPixel.Pixel32
  Protected *ndc = *param\addr[2]
  
  For y = 0 To ht - 1
    For x = 0 To lg - 1
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l, a, oldR, oldG, oldB)
      
      ; Quantification via LUT
      newR = PeekA(*ndc + oldR)
      newG = PeekA(*ndc + oldG)
      newB = PeekA(*ndc + oldB)
      
      errR = oldR - newR
      errG = oldG - newG
      errB = oldB - newB
      
      a = a << 24
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB
      
      ; Diffusion selon Shiau–Fan
      ; Ligne y
      SF_sp(8, x+1, y)
      SF_sp(4, x+2, y)
      
      ; Ligne y+1
      SF_sp(2, x-2, y+1)
      SF_sp(4, x-1, y+1)
      SF_sp(8, x,   y+1)
      SF_sp(4, x+1, y+1)
      SF_sp(2, x+2, y+1)
    Next
  Next
EndProcedure

Procedure ShiauFanDither(*param.parametre)
  dither(@ShiauFanDither_MT(), "ShiauFanDither")
EndProcedure

;------------------------

Macro Kite_sp(mul, xx, yy)
  If xx >= 0 And xx < lg And yy >= 0 And yy < ht
    *dstPixel = *cible + ((yy * lg + xx) << 2)
    getrgb(*dstPixel\l, r, g, b)
    r + (errR * mul) / 32
    g + (errG * mul) / 32
    b + (errB * mul) / 32
    clamp_RGB(r, g, b)
    *dstPixel\l = a + (r << 16) | (g << 8) | b
  EndIf
EndMacro

Procedure KiteDither_MT(*param.parametre)
  Protected *cible = *param\cible
  Protected lg = *param\lg, ht = *param\ht
  Protected x, y, i
  Protected oldR, oldG, oldB
  Protected newR, newG, newB
  Protected errR, errG, errB
  Protected a, r, g, b
  Protected *dstPixel.Pixel32
  Protected *ndc = *param\addr[2]

  For y = 0 To ht - 1
    For x = 0 To lg - 1
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l, a, oldR, oldG, oldB)

      ; Quantification via LUT
      newR = PeekA(*ndc + oldR)
      newG = PeekA(*ndc + oldG)
      newB = PeekA(*ndc + oldB)

      errR = oldR - newR
      errG = oldG - newG
      errB = oldB - newB

      a = a << 24
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB

      ; Diffusion Kite (cerf-volant)
      ; Ligne y
      Kite_sp(7, x+1, y)
      Kite_sp(5, x+2, y)
      ; Ligne y+1
      Kite_sp(3, x-2, y+1)
      Kite_sp(5, x-1, y+1)
      Kite_sp(7, x,   y+1)
      Kite_sp(5, x+1, y+1)
      Kite_sp(3, x+2, y+1)
    Next
  Next
EndProcedure

Procedure KiteDither(*param.parametre)
  dither(@KiteDither_MT(), "KiteDither")
EndProcedure

;------------------------

Macro FL_sp(mul, xx, yy)
  If xx >= 0 And xx < lg And yy >= 0 And yy < ht
    *dstPixel = *cible + ((yy * lg + xx) << 2)
    getrgb(*dstPixel\l, r, g, b)
    r + (errR * mul) / 2
    g + (errG * mul) / 2
    b + (errB * mul) / 2
    clamp_RGB(r, g, b)
    *dstPixel\l = a + (r << 16) | (g << 8) | b
  EndIf
EndMacro

Procedure FilterLiteDither_MT(*param.parametre)
  Protected *cible = *param\cible
  Protected lg = *param\lg, ht = *param\ht
  Protected x, y, i
  Protected oldR, oldG, oldB
  Protected newR, newG, newB
  Protected errR, errG, errB
  Protected a, r, g, b
  Protected *dstPixel.Pixel32
  Protected *ndc = *param\addr[2]

  For y = 0 To ht - 1
    For x = 0 To lg - 1
      i = y * lg + x
      *dstPixel = *cible + (i << 2)
      getargb(*dstPixel\l, a, oldR, oldG, oldB)

      ; Quantification via LUT
      newR = PeekA(*ndc + oldR)
      newG = PeekA(*ndc + oldG)
      newB = PeekA(*ndc + oldB)

      errR = oldR - newR
      errG = oldG - newG
      errB = oldB - newB

      a = a << 24
      *dstPixel\l = a + (newR << 16) | (newG << 8) | newB

      ; Diffusion minimaliste FilterLite
      FL_sp(1, x+1, y)   ; pixel de droite
      FL_sp(1, x,   y+1) ; pixel dessous
    Next
  Next
EndProcedure

Procedure LiteDither(*param.parametre)
  dither(@FilterLiteDither_MT(), "LiteDither")
EndProcedure



Avatar de l’utilisateur
Jacobus
Messages : 1575
Inscription : mar. 06/avr./2004 10:35
Contact :

Re: filtre graphique

Message par Jacobus »

Bonjour,
As-tu testé les codes que tu proposes ?
J'ai tenté de tester avec PB6.21 32 bit sur Windows11pro
Beaucoup d'erreurs, accès mémoire invalides par exemple sur "ProcedureReturn" notamment, ou comparaison du type: >>32 impossible, de 0 à 31 uniquement.
Si le projet a l'air intéressant, c'est dommage de ne pouvoir tester sans d'abord passer son temps à déboguer.
Je passe.
Quand tous les glands seront tombés, les feuilles dispersées, la vigueur retombée... Dans la morne solitude, ancré au coeur de ses racines, c'est de sa force maturité qu'il renaîtra en pleine magnificence...Jacobus.
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

bonjour ,
je n'ai pas testé mon programme en 32bits , seulement en 64bits.
je regarde ca.
Avatar de l’utilisateur
Jacobus
Messages : 1575
Inscription : mar. 06/avr./2004 10:35
Contact :

Re: filtre graphique

Message par Jacobus »

Je viens d'essayer en 64 biroutes et ça fonctionne.
Je trouve très positif : l'application des filtres à l'image, le nombre de filtres et les possibilités offertes, les fenêtres d'outils des filtres,
Par contre : Pas très intuitif pour appliquer les modifs à l'image, à la 1 ou la 2, comment fait-on ? Comment sauvegarder ? Ce n'est pas très fluide, l'application des filtres est saccadée et pas instantané.
En général : je vais suivre avec intérêt ton application qui promet d'être quelque chose de top en matière de traitement image, d'autant si tout ce qui est commenté vient s'y ajouter :)
Merci et bonne continuation.
Quand tous les glands seront tombés, les feuilles dispersées, la vigueur retombée... Dans la morne solitude, ancré au coeur de ses racines, c'est de sa force maturité qu'il renaîtra en pleine magnificence...Jacobus.
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

oui , le programme n'est pas du tout intuitif , à la base je ne modifiais qu'une image , puis de nouvelles options ont ete ajoutees, je me suis concentré sur les filtres , pas sur la programme.
ce n'est qu'une demo,elle ne me sert que pour pour tester les filtres.( je prefere travailler avec l'image taille reelle)
pour que le programme soit plus rapide il faudrait travailler avec des echantillons d'image .
l'application est sacadee car le traitement d'image est tres gourmant en resource CPU , utiliser des image de petites taile pour le moment .
pour sauvegarder ; allez dans "file \ save BMP" , apres avoir modifie votre image
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

les filtres "couleur"

couleur.pbi

Code : Tout sélectionner

Procedure Balance_MT(*p.parametre)
  Protected i, pixel.l, a.l, r.l, g.l, b.l
  Protected factorR = *p\option[0]
  Protected factorG = *p\option[1]
  Protected factorB = *p\option[2]
  Protected totalPixels = *p\lg * *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    pixel = *srcPixel\l
    GetARGB(pixel, a, r, g, b)
    r = (factorR * r) >> 8
    g = (factorG * g) >> 8
    b = (factorB * b) >> 8
    Clamp_RGB(r, g, b)
    *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
  Next
EndProcedure

Procedure Balance(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Balance"
    param\remarque = ""
    param\info[0] = "Rouge (0-255)"
    param\info[1] = "Vert (0-255)"
    param\info[2] = "Bleu (0-255)"
    param\info[3] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 512 : param\info_data(0,2) = 255
    param\info_data(1,0) = 1 : param\info_data(1,1) = 512 : param\info_data(1,2) = 255
    param\info_data(2,0) = 1 : param\info_data(2,1) = 512 : param\info_data(2,2) = 255
    param\info_data(3,0) = 0 : param\info_data(3,1) = 2  : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
filter_start(@Balance_MT() , 3)
EndProcedure

;-------------------------

Procedure Bend_MT(*p.parametre)
  Protected i, pixel.l, a.l, r.l, g.l, b.l
  Protected totalPixels = *p\lg * *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected r1.f = (*p\option[0] - 180) / 255.0 * #PI / 180.0
  Protected g1.f = (*p\option[1] - 180) / 255.0 * #PI / 180.0
  Protected b1.f = (*p\option[2] - 180) / 255.0 * #PI / 180.0
  Protected tabr = *p\addr[3]
  Protected tabg = *p\addr[4]
  Protected tabb = *p\addr[5]
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    pixel = *srcPixel\l
    GetARGB(pixel, a, r, g, b)
    r = PeekA(tabr + r) 
    g = PeekA(tabg + g) 
    b = PeekA(tabb + b) 
    *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
  Next
EndProcedure

Procedure Bend(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Bend"
    param\remarque = "Distorsion RGB"
    param\info[0] = "Angle Rouge"
    param\info[1] = "Angle Vert"
    param\info[2] = "Angle Bleu"
    param\info[3] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 512 : param\info_data(0,2) = 255
    param\info_data(1,0) = 1 : param\info_data(1,1) = 512 : param\info_data(1,2) = 255
    param\info_data(2,0) = 1 : param\info_data(2,1) = 512 : param\info_data(2,2) = 255
    param\info_data(3,0) = 0 : param\info_data(3,1) = 2  : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  Protected *source = *param\source
  Protected *cible  = *param\cible
  Protected i
  If *source = 0 Or *cible = 0 : ProcedureReturn : EndIf
  Protected r1.f = (*param\option[0] - 180) / 255.0 * #PI / 180.0
  Protected g1.f = (*param\option[1] - 180) / 255.0 * #PI / 180.0
  Protected b1.f = (*param\option[2] - 180) / 255.0 * #PI / 180.0
  Protected tabr = AllocateMemory(255)
  Protected tabg = AllocateMemory(255)
  Protected tabb = AllocateMemory(255)
  Protected r , g , b
  For i = 0 To 255
    r = Sin(i * r1) * 127 + i
    g = Sin(i * g1) * 127 + i
    b = Sin(i * b1) * 127 + i
    Clamp_RGB(r, g, b)
    PokeA(tabr + i , r)
    PokeA(tabg + i , g)
    PokeA(tabb + i , b)
  Next
  param\addr[3] = tabr
  param\addr[4] = tabg
  param\addr[5] = tabb
  filter_start(@Bend_MT() , 3)
  FreeMemory(tabr)
  FreeMemory(tabg)
  FreeMemory(tabb)
EndProcedure

;-------------------------

Procedure BlackAndWhite_MT(*param.parametre)   
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected seuil = *param\option[0]
  Protected option = *param\option[1]
  Protected i , t , lum , l1 , l2
  Protected var , a , r , g , b
  t = (lg * ht * 4)
  Protected start = (*param\thread_pos * t) / *param\thread_max
  Protected stop = ((*param\thread_pos + 1) * t) / *param\thread_max - 1
  For i = start To stop-1 Step 4
    var = PeekL(*param\addr[0] + i)
    getargb(var , a , r , g , b)
    Select option
      Case 1 
        lum = (R * 77 + G * 150 + B * 29) >> 8
      Case 2
        lum = (R * 54 + G * 183 + B * 18) >> 8
      Case 3
        max(lum,r,g)
        max(lum,lum,b)
      Case 4 
        min(lum,r,g)
        min(lum,lum,b)
      Case 5 
        l1 = g
        If (r > l1) : Swap r, l1 : EndIf
        If (l1 > b) : Swap l1, b : EndIf
        If (r > l1) : Swap r, l1 : EndIf
        lum = l1
      Case 6
        min(l1,r,g)
        min(l1,l1,b)
        max(l2,r,g)
        max(l2,l2,b)
        lum = (l1 + l2) * 0.5
      Case 7
        lum = r
      Case 8
        lum = g
      Case 9
        lum = b
      Default 
        lum = ((r + g + b) * 85)>> 8
    EndSelect
    If lum > seuil : PokeL(*param\addr[1] + i,a << 24 | $ffffff) : Else : PokeL(*param\addr[1] + i,a << 24) : EndIf
  Next      
EndProcedure

Procedure BlackAndWhite(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "BlackAndWhite"
    param\remarque = ""
    param\info[0] = "seuil"
    param\info[1] = "type" ; Rayon vertical
    param\info[2] = "Masque binaire" ; Optionnel : appliquer un masque
    param\info_data(0,0) = 1 : param\info_data(0,1) = 254 : param\info_data(0,2) = 127
    param\info_data(1,0) = 0 : param\info_data(1,1) = 9   : param\info_data(1,2) = 0
    param\info_data(2,0) = 0 : param\info_data(2,1) = 2   : param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@BlackAndWhite_MT() , 2)
EndProcedure

;-------------------------

Procedure Brightness_MT(*p.parametre)
  Protected i, a, r, g, b
  Protected totalPixels = *p\lg * *p\ht
  Protected sr = *p\option[0] - 255
  Protected sg = *p\option[1] - 255
  Protected sb = *p\option[2] - 255
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    GetARGB(*srcPixel\l, a, r, g, b)
    r + sr
    g + sg
    b + sb
    Clamp_RGB(r, g, b)
    *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
  Next
EndProcedure

Procedure Brightness(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Brightness"
    param\remarque = ""
    param\info[0] = "ajustement Rouge"
    param\info[1] = "ajustement Vert"
    param\info[2] = "ajustement Bleu"
    param\info[3] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 512 : param\info_data(0,2) = 255
    param\info_data(1,0) = 1 : param\info_data(1,1) = 512 : param\info_data(1,2) = 255
    param\info_data(2,0) = 1 : param\info_data(2,1) = 512 : param\info_data(2,2) = 255
    param\info_data(3,0) = 0 : param\info_data(3,1) = 2  : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Brightness_MT() , 3)
EndProcedure

;-------------------------

Macro Color_Gray()
  var =  ((R * 54 + G * 183 + B * 18) >> 8 )  * $10101
EndMacro

Procedure Color_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected maxVal , minVal , saturation
  Protected deltaRG , deltaRB , deltaGB 
  
  Protected seuil = *param\option[0]
  Protected mode = *param\option[1]
 
  Protected i, totalPixels = lg * ht * 4
  Protected var, r, g, b
  
  Protected startPos = (*param\thread_pos * totalPixels) / *param\thread_max
  Protected endPos = ((*param\thread_pos + 1) * totalPixels) / *param\thread_max
  
  For i = startPos To endPos - 1 Step 4
    var = PeekL(*source + i)
    getrgb(var, r, g, b)
    
    Select mode
        Case 0 :  If (g > r Or b > r Or r > seuil) : Color_Gray() : EndIf         ; Red OR >
        Case 1 :  If (r > g Or b > g Or g > seuil) : Color_Gray() : EndIf        ; Green OR >
        Case 2 :  If (g > b Or r > b Or b > seuil) : Color_Gray() : EndIf         ; Blue OR >
        
        Case 3 :  If (r < g Or r < b Or r > seuil) : Color_Gray() : EndIf         ; Red OR <
        Case 4 :  If (g < r Or g < b Or g > seuil) : Color_Gray() : EndIf         ; Green OR <
        Case 5 :  If (b < r Or b < g Or b > seuil) : Color_Gray() : EndIf          ; Blue OR <
        
        Case 6 :  If ((g > r And b > r) Or r > seuil) : Color_Gray() : EndIf       ; Red AND >
        Case 7 :  If ((r > g And b > g) Or g > seuil) : Color_Gray() : EndIf       ; Green AND >
        Case 8 :  If ((g > b And r > b) Or b > seuil) : Color_Gray() : EndIf      ; Blue AND >
        
        Case 9 :  If ((r < g And r < b) Or r > seuil) : Color_Gray() : EndIf       ; Red AND <
        Case 10:  If ((g < r And g < b) Or g > seuil) : Color_Gray() : EndIf       ; Green AND <
        Case 11:  If ((b < g And b < r) Or b > seuil) : Color_Gray() : EndIf       ; Blue AND <
        
        Case 12:  If ((g > r) XOr (b > r) Or r > seuil) : Color_Gray() : EndIf     ; Red XOR >
        Case 13:  If ((r > g) XOr (b > g) Or g > seuil) : Color_Gray() : EndIf      ; Green XOR >
        Case 14:  If ((g > b) XOr (r > b) Or b > seuil) : Color_Gray() : EndIf     ; Blue XOR >
        
        Case 15:  If ((r < g) XOr (r < b) Or r > seuil) : Color_Gray() : EndIf     ; Red XOR <
        Case 16:  If ((g < r) XOr (g < b) Or g > seuil) : Color_Gray() : EndIf     ; Green XOR <
        Case 17:  If ((b < g) XOr (b < r) Or b > seuil) : Color_Gray() : EndIf     ; Blue XOR <
        
      Case 18
        max(maxVal , r , g)
        Max(maxVal , maxVal , b)
        Min(minVal , r , g)
        Min(minVal , minVal , b)
        saturation = maxVal - minVal
        If saturation < seuil : Color_Gray() : EndIf
        
      Case 19 
        deltaRG = Abs(r - g)
        deltaRB = Abs(r - b)
        deltaGB = Abs(g - b)
        If deltaRG > seuil Or deltaRB > seuil Or deltaGB > seuil : Color_Gray() : EndIf
        
    EndSelect
    PokeL(*cible + i, var)
  Next
EndProcedure

Procedure Color(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Color"
    param\remarque = ""
    param\info[0] = "seuil"
    param\info[1] = "mode"
    param\info[2] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 255 : param\info_data(0,2) = 127
    param\info_data(1,0) = 0 : param\info_data(1,1) = 19 : param\info_data(1,2) = 0
    param\info_data(2,0) = 0 : param\info_data(2,1) = 2  : param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Color_MT() , 2)
EndProcedure

;-------------------------


Procedure color_effect_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected opt = *param\option[0] 
  
  clamp(opt , 0 , 2 )  
  Protected i, var, alpha
  Protected r , g , b , r2 , g2 , b2 , rgb
  
  Protected totalPixels  = lg * ht
  Protected startPos = (*param\thread_pos * totalPixels) / *param\thread_max
  Protected endPos = ((*param\thread_pos + 1) * totalPixels) / *param\thread_max
  
  For i = startPos To endPos - 1
    var = PeekL(*source + i * 4)
    getrgb(var , r , g , b)
    r2 = (g + b) >> 1
    g2 = (r + b) >> 1
    b2 = (r + g) >> 1
    Select opt
      Case 0 : rgb= b2<<16 + g2<<8 + r2
      Case 1 : rgb= r2<<16 + g2<<8 + b2
      Case 2 : rgb= g2<<16 + b2<<8 + r2
      Default : rgb= b2<<16 + r2<<8 + b2
    EndSelect
    
    PokeL(*cible + i * 4, rgb)
  Next
EndProcedure

Procedure color_effect(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "color_effect"
    param\remarque = ""
    param\info[0] = "option"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 2 : param\info_data(0,2) = 0
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@color_effect_MT() , 1)
EndProcedure

;-------------------------

Procedure Color_hue_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected maxVal , minVal
  Protected delta.f , hue.f , deltaHue.f
  
  Protected hueTarget = *param\option[0] 
  Protected tolerance = *param\option[1] 
  
  Protected i, totalPixels = lg * ht
  Protected var, r, g, b
  
  Protected startPos = (*param\thread_pos * totalPixels) / *param\thread_max
  Protected endPos = ((*param\thread_pos + 1) * totalPixels) / *param\thread_max
  
  For i = startPos To endPos - 1
    var = PeekL(*source + i << 2)
    getrgb(var, r, g, b)
    
    max3(maxVal , r , g , b)
    Min3(minVal , r , g , b)
    delta = maxVal - minVal
    If delta <> 0 
      
      Select maxVal
        Case r : hue = 60 * (g - b) / delta
        Case g : hue = 120 + 60 * (b - r) / delta
        Case b : hue = 240 + 60 * (r - g) / delta
      EndSelect
      If hue < 0 : hue + 360 : EndIf
      
      deltaHue = Abs(hue - hueTarget)
      If deltaHue > 180
        deltaHue = 360 - deltaHue
      EndIf
      
      If deltaHue <= tolerance : var =  ((R * 54 + G * 183 + B * 18) >> 8 )  * $10101 : EndIf
      
    EndIf
    PokeL(*cible + i << 2, var)
  Next
EndProcedure


Procedure Color_hue(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Color_hue"
    param\remarque = ""
    param\info[0] = "hueTarget"
    param\info[1] = "tolerance"
    param\info[2] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 360 : param\info_data(0,2) = 0
    param\info_data(1,0) = 0 : param\info_data(1,1) = 255 : param\info_data(1,2) = 20
    param\info_data(2,0) = 0 : param\info_data(2,1) = 2  : param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Color_hue_MT() , 2)
EndProcedure

;-------------------------

Procedure Colorize_MT(*p.parametre)
  Protected i, r, g, b, gray, alpha
  Protected intensity.f = *p\option[0] / 128.0
  Protected invIntensity.f = 1.0 - intensity
  Protected var.l
  Protected totalPixels = *p\lg * *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    var = *srcPixel\l
    getrgb(var, r, g, b)
    gray = (R * 54 + G * 183 + B * 18) >> 8 
    r = Int(r * intensity + gray * invIntensity)
    g = Int(g * intensity + gray * invIntensity)
    b = Int(b * intensity + gray * invIntensity)
    Clamp_RGB(r, g, b)
    *dstPixel\l = (r << 16) | (g << 8) | b
  Next
EndProcedure

; ────────────────────────────────────────────────────────────────
Procedure Colorize(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Colorize"
    param\remarque = ""
    param\info[0] = "intensité"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 512 : param\info_data(0,2) = 127
    param\info_data(1,1) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Colorize_MT() , 1)
EndProcedure

;-------------------------

Procedure Teinte_Simple_YUV_MT(*p.parametre)
  Protected *src = *p\addr[0]
  Protected *dst = *p\addr[1]
  Protected angleA.f = *p\option[0]
  Protected angleB.f = *p\option[1]
  Protected tolerance.f = *p\option[2]
  Protected mode = *p\option[3] 
  
  angleA = Mod(angleA, 360)
  angleB = Mod(angleB, 360)

  Protected angleA_rad.f = #PI * angleA / 180
  Protected angleB_rad.f = #PI * angleB / 180

  Protected cosA.f = Cos(angleA_rad)
  Protected sinA.f = Sin(angleA_rad)
  Protected cosB.f = Cos(angleB_rad)
  Protected sinB.f = Sin(angleB_rad)

  Protected w = *p\lg
  Protected h = *p\ht
  Protected start = (*p\thread_pos * w * h) / *p\thread_max
  Protected stop  = ((*p\thread_pos + 1) * w * h) / *p\thread_max

  Protected i, var, a, r, g, b , xpos , ypos
  Protected y.f, u.f, v.f
  Protected u2.f, v2.f
  Protected rA, gA, bA, rB, gB, bB
  Protected countA = 0, countB = 0

  If mode
    r = 0
    g = 255
    b = 0
      y =  0.299 * r + 0.587 * g + 0.114 * b
      u = -0.14713 * r - 0.28886 * g + 0.436 * b
      v =  0.615 * r - 0.51499 * g - 0.10001 * b
      u2 = u * cosA - v * sinA
      v2 = u * sinA + v * cosA
      rA = y + 1.13983 * v2
      gA = y - 0.39465 * u2 - 0.58060 * v2
      bA = y + 2.03211 * u2
      Clamp_rgb(rA, gA, bA)
      u2 = u * cosB - v * sinB
      v2 = u * sinB + v * cosB
      rB = y + 1.13983 * v2
      gB = y - 0.39465 * u2 - 0.58060 * v2
      bB = y + 2.03211 * u2
      Clamp_rgb(rB, gB, bB)
    Protected squareSize = 32
    For yPos = 0 To squareSize - 1
      For xPos = 0 To squareSize - 1
        PokeL(*dst + ((yPos * w) + xPos) * 4, $FF000000 | (rA << 16) | (gA << 8) | bA)
        PokeL(*dst + ((yPos * w) + ( squareSize + xPos + 1)) * 4, $FF000000 | (rB << 16) | (gB << 8) | bB)
      Next
    Next
  EndIf
  
  Protected angle_src_rad.f = #PI * angleB / 180  
  Protected angle_dst_rad.f = #PI * angleA / 180   
  Protected tolerance_deg.f = tolerance           
  Protected tol_rad.f = #PI * tolerance_deg / 180

  For i = start To stop - 1
    var = PeekL(*src + i * 4)
    getargb(var, a, r, g, b)
    y =  0.299 * r + 0.587 * g + 0.114 * b
    u = -0.14713 * r - 0.28886 * g + 0.436 * b
    v =  0.615 * r - 0.51499 * g - 0.10001 * b
    Protected angle_pixel.f = ATan2(v, u)
    Protected angle_diff.f = angle_pixel - angle_src_rad
    If angle_diff > #PI : angle_diff - 2 * #PI : EndIf
    If angle_diff < -#PI : angle_diff + 2 * #PI : EndIf
    angle_diff = Abs(angle_diff)
    If angle_diff <= tol_rad
      Protected angle_delta.f = angle_dst_rad - angle_pixel
      Protected cosD.f = Cos(angle_delta)
      Protected sinD.f = Sin(angle_delta)
      u2 = u * cosD - v * sinD
      v2 = u * sinD + v * cosD
      r = y + 1.13983 * v2
      g = y - 0.39465 * u2 - 0.58060 * v2
      b = y + 2.03211 * u2
      Clamp_rgb(r, g, b)
    EndIf
    PokeL(*dst + i * 4, a << 24 | r << 16 | g << 8 | b)
  Next
EndProcedure


Procedure ColorPermutation(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "ColorPermutation"
    param\remarque = ""
    param\info[0] = "Teinte 1"
    param\info[1] = "Teinte 2"
    param\info[2] = "tolerence"
    param\info[3] = "affiche"
    param\info[4] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 360 : param\info_data(0,2) = 0
    param\info_data(1,0) = 0 : param\info_data(1,1) = 360 : param\info_data(1,2) = 0
    param\info_data(2,0) = 0 : param\info_data(2,1) = 180 : param\info_data(2,2) = 25
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1 : param\info_data(3,2) = 0
    param\info_data(4,0) = 0 : param\info_data(4,1) = 2 : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  
  filter_start(@Teinte_Simple_YUV_MT() , 4)
EndProcedure

;-------------------------

Procedure Contrast_MT(*p.parametre)
  Protected i, a, r, g, b, contrast, alpha, var
  Protected totalPixels = *p\lg * *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected *mask = *p\mask
  contrast = (( *p\option[0] - 128 ) * 256 ) / 128 + 256
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    var = *srcPixel\l
    GetARGB(var, a, r, g, b)
    r = ((r - 128) * contrast) >> 8 + 128
    g = ((g - 128) * contrast) >> 8 + 128
    b = ((b - 128) * contrast) >> 8 + 128
    Clamp_RGB(r, g, b)
    *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
  Next
EndProcedure

Procedure Contrast(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Contrast"
    param\remarque = ""
    param\info[0] = "Contraste"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 512 : param\info_data(0,2) = 255
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Contrast_MT() , 1)
EndProcedure

;-------------------------

Procedure Dichromatic_MT(*p.parametre)
  Protected i, a, r, g, b
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected definition.f = (*p\option[0] / 100.0) * 255
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected totalPixels = lg * ht
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    getargb(*srcPixel\l , a, r , g , b)
    Protected grey = ((r * 1225 + g * 2405 + b * 466) >> 12)
    If grey < definition
      *dstPixel\l = ( (a << 24) )
    Else
      *dstPixel\l = ( (a << 24) | $ffffff)
    EndIf
  Next
EndProcedure

Procedure Dichromatic(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Dichromatic"
    param\remarque = ""
    param\info[0] = "Intensité"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 25 : param\info_data(0,1) = 75 : param\info_data(0,2) = 50
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Dichromatic_MT() , 1)
EndProcedure

;-------------------------

Procedure Exposure_MT(*p.parametre)
  Protected i, a, r, g, b, alpha, var
  Protected totalPixels = *p\lg * *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected exposure.f = *p\option[0]
  Clamp(exposure, 1, 255)
  exposure * 0.1
  Protected Dim tab.a(255)
  For i = 0 To 255
    Protected val.f = 255 * (1.0 - Exp(-i * exposure / 255.0))
    If val > 255 : val = 255 : EndIf
    tab(i) = Int(val)
  Next
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    var = *srcPixel\l
    getargb(var, a, r, g, b)
    r = tab(r)
    g = tab(g)
    b = tab(b)
    Clamp_RGB(r, g, b)
    *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
  Next
  FreeArray(tab())
EndProcedure

Procedure Exposure(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Exposure"
    param\remarque = "Correction d’exposition (type photo)"
    param\info[0] = "Exposition"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 255 : param\info_data(0,2) = 15
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2 : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Exposure_MT() , 1)
EndProcedure

;-------------------------

Procedure FalseColour_RGBfromHSL(h.f, s.f, l.f)
  Protected r.f, g.f, b.f
  Protected c.f = (1 - Abs(2 * l - 1)) * s
  Protected x.f = c * (1 - Abs(Mod(h / 60, 2) - 1))
  Protected m.f = l - c / 2
  Select Int(h / 60)
    Case 0 : r=c : g=x : b=0
    Case 1 : r=x : g=c : b=0
    Case 2 : r=0 : g=c : b=x
    Case 3 : r=0 : g=x : b=c
    Case 4 : r=x : g=0 : b=c
    Default: r=c : g=0 : b=x
  EndSelect
  r = (r + m) * 255
  g = (g + m) * 255
  b = (b + m) * 255

  ProcedureReturn $FF000000 | (Int(r) << 16) | (Int(g) << 8) | Int(b)
EndProcedure

Procedure FalseColour_MT(*p.parametre)
  Protected i, a, r, g, b
  Protected teinte.f = *p\option[0] ;* 0.01
  Protected Dim RainbowLUT(1000)
  For i = 0 To 1000 : RainbowLUT(i) = FalseColour_RGBfromHSL(Mod((i/1000.0*360 + teinte) , 360), 1, 0.5): Next
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected totalPixels = lg * ht
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    getargb(*srcPixel\l , a , r , g , b)
    Protected grey = ((r * 1225 + g * 2405 + b * 466) >> 12)
    Protected ratio = (grey * 1000) / 255
    Protected color = RainbowLUT(ratio)
    getargb(color, a, r, g, b)
    *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
  Next
  FreeArray(RainbowLUT())
EndProcedure

Procedure FalseColour(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "False Colour"
    param\remarque = "Teinte basée sur l'intensité"
    param\info[0] = "Mode Couleur"
    param\info[1] = "Masque"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 360 : param\info_data(0,2) = 0
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2   : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@FalseColour_MT() , 1)
EndProcedure

;-------------------------

Procedure Gamma_MT(*p.parametre)
  Protected i, var, a, r, g, b, alpha
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected totalPixels = lg * ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  Protected lut = *p\addr[2]
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    getargb(*srcPixel\l , a , r ,g , b)
    r = PeekA(lut + r)
    g = PeekA(lut + g)
    b = PeekA(lut + b)
    *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
  Next
EndProcedure

Procedure Gamma(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Gamma"
    param\remarque = ""
    param\info[0] = "Gamma"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 255 : param\info_data(0,2) = 127
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  Protected i , var
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  Protected lut = AllocateMemory(255)
  Protected div.f
  Protected gamma_raw.f = 255 - *param\option[0]
  clamp(gamma_raw, 0, 255)
  Protected gamma_f.f = gamma_raw / 100
  For i = 0 To 255
    div = i
    var = Pow(div / 255.0, gamma_f) * 255.0
    Clamp(var, 0, 255)
    PokeA(lut + i , var)
  Next
  *param\addr[2] = lut
  filter_start(@Gamma_MT() , 1)
  FreeMemory(lut)
EndProcedure

;-------------------------

Procedure Grayscale_MT(*param.parametre) 
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected typ = *param\option[0]
  Protected i, var
  Protected a , r, g, b, gray
  Protected t = lg * ht
  Protected start = (*param\thread_pos * t) / *param\thread_max
  Protected stop = ((*param\thread_pos + 1) * t) / *param\thread_max - 1
  For i = start To stop - 1
    var = PeekL(*param\addr[0] + i * 4)
    getargb( var , a , r , g , b)
    Select typ    
      Case 1
        gray = (r * 1225 + g * 2405 + b * 466) >> 12
      Case 2 
        gray = (r * 870 + g * 2930 + b * 296) >> 12 
      Case 3
        gray = (r * 1293 + g * 2156 + b * 647) >> 12  
      Case 4 
        gray = r : If g > gray : gray = g : EndIf
        If b > gray : gray = b : EndIf  
      Case 5 
        gray = r : If g < gray : gray = g : EndIf
        If b < gray : gray = b : EndIf     
      Case 6 
        If r > g : Swap r, g : EndIf
        If g > b : Swap g, b : EndIf
        If r > g : Swap r, g : EndIf
        gray = g    
      Case 7 
        gray = r       
      Case 8 
        gray = g    
      Case 9 
        gray = b      
      Case 10 
        gray = Sqr(r * r * 0.299 + g * g * 0.587 + b * b * 0.114)  
      Case 11 
        gray = Sqr(r * r * 0.2126 + g * g *0.7152 + b * b * 0.0722)   
      Case 12
        max3( r , r , g , b)
        min3( g , r , g ,b)
        gray = (r + g) >> 1 
      Case 13 
         Max3( gray , r, g, b) 
      Default 
        gray = (r * 1365 + g * 1365 + b * 1366) >> 12
    EndSelect
    Clamp(gray, 0, 255)
    PokeL(*param\addr[1] + i * 4, a << 24 | gray * $10101)
  Next
EndProcedure

Procedure Grayscale(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Grayscale"
    param\remarque = ""
    param\info[0] = "type" 
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 13 : param\info_data(0,2) = 0
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Grayscale_MT() , 1)
EndProcedure

;-------------------------

Procedure Hollow_MT(*p.parametre)
  Protected i, r, g, b, var, alpha
  Protected totalPixels = *p\lg * *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected *mask = *p\mask
  Protected opt = *p\option[0]
  Protected hollow = *p\option[1]
  Protected v.f, v1.f

  clamp(opt, 0, 360)

  v = opt / 255.0 * #PI / 180.0

  Protected Dim tab_hollow.f(255)
  For i = 0 To 255
    If hollow
      v1 = 255 * (1 - Sin(i * v))
    Else
      v1 = 255 * (Sin(i * v))
    EndIf
    clamp(v1, 0, 255)
    tab_hollow(i) = v1
  Next

  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max

  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)

    var = *srcPixel\l
    getrgb(var, r, g, b)

    r = tab_hollow(r)
    g = tab_hollow(g)
    b = tab_hollow(b)

    *dstPixel\l = RGB(r, g, b)
  Next

  FreeArray(tab_hollow()) 
EndProcedure


Procedure Hollow(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Hollow"
    param\remarque = ""
    param\info[0] = "angle"
    param\info[1] = "Hollow/Ledge"
    param\info[2] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 360 : param\info_data(0,2) = 180
    param\info_data(1,1) = 0 : param\info_data(1,1) = 1  : param\info_data(1,2) = 0
    param\info_data(2,1) = 0 : param\info_data(2,1) = 2  : param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Hollow_MT() , 2)
EndProcedure

;-------------------------

Procedure Negatif_MT(*p.parametre)
  Protected i, a, r, g, b, alpha, var
  Protected totalPixels = *p\lg * *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected *mask = *p\mask

  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max

  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    var = *srcPixel\l
    GetARGB(var, a, r, g, b)
    *dstPixel\l = ~var
  Next
EndProcedure


Procedure Negatif(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Négatif"
    param\remarque = ""
    param\info[0] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 2 : param\info_data(0,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Negatif_MT() , 0)
EndProcedure

;-------------------------

Procedure Normalize_Color(*p.parametre)
  Protected start, stop, i
  Protected var.l
  Protected r, g, b
  Protected rmin = 255, gmin = 255, bmin = 255
  Protected rmax = 0, gmax = 0, bmax = 0
  Protected rangeR, rangeG, rangeB
  Protected pixelCount = *p\lg * *p\ht
  Protected *source = *p\addr[0]
  Protected *cible = *p\addr[1]
  start = ( *p\thread_pos * pixelCount ) / *p\thread_max
  stop  = ( (*p\thread_pos + 1) * pixelCount ) / *p\thread_max - 1
  For i = 0 To pixelCount - 1
    var = PeekL(*source + i << 2)
    getrgb(var, r, g, b)
    
    If r < rmin : rmin = r : EndIf
    If g < gmin : gmin = g : EndIf
    If b < bmin : bmin = b : EndIf
    
    If r > rmax : rmax = r : EndIf
    If g > gmax : gmax = g : EndIf
    If b > bmax : bmax = b : EndIf
  Next
  rangeR = rmax - rmin
  rangeG = gmax - gmin
  rangeB = bmax - bmin
  If rangeR = 0 : rangeR = 1 : EndIf
  If rangeG = 0 : rangeG = 1 : EndIf
  If rangeB = 0 : rangeB = 1 : EndIf
  
  For i = start To stop
    var = PeekL(*source + i << 2)
    getrgb(var, r, g, b)
    r = ((r - rmin) * 255) / rangeR
    g = ((g - gmin) * 255) / rangeG
    b = ((b - bmin) * 255) / rangeB
    Clamp_rgb(r, g, b)
    PokeL(*cible + i << 2, (var & $FF000000) | (r << 16) | (g << 8) | b)
  Next
EndProcedure

Procedure Normalize_Color_Filter(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Normalize_Color"
    param\remarque = ""
    param\info[0] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 2 : param\info_data(0,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Normalize_Color() , 0)
EndProcedure

;-------------------------

Procedure Pencil_MT(*p.parametre)
  Protected i, a, r, g, b, grey, pixel , grey1
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected intensity.f = ((*p\option[0] /5) + 40) / 100
  Protected limit = *p\option[1]
  Protected couleur = *p\option[2]
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected totalPixels = lg * ht
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max

  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    getargb(*srcPixel\l, a, r, g, b)
    grey = ((r * 1225 + g * 2405 + b * 466) >> 12)
    grey1 = grey
    If couleur
      grey = grey / couleur
      grey = grey * couleur
    EndIf
    If grey1 < (limit * intensity)
      If grey > 0
        grey - ((i % 4) * 4 * intensity)
        grey + (((i / lg) % 8) * 2 * intensity)
        pixel = Random(grey)
        If pixel < grey * intensity
          grey = pixel
        EndIf
      Else
        grey = Random(Int(intensity * 32.0))
      EndIf
    Else
      If grey > (254 - (16 * intensity)) : grey = 255 : EndIf
    EndIf
    clamp(grey , 0 , 255)
    *dstPixel\l = (a << 24) | (grey << 16) | (grey << 8) | grey
  Next
EndProcedure


Procedure PencilImage(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Pencil Sketch"
    param\remarque = "Effet Crayon à Papier"
    param\info[0] = "Intensité"
    param\info[1] = "limite"
    param\info[2] = "couleur"
    param\info[3] = "Masque"
    param\info_data(0,0) = 0   : param\info_data(0,1) = 100 : param\info_data(0,2) = 50
    param\info_data(1,0) = 0   : param\info_data(1,1) = 255 : param\info_data(1,2) = 240
    param\info_data(2,0) = 0   : param\info_data(2,1) = 64  : param\info_data(2,2) = 0
    param\info_data(3,0) = 0   : param\info_data(3,1) = 2   : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Pencil_MT() , 3)
EndProcedure

;------------------------


Procedure Posterize_MT(*p.parametre)
  Protected i, pixel.l, a.l, r.l, g.l, b.l
  Protected totalPixels = *p\lg * *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  Protected *cr = *p\addr[2]
  Protected *cg = *p\addr[3]
  Protected *cb = *p\addr[4]
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    pixel = *srcPixel\l
    GetARGB(pixel, a, r, g, b)
    r = PeekA(*cr + r)
    g = PeekA(*cg + g)
    b = PeekA(*cb + b)
    *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
  Next
EndProcedure

Procedure Posterize(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Posterize"
    param\remarque = "reduit le nombre de couleur"
    param\info[0] = "rourge"
    param\info[1] = "vert"
    param\info[2] = "bleu"
    param\info[3] = "masque"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 255 : param\info_data(0,2) = 255
    param\info_data(1,0) = 1 : param\info_data(1,1) = 255 : param\info_data(1,2) = 255
    param\info_data(2,0) = 1 : param\info_data(2,1) = 255 : param\info_data(2,2) = 255
    param\info_data(3,0) = 0 : param\info_data(3,1) = 2  : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf

  Protected i
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  Protected levelr = *param\option[0]
  Protected levelg = *param\option[1]
  Protected levelb = *param\option[2]
  clamp(levelr , 1 , 255)
  clamp(levelg , 1 , 255)
  clamp(levelb , 1 , 255)
  levelr = 256 - levelr
  levelg = 256 - levelg
  levelb = 256 - levelb
  Protected *cr = AllocateMemory(256)
  Protected *cg = AllocateMemory(256)
  Protected *cb = AllocateMemory(256)
  For i = 0 To 255
    PokeA(*cr + i , ((i / levelr) * levelr) )
    PokeA(*cg + i , ((i / levelg) * levelg) )
    PokeA(*cb + i , ((i / levelb) * levelb) )
  Next
  *param\addr[2] = *cr
  *param\addr[3] = *cg
  *param\addr[4] = *cb
  
  filter_start(@Posterize_MT() , 3)
  
  FreeMemory(*cr)
  FreeMemory(*cg)
  FreeMemory(*cb)
EndProcedure

;------------------------

Procedure RaviverCouleurs_MT(*p.parametre)
  Protected i, a, r, g, b, gray
  Protected diffR, diffG, diffB, maxDiff
  Protected lightness, saturation, factor, factorInput
  Protected totalPixels = *p\lg * *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected minSaturation = 4
  Protected minLightness = 32
  factorInput = *p\option[0]
  clamp(factorInput, 0, 500)
  factorInput = 256 + (factorInput * 256) / 100
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    GetARGB(*srcPixel\l, a, r, g, b)
    gray = (r * 1365 + g * 1365 + b * 1366) >> 12
    lightness = gray
    diffR = r - gray
    diffG = g - gray
    diffB = b - gray
    max3(maxDiff, Abs(diffR), Abs(diffG), Abs(diffB))
    If maxDiff > minSaturation And lightness > minLightness
      saturation = (maxDiff << 8) / 128 
      factor = 256 + ((factorInput - 256) * saturation) >> 8
      r = gray + (diffR * factor) >> 8
      g = gray + (diffG * factor) >> 8
      b = gray + (diffB * factor) >> 8
    EndIf
    Clamp_RGB(r, g, b)
    *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
  Next
EndProcedure

Procedure RaviverCouleurs(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "RaviverCouleurs"
    param\remarque = ""
    param\info[0] = "Intensité"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 512 : param\info_data(0,2) = 255
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@RaviverCouleurs_MT() , 1)
EndProcedure

;------------------------

Procedure Saturation_MT(*p.parametre)
  Protected i, a.l ,r.l, g.l, b.l , gray
  Protected intensity.i = *p\option[0]
  clamp( intensity , 0 , 255)
  Protected invIntensity.i = 256 - intensity
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected totalPixels = *p\lg * *p\ht
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    getargb(*srcPixel\l, a, r, g, b)
    gray = (r * 77 + g * 151 + b * 28) >> 8
    r = (r * intensity + gray * invIntensity) >> 8
    g = (g * intensity + gray * invIntensity) >> 8
    b = (b * intensity + gray * invIntensity) >> 8
    Clamp_RGB(r, g, b)
    *dstPixel\l = (a << 24) + (r << 16) + (g << 8) + b
  Next
EndProcedure

Procedure Saturation(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Saturation"
    param\remarque = ""
    param\info[0] = "saturation"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 1 : param\info_data(0,1) = 255 : param\info_data(0,2) = 255
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf 
  filter_start(@Saturation_MT() , 1)
EndProcedure

;------------------------

Procedure Sepia_MT(*p.parametre)
  Protected i, r, g, b, a, var, alpha
  Protected totalPixels = *p\lg * *p\ht
  Protected *src.Pixel32
  Protected *dst.Pixel32
  Protected *mask = *p\mask
  Protected factor.f = (*p\option[0] - 100)/100
  Protected start = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected stop  = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = start To stop - 1
    *src = *p\addr[0] + (i << 2)
    *dst = *p\addr[1] + (i << 2)
    var = *src\l
    GetARGB(var, a, r, g, b)
    Protected r2 = (r * 101 + g * 197 + b * 48) >> 8
    Protected g2 = (r * 89  + g * 175 + b * 43) >> 8
    Protected b2 = (r * 70  + g * 137 + b * 33) >> 8
    r2 + (factor * 40)
    b2 - (factor * 40)
    Clamp_RGB(r2, g2, b2)
    *dst\l = (a << 24) | (r2 << 16) | (g2 << 8) | b2
  Next
EndProcedure

Procedure Sepia(*param.parametre)
  If *param\info_active
    param\typ = #Filter_Type_Color
    param\name = "Sepia"
    param\remarque = "Convertit l'image avec une teinte sépia chaude"
    *param\info[0] = "Filtre sépia"
    *param\info[1] = "Chaud\froid"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 200  : param\info_data(0,2) = 100
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Sepia_MT() , 1)
EndProcedure

;------------------------

Procedure SquareLaw_MT(*p.parametre)
  Protected i, a, r, g, b, alpha, var
  Protected totalPixels = *p\lg * *p\ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected *mask = *p\mask

  Clamp(*p\option[0], 1, 255)
  Protected sqrval = *p\option[0] * *p\option[0]
  Protected Dim tab.a(255)
  For i = 0 To 255
    Protected inv = 255 - i
    Protected val.f = sqrval - inv * inv
    If val < 0 : val = 0 : EndIf
    tab(i) = Int(Sqr(val))
  Next
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)
    var = *srcPixel\l
    getargb(var, a , r, g, b)
    r = tab(r)
    g = tab(g)
    b = tab(b)
    Clamp_RGB(r, g, b)
    *dstPixel\l = (a<< 24) | (r << 16) | (g << 8) | b
  Next
  FreeArray(tab())
EndProcedure


Procedure SquareLaw_Lightening(*param.parametre)
  If *param\info_active
    param\typ = #Filter_Type_Color
    param\name = "SquareLaw_Lightening"
    param\remarque = "Éclaircissement par loi quadratique"
    *param\info[0] = "Intensité"
    *param\info[1] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 255 : param\info_data(0,2) = 127
    param\info_data(1,1) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@SquareLaw_MT() , 1)
EndProcedure

;------------------------

Procedure.f HUEtoRGB(p.f, q.f, t.f)
  If t < 0 : t + 360 : EndIf
  If t >= 360 : t - 360 : EndIf
  If t < 60
    ProcedureReturn p + (q - p) * t / 60
  ElseIf t < 180
    ProcedureReturn q
  ElseIf t < 240
    ProcedureReturn p + (q - p) * (240 - t) / 60
  Else
    ProcedureReturn p
  EndIf
EndProcedure

Procedure teinte_MT(*p.parametre)
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected totalPixels = lg * ht
  Protected mode = *p\option[1]
  Protected angle = (#PI * *p\option[0]) / 180
  Protected cs = Cos(angle) * 256
  Protected sc  = Sin(angle) * 256
  Protected d.f = 1 / 100
  Protected j, var, a, r, g, b
  Protected ry.f, by.f, y.f, ryy.f, byy.f, gyy.f
  Protected h.f, rf.f, gf.f, bf.f
  Protected q.f, p.f , i.f
  Protected start = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected stop  = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For j = start To stop - 1
    var = PeekL(*source + j << 2)
    getargb(var, a, r, g, b)
    Select mode
      Case 0 
        ry = (70 * r - 59 * g - 11 * b) * d
        by = (-30 * r - 59 * g + 89 * b) * d
        y  = (30 * r + 59 * g + 11 * b) * d
        ryy = (sc * by + cs * ry) /256
        byy = (cs * by - sc * ry) /256
        gyy = (-51 * ryy - 19 * byy) * d
        r = y + ryy
        g = y + gyy
        b = y + byy
      Case 1
        I = 0.596*r - 0.274*g - 0.322*b
        q = 0.211*r - 0.523*g + 0.312*b
        y = 0.299*r + 0.587*g + 0.114*b
        Protected I2.f = I * Cos(angle) - Q * Sin(angle)
        Protected Q2.f = I * Sin(angle) + Q * Cos(angle)
        r = y + 0.956*I2 + 0.621*Q2
        g = y - 0.272*I2 - 0.647*Q2
        b = y - 1.106*I2 + 1.703*Q2
      Case 2 
        rf = r / 255.0
        gf = g / 255.0
        bf = b / 255.0
        Protected maxVal.f = rf
        If gf > maxVal : maxVal = gf : EndIf
        If bf > maxVal : maxVal = bf : EndIf
        Protected minVal.f = rf
        If gf < minVal : minVal = gf : EndIf
        If bf < minVal : minVal = bf : EndIf
        Protected l.f = (maxVal + minVal) / 2.0
        Protected s.f = 0.0
        Protected delta.f = maxVal - minVal
        If delta = 0.0
          rf = l : gf = l : bf = l
        Else
          If l < 0.5
            s = delta / (maxVal + minVal)
          Else
            s = delta / (2.0 - maxVal - minVal)
          EndIf

          Select maxVal
            Case rf
              h = (gf - bf) / delta
              If gf < bf : h = h + 6.0 : EndIf
            Case gf
              h = (bf - rf) / delta + 2.0
            Case bf
              h = (rf - gf) / delta + 4.0
          EndSelect
          h * 60.0
          h = h + *p\option[0]
          If h >= 360 : h = h - 360 : EndIf
          If h < 0    : h = h + 360 : EndIf
          If l < 0.5
            q = l * (1 + s)
          Else
            q = l + s - (l * s)
          EndIf
          p = 2 * l - q
          rf = HUEtoRGB(p, q, h + 120)
          gf = HUEtoRGB(p, q, h)
          bf = HUEtoRGB(p, q, h - 120)
        EndIf
        r = rf * 255
        g = gf * 255
        b = bf * 255
    EndSelect
    Clamp_rgb(r, g, b)
    PokeL(*cible + j << 2, a << 24 | r << 16 | g << 8 | b)
  Next

EndProcedure

Procedure teinte(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Color
    param\name = "teinte"
    param\remarque = ""
    param\info[0] = "angle"
    param\info[1] = "YUV, YIQ, HSL"
    param\info[2] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 360 : param\info_data(0,2) = 1
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2 : param\info_data(1,2) = 0
    param\info_data(2,0) = 0 : param\info_data(2,1) = 2 : param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@teinte_MT() , 2)
EndProcedure
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

les filtres "deform"

deform.pbi

Code : Tout sélectionner

Procedure Ellipse_MT(*p.parametre)
  Protected start, stop
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  
  Protected cx.f = (*p\option[1] * lg) / 100
  Protected cy.f = (*p\option[2] * ht) / 100
  Protected rayon_x.f = (lg * *p\option[3]) / 100 + 10
  Protected rayon_y.f = (ht * *p\option[4]) / 100 + 10
  Protected force.f = (*p\option[0] - 200.0) / 100.0 
  
  Protected x, y
  Protected dx.f, dy.f, r.f, facteur.f
  Protected src_x.f, src_y.f
  Protected ligne_source, ligne_cible
  Protected pix.l
  
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = (( *p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    For x = 0 To lg - 1
      dx = (x - cx) / rayon_x
      dy = (y - cy) / rayon_y
      r = dx * dx + dy * dy
      
      If r <= 1.0
        facteur = Pow(Sin(Sqr(r) * #PI / 2), 1 + force)
        src_x = cx + dx * rayon_x * facteur
        src_y = cy + dy * rayon_y * facteur
      Else
        src_x = x
        src_y = y
      EndIf
      
      If src_x >= 0 And src_x < lg And src_y >= 0 And src_y < ht
        ligne_source = *source + (Int(src_y) * lg + Int(src_x)) * 4
        pix = PeekL(ligne_source)
      Else
        pix = 0
      EndIf
      
      ligne_cible = *cible + (y * lg + x) * 4
      PokeL(ligne_cible, pix)
    Next
  Next
EndProcedure


Procedure Ellipze(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Ellipze"
    param\remarque = "Déformation elliptique (lentille)"
    param\info[0] = "Force"
    param\info[1] = "PosX"
    param\info[2] = "PosY"
    param\info[3] = "Rayon X"
    param\info[4] = "Rayon Y"
    param\info[5] = "Masque binaire"
    param\info_data(0,0) = 0   : param\info_data(0,1) = 600 : param\info_data(0,2) = 200
    param\info_data(1,0) = 0   : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 0   : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 0   : param\info_data(3,1) = 100 : param\info_data(3,2) = 50
    param\info_data(4,0) = 0   : param\info_data(4,1) = 100 : param\info_data(4,2) = 50
    param\info_data(5,0) = 0   : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Ellipse_MT() , 5)
EndProcedure

;-----------------------

Procedure FlipH_MT(*p.parametre)
  Protected start, stop, i
  Protected pix.l
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected y0 , y1
  
  start = ( *p\thread_pos * ht ) / *p\thread_max
  stop  = ( (*p\thread_pos + 1) * ht ) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht -1 : EndIf
  For y0 = start To stop
    y1 = ht - y0 - 1 
    CopyMemory(*p\addr[0] + y0 * lg * 4, *p\addr[1] + y1 * lg * 4, lg * 4)
  Next
EndProcedure


Procedure FlipH(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "FlipH"
    param\remarque = "Miroir horizontal"
    param\info[0] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 2 : param\info_data(0,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@FlipH_MT() , 0)
  
EndProcedure

;-----------------------

Procedure FlipV_MT(*p.parametre)
  Protected start, stop
  Protected pix.l
  Protected lg      = *p\lg
  Protected ht      = *p\ht
  Protected x, y, x0, x1
  Protected ligne_source, ligne_cible
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = (( *p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    ligne_source = *p\addr[0] + y * lg * 4
    ligne_cible  = *p\addr[1]  + y * lg * 4
    For x = 0 To lg - 1
      x0 = x
      x1 = lg - 1 - x
      pix = PeekL(ligne_source + x0 * 4)
      PokeL(ligne_cible  + x1 * 4, pix)
    Next
  Next
EndProcedure


Procedure FlipV(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "FlipV"
    param\remarque = "Miroir vertical"
    param\info[0] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 2 : param\info_data(0,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@FlipV_MT() , 0)
EndProcedure

;-----------------------

Procedure Lens_MT(*p.parametre)
  Protected start, stop
  Protected *source = *p\source
  Protected *cible  = *p\cible
  Protected lg = *p\lg
  Protected ht = *p\ht
  
  Protected cx.f = (*p\option[1] * lg) / 100
  Protected cy.f = (*p\option[2] * ht) / 100
  Protected rayon.f = ((Sqr(lg * lg + ht * ht) * *p\option[3]) / 100) + 1
  Protected zoom.f = *p\option[0] / 100.0
  
  Protected x, y
  Protected dx.f, dy.f, dist.f
  Protected factor.f
  Protected src_x.f, src_y.f
  Protected ligne_source, ligne_cible
  Protected pix.l
  
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = (( *p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    For x = 0 To lg - 1
      dx = x - cx
      dy = y - cy
      dist = Sqr(dx*dx + dy*dy)
      
      If dist < rayon And dist > 0
        factor = 1 + zoom * (1 - (dist / rayon))
        src_x = cx + dx / factor
        src_y = cy + dy / factor
      Else
        src_x = x
        src_y = y
      EndIf
      
      If src_x >= 0 And src_x < lg And src_y >= 0 And src_y < ht
        ligne_source = *source + (Int(src_y) * lg + Int(src_x)) * 4
        pix = PeekL(ligne_source)
      Else
        pix = 0
      EndIf
      
      ligne_cible = *cible + (y * lg + x) * 4
      PokeL(ligne_cible, pix)
    Next
  Next
EndProcedure

Procedure Lens(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Lens"
    param\remarque = "Effet loupe ou lentille"
    param\info[0] = "Zoom (%)"
    param\info[1] = "Centre X (%)"
    param\info[2] = "Centre Y (%)"
    param\info[3] = "Rayon (%)"
    param\info[4] = "Masque binaire"
    
    param\info_data(0,0) = -100 : param\info_data(0,1) = 300 : param\info_data(0,2) = 100
    param\info_data(1,0) = 0    : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 0    : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 1    : param\info_data(3,1) = 100 : param\info_data(3,2) = 30
    param\info_data(4,0) = 0    : param\info_data(4,1) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Lens_MT() , 4)
EndProcedure

;-----------------------

Procedure Perspective_MT(*p.parametre)
  Protected x, y
  Protected sx.f, sy.f, u.f, v.f
  Protected *srcPixel.LONG, *dstPixel.LONG
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht

  Protected deltaX = lg / 2
  Protected deltaY = ht / 2
  
  Protected x00.f = deltaX * ((*p\option[0] - 50) / 50) + 0
  Protected y00.f = deltaY * ((*p\option[1] - 50) / 50) + 0
  
  Protected x10.f = deltaX * ((*p\option[2] - 50) / 50) + lg
  Protected y10.f = deltaY * ((*p\option[3] - 50) / 50) + 0
  
  Protected x01.f = deltaX * ((*p\option[4] - 50) / 50) + 0
  Protected y01.f = deltaY * ((*p\option[5] - 50) / 50) + ht
  
  Protected x11.f = deltaX * ((*p\option[6] - 50) / 50) + lg
  Protected y11.f = deltaY * ((*p\option[7] - 50) / 50) + ht
  
  Protected startY = (*p\thread_pos * ht) / *p\thread_max
  Protected stopY  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht - 1 : EndIf
  
  For y = startY To stopY
    v = y / ht
    For x = 0 To lg - 1
      u = x / lg
      sx = (1 - u) * (1 - v) * x00 + u * (1 - v) * x10 + (1 - u) * v * x01 + u * v * x11
      sy = (1 - u) * (1 - v) * y00 + u * (1 - v) * y10 + (1 - u) * v * y01 + u * v * y11
      
      *dstPixel = *cible + (y * lg + x) * 4
      If sx >= 0 And sx < lg And sy >= 0 And sy < ht
        *srcPixel = *source + (Int(sy) * lg + Int(sx)) * 4
        *dstPixel\l = *srcPixel\l
      Else
        *dstPixel\l = 0
      EndIf
    Next
  Next
EndProcedure

Procedure.f Area2D(x1.f, y1.f, x2.f, y2.f, x3.f, y3.f)
  ProcedureReturn Abs((x2 - x1)*(y3 - y1) - (x3 - x1)*(y2 - y1)) / 2.0
EndProcedure

Procedure.b PointInQuad(x.f, y.f, Array  pts.f(1) )
  Protected A_x = pts(0), A_y = pts(1)
  Protected B_x = pts(2), B_y = pts(3)
  Protected C_x = pts(6), C_y = pts(7)
  Protected D_x = pts(4), D_y = pts(5)
  
  Protected areaQuad.f = Area2D(A_x, A_y, B_x, B_y, C_x, C_y) + Area2D(A_x, A_y, C_x, C_y, D_x, D_y)

  Protected areaSum.f , quadArea
  areaSum + Area2D(x, y, A_x, A_y, B_x, B_y)
  areaSum + Area2D(x, y, B_x, B_y, C_x, C_y)
  areaSum + Area2D(x, y, C_x, C_y, D_x, D_y)
  areaSum + Area2D(x, y, D_x, D_y, A_x, A_y)
  
  If Abs(quadArea - quadArea) < 0.5
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
EndProcedure


Procedure DrawTextureInQuad_MT(*param.parametre)

  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  
  Protected srcWidth = param\lg
  Protected srcHeight = param\ht
  Protected dstWidth = param\lg
  Protected dstHeight = param\ht
  
  Protected Dim ptsSrc.f(7)
  ptsSrc(0) = 0 : ptsSrc(1) = 0
  ptsSrc(2) = srcWidth - 1 : ptsSrc(3) = 0
  ptsSrc(4) = 0 : ptsSrc(5) = srcHeight - 1
  ptsSrc(6) = srcWidth - 1 : ptsSrc(7) = srcHeight - 1
  
  Protected Dim ptsDst.f(7)
  
  ptsDst(0) = ((param\option[0]) / 100.0) * (dstWidth - 1)
  ptsDst(1) = ((param\option[1]) / 100.0) * (dstHeight - 1)
  ptsDst(2) = ((param\option[2]) / 100.0) * (dstWidth - 1)
  ptsDst(3) = ((param\option[3]) / 100.0) * (dstHeight - 1)
  ptsDst(4) = ((param\option[4]) / 100.0) * (dstWidth - 1)
  ptsDst(5) = ((param\option[5]) / 100.0) * (dstHeight - 1)
  ptsDst(6) = ((param\option[6]) / 100.0) * (dstWidth - 1)
  ptsDst(7) = ((param\option[7]) / 100.0) * (dstHeight - 1)
  
  Protected Dim A.f(63)
  Protected Dim b.f(7)
  Protected Dim H.f(8)
  Protected xi.f, yi.f, xdi.f, ydi.f
  Protected i , k , j
  
  For i = 0 To 3
    xi = ptsSrc(i*2)
    yi = ptsSrc(i*2 + 1)
    xdi = ptsDst(i*2)
    ydi = ptsDst(i*2 + 1)

    A(2*i*8 + 0) = xi
    A(2*i*8 + 1) = yi
    A(2*i*8 + 2) = 1
    A(2*i*8 + 3) = 0
    A(2*i*8 + 4) = 0
    A(2*i*8 + 5) = 0
    A(2*i*8 + 6) = -xi * xdi
    A(2*i*8 + 7) = -yi * xdi
    b(2*i) = xdi

    A((2*i+1)*8 + 0) = 0
    A((2*i+1)*8 + 1) = 0
    A((2*i+1)*8 + 2) = 0
    A((2*i+1)*8 + 3) = xi
    A((2*i+1)*8 + 4) = yi
    A((2*i+1)*8 + 5) = 1
    A((2*i+1)*8 + 6) = -xi * ydi
    A((2*i+1)*8 + 7) = -yi * ydi
    b(2*i + 1) = ydi
  Next
  
  For i = 0 To 7
    Protected maxRow = i
    For  k = i + 1 To 7
      If Abs(A(k*8 + i)) > Abs(A(maxRow*8 + i)) : maxRow = k : EndIf
    Next
    If maxRow <> i
      For  j = 0 To 7
        Protected tmp.f = A(i*8 + j)
        A(i*8 + j) = A(maxRow*8 + j)
        A(maxRow*8 + j) = tmp
      Next
      tmp.f = b(i)
      b(i) = b(maxRow)
      b(maxRow) = tmp
    EndIf
    
    Protected pivot.f = A(i*8 + i)
    If pivot = 0.0 : ProcedureReturn : EndIf
    
    For  j = i To 7
      A(i*8 + j) / pivot
    Next
    b(i) / pivot
    
    For  k = 0 To 7
      If k <> i
        Protected factor.f = A(k*8 + i)
        For  j = i To 7
          A(k*8 + j) - factor * A(i*8 + j)
        Next
        b(k) - factor * b(i)
      EndIf
    Next
  Next
  
  For i = 0 To 7
    H(i) = b(i)
  Next
  H(8) = 1.0
  
  Protected denom.f, u.f, v.f
  Protected sx.l, sy.l, posSrc.l, posDst.l
  Protected x , y
  
  For y = 0 To dstHeight - 1
    For x = 0 To dstWidth - 1
      If PointInQuad(x, y, ptsDst())
        denom = H(6)*x + H(7)*y + H(8)
        If denom <> 0.0
          u = (H(0)*x + H(1)*y + H(2)) / denom
          v = (H(3)*x + H(4)*y + H(5)) / denom
          
          If u >= 0 And u < srcWidth And v >= 0 And v < srcHeight
            sx = Int(u)
            sy = Int(v)
            posSrc = (sy * srcWidth + sx) * 4
            posDst = (y * dstWidth + x) * 4
            PokeL((*cible) + posDst, PeekL((*source) + posSrc))
          Else
            posDst = (y * dstWidth + x) * 4
            PokeL((*cible) + posDst, $FF000000)
          EndIf
        EndIf
      Else
        posDst = (y * dstWidth + x) * 4
        PokeL((*cible) + posDst, $FF000000)
      EndIf
    Next
  Next
  
EndProcedure


Procedure Perspective(*param.parametre)
  Protected i
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Perspective"
    param\remarque = "Déformation quadrilatérale (4 coins)"
    
    param\info[0] = "Haut gauche X"
    param\info[1] = "Haut gauche Y"
    param\info[2] = "Haut droite X"
    param\info[3] = "Haut droite Y"
    param\info[4] = "Bas gauche X"
    param\info[5] = "Bas gauche Y"
    param\info[6] = "Bas droite X"
    param\info[7] = "Bas droite Y"
    param\info[8] = "Masque binaire"
    For i = 0 To 7
      param\info_data(i, 0) = 0
      param\info_data(i, 1) = 100
      param\info_data(i, 2) = 50
    Next
    
    param\info_data(8, 0) = 0 : param\info_data(8, 1) = 2 : param\info_data(8, 2) = 0
    ProcedureReturn
  EndIf
  
  filter_start(@Perspective_MT() , 8)
EndProcedure

;-----------------------

Procedure Perspective4Borders_MT(*p.parametre)
  Protected start, stop
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  
  Protected tiltTop.f    = (*p\option[0] - 100) / 100.0
  Protected tiltBottom.f = (*p\option[1] - 100) / 100.0
  Protected tiltLeft.f   = (*p\option[2] - 100) / 100.0
  Protected tiltRight.f  = (*p\option[3] - 100) / 100.0
  Protected scaleGlobal.f = *p\option[4] / 100.0
  If scaleGlobal <= 0.01 : scaleGlobal = 0.01 : EndIf
  Protected shiftX.f = ((*p\option[5] - 100) * lg) /100
  Protected shiftY.f = ((*p\option[6] - 100) * ht) /100
  Protected angle.f = Radian(*p\option[7])
  Protected cosA.f = Cos(angle)
  Protected sinA.f = Sin(angle)  
  Clamp(tiltTop, -1.0, 1.0)
  Clamp(tiltBottom, -1.0, 1.0)
  Clamp(tiltLeft, -1.0, 1.0)
  Clamp(tiltRight, -1.0, 1.0)
  
  Protected y, x
  Protected u.f, v.f
  Protected scaleX.f, scaleY.f
  Protected src_x.f, src_y.f
  Protected ligne_source, ligne_cible
  Protected pix.l
  
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = (( *p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    v = y / (ht - 1)
    scaleX = 1.0 - ((1.0 - v) * tiltTop + v * tiltBottom)
    
    For x = 0 To lg - 1
      u = x / (lg - 1)
      scaleY = 1.0 - ((1.0 - u) * tiltLeft + u * tiltRight)
      
      If scaleX <= 0.01 : scaleX = 0.01 : EndIf
      If scaleY <= 0.01 : scaleY = 0.01 : EndIf
      
      Protected tmp_x.f, tmp_y.f
      
      tmp_x = ((x - (lg / 2)) / (scaleX * scaleGlobal)) + shiftX
      tmp_y = ((y - (ht / 2)) / (scaleY * scaleGlobal)) + shiftY
      
      src_x = tmp_x * cosA - tmp_y * sinA + (lg / 2)
      src_y = tmp_x * sinA + tmp_y * cosA + (ht / 2)
      
      If src_x >= 0 And src_x < lg And src_y >= 0 And src_y < ht
        ligne_source = *source + (Int(src_y) * lg + Int(src_x)) * 4
        pix = PeekL(ligne_source)
      Else
        pix = 0
      EndIf
      
      ligne_cible = *cible + (y * lg + x) * 4
      PokeL(ligne_cible, pix)
    Next
  Next
EndProcedure


Procedure Perspective2(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Perspective2"
    param\remarque = "Effet trapèze indépendant sur 4 bords"
    param\info[0] = "haut"
    param\info[1] = "bas"
    param\info[2] = "gauche"
    param\info[3] = "droite"
    param\info[4] = "Zoom"
    param\info[5] = "Pos X"
    param\info[6] = "Pos Y"
    param\info[7] = "Rotation"
    param\info[8] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 200 : param\info_data(0,2) = 100
    param\info_data(1,0) = 0 : param\info_data(1,1) = 200 : param\info_data(1,2) = 100
    param\info_data(2,0) = 0 : param\info_data(2,1) = 200 : param\info_data(2,2) = 100
    param\info_data(3,0) = 0 : param\info_data(3,1) = 200 : param\info_data(3,2) = 100
    param\info_data(4,0) = 0 : param\info_data(4,1) = 200 : param\info_data(4,2) = 100
    param\info_data(5,0) = 0 : param\info_data(5,1) = 200 : param\info_data(5,2) = 100
    param\info_data(6,0) = 0 : param\info_data(6,1) = 200 : param\info_data(6,2) = 100
    param\info_data(7,0) = 0 : param\info_data(7,1) = 360 : param\info_data(7,2) = 0
    param\info_data(8,0) = 0 : param\info_data(8,1) = 2   : param\info_data(8,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Perspective4Borders_MT() , 8)
EndProcedure

;-----------------------
;-----------------------
Procedure PerspectiveTrapezeLin_MT(*p.parametre)
  Protected x, y , x1.f , y1.f
  Protected sx.f, sy.f, u.f, v.f
  Protected *srcPixel.LONG, *dstPixel.LONG
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  
  Protected offsetY_Gauche.f = ((50.0 - *p\option[0]) / 50.0) * (ht * 0.5)
  Protected offsetY_Droite.f = ((50.0 - *p\option[1]) / 50.0) * (ht * 0.5)
  
  Protected offsetX_HautGauche.f  = ((50.0 - *p\option[2]) / 50.0) * (lg * 0.5)
  Protected offsetX_HautDroit.f   = -offsetX_HautGauche
  
  Protected offsetX_BasGauche.f   = ((50.0 - *p\option[3]) / 50.0) * (lg * 0.5)
  Protected offsetX_BasDroit.f    = -offsetX_BasGauche
  
  Protected x00.f = 0 + offsetX_HautGauche
  Protected y00.f = 0 - offsetY_Gauche
  
  Protected x10.f = (lg - 1) + offsetX_HautDroit
  Protected y10.f = 0 - offsetY_Droite
  
  Protected x01.f = 0 + offsetX_BasGauche
  Protected y01.f = (ht - 1) + offsetY_Gauche
  
  Protected x11.f = (lg - 1) + offsetX_BasDroit
  Protected y11.f = (ht - 1) + offsetY_Droite
  
  Protected startY = (*p\thread_pos * ht) / *p\thread_max
  Protected stopY  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht - 1 : EndIf
  
  For y = startY To stopY
    v = y / (ht - 1)
    
    Protected borderLeftX  = x00 
    Protected borderLeftY  = y00 + v * (y01 - y00)
    
    Protected borderRightX = x10 
    Protected borderRightY = y10 + v * (y11 - y10)
    
    For x = 0 To lg - 1
      u = x / (lg - 1)
      
      sx = borderLeftX + u * (borderRightX - borderLeftX)
      sy = borderLeftY + u * (borderRightY - borderLeftY)
      
      *dstPixel = *cible + (y * lg + x) * 4
      
      If sx >= 0 And sx < lg And sy >= 0 And sy < ht
        *srcPixel = *source + (Int(sy) * lg + Int(sx)) * 4
        *dstPixel\l = *srcPixel\l
      Else
        *dstPixel\l = 0
      EndIf
    Next
  Next
EndProcedure

Procedure DrawTexturePerspective_MT(*p.parametre)
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected *srcPixel.LONG, *dstPixel.LONG
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected x, y

  Define.f x0 = ((*p\option[0] - 50.0) / 50.0) * (lg * 0.5) + 0
  Define.f y0 = ((*p\option[1] - 50.0) / 50.0) * (ht * 0.5) + 0
  
  Define.f x1 = ((*p\option[2] - 50.0) / 50.0) * (lg * 0.5) + (lg - 1)
  Define.f y1 = ((*p\option[3] - 50.0) / 50.0) * (ht * 0.5) + 0
  
  Define.f x2 = ((*p\option[4] - 50.0) / 50.0) * (lg * 0.5) + (lg - 1)
  Define.f y2 = ((*p\option[5] - 50.0) / 50.0) * (ht * 0.5) + (ht - 1)
  
  Define.f x3 = ((*p\option[6] - 50.0) / 50.0) * (lg * 0.5) + 0
  Define.f y3 = ((*p\option[7] - 50.0) / 50.0) * (ht * 0.5) + (ht - 1)
  
  Define.f dx1 = x1 - x2, dx2 = x3 - x2, dx3 = x0 - x1 + x2 - x3
  Define.f dy1 = y1 - y2, dy2 = y3 - y2, dy3 = y0 - y1 + y2 - y3
  
  Define.f det = dx1 * dy2 - dx2 * dy1
  If det = 0 : ProcedureReturn : EndIf
  
  Define.f a13 = (dx3 * dy2 - dx2 * dy3) / det
  Define.f a23 = (dx1 * dy3 - dx3 * dy1) / det
  
  Define.f h11 = x1 - x0 + a13 * x1
  Define.f h12 = x3 - x0 + a23 * x3
  Define.f h13 = x0
  Define.f h21 = y1 - y0 + a13 * y1
  Define.f h22 = y3 - y0 + a23 * y3
  Define.f h23 = y0
  Define.f h31 = a13
  Define.f h32 = a23
  Define.f h33 = 1.0

  Define startY = (*p\thread_pos * ht) / *p\thread_max
  Define stopY  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht - 1 : EndIf
  
  For y = startY To stopY
    For x = 0 To lg - 1
      X1 = x:Y1 = y
      Define.f denom = h31 * X1 + h32 * Y1 + h33
      If denom = 0 : Continue : EndIf
      
      Define.f u = (h11 * X1 + h12 * Y1 + h13) / denom
      Define.f v = (h21 * X1 + h22 * Y1 + h23) / denom

      If u >= 0 And u <= lg - 1 And v >= 0 And v <= ht - 1
        *dstPixel = *cible + (y * lg + x) * 4
        *srcPixel = *source + (Int(v) * lg + Int(u)) * 4
        *dstPixel\l = *srcPixel\l
      EndIf
    Next
  Next
EndProcedure

Procedure PerspectiveSimple(*param.parametre)
  Protected i
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "PerspectiveSimple"
    param\remarque = "Déformation simple : décalage gauche, droite, haut, bas"
    param\info[0] = "Décalage Gauche"
    param\info[1] = "Décalage Droite"
    param\info[2] = "Décalage Haut"
    param\info[3] = "Décalage Bas"
    param\info[4] = "Masque binaire"
    For i = 0 To 3
      param\info_data(i, 0) = 0
      param\info_data(i, 1) = 100
      param\info_data(i, 2) = 50 ; Valeur par défaut = 50%, pas de décalage
    Next
    param\info_data(4, 0) = 0 : param\info_data(4, 1) = 2 : param\info_data(4, 2) = 0
    ProcedureReturn
  EndIf
  
  filter_start(@PerspectiveTrapezeLin_MT() , 4)
EndProcedure
;-----------------------
;-----------------------

Procedure PinchBulge_MT(*p.parametre)
  Protected start, stop
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected cx.f = (*p\option[1] * lg) / 100
  Protected cy.f = (*p\option[2] * ht) / 100
  Protected rayon.f = ((Sqr(lg * lg + ht * ht) * *p\option[3]) / 100) + 1
  Protected force.f = (*p\option[0]) / 100.0 
  
  Protected x, y
  Protected dx.f, dy.f, dist.f, factor.f
  Protected src_x.f, src_y.f
  Protected ligne_source, ligne_cible
  Protected pix.l
  
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = (( *p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    For x = 0 To lg - 1
      dx = x - cx
      dy = y - cy
      dist = Sqr(dx * dx + dy * dy)
      
      If dist < rayon And dist > 0
        factor = Pow(dist / rayon, 1.0 - force)

        src_x = cx + dx * factor
        src_y = cy + dy * factor
      Else
        src_x = x
        src_y = y
      EndIf
      
      If src_x >= 0 And src_x < lg And src_y >= 0 And src_y < ht
        ligne_source = *source + (Int(src_y) * lg + Int(src_x)) * 4
        pix = PeekL(ligne_source)
      Else
        pix = 0
      EndIf
      
      ligne_cible = *cible + (y * lg + x) * 4
      PokeL(ligne_cible, pix)
    Next
  Next
EndProcedure

Procedure PinchBulge(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Pinch / Bulge"
    param\remarque = "Déformation en pincement ou bombement"
    param\info[0] = "Force (-100 à 100)"
    param\info[1] = "PosX (%)"
    param\info[2] = "PosY (%)"
    param\info[3] = "Rayon (%)"
    param\info[4] = "Masque binaire"
    param\info_data(0,0) = -100 : param\info_data(0,1) = 100 : param\info_data(0,2) = 0
    param\info_data(1,0) = 0    : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 0    : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 0    : param\info_data(3,1) = 100 : param\info_data(3,2) = 30
    param\info_data(4,0) = 0    : param\info_data(4,1) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  
  filter_start(@PinchBulge_MT() , 4)
EndProcedure

;-----------------------

Procedure Ripple_MT(*p.parametre)
  Protected start, stop
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  
  Protected amp_x.f = *p\option[0] / 1.0       ; Amplitude horizontale
  Protected period_x.f = (*p\option[1] / 100.0) * ht    ; Période horizontale
  
  Protected amp_y.f = *p\option[2] / 1.0       ; Amplitude verticale
  Protected period_y.f = (*p\option[3] / 100.0) * lg   ; Période verticale
  
  Protected x, y
  Protected offset_x.f, offset_y.f
  Protected src_x, src_y
  Protected ligne_source, ligne_cible
  Protected pix.l
  
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = (( *p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    For x = 0 To lg - 1
      offset_x = amp_x * Sin((y / period_x) * 2 * #PI)
      offset_y = amp_y * Sin((x / period_y) * 2 * #PI)
      
      src_x = x + offset_x
      src_y = y + offset_y
      
      If src_x >= 0 And src_x < lg And src_y >= 0 And src_y < ht
        ligne_source = *source + (Int(src_y) * lg + Int(src_x)) * 4
        pix = PeekL(ligne_source)
      Else
        pix = 0
      EndIf
      
      ligne_cible = *cible + (y * lg + x) * 4
      PokeL(ligne_cible, pix)
    Next
  Next
EndProcedure

Procedure Ripple(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Ripple"
    param\remarque = "Déformation sinusoïdale"
    param\info[0] = "Amplitude X"
    param\info[1] = "Période X"
    param\info[2] = "Amplitude Y"
    param\info[3] = "Période Y"
    param\info[4] = "Masque binaire"
    
    param\info_data(0,0) = 0   : param\info_data(0,1) = 100 : param\info_data(0,2) = 0
    param\info_data(1,0) = 1   : param\info_data(1,1) = 100 : param\info_data(1,2) = 1
    param\info_data(2,0) = 0   : param\info_data(2,1) = 100 : param\info_data(2,2) = 0
    param\info_data(3,0) = 1   : param\info_data(3,1) = 100 : param\info_data(3,2) = 1
    param\info_data(4,0) = 0   : param\info_data(4,1) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  
  filter_start(@Ripple_MT() , 4)
EndProcedure

;-----------------------

Procedure Rotation_MT(*p.parametre)
  Protected x, y, sx, sy, dx.f, dy.f
  Protected pix.l
  Protected *srcPixel.LONG, *dstPixel.LONG
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  
  Protected angle.f = *p\option[0] * #PI / 180.0 ; Angle en radians
  Protected cosA.f = Cos(angle)
  Protected sinA.f = Sin(angle)
  
  Protected cx.f = *p\option[1] / 100 * lg
  Protected cy.f = *p\option[2] / 100 * ht
  
  Protected startY = ( *p\thread_pos * ht ) / *p\thread_max
  Protected stopY  = ( (*p\thread_pos + 1) * ht ) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht -1 : EndIf
  
  For y = startY To stopY
    For x = 0 To lg - 1
      ; Calcul position dans l'image source
      dx.f = x - cx
      dy.f = y - cy
      
      sx = Round( cosA * dx + sinA * dy + cx, #PB_Round_Nearest )
      sy = Round(-sinA * dx + cosA * dy + cy, #PB_Round_Nearest )
      
      If sx >= 0 And sx < lg And sy >= 0 And sy < ht
        *srcPixel = *source + (sy * lg + sx) * 4
        *dstPixel = *cible  + (y  * lg + x ) * 4
        *dstPixel\l = *srcPixel\l
      Else
        *dstPixel = *cible  + (y  * lg + x ) * 4
        *dstPixel\l = 0 ; Transparent ou noir
      EndIf
    Next
  Next
EndProcedure

Procedure Rotate(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Rotation"
    param\remarque = "Rotation d'image de 0 à 360°"
    param\info[0] = "Angle"
    param\info[1] = "PosX"
    param\info[2] = "PosY"
    param\info[3] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 360 : param\info_data(0,2) = 0
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 0 : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 0 : param\info_data(3,1) = 2 : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Rotation_MT() , 3)
EndProcedure

;-----------------------

Procedure Spherize_MT(*p.parametre)
  Protected start, stop
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected cx.f = (*p\option[1] * lg) / 100
  Protected cy.f = (*p\option[2] * ht) / 100
  Protected rayon.f = ((Sqr(lg * lg + ht * ht) * *p\option[3])/100) + 1

  Protected force.f = ((*p\option[0] - 200.0) / 100.0)
  Protected x, y
  Protected dx.f, dy.f, r.f, angle.f, facteur.f
  Protected src_x.f, src_y.f
  Protected pix.l
  Protected ligne_source, ligne_cible
  
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = (( *p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    For x = 0 To lg - 1
      dx = (x - cx) / rayon
      dy = (y - cy) / rayon
      r = Sqr(dx*dx + dy*dy)
      
      If r <= 1.0
        angle = r * #PI / 2
        facteur = Pow(Sin(angle), 1 + force)
        src_x = cx + dx * facteur * rayon
        src_y = cy + dy * facteur * rayon
      Else
        src_x = x
        src_y = y
      EndIf
      
      If src_x >= 0 And src_x < lg And src_y >= 0 And src_y < ht
        ligne_source = *source + (Int(src_y) * lg + Int(src_x)) * 4
        pix = PeekL(ligne_source)
      Else
        pix = 0
      EndIf
      
      ligne_cible = *cible + (y * lg + x) * 4
      PokeL(ligne_cible, pix)
    Next
  Next
EndProcedure

Procedure Spherize(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Spherize"
    param\remarque = "Effet lentille convexe ou concave"
    param\info[0] = "Force"
    param\info[1] = "PosX"
    param\info[2] = "PosY"
    param\info[3] = "Rayon"
    param\info[4] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 600 : param\info_data(0,2) = 100
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 0 : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 0 : param\info_data(3,1) = 100 : param\info_data(3,2) = 50
    param\info_data(4,0) = 0 : param\info_data(4,1) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Spherize_MT() , 4)
EndProcedure

;-----------------------

Procedure Spiralize_MT(*p.parametre)
  Protected start, stop
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  
  Protected cx.f = (*p\option[1] * lg) / 100
  Protected cy.f = (*p\option[2] * ht) / 100
  Protected rayon.f = ((Sqr(lg * lg + ht * ht) * *p\option[3]) / 100) + 1
  Protected angle_max.f = (*p\option[0] - 1000) * #PI / 180.0 
  
  Protected x, y
  Protected dx.f, dy.f, r.f, a.f, new_a.f
  Protected src_x.f, src_y.f
  Protected ligne_source, ligne_cible
  Protected pix.l
  
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = (( *p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    For x = 0 To lg - 1
      dx = x - cx
      dy = y - cy
      r = Sqr(dx * dx + dy * dy)
      
      If r <= rayon
        a = ATan2(dy, dx) + #PI/2
        new_a = a + angle_max * (1.0 - (r / rayon))
        
        src_x = cx + r * Cos(new_a)
        src_y = cy + r * Sin(new_a)
      Else
        src_x = x
        src_y = y
      EndIf
      
      If src_x >= 0 And src_x < lg And src_y >= 0 And src_y < ht
        ligne_source = *source + (Int(src_y) * lg + Int(src_x)) * 4
        pix = PeekL(ligne_source)
      Else
        pix = 0
      EndIf
      
      ligne_cible = *cible + (y * lg + x) * 4
      PokeL(ligne_cible, pix)
    Next
  Next
EndProcedure


Procedure Spiralize(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Spiralize"
    param\remarque = "Déformation en spirale"
    param\info[0] = "Rotation"     ; de 0 à 200 (100 = neutre)
    param\info[1] = "PosX"
    param\info[2] = "PosY"
    param\info[3] = "Rayon"
    param\info[4] = "Masque binaire"
    
    param\info_data(0,0) = 0   : param\info_data(0,1) = 2000 : param\info_data(0,2) = 1000
    param\info_data(1,0) = 0   : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 0   : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 0   : param\info_data(3,1) = 100 : param\info_data(3,2) = 50
    param\info_data(4,0) = 0   : param\info_data(4,1) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Spiralize_MT() , 4)
EndProcedure

;-----------------------

Procedure Tile_MT(*p.parametre)
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  
  Protected tilesX = *p\option[0]
  Protected tilesY = *p\option[1]

  Protected x, y
  Protected src_x, src_y
  Protected ligne_source, ligne_cible
  Protected pix.l

  Protected start, stop
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf

  For y = start To stop
    For x = 0 To lg - 1
      src_x = Mod(x * tilesX, lg)
      src_y = Mod(y * tilesY, ht)
      ligne_source = *source + (src_y * lg + src_x) * 4
      ligne_cible  = *cible  + (y * lg + x) * 4
      pix = PeekL(ligne_source)
      PokeL(ligne_cible, pix)
    Next
  Next
EndProcedure

Procedure Tile(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Tile"
    param\remarque = "Répétition en mosaïque"
    param\info[0] = "Nb Horizontal"
    param\info[1] = "Nb Vertical"
    param\info[2] = "Masque binaire"
    
    param\info_data(0,0) = 1   : param\info_data(0,1) = 100 : param\info_data(0,2) = 3
    param\info_data(1,0) = 1   : param\info_data(1,1) = 100 : param\info_data(1,2) = 3
    param\info_data(2,0) = 0   : param\info_data(2,1) = 2   : param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  Protected *tempo = 0
  *tempo = AllocateMemory(*param\lg * *param\ht * 4)
  If Not *tempo : ProcedureReturn : EndIf
  CopyMemory(*param\source , *tempo , *param\lg * *param\ht * 4)
  *param\addr[0] = *tempo
  *param\addr[1] = *param\cible    
  MultiThread_MT(@Tile_MT())
  If *param\mask And *param\option[2] : *param\mask_type = *param\option[2] - 1 : MultiThread_MT(@_mask()) : EndIf
  If *tempo : FreeMemory(*tempo) : EndIf

EndProcedure

;-----------------------

Procedure Translate_MT(*p.parametre)
  Protected start, stop
  Protected pix.l
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg      = *p\lg
  Protected ht      = *p\ht
  Protected dx, dy
  Protected x, y, src_x, src_y
  Protected ligne_source, ligne_cible
  
  dx = ((*p\option[0]-100) * lg) / 100
  dy = ((*p\option[1]-100) * ht) / 100
  
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = (( *p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    For x = 0 To lg - 1
      src_x = x - dx
      src_y = y - dy
      
      If src_x >= 0 And src_x < lg And src_y >= 0 And src_y < ht
        ligne_source = *source + (src_y * lg + src_x) * 4
        pix = PeekL(ligne_source)
      Else
        If *p\option[2] 
          pix = 0
        Else
          If src_x >= lg : src_x = src_x - lg : EndIf
          If src_x < 0 : src_x = src_x + lg : EndIf
          If src_y >= ht : src_y = src_y - ht : EndIf
          If src_y < 0 : src_y = src_y + ht : EndIf
          ligne_source = *source + (src_y * lg + src_x) * 4
          pix = PeekL(ligne_source)
        EndIf
      EndIf     
      ligne_cible = *cible + (y * lg + x) * 4
      PokeL(ligne_cible, pix)
    Next
  Next
EndProcedure

Procedure Translate(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Translate"
    param\remarque = "Translation en pourcentage + mode"
    param\info[0] = "Décalage X (%)"
    param\info[1] = "Décalage Y (%)"
    param\info[2] = "Rot/Trans"
    param\info[3] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 200 : param\info_data(0,2) = 100
    param\info_data(1,0) = 0 : param\info_data(1,1) = 200 : param\info_data(1,2) = 100
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1   : param\info_data(2,2) = 1
    param\info_data(3,0) = 0 : param\info_data(3,1) = 2   : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Translate_MT() , 3)
  
EndProcedure

;-----------------------

Procedure WaveCircular_MT(*p.parametre)
  Protected start, stop
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  
  Protected cx.f = (*p\option[1] * lg) / 100
  Protected cy.f = (*p\option[2] * ht) / 100
  Protected amplitude.f = *p\option[0]
  Protected wavelength.f = (*p\option[3] / 100.0) * Sqr(lg * lg + ht * ht)
  Protected phase.f = (*p\option[4] / 360.0) * 2 * #PI   
  
  Protected x, y
  Protected dx.f, dy.f, r.f
  Protected offset.f
  Protected src_x.f, src_y.f
  Protected ligne_source, ligne_cible
  Protected pix.l
  
  start = (*p\thread_pos * ht) / *p\thread_max
  stop  = (( *p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    For x = 0 To lg - 1
      dx = x - cx
      dy = y - cy
      r = Sqr(dx*dx + dy*dy)
      offset = amplitude * Sin((2 * #PI * r / wavelength) + phase)
      If r <> 0
        src_x = cx + dx * (1 + offset / r)
        src_y = cy + dy * (1 + offset / r)
      Else
        src_x = x
        src_y = y
      EndIf
      
      If src_x >= 0 And src_x < lg And src_y >= 0 And src_y < ht
        ligne_source = *source + (Int(src_y) * lg + Int(src_x)) * 4
        pix = PeekL(ligne_source)
      Else
        pix = 0
      EndIf
      
      ligne_cible = *cible + (y * lg + x) * 4
      PokeL(ligne_cible, pix)
    Next
  Next
EndProcedure

Procedure WaveCircular(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Wave Circular"
    param\remarque = "Ondulations concentriques"
    param\info[0] = "Amplitude"
    param\info[1] = "Centre X (%)"
    param\info[2] = "Centre Y (%)"
    param\info[3] = "Longueur d’onde (%)"
    param\info[4] = "Phase (°)"
    param\info[5] = "Masque binaire"
    
    param\info_data(0,0) = 0    : param\info_data(0,1) = 100 : param\info_data(0,2) = 10
    param\info_data(1,0) = 0    : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 0    : param\info_data(2,1) = 100 : param\info_data(2,2) = 50
    param\info_data(3,0) = 1    : param\info_data(3,1) = 100 : param\info_data(3,2) = 20
    param\info_data(4,0) = 0    : param\info_data(4,1) = 360 : param\info_data(4,2) = 0
    param\info_data(5,0) = 0    : param\info_data(5,1) = 2   : param\info_data(5,2) = 0
    ProcedureReturn
  EndIf
  
  filter_start(@WaveCircular_MT() , 5)
EndProcedure


;-----------------------

Procedure deform_Bend_MT(*p.parametre)
  Protected start, stop, y, x
  Protected newX, newY, offsetX, offsetY
  Protected amplitudeX.f = *p\option[0]
  Protected frequencyX.f = *p\option[1] / 1000
  Protected amplitudeY.f = *p\option[2]
  Protected frequencyY.f = *p\option[3] / 1000
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected *src_pixel, *dst_pixel

  start = ( *p\thread_pos * ht ) / *p\thread_max
  stop  = ( (*p\thread_pos + 1) * ht ) / *p\thread_max - 1
  If stop > ht - 1 : stop = ht -1 : EndIf

  For y = start To stop
    For x = 0 To lg - 1
      offsetX = Int(Sin(y * frequencyX) * amplitudeX)
      offsetY = Int(Sin(x * frequencyY) * amplitudeY)
      newX = x + offsetX
      newY = y + offsetY

      If newX >= 0 And newX < lg And newY >= 0 And newY < ht
        *src_pixel = *p\addr[0] + (newY * lg + newX) * 4
        *dst_pixel = *p\addr[1] + (y * lg + x) * 4
        CopyMemory(*src_pixel, *dst_pixel, 4)
      Else
        PokeL(*p\addr[1] + (y * lg + x) * 4, 0)
      EndIf
    Next
  Next
EndProcedure

Procedure deform_Bend(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_Deform
    param\name = "Bend2D"
    param\remarque = "Courbure bidirectionnelle (X & Y)"
    param\info[0] = "Amplitude X"
    param\info[1] = "Fréquence X"
    param\info[2] = "Amplitude Y"
    param\info[3] = "Fréquence Y"
    param\info[4] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100 : param\info_data(0,2) = 20
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1000 : param\info_data(1,2) = 200
    param\info_data(2,0) = 0 : param\info_data(2,1) = 100 : param\info_data(2,2) = 20
    param\info_data(3,0) = 0 : param\info_data(3,1) = 1000 : param\info_data(3,2) = 200
    param\info_data(4,0) = 0 : param\info_data(4,1) = 2 : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf

  filter_start(@deform_Bend_MT(), 4)
EndProcedure

;-----------------------


manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

les filtres "mix"

mix l'image 1 avec l'image 2

mix.bpi

Code : Tout sélectionner

Macro Filtre_entete_mix(nom) 
  If param\info_active
    param\typ = #Filter_Type_mix
    param\name = nom
    param\remarque = ""         
    param\info[0] = "invert image"   
    param\info[1] = "neg image 1"
    param\info[2] = "neg image 2"
    param\info[3] = "scaleX image 2"
    param\info[4] = "scaleX image 2"
    param\info[5] = "PosX image 2"
    param\info[6] = "Posy image 2"
    param\info[7] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 1  : param\info_data(0,2) = 0 
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1  : param\info_data(1,2) = 0 
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1  : param\info_data(2,2) = 0 
    param\info_data(3,0) = 0 : param\info_data(3,1) = 100  : param\info_data(3,2) = 100 
    param\info_data(4,0) = 0 : param\info_data(4,1) = 100  : param\info_data(4,2) = 100
    param\info_data(5,0) = 0 : param\info_data(5,1) = 200  : param\info_data(5,2) = 100
    param\info_data(6,0) = 0 : param\info_data(6,1) = 200  : param\info_data(6,2) = 100
    param\info_data(7,0) = 0 : param\info_data(7,1) = 2  : param\info_data(7,2) = 0
    ProcedureReturn
  EndIf
  
  Protected *tempo
  If *param\source = *param\cible
    *tempo = AllocateMemory(*param\lg * *param\ht * 4)
    If Not *tempo : ProcedureReturn : EndIf
    CopyMemory(*param\source , *tempo , *param\lg * *param\ht * 4)
    *param\addr[0] = *tempo
  Else
    *param\addr[0] = *param\source
  EndIf
  
  *param\addr[1] = *param\source2
  If *param\option[0]
    Swap *param\addr[0] , *param\addr[1]
  EndIf
  Protected var = 7 ; = "Masque binaire"
EndMacro

Macro Filtre_entete_mix2(nom,op1) 
  If param\info_active
    param\typ = #Filter_Type_mix
    param\name = nom
    param\remarque = ""   
    param\info[0] = "invert image" 
    param\info[1] = "neg image 1"
    param\info[2] = "neg image 2"
    param\info[3] = "scaleX image 2"
    param\info[4] = "scaleX image 2"
    param\info[5] = "PosX image 2"
    param\info[6] = "Posy image 2"
    param\info[7] = op1   
    param\info[8] = "Masque binaire" 
    param\info_data(0,0) = 0 : param\info_data(0,1) = 1  : param\info_data(0,2) = 0 
    param\info_data(1,0) = 0 : param\info_data(1,1) = 1  : param\info_data(1,2) = 0 
    param\info_data(2,0) = 0 : param\info_data(2,1) = 1  : param\info_data(2,2) = 0 
    param\info_data(3,0) = 0 : param\info_data(3,1) = 100  : param\info_data(3,2) = 100 
    param\info_data(4,0) = 0 : param\info_data(4,1) = 100  : param\info_data(4,2) = 100
    param\info_data(5,0) = 0 : param\info_data(5,1) = 200  : param\info_data(5,2) = 100
    param\info_data(6,0) = 0 : param\info_data(6,1) = 200  : param\info_data(6,2) = 100
    param\info_data(7,0) = 0 : param\info_data(7,1) = 255  : param\info_data(7,2) = 128
    param\info_data(8,0) = 0 : param\info_data(8,1) = 2  : param\info_data(8,2) = 0
    ProcedureReturn
  EndIf
  
  If *param\source = 0 Or *param\source2 = 0 Or  *param\cible = 0 : ProcedureReturn : EndIf
  
  Protected *tempo
  If *param\source = *param\cible
    *tempo = AllocateMemory(*param\lg * *param\ht * 4)
    If Not *tempo : ProcedureReturn : EndIf
    CopyMemory(*param\source , *tempo , *param\lg * *param\ht * 4)
    *param\addr[0] = *tempo
  Else
    *param\addr[0] = *param\source
  EndIf
  
  *param\addr[1] = *param\source2
  If *param\option[0]
    Swap *param\addr[0] , *param\addr[1]
  EndIf
  Protected var = 8 ; = "Masque binaire"
EndMacro


Macro Filtre2_start()
  Protected *src1.Pixel32 = *param\source
  Protected *src2.Pixel32 = *param\source2
  Protected *dst.Pixel32 = *param\cible
  
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected scale_x = *param\option[3]; max 100% = lg
  Protected scale_y = *param\option[4] ; max 100% = ht
  Protected posX_start = ((*param\option[5]-100) * lg) / 100
  Protected posY_start = ((*param\option[6]-100) * ht) / 100
  Protected lg2 = (lg * scale_x) / 100
  Protected ht2 = (ht * scale_y) / 100
  Protected posX_end
  Protected posY_end
  Min(posX_end , lg, (posX_start + lg2))
  Min(posY_end , ht, (posY_start + ht2))
  
  Protected cx = lg2 / 2
  Protected cy = ht2 / 2
  
  Protected dx
  Protected dy
  
  Protected x , y , x1 , y1 , pos , pos2
  Protected a , r , g , b 
  Protected a1 , r1 , g1 , b1
  Protected a2 , r2 , g2 , b2
  
  Protected start = (*param\thread_pos * ht) / *param\thread_max
  Protected stop  = (( *param\thread_pos + 1) * ht) / *param\thread_max - 1
  If stop >= ht - 1 : stop = ht - 1 : EndIf
  
  For y = start To stop
    For x = 0 To lg -1
      pos = (y * lg + x) << 2
      *dst = *param\cible + pos
      *src1 = *param\addr[0] + pos
      getargb(*src1\l , a1 , r1 , g1 , b1)
      If *param\option[1] : r1 = 255 - r1 : g1 = 255 - g1 : b1 = 255 - b1 : EndIf
      
      If x >= posX_start And y >= posY_start And x < posX_end And y < posY_end

          x1 = ((x - posX_start) * lg) / lg2
          y1 = ((y - posY_start) * ht) / ht2
          pos2 = (y1 * lg + x1) << 2
          *src2 = *param\addr[1] + pos2
          getargb(*src2\l , a2 , r2 , g2 , b2)
          If *param\option[2] : r2 = 255 - r2 : g2 = 255 - g2 : b2 = 255 - b2 : EndIf
EndMacro
  
 
Macro Filtre2_stop()
      *dst\l = (a1 <<24) | (r << 16 ) | (g << 8) | b
    Else
      *dst\l = (a1 <<24) | (r1 << 16 ) | (g1 << 8) | b1
    EndIf
    Next
  Next
EndMacro

Macro Filtre2_end()
  If *param\mask And *param\option[var] : *param\mask_type = *param\option[var] - 1 : MultiThread_MT(@_mask()) : EndIf
  If *tempo : FreeMemory(*tempo) : EndIf
EndMacro

;**************

Procedure Filtre2_additive_MT(*param.parametre)
  Filtre2_start()
  min(r , (r1 + r2) , 255)
  min(g , (g1 + g2) , 255)
  min(b , (b1 + b2) , 255)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_additive(*param.parametre)
  Filtre_entete_mix("Filtre2_additive")
  MultiThread_MT(@Filtre2_additive_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_additive_inverted_MT(*param.parametre)
  Filtre2_start()
  Min(r , (r2 + (255 - r1)), 255)
  Min(g , (g2 + (255 - g1)), 255)
  Min(b , (b2 + (255 - b1)), 255)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_additive_inverted(*param.parametre)
  Filtre_entete_mix("Filtre2_additive_inverted")
  MultiThread_MT(@Filtre2_additive_inverted_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_alphablend_MT(*param.parametre)
  Protected alpha = *param\option[7]
  clamp(alpha , 0 , 255)
  Protected inv_alpha = 255 - alpha
  Filtre2_start()
  r = (r1 * alpha + r2 * inv_alpha + 127) / 255
  g = (g1 * alpha + g2 * inv_alpha + 127) / 255
  b = (b1 * alpha + b2 * inv_alpha + 127) / 255
  Filtre2_stop()
EndProcedure

Procedure Filtre2_alphablend(*param.parametre)
  Filtre_entete_mix2("Filtre2_alphablend","alpa")
  MultiThread_MT(@Filtre2_alphablend_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_RMSColor_MT(*param.parametre)
  ;Filtre2_QuadraticBlend
  ;Filtre2_SquaredAverage
  Filtre2_start()
  r = (r1*r1*77 + r2*r2*77) >> 8
  g = (g1*g1*150 + g2*g2*150) >> 8
  b = (b1*b1*29 + b2*b2*29) >> 8
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_RMSColor(*param.parametre)
  Filtre_entete_mix("Filtre2_RMSColor")
  MultiThread_MT(@Filtre2_RMSColor_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_And_MT(*param.parametre)
  Filtre2_start()
  r = r1 & r2
  g = g1 & g2
  b = b1 & b2
  Filtre2_stop()
EndProcedure

Procedure Filtre2_And(*param.parametre)
  Filtre_entete_mix("Filtre2_And")
  MultiThread_MT(@Filtre2_And_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Average_MT(*param.parametre)
  Filtre2_start()
  r = (r1 + r2) >> 1
  g = (g1 + g2) >> 1
  b = (b1 + b2) >> 1
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Average(*param.parametre)
  Filtre_entete_mix("Filtre2_Average")
  MultiThread_MT(@Filtre2_Average_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_LightBlend_MT(*param.parametre)
  ;Filtre2_IntensityBlend
  ;Filtre2_WeightedBlend
  Filtre2_start()
  Protected v = r1 + g1 + b1
  r = ((r2 * v) + (r1 * v)) >> 11
  g = ((g2 * v) + (g1 * v)) >> 11
  b = ((b2 * v) + (b1 * v)) >> 11
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_LightBlend(*param.parametre)
  Filtre_entete_mix("Filtre2_LightBlend")
  MultiThread_MT(@Filtre2_LightBlend_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_IntensityBoost_MT(*param.parametre)
  ;Filtre2_PowerBlend
  ;Filtre2_Amplify
  Filtre2_start()
  r = r2 + ((r1 * r1 * r2) >> 16)
  g = g2 + ((g1 * g1 * g2) >> 16)
  b = b2 + ((b1 * b1 * b2) >> 16)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_IntensityBoost(*param.parametre)
  Filtre_entete_mix("Filtre2_IntensityBoost")
  MultiThread_MT(@Filtre2_IntensityBoost_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_BrushUp_MT(*param.parametre)
  Filtre2_start()
  Protected l1 = (r1 * 1225 + g1 * 2405 + b1 * 466) >> 12
  Protected l2 = (r2 * 1225 + g2 * 2405 + b2 * 466) >> 12
  r = (r1 * l2 + r2 * l1) >> 9
  g = (g1 * l2 + g2 * l1) >> 9
  b = (b1 * l2 + b2 * l1) >> 9
  ;Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_BrushUp(*param.parametre)
  Filtre_entete_mix("Filtre2_BrushUp")
  MultiThread_MT(@Filtre2_BrushUp_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Burn_MT(*param.parametre)
  Filtre2_start()
  r = 256 - ((256 - r2) << 8) / (r1 + 1)
  g = 256 - ((256 - g2) << 8) / (g1 + 1)
  b = 256 - ((256 - b2) << 8) / (b1 + 1)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Burn(*param.parametre)
  Filtre_entete_mix("Filtre2_Burn")
  MultiThread_MT(@Filtre2_Burn_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_SubtractiveDodge_MT(*param.parametre)
  ;Filtre2_LinearDodge
  Filtre2_start()
  Max(r , 0, (r2 - 255 + r1))
  Max(g , 0, (g2 - 255 + g1))
  Max(b , 0, (b2 - 255 + b1))
  Min(r , 255, (r2 - r))
  Min(g , 255, (g2 - g))
  Min(b , 255, (b2 - b))
  Filtre2_stop()
EndProcedure

Procedure Filtre2_SubtractiveDodge(*param.parametre)
  Filtre_entete_mix("Filtre2_SubtractiveDodge")
  MultiThread_MT(@Filtre2_SubtractiveDodge_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_ColorBurn_MT(*param.parametre)
  Filtre2_start()
  r = 0 : g = 0 : b = 0
  If r1 > 0 : r = 255 - (((255 - r2) << 8) / r1) : EndIf
  If g1 > 0 : g = 255 - (((255 - g2) << 8) / g1) : EndIf
  If b1 > 0 : b = 255 - (((255 - b2) << 8) / b1) : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_ColorBurn(*param.parametre)
  ; Partie en-tête + appel multi-thread
  Filtre_entete_mix("Filtre2_ColorBurn")
  MultiThread_MT(@Filtre2_ColorBurn_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_ColorDodge_MT(*param.parametre)
  Filtre2_start()
  r = 0 : g = 0 : b = 0
  If r1 < 255 : Min(r, ((r2 << 8) / (255 - r1)), 255) : EndIf
  If g1 < 255 : Min(g, ((g2 << 8) / (255 - g1)), 255) : EndIf
  If b1 < 255 : Min(b, ((b2 << 8) / (255 - b1)), 255) : EndIf
  Filtre2_stop()
EndProcedure

Procedure Filtre2_ColorDodge(*param.parametre)
  Filtre_entete_mix("Filtre2_ColorDodge")
  MultiThread_MT(@Filtre2_ColorDodge_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Contrast_MT(*param.parametre)
  Filtre2_start()
  r = 127 + ((r2 - 127) * r1) / 127
  g = 127 + ((g2 - 127) * g1) / 127
  b = 127 + ((b2 - 127) * b1) / 127
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Contrast(*param.parametre)
  Filtre_entete_mix("Filtre2_Contrast")
  MultiThread_MT(@Filtre2_Contrast_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Cosine_MT(*param.parametre)
  Protected Dim CosLUT(256) , j
  For j = 0 To 255 : CosLUT(j) = Int(Abs(Cos(j * 3.14159265 / 255)) * 255) : Next
  Filtre2_start()
  r = (CosLUT(r1) * r2) >> 8
  g = (CosLUT(g1) * g2) >> 8
  b = (CosLUT(b1) * b2) >> 8
  Clamp_RGB(r, g, b)
  Filtre2_stop()
  FreeArray(CosLUT())
EndProcedure

Procedure Filtre2_Cosine(*param.parametre)
  Filtre_entete_mix("Filtre2_Cosine")
  MultiThread_MT(@Filtre2_Cosine_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_CrossFading_MT(*param.parametre)
  Protected fading = *param\option[7]
  Filtre2_start()
  r = (r1 * fading + r2 * (255 - fading)) >> 8
  g = (g1 * fading + g2 * (255 - fading)) >> 8
  b = (b1 * fading + b2 * (255 - fading)) >> 8
  Filtre2_stop()
EndProcedure

Procedure Filtre2_CrossFading(*param.parametre)
  Filtre_entete_mix2("Filtre2_CrossFading","fading")
  MultiThread_MT(@Filtre2_CrossFading_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_InverseMultiply_MT(*param.parametre)
  Filtre2_start()
  r1 = 255 - r1
  g1 = 255 - g1
  b1 = 255 - b1
  r2 = 255 - r2
  g2 = 255 - g2
  b2 = 255 - b2
  r = (r1 * r1 * r2) / 65025
  g = (g1 * g1 * g2) / 65025
  b = (b1 * b1 * b2) / 65025
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_InverseMultiply(*param.parametre)
  Filtre_entete_mix("Filtre2_InverseMultiply")
  MultiThread_MT(@Filtre2_InverseMultiply_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Darken_MT(*param.parametre)
  Filtre2_start()
  r = r2
  g = g2
  b = b2
  If r1 < r2 : r = r1 : EndIf
  If g1 < g2 : g = g1 : EndIf
  If b1 < b2 : b = b1 : EndIf
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Darken(*param.parametre)
  Filtre_entete_mix("Filtre2_Darken")
  MultiThread_MT(@Filtre2_Darken_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_SubtractiveBlend_MT(*param.parametre)
  Filtre2_start()
  r = r2 - (255 - ((r1 * r2) >> 8))
  g = g2 - (255 - ((g1 * g2) >> 8))
  b = b2 - (255 - ((b1 * b2) >> 8))
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_SubtractiveBlend(*param.parametre)
  Filtre_entete_mix("Filtre2_SubtractiveBlend")
  MultiThread_MT(@Filtre2_SubtractiveBlend_MT())
  Filtre2_end() 
EndProcedure
;**************
Procedure Filtre2_Difference_MT(*param.parametre)
  Filtre2_start()
  r = Abs(r1 - r2)
  g = Abs(g1 - g2)
  b = Abs(b1 - b2)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Difference(*param.parametre)
  Filtre_entete_mix("Filtre2_Difference")
  MultiThread_MT(@Filtre2_Difference_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Div_MT(*param.parametre)
  Protected m = *param\option[7]
  Filtre2_start()
  r = r1 * m / (r2 + 1)
  g = g1 * m / (g2 + 1)
  b = b1 * m / (b2 + 1)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Div(*param.parametre)
  Filtre_entete_mix2("Filtre2_Div","mul")
  MultiThread_MT(@Filtre2_Div_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_SoftAdd_MT(*param.parametre)
  ;Filtre2_ScreenBlend
  ;Filtre2_LightenBlend
  Filtre2_start()
  r = (r1 + r2) - ((r1 * r2) >> 7)
  g = (g1 + g2) - ((g1 * g2) >> 7)
  b = (b1 + b2) - ((b1 * b2) >> 7)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_SoftAdd(*param.parametre)
  Filtre_entete_mix("Filtre2_SoftAdd")
  MultiThread_MT(@Filtre2_SoftAdd_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_SoftLightBoost_MT(*param.parametre)
  Filtre2_start()
  r = r2 + r1 * (r1 / 127.5 - 1)
  g = g2 + g1 * (g1 / 127.5 - 1)
  b = b2 + b1 * (b1 / 127.5 - 1)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_SoftLightBoost(*param.parametre)
  Filtre_entete_mix("Filtre2_SoftLightBoost")
  MultiThread_MT(@Filtre2_SoftLightBoost_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Exponentiale_MT(*param.parametre)
  Protected Dim ExpLUT(256) , j
  For j = 0 To 255
    ExpLUT(j) = Int(Pow(255, j / 255.0) + 0.5)  ; valeur entière arrondie
    If ExpLUT(j) > 255 : ExpLUT(j) = 255 : EndIf
  Next
  Filtre2_start()
  r = (ExpLUT(r1) * r2) >> 8
  g = (ExpLUT(g1) * g2) >> 8
  b = (ExpLUT(b1) * b2) >> 8
  Clamp_RGB(r, g, b)
  Filtre2_stop()
  FreeArray(ExpLUT())
EndProcedure

Procedure Filtre2_Exponentiale(*param.parametre)
  Filtre_entete_mix("Filtre2_Exponentiale")
  MultiThread_MT(@Filtre2_Exponentiale_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Fade_MT(*param.parametre)
  Protected Dim SumLUT(766) , j
  For j = 0 To 765 : SumLUT(j) = j : Next
  Filtre2_start()
  Protected s2 = SumLUT(r2 + g2 + b2)
  Protected s1 = SumLUT(r1 + g1 + b1)
  r = ((r2 + s2) * (r1 + s1)) >> 12
  g = ((g2 + s2) * (g1 + s1)) >> 12
  b = ((b2 + s2) * (b1 + s1)) >> 12
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Fade(*param.parametre)
  Filtre_entete_mix("Filtre2_Fade")
  MultiThread_MT(@Filtre2_Fade_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Fence_MT(*param.parametre)
  Filtre2_start()
  r = (r2 * (r1 + r2)) >> 9 
  g = (g2 * (g1 + g2)) >> 9
  b = (b2 * (b1 + b2)) >> 9
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Fence(*param.parametre)
  Filtre_entete_mix("Filtre2_Fence")
  MultiThread_MT(@Filtre2_Fence_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Freeze_MT(*param.parametre)
  Filtre2_start()
  r = 255 - ((255 - r1) * (255 - r1)) / (r2 + 1)
  g = 255 - ((255 - g1) * (255 - g1)) / (g2 + 1)
  b = 255 - ((255 - b1) * (255 - b1)) / (b2 + 1)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Freeze(*param.parametre)
  Filtre_entete_mix("Filtre2_Freeze")
  MultiThread_MT(@Filtre2_Freeze_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Glow_MT(*param.parametre)
  Filtre2_start()
  r = (r2 * r2) / ((255 - r1) + 1)
  g = (g2 * g2) / ((255 - g1) + 1)
  b = (b2 * b2) / ((255 - b1) + 1)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Glow(*param.parametre)
  Filtre_entete_mix("Filtre2_Glow")
  MultiThread_MT(@Filtre2_Glow_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_HardContrast_MT(*param.parametre)
  Filtre2_start()
  If r2 > 127 : r = r2 + r1 - 127 : Else : r = r2 - r1 + 127 : EndIf
  If g2 > 127 : g = g2 + g1 - 127 : Else : g = g2 - g1 + 127 : EndIf
  If b2 > 127 : b = b2 + b1 - 127 : Else : b = b2 - b1 + 127 : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_HardContrast(*param.parametre)
  Filtre_entete_mix("Filtre2_HardContrast")
  MultiThread_MT(@Filtre2_HardContrast_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Hardlight_MT(*param.parametre)
  Filtre2_start()
  If r2 < 128 : r = (r1 * r2) >> 7 : Else : r = 255 - ((255 - r1) * (255 - r2) >> 7) : EndIf
  If g2 < 128 : g = (g1 * g2) >> 7 : Else : g = 255 - ((255 - g1) * (255 - g2) >> 7) : EndIf
  If b2 < 128 : b = (b1 * b2) >> 7 : Else : b = 255 - ((255 - b1) * (255 - b2) >> 7) : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Hardlight(*param.parametre)
  Filtre_entete_mix("Filtre2_Hardlight")
  MultiThread_MT(@Filtre2_Hardlight_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_TanBlend_MT(*param.parametre)
  Filtre2_start()
  r = r2 + Tan(r1 * 0.706125 - 90) * 128  
  g = g2 + Tan(g1 * 0.706125 - 90) * 128
  b = b2 + Tan(b1 * 0.706125 - 90) * 128
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_TanBlend(*param.parametre)
  Filtre_entete_mix("Filtre2_TanBlend")
  MultiThread_MT(@Filtre2_TanBlend_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_HardlTangent_MT(*param.parametre)
  Protected Dim tab(255) , j , c
  ;For j = 0 To 255 : tab(j) = Tan(j * 180 / 256 - 90) * 128 : Next
  c = 4 ; 8 ou 16
  For j = 0 To 255 : tab(j) = TanH((j - 128) / c) * 128 : Next
  Filtre2_start()
  r = r2 + tab(r1)
  g = g2 + tab(g1)
  b = b2 + tab(b1)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
  FreeArray(tab())
EndProcedure

Procedure Filtre2_HardlTangent(*param.parametre)
  Filtre_entete_mix("Filtre2_HardlTangent")
  MultiThread_MT(@Filtre2_HardlTangent_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Heat_MT(*param.parametre)
  Filtre2_start()
  r = 255 - ((255 - r2) * (255 - r2)) / (r1 + 1)
  g = 255 - ((255 - g2) * (255 - g2)) / (g1 + 1)
  b = 255 - ((255 - b2) * (255 - b2)) / (b1 + 1)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Heat(*param.parametre)
  Filtre_entete_mix("Filtre2_Heat")
  MultiThread_MT(@Filtre2_Heat_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_InHale_MT(*param.parametre)
  Protected Dim tab(255) , j
  For j = 0 To 255 : tab(j) = (255 - j) * ((255 - j) / 127.5 - 1) : Clamp(tab(j), 0, 255) : Next
  Filtre2_start()
  r = r2 - tab(r1)
  g = g2 - tab(g1)
  b = b2 - tab(b1)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
  FreeArray(tab())
EndProcedure

Procedure Filtre2_InHale(*param.parametre)
  Filtre_entete_mix("Filtre2_InHale")
  MultiThread_MT(@Filtre2_InHale_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Intensify_MT(*param.parametre)
  Protected Dim tab(256) , j
  For j = 0 To 255 : tab(j) = 64 - Cos(j * 3.14 / 255) * 64 : Next
  Filtre2_start()
  r = r2 + ((r1 * r2) >> 8)
  g = g2 + ((g1 * g2) >> 8)
  b = b2 + ((b1 * b2) >> 8)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
  FreeArray(tab())
EndProcedure

Procedure Filtre2_Intensify(*param.parametre)
  Filtre_entete_mix("Filtre2_Intensify")
  MultiThread_MT(@Filtre2_Intensify_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_CosBlend_MT(*param.parametre)
  Protected Dim tab(256) , j
  For j = 0 To 255 : tab(j) = 64 - Cos(j * 3.14 / 255) * 64 : Next
  Filtre2_start()
  r = tab(r1) + tab(r2)
  g = tab(g1) + tab(g2)
  b = tab(b1) + tab(b2)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
  FreeArray(tab())
EndProcedure

Procedure Filtre2_CosBlend(*param.parametre)
  Filtre_entete_mix("Filtre2_CosBlend")
  MultiThread_MT(@Filtre2_CosBlend_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Interpolation_MT(*param.parametre)
  Protected Dim tab(256) , j
  For j = 0 To 255 : tab(j) = 64 - Cos(j * 3.14159265 / 255) * 64 : Next
  Protected fading = *param\option[0]
  Filtre2_start() 
  r = (tab(r1) * fading + tab(r2) * (255 - fading)) >> 8
  g = (tab(g1) * fading + tab(g2) * (255 - fading)) >> 8
  b = (tab(b1) * fading + tab(b2) * (255 - fading)) >> 8
  Clamp_RGB(r, g, b)
  Filtre2_stop()
  FreeArray(tab())
EndProcedure

Procedure Filtre2_Interpolation(*param.parametre)
  Filtre_entete_mix("Filtre2_Interpolation")
  MultiThread_MT(@Filtre2_Interpolation_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_InvBurn_MT(*param.parametre)
  Filtre2_start()
  r = 0 : g = 0 : b = 0
  If r1 > 0 : r = 255 - (255 - r2) / r1 : EndIf
  If g1 > 0 : g = 255 - (255 - g2) / g1 : EndIf
  If b1 > 0 : b = 255 - (255 - b2) / b1 : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_InvBurn(*param.parametre)
  Filtre_entete_mix("Filtre2_InvBurn")
  MultiThread_MT(@Filtre2_InvBurn_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_InvColorBurn_MT(*param.parametre)
  Filtre2_start()
  r = 0 : g = 0 : b = 0
  If r1 > 0 : r = 255 - (((255 - r2) << 8) / r1) : EndIf
  If g1 > 0 : g = 255 - (((255 - g2) << 8) / g1) : EndIf
  If b1 > 0 : b = 255 - (((255 - b2) << 8) / b1) : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_InvColorBurn(*param.parametre)
  Filtre_entete_mix("Filtre2_InvColorBurn")
  MultiThread_MT(@Filtre2_InvColorBurn_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_InvColorDodge_MT(*param.parametre)
  Filtre2_start()
  r = 255 : g = 255 : b = 255
  If r1 < 255 : r = (r2 << 8) / (255 - r1) : EndIf
  If g1 < 255 : g = (g2 << 8) / (255 - g1) : EndIf
  If b1 < 255 : b = (b2 << 8) / (255 - b1) : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_InvColorDodge(*param.parametre)
  Filtre_entete_mix("Filtre2_InvColorDodge")
  MultiThread_MT(@Filtre2_InvColorDodge_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_InvDodge_MT(*param.parametre)
  Filtre2_start()
  r = 255 : g = 255 : b = 255
  If r1 < 255 : r = r2 / (255 - r1) : EndIf
  If g1 < 255 : g = g2 / (255 - g1) : EndIf
  If b1 < 255 : b = b2 / (255 - b1) : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_InvDodge(*param.parametre)
  Filtre_entete_mix("Filtre2_InvDodge")
  MultiThread_MT(@Filtre2_InvDodge_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Lighten_MT(*param.parametre)
  Filtre2_start()
  r = r2 : g = g2 : b = b2
  If r1 > r2 : r = r1 : EndIf
  If g1 > g2 : g = g1 : EndIf
  If b1 > b2 : b = b1 : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Lighten(*param.parametre)
  Filtre_entete_mix("Filtre2_Lighten")
  MultiThread_MT(@Filtre2_Lighten_MT())
  Filtre2_end()
EndProcedure                                    

;**************
Procedure Filtre2_LinearBurn_MT(*param.parametre)
  Filtre2_start()
  r = r1 + r2
  g = g1 + g2
  b = b1 + b2
  If r < 256 : r = 0 : Else : r = r - 255 : EndIf
  If g < 256 : g = 0 : Else : g = g - 255 : EndIf
  If b < 256 : b = 0 : Else : b = b - 255 : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure                               

Procedure Filtre2_LinearBurn(*param.parametre)
  Filtre_entete_mix("Filtre2_LinearBurn")
  MultiThread_MT(@Filtre2_LinearBurn_MT())
  Filtre2_end()
EndProcedure      
;**************
Procedure Filtre2_LinearLight_MT(*param.parametre)
  Protected Dim comps(2)
  Protected Dim src1(2), Dim src2(2)
  Protected k
  Filtre2_start()
  src1(0)=r1 : src1(1)=g1 : src1(2)=b1
  src2(0)=r2 : src2(1)=g2 : src2(2)=b2
  For k = 0 To 2
    If src1(k) < 128 
      comps(k) = src2(k) + src1(k)*2
    Else
      comps(k) = src2(k) + (src1(k)-128)*2
    EndIf
    Clamp(comps(k), 0, 255)
  Next
  r = comps(0)
  g = comps(1)
  b = comps(2)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
  FreeArray(comps())
  FreeArray(src1())
  FreeArray(src2())
EndProcedure

Procedure Filtre2_LinearLight(*param.parametre)
  Filtre_entete_mix("Filtre2_LinearLight")
  MultiThread_MT(@Filtre2_LinearLight_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Logarithmic_MT(*param.parametre)
  Filtre2_start()
  r = 255 * (Log(r1 + 1) + Log(r2 + 1)) / (2 * Log(256))
  g = 255 * (Log(g1 + 1) + Log(g2 + 1)) / (2 * Log(256))
  b = 255 * (Log(b1 + 1) + Log(b2 + 1)) / (2 * Log(256))
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Logarithmic(*param.parametre)
  Filtre_entete_mix("Filtre2_Logarithmic")
  MultiThread_MT(@Filtre2_Logarithmic_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Mean_MT(*param.parametre)
  Filtre2_start()
  r = (r1 + r2) >> 1
  g = (g1 + g2) >> 1
  b = (b1 + b2) >> 1
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Mean(*param.parametre)
  Filtre_entete_mix("Filtre2_Mean")
  MultiThread_MT(@Filtre2_Mean_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_ColorVivify_MT(*param.parametre)
  Filtre2_start()
  r = r2 + r1 - (g1 + b1) >> 1
  g = g2 + g1 - (r1 + b1) >> 1
  b = b2 + b1 - (g1 + r1) >> 1
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_ColorVivify(*param.parametre)
  Filtre_entete_mix("Filtre2_ColorVivify")
  MultiThread_MT(@Filtre2_ColorVivify_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Multiply_MT(*param.parametre)
  Filtre2_start()
  r = (r1 * r2) >> 8
  g = (g1 * g2) >> 8
  b = (b1 * b2) >> 8
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Multiply(*param.parametre)
  Filtre_entete_mix("Filtre2_Multiply")
  MultiThread_MT(@Filtre2_Multiply_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Negation_MT(*param.parametre)
  Filtre2_start()
  r = 255 - Abs(255 - r1 - r2)
  g = 255 - Abs(255 - g1 - g2)
  b = 255 - Abs(255 - b1 - b2)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Negation(*param.parametre)
  Filtre_entete_mix("Filtre2_Negation")
  MultiThread_MT(@Filtre2_Negation_MT())
  Filtre2_end()
EndProcedure                  

;**************
Procedure Filtre2_PinLight_MT(*param.parametre)
  Filtre2_start()
  If r1 < 128 : Min(r , r2, (2 * r1)) : Else : Max(r , r2, (2 * (r1 - 128))) : EndIf
  If g1 < 128 : Min(g , g2, (2 * g1)) : Else : Max(g , g2, (2 * (g1 - 128))) : EndIf
  If b1 < 128 : Min(b , b2, (2 * b1)) : Else : Max(b , b2, (2 * (b1 - 128))) : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_PinLight(*param.parametre)
  Filtre_entete_mix("Filtre2_PinLight")
  MultiThread_MT(@Filtre2_PinLight_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Or_MT(*param.parametre)
  Filtre2_start()
  r = r1 | r2
  g = g1 | g2
  b = b1 | b2
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Or(*param.parametre)
  Filtre_entete_mix("Filtre2_Or")
  MultiThread_MT(@Filtre2_Or_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Overlay_MT(*param.parametre)
  Filtre2_start()
  r = (r1 * r2) >> 7
  g = (g1 * g2) >> 7
  b = (b1 * b2) >> 7
  If r1 >= 128 : r = 255 - ((255 - r1) * (255 - r2) >> 7) : EndIf
  If g1 >= 128 : g = 255 - ((255 - g1) * (255 - g2) >> 7) : EndIf
  If b1 >= 128 : b = 255 - ((255 - b1) * (255 - b2) >> 7) : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Overlay(*param.parametre)
  Filtre_entete_mix("Filtre2_Overlay")
  MultiThread_MT(@Filtre2_Overlay_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Pegtop_soft_light_MT(*param.parametre)
  Filtre2_start()
  Protected c = (r1 * r2) >> 8
  r = c + r1 * (255 - ((255 - r1) * (255 - r2) >> 8) - c) >> 8
  c = (g1 * g2) >> 8
  g = c + g1 * (255 - ((255 - g1) * (255 - g2) >> 8) - c) >> 8
  c = (b1 * b2) >> 8
  b = c + b1 * (255 - ((255 - b1) * (255 - b2) >> 8) - c) >> 8
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Pegtop_soft_light(*param.parametre)
  Filtre_entete_mix("Filtre2_Pegtop_soft_light")
  MultiThread_MT(@Filtre2_Pegtop_soft_light_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_quadritic_MT(*param.parametre)
  Filtre2_start()
  r = 255
  If r2 <> 255 : r = r1 * r1 / (255 - r2) : EndIf
  g = 255
  If g2 <> 255 : g = g1 * g1 / (255 - g2) : EndIf
  b = 255
  If b2 <> 255 : b = b1 * b1 / (255 - b2) : EndIf
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_quadritic(*param.parametre)
  Filtre_entete_mix("Filtre2_quadritic")
  MultiThread_MT(@Filtre2_quadritic_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_Screen_MT(*param.parametre)
  Filtre2_start()
  r = 255 - ((255 - r1) * (255 - r2) >> 8)
  g = 255 - ((255 - g1) * (255 - g2) >> 8)
  b = 255 - ((255 - b1) * (255 - b2) >> 8)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Screen(*param.parametre)
  Filtre_entete_mix("Filtre2_Screen")
  MultiThread_MT(@Filtre2_Screen_MT())
  Filtre2_end()
EndProcedure          
;**************
Procedure Filtre2_SoftColorBurn_MT(*param.parametre)
  Filtre2_start()
  ; Calcul soft burn pour chaque composante
  If r1 + r2 < 256
    If r1 = 255
      r = 255
    Else
      r = (r2 << 7) / (255 - r1)
      If r > 255 : r = 255 : EndIf
    EndIf
  Else
    r = 255 - (((255 - r1) << 7) / r2)
    If r < 0 : r = 0 : EndIf
  EndIf
  
  If g1 + g2 < 256
    If g1 = 255
      g = 255
    Else
      g = (g2 << 7) / (255 - g1)
      If g > 255 : g = 255 : EndIf
    EndIf
  Else
    g = 255 - (((255 - g1) << 7) / g2)
    If g < 0 : g = 0 : EndIf
  EndIf
  
  If b1 + b2 < 256
    If b1 = 255
      b = 255
    Else
      b = (b2 << 7) / (255 - b1)
      If b > 255 : b = 255 : EndIf
    EndIf
  Else
    b = 255 - (((255 - b1) << 7) / b2)
    If b < 0 : b = 0 : EndIf
  EndIf
  
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_SoftColorBurn(*param.parametre)
  Filtre_entete_mix("Filtre2_SoftColorBurn")
  MultiThread_MT(@Filtre2_SoftColorBurn_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_SoftColorDodge_MT(*param.parametre)
  Filtre2_start()
  ; Composante rouge
  If r1 + r2 < 256
    If r2 = 255
      r = 255
    Else
      r = (r1 << 7) / (255 - r2)
      If r > 255 : r = 255 : EndIf
    EndIf
  Else
    r = 255 - (((255 - r2) << 7) / r1)
    If r < 0 : r = 0 : EndIf
  EndIf
  
  ; Composante verte
  If g1 + g2 < 256
    If g2 = 255
      g = 255
    Else
      g = (g1 << 7) / (255 - g2)
      If g > 255 : g = 255 : EndIf
    EndIf
  Else
    g = 255 - (((255 - g2) << 7) / g1)
    If g < 0 : g = 0 : EndIf
  EndIf
  
  ; Composante bleue
  If b1 + b2 < 256
    If b2 = 255
      b = 255
    Else
      b = (b1 << 7) / (255 - b2)
      If b > 255 : b = 255 : EndIf
    EndIf
  Else
    b = 255 - (((255 - b2) << 7) / b1)
    If b < 0 : b = 0 : EndIf
  EndIf
  
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_SoftColorDodge(*param.parametre)
  Filtre_entete_mix("Filtre2_SoftColorDodge")
  MultiThread_MT(@Filtre2_SoftColorDodge_MT())
  Filtre2_end()
EndProcedure             

;**************
Procedure Filtre2_SoftLight_MT(*param.parametre)
  Protected k
  Filtre2_start()
  Protected Dim src1(2), Dim src2(2), Dim res(2)
  src1(0)=r1 : src1(1)=g1 : src1(2)=b1
  src2(0)=r2 : src2(1)=g2 : src2(2)=b2 
  For k = 0 To 2
    Protected c = (src1(k) * src2(k)) >> 8
    res(k) = c + src1(k) * (255 - (((255 - src1(k)) * (255 - src2(k))) >> 8) - c) >> 8
  Next
  r = res(0) : g = res(1) : b = res(2)
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_SoftLight(*param.parametre)
  Filtre_entete_mix("Filtre2_SoftLight")
  MultiThread_MT(@Filtre2_SoftLight_MT())
  Filtre2_end()
EndProcedure
;**************
Procedure Filtre2_SoftOverlay_MT(*param.parametre)
  Filtre2_start()
  If r1 < 128
    r = (r1 * r2) >> 7
  Else
    r = 255 - ((255 - r1) * (255 - r2) >> 7)
  EndIf

  If g1 < 128
    g = (g1 * g2) >> 7
  Else
    g = 255 - ((255 - g1) * (255 - g2) >> 7)
  EndIf

  If b1 < 128
    b = (b1 * b2) >> 7
  Else
    b = 255 - ((255 - b1) * (255 - b2) >> 7)
  EndIf
  
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_SoftOverlay(*param.parametre)
  Filtre_entete_mix("Filtre2_SoftOverlay")
  MultiThread_MT(@Filtre2_SoftOverlay_MT())
  Filtre2_end()
EndProcedure                 

;**************
Procedure Filtre2_Stamp_MT(*param.parametre)
  Filtre2_start()
  r = (r1 + r2 * 2) - 256
  g = (g1 + g2 * 2) - 256
  b = (b1 + b2 * 2) - 256
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Stamp(*param.parametre)
  Filtre_entete_mix("Filtre2_Stamp")
  MultiThread_MT(@Filtre2_Stamp_MT())
  Filtre2_end()
EndProcedure

;**************
Procedure Filtre2_Subtractive_MT(*param.parametre)
  Filtre2_start()
  r = (r1 + r2) - 256
  g = (g1 + g2) - 256
  b = (b1 + b2) - 256
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Subtractive(*param.parametre)
  Filtre_entete_mix("Filtre2_Subtractive")
  MultiThread_MT(@Filtre2_Subtractive_MT())
  Filtre2_end()
EndProcedure                  

;**************
Procedure Filtre2_Xor_MT(*param.parametre)
  Filtre2_start()
  r = r1 ! r2
  g = g1 ! g2
  b = b1 ! b2
  Clamp_RGB(r, g, b)
  Filtre2_stop()
EndProcedure

Procedure Filtre2_Xor(*param.parametre)
  Filtre_entete_mix("Filtre2_Xor")
  MultiThread_MT(@Filtre2_Xor_MT())
  Filtre2_end()
EndProcedure

Avatar de l’utilisateur
SPH
Messages : 4961
Inscription : mer. 09/nov./2005 9:53

Re: filtre graphique

Message par SPH »

Penses tu regrouper tous ces effets dans un seul fichier de traitement d'image ? (ce serait cool et plus facile d'utilisation) :idea:

!i!i!i!i!i!i!i!i!i!
!i!i!i!i!i!i!
!i!i!i!
//// Informations ////
Intel Core i7 4770 64 bits - GTX 650 Ti
Version de PB : 6.12LTS- 64 bits
Fred
Site Admin
Messages : 2844
Inscription : mer. 21/janv./2004 11:03

Re: filtre graphique

Message par Fred »

Joil travail, pour des perfs maximales, il faut utiliser le backend C avec les optimisations activées
Répondre