1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-19 00:17:56 +02:00

- support for Chinese fonts (GBK 2-byte encoding)

- fixes #1446
This commit is contained in:
Ivan Savenko
2013-09-08 16:49:23 +00:00
parent 42879225f0
commit e13dc872e0
7 changed files with 195 additions and 55 deletions

View File

@ -688,7 +688,7 @@ void processCommand(const std::string &message)
auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll(); auto data = CResourceHandler::get()->load(ResourceID(URI))->readAll();
boost::filesystem::create_directories(fullPath.substr(0, fullPath.find_last_of("/"))); boost::filesystem::create_directories(fullPath.substr(0, fullPath.find_last_of("/")));
std::ofstream outFile(outPath + outName); std::ofstream outFile(outPath + outName, std::ofstream::binary);
outFile.write((char*)data.first.get(), data.second); outFile.write((char*)data.first.get(), data.second);
} }
else else

View File

@ -138,49 +138,45 @@ std::vector<std::string> CMessage::breakText( std::string text, size_t maxLineSi
while (text.length()) while (text.length())
{ {
ui32 lineLength = 0; //in characters or given char metric ui32 lineLength = 0; //in characters or given char metric
ui32 z = 0; //our position in text ui32 wordBreak = -1;
ui32 currPos = 0; //our position in text
bool opened = false;//if we have an unclosed brace in current line bool opened = false;//if we have an unclosed brace in current line
bool lineManuallyBroken = false; bool lineManuallyBroken = false;
while(z < text.length() && text[z] != 0x0a && lineLength < maxLineSize) while(currPos < text.length() && text[currPos] != 0x0a && lineLength < maxLineSize)
{ {
if (ui8(text[currPos]) <= ui8(' ')) // candidate for line break
wordBreak = currPos;
/* We don't count braces in string length. */ /* We don't count braces in string length. */
if (text[z] == '{') if (text[currPos] == '{')
opened=true; opened=true;
else if (text[z]=='}') else if (text[currPos]=='}')
opened=false; opened=false;
else else
lineLength += graphics->fonts[font]->getSymbolWidth(text[z]); lineLength += graphics->fonts[font]->getGlyphWidth(text.data() + currPos);
z++; currPos += graphics->fonts[font]->getCharacterSize(text[currPos]);
} }
if (z < text.length() && (text[z] != 0x0a)) if (currPos < text.length() && (text[currPos] != 0x0a))
{ {
/* We have a long line. Try to do a nice line break, if // We have a long line. Try to do a nice line break, if possible
* possible. We backtrack on the line until we find a if (wordBreak != ui32(-1))
* suitable character. currPos = wordBreak;
* Note: Cyrillic symbols have indexes 220-255 so we need else
* to use ui8 for comparison currPos--;
*/
int pos = z-1;
while(pos > 0 && ((ui8)text[pos]) > ' ' )
pos --;
if (pos > 0)
z = pos+1;
} }
if(z) //non-blank line if(currPos) //non-blank line
{ {
ret.push_back(text.substr(0, z)); ret.push_back(text.substr(0, currPos));
if (opened) if (opened)
/* Close the brace for the current line. */ /* Close the brace for the current line. */
ret.back() += '}'; ret.back() += '}';
text.erase(0, z); text.erase(0, currPos);
} }
else if(text[z] == 0x0a) //blank line else if(text[currPos] == 0x0a) //blank line
{ {
ret.push_back(""); //add empty string, no extra actions needed ret.push_back(""); //add empty string, no extra actions needed
} }

View File

@ -316,6 +316,7 @@ void Graphics::loadFonts()
const JsonVector & bmpConf = config["bitmap"].Vector(); const JsonVector & bmpConf = config["bitmap"].Vector();
const JsonNode & ttfConf = config["trueType"]; const JsonNode & ttfConf = config["trueType"];
const JsonNode & hanConf = config["bitmapHan"];
assert(bmpConf.size() == FONTS_NUMBER); assert(bmpConf.size() == FONTS_NUMBER);
@ -323,11 +324,12 @@ void Graphics::loadFonts()
{ {
std::string filename = bmpConf[i].String(); std::string filename = bmpConf[i].String();
if (!ttfConf[filename].isNull()) // no ttf override if (!hanConf[filename].isNull())
fonts[i] = new CBitmapHanFont(hanConf[filename]);
else if (!ttfConf[filename].isNull()) // no ttf override
fonts[i] = new CTrueTypeFont(ttfConf[filename]); fonts[i] = new CTrueTypeFont(ttfConf[filename]);
else else
fonts[i] = new CBitmapFont(filename); fonts[i] = new CBitmapFont(filename);
} }
} }

View File

@ -18,6 +18,16 @@
* *
*/ */
size_t IFont::getStringWidth(const std::string & data) const
{
size_t width = 0;
for(size_t i=0; i<data.size(); i += getCharacterSize(data[i]))
{
width += getGlyphWidth(data.data() + i);
}
return width;
}
void IFont::renderTextLeft(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const void IFont::renderTextLeft(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{ {
@ -105,23 +115,12 @@ size_t CBitmapFont::getLineHeight() const
return height; return height;
} }
size_t CBitmapFont::getSymbolWidth(char data) const size_t CBitmapFont::getGlyphWidth(const char * data) const
{ {
const Char & ch = chars[ui8(data)]; const Char & ch = chars[ui8(*data)];
return ch.leftOffset + ch.width + ch.rightOffset; return ch.leftOffset + ch.width + ch.rightOffset;
} }
size_t CBitmapFont::getStringWidth(const std::string & data) const
{
size_t width = 0;
for(auto & ch : data)
{
width += getSymbolWidth(ch);
}
return width;
}
void CBitmapFont::renderCharacter(SDL_Surface * surface, const Char & character, const SDL_Color & color, int &posX, int &posY) const void CBitmapFont::renderCharacter(SDL_Surface * surface, const Char & character, const SDL_Color & color, int &posX, int &posY) const
{ {
Rect clipRect; Rect clipRect;
@ -195,6 +194,11 @@ void CBitmapFont::renderText(SDL_Surface * surface, const std::string & data, co
SDL_UnlockSurface(surface); SDL_UnlockSurface(surface);
} }
size_t CBitmapFont::getCharacterSize(char data) const
{
return 1;
}
std::pair<std::unique_ptr<ui8[]>, ui64> CTrueTypeFont::loadData(const JsonNode & config) std::pair<std::unique_ptr<ui8[]>, ui64> CTrueTypeFont::loadData(const JsonNode & config)
{ {
std::string filename = "Data/" + config["file"].String(); std::string filename = "Data/" + config["file"].String();
@ -240,10 +244,10 @@ size_t CTrueTypeFont::getLineHeight() const
return TTF_FontHeight(font.get()); return TTF_FontHeight(font.get());
} }
size_t CTrueTypeFont::getSymbolWidth(char data) const size_t CTrueTypeFont::getGlyphWidth(const char *data) const
{ {
int advance; int advance;
TTF_GlyphMetrics(font.get(), data, nullptr, nullptr, nullptr, nullptr, &advance); TTF_GlyphMetrics(font.get(), *data, nullptr, nullptr, nullptr, nullptr, &advance);
return advance; return advance;
} }
@ -277,3 +281,109 @@ void CTrueTypeFont::renderText(SDL_Surface * surface, const std::string & data,
SDL_FreeSurface(rendered); SDL_FreeSurface(rendered);
} }
} }
size_t CTrueTypeFont::getCharacterSize(char data) const
{
return 1;
}
size_t CBitmapHanFont::getCharacterDataOffset(size_t index) const
{
size_t rowSize = (size + 7) / 8; // 1 bit per pixel, rounded up
size_t charSize = rowSize * size; // glyph contains "size" rows
return index * charSize;
}
size_t CBitmapHanFont::getCharacterIndex(ui8 first, ui8 second) const
{
if (second > 0x7f )
second--;
return (first - 0x81) * (12*16 - 2) + (second - 0x40);
}
void CBitmapHanFont::renderCharacter(SDL_Surface * surface, int characterIndex, const SDL_Color & color, int &posX, int &posY) const
{
//TODO: somewhat duplicated with CBitmapFont::renderCharacter();
Rect clipRect;
SDL_GetClipRect(surface, &clipRect);
TColorPutter colorPutter = CSDL_Ext::getPutterFor(surface, 0);
Uint8 bpp = surface->format->BytesPerPixel;
// start of line, may differ from 0 due to end of surface or clipped surface
int lineBegin = std::max<int>(0, clipRect.y - posY);
int lineEnd = std::min<int>(size, clipRect.y + clipRect.h - posY - 1);
// start end end of each row, may differ from 0
int rowBegin = std::max<int>(0, clipRect.x - posX);
int rowEnd = std::min<int>(size, clipRect.x + clipRect.w - posX - 1);
//for each line in symbol
for(int dy = lineBegin; dy <lineEnd; dy++)
{
Uint8 *dstLine = (Uint8*)surface->pixels;
Uint8 *source = data.first.get() + getCharacterDataOffset(characterIndex);
// shift source\destination pixels to current position
dstLine += (posY+dy) * surface->pitch + posX * bpp;
source += ((size + 7) / 8) * dy;
//for each column in line
for(int dx = rowBegin; dx < rowEnd; dx++)
{
// select current bit in bitmap
int bit = (source[dx / 8] << (dx % 8)) & 0x80;
Uint8* dstPixel = dstLine + dx*bpp;
if (bit != 0)
colorPutter(dstPixel, color.r, color.g, color.b);
}
}
posX += size;
}
void CBitmapHanFont::renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const
{
int posX = pos.x;
int posY = pos.y;
SDL_LockSurface(surface);
for(size_t i=0; i<data.size(); i += getCharacterSize(data[i]))
{
if (ui8(data[i]) < 0x80)
renderCharacter(surface, getCharacterIndex(0xa3, data[i] + 0x80), color, posX, posY);
else
renderCharacter(surface, getCharacterIndex(data[i], data[i+1]), color, posX, posY);
}
SDL_UnlockSurface(surface);
}
CBitmapHanFont::CBitmapHanFont(const JsonNode &config):
data(CResourceHandler::get()->load(ResourceID("data/" + config["name"].String(), EResType::OTHER))->readAll()),
size(config["size"].Float())
{
// basic tests to make sure that fonts are OK
// 1) fonts must contain 190 "sections", 126 symbols each.
assert(getCharacterIndex(0xfe, 0xff) == 190*126);
// ensure that font size is correct - enough to fit all possible symbols
assert(getCharacterDataOffset(getCharacterIndex(0xfe, 0xff)) == data.second);
}
size_t CBitmapHanFont::getLineHeight() const
{
return size;
}
size_t CBitmapHanFont::getGlyphWidth(const char * data) const
{
return size;
}
size_t CBitmapHanFont::getCharacterSize(char data) const
{
if (ui8(data) < 0x80)
return 1;
return 2;
}

View File

@ -18,6 +18,9 @@ struct SDL_Color;
typedef struct _TTF_Font TTF_Font; typedef struct _TTF_Font TTF_Font;
class CBitmapFont;
class CBitmapHanFont;
class IFont class IFont
{ {
protected: protected:
@ -30,10 +33,13 @@ public:
/// Returns height of font /// Returns height of font
virtual size_t getLineHeight() const = 0; virtual size_t getLineHeight() const = 0;
/// Returns width of a single symbol /// Returns width, in pixels of a character glyph. Pointer must contain at least characterSize valid bytes
virtual size_t getSymbolWidth(char data) const = 0; virtual size_t getGlyphWidth(const char * data) const = 0;
/// Returns size (in bytes) of one char in current encoding, may be bigger than one for non-ascii
/// TODO: move it out of this class. Separate entity for handling localization/different encodings?
virtual size_t getCharacterSize(char data) const = 0;
/// Return width of the string /// Return width of the string
virtual size_t getStringWidth(const std::string & data) const = 0; virtual size_t getStringWidth(const std::string & data) const;
/** /**
* @param surface - destination to print text on * @param surface - destination to print text on
@ -77,13 +83,37 @@ class CBitmapFont : public IFont
void renderCharacter(SDL_Surface * surface, const Char & character, const SDL_Color & color, int &posX, int &posY) const; void renderCharacter(SDL_Surface * surface, const Char & character, const SDL_Color & color, int &posX, int &posY) const;
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const; void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override;
public: public:
CBitmapFont(const std::string & filename); CBitmapFont(const std::string & filename);
size_t getLineHeight() const; size_t getLineHeight() const override;
size_t getSymbolWidth(char data) const; size_t getGlyphWidth(const char * data) const override;
size_t getStringWidth(const std::string & data) const; size_t getCharacterSize(char data) const override;
friend class CBitmapHanFont;
};
/// supports multi-byte characters for such languages like Chinese
class CBitmapHanFont : public IFont
{
// data, directly copied from file
const std::pair<std::unique_ptr<ui8[]>, ui64> data;
// size of the font. Not available in file but needed for proper rendering
const size_t size;
size_t getCharacterDataOffset(size_t index) const;
size_t getCharacterIndex(ui8 first, ui8 second) const;
void renderCharacter(SDL_Surface * surface, int characterIndex, const SDL_Color & color, int &posX, int &posY) const;
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override;
public:
CBitmapHanFont(const JsonNode & config);
size_t getLineHeight() const override;
size_t getGlyphWidth(const char * data) const override;
size_t getCharacterSize(char data) const override;
}; };
class CTrueTypeFont : public IFont class CTrueTypeFont : public IFont
@ -97,11 +127,12 @@ class CTrueTypeFont : public IFont
TTF_Font * loadFont(const JsonNode & config); TTF_Font * loadFont(const JsonNode & config);
int getFontStyle(const JsonNode & config); int getFontStyle(const JsonNode & config);
void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const; void renderText(SDL_Surface * surface, const std::string & data, const SDL_Color & color, const Point & pos) const override;
public: public:
CTrueTypeFont(const JsonNode & fontConfig); CTrueTypeFont(const JsonNode & fontConfig);
size_t getLineHeight() const; size_t getLineHeight() const override;
size_t getSymbolWidth(char data) const; size_t getGlyphWidth(const char * data) const override;
size_t getStringWidth(const std::string & data) const; size_t getCharacterSize(char data) const override;
size_t getStringWidth(const std::string & data) const override;
}; };

View File

@ -66,8 +66,6 @@ void CDownloadManager::downloadFinished(QNetworkReply *reply)
file.status = FileEntry::FINISHED; file.status = FileEntry::FINISHED;
} }
file.reply->deleteLater();
bool downloadComplete = true; bool downloadComplete = true;
for (auto & entry : currentDownloads) for (auto & entry : currentDownloads)
{ {
@ -91,6 +89,9 @@ void CDownloadManager::downloadFinished(QNetworkReply *reply)
if (downloadComplete) if (downloadComplete)
emit finished(successful, failed, encounteredErrors); emit finished(successful, failed, encounteredErrors);
file.reply->deleteLater();
file.reply = nullptr;
} }
void CDownloadManager::downloadProgressChanged(qint64 bytesReceived, qint64 bytesTotal) void CDownloadManager::downloadProgressChanged(qint64 bytesReceived, qint64 bytesTotal)

View File

@ -191,7 +191,7 @@ bool ZipArchive::extract(std::string from, std::string where, std::vector<std::s
if (boost::algorithm::ends_with(file, "/")) if (boost::algorithm::ends_with(file, "/"))
continue; continue;
std::ofstream destFile(fullName); std::ofstream destFile(fullName, std::ofstream::binary);
if (!destFile.good()) if (!destFile.good())
return false; return false;