Skip to content

Commit

Permalink
WIP: Initial cairo support
Browse files Browse the repository at this point in the history
Implements:
* image_ops_create (TODO: image_ops_update/image_ops_delete)
* font_ops_create (Using cairo w/ freetype2)
* script_ops_draw_line
* script_ops_draw_rect
* script_ops_draw_rrect
* script_ops_draw_circle
* script_ops_draw_text
* script_ops_begin_path
* script_ops_close_path
* script_ops_fill_path
* script_ops_stroke_path
* script_ops_move_to
* script_ops_line_to
* script_ops_push_state
* script_ops_pop_state
* script_ops_translate
* script_ops_fill_color
* script_ops_fill_linear
* script_ops_fill_image
* script_ops_stroke_width
* script_ops_stroke_color
* script_ops_line_cap
* script_ops_line_join
* script_ops_miter_limit
* script_ops_font
* script_ops_font_size
* script_ops_text_align
* script_ops_text_base

Signed-off-by: Jon Ringle <[email protected]>
  • Loading branch information
ringlej committed Jul 21, 2023
1 parent 1e08742 commit 6387f93
Show file tree
Hide file tree
Showing 6 changed files with 748 additions and 0 deletions.
12 changes: 12 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,18 @@ else ifeq ($(SCENIC_LOCAL_TARGET),drm)
else
CFLAGS += -DSCENIC_GLES3
endif
else ifeq ($(SCENIC_LOCAL_TARGET),cairo)
LDFLAGS += `pkg-config --static --libs freetype2 cairo`
CFLAGS += `pkg-config --static --cflags freetype2 cairo`
LDFLAGS += -lm
CFLAGS ?= -O2 -Wall -Wextra -Wno-unused-parameter -pedantic
CFLAGS += -std=gnu99

DEVICE_SRCS += \
c_src/device/cairo.c
FONT_SRCS += c_src/font/cairo_font_ops.c
IMAGE_SRCS += c_src/image/cairo_image_ops.c
SCENIC_SRCS += c_src/scenic/ops/cairo_script_ops.c
else
$(info ------ no SCENIC_LOCAL_TARGET set ------)
$(info If you get here, then you are probably using a custom Nerves system)
Expand Down
152 changes: 152 additions & 0 deletions c_src/device/cairo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
#include <cairo.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/fb.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>

#include "cairo_ctx.h"
#include "comms.h"
#include "device.h"
#include "fontstash.h"
#include "ops/script_ops.h"

const char* device = "/dev/fb0";

typedef struct {
int fd;

struct fb_var_screeninfo var;
struct fb_fix_screeninfo fix;
} cairo_fb_t;

cairo_fb_t g_cairo_fb = {0};

extern device_info_t g_device_info;
extern device_opts_t g_opts;

int device_init(const device_opts_t* p_opts,
device_info_t* p_info,
driver_data_t* p_data)
{
if (g_opts.debug_mode) {
log_info("cairo %s", __func__);
}

if ((g_cairo_fb.fd = open(device, O_RDWR)) == -1) {
log_error("Failed to open device %s: %s", device, strerror(errno));
return -1;
}

if (ioctl(g_cairo_fb.fd, FBIOGET_VSCREENINFO, &g_cairo_fb.var)) {
log_error("Failed to get fb_var_screeninfo: %s", strerror(errno));
return -1;
}

if (ioctl(g_cairo_fb.fd, FBIOGET_FSCREENINFO, &g_cairo_fb.fix)) {
log_error("Failed to get fb_fix_screeninfo: %s", strerror(errno));
return -1;
}

CAIROcontext_t* p_ctx = (CAIROcontext_t*)calloc(1, sizeof(CAIROcontext_t));

FT_Error status = FT_Init_FreeType(&p_ctx->ft_library);
if (status != 0) {
log_error("cairo: FT_Init_FreeType: Error: %d", status);
close(g_cairo_fb.fd);
free(p_ctx);

return -1;
}

p_info->width = g_cairo_fb.var.xres;
p_info->height = g_cairo_fb.var.yres;

p_ctx->font_size = 10.0; // Cairo default
p_ctx->text_align = TEXT_ALIGN_LEFT;
p_ctx->text_base = TEXT_BASE_ALPHABETIC;

p_info->v_ctx = p_ctx;

p_ctx->surface = cairo_image_surface_create(CAIRO_FORMAT_RGB24,
p_info->width, p_info->height);
return 0;
}

int device_close(device_info_t* p_info)
{
if (g_opts.debug_mode) {
log_info("cairo %s", __func__);
}
close(g_cairo_fb.fd);

CAIROcontext_t* p_ctx = (CAIROcontext_t*)p_info->v_ctx;
cairo_surface_destroy(p_ctx->surface);
free(p_ctx);
}

void device_poll()
{
}

void device_begin_render(driver_data_t* p_data)
{
if (g_opts.debug_mode) {
log_info("cairo %s", __func__);
}

CAIROcontext_t* p_ctx = (CAIROcontext_t*)p_data->v_ctx;

p_ctx->cr = cairo_create(p_ctx->surface);
cairo_set_source_rgb(p_ctx->cr, 0, 0, 0);
}

void device_begin_cursor_render(driver_data_t* p_data)
{
CAIROcontext_t* p_ctx = (CAIROcontext_t*)p_data->v_ctx;
cairo_translate(p_ctx->cr, p_data->cursor_pos[0], p_data->cursor_pos[1]);
}

void cairo_render_surface_to_fb(int fd, cairo_surface_t* surface)
{
cairo_surface_flush(surface);
uint8_t* surface_data = cairo_image_surface_get_data(surface);

int fb_x = g_cairo_fb.var.xres;
int fb_y = g_cairo_fb.var.yres;
size_t fb_size = fb_x * fb_y * 3;
uint8_t* fbbuff = mmap(NULL, fb_size, PROT_WRITE | PROT_READ, MAP_SHARED, fd, 0);

for(uint32_t i = 0, j = 0; i < fb_size; i += 3, j += 4) {
fbbuff[i+0] = surface_data[j+0];
fbbuff[i+1] = surface_data[j+1];
fbbuff[i+2] = surface_data[j+2];
}

munmap(fbbuff, fb_size);
}

void device_end_render(driver_data_t* p_data)
{
if (g_opts.debug_mode) {
log_info("cairo %s", __func__);
}

CAIROcontext_t* p_ctx = (CAIROcontext_t*)p_data->v_ctx;
cairo_render_surface_to_fb(g_cairo_fb.fd, p_ctx->surface);

cairo_destroy(p_ctx->cr);
}

void device_clear_color(float red, float green, float blue, float alpha)
{
log_warn("%s NOT IMPLEMENTED: {%.1f, %.1f, %.1f, %.1f", __func__, red, green, blue, alpha);
}

char* device_gl_error()
{
return NULL;
}
17 changes: 17 additions & 0 deletions c_src/device/cairo_ctx.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#pragma once

#include <cairo.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include "ops/script_ops.h"

typedef struct {
FT_Library ft_library;
float font_size;
float scale_x;
float scale_y;
text_align_t text_align;
text_base_t text_base;
cairo_surface_t* surface;
cairo_t* cr;
} CAIROcontext_t;
31 changes: 31 additions & 0 deletions c_src/font/cairo_font_ops.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include "cairo_ctx.h"
#include "font_ops.h"

#include <cairo-ft.h>
int32_t font_ops_create(void* v_ctx, font_t* p_font, uint32_t size)
{
CAIROcontext_t* p_ctx = (CAIROcontext_t*)v_ctx;
const char* name = p_font->id.p_data;
unsigned char* data = p_font->blob.p_data;

FT_Face ft_face;
FT_Error ft_status = FT_New_Memory_Face(p_ctx->ft_library, data, size, 0, &ft_face);

if (ft_status != 0) {
log_error("cairo: FT_New_Memory_Face: Error: %d", ft_status);
return -1;
}

cairo_font_face_t* font_face = cairo_ft_font_face_create_for_ft_face(ft_face, 0);
cairo_status_t status = cairo_font_face_set_user_data(font_face, NULL,
ft_face,
(cairo_destroy_func_t) FT_Done_Face);
if (status) {
log_error("cairo: Failed to create font face: %d", status);
cairo_font_face_destroy(font_face);
FT_Done_Face(ft_face);
return -1;
}

return (int32_t)font_face;
}
56 changes: 56 additions & 0 deletions c_src/image/cairo_image_ops.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include <arpa/inet.h>
#include <cairo.h>
#include <stdlib.h>

#include "comms.h"
#include "image_ops.h"

uint32_t convert_rgba_to_argb(uint32_t pixel)
{
return ((pixel & 0xFFFFFF00) >> 8) | ((pixel & 0xFF) << 24);
}

int32_t image_ops_create(void* v_ctx,
uint32_t width, uint32_t height,
void* p_pixels)
{
cairo_format_t format = CAIRO_FORMAT_ARGB32;
int stride = cairo_format_stride_for_width(format, width);
size_t num_pixels = width * height;
uint32_t* rgba_pixels = (uint32_t*)p_pixels;
uint32_t* argb_pixels = malloc(num_pixels * sizeof(uint32_t));

for(size_t i = 0; i < num_pixels; ++i) {
argb_pixels[i] = convert_rgba_to_argb(htonl(rgba_pixels[i]));
}

cairo_surface_t* surface
= cairo_image_surface_create_for_data((uint8_t*)argb_pixels,
format,
width, height, stride);

static cairo_user_data_key_t dummy_key;
cairo_surface_set_user_data(surface, &dummy_key, argb_pixels, free);

return (int32_t)surface;
}

void image_ops_update(void* v_ctx, uint32_t image_id, void* p_pixels)
{
cairo_surface_t* surface = (cairo_surface_t*)image_id;
uint32_t width = cairo_image_surface_get_width(surface);
uint32_t height = cairo_image_surface_get_height(surface);
size_t num_pixels = width * height;
uint32_t* rgba_pixels = (uint32_t*)p_pixels;
uint32_t* argb_pixels = (uint32_t*)cairo_image_surface_get_data(surface);

for(size_t i = 0; i < num_pixels; ++i) {
argb_pixels[i] = convert_rgba_to_argb(htonl(rgba_pixels[i]));
}
}

void image_ops_delete(void* v_ctx, uint32_t image_id)
{
cairo_surface_t* surface = (cairo_surface_t*)image_id;
cairo_surface_destroy(surface);
}
Loading

0 comments on commit 6387f93

Please sign in to comment.