2016-07-25 09:04:22 +02:00
|
|
|
#include <internals/tales.h>
|
2016-07-28 18:12:07 +02:00
|
|
|
#include <alloca.h>
|
|
|
|
#include <string.h>
|
2016-07-06 11:28:51 +02:00
|
|
|
#include <ctype.h>
|
2016-07-28 18:12:07 +02:00
|
|
|
#include <gray.h>
|
2016-07-06 11:28:51 +02:00
|
|
|
|
2017-07-05 21:38:12 +02:00
|
|
|
/* Put these in gint's uninitialized bss section so that text rendering can be
|
|
|
|
used before the library is fully initialized */
|
|
|
|
__attribute__((section(".gint.bss"))) font_t *font = NULL;
|
|
|
|
__attribute__((section(".gint.bss"))) color_t operator;
|
2016-07-06 11:28:51 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
getCharacterIndex()
|
|
|
|
Returns the index of a character in a font data area depending on the
|
|
|
|
font format and the size of the characters. Returns the index in the
|
|
|
|
data area, as long array, or -1 when the character does not belong to
|
|
|
|
the font format set.
|
|
|
|
*/
|
|
|
|
int getCharacterIndex(int c)
|
|
|
|
{
|
|
|
|
const char *data = (const char *)&font->glyphs;
|
|
|
|
int index, current;
|
|
|
|
int width, bits;
|
2017-04-19 19:57:31 +02:00
|
|
|
size_t offset;
|
2016-07-06 11:28:51 +02:00
|
|
|
|
|
|
|
c &= 0x7f;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Getting the character index in the glyph array.
|
|
|
|
|
2017-04-19 19:57:31 +02:00
|
|
|
if(font->format == font_format_ascii) index = c;
|
|
|
|
else if(font->format == font_format_print) index = c - 32;
|
2016-07-06 11:28:51 +02:00
|
|
|
|
|
|
|
else switch(font->format)
|
|
|
|
{
|
2017-04-19 19:57:31 +02:00
|
|
|
case font_format_numeric:
|
2016-07-06 11:28:51 +02:00
|
|
|
if(!isdigit(c)) return -1;
|
|
|
|
index = c - '0';
|
|
|
|
break;
|
2017-04-19 19:57:31 +02:00
|
|
|
case font_format_lower:
|
2016-07-06 11:28:51 +02:00
|
|
|
if(!islower(c)) return -1;
|
|
|
|
index = c - 'a';
|
|
|
|
break;
|
2017-04-19 19:57:31 +02:00
|
|
|
case font_format_upper:
|
2016-07-06 11:28:51 +02:00
|
|
|
if(!isupper(c)) return -1;
|
|
|
|
index = c - 'A';
|
|
|
|
break;
|
2017-04-19 19:57:31 +02:00
|
|
|
case font_format_letters:
|
2016-07-06 11:28:51 +02:00
|
|
|
if(!isalpha(c)) return -1;
|
2017-01-01 17:41:16 +01:00
|
|
|
index = c - 'A' - ('a' - 'Z') * (c >= 'a');
|
2016-07-06 11:28:51 +02:00
|
|
|
break;
|
2017-04-19 19:57:31 +02:00
|
|
|
case font_format_common:
|
2016-07-06 11:28:51 +02:00
|
|
|
if(!isalnum(c)) return -1;
|
|
|
|
index = c - '0' - ('A' - '9') * (c >= 'A') -
|
2017-01-01 17:41:16 +01:00
|
|
|
('a' - 'Z') * (c >= 'a');
|
2016-07-06 11:28:51 +02:00
|
|
|
break;
|
2017-04-19 19:57:31 +02:00
|
|
|
case font_format_unknown:
|
2016-07-06 11:28:51 +02:00
|
|
|
default:
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Reaching the character offset.
|
|
|
|
|
|
|
|
current = index & ~7;
|
|
|
|
offset = font->index[current >> 3];
|
|
|
|
|
|
|
|
while(current < index)
|
|
|
|
{
|
|
|
|
width = data[offset << 2];
|
|
|
|
bits = font->data_height * width + 8;
|
|
|
|
|
|
|
|
offset += (bits + 31) >> 5;
|
|
|
|
current++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
operate()
|
|
|
|
Operates on the vram using the given operators. The x-coordinate should
|
|
|
|
be a multiple of 32. There should be `height` operators.
|
|
|
|
*/
|
2016-07-28 18:12:07 +02:00
|
|
|
void operate_mono(OPERATE_ARGS)
|
2016-07-06 11:28:51 +02:00
|
|
|
{
|
2017-04-19 19:57:31 +02:00
|
|
|
if(x < 0) return;
|
|
|
|
|
2017-03-26 18:38:32 +02:00
|
|
|
uint32_t *vram = display_getCurrentVRAM();
|
2017-04-19 19:57:31 +02:00
|
|
|
int start = (y < 0) ? (-y) : (0);
|
|
|
|
uint32_t *video = vram + (x >> 5) + ((y + start) << 2);
|
2016-07-06 11:28:51 +02:00
|
|
|
|
2017-03-26 18:38:32 +02:00
|
|
|
switch(operator)
|
2016-07-06 11:28:51 +02:00
|
|
|
{
|
2017-03-26 18:38:32 +02:00
|
|
|
case color_white:
|
2017-04-19 19:57:31 +02:00
|
|
|
for(int i = start; i < height; i++)
|
2017-03-26 18:38:32 +02:00
|
|
|
{
|
|
|
|
*video &= ~operators[i];
|
|
|
|
video += 4;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case color_black:
|
2017-04-19 19:57:31 +02:00
|
|
|
for(int i = start; i < height; i++)
|
2017-03-26 18:38:32 +02:00
|
|
|
{
|
|
|
|
*video |= operators[i];
|
|
|
|
video += 4;
|
|
|
|
}
|
|
|
|
break;
|
2016-07-28 18:12:07 +02:00
|
|
|
|
2017-03-26 18:38:32 +02:00
|
|
|
case color_invert:
|
2017-04-19 19:57:31 +02:00
|
|
|
for(int i = start; i < height; i++)
|
2016-07-28 18:12:07 +02:00
|
|
|
{
|
2017-03-26 18:38:32 +02:00
|
|
|
*video ^= operators[i];
|
|
|
|
video += 4;
|
2016-07-28 18:12:07 +02:00
|
|
|
}
|
2017-03-26 18:38:32 +02:00
|
|
|
break;
|
2016-07-28 18:12:07 +02:00
|
|
|
|
2017-03-26 18:38:32 +02:00
|
|
|
default: return;
|
2016-07-28 18:12:07 +02:00
|
|
|
}
|
|
|
|
}
|
2016-07-06 11:28:51 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
update()
|
|
|
|
Updates the operators using the given glyph. The operation will not be
|
|
|
|
complete if there are not enough bits available in the operator data.
|
|
|
|
In this case the offset will become negative, which means that the
|
|
|
|
calling procedure has to call operate() and re-call update().
|
|
|
|
`available` represents the number of free bits in the operators (lower
|
|
|
|
bits).
|
|
|
|
Returns the number of bits available after the operation. If it's
|
|
|
|
negative, call operate() and update() again.
|
|
|
|
*/
|
2017-02-25 19:02:07 +01:00
|
|
|
int update(uint32_t *operators, int height, int available, uint32_t *glyph)
|
2016-07-06 11:28:51 +02:00
|
|
|
{
|
|
|
|
// Glyph width.
|
|
|
|
int width = glyph[0] >> 24;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// The glyph mask extracts 'width' bits at the left. The partial mask
|
|
|
|
// is used when there are less than 'width' bits available in the
|
|
|
|
// current data longword.
|
|
|
|
uint32_t glyph_mask = 0xffffffff << (32 - width);
|
|
|
|
uint32_t partial_mask;
|
|
|
|
|
|
|
|
int shift;
|
|
|
|
uint32_t line;
|
|
|
|
|
|
|
|
// Current data longword, next data array index, and number of bits
|
|
|
|
// still available in 'data'.
|
|
|
|
uint32_t data = glyph[0] << 8;
|
|
|
|
int data_index = 1;
|
|
|
|
int bits_available = 24;
|
|
|
|
|
|
|
|
for(i = 0; i < height; i++)
|
|
|
|
{
|
|
|
|
shift = 32 - available;
|
|
|
|
|
|
|
|
// Getting the next 'width' bits. In some cases these bits will
|
2017-04-19 19:57:31 +02:00
|
|
|
// intersect two different longs...
|
2016-07-06 11:28:51 +02:00
|
|
|
line = data & glyph_mask;
|
|
|
|
line = (shift >= 0) ? (line >> shift) : (line << -shift);
|
|
|
|
operators[i] |= line;
|
|
|
|
|
|
|
|
data <<= width;
|
|
|
|
bits_available -= width;
|
|
|
|
|
2017-04-19 19:57:31 +02:00
|
|
|
// ... continue looping until they do.
|
2016-07-06 11:28:51 +02:00
|
|
|
if(bits_available >= 0) continue;
|
|
|
|
|
|
|
|
// Computing a special mask that extracts just the number of
|
|
|
|
// bits missing, and loading a new data longword.
|
|
|
|
partial_mask = 0xffffffff << (32 + bits_available);
|
|
|
|
data = glyph[data_index++];
|
|
|
|
shift += width + bits_available;
|
|
|
|
|
2017-04-19 19:57:31 +02:00
|
|
|
// In case this condition is not verified, the program invokes
|
|
|
|
// undefined behavior because of the bit shift. Anyway, it
|
|
|
|
// means that the end of the end of the operators was reached,
|
|
|
|
// in which case the function should not continue writing.
|
2016-07-06 11:28:51 +02:00
|
|
|
if(shift <= 31)
|
|
|
|
{
|
|
|
|
line = data & partial_mask;
|
|
|
|
line = (shift >= 0) ? (line >> shift) :
|
|
|
|
(line << -shift);
|
|
|
|
operators[i] |= line;
|
|
|
|
}
|
|
|
|
|
|
|
|
data <<= -bits_available;
|
|
|
|
bits_available += 32;
|
|
|
|
}
|
|
|
|
|
|
|
|
return available - width;
|
|
|
|
}
|
2016-07-28 18:12:07 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
render()
|
|
|
|
Renders text without any formatting analysis, using the given operation
|
|
|
|
function.
|
|
|
|
*/
|
|
|
|
void render(int x, int y, const char *str, void (*op)(OPERATE_ARGS))
|
|
|
|
{
|
2017-04-19 19:57:31 +02:00
|
|
|
if(!font) return;
|
|
|
|
|
2016-07-28 18:12:07 +02:00
|
|
|
// Operator data, and number of available bits in the operators (which
|
|
|
|
// is the same for all operators, since they are treated equally).
|
|
|
|
uint32_t *operators;
|
|
|
|
int available;
|
|
|
|
|
2017-04-19 19:57:31 +02:00
|
|
|
// Raw glyph data, each glyph being represented as one or several
|
2016-07-28 18:12:07 +02:00
|
|
|
// longwords, and an index in this array.
|
|
|
|
uint32_t *data = (uint32_t *)font->glyphs;
|
|
|
|
int index;
|
|
|
|
|
2017-04-19 19:57:31 +02:00
|
|
|
// Storage height of each glyph. This is a constant for all glyphs
|
|
|
|
// because the format required it. It makes this routine consequently
|
|
|
|
// faster.
|
|
|
|
int height = font->data_height;
|
2016-07-28 18:12:07 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Allocating data. There will be one operator for each line.
|
|
|
|
if(x > 127 || y > 63 || y <= -height) return;
|
2016-08-14 19:57:58 +02:00
|
|
|
if(y + height > 64) height = 64 - y;
|
2016-07-28 18:12:07 +02:00
|
|
|
|
|
|
|
operators = alloca(height * sizeof(uint32_t));
|
|
|
|
if(!operators) return;
|
2017-04-19 19:57:31 +02:00
|
|
|
for(int i = 0; i < height; i++) operators[i] = 0;
|
2016-07-28 18:12:07 +02:00
|
|
|
|
|
|
|
// Computing the initial operator offset to have 32-aligned operators.
|
|
|
|
// This allows to write directly video ram longs instead of having to
|
|
|
|
// shift operators, and do all the vram operation twice.
|
2017-04-17 12:56:27 +02:00
|
|
|
// I double-checked that this operation is still valid when x is
|
|
|
|
// negative.
|
2016-07-28 18:12:07 +02:00
|
|
|
available = 32 - (x & 31);
|
|
|
|
x &= ~31;
|
|
|
|
|
|
|
|
// Displaying character after another.
|
|
|
|
while(*str)
|
|
|
|
{
|
|
|
|
index = getCharacterIndex(*str++);
|
|
|
|
if(index < 0) continue;
|
|
|
|
|
|
|
|
// Updating the operators.
|
|
|
|
available = update(operators, height, available, data + index);
|
|
|
|
|
|
|
|
// Continue until operators are full (this includes an
|
|
|
|
// additional bit to add a space between each character).
|
|
|
|
if(available > 1)
|
|
|
|
{
|
|
|
|
available--;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// When operators are full, updating the video ram and
|
|
|
|
// preparing the operators for another row.
|
|
|
|
|
2016-12-22 23:08:44 +01:00
|
|
|
if(x >= 0) (*op)(operators, height, x, y);
|
2016-07-28 18:12:07 +02:00
|
|
|
x += 32;
|
|
|
|
if(x > 96) break;
|
|
|
|
|
2017-04-19 19:57:31 +02:00
|
|
|
for(int i = 0; i < height; i++) operators[i] = 0;
|
2016-07-28 18:12:07 +02:00
|
|
|
if(available >= 0)
|
|
|
|
{
|
2017-04-19 19:57:31 +02:00
|
|
|
available += 31;
|
2016-07-28 18:12:07 +02:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2017-04-19 19:57:31 +02:00
|
|
|
// Finishing update, in cases where it has been only partially
|
|
|
|
// done because there was not enough bits available to fit all
|
|
|
|
// the information. Also adding a space, assuming that
|
|
|
|
// characters aren't more than 30 bits wide. (=p)
|
2016-07-28 18:12:07 +02:00
|
|
|
available += 32 + (data[index] >> 24);
|
|
|
|
available = update(operators, height, available, data + index);
|
|
|
|
available--;
|
|
|
|
}
|
|
|
|
|
2017-04-19 19:57:31 +02:00
|
|
|
// Final operation. This condition allows a single bit of the operators
|
|
|
|
// to be used - that's because the loop will add a final spacing pixel.
|
|
|
|
if(x <= 96 && available < 31) (*op)(operators, height, x, y);
|
2016-07-28 18:12:07 +02:00
|
|
|
}
|