SpriteLibrary/SpriteLibrary/SmartImage.cs

458 lines
20 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Drawing;
namespace SpriteLibrary
{
/// <summary>
/// A single frame of an animation
/// </summary>
internal class AnimationSingleFrame
{
public Image Frame;
public Image ResizedFrame;
public int ID;
public AnimationSingleFrame(Image SpriteImage, int id)
{
Frame = SpriteImage;
ID = id;
ResizedFrame = null;
}
}
internal class AnimationFrame
{
public int SingleFrameID; //The ID number of the image we want to view
public TimeSpan Duration; //How long does this image stay there.
public AnimationFrame(int ID, TimeSpan HowLong)
{
SingleFrameID = ID;
Duration = HowLong;
}
}
/// <summary>
/// One animation. A series of images.
/// </summary>
internal class Animation
{
public int AnimationID;
public List<AnimationFrame> Frames = new List<AnimationFrame>();
//The simplest case. It is just one image. No animation.
public Animation(SmartImage Smart_Image, Image SpriteImage)
{
//We create a new frame for it and make an animation of just one frame
AnimationID = Smart_Image.AnimationCount;
AnimationSingleFrame newSingle = new AnimationSingleFrame(SpriteImage, Smart_Image.FrameCount);
AnimationFrame newFrame = new AnimationFrame(Smart_Image.FrameCount, TimeSpan.FromMilliseconds(500));
Frames.Add(newFrame);
Smart_Image.AddFrame(newSingle);
}
/// <summary>
/// Create an image from an image that has a bunch of frames in the one image.
/// Start at the specified position (Start), and grab Count items (if we can find them)
/// </summary>
/// <param name="Count">The number of frames to grab</param>
/// <param name="Start">A point in the image where we start capturing frames</param>
/// <param name="Smart_Image">The smart image this is part of</param>
/// <param name="SpriteImage">the image we use for the sprite. Should have lots of images as a part of it.</param>
/// <param name="width">the width of each frame</param>
/// <param name="height">the height of each frame</param>
/// <param name="duration">The duration in miliseconds for this frame</param>
internal Animation(Point Start, SmartImage Smart_Image, Image SpriteImage, int width, int height, int duration, int Count)
{
//We create a new animation number for this new animation
AnimationID = Smart_Image.AnimationCount;
//Now, we make new frames for each image we can find.
int x = Start.X;
int y = Start.Y;
int counter = 0;
Rectangle where;
Image newImage;
while (y + height <= SpriteImage.Height)
{
while (x + width <= SpriteImage.Width)
{
where = new Rectangle(x, y, width, height);
Bitmap tImage = (Bitmap)SpriteImage;
newImage = (Bitmap)tImage.Clone(where, tImage.PixelFormat);
AnimationSingleFrame newSingle = new AnimationSingleFrame(newImage, Smart_Image.FrameCount);
AnimationFrame newFrame = new AnimationFrame(Smart_Image.FrameCount, TimeSpan.FromMilliseconds(duration));
Frames.Add(newFrame);
Smart_Image.AddFrame(newSingle);
x += width;
counter++;
if (counter >= Count)
return; //Stop when we have reached Count of them
}
y += height;
x = 0;
}
}
/// <summary>
/// Create an image from an image that has a bunch of frames in the one image.
/// Start at (0,0) with the specified height and width. Pull out as many images as we can
/// </summary>
/// <param name="Smart_Image">The smart image this is part of</param>
/// <param name="SpriteImage">the image we use for the sprite. Should have lots of images as a part of it.</param>
/// <param name="width">the width of each frame</param>
/// <param name="height">the height of each frame</param>
/// <param name="duration">The duration in miliseconds for this frame</param>
internal Animation(SmartImage Smart_Image, Image SpriteImage, int width, int height, int duration)
{
//We create a new animation number for this new animation
AnimationID = Smart_Image.AnimationCount;
//Now, we make new frames for each image we can find.
int x = 0;
int y = 0;
Rectangle where;
Image newImage;
while (y + height <= SpriteImage.Height)
{
while (x + width <= SpriteImage.Width)
{
where = new Rectangle(x, y, width, height);
Bitmap tImage = (Bitmap)SpriteImage;
newImage = (Bitmap)tImage.Clone(where, tImage.PixelFormat);
AnimationSingleFrame newSingle = new AnimationSingleFrame(newImage, Smart_Image.FrameCount);
AnimationFrame newFrame = new AnimationFrame(Smart_Image.FrameCount, TimeSpan.FromMilliseconds(duration));
Frames.Add(newFrame);
Smart_Image.AddFrame(newSingle);
x += width;
}
y += height;
x = 0;
}
}
internal Animation(SmartImage Smart_Image, int AnimationToCopy, bool MirrorHorizontally, bool MirrorVertically)
{
//We create a new animation number for this new animation
AnimationID = Smart_Image.AnimationCount;
Animation One = Smart_Image.getAnimation(AnimationToCopy);
Image MeImage;
for (int i = 0; i < One.Frames.Count; i++)
{
AnimationFrame AF = Smart_Image.GetAnimationFrame(AnimationToCopy, i);
AnimationSingleFrame ASF = Smart_Image.GetSingleFrame(AnimationToCopy, i);
MeImage = ASF.Frame;
TimeSpan Duration = AF.Duration;
Image MirrorImage = new Bitmap(MeImage);
if (MirrorHorizontally) MirrorImage.RotateFlip(RotateFlipType.RotateNoneFlipX);
if (MirrorVertically) MirrorImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
//GC.Collect();
MeImage = MirrorImage;
AnimationSingleFrame newSingle = new AnimationSingleFrame(MeImage, Smart_Image.FrameCount);
AnimationFrame newFrame = new AnimationFrame(Smart_Image.FrameCount, Duration);
Frames.Add(newFrame);
Smart_Image.AddFrame(newSingle);
}
}
internal Animation(SmartImage Smart_Image, int AnimationToCopy, int DegreesOfRotation)
{
//We create a new animation number for this new animation
AnimationID = Smart_Image.AnimationCount;
GraphicsUnit tUnit = GraphicsUnit.Pixel;
Animation One = Smart_Image.getAnimation(AnimationToCopy);
Image MeImage;
for (int i = 0; i < One.Frames.Count; i++)
{
AnimationFrame AF = Smart_Image.GetAnimationFrame(AnimationToCopy, i);
AnimationSingleFrame ASF = Smart_Image.GetSingleFrame(AnimationToCopy, i);
MeImage = ASF.Frame;
TimeSpan Duration = AF.Duration;
Image rotatedImage = new Bitmap(MeImage.Width, MeImage.Height);
using (Graphics g = Graphics.FromImage(rotatedImage))
{
g.TranslateTransform(MeImage.Width / 2, MeImage.Height / 2); //set the rotation point as the center into the matrix
g.RotateTransform(DegreesOfRotation * -1); //rotate. The rotation direction we use is opposite of what they use
g.TranslateTransform(-MeImage.Width / 2, -MeImage.Height / 2); //restore rotation point into the matrix
g.DrawImage(MeImage, MeImage.GetBounds(ref tUnit), rotatedImage.GetBounds(ref tUnit), tUnit); //draw the image on the new bitmap
}
//GC.Collect();
MeImage = rotatedImage;
AnimationSingleFrame newSingle = new AnimationSingleFrame(MeImage, Smart_Image.FrameCount);
AnimationFrame newFrame = new AnimationFrame(Smart_Image.FrameCount, Duration);
Frames.Add(newFrame);
Smart_Image.AddFrame(newSingle);
}
}
}
/// <summary>
/// This is the holder and parser for images within the AnimatedSprite world
/// It allows you to store and access animations. A smart image might be a "troll"
/// that has a series of animations for up, down, left, right, and die.
/// </summary>
internal class SmartImage
{
SpriteController MyController;
List<Animation> Animations = new List<Animation>();
List<AnimationSingleFrame> Frames = new List<AnimationSingleFrame>();
public int FrameCount { get { return Frames.Count; } }
public int AnimationCount { get { return Animations.Count; } }
public SmartImage(SpriteController Controller, Image SpriteImage)
{
MyController = Controller;
AddAnimation(SpriteImage);
}
/// <summary>
/// Make an animated image from an image that contains multiple frames
/// </summary>
/// <param name="Controller">The sprite controller this is attached to</param>
/// <param name="SpriteImage">The image we use to draw the animation from</param>
/// <param name="width">The width of the image to cut out of the main image</param>
/// <param name="height">The height of the image to cut out of the main image</param>
/// <param name="duration">The duration in miliseconds</param>
public SmartImage(SpriteController Controller, Image SpriteImage, int width, int height, int duration)
{
MyController = Controller;
AddAnimation(SpriteImage, width, height, duration);
}
public SmartImage(Point Start, SpriteController Controller, Image SpriteImage, int width, int height, int duration, int Count)
{
MyController = Controller;
AddAnimation(Start, SpriteImage, width, height, duration, Count);
}
public Animation getAnimation(int index)
{
if (index < 0 || index > AnimationCount) return null;
return Animations[index];
}
public void AddAnimation(Image SpriteImage)
{
Animation tAnimation = new Animation(this, SpriteImage);
Animations.Add(tAnimation);
}
public void AddAnimation(Image SpriteImage, int width, int height, int duration)
{
Animation tAnimation = new Animation(this, SpriteImage, width, height, duration);
Animations.Add(tAnimation);
}
public void AddAnimation(Point Start, Image SpriteImage, int width, int height, int duration, int Count)
{
Animation tAnimation = new Animation(Start, this, SpriteImage, width, height, duration, Count);
Animations.Add(tAnimation);
}
public void AddAnimation(int AnimationToCopy, bool MirrorHorizontal, bool MirrorVertical)
{
if (AnimationToCopy < 0) return;
if (AnimationToCopy >= AnimationCount) return;
Animation tAnimation = new Animation(this, AnimationToCopy, MirrorHorizontal, MirrorVertical);
Animations.Add(tAnimation);
}
public void AddAnimation(int AnimationToCopy, int RotationDegrees)
{
if (AnimationToCopy < 0) return;
if (AnimationToCopy >= AnimationCount) return;
Animation tAnimation = new Animation(this, AnimationToCopy, RotationDegrees);
Animations.Add(tAnimation);
}
public void ReplaceImage(Image newimage, int animation, int frame)
{
Image SpriteImage;
if (animation >= 0 && animation < Animations.Count)
{
if (frame >= 0 && frame < Animations[animation].Frames.Count)
{
int wFrame = Animations[animation].Frames[frame].SingleFrameID;
for (int looper = 0; looper < Frames.Count; looper++)
{
if (Frames[looper].ID == wFrame)
{
SpriteImage = Frames[looper].Frame;
if (SpriteImage == null) return;
Graphics.FromImage(SpriteImage).Clear(Color.Transparent); //Erase the old image
Graphics.FromImage(SpriteImage).DrawImage(newimage, 0, 0, SpriteImage.Width, SpriteImage.Height);
Frames[looper].ResizedFrame = null; //make sure we redraw the resized frame if we need to do that.
return; //Now that we have found it, return from the loop
}
}
}
}
}
public AnimationFrame GetAnimationFrame(int animation, int frame)
{
if (animation >= 0 && animation < Animations.Count)
{
if (frame >= 0 && frame < Animations[animation].Frames.Count)
{
return Animations[animation].Frames[frame];
}
}
return null;
}
public AnimationSingleFrame GetSingleFrame(int animation, int frame)
{
AnimationFrame AF = GetAnimationFrame(animation, frame);
int wFrame = AF.SingleFrameID;
for (int looper = 0; looper < Frames.Count; looper++)
{
if (Frames[looper].ID == wFrame)
return Frames[looper];
}
return null;
}
public Image GetImage(int animation, int frame, Size HowBig)
{
Image tImage;
bool NeedsResize = false;
//This will change.
if (animation >= 0 && animation < Animations.Count)
{
if (frame >= 0 && frame < Animations[animation].Frames.Count)
{
int wFrame = Animations[animation].Frames[frame].SingleFrameID;
for (int looper = 0; looper < Frames.Count; looper++)
{
if (Frames[looper].ID == wFrame)
{
tImage = Frames[looper].Frame;
if (!MyController.OptimizeForLargeSpriteImages) return tImage; //If we are not set to optimize, do not try to do so.
if (HowBig.Width == 0) return tImage;
if (Math.Abs(((double)tImage.Size.Width - (double)HowBig.Width) / HowBig.Width) - 1 > .3)
NeedsResize = true;
if (Math.Abs(((double)tImage.Size.Height - (double)HowBig.Height) / HowBig.Height) - 1 > .3)
NeedsResize = true;
if(NeedsResize)
{
Size newsize = new Size(HowBig.Width + (int)(HowBig.Width * .2), HowBig.Height + (int)(HowBig.Height * .2));
if(Frames[looper].ResizedFrame == null || Frames[looper].ResizedFrame.Size != newsize)
{
Frames[looper].ResizedFrame = new Bitmap(newsize.Width, newsize.Height);
Rectangle newrec = new Rectangle(0, 0, newsize.Width, newsize.Height);
Graphics.FromImage(Frames[looper].ResizedFrame).DrawImage(tImage, newrec);
}
return Frames[looper].ResizedFrame;
}
return tImage;
}
}
}
}
return null;
}
public TimeSpan GetCurrentDuration(int animation, int frame)
{
if (animation < 0 || animation >= Animations.Count) return new TimeSpan();
if (frame < 0 || frame >= Animations[animation].Frames.Count) return new TimeSpan();
return Animations[animation].Frames[frame].Duration;
}
public void AddFrame(AnimationSingleFrame ToAdd)
{
Frames.Add(ToAdd);
}
/// <summary>
/// Return true if the specified animation and frame for that animation needs
/// to be changed due to the time passing.
/// </summary>
/// <param name="animation">The animation index</param>
/// <param name="frame">the frame index</param>
/// <param name="duration">The time that has passed since the last frame was displayed.</param>
/// <returns></returns>
public bool NeedsNewImage(int animation, int frame, TimeSpan duration)
{
//If we do not have a valid index, return true
if (frame < 0) return true;
if (animation < 0) return true;
if (animation >= Animations.Count) return true; //Hopefully we never get here
if (frame >= Animations[animation].Frames.Count) return true;
//If no duration is set, we never have to change it.
if (Animations[animation].Frames[frame].Duration.TotalMilliseconds == 0)
return true;
//If we get here, we the current index is a valid one. Now, see if the timeframe needs to be changed
if (duration > Animations[animation].Frames[frame].Duration)
return true;
//If we get here, it does not need to be changed.
return false;
}
/// <summary>
/// Check to see if the animation is in the last frame. Only works if animateonce is set to true
/// </summary>
/// <param name="AnimateOnce">The animateOnce value of the sprite</param>
/// <param name="animation">The animation we think we are on</param>
/// <param name="frame">The frame we think we are on</param>
/// <returns></returns>
public bool AnimationDone(int animation, int frame, bool AnimateOnce)
{
if (!AnimateOnce) return false;
if (frame == Animations[animation].Frames.Count - 1)
return true;
return false;
}
/// <summary>
/// Return the number of frames that the specified animation has.
/// </summary>
/// <param name="Animation">What animation to check</param>
/// <returns>The number of animation frames found in that animation</returns>
public int AnimationFrameCount(int Animation)
{
if (Animation < 0 || Animation >= Animations.Count) return 0;
return Animations[Animation].Frames.Count;
}
public TimeSpan ChangeIndex(int animation, ref int frame, TimeSpan duration, bool AnimateOnce = false)
{
TimeSpan remainder = duration;
if (frame < 0) frame = 0;
if (animation < 0) return remainder;
if (animation >= Animations.Count) return remainder; //Hopefully we never get here
if (frame >= Animations[animation].Frames.Count) return remainder;
//we have a valid index
while (remainder.TotalMilliseconds >= Animations[animation].Frames[frame].Duration.TotalMilliseconds &&
Animations[animation].Frames[frame].Duration.TotalMilliseconds != 0)
{
remainder = remainder - Animations[animation].Frames[frame].Duration;
frame++;
if (AnimateOnce && frame >= Animations[animation].Frames.Count)
{
//we stay on the last frame. We also stop decrementing the timeamount. That lets us know when the last frame terminates.
frame = Animations[animation].Frames.Count - 1;
remainder = remainder + Animations[animation].Frames[frame].Duration;
return remainder;
}
else if (frame >= Animations[animation].Frames.Count)
{
frame = 0;
}
}
return remainder;
}
}
}