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のようにと、配列(ビットマップなど)は、最初の要素へのポインターとサイズ/長さとして表されます。したがって、この関数は2つのパラメーターを取ります。ビットマップ内のピクセルの総数(行×列)とビットマップ自体へのポインターです。
このコードは、カスタムレジスタベースの呼び出し規則を使用します。この規則では、ビットマップポインターがESI
レジスターに渡され、ビットマップサイズがECX
レジスターに渡されます。結果(oktas)は、通常、で返されEAX
ます。
すでに上で述べたように、入力はビットマップとして取得されます。具体的には、リトルエンディアン形式で32 bpp形式が使用されますが、アルファチャネル(最上位バイト)は無視されます。これにより、多くのことが単純化され、各ピクセルを単純に反復処理し、32ビットRGBカラー値を確認できるようになります。巧妙な最適化もここで使用されます。各色成分を分離して192以上かどうかをチェックする代わりに、32ビット値全体を0xC0C0C0でマスクし、結果が0xC0C0C0以上かどうかをテストします。これは、すべての「雲」色に対してtrue、すべての「空」(雲以外)色に対してfalseと評価されます。まあ、私ははそれが賢いと思った!:-)それは確かに大量のバイトを節約します。
したがって、このコードをテストするには、入力画像を32-bppビットマップに変換する必要があります。これにはWindowsペイントを使用できません。これは、ピクセルあたり最大24ビットをサポートしているためです。ただし、Adobe Photoshopなど、それを実行できるソフトウェアソリューションは他にも多数あります。この無料ツールを使用しましたWindowsでPNGを32-bpp BMPに変換するしました。つまり、JPEGからPNGに変換するだけで済みます(ペイントで実行できます)。
私が主張する他の仮定は非常に合理的です:
- ビットマップのサイズは0より大きいと想定されます(つまり、と想定されます、少なくとも1つのピクセルが含まれていると想定されます)。空が空のとき、気象よりも大きな問題があるため、これは合理的です。
- 命令
DF
を使用してビットマップを正しく反復処理するために、方向フラグ()はクリアされていると想定されますLODSD
。これは、ほとんどのx86呼び出し規約で行われた仮定と同じであるため、公平に思えます。気に入らなければ、1バイトをカウントに追加しますCLD
命令のください。
- x87 FPUの丸めモードは、最も近い偶数に丸められるように設定されていると想定されています。これにより、テストケース#4で検証されているように、oktasの数を一時的な浮動小数点から最終的な整数結果に変換するときに正しい動作が得られます。これは、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]