машинний код x86, 34 байти
51
31 D2
AD
F7 D0
25 C0 C0 C0 00
75 01
42
E2 F3
C1 E2 03
DB 04 24
52
DB 04 24
DE F1
DB 1C 24
58
5A
C3
Ці байти коду визначають функцію, яка приймає растрове введення та повертає ціле значення із зазначенням його октатів. Як і в C , масиви (як растрові карти) представлені у вигляді вказівника на перший елемент та розміру / довжини. Таким чином, ця функція приймає два параметри: загальну кількість пікселів у растровій карті (рядки × стовпці) та вказівник на саму растрову карту.
Цей код використовує власну конвенцію виклику на основі реєстру, де покажчик растрових зображень передається в ESIрегістр, а розмір растрової карти передається в ECXрегістр. Результат (октати), як правило, повертається EAX.
Як вже було сказано вище, вхід приймається як растрова карта. Зокрема, використовується формат 32-bpp у форматі мало-ендіанського типу, але альфа-канал (байт найвищого порядку) ігнорується. Це спрощує багато речей, дозволяючи нам просто повторити кожен піксель і перевірити його 32-бітове значення RGB кольору. Тут також використовується розумна оптимізація. Замість того, щоб виділяти кожну кольорову складову та перевіряти, чи є вона> = 192, ми просто маскуємо все 32-бітове значення на 0xC0C0C0 і перевіряємо, чи результат>> 0xC0C0C0. Це оцінить як істинне для всіх "хмарних" кольорів, а неправдиве - для всіх "небесних" (нехмарних) кольорів. Ну, я думав, що це розумно! :-) Це звичайно економить велику кількість байтів.
Тому для перевірки цього коду вам потрібно буде перетворити вхідні зображення в 32-bpp растрові карти. Для цього не можна використовувати Windows Paint, оскільки він підтримує максимум 24 біти на піксель. Однак існує ряд інших програмних рішень, які можуть це зробити, наприклад Adobe Photoshop. Я використав цей безкоштовний інструмент , який перетворює PNG в BMP з 32-bpp в Windows, тобто означає, що вам потрібно конвертувати лише JPEG в PNG (що Paint може зробити).
Інші припущення, які я висловлюю, є надзвичайно обґрунтованими:
- Припускається, що бітова карта має розмір більше 0 ( тобто передбачається , що вона містить принаймні один піксель). Це розумно, тому що, коли небо є недійсним, у нас є більші проблеми, ніж метеорологія.
- Прапор напрямку (
DF) вважається чітким, тому ми будемо правильно повторювати растрову карту за допомогою LODSDінструкції. Це те саме припущення, яке роблять більшість конвенцій для викликів x86, тому здається справедливим. Якщо вам це не подобається, додайте 1 байт до рахунку для CLDінструкції.
- Для режиму округлення для FPU x87 передбачається встановити значення «кругле до найближчого». Це гарантує, що ми отримуємо правильну поведінку, коли перетворюємо кількість октатів з тимчасової з плаваючою комою до кінцевого цілого результату, як це підтверджено тестовим випадком №4. Це припущення є обґрунтованим, оскільки це стан за замовчуванням для FPU, і його потрібно підтримувати навіть у коді С (де усічення - це поведінка округлення за замовчуванням, змушуючи компілятори, які бажають відповідати стандартам, щоб генерувати неефективний код, що змінює округлення режим, робить перетворення, а потім повертає режим округлення назад).
Невикольована збірна мнемоніка:
; int ComputeOktas(void* bmpBits /* ESI */,
; uint32_t bmpSize /* ECX */);
push ecx ; save size on stack
xor edx, edx ; EDX = 0 (cloudy pixel counter)
CheckPixels:
lodsd ; EAX = DS:[ESI]; ESI += 4
not eax
and eax, 0x00C0C0C0
jnz NotCloudy
inc edx
NotCloudy:
loop CheckPixels ; ECX -= 1; loop if ECX > 0
shl edx, 3 ; counter *= 8
fild DWORD PTR [esp] ; load original size from stack
push edx
fild DWORD PTR [esp] ; load counter from stack
fdivrp st(1), st(0) ; ST(0) = counter*8 / size
fistp DWORD PTR [esp] ; convert to integer, rounding to nearest even
pop eax ; load result
pop edx
ret
Ви, звичайно, не зробили це так, і все ще цікавитесь, як працює код? :-)
Ну, це досить просто. Ми просто повторюємо одночасно 32-бітове значення бітової карти, перевіряючи, чи є значення RGB пікселя "хмарним" чи "не хмарним". Якщо хмарно, ми збільшуємо свій попередньо нульовий лічильник. Наприкінці ми обчислюємо: мутні пікселі ⁄ загальна пікселя × 8
(що еквівалентно: хмарним пікселям ⁄ загальним пікселям ÷ 0,125).
Я не можу включити для цього посилання TIO через необхідність введення зображень. Я можу, однак, надати вам джгут, який я використовував для тестування цього в Windows:
#include <stdio.h>
#include <assert.h>
#include <Windows.h>
int main()
{
// Load bitmap as a DIB section under Windows, ensuring device-neutrality
// and providing us direct access to its bits.
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,
TEXT("C:\\...\\test1.bmp"),
IMAGE_BITMAP,
0, 0,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
assert(hBitmap != NULL);
// Get the bitmap's bits and attributes.
DIBSECTION dib;
GetObject(hBitmap, sizeof(dib), &dib);
assert(dib.dsBm.bmBitsPixel == 32);
uint32_t cx = dib.dsBm.bmWidth;
uint32_t cy = abs(dib.dsBm.bmHeight);
uint32_t sz = cx * cy;
assert(sz > 0);
int oktas = ComputeOktas(sz, dib.dsBm.bmBits);
printf("%d\n", oktas);
return 0;
}
Будьте обережні з цим, хоча! Як визначено вище, ComputeOktasвикористовується користувацька конвенція про виклики, яку компілятор C не дотримуватиметься. Вам потрібно додати код у верхній частині процедури складання мови для завантаження значень із стека в очікувані регістри, наприклад :
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]