Código de máquina x86, 34 bytes
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
Esses bytes de código definem uma função que recebe uma entrada de bitmap e retorna um valor inteiro indicando seus oktas. Como em C , matrizes (como bitmaps) são representadas como um ponteiro para o primeiro elemento e um tamanho / comprimento. Portanto, essa função usa dois parâmetros: o número total de pixels no bitmap (linhas × colunas) e um ponteiro para o próprio bitmap.
Esse código usa uma convenção de chamada personalizada baseada em registro, em que o ponteiro de bitmap é passado no ESI
registro e o tamanho do bitmap é passado no ECX
registro. O resultado (octas) é, normalmente, retornado emEAX
.
Como já mencionado acima, a entrada é tomada como um bitmap. Especificamente, um formato de 32 bpp é usado, em um formato little endian, mas o canal alfa (byte de ordem mais alta) é ignorado. Isso simplifica muitas coisas, permitindo simplesmente percorrer cada pixel e verificar seu valor de cor RGB de 32 bits. Uma otimização inteligente também é usada aqui. Em vez de isolar cada componente de cor e verificar se é> = 192, apenas mascaramos o valor inteiro de 32 bits por 0xC0C0C0 e testamos se o resultado é> = 0xC0C0C0. Isso será avaliado como verdadeiro para todas as cores "nuvem" e falso para todas as cores "céu" (sem nuvem). Bem, eu pensei que era inteligente! :-) Certamente salva um grande número de bytes.
Portanto, para testar esse código, você precisará converter as imagens de entrada em bitmaps de 32 bpp. Você não pode usar o Windows Paint para isso, porque ele suporta no máximo 24 bits por pixel. No entanto, existem várias outras soluções de software que podem fazer isso, como o Adobe Photoshop. Usei essa ferramenta gratuita , que converte um PNG em um BMP de 32 bpp no Windows, o que significa que você só precisa converter de JPEG para PNG (o que o Paint pode fazer).
Outras suposições que afirmo são eminentemente razoáveis:
- Supõe-se que o bitmap tenha um tamanho maior que 0 ( isto é, supõe-se que contenha pelo menos um pixel). Isso é razoável porque, quando o céu é nulo, temos problemas maiores que a meteorologia.
- O sinalizador de direção (
DF
) é assumido como claro, para que iteremos corretamente pelo bitmap usando oLODSD
instrução Essa é a mesma suposição feita pela maioria das convenções de chamada x86, portanto parece justo. Se você não gostar, adicione 1 byte à contagem para obter uma CLD
instrução.
- Presume-se que o modo de arredondamento para a FPU x87 esteja definido como arredondar para o par mais próximo. Isso garante que obtemos o comportamento correto ao converter o número de octas de um resultado temporário de ponto flutuante para o resultado inteiro final, conforme verificado no caso de teste nº 4. Essa suposição é razoável, porque esse é o estado padrão da FPU e deve ser mantido mesmo no código C (onde truncamento é o comportamento de arredondamento padrão, forçando os compiladores que desejam ser compatíveis com os padrões a gerar código ineficiente que altera o arredondamento , faz a conversão e depois altera o modo de arredondamento).
Mnemônicos de montagem não destruídos:
; 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
Certamente você não chegou até aqui e ainda está se perguntando como o código funciona? :-)
Bem, é bem simples. Apenas iteramos o bitmap de um valor de 32 bits por vez, verificando se o valor RGB do pixel está "nublado" ou "não nublado". Se estiver nublado, incrementamos nosso contador pré-zerado. No final, calculamos: pixels nublados ⁄ pixels totais × 8
(o que é equivalente a: pixels nublados ⁄ pixels totais ÷ 0,125).
Não posso incluir um link TIO para isso devido à necessidade de imagens de entrada. No entanto, posso fornecer o chicote que usei para testar isso no 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;
}
Tenha cuidado com isso, no entanto! Conforme definido acima, ComputeOktas
usa uma convenção de chamada personalizada, que um compilador C não respeitará. Você precisa adicionar código na parte superior do procedimento da linguagem assembly para carregar valores da pilha nos registros esperados, por exemplo :
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]