Added TextureAtlasEditor.cs and moved some extension methods

This commit is contained in:
miku-666
2023-08-01 17:19:24 +02:00
parent 99e580fd85
commit 07fa88ce51
11 changed files with 3314 additions and 40 deletions

View File

@@ -11,11 +11,11 @@ namespace PckStudio.Extensions
{
public GraphicsConfig()
{
CompositingQuality = CompositingQuality.Default;
InterpolationMode = InterpolationMode.Default;
SmoothingMode = SmoothingMode.Default;
PixelOffsetMode = PixelOffsetMode.Default;
CompositingMode = CompositingMode.SourceOver;
CompositingQuality = default;
InterpolationMode = default;
SmoothingMode = default;
PixelOffsetMode = default;
CompositingMode = default;
}
public CompositingMode CompositingMode { get; set; }
@@ -35,5 +35,14 @@ namespace PckStudio.Extensions
graphics.SmoothingMode = config.SmoothingMode;
graphics.PixelOffsetMode = config.PixelOffsetMode;
}
internal static Graphics Fill(this Graphics graphics, Rectangle area, Color color)
{
var clip = graphics.Clip;
graphics.SetClip(area, CombineMode.Replace);
graphics.Clear(color);
graphics.SetClip(clip, CombineMode.Replace);
return graphics;
}
}
}

View File

@@ -15,7 +15,7 @@
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System;
using System.Drawing;
using System.Diagnostics;
using System.Drawing.Imaging;
@@ -125,6 +125,11 @@ namespace PckStudio.Extensions
return new Size(width, height);
}
internal static Image ResizeImage(this Image image, Size size, GraphicsConfig graphicsConfig)
{
return image.ResizeImage(size.Width, size.Height, graphicsConfig);
}
internal static Image ResizeImage(this Image image, int width, int height, GraphicsConfig graphicsConfig)
{
var destRect = new Rectangle(0, 0, width, height);
@@ -144,18 +149,6 @@ namespace PckStudio.Extensions
return destImage;
}
internal static Image Fill(this Image image, Color color)
{
using (var g = Graphics.FromImage(image))
{
using (SolidBrush brush = new SolidBrush(color))
{
g.FillRectangle(brush, new Rectangle(Point.Empty, image.Size));
}
}
return image;
}
internal static Image Blend(this Image image, Color overlayColor, BlendMode mode)
{
if (image is not Bitmap baseImage)

View File

@@ -11,5 +11,10 @@ namespace PckStudio.Extensions
list[index2] = temp;
return list;
}
public static bool IndexInRange<T>(this IList<T> list, int index)
{
return index >= 0 && index < list.Count;
}
}
}

View File

@@ -63,12 +63,11 @@ namespace PckStudio.Forms.Editor
public Animation(IEnumerable<Image> textures)
{
this.textures = new List<Image>(textures);
AddSingleFrames();
}
public Animation(IEnumerable<Image> textures, string ANIM)
: this(textures)
{
this.textures = new List<Image>(textures);
ParseAnim(ANIM);
}
@@ -89,7 +88,11 @@ namespace PckStudio.Forms.Editor
private void ParseAnim(string anim)
{
_ = anim ?? throw new ArgumentNullException(nameof(anim));
if (string.IsNullOrEmpty(anim))
{
AddSingleFrames();
return;
}
anim = anim.Trim();
anim = (Interpolate = anim.StartsWith("#")) ? anim.Substring(1) : anim;
string[] animData = anim.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
@@ -119,7 +122,7 @@ namespace PckStudio.Forms.Editor
private void CheckTextureIndex(int index)
{
if ((index < 0 || index >= textures.Count))
if (!textures.IndexInRange(index))
throw new ArgumentOutOfRangeException(nameof(index));
}

View File

@@ -45,7 +45,8 @@ namespace PckStudio.Forms.Editor
toolStripSeparator1.Visible = saveToolStripMenuItem1.Visible = !Settings.Default.AutoSaveChanges;
if (animationFile.Size > 0)
currentAnimation = new Animation(Array.Empty<Image>());
if (animationFile.Size > 0)
{
using MemoryStream textureMem = new MemoryStream(animationFile.Data);
var texture = new Bitmap(textureMem);
@@ -53,9 +54,9 @@ namespace PckStudio.Forms.Editor
currentAnimation = animationFile.Properties.HasProperty("ANIM")
? new Animation(frameTextures, animationFile.Properties.GetPropertyValue("ANIM"))
: new Animation(frameTextures);
: new Animation(frameTextures, string.Empty);
}
currentAnimation.Category = animationFile.Filename.Split('/').Contains("items")
currentAnimation.Category = animationFile.Filename.Split('/').Contains("items")
? Animation.AnimationCategory.Items
: Animation.AnimationCategory.Blocks;
SetTileLabel();
@@ -83,7 +84,10 @@ namespace PckStudio.Forms.Editor
SelectedImageIndex = imageIndex,
});
}
animationPictureBox.SelectFrame(currentAnimation, 0);
if (currentAnimation.FrameCount > 0)
{
animationPictureBox.SelectFrame(currentAnimation, 0);
}
}
private void frameTreeView_AfterSelect(object sender, TreeViewEventArgs e)
@@ -296,9 +300,10 @@ namespace PckStudio.Forms.Editor
MessageBox.Show(textureFile + " was not found", "Texture not found");
return;
}
var textures = Image.FromFile(textureFile).CreateImageList(ImageLayoutDirection.Horizontal);
var textures = Image.FromFile(textureFile).CreateImageList(ImageLayoutDirection.Vertical);
var new_animation = new Animation(textures);
try
new_animation.Category = currentAnimation.Category;
try
{
JObject mcmeta = JObject.Parse(File.ReadAllText(fileDialog.FileName));
if (mcmeta["animation"] is JToken animation)
@@ -479,7 +484,7 @@ namespace PckStudio.Forms.Editor
gif.SelectActiveFrame(dimension, i);
textures.Add(new Bitmap(gif));
}
currentAnimation = new Animation(textures);
currentAnimation = new Animation(textures, string.Empty);
LoadAnimationTreeView();
}
@@ -494,7 +499,7 @@ namespace PckStudio.Forms.Editor
return;
Image img = Image.FromFile(ofd.FileName);
var textures = img.CreateImageList(ImageLayoutDirection.Vertical);
currentAnimation = new Animation(textures);
currentAnimation = new Animation(textures, string.Empty);
LoadAnimationTreeView();
}

View File

@@ -106,12 +106,11 @@ namespace PckStudio.Forms.Editor
for (int tick = 0; tick < currentFrame.Ticks && !cts.IsCancellationRequested; tick++)
{
double delta = 1.0f - tick / (double)currentFrame.Ticks;
if (!IsDisposed)
Invoke(() =>
{
if (!IsDisposed)
Image = currentFrame.Texture.Interpolate(nextFrame.Texture, delta);
});
Invoke(() =>
{
if (!Disposing)
Image = currentFrame.Texture.Interpolate(nextFrame.Texture, delta);
});
await Task.Delay(TickInMillisecond);
}
}

View File

@@ -0,0 +1,259 @@
namespace PckStudio
{
partial class TextureAtlasEditor
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TextureAtlasEditor));
this.prevButton = new MetroFramework.Controls.MetroButton();
this.nextButton = new MetroFramework.Controls.MetroButton();
this.menuStrip1 = new System.Windows.Forms.MenuStrip();
this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.extractTileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.originalPictureBox = new PckStudio.PictureBoxWithInterpolationMode();
this.selectImagePictureBox = new PckStudio.PictureBoxWithInterpolationMode();
this.infoTextBox = new System.Windows.Forms.RichTextBox();
this.replaceButton = new MetroFramework.Controls.MetroButton();
this.animationButton = new MetroFramework.Controls.MetroButton();
this.menuStrip1.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.originalPictureBox)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.selectImagePictureBox)).BeginInit();
this.SuspendLayout();
//
// prevButton
//
this.prevButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.prevButton.Enabled = false;
this.prevButton.Location = new System.Drawing.Point(3, 210);
this.prevButton.Name = "prevButton";
this.prevButton.Size = new System.Drawing.Size(145, 19);
this.prevButton.TabIndex = 1;
this.prevButton.Text = "Prev.";
this.prevButton.Theme = MetroFramework.MetroThemeStyle.Dark;
this.prevButton.UseSelectable = true;
this.prevButton.Click += new System.EventHandler(this.prevButton_Click);
//
// nextButton
//
this.nextButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.nextButton.Enabled = false;
this.nextButton.Location = new System.Drawing.Point(154, 210);
this.nextButton.Name = "nextButton";
this.nextButton.Size = new System.Drawing.Size(145, 19);
this.nextButton.TabIndex = 2;
this.nextButton.Text = "Next";
this.nextButton.Theme = MetroFramework.MetroThemeStyle.Dark;
this.nextButton.UseSelectable = true;
this.nextButton.Click += new System.EventHandler(this.nextButton_Click);
//
// menuStrip1
//
this.menuStrip1.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileToolStripMenuItem});
this.menuStrip1.Location = new System.Drawing.Point(20, 60);
this.menuStrip1.Name = "menuStrip1";
this.menuStrip1.Size = new System.Drawing.Size(759, 24);
this.menuStrip1.TabIndex = 16;
this.menuStrip1.Text = "menuStrip1";
//
// fileToolStripMenuItem
//
this.fileToolStripMenuItem.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.saveToolStripMenuItem,
this.extractTileToolStripMenuItem});
this.fileToolStripMenuItem.ForeColor = System.Drawing.SystemColors.Menu;
this.fileToolStripMenuItem.Name = "fileToolStripMenuItem";
this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20);
this.fileToolStripMenuItem.Text = "File";
//
// saveToolStripMenuItem
//
this.saveToolStripMenuItem.Name = "saveToolStripMenuItem";
this.saveToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.saveToolStripMenuItem.Text = "Save";
this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click);
//
// extractTileToolStripMenuItem
//
this.extractTileToolStripMenuItem.Name = "extractTileToolStripMenuItem";
this.extractTileToolStripMenuItem.Size = new System.Drawing.Size(180, 22);
this.extractTileToolStripMenuItem.Text = "Extract Tile";
this.extractTileToolStripMenuItem.Click += new System.EventHandler(this.extractTileToolStripMenuItem_Click);
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.AutoSize = true;
this.tableLayoutPanel1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
this.tableLayoutPanel1.ColumnCount = 3;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 60F));
this.tableLayoutPanel1.Controls.Add(this.prevButton, 0, 1);
this.tableLayoutPanel1.Controls.Add(this.nextButton, 1, 1);
this.tableLayoutPanel1.Controls.Add(this.originalPictureBox, 2, 0);
this.tableLayoutPanel1.Controls.Add(this.selectImagePictureBox, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.infoTextBox, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.replaceButton, 0, 4);
this.tableLayoutPanel1.Controls.Add(this.animationButton, 0, 3);
this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel1.Location = new System.Drawing.Point(20, 84);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 5;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 38.09524F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 4.761905F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 47.61905F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 4.761905F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 4.761905F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(759, 545);
this.tableLayoutPanel1.TabIndex = 17;
//
// originalPictureBox
//
this.originalPictureBox.BackColor = System.Drawing.Color.Transparent;
this.originalPictureBox.Dock = System.Windows.Forms.DockStyle.Fill;
this.originalPictureBox.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
this.originalPictureBox.Location = new System.Drawing.Point(305, 3);
this.originalPictureBox.Name = "originalPictureBox";
this.tableLayoutPanel1.SetRowSpan(this.originalPictureBox, 5);
this.originalPictureBox.Size = new System.Drawing.Size(451, 539);
this.originalPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.originalPictureBox.TabIndex = 4;
this.originalPictureBox.TabStop = false;
this.originalPictureBox.MouseClick += new System.Windows.Forms.MouseEventHandler(this.originalPictureBox_MouseClick);
//
// selectImagePictureBox
//
this.selectImagePictureBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.selectImagePictureBox.BackColor = System.Drawing.Color.Transparent;
this.tableLayoutPanel1.SetColumnSpan(this.selectImagePictureBox, 2);
this.selectImagePictureBox.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor;
this.selectImagePictureBox.Location = new System.Drawing.Point(3, 3);
this.selectImagePictureBox.Name = "selectImagePictureBox";
this.selectImagePictureBox.Size = new System.Drawing.Size(296, 201);
this.selectImagePictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.selectImagePictureBox.TabIndex = 0;
this.selectImagePictureBox.TabStop = false;
//
// infoTextBox
//
this.infoTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.infoTextBox.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(20)))), ((int)(((byte)(20)))), ((int)(((byte)(20)))));
this.infoTextBox.BorderStyle = System.Windows.Forms.BorderStyle.None;
this.tableLayoutPanel1.SetColumnSpan(this.infoTextBox, 2);
this.infoTextBox.Cursor = System.Windows.Forms.Cursors.Arrow;
this.infoTextBox.ForeColor = System.Drawing.SystemColors.Window;
this.infoTextBox.Location = new System.Drawing.Point(3, 235);
this.infoTextBox.Name = "infoTextBox";
this.infoTextBox.ReadOnly = true;
this.infoTextBox.Size = new System.Drawing.Size(296, 253);
this.infoTextBox.TabIndex = 15;
this.infoTextBox.TabStop = false;
this.infoTextBox.Text = "";
//
// replaceButton
//
this.replaceButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.replaceButton.AutoSize = true;
this.tableLayoutPanel1.SetColumnSpan(this.replaceButton, 2);
this.replaceButton.Location = new System.Drawing.Point(3, 519);
this.replaceButton.Name = "replaceButton";
this.replaceButton.Size = new System.Drawing.Size(296, 23);
this.replaceButton.TabIndex = 14;
this.replaceButton.Text = "Replace";
this.replaceButton.Theme = MetroFramework.MetroThemeStyle.Dark;
this.replaceButton.UseSelectable = true;
this.replaceButton.Click += new System.EventHandler(this.replaceButton_Click);
//
// animationButton
//
this.animationButton.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.animationButton.AutoSize = true;
this.tableLayoutPanel1.SetColumnSpan(this.animationButton, 2);
this.animationButton.Location = new System.Drawing.Point(3, 494);
this.animationButton.Name = "animationButton";
this.animationButton.Size = new System.Drawing.Size(296, 19);
this.animationButton.TabIndex = 16;
this.animationButton.Text = "Animation";
this.animationButton.Theme = MetroFramework.MetroThemeStyle.Dark;
this.animationButton.UseSelectable = true;
this.animationButton.Click += new System.EventHandler(this.animationButton_Click);
//
// TextureAtlasEditor
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(799, 649);
this.Controls.Add(this.tableLayoutPanel1);
this.Controls.Add(this.menuStrip1);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MainMenuStrip = this.menuStrip1;
this.Name = "TextureAtlasEditor";
this.ShadowType = MetroFramework.Forms.MetroFormShadowType.None;
this.Style = MetroFramework.MetroColorStyle.Silver;
this.Text = "Texture Atlas Editor";
this.Theme = MetroFramework.MetroThemeStyle.Dark;
this.menuStrip1.ResumeLayout(false);
this.menuStrip1.PerformLayout();
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.originalPictureBox)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.selectImagePictureBox)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private PictureBoxWithInterpolationMode selectImagePictureBox;
private MetroFramework.Controls.MetroButton prevButton;
private MetroFramework.Controls.MetroButton nextButton;
private System.Windows.Forms.MenuStrip menuStrip1;
private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private MetroFramework.Controls.MetroButton replaceButton;
private PictureBoxWithInterpolationMode originalPictureBox;
private System.Windows.Forms.ToolStripMenuItem saveToolStripMenuItem;
private System.Windows.Forms.RichTextBox infoTextBox;
private MetroFramework.Controls.MetroButton animationButton;
private System.Windows.Forms.ToolStripMenuItem extractTileToolStripMenuItem;
}
}

View File

@@ -0,0 +1,339 @@
/* Copyright (c) 2023-present miku-666
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1.The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software
* in a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
* 3. This notice may not be removed or altered from any source distribution.
**/
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;
using MetroFramework.Forms;
using OMI.Formats.Pck;
using PckStudio.Extensions;
using PckStudio.Forms.Editor;
using PckStudio.Forms.Utilities;
namespace PckStudio
{
internal partial class TextureAtlasEditor : MetroForm
{
public Image FinalTexture
{
get
{
if (DialogResult != DialogResult.OK)
return null;
return (Image)originalPictureBox.Image.Clone();
}
}
private struct SelectedTile
{
internal readonly int Index;
internal readonly string Name;
internal readonly string TextureName;
internal readonly Rectangle Area;
public SelectedTile(int index, string name, string textureName, Rectangle area)
{
Index = index;
Name = name;
TextureName = textureName;
Area = area;
}
}
private readonly PckFile _pckFile;
private readonly Size _areaSize;
private readonly int _rowCount;
private readonly int _columnCount;
private readonly string _atlasType;
private readonly List<Image> _textures;
private readonly List<AnimationResources.TileInfo> _textureInfos;
private SelectedTile _selectedItem = new SelectedTile();
private int SelectedIndex
{
set => SetImageDisplayed(value);
}
private const ImageLayoutDirection _imageLayout = ImageLayoutDirection.Horizontal;
public TextureAtlasEditor(PckFile pckFile, string path, Image atlas, Size areaSize)
{
InitializeComponent();
_areaSize = areaSize;
_pckFile = pckFile;
_rowCount = atlas.Width / areaSize.Width;
_columnCount = atlas.Height / areaSize.Height;
(_textureInfos, _atlasType) = Path.GetFileNameWithoutExtension(path) switch
{
"terrain" => (AnimationResources.BlockTileInfos, "blocks"),
"items" => (AnimationResources.ItemTileInfos, "items"),
_ => (null, null),
};
originalPictureBox.Image = atlas;
var images = atlas.CreateImageList(_areaSize, _imageLayout);
_textures = new List<Image>(images);
SelectedIndex = 0;
}
private void SetImageDisplayed(int index)
{
prevButton.Enabled = index > 0;
nextButton.Enabled = index < _textures.Count - 1;
infoTextBox.Text = string.Empty;
if (_textureInfos is not null && _textureInfos.IndexInRange(index))
{
var info = _textureInfos[index];
var pos = GetSelectedPoint(index, _rowCount, _columnCount, _imageLayout);
var selectedArea = new Rectangle(pos.X * _areaSize.Width, pos.Y * _areaSize.Height, _areaSize.Width, _areaSize.Height);
_selectedItem = new SelectedTile(index, info.DisplayName, info.InternalName, selectedArea);
infoTextBox.Text = $"{_selectedItem.Name}\n{_selectedItem.TextureName}";
animationButton.Text =
_pckFile.Files.Contains($"res/textures/{_atlasType}/{_selectedItem.TextureName}.png", PckFile.FileData.FileType.TextureFile)
? "Open Animation"
: "Create Animation";
}
if (_textures.IndexInRange(index))
{
selectImagePictureBox.Image = _textures[index];
}
}
private void prevButton_Click(object sender, EventArgs e)
{
SelectedIndex = _selectedItem.Index - 1;
}
private void nextButton_Click(object sender, EventArgs e)
{
SelectedIndex = _selectedItem.Index + 1;
}
private void originalPictureBox_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
var index = GetSelectedImageIndex(
originalPictureBox.Size,
originalPictureBox.Image.Size,
_areaSize,
e.Location,
originalPictureBox.SizeMode,
_imageLayout);
Debug.WriteLine(index);
if (index != -1)
{
SelectedIndex = index;
}
}
private static int GetSelectedImageIndex(
Size pictureBoxSize,
Size imageSize,
Size areaSize,
Point clickLocation,
PictureBoxSizeMode sizeMode,
ImageLayoutDirection imageLayout)
{
Point result = new Point();
int rowCount = imageSize.Width / areaSize.Width;
int columnCount = imageSize.Height / areaSize.Height;
switch (sizeMode)
{
case PictureBoxSizeMode.Normal:
case PictureBoxSizeMode.AutoSize:
{
var imageArea = new Rectangle(Point.Empty, imageSize);
if (!imageArea.Contains(clickLocation))
return -1;
result.X = clickLocation.X / areaSize.Width;
result.Y = clickLocation.Y / areaSize.Height;
break;
}
case PictureBoxSizeMode.StretchImage:
{
float widthDiff = (float)pictureBoxSize.Width / imageSize.Width;
float heightDiff = (float)pictureBoxSize.Height / imageSize.Height;
Size scaledArea = Size.Round(new SizeF(areaSize.Width * widthDiff, areaSize.Height * heightDiff));
result.X = clickLocation.X / scaledArea.Width;
result.Y = clickLocation.Y / scaledArea.Height;
break;
}
case PictureBoxSizeMode.CenterImage:
{
Rectangle imageArea = new Rectangle(Point.Empty, imageSize);
imageArea.X = (pictureBoxSize.Width - imageArea.Width) / 2;
imageArea.Y = (pictureBoxSize.Height - imageArea.Height) / 2;
if (!imageArea.Contains(clickLocation))
return -1;
result.X = (clickLocation.X - imageArea.X) / (clickLocation.X * areaSize.Width);
result.Y = (clickLocation.Y - imageArea.Y) / (clickLocation.Y * areaSize.Height);
break;
}
case PictureBoxSizeMode.Zoom:
{
Rectangle imageArea = new Rectangle();
float widthDiff = (float)pictureBoxSize.Width / imageSize.Width;
float heightDiff = (float)pictureBoxSize.Height / imageSize.Height;
float scale = Math.Min(widthDiff, heightDiff);
imageArea.Width = (int)(imageSize.Width * scale);
imageArea.Height = (int)(imageSize.Height * scale);
imageArea.X = (pictureBoxSize.Width - imageArea.Width) / 2;
imageArea.Y = (pictureBoxSize.Height - imageArea.Height) / 2;
if (!imageArea.Contains(clickLocation))
return -1;
Size scaledArea = Size.Round(new SizeF(areaSize.Width * scale, areaSize.Height * scale));
result.X = (clickLocation.X - imageArea.X) / scaledArea.Width;
result.Y = (clickLocation.Y - imageArea.Y) / scaledArea.Height;
}
break;
default:
break;
};
return GetSelectedIndex(result.X, result.Y, rowCount, columnCount, imageLayout);
}
private static int GetSelectedIndex(int x, int y, int rowCount, int columnCount, ImageLayoutDirection imageLayout)
{
return imageLayout switch
{
ImageLayoutDirection.Horizontal => x + y * rowCount,
ImageLayoutDirection.Vertical => y + x * columnCount,
_ => throw new ArgumentOutOfRangeException(nameof(imageLayout)),
};
}
private static Point GetSelectedPoint(int index, int rowCount, int columnCount, ImageLayoutDirection imageLayout)
{
int y = Math.DivRem(index, rowCount, out int x);
if (imageLayout == ImageLayoutDirection.Vertical)
x = Math.DivRem(index, columnCount, out y);
return new Point(x, y);
}
private void replaceButton_Click(object sender, EventArgs e)
{
OpenFileDialog fileDialog = new OpenFileDialog()
{
Filter = "PNG Image|*.png",
Title = "Select Texture"
};
if (fileDialog.ShowDialog() == DialogResult.OK)
{
var img = Image.FromFile(fileDialog.FileName);
SetTile(img);
}
}
private void SetTile(Image texture)
{
var graphicsConfig = new GraphicsConfig()
{
InterpolationMode = selectImagePictureBox.InterpolationMode,
PixelOffsetMode = PixelOffsetMode.HighQuality
};
if (texture.Size != _areaSize)
texture = texture.ResizeImage(_areaSize, graphicsConfig);
using (var g = Graphics.FromImage(originalPictureBox.Image))
{
g.ApplyConfig(graphicsConfig);
g.Fill(_selectedItem.Area, Color.Transparent);
g.DrawImage(texture, _selectedItem.Area);
}
_textures[_selectedItem.Index] = texture;
selectImagePictureBox.Image = texture;
originalPictureBox.Invalidate();
}
private void saveToolStripMenuItem_Click(object sender, EventArgs e)
{
DialogResult = DialogResult.OK;
}
private void animationButton_Click(object sender, EventArgs e)
{
bool isNewFile;
if (isNewFile = !_pckFile.Files.TryGetValue(
$"res/textures/{_atlasType}/{_selectedItem.TextureName}.png",
PckFile.FileData.FileType.TextureFile, out var file
))
{
file = new PckFile.FileData($"res/textures/{_atlasType}/{_selectedItem.TextureName}.png", PckFile.FileData.FileType.TextureFile);
}
var animationEditor = new AnimationEditor(file);
if (animationEditor.ShowDialog() == DialogResult.OK && isNewFile)
{
_pckFile.Files.Add(file);
}
}
private void extractTileToolStripMenuItem_Click(object sender, EventArgs e)
{
SaveFileDialog saveFileDialog = new SaveFileDialog()
{
Filter = "Tile Texture|*.png",
FileName = _selectedItem.TextureName
};
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
selectImagePictureBox.Image.Save(saveFileDialog.FileName, ImageFormat.Png);
}
}
protected override bool ProcessDialogKey(Keys keyData)
{
switch (keyData)
{
case Keys.Left:
if (prevButton.Enabled)
{
prevButton_Click(this, EventArgs.Empty);
return true;
}
break;
case Keys.Right:
if (nextButton.Enabled)
{
nextButton_Click(this, EventArgs.Empty);
return true;
}
break;
}
return false;
// return base.ProcessDialogKey(keyData);
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -367,14 +367,35 @@ namespace PckStudio
private void HandleTextureFile(PckFile.FileData file)
{
if (!(file.Filename.StartsWith("res/textures/blocks/") || file.Filename.StartsWith("res/textures/items/")))
return;
_ = file.IsMipmappedFile() && currentPCK.Files.TryGetValue(file.GetNormalPath(), PckFile.FileData.FileType.TextureFile, out file);
if (file.IsMipmappedFile() && currentPCK.Files.TryGetValue(file.GetNormalPath(), PckFile.FileData.FileType.TextureFile, out PckFile.FileData originalAnimationFile))
if (file.Size <= 0)
{
file = originalAnimationFile;
Debug.WriteLine($"'{file.Filename}' size is 0.", category: nameof(HandleTextureFile));
return;
}
if (file.Filename == "res/terrain.png" || file.Filename == "res/items.png")
{
using var ms = new MemoryStream(file.Data);
var img = Image.FromStream(ms);
var res = img.Width / 16; // texture count on X axes
var size = new Size(res, res);
var viewer = new TextureAtlasEditor(currentPCK, file.Filename, img, size);
if (viewer.ShowDialog() == DialogResult.OK)
{
using (var result = new MemoryStream())
{
viewer.FinalTexture.Save(result, ImageFormat.Png);
file.SetData(result.ToArray());
}
}
return;
}
if (file.Filename.StartsWith("res/textures/blocks/") || file.Filename.StartsWith("res/textures/items/"))
{
using (AnimationEditor animationEditor = new AnimationEditor(file))
{
if (animationEditor.ShowDialog(this) == DialogResult.OK)
@@ -383,6 +404,8 @@ namespace PckStudio
BuildMainTreeView();
}
}
return;
}
}
private void HandleGameRuleFile(PckFile.FileData file)

View File

@@ -196,6 +196,12 @@
<DependentUpon>AppSettingsForm.cs</DependentUpon>
</Compile>
<Compile Include="Internal\ApplicationBuildInfo.cs" />
<Compile Include="Forms\Editor\TextureAtlasEditor.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\Editor\TextureAtlasEditor.Designer.cs">
<DependentUpon>TextureAtlasEditor.cs</DependentUpon>
</Compile>
<Compile Include="Classes\API\PCKCenter\model\PCKCenterJSON.cs" />
<Compile Include="Classes\API\PCKCenter\PCKCollections.cs" />
<Compile Include="Classes\API\PCKCenter\PCKCollectionsLocal.cs" />
@@ -594,6 +600,9 @@
<DependentUpon>AppSettingsForm.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Forms\Editor\TextureAtlasEditor.resx">
<DependentUpon>TextureAtlasEditor.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="MainForm.ja.resx">
<DependentUpon>MainForm.cs</DependentUpon>
<SubType>Designer</SubType>