video_layout

This commit is contained in:
Huw Pascoe 2019-04-01 20:44:11 +01:00
parent ea931428e1
commit 6aaa4df394
16 changed files with 2633 additions and 0 deletions

View File

@ -842,6 +842,10 @@ static void video_driver_free_internal(void)
bool is_threaded = video_driver_is_threaded_internal();
#endif
#ifdef HAVE_VIDEO_LAYOUT
video_layout_deinit();
#endif
command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
if (!video_driver_is_video_cache_context())
@ -1099,6 +1103,15 @@ static bool video_driver_init_internal(bool *video_is_threaded)
command_event(CMD_EVENT_OVERLAY_DEINIT, NULL);
command_event(CMD_EVENT_OVERLAY_INIT, NULL);
#ifdef HAVE_VIDEO_LAYOUT
if(settings->bools.video_layout_enable)
{
video_layout_init(video_driver_data, video_driver_layout_render_interface());
video_layout_load(settings->paths.path_video_layout);
video_layout_view_select(settings->uints.video_layout_selected_view);
}
#endif
if (!core_is_game_loaded())
video_driver_cached_frame_set(&dummy_pixels, 4, 4, 8);

470
gfx/video_layout.c Normal file
View File

@ -0,0 +1,470 @@
#include "video_layout.h"
#include "video_layout/view.h"
#include "video_driver.h"
#include <stdlib.h>
#include <string.h>
#include <formats/rxml.h>
#include <file/file_path.h>
#include <file/archive_file.h>
#include <compat/strl.h>
#include <verbosity.h>
bool load(view_array_t *view_array, rxml_document_t *doc);
typedef struct io
{
char *name;
int base_value;
int value;
}
io_t;
typedef struct video_layout_state
{
video_layout_render_info_t render_info;
const video_layout_render_interface_t *render;
view_array_t view_array;
view_t *view;
int view_index;
io_t *io;
int io_count;
void **images;
int images_count;
char *base_path;
bool is_archive;
bool view_changed;
}
video_layout_state_t;
static video_layout_state_t *video_layout_state = NULL;
void video_layout_init(void *video_driver_data, const video_layout_render_interface_t *render)
{
if (video_layout_state)
video_layout_deinit();
video_layout_state = (video_layout_state_t*)calloc(1, sizeof(video_layout_state_t));
video_layout_state->render_info.video_driver_data = video_driver_data;
video_layout_state->render = render;
vec_size((void**)&video_layout_state->images, sizeof(void*), 1);
video_layout_state->images[0] = NULL;
video_layout_state->images_count = 1;
}
void video_layout_deinit(void)
{
int i;
if (!video_layout_state)
return;
free(video_layout_state->base_path);
for (i = 1; i < video_layout_state->images_count; ++i)
{
video_layout_state->render->free_image(
video_layout_state->render_info.video_driver_data,
video_layout_state->images[i]
);
}
free(video_layout_state->images);
for (i = 0; i < video_layout_state->io_count; ++i)
free(video_layout_state->io[i].name);
free(video_layout_state->io);
view_array_deinit(&video_layout_state->view_array);
free(video_layout_state);
video_layout_state = NULL;
}
int video_layout_io_assign(const char *name, int base_value)
{
int index;
index = video_layout_state->io_count;
vec_size((void**)&video_layout_state->io, sizeof(io_t), ++video_layout_state->io_count);
video_layout_state->io[index].name = init_string(name);
video_layout_state->io[index].base_value = base_value;
video_layout_state->io[index].value = base_value;
return index;
}
int video_layout_io_find(const char *name)
{
int i;
for (i = 0; i < video_layout_state->io_count; ++i)
{
if (strcmp(video_layout_state->io[i].name, name) == 0)
return i;
}
return -1;
}
int video_layout_io_get(int index)
{
return video_layout_state->io[index].value;
}
void video_layout_io_set(int index, int value)
{
video_layout_state->io[index].value = value;
}
bool video_layout_load(const char *path)
{
rxml_document_t *doc;
bool result;
if(!path || !strlen(path))
return true;
video_layout_state->is_archive = path_is_compressed_file(path);
doc = NULL;
if(video_layout_state->is_archive)
{
void *buf;
int64_t len;
char respath[PATH_MAX_LENGTH];
strlcpy(respath, path, sizeof(respath));
strlcat(respath, "#", sizeof(respath));
set_string(&video_layout_state->base_path, respath);
strlcat(respath, "default.lay", sizeof(respath));
if (file_archive_compressed_read(respath, &buf, NULL, &len))
{
char *str;
if ((str = (char*)realloc(buf, (size_t)len + 1)))
{
str[(size_t)len] = '\0';
doc = rxml_load_document_string(str);
free(str);
}
else free(buf);
}
}
else
{
char respath[PATH_MAX_LENGTH];
fill_pathname_basedir(respath, path, sizeof(respath));
set_string(&video_layout_state->base_path, respath);
doc = rxml_load_document(path);
}
if (!doc)
{
RARCH_LOG("video_layout: unable to open file \"%s\"\n", path);
return false;
}
result = load(&video_layout_state->view_array, doc);
rxml_free_document(doc);
video_layout_view_select(video_layout_view_index());
return result;
}
bool video_layout_valid(void)
{
return video_layout_state && video_layout_state->view;
}
static int video_layout_load_image(const char *path)
{
struct texture_image image;
void *handle;
int index;
image.supports_rgba = video_driver_supports_rgba();
if (video_layout_state->is_archive)
{
void *buf;
int64_t len;
char respath[PATH_MAX_LENGTH];
strlcpy(respath, video_layout_state->base_path, sizeof(respath));
strlcat(respath, path, sizeof(respath));
if (!file_archive_compressed_read(respath, &buf, NULL, &len))
{
RARCH_LOG("video_layout: failed to decompress image: %s\n", respath);
return 0;
}
if (!image_texture_load_buffer(&image, image_texture_get_type(path), buf, (size_t)len))
{
free(buf);
RARCH_LOG("video_layout: failed to load image: %s\n", respath);
return 0;
}
free(buf);
}
else
{
char respath[PATH_MAX_LENGTH];
strlcpy(respath, video_layout_state->base_path, sizeof(respath));
strlcat(respath, path, sizeof(respath));
if (!image_texture_load(&image, respath))
{
RARCH_LOG("video_layout: failed to load image: %s\n", respath);
return 0;
}
}
handle = video_layout_state->render->take_image(
video_layout_state->render_info.video_driver_data, image);
if (!handle)
return 0;
index = video_layout_state->images_count;
vec_size((void**)&video_layout_state->images, sizeof(void*), ++video_layout_state->images_count);
video_layout_state->images[index] = handle;
return index;
}
int video_layout_view_count(void)
{
return video_layout_state->view_array.views_count;
}
const char *video_layout_view_name(int index)
{
return video_layout_state->view_array.views[index].name;
}
int video_layout_view_select(int index)
{
index = MAX(0, MIN(index, video_layout_state->view_array.views_count - 1));
video_layout_state->view_index = index;
video_layout_state->view = video_layout_state->view_array.views_count ?
&video_layout_state->view_array.views[index] : NULL;
video_layout_view_change();
return index;
}
int video_layout_view_cycle(void)
{
return video_layout_view_select(
(video_layout_state->view_index + 1) % video_layout_state->view_array.views_count);
}
int video_layout_view_index(void)
{
return video_layout_state->view_index;
}
void video_layout_view_change(void)
{
video_layout_state->view_changed = true;
}
bool video_layout_view_on_change(void)
{
if (video_layout_state->view_changed)
{
video_layout_state->view_changed = false;
return true;
}
return false;
}
void video_layout_view_fit_bounds(video_layout_bounds_t bounds)
{
view_t *view;
float c, dx, dy;
int i, j, k;
view = video_layout_state->view;
c = MIN(bounds.w / view->bounds.w, bounds.h / view->bounds.h);
dx = view->bounds.w * c;
dy = view->bounds.h * c;
view->render_bounds.w = dx;
view->render_bounds.h = dy;
view->render_bounds.x = (bounds.w - dx) / 2.f;
view->render_bounds.y = (bounds.h - dy) / 2.f;
for (i = 0; i < view->layers_count; ++i)
{
layer_t *layer;
layer = &view->layers[i];
for (j = 0; j < layer->elements_count; ++j)
{
element_t *elem;
elem = &layer->elements[j];
elem->render_bounds.x = elem->bounds.x * view->render_bounds.w + view->render_bounds.x;
elem->render_bounds.y = elem->bounds.y * view->render_bounds.h + view->render_bounds.y;
elem->render_bounds.w = elem->bounds.w * view->render_bounds.w;
elem->render_bounds.h = elem->bounds.h * view->render_bounds.h;
for (k = 0; k < elem->components_count; ++k)
{
component_t *comp;
comp = &elem->components[k];
comp->render_bounds.x = comp->bounds.x * elem->render_bounds.w + elem->render_bounds.x;
comp->render_bounds.y = comp->bounds.y * elem->render_bounds.h + elem->render_bounds.y;
comp->render_bounds.w = comp->bounds.w * elem->render_bounds.w;
comp->render_bounds.h = comp->bounds.h * elem->render_bounds.h;
if (comp->type == VIDEO_LAYOUT_C_SCREEN)
view->screens[comp->attr.screen.index] = comp->render_bounds;
}
}
}
}
int video_layout_layer_count(void)
{
return video_layout_state->view->layers_count;
}
void video_layout_layer_render(void *video_driver_frame_data, int index)
{
video_layout_render_info_t *info;
const video_layout_render_interface_t *r;
layer_t *layer;
int i, j;
info = &video_layout_state->render_info;
r = video_layout_state->render;
layer = &video_layout_state->view->layers[index];
info->video_driver_frame_data = video_driver_frame_data;
r->layer_begin(info);
for (i = 0; i < layer->elements_count; ++i)
{
element_t *elem;
elem = &layer->elements[i];
if (elem->o_bind != -1)
elem->state = video_layout_state->io[elem->o_bind].value;
for (j = 0; j < elem->components_count; ++j)
{
component_t *comp;
comp = &elem->components[j];
if (comp->enabled_state != -1)
{
if(comp->enabled_state != elem->state)
continue;
}
info->bounds = comp->render_bounds;
info->orientation = comp->orientation;
info->color = comp->color;
switch (comp->type)
{
case VIDEO_LAYOUT_C_UNKNOWN:
break;
case VIDEO_LAYOUT_C_SCREEN:
r->screen(info, comp->attr.screen.index);
break;
case VIDEO_LAYOUT_C_RECT:
r->rect(info);
break;
case VIDEO_LAYOUT_C_DISK:
r->ellipse(info);
break;
case VIDEO_LAYOUT_C_IMAGE:
if(!comp->attr.image.loaded)
{
comp->attr.image.image_idx = video_layout_load_image(comp->attr.image.file);
if(comp->attr.image.alpha_file)
comp->attr.image.alpha_idx = video_layout_load_image(comp->attr.image.alpha_file);
comp->attr.image.loaded = true;
}
r->image(info,
video_layout_state->images[comp->attr.image.image_idx],
video_layout_state->images[comp->attr.image.alpha_idx]);
break;
case VIDEO_LAYOUT_C_TEXT:
r->text(info, comp->attr.text.string);
break;
case VIDEO_LAYOUT_C_COUNTER:
r->counter(info, MIN(elem->state, comp->attr.counter.max_state));
break;
case VIDEO_LAYOUT_C_DOTMATRIX_X1:
r->led_dot(info, 1, elem->state);
break;
case VIDEO_LAYOUT_C_DOTMATRIX_H5:
r->led_dot(info, 5, elem->state);
break;
case VIDEO_LAYOUT_C_DOTMATRIX_H8:
r->led_dot(info, 8, elem->state);
break;
case VIDEO_LAYOUT_C_LED_7:
r->led_seg(info, VIDEO_LAYOUT_LED_7, elem->state);
break;
case VIDEO_LAYOUT_C_LED_8_GTS1:
r->led_seg(info, VIDEO_LAYOUT_LED_8_GTS1, elem->state);
break;
case VIDEO_LAYOUT_C_LED_14:
r->led_seg(info, VIDEO_LAYOUT_LED_14, elem->state);
break;
case VIDEO_LAYOUT_C_LED_14_SC:
r->led_seg(info, VIDEO_LAYOUT_LED_14_SC, elem->state);
break;
case VIDEO_LAYOUT_C_LED_16:
r->led_seg(info, VIDEO_LAYOUT_LED_16, elem->state);
break;
case VIDEO_LAYOUT_C_LED_16_SC:
r->led_seg(info, VIDEO_LAYOUT_LED_16_SC, elem->state);
break;
case VIDEO_LAYOUT_C_REEL:
/* not implemented */
break;
}
}
}
r->layer_end(info, layer->blend);
}
const video_layout_bounds_t *video_layout_screen(int index)
{
return &video_layout_state->view->screens[index];
}
int video_layout_screen_count(void)
{
return video_layout_state->view->screens_count;
}

77
gfx/video_layout.h Normal file
View File

@ -0,0 +1,77 @@
#ifndef VIDEO_LAYOUT_H
#define VIDEO_LAYOUT_H
#include "video_layout/types.h"
#include <boolean.h>
#include <formats/image.h>
typedef struct video_layout_render_info
{
void *video_driver_data;
void *video_driver_frame_data;
video_layout_bounds_t bounds;
video_layout_orientation_t orientation;
video_layout_color_t color;
}
video_layout_render_info_t;
typedef enum video_layout_led
{
VIDEO_LAYOUT_LED_7, /* digit with . */
VIDEO_LAYOUT_LED_8_GTS1, /* digit with vertical split */
VIDEO_LAYOUT_LED_14, /* alphanumeric */
VIDEO_LAYOUT_LED_14_SC, /* alphanumeric with ., */
VIDEO_LAYOUT_LED_16, /* full alphanumeric */
VIDEO_LAYOUT_LED_16_SC /* full alphanumeric with ., */
}
video_layout_led_t;
typedef struct video_layout_render_interface
{
void *(*take_image) (void *video_driver_data, struct texture_image image);
void (*free_image) (void *video_driver_data, void *image);
void (*layer_begin) (const video_layout_render_info_t *info);
void (*screen) (const video_layout_render_info_t *info, int screen_index);
void (*image) (const video_layout_render_info_t *info, void *image_handle, void *alpha_handle);
void (*text) (const video_layout_render_info_t *info, const char *str);
void (*counter) (const video_layout_render_info_t *info, int value);
void (*rect) (const video_layout_render_info_t *info);
void (*ellipse) (const video_layout_render_info_t *info);
void (*led_dot) (const video_layout_render_info_t *info, int dot_count, int dot_mask);
void (*led_seg) (const video_layout_render_info_t *info, video_layout_led_t seg_layout, int seg_mask);
void (*layer_end) (const video_layout_render_info_t *info, video_layout_blend_t blend_type);
}
video_layout_render_interface_t;
void video_layout_init (void *video_driver_data, const video_layout_render_interface_t *render);
void video_layout_deinit (void);
int video_layout_io_assign (const char *name, int base_value);
int video_layout_io_get (int index);
void video_layout_io_set (int index, int value);
bool video_layout_load (const char *path);
bool video_layout_valid (void);
int video_layout_view_count (void);
const char *video_layout_view_name (int index);
int video_layout_view_select (int index);
int video_layout_view_cycle (void);
int video_layout_view_index (void);
void video_layout_view_change (void);
bool video_layout_view_on_change (void);
void video_layout_view_fit_bounds (video_layout_bounds_t bounds);
int video_layout_layer_count (void);
void video_layout_layer_render (void *video_driver_frame_data, int index);
const video_layout_bounds_t
*video_layout_screen (int index);
int video_layout_screen_count (void);
#endif

View File

@ -0,0 +1,165 @@
#include "component.h"
#include <stdlib.h>
#include <string.h>
void component_init(component_t *comp, comp_type_t type)
{
comp->type = type;
comp->bounds = make_bounds();
comp->render_bounds = make_bounds_unit();
comp->orientation = VIDEO_LAYOUT_ROT0;
comp->color = make_color_white();
comp->enabled_state = -1;
switch (comp->type)
{
case VIDEO_LAYOUT_C_UNKNOWN:
break;
case VIDEO_LAYOUT_C_SCREEN:
comp->attr.screen.index = 0;
break;
case VIDEO_LAYOUT_C_RECT:
break;
case VIDEO_LAYOUT_C_DISK:
break;
case VIDEO_LAYOUT_C_IMAGE:
comp->attr.image.file = NULL;
comp->attr.image.alpha_file = NULL;
comp->attr.image.image_idx = 0;
comp->attr.image.alpha_idx = 0;
comp->attr.image.loaded = false;
break;
case VIDEO_LAYOUT_C_TEXT:
comp->attr.text.string = NULL;
comp->attr.text.align = VIDEO_LAYOUT_TEXT_ALIGN_CENTER;
break;
case VIDEO_LAYOUT_C_COUNTER:
comp->attr.counter.digits = 2;
comp->attr.counter.max_state = 999;
comp->attr.counter.align = VIDEO_LAYOUT_TEXT_ALIGN_CENTER;
break;
case VIDEO_LAYOUT_C_DOTMATRIX_X1:
break;
case VIDEO_LAYOUT_C_DOTMATRIX_H5:
break;
case VIDEO_LAYOUT_C_DOTMATRIX_H8:
break;
case VIDEO_LAYOUT_C_LED_7:
break;
case VIDEO_LAYOUT_C_LED_8_GTS1:
break;
case VIDEO_LAYOUT_C_LED_14:
break;
case VIDEO_LAYOUT_C_LED_14_SC:
break;
case VIDEO_LAYOUT_C_LED_16:
break;
case VIDEO_LAYOUT_C_LED_16_SC:
break;
case VIDEO_LAYOUT_C_REEL:
break;
}
}
void component_copy(component_t *comp, const component_t *src)
{
comp->type = src->type;
comp->bounds = src->bounds;
comp->render_bounds = src->render_bounds;
comp->orientation = src->orientation;
comp->color = src->color;
comp->enabled_state = src->enabled_state;
switch (comp->type)
{
case VIDEO_LAYOUT_C_UNKNOWN:
break;
case VIDEO_LAYOUT_C_SCREEN:
comp->attr.screen.index = src->attr.screen.index;
break;
case VIDEO_LAYOUT_C_RECT:
break;
case VIDEO_LAYOUT_C_DISK:
break;
case VIDEO_LAYOUT_C_IMAGE:
comp->attr.image.file = init_string(src->attr.image.file);
comp->attr.image.alpha_file = init_string(src->attr.image.alpha_file);
comp->attr.image.image_idx = src->attr.image.image_idx;
comp->attr.image.alpha_idx = src->attr.image.alpha_idx;
comp->attr.image.loaded = src->attr.image.loaded;
break;
case VIDEO_LAYOUT_C_TEXT:
comp->attr.text.string = init_string(src->attr.text.string);
comp->attr.text.align = src->attr.text.align;
break;
case VIDEO_LAYOUT_C_COUNTER:
comp->attr.counter.digits = src->attr.counter.digits;
comp->attr.counter.max_state = src->attr.counter.max_state;
comp->attr.counter.align = src->attr.counter.align;
break;
case VIDEO_LAYOUT_C_DOTMATRIX_X1:
break;
case VIDEO_LAYOUT_C_DOTMATRIX_H5:
break;
case VIDEO_LAYOUT_C_DOTMATRIX_H8:
break;
case VIDEO_LAYOUT_C_LED_7:
break;
case VIDEO_LAYOUT_C_LED_8_GTS1:
break;
case VIDEO_LAYOUT_C_LED_14:
break;
case VIDEO_LAYOUT_C_LED_14_SC:
break;
case VIDEO_LAYOUT_C_LED_16:
break;
case VIDEO_LAYOUT_C_LED_16_SC:
break;
case VIDEO_LAYOUT_C_REEL:
break;
}
}
void component_deinit(component_t *comp)
{
switch (comp->type)
{
case VIDEO_LAYOUT_C_UNKNOWN:
break;
case VIDEO_LAYOUT_C_SCREEN:
break;
case VIDEO_LAYOUT_C_RECT:
break;
case VIDEO_LAYOUT_C_DISK:
break;
case VIDEO_LAYOUT_C_IMAGE:
free(comp->attr.image.file);
free(comp->attr.image.alpha_file);
break;
case VIDEO_LAYOUT_C_TEXT:
free(comp->attr.text.string);
break;
case VIDEO_LAYOUT_C_COUNTER:
break;
case VIDEO_LAYOUT_C_DOTMATRIX_X1:
break;
case VIDEO_LAYOUT_C_DOTMATRIX_H5:
break;
case VIDEO_LAYOUT_C_DOTMATRIX_H8:
break;
case VIDEO_LAYOUT_C_LED_7:
break;
case VIDEO_LAYOUT_C_LED_8_GTS1:
break;
case VIDEO_LAYOUT_C_LED_14:
break;
case VIDEO_LAYOUT_C_LED_14_SC:
break;
case VIDEO_LAYOUT_C_LED_16:
break;
case VIDEO_LAYOUT_C_LED_16_SC:
break;
case VIDEO_LAYOUT_C_REEL:
break;
}
}

View File

@ -0,0 +1,53 @@
#ifndef VIDEO_LAYOUT_COMPONENT_H
#define VIDEO_LAYOUT_COMPONENT_H
#include "internal.h"
#include "component_attr.h"
typedef enum comp_type
{
VIDEO_LAYOUT_C_UNKNOWN,
VIDEO_LAYOUT_C_SCREEN,
VIDEO_LAYOUT_C_RECT,
VIDEO_LAYOUT_C_DISK,
VIDEO_LAYOUT_C_IMAGE,
VIDEO_LAYOUT_C_TEXT,
VIDEO_LAYOUT_C_COUNTER,
VIDEO_LAYOUT_C_DOTMATRIX_X1,
VIDEO_LAYOUT_C_DOTMATRIX_H5,
VIDEO_LAYOUT_C_DOTMATRIX_H8,
VIDEO_LAYOUT_C_LED_7,
VIDEO_LAYOUT_C_LED_8_GTS1,
VIDEO_LAYOUT_C_LED_14,
VIDEO_LAYOUT_C_LED_14_SC,
VIDEO_LAYOUT_C_LED_16,
VIDEO_LAYOUT_C_LED_16_SC,
VIDEO_LAYOUT_C_REEL
}
comp_type_t;
union comp_attr
{
c_attr_screen_t screen;
c_attr_image_t image;
c_attr_text_t text;
c_attr_counter_t counter;
};
typedef struct component
{
comp_type_t type;
video_layout_bounds_t bounds;
video_layout_bounds_t render_bounds;
video_layout_orientation_t orientation;
video_layout_color_t color;
int enabled_state;
union comp_attr attr;
}
component_t;
void component_init (component_t *comp, comp_type_t type);
void component_copy (component_t *comp, const component_t *src);
void component_deinit (component_t *comp);
#endif

View File

@ -0,0 +1,35 @@
#ifndef VIDEO_LAYOUT_COMPONENT_ATTR_H
#define VIDEO_LAYOUT_COMPONENT_ATTR_H
typedef struct c_attr_screen
{
int index;
}
c_attr_screen_t;
typedef struct c_attr_image
{
char *file;
char *alpha_file;
int image_idx;
int alpha_idx;
bool loaded;
}
c_attr_image_t;
typedef struct c_attr_text
{
char *string;
video_layout_text_align_t align;
}
c_attr_text_t;
typedef struct c_attr_counter
{
int digits;
int max_state;
video_layout_text_align_t align;
}
c_attr_counter_t;
#endif

View File

@ -0,0 +1,78 @@
#include "element.h"
#include <stdlib.h>
void element_init(element_t *elem, const char *name, int components_count)
{
elem->name = init_string(name);
elem->state = -1;
elem->o_bind = -1;
elem->i_bind = -1;
elem->i_mask = -1;
elem->i_raw = false;
elem->bounds = make_bounds();
elem->render_bounds = make_bounds_unit();
elem->components = (component_t*)(components_count > 0 ?
calloc(components_count, sizeof(component_t)) : NULL);
elem->components_count = components_count;
}
void element_copy(element_t *elem, const element_t *src)
{
int i;
elem->name = init_string(src->name);
elem->state = src->state;
elem->bounds = src->bounds;
elem->render_bounds = src->render_bounds;
elem->components = (component_t*)(src->components_count > 0 ?
calloc(src->components_count, sizeof(component_t)) : NULL);
for (i = 0; i < src->components_count; ++i)
component_copy(&elem->components[i], &src->components[i]);
elem->components_count = src->components_count;
}
void element_deinit(element_t *elem)
{
int i;
for (i = 0; i < elem->components_count; ++i)
component_deinit(&elem->components[i]);
free(elem->components);
free(elem->name);
}
void element_apply_orientation(element_t *elem, video_layout_orientation_t orientation)
{
int i;
for (i = 0; i < elem->components_count; ++i)
{
component_t *comp;
comp = &elem->components[i];
comp->orientation ^= orientation;
if (orientation & VIDEO_LAYOUT_SWAP_XY)
{
video_layout_bounds_t b;
b = comp->bounds;
comp->bounds.x = b.y;
comp->bounds.y = b.x;
comp->bounds.w = b.h;
comp->bounds.h = b.w;
}
if (orientation & VIDEO_LAYOUT_FLIP_X)
comp->bounds.x = 1.0f - comp->bounds.x - comp->bounds.w;
if (orientation & VIDEO_LAYOUT_FLIP_Y)
comp->bounds.y = 1.0f - comp->bounds.y - comp->bounds.h;
}
}

View File

@ -0,0 +1,28 @@
#ifndef VIDEO_LAYOUT_ELEMENT_H
#define VIDEO_LAYOUT_ELEMENT_H
#include "internal.h"
#include "component.h"
typedef struct element
{
char *name;
int state;
int o_bind;
int i_bind;
int i_mask;
bool i_raw;
video_layout_bounds_t bounds;
video_layout_bounds_t render_bounds;
component_t *components;
int components_count;
}
element_t;
void element_init (element_t *elem, const char *name, int components_count);
void element_copy (element_t *elem, const element_t *src);
void element_deinit (element_t *elem);
void element_apply_orientation (element_t *elem, video_layout_orientation_t orientation);
#endif

181
gfx/video_layout/internal.c Normal file
View File

@ -0,0 +1,181 @@
#include "internal.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <compat/posix_string.h>
char *init_string(const char *src)
{
return src ? strdup(src) : NULL;
}
void set_string(char **string, const char *src)
{
free(*string);
*string = src ? strdup(src) : NULL;
}
bool vec_size(void **target, size_t elem_size, int count)
{
const int seg = 4;
if (--count % seg == 0)
{
void *resized = realloc(*target, elem_size * (count + seg));
if (!resized)
return false;
*target = resized;
}
return true;
}
bool is_decimal(const char *str)
{
float v;
v = 0.0f;
sscanf(str, "%f", &v);
return (v && v != (int)v);
}
int get_int(const char *str)
{
int res;
res = 0;
if (str[0] == '#')
++str;
if (str[0] == '$')
{
unsigned hex;
++str;
sscanf(str, "%x", &hex);
res = (int)hex;
}
else
{
sscanf(str, "%i", &res);
}
return res;
}
float get_dec(const char *str)
{
float res;
res = 0.0f;
sscanf(str, "%f", &res);
return res;
}
video_layout_color_t make_color(void)
{
video_layout_color_t color;
color.r = 0.0f;
color.g = 0.0f;
color.b = 0.0f;
color.a = 0.0f;
return color;
}
video_layout_color_t make_color_white(void)
{
video_layout_color_t color;
color.r = 1.0f;
color.g = 1.0f;
color.b = 1.0f;
color.a = 1.0f;
return color;
}
video_layout_color_t make_color_v(float v)
{
video_layout_color_t color;
color.r = v;
color.g = v;
color.b = v;
color.a = 1.0f;
return color;
}
video_layout_color_t make_color_rgb(float r, float g, float b)
{
video_layout_color_t color;
color.r = r;
color.g = g;
color.b = b;
color.a = 1.0f;
return color;
}
video_layout_color_t make_color_rgba(float r, float g, float b, float a)
{
video_layout_color_t color;
color.r = r;
color.g = g;
color.b = b;
color.a = a;
return color;
}
void color_mod(video_layout_color_t *dst, const video_layout_color_t *src)
{
dst->r *= src->r;
dst->g *= src->g;
dst->b *= src->b;
dst->a *= src->a;
}
video_layout_bounds_t make_bounds(void)
{
video_layout_bounds_t bounds;
bounds.x = 0.0f;
bounds.y = 0.0f;
bounds.w = 0.0f;
bounds.h = 0.0f;
return bounds;
}
video_layout_bounds_t make_bounds_unit(void)
{
video_layout_bounds_t bounds;
bounds.x = 0.0f;
bounds.y = 0.0f;
bounds.w = 1.0f;
bounds.h = 1.0f;
return bounds;
}
video_layout_bounds_t bounds_union(const video_layout_bounds_t *a, const video_layout_bounds_t *b)
{
video_layout_bounds_t bounds;
if (!bounds_valid(a)) return *b;
if (!bounds_valid(b)) return *a;
bounds.x = MIN(a->x, b->x);
bounds.y = MIN(a->y, b->y);
bounds.w = MAX(a->x + a->w, b->x + b->w) - bounds.x;
bounds.h = MAX(a->y + a->h, b->y + b->h) - bounds.y;
return bounds;
}
void bounds_scale(video_layout_bounds_t *dst, const video_layout_bounds_t *dim)
{
dst->x *= dim->w;
dst->y *= dim->h;
dst->w *= dim->w;
dst->h *= dim->h;
}
bool bounds_valid(const video_layout_bounds_t *bounds)
{
return (bounds->w > 0 && bounds->h > 0);
}

View File

@ -0,0 +1,29 @@
#ifndef VIDEO_LAYOUT_INTERNAL_H
#define VIDEO_LAYOUT_INTERNAL_H
#include "types.h"
#include <retro_miscellaneous.h>
#include <boolean.h>
#include <stddef.h>
char *init_string (const char *src);
void set_string (char **string, const char *src);
bool vec_size (void **target, size_t elem_size, int count);
bool is_decimal (const char *str);
int get_int (const char *str);
float get_dec (const char *str);
video_layout_color_t make_color (void);
video_layout_color_t make_color_white (void);
video_layout_color_t make_color_v (float v);
video_layout_color_t make_color_rgb (float r, float g, float b);
video_layout_color_t make_color_rgba (float r, float g, float b, float a);
void color_mod (video_layout_color_t *dst, const video_layout_color_t *src);
video_layout_bounds_t make_bounds (void);
video_layout_bounds_t make_bounds_unit (void);
video_layout_bounds_t bounds_union (const video_layout_bounds_t *a, const video_layout_bounds_t *b);
void bounds_scale (video_layout_bounds_t *dst, const video_layout_bounds_t *dim);
bool bounds_valid (const video_layout_bounds_t *bounds);
#endif

762
gfx/video_layout/load.c Normal file
View File

@ -0,0 +1,762 @@
#include "internal.h"
#include "view.h"
#include "scope.h"
#include <formats/rxml.h>
#include <verbosity.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int video_layout_io_find(const char *name);
static const char *const comp_type_str[] = {
NULL, /* VIDEO_LAYOUT_C_UNKNOWN */
NULL, /* VIDEO_LAYOUT_C_SCREEN */
"rect",
"disk",
"image",
"text",
"dotmatrixdot",
"dotmatrix5dot",
"dotmatrix",
"led7seg",
"led8seg_gts1",
"led14seg",
"led14segsc",
"led16seg",
"led16segsc",
"simplecounter",
"reel"
};
static const char *const video_layout_internal_device_params[] =
{
"devicetag" , ":",
"devicebasetag" , "root",
"devicename" , "RetroArch",
"deviceshortname" , "libretro"
};
static const char *const video_layout_internal_screen_params[] =
{
"scr#physicalxaspect" , "1",
"scr#physicalyaspect" , "1",
"scr#nativexaspect" , "1",
"scr#nativeyaspect" , "1",
"scr#width" , "1",
"scr#height" , "1"
};
static int child_count(rxml_node_t *node)
{
int res;
rxml_node_t *child;
res = 0;
for (child = node->children; child; child = child->next)
++res;
return res;
}
static comp_type_t comp_type_from_str(const char *s)
{
size_t i;
for (i = 2; i < ARRAY_SIZE(comp_type_str); ++i)
{
if (strcmp(s, comp_type_str[i]) == 0)
return (comp_type_t)(int)i;
}
return VIDEO_LAYOUT_C_UNKNOWN;
}
static void init_device_params(scope_t *scope)
{
size_t i;
for (i = 0; i < ARRAY_SIZE(video_layout_internal_device_params); i += 2)
{
scope_param(scope, video_layout_internal_device_params[i], video_layout_internal_device_params[i + 1]);
}
}
static void init_screen_params(scope_t *scope, int screen_index)
{
char buf[64];
size_t i;
for (i = 0; i < ARRAY_SIZE(video_layout_internal_screen_params); i += 2)
{
strcpy(buf, video_layout_internal_screen_params[i + 1]);
buf[3] = '0' + screen_index;
scope_param(scope, video_layout_internal_screen_params[i], buf);
}
}
static video_layout_bounds_t parse_bounds(scope_t *scope, rxml_node_t *node)
{
video_layout_bounds_t bounds;
const char *prop;
bounds = make_bounds_unit();
if ((prop = scope_eval(scope, rxml_node_attrib(node, "x")))) bounds.x = get_dec(prop);
if ((prop = scope_eval(scope, rxml_node_attrib(node, "y")))) bounds.y = get_dec(prop);
if ((prop = scope_eval(scope, rxml_node_attrib(node, "width")))) bounds.w = get_dec(prop);
if ((prop = scope_eval(scope, rxml_node_attrib(node, "height")))) bounds.h = get_dec(prop);
if ((prop = scope_eval(scope, rxml_node_attrib(node, "left")))) bounds.x = get_dec(prop);
if ((prop = scope_eval(scope, rxml_node_attrib(node, "top")))) bounds.y = get_dec(prop);
if ((prop = scope_eval(scope, rxml_node_attrib(node, "right")))) bounds.w = get_dec(prop) - bounds.x;
if ((prop = scope_eval(scope, rxml_node_attrib(node, "bottom")))) bounds.h = get_dec(prop) - bounds.y;
return bounds;
}
static video_layout_color_t parse_color(scope_t *scope, rxml_node_t *node)
{
video_layout_color_t color;
const char *prop;
color = make_color_white();
if ((prop = scope_eval(scope, rxml_node_attrib(node, "red")))) color.r = get_dec(prop);
if ((prop = scope_eval(scope, rxml_node_attrib(node, "green")))) color.g = get_dec(prop);
if ((prop = scope_eval(scope, rxml_node_attrib(node, "blue")))) color.b = get_dec(prop);
if ((prop = scope_eval(scope, rxml_node_attrib(node, "alpha")))) color.a = get_dec(prop);
return color;
}
static video_layout_orientation_t parse_orientation(scope_t *scope, rxml_node_t *node)
{
video_layout_orientation_t result;
const char *prop;
result = VIDEO_LAYOUT_ROT0;
if ((prop = scope_eval(scope, rxml_node_attrib(node, "rotate"))))
{
if (strcmp(prop, "90") == 0)
result = VIDEO_LAYOUT_ROT90;
else if (strcmp(prop, "180") == 0)
result = VIDEO_LAYOUT_ROT180;
else if (strcmp(prop, "270") == 0)
result = VIDEO_LAYOUT_ROT270;
}
if ((prop = scope_eval(scope, rxml_node_attrib(node, "swapxy"))))
{
if (strcmp(prop, "no") != 0)
result ^= VIDEO_LAYOUT_SWAP_XY;
}
if ((prop = scope_eval(scope, rxml_node_attrib(node, "flipx"))))
{
if (strcmp(prop, "no") != 0)
result ^= VIDEO_LAYOUT_FLIP_X;
}
if ((prop = scope_eval(scope, rxml_node_attrib(node, "flipy"))))
{
if (strcmp(prop, "no") != 0)
result ^= VIDEO_LAYOUT_FLIP_Y;
}
return result;
}
static bool load_param(scope_t *scope, rxml_node_t *node, bool can_repeat)
{
const char *name;
const char *value;
const char *start;
if (!(name = rxml_node_attrib(node, "name")))
{
RARCH_LOG("video_layout: <param> is missing 'name' attribute\n");
return false;
}
value = rxml_node_attrib(node, "value");
start = rxml_node_attrib(node, "start");
if (can_repeat && start)
{
const char *inc = rxml_node_attrib(node, "increment");
const char *ls = rxml_node_attrib(node, "lshift");
const char *rs = rxml_node_attrib(node, "rshift");
if (inc || ls || rs)
{
scope_generator(scope, name, start, inc, ls, rs);
}
else
{
RARCH_LOG("video_layout: invalid generator <param name=\"%s\" /> missing increment/shift\n",
scope_eval(scope, name));
return false;
}
}
else if (name && value)
{
scope_param(scope, name, value);
}
else
{
RARCH_LOG("video_layout: invalid parameter <param name=\"%s\" /> missing value\n",
scope_eval(scope, name));
return false;
}
return true;
}
static bool load_component(scope_t *scope, component_t *comp, rxml_node_t *node)
{
comp_type_t type;
bool result;
const char *state;
const char *attr;
rxml_node_t *n;
type = comp_type_from_str(node->name);
result = true;
if (type == VIDEO_LAYOUT_C_UNKNOWN)
{
RARCH_LOG("video_layout: invalid component <%s />\n", node->name);
return false;
}
component_init(comp, type);
if ((state = rxml_node_attrib(node, "state")))
comp->enabled_state = get_int(scope_eval(scope, state));
for (n = node->children; n; n = n->next)
{
if (strcmp(n->name, "bounds") == 0)
comp->bounds = parse_bounds(scope, n);
else if (strcmp(n->name, "color") == 0)
comp->color = parse_color(scope, n);
}
switch (comp->type)
{
case VIDEO_LAYOUT_C_UNKNOWN:
break;
case VIDEO_LAYOUT_C_SCREEN:
break;
case VIDEO_LAYOUT_C_RECT:
break;
case VIDEO_LAYOUT_C_DISK:
break;
case VIDEO_LAYOUT_C_IMAGE:
{
if (!(attr = rxml_node_attrib(node, "file")))
{
RARCH_LOG("video_layout: invalid component <%s />, missing 'file' attribute\n", node->name);
result = false;
}
set_string(&comp->attr.image.file, scope_eval(scope, attr));
if ((attr = rxml_node_attrib(node, "alphafile")))
set_string(&comp->attr.image.alpha_file, scope_eval(scope, attr));
}
break;
case VIDEO_LAYOUT_C_TEXT:
{
if (!(attr = rxml_node_attrib(node, "string")))
{
RARCH_LOG("video_layout: invalid component <%s />, missing 'string' attribute\n", node->name);
result = false;
}
set_string(&comp->attr.text.string, scope_eval(scope, attr));
if ((attr = rxml_node_attrib(node, "align")))
comp->attr.text.align = (video_layout_text_align_t)get_int(scope_eval(scope, attr));
}
break;
case VIDEO_LAYOUT_C_COUNTER:
{
if ((attr = rxml_node_attrib(node, "digits")))
comp->attr.counter.digits = get_int(scope_eval(scope, attr));
if ((attr = rxml_node_attrib(node, "maxstate")))
comp->attr.counter.max_state = get_int(scope_eval(scope, attr));
if ((attr = rxml_node_attrib(node, "align")))
comp->attr.counter.align = (video_layout_text_align_t)get_int(scope_eval(scope, attr));
}
break;
case VIDEO_LAYOUT_C_DOTMATRIX_X1:
break;
case VIDEO_LAYOUT_C_DOTMATRIX_H5:
break;
case VIDEO_LAYOUT_C_DOTMATRIX_H8:
break;
case VIDEO_LAYOUT_C_LED_7:
break;
case VIDEO_LAYOUT_C_LED_8_GTS1:
break;
case VIDEO_LAYOUT_C_LED_14:
break;
case VIDEO_LAYOUT_C_LED_14_SC:
break;
case VIDEO_LAYOUT_C_LED_16:
break;
case VIDEO_LAYOUT_C_LED_16_SC:
break;
case VIDEO_LAYOUT_C_REEL:
break;
}
return result;
}
static bool load_element(scope_t *scope, rxml_node_t *node)
{
const char *name;
const char *state;
bool result;
int i;
element_t *elem;
rxml_node_t *n;
video_layout_bounds_t dim;
result = true;
if (!(name = rxml_node_attrib(node, "name")))
{
RARCH_LOG("video_layout: <element> is missing 'name' attribute\n");
return false;
}
elem = scope_add_element(scope);
element_init(elem, scope_eval(scope, name), child_count(node));
if ((state = rxml_node_attrib(node, "defstate")))
elem->state = get_int(scope_eval(scope, state));
i = 0;
for (n = node->children; n; n = n->next, ++i)
{
component_t *comp;
comp = &elem->components[i];
if (load_component(scope, comp, n))
elem->bounds = bounds_union(&elem->bounds, &comp->bounds);
else
result = false;
}
if (bounds_valid(&elem->bounds))
{
dim.x = elem->bounds.x / elem->bounds.w;
dim.y = elem->bounds.y / elem->bounds.h;
dim.w = 1.0f / elem->bounds.w;
dim.h = 1.0f / elem->bounds.h;
}
else
{
dim = make_bounds_unit();
}
for (i = 0; i < elem->components_count; ++i)
{
component_t *comp;
comp = &elem->components[i];
if (bounds_valid(&comp->bounds))
bounds_scale(&comp->bounds, &dim);
else
comp->bounds = dim;
comp->bounds.x -= dim.x;
comp->bounds.y -= dim.y;
}
elem->bounds = make_bounds_unit();
return result;
}
static bool load_screen(scope_t *scope, element_t *elem, rxml_node_t *node)
{
const char *index;
component_t *comp;
index = rxml_node_attrib(node, "index");
element_init(elem, NULL, 1);
comp = &elem->components[0];
component_init(comp, VIDEO_LAYOUT_C_SCREEN);
comp->bounds = make_bounds_unit();
comp->attr.screen.index = get_int(scope_eval(scope, index));
return true;
}
static void merge_group(scope_t *scope, view_t *view, view_t *group,
bool has_bounds, video_layout_bounds_t n_bounds, video_layout_orientation_t n_orient, video_layout_color_t n_color)
{
bool constrain;
int i, j, k;
constrain = bounds_valid(&n_bounds);
for (i = 0; i < group->layers_count; ++i)
{
layer_t *group_layer;
layer_t *layer;
group_layer = &group->layers[i];
layer = view_emplace_layer(view, group_layer->name);
for (j = 0; j < group_layer->elements_count; ++j)
{
element_t *elem;
elem = layer_add_element(layer);
element_copy(elem, &group_layer->elements[j]);
for (k = 0; k < elem->components_count; ++k)
color_mod(&elem->components->color, &n_color);
if (n_orient)
element_apply_orientation(elem, n_orient);
if (constrain)
{
bounds_scale(&elem->bounds, &n_bounds);
elem->bounds.x += n_bounds.x;
elem->bounds.y += n_bounds.y;
}
if (!has_bounds)
view->bounds = bounds_union(&view->bounds, &elem->bounds);
}
}
}
static bool load_view(scope_t *scope, view_t *view, rxml_node_t *node, bool is_named)
{
bool result, has_bounds;
rxml_node_t *n;
rxml_node_t *o;
int i;
if (is_named)
{
const char *name;
if (!(name = rxml_node_attrib(node, "name")))
{
RARCH_LOG("video_layout: <view> is missing 'name' attribute\n");
return false;
}
view_init(view, scope_eval(scope, name));
}
result = true;
has_bounds = false;
for (n = node->children; n; n = n->next)
{
video_layout_color_t n_color;
video_layout_bounds_t n_bounds;
video_layout_orientation_t n_orient;
if (strcmp(n->name, "param") == 0)
{
if (!load_param(scope, n, true))
result = false;
continue;
}
else if (strcmp(n->name, "bounds") == 0)
{
view->bounds = parse_bounds(scope, n);
has_bounds = true;
continue;
}
n_color = make_color_white();
n_bounds = make_bounds();
n_orient = VIDEO_LAYOUT_ROT0;
for (o = n->children; o; o = o->next)
{
if (strcmp(o->name, "color") == 0)
n_color = parse_color(scope, o);
else if (strcmp(o->name, "bounds") == 0)
n_bounds = parse_bounds(scope, o);
else if (strcmp(o->name, "orientation") == 0)
n_orient = parse_orientation(scope, o);
}
if (strcmp(n->name, "group") == 0)
{
const char *ref;
if ((ref = rxml_node_attrib(n, "ref")))
{
view_t *group;
if ((group = scope_find_group(scope, scope_eval(scope, ref))))
{
merge_group(scope, view, group, has_bounds, n_bounds, n_orient, n_color);
}
else
{
RARCH_LOG("video_layout: group \"%s\" is missing\n", scope_eval(scope, ref));
result = false;
}
}
else
{
RARCH_LOG("video_layout: <group> is missing 'ref' attribute\n");
result = false;
}
}
else if (strcmp(n->name, "repeat") == 0)
{
const char *count_s;
int count;
if (!(count_s = rxml_node_attrib(n, "count")))
{
RARCH_LOG("video_layout: <repeat> is missing 'count' attribute\n");
result = false;
continue;
}
count = get_int(scope_eval(scope, count_s));
scope_push(scope);
for (o = n->children; o; o = o->next)
{
if (strcmp(o->name, "param") == 0)
{
if (!load_param(scope, o, true))
result = false;
}
}
for (i = 0; i < count; ++i)
{
view_t rep;
view_init(&rep, NULL);
if (!load_view(scope, &rep, n, false))
result = false;
merge_group(scope, view, &rep, has_bounds, n_bounds, n_orient, n_color);
view_deinit(&rep);
scope_repeat(scope);
}
scope_pop(scope);
}
else /* element */
{
layer_t *layer;
element_t *elem;
layer = view_emplace_layer(view, n->name);
elem = layer_add_element(layer);
if (strcmp(n->name, "screen") == 0)
{
if (!load_screen(scope, elem, n))
result = false;
}
else
{
const char *elem_name;
const char *attr;
if ((elem_name = rxml_node_attrib(n, "element")))
{
element_t *elem_src;
if ((elem_src = scope_find_element(scope, elem_name)))
{
element_copy(elem, elem_src);
if ((attr = rxml_node_attrib(n, "name")))
elem->o_bind = video_layout_io_find(scope_eval(scope, attr));
if ((attr = rxml_node_attrib(n, "inputtag")))
elem->i_bind = video_layout_io_find(scope_eval(scope, attr));
if ((attr = rxml_node_attrib(n, "inputmask")))
elem->i_mask = get_int(scope_eval(scope, attr));
if ((attr = rxml_node_attrib(n, "inputraw")))
elem->i_raw = get_int(scope_eval(scope, attr)) ? true : false;
}
else
{
RARCH_LOG("video_layout: element \"%s\" is missing\n", scope_eval(scope, elem_name));
result = false;
}
}
else
{
RARCH_LOG("video_layout: <%s> is missing 'element' attribute\n", n->name);
result = false;
}
}
for (i = 0; i < elem->components_count; ++i)
color_mod(&elem->components->color, &n_color);
elem->bounds = n_bounds;
if (n_orient)
element_apply_orientation(elem, n_orient);
if (!has_bounds)
view->bounds = bounds_union(&view->bounds, &elem->bounds);
}
}
return result;
}
static bool load_group(scope_t *scope, rxml_node_t *node)
{
bool result = true;
view_t *group = scope_add_group(scope);
scope_push(scope);
if (!load_view(scope, group, node, true))
result = false;
scope_pop(scope);
return result;
}
static bool load_top_level(scope_t *scope, int *view_count, rxml_node_t *root)
{
bool result;
rxml_node_t *node;
result = true;
*view_count = 0;
for (node = root->children; node; node = node->next)
{
if (strcmp(node->name, "param") == 0)
{
if (!load_param(scope, node, false))
result = false;
}
else if (strcmp(node->name, "element") == 0)
{
if (!load_element(scope, node))
result = false;
}
else if (strcmp(node->name, "group") == 0)
{
if (!load_group(scope, node))
result = false;
}
else if (strcmp(node->name, "view") == 0)
++(*view_count);
}
return result;
}
static bool load_views(scope_t *scope, view_array_t *view_array, rxml_node_t *root)
{
bool result;
int i;
rxml_node_t *node;
result = true;
i = 0;
for (node = root->children; node; node = node->next)
{
if (strcmp(node->name, "view") == 0)
{
view_t *view;
view = &view_array->views[i];
scope_push(scope);
if (!load_view(scope, view, node, true))
result = false;
view_sort_layers(view);
view_normalize(view);
view_count_screens(view);
scope_pop(scope);
++i;
}
}
return result;
}
bool load(view_array_t *view_array, rxml_document_t *doc)
{
bool result;
rxml_node_t *root;
scope_t scope;
int view_count;
root = rxml_root_node(doc);
if (strcmp(root->name, "mamelayout") ||
strcmp(rxml_node_attrib(root, "version"), "2"))
{
RARCH_LOG("video_layout: invalid MAME Layout file\n");
return false;
}
result = false;
scope_init(&scope);
init_device_params(&scope);
init_screen_params(&scope, 0);
init_screen_params(&scope, 1);
if (!load_top_level(&scope, &view_count, root))
result = false;
view_array_init(view_array, view_count);
if (!load_views(&scope, view_array, root))
result = false;
scope_deinit(&scope);
return result;
}

339
gfx/video_layout/scope.c Normal file
View File

@ -0,0 +1,339 @@
#include "scope.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
union number
{
int val_int;
float val_dec;
};
typedef struct generator
{
bool is_decimal;
union number value;
union number increment;
int shift;
}
generator_t;
struct param
{
char *name;
char *value;
generator_t *generator;
param_t *prev;
int level;
};
static void param_deinit(param_t *param)
{
free(param->generator);
free(param->value);
free(param->name);
}
static param_t *param_find(scope_t *scope, const char *name, int level)
{
param_t *param;
param = scope->param;
while (param && param->level >= level)
{
if (strcmp(param->name, name) == 0)
{
return param;
}
param = param->prev;
}
return NULL;
}
void scope_init(scope_t *scope)
{
scope->level = 0;
scope->param = NULL;
scope->elements = NULL;
scope->elements_count = 0;
scope->groups = NULL;
scope->groups_count = 0;
}
void scope_deinit(scope_t *scope)
{
int i;
param_t *param;
param_t *prev;
for (i = 0; i < scope->elements_count; ++i)
element_deinit(&scope->elements[i]);
free(scope->elements);
for (i = 0; i < scope->groups_count; ++i)
view_deinit(&scope->groups[i]);
free(scope->groups);
for (param = scope->param; param; param = prev)
{
prev = param->prev;
param_deinit(param);
free(param);
}
}
void scope_push(scope_t *scope)
{
++scope->level;
}
void scope_pop(scope_t *scope)
{
param_t *param;
--scope->level;
while ((param = scope->param))
{
if (param->level <= scope->level)
break;
scope->param = param->prev;
param_deinit(param);
free(param);
}
}
void scope_repeat(scope_t *scope)
{
param_t *param;
for (param = scope->param; param && param->level >= scope->level; param = param->prev)
{
generator_t *gen;
if ((gen = param->generator))
{
char tmp[SCOPE_BUFFER_SIZE];
tmp[0] = '\0';
if (gen->is_decimal)
{
gen->value.val_dec += gen->increment.val_dec;
if (gen->shift > 0)
gen->value.val_dec = (float)((int)gen->value.val_dec << gen->shift);
else if (gen->shift < 0)
gen->value.val_dec = (float)((int)gen->value.val_dec >> -gen->shift);
sprintf(tmp, "%f", gen->value.val_dec);
}
else
{
gen->value.val_int += gen->increment.val_int;
if(gen->shift > 0)
gen->value.val_int <<= gen->shift;
else if (gen->shift < 0)
gen->value.val_int >>= -gen->shift;
sprintf(tmp, "%d", gen->value.val_int);
}
set_string(&param->value, tmp);
}
}
}
void scope_param(scope_t *scope, const char *name, const char *value)
{
char *eval_name;
char *eval_value;
param_t *param;
eval_name = init_string(scope_eval(scope, name));
eval_value = init_string(scope_eval(scope, value));
if ((param = param_find(scope, eval_name, scope->level)))
{
free(param->value);
param->value = eval_value;
}
else
{
param = (param_t*)malloc(sizeof(param_t));
param->name = init_string(name);
param->value = eval_value;
param->generator = NULL;
param->level = scope->level;
param->prev = scope->param;
scope->param = param;
}
free(eval_name);
}
void scope_generator(scope_t *scope, const char *name, const char *start, const char *increment, const char *lshift, const char *rshift)
{
char *e_name;
char *e_val;
char *e_inc;
generator_t *gen;
param_t *param;
e_name = init_string(scope_eval(scope, name));
if (param_find(scope, e_name, scope->level))
{
free(e_name);
return;
}
e_val = init_string(scope_eval(scope, start));
e_inc = init_string(scope_eval(scope, increment));
gen = (generator_t*)malloc(sizeof(generator_t));
param = (param_t*)malloc(sizeof(param_t));
param->name = init_string(e_name);
param->value = init_string(e_val);
param->generator = gen;
param->level = scope->level;
param->prev = scope->param;
scope->param = param;
gen->is_decimal = is_decimal(e_val) | is_decimal(e_inc);
if (gen->is_decimal)
{
gen->value.val_dec = get_dec(e_val);
gen->increment.val_dec = get_dec(e_inc);
}
else
{
gen->value.val_int = get_int(e_val);
gen->increment.val_int = get_int(e_inc);
}
gen->shift = 0;
if (lshift)
gen->shift += get_int(scope_eval(scope, lshift));
if (rshift)
gen->shift -= get_int(scope_eval(scope, rshift));
free(e_inc);
free(e_val);
free(e_name);
}
const char *scope_eval(scope_t *scope, const char *src)
{
const char* next;
bool in_var;
char tmp[SCOPE_BUFFER_SIZE];
if (!src)
return NULL;
scope->eval[0] = '\0';
next = src;
while (next[0] != '\0')
{
const char* cur;
cur = next;
if ((in_var = (next[0] == '~')))
++cur;
next = strchr(cur, '~');
if (next && next != cur)
{
size_t len;
len = next - cur;
if (in_var)
{
param_t *param;
strncpy(tmp, cur, len);
tmp[len] = '\0';
if ((param = param_find(scope, tmp, 0)))
strcat(scope->eval, param->value);
else
strcat(scope->eval, tmp);
++next;
}
else
{
strncat(scope->eval, cur, len);
}
}
else
{
if (in_var)
--cur;
strcat(scope->eval, cur);
break;
}
}
return scope->eval;
}
element_t *scope_add_element(scope_t *scope)
{
element_t *elem;
vec_size((void**)&scope->elements, sizeof(element_t), ++scope->elements_count);
elem = &scope->elements[scope->elements_count - 1];
element_init(elem, NULL, 0);
return elem;
}
element_t *scope_find_element(scope_t *scope, const char *name)
{
int i;
for (i = 0; i < scope->elements_count; ++i)
{
if (strcmp(name, scope->elements[i].name) == 0)
return &scope->elements[i];
}
return NULL;
}
view_t *scope_add_group(scope_t *scope)
{
view_t *group;
vec_size((void**)&scope->groups, sizeof(view_t), ++scope->groups_count);
group = &scope->groups[scope->groups_count - 1];
view_init(group, NULL);
return group;
}
view_t *scope_find_group(scope_t *scope, const char *name)
{
int i;
for (i = 0; i < scope->groups_count; ++i)
{
if (strcmp(name, scope->groups[i].name) == 0)
return &scope->groups[i];
}
return NULL;
}

42
gfx/video_layout/scope.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef VIDEO_LAYOUT_SCOPE_H
#define VIDEO_LAYOUT_SCOPE_H
#include "view.h"
#include "element.h"
#define SCOPE_BUFFER_SIZE 256
typedef struct param param_t;
typedef struct scope
{
int level;
param_t *param;
element_t *elements;
int elements_count;
view_t *groups;
int groups_count;
char eval[SCOPE_BUFFER_SIZE];
}
scope_t;
void scope_init (scope_t *scope);
void scope_deinit (scope_t *scope);
void scope_push (scope_t *scope);
void scope_pop (scope_t *scope);
void scope_repeat (scope_t *scope);
void scope_param (scope_t *scope, const char *name, const char *value);
void scope_generator (scope_t *scope, const char *name, const char *start, const char *increment, const char *lshift, const char *rshift);
const char *scope_eval (scope_t *scope, const char *src);
element_t *scope_add_element (scope_t *scope);
element_t *scope_find_element (scope_t *scope, const char *name);
view_t *scope_add_group (scope_t *scope);
view_t *scope_find_group (scope_t *scope, const char *name);
#endif

49
gfx/video_layout/types.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef VIDEO_LAYOUT_TYPES_H
#define VIDEO_LAYOUT_TYPES_H
typedef unsigned char video_layout_orientation_t;
#define VIDEO_LAYOUT_FLIP_X 1
#define VIDEO_LAYOUT_FLIP_Y 2
#define VIDEO_LAYOUT_SWAP_XY 4
#define VIDEO_LAYOUT_ROT0 0
#define VIDEO_LAYOUT_ROT90 VIDEO_LAYOUT_SWAP_XY | VIDEO_LAYOUT_FLIP_X
#define VIDEO_LAYOUT_ROT180 VIDEO_LAYOUT_FLIP_X | VIDEO_LAYOUT_FLIP_Y
#define VIDEO_LAYOUT_ROT270 VIDEO_LAYOUT_SWAP_XY | VIDEO_LAYOUT_FLIP_Y
typedef enum video_layout_blend
{
VIDEO_LAYOUT_BLEND_ALPHA,
VIDEO_LAYOUT_BLEND_ADD,
VIDEO_LAYOUT_BLEND_MOD
}
video_layout_blend_t;
typedef enum video_layout_text_align
{
VIDEO_LAYOUT_TEXT_ALIGN_CENTER,
VIDEO_LAYOUT_TEXT_ALIGN_LEFT,
VIDEO_LAYOUT_TEXT_ALIGN_RIGHT
}
video_layout_text_align_t;
typedef struct video_layout_color
{
float r;
float g;
float b;
float a;
}
video_layout_color_t;
typedef struct video_layout_bounds
{
float x;
float y;
float w;
float h;
}
video_layout_bounds_t;
#endif

259
gfx/video_layout/view.c Normal file
View File

@ -0,0 +1,259 @@
#include "view.h"
#include <stdlib.h>
#include <string.h>
void layer_init(layer_t *layer, const char *name)
{
layer->name = init_string(name);
layer->blend = VIDEO_LAYOUT_BLEND_ALPHA;
layer->elements = NULL;
layer->elements_count = 0;
}
void layer_deinit(layer_t *layer)
{
int i;
for (i = 0; i < layer->elements_count; ++i)
element_deinit(&layer->elements[i]);
free(layer->elements);
free(layer->name);
}
element_t *layer_add_element(layer_t *layer)
{
element_t *elem;
vec_size((void**)&layer->elements, sizeof(element_t), ++layer->elements_count);
elem = &layer->elements[layer->elements_count - 1];
element_init(elem, NULL, 0);
return elem;
}
void view_init(view_t *view, const char *name)
{
view->name = init_string(name);
view->bounds = make_bounds();
view->render_bounds = make_bounds_unit();
view->layers = NULL;
view->layers_count = 0;
view->screens = NULL;
view->screens_count = 0;
}
void view_deinit(view_t *view)
{
int i;
free(view->screens);
for (i = 0; i < view->layers_count; ++i)
layer_deinit(&view->layers[i]);
free(view->layers);
free(view->name);
}
layer_t *view_find_layer(view_t *view, const char *name)
{
int i;
for (i = 0; i < view->layers_count; ++i)
{
if (strcmp(name, view->layers[i].name) == 0)
return &view->layers[i];
}
return NULL;
}
layer_t *view_emplace_layer(view_t *view, const char *name)
{
layer_t *layer = view_find_layer(view, name);
if (!layer)
{
vec_size((void**)&view->layers, sizeof(layer_t), ++view->layers_count);
layer = &view->layers[view->layers_count - 1];
layer_init(layer, name);
}
return layer;
}
void view_sort_layers(view_t *view)
{
layer_t sorted[6];
layer_t *layer;
int i = 0;
/* retroarch frame *= screen's color */
if ((layer = view_find_layer(view, "screen")))
{
layer->blend = VIDEO_LAYOUT_BLEND_MOD;
sorted[i] = *layer;
++i;
}
if ((layer = view_find_layer(view, "overlay")))
{
layer->blend = VIDEO_LAYOUT_BLEND_MOD;
sorted[i] = *layer;
++i;
}
if ((layer = view_find_layer(view, "backdrop")))
{
layer->blend = VIDEO_LAYOUT_BLEND_ADD;
sorted[i] = *layer;
++i;
}
if ((layer = view_find_layer(view, "bezel")))
{
layer->blend = VIDEO_LAYOUT_BLEND_ALPHA;
sorted[i] = *layer;
++i;
}
if ((layer = view_find_layer(view, "cpanel")))
{
layer->blend = VIDEO_LAYOUT_BLEND_ALPHA;
sorted[i] = *layer;
++i;
}
if ((layer = view_find_layer(view, "marquee")))
{
layer->blend = VIDEO_LAYOUT_BLEND_ALPHA;
sorted[i] = *layer;
++i;
}
for (i = 0; i < view->layers_count; ++i)
view->layers[i] = sorted[i];
}
void view_normalize(view_t *view)
{
video_layout_bounds_t dim;
int i, j;
if (bounds_valid(&view->bounds))
{
dim.x = view->bounds.x / view->bounds.w;
dim.y = view->bounds.y / view->bounds.h;
dim.w = 1.0f / view->bounds.w;
dim.h = 1.0f / view->bounds.h;
if (view->bounds.w < view->bounds.h)
{
view->bounds.w = view->bounds.w / view->bounds.h;
view->bounds.h = 1.f;
}
else
{
view->bounds.h = view->bounds.h / view->bounds.w;
view->bounds.w = 1.f;
}
view->bounds.x = 0;
view->bounds.y = 0;
}
else
{
dim = view->bounds = make_bounds_unit();
}
for (i = 0; i < view->layers_count; ++i)
{
layer_t *layer;
layer = &view->layers[i];
for (j = 0; j < layer->elements_count; ++j)
{
element_t *elem;
elem = &layer->elements[j];
if (bounds_valid(&elem->bounds))
{
bounds_scale(&elem->bounds, &dim);
}
else
{
elem->bounds = dim;
}
elem->bounds.x -= dim.x;
elem->bounds.y -= dim.y;
}
}
}
void view_count_screens(view_t *view)
{
int idx, i, j, k;
idx = -1;
for (i = 0; i < view->layers_count; ++i)
{
layer_t *layer = &view->layers[i];
for (j = 0; j < layer->elements_count; ++j)
{
element_t *elem = &layer->elements[j];
for (k = 0; k < elem->components_count; ++k)
{
component_t *comp = &elem->components[k];
if (comp->type == VIDEO_LAYOUT_C_SCREEN)
idx = MAX(idx, comp->attr.screen.index);
}
}
}
if (view->screens_count)
{
free(view->screens);
view->screens_count = 0;
}
if ((++idx))
{
view->screens = (video_layout_bounds_t*)calloc(idx, sizeof(video_layout_bounds_t));
view->screens_count = idx;
}
}
void view_array_init(view_array_t *view_array, int views_count)
{
view_array->views = (view_t*)(views_count > 0 ?
calloc(views_count, sizeof(view_t)) : NULL);
view_array->views_count = views_count;
}
void view_array_deinit(view_array_t *view_array)
{
int i;
for (i = 0; i < view_array->views_count; ++i)
view_deinit(&view_array->views[i]);
free(view_array->views);
view_array->views = NULL;
view_array->views_count = 0;
}
view_t *view_array_find(view_array_t *view_array, const char *name)
{
int i;
for (i = 0; i < view_array->views_count; ++i)
{
if (strcmp(name, view_array->views[i].name) == 0)
return &view_array->views[i];
}
return NULL;
}

53
gfx/video_layout/view.h Normal file
View File

@ -0,0 +1,53 @@
#ifndef VIDEO_LAYOUT_VIEW_H
#define VIDEO_LAYOUT_VIEW_H
#include "internal.h"
#include "element.h"
typedef struct layer
{
char *name;
video_layout_blend_t blend;
element_t *elements;
int elements_count;
}
layer_t;
typedef struct view
{
char *name;
video_layout_bounds_t bounds;
video_layout_bounds_t render_bounds;
layer_t *layers;
int layers_count;
video_layout_bounds_t *screens;
int screens_count;
}
view_t;
typedef struct view_array
{
view_t *views;
int views_count;
}
view_array_t;
void layer_init (layer_t *layer, const char *name);
void layer_deinit (layer_t *layer);
element_t *layer_add_element (layer_t *layer);
void view_init (view_t *view, const char *name);
void view_deinit (view_t *view);
layer_t *view_find_layer (view_t *view, const char *name);
layer_t *view_emplace_layer (view_t *view, const char *name);
void view_sort_layers (view_t *view);
void view_normalize (view_t *view);
void view_count_screens (view_t *view);
void view_array_init (view_array_t *view_array, int views_count);
void view_array_deinit (view_array_t *view_array);
view_t *view_array_find (view_array_t *view_array, const char *name);
#endif