3. The Buffer
3.1. Buffer Overview
The lowest level data construct is a buffer. This is where bits are written to.
typedef struct btprnt_buf btprnt_buf;
A bitmap has an array where it stores data, and integers storing the width and a height.
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
.
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.
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
btprnt_buf * btprnt_buf_extmem(int w, int h,
unsigned char *data);
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
.
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.
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_read
and btprnt_buf_write
.
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.
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
.
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
.
int btprnt_buf_width(btprnt_buf *buf);
int btprnt_buf_height(btprnt_buf *buf);
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
.
void btprnt_buf_pbm(btprnt_buf *buf, const char *filename);
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.
void btprnt_pbm(btprnt *bp, const char *filename);
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
.
void btprnt_buf_xbm(btprnt_buf *buf,
const char *name,
const char *filename);
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