ゆるく楽しむプログラミング

JPEG画像をスケッチに埋め込み

天気予報表j表示の為に、アイコン画像をスケッチに埋め込む方法の小技(?)

JPEGファイルをテキストデータに変換し、表示時にデコードして表示します。
これによって、BITMAP型式でスケッチに埋め込むよりもメモリー使用量を節約できます。


JPEGファイルをソースコードに変換

1) SDカードに/ICONフォルダを作成し、/ICONフォルダに変換対象のJPEGファイルを入れる
2) M5Stackをリセット
3) SDカードに/Image.h ファイルが作成される。

/ICONフォルダ内のファイルをバイナルファイルとして16進表記のテキストデータに変換しているだけ。
JPEGファイルのヘッダ情報からファイルサイズ、画像サイズを取得して、テキストに変換したデータの先頭6バイトに付与。
※JPEGデコードすれば得られるので、画像サイズは必要なかった。



フォントサイズ=1 で表示しているので超見辛いですが、変換中のファイル名、ファイルサイズ、画像サイズを表示しています。
複数ファイルを連続して変換すると出力ファイル(Image.h)が書き込まれない事があるのでflush()を多用しているせいで遅いです。

単にファイルを読み込んで、読み込んだファイルをHEX表示でテキストに書き出しているだけなので技術的に変わった事はしていません。
「サンプルスケッチを見れば大体やり方がわかる」まであります。

JPEG画像をソースコードからデコード

ESP32のライブラリにあるJPEGデコード関数を利用します。
※M5Stack/M5Atom/M5StampなどのESP32系のマイコンボードではディフォルト状態で使用可能

JPEGデコード関数 jd_prepare() のリファレンスは▼
JRESULT jd_prepare (  
 JDEC* jdec,        /* 解凍オブジェクトへのポインタ */  
 size_t (*infunc)(JDEC*, uint8_t*, size_t), /* デバイスからの入力関数への関数ポインタ */ 
 void* work,        /* 解凍作業領域のポインタ */
  size_t sz_work,    /* 解凍作業領域のサイズ(バイト数) */
  void* device       /* 入力デバイスを管理するための構造体へのポインタ */
);

 入力デバイスを管理するための構造体は任意に定義可能ですので、デバイスからの入力関数内で都合の良いように定義すれば良いです。
 スケッチ内の配列を入力デバイスとするために▼のように定義しました。※深く考えてないから他にもっと良い方法は多分ある。
typedef struct {
 uint8_t *dp ;     /* 解凍オブジェクトへのポインタ */ 
 int ImageDataSize ; /* デバイスからの入力関数への関数ポインタ */ 
  uint8_t *fbuf;      /* 解凍作業領域のポインタ */
  unsigned int wfbuf; /* 解凍作業領域のサイズ(バイト数) */
} IODEV;

 JPEGデコードの呼び出しは▼のようにします。※WorkBuffSizeは適当。もっと小さくて大丈夫かも。
  JRESULT res;      /* TJpgDec API の結果 */
  IODEV devid;      /* 入力デバイスを管理する構造体 */

  devid.ImageDataSize = iconDataSize ; /* JPEG画像データのサイズ */
  devid.dp = iconData ;	/* JPEG画像データへのポインタ */
  devid.fbuf = NULL ;
  devid.wfbuf = 0 ;

  const int WorkBuffSize = 1024*4 ;
  work = malloc(WorkBuffSize);
  
  JRESULT res = jd_prepare(&jdec, in_func, work, WorkBuffSize, &devid);
  if (res == JDR_OK) {
    // デコード成功
  }

 入力関数では▼のように、要求されたバイト数分のデータをbuffに入れて返せばOK
 ※データの読み出し位置、読み出し可能なバイト数などはIODEV構造体で管理する
UINT in_func(JDEC* jd, BYTE* buff, UINT nbyte)
{
  IODEV *dev = (IODEV*)jd->device;
  UINT ret = 0;
  if (nbyte > dev->ImageDataSize) {
    nbyte = dev->ImageDataSize ;
  }

  if (buff) {
    memcpy(buff,dev->dp,nbyte) ;
    dev->dp += nbyte ;
    dev->ImageDataSize -= nbyte ;
    ret = nbyte;
  } else {
    // Remove bytes from input stream 
    if(nbyte > 0){ //seek mode:SeekCur=1(FS.h)
      dev->dp += nbyte ;
      dev->ImageDataSize -= nbyte ;
      ret = nbyte;
    }else{
      ret = 0;
    }
  }
  return ret;
}

 基本、コレだけなので、入力デバイスに合わせて構造体定義と入力関数を変更すればSDカードやメモリー以外(EEPROMとか)からでもデコードが可能になります。

スケッチ

JPEGファイルをTextファイルに変換するスケッチ
File2TextM5.zip

スケッチに埋め込んだJPEGファイル(お天気アイコン)を表示するスケッチ
IMAGE-DISP2.zip

解説動画