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
.
위에서 이미 언급했듯이 입력은 비트 맵으로 사용됩니다. 특히 32bpp 형식이 리틀 엔디안 형식으로 사용되지만 알파 채널 (최고 바이트)은 무시됩니다. 이를 통해 많은 작업을 단순화하여 각 픽셀을 간단히 반복하고 32 비트 RGB 색상 값을 확인할 수 있습니다. 영리한 최적화도 여기에서 사용됩니다. 각 색상 구성 요소를 분리하고> = 192인지 확인하는 대신 전체 32 비트 값을 0xC0C0C0으로 마스킹하고 결과가> = 0xC0C0C0인지 테스트합니다. 모든 "구름"색상의 경우 true로 평가되고 모든 "하늘"(비구름) 색상의 경우 false로 평가됩니다. 글쎄, 나는 그것이 영리하다고 생각했다! :-) 확실히 많은 수의 바이트를 저장합니다.
따라서이 코드를 테스트하려면 입력 이미지를 32bpp 비트 맵으로 변환해야합니다. 픽셀 당 최대 24 비트를 지원하므로 Windows Paint를 사용할 수 없습니다. 그러나 Adobe Photoshop과 같은 다른 소프트웨어 솔루션도 있습니다. 필자 는 Windows에서 PNG를 32bpp BMP로 변환하는 이 무료 도구를 사용 했습니다 . 즉, JPEG에서 PNG로만 변환하면됩니다 (페인트 가능).
내가 주장하는 다른 가정은 명백히 합리적입니다.
- 비트 맵은 0보다 큰 크기를 갖는 것으로 가정된다 ( 즉 , 적어도 하나의 픽셀을 포함하는 것으로 가정 됨). 하늘이 널 때 기상학보다 더 큰 문제가 있기 때문에 이것은 합리적입니다.
- 방향 플래그 (
DF
)는 LODSD
명령을 사용하여 비트 맵을 올바르게 반복 할 수 있도록 명확하다고 가정합니다 . 이것은 대부분의 x86 호출 규칙과 동일한 가정이므로 공정한 것 같습니다. 마음에 들지 않으면 CLD
명령어 수에 1 바이트를 추가하십시오 .
- x87 FPU의 반올림 모드는 가장 가까운 반올림으로 설정된 것으로 가정합니다. 이를 통해 테스트 사례 # 4에서 확인한 것처럼 oktas 수를 부동 소수점 임시에서 최종 정수 결과로 변환 할 때 올바른 동작을 얻을 수 있습니다. 이것은 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
확실히 당신은이 방법으로 그것을 만들지 않았고 여전히 코드가 어떻게 작동하는지 궁금합니다? :-)
글쎄요, 꽤 간단합니다. 픽셀 RGB 값이 "흐리게"또는 "흐리게"아닌지 확인하여 비트 맵을 한 번에 하나의 32 비트 값으로 반복합니다. 흐린 경우 사전 영점 카운터를 증가시킵니다. 결국, 우리는 흐린 픽셀 ⁄ 총 픽셀 × 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]