Reading R8875 BMP image files

econjack

Well-known member
Can someone share code that can read and display a BMP file on a RA8875-type display. I'm using Teensy 4.1 and trying to display a BMP image from the onboard SD reader.

Thanks!
 
This code is from my printscreen library for and ILI9341--and relies on a writeRect. If your lib has that call, this code will probably work. I've not tested it to work on your display but I use it frequently to display 24-bit BMPs from and SD card. This is for SD.h. but can be modified for SdFat.h.

If your display lib has a call called readPixel(), i hve code for reading a screen and saving a BMP to the SD card.

You could save this to a .h file and call directly or embed the code in your .ino (of course replace the d->blabla with YourDislayObject.blabla

hope this helps

Code:
bool inline DrawBMP24(ILI9341_t3 *d, int cs, const char *FileName, uint8_t x = 0, uint16_t y = 0) {

	File     bmpFile;
	int      bmpWidth, bmpHeight;			// W+H in pixels
	int      w, h, row, col;
	uint8_t  bmpDepth;					// Bit depth (currently must be 24)
	uint32_t bmpImageoffset;				// Start of image data in file
	uint32_t rowSize;						// Not always = bmpWidth; may have padding
	uint8_t  sdbuffer[3 * 8];				// pixel buffer (R+G+B per pixel)
	uint8_t  buffidx = sizeof(sdbuffer);	// Current position in sdbuffer
	boolean  flip    = true;				// BMP is stored bottom-to-top
	uint8_t  r, g, b;						// holders for red green blue
	uint32_t pos = 0;						// file position
	uint16_t awColors[320];				// hold colors for one row at a time...
	uint16_t Read16;						// file read placeholder
	uint32_t Read32;						// file read placeholder


	// let's not test begin, reason is if you create a file SD exists and will fail here
	// just use any existing object
	// we will trap file open next
	SD.begin(cs);
  
	bmpFile = SD.open(FileName, FILE_READ);

	if (!bmpFile){
		//Serial.print("SD.open: ");
		//Serial.println(bmpFile);
		return false;
	}


	if ((x >= d->width()) || (y >= d->height())) {
	return false;
	}

  // Parse BMP header

  ((uint8_t *)&Read16)[0] = bmpFile.read(); 
  ((uint8_t *)&Read16)[1] = bmpFile.read(); 

  if (Read16 == 0x4D42) { 
	// ignore read
	((uint8_t *)&Read32)[0] = bmpFile.read(); 
	((uint8_t *)&Read32)[1] = bmpFile.read();
	((uint8_t *)&Read32)[2] = bmpFile.read();
	((uint8_t *)&Read32)[3] = bmpFile.read(); 
	// ignore read
	((uint8_t *)&Read32)[0] = bmpFile.read(); 
	((uint8_t *)&Read32)[1] = bmpFile.read();
	((uint8_t *)&Read32)[2] = bmpFile.read();
	((uint8_t *)&Read32)[3] = bmpFile.read(); 

	// read offset
	((uint8_t *)&Read32)[0] = bmpFile.read(); 
	((uint8_t *)&Read32)[1] = bmpFile.read();
	((uint8_t *)&Read32)[2] = bmpFile.read();
	((uint8_t *)&Read32)[3] = bmpFile.read(); 
	bmpImageoffset = Read32; 

	// ignore read
    ((uint8_t *)&Read32)[0] = bmpFile.read(); 
	((uint8_t *)&Read32)[1] = bmpFile.read();
	((uint8_t *)&Read32)[2] = bmpFile.read();
	((uint8_t *)&Read32)[3] = bmpFile.read(); 

	// read width
	((uint8_t *)&Read32)[0] = bmpFile.read(); 
	((uint8_t *)&Read32)[1] = bmpFile.read();
	((uint8_t *)&Read32)[2] = bmpFile.read();
	((uint8_t *)&Read32)[3] = bmpFile.read(); 
	bmpWidth  = Read32;

	// read height
	((uint8_t *)&Read32)[0] = bmpFile.read(); 
	((uint8_t *)&Read32)[1] = bmpFile.read();
	((uint8_t *)&Read32)[2] = bmpFile.read();
	((uint8_t *)&Read32)[3] = bmpFile.read(); 
	bmpHeight = Read32;

	// get planes
	((uint8_t *)&Read16)[0] = bmpFile.read(); 
	((uint8_t *)&Read16)[1] = bmpFile.read();

    if (Read16 == 1) { // # planes -- must be '1'

		// read depth
		((uint8_t *)&Read16)[0] = bmpFile.read(); 
		((uint8_t *)&Read16)[1] = bmpFile.read();
		bmpDepth = Read16; // bits per pixel

		// read compression
		((uint8_t *)&Read32)[0] = bmpFile.read(); 
		((uint8_t *)&Read32)[1] = bmpFile.read();
		((uint8_t *)&Read32)[2] = bmpFile.read();
		((uint8_t *)&Read32)[3] = bmpFile.read(); 


      if ((bmpDepth == 24) && (Read32 == 0)) { // 0 = uncompressed


		  // if you got this far, legit image file
        rowSize = (bmpWidth * 3 + 3) & ~3;

        if (bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        w = bmpWidth;
        h = bmpHeight;
        if ((x + w - 1) >= d->width())  w = d->width()  - x;
        if ((y + h - 1) >= d->height()) h = d->height() - y;

        for (row = 0; row < h; row++) { // For each scanline...
          if (flip) // Bitmap is stored bottom-to-top order (normal BMP)
            pos = bmpImageoffset + (bmpHeight - 1 - row) * rowSize;
          else     // Bitmap is stored top-to-bottom
            pos = bmpImageoffset + row * rowSize;
          if (bmpFile.position() != pos) { // Need seek?
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
          }

          for (col = 0; col < w; col++) { // For each pixel...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
            }
           
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            awColors[col] = d->color565(r, g, b);
          } 

          d->writeRect(0, row, w, 1, awColors);

        }
      }
	  else {
		 return false;
	  }
    }
	else {
		 return false;
	}
  }

  bmpFile.close();

  return true;
  
}
 
For what it is worth, @mjs513 and I were playing around with a picture viewer sketch, that
could read in BMP files as well as JPEG and PNG files and we have it, such that it could be configured to run
on several different displays (ILI9341, ILI9488, ST7735/89, RA8875 and RA8876).

We were having fun with it, so for example with BMP files we have code that can scale it to fit the display
For PNG/JPEG files it uses optional libraries.

It is also setup to optionally use the USB Types including MTP.

The code is up at: https://github.com/KurtE/mtp_tft_picture_view
 
Kurt: After supplying a couple of missing header files and adjusting comments for the RA8875, your code displays my image perfectly, including scaling for a 5" display. Thanks a boatload for letting me use your code. BTW, I would like to use your code in my T41-EP Open Source project. That's a SDR transceiver for amateur radio operators. Both the hardware.software are open source and the Teensy 4.1 drives the entire system. This is the prototype of the radio:
Figure00-001Small.jpg
I would also like to feature the code in a revised edition of our book (Amazon B09WYP1ST8) if that okay. It is really a nice piece of software.
 
Kurt: I failed to notice in my previous reply that the "block artifact" appears in the upper-left corner of the display. I mentioned this in an earlier post and solved it by supplying two missing arguments from the tft.begin() method call. That did not fix it here, however, as you can see below:
WorldMapBlock.jpg
Any ideas?
 
It has been a very long time since I played much with the RA8875.

What a vaguely remembered was when I saw this, it had something like a hardware cursor turned on. And I needed to do something to turn it off.

@mjs513 and others, do you remember any more about this?

As far as using any of this code. As far as I am concerned, I have not problems at all. Except the standard, of the code is as is, and that there are no guarantees or warrantees...

Note: The original BMP code was done by Adafruit and they had examples in most of their display drivers. Note: @mjs513 and myself did update it significantly. But I still left the mentions at the start.

The PNG and JPEG stuff are Arduino libraries by @bitbank2, with whatever restrictions it may have (Apache 2)
 
Ditto on how long its been since using a RA8875.

I don't remember ever seeing a block artifact during testing to be honest.

In looking at existing example (RA8875/Examples/_Teensy3/basicTextFunctions/basicTextFunctions.ino) I did notice this command:

Code:
tft.showCursor(NOCURSOR, false); //deactivate cursor

You can give it a try to see if gets rid of it, if that is the issue.
 
KurtE: Thanks for the OK on using the code. To keep things simple, I'm striping out all but the BMP code. I will, of course, give you, mjs513, and Adafruit credits for the code. I would like to use your full names in the book, if that's okay. You can email me at jack52443 at yahoo dot com.
mjs5113: Came down this morning to work on the code and the artifact is gone! The only thing different was this morning there was a cold boot. I'll keep you suggestion in the back of my mind!
 
Back
Top