Kod maszynowy x86, 34 bajty
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
Te bajty kodu definiują funkcję, która pobiera dane bitmapy i zwraca wartość całkowitą wskazującą jej okty. Podobnie jak w C , tablice (podobnie jak mapy bitowe) są reprezentowane jako wskaźnik do pierwszego elementu oraz rozmiar / długość. Tak więc ta funkcja przyjmuje dwa parametry: całkowitą liczbę pikseli na mapie bitowej (wiersze x kolumny) i wskaźnik do samej mapy bitowej.
W tym kodzie zastosowano niestandardową konwencję wywoływania opartą na rejestrze, w której wskaźnik bitmapy jest przekazywany w pliku ESI
rejestru, a rozmiar mapy bitowej jest przekazywany do ECX
rejestru. Wynik (oktas) jest zwykle zwracany EAX
.
Jak już wspomniano powyżej, dane wejściowe są traktowane jako mapa bitowa. W szczególności używany jest format 32-bpp w formacie little-endian, ale kanał alfa (bajt najwyższego rzędu) jest ignorowany. Upraszcza to wiele rzeczy, pozwalając nam po prostu iterować każdy piksel i sprawdzać jego 32-bitową wartość koloru RGB. Zastosowano tu również sprytną optymalizację. Zamiast izolować każdy składnik koloru i sprawdzać, czy jest to> = 192, po prostu maskujemy całą 32-bitową wartość o 0xC0C0C0 i sprawdzamy, czy wynik to> = 0xC0C0C0. Spowoduje to ocenę wartości true dla wszystkich kolorów „chmurowych” i false dla wszystkich kolorów „niebiańskich” (niebędących chmurami). Dobrze, ja myślałem, że to mądry! :-) Z pewnością oszczędza dużą liczbę bajtów.
Dlatego, aby przetestować ten kod, musisz przekonwertować obrazy wejściowe na bitmapy o 32 bitach na sekundę. Nie można do tego użyć Windows Paint, ponieważ obsługuje on maksymalnie 24 bity na piksel. Istnieje jednak wiele innych rozwiązań programowych, takich jak Adobe Photoshop. Użyłem tego bezpłatnego narzędzia , które konwertuje PNG do BMP o 32 bitach na sekundę w systemie Windows, co oznacza, że potrzebujesz tylko konwersji z JPEG na PNG (co potrafi Paint).
Inne założenia, które moim zdaniem są wyjątkowo uzasadnione:
- Zakłada się, że bitmapa ma rozmiar większy niż 0 ( tj . Zakłada się, że zawiera co najmniej jeden piksel). Jest to uzasadnione, ponieważ gdy niebo jest puste, mamy większe problemy niż meteorologia.
DF
Zakłada się, że flag kierunku ( ) jest czysty, dzięki czemu będziemy poprawnie iterować przez mapę bitową za pomocą LODSD
instrukcji. Jest to to samo założenie przyjęte przez większość konwencji wywoływania x86, więc wydaje się uczciwe. Jeśli ci się nie podoba, dodaj 1 bajt do liczby dlaCLD
instrukcji.
- Zakłada się, że tryb zaokrąglania dla FPU x87 jest ustawiony na zaokrąglenie do najbliższego parzystego. To gwarantuje, że otrzymamy prawidłowe zachowanie, gdy przekonwertujemy liczbę okt z liczb zmiennoprzecinkowych tymczasowych na końcowy wynik w postaci liczb całkowitych, co weryfikuje przypadek testowy nr 4. To założenie jest uzasadnione, ponieważ jest to stan domyślny dla FPU i go zachować nawet w kodzie C (gdzie obcięcie jest domyślnym zachowaniem zaokrąglania, zmuszając kompilatory, które chcą być zgodne ze standardami, do generowania nieefektywnego kodu zmieniającego zaokrąglanie , wykonuje konwersję, a następnie zmienia z powrotem tryb zaokrąglania).
Mnemoniki do montażu bez golfa:
; 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
Z pewnością nie udało ci się tak dobrze i nadal zastanawiasz się, jak działa kod? :-)
Cóż, to całkiem proste. Po prostu iterujemy bitmapę po jednej wartości 32-bitowej na raz, sprawdzając, czy wartość RGB tego piksela jest „zachmurzona”, czy „nieprzejrzysta”. Jeśli jest pochmurno, zwiększamy nasz wstępnie wyzerowany licznik. Na koniec obliczamy: piksele zachmurzone ⁄ piksele całkowite × 8
(co odpowiada: pikselom pochmurnym ⁄ pikselom całkowitym ÷ 0,125).
Nie mogę do tego dołączyć łącza TIO ze względu na potrzebę wprowadzenia obrazów wejściowych. Mogę jednak dostarczyć uprząż, której użyłem do przetestowania tego w systemie 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;
}
Uważaj jednak na to! Jak zdefiniowano powyżej, ComputeOktas
wykorzystuje niestandardową konwencję wywoływania, której kompilator C nie będzie przestrzegał. Musisz dodać kod u góry procedury języka asemblera, aby załadować wartości ze stosu do oczekiwanych rejestrów, np . :
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]