Here’s how I’ve been reading BMP files for POV on a Uno. It still has a lot of debug printing stuff. This is just a fragment copy-pasted from my code, not refactored to be stand alone, so you will need to define FILENAME and dbg and such, include the sdfatlib etc.
I currently use standard BMP files which go from bottom to top; you can create them with top to bottom order (length in that case is negative), but I wanted easy compatibility with most software.
rowlen is the offset between rows (may need to be be rounded up to a multiple of 4 if you image width in pixels is not a multiple of 4, see commented out line). readlen is the actual bytes read from each row to the leds[] array, and is a multiple of 3. I generate images with just the number of pixels I will need in width - but it would be easy to adjust this to extract just the columns needed from a wider image by adding an initial offset at the beginning of the display loop.
The other alternative I was considering was using the 8 bits per pixel BMP format, which uses a Color Look Up Table (CLUT) to translate each of 256 colors into a 24 bit RGB value. (or even a 4 bit/pixel format with 16 values in the CLUT) For now I’ve been satisfied with this format, tho.
(Let me know if there’s a better way to format the code here)
//------ code begins ----
myFile = SD.open(FILENAME);
if (!myFile) {
// if the file didn’t open, print an error:
Serial.print("error opening file: ");
Serial.println(FILENAME);
return;
}
Serial.println(FILENAME);
long elapsed;
uint8_t <b>buf = (uint8_t</b>) malloc(0x40);
int got;
elapsed = millis();
// read BMP file
// note that the BMP default is to scan from bottom to top row
// left to right within row
// and BMP is in BGR order
got = myFile.read(buf, 0x36);
if(got < 0x36) {
Serial.println("Error reading header");
return;
}
if(0x4D42 != *(uint16_t *)buf) {
Serial.println("Did not start with BM");
return;
}
int bpp = *(int *)(buf+0x1C);
int wide = *(int *)(buf+0x12); // really a long but LSB first
int high = *(int *)(buf+0x16); // likewise
high = abs(high); // can be negative
long data_offset = *(long *)(buf + 0x0A);
Serial.print("Size ");
Serial.print(wide);
Serial.print("w x ");
Serial.print(high);
Serial.print("h x ");
Serial.print(bpp);
Serial.println(" bpp");
if(bpp != 24) {
Serial.print("Not 24 bpp");
return;
}
int rowlen = wide * 3;
// rowlen = ~3 & (rowlen + 3); // round up to multiple of 4
int readlen = NUM_LEDS * 3;
if(readlen > rowlen) readlen = rowlen;
free(buf);
//buf = (uint8_t*) malloc(rowlen);
buf = (uint8_t *) &leds[0];
while(1) {
long nextrow = data_offset;
myFile.seek(nextrow);
for(int row = 0; row < high; row++) {
got = myFile.read(buf, readlen);
if(got < readlen) {
Serial.print("Failed to read row ");
Serial.print(row);
Serial.print(", got ");
Serial.print(got);
Serial.print(" of ");
Serial.print(rowlen);
Serial.println(" bytes");
return;
}
if(rowlen > readlen) {
nextrow += rowlen;
myFile.seek(nextrow);
}
if(dbg) {
if(row < 10) { Serial.print(" "); }
else if(row < 100) { Serial.print(" "); }
Serial.print(row);
for(int j = 0; j < 30; j++) {
Serial.print(" ");
if(leds[j].r < 16) Serial.print("0");
Serial.print(leds[j].r, HEX);
if(leds[j].g < 16) Serial.print("0");
Serial.print(leds[j].g, HEX);
if(leds[j].b < 16) Serial.print("0");
Serial.print(leds[j].b, HEX);
}
Serial.println("");
while(!Serial.available()); // wait for input
while(Serial.available()) Serial.read(); // empty input buffer
}
LEDS.show();
} // for row