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

<<static_funcdefs>>=
``````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));``````
<<funcs>>=
``````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++) {
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

<<funcdefs>>=
``````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);``````
<<funcs>>=
``````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.

<<funcdefs>>=
``````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);``````
<<funcs>>=
``````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`.

<<funcdefs>>=
``````void btprnt_draw_char(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
char c, int scale, int color);``````
<<funcs>>=
``````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.

<<funcdefs>>=
``````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);``````
<<funcs>>=
``````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.

<<funcdefs>>=
``````void btprnt_draw_text(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str);``````
<<funcs>>=
``````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.

<<funcdefs>>=
``````void btprnt_draw_scrolling_text(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str);``````
<<funcs>>=
``````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.

<<funcdefs>>=
``````void btprnt_draw_wraptext(btprnt_region *reg,
btprnt_buf *map,
int xpos, int ypos,
int w, int h,
const char *str);``````
<<funcs>>=
``````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.

<<funcdefs>>=
``````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.

<<funcs>>=
``````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