1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "mutationofjb/font.h"
24 #include "mutationofjb/encryptedfile.h"
25 #include "mutationofjb/util.h"
26 #include "common/debug.h"
27 
28 namespace MutationOfJB {
29 
Font(const Common::String & fileName,int horizSpacing,int lineHeight)30 Font::Font(const Common::String &fileName, int horizSpacing, int lineHeight) :
31 	_horizSpacing(horizSpacing),
32 	_lineHeight(lineHeight),
33 	_maxCharWidth(0) {
34 
35 	load(fileName);
36 }
37 
load(const Common::String & fileName)38 bool Font::load(const Common::String &fileName) {
39 	EncryptedFile file;
40 	file.open(fileName);
41 	if (!file.isOpen()) {
42 		reportFileMissingError(fileName.c_str());
43 		return false;
44 	}
45 
46 	file.seek(0x02D6, SEEK_SET); // Skip header + unknown data (unused palette?).
47 
48 	uint16 noGlyphs = 0;
49 	noGlyphs = file.readUint16LE();
50 
51 	file.seek(7, SEEK_CUR); // Skip unknown data (0s).
52 
53 	uint8 maxHeight = 0;
54 
55 	while (noGlyphs--) {
56 		const uint8 character = file.readByte();
57 		const uint8 width = file.readByte();
58 		const uint8 height = file.readByte();
59 
60 		Graphics::ManagedSurface &surf = _glyphs[character];
61 		surf.create(width, height);
62 		for (int h = 0; h < height; ++h) {
63 			file.read(surf.getBasePtr(0, h), width);
64 		}
65 
66 		if (width > _maxCharWidth) {
67 			_maxCharWidth = width;
68 		}
69 
70 		if (height > maxHeight) {
71 			maxHeight = height;
72 		}
73 	}
74 
75 	if (_lineHeight == -1) {
76 		_lineHeight = maxHeight;
77 	}
78 
79 	return true;
80 }
81 
getFontHeight() const82 int Font::getFontHeight() const {
83 	return _lineHeight;
84 }
85 
getMaxCharWidth() const86 int Font::getMaxCharWidth() const {
87 	return _maxCharWidth;
88 }
89 
getCharWidth(uint32 chr) const90 int Font::getCharWidth(uint32 chr) const {
91 	GlyphMap::iterator it = _glyphs.find(chr);
92 	if (it == _glyphs.end()) {
93 		return 0;
94 	}
95 	return it->_value.w;
96 }
97 
getKerningOffset(uint32 left,uint32 right) const98 int Font::getKerningOffset(uint32 left, uint32 right) const {
99 	if (left == 0) {
100 		// Do not displace the first character.
101 		return 0;
102 	}
103 
104 	if (_glyphs.find(left) == _glyphs.end()) {
105 		// Missing glyphs must not create extra displacement.
106 		// FIXME: This way is not completely correct, as if the last character is
107 		// missing a glyph, it will still create extra displacement. This should
108 		// not affect the visuals but it might affect getStringWidth() / getBoundingBox().
109 		return 0;
110 	}
111 
112 	return _horizSpacing;
113 }
114 
115 class FontBlitOperation {
116 public:
FontBlitOperation(const Font & font,const byte baseColor)117 	FontBlitOperation(const Font &font, const byte baseColor)
118 		: _font(font),
119 		  _baseColor(baseColor) {}
120 
operator ()(const byte srcColor,const byte destColor)121 	byte operator()(const byte srcColor, const byte destColor) {
122 		if (srcColor == 0) {
123 			// Transparency - keep destination color.
124 			return destColor;
125 		}
126 
127 		// Replace destination with transformed source color.
128 		return _font.transformColor(_baseColor, srcColor);
129 	}
130 
131 private:
132 	const Font &_font;
133 	const byte _baseColor;
134 };
135 
drawChar(Graphics::Surface * dst,uint32 chr,int x,int y,uint32 color) const136 void Font::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
137 	GlyphMap::iterator it = _glyphs.find(chr);
138 	if (it == _glyphs.end()) {
139 		// Missing glyph is a common situation in the game and it's okay to ignore it.
140 		return;
141 	}
142 
143 	Graphics::ManagedSurface &glyphSurface = it->_value;
144 	blit_if(glyphSurface, *dst, Common::Point(x, y), FontBlitOperation(*this, color));
145 }
146 
transformColor(uint8 baseColor,uint8 glyphColor) const147 uint8 Font::transformColor(uint8 baseColor, uint8 glyphColor) const {
148 	return baseColor + glyphColor - 0x10;
149 }
150 
SystemFont()151 SystemFont::SystemFont() : Font("sysfnt.aft", 1, 7) {}
152 
SpeechFont()153 SpeechFont::SpeechFont() : Font("font1.aft", -1, -1) {}
154 
transformColor(uint8 baseColor,uint8 glyphColor) const155 uint8 SpeechFont::transformColor(uint8 baseColor, uint8 glyphColor) const {
156 	// Hack in original game.
157 	if (glyphColor == 0x11) {
158 		return 0xC0;
159 	}
160 
161 	return Font::transformColor(baseColor, glyphColor);
162 }
163 
164 }
165