Initial commit. Added base of framework

This commit is contained in:
nedko 2025-12-04 13:05:12 +02:00
commit 34ce515521
8 changed files with 250 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
trmnl_sdl
*.json

9
Makefile Normal file
View File

@ -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

15
README.md Normal file
View File

@ -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`

42
Widgets/Widget.cpp Normal file
View File

@ -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;
}

36
Widgets/Widget.h Normal file
View File

@ -0,0 +1,36 @@
#ifndef WIDGET_H_
#define WIDGET_H_
#include <SDL2/SDL.h>
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_

52
main.cpp Normal file
View File

@ -0,0 +1,52 @@
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <SDL2/SDL_image.h>
#include <vector>
#include <stdio.h>
#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<Widget*> 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;
}

76
sdl_helpers.cpp Normal file
View File

@ -0,0 +1,76 @@
#include "sdl_helpers.h"
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <map>
#include <utility>
#include <stdio.h>
using namespace std;
map<pair<string, int>, 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<string, int> 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;
}
}

18
sdl_helpers.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef SDL_HELPERS_H_
#define SDL_HELPERS_H_
#include <string>
#include <SDL2/SDL_ttf.h>
// 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_