2010-11-20 10:37:14 +00:00
|
|
|
/* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
|
|
|
|
/* If you are missing that file, acquire a complete release at teeworlds.com. */
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <base/math.h>
|
2020-09-26 19:41:58 +00:00
|
|
|
#include <base/system.h>
|
2021-09-12 17:40:23 +00:00
|
|
|
#include <cstddef>
|
|
|
|
#include <cstdint>
|
2010-05-29 07:25:38 +00:00
|
|
|
#include <engine/graphics.h>
|
2018-03-13 20:49:07 +00:00
|
|
|
#include <engine/shared/config.h>
|
|
|
|
#include <engine/storage.h>
|
2020-09-26 19:41:58 +00:00
|
|
|
#include <engine/textrender.h>
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
// ft2 texture
|
|
|
|
#include <ft2build.h>
|
|
|
|
#include FT_FREETYPE_H
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
#include <limits>
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// TODO: Refactor: clean this up
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
MAX_CHARACTERS = 64,
|
|
|
|
};
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
#include <map>
|
2020-09-26 19:41:58 +00:00
|
|
|
#include <vector>
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
struct SFontSizeChar
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-02-12 10:40:36 +00:00
|
|
|
int m_ID;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// these values are scaled to the pFont size
|
|
|
|
// width * font_size == real_size
|
|
|
|
float m_Width;
|
|
|
|
float m_Height;
|
2021-09-12 17:40:23 +00:00
|
|
|
float m_CharWidth;
|
|
|
|
float m_CharHeight;
|
2010-05-29 07:25:38 +00:00
|
|
|
float m_OffsetX;
|
|
|
|
float m_OffsetY;
|
|
|
|
float m_AdvanceX;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
float m_aUVs[4];
|
2021-06-23 05:05:49 +00:00
|
|
|
int64_t m_TouchTime;
|
2019-01-06 05:42:57 +00:00
|
|
|
FT_UInt m_GlyphIndex;
|
2010-05-29 07:25:38 +00:00
|
|
|
};
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
struct STextCharQuadVertexColor
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
unsigned char m_R, m_G, m_B, m_A;
|
|
|
|
};
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
struct STextCharQuadVertex
|
|
|
|
{
|
|
|
|
STextCharQuadVertex()
|
|
|
|
{
|
|
|
|
m_Color.m_R = m_Color.m_G = m_Color.m_B = m_Color.m_A = 255;
|
|
|
|
}
|
|
|
|
float m_X, m_Y;
|
|
|
|
// do not use normalized floats as coordinates, since the texture might grow
|
|
|
|
float m_U, m_V;
|
|
|
|
STextCharQuadVertexColor m_Color;
|
|
|
|
};
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
struct STextCharQuad
|
|
|
|
{
|
|
|
|
STextCharQuadVertex m_Vertices[4];
|
|
|
|
};
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
struct STextureSkyline
|
|
|
|
{
|
|
|
|
// the height of each column
|
|
|
|
std::vector<int> m_CurHeightOfPixelColumn;
|
|
|
|
};
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
struct CFontSizeData
|
|
|
|
{
|
|
|
|
int m_FontSize;
|
|
|
|
FT_Face *m_pFace;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
std::map<int, SFontSizeChar> m_Chars;
|
2010-05-29 07:25:38 +00:00
|
|
|
};
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
#define MIN_FONT_SIZE 6
|
|
|
|
#define MAX_FONT_SIZE 128
|
|
|
|
#define NUM_FONT_SIZES MAX_FONT_SIZE - MIN_FONT_SIZE + 1
|
|
|
|
|
2010-07-05 18:57:07 +00:00
|
|
|
class CFont
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2010-07-05 18:57:07 +00:00
|
|
|
public:
|
2020-09-30 21:38:05 +00:00
|
|
|
~CFont()
|
|
|
|
{
|
|
|
|
free(m_pBuf);
|
|
|
|
delete[] m_TextureData[0];
|
|
|
|
delete[] m_TextureData[1];
|
2020-10-26 14:14:07 +00:00
|
|
|
for(auto &FtFallbackFont : m_FtFallbackFonts)
|
2020-09-30 21:38:05 +00:00
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
free(FtFallbackFont.m_pBuf);
|
2020-09-30 21:38:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
void InitFontSizes()
|
|
|
|
{
|
|
|
|
for(int i = 0; i < NUM_FONT_SIZES; ++i)
|
|
|
|
{
|
|
|
|
m_aFontSizes[i].m_FontSize = i + MIN_FONT_SIZE;
|
|
|
|
m_aFontSizes[i].m_pFace = &this->m_FtFace;
|
|
|
|
m_aFontSizes[i].m_Chars.clear();
|
|
|
|
}
|
|
|
|
}
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
CFontSizeData *GetFontSize(int Pixelsize)
|
|
|
|
{
|
|
|
|
int FontSize = (Pixelsize >= MIN_FONT_SIZE ? (Pixelsize > MAX_FONT_SIZE ? MAX_FONT_SIZE : Pixelsize) : MIN_FONT_SIZE);
|
|
|
|
|
|
|
|
return &m_aFontSizes[FontSize - MIN_FONT_SIZE];
|
|
|
|
}
|
|
|
|
|
2020-09-30 21:38:05 +00:00
|
|
|
void *m_pBuf;
|
2021-09-13 08:06:34 +00:00
|
|
|
char m_aFilename[IO_MAX_PATH_LENGTH];
|
2010-05-29 07:25:38 +00:00
|
|
|
FT_Face m_FtFace;
|
2020-08-20 09:22:25 +00:00
|
|
|
|
|
|
|
struct SFontFallBack
|
|
|
|
{
|
2020-09-30 21:38:05 +00:00
|
|
|
void *m_pBuf;
|
2021-09-13 08:06:34 +00:00
|
|
|
char m_aFilename[IO_MAX_PATH_LENGTH];
|
2020-08-20 09:22:25 +00:00
|
|
|
FT_Face m_FtFace;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::vector<SFontFallBack> m_FtFallbackFonts;
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
CFontSizeData m_aFontSizes[NUM_FONT_SIZES];
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2012-08-12 10:41:50 +00:00
|
|
|
IGraphics::CTextureHandle m_aTextures[2];
|
2018-03-13 20:49:07 +00:00
|
|
|
// keep the full texture, because opengl doesn't provide texture copying
|
2018-04-09 09:56:39 +00:00
|
|
|
unsigned char *m_TextureData[2];
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
// width and height are the same
|
|
|
|
int m_CurTextureDimensions[2];
|
|
|
|
|
|
|
|
STextureSkyline m_TextureSkyline[2];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct STextString
|
|
|
|
{
|
|
|
|
int m_QuadBufferObjectIndex;
|
|
|
|
int m_QuadBufferContainerIndex;
|
|
|
|
size_t m_QuadNum;
|
|
|
|
int m_SelectionQuadContainerIndex;
|
|
|
|
|
|
|
|
std::vector<STextCharQuad> m_CharacterQuads;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct STextContainer
|
|
|
|
{
|
|
|
|
STextContainer() { Reset(); }
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
CFont *m_pFont;
|
2018-03-13 20:49:07 +00:00
|
|
|
int m_FontSize;
|
|
|
|
STextString m_StringInfo;
|
|
|
|
|
|
|
|
// keep these values to calculate offsets
|
2018-03-21 14:43:56 +00:00
|
|
|
float m_AlignedStartX;
|
|
|
|
float m_AlignedStartY;
|
2018-03-15 02:33:22 +00:00
|
|
|
float m_X;
|
|
|
|
float m_Y;
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
int m_Flags;
|
|
|
|
int m_LineCount;
|
2020-09-03 22:53:26 +00:00
|
|
|
int m_GlyphCount;
|
2018-03-13 20:49:07 +00:00
|
|
|
int m_CharCount;
|
|
|
|
int m_MaxLines;
|
|
|
|
|
|
|
|
float m_StartX;
|
|
|
|
float m_StartY;
|
|
|
|
float m_LineWidth;
|
|
|
|
float m_UnscaledFontSize;
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
int m_RenderFlags;
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
bool m_HasCursor;
|
|
|
|
bool m_HasSelection;
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
void Reset()
|
|
|
|
{
|
2018-03-21 14:43:56 +00:00
|
|
|
m_pFont = NULL;
|
|
|
|
m_FontSize = 0;
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
m_StringInfo.m_QuadBufferObjectIndex = m_StringInfo.m_QuadBufferContainerIndex = m_StringInfo.m_SelectionQuadContainerIndex = -1;
|
|
|
|
m_StringInfo.m_QuadNum = 0;
|
|
|
|
m_StringInfo.m_CharacterQuads.clear();
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
m_AlignedStartX = m_AlignedStartY = m_X = m_Y = 0.f;
|
2020-09-03 22:53:26 +00:00
|
|
|
m_Flags = m_LineCount = m_CharCount = m_GlyphCount = 0;
|
2018-03-13 20:49:07 +00:00
|
|
|
m_MaxLines = -1;
|
|
|
|
m_StartX = m_StartY = 0.f;
|
|
|
|
m_LineWidth = -1.f;
|
|
|
|
m_UnscaledFontSize = 0.f;
|
2018-03-21 14:43:56 +00:00
|
|
|
|
|
|
|
m_RenderFlags = 0;
|
2021-09-16 14:50:17 +00:00
|
|
|
|
|
|
|
m_HasCursor = false;
|
|
|
|
m_HasSelection = false;
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
class CTextRender : public IEngineTextRender
|
|
|
|
{
|
|
|
|
IGraphics *m_pGraphics;
|
|
|
|
IGraphics *Graphics() { return m_pGraphics; }
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
unsigned int m_RenderFlags;
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
std::vector<STextContainer *> m_TextContainers;
|
2018-03-13 20:49:07 +00:00
|
|
|
std::vector<int> m_TextContainerIndices;
|
|
|
|
int m_FirstFreeTextContainerIndex;
|
|
|
|
|
|
|
|
SBufferContainerInfo m_DefaultTextContainerInfo;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
std::vector<CFont *> m_Fonts;
|
2018-03-13 20:49:07 +00:00
|
|
|
CFont *m_pCurFont;
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
int64_t m_CursorRenderTime;
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
int GetFreeTextContainerIndex()
|
|
|
|
{
|
|
|
|
if(m_FirstFreeTextContainerIndex == -1)
|
|
|
|
{
|
|
|
|
int Index = (int)m_TextContainerIndices.size();
|
|
|
|
m_TextContainerIndices.push_back(Index);
|
|
|
|
return Index;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int Index = m_FirstFreeTextContainerIndex;
|
|
|
|
m_FirstFreeTextContainerIndex = m_TextContainerIndices[Index];
|
|
|
|
m_TextContainerIndices[Index] = Index;
|
|
|
|
return Index;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void FreeTextContainerIndex(int Index)
|
|
|
|
{
|
|
|
|
m_TextContainerIndices[Index] = m_FirstFreeTextContainerIndex;
|
|
|
|
m_FirstFreeTextContainerIndex = Index;
|
|
|
|
}
|
|
|
|
|
2020-10-13 20:08:52 +00:00
|
|
|
void FreeTextContainer(int Index)
|
|
|
|
{
|
2021-09-12 17:40:23 +00:00
|
|
|
m_TextContainers[Index]->Reset();
|
2020-10-13 20:08:52 +00:00
|
|
|
FreeTextContainerIndex(Index);
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
STextContainer &GetTextContainer(int Index)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2019-04-26 19:36:49 +00:00
|
|
|
if(Index >= (int)m_TextContainers.size())
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
|
|
|
int Size = (int)m_TextContainers.size();
|
|
|
|
for(int i = 0; i < (Index + 1) - Size; ++i)
|
2021-09-12 17:40:23 +00:00
|
|
|
m_TextContainers.push_back(new STextContainer());
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
return *m_TextContainers[Index];
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
int WordLength(const char *pText)
|
|
|
|
{
|
2021-12-20 01:17:10 +00:00
|
|
|
const char *pCursor = pText;
|
2022-02-14 23:12:52 +00:00
|
|
|
while(true)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-09-03 22:53:26 +00:00
|
|
|
if(*pCursor == 0)
|
2021-12-20 01:34:02 +00:00
|
|
|
return pCursor - pText;
|
2020-09-03 22:53:26 +00:00
|
|
|
if(*pCursor == '\n' || *pCursor == '\t' || *pCursor == ' ')
|
2021-12-20 01:34:02 +00:00
|
|
|
return pCursor - pText + 1;
|
2021-12-20 01:17:10 +00:00
|
|
|
str_utf8_decode(&pCursor);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-26 22:34:20 +00:00
|
|
|
ColorRGBA m_Color;
|
|
|
|
ColorRGBA m_OutlineColor;
|
2021-09-12 17:40:23 +00:00
|
|
|
ColorRGBA m_SelectionColor;
|
2010-07-05 18:57:07 +00:00
|
|
|
CFont *m_pDefaultFont;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
|
|
|
FT_Library m_FTLibrary;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
virtual void SetRenderFlags(unsigned int Flags)
|
|
|
|
{
|
|
|
|
m_RenderFlags = Flags;
|
|
|
|
}
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2020-10-22 20:21:19 +00:00
|
|
|
virtual unsigned int GetRenderFlags()
|
|
|
|
{
|
|
|
|
return m_RenderFlags;
|
|
|
|
}
|
|
|
|
|
2020-09-14 14:42:33 +00:00
|
|
|
void Grow(unsigned char *pIn, unsigned char *pOut, int w, int h, int OutlineCount)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2011-04-13 18:37:12 +00:00
|
|
|
for(int y = 0; y < h; y++)
|
|
|
|
for(int x = 0; x < w; x++)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int c = pIn[y * w + x];
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2020-09-14 14:42:33 +00:00
|
|
|
for(int sy = -OutlineCount; sy <= OutlineCount; sy++)
|
|
|
|
for(int sx = -OutlineCount; sx <= OutlineCount; sx++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int GetX = x + sx;
|
|
|
|
int GetY = y + sy;
|
2018-03-13 20:49:07 +00:00
|
|
|
if(GetX >= 0 && GetY >= 0 && GetX < w && GetY < h)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
int Index = GetY * w + GetX;
|
2010-05-29 07:25:38 +00:00
|
|
|
if(pIn[Index] > c)
|
2011-04-13 18:37:12 +00:00
|
|
|
c = pIn[Index];
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
pOut[y * w + x] = c;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-12 10:41:50 +00:00
|
|
|
IGraphics::CTextureHandle InitTexture(int Width, int Height, void *pUploadData = NULL)
|
2019-04-26 19:36:49 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
void *pMem = NULL;
|
|
|
|
if(pUploadData)
|
2018-04-09 09:56:39 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
pMem = pUploadData;
|
2018-04-09 09:56:39 +00:00
|
|
|
}
|
2018-03-13 20:49:07 +00:00
|
|
|
else
|
|
|
|
{
|
2020-10-05 17:03:14 +00:00
|
|
|
pMem = calloc((size_t)Width * Height, 1);
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
2012-08-12 10:41:50 +00:00
|
|
|
IGraphics::CTextureHandle Texture = Graphics()->LoadTextureRaw(Width, Height, CImageInfo::FORMAT_ALPHA, pMem, CImageInfo::FORMAT_ALPHA, IGraphics::TEXLOAD_NOMIPMAPS | IGraphics::TEXLOAD_NO_COMPRESSION);
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
if(!pUploadData)
|
2018-04-09 09:56:39 +00:00
|
|
|
free(pMem);
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2012-08-12 10:41:50 +00:00
|
|
|
return Texture;
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
2012-08-12 10:41:50 +00:00
|
|
|
void UnloadTexture(IGraphics::CTextureHandle Index)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2016-07-02 08:33:37 +00:00
|
|
|
Graphics()->UnloadTexture(&Index);
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void IncreaseFontTexture(CFont *pFont, int TextureIndex)
|
|
|
|
{
|
|
|
|
int NewDimensions = pFont->m_CurTextureDimensions[TextureIndex] * 2;
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
unsigned char *pTmpTexBuffer = new unsigned char[NewDimensions * NewDimensions];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(pTmpTexBuffer, (size_t)NewDimensions * NewDimensions * sizeof(unsigned char));
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
for(int y = 0; y < pFont->m_CurTextureDimensions[TextureIndex]; ++y)
|
2011-12-31 08:40:11 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
for(int x = 0; x < pFont->m_CurTextureDimensions[TextureIndex]; ++x)
|
2011-12-31 08:40:11 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
pTmpTexBuffer[x + y * NewDimensions] = pFont->m_TextureData[TextureIndex][x + y * pFont->m_CurTextureDimensions[TextureIndex]];
|
2011-12-31 08:40:11 +00:00
|
|
|
}
|
|
|
|
}
|
2018-03-13 20:49:07 +00:00
|
|
|
UnloadTexture(pFont->m_aTextures[TextureIndex]);
|
|
|
|
pFont->m_aTextures[TextureIndex] = InitTexture(NewDimensions, NewDimensions, pTmpTexBuffer);
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
delete[] pFont->m_TextureData[TextureIndex];
|
|
|
|
pFont->m_TextureData[TextureIndex] = pTmpTexBuffer;
|
|
|
|
pFont->m_CurTextureDimensions[TextureIndex] = NewDimensions;
|
|
|
|
pFont->m_TextureSkyline[TextureIndex].m_CurHeightOfPixelColumn.resize(NewDimensions, 0);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2011-01-29 17:48:55 +00:00
|
|
|
int AdjustOutlineThicknessToFontSize(int OutlineThickness, int FontSize)
|
|
|
|
{
|
2018-03-21 14:43:56 +00:00
|
|
|
if(FontSize > 48)
|
2011-01-29 17:48:55 +00:00
|
|
|
OutlineThickness *= 4;
|
|
|
|
else if(FontSize >= 18)
|
|
|
|
OutlineThickness *= 2;
|
|
|
|
return OutlineThickness;
|
2011-01-04 11:30:40 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
void UploadGlyph(CFont *pFont, int TextureIndex, int PosX, int PosY, int Width, int Height, const unsigned char *pData)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
for(int y = 0; y < Height; ++y)
|
|
|
|
{
|
|
|
|
for(int x = 0; x < Width; ++x)
|
|
|
|
{
|
|
|
|
pFont->m_TextureData[TextureIndex][x + PosX + ((y + PosY) * pFont->m_CurTextureDimensions[TextureIndex])] = pData[x + y * Width];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Graphics()->LoadTextureRawSub(pFont->m_aTextures[TextureIndex], PosX, PosY, Width, Height, CImageInfo::FORMAT_ALPHA, pData);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
// 128k * 2 of data used for rendering glyphs
|
2020-09-26 19:41:58 +00:00
|
|
|
unsigned char ms_aGlyphData[(1024 / 4) * (1024 / 4)];
|
|
|
|
unsigned char ms_aGlyphDataOutlined[(1024 / 4) * (1024 / 4)];
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
bool GetCharacterSpace(CFont *pFont, int TextureIndex, int Width, int Height, int &PosX, int &PosY)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
if(pFont->m_CurTextureDimensions[TextureIndex] < Width)
|
|
|
|
return false;
|
|
|
|
if(pFont->m_CurTextureDimensions[TextureIndex] < Height)
|
|
|
|
return false;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2018-03-26 03:08:21 +00:00
|
|
|
// skyline bottom left algorithm
|
2020-09-26 19:41:58 +00:00
|
|
|
std::vector<int> &SkylineHeights = pFont->m_TextureSkyline[TextureIndex].m_CurHeightOfPixelColumn;
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2018-03-26 03:08:21 +00:00
|
|
|
// search a fitting area with less pixel loss
|
2018-03-13 20:49:07 +00:00
|
|
|
int SmallestPixelLossAreaX = 0;
|
|
|
|
int SmallestPixelLossAreaY = pFont->m_CurTextureDimensions[TextureIndex] + 1;
|
|
|
|
int SmallestPixelLossCurPixelLoss = pFont->m_CurTextureDimensions[TextureIndex] * pFont->m_CurTextureDimensions[TextureIndex];
|
|
|
|
|
|
|
|
bool FoundAnyArea = false;
|
|
|
|
for(size_t i = 0; i < SkylineHeights.size(); i++)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
int CurHeight = SkylineHeights[i];
|
|
|
|
int CurPixelLoss = 0;
|
|
|
|
// find width pixels, and we are happy
|
|
|
|
int AreaWidth = 1;
|
|
|
|
for(size_t n = i + 1; n < i + Width && n < SkylineHeights.size(); ++n)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-26 03:08:21 +00:00
|
|
|
++AreaWidth;
|
2018-03-13 20:49:07 +00:00
|
|
|
if(SkylineHeights[n] <= CurHeight)
|
|
|
|
{
|
|
|
|
CurPixelLoss += CurHeight - SkylineHeights[n];
|
|
|
|
}
|
|
|
|
// if the height changed, we will use that new height and adjust the pixel loss
|
|
|
|
else
|
|
|
|
{
|
|
|
|
CurPixelLoss = 0;
|
|
|
|
CurHeight = SkylineHeights[n];
|
|
|
|
for(size_t l = i; l <= n; ++l)
|
|
|
|
{
|
|
|
|
CurPixelLoss += CurHeight - SkylineHeights[l];
|
|
|
|
}
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
// if the area is too high, continue
|
|
|
|
if(CurHeight + Height > pFont->m_CurTextureDimensions[TextureIndex])
|
|
|
|
continue;
|
|
|
|
// if the found area fits our needs, check if we can use it
|
|
|
|
if(AreaWidth == Width)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
if(SmallestPixelLossCurPixelLoss >= CurPixelLoss)
|
|
|
|
{
|
|
|
|
if(CurHeight < SmallestPixelLossAreaY)
|
|
|
|
{
|
|
|
|
SmallestPixelLossCurPixelLoss = CurPixelLoss;
|
|
|
|
SmallestPixelLossAreaX = (int)i;
|
|
|
|
SmallestPixelLossAreaY = CurHeight;
|
|
|
|
FoundAnyArea = true;
|
|
|
|
if(CurPixelLoss == 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
if(FoundAnyArea)
|
|
|
|
{
|
|
|
|
PosX = SmallestPixelLossAreaX;
|
|
|
|
PosY = SmallestPixelLossAreaY;
|
|
|
|
for(int i = PosX; i < PosX + Width; ++i)
|
|
|
|
{
|
|
|
|
SkylineHeights[i] = PosY + Height;
|
|
|
|
}
|
|
|
|
return true;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2018-03-13 20:49:07 +00:00
|
|
|
else
|
|
|
|
return false;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2020-08-20 09:22:25 +00:00
|
|
|
void RenderGlyph(CFont *pFont, CFontSizeData *pSizeData, int Chr)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
FT_Bitmap *pBitmap;
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
int x = 0;
|
|
|
|
int y = 0;
|
2017-03-12 15:47:37 +00:00
|
|
|
unsigned int px, py;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2020-08-20 09:22:25 +00:00
|
|
|
FT_Face FtFace = pFont->m_FtFace;
|
2020-08-20 06:54:59 +00:00
|
|
|
|
2020-08-20 09:22:25 +00:00
|
|
|
FT_Set_Pixel_Sizes(FtFace, 0, pSizeData->m_FontSize);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2019-01-06 05:42:57 +00:00
|
|
|
FT_UInt GlyphIndex = 0;
|
2020-08-20 09:22:25 +00:00
|
|
|
if(FtFace->charmap)
|
|
|
|
GlyphIndex = FT_Get_Char_Index(FtFace, (FT_ULong)Chr);
|
2019-01-06 05:42:57 +00:00
|
|
|
|
2019-03-28 21:38:20 +00:00
|
|
|
if(GlyphIndex == 0)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
for(CFont::SFontFallBack &FallbackFont : pFont->m_FtFallbackFonts)
|
2020-08-20 06:54:59 +00:00
|
|
|
{
|
2020-08-20 09:22:25 +00:00
|
|
|
FtFace = FallbackFont.m_FtFace;
|
|
|
|
FT_Set_Pixel_Sizes(FtFace, 0, pSizeData->m_FontSize);
|
|
|
|
|
|
|
|
if(FtFace->charmap)
|
|
|
|
GlyphIndex = FT_Get_Char_Index(FtFace, (FT_ULong)Chr);
|
2020-08-20 06:54:59 +00:00
|
|
|
|
2020-08-20 09:22:25 +00:00
|
|
|
if(GlyphIndex != 0)
|
|
|
|
break;
|
2020-08-20 06:54:59 +00:00
|
|
|
}
|
2019-03-28 21:38:20 +00:00
|
|
|
|
|
|
|
if(GlyphIndex == 0)
|
|
|
|
{
|
2020-08-20 06:54:59 +00:00
|
|
|
const int ReplacementChr = 0x25a1; // White square to indicate missing glyph
|
2020-08-20 09:22:25 +00:00
|
|
|
FtFace = pFont->m_FtFace;
|
|
|
|
GlyphIndex = FT_Get_Char_Index(FtFace, (FT_ULong)ReplacementChr);
|
2020-08-20 06:54:59 +00:00
|
|
|
|
|
|
|
if(GlyphIndex == 0)
|
|
|
|
{
|
2020-10-26 08:57:41 +00:00
|
|
|
dbg_msg("textrender", "font has no glyph for either %d or replacement char %d", Chr, ReplacementChr);
|
2020-08-20 06:54:59 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-03-28 21:38:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(FT_Load_Glyph(FtFace, GlyphIndex, FT_LOAD_RENDER | FT_LOAD_NO_BITMAP))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-10-26 08:57:41 +00:00
|
|
|
dbg_msg("textrender", "error loading glyph %d", Chr);
|
2018-03-13 20:49:07 +00:00
|
|
|
return;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2020-08-20 09:22:25 +00:00
|
|
|
pBitmap = &FtFace->glyph->bitmap; // ignore_convention
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
unsigned int RealWidth = pBitmap->width;
|
|
|
|
unsigned int RealHeight = pBitmap->rows;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
// adjust spacing
|
|
|
|
int OutlineThickness = 0;
|
|
|
|
if(RealWidth > 0)
|
|
|
|
{
|
|
|
|
OutlineThickness = AdjustOutlineThicknessToFontSize(1, pSizeData->m_FontSize);
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
x += (OutlineThickness + 1);
|
|
|
|
y += (OutlineThickness + 1);
|
|
|
|
}
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
unsigned int Width = RealWidth + x * 2;
|
|
|
|
unsigned int Height = RealHeight + y * 2;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
int X = 0;
|
|
|
|
int Y = 0;
|
2021-09-12 17:40:23 +00:00
|
|
|
|
|
|
|
if(Width > 0 && Height > 0)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2021-09-12 17:40:23 +00:00
|
|
|
// prepare glyph data
|
|
|
|
mem_zero(ms_aGlyphData, Width * Height);
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
for(py = 0; py < pBitmap->rows; py++) // ignore_convention
|
|
|
|
for(px = 0; px < pBitmap->width; px++) // ignore_convention
|
|
|
|
ms_aGlyphData[(py + y) * Width + px + x] = pBitmap->buffer[py * pBitmap->width + px]; // ignore_convention
|
2020-09-14 14:42:33 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
// upload the glyph
|
|
|
|
while(!GetCharacterSpace(pFont, 0, (int)Width, (int)Height, X, Y))
|
|
|
|
{
|
|
|
|
IncreaseFontTexture(pFont, 0);
|
|
|
|
}
|
|
|
|
UploadGlyph(pFont, 0, X, Y, (int)Width, (int)Height, ms_aGlyphData);
|
|
|
|
|
|
|
|
Grow(ms_aGlyphData, ms_aGlyphDataOutlined, Width, Height, OutlineThickness);
|
|
|
|
|
|
|
|
while(!GetCharacterSpace(pFont, 1, (int)Width, (int)Height, X, Y))
|
|
|
|
{
|
|
|
|
IncreaseFontTexture(pFont, 1);
|
|
|
|
}
|
|
|
|
UploadGlyph(pFont, 1, X, Y, (int)Width, (int)Height, ms_aGlyphDataOutlined);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
// set char info
|
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
SFontSizeChar *pFontchr = &pSizeData->m_Chars[Chr];
|
|
|
|
int BMPHeight = pBitmap->rows + y * 2;
|
|
|
|
int BMPWidth = pBitmap->width + x * 2;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2011-02-12 10:40:36 +00:00
|
|
|
pFontchr->m_ID = Chr;
|
2018-03-13 20:49:07 +00:00
|
|
|
pFontchr->m_Height = Height;
|
|
|
|
pFontchr->m_Width = Width;
|
2021-09-12 17:40:23 +00:00
|
|
|
pFontchr->m_CharHeight = RealHeight;
|
|
|
|
pFontchr->m_CharWidth = RealWidth;
|
2020-08-20 09:22:25 +00:00
|
|
|
pFontchr->m_OffsetX = (FtFace->glyph->metrics.horiBearingX >> 6); // ignore_convention
|
|
|
|
pFontchr->m_OffsetY = -((FtFace->glyph->metrics.height >> 6) - (FtFace->glyph->metrics.horiBearingY >> 6));
|
2020-09-26 19:41:58 +00:00
|
|
|
pFontchr->m_AdvanceX = (FtFace->glyph->advance.x >> 6); // ignore_convention
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
pFontchr->m_aUVs[0] = X;
|
|
|
|
pFontchr->m_aUVs[1] = Y;
|
|
|
|
pFontchr->m_aUVs[2] = pFontchr->m_aUVs[0] + BMPWidth;
|
|
|
|
pFontchr->m_aUVs[3] = pFontchr->m_aUVs[1] + BMPHeight;
|
2019-01-06 05:42:57 +00:00
|
|
|
pFontchr->m_GlyphIndex = GlyphIndex;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-20 09:22:25 +00:00
|
|
|
SFontSizeChar *GetChar(CFont *pFont, CFontSizeData *pSizeData, int Chr)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
std::map<int, SFontSizeChar>::iterator it = pSizeData->m_Chars.find(Chr);
|
|
|
|
if(it == pSizeData->m_Chars.end())
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
// render and add character
|
2020-09-26 19:41:58 +00:00
|
|
|
SFontSizeChar &FontSizeChr = pSizeData->m_Chars[Chr];
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2020-08-20 09:22:25 +00:00
|
|
|
RenderGlyph(pFont, pSizeData, Chr);
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
return &FontSizeChr;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2018-03-13 20:49:07 +00:00
|
|
|
else
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
return &it->second;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
}
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2019-01-06 05:42:57 +00:00
|
|
|
float Kerning(CFont *pFont, FT_UInt GlyphIndexLeft, FT_UInt GlyphIndexRight)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
FT_Vector Kerning = {0, 0};
|
2019-01-06 05:42:57 +00:00
|
|
|
FT_Get_Kerning(pFont->m_FtFace, GlyphIndexLeft, GlyphIndexRight, FT_KERNING_DEFAULT, &Kerning);
|
2020-09-26 19:41:58 +00:00
|
|
|
return (Kerning.x >> 6);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
public:
|
|
|
|
CTextRender()
|
|
|
|
{
|
|
|
|
m_pGraphics = 0;
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
m_Color = DefaultTextColor();
|
|
|
|
m_OutlineColor = DefaultTextOutlineColor();
|
|
|
|
m_SelectionColor = DefaultSelectionColor();
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
m_pCurFont = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
m_pDefaultFont = 0;
|
2017-07-21 18:45:23 +00:00
|
|
|
m_FTLibrary = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
m_RenderFlags = 0;
|
2021-09-16 14:50:17 +00:00
|
|
|
m_CursorRenderTime = time_get_microseconds();
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2017-07-21 17:10:50 +00:00
|
|
|
virtual ~CTextRender()
|
|
|
|
{
|
2021-09-12 17:40:23 +00:00
|
|
|
for(auto *pTextCont : m_TextContainers)
|
|
|
|
{
|
|
|
|
pTextCont->Reset();
|
|
|
|
delete pTextCont;
|
|
|
|
}
|
|
|
|
m_TextContainers.clear();
|
|
|
|
|
2020-10-26 14:14:07 +00:00
|
|
|
for(auto &pFont : m_Fonts)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
FT_Done_Face(pFont->m_FtFace);
|
2020-09-30 21:38:05 +00:00
|
|
|
|
2020-10-26 14:14:07 +00:00
|
|
|
for(CFont::SFontFallBack &FallbackFont : pFont->m_FtFallbackFonts)
|
2020-09-30 21:38:05 +00:00
|
|
|
{
|
|
|
|
FT_Done_Face(FallbackFont.m_FtFace);
|
|
|
|
}
|
|
|
|
|
2020-10-26 14:14:07 +00:00
|
|
|
delete pFont;
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
2017-07-21 18:45:23 +00:00
|
|
|
|
2017-07-28 18:44:03 +00:00
|
|
|
if(m_FTLibrary != 0)
|
2017-07-21 18:45:23 +00:00
|
|
|
FT_Done_FreeType(m_FTLibrary);
|
2017-07-21 17:10:50 +00:00
|
|
|
}
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
virtual void Init()
|
|
|
|
{
|
|
|
|
m_pGraphics = Kernel()->RequestInterface<IGraphics>();
|
|
|
|
FT_Init_FreeType(&m_FTLibrary);
|
2020-03-20 12:48:45 +00:00
|
|
|
// print freetype version
|
|
|
|
{
|
|
|
|
int LMajor, LMinor, LPatch;
|
|
|
|
FT_Library_Version(m_FTLibrary, &LMajor, &LMinor, &LPatch);
|
2020-03-23 11:50:19 +00:00
|
|
|
dbg_msg("freetype", "freetype version %d.%d.%d (compiled = %d.%d.%d)", LMajor, LMinor, LPatch,
|
|
|
|
FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH);
|
2020-03-20 12:48:45 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
m_FirstFreeTextContainerIndex = -1;
|
|
|
|
|
|
|
|
m_DefaultTextContainerInfo.m_Stride = sizeof(STextCharQuadVertex);
|
|
|
|
|
2022-02-14 23:15:06 +00:00
|
|
|
m_DefaultTextContainerInfo.m_Attributes.emplace_back();
|
2020-09-26 19:41:58 +00:00
|
|
|
SBufferContainerInfo::SAttribute *pAttr = &m_DefaultTextContainerInfo.m_Attributes.back();
|
2018-03-13 20:49:07 +00:00
|
|
|
pAttr->m_DataTypeCount = 2;
|
|
|
|
pAttr->m_FuncType = 0;
|
|
|
|
pAttr->m_Normalized = false;
|
|
|
|
pAttr->m_pOffset = 0;
|
|
|
|
pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
|
|
|
|
pAttr->m_VertBufferBindingIndex = -1;
|
2022-02-14 23:15:06 +00:00
|
|
|
m_DefaultTextContainerInfo.m_Attributes.emplace_back();
|
2018-03-13 20:49:07 +00:00
|
|
|
pAttr = &m_DefaultTextContainerInfo.m_Attributes.back();
|
|
|
|
pAttr->m_DataTypeCount = 2;
|
|
|
|
pAttr->m_FuncType = 0;
|
|
|
|
pAttr->m_Normalized = false;
|
2020-09-26 19:41:58 +00:00
|
|
|
pAttr->m_pOffset = (void *)(sizeof(float) * 2);
|
2018-03-13 20:49:07 +00:00
|
|
|
pAttr->m_Type = GRAPHICS_TYPE_FLOAT;
|
|
|
|
pAttr->m_VertBufferBindingIndex = -1;
|
2022-02-14 23:15:06 +00:00
|
|
|
m_DefaultTextContainerInfo.m_Attributes.emplace_back();
|
2018-03-13 20:49:07 +00:00
|
|
|
pAttr = &m_DefaultTextContainerInfo.m_Attributes.back();
|
|
|
|
pAttr->m_DataTypeCount = 4;
|
|
|
|
pAttr->m_FuncType = 0;
|
|
|
|
pAttr->m_Normalized = true;
|
2020-09-26 19:41:58 +00:00
|
|
|
pAttr->m_pOffset = (void *)(sizeof(float) * 2 + sizeof(float) * 2);
|
2018-03-13 20:49:07 +00:00
|
|
|
pAttr->m_Type = GRAPHICS_TYPE_UNSIGNED_BYTE;
|
|
|
|
pAttr->m_VertBufferBindingIndex = -1;
|
|
|
|
|
|
|
|
IStorage *pStorage = Kernel()->RequestInterface<IStorage>();
|
2021-09-13 08:06:34 +00:00
|
|
|
char aFilename[IO_MAX_PATH_LENGTH];
|
2018-03-13 20:49:07 +00:00
|
|
|
const char *pFontFile = "fonts/Icons.ttf";
|
|
|
|
IOHANDLE File = pStorage->OpenFile(pFontFile, IOFLAG_READ, IStorage::TYPE_ALL, aFilename, sizeof(aFilename));
|
|
|
|
if(File)
|
|
|
|
{
|
2020-09-13 21:02:01 +00:00
|
|
|
size_t Size = io_length(File);
|
|
|
|
unsigned char *pBuf = (unsigned char *)malloc(Size);
|
|
|
|
io_read(File, pBuf, Size);
|
2018-03-13 20:49:07 +00:00
|
|
|
io_close(File);
|
2020-09-13 21:02:01 +00:00
|
|
|
LoadFont(aFilename, pBuf, Size);
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2020-09-13 21:02:01 +00:00
|
|
|
virtual CFont *LoadFont(const char *pFilename, const unsigned char *pBuf, size_t Size)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
CFont *pFont = new CFont();
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
str_copy(pFont->m_aFilename, pFilename, sizeof(pFont->m_aFilename));
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2020-09-13 21:02:01 +00:00
|
|
|
if(FT_New_Memory_Face(m_FTLibrary, pBuf, Size, 0, &pFont->m_FtFace))
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2018-03-13 20:49:07 +00:00
|
|
|
delete pFont;
|
2010-05-29 07:25:38 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
dbg_msg("textrender", "loaded pFont from '%s'", pFilename);
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2020-09-30 21:38:05 +00:00
|
|
|
pFont->m_pBuf = (void *)pBuf;
|
2020-09-03 12:18:44 +00:00
|
|
|
pFont->m_CurTextureDimensions[0] = 1024;
|
2018-03-13 20:49:07 +00:00
|
|
|
pFont->m_TextureData[0] = new unsigned char[pFont->m_CurTextureDimensions[0] * pFont->m_CurTextureDimensions[0]];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(pFont->m_TextureData[0], (size_t)pFont->m_CurTextureDimensions[0] * pFont->m_CurTextureDimensions[0] * sizeof(unsigned char));
|
2020-09-03 12:18:44 +00:00
|
|
|
pFont->m_CurTextureDimensions[1] = 1024;
|
2018-03-13 20:49:07 +00:00
|
|
|
pFont->m_TextureData[1] = new unsigned char[pFont->m_CurTextureDimensions[1] * pFont->m_CurTextureDimensions[1]];
|
2020-10-05 17:03:14 +00:00
|
|
|
mem_zero(pFont->m_TextureData[1], (size_t)pFont->m_CurTextureDimensions[1] * pFont->m_CurTextureDimensions[1] * sizeof(unsigned char));
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
pFont->m_aTextures[0] = InitTexture(pFont->m_CurTextureDimensions[0], pFont->m_CurTextureDimensions[0]);
|
|
|
|
pFont->m_aTextures[1] = InitTexture(pFont->m_CurTextureDimensions[1], pFont->m_CurTextureDimensions[1]);
|
|
|
|
|
|
|
|
pFont->m_TextureSkyline[0].m_CurHeightOfPixelColumn.resize(pFont->m_CurTextureDimensions[0], 0);
|
|
|
|
pFont->m_TextureSkyline[1].m_CurHeightOfPixelColumn.resize(pFont->m_CurTextureDimensions[1], 0);
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
pFont->InitFontSizes();
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
m_Fonts.push_back(pFont);
|
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
return pFont;
|
2020-08-20 09:22:25 +00:00
|
|
|
}
|
|
|
|
|
2020-09-13 21:02:01 +00:00
|
|
|
virtual bool LoadFallbackFont(CFont *pFont, const char *pFilename, const unsigned char *pBuf, size_t Size)
|
2020-08-20 09:22:25 +00:00
|
|
|
{
|
|
|
|
CFont::SFontFallBack FallbackFont;
|
2020-09-30 21:38:05 +00:00
|
|
|
FallbackFont.m_pBuf = (void *)pBuf;
|
2020-08-20 09:22:25 +00:00
|
|
|
str_copy(FallbackFont.m_aFilename, pFilename, sizeof(FallbackFont.m_aFilename));
|
|
|
|
|
2020-09-13 21:02:01 +00:00
|
|
|
if(FT_New_Memory_Face(m_FTLibrary, pBuf, Size, 0, &FallbackFont.m_FtFace) == 0)
|
2020-08-20 09:22:25 +00:00
|
|
|
{
|
|
|
|
dbg_msg("textrender", "loaded fallback font from '%s'", pFilename);
|
2020-11-05 10:34:20 +00:00
|
|
|
pFont->m_FtFallbackFonts.emplace_back(FallbackFont);
|
2020-08-20 09:22:25 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
virtual CFont *GetFont(int FontIndex)
|
|
|
|
{
|
|
|
|
if(FontIndex >= 0 && FontIndex < (int)m_Fonts.size())
|
|
|
|
return m_Fonts[FontIndex];
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
CFont *GetFont(const char *pFilename)
|
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
for(auto &pFont : m_Fonts)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
if(str_comp(pFilename, pFont->m_aFilename) == 0)
|
|
|
|
return pFont;
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
|
2020-08-20 09:22:25 +00:00
|
|
|
virtual void SetDefaultFont(CFont *pFont)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
2020-08-20 09:22:25 +00:00
|
|
|
dbg_msg("textrender", "default pFont set %p", pFont);
|
2010-05-29 07:25:38 +00:00
|
|
|
m_pDefaultFont = pFont;
|
2018-03-13 20:49:07 +00:00
|
|
|
m_pCurFont = m_pDefaultFont;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
virtual void SetCurFont(CFont *pFont)
|
|
|
|
{
|
|
|
|
if(pFont == NULL)
|
|
|
|
m_pCurFont = m_pDefaultFont;
|
|
|
|
else
|
|
|
|
m_pCurFont = pFont;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
virtual void SetCursor(CTextCursor *pCursor, float x, float y, float FontSize, int Flags)
|
|
|
|
{
|
|
|
|
mem_zero(pCursor, sizeof(*pCursor));
|
|
|
|
pCursor->m_FontSize = FontSize;
|
|
|
|
pCursor->m_StartX = x;
|
|
|
|
pCursor->m_StartY = y;
|
|
|
|
pCursor->m_X = x;
|
|
|
|
pCursor->m_Y = y;
|
|
|
|
pCursor->m_LineCount = 1;
|
|
|
|
pCursor->m_LineWidth = -1;
|
|
|
|
pCursor->m_Flags = Flags;
|
2020-09-03 22:53:26 +00:00
|
|
|
pCursor->m_GlyphCount = 0;
|
2010-05-29 07:25:38 +00:00
|
|
|
pCursor->m_CharCount = 0;
|
2020-10-06 10:25:10 +00:00
|
|
|
pCursor->m_MaxCharacterHeight = 0;
|
2020-10-13 20:08:52 +00:00
|
|
|
pCursor->m_LongestLineWidth = 0;
|
2021-09-12 17:40:23 +00:00
|
|
|
|
|
|
|
pCursor->m_CalculateSelectionMode = TEXT_CURSOR_SELECTION_MODE_NONE;
|
|
|
|
pCursor->m_PressMouseX = 0;
|
|
|
|
pCursor->m_PressMouseY = 0;
|
|
|
|
pCursor->m_ReleaseMouseX = 0;
|
|
|
|
pCursor->m_ReleaseMouseY = 0;
|
|
|
|
pCursor->m_SelectionStart = 0;
|
|
|
|
pCursor->m_SelectionEnd = 0;
|
2021-09-16 14:50:17 +00:00
|
|
|
|
|
|
|
pCursor->m_CursorMode = TEXT_CURSOR_CURSOR_MODE_NONE;
|
|
|
|
pCursor->m_CursorCharacter = -1;
|
2020-10-13 20:08:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void MoveCursor(CTextCursor *pCursor, float x, float y)
|
|
|
|
{
|
|
|
|
pCursor->m_X += x;
|
|
|
|
pCursor->m_Y += y;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2020-10-29 00:55:01 +00:00
|
|
|
virtual void SetCursorPosition(CTextCursor *pCursor, float x, float y)
|
|
|
|
{
|
|
|
|
pCursor->m_X = x;
|
|
|
|
pCursor->m_Y = y;
|
|
|
|
}
|
|
|
|
|
2020-07-15 19:42:48 +00:00
|
|
|
virtual void Text(void *pFontSetV, float x, float y, float Size, const char *pText, float LineWidth)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CTextCursor Cursor;
|
|
|
|
SetCursor(&Cursor, x, y, Size, TEXTFLAG_RENDER);
|
2020-07-15 19:42:48 +00:00
|
|
|
Cursor.m_LineWidth = LineWidth;
|
2020-10-06 10:25:10 +00:00
|
|
|
int OldRenderFlags = m_RenderFlags;
|
|
|
|
if(LineWidth <= 0)
|
|
|
|
SetRenderFlags(OldRenderFlags | ETextRenderFlags::TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE);
|
2010-05-29 07:25:38 +00:00
|
|
|
TextEx(&Cursor, pText, -1);
|
2020-10-06 10:25:10 +00:00
|
|
|
SetRenderFlags(OldRenderFlags);
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
|
|
|
|
2020-10-06 10:25:10 +00:00
|
|
|
virtual float TextWidth(void *pFontSetV, float Size, const char *pText, int StrLength, float LineWidth, float *pAlignedHeight = NULL, float *pMaxCharacterHeightInLine = NULL)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CTextCursor Cursor;
|
|
|
|
SetCursor(&Cursor, 0, 0, Size, 0);
|
2020-07-15 19:10:13 +00:00
|
|
|
Cursor.m_LineWidth = LineWidth;
|
2020-10-06 10:25:10 +00:00
|
|
|
int OldRenderFlags = m_RenderFlags;
|
|
|
|
if(LineWidth <= 0)
|
|
|
|
SetRenderFlags(OldRenderFlags | ETextRenderFlags::TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE);
|
2020-07-15 19:10:13 +00:00
|
|
|
TextEx(&Cursor, pText, StrLength);
|
2020-10-06 10:25:10 +00:00
|
|
|
SetRenderFlags(OldRenderFlags);
|
2018-03-21 14:43:56 +00:00
|
|
|
if(pAlignedHeight != NULL)
|
|
|
|
*pAlignedHeight = Cursor.m_AlignedFontSize;
|
2020-10-06 10:25:10 +00:00
|
|
|
if(pMaxCharacterHeightInLine != NULL)
|
|
|
|
*pMaxCharacterHeightInLine = Cursor.m_MaxCharacterHeight;
|
2010-05-29 07:25:38 +00:00
|
|
|
return Cursor.m_X;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-08-16 00:21:18 +00:00
|
|
|
virtual int TextLineCount(void *pFontSetV, float Size, const char *pText, float LineWidth)
|
2010-05-29 07:25:38 +00:00
|
|
|
{
|
|
|
|
CTextCursor Cursor;
|
|
|
|
SetCursor(&Cursor, 0, 0, Size, 0);
|
|
|
|
Cursor.m_LineWidth = LineWidth;
|
2020-10-06 10:25:10 +00:00
|
|
|
int OldRenderFlags = m_RenderFlags;
|
|
|
|
if(LineWidth <= 0)
|
|
|
|
SetRenderFlags(OldRenderFlags | ETextRenderFlags::TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE);
|
2010-05-29 07:25:38 +00:00
|
|
|
TextEx(&Cursor, pText, -1);
|
2020-10-06 10:25:10 +00:00
|
|
|
SetRenderFlags(OldRenderFlags);
|
2010-05-29 07:25:38 +00:00
|
|
|
return Cursor.m_LineCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void TextColor(float r, float g, float b, float a)
|
|
|
|
{
|
2019-04-26 22:34:20 +00:00
|
|
|
m_Color.r = r;
|
|
|
|
m_Color.g = g;
|
|
|
|
m_Color.b = b;
|
|
|
|
m_Color.a = a;
|
2010-05-29 07:25:38 +00:00
|
|
|
}
|
2019-04-26 22:34:20 +00:00
|
|
|
virtual void TextColor(ColorRGBA rgb) { m_Color = rgb; };
|
2011-03-13 11:55:00 +00:00
|
|
|
|
|
|
|
virtual void TextOutlineColor(float r, float g, float b, float a)
|
|
|
|
{
|
2019-04-26 22:34:20 +00:00
|
|
|
m_OutlineColor.r = r;
|
|
|
|
m_OutlineColor.g = g;
|
|
|
|
m_OutlineColor.b = b;
|
|
|
|
m_OutlineColor.a = a;
|
2011-03-13 11:55:00 +00:00
|
|
|
}
|
2019-04-26 22:34:20 +00:00
|
|
|
virtual void TextOutlineColor(ColorRGBA rgb) { m_OutlineColor = rgb; };
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
virtual void TextSelectionColor(float r, float g, float b, float a)
|
|
|
|
{
|
|
|
|
m_SelectionColor.r = r;
|
|
|
|
m_SelectionColor.g = g;
|
|
|
|
m_SelectionColor.b = b;
|
|
|
|
m_SelectionColor.a = a;
|
|
|
|
}
|
|
|
|
virtual void TextSelectionColor(ColorRGBA rgb) { m_SelectionColor = rgb; };
|
|
|
|
|
2020-11-08 18:41:16 +00:00
|
|
|
virtual ColorRGBA GetTextColor() { return m_Color; }
|
|
|
|
virtual ColorRGBA GetTextOutlineColor() { return m_OutlineColor; }
|
2021-09-12 17:40:23 +00:00
|
|
|
virtual ColorRGBA GetTextSelectionColor() { return m_SelectionColor; }
|
2020-11-08 18:41:16 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
virtual void TextEx(CTextCursor *pCursor, const char *pText, int Length)
|
|
|
|
{
|
2021-09-12 17:40:23 +00:00
|
|
|
int TextCont = CreateTextContainer(pCursor, pText, Length);
|
|
|
|
if(TextCont != -1)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2021-09-12 17:40:23 +00:00
|
|
|
if((pCursor->m_Flags & TEXTFLAG_RENDER) != 0)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2021-09-12 17:40:23 +00:00
|
|
|
STextRenderColor TextColor = DefaultTextColor();
|
|
|
|
STextRenderColor TextColorOutline = DefaultTextOutlineColor();
|
|
|
|
RenderTextContainer(TextCont, &TextColor, &TextColorOutline);
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
2021-09-12 17:40:23 +00:00
|
|
|
DeleteTextContainer(TextCont);
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-12 10:29:47 +00:00
|
|
|
virtual int CreateTextContainer(CTextCursor *pCursor, const char *pText, int Length = -1)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
|
|
|
CFont *pFont = pCursor->m_pFont;
|
|
|
|
|
|
|
|
// fetch pFont data
|
|
|
|
if(!pFont)
|
|
|
|
pFont = m_pCurFont;
|
|
|
|
|
|
|
|
if(!pFont)
|
|
|
|
return -1;
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
bool IsRendered = (pCursor->m_Flags & TEXTFLAG_RENDER) != 0;
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
int ContainerIndex = GetFreeTextContainerIndex();
|
2020-09-26 19:41:58 +00:00
|
|
|
STextContainer &TextContainer = GetTextContainer(ContainerIndex);
|
2018-03-13 20:49:07 +00:00
|
|
|
TextContainer.m_pFont = pFont;
|
|
|
|
|
|
|
|
CFontSizeData *pSizeData = NULL;
|
|
|
|
|
|
|
|
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
|
2018-03-21 14:43:56 +00:00
|
|
|
float FakeToScreenX, FakeToScreenY;
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
int ActualSize;
|
|
|
|
|
|
|
|
float Size = pCursor->m_FontSize;
|
2018-03-15 02:33:22 +00:00
|
|
|
|
|
|
|
// calculate the font size of the displayed glyphs
|
2018-03-13 20:49:07 +00:00
|
|
|
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
FakeToScreenX = (Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0));
|
2018-03-13 20:49:07 +00:00
|
|
|
FakeToScreenY = (Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0));
|
|
|
|
|
2021-05-02 08:32:05 +00:00
|
|
|
int ActualX = (int)((pCursor->m_X * FakeToScreenX) + 0.5f);
|
|
|
|
int ActualY = (int)((pCursor->m_Y * FakeToScreenY) + 0.5f);
|
2018-03-21 14:43:56 +00:00
|
|
|
|
|
|
|
TextContainer.m_AlignedStartX = ActualX / FakeToScreenX;
|
|
|
|
TextContainer.m_AlignedStartY = ActualY / FakeToScreenY;
|
2018-03-15 02:33:22 +00:00
|
|
|
TextContainer.m_X = pCursor->m_X;
|
|
|
|
TextContainer.m_Y = pCursor->m_Y;
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
TextContainer.m_Flags = pCursor->m_Flags;
|
|
|
|
|
2020-10-12 15:46:06 +00:00
|
|
|
int OldRenderFlags = m_RenderFlags;
|
|
|
|
if(pCursor->m_LineWidth <= 0)
|
|
|
|
SetRenderFlags(OldRenderFlags | ETextRenderFlags::TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING | ETextRenderFlags::TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE);
|
2020-10-13 20:08:52 +00:00
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
TextContainer.m_RenderFlags = m_RenderFlags;
|
2020-10-12 15:46:06 +00:00
|
|
|
SetRenderFlags(OldRenderFlags);
|
2018-03-21 14:43:56 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
// same with size
|
|
|
|
ActualSize = (int)(Size * FakeToScreenY);
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
pSizeData = pFont->GetFontSize(ActualSize);
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
TextContainer.m_FontSize = pSizeData->m_FontSize;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2020-10-12 10:29:47 +00:00
|
|
|
AppendTextContainer(pCursor, ContainerIndex, pText, Length);
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2022-01-22 12:54:25 +00:00
|
|
|
if(TextContainer.m_StringInfo.m_CharacterQuads.empty() && TextContainer.m_StringInfo.m_SelectionQuadContainerIndex == -1 && IsRendered)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
|
|
|
FreeTextContainer(ContainerIndex);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
TextContainer.m_StringInfo.m_QuadNum = TextContainer.m_StringInfo.m_CharacterQuads.size();
|
2021-09-16 14:50:17 +00:00
|
|
|
if(Graphics()->IsTextBufferingEnabled() && IsRendered && !TextContainer.m_StringInfo.m_CharacterQuads.empty())
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2020-10-22 20:21:19 +00:00
|
|
|
if((TextContainer.m_RenderFlags & TEXT_RENDER_FLAG_NO_AUTOMATIC_QUAD_UPLOAD) == 0)
|
|
|
|
{
|
|
|
|
UploadTextContainer(ContainerIndex);
|
|
|
|
}
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
TextContainer.m_LineCount = pCursor->m_LineCount;
|
2020-09-03 22:53:26 +00:00
|
|
|
TextContainer.m_GlyphCount = pCursor->m_GlyphCount;
|
2018-03-13 20:49:07 +00:00
|
|
|
TextContainer.m_CharCount = pCursor->m_CharCount;
|
|
|
|
TextContainer.m_MaxLines = pCursor->m_MaxLines;
|
|
|
|
TextContainer.m_StartX = pCursor->m_StartX;
|
|
|
|
TextContainer.m_StartY = pCursor->m_StartY;
|
|
|
|
TextContainer.m_LineWidth = pCursor->m_LineWidth;
|
|
|
|
TextContainer.m_UnscaledFontSize = pCursor->m_FontSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ContainerIndex;
|
|
|
|
}
|
|
|
|
|
2020-10-12 10:29:47 +00:00
|
|
|
virtual void AppendTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
STextContainer &TextContainer = GetTextContainer(TextContainerIndex);
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
CFontSizeData *pSizeData = NULL;
|
|
|
|
|
|
|
|
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
|
2018-03-21 14:43:56 +00:00
|
|
|
float FakeToScreenX, FakeToScreenY;
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
int ActualSize;
|
|
|
|
int GotNewLine = 0;
|
2020-10-28 14:56:02 +00:00
|
|
|
int GotNewLineLast = 0;
|
2018-03-13 20:49:07 +00:00
|
|
|
float DrawX = 0.0f, DrawY = 0.0f;
|
|
|
|
int LineCount = 0;
|
2018-03-21 14:43:56 +00:00
|
|
|
float CursorX, CursorY;
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
float Size = pCursor->m_FontSize;
|
|
|
|
|
2018-03-15 02:33:22 +00:00
|
|
|
// calculate the font size of the displayed glyphs
|
2018-03-13 20:49:07 +00:00
|
|
|
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
FakeToScreenX = (Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0));
|
2018-03-13 20:49:07 +00:00
|
|
|
FakeToScreenY = (Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0));
|
|
|
|
|
2021-05-02 08:32:05 +00:00
|
|
|
int ActualX = (int)((pCursor->m_X * FakeToScreenX) + 0.5f);
|
|
|
|
int ActualY = (int)((pCursor->m_Y * FakeToScreenY) + 0.5f);
|
2018-03-21 14:43:56 +00:00
|
|
|
CursorX = ActualX / FakeToScreenX;
|
|
|
|
CursorY = ActualY / FakeToScreenY;
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
// same with size
|
|
|
|
ActualSize = (int)(Size * FakeToScreenY);
|
|
|
|
Size = ActualSize / FakeToScreenY;
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
pCursor->m_AlignedFontSize = Size;
|
|
|
|
|
|
|
|
pSizeData = TextContainer.m_pFont->GetFontSize(TextContainer.m_FontSize);
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
// string length
|
2020-10-12 10:29:47 +00:00
|
|
|
if(Length < 0)
|
|
|
|
Length = str_length(pText);
|
2018-03-13 20:49:07 +00:00
|
|
|
|
|
|
|
float Scale = 1.0f / pSizeData->m_FontSize;
|
|
|
|
|
|
|
|
const char *pCurrent = (char *)pText;
|
|
|
|
const char *pEnd = pCurrent + Length;
|
2018-03-15 02:33:22 +00:00
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
int RenderFlags = TextContainer.m_RenderFlags;
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if((RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) != 0)
|
2018-03-21 14:43:56 +00:00
|
|
|
{
|
|
|
|
DrawX = pCursor->m_X;
|
|
|
|
DrawY = pCursor->m_Y;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
DrawX = CursorX;
|
|
|
|
DrawY = CursorY;
|
|
|
|
}
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
LineCount = pCursor->m_LineCount;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2019-01-06 05:42:57 +00:00
|
|
|
FT_UInt LastCharGlyphIndex = 0;
|
|
|
|
size_t CharacterCounter = 0;
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
bool IsRendered = (pCursor->m_Flags & TEXTFLAG_RENDER) != 0;
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
IGraphics::CQuadItem CursorQuads[2];
|
|
|
|
bool HasCursor = false;
|
|
|
|
|
|
|
|
float CursorInnerWidth = (((ScreenX1 - ScreenX0) / Graphics()->ScreenWidth())) * 2;
|
|
|
|
float CursorOuterWidth = CursorInnerWidth * 2;
|
|
|
|
float CursorOuterInnerDiff = (CursorOuterWidth - CursorInnerWidth) / 2;
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
std::vector<IGraphics::CQuadItem> SelectionQuads;
|
|
|
|
bool SelectionStarted = false;
|
|
|
|
bool SelectionUsedPress = false;
|
|
|
|
bool SelectionUsedRelease = false;
|
|
|
|
int SelectionStartChar = -1;
|
|
|
|
int SelectionEndChar = -1;
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
auto &&CheckInsideChar = [&](bool CheckOuter, int CursorX, int CursorY, float LastCharX, float LastCharWidth, float CharX, float CharWidth, float CharY) -> bool {
|
2022-01-22 16:34:23 +00:00
|
|
|
return (LastCharX - LastCharWidth / 2 <= CursorX &&
|
|
|
|
CharX + CharWidth / 2 > CursorX &&
|
|
|
|
CharY - Size <= CursorY &&
|
|
|
|
CharY > CursorY) ||
|
|
|
|
(CheckOuter &&
|
|
|
|
CharY - Size > CursorY);
|
2021-09-16 14:50:17 +00:00
|
|
|
};
|
2021-09-12 17:40:23 +00:00
|
|
|
auto &&CheckSelectionStart = [&](bool CheckOuter, int CursorX, int CursorY, int &SelectionChar, bool &SelectionUsedCase, float LastCharX, float LastCharWidth, float CharX, float CharWidth, float CharY) {
|
|
|
|
if(!SelectionStarted && !SelectionUsedCase)
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
if(CheckInsideChar(CheckOuter, CursorX, CursorY, LastCharX, LastCharWidth, CharX, CharWidth, CharY))
|
2021-09-12 17:40:23 +00:00
|
|
|
{
|
|
|
|
SelectionChar = CharacterCounter;
|
|
|
|
SelectionStarted = !SelectionStarted;
|
|
|
|
SelectionUsedCase = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2021-09-16 14:50:17 +00:00
|
|
|
auto &&CheckOutsideChar = [&](bool CheckOuter, int CursorX, int CursorY, float CharX, float CharWidth, float CharY) -> bool {
|
2022-01-22 16:34:23 +00:00
|
|
|
return (CharX + CharWidth / 2 > CursorX &&
|
|
|
|
CharY - Size <= CursorY &&
|
|
|
|
CharY > CursorY) ||
|
|
|
|
(CheckOuter &&
|
|
|
|
CharY <= CursorY);
|
2021-09-16 14:50:17 +00:00
|
|
|
};
|
2021-09-12 17:40:23 +00:00
|
|
|
auto &&CheckSelectionEnd = [&](bool CheckOuter, int CursorX, int CursorY, int &SelectionChar, bool &SelectionUsedCase, float CharX, float CharWidth, float CharY) {
|
|
|
|
if(SelectionStarted && !SelectionUsedCase)
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
if(CheckOutsideChar(CheckOuter, CursorX, CursorY, CharX, CharWidth, CharY))
|
2021-09-12 17:40:23 +00:00
|
|
|
{
|
|
|
|
SelectionChar = CharacterCounter;
|
|
|
|
SelectionStarted = !SelectionStarted;
|
|
|
|
SelectionUsedCase = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
float LastSelX = DrawX;
|
|
|
|
float LastSelWidth = 0;
|
|
|
|
float LastCharX = DrawX;
|
|
|
|
float LastCharWidth = 0;
|
|
|
|
|
|
|
|
auto &&StartNewLine = [&]() {
|
|
|
|
DrawX = pCursor->m_StartX;
|
|
|
|
DrawY += Size;
|
|
|
|
if((RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0)
|
|
|
|
{
|
|
|
|
DrawX = (int)((DrawX * FakeToScreenX) + 0.5f) / FakeToScreenX; // realign
|
|
|
|
DrawY = (int)((DrawY * FakeToScreenY) + 0.5f) / FakeToScreenY;
|
|
|
|
}
|
|
|
|
LastSelX = DrawX;
|
|
|
|
LastSelWidth = 0;
|
|
|
|
LastCharX = DrawX;
|
|
|
|
LastCharWidth = 0;
|
|
|
|
++LineCount;
|
|
|
|
};
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
if(pCursor->m_CalculateSelectionMode != TEXT_CURSOR_SELECTION_MODE_NONE || pCursor->m_CursorMode != TEXT_CURSOR_CURSOR_MODE_NONE)
|
2021-09-12 17:40:23 +00:00
|
|
|
{
|
|
|
|
if(IsRendered)
|
|
|
|
{
|
|
|
|
if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex != -1)
|
|
|
|
Graphics()->QuadContainerReset(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex);
|
|
|
|
}
|
2021-09-16 14:50:17 +00:00
|
|
|
|
|
|
|
// if in calculate mode, also calculate the cursor
|
|
|
|
if(pCursor->m_CursorMode == TEXT_CURSOR_CURSOR_MODE_CALCULATE)
|
|
|
|
pCursor->m_CursorCharacter = -1;
|
2021-09-12 17:40:23 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
while(pCurrent < pEnd && (pCursor->m_MaxLines < 1 || LineCount <= pCursor->m_MaxLines))
|
|
|
|
{
|
|
|
|
int NewLine = 0;
|
|
|
|
const char *pBatchEnd = pEnd;
|
2020-09-26 19:41:58 +00:00
|
|
|
if(pCursor->m_LineWidth > 0 && !(pCursor->m_Flags & TEXTFLAG_STOP_AT_END))
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2019-04-26 19:36:49 +00:00
|
|
|
int Wlen = minimum(WordLength((char *)pCurrent), (int)(pEnd - pCurrent));
|
2018-03-13 20:49:07 +00:00
|
|
|
CTextCursor Compare = *pCursor;
|
2021-09-12 17:40:23 +00:00
|
|
|
Compare.m_CalculateSelectionMode = TEXT_CURSOR_SELECTION_MODE_NONE;
|
2021-09-16 14:50:17 +00:00
|
|
|
Compare.m_CursorMode = TEXT_CURSOR_CURSOR_MODE_NONE;
|
2018-03-13 20:49:07 +00:00
|
|
|
Compare.m_X = DrawX;
|
|
|
|
Compare.m_Y = DrawY;
|
|
|
|
Compare.m_Flags &= ~TEXTFLAG_RENDER;
|
|
|
|
Compare.m_LineWidth = -1;
|
|
|
|
TextEx(&Compare, pCurrent, Wlen);
|
|
|
|
|
|
|
|
if(Compare.m_X - DrawX > pCursor->m_LineWidth)
|
|
|
|
{
|
|
|
|
// word can't be fitted in one line, cut it
|
|
|
|
CTextCursor Cutter = *pCursor;
|
2021-09-12 17:40:23 +00:00
|
|
|
Cutter.m_CalculateSelectionMode = TEXT_CURSOR_SELECTION_MODE_NONE;
|
2021-09-16 14:50:17 +00:00
|
|
|
Cutter.m_CursorMode = TEXT_CURSOR_CURSOR_MODE_NONE;
|
2020-09-03 22:53:26 +00:00
|
|
|
Cutter.m_GlyphCount = 0;
|
2018-03-13 20:49:07 +00:00
|
|
|
Cutter.m_CharCount = 0;
|
|
|
|
Cutter.m_X = DrawX;
|
|
|
|
Cutter.m_Y = DrawY;
|
|
|
|
Cutter.m_Flags &= ~TEXTFLAG_RENDER;
|
|
|
|
Cutter.m_Flags |= TEXTFLAG_STOP_AT_END;
|
|
|
|
|
2019-04-11 10:21:42 +00:00
|
|
|
TextEx(&Cutter, pCurrent, Wlen);
|
2020-09-03 22:53:26 +00:00
|
|
|
int WordGlyphs = Cutter.m_GlyphCount;
|
2018-03-13 20:49:07 +00:00
|
|
|
Wlen = Cutter.m_CharCount;
|
|
|
|
NewLine = 1;
|
|
|
|
|
2020-10-28 14:56:02 +00:00
|
|
|
if(WordGlyphs <= 3 && GotNewLineLast == 0) // if we can't place 3 chars of the word on this line, take the next
|
2018-03-13 20:49:07 +00:00
|
|
|
Wlen = 0;
|
|
|
|
}
|
2020-10-28 14:56:02 +00:00
|
|
|
else if(Compare.m_X - pCursor->m_StartX > pCursor->m_LineWidth && GotNewLineLast == 0)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
|
|
|
NewLine = 1;
|
|
|
|
Wlen = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
pBatchEnd = pCurrent + Wlen;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *pTmp = pCurrent;
|
|
|
|
int NextCharacter = str_utf8_decode(&pTmp);
|
2020-10-13 20:08:52 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
while(pCurrent < pBatchEnd)
|
|
|
|
{
|
2021-09-12 17:40:23 +00:00
|
|
|
pCursor->m_CharCount += pTmp - pCurrent;
|
2018-03-13 20:49:07 +00:00
|
|
|
int Character = NextCharacter;
|
|
|
|
pCurrent = pTmp;
|
|
|
|
NextCharacter = str_utf8_decode(&pTmp);
|
|
|
|
|
|
|
|
if(Character == '\n')
|
|
|
|
{
|
2019-01-06 05:42:57 +00:00
|
|
|
LastCharGlyphIndex = 0;
|
|
|
|
++CharacterCounter;
|
2021-09-12 17:40:23 +00:00
|
|
|
StartNewLine();
|
2018-03-13 20:49:07 +00:00
|
|
|
if(pCursor->m_MaxLines > 0 && LineCount > pCursor->m_MaxLines)
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-08-20 09:22:25 +00:00
|
|
|
SFontSizeChar *pChr = GetChar(TextContainer.m_pFont, pSizeData, Character);
|
2018-03-13 20:49:07 +00:00
|
|
|
if(pChr)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
bool ApplyBearingX = !(((RenderFlags & TEXT_RENDER_FLAG_NO_X_BEARING) != 0) || (CharacterCounter == 0 && (RenderFlags & TEXT_RENDER_FLAG_NO_FIRST_CHARACTER_X_BEARING) != 0));
|
2021-09-12 17:40:23 +00:00
|
|
|
float Advance = ((((RenderFlags & TEXT_RENDER_FLAG_ONLY_ADVANCE_WIDTH) != 0) ? (pChr->m_Width) : (pChr->m_AdvanceX + ((!ApplyBearingX) ? (-pChr->m_OffsetX) : 0.f)))) * Scale * Size;
|
|
|
|
|
|
|
|
float OutLineRealDiff = (pChr->m_Width - pChr->m_CharWidth) * Scale * Size;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2019-01-06 05:42:57 +00:00
|
|
|
float CharKerning = 0.f;
|
2020-09-26 19:41:58 +00:00
|
|
|
if((RenderFlags & TEXT_RENDER_FLAG_KERNING) != 0)
|
|
|
|
CharKerning = Kerning(TextContainer.m_pFont, LastCharGlyphIndex, pChr->m_GlyphIndex) * Scale * Size;
|
2019-01-06 05:42:57 +00:00
|
|
|
LastCharGlyphIndex = pChr->m_GlyphIndex;
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
if(pCursor->m_Flags & TEXTFLAG_STOP_AT_END && (DrawX + CharKerning) + Advance - pCursor->m_StartX > pCursor->m_LineWidth)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
|
|
|
// we hit the end of the line, no more to render or count
|
|
|
|
pCurrent = pEnd;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
float BearingX = (!ApplyBearingX ? 0.f : pChr->m_OffsetX) * Scale * Size;
|
|
|
|
float CharWidth = pChr->m_Width * Scale * Size;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2020-10-06 10:25:10 +00:00
|
|
|
float BearingY = (((RenderFlags & TEXT_RENDER_FLAG_NO_Y_BEARING) != 0) ? 0.f : (pChr->m_OffsetY * Scale * Size));
|
|
|
|
float CharHeight = pChr->m_Height * Scale * Size;
|
|
|
|
|
|
|
|
if((RenderFlags & TEXT_RENDER_FLAG_NO_OVERSIZE) != 0)
|
|
|
|
{
|
|
|
|
if(CharHeight + BearingY > Size)
|
|
|
|
{
|
|
|
|
BearingY = 0;
|
|
|
|
float ScaleChar = (CharHeight + BearingY) / Size;
|
|
|
|
CharHeight = Size;
|
|
|
|
CharWidth /= ScaleChar;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
float TmpY = (DrawY + Size);
|
|
|
|
float CharX = (DrawX + CharKerning) + BearingX;
|
|
|
|
float CharY = TmpY - BearingY;
|
|
|
|
|
2018-07-10 09:29:02 +00:00
|
|
|
// don't add text that isn't drawn, the color overwrite is used for that
|
2021-09-12 17:40:23 +00:00
|
|
|
if(m_Color.a != 0.f && IsRendered)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2022-02-14 23:15:06 +00:00
|
|
|
TextContainer.m_StringInfo.m_CharacterQuads.emplace_back();
|
2020-09-26 19:41:58 +00:00
|
|
|
STextCharQuad &TextCharQuad = TextContainer.m_StringInfo.m_CharacterQuads.back();
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
TextCharQuad.m_Vertices[0].m_X = CharX;
|
|
|
|
TextCharQuad.m_Vertices[0].m_Y = CharY;
|
2018-03-13 20:49:07 +00:00
|
|
|
TextCharQuad.m_Vertices[0].m_U = pChr->m_aUVs[0];
|
|
|
|
TextCharQuad.m_Vertices[0].m_V = pChr->m_aUVs[3];
|
2019-04-26 22:34:20 +00:00
|
|
|
TextCharQuad.m_Vertices[0].m_Color.m_R = (unsigned char)(m_Color.r * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[0].m_Color.m_G = (unsigned char)(m_Color.g * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[0].m_Color.m_B = (unsigned char)(m_Color.b * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[0].m_Color.m_A = (unsigned char)(m_Color.a * 255.f);
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
TextCharQuad.m_Vertices[1].m_X = CharX + CharWidth;
|
|
|
|
TextCharQuad.m_Vertices[1].m_Y = CharY;
|
2018-03-13 20:49:07 +00:00
|
|
|
TextCharQuad.m_Vertices[1].m_U = pChr->m_aUVs[2];
|
|
|
|
TextCharQuad.m_Vertices[1].m_V = pChr->m_aUVs[3];
|
2019-04-26 22:34:20 +00:00
|
|
|
TextCharQuad.m_Vertices[1].m_Color.m_R = (unsigned char)(m_Color.r * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[1].m_Color.m_G = (unsigned char)(m_Color.g * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[1].m_Color.m_B = (unsigned char)(m_Color.b * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[1].m_Color.m_A = (unsigned char)(m_Color.a * 255.f);
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
TextCharQuad.m_Vertices[2].m_X = CharX + CharWidth;
|
|
|
|
TextCharQuad.m_Vertices[2].m_Y = CharY - CharHeight;
|
2018-03-13 20:49:07 +00:00
|
|
|
TextCharQuad.m_Vertices[2].m_U = pChr->m_aUVs[2];
|
|
|
|
TextCharQuad.m_Vertices[2].m_V = pChr->m_aUVs[1];
|
2019-04-26 22:34:20 +00:00
|
|
|
TextCharQuad.m_Vertices[2].m_Color.m_R = (unsigned char)(m_Color.r * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[2].m_Color.m_G = (unsigned char)(m_Color.g * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[2].m_Color.m_B = (unsigned char)(m_Color.b * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[2].m_Color.m_A = (unsigned char)(m_Color.a * 255.f);
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
TextCharQuad.m_Vertices[3].m_X = CharX;
|
|
|
|
TextCharQuad.m_Vertices[3].m_Y = CharY - CharHeight;
|
2018-03-13 20:49:07 +00:00
|
|
|
TextCharQuad.m_Vertices[3].m_U = pChr->m_aUVs[0];
|
|
|
|
TextCharQuad.m_Vertices[3].m_V = pChr->m_aUVs[1];
|
2019-04-26 22:34:20 +00:00
|
|
|
TextCharQuad.m_Vertices[3].m_Color.m_R = (unsigned char)(m_Color.r * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[3].m_Color.m_G = (unsigned char)(m_Color.g * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[3].m_Color.m_B = (unsigned char)(m_Color.b * 255.f);
|
|
|
|
TextCharQuad.m_Vertices[3].m_Color.m_A = (unsigned char)(m_Color.a * 255.f);
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
// calculate the full width from the last selection point to the end of this selection draw on screen
|
|
|
|
float SelWidth = (CharX + maximum(Advance, CharWidth - OutLineRealDiff / 2)) - (LastSelX + LastSelWidth);
|
|
|
|
float SelX = (LastSelX + LastSelWidth);
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
if(pCursor->m_CursorMode == TEXT_CURSOR_CURSOR_MODE_CALCULATE)
|
|
|
|
{
|
|
|
|
if(pCursor->m_CursorCharacter == -1 && CheckInsideChar(CharacterCounter == 0, pCursor->m_ReleaseMouseX, pCursor->m_ReleaseMouseY, CharacterCounter == 0 ? std::numeric_limits<float>::lowest() : LastCharX, LastCharWidth, CharX, CharWidth, TmpY))
|
|
|
|
{
|
|
|
|
pCursor->m_CursorCharacter = CharacterCounter;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
if(pCursor->m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_CALCULATE)
|
|
|
|
{
|
|
|
|
if(CharacterCounter == 0)
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
CheckSelectionStart(true, pCursor->m_PressMouseX, pCursor->m_PressMouseY, SelectionStartChar, SelectionUsedPress, std::numeric_limits<float>::lowest(), 0, CharX, CharWidth, TmpY);
|
|
|
|
CheckSelectionStart(true, pCursor->m_ReleaseMouseX, pCursor->m_ReleaseMouseY, SelectionEndChar, SelectionUsedRelease, std::numeric_limits<float>::lowest(), 0, CharX, CharWidth, TmpY);
|
2021-09-12 17:40:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// if selection didn't start and the mouse pos is atleast on 50% of the right side of the character start
|
|
|
|
CheckSelectionStart(false, pCursor->m_PressMouseX, pCursor->m_PressMouseY, SelectionStartChar, SelectionUsedPress, LastCharX, LastCharWidth, CharX, CharWidth, TmpY);
|
|
|
|
CheckSelectionStart(false, pCursor->m_ReleaseMouseX, pCursor->m_ReleaseMouseY, SelectionEndChar, SelectionUsedRelease, LastCharX, LastCharWidth, CharX, CharWidth, TmpY);
|
|
|
|
CheckSelectionEnd(false, pCursor->m_ReleaseMouseX, pCursor->m_ReleaseMouseY, SelectionEndChar, SelectionUsedRelease, CharX, CharWidth, TmpY);
|
|
|
|
CheckSelectionEnd(false, pCursor->m_PressMouseX, pCursor->m_PressMouseY, SelectionStartChar, SelectionUsedPress, CharX, CharWidth, TmpY);
|
|
|
|
}
|
|
|
|
if(pCursor->m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_SET)
|
|
|
|
{
|
|
|
|
if((int)CharacterCounter == pCursor->m_SelectionStart)
|
|
|
|
{
|
|
|
|
SelectionStarted = !SelectionStarted;
|
2021-09-16 14:50:17 +00:00
|
|
|
SelectionStartChar = CharacterCounter;
|
2021-09-12 17:40:23 +00:00
|
|
|
SelectionUsedPress = true;
|
|
|
|
}
|
|
|
|
if((int)CharacterCounter == pCursor->m_SelectionEnd)
|
|
|
|
{
|
|
|
|
SelectionStarted = !SelectionStarted;
|
2021-09-16 14:50:17 +00:00
|
|
|
SelectionEndChar = CharacterCounter;
|
2021-09-12 17:40:23 +00:00
|
|
|
SelectionUsedRelease = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
if(pCursor->m_CursorMode != TEXT_CURSOR_CURSOR_MODE_NONE)
|
|
|
|
{
|
|
|
|
if((int)CharacterCounter == pCursor->m_CursorCharacter)
|
|
|
|
{
|
|
|
|
HasCursor = true;
|
|
|
|
CursorQuads[0] = IGraphics::CQuadItem(SelX - CursorOuterInnerDiff, DrawY, CursorOuterWidth, Size);
|
|
|
|
CursorQuads[1] = IGraphics::CQuadItem(SelX, DrawY + CursorOuterInnerDiff, CursorInnerWidth, Size - CursorOuterInnerDiff * 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-06 10:25:10 +00:00
|
|
|
pCursor->m_MaxCharacterHeight = maximum(pCursor->m_MaxCharacterHeight, CharHeight + BearingY);
|
|
|
|
|
2020-10-16 18:00:57 +00:00
|
|
|
if(NextCharacter == 0 && (RenderFlags & TEXT_RENDER_FLAG_NO_LAST_CHARACTER_ADVANCE) != 0 && Character != ' ')
|
2019-01-06 05:42:57 +00:00
|
|
|
DrawX += BearingX + CharKerning + CharWidth;
|
|
|
|
else
|
2021-09-12 17:40:23 +00:00
|
|
|
DrawX += Advance + CharKerning;
|
|
|
|
|
2020-09-03 22:53:26 +00:00
|
|
|
pCursor->m_GlyphCount++;
|
2019-01-06 05:42:57 +00:00
|
|
|
++CharacterCounter;
|
2021-09-12 17:40:23 +00:00
|
|
|
|
|
|
|
if(SelectionStarted && IsRendered)
|
|
|
|
{
|
2022-02-14 23:15:06 +00:00
|
|
|
SelectionQuads.emplace_back(SelX, DrawY, SelWidth, Size);
|
2021-09-12 17:40:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
LastSelX = SelX;
|
|
|
|
LastSelWidth = SelWidth;
|
|
|
|
LastCharX = CharX;
|
|
|
|
LastCharWidth = CharWidth;
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
2020-10-13 20:08:52 +00:00
|
|
|
|
|
|
|
if(DrawX > pCursor->m_LongestLineWidth)
|
|
|
|
pCursor->m_LongestLineWidth = DrawX;
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(NewLine)
|
|
|
|
{
|
2021-09-12 17:40:23 +00:00
|
|
|
StartNewLine();
|
2018-03-13 20:49:07 +00:00
|
|
|
GotNewLine = 1;
|
2020-10-28 14:56:02 +00:00
|
|
|
GotNewLineLast = 1;
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
2020-10-28 14:56:02 +00:00
|
|
|
else
|
|
|
|
GotNewLineLast = 0;
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
2022-01-22 12:54:25 +00:00
|
|
|
if(!TextContainer.m_StringInfo.m_CharacterQuads.empty() && IsRendered)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
|
|
|
TextContainer.m_StringInfo.m_QuadNum = TextContainer.m_StringInfo.m_CharacterQuads.size();
|
|
|
|
// setup the buffers
|
2020-08-29 10:10:38 +00:00
|
|
|
if(Graphics()->IsTextBufferingEnabled())
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
|
|
|
size_t DataSize = TextContainer.m_StringInfo.m_CharacterQuads.size() * sizeof(STextCharQuad);
|
|
|
|
void *pUploadData = &TextContainer.m_StringInfo.m_CharacterQuads[0];
|
|
|
|
|
2020-10-22 20:21:19 +00:00
|
|
|
if(TextContainer.m_StringInfo.m_QuadBufferObjectIndex != -1 && (TextContainer.m_RenderFlags & TEXT_RENDER_FLAG_NO_AUTOMATIC_QUAD_UPLOAD) == 0)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
|
|
|
Graphics()->RecreateBufferObject(TextContainer.m_StringInfo.m_QuadBufferObjectIndex, DataSize, pUploadData);
|
|
|
|
Graphics()->IndicesNumRequiredNotify(TextContainer.m_StringInfo.m_QuadNum * 6);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
if(pCursor->m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_CALCULATE)
|
|
|
|
{
|
|
|
|
pCursor->m_SelectionStart = -1;
|
|
|
|
pCursor->m_SelectionEnd = -1;
|
|
|
|
|
|
|
|
if(SelectionStarted)
|
|
|
|
{
|
|
|
|
CheckSelectionEnd(true, pCursor->m_ReleaseMouseX, pCursor->m_ReleaseMouseY, SelectionEndChar, SelectionUsedRelease, std::numeric_limits<float>::max(), 0, DrawY + Size);
|
|
|
|
CheckSelectionEnd(true, pCursor->m_PressMouseX, pCursor->m_PressMouseY, SelectionStartChar, SelectionUsedPress, std::numeric_limits<float>::max(), 0, DrawY + Size);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(pCursor->m_CalculateSelectionMode == TEXT_CURSOR_SELECTION_MODE_SET)
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
if((int)CharacterCounter == pCursor->m_SelectionStart)
|
|
|
|
{
|
|
|
|
SelectionStarted = !SelectionStarted;
|
|
|
|
SelectionStartChar = CharacterCounter;
|
|
|
|
SelectionUsedPress = true;
|
|
|
|
}
|
2021-09-12 17:40:23 +00:00
|
|
|
if((int)CharacterCounter == pCursor->m_SelectionEnd)
|
|
|
|
{
|
|
|
|
SelectionStarted = !SelectionStarted;
|
2021-09-16 14:50:17 +00:00
|
|
|
SelectionEndChar = CharacterCounter;
|
2021-09-12 17:40:23 +00:00
|
|
|
SelectionUsedRelease = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
if(pCursor->m_CursorMode != TEXT_CURSOR_CURSOR_MODE_NONE)
|
2021-09-12 17:40:23 +00:00
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
if(pCursor->m_CursorMode == TEXT_CURSOR_CURSOR_MODE_CALCULATE && pCursor->m_CursorCharacter == -1 && CheckOutsideChar(true, pCursor->m_ReleaseMouseX, pCursor->m_ReleaseMouseY, std::numeric_limits<float>::max(), 0, DrawY + Size))
|
2021-09-12 17:40:23 +00:00
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
pCursor->m_CursorCharacter = CharacterCounter;
|
|
|
|
}
|
2021-09-12 17:40:23 +00:00
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
if((int)CharacterCounter == pCursor->m_CursorCharacter)
|
|
|
|
{
|
|
|
|
HasCursor = true;
|
|
|
|
CursorQuads[0] = IGraphics::CQuadItem((LastSelX + LastSelWidth) - CursorOuterInnerDiff, DrawY, CursorOuterWidth, Size);
|
|
|
|
CursorQuads[1] = IGraphics::CQuadItem((LastSelX + LastSelWidth), DrawY + CursorOuterInnerDiff, CursorInnerWidth, Size - CursorOuterInnerDiff * 2);
|
2021-09-12 17:40:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
bool HasSelection = !SelectionQuads.empty() && SelectionUsedPress && SelectionUsedRelease;
|
|
|
|
if((HasSelection || HasCursor) && IsRendered)
|
|
|
|
{
|
|
|
|
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
|
|
|
|
if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex == -1)
|
|
|
|
TextContainer.m_StringInfo.m_SelectionQuadContainerIndex = Graphics()->CreateQuadContainer(false);
|
|
|
|
if(HasCursor)
|
|
|
|
Graphics()->QuadContainerAddQuads(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, CursorQuads, 2);
|
|
|
|
if(HasSelection)
|
|
|
|
Graphics()->QuadContainerAddQuads(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, &SelectionQuads[0], (int)SelectionQuads.size());
|
|
|
|
Graphics()->QuadContainerUpload(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex);
|
|
|
|
|
|
|
|
TextContainer.m_HasCursor = HasCursor;
|
|
|
|
TextContainer.m_HasSelection = HasSelection;
|
|
|
|
|
2021-11-02 23:08:00 +00:00
|
|
|
if(HasSelection)
|
|
|
|
{
|
|
|
|
pCursor->m_SelectionStart = SelectionStartChar;
|
|
|
|
pCursor->m_SelectionEnd = SelectionEndChar;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
pCursor->m_SelectionStart = -1;
|
|
|
|
pCursor->m_SelectionEnd = -1;
|
|
|
|
}
|
2021-09-16 14:50:17 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
// even if no text is drawn the cursor position will be adjusted
|
|
|
|
pCursor->m_X = DrawX;
|
2010-10-11 00:29:30 +00:00
|
|
|
pCursor->m_LineCount = LineCount;
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2010-05-29 07:25:38 +00:00
|
|
|
if(GotNewLine)
|
|
|
|
pCursor->m_Y = DrawY;
|
|
|
|
}
|
2011-04-13 18:37:12 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
// just deletes and creates text container
|
2021-09-16 14:50:17 +00:00
|
|
|
virtual void
|
|
|
|
RecreateTextContainer(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
|
|
|
DeleteTextContainer(TextContainerIndex);
|
2020-10-12 10:29:47 +00:00
|
|
|
CreateTextContainer(pCursor, pText, Length);
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
2020-10-12 10:29:47 +00:00
|
|
|
virtual void RecreateTextContainerSoft(CTextCursor *pCursor, int TextContainerIndex, const char *pText, int Length = -1)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
STextContainer &TextContainer = GetTextContainer(TextContainerIndex);
|
2018-03-13 20:49:07 +00:00
|
|
|
TextContainer.m_StringInfo.m_CharacterQuads.clear();
|
|
|
|
TextContainer.m_StringInfo.m_QuadNum = 0;
|
|
|
|
// the text buffer gets then recreated by the appended quads
|
2020-10-12 10:29:47 +00:00
|
|
|
AppendTextContainer(pCursor, TextContainerIndex, pText, Length);
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
virtual void DeleteTextContainer(int TextContainerIndex)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
STextContainer &TextContainer = GetTextContainer(TextContainerIndex);
|
2020-08-29 10:10:38 +00:00
|
|
|
if(Graphics()->IsTextBufferingEnabled())
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
|
|
|
if(TextContainer.m_StringInfo.m_QuadBufferContainerIndex != -1)
|
|
|
|
Graphics()->DeleteBufferContainer(TextContainer.m_StringInfo.m_QuadBufferContainerIndex, true);
|
|
|
|
}
|
2021-09-12 17:40:23 +00:00
|
|
|
if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex != -1)
|
|
|
|
Graphics()->DeleteQuadContainer(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex);
|
2018-03-13 20:49:07 +00:00
|
|
|
FreeTextContainer(TextContainerIndex);
|
|
|
|
}
|
|
|
|
|
2020-10-22 20:21:19 +00:00
|
|
|
virtual void UploadTextContainer(int TextContainerIndex)
|
|
|
|
{
|
2021-09-24 23:57:54 +00:00
|
|
|
if(Graphics()->IsTextBufferingEnabled())
|
|
|
|
{
|
|
|
|
STextContainer &TextContainer = GetTextContainer(TextContainerIndex);
|
|
|
|
size_t DataSize = TextContainer.m_StringInfo.m_CharacterQuads.size() * sizeof(STextCharQuad);
|
|
|
|
void *pUploadData = &TextContainer.m_StringInfo.m_CharacterQuads[0];
|
|
|
|
TextContainer.m_StringInfo.m_QuadBufferObjectIndex = Graphics()->CreateBufferObject(DataSize, pUploadData);
|
2020-10-22 20:21:19 +00:00
|
|
|
|
2021-09-24 23:57:54 +00:00
|
|
|
for(auto &Attribute : m_DefaultTextContainerInfo.m_Attributes)
|
|
|
|
Attribute.m_VertBufferBindingIndex = TextContainer.m_StringInfo.m_QuadBufferObjectIndex;
|
2020-10-22 20:21:19 +00:00
|
|
|
|
2021-09-24 23:57:54 +00:00
|
|
|
TextContainer.m_StringInfo.m_QuadBufferContainerIndex = Graphics()->CreateBufferContainer(&m_DefaultTextContainerInfo);
|
|
|
|
Graphics()->IndicesNumRequiredNotify(TextContainer.m_StringInfo.m_QuadNum * 6);
|
|
|
|
}
|
2020-10-22 20:21:19 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
virtual void RenderTextContainer(int TextContainerIndex, STextRenderColor *pTextColor, STextRenderColor *pTextOutlineColor)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
STextContainer &TextContainer = GetTextContainer(TextContainerIndex);
|
2018-03-13 20:49:07 +00:00
|
|
|
CFont *pFont = TextContainer.m_pFont;
|
|
|
|
|
|
|
|
if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex != -1)
|
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
if(TextContainer.m_HasSelection)
|
|
|
|
{
|
|
|
|
Graphics()->TextureClear();
|
|
|
|
Graphics()->SetColor(m_SelectionColor);
|
|
|
|
Graphics()->RenderQuadContainerEx(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, TextContainer.m_HasCursor ? 2 : 0, -1, 0, 0);
|
|
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
}
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
if(TextContainer.m_StringInfo.m_QuadNum > 0)
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
if(Graphics()->IsTextBufferingEnabled())
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
Graphics()->TextureClear();
|
|
|
|
// render buffered text
|
|
|
|
Graphics()->RenderText(TextContainer.m_StringInfo.m_QuadBufferContainerIndex, TextContainer.m_StringInfo.m_QuadNum, pFont->m_CurTextureDimensions[0], pFont->m_aTextures[0].Id(), pFont->m_aTextures[1].Id(), (float *)pTextColor, (float *)pTextOutlineColor);
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
2021-09-16 14:50:17 +00:00
|
|
|
else
|
2018-03-13 20:49:07 +00:00
|
|
|
{
|
2021-09-16 14:50:17 +00:00
|
|
|
// render tiles
|
|
|
|
float UVScale = 1.0f / pFont->m_CurTextureDimensions[0];
|
2018-05-07 03:52:02 +00:00
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
Graphics()->FlushVertices();
|
|
|
|
Graphics()->TextureSet(pFont->m_aTextures[1]);
|
|
|
|
|
|
|
|
Graphics()->QuadsBegin();
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-04-03 15:40:21 +00:00
|
|
|
for(size_t i = 0; i < TextContainer.m_StringInfo.m_QuadNum; ++i)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
STextCharQuad &TextCharQuad = TextContainer.m_StringInfo.m_CharacterQuads[i];
|
2021-09-16 14:50:17 +00:00
|
|
|
|
|
|
|
Graphics()->SetColor(TextCharQuad.m_Vertices[0].m_Color.m_R / 255.f * pTextOutlineColor->m_R, TextCharQuad.m_Vertices[0].m_Color.m_G / 255.f * pTextOutlineColor->m_G, TextCharQuad.m_Vertices[0].m_Color.m_B / 255.f * pTextOutlineColor->m_B, TextCharQuad.m_Vertices[0].m_Color.m_A / 255.f * pTextOutlineColor->m_A);
|
|
|
|
|
|
|
|
Graphics()->QuadsSetSubset(TextCharQuad.m_Vertices[0].m_U * UVScale, TextCharQuad.m_Vertices[0].m_V * UVScale, TextCharQuad.m_Vertices[2].m_U * UVScale, TextCharQuad.m_Vertices[2].m_V * UVScale);
|
|
|
|
IGraphics::CQuadItem QuadItem(TextCharQuad.m_Vertices[0].m_X, TextCharQuad.m_Vertices[0].m_Y, TextCharQuad.m_Vertices[1].m_X - TextCharQuad.m_Vertices[0].m_X, TextCharQuad.m_Vertices[2].m_Y - TextCharQuad.m_Vertices[0].m_Y);
|
|
|
|
Graphics()->QuadsDrawTL(&QuadItem, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pTextColor->m_A != 0)
|
|
|
|
{
|
|
|
|
Graphics()->QuadsEndKeepVertices();
|
|
|
|
|
|
|
|
Graphics()->TextureSet(pFont->m_aTextures[0]);
|
|
|
|
|
|
|
|
for(size_t i = 0; i < TextContainer.m_StringInfo.m_QuadNum; ++i)
|
|
|
|
{
|
|
|
|
STextCharQuad &TextCharQuad = TextContainer.m_StringInfo.m_CharacterQuads[i];
|
|
|
|
unsigned char CR = (unsigned char)((float)(TextCharQuad.m_Vertices[0].m_Color.m_R) * pTextColor->m_R);
|
|
|
|
unsigned char CG = (unsigned char)((float)(TextCharQuad.m_Vertices[0].m_Color.m_G) * pTextColor->m_G);
|
|
|
|
unsigned char CB = (unsigned char)((float)(TextCharQuad.m_Vertices[0].m_Color.m_B) * pTextColor->m_B);
|
|
|
|
unsigned char CA = (unsigned char)((float)(TextCharQuad.m_Vertices[0].m_Color.m_A) * pTextColor->m_A);
|
|
|
|
Graphics()->ChangeColorOfQuadVertices((int)i, CR, CG, CB, CA);
|
|
|
|
}
|
|
|
|
|
|
|
|
// render non outlined
|
|
|
|
Graphics()->QuadsDrawCurrentVertices(false);
|
2018-04-03 15:40:21 +00:00
|
|
|
}
|
2021-09-16 14:50:17 +00:00
|
|
|
else
|
|
|
|
Graphics()->QuadsEnd();
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
// reset
|
|
|
|
Graphics()->SetColor(1.f, 1.f, 1.f, 1.f);
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
2021-09-16 14:50:17 +00:00
|
|
|
}
|
2018-03-13 20:49:07 +00:00
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
if(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex != -1)
|
|
|
|
{
|
|
|
|
if(TextContainer.m_HasCursor)
|
|
|
|
{
|
|
|
|
int64_t CurTime = time_get_microseconds();
|
|
|
|
|
|
|
|
Graphics()->TextureClear();
|
|
|
|
if((CurTime - m_CursorRenderTime) > 500000)
|
|
|
|
{
|
|
|
|
Graphics()->SetColor(*pTextOutlineColor);
|
|
|
|
Graphics()->RenderQuadContainerEx(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, 0, 1, 0, 0);
|
|
|
|
Graphics()->SetColor(*pTextColor);
|
|
|
|
Graphics()->RenderQuadContainerEx(TextContainer.m_StringInfo.m_SelectionQuadContainerIndex, 1, 1, 0, 0);
|
|
|
|
}
|
|
|
|
if((CurTime - m_CursorRenderTime) > 1000000)
|
|
|
|
m_CursorRenderTime = time_get_microseconds();
|
|
|
|
Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
|
|
|
|
}
|
2018-03-13 20:49:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual void RenderTextContainer(int TextContainerIndex, STextRenderColor *pTextColor, STextRenderColor *pTextOutlineColor, float X, float Y)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
STextContainer &TextContainer = GetTextContainer(TextContainerIndex);
|
2018-03-21 14:43:56 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
// remap the current screen, after render revert the change again
|
|
|
|
float ScreenX0, ScreenY0, ScreenX1, ScreenY1;
|
2019-04-26 19:36:49 +00:00
|
|
|
Graphics()->GetScreen(&ScreenX0, &ScreenY0, &ScreenX1, &ScreenY1);
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if((TextContainer.m_RenderFlags & TEXT_RENDER_FLAG_NO_PIXEL_ALIGMENT) == 0)
|
2018-03-21 14:43:56 +00:00
|
|
|
{
|
|
|
|
float FakeToScreenX = (Graphics()->ScreenWidth() / (ScreenX1 - ScreenX0));
|
|
|
|
float FakeToScreenY = (Graphics()->ScreenHeight() / (ScreenY1 - ScreenY0));
|
2021-05-02 08:32:05 +00:00
|
|
|
int ActualX = (int)(((TextContainer.m_X + X) * FakeToScreenX) + 0.5f);
|
|
|
|
int ActualY = (int)(((TextContainer.m_Y + Y) * FakeToScreenY) + 0.5f);
|
2018-03-21 14:43:56 +00:00
|
|
|
float AlignedX = ActualX / FakeToScreenX;
|
|
|
|
float AlignedY = ActualY / FakeToScreenY;
|
|
|
|
X = AlignedX - TextContainer.m_AlignedStartX;
|
|
|
|
Y = AlignedY - TextContainer.m_AlignedStartY;
|
|
|
|
}
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2018-03-13 20:49:07 +00:00
|
|
|
Graphics()->MapScreen(ScreenX0 - X, ScreenY0 - Y, ScreenX1 - X, ScreenY1 - Y);
|
|
|
|
RenderTextContainer(TextContainerIndex, pTextColor, pTextOutlineColor);
|
|
|
|
Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
|
|
|
|
}
|
|
|
|
|
2020-09-14 14:39:05 +00:00
|
|
|
virtual void UploadEntityLayerText(void *pTexBuff, int ImageColorChannelCount, int TexWidth, int TexHeight, int TexSubWidth, int TexSubHeight, const char *pText, int Length, float x, float y, int FontSize)
|
2017-09-12 18:10:27 +00:00
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
if(FontSize < 1)
|
2019-05-11 15:59:47 +00:00
|
|
|
return;
|
|
|
|
|
2019-05-06 12:19:10 +00:00
|
|
|
const char *pCurrent = (char *)pText;
|
|
|
|
const char *pEnd = pCurrent + Length;
|
2017-09-12 18:10:27 +00:00
|
|
|
CFont *pFont = m_pDefaultFont;
|
|
|
|
FT_Bitmap *pBitmap;
|
|
|
|
int WidthLastChars = 0;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2017-09-27 10:19:39 +00:00
|
|
|
while(pCurrent < pEnd)
|
2019-04-26 19:36:49 +00:00
|
|
|
{
|
2017-09-12 18:10:27 +00:00
|
|
|
const char *pTmp = pCurrent;
|
|
|
|
int NextCharacter = str_utf8_decode(&pTmp);
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2017-09-13 18:33:58 +00:00
|
|
|
if(NextCharacter)
|
|
|
|
{
|
2017-09-12 18:10:27 +00:00
|
|
|
unsigned int px, py;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2019-05-11 15:59:47 +00:00
|
|
|
FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, FontSize);
|
2017-09-12 18:10:27 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(FT_Load_Char(pFont->m_FtFace, NextCharacter, FT_LOAD_RENDER | FT_LOAD_NO_BITMAP))
|
2017-09-12 18:10:27 +00:00
|
|
|
{
|
2020-10-26 08:57:41 +00:00
|
|
|
dbg_msg("textrender", "error loading glyph %d", NextCharacter);
|
2017-09-12 18:10:27 +00:00
|
|
|
pCurrent = pTmp;
|
|
|
|
continue;
|
|
|
|
}
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2017-09-12 18:10:27 +00:00
|
|
|
pBitmap = &pFont->m_FtFace->glyph->bitmap; // ignore_convention
|
2020-03-20 12:48:45 +00:00
|
|
|
|
2019-05-06 12:19:10 +00:00
|
|
|
int SlotW = pBitmap->width;
|
|
|
|
int SlotH = pBitmap->rows;
|
2020-09-26 19:41:58 +00:00
|
|
|
int SlotSize = SlotW * SlotH;
|
2020-03-20 12:48:45 +00:00
|
|
|
|
2019-05-06 12:19:10 +00:00
|
|
|
// prepare glyph data
|
|
|
|
mem_zero(ms_aGlyphData, SlotSize);
|
|
|
|
|
|
|
|
if(pBitmap->pixel_mode == FT_PIXEL_MODE_GRAY) // ignore_convention
|
2020-08-29 10:10:38 +00:00
|
|
|
{
|
|
|
|
for(py = 0; py < (unsigned)SlotH; py++) // ignore_convention
|
|
|
|
for(px = 0; px < (unsigned)SlotW; px++)
|
|
|
|
{
|
2020-09-26 19:41:58 +00:00
|
|
|
ms_aGlyphData[(py)*SlotW + px] = pBitmap->buffer[py * pBitmap->width + px]; // ignore_convention
|
2020-08-29 10:10:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
uint8_t *pImageBuff = (uint8_t *)pTexBuff;
|
2020-08-29 10:10:38 +00:00
|
|
|
for(int OffY = 0; OffY < SlotH; ++OffY)
|
|
|
|
{
|
|
|
|
for(int OffX = 0; OffX < SlotW; ++OffX)
|
2017-09-15 01:01:26 +00:00
|
|
|
{
|
2020-09-14 14:39:05 +00:00
|
|
|
int ImgOffX = clamp(x + OffX + WidthLastChars, x, (x + TexSubWidth) - 1);
|
|
|
|
int ImgOffY = clamp(y + OffY, y, (y + TexSubHeight) - 1);
|
|
|
|
size_t ImageOffset = ImgOffY * (TexWidth * ImageColorChannelCount) + ImgOffX * ImageColorChannelCount;
|
|
|
|
size_t GlyphOffset = (OffY)*SlotW + OffX;
|
2020-08-29 10:10:38 +00:00
|
|
|
for(size_t i = 0; i < (size_t)ImageColorChannelCount; ++i)
|
|
|
|
{
|
|
|
|
if(i != (size_t)ImageColorChannelCount - 1)
|
|
|
|
{
|
|
|
|
*(pImageBuff + ImageOffset + i) = 255;
|
|
|
|
}
|
|
|
|
else
|
2017-09-15 01:01:26 +00:00
|
|
|
{
|
2020-08-29 10:10:38 +00:00
|
|
|
*(pImageBuff + ImageOffset + i) = *(ms_aGlyphData + GlyphOffset);
|
2017-09-15 01:01:26 +00:00
|
|
|
}
|
2020-08-29 10:10:38 +00:00
|
|
|
}
|
2017-09-15 01:01:26 +00:00
|
|
|
}
|
2020-08-29 10:10:38 +00:00
|
|
|
}
|
2020-03-20 12:48:45 +00:00
|
|
|
|
2019-05-06 12:19:10 +00:00
|
|
|
WidthLastChars += (SlotW + 1);
|
|
|
|
}
|
|
|
|
pCurrent = pTmp;
|
|
|
|
}
|
|
|
|
}
|
2020-03-20 12:48:45 +00:00
|
|
|
|
2020-08-29 10:10:38 +00:00
|
|
|
virtual int AdjustFontSize(const char *pText, int TextLength, int MaxSize, int MaxWidth)
|
2019-05-06 12:19:10 +00:00
|
|
|
{
|
|
|
|
int WidthOfText = CalculateTextWidth(pText, TextLength, 0, 100);
|
|
|
|
|
2020-08-29 10:10:38 +00:00
|
|
|
int FontSize = 100.f / ((float)WidthOfText / (float)MaxWidth);
|
2019-05-06 12:19:10 +00:00
|
|
|
|
2020-09-26 19:41:58 +00:00
|
|
|
if(MaxSize > 0 && FontSize > MaxSize)
|
2019-05-06 12:19:10 +00:00
|
|
|
FontSize = MaxSize;
|
|
|
|
|
|
|
|
return FontSize;
|
|
|
|
}
|
|
|
|
|
2020-10-16 18:00:57 +00:00
|
|
|
virtual float GetGlyphOffsetX(int FontSize, char TextCharacter)
|
|
|
|
{
|
|
|
|
CFont *pFont = m_pDefaultFont;
|
|
|
|
FT_Set_Pixel_Sizes(pFont->m_FtFace, 0, FontSize);
|
|
|
|
const char *pTmp = &TextCharacter;
|
|
|
|
int NextCharacter = str_utf8_decode(&pTmp);
|
|
|
|
|
|
|
|
if(NextCharacter)
|
|
|
|
{
|
|
|
|
FT_Int32 FTFlags = 0;
|
|
|
|
#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 7 && (FREETYPE_MINOR > 7 || FREETYPE_PATCH >= 1)
|
|
|
|
FTFlags = FT_LOAD_BITMAP_METRICS_ONLY | FT_LOAD_NO_BITMAP;
|
|
|
|
#else
|
|
|
|
FTFlags = FT_LOAD_RENDER | FT_LOAD_NO_BITMAP;
|
|
|
|
#endif
|
|
|
|
if(FT_Load_Char(pFont->m_FtFace, NextCharacter, FTFlags))
|
|
|
|
{
|
2020-10-26 08:57:41 +00:00
|
|
|
dbg_msg("textrender", "error loading glyph %d in GetGlyphOffsetX", NextCharacter);
|
2020-10-16 18:00:57 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (float)(pFont->m_FtFace->glyph->metrics.horiBearingX >> 6);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-05-06 12:19:10 +00:00
|
|
|
virtual int CalculateTextWidth(const char *pText, int TextLength, int FontWidth, int FontHeight)
|
|
|
|
{
|
|
|
|
CFont *pFont = m_pDefaultFont;
|
|
|
|
const char *pCurrent = (char *)pText;
|
|
|
|
const char *pEnd = pCurrent + TextLength;
|
2019-04-26 19:36:49 +00:00
|
|
|
|
2019-05-06 12:19:10 +00:00
|
|
|
int WidthOfText = 0;
|
|
|
|
FT_Set_Pixel_Sizes(pFont->m_FtFace, FontWidth, FontHeight);
|
2020-09-26 19:41:58 +00:00
|
|
|
while(pCurrent < pEnd)
|
2019-05-06 12:19:10 +00:00
|
|
|
{
|
|
|
|
const char *pTmp = pCurrent;
|
|
|
|
int NextCharacter = str_utf8_decode(&pTmp);
|
|
|
|
|
|
|
|
if(NextCharacter)
|
|
|
|
{
|
|
|
|
FT_Int32 FTFlags = 0;
|
|
|
|
#if FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 7 && (FREETYPE_MINOR > 7 || FREETYPE_PATCH >= 1)
|
|
|
|
FTFlags = FT_LOAD_BITMAP_METRICS_ONLY | FT_LOAD_NO_BITMAP;
|
|
|
|
#else
|
|
|
|
FTFlags = FT_LOAD_RENDER | FT_LOAD_NO_BITMAP;
|
|
|
|
#endif
|
|
|
|
if(FT_Load_Char(pFont->m_FtFace, NextCharacter, FTFlags))
|
|
|
|
{
|
2020-10-26 08:57:41 +00:00
|
|
|
dbg_msg("textrender", "error loading glyph %d in CalculateTextWidth", NextCharacter);
|
2019-05-06 12:19:10 +00:00
|
|
|
pCurrent = pTmp;
|
|
|
|
continue;
|
2017-09-12 18:10:27 +00:00
|
|
|
}
|
2019-05-06 12:19:10 +00:00
|
|
|
|
|
|
|
WidthOfText += (pFont->m_FtFace->glyph->metrics.width >> 6) + 1;
|
2017-09-12 18:10:27 +00:00
|
|
|
}
|
|
|
|
pCurrent = pTmp;
|
|
|
|
}
|
2018-03-21 14:43:56 +00:00
|
|
|
|
2019-05-06 12:19:10 +00:00
|
|
|
return WidthOfText;
|
|
|
|
}
|
2020-03-20 12:48:45 +00:00
|
|
|
|
2021-09-12 17:40:23 +00:00
|
|
|
virtual bool SelectionToUTF8OffSets(const char *pText, int SelStart, int SelEnd, int &OffUTF8Start, int &OffUTF8End)
|
|
|
|
{
|
|
|
|
const char *pIt = pText;
|
|
|
|
|
|
|
|
OffUTF8Start = -1;
|
|
|
|
OffUTF8End = -1;
|
|
|
|
|
|
|
|
int CharCount = 0;
|
|
|
|
while(*pIt)
|
|
|
|
{
|
|
|
|
const char *pTmp = pIt;
|
|
|
|
int Character = str_utf8_decode(&pTmp);
|
|
|
|
if(Character == -1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if(CharCount == SelStart)
|
|
|
|
OffUTF8Start = (int)((std::intptr_t)(pIt - pText));
|
|
|
|
|
|
|
|
if(CharCount == SelEnd)
|
|
|
|
OffUTF8End = (int)((std::intptr_t)(pIt - pText));
|
|
|
|
|
|
|
|
pIt = pTmp;
|
|
|
|
++CharCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(CharCount == SelStart)
|
|
|
|
OffUTF8Start = (int)((std::intptr_t)(pIt - pText));
|
|
|
|
|
|
|
|
if(CharCount == SelEnd)
|
|
|
|
OffUTF8End = (int)((std::intptr_t)(pIt - pText));
|
|
|
|
|
|
|
|
return OffUTF8Start != -1 && OffUTF8End != -1;
|
|
|
|
}
|
|
|
|
|
2021-09-16 14:50:17 +00:00
|
|
|
virtual bool UTF8OffToDecodedOff(const char *pText, int UTF8Off, int &DecodedOff)
|
|
|
|
{
|
|
|
|
const char *pIt = pText;
|
|
|
|
|
|
|
|
DecodedOff = -1;
|
|
|
|
|
|
|
|
int CharCount = 0;
|
|
|
|
while(*pIt)
|
|
|
|
{
|
|
|
|
if((int)(intptr_t)(pIt - pText) == UTF8Off)
|
|
|
|
{
|
|
|
|
DecodedOff = CharCount;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *pTmp = pIt;
|
|
|
|
int Character = str_utf8_decode(&pTmp);
|
|
|
|
if(Character == -1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
pIt = pTmp;
|
|
|
|
++CharCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if((int)(std::intptr_t)(pIt - pText) == UTF8Off)
|
|
|
|
{
|
|
|
|
DecodedOff = CharCount;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual bool DecodedOffToUTF8Off(const char *pText, int DecodedOff, int &UTF8Off)
|
|
|
|
{
|
|
|
|
const char *pIt = pText;
|
|
|
|
|
|
|
|
UTF8Off = -1;
|
|
|
|
|
|
|
|
int CharCount = 0;
|
|
|
|
while(*pIt)
|
|
|
|
{
|
|
|
|
const char *pTmp = pIt;
|
|
|
|
int Character = str_utf8_decode(&pTmp);
|
|
|
|
if(Character == -1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if(CharCount == DecodedOff)
|
|
|
|
{
|
|
|
|
UTF8Off = (int)((std::intptr_t)(pIt - pText));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
pIt = pTmp;
|
|
|
|
++CharCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(CharCount == DecodedOff)
|
|
|
|
UTF8Off = (int)((std::intptr_t)(pIt - pText));
|
|
|
|
|
|
|
|
return UTF8Off != -1;
|
|
|
|
}
|
|
|
|
|
2018-03-21 14:43:56 +00:00
|
|
|
virtual void OnWindowResize()
|
|
|
|
{
|
|
|
|
bool FoundTextContainer = false;
|
2021-09-12 17:40:23 +00:00
|
|
|
for(auto *pTextContainer : m_TextContainers)
|
|
|
|
if(pTextContainer->m_StringInfo.m_QuadBufferContainerIndex != -1)
|
2018-03-21 14:43:56 +00:00
|
|
|
FoundTextContainer = true;
|
2020-09-26 19:41:58 +00:00
|
|
|
if(FoundTextContainer)
|
2018-03-21 15:07:03 +00:00
|
|
|
{
|
2020-10-26 08:57:41 +00:00
|
|
|
dbg_msg("textrender", "%s", "Found non empty text container");
|
2018-03-21 15:07:03 +00:00
|
|
|
dbg_assert(false, "text container was not empty");
|
|
|
|
}
|
2018-03-21 14:43:56 +00:00
|
|
|
|
2020-10-26 14:14:07 +00:00
|
|
|
for(auto &pFont : m_Fonts)
|
2018-03-21 14:43:56 +00:00
|
|
|
{
|
|
|
|
// reset the skylines
|
|
|
|
for(int j = 0; j < 2; ++j)
|
2018-05-07 03:52:02 +00:00
|
|
|
{
|
2020-10-26 14:14:07 +00:00
|
|
|
for(int &k : pFont->m_TextureSkyline[j].m_CurHeightOfPixelColumn)
|
2020-10-26 13:11:11 +00:00
|
|
|
k = 0;
|
2018-03-21 14:43:56 +00:00
|
|
|
|
2020-10-26 14:14:07 +00:00
|
|
|
mem_zero(pFont->m_TextureData[j], (size_t)pFont->m_CurTextureDimensions[j] * pFont->m_CurTextureDimensions[j] * sizeof(unsigned char));
|
|
|
|
Graphics()->LoadTextureRawSub(pFont->m_aTextures[j], 0, 0, pFont->m_CurTextureDimensions[j], pFont->m_CurTextureDimensions[j], CImageInfo::FORMAT_ALPHA, pFont->m_TextureData[j]);
|
2018-05-07 03:52:02 +00:00
|
|
|
}
|
|
|
|
|
2020-10-26 14:14:07 +00:00
|
|
|
pFont->InitFontSizes();
|
2018-03-21 14:43:56 +00:00
|
|
|
}
|
|
|
|
}
|
2010-05-29 07:25:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
IEngineTextRender *CreateEngineTextRender() { return new CTextRender; }
|