18. Graphics
18.1. The Graphics Framebuffer
18.1.1. GFX Framebuffer Top-Level
18.1.1.1. GFX Framebuffer Struct Declaration
monolith_framebuffer *fb;
m->fb = NULL;
monolith_framebuffer *monolith_fb_get(monolith_d *m);
monolith_framebuffer *monolith_fb_get(monolith_d *m)
{
return m->fb;
}
18.1.1.2. GFX Framebuffer Top-level initialization/cleanup
The framebuffer must be explicitely allocated and
initialized before it can be used. This is done with the
function monolith_gfx_fb_init
.
int monolith_gfx_fb_init(monolith_d *m);
int monolith_gfx_fb_init(monolith_d *m)
{
if(m->fb != NULL) return 0;
m->fb = calloc(1, sizeof(monolith_framebuffer));
monolith_framebuffer_init(m->fb);
return 0;
}
It is freed with monolith_gfx_fb_clean
#+NAME: function_declarations
void monolith_gfx_fb_clean(monolith_d *m);
void monolith_gfx_fb_clean(monolith_d *m)
{
if(m->fb != NULL) {
monolith_framebuffer_clean(m->fb);
free(m->fb);
}
}
It is called automatically at cleanup.
monolith_gfx_fb_clean(m);
18.1.2. The Framebuffer Struct
All data for a framebuffer is contained in a struct called a
monolith_framebuffer
.
typedef struct monolith_framebuffer monolith_framebuffer;
struct monolith_framebuffer {
<<monolith_framebuffer_contents>>
};
A framebuffer is initialized with the funciton
monolith_framebuffer_init
.
void monolith_framebuffer_init(monolith_framebuffer *fb);
void monolith_framebuffer_init(monolith_framebuffer *fb)
{
unsigned int i;
for(i = 0; i < WIDTHxHEIGHT; i++) {
fb->pix[i] = monolith_pixel_make(0, 0, 0, 255);
}
<<monolith_framebuffer_init>>
}
Allocated data is freed with monolith_framebuffer_clean
.
void monolith_framebuffer_clean(monolith_framebuffer *fb);
void monolith_framebuffer_clean(monolith_framebuffer *fb)
{
<<monolith_framebuffer_clean>>
}
18.1.3. A Single Pixel
A single pixel in represented as a RGBA value, called a
monolith_pixel
. Each component is an 8 bit value.
typedef struct monolith_pixel monolith_pixel;
<<monolith_pixel>>
struct monolith_pixel {
unsigned char r, g, b, a;
};
A new pixel can be made using the function
monolith_pixel_make
#+NAME: function_declarations
monolith_pixel monolith_pixel_make(unsigned int r,
unsigned int g,
unsigned int b,
unsigned int a);
monolith_pixel monolith_pixel_make(unsigned int r,
unsigned int g,
unsigned int b,
unsigned int a)
{
monolith_pixel p;
p.r = r;
p.g = g;
p.b = b;
p.a = a;
return p;
}
18.1.4. The Pixel Array
Stores the actual pixel information.
monolith_pixel pix[WIDTHxHEIGHT];
Can be retrieved with monolith_framebuffer_pix
.
monolith_pixel * monolith_framebuffer_pix(monolith_framebuffer *fb);
monolith_pixel * monolith_framebuffer_pix(monolith_framebuffer *fb)
{
return fb->pix;
}
18.1.5. Framebuffer Dimensions
The maximum width and height for the screen is 320x200 pixels, corresponding to screen dimmensions of the commodore 64. This can be adjusted later.
#define MAXWIDTH 320
#define MAXHEIGHT 200
#define WIDTHxHEIGHT 64000 /* 320 x 200 */
The dimmensions are stored as integers and can be changed at runtime. By default they are initialized to be the maximum width and heigh.
int w;
int h;
fb->w = MAXWIDTH;
fb->h = MAXHEIGHT;
The width can be obtained using the function monolith_gfx_width
.
and monolith_gfx_height
.
int monolith_gfx_width(monolith_framebuffer *fb);
int monolith_gfx_height(monolith_framebuffer *fb);
int monolith_gfx_width(monolith_framebuffer *fb)
{
return fb->w;
}
int monolith_gfx_height(monolith_framebuffer *fb)
{
return fb->h;
}
Dimensions can be set using the function monolith_gfx_setsize
.
void monolith_framebuffer_setsize(monolith_framebuffer *fb, int w, int h);
void monolith_framebuffer_setsize(monolith_framebuffer *fb, int w, int h)
{
if(fb == NULL) return;
if(fb->w > 0 && fb->w <= MAXWIDTH) fb->w = w;
if(fb->h > 0 && fb->h <= MAXHEIGHT) fb->h = h;
alloc_zoom_buffer(fb);
}
18.1.6. Zoom Factor
The zoom factor indicates the zoom amount for the framebuffer, as a whole integer multiple. A value of 1 is the original, a value of 2 is twice the size, 3 is three times, etc..
By default, the zoom level is set to 1.
unsigned int zoom;
fb->zoom = 1;
The framebuffer can be set using the function
monolith_framebuffer_zoom
.
void monolith_framebuffer_zoom(monolith_framebuffer *fb, unsigned int zoom);
void monolith_framebuffer_zoom(monolith_framebuffer *fb, unsigned int zoom)
{
fb->zoom = zoom;
alloc_zoom_buffer(fb);
}
It can be retrieved with monolith_framebuffer_zoom_get
.
unsigned int monolith_framebuffer_zoom_get(monolith_framebuffer *fb);
unsigned int monolith_framebuffer_zoom_get(monolith_framebuffer *fb)
{
return fb->zoom;
}
18.2. The Zoom Buffer
The zoom buffer is a special buffer allocated any time the main graphics frame buffer has a zoom factor greater than 1. Anytime a zoomed framebuffer encounters a write operation, it will render a scaled version of itself to the zoom buffer to then be written.
monolith_pixel *zoom_buf;
The zoom buffer is only allocated when the zoom facter is set to be greater than 1. It is otherwise initialized to be NULL.
fb->zoom_buf = NULL;
The size of the zoom buffer is stored in a size_t
variable
called zoom_buf_size
. This can is used to prevent
unncessary mallocs.
size_t zoom_buf_size;
fb->zoom_buf_size = 0;
free(fb->zoom_buf);
fb->zoom_buf = NULL;
Anytime a zoom buffer potentially needs to be allocated, the
function alloc_zoom_buffer
is called. This is expected to
be called after dimensions are set, or the zoom amount is
set.
static void alloc_zoom_buffer(monolith_framebuffer *fb);
The zoom buffer will only be used if the zoom setting is set to be greater than 1. If it is 1, than nothing will happen.
The needed size of the zoom buffer is calculated. If the current size is less than the needed size, or the zoom buffer is NULL, then an allocation happens.
Finally, the previous zoom buffer is freed (no need to check for NULL, standard free shouldn't care), and then malloc is called to allocate the buffer.
static void alloc_zoom_buffer(monolith_framebuffer *fb)
{
size_t needed;
if(fb->zoom <= 1) return;
needed = fb->w * fb->zoom * fb->h * fb->zoom;
if(needed > fb->zoom_buf_size || fb->zoom_buf == NULL) {
free(fb->zoom_buf);
fb->zoom_buf = calloc(1, needed * sizeof(monolith_pixel));
fb->zoom_buf_size = needed;
}
}
A framebuffer will copy and rescale itself to the zoom
buffer in a single operation. This is done with the function
zbuf_rescale
. This will not do any checks, so sanitize
before calling this guy.
#ifdef MONOLITH_H264
static void zbuf_rescale(monolith_pixel *pix,
monolith_pixel *zbuf,
unsigned int w,
unsigned int h,
int zoom);
#endif
#ifdef MONOLITH_H264
static void zbuf_rescale(monolith_pixel *pix,
monolith_pixel *zbuf,
unsigned int w,
unsigned int h,
int zoom)
{
unsigned int x, y, xi, yi;
unsigned int pos;
unsigned int pos_zoom;
pos = 0;
pos_zoom = 0;
for(y = 0; y < h; y++) {
for(x = 0; x < w; x++) {
pos = y * w + x;
for(yi = 0; yi < zoom; yi++) {
for(xi = 0; xi < zoom; xi++) {
pos_zoom =
y * zoom * w * zoom +
x * zoom +
yi * w * zoom +
xi;
zbuf[pos_zoom] = pix[pos];
}
}
}
}
}
#endif
18.3. Channels Interface
Channels are used to share information between monolith and
graforge. What these are are an array of floating
point values, which can be addressed by index position. The
number of values is specified by the macro
MONOLITH_MAXCHAN
.
18.3.1. Channel Worgle Constructs
<<chan_function_declarations>>
<<chan_static_function_declarations>>
<<chan_functions>>
<<chan_runt_entries>>
18.3.2. Channels Top Level Declaration
Really, the only thing needed here is an array of floats. In
this case, GFFLTS
will be used to match the resolution of
graforge.
#ifndef MONOLITH_MAXCHAN
#define MONOLITH_MAXCHAN 16
#endif
GFFLT chan[MONOLITH_MAXCHAN];
18.3.3. Channel Initialization
Channels are initialized at runtime by being zeroed out,
via the function monolith_chan_init
.
monolith_chan_init(m);
void monolith_chan_init(monolith_d *m);
void monolith_chan_init(monolith_d *m)
{
int i;
for(i = 0; i < MONOLITH_MAXCHAN; i++) {
m->chan[i] = 0;
}
}
18.3.4. Channel Get
The function monolith_chan_get
will retrieve a
channel value at position N. If the value is out of range,
0 will be returned.
GFFLT monolith_chan_get(monolith_d *m, int chan);
GFFLT monolith_chan_get(monolith_d *m, int chan)
{
if(chan < 0 || chan >= MONOLITH_MAXCHAN) {
return 0;
}
return m->chan[chan];
}
18.3.5. Channel Set
The function monolith_gfx_chan_set
will set a particular
channel value. If the channel is out of range, no action
will happen.
void monolith_chan_set(monolith_d *m, int chan, GFFLT val);
void monolith_chan_set(monolith_d *m, int chan, GFFLT val)
{
if(chan < 0 || chan >= MONOLITH_MAXCHAN) {
return;
}
m->chan[chan] = val;
}
18.3.6. Channel Nodes + Words
18.3.6.1. monget
The monget
node will get a particular channel. The
channel taken in is init-time.
18.3.6.1.1. DONE monget node
CLOSED: [2019-05-31 Fri 22:15]
static int node_monget(gf_node *n, monolith_d *m, int chan);
static int node_monget(gf_node *n, monolith_d *m, int chan)
{
GFFLT *val;
gf_node_cables_alloc(n, 1);
gf_node_set_block(n, 0);
if(chan < 0 || chan >= MONOLITH_MAXCHAN) {
return GF_NOT_OK;
}
val = &m->chan[chan];
gf_node_set_compute(n, monget_compute);
gf_node_set_destroy(n, monget_destroy);
gf_node_set_data(n, val);
return GF_OK;
}
static void monget_compute(gf_node *n);
static void monget_compute(gf_node *n)
{
int s;
int blksize;
GFFLT *val;
gf_cable *out;
blksize = gf_node_blksize(n);
gf_node_get_cable(n, 0, &out);
val = gf_node_get_data(n);
for(s = 0; s < blksize; s++) {
gf_cable_set(out, s, *val);
}
}
static void monget_destroy(gf_node *n);
static void monget_destroy(gf_node *n)
{
gf_node_cables_free(n);
}
18.3.6.1.2. DONE monget runt word
CLOSED: [2019-05-31 Fri 22:15]
monolith_runt_keyword(m, "monget", 6, rproc_monget, m);
static runt_int rproc_monget(runt_vm *vm, runt_ptr p);
static runt_int rproc_monget(runt_vm *vm, runt_ptr p)
{
monolith_d *m;
runt_stacklet *out;
runt_int rc;
rgf_param chan;
gf_patch *patch;
gf_node *n;
rc = rgf_get_param(vm, &chan);
RUNT_ERROR_CHECK(rc);
if(!rgf_param_is_constant(&chan)) {
runt_print(vm,
"monget: channel must be a constant\n");
return RUNT_NOT_OK;
}
rc = runt_ppush(vm, &out);
RUNT_ERROR_CHECK(rc);
m = runt_to_cptr(p);
patch = monolith_graforge_get(m);
rc = gf_patch_new_node(patch, &n);
GF_RUNT_ERROR_CHECK(rc);
rc = node_monget(n, m, rgf_param_get_constant(&chan));
if(rc != GF_OK) {
runt_print(vm, "monget: invalid channel\n");
return RUNT_NOT_OK;
}
rgf_push_output(vm, n, out, 0);
return RUNT_OK;
}
18.3.6.2. monset
The monset
node will set a particular channel with
a signal from graforge. Note that this value will
automatically be downsampled to block-rate, as that is
what is only readable from monolith.
18.3.6.2.1. monset node
static int node_monset(gf_node *n, monolith_d *m, int chan);
static int node_monset(gf_node *n, monolith_d *m, int chan)
{
GFFLT *val;
gf_node_cables_alloc(n, 1);
if(chan < 0 || chan >= MONOLITH_MAXCHAN) {
return GF_NOT_OK;
}
val = &m->chan[chan];
gf_node_set_compute(n, monset_compute);
gf_node_set_data(n, val);
return GF_OK;
}
static void monset_compute(gf_node *n);
static void monset_compute(gf_node *n)
{
GFFLT *val;
gf_cable *in;
gf_node_get_cable(n, 0, &in);
val = gf_node_get_data(n);
*val = gf_cable_get(in, 0);
}
18.3.6.2.2. monset runt word
monolith_runt_keyword(m, "monset", 6, rproc_monset, m);
static runt_int rproc_monset(runt_vm *vm, runt_ptr p);
static runt_int rproc_monset(runt_vm *vm, runt_ptr p)
{
monolith_d *m;
runt_int rc;
rgf_param in;
rgf_param chan;
gf_patch *patch;
gf_node *n;
rc = rgf_get_param(vm, &chan);
RUNT_ERROR_CHECK(rc);
if(!rgf_param_is_constant(&chan)) {
runt_print(vm,
"monset: channel must be a constant\n");
return RUNT_NOT_OK;
}
rc = rgf_get_param(vm, &in);
RUNT_ERROR_CHECK(rc);
m = runt_to_cptr(p);
patch = monolith_graforge_get(m);
rc = gf_patch_new_node(patch, &n);
GF_RUNT_ERROR_CHECK(rc);
rc = node_monset(n, m, rgf_param_get_constant(&chan));
if(rc != GF_OK) {
runt_print(vm, "monget: invalid channel\n");
return RUNT_NOT_OK;
}
rgf_set_param(vm, n, &in, 0);
return RUNT_OK;
}
18.4. H264 Video Support
The H264 is a remarkable video codec used to create high-quality videos with in a very small amount of disk space. Using the x264 library, one can easily encode video directly instead of writing a sequence of PNG files.
18.4.1. x264 system include
This will only be included if the MONOLITH_H264
macro is
defined.
#ifdef MONOLITH_H264
#include <x264.h>
#endif
18.4.2. h264 top level constructs
Because all video encoder functions are all congregated
inside of an org-mode block called
h264_function_declarations
, where they are enclosed inside
of a macro ifdef.
#ifdef MONOLITH_H264
<<h264_function_declarations>>
#endif
#ifdef MONOLITH_H264
<<h264_static_function_declarations>>
#endif
#ifdef MONOLITH_H264
<<h264_functions>>
#endif
18.4.3. h264 top-level struct declaration
18.4.3.1. h264 struct entry
#ifdef MONOLITH_H264
monolith_h264 vid;
#endif
18.4.3.2. h264 struct init/cleanup
#ifdef MONOLITH_H264
monolith_h264_init(&m->vid);
#endif
#ifdef MONOLITH_H264
monolith_h264_clean(&m->vid);
#endif
18.4.3.3. h264 struct retrieval
monolith_h264 *monolith_h264_get(monolith_d *m);
monolith_h264 *monolith_h264_get(monolith_d *m)
{
return &m->vid;
}
18.4.4. h264 video struct
#ifdef MONOLITH_H264
typedef struct monolith_h264 monolith_h264;
#endif
#ifdef MONOLITH_H264
struct monolith_h264 {
x264_param_t param;
x264_picture_t pic;
x264_picture_t pic_out;
x264_t *h;
int i_frame;
x264_nal_t *nal;
int i_nal;
FILE *fp;
};
#endif
18.4.5. h264 video initialization
void monolith_h264_init(monolith_h264 *vid);
void monolith_h264_init(monolith_h264 *vid)
{
memset(vid, 0, sizeof(monolith_h264));
}
18.4.6. h264 video cleanup
void monolith_h264_clean(monolith_h264 *vid);
void monolith_h264_clean(monolith_h264 *vid)
{
monolith_h264_end(vid);
}
18.4.7. h264 video interface
18.4.7.1. begin
Begins a video. Opens the file. Gets FPS. Will return 0 on failure.
int monolith_h264_begin(monolith_h264 *vid,
monolith_framebuffer *fb,
const char *filename,
int fps);
int monolith_h264_begin(monolith_h264 *vid,
monolith_framebuffer *fb,
const char *filename,
int fps)
{
x264_param_t *p;
p = &vid->param;
vid->fp = fopen(filename, "w");
if (vid->fp == NULL) return 0;
vid->i_frame = 0;
if(x264_param_default_preset(p, "slow", NULL) < 0)
return 0;
p->i_csp = X264_CSP_I444;
p->i_width = fb->w * fb->zoom;
p->i_height = fb->h * fb->zoom;
p->b_vfr_input = 0;
p->b_repeat_headers = 1;
p->b_annexb = 1;
p->i_fps_num = fps;
/* p->rc.f_aq_strength = 1.0; */
p->rc.f_aq_strength = 0.1;
p->rc.i_aq_mode= 1;
p->i_log_level = X264_LOG_NONE;
p->vui.i_colmatrix = 1;
p->vui.i_transfer = 1;
p->vui.i_colorprim = 1;
/* yuv444p is 16-235, yuvj444p doesn't work with mplayer */
p->vui.b_fullrange = 0;
if (x264_param_apply_profile(p, "high444") < 0 ) {
return 0;
}
if (x264_picture_alloc(&vid->pic,
p->i_csp,
p->i_width,
p->i_height) < 0 ) {
return 0;
}
vid->h = x264_encoder_open(p);
if (!vid->h) return 0;
return 1;
}
18.4.7.2. append
Appends current gfx framebuffer as new frame. This one requires the most code on our end because RGB buffer needs to be converted to a YUV frame. The components are laid out below.
18.4.7.2.1. rgb2yuv
The static function rgb2yuv
will take in a single RGB
triplet and return a YUV triplet
(original) source: https://www.fourcc.org/fccyvrgb.php
The new rgb2yuv conversions are from:
http://avisynth.nl/index.php/Color_conversions
These coefficients seem to better match the colorspace that bt709 wants.
static void rgb2yuv(uint8_t r, uint8_t g, uint8_t b,
uint8_t *y, uint8_t *u, uint8_t *v);
full-range (yuvj444) is between 0 and 255, while yuv444 is between 16 and 235:
https://www.eoshd.com/comments/topic/20799-what-is-the-difference-between-yuvj420p-and-yuv420p/
raw h264 yuvj444 doesn't render yellows properly with mplayer, but yuv444 does.
static void rgb2yuv(uint8_t r, uint8_t g, uint8_t b,
uint8_t *y, uint8_t *u, uint8_t *v)
{
double Ey;
double Ecr;
double Ecb;
double norm;
norm = 1.0/255;
/* Ey = (0.299*r + 0.587*g + 0.114*b)*norm; */
Ey = (0.2126*r + 0.7152*g + 0.0722*b)*norm;
/* Ecr = 0.713 * (r*norm - Ey); */
/* Ecb = 0.564 * (b*norm - Ey); */
Ecr = (r*norm - Ey) / (1 - 0.2126);
Ecb = (b*norm - Ey) / (1 - 0.0722);
/* (*y) = Ey * 255; */
/* (*u) = Ecb * 127.5 + 128; */
/* (*v) = Ecr * 127.5 + 128; */
/* scale between 16 and 235 */
(*y) = Ey * 219 + 16;
/* clamp between 16 and 239 */
(*u) = Ecb * 111.5 + 112 + 16;
(*v) = Ecr * 111.5 + 112 + 16;
}
18.4.7.2.2. mkyuv
The mkyuv
static function will create a YUV frame from
a GFX framebuffer, and store it in separate YUV buffers.
The encoding used for the video is IC420, which means the
Y buffer is full resolution, while the U and V components
subsamped to be at quarter resolution.
static void mkyuv(monolith_framebuffer *fb,
uint8_t *ybuf,
uint8_t *ubuf,
uint8_t *vbuf);
static void mkyuv(monolith_framebuffer *fb,
uint8_t *ybuf,
uint8_t *ubuf,
uint8_t *vbuf)
{
unsigned int x, y;
unsigned int pos;
monolith_pixel *p;
uint8_t yv, uv, vv;
monolith_pixel *pix;
unsigned int w, h;
pix = fb->pix;
w = fb->w;
h = fb->h;
if (fb->zoom > 1 && fb->zoom_buf != NULL) {
zbuf_rescale(fb->pix, fb->zoom_buf,
fb->w, fb->h,
fb->zoom);
pix = fb->zoom_buf;
w *= fb->zoom;
h *= fb->zoom;
}
pos = 0;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
p = &pix[y * w + x];
rgb2yuv(p->r, p->g, p->b, &yv, &uv, &vv);
ybuf[pos] = yv;
ubuf[pos] = uv;
vbuf[pos] = vv;
pos++;
}
}
}
18.4.7.2.3. append C function
int monolith_h264_append(monolith_h264 *vid,
monolith_framebuffer *fb);
int monolith_h264_append(monolith_h264 *vid,
monolith_framebuffer *fb)
{
int i_frame_size;
if(vid->fp == NULL) return 0;
if(vid->h == NULL) return 0;
mkyuv(fb,
vid->pic.img.plane[0],
vid->pic.img.plane[1],
vid->pic.img.plane[2]);
vid->pic.i_pts = vid->i_frame;
vid->i_frame++;
i_frame_size = x264_encoder_encode(vid->h,
&vid->nal,
&vid->i_nal,
&vid->pic,
&vid->pic_out);
if(i_frame_size < 0) return 0;
else if(i_frame_size) {
fwrite(vid->nal->p_payload,
i_frame_size,
1,
vid->fp);
}
return 1;
}
18.4.7.3. end
Writes remaining frames. Closes file. Cleans up x264. This is also called at cleanup in case some things are not fully cleaned up.
int monolith_h264_end(monolith_h264 *vid);
int monolith_h264_end(monolith_h264 *vid)
{
int i_frame_size;
if(vid->h == NULL) return 0;
while( x264_encoder_delayed_frames(vid->h))
{
i_frame_size = x264_encoder_encode(vid->h,
&vid->nal,
&vid->i_nal,
NULL,
&vid->pic_out );
if( i_frame_size ) {
fwrite(vid->nal->p_payload,
i_frame_size,
1,
vid->fp);
}
}
x264_encoder_close(vid->h);
x264_picture_clean(&vid->pic);
fclose(vid->fp);
vid->fp = NULL;
vid->h = NULL;
return 1;
}
18.5. Image Loading and Storing
#include "lodepng/lodepng.h"
18.5.1. Image Struct
The monolith image struct stores loaded image information.
It is called monolith_gfx_image
.
18.5.1.1. Typedef
typedef struct monolith_gfx_img monolith_gfx_img;
18.5.1.2. Struct Contents
Included in this struct is the width, height, as well as raw RGBA data.
struct monolith_gfx_img {
unsigned int w;
unsigned int h;
unsigned char *data;
};
Here are the getters:
void monolith_gfx_img_dim(monolith_gfx_img *img,
unsigned int *w,
unsigned int *h);
void monolith_gfx_img_dim(monolith_gfx_img *img,
unsigned int *w,
unsigned int *h)
{
if (w != NULL) *w = img->w;
if (h != NULL) *h = img->h;
}
void monolith_gfx_img_data(monolith_gfx_img *img,
unsigned char **data,
unsigned int *sz);
void monolith_gfx_img_data(monolith_gfx_img *img,
unsigned char **data,
unsigned int *sz)
{
*data = img->data;
if (sz != NULL) {
*sz = img->w * img->h * 4;
}
}
18.5.2. Loading Image Data
An image struct can be allocated and loaded in a
monolith_gfx_image
struct using the function
monlith_gfx_img_load
.
This will allocate the image data AND the struct itself. For
image loading, monolith uses the internal lodepng
library.
This means that only valid PNG files will be used.
void monolith_gfx_img_load(const char *filename,
monolith_gfx_img **img);
void monolith_gfx_img_load(const char *filename,
monolith_gfx_img **img)
{
int error;
monolith_gfx_img *pimg;
pimg = calloc(1, sizeof(monolith_gfx_img));
error = lodepng_decode32_file(&pimg->data,
&pimg->w,
&pimg->h,
filename);
if (error) {
fprintf(stderr,
"lodepng error %u: %s\n",
error,
lodepng_error_text(error));
*img = NULL;
return;
}
*img = pimg;
}
18.5.3. Freeing Image Data
Image struct + data can be freed simultaneously with the
function monolith_gfx_image_free
. This should be called
inside of something like monolith_dict_entry_list_free
.
void monolith_gfx_img_free(monolith_gfx_img **img);
void monolith_gfx_img_free(monolith_gfx_img **img)
{
monolith_gfx_img *pimg;
pimg = *img;
free(pimg->data);
free(pimg);
pimg = NULL;
}
18.5.4. Loading an image to a dictionary
The function monolith_img_load
will load an image and
store it inside of the monolith dictionary to a unique
keyword.
int monolith_img_load(monolith_d *m,
const char *key,
size_t len,
const char *filename,
monolith_gfx_img **img);
int monolith_img_load(monolith_d *m,
const char *key,
size_t len,
const char *filename,
monolith_gfx_img **img)
{
monolith_gfx_img *pimg;
monolith_dict_entry *ent;
int rc;
ent = NULL;
rc = monolith_dict_newentry(&m->dict, &ent, key, len);
if (img != NULL) *img = NULL;
if (rc != MONOLITH_OK) {
fprintf(stderr, "Unable to create entry ");
fwrite(key, 1, len, stderr);
fprintf(stderr, "\n");
return MONOLITH_NOTOK;
}
pimg = NULL;
monolith_gfx_img_load(filename, &pimg);
if (pimg == NULL) {
fprintf(stderr,
"Could not load file %s\n",
filename);
return MONOLITH_NOTOK;
}
ent->type = MONOLITH_ENTRY_IMAGE;
ent->ud = pimg;
if (img != NULL) *img = pimg;
return MONOLITH_OK;
}
18.5.5. Finding an entry
The function monolith_img_lookup
will attempt to look up
an image given a key.
int monolith_img_find(monolith_d *m,
const char *key,
size_t len,
monolith_gfx_img **img);
int monolith_img_find(monolith_d *m,
const char *key,
size_t len,
monolith_gfx_img **img)
{
monolith_dict_entry *ent;
int rc;
ent = NULL;
rc = monolith_dict_find(&m->dict, &ent, key, len);
if (rc != MONOLITH_OK) {
return rc;
}
if (ent->type != MONOLITH_ENTRY_IMAGE) {
return MONOLITH_NOTOK;
}
if (img != NULL) *img = ent->ud;
return MONOLITH_OK;
}
18.6. Setting/Getting Pixels
18.6.1. Get Pixel
int monolith_gfx_pixel_get(monolith_framebuffer *f,
int x, int y,
monolith_pixel *p);
int monolith_gfx_pixel_get(monolith_framebuffer *f,
int x, int y,
monolith_pixel *p)
{
monolith_pixel *pix;
pix = monolith_framebuffer_pix(f);
if (p == NULL) return 0;
if(x < 0 || x >= monolith_gfx_width(f)) return 0;
if(y < 0 || y >= monolith_gfx_height(f)) return 0;
*p = pix[y * monolith_gfx_width(f) + x];
return 1;
}
18.6.2. Set Pixel in C
int monolith_gfx_pixel_set(monolith_framebuffer *f,
int x, int y,
monolith_pixel p);
int monolith_gfx_pixel_set(monolith_framebuffer *f,
int x, int y,
monolith_pixel p)
{
monolith_pixel *pix;
pix = monolith_framebuffer_pix(f);
if(x < 0 || x >= monolith_gfx_width(f)) return 0;
if(y < 0 || y >= monolith_gfx_height(f)) return 0;
pix[y * monolith_gfx_width(f) + x] = p;
return 1;
}
prev | home | next