Commits

volca  committed 032a25a

* Added text block rendering function

  • Participants
  • Parent commits a936e1b

Comments (0)

Files changed (2)

File include/fntsys.h

 * @return Width of the string rendered */
 int fntRenderString(int font, int x, int y, short aligned, const unsigned char* string, u64 colour);
 
+/** Renders a text with specified window dimensions */
+void fntRenderText(int font, int sx, int sy, size_t width, size_t height, const unsigned char* string, u64 colour);
+
+/** replaces spaces with newlines so that the text fits into the specified width.
+  * @note A destrutive operation - modifies the given string!
+  */
+void fntFitString(int font, unsigned char *string, size_t width);
+
 /** Calculates the width of the given text string
 * We can't use the height for alignment, as the horizontal center would depends of the contained text itself */
 int fntCalcDimensions(int font, const unsigned char* str);

File src/fntsys.c

 	// return to the prev. aspect ratio
 	rmSetAspectRatio(aw, ah);
 
-	// rmDispatch();
+	return w;
+}
 
-	return w;
+void fntRenderText(int font, int sx, int sy, size_t width, size_t height, const unsigned char* string, u64 colour) {
+	// wait for font lock to unlock
+	WaitSema(gFontSemaId);
+	font_t *fnt = &fonts[font];
+	SignalSema(gFontSemaId);
+
+	int x = sx;
+	int y = sy;
+	int xm = sx + width;
+	int ym = sy + height;
+	
+	// backup the aspect ratio, restore 1:1 to have the font rendering clean
+	float aw, ah;
+	rmGetAspectRatio(&aw, &ah);
+	rmResetAspectRatio();
+
+	uint32_t codepoint;
+	uint32_t state = 0;
+	int w = 0, d;
+	FT_Bool use_kerning = FT_HAS_KERNING(fnt->face);
+	FT_UInt glyph_index, previous = 0;
+	FT_Vector delta;
+
+	int hmax = 0;
+	
+	// Note: We need to change this so that we'll accumulate whole word before doing a layout with it
+	// for now this method breaks on any character - which is a bit ugly
+	
+	// I don't want to do anything complicated though so I'd say
+	// we should instead have a dynamic layout routine that'll replace spaces with newlines as appropriate
+	// because that'll make the code run only once per N frames, not every frame
+	
+	// cache glyphs and render as we go
+	for (; *string; ++string) {
+		unsigned char c = *string;
+
+		if (utf8Decode(&state, &codepoint, c)) // accumulate the codepoint value
+			continue;
+
+		fnt_glyph_cache_entry_t* glyph = fntCacheGlyph(fnt, codepoint);
+		if (!glyph)
+			continue;
+		
+		// kerning
+		if (use_kerning && previous) {
+			glyph_index = FT_Get_Char_Index(fnt->face, codepoint);
+			if (glyph_index) {
+				FT_Get_Kerning(fnt->face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
+				d = delta.x >> 6;
+				x += d;
+				w += d;
+			}
+			previous = glyph_index;
+		}
+
+		// do we fit to xmax?
+		if ((x + glyph->width > xm) || (codepoint == '\n')) {
+			x = sx;
+			// y += hmax + 5;
+			y += MENU_ITEM_HEIGHT; // hmax is too tight and unordered, generally
+			hmax = 0;
+			
+			if (codepoint == '\n')
+				continue;
+			
+			if (y > ym) // stepped over ymax
+				break;
+		}
+		
+		if (glyph->height > hmax)
+			hmax = glyph->height;
+		
+		// only if glyph has atlas placement
+		if (glyph->allocation) {
+			rmSetupQuad(NULL, x, y, ALIGN_NONE, glyph->width, glyph->height, colour, &quad);
+			quad.ul.x += glyph->ox;
+			quad.br.x += glyph->ox;
+			quad.ul.y += glyph->oy;
+			quad.br.y += glyph->oy;
+			
+			// UV is our own, custom thing here
+			quad.txt = &glyph->atlas->surface;
+			quad.ul.u = glyph->allocation->x;
+			quad.ul.v = glyph->allocation->y;
+			
+			quad.br.u = glyph->allocation->x + glyph->width;
+			quad.br.v = glyph->allocation->y + glyph->height;
+			
+			rmDrawQuad(&quad);
+		}
+		
+		int ofs = glyph->shx >> 6;
+		w += ofs;
+		x += ofs;
+		y += glyph->shy >> 6;
+	}
+
+	// return to the prev. aspect ratio
+	rmSetAspectRatio(aw, ah);
+}
+
+void fntFitString(int font, unsigned char *string, size_t width) {
+	size_t cw = 0;
+	unsigned char *str = string;
+	size_t spacewidth = fntCalcDimensions(font, " ");
+	unsigned char *psp = NULL;
+	
+	while (*str) {
+		// scan forward to the next whitespace
+		unsigned char *sp = str;
+		for (; *sp && *sp != ' ' && *sp != '\n'; ++sp);
+		
+		// store what was there before
+		unsigned char osp = *sp;
+		
+		// newline resets the situation
+		if (osp == '\n') {
+			cw = 0;
+			str = ++sp;
+			psp = NULL;
+			continue;
+		}
+		
+		// terminate after the word
+		*sp = '\0';
+		
+		// Calc the font's width...
+		// NOTE: The word was terminated, so we're seeing a single word
+		// on that position
+		size_t ww = fntCalcDimensions(font, str);
+		
+		if (cw + ww > width) {
+			if (psp) {
+				// we have a prev space to utilise (wrap on it)
+				*psp = '\n';
+				*sp = osp;
+				cw = ww;
+				psp = sp;
+			} else {
+				// no prev. space to hijack, must break after the word
+				// this will mean overflowed text...
+				*sp = '\n';
+				cw = 0;
+			}
+		} else {
+			cw += ww;
+			*sp = osp;
+			psp = sp;
+		}
+		
+		cw += spacewidth;
+		str = ++sp;
+	}
 }
 
 int fntCalcDimensions(int font, const unsigned char* str) {