6. Text
Text is next most important thing after all the fundamentals. This can be broken up into levels.
6.1. Level 1: Drawing tiles from a map
The lowest layer involves drawing a single tile from a tile map stored in memory.
6.1.1. Top Level Draw Tile
The static function draw_tile
will draw the tile using
a specified draw
callback, to allow for regular or
wraparound drawing.
reg
refers to the btprnt region to draw onto.
map
is a buffer that contains the tilemap as a bitmap.
xpos
and ypos
indicate where to draw the tile on the
region. The coordinates point to the top-left corner of
the tile.
mx
and my
are coordinates for the tile. The position
(1, 2) would indicate the tile on the second row
(offset 1), on the third column (offset 2).
w
and h
indicate the width and height dimensions of
the tile (in pixels).
scale
is a scaling factor. 1 is regular, 2 is 2x, 3 is
3x, etc.
color
is the color of the glyph. 1 is the 'on'
color (usually black), and 0 is 'off' color (usually white).
static void draw_tile(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int mx, int my,
int w, int h,
int scale, int color,
void (*draw)(btprnt_region*,int,int,int));
The draw_tile
function uses btprnt_buf_read
to read
bits from the tilemap. Things like stride are abstracted
away there, which makes this aspect of the function a little
more straight forward.
Scaling is something that is handled here, which requires an additional nested loop, and some extra variables.
static void draw_tile(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int mx, int my,
int w, int h,
int scale, int color,
void (*draw)(btprnt_region*,int,int,int))
{
int startx;
int starty;
int x;
int y;
int c;
startx = mx * w;
starty = my * h;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
c = btprnt_buf_read(map,
startx + x,
starty + y);
if (c) {
if (scale == 1) {
draw(reg, xpos + x, ypos + y, color);
} else {
int sx, sy;
for (sy = 0; sy < scale; sy++) {
for (sx = 0; sx < scale; sx++) {
draw(reg,
xpos + x*scale + sx,
ypos + y*scale + sy,
color);
}
}
}
}
}
}
}
6.1.2. Default Draw Tile
void btprnt_draw_tile(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int mx, int my,
int w, int h,
int scale, int color);
void btprnt_draw_tile(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int mx, int my,
int w, int h,
int scale, int color)
{
draw_tile(reg, map,
xpos, ypos,
mx, my,
w, h,
scale, color,
btprnt_region_draw);
}
6.1.3. Draw Tile with Wraparound
This draws a tile in a region, but using wraparound.
void btprnt_draw_tile_wrap(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int mx, int my,
int w, int h,
int scale, int color);
void btprnt_draw_tile_wrap(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int mx, int my,
int w, int h,
int scale, int color)
{
draw_tile(reg, map,
xpos, ypos,
mx, my,
w, h,
scale, color,
btprnt_region_draw_wrap);
}
6.2. Level 2: Drawing a character
To draw the right tile, we need to be be able to match an ASCII character to the position on the map, staring with the 'space' character. Maps will assume the glyphs are in ascii order, and are fixed width and height.
The number of glyph columns in the map (ncols
) is obtained
in order to calculate the glyph coordinates gx
and gy
.
void btprnt_draw_char(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
char c, int scale, int color);
void btprnt_draw_char(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
char c, int scale, int color)
{
int gx, gy;
char o;
int ncols;
o = c - ' '; /* start at 0 */
ncols = map->w / w;
gx = o % ncols;
gy = o / ncols;
btprnt_draw_tile(reg, map,
xpos, ypos,
gx, gy,
w, h,
scale, color);
}
btprnt_draw_char_wrap
can do the same thing, but with
wrapping.
void btprnt_draw_char_wrap(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
char c, int scale, int color);
void btprnt_draw_char_wrap(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
char c, int scale, int color)
{
int gx, gy;
char o;
int ncols;
o = c - ' '; /* start at 0 */
ncols = map->w / w;
gx = o % ncols;
gy = o / ncols;
btprnt_draw_tile_wrap(reg, map,
xpos, ypos,
gx, gy,
w, h,
scale, color);
}
6.3. Level 3: Drawing a string
From there, a string characters can be drawn onto a region.
void btprnt_draw_text(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str);
void btprnt_draw_text(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str)
{
int len;
int n;
len = strlen(str);
for (n = 0; n < len; n++) {
btprnt_draw_char(reg, map,
xpos + w*n, ypos,
w, h,
str[n], 1, 1);
}
}
btprnt_draw_scrolling_text
will draw text with bounds
wrapping.
void btprnt_draw_scrolling_text(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str);
void btprnt_draw_scrolling_text(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str)
{
int len;
int n;
len = strlen(str);
for (n = 0; n < len; n++) {
btprnt_draw_char_wrap(reg, map,
xpos + w*n, ypos,
w, h,
str[n], 1, 1);
}
}
6.4. Level 4: Text wrapping
Since the dimensions of the textbox are known, some basic text wrapping can be implemented.
void btprnt_draw_wraptext(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str);
void btprnt_draw_wraptext(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str)
{
int len;
int n;
int curpos;
int line;
int c;
len = strlen(str);
line = 0;
curpos = 0;
c = 0;
for (n = 0; n < len; n++) {
curpos = xpos + w*c;
if (curpos >= reg->w) {
curpos = xpos;
line++;
c = 0;
}
btprnt_draw_char(reg, map,
curpos, ypos + line*h,
w, h,
str[n], 1, 1);
c++;
}
}
6.5. Level 5: Word Wrapping
With a bit more sophistication, some basic word wrapping can done by writing the text chunks between spaces. If a word is larger than what it left, it will know to go to the next line. If the word is larger than how many characters there are on a line, it will do the best it can to break to wrap the text up.
void btprnt_draw_textbox(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str,
int scale,
int color);
This function works by counting characters until it reaches a space. Once it finds that space, it will write that chunk of letters up to (and including) that space. Some arithmetic will done. If it happens that the number of characters exceeds the bounds of the current line position, it will start a new line. (Space needs to be included with this count so there aren't any trailing spaces at the end of a line.) If it happens that the number of characters is greater than the length of the line, it won't matter if a newline happens, and the word will be split up as best as it can.
To be clear: a newline shouldn't happen if the number of characters in a word is longer than the width. A weird edge case I ran into involved having the first word in the textbox be long. The original code added a empty line on the first line, which looked weird.
When the text has reached the end, it has to print out the last word, if there is any. This process is pretty much indentical to what happens in the for loop, except that some of the variables updated don't matter.
For now, I literally copy-pasted this twice because I'm tired and lazy. I may come back at some point and do something more elegant when I have the time.
void btprnt_draw_textbox(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str,
int scale,
int color)
{
int len;
int n;
int start;
int nchars;
int c;
int line;
len = strlen(str);
start = 0;
nchars = 0;
c = 0;
line = 0;
for (n = 0; n < len; n++) {
nchars++;
if (str[n] == ' ' || str[n] == '\n') {
int wordlen;
int off;
int i;
int curpos;
char x;
wordlen = nchars*w*scale;
off = xpos + c*w*scale;
if ((off + wordlen) > reg->w) {
/* nested if is a clumsy, but it works */
if (wordlen < reg->w) {
line++;
c = 0;
}
}
for (i = 0; i < nchars; i++) {
curpos = xpos + c*w*scale;
x = str[start + i];
if ((curpos + w*scale) > reg->w || x == '\n') {
curpos = xpos;
line++;
c = 0;
}
if (x != '\n') {
btprnt_draw_char(reg, map,
curpos,
ypos + line*h*scale,
w, h, x, scale, color);
c++;
}
}
start = n + 1;
nchars = 0;
}
}
if (nchars > 0) {
/* duplicate code alert ring ring ring */
int wordlen;
int off;
int i;
int curpos;
wordlen = nchars * w * scale;
off = xpos + c*w*scale;
if ((off + wordlen) > reg->w) {
line++;
c = 0;
}
for (i = 0; i < nchars; i++) {
curpos = xpos + c*w*scale;
if ((curpos + w*scale) > reg->w) {
curpos = xpos;
line++;
c = 0;
}
btprnt_draw_char(reg, map,
curpos, ypos + line*h*scale,
w, h,
str[start + i], scale, color);
c++;
}
start = n + 1;
nchars = 0;
}
}
prev | home | next