diff --git a/CMakeLists.txt b/CMakeLists.txt index d908267..93c8de1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,7 +65,8 @@ set(CMAKE_C_VISIBILITY_PRESET hidden) set(DMDUTIL_SOURCES src/Config.cpp src/DMD.cpp - src/VirtualDMD.cpp + src/LevelDMD.cpp + src/RGB24DMD.cpp src/Logger.cpp src/AlphaNumeric.cpp src/FrameUtil.cpp diff --git a/include/DMDUtil/DMD.h b/include/DMDUtil/DMD.h index afff259..90af35b 100644 --- a/include/DMDUtil/DMD.h +++ b/include/DMDUtil/DMD.h @@ -8,9 +8,15 @@ #define DMDUTILCALLBACK #endif +#define DMDUTIL_FRAME_BUFFER_SIZE 16 +#define DMDUTIL_MAX_NAME_SIZE 32 + +#include +#include #include #include #include +#include #include #include @@ -47,89 +53,103 @@ enum class AlphaNumericLayout class AlphaNumeric; class Serum; class PixelcadeDMD; -class VirtualDMD; +class LevelDMD; +class RGB24DMD; class DMDUTILAPI DMD { public: - DMD(int width, int height, bool sam = false, const char* name = nullptr); + DMD(); ~DMD(); + void FindDisplays(); static bool IsFinding(); bool HasDisplay() const; - int GetWidth() const { return m_width; } - int GetHeight() const { return m_height; } - int GetLength() const { return m_length; } - VirtualDMD* CreateVirtualDMD(); - bool DestroyVirtualDMD(VirtualDMD* pVirtualDMD); - void UpdateData(const uint8_t* pData, int depth, uint8_t r, uint8_t g, uint8_t b); - void UpdateRGB24Data(const uint8_t* pData, int depth, uint8_t r, uint8_t g, uint8_t b); + void DumpDMDTxt(); + void DumpDMDRaw(); + LevelDMD* CreateLevelDMD(uint16_t width, uint16_t height, bool sam); + bool DestroyLevelDMD(LevelDMD* pLevelDMD); + RGB24DMD* CreateRGB24DMD(uint16_t width, uint16_t height); + bool DestroyRGB24DMD(RGB24DMD* pRGB24DMD); + void UpdateData(const uint8_t* pData, int depth, uint16_t width, uint16_t height, uint8_t r, uint8_t g, uint8_t b, + const char* name = nullptr); + void UpdateRGB24Data(const uint8_t* pData, int depth, uint16_t width, uint16_t height, uint8_t r, uint8_t g, + uint8_t b); + void UpdateRGB24Data(const uint8_t* pData, uint16_t width, uint16_t height); + void UpdateRGB16Data(const uint16_t* pData, uint16_t width, uint16_t height); void UpdateAlphaNumericData(AlphaNumericLayout layout, const uint16_t* pData1, const uint16_t* pData2, uint8_t r, - uint8_t g, uint8_t b); + uint8_t g, uint8_t b, const char* name = nullptr); private: - enum class DmdMode + enum class DMDMode { Unknown, Data, - RGB24, + RGB24, // RGB888 + RGB16, // RGB565 AlphaNumeric }; struct DMDUpdate { - DmdMode mode; + DMDMode mode; AlphaNumericLayout layout; int depth; - void* pData; - void* pData2; + uint8_t data[256 * 64 * 3]; + uint16_t segData[256 * 64]; // RGB16 or segment data + uint16_t segData2[128]; + bool hasData; + bool hasSegData; + bool hasSegData2; uint8_t r; uint8_t g; uint8_t b; + uint16_t width; + uint16_t height; + char name[DMDUTIL_MAX_NAME_SIZE]; }; - static constexpr uint8_t LEVELS_WPC[] = {0x14, 0x21, 0x43, 0x64}; - static constexpr uint8_t LEVELS_GTS3[] = {0x00, 0x1E, 0x23, 0x28, 0x2D, 0x32, 0x37, 0x3C, - 0x41, 0x46, 0x4B, 0x50, 0x55, 0x5A, 0x5F, 0x64}; - static constexpr uint8_t LEVELS_SAM[] = {0x00, 0x14, 0x19, 0x1E, 0x23, 0x28, 0x2D, 0x32, - 0x37, 0x3C, 0x41, 0x46, 0x4B, 0x50, 0x5A, 0x64}; - - void FindDevices(); - void Run(); - void Stop(); - bool UpdatePalette(const DMDUpdate* pUpdate); - void UpdateData(const DMDUpdate* pUpdate, bool update); - void UpdateRGB24Data(const DMDUpdate* pUpdate, bool update); - void UpdateAlphaNumericData(const DMDUpdate* pUpdate, bool update); - - int m_width; - int m_height; - int m_length; - bool m_sam; - uint8_t* m_pBuffer; - uint8_t* m_pRGB24Buffer; - uint16_t m_segData1[128]; - uint16_t m_segData2[128]; - uint8_t* m_pLevelData; - uint8_t* m_pRGB24Data; - uint16_t* m_pRGB565Data; - uint8_t m_palette[192]; + DMDUpdate* m_updateBuffer[DMDUTIL_FRAME_BUFFER_SIZE]; + + bool UpdatePalette(uint8_t* pPalette, uint8_t depth, uint8_t r, uint8_t g, uint8_t b); + void UpdateData(const uint8_t* pData, int depth, uint16_t width, uint16_t height, uint8_t r, uint8_t g, uint8_t b, + DMDMode node, const char* name); + void AdjustRGB24Depth(uint8_t* pData, uint8_t* pDstData, int length, uint8_t* palette, uint8_t depth); + + void DmdFrameReadyResetThread(); + void LevelDMDThread(); + void RGB24DMDThread(); + void ZeDMDThread(); + void DumpDMDTxtThread(); + void DumpDMDRawThread(); + + uint8_t m_updateBufferPosition = 0; AlphaNumeric* m_pAlphaNumeric; Serum* m_pSerum; ZeDMD* m_pZeDMD; + std::vector m_levelDMDs; + std::vector m_rgb24DMDs; + + std::thread* m_pLevelDMDThread; + std::thread* m_pRGB24DMDThread; + std::thread* m_pZeDMDThread; + std::thread* m_pdmdFrameReadyResetThread; + std::thread* m_pDumpDMDTxtThread; + std::thread* m_pDumpDMDRawThread; + std::shared_mutex m_dmdSharedMutex; + std::condition_variable_any m_dmdCV; + std::atomic m_dmdFrameReady = false; + std::atomic m_stopFlag = false; + + static bool m_finding; + #if !( \ (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ defined(__ANDROID__)) + void PixelcadeDMDThread(); PixelcadeDMD* m_pPixelcadeDMD; + std::thread* m_pPixelcadeDMDThread; #endif - std::vector m_virtualDMDs; - - std::thread* m_pThread; - std::queue m_updates; - std::mutex m_mutex; - bool m_running; - - static bool m_finding; }; } // namespace DMDUtil diff --git a/include/DMDUtil/DMDUtil.h b/include/DMDUtil/DMDUtil.h index 3a27514..3000448 100644 --- a/include/DMDUtil/DMDUtil.h +++ b/include/DMDUtil/DMDUtil.h @@ -1,7 +1,7 @@ #pragma once #define DMDUTIL_VERSION_MAJOR 0 // X Digits -#define DMDUTIL_VERSION_MINOR 2 // Max 2 Digits +#define DMDUTIL_VERSION_MINOR 3 // Max 2 Digits #define DMDUTIL_VERSION_PATCH 0 // Max 2 Digits #define _DMDUTIL_STR(x) #x @@ -13,4 +13,5 @@ #include "Config.h" #include "DMD.h" -#include "VirtualDMD.h" \ No newline at end of file +#include "LevelDMD.h" +#include "RGB24DMD.h" diff --git a/include/DMDUtil/LevelDMD.h b/include/DMDUtil/LevelDMD.h new file mode 100644 index 0000000..e83289c --- /dev/null +++ b/include/DMDUtil/LevelDMD.h @@ -0,0 +1,46 @@ +#pragma once + +#ifdef _MSC_VER +#define DMDUTILAPI __declspec(dllexport) +#define DMDUTILCALLBACK __stdcall +#else +#define DMDUTILAPI __attribute__((visibility("default"))) +#define DMDUTILCALLBACK +#endif + +#include + +namespace DMDUtil +{ + +class DMDUTILAPI LevelDMD +{ + public: + LevelDMD(uint16_t width, uint16_t height, bool sam); + ~LevelDMD(); + + void Update(uint8_t* pLevelData, uint8_t depth); + int GetWidth() { return m_width; } + int GetHeight() { return m_height; } + int GetLength() const { return m_length; } + int GetPitch() const { return m_pitch; } + uint8_t* GetData(); + + private: + static constexpr uint8_t LEVELS_WPC[] = {0x14, 0x21, 0x43, 0x64}; + static constexpr uint8_t LEVELS_GTS3[] = {0x00, 0x1E, 0x23, 0x28, 0x2D, 0x32, 0x37, 0x3C, + 0x41, 0x46, 0x4B, 0x50, 0x55, 0x5A, 0x5F, 0x64}; + static constexpr uint8_t LEVELS_SAM[] = {0x00, 0x14, 0x19, 0x1E, 0x23, 0x28, 0x2D, 0x32, + 0x37, 0x3C, 0x41, 0x46, 0x4B, 0x50, 0x5A, 0x64}; + + uint16_t m_width; + uint16_t m_height; + int m_length; + int m_pitch; + int m_update; + bool m_sam; + + uint8_t* m_pData; +}; + +} // namespace DMDUtil \ No newline at end of file diff --git a/include/DMDUtil/VirtualDMD.h b/include/DMDUtil/RGB24DMD.h similarity index 61% rename from include/DMDUtil/VirtualDMD.h rename to include/DMDUtil/RGB24DMD.h index 7d1c349..34d0d3a 100644 --- a/include/DMDUtil/VirtualDMD.h +++ b/include/DMDUtil/RGB24DMD.h @@ -13,29 +13,27 @@ namespace DMDUtil { -class DMDUTILAPI VirtualDMD +class DMDUTILAPI RGB24DMD { public: - VirtualDMD(int width, int height); - ~VirtualDMD(); + RGB24DMD(uint16_t width, uint16_t height); + ~RGB24DMD(); - void Update(uint8_t* pLevelData, uint8_t* pRGB24Data); + void Update(uint8_t* pRGB24Data); int GetWidth() { return m_width; } int GetHeight() { return m_height; } int GetLength() const { return m_length; } int GetPitch() const { return m_pitch; } - uint8_t* GetLevelData(); - uint8_t* GetRGB24Data(); + uint8_t* GetData(); private: - int m_width; - int m_height; + uint16_t m_width; + uint16_t m_height; int m_length; int m_pitch; int m_update; - uint8_t* m_pLevelData; - uint8_t* m_pRGB24Data; + uint8_t* m_pData; }; } // namespace DMDUtil \ No newline at end of file diff --git a/platforms/android/arm64-v8a/external.sh b/platforms/android/arm64-v8a/external.sh index 6c1431f..ff90dd3 100755 --- a/platforms/android/arm64-v8a/external.sh +++ b/platforms/android/arm64-v8a/external.sh @@ -2,7 +2,7 @@ set -e -LIBZEDMD_SHA=b2190a3efaa52d705c9a5c62f00b418376a2604d +LIBZEDMD_SHA=7d2b0fc39475940b61b0126f3ff308dd193fe2a8 LIBSERUM_SHA=b69d2b436bc93570a2e7e78d0946cd3c43f7aed5 if [[ $(uname) == "Linux" ]]; then diff --git a/platforms/ios/arm64/external.sh b/platforms/ios/arm64/external.sh index 1f9dd22..9f913ac 100755 --- a/platforms/ios/arm64/external.sh +++ b/platforms/ios/arm64/external.sh @@ -2,7 +2,7 @@ set -e -LIBZEDMD_SHA=b2190a3efaa52d705c9a5c62f00b418376a2604d +LIBZEDMD_SHA=7d2b0fc39475940b61b0126f3ff308dd193fe2a8 LIBSERUM_SHA=b69d2b436bc93570a2e7e78d0946cd3c43f7aed5 NUM_PROCS=$(sysctl -n hw.ncpu) diff --git a/platforms/linux/aarch64/external.sh b/platforms/linux/aarch64/external.sh index 9525f1d..4248a39 100755 --- a/platforms/linux/aarch64/external.sh +++ b/platforms/linux/aarch64/external.sh @@ -2,7 +2,7 @@ set -e -LIBZEDMD_SHA=b2190a3efaa52d705c9a5c62f00b418376a2604d +LIBZEDMD_SHA=7d2b0fc39475940b61b0126f3ff308dd193fe2a8 LIBSERUM_SHA=b69d2b436bc93570a2e7e78d0946cd3c43f7aed5 NUM_PROCS=$(nproc) diff --git a/platforms/linux/x64/external.sh b/platforms/linux/x64/external.sh index 57598cb..54ec8d1 100755 --- a/platforms/linux/x64/external.sh +++ b/platforms/linux/x64/external.sh @@ -2,7 +2,7 @@ set -e -LIBZEDMD_SHA=b2190a3efaa52d705c9a5c62f00b418376a2604d +LIBZEDMD_SHA=7d2b0fc39475940b61b0126f3ff308dd193fe2a8 LIBSERUM_SHA=b69d2b436bc93570a2e7e78d0946cd3c43f7aed5 NUM_PROCS=$(nproc) diff --git a/platforms/macos/arm64/external.sh b/platforms/macos/arm64/external.sh index ff22747..31090d0 100755 --- a/platforms/macos/arm64/external.sh +++ b/platforms/macos/arm64/external.sh @@ -2,7 +2,7 @@ set -e -LIBZEDMD_SHA=b2190a3efaa52d705c9a5c62f00b418376a2604d +LIBZEDMD_SHA=7d2b0fc39475940b61b0126f3ff308dd193fe2a8 LIBSERUM_SHA=b69d2b436bc93570a2e7e78d0946cd3c43f7aed5 NUM_PROCS=$(sysctl -n hw.ncpu) diff --git a/platforms/macos/x64/external.sh b/platforms/macos/x64/external.sh index 5415fb4..b2d9c81 100755 --- a/platforms/macos/x64/external.sh +++ b/platforms/macos/x64/external.sh @@ -2,7 +2,7 @@ set -e -LIBZEDMD_SHA=b2190a3efaa52d705c9a5c62f00b418376a2604d +LIBZEDMD_SHA=7d2b0fc39475940b61b0126f3ff308dd193fe2a8 LIBSERUM_SHA=b69d2b436bc93570a2e7e78d0946cd3c43f7aed5 NUM_PROCS=$(sysctl -n hw.ncpu) diff --git a/platforms/tvos/arm64/external.sh b/platforms/tvos/arm64/external.sh index 64b02d5..7044c80 100755 --- a/platforms/tvos/arm64/external.sh +++ b/platforms/tvos/arm64/external.sh @@ -2,7 +2,7 @@ set -e -LIBZEDMD_SHA=b2190a3efaa52d705c9a5c62f00b418376a2604d +LIBZEDMD_SHA=7d2b0fc39475940b61b0126f3ff308dd193fe2a8 LIBSERUM_SHA=b69d2b436bc93570a2e7e78d0946cd3c43f7aed5 NUM_PROCS=$(sysctl -n hw.ncpu) diff --git a/platforms/win/x64/external.sh b/platforms/win/x64/external.sh index 9a3ae50..43fc219 100755 --- a/platforms/win/x64/external.sh +++ b/platforms/win/x64/external.sh @@ -2,7 +2,7 @@ set -e -LIBZEDMD_SHA=b2190a3efaa52d705c9a5c62f00b418376a2604d +LIBZEDMD_SHA=7d2b0fc39475940b61b0126f3ff308dd193fe2a8 LIBSERUM_SHA=b69d2b436bc93570a2e7e78d0946cd3c43f7aed5 echo "Building libraries..." diff --git a/platforms/win/x86/external.sh b/platforms/win/x86/external.sh index 7121e49..6143d91 100755 --- a/platforms/win/x86/external.sh +++ b/platforms/win/x86/external.sh @@ -2,7 +2,7 @@ set -e -LIBZEDMD_SHA=b2190a3efaa52d705c9a5c62f00b418376a2604d +LIBZEDMD_SHA=7d2b0fc39475940b61b0126f3ff308dd193fe2a8 LIBSERUM_SHA=b69d2b436bc93570a2e7e78d0946cd3c43f7aed5 echo "Building libraries..." diff --git a/src/AlphaNumeric.cpp b/src/AlphaNumeric.cpp index d153dce..d06d200 100644 --- a/src/AlphaNumeric.cpp +++ b/src/AlphaNumeric.cpp @@ -180,409 +180,411 @@ const uint8_t AlphaNumeric::Segs[8][17][5][2] = { }; // clang-format on -AlphaNumeric::AlphaNumeric() { memset(m_frameBuffer, 0, sizeof(m_frameBuffer)); } +AlphaNumeric::AlphaNumeric() {} -uint8_t* AlphaNumeric::Render(AlphaNumericLayout layout, const uint16_t* const seg_data) +void AlphaNumeric::Render(uint8_t* pFrame, AlphaNumericLayout layout, const uint16_t* const seg_data) { if (layout != AlphaNumericLayout::__2x7Num_2x7Num_10x1Num) - Render(layout, seg_data, nullptr); + Render(pFrame, layout, seg_data, nullptr); else - Render(layout, seg_data, seg_data + 32); - - return m_frameBuffer; + Render(pFrame, layout, seg_data, seg_data + 32); } -uint8_t* AlphaNumeric::Render(AlphaNumericLayout layout, const uint16_t* const seg_data, - const uint16_t* const seg_data2) +void AlphaNumeric::Render(uint8_t* pFrame, AlphaNumericLayout layout, const uint16_t* const seg_data, + const uint16_t* const seg_data2) { - Clear(); + Clear(pFrame); switch (layout) { case AlphaNumericLayout::__2x16Alpha: - Render2x16Alpha(seg_data); + Render2x16Alpha(pFrame, seg_data); break; case AlphaNumericLayout::__2x20Alpha: - Render2x20Alpha(seg_data); + Render2x20Alpha(pFrame, seg_data); break; case AlphaNumericLayout::__2x7Alpha_2x7Num: - Render2x7Alpha_2x7Num(seg_data); + Render2x7Alpha_2x7Num(pFrame, seg_data); break; case AlphaNumericLayout::__2x7Alpha_2x7Num_4x1Num: - Render2x7Alpha_2x7Num_4x1Num(seg_data); + Render2x7Alpha_2x7Num_4x1Num(pFrame, seg_data); break; case AlphaNumericLayout::__2x7Num_2x7Num_4x1Num: - Render2x7Num_2x7Num_4x1Num(seg_data); + Render2x7Num_2x7Num_4x1Num(pFrame, seg_data); break; case AlphaNumericLayout::__2x7Num_2x7Num_10x1Num: - Render2x7Num_2x7Num_10x1Num(seg_data, seg_data2); + Render2x7Num_2x7Num_10x1Num(pFrame, seg_data, seg_data2); break; case AlphaNumericLayout::__2x7Num_2x7Num_4x1Num_gen7: - Render2x7Num_2x7Num_4x1Num_gen7(seg_data); + Render2x7Num_2x7Num_4x1Num_gen7(pFrame, seg_data); break; case AlphaNumericLayout::__2x7Num10_2x7Num10_4x1Num: - Render2x7Num10_2x7Num10_4x1Num(seg_data); + Render2x7Num10_2x7Num10_4x1Num(pFrame, seg_data); break; case AlphaNumericLayout::__2x6Num_2x6Num_4x1Num: - Render2x6Num_2x6Num_4x1Num(seg_data); + Render2x6Num_2x6Num_4x1Num(pFrame, seg_data); break; case AlphaNumericLayout::__2x6Num10_2x6Num10_4x1Num: - Render2x6Num10_2x6Num10_4x1Num(seg_data); + Render2x6Num10_2x6Num10_4x1Num(pFrame, seg_data); break; case AlphaNumericLayout::__4x7Num10: - Render4x7Num10(seg_data); + Render4x7Num10(pFrame, seg_data); break; case AlphaNumericLayout::__6x4Num_4x1Num: - Render6x4Num_4x1Num(seg_data); + Render6x4Num_4x1Num(pFrame, seg_data); break; case AlphaNumericLayout::__2x7Num_4x1Num_1x16Alpha: - Render2x7Num_4x1Num_1x16Alpha(seg_data); + Render2x7Num_4x1Num_1x16Alpha(pFrame, seg_data); break; case AlphaNumericLayout::__1x16Alpha_1x16Num_1x7Num: - Render1x16Alpha_1x16Num_1x7Num(seg_data); + Render1x16Alpha_1x16Num_1x7Num(pFrame, seg_data); break; case AlphaNumericLayout::__1x7Num_1x16Alpha_1x16Num: - Render1x7Num_1x16Alpha_1x16Num(seg_data); + Render1x7Num_1x16Alpha_1x16Num(pFrame, seg_data); break; case AlphaNumericLayout::__1x16Alpha_1x16Num_1x7Num_1x4Num: - Render1x16Alpha_1x16Num_1x7Num_1x4Num(seg_data); + Render1x16Alpha_1x16Num_1x7Num_1x4Num(pFrame, seg_data); break; default: break; } - - return m_frameBuffer; } -void AlphaNumeric::SmoothDigitCorners(const int x, const int y) +void AlphaNumeric::SmoothDigitCorners(uint8_t* pFrame, const int x, const int y) { - if (GetPixel(x, 1 + y) && GetPixel(1 + x, y)) DrawPixel(0 + x, y, 0); - if (GetPixel(x + 6, 1 + y) && GetPixel(5 + x, y)) DrawPixel(6 + x, y, 0); - if (GetPixel(x, 9 + y) && GetPixel(1 + x, 10 + y)) DrawPixel(0 + x, 10 + y, 0); - if (GetPixel(x + 6, 9 + y) && GetPixel(5 + x, 10 + y)) DrawPixel(6 + x, 10 + y, 0); + if (GetPixel(pFrame, x, 1 + y) && GetPixel(pFrame, 1 + x, y)) DrawPixel(pFrame, 0 + x, y, 0); + if (GetPixel(pFrame, x + 6, 1 + y) && GetPixel(pFrame, 5 + x, y)) DrawPixel(pFrame, 6 + x, y, 0); + if (GetPixel(pFrame, x, 9 + y) && GetPixel(pFrame, 1 + x, 10 + y)) DrawPixel(pFrame, 0 + x, 10 + y, 0); + if (GetPixel(pFrame, x + 6, 9 + y) && GetPixel(pFrame, 5 + x, 10 + y)) DrawPixel(pFrame, 6 + x, 10 + y, 0); } -void AlphaNumeric::SmoothDigitCorners6Px(const int x, const int y) +void AlphaNumeric::SmoothDigitCorners6Px(uint8_t* pFrame, const int x, const int y) { - if (GetPixel(x, 1 + y) && GetPixel(1 + x, y)) DrawPixel(0 + x, y, 0); - if (GetPixel(x + 4, 1 + y) && GetPixel(3 + x, y)) DrawPixel(4 + x, y, 0); - if (GetPixel(x, 9 + y) && GetPixel(1 + x, 10 + y)) DrawPixel(0 + x, 10 + y, 0); - if (GetPixel(x + 4, 9 + y) && GetPixel(3 + x, 10 + y)) DrawPixel(4 + x, 10 + y, 0); + if (GetPixel(pFrame, x, 1 + y) && GetPixel(pFrame, 1 + x, y)) DrawPixel(pFrame, 0 + x, y, 0); + if (GetPixel(pFrame, x + 4, 1 + y) && GetPixel(pFrame, 3 + x, y)) DrawPixel(pFrame, 4 + x, y, 0); + if (GetPixel(pFrame, x, 9 + y) && GetPixel(pFrame, 1 + x, 10 + y)) DrawPixel(pFrame, 0 + x, 10 + y, 0); + if (GetPixel(pFrame, x + 4, 9 + y) && GetPixel(pFrame, 3 + x, 10 + y)) DrawPixel(pFrame, 4 + x, 10 + y, 0); } -void AlphaNumeric::DrawSegment(const int x, const int y, const uint8_t type, const uint16_t seg, const uint8_t colour) +void AlphaNumeric::DrawSegment(uint8_t* pFrame, const int x, const int y, const uint8_t type, const uint16_t seg, + const uint8_t colour) { - for (int i = 0; i < SegSizes[type][seg]; i++) DrawPixel(Segs[type][seg][i][0] + x, Segs[type][seg][i][1] + y, colour); + for (int i = 0; i < SegSizes[type][seg]; i++) + DrawPixel(pFrame, Segs[type][seg][i][0] + x, Segs[type][seg][i][1] + y, colour); } -bool AlphaNumeric::GetPixel(const int x, const int y) const { return m_frameBuffer[y * 128 + x] > 0; } +bool AlphaNumeric::GetPixel(uint8_t* pFrame, const int x, const int y) const { return pFrame[y * 128 + x] > 0; } -void AlphaNumeric::DrawPixel(const int x, const int y, const uint8_t colour) { m_frameBuffer[y * 128 + x] = colour; } +void AlphaNumeric::DrawPixel(uint8_t* pFrame, const int x, const int y, const uint8_t colour) +{ + pFrame[y * 128 + x] = colour; +} -void AlphaNumeric::Clear() { memset(m_frameBuffer, 0, sizeof(m_frameBuffer)); } +void AlphaNumeric::Clear(uint8_t* pFrame) { memset(pFrame, 0, sizeof(pFrame)); } -void AlphaNumeric::Render2x16Alpha(const uint16_t* const seg_data) +void AlphaNumeric::Render2x16Alpha(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { - if ((seg_data[i] >> j) & 0x1) DrawSegment(i * 8, 2, 0, j, 3); - if ((seg_data[i + 16] >> j) & 0x1) DrawSegment(i * 8, 19, 0, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, i * 8, 2, 0, j, 3); + if ((seg_data[i + 16] >> j) & 0x1) DrawSegment(pFrame, i * 8, 19, 0, j, 3); } - SmoothDigitCorners(i * 8, 2); - SmoothDigitCorners(i * 8, 19); + SmoothDigitCorners(pFrame, i * 8, 2); + SmoothDigitCorners(pFrame, i * 8, 19); } } -void AlphaNumeric::Render2x20Alpha(const uint16_t* const seg_data) +void AlphaNumeric::Render2x20Alpha(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 20; i++) { for (int j = 0; j < 16; j++) { - if ((seg_data[i] >> j) & 0x1) DrawSegment((i * 6) + 4, 2, 7, j, 3); - if ((seg_data[i + 20] >> j) & 0x1) DrawSegment((i * 6) + 4, 19, 7, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i * 6) + 4, 2, 7, j, 3); + if ((seg_data[i + 20] >> j) & 0x1) DrawSegment(pFrame, (i * 6) + 4, 19, 7, j, 3); } - SmoothDigitCorners6Px((i * 6) + 4, 2); - SmoothDigitCorners6Px((i * 6) + 4, 19); + SmoothDigitCorners6Px(pFrame, (i * 6) + 4, 2); + SmoothDigitCorners6Px(pFrame, (i * 6) + 4, 19); } } -void AlphaNumeric::Render2x7Alpha_2x7Num(const uint16_t* const seg_data) +void AlphaNumeric::Render2x7Alpha_2x7Num(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 14; i++) { for (int j = 0; j < 16; j++) { // 2x7 alphanumeric - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 2, 0, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 2, 0, j, 3); // 2x7 numeric - if ((seg_data[i + 14] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 19, 1, j, 3); + if ((seg_data[i + 14] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 19, 1, j, 3); } - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 2); - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 19); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 2); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 19); } } -void AlphaNumeric::Render2x7Alpha_2x7Num_4x1Num(const uint16_t* const seg_data) +void AlphaNumeric::Render2x7Alpha_2x7Num_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 14; i++) { for (int j = 0; j < 16; j++) { // 2x7 alphanumeric - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 0, 0, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 0, 0, j, 3); // 2x7 numeric - if ((seg_data[i + 14] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 21, 1, j, 3); + if ((seg_data[i + 14] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 21, 1, j, 3); } - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 0); - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 21); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 0); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 21); } // 4x1 numeric small for (int j = 0; j < 16; j++) { - if ((seg_data[28] >> j) & 0x1) DrawSegment(8, 12, 5, j, 3); - if ((seg_data[29] >> j) & 0x1) DrawSegment(16, 12, 5, j, 3); - if ((seg_data[30] >> j) & 0x1) DrawSegment(32, 12, 5, j, 3); - if ((seg_data[31] >> j) & 0x1) DrawSegment(40, 12, 5, j, 3); + if ((seg_data[28] >> j) & 0x1) DrawSegment(pFrame, 8, 12, 5, j, 3); + if ((seg_data[29] >> j) & 0x1) DrawSegment(pFrame, 16, 12, 5, j, 3); + if ((seg_data[30] >> j) & 0x1) DrawSegment(pFrame, 32, 12, 5, j, 3); + if ((seg_data[31] >> j) & 0x1) DrawSegment(pFrame, 40, 12, 5, j, 3); } } -void AlphaNumeric::Render2x6Num_2x6Num_4x1Num(const uint16_t* const seg_data) +void AlphaNumeric::Render2x6Num_2x6Num_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 12; i++) { for (int j = 0; j < 16; j++) { // 2x6 numeric - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 6) ? 0 : 4)) * 8, 0, 1, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 6) ? 0 : 4)) * 8, 0, 1, j, 3); // 2x6 numeric - if ((seg_data[i + 12] >> j) & 0x1) DrawSegment((i + ((i < 6) ? 0 : 4)) * 8, 12, 1, j, 3); + if ((seg_data[i + 12] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 6) ? 0 : 4)) * 8, 12, 1, j, 3); } - SmoothDigitCorners((i + ((i < 6) ? 0 : 4)) * 8, 0); - SmoothDigitCorners((i + ((i < 6) ? 0 : 4)) * 8, 12); + SmoothDigitCorners(pFrame, (i + ((i < 6) ? 0 : 4)) * 8, 0); + SmoothDigitCorners(pFrame, (i + ((i < 6) ? 0 : 4)) * 8, 12); } // 4x1 numeric small for (int j = 0; j < 16; j++) { - if ((seg_data[24] >> j) & 0x1) DrawSegment(8, 24, 5, j, 3); - if ((seg_data[25] >> j) & 0x1) DrawSegment(16, 24, 5, j, 3); - if ((seg_data[26] >> j) & 0x1) DrawSegment(32, 24, 5, j, 3); - if ((seg_data[27] >> j) & 0x1) DrawSegment(40, 24, 5, j, 3); + if ((seg_data[24] >> j) & 0x1) DrawSegment(pFrame, 8, 24, 5, j, 3); + if ((seg_data[25] >> j) & 0x1) DrawSegment(pFrame, 16, 24, 5, j, 3); + if ((seg_data[26] >> j) & 0x1) DrawSegment(pFrame, 32, 24, 5, j, 3); + if ((seg_data[27] >> j) & 0x1) DrawSegment(pFrame, 40, 24, 5, j, 3); } } -void AlphaNumeric::Render2x6Num10_2x6Num10_4x1Num(const uint16_t* const seg_data) +void AlphaNumeric::Render2x6Num10_2x6Num10_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 12; i++) { for (int j = 0; j < 16; j++) { // 2x6 numeric - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 6) ? 0 : 4)) * 8, 0, 2, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 6) ? 0 : 4)) * 8, 0, 2, j, 3); // 2x6 numeric - if ((seg_data[i + 12] >> j) & 0x1) DrawSegment((i + ((i < 6) ? 0 : 4)) * 8, 20, 2, j, 3); + if ((seg_data[i + 12] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 6) ? 0 : 4)) * 8, 20, 2, j, 3); } - SmoothDigitCorners((i + ((i < 6) ? 0 : 4)) * 8, 0); - SmoothDigitCorners((i + ((i < 6) ? 0 : 4)) * 8, 20); + SmoothDigitCorners(pFrame, (i + ((i < 6) ? 0 : 4)) * 8, 0); + SmoothDigitCorners(pFrame, (i + ((i < 6) ? 0 : 4)) * 8, 20); } // 4x1 numeric small for (int j = 0; j < 16; j++) { - if ((seg_data[24] >> j) & 0x1) DrawSegment(8, 12, 5, j, 3); - if ((seg_data[25] >> j) & 0x1) DrawSegment(16, 12, 5, j, 3); - if ((seg_data[26] >> j) & 0x1) DrawSegment(32, 12, 5, j, 3); - if ((seg_data[27] >> j) & 0x1) DrawSegment(40, 12, 5, j, 3); + if ((seg_data[24] >> j) & 0x1) DrawSegment(pFrame, 8, 12, 5, j, 3); + if ((seg_data[25] >> j) & 0x1) DrawSegment(pFrame, 16, 12, 5, j, 3); + if ((seg_data[26] >> j) & 0x1) DrawSegment(pFrame, 32, 12, 5, j, 3); + if ((seg_data[27] >> j) & 0x1) DrawSegment(pFrame, 40, 12, 5, j, 3); } } -void AlphaNumeric::Render2x7Num_2x7Num_4x1Num(const uint16_t* const seg_data) +void AlphaNumeric::Render2x7Num_2x7Num_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 14; i++) { for (int j = 0; j < 16; j++) { // 2x7 numeric - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 0, 1, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 0, 1, j, 3); // 2x7 numeric - if ((seg_data[i + 14] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 12, 1, j, 3); + if ((seg_data[i + 14] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 12, 1, j, 3); } - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 0); - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 12); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 0); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 12); } // 4x1 numeric small for (int j = 0; j < 16; j++) { - if ((seg_data[28] >> j) & 0x1) DrawSegment(16, 24, 5, j, 3); - if ((seg_data[29] >> j) & 0x1) DrawSegment(24, 24, 5, j, 3); - if ((seg_data[30] >> j) & 0x1) DrawSegment(40, 24, 5, j, 3); - if ((seg_data[31] >> j) & 0x1) DrawSegment(48, 24, 5, j, 3); + if ((seg_data[28] >> j) & 0x1) DrawSegment(pFrame, 16, 24, 5, j, 3); + if ((seg_data[29] >> j) & 0x1) DrawSegment(pFrame, 24, 24, 5, j, 3); + if ((seg_data[30] >> j) & 0x1) DrawSegment(pFrame, 40, 24, 5, j, 3); + if ((seg_data[31] >> j) & 0x1) DrawSegment(pFrame, 48, 24, 5, j, 3); } } -void AlphaNumeric::Render2x7Num_2x7Num_10x1Num(const uint16_t* const seg_data, const uint16_t* const extra_seg_data) +void AlphaNumeric::Render2x7Num_2x7Num_10x1Num(uint8_t* pFrame, const uint16_t* const seg_data, + const uint16_t* const extra_seg_data) { for (int i = 0; i < 14; i++) { for (int j = 0; j < 16; j++) { // 2x7 numeric - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 0, 1, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 0, 1, j, 3); // 2x7 numeric - if ((seg_data[i + 14] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 12, 1, j, 3); + if ((seg_data[i + 14] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 12, 1, j, 3); } - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 0); - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 12); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 0); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 12); } // 10x1 numeric small for (int j = 0; j < 16; j++) { - if ((seg_data[28] >> j) & 0x1) DrawSegment(16, 24, 5, j, 3); - if ((seg_data[29] >> j) & 0x1) DrawSegment(24, 24, 5, j, 3); - if ((seg_data[30] >> j) & 0x1) DrawSegment(40, 24, 5, j, 3); - if ((seg_data[31] >> j) & 0x1) DrawSegment(48, 24, 5, j, 3); - if ((extra_seg_data[0] >> j) & 0x1) DrawSegment(64, 24, 5, j, 3); - if ((extra_seg_data[1] >> j) & 0x1) DrawSegment(72, 24, 5, j, 3); - if ((extra_seg_data[2] >> j) & 0x1) DrawSegment(88, 24, 5, j, 3); - if ((extra_seg_data[3] >> j) & 0x1) DrawSegment(96, 24, 5, j, 3); - if ((extra_seg_data[4] >> j) & 0x1) DrawSegment(112, 24, 5, j, 3); - if ((extra_seg_data[5] >> j) & 0x1) DrawSegment(120, 24, 5, j, 3); + if ((seg_data[28] >> j) & 0x1) DrawSegment(pFrame, 16, 24, 5, j, 3); + if ((seg_data[29] >> j) & 0x1) DrawSegment(pFrame, 24, 24, 5, j, 3); + if ((seg_data[30] >> j) & 0x1) DrawSegment(pFrame, 40, 24, 5, j, 3); + if ((seg_data[31] >> j) & 0x1) DrawSegment(pFrame, 48, 24, 5, j, 3); + if ((extra_seg_data[0] >> j) & 0x1) DrawSegment(pFrame, 64, 24, 5, j, 3); + if ((extra_seg_data[1] >> j) & 0x1) DrawSegment(pFrame, 72, 24, 5, j, 3); + if ((extra_seg_data[2] >> j) & 0x1) DrawSegment(pFrame, 88, 24, 5, j, 3); + if ((extra_seg_data[3] >> j) & 0x1) DrawSegment(pFrame, 96, 24, 5, j, 3); + if ((extra_seg_data[4] >> j) & 0x1) DrawSegment(pFrame, 112, 24, 5, j, 3); + if ((extra_seg_data[5] >> j) & 0x1) DrawSegment(pFrame, 120, 24, 5, j, 3); } } -void AlphaNumeric::Render2x7Num_2x7Num_4x1Num_gen7(const uint16_t* const seg_data) +void AlphaNumeric::Render2x7Num_2x7Num_4x1Num_gen7(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 14; i++) { for (int j = 0; j < 16; j++) { // 2x7 numeric - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 21, 1, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 21, 1, j, 3); // 2x7 numeric - if ((seg_data[i + 14] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 1, 1, j, 3); + if ((seg_data[i + 14] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 1, 1, j, 3); } - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 21); - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 1); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 21); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 1); } // 4x1 numeric small for (int j = 0; j < 16; j++) { - if ((seg_data[28] >> j) & 0x1) DrawSegment(8, 13, 5, j, 3); - if ((seg_data[29] >> j) & 0x1) DrawSegment(16, 13, 5, j, 3); - if ((seg_data[30] >> j) & 0x1) DrawSegment(32, 13, 5, j, 3); - if ((seg_data[31] >> j) & 0x1) DrawSegment(40, 13, 5, j, 3); + if ((seg_data[28] >> j) & 0x1) DrawSegment(pFrame, 8, 13, 5, j, 3); + if ((seg_data[29] >> j) & 0x1) DrawSegment(pFrame, 16, 13, 5, j, 3); + if ((seg_data[30] >> j) & 0x1) DrawSegment(pFrame, 32, 13, 5, j, 3); + if ((seg_data[31] >> j) & 0x1) DrawSegment(pFrame, 40, 13, 5, j, 3); } } -void AlphaNumeric::Render2x7Num10_2x7Num10_4x1Num(const uint16_t* const seg_data) +void AlphaNumeric::Render2x7Num10_2x7Num10_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 14; i++) { for (int j = 0; j < 16; j++) { // 2x7 numeric - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 0, 2, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 0, 2, j, 3); // 2x7 numeric - if ((seg_data[i + 14] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 20, 2, j, 3); + if ((seg_data[i + 14] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 20, 2, j, 3); } - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 0); - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 20); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 0); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 20); } // 4x1 numeric small for (int j = 0; j < 16; j++) { - if ((seg_data[28] >> j) & 0x1) DrawSegment(8, 12, 5, j, 3); - if ((seg_data[29] >> j) & 0x1) DrawSegment(16, 12, 5, j, 3); - if ((seg_data[30] >> j) & 0x1) DrawSegment(32, 12, 5, j, 3); - if ((seg_data[31] >> j) & 0x1) DrawSegment(40, 12, 5, j, 3); + if ((seg_data[28] >> j) & 0x1) DrawSegment(pFrame, 8, 12, 5, j, 3); + if ((seg_data[29] >> j) & 0x1) DrawSegment(pFrame, 16, 12, 5, j, 3); + if ((seg_data[30] >> j) & 0x1) DrawSegment(pFrame, 32, 12, 5, j, 3); + if ((seg_data[31] >> j) & 0x1) DrawSegment(pFrame, 40, 12, 5, j, 3); } } -void AlphaNumeric::Render4x7Num10(const uint16_t* const seg_data) +void AlphaNumeric::Render4x7Num10(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 14; i++) { for (int j = 0; j < 16; j++) { // 2x7 numeric10 - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 1, 2, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 1, 2, j, 3); // 2x7 numeric10 - if ((seg_data[i + 14] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 13, 2, j, 3); + if ((seg_data[i + 14] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 13, 2, j, 3); } - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 1); - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 13); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 1); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 13); } } -void AlphaNumeric::Render6x4Num_4x1Num(const uint16_t* const seg_data) +void AlphaNumeric::Render6x4Num_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 8; i++) { for (int j = 0; j < 16; j++) { // 2x4 numeric - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 4) ? 0 : 2)) * 8, 1, 5, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 4) ? 0 : 2)) * 8, 1, 5, j, 3); // 2x4 numeric - if ((seg_data[i + 8] >> j) & 0x1) DrawSegment((i + ((i < 4) ? 0 : 2)) * 8, 9, 5, j, 3); + if ((seg_data[i + 8] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 4) ? 0 : 2)) * 8, 9, 5, j, 3); // 2x4 numeric - if ((seg_data[i + 16] >> j) & 0x1) DrawSegment((i + ((i < 4) ? 0 : 2)) * 8, 17, 5, j, 3); + if ((seg_data[i + 16] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 4) ? 0 : 2)) * 8, 17, 5, j, 3); } - SmoothDigitCorners((i + ((i < 4) ? 0 : 2)) * 8, 1); - SmoothDigitCorners((i + ((i < 4) ? 0 : 2)) * 8, 9); - SmoothDigitCorners((i + ((i < 4) ? 0 : 2)) * 8, 17); + SmoothDigitCorners(pFrame, (i + ((i < 4) ? 0 : 2)) * 8, 1); + SmoothDigitCorners(pFrame, (i + ((i < 4) ? 0 : 2)) * 8, 9); + SmoothDigitCorners(pFrame, (i + ((i < 4) ? 0 : 2)) * 8, 17); } // 4x1 numeric small for (int j = 0; j < 16; j++) { - if ((seg_data[24] >> j) & 0x1) DrawSegment(16, 25, 5, j, 3); - if ((seg_data[25] >> j) & 0x1) DrawSegment(24, 25, 5, j, 3); - if ((seg_data[26] >> j) & 0x1) DrawSegment(48, 25, 5, j, 3); - if ((seg_data[27] >> j) & 0x1) DrawSegment(56, 25, 5, j, 3); + if ((seg_data[24] >> j) & 0x1) DrawSegment(pFrame, 16, 25, 5, j, 3); + if ((seg_data[25] >> j) & 0x1) DrawSegment(pFrame, 24, 25, 5, j, 3); + if ((seg_data[26] >> j) & 0x1) DrawSegment(pFrame, 48, 25, 5, j, 3); + if ((seg_data[27] >> j) & 0x1) DrawSegment(pFrame, 56, 25, 5, j, 3); } } -void AlphaNumeric::Render2x7Num_4x1Num_1x16Alpha(const uint16_t* const seg_data) +void AlphaNumeric::Render2x7Num_4x1Num_1x16Alpha(uint8_t* pFrame, const uint16_t* const seg_data) { for (int i = 0; i < 14; i++) { for (int j = 0; j < 16; j++) { // 2x7 numeric - if ((seg_data[i] >> j) & 0x1) DrawSegment((i + ((i < 7) ? 0 : 2)) * 8, 0, 1, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 0, 1, j, 3); } - SmoothDigitCorners((i + ((i < 7) ? 0 : 2)) * 8, 0); + SmoothDigitCorners(pFrame, (i + ((i < 7) ? 0 : 2)) * 8, 0); } // 4x1 numeric small for (int j = 0; j < 16; j++) { - if ((seg_data[14] >> j) & 0x1) DrawSegment(16, 12, 5, j, 3); - if ((seg_data[15] >> j) & 0x1) DrawSegment(24, 12, 5, j, 3); - if ((seg_data[16] >> j) & 0x1) DrawSegment(40, 12, 5, j, 3); - if ((seg_data[17] >> j) & 0x1) DrawSegment(48, 12, 5, j, 3); + if ((seg_data[14] >> j) & 0x1) DrawSegment(pFrame, 16, 12, 5, j, 3); + if ((seg_data[15] >> j) & 0x1) DrawSegment(pFrame, 24, 12, 5, j, 3); + if ((seg_data[16] >> j) & 0x1) DrawSegment(pFrame, 40, 12, 5, j, 3); + if ((seg_data[17] >> j) & 0x1) DrawSegment(pFrame, 48, 12, 5, j, 3); } // 1x16 alphanumeric for (int i = 0; i < 12; i++) { for (int j = 0; j < 16; j++) { - if ((seg_data[i + 18] >> j) & 0x1) DrawSegment((i * 8) + 16, 21, 0, j, 3); + if ((seg_data[i + 18] >> j) & 0x1) DrawSegment(pFrame, (i * 8) + 16, 21, 0, j, 3); } - SmoothDigitCorners((i * 8) + 16, 21); + SmoothDigitCorners(pFrame, (i * 8) + 16, 21); } } -void AlphaNumeric::Render1x16Alpha_1x16Num_1x7Num(const uint16_t* const seg_data) +void AlphaNumeric::Render1x16Alpha_1x16Num_1x7Num(uint8_t* pFrame, const uint16_t* const seg_data) { // 1x16 alphanumeric for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { - if ((seg_data[i] >> j) & 0x1) DrawSegment((i * 8), 9, 0, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i * 8), 9, 0, j, 3); } - SmoothDigitCorners((i * 8), 9); + SmoothDigitCorners(pFrame, (i * 8), 9); } // 1x16 numeric @@ -590,9 +592,9 @@ void AlphaNumeric::Render1x16Alpha_1x16Num_1x7Num(const uint16_t* const seg_data { for (int j = 0; j < 16; j++) { - if ((seg_data[i + 16] >> j) & 0x1) DrawSegment((i * 8), 21, 1, j, 3); + if ((seg_data[i + 16] >> j) & 0x1) DrawSegment(pFrame, (i * 8), 21, 1, j, 3); } - SmoothDigitCorners((i * 8), 21); + SmoothDigitCorners(pFrame, (i * 8), 21); } // 1x7 numeric small @@ -600,67 +602,67 @@ void AlphaNumeric::Render1x16Alpha_1x16Num_1x7Num(const uint16_t* const seg_data { for (int j = 0; j < 16; j++) { - if ((seg_data[i + 32] >> j) & 0x1) DrawSegment((i * 8) + 68, 1, 5, j, 3); + if ((seg_data[i + 32] >> j) & 0x1) DrawSegment(pFrame, (i * 8) + 68, 1, 5, j, 3); } } } -void AlphaNumeric::Render1x7Num_1x16Alpha_1x16Num(const uint16_t* const seg_data) +void AlphaNumeric::Render1x7Num_1x16Alpha_1x16Num(uint8_t* pFrame, const uint16_t* const seg_data) { // 1x16 alphanumeric for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { - if ((seg_data[i + 8] >> j) & 0x1) DrawSegment((i * 8), 9, 0, j, 3); + if ((seg_data[i + 8] >> j) & 0x1) DrawSegment(pFrame, (i * 8), 9, 0, j, 3); } - SmoothDigitCorners((i * 8), 9); + SmoothDigitCorners(pFrame, (i * 8), 9); } // 1x16 numeric for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { - if ((seg_data[i + 24] >> j) & 0x1) DrawSegment((i * 8), 21, 1, j, 3); + if ((seg_data[i + 24] >> j) & 0x1) DrawSegment(pFrame, (i * 8), 21, 1, j, 3); } - SmoothDigitCorners((i * 8), 21); + SmoothDigitCorners(pFrame, (i * 8), 21); } // 1x7 numeric small for (int i = 0; i < 7; i++) { for (int j = 0; j < 16; j++) { - if ((seg_data[i + 1] >> j) & 0x1) DrawSegment((i * 8) + 68, 1, 5, j, 3); + if ((seg_data[i + 1] >> j) & 0x1) DrawSegment(pFrame, (i * 8) + 68, 1, 5, j, 3); } } } -void AlphaNumeric::Render1x16Alpha_1x16Num_1x7Num_1x4Num(const uint16_t* const seg_data) +void AlphaNumeric::Render1x16Alpha_1x16Num_1x7Num_1x4Num(uint8_t* pFrame, const uint16_t* const seg_data) { // 1x16 alphanumeric for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { - if ((seg_data[i + 11] >> j) & 0x1) DrawSegment((i * 8), 9, 0, j, 3); + if ((seg_data[i + 11] >> j) & 0x1) DrawSegment(pFrame, (i * 8), 9, 0, j, 3); } - SmoothDigitCorners((i * 8), 9); + SmoothDigitCorners(pFrame, (i * 8), 9); } // 1x16 numeric for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { - if ((seg_data[i + 27] >> j) & 0x1) DrawSegment((i * 8), 21, 1, j, 3); + if ((seg_data[i + 27] >> j) & 0x1) DrawSegment(pFrame, (i * 8), 21, 1, j, 3); } - SmoothDigitCorners((i * 8), 21); + SmoothDigitCorners(pFrame, (i * 8), 21); } // 1x4 numeric small for (int i = 0; i < 4; i++) { for (int j = 0; j < 16; j++) { - if ((seg_data[i + 7] >> j) & 0x1) DrawSegment((i * 8) + 4, 1, 5, j, 3); + if ((seg_data[i + 7] >> j) & 0x1) DrawSegment(pFrame, (i * 8) + 4, 1, 5, j, 3); } } // 1x7 numeric small @@ -668,9 +670,9 @@ void AlphaNumeric::Render1x16Alpha_1x16Num_1x7Num_1x4Num(const uint16_t* const s { for (int j = 0; j < 16; j++) { - if ((seg_data[i] >> j) & 0x1) DrawSegment((i * 8) + 68, 1, 5, j, 3); + if ((seg_data[i] >> j) & 0x1) DrawSegment(pFrame, (i * 8) + 68, 1, 5, j, 3); } } } -} // namespace DMDUtil \ No newline at end of file +} // namespace DMDUtil diff --git a/src/AlphaNumeric.h b/src/AlphaNumeric.h index 17ed3bd..c282daf 100644 --- a/src/AlphaNumeric.h +++ b/src/AlphaNumeric.h @@ -20,38 +20,39 @@ class AlphaNumeric AlphaNumeric(); ~AlphaNumeric() {} - uint8_t* Render(AlphaNumericLayout layout, const uint16_t* const seg_data); - uint8_t* Render(AlphaNumericLayout layout, const uint16_t* const seg_data, const uint16_t* const seg_data2); + void Render(uint8_t* pFrame, AlphaNumericLayout layout, const uint16_t* const seg_data); + void Render(uint8_t* pFrame, AlphaNumericLayout layout, const uint16_t* const seg_data, + const uint16_t* const seg_data2); private: - void SmoothDigitCorners(const int x, const int y); - void SmoothDigitCorners6Px(const int x, const int y); - void DrawSegment(const int x, const int y, const uint8_t type, const uint16_t seg, const uint8_t colour); - bool GetPixel(const int x, const int y) const; - void DrawPixel(const int x, const int y, const uint8_t colour); - void Clear(); - - void Render2x16Alpha(const uint16_t* const seg_data); - void Render2x20Alpha(const uint16_t* const seg_data); - void Render2x7Alpha_2x7Num(const uint16_t* const seg_data); - void Render2x7Alpha_2x7Num_4x1Num(const uint16_t* const seg_data); - void Render2x6Num_2x6Num_4x1Num(const uint16_t* const seg_data); - void Render2x6Num10_2x6Num10_4x1Num(const uint16_t* const seg_data); - void Render2x7Num_2x7Num_4x1Num(const uint16_t* const seg_data); - void Render2x7Num_2x7Num_10x1Num(const uint16_t* const seg_data, const uint16_t* const extra_seg_data); - void Render2x7Num_2x7Num_4x1Num_gen7(const uint16_t* const seg_data); - void Render2x7Num10_2x7Num10_4x1Num(const uint16_t* const seg_data); - void Render4x7Num10(const uint16_t* const seg_data); - void Render6x4Num_4x1Num(const uint16_t* const seg_data); - void Render2x7Num_4x1Num_1x16Alpha(const uint16_t* const seg_data); - void Render1x16Alpha_1x16Num_1x7Num(const uint16_t* const seg_data); - void Render1x7Num_1x16Alpha_1x16Num(const uint16_t* const seg_data); - void Render1x16Alpha_1x16Num_1x7Num_1x4Num(const uint16_t* const seg_data); - - uint8_t m_frameBuffer[4096]; + void SmoothDigitCorners(uint8_t* pFrame, const int x, const int y); + void SmoothDigitCorners6Px(uint8_t* pFrame, const int x, const int y); + void DrawSegment(uint8_t* pFrame, const int x, const int y, const uint8_t type, const uint16_t seg, + const uint8_t colour); + bool GetPixel(uint8_t* pFrame, const int x, const int y) const; + void DrawPixel(uint8_t* pFrame, const int x, const int y, const uint8_t colour); + void Clear(uint8_t* pFrame); + + void Render2x16Alpha(uint8_t* pFrame, const uint16_t* const seg_data); + void Render2x20Alpha(uint8_t* pFrame, const uint16_t* const seg_data); + void Render2x7Alpha_2x7Num(uint8_t* pFrame, const uint16_t* const seg_data); + void Render2x7Alpha_2x7Num_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data); + void Render2x6Num_2x6Num_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data); + void Render2x6Num10_2x6Num10_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data); + void Render2x7Num_2x7Num_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data); + void Render2x7Num_2x7Num_10x1Num(uint8_t* pFrame, const uint16_t* const seg_data, + const uint16_t* const extra_seg_data); + void Render2x7Num_2x7Num_4x1Num_gen7(uint8_t* pFrame, const uint16_t* const seg_data); + void Render2x7Num10_2x7Num10_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data); + void Render4x7Num10(uint8_t* pFrame, const uint16_t* const seg_data); + void Render6x4Num_4x1Num(uint8_t* pFrame, const uint16_t* const seg_data); + void Render2x7Num_4x1Num_1x16Alpha(uint8_t* pFrame, const uint16_t* const seg_data); + void Render1x16Alpha_1x16Num_1x7Num(uint8_t* pFrame, const uint16_t* const seg_data); + void Render1x7Num_1x16Alpha_1x16Num(uint8_t* pFrame, const uint16_t* const seg_data); + void Render1x16Alpha_1x16Num_1x7Num_1x4Num(uint8_t* pFrame, const uint16_t* const seg_data); static const uint8_t SegSizes[8][16]; static const uint8_t Segs[8][17][5][2]; }; -} // namespace DMDUtil \ No newline at end of file +} // namespace DMDUtil diff --git a/src/Config.cpp b/src/Config.cpp index 498ae62..b78a00d 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -27,4 +27,4 @@ Config::Config() m_logCallback = nullptr; } -} // namespace DMDUtil \ No newline at end of file +} // namespace DMDUtil diff --git a/src/DMD.cpp b/src/DMD.cpp index b0b10ab..5b705e3 100644 --- a/src/DMD.cpp +++ b/src/DMD.cpp @@ -1,7 +1,8 @@ #include "DMDUtil/DMD.h" #include "DMDUtil/Config.h" -#include "DMDUtil/VirtualDMD.h" +#include "DMDUtil/LevelDMD.h" +#include "DMDUtil/RGB24DMD.h" #if !( \ (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ @@ -10,6 +11,7 @@ #endif #include +#include #include #include "AlphaNumeric.h" @@ -31,66 +33,86 @@ void ZEDMDCALLBACK ZeDMDLogCallback(const char* format, va_list args, const void bool DMD::m_finding = false; -DMD::DMD(int width, int height, bool sam, const char* name) +DMD::DMD() { - m_width = width; - m_height = height; - m_length = width * height; - m_sam = sam; - m_pBuffer = (uint8_t*)malloc(m_length); - memset(m_pBuffer, 0, m_length); - m_pRGB24Buffer = (uint8_t*)malloc(m_length * 3); - memset(m_pRGB24Buffer, 0, m_length * 3); - memset(m_segData1, 0, 128 * sizeof(uint16_t)); - memset(m_segData2, 0, 128 * sizeof(uint16_t)); - m_pLevelData = (uint8_t*)malloc(m_length); - memset(m_pLevelData, 0, m_length); - m_pRGB24Data = (uint8_t*)malloc(m_length * 3); - memset(m_pRGB24Data, 0, m_length * 3); - m_pRGB565Data = (uint16_t*)malloc(m_length * sizeof(uint16_t)); - memset(m_pRGB565Data, 0, m_length * sizeof(uint16_t)); - memset(m_palette, 0, 192); + for (uint8_t i = 0; i < DMDUTIL_FRAME_BUFFER_SIZE; i++) + { + m_updateBuffer[i] = new DMDUpdate(); + } m_pAlphaNumeric = new AlphaNumeric(); - m_pSerum = (Config::GetInstance()->IsAltColor() && name != nullptr && name[0] != '\0') ? Serum::Load(name) : nullptr; + m_pSerum = nullptr; m_pZeDMD = nullptr; + m_pZeDMDThread = nullptr; + m_pLevelDMDThread = nullptr; + m_pRGB24DMDThread = nullptr; + m_pDumpDMDTxtThread = nullptr; + m_pDumpDMDRawThread = nullptr; #if !( \ (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ defined(__ANDROID__)) m_pPixelcadeDMD = nullptr; + m_pPixelcadeDMDThread = nullptr; #endif - m_pThread = nullptr; - m_running = false; - - FindDevices(); - Run(); + m_pdmdFrameReadyResetThread = new std::thread(&DMD::DmdFrameReadyResetThread, this); } DMD::~DMD() { - if (m_pThread) + std::unique_lock ul(m_dmdSharedMutex); + m_stopFlag = true; + ul.unlock(); + m_dmdCV.notify_all(); + + m_pdmdFrameReadyResetThread->join(); + delete m_pdmdFrameReadyResetThread; + m_pdmdFrameReadyResetThread = nullptr; + + if (m_pLevelDMDThread) { - m_running = false; + m_pLevelDMDThread->join(); + delete m_pLevelDMDThread; + m_pLevelDMDThread = nullptr; + } - m_pThread->join(); - delete m_pThread; - m_pThread = nullptr; + if (m_pRGB24DMDThread) + { + m_pRGB24DMDThread->join(); + delete m_pRGB24DMDThread; + m_pRGB24DMDThread = nullptr; } - while (!m_updates.empty()) + if (m_pZeDMDThread) { - DMDUpdate* const pUpdate = m_updates.front(); - m_updates.pop(); - free(pUpdate->pData); - free(pUpdate->pData2); - delete pUpdate; + m_pZeDMDThread->join(); + delete m_pZeDMDThread; + m_pZeDMDThread = nullptr; } - free(m_pBuffer); - free(m_pRGB24Buffer); - free(m_pLevelData); - free(m_pRGB24Data); - free(m_pRGB565Data); + if (m_pDumpDMDTxtThread) + { + m_pDumpDMDTxtThread->join(); + delete m_pDumpDMDTxtThread; + m_pDumpDMDTxtThread = nullptr; + } + + if (m_pDumpDMDRawThread) + { + m_pDumpDMDRawThread->join(); + delete m_pDumpDMDRawThread; + m_pDumpDMDRawThread = nullptr; + } + +#if !( \ + (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ + defined(__ANDROID__)) + if (m_pPixelcadeDMDThread) + { + m_pPixelcadeDMDThread->join(); + delete m_pPixelcadeDMDThread; + m_pPixelcadeDMDThread = nullptr; + } +#endif delete m_pAlphaNumeric; delete m_pSerum; delete m_pZeDMD; @@ -100,7 +122,8 @@ DMD::~DMD() delete m_pPixelcadeDMD; #endif - for (VirtualDMD* pVirtualDMD : m_virtualDMDs) delete pVirtualDMD; + for (LevelDMD* pLevelDMD : m_levelDMDs) delete pLevelDMD; + for (RGB24DMD* pRGB24DMD : m_rgb24DMDs) delete pRGB24DMD; } bool DMD::IsFinding() { return m_finding; } @@ -116,94 +139,199 @@ bool DMD::HasDisplay() const #endif } -VirtualDMD* DMD::CreateVirtualDMD() +void DMD::DumpDMDTxt() { m_pDumpDMDTxtThread = new std::thread(&DMD::DumpDMDTxtThread, this); } + +void DMD::DumpDMDRaw() { m_pDumpDMDRawThread = new std::thread(&DMD::DumpDMDRawThread, this); } + +LevelDMD* DMD::CreateLevelDMD(uint16_t width, uint16_t height, bool sam) +{ + LevelDMD* const pLevelDMD = new LevelDMD(width, height, sam); + m_levelDMDs.push_back(pLevelDMD); + if (!m_pLevelDMDThread) m_pLevelDMDThread = new std::thread(&DMD::LevelDMDThread, this); + return pLevelDMD; +} + +bool DMD::DestroyLevelDMD(LevelDMD* pLevelDMD) { - VirtualDMD* const pVirtualDMD = new VirtualDMD(m_width, m_height); - m_virtualDMDs.push_back(pVirtualDMD); - return pVirtualDMD; + auto it = std::find(m_levelDMDs.begin(), m_levelDMDs.end(), pLevelDMD); + if (it != m_levelDMDs.end()) + { + m_levelDMDs.erase(it); + delete pLevelDMD; + + if (m_levelDMDs.empty()) + { + //@todo terminate LevelDMDThread + } + + return true; + } + return false; } -bool DMD::DestroyVirtualDMD(VirtualDMD* pVirtualDMD) +RGB24DMD* DMD::CreateRGB24DMD(uint16_t width, uint16_t height) { - auto it = std::find(m_virtualDMDs.begin(), m_virtualDMDs.end(), pVirtualDMD); - if (it != m_virtualDMDs.end()) + RGB24DMD* const pRGB24DMD = new RGB24DMD(width, height); + m_rgb24DMDs.push_back(pRGB24DMD); + if (!m_pRGB24DMDThread) m_pRGB24DMDThread = new std::thread(&DMD::RGB24DMDThread, this); + return pRGB24DMD; +} + +bool DMD::DestroyRGB24DMD(RGB24DMD* pRGB24DMD) +{ + auto it = std::find(m_rgb24DMDs.begin(), m_rgb24DMDs.end(), pRGB24DMD); + if (it != m_rgb24DMDs.end()) { - m_virtualDMDs.erase(it); - delete pVirtualDMD; + m_rgb24DMDs.erase(it); + delete pRGB24DMD; + + if (m_rgb24DMDs.empty()) + { + //@todo terminate RGB24DMDThread + } return true; } return false; } -void DMD::UpdateData(const uint8_t* pData, int depth, uint8_t r, uint8_t g, uint8_t b) +void DMD::UpdateData(const uint8_t* pData, int depth, uint16_t width, uint16_t height, uint8_t r, uint8_t g, uint8_t b, + DMDMode mode, const char* name) { - DMDUpdate* const pUpdate = new DMDUpdate(); - memset(pUpdate, 0, sizeof(DMDUpdate)); - pUpdate->mode = DmdMode::Data; - pUpdate->depth = depth; + DMDUpdate dmdUpdate = DMDUpdate(); + dmdUpdate.mode = mode; + dmdUpdate.depth = depth; + dmdUpdate.width = width; + dmdUpdate.height = height; if (pData) { - pUpdate->pData = malloc(m_length); - memcpy(pUpdate->pData, pData, m_length); + memcpy(dmdUpdate.data, pData, width * height * (mode == DMDMode::RGB24 ? 3 : 1)); + dmdUpdate.hasData = true; } - pUpdate->r = r; - pUpdate->g = g; - pUpdate->b = b; - + else { - std::lock_guard lock(m_mutex); - m_updates.push(pUpdate); + dmdUpdate.hasData = false; } + dmdUpdate.hasSegData = false; + dmdUpdate.hasSegData2 = false; + dmdUpdate.r = r; + dmdUpdate.g = g; + dmdUpdate.b = b; + strcpy(dmdUpdate.name, name[0] != '\0' ? name : ""); + + new std::thread( + [this, dmdUpdate]() + { + std::unique_lock ul(m_dmdSharedMutex); + + if (++m_updateBufferPosition >= DMDUTIL_FRAME_BUFFER_SIZE) m_updateBufferPosition = 0; + memcpy(m_updateBuffer[m_updateBufferPosition], &dmdUpdate, sizeof(DMDUpdate)); + m_dmdFrameReady = true; + + ul.unlock(); + m_dmdCV.notify_all(); + }); } -void DMD::UpdateRGB24Data(const uint8_t* pData, int depth, uint8_t r, uint8_t g, uint8_t b) +void DMD::UpdateData(const uint8_t* pData, int depth, uint16_t width, uint16_t height, uint8_t r, uint8_t g, uint8_t b, + const char* name) { - DMDUpdate* const pUpdate = new DMDUpdate(); - memset(pUpdate, 0, sizeof(DMDUpdate)); - pUpdate->mode = DmdMode::RGB24; - pUpdate->depth = depth; + UpdateData(pData, depth, width, height, r, g, b, DMDMode::Data, name); +} + +void DMD::UpdateRGB24Data(const uint8_t* pData, int depth, uint16_t width, uint16_t height, uint8_t r, uint8_t g, + uint8_t b) +{ + UpdateData(pData, depth, width, height, r, g, b, DMDMode::RGB24, ""); +} + +void DMD::UpdateRGB24Data(const uint8_t* pData, uint16_t width, uint16_t height) +{ + UpdateData(pData, 24, width, height, 0, 0, 0, DMDMode::RGB24, ""); +} + +void DMD::UpdateRGB16Data(const uint16_t* pData, uint16_t width, uint16_t height) +{ + DMDUpdate dmdUpdate = DMDUpdate(); + dmdUpdate.mode = DMDMode::RGB16; + dmdUpdate.depth = 24; + dmdUpdate.width = width; + dmdUpdate.height = height; if (pData) { - pUpdate->pData = malloc(m_length * 3); - memcpy(pUpdate->pData, pData, m_length * 3); + memcpy(dmdUpdate.segData, pData, width * height * sizeof(uint16_t)); + dmdUpdate.hasData = true; } - pUpdate->r = r; - pUpdate->g = g; - pUpdate->b = b; - + else { - std::lock_guard lock(m_mutex); - m_updates.push(pUpdate); + dmdUpdate.hasData = false; } + dmdUpdate.hasSegData = false; + dmdUpdate.hasSegData2 = false; + strcpy(dmdUpdate.name, ""); + + new std::thread( + [this, dmdUpdate]() + { + std::unique_lock ul(m_dmdSharedMutex); + + if (++m_updateBufferPosition >= DMDUTIL_FRAME_BUFFER_SIZE) m_updateBufferPosition = 0; + memcpy(m_updateBuffer[m_updateBufferPosition], &dmdUpdate, sizeof(DMDUpdate)); + m_dmdFrameReady = true; + + ul.unlock(); + m_dmdCV.notify_all(); + }); } void DMD::UpdateAlphaNumericData(AlphaNumericLayout layout, const uint16_t* pData1, const uint16_t* pData2, uint8_t r, - uint8_t g, uint8_t b) + uint8_t g, uint8_t b, const char* name) { - DMDUpdate* const pUpdate = new DMDUpdate(); - memset(pUpdate, 0, sizeof(DMDUpdate)); - pUpdate->mode = DmdMode::AlphaNumeric; - pUpdate->layout = layout; - pUpdate->depth = 2; - pUpdate->pData = malloc(128 * sizeof(uint16_t)); - memcpy(pUpdate->pData, pData1, 128 * sizeof(uint16_t)); + DMDUpdate dmdUpdate = DMDUpdate(); + dmdUpdate.mode = DMDMode::AlphaNumeric; + dmdUpdate.layout = layout; + dmdUpdate.depth = 2; + dmdUpdate.width = 128; + dmdUpdate.height = 32; + dmdUpdate.hasData = false; + if (pData1) + { + memcpy(dmdUpdate.segData, pData1, 128 * sizeof(uint16_t)); + dmdUpdate.hasSegData = true; + } + else + { + dmdUpdate.hasSegData = false; + } if (pData2) { - pUpdate->pData2 = malloc(128 * sizeof(uint16_t)); - memcpy(pUpdate->pData2, pData2, 128 * sizeof(uint16_t)); + memcpy(dmdUpdate.segData2, pData2, 128 * sizeof(uint16_t)); + dmdUpdate.hasSegData2 = true; } - pUpdate->r = r; - pUpdate->g = g; - pUpdate->b = b; - + else { - std::lock_guard lock(m_mutex); - m_updates.push(pUpdate); + dmdUpdate.hasSegData2 = false; } + dmdUpdate.r = r; + dmdUpdate.g = g; + dmdUpdate.b = b; + strcpy(dmdUpdate.name, name[0] != '\0' ? name : ""); + + new std::thread( + [this, dmdUpdate]() + { + std::unique_lock ul(m_dmdSharedMutex); + + if (++m_updateBufferPosition >= DMDUTIL_FRAME_BUFFER_SIZE) m_updateBufferPosition = 0; + memcpy(m_updateBuffer[m_updateBufferPosition], &dmdUpdate, sizeof(DMDUpdate)); + m_dmdFrameReady = true; + + ul.unlock(); + m_dmdCV.notify_all(); + }); } -void DMD::FindDevices() +void DMD::FindDisplays() { if (m_finding) return; @@ -212,14 +340,10 @@ void DMD::FindDevices() new std::thread( [this]() { + Config* const pConfig = Config::GetInstance(); + ZeDMD* pZeDMD = nullptr; -#if !( \ - (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ - defined(__ANDROID__)) - PixelcadeDMD* pPixelcadeDMD = nullptr; -#endif - Config* const pConfig = Config::GetInstance(); if (pConfig->IsZeDMD()) { pZeDMD = new ZeDMD(); @@ -228,7 +352,7 @@ void DMD::FindDevices() if (pConfig->GetZeDMDDevice() != nullptr && pConfig->GetZeDMDDevice()[0] != '\0') pZeDMD->SetDevice(pConfig->GetZeDMDDevice()); - if (pZeDMD->Open(m_width, m_height)) + if (pZeDMD->Open()) { if (pConfig->IsZeDMDDebug()) pZeDMD->EnableDebug(); @@ -237,6 +361,8 @@ void DMD::FindDevices() if (pConfig->GetZeDMDBrightness() != -1) pZeDMD->SetBrightness(pConfig->GetZeDMDBrightness()); if (pConfig->IsZeDMDSaveSettings()) pZeDMD->SaveSettings(); + + m_pZeDMDThread = new std::thread(&DMD::ZeDMDThread, this); } else { @@ -245,17 +371,19 @@ void DMD::FindDevices() } } + m_pZeDMD = pZeDMD; + #if !( \ (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ defined(__ANDROID__)) + PixelcadeDMD* pPixelcadeDMD = nullptr; + if (pConfig->IsPixelcade()) - pPixelcadeDMD = PixelcadeDMD::Connect(pConfig->GetPixelcadeDevice(), m_width, m_height); -#endif + { + pPixelcadeDMD = PixelcadeDMD::Connect(pConfig->GetPixelcadeDevice(), 128, 32); + if (pPixelcadeDMD) m_pPixelcadeDMDThread = new std::thread(&DMD::PixelcadeDMDThread, this); + } - m_pZeDMD = pZeDMD; -#if !( \ - (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ - defined(__ANDROID__)) m_pPixelcadeDMD = pPixelcadeDMD; #endif @@ -263,307 +391,630 @@ void DMD::FindDevices() }); } -void DMD::Run() +void DMD::DmdFrameReadyResetThread() { - if (m_running) return; + char name[DMDUTIL_MAX_NAME_SIZE] = ""; - m_running = true; + while (true) + { + std::shared_lock sl(m_dmdSharedMutex); + m_dmdCV.wait(sl, [&]() { return m_dmdFrameReady || m_stopFlag; }); + sl.unlock(); - m_pThread = new std::thread( - [this]() + if (strcmp(m_updateBuffer[m_updateBufferPosition]->name, name) != 0) + { + if (m_pSerum) { - Log("DMD run thread starting"); + delete (m_pSerum); + m_pSerum = nullptr; + } + + strcpy(name, m_updateBuffer[m_updateBufferPosition]->name); + + m_pSerum = (Config::GetInstance()->IsAltColor() && name[0] != '\0') ? Serum::Load(name) : nullptr; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); - DmdMode mode = DmdMode::Unknown; + std::unique_lock ul(m_dmdSharedMutex); + m_dmdFrameReady = false; + ul.unlock(); - while (m_running) + if (m_stopFlag) + { + return; + } + } +} + +void DMD::ZeDMDThread() +{ + int bufferPosition = 0; + uint16_t width = 0; + uint16_t height = 0; + uint16_t segData1[128]; + uint16_t segData2[128]; + uint8_t palette[192] = {0}; + // ZeDMD HD supports 256 * 64 pixels. + uint8_t renderBuffer[256 * 64] = {0}; + uint8_t rgb24Data[256 * 64 * 3] = {0}; + + while (true) + { + std::shared_lock sl(m_dmdSharedMutex); + m_dmdCV.wait(sl, [&]() { return m_dmdFrameReady || m_stopFlag; }); + sl.unlock(); + if (m_stopFlag) + { + return; + } + + while (!m_stopFlag && bufferPosition != m_updateBufferPosition) + { + if (++bufferPosition >= DMDUTIL_FRAME_BUFFER_SIZE) bufferPosition = 0; + + // Note: libzedmd has its own update detection. + + if (m_updateBuffer[bufferPosition]->hasData || m_updateBuffer[bufferPosition]->hasSegData) + { + if (m_updateBuffer[bufferPosition]->width != width || m_updateBuffer[bufferPosition]->height != height) + { + width = m_updateBuffer[bufferPosition]->width; + height = m_updateBuffer[bufferPosition]->height; + // Activate the correct scaling mode. + m_pZeDMD->SetFrameSize(width, height); + } + + bool update = false; + if (m_updateBuffer[bufferPosition]->depth != 24) { - DMDUpdate* pUpdate = nullptr; + update = UpdatePalette(palette, m_updateBuffer[bufferPosition]->depth, m_updateBuffer[bufferPosition]->r, + m_updateBuffer[bufferPosition]->g, m_updateBuffer[bufferPosition]->b); + } + if (m_updateBuffer[bufferPosition]->mode == DMDMode::RGB24) + { + AdjustRGB24Depth(m_updateBuffer[bufferPosition]->data, rgb24Data, width * height * 3, palette, + m_updateBuffer[bufferPosition]->depth); + m_pZeDMD->RenderRgb24(rgb24Data); + } + else if (m_updateBuffer[bufferPosition]->mode == DMDMode::RGB16) + { + m_pZeDMD->RenderRgb565(m_updateBuffer[bufferPosition]->segData); + } + else + { + if (m_updateBuffer[bufferPosition]->mode == DMDMode::Data) { - std::lock_guard lock(m_mutex); - if (!m_updates.empty()) + memcpy(renderBuffer, m_updateBuffer[bufferPosition]->data, width * height); + + if (m_pSerum) { - pUpdate = m_updates.front(); - m_updates.pop(); + uint8_t rotations[24] = {0}; + uint32_t triggerID; + uint32_t hashcode; + int frameID; + + m_pSerum->SetStandardPalette(palette, m_updateBuffer[bufferPosition]->depth); + + if (m_pSerum->ColorizeWithMetadata(renderBuffer, width, height, palette, rotations, &triggerID, &hashcode, + &frameID)) + { + m_pZeDMD->RenderColoredGray6(renderBuffer, palette, rotations); + + // @todo: send DMD PUP Event with triggerID + } + } + else + { + m_pZeDMD->SetPalette(palette, m_updateBuffer[bufferPosition]->depth == 2 ? 4 : 16); + + switch (m_updateBuffer[bufferPosition]->depth) + { + case 2: + m_pZeDMD->RenderGray2(renderBuffer); + break; + + case 4: + m_pZeDMD->RenderGray4(renderBuffer); + break; + + default: + //@todo log error + break; + } } } - - if (pUpdate) + else if (m_updateBuffer[bufferPosition]->mode == DMDMode::AlphaNumeric) { - const bool update = (mode != pUpdate->mode); - mode = pUpdate->mode; - - if (mode == DmdMode::Data) - UpdateData(pUpdate, update); - else if (mode == DmdMode::RGB24) - UpdateRGB24Data(pUpdate, update); - else if (mode == DmdMode::AlphaNumeric) - UpdateAlphaNumericData(pUpdate, update); - - free(pUpdate->pData); - free(pUpdate->pData2); - delete pUpdate; + if (memcmp(segData1, m_updateBuffer[bufferPosition]->segData, 128 * sizeof(uint16_t)) != 0) + { + memcpy(segData1, m_updateBuffer[bufferPosition]->segData, 128 * sizeof(uint16_t)); + update = true; + } + + if (m_updateBuffer[bufferPosition]->hasSegData2 && + memcmp(segData2, m_updateBuffer[bufferPosition]->segData2, 128 * sizeof(uint16_t)) != 0) + { + memcpy(segData2, m_updateBuffer[bufferPosition]->segData2, 128 * sizeof(uint16_t)); + update = true; + } + + if (update) + { + if (m_updateBuffer[bufferPosition]->hasSegData2) + m_pAlphaNumeric->Render(renderBuffer, m_updateBuffer[bufferPosition]->layout, segData1, segData2); + else + m_pAlphaNumeric->Render(renderBuffer, m_updateBuffer[bufferPosition]->layout, segData1); + + m_pZeDMD->SetPalette(palette, 4); + m_pZeDMD->RenderGray2(renderBuffer); + } } - else - std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - - Log("DMD run thread finished"); - }); + } + } + } } -bool DMD::UpdatePalette(const DMDUpdate* pUpdate) +#if !( \ + (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ + defined(__ANDROID__)) + +void DMD::PixelcadeDMDThread() { - if (pUpdate->depth != 2 && pUpdate->depth != 4) return false; + int bufferPosition = 0; + uint16_t segData1[128]; + uint16_t segData2[128]; + uint8_t palette[192] = {0}; + uint16_t rgb565Data[128 * 32] = {0}; + uint8_t renderBuffer[128 * 32] = {0}; + uint8_t rgb24Data[128 * 32 * 3] = {0}; + + while (true) + { + std::shared_lock sl(m_dmdSharedMutex); + m_dmdCV.wait(sl, [&]() { return m_dmdFrameReady || m_stopFlag; }); + sl.unlock(); + if (m_stopFlag) + { + return; + } - uint8_t palette[192]; - memcpy(palette, m_palette, 192); + while (!m_stopFlag && bufferPosition != m_updateBufferPosition) + { + if (++bufferPosition >= DMDUTIL_FRAME_BUFFER_SIZE) bufferPosition = 0; + + // @todo scaling + if (m_updateBuffer[bufferPosition]->width == 128 && m_updateBuffer[bufferPosition]->height == 32 && + (m_updateBuffer[bufferPosition]->hasData || m_updateBuffer[bufferPosition]->hasSegData)) + { + int length = m_updateBuffer[bufferPosition]->width * m_updateBuffer[bufferPosition]->height; + bool update = false; + if (m_updateBuffer[bufferPosition]->depth != 24) + { + update = UpdatePalette(palette, m_updateBuffer[bufferPosition]->depth, m_updateBuffer[bufferPosition]->r, + m_updateBuffer[bufferPosition]->g, m_updateBuffer[bufferPosition]->b); + } + + if (m_updateBuffer[bufferPosition]->mode == DMDMode::RGB24) + { + AdjustRGB24Depth(m_updateBuffer[bufferPosition]->data, rgb24Data, length * 3, palette, + m_updateBuffer[bufferPosition]->depth); + for (int i = 0; i < length; i++) + { + int pos = i * 3; + uint32_t r = m_updateBuffer[bufferPosition]->data[pos]; + uint32_t g = m_updateBuffer[bufferPosition]->data[pos + 1]; + uint32_t b = m_updateBuffer[bufferPosition]->data[pos + 2]; - memset(m_palette, 0, 192); + rgb565Data[i] = (uint16_t)(((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | (b >> 3)); + } + update = true; + } + else if (m_updateBuffer[bufferPosition]->mode == DMDMode::RGB16) + { + memcpy(rgb565Data, m_updateBuffer[bufferPosition]->segData, 128 * 32 * sizeof(uint16_t)); + update = true; + } + else + { + if (m_updateBuffer[bufferPosition]->mode == DMDMode::Data) + { + // @todo At the momeent libserum only supports on instance. So don't apply colorization if a ZeDMD is + // attached. + if (m_pSerum && !m_pZeDMD) + { + update = m_pSerum->Convert((uint8_t*)m_updateBuffer[bufferPosition]->data, renderBuffer, palette, + m_updateBuffer[bufferPosition]->width, m_updateBuffer[bufferPosition]->height); + } + else + { + memcpy(renderBuffer, (uint8_t*)m_updateBuffer[bufferPosition]->data, + m_updateBuffer[bufferPosition]->width * m_updateBuffer[bufferPosition]->height); + update = true; + } + } + else if (m_updateBuffer[bufferPosition]->mode == DMDMode::AlphaNumeric) + { + if (memcmp(segData1, m_updateBuffer[bufferPosition]->segData, 128 * sizeof(uint16_t)) != 0) + { + memcpy(segData1, m_updateBuffer[bufferPosition]->segData, 128 * sizeof(uint16_t)); + update = true; + } - const float r = (float)pUpdate->r; - const float g = (float)pUpdate->g; - const float b = (float)pUpdate->b; + if (m_updateBuffer[bufferPosition]->hasSegData2 && + memcmp(segData2, m_updateBuffer[bufferPosition]->segData2, 128 * sizeof(uint16_t)) != 0) + { + memcpy(segData2, m_updateBuffer[bufferPosition]->segData2, 128 * sizeof(uint16_t)); + update = true; + } - const int colors = (pUpdate->depth == 2) ? 4 : 16; - int pos = 0; + if (update) + { + uint8_t* pData; - for (int i = 0; i < colors; i++) - { - float perc = FrameUtil::CalcBrightness((float)i / (float)(colors - 1)); - m_palette[pos++] = (uint8_t)(r * perc); - m_palette[pos++] = (uint8_t)(g * perc); - m_palette[pos++] = (uint8_t)(b * perc); - } + if (m_updateBuffer[bufferPosition]->hasSegData2) + m_pAlphaNumeric->Render(renderBuffer, m_updateBuffer[bufferPosition]->layout, segData1, segData2); + else + m_pAlphaNumeric->Render(renderBuffer, m_updateBuffer[bufferPosition]->layout, segData1); + } + } + + if (update) + { + for (int i = 0; i < length; i++) + { + int pos = renderBuffer[i] * 3; + uint32_t r = palette[pos]; + uint32_t g = palette[pos + 1]; + uint32_t b = palette[pos + 2]; - return (memcmp(m_palette, palette, 192) != 0); + rgb565Data[i] = (uint16_t)(((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | (b >> 3)); + } + } + } + + if (update) m_pPixelcadeDMD->Update(rgb565Data); + } + } + } } +#endif -void DMD::UpdateData(const DMDUpdate* pUpdate, bool update) +void DMD::LevelDMDThread() { - uint8_t* const pData = (uint8_t*)pUpdate->pData; + int bufferPosition = 0; + uint8_t renderBuffer[256 * 64] = {0}; - if (pData) + while (true) { - if (pUpdate->depth == 2) + std::shared_lock sl(m_dmdSharedMutex); + m_dmdCV.wait(sl, [&]() { return m_dmdFrameReady || m_stopFlag; }); + sl.unlock(); + if (m_stopFlag) { - for (int i = 0; i < m_length; i++) m_pLevelData[i] = LEVELS_WPC[pData[i]]; + return; } - else if (pUpdate->depth == 4) + + while (!m_stopFlag && bufferPosition != m_updateBufferPosition) { - if (!m_sam) - { - for (int i = 0; i < m_length; i++) m_pLevelData[i] = LEVELS_GTS3[pData[i]]; - } - else + if (++bufferPosition >= DMDUTIL_FRAME_BUFFER_SIZE) bufferPosition = 0; + + if (!m_levelDMDs.empty() && m_updateBuffer[bufferPosition]->mode == DMDMode::Data && !m_pSerum && + m_updateBuffer[bufferPosition]->hasData) { - for (int i = 0; i < m_length; i++) m_pLevelData[i] = LEVELS_SAM[pData[i]]; + int length = m_updateBuffer[bufferPosition]->width * m_updateBuffer[bufferPosition]->height; + if (memcmp(renderBuffer, m_updateBuffer[bufferPosition]->data, length) != 0) + { + memcpy(renderBuffer, m_updateBuffer[bufferPosition]->data, length); + for (LevelDMD* pLevelDMD : m_levelDMDs) + { + if (pLevelDMD->GetLength() == length * 3) + pLevelDMD->Update(renderBuffer, m_updateBuffer[bufferPosition]->depth); + } + } } } } +} - if (!m_pSerum) +void DMD::RGB24DMDThread() +{ + int bufferPosition = 0; + uint16_t segData1[128]; + uint16_t segData2[128]; + uint8_t palette[192] = {0}; + uint8_t renderBuffer[256 * 64] = {0}; + uint8_t rgb24Data[256 * 64 * 3] = {0}; + + while (true) { - if (pData) + std::shared_lock sl(m_dmdSharedMutex); + m_dmdCV.wait(sl, [&]() { return m_dmdFrameReady || m_stopFlag; }); + sl.unlock(); + if (m_stopFlag) { - if (memcmp(m_pBuffer, pData, m_length) != 0) - { - memcpy(m_pBuffer, pData, m_length); - update = true; - } + return; } - if (UpdatePalette(pUpdate)) update = true; - } - else if (m_pSerum->Convert(pData, m_pBuffer, m_palette)) - { - // if we have serum, run a conversion, and if success, we have an update (needed for rotations) - // serum will take care of updating the data buffer - update = true; - } + while (!m_stopFlag && bufferPosition != m_updateBufferPosition) + { + if (++bufferPosition >= DMDUTIL_FRAME_BUFFER_SIZE) bufferPosition = 0; - if (!update) return; + if (!m_rgb24DMDs.empty() && + (m_updateBuffer[bufferPosition]->hasData || m_updateBuffer[bufferPosition]->hasSegData)) + { + int length = m_updateBuffer[bufferPosition]->width * m_updateBuffer[bufferPosition]->height; + bool update = false; - for (int i = 0; i < m_length; i++) - { - int pos = m_pBuffer[i] * 3; - uint32_t r = m_palette[pos]; - uint32_t g = m_palette[pos + 1]; - uint32_t b = m_palette[pos + 2]; + if (m_updateBuffer[bufferPosition]->mode == DMDMode::RGB24) + { + if (memcmp(rgb24Data, m_updateBuffer[bufferPosition]->data, length * 3) != 0) + { + if (m_updateBuffer[bufferPosition]->depth != 24) + { + UpdatePalette(palette, m_updateBuffer[bufferPosition]->depth, m_updateBuffer[bufferPosition]->r, + m_updateBuffer[bufferPosition]->g, m_updateBuffer[bufferPosition]->b); + } - pos = i * 3; - m_pRGB24Data[pos] = r; - m_pRGB24Data[pos + 1] = g; - m_pRGB24Data[pos + 2] = b; + AdjustRGB24Depth(m_updateBuffer[bufferPosition]->data, rgb24Data, length * 3, palette, + m_updateBuffer[bufferPosition]->depth); - m_pRGB565Data[i] = (uint16_t)(((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | (b >> 3)); - } + for (RGB24DMD* pRGB24DMD : m_rgb24DMDs) + { + if (pRGB24DMD->GetLength() == length * 3) pRGB24DMD->Update(rgb24Data); + } + // Reset renderBuffer in case the mode changes for the next frame to ensure that memcmp() will detect it. + memset(renderBuffer, 0, sizeof(renderBuffer)); + } + } + else if (m_updateBuffer[bufferPosition]->mode != DMDMode::RGB16) + { + // @todo At the momeent libserum only supports on instance. So don't apply colorization if any hardware DMD is + // attached. + if (m_updateBuffer[bufferPosition]->mode == DMDMode::Data && m_pSerum && !HasDisplay()) + { + update = m_pSerum->Convert(m_updateBuffer[bufferPosition]->data, renderBuffer, palette, + m_updateBuffer[bufferPosition]->width, m_updateBuffer[bufferPosition]->height); + } + else + { + update = UpdatePalette(palette, m_updateBuffer[bufferPosition]->depth, m_updateBuffer[bufferPosition]->r, + m_updateBuffer[bufferPosition]->g, m_updateBuffer[bufferPosition]->b); - if (m_pZeDMD) - { - if (m_pSerum) - { - m_pZeDMD->SetPalette(m_palette, 64); - m_pZeDMD->RenderColoredGray6(m_pBuffer, nullptr); - } - else - { - if (pUpdate->depth == 2) - { - m_pZeDMD->SetPalette(m_palette, 4); - m_pZeDMD->RenderGray2(m_pBuffer); - } - else - { - m_pZeDMD->SetPalette(m_palette, 16); - m_pZeDMD->RenderGray4(m_pBuffer); + if (m_updateBuffer[bufferPosition]->mode == DMDMode::Data) + { + if (memcmp(renderBuffer, m_updateBuffer[bufferPosition]->data, length) != 0) + { + memcpy(renderBuffer, m_updateBuffer[bufferPosition]->data, length); + update = true; + } + } + else if (m_updateBuffer[bufferPosition]->mode == DMDMode::AlphaNumeric) + { + if (memcmp(segData1, m_updateBuffer[bufferPosition]->segData, 128 * sizeof(uint16_t)) != 0) + { + memcpy(segData1, m_updateBuffer[bufferPosition]->segData, 128 * sizeof(uint16_t)); + update = true; + } + + if (m_updateBuffer[bufferPosition]->hasSegData2 && + memcmp(segData2, m_updateBuffer[bufferPosition]->segData2, 128 * sizeof(uint16_t)) != 0) + { + memcpy(segData2, m_updateBuffer[bufferPosition]->segData2, 128 * sizeof(uint16_t)); + update = true; + } + + if (update) + { + if (m_updateBuffer[bufferPosition]->hasSegData2) + m_pAlphaNumeric->Render(renderBuffer, m_updateBuffer[bufferPosition]->layout, segData1, segData2); + else + m_pAlphaNumeric->Render(renderBuffer, m_updateBuffer[bufferPosition]->layout, segData1); + } + } + } + + if (update) + { + for (int i = 0; i < length; i++) + { + int palettePos = renderBuffer[i] * 3; + int pos = i * 3; + rgb24Data[pos] = palette[palettePos]; + rgb24Data[pos + 1] = palette[palettePos + 1]; + rgb24Data[pos + 2] = palette[palettePos + 2]; + } + + for (RGB24DMD* pRGB24DMD : m_rgb24DMDs) + { + if (pRGB24DMD->GetLength() == length * 3) pRGB24DMD->Update(rgb24Data); + } + } + } } } } - -#if !( \ - (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ - defined(__ANDROID__)) - if (m_pPixelcadeDMD) m_pPixelcadeDMD->Update(m_pRGB565Data); -#endif - - for (VirtualDMD* pVirtualDMD : m_virtualDMDs) pVirtualDMD->Update(m_pLevelData, m_pRGB24Data); } -void DMD::UpdateRGB24Data(const DMDUpdate* pUpdate, bool update) +bool DMD::UpdatePalette(uint8_t* pPalette, uint8_t depth, uint8_t r, uint8_t g, uint8_t b) { - uint8_t* const pData = (uint8_t*)pUpdate->pData; + if (depth != 2 && depth != 4) return false; + uint8_t palette[192]; + memcpy(palette, pPalette, 192); - if (pUpdate->depth != 24) - { - if (UpdatePalette(pUpdate)) update = true; - } + memset(pPalette, 0, 192); - if (memcmp(m_pRGB24Buffer, pData, m_length * 3) != 0) update = true; + const uint8_t colors = (depth == 2) ? 4 : 16; + uint8_t pos = 0; - if (!update) return; + for (uint8_t i = 0; i < colors; i++) + { + float perc = FrameUtil::CalcBrightness((float)i / (float)(colors - 1)); + pPalette[pos++] = (uint8_t)(((float)r) * perc); + pPalette[pos++] = (uint8_t)(((float)g) * perc); + pPalette[pos++] = (uint8_t)(((float)b) * perc); + } - memcpy(m_pRGB24Buffer, pData, m_length * 3); + return (memcmp(pPalette, palette, 192) != 0); +} - for (int i = 0; i < m_length; i++) +void DMD::AdjustRGB24Depth(uint8_t* pData, uint8_t* pDstData, int length, uint8_t* palette, uint8_t depth) +{ + if (depth != 24) { - int pos = i * 3; - uint32_t r = m_pRGB24Buffer[pos]; - uint32_t g = m_pRGB24Buffer[pos + 1]; - uint32_t b = m_pRGB24Buffer[pos + 2]; - - if (pUpdate->depth != 24) + for (int i = 0; i < length; i++) { + int pos = i * 3; + uint32_t r = pData[pos]; + uint32_t g = pData[pos + 1]; + uint32_t b = pData[pos + 2]; + int v = (int)(0.2126f * (float)r + 0.7152f * (float)g + 0.0722f * (float)b); if (v > 255) v = 255; uint8_t level; - if (pUpdate->depth == 2) + if (depth == 2) level = (uint8_t)(v >> 6); else level = (uint8_t)(v >> 4); - m_pLevelData[i] = level; - int pos2 = level * 3; - r = m_palette[pos2]; - g = m_palette[pos2 + 1]; - b = m_palette[pos2 + 2]; - } + r = palette[pos2]; + g = palette[pos2 + 1]; + b = palette[pos2 + 2]; - m_pRGB24Data[pos] = (uint8_t)r; - m_pRGB24Data[pos + 1] = (uint8_t)g; - m_pRGB24Data[pos + 2] = (uint8_t)b; - - m_pRGB565Data[i] = (uint16_t)(((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | (b >> 3)); + pDstData[pos] = (uint8_t)r; + pDstData[pos + 1] = (uint8_t)g; + pDstData[pos + 2] = (uint8_t)b; + } } - - if (pUpdate->depth == 2) + else { - if (m_pZeDMD) - { - m_pZeDMD->SetPalette(m_palette, 4); - m_pZeDMD->RenderGray2(m_pLevelData); - } + memcpy(pDstData, pData, length); } - else if (pUpdate->depth == 4) +} + +void DMD::DumpDMDTxtThread() +{ + char name[DMDUTIL_MAX_NAME_SIZE] = ""; + char filename[128]; + int bufferPosition = 0; + uint8_t renderBuffer[256 * 64] = {0}; + bool update = false; + std::chrono::steady_clock::time_point start; + + while (true) { - if (m_pZeDMD) + std::shared_lock sl(m_dmdSharedMutex); + m_dmdCV.wait(sl, [&]() { return m_dmdFrameReady || m_stopFlag; }); + sl.unlock(); + if (m_stopFlag) { - m_pZeDMD->SetPalette(m_palette, 16); - m_pZeDMD->RenderGray4(m_pLevelData); + return; } - } - else if (pUpdate->depth == 24) - { - if (m_pZeDMD) m_pZeDMD->RenderRgb24((uint8_t*)m_pRGB24Buffer); - } -#if !( \ - (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ - defined(__ANDROID__)) - if (m_pPixelcadeDMD) m_pPixelcadeDMD->Update(m_pRGB565Data); -#endif + while (!m_stopFlag && bufferPosition != m_updateBufferPosition) + { + if (++bufferPosition >= DMDUTIL_FRAME_BUFFER_SIZE) bufferPosition = 0; - for (VirtualDMD* pVirtualDMD : m_virtualDMDs) pVirtualDMD->Update(m_pLevelData, m_pRGB24Data); + if (m_updateBuffer[bufferPosition]->depth <= 4 && m_updateBuffer[bufferPosition]->mode == DMDMode::Data && + m_updateBuffer[bufferPosition]->hasData) + { + update = false; + if (strcmp(m_updateBuffer[m_updateBufferPosition]->name, name) != 0) + { + // New game ROM. + strcpy(name, m_updateBuffer[m_updateBufferPosition]->name); + snprintf(filename, DMDUTIL_MAX_NAME_SIZE + 5, "%s.txt", name); + update = true; + start = std::chrono::steady_clock::now(); + } + + if (name[0] != '\0') + { + int length = m_updateBuffer[bufferPosition]->width * m_updateBuffer[bufferPosition]->height; + if (update || (memcmp(renderBuffer, m_updateBuffer[bufferPosition]->data, length) != 0)) + { + memcpy(renderBuffer, m_updateBuffer[bufferPosition]->data, length); + FILE* f = fopen(filename, "a"); + if (f) + { + fprintf(f, "0x%08x\n", + (uint32_t)(std::chrono::duration_cast( + std::chrono::steady_clock::now() - start) + .count())); + for (int y = 0; y < m_updateBuffer[bufferPosition]->height; y++) + { + for (int x = 0; x < m_updateBuffer[bufferPosition]->width; x++) + { + fprintf(f, "%x", renderBuffer[y * m_updateBuffer[bufferPosition]->width + x]); + } + fprintf(f, "\n"); + } + fprintf(f, "\n"); + fclose(f); + } + } + } + } + } + } } -void DMD::UpdateAlphaNumericData(const DMDUpdate* pUpdate, bool update) +void DMD::DumpDMDRawThread() { - if (memcmp(m_segData1, pUpdate->pData, 128 * sizeof(uint16_t)) != 0) - { - memcpy(m_segData1, pUpdate->pData, 128 * sizeof(uint16_t)); - update = true; - } + char name[DMDUTIL_MAX_NAME_SIZE] = ""; + char filename[128]; + int bufferPosition = 0; + std::chrono::steady_clock::time_point start; - if (pUpdate->pData2 && memcmp(m_segData2, pUpdate->pData2, 128 * sizeof(uint16_t)) != 0) + while (true) { - memcpy(m_segData2, pUpdate->pData2, 128 * sizeof(uint16_t)); - update = true; - } - - if (UpdatePalette(pUpdate)) update = true; - - if (!update) return; - - uint8_t* pData; + std::shared_lock sl(m_dmdSharedMutex); + m_dmdCV.wait(sl, [&]() { return m_dmdFrameReady || m_stopFlag; }); + sl.unlock(); + if (m_stopFlag) + { + return; + } - if (pUpdate->pData2) - pData = m_pAlphaNumeric->Render(pUpdate->layout, (const uint16_t*)m_segData1, (const uint16_t*)m_segData2); - else - pData = m_pAlphaNumeric->Render(pUpdate->layout, (const uint16_t*)m_segData1); + while (!m_stopFlag && bufferPosition != m_updateBufferPosition) + { + if (++bufferPosition >= DMDUTIL_FRAME_BUFFER_SIZE) bufferPosition = 0; - for (int i = 0; i < m_length; i++) m_pLevelData[i] = LEVELS_WPC[pData[i]]; + if (m_updateBuffer[bufferPosition]->hasData || m_updateBuffer[bufferPosition]->hasSegData) + { + if (strcmp(m_updateBuffer[m_updateBufferPosition]->name, name) != 0) + { + // New game ROM. + strcpy(name, m_updateBuffer[m_updateBufferPosition]->name); + snprintf(filename, DMDUTIL_MAX_NAME_SIZE + 5, "%s.raw", name); + start = std::chrono::steady_clock::now(); + } - for (int i = 0; i < m_length; i++) - { - int pos = pData[i] * 3; - uint32_t r = m_palette[pos]; - uint32_t g = m_palette[pos + 1]; - uint32_t b = m_palette[pos + 2]; + if (name[0] != '\0') + { + int length = m_updateBuffer[bufferPosition]->width * m_updateBuffer[bufferPosition]->height; + FILE* f = fopen(filename, "ab"); + if (f) + { + auto current = + std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count(); + fwrite(¤t, 1, 4, f); - pos = i * 3; - m_pRGB24Data[pos] = (uint8_t)r; - m_pRGB24Data[pos + 1] = (uint8_t)g; - m_pRGB24Data[pos + 2] = (uint8_t)b; + uint32_t size = sizeof(m_updateBuffer[bufferPosition]); + fwrite(&size, 1, 4, f); - m_pRGB565Data[i] = (uint16_t)(((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | (b >> 3)); - } + fwrite(m_updateBuffer[bufferPosition], 1, size, f); - if (m_pZeDMD) - { - m_pZeDMD->SetPalette(m_palette, 4); - m_pZeDMD->RenderGray2(pData); + fclose(f); + } + } + } + } } - -#if !( \ - (defined(__APPLE__) && ((defined(TARGET_OS_IOS) && TARGET_OS_IOS) || (defined(TARGET_OS_TV) && TARGET_OS_TV))) || \ - defined(__ANDROID__)) - if (m_pPixelcadeDMD) m_pPixelcadeDMD->Update(m_pRGB565Data); -#endif - - for (VirtualDMD* pVirtualDMD : m_virtualDMDs) pVirtualDMD->Update(m_pLevelData, m_pRGB24Data); } -} // namespace DMDUtil \ No newline at end of file +} // namespace DMDUtil diff --git a/src/FrameUtil.cpp b/src/FrameUtil.cpp index 280d1b3..0b60920 100644 --- a/src/FrameUtil.cpp +++ b/src/FrameUtil.cpp @@ -1,233 +1,233 @@ -/* - * Portions of this code was derived from DMDExt - * - * https://github.com/freezy/dmd-extensions/blob/master/LibDmd/Common/FrameUtil.cs - */ - -#include "FrameUtil.h" - -#include -#include -#include - -namespace DMDUtil -{ - -inline int FrameUtil::MapAdafruitIndex(int x, int y, int width, int height, int numLogicalRows) -{ - int logicalRowLengthPerMatrix = 32 * 32 / 2 / numLogicalRows; - int logicalRow = y % numLogicalRows; - int dotPairsPerLogicalRow = width * height / numLogicalRows / 2; - int widthInMatrices = width / 32; - int matrixX = x / 32; - int matrixY = y / 32; - int totalMatrices = width * height / 1024; - int matrixNumber = totalMatrices - ((matrixY + 1) * widthInMatrices) + matrixX; - int indexWithinMatrixRow = x % logicalRowLengthPerMatrix; - int index = logicalRow * dotPairsPerLogicalRow + matrixNumber * logicalRowLengthPerMatrix + indexWithinMatrixRow; - return index; -} - -void FrameUtil::SplitIntoRgbPlanes(const uint16_t* rgb565, int rgb565Size, int width, int numLogicalRows, uint8_t* dest, - ColorMatrix colorMatrix) -{ - constexpr int pairOffset = 16; - int height = rgb565Size / width; - int subframeSize = rgb565Size / 2; - - for (int x = 0; x < width; ++x) - { - for (int y = 0; y < height; ++y) - { - if (y % (pairOffset * 2) >= pairOffset) continue; - - int inputIndex0 = y * width + x; - int inputIndex1 = inputIndex0 + pairOffset * width; - - uint16_t color0 = rgb565[inputIndex0]; - uint16_t color1 = rgb565[inputIndex1]; - - int r0 = 0, r1 = 0, g0 = 0, g1 = 0, b0 = 0, b1 = 0; - switch (colorMatrix) - { - case ColorMatrix::Rgb: - r0 = (color0 >> 13) /*& 0x7*/; - g0 = (color0 >> 8) /*& 0x7*/; - b0 = (color0 >> 2) /*& 0x7*/; - r1 = (color1 >> 13) /*& 0x7*/; - g1 = (color1 >> 8) /*& 0x7*/; - b1 = (color1 >> 2) /*& 0x7*/; - break; - - case ColorMatrix::Rbg: - r0 = (color0 >> 13) /*& 0x7*/; - b0 = (color0 >> 8) /*& 0x7*/; - g0 = (color0 >> 2) /*& 0x7*/; - r1 = (color1 >> 13) /*& 0x7*/; - b1 = (color1 >> 8) /*& 0x7*/; - g1 = (color1 >> 2) /*& 0x7*/; - break; - } - - for (int subframe = 0; subframe < 3; ++subframe) - { - uint8_t dotPair = (r0 & 1) << 5 | (g0 & 1) << 4 | (b0 & 1) << 3 | (r1 & 1) << 2 | (g1 & 1) << 1 | (b1 & 1); - int indexWithinSubframe = MapAdafruitIndex(x, y, width, height, numLogicalRows); - int indexWithinOutput = subframe * subframeSize + indexWithinSubframe; - dest[indexWithinOutput] = dotPair; - r0 >>= 1; - g0 >>= 1; - b0 >>= 1; - r1 >>= 1; - g1 >>= 1; - b1 >>= 1; - } - } - } -} - -inline uint16_t FrameUtil::InterpolateRgb565Color(uint16_t color1, uint16_t color2, float ratio) -{ - // ratio *= ratio*(3.0f-2.0f*ratio); // = biquintic - - int red1 = (int)color1 >> 11; - int green1 = ((int)color1 >> 5) & 0x3F; - int blue1 = (int)color1 & 0x1F; - - int red2 = (int)color2 >> 11; - int green2 = ((int)color2 >> 5) & 0x3F; - int blue2 = (int)color2 & 0x1F; - - int red = red1 + static_cast((float)(red2 - red1) * ratio); - int green = green1 + static_cast((float)(green2 - green1) * ratio); - int blue = blue1 + static_cast((float)(blue2 - blue1) * ratio); - - red = std::min(std::max(red, 0), 0x1F); - green = std::min(std::max(green, 0), 0x3F); - blue = std::min(std::max(blue, 0), 0x1F); - - return (uint16_t)((red << 11) | (green << 5) | blue); -} - -inline uint16_t FrameUtil::InterpolatedRgb565Pixel(const uint16_t* src, float srcX, float srcY, int srcWidth, - int srcHeight) -{ - int x = (int)srcX; - int y = (int)srcY; - float xDiff = srcX - (float)x; - float yDiff = srcY - (float)y; - - // xDiff *= xDiff*(3.0f-2.0f*xDiff); // = biquintic - // yDiff *= yDiff*(3.0f-2.0f*yDiff); - - const int offs = y * srcWidth + x; - int a = src[offs]; - int b = (x < srcWidth - 1) ? src[offs + 1] : a; - int c = (y < srcHeight - 1) ? src[offs + srcWidth] : a; - int d = (x < srcWidth - 1 && y < srcHeight - 1) ? src[offs + srcWidth + 1] : c; - - int red1 = a >> 11; - int green1 = (a >> 5) & 0x3F; - int blue1 = a & 0x1F; - - int red2 = b >> 11; - int green2 = (b >> 5) & 0x3F; - int blue2 = b & 0x1F; - - float redab = (float)red1 + (float)(red2 - red1) * xDiff; - float greenab = (float)green1 + (float)(green2 - green1) * xDiff; - float blueab = (float)blue1 + (float)(blue2 - blue1) * xDiff; - - red1 = c >> 11; - green1 = (c >> 5) & 0x3F; - blue1 = c & 0x1F; - - red2 = d >> 11; - green2 = (d >> 5) & 0x3F; - blue2 = d & 0x1F; - - float redcd = (float)red1 + (float)(red2 - red1) * xDiff; - float greencd = (float)green1 + (float)(green2 - green1) * xDiff; - float bluecd = (float)blue1 + (float)(blue2 - blue1) * xDiff; - - float red = redab + (redcd - redab) * yDiff; - float green = greenab + (greencd - greenab) * yDiff; - float blue = blueab + (bluecd - blueab) * yDiff; - - red = std::min(std::max(red, 0.f), (float)0x1F); - green = std::min(std::max(green, 0.f), (float)0x3F); - blue = std::min(std::max(blue, 0.f), (float)0x1F); - - return (uint16_t)(((int)red << 11) | ((int)green << 5) | (int)blue); -} - -void FrameUtil::ResizeRgb565Bilinear(const uint16_t* src, int srcWidth, int srcHeight, uint16_t* dest, int destWidth, - int destHeight) -{ - memset(dest, 0, destWidth * destHeight * sizeof(uint16_t)); - - float srcAspect = (float)srcWidth / (float)srcHeight; - float destAspect = (float)destWidth / (float)destHeight; - int scaledWidth, scaledHeight; - - if (srcAspect > destAspect) - { - scaledWidth = destWidth; - scaledHeight = (int)((float)destWidth / srcAspect); - } - else - { - scaledHeight = destHeight; - scaledWidth = (int)((float)destHeight * srcAspect); - } - - int offsetX = (destWidth - scaledWidth) / 2; - int offsetY = (destHeight - scaledHeight) / 2; - int offs = offsetX + offsetY * destWidth; - - for (int y = 0; y < scaledHeight; ++y) - { - for (int x = 0; x < scaledWidth; ++x) - { - float srcX = ((float)x + 0.5f) * ((float)srcWidth / (float)scaledWidth) - 0.5f; - float srcY = ((float)y + 0.5f) * ((float)srcHeight / (float)scaledHeight) - 0.5f; - - srcX = std::max(0.0f, std::min(srcX, static_cast(srcWidth - 1))); - srcY = std::max(0.0f, std::min(srcY, static_cast(srcHeight - 1))); - - dest[y * destWidth + offs + x] = InterpolatedRgb565Pixel(src, srcX, srcY, srcWidth, srcHeight); - } - } -} - -float FrameUtil::CalcBrightness(float x) -{ - // function to improve the brightness with fx=ax²+bc+c, f(0)=0, f(1)=1, f'(1.1)=0 - return (-x * x + 2.1f * x) / 1.1f; -} - -std::string FrameUtil::HexDump(const uint8_t* data, size_t size) -{ - constexpr int bytesPerLine = 32; - - std::stringstream ss; - - for (size_t i = 0; i < size; i += bytesPerLine) - { - for (size_t j = i; j < i + bytesPerLine && j < size; ++j) - ss << std::setw(2) << std::setfill('0') << std::hex << static_cast(data[j]) << ' '; - - for (size_t j = i; j < i + bytesPerLine && j < size; ++j) - { - char ch = data[j]; - if (ch < 32 || ch > 126) ch = '.'; - ss << ch; - } - - ss << std::endl; - } - - return ss.str(); -} - -} // namespace DMDUtil \ No newline at end of file +/* + * Portions of this code was derived from DMDExt + * + * https://github.com/freezy/dmd-extensions/blob/master/LibDmd/Common/FrameUtil.cs + */ + +#include "FrameUtil.h" + +#include +#include +#include + +namespace DMDUtil +{ + +inline int FrameUtil::MapAdafruitIndex(int x, int y, int width, int height, int numLogicalRows) +{ + int logicalRowLengthPerMatrix = 32 * 32 / 2 / numLogicalRows; + int logicalRow = y % numLogicalRows; + int dotPairsPerLogicalRow = width * height / numLogicalRows / 2; + int widthInMatrices = width / 32; + int matrixX = x / 32; + int matrixY = y / 32; + int totalMatrices = width * height / 1024; + int matrixNumber = totalMatrices - ((matrixY + 1) * widthInMatrices) + matrixX; + int indexWithinMatrixRow = x % logicalRowLengthPerMatrix; + int index = logicalRow * dotPairsPerLogicalRow + matrixNumber * logicalRowLengthPerMatrix + indexWithinMatrixRow; + return index; +} + +void FrameUtil::SplitIntoRgbPlanes(const uint16_t* rgb565, int rgb565Size, int width, int numLogicalRows, uint8_t* dest, + ColorMatrix colorMatrix) +{ + constexpr int pairOffset = 16; + int height = rgb565Size / width; + int subframeSize = rgb565Size / 2; + + for (int x = 0; x < width; ++x) + { + for (int y = 0; y < height; ++y) + { + if (y % (pairOffset * 2) >= pairOffset) continue; + + int inputIndex0 = y * width + x; + int inputIndex1 = inputIndex0 + pairOffset * width; + + uint16_t color0 = rgb565[inputIndex0]; + uint16_t color1 = rgb565[inputIndex1]; + + int r0 = 0, r1 = 0, g0 = 0, g1 = 0, b0 = 0, b1 = 0; + switch (colorMatrix) + { + case ColorMatrix::Rgb: + r0 = (color0 >> 13) /*& 0x7*/; + g0 = (color0 >> 8) /*& 0x7*/; + b0 = (color0 >> 2) /*& 0x7*/; + r1 = (color1 >> 13) /*& 0x7*/; + g1 = (color1 >> 8) /*& 0x7*/; + b1 = (color1 >> 2) /*& 0x7*/; + break; + + case ColorMatrix::Rbg: + r0 = (color0 >> 13) /*& 0x7*/; + b0 = (color0 >> 8) /*& 0x7*/; + g0 = (color0 >> 2) /*& 0x7*/; + r1 = (color1 >> 13) /*& 0x7*/; + b1 = (color1 >> 8) /*& 0x7*/; + g1 = (color1 >> 2) /*& 0x7*/; + break; + } + + for (int subframe = 0; subframe < 3; ++subframe) + { + uint8_t dotPair = (r0 & 1) << 5 | (g0 & 1) << 4 | (b0 & 1) << 3 | (r1 & 1) << 2 | (g1 & 1) << 1 | (b1 & 1); + int indexWithinSubframe = MapAdafruitIndex(x, y, width, height, numLogicalRows); + int indexWithinOutput = subframe * subframeSize + indexWithinSubframe; + dest[indexWithinOutput] = dotPair; + r0 >>= 1; + g0 >>= 1; + b0 >>= 1; + r1 >>= 1; + g1 >>= 1; + b1 >>= 1; + } + } + } +} + +inline uint16_t FrameUtil::InterpolateRgb565Color(uint16_t color1, uint16_t color2, float ratio) +{ + // ratio *= ratio*(3.0f-2.0f*ratio); // = biquintic + + int red1 = (int)color1 >> 11; + int green1 = ((int)color1 >> 5) & 0x3F; + int blue1 = (int)color1 & 0x1F; + + int red2 = (int)color2 >> 11; + int green2 = ((int)color2 >> 5) & 0x3F; + int blue2 = (int)color2 & 0x1F; + + int red = red1 + static_cast((float)(red2 - red1) * ratio); + int green = green1 + static_cast((float)(green2 - green1) * ratio); + int blue = blue1 + static_cast((float)(blue2 - blue1) * ratio); + + red = std::min(std::max(red, 0), 0x1F); + green = std::min(std::max(green, 0), 0x3F); + blue = std::min(std::max(blue, 0), 0x1F); + + return (uint16_t)((red << 11) | (green << 5) | blue); +} + +inline uint16_t FrameUtil::InterpolatedRgb565Pixel(const uint16_t* src, float srcX, float srcY, int srcWidth, + int srcHeight) +{ + int x = (int)srcX; + int y = (int)srcY; + float xDiff = srcX - (float)x; + float yDiff = srcY - (float)y; + + // xDiff *= xDiff*(3.0f-2.0f*xDiff); // = biquintic + // yDiff *= yDiff*(3.0f-2.0f*yDiff); + + const int offs = y * srcWidth + x; + int a = src[offs]; + int b = (x < srcWidth - 1) ? src[offs + 1] : a; + int c = (y < srcHeight - 1) ? src[offs + srcWidth] : a; + int d = (x < srcWidth - 1 && y < srcHeight - 1) ? src[offs + srcWidth + 1] : c; + + int red1 = a >> 11; + int green1 = (a >> 5) & 0x3F; + int blue1 = a & 0x1F; + + int red2 = b >> 11; + int green2 = (b >> 5) & 0x3F; + int blue2 = b & 0x1F; + + float redab = (float)red1 + (float)(red2 - red1) * xDiff; + float greenab = (float)green1 + (float)(green2 - green1) * xDiff; + float blueab = (float)blue1 + (float)(blue2 - blue1) * xDiff; + + red1 = c >> 11; + green1 = (c >> 5) & 0x3F; + blue1 = c & 0x1F; + + red2 = d >> 11; + green2 = (d >> 5) & 0x3F; + blue2 = d & 0x1F; + + float redcd = (float)red1 + (float)(red2 - red1) * xDiff; + float greencd = (float)green1 + (float)(green2 - green1) * xDiff; + float bluecd = (float)blue1 + (float)(blue2 - blue1) * xDiff; + + float red = redab + (redcd - redab) * yDiff; + float green = greenab + (greencd - greenab) * yDiff; + float blue = blueab + (bluecd - blueab) * yDiff; + + red = std::min(std::max(red, 0.f), (float)0x1F); + green = std::min(std::max(green, 0.f), (float)0x3F); + blue = std::min(std::max(blue, 0.f), (float)0x1F); + + return (uint16_t)(((int)red << 11) | ((int)green << 5) | (int)blue); +} + +void FrameUtil::ResizeRgb565Bilinear(const uint16_t* src, int srcWidth, int srcHeight, uint16_t* dest, int destWidth, + int destHeight) +{ + memset(dest, 0, destWidth * destHeight * sizeof(uint16_t)); + + float srcAspect = (float)srcWidth / (float)srcHeight; + float destAspect = (float)destWidth / (float)destHeight; + int scaledWidth, scaledHeight; + + if (srcAspect > destAspect) + { + scaledWidth = destWidth; + scaledHeight = (int)((float)destWidth / srcAspect); + } + else + { + scaledHeight = destHeight; + scaledWidth = (int)((float)destHeight * srcAspect); + } + + int offsetX = (destWidth - scaledWidth) / 2; + int offsetY = (destHeight - scaledHeight) / 2; + int offs = offsetX + offsetY * destWidth; + + for (int y = 0; y < scaledHeight; ++y) + { + for (int x = 0; x < scaledWidth; ++x) + { + float srcX = ((float)x + 0.5f) * ((float)srcWidth / (float)scaledWidth) - 0.5f; + float srcY = ((float)y + 0.5f) * ((float)srcHeight / (float)scaledHeight) - 0.5f; + + srcX = std::max(0.0f, std::min(srcX, static_cast(srcWidth - 1))); + srcY = std::max(0.0f, std::min(srcY, static_cast(srcHeight - 1))); + + dest[y * destWidth + offs + x] = InterpolatedRgb565Pixel(src, srcX, srcY, srcWidth, srcHeight); + } + } +} + +float FrameUtil::CalcBrightness(float x) +{ + // function to improve the brightness with fx=ax²+bc+c, f(0)=0, f(1)=1, f'(1.1)=0 + return (-x * x + 2.1f * x) / 1.1f; +} + +std::string FrameUtil::HexDump(const uint8_t* data, size_t size) +{ + constexpr int bytesPerLine = 32; + + std::stringstream ss; + + for (size_t i = 0; i < size; i += bytesPerLine) + { + for (size_t j = i; j < i + bytesPerLine && j < size; ++j) + ss << std::setw(2) << std::setfill('0') << std::hex << static_cast(data[j]) << ' '; + + for (size_t j = i; j < i + bytesPerLine && j < size; ++j) + { + char ch = data[j]; + if (ch < 32 || ch > 126) ch = '.'; + ss << ch; + } + + ss << std::endl; + } + + return ss.str(); +} + +} // namespace DMDUtil diff --git a/src/FrameUtil.h b/src/FrameUtil.h index 8ace230..4103319 100644 --- a/src/FrameUtil.h +++ b/src/FrameUtil.h @@ -1,36 +1,36 @@ -/* - * Portions of this code was derived from DMDExt - * - * https://github.com/freezy/dmd-extensions/blob/master/LibDmd/Common/FrameUtil.cs - */ - -#pragma once - -#include -#include - -namespace DMDUtil -{ - -enum class ColorMatrix -{ - Rgb, - Rbg -}; - -class FrameUtil -{ - public: - static inline int MapAdafruitIndex(int x, int y, int width, int height, int numLogicalRows); - static void SplitIntoRgbPlanes(const uint16_t* rgb565, int rgb565Size, int width, int numLogicalRows, uint8_t* dest, - ColorMatrix colorMatrix = ColorMatrix::Rgb); - static inline uint16_t InterpolateRgb565Color(uint16_t color1, uint16_t color2, float ratio); - static inline uint16_t InterpolatedRgb565Pixel(const uint16_t* src, float srcX, float srcY, int srcWidth, - int srcHeight); - static void ResizeRgb565Bilinear(const uint16_t* src, int srcWidth, int srcHeight, uint16_t* dest, int destWidth, - int destHeight); - static float CalcBrightness(float x); - static std::string HexDump(const uint8_t* data, size_t size); -}; - -} // namespace DMDUtil \ No newline at end of file +/* + * Portions of this code was derived from DMDExt + * + * https://github.com/freezy/dmd-extensions/blob/master/LibDmd/Common/FrameUtil.cs + */ + +#pragma once + +#include +#include + +namespace DMDUtil +{ + +enum class ColorMatrix +{ + Rgb, + Rbg +}; + +class FrameUtil +{ + public: + static inline int MapAdafruitIndex(int x, int y, int width, int height, int numLogicalRows); + static void SplitIntoRgbPlanes(const uint16_t* rgb565, int rgb565Size, int width, int numLogicalRows, uint8_t* dest, + ColorMatrix colorMatrix = ColorMatrix::Rgb); + static inline uint16_t InterpolateRgb565Color(uint16_t color1, uint16_t color2, float ratio); + static inline uint16_t InterpolatedRgb565Pixel(const uint16_t* src, float srcX, float srcY, int srcWidth, + int srcHeight); + static void ResizeRgb565Bilinear(const uint16_t* src, int srcWidth, int srcHeight, uint16_t* dest, int destWidth, + int destHeight); + static float CalcBrightness(float x); + static std::string HexDump(const uint8_t* data, size_t size); +}; + +} // namespace DMDUtil diff --git a/src/LevelDMD.cpp b/src/LevelDMD.cpp new file mode 100644 index 0000000..d873b6b --- /dev/null +++ b/src/LevelDMD.cpp @@ -0,0 +1,56 @@ +#include "DMDUtil/LevelDMD.h" + +#include +#include +#include + +namespace DMDUtil +{ + +LevelDMD::LevelDMD(uint16_t width, uint16_t height, bool sam) +{ + m_width = width; + m_height = height; + m_length = width * height; + m_pitch = width * 3; + m_sam = sam; + + m_pData = (uint8_t*)malloc(m_length); + memset(m_pData, 0, m_length); + + m_update = false; +} + +LevelDMD::~LevelDMD() { free(m_pData); } + +void LevelDMD::Update(uint8_t* pData, uint8_t depth) +{ + memcpy(m_pData, pData, m_length); + if (depth == 2) + { + for (int i = 0; i < m_length; i++) m_pData[i] = LEVELS_WPC[pData[i]]; + m_update = true; + } + else if (depth == 4) + { + if (m_sam) + { + for (int i = 0; i < m_length; i++) m_pData[i] = LEVELS_SAM[pData[i]]; + } + else + { + for (int i = 0; i < m_length; i++) m_pData[i] = LEVELS_GTS3[pData[i]]; + } + m_update = true; + } +} + +uint8_t* LevelDMD::GetData() +{ + if (!m_update) return nullptr; + + m_update = false; + return m_pData; +} + +} // namespace DMDUtil \ No newline at end of file diff --git a/src/Logger.cpp b/src/Logger.cpp index 0dc0969..c4cea93 100644 --- a/src/Logger.cpp +++ b/src/Logger.cpp @@ -1,20 +1,20 @@ -#include "Logger.h" - -#include "DMDUtil/Config.h" - -namespace DMDUtil -{ - -void Log(const char *format, ...) -{ - DMDUtil_LogCallback logCallback = Config::GetInstance()->GetLogCallback(); - - if (!logCallback) return; - - va_list args; - va_start(args, format); - (*(logCallback))(format, args); - va_end(args); -} - -} // namespace DMDUtil \ No newline at end of file +#include "Logger.h" + +#include "DMDUtil/Config.h" + +namespace DMDUtil +{ + +void Log(const char *format, ...) +{ + DMDUtil_LogCallback logCallback = Config::GetInstance()->GetLogCallback(); + + if (!logCallback) return; + + va_list args; + va_start(args, format); + (*(logCallback))(format, args); + va_end(args); +} + +} // namespace DMDUtil diff --git a/src/Logger.h b/src/Logger.h index d1490f2..9243d1c 100644 --- a/src/Logger.h +++ b/src/Logger.h @@ -1,8 +1,8 @@ -#pragma once - -namespace DMDUtil -{ - -void Log(const char *format, ...); - -} +#pragma once + +namespace DMDUtil +{ + +void Log(const char *format, ...); + +} diff --git a/src/RGB24DMD.cpp b/src/RGB24DMD.cpp new file mode 100644 index 0000000..dff88bb --- /dev/null +++ b/src/RGB24DMD.cpp @@ -0,0 +1,40 @@ +#include "DMDUtil/RGB24DMD.h" + +#include +#include +#include + +namespace DMDUtil +{ + +RGB24DMD::RGB24DMD(uint16_t width, uint16_t height) +{ + m_width = width; + m_height = height; + m_length = width * height * 3; + m_pitch = width * 3; + + m_pData = (uint8_t*)malloc(m_length); + memset(m_pData, 0, m_length); + + m_update = false; +} + +RGB24DMD::~RGB24DMD() { free(m_pData); } + +void RGB24DMD::Update(uint8_t* pData) +{ + memcpy(m_pData, pData, m_length); + + m_update = true; +} + +uint8_t* RGB24DMD::GetData() +{ + if (!m_update) return nullptr; + + m_update = false; + return m_pData; +} + +} // namespace DMDUtil \ No newline at end of file diff --git a/src/Serum.cpp b/src/Serum.cpp index 0941074..3d7343a 100644 --- a/src/Serum.cpp +++ b/src/Serum.cpp @@ -1,76 +1,75 @@ -#include "Serum.h" - -#include - -#include "DMDUtil/Config.h" -#include "Logger.h" -#include "serum-decode.h" - -namespace DMDUtil -{ - -bool Serum::m_isLoaded = false; - -Serum::Serum(int width, int height) -{ - m_width = width; - m_height = height; - m_length = width * height; - m_pFrame = (uint8_t*)malloc(m_length); - memset(m_pFrame, 0, m_length); -} - -Serum::~Serum() -{ - free(m_pFrame); - - Serum_Dispose(); - - m_isLoaded = false; -} - -Serum* Serum::Load(const std::string& romName) -{ - if (m_isLoaded) return nullptr; - - std::string altColorPath = Config::GetInstance()->GetAltColorPath(); - - if (altColorPath.empty()) return nullptr; - - int width; - int height; - unsigned int numColors; - unsigned int numTriggers; - - if (!Serum_Load(altColorPath.c_str(), romName.c_str(), &width, &height, &numColors, &numTriggers)) - { - Serum_Dispose(); - return nullptr; - } - - Log("Serum loaded: romName=%s, width=%d, height=%d, numColors=%d, numTriggers=%d", romName.c_str(), width, height, - numColors, numTriggers); - - m_isLoaded = true; - - return new Serum(width, height); -} - -bool Serum::Convert(uint8_t* pFrame, uint8_t* pDstFrame, uint8_t* pDstPalette) -{ - if (pFrame) memcpy(m_pFrame, pFrame, m_length); - - unsigned int triggerId; - - if (Serum_ColorizeOrApplyRotations(pFrame ? m_pFrame : nullptr, m_width, m_height, m_palette, &triggerId)) - { - memcpy(pDstFrame, m_pFrame, m_length); - memcpy(pDstPalette, m_palette, 192); - - return true; - } - - return false; -}; - -} // namespace DMDUtil +#include "Serum.h" + +#include + +#include "DMDUtil/Config.h" +#include "Logger.h" +#include "serum-decode.h" + +namespace DMDUtil +{ + +bool Serum::m_isLoaded = false; + +Serum::Serum(int width, int height) +{ + m_width = width; + m_height = height; + m_length = width * height; +} + +Serum::~Serum() +{ + Serum_Dispose(); + + m_isLoaded = false; +} + +Serum* Serum::Load(const std::string& romName) +{ + if (m_isLoaded) return nullptr; + + std::string altColorPath = Config::GetInstance()->GetAltColorPath(); + + if (altColorPath.empty()) return nullptr; + + int width; + int height; + unsigned int numColors; + unsigned int numTriggers; + + if (!Serum_Load(altColorPath.c_str(), romName.c_str(), &width, &height, &numColors, &numTriggers)) + { + Serum_Dispose(); + return nullptr; + } + + Log("Serum loaded: romName=%s, width=%d, height=%d, numColors=%d, numTriggers=%d", romName.c_str(), width, height, + numColors, numTriggers); + + m_isLoaded = true; + + return new Serum(width, height); +} + +bool Serum::Convert(uint8_t* pFrame, uint8_t* pDstFrame, uint8_t* pDstPalette, uint16_t width, uint16_t height) +{ + if (pFrame) memcpy(pDstFrame, pFrame, width * height); + + unsigned int triggerId; + + return Serum_ColorizeOrApplyRotations(pDstFrame ? pDstFrame : nullptr, width, height, pDstPalette, &triggerId); +}; + +void Serum::SetStandardPalette(const uint8_t* palette, const int bitDepth) +{ + Serum_SetStandardPalette(palette, bitDepth); +} + +bool Serum::ColorizeWithMetadata(uint8_t* frame, int width, int height, uint8_t* palette, uint8_t* rotations, + uint32_t* triggerID, uint32_t* hashcode, int* frameID) +{ + return Serum_ColorizeWithMetadata(frame, width, height, palette, rotations, triggerID, hashcode, frameID); +} + +} // namespace DMDUtil diff --git a/src/Serum.h b/src/Serum.h index 05496ef..fad4742 100644 --- a/src/Serum.h +++ b/src/Serum.h @@ -1,29 +1,30 @@ -#pragma once - -#include -#include - -namespace DMDUtil -{ - -class Serum -{ - public: - ~Serum(); - - static Serum* Load(const std::string& romName); - bool Convert(uint8_t* pFrame, uint8_t* pDstFrame, uint8_t* pDstPalette); - - private: - Serum(int width, int height); - - int m_width; - int m_height; - int m_length; - uint8_t* m_pFrame; - uint8_t m_palette[64 * 3]; - - static bool m_isLoaded; -}; - -} // namespace DMDUtil \ No newline at end of file +#pragma once + +#include +#include + +namespace DMDUtil +{ + +class Serum +{ + public: + ~Serum(); + + static Serum* Load(const std::string& romName); + bool Convert(uint8_t* pFrame, uint8_t* pDstFrame, uint8_t* pDstPalette, uint16_t width, uint16_t height); + void SetStandardPalette(const uint8_t* palette, const int bitDepth); + bool ColorizeWithMetadata(uint8_t* frame, int width, int height, uint8_t* palette, uint8_t* rotations, + uint32_t* triggerID, uint32_t* hashcode, int* frameID); + + private: + Serum(int width, int height); + + int m_width; + int m_height; + int m_length; + + static bool m_isLoaded; +}; + +} // namespace DMDUtil diff --git a/src/VirtualDMD.cpp b/src/VirtualDMD.cpp deleted file mode 100644 index 1e3bf01..0000000 --- a/src/VirtualDMD.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "DMDUtil/VirtualDMD.h" - -#include -#include -#include - -namespace DMDUtil -{ - -VirtualDMD::VirtualDMD(int width, int height) -{ - m_width = width; - m_height = height; - m_length = width * height; - m_pitch = width * 3; - - m_pLevelData = (uint8_t*)malloc(m_length); - memset(m_pLevelData, 0, m_length); - - m_pRGB24Data = (uint8_t*)malloc(m_length * 3); - memset(m_pRGB24Data, 0, m_length * 3); - - m_update = false; -} - -VirtualDMD::~VirtualDMD() -{ - free(m_pLevelData); - free(m_pRGB24Data); -} - -void VirtualDMD::Update(uint8_t* pLevelData, uint8_t* pRGB24Data) -{ - memcpy(m_pLevelData, pLevelData, m_length); - memcpy(m_pRGB24Data, pRGB24Data, m_length * 3); - - m_update = true; -} - -uint8_t* VirtualDMD::GetLevelData() -{ - if (!m_update) return nullptr; - - m_update = false; - return m_pLevelData; -} - -uint8_t* VirtualDMD::GetRGB24Data() -{ - if (!m_update) return nullptr; - - m_update = false; - return m_pRGB24Data; -} - -} // namespace DMDUtil \ No newline at end of file diff --git a/src/test.cpp b/src/test.cpp index b43020f..e009037 100644 --- a/src/test.cpp +++ b/src/test.cpp @@ -1,7 +1,11 @@ +#include + #include #include #include "DMDUtil/DMDUtil.h" +#include "DMDUtil/LevelDMD.h" +#include "DMDUtil/RGB24DMD.h" void DMDUTILCALLBACK LogCallback(const char* format, va_list args) { @@ -47,10 +51,11 @@ int main(int argc, const char* argv[]) int width = 128; int height = 32; - DMDUtil::DMD* pDmd = new DMDUtil::DMD(width, height); + DMDUtil::DMD* pDmd = new DMDUtil::DMD(); printf("Finding displays...\n"); + pDmd->FindDisplays(); while (DMDUtil::DMD::IsFinding()) std::this_thread::sleep_for(std::chrono::milliseconds(100)); if (!pDmd->HasDisplay()) @@ -60,42 +65,128 @@ int main(int argc, const char* argv[]) return 1; } + pDmd->DumpDMDTxt(); + pDmd->DumpDMDRaw(); + printf("Rendering...\n"); - uint8_t* pImage2 = CreateImage(pDmd->GetWidth(), pDmd->GetHeight(), 2); - uint8_t* pImage4 = CreateImage(pDmd->GetWidth(), pDmd->GetHeight(), 4); - uint8_t* pImage24 = CreateImageRGB24(pDmd->GetWidth(), pDmd->GetHeight()); + uint8_t* pImage2 = CreateImage(128, 32, 2); + uint8_t* pImage4 = CreateImage(128, 32, 4); + uint8_t* pImage24 = CreateImageRGB24(128, 32); + uint16_t image16[128 * 32]; + for (int i = 0; i < 128 * 32; i++) + { + int pos = i * 3; + uint32_t r = pImage24[pos]; + uint32_t g = pImage24[pos + 1]; + uint32_t b = pImage24[pos + 2]; + image16[i] = (uint16_t)(((r & 0xF8u) << 8) | ((g & 0xFCu) << 3) | (b >> 3)); + } + + DMDUtil::LevelDMD* pLevelDMD128_2; + DMDUtil::LevelDMD* pLevelDMD128_4; + DMDUtil::LevelDMD* pLevelDMD196_4; + DMDUtil::RGB24DMD* pRGB24DMD128; + DMDUtil::RGB24DMD* pRGB24DMD196; + + int ms = 200; for (int i = 0; i < 4; i++) { - pDmd->UpdateData(pImage2, 2, 255, 0, 0); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + if (i == 0) pLevelDMD128_2 = pDmd->CreateLevelDMD(128, 32, 2); + if (i == 1) pLevelDMD128_4 = pDmd->CreateLevelDMD(128, 32, 4); + if (i == 2) pLevelDMD196_4 = pDmd->CreateLevelDMD(192, 64, 4); + if (i == 3) pDmd->DestroyLevelDMD(pLevelDMD128_2); + + if (i == 1) pRGB24DMD128 = pDmd->CreateRGB24DMD(128, 32); + if (i == 2) pRGB24DMD196 = pDmd->CreateRGB24DMD(192, 64); + if (i == 3) pDmd->DestroyRGB24DMD(pRGB24DMD196); + + printf("Delay %dms\n", ms); + + pDmd->UpdateData(pImage2, 2, 128, 32, 255, 0, 0, "test2"); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + pDmd->UpdateData(pImage2, 2, 128, 32, 0, 255, 0, "test2"); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + pDmd->UpdateData(pImage2, 2, 128, 32, 0, 0, 255, "test2"); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + pDmd->UpdateData(pImage2, 2, 128, 32, 255, 255, 255, "test2"); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + pDmd->UpdateData(pImage4, 4, 128, 32, 255, 0, 0, "test4"); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); - pDmd->UpdateData(pImage2, 2, 0, 255, 0); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + pDmd->UpdateData(pImage4, 4, 128, 32, 0, 255, 0, "test4"); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); - pDmd->UpdateData(pImage2, 2, 0, 0, 255); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + pDmd->UpdateData(pImage4, 4, 128, 32, 0, 0, 255, "test4"); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); - pDmd->UpdateData(pImage2, 2, 255, 255, 255); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + pDmd->UpdateData(pImage4, 4, 128, 32, 255, 255, 255, "test4"); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); - pDmd->UpdateData(pImage4, 4, 255, 0, 0); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + pDmd->UpdateRGB24Data(pImage24, 128, 32); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); - pDmd->UpdateData(pImage4, 4, 0, 255, 0); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + pDmd->UpdateRGB24Data(pImage24, 2, 128, 32, 255, 0, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); - pDmd->UpdateData(pImage4, 4, 0, 0, 255); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + pDmd->UpdateRGB24Data(pImage24, 2, 128, 32, 0, 255, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); - pDmd->UpdateData(pImage4, 4, 255, 255, 255); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + pDmd->UpdateRGB24Data(pImage24, 2, 128, 32, 0, 0, 255); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); - pDmd->UpdateRGB24Data(pImage24, 24, 0, 0, 0); - std::this_thread::sleep_for(std::chrono::milliseconds(200)); + pDmd->UpdateRGB24Data(pImage24, 2, 128, 32, 255, 255, 255); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + pDmd->UpdateRGB24Data(pImage24, 4, 128, 32, 255, 0, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + pDmd->UpdateRGB24Data(pImage24, 4, 128, 32, 0, 255, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + pDmd->UpdateRGB24Data(pImage24, 4, 128, 32, 0, 0, 255); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + pDmd->UpdateRGB24Data(pImage24, 4, 128, 32, 255, 255, 255); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + pDmd->UpdateRGB24Data(pImage24, 24, 128, 32, 0, 0, 0); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + pDmd->UpdateRGB16Data(image16, 128, 32); + std::this_thread::sleep_for(std::chrono::milliseconds(ms)); + + ms -= 60; } + FILE* fileptr; + uint16_t size = width * height * 2; + uint8_t* buffer = (uint8_t*)malloc(size * sizeof(uint8_t)); + uint16_t* rgb565 = (uint16_t*)malloc(size / 2 * sizeof(uint16_t)); + char filename[128]; + + for (int i = 1; i <= 100; i++) + { + snprintf(filename, 87, "external/libzedmd-7d2b0fc39475940b61b0126f3ff308dd193fe2a8/test/rgb565_%dx%d/%04d.raw", + width, height, i); + printf("Render raw: %s\n", filename); + fileptr = fopen(filename, "rb"); + fread(buffer, size, 1, fileptr); + fclose(fileptr); + + memcpy(rgb565, buffer, size); + pDmd->UpdateRGB16Data(rgb565, 128, 32); + std::this_thread::sleep_for(std::chrono::milliseconds(80)); + } + + free(buffer); + free(rgb565); + printf("Finished rendering\n"); free(pImage2); @@ -105,4 +196,4 @@ int main(int argc, const char* argv[]) delete pDmd; return 0; -} \ No newline at end of file +}