Bmp24 draws at a wrong position | 3.5'' HX8357

Hello guys.

Im having a 3,5 inch display (480x320). I try to draw a bmp24 80x80 on the display.

Everything works fine when I do it like this:

bmpDraw("Plus.bmp", 200, 20);

as soon as the x value goes over 250 the bmp isnt in the right place. I did:

bmpDraw("Plus.bmp", 380, 20); and the picture is at around position 100, 20 or so..

I tested it and <250 is fine and everything above is placed much more on the left side...

tft.fillRect(380, 20, 80, 80 ,HX8357_WHITE); draws a 80x80 square a the correct position.

I read out the serial of display.width() and display.height() and its says 480 and 320 so that should be fine. I tried with other bigger and also smaller bitmaps but the problem is the same.

Thanks for the help
& have a good one,
Anxodia

EDIT: A friend of mine has the exact same display. He tried the example with a different bmp24 and he found that he is having the same issue.

Heres my code. Its basically the spitftbitmap example from the Adafruit_HX8358.h library:

#include <Adafruit_GFX.h>    // Core graphics library
#include "Adafruit_HX8357.h"
#include <SPI.h>
#include <SD.h>

#define TFT_DC 45
#define TFT_CS 47
// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_HX8357 tft = Adafruit_HX8357(TFT_CS, TFT_DC);

#define SD_CS 46

void setup(void) {
  
  display.begin();
  SD.begin(SD_CS);
  display.setRotation(1);
  display.fillScreen(HX8357_BLACK);

  bmpDraw("Plus.bmp", 380, 20);
}

void loop() {
}

// This function opens a Windows Bitmap (BMP) file and
// displays it at the given coordinates.  It's sped up
// by reading many pixels worth of data at a time
// (rather than pixel by pixel).  Increasing the buffer
// size takes more of the Arduino's precious RAM but
// makes loading a little faster.  20 pixels seems a
// good balance.

#define BUFFPIXEL 20

void bmpDraw(char *filename, uint8_t x, uint16_t y) {

  File     bmpFile;
  int      bmpWidth, bmpHeight;   // W+H in pixels
  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*BUFFPIXEL]; // pixel buffer (R+G+B per pixel)
  uint8_t  buffidx = sizeof(sdbuffer); // Current position in sdbuffer
  boolean  goodBmp = false;       // Set to true on valid header parse
  boolean  flip    = true;        // BMP is stored bottom-to-top
  int      w, h, row, col;
  uint8_t  r, g, b;
  uint32_t pos = 0, startTime = millis();

  if((x >= tft.width()) || (y >= tft.height())) return;

  Serial.println();
  Serial.print(F("Loading image '"));
  Serial.print(filename);
  Serial.println('\'');

  // Open requested file on SD card
  if ((bmpFile = SD.open(filename)) == NULL) {
    Serial.print(F("File not found"));
    return;
  }

  // Parse BMP header
  if(read16(bmpFile) == 0x4D42) { // BMP signature
    Serial.print(F("File size: ")); Serial.println(read32(bmpFile));
    (void)read32(bmpFile); // Read & ignore creator bytes
    bmpImageoffset = read32(bmpFile); // Start of image data
    Serial.print(F("Image Offset: ")); Serial.println(bmpImageoffset, DEC);
    // Read DIB header
    Serial.print(F("Header size: ")); Serial.println(read32(bmpFile));
    bmpWidth  = read32(bmpFile);
    bmpHeight = read32(bmpFile);
    if(read16(bmpFile) == 1) { // # planes -- must be '1'
      bmpDepth = read16(bmpFile); // bits per pixel
      Serial.print(F("Bit Depth: ")); Serial.println(bmpDepth);
      if((bmpDepth == 24) && (read32(bmpFile) == 0)) { // 0 = uncompressed

        goodBmp = true; // Supported BMP format -- proceed!
        Serial.print(F("Image size: "));
        Serial.print(bmpWidth);
        Serial.print('x');
        Serial.println(bmpHeight);

        // BMP rows are padded (if needed) to 4-byte boundary
        rowSize = (bmpWidth * 3 + 3) & ~3;

        // If bmpHeight is negative, image is in top-down order.
        // This is not canon but has been observed in the wild.
        if(bmpHeight < 0) {
          bmpHeight = -bmpHeight;
          flip      = false;
        }

        // Crop area to be loaded
        w = bmpWidth;
        h = bmpHeight;
        if((x+w-1) >= tft.width())  w = tft.width()  - x;
        if((y+h-1) >= tft.height()) h = tft.height() - y;

        // Set TFT address window to clipped image bounds
        tft.startWrite(); // Start TFT transaction
        tft.setAddrWindow(x, y, w, h);

        for (row=0; row<h; row++) { // For each scanline...

          // Seek to start of scan line.  It might seem labor-
          // intensive to be doing this on every line, but this
          // method covers a lot of gritty details like cropping
          // and scanline padding.  Also, the seek only takes
          // place if the file position actually needs to change
          // (avoids a lot of cluster math in SD library).
          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?
            tft.endWrite(); // End TFT transaction
            bmpFile.seek(pos);
            buffidx = sizeof(sdbuffer); // Force buffer reload
            tft.startWrite(); // Start new TFT transaction
          }

          for (col=0; col<w; col++) { // For each pixel...
            // Time to read more pixel data?
            if (buffidx >= sizeof(sdbuffer)) { // Indeed
              tft.endWrite(); // End TFT transaction
              bmpFile.read(sdbuffer, sizeof(sdbuffer));
              buffidx = 0; // Set index to beginning
              tft.startWrite(); // Start new TFT transaction
            }

            // Convert pixel from BMP to TFT format, push to display
            b = sdbuffer[buffidx++];
            g = sdbuffer[buffidx++];
            r = sdbuffer[buffidx++];
            tft.pushColor(tft.color565(r,g,b));
          } // end pixel
        } // end scanline
        tft.endWrite(); // End last TFT transaction
        Serial.print(F("Loaded in "));
        Serial.print(millis() - startTime);
        Serial.println(" ms");
      } // end goodBmp
    }
  }

  bmpFile.close();
  if(!goodBmp) Serial.println(F("BMP format not recognized."));
}

// These read 16- and 32-bit types from the SD card file.
// BMP data is stored little-endian, Arduino is little-endian too.
// May need to reverse subscript order if porting elsewhere.

uint16_t read16(File &f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read(); // MSB
  return result;
}

uint32_t read32(File &f) {
  uint32_t result;
  ((uint8_t *)&result)[0] = f.read(); // LSB
  ((uint8_t *)&result)[1] = f.read();
  ((uint8_t *)&result)[2] = f.read();
  ((uint8_t *)&result)[3] = f.read(); // MSB
  return result;
}

Surely the Adafruit_HX8357 library comes with a 320x480 "jumpers.bmp" example.

I can't see anything wrong with the maths nor the variables e.g. pos

I don't have an SPI HX8357 but the example works fine on a 320x480 ST7796S SPI display.

Please test your "plus.bmp" by displaying on a PC e.g. with IrfanView. It should report the geometry and the format.
If in doubt, attach the "plus.bmp" to your message.

David.

Yeah "jumpers.bmp" works fine, but its placed in the upper left corner and is a fullscreen picture. If I place a smaller picture above the 250 in x direction its messing something up. Cant attach my picture as it is bmp not PNG..

I don't know whether the Adafruit code copes with off-screen pixels.

If you have a 480x320 BMP, use Landscape rotation.
If you have 320x480 use Portrait rotation.

If your picture is overlapping the screen edges, you should setAddrWindow() for each row. This will keep everything straight.

David.

So in my code I have tft.setRotation(1); to make it landscape. Now I feel like that something in the function doesnt proberly swap and still have the "XY-Matrix" in portrait mode. And then the 380 x position of my picture is oviously more than the 320 display width and thats why it's placing the picture at x = 60. Now I understand.

Can I somehow swap it so the Pixel Matrix is also landscape?

Anxodia

Have you displayed on a PC?
What are the dimensions ?

Oops. Re-reading #1 you have an 80x80 BMP that you want to display at (200, 20)
That should fit easily on Portrait 320x480 i.e. from (200, 20) to (279, 99)
Or on Landscape 480x320 it will still fit. i.e. from (200, 20) to (279, 99)

I assumed you were trying to squeeze a big BMP.

I would be much happier if you have tested the BMP picture on a PC,

David.

Exactly! And if it would swap correctly the XY with the rotation, then the 80x80 picture should fit in a 480x320 at position 380,20 (picture would end at x = 460), but instead it thinks its only 320 and places the object at x = 60. I made a quick drawing. Its ugly but maybe helps to clarify.

I opened the BMP in IrfanView. Could not see anything that wouldnt be ok with the file. Looks like this:

IrfanView Plus

I could leave the setRotation unchanged, turn all my button bmps 90° and re calculate their position but its alot of work as I have around 8 screen with buttons on it that my display will show. And its only few buttons that are not correctly positioned... So I'd rather find another solution. I started out having one picture for every screen at 480x320 but it took too long to load, so I decided to place the button pictures individually to not have to draw the black area around the buttons.

Anxodia

My apologies. All that you need is to change to uint16_t x

I was looking at the BMP algorithm and not the function arguments.

David.

Thank you David!

It works. Woudlnt have thinked of that one.

Thanks for the easy fix!

Best,
Anxodia

I wouldn't have thought that Adafruit would have made such a simple mistake!

I still recommend that you setAddrWindow() one row at a time (unless you never overlap the screen edges).

It looks as if you are using a Due or MEGA2560. So you can afford a bigger buffer.

Do you have a genuine Adafruit display ?

David.

I have HX8357 display directly from adafruit.

I do not overlap the screen edges.

I tried buffpixel with 60 instead of 20 but I didnt notice a speed difference. I stopped time with stopwatch. It was 21 seconds both times. But before when I had the whole picture with the buttons on it, it took 48 second to go trough all eight screens so im pretty happy with the speed now.

Buffpixel higer than 80 or so gave me random lines instead of the pictures. Or what do you mean by bigger buffer?

Anxodia