/**************************************************************************** * Copyright (C) 2015 Dimok * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . ****************************************************************************/ #include "video/CVideo.h" #include "FreeTypeGX.h" #include "GuiText.h" FreeTypeGX * GuiText::presentFont = NULL; int GuiText::presetSize = 28; int GuiText::presetInternalRenderingScale = 2.0f; //Lets render the font at the doubled size. This make it even smoother! int GuiText::presetMaxWidth = 0xFFFF; int GuiText::presetAlignment = ALIGN_CENTER | ALIGN_MIDDLE; GX2ColorF32 GuiText::presetColor = (GX2ColorF32){ 1.0f, 1.0f, 1.0f, 1.0f }; #define TEXT_SCROLL_DELAY 6 #define TEXT_SCROLL_INITIAL_DELAY 10 #define MAX_LINES_TO_DRAW 10 /** * Constructor for the GuiText class. */ GuiText::GuiText() { text = NULL; size = presetSize; currentSize = size; color = glm::vec4(presetColor.r, presetColor.g, presetColor.b, presetColor.a); alpha = presetColor.a; alignment = presetAlignment; maxWidth = presetMaxWidth; wrapMode = 0; textWidth = 0; font = presentFont; linestodraw = MAX_LINES_TO_DRAW; textScrollPos = 0; textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; textScrollDelay = TEXT_SCROLL_DELAY; defaultBlur = 4.0f; blurGlowIntensity = 0.0f; blurAlpha = 0.0f; blurGlowColor = glm::vec4(0.0f); internalRenderingScale = presetInternalRenderingScale; } GuiText::GuiText(const char * t, int s, const glm::vec4 & c) { text = NULL; size = s; currentSize = size; color = c; alpha = c[3]; alignment = ALIGN_CENTER | ALIGN_MIDDLE; maxWidth = presetMaxWidth; wrapMode = 0; textWidth = 0; font = presentFont; linestodraw = MAX_LINES_TO_DRAW; textScrollPos = 0; textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; textScrollDelay = TEXT_SCROLL_DELAY; defaultBlur = 4.0f; blurGlowIntensity = 0.0f; blurAlpha = 0.0f; blurGlowColor = glm::vec4(0.0f); internalRenderingScale = presetInternalRenderingScale; if(t) { text = FreeTypeGX::charToWideChar(t); if(!text) return; textWidth = font->getWidth(text, currentSize); } } GuiText::GuiText(const wchar_t * t, int s, const glm::vec4 & c) { text = NULL; size = s; currentSize = size; color = c; alpha = c[3]; alignment = ALIGN_CENTER | ALIGN_MIDDLE; maxWidth = presetMaxWidth; wrapMode = 0; textWidth = 0; font = presentFont; linestodraw = MAX_LINES_TO_DRAW; textScrollPos = 0; textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; textScrollDelay = TEXT_SCROLL_DELAY; defaultBlur = 4.0f; blurGlowIntensity = 0.0f; blurAlpha = 0.0f; blurGlowColor = glm::vec4(0.0f); internalRenderingScale = presetInternalRenderingScale; if(t) { text = new (std::nothrow) wchar_t[wcslen(t)+1]; if(!text) return; wcscpy(text, t); textWidth = font->getWidth(text, currentSize); } } /** * Constructor for the GuiText class, uses presets */ GuiText::GuiText(const char * t) { text = NULL; size = presetSize; currentSize = size; color = glm::vec4(presetColor.r, presetColor.g, presetColor.b, presetColor.a); alpha = presetColor.a; alignment = presetAlignment; maxWidth = presetMaxWidth; wrapMode = 0; textWidth = 0; font = presentFont; linestodraw = MAX_LINES_TO_DRAW; textScrollPos = 0; textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; textScrollDelay = TEXT_SCROLL_DELAY; defaultBlur = 4.0f; blurGlowIntensity = 0.0f; blurAlpha = 0.0f; blurGlowColor = glm::vec4(0.0f); internalRenderingScale = presetInternalRenderingScale; if(t) { text = FreeTypeGX::charToWideChar(t); if(!text) return; textWidth = font->getWidth(text, currentSize); } } /** * Destructor for the GuiText class. */ GuiText::~GuiText() { if(text) delete [] text; text = NULL; clearDynamicText(); } void GuiText::setText(const char * t) { if(text) delete [] text; text = NULL; clearDynamicText(); textScrollPos = 0; textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; if(t) { text = FreeTypeGX::charToWideChar(t); if(!text) return; textWidth = font->getWidth(text, currentSize); } } void GuiText::setTextf(const char *format, ...) { if(!format) { setText((char *) NULL); return; } int max_len = strlen(format) + 8192; char *tmp = new char[max_len]; va_list va; va_start(va, format); if((vsnprintf(tmp, max_len, format, va) >= 0) && tmp) { setText(tmp); } va_end(va); if(tmp) delete [] tmp; } void GuiText::setText(const wchar_t * t) { if(text) delete [] text; text = NULL; clearDynamicText(); textScrollPos = 0; textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; if(t) { text = new (std::nothrow) wchar_t[wcslen(t)+1]; if(!text) return; wcscpy(text, t); textWidth = font->getWidth(text, currentSize); } } void GuiText::clearDynamicText() { for(u32 i = 0; i < textDyn.size(); i++) { if(textDyn[i]) delete [] textDyn[i]; } textDyn.clear(); textDynWidth.clear(); } void GuiText::setPresets(int sz, const glm::vec4 & c, int w, int a) { presetSize = sz; presetColor = (GX2ColorF32) { (f32)c.r / 255.0f, (f32)c.g / 255.0f, (f32)c.b / 255.0f, (f32)c.a / 255.0f }; presetMaxWidth = w; presetAlignment = a; } void GuiText::setPresetFont(FreeTypeGX *f) { presentFont = f; } void GuiText::setFontSize(int s) { size = s; } void GuiText::setMaxWidth(int width, int w) { maxWidth = width; wrapMode = w; if(w == SCROLL_HORIZONTAL) { textScrollPos = 0; textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; textScrollDelay = TEXT_SCROLL_DELAY; } clearDynamicText(); } void GuiText::setColor(const glm::vec4 & c) { color = c; alpha = c[3]; } void GuiText::setBlurGlowColor(float blur, const glm::vec4 & c) { blurGlowColor = c; blurGlowIntensity = blur; blurAlpha = c[3]; } int GuiText::getTextWidth(int ind) { if(ind < 0 || ind >= (int) textDyn.size()) return this->getTextWidth(); return font->getWidth(textDyn[ind], currentSize); } const wchar_t * GuiText::getDynText(int ind) { if(ind < 0 || ind >= (int) textDyn.size()) return text; return textDyn[ind]; } /** * Change font */ bool GuiText::setFont(FreeTypeGX *f) { if(!f) return false; font = f; textWidth = font->getWidth(text, currentSize); return true; } std::string GuiText::toUTF8(void) const { if(!text) return std::string(); char *pUtf8 = FreeTypeGX::wideCharToUTF8(text); if(!pUtf8) return std::string(); std::string strOutput(pUtf8); delete [] pUtf8; return strOutput; } void GuiText::makeDottedText() { int pos = textDyn.size(); textDyn.resize(pos + 1); int i = 0, currentWidth = 0; textDyn[pos] = new (std::nothrow) wchar_t[maxWidth]; if(!textDyn[pos]) { textDyn.resize(pos); return; } while (text[i]) { currentWidth += font->getCharWidth(text[i], currentSize, i > 0 ? text[i - 1] : 0); if (currentWidth >= maxWidth && i > 2) { textDyn[pos][i - 2] = '.'; textDyn[pos][i - 1] = '.'; textDyn[pos][i] = '.'; i++; break; } textDyn[pos][i] = text[i]; i++; } textDyn[pos][i] = 0; } void GuiText::scrollText(u32 frameCount) { if (textDyn.size() == 0) { int pos = textDyn.size(); int i = 0, currentWidth = 0; textDyn.resize(pos + 1); textDyn[pos] = new (std::nothrow) wchar_t[maxWidth]; if(!textDyn[pos]) { textDyn.resize(pos); return; } while (text[i] && currentWidth < maxWidth) { textDyn[pos][i] = text[i]; currentWidth += font->getCharWidth(text[i], currentSize, i > 0 ? text[i - 1] : 0); ++i; } textDyn[pos][i] = 0; return; } if (frameCount % textScrollDelay != 0) { return; } if (textScrollInitialDelay) { --textScrollInitialDelay; return; } int stringlen = wcslen(text); ++textScrollPos; if (textScrollPos > stringlen) { textScrollPos = 0; textScrollInitialDelay = TEXT_SCROLL_INITIAL_DELAY; } int ch = textScrollPos; int pos = textDyn.size() - 1; if (!textDyn[pos]) textDyn[pos] = new (std::nothrow) wchar_t[maxWidth]; if(!textDyn[pos]) { textDyn.resize(pos); return; } int i = 0, currentWidth = 0; while (currentWidth < maxWidth) { if (ch > stringlen - 1) { textDyn[pos][i++] = ' '; currentWidth += font->getCharWidth(L' ', currentSize, ch > 0 ? text[ch - 1] : 0); textDyn[pos][i++] = ' '; currentWidth += font->getCharWidth(L' ', currentSize, L' '); textDyn[pos][i++] = ' '; currentWidth += font->getCharWidth(L' ', currentSize, L' '); ch = 0; if(currentWidth >= maxWidth) break; } textDyn[pos][i] = text[ch]; currentWidth += font->getCharWidth(text[ch], currentSize, ch > 0 ? text[ch - 1] : 0); ++ch; ++i; } textDyn[pos][i] = 0; } void GuiText::wrapText() { if (textDyn.size() > 0) return; int i = 0; int ch = 0; int linenum = 0; int lastSpace = -1; int lastSpaceIndex = -1; int currentWidth = 0; while (text[ch] && linenum < linestodraw) { if (linenum >= (int) textDyn.size()) { textDyn.resize(linenum + 1); textDyn[linenum] = new (std::nothrow) wchar_t[maxWidth]; if(!textDyn[linenum]) { textDyn.resize(linenum); break; } } textDyn[linenum][i] = text[ch]; textDyn[linenum][i + 1] = 0; currentWidth += font->getCharWidth(text[ch], currentSize, ch > 0 ? text[ch - 1] : 0x0000); if (currentWidth >= maxWidth || (text[ch] == '\n')) { if(text[ch] == '\n') { lastSpace = -1; lastSpaceIndex = -1; } else if (lastSpace >= 0) { textDyn[linenum][lastSpaceIndex] = 0; // discard space, and everything after ch = lastSpace; // go backwards to the last space lastSpace = -1; // we have used this space lastSpaceIndex = -1; } if (linenum + 1 == linestodraw && text[ch + 1] != 0x0000) { if(i < 2) i = 2; textDyn[linenum][i - 2] = '.'; textDyn[linenum][i - 1] = '.'; textDyn[linenum][i] = '.'; textDyn[linenum][i + 1] = 0; } currentWidth = 0; ++linenum; i = -1; } if (text[ch] == ' ' && i >= 0) { lastSpace = ch; lastSpaceIndex = i; } ++ch; ++i; } } /** * Draw the text on screen */ void GuiText::draw(CVideo *pVideo) { if(!text) return; if(!isVisible()) return; color[3] = getAlpha(); blurGlowColor[3] = blurAlpha * getAlpha(); float finalRenderingScale = 2.0f * internalRenderingScale; int newSize = size * getScale() * finalRenderingScale; int normal_size = size * getScale(); if(newSize != currentSize) { currentSize = normal_size; if(text) textWidth = font->getWidth(text, normal_size); } f32 x_pos = getCenterX() * finalRenderingScale; f32 y_pos = getCenterY() * finalRenderingScale; if(maxWidth > 0 && maxWidth <= textWidth) { if(wrapMode == DOTTED) // text dotted { if(textDyn.size() == 0) makeDottedText(); if(textDynWidth.size() != textDyn.size()) { textDynWidth.resize(textDyn.size()); for(u32 i = 0; i < textDynWidth.size(); i++) textDynWidth[i] = font->getWidth(textDyn[i], newSize); } if(textDyn.size() > 0) font->drawText(pVideo, x_pos, y_pos, getDepth(), textDyn[textDyn.size()-1], newSize, color, alignment, textDynWidth[textDyn.size()-1], defaultBlur, blurGlowIntensity, blurGlowColor,finalRenderingScale); } else if(wrapMode == SCROLL_HORIZONTAL) { scrollText(pVideo->getFrameCount()); if(textDyn.size() > 0) font->drawText(pVideo, x_pos, y_pos, getDepth(), textDyn[textDyn.size()-1], newSize, color, alignment, maxWidth*finalRenderingScale, defaultBlur, blurGlowIntensity, blurGlowColor,finalRenderingScale); } else if(wrapMode == WRAP) { int lineheight = newSize + 6; int yoffset = 0; int voffset = 0; if(textDyn.size() == 0) wrapText(); if(textDynWidth.size() != textDyn.size()) { textDynWidth.resize(textDyn.size()); for(u32 i = 0; i < textDynWidth.size(); i++) textDynWidth[i] = font->getWidth(textDyn[i], newSize); } if(alignment & ALIGN_MIDDLE) voffset = (lineheight * (textDyn.size()-1)) >> 1; for(u32 i = 0; i < textDyn.size(); i++) { font->drawText(pVideo, x_pos, y_pos + voffset + yoffset, getDepth(), textDyn[i], newSize, color, alignment, textDynWidth[i], defaultBlur, blurGlowIntensity, blurGlowColor,finalRenderingScale); yoffset -= lineheight; } } } else { uint16_t newtextWidth = font->getWidth(text, newSize); font->drawText(pVideo, x_pos, y_pos, getDepth(), text, newSize, color, alignment, newtextWidth, defaultBlur, blurGlowIntensity, blurGlowColor,finalRenderingScale); } }