машинний код 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) передається в edxx86-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, перш ніж йти зі скалярним цілим числом.)