code machine x86-64 (et x86-32), 13 15 13 octets
journal des modifications:
Bugfix: la première version vérifiait seulement G = 0xff, ne nécessitant pas que R et B soient 0. J'ai changé pour modifier l'arrière-plan en place afin que je puisse utiliser lodsd
au premier plan pour avoir des pixels fg eax
pour l' cmp eax, imm32
encodage court (5 octets) ), au lieu de cmp dh,0xff
(3 octets).
Enregistrer 2 octets: a remarqué que la modification du bg en place permettait d'utiliser un opérande de mémoire pour cmov
, l'enregistrement d'une mov
charge de 2 octets (et l'enregistrement d'un registre, au cas où cela importait).
Il s'agit d'une fonction suivant la convention d'appel System V x86-64, appelable directement à partir de C ou C ++ (sur les systèmes x86-64 non Windows) avec cette signature:
void chromakey_blend_RGB32(uint32_t *background /*rdi*/,
const uint32_t *foreground /*rsi*/,
int dummy, size_t pixel_count /*rcx*/);
Le format d'image est RGB0 32bpp, avec le composant vert à la 2ème adresse mémoire la plus basse dans chaque pixel. L' image d'arrière- plan de premier plan est modifiée sur place. pixel_count
est lignes * colonnes. Il ne se soucie pas des lignes / colonnes; il suffit que chromekey mélange les nombreux mots de mémoire que vous spécifiez.
RGBA (avec A requis pour être 0xFF) nécessiterait l'utilisation d'une constante différente, mais aucun changement dans la taille de la fonction. Les DWORD de premier plan sont comparés pour une égalité exacte avec une constante arbitraire de 32 bits stockée sur 4 octets, de sorte que toute couleur d'ordre des pixels ou de chrominance peut être facilement prise en charge.
Le même code machine fonctionne également en mode 32 bits. Pour assembler en 32 bits, passez rdi
à edi
dans la source. Tous les autres registres qui deviennent 64 bits sont implicites (lodsd / stosd et boucle), et les autres regs explicites restent 32 bits. Mais notez que vous aurez besoin d'un wrapper pour appeler à partir de C 32 bits, car aucune des conventions d'appel x86-32 standard n'utilise les mêmes paramètres que x86-64 SysV.
Liste NASM (code machine + source), commentée pour les débutants asm avec des descriptions de ce que font les instructions les plus complexes. (La duplication du manuel de référence des instructions est un mauvais style en utilisation normale.)
1 ;; inputs:
2 ;; Background image pointed to by RDI, RGB0 format (32bpp)
3 ;; Foreground image pointed to by RSI, RGBA or RGBx (32bpp)
4 machine ;; Pixel count in RCX
5 code global chromakey_blend_RGB32
6 bytes chromakey_blend_RGB32:
7 address .loop: ;do {
8 00000000 AD lodsd ; eax=[rsi], esi+=4. load fg++
9 00000001 3D00FF0000 cmp eax, 0x0000ff00 ; check for chromakey
10 00000006 0F4407 cmove eax, [rdi] ; eax = (fg==key) ? bg : fg
11 00000009 AB stosd ; [rdi]=eax, edi+=4. store into bg++
12 0000000A E2F4 loop .loop ;} while(--rcx)
13
14 0000000C C3 ret
## next byte starts at 0x0D, function length is 0xD = 13 bytes
Pour obtenir la source NASM d'origine de cette liste, supprimez les 26 premiers caractères de chaque ligne avec <chromakey.lst cut -b 26- > chromakey.asm
. J'ai généré cela avec des
nasm -felf64 chromakey-blend.asm -l /dev/stdout | cut -b -28,$((28+12))-
listes NASM laissant plus de colonnes vides que je ne le souhaite entre le code machine et la source. Pour créer un fichier objet que vous pouvez lier avec C ou C ++, utilisez nasm -felf64 chromakey.asm
. (Ou yasm -felf64 chromakey.asm
).
non testé , mais je suis assez confiant que l'idée de base de load / load / cmov / store est bonne, car c'est si simple.
Je pourrais économiser 3 octets si je pouvais exiger que l'appelant passe la constante de chrominance (0x00ff00) comme argument supplémentaire, au lieu de coder en dur la constante dans la fonction. Je ne pense pas que les règles habituelles permettent d'écrire une fonction plus générique pour laquelle l'appelant a configuré des constantes. Mais si c'est le cas, le 3ème argument (actuellement dummy
) est passé edx
dans l'ABI SysV x86-64. Changez simplement cmp eax, 0x0000ff00
(5B) en cmp eax, edx
(2B).
Avec SSE4 ou AVX, vous pouvez le faire plus rapidement (mais avec une taille de code plus grande) avec pcmpeqd
et blendvps
pour effectuer un mélange variable de taille d'élément 32 bits contrôlé par le masque de comparaison. (Avec pand
, vous pouvez ignorer l'octet de poids fort). Pour RGB24 compressé, vous pouvez utiliser pcmpeqb
puis 2x pshufb
+ pand
pour obtenir VRAI en octets où les 3 composants de ce pixel correspondent, alors pblendvb
.
(Je sais que c'est du code-golf, mais j'ai envisagé d'essayer MMX avant de choisir un entier scalaire.)