実行時の画像の読込み
このセクションでは、TouchGFXアプリケーションでファイルから画像を読み込む方法について説明します。
TouchGFXアプリケーションでビットマップを使用するための標準的な手順は、アプリケーションにビットマップを含めることです。 PNGファイルは.cppファイル内でバイナリ・データに変換され、コンパイルされてアプリケーションにリンクされます。 これが、実行時にアプリケーションでビットマップを使用できるようにするための簡単な方法です。
アプリケーションによってはこの方法が可能でない、もしくは実用的でない場合があります。 たとえば、コンパイル時に画像が使用可能でない場合(インターネット接続によって後からダウンロードするような場合)や、単純にビットマップが多すぎてアプリケーションのFlash領域に収まらない場合などがあります。
ダイナミック・ビットマップ(下のリンクを参照)は、この問題に対するRAMベースのソリューションです。 ダイナミック・ビットマップは実行時に作成され、アプリケーションがビットマップの数、フォーマット、幅と高さを自由に決定できます。
ダイナミック・ビットマップはRAM(ビットマップ・キャッシュ内)に保存されるので、ダイナミック・ビットマップの作成後に、アプリケーションはソースのピクセル値をダイナミック・ビットマップ内にコピーする必要があります。 作成後、ダイナミック・ビットマップには初期化されていないピクセルが含まれます。
このセクションでは、ダイナミック・ビットマップを使用して、グラフィック・コンテンツの一部をBMPファイルから読み取るアプリケーションを作成する方法について説明します。 単純なユース・ケースには、SDカードにあるBMPファイルを表示するアプリケーションがあります。
Note
標準のビットマップはアプリケーションにコンパイルされるので、コンパイル時に使用可能でなければならないことを思い出してください。 ダイナミック・ビットマップ機能により、実行時にファイルから画像を読み取ることができ、インターネット接続を介して画像をダウンロードすることもできます。
BMPファイルの読込み例
次に、BMPローダを使用してWindows BMPファイルからピクセルを読み込む方法について見ていきます。 ローダ用のコードは記事の後半にあります。
まず画像ウィジェットをビューに挿入します。 このウィジェットがBMPを表示します。
class TemplateView : public View
{
private:
Image image;
}
画像の日付をsetupScreenに読み込みます。
void TemplateView::setupScreen()
{
FILE* f = fopen("image.jpg", "rb");
uint16_t width, height;
//Get the image dimensions from the BMP file
BMPFileLoader::getBMP24Dimensions(f, width, height);
BitmapId bmpId;
//Create (16bit) dynamic bitmap of same dimension
bmpId = Bitmap::dynamicBitmapCreate(width, height, Bitmap::RGB565);
//Load pixels from BMP file to dynamic bitmap
BMPFileLoader::readBMP24File(Bitmap(bmpId), f);
//Make Image show the loaded bitmap
image.setBitmap(Bitmap(bmpId));
//Position image and add to View
image.setXY(20, 20);
add(image);
...
}
BMPローダ
次に、単純なBMPファイル・ローダのコードを示します。 これは24bppのBMPファイルのみをサポートします。 お使いのシステムに合わせて、ファイル・システムの呼び出しの調整が必要になる可能性があります。
BMPFileLoader.hpp
#include <touchgfx/hal/Types.hpp>
#include <touchgfx/Bitmap.hpp>
using namespace touchgfx;
class BMPFileLoader
{
public:
typedef void* FileHdl;
static void getBMP24Dimensions(FileHdl fileHandle, uint16_t& width, uint16_t& height);
static void readBMP24File(Bitmap bitmap, FileHdl fileHandle);
private:
static int readFile(FileHdl hdl, uint8_t* const buffer, uint32_t length);
static void seekFile(FileHdl hdl, uint32_t offset);
};
BMPFileLoader.cpp
#include <gui/common/BMPFileLoader.hpp>
#include <platform/driver/lcd/LCD16bpp.hpp>
#include <touchgfx/Color.hpp>
int BMPFileLoader::readFile(FileHdl hdl, uint8_t* const buffer, uint32_t length)
{
uint32_t r = fread(buffer, 1, length, (FILE*)hdl);
if (r != length)
{
return 1;
}
return 0;
}
void BMPFileLoader::seekFile(FileHdl hdl, uint32_t offset)
{
fseek((FILE*)hdl, offset, SEEK_SET);
}
void BMPFileLoader::getBMP24Dimensions(FileHdl fileHandle, uint16_t& width, uint16_t& height)
{
uint8_t data[50];
seekFile(fileHandle, 0);
readFile(fileHandle, data, 26); //read first part of header.
width = data[18] | (data[19] << 8) | (data[20] << 16) | (data[21] << 24);
height = data[22] | (data[23] << 8) | (data[24] << 16) | (data[25] << 24);
}
void BMPFileLoader::readBMP24File(Bitmap bitmap, FileHdl fileHandle)
{
uint8_t data[50];
seekFile(fileHandle, 0);
readFile(fileHandle, data, 26); //read first part of header.
const uint32_t offset = data[10] | (data[11] << 8) | (data[12] << 16) | (data[12] << 24);
const uint32_t width = data[18] | (data[19] << 8) | (data[20] << 16) | (data[21] << 24);
const uint32_t height = data[22] | (data[23] << 8) | (data[24] << 16) | (data[25] << 24);
readFile(fileHandle, data, offset - 26); //read rest of header.
//get dynamic bitmap boundaries
const uint32_t buffer_width = bitmap.getWidth();
const uint32_t buffer_height = bitmap.getHeight();
const uint32_t rowpadding = (4 - ((width * 3) % 4)) % 4;
const Bitmap::BitmapFormat format = bitmap.getFormat();
uint8_t* const buffer8 = Bitmap::dynamicBitmapGetAddress(bitmap.getId());
uint16_t* const buffer16 = (uint16_t*)buffer8;
for (uint32_t y = 0; y < height; y++)
{
for (uint32_t x = 0; x < width; x++)
{
if (x % 10 == 0) //read data every 10 pixels = 30 bytes
{
if (x + 10 <= width) //read 10
{
readFile(fileHandle, data, 10 * 3); //10 pixels
}
else
{
readFile(fileHandle, data, (width - x) * 3 + rowpadding); //rest of line
}
}
//insert pixel, if within dynamic bitmap boundaries
if (x < buffer_width && ((height - y - 1) < buffer_height))
{
switch (format)
{
case Bitmap::RGB565:
buffer16[x + (height - y - 1) * buffer_width] =
LCD16bpp::getNativeColorFromRGB(data[(x % 10) * 3 + 2], data[(x % 10) * 3 + 1], data[(x % 10) * 3]);
break;
case Bitmap::RGB888:
{
//24 bit framebuffer
const uint32_t inx = 3 * (x + (height - y - 1) * buffer_width);
buffer8[inx + 0] = data[(x % 10) * 3 + 0];
buffer8[inx + 1] = data[(x % 10) * 3 + 1];
buffer8[inx + 2] = data[(x % 10) * 3 + 2];
break;
}
case Bitmap::ARGB8888:
{
//24 bit framebuffer
const uint32_t inx = 4 * (x + (height - y - 1) * buffer_width);
buffer8[inx + 0] = data[(x % 10) * 3 + 0];
buffer8[inx + 1] = data[(x % 10) * 3 + 1];
buffer8[inx + 2] = data[(x % 10) * 3 + 2];
buffer8[inx + 3] = 255; //solid
break;
}
default:
assert(!"Unsupported bitmap format in BMPFileLoader!");
}
}
}
}
}
このコードは実例を示すことを目的としています。 RGB888のもっと最適なリーダでは、ファイルからダイナミック・ビットマップ・メモリに直接読み取りできます(行のパディングをスキップすることを忘れないでください)。 上記のリーダは、BMPファイルから一時的なバッファに10ピクセルを読み取ります。 これらのピクセルは、正しいフォーマットに変換されながらビットマップにコピーされます。
ダイナミック・ビットマップ用のメモリ設定
ダイナミック・ビットマップを作成して使用する前に、TouchGFXを設定する必要があります。 まず、バッファとダイナミック・ビットマップの最大数を、設定する必要があります(シミュレータ用にも)。
次に示すSTM32F7xxの例では、外部RAMにバッファを割り当てています。ここでは、サイズが320x240の24ビットのビットマップを読み込んで表示しようとしています。 したがって、必要なメモリは320x240x3 = 230,400バイトになります。 さらにブックキーピング用の小さい領域も必要なので、バッファ用に232,000バイトを割り当てます。
FrontendApplication.cpp (extract)
#include <gui/common/FrontendApplication.hpp>
#include <touchgfx/Bitmap.hpp>
FrontendApplication::FrontendApplication(Model& m, FrontendHeap& heap)
: FrontendApplicationBase(m, heap)
{
static uint32_t bmpCache = (uint32_t)(0xC00C0000); // SDRAM
Bitmap::setCache((uint16_t*)bmpCache, 232000, 1);
}
最後の引数はダイナミック・ビットマップの最大数なので、各自のニーズに合わせてこれを調整します。 多くのプラットフォームでは、フレームバッファも外部RAMに保存されるので注意してください。 ビットマップ・キャッシュを重複するメモリ領域に配置しないでください。
Note
JPEGファイルの読込み
JPEGファイル・ローダの例はこちらにあります。ここには、LibJPEGを使用してJPEGファイルを使用する方法が示されています。 上記のBMPFileLoaderと同じように、JPEGLoaderクラスが使用されています。