x86 รหัสเครื่อง, 34 ไบต์
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
ไบต์ของโค้ดเหล่านี้จะกำหนดฟังก์ชั่นที่รับอินพุตบิตแมปและส่งกลับค่าจำนวนเต็มเพื่อระบุว่า oktas เช่นเดียวกับใน Cอาร์เรย์ (เช่นบิตแมป) จะแสดงเป็นตัวชี้ไปยังองค์ประกอบแรกและขนาด / ความยาว ดังนั้นฟังก์ชั่นนี้ใช้สองพารามิเตอร์: จำนวนพิกเซลทั้งหมดในบิตแมป (แถว×คอลัมน์) และตัวชี้ไปยังบิตแมปตัวเอง
รหัสนี้ใช้แบบแผนการลงทะเบียนแบบกำหนดเองซึ่งตัวชี้บิตแมปถูกส่งผ่านในการESI
ลงทะเบียนและขนาดบิตแมปถูกส่งผ่านในการECX
ลงทะเบียน ผลที่ตามมา (oktas) EAX
เป็นต่อตามปกติกลับมาใน
ตามที่ระบุไว้ข้างต้นอินพุตจะถูกใช้เป็นบิตแมป โดยเฉพาะจะใช้รูปแบบ 32-bpp ในรูปแบบเล็ก ๆ น้อย ๆ แต่จะไม่สนใจช่องอัลฟา (ไบต์เรียงลำดับสูงสุด) สิ่งนี้ทำให้สิ่งต่างๆง่ายขึ้นมากทำให้เราสามารถวนซ้ำแต่ละพิกเซลและตรวจสอบค่าสี RGB แบบ 32 บิต การเพิ่มประสิทธิภาพที่ฉลาดยังใช้ที่นี่ แทนที่จะแยกแต่ละองค์ประกอบสีและตรวจสอบว่ามันเป็น> = 192 เราเพียงปกปิดค่า 32- บิตทั้งหมดโดย 0xC0C0C0 และทดสอบว่าผลลัพธ์คือ> = 0xC0C0C0 สิ่งนี้จะประเมินว่าเป็นจริงสำหรับทุกสี "เมฆ" และเป็นเท็จสำหรับสี "ท้องฟ้า" (ไม่ใช่เมฆ) ทั้งหมด ดีฉันคิดว่ามันเป็นฉลาด! :-) มันช่วยประหยัดจำนวนไบต์ได้อย่างแน่นอน
ดังนั้นเพื่อทดสอบรหัสนี้คุณจะต้องแปลงภาพอินพุตเป็นบิตแมป 32-bpp คุณไม่สามารถใช้ Windows Paint สำหรับสิ่งนี้เนื่องจากรองรับสูงสุด 24 บิตต่อพิกเซล อย่างไรก็ตามมีโซลูชันซอฟต์แวร์อื่น ๆ อีกจำนวนมากที่สามารถทำได้เช่น Adobe Photoshop ฉันใช้เครื่องมือฟรีนี้ซึ่งแปลง PNG เป็น BMP แบบ 32 bpp บน Windows หมายความว่าคุณต้องแปลงจาก JPEG เป็น PNG เท่านั้น (ซึ่ง Paint สามารถทำได้)
สมมติฐานอื่น ๆ ที่ฉันวางไว้มีเหตุผลชัดเจน:
- บิตแมปจะถือว่ามีขนาดใหญ่กว่า 0 ( กล่าวคือจะมีขนาดอย่างน้อยหนึ่งพิกเซล) สิ่งนี้สมเหตุสมผลเนื่องจากเมื่อท้องฟ้าไม่ว่างเรามีปัญหาใหญ่กว่าอุตุนิยมวิทยา
- ทิศทางธง (
DF
) จะถือว่าชัดเจนเพื่อให้เราทำซ้ำอย่างถูกต้องผ่านบิตแมปโดยใช้LODSD
คำสั่ง นี่คือสมมติฐานเดียวกันที่ทำโดยอนุสัญญาการเรียก x86 ส่วนใหญ่ดังนั้นจึงดูเหมือนยุติธรรม หากคุณไม่ชอบให้เพิ่ม 1 ไบต์ไปยังจำนวนสำหรับ aCLD
คำสั่ง
- โหมดการปัดเศษสำหรับ x87 FPU นั้นถูกกำหนดให้เป็นแบบปัดเศษเป็นแบบใกล้เคียงที่สุด สิ่งนี้ช่วยให้มั่นใจได้ว่าเราได้รับพฤติกรรมที่ถูกต้องเมื่อเราแปลงจำนวน oktas จากจุดลอยตัวชั่วคราวเป็นผลลัพธ์จำนวนเต็มสุดท้ายตามที่ตรวจสอบโดยกรณีทดสอบ # 4 สมมติฐานนี้มีเหตุผลเพราะนี่คือสถานะเริ่มต้นสำหรับ FPU และจำเป็นต้องได้รับการดูแลแม้ในรหัส C (โดยที่การตัดปลายเป็นพฤติกรรมการปัดเศษเริ่มต้นบังคับให้คอมไพเลอร์ที่ต้องการให้เป็นไปตามมาตรฐานเพื่อสร้างรหัสที่ไม่มีประสิทธิภาพ โหมดทำการแปลงและเปลี่ยนโหมดการปัดเศษกลับ)
คำย่อของชุดประกอบ Ungolfed:
; 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
แน่นอนคุณยังไม่ได้ทำให้มันลงและยังคงสงสัยว่ารหัสทำงานอย่างไร :-)
ดีมันค่อนข้างง่าย เราแค่วนซ้ำบิตแมปหนึ่งค่า 32- บิตในแต่ละครั้งตรวจสอบเพื่อดูว่าค่าพิกเซล RGB นั้นเป็น "ขุ่น" หรือ "ไม่ขุ่น" ถ้ามันมีเมฆมากเราจะเพิ่มตัวนับที่เป็นศูนย์ล่วงหน้า ในตอนท้ายเราคำนวณ: พิกเซลมีเมฆมาก / พิกเซลรวม × 8
(ซึ่งเทียบเท่ากับ: พิกเซลมีเมฆมาก / พิกเซลรวม ÷ 0.125)
ฉันไม่สามารถรวมลิงค์ TIO สำหรับสิ่งนี้ได้เนื่องจากความต้องการใช้ภาพอินพุต อย่างไรก็ตามฉันสามารถมอบสายรัดให้คุณทดสอบบน 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;
}
ระวังตัวด้วยล่ะ! ตามที่นิยามไว้ข้างต้นComputeOktas
ใช้หลักการเรียกที่กำหนดเองซึ่งคอมไพเลอร์ C จะไม่เคารพ คุณต้องเพิ่มรหัสที่ด้านบนสุดของขั้นตอนภาษาแอสเซมบลีเพื่อโหลดค่าจากสแต็กไปยังรีจิสเตอร์ที่คาดไว้เช่น :
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]