Kode Mesin 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
Byte kode ini mendefinisikan fungsi yang mengambil input bitmap dan mengembalikan nilai integer yang mengindikasikan oktas-nya. Seperti dalam C , array (seperti bitmap) direpresentasikan sebagai pointer ke elemen pertama dan ukuran / panjang. Dengan demikian, fungsi ini mengambil dua parameter: jumlah piksel dalam bitmap (baris × kolom) dan pointer ke bitmap itu sendiri.
Kode ini menggunakan konvensi panggilan berbasis register kustom, di mana pointer bitmap dilewatkan dalam ESIregister dan ukuran bitmap dilewatkan dalam ECXregister. Hasilnya (oktas), seperti biasa, dikembalikan EAX.
Seperti yang sudah dinyatakan di atas, input diambil sebagai bitmap. Secara khusus, format 32-bpp digunakan, dalam format little-endian, tetapi kanal alfa (byte orde tertinggi) diabaikan. Ini menyederhanakan banyak hal, memungkinkan kita untuk hanya mengulangi setiap piksel dan memeriksa nilai warna RGB 32-bitnya. Optimalisasi cerdas juga digunakan di sini. Alih-alih mengisolasi setiap komponen warna dan memeriksa apakah itu adalah> = 192, kami hanya menutupi seluruh nilai 32-bit dengan 0xC0C0C0 dan menguji apakah hasilnya adalah> = 0xC0C0C0. Ini akan mengevaluasi true untuk semua warna "cloud", dan false untuk semua warna "sky" (non-cloud). Yah, saya pikir itu pintar! :-) Ini tentu saja menghemat banyak byte.
Oleh karena itu, untuk menguji kode ini, Anda harus mengubah gambar input menjadi bitmap 32-bpp. Anda tidak dapat menggunakan Windows Paint untuk ini, karena mendukung maksimum 24 bit per piksel. Namun, ada sejumlah solusi perangkat lunak lain yang dapat melakukannya, seperti Adobe Photoshop. Saya menggunakan alat gratis ini , yang mengubah PNG menjadi 32-bpp BMP di Windows, artinya Anda hanya perlu mengonversi dari JPEG ke PNG (yang dapat dilakukan Paint).
Asumsi lain yang saya anggap sangat masuk akal:
- Bitmap diasumsikan memiliki ukuran lebih besar dari 0 ( yaitu , diasumsikan mengandung setidaknya satu piksel). Ini masuk akal karena, ketika langit nol, kita memiliki masalah yang lebih besar daripada meteorologi.
- Bendera arah (
DF) diasumsikan jelas sehingga kita akan beralih dengan benar melalui bitmap menggunakan LODSDinstruksi. Ini adalah asumsi yang sama yang dibuat oleh kebanyakan konvensi pemanggilan x86, sehingga tampak adil. Jika Anda tidak menyukainya, tambahkan 1 byte ke hitungan untuk CLDinstruksi.
- Mode pembulatan untuk FPU x87 diasumsikan disetel ke pembulatan ke terdekat. Ini memastikan bahwa kita mendapatkan perilaku yang benar ketika kita mengonversi jumlah okta dari floating-point sementara ke hasil integer akhir, seperti yang diverifikasi oleh uji kasus # 4. Asumsi ini masuk akal karena ini adalah keadaan default untuk FPU dan diharuskan untuk dipertahankan bahkan dalam kode C (di mana pemotongan adalah perilaku pembulatan standar, memaksa kompiler yang ingin memenuhi standar untuk menghasilkan kode tidak efisien yang mengubah pembulatan mode, lakukan konversi, dan kemudian ubah kembali mode pembulatan).
Mnemonik perakitan tidak dikumpulkan:
; 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
Tentunya Anda belum berhasil sampai sejauh ini dan masih bertanya-tanya bagaimana kodenya bekerja? :-)
Yah, ini sangat sederhana. Kami hanya mengulangi bitmap satu nilai 32-bit pada satu waktu, memeriksa untuk melihat apakah nilai RGB pixel "keruh" atau "tidak keruh". Jika mendung, kami menambah penghitung pra-nol kami. Pada akhirnya, kami menghitung: piksel berawan ⁄ total piksel × 8
(yang setara dengan: piksel berawan ⁄ total piksel ÷ 0,125).
Saya tidak dapat menyertakan tautan TIO untuk ini karena kebutuhan untuk memasukkan gambar. Namun, saya dapat memberi Anda harness yang saya gunakan untuk menguji ini pada 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;
}
Berhati-hatilah dengan ini! Seperti yang didefinisikan di atas, ComputeOktasmenggunakan konvensi pemanggilan kustom, yang tidak akan dihormati oleh kompiler C. Anda perlu menambahkan kode di bagian atas prosedur bahasa rakitan untuk memuat nilai dari tumpukan ke register yang diharapkan, misalnya :
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]