first commit

This commit is contained in:
valeh
2020-12-22 14:30:09 +02:00
commit 26b0ba5954
1832 changed files with 17777948 additions and 0 deletions

View File

@@ -0,0 +1,16 @@
CFLAGS = -g -Wall -I../../../csrc/ -ICImg
CXXFLAGS = $(CFLAGS)
LDFLAGS = -lpthread
CSRC = $(wildcard ls ../../../csrc/*.c) ../common/u8x8_d_bitmap.c
CPPSRC = main.cpp
OBJ = $(CSRC:.c=.o) $(CPPSRC:.cpp=.o)
cimg_annotate_screenshot: $(OBJ) CImg
$(CXX) $(CFLAGS) $(LDFLAGS) $(OBJ) -o cimg_annotate_screenshot
CImg:
git clone --depth=1 https://github.com/dtschump/CImg.git
clean:
-rm $(OBJ) cimg_annotate_screenshot

View File

@@ -0,0 +1,164 @@
#include <stdlib.h>
#include <stdint.h>
#include <sys/stat.h>
#include <assert.h>
#include <u8g2.h>
#define cimg_display 0 // Prevent CImg from including Xlib and other unneeded stuff
#include <CImg.h>
using namespace cimg_library;
const char * OUT_DIR = "output";
u8g2_t u8g2;
// Convert the buffer of an u8g2 bitmap device to a CImg bitmap
template<typename T>
CImg<T> u8x8_bitmap_to_cimg(u8g2_t *u8g2, unsigned int size_c, T *fg_color, T *bg_color) {
uint16_t width = u8g2_GetDisplayWidth(u8g2);
uint16_t height = u8g2_GetDisplayHeight(u8g2);
u8x8_t *u8x8 = u8g2_GetU8x8(u8g2);
CImg<T> img(width, height, /* size_z */ 1, size_c);
for (uint16_t y = 0; y < height; ++y) {
for (uint16_t x = 0; x < width; ++x) {
T *color = u8x8_GetBitmapPixel(u8x8, x, y) ? fg_color : bg_color;
img.draw_point(x, y, color);
}
}
return img;
}
static uint8_t black[] = {0, 0, 0};
static uint8_t white[] = {255, 255, 255};
static uint8_t grey[] = {128, 128, 128};
static uint8_t red[] = {255, 0, 0};
using Font = CImgList<uint8_t>;
const float OPAQUE = 1;
const unsigned SOLID_LINE = ~0U;
const uint8_t SCREENSHOT_BORDER = 2;
const uint8_t SCREENSHOT_SCALE = 3;
const float ARROW_ANGLE = 30;
const float ARROW_LENGTH = 2 * SCREENSHOT_SCALE + 1; // +1 makes for a more (but not necessarily perfect) symmetrical triangle
auto ANNOTATE_FONT = Font::font(8 * SCREENSHOT_SCALE);
// Draw a non-filled rectangle, using the specified line width (in pixels)
template<typename T>
void draw_outline(CImg<T>& img, int x, int y, int w, int h, const T* color, int line_width = 1) {
for (int i = 0; i < line_width; ++i)
img.draw_rectangle(x + i, y + i, x + w - 1 - i, y + h - 1 - i, color, OPAQUE, SOLID_LINE);
}
enum { ALIGN_LEFT = 0, ALIGN_CENTER = 1, ALIGN_RIGHT = 2};
enum { ALIGN_TOP = 0, ALIGN_MIDDLE = 4, ALIGN_BOTTOM = 8};
// Draw aligned text
template<typename T>
void draw_text(CImg<T>& img, int x, int y, const T* color, Font& font, unsigned align, const char *fmt, ...) {
va_list ap;
va_start(ap, fmt);
char buf[100];
int ret = vsnprintf(buf, sizeof(buf), fmt, ap);
assert(ret >= 0 && (size_t)ret < sizeof(buf));
if (align) {
auto text = CImg<uint8_t>().draw_text(0, 0, "%s", color, 0, OPAQUE, font, buf);
if (align & ALIGN_RIGHT)
x -= text.width();
else if (align & ALIGN_CENTER)
x -= text.width() / 2;
if (align & ALIGN_BOTTOM)
y -= text.height();
else if (align & ALIGN_MIDDLE)
y -= text.height() / 2;
}
img.draw_text(x, y, "%s", color, 0, OPAQUE, font, buf);
va_end(ap);
}
enum {TOP, RIGHT, BOTTOM, LEFT};
struct Annotation {
// Will be called to add annotations to the screenshot
virtual void annotate(CImg<uint8_t>& /*img*/) const { }
// Margins are in screenshot pixels, will be scaled later
// Top, right, bottom, left (like CSS)
size_t margin[4] = {0, 0, 0, 0};
};
int translate_x(float x, const Annotation& ann) {
return (x + ann.margin[LEFT]) * SCREENSHOT_SCALE + SCREENSHOT_BORDER;
}
int translate_y(float y, const Annotation& ann) {
return (y + ann.margin[TOP]) * SCREENSHOT_SCALE + SCREENSHOT_BORDER;
}
int translate_wh(float wh) {
return wh * SCREENSHOT_SCALE;
}
void save_screenshot(const char *dir, const char* name, const Annotation& ann = Annotation()) {
char filename[PATH_MAX];
int ret = snprintf(filename, sizeof(filename), "%s/%s.png", dir, name);
assert(ret >= 0 && (size_t)ret < sizeof(filename));
auto screenshot = u8x8_bitmap_to_cimg<uint8_t>(&u8g2, 3, black, white);
screenshot.resize(screenshot.width() * SCREENSHOT_SCALE, screenshot.height() * SCREENSHOT_SCALE);
// Create a bigger output image
size_t width = screenshot.width() + 2*SCREENSHOT_BORDER + (ann.margin[LEFT] + ann.margin[RIGHT]) * SCREENSHOT_SCALE;
size_t height = screenshot.height() + 2*SCREENSHOT_BORDER + (ann.margin[TOP] + ann.margin[BOTTOM]) * SCREENSHOT_SCALE;
auto output = CImg<uint8_t>(width, height, 1, 3, /* value for all channels */ 255);
// Draw the screenshot in the center
output.draw_image(translate_x(0, ann), translate_y(0, ann), 0, 0, screenshot);
// Draw a 1px border around the screenshot, just inside the
// SCREENSHOT_BORDER area (leaving a bit of white space between
// the border and the screenshot).
draw_outline(output, ann.margin[LEFT] * SCREENSHOT_SCALE, ann.margin[TOP] * SCREENSHOT_SCALE, screenshot.width() + 2 * SCREENSHOT_BORDER, screenshot.height() + 2 * SCREENSHOT_BORDER, grey);
ann.annotate(output);
output.save_png(filename);
printf("Generated %s\n", filename);
}
struct DescribeFigures : public Annotation {
DescribeFigures() {
this->margin[RIGHT] = 30;
this->margin[LEFT] = 30;
this->margin[TOP] = 10;
this->margin[BOTTOM] = 10;
}
virtual void annotate(CImg<uint8_t>& img) const {
// Add annotations. Coordinates are converted by translate_x/y from
// original screenshot pixels to the output screenshot pixels
draw_text(img, translate_x(132, *this), translate_y(15, *this), red, ANNOTATE_FONT, ALIGN_LEFT|ALIGN_MIDDLE, "Box");
img.draw_arrow(translate_x(130, *this), translate_y(15, *this), translate_x(116, *this), translate_y(15, *this), red, OPAQUE, ARROW_ANGLE, ARROW_LENGTH);
draw_text(img, translate_x(-4, *this), translate_y(10, *this), red, ANNOTATE_FONT, ALIGN_RIGHT|ALIGN_MIDDLE, "Circle");
img.draw_arrow(translate_x(-3, *this), translate_y(10, *this), translate_x(8, *this), translate_y(10, *this), red, OPAQUE, ARROW_ANGLE, ARROW_LENGTH);
}
};
int main() {
mkdir(OUT_DIR, 0755);
u8g2_SetupBitmap(&u8g2, &u8g2_cb_r0, 128, 64);
u8x8_InitDisplay(u8g2_GetU8x8(&u8g2));
u8x8_SetPowerSave(u8g2_GetU8x8(&u8g2), 0);
u8g2_SetFont(&u8g2, u8g2_font_helvB08_tr);
u8g2_ClearBuffer(&u8g2);
u8g2_DrawCircle(&u8g2, 15, 10, 5, U8G2_DRAW_ALL);
u8g2_DrawBox(&u8g2, 100, 10, 10, 10);
u8g2_SendBuffer(&u8g2);
save_screenshot(OUT_DIR, "plain_screenshot");
save_screenshot(OUT_DIR, "annotated_screenshot", DescribeFigures());
return 0;
}

View File

@@ -0,0 +1,291 @@
/*
u8x8_d_bitmap.c
a bitmap device
*/
#include "stdlib.h" /* malloc */
#include "stdint.h" /* uint16_t */
#include "string.h" /* memcpy */
#include "stdio.h" /* FILE */
#include "u8g2.h" /* because of u8g2_Setup... */
/*========================================================*/
/* bitmap struct */
struct _u8x8_bitmap_struct
{
u8x8_msg_cb u8x8_bitmap_display_old_cb;
uint16_t tile_width;
uint16_t tile_height;
uint16_t pixel_width;
uint16_t pixel_height;
uint8_t *u8x8_buf;
uint8_t *u8g2_buf;
};
typedef struct _u8x8_bitmap_struct u8x8_bitmap_t;
/*========================================================*/
/* bitmap functions */
uint8_t u8x8_bitmap_SetSize(u8x8_bitmap_t *b, uint16_t pixel_width, uint16_t pixel_height)
{
if ( b->u8x8_buf != NULL )
free(b->u8x8_buf);
b->tile_width = (pixel_width+7)/8;
b->tile_height = (pixel_height+7)/8;
b->pixel_width = pixel_width;
b->pixel_height = pixel_height;
/* allocate the bitmap twice, one for u8x8 and another bitmap for u8g2 */
/* however, the final bitmap will always be in the first half of the buffer */
b->u8x8_buf = (uint8_t *)malloc((size_t)b->tile_width*(size_t)b->tile_height*(size_t)8*(size_t)2);
b->u8g2_buf = b->u8x8_buf+(size_t)b->tile_width*(size_t)b->tile_height*(size_t)8;
if ( b->u8x8_buf == NULL ) {
b->u8g2_buf = NULL;
return 0;
}
//printf("pixel size: %d %d\n", b->pixel_width, b->pixel_height);
//printf("tile size: %d %d\n", b->tile_width, b->tile_height);
return 1;
}
void u8x8_bitmap_DrawTiles(u8x8_bitmap_t *b, uint16_t tx, uint16_t ty, uint8_t tile_cnt, uint8_t *tile_ptr)
{
uint8_t *dest_ptr = b->u8x8_buf;
//printf("tile pos: %d %d, cnt=%d\n", tx, ty, tile_cnt);
if ( dest_ptr == NULL )
return;
dest_ptr += ty*b->pixel_width;
dest_ptr += tx*8;
memcpy(dest_ptr, tile_ptr, tile_cnt*8);
}
uint8_t u8x8_bitmap_GetPixel(u8x8_bitmap_t *b, uint16_t x, uint16_t y)
{
uint8_t *dest_ptr = b->u8x8_buf;
if ( dest_ptr == NULL )
return 0;
dest_ptr += (y/8)*b->pixel_width;
y &= 7;
dest_ptr += x;
if ( (*dest_ptr & (1<<y)) == 0 )
return 0;
return 1;
}
static void tga_write_byte(FILE *fp, uint8_t byte)
{
fputc(byte, fp);
}
static void tga_write_word(FILE *fp, uint16_t word)
{
tga_write_byte(fp, word&255);
tga_write_byte(fp, word>>8);
}
void u8x8_bitmap_SaveTGA(u8x8_bitmap_t *b, const char *name)
{
FILE *fp;
uint16_t x, y;
fp = fopen(name, "wb");
if ( fp != NULL )
{
tga_write_byte(fp, 0); /* no ID */
tga_write_byte(fp, 0); /* no color map */
tga_write_byte(fp, 2); /* uncompressed true color */
tga_write_word(fp, 0);
tga_write_word(fp, 0);
tga_write_byte(fp, 0);
tga_write_word(fp, 0); /* x origin */
tga_write_word(fp, 0); /* y origin */
tga_write_word(fp, b->pixel_width); /* width */
tga_write_word(fp, b->pixel_height); /* height */
tga_write_byte(fp, 24); /* color depth */
tga_write_byte(fp, 0);
for( y = 0; y < b->pixel_height; y++ )
{
for( x = 0; x < b->pixel_width; x++ )
{
if ( u8x8_bitmap_GetPixel(b, x, b->pixel_height-y-1) == 0 )
{
tga_write_byte(fp, 255); /* R */
tga_write_byte(fp, 255); /* G */
tga_write_byte(fp, 255); /* B */
}
else
{
tga_write_byte(fp, 0); /* R */
tga_write_byte(fp, 0); /* G */
tga_write_byte(fp, 0); /* B */
}
}
}
tga_write_word(fp, 0);
tga_write_word(fp, 0);
tga_write_word(fp, 0);
tga_write_word(fp, 0);
fwrite("TRUEVISION-XFILE.", 18, 1, fp);
fclose(fp);
}
}
/*========================================================*/
/* global objects for the bitmap */
u8x8_bitmap_t u8x8_bitmap;
static u8x8_display_info_t u8x8_bitmap_info =
{
/* chip_enable_level = */ 0,
/* chip_disable_level = */ 1,
/* post_chip_enable_wait_ns = */ 0,
/* pre_chip_disable_wait_ns = */ 0,
/* reset_pulse_width_ms = */ 0,
/* post_reset_wait_ms = */ 0,
/* sda_setup_time_ns = */ 0,
/* sck_pulse_width_ns = */ 0,
/* sck_clock_hz = */ 4000000UL,
/* spi_mode = */ 1,
/* i2c_bus_clock_100kHz = */ 0,
/* data_setup_time_ns = */ 0,
/* write_pulse_width_ns = */ 0,
/* tile_width = */ 8, /* dummy value */
/* tile_hight = */ 4, /* dummy value */
/* default_x_offset = */ 0,
/* flipmode_x_offset = */ 0,
/* pixel_width = */ 64, /* dummy value */
/* pixel_height = */ 32 /* dummy value */
};
/*========================================================*/
/* functions for handling of the global objects */
/* allocate bitmap */
/* will be called by u8x8_SetupBitmap or u8g2_SetupBitmap */
static uint8_t u8x8_SetBitmapDeviceSize(u8x8_t *u8x8, uint16_t pixel_width, uint16_t pixel_height)
{
/* update the global bitmap object, allocate the bitmap */
if ( u8x8_bitmap_SetSize(&u8x8_bitmap, pixel_width, pixel_height) == 0 )
return 0;
/* update the u8x8 object */
u8x8_bitmap_info.tile_width = (pixel_width+7)/8;
u8x8_bitmap_info.tile_height = (pixel_height+7)/8;
u8x8_bitmap_info.pixel_width = pixel_width;
u8x8_bitmap_info.pixel_height = pixel_height;
return 1;
}
/* draw tiles to the bitmap, called by the device procedure */
static void u8x8_DrawBitmapTiles(u8x8_t *u8x8, uint16_t tx, uint16_t ty, uint8_t tile_cnt, uint8_t *tile_ptr)
{
u8x8_bitmap_DrawTiles(&u8x8_bitmap, tx, ty, tile_cnt, tile_ptr);
}
uint8_t u8x8_GetBitmapPixel(u8x8_t *u8x8, uint16_t x, uint16_t y)
{
return u8x8_bitmap_GetPixel(&u8x8_bitmap, x, y);
}
void u8x8_SaveBitmapTGA(u8x8_t *u8x8, const char *filename)
{
u8x8_bitmap_SaveTGA(&u8x8_bitmap, filename);
}
/*========================================================*/
static uint8_t u8x8_d_bitmap(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
u8g2_uint_t x, y, c;
uint8_t *ptr;
switch(msg)
{
case U8X8_MSG_DISPLAY_SETUP_MEMORY:
u8x8_d_helper_display_setup_memory(u8x8, &u8x8_bitmap_info);
break;
case U8X8_MSG_DISPLAY_INIT:
u8x8_d_helper_display_init(u8x8); /* update low level interfaces (not required here) */
break;
case U8X8_MSG_DISPLAY_SET_POWER_SAVE:
break;
case U8X8_MSG_DISPLAY_SET_FLIP_MODE:
break;
case U8X8_MSG_DISPLAY_DRAW_TILE:
x = ((u8x8_tile_t *)arg_ptr)->x_pos;
y = ((u8x8_tile_t *)arg_ptr)->y_pos;
c = ((u8x8_tile_t *)arg_ptr)->cnt;
ptr = ((u8x8_tile_t *)arg_ptr)->tile_ptr;
do
{
u8x8_DrawBitmapTiles(u8x8, x, y, c, ptr);
x += c;
arg_int--;
} while( arg_int > 0 );
break;
default:
return 0;
}
return 1;
}
/*========================================================*/
/* u8x8 and u8g2 setup functions */
void u8x8_SetupBitmap(u8x8_t *u8x8, uint16_t pixel_width, uint16_t pixel_height)
{
u8x8_SetBitmapDeviceSize(u8x8, pixel_width, pixel_height);
/* setup defaults */
u8x8_SetupDefaults(u8x8);
/* setup specific callbacks */
u8x8->display_cb = u8x8_d_bitmap;
/* setup display info */
u8x8_SetupMemory(u8x8);
}
void u8g2_SetupBitmap(u8g2_t *u8g2, const u8g2_cb_t *u8g2_cb, uint16_t pixel_width, uint16_t pixel_height)
{
/* allocate bitmap, assign the device callback to u8x8 */
u8x8_SetupBitmap(u8g2_GetU8x8(u8g2), pixel_width, pixel_height);
/* configure u8g2 in full buffer mode */
u8g2_SetupBuffer(u8g2, u8x8_bitmap.u8g2_buf, (pixel_height+7)/8, u8g2_ll_hvline_vertical_top_lsb, u8g2_cb);
}
/*========================================================*/
static uint8_t u8x8_d_bitmap_chain(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
if ( msg == U8X8_MSG_DISPLAY_DRAW_TILE )
u8x8_d_bitmap(u8x8, msg, arg_int, arg_ptr);
return u8x8_bitmap.u8x8_bitmap_display_old_cb(u8x8, msg, arg_int, arg_ptr);
}
/* connect the bitmap to an existing u8g2 or u8x8 object */
uint8_t u8x8_ConnectBitmapToU8x8(u8x8_t *u8x8)
{
if ( u8x8_SetBitmapDeviceSize(u8x8, u8x8_GetCols(u8x8)*8, u8x8_GetRows(u8x8)*8) == 0 )
return 0;
u8x8_bitmap.u8x8_bitmap_display_old_cb = u8x8->display_cb;
u8x8->display_cb = u8x8_d_bitmap_chain;
return 1;
}

View File

@@ -0,0 +1,12 @@
CFLAGS = -g -Wall -I../../../csrc/.
SRC = $(shell ls ../../../csrc/*.c) $(shell ls ../common/u8x8_d_bitmap.c ) main.c
OBJ = $(SRC:.c=.o)
hello_world: $(OBJ)
$(CC) $(CFLAGS) $(LDFLAGS) $(OBJ) -o $@
clean:
-rm -f $(OBJ) hello_world *.tga

View File

@@ -0,0 +1,40 @@
#include "u8g2.h"
#include <stdio.h>
/*
* This example uses the Bitmap device to draw to an in-memory buffer
* and writes the result to a TGA file. This can for example be used to
* make screenshots of an application normally running on an Arduino.
*/
u8g2_t u8g2;
int main(void)
{
u8g2_SetupBitmap(&u8g2, &u8g2_cb_r0, 128, 64);
u8x8_InitDisplay(u8g2_GetU8x8(&u8g2));
u8x8_SetPowerSave(u8g2_GetU8x8(&u8g2), 0);
u8g2_SetFont(&u8g2, u8g2_font_helvB08_tr);
u8g2_ClearBuffer(&u8g2);
// Here, you can call other code to actually draw things, such as
// existing application code to generate a screenshot
u8g2_DrawStr(&u8g2, 10, 30, "Hello, world!");
u8g2_SendBuffer(&u8g2);
u8x8_SaveBitmapTGA(u8g2_GetU8x8(&u8g2), "small.tga");
// This changes size, but also resets other stuff like font
u8g2_SetupBitmap(&u8g2, &u8g2_cb_r0, 128, 128);
u8g2_SetFont(&u8g2, u8g2_font_helvB08_tr);
u8g2_ClearBuffer(&u8g2);
// Here, you can call other code to actually draw things, such as
// existing application code to generate a screenshot
u8g2_DrawStr(&u8g2, 10, 30, "Hello, world!");
u8g2_DrawStr(&u8g2, 10, 100, "Hello, larger world!");
u8g2_SendBuffer(&u8g2);
u8x8_SaveBitmapTGA(u8g2_GetU8x8(&u8g2), "large.tga");
return 0;
}