Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/sn4k3/UVtools.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'UVtools.Core/FileFormats/MDLPFile.cs')
-rw-r--r--UVtools.Core/FileFormats/MDLPFile.cs493
1 files changed, 493 insertions, 0 deletions
diff --git a/UVtools.Core/FileFormats/MDLPFile.cs b/UVtools.Core/FileFormats/MDLPFile.cs
new file mode 100644
index 0000000..784ad28
--- /dev/null
+++ b/UVtools.Core/FileFormats/MDLPFile.cs
@@ -0,0 +1,493 @@
+/*
+ * 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.
+ */
+
+// https://github.com/cbiffle/catibo/blob/master/doc/cbddlp-ctb.adoc
+
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Threading.Tasks;
+using BinarySerialization;
+using Emgu.CV;
+using Emgu.CV.CvEnum;
+using UVtools.Core.Extensions;
+using UVtools.Core.Operations;
+
+namespace UVtools.Core.FileFormats
+{
+ public class MDLPFile : FileFormat
+ {
+ #region Constants
+
+ private const uint SlicerInfoAddress = 4 + 7 + 290 * 290 * 2 + 116 * 116 * 2 + 4;
+ #endregion
+
+ #region Sub Classes
+
+ #region Header
+
+ public sealed class Header
+ {
+ public const byte HEADER_SIZE = 7;
+ public const string HEADER_VALUE = "MKSDLP";
+
+ [FieldOrder(0)]
+ [FieldEndianness(Endianness.Big)]
+ public uint HeaderSize { get; set; } = HEADER_SIZE;
+
+ /// <summary>
+ /// Gets the file tag = MKSDLP
+ /// </summary>
+ [FieldOrder(1)]
+ [FieldLength(HEADER_SIZE)]
+ [SerializeAs(SerializedType.TerminatedString)]
+ public string HeaderValue { get; set; } = HEADER_VALUE;
+
+ public void Validate()
+ {
+ if (HeaderSize != HEADER_SIZE || HeaderValue != HEADER_VALUE)
+ {
+ throw new FileLoadException("Not a valid Makerbase mdlp file!");
+ }
+ }
+ }
+
+ public sealed class SlicerInfo
+ {
+ // 290 * 290 * 2 + 116 * 116 * 2 + 4
+ //[FieldOrder(0)] [FieldLength(195116)] public byte[] PreviewData { get; set; }
+
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort LayerCount { get; set; }
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; }
+ [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; }
+ [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public uint DisplayWidthDataSize { get; set; } = 6;
+ [FieldOrder(4)] [FieldLength(nameof(DisplayWidthDataSize))] public byte[] DisplayWidth { get; set; }
+ [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public uint DisplayHeightDataSize { get; set; } = 6;
+ [FieldOrder(6)] [FieldLength(nameof(DisplayHeightDataSize))] public byte[] DisplayHeight { get; set; }
+
+ [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public uint LayerHeightDataSize { get; set; } = 6;
+ [FieldOrder(8)] [FieldLength(nameof(LayerHeightDataSize))] public byte[] LayerHeight { get; set; }
+ [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public ushort ExposureTime { get; set; }
+ [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort LightOffDelay { get; set; }
+ [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public ushort BottomExposureTime { get; set; }
+ [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public ushort BottomLayers { get; set; }
+ /* [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftHeight { get; set; }
+ [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeed { get; set; }
+ [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public ushort LiftHeight { get; set; }
+ [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeed { get; set; }
+ [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeed { get; set; }
+ [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public ushort BottomLightPWM { get; set; }
+ [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public ushort LightPWM { get; set; }*/
+ }
+ #endregion
+
+ #region LayerDef
+
+ public sealed class LayerDef
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint LineCount { get; set; }
+ [FieldOrder(1)] [FieldCount(nameof(LineCount))] public LayerLine[] Lines { get; set; }
+ [FieldOrder(2)] public PageBreak PageBreak { get; set; } = new();
+
+
+ public LayerDef(uint lineCount, LayerLine[] lines)
+ {
+ LineCount = lineCount;
+ Lines = lines;
+ }
+ }
+
+ public sealed class LayerLine
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort StartY { get; set; }
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort EndY { get; set; }
+ [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort StartX { get; set; }
+
+
+ public LayerLine()
+ { }
+
+ public LayerLine(ushort startY, ushort endY, ushort startX)
+ {
+ StartY = startY;
+ EndY = endY;
+ StartX = startX;
+ }
+ }
+
+ public sealed class PageBreak
+ {
+ [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public byte Line { get; set; } = 0x0D;
+ [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public byte Break { get; set; } = 0x0A;
+ }
+
+ #endregion
+ #endregion
+
+ #region Properties
+
+ public Header HeaderSettings { get; protected internal set; } = new();
+ public SlicerInfo SlicerInfoSettings { get; protected internal set; } = new();
+ public override FileFormatType FileType => FileFormatType.Binary;
+
+ public override FileExtension[] FileExtensions { get; } = {
+ new ("mdlp", "Makerbase MDLP v1"),
+ };
+
+ public override PrintParameterModifier[] PrintParameterModifiers { get; } =
+ {
+ PrintParameterModifier.BottomLayerCount,
+ PrintParameterModifier.BottomExposureSeconds,
+ PrintParameterModifier.ExposureSeconds,
+
+ //PrintParameterModifier.BottomLightOffDelay,
+ PrintParameterModifier.LightOffDelay,
+ /*PrintParameterModifier.BottomLiftHeight,
+ PrintParameterModifier.BottomLiftSpeed,
+ PrintParameterModifier.LiftHeight,
+ PrintParameterModifier.LiftSpeed,
+ PrintParameterModifier.RetractSpeed,
+
+ PrintParameterModifier.BottomLightPWM,
+ PrintParameterModifier.LightPWM,*/
+ };
+
+ public override Size[] ThumbnailsOriginalSize { get; } =
+ {
+ new (116, 116),
+ new (290, 290)
+ };
+
+ public override uint ResolutionX
+ {
+ get => SlicerInfoSettings.ResolutionX;
+ set
+ {
+ SlicerInfoSettings.ResolutionX = (ushort) value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public override uint ResolutionY
+ {
+ get => SlicerInfoSettings.ResolutionY;
+ set
+ {
+ SlicerInfoSettings.ResolutionY = (ushort) value;
+ RaisePropertyChanged();
+ }
+ }
+
+ public override float DisplayWidth
+ {
+ get => float.Parse(SlicerInfoSettings.DisplayWidth.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b)));
+ set
+ {
+ string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture);
+ SlicerInfoSettings.DisplayWidthDataSize = (uint) (str.Length * 2);
+ var data = new byte[SlicerInfoSettings.DisplayWidthDataSize];
+ for (var i = 0; i < str.Length; i++)
+ {
+ data[i * 2] = System.Convert.ToByte(str[i]);
+ }
+
+ SlicerInfoSettings.DisplayWidth = data;
+ RaisePropertyChanged();
+ }
+ }
+
+ public override float DisplayHeight
+ {
+ get => float.Parse(SlicerInfoSettings.DisplayHeight.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b)));
+ set
+ {
+ string str = Math.Round(value, 2).ToString(CultureInfo.InvariantCulture);
+ SlicerInfoSettings.DisplayHeightDataSize = (uint)(str.Length * 2);
+ var data = new byte[SlicerInfoSettings.DisplayHeightDataSize];
+ for (var i = 0; i < str.Length; i++)
+ {
+ data[i * 2] = System.Convert.ToByte(str[i]);
+ }
+
+ SlicerInfoSettings.DisplayHeight = data;
+ RaisePropertyChanged();
+ }
+ }
+
+ public override bool MirrorDisplay { get; set; }
+
+ public override byte AntiAliasing
+ {
+ get => 1;
+ set { }
+ }
+
+ public override float LayerHeight
+ {
+ get => float.Parse(SlicerInfoSettings.LayerHeight.Where(b => b != 0).Aggregate(string.Empty, (current, b) => current + System.Convert.ToChar(b)));
+ set
+ {
+ string str = Layer.RoundHeight(value).ToString(CultureInfo.InvariantCulture);
+ SlicerInfoSettings.LayerHeightDataSize = (uint)(str.Length * 2);
+ var data = new byte[SlicerInfoSettings.LayerHeightDataSize];
+ for (var i = 0; i < str.Length; i++)
+ {
+ data[i * 2] = System.Convert.ToByte(str[i]);
+ }
+
+ SlicerInfoSettings.LayerHeight = data;
+ RaisePropertyChanged();
+ }
+ }
+
+ public override uint LayerCount
+ {
+ get => base.LayerCount;
+ set => base.LayerCount = SlicerInfoSettings.LayerCount = (ushort)base.LayerCount;
+ }
+
+ public override ushort BottomLayerCount
+ {
+ get => SlicerInfoSettings.BottomLayers;
+ set => base.BottomLayerCount = SlicerInfoSettings.BottomLayers = value;
+ }
+
+ public override float BottomExposureTime
+ {
+ get => SlicerInfoSettings.BottomExposureTime;
+ set => base.BottomExposureTime = SlicerInfoSettings.BottomExposureTime = (ushort)value;
+ }
+
+ public override float ExposureTime
+ {
+ get => SlicerInfoSettings.ExposureTime;
+ set => base.ExposureTime = SlicerInfoSettings.ExposureTime = (ushort)value;
+ }
+
+ public override float BottomLightOffDelay
+ {
+ get => LightOffDelay;
+ set { }
+ }
+
+ public override float LightOffDelay
+ {
+ get => SlicerInfoSettings.LightOffDelay;
+ set => base.LightOffDelay = SlicerInfoSettings.LightOffDelay = (ushort)value;
+ }
+
+ public override object[] Configs => new[] { (object)HeaderSettings, SlicerInfoSettings };
+
+ #endregion
+
+ #region Constructors
+
+ #endregion
+
+ #region Methods
+ protected override void EncodeInternally(string fileFullPath, OperationProgress progress)
+ {
+ using var outputFile = new FileStream(fileFullPath, FileMode.Create, FileAccess.Write);
+ var pageBreak = new PageBreak();
+
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+
+ byte[][] previews = new byte[2][];
+ for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ {
+ previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2];
+ }
+ // Previews
+ Parallel.For(0, previews.Length, previewIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ if (Thumbnails[previewIndex] is null) return;
+ var span = Thumbnails[previewIndex].GetPixelSpanByte();
+ int index = 0;
+ for (int i = 0; i < span.Length; i += 3)
+ {
+ byte b = span[i];
+ byte g = span[i + 1];
+ byte r = span[i + 2];
+
+ ushort rgb15 = (ushort)(((r >> 3) << 11) | ((g >> 2) << 5) | ((b >> 3) << 0));
+
+ previews[previewIndex][index++] = (byte)(rgb15 >> 8);
+ previews[previewIndex][index++] = (byte)(rgb15 & 0xff);
+ }
+
+ if (index != previews[previewIndex].Length)
+ {
+ throw new FileLoadException($"Preview encode incomplete encode, expected: {previews[previewIndex].Length}, encoded: {index}");
+ }
+ });
+
+ for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ {
+ Helpers.SerializeWriteFileStream(outputFile, previews[i]);
+ Helpers.SerializeWriteFileStream(outputFile, pageBreak);
+ }
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+
+ progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount);
+ var layerDefs = new LayerDef[LayerCount];
+ Parallel.For(0, LayerCount, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ List<LayerLine> layerLines = new();
+ var layer = this[layerIndex];
+ using var mat = layer.LayerMat;
+ var span = mat.GetPixelSpanByte();
+
+ for (int x = layer.BoundingRectangle.X; x < layer.BoundingRectangle.Right; x++)
+ {
+ int y = layer.BoundingRectangle.Y;
+ int startY = -1;
+ for (; y < layer.BoundingRectangle.Bottom; y++)
+ {
+ int pos = mat.GetPixelPos(x, y);
+ if (span[pos] < 128) // Black pixel
+ {
+ if(startY == -1) continue; // Keep ignoring
+ layerLines.Add(new LayerLine((ushort) startY, (ushort) (y-1), (ushort) x));
+ startY = -1;
+ }
+ else // White pixel
+ {
+ if (startY >= 0) continue; // Keep sum
+ startY = y;
+ }
+ }
+
+ if (startY >= 0)
+ {
+ layerLines.Add(new LayerLine((ushort)startY, (ushort)(y - 1), (ushort) x));
+ }
+ }
+
+ layerDefs[layerIndex] = new LayerDef((uint) layerLines.Count, layerLines.ToArray());
+
+ progress.LockAndIncrement();
+ });
+
+ progress.Reset(OperationProgress.StatusWritingFile, LayerCount);
+ for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ Helpers.SerializeWriteFileStream(outputFile, layerDefs[layerIndex]);
+ progress++;
+ }
+
+ Helpers.SerializeWriteFileStream(outputFile, HeaderSettings);
+
+ Debug.WriteLine("Encode Results:");
+ Debug.WriteLine(HeaderSettings);
+ Debug.WriteLine("-End-");
+ }
+
+
+
+ protected override void DecodeInternally(string fileFullPath, OperationProgress progress)
+ {
+ using var inputFile = new FileStream(fileFullPath, FileMode.Open, FileAccess.Read);
+ //HeaderSettings = Helpers.ByteToType<CbddlpFile.Header>(InputFile);
+ //HeaderSettings = Helpers.Serializer.Deserialize<Header>(InputFile.ReadBytes(Helpers.Serializer.SizeOf(typeof(Header))));
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ HeaderSettings.Validate();
+
+ byte[][] previews = new byte[ThumbnailsOriginalSize.Length][];
+ for (int i = 0; i < ThumbnailsOriginalSize.Length; i++)
+ {
+ previews[i] = new byte[ThumbnailsOriginalSize[i].Area() * 2];
+ inputFile.ReadBytes(previews[i]);
+ inputFile.Seek(2, SeekOrigin.Current);
+ }
+
+ Parallel.For(0, previews.Length, previewIndex =>
+ {
+ var mat = new Mat(ThumbnailsOriginalSize[previewIndex], DepthType.Cv8U, 3);
+ var span = mat.GetPixelSpanByte();
+
+ int spanIndex = 0;
+ for (int i = 0; i < previews[previewIndex].Length; i += 2)
+ {
+ ushort rgb15 = (ushort)((ushort)(previews[previewIndex][i + 0] << 8) | previews[previewIndex][i + 1]);
+ byte r = (byte)((rgb15 >> 11) << 3);
+ byte g = (byte)((rgb15 >> 5) << 2);
+ byte b = (byte)((rgb15 >> 0) << 3);
+
+ span[spanIndex++] = b;
+ span[spanIndex++] = g;
+ span[spanIndex++] = r;
+ }
+
+ Thumbnails[previewIndex] = mat;
+ });
+
+
+ SlicerInfoSettings = Helpers.Deserialize<SlicerInfo>(inputFile);
+
+ LayerManager.Init(SlicerInfoSettings.LayerCount);
+ progress.ItemCount = LayerCount;
+ LayerDef[] layerDefs = new LayerDef[LayerCount];
+ for (int layerIndex = 0; layerIndex < LayerCount; layerIndex++)
+ {
+ progress.Token.ThrowIfCancellationRequested();
+ layerDefs[layerIndex] = Helpers.Deserialize<LayerDef>(inputFile);
+ progress++;
+ }
+
+ progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount);
+ Parallel.For(0, LayerCount, layerIndex =>
+ {
+ if (progress.Token.IsCancellationRequested) return;
+ using var mat = EmguExtensions.InitMat(Resolution);
+ foreach (var line in layerDefs[layerIndex].Lines)
+ {
+ CvInvoke.Line(mat, new Point(line.StartX, line.StartY), new Point(line.StartX, line.EndY), EmguExtensions.WhiteByte);
+ }
+
+ this[layerIndex] = new Layer((uint) layerIndex, mat, this);
+ progress.LockAndIncrement();
+ });
+
+ HeaderSettings = Helpers.Deserialize<Header>(inputFile);
+ HeaderSettings.Validate();
+ }
+
+ public override void SaveAs(string filePath = null, OperationProgress progress = null)
+ {
+ if (RequireFullEncode)
+ {
+ if (!string.IsNullOrEmpty(filePath))
+ {
+ FileFullPath = filePath;
+ }
+ Encode(FileFullPath, progress);
+ return;
+ }
+
+
+ if (!string.IsNullOrEmpty(filePath))
+ {
+ File.Copy(FileFullPath, filePath, true);
+ FileFullPath = filePath;
+ }
+
+ using var outputFile = new FileStream(FileFullPath, FileMode.Open, FileAccess.Write);
+ outputFile.Seek(SlicerInfoAddress, SeekOrigin.Begin);
+ Helpers.SerializeWriteFileStream(outputFile, SlicerInfoSettings);
+ }
+
+ #endregion
+ }
+}