/// /// 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 } }