#region LGPL License
/*************************************************************************
Crazy Eddie's GUI System (http://crayzedsgui.sourceforge.net)
Copyright (C)2004 Paul D Turner (crayzed@users.sourceforge.net)
C# Port developed by Chris McGuirk (leedgitar@latenitegames.com)
Compatible with the Axiom 3D Engine (http://axiomengine.sf.net)
This 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.
This 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 library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*************************************************************************/
#endregion LGPL License
#region Using directives
using System;
using System.Data;
using System.Text;
using System.Xml;
#endregion
namespace CrayzEdsGui.Base{
/// <summary>
/// Offers functions to define, access, and draw, a set of image components
/// on a single graphical surface or <see cref="Texture"/>.
/// </summary>
/// <remarks>
/// Imageset objects are a means by which a single graphical image (file, Texture, etc), can be split into a number
/// of 'components' which can later be accessed via name. The components of an Imageset can queried for
/// various details, and sent to the <see cref="Renderer"/> object for drawing.
/// </remarks>
public class Imageset : IDisposable {
#region Constants
/// <summary>
/// Name of the XSD file to use for validation.
/// </summary>
const string SchemaFileName = "Imageset.xsd";
// TODO: Move into a central location
/// <summary>
/// Default native horizontal resolution (for fonts and imagesets).
/// </summary>
const float DefaultNativeHorzRes = 640.0f;
/// <summary>
/// Default native vertical resolution (for fonts and imagesets)
/// </summary>
const float DefaultNativeVertRes = 480.0f;
#endregion Constants
#region Fields
/// <summary>
/// Name of this image set.
/// </summary>
protected string name;
/// <summary>
/// List of images defined in this set.
/// </summary>
protected ImageList imageList = new ImageList();
/// <summary>
/// Texture object that handles imagery for this Imageset.
/// </summary>
protected Texture texture;
/// <summary>
/// True when auto-scaling is enabled.
/// </summary>
protected bool autoScale;
/// <summary>
/// Current horizontal scaling factor.
/// </summary>
protected float horzScaling;
/// <summary>
/// Current vertical scaling factor.
/// </summary>
protected float vertScaling;
/// <summary>
/// Native horizontal/vertical resolution for this Imageset.
/// </summary>
protected Size nativeResolution;
#endregion Fields
#region Constructors
internal Imageset(string name, Texture texture) {
this.name = name;
this.texture = texture;
if(texture == null) {
throw new ArgumentNullException("Texture object supplied for Imageset creation must not be null.");
}
// set scaling defaults
autoScale = false;
NativeResolution = new Size(DefaultNativeHorzRes, DefaultNativeVertRes);
}
/// <summary>
/// Internal constructor. Constructs a new Imageset object using data contained in the specified file.
/// </summary>
/// <remarks>
/// This is internal, and only needs to be accessible via the various CreateXXX methods of the <see cref="ImagesetManager"/>.
/// </remarks>
/// <param name="fileName">The name of the Imageset data file that is to be processed.</param>
/// <exception cref="System.IO.FileNotFoundException">If the specified file simply does not exist.</exception>
internal Imageset(string fileName) {
// set scaling defaults
autoScale = false;
NativeResolution = new Size(DefaultNativeHorzRes, DefaultNativeVertRes);
// load the texture from file
Load(fileName);
}
#endregion Constructors
#region Public Properties
/// <summary>
/// Get/Set flag indicating whether to use auto-scaling for this Imageset.
/// </summary>
/// <value>true to enable auto-scaling, false to disable auto-scaling.</value>
public bool AutoScale {
get {
return autoScale;
}
set {
if(value != autoScale) {
autoScale = value;
UpdateImageScalingFactors();
}
}
}
/// <summary>
/// Gets the number of images defined for this Imageset.
/// </summary>
/// <value>Integer value equal to the number of Image objects defined for the Imageset.</value>
public int ImageCount {
get {
return imageList.Count;
}
}
/// <summary>
/// Name of the Imageset.
/// </summary>
/// <value>String holding the name of this Imageset.</value>
public string Name {
get {
return name;
}
}
/// <summary>
/// Get/Set the native resolution for this Imageset.
/// </summary>
/// <value>Size describing the new native screen resolution for this Imageset.</value>
public Size NativeResolution {
get {
return nativeResolution;
}
set {
nativeResolution = value;
// re-calculate scaling factors and notify images as required
NotifyScreenResolution(GuiSystem.Instance.Renderer.Size);
}
}
/// <summary>
/// Gets the Texture object for this Imageset.
/// </summary>
/// <value>Texture object that holds the imagery for this Imageset</value>
public Texture Texture {
get {
return texture;
}
}
#endregion Public Properties
#region Protected Methods
/// <summary>
/// Initialize the image set with information taken from the specified file.
/// </summary>
/// <param name="fileName">The name of the Imageset data file that is to be processed.</param>
protected void Load(string fileName) {
// unload old data and textures
Unload();
if (fileName == null || fileName.Length == 0) {
throw new ArgumentException("Invalid Imageset name requested.", "fileName");
}
try {
// initialize a validating reader with the schema
XmlValidatingReader reader = new XmlValidatingReader(new XmlTextReader(fileName));
reader.Schemas.Add("", SchemaFileName);
// grab the Imageset and Image data
XmlDocument doc = new XmlDocument();
doc.Load(reader);
// grab the Imageset data
XmlNode imagesetNode = doc.SelectSingleNode("//Imageset");
name = imagesetNode.Attributes["Name"].Value;
int horzRes = XmlConvert.ToInt32(imagesetNode.Attributes["NativeHorzRes"].Value);
int vertRes = XmlConvert.ToInt32(imagesetNode.Attributes["NativeVertRes"].Value);
this.NativeResolution = new Size(horzRes, vertRes);
this.AutoScale = XmlConvert.ToBoolean(imagesetNode.Attributes["AutoScaled"].Value);
string imageFileName = imagesetNode.Attributes["Imagefile"].Value;
// create the texture for the imageset texture
texture = GuiSystem.Instance.Renderer.CreateTexture(imageFileName);
// load the data for each image defined
foreach (XmlNode imageNode in imagesetNode.SelectNodes("Image")) {
string imageName = imageNode.Attributes["Name"].Value;
Rect rect = new Rect();
rect.left = XmlConvert.ToSingle(imageNode.Attributes["XPos"].Value);
rect.top = XmlConvert.ToSingle(imageNode.Attributes["YPos"].Value);
rect.Width = XmlConvert.ToSingle(imageNode.Attributes["Width"].Value);
rect.Height = XmlConvert.ToSingle(imageNode.Attributes["Height"].Value);
Point offset = new Point();
offset.x = XmlConvert.ToSingle(imageNode.Attributes["XOffset"].Value);
offset.y = XmlConvert.ToSingle(imageNode.Attributes["YOffset"].Value);
// define a new image with the xml data
DefineImage(imageName, rect, offset);
}
}
catch (System.Xml.XmlException ex) {
// unload anything that was created since this failed
Unload();
// just rethrow this one
throw ex;
}
}
/// <summary>
/// Set the Texture object to be used by this Imageset.
/// </summary>
/// <remarks>Changing textures on an Imageset that is in use is not a good idea!.</remarks>
/// <param name="texture">
/// Texture object to be used by the Imageset. The old texture is NOT disposed of, that is the clients responsibility.
/// </param>
protected void SetTexture(Texture texture) {
if (texture == null) {
throw new ArgumentNullException("An Imageset cannot be set to use a null texture");
}
this.texture = texture;
}
/// <summary>
/// Unloads all loaded data and leaves the Imageset in a clean (but un-usable) state.
/// </summary>
/// <remarks>
/// This should be called for cleanup purposes only.
/// </remarks>
protected void Unload() {
UndefineAllImages();
// cleanup texture
GuiSystem.Instance.Renderer.DestroyTexture(texture);
texture = null;
}
/// <summary>
/// Sets the scaling factor for all Images that are a part of this Imageset.
/// </summary>
protected void UpdateImageScalingFactors() {
float hScale = 1.0f, vScale = 1.0f;
if (autoScale) {
hScale = horzScaling;
vScale = vertScaling;
}
for (int i = 0; i < imageList.Count; i++) {
Image image = imageList[i];
image.SetHorizontalScaling(hScale);
image.SetVerticalScaling(vScale);
}
}
#endregion Protected Methods
#region Public Methods
/// <summary>
/// Define a new Image for this Imageset.
/// </summary>
/// <param name="name">The name that will be assigned to the new Image, which must be unique within the Imageset.</param>
/// <param name="position">Point describing the pixel location of the Image on the image file / texture associated with this Imageset.</param>
/// <param name="size">Size describing the dimensions of the Image, in pixels.</param>
/// <param name="renderOffset">Point describing the offsets, in pixels, that are to be applied to the Image when it is drawn.</param>
/// <exception cref="AlreadyExistsException">If an image named <paramref name="name"/> already exists.</exception>
public void DefineImage(string name, Point position, Size size, Point renderOffset) {
Rect imageRect = new Rect(
position.x, position.y,
position.x + size.width,
position.y + size.height);
DefineImage(name, imageRect, renderOffset);
}
/// <summary>
/// Define a new Image for this Imageset.
/// </summary>
/// <param name="name">The name that will be assigned to the new Image, which must be unique within the Imageset.</param>
/// <param name="imageRect"></param>
/// <param name="renderOffset">Point describing the offsets, in pixels, that are to be applied to the Image when it is drawn.</param>
/// <exception cref="AlreadyExistsException">If an image named <paramref name="name"/> already exists.</exception>
public void DefineImage(string name, Rect imageRect, Point renderOffset) {
if (IsImageDefined(name)) {
throw new AlreadyExistsException("Imageset '{0}' already has a definition for Image '{1}'", this.name, name);
}
// get scaling factor
float hScale = autoScale ? horzScaling : 1.0f;
float vScale = autoScale ? vertScaling : 1.0f;
// add the new image definition
imageList.Add(name, new Image(name, this, imageRect, renderOffset, hScale, vScale));
}
/// <summary>
/// Queues an area of the associated Texture the be drawn on the screen.
/// </summary>
/// <remarks>Low-level routine to be used carefully!</remarks>
/// <param name="sourceRect">Rect describing the area of the image file / texture that is to be queued for drawing.</param>
/// <param name="destRect">Rect describing the area of the screen that will be filled with the imagery from <paramref name="sourceRect"/>.</param>
/// <param name="z">float value specifying 'z' order. 0 is topmost with increasing values moving back into the screen.</param>
/// <param name="clipRect">Rect describing a 'clipping rectangle' that will be applied when drawing the requested imagery.</param>
/// <param name="colors">ColorRect holding the ARGB colors to be applied to the four corners of the rendered imagery.</param>
public void Draw(Rect sourceRect, Rect destRect, float z, Rect clipRect, ColorRect colors) {
// get the rect area that we will actually draw to (i.e. perform clipping)
Rect finalRect = destRect.GetIntersection(clipRect);
// check if rect was totally clipped
if (finalRect.Width != 0) {
float xScale = 1.0f / (float)texture.Width;
float yScale = 1.0f / (float)texture.Height;
float xTexPerPix = sourceRect.Width / destRect.Width;
float yTexPerPix = sourceRect.Height / destRect.Height;
// calculate final, clipped, texture co-ordinates
Rect texRect = new Rect(
(sourceRect.left + ((finalRect.left - destRect.left) * xTexPerPix)) * xScale,
(sourceRect.top + ((finalRect.top - destRect.top) * yTexPerPix)) * yScale,
(sourceRect.right + ((finalRect.right - destRect.right) * xTexPerPix)) * xScale,
(sourceRect.bottom + ((finalRect.bottom - destRect.bottom) * yTexPerPix)) * yScale);
// queue a quad to be rendered
texture.Renderer.AddQuad(finalRect, z, texture, texRect, colors);
}
}
/// <summary>
/// Gets the image with the specified name.
/// </summary>
/// <param name="name">The name of the Image object to be returned.</param>
/// <returns>Image object that has the requested name.</returns>
public Image GetImage(string name) {
if (!IsImageDefined(name)) {
throw new UnknownObjectException("Image '{0}' could not be found in Imageset '{1}'", name, this.name);
}
return imageList[name];
}
/// <summary>
/// Returns true if an Image with the specified name exists.
/// </summary>
/// <param name="name">The name of the image to look for.</param>
/// <returns>True if the image exists within this Imageset, false otherwise.</returns>
public bool IsImageDefined(string name) {
return imageList[name] != null;
}
/// <summary>
/// Notify the Imageset of the current (usually new) display resolution.
/// </summary>
/// <param name="size">Size describing the display resolution.</param>
public void NotifyScreenResolution(Size size) {
horzScaling = size.width / nativeResolution.width;
vertScaling = size.height / nativeResolution.height;
if (autoScale) {
UpdateImageScalingFactors();
}
}
/// <summary>
/// Removes the definitions for all Image objects currently defined in the Imageset.
/// </summary>
public void UndefineAllImages() {
imageList.Clear();
}
/// <summary>
/// Remove the definition for the Image with the specified name.
/// If no such Image exists, nothing happens.
/// </summary>
/// <param name="name">The name of the Image object to be removed from the Imageset.</param>
public void UndefineImage(string name) {
imageList.Remove(name);
}
#endregion Public Methods
#region IDisposable Members
/// <summary>
/// Cleans up resources in use.
/// </summary>
public void Dispose() {
Unload();
}
#endregion
}
}
|