* (Fix) phz: Files with encryption or sliced by chitubox produced black images after save due not setting the image address nor size (Spotted by Burak Cezairli)
This commit is contained in:
Tiago Conceição
2020-06-21 17:06:52 +01:00
parent c10bf6cef3
commit 043770ca12
20 changed files with 2676 additions and 25 deletions
+4
View File
@@ -1,5 +1,9 @@
# Changelog
## 21/06/2020 - v0.5.2.2
* (Fix) phz: Files with encryption or sliced by chitubox produced black images after save due not setting the image address nor size (Spotted by Burak Cezairli)
## 20/06/2020 - v0.5.2.1
* (Add) cws: Allow change layer PWM value
+19
View File
@@ -0,0 +1,19 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
namespace UVtools.Core
{
public static class About
{
public static string Software = "UVtools";
public static string Author = "Tiago Conceição";
public static string Company = "PTRTECH";
public static string Website = "https://github.com/sn4k3/UVtools";
public static string Donate = "https://paypal.me/SkillTournament";
}
}
+28
View File
@@ -0,0 +1,28 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System.Runtime.InteropServices;
using Emgu.CV;
using Emgu.CV.CvEnum;
namespace UVtools.Core.Extensions
{
public static class EmguExtensions
{
public static byte[] GetBytes(this Mat mat)
{
byte[] data = new byte[mat.Width * mat.Height * mat.NumberOfChannels];
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
using (Mat m2 = new Mat(mat.Size, DepthType.Cv8U, 3, handle.AddrOfPinnedObject(), mat.Width * mat.NumberOfChannels)) { }
handle.Free();
return data;
}
}
}
@@ -0,0 +1,31 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System.IO;
namespace UVtools.Core.Extensions
{
public static class FileStreamExtensions
{
public static uint ReadBytes(this FileStream fs, byte[] bytes, int offset = 0)
{
return (uint)fs.Read(bytes, offset, bytes.Length);
}
public static uint WriteStream(this FileStream fs, MemoryStream stream, int offset = 0)
{
return fs.WriteBytes(stream.ToArray(), offset);
}
public static uint WriteBytes(this FileStream fs, byte[] bytes, int offset = 0)
{
fs.Write(bytes, offset, bytes.Length);
return (uint)bytes.Length;
}
}
}
@@ -0,0 +1,29 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System.IO;
namespace UVtools.Core.Extensions
{
public static class StreamExtensions
{
/// <summary>
/// Converts stream into byte array
/// </summary>
/// <param name="stream">Input</param>
/// <returns>Byte array data</returns>
public static byte[] ToArray(this Stream stream)
{
using (var memoryStream = new MemoryStream())
{
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
}
}
}
@@ -0,0 +1,49 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.ComponentModel;
using System.Linq;
namespace UVtools.Core.Extensions
{
public static class StringExtensions
{
/// <summary>
/// Upper the first character in a string
/// </summary>
/// <param name="input">Input string</param>
/// <returns>Modified string with fist character upper</returns>
public static string FirstCharToUpper(this string input)
{
switch (input)
{
case null: throw new ArgumentNullException(nameof(input));
case "": throw new ArgumentException($"{nameof(input)} cannot be empty", nameof(input));
default: return input.First().ToString().ToUpper() + input.Substring(1);
}
}
/// <summary>
/// Converts a string into a target type
/// </summary>
/// <typeparam name="T">Target type to convert into</typeparam>
/// <param name="input">Value</param>
/// <returns>Converted value into target type</returns>
public static T Convert<T>(this string input)
{
var converter = TypeDescriptor.GetConverter(typeof(T));
if (converter != null)
{
//Cast ConvertFromString(string text) : object to (T)
return (T)converter.ConvertFromString(input);
}
return default(T);
}
}
}
@@ -0,0 +1,333 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
namespace UVtools.Core.Extensions
{
public static class ZipArchiveExtensions
{
/// <summary>
/// Used to specify what our overwrite policy
/// is for files we are extracting.
/// </summary>
public enum Overwrite
{
Always,
IfNewer,
Never
}
/// <summary>
/// Used to identify what we will do if we are
/// trying to create a zip file and it already
/// exists.
/// </summary>
public enum ArchiveAction
{
Merge,
Replace,
Error,
Ignore
}
/// <summary>
/// Unzips the specified file to the given folder in a safe
/// manner. This plans for missing paths and existing files
/// and handles them gracefully.
/// </summary>
/// <param name="sourceArchiveFileName">
/// The name of the zip file to be extracted
/// </param>
/// <param name="destinationDirectoryName">
/// The directory to extract the zip file to
/// </param>
/// <param name="overwriteMethod">
/// Specifies how we are going to handle an existing file.
/// The default is IfNewer.
/// </param>
public static void ImprovedExtractToDirectory(string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer)
{
//Opens the zip file up to be read
using (ZipArchive archive = ZipFile.OpenRead(sourceArchiveFileName))
{
archive.ImprovedExtractToDirectory(sourceArchiveFileName, destinationDirectoryName, overwriteMethod);
}
}
/// <summary>
/// Unzips the specified file to the given folder in a safe
/// manner. This plans for missing paths and existing files
/// and handles them gracefully.
/// </summary>
/// <param name="sourceArchiveFileName">
/// The name of the zip file to be extracted
/// </param>
/// <param name="destinationDirectoryName">
/// The directory to extract the zip file to
/// </param>
/// <param name="overwriteMethod">
/// Specifies how we are going to handle an existing file.
/// The default is IfNewer.
/// </param>
public static void ImprovedExtractToDirectory(this ZipArchive archive, string sourceArchiveFileName, string destinationDirectoryName, Overwrite overwriteMethod = Overwrite.IfNewer)
{
//Loops through each file in the zip file
foreach (ZipArchiveEntry file in archive.Entries)
{
file.ImprovedExtractToFile(destinationDirectoryName, overwriteMethod);
}
}
/// <summary>
/// Safely extracts a single file from a zip file
/// </summary>
/// <param name="file">
/// The zip entry we are pulling the file from
/// </param>
/// <param name="destinationPath">
/// The root of where the file is going
/// </param>
/// <param name="overwriteMethod">
/// Specifies how we are going to handle an existing file.
/// The default is Overwrite.IfNewer.
/// </param>
public static void ImprovedExtractToFile(this ZipArchiveEntry file, string destinationPath, Overwrite overwriteMethod = Overwrite.IfNewer)
{
//Gets the complete path for the destination file, including any
//relative paths that were in the zip file
string destinationFileName = Path.Combine(destinationPath, file.FullName);
//Gets just the new path, minus the file name so we can create the
//directory if it does not exist
string destinationFilePath = Path.GetDirectoryName(destinationFileName);
//Creates the directory (if it doesn't exist) for the new path
Directory.CreateDirectory(destinationFilePath);
//Determines what to do with the file based upon the
//method of overwriting chosen
switch (overwriteMethod)
{
case Overwrite.Always:
//Just put the file in and overwrite anything that is found
file.ExtractToFile(destinationFileName, true);
break;
case Overwrite.IfNewer:
//Checks to see if the file exists, and if so, if it should
//be overwritten
if (!File.Exists(destinationFileName) || File.GetLastWriteTime(destinationFileName) < file.LastWriteTime)
{
//Either the file didn't exist or this file is newer, so
//we will extract it and overwrite any existing file
file.ExtractToFile(destinationFileName, true);
}
break;
case Overwrite.Never:
//Put the file in if it is new but ignores the
//file if it already exists
if (!File.Exists(destinationFileName))
{
file.ExtractToFile(destinationFileName);
}
break;
}
}
/// <summary>
/// Allows you to add files to an archive, whether the archive
/// already exists or not
/// </summary>
/// <param name="archiveFullName">
/// The name of the archive to you want to add your files to
/// </param>
/// <param name="files">
/// A set of file names that are to be added
/// </param>
/// <param name="action">
/// Specifies how we are going to handle an existing archive
/// </param>
/// <param name="compression">
/// Specifies what type of compression to use - defaults to Optimal
/// </param>
public static void AddToArchive(string archiveFullName,
List<string> files,
ArchiveAction action = ArchiveAction.Replace,
Overwrite fileOverwrite = Overwrite.IfNewer,
CompressionLevel compression = CompressionLevel.Optimal)
{
//Identifies the mode we will be using - the default is Create
ZipArchiveMode mode = ZipArchiveMode.Create;
//Determines if the zip file even exists
bool archiveExists = File.Exists(archiveFullName);
//Figures out what to do based upon our specified overwrite method
switch (action)
{
case ArchiveAction.Merge:
//Sets the mode to update if the file exists, otherwise
//the default of Create is fine
if (archiveExists)
{
mode = ZipArchiveMode.Update;
}
break;
case ArchiveAction.Replace:
//Deletes the file if it exists. Either way, the default
//mode of Create is fine
if (archiveExists)
{
File.Delete(archiveFullName);
}
break;
case ArchiveAction.Error:
//Throws an error if the file exists
if (archiveExists)
{
throw new IOException(String.Format("The zip file {0} already exists.", archiveFullName));
}
break;
case ArchiveAction.Ignore:
//Closes the method silently and does nothing
if (archiveExists)
{
return;
}
break;
}
//Opens the zip file in the mode we specified
using (ZipArchive zipFile = ZipFile.Open(archiveFullName, mode))
{
//This is a bit of a hack and should be refactored - I am
//doing a similar foreach loop for both modes, but for Create
//I am doing very little work while Update gets a lot of
//code. This also does not handle any other mode (of
//which there currently wouldn't be one since we don't
//use Read here).
if (mode == ZipArchiveMode.Create)
{
foreach (string file in files)
{
//Adds the file to the archive
zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
}
}
else
{
foreach (string file in files)
{
var fileInZip = (from f in zipFile.Entries
where f.Name == Path.GetFileName(file)
select f).FirstOrDefault();
switch (fileOverwrite)
{
case Overwrite.Always:
//Deletes the file if it is found
if (fileInZip != null)
{
fileInZip.Delete();
}
//Adds the file to the archive
zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
break;
case Overwrite.IfNewer:
//This is a bit trickier - we only delete the file if it is
//newer, but if it is newer or if the file isn't already in
//the zip file, we will write it to the zip file
if (fileInZip != null)
{
//Deletes the file only if it is older than our file.
//Note that the file will be ignored if the existing file
//in the archive is newer.
if (fileInZip.LastWriteTime < File.GetLastWriteTime(file))
{
fileInZip.Delete();
//Adds the file to the archive
zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
}
}
else
{
//The file wasn't already in the zip file so add it to the archive
zipFile.CreateEntryFromFile(file, Path.GetFileName(file), compression);
}
break;
case Overwrite.Never:
//Don't do anything - this is a decision that you need to
//consider, however, since this will mean that no file will
//be written. You could write a second copy to the zip with
//the same name (not sure that is wise, however).
break;
}
}
}
}
}
/// <summary>
/// Get or put a file into archive
/// </summary>
/// <param name="input"><see cref="ZipArchive"/></param>
/// <param name="filename">Filename to create</param>
/// <returns>Created <see cref="ZipArchiveEntry"/></returns>
public static ZipArchiveEntry GetPutFile(this ZipArchive input, string filename)
{
return input.GetEntry(filename) ?? input.CreateEntry(filename);
}
/// <summary>
/// Create or update a file into archive and write content to it
/// </summary>
/// <param name="input"><see cref="ZipArchive"/></param>
/// <param name="filename">Filename to create</param>
/// <param name="content">Content to write</param>
/// <returns>Created <see cref="ZipArchiveEntry"/></returns>
public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, string content)
{
ZipArchiveEntry entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
if (string.IsNullOrEmpty(content)) return entry;
Stream stream = entry.Open();
stream.SetLength(0);
using (TextWriter tw = new StreamWriter(stream))
{
tw.Write(content);
tw.Close();
}
return entry;
}
/// <summary>
/// Create or update a file into archive and write content to it
/// </summary>
/// <param name="input"><see cref="ZipArchive"/></param>
/// <param name="filename">Filename to create</param>
/// <param name="content">Content to write</param>
/// <returns>Created <see cref="ZipArchiveEntry"/></returns>
public static ZipArchiveEntry PutFileContent(this ZipArchive input, string filename, byte[] content)
{
ZipArchiveEntry entry = input.GetEntry(filename) ?? input.CreateEntry(filename);
if (ReferenceEquals(content, null)) return entry;
Stream stream = entry.Open();
stream.SetLength(0);
stream.Write(content, 0, content.Length);
return entry;
}
}
}
+89
View File
@@ -0,0 +1,89 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System.Collections.Generic;
namespace UVtools.Parser
{
/// <summary>
/// Represents a file extension for slicer file formats
/// </summary>
public sealed class FileExtension
{
#region Properties
/// <summary>
/// Gets the extension name without the dot (.)
/// </summary>
public string Extension { get; }
/// <summary>
/// Gets the extension description
/// </summary>
public string Description { get; }
/// <summary>
/// Gets the file filter for open and save dialogs
/// </summary>
public string Filter => $@"{Description} (*.{Extension})|*.{Extension}";
#endregion
#region Constructor
/// <summary>
/// Constructor
/// </summary>
/// <param name="extension">The extension name without the dot (.)</param>
/// <param name="description">The extension description</param>
public FileExtension(string extension, string description)
{
Extension = extension;
Description = description;
}
#endregion
#region Overrides
public override string ToString()
{
return $"{nameof(Extension)}: {Extension}, {nameof(Description)}: {Description}";
}
private bool Equals(FileExtension other)
{
return Extension == other.Extension;
}
public override bool Equals(object obj)
{
return ReferenceEquals(this, obj) || obj is FileExtension other && Equals(other);
}
public override int GetHashCode()
{
return (Extension != null ? Extension.GetHashCode() : 0);
}
private sealed class ExtensionEqualityComparer : IEqualityComparer<FileExtension>
{
public bool Equals(FileExtension x, FileExtension y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return x.Extension == y.Extension;
}
public int GetHashCode(FileExtension obj)
{
return (obj.Extension != null ? obj.Extension.GetHashCode() : 0);
}
}
public static IEqualityComparer<FileExtension> ExtensionComparer { get; } = new ExtensionEqualityComparer();
#endregion
}
}
+697
View File
@@ -0,0 +1,697 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Emgu.CV;
using UVtools.Core.Extensions;
using UVtools.Parser;
namespace UVtools.Core
{
/// <summary>
/// Slicer <see cref="FileFormat"/> representation
/// </summary>
public abstract class FileFormat : IFileFormat, IDisposable, IEquatable<FileFormat>, IEnumerable<Layer>
{
#region Enums
/// <summary>
/// Enumeration of file format types
/// </summary>
public enum FileFormatType : byte
{
Archive,
Binary
}
/// <summary>
/// Enumeration of file thumbnail size types
/// </summary>
public enum FileThumbnailSize : byte
{
Small = 0,
Large
}
#endregion
#region Sub Classes
/// <summary>
/// Available Print Parameters to modify
/// </summary>
public class PrintParameterModifier
{
#region Instances
public static PrintParameterModifier InitialLayerCount { get; } = new PrintParameterModifier("Initial Layer Count", @"Modify 'Initial Layer Count' value", null,0, ushort.MaxValue);
public static PrintParameterModifier InitialExposureSeconds { get; } = new PrintParameterModifier("Initial Exposure Time", @"Modify 'Initial Exposure Time' seconds", "s", 0.1M, byte.MaxValue);
public static PrintParameterModifier ExposureSeconds { get; } = new PrintParameterModifier("Exposure Time", @"Modify 'Exposure Time' seconds", "s", 0.1M, byte.MaxValue);
public static PrintParameterModifier BottomLayerOffTime { get; } = new PrintParameterModifier("Bottom Layer Off Time", @"Modify 'Bottom Layer Off Time' seconds", "s");
public static PrintParameterModifier LayerOffTime { get; } = new PrintParameterModifier("Layer Off Time", @"Modify 'Layer Off Time' seconds", "s");
public static PrintParameterModifier BottomLiftHeight { get; } = new PrintParameterModifier("Bottom Lift Height", @"Modify 'Bottom Lift Height' millimeters between bottom layers", "mm");
public static PrintParameterModifier BottomLiftSpeed { get; } = new PrintParameterModifier("Bottom Lift Speed", @"Modify 'Bottom Lift Speed' mm/min between bottom layers", "mm/min");
public static PrintParameterModifier LiftHeight { get; } = new PrintParameterModifier("Lift Height", @"Modify 'Lift Height' millimeters between layers", "mm");
public static PrintParameterModifier LiftSpeed { get; } = new PrintParameterModifier("Lift Speed", @"Modify 'Lift Speed' mm/min between layers", "mm/min", 10, 5000);
public static PrintParameterModifier RetractSpeed { get; } = new PrintParameterModifier("Retract Speed", @"Modify 'Retract Speed' mm/min between layers", "mm/min", 10, 5000);
public static PrintParameterModifier BottomLightPWM { get; } = new PrintParameterModifier("Bottom Light PWM", @"Modify 'Bottom Light PWM' value", null, 50, byte.MaxValue);
public static PrintParameterModifier LightPWM { get; } = new PrintParameterModifier("Light PWM", @"Modify 'Light PWM' value", null, 50, byte.MaxValue);
#endregion
#region Properties
/// <summary>
/// Gets the name
/// </summary>
public string Name { get; }
/// <summary>
/// Gets the description
/// </summary>
public string Description { get; }
/// <summary>
/// Gets the value unit
/// </summary>
public string ValueUnit { get; }
/// <summary>
/// Gets the minimum value
/// </summary>
public decimal Minimum { get; }
/// <summary>
/// Gets the maximum value
/// </summary>
public decimal Maximum { get; }
#endregion
#region Constructor
public PrintParameterModifier(string name, string description, string valueUnit = null, decimal minimum = 0, decimal maximum = 1000)
{
Name = name;
Description = description;
ValueUnit = valueUnit ?? string.Empty;
Minimum = minimum;
Maximum = maximum;
}
#endregion
#region Overrides
public override string ToString()
{
return $"{nameof(Name)}: {Name}, {nameof(Description)}: {Description}, {nameof(ValueUnit)}: {ValueUnit}, {nameof(Minimum)}: {Minimum}, {nameof(Maximum)}: {Maximum}";
}
#endregion
}
#endregion
#region Constants
private const string ExtractConfigFileName = "Configuration";
private const string ExtractConfigFileExtension = "ini";
#endregion
#region Static Methods
/// <summary>
/// Gets the available formats to process
/// </summary>
public static FileFormat[] AvaliableFormats { get; } =
{
new SL1File(), // Prusa SL1
new ChituboxZipFile(), // Zip
new ChituboxFile(), // cbddlp, cbt, photon
new PHZFile(), // phz
new PWSFile(), // PSW
new ZCodexFile(), // zcodex
new CWSFile(), // CWS
new ImageFile(), // images
};
/// <summary>
/// Gets all filters for open and save file dialogs
/// </summary>
public static string AllFileFilters =>
AvaliableFormats.Aggregate(string.Empty,
(current, fileFormat) => string.IsNullOrEmpty(current)
? fileFormat.FileFilter
: $"{current}|" + fileFormat.FileFilter)
+
AvaliableFormats.Aggregate("|All slicer files|",
(current, fileFormat) => current.EndsWith("|")
? $"{current}{fileFormat.FileFilterExtensionsOnly}"
: $"{current};{fileFormat.FileFilterExtensionsOnly}");
/// <summary>
/// Gets the count of available file extensions
/// </summary>
public static byte FileExtensionsCount
{
get
{
return AvaliableFormats.Aggregate<FileFormat, byte>(0, (current, fileFormat) => (byte) (current + fileFormat.FileExtensions.Length));
}
}
/// <summary>
/// Find <see cref="FileFormat"/> by an extension
/// </summary>
/// <param name="extension">Extension name to find</param>
/// <param name="isFilePath">True if <see cref="extension"/> is a file path rather than only a extension name</param>
/// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param>
/// <returns><see cref="FileFormat"/> object or null if not found</returns>
public static FileFormat FindByExtension(string extension, bool isFilePath = false, bool createNewInstance = false)
{
return (from fileFormat in AvaliableFormats where fileFormat.IsExtensionValid(extension, isFilePath) select createNewInstance ? (FileFormat) Activator.CreateInstance(fileFormat.GetType()) : fileFormat).FirstOrDefault();
}
/// <summary>
/// Find <see cref="FileFormat"/> by an type
/// </summary>
/// <param name="type">Type to find</param>
/// <param name="createNewInstance">True to create a new instance of found file format, otherwise will return a pre created one which should be used for read-only purpose</param>
/// <returns><see cref="FileFormat"/> object or null if not found</returns>
public static FileFormat FindByType(Type type, bool createNewInstance = false)
{
return (from t in AvaliableFormats where type == t.GetType() select createNewInstance ? (FileFormat) Activator.CreateInstance(type) : t).FirstOrDefault();
}
#endregion
#region Properties
public abstract FileFormatType FileType { get; }
public abstract FileExtension[] FileExtensions { get; }
public abstract Type[] ConvertToFormats { get; }
public abstract PrintParameterModifier[] PrintParameterModifiers { get; }
public string FileFilter {
get
{
var result = string.Empty;
foreach (var fileExt in FileExtensions)
{
if (!ReferenceEquals(result, string.Empty))
{
result += '|';
}
result += fileExt.Filter;
}
return result;
}
}
public string FileFilterExtensionsOnly
{
get
{
var result = string.Empty;
foreach (var fileExt in FileExtensions)
{
if (!ReferenceEquals(result, string.Empty))
{
result += ';';
}
result += $"*.{fileExt.Extension}";
}
return result;
}
}
public string FileFullPath { get; set; }
public abstract byte ThumbnailsCount { get; }
public byte CreatedThumbnailsCount {
get
{
if (ReferenceEquals(Thumbnails, null)) return 0;
byte count = 0;
foreach (var thumbnail in Thumbnails)
{
if (ReferenceEquals(thumbnail, null)) continue;
count++;
}
return count;
}
}
public abstract Size[] ThumbnailsOriginalSize { get; }
public Mat[] Thumbnails { get; set; }
public LayerManager LayerManager { get; set; }
/// <summary>
/// Gets if any layer got modified
/// </summary>
public bool ModifiedLayers => LayerManager.IsModified;
public abstract uint ResolutionX { get; }
public abstract uint ResolutionY { get; }
public bool HaveAntiAliasing => AntiAliasing > 1;
public abstract byte AntiAliasing { get; }
public abstract float LayerHeight { get; }
public float TotalHeight => (float)Math.Round(LayerCount * LayerHeight, 2);
public uint LayerCount => LayerManager.Count;
public abstract ushort InitialLayerCount { get; }
public abstract float InitialExposureTime { get; }
public abstract float LayerExposureTime { get; }
public abstract float LiftHeight { get; }
public abstract float RetractSpeed { get; }
public abstract float LiftSpeed { get; }
public abstract float PrintTime { get; }
public abstract float UsedMaterial { get; }
public abstract float MaterialCost { get; }
public abstract string MaterialName { get; }
public abstract string MachineName { get; }
public StringBuilder GCode { get; set; }
public abstract object[] Configs { get; }
public bool IsValid => !ReferenceEquals(FileFullPath, null);
#endregion
#region Constructor
protected FileFormat()
{
Thumbnails = new Mat[ThumbnailsCount];
}
#endregion
#region Indexers
public Layer this[int index]
{
get => LayerManager[index];
set => LayerManager[index] = value;
}
public Layer this[uint index]
{
get => LayerManager[index];
set => LayerManager[index] = value;
}
public Layer this[long index]
{
get => LayerManager[index];
set => LayerManager[index] = value;
}
#endregion
#region Numerators
public IEnumerator<Layer> GetEnumerator()
{
return ((IEnumerable<Layer>)LayerManager.Layers).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region Overrides
public override bool Equals(object obj)
{
return Equals(obj as FileFormat);
}
public bool Equals(FileFormat other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return FileFullPath.Equals(other.FileFullPath);
}
public override int GetHashCode()
{
return (FileFullPath != null ? FileFullPath.GetHashCode() : 0);
}
public void Dispose()
{
Clear();
}
#endregion
#region Methods
public virtual void Clear()
{
FileFullPath = null;
LayerManager = null;
GCode = null;
if (!ReferenceEquals(Thumbnails, null))
{
for (int i = 0; i < ThumbnailsCount; i++)
{
Thumbnails[i]?.Dispose();
}
}
}
public void FileValidation(string fileFullPath)
{
if (ReferenceEquals(fileFullPath, null)) throw new ArgumentNullException(nameof(FileFullPath), "fullFilePath can't be null.");
if (!File.Exists(fileFullPath)) throw new FileNotFoundException("The specified file does not exists.", fileFullPath);
if (IsExtensionValid(fileFullPath, true))
{
return;
}
throw new FileLoadException($"The specified file is not valid.", fileFullPath);
}
public bool IsExtensionValid(string extension, bool isFilePath = false)
{
extension = isFilePath ? Path.GetExtension(extension)?.Remove(0, 1) : extension;
return FileExtensions.Any(fileExtension => fileExtension.Extension.Equals(extension, StringComparison.InvariantCultureIgnoreCase));
}
public string GetFileExtensions(string prepend = ".", string separator = ", ")
{
var result = string.Empty;
foreach (var fileExt in FileExtensions)
{
if (!ReferenceEquals(result, string.Empty))
{
result += separator;
}
result += $"{prepend}{fileExt.Extension}";
}
return result;
}
public Mat GetThumbnail(uint maxHeight = 400)
{
for (int i = 0; i < ThumbnailsCount; i++)
{
if(ReferenceEquals(Thumbnails[i], null)) continue;
if (Thumbnails[i].Height <= maxHeight) return Thumbnails[i];
}
return null;
}
public void SetThumbnails(Mat[] images)
{
for (var i = 0; i < ThumbnailsCount; i++)
{
Thumbnails[i] = images[Math.Min(i, images.Length - 1)].Clone();
}
}
public void SetThumbnails(Mat image)
{
for (var i = 0; i < ThumbnailsCount; i++)
{
Thumbnails[i] = image.Clone();
}
}
public virtual void Encode(string fileFullPath)
{
FileFullPath = fileFullPath;
if (File.Exists(fileFullPath))
{
File.Delete(fileFullPath);
}
for (var i = 0; i < Thumbnails.Length; i++)
{
if (ReferenceEquals(Thumbnails[i], null)) continue;
Mat output = new Mat();
CvInvoke.Resize(Thumbnails[i], output, new Size(ThumbnailsOriginalSize[i].Width, ThumbnailsOriginalSize[i].Height));
Thumbnails[i] = output;
}
}
/*public virtual void BeginEncode(string fileFullPath)
{
}
public abstract void InsertLayerImageEncode(Image<L8> image, uint layerIndex);
public abstract void EndEncode();*/
public virtual void Decode(string fileFullPath)
{
Clear();
FileValidation(fileFullPath);
FileFullPath = fileFullPath;
}
public virtual void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true)
{
/*if (emptyFirst)
{
if (Directory.Exists(path))
{
DirectoryInfo di = new DirectoryInfo(path);
foreach (FileInfo file in di.GetFiles())
{
file.Delete();
}
foreach (DirectoryInfo dir in di.GetDirectories())
{
dir.Delete(true);
}
}
}*/
//if (!Directory.Exists(path))
//{
Directory.CreateDirectory(path);
//}
if (FileType == FileFormatType.Archive)
{
//ZipFile.ExtractToDirectory(FileFullPath, path);
ZipArchiveExtensions.ImprovedExtractToDirectory(FileFullPath, path, ZipArchiveExtensions.Overwrite.Always);
return;
}
if (genericConfigExtract)
{
if (!ReferenceEquals(Configs, null))
{
using (TextWriter tw = new StreamWriter(Path.Combine(path, $"{ExtractConfigFileName}.{ExtractConfigFileExtension}"), false))
{
foreach (var config in Configs)
{
var type = config.GetType();
tw.WriteLine($"[{type.Name}]");
foreach (var property in type.GetProperties())
{
tw.WriteLine($"{property.Name} = {property.GetValue(config)}");
}
tw.WriteLine();
}
tw.Close();
}
}
}
if (genericLayersExtract)
{
uint i = 0;
if (!ReferenceEquals(Thumbnails, null))
{
foreach (var thumbnail in Thumbnails)
{
if (ReferenceEquals(thumbnail, null))
{
continue;
}
thumbnail.Save(Path.Combine(path, $"Thumbnail{i}.png"));
i++;
}
}
if (LayerCount > 0)
{
Parallel.ForEach(this, (layer) =>
{
var byteArr = layer.CompressedBytes;
using (FileStream stream = File.Create(Path.Combine(path, $"Layer{layer.Index}.png"),
byteArr.Length))
{
stream.Write(byteArr, 0, byteArr.Length);
stream.Close();
}
});
}
/* Parallel.For(0, LayerCount, layerIndex => {
var byteArr = this[layerIndex].RawData;
using (FileStream stream = File.Create(Path.Combine(path, $"Layer{layerIndex}.png"), byteArr.Length))
{
stream.Write(byteArr, 0, byteArr.Length);
stream.Close();
}
});*/
/*for (i = 0; i < LayerCount; i++)
{
var byteArr = GetLayer(i);
using (FileStream stream = File.Create(Path.Combine(path, $"Layer{i}.png"), byteArr.Length))
{
stream.Write(byteArr, 0, byteArr.Length);
stream.Close();
}
}*/
}
}
public virtual float GetHeightFromLayer(uint layerIndex, bool realHeight = true)
{
return (float)Math.Round((layerIndex+(realHeight ? 1 : 0)) * LayerHeight, 2);
}
public T GetInitialLayerValueOrNormal<T>(uint layerIndex, T initialLayerValue, T normalLayerValue)
{
return layerIndex < InitialLayerCount ? initialLayerValue : normalLayerValue;
}
public virtual object GetValueFromPrintParameterModifier(PrintParameterModifier modifier)
{
if (ReferenceEquals(modifier, PrintParameterModifier.InitialLayerCount))
return InitialLayerCount;
if (ReferenceEquals(modifier, PrintParameterModifier.InitialExposureSeconds))
return InitialExposureTime;
if (ReferenceEquals(modifier, PrintParameterModifier.ExposureSeconds))
return LayerExposureTime;
if (ReferenceEquals(modifier, PrintParameterModifier.LiftHeight))
return LiftHeight;
if (ReferenceEquals(modifier, PrintParameterModifier.LiftSpeed))
return LiftSpeed;
if (ReferenceEquals(modifier, PrintParameterModifier.RetractSpeed))
return RetractSpeed;
return null;
}
public virtual bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, object value)
{
return SetValueFromPrintParameterModifier(modifier, value.ToString());
}
public abstract bool SetValueFromPrintParameterModifier(PrintParameterModifier modifier, string value);
public void Save()
{
SaveAs();
}
public abstract void SaveAs(string filePath = null);
public abstract bool Convert(Type to, string fileFullPath);
public bool Convert(FileFormat to, string fileFullPath)
{
return Convert(to.GetType(), fileFullPath);
}
public void Resize(uint startLayerIndex, uint endLayerIndex, float x, float y, bool fade)
{
if (x == 1f && y == 1f) return;
Parallel.For(startLayerIndex, endLayerIndex+1, /*new ParallelOptions { MaxDegreeOfParallelism = 1 },*/ layerIndex =>
{
var newX = x;
var newY = y;
if (fade)
{
if (newX != 1f)
{
double steps = Math.Abs(newX - 1.0) / (endLayerIndex - startLayerIndex);
//maxIteration = Math.Max(iterationsStart, iterationsEnd);
newX = (float) (newX < 1f
? newX + (layerIndex - startLayerIndex) * steps
: newX - (layerIndex - startLayerIndex) * steps);
// constrain
//iterations = Math.Min(Math.Max(1, iterations), maxIteration);
}
if (y != 1f)
{
double steps = Math.Abs(newY - 1.0) / (endLayerIndex - startLayerIndex);
//maxIteration = Math.Max(iterationsStart, iterationsEnd);
newY = (float) (newY < 1f
? newY + (layerIndex - startLayerIndex) * steps
: newY - (layerIndex - startLayerIndex) * steps);
// constrain
//iterations = Math.Min(Math.Max(1, iterations), maxIteration);
}
}
if (newX == 1f && newY == 1f) return;
this[layerIndex].Resize(newX, newY);
});
}
public byte ValidateAntiAliasingLevel()
{
if (AntiAliasing < 2) return 1;
if(AntiAliasing % 2 != 0) throw new ArgumentException("AntiAliasing must be multiples of 2, otherwise use 0 or 1 to disable it", nameof(AntiAliasing));
return AntiAliasing;
}
#endregion
}
}
+101
View File
@@ -0,0 +1,101 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using BinarySerialization;
using Newtonsoft.Json;
using UVtools.Core.Extensions;
namespace UVtools.Core
{
/// <summary>
/// A helper class with utilities
/// </summary>
public static class Helpers
{
/// <summary>
/// Gets the <see cref="BinarySerializer"/> instance
/// </summary>
public static BinarySerializer Serializer { get; } = new BinarySerializer {Endianness = Endianness.Little };
public static MemoryStream Serialize(object value)
{
MemoryStream stream = new MemoryStream();
Serializer.Serialize(stream, value);
return stream;
}
public static T Deserialize<T>(Stream stream)
{
return Serializer.Deserialize<T>(stream);
}
public static uint SerializeWriteFileStream(FileStream fs, object value, int offset = 0)
{
using (MemoryStream stream = Helpers.Serialize(value))
{
return fs.WriteStream(stream, offset);
}
}
public static T JsonDeserializeObject<T>(Stream stream)
{
using (TextReader tr = new StreamReader(stream))
{
return JsonConvert.DeserializeObject<T>(tr.ReadToEnd());
}
}
public static SHA1CryptoServiceProvider SHA1 { get; } = new SHA1CryptoServiceProvider();
public static string ComputeSHA1Hash(byte[] input)
{
return Convert.ToBase64String(SHA1.ComputeHash(input));
}
public static bool SetPropertyValue(PropertyInfo attribute, object obj, string value)
{
var name = attribute.PropertyType.Name.ToLower();
switch (name)
{
case "string":
attribute.SetValue(obj, value.Convert<string>());
return true;
case "boolean":
if(char.IsDigit(value[0]))
attribute.SetValue(obj, !value.Equals(0));
else
attribute.SetValue(obj, value.Equals("True", StringComparison.InvariantCultureIgnoreCase));
return true;
case "byte":
attribute.SetValue(obj, value.Convert<byte>());
return true;
case "uint16":
attribute.SetValue(obj, value.Convert<ushort>());
return true;
case "uint32":
attribute.SetValue(obj, value.Convert<uint>());
return true;
case "single":
attribute.SetValue(obj, (float)Math.Round(float.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
return true;
case "double":
attribute.SetValue(obj, Math.Round(double.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
return true;
case "decimal":
attribute.SetValue(obj, Math.Round(decimal.Parse(value, CultureInfo.InvariantCulture.NumberFormat), 3));
return true;
default:
throw new Exception($"Data type '{name}' not recognized, contact developer.");
}
}
}
}
+357
View File
@@ -0,0 +1,357 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.Text;
using Emgu.CV;
using UVtools.Parser;
namespace UVtools.Core
{
/// <summary>
/// Slicer file format representation interface
/// </summary>
public interface IFileFormat
{
#region Properties
/// <summary>
/// Gets the file format type
/// </summary>
FileFormat.FileFormatType FileType { get; }
/// <summary>
/// Gets the valid file extensions for this <see cref="FileFormat"/>
/// </summary>
FileExtension[] FileExtensions { get; }
/// <summary>
/// Gets the implemented file formats able to convert to
/// </summary>
Type[] ConvertToFormats { get; }
/// <summary>
/// Gets the available <see cref="FileFormat.PrintParameterModifier"/>
/// </summary>
FileFormat.PrintParameterModifier[] PrintParameterModifiers { get; }
/// <summary>
/// Gets the file filter for open and save dialogs
/// </summary>
string FileFilter { get; }
/// <summary>
/// Gets all valid file extensions in "*.extension1;*.extension2" format
/// </summary>
string FileFilterExtensionsOnly { get; }
/// <summary>
/// Gets the input file path loaded into this <see cref="FileFormat"/>
/// </summary>
string FileFullPath { get; set; }
/// <summary>
/// Gets the thumbnails count present in this file format
/// </summary>
byte ThumbnailsCount { get; }
/// <summary>
/// Gets the number of created thumbnails
/// </summary>
byte CreatedThumbnailsCount { get; }
/// <summary>
/// Gets the original thumbnail sizes
/// </summary>
System.Drawing.Size[] ThumbnailsOriginalSize { get; }
/// <summary>
/// Gets the thumbnails for this <see cref="FileFormat"/>
/// </summary>
Mat Thumbnails { get; set; }
/// <summary>
/// Gets the cached layers into compressed bytes
/// </summary>
LayerManager LayerManager { get; set; }
/// <summary>
/// Gets the image width resolution
/// </summary>
uint ResolutionX { get; }
/// <summary>
/// Gets the image height resolution
/// </summary>
uint ResolutionY { get; }
bool HaveAntiAliasing { get; }
/// <summary>
/// Gets the AntiAliasing level
/// </summary>
byte AntiAliasing { get; }
/// <summary>
/// Gets Layer Height in mm
/// </summary>
float LayerHeight { get; }
/// <summary>
/// Gets Total Height in mm
/// </summary>
float TotalHeight { get; }
/// <summary>
/// Gets the number of layers present in this file
/// </summary>
uint LayerCount { get; }
/// <summary>
/// Gets the number of initial layer count
/// </summary>
/// </summary>
ushort InitialLayerCount { get; }
/// <summary>
/// Gets the initial exposure time for <see cref="InitialLayerCount"/>
/// </summary>
float InitialExposureTime { get; }
/// <summary>
/// Gets the normal layer exposure time
/// </summary>
float LayerExposureTime { get; }
/// <summary>
/// Gets the speed in mm/min for the detracts
/// </summary>
float LiftSpeed { get; }
/// <summary>
/// Gets the height in mm to retract between layers
/// </summary>
float LiftHeight { get; }
/// <summary>
/// Gets the speed in mm/min for the retracts
/// </summary>
float RetractSpeed { get; }
/// <summary>
/// Gets the estimate print time in seconds
/// </summary>
float PrintTime { get; }
/// <summary>
/// Gets the estimate used material in ml
/// </summary>
float UsedMaterial { get; }
/// <summary>
/// Gets the estimate material cost
/// </summary>
float MaterialCost { get; }
/// <summary>
/// Gets the material name
/// </summary>
string MaterialName { get; }
/// <summary>
/// Gets the machine name
/// </summary>
string MachineName { get; }
/// <summary>
/// Gets the GCode, returns null if not supported
/// </summary>
StringBuilder GCode { get; set; }
/// <summary>
/// Get all configuration objects with properties and values
/// </summary>
object[] Configs { get; }
/// <summary>
/// Gets if this file is valid to read
/// </summary>
bool IsValid { get; }
#endregion
#region Methods
/// <summary>
/// Clears all definitions and properties, it also dispose valid candidates
/// </summary>
void Clear();
/// <summary>
/// Validate if a file is a valid <see cref="FileFormat"/>
/// </summary>
/// <param name="fileFullPath">Full file path</param>
void FileValidation(string fileFullPath);
/// <summary>
/// Checks if a extension is valid under the <see cref="FileFormat"/>
/// </summary>
/// <param name="extension">Extension to check</param>
/// <param name="isFilePath">True if <see cref="extension"/> is a full file path, otherwise false for extension only</param>
/// <returns>True if valid, otherwise false</returns>
bool IsExtensionValid(string extension, bool isFilePath = false);
/// <summary>
/// Gets all valid file extensions in a specified format
/// </summary>
string GetFileExtensions(string prepend = ".", string separator = ", ");
/// <summary>
/// Gets a thumbnail by it height or lower
/// </summary>
/// <param name="maxHeight">Max height allowed</param>
/// <returns></returns>
Mat GetThumbnail(uint maxHeight = 400);
/// <summary>
/// Sets thumbnails from a list of thumbnails and clone them
/// </summary>
/// <param name="images"></param>
void SetThumbnails(Mat[] images);
/// <summary>
/// Sets all thumbnails the same image
/// </summary>
/// <param name="images">Image to set</param>
void SetThumbnails(Mat images);
/// <summary>
/// Encode to an output file
/// </summary>
/// <param name="fileFullPath">Output file</param>
void Encode(string fileFullPath);
/*
/// <summary>
/// Begin encode to an output file
/// </summary>
/// <param name="fileFullPath">Output file</param>
//void BeginEncode(string fileFullPath);
/// <summary>
/// Insert a layer image to be encoded
/// </summary>
/// <param name="image"></param>
/// <param name="layerIndex"></param>
//void InsertLayerImageEncode(Image<L8> image, uint layerIndex);
/// <summary>
/// Finish the encoding procedure
/// </summary>
//void EndEncode();*/
/// <summary>
/// Decode a slicer file
/// </summary>
/// <param name="fileFullPath"></param>
void Decode(string fileFullPath);
/// <summary>
/// Extract contents to a folder
/// </summary>
/// <param name="path">Path to folder where content will be extracted</param>
/// <param name="genericConfigExtract"></param>
/// <param name="genericLayersExtract"></param>
void Extract(string path, bool genericConfigExtract = true, bool genericLayersExtract = true);
/// <summary>
/// Get height in mm from layer height
/// </summary>
/// <param name="layerIndex"></param>
/// <param name="realHeight"></param>
/// <returns>The height in mm</returns>
float GetHeightFromLayer(uint layerIndex, bool realHeight = true);
/// <summary>
/// Gets the value for initial layer or normal layers based on layer index
/// </summary>
/// <typeparam name="T">Type of value</typeparam>
/// <param name="layerIndex">Layer index</param>
/// <param name="initialLayerValue">Initial value</param>
/// <param name="normalLayerValue">Normal value</param>
/// <returns></returns>
T GetInitialLayerValueOrNormal<T>(uint layerIndex, T initialLayerValue, T normalLayerValue);
/// <summary>
/// Gets the value attributed to <see cref="FileFormat.PrintParameterModifier"/>
/// </summary>
/// <param name="modifier">Modifier to use</param>
/// <returns>A value</returns>
object GetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier);
/// <summary>
/// Sets a property value attributed to <see cref="modifier"/>
/// </summary>
/// <param name="modifier">Modifier to use</param>
/// <param name="value">Value to set</param>
/// <returns>True if set, otherwise false = <see cref="modifier"/> not found</returns>
bool SetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier, object value);
/// <summary>
/// Sets a property value attributed to <see cref="modifier"/>
/// </summary>
/// <param name="modifier">Modifier to use</param>
/// <param name="value">Value to set</param>
/// <returns>True if set, otherwise false = <see cref="modifier"/> not found</returns>
bool SetValueFromPrintParameterModifier(FileFormat.PrintParameterModifier modifier, string value);
/// <summary>
/// Saves current configuration on input file
/// </summary>
void Save();
/// <summary>
/// Saves current configuration on a copy
/// </summary>
/// <param name="filePath">File path to save copy as, use null to overwrite active file (Same as <see cref="Save"/>)</param>
void SaveAs(string filePath = null);
/// <summary>
/// Converts this file type to another file type
/// </summary>
/// <param name="to">Target file format</param>
/// <param name="fileFullPath">Output path file</param>
/// <returns>True if convert succeed, otherwise false</returns>
bool Convert(Type to, string fileFullPath);
/// <summary>
/// Converts this file type to another file type
/// </summary>
/// <param name="to">Target file format</param>
/// <param name="fileFullPath">Output path file</param>
/// <returns>True if convert succeed, otherwise false</returns>
bool Convert(FileFormat to, string fileFullPath);
/// <summary>
/// Resizes layer images in x and y factor, starting at 1 = 100%
/// </summary>
/// <param name="startLayerIndex">Layer index to start</param>
/// <param name="endLayerIndex">Layer index to end</param>
/// <param name="x">X factor, starts at 1</param>
/// <param name="y">Y factor, starts at 1</param>
/// <param name="fade">Fade X/Y towards 100%</param>
void Resize(uint startLayerIndex, uint endLayerIndex, float x, float y, bool fade);
/// <summary>
/// Validate AntiAlias Level
/// </summary>
byte ValidateAntiAliasingLevel();
#endregion
}
}
+853
View File
@@ -0,0 +1,853 @@
/*
* GNU AFFERO GENERAL PUBLIC LICENSE
* Version 3, 19 November 2007
* Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
* Everyone is permitted to copy and distribute verbatim copies
* of this license document, but changing it is not allowed.
*/
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV.Util;
using UVtools.Core.Extensions;
namespace UVtools.Parser
{
#region LayerIssue Class
public class LayerIssue : IEnumerable<Point>
{
public enum IssueType : byte
{
Island,
ResinTrap,
TouchingBound,
//HoleSandwich,
}
/// <summary>
/// Gets the parent layer
/// </summary>
public Layer Layer { get; }
/// <summary>
/// Gets the issue type associated
/// </summary>
public IssueType Type { get; }
/// <summary>
/// Gets the pixels containing the issue
/// </summary>
public Point[] Pixels { get; }
/// <summary>
/// Gets the bounding rectangle of the pixel area
/// </summary>
public Rectangle BoundingRectangle { get; }
/// <summary>
/// Gets the X coordinate for the first point, -1 if doesn't exists
/// </summary>
public int X => HaveValidPoint ? Pixels[0].X : -1;
/// <summary>
/// Gets the Y coordinate for the first point, -1 if doesn't exists
/// </summary>
public int Y => HaveValidPoint ? Pixels[0].Y : -1;
/// <summary>
/// Gets the XY point for first point
/// </summary>
public Point Point => HaveValidPoint ? Pixels[0] : new Point(-1, -1);
/// <summary>
/// Gets the number of pixels on this issue
/// </summary>
public uint Size {
get
{
if (Type == IssueType.ResinTrap && !BoundingRectangle.IsEmpty)
{
return (uint) (BoundingRectangle.Width * BoundingRectangle.Height);
}
if (ReferenceEquals(Pixels, null)) return 0;
return (uint) Pixels.Length;
}
}
/// <summary>
/// Check if this issue have a valid start point to show
/// </summary>
public bool HaveValidPoint => !ReferenceEquals(Pixels, null) && Pixels.Length > 0;
public LayerIssue(Layer layer, IssueType type, Point[] pixels = null, Rectangle boundingRectangle = new Rectangle())
{
Layer = layer;
Type = type;
Pixels = pixels;
BoundingRectangle = boundingRectangle;
}
public Point this[uint index] => Pixels[index];
public Point this[int index] => Pixels[index];
public IEnumerator<Point> GetEnumerator()
{
return ((IEnumerable<Point>)Pixels).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public override string ToString()
{
return $"{nameof(Type)}: {Type}";
}
}
#endregion
#region LayerHollowArea
public class LayerHollowArea : IEnumerable<Point>
{
public enum AreaType : byte
{
Unknown = 0,
Trap,
Drain
}
/// <summary>
/// Gets area pixels
/// </summary>
public Point[] Contour { get; }
public System.Drawing.Rectangle BoundingRectangle { get; }
public AreaType Type { get; set; } = AreaType.Unknown;
public bool Processed { get; set; }
#region Indexers
public Point this[uint index]
{
get => index < Contour.Length ? Contour[index] : Point.Empty;
set => Contour[index] = value;
}
public Point this[int index]
{
get => index < Contour.Length ? Contour[index] : Point.Empty;
set => Contour[index] = value;
}
public Point this[uint x, uint y]
{
get
{
for (uint i = 0; i < Contour.Length; i++)
{
if (Contour[i].X == x && Contour[i].Y == y) return Contour[i];
}
return Point.Empty;
}
}
public Point this[int x, int y] => this[(uint) x, (uint)y];
public Point this[Point point] => this[point.X, point.Y];
#endregion
public IEnumerator<Point> GetEnumerator()
{
return ((IEnumerable<Point>)Contour).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public LayerHollowArea()
{
}
public LayerHollowArea(Point[] contour, System.Drawing.Rectangle boundingRectangle, AreaType type = AreaType.Unknown)
{
Contour = contour;
BoundingRectangle = boundingRectangle;
Type = type;
}
}
#endregion
#region Layer Class
/// <summary>
/// Represent a Layer
/// </summary>
public class Layer : IEquatable<Layer>, IEquatable<uint>
{
#region Properties
/// <summary>
/// Gets the parent layer manager
/// </summary>
public LayerManager ParentLayerManager { get; set; }
/// <summary>
/// Gets the layer index
/// </summary>
public uint Index { get; }
private byte[] _compressedBytes;
/// <summary>
/// Gets or sets layer image compressed data
/// </summary>
public byte[] CompressedBytes
{
get => LayerManager.DecompressLayer(_compressedBytes);
set
{
_compressedBytes = LayerManager.CompressLayer(value);
IsModified = true;
}
}
/// <summary>
/// Gets the original filename, null if no filename attached with layer
/// </summary>
public string Filename { get; set; }
/// <summary>
/// Gets if layer has been modified
/// </summary>
public bool IsModified { get; set; }
/// <summary>
/// Gets or sets a new image instance
/// </summary>
public Mat LayerMat
{
get
{
Mat mat = new Mat();
CvInvoke.Imdecode(CompressedBytes, ImreadModes.Grayscale, mat);
return mat;
}
set
{
using (var vector = new VectorOfByte())
{
CvInvoke.Imencode(".png", value, vector);
CompressedBytes = vector.ToArray();
}
}
}
/// <summary>
/// Gets a new Brg image instance
/// </summary>
public Mat BrgMat
{
get {
using (Mat image = LayerMat)
{
Mat mat = new Mat();
CvInvoke.CvtColor(image, mat, ColorConversion.Gray2Bgr);
return mat;
}
}
}
#endregion
#region Constructor
public Layer(uint index, byte[] compressedBytes, string filename = null, LayerManager pararentLayerManager = null)
{
Index = index;
CompressedBytes = compressedBytes;
Filename = filename ?? $"Layer{index}.png";
IsModified = false;
ParentLayerManager = pararentLayerManager;
}
public Layer(uint index, Mat layerMat, string filename = null, LayerManager pararentLayerManager = null) : this(index, new byte[0], filename, pararentLayerManager)
{
LayerMat = layerMat;
IsModified = false;
}
public Layer(uint index, Stream stream, string filename = null, LayerManager pararentLayerManager = null) : this(index, stream.ToArray(), filename, pararentLayerManager)
{ }
#endregion
#region Equatables
public static bool operator ==(Layer obj1, Layer obj2)
{
return obj1.Equals(obj2);
}
public static bool operator !=(Layer obj1, Layer obj2)
{
return !obj1.Equals(obj2);
}
public static bool operator >(Layer obj1, Layer obj2)
{
return obj1.Index > obj2.Index;
}
public static bool operator <(Layer obj1, Layer obj2)
{
return obj1.Index < obj2.Index;
}
public static bool operator >=(Layer obj1, Layer obj2)
{
return obj1.Index >= obj2.Index;
}
public static bool operator <=(Layer obj1, Layer obj2)
{
return obj1.Index <= obj2.Index;
}
public bool Equals(uint other)
{
return Index == other;
}
public bool Equals(Layer other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(_compressedBytes, other._compressedBytes);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((Layer)obj);
}
public override int GetHashCode()
{
return (_compressedBytes != null ? _compressedBytes.GetHashCode() : 0);
}
private sealed class IndexRelationalComparer : IComparer<Layer>
{
public int Compare(Layer x, Layer y)
{
if (ReferenceEquals(x, y)) return 0;
if (ReferenceEquals(null, y)) return 1;
if (ReferenceEquals(null, x)) return -1;
return x.Index.CompareTo(y.Index);
}
}
public static IComparer<Layer> IndexComparer { get; } = new IndexRelationalComparer();
#endregion
#region Formaters
public override string ToString()
{
return $"{nameof(Index)}: {Index}, {nameof(Filename)}: {Filename}, {nameof(IsModified)}: {IsModified}";
}
#endregion
#region Methods
public Layer PreviousLayer()
{
if (ReferenceEquals(ParentLayerManager, null) || Index == 0)
return null;
return ParentLayerManager[Index - 1];
}
public Layer NextLayer()
{
if (ReferenceEquals(ParentLayerManager, null) || Index >= ParentLayerManager.Count - 1)
return null;
return ParentLayerManager[Index + 1];
}
/// <summary>
/// Gets all islands start pixel location for this layer
/// https://www.geeksforgeeks.org/find-number-of-islands/
/// </summary>
/// <returns><see cref="List{T}"/> holding all islands coordinates</returns>
public List<LayerIssue> GetIssues(uint requiredPixelsToSupportIsland = 5)
{
if (requiredPixelsToSupportIsland == 0)
requiredPixelsToSupportIsland = 1;
// These arrays are used to
// get row and column numbers
// of 8 neighbors of a given cell
List<LayerIssue> result = new List<LayerIssue>();
List<Point> pixels = new List<Point>();
var mat = LayerMat;
var bytes = mat.GetBytes();
var previousLayerImage = PreviousLayer()?.LayerMat;
byte[] previousBytes = previousLayerImage?.GetBytes();
/*var nextLayerImage = NextLayer()?.Image;
byte[] nextBytes = null;
if (!ReferenceEquals(nextLayerImage, null))
{
if (nextLayerImage.TryGetSinglePixelSpan(out var nextPixelSpan))
{
nextBytes = MemoryMarshal.AsBytes(nextPixelSpan).ToArray();
}
}*/
// Make a bool array to
// mark visited cells.
// Initially all cells
// are unvisited
bool[,] visited = new bool[mat.Width, mat.Height];
// Initialize count as 0 and
// traverse through the all
// cells of given matrix
//uint count = 0;
// Island checker
sbyte[] rowNbr = { -1, -1, -1, 0, 0, 1, 1, 1 };
sbyte[] colNbr = { -1, 0, 1, -1, 1, -1, 0, 1 };
const uint minPixel = 10;
const uint minPixelForSupportIsland = 200;
int pixelIndex;
uint islandSupportingPixels;
if (Index > 0)
{
for (int y = 0; y < mat.Height; y++)
{
for (int x = 0; x < mat.Width; x++)
{
pixelIndex = y * mat.Width + x;
/*if (bytes[pixelIndex] == 0 && previousBytes?[pixelIndex] == byte.MaxValue &&
nextBytes?[pixelIndex] == byte.MaxValue)
{
result.Add(new LayerIssue(this, LayerIssue.IssueType.HoleSandwich, new []{new Point(x, y)}));
}*/
if (bytes[pixelIndex] > minPixel && !visited[x, y])
{
// If a cell with value 1 is not
// visited yet, then new island
// found, Visit all cells in this
// island and increment island count
pixels.Clear();
pixels.Add(new Point(x, y));
islandSupportingPixels = previousBytes[pixelIndex] >= minPixelForSupportIsland ? 1u : 0;
int minX = x;
int maxX = x;
int minY = y;
int maxY = y;
int x2;
int y2;
Queue<Point> queue = new Queue<Point>();
queue.Enqueue(new Point(x, y));
// Mark this cell as visited
visited[x, y] = true;
while (queue.Count > 0)
{
var point = queue.Dequeue();
y2 = point.Y;
x2 = point.X;
for (byte k = 0; k < 8; k++)
{
//if (isSafe(y2 + rowNbr[k], x2 + colNbr[k]))
var tempy2 = y2 + rowNbr[k];
var tempx2 = x2 + colNbr[k];
pixelIndex = tempy2 * mat.Width + tempx2;
if (tempy2 >= 0 &&
tempy2 < mat.Height &&
tempx2 >= 0 && tempx2 < mat.Width &&
bytes[pixelIndex] >= minPixel &&
!visited[tempx2, tempy2])
{
visited[tempx2, tempy2] = true;
point = new Point(tempx2, tempy2);
pixels.Add(point);
queue.Enqueue(point);
minX = Math.Min(minX, tempx2);
maxX = Math.Max(maxX, tempx2);
minY = Math.Min(minY, tempy2);
maxY = Math.Max(maxY, tempy2);
islandSupportingPixels += previousBytes[pixelIndex] >= minPixelForSupportIsland ? 1u : 0;
}
}
}
//count++;
if (islandSupportingPixels >= requiredPixelsToSupportIsland)
continue; // Not a island, bounding is strong
if (islandSupportingPixels > 0 && pixels.Count < requiredPixelsToSupportIsland &&
islandSupportingPixels >= Math.Max(1, pixels.Count / 2)) continue; // Not a island
result.Add(new LayerIssue(this, LayerIssue.IssueType.Island, pixels.ToArray(), new Rectangle(minX, minY, maxX-minX, maxY-minY)));
}
}
}
}
pixels.Clear();
// TouchingBounds Checker
for (int x = 0; x < mat.Width; x++) // Check Top and Bottom bounds
{
if (bytes[x] >= 200) // Top
{
pixels.Add(new Point(x, 0));
}
if (bytes[mat.Width * mat.Height - mat.Width + x] >= 200) // Bottom
{
pixels.Add(new Point(x, mat.Height-1));
}
}
for (int y = 0; y < mat.Height; y++) // Check Left and Right bounds
{
if (bytes[y * mat.Width] >= 200) // Left
{
pixels.Add(new Point(0, y));
}
if (bytes[y * mat.Width + mat.Width - 1] >= 200) // Right
{
pixels.Add(new Point(mat.Width-1, y));
}
}
if (pixels.Count > 0)
{
result.Add(new LayerIssue(this, LayerIssue.IssueType.TouchingBound, pixels.ToArray()));
}
pixels.Clear();
return result;
}
public Layer Clone()
{
return new Layer(Index, CompressedBytes, Filename, ParentLayerManager);
}
#endregion
public void Resize(float newWidth, float newHeight)
{
using (var mat = LayerMat)
{
using (var resizedMat = new Mat())
{
int width = (int)(mat.Width * newWidth);
int height = (int)(mat.Height * newHeight);
Point location = new Point(mat.Width / 2 - width / 2, mat.Height / 2 - height / 2);
CvInvoke.Resize(mat, resizedMat, new Size(width, height));
mat.SetTo(new MCvScalar(0));
resizedMat.CopyTo(mat);
LayerMat = mat;
}
}
}
}
#endregion
#region LayerManager Class
public class LayerManager : IEnumerable<Layer>
{
#region Properties
/// <summary>
/// Layers List
/// </summary>
public Layer[] Layers { get; }
/// <summary>
/// Gets the layers count
/// </summary>
public uint Count => (uint) Layers.Length;
/// <summary>
/// Gets if any layer got modified, otherwise false
/// </summary>
public bool IsModified
{
get
{
for (uint i = 0; i < Count; i++)
{
if (Layers[i].IsModified) return true;
}
return false;
}
}
#endregion
#region Constructors
public LayerManager(uint layerCount)
{
Layers = new Layer[layerCount];
}
#endregion
#region Indexers
public Layer this[uint index]
{
get => Layers[index];
set => AddLayer(index, value);
}
public Layer this[int index]
{
get => Layers[index];
set => AddLayer((uint) index, value);
}
public Layer this[long index]
{
get => Layers[index];
set => AddLayer((uint) index, value);
}
#endregion
#region Numerators
public IEnumerator<Layer> GetEnumerator()
{
return ((IEnumerable<Layer>)Layers).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
#region Static Methods
/// <summary>
/// Compress a layer from a <see cref="Stream"/>
/// </summary>
/// <param name="input"><see cref="Stream"/> to compress</param>
/// <returns>Compressed byte array</returns>
public static byte[] CompressLayer(Stream input)
{
return CompressLayer(input.ToArray());
}
/// <summary>
/// Compress a layer from a byte array
/// </summary>
/// <param name="input">byte array to compress</param>
/// <returns>Compressed byte array</returns>
public static byte[] CompressLayer(byte[] input)
{
return input;
/*using (MemoryStream output = new MemoryStream())
{
using (DeflateStream dstream = new DeflateStream(output, CompressionLevel.Optimal))
{
dstream.Write(input, 0, input.Length);
}
return output.ToArray();
}*/
}
/// <summary>
/// Decompress a layer from a byte array
/// </summary>
/// <param name="input">byte array to decompress</param>
/// <returns>Decompressed byte array</returns>
public static byte[] DecompressLayer(byte[] input)
{
return input;
/*using (MemoryStream ms = new MemoryStream(input))
{
using (MemoryStream output = new MemoryStream())
{
using (DeflateStream dstream = new DeflateStream(ms, CompressionMode.Decompress))
{
dstream.CopyTo(output);
}
return output.ToArray();
}
}*/
}
#endregion
#region Methods
/// <summary>
/// Add a layer
/// </summary>
/// <param name="index">Layer index</param>
/// <param name="layer">Layer to add</param>
public void AddLayer(uint index, Layer layer)
{
Layers[index] = layer;
layer.ParentLayerManager = this;
}
/// <summary>
/// Get layer given index
/// </summary>
/// <param name="index">Layer index</param>
/// <returns></returns>
public Layer GetLayer(uint index)
{
return Layers[index];
}
public ConcurrentDictionary<uint, List<LayerIssue>> GetAllIssues()
{
var result = new ConcurrentDictionary<uint, List<LayerIssue>>();
Parallel.ForEach(this, layer =>
{
var issues = layer.GetIssues();
if (issues.Count > 0)
{
if (!result.TryAdd(layer.Index, issues))
{
throw new AccessViolationException("Error while trying to add an issue to the dictionary, please try again.");
}
}
});
/*const byte minPixel = 50;
sbyte[] rowNbr = { -1, -1, -1, 0, 0, 1, 1, 1 };
sbyte[] colNbr = { -1, 0, 1, -1, 1, -1, 0, 1 };
int pixelIndex;
for (uint layerindex = 0; layerindex < Count; layerindex++)
{
var image = this[layerindex].Image;
byte[] bytes = null;
if (image.TryGetSinglePixelSpan(out var pixelSpan))
{
bytes = MemoryMarshal.AsBytes(pixelSpan).ToArray();
}
bool[,] visited = new bool[image.Width, image.Height];
for (int y = 0; y < image.Height; y++)
{
for (int x = 0; x < image.Width; x++)
{
pixelIndex = y * image.Width + x;
if (bytes[pixelIndex] > minPixel && !visited[y, x])
{
Queue<Point> queue = new Queue<Point>();
queue.Enqueue(new Point(x, y));
// Mark this cell as visited
visited[x, y] = true;
var x2 = x;
var y2 = y;
while (queue.Count > 0)
{
var point = queue.Dequeue();
y2 = point.Y;
x2 = point.X;
for (byte k = 0; k < 8; k++)
{
//if (isSafe(y2 + rowNbr[k], x2 + colNbr[k]))
var tempy2 = y2 + rowNbr[k];
var tempx2 = x2 + colNbr[k];
pixelIndex = tempy2 * image.Width + tempx2;
if (tempy2 >= 0 &&
tempy2 < image.Height &&
tempx2 >= 0 && tempx2 < image.Width &&
bytes[pixelIndex] >= minPixel &&
!visited[tempx2, tempy2])
{
visited[tempx2, tempy2] = true;
point = new Point(tempx2, tempy2);
pixels.Add(point);
queue.Enqueue(point);
islandSupportingPixels += previousBytes[pixelIndex] >= minPixelForSupportIsland ? 1u : 0;
}
}
}
}
}
}
}*/
return result;
}
/// <summary>
/// Desmodify all layers
/// </summary>
public void Desmodify()
{
for (uint i = 0; i < Count; i++)
{
Layers[i].IsModified = false;
}
}
/// <summary>
/// Clone this object
/// </summary>
/// <returns></returns>
public LayerManager Clone()
{
LayerManager layerManager = new LayerManager(Count);
foreach (var layer in this)
{
layerManager[layer.Index] = layer.Clone();
}
return layerManager;
}
#endregion
}
#endregion
}
+28
View File
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Company>PTRTECH</Company>
<Authors>Tiago Conceição</Authors>
<RepositoryType>Git</RepositoryType>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description>
</PropertyGroup>
<ItemGroup>
<None Include="..\LICENSE">
<Pack>True</Pack>
<PackagePath></PackagePath>
</None>
</ItemGroup>
<ItemGroup>
<PackageReference Include="BinarySerializer" Version="8.5.1" />
<PackageReference Include="Emgu.CV" Version="4.3.0.3890" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>
@@ -6,12 +6,16 @@
* of this license document, but changing it is not allowed.
*/
using System;
using System.Drawing;
using System.IO;
using Emgu.CV;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using SixLabors.ImageSharp;
using SixLabors.ImageSharp.PixelFormats;
using UVtools.Parser;
using Image = SixLabors.ImageSharp.Image;
namespace UVtools.GUI.Extensions
{
@@ -40,6 +44,13 @@ namespace UVtools.GUI.Extensions
};
}
public static Mat ToEmguMat(this Image<L8> image)
{
var mat = new Mat(new System.Drawing.Size(image.Width, image.Height), DepthType.Cv8U, 1);
mat.SetTo(Helpers.ImageL8ToBytes(image));
return mat;
}
/*public static Image<TPixel> ToImageSharpImage<TPixel>(this System.Drawing.Bitmap bitmap) where TPixel : struct, IPixel<TPixel>
{
using (var memoryStream = new MemoryStream())
+11 -6
View File
@@ -1715,13 +1715,19 @@ namespace UVtools.GUI
if (tsLayerImageLayerOutline.Checked)
{
Image<Gray, byte> greyscale = ActualLayerImage.ToEmguImage();
Image<Gray, byte> greyscale = ActualLayerImage.ToEmguImage(); // 10ms
SlicerFile[ActualLayer].Image = ActualLayerImage; // 127ms
var mat = ActualLayerImage.ToEmguMat(); // 4ms
var bytes = new VectorOfByte();
CvInvoke.Imencode(".png", mat, bytes); // 9ms
CvInvoke.Imdecode(bytes, ImreadModes.Grayscale, mat); // 10ms
Debug.WriteLine(CvInvoke.UseOpenCL);
#if DEBUG
greyscale = greyscale.ThresholdBinary(new Gray(254), new Gray(255));
VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint();
Mat hierarchy = new Mat();
CvInvoke.FindContours(greyscale, contours, hierarchy, RetrType.Ccomp, ChainApproxMethod.ChainApproxSimple);
/*
@@ -1735,10 +1741,9 @@ namespace UVtools.GUI
{
if ((int)arr.GetValue(0, i, 3) >= 0) continue;
var r = CvInvoke.BoundingRectangle(contours[i]);
CvInvoke.Rectangle(greyscale, r, new MCvScalar(80), 2);
greyscale.Draw(contours, i, new Gray(125), -1);
//CvInvoke.Rectangle(greyscale, r, new MCvScalar(80), 2);
greyscale.Draw(contours, -1, new Gray(125), -1);
//CvInvoke.DrawContours(mat, contours, -1, new MCvScalar(255), -1);
}
#else
+2 -2
View File
@@ -32,5 +32,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.5.2.1")]
[assembly: AssemblyFileVersion("0.5.2.1")]
[assembly: AssemblyVersion("0.5.2.2")]
[assembly: AssemblyFileVersion("0.5.2.2")]
-1
View File
@@ -9,7 +9,6 @@ using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
+18 -11
View File
@@ -586,10 +586,13 @@ namespace UVtools.Parser
{
KeyRing kr = new KeyRing(Parent.HeaderSettings.EncryptionKey, layerIndex);
EncodedRle = kr.Read(rawData).ToArray();
return;
}
else
{
EncodedRle = rawData.ToArray();
}
EncodedRle = rawData.ToArray();
DataSize = (uint) EncodedRle.Length;
}
public override string ToString()
@@ -603,8 +606,8 @@ namespace UVtools.Parser
public class KeyRing
{
public ulong Init { get; }
public ulong Key { get; private set; }
public uint Init { get; }
public uint Key { get; private set; }
public uint Index { get; private set; }
public KeyRing(uint seed, uint layerIndex)
@@ -819,11 +822,12 @@ namespace UVtools.Parser
for (uint layerIndex = 0; layerIndex < LayerCount; layerIndex++)
{
LayerData layerData = LayersDefinitions[layerIndex];
LayerData layerDataHash = null;
if (HeaderSettings.EncryptionKey == 0)
{
string hash = Helpers.ComputeSHA1Hash(layerData.EncodedRle);
if (LayersHash.TryGetValue(hash, out var layerDataHash))
if (LayersHash.TryGetValue(hash, out layerDataHash))
{
layerData.DataAddress = layerDataHash.DataAddress;
layerData.DataSize = layerDataHash.DataSize;
@@ -831,15 +835,18 @@ namespace UVtools.Parser
else
{
LayersHash.Add(hash, layerData);
layerData.DataAddress = layerDataCurrentOffset;
layerData.DataSize = (uint)layerData.EncodedRle.Length;
outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin);
layerDataCurrentOffset += outputFile.WriteBytes(layerData.EncodedRle);
}
}
if (ReferenceEquals(layerDataHash, null))
{
layerData.DataAddress = layerDataCurrentOffset;
outputFile.Seek(layerDataCurrentOffset, SeekOrigin.Begin);
layerDataCurrentOffset += outputFile.WriteBytes(layerData.EncodedRle);
}
LayersDefinitions[layerIndex] = layerData;
outputFile.Seek(currentOffset, SeekOrigin.Begin);
+6 -5
View File
@@ -7,13 +7,14 @@
<PackageProjectUrl>https://github.com/sn4k3/UVtools</PackageProjectUrl>
<PackageIcon></PackageIcon>
<RepositoryUrl>https://github.com/sn4k3/UVtools</RepositoryUrl>
<AssemblyVersion>0.5.2.1</AssemblyVersion>
<FileVersion>0.5.2.1</FileVersion>
<Version>0.5.2.1</Version>
<AssemblyVersion>0.5.2.2</AssemblyVersion>
<FileVersion>0.5.2.2</FileVersion>
<Version>0.5.2.2</Version>
<Description>MSLA/DLP, file analysis, repair, conversion and manipulation</Description>
<PackageId>UVtoolsParser</PackageId>
<PackageId>UVtools.Parser</PackageId>
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<Product>UVtoolsParser</Product>
<Product>UVtools.Parser</Product>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
</PropertyGroup>
<ItemGroup>
+10
View File
@@ -14,6 +14,8 @@ Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "UVtools.InstallerMM", "UVto
EndProject
Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "UVtools.Installer", "UVtools.Installer\UVtools.Installer.wixproj", "{41224896-08E9-4F22-9E56-5F9D46DEC7D2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UVtools.Core", "UVtools.Core\UVtools.Core.csproj", "{7C9927F8-132E-4A37-B894-440E0FD5AA3D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -50,6 +52,14 @@ Global
{41224896-08E9-4F22-9E56-5F9D46DEC7D2}.Release|Any CPU.ActiveCfg = Release|x86
{41224896-08E9-4F22-9E56-5F9D46DEC7D2}.Release|x86.ActiveCfg = Release|x86
{41224896-08E9-4F22-9E56-5F9D46DEC7D2}.Release|x86.Build.0 = Release|x86
{7C9927F8-132E-4A37-B894-440E0FD5AA3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7C9927F8-132E-4A37-B894-440E0FD5AA3D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7C9927F8-132E-4A37-B894-440E0FD5AA3D}.Debug|x86.ActiveCfg = Debug|Any CPU
{7C9927F8-132E-4A37-B894-440E0FD5AA3D}.Debug|x86.Build.0 = Debug|Any CPU
{7C9927F8-132E-4A37-B894-440E0FD5AA3D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7C9927F8-132E-4A37-B894-440E0FD5AA3D}.Release|Any CPU.Build.0 = Release|Any CPU
{7C9927F8-132E-4A37-B894-440E0FD5AA3D}.Release|x86.ActiveCfg = Release|Any CPU
{7C9927F8-132E-4A37-B894-440E0FD5AA3D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE