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     // First try loading via the NE code
00081     if (loadFromNE(fileName, dirEntry))
00082         return true;
00083 
00084     // Then try loading via the PE code
00085     return loadFromPE(fileName, dirEntry);
00086 }
00087 
00088 bool WinFont::loadFromNE(const Common::String &fileName, const WinFontDirEntry &dirEntry) {
00089     Common::NEResources exe;
00090 
00091     if (!exe.loadFromEXE(fileName))
00092         return false;
00093 
00094     // Let's pull out the font directory
00095     Common::SeekableReadStream *fontDirectory = exe.getResource(Common::kNEFontDir, Common::String("FONTDIR"));
00096     if (!fontDirectory) {
00097         warning("No font directory in '%s'", fileName.c_str());
00098         return false;
00099     }
00100 
00101     uint32 fontId = getFontIndex(*fontDirectory, dirEntry);
00102 
00103     delete fontDirectory;
00104 
00105     // Couldn't match the face name
00106     if (fontId == 0xffffffff) {
00107         warning("Could not find face '%s' in '%s'", dirEntry.faceName.c_str(), fileName.c_str());
00108         return false;
00109     }
00110 
00111     // Actually go get our font now...
00112     Common::SeekableReadStream *fontStream = exe.getResource(Common::kNEFont, fontId);
00113     if (!fontStream) {
00114         warning("Could not find font %d in %s", fontId, fileName.c_str());
00115         return false;
00116     }
00117 
00118     bool ok = loadFromFNT(*fontStream);
00119     delete fontStream;
00120     return ok;
00121 }
00122 
00123 bool WinFont::loadFromPE(const Common::String &fileName, const WinFontDirEntry &dirEntry) {
00124     Common::PEResources *exe = new Common::PEResources();
00125 
00126     if (!exe->loadFromEXE(fileName)) {
00127         delete exe;
00128         return false;
00129     }
00130 
00131     // Let's pull out the font directory
00132     Common::SeekableReadStream *fontDirectory = exe->getResource(Common::kPEFontDir, Common::String("FONTDIR"));
00133     if (!fontDirectory) {
00134         warning("No font directory in '%s'", fileName.c_str());
00135         delete exe;
00136         return false;
00137     }
00138 
00139     uint32 fontId = getFontIndex(*fontDirectory, dirEntry);
00140 
00141     delete fontDirectory;
00142 
00143     // Couldn't match the face name
00144     if (fontId == 0xffffffff) {
00145         warning("Could not find face '%s' in '%s'", dirEntry.faceName.c_str(), fileName.c_str());
00146         delete exe;
00147         return false;
00148     }
00149 
00150     // Actually go get our font now...
00151     Common::SeekableReadStream *fontStream = exe->getResource(Common::kPEFont, fontId);
00152     if (!fontStream) {
00153         warning("Could not find font %d in %s", fontId, fileName.c_str());
00154         delete exe;
00155         return false;
00156     }
00157 
00158     bool ok = loadFromFNT(*fontStream);
00159     delete fontStream;
00160     delete exe;
00161     return ok;
00162 }
00163 
00164 uint32 WinFont::getFontIndex(Common::SeekableReadStream &stream, const WinFontDirEntry &dirEntry) {
00165     uint16 numFonts = stream.readUint16LE();
00166 
00167     // Probably not possible, so this is really a sanity check
00168     if (numFonts == 0) {
00169         warning("No fonts in exe");
00170         return 0xffffffff;
00171     }
00172 
00173     // Scour the directory for our matching name
00174     for (uint16 i = 0; i < numFonts; i++) {
00175         uint16 id = stream.readUint16LE();
00176 
00177         // Use the first name when empty
00178         if (dirEntry.faceName.empty())
00179             return id;
00180 
00181         WinFontDirEntry entry = readDirEntry(stream);
00182 
00183         if (dirEntry.faceName.equalsIgnoreCase(entry.faceName) && dirEntry.points == entry.points) // Match!
00184             return id;
00185     }
00186 
00187     return 0xffffffff;
00188 }
00189 
00190 bool WinFont::loadFromFNT(const Common::String &fileName) {
00191     Common::File file;
00192 
00193     return file.open(fileName) && loadFromFNT(file);
00194 }
00195 
00196 char WinFont::indexToCharacter(uint16 index) const {
00197     // Use a space for the sentinel value
00198     if (index == _glyphCount - 1)
00199         return ' ';
00200 
00201     return index + _firstChar;
00202 }
00203 
00204 uint16 WinFont::characterToIndex(uint32 character) const {
00205     // Go to the default character if we didn't find a mapping
00206     if (character < _firstChar || character > _lastChar)
00207         character = _defaultChar;
00208 
00209     return character - _firstChar;
00210 }
00211 
00212 int WinFont::getCharWidth(uint32 chr) const {
00213     return _glyphs[characterToIndex(chr)].charWidth;
00214 }
00215 
00216 bool WinFont::loadFromFNT(Common::SeekableReadStream &stream) {
00217     uint16 version = stream.readUint16LE();
00218 
00219     // We'll accept Win1, Win2, and Win3 fonts
00220     if (version != 0x100 && version != 0x200 && version != 0x300) {
00221         warning("Bad FNT version %04x", version);
00222         return false;
00223     }
00224 
00225     /* uint32 size = */ stream.readUint32LE();
00226     stream.skip(60); // Copyright info
00227     uint16 fontType = stream.readUint16LE();
00228     /* uint16 points = */ stream.readUint16LE();
00229     /* uint16 vertRes = */ stream.readUint16LE();
00230     /* uint16 horizRes = */ stream.readUint16LE();
00231     /* uint16 ascent = */ stream.readUint16LE();
00232     /* uint16 internalLeading = */ stream.readUint16LE();
00233     /* uint16 externalLeading = */ stream.readUint16LE();
00234     /* byte italic = */ stream.readByte();
00235     /* byte underline = */ stream.readByte();
00236     /* byte strikeOut = */ stream.readByte();
00237     /* uint16 weight = */ stream.readUint16LE();
00238     /* byte charSet = */ stream.readByte();
00239     uint16 pixWidth = stream.readUint16LE();
00240     _pixHeight = stream.readUint16LE();
00241     /* byte pitchAndFamily = */ stream.readByte();
00242     /* uint16 avgWidth = */ stream.readUint16LE();
00243     _maxWidth = stream.readUint16LE();
00244     _firstChar = stream.readByte();
00245     _lastChar = stream.readByte();
00246     _defaultChar = stream.readByte();
00247     /* byte breakChar = */ stream.readByte();
00248     /* uint16 widthBytes = */ stream.readUint16LE();
00249     /* uint32 device = */ stream.readUint32LE();
00250     /* uint32 face = */ stream.readUint32LE();
00251     /* uint32 bitsPointer = */ stream.readUint32LE();
00252     uint32 bitsOffset = stream.readUint32LE();
00253     /* byte reserved = */ stream.readByte();
00254 
00255     if (version == 0x100) {
00256         // Seems Win1 has an extra byte?
00257         stream.readByte();
00258     } else if (version == 0x300) {
00259         // For Windows 3.0, Microsoft added 6 new fields. All of which are
00260         // guaranteed to be 0. Which leads to the question: Why add these at all?
00261 
00262         /* uint32 flags = */ stream.readUint32LE();
00263         /* uint16 aSpace = */ stream.readUint16LE();
00264         /* uint16 bSpace = */ stream.readUint16LE();
00265         /* uint16 cSpace = */ stream.readUint16LE();
00266         /* uint32 colorPointer = */ stream.readUint32LE();
00267         stream.skip(16); // Reserved
00268     }
00269 
00270     // Begin loading in the glyphs
00271     _glyphCount = (_lastChar - _firstChar) + 2;
00272     _glyphs = new GlyphEntry[_glyphCount];
00273 
00274     for (uint16 i = 0; i < _glyphCount; i++) {
00275         _glyphs[i].charWidth = stream.readUint16LE();
00276 
00277         // Use the default if present
00278         if (pixWidth)
00279             _glyphs[i].charWidth = pixWidth;
00280 
00281         _glyphs[i].offset = (version == 0x300) ? stream.readUint32LE() : stream.readUint16LE();
00282 
00283         // Seems the offsets in the Win1 font format are based on bitsOffset
00284         if (version == 0x100)
00285             _glyphs[i].offset += bitsOffset;
00286     }
00287 
00288     // TODO: Currently only raster fonts are supported!
00289     if (fontType & 1) {
00290         warning("Vector FNT files not supported yet");
00291         return false;
00292     }
00293 
00294     // Read in the bitmaps for the raster images
00295     for (uint16 i = 0; i < _glyphCount - 1; i++) {
00296         stream.seek(_glyphs[i].offset);
00297 
00298         _glyphs[i].bitmap = new byte[_pixHeight * _glyphs[i].charWidth];
00299 
00300         // Calculate the amount of columns
00301         byte colCount = (_glyphs[i].charWidth + 7) / 8;
00302 
00303         for (uint16 j = 0; j < colCount; j++) {
00304             for (uint16 k = 0; k < _pixHeight; k++) {
00305                 byte x = stream.readByte();
00306                 uint offset = j * 8 + k * _glyphs[i].charWidth;
00307 
00308                 for (byte l = 0; l < 8 && j * 8 + l < _glyphs[i].charWidth; l++)
00309                     _glyphs[i].bitmap[offset + l] = (x & (1 << (7 - l))) ? 1 : 0;
00310             }
00311         }
00312 
00313 #if 0
00314         // Debug print
00315         debug("Character %02x '%c' at %08x", indexToCharacter(i), indexToCharacter(i), _glyphs[i].offset);
00316         for (uint16 j = 0; j < _pixHeight; j++) {
00317             for (uint16 k = 0; k < _glyphs[i].charWidth; k++)
00318                 debugN("%c", _glyphs[i].bitmap[k + j * _glyphs[i].charWidth] ? 'X' : ' ');
00319 
00320             debugN("\n");
00321         }
00322 #endif
00323     }
00324 
00325     return true;
00326 }
00327 
00328 void WinFont::drawChar(Surface *dst, uint32 chr, int x, int y, uint32 color) const {
00329     assert(dst);
00330     assert(dst->format.bytesPerPixel == 1 || dst->format.bytesPerPixel == 2 || dst->format.bytesPerPixel == 4);
00331     assert(_glyphs);
00332 
00333     GlyphEntry &glyph = _glyphs[characterToIndex(chr)];
00334 
00335     for (uint16 i = 0; i < _pixHeight; i++) {
00336         for (uint16 j = 0; j < glyph.charWidth; j++) {
00337             if (glyph.bitmap[j + i * glyph.charWidth]) {
00338                 if (dst->format.bytesPerPixel == 1)
00339                     *((byte *)dst->getBasePtr(x + j, y + i)) = color;
00340                 else if (dst->format.bytesPerPixel == 2)
00341                     *((uint16 *)dst->getBasePtr(x + j, y + i)) = color;
00342                 else if (dst->format.bytesPerPixel == 4)
00343                     *((uint32 *)dst->getBasePtr(x + j, y + i)) = color;
00344             }
00345         }
00346     }
00347 }
00348 
00349 } // End of namespace Graphics


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