///
/// 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.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
namespace ILNumerics.Drawing.Labeling {
///
/// The class provides texture storage for a single class of texures (e.g. one font)
///
/// The texture items are stored in a single texture sheet
/// and organized via a simple binary tree.
public abstract class ILTextureStorage {
#region attributes / properties
protected Dictionary m_items;
protected int m_height;
protected int m_width;
protected int m_textureId;
///
/// cache, which texture has been bound at last
///
protected bool m_disposed = true;
protected static int[] m_tmpData;
protected Node m_root;
///
/// overall height of the internal texture sheet
///
public int Height {
get { return m_height; }
}
///
/// current width of the internal texture sheet
///
public int Width {
get { return m_width; }
}
///
/// Key used to identify the texure in the graphic system
///
public int TextureID {
get {
return m_textureId;
}
}
#endregion
#region constructor
///
/// construct new storage
///
/// absolute height (permanent)
/// absolute width (permanent)
/// Suggested size parameter will be increased to the next power of two.
public ILTextureStorage(int height, int width) {
m_items = new Dictionary();
if (width <= 0 || height <= 0)
throw new ArgumentOutOfRangeException("width & height ", width, "Must be greater than zero.");
// make size a power of 2
m_height = (int)Math.Pow(2,Math.Ceiling(Math.Log(height,2)));
m_width = (int)Math.Pow(2,Math.Ceiling(Math.Log(width,2)));
if (m_height > 1024) m_height = 1024; // limit to 1024 for compatibility reasons
if (m_width > 1024) m_width = 1024;
m_root = new Node();
m_root.Rect = new Rectangle(0,0,m_width,m_height);
InitTexture();
m_disposed = false;
//System.Diagnostics.Debug.WriteLine("ILTextureStorage (constr) ThreadID:"
// + System.Threading.Thread.CurrentThread.ManagedThreadId);
}
#endregion
#region public / abstract interface
///
/// fetch texture item from storage
///
///
///
public virtual ILTextureData Get (string key) {
ILTextureData ret = null;
if (!m_items.TryGetValue(key,out ret)) {
return null; //throw new ArgumentException("no texture item has been found for key: " + key);
}
return ret;
}
///
/// try to fetch item by key
///
/// unique key
/// [output] item found
/// true: item was found, false otherwise
public virtual bool TryGetTextureItem(string key, out ILTextureData item) {
if (m_items.ContainsKey(key)) {
item = m_items[key];
return true;
}
item = null;
return false;
}
///
/// test, if a key exists in the texture storage
///
/// unique key to be tested for
/// true if a texture item associated with that key exists, false otherwise
public bool Exists (string key) {
return m_items.ContainsKey(key);
}
///
/// store bitmap into texture sheet
///
/// unique key for item
/// item bitmap data
/// rectangle used in data bitmap
/// true on success
public virtual bool Store (string key, Bitmap data, Rectangle rect) {
if (data.Height > m_height || data.Width > m_width)
throw new ArgumentException("texture size is too large for this packer!");
// add to packer
ILTextureData item;
if (m_items.TryGetValue(key,out item)) {
item.Height = data.Height;
item.Width = data.Width;
// todo: remove item from packer & from texture sheet (somehow...[?])
} else {
item = new ILTextureData(rect.Height,rect.Width);
m_items.Add(key, item);
}
Node node = m_root.Insert(item);
if (node == null)
return false;
item.TextureRectangle = RectangleF.FromLTRB( (0.5f + node.Rect.Left) / m_width,
(0.5f + node.Rect.Top) / m_height,
(node.Rect.Right - 0.5f) / m_width,
(node.Rect.Bottom - 0.5f) /m_height);
Store(data,rect, node.Rect);
return true;
}
///
/// store bitmap into texture sheet
///
/// unique key for item
/// item bitmap data
/// used rectangle in data bitmap
public virtual bool Store (string key, Bitmap data, RectangleF bmpRect) {
if (bmpRect.Height > m_height || bmpRect.Width > m_width)
throw new ArgumentException("texture size is too large for this packer!");
// add to packer
ILTextureData item; Node node;
Rectangle itemRect = Rectangle.Ceiling(bmpRect);
if (m_items.TryGetValue(key,out item)) {
item.Height = itemRect.Height;
item.Width = itemRect.Width;
// todo: remove item from packer & from texture sheet (somehow...[?])
node = m_root.Insert(item);
if (node == null)
return false;
} else {
item = new ILTextureData(itemRect.Height, itemRect.Width);
node = m_root.Insert(item);
if (node == null)
return false;
m_items.Add(key, item);
}
item.TextureRectangle = RectangleF.FromLTRB( (0.5f + node.Rect.Left) / m_width,
(0.5f + node.Rect.Top) / m_height,
(node.Rect.Right - 0.5f) / m_width,
(node.Rect.Bottom - 0.5f) /m_height);
//item.TextureRectangle = RectangleF.FromLTRB( (0f + node.Rect.Left) / m_width,
// (0f + node.Rect.Top) / m_height,
// (node.Rect.Right - 0f) / m_width,
// (node.Rect.Bottom - 0f) /m_height);
Store(data,bmpRect,node.Rect);
return true;
}
///
/// initialize texture sheet
///
protected abstract void InitTexture();
///
/// store item in texture sheet in GL
///
/// new item bitmap data
/// area in bitmap data to be stored
/// rectangle specifying area to store the data into,
/// texture coords: range from 0...1.0
protected abstract void Store(Bitmap data, RectangleF location, RectangleF rect);
///
/// select the texture storage as current in the GL
///
/// Calling this function before an storage / render operation is
/// obligatory in specific rendering machines (e.g. OpenGL). For GL's, where
/// it is not neccessary, the implementation must ignore any calls to this function.
public abstract void MakeCurrent();
///
/// Dispose off any texture storage's ressources
///
public void Dispose() {
GC.SuppressFinalize(this);
Dispose(true);
}
///
/// Dispose off manually
///
///
/// The true disposing is done in the concrete implementation.
public virtual void Dispose(bool manual) {
if (!m_disposed) {
if (manual) {
// free texture from GL
}
m_disposed = true;
}
}
///
/// Finalizer, disposing ressources
///
~ILTextureStorage() {
Dispose(false);
}
#endregion
#region helper functions
#endregion
#region Node - binary tree
///
/// class representing a binary tree, used to manage the items on the texture sheet
///
/// This code is a slightly modified version of the OpenTK.Utilities framework
/// TextPrinter/TextureStorage classes. See http://opentk.com for details.
protected class Node
{
public Node()
{
}
Node left, right;
Rectangle rect;
int use_count;
public Rectangle Rect { get { return rect; } set { rect = value; } }
public Node Left { get { return left; } set { left = value; } }
public Node Right { get { return right; } set { right = value; } }
#region --- Constructor ---
public bool Leaf
{
get { return left == null && right == null; }
}
#endregion
#region public Node Insert(ILTextureData item)
public Node Insert(ILTextureData item)
{
if (!this.Leaf)
{
// Recurse towards left child, and if that fails, towards the right.
Node new_node = left.Insert(item);
return new_node ?? right.Insert(item);
}
else
{
// We have recursed to a leaf.
// If it is not empty go back.
if (use_count != 0)
return null;
// If this leaf is too small go back.
if (rect.Width < item.Width || rect.Height < item.Height)
return null;
// If this leaf is the right size, insert here.
if (rect.Width == item.Width && rect.Height == item.Height)
{
use_count = 1;
return this;
}
// This leaf is too large, split it up. We'll decide which way to split
// by checking the width and height difference between this rectangle and
// out item's bounding box. If the width difference is larger, we'll split
// horizontaly, else verticaly.
left = new Node();
right = new Node();
int dw = this.rect.Width - item.Width + 1;
int dh = this.rect.Height - item.Height + 1;
if (dw > dh)
{
left.rect = new Rectangle(rect.Left, rect.Top, item.Width, rect.Height);
right.rect = new Rectangle(rect.Left + item.Width, rect.Top, rect.Width - item.Width, rect.Height);
}
else
{
left.rect = new Rectangle(rect.Left, rect.Top, rect.Width, item.Height);
right.rect = new Rectangle(rect.Left, rect.Top + item.Height, rect.Width, rect.Height - item.Height);
}
return left.Insert(item);
}
}
#endregion
#region public void Clear()
public void Clear()
{
if (left != null)
left.Clear();
if (right != null)
right.Clear();
left = right = null;
}
#endregion
}
#endregion
}
}