SYS_GetFontTexture & SYS_GetFontTexel

Eke
Posts: 64
Joined: Sat Mar 01, 2008 11:01 am

SYS_GetFontTexture & SYS_GetFontTexel

Post by Eke » Wed Nov 05, 2008 3:33 pm

Hi,

in my projects, I want to be able to easily display text in various font size but still want to rely on the IPL font only (and do no want to use libfreetype or any embedded PNG fonts)

The best way to do it seems to read/decode the font from the Boot ROM (which we are already doing fine) then render a textured quad from each char, based on the font data.

I came across those two little undocumented functions (SYS_GetFontTexture & SYS_GetFontTexel) in system.c and was wondering if some libogc authors could help me:

1/ to understand how to use these functions (what are the arguments passed in,...)
2/ to know what I should do first to be able to use them (is SYS_InitFont enough, why passing a fontheader in argument ?)

Thank you

Samson
Posts: 35
Joined: Mon May 19, 2008 8:05 am

Re: SYS_GetFontTexture & SYS_GetFontTexel

Post by Samson » Sun Nov 09, 2008 9:12 pm

Based on yagcd the font is a compressed I4 texture. And looking at libogc/system.c I'd say SYS_InitFont decompresses the font and fills in the font header with the information how to render text.
SYS_GetFontTexture seems to just return the texture address and glyph position, if you want to render it using the existing texture, SYS_GetFontTexel copies the glyph into your own texture.
Trying to use this is on my TODO list, but so far I didn't have the time. :( Let me know if you get anything working.

By the way, would it be worth overriding the font encoding instead of selecting it automatically? Both the PAL and NTSC firmware seem to have the font at the same position, so an application could use whatever encoding it wants.

Eke
Posts: 64
Joined: Sat Mar 01, 2008 11:01 am

Re: SYS_GetFontTexture & SYS_GetFontTexel

Post by Eke » Thu Jan 22, 2009 10:14 am

Samson wrote:Based on yagcd the font is a compressed I4 texture. And looking at libogc/system.c I'd say SYS_InitFont decompresses the font and fills in the font header with the information how to render text.
SYS_GetFontTexture seems to just return the texture address and glyph position, if you want to render it using the existing texture, SYS_GetFontTexel copies the glyph into your own texture.
Trying to use this is on my TODO list, but so far I didn't have the time. :( Let me know if you get anything working.

By the way, would it be worth overriding the font encoding instead of selecting it automatically? Both the PAL and NTSC firmware seem to have the font at the same position, so an application could use whatever encoding it wants.
seems like I don't get it working but at least this lead me trying to figure how the stuff was working

now there are some things confusing me, here is what I figured so far (would have been easier if the code had been commented btw :!: ) :

1/ beside the wellknown YaY0 font compression, the original texture data is also in compressed format: 1 byte represents 4 pixels, so each pixel is coded as a 2-bytes value (0-3) and represent an index into a 4-entry color lookup table stored at the end of the font header (c0,c1,c2,c3 in libogc definition). Each LUT entry is one byte, with a 4bits color intensity value most probably duplicated in LSB and MSB...

2/ the compressed texture data is "expanded" to a I4 texture (1 byte = 2 pixels, each 4-bits nibble representing pixel color intensity) by SYS_InitFont and the original compresssed data is overwritten by I4 texture data:

Code: Select all

sys_fontimage = (u8*)((((u32)unpacked_data+sys_fontdata->sheet_image)+31)&~31);
__expand_font(unpacked_data+sys_fontdata->sheet_image,sys_fontimage);
The expand function starts from the end of the texture data so there is no mistake or unexpected overwrites

3/ SYS_GetFontTexture returns a pointer to the decompressed texture data (I4 format) and horizontal/vertical offsets (in pixels) to the cell containing the character. It's up to you to read the texture data correctly (all texture are generally stored in 32 bytes block, which means 8x8 texels blocks are stored consecutively in memory)

Code: Select all

*image = [b]sys_fontimage[/b]+(sys_fontdata->sheet_size*sheets);
4/ SYS_GetFontTexel, on the contrary, is trying to expand compressed texture data "on the fly", and fill the texture data pointer you passed in parameter as a I4 texture. Maybe I'm missing something but this seems problematic: since the compressed texture data has already been expanded AND overwriten in SYS_InitFont, you are "expanding" something that is already in I4 format, which is wrong.

Code: Select all

img_start = (u8*)[b]sys_fontdata+sys_fontdata->sheet_image[/b]+(sys_fontdata->sheet_size*sheets);

Then, it seems there are two errors in this function, regarding the byte offset calculation in the destination texture:

Code: Select all

 ptr2 = image+((ypos/8)*(((stride<<2)/8)<<5));
 ptr2 = ptr2+(((xpos+pos)/8)<<5);
 ptr2 = ptr2+(((xpos+pos)%8)/2);
We always have to keep in mind that texture data are stored as consecutive 8x8 texels blocks in memory.

- "stride" generally means the width in bytes of the texture image, so to get the base offset for a given line, it should be:
vertical offset in number of 8x8 texels blocks = line / 8
horizontal offset in number of 8x8 texels blocks = (stride * 2) / 8
offset in bytes = (line /8) * (stride * 2 / 8) * 32

so

Code: Select all

ptr2 = image+((ypos/8)*(((stride<<1)/8)<<5));
instead of

Code: Select all

ptr2 = image+((ypos/8)*(((stride<<2)/8)<<5));

- a final vertical offset is missing, the offset within the 8x8 block. The following line should be added :

Code: Select all

ptr2 = ptr2+((ypos%8)<<2);

Hope that helps..

Eke
Posts: 64
Joined: Sat Mar 01, 2008 11:01 am

Re: SYS_GetFontTexture & SYS_GetFontTexel

Post by Eke » Sun Jan 25, 2009 7:29 pm

ok, let's continue on this one (maybe it could be useful to someone else)

after fixing the "SYS_GetFontTexel," function, I found out that this was still not working: got a blank texture

the strange thing is: if I replace the two "while" nested loop by two "for" loops, this is now working :shock:

Code: Select all

	for(ypos = 0;ypos<sys_fontdata->cell_height;ypos++) {
		for(xpos = 0;xpos<sys_fontdata->cell_width;xpos++) {
			ptr1 = img_start+((((sys_fontdata->sheet_width/8)<<5)/2)*((ypos+yoff)/8));
			ptr1 = ptr1+(((xpos+xoff)/8)<<4);
			ptr1 = ptr1+(((ypos+yoff)%8)<<1);
			ptr1 = ptr1+(((xpos+xoff)%8)/4);

			ptr2 = image+((ypos/8)*(((stride<<1)/8)<<5));
			ptr2 = ptr2+(((xpos+pos)/8)<<5);
			ptr2 = ptr2+(((xpos+pos)%8)/2);
			ptr2 = ptr2+(((ypos+yoff)%8)<<2);

			idx = (*ptr1>>(6-(((xpos+pos)%4)<<1)))&0x03;
			val = data[idx];
			if(((xpos+pos)%2)) mask = 0x0f;
			else mask = 0xf0;

			*ptr2 = *ptr2|(val&mask);
		}
	}
works but

Code: Select all

	while(ypos<sys_fontdata->cell_height) {
		while(xpos<sys_fontdata->cell_width) {
			ptr1 = img_start+((((sys_fontdata->sheet_width/8)<<5)/2)*((ypos+yoff)/8));
			ptr1 = ptr1+(((xpos+xoff)/8)<<4);
			ptr1 = ptr1+(((ypos+yoff)%8)<<1);
			ptr1 = ptr1+(((xpos+xoff)%8)/4);

			ptr2 = image+((ypos/8)*(((stride<<1)/8)<<5));
			ptr2 = ptr2+(((xpos+pos)/8)<<5);
			ptr2 = ptr2+(((xpos+pos)%8)/2);
			ptr2 = ptr2+(((ypos+yoff)%8)<<2);

			idx = (*ptr1>>(6-(((xpos+pos)%4)<<1)))&0x03;
			val = data[idx];
			if(((xpos+pos)%2)) mask = 0x0f;
			else mask = 0xf0;

			*ptr2 = *ptr2|(val&mask);
			xpos++;
		}
		ypos++;
	}
doesn't !

can some C guru explain me why ?

Samson
Posts: 35
Joined: Mon May 19, 2008 8:05 am

Re: SYS_GetFontTexture & SYS_GetFontTexel

Post by Samson » Sun Jan 25, 2009 9:40 pm

Maybe because xpos is not reset to 0 for each row? I assume ypos is initialised to 0.

Eke
Posts: 64
Joined: Sat Mar 01, 2008 11:01 am

Re: SYS_GetFontTexture & SYS_GetFontTexel

Post by Eke » Mon Jan 26, 2009 9:34 am

exactly, I figured this a little later :|

ANother thing I figured out: the SYS_InitFont crashes the system

indeed, the _expand_font (which expand compressed I4 texture data to normal I4 texture data) requires at least twice the size of the original FONT.

but (let's take the ANSI example):

-YAYO compressed font is 12288 bytes read in BOOTROM
-uncompressed font is 65808 bytes (according to the YAY0 header)
-libogc allocates 131072+288 bytes for the FONT buffer
-YAY0 font is stored at the end of the buffer (exactly buffer_address+131072+288-12288)
-It is then decompresssed at buffer_address+288

This only leaves (131072+288)-288-65808=65264 bytes after the raw texture data

However, decompressed texture data is 131072 bytes (512*512 pixels), which means that the raw one is half that size (65536 bytes) and that he "expand" function requires at least the same size after the end of the raw texture data.

As you can see, there is some memory space missing, which results in memory overflow and program crash :!:

The solution is to decompress the YAY0 compressed font at the start of the allocated buffer (and NOT at buffer_address+288 as SYS_InitFont is doing)

shagkur
Posts: 53
Joined: Thu Sep 08, 2005 8:40 pm

Re: SYS_GetFontTexture & SYS_GetFontTexel

Post by shagkur » Tue Jan 27, 2009 10:36 am

Hi,

first of all: Thanks for pointing this out.
To the Yay0 decompression: this 288byte offset is needed because if there's a "link" in the compressed data the offset may become negative.
And while the destination buffer is used to read out from that offset to write it to the current position in the dest buffer you need to have this 288B offset.
Therefor it's probably better to double the 288B extension rather than to start from 0 in the dest buffer for decompression.

regards
shagkur

Eke
Posts: 64
Joined: Sat Mar 01, 2008 11:01 am

Re: SYS_GetFontTexture & SYS_GetFontTexel

Post by Eke » Tue Jan 27, 2009 12:11 pm

Oh, I see.. I'll leave the correction to you then

Anyway, i finally get working IPL font rendering using GX texture

Just to let you know, I had to modify the SYS_InitFont function to return a pointer a sys_fontheader structure:

Code: Select all

return sys_fontdata;
instead of simply doing this:

Code: Select all

*font_header = *sys_fontdata;
because it didn't seems to return correct header information


I also fixed the SYS_GetFontTexel function:

- removed redundant font expanding (as it is already done during initialization)
- fixed source pointer calculation (as source texture is already expanded and now take twice memory as before)
- fixed destination pointer calculation (stride parameter was not handled properly and line offset within the 8x8 tile was missing)
- fixed character clamping (first and last character were considered as invalid, now uses font header informations)
- fixed nested loops indexes initialization (xpos was not reinitialized before the loop)

Code: Select all

void SYS_GetFontTexel(s32 c,void *image,s32 pos,s32 stride,s32 *width)
{
	u32 sheets,rem;
	u32 xoff,yoff;
	u32 xpos,ypos;
	u8 *img_start;
	u8 *ptr1,*ptr2;

	if(!sys_fontwidthtab || ! sys_fontimage) return;

	if(c<sys_fontdata->first_char || c>sys_fontdata->last_char) c = sys_fontdata->inval_char;
	else c -= sys_fontdata->first_char;

	sheets = c/sys_fontcharsinsheet;
	rem = c%sys_fontcharsinsheet;
	xoff = (rem%sys_fontdata->sheet_column)*sys_fontdata->cell_width;
	yoff = (rem/sys_fontdata->sheet_column)*sys_fontdata->cell_height;
	img_start = sys_fontimage+(sys_fontdata->sheet_size*sheets);

	xpos = 0; ypos = 0;
	while(ypos<sys_fontdata->cell_height) {
		xpos = 0;
		while(xpos<sys_fontdata->cell_width) {
			ptr1 = img_start+(((sys_fontdata->sheet_width/8)<<5)*((ypos+yoff)/8));
			ptr1 = ptr1+(((xpos+xoff)/8)<<5);
			ptr1 = ptr1+(((ypos+yoff)%8)<<2);
			ptr1 = ptr1+(((xpos+xoff)%8)/2);

			ptr2 = image+((ypos/8)*(((stride<<1)/8)<<5));
			ptr2 = ptr2+(((xpos+pos)/8)<<5);
			ptr2 = ptr2+((ypos%8)<<2);
			ptr2 = ptr2+(((xpos+pos)%8)/2);

			*ptr2 = *ptr1;

			xpos++;
		}
		ypos++;
	}
	*width = sys_fontwidthtab[c];
}

shagkur
Posts: 53
Joined: Thu Sep 08, 2005 8:40 pm

Re: SYS_GetFontTexture & SYS_GetFontTexel

Post by shagkur » Tue Jan 27, 2009 12:43 pm

Hi,

well, apologize for all the hassle you got with that. :|
Also i've taken again a deeper look into the yay0 decompression and you was right with removing the 288B offset.
Dunno where this comes from, but probably from the old cube days.
So i now decompress, as proposed by you, directly into the beginning of the buffer.

Hmm....quite strange that "*font_header = *sys_fontheader" doesnt work. should use the builtin memcpy.
Could you do a manual memcpy at this line for a test? (I'm not able to test this right now - currently i'm in the office)

Also i'd like to thank you for fixing the other function. I'll immediately take this into the SVN :)

kind regards
shagkur

Eke
Posts: 64
Joined: Sat Mar 01, 2008 11:01 am

Re: SYS_GetFontTexture & SYS_GetFontTexel

Post by Eke » Tue Jan 27, 2009 9:19 pm

No problem, I actually found some fun in understanding how stuff work at a lower level ...

about that pointer issue, memcpy does not work either. The two following implementations work though:

Code: Select all

u32 SYS_InitFont(sys_fontheader **font_header)
{
	/*if(!font_header) return 0;
           memset(font_header,0,sizeof(sys_fontheader));*/
	
...
       *font_header = sys_fontdata;
}

Code: Select all

sys_fontheader *SYS_InitFont()
{
...
		return sys_fontdata;
}


About the "SYS_GetFontTexel" function, here is a better (faster) implementation. The second loop should be something like this (xpos+=2 instead of xpos++):

Code: Select all

while(xpos<sys_fontdata->cell_width) {
			ptr1 = img_start+(((sys_fontdata->sheet_width/8)<<5)*((ypos+yoff)/8));
			ptr1 = ptr1+(((xpos+xoff)/8)<<5);
			ptr1 = ptr1+(((ypos+yoff)%8)<<2);
			ptr1 = ptr1+(((xpos+xoff)%8)/2);

			ptr2 = image+((ypos/8)*(((stride<<1)/8)<<5));
			ptr2 = ptr2+(((xpos+pos)/8)<<5);
			ptr2 = ptr2+((ypos%8)<<2);
			ptr2 = ptr2+(((xpos+pos)%8)/2);

			*ptr2 = *ptr1;
			xpos+=2;
		}

Post Reply

Who is online

Users browsing this forum: Google [Bot] and 1 guest