Code machine x86, 34 octets
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
Ces octets de code définissent une fonction qui prend une entrée bitmap et renvoie une valeur entière indiquant ses octas. Comme en C , les tableaux (comme les bitmaps) sont représentés comme un pointeur vers le premier élément et une taille / longueur. Ainsi, cette fonction prend deux paramètres: le nombre total de pixels dans le bitmap (lignes × colonnes) et un pointeur sur le bitmap lui-même.
Ce code utilise une convention d'appel basée sur un registre personnalisé, où le pointeur bitmap est passé dans le ESI
registre et la taille bitmap est passée dans leECX
registre. Le résultat (octas) est, comme d'habitude, retourné EAX
.
Comme déjà indiqué ci-dessus, l'entrée est considérée comme un bitmap. Plus précisément, un format 32 bpp est utilisé, dans un format petit-boutien, mais le canal alpha (octet de poids fort) est ignoré. Cela simplifie beaucoup de choses, nous permettant simplement de parcourir chaque pixel et de vérifier sa valeur de couleur RVB 32 bits. Une optimisation intelligente est également utilisée ici. Au lieu d'isoler chaque composante de couleur et de vérifier si elle est> = 192, nous masquons simplement la valeur entière de 32 bits par 0xC0C0C0 et testons si le résultat est> = 0xC0C0C0. Ceci sera évalué à vrai pour toutes les couleurs "nuage" et faux à toutes les couleurs "ciel" (hors nuage). Eh bien, je pensais que c'était intelligent! :-) Il enregistre certainement un grand nombre d'octets.
Par conséquent, afin de tester ce code, vous devrez convertir les images d'entrée en bitmaps 32 bpp. Vous ne pouvez pas utiliser Windows Paint pour cela, car il prend en charge un maximum de 24 bits par pixel. Cependant, il existe un certain nombre d'autres solutions logicielles qui peuvent le faire, comme Adobe Photoshop. j'ai utilisé cet outil gratuit , qui convertit un PNG en BMP 32 bpp sous Windows, ce qui signifie que vous n'avez besoin que de convertir du JPEG au PNG (ce que Paint peut faire).
Les autres hypothèses que je pose sont éminemment raisonnables:
- Le bitmap est supposé avoir une taille supérieure à 0 ( c'est -à- dire qu'il est supposé contenir au moins un pixel). C'est raisonnable car, quand le ciel est nul, nous avons des problèmes plus importants que la météorologie.
- Le drapeau de direction (
DF
) est supposé être clair afin que nous puissions itérer correctement à travers le bitmap en utilisant leLODSD
instruction. C'est la même hypothèse faite par la plupart des conventions d'appel x86, donc cela semble juste. Si vous ne l'aimez pas, ajoutez 1 octet au nombre d'une CLD
instruction.
- Le mode d'arrondi du FPU x87 est supposé être réglé sur arrondi au plus proche pair. Cela garantit que nous obtenons le comportement correct lorsque nous convertissons le nombre d'octas d'un nombre à virgule flottante en résultat entier final, comme vérifié par le cas de test # 4. Cette hypothèse est raisonnable car il s'agit de l'état par défaut pour le FPU et il doit être conservé même en code C (où la troncature est le comportement d'arrondi par défaut, forçant les compilateurs qui souhaitent être conformes aux normes à générer du code inefficace qui modifie l'arrondi , effectue la conversion, puis modifie le mode d'arrondi).
Mnémoniques d'assemblage non golfés:
; 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
Vous n'avez sûrement pas fait tout ce chemin et vous vous demandez toujours comment fonctionne le code? :-)
Eh bien, c'est assez simple. Nous parcourons simplement le bitmap une valeur 32 bits à la fois, en vérifiant si cette valeur RVB de pixel est "nuageuse" ou "non nuageuse". S'il fait nuageux, nous incrémentons notre compteur pré-mis à zéro. À la fin, nous calculons: pixels nuageux ⁄ pixels totaux × 8
(ce qui équivaut à: pixels nuageux ⁄ pixels totaux ÷ 0,125).
Je ne peux pas inclure de lien TIO pour cela en raison du besoin d'images d'entrée. Je peux cependant vous fournir le harnais que j'ai utilisé pour tester cela sur 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;
}
Soyez prudent avec cela, cependant! Comme défini ci-dessus, ComputeOktas
utilise une convention d'appel personnalisée, qu'un compilateur C ne respectera pas. Vous devez ajouter du code en haut de la procédure du langage assembleur pour charger les valeurs de la pile dans les registres attendus, par exemple :
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]