(********************************************************************)
(*                                                                  *)
(*  bitmapfont.s7i  Font implementation type for bitmap fonts       *)
(*  Copyright (C) 2007  Thomas Mertes                               *)
(*                                                                  *)
(*  This file is part of the Seed7 Runtime Library.                 *)
(*                                                                  *)
(*  The Seed7 Runtime Library is free software; you can             *)
(*  redistribute it and/or modify it under the terms of the GNU     *)
(*  Lesser General Public License as published by the Free Software *)
(*  Foundation; either version 2.1 of the License, or (at your      *)
(*  option) any later version.                                      *)
(*                                                                  *)
(*  The Seed7 Runtime Library 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 Lesser General Public License for more    *)
(*  details.                                                        *)
(*                                                                  *)
(*  You should have received a copy of the GNU Lesser 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.                       *)
(*                                                                  *)
(********************************************************************)


include "font.s7i";
include "pixmapfont.s7i";
include "pixmap_file.s7i";


const type: fontPicType is hash [char] array string;


(**
 *  [[font|Font]] implementation type for bitmap fonts.
 *  The following bitmap fonts are available:
 *  stdfont8.s7i, stdfont9.s7i, stdfont10.s7i, stdfont12.s7i,
 *  stdfont14.s7i, stdfont16.s7i, stdfont18.s7i, stdfont20.s7i,
 *  stdfont24.s7i
 *)
const type: bitmapFont is sub fontProperties struct
    var fontPicType: fontPictures is fontPicType.value;
  end struct;


type_implements_interface(bitmapFont, font);


(**
 *  Determine the pixel width of a string displayed with a font.
 *  @return the pixel width of ''stri'' displayed with ''bmpFont''.
 *)
const func integer: width (in bitmapFont: bmpFont, in string: stri) is func
  result
    var integer: width is 0;
  local
    var char: ch is ' ';
  begin
    for ch range stri do
      if ch in bmpFont.fontPictures then
        width +:= length(bmpFont.fontPictures[ch][1]);
      else
        width +:= length(bmpFont.fontPictures[' '][1]);
      end if;
    end for;
    width +:= characterSpacing(bmpFont) * length(stri);
  end func;


(**
 *  Compute how many chars fit in a width, if ''stri'' is displayed.
 *  This is done for the given ''bmpFont''. The ''allowedWidth'' is
 *  specified in pixels. The expression:
 *   numOfCharsInWidth(aFont, stri, width(aFont, stri))
 *  will always be equivalent to
 *   length(stri);
 *  @return the number of chars from ''stri'' that fit into ''allowedWidth''
 *          if ''stri'' is displayed with ''bmpFont''.
 *)
const func integer: numOfCharsInWidth (in bitmapFont: bmpFont,
    in string: stri, in integer: allowedWidth) is func
  result
    var integer: numOfChars is 0;
  local
    var integer: index is 1;
    var integer: totalWidth is 0;
  begin
    while index <= length(stri) and totalWidth <= allowedWidth do
      if stri[index] in bmpFont.fontPictures then
        totalWidth +:= length(bmpFont.fontPictures[stri[index]][1]);
      else
        totalWidth +:= length(bmpFont.fontPictures[' '][1]);
      end if;
      totalWidth +:= characterSpacing(bmpFont);
      if totalWidth <= allowedWidth then
        incr(index);
      end if;
    end while;
    numOfChars := pred(index);
  end func;


(**
 *  Create a pixmap from a ''pattern''.
 *  A ''pattern'' is a rectangular grid of characters. It encodes
 *  the pixels of a pixmap with characters. The space (' ') encodes
 *  a ''background'' pixel and any other character encodes a
 *  ''foregound'' pixel. A ''scale'' of 1 describes an encoding
 *  with pixels. If ''scale'' is greater than 1 rectangles
 *  with side length ''scale'' are used instead of pixels.
 *  @return the created pixmap.
 *)
const func PRIMITIVE_WINDOW: genPixmap (in array string: pattern,
    in color: foreground, in color: background, in integer: scale) is func
  result
    var PRIMITIVE_WINDOW: pixmap is PRIMITIVE_WINDOW.value;
  local
    var integer: height is 0;
    var integer: width is 0;
    var integer: line is 0;
    var integer: column is 0;
  begin
    height := length(pattern);
    width := length(pattern[1]);
    pixmap := newPixmap(width * scale, height * scale);
    clear(pixmap, background);
    for line range 1 to height do
      for column range 1 to width do
        if pattern[line][column] <> ' ' then
          rect(pixmap, pred(column) * scale, pred(line) * scale,
              scale, scale, foreground);
        end if;
      end for;
    end for;
  end func;


(**
 *  Create a pixmap font from a bitmap font.
 *  The pixmapFont structure is set up and the pixmap for space (' ')
 *  is created. The pixmaps of other characters are created on demand
 *  with ''getFontCharPixmap''.
 *  @return the created pixmap font.
 *)
const func pixmapFontType: genPixmapFont (in bitmapFont: bmpFont,
    in integer: fontSize, in integer: scale,
    in color: foreground, in color: background) is func
  result
    var pixmapFontType: pixmapFont is pixmapFontType.value;
  begin
    if ' ' in bmpFont.fontPictures then
      incl(pixmapFont.pixmap, ' ', genPixmap(bmpFont.fontPictures[' '],
          foreground, background, scale));
    end if;
    pixmapFont.baseFont := bmpFont;
    pixmapFont.fontSize := fontSize;
    pixmapFont.scale := scale;
    pixmapFont.foreground := foreground;
    pixmapFont.background := background;
    pixmapFont.baseLineDelta := baseLineDelta(bmpFont) * scale;
    pixmapFont.line_delta := lineHeight(bmpFont) * scale;
    pixmapFont.column_delta := columnWidth(bmpFont) * scale;
    pixmapFont.characterSpacing := characterSpacing(bmpFont) * scale;
  end func;


(**
 *  Get the pixmap of a given ''bitmapFont'' and char.
 *  This function is used to create character pixmaps on demand.
 *  @return the pixmap of the character ''ch''.
 *)
const func PRIMITIVE_WINDOW: getFontCharPixmap (in bitmapFont: bmpFont,
    inout pixmapFontType: pixmapFont, in char: ch) is func
  result
    var PRIMITIVE_WINDOW: charPixmap is PRIMITIVE_WINDOW.value;
  begin
    if ch in bmpFont.fontPictures then
      charPixmap := genPixmap(bmpFont.fontPictures[ch],
          pixmapFont.foreground, pixmapFont.background, pixmapFont.scale);
      incl(pixmapFont.pixmap, ch, charPixmap);
    else
      charPixmap := pixmapFont.pixmap[' '];
    end if;
  end func;


const proc: setFont (inout pixmapFontFile: fontFile, in bitmapFont: aFont) is func
  begin
    fontFile.font := getFont(aFont, fontSize(fontFile.font),
        scale(fontFile.font), foreground(fontFile.font), background(fontFile.font));
  end func;


(**
 *  Determine the maximum column width of all chars in a ''fontPicType''.
 *  This is used as helper function when a font is defined.
 *  @return the maximum column width of all chars in ''fontPictures''.
 *)
const func integer: columnWidth (in fontPicType: fontPictures) is func
  result
    var integer: maxWidth is 0;
  local
    var char: ch is ' ';
  begin
    for key ch range fontPictures do
      if length(fontPictures[ch][1]) > maxWidth then
        maxWidth := length(fontPictures[ch][1]);
      end if;
    end for;
  end func;