TTF_SizeText() 未在 SDL2、C++ 中返回 'i' 'j' 和“1”(目前已发现)的正确值
TTF_SizeText() not returning correct values for 'i' 'j' and '1' (so far discovered) in SDL2, C++
我有一个文本框 class 可以很好地处理更宽的字符,例如 a、b、c... 但是对于 'f' 和 'l' 这样的字符,它似乎不正确这些字符的大小,但正确地得到其他人的大小?这是文本框 class 文本的 'highlighting' 的代码,它有点长,我稍后会修复它,但应该有足够的文档以便于理解。
void Textbox::Highlight_Text(SDL_Renderer *renderer)
{
if (clickedOn == true){
int currentCharacterWidth = 0;
int currentCharacterHeight = 0;
int totalSize = 0;
SDL_Rect currentCharacterRect;
string currentCharacter, tempText;
if (highlightedCharacters.size() >= 1){ ///To make sure only 1 thing is highlighted, in conjunction with next part
highlighted = true;
}
if (highlighted == true){ /// if a part is highlighted, and is left highlighted, next time clicked, remove the highlighting and redo it
if (EVENTS.mouseClicked == false){
resetHighlightingNextClick = true;
}
}
if (resetHighlightingNextClick == true){
if (highlighted == true){
if (EVENTS.mouseClicked == true){ ///actually remove the highlighting
highlightedCharacters.clear();
indexOfCharactersHighlighted.clear();
highlighted = false;
resetHighlightingNextClick = false;
}
}
}
for (int i=0; i < textboxText.Get_Text().size(); i++){
currentCharacter = textboxText.Get_Text()[i];
TTF_SizeText(textboxText.fonts[textboxText.fontIndex], currentCharacter.c_str(), ¤tCharacterWidth, ¤tCharacterHeight);
///the totalSize added to rectangle is not making it wider, its adjusting its x value offset
currentCharacterRect = {textboxText.x + totalSize, textboxText.y + int(textboxText.textSize*0.1), currentCharacterWidth, currentCharacterHeight};
totalSize += currentCharacterWidth; ///"current" size of text in loop to get x value of specific character clicked on
///If mouse is touching any of the characters in the text
if ( SDL_PointInRect(&EVENTS.mousePos, ¤tCharacterRect) ){
EVENTS.Change_Cursor(SDL_SYSTEM_CURSOR_IBEAM);
if (EVENTS.mouseClicked == true){ ///Clicking on the text to highlight
if (In_Array(highlightedCharacters, currentCharacterRect.x) == false ){
highlightedCharacters.push_back(currentCharacterRect); ///If there is no duplicates
indexOfCharactersHighlighted.push_back(i); ///Get index of text being highlighted, its always in order too
}
if ( currentCharacterRect.x != highlightedCharacters[highlightedCharacters.size()-1].x){ ///So they don't stack up highlights, ie, you can remove them
/// If the mouse is not highlighting the last one, say second last on the right for example, delete the one in front of it (last one)
///Like when highlighting text with mouse, it adapts to how you move it, so it unhighlights text not being highlighted
highlightedCharacters.pop_back();
indexOfCharactersHighlighted.pop_back();
}
}
}
}///End for loop
if (highlighted == true ){
if (EVENTS.backspacePressed == true || EVENTS.currentKey != ""){
tempText = textboxText.Get_Text();
///remove highlighted characters
if (indexOfCharactersHighlighted.size() != 0){
///the range of values highlighted will always be in a sorted order
tempText.erase( Min(indexOfCharactersHighlighted) , Max(indexOfCharactersHighlighted)-Min(indexOfCharactersHighlighted)+1 ); ///erase the range of values highlighted
textboxText.Change_Text(renderer, tempText);
///once removed text, clear every highlighted related thing
highlightedCharacters.clear();
indexOfCharactersHighlighted.clear();
highlighted = false;
resetHighlightingNextClick = false;
EVENTS.backspacePressed = false;
EVENTS.currentKey = "";
}
}
}
} ///End if for clicked on
///fit with scrolling offsets
if (EVENTS.scrolled == true){
for (int p=0; p < highlightedCharacters.size(); p++){
highlightedCharacters[p].y += EVENTS.scrollVal;
}
}
///Drawing the highlighted text
if (highlighted == true && clickedOn == true){
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, 55,60,65, 75);
for (int j=0; j < highlightedCharacters.size(); j++){
SDL_RenderFillRect(renderer, &highlightedCharacters[j]);
}
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
}
///when clicked off textbox, clear everything/highlighting
if (clickedOn == false){
highlightedCharacters.clear();
indexOfCharactersHighlighted.clear();
highlighted = false;
}
}
在传入的字体中参考,这里是我在文本中的获取方式class
fontIndex = textSize-lowestFontSize -1;
///One time setups
if (numOfInstances == 1){
try{
TTF_Init();
//cout << "Initialised ttf" << endl;
}
catch (exception &err){
cout << "Could not initialise ttf for text \"" << text << "\". Error from SDL is: " << TTF_GetError() << ". Error from C++ is: " << err.what() << endl;
}
for (int i=lowestFontSize; i <= highestFontSize; i++){
TTF_Font *currentFont = TTF_OpenFont(fontType.c_str(), i);
if (!currentFont){
cout << "Error with font in text \"" << txt << "\" Error is: " << SDL_GetError() << endl;
}
fonts.push_back(currentFont);
}
}
所以如果我传入,假设 25 作为我的文本大小,我的 lowestFontSize = 10 和 highestFontSize = 100,所以我需要的大小 25 的索引将是 (25-10 -1 = 14),因为索引从 0 开始,这是我在文本 class 中创建静态字体矢量之前的第一行。这是我要解释的片段:
这显然工作正常。
但是现在,完全不准确了。如果我 select 文本末尾的一个随机字符,它没有正确突出显示,只有从一开始第一个看起来非常完美,但似乎不准确是复合的,因此使总灰色突出显示比预期的要宽得多。
你的方法的问题是单个字符的宽度没有意义。渲染器根据上下文(它们在渲染字符串中的邻居)调整它们。所以i
在渲染字符串bit
中的宽度不一定和i
在渲染字符串fil
.
中的宽度相同
查找文本选择坐标的方法需要考虑上下文。
假设我们有三个字符串的宽度:
prefixWidth
是前缀的大小(原始文本行到但不包括选择)
selWidth
是选区本身的宽度
totalWidth
是前缀宽度和选择串接
(我们不关心选择后的部分)。前两个宽度将接近第三个,但不会加起来,因为前缀的最后一个字符和所选内容的第一个字符之间的字距调整。不同之处在于您需要对渲染选区的 X 坐标应用校正。所以我们需要在不是prefixWidth
而是
的X坐标处开始渲染选区
prefixWidth + (totalWidth - (prefixWidth + selWidth))
与
相同
totalWidth - selWidth
所以你根本不需要计算 prefixWidth。
下面是一个完整的(半)工作示例程序。参数是 (1) 字体文件完整路径 (2) 字体大小 (3) 要呈现的字符串 (4) 选择开始 (5) 选择长度。选区是在原文之上渲染的,向下和向右移动1个像素,所以很容易看出是否有任何偏差。
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
char* substr(const char* src, int fromChar, int subLen)
{
if (subLen < 0 || fromChar < 0 || fromChar + subLen > (int)strlen(src))
{
fprintf (stderr, "invalid substring\n");
exit (EXIT_FAILURE);
}
char* z = (char*)malloc(subLen);
strncpy(z, src + fromChar, subLen);
z[subLen] = '[=12=]';
return z;
}
void textExtent(TTF_Font* font, const char* text,
int fromChar, int subLen, int* w, int* h)
{
int l = strlen(text);
if (subLen == -1) subLen = l;
if (fromChar < 0 || subLen > l)
{
fprintf (stderr, "Bad text extent\n");
exit (EXIT_FAILURE);
}
char* z = substr(text, fromChar, subLen);
TTF_SizeUTF8(font, z, w, h);
free(z);
}
int textWidth(TTF_Font* font, const char* text,
int fromChar, int subLen)
{
int w, h;
textExtent(font, text, fromChar, subLen, &w, &h);
return w;
};
int main(int argc, char ** argv)
{
bool quit = false;
SDL_Event event;
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
if (argc != 6)
{
fprintf (stderr, "usage: %s font text from length\n", argv[0]);
exit(EXIT_FAILURE);
}
const char* fontpath = argv[1];
int fontSz = atoi(argv[2]);
const char* txt = argv[3];
int from = atoi(argv[4]);
int len = atoi(argv[5]);
int tsize = strlen(txt);
if (from < 0 || from + len >= tsize)
{
fprintf (stderr, "Invalid text portion to highlight\n");
exit (EXIT_FAILURE);
}
if (fontSz < 2 || fontSz > 300)
{
fprintf (stderr, "Invalid font size\n");
exit (EXIT_FAILURE);
}
// open font to render with
TTF_Font * font = TTF_OpenFont(fontpath, fontSz);
if (!font)
{
fprintf (stderr, "Could not open font %s\n", fontpath);
exit (EXIT_FAILURE);
}
// Query text size
int textW, textH;
textExtent(font, txt, 0, -1, &textW, &textH);
SDL_Window * window = SDL_CreateWindow("SDL_ttf in SDL2",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, textW, textH, 0);
// Query selection coords
//
int selWidth = textWidth(font, txt, from, len);
int totalWidth = textWidth(font, txt, 0, from+len);
// Render portions of text
SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);
SDL_Color color = { 255, 128, 0, 0 };
SDL_Surface * surface = TTF_RenderUTF8_Blended(font, txt, color);
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_Rect dstrect = { 0, 0, textW, textH };
SDL_Color color2 = { 0, 128, 255, 128 };
char* s = substr(txt, from, len);
SDL_Surface * surface2 = TTF_RenderUTF8_Blended(font, s, color2);
free(s);
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
SDL_Texture * texture2 = SDL_CreateTextureFromSurface(renderer, surface2);
SDL_SetTextureAlphaMod(texture2, 128);
SDL_SetTextureBlendMode(texture2, SDL_BLENDMODE_BLEND);
SDL_Rect dstrect2 = {totalWidth - selWidth + 1, 1, selWidth, textH };
while (!quit)
{
SDL_WaitEvent(&event);
switch (event.type)
{
case SDL_QUIT:
quit = true;
break;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
SDL_RenderCopy(renderer, texture2, NULL, &dstrect2);
//Update screen
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_DestroyTexture(texture);
SDL_FreeSurface(surface);
TTF_CloseFont(font);
TTF_Quit();
SDL_Quit();
return 0;
}
我有一个文本框 class 可以很好地处理更宽的字符,例如 a、b、c... 但是对于 'f' 和 'l' 这样的字符,它似乎不正确这些字符的大小,但正确地得到其他人的大小?这是文本框 class 文本的 'highlighting' 的代码,它有点长,我稍后会修复它,但应该有足够的文档以便于理解。
void Textbox::Highlight_Text(SDL_Renderer *renderer)
{
if (clickedOn == true){
int currentCharacterWidth = 0;
int currentCharacterHeight = 0;
int totalSize = 0;
SDL_Rect currentCharacterRect;
string currentCharacter, tempText;
if (highlightedCharacters.size() >= 1){ ///To make sure only 1 thing is highlighted, in conjunction with next part
highlighted = true;
}
if (highlighted == true){ /// if a part is highlighted, and is left highlighted, next time clicked, remove the highlighting and redo it
if (EVENTS.mouseClicked == false){
resetHighlightingNextClick = true;
}
}
if (resetHighlightingNextClick == true){
if (highlighted == true){
if (EVENTS.mouseClicked == true){ ///actually remove the highlighting
highlightedCharacters.clear();
indexOfCharactersHighlighted.clear();
highlighted = false;
resetHighlightingNextClick = false;
}
}
}
for (int i=0; i < textboxText.Get_Text().size(); i++){
currentCharacter = textboxText.Get_Text()[i];
TTF_SizeText(textboxText.fonts[textboxText.fontIndex], currentCharacter.c_str(), ¤tCharacterWidth, ¤tCharacterHeight);
///the totalSize added to rectangle is not making it wider, its adjusting its x value offset
currentCharacterRect = {textboxText.x + totalSize, textboxText.y + int(textboxText.textSize*0.1), currentCharacterWidth, currentCharacterHeight};
totalSize += currentCharacterWidth; ///"current" size of text in loop to get x value of specific character clicked on
///If mouse is touching any of the characters in the text
if ( SDL_PointInRect(&EVENTS.mousePos, ¤tCharacterRect) ){
EVENTS.Change_Cursor(SDL_SYSTEM_CURSOR_IBEAM);
if (EVENTS.mouseClicked == true){ ///Clicking on the text to highlight
if (In_Array(highlightedCharacters, currentCharacterRect.x) == false ){
highlightedCharacters.push_back(currentCharacterRect); ///If there is no duplicates
indexOfCharactersHighlighted.push_back(i); ///Get index of text being highlighted, its always in order too
}
if ( currentCharacterRect.x != highlightedCharacters[highlightedCharacters.size()-1].x){ ///So they don't stack up highlights, ie, you can remove them
/// If the mouse is not highlighting the last one, say second last on the right for example, delete the one in front of it (last one)
///Like when highlighting text with mouse, it adapts to how you move it, so it unhighlights text not being highlighted
highlightedCharacters.pop_back();
indexOfCharactersHighlighted.pop_back();
}
}
}
}///End for loop
if (highlighted == true ){
if (EVENTS.backspacePressed == true || EVENTS.currentKey != ""){
tempText = textboxText.Get_Text();
///remove highlighted characters
if (indexOfCharactersHighlighted.size() != 0){
///the range of values highlighted will always be in a sorted order
tempText.erase( Min(indexOfCharactersHighlighted) , Max(indexOfCharactersHighlighted)-Min(indexOfCharactersHighlighted)+1 ); ///erase the range of values highlighted
textboxText.Change_Text(renderer, tempText);
///once removed text, clear every highlighted related thing
highlightedCharacters.clear();
indexOfCharactersHighlighted.clear();
highlighted = false;
resetHighlightingNextClick = false;
EVENTS.backspacePressed = false;
EVENTS.currentKey = "";
}
}
}
} ///End if for clicked on
///fit with scrolling offsets
if (EVENTS.scrolled == true){
for (int p=0; p < highlightedCharacters.size(); p++){
highlightedCharacters[p].y += EVENTS.scrollVal;
}
}
///Drawing the highlighted text
if (highlighted == true && clickedOn == true){
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_BLEND);
SDL_SetRenderDrawColor(renderer, 55,60,65, 75);
for (int j=0; j < highlightedCharacters.size(); j++){
SDL_RenderFillRect(renderer, &highlightedCharacters[j]);
}
SDL_SetRenderDrawBlendMode(renderer, SDL_BLENDMODE_NONE);
}
///when clicked off textbox, clear everything/highlighting
if (clickedOn == false){
highlightedCharacters.clear();
indexOfCharactersHighlighted.clear();
highlighted = false;
}
}
在传入的字体中参考,这里是我在文本中的获取方式class
fontIndex = textSize-lowestFontSize -1;
///One time setups
if (numOfInstances == 1){
try{
TTF_Init();
//cout << "Initialised ttf" << endl;
}
catch (exception &err){
cout << "Could not initialise ttf for text \"" << text << "\". Error from SDL is: " << TTF_GetError() << ". Error from C++ is: " << err.what() << endl;
}
for (int i=lowestFontSize; i <= highestFontSize; i++){
TTF_Font *currentFont = TTF_OpenFont(fontType.c_str(), i);
if (!currentFont){
cout << "Error with font in text \"" << txt << "\" Error is: " << SDL_GetError() << endl;
}
fonts.push_back(currentFont);
}
}
所以如果我传入,假设 25 作为我的文本大小,我的 lowestFontSize = 10 和 highestFontSize = 100,所以我需要的大小 25 的索引将是 (25-10 -1 = 14),因为索引从 0 开始,这是我在文本 class 中创建静态字体矢量之前的第一行。这是我要解释的片段:
这显然工作正常。
但是现在,完全不准确了。如果我 select 文本末尾的一个随机字符,它没有正确突出显示,只有从一开始第一个看起来非常完美,但似乎不准确是复合的,因此使总灰色突出显示比预期的要宽得多。
你的方法的问题是单个字符的宽度没有意义。渲染器根据上下文(它们在渲染字符串中的邻居)调整它们。所以i
在渲染字符串bit
中的宽度不一定和i
在渲染字符串fil
.
查找文本选择坐标的方法需要考虑上下文。
假设我们有三个字符串的宽度:
prefixWidth
是前缀的大小(原始文本行到但不包括选择)selWidth
是选区本身的宽度totalWidth
是前缀宽度和选择串接
(我们不关心选择后的部分)。前两个宽度将接近第三个,但不会加起来,因为前缀的最后一个字符和所选内容的第一个字符之间的字距调整。不同之处在于您需要对渲染选区的 X 坐标应用校正。所以我们需要在不是prefixWidth
而是
prefixWidth + (totalWidth - (prefixWidth + selWidth))
与
相同totalWidth - selWidth
所以你根本不需要计算 prefixWidth。
下面是一个完整的(半)工作示例程序。参数是 (1) 字体文件完整路径 (2) 字体大小 (3) 要呈现的字符串 (4) 选择开始 (5) 选择长度。选区是在原文之上渲染的,向下和向右移动1个像素,所以很容易看出是否有任何偏差。
#include <SDL2/SDL.h>
#include <SDL2/SDL_ttf.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
char* substr(const char* src, int fromChar, int subLen)
{
if (subLen < 0 || fromChar < 0 || fromChar + subLen > (int)strlen(src))
{
fprintf (stderr, "invalid substring\n");
exit (EXIT_FAILURE);
}
char* z = (char*)malloc(subLen);
strncpy(z, src + fromChar, subLen);
z[subLen] = '[=12=]';
return z;
}
void textExtent(TTF_Font* font, const char* text,
int fromChar, int subLen, int* w, int* h)
{
int l = strlen(text);
if (subLen == -1) subLen = l;
if (fromChar < 0 || subLen > l)
{
fprintf (stderr, "Bad text extent\n");
exit (EXIT_FAILURE);
}
char* z = substr(text, fromChar, subLen);
TTF_SizeUTF8(font, z, w, h);
free(z);
}
int textWidth(TTF_Font* font, const char* text,
int fromChar, int subLen)
{
int w, h;
textExtent(font, text, fromChar, subLen, &w, &h);
return w;
};
int main(int argc, char ** argv)
{
bool quit = false;
SDL_Event event;
SDL_Init(SDL_INIT_VIDEO);
TTF_Init();
if (argc != 6)
{
fprintf (stderr, "usage: %s font text from length\n", argv[0]);
exit(EXIT_FAILURE);
}
const char* fontpath = argv[1];
int fontSz = atoi(argv[2]);
const char* txt = argv[3];
int from = atoi(argv[4]);
int len = atoi(argv[5]);
int tsize = strlen(txt);
if (from < 0 || from + len >= tsize)
{
fprintf (stderr, "Invalid text portion to highlight\n");
exit (EXIT_FAILURE);
}
if (fontSz < 2 || fontSz > 300)
{
fprintf (stderr, "Invalid font size\n");
exit (EXIT_FAILURE);
}
// open font to render with
TTF_Font * font = TTF_OpenFont(fontpath, fontSz);
if (!font)
{
fprintf (stderr, "Could not open font %s\n", fontpath);
exit (EXIT_FAILURE);
}
// Query text size
int textW, textH;
textExtent(font, txt, 0, -1, &textW, &textH);
SDL_Window * window = SDL_CreateWindow("SDL_ttf in SDL2",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, textW, textH, 0);
// Query selection coords
//
int selWidth = textWidth(font, txt, from, len);
int totalWidth = textWidth(font, txt, 0, from+len);
// Render portions of text
SDL_Renderer * renderer = SDL_CreateRenderer(window, -1, 0);
SDL_Color color = { 255, 128, 0, 0 };
SDL_Surface * surface = TTF_RenderUTF8_Blended(font, txt, color);
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
SDL_Texture * texture = SDL_CreateTextureFromSurface(renderer, surface);
SDL_SetTextureBlendMode(texture, SDL_BLENDMODE_BLEND);
SDL_Rect dstrect = { 0, 0, textW, textH };
SDL_Color color2 = { 0, 128, 255, 128 };
char* s = substr(txt, from, len);
SDL_Surface * surface2 = TTF_RenderUTF8_Blended(font, s, color2);
free(s);
SDL_SetSurfaceBlendMode(surface, SDL_BLENDMODE_BLEND);
SDL_Texture * texture2 = SDL_CreateTextureFromSurface(renderer, surface2);
SDL_SetTextureAlphaMod(texture2, 128);
SDL_SetTextureBlendMode(texture2, SDL_BLENDMODE_BLEND);
SDL_Rect dstrect2 = {totalWidth - selWidth + 1, 1, selWidth, textH };
while (!quit)
{
SDL_WaitEvent(&event);
switch (event.type)
{
case SDL_QUIT:
quit = true;
break;
}
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, &dstrect);
SDL_RenderCopy(renderer, texture2, NULL, &dstrect2);
//Update screen
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_DestroyTexture(texture);
SDL_FreeSurface(surface);
TTF_CloseFont(font);
TTF_Quit();
SDL_Quit();
return 0;
}