ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

winfont.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "common/file.h"
00024 #include "common/str.h"
00025 #include "common/stream.h"
00026 #include "common/textconsole.h"
00027 #include "common/winexe_ne.h"
00028 #include "common/winexe_pe.h"
00029 #include "graphics/surface.h"
00030 #include "graphics/fonts/winfont.h"
00031 
00032 namespace Graphics {
00033 
00034 WinFont::WinFont() {
00035     _glyphs = 0;
00036     close();
00037 }
00038 
00039 WinFont::~WinFont() {
00040     close();
00041 }
00042 
00043 void WinFont::close() {
00044     _pixHeight = 0;
00045     _maxWidth = 0;
00046     _firstChar = 0;
00047     _lastChar = 0;
00048     _defaultChar = 0;
00049     _glyphCount = 0;
00050     delete[] _glyphs;
00051     _glyphs = 0;
00052 }
00053 
00054 // Reads a null-terminated string
00055 static Common::String readString(Common::SeekableReadStream &stream) {
00056     Common::String string;
00057 
00058     char c = stream.readByte();
00059     while (c && stream.pos() < stream.size()) {
00060         string += c;
00061         c = stream.readByte();
00062     }
00063 
00064     return string;
00065 }
00066 
00067 static WinFontDirEntry readDirEntry(Common::SeekableReadStream &stream) {
00068     WinFontDirEntry entry;
00069 
00070     stream.skip(68); // Useless
00071     entry.points = stream.readUint16LE();
00072     stream.skip(43); // Useless (for now, maybe not in the future)
00073     readString(stream);
00074     entry.faceName = readString(stream);
00075 
00076     return entry;
00077 }
00078 
00079 bool WinFont::loadFromFON(const Common::String &fileName, const WinFontDirEntry &dirEntry) {
00080     Common::WinResources *exe = Common::WinResources::createFromEXE(fileName);
00081     if (!exe)
00082         return false;
00083 
00084     bool ok = loadFromEXE(exe, fileName, dirEntry);
00085     delete exe;
00086     return ok;
00087 }
00088 
00089 bool WinFont::loadFromEXE(Common::WinResources *exe, const Common::String &fileName, const WinFontDirEntry &dirEntry) {
00090     // Let's pull out the font directory
00091     Common::SeekableReadStream *fontDirectory = exe->getResource(Common::kWinFontDir, Common::String("FONTDIR"));
00092     if (!fontDirectory) {
00093         warning("No font directory in '%s'", fileName.c_str());
00094         return false;
00095     }
00096 
00097     uint32 fontId = getFontIndex(*fontDirectory, dirEntry);
00098 
00099     delete fontDirectory;
00100 
00101     // Couldn't match the face name
00102     if (fontId == 0xffffffff) {
00103         warning("Could not find face '%s' in '%s'", dirEntry.faceName.c_str(), fileName.c_str());
00104         return false;
00105     }
00106 
00107     // Actually go get our font now...
00108     Common::SeekableReadStream *fontStream = exe->getResource(Common::kWinFont, fontId);
00109     if (!fontStream) {
00110         warning("Could not find font %d in %s", fontId, fileName.c_str());
00111         return false;
00112     }
00113 
00114     bool ok = loadFromFNT(*fontStream);
00115     delete fontStream;
00116     return ok;
00117 }
00118 
00119 uint32 WinFont::getFontIndex(Common::SeekableReadStream &stream, const WinFontDirEntry &dirEntry) {
00120     uint16 numFonts = stream.readUint16LE();
00121 
00122     // Probably not possible, so this is really a sanity check
00123     if (numFonts == 0) {
00124         warning("No fonts in exe");
00125         return 0xffffffff;
00126     }
00127 
00128     // Scour the directory for our matching name
00129     for (uint16 i = 0; i < numFonts; i++) {
00130         uint16 id = stream.readUint16LE();
00131 
00132         // Use the first name when empty
00133         if (dirEntry.faceName.empty())
00134             return id;
00135 
00136         WinFontDirEntry entry = readDirEntry(stream);
00137 
00138         if (dirEntry.faceName.equalsIgnoreCase(entry.faceName) && dirEntry.points == entry.points) // Match!
00139             return id;
00140     }
00141 
00142     return 0xffffffff;
00143 }
00144 
00145 bool WinFont::loadFromFNT(const Common::String &fileName) {
00146     Common::File file;
00147 
00148     return file.open(fileName) && loadFromFNT(file);
00149 }
00150 
00151 char WinFont::indexToCharacter(uint16 index) const {
00152     // Use a space for the sentinel value
00153     if (index == _glyphCount - 1)
00154         return ' ';
00155 
00156     return index + _firstChar;
00157 }
00158 
00159 uint16 WinFont::characterToIndex(uint32 character) const {
00160     // Go to the default character if we didn't find a mapping
00161     if (character < _firstChar || character > _lastChar)
00162         character = _defaultChar;
00163 
00164     return character - _firstChar;
00165 }
00166 
00167 int WinFont::getCharWidth(uint32 chr) const {
00168     return _glyphs[characterToIndex(chr)].charWidth;
00169 }
00170 
00171 bool WinFont::loadFromFNT(Common::SeekableReadStream &stream) {
00172     uint16 version = stream.readUint16LE();
00173 
00174     // We'll accept Win1, Win2, and Win3 fonts
00175     if (version != 0x100 && version != 0x200 && version != 0x300) {
00176         warning("Bad FNT version %04x", version);
00177         return false;
00178     }
00179 
00180     /* uint32 size = */ stream.readUint32LE();
00181     stream.skip(60); // Copyright info
00182     uint16 fontType = stream.readUint16LE();
00183     /* uint16 points = */ stream.readUint16LE();
00184     /* uint16 vertRes = */ stream.readUint16LE();
00185     /* uint16 horizRes = */ stream.readUint16LE();
00186     /* uint16 ascent = */ stream.readUint16LE();
00187     /* uint16 internalLeading = */ stream.readUint16LE();
00188     /* uint16 externalLeading = */ stream.readUint16LE();
00189     /* byte italic = */ stream.readByte();
00190     /* byte underline = */ stream.readByte();
00191     /* byte strikeOut = */ stream.readByte();
00192     /* uint16 weight = */ stream.readUint16LE();
00193     /* byte charSet = */ stream.readByte();
00194     uint16 pixWidth = stream.readUint16LE();
00195     _pixHeight = stream.readUint16LE();
00196     /* byte pitchAndFamily = */ stream.readByte();
00197     /* uint16 avgWidth = */ stream.readUint16LE();
00198     _maxWidth = stream.readUint16LE();
00199     _firstChar = stream.readByte();
00200     _lastChar = stream.readByte();
00201     _defaultChar = stream.readByte();
00202     /* byte breakChar = */ stream.readByte();
00203     /* uint16 widthBytes = */ stream.readUint16LE();
00204     /* uint32 device = */ stream.readUint32LE();
00205     /* uint32 face = */ stream.readUint32LE();
00206     /* uint32 bitsPointer = */ stream.readUint32LE();
00207     uint32 bitsOffset = stream.readUint32LE();
00208     /* byte reserved = */ stream.readByte();
00209 
00210     if (version == 0x100) {
00211         // Seems Win1 has an extra byte?
00212         stream.readByte();
00213     } else if (version == 0x300) {
00214         // For Windows 3.0, Microsoft added 6 new fields. All of which are
00215         // guaranteed to be 0. Which leads to the question: Why add these at all?
00216 
00217         /* uint32 flags = */ stream.readUint32LE();
00218         /* uint16 aSpace = */ stream.readUint16LE();
00219         /* uint16 bSpace = */ stream.readUint16LE();
00220         /* uint16 cSpace = */ stream.readUint16LE();
00221         /* uint32 colorPointer = */ stream.readUint32LE();
00222         stream.skip(16); // Reserved
00223     }
00224 
00225     // Begin loading in the glyphs
00226     _glyphCount = (_lastChar - _firstChar) + 2;
00227     _glyphs = new GlyphEntry[_glyphCount];
00228 
00229     for (uint16 i = 0; i < _glyphCount; i++) {
00230         _glyphs[i].charWidth = stream.readUint16LE();
00231 
00232         // Use the default if present
00233         if (pixWidth)
00234             _glyphs[i].charWidth = pixWidth;
00235 
00236         _glyphs[i].offset = (version == 0x300) ? stream.readUint32LE() : stream.readUint16LE();
00237 
00238         // Seems the offsets in the Win1 font format are based on bitsOffset
00239         if (version == 0x100)
00240             _glyphs[i].offset += bitsOffset;
00241     }
00242 
00243     // TODO: Currently only raster fonts are supported!
00244     if (fontType & 1) {
00245         warning("Vector FNT files not supported yet");
00246         return false;
00247     }
00248 
00249     // Read in the bitmaps for the raster images
00250     for (uint16 i = 0; i < _glyphCount - 1; i++) {
00251         stream.seek(_glyphs[i].offset);
00252 
00253         _glyphs[i].bitmap = new byte[_pixHeight * _glyphs[i].charWidth];
00254 
00255         // Calculate the amount of columns
00256         byte colCount = (_glyphs[i].charWidth + 7) / 8;
00257 
00258         for (uint16 j = 0; j < colCount; j++) {
00259             for (uint16 k = 0; k < _pixHeight; k++) {
00260                 byte x = stream.readByte();
00261                 uint offset = j * 8 + k * _glyphs[i].charWidth;
00262 
00263                 for (byte l = 0; l < 8 && j * 8 + l < _glyphs[i].charWidth; l++)
00264                     _glyphs[i].bitmap[offset + l] = (x & (1 << (7 - l))) ? 1 : 0;
00265             }
00266         }
00267 
00268 #if 0
00269         // Debug print
00270         debug("Character %02x '%c' at %08x", indexToCharacter(i), indexToCharacter(i), _glyphs[i].offset);
00271         for (uint16 j = 0; j < _pixHeight; j++) {
00272             for (uint16 k = 0; k < _glyphs[i].charWidth; k++)
00273                 debugN("%c", _glyphs[i].bitmap[k + j * _glyphs[i].charWidth] ? 'X' : ' ');
00274 
00275             debugN("\n");
00276         }
00277 #endif
00278     }
00279 
00280     return true;
00281 }
00282 
00283 void WinFont::drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const {
00284     assert(dst);
00285     assert(dst->format.bytesPerPixel == 1 || dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
00286     assert(_glyphs);
00287 
00288     GlyphEntry &glyph = _glyphs[characterToIndex(chr)];
00289 
00290     for (uint16 i = 0; i < _pixHeight; i++) {
00291         for (uint16 j = 0; j < glyph.charWidth; j++) {
00292             if (glyph.bitmap[j + i * glyph.charWidth]) {
00293                 if (dst->format.bytesPerPixel == 1)
00294                     *((byte *)dst->getBasePtr(x + j, y + i)) = color;
00295                 else if (dst->format.bytesPerPixel == 2)
00296                     *((uint16 *)dst->getBasePtr(x + j, y + i)) = color;
00297                 else if (dst->format.bytesPerPixel == 4)
00298                     *((uint32 *)dst->getBasePtr(x + j, y + i)) = color;
00299             }
00300         }
00301     }
00302 }
00303 
00304 } // End of namespace Graphics


Generated on Sat May 30 2020 05:01:01 for ResidualVM by doxygen 1.7.1
curved edge   curved edge