Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

font.cpp

/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.

 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.

 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * $URL: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-1-2-0/engines/draci/font.cpp $
 * $Id: font.cpp 48462 2010-04-02 01:00:35Z spalek $
 *
 */

#include "common/file.h"

#include "draci/draci.h"
#include "draci/font.h"
#include "draci/surface.h"

namespace Draci {

const char * const kFontSmall = "Small.fon";
const char * const kFontBig = "Big.fon";

Font::Font(const Common::String &filename) {
      _fontHeight = 0;
      _maxCharWidth = 0;
      _charWidths = NULL;
      _charData = NULL;

      loadFont(filename);
}

Font::~Font() {
       freeFont();
}

/**
 * @brief Loads fonts from a file
 * @param path Path to font file
 * @return true if the font was loaded successfully, false otherwise
 *
 * Loads fonts from a file into a Font instance. The original game uses two
 * fonts (located inside files "Small.fon" and "Big.fon"). The characters in the
 * font are indexed from the space character so an appropriate offset must be
 * added to convert them to equivalent char values, i.e. kDraciIndexOffset.
 * Characters in the higher range are non-ASCII and vary between different
 * language versions of the game.
 *
 * font format: [1 byte] maximum character width
 *              [1 byte] font height
 *              [138 bytes] character widths of all 138 characters in the font
 *              [138 * fontHeight * maxWidth bytes] character data, stored row-wise
 */

00068 bool Font::loadFont(const Common::String &filename) {
      // Free previously loaded font (if any)
      freeFont();

      Common::File f;

      f.open(filename);
      if (f.isOpen()) {
            debugC(6, kDraciGeneralDebugLevel, "Opened font file %s",
                  filename.c_str());
      } else {
            debugC(6, kDraciGeneralDebugLevel, "Error opening font file %s",
                  filename.c_str());
            return false;
      }

      _maxCharWidth = f.readByte();
      _fontHeight = f.readByte();

      // Read in the widths of the glyphs
      _charWidths = new uint8[kCharNum];
      for (uint i = 0; i < kCharNum; ++i) {
            _charWidths[i] = f.readByte();
      }

      // Calculate size of font data
      uint fontDataSize = kCharNum * _maxCharWidth * _fontHeight;

      // Read in all glyphs
      _charData = new byte[fontDataSize];
      f.read(_charData, fontDataSize);

      debugC(5, kDraciGeneralDebugLevel, "Font %s loaded", filename.c_str());

      return true;
}

00105 void Font::freeFont() {
      delete[] _charWidths;
      delete[] _charData;
}

uint8 Font::getCharWidth(uint8 chr) const {
      // Safe-guard against incorrect strings containing localized characters
      // with inaccessible codes.  These strings do not exist in the original
      // Czech version, but they do in the (never properly reviewed) English
      // version.
      return chr >= kCharIndexOffset && chr < kCharIndexOffset + kCharNum
            ? _charWidths[chr - kCharIndexOffset] : 0;
}

/**
 * @brief Draw a char to a Draci::Surface
 *
 * @param dst   Pointer to the destination surface
 * @param chr   Character to draw
 * @param tx    Horizontal offset on the surface
 * @param ty    Vertical offset on the surface
 */

00128 void Font::drawChar(Surface *dst, uint8 chr, int tx, int ty, int with_colour) const {
      assert(dst != NULL);
      assert(tx >= 0);
      assert(ty >= 0);

      byte *ptr = (byte *)dst->getBasePtr(tx, ty);
      const uint8 currentWidth = getCharWidth(chr);
      if (currentWidth == 0) {
            return;
      }

      const uint8 charIndex = chr - kCharIndexOffset;
      const int charOffset = charIndex * _fontHeight * _maxCharWidth;

      // Determine how many pixels to draw horizontally (to prevent overflow)
      int xSpaceLeft = dst->w - tx - 1;
      int xPixelsToDraw = (currentWidth < xSpaceLeft) ? currentWidth : xSpaceLeft;

      // Determine how many pixels to draw vertically
      int ySpaceLeft = dst->h - ty - 1;
      int yPixelsToDraw = (_fontHeight < ySpaceLeft) ? _fontHeight : ySpaceLeft;

      int _transparent = dst->getTransparentColour();

      for (int y = 0; y < yPixelsToDraw; ++y) {
            for (int x = 0; x <= xPixelsToDraw; ++x) {

                  int curr = y * _maxCharWidth + x;
                  int colour = _charData[charOffset + curr];

                  // If pixel is transparent, skip it
                  if (colour == _transparent)
                        continue;

                  // Replace colour with font colours
                  switch (colour) {
                  case 254:
                        colour = with_colour;
                        break;

                  case 253:
                        colour = kFontColour2;
                        break;

                  case 252:
                        colour = kFontColour3;
                        break;

                  case 251:
                        colour = kFontColour4;
                        break;
                  }

                  // Paint the pixel
                  ptr[x] = colour;
            }

            // Advance to next row
            ptr += dst->pitch;
      }
}

/**
 * @brief Draw a string to a Draci::Surface
 *
 * @param dst       Pointer to the destination surface
 * @param str       Buffer containing string data
 * @param len       Length of the data
 * @param x         Horizontal offset on the surface
 * @param y         Vertical offset on the surface
 * @param spacing   Space to leave between individual characters. Defaults to 0.
 */
00200 void Font::drawString(Surface *dst, const byte *str, uint len,
                        int x, int y, int with_colour, int spacing, bool markDirty) const {
      drawString(dst, Common::String((const char *)str, len), x, y, with_colour, spacing, markDirty);
}

/**
 * @brief Draw a string to a Draci::Surface
 *
 * @param dst       Pointer to the destination surface
 * @param str       String to draw
 * @param x         Horizontal offset on the surface
 * @param y         Vertical offset on the surface
 * @param spacing   Space to leave between individual characters. Defaults to 0.
 */

00215 void Font::drawString(Surface *dst, const Common::String &str,
                        int x, int y, int with_colour, int spacing, bool markDirty) const {
      assert(dst != NULL);
      assert(x >= 0);
      assert(y >= 0);

      uint widest = getStringWidth(str, spacing);

      int curx = x + (widest - getLineWidth(str, 0, spacing)) / 2;
      int cury = y;

      for (uint i = 0; i < str.size(); ++i) {

            // If we encounter the '|' char (newline and end of string marker),
            // skip it and go to the start of the next line
            if (str[i] == '|') {
                  cury += getFontHeight();
                  curx = x + (widest - getLineWidth(str, i+1, spacing) - 1) / 2;
                  continue;
            }

            // Break early if there's no more space on the screen
            if (curx >= dst->w - 1 || cury >= dst->h - 1) {
                  break;
            }

            drawChar(dst, str[i], curx, cury, with_colour);
            curx += getCharWidth(str[i]) + spacing;
      }

      if (markDirty) {
            Common::Rect r(x, y, x + widest, y + getStringHeight(str));
            dst->markDirtyRect(r);
      }
}

/**
 * @brief Calculate the width of a string when drawn in the current font
 *
 * @param str       String to draw
 * @param spacing   Space to leave between individual characters. Defaults to 0.
 *
 * @return The calculated width of the string
 */
00259 uint Font::getStringWidth(const Common::String &str, int spacing) const {
      uint width = 0;

      // Real length, including '|' separators
      uint len = str.size();

      for (uint i = 0, tmp = 0; i < len; ++i) {

            if (str[i] != '|') {
                  tmp += getCharWidth(str[i]) + spacing;
            }

            // Newline char encountered, skip it and store the new length if it is greater.
            // Also, all strings in the data files should end with '|' but not all do.
            // This is why we check whether we are at the last char too.
            if (str[i] == '|' || i == len - 1) {
                  if (tmp > width) {
                        width = tmp;
                  }

                  tmp = 0;
            }
      }

      return width + 1;
}

uint Font::getLineWidth(const Common::String &str, uint startIndex, int spacing) const {
      uint width = 0;

      // If the index is greater or equal to the string size,
      // the width of the line is 0
      if (startIndex >= str.size())
            return 0;

      for (uint i = startIndex; i < str.size(); ++i) {

            // EOL encountered
            if (str[i] == '|')
                  break;

            // Add width of the current char
            width += getCharWidth(str[i]) + spacing;
      }

      return width;
}

/**
 * @brief Calculate the height of a string by counting the number of '|' chars (which
 *        are used as newline characters and end-of-string markers)
 *
 * @param str       String to draw
 * @param spacing   Space to leave between individual characters. Defaults to 0.
 *
 * @return The calculated height of the string
 */
00316 uint Font::getStringHeight(const Common::String &str) const {
      uint len = str.size();
      int separators = 0;

      for (uint i = 0; i < len; ++i) {
            // All strings in the data files should end with '|' but not all do.
            // This is why we check whether we are at the last char too.
            if (str[i] == '|' || i == len - 1) {
                  ++separators;
            }
      }

      return separators * getFontHeight();
}

} // End of namespace Draci

Generated by  Doxygen 1.6.0   Back to index