M5Stack TFカード　ビットマップフォーマットのダンプ表示

# bitmapのフォーマットをdump表示。

![bitmapフォーマットダンプ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693934/88f1c629-37ec-7e26-ae79-8be5fe4dc2e7.png)
TFカードからビットマップを読み出すときの内容をダンプしました。
＜参考＞
bitmapフォーマットの解説を参考にさせてもらいました。
https://www.marutsu.co.jp/pc/static/large_order/capture_220812



```C++:main.cpp
#include <M5Core2.h>

uint16_t s_read16(fs::File &f);
uint32_t s_read32(fs::File &f);
void readSD(fs::FS &fs, const char *path);

void setup()
{
  M5.begin();
  M5.Lcd.fillScreen(WHITE);
  M5.Lcd.setTextSize(2);
  M5.Lcd.print("SD TEST");
  readSD(SD,"/tyrrell019mini.bmp");
}

void loop()
{
  delay(1);
}

// 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 s_read16(fs::File &f) {
  uint16_t result;
  ((uint8_t *)&result)[0] = f.read();  // LSB
  ((uint8_t *)&result)[1] = f.read();  // MSB
  return result;
}

uint32_t s_read32(fs::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;
}

void readSD(fs::FS &fs, const char *path)
{
  //SDカードオープン
  File bmpFS = fs.open(path, "r", false);
  if(!bmpFS)
  {
    Serial.print("File not found");
    return;
  }
  //経過時間の計測
  uint32_t startTime = millis();

  //BMPフォーマット
  uint32_t seekOffset;
  uint16_t w,h;
  uint16_t row, col;

  uint16_t ch; 
  ch = s_read16(bmpFS);
  if(((uint8_t *)&ch)[1] =='M' && ((uint8_t *)&ch)[0] == 'B')//0000:M(0x4D) 0001:B(0x42)
  {
    //BMPファイルヘッダ
    uint32_t bfSize = s_read32(bmpFS);//0002h:ファイルサイズ
    uint32_t bfReserved1 = s_read16(bmpFS);//0006h:リザーブ0x0000
    uint32_t bfReserved2 = s_read16(bmpFS);//0008h:リザーブ0x0000
    seekOffset = s_read32(bmpFS);//000Ahファイル先頭からイメージデータまでの総バイト数
    Serial.printf("0002:FileSize:%d(bytes)\r", bfSize);
    Serial.printf("0006:Reserve:%04x\r",bfReserved1);
    Serial.printf("0008:Reserve:%04x\r",bfReserved2);
    Serial.printf("000A:Offset:%d(bytes)\r",seekOffset);
    //BMPファイル情報ヘッダ
    uint32_t hsize = s_read32(bmpFS);//000Eh:ヘッダーサイズ(40byte)
    w=s_read32(bmpFS);//0012h:イメージ幅
    h=s_read32(bmpFS);//0016h:イメージ高さ
    uint16_t biPlanes = s_read16(bmpFS);//001Ah:プレーン数(常に１)
    uint16_t biBitCount = s_read16(bmpFS);//001Ch:ビット数/pixel
    uint32_t biCompression = s_read32(bmpFS);//001Eh:圧縮方式:0
    Serial.printf("000E:HeaderSize:%d(bytes)\r",hsize);
    Serial.printf("0012:Width:%d(pix)\r",w);
    Serial.printf("0016:Height:%d(pix)\r",h);
    Serial.printf("001A:Plane:%d\r",biPlanes);
    Serial.printf("001C:BitCount:%d(bit/pixel)\r",biBitCount);
    Serial.printf("001E:Compression:%d\r",biCompression);

    //BMP format check
    if((biPlanes==1) && (biBitCount==24) && (biCompression == 0))
    {
      bmpFS.seek(seekOffset);//イメージデータへポインタジャンプ
      //1行分のデータは４の倍数に合わせるpadding.
      uint16_t padding = (4 - ((w*3)&3))&3;//&3は４で割った余り。
      uint8_t lineBuffer[w * 3 + padding];//４の倍数の配列確保
      //イメージデータの読み出し　リトルエンディアン
      for(row=0; row<h; row++)
      {
        bmpFS.read(lineBuffer,sizeof(lineBuffer));
        uint8_t *bptr = lineBuffer;
        //uint16_t *tptr = (uint16_t*)lineBuffer;
        Serial.printf("ROW:%d ",row);
        for(col=0; col<w; col++)
        {
           Serial.printf("%02X %02X %02X,",*bptr++, *bptr++, *bptr);
        }
        Serial.printf("\r");
        
      }
      Serial.print("elapsed time ");
      Serial.print(millis()-startTime);
      Serial.println(" ms");
    }else
    {
      Serial.println("BMP format not recognized.");
    }
  }else
  {
    Serial.println("BM error");
  }
  bmpFS.close();

}
```
# 参考にしたコード。M5ライブラリ TFカードからBMPを読込む関数。
```C++:M5display.cpp
// Bodmers BMP image rendering function
void M5Display::drawBmpFile(fs::FS &fs, const char *path, uint16_t x,
                            uint16_t y) {
  if ((x >= width()) || (y >= height())) return;

  // Open requested file on SD card
  File bmpFS = fs.open(path, "r");

  if (!bmpFS) {
    Serial.print("File not found");
    return;
  }

  uint32_t seekOffset;
  uint16_t w, h, row, col;
  uint8_t r, g, b;

  uint32_t startTime = millis();

  if (read16(bmpFS) == 0x4D42) {
    read32(bmpFS);
    read32(bmpFS);
    seekOffset = read32(bmpFS);
    read32(bmpFS);
    w = read32(bmpFS);
    h = read32(bmpFS);

    if ((read16(bmpFS) == 1) && (read16(bmpFS) == 24) && (read32(bmpFS) == 0)) {
      y += h - 1;

      setSwapBytes(true);
      bmpFS.seek(seekOffset);

      uint16_t padding = (4 - ((w * 3) & 3)) & 3;
      uint8_t lineBuffer[w * 3 + padding];

      for (row = 0; row < h; row++) {
        bmpFS.read(lineBuffer, sizeof(lineBuffer));
        uint8_t *bptr = lineBuffer;
        uint16_t *tptr = (uint16_t *)lineBuffer;
        // Convert 24 to 16 bit colours
        for (col = 0; col < w; col++) {
          b = *bptr++;
          g = *bptr++;
          r = *bptr++;
          *tptr++ = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3);
        }

        // Push the pixel row to screen, pushImage will crop the line if needed
        // y is decremented as the BMP image is drawn bottom up
        pushImage(x, y--, w, 1, (uint16_t *)lineBuffer);
      }
      Serial.print("Loaded in ");
      Serial.print(millis() - startTime);
      Serial.println(" ms");
    } else
      Serial.println("BMP format not recognized.");
  }
  bmpFS.close();
}
```