///
/// This file is part of ILNumerics Community Edition.
///
/// ILNumerics Community Edition - high performance computing for applications.
/// Copyright (C) 2006 - 2012 Haymo Kutschbach, http://ilnumerics.net
///
/// ILNumerics Community Edition is free software: you can redistribute it and/or modify
/// it under the terms of the GNU General Public License version 3 as published by
/// the Free Software Foundation.
///
/// ILNumerics Community Edition 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 ILNumerics Community Edition. See the file License.txt in the root
/// of your distribution package. If not, see .
///
/// In addition this software uses the following components and/or licenses:
///
/// =================================================================================
/// The Open Toolkit Library License
///
/// Copyright (c) 2006 - 2009 the Open Toolkit library.
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights to
/// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
/// the Software, and to permit persons to whom the Software is furnished to do
/// so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in all
/// copies or substantial portions of the Software.
///
/// =================================================================================
///
using System;
using System.Text;
using System.Drawing;
using System.Collections.Generic;
using ILNumerics.Drawing.Interfaces;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using OpenTK.Graphics.OpenGL.Enums;
using ILNumerics.Drawing;
using ILNumerics.Drawing.Labeling;
namespace ILNumerics.Drawing.Platform.OpenGL {
///
/// OpenGL text renderer in screen coords
///
[ILRenderer(GraphicDeviceType.OpenGL,"Outline","OpenGL cached, outlined textures",true,CoordSystem.Screen)]
public class ILOGLRenderer : IILTextRenderer {
#region event handling
///
/// (IILTextRenderer) Event firing if the texture storage has been cleared
///
public event EventHandler CacheCleared;
void m_textureManager_TextureCacheCleared(object sender, EventArgs e) {
if (CacheCleared != null)
CacheCleared(this,null);
}
#endregion
#region member / properties
ILTextureManager m_textureManager;
float[] m_curPosition = new float[16];
Color m_colorOverride = Color.Empty;
float m_xMin, m_xMax, m_yMin, m_yMax;
#endregion
#region constructors
public ILOGLRenderer (ILPanel panel) {
m_textureManager = panel.TextureManager;
m_textureManager.TextureCacheCleared += new EventHandler(m_textureManager_TextureCacheCleared);
}
#endregion
#region IILTextRenderer Member
public CoordSystem CoordSystem {
get { return CoordSystem.Screen; }
}
public bool Cached {
get {
return true;
}
}
public string Name {
get { return "Outline fonts"; }
}
public string NameLong {
get { return "OpenGL outline fonts (OpenTK)"; }
}
public GraphicDeviceType DeviceType {
get { return GraphicDeviceType.OpenGL; }
}
public bool DrawAfterBufferSwapped {
get { return false; }
}
public void Begin(ILRenderProperties p) {
if (GraphicsContext.CurrentContext == null)
throw new GraphicsContextException("No GraphicsContext is current in the calling thread.");
float[] viewport = new float[4];
GL.GetFloat(GetPName.Viewport, viewport);
// Prepare to draw text. We want pixel perfect precision, so we setup a 2D mode,
// with size equal to the window (in pixels).
// While we could also render text in 3D mode, it would be very hard to get
// pixel-perfect precision.
GL.MatrixMode(MatrixMode.Projection);
GL.PushMatrix();
GL.LoadIdentity();
GL.Ortho(viewport[0], viewport[2], viewport[3], viewport[1], -1.0, 1.0);
GL.MatrixMode(MatrixMode.Modelview);
GL.PushMatrix();
GL.LoadIdentity();
GL.PushAttrib(AttribMask.TextureBit | AttribMask.EnableBit | AttribMask.ColorBufferBit);
GL.Enable(EnableCap.Texture2D);
GL.Enable(EnableCap.Blend);
GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
GL.Disable(EnableCap.DepthTest);
if (p.Clipping) {
GL.Disable(EnableCap.ClipPlane0);
GL.Disable(EnableCap.ClipPlane1);
GL.Disable(EnableCap.ClipPlane2);
GL.Disable(EnableCap.ClipPlane3);
GL.Disable(EnableCap.ClipPlane4);
GL.Disable(EnableCap.ClipPlane5);
}
if (p.PassCount == 0) {
m_xMin = float.MaxValue;
m_xMax = float.MinValue;
m_yMin = float.MaxValue;
m_yMax = float.MinValue;
}
}
public void Begin (ILRenderProperties p, ref double[] modelview) {
if (modelview == null || modelview.Length < 16) {
modelview = new double[16];
}
GL.GetDouble(GetPName.ModelviewMatrix,modelview);
Begin(p);
}
public void End(ILRenderProperties p) {
if (p.Clipping) {
GL.Enable(EnableCap.ClipPlane0);
GL.Enable(EnableCap.ClipPlane1);
GL.Enable(EnableCap.ClipPlane2);
GL.Enable(EnableCap.ClipPlane3);
GL.Enable(EnableCap.ClipPlane4);
GL.Enable(EnableCap.ClipPlane5);
}
GL.PopAttrib();
GL.MatrixMode(MatrixMode.Modelview);
GL.PopMatrix();
GL.MatrixMode(MatrixMode.Projection);
GL.PopMatrix();
if (p.PassCount == 0) {
if (p.MinX > m_xMin) p.MinX = (int)m_xMin;
if (p.MaxX < m_xMax) p.MaxX = (int)m_xMax;
if (p.MaxY < m_yMax) p.MaxY = (int)m_yMax;
if (p.MinY > m_yMin) p.MinY = (int)m_yMin;
}
}
#endregion
#region IILTextRenderer Member
///
/// Place a new item into the texture cache
///
/// unique key to identify the item for later reference
/// bitmap containing the item data
/// area in containing the item's data
public void Cache(string key, Bitmap bmp, RectangleF rect) {
m_textureManager.StoreTextureItem(key,bmp,rect);
}
///
/// Test if the cache contains an item with a specific key
///
/// key
/// true if the item was cached, false otherwise
public bool ExistsKey(string key) {
return m_textureManager.Exists(key);
}
///
/// try to get the size (screen pixels) of an item
///
/// unique key identifying the item
/// [output] size of the item (if found)
/// true, if the item was found, false otherwise
public bool TryGetSize(string key, ref Size size) {
ILTextureData item;
if (m_textureManager.TryGetTextureItem(key,out item)) {
size = new Size(item.Width,item.Height);
return true;
} else {
return false;
}
}
///
/// Draws all items contained in a given render queue
///
/// render queue
/// starting point
/// orientation for the whole queues output
/// starting color
/// The render queue must contain only keys for already cached items!
/// The color parameter serves as a global color definition. It may be overridem
/// by individual color specifications of the queue items.
///
public void Draw(ILRenderQueue queue,
System.Drawing.Point location,
TextOrientation orientation,
Color color) {
float w, h;
if (String.IsNullOrEmpty(queue.Expression.Trim())) return;
w = queue.Size.Width;
h = queue.Size.Height;
// compensate for unexact glx.MeasureString results... (to be fixed!)
//w *= 1.09f;
//h *= 1.09f;
if (orientation == TextOrientation.Vertical) {
GL.Rotate(90,0,0,1);
}
GL.GetFloat(GetPName.ModelviewMatrix,m_curPosition);
m_curPosition[12] = location.X;
m_curPosition[13] = location.Y;
GL.LoadMatrix(m_curPosition);
m_textureManager.Reset();
w = 0.5f; h = 0.5f;
int lineHeight = 0;
float drawPosY;
float xMin = float.MaxValue; // relative to label (include rotation)
float yMin = float.MaxValue;
float xMax = float.MinValue;
float yMax = float.MinValue;
GLColor3(color);
foreach (ILRenderQueueItem item in queue) {
// special symbols & control sequences
switch (item.Key) {
case "\r":
w = 0.5f;
break;
case "\n":
w = 0.5f;
h += lineHeight;
lineHeight = 0;
break;
default:
//since textures (may) lay on different sheets in GL memory,
// we cannot switch them in between Begin() and End(),
// we must start a new Begin with each Quad :(
// possible workaround: If only one sheet is used, draw all
// quads at once.
ILTextureData textData = m_textureManager.GetTextureItem(item.Key,true);
if (item.Color != Color.Empty) {
GLColor3(item.Color);
} else {
GLColor3(color);
}
drawPosY = h + item.Offset.Y;
GL.Begin(BeginMode.Quads);
RectangleF rectF = textData.TextureRectangle;
GL.TexCoord2(rectF.Left,rectF.Bottom);
GL.Vertex2(w, drawPosY + textData.Height - 1); // bl
GL.TexCoord2(rectF.Left,rectF.Top);
GL.Vertex2(w, drawPosY); // tl
if (xMin > w) xMin = w;
if (yMin > drawPosY) yMin = drawPosY;
w += textData.Width-1;
GL.TexCoord2(rectF.Right,rectF.Top);
GL.Vertex2(w, drawPosY); // tr
GL.TexCoord2(rectF.Right,rectF.Bottom);
GL.Vertex2(w, drawPosY + textData.Height - 1); // br
if (textData.Height > lineHeight)
lineHeight = textData.Height;
GL.End();
if (xMax < w) xMax = w;
if (yMax < drawPosY + textData.Height - 1)
yMax = drawPosY + textData.Height - 1;
#if BOUNDINGBOXES_ITEM
// define DEBUG symbol to draw bounding box around each item (debug feature)
GL.Color3(Color.Red);
GL.Begin(BeginMode.LineLoop);
w-= textData.Width;
GL.Vertex2(w,h + textData.Height); // ul
GL.Vertex2(w,h); // bl
w += textData.Width;
GL.Vertex2(w,h); // br
GL.Vertex2(w,h + textData.Height); // tr
GL.End();
#endif
break;
}
}
#if BOUNDINGBOXES
// define debugsymbol "BOUNDINGBOXES", to draw bounding box around whole expression
GL.Disable(EnableCap.Texture2D);
GL.Color3(Color.Red);
GL.LineWidth(1);
GL.Disable(EnableCap.LineStipple);
GL.Begin(BeginMode.LineLoop);
GL.Vertex2(0,0); // ul
GL.Vertex2(0,queue.Size.Height); // bl
GL.Vertex2(queue.Size.Width,queue.Size.Height); // br
GL.Vertex2(queue.Size.Width,0); // tr
GL.End();
GL.Enable(EnableCap.Texture2D);
#endif
// check outer limits
if (orientation == TextOrientation.Horizontal) {
if (m_xMin > location.X + xMin) m_xMin = location.X + xMin;
if (m_xMax < location.X + xMax) m_xMax = location.X + xMax;
if (m_yMin > location.Y + yMin) m_yMin = location.Y + yMin;
if (m_yMax < location.Y + yMax) m_yMax = location.Y + yMax;
} else if (orientation == TextOrientation.Vertical) {
if (m_xMin > location.X - yMax) m_xMin = location.X - yMax;
if (m_xMax < location.X - yMin) m_xMax = location.X - yMin;
if (m_yMin > location.Y - xMin) m_yMin = location.Y - xMin;
if (m_yMax < location.Y - xMax) m_yMax = location.Y - xMax;
}
}
public void Draw(ILRenderQueue renderQueue, float x1, float y1, float z1, float x2, float y2, float z2, Color color) {
Draw(renderQueue,new Point((int)x1,(int)y1),TextOrientation.Horizontal,color);
}
public Color ColorOverride {
get {
return m_colorOverride;
}
set {
m_colorOverride = value;
}
}
#endregion
private void GLColor3(Color color) {
if (m_colorOverride.IsEmpty) {
GL.Color3(color);
} else {
GL.Color3(m_colorOverride);
}
}
}
}