From 34ce515521bdc92c95317a82aeeeee7a9bca8ec1 Mon Sep 17 00:00:00 2001 From: nedko Date: Thu, 4 Dec 2025 13:05:12 +0200 Subject: [PATCH] Initial commit. Added base of framework --- .gitignore | 2 ++ Makefile | 9 ++++++ README.md | 15 +++++++++ Widgets/Widget.cpp | 42 +++++++++++++++++++++++++ Widgets/Widget.h | 36 ++++++++++++++++++++++ main.cpp | 52 +++++++++++++++++++++++++++++++ sdl_helpers.cpp | 76 ++++++++++++++++++++++++++++++++++++++++++++++ sdl_helpers.h | 18 +++++++++++ 8 files changed, 250 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 Widgets/Widget.cpp create mode 100644 Widgets/Widget.h create mode 100644 main.cpp create mode 100644 sdl_helpers.cpp create mode 100644 sdl_helpers.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50b1c00 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +trmnl_sdl +*.json diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1ef9927 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +src_files += main.cpp +src_files += sdl_helpers.cpp + +src_files += Widgets/Widget.cpp + +all: + g++ $(src_files) -Wall -lSDL2 -lSDL2_ttf -lSDL2_image -o trmnl_sdl + +.PHONY: all diff --git a/README.md b/README.md new file mode 100644 index 0000000..440cfe8 --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# What is this +A utility which can be used to create custom images for TRMNL devices via code + +# How to write new visual stuff +1. Inherit from `Widget` and then do your magic inside your own class. +2. Instantiate your widget in `main.cpp` and add it to the vector of widgets. +3. ??? +4. Profit + +# ImageMagick commands +### Convert image without dithering +`magick input.png -monochrome -colors 2 -depth 1 -strip png:output.png` + +### Convert image with dithering +`magick input.png -dither FloydSteinberg -remap pattern:gray50 -depth 1 -strip png:output.png` diff --git a/Widgets/Widget.cpp b/Widgets/Widget.cpp new file mode 100644 index 0000000..ff23bb3 --- /dev/null +++ b/Widgets/Widget.cpp @@ -0,0 +1,42 @@ +#include "Widget.h" + +Widget::Widget(int width, int height) +: m_surface(nullptr), m_rect{.x = 0, .y = 0, .w = width, .h = height} +{ + m_surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_RGBA8888); +} + +Widget::~Widget() +{ + if (nullptr != m_surface) + { + SDL_FreeSurface(m_surface); + } +} + +SDL_Surface* Widget::get_internal_surface() +{ + return m_surface; +} + +SDL_Rect Widget::get_rect() +{ + return m_rect; +} + +void Widget::set_position(int x, int y) +{ + m_rect.x = x; + m_rect.y = y; +} + +void Widget::resize(int width, int height) +{ + if (nullptr != m_surface) + { + SDL_FreeSurface(m_surface); + } + m_surface = SDL_CreateRGBSurfaceWithFormat(0, width, height, 32, SDL_PIXELFORMAT_RGBA8888); + m_rect.w = width; + m_rect.h = height; +} diff --git a/Widgets/Widget.h b/Widgets/Widget.h new file mode 100644 index 0000000..8a879e1 --- /dev/null +++ b/Widgets/Widget.h @@ -0,0 +1,36 @@ +#ifndef WIDGET_H_ +#define WIDGET_H_ + +#include + +class Widget +{ +private: + // Internal surface for drawing + SDL_Surface* m_surface; + + // Holds the size and position of the widget + SDL_Rect m_rect; + +public: + Widget(int width, int height); + ~Widget(); + + // Method which updates the internal surface with the latest image + virtual void draw() = 0; + + // Returns the internal surface + // Used for bliting to the main surface; + SDL_Surface* get_internal_surface(); + + // Returns the internal position and size + SDL_Rect get_rect(); + + // Sets the position of the widget on the screen + void set_position(int x, int y); + + // Sets the size of the widget on the screen + void resize(int width, int height); +}; + +#endif // WIDGET_H_ diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..f0c1dac --- /dev/null +++ b/main.cpp @@ -0,0 +1,52 @@ +#include +#include +#include + +#include + +#include + +#include "sdl_helpers.h" +#include "Widgets/Widget.h" + +using std::vector; + +int main(int argd, char **argv) +{ + int result = 0; + int screen_width = 800; + int screen_height = 600; + + vector widgets; + + if (!init_sdl()) + { + return -1; + } + + // Create surface + SDL_Surface* main_surface = SDL_CreateRGBSurfaceWithFormat(0, screen_width, screen_height, 32, SDL_PIXELFORMAT_RGBA8888); + if (nullptr == main_surface) + { + printf("Could not allocate main surface\n"); + result = -1; + goto cleanup; + } + + // Clear screen with white + SDL_FillRect(main_surface, nullptr, SDL_MapRGBA(main_surface->format, 255, 0, 255, 255)); + + // Apply all widgets + for (Widget* widget : widgets) + { + SDL_Rect rect = widget->get_rect(); + SDL_BlitSurface(widget->get_internal_surface(), nullptr, main_surface, &rect); + } + + // Save image + IMG_SavePNG(main_surface, "asdf.png"); + +cleanup: + clean_sdl(); + return result; +} diff --git a/sdl_helpers.cpp b/sdl_helpers.cpp new file mode 100644 index 0000000..9c7de8e --- /dev/null +++ b/sdl_helpers.cpp @@ -0,0 +1,76 @@ +#include "sdl_helpers.h" + +#include +#include + +#include +#include + +#include + +using namespace std; + +map, TTF_Font*> font_map; + +bool init_sdl() +{ + if (SDL_Init(SDL_INIT_VIDEO) < 0) + { + printf("SDL could not initialize! SDL - %s\n", SDL_GetError()); + return false; + } + + if (0 != TTF_Init()) + { + printf("Could not init TTF! SDL - %s\n", SDL_GetError()); + SDL_Quit(); + return false; + } + + int img_flags = IMG_Init(IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF); + if (img_flags != (IMG_INIT_JPG | IMG_INIT_PNG | IMG_INIT_TIF)) + { + printf("Could not init IMG! SDL - %s\n", SDL_GetError()); + TTF_Quit(); + SDL_Quit(); + return false; + } + + return true; +} + +void clean_sdl() +{ + // Clean up all fonts + for (auto it = font_map.begin(); it != font_map.end(); ++it) + { + TTF_CloseFont(it->second); + } + font_map.clear(); + + IMG_Quit(); + TTF_Quit(); + SDL_Quit(); +} + +TTF_Font* get_font(const string& filename, int size) +{ + pair key(filename, size); + if (0 != font_map.count(key)) + { + return font_map[key]; + } + else + { + TTF_Font* font = TTF_OpenFont(filename.c_str(), size); + if (nullptr != font) + { + font_map[key] = font; + } + else + { + printf("Could not open font '%s' with size %d\n", filename.c_str(), size); + } + return font; + } +} diff --git a/sdl_helpers.h b/sdl_helpers.h new file mode 100644 index 0000000..eb8d737 --- /dev/null +++ b/sdl_helpers.h @@ -0,0 +1,18 @@ +#ifndef SDL_HELPERS_H_ +#define SDL_HELPERS_H_ + +#include +#include + +// Call this before everything +// Prints its messages +bool init_sdl(); + +// Call this at the end only if init has passed +void clean_sdl(); + +// A simple way to get a font pointer to use +// Can return NULL +TTF_Font* get_font(const std::string& filename, int size); + +#endif // SDL_HELPERS_H_