x86 Makine Kodu, 34 bayt
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
Bu kod baytları, bir bitmap girişi alan ve oktasını gösteren bir tamsayı değeri döndüren bir işlev tanımlar. C gibi gibi, diziler (bitmapler gibi) ilk elemana bir işaretçi ve bir boyut / uzunluk olarak temsil edilir. Bu nedenle, bu işlev iki parametre alır: bitmap'teki toplam piksel sayısı (satırlar × sütunlar) ve bitmap'in kendisine bir işaretçi.
Bu kod, bitmap işaretçisinin ESI
kayıt defterine iletildiği ve bitmap boyutunun kayıt defterine iletildiği özel bir kayıt tabanlı çağrı kuralı kullanır ECX
. Sonuç (oktaş) her zamanki gibi iade edilir.EAX
.
Yukarıda belirtildiği gibi giriş bir bitmap olarak alınır. Spesifik olarak, 32-bpp formatı, biraz endian formatta kullanılır, ancak alfa kanalı (en yüksek sıradaki bayt) dikkate alınmaz. Bu, pek çok şeyi basitleştirir, her bir piksele basitçe yinelememize ve 32 bit RGB renk değerini kontrol etmemize izin verir. Akıllıca bir optimizasyon da burada kullanılmaktadır. Her renk bileşenini izole etmek ve> = 192 olup olmadığını kontrol etmek yerine, tüm 32 bitlik değeri 0xC0C0C0 ile maskeleyip sonucun> = 0xC0C0C0 olup olmadığını test ederiz. Bu, tüm "bulut" renkleri için doğru, tüm "gökyüzü" (bulut olmayan) renkler için yanlış olarak değerlendirilecektir. Peki, ben o zeki olduğunu düşündüm! :-) Kesinlikle çok sayıda bayt tasarrufu sağlar.
Bu nedenle, bu kodu test etmek için, giriş görüntülerini 32 bpp bitmap'lere dönüştürmeniz gerekir. Bunun için Windows Paint'i kullanamazsınız, çünkü piksel başına maksimum 24 bit desteklemektedir. Ancak, Adobe Photoshop gibi bunu yapabilecek başka yazılım çözümleri de var. Ben kullanılan bu ücretsiz aracıBir PNG'yi Windows'ta 32 bpp BMP'ye dönüştüren , bu da yalnızca JPEG'den PNG'ye (Paint'in yapabileceği) dönüştürmeniz gereken anlamına gelir.
Benim öne sürdüğüm diğer varsayımlar kesinlikle makul:
- Bitmap'in 0'dan büyük bir boyuta sahip olduğu kabul edilir ( yani en az bir piksel içerdiği varsayılır). Bu makul, çünkü gökyüzü boş olduğunda meteorolojiden daha büyük problemlerimiz var.
- Yön işaretinin (
DF
) açık olduğu varsayılır, böylece LODSD
talimatı kullanarak bitmap boyunca doğru şekilde yineleniriz . Bu x86 çağrı sözleşmelerinin çoğu tarafından yapılan varsayımla aynıdır, bu yüzden adil görünüyor. Hoşunuza gitmediyse, bir sayı için 1 bayt ekleyin.CLD
talimat .
- X87 FPU’nun yuvarlama modunun, en yakına yuvarlağa ayarlandığı varsayılmıştır. Bu, okta sayısını kayan noktadan geçici bir noktadan nihai tamsayı sonucuna dönüştürdüğümüzde doğru davranışı elde etmemizi sağlar (4 numaralı test durumu). Bu varsayım mantıklıdır, çünkü bu FPU için varsayılan durumdur ve C kodunda bile tutulmalıdır (burada kesmenin varsayılan yuvarlama davranışıdır, yuvarlamayı değiştiren verimsiz kod üretmeyi standartlaştırmak isteyen derleyicileri zorlar) modu, dönüştürme yapar ve sonra yuvarlama modunu geri değiştirir).
Ungolfed montaj anımsatıcıları:
; 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
Elbette bütün bu yolu bulamadı ve hala kodun nasıl çalıştığını merak ediyorsun? :-)
Eh, oldukça basit. Sadece bir seferde bitmapte bir 32 bitlik değeri yineleyerek, piksel RGB değerinin "bulutlu" veya "bulutlu değil" olup olmadığını kontrol ettik. Hava bulutluysa, sıfırlanan sayacımızı artırırız. Sonunda hesaplıyoruz: bulutlu pikseller ⁄ toplam pikseller × 8
(buna eşittir: bulutlu pikseller ⁄ toplam pikseller ÷ 0.125).
Giriş görüntülerine duyulan ihtiyaç nedeniyle bunun için bir TIO bağlantısı içeremiyorum. Bununla birlikte, bunu Windows'ta test etmek için kullandığım kablo demetini sağlayabilirim:
#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;
}
Yine de buna dikkat et! Yukarıda tanımlandığı gibi, ComputeOktas
bir C derleyicisinin saygı göstermeyeceği özel bir çağrı kuralları kullanır. Yığından gelen değerleri beklenen kayıtlara yüklemek için, montaj dili prosedürünün en üstüne kod eklemeniz gerekir, örneğin :
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]