Skip to content

Commit

Permalink
Initial cairo support
Browse files Browse the repository at this point in the history
Implements:
* image_ops_create
* image_ops_update
* image_ops_delete
* font_ops_create (Using cairo w/ freetype2)
* script_ops_draw_line
* script_ops_draw_triangle
* script_ops_draw_quad
* script_ops_draw_rect
* script_ops_draw_rrect
* script_ops_draw_arc
* script_ops_draw_sector
* 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_bezier_to
* script_ops_push_state
* script_ops_pop_state
* script_ops_transform
* script_ops_scale
* script_ops_rotate
* script_ops_translate
* script_ops_fill_color
* script_ops_fill_linear
* script_ops_fill_radial
* 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 28, 2023
1 parent b70e59e commit 704c03b
Show file tree
Hide file tree
Showing 7 changed files with 968 additions and 1 deletion.
11 changes: 11 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,17 @@ 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
173 changes: 173 additions & 0 deletions c_src/device/cairo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
#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;
}

scenic_cairo_ctx_t* p_ctx = calloc(1, sizeof(scenic_cairo_ctx_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_ctx->clear_color = (color_rgba_t){
// black opaque
.red = 0.0,
.green = 0.0,
.blue = 0.0,
.alpha = 1.0
};

p_info->v_ctx = p_ctx;

p_ctx->surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
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);

scenic_cairo_ctx_t* p_ctx = (scenic_cairo_ctx_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__);
}

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

p_ctx->cr = cairo_create(p_ctx->surface);

// Paint surface to clear color
cairo_set_source_rgba(p_ctx->cr,
p_ctx->clear_color.red,
p_ctx->clear_color.green,
p_ctx->clear_color.blue,
p_ctx->clear_color.alpha);
cairo_paint(p_ctx->cr);
}

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

void render_cairo_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__);
}

scenic_cairo_ctx_t* p_ctx = (scenic_cairo_ctx_t*)p_data->v_ctx;
render_cairo_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)
{
scenic_cairo_ctx_t* p_ctx = (scenic_cairo_ctx_t*)g_device_info.v_ctx;
p_ctx->clear_color = (color_rgba_t){
.red = red,
.green = green,
.blue = blue,
.alpha = alpha
};
}

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

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

typedef struct {
color_rgba_t clear_color;
FT_Library ft_library;
float font_size;
text_align_t text_align;
text_base_t text_base;
cairo_surface_t* surface;
cairo_t* cr;
} scenic_cairo_ctx_t;

typedef struct {
cairo_surface_t* surface;
cairo_pattern_t* pattern;
} image_data_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)
{
scenic_cairo_ctx_t* p_ctx = (scenic_cairo_ctx_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;
}
68 changes: 68 additions & 0 deletions c_src/image/cairo_image_ops.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#include <arpa/inet.h>
#include <cairo.h>
#include <stdlib.h>

#include "cairo_ctx.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));

if (!argb_pixels) return 0;

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

image_data_t* image_data = malloc(sizeof(image_data_t));

image_data->surface
= cairo_image_surface_create_for_data((uint8_t*)argb_pixels,
format,
width, height, stride);

image_data->pattern = cairo_pattern_create_for_surface(image_data->surface);

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

return (int32_t)image_data;
}

void image_ops_update(void* v_ctx, uint32_t image_id, void* p_pixels)
{
image_data_t* image_data = (image_data_t*)image_id;
uint32_t width = cairo_image_surface_get_width(image_data->surface);
uint32_t height = cairo_image_surface_get_height(image_data->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(image_data->surface);

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

cairo_pattern_destroy(image_data->pattern);
image_data->pattern = cairo_pattern_create_for_surface(image_data->surface);
}

void image_ops_delete(void* v_ctx, uint32_t image_id)
{
image_data_t* image_data = (image_data_t*)image_id;
cairo_surface_destroy(image_data->surface);
cairo_pattern_destroy(image_data->pattern);
free(image_data);
}
Loading

0 comments on commit 704c03b

Please sign in to comment.