mirror of
https://git.huckle.dev/Huckles-Minecraft-Archive/PCK-Studio.git
synced 2026-06-03 12:05:48 +00:00
Move Common functionality to Core project & rendering and Model support as well
This commit is contained in:
24
PckStudio.Core/Extensions/AnimationExtensions.cs
Normal file
24
PckStudio.Core/Extensions/AnimationExtensions.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System.Drawing;
|
||||
using AnimatedGif;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class AnimationExtensions
|
||||
{
|
||||
public static Image CreateAnimationImage(this Animation animation)
|
||||
{
|
||||
if (animation.FrameCount == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var ms = new System.IO.MemoryStream();
|
||||
var generateor = new AnimatedGifCreator(ms, GameConstants.GameTickInMilliseconds, 0);
|
||||
foreach (Animation.Frame frame in animation.GetInterpolatedFrames())
|
||||
{
|
||||
generateor.AddFrame(frame.Texture, frame.Ticks * GameConstants.GameTickInMilliseconds, GifQuality.Bit8);
|
||||
}
|
||||
ms.Position = 0;
|
||||
return Image.FromStream(ms);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
PckStudio.Core/Extensions/BlendMode.cs
Normal file
14
PckStudio.Core/Extensions/BlendMode.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public enum BlendMode
|
||||
{
|
||||
Add,
|
||||
Subtract,
|
||||
Multiply,
|
||||
Average,
|
||||
DescendingOrder,
|
||||
AscendingOrder,
|
||||
Screen,
|
||||
Overlay
|
||||
}
|
||||
}
|
||||
16
PckStudio.Core/Extensions/BoundingBoxExtensions.cs
Normal file
16
PckStudio.Core/Extensions/BoundingBoxExtensions.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class BoundingBoxExtensions
|
||||
{
|
||||
public static BoundingBox GetEnclosingBoundingBox(this IEnumerable<BoundingBox> boundingBoxes)
|
||||
{
|
||||
return boundingBoxes.DefaultIfEmpty().Aggregate((a, b) => new BoundingBox(OpenTK.Vector3.ComponentMin(a.Start, b.Start), OpenTK.Vector3.ComponentMax(a.End, b.End)));
|
||||
}
|
||||
}
|
||||
}
|
||||
73
PckStudio.Core/Extensions/ColorExtensions.cs
Normal file
73
PckStudio.Core/Extensions/ColorExtensions.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
using System.Drawing;
|
||||
using System.Numerics;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class ColorExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Normalizes the Color between 0.0 - 1.0
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static Vector4 Normalize(this Color color)
|
||||
{
|
||||
return new Vector4(color.R / 255f, color.G / 255f, color.B / 255f, color.A / 255f);
|
||||
}
|
||||
|
||||
public static Color Inversed(this Color color)
|
||||
{
|
||||
return Color.FromArgb(color.A, 255 - color.R, 255 - color.G, 255 - color.B);
|
||||
}
|
||||
|
||||
public static Color GreyScaled(this Color color)
|
||||
{
|
||||
int greyScaleValue = (color.R + color.G + color.B) / 3;
|
||||
return Color.FromArgb(color.A, greyScaleValue, greyScaleValue, greyScaleValue);
|
||||
}
|
||||
|
||||
public static int ToBGR(this Color color)
|
||||
{
|
||||
return color.B << 16 | color.G << 8 | color.R;
|
||||
}
|
||||
|
||||
public static byte BlendValues(byte source, byte overlay, BlendMode blendType)
|
||||
{
|
||||
return (byte)MathExtensions.Clamp(BlendValues(source / 255f, overlay / 255f, blendType) * 255, 0, 255);
|
||||
}
|
||||
|
||||
public 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);
|
||||
float resultValue = blendType switch
|
||||
{
|
||||
BlendMode.Add => source + overlay,
|
||||
BlendMode.Subtract => source - overlay,
|
||||
BlendMode.Multiply => source * overlay,
|
||||
BlendMode.Average => (source + overlay) / 2.0f,
|
||||
BlendMode.AscendingOrder => source > overlay ? overlay : source,
|
||||
BlendMode.DescendingOrder => source < overlay ? overlay : source,
|
||||
BlendMode.Screen => 1f - (1f - source) * (1f - overlay),
|
||||
BlendMode.Overlay => source < 0.5f ? 2f * source * overlay : 1f - 2f * (1f - source) * (1f - overlay),
|
||||
_ => 0.0f
|
||||
};
|
||||
return MathExtensions.Clamp(resultValue, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
public static byte Mix(double ratio, byte val1, byte val2)
|
||||
{
|
||||
ratio = MathExtensions.Clamp(ratio, 0.0, 1.0);
|
||||
return (byte)(ratio * val1 + (1.0 - ratio) * val2);
|
||||
}
|
||||
|
||||
public static Color Mix(this Color c1, Color c2, double ratio)
|
||||
{
|
||||
ratio = MathExtensions.Clamp(ratio, 0.0, 1.0);
|
||||
return Color.FromArgb(c1.A,
|
||||
Mix(ratio, c1.R, c2.R),
|
||||
Mix(ratio, c1.G, c2.G),
|
||||
Mix(ratio, c1.B, c2.B)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
51
PckStudio.Core/Extensions/CursorExtensions.cs
Normal file
51
PckStudio.Core/Extensions/CursorExtensions.cs
Normal file
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class CursorExtensions
|
||||
{
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct PointStruct
|
||||
{
|
||||
public Int32 x;
|
||||
public Int32 y;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
struct CursorInfoStruct
|
||||
{
|
||||
/// <summary> The structure size in bytes that must be set via calling Marshal.SizeOf(typeof(CursorInfoStruct)).</summary>
|
||||
public Int32 cbSize;
|
||||
/// <summary> The cursor state: 0 == hidden, 1 == showing, 2 == suppressed (is supposed to be when finger touch is used, but in practice finger touch results in 0, not 2)</summary>
|
||||
public Int32 flags;
|
||||
/// <summary> A handle to the cursor. </summary>
|
||||
public IntPtr hCursor;
|
||||
/// <summary> The cursor screen coordinates.</summary>
|
||||
public PointStruct pt;
|
||||
}
|
||||
|
||||
/// <summary> Must initialize cbSize</summary>
|
||||
[DllImport("user32.dll")]
|
||||
static extern bool GetCursorInfo(ref CursorInfoStruct pci);
|
||||
|
||||
public static bool IsVisible(this Cursor _)
|
||||
{
|
||||
CursorInfoStruct pci = new CursorInfoStruct();
|
||||
pci.cbSize = Marshal.SizeOf(typeof(CursorInfoStruct));
|
||||
GetCursorInfo(ref pci);
|
||||
// const Int32 hidden = 0x00;
|
||||
const Int32 showing = 0x01;
|
||||
// const Int32 suppressed = 0x02;
|
||||
bool isVisible = ((pci.flags & showing) != 0);
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
38
PckStudio.Core/Extensions/EnumerableExtensions.cs
Normal file
38
PckStudio.Core/Extensions/EnumerableExtensions.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
public static IEnumerable<(int index, T value)>enumerate<T>(this IEnumerable<T> array)
|
||||
{
|
||||
int i = 0;
|
||||
foreach (T item in array)
|
||||
{
|
||||
yield return (i++, item);
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
public static bool EqualsAny<T>(this T type, params T[] items)
|
||||
{
|
||||
foreach (T item in items)
|
||||
{
|
||||
if (item.Equals(type))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool ContainsAny<T>(this IEnumerable<T> array, params T[] items)
|
||||
{
|
||||
foreach (T item in array)
|
||||
{
|
||||
if (items.Contains(item))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
44
PckStudio.Core/Extensions/GraphicsExtensions.cs
Normal file
44
PckStudio.Core/Extensions/GraphicsExtensions.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public struct GraphicsConfig
|
||||
{
|
||||
public GraphicsConfig()
|
||||
{
|
||||
CompositingQuality = default;
|
||||
InterpolationMode = default;
|
||||
SmoothingMode = default;
|
||||
PixelOffsetMode = default;
|
||||
CompositingMode = default;
|
||||
}
|
||||
|
||||
public CompositingMode CompositingMode { get; set; }
|
||||
public CompositingQuality CompositingQuality { get; set; }
|
||||
public InterpolationMode InterpolationMode { get; set; }
|
||||
public SmoothingMode SmoothingMode { get; set; }
|
||||
public PixelOffsetMode PixelOffsetMode { get; set; }
|
||||
}
|
||||
|
||||
public static class GraphicsExtensions
|
||||
{
|
||||
public static void ApplyConfig(this Graphics graphics, GraphicsConfig config)
|
||||
{
|
||||
graphics.CompositingMode = config.CompositingMode;
|
||||
graphics.CompositingQuality = config.CompositingQuality;
|
||||
graphics.InterpolationMode = config.InterpolationMode;
|
||||
graphics.SmoothingMode = config.SmoothingMode;
|
||||
graphics.PixelOffsetMode = config.PixelOffsetMode;
|
||||
}
|
||||
|
||||
public static Graphics Fill(this Graphics graphics, Rectangle area, Color color)
|
||||
{
|
||||
Region clip = graphics.Clip;
|
||||
graphics.SetClip(area, CombineMode.Replace);
|
||||
graphics.Clear(color);
|
||||
graphics.SetClip(clip, CombineMode.Replace);
|
||||
return graphics;
|
||||
}
|
||||
}
|
||||
}
|
||||
273
PckStudio.Core/Extensions/ImageExtensions.cs
Normal file
273
PckStudio.Core/Extensions/ImageExtensions.cs
Normal file
@@ -0,0 +1,273 @@
|
||||
/* 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.Drawing;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class ImageExtensions
|
||||
{
|
||||
public static Image ReleaseFromFile(this Image image)
|
||||
{
|
||||
Image img = new Bitmap(image);
|
||||
image.Dispose();
|
||||
return img;
|
||||
}
|
||||
|
||||
public static Image GetArea(this Image source, Rectangle area)
|
||||
{
|
||||
Image tileImage = new Bitmap(area.Width, area.Height);
|
||||
using (Graphics gfx = Graphics.FromImage(tileImage))
|
||||
{
|
||||
gfx.SmoothingMode = SmoothingMode.None;
|
||||
gfx.InterpolationMode = InterpolationMode.NearestNeighbor;
|
||||
gfx.PixelOffsetMode = PixelOffsetMode.HighQuality;
|
||||
gfx.DrawImage(source, new Rectangle(Point.Empty, area.Size), area, GraphicsUnit.Pixel);
|
||||
}
|
||||
return tileImage;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an IEnumerable by reading in horizontal order
|
||||
/// </summary>
|
||||
/// <param name="source">this image</param>
|
||||
/// <param name="scalar">Indecates width and height of image sub section</param>
|
||||
/// <returns><see cref="IEnumerable{Image}"/> of type <see cref="Image"/></returns>
|
||||
public static IEnumerable<Image> SplitHorizontal(this Image source, int scalar)
|
||||
{
|
||||
return source.Split(scalar, ImageLayoutDirection.Horizontal);
|
||||
}
|
||||
|
||||
public static IEnumerable<Image> Split(this Image source, int scalar, ImageLayoutDirection layoutDirection)
|
||||
{
|
||||
return Split(source, new Size(scalar, scalar), layoutDirection);
|
||||
}
|
||||
|
||||
public static IEnumerable<Image> Split(this Image source, Size size, ImageLayoutDirection imageLayout)
|
||||
{
|
||||
int rowCount = source.Width / size.Width;
|
||||
int columnCount = source.Height / size.Height;
|
||||
Debug.WriteLine($"Image size: {source.Size}, Area size: {size}, col num: {columnCount}, row num: {rowCount}");
|
||||
for (int i = 0; i < columnCount * rowCount; i++)
|
||||
{
|
||||
int row = Math.DivRem(i, rowCount, out int column);
|
||||
if (imageLayout == ImageLayoutDirection.Vertical)
|
||||
column = Math.DivRem(i, columnCount, out row);
|
||||
Rectangle tileArea = new Rectangle(new Point(column * size.Width, row * size.Height), size);
|
||||
yield return source.GetArea(tileArea);
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
public static IEnumerable<Image> Split(this Image source, ImageLayoutDirection layoutDirection)
|
||||
{
|
||||
for (int i = 0; i < source.Height / source.Width; i++)
|
||||
{
|
||||
ImageSection locationInfo = new ImageSection(source.Size, i, layoutDirection);
|
||||
yield return source.GetArea(locationInfo.Area);
|
||||
}
|
||||
yield break;
|
||||
}
|
||||
|
||||
public static Image Combine(this IEnumerable<Image> sources, ImageLayoutDirection layoutDirection)
|
||||
{
|
||||
Size imageSize = CalculateImageSize(sources, layoutDirection);
|
||||
var image = new Bitmap(imageSize.Width, imageSize.Height);
|
||||
|
||||
using (var graphic = Graphics.FromImage(image))
|
||||
{
|
||||
foreach ((int i, Image texture) in sources.enumerate())
|
||||
{
|
||||
var info = new ImageSection(texture.Size, i, layoutDirection);
|
||||
graphic.DrawImage(texture, info.Point);
|
||||
}
|
||||
}
|
||||
return image;
|
||||
}
|
||||
|
||||
private static Size CalculateImageSize(IEnumerable<Image> sources, ImageLayoutDirection layoutDirection)
|
||||
{
|
||||
Size size = sources.First().Size;
|
||||
int count = sources.Count();
|
||||
|
||||
if (count < 2)
|
||||
return count < 1 ? Size.Empty : size;
|
||||
|
||||
var horizontal = layoutDirection == ImageLayoutDirection.Horizontal;
|
||||
|
||||
if (!sources.All(img => img.Size == size))
|
||||
throw new InvalidOperationException("Images must have the same width and height.");
|
||||
|
||||
if (horizontal)
|
||||
size.Width *= count;
|
||||
else
|
||||
size.Height *= count;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
public static Image Resize(this Image image, Size size, GraphicsConfig graphicsConfig)
|
||||
{
|
||||
return image.Resize(size.Width, size.Height, graphicsConfig);
|
||||
}
|
||||
|
||||
public static Image Resize(this Image image, int width, int height, GraphicsConfig graphicsConfig)
|
||||
{
|
||||
var destRect = new Rectangle(0, 0, width, height);
|
||||
var destImage = new Bitmap(width, height);
|
||||
|
||||
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
|
||||
|
||||
using (var graphics = Graphics.FromImage(destImage))
|
||||
{
|
||||
graphics.ApplyConfig(graphicsConfig);
|
||||
using (var wrapMode = new ImageAttributes())
|
||||
{
|
||||
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
|
||||
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
|
||||
}
|
||||
}
|
||||
return destImage;
|
||||
}
|
||||
|
||||
public static Image Blend(this Image image, Color overlayColor, BlendMode mode)
|
||||
{
|
||||
if (image is not Bitmap baseImage)
|
||||
return image;
|
||||
|
||||
Bitmap bitmapResult = new Bitmap(baseImage.Width, baseImage.Height, PixelFormat.Format32bppArgb);
|
||||
|
||||
BitmapData baseImageData = baseImage.LockBits(new Rectangle(Point.Empty, baseImage.Size),
|
||||
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
BitmapData resultImageData = bitmapResult.LockBits(new Rectangle(Point.Empty, bitmapResult.Size),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
Parallel.For(0, baseImageData.Stride * baseImageData.Height / 4, (i) =>
|
||||
{
|
||||
int k = i * 4;
|
||||
unsafe
|
||||
{
|
||||
int color = Unsafe.Read<int>((baseImageData.Scan0 + k).ToPointer());
|
||||
byte a = (byte)(color >> 24 & 0xff);
|
||||
if (a == 0)
|
||||
{
|
||||
Unsafe.Write((resultImageData.Scan0 + k).ToPointer(), 0);
|
||||
return;
|
||||
}
|
||||
var b = ColorExtensions.BlendValues((byte)(color >> 0 & 0xff), overlayColor.B, mode);
|
||||
var g = ColorExtensions.BlendValues((byte)(color >> 8 & 0xff), overlayColor.G, mode);
|
||||
var r = ColorExtensions.BlendValues((byte)(color >> 16 & 0xff), overlayColor.R, mode);
|
||||
int blendedValue = a << 24 | r << 16 | g << 8 | b;
|
||||
Unsafe.Write((resultImageData.Scan0 + k).ToPointer(), blendedValue);
|
||||
}
|
||||
});
|
||||
|
||||
bitmapResult.UnlockBits(resultImageData);
|
||||
baseImage.UnlockBits(baseImageData);
|
||||
return bitmapResult;
|
||||
}
|
||||
|
||||
public static Image Blend(this Image image, Image overlay, BlendMode mode)
|
||||
{
|
||||
if (image is not Bitmap baseImage || overlay is not Bitmap overlayImage ||
|
||||
image.Width != overlay.Width || image.Height != overlay.Height)
|
||||
return image;
|
||||
|
||||
BitmapData baseImageData = baseImage.LockBits(new Rectangle(Point.Empty, baseImage.Size),
|
||||
ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
|
||||
byte[] baseImageBuffer = new byte[baseImageData.Stride * baseImageData.Height];
|
||||
|
||||
Marshal.Copy(baseImageData.Scan0, baseImageBuffer, 0, baseImageBuffer.Length);
|
||||
|
||||
BitmapData overlayImageData = overlayImage.LockBits(new Rectangle(Point.Empty, overlayImage.Size),
|
||||
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
byte[] overlayImageBuffer = new byte[overlayImageData.Stride * overlayImageData.Height];
|
||||
|
||||
Marshal.Copy(overlayImageData.Scan0, overlayImageBuffer, 0, overlayImageBuffer.Length);
|
||||
|
||||
for (int k = 0; k < baseImageBuffer.Length && k < overlayImageBuffer.Length; k += 4)
|
||||
{
|
||||
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);
|
||||
BitmapData resultImageData = bitmapResult.LockBits(new Rectangle(Point.Empty, bitmapResult.Size),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
Marshal.Copy(baseImageBuffer, 0, resultImageData.Scan0, baseImageBuffer.Length);
|
||||
|
||||
bitmapResult.UnlockBits(resultImageData);
|
||||
baseImage.UnlockBits(baseImageData);
|
||||
overlayImage.UnlockBits(overlayImageData);
|
||||
return bitmapResult;
|
||||
}
|
||||
|
||||
public static Image Interpolate(this Image image1, Image image2, double delta)
|
||||
{
|
||||
delta = MathExtensions.Clamp(delta, 0.0, 1.0);
|
||||
if (image1 is not Bitmap baseImage || image2 is not Bitmap overlayImage ||
|
||||
image1.Width != image2.Width || image1.Height != image2.Height)
|
||||
return image1;
|
||||
|
||||
BitmapData baseImageData = baseImage.LockBits(new Rectangle(Point.Empty, baseImage.Size),
|
||||
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
byte[] baseImageBuffer = new byte[baseImageData.Stride * baseImageData.Height];
|
||||
|
||||
Marshal.Copy(baseImageData.Scan0, baseImageBuffer, 0, baseImageBuffer.Length);
|
||||
|
||||
baseImage.UnlockBits(baseImageData);
|
||||
|
||||
BitmapData overlayImageData = overlayImage.LockBits(new Rectangle(Point.Empty, overlayImage.Size),
|
||||
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
byte[] overlayImageBuffer = new byte[overlayImageData.Stride * overlayImageData.Height];
|
||||
|
||||
Marshal.Copy(overlayImageData.Scan0, overlayImageBuffer, 0, overlayImageBuffer.Length);
|
||||
|
||||
overlayImage.UnlockBits(overlayImageData);
|
||||
|
||||
byte[] finalBuffer = new byte[baseImageData.Stride * baseImageData.Height];
|
||||
for (int k = 0; k < baseImageBuffer.Length && k < overlayImageBuffer.Length; k += 4)
|
||||
{
|
||||
finalBuffer[k + 0] = ColorExtensions.Mix(delta, baseImageBuffer[k + 0], overlayImageBuffer[k + 0]);
|
||||
finalBuffer[k + 1] = ColorExtensions.Mix(delta, baseImageBuffer[k + 1], overlayImageBuffer[k + 1]);
|
||||
finalBuffer[k + 2] = ColorExtensions.Mix(delta, baseImageBuffer[k + 2], overlayImageBuffer[k + 2]);
|
||||
finalBuffer[k + 3] = ColorExtensions.Mix(delta, baseImageBuffer[k + 3], overlayImageBuffer[k + 3]);
|
||||
}
|
||||
|
||||
Bitmap bitmapResult = new Bitmap(baseImage.Width, baseImage.Height, PixelFormat.Format32bppArgb);
|
||||
BitmapData resultImageData = bitmapResult.LockBits(new Rectangle(Point.Empty, bitmapResult.Size),
|
||||
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
|
||||
|
||||
Marshal.Copy(finalBuffer, 0, resultImageData.Scan0, finalBuffer.Length);
|
||||
|
||||
bitmapResult.UnlockBits(resultImageData);
|
||||
return bitmapResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
8
PckStudio.Core/Extensions/ImageLayoutDirection.cs
Normal file
8
PckStudio.Core/Extensions/ImageLayoutDirection.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace PckStudio.Core
|
||||
{
|
||||
public enum ImageLayoutDirection
|
||||
{
|
||||
Horizontal,
|
||||
Vertical
|
||||
}
|
||||
}
|
||||
37
PckStudio.Core/Extensions/ImageSection.cs
Normal file
37
PckStudio.Core/Extensions/ImageSection.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using System.Drawing;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
internal struct ImageSection
|
||||
{
|
||||
public readonly Size Size;
|
||||
public readonly Point Point;
|
||||
public readonly Rectangle Area;
|
||||
|
||||
internal ImageSection(Size originalSize, int index, ImageLayoutDirection layoutDirection)
|
||||
{
|
||||
switch(layoutDirection)
|
||||
{
|
||||
case ImageLayoutDirection.Horizontal:
|
||||
{
|
||||
Size = new Size(originalSize.Height, originalSize.Height);
|
||||
Point = new Point(index * originalSize.Height, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case ImageLayoutDirection.Vertical:
|
||||
{
|
||||
Size = new Size(originalSize.Width, originalSize.Width);
|
||||
Point = new Point(0, index * originalSize.Width);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Size = Size.Empty;
|
||||
Point = new Point(-1, -1);
|
||||
break;
|
||||
}
|
||||
Area = new Rectangle(Point, Size);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
PckStudio.Core/Extensions/ListExtensions.cs
Normal file
20
PckStudio.Core/Extensions/ListExtensions.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class ListExtensions
|
||||
{
|
||||
public static IList<T> Swap<T>(this IList<T> list, int index1, int index2)
|
||||
{
|
||||
T temp = list[index1];
|
||||
list[index1] = list[index2];
|
||||
list[index2] = temp;
|
||||
return list;
|
||||
}
|
||||
|
||||
public static bool IndexInRange<T>(this IList<T> list, int index)
|
||||
{
|
||||
return index >= 0 && index < list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
17
PckStudio.Core/Extensions/LocFileExtensions.cs
Normal file
17
PckStudio.Core/Extensions/LocFileExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using OMI.Formats.Languages;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class LocFileExtensions
|
||||
{
|
||||
|
||||
public static void InitializeDefault(this LOCFile locFile, string packName) => locFile.Initialize("en-EN", ("IDS_DISPLAY_NAME", packName));
|
||||
|
||||
public static void Initialize(this LOCFile locFile, string language, params (string, string)[] locKeyValuePairs)
|
||||
{
|
||||
locFile.AddLanguage(language);
|
||||
foreach ((string, string) locKeyValue in locKeyValuePairs)
|
||||
locFile.AddLocKey(locKeyValue.Item1, locKeyValue.Item2);
|
||||
}
|
||||
}
|
||||
}
|
||||
37
PckStudio.Core/Extensions/MaterialContainerExtensions.cs
Normal file
37
PckStudio.Core/Extensions/MaterialContainerExtensions.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using OMI.Formats.Material;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class MaterialContainerExtensions
|
||||
{
|
||||
private static readonly MaterialContainer.Material[] defaultMaterials = [
|
||||
new MaterialContainer.Material("bat", "entity_alphatest"),
|
||||
new MaterialContainer.Material("ender_dragon", "entity_emissive_alpha"),
|
||||
new MaterialContainer.Material("blaze_head", "entity_emissive_alpha"),
|
||||
new MaterialContainer.Material("drowned", "entity_emissive_alpha"),
|
||||
new MaterialContainer.Material("enderman", "entity_emissive_alpha"),
|
||||
new MaterialContainer.Material("enderman_invisible", "entity_emissive_alpha_only"),
|
||||
new MaterialContainer.Material("ghast", "entity_emissive_alpha"),
|
||||
new MaterialContainer.Material("guardian", "entity_alphatest"),
|
||||
new MaterialContainer.Material("magma_cube", "entity_emissive_alpha"),
|
||||
new MaterialContainer.Material("zombie_pigman", "entity"),
|
||||
new MaterialContainer.Material("phantom", "entity_emissive_alpha"),
|
||||
new MaterialContainer.Material("phantom_invisible", "entity_emissive_alpha_only"),
|
||||
new MaterialContainer.Material("sheep", "entity_change_color"),
|
||||
new MaterialContainer.Material("shulker", "entity_change_color"),
|
||||
new MaterialContainer.Material("skeleton", "entity_alphatest"),
|
||||
new MaterialContainer.Material("spider", "entity_emissive_alpha"),
|
||||
new MaterialContainer.Material("spider_invisible", "entity_emissive_alpha_only"),
|
||||
new MaterialContainer.Material("stray", "entity_alphatest"),
|
||||
new MaterialContainer.Material("iron_golem", "entity_alphatest"),
|
||||
new MaterialContainer.Material("wither_boss", "entity_alphatest"),
|
||||
new MaterialContainer.Material("wither_skeleton", "entity_alphatest"),
|
||||
new MaterialContainer.Material("wolf", "entity_alphatest_change_color")
|
||||
];
|
||||
|
||||
public static void InitializeDefault(this MaterialContainer materials)
|
||||
{
|
||||
materials.AddRange(defaultMaterials);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
PckStudio.Core/Extensions/MaterialExtensions.cs
Normal file
17
PckStudio.Core/Extensions/MaterialExtensions.cs
Normal file
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OMI.Formats.Material;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class MaterialExtensions
|
||||
{
|
||||
public static bool HasInvalidEntries(this MaterialContainer materials)
|
||||
{
|
||||
return materials.Any(mat => !MaterialContainer.SupportedEntities.Contains(mat.Name) || !MaterialContainer.ValidMaterialTypes.Contains(mat.Type));
|
||||
}
|
||||
}
|
||||
}
|
||||
16
PckStudio.Core/Extensions/MathExtensions.cs
Normal file
16
PckStudio.Core/Extensions/MathExtensions.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public class MathExtensions
|
||||
{
|
||||
public static T Clamp<T>(T value, T min, T max) where T : IComparable<T>
|
||||
{
|
||||
if (value.CompareTo(min) < 0)
|
||||
return min;
|
||||
if (value.CompareTo(max) > 0)
|
||||
return max;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
20
PckStudio.Core/Extensions/ModelBoxExtension.cs
Normal file
20
PckStudio.Core/Extensions/ModelBoxExtension.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
using OMI.Formats.Model;
|
||||
using System.Numerics;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class ModelBoxExtension
|
||||
{
|
||||
public static BoundingBox GetBoundingBox(this ModelBox modelBox)
|
||||
{
|
||||
Vector3 halfSize = modelBox.Size / 2f;
|
||||
Vector3 halfSizeInflated = new Vector3(modelBox.Inflate) + halfSize;
|
||||
Vector3 transformedCenter = modelBox.Position + halfSize;
|
||||
Vector3 start = transformedCenter - halfSizeInflated;
|
||||
Vector3 end = transformedCenter + halfSizeInflated;
|
||||
return new BoundingBox(start, end);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
25
PckStudio.Core/Extensions/OpenTKExtensions.cs
Normal file
25
PckStudio.Core/Extensions/OpenTKExtensions.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OpenTK;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class OpenTKExtensions
|
||||
{
|
||||
public static Matrix4 Pivoted(this Matrix4 rotation, Vector3 pivot)
|
||||
{
|
||||
var model = Matrix4.CreateTranslation(pivot);
|
||||
model *= rotation;
|
||||
model *= Matrix4.CreateTranslation(pivot).Inverted();
|
||||
return model;
|
||||
}
|
||||
|
||||
public static Vector3 Abs(Vector3 value)
|
||||
{
|
||||
return new Vector3(Math.Abs(value.X), Math.Abs(value.Y), Math.Abs(value.Z));
|
||||
}
|
||||
}
|
||||
}
|
||||
226
PckStudio.Core/Extensions/PckAssetExtensions.cs
Normal file
226
PckStudio.Core/Extensions/PckAssetExtensions.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Drawing;
|
||||
using System.Diagnostics;
|
||||
using System.Collections.Generic;
|
||||
|
||||
using OMI.Formats.Languages;
|
||||
using OMI.Formats.Pck;
|
||||
using OMI.Workers;
|
||||
|
||||
using PckStudio.Interfaces;
|
||||
using PckStudio.Core.Deserializer;
|
||||
using PckStudio.Core.Serializer;
|
||||
using PckStudio.Core.Skin;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class PckAssetExtensions
|
||||
{
|
||||
private const string MipMap = "MipMapLevel";
|
||||
|
||||
public static PckAsset CreateNewAssetIf(this PckFile pck, bool condition, string filename, PckAssetType filetype, IDataFormatWriter writer)
|
||||
{
|
||||
if (condition)
|
||||
{
|
||||
return pck.CreateNewAsset(filename, filetype, writer);
|
||||
}
|
||||
return default;
|
||||
}
|
||||
|
||||
public static PckAsset CreateNewAsset(this PckFile pck, string filename, PckAssetType filetype, IDataFormatWriter writer)
|
||||
{
|
||||
PckAsset asset = pck.CreateNewAsset(filename, filetype);
|
||||
asset.SetData(writer);
|
||||
return asset;
|
||||
}
|
||||
|
||||
public static Image GetTexture(this PckAsset asset)
|
||||
{
|
||||
if (asset.Type != PckAssetType.SkinFile &&
|
||||
asset.Type != PckAssetType.CapeFile &&
|
||||
asset.Type != PckAssetType.TextureFile)
|
||||
{
|
||||
throw new Exception("Asset is not suitable to contain image data.");
|
||||
}
|
||||
return asset.GetDeserializedData(ImageDeserializer.DefaultDeserializer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to get the skin id of the skin <paramref name="asset"/>
|
||||
/// </summary>
|
||||
/// <param name="asset"></param>
|
||||
/// <returns>Non-zero base number on success, otherwise 0</returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public static int GetSkinId(this PckAsset asset)
|
||||
{
|
||||
if (asset.Type != PckAssetType.SkinFile)
|
||||
throw new InvalidOperationException("Asset is not a skin.");
|
||||
|
||||
const string skinAssetnamePrefix = "dlcskin";
|
||||
|
||||
string assetPath = Path.GetFileNameWithoutExtension(asset.Filename);
|
||||
if (!assetPath.StartsWith(skinAssetnamePrefix))
|
||||
{
|
||||
Trace.TraceWarning($"[{nameof(GetSkinId)}] Asset name does not start with '{skinAssetnamePrefix}'");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int skinId = 0;
|
||||
if (!int.TryParse(assetPath.Substring(skinAssetnamePrefix.Length), out skinId))
|
||||
{
|
||||
Trace.TraceWarning($"[{nameof(GetSkinId)}] Failed to parse Skin Id");
|
||||
}
|
||||
return skinId;
|
||||
}
|
||||
|
||||
public static Skin.Skin GetSkin(this PckAsset asset)
|
||||
{
|
||||
if (asset.Type != PckAssetType.SkinFile)
|
||||
throw new InvalidOperationException("Asset is not a skin.");
|
||||
|
||||
int skinId = asset.GetSkinId();
|
||||
|
||||
string name = asset.GetProperty("DISPLAYNAME");
|
||||
Image texture = asset.GetTexture();
|
||||
SkinANIM anim = asset.GetProperty("ANIM", SkinANIM.FromString);
|
||||
IEnumerable<SkinBOX> boxes = asset.GetMultipleProperties("BOX").Select(kv => SkinBOX.FromString(kv.Value));
|
||||
IEnumerable<SkinPartOffset> offsets = asset.GetMultipleProperties("OFFSET").Select(kv => SkinPartOffset.FromString(kv.Value));
|
||||
return new Skin.Skin(name, skinId, texture, anim, boxes, offsets);
|
||||
}
|
||||
|
||||
public static void SetSkin(this PckAsset asset, Skin.Skin skin, LOCFile localizationFile)
|
||||
{
|
||||
if (asset.Type != PckAssetType.SkinFile)
|
||||
throw new InvalidOperationException("Asset is not a skin file");
|
||||
|
||||
asset.SetTexture(skin.Texture);
|
||||
|
||||
string skinId = skin.Identifier.ToString("d08");
|
||||
|
||||
// TODO: keep filepath
|
||||
asset.Filename = $"dlcskin{skinId}.png";
|
||||
|
||||
asset.SetProperty("DISPLAYNAME", skin.MetaData.Name);
|
||||
|
||||
if (localizationFile is not null)
|
||||
{
|
||||
string skinLocKey = $"IDS_dlcskin{skinId}_DISPLAYNAME";
|
||||
asset.SetProperty("DISPLAYNAMEID", skinLocKey);
|
||||
localizationFile.SetLocEntry(skinLocKey, skin.MetaData.Name);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(skin.MetaData.Theme))
|
||||
{
|
||||
asset.SetProperty("THEMENAME", skin.MetaData.Theme);
|
||||
|
||||
if (localizationFile is not null)
|
||||
{
|
||||
string skinThemeLocKey = $"IDS_dlcskin{skinId}_THEMENAME";
|
||||
asset.SetProperty("THEMENAMEID", skinThemeLocKey);
|
||||
localizationFile.SetLocEntry(skinThemeLocKey, skin.MetaData.Theme);
|
||||
}
|
||||
}
|
||||
|
||||
if (skin.HasCape)
|
||||
{
|
||||
asset.SetProperty("CAPEPATH", $"dlccape{skinId}.png");
|
||||
}
|
||||
|
||||
asset.SetProperty("ANIM", skin.Anim.ToString());
|
||||
asset.SetProperty("GAME_FLAGS", "0x18");
|
||||
asset.SetProperty("FREE", "1");
|
||||
|
||||
asset.RemoveProperties("BOX");
|
||||
asset.RemoveProperties("OFFSET");
|
||||
|
||||
foreach (SkinBOX box in skin.Model.AdditionalBoxes)
|
||||
{
|
||||
asset.AddProperty(box.ToProperty());
|
||||
}
|
||||
foreach (SkinPartOffset offset in skin.Model.PartOffsets)
|
||||
{
|
||||
asset.AddProperty(offset.ToProperty());
|
||||
}
|
||||
}
|
||||
|
||||
public static T GetDeserializedData<T>(this PckAsset asset, IPckAssetDeserializer<T> deserializer)
|
||||
{
|
||||
return deserializer.Deserialize(asset);
|
||||
}
|
||||
|
||||
public static T GetData<T>(this PckAsset asset, IDataFormatReader<T> formatReader) where T : class
|
||||
{
|
||||
using var ms = new MemoryStream(asset.Data);
|
||||
return formatReader.FromStream(ms);
|
||||
}
|
||||
|
||||
public static void SetSerializedData<T>(this PckAsset asset, T obj, IPckAssetSerializer<T> serializer)
|
||||
{
|
||||
serializer.Serialize(obj, ref asset);
|
||||
}
|
||||
|
||||
public static void SetData(this PckAsset asset, IDataFormatWriter formatWriter)
|
||||
{
|
||||
using (var stream = new MemoryStream())
|
||||
{
|
||||
formatWriter.WriteToStream(stream);
|
||||
asset.SetData(stream.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetTexture(this PckAsset asset, Image image)
|
||||
{
|
||||
if (asset.Type != PckAssetType.SkinFile &&
|
||||
asset.Type != PckAssetType.CapeFile &&
|
||||
asset.Type != PckAssetType.TextureFile)
|
||||
{
|
||||
throw new Exception("Asset is not suitable to contain image data.");
|
||||
}
|
||||
asset.SetSerializedData(image, ImageSerializer.DefaultSerializer);
|
||||
}
|
||||
|
||||
public static bool IsMipmappedFile(this PckAsset asset)
|
||||
{
|
||||
// We only want to test the file name itself. ex: "terrainMipMapLevel2"
|
||||
string name = Path.GetFileNameWithoutExtension(asset.Filename);
|
||||
|
||||
// check if last character is a digit (0-9). If not return false
|
||||
if (!char.IsDigit(name[name.Length - 1]))
|
||||
return false;
|
||||
|
||||
// If string does not end with MipMapLevel, then it's not MipMapped
|
||||
if (!name.Remove(name.Length - 1, 1).EndsWith(MipMap))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public static string GetNormalPath(this PckAsset asset)
|
||||
{
|
||||
if (!asset.IsMipmappedFile())
|
||||
return asset.Filename;
|
||||
string ext = Path.GetExtension(asset.Filename);
|
||||
return asset.Filename.Remove(asset.Filename.Length - (MipMap.Length + 1) - ext.Length) + ext;
|
||||
}
|
||||
|
||||
public static void DeserializeProperties(this PckAsset asset, IEnumerable<string> serializedData)
|
||||
{
|
||||
IEnumerable<KeyValuePair<string, string>> lines = serializedData
|
||||
.Select(line => line.Split([' '], 2))
|
||||
.Where (keyValue => keyValue.Length == 2)
|
||||
.Select(keyValue => new KeyValuePair<string, string>(keyValue[0].Replace(":", ""), keyValue[1]));
|
||||
foreach (KeyValuePair<string, string> kv in lines)
|
||||
{
|
||||
asset.AddProperty(kv);
|
||||
}
|
||||
}
|
||||
|
||||
public static IEnumerable<string> SerializeProperties(this PckAsset asset, string seperater = ":")
|
||||
{
|
||||
IReadOnlyList<KeyValuePair<string, string>> properties = asset.GetProperties();
|
||||
return properties.Select(property => property.Key + seperater + property.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
23
PckStudio.Core/Extensions/PictureBoxExtensions.cs
Normal file
23
PckStudio.Core/Extensions/PictureBoxExtensions.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class PictureBoxExtensions
|
||||
{
|
||||
public static bool IsAnimating(this PictureBox pictureBox)
|
||||
{
|
||||
FieldInfo fi = typeof(PictureBox).GetField("currentlyAnimating", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
return (bool)fi.GetValue(pictureBox);
|
||||
}
|
||||
|
||||
public static void Animate(this PictureBox pictureBox, bool animate)
|
||||
{
|
||||
MethodInfo animateMethod = typeof(PictureBox).GetMethod("Animate", BindingFlags.NonPublic | BindingFlags.Instance,
|
||||
null, new Type[] { typeof(bool) }, null);
|
||||
animateMethod.Invoke(pictureBox, new object[] { animate });
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
81
PckStudio.Core/Extensions/SkinBOXExtensions.cs
Normal file
81
PckStudio.Core/Extensions/SkinBOXExtensions.cs
Normal file
@@ -0,0 +1,81 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Drawing2D;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using PckStudio.Core.Skin;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class SkinBOXExtensions
|
||||
{
|
||||
public static GraphicsPath GetUVGraphicsPath(this SkinBOX skinBOX, Vector2 tillingFactor)
|
||||
{
|
||||
var types = new byte[9];
|
||||
var points = new PointF[9];
|
||||
|
||||
var path = new GraphicsPath(FillMode.Winding);
|
||||
|
||||
Vector2 uv = skinBOX.UV;
|
||||
Vector3 size = skinBOX.Size;
|
||||
|
||||
path.AddRectangle(new RectangleF(new PointF((uv.X ) * tillingFactor.X, (uv.Y + size.Z) * tillingFactor.Y), new SizeF(size.Z * tillingFactor.X, size.Y * tillingFactor.Y)));
|
||||
path.AddRectangle(new RectangleF(new PointF((uv.X + size.Z ) * tillingFactor.X, (uv.Y + size.Z) * tillingFactor.Y), new SizeF(size.X * tillingFactor.X, size.Y * tillingFactor.Y)));
|
||||
path.AddRectangle(new RectangleF(new PointF((uv.X + size.Z + size.X ) * tillingFactor.X, (uv.Y + size.Z) * tillingFactor.Y), new SizeF(size.Z * tillingFactor.X, size.Y * tillingFactor.Y)));
|
||||
path.AddRectangle(new RectangleF(new PointF((uv.X + size.Z * 2 + size.X) * tillingFactor.X, (uv.Y + size.Z) * tillingFactor.Y), new SizeF(size.X * tillingFactor.X, size.Y * tillingFactor.Y)));
|
||||
|
||||
path.AddRectangle(new RectangleF(new PointF((uv.X + size.Z ) * tillingFactor.X, (uv.Y ) * tillingFactor.Y), new SizeF(size.X * tillingFactor.X, size.Z * tillingFactor.Y)));
|
||||
path.AddRectangle(new RectangleF(new PointF((uv.X + size.Z + size.X ) * tillingFactor.X, (uv.Y ) * tillingFactor.Y), new SizeF(size.X * tillingFactor.X, size.Z * tillingFactor.Y)));
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
public static GraphicsPath GetUVGraphicsPath(this SkinBOX skinBox)
|
||||
{
|
||||
return skinBox.GetUVGraphicsPath(Vector2.One);
|
||||
}
|
||||
|
||||
public static string GetOverlayType(this SkinBOX skinBox)
|
||||
{
|
||||
if (!skinBox.IsValidType())
|
||||
return "";
|
||||
if (skinBox.IsOverlayPart())
|
||||
return skinBox.Type;
|
||||
int index = Array.IndexOf(SkinBOX.BaseTypes, skinBox.Type);
|
||||
return SkinBOX.OverlayTypes.IndexInRange(index) ? SkinBOX.OverlayTypes[index] : "";
|
||||
}
|
||||
|
||||
public static string GetOverlayType(string type)
|
||||
{
|
||||
if (!SkinBOX.IsValidType(type))
|
||||
return "";
|
||||
if (SkinBOX.IsOverlayPart(type))
|
||||
return type;
|
||||
int index = Array.IndexOf(SkinBOX.BaseTypes, type);
|
||||
return SkinBOX.OverlayTypes.IndexInRange(index) ? SkinBOX.OverlayTypes[index] : "";
|
||||
}
|
||||
|
||||
public static string GetBaseType(this SkinBOX skinBox)
|
||||
{
|
||||
if (!skinBox.IsValidType())
|
||||
return "";
|
||||
if (skinBox.IsBasePart())
|
||||
return skinBox.Type;
|
||||
int index = Array.IndexOf(SkinBOX.OverlayTypes, skinBox.Type);
|
||||
return SkinBOX.BaseTypes.IndexInRange(index) ? SkinBOX.BaseTypes[index] : "";
|
||||
}
|
||||
|
||||
public static string GetBaseType(string type)
|
||||
{
|
||||
if (!SkinBOX.IsValidType(type))
|
||||
return "";
|
||||
if (SkinBOX.IsBasePart(type))
|
||||
return type;
|
||||
int index = Array.IndexOf(SkinBOX.OverlayTypes, type);
|
||||
return SkinBOX.BaseTypes.IndexInRange(index) ? SkinBOX.BaseTypes[index] : "";
|
||||
}
|
||||
}
|
||||
}
|
||||
71
PckStudio.Core/Extensions/SkinExtensions.cs
Normal file
71
PckStudio.Core/Extensions/SkinExtensions.cs
Normal file
@@ -0,0 +1,71 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing.Imaging;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using OMI.Formats.Languages;
|
||||
using OMI.Formats.Pck;
|
||||
using PckStudio.Core.Skin;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class SkinExtensions
|
||||
{
|
||||
public static PckAsset CreateFile(this Skin.Skin skin, LOCFile localizationFile)
|
||||
{
|
||||
string skinId = skin.Identifier.ToString("d08");
|
||||
PckAsset skinFile = new PckAsset($"dlcskin{skinId}.png", PckAssetType.SkinFile);
|
||||
|
||||
skinFile.AddProperty("DISPLAYNAME", skin.MetaData.Name);
|
||||
if (localizationFile is not null)
|
||||
{
|
||||
string skinLocKey = $"IDS_dlcskin{skinId}_DISPLAYNAME";
|
||||
skinFile.AddProperty("DISPLAYNAMEID", skinLocKey);
|
||||
localizationFile.AddLocKey(skinLocKey, skin.MetaData.Name);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(skin.MetaData.Theme))
|
||||
{
|
||||
skinFile.AddProperty("THEMENAME", skin.MetaData.Theme);
|
||||
if (localizationFile is not null)
|
||||
{
|
||||
skinFile.AddProperty("THEMENAMEID", $"IDS_dlcskin{skinId}_THEMENAME");
|
||||
localizationFile.AddLocKey($"IDS_dlcskin{skinId}_THEMENAME", skin.MetaData.Theme);
|
||||
}
|
||||
}
|
||||
|
||||
if (skin.HasCape)
|
||||
{
|
||||
skinFile.AddProperty("CAPEPATH", $"dlccape{skinId}.png");
|
||||
}
|
||||
|
||||
skinFile.AddProperty("ANIM", skin.Anim);
|
||||
skinFile.AddProperty("GAME_FLAGS", "0x18");
|
||||
skinFile.AddProperty("FREE", "1");
|
||||
|
||||
foreach (SkinBOX box in skin.Model.AdditionalBoxes)
|
||||
{
|
||||
skinFile.AddProperty(box.ToProperty());
|
||||
}
|
||||
foreach (SkinPartOffset offset in skin.Model.PartOffsets)
|
||||
{
|
||||
skinFile.AddProperty(offset.ToProperty());
|
||||
}
|
||||
|
||||
skinFile.SetTexture(skin.Texture);
|
||||
|
||||
return skinFile;
|
||||
}
|
||||
|
||||
public static PckAsset CreateCapeFile(this Skin.Skin skin)
|
||||
{
|
||||
if (!skin.HasCape)
|
||||
throw new InvalidOperationException("Skin does not contain a cape.");
|
||||
string skinId = skin.Identifier.ToString("d08");
|
||||
PckAsset capeFile = new PckAsset($"dlccape{skinId}.png", PckAssetType.CapeFile);
|
||||
capeFile.SetTexture(skin.CapeTexture);
|
||||
return capeFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
29
PckStudio.Core/Extensions/System.Numerics.cs
Normal file
29
PckStudio.Core/Extensions/System.Numerics.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class NumericsExtensions
|
||||
{
|
||||
//internal static Cube ToCube(this SkinBOX skinBOX) => skinBOX.ToCube(0f);
|
||||
|
||||
//internal static Cube ToCube(this SkinBOX skinBOX, float inflate, bool flipZMapping = false)
|
||||
// => new Cube(skinBOX.Pos.ToOpenTKVector(), skinBOX.Size.ToOpenTKVector(), skinBOX.UV.ToOpenTKVector(), skinBOX.Scale + inflate, skinBOX.Mirror, flipZMapping);
|
||||
public static OpenTK.Vector3 ToOpenTKVector(this System.Numerics.Vector3 vector3)
|
||||
{
|
||||
return new OpenTK.Vector3(vector3.X, vector3.Y, vector3.Z);
|
||||
}
|
||||
|
||||
public static OpenTK.Vector2 ToOpenTKVector(this System.Numerics.Vector2 vector2)
|
||||
{
|
||||
return new OpenTK.Vector2(vector2.X, vector2.Y);
|
||||
}
|
||||
|
||||
public static System.Numerics.Vector3 ToNumericsVector(this OpenTK.Vector3 vector3)
|
||||
{
|
||||
return new System.Numerics.Vector3(vector3.X, vector3.Y, vector3.Z);
|
||||
}
|
||||
|
||||
public static System.Numerics.Vector2 ToNumericsVector(this OpenTK.Vector2 vector2)
|
||||
{
|
||||
return new System.Numerics.Vector2(vector2.X, vector2.Y);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
PckStudio.Core/Extensions/TreeNodeExtensions.cs
Normal file
47
PckStudio.Core/Extensions/TreeNodeExtensions.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class TreeNodeExtensions
|
||||
{
|
||||
public static bool IsTagOfType<T>(this TreeNode node) where T : class
|
||||
{
|
||||
return node?.Tag is T;
|
||||
}
|
||||
|
||||
public static bool TryGetTagData<TOut>(this TreeNode node, out TOut tagData) where TOut : class
|
||||
{
|
||||
if (node?.Tag is TOut data)
|
||||
{
|
||||
tagData = data;
|
||||
return true;
|
||||
}
|
||||
tagData = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static bool Contains(this TreeNode thisNode, TreeNode childNode)
|
||||
{
|
||||
if (childNode.Parent == null)
|
||||
return false;
|
||||
if (thisNode.Equals(childNode.Parent))
|
||||
return true;
|
||||
// If the parent node is not equal to the first node,
|
||||
// call the TreeNode.Contains recursively using the parent of the node.
|
||||
return thisNode.Contains(childNode.Parent);
|
||||
}
|
||||
|
||||
public static List<TreeNode> GetChildNodes(this TreeNode thisNode)
|
||||
{
|
||||
List<TreeNode> nodes = new List<TreeNode>(thisNode.Nodes.Count);
|
||||
foreach (TreeNode node in thisNode.Nodes)
|
||||
{
|
||||
nodes.Add(node);
|
||||
nodes.AddRange(node.GetChildNodes());
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
28
PckStudio.Core/Extensions/TreeViewExtensions.cs
Normal file
28
PckStudio.Core/Extensions/TreeViewExtensions.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Windows.Forms;
|
||||
|
||||
namespace PckStudio.Core.Extensions
|
||||
{
|
||||
public static class TreeViewExtensions
|
||||
{
|
||||
public static TreeNode[] FindPath(this TreeView treeView, string path)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(path))
|
||||
return Array.Empty<TreeNode>();
|
||||
|
||||
if (!path.Contains(treeView.PathSeparator))
|
||||
{
|
||||
return treeView.Nodes.Find(path, false);
|
||||
}
|
||||
|
||||
string segment = path.Substring(0, path.IndexOf(treeView.PathSeparator));
|
||||
if (treeView.Nodes.ContainsKey(segment))
|
||||
{
|
||||
TreeNode[] res = treeView.Nodes[segment].GetChildNodes().Where(node => node.FullPath == path).ToArray();
|
||||
return res;
|
||||
}
|
||||
return Array.Empty<TreeNode>();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user