x86 Machine Code, 34 bytes
25 C0 C0 C0 00
C1 E2 03
DB 04 24
DB 04 24
DB 1C 24
These bytes of code define a function that takes a bitmap input and returns an integer value indicating its oktas. As in C, arrays (like bitmaps) are represented as a pointer to the first element and a size/length. Thus, this function takes two parameters: the total number of pixels in the bitmap (rows × columns) and a pointer to the bitmap itself.
This code uses a custom register-based calling convention, where the bitmap pointer is passed in the
ESI register and the bitmap size is passed in the
ECX register. The result (oktas) is, per usual, returned in
As already stated above, the input is taken as a bitmap. Specifically, a 32-bpp format is used, in a little-endian format, but the alpha channel (highest-order byte) is ignored. This simplifies a lot of things, allowing us to simply iterate through each pixel and check its 32-bit RGB color value. A clever optimization is also used here. Instead of isolating each color component and checking whether it is >= 192, we just mask the entire 32-bit value by 0xC0C0C0 and test whether the result is >= 0xC0C0C0. This will evaluate to true for all "cloud" colors, and false for all "sky" (non-cloud) colors. Well, I thought it was clever! :-) It certainly saves a large number of bytes.
Therefore, in order to test this code, you will need to convert the input images to 32-bpp bitmaps. You cannot use Windows Paint for this, because it supports maximum of 24 bits-per-pixel. However, there are a number of other software solutions that can do it, such as Adobe Photoshop. I used this free tool, which converts a PNG to a 32-bpp BMP on Windows, meaning that you need only convert from JPEG to PNG (which Paint can do).
Other assumptions that I posit are eminently reasonable:
- The bitmap is assumed to have a size greater than 0 (i.e., it is assumed to contain at least one pixel). This is reasonable because, when they sky is null, we have bigger problems than meteorology.
- The direction flag (
DF) is assumed to be clear so that we will iterate correctly through the bitmap using the
LODSD instruction. This is the same assumption made by most x86 calling conventions, so it seems fair. If you don't like it, add 1 byte to the count for a
- The rounding mode for the x87 FPU is assumed to be set to round-to-nearest-even. This ensures that we get the correct behavior when we convert the number of oktas from a floating-point temporary to the final integer result, as verified by test case #4. This assumption is reasonable because this is the default state for the FPU and it is required to be maintained even in C code (where truncation is the default rounding behavior, forcing compilers that wish to be standards-compliant to generate inefficient code that changes the rounding mode, does the conversion, and then changes the rounding mode back).
Ungolfed assembly mnemonics:
; int ComputeOktas(void* bmpBits /* ESI */,
; uint32_t bmpSize /* ECX */);
push ecx ; save size on stack
xor edx, edx ; EDX = 0 (cloudy pixel counter)
lodsd ; EAX = DS:[ESI]; ESI += 4
and eax, 0x00C0C0C0
loop CheckPixels ; ECX -= 1; loop if ECX > 0
shl edx, 3 ; counter *= 8
fild DWORD PTR [esp] ; load original size from stack
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
Surely you haven't made it all this way down and are still wondering how the code works? :-)
Well, it's pretty simple. We just iterate through the bitmap one 32-bit value at a time, checking to see if that pixel RGB value is "cloudy" or "not cloudy". If it's cloudy, we increment our pre-zeroed counter. At the end, we compute: cloudy pixels⁄total pixels × 8
(which is equivalent to: cloudy pixels⁄total pixels ÷ 0.125).
I can't include a TIO link for this because of the need for input images. I can, however, provide you with the harness I used to test this on Windows:
// Load bitmap as a DIB section under Windows, ensuring device-neutrality
// and providing us direct access to its bits.
HBITMAP hBitmap = (HBITMAP)LoadImage(NULL,
LR_LOADFROMFILE | LR_CREATEDIBSECTION);
assert(hBitmap != NULL);
// Get the bitmap's bits and attributes.
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);
Be careful with this, though! As defined above,
ComputeOktas uses a custom calling convention, which a C compiler will not respect. You need to add code at the top of the assembly language procedure to load values from the stack into the expected registers, e.g.:
mov ecx, DWORD PTR [bmpSize]
mov esi, DWORD PTR [bmpBits]