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 ... |