mirror of
				https://github.com/vcmi/vcmi.git
				synced 2025-10-31 00:07:39 +02:00 
			
		
		
		
	| @@ -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 | ||||||
|   | |||||||
| @@ -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 | ||||||
| 		} | 		} | ||||||
|   | |||||||
| @@ -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); | ||||||
|  |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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; | ||||||
|  | } | ||||||
|   | |||||||
| @@ -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; | ||||||
| }; | }; | ||||||
|   | |||||||
| @@ -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) | ||||||
|   | |||||||
| @@ -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; | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user