diff --git a/CMakeLists.txt b/CMakeLists.txt index 94b39c19..22bcd7be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,6 +102,9 @@ target_sources (Ardrivo PRIVATE include/Ardrivo/WiFi.h src/Ardrivo/WiFi.cpp + + include/Ardrivo/FrameBufferAccess.h + src/Ardrivo/FrameBufferAccess.cpp ) if (SMCE_ARDRIVO_MQTT) target_sources (Ardrivo PRIVATE include/Ardrivo/MQTT.h src/Ardrivo/MQTT.cpp) diff --git a/include/Ardrivo/FrameBufferAccess.h b/include/Ardrivo/FrameBufferAccess.h new file mode 100644 index 00000000..0a815e80 --- /dev/null +++ b/include/Ardrivo/FrameBufferAccess.h @@ -0,0 +1,37 @@ +// +// Created by danie on 2021-11-02. +// + +#ifndef FRAMEBUFFER_ACCESS_H +#define FRAMEBUFFER_ACCESS_H + +#include "SMCE_dll.hpp" + +enum SMCE_Pixel_Format +{ + RGB888, // RRRRRRRRGGGGGGGGBBBBBBBB // SMCE extension + RGB444, // GGGGBBBB----RRRR +}; + +class SMCE__DLL_RT_API FramebufferAccess { + private: + std::size_t m_key = 0; + SMCE_Pixel_Format m_format = RGB888; + bool m_begun = false; + + public: + int begin(std::uint16_t width, std::uint16_t height, SMCE_Pixel_Format format, std::uint8_t fps); + void end(); + bool read(void* buffer); + bool write(void* buffer); + + [[nodiscard]] int width() const; + [[nodiscard]] int height() const; + [[nodiscard]] int bitsPerPixel() const; + [[nodiscard]] int bytesPerPixel() const; + + void horizontalFlip(bool flipped); + void verticalFlip(bool flipped); +}; + +#endif // FRAMEBUFFER_ACCESS_H \ No newline at end of file diff --git a/include/Ardrivo/WString.h b/include/Ardrivo/WString.h index 8181eaa1..40f1befa 100644 --- a/include/Ardrivo/WString.h +++ b/include/Ardrivo/WString.h @@ -181,6 +181,47 @@ class SMCE__DLL_RT_API String { [[nodiscard]] bool operator>(const String& s) const noexcept; [[nodiscard]] bool operator>=(const String& s) const noexcept; + String& operator+=(const String& rhs) { + concat(rhs); + return (*this); + } + String& operator+=(const char* cstr) { + concat(cstr); + return (*this); + } + String& operator+=(char c) { + concat(c); + return (*this); + } + String& operator+=(unsigned char num) { + concat(num); + return (*this); + } + String& operator+=(int num) { + concat(num); + return (*this); + } + String& operator+=(unsigned int num) { + concat(num); + return (*this); + } + String& operator+=(long num) { + concat(num); + return (*this); + } + String& operator+=(unsigned long num) { + concat(num); + return (*this); + } + String& operator+=(float num) { + concat(num); + return (*this); + } + String& operator+=(double num) { + concat(num); + return (*this); + } + friend SMCE__DLL_RT_API String operator+(const String&, const char*); friend SMCE__DLL_RT_API String operator+(const char*, const String&); }; diff --git a/src/Ardrivo/FrameBufferAccess.cpp b/src/Ardrivo/FrameBufferAccess.cpp new file mode 100644 index 00000000..e8341cf3 --- /dev/null +++ b/src/Ardrivo/FrameBufferAccess.cpp @@ -0,0 +1,139 @@ +// +// Created by danie on 2021-11-02. +// + +#include +#include +#include +#include +#include "../../include/Ardrivo/FrameBufferAccess.h" + +namespace smce { +extern BoardView board_view; +extern void maybe_init(); +} // namespace smce + +int FramebufferAccess::begin(std::uint16_t width, std::uint16_t height, SMCE_Pixel_Format format, std::uint8_t fps) { + const auto error = [=](const char* msg) { + std::cerr << "ERROR: FramebufferAccess::begin(" << width << "x" << height << ", " << format << ", " << fps + << "): " << msg << std::endl; + return -1; + }; + if (m_begun) { + std::cerr << "FramebufferAccess::begin: device already active" << std::endl; + return -1; + } + switch (format) { + case RGB888: + case RGB444: + m_format = format; + break; + default: + return error("Unknown format"); + } + + auto fb = smce::board_view.frame_buffers[m_key]; + if (!fb.exists()) + return error("Framebuffer does not exist"); + if (fb.direction() != smce::FrameBuffer::Direction::in) + return error("Framebuffer not in input mode"); + + fb.set_width(width); + fb.set_height(height); + fb.set_freq(fps); + + m_begun = true; + return 0; +} + +void FramebufferAccess::end() { + if (!m_begun) { + std::cerr << "FramebufferAccess::end: device inactive" << std::endl; + return; + } + auto fb = smce::board_view.frame_buffers[m_key]; + fb.set_width(0); + fb.set_height(0); + fb.set_freq(0); + m_begun = false; +} + +bool FramebufferAccess::read(void* buffer) { + if (!m_begun) { + std::cerr << "FramebufferAccess::read: device inactive" << std::endl; + return false; + } + using ReadType = std::add_const_t; + constexpr ReadType format_read[2] = { + &smce::FrameBuffer::read_rgb888, + &smce::FrameBuffer::read_rgb444, + }; + (smce::board_view.frame_buffers[m_key].*format_read[m_format])( + {static_cast(buffer), static_cast(bitsPerPixel() * width() * height() / CHAR_BIT)}); + return true; +} + +bool FramebufferAccess::write(void* buffer) { + if (!m_begun) { + std::cerr << "FramebufferAccess::write: device inactive" << std::endl; + return false; + } + using WriteType = std::add_const_t; + constexpr WriteType format_write[2] = { + &smce::FrameBuffer::write_rgb888, + &smce::FrameBuffer::write_rgb444, + }; + (smce::board_view.frame_buffers[m_key].*format_write[m_format])( + {static_cast(buffer), static_cast(bitsPerPixel() * width() * height() / CHAR_BIT)}); + return true; +} + +int FramebufferAccess::width() const { + if (!m_begun) { + std::cerr << "FramebufferAccess::width: device inactive" << std::endl; + return 0; + } + return smce::board_view.frame_buffers[m_key].get_width(); +} + +int FramebufferAccess::height() const { + if (!m_begun) { + std::cerr << "FramebufferAccess::height: device inactive" << std::endl; + return 0; + } + return smce::board_view.frame_buffers[m_key].get_height(); +} + +constexpr std::array, 2> bits_bytes_pixel_formats{{{24, 3}, {16, 2}}}; + +int FramebufferAccess::bitsPerPixel() const { + if (!m_begun) { + std::cerr << "FramebufferAccess::bitsPerPixel: device inactive" << std::endl; + return 0; + } + return bits_bytes_pixel_formats[m_format].first; +} + +int FramebufferAccess::bytesPerPixel() const { + if (!m_begun) { + std::cerr << "FramebufferAccess::bytesPerPixel: device inactive" << std::endl; + return 0; + } + return bits_bytes_pixel_formats[m_format].second; +} + +void FramebufferAccess::horizontalFlip(bool flipped) { + if (!m_begun) { + std::cerr << "OV767X::horizontalFlip: device inactive" << std::endl; + return; + } + smce::board_view.frame_buffers[m_key].needs_horizontal_flip(flipped); +} + +void FramebufferAccess::verticalFlip(bool flipped) { + if (!m_begun) { + std::cerr << "OV767X::verticalFlip: device inactive" << std::endl; + return; + } + smce::board_view.frame_buffers[m_key].needs_vertical_flip(flipped); +} \ No newline at end of file