Машинный код 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-битный формат в формате с прямым порядком байтов, но альфа-канал (байт высшего порядка) игнорируется. Это упрощает многие вещи, позволяя нам просто перебирать каждый пиксель и проверять его 32-битное значение цвета RGB. Умная оптимизация также используется здесь. Вместо того, чтобы выделять каждый компонент цвета и проверять, равен ли он> = 192, мы просто маскируем все 32-битное значение на 0xC0C0C0 и проверяем, равен ли результат> = 0xC0C0C0. Это будет иметь значение true для всех «облачных» цветов и false для всех «небесных» (не облачных) цветов. Ну, я думал , что это было умно! :-) Это, безусловно, экономит большое количество байтов.
Поэтому, чтобы протестировать этот код, вам нужно будет преобразовать входные изображения в растровые изображения 32 бит / бит. Вы не можете использовать Windows Paint для этого, потому что он поддерживает максимум 24 бит на пиксель. Тем не менее, есть ряд других программных решений, которые могут сделать это, например, Adobe Photoshop. Я использовал этот бесплатный инструмент , который конвертирует PNG в 32-битный BMP в Windows, что означает, что вам нужно конвертировать только из JPEG в PNG (что может сделать Paint).
Другие предположения, которые я высказываю, в высшей степени обоснованы:
- Предполагается, что битовая карта имеет размер больше 0 ( то есть предполагается, что она содержит по меньшей мере один пиксель). Это разумно, потому что, когда у них нулевое небо, у нас больше проблем, чем в метеорологии.
- Предполагается, что флаг направления (
DF
) ясен, поэтому мы будем корректно выполнять итерацию по растровому изображению, используя LODSD
инструкцию. Это то же предположение, которое принято большинством соглашений о вызовах x86, поэтому оно выглядит справедливым. Если вам это не нравится, добавьте 1 байт в счетчик для CLD
инструкции.
- Предполагается, что режим округления для FPU x87 установлен на округление до ближайшего четного. Это гарантирует, что мы получим правильное поведение, когда преобразуем количество окт из временного числа с плавающей запятой в конечный целочисленный результат, что подтверждается контрольным примером № 4. Это предположение разумно, потому что это состояние по умолчанию для FPU, и его необходимо поддерживать даже в коде C (где усечение является поведением округления по умолчанию, заставляя компиляторы, которые хотят быть совместимыми со стандартами, генерировать неэффективный код, который изменяет округление режим, выполняет преобразование, а затем изменяет режим округления обратно).
Неуправляемая сборка мнемоники:
; 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]