SSD1306 portrait BMP byte-order bug in ProffieOS 8.10

I’ve discovered an issue when creating a stacked BMP file to animate a kyber crystal on an OLED display. ProffieOS has a built-in image display function for each sound font on the SD card. You can either use a single-frame BMP image that is the size of your screen, or create a “stacked” bmp file that has multiple animation frames, each the size of the screen and stacked in sequence creating a long skinny bmp image. When the BMP file is named “on.bmp” and placed inside a font folder, that BMP image or image sequence will be displayed while the saber is on with the corresponding font selected. Side note: if a stacked bmp is used, a line should be added to a config.ini file in the font folder to control frame rate.

But there’s currently a bug in ProffieOS 8.10 that prevents portrait-oriented BMPs from being converted properly. When ProffieOS detects a portrait BMP, it correctly calculates the number of frames, but it never sets layout_ to LAYOUT_PORTRAIT. As a result, the existing ConvertPortrait() routine is never called, and the required byte reversal does not occur.

In my case, the image is 32 px wide, so each scanline consists of four bytes representing four 8-pixel-wide sections. The intended visual order is ABCD, but without the portrait conversion the displayed image appears as DCBA. Below are screenshots illustrating what’s happening. The black & white screenshot on the far right is the first frame (top part) of my tall BMP sequence image. The screenshot on the far left is what the OLED displays for frame 1. If the displayed image is divided into four 8-pixel-wide vertical sections and reordered from DCBA back to ABCD, it closely matches the source BMP.

The fix is a single line of code added in ssd1306.h: layout_ = LAYOUT_PORTRAIT;

Original Code:

if (width == WIDTH)
{layout_ = LAYOUT_LANDSCAPE;
looped_frames_ = height / HEIGHT;
} else {looped_frames_ = height / WIDTH;
}

Fixed Code:

if (width == WIDTH)
{layout_ = LAYOUT_LANDSCAPE;
looped_frames_ = height / HEIGHT;
} else {layout_ = LAYOUT_PORTRAIT;
looped_frames_ = height / WIDTH;
}

The portrait-conversion code was already present in ProffieOS, including LAYOUT_PORTRAIT and the ConvertPortrait() routine. The portrait BMP branch simply appears to be missing the line that selects that layout. Adding layout_ = LAYOUT_PORTRAIT; causes the existing conversion routine to run and fixes the image on the OLED.

I have confirmed this fix on the hardware while leaving the original BMP unchanged.

I hope this helps anyone who runs into the same issue.

1 Like

correction to code formatting:

Original Code:

if (width == WIDTH)
{layout_ = LAYOUT_LANDSCAPE;
looped_frames_ = height / HEIGHT;
} else {looped_frames_ = height / WIDTH;
}

Fixed Code:

if (width == WIDTH)
{layout_ = LAYOUT_LANDSCAPE;
looped_frames_ = height / HEIGHT;
} else {layout_ = LAYOUT_PORTRAIT;
looped_frames_ = height / WIDTH;
}

Nice!

I don’t suppose you want to submit a pull request on github?
(If not, I will add this fix myself, no problem.)

Yeah I’ll submit a request.