2023-07-01 20:22:27 +00:00
|
|
|
#pragma once
|
|
|
|
#include <type_traits>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
struct ILcd
|
|
|
|
{
|
|
|
|
virtual void UpdateScreen() = 0;
|
|
|
|
};
|
|
|
|
|
2023-07-03 23:38:00 +00:00
|
|
|
template <unsigned short _SizeX, unsigned short _SizeY, unsigned char _LineHeight>
|
2023-07-01 20:22:27 +00:00
|
|
|
struct IBitmap
|
|
|
|
{
|
2023-07-03 23:38:00 +00:00
|
|
|
constexpr IBitmap(const unsigned char *_pBuffStart) : pBuffStart(_pBuffStart){};
|
2023-07-01 20:22:27 +00:00
|
|
|
static constexpr auto SizeX = _SizeX;
|
|
|
|
static constexpr auto SizeY = _SizeY;
|
|
|
|
static constexpr auto LineHeight = _LineHeight;
|
|
|
|
static constexpr auto Lines = _SizeX / _LineHeight;
|
|
|
|
static constexpr unsigned short GetCoursorPosition(unsigned char u8Line, unsigned char u8XPos)
|
|
|
|
{
|
|
|
|
return (u8Line * SizeX) + u8XPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool GetPixel(unsigned char u8X, unsigned char u8Y) const = 0;
|
|
|
|
virtual void SetPixel(unsigned char u8X, unsigned char u8Y) const = 0;
|
2023-07-03 23:38:00 +00:00
|
|
|
virtual void *GetCoursorData(unsigned short u16CoursorPosition) const { return nullptr; }
|
|
|
|
virtual void ClearAll() = 0;
|
|
|
|
const unsigned char *pBuffStart;
|
2023-07-01 20:22:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
struct IFont
|
|
|
|
{
|
|
|
|
virtual bool GetPixel(char c8Character, unsigned char u8X, unsigned char u8Y) const = 0;
|
2023-07-03 23:38:00 +00:00
|
|
|
virtual unsigned char *GetRaw(char c8Character) const = 0;
|
2023-07-01 20:22:27 +00:00
|
|
|
virtual unsigned char GetSizeX(char c8Character) const = 0;
|
|
|
|
virtual unsigned char GetSizeY(char c8Character) const = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
template <class BitmapType>
|
|
|
|
class CDisplay
|
|
|
|
{
|
2023-07-03 23:38:00 +00:00
|
|
|
public:
|
|
|
|
constexpr CDisplay(BitmapType &_Bitmap)
|
|
|
|
: Bitmap(_Bitmap), pCurrentFont(nullptr), u16CoursorPosition(0)
|
|
|
|
{
|
|
|
|
}
|
2023-07-01 20:22:27 +00:00
|
|
|
|
|
|
|
void SetCoursor(unsigned char u8Line, unsigned char u8X) const
|
|
|
|
{
|
|
|
|
u16CoursorPosition = (u8Line * Bitmap.SizeX) + u8X;
|
|
|
|
}
|
|
|
|
|
2023-07-03 23:38:00 +00:00
|
|
|
void SetFont(const IFont *pFont) const
|
2023-07-01 20:22:27 +00:00
|
|
|
{
|
|
|
|
pCurrentFont = pFont;
|
|
|
|
}
|
|
|
|
|
2023-07-03 23:38:00 +00:00
|
|
|
void DrawLine(int sx, int ex, int ny)
|
|
|
|
{
|
|
|
|
for (int i = sx; i <= ex; i++)
|
|
|
|
{
|
|
|
|
if (i < Bitmap.SizeX && ny < Bitmap.SizeY)
|
|
|
|
{
|
|
|
|
Bitmap.SetPixel(i, ny);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-05 18:34:09 +00:00
|
|
|
void DrawHLine(int sy, int ey, int nx, bool bCropped = false)
|
|
|
|
{
|
|
|
|
for (int i = sy; i <= ey; i++)
|
|
|
|
{
|
|
|
|
if (i < Bitmap.SizeY && nx < Bitmap.SizeX && (!bCropped || i % 2))
|
|
|
|
{
|
|
|
|
Bitmap.SetPixel(nx, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-03 23:38:00 +00:00
|
|
|
void DrawCircle(unsigned char cx, unsigned char cy, unsigned int r, bool bFilled = false)
|
|
|
|
{
|
|
|
|
int x = 0;
|
|
|
|
int y = r;
|
|
|
|
int d = 3 - 2 * r;
|
|
|
|
if (!r)
|
|
|
|
return;
|
|
|
|
|
|
|
|
while (y >= x)
|
|
|
|
{
|
|
|
|
// when bFilled is true, draw lines to fill the circle
|
|
|
|
if (bFilled)
|
|
|
|
{
|
|
|
|
DrawLine(cx - x, cx + x, cy - y);
|
|
|
|
DrawLine(cx - y, cx + y, cy - x);
|
|
|
|
DrawLine(cx - x, cx + x, cy + y);
|
|
|
|
DrawLine(cx - y, cx + y, cy + x);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (cx + x < Bitmap.SizeX && cy + y < Bitmap.SizeY)
|
|
|
|
Bitmap.SetPixel(cx + x, cy - y); // 1st quarter
|
|
|
|
if (cx + y < Bitmap.SizeX && cy + x < Bitmap.SizeY)
|
|
|
|
Bitmap.SetPixel(cx + y, cy - x); // 2nd quarter
|
|
|
|
if (cx - x >= 0 && cy + y < Bitmap.SizeY)
|
|
|
|
Bitmap.SetPixel(cx - x, cy - y); // 3rd quarter
|
|
|
|
if (cx - y >= 0 && cy + x < Bitmap.SizeY)
|
|
|
|
Bitmap.SetPixel(cx - y, cy - x); // 4th quarter
|
|
|
|
if (cx + x < Bitmap.SizeX && cy + y < Bitmap.SizeY)
|
|
|
|
Bitmap.SetPixel(cx + x, cy + y); // 5th quarter
|
|
|
|
if (cx + y < Bitmap.SizeX && cy - x >= 0)
|
|
|
|
Bitmap.SetPixel(cx + y, cy + x); // 6th quarter
|
|
|
|
if (cx - x >= 0 && cy - y >= 0)
|
|
|
|
Bitmap.SetPixel(cx - x, cy + y); // 7th quarter
|
|
|
|
if (cx - y >= 0 && cy - x >= 0)
|
|
|
|
Bitmap.SetPixel(cx - y, cy + x); // 8th quarter
|
|
|
|
}
|
|
|
|
x++;
|
|
|
|
if (d < 0)
|
|
|
|
d += 4 * x + 6;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
y--;
|
|
|
|
d += 4 * (x - y) + 10;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void DrawRectangle(unsigned char sx, unsigned char sy, unsigned int width, unsigned int height, bool bFilled)
|
|
|
|
{
|
|
|
|
// Draw vertical lines
|
|
|
|
for (unsigned int y = sy; y < sy + height; y++)
|
|
|
|
{
|
|
|
|
if (y < Bitmap.SizeY)
|
|
|
|
{
|
|
|
|
Bitmap.SetPixel(sx, y);
|
|
|
|
Bitmap.SetPixel(sx + width - 1, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw horizontal lines
|
|
|
|
for (unsigned int x = sx; x < sx + width; x++)
|
|
|
|
{
|
|
|
|
if (x < Bitmap.SizeX)
|
|
|
|
{
|
|
|
|
Bitmap.SetPixel(x, sy);
|
|
|
|
Bitmap.SetPixel(x, sy + height - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If filled, draw horizontal lines within the rectangle
|
|
|
|
if (bFilled)
|
|
|
|
{
|
|
|
|
for (unsigned int x = sx + 1; x < sx + width - 1; x++)
|
|
|
|
{
|
|
|
|
if (x < Bitmap.SizeX)
|
|
|
|
{
|
|
|
|
for (unsigned int y = sy + 1; y < sy + height - 1; y++)
|
|
|
|
{
|
|
|
|
if (y < Bitmap.SizeY)
|
|
|
|
{
|
|
|
|
Bitmap.SetPixel(x, y);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-01 20:22:27 +00:00
|
|
|
unsigned char PrintCharacter(const char c8Character) const
|
|
|
|
{
|
2023-07-03 23:38:00 +00:00
|
|
|
if (!pCurrentFont)
|
2023-07-01 20:22:27 +00:00
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-07-03 23:38:00 +00:00
|
|
|
const auto *const pFontRawData = pCurrentFont->GetRaw(c8Character);
|
|
|
|
auto *pCoursorPosition = Bitmap.GetCoursorData(u16CoursorPosition);
|
2023-07-01 20:22:27 +00:00
|
|
|
auto const CopySize = pCurrentFont->GetSizeY(c8Character) * (BitmapType::LineHeight / 8);
|
2023-07-03 23:38:00 +00:00
|
|
|
if (pCoursorPosition && !(BitmapType::LineHeight % 8))
|
2023-07-01 20:22:27 +00:00
|
|
|
{
|
2023-07-03 23:38:00 +00:00
|
|
|
if (pFontRawData)
|
2023-07-01 20:22:27 +00:00
|
|
|
memcpy(pCoursorPosition, pFontRawData, CopySize);
|
|
|
|
else
|
|
|
|
memset(pCoursorPosition, 0, CopySize);
|
|
|
|
}
|
2023-07-03 23:38:00 +00:00
|
|
|
|
2023-07-01 20:22:27 +00:00
|
|
|
u16CoursorPosition += pCurrentFont->GetSizeY(c8Character);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-07-03 23:38:00 +00:00
|
|
|
void Print(const char *C8String) const
|
2023-07-01 20:22:27 +00:00
|
|
|
{
|
2023-07-03 23:38:00 +00:00
|
|
|
for (unsigned char i = 0; i < strlen(C8String); i++)
|
2023-07-01 20:22:27 +00:00
|
|
|
{
|
|
|
|
PrintCharacter(C8String[i]);
|
2023-07-03 23:38:00 +00:00
|
|
|
}
|
2023-07-01 20:22:27 +00:00
|
|
|
}
|
|
|
|
|
2023-07-04 20:32:40 +00:00
|
|
|
unsigned char PrintFixedDigtsNumer(int s32Number, unsigned char u8Digts)
|
|
|
|
{
|
|
|
|
char U8NumBuff[32];
|
|
|
|
memset(U8NumBuff, 0, sizeof(U8NumBuff));
|
|
|
|
|
2023-07-05 18:34:09 +00:00
|
|
|
char *pString = U8NumBuff + u8Digts;
|
2023-07-06 18:25:23 +00:00
|
|
|
*pString = '\0';
|
2023-07-05 18:34:09 +00:00
|
|
|
|
|
|
|
if (s32Number < 0)
|
2023-07-04 20:32:40 +00:00
|
|
|
{
|
2023-07-05 18:34:09 +00:00
|
|
|
U8NumBuff[0] = '-';
|
2023-07-06 18:25:23 +00:00
|
|
|
s32Number = -s32Number;
|
2023-07-04 20:32:40 +00:00
|
|
|
}
|
|
|
|
|
2023-07-05 18:34:09 +00:00
|
|
|
while (u8Digts--)
|
2023-07-04 20:32:40 +00:00
|
|
|
{
|
2023-07-06 18:25:23 +00:00
|
|
|
*--pString = '0' + (s32Number % 10);
|
|
|
|
s32Number /= 10;
|
2023-07-04 20:32:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Print(U8NumBuff);
|
|
|
|
return u8Digts * pCurrentFont->GetSizeX('0');
|
|
|
|
}
|
|
|
|
|
2023-07-06 18:25:23 +00:00
|
|
|
static constexpr int powersOfTen[9] = {
|
|
|
|
1, // 10^0
|
|
|
|
10, // 10^1
|
|
|
|
100, // 10^2
|
|
|
|
1000, // 10^3
|
|
|
|
10000, // 10^4
|
|
|
|
100000, // 10^5
|
|
|
|
1000000, // 10^6
|
|
|
|
10000000, // 10^7
|
|
|
|
100000000 // 10^8
|
|
|
|
};
|
2023-07-07 23:34:11 +00:00
|
|
|
void PrintFixedDigitsNumber2(int s32Number, unsigned char u8DigsToCut = 2)
|
2023-07-06 18:25:23 +00:00
|
|
|
{
|
|
|
|
char U8NumBuff[11] = {0}; // 9 digits, sign, and null terminator
|
|
|
|
int startIdx = 0;
|
|
|
|
bool isNegative = false;
|
|
|
|
|
|
|
|
if (s32Number < 0)
|
|
|
|
{
|
|
|
|
U8NumBuff[0] = '-';
|
|
|
|
s32Number = -s32Number;
|
|
|
|
isNegative = true;
|
|
|
|
}
|
|
|
|
|
2023-07-07 23:34:11 +00:00
|
|
|
for (int i = 8; i >= u8DigsToCut; --i) // assuming powersOfTen is an array of powers of 10
|
2023-07-06 18:25:23 +00:00
|
|
|
{
|
|
|
|
int digit = 0;
|
|
|
|
while (s32Number >= powersOfTen[i])
|
|
|
|
{
|
|
|
|
s32Number -= powersOfTen[i];
|
|
|
|
++digit;
|
|
|
|
}
|
|
|
|
U8NumBuff[isNegative + (8 - i)] = '0' + digit;
|
|
|
|
|
|
|
|
// We found the first non-zero digit
|
|
|
|
if (digit != 0 && startIdx == (isNegative ? 1 : 0))
|
|
|
|
startIdx = isNegative + (8 - i);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the number was 0, we write a single 0.
|
|
|
|
if (startIdx == (isNegative ? 1 : 0))
|
|
|
|
U8NumBuff[isNegative] = '0';
|
|
|
|
|
|
|
|
// Print the string from the start index
|
|
|
|
Print(U8NumBuff + startIdx);
|
|
|
|
}
|
|
|
|
|
2023-07-03 23:38:00 +00:00
|
|
|
private:
|
|
|
|
const BitmapType &Bitmap;
|
|
|
|
mutable const IFont *pCurrentFont;
|
2023-07-01 20:22:27 +00:00
|
|
|
mutable unsigned short u16CoursorPosition;
|
|
|
|
};
|