Hi everyone,
I've created the following function from an example, to show parts of bmp files on a screen. It has the following parameters:
*nm : filename
x : X starting coordinate on screen for the bmp
y : Y starting coordinate on screen for the bmp
startX : X starting coordinate on the bmp
startY : Y starting coordinate on the bmp
sizeX : Length of the bmp piece showed
sizeY : Height of the bmp piece showed
It works, but only if the BMP is 8 bit and if it's upside down. I would like to change that to 32 bit and for normal pictures(so I don't have to flip all my pictures).
I'm using the following libraries:
#include <MCUFRIEND_kbv.h>
#include <Adafruit_GFX.h>
uint8_t showBMP( char *nm, int x, int y, int startX, int startY, int sizeX, int sizeY ) {
File bmpFile;
int bmpWidth, bmpHeight; // W+H in pixels
uint8_t bmpDepth; // Bit depth (currently must be 24, 16, 8, 4, 1)
uint32_t bmpImageoffset; // Start of image data in file
uint32_t rowSize; // Not always = bmpWidth; may have padding
uint8_t sdbuffer[3 * BUFFPIXEL]; // pixel in buffer (R+G+B per pixel)
uint16_t lcdbuffer[(1 << PALETTEDEPTH) + BUFFPIXEL], *palette = NULL;
int row, col, lcdbufsiz = (1 << PALETTEDEPTH) + BUFFPIXEL, buffidx;
uint32_t pos; // seek position
boolean is565 = false; //
uint16_t bmpID;
uint16_t n; // blocks read
uint8_t ret;
if ( ( x >= tft.width() ) || ( y >= tft.height() ) )
return 1; // off screen
bmpFile = SD.open(nm); // Parse BMP header
bmpID = read16(bmpFile); // BMP signature
(void) read32(bmpFile); // Read & ignore file size
(void) read32(bmpFile); // Read & ignore creator bytes
bmpImageoffset = read32(bmpFile); // Start of image data
(void) read32(bmpFile); // Read & ignore DIB header size
bmpWidth = read32(bmpFile);
bmpHeight = read32(bmpFile);
n = read16(bmpFile); // # planes -- must be '1'
bmpDepth = read16(bmpFile); // bits per pixel
pos = read32(bmpFile); // format
if ( bmpID != 0x4D42 ) ret = 2; // bad ID
else if ( n != 1 ) ret = 3; // too many planes
else if ( pos != 0 && pos != 3 ) ret = 4; // format: 0 = uncompressed, 3 = 565
else if ( bmpDepth < 16 && bmpDepth > PALETTEDEPTH ) ret = 5; // palette
else {
bool first = true;
is565 = (pos == 3); // ?already in 16-bit format
// BMP rows are padded (if needed) to 4-byte boundary
rowSize = ( bmpWidth * bmpDepth / 8 + 3 ) & ~3;
if ( bmpHeight < 0 ) { // If negative, image is in top-down order.
bmpHeight = -bmpHeight;
// flip = false;
}
// Set TFT address window to clipped image bounds
tft.setAddrWindow(x, y, x + sizeX - 1, y + sizeY - 1);
for ( row = startY; row < startY+sizeY; row++ ) { // For each scanline...
uint8_t r, g, b, *sdptr;
int lcdidx, lcdleft;
pos = bmpImageoffset + row * rowSize +2 * startX;
bmpFile.seek(pos);//Adjust X position TOP DOWN PICTURES ONLY!!!
buffidx = sizeof(sdbuffer); // Force buffer reload
for ( col = 0; col < sizeX; ) { //pixels in row
lcdleft = sizeX - col;
if ( lcdleft > lcdbufsiz ) lcdleft = lcdbufsiz;
for (lcdidx = 0; lcdidx < lcdleft; lcdidx++) { // buffer at a time
uint16_t color;
// Time to read more pixel data?
if ( buffidx >= sizeof(sdbuffer) ) { // Indeed
bmpFile.read(sdbuffer, sizeof(sdbuffer));
buffidx = 0; // Set index to beginning
r = 0;
}
switch (bmpDepth) { // Convert pixel from BMP to TFT format
case 24:
b = sdbuffer[buffidx++];
g = sdbuffer[buffidx++];
r = sdbuffer[buffidx++];
color = tft.color565(r, g, b);
break;
case 16:
b = sdbuffer[buffidx++];
r = sdbuffer[buffidx++];
if ( is565 ) color = (r << 😎 | (b); else color = (r << 9) | ((b & 0xE0) << 1) | (b & 0x1F);
break;
}
lcdbuffer[lcdidx] = color;
}
tft.pushColors(lcdbuffer, lcdidx, first);
first = false;
col += lcdidx;
} // end cols
} // end rows
tft.setAddrWindow(0, 0, tft.width() - 1, tft.height() - 1); //restore full screen
ret = 0; // good render
}
bmpFile.close();
return (ret);
}
I'm not quite familiar how the bmp is built up, any help in that area would also be appreciated. Could you please help me? Thanks in advance!