diff --git a/radio/src/bmp.cpp b/radio/src/bmp.cpp index a20e07ab6..ffbe8efc8 100644 --- a/radio/src/bmp.cpp +++ b/radio/src/bmp.cpp @@ -44,7 +44,9 @@ const pm_char * bmpLoad(uint8_t *bmp, const char *filename, const unsigned int w uint8_t bmpBuf[LCD_W]; /* maximum with LCD_W */ uint8_t *buf = &bmpBuf[0]; - assert(width <= LCD_W); + if (width > LCD_W) { + return STR_INCOMPATIBLE; + } FRESULT result = f_open(&bmpFile, filename, FA_OPEN_EXISTING | FA_READ); if (result != FR_OK) { @@ -70,7 +72,7 @@ const pm_char * bmpLoad(uint8_t *bmp, const char *filename, const unsigned int w uint32_t fsize = *((uint32_t *)&buf[2]); uint32_t hsize = *((uint32_t *)&buf[10]); /* header size */ - uint32_t len = limit((uint32_t)0, (uint32_t)(hsize-14), (uint32_t)32); + uint32_t len = limit((uint32_t)4, (uint32_t)(hsize-14), (uint32_t)32); result = f_read(&bmpFile, buf, len, &read); if (result != FR_OK || read != len) { f_close(&bmpFile); @@ -152,16 +154,16 @@ const pm_char * bmpLoad(uint8_t *bmp, const char *filename, const unsigned int w *dest++ = w; *dest++ = h; - memset(dest, 0, w*((h+7)/8)*4); + memset(dest, 0, BITMAP_BUFFER_SIZE(w, h) - 2); - uint32_t n; + uint32_t rowSize; switch (depth) { case 1: - n = w/8; + rowSize = ((w+31)/32)*4; for (uint32_t i=0; i=0; i--) { - result = f_read(&bmpFile, buf, ((w+7)/8)*4, &read); - if (result != FR_OK || read != ((w+7)/8)*4) { + result = f_read(&bmpFile, buf, rowSize, &read); + if (result != FR_OK || read != rowSize) { f_close(&bmpFile); return SDCARD_ERROR(result); } - for (uint32_t j=0; j<(w+1)/2; j++) { + for (uint32_t j=0; j> 4) & 0x0F] << 4; *dst |= val ^ 0xF0; + if ((j+1)*2 >= w) break; val = palette[buf[j] & 0x0F] << 4; *(dst+1) |= val ^ 0xF0; } else { uint8_t val = palette[(buf[j] >> 4) & 0x0F]; *dst |= val ^ 0x0F; + if ((j+1)*2 >= w) break; val = palette[buf[j] & 0x0F]; *(dst+1) |= val ^ 0x0F; } diff --git a/radio/src/lua.cpp b/radio/src/lua.cpp index c74987ac1..8890e6392 100644 --- a/radio/src/lua.cpp +++ b/radio/src/lua.cpp @@ -450,7 +450,7 @@ static int luaLcdDrawPixmap(lua_State *L) int x = luaL_checkinteger(L, 1); int y = luaL_checkinteger(L, 2); const char * filename = luaL_checkstring(L, 3); - uint8_t bitmap[2+LCD_W/2*LCD_H/2]; // width max is LCD_W/2 pixels for saving stack and avoid a malloc here + uint8_t bitmap[BITMAP_BUFFER_SIZE(LCD_W/2, LCD_H)]; // width max is LCD_W/2 pixels for saving stack and avoid a malloc here const pm_char * error = bmpLoad(bitmap, filename, LCD_W/2, LCD_H); if (!error) { lcd_bmp(x, y, bitmap); diff --git a/radio/src/opentx.h b/radio/src/opentx.h index 44b85645e..f16de4763 100644 --- a/radio/src/opentx.h +++ b/radio/src/opentx.h @@ -439,9 +439,10 @@ enum PotType { #endif #if defined(PCBTARANIS) + #define BITMAP_BUFFER_SIZE(width, height) (2 + width * ((height+7)/8)*4) #define MODEL_BITMAP_WIDTH 64 #define MODEL_BITMAP_HEIGHT 32 - #define MODEL_BITMAP_SIZE (2+4*(MODEL_BITMAP_WIDTH*MODEL_BITMAP_HEIGHT/8)) + #define MODEL_BITMAP_SIZE BITMAP_BUFFER_SIZE(MODEL_BITMAP_WIDTH, MODEL_BITMAP_HEIGHT) extern uint8_t modelBitmap[MODEL_BITMAP_SIZE]; void loadModelBitmap(char *name, uint8_t *bitmap); #define LOAD_MODEL_BITMAP() loadModelBitmap(g_model.header.bitmap, modelBitmap) diff --git a/radio/src/targets/simu/simpgmspace.cpp b/radio/src/targets/simu/simpgmspace.cpp index a8758c0fc..b5e3648c3 100644 --- a/radio/src/targets/simu/simpgmspace.cpp +++ b/radio/src/targets/simu/simpgmspace.cpp @@ -520,7 +520,10 @@ FRESULT f_open (FIL * fil, const TCHAR *name, BYTE flag) FRESULT f_read (FIL* fil, void* data, UINT size, UINT* read) { - if (fil && fil->fs) *read = fread(data, 1, size, (FILE*)fil->fs); + if (fil && fil->fs) { + *read = fread(data, 1, size, (FILE*)fil->fs); + // TRACE("fread(%p) %u, %u", fil->fs, size, *read); + } return FR_OK; } diff --git a/radio/src/tests/1b_39x32.bmp b/radio/src/tests/1b_39x32.bmp new file mode 100644 index 000000000..1730b7bd5 Binary files /dev/null and b/radio/src/tests/1b_39x32.bmp differ diff --git a/radio/src/tests/1b_6x32.bmp b/radio/src/tests/1b_6x32.bmp new file mode 100644 index 000000000..f130b9986 Binary files /dev/null and b/radio/src/tests/1b_6x32.bmp differ diff --git a/radio/src/tests/4b_31x31.bmp b/radio/src/tests/4b_31x31.bmp new file mode 100644 index 000000000..f4fe798af Binary files /dev/null and b/radio/src/tests/4b_31x31.bmp differ diff --git a/radio/src/tests/4b_7x32.bmp b/radio/src/tests/4b_7x32.bmp new file mode 100644 index 000000000..5de76c4b6 Binary files /dev/null and b/radio/src/tests/4b_7x32.bmp differ diff --git a/radio/src/tests/lcd.cpp b/radio/src/tests/lcd.cpp index 884f2cf5a..47131b578 100644 --- a/radio/src/tests/lcd.cpp +++ b/radio/src/tests/lcd.cpp @@ -296,3 +296,74 @@ TEST(Lcd, lcd_vlineStip) } #endif +template class TestBuffer +{ +private: + uint8_t * buf; + uint32_t size; +public: + TestBuffer(uint32_t size) : buf(0), size(size) { + buf = new uint8_t[size + padding * 2]; + memset(buf, 0xA5, padding); + memset(buf+padding, 0x00, size); + memset(buf+padding+size, 0x5A, padding); + }; + ~TestBuffer() { if (buf) delete[] buf; }; + uint8_t * buffer() { return buf + padding; }; + void leakCheck() const { + uint8_t paddingCompareBuf[padding]; + memset(paddingCompareBuf, 0xA5, padding); + if (memcmp(buf, paddingCompareBuf, padding) != 0) { + ADD_FAILURE() << "buffer leaked low"; + }; + memset(paddingCompareBuf, 0x5A, padding); + if (memcmp(buf+padding+size, paddingCompareBuf, padding) != 0) { + ADD_FAILURE() << "buffer leaked high"; + } + }; +}; + +#if defined(PCBTARANIS) +TEST(Lcd, lcd_bmpLoadAndDisplay) +{ + lcd_clear(); + // Test proper BMP files, they should display correctly + { + TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(7, 32)); + EXPECT_EQ(bmpLoad(bitmap.buffer(), "./tests/4b_7x32.bmp", 7, 32), (char *)0); + bitmap.leakCheck(); + lcd_bmp(10, 2, bitmap.buffer()); + } + { + TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(6, 32)); + EXPECT_EQ(bmpLoad(bitmap.buffer(), "./tests/1b_6x32.bmp", 6, 32), (char *)0); + bitmap.leakCheck(); + lcd_bmp(20, 2, bitmap.buffer()); + } + { + TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(31, 31)); + EXPECT_EQ(bmpLoad(bitmap.buffer(), "./tests/4b_31x31.bmp", 31, 31), (char *)0); + bitmap.leakCheck(); + lcd_bmp(30, 2, bitmap.buffer()); + } + { + TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(39, 32)); + EXPECT_EQ(bmpLoad(bitmap.buffer(), "./tests/1b_39x32.bmp", 39, 32), (char *)0); + bitmap.leakCheck(); + lcd_bmp(70, 2, bitmap.buffer()); + } + EXPECT_TRUE(checkScreenshot("lcd_bmpLoadAndDisplay")); + + // Test various bad BMP files, they should not display + { + TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(LCD_W+1, 32)); + EXPECT_EQ(bmpLoad(bitmap.buffer(), "", LCD_W+1, 32), STR_INCOMPATIBLE) << "to wide"; + bitmap.leakCheck(); + } + { + TestBuffer<1000> bitmap(BITMAP_BUFFER_SIZE(10, 10)); + EXPECT_EQ(bmpLoad(bitmap.buffer(), "./tests/1b_39x32.bmp", 10, 10), STR_INCOMPATIBLE) << "to small buffer"; + bitmap.leakCheck(); + } +} +#endif diff --git a/radio/src/tests/lcd_bmpLoadAndDisplay_212x64.png b/radio/src/tests/lcd_bmpLoadAndDisplay_212x64.png new file mode 100644 index 000000000..70fab5d38 Binary files /dev/null and b/radio/src/tests/lcd_bmpLoadAndDisplay_212x64.png differ