x86-Maschinencode, 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
Diese Codebytes definieren eine Funktion, die eine Bitmap-Eingabe annimmt und einen ganzzahligen Wert zurückgibt, der die Oktas angibt. Wie in C werden Arrays (wie Bitmaps) als Zeiger auf das erste Element und als Größe / Länge dargestellt. Daher akzeptiert diese Funktion zwei Parameter: die Gesamtzahl der Pixel in der Bitmap (Zeilen × Spalten) und einen Zeiger auf die Bitmap selbst.
Dieser Code verwendet eine benutzerdefinierte, auf Registern basierende Aufrufkonvention, bei der der Bitmap-Zeiger im ESI
Register und die Bitmap-Größe im ECX
Register übergeben werden. Das Ergebnis (Oktas) wird wie üblich in zurückgegeben EAX
.
Wie oben bereits erwähnt, wird die Eingabe als Bitmap verwendet. Insbesondere wird ein 32-bpp-Format in einem Little-Endian-Format verwendet, aber der Alphakanal (Byte höchster Ordnung) wird ignoriert. Dies vereinfacht eine Menge Dinge und ermöglicht es uns, einfach durch jedes Pixel zu iterieren und seinen 32-Bit-RGB-Farbwert zu überprüfen. Auch hier kommt eine clevere Optimierung zum Einsatz. Anstatt jede Farbkomponente zu isolieren und zu prüfen, ob sie> = 192 ist, maskieren wir einfach den gesamten 32-Bit-Wert mit 0xC0C0C0 und testen, ob das Ergebnis> = 0xC0C0C0 ist. Dies wird für alle "Wolken" -Farben als wahr und für alle "Himmel" -Farben (keine Wolken) als falsch bewertet. Nun, ich fand es klug! :-) Es spart sicherlich eine große Anzahl von Bytes.
Um diesen Code zu testen, müssen Sie die Eingabebilder daher in Bitmaps mit 32 Bit / s konvertieren. Sie können Windows Paint hierfür nicht verwenden, da es maximal 24 Bit pro Pixel unterstützt. Es gibt jedoch eine Reihe anderer Softwarelösungen, die dies unterstützen, beispielsweise Adobe Photoshop. Ich habe dieses kostenlose Tool verwendet , das ein PNG in ein 32-bpp-BMP unter Windows konvertiert, was bedeutet, dass Sie nur von JPEG in PNG konvertieren müssen (was Paint kann).
Andere Annahmen, die ich setze, sind ausgesprochen vernünftig:
- Es wird angenommen, dass die Bitmap eine Größe größer als 0 hat ( d. H , es wird angenommen, dass sie mindestens ein Pixel enthält). Das ist vernünftig, denn wenn der Himmel null ist, haben wir größere Probleme als die Meteorologie.
- Es
DF
wird angenommen, dass das Richtungsflag ( ) klar ist, damit wir die Bitmap unter Verwendung der LODSD
Anweisung korrekt durchlaufen . Dies ist die gleiche Annahme wie bei den meisten x86-Aufrufkonventionen. Wenn es Ihnen nicht gefällt, addieren Sie 1 Byte zur Anzahl für eine CLD
Anweisung.
- Es wird davon ausgegangen, dass der Rundungsmodus für die x87-FPU auf die geraden Werte gerundet wird. Dies stellt sicher, dass wir das richtige Verhalten erhalten, wenn wir die Anzahl der Oktas von einem temporären Gleitkommawert in das endgültige ganzzahlige Ergebnis konvertieren, wie im Testfall Nr. 4 verifiziert. Diese Annahme ist vernünftig, da dies der Standardstatus für die FPU ist und er auch im C-Code beibehalten werden muss (wobei das Standardrundungsverhalten das Abschneiden ist), wodurch Compiler, die normenkonform sein möchten, gezwungen werden, ineffizienten Code zu generieren, der die Rundung ändert führt die Konvertierung durch und ändert dann den Rundungsmodus zurück).
Ungolfed Assembler-Mnemonik:
; 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
Sicherlich haben Sie es nicht so weit gebracht und fragen sich immer noch, wie der Code funktioniert? :-)
Nun, es ist ziemlich einfach. Wir durchlaufen die Bitmap nur jeweils um einen 32-Bit-Wert und prüfen, ob dieser Pixel-RGB-Wert "wolkig" oder "nicht wolkig" ist. Wenn es bewölkt ist, erhöhen wir unseren Zähler vor dem Nullsetzen. Am Ende berechnen wir: Bewölkte Pixel ⁄ Gesamtpixel × 8
(was äquivalent ist zu: Bewölkte Pixel ⁄ Gesamtpixel ÷ 0,125).
Ich kann hierfür keinen TIO-Link einfügen, da Eingabebilder erforderlich sind. Ich kann Ihnen jedoch das Kabel zur Verfügung stellen, mit dem ich dies unter Windows getestet habe:
#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;
}
Aber sei vorsichtig damit! ComputeOktas
Verwendet wie oben definiert eine benutzerdefinierte Aufrufkonvention, die von einem C-Compiler nicht beachtet wird. Sie müssen Code oben in der Assemblersprachenprozedur hinzufügen, um Werte aus dem Stapel in die erwarteten Register zu laden, z .
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]