diff src/txt-eng-ft.cc @ 9403:4af6e29449c1

[mq]: graphics_text_engine
author Michael Goffioul <michael.goffioul@gmail.com>
date Fri, 26 Jun 2009 21:12:09 +0100
parents
children 7cc35bc348cc
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/src/txt-eng-ft.cc
@@ -0,0 +1,439 @@
+/*
+
+Copyright (C) 2009 Michael Goffioul
+
+This file is part of Octave.
+
+Octave 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 3 of the License, or (at your
+option) any later version.
+
+Octave 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 Octave; see the file COPYING.  If not, see
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#if HAVE_FREETYPE
+
+#if HAVE_FONTCONFIG
+#include <fontconfig/fontconfig.h>
+#endif
+
+#include <iostream>
+
+#include "error.h"
+#include "pr-output.h"
+#include "txt-eng-ft.h"
+
+class
+ft_manager
+{
+public:
+  static bool instance_ok (void)
+    {
+      bool retval = true;
+
+      if (! instance)
+	instance = new ft_manager ();
+
+      if (! instance)
+	{
+	  ::error ("unable to create ft_manager!");
+
+	  retval = false;
+	}
+
+      return retval;
+    }
+
+  static FT_Face get_font (const std::string& name, const std::string& weight,
+			   const std::string& angle, double size)
+    { return (instance_ok ()
+	      ? instance->do_get_font (name, weight, angle, size)
+	      : 0); }
+
+private:
+  static ft_manager *instance;
+
+private:
+  ft_manager (void)
+    {
+      if (FT_Init_FreeType (&library))
+	{
+	  ::error ("unable to initialize freetype library");
+	}
+
+#if HAVE_FONTCONFIG
+      fc_init_done = false;
+      if (! FcInit ())
+	{
+	  ::error ("unable to initialize fontconfig library");
+	}
+      else
+	{
+	  fc_init_done = true;
+	}
+#endif
+    }
+
+  ~ft_manager (void)
+    {
+#if HAVE_FONTCONFIG
+      FcFini ();
+      fc_init_done = false;
+#endif
+    }
+
+  FT_Face do_get_font (const std::string& name, const std::string& weight,
+		       const std::string& angle, double size)
+    {
+      FT_Face retval = 0;
+
+      std::string file;
+
+#if HAVE_FONTCONFIG
+      if (fc_init_done)
+	{
+	  int fc_weight, fc_angle;
+
+	  if (weight == "bold")
+	    fc_weight = FC_WEIGHT_BOLD;
+	  else if (weight == "light")
+	    fc_weight = FC_WEIGHT_LIGHT;
+	  else if (weight == "demi")
+	    fc_weight = FC_WEIGHT_DEMIBOLD;
+	  else
+	    fc_weight = FC_WEIGHT_NORMAL;
+
+	  if (angle == "italic")
+	    fc_angle = FC_SLANT_ITALIC;
+	  else if (angle == "oblique")
+	    fc_angle = FC_SLANT_OBLIQUE;
+	  else
+	    fc_angle = FC_SLANT_ROMAN;
+
+	  FcPattern *pat = FcPatternCreate ();
+
+	  FcPatternAddString (pat, FC_FAMILY, reinterpret_cast<const FcChar8*> (name == "*" ? "sans" : name.c_str ()));
+	  FcPatternAddInteger (pat, FC_WEIGHT, fc_weight);
+	  FcPatternAddInteger (pat, FC_SLANT, fc_angle);
+	  FcPatternAddDouble (pat, FC_PIXEL_SIZE, size);
+
+	  if (FcConfigSubstitute (0, pat, FcMatchPattern))
+	    {
+	      FcResult res;
+	      FcPattern *match;
+
+	      FcDefaultSubstitute (pat);
+	      match = FcFontMatch (0, pat, &res);
+	      
+	      if (match && res != FcResultNoMatch)
+		{
+		  unsigned char *tmp;
+
+		  FcPatternGetString (match, FC_FILE, 0, &tmp);
+		  file = reinterpret_cast<char*> (tmp);
+		}
+	      else
+		::error ("could not match any font: %s-%s-%s-%g",
+			 name.c_str (), weight.c_str (), angle.c_str (),
+			 size);
+
+	      if (match)
+		FcPatternDestroy (match);
+	    }
+
+	  FcPatternDestroy (pat);
+	}
+#endif
+
+      if (file.empty ())
+	{
+#ifdef __WIN32__
+	  file = "C:/WINDOWS/Fonts/verdana.ttf";
+#else
+	  // FIXME: find a "standard" font for UNIX platforms
+#endif
+	}
+
+      if (FT_New_Face (library, file.c_str (), 0, &retval))
+	{
+	  ::error ("unable to load font: %s", file.c_str ());
+	}
+
+      
+      return retval;
+    }
+
+private:
+  FT_Library library;
+#if HAVE_FONTCONFIG
+  bool fc_init_done;
+#endif
+};
+
+ft_manager* ft_manager::instance = 0;
+
+// ---------------------------------------------------------------------------
+
+ft_render::ft_render (void)
+    : text_processor (), face (0), bbox (1, 4, 0.0),
+      xoffset (0), yoffset (0), mode (MODE_BBOX),
+      red (0), green (0), blue (0)
+{
+}
+
+ft_render::~ft_render (void)
+{
+  if (face)
+    FT_Done_Face (face);
+}
+
+void
+ft_render::set_font (const base_properties& props)
+{
+  if (face)
+    FT_Done_Face (face);
+
+  // FIXME: take "fontunits" into account
+  double font_size = props.get (caseless_str ("fontsize")).double_value ();
+
+  face = ft_manager::get_font (props.get (caseless_str ("fontname")).string_value (),
+			       props.get (caseless_str ("fontweight")).string_value (),
+			       props.get (caseless_str ("fontangle")).string_value (),
+			       font_size);
+
+  if (face)
+    {
+      if (FT_Set_Char_Size (face, 0, font_size*64, 0, 0))
+	{
+	  ::error ("ft_render: unable to set font size to %d", font_size);
+	}
+    }
+  else
+    ::error ("ft_render: unable to load appropriate font");
+}
+
+void
+ft_render::set_mode (int m)
+{
+  mode = m;
+
+  switch (mode)
+    {
+    case MODE_BBOX:
+      xoffset = yoffset = 0;
+      bbox = Matrix (1, 4, 0.0);
+      break;
+    case MODE_RENDER:
+      if (bbox.numel () != 4)
+	{
+	  ::error ("ft_render: invalid bounding box, cannot render");
+
+	  xoffset = yoffset = 0;
+	  pixels = uint8NDArray ();
+	}
+      else
+	{
+	  pixels = uint8NDArray (dim_vector (4, bbox(2), bbox(3)),
+				 static_cast<uint8_t> (0));
+	  xoffset = 0;
+	  yoffset = -bbox(1)-1;
+	}
+      break;
+    default:
+      ::error ("ft_render: invalid mode `%d'", mode);
+      break;
+    }
+}
+
+void
+ft_render::visit (text_element_string& e)
+{
+  if (! face)
+    {
+      ::error ("ft_render: font not initialized");
+      return;
+    }
+
+  std::string str = e.string_value ();
+  FT_UInt glyph_index, previous = 0;
+
+  for (int i = 0; i < str.length (); i++)
+    {
+      glyph_index = FT_Get_Char_Index (face, str[i]);
+
+      if (! glyph_index || FT_Load_Glyph (face, glyph_index, FT_LOAD_DEFAULT))
+	{
+	  ::error ("ft_render: unable to load glyph from font for character `%c', skipping",
+		   str[i]);
+	}
+      else
+	{
+	  switch (mode)
+	    {
+	    case MODE_RENDER:
+	      if (FT_Render_Glyph (face->glyph, FT_RENDER_MODE_NORMAL))
+		{
+		  ::error ("ft_render: unable to render glyph for character `%c', skipping",
+			   str[i]);
+		}
+	      else
+		{
+		  FT_Bitmap& bitmap = face->glyph->bitmap;
+		  int x0, y0;
+
+		  if (previous)
+		    {
+		      FT_Vector delta;
+
+		      FT_Get_Kerning (face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
+		      xoffset += (delta.x >> 6);
+		    }
+
+		  x0 = xoffset+face->glyph->bitmap_left;
+		  y0 = yoffset+face->glyph->bitmap_top;
+		  for (int r = 0; r < bitmap.rows; r++)
+		    for (int c = 0; c < bitmap.width; c++)
+		      {
+			unsigned char pix = bitmap.buffer[r*bitmap.width+c];
+			if (x0+c < 0 || x0+c >= pixels.dim2()
+			    || y0-r < 0 || y0-r >= pixels.dim3())
+			  {
+			    //::error ("out-of-bound indexing!!");
+			  }
+			else if (pixels(3, x0+c, y0-r).value () == 0)
+			  {
+			    pixels(0, x0+c, y0-r) = red;
+			    pixels(1, x0+c, y0-r) = green;
+			    pixels(2, x0+c, y0-r) = blue;
+			    pixels(3, x0+c, y0-r) = pix;
+			  }
+		      }
+
+		  xoffset += (face->glyph->advance.x >> 6);
+		}
+	      break;
+
+	    case MODE_BBOX:
+	      // width
+	      if (previous)
+		{
+		  FT_Vector delta;
+
+		  FT_Get_Kerning (face, previous, glyph_index, FT_KERNING_DEFAULT, &delta);
+		  bbox(2) += (delta.x >> 6);
+		}
+	      bbox(2) += (face->glyph->advance.x >> 6);
+
+	      int asc, desc;
+
+	      if (false /*tight*/)
+		{
+		  desc = face->glyph->metrics.horiBearingY - face->glyph->metrics.height;
+		  asc = face->glyph->metrics.horiBearingY;
+		}
+	      else
+		{
+		  asc = face->size->metrics.ascender;
+		  desc = face->size->metrics.descender;
+		}
+
+	      asc = yoffset + (asc >> 6);
+	      desc = yoffset + (desc >> 6);
+
+	      if (desc < bbox(1))
+		{
+		  bbox(3) += (bbox(1) - desc);
+		  bbox(1) = desc;
+		}
+	      if (asc > (bbox(3)+bbox(1)))
+		bbox(3) = asc-bbox(1);
+	      break;
+	    }
+	  
+	  previous = glyph_index;
+	}
+    }
+}
+
+void
+ft_render::reset (void)
+{
+  set_mode (MODE_BBOX);
+  set_color (Matrix (1, 3, 0.0));
+}
+
+void
+ft_render::set_color (Matrix c)
+{
+  if (c.numel () == 3)
+    {
+      red = static_cast<uint8_t> (c(0)*255);
+      green = static_cast<uint8_t> (c(1)*255);
+      blue = static_cast<uint8_t> (c(2)*255);
+    }
+  else
+    ::error ("ft_render::set_color: invalid color");
+}
+
+uint8NDArray
+ft_render::render (text_element* elt, Matrix& box, int rotation)
+{
+  set_mode (MODE_BBOX);
+  elt->accept (*this);
+  box = bbox;
+
+  set_mode (MODE_RENDER);
+  elt->accept (*this);
+
+  switch (rotation)
+    {
+    case ROTATION_0:
+      break;
+    case ROTATION_90:
+	{
+	  Array<octave_idx_type> perm (3);
+	  perm(0) = 0;
+	  perm(1) = 2;
+	  perm(2) = 1;
+	  pixels = pixels.permute (perm);
+
+	  Array<idx_vector> idx (3);
+	  idx(0) = idx_vector (':');
+	  idx(1) = idx_vector (pixels.dim2()-1, -1, -1);
+	  idx(2) = idx_vector (':');
+	  pixels = uint8NDArray (pixels.index (idx));
+	}
+      break;
+    case ROTATION_180:
+	{
+	  Array<idx_vector> idx (3);
+	  idx(0) = idx_vector (':');
+	  idx(1) = idx_vector (pixels.dim2()-1, 0, -1);
+	  idx(2)=  idx_vector (':');
+	  pixels = uint8NDArray (pixels.index (idx));
+	}
+      break;
+    case ROTATION_270:
+	{
+	  // FIXME: implement this...
+	}
+      break;
+    }
+
+  return pixels;
+}
+
+#endif // HAVE_FREETYPE