Odbicie lustrzane Y

Autor: Wojciech Muła
Dodany:6.02.2002
Aktualizacja:3.03.2002

Obrazy grayscale

Najprościej jest po prostu zamieniać bajty (bądź słowa):

; edi - wskazuje na początek linii (pierwszy piksel)

; obrazy 8-bitowe            ; obrazy 15/16-bitowe

; esi wskazuje na koniec linii
lea esi, [edi + img_width-1] ; lea esi, [edi + img_width-2]
mov ecx, img_width/2         ; mov ecx, img_width/2

flipx:
        mov     al, [edi]    ; mov    ax, [edi]
        xchg    al, [esi]    ; xchg   ax, [esi]
        mov  [edi], al       ; mov [edi], ax

        ; kolejny piksel
        inc edi              ; add edi, byte 2

        ; wcześniejszy piksel
        dec esi              ; sub esi, byte 2
        loop flipx

Podstawową wadą programu jest liczba odwołań do pamięci, równa 3*img_width/2. Poniżej zoptymalizowany program dla obrazów grayscale.

; edi - pierwszy piksel
; esi - ostatni piksel

mov ecx, img_width/8        ; tylko img_width/8 iteracji

flipx:
        mov eax, [edi]
        mov ebx, [esi]

        bswap eax           ; instrukcja bswap zamienia kolejność bajtów -
        bswap ebx           ; jest wręcz idealna dla tego zastosowania

        mov [esi], eax
        mov [edi], ebx

        add edi, 4
        sub esi, 4

        loop flipx

W tym przypadku liczba pobrań jest równa img_width/2, tj. 3 razy mniej niż w poprzednim programie. Jedyną, myślę niezbyt dokuczliwą wadą, jest wymogów by szerokość obrazu (img_width) była całkowitą wielokrotnością czwórki.

Użycie rozszerzonych rozkazów MMX może znacznie przyspieszyć kod --- przedstawiam kod zamieniający kolejność bajtów w rejestrze MMX.

movq  mm0, [edi]      ; mm0 = |a|b|c|d|e|f|g|h|
movq  mm1, mm0        ; mm1 = |a|b|c|d|e|f|g|h|

psrlw mm0, 8          ; mm0 = |0a|0c|0e|0g|
psllw mm1, 8          ; mm1 = |b0|d0|f0|h0|

por   mm0, mm1        ; mm0 = |ba|dc|fe|hg| -- zamiana bajtów w słowach

pshufw mm0, mm0, 0x1b ; 0x1b= |00|01|10|11|

                      ; mm0 = |hg|fe|dc|ba|

Obrazy 15/16bpp

Przy przetwarzaniu obrazów 15/16 bpp można pobierać po 2 piksele do rejestru 32-bitowego, po czym dokonać obrotu bitowego (w lewo, bądź prawo) o 16 pozycji.

mov eax, [edi+0] ; eax = |piksel1|piksel0|
mov ebx, [edi+4] ; ebx = |piksel3|piksel2|

rol eax, 16      ; eax = |piksel0|piksel1|
rol ebx, 16      ; ebx = |piksel2|piksel3|

mov [esi-4], ebx
mov [esi-8], eax

Użycie rozkazu pshufw umożliwi blisko dwukrotne przyspieszenie.

movq mm0, [edi+0] ; załaduj 4 piksele
movq mm1, [edi+8] ; i kolejne 4

pshufw mm0, mm0, 0x1b ; 0x1b = |00|01|10|11|
pshufw mm1, mm1, 0x1b ;

movq mm0, [esi-8]
movq mm0, [esi-16]