SetBits / GetBits gut genug?

Für allgemeine Fragen zur Programmierung mit PureBasic.
Benutzeravatar
RSBasic
Admin
Beiträge: 8022
Registriert: 05.10.2006 18:55
Wohnort: Gernsbach
Kontaktdaten:

Re: SetBits / GetBits gut genug?

Beitrag von RSBasic »

:allright:
Aus privaten Gründen habe ich leider nicht mehr so viel Zeit wie früher. Bitte habt Verständnis dafür.
Bild
Bild
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: SetBits / GetBits gut genug?

Beitrag von Mijikai »

Die FPU macht mich noch wahnsinning :freak:

Ich hänge noch bei der Konvertierung der Dither Funktion :(
Ware für jede Hilfe Dankbar.

Hier der Assembly Code:

Code: Alles auswählen

format MS64 COFF
public nifDitherRGB
section '.text' code readable executable

nifDitherRGB:;*Pixel(Pointer zur RGB Struktur),*Exit(Pointer zum Lezten Pixel),*Error(Pointer zur Error Variable),Factor(Float)
        ;------------------------------------------ SETUP STACK FRAME
        push rbp
        mov rbp,rsp
        ;------------------------------------------ ALLOCATE SPACE FOR LOCAL VARIABLES
        sub rsp,48h
        ;------------------------------------------ SET DEFAULT RETURN
        xor rax,rax
        ;------------------------------------------ POINTER VERIFICATION
        cmp rcx,rdx
        jnb @f
        ;------------------------------------------ LOAD FACTOR
        mov[rbp-8h],r9
        ;------------------------------------------ LOAD PIXEL (RGB)
        movzx rdx,byte[rcx]
        mov[rbp-28h],rdx
        movzx rdx,byte[rcx+1h]
        mov[rbp-30h],rdx
        movzx rdx,byte[rcx+2h]
        mov[rbp-38h],rdx
        ;------------------------------------------ CALCULATE NEW RED
        fild dword[rbp-28h]                         ;Rot
        fimul dword[rbp-8h]                         ;Factor
        fistp dword[rbp-28h]                        ;Ergebnis
        mov rax,[rbp-28h]                           ;Fehler!!! -> erhalte nicht den Wert von (Rot * Faktor)?!
        ;------------------------------------------ RETURN LABEL
        @@:
        ;------------------------------------------ DEALLOCATE SPACE
        add rsp,48h
        ;------------------------------------------ RESTORE OLD STACK
        mov rsp,rbp
        pop rbp
        ret  
Der PureBasic Code:

Code: Alles auswählen

EnableExplicit

;PB v.6.52 x64 (Windows)

Import "nifDither.obj"
  nifDitherRGB(*Pixel,*Exit,*Error,Factor.f)
EndImport

Structure RGB_STRUCT
  RGB.a[3]
EndStructure

Global Pixel.RGB_STRUCT
Global Error.i
Global Factor.f

;Testwerte setzen:

Pixel\RGB[0] = 10
Pixel\RGB[1] = 20
Pixel\RGB[2] = 30

Factor = 2

Debug nifDitherRGB(@Pixel,@Pixel + 1,@Error,Factor)
Das Ergebnis der Rechnung is nicht korrekt!?
-> Pixel\RGB[0] * Factor

:coderselixir:
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: SetBits / GetBits gut genug?

Beitrag von Helle »

Wenn Factor als Float deklariert wird hat r9 damit nichts zu tun. Als 4.Parameter wird Factor dann in xmm3 an die Procedure übergeben (Windows 64-Bit).
Andererseits sind doch die Parameter schon als Global deklariert, also warum die Übergabe-Klimmzüge (und der ganze Klimbim drumrum)?
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: SetBits / GetBits gut genug?

Beitrag von Mijikai »

Helle hat geschrieben:Wenn Factor als Float deklariert wird hat r9 damit nichts zu tun. Als 4.Parameter wird Factor dann in xmm3 an die Procedure übergeben (Windows 64-Bit).
Andererseits sind doch die Parameter schon als Global deklariert, also warum die Übergabe-Klimmzüge (und der ganze Klimbim drumrum)?
:)

Es war mir nicht bewusst das der Parameter als xmm3 übergeben wird.
Könnte der Parameter auch anders übergeben werden?
Die Parameter sind nur im Beispiel Global (später wird die Assembler Funktion für jedes Pixel mehrfach aufgerufen).

Ich hatte mich and den Assembler Code von diesem Macro gehalten:

Code: Alles auswählen

Macro MM(A,B)
  (A * B)
EndMacro

MM(Pixel\RGB[0],1.33)
Assembler Ausgabe:

Code: Alles auswählen

lea rbp,[imagebase+5554]
push rbp
pop rbp
movzx r15,byte ptr [rbp+00]
mov [rsp-08],r15
fild qword ptr [rsp-08]
fmul qword ptr [imagebase+502C]
fistp qword ptr [rsp-08]
mov rax,[rsp-08]
ret
SSE ist wahrscheinlich besser - da muss ich mich noch einlesen.

Wäre so etwas möglich?

Code: Alles auswählen

xmm0[Red,Green]
xmm1[Blue,0]
xmm3[Factor,Factor]
xmm0 * xmm3
xmm1 * xmm3
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: SetBits / GetBits gut genug?

Beitrag von Helle »

Hier ein zartes Prinzip-Beispiel (nur SSEx, kein AVX):

Code: Alles auswählen

Buffer1 = AllocateMemory(16) ;Werte-Buffer
Buffer2 = AllocateMemory(16) ;Zwischenspeicher

PokeB(Buffer1, 10)           ;Beispiel-Werte
PokeB(Buffer1 + 1, 20)
PokeB(Buffer1 + 2, 30)

Factor.f = 0.4

!mov r8,[v_Buffer1]          ;Register hier beliebig gewählt
!mov r9,[v_Buffer2]
!lea r10,[v_Factor]

!pshufd xmm2,[r10],0         ;Factor vervielfachen, bleibt unverändert

!pmovzxbd xmm0,[r8]          ;4 Integer-Bytes zu 4 Integer-DWords erweitern (SSE4_1)
!cvtdq2ps xmm1,xmm0          ;4 Integer-DWords in 4 Floats konvertieren (SSE2)
!mulps xmm1,xmm2             ;4 Float-Multiplikationen
!cvtps2dq xmm0,xmm1          ;4 Floats in 4 integer-DWords konvertieren 
!movups [r9],xmm0            ;4 integer-DWords in Buffer2 speichern


Debug PeekB(Buffer2)         ;diese Einzel-Werte (als Bytes) können auch wieder in Buffer1 zurückgeschrieben werden; Buffer2 dadurch wieder verfügbar
Debug PeekB(Buffer2 + 4)
Debug PeekB(Buffer2 + 8)
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: SetBits / GetBits gut genug?

Beitrag von Helle »

Hier ohne Zwischenspeicher und mit Alignment-Beachtung von Factor (kann zur Sicherheit auch mit Buffer1 gemacht werden):

Code: Alles auswählen

Factor.f = 0.4
Buffer1 = AllocateMemory(16) ;Werte-Buffer

PokeB(Buffer1, 10)           ;Beispiel-Werte
PokeB(Buffer1 + 1, 20)
PokeB(Buffer1 + 2, 30)

!mov r8,[v_Buffer1]          ;Register hier beliebig gewählt
!lea r10,[v_Factor]

!movups xmm0,[r10]           ;über Register wegen Alignment!
!pshufd xmm2,xmm0,0          ;Factor vervielfachen, bleibt unverändert

!pmovzxbd xmm0,[r8]          ;4 Integer-Bytes zu 4 Integer-DWords erweitern (SSE4_1)
!cvtdq2ps xmm1,xmm0          ;4 Integer-DWords in 4 Floats konvertieren (SSE2)
!mulps xmm1,xmm2             ;4 Float-Multiplikationen
!cvtps2dq xmm0,xmm1          ;4 Floats in 4 integer-DWords konvertieren 

!pextrb byte[r8],xmm0,0b
!pextrb byte[r8+1],xmm0,100b
!pextrb byte[r8+2],xmm0,1000b

Debug PeekB(Buffer1)
Debug PeekB(Buffer1 + 1)
Debug PeekB(Buffer1 + 2)
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: SetBits / GetBits gut genug?

Beitrag von Mijikai »

:shock:
Vielen Dank für das genialen Beispiele :D
das es so tolle Instruktionen gibt :praise:
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: SetBits / GetBits gut genug?

Beitrag von Mijikai »

Ich muss den ersten Farbwert erst in ein Register übertragen da ich keine 4 Bytes auf einmal lesen darf.

Farbwert einlesen (*Pixel):

Code: Alles auswählen

movzx rax,byte[rcx+2h]
movzx rbx,word[rcx]
shl rax,10h
or rax,rbx
Wie bekomme ich nun an die Addresse von rax um diese and die SSE Instruktionen zu reichen?

Habe so versucht:

Code: Alles auswählen

mov [rsp + 8h],rax
pmovzxbd xmm0,[rsp + 8h]
movups [rdx],xmm0;<- überträgt nur das erste Byte!     
Benutzeravatar
Helle
Beiträge: 566
Registriert: 11.11.2004 16:13
Wohnort: Magdeburg

Re: SetBits / GetBits gut genug?

Beitrag von Helle »

Hmmm, wieso soll das nicht funzen?
Test:

Code: Alles auswählen

Buffer1 = AllocateMemory(16) ;Werte-Buffer für Test
!mov rdx,[v_Buffer1]
!mov rax,102030h             ;Testwert
!mov [rsp + 8h],rax          ;rax auf Stack
!pmovzxbd xmm0,[rsp + 8h]    ;4 Bytes vom Stack erweitern auf 4 DWords in xmm0
!movdqu [rdx],xmm0           ;die DWords rein in Buffer1, hier mal als Integers, movups tuts aber auch  
;Test:
Debug Hex(PeekL(Buffer1))    ;DWord auslesen, Hex wegen optischen Vergleich
Debug Hex(PeekL(Buffer1 + 4));hier beachten, das DWords ausgelesen werden! Little Endian!
Debug Hex(PeekL(Buffer1 + 8))
Den Stack zu benutzen tue ich ohne ersichtlichen Vorteil selten, da ist schnell mal eine Gurke drin. Ich würde es so machen:

Code: Alles auswählen

Buffer1 = AllocateMemory(16) ;Werte-Buffer
!mov rdx,[v_Buffer1]
!mov rax,405060h             ;Testwert
!movq xmm1,rax               ;rax als Quadword nach xmm1, "movd xmm1,eax" tuts genauso 
!pmovzxbd xmm0,xmm1          ;4 (Low-)Bytes von rax in xmm1 erweitern auf 4 DWords in xmm0
!movdqu [rdx],xmm0           ;die DWords rein in Buffer1, hier mal als Integers, movups tuts aber auch
;Test:
Debug Hex(PeekL(Buffer1))    ;DWord auslesen, Hex wegen optischen Vergleich
Debug Hex(PeekL(Buffer1 + 4));hier beachten, das DWords ausgelesen werden! Little Endian!
Debug Hex(PeekL(Buffer1 + 8))
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: SetBits / GetBits gut genug?

Beitrag von Mijikai »

Danke @Helle für die Assembler Hilfe :)

Nach längerem basteln will ich nun den aktuellen OBJ - Code und ein PureBasic Beispiel zeigen.
Das Dithering (Atkinson) funktioniert ganz gut allerdings belieben links und ganz unten im Bild unter Umständen Artefakte!
Die Ursache hierfür liegt noch im Dunkeln :(

Über weiter Ratschläge (vor allem zum Assembler Code) würde ich mich freuen <)

OBJ:

Code: Alles auswählen

format MS64 COFF
;.......................................
public nifEncodeColor
public nifDither
;.......................................
section '.text' code readable executable
;.......................................

nifEncodeColor:;*Pixel,*Error
     push rbp
     mov rbp,rsp
     sub rsp,8h
     ;------------------- GET PIXEL COLOR
     movzx rax,byte[rcx+2h]
     movzx r8,word[rcx]
     shl rax,10h
     or rax,r8
     ;------------------- STORE PIXEL COLOR ADDRESS
     mov [rsp+8h],rax
     ;------------------- RESET ORIGINAL PIXEL COLOR
     mov byte[rcx],0h
     mov word[rcx+1h],0h
     ;------------------- CALCULATE & SET NEW PIXEL COLOR
     cmp byte[rsp+8h],7Fh
     ja @f
     mov byte[rcx],0FFh
     @@:
     cmp byte[rsp+9h],7Fh
     ja @f
     mov byte[rcx+1h],0FFh
     @@:
     cmp byte[rsp+0Ah],7Fh
     ja @f
     mov byte[rcx+2h],0FFh
     @@:
     ;------------------- CALCULATE ERROR
     mov al,byte[rsp+8h]
     sub al,byte[rcx]
     mov byte[rdx],al
     mov al,byte[rsp+9h]
     sub al,byte[rcx+1h]
     mov byte[rdx+1h],al
     mov al,byte[rsp+0Ah]
     sub al,byte[rcx+2h]
     mov byte[rdx+2h],al
     add rsp,8h
     mov rsp,rbp
     pop rbp
     ret

nifDither:;*Pixel,*Exit,*Error,*Factor - Dank Helle :)
     cmp rcx,rdx
     ja @f
     push rbp
     mov rbp,rsp
     sub rsp,8h
     movzx rax,byte[rcx+2h]
     movzx rdx,word[rcx]
     shl rax,10h
     or rax,rdx
     mov [rsp+8h],rax
     pmovzxbd xmm0,[r8]
     cvtdq2ps xmm1,xmm0
     pmovzxbd xmm0,[rsp+8h]
     cvtdq2ps xmm3,xmm0
     movups xmm0,[r9]
     pshufd xmm2,xmm0,0h
     mulps xmm1,xmm2
     addps xmm1,xmm3
     cvtps2dq xmm0,xmm1
     pextrb byte[rcx],xmm0,0b
     pextrb byte[rcx+1h],xmm0,100b
     pextrb byte[rcx+2h],xmm0,1000b
     add rsp,8h
     mov rsp,rbp
     pop rbp
     @@:
     ret  
Beispiel:

Code: Alles auswählen

;PureBasic v.6.52 x64

EnableExplicit

UseJPEGImageDecoder();Unterstützung für JPEG und PNG!
UseJPEG2000ImageDecoder()
UsePNGImageDecoder()

Import "nifDither.obj"
  nifEncodeColor.i(*Pixel,*Error)
  nifDither.i(*Pixel,*Exit,*Error,*Factor)
EndImport

Structure BITMAP_STRUCT
  bmType.l
  bmWidth.l
  bmHeight.l
  bmWidthBytes.l
  bmPlanes.w
  bmBitsPixel.w
  Alignment.b[4]
  *bmBits
  PixelSize.i
  Bytes.i
  Exit.i
EndStructure

Structure IMAGE_STRUCT
  Id.i
  Handle.i
  Bitmap.BITMAP_STRUCT
EndStructure

Procedure.i nifFree(*nif.IMAGE_STRUCT)
  With *nif
    If IsImage(\Id)
      FreeImage(\Id)
    EndIf
    FreeStructure(*nif)
  EndWith
EndProcedure

Procedure.i nifBitmap(*nif.IMAGE_STRUCT)
  With *nif
    If \Handle
      If GetObject_(\Handle,SizeOf(BITMAP),@\Bitmap)
        \Bitmap\PixelSize = \Bitmap\bmBitsPixel / 8
        \Bitmap\Bytes = \Bitmap\bmWidthBytes * \Bitmap\bmHeight
        \Bitmap\Exit = \Bitmap\bmBits + \Bitmap\Bytes - \Bitmap\PixelSize
        ProcedureReturn #True
      EndIf
    EndIf 
  EndWith
EndProcedure

Procedure.i nifEncode(*nif.IMAGE_STRUCT)
  Protected ImageX.i
  Protected ImageY.i
  Protected *Scanline
  Protected *Pixel
  Protected Index.i
  Protected Factor1.f
  Protected Factor2.f
  With *nif
    If \Bitmap\bmBitsPixel = 24
      Factor1 = 0.375
      Factor2 = 0.125
      For ImageY = 0 To \Bitmap\bmHeight - 1
        *Scanline = \Bitmap\bmBits +(ImageY * \Bitmap\bmWidthBytes)
        For ImageX = 0 To \Bitmap\bmWidth - 1
          *Pixel = *Scanline + (ImageX * \Bitmap\PixelSize)
          nifEncodeColor(*Pixel,@Error)
          nifDither(*Pixel + \Bitmap\PixelSize,\Bitmap\Exit,@Error,@Factor1);Atkinson
          nifDither(*Pixel + \Bitmap\bmWidthBytes + \Bitmap\PixelSize,\Bitmap\Exit,@Error,@Factor2)
          nifDither(*Pixel + \Bitmap\PixelSize + \Bitmap\PixelSize,\Bitmap\Exit,@Error,@Factor2)
          nifDither(*Pixel + \Bitmap\bmWidthBytes,\Bitmap\Exit,@Error,@Factor2)
          nifDither(*Pixel + \Bitmap\bmWidthBytes - \Bitmap\PixelSize,\Bitmap\Exit,@Error,@Factor2)
          nifDither(*Pixel + \Bitmap\bmWidthBytes + \Bitmap\bmWidthBytes,\Bitmap\Exit,@Error,@Factor2)
        Next
      Next
      ProcedureReturn #True
    Else
      ;Bild mit Alpha!
    EndIf
  EndWith
EndProcedure

Procedure.i nifHandle(*nif.IMAGE_STRUCT)
  With *nif
    ProcedureReturn \Handle
  EndWith
EndProcedure

Procedure.i nifLoadImage(File.s)
  Protected *nif.IMAGE_STRUCT
  Protected Timer.q
  *nif = AllocateStructure(IMAGE_STRUCT)
  If *nif
    With *nif
      \Id = LoadImage(#PB_Any,File)
      If \Id
        \Handle = ImageID(\Id)
        If nifBitmap(*nif)
          Timer = ElapsedMilliseconds()
          If nifEncode(*nif)
            Timer = ElapsedMilliseconds() - Timer
            MessageRequester("","Timer: " + Str(Timer))
            ProcedureReturn *nif
          EndIf
        EndIf
      EndIf
      nifFree(*nif)
    EndWith
  EndIf 
EndProcedure

Procedure.i nifSaveBitmap(*nif.IMAGE_STRUCT,File.s)
  With *nif
    ProcedureReturn SaveImage(\Id,File,#PB_ImagePlugin_BMP,#Null,\Bitmap\bmBitsPixel)
  EndWith
EndProcedure

Global Image.i
Global File.s

File = "XYZ";<- ÄNDERN!!!
Image = nifLoadImage(File)

If Image
  ;nifSaveBitmap(Image,"converted_" + File)
  If OpenWindow(0,0,0,800,580,"",#PB_Window_ScreenCentered|#PB_Window_SystemMenu)
    If StartVectorDrawing(WindowVectorOutput(0))
      MovePathCursor(0,0)
      DrawVectorImage(nifHandle(Image),$FF,800,580)
    EndIf
    Repeat
    Until WaitWindowEvent() = #PB_Event_CloseWindow
    CloseWindow(0)
  EndIf
  nifFree(Image)
EndIf

End
Antworten