## 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, the rows will be rounded to the nearest multiple of 8.

<<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) * 8;
} 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;
}``````

The main operations are read + write with `btprnt_buf_read`and `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 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 to look at. Adding x divided by 8 (bits to a byte), gives us the byte offset.

No coordinate checks done here so be careful. This isn't an interface to be accessed directly. Sanitized inputs are expected to be here.

<<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++) {
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