| Autor: | Wojciech Muła |
|---|---|
| Dodany: | 2002 |
| Aktualizacja: | 30.04.2008 |
Najprostszy sposób to pominąć co drugi pixel (i co drugą linię):
|a|b|c|d|e|f|... -> |a|c|e|...
Można również przeprowadzić skalowanie z filtrowaniem bilinearnym:
|a|b|c|d|e|f|... -> |(a+b)/2|(c+d)/2|(e+f)/2|...
Obraz grayscale:
mov eax, [edi] ; eax = |d|c|b|a| -- pobierz 4 pixele mov ah, al ; eax = |d|c|a|a| shr eax, 8 ; eax = |0|d|c|a| mov [esi], ax ; -- zapisz 2 pixele
Powyższy kod ma jedną dość istotną wadę: przeskalowanie 8 pixeli to 2 zapisy do pamięci — po prostu musi zostać dwukrotnie wykonany; kolejny program omija tę wadę.
mov eax, [edi] ; eax = |d|c|b|a| mov ebx, [edi+4] ; ebx = |h|g|f|e| mov ah, al ; eax = |d|c|a|a| mov bh, bl ; ebx = |h|g|e|e| shl eax, 8 ; eax = |c|a|a|0| shr ebx, 8 ; ebx = |0|h|g|e| shrd eax, ebx, 8 ; eax = |g|e|c|a| mov [esi], eax
Użycie MMX bardzo ułatwia sprawę.
pcmpeqb mm2, mm2 ; generowanie maski 0x00ff00ff00ff00ff
psrlw mm2, 8 ;
mov ecx, img_width/8
_scalehalf:
movq mm0, [edi] ; mm0 = |h|g|f|e|d|c|b|a|
movq mm1, [edi+8] ; mm1 = |p|o|n|m|l|k|j|i|
pand mm0, mm2 ; mm0 = |-|g|-|e|-|c|-|a|
pand mm1, mm2 ; mm1 = |-|o|-|m|-|k|-|i|
packuswb mm0, mm1 ; mm0 = |o|m|k|i|h|e|c|a|
movq [esi], mm0
add edi, 8
add esi, 8
loop _scalehalf
Proszę zapoznać się z Średnia arytmetyczna.
mov eax, [edi] ; eax = |d|c|b|a| add al, ah ; rcr al, 1 ; eax = | d | c | b |(a+b)/2| bswap eax ; eax = |(a+b)/2| b | c | d | add al, ah ; rcr al, 1 ; eax = |(a+b)/2| b | c |(d+c)/2| rcl eax, 8 ; eax = | b | c |(d+c)/2|(a+b)/2| *** mov [esi], ax
Uwaga taka sama jak poprzednio: warto przeskalować 8 pixeli, by zapisać równocześnie 4.
mov eax, [edi] mov eax, [edi+4] ; średnia w ax ; średnia w bx -- w miejscu oznaczonym (***) zamiast ; rozkazu rcl, należy użyć RCR. and eax, 0x0000ffff ; and ebx, 0xffff000 ; zamaskowanie słów zawierających średnie or eax, ebx ; połączenie wyników mov [esi], eax
Tu również MMX ułatwi działanie.
; mm4 = 0x00ff00ff00ff00ff
; [edi] = |i|h|g|f|e|d|c|b|a|
movq mm0, [edi] ; mm0 = |h|g|f|e|d|c|b|a|
movq mm1, [edi+1] ; mm1 = |i|h|g|f|e|d|c|b|
movq mm2, [edi+8] ; skalowane będzie 16 pixeli równocześnie
movq mm3, [edi+9] ;
pand mm0, mm4 ; mm0 = |-|g|-|e|-|c|-|a|
pand mm1, mm4 ; mm1 = |-|h|-|f|-|d|-|b|
pand mm2, mm4
pand mm3, mm4
paddw mm0, mm1 ; mm0 = |g+h |e+f |c+d |a+b |
psrlw mm0, 1 ; mm0 = | gh | ef | cd | ab | -- 'ab' oznacza '(a+b)/2'
paddw mm2, mm2
psrlw mm2, 1 ; mm2 = | op | mn | kl | ij |
packuswb mm0, mm2 ; mm0 = |op|mn|kl|ij|gh|ef|cd|ab|
movq [esi], mm0
Z filtrowanie bilinearnym:
movdqa (%eax), %xmm0 ; xmm0 = |a b c d e f g h|...|
movdqa 16(%eax), %xmm2 ; xmm2 = |i j k l m n o p|...|
movdqa %xmm0, %xmm1
movdqa %xmm2, %xmm3
pand MASK, %xmm0 ; xmm0 = |a _ c _ e _ g _|...| _ = 0
pand MASK, %xmm2 ; xmm2 = |i _ k _ m _ o _|...|
; MASK = packed_word(0x00ff)
psrlw $8, %xmm1 ; xmm1 = |b _ d _ f _ h _|...|
psrlw $8, %xmm3 ; xmm3 = |j _ l _ n _ p _|...|
packuswb %xmm1, %xmm0 ; xmm0 = |b d f h j l n p|...|
packuswb %xmm3, %xmm2 ; xmm2 = |a c e g i k m o|...|
pavgb %xmm2, %xmm0 ; xmm0 = |(a+b+1)/2 (d+c+1)/2 (f+e+1)/2 (h+g+1)/2 ... |