3. The Buffer

3.1. Buffer Overview

The lowest level data construct is a buffer. This is where bits are written to.

<<typedefs>>=
typedef struct btprnt_buf btprnt_buf;

A bitmap has an array where it stores data, and integers storing the width and a height.

<<structs>>=
struct btprnt_buf {
    int w;
    int h;
    int stride;
    unsigned char free;
    unsigned char *data;
};

3.2. Initialization and Allocation

The buffer is allocated + initialized with the function btprnt_buf_init.

<<funcdefs>>=
btprnt_buf * btprnt_buf_init(int w, int h);

To make the math a bit easier for read/write operations, the rows will be rounded to the nearest multiple of 8, so they can be aligned with byte boundaries. For non-multiples of 8, this will produce padding, or extra unused bits at the end of the row.

The size of each row in bytes is stored in a variable called stride. The stride value gets used any time a read or write operation is computed.

<<funcs>>=
btprnt_buf * btprnt_buf_init(int w, int h)
{
    btprnt_buf *b;
    int stride;

    b = calloc(1, sizeof(btprnt_buf));

    if (b == NULL) return NULL;

    b->free = 1;
    b->w = w;
    b->h = h;

    if (w % 8) {
        stride = ((w / 8) + 1);
    } else {
        stride = w / 8;
    }

    b->stride = stride;
    b->data = calloc(1, stride * h);

    if (b->data == NULL) {
        free(b);
        return NULL;
    }

    return b;
}

3.3. Buffers with externally managed memory

Buffers that use externally managed memory blocks can be created with btprnt_buf_extmem

<<funcdefs>>=
btprnt_buf * btprnt_buf_extmem(int w, int h,
                               unsigned char *data);
<<funcs>>=
btprnt_buf * btprnt_buf_extmem(int w, int h,
                               unsigned char *data)
{
    btprnt_buf *b;
    int stride;

    b = calloc(1, sizeof(btprnt_buf));

    if (b == NULL) return NULL;

    b->free = 0;
    b->w = w;
    b->h = h;

    if (w % 8) {
        stride = ((w / 8) + 1) * 8;
    } else {
        stride = w / 8;
    }

    b->stride = stride;
    b->data = data;

    return b;
}

3.4. Freeing a Buffer

The buffer is freed with btprnt_buf_free.

<<funcdefs>>=
void btprnt_buf_free(btprnt_buf **buf);

The data and the struct must be freed. To prevent double-free corruptions, the pointer is set to be NULL.

<<funcs>>=
void btprnt_buf_free(btprnt_buf **buf)
{
    if (*buf == NULL) return;
    if ((*buf)->free) free((*buf)->data);
    free(*buf);
    *buf = NULL;
}

3.5. Read/Write Operations

The main operations are read + write with btprnt_buf_readand btprnt_buf_write.

<<funcdefs>>=
unsigned char btprnt_buf_read(btprnt_buf *b, int x, int y);
void btprnt_buf_write(btprnt_buf *b, int x, int y, int c);

Reading a bit is a matter of first finding the byte where it is located, and then ANDing with the local bit location.

Multiplying the stride by the height gives us which row to look at. Adding x divided by 8 (bits to a byte), gives us the byte offset.

No coordinate checks are done here so be careful. This isn't an interface to be accessed directly. Sanitized inputs with boundary checks before running this function.

<<funcs>>=
unsigned char btprnt_buf_read(btprnt_buf *b, int x, int y)
{
    unsigned char bitpos;
    int pos;
    int off;

    off = x >> 3;
    pos = (y * b->stride) + off;
    bitpos = x - (off * 8);

    return (b->data[pos] & (1 << bitpos)) > 0;
}

Similar process with reading, except the buffer is toggled on or off depending on the value of c.

<<funcs>>=
void btprnt_buf_write(btprnt_buf *b, int x, int y, int c)
{
    unsigned char bitpos;
    int pos;
    int off;

    off = x >> 3;
    pos = (y * b->stride) + off;
    bitpos = x - (off * 8);

    if (c) {
        b->data[pos] |= (1 << bitpos);
    } else {
        b->data[pos] &= ~(1 << bitpos);
    }
}

3.6. Dimension Getters

Dimensions for the buffer can be retrieved using btprnt_buf_width and btprnt_buf_height.

<<funcdefs>>=
int btprnt_buf_width(btprnt_buf *buf);
int btprnt_buf_height(btprnt_buf *buf);
<<funcs>>=
int btprnt_buf_width(btprnt_buf *buf)
{
    return buf->w;
}

int btprnt_buf_height(btprnt_buf *buf)
{
    return buf->h;
}

3.7. Write buffer to PBM file

A buffer can be written to a pbm file with the function btprnt_buf_pbm.

<<funcdefs>>=
void btprnt_buf_pbm(btprnt_buf *buf, const char *filename);
<<funcs>>=
void btprnt_buf_pbm(btprnt_buf *buf, const char *filename)
{
    FILE *fp;
    int x, y;
    int count;
    fp = fopen(filename, "w");

    if (buf == NULL || fp == NULL) return;

    fprintf(fp, "P1\n");
    fprintf(fp, "# Generated with btprnt\n");
    fprintf(fp, "%d %d\n", buf->w, buf->h);

    count = 0;
    for(y = 0; y < buf->h; y++) {
        for(x = 0; x < buf->w; x++) {
            fprintf(fp, "%d", btprnt_buf_read(buf, x, y));
            count++;
            if (count == 16) {
                count = 0;
                fprintf(fp, "\n");
            } else if (count != 0) {
                fprintf(fp, " ");
            }
        }
    }

    fclose(fp);
}

The function btprnt_pbm is a helpful function that calls btprnt_buf_pbm from the btprnt struct.

<<funcdefs>>=
void btprnt_pbm(btprnt *bp, const char *filename);
<<funcs>>=
void btprnt_pbm(btprnt *bp, const char *filename)
{
    btprnt_buf_pbm(bp->buf, filename);
}

3.8. Write buffer to XBM file

The buffer can also be written to an XBM file using the function btprnt_buf_xbm.

<<funcdefs>>=
void btprnt_buf_xbm(btprnt_buf *buf,
                    const char *name,
                    const char *filename);
<<funcs>>=
void btprnt_buf_xbm(btprnt_buf *buf,
                    const char *name,
                    const char *filename)
{
    FILE *fp;
    int n;
    unsigned int count;

    fp = fopen(filename, "w");

    if (buf == NULL || fp == NULL) return;

    fprintf(fp, "#define %s_width %d\n", name, buf->w);
    fprintf(fp, "#define %s_height %d\n", name, buf->h);
    fprintf(fp, "static unsigned char %s_bits[] = {\n", name);

    count = buf->h * buf->stride;

    for (n = 0; n < count; n++) {
        fprintf(fp, "0x%x,", buf->data[n]);
        if ((n + 1) % 8 == 0) {
            fprintf(fp, "\n");
        } else {
            fprintf(fp, " ");
        }
    }

    fprintf(fp, "};");


    fclose(fp);
}



prev | home | next