/* * GNU AFFERO GENERAL PUBLIC LICENSE * Version 3, 19 November 2007 * Copyright (C) 2007 Free Software Foundation, Inc. * Everyone is permitted to copy and distribute verbatim copies * of this license document, but changing it is not allowed. */ using BinarySerialization; using Emgu.CV; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.IO; using System.Threading.Tasks; using UVtools.Core.Extensions; using UVtools.Core.Layers; using UVtools.Core.Objects; using UVtools.Core.Operations; namespace UVtools.Core.FileFormats; public sealed class OSFFile : FileFormat { #region Constants public const ushort DEFAULT_VERSION = 4; #endregion #region Sub Classes #region Header public class OSFHeader { [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public uint HeaderLength { get; set; } = 350001; [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort Version { get; set; } = DEFAULT_VERSION; [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public byte ImageLog { get; set; } = 2; public override string ToString() { return $"{nameof(HeaderLength)}: {HeaderLength}, {nameof(Version)}: {Version}, {nameof(ImageLog)}: {ImageLog}"; } } public sealed class OSFSettings { [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort ResolutionX { get; set; } [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public ushort ResolutionY { get; set; } [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort PixelUmMagnified100Times { get; set; } /// /// (0x00 not mirrored, 0x01 X-axis mirroring, 0x02 Y-axis mirroring, 0x03 XY-axis mirroring) /// [FieldOrder(3)] [FieldEndianness(Endianness.Big)] public byte Mirror { get; set; } [FieldOrder(4)] [FieldEndianness(Endianness.Big)] public byte BottomLightPWM { get; set; } = DefaultBottomLightPWM; [FieldOrder(5)] [FieldEndianness(Endianness.Big)] public byte LightPWM { get; set; } = DefaultLightPWM; [FieldOrder(6)] [FieldEndianness(Endianness.Big)] public bool AntiAliasEnabled { get; set; } [FieldOrder(7)] [FieldEndianness(Endianness.Big)] public bool DistortionEnabled { get; set; } [FieldOrder(8)] [FieldEndianness(Endianness.Big)] public bool DelayedExposureActivationEnabled { get; set; } [FieldOrder(9)] [FieldEndianness(Endianness.Big)] public uint LayerCount { get; set; } [FieldOrder(10)] [FieldEndianness(Endianness.Big)] public ushort NumberParameterSets { get; set; } = 1; [FieldOrder(11)] [FieldEndianness(Endianness.Big)] public uint LastLayerIndex { set; get; } [FieldOrder(12)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian LayerHeightUmMagnified100Times { set; get; } = new(); [FieldOrder(13)] [FieldEndianness(Endianness.Big)] public byte BottomLayerCount { set; get; } [FieldOrder(14)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian ExposureTimeMagnified100Times { set; get; } = new((uint) (DefaultExposureTime*1000)); [FieldOrder(15)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomExposureTimeMagnified100Times { set; get; } = new((uint)(DefaultBottomExposureTime * 1000)); [FieldOrder(16)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian SupportDelayTimeMagnified100Times { set; get; } = new(50); [FieldOrder(17)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomSupportDelayTimeMagnified100Times { set; get; } = new(50); [FieldOrder(18)] [FieldEndianness(Endianness.Big)] public byte TransitionLayerCount { set; get; } /// /// (0x00 linear transition) /// [FieldOrder(19)] [FieldEndianness(Endianness.Big)] public byte TransitionType { set; get; } [FieldOrder(20)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian TransitionLayerIntervalTimeDifferenceMagnified100Times { set; get; } = new(); [FieldOrder(21)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian WaitTimeAfterCureMagnified100Times { set; get; } = new(); [FieldOrder(22)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian WaitTimeAfterLiftMagnified100Times { set; get; } = new(); [FieldOrder(23)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian WaitTimeBeforeCureMagnified100Times { set; get; } = new(); [FieldOrder(24)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomLiftHeightSlowMagnified1000Times { set; get; } = new(); [FieldOrder(25)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomLiftHeightTotalMagnified1000Times { set; get; } = new((uint)(DefaultBottomLiftHeight * 1000)); [FieldOrder(26)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian LiftHeightSlowMagnified1000Times { set; get; } = new(); [FieldOrder(27)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian LiftHeightTotalMagnified1000Times { set; get; } = new((uint)(DefaultLiftHeight * 1000)); [FieldOrder(28)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomRetractHeightSlowMagnified1000Times { set; get; } = new(); [FieldOrder(29)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian BottomRetractHeightTotalMagnified1000Times { set; get; } = new((uint)(DefaultBottomLiftHeight * 1000)); [FieldOrder(30)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian RetractHeightSlowMagnified1000Times { set; get; } = new(); [FieldOrder(31)] [FieldEndianness(Endianness.Big)] public UInt24BigEndian RetractHeightTotalMagnified1000Times { set; get; } = new((uint)(DefaultLiftHeight * 1000)); /// /// (0x00: S-shaped acceleration, 0x01: T-shaped acceleration, Default Value: S-shaped acceleration, currently only supports S-shaped acceleration) /// [FieldOrder(32)] [FieldEndianness(Endianness.Big)] public byte AccelerationType { set; get; } [FieldOrder(33)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedStart { set; get; } = 80; [FieldOrder(34)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedSlow { set; get; } = (ushort)DefaultBottomLiftSpeed; [FieldOrder(35)] [FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedFast { set; get; } = (ushort)DefaultBottomLiftSpeed2; [FieldOrder(36)] [FieldEndianness(Endianness.Big)] public byte BottomLiftAccelerationChange { set; get; } = 5; [FieldOrder(37)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedStart { set; get; } = 80; [FieldOrder(38)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedSlow { set; get; } = (ushort)DefaultLiftSpeed; [FieldOrder(39)] [FieldEndianness(Endianness.Big)] public ushort LiftSpeedFast { set; get; } = (ushort)DefaultLiftSpeed2; [FieldOrder(40)] [FieldEndianness(Endianness.Big)] public byte LiftAccelerationChange { set; get; } = 5; [FieldOrder(41)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedStart { set; get; } = 80; [FieldOrder(42)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedSlow { set; get; } = (ushort)DefaultBottomRetractSpeed2; [FieldOrder(43)] [FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedFast { set; get; } = (ushort)DefaultBottomRetractSpeed; [FieldOrder(44)] [FieldEndianness(Endianness.Big)] public byte BottomRetractAccelerationChange { set; get; } = 5; [FieldOrder(45)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeedStart { set; get; } = 80; [FieldOrder(46)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeedSlow { set; get; } = (ushort)DefaultRetractSpeed2; [FieldOrder(47)] [FieldEndianness(Endianness.Big)] public ushort RetractSpeedFast { set; get; } = (ushort)DefaultRetractSpeed; [FieldOrder(48)] [FieldEndianness(Endianness.Big)] public byte RetractAccelerationChange { set; get; } = 5; [FieldOrder(49)][FieldEndianness(Endianness.Big)] public ushort BottomLiftSpeedEnd { set; get; } = 7; [FieldOrder(50)][FieldEndianness(Endianness.Big)] public byte BottomLiftDecelerationChange { set; get; } = 5; [FieldOrder(51)][FieldEndianness(Endianness.Big)] public ushort LiftSpeedEnd { set; get; } = 7; [FieldOrder(52)][FieldEndianness(Endianness.Big)] public byte LiftDecelerationChange { set; get; } = 5; [FieldOrder(53)][FieldEndianness(Endianness.Big)] public ushort BottomRetractSpeedEnd { set; get; } = 7; [FieldOrder(54)][FieldEndianness(Endianness.Big)] public byte BottomRetractDecelerationChange { set; get; } = 5; [FieldOrder(55)][FieldEndianness(Endianness.Big)] public ushort RetractSpeedEnd { set; get; } = 7; [FieldOrder(56)][FieldEndianness(Endianness.Big)] public byte RetractDecelerationChange { set; get; } = 5; [FieldOrder(57)][FieldEndianness(Endianness.Big)] public ushort BottomWaitTimeAfterCureMagnified100Times { set; get; } [FieldOrder(58)][FieldEndianness(Endianness.Big)] public ushort BottomWaitTimeAfterLiftMagnified100Times { set; get; } [FieldOrder(59)][FieldEndianness(Endianness.Big)] public ushort BottomWaitTimeBeforeCureMagnified100Times { set; get; } [FieldOrder(60)] [FieldEndianness(Endianness.Big)] public ushort Reserved { set; get; } [FieldOrder(61)] [FieldEndianness(Endianness.Big)] public byte ProtocolType { set; get; } // 0 public override string ToString() { return $"{nameof(ResolutionX)}: {ResolutionX}, {nameof(ResolutionY)}: {ResolutionY}, {nameof(PixelUmMagnified100Times)}: {PixelUmMagnified100Times}, {nameof(Mirror)}: {Mirror}, {nameof(BottomLightPWM)}: {BottomLightPWM}, {nameof(LightPWM)}: {LightPWM}, {nameof(AntiAliasEnabled)}: {AntiAliasEnabled}, {nameof(DistortionEnabled)}: {DistortionEnabled}, {nameof(DelayedExposureActivationEnabled)}: {DelayedExposureActivationEnabled}, {nameof(LayerCount)}: {LayerCount}, {nameof(NumberParameterSets)}: {NumberParameterSets}, {nameof(LastLayerIndex)}: {LastLayerIndex}, {nameof(LayerHeightUmMagnified100Times)}: {LayerHeightUmMagnified100Times}, {nameof(BottomLayerCount)}: {BottomLayerCount}, {nameof(ExposureTimeMagnified100Times)}: {ExposureTimeMagnified100Times}, {nameof(BottomExposureTimeMagnified100Times)}: {BottomExposureTimeMagnified100Times}, {nameof(SupportDelayTimeMagnified100Times)}: {SupportDelayTimeMagnified100Times}, {nameof(BottomSupportDelayTimeMagnified100Times)}: {BottomSupportDelayTimeMagnified100Times}, {nameof(TransitionLayerCount)}: {TransitionLayerCount}, {nameof(TransitionType)}: {TransitionType}, {nameof(TransitionLayerIntervalTimeDifferenceMagnified100Times)}: {TransitionLayerIntervalTimeDifferenceMagnified100Times}, {nameof(WaitTimeAfterCureMagnified100Times)}: {WaitTimeAfterCureMagnified100Times}, {nameof(WaitTimeAfterLiftMagnified100Times)}: {WaitTimeAfterLiftMagnified100Times}, {nameof(WaitTimeBeforeCureMagnified100Times)}: {WaitTimeBeforeCureMagnified100Times}, {nameof(BottomLiftHeightSlowMagnified1000Times)}: {BottomLiftHeightSlowMagnified1000Times}, {nameof(BottomLiftHeightTotalMagnified1000Times)}: {BottomLiftHeightTotalMagnified1000Times}, {nameof(LiftHeightSlowMagnified1000Times)}: {LiftHeightSlowMagnified1000Times}, {nameof(LiftHeightTotalMagnified1000Times)}: {LiftHeightTotalMagnified1000Times}, {nameof(BottomRetractHeightSlowMagnified1000Times)}: {BottomRetractHeightSlowMagnified1000Times}, {nameof(BottomRetractHeightTotalMagnified1000Times)}: {BottomRetractHeightTotalMagnified1000Times}, {nameof(RetractHeightSlowMagnified1000Times)}: {RetractHeightSlowMagnified1000Times}, {nameof(RetractHeightTotalMagnified1000Times)}: {RetractHeightTotalMagnified1000Times}, {nameof(AccelerationType)}: {AccelerationType}, {nameof(BottomLiftSpeedStart)}: {BottomLiftSpeedStart}, {nameof(BottomLiftSpeedSlow)}: {BottomLiftSpeedSlow}, {nameof(BottomLiftSpeedFast)}: {BottomLiftSpeedFast}, {nameof(BottomLiftAccelerationChange)}: {BottomLiftAccelerationChange}, {nameof(LiftSpeedStart)}: {LiftSpeedStart}, {nameof(LiftSpeedSlow)}: {LiftSpeedSlow}, {nameof(LiftSpeedFast)}: {LiftSpeedFast}, {nameof(LiftAccelerationChange)}: {LiftAccelerationChange}, {nameof(BottomRetractSpeedStart)}: {BottomRetractSpeedStart}, {nameof(BottomRetractSpeedSlow)}: {BottomRetractSpeedSlow}, {nameof(BottomRetractSpeedFast)}: {BottomRetractSpeedFast}, {nameof(BottomRetractAccelerationChange)}: {BottomRetractAccelerationChange}, {nameof(RetractSpeedStart)}: {RetractSpeedStart}, {nameof(RetractSpeedSlow)}: {RetractSpeedSlow}, {nameof(RetractSpeedFast)}: {RetractSpeedFast}, {nameof(RetractAccelerationChange)}: {RetractAccelerationChange}, {nameof(BottomLiftSpeedEnd)}: {BottomLiftSpeedEnd}, {nameof(BottomLiftDecelerationChange)}: {BottomLiftDecelerationChange}, {nameof(LiftSpeedEnd)}: {LiftSpeedEnd}, {nameof(LiftDecelerationChange)}: {LiftDecelerationChange}, {nameof(BottomRetractSpeedEnd)}: {BottomRetractSpeedEnd}, {nameof(BottomRetractDecelerationChange)}: {BottomRetractDecelerationChange}, {nameof(RetractSpeedEnd)}: {RetractSpeedEnd}, {nameof(RetractDecelerationChange)}: {RetractDecelerationChange}, {nameof(BottomWaitTimeAfterCureMagnified100Times)}: {BottomWaitTimeAfterCureMagnified100Times}, {nameof(BottomWaitTimeAfterLiftMagnified100Times)}: {BottomWaitTimeAfterLiftMagnified100Times}, {nameof(BottomWaitTimeBeforeCureMagnified100Times)}: {BottomWaitTimeBeforeCureMagnified100Times}, {nameof(Reserved)}: {Reserved}, {nameof(ProtocolType)}: {ProtocolType}"; } } #endregion #region LayerDef public class OSFLayerDef { /// /// OD OA begins, indicating that the model + support is included; the beginning of 0D 0B, indicating that the layer only has support data /// [FieldOrder(0)] [FieldEndianness(Endianness.Big)] public ushort Mark { get; set; } = 0x0D_0A; [FieldOrder(1)] [FieldEndianness(Endianness.Big)] public uint NumberOfLines { get; set; } [FieldOrder(2)] [FieldEndianness(Endianness.Big)] public ushort StartY { get; set; } [Ignore] public byte[] EncodedRle { get; set; } = Array.Empty(); public OSFLayerDef() { } public override string ToString() { return $"{nameof(Mark)}: {Mark}, {nameof(NumberOfLines)}: {NumberOfLines}, {nameof(StartY)}: {StartY}"; } internal unsafe void EncodeImage(Mat mat, Layer layer) { List rawData = new(); byte color = 0; uint stride = 0; var span = mat.GetBytePointer(); var imageLength = mat.GetLength(); var step = mat.GetRealStep(); uint lines = 0; void AddRep() { switch (stride) { case 0: return; case 1: color &= 0xfe; break; case > 1: color |= 0x01; break; } lines++; rawData.Add(color); if (stride <= 1) { // no run needed return; } if (stride <= 0x7f) { rawData.Add((byte)stride); return; } if (stride <= 0x3fff) { rawData.Add((byte)((stride >> 8) | 0x80)); rawData.Add((byte)stride); return; } if (stride <= 0x1fffff) { rawData.Add((byte)((stride >> 16) | 0xc0)); rawData.Add((byte)(stride >> 8)); rawData.Add((byte)stride); return; } if (stride <= 0xfffffff) { rawData.Add((byte)((stride >> 24) | 0xe0)); rawData.Add((byte)(stride >> 16)); rawData.Add((byte)(stride >> 8)); rawData.Add((byte)stride); } } for (var pixel = StartY * step; pixel <= layer.LastPixelIndex; pixel++) { var grey = span[pixel]; if (grey == color) { stride++; } else { AddRep(); color = grey; stride = 1; } } // Left-over if(color != 0) AddRep(); stride = (uint) ((imageLength - layer.LastPixelIndex - 1) % step); color = 0; AddRep(); NumberOfLines = lines; EncodedRle = rawData.ToArray(); } internal Mat DecodeImage(OSFFile parent) { var mat = parent.CreateMat(); if (NumberOfLines == 0) return mat; int pixel = (int)(StartY * parent.ResolutionX); for (var n = 0; n < EncodedRle.Length; n++) { byte code = EncodedRle[n]; int stride = 1; if ((code & 0x01) == 0x01) // It's a run { code &= 0xfe; // Get the grey value var slen = EncodedRle[++n]; if ((slen & 0x80) == 0) { stride = slen; } else if ((slen & 0xc0) == 0x80) { stride = ((slen & 0x3f) << 8) + EncodedRle[n + 1]; n++; } else if ((slen & 0xe0) == 0xc0) { stride = ((slen & 0x1f) << 16) + (EncodedRle[n + 1] << 8) + EncodedRle[n + 2]; n += 2; } else if ((slen & 0xf0) == 0xe0) { stride = ((slen & 0xf) << 24) + (EncodedRle[n + 1] << 16) + (EncodedRle[n + 2] << 8) + EncodedRle[n + 3]; n += 3; } else { mat.Dispose(); throw new FileLoadException("Corrupted RLE data"); } } // Bit extend from 7-bit to 8-bit greymap if (code != 0) { code = (byte)(code | 1); } mat.FillSpan(ref pixel, stride, code); } return mat; } } #endregion #endregion #region Properties public OSFHeader Header { get; private set; } = new(); public OSFSettings Settings { get; private set; } = new(); public override FileFormatType FileType => FileFormatType.Binary; public override FileExtension[] FileExtensions { get; } = { new (typeof(OSFFile), "osf", "Vlare Open File Format (OSF)"), }; public override PrintParameterModifier[]? PrintParameterModifiers { get; } = { PrintParameterModifier.BottomLayerCount, PrintParameterModifier.TransitionLayerCount, PrintParameterModifier.BottomWaitTimeBeforeCure, PrintParameterModifier.WaitTimeBeforeCure, PrintParameterModifier.BottomExposureTime, PrintParameterModifier.ExposureTime, PrintParameterModifier.BottomWaitTimeAfterCure, PrintParameterModifier.WaitTimeAfterCure, PrintParameterModifier.BottomLiftHeight, PrintParameterModifier.BottomLiftSpeed, PrintParameterModifier.LiftHeight, PrintParameterModifier.LiftSpeed, PrintParameterModifier.BottomLiftHeight2, PrintParameterModifier.BottomLiftSpeed2, PrintParameterModifier.LiftHeight2, PrintParameterModifier.LiftSpeed2, PrintParameterModifier.BottomWaitTimeAfterLift, PrintParameterModifier.WaitTimeAfterLift, PrintParameterModifier.BottomRetractSpeed, PrintParameterModifier.RetractSpeed, PrintParameterModifier.BottomRetractHeight2, PrintParameterModifier.BottomRetractSpeed2, PrintParameterModifier.RetractHeight2, PrintParameterModifier.RetractSpeed2, PrintParameterModifier.BottomLightPWM, PrintParameterModifier.LightPWM, }; public override Size[]? ThumbnailsOriginalSize { get; } = { new(148, 80), new(300, 140), new(208, 116), new(404, 240), }; public override uint[] AvailableVersions { get; } = { 4 }; public override uint DefaultVersion => DEFAULT_VERSION; public override uint Version { get => Header.Version; set { base.Version = value; Header.Version = (ushort) base.Version; } } public override uint ResolutionX { get => Settings.ResolutionX; set => base.ResolutionX = Settings.ResolutionX = (ushort) value; } public override uint ResolutionY { get => Settings.ResolutionY; set => base.ResolutionY = Settings.ResolutionY = (ushort)value; } public override FlipDirection DisplayMirror { get => Settings.Mirror switch { 1 => FlipDirection.Horizontally, 2 => FlipDirection.Vertically, 3 => FlipDirection.Both, _ => FlipDirection.None }; set { Settings.Mirror = value switch { FlipDirection.None => 0, FlipDirection.Horizontally => 1, FlipDirection.Vertically => 2, FlipDirection.Both => 3, _ => throw new ArgumentOutOfRangeException(nameof(value), value, null) }; } } public override float LayerHeight { get => Layer.RoundHeight(Settings.LayerHeightUmMagnified100Times.Value / 1000_00f); set { Settings.LayerHeightUmMagnified100Times.Value = (ushort)(value * 1000_00f); RaisePropertyChanged(); } } public override uint LayerCount { get => base.LayerCount; set { base.LayerCount = Settings.LayerCount = base.LayerCount; Settings.LastLayerIndex = Settings.LayerCount - 1; } } public override ushort BottomLayerCount { get => Settings.BottomLayerCount; set { Settings.BottomLayerCount = (byte)value; base.BottomLayerCount = value; } } public override TransitionLayerTypes TransitionLayerType => TransitionLayerTypes.Software; public override ushort TransitionLayerCount { get => Settings.TransitionLayerCount; set => base.TransitionLayerCount = Settings.TransitionLayerCount = (byte)Math.Min(byte.MaxValue, Math.Min(value, MaximumPossibleTransitionLayerCount)); } public override float BottomLightOffDelay => BottomWaitTimeBeforeCure; public override float LightOffDelay => WaitTimeBeforeCure; public override float BottomWaitTimeBeforeCure { get => (float)Math.Round(Settings.BottomWaitTimeBeforeCureMagnified100Times / 100f, 2); set { Settings.BottomWaitTimeBeforeCureMagnified100Times = (ushort) (value * 100); base.BottomWaitTimeBeforeCure = (float)Math.Round(value, 2); } } public override float WaitTimeBeforeCure { get => (float)Math.Round(Settings.WaitTimeBeforeCureMagnified100Times.Value / 100f, 2); set { Settings.WaitTimeBeforeCureMagnified100Times.Value = (uint)(value * 100); base.WaitTimeBeforeCure = (float)Math.Round(value, 2); } } public override float BottomExposureTime { get => (float)Math.Round(Settings.BottomExposureTimeMagnified100Times.Value / 100f, 2); set { Settings.BottomExposureTimeMagnified100Times.Value = (uint)(value * 100); base.BottomExposureTime = (float)Math.Round(value, 2); } } public override float ExposureTime { get => (float)Math.Round(Settings.ExposureTimeMagnified100Times.Value / 100f, 2); set { Settings.ExposureTimeMagnified100Times.Value = (uint)(value * 100); base.ExposureTime = (float)Math.Round(value, 2); } } public override float BottomWaitTimeAfterCure { get => (float)Math.Round(Settings.BottomWaitTimeAfterCureMagnified100Times / 100f, 2); set { Settings.BottomWaitTimeAfterCureMagnified100Times = (ushort)(value * 100); base.BottomWaitTimeAfterCure = (float)Math.Round(value, 2); } } public override float WaitTimeAfterCure { get => (float)Math.Round(Settings.WaitTimeAfterCureMagnified100Times.Value / 100f, 2); set { Settings.WaitTimeAfterCureMagnified100Times.Value = (uint)(value * 100); base.WaitTimeAfterCure = (float)Math.Round(value, 2); } } public override float BottomLiftHeight { get => (float)Math.Round(Settings.BottomLiftHeightSlowMagnified1000Times.Value / 1000f, 2); set { value = (float)Math.Round(value, 2); Settings.BottomLiftHeightTotalMagnified1000Times.Value -= Settings.BottomLiftHeightSlowMagnified1000Times.Value; Settings.BottomLiftHeightSlowMagnified1000Times.Value = (uint)(value * 1000); Settings.BottomLiftHeightTotalMagnified1000Times.Value += Settings.BottomLiftHeightSlowMagnified1000Times.Value; base.BottomLiftHeight = value; } } public override float LiftHeight { get => (float)Math.Round(Settings.LiftHeightSlowMagnified1000Times.Value / 1000f, 2); set { value = (float)Math.Round(value, 2); Settings.LiftHeightTotalMagnified1000Times.Value -= Settings.LiftHeightSlowMagnified1000Times.Value; Settings.LiftHeightSlowMagnified1000Times.Value = (uint)(value * 1000); Settings.LiftHeightTotalMagnified1000Times.Value += Settings.LiftHeightSlowMagnified1000Times.Value; base.LiftHeight = value; } } public override float BottomLiftSpeed { get => Settings.BottomLiftSpeedSlow; set => base.BottomLiftSpeed = Settings.BottomLiftSpeedSlow = (ushort)value; } public override float LiftSpeed { get => Settings.LiftSpeedSlow; set => base.LiftSpeed = Settings.LiftSpeedSlow = (ushort)value; } public override float BottomLiftHeight2 { get => (float)Math.Max(0, Math.Round((Settings.BottomLiftHeightTotalMagnified1000Times - Settings.BottomLiftHeightSlowMagnified1000Times) / 1000f, 2)); set { value = (float)Math.Round(value, 2); Settings.BottomLiftHeightTotalMagnified1000Times.Value = Settings.BottomLiftHeightSlowMagnified1000Times.Value + (uint)(value * 1000); base.BottomLiftHeight2 = value; } } public override float BottomLiftSpeed2 { get =>Settings.BottomLiftSpeedFast; set => base.BottomLiftSpeed2 = Settings.BottomLiftSpeedFast = (ushort) value; } public override float LiftHeight2 { get => (float)Math.Max(0, Math.Round((Settings.LiftHeightTotalMagnified1000Times - Settings.LiftHeightSlowMagnified1000Times) / 1000f, 2)); set { value = (float)Math.Round(value, 2); Settings.LiftHeightTotalMagnified1000Times.Value = Settings.LiftHeightSlowMagnified1000Times.Value + (uint)(value * 1000); base.LiftHeight2 = value; } } public override float LiftSpeed2 { get => Settings.LiftSpeedFast; set => base.LiftSpeed2 = Settings.LiftSpeedFast = (ushort)value; } public override float BottomWaitTimeAfterLift { get => (float)Math.Round(Settings.BottomWaitTimeAfterLiftMagnified100Times / 100f, 2); set { Settings.BottomWaitTimeAfterLiftMagnified100Times = (ushort)(value * 100); base.BottomWaitTimeAfterLift = (float)Math.Round(value, 2); } } public override float WaitTimeAfterLift { get => (float)Math.Round(Settings.WaitTimeAfterLiftMagnified100Times.Value / 100f, 2); set { Settings.WaitTimeAfterLiftMagnified100Times.Value = (uint)(value * 100); base.WaitTimeAfterLift = (float)Math.Round(value, 2); } } public override float BottomRetractSpeed { get => Settings.BottomRetractSpeedFast; set => base.BottomRetractSpeed = Settings.BottomRetractSpeedFast = (ushort)value; } public override float RetractSpeed { get => Settings.RetractSpeedFast; set => base.RetractSpeed = Settings.RetractSpeedFast = (ushort)value; } public override float BottomRetractHeight2 { get => (float)Math.Round(Settings.BottomRetractHeightSlowMagnified1000Times.Value / 1000f, 2); set { value = Math.Clamp((float)Math.Round(value, 2), 0, BottomRetractHeightTotal); Settings.BottomRetractHeightSlowMagnified1000Times.Value = (uint)(value * 1000); Settings.BottomRetractHeightTotalMagnified1000Times.Value = (uint)(BottomRetractHeightTotal * 1000); base.BottomRetractHeight2 = value; } } public override float BottomRetractSpeed2 { get => Settings.BottomRetractSpeedSlow; set => base.BottomRetractSpeed2 = Settings.BottomRetractSpeedSlow = (ushort)value; } public override float RetractHeight2 { get => (float)Math.Round(Settings.RetractHeightSlowMagnified1000Times.Value / 1000f, 2); set { value = Math.Clamp((float)Math.Round(value, 2), 0, RetractHeightTotal); Settings.RetractHeightSlowMagnified1000Times.Value = (uint)(value * 1000); Settings.RetractHeightTotalMagnified1000Times.Value = (uint)(RetractHeightTotal * 1000); base.RetractHeight2 = value; } } public override float RetractSpeed2 { get => Settings.RetractSpeedSlow; set => base.RetractSpeed2 = Settings.RetractSpeedSlow = (ushort)value; } public override byte BottomLightPWM { get => Settings.BottomLightPWM; set => base.BottomLightPWM = Settings.BottomLightPWM = value; } public override byte LightPWM { get => Settings.LightPWM; set => base.LightPWM = Settings.LightPWM = value; } public override object[] Configs => new object[] { Settings }; #endregion #region Constructors public OSFFile() { } #endregion #region Methods protected override void EncodeInternally(OperationProgress progress) { using var outputFile = new FileStream(TemporaryOutputFileFullPath, FileMode.Create, FileAccess.Write); Settings.PixelUmMagnified100Times = (ushort)(PixelSizeMicronsMax * 100); Header.HeaderLength = (uint) (Helpers.Serializer.SizeOf(Header) + Helpers.Serializer.SizeOf(Settings) + 3 * 4); var previews = new[] { Array.Empty(), Array.Empty(), Array.Empty(), Array.Empty() }; for (var i = 0; i < previews.Length; i++) { if (Thumbnails[i] is null) continue; previews[i] = EncodeImage(DATATYPE_RGB565, Thumbnails[i]!); Header.HeaderLength += (uint)previews[i].Length; } outputFile.WriteSerialize(Header); for (var i = 0; i < previews.Length; i++) { outputFile.WriteSerialize(new UInt24BigEndian((uint) previews[i].Length)); outputFile.WriteBytes(previews[i]); } outputFile.WriteSerialize(Settings); progress.Reset(OperationProgress.StatusEncodeLayers, LayerCount); var layerDef = new OSFLayerDef[LayerCount]; foreach (var batch in BatchLayersIndexes()) { Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { progress.PauseIfRequested(); var layer = this[layerIndex]; using (var mat = layer.LayerMat) { layerDef[layerIndex] = new OSFLayerDef { StartY = (ushort)layer.BoundingRectangle.Y, }; layerDef[layerIndex].EncodeImage(mat, layer); } progress.LockAndIncrement(); }); foreach (var layerIndex in batch) { progress.PauseOrCancelIfRequested(); outputFile.WriteSerialize(layerDef[layerIndex]); outputFile.WriteBytes(layerDef[layerIndex].EncodedRle); layerDef[layerIndex].EncodedRle = null!; // Free this } } Debug.WriteLine("Encode Results:"); Debug.WriteLine(Header); Debug.WriteLine(Settings); Debug.WriteLine("-End-"); } protected override void DecodeInternally(OperationProgress progress) { using var inputFile = new FileStream(FileFullPath!, FileMode.Open, FileAccess.Read); Header = Helpers.Deserialize(inputFile); Debug.WriteLine(Header); for (byte i = 0; i < ThumbnailsOriginalSize!.Length; i++) { var previewSize = Helpers.Deserialize(inputFile); var previewData = inputFile.ReadBytes(previewSize.Value); Thumbnails[i] = DecodeImage(DATATYPE_RGB565, previewData, ThumbnailsOriginalSize[i]); } Settings = Helpers.Deserialize(inputFile); Debug.WriteLine(Settings); Display = new SizeF( ResolutionX * (Settings.PixelUmMagnified100Times / 100_000f), ResolutionY * (Settings.PixelUmMagnified100Times / 100_000f) ); Init(Settings.LayerCount, DecodeType == FileDecodeType.Partial); if (DecodeType == FileDecodeType.Full) { inputFile.Seek(Header.HeaderLength, SeekOrigin.Begin); var layerDef = new OSFLayerDef[LayerCount]; var rle = new List(); progress.Reset(OperationProgress.StatusDecodeLayers, LayerCount); foreach (var batch in BatchLayersIndexes()) { foreach (var layerIndex in batch) { progress.PauseOrCancelIfRequested(); //Debug.WriteLine($"{layerIndex}: {inputFile.Position}"); layerDef[layerIndex] = Helpers.Deserialize(inputFile); if (layerDef[layerIndex].NumberOfLines == 0) continue; int buffer; int slen; while ((buffer = inputFile.ReadByte()) > -1) { if ((buffer & 0x01) == 0x01) // It's a run { slen = inputFile.ReadByte(); if (buffer == 0x0D && slen is 0x0A or 0x0B) { inputFile.Seek(-2, SeekOrigin.Current); break; } rle.Add((byte)buffer); rle.Add((byte)slen); if ((slen & 0x80) == 0) { } else if ((slen & 0xc0) == 0x80) { rle.Add((byte)inputFile.ReadByte()); } else if ((slen & 0xe0) == 0xc0) { rle.AddRange(inputFile.ReadBytes(2)); } else if ((slen & 0xf0) == 0xe0) { rle.AddRange(inputFile.ReadBytes(3)); } else { throw new FileLoadException("Corrupted RLE data"); } } else { rle.Add((byte)buffer); } } layerDef[layerIndex].EncodedRle = rle.ToArray(); rle.Clear(); } Parallel.ForEach(batch, CoreSettings.GetParallelOptions(progress), layerIndex => { progress.PauseIfRequested(); using var mat = layerDef[layerIndex].DecodeImage(this); _layers[layerIndex] = new Layer((uint)layerIndex, mat, this); layerDef[layerIndex].EncodedRle = null!; progress.LockAndIncrement(); }); } } RebuildLayersProperties(); } protected override void PartialSaveInternally(OperationProgress progress) { using var outputFile = new FileStream(TemporaryOutputFileFullPath, FileMode.Open, FileAccess.Write); outputFile.Seek(Header.HeaderLength - Helpers.Serializer.SizeOf(Settings), SeekOrigin.Begin); outputFile.WriteSerialize(Settings); } #endregion }