машинний код x86-64 (і x86-32), 13 15 13 байт
журнал змін:
Виправлення: перша версія перевіряла лише G = 0xff, не вимагаючи, щоб R і B були 0. Я змінив змінити фон на місці, щоб я міг lodsd
на передньому плані мати fg пікселі eax
для cmp eax, imm32
кодування короткої форми (5 байт ), а не cmp dh,0xff
(3 байти).
Збережіть 2 байти: помітили, що зміна bg на місці дозволяється використовувати операнд пам'яті для cmov
, збереження 2-байтового mov
навантаження (та збереження регістра, якщо це має значення).
Це функція, що відповідає конвенції про виклик системи x86-64 System V, яку можна дзвонити безпосередньо з C або C ++ (у x86-64, не в системах Windows) з цим підписом:
void chromakey_blend_RGB32(uint32_t *background /*rdi*/,
const uint32_t *foreground /*rsi*/,
int dummy, size_t pixel_count /*rcx*/);
Формат зображення - RGB0 32bpp, із зеленим компонентом на 2-й нижній адресі пам'яті в межах кожного пікселя. На передньому плані фонове зображення змінюється на місці. pixel_count
це рядки * стовпці. Це не хвилює рядків / стовпців; просто chromekey поєднує в собі скільки завгодно вказаних вами слов пам'яті.
RGBA (з A, який повинен бути 0xFF) вимагає використання іншої постійної, але не змінювати розмір функції. DWORD-файли переднього плану порівнюються для точної рівності проти довільної 32-бітової константи, що зберігається в 4 байтах, тому будь-який колір пікселів або кольоровий ключ можна легко підтримувати.
Цей же машинний код також працює в 32-бітному режимі. Щоб зібрати як 32-бітний, перейдіть rdi
до edi
джерела. Усі інші регістри, які стають 64-бітними, є неявними (lodsd / stosd і loop), а інші явні регістри залишаються 32-бітовими. Але зауважте, що вам потрібна обгортка для виклику з 32-бітного С, оскільки жодна зі стандартних конвенцій викликів x86-32 не використовує ті ж регістри, що і x86-64 SysV.
Перелік NASM (машинний код + джерело), коментується для початківців asm з описом того, що роблять більш складні інструкції. (Дублювання посібника з інструкціями - це поганий стиль за звичайного використання.)
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
Щоб вивести оригінальне джерело NASM з цього списку, зніміть 26 провідних символів кожного рядка <chromakey.lst cut -b 26- > chromakey.asm
. Я створив це за допомогою
nasm -felf64 chromakey-blend.asm -l /dev/stdout | cut -b -28,$((28+12))-
списків NASM, залишаю порожні стовпці, ніж я хочу, між машинним кодом та джерелом. Для створення об’єктного файлу, який ви можете зв’язати з C або C ++, використовуйте nasm -felf64 chromakey.asm
. (Або yasm -felf64 chromakey.asm
).
неперевірений , але я досить впевнений, що основна ідея load / load / cmov / store є здоровою, адже це так просто.
Я міг би зберегти 3 байти, якби міг вимагати від абонента передати константу хромального ключа (0x00ff00) як додатковий аргумент, замість жорсткого кодування постійної функції. Я не думаю, що звичайні правила дозволяють записати більш загальну функцію, в якій абонент встановлює для неї константи. Але якщо це було так, 3-й аргумент (наразі dummy
) передається в edx
x86-64 SysV ABI. Просто змініть cmp eax, 0x0000ff00
(5B) на cmp eax, edx
(2B).
За допомогою SSE4 або AVX ви можете зробити це швидше (але більший розмір коду) за допомогою pcmpeqd
та blendvps
зробити 32-бітний розмір елементів із змінною сумішшю, керованою маскою порівняння. (З pand
, ви можете ігнорувати високий байт). Для упакованого RGB24 ви можете використовувати, pcmpeqb
а потім 2x pshufb
+, pand
щоб отримати TRUE в байтах, де відповідають всі 3 компоненти цього пікселя pblendvb
.
(Я знаю, що це код-гольф, але я подумав про те, щоб спробувати MMX, перш ніж йти зі скалярним цілим числом.)