filtre graphique

Programmation d'applications complexes
manababel
Messages : 163
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 : 163
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 : 163
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 : 7411
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.23 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 : 742
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 : 163
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
manababel
Messages : 163
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

bonjour

Voici le lien GitHub pour les filtres.
https://github.com/manababel/filtre-grapique

J'ai privilégié la quantité. Moins d'une vingtaine de filtres ne font pas ce qu'ils devraient faire (ou le font mal) et quelques-uns font planter l'application (crash).
Utilisation de la version démo :
Il y a 3 types d'images :
L'image de travail (principale).
L'image secondaire, à utiliser avec les filtres "blend modes" (elle se mélange avec l'image de travail).
Le masque.
Vous pouvez appliquer plusieurs filtres les uns après les autres.

Note : Pour le moment, la démo ne permet pas de sauvegarder son travail.
Avatar de l’utilisateur
venom
Messages : 3212
Inscription : jeu. 29/juil./2004 16:33
Localisation : Klyntar
Contact :

Re: filtre graphique

Message par venom »

Salut,
Merci pour le lien github et les codes. Je compilerai ça a l'occasion :wink:







@++
Windows 10 x64, PureBasic 6.30 x86 & x64
GPU : radeon HD6370M, CPU : p6200 2.13Ghz
manababel
Messages : 163
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

bonjour

Voici une mise à jour du programme et des fonctions de filtrage
Il existe désormais deux noms de fonctions pour chaque filtre :
filtreEx(*pointer)
Filtre(source, cible, masque, option ...)

Utilisation :
Le programme "demo" utilise les fonctions FiltreEx.
Les autres programmes de demo utilisent la seconde fonction (Filtre).

Modifications de l'interface dans le programme "demo"
L'interface a été enrichie de plusieurs nouveautés :
Filtres : Ajout de trois filtres de convolution supplémentaires.
Sauvegarde : L'option de sauvegarde a été intégrée, mais attention : seule l'image 'source' peut être sauvegardée.

Procédure pour sauvegarder l'image :
Cliquez sur "Appliquer -> Source".
Allez dans le menu "File".
Sélectionnez "Save".
manababel
Messages : 163
Inscription : jeu. 14/mai/2020 7:40

Re: filtre graphique

Message par manababel »

Petite mise à jour, principalement sur la démo :

Ajout de filtres "Resize" (redimensionnement x2 uniquement pour le moment) : Ces filtres sont séparés des autres filtres . Sélectionnez l'image que vous souhaitez agrandir, puis appliquez le filtre.

Image de travail (aperçu) : Pour modifier l'image de travail, sélectionnez-la. pour le moment, aucun visuel n'indique qu'elle est sélectionnée, mis à part la désélection des images miniatures. Appliquez ensuite le filtre.
Avec l'image de travail (l'aperçu), il est difficile de voir la différence entre l'image originale et l'image zoomée. Pour vous assurer que l'image a bien été redimensionnée, rendez-vous dans l'option "info_image".

Options : Ajout de la fonction "Favoris" et modification de l'option "Save" (Enregistrer).
Répondre