filtre graphique

Programmation d'applications complexes
manababel
Messages : 151
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 Appliquez le ou les filtres, puis appliquez 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 ».

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_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(RaysFilter , #Filter_RaysFilter)
  ;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(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\BlackAndWhite.pbi"
  ;XIncludeFile "Couleur\grayscale.pbi"
  ;XIncludeFile "Couleur\Posterize.pbi"
  ;XIncludeFile "Couleur\Balance.pbi"
  ;XIncludeFile "Couleur\Bend.pbi"
  ;XIncludeFile "Couleur\Brightness.pbi"
  ;XIncludeFile "Couleur\RaviverCouleurs.pbi"
  ;XIncludeFile "Couleur\saturation.pbi"
  ;XIncludeFile "Couleur\Contrast.pbi"
  ;XIncludeFile "Couleur\Gamma.pbi"
  ;XIncludeFile "Couleur\Color.pbi"
  ;XIncludeFile "Couleur\Color_hue.pbi"
  ;XIncludeFile "Couleur\color_effect.pbi"
  ;XIncludeFile "Couleur\Colorize.pbi"
  ;XIncludeFile "Couleur\Hollow.pbi"
  ;XIncludeFile "Couleur\SquareLaw_Lightening.pbi"
  ;XIncludeFile "Couleur\Sepia.pbi"
  ;XIncludeFile "Couleur\Exposure.pbi"
  ;XIncludeFile "Couleur\negatif.pbi"
  ;XIncludeFile "Couleur\teinte.pbi"
  ;XIncludeFile "Couleur\Normalize_Color_Filter.pbi"
  ;XIncludeFile "Couleur\ColorPermutation.pbi"
  ;XIncludeFile "Couleur\Dichromaticimage.pbi"
  ;XIncludeFile "Couleur\FalseColourImage.pbi"
  ;XIncludeFile "Couleur\PencilImage.pbi"
  
  ;XIncludeFile "autre\Glow_IIR.pbi"
  ;XIncludeFile "autre\Emboss.pbi"
  ;XIncludeFile "autre\Fake_Hdr.pbi"
  ;XIncludeFile "autre\pencil.pbi"
  ;XIncludeFile "autre\CharcoalImage.pbi"
  ;XIncludeFile "autre\RaysFilter.pbi"
  ;XIncludeFile "autre\Histogram.pbi"
  
  ;XIncludeFile "fx\Diffuse.pbi"
  ;XIncludeFile "fx\Emboss_bump.pbi"
  ;XIncludeFile "fx\Mosaic.pbi"
  ;XIncludeFile "fx\HexMosaic.pbi"
  ;XIncludeFile "fx\IrregularHexMosaic.pbi"
  ;XIncludeFile "fx\Glitch.pbi"
  ;XIncludeFile "fx\Kaleidoscope.pbi"
  ;XIncludeFile "fx\FlowLiquify.pbi"
  ;XIncludeFile "fx\DisplacementMap.pbi"
  ;XIncludeFile "fx\Dilate.pbi"
  ;XIncludeFile "fx\mettalic_effect.pbi"
  
  ;XIncludeFile "Convolution\Convol3x3.pbi"
  
  ;XIncludeFile "Deform\FlipH.pbi"
  ;XIncludeFile "Deform\FlipV.pbi"
  ;XIncludeFile "Deform\Rotate.pbi"
  ;XIncludeFile "Deform\PerspectiveSimple.pbi"
  ;XIncludeFile "Deform\Perspective.pbi"
  ;XIncludeFile "Deform\Translate.pbi"
  ;XIncludeFile "Deform\Spherize.pbi"
  ;XIncludeFile "Deform\Spiralize.pbi"
  ;XIncludeFile "Deform\Ellipse.pbi"
  ;XIncludeFile "Deform\Ripple.pbi"
  ;XIncludeFile "Deform\PinchBulge.pbi"
  ;XIncludeFile "Deform\WaveCircular.pbi"
  ;XIncludeFile "Deform\Lens.pbi"
  ;XIncludeFile "Deform\Tile.pbi"
  ;XIncludeFile "Deform\Perspective2.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\mix.pbi"
EndModule

Dernière modification par manababel le dim. 14/sept./2025 8:20, modifié 3 fois.
manababel
Messages : 151
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 : 151
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 : 151
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 : 151
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 : 151
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 : 1563
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 : 151
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 : 1563
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.
Répondre