18. Graphics

18.1. The Graphics Framebuffer

18.1.1. GFX Framebuffer Top-Level

18.1.1.1. GFX Framebuffer Struct Declaration

<<struct_contents>>=
monolith_framebuffer *fb;
<<init>>=
m->fb = NULL;
<<function_declarations>>=
monolith_framebuffer *monolith_fb_get(monolith_d *m);
<<functions>>=
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.

<<function_declarations>>=
int monolith_gfx_fb_init(monolith_d *m);
<<functions>>=
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);
<<functions>>=
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.

<<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.

<<typedefs>>=
typedef struct monolith_framebuffer monolith_framebuffer;
<<structs>>=
struct monolith_framebuffer {
<<monolith_framebuffer_contents>>
};

A framebuffer is initialized with the funciton monolith_framebuffer_init.

<<function_declarations>>=
void monolith_framebuffer_init(monolith_framebuffer *fb);
<<functions>>=
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.

<<function_declarations>>=
void monolith_framebuffer_clean(monolith_framebuffer *fb);
<<functions>>=
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.

<<typedefs>>=
typedef struct monolith_pixel 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);
<<functions>>=
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_framebuffer_contents>>=
monolith_pixel pix[WIDTHxHEIGHT];

Can be retrieved with monolith_framebuffer_pix.

<<function_declarations>>=
monolith_pixel * monolith_framebuffer_pix(monolith_framebuffer *fb);
<<functions>>=
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.

<<macros>>=
#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.

<<monolith_framebuffer_contents>>=
int w;
int h;
<<monolith_framebuffer_init>>=
fb->w = MAXWIDTH;
fb->h = MAXHEIGHT;

The width can be obtained using the function monolith_gfx_width. and monolith_gfx_height.

<<function_declarations>>=
int monolith_gfx_width(monolith_framebuffer *fb);
int monolith_gfx_height(monolith_framebuffer *fb);
<<functions>>=
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.

<<function_declarations>>=
void monolith_framebuffer_setsize(monolith_framebuffer *fb, int w, int h);
<<functions>>=
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.

<<monolith_framebuffer_contents>>=
unsigned int zoom;
<<monolith_framebuffer_init>>=
fb->zoom = 1;

The framebuffer can be set using the function monolith_framebuffer_zoom.

<<function_declarations>>=
void monolith_framebuffer_zoom(monolith_framebuffer *fb, unsigned int zoom);
<<functions>>=
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.

<<function_declarations>>=
unsigned int monolith_framebuffer_zoom_get(monolith_framebuffer *fb);
<<functions>>=
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_framebuffer_contents>>=
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.

<<monolith_framebuffer_init>>=
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.

<<monolith_framebuffer_contents>>=
size_t zoom_buf_size;
<<monolith_framebuffer_init>>=
fb->zoom_buf_size = 0;
<<monolith_framebuffer_clean>>=
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_function_declarations>>=
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.

<<functions>>=
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.

<<static_function_declarations>>=
#ifdef MONOLITH_H264
static void zbuf_rescale(monolith_pixel *pix,
                         monolith_pixel *zbuf,
                         unsigned int w,
                         unsigned int h,
                         int zoom);
#endif
<<functions>>=
#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

<<function_declarations>>=
<<chan_function_declarations>>
<<static_function_declarations>>=
<<chan_static_function_declarations>>
<<functions>>=
<<chan_functions>>
<<monolith_runt_loader>>=
<<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.

<<macros>>=
#ifndef MONOLITH_MAXCHAN
#define MONOLITH_MAXCHAN 16
#endif
<<struct_contents>>=
GFFLT chan[MONOLITH_MAXCHAN];

18.3.3. Channel Initialization

Channels are initialized at runtime by being zeroed out, via the function monolith_chan_init.

<<init>>=
monolith_chan_init(m);
<<chan_function_declarations>>=
void monolith_chan_init(monolith_d *m);
<<chan_functions>>=
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.

<<chan_function_declarations>>=
GFFLT monolith_chan_get(monolith_d *m, int chan);
<<chan_functions>>=
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.

<<chan_function_declarations>>=
void monolith_chan_set(monolith_d *m, int chan, GFFLT val);
<<chan_functions>>=
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]

<<chan_static_function_declarations>>=
static int node_monget(gf_node *n, monolith_d *m, int chan);
<<chan_functions>>=
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;
}
<<chan_static_function_declarations>>=
static void monget_compute(gf_node *n);
<<chan_functions>>=
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);
    }
}
<<chan_static_function_declarations>>=
static void monget_destroy(gf_node *n);
<<chan_functions>>=
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]

<<chan_runt_entries>>=
monolith_runt_keyword(m, "monget", 6, rproc_monget, m);
<<chan_static_function_declarations>>=
static runt_int rproc_monget(runt_vm *vm, runt_ptr p);
<<chan_functions>>=
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

<<chan_static_function_declarations>>=
static int node_monset(gf_node *n, monolith_d *m, int chan);
<<chan_functions>>=
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;
}
<<chan_static_function_declarations>>=
static void monset_compute(gf_node *n);
<<chan_functions>>=
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

<<chan_runt_entries>>=
monolith_runt_keyword(m, "monset", 6, rproc_monset, m);
<<chan_static_function_declarations>>=
static runt_int rproc_monset(runt_vm *vm, runt_ptr p);
<<chan_functions>>=
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.

<<system_includes>>=
#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.

<<function_declarations>>=
#ifdef MONOLITH_H264
<<h264_function_declarations>>
#endif
<<static_function_declarations>>=
#ifdef MONOLITH_H264
<<h264_static_function_declarations>>
#endif
<<functions>>=
#ifdef MONOLITH_H264
<<h264_functions>>
#endif

18.4.3. h264 top-level struct declaration

18.4.3.1. h264 struct entry

<<struct_contents>>=
#ifdef MONOLITH_H264
monolith_h264 vid;
#endif
18.4.3.2. h264 struct init/cleanup

<<init>>=
#ifdef MONOLITH_H264
monolith_h264_init(&m->vid);
#endif
<<cleanup>>=
#ifdef MONOLITH_H264
monolith_h264_clean(&m->vid);
#endif
18.4.3.3. h264 struct retrieval

<<h264_function_declarations>>=
monolith_h264 *monolith_h264_get(monolith_d *m);
<<h264_functions>>=
monolith_h264 *monolith_h264_get(monolith_d *m)
{
    return &m->vid;
}

18.4.4. h264 video struct

<<typedefs>>=
#ifdef MONOLITH_H264
typedef struct monolith_h264 monolith_h264;
#endif
<<structs>>=
#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

<<h264_function_declarations>>=
void monolith_h264_init(monolith_h264 *vid);
<<h264_functions>>=
void monolith_h264_init(monolith_h264 *vid)
{
    memset(vid, 0, sizeof(monolith_h264));
}

18.4.6. h264 video cleanup

<<h264_function_declarations>>=
void monolith_h264_clean(monolith_h264 *vid);
<<h264_functions>>=
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.

<<h264_function_declarations>>=
int monolith_h264_begin(monolith_h264 *vid,
                        monolith_framebuffer *fb,
                        const char *filename,
                        int fps);
<<h264_functions>>=
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.

<<h264_static_function_declarations>>=
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.

<<h264_functions>>=
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.

<<h264_static_function_declarations>>=
static void mkyuv(monolith_framebuffer *fb,
                  uint8_t *ybuf,
                  uint8_t *ubuf,
                  uint8_t *vbuf);
<<h264_functions>>=
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

<<h264_function_declarations>>=
int monolith_h264_append(monolith_h264 *vid,
                         monolith_framebuffer *fb);
<<h264_functions>>=
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.

<<h264_function_declarations>>=
int monolith_h264_end(monolith_h264 *vid);
<<h264_functions>>=
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

<<aux_includes>>=
#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

<<typedefs>>=
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.

<<structs>>=
struct monolith_gfx_img {
    unsigned int w;
    unsigned int h;
    unsigned char *data;
};

Here are the getters:

<<function_declarations>>=
void monolith_gfx_img_dim(monolith_gfx_img *img,
                          unsigned int *w,
                          unsigned int *h);
<<functions>>=
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;
}
<<function_declarations>>=
void monolith_gfx_img_data(monolith_gfx_img *img,
                           unsigned char **data,
                           unsigned int *sz);
<<functions>>=
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.

<<function_declarations>>=
void monolith_gfx_img_load(const char *filename,
                           monolith_gfx_img **img);
<<functions>>=
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.

<<function_declarations>>=
void monolith_gfx_img_free(monolith_gfx_img **img);
<<functions>>=
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.

<<function_declarations>>=
int monolith_img_load(monolith_d *m,
                      const char *key,
                      size_t len,
                      const char *filename,
                      monolith_gfx_img **img);
<<functions>>=
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.

<<function_declarations>>=
int monolith_img_find(monolith_d *m,
                      const char *key,
                      size_t len,
                      monolith_gfx_img **img);
<<functions>>=
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

<<function_declarations>>=
int monolith_gfx_pixel_get(monolith_framebuffer *f,
                           int x, int y,
                           monolith_pixel *p);
<<functions>>=
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

<<function_declarations>>=
int monolith_gfx_pixel_set(monolith_framebuffer *f,
                          int x, int y,
                          monolith_pixel p);
<<functions>>=
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