mediamtx/internal/rpicamera/exe/text.c

172 lines
4.7 KiB
C

#include <time.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "text_font.h"
#include "text.h"
static char errbuf[256];
static void set_error(const char *format, ...) {
va_list args;
va_start(args, format);
vsnprintf(errbuf, 256, format, args);
}
const char *text_get_error() {
return errbuf;
}
typedef struct {
bool enabled;
char *text_overlay;
FT_Library library;
FT_Face face;
} text_priv_t;
bool text_create(const parameters_t *params, text_t **text) {
*text = malloc(sizeof(text_priv_t));
text_priv_t *textp = (text_priv_t *)(*text);
memset(textp, 0, sizeof(text_priv_t));
textp->enabled = params->text_overlay_enable;
textp->text_overlay = strdup(params->text_overlay);
if (textp->enabled) {
int error = FT_Init_FreeType(&textp->library);
if (error) {
set_error("FT_Init_FreeType() failed");
goto failed;
}
error = FT_New_Memory_Face(
textp->library,
text_font_ttf,
sizeof(text_font_ttf),
0,
&textp->face);
if (error) {
set_error("FT_New_Memory_Face() failed");
goto failed;
}
error = FT_Set_Pixel_Sizes(
textp->face,
25,
25);
if (error) {
set_error("FT_Set_Pixel_Sizes() failed");
goto failed;
}
}
return true;
failed:
free(textp);
return false;
}
static void draw_rect(uint8_t *buf, int stride, int height, int x, int y, unsigned int rect_width, unsigned int rect_height) {
uint8_t *Y = buf;
uint8_t *U = Y + stride * height;
uint8_t *V = U + (stride / 2) * (height / 2);
const uint8_t color[3] = {0, 128, 128};
uint32_t opacity = 45;
for (unsigned int src_y = 0; src_y < rect_height; src_y++) {
for (unsigned int src_x = 0; src_x < rect_width; src_x++) {
unsigned int dest_x = x + src_x;
unsigned int dest_y = y + src_y;
int i1 = dest_y*stride + dest_x;
int i2 = dest_y/2*stride/2 + dest_x/2;
Y[i1] = ((color[0] * opacity) + (uint32_t)Y[i1] * (255 - opacity)) / 255;
U[i2] = ((color[1] * opacity) + (uint32_t)U[i2] * (255 - opacity)) / 255;
V[i2] = ((color[2] * opacity) + (uint32_t)V[i2] * (255 - opacity)) / 255;
}
}
}
static void draw_bitmap(uint8_t *buf, int stride, int height, const FT_Bitmap *bitmap, int x, int y) {
uint8_t *Y = buf;
uint8_t *U = Y + stride * height;
uint8_t *V = U + (stride / 2) * (height / 2);
for (unsigned int src_y = 0; src_y < bitmap->rows; src_y++) {
for (unsigned int src_x = 0; src_x < bitmap->width; src_x++) {
uint8_t v = bitmap->buffer[src_y*bitmap->pitch + src_x];
if (v != 0) {
unsigned int dest_x = x + src_x;
unsigned int dest_y = y + src_y;
int i1 = dest_y*stride + dest_x;
int i2 = dest_y/2*stride/2 + dest_x/2;
uint32_t opacity = (uint32_t)v;
Y[i1] = (uint8_t)(((uint32_t)v * opacity + (uint32_t)Y[i1] * (255 - opacity)) / 255);
U[i2] = (uint8_t)((128 * opacity + (uint32_t)U[i2] * (255 - opacity)) / 255);
V[i2] = (uint8_t)((128 * opacity + (uint32_t)V[i2] * (255 - opacity)) / 255);
}
}
}
}
static int get_text_width(FT_Face face, const char *text) {
int ret = 0;
for (const char *ptr = text; *ptr != 0x00; ptr++) {
int error = FT_Load_Char(face, *ptr, FT_LOAD_RENDER);
if (error) {
continue;
}
ret += face->glyph->advance.x >> 6;
}
return ret;
}
void text_draw(text_t *text, uint8_t *buf, int stride, int height) {
text_priv_t *textp = (text_priv_t *)text;
if (textp->enabled) {
time_t timer = time(NULL);
struct tm *tm_info = localtime(&timer);
char buffer[255];
memset(buffer, 0, sizeof(buffer));
strftime(buffer, 255, textp->text_overlay, tm_info);
draw_rect(
buf,
stride,
height,
7,
7,
get_text_width(textp->face, buffer) + 10,
34);
int x = 12;
int y = 33;
for (const char *ptr = buffer; *ptr != 0x00; ptr++) {
int error = FT_Load_Char(textp->face, *ptr, FT_LOAD_RENDER);
if (error) {
continue;
}
draw_bitmap(
buf,
stride,
height,
&textp->face->glyph->bitmap,
x + textp->face->glyph->bitmap_left,
y - textp->face->glyph->bitmap_top);
x += textp->face->glyph->advance.x >> 6;
}
}
}