Codice macchina x86, 34 byte
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
Questi byte di codice definiscono una funzione che accetta un input bitmap e restituisce un valore intero che indica i suoi okta. Come in C , le matrici (come le bitmap) sono rappresentate come puntatore al primo elemento e dimensioni / lunghezza. Pertanto, questa funzione accetta due parametri: il numero totale di pixel nella bitmap (righe × colonne) e un puntatore alla bitmap stessa.
Questo codice utilizza una convenzione di chiamata basata su registro personalizzata, in cui il puntatore bitmap viene passato nel ESI
registro e la dimensione bitmap viene passata nel ECX
registro. Il risultato (oktas) è, come al solito, restituito EAX
.
Come già detto sopra, l'input è preso come bitmap. In particolare, viene utilizzato un formato a 32 bpp, in un formato little-endian, ma il canale alfa (byte di ordine più elevato) viene ignorato. Questo semplifica molte cose, permettendoci di scorrere semplicemente ogni pixel e controllare il suo valore di colore RGB a 32 bit. Qui viene anche utilizzata un'ottimizzazione intelligente. Invece di isolare ciascun componente colore e verificare se è> = 192, mascheriamo l'intero valore a 32 bit di 0xC0C0C0 e testiamo se il risultato è> = 0xC0C0C0. Ciò verrà valutato come vero per tutti i colori "nuvola" e falso per tutti i colori "cielo" (non nuvola). Beh, ho pensato che fosse intelligente! :-) Salva sicuramente un gran numero di byte.
Pertanto, per testare questo codice, dovrai convertire le immagini di input in bitmap a 32 bpp. Non è possibile utilizzare Windows Paint per questo, perché supporta un massimo di 24 bit per pixel. Tuttavia, ci sono diverse altre soluzioni software che possono farlo, come Adobe Photoshop. Ho usato questo strumento gratuito , che converte un PNG in un BMP a 32 bpp su Windows, il che significa che devi solo convertire da JPEG a PNG (cosa che Paint può fare).
Altre ipotesi che io sostengo sono eminentemente ragionevoli:
- Si presume che la bitmap abbia una dimensione maggiore di 0 ( ovvero , si presume che contenga almeno un pixel). Questo è ragionevole perché, quando il loro cielo è nullo, abbiamo problemi più grandi della meteorologia.
- Si
DF
presume che il flag di direzione ( ) sia chiaro in modo da iterare correttamente attraverso la bitmap usando l' LODSD
istruzione. Questo è lo stesso presupposto fatto dalla maggior parte delle convenzioni di chiamata x86, quindi sembra giusto. Se non ti piace, aggiungi 1 byte al conteggio per CLD
un'istruzione.
- Si presume che la modalità di arrotondamento per FPU x87 sia impostata su arrotondamento al più vicino-pari. Questo assicura che otteniamo il comportamento corretto quando convertiamo il numero di okta da un temporaneo in virgola mobile al risultato intero finale, come verificato dal test case n. 4. Questo presupposto è ragionevole perché questo è lo stato predefinito per la FPU e deve essere mantenuto anche nel codice C (dove il troncamento è il comportamento di arrotondamento predefinito, costringendo i compilatori che desiderano essere conformi agli standard per generare codice inefficiente che modifica l'arrotondamento modalità, esegue la conversione, quindi modifica la modalità di arrotondamento indietro).
Mnemonici di assemblaggio non golfati:
; 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
Sicuramente non l'hai fatto fino in fondo e ti stai ancora chiedendo come funziona il codice? :-)
Beh, è abbastanza semplice. Esaminiamo semplicemente la bitmap un valore a 32 bit alla volta, verificando se quel valore di pixel RGB è "nuvoloso" o "non nuvoloso". Se è nuvoloso, incrementiamo il nostro contatore pre-azzerato. Alla fine, calcoliamo: pixel nuvolosi ⁄ pixel totali × 8
(che equivale a: pixel nuvolosi ⁄ pixel totali ÷ 0,125).
Non posso includere un collegamento TIO per questo a causa della necessità di immagini di input. Posso, tuttavia, fornirti il cablaggio che ho usato per testarlo su 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;
}
Stai attento con questo, però! Come definito sopra, ComputeOktas
utilizza una convenzione di chiamata personalizzata, che un compilatore C non rispetterà. È necessario aggiungere il codice nella parte superiore della procedura del linguaggio assembly per caricare i valori dallo stack nei registri previsti, ad es :
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]