Voici quelques filtres de "dithering".
Se type de filtre permet de diminuer le nombre de couleurs dans une image en créant des "trames de points"donnant l'impression d'avoir plus de couleur qu'il n'y en a.
Dans le programme, vous pouvez choisir le nombre de couleurs par canal (r, g, b).
Il se peut que le nombre de couleurs sélectionnées soit déférent de la réalité.
Il y a 12 filtres écrits en purebasic, le 13e n'est qu'une conversion du filtre "Fake-Floyd" en ASM.
Code : Tout sélectionner
EnableExplicit
Global Dim Reg_memory.q(4*344)
; charge une image et la convertie en 32bit
;-------------------------------------------------------------------
Procedure load_image(nom,file$)
Protected source_p , cible_p , s.q
Protected lg.q , ht.q , taille.q , depth.q
LoadImage(nom,file$)
If Not IsImage(nom) : ProcedureReturn 0 : EndIf
StartDrawing(ImageOutput(nom))
source_p = DrawingBuffer()
ht = ImageHeight(nom)
lg = ImageWidth(nom)
Depth=OutputDepth()
StopDrawing()
If Depth=24
taille=lg*ht
cible_p=AllocateMemory(taille*4)
Dim save.q(4)
s=@save()
EnableASM
!mov rax,[p.v_s]
!mov [rax],rcx
!mov [rax+8],rdx
!mov [rax+16],r8
!mov rcx,[p.v_source_p]
!mov rdx,[p.v_cible_p]
!mov r8,[p.v_taille]
!sub r8,1 ; <---------------- ????
!copy_boucle24:
!mov eax,[rcx]
!mov [rdx],eax
!add rcx,3
!add rdx,4
!dec r8
!jnz copy_boucle24
!mov rax,[p.v_s]
!mov rcx,[rax]
!mov rdx, [rax+8]
!mov r8,[rax+16]
DisableASM
FreeArray(save())
FreeImage(nom) ; supprime l'image 24bits
CreateImage(nom,lg,ht,32)
StartDrawing(ImageOutput(nom))
source_p = DrawingBuffer()
StopDrawing()
CopyMemory( cible_p , source_p , taille*4 )
FreeMemory(cible_p)
EndIf
ProcedureReturn 1
EndProcedure
; partie du programme à modifier pour adapter se programme au votre
; convertie les "IDs" des images en pointer d'adresse
; test si les images sont en 32bits et de la meme taille
;-------------------------------------------------------------------
Macro sp(source,cible)
Protected Depth.q , lg.q , ht.q , lg1.q , ht1.q , taille.q
Protected cible_p.q , source_p.q
StartDrawing(ImageOutput(cible))
cible_p = DrawingBuffer()
ht1 = ImageHeight(cible)
lg1 = ImageWidth(cible)
Depth=OutputDepth()
StopDrawing()
If depth<>32 : ProcedureReturn : EndIf
StartDrawing(ImageOutput(source))
source_p = DrawingBuffer()
ht = ImageHeight(source)
lg = ImageWidth(source)
Depth=OutputDepth()
StopDrawing()
If depth<>32 : ProcedureReturn : EndIf
If lg<>lg1 Or ht<>ht1 : ProcedureReturn : EndIf
taille = lg * ht
EndMacro
Macro clampRGB(r,g,b)
If r<0:r=0:EndIf
If g<0:g=0:EndIf
If b<0:b=0:EndIf
If r>255:r=255:EndIf
If g>255:g=255:EndIf
If b>255:b=255:EndIf
EndMacro
Macro returnRGB(pixel,r,g,b)
r=(pixel & $ff0000)>>16
g=(pixel & $ff00)>>8
b=(pixel & $ff)
EndMacro
;-------------------------------------------------------------------
; sauvegarde de registres
Macro Save_reg()
s=@reg_memory()
EnableASM
!mov rax,[p.v_s]
!mov [rax+000],rbx
!mov [rax+008],rcx ;
!mov [rax+016],rdx ;
!mov [rax+024],rsi
!mov [rax+032],rdi
!mov [rax+040],r8 ;
!mov [rax+048],r9 ;
!mov [rax+056],r10
!mov [rax+064],r11
!mov [rax+072],r12
!mov [rax+080],r13
!mov [rax+088],r14
!mov [rax+096],r15
!movdqu [rax+104],xmm0 ;
!movdqu [rax+120],xmm1 ;
!movdqu [rax+136],xmm2 ;
!movdqu [rax+152],xmm3 ;
!movdqu [rax+168],xmm4
!movdqu [rax+184],xmm5
!movdqu [rax+200],xmm6
!movdqu [rax+216],xmm7
!movdqu [rax+232],xmm8
!movdqu [rax+248],xmm9
!movdqu [rax+264],xmm10
!movdqu [rax+280],xmm11
!movdqu [rax+296],xmm12
!movdqu [rax+312],xmm13
!movdqu [rax+328],xmm14
!movdqu [rax+344],xmm15
DisableASM
EndMacro
Macro Rest_Reg()
; restore les registres
s=@reg_memory()
EnableASM
!mov rax,[p.v_s]
!mov rbx,[rax+000]
!mov rcx,[rax+008]
!mov rdx,[rax+016]
!mov rsi,[rax+024]
!mov rdi,[rax+032]
!mov r8,[rax+040]
!mov r9,[rax+048]
!mov r10,[rax+056]
!mov r11,[rax+064]
!mov r12,[rax+072]
!mov r13,[rax+080]
!mov r14,[rax+088]
!mov r15,[rax+096]
!movdqu xmm0,[rax+104] ;
!movdqu xmm1,[rax+120] ;
!movdqu xmm2,[rax+136] ;
!movdqu xmm3,[rax+152] ;
!movdqu xmm4,[rax+168]
!movdqu xmm5,[rax+184]
!movdqu xmm6,[rax+200]
!movdqu xmm7,[rax+216]
!movdqu xmm8,[rax+232]
!movdqu xmm9,[rax+248]
!movdqu xmm10,[rax+264]
!movdqu xmm11,[rax+280]
!movdqu xmm12,[rax+296]
!movdqu xmm13,[rax+312]
!movdqu xmm14,[rax+328]
!movdqu xmm15,[rax+344]
DisableASM
EndMacro
;-------------------------------------------------------------------
Macro Filter_Floyddither_Macro_read(pos)
var=PeekL(pos)
returnRGB(var,r,g,b)
nr = tab(r)
ng = tab(g)
nb = tab(b)
PokeL(pos,(nr<<16+ng<<8+nb))
err=(r-nr)
erg=(g-ng)
erb=(b-nb)
EndMacro
Macro Filter_Floyddither_Macro_write(npos,v1,v2)
var=PeekL(npos)
returnRGB(var,r,g,b)
r=(r+((err*v1)>>v2))
g=(g+((erg*v1)>>v2))
b=(b+((erb*v1)>>v2))
ClampRGB(r,g,b)
PokeL(npos,(r<<16+g<<8+b))
EndMacro
Procedure Filter_Floyddither(source,cible,opt1,opt2)
Define m,i,lg2,x,y,pos
Define var,r,g,b,nr,ng,nb,err,erg,erb
Define pix,taille2,s
sp(source,cible)
If opt2<1 : opt2=1: EndIf ; evite une division par 0
If opt2>16 : opt2=16:EndIf
m=256/opt2
opt2=opt2+1
Dim tab.q(256)
For i=0 To 255
tab(i) = ((i*opt2)>>8)*m
If tab(i)>255 : tab(i) = 255:EndIf
;if tab(i)>127 : tab(i)=255 : Else : tab(i)=0 : EndIf
Next
lg1 = lg * 4
lg2 = lg1 * 2
;copy(source_p,cible_p,long*4)
CopyMemory(source_p, cible_p, taille*4)
Select opt1
;-- method Floyd-Steinberg
Case 0
For y=0 To ht-2
For x=1 To lg-2
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,7,4)
Filter_Floyddither_Macro_write(pos+lg1-4,3,4)
Filter_Floyddither_Macro_write(pos+lg1,5,4)
Filter_Floyddither_Macro_write(pos+lg1+4,1,4)
Next
Next
;-- method Fake-Floyd
Case 1
For y=0 To ht-2
For x=1 To lg-2
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,3,3)
Filter_Floyddither_Macro_write(pos+lg1-4,3,3)
Filter_Floyddither_Macro_write(pos+lg1+0,2,3)
Next
Next
;-- method Sierra Lite
Case 2
For y=0 To ht-2
For x=1 To lg-2
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,2,2)
Filter_Floyddither_Macro_write(pos+lg1-4,1,2)
Filter_Floyddither_Macro_write(pos+lg1,1,2)
Next
Next
;-- method Atkinson
Case 3
For y=0 To ht-3
For x=1 To lg-3
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,1,3)
Filter_Floyddither_Macro_write(pos+8,1,3)
Filter_Floyddither_Macro_write(pos-4+lg1,1,3)
Filter_Floyddither_Macro_write(pos+lg1,1,3)
Filter_Floyddither_Macro_write(pos+4+lg1,1,3)
Filter_Floyddither_Macro_write(pos+lg2,1,3)
Next
Next
;-- method Two Sierra
Case 4
For y=0 To ht-3
For x=2 To lg-2
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,4,4)
Filter_Floyddither_Macro_write(pos+8,3,4)
Filter_Floyddither_Macro_write(pos-8+lg1,1,4)
Filter_Floyddither_Macro_write(pos-4+lg1,2,4)
Filter_Floyddither_Macro_write(pos+lg1,3,4)
Filter_Floyddither_Macro_write(pos+4+lg1,2,4)
Filter_Floyddither_Macro_write(pos+8+lg1,1,4)
Next
Next
;-- method Burves
Case 5
For y=0 To ht-3
For x=2 To lg-2
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,8,5)
Filter_Floyddither_Macro_write(pos+8,4,5)
Filter_Floyddither_Macro_write(pos-8+lg1,2,5)
Filter_Floyddither_Macro_write(pos-4+lg1,4,5)
Filter_Floyddither_Macro_write(pos+lg1,8,5)
Filter_Floyddither_Macro_write(pos+4+lg1,4,5)
Filter_Floyddither_Macro_write(pos+8+lg1,2,5)
Next
Next
;-- method Sierra
Case 6
For y=0 To ht-3
For x=2 To lg-3
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,5,5)
Filter_Floyddither_Macro_write(pos+8,3,5)
Filter_Floyddither_Macro_write(pos-8+lg1,2,5)
Filter_Floyddither_Macro_write(pos-4+lg1,4,5)
Filter_Floyddither_Macro_write(pos+lg1,8,5)
Filter_Floyddither_Macro_write(pos+4+lg1,4,5)
Filter_Floyddither_Macro_write(pos+8+lg1,2,5)
Filter_Floyddither_Macro_write(pos+lg2-4,2,5)
Filter_Floyddither_Macro_write(pos+lg2,3,5)
Filter_Floyddither_Macro_write(pos+lg2+4,2,5)
Next
Next
;-- method Jarvis ( approx )
Case 7
For y=0 To ht-3
For x=2 To lg-3
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,2387,14) ;7/48 ( 2387 = 7 x 341 )
Filter_Floyddither_Macro_write(pos+8,1705,14) ;5/48
Filter_Floyddither_Macro_write(pos-8+lg1,1023,14);3/48
Filter_Floyddither_Macro_write(pos-4+lg1,1705,14);5/48
Filter_Floyddither_Macro_write(pos+lg1,7*390,14);7/48
Filter_Floyddither_Macro_write(pos+4+lg1,1705,14);5/48
Filter_Floyddither_Macro_write(pos+8+lg1,1023,14);3/48
Filter_Floyddither_Macro_write(pos+lg2-8,341,14);1/48
Filter_Floyddither_Macro_write(pos+lg2-4,1023,14);3/48
Filter_Floyddither_Macro_write(pos+lg2,1705,14);5/48
Filter_Floyddither_Macro_write(pos+lg2+4,1023,14);3/48
Filter_Floyddither_Macro_write(pos+lg2+8,341,14);1/48
Next
Next
;-- method Stuki ( approx )
Case 8
For y=0 To ht-3
For x=2 To lg-3
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,3120,14) ;8/42 ( 3120 = 7 x 390 )
Filter_Floyddither_Macro_write(pos+8,1560,14) ;4/42
Filter_Floyddither_Macro_write(pos-8+lg1,780,14);2/42
Filter_Floyddither_Macro_write(pos-4+lg1,1560,14);4/42
Filter_Floyddither_Macro_write(pos+lg1,3120,14);8/42
Filter_Floyddither_Macro_write(pos+4+lg1,1560,14);4/42
Filter_Floyddither_Macro_write(pos+8+lg1,780,14);2/42
Filter_Floyddither_Macro_write(pos+lg2-8,390,14);1/42
Filter_Floyddither_Macro_write(pos+lg2-4,780,14);2/42
Filter_Floyddither_Macro_write(pos+lg2,1560,14);4/42
Filter_Floyddither_Macro_write(pos+lg2+4,780,14);2/42
Filter_Floyddither_Macro_write(pos+lg2+8,390,14);1/42
Next
Next
;-- method Shiau Fan V1
Case 9
For y=0 To ht-2
For x=3 To lg-2
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,8,4)
Filter_Floyddither_Macro_write(pos+lg1-12,1,4)
Filter_Floyddither_Macro_write(pos+lg1-8,1,4)
Filter_Floyddither_Macro_write(pos+lg1-4,2,4)
Filter_Floyddither_Macro_write(pos+lg1,4,4)
Next
Next
;-- method Shiau Fan V2
Case 10
For y=0 To ht-2
For x=2 To lg-2
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos+4,4,3)
Filter_Floyddither_Macro_write(pos+lg1-8,1,3)
Filter_Floyddither_Macro_write(pos+lg1-4,1,3)
Filter_Floyddither_Macro_write(pos+lg1,2,3)
Next
Next
;-- method omni directional ( approx )
Case 11
For y=1 To ht-2
For x=1 To lg-2
pos=cible_p+((lg*y)+x)*4
Filter_Floyddither_Macro_read(pos)
Filter_Floyddither_Macro_write(pos-lg1-4,1638,14);1/10
Filter_Floyddither_Macro_write(pos-lg1,3277,14);2/10
Filter_Floyddither_Macro_write(pos-lg1+4,1638,14);1/10
Filter_Floyddither_Macro_write(pos-4,1638,14);1/10
Filter_Floyddither_Macro_write(pos+4,1638,14);1/10
Filter_Floyddither_Macro_write(pos+lg1-4,1638,14);1/10
Filter_Floyddither_Macro_write(pos+lg1,3277,14);2/10
Filter_Floyddither_Macro_write(pos+lg1+4,1638,14);1/10
Next
Next
;-- ASM method Fake-Floyd
Case 12
pix=@tab()
taille2=(taille*4)-lg1
EnableASM
Save_Reg()
!mov rdi,[p.v_cible_p]
!mov r10,[p.v_taille2]
!mov rcx,[p.v_pix]
!xor r9,r9
!pxor xmm15,xmm15
!Filter_Floyddither_saut:
!movd xmm2,[rdi] ; var=PeekL(pos)
!xor rax,rax
!xor rbx,rbx
!mov bl,[rdi+2]
!mov al,[rcx+rbx*8] ; nr = tab(r)
!shl rax,8
!mov bl,[rdi+1]
!mov al,[rcx+rbx*8] ; ng = tab(g)
!shl rax,8
!mov bl,[rdi+0]
!mov al,[rcx+rbx*8] ; nb = tab(b)
!mov [rdi],eax ; PokeL(pos,(nr<<16+ng<<8+nb))
!movd xmm1,eax
!punpcklbw xmm2,xmm15 ; 8 -> 16 ; rgb
!punpcklbw xmm1,xmm15 ; 8 -> 16 ; new_rgb
!psubsw xmm2,xmm1 ; err=(r-nr) ; err_rgb
!movdqu xmm3,xmm2
!paddsw xmm3,xmm3
!paddsw xmm3,xmm2; err*3
!psraw xmm3,3 ; / 8
!movd xmm1,[rdi+4]
!punpcklbw xmm1,xmm15
!paddsw xmm1 , xmm3 ; r=r+(err*3)/8
!packuswb xmm1,xmm15
!movd [rdi+4],xmm1
!add rdi,[p.v_lg1]
!movd xmm1,[rdi-4]
!punpcklbw xmm1,xmm15
!paddsw xmm1 , xmm3 ; r=r+(err*3)/8
!packuswb xmm1,xmm15
!movd [rdi-4],xmm1
!movd xmm1,[rdi]
!punpcklbw xmm1,xmm15
!paddsw xmm2,xmm2 ; err*2
!psraw xmm2,3 ; / 8
!paddsw xmm1 , xmm2 ; r=r+(err*2)/8
!packuswb xmm1,xmm15
!movd [rdi],xmm1
!sub rdi,[p.v_lg1]
!add rdi,4
!add r9,4
!cmp r9,r10
!jb Filter_Floyddither_saut
DisableASM
Rest_Reg()
EndSelect
FreeArray(tab())
EndProcedure
;-------------------------------------------------------------------
;------------------------------------------------------------------
;-- programme
UseJPEGImageDecoder()
UsePNGImageDecoder()
Global imgx=1200
Global imgy=800
If OpenWindow(0, 0, 0, imgx, imgy, "Floyddither", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
Define source.q , cible.q , t.q , file$ , i , var1 , var2 , tx$
ComboBoxGadget(1, imgx/4, 1, imgx/4, 24)
Restore DATA_NOM
tx$=""
While tx$<>"***END***"
Read.s tx$
If tx$<>"***END***"
AddGadgetItem(1, -1, tx$)
EndIf
Wend
ComboBoxGadget(2, imgx/4+imgx/4, 1, imgx/4, 24)
For i=2 To 16
tx$=Str(i)
AddGadgetItem(2, -1, "Nombre de couleurs par canal "+tx$)
Next
file$ = OpenFileRequester("Image","","",0)
source=10
cible=20
If Load_Image(source,file$) = 0 ; <- commande differente de "LOADIMAGE"
MessageRequester("erreur", "image non chargeé" ,#PB_MessageRequester_Ok )
End
EndIf
ResizeImage(source,imgx,imgy,#PB_Image_Smooth)
CreateImage(cible,imgx,imgy,32) ; l'image doit entre en mode 32bits
Repeat
Select WaitWindowEvent()
Case #PB_Event_CloseWindow
End
Case #PB_Event_Gadget
Select EventGadget()
Case 1,2
var1=GetGadgetState(1)
var2=GetGadgetState(2)+1
t=ElapsedMilliseconds()
Filter_Floyddither(source,cible,var1,var2)
t=ElapsedMilliseconds()-t
EndSelect
EndSelect
StartDrawing(WindowOutput(0))
DrawImage(ImageID(cible),0,50)
DrawText(5,50,"temps : "+Str(t))
DrawText(5,5,Str(GetGadgetState(1))+" ")
StopDrawing()
ForEver
EndIf
;-------------------------------------------------------------------
DataSection
DATA_NOM:
Data.s "Floyd-Steinberg"
Data.s "Fake-Floyd"
Data.s "Sierra Lite"
Data.s "Atkinson"
Data.s "Two Sierra"
Data.s "Burves"
Data.s "Sierra"
Data.s "Jarvis ( approx )"
Data.s "Stuki ( approx )"
Data.s "Shiau Fan V1"
Data.s "Shiau Fan V2"
Data.s "omni directional ( approx )"
Data.s " ***ASM*** Fake-Floyd"
Data.s "***END***"
EndDataSection