Skip to content

Commit

Permalink
TextureAtlasEditor - Animated Tiles now play in the selected tile pic…
Browse files Browse the repository at this point in the history
…ture box and AnimationEditor can now accept Animation in constructor aswell as blend the frames with a given color
  • Loading branch information
NessieHax committed Aug 3, 2023
1 parent afb01e6 commit a878e37
Show file tree
Hide file tree
Showing 9 changed files with 532 additions and 375 deletions.
9 changes: 7 additions & 2 deletions PCK-Studio/Extensions/ColorExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@ internal static Vector4 Normalize(this Color color)
return new Vector4(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
}

internal static byte BlendValues(float source, float overlay, BlendMode blendType)
internal static byte BlendValues(byte source, byte overlay, BlendMode blendType)
{
return (byte)MathExtensions.Clamp(BlendValues(source / 255f, overlay / 255f, blendType) * 255, 0, 255);
}

internal static float BlendValues(float source, float overlay, BlendMode blendType)
{
source = MathExtensions.Clamp(source, 0.0f, 1.0f);
overlay = MathExtensions.Clamp(overlay, 0.0f, 1.0f);
Expand All @@ -30,7 +35,7 @@ internal static byte BlendValues(float source, float overlay, BlendMode blendTyp
BlendMode.Screen => 1f - (1f - source) * (1f - overlay),
_ => 0.0f
};
return (byte)MathExtensions.Clamp(resultValue * 255, 0, 255);
return MathExtensions.Clamp(resultValue, 0.0f, 1.0f);
}

internal static byte Mix(double ratio, byte val1, byte val2)
Expand Down
14 changes: 6 additions & 8 deletions PCK-Studio/Extensions/ImageExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,14 +159,12 @@ internal static Image Blend(this Image image, Color overlayColor, BlendMode mode
byte[] baseImageBuffer = new byte[baseImageData.Stride * baseImageData.Height];

Marshal.Copy(baseImageData.Scan0, baseImageBuffer, 0, baseImageBuffer.Length);

var normalized = overlayColor.Normalize();

for (int k = 0; k < baseImageBuffer.Length; k += 4)
{
baseImageBuffer[k + 0] = ColorExtensions.BlendValues(baseImageBuffer[k + 0] / 255f, normalized.X, mode);
baseImageBuffer[k + 1] = ColorExtensions.BlendValues(baseImageBuffer[k + 1] / 255f, normalized.Y, mode);
baseImageBuffer[k + 2] = ColorExtensions.BlendValues(baseImageBuffer[k + 2] / 255f, normalized.Z, mode);
baseImageBuffer[k + 0] = ColorExtensions.BlendValues(baseImageBuffer[k + 0], overlayColor.B, mode);
baseImageBuffer[k + 1] = ColorExtensions.BlendValues(baseImageBuffer[k + 1], overlayColor.G, mode);
baseImageBuffer[k + 2] = ColorExtensions.BlendValues(baseImageBuffer[k + 2], overlayColor.R, mode);
}

Bitmap bitmapResult = new Bitmap(baseImage.Width, baseImage.Height, PixelFormat.Format32bppArgb);
Expand Down Expand Up @@ -201,9 +199,9 @@ internal static Image Blend(this Image image, Image overlay, BlendMode mode)

for (int k = 0; k < baseImageBuffer.Length && k < overlayImageBuffer.Length; k += 4)
{
baseImageBuffer[k + 0] = ColorExtensions.BlendValues(baseImageBuffer[k + 0] / 255f, overlayImageBuffer[k + 0] / 255f, mode);
baseImageBuffer[k + 1] = ColorExtensions.BlendValues(baseImageBuffer[k + 1] / 255f, overlayImageBuffer[k + 1] / 255f, mode);
baseImageBuffer[k + 2] = ColorExtensions.BlendValues(baseImageBuffer[k + 2] / 255f, overlayImageBuffer[k + 2] / 255f, mode);
baseImageBuffer[k + 0] = ColorExtensions.BlendValues(baseImageBuffer[k + 0], overlayImageBuffer[k + 0], mode);
baseImageBuffer[k + 1] = ColorExtensions.BlendValues(baseImageBuffer[k + 1], overlayImageBuffer[k + 1], mode);
baseImageBuffer[k + 2] = ColorExtensions.BlendValues(baseImageBuffer[k + 2], overlayImageBuffer[k + 2], mode);
}

Bitmap bitmapResult = new Bitmap(baseImage.Width, baseImage.Height, PixelFormat.Format32bppArgb);
Expand Down
636 changes: 319 additions & 317 deletions PCK-Studio/Forms/Editor/AnimationEditor.Designer.cs

Large diffs are not rendered by default.

81 changes: 57 additions & 24 deletions PCK-Studio/Forms/Editor/AnimationEditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,42 +27,75 @@ public partial class AnimationEditor : MetroForm

private string TileName = string.Empty;

private readonly bool hasBlendColor = false;
private Color blendColor;
private bool IsSpecialTile(string tileName)
{
return tileName == "clock" || tileName == "compass";
}

public AnimationEditor(PckFile.FileData file)
private AnimationEditor()
{
InitializeComponent();
animationFile = file;
InitializeComponent();
toolStripSeparator1.Visible = saveToolStripMenuItem1.Visible = !Settings.Default.AutoSaveChanges;
}

TileName = Path.GetFileNameWithoutExtension(animationFile.Filename);
internal AnimationEditor(Animation animation, string tileName)
: this()
{
currentAnimation = animation;
TileName = tileName;
}

bulkAnimationSpeedToolStripMenuItem.Enabled =
importToolStripMenuItem.Enabled =
exportAsToolStripMenuItem.Enabled =
InterpolationCheckbox.Visible = !IsSpecialTile(TileName);
public AnimationEditor(PckFile.FileData file)
: this()
{
animationFile = file;
TileName = Path.GetFileNameWithoutExtension(animationFile.Filename);
}

toolStripSeparator1.Visible = saveToolStripMenuItem1.Visible = !Settings.Default.AutoSaveChanges;
public AnimationEditor(PckFile.FileData file, Color blendColor)
: this(file)
{
hasBlendColor = true;
this.blendColor = blendColor;
}

currentAnimation = new Animation(Array.Empty<Image>());
if (animationFile.Size > 0)
{
using MemoryStream textureMem = new MemoryStream(animationFile.Data);
var texture = new Bitmap(textureMem);
var frameTextures = texture.CreateImageList(ImageLayoutDirection.Vertical);

currentAnimation = animationFile.Properties.HasProperty("ANIM")
? new Animation(frameTextures, animationFile.Properties.GetPropertyValue("ANIM"))
: new Animation(frameTextures, string.Empty);
}
private void AnimationEditor_Load(object sender, EventArgs e)
{
bulkAnimationSpeedToolStripMenuItem.Enabled =
importToolStripMenuItem.Enabled =
exportAsToolStripMenuItem.Enabled =
InterpolationCheckbox.Visible = !IsSpecialTile(TileName);

if (currentAnimation is null)
CreateAnimation();
SetTileLabel();
LoadAnimationTreeView();
}

private void CreateAnimation()
{
currentAnimation = new Animation(Array.Empty<Image>());
if (animationFile is not null && animationFile.Size > 0)
{
using MemoryStream textureMem = new MemoryStream(animationFile.Data);
var texture = new Bitmap(textureMem);
var frameTextures = texture.CreateImageList(ImageLayoutDirection.Vertical);
if (hasBlendColor)
{
frameTextures = frameTextures.Select(frameTexture => frameTexture.Blend(blendColor, BlendMode.Multiply));
}

currentAnimation = animationFile.Properties.HasProperty("ANIM")
? new Animation(frameTextures, animationFile.Properties.GetPropertyValue("ANIM"))
: new Animation(frameTextures, string.Empty);
}
currentAnimation.Category = animationFile.Filename.Split('/').Contains("items")
? Animation.AnimationCategory.Items
: Animation.AnimationCategory.Blocks;
SetTileLabel();
LoadAnimationTreeView();
}
}

private void LoadAnimationTreeView()
{
Expand Down Expand Up @@ -131,7 +164,7 @@ private TreeNode FindNodeByName(TreeNode treeNode, string name)

private void saveToolStripMenuItem1_Click(object sender, EventArgs e)
{
if (!IsSpecialTile(TileName) && currentAnimation is not null)
if (!IsSpecialTile(TileName) && currentAnimation is not null && animationFile is not null)
{
string anim = currentAnimation.BuildAnim();
animationFile.Properties.SetProperty("ANIM", anim);
Expand Down Expand Up @@ -546,5 +579,5 @@ private void frameTimeandTicksToolStripMenuItem_Click(object sender, EventArgs e
"All time related functions in Minecraft use ticks, notably redstone repeaters. There are 20 ticks in 1 second, so " +
"1 tick is 1/20 of a second. To find how long your frame is, divide the frame time by 20", "Frame Time and Ticks");
}
}
}
}
56 changes: 47 additions & 9 deletions PCK-Studio/Forms/Editor/AnimationPictureBox.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@

namespace PckStudio.Forms.Editor
{
internal class AnimationPictureBox : PictureBox
internal class AnimationPictureBox : PictureBoxWithInterpolationMode
{
public bool IsPlaying => _isPlaying;

Expand All @@ -39,6 +39,7 @@ internal class AnimationPictureBox : PictureBox
private Animation.Frame currentFrame;
private Animation _animation;
private CancellationTokenSource cts = new CancellationTokenSource();
private object l_dispose = new object();

public void Start(Animation animation)
{
Expand Down Expand Up @@ -97,7 +98,8 @@ private async void DoAnimate()
continue;
}
SetAnimationFrame(currentFrame);
await Task.Delay(TickInMillisecond * currentFrame.Ticks);
if (!await DelayAsync(TickInMillisecond * currentFrame.Ticks, cts.Token))
break;
}
_isPlaying = false;
}
Expand All @@ -107,15 +109,35 @@ private async Task InterpolateFrame(Animation.Frame currentFrame, Animation.Fram
for (int tick = 0; tick < currentFrame.Ticks && !cts.IsCancellationRequested; tick++)
{
double delta = 1.0f - tick / (double)currentFrame.Ticks;
Invoke(() =>
{
if (!Disposing)
Image = currentFrame.Texture.Interpolate(nextFrame.Texture, delta);
});
await Task.Delay(TickInMillisecond);
if (!IsHandleCreated)
break;
lock (l_dispose)
{
Invoke(() =>
{
Image = currentFrame.Texture.Interpolate(nextFrame.Texture, delta);
});
}

if (!await DelayAsync(TickInMillisecond, cts.Token))
break;
}
}

private async Task<bool> DelayAsync(int millisecondsDelay, CancellationToken cancellationToken, [CallerMemberName] string caller = default!)
{
try
{
await Task.Delay(millisecondsDelay, cancellationToken);
}
catch
{
Debug.WriteLine($"Stoping {caller}");
return false;
}
return true;
}

private Animation.Frame SetAnimationFrame(int frameIndex)
{
var frame = _animation.GetFrame(frameIndex);
Expand All @@ -125,7 +147,23 @@ private Animation.Frame SetAnimationFrame(int frameIndex)

private void SetAnimationFrame(Animation.Frame frame)
{
Invoke(() => Image = frame.Texture);
if (!IsHandleCreated)
return;
lock (l_dispose)
{
Invoke(() =>
{
Image = frame.Texture;
});
}
}

protected override void Dispose(bool disposing)
{
lock(l_dispose)
{
base.Dispose(disposing);
}
}
}
}
4 changes: 2 additions & 2 deletions PCK-Studio/Forms/Editor/TextureAtlasEditor.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit a878e37

Please sign in to comment.