Page 1 of 2

[Solved] Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 3:33 am
by Octacone
Hello.

I need some help with drawing text to the screen. At the moment I have a function that can draw one individual character, and it works very well.
I use this to draw one character:

Code: Select all

InternalDrawCharacter(Point position, int characterWidth, int characterArraySize, Color drawColor, int* character);
Now I need a method that would recognize my Input like for example DrawText((Point) {0, 0}, "Abc123Test", ColorRed); and use that function above to draw text at given position. Now I am facing some issues. I have exactly 100 characters. That means 100 different cases inside my switch statement. I don't think that function it is efficient enough. I tried using switch statement but I was getting overlapping issues (if one of the characters inside a string was not the same...etc). I just need a simple but efficient method to recognize my input from my method, draw wanted character, + calculate spacing between characters (character width + 1) and if I type \n I just need to add 16 to my Y.

Let's just begin with:

Code: Select all

void DrawText(string text, Color colorText, Point position)
{
    for(int i = 0; i < stringLength(text); i++)
    {
        DrawCharacterMethodIneed(position, color, text[i]);
    }
}
I hope you all understand what I want.

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 3:42 am
by BrightLight
If you have a function that draws a character and you need a function to draw a string, that's really easy.
Psuedo-code might look like:

Code: Select all

void draw_string(char* string, int x, int y)
{
	while(string[0] != 0)
	{
		draw_char(string[0], x, y);	// void draw_char(char character, int x, int y);
		x += character_width;
		if(x >= screen_width)
		{
			x = 0;
			y += character_height;
		}
		string++;	// move on
	}
}
That should give you the basic idea. I think you can implement carriage returns and new lines from here. ;)

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 3:44 am
by iansjack
I don't understand why you need a switch statement within your DrawText function. Surely you just pass the character from your string to the character drawing function?

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 3:46 am
by Octacone
omarrx024 wrote:If you have a function that draws a character and you need a function to draw a string, that's really easy.
Psuedo-code might look like:

Code: Select all

void draw_string(char* string, int x, int y)
{
	while(string[0] != 0)
	{
		draw_char(string[0], x, y);	// void draw_char(char character, int x, int y);
		x += character_width;
		if(x >= screen_width)
		{
			x = 0;
			y += character_height;
		}
		string++;	// move on
	}
}
That should give you the basic idea. I think you can implement carriage returns and new lines from here. ;)
iansjack wrote:I don't understand why you need a switch statement within your DrawText function. Surely you just pass the character from your string to the character drawing function?
How is the function going to know which character to draw?
My characters are bitmap arrays. --->>InternalDrawCharacter is a function that draws that array. You need to pass the character you want to draw + array size + array width.

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 3:51 am
by BrightLight
Wrong, you have an array of 256 characters (that's how the BIOS font is, and you said in another thread you were using the BIOS's font), each character 8*16, 1 bit per pixel. Each character is therefore 16 bytes in size, and you have 256 characters, numbered from 0 to 255, thus a 4096 byte buffer. The raw font data for a specific character should be (character * 16) + font_data, where font_data is the contiguous buffer of 4096 bytes. Dear god, you are losing all performance by using switch statements; just use an array.

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 3:52 am
by Octacone
omarrx024 wrote:Wrong, you have an array of 256 characters (that's how the BIOS font is, and you said in another thread you were using the BIOS's font), each character 8*16, 1 bit per pixel. Each character is therefore 16 bytes in size, and you have 256 characters, numbered from 0 to 255, thus a 4096 byte buffer. The raw font data for a specific character should be (character * 16) + font_data, where font_data is the contiguous buffer of 4096 bytes. Dear god, you are losing all performance by using switch statements; just use an array.
Wrong, I don't use BIOS font. I just ported it into bitmap arrays. Every array is different, that I how I made it. Here is an example:

Code: Select all

int Character_S_Width = 7;
int Character_S_ArraySize = 70;
int Character_S[] =
{
	0, 1, 1, 1, 1, 1, 0,
	1, 1, 0, 0, 0, 1, 1,
	1, 1, 0, 0, 0, 1, 1,
	0, 1, 1, 0, 0, 0, 0,
	0, 0, 1, 1, 1, 0, 0,
	0, 0, 0, 0, 1, 1, 0,
	0, 0, 0, 0, 0, 1, 1,
	1, 1, 0, 0, 0, 1, 1,
	1, 1, 0, 0, 0, 1, 1,
	0, 1, 1, 1, 1, 1, 0
};

int Character_UpperDot_Width = 2;
int Character_UpperDot_ArraySize = 20;
int Character_UpperDot[] =
{
	1, 1,  
	1, 1, 
	0, 0,  
	0, 0,   
	0, 0, 
	0, 0, 
	0, 0,
	0, 0, 
	0, 0, 
	0, 0
};

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 4:02 am
by Octocontrabass
octacone wrote: Wrong, I don't use BIOS font. I just ported it into bitmap arrays. Every array is different, that I how I made it. Here is an example:

Code: Select all

int Character_S_Width = 7;
int Character_S_ArraySize = 70;
int Character_S[] =
{
...
Instead of a single int for character width, why not an array?

Instead of a single int for "array size", why not an array? (For that matter, why not character height?)

Instead of a single array for each character's data, why not an array of pointers to each character array?

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 4:20 am
by Octacone
Octocontrabass wrote:
octacone wrote: Wrong, I don't use BIOS font. I just ported it into bitmap arrays. Every array is different, that I how I made it. Here is an example:

Code: Select all

int Character_S_Width = 7;
int Character_S_ArraySize = 70;
int Character_S[] =
{
...
Instead of a single int for character width, why not an array?

Instead of a single int for "array size", why not an array? (For that matter, why not character height?)

Instead of a single array for each character's data, why not an array of pointers to each character array?
I don't need height since it is not required by my internal function.
What do you mean by that? Why to make an array for each character width/array size?

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 4:33 am
by onlyonemac
octacone wrote:Now I am facing some issues. I have exactly 100 characters. That means 100 different cases inside my switch statement.
Firstly, you will need 256 characters. It doesn't matter if some of those are unused, or if you're treating them differently (e.g. '\n' which will make a newline, rather than printing character 10). Now that you've got 256 characters, directly mapped to the ASCII code, you can do away with the switch statement; create an array of 256 character bitmaps, then just plot the bitmap at the index equal to the ASCII value of the character that you want to draw. So something like this:

Code: Select all

typedef uint8_t[8] character_bitmap; // for an 8x8 character; change as appropriate for your font

character_bitmap font[256];

draw_character(char character, int x, int y)
{
    character_bitmap bitmap = font[character];
    draw_bitmap(bitmap, x, y); // however you're plotting the bitmap
}

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 4:38 am
by Octacone
onlyonemac wrote:
octacone wrote:Now I am facing some issues. I have exactly 100 characters. That means 100 different cases inside my switch statement.
Firstly, you will need 256 characters. It doesn't matter if some of those are unused, or if you're treating them differently (e.g. '\n' which will make a newline, rather than printing character 10). Now that you've got 256 characters, directly mapped to the ASCII code, you can do away with the switch statement; create an array of 256 character bitmaps, then just plot the bitmap at the index equal to the ASCII value of the character that you want to draw. So something like this:

Code: Select all

typedef uint8_t[8] character_bitmap; // for an 8x8 character; change as appropriate for your font

character_bitmap font[256];

draw_character(char character, int x, int y)
{
    character_bitmap bitmap = font[character];
    draw_bitmap(bitmap, x, y); // however you're plotting the bitmap
}
I only have 100 characters and I do not need more. So I just need to create 156 empty character with random names? :|
So I need to assign ASCII values to every each one of my 100 characters? I can not do 8x8 or anything like that, my arrays are all different. I also need to pass Array size information and character width information to my internal method in order to draw a character.

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 4:47 am
by Octocontrabass
It's all about using the right tool for the job. A switch statement requires hundreds of lines of code.

Code: Select all

switch( c )
{
	case 'S':
		InternalDrawCharacter( position, Character_S_Width, Character_S_ArraySize, drawColor, Character_S );
		break;
	// repeat 99 times
}
If you use an array, it's trivial.

Code: Select all

InternalDrawCharacter( position, Character_Width[c], Character_ArraySize[c], drawColor, Character[c] );
You would then define those three arrays like this: (note that this is just an example, you must modify it to be in ASCII order)

Code: Select all

const int Character_Width[128] =
{
	2, 4, 6, 8, ...
}
const int Character_ArraySize[128] =
{
	16, 32, 48, 64, ...
}
const int * const Character[128] =
{
	Character_empty, Character_A, Character_B, ...
}

Keep in mind that this isn't flexible enough for proper internationalization, where you'll need true Unicode support, bidirectional support, and somewhat more complex mapping between code points and glyphs.

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 5:09 am
by Octacone
Octocontrabass wrote:It's all about using the right tool for the job. A switch statement requires hundreds of lines of code.

Code: Select all

switch( c )
{
	case 'S':
		InternalDrawCharacter( position, Character_S_Width, Character_S_ArraySize, drawColor, Character_S );
		break;
	// repeat 99 times
}
If you use an array, it's trivial.

Code: Select all

InternalDrawCharacter( position, Character_Width[c], Character_ArraySize[c], drawColor, Character[c] );
You would then define those three arrays like this: (note that this is just an example, you must modify it to be in ASCII order)

Code: Select all

const int Character_Width[128] =
{
	2, 4, 6, 8, ...
}
const int Character_ArraySize[128] =
{
	16, 32, 48, 64, ...
}
const int * const Character[128] =
{
	Character_empty, Character_A, Character_B, ...
}

Keep in mind that this isn't flexible enough for proper internationalization, where you'll need true Unicode support, bidirectional support, and somewhat more complex mapping between code points and glyphs.
Read this about 11 times. I finally get it. So if I try this I am not going to get any type of errors because of using not static variables inside a struct?

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 5:11 am
by Roman
I do it this way:
1. The code sets the "best" graphics mode available, it also saves the pixel format.
2. The PSF (a simple format supported by the Linux kernel) font parser generates the font cache, which is in my case a hash map, where the key is the UTF-8 sequence, and the value is the glyph. A glyph is just a fixed size array of pixels.
3. Upon printing a character the code gets the glyph and copies it to the frame buffer.

Note, that it is not the best way to do these things. Unicode is much more complex, vector fonts are also superior to bitmap ones. But it's enough for my purposes at the moment.

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 5:17 am
by Octocontrabass
octacone wrote:So if I try this I am not going to get any type of errors because of using not static variables inside a struct?
There are no structs here.

Re: Best way of drawing text in graphics mode

Posted: Fri Aug 19, 2016 5:23 am
by Octacone
Roman wrote:I do it this way:
1. The code sets the "best" graphics mode available, it also saves the pixel format.
2. The PSF (a simple format supported by the Linux kernel) font parser generates the font cache, which is in my case a hash map, where the key is the UTF-8 sequence, and the value is the glyph. A glyph is just a fixed size array of pixels.
3. Upon printing a character the code gets the glyph and copies it to the frame buffer.

Note, that it is not the best way to do these things. Unicode is much more complex, vector fonts are also superior to bitmap ones. But it's enough for my purposes at the moment.
You are using one very sophisticated method. You can even generate the font cache. I already have my own way of plotting the characters themselves.