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格式(低位字节序格式),但是忽略了alpha通道(最高位字节)。这简化了很多事情,使我们可以简单地遍历每个像素并检查其32位RGB颜色值。此处还使用了巧妙的优化。无需隔离每个颜色分量并检查其是否大于等于192,我们只需将整个32位值屏蔽为0xC0C0C0,然后测试结果是否大于等于0xC0C0C0。对于所有“云”颜色,这将评估为true;对于所有“天空”(非云)颜色,则评估为false。好吧,我认为这很聪明!:-)当然可以节省大量字节。
因此,为了测试此代码,您将需要将输入图像转换为32 bpp位图。您不能为此使用Windows Paint,因为它最多支持每像素24位。但是,还有许多其他软件解决方案也可以做到这一点,例如Adobe Photoshop。我使用了这个免费工具,可以在Windows上将PNG转换为32-bpp BMP,这意味着您只需要从JPEG转换为PNG(Paint可以做到)。
我提出的其他假设非常合理:
- 假定位图具有大于0的大小(即,假定包含至少一个像素)。这是合理的,因为当天空为零时,我们要比气象学面临更大的问题。
DF
假定方向标志()清楚,因此我们将使用LODSD
指令正确地遍历位图。这与大多数x86调用约定所做的假设相同,因此似乎很公平。如果您不喜欢它,请在CLD
指令计数中增加1个字节。
- 假定x87 FPU的舍入模式设置为“舍入到最近”。这可以确保在将Oktas的数量从浮点临时转换为最终整数结果时得到正确的行为,如测试用例4所示。这种假设是合理的,因为这是FPU的默认状态,并且即使在C代码中也需要保持这种状态(其中截断是默认的舍入行为,这迫使希望符合标准的编译器生成无效的代码来更改舍入模式,进行转换,然后再将舍入模式更改回)。
非高尔夫装配助记符:
; 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]