在執行時間載入圖像
本節中,我們將討論從TouchGFX應用程式中的檔案系統載入圖像的方法。
在TouchGFX應用程式中使用點陣圖的標準程式是在應用程式中包含點陣圖。 PNG檔被轉換為.cpp檔中的二進位資料,並被編譯和連結到應用程式中。 這是一種在運行時使點陣圖對應用程式可用的簡單方法。
在某些應用中,這是不可能或不實際的。 例如,當圖像在編譯時不可用(可能稍後通過互聯網連接下載),或者當您的應用程式快閃記憶體空間中有太多點陣圖時。
對於這個問題,動態點陣圖(見以下連結) 是一個基於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 = 230400。 我們還需要一點空間用於記錄,因此為緩衝區分配了232000位元組。
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類。