filtre graphique

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

Re: filtre graphique

Message par manababel »

les "fx"

fx.pbi

Code : Tout sélectionner

Procedure Diffuse_MT(*p.parametre)
  Protected i, x, y, px, py, a, b, var, alpha
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected opt = *p\option[0]
  Protected totalPixels = lg * ht
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected *mask = *p\mask

  ; Clamp de l'option d'intensité
  Clamp(opt, 0, 256)
  ; Calcul des bornes pour la gestion du multithreading
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  For i = startPos To endPos - 1
    ; Calcul des coordonnées du pixel courant
    y = i / lg
    x = i % lg
    ; Génération d'un décalage aléatoire dans un carré centré sur le pixel
    a = Random(opt) - (opt >> 1)
    b = Random(opt) - (opt >> 1)
    px = x + a
    py = y + b
    ; Clamp pour ne pas sortir des limites de l'image
    Clamp(px, 0, lg - 1)
    Clamp(py, 0, ht - 1)
    ; Récupération de la couleur source du pixel décalé
    var = PeekL(*p\addr[0] + ((py * lg + px) << 2))
    ; Ecriture de la couleur dans la cible
    PokeL(*p\addr[1] + (i << 2), var)
  Next
EndProcedure

; Procédure principale pour lancer l'effet de diffusion avec multithreading et masque alpha
Procedure Diffuse(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_FX
    param\name = "Diffuse"
    param\remarque = ""
    param\info[0] = "intensité"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 256 : 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
  filter_start(@Diffuse_MT() , 1)
EndProcedure

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

Procedure DilateEffect_MT(*p.parametre)
  Protected *src = *p\addr[0]
  Protected *dst = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht

  Protected startY = (*p\thread_pos * ht) / *p\thread_max
  Protected stopY  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht - 1 : EndIf

  Protected x, y, nx, ny
  Protected srcOffset, dstOffset
  Protected maxR, maxG, maxB, maxA
  Protected r, g, b, a
  Protected pix

  For y = startY To stopY
    For x = 0 To lg - 1
      maxR = 0
      maxG = 0
      maxB = 0
      maxA = 0

      ; Parcourir voisins 3x3
      For ny = y - 1 To y + 1
        If ny < 0 Or ny >= ht : Continue : EndIf; hors limites en Y
        For nx = x - 1 To x + 1
          If nx < 0 Or nx >= lg : Continue : EndIf ; hors limites en X

          srcOffset = (ny * lg + nx) * 4
          pix = PeekL(*src + srcOffset)
          getargb(pix , a , r , g , b)

          If r > maxR : maxR = r : EndIf
          If g > maxG : maxG = g : EndIf
          If b > maxB : maxB = b : EndIf
          If a > maxA : maxA = a : EndIf
        Next
      Next

      dstOffset = (y * lg + x) * 4
      PokeL(*dst + dstOffset, (a<<24) | (r<<16) | (g<<8) | b)
    Next
  Next
EndProcedure


Procedure Dilate(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_FX
    param\name = "Dilate"
    param\remarque = "Dilat. morph. 3x3"
    param\info[0] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 2 : param\info_data(0,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@DilateEffect_MT() , 0)
EndProcedure


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


Procedure DisplacementMap_MT(*p.parametre)
  Protected *src = *p\addr[0]
  Protected *dst = *p\addr[1]
  Protected *disp = *p\source2
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected intensity.f = *p\option[0] * 0.5
  Protected offsetX.f = ((*p\option[1] - 100) * lg) / 100
  Protected offsetY.f = ((*p\option[2] - 100) * ht) / 100
  Protected wrapMode = *p\option[3]  ; 0 = clamp, 1 = wrap (modulo)

  Protected startY = (*p\thread_pos * ht) / *p\thread_max
  Protected stopY  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht - 1 : EndIf

  Protected x, y
  Protected srcX.f, srcY.f
  Protected dispXPos.f, dispYPos.f
  Protected offsetDst, offsetDisp

  For y = startY To stopY
    For x = 0 To lg - 1
      dispXPos = x + offsetX
      dispYPos = y + offsetY
      
      If wrapMode = 0
        Clamp(dispXPos, 0, lg - 1)
        Clamp(dispYPos, 0, ht - 1)
      Else
        ; Wrap autour avec modulo
        dispXPos =  Mod(dispXPos , lg)
        If dispXPos < 0 : dispXPos + lg : EndIf
        dispYPos =  Mod(dispYPos , ht)
        If dispYPos < 0 : dispYPos + ht : EndIf
      EndIf

      offsetDisp = (Int(dispYPos) * lg + Int(dispXPos)) * 4
      Protected dispColor = PeekL(*disp + offsetDisp)

      ; Utiliser rouge et vert comme vecteurs de déplacement
      Protected dispX = ((dispColor >> 16) & $FF) - 128 ; rouge
      Protected dispY = ((dispColor >> 8) & $FF) - 128 ; vert

      srcX = x + (dispX / 128.0) * intensity
      srcY = y + (dispY / 128.0) * intensity

      Clamp(srcX, 0, (lg - 1))
      Clamp(srcY, 0, (ht - 1))

      offsetDst = (y * lg + x) * 4
      PokeL(*dst + offsetDst, BilinearSample(*src, lg, ht, srcX, srcY))
    Next
  Next
EndProcedure

Procedure DisplacementMap(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_FX
    param\name = "DisplacementMap"
    param\remarque = "Nécessite 2 images : source + displacement"
    param\info[0] = "intensity"
    param\info[1] = "offset X"
    param\info[2] = "offset Y"
    param\info[3] = "Wrap mode"
    param\info[4] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 500 : param\info_data(0,2) = 1
    param\info_data(1,0) = 0 : param\info_data(1,1) = 200 : param\info_data(1,2) = 100
    param\info_data(2,0) = 0 : param\info_data(2,1) = 200 : param\info_data(2,2) = 100
    param\info_data(3,0) = 0 : param\info_data(3,1) = 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
  If *param\source2 = 0 : ProcedureReturn : EndIf
  
  filter_start(@DisplacementMap_MT() , 4)
EndProcedure

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

Procedure Emboss_bump_MT(*p.parametre)
  Protected x, y, pos, j, i
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected a, r, g, b
  Protected lValue
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected Dim l(2,2)  ; 3x3 pour le gradient

  ; === Paramètres de lumière ===
  Protected azimuth.f   = *p\option[0]   ; 0..360°
  Protected elevation.f = *p\option[1] * 90 / 100  ; 0..90°
  If elevation < 1 : elevation = 1 : EndIf
  Protected intensity.f = (*p\option[2] + 50) / 100.0
  Protected light_mix   = *p\option[3]
  Protected bn     = *p\option[5]
  Protected mix_strength.f = *p\option[4] / 100
  Protected invert   = *p\option[6]
  
  ; --- Calcul vecteur lumière ---
  Protected lx.f, ly.f, lz.f
  lx = Cos(Radian(azimuth)) * Sin(Radian(elevation))
  ly = Sin(Radian(azimuth)) * Sin(Radian(elevation))
  lz = Cos(Radian(elevation))
  ; Normalisation correcte (affectation)
  Protected llen.f = Sqr(lx*lx + ly*ly + lz*lz)
  If llen <> 0.0
    lx = lx / llen : ly = ly / llen : lz = lz / llen
  EndIf

  ; --- Calcul plage verticale pour le thread ---
  Protected startY = (*p\thread_pos * ht) / *p\thread_max
  Protected endY   = ((*p\thread_pos + 1) * ht) / *p\thread_max
  If endY > ht : endY = ht : EndIf
  Protected readStart = startY
  Protected readEnd   = endY
  If readStart < 1 : readStart = 1 : EndIf
  If readEnd > ht-2 : readEnd = ht-2 : EndIf

  For y = readStart To readEnd
    For x = 1 To lg-2
      pos = *p\addr[0] + ((y * lg + x) << 2)
      ; --- Lecture des 3x3 voisins ---
      For j = -1 To 1
        For i = -1 To 1
          *srcPixel = pos + ((j * lg + i) << 2)
          GetARGB(*srcPixel\l, a, r, g, b)
          l(i+1, j+1) = (r * 1225 + g * 2405 + b * 466) >> 12
        Next i
      Next j
      ; --- Calcul gradient ---
      Protected gx.f, gy.f, gz.f
      gx = ((l(2,0) + 2*l(2,1) + l(2,2)) - (l(0,0) + 2*l(0,1) + l(0,2)))
      gy = ((l(0,2) + 2*l(1,2) + l(2,2)) - (l(0,0) + 2*l(1,0) + l(2,0)))
      gz = 1.0  ; normalisation approximative

      ; --- Produit scalaire lumière × gradient ---
      lValue = 128 + intensity * (gx * lx + gy * ly + gz * lz)
      If bn
        lValue = lValue - 128
        If invert
          lValue = 255 - lValue
        EndIf 
      EndIf  
      lValue = Pow(lValue/255.0, 1.2) * 255
      Clamp(lValue, 0, 255)
      If y >= startY And y < endY
        *dstPixel = *p\addr[1] + ((y * lg + x) << 2)
        ; ---- Mélange lumière / couleur d'origine ----
        If light_mix
          ; récupération de la couleur d'origine
          GetARGB(*srcPixel\l, a, r, g, b)
          ; mélange (tu peux ajuster le facteur de mixage 0.5 → 0.2..0.8)
          r = r * (1.0 - mix_strength) + lValue * mix_strength
          g = g * (1.0 - mix_strength) + lValue * mix_strength
          b = b * (1.0 - mix_strength) + lValue * mix_strength
          
          Clamp_rgb(r, g , b)
          *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
        Else
          ; rendu emboss pur en niveaux de gris
          *dstPixel\l = (a << 24) | lValue * $10101
        EndIf
      EndIf
    Next
  Next
EndProcedure

    
    ;For y=0 To ht
    ;For x=0 To lg
        ;k1=0
        ;For yy=-val To 0
        ;For xx=-val To 0
            ;If ((x+xx)>=0 And (y+yy)>=0) Then
                ;rgb=tab(x+xx,y+yy)
                ;r=((rgb And $ff0000)Shr 16)
                ;g=((rgb And $00ff00)Shr 8)
                ;b=((rgb And $0000ff))
                ;c=(tabr(r)+tabg(g)+tabb(b))Shr 10
                ;If (xx+yy)=0 Then
                    ;k1=(c-(k1/k2)+255)Shr 1
                ;Else
                    ;k1=k1+c
                ;EndIf
            ;EndIf
        ;Next
        ;Next
            ;WritePixelFast(x,y,taba(k1),ImageBuffer(img))
    ;Next
  ;Next
  
; ----------------------------------------------------------------------------------
; Procédure principale d'effet Emboss (relief directionnel)

Procedure Emboss_bump(*param.parametre)
  If *param\info_active
    *param\typ = #Filter_Type_FX
    *param\name = "Emboss"
    *param\remarque = "Emboss (relief directionnel niveaux de gris)"
    *param\info[0] = "angle"
    *param\info[1] = "inclinaison"
    *param\info[2] = "intensity"
    *param\info[3] = "Mix_image"
    *param\info[4] = "mix_alpha"
    *param\info[5] = "Blanc/noir"
    *param\info[6] = "invert"
    *param\info[7] = "masque"
    *param\info_data(0,0) = 0    : *param\info_data(0,1) = 360  : *param\info_data(0,2) = 50
    *param\info_data(1,0) = 1    : *param\info_data(1,1) = 100  : *param\info_data(1,2) = 25
    *param\info_data(2,0) = 1    : *param\info_data(2,1) = 500  : *param\info_data(2,2) = 250
    *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) = 100  : *param\info_data(4,2) = 50
    *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) = 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 *tempo
  If *param\source =  *param\cible
    *tempo = AllocateMemory(*param\lg * *param\ht * 4)
    If *tempo = 0 : ProcedureReturn : EndIf
    CopyMemory(*param\cible , *tempo , *param\lg * *param\ht * 4)
    *param\addr[0] = *tempo
  Else
    *param\addr[0] = *param\source
  EndIf
  *param\addr[1] = *param\cible
  MultiThread_MT(@Emboss_bump_MT())
  If *param\mask And *param\option[7] : *param\mask_type = *param\option[7] - 1 : MultiThread_MT(@_mask()) : EndIf
  If *tempo : FreeMemory(*tempo) : EndIf
  
EndProcedure

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



Procedure FlowLiquify_MT(*p.parametre)
  Protected *src = *p\addr[0]
  Protected *dst = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected intensity.f = *p\option[0] ; Amplitude max déplacement
  Protected scale.f = *p\option[1] / 100 ; Echelle du bruit
  
  Protected startY = (*p\thread_pos * ht) / *p\thread_max
  Protected stopY  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht - 1 : EndIf
  
  Protected x, y
  Protected srcX.f, srcY.f
  Protected offsetDst
  
  For y = startY To stopY
    For x = 0 To lg - 1
      Protected angle.f = PerlinNoise(x * scale, y * scale) * 2.0 * #PI  ; angle entre 0 et 2PI
      Protected vx.f = Cos(angle) * intensity
      Protected vy.f = Sin(angle) * intensity
      ;Protected vx.f = (PerlinFractal(x * scale, y * scale) - 0.5) * 2.0 * intensity
      ;Protected vy.f = (PerlinFractal((x + 1000) * scale, (y + 1000) * scale) - 0.5) * 2.0 * intensity
      srcX = x + vx
      srcY = y + vy
      Clamp(srcX, 0, (lg - 1))
      Clamp(srcY, 0, (ht - 1))
      
      offsetDst = (y * lg + x) * 4
      PokeL(*dst + offsetDst, BilinearSample(*src, lg, ht, srcX, srcY))
    Next
  Next
EndProcedure


Procedure FlowLiquify(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_FX
    param\name = "FlowLiquify"
    param\remarque = "Effet déformation fluide/liquide avec bruit 2D"
    param\info[0] = "Intensité"
    param\info[1] = "Echelle bruit"
    param\info[2] = "gradients"
    param\info[3] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 50 : param\info_data(0,2) = 5
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100 : param\info_data(1,2) = 10
    param\info_data(2,0) = 0 : param\info_data(2,1) = 5 : param\info_data(2,2) = 0
    param\info_data(3,0) = 0 : param\info_data(3,1) = 2 : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  ; filtre / perlin noise
  SetupGradients(param\option[2])
  filter_start(@FlowLiquify_MT() , 3)
EndProcedure

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

Procedure GlitchEffect_MT(*p.parametre)
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected intensity = *p\option[0] ; [0–100] % déplacement max
  Protected sliceHeight = 4 + Random(8)

  Protected startY = (*p\thread_pos * ht) / *p\thread_max
  Protected stopY  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht - 1 : EndIf

  Protected j, x, x1 , x2 , y = startY
  Protected srcX, srcOffset, dstOffset
  Protected pix, r, g, b, a, noiseLevel

  While y <= stopY
    If y % sliceHeight = 0
      Protected offsetX = Random((lg * intensity) / 100) - (lg * intensity) / 200
      If offsetX = 0 : offsetX = 1 : EndIf
      
      ; --- Glitch horizontal décalé
      For j = 0 To sliceHeight - 1
        If y + j >= ht : Break : EndIf
        For x = 0 To lg - 1
          srcX = x + offsetX
          If srcX < 0 : srcX = 0 : ElseIf srcX >= lg : srcX = lg - 1 : EndIf
          srcOffset = ((y + j) * lg + srcX) * 4
          dstOffset = ((y + j) * lg + x) * 4
          PokeL(*cible + dstOffset, PeekL(*source + srcOffset))
        Next
      Next
      
      ; --- Ajout ligne horizontale bruitée
      If y + sliceHeight < ht
        Protected lineY = y + sliceHeight
        noiseLevel = Random(32) + 16 ; Niveau de bruit (plus = plus intense)
        
        For x = 0 To lg - 1
          srcOffset = (lineY * lg + x) * 4
          pix = PeekL(*source + srcOffset)
          a = (pix >> 24) & $FF
          
          x1 = x + 2
          x2 = x - 2
          Clamp(x1, 0, (lg - 1))
          Clamp(x2, 0, (lg - 1))
          r = PeekA(*source + ((lineY * lg + x1) * 4 + 1)) ; R décalé
          g = PeekA(*source + ((lineY * lg + x2) * 4 + 2)) ; G décalé
          b = PeekA(*source + ((lineY * lg + x) * 4 + 3))                       ; B normal
          
          ; Ajouter un peu de bruit si tu veux
          r + Random(noiseLevel) - noiseLevel / 2 : Clamp(r, 0, 255)
          g + Random(noiseLevel) - noiseLevel / 2 : Clamp(g, 0, 255)
          b + Random(noiseLevel) - noiseLevel / 2 : Clamp(b, 0, 255)
          
          PokeL(*cible + srcOffset, RGBA(r, g, b, a))
        Next
      EndIf
      
      y + sliceHeight + 1 ; on saute après la ligne bruitée
    Else
      y + 1
    EndIf
  Wend
EndProcedure

Procedure Glitch(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_FX
    param\name = "GlitchEffect"
    param\remarque = "Effet Glitch Numérique"
    param\info[0] = "Intensité"
    param\info[1] = "Niveau de bruit"
    param\info[2] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100 : param\info_data(0,2) = 30
    param\info_data(1,0) = 0 : param\info_data(1,1) = 128 : param\info_data(1,2) = 32
    param\info_data(2,0) = 0 : param\info_data(2,1) = 2 : param\info_data(2,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@GlitchEffect_MT() , 2)
EndProcedure

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

Procedure HexMosaic_MT(*p.parametre)
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected hexSize = *p\option[0]
  If hexSize < 4 : hexSize = 8 : EndIf

  Protected startY = (*p\thread_pos * ht) / *p\thread_max
  Protected stopY  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht - 1 : EndIf

  Protected hexWidth  = 2 * hexSize
  Protected hexHeight = Int(Sqr(3) * hexSize)
  Protected stepX     = Int(hexWidth * 3 / 4)
  Protected stepY     = Int(hexHeight / 2)

  Protected cx, cy, offset
  Protected r, g, b, a, count, pix
  Protected i, j, x, y, px, py

  y = startY
  While y <= stopY
    x = 0
    While x < lg
      If (x / stepX) % 2 = 1
        cy = y + stepY
      Else
        cy = y
      EndIf
      cx = x

      ; Moyenne des couleurs
      r = 0
      g = 0
      b = 0
      a = 0
      count = 0
      For j = -hexSize To hexSize
        For i = -hexSize To hexSize
          If Sqr(i*i + j*j) <= hexSize
            px = cx + i
            py = cy + j
            If px >= 0 And px < lg And py >= 0 And py < ht
              offset = (py * lg + px) * 4
              pix = PeekL(*source + offset)
              r + (pix & $FF)
              g + ((pix >> 8) & $FF)
              b + ((pix >> 16) & $FF)
              a + ((pix >> 24) & $FF)
              count + 1
            EndIf
          EndIf
        Next
      Next

      If count > 0
        r / count : g / count : b / count : a / count
        pix = RGBA(r, g, b, a)

        ; Remplissage
        For j = -hexSize To hexSize
          For i = -hexSize To hexSize
            If Sqr(i*i + j*j) <= hexSize
              px = cx + i
              py = cy + j
              If px >= 0 And px < lg And py >= 0 And py < ht
                offset = (py * lg + px) * 4
                PokeL(*cible + offset, pix)
              EndIf
            EndIf
          Next
        Next
      EndIf

      x + stepX
    Wend
    y + stepY
  Wend
EndProcedure

Procedure HexMosaic(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_FX
    param\name = "HexMosaic"
    param\remarque = "Effet mosaïque hexagonal"
    param\info[0] = "Taille"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 4 : param\info_data(0,1) = 64 : param\info_data(0,2) = 12
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2 : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@HexMosaic_MT() , 1)
EndProcedure

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

Procedure.i PointDansPolygon(x.i, y.i, radius.f, sides.i, rotation.f)
  If sides < 3 : ProcedureReturn #False : EndIf
  
  Protected cosRot.f = Cos(rotation)
  Protected sinRot.f = Sin(rotation)
  
  Protected xRot.f = x * cosRot - y * sinRot
  Protected yRot.f = x * sinRot + y * cosRot
  
  Protected angle.f = ATan2(yRot, xRot)
  Protected dist.f = Sqr(xRot * xRot + yRot * yRot)
  
  Protected theta.f = 2 * #PI / sides
  Protected halfTheta.f = theta / 2
  
  angle = angle + #PI
  While angle >= theta
    angle - theta
  Wend
  While angle < 0
    angle + theta
  Wend
  angle - halfTheta
  
  Protected maxDist.f = Cos(halfTheta) / Cos(angle) * radius
  If dist <= maxDist
    ProcedureReturn 1
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Procedure IrregularHexMosaic_MT(*p.parametre)
  Protected *source = *p\addr[0]
  Protected *cible  = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected hexSize = *p\option[0]
  If hexSize < 4 : hexSize = 4 : EndIf
  Protected alpha = *p\option[4]
  clamp(alpha , 0 , 255)
  Protected inv_alpha = 255 - alpha
  Protected rotationRad.f = *p\option[3] * #PI / 180
  Protected sides = *p\option[2]
  Protected alpha2 = *p\option[6]
  clamp(alpha2 , 0 , 255)
  If sides < 3 : sides = 3 : EndIf
  
  ; Pré-calcul du masque polygonal
  Protected Dim polyMask(hexSize * 2 + 1, hexSize * 2 + 1)
  Protected i, j, x, y, px, py
  For j = -hexSize To hexSize
    For i = -hexSize To hexSize
      If PointDansPolygon(i, j, hexSize, sides, rotationRad)
        polyMask(i + hexSize, j + hexSize) = 1
      EndIf
    Next
  Next
  
  Protected startY = (*p\thread_pos * ht) / *p\thread_max
  Protected stopY  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht - 1 : EndIf
  
  Protected hexWidth  = 2 * hexSize
  Protected hexHeight = Int(Sqr(3) * hexSize)
  Protected stepX     = Int(hexWidth * 3 / 4)
  Protected stepY     = Int(hexHeight / 2)
  
  Protected cx, cy, offset
  Protected r, g, b, a, count, pix ,pix2
  Protected a1 , r1, g1, b1, r2 , g2 , b2
  Protected jitter = (hexSize * *p\option[1]) / 100
  If jitter > hexSize : jitter = hexSize : EndIf
  
  y = startY
  While y <= stopY
    x = 0
    While x < lg
      cx = x + Random(jitter * 2) - jitter
      cy = y + Random(jitter * 2) - jitter
      
      r = 0 : g = 0 : b = 0 : a = 0 : count = 0
      For j = -hexSize To hexSize
        For i = -hexSize To hexSize
          If polyMask(i + hexSize, j + hexSize)
            px = cx + i
            py = cy + j
            If px >= 0 And px < lg And py >= 0 And py < ht
              offset = (py * lg + px) * 4
              pix = PeekL(*source + offset)
              getargb(pix , a1 , r1 , g1 , b1)
              r + r1
              g + g1
              b + b1
              a + a1
              count + 1
            EndIf
          EndIf
        Next
      Next
      
      If count > 0
        r / count : g / count : b / count : a / count
        pix = ( (a << 24) | (r << 16) | (g << 8) | b )
        
        For j = -hexSize To hexSize
          For i = -hexSize To hexSize
            If polyMask(i + hexSize, j + hexSize)
              px = cx + i
              py = cy + j
              If px >= 0 And px < lg And py >= 0 And py < ht
                offset = (py * lg + px) * 4
                
                Protected onEdge = 0
                If *p\option[5]
                  If Not polyMask(i + 1 + hexSize, j + hexSize) Or
                     Not polyMask(i - 1 + hexSize, j + hexSize) Or
                     Not polyMask(i + hexSize, j + 1 + hexSize) Or
                     Not polyMask(i + hexSize, j - 1 + hexSize)
                    onEdge = 1
                  EndIf
                EndIf
                
                If onEdge
                  If alpha2
                    pix2 = PeekL(*cible  + offset)
                    getrgb(pix2 , r2 , g2 , b2)
                    r = (r2 * alpha2) >> 8
                    g = (g2 * alpha2) >> 8
                    b = (b2 * alpha2) >> 8
                    PokeL(*cible + offset, (a << 24) | (r << 16) | (g << 8) | b)   
                  Else
                    PokeL(*cible + offset, RGBA(0, 0, 0, 255))
                  EndIf
                Else
                  If alpha
                    pix2 = PeekL(*cible  + offset)
                    getrgb(pix2 , r2 , g2 , b2)
                    getARGB(pix , a , r1 , g1 , b1)
                    r = (r1 * inv_alpha + r2 * alpha) >> 8
                    g = (g1 * inv_alpha + g2 * alpha) >> 8
                    b = (b1 * inv_alpha + b2 * alpha) >> 8
                    PokeL(*cible + offset, (a << 24) | (r << 16) | (g << 8) | b)
                  Else
                    
                    PokeL(*cible + offset, pix)
                  EndIf
                EndIf
              EndIf
            EndIf
          Next
        Next
      EndIf
      
      x + stepX
    Wend
    y + stepY
  Wend
  FreeArray(polyMask())
EndProcedure



Procedure IrregularHexMosaic(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_FX
    param\name = "IrregularHex"
    param\remarque = "Effet mosaïque hexagonal irrégulier"
    param\info[0] = "Taille des cellules"
    param\info[1] = "Taux d’irrégularité"
    param\info[2] = "Nombre de côtés"
    param\info[3] = "Rotation"
    param\info[4] = "Alpha"
    param\info[5] = "Contours"
    param\info[6] = "Alpha Contours"
    param\info[7] = "Masque binaire"
    param\info_data(0,0) = 4 : param\info_data(0,1) = 64  : param\info_data(0,2) = 12
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100 : param\info_data(1,2) = 50
    param\info_data(2,0) = 3 : param\info_data(2,1) = 12  : param\info_data(2,2) = 6
    param\info_data(3,0) = 0 : param\info_data(3,1) = 360 : 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) = 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(@IrregularHexMosaic_MT() , 7)
EndProcedure


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

Procedure KaleidoscopeEffect_MT(*p.parametre)
  Protected *src = *p\addr[0]
  Protected *dst = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected numSlices = *p\option[0]
  If numSlices < 1 : numSlices = 1 : EndIf

  Protected rotationDeg.f = *p\option[1] - 360
  Protected zoom.f = *p\option[2] / 100.0
  If zoom <= 0.01 : zoom = 0.01 : EndIf

  Protected angleOffset.f = Radian(rotationDeg)
  Protected angleStep.f = 2 * #PI / numSlices

  Protected cx = lg / 2
  Protected cy = ht / 2

  Protected startY = (*p\thread_pos * ht) / *p\thread_max
  Protected stopY  = ((*p\thread_pos + 1) * ht) / *p\thread_max - 1
  If stopY > ht - 1 : stopY = ht - 1 : EndIf

  Protected x, y, dx.f, dy.f, angle.f, dist.f, srcAngle.f
  Protected sx.f, sy.f, sxi, syi
  Protected offsetSrc, offsetDst

  For y = startY To stopY
    dy = y - cy
    For x = 0 To lg - 1
      dx = x - cx
      
      angle = ATan2(dy, dx) + angleOffset
      dist  = Sqr(dx*dx + dy*dy) * zoom

      ; ramener angle dans [0, 2PI]
      While angle < 0 : angle + 2 * #PI : Wend
      While angle >= 2 * #PI : angle - 2 * #PI : Wend

      ; Miroir tous les secteurs impairs
      srcAngle = Mod(angle, angleStep)
      If Mod(Int(angle / angleStep), 2) = 1
        srcAngle = angleStep - srcAngle
      EndIf

      sx = cx + Cos(srcAngle) * dist
      sy = cy + Sin(srcAngle) * dist

      Clamp(sx, 0, lg - 1)
      Clamp(sy, 0, ht - 1)

      sxi = Int(sx)
      syi = Int(sy)

      offsetSrc = (syi * lg + sxi) * 4
      offsetDst = (y * lg + x) * 4

      PokeL(*dst + offsetDst, PeekL(*src + offsetSrc))
    Next
  Next
EndProcedure

Procedure Kaleidoscope(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_FX
    param\name = "Kaleidoscope"
    param\remarque = "Effet kaléidoscopique avec rotation et zoom"
    param\info[0] = "Nb secteurs"
    param\info[1] = "Rotation"
    param\info[2] = "Zoom"
    param\info[3] = "Masque binaire"
    param\info_data(0,0) = 1  : param\info_data(0,1) = 24  : param\info_data(0,2) = 6
    param\info_data(1,0) = 0 : param\info_data(1,1) = 720 : param\info_data(1,2) = 360
    param\info_data(2,0) = 10 : param\info_data(2,1) = 500 : param\info_data(2,2) = 100
    param\info_data(3,0) = 0 : param\info_data(3,1) = 2 : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@KaleidoscopeEffect_MT() , 3)
  
EndProcedure

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

Procedure Mosaic_MT(*p.parametre)
  Protected start, stop, y, x, xx, yy
  Protected pixSize = *p\option[0]
  If pixSize < 1 : pixSize = 8 : EndIf
  
  Protected *source = *p\addr[0]
  Protected *cible = *p\addr[1]
  Protected lg = *p\lg
  Protected ht = *p\ht
  Protected pix, srcOffset, dstOffset

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

  start - Mod(start, pixSize)
  stop  - Mod(stop, pixSize)

  y = start
  While y <= stop
    x = 0
    While x < lg
      srcOffset = *source + (y * lg + x) * 4
      pix = PeekL(srcOffset)

      Protected blockBottom = y + pixSize - 1
      If blockBottom > ht - 1 : blockBottom = ht - 1 : EndIf
      Protected blockRight = x + pixSize - 1
      If blockRight > lg - 1 : blockRight = lg - 1 : EndIf

      For yy = y To blockBottom
        For xx = x To blockRight
          dstOffset = *cible + (yy * lg + xx) * 4
          PokeL(dstOffset, pix)
        Next
      Next

      x + pixSize
    Wend
    y + pixSize
  Wend
EndProcedure

Procedure Mosaic(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_FX
    param\name = "Mosaic"
    param\remarque = "Effet de pixelisation en blocs"
    param\info[0] = "Taille des blocs"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 32 : param\info_data(0,2) = 8
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2 : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Mosaic_MT() , 1)
  
EndProcedure

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

Re: filtre graphique

Message par manababel »

le dernier

autre.pbi

Code : Tout sélectionner

; Procédure thread pour l'effet "Charcoal" sur une image ARGB 32 bits

Procedure.f RandomFloat(min.f=0.0, max.f=1.0)
  ProcedureReturn min + (max - min) * Random(1000000) / 1000000.0
EndProcedure


Procedure ContrastColour(Colour,Scale.f)
  ; ContrastPixel(Red(Colour),Scale)
  ;Return Int(Float((Pixel*Scale))) 
  Protected r ,g , b
  getrgb(Colour , r , g , b)
  r = r * (1.0 + Scale)
  g = g * (1.0 + Scale)
  b = b * (1.0 + Scale)
  clamp_rgb(r , g , b)
  ProcedureReturn ((r << 16) + ( g << 8) + b)
EndProcedure

Procedure Charcoal_MT(*p.parametre)
  Protected i, x, y, pixel, a, r, g , b
  Protected r1 , g1 , b1
  Protected r2 , g2 , b2
  Protected w = *p\lg
  Protected h = *p\ht
  Protected intensity.f = 0.32 + (*p\option[0] / 100) ; 0.32 to 0.49
  Protected tolerance.f = 1.0 - intensity
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected totalPixels = w * h
  Protected startPos = (*p\thread_pos * totalPixels) / *p\thread_max
  Protected endPos   = ((*p\thread_pos + 1) * totalPixels) / *p\thread_max
  Protected col, grey, grade
  
  Protected Definition.f
  Protected Colour
  Protected Chalking
  For i = startPos To endPos - 1
    *srcPixel = *p\addr[0] + (i << 2)
    *dstPixel = *p\addr[1] + (i << 2)   
    Colour = *srcPixel\l
    getrgb(Colour , r , g , b)
    Chalking = ((r * 1225 + g * 2405 + b * 466) >> 12)
    grade = intensity * 64.0
    If Chalking > (255.0 - grade)
      r = 255 : g = 255 : b = 255
    Else
      getrgb(Colour , r1 , g1 , b1)
      r1 = r1 * (1.0 + Intensity)
      g1 = g1 * (1.0 + Intensity)
      b1 = b1 * (1.0 + Intensity)
      clamp_rgb(r1 , g1 , b1)
      Colour = ((r1 << 16) + ( g1 << 8) + b1)
      Definition = RandomFloat(0,1)
      If (Definition>Tolerance)
        getrgb(Pixel , r1 , g1 , b1)
        getrgb(Colour , r2 , g2 , b2)
        r1 = ((r2 - r1) * Tolerance) + r1
        clamp_rgb(r1 , g1 , b1)
        Pixel = ((r1 << 16) + ( g1 << 8) + b1)
      EndIf
      getrgb(Pixel , r , g , b)
      Grey = ((r * 1225 + g * 2405 + b * 466) >> 12)
      r = grey : g = r : b = r
      Grade=(Intensity*64)
      If (grey > grade) And (grey < (255.0 - grade))
        If RandomFloat(0 , 100) >= Int(tolerance * 100.0)
          r + grade
          g + grade * 0.5
          clamp(r, 0, 224)
          clamp(g, 0, 224)
        EndIf
      Else
        If r > 127 : r = 224 : Else : r = 0 : EndIf
        If g > 127 : g = 224 : Else : g = 0 : EndIf
        If b > 127 : b = 224 : Else : b = 0 : EndIf
      EndIf
    EndIf
    *dstPixel\l = (a << 24) | (r << 16) | (g << 8) | b
  Next
EndProcedure

Procedure CharcoalImage(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_autre
    param\name = "Charcoal"
    param\remarque = ""
    param\info[0] = "Intensité"
    param\info[1] = "Masque binaire"
    param\info_data(0,0) = 0 : param\info_data(0,1) = 17 : param\info_data(0,2) = 8
    param\info_data(1,0) = 0 : param\info_data(1,1) = 2  : param\info_data(1,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Charcoal_MT() , 1)
EndProcedure



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


Procedure Emboss_MT(*p.parametre)
  Protected x, y
  Protected w = *p\lg
  Protected h = *p\ht
  Protected a1, r1, g1, b1
  Protected a2, r2, g2, b2
  Protected r, g, b
  Protected gray1, gray2, diff
  Protected *srcPixel.Pixel32
  Protected *dstPixel.Pixel32
  Protected contrast = *p\option[0]
  Protected direction = *p\option[1]
  Protected renforcer = *p\option[2]
  Protected inversion = *p\option[3]
  

  ; Clamp du contraste pour éviter les débordements
  clamp(contrast, 1, 1024)

  ; Détermination direction du relief
  Protected dx, dy
  Select direction
    Case 0 : dx = 1 : dy = 1   ; ↘ Bas-droite
    Case 1 : dx = 0 : dy = 1   ; ↓ Bas
    Case 2 : dx = 1 : dy = 0   ; → Droite
    Case 3 : dx = -1 : dy = -1 ; ↖ Haut-gauche
    Case 4 : dx = 0 : dy = -1  ; ↑ Haut
    Case 5 : dx = -1 : dy = 0  ; ← Gauche
    Default : dx = 1 : dy = 1
  EndSelect

  ; Calcul des limites verticales pour multithreading
  Protected startY = (*p\thread_pos * h) / *p\thread_max
  Protected endY   = ((*p\thread_pos + 1) * h) / *p\thread_max

  Protected x2, y2, index, index2, dot.f, lx.f, ly.f, len.f

  ; Traitement
  For y = startY To endY - 1
    For x = 0 To w - 1
      x2 = x + dx
      y2 = y + dy
      If x2 < 0 Or x2 >= w Or y2 < 0 Or y2 >= h : Continue : EndIf

      index  = y  * w + x
      index2 = y2 * w + x2

      *srcPixel = *p\addr[0] + (index << 2)
      GetARGB(*srcPixel\l, a1, r1, g1, b1)

      *srcPixel = *p\addr[0] + (index2 << 2)
      GetARGB(*srcPixel\l, a2, r2, g2, b2)
      gray1 = ((r1 + g1 + b1) * 85) >> 8
      gray2 = ((r2 + g2 + b2) * 85) >> 8
      If inversion
        If renforcer
          diff = 128 - ((gray1 - gray2) * (contrast << 2)) >> 8
        Else
          diff = 128 - ((gray1 - gray2) * contrast) >> 7
        EndIf
      Else
        If renforcer
          diff = ((gray1 - gray2) * (contrast << 2)) >> 8 + 128
        Else
          diff = ((gray1 - gray2) * contrast) >> 7 + 128
        EndIf
      EndIf
      clamp(diff, 0, 255)
      *dstPixel = *p\addr[1] + (index << 2)
      *dstPixel\l = (a1 << 24) | (diff << 16) | (diff << 8) | diff
    Next
  Next
EndProcedure


; Procédure principale d'effet Emboss (relief directionnel)

Procedure Emboss(*param.parametre)
  If param\info_active
    param\typ = #Filter_Type_autre
    param\name = "Emboss"
    param\remarque = "Emboss (relief directionnel niveaux de gris)"
    param\info[0] = "Contraste"
    param\info[1] = "Direction"
    param\info[2] = "Renforcer"
    param\info[3] = "Inversion"
    param\info[4] = "Masque binaire"
    param\info_data(0,0) = 0    : param\info_data(0,1) = 1024 : param\info_data(0,2) = 512
    param\info_data(1,0) = 0    : param\info_data(1,1) = 5    : 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) = 2   : param\info_data(4,2) = 0
    ProcedureReturn
  EndIf
  filter_start(@Emboss_MT() , 4)
EndProcedure

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

Macro GlowEffect_IIR_Declare()
  Protected w = *param\lg
  Protected h = *param\ht
  Protected start = (*param\thread_pos * h) / *param\thread_max
  Protected stop   = ((*param\thread_pos + 1) * h) / *param\thread_max
  Protected x, y, pos , col
  Protected a , r, g, b, lum
  Protected GlowStrength = *param\option[0]
  Protected Radius = 50 - *param\option[1]
  Protected seuil = param\option[2]
  Protected Alpha, inv_Alpha, mul = 256
  Alpha = (Exp(-2.3 / (Radius + 1.0))) * mul
  inv_Alpha = mul - Alpha
  Protected glowR = *param\addr[2]
  Protected glowG = *param\addr[3]
  Protected glowB = *param\addr[4]
EndMacro

Macro GlowEffect_IIR_sp1()
  ;r = (Alpha * r + inv_Alpha * glowR(pos)) >> 8
  ;g = (Alpha * g + inv_Alpha * glowG(pos)) >> 8
  ;b = (Alpha * b + inv_Alpha * glowB(pos)) >> 8
  ;glowR(pos) = r : glowG(pos) = g : glowB(pos) = b
  r = (Alpha * r + inv_Alpha * PeekL(glowR + pos)) >> 8
  g = (Alpha * g + inv_Alpha * PeekL(glowG + pos)) >> 8
  b = (Alpha * b + inv_Alpha * PeekL(glowB + pos)) >> 8
  PokeL((glowR + pos) , r)
  PokeL((glowG + pos) , g)
  PokeL((glowB + pos) , b)
EndMacro

Procedure GlowEffect_IIR_MT_sp1(*param.parametre)
  ; 1. Extraire les zones lumineuses
  GlowEffect_IIR_Declare()
  For y = start To stop - 1
    For x = 0 To w - 1
      pos = (y * w + x) << 2
      col = PeekL(*param\addr[0] + pos)
      getrgb(col , r , g , b)
      lum = ((r + g + b) * 85) >> 8
      If lum > seuil ; seuil de brillance
        PokeL((glowR + pos) , r)
        PokeL((glowG + pos) , g)
        PokeL((glowB + pos) , b)
        ;glowR(pos) = r
        ;glowG(pos) = g
        ;glowB(pos) = b
      Else
        PokeL((glowR + pos) , 0)
        PokeL((glowG + pos) , 0)
        PokeL((glowB + pos) , 0)
        ;glowR(pos) = 0
        ;glowG(pos) = 0
        ;glowB(pos) = 0
      EndIf
    Next
  Next
EndProcedure

Procedure GlowEffect_IIR_MT_spx(*param.parametre)
  GlowEffect_IIR_Declare()
  ; 2. Flou IIR horizontal
  For y = start To stop - 1
    pos = (y * w) << 2
    ;r = glowR(pos) : g = glowG(pos) : b = glowB(pos)
    r = PeekL(glowR + pos)
    g = PeekL(glowG + pos)
    b = PeekL(glowB + pos)
    For x = 1 To w - 1
      pos = (y * w + x) << 2
      GlowEffect_IIR_sp1()
    Next
    pos = (y * w + (w - 1)) << 2
    ;r = glowR(pos) : g = glowG(pos) : b = glowB(pos)
    r = PeekL(glowR + pos)
    g = PeekL(glowG + pos)
    b = PeekL(glowB + pos)
    For x = w - 2 To 0 Step -1
      pos = (y * w + x) << 2
      GlowEffect_IIR_sp1()
    Next
  Next
EndProcedure

Procedure GlowEffect_IIR_MT_spy(*param.parametre)
  ; 3. Flou IIR vertical
  GlowEffect_IIR_Declare()
  start = (*param\thread_pos * w) / *param\thread_max
  stop   = ((*param\thread_pos + 1) * w) / *param\thread_max
  For x = start To stop - 1
    pos = x << 2
    ;r = glowR(pos) : g = glowG(pos) : b = glowB(pos)
    r = PeekL(glowR + pos)
    g = PeekL(glowG + pos)
    b = PeekL(glowB + pos)
    For y = 1 To h - 1
      pos = (y * w + x) << 2
     GlowEffect_IIR_sp1()
    Next
    pos = ((h - 1) * w + x) << 2
    ;r = glowR(pos) : g = glowG(pos) : b = glowB(pos)
    r = PeekL(glowR + pos)
    g = PeekL(glowG + pos)
    b = PeekL(glowB + pos)
    For y = h - 2 To 0 Step -1
      pos = (y * w + x) << 2
      GlowEffect_IIR_sp1()
    Next
  Next
EndProcedure


Procedure GlowEffect_IIR_MT(*param.parametre)
  GlowEffect_IIR_Declare() 
  ; 4. Additionner le glow à l'image d’origine
  For y = start To stop - 1
    For x = 0 To w - 1
      pos = (y * w + x) << 2
      col = PeekL(*param\addr[0] + pos )
        r = ((col >> 16) & $FF) + (PeekL(glowR + pos) * GlowStrength) >> 4
        g = ((col >> 8) & $FF)  + (PeekL(glowG + pos) * GlowStrength) >> 4
        b = (col & $FF)         + (PeekL(glowB + pos) * GlowStrength) >> 4
      clamp_rgb(r,g,b)
      PokeL(*param\addr[1] + pos , (r << 16) | (g << 8) | b)
    Next
  Next
EndProcedure

Procedure GlowEffect_IIR(*param.parametre)
  If *param\info_active
    param\typ = #Filter_Type_autre
    param\name = "GlowEffect_IIR"
    param\remarque = ""
    *param\info[0] = "GlowStrength"
    *param\info[1] = "Radius"
    *param\info[2] = "seuil"
    *param\info[3] = "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) = 50 : param\info_data(1,2) = 0
    param\info_data(2,0) = 0 : param\info_data(2,1) = 255 : param\info_data(2,2) = 127
    param\info_data(3,1) = 0 : param\info_data(3,1) = 2  : param\info_data(3,2) = 0
    ProcedureReturn
  EndIf
  Protected total = *param\lg * *param\ht * 4
  If *param\source = 0 Or *param\cible = 0 : ProcedureReturn : EndIf
  
  Protected *glowR = AllocateMemory(total)
  Protected *glowG = AllocateMemory(total)
  Protected *glowB = AllocateMemory(total)
  
  Protected *tempo
  If *param\source = *param\cible
    *tempo = AllocateMemory(total)
    If Not *tempo : ProcedureReturn : EndIf
    CopyMemory(*param\source , *tempo , total)
    *param\addr[0] = *tempo
  Else
    *param\addr[0] = *param\source
  EndIf
  
  *param\addr[1] = *param\cible
  *param\addr[2] = *glowR
  *param\addr[3] = *glowG
  *param\addr[4] = *glowB
  
  MultiThread_MT(@GlowEffect_IIR_MT_sp1())
  MultiThread_MT(@GlowEffect_IIR_MT_spx())
  MultiThread_MT(@GlowEffect_IIR_MT_spy())
  MultiThread_MT(@GlowEffect_IIR_MT())
  
  ; Application d’un second passage pour le masque alpha (optionnel)
  If *param\mask And *param\option[3] : *param\mask_type = *param\option[3] - 1 : MultiThread_MT(@_mask()) : EndIf
  
  FreeMemory(*glowR)
  FreeMemory(*glowG)
  FreeMemory(*glowB)
  If *tempo : FreeMemory(*tempo) : EndIf
EndProcedure

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


Procedure Histogram_SP1_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected lg  = *param\lg
  Protected ht  = *param\ht
  Protected total = lg * ht
  
  Protected start = (*param\thread_pos * total) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * total) / *param\thread_max
  
  Protected i, pix, r, g, b
  
  ; Histogrammes locaux (par thread)
  Protected Dim histR(255)
  Protected Dim histG(255)
  Protected Dim histB(255)
  
  ; Comptage local
  For i = start To stop - 1
    pix = PeekL(*source + i * 4)
    getrgb(pix, r, g, b)
    histR(r) + 1
    histG(g) + 1
    histB(b) + 1
  Next
  
  ; Fusion (ajout dans les histogrammes globaux)
  For i = 0 To 255
    PokeL(*param\addr[2] + i * 4, PeekL(*param\addr[2] + i * 4) + histR(i))
    PokeL(*param\addr[3] + i * 4, PeekL(*param\addr[3] + i * 4) + histG(i))
    PokeL(*param\addr[4] + i * 4, PeekL(*param\addr[4] + i * 4) + histB(i))
  Next
EndProcedure


Procedure Histogram_SP2_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 minr = *param\option[4]
  Protected ming = *param\option[5]
  Protected minb = *param\option[6]
  Protected maxr = *param\option[7]
  Protected maxg = *param\option[8]
  Protected maxb = *param\option[9]
  
  Protected intensity.f
  If *param\option[1] ; mode automatique
    Protected rangeR = maxr - minr
    Protected rangeG = maxg - ming
    Protected rangeB = maxb - minb
    Protected avgRange = (rangeR + rangeG + rangeB) / 3
    intensity = 1.0 - (avgRange / 255.0) ; <-- Vérifier si c'est bien ce que tu veux
    clamp(intensity , 0 , 1)
  Else
    intensity = (*param\option[0] - 100) / 100.0
  EndIf
  
  Protected start = (*param\thread_pos * total) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * total) / *param\thread_max
  
  Protected i, pix, ro, go, bo
  Protected r, g, b
  
  For i = start To stop - 1
    pix = PeekL(*source + i * 4)
    getrgb(pix, ro, go, bo)
    
    Protected denomR = maxr - minr : If denomR = 0 : denomR = 1 : EndIf
    Protected denomG = maxg - ming : If denomG = 0 : denomG = 1 : EndIf
    Protected denomB = maxb - minb : If denomB = 0 : denomB = 1 : EndIf
    
    r = (PeekL(*param\addr[5] + ro * 4) - minr) * 255 / denomR
    g = (PeekL(*param\addr[6] + go * 4) - ming) * 255 / denomG
    b = (PeekL(*param\addr[7] + bo * 4) - minb) * 255 / denomB
    
    ; Mix avec couleur originale selon intensité
    r = ro * (1.0 - intensity) + r * intensity
    g = go * (1.0 - intensity) + g * intensity
    b = bo * (1.0 - intensity) + b * intensity
    
    clamp_rgb(r, g, b)
    PokeL(*cible + i * 4, (r << 16) | (g << 8) | b)
  Next
EndProcedure

; ---------------- Procédure principale ----------------
Procedure Histogram(*param.parametre)
  If *param\info_active
    *param\name = "Histogram"
    *param\typ  = #Filter_Type_autre
    *param\remarque = ""
    *param\info[0] = "Intensité"
    *param\info[1] = "Mode auto"
    *param\info[2] = "Masque"
    *param\info_data(0,0)=0 : *param\info_data(0,1)=200 : *param\info_data(0,2)=100
    *param\info_data(1,0)=0 : *param\info_data(1,1)=1 : *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
  
  If *param\source=0 Or *param\cible=0 : ProcedureReturn : EndIf
  Protected i, r, g, b
  Protected minr, ming, minb
  Protected maxr, maxg, maxb
  
  Protected *tempo
  If *param\source = *param\cible
    *tempo = AllocateMemory(*param\lg * *param\ht * 4)
    If Not *tempo : ProcedureReturn : EndIf
    CopyMemory(*param\source , *tempo , *param\lg * *param\ht * 4)
    *param\addr[0] = *tempo
  Else
    *param\addr[0] = *param\source
  EndIf
  
  *param\addr[1] = *param\cible
  
; Allocation pour histogrammes par canal (valeurs Long)
*param\addr[2] = AllocateMemory(256 * 4) ; Histogramme R
*param\addr[3] = AllocateMemory(256 * 4) ; Histogramme G
*param\addr[4] = AllocateMemory(256 * 4) ; Histogramme B
  
; Allocation pour histogrammes cumulés
*param\addr[5] = AllocateMemory(256 * 4)
*param\addr[6] = AllocateMemory(256 * 4)
*param\addr[7] = AllocateMemory(256 * 4)
  
  ; Étape 1 - Construire les histogrammes
  MultiThread_MT(@Histogram_SP1_MT())
  
  ; Étape 2 - Cumul
  Protected cumulR, cumulG, cumulB
  cumulR = 0 : cumulG = 0 : cumulB = 0
  For i = 0 To 255
    cumulR + PeekL(*param\addr[2] + i * 4)
    cumulG + PeekL(*param\addr[3] + i * 4)
    cumulB + PeekL(*param\addr[4] + i * 4)
    
    PokeL(*param\addr[5] + i * 4, cumulR)
    PokeL(*param\addr[6] + i * 4, cumulG)
    PokeL(*param\addr[7] + i * 4, cumulB)
  Next
  
  ; Étape 3 - Min / Max
  minr = $7FFFFFFF : ming = $7FFFFFFF : minb = $7FFFFFFF
  maxr = 0 : maxg = 0 : maxb = 0
  
  For i = 0 To 255
    r = PeekL(*param\addr[5] + i * 4)
    g = PeekL(*param\addr[6] + i * 4)
    b = PeekL(*param\addr[7] + i * 4)
    
    If r < minr : minr = r : EndIf
    If g < ming : ming = g : EndIf
    If b < minb : minb = b : EndIf
    
    If r > maxr : maxr = r : EndIf
    If g > maxg : maxg = g : EndIf
    If b > maxb : maxb = b : EndIf
  Next
  
  *param\option[4] = minr
  *param\option[5] = ming
  *param\option[6] = minb
  *param\option[7] = maxr
  *param\option[8] = maxg
  *param\option[9] = maxb  
  
  ; Étape 4 - Normalisation finale
  MultiThread_MT(@Histogram_SP2_MT())
  
  If *param\mask And *param\option[2] : *param\mask_type = *param\option[2] - 1 : MultiThread_MT(@_mask()) : EndIf
  
  ; Nettoyage
  FreeMemory(*param\addr[2])
  FreeMemory(*param\addr[3])
  FreeMemory(*param\addr[4])
  FreeMemory(*param\addr[5])
  FreeMemory(*param\addr[6])
  FreeMemory(*param\addr[7])
  If *tempo : FreeMemory(*tempo) : EndIf
EndProcedure

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

; --- Macro d'init commune (pixel déclaré et clamp start/stop) ---
Macro pencil_Blur_IIR_int(var)
  Protected *pix32.pixel32
  Protected *dst32.pixel32 = *param\addr[0]
  Protected lg       = *param\lg
  Protected ht       = *param\ht
  Protected alpha    = Int(Exp(-2.3 / *param\option[0]) * 256 + 0.5)
  Protected inv_alpha= 256 - alpha
  Protected x, y ,pos , mem
  Protected r, g, b
  Protected r1, g1, b1
  Protected start = (*param\thread_pos * var) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * var) / *param\thread_max
  If start < 0 : start = 0 : EndIf
  If stop  > var : stop = var : EndIf
EndMacro

Macro pencil_Blur_IIR_sp0(r , g , b)
  *pix32 = *dst32 + (pos * 4)
  getrgb(*pix32\l ,r , g , b) 
  r = r << 8
  g = g << 8
  b = b << 8
EndMacro

Macro pencil_Blur_IIR_sp1()
  pencil_Blur_IIR_sp0(r1 , g1 , b1)
  r = (r * alpha + inv_alpha * r1) >> 8 
  g = (g * alpha + inv_alpha * g1) >> 8
  b = (b * alpha + inv_alpha * b1) >> 8
  r1 = (r + 128 ) >> 8
  g1 = (g + 128 ) >> 8
  b1 = (b + 128 ) >> 8
  *pix32\l = (r1 << 16) | (g1 << 8) | b1
EndMacro

Procedure pencil_Blur_IIR_y_MT(*param.parametre)
  pencil_Blur_IIR_int(*param\ht)
  For y = start To stop - 1               ; Parcourt les lignes de haut en bas
    pos = (y * lg)
    mem = pos 
    pencil_Blur_IIR_sp0(r , g , b)
    For x = 1 To lg - 1 : pos = (mem + x) : pencil_Blur_IIR_sp1() : Next ; Gauche → droite
    pos = (mem + (lg - 1))
    pencil_Blur_IIR_sp0(r , g , b)
    For x = lg - 2 To 0 Step -1 : pos = (y * lg + x) : pencil_Blur_IIR_sp1() : Next ; Droite → gauche
  Next
EndProcedure

Procedure pencil_Blur_IIR_x_MT(*param.parametre)
  pencil_Blur_IIR_int(*param\lg)
  For x = start To stop - 1               ; Parcourt les colonnes de gauche à droite
    pos = x 
    pencil_Blur_IIR_sp0(r , g , b)
    For y = 1 To ht - 1 : pos = (y * lg + x) : pencil_Blur_IIR_sp1() : Next ; Haut → bas
    pos = ((ht - 1) * lg + x) 
    pencil_Blur_IIR_sp0(r , g , b)
    For y = ht - 2 To 0 Step -1 : pos = (y * lg + x) : pencil_Blur_IIR_sp1() : Next ; Bas → haut
  Next
EndProcedure

;--
Procedure pencil_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 *cible = *param\cible
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected *tempo = *param\addr[0]
  Protected lx = *param\addr[1]
  Protected ly = *param\addr[2]
  ; Paramètres du filtre
  Protected nrx = param\option[17] ; Largeur de la fenêtre de flou (X)
  Protected nry = param\option[18] ; Hauteur de la fenêtre de flou (Y)
  Protected div = param\option[19] ; Facteur de division (65536 / (nrx * nry))
  ; Informations de thread (multi-threading horizontal)
  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)
  Protected Dim r.l(lg)
  Protected Dim g.l(lg)
  Protected 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) * 4)
    *srcPixel1 = *cible + ((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) * 4)
    p2 = PeekL(ly + (j * 4))
    *srcPixel1 = *cible + (p1 * lg) << 2
    *srcPixel2 = *cible + (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 * 4)
      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) * 4)
      p2 = PeekL(lx + i * 4)
      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) >> 16 : r1 = (rx1 * div) >> 16 : g1 = (gx1 * div) >> 16 : b1 = (bx1 * div) >> 16
      ; Écriture dans le buffer temporaire
      *dstPixel = *tempo + ((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

;--

Macro pencil_sobel_4d_sp(i)
  getrgb(PeekL(pos + 0) ,  r , g , b)
  p(i + 0 ) = ((r * 76 + g * 150 + b * 30) >> 8)
  getrgb(PeekL(pos + 4) ,  r , g , b)
  p(i + 1 ) = ((r * 76 + g * 150 + b * 30) >> 8)
  getrgb(PeekL(pos + 8) ,  r , g , b)
  p(i + 2 ) = ((r * 76 + g * 150 + b * 30) >> 8)
EndMacro


Procedure pencil_sobel_4d_MT(*param.parametre)
  Protected *source = *param\addr[0]
  Protected *cible  = *param\addr[1]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected mul.f = *param\option[3]
  Protected pos , f
  Protected r , g , b
  Protected c0 , c45 , c90 , c135
  Protected cx0 , cx45 , cx90 , cx135
  Protected cy0 , cy45 , cy90 , cy135
  clamp(mul, 0, 100)
  mul = mul * 0.1
  Protected x, y
  Protected Dim p(8)
  
  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
  
  For y = startPos To endPos
    For x = 1 To lg - 2
      
      ; Lecture des 9 pixels (3x3 autour du pixel courant)
      pos = *source + ((y - 1) * lg + (x - 1)) * 4
      pencil_sobel_4d_sp(0)
        
      pos = *source + (y * lg + (x - 1)) * 4
      pencil_sobel_4d_sp(3)
      
      pos = *source + ((y + 1) * lg + (x - 1)) * 4
      pencil_sobel_4d_sp(6)
      
      ; --- Sobel 0° ---
      cx0 = p(2) + 2 * p(5) + p(8) - (p(0) + 2 * p(3) + p(6))
      cy0 = p(0) + 2 * p(1) + p(2) - (p(6) + 2 * p(7) + p(8))
      
      ; --- Sobel 45° ---
      cx45 = p(0) + 2 * p(1) + p(2) - (p(6) + 2 * p(7) + p(8))
      cy45 = p(2) + 2 * p(5) + p(8) - (p(0) + 2 * p(3) + p(6))
      
      ; --- Sobel 90° ---
      cx90 = p(6) + 2 * p(7) + p(8) - (p(0) + 2 * p(1) + p(2))
      cy90 = p(2) + 2 * p(5) + p(8) - (p(0) + 2 * p(3) + p(6))
      
      ; --- Sobel 135° ---
      cx135 = p(6) + 2 * p(3) + p(0) - (p(8) + 2 * p(5) + p(2))
      cy135 = p(0) + 2 * p(3) + p(6) - (p(2) + 2 * p(5) + p(8))
      
      ; Magnitudes
      c0    = Sqr(cx0   * cx0   + cy0   * cy0)
      c45   = Sqr(cx45  * cx45  + cy45  * cy45)
      c90   = Sqr(cx90  * cx90  + cy90  * cy90)
      c135  = Sqr(cx135 * cx135 + cy135 * cy135)
      
      ; Max
      max4(f , c0 , c45 , c90 , c135)
      f * mul
      clamp(f, 0, 255)
      PokeL(*cible + (y * lg + x) * 4, (255-f) * $10101)
      
    Next
  Next
EndProcedure

; --

Procedure pencil_color_dodge(*param.parametre)
  Protected *dodge = *param\addr[0]
  Protected *blur  = *param\addr[1]
  Protected *cible = *param\addr[2]
  Protected lg     = *param\lg
  Protected ht     = *param\ht
  Protected total  = lg * ht
  
  Protected intensity = (*param\option[1] * 255) / 100
  Protected gamma.f   = *param\option[2] * 0.1
  
  Protected start = (*param\thread_pos * total) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * total) / *param\thread_max
  If stop > total : stop = total : EndIf
  
  Protected i, pos
  Protected r, g, b
  Protected r1, g1, b1
  Protected r2, g2, b2
  Protected r3, g3, b3
  
  Protected Dim GammaLUT(255)
  
  ; --- Pré-calcul de la LUT gamma ---
  For i = 0 To 255
    GammaLUT(i) = Int(255.0 * Pow(i / 255.0, gamma))
    clamp(GammaLUT(i), 0, 255)
  Next
  
  ; --- Boucle principale ---
  For i = start To stop - 1
    pos = i << 2
    
    getrgb(PeekL(*dodge + pos), r1, g1, b1)
    getrgb(PeekL(*blur  + pos), r2, g2, b2)
    
    ; Inversion
    r3 = 255 - r1 : If r3 < 1 : r3 = 1 : EndIf
    g3 = 255 - g1 : If g3 < 1 : g3 = 1 : EndIf
    b3 = 255 - b1 : If b3 < 1 : b3 = 1 : EndIf
    
    ; Division (Color Dodge)
    r = (r2 << 8) / r3
    g = (g2 << 8) / g3
    b = (b2 << 8) / b3
    
    ; Application intensité
    r = (r * intensity) >> 8
    g = (g * intensity) >> 8
    b = (b * intensity) >> 8
    
    clamp_rgb(r, g, b)
    
    ; Application gamma via LUT
    r = GammaLUT(r)
    g = GammaLUT(g)
    b = GammaLUT(b)
    
    PokeL(*cible + pos, (r << 16) | (g << 8) | b)
  Next
  
  FreeArray(GammaLUT())
EndProcedure


Procedure pencil_gray_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 start = (*param\thread_pos * total) / *param\thread_max
  Protected stop  = ((*param\thread_pos + 1) * total) / *param\thread_max
  If stop > total : stop = total : EndIf
  
  Protected i, lum, a, r, g, b
  
  For i = start To stop - 1
    getargb(PeekL(*source + (i << 2)), a, r, g, b)
    lum = ((r * 76 + g * 150 + b * 30) >> 8)
    PokeL(*cible + (i << 2), lum * $10101) ; Écrit R=G=B=lum
  Next
EndProcedure

Procedure pencil( *param.parametre )
  ; Mode interface : renseigner les informations sur les options si demandé
  If param\info_active
    param\typ = #Filter_Type_autre
    param\name = "pencil"
    param\remarque = ""
    param\info[0] = "option 1"          
    param\info[1] = "intensité mellange"       
    param\info[2] = "gamma"   
    param\info[3] = "intensité contour"  
    param\info[4] = "Style du crayon"
    param\info[5] = "Masque binaire" 
    param\info_data(0,0) = 1 : param\info_data(0,1) = 80 : param\info_data(0,2) = 3
    param\info_data(1,0) = 1 : param\info_data(1,1) = 100 : param\info_data(1,2) = 10
    param\info_data(2,0) = 1 : param\info_data(2,1) = 100   : param\info_data(2,2) = 10
    param\info_data(3,0) = 1 : param\info_data(3,1) = 100   : param\info_data(3,2) = 10
    param\info_data(4,0) = 0 : param\info_data(4,1) = 9   : 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
  
  Protected i 
  Protected *source = *param\source
  Protected *cible = *param\cible
  Protected *mask = *param\mask
  Protected lg = *param\lg
  Protected ht = *param\ht
  If *source = 0 Or *cible = 0 : ProcedureReturn : EndIf
  
  
  Protected *gray= AllocateMemory(lg * ht * 4)
  Protected *blur = AllocateMemory(lg * ht * 4)
  Protected *sobel = AllocateMemory(lg * ht * 4)
  Protected *tmp = AllocateMemory(lg * ht * 4)
  
  Protected thread = CountCPUs(#PB_System_CPUs)
  clamp(thread , 1 , 128)
  Protected Dim tr(thread)
  
  
  Protected *tempo
  If *source = *cible
    *tempo = AllocateMemory(lg * ht * 4)
    If Not *tempo : ProcedureReturn :EndIf
    CopyMemory(*source , *tempo , lg * ht * 4)
    *param\addr[0] = *tempo
  Else
    *param\addr[0] = *source
  EndIf
  *param\addr[1] = *gray
  MultiThread_MT(@pencil_gray_MT())
  
  If blur_box_create_limit(lg, ht, 3, 3, 0)
    ;Pré-filtrage ou Post-traitement optionnel : blurbox 2 pass
    Protected *tempo2 = AllocateMemory(lg * ht * 4)
    ; Passage des paramètres au thread de travail
    param\addr[0] = *tempo2
    param\addr[1] = *blur_box_limit_x
    param\addr[2] = *blur_box_limit_y
    param\option[17] = 3
    param\option[18] = 3
    param\option[19] = Int(65536 / (3 * 3)) ; Facteur normalisation
    Protected passe                                         ; Boucle de passes de flou (1 à 3)
    For passe = 1 To 2
      MultiThread_MT(@pencil_Guillossien_MT())
      CopyMemory(*tempo2, *cible, lg * ht * 4)
    Next
    blur_box_free_limit()
  EndIf
  
  CopyMemory(*gray , *blur , lg * ht * 4)
  *param\addr[0] = *blur
  MultiThread_MT(@pencil_Blur_IIR_y_MT())
  MultiThread_MT(@pencil_Blur_IIR_x_MT())
  
  ;[Post-traitement (optionnel : contraste, correction, niveaux)] correction gamma
  
  *param\addr[0] = *blur
  *param\addr[1] = *sobel
  MultiThread_MT(@pencil_sobel_4d_MT())
  
  Select *param\option[4]
      
    Case 0 ; Style par défaut (actuel)
      *param\addr[0] = *sobel
      *param\addr[1] = *blur
      *param\addr[2] = *cible
      MultiThread_MT(@pencil_color_dodge())
      
    Case 1 ; Contour seul (line art)
      *param\addr[0] = *gray
      *param\addr[1] = *cible
      MultiThread_MT(@pencil_sobel_4d_MT()) ; Pas de dodge ici
      
    Case 2 ; Crayon sombre (inversé)
      *param\addr[0] = *blur
      *param\addr[1] = *sobel
      *param\addr[2] = *cible
      MultiThread_MT(@pencil_color_dodge()) ; mais inverser couleur (voir plus bas)
      
    Case 3 ; Crayon doux (moins de contours)
      ;*param\option[3] * 0.5 ; réduit l’intensité des contours
      *param\addr[0] = *sobel
      *param\addr[1] = *blur
      *param\addr[2] = *cible
      MultiThread_MT(@pencil_color_dodge())
      
    Case 4 ; Crayon esquissé (avec bruit léger)
      For i = 0 To (lg * ht - 1)
        PokeL(*blur + i << 2, PeekL(*blur + i << 2) + Random(10) - 5)
      Next
      *param\addr[0] = *sobel
      *param\addr[1] = *blur
      *param\addr[2] = *cible
      MultiThread_MT(@pencil_color_dodge())
      
    Case 5 ; Style charbon (charcoal)
           ; Accentue le contour et obscurcit l’image
      For i = 0 To (lg * ht - 1)
        Protected v = PeekL(*sobel + i << 2)
        v = v + (255 - PeekL(*blur + i)) >> 1
        clamp(v, 0, 255)
        PokeL(*sobel + i << 2, v)
      Next
      *param\addr[0] = *sobel
      *param\addr[1] = *blur
      *param\addr[2] = *cible
      ;MultiThread_MT(@pencil_color_dodge_invert()) ; version sombre
      
    Case 6 ; Style estampe (high contrast)
      For i = 0 To (lg * ht - 1)
        v = PeekL(*sobel + i << 2)
        If v > 128
          PokeL(*sobel + i << 2, 255)
        Else
          PokeL(*sobel + i << 2, 0)
        EndIf
      Next
      *param\addr[0] = *sobel
      *param\addr[1] = *blur
      *param\addr[2] = *cible
      MultiThread_MT(@pencil_color_dodge())
      
    Case 7 ; Style peinture crayonnée
           ; Mélange les niveaux de gris et flou pour créer un fond doux
      For i = 0 To (lg * ht - 1)
        Protected v1 = PeekL(*gray + i << 2)
        Protected v2 = PeekL(*blur + i << 2)
        PokeL(*blur + i << 2, (v1 * 3 + v2) >> 2) ; mix 75/25
      Next
      *param\addr[0] = *sobel
      *param\addr[1] = *blur
      *param\addr[2] = *cible
      MultiThread_MT(@pencil_color_dodge())
      
    Case 8 ; Style pastel doux
      ;*param\option[3] * 0.3 ; contours très doux
      ;*param\option[2] * 0.6 ; gamma plus clair
      
      For i = 0 To (lg * ht - 1)
         v1 = PeekL(*gray + i)
         v2 = PeekL(*blur + i)
        PokeL(*blur + i << 2, (v1 + v2 * 3) >> 2) ; mix 25/75 (plus doux)
      Next
      *param\addr[0] = *sobel
      *param\addr[1] = *blur
      *param\addr[2] = *cible
      MultiThread_MT(@pencil_color_dodge())
      
    Case 9 ; Style cartoon
      *param\addr[0] = *gray
      *param\addr[1] = *tmp
      MultiThread_MT(@pencil_sobel_4d_MT()) ; détection des contours
      
      For i = 0 To (lg * ht - 1)
        Protected lum = PeekL(*gray + i << 2)
        Protected steps = 4
        Protected level = (lum * steps) / 256
        lum = (255 * level) / (steps - 1) ; postérisation 4 niveaux
        clamp(lum, 0, 255)
        PokeL(*gray + i << 2, lum)
      Next
      
      ; Mix image postérisée et contours
      For i = 0 To (lg * ht - 1)
        Protected edge = PeekL(*tmp + i << 2)
        Protected base = PeekL(*gray + i << 2)
        Protected final = base - (edge >> 1)
        clamp(final, 0, 255)
        PokeL(*cible + i * 4, final | (final << 8) | (final << 16))
      Next
      
      
  EndSelect
  
  ;*param\addr[0] = *sobel
  ;*param\addr[1] = *blur
  ;*param\addr[2] = *cible
  ;MultiThread_MT(@pencil_color_dodge())
  
  If *param\mask And *param\option[5] : *param\mask_type = *param\option[5] - 1 : MultiThread_MT(@_mask()) : EndIf
  FreeArray(tr())
  FreeMemory(*gray)
  FreeMemory(*blur)
  FreeMemory(*sobel)
  FreeMemory(*tmp)
  If *tempo2 : FreeMemory(*tempo2) : EndIf
EndProcedure

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

;--
Macro FakeHDR_thread_total()
  Protected lg =  *param\lg
  Protected ht =  *param\ht
  Protected total = lg * ht
  Protected start = (*param\thread_pos * total) / *param\thread_max
  Protected stop   = ((*param\thread_pos + 1) * total) / *param\thread_max
  If stop >= total : stop = total - 1 : EndIf
EndMacro
;---
Procedure FakeHDR_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.l, rx1.l, gx1.l, bx1.l
  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 *cible = *param\addr[3]
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected *tempo = *param\addr[0]
  Protected lx = *param\addr[1]
  Protected ly = *param\addr[2]

  ; Paramètres du filtre
  Protected nrx = param\option[17] ; Largeur de la fenêtre de flou (X)
  Protected nry = param\option[18] ; Hauteur de la fenêtre de flou (Y)
  Protected div = param\option[19] ; Facteur de division (65536 / (nrx * nry))

  ; Threads
  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)
  Protected Dim r.l(lg)
  Protected Dim g.l(lg)
  Protected 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 = *cible + ((p1 * lg) << 2)
    For i = 0 To lg - 1
      getargb(*srcPixel1\l, a1, r1, g1, b1)
      a(i) = a(i) + a1
      r(i) = r(i) + r1
      g(i) = g(i) + g1
      b(i) = 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) ; index de la ligne ajoutée
    p2 = PeekL(ly + (j << 2))       ; index de la ligne retirée
    *srcPixel1 = *cible + (p1 * lg) << 2
    *srcPixel2 = *cible + (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) = a(i) + a1 - a2
      r(i) = r(i) + r1 - r2
      g(i) = g(i) + g1 - g2
      b(i) = b(i) + b1 - b2
      *srcPixel1 + 4
      *srcPixel2 + 4
    Next

    ; Application du filtre horizontal (initialisation des accumulateurs)
    ax1 = 0 : rx1 = 0 : gx1 = 0 : bx1 = 0
    For i = 0 To nrx - 1
      p1 = PeekL(lx + i << 2)
      ax1 = ax1 + a(p1)
      rx1 = rx1 + r(p1)
      gx1 = gx1 + g(p1)
      bx1 = bx1 + b(p1)
    Next

    ; Boucle de sortie pour chaque pixel de la ligne (fenêtre glissante)
    For i = 0 To lg - 1
      p1 = PeekL(lx + (nrx + i) << 2)
      p2 = PeekL(lx + i  << 2)
      ax1 = ax1 + a(p1) - a(p2)
      rx1 = rx1 + r(p1) - r(p2)
      gx1 = gx1 + g(p1) - g(p2)
      bx1 = bx1 + b(p1) - b(p2)

      ; Calcul final avec facteur de division
      a1 = (ax1 * div) >> 16
      r1 = (rx1 * div) >> 16
      g1 = (gx1 * div) >> 16
      b1 = (bx1 * div) >> 16

      ; Clamp pour sécurité
      clamp_argb(a1 , r1 , g1 , b1)
      ; Écriture dans le buffer temporaire
      *dstPixel = *tempo + ((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 FakeHDR_sp_MT(*param.parametre)
  Protected *src = *param\source
  Protected *dst = *param\addr[0]
  Protected *bright =  *param\addr[1]
  Protected vmin.f =  *param\option[0] * 0.05
  Protected vmax.f =  *param\option[1] * 0.05
  Protected seuil1 = *param\option[2]
  Protected shadowBoos = *param\option[3]
  Protected seuil2 = *param\option[4]
  Protected i , pixel , lum
  Protected r0.f, g0.f, b0.f
  Protected r_under.f, g_under.f, b_under.f
  Protected r_over.f, g_over.f, b_over.f
  Protected r, g, b
  
  ;Protected Dim tab(255)
  ;For i = 0 To 255
    ;tab(i) = Pow(i / 255 , 2.2) * 255
  ;Next
  
  FakeHDR_thread_total()
  
    For i = start To stop 
      pixel = PeekL(*src + i << 2)
      getrgb(pixel ,r ,g , b)
      ;r = tab(r)
      ;g = tab(g)
      ;b = tab(b)
      r0 = r : g0 = g : b0 = b
      ; Sous-exposition
      r_under = r0 * vmin
      g_under = g0 * vmin
      b_under = b0 * vmin
      ; Sur-exposition
      r_over = r0 * vmax
      g_over = g0 * vmax
      b_over = b0 * vmax
      If r_over > 255 : r_over = 255 : EndIf
      If g_over > 255 : g_over = 255 : EndIf
      If b_over > 255 : b_over = 255 : EndIf
      ; Fusion pondérée
      r = r_under * 0.3 + r0 * 0.4 + r_over * 0.3
      g = g_under * 0.3 + g0 * 0.4 + g_over * 0.3
      b = b_under * 0.3 + b0 * 0.4 + b_over * 0.3
      ; Clamp
      If r > 255 : r = 255 : EndIf
      If g > 255 : g = 255 : EndIf
      If b > 255 : b = 255 : EndIf
      
      ; FakeHDR_ShadowBoost_MT
      lum = ((r * 77 + g * 150 + b * 29) >> 8)
      If lum < seuil1
        r = (r + ((seuil1 - lum) * shadowBoos))
        g = (g + ((seuil1 - lum) * shadowBoos))
        b = (b + ((seuil1 - lum) * shadowBoos))
      EndIf
      clamp_rgb(r ,g , b)
      PokeL(*dst + i << 2, (r<<16) | (g<<8) | b)
      
      ;Procedure FakeHDR_GlowEffect_IIR_sp1_MT
      lum = (r * 77 + g * 150 + b * 29) >> 8
      If lum > seuil2 : PokeL(*bright + i << 2, pixel) : Else : PokeL(*bright + i << 2, 0) : EndIf
      
    Next
    ;FreeArray(tab())
  EndProcedure
  ;--
  

Macro FakeHDR_Blur_IIR_sp()
  pos = (y * lg + x) << 2
  *pix32 = *dst32 + pos
  getrgb(*pix32\l ,r1 , g1 , b1)
  r1 = r1 << 8 : g1 = g1 << 8 : b1 = b1 << 8 
  r = (r * alpha + inv_alpha * r1) >> 8 
  g = (g * alpha + inv_alpha * g1) >> 8 
  b = (b * alpha + inv_alpha * b1) >> 8 
  r2 = (r + 128 ) >> 8 : g2 = (g + 128 ) >> 8 : b2 = (b + 128 ) >> 8
  clamp_rgb(r2 ,g2 ,b2)
  *pix32\l = (r2 << 16) + (g2 << 8) + b2
EndMacro

Procedure FakeHDR_Blur_IIR_y_MT(*param.parametre)
  Protected *dst32.pixel32 = *param\addr[0]
  Protected *pix32.pixel32
  Protected lg =  *param\lg
  Protected ht =  *param\ht
  Protected alpha = *param\option[18]
  Protected inv_alpha = *param\option[19]
  Protected x, y, pos
  Protected r, g, b
  Protected r1, g1, b1
  Protected r2, g2, b2
  Protected pixel
  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
    r = 0 : g = 0 : b = 0
    For x = 0 To lg - 1 : FakeHDR_Blur_IIR_sp() : Next
  Next
  For y = start To stop -1
    r = 0 : g = 0 : b = 0
    For x = lg - 1 To 0 Step -1 : FakeHDR_Blur_IIR_sp() : Next
  Next
EndProcedure

Procedure FakeHDR_Blur_IIR_x_MT(*param.parametre)
  Protected *dst32.pixel32 =  *param\addr[0]
  Protected *pix32.pixel32
  Protected lg =  *param\lg
  Protected ht =  *param\ht
  Protected alpha = *param\option[18]
  Protected inv_alpha = *param\option[19]
  Protected x, y, pos
  Protected r, g, b
  Protected r1, g1, b1
  Protected r2, g2, b2
  Protected pixel
  Protected start = (*param\thread_pos * lg) / *param\thread_max
  Protected stop   = ((*param\thread_pos + 1) * lg) / *param\thread_max
  For x = start To stop -1
    r = 0 : g = 0 : b = 0
    For y = 0 To ht - 1 : FakeHDR_Blur_IIR_sp() : Next
  Next
  For x = start To stop -1
    r = 0 : g = 0 : b = 0
    For y = ht - 1 To 0 Step -1 : FakeHDR_Blur_IIR_sp() : Next
  Next
EndProcedure

;--
Procedure FakeHDR_GlowEffect_IIR_sp2_MT(*param.parametre)
  Protected *src = *param\addr[0]
  Protected *bright = *param\addr[1]
  Protected *dst = *param\addr[2]
  Protected glowStrength = (*param\option[5] * 256) / 100
  Protected i , pixel, r, g, b
  Protected r0, g0, b0
  FakeHDR_thread_total()
  For i = start To stop
    pixel = PeekL(*src + i << 2)
    getrgb(pixel , r0 , g0 , b0)
    pixel = PeekL(*bright + i << 2)
    getrgb(pixel , r , g , b)
    ; Mélange glow + original avec intensité
    r = r0 + ((r * glowStrength) >> 8)
    g = g0 + ((g * glowStrength) >> 8)
    b = b0 + ((b * glowStrength) >> 8)
    clamp_rgb(r, g, b)
    PokeL(*dst + i << 2, (r << 16) + (g << 8) + b)
  Next
EndProcedure
;--
Procedure UnsharpMask_MT(*param.parametre)
  Protected *src  = *param\addr[0]
  Protected *dst  = *param\addr[1]
  Protected *blur = *param\addr[2]
  Protected strengthQ8 = Int(*param\option[6] * 25.6) ; 0.0–10.0 → 0–2560 (Q8)

  Protected i, pixelOrig, pixelBlur
  Protected rOrig, gOrig, bOrig, rBlur, gBlur, bBlur
  Protected rDiff, gDiff, bDiff, r, g, b

  FakeHDR_thread_total()  

  For i = start To stop
      pixelOrig = PeekL(*src + i << 2)
      pixelBlur = PeekL(*blur + i << 2)
      getrgb(pixelOrig, rOrig, gOrig, bOrig)
      getrgb(pixelBlur, rBlur, gBlur, bBlur)
      rDiff = rOrig - rBlur
      gDiff = gOrig - gBlur
      bDiff = bOrig - bBlur
      r = rOrig + ((rDiff * strengthQ8) >> 8)
      g = gOrig + ((gDiff * strengthQ8) >> 8)
      b = bOrig + ((bDiff * strengthQ8) >> 8)
      clamp_rgb(r, g, b)
      PokeL(*dst + i <<2 , (r << 16) + (g << 8) + b)
  Next
EndProcedure
;--
Procedure LocalContrast_MT(*param.parametre)
  Protected *src1 =  *param\addr[0]
  Protected *dst =  *param\cible

  ; Conversion de contrast en Q8 (x256)
  Protected contrastQ8 = Int(*param\option[8] * 10)
  Protected factorQ8 = Int(*param\option[9] * 10) ; Q8
  Protected levels = 100 - *param\option[10]
  If levels < 2 : levels = 2 : EndIf
  If contrastQ8 < 26 : contrastQ8 = 26 : EndIf ; équivaut à 0.1
  
  ; On calcule en Q8 fixed point les échelles pour quantification et déquantification
  ; scaleQuant = (levels - 1) << 8 / 255  -> pour r * scaleQuant >> 8 = quantification en [0..levels-1]
  Protected scaleQuant = ((levels - 1) << 8) / 255
  ; scaleDequant = 255 << 8 / (levels - 1) -> pour restituer la valeur dans [0..255]
  Protected scaleDequant = (255 << 8) / (levels - 1)
  
  Protected half = 128 ; pour arrondi (0.5 en Q8)
  
  Protected i , lum
  Protected r1, g1, b1, r2, g2, b2
  Protected r, g, b , rF, gF, bF
  FakeHDR_thread_total() 

  For i = start To stop
      getrgb(PeekL(*src1 + i << 2), r1, g1, b1)
      getrgb(PeekL(*dst + i << 2), r2, g2, b2)

      r = ((r1 - r2) * contrastQ8) >> 8 + r2
      g = ((g1 - g2) * contrastQ8) >> 8 + g2
      b = ((b1 - b2) * contrastQ8) >> 8 + b2

      clamp_rgb(r, g, b)
      ;PokeL(*dst + i << 2, (r << 16) + (g << 8) + b)
      
      ;procedure FakeHDR_sat_MT
     lum = (r * 77 + g * 150 + b * 29) >> 8

    ; Saturation ajustée avec Q8 fixed point
    rF = lum + ((r - lum) * factorQ8) >> 8
    gF = lum + ((g - lum) * factorQ8) >> 8
    bF = lum + ((b - lum) * factorQ8) >> 8

    clamp_rgb(rF, gF, bF)
    ;PokeL(*dst + i << 2, (rF << 16) + (gF << 8) + bF)     
    
    ;procedure PosterizeDoucement_MT
    r = (((rf * scaleQuant + half) >> 8) * scaleDequant + half) >> 8
    g = (((gf * scaleQuant + half) >> 8) * scaleDequant + half) >> 8
    b = (((bf * scaleQuant + half) >> 8) * scaleDequant + half) >> 8
    clamp_rgb(r, g, b)
    PokeL(*dst + i << 2, (r << 16) | (g << 8) | b)
  Next
EndProcedure
;--

;--
Procedure FakeHDR_MixWithOriginal_MT(*param.parametre)
  Protected *src1 = *param\source
  Protected *src2 = *param\cible
  
  ; mix en pourcentage [0..100], on convertit en Q8 [0..256]
  Protected mixPercent = *param\option[11]
  If mixPercent < 0 : mixPercent = 0 : EndIf
  If mixPercent > 100 : mixPercent = 100 : EndIf
  Protected mix = (mixPercent * 256) / 100
  Protected invMix = 256 - mix
  Protected half = 128 ; pour arrondi
  
  Protected i, pixel1, pixel2
  Protected r1, g1, b1, r2, g2, b2
  Protected r, g, b
  
  ;Protected Dim tab(255)
  ;For i = 0 To 255
    ;tab(i) = Pow(i/255  ,1 /  2.2) * 255
  ;Next
  
  FakeHDR_thread_total()

  For i = start To stop
    pixel1 = PeekL(*src1 + i << 2)
    pixel2 = PeekL(*src2 + i << 2)
    getrgb(pixel1, r1, g1, b1)
    getrgb(pixel2, r2, g2, b2)
    ;r2 = tab(r2)
    ;g2 = tab(g2)
    ;b2 = tab(b2)

    ; Interpolation en Q8 avec arrondi
    r = (r1 * invMix + r2 * mix + half) >> 8
    g = (g1 * invMix + g2 * mix + half) >> 8
    b = (b1 * invMix + b2 * mix + half) >> 8

    clamp_rgb(r, g, b)
    PokeL(*src2 + i << 2, (r << 16) + (g << 8) + b)
  Next
  ;FreeArray(tab())
EndProcedure
;--
Macro FakeHDR_sp1()
  dx = lg - 1
  dy = ht - 1
  If radius > dx : radius = dx : EndIf
  If radius > dy : radius = dy : EndIf
  nrx = radius + 1
  nry = radius + 1
  ; Allocation mémoire pour les tables d’indices en X et Y
  *lx = AllocateMemory((lg + 2 * nrx) * 4)
  *ly = AllocateMemory((ht + 2 * nry) * 4)
  ; Remplissage des tables selon le mode bord ou boucle
  For i = 0 To dx + 2 * nrx : ii = i - 1 - nrx / 2 : If ii < 0 : ii = 0 : ElseIf ii > dx : ii = dx : EndIf : PokeL(*lx + 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(*ly + i * 4, ii) : Next
  param\addr[1] = *lx
  param\addr[2] = *ly
  param\option[17] = nrx
  param\option[18] = nry
  param\option[19] = Int(65536 / (nrx * nry)) ; Facteur normalisation
EndMacro
;--
Procedure FakeHDR(*param.parametre)
  
  If param\info_active
    param\typ = #Filter_Type_autre
    param\name = "FakeHDR"
    param\remarque = ""
    param\info[0] = "vmin"
    param\info[1] = "vmax"
    param\info[2] = "ShadowBoost_seuil"
    param\info[3] = "ShadowBoost_value"
    param\info[4] = "seuil"
    param\info[5] = "Intensité Glow"
    param\info[6] = "strength"
    param\info[7] = "radius"
    param\info[8] = "contrast"
    param\info[9] = "factor"
    param\info[10] = "Posterize"
    param\info[11] = "Mix final"
    param\info[12] = "Masque binaire"
    
    param\info_data(0,0) = 0 : param\info_data(0,1) = 100 : param\info_data(0,2) = 30
    param\info_data(1,0) = 0 : param\info_data(1,1) = 100 : param\info_data(1,2) = 40
    param\info_data(2,0) = 0 : param\info_data(2,1) = 100 : param\info_data(2,2) = 7
    param\info_data(3,0) = 0 : param\info_data(3,1) = 100 : param\info_data(3,2) = 4
    param\info_data(4,0) = 0 : param\info_data(4,1) = 255 : param\info_data(4,2) = 127
    param\info_data(5,0) = 0 : param\info_data(5,1) = 100 : param\info_data(5,2) = 6
    param\info_data(6,0) = 0 : param\info_data(6,1) = 100 : param\info_data(6,2) = 50
    param\info_data(7,0) = 0 : param\info_data(7,1) = 100 : param\info_data(7,2) = 100
    param\info_data(8,0) = 0 : param\info_data(8,1) = 100 : param\info_data(8,2) = 30
    param\info_data(9,0) = 0 : param\info_data(9, 1) = 100 : param\info_data(9, 2) = 60
    param\info_data(10,0) = 0 : param\info_data(10,1) = 100 : param\info_data(10,2) = 0
    param\info_data(11,0) = 0 : param\info_data(11,1) = 100 : param\info_data(11,2) = 100 ; Mix final
    param\info_data(12,0) = 0 : param\info_data(12,1) = 2 : param\info_data(12,2) = 0     ; Masque binaire
    
    ProcedureReturn
  EndIf
  
  Protected *source = *param\source
  Protected *cible = *param\cible
  Protected *mask = *param\mask
  Protected lg = *param\lg
  Protected ht = *param\ht
  Protected i
  If *source = 0 Or *cible = 0 : ProcedureReturn : EndIf
  
  Protected *temp1 = AllocateMemory(lg * ht * 4)
  Protected *temp2 = AllocateMemory(lg * ht * 4)
  Protected *bright = AllocateMemory(lg * ht * 4)
  Protected *blur = AllocateMemory(lg * ht * 4)
  Protected *tempo = AllocateMemory(lg * ht * 4)
  
  ; Détermine le nombre de threads disponibles
  Protected thread = CountCPUs(#PB_System_CPUs)
  clamp(thread , 1 , 128)
  Protected Dim tr(thread)
  
  Protected ii, e, passe , t

  ; Étape 1 : Fake HDR
  ;FakeHDR_sp(*source, *temp1, lg, ht ,  vmin , vmax)
  *param\addr[0] = *temp1
  *param\addr[1] = *bright
  MultiThread_MT(@FakeHDR_sp_MT())
  

  Protected Radius0.f = 0.3
  *param\option[18] = Int((Exp(-2.3 / (Radius0 + 1.0))) * 256)
  *param\option[19]  = 256 - *param\option[18]
  *param\addr[0] = *bright
  MultiThread_MT(@FakeHDR_Blur_IIR_y_MT())
  MultiThread_MT(@FakeHDR_Blur_IIR_x_MT())
  
  *param\addr[0] = *temp1
  *param\addr[1] = *bright
  *param\addr[2] = *temp2
  MultiThread_MT(@FakeHDR_GlowEffect_IIR_sp2_MT())

  

  ; Étape 3 : Sharpen
  Protected dx , dy , nrx ,nry
  Protected *lx , *ly
  Protected radius.f = *param\option[7] 
  clamp(radius, 1, 100)
  radius * 0.1
  FakeHDR_sp1()
  param\addr[0] = *blur
  param\addr[3] = *temp2
  MultiThread_MT(@FakeHDR_Guillossien_MT())
  FreeMemory(*lx) : FreeMemory(*ly)
  
  param\addr[0] = *temp2
  param\addr[1] = *temp1
  param\addr[2] = *blur  
  MultiThread_MT(@UnsharpMask_MT())

  
  ; Étape 4 : Local contrast
  radius = 3
  FakeHDR_sp1()
  param\addr[0] = *temp2
  param\addr[3] = *temp1
  MultiThread_MT(@FakeHDR_Guillossien_MT())
  FreeMemory(*lx) : FreeMemory(*ly)
  
  param\addr[0] = *temp1
  param\addr[1] = *temp2
  MultiThread_MT(@LocalContrast_Mt())


  MultiThread_MT(@FakeHDR_MixWithOriginal_MT())

  If *param\mask And *param\option[12] : *param\mask_type = *param\option[12] - 1 : MultiThread_MT(@_mask()) : EndIf
  
  FreeMemory(*temp1)
  FreeMemory(*temp2)
  FreeMemory(*bright)
  FreeMemory(*blur)
  FreeMemory(*tempo)
  FreeArray(tr())
EndProcedure
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

Jacobus , pour la version 32bits , ce n'est pas gagné , il doit me reste 2 vieux P4 , ca fait longtemps que je suis en 64bits

SPH , oui tu as raison , je vais essayer "github"

merci falsam.

pour la suite , si j'arrive a utiliser "github" , je ne devrais pas trop modifier les programmes de ce poste.

il y a encore beaucoup de travail , rendre la demo plus intuitive , reorganiser les filtres plus logiquement , corriger quel bug , ajouter de nouveaux filtres ..

donc , je ne devrais pas modifier ce poste avant quelque temps
Avatar de l’utilisateur
falsam
Messages : 7334
Inscription : dim. 22/août/2010 15:24
Localisation : IDF (Yvelines)
Contact :

Re: filtre graphique

Message par falsam »

:arrow: Mise à jour de l'ensemble des codes sur GitHub
https://github.com/pbcodex/Graphic-Filt ... s/main.zip

et lancer le code demo.pb 😉

:arrow: Voir les différents sources sur GitHub
https://github.com/pbcodex/Graphic-Filters

- Ajout des filtres fx.pbi et autre.pbi
- Mise à jour de filtres.pbi
Configuration : Windows 11 Famille 64-bit - PB 6.20 x64 - AMD Ryzen 7 - 16 GO RAM
Vidéo NVIDIA GeForce GTX 1650 Ti - Résolution 1920x1080 - Mise à l'échelle 125%
Avatar de l’utilisateur
Guillot
Messages : 709
Inscription : jeu. 25/juin/2015 16:18

Re: filtre graphique

Message par Guillot »

pour tester en 32 bit t'as juste besoin d'installer PB en version 32bit
pas besoin d'un OS ou d'un vieux PC
manababel
Messages : 160
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

merci pour vos aides .

voici le programme "demo" corrigé pour la version 32bits et 64bits

c'est juste une erreur de copier-coller entre "CallFunctionFast" et "CallCFunctionFast"

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
      CallFunctionFast(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 : CallFunctionFast(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
        CallFunctionFast(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 : CallFunctionFast(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
Répondre