diff --git a/CHANGELOG.md b/CHANGELOG.md
index a729c1fa..f431d414 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## 22/09/2020 - v0.8.2.4
+
+* (Add) Layer Importer: Option to merge images
+* (Improvement) Layer difference computation time, faster render
+
## 19/09/2020 - v0.8.2.3
* (Add) Tooltip for next and previous layer buttons with associated shortcut (#61)
diff --git a/UVtools.Core/FileFormats/FileFormat.cs b/UVtools.Core/FileFormats/FileFormat.cs
index 7c72f152..3f245f25 100644
--- a/UVtools.Core/FileFormats/FileFormat.cs
+++ b/UVtools.Core/FileFormats/FileFormat.cs
@@ -354,7 +354,9 @@ public virtual uint LayerCount
public abstract float PrintTime { get; }
-
+
+ public float PrintTimeHours => (float) Math.Round(PrintTime / 3600, 2);
+
public abstract float UsedMaterial { get; }
public abstract float MaterialCost { get; }
diff --git a/UVtools.Core/FileFormats/IFileFormat.cs b/UVtools.Core/FileFormats/IFileFormat.cs
index e166b55c..3b778580 100644
--- a/UVtools.Core/FileFormats/IFileFormat.cs
+++ b/UVtools.Core/FileFormats/IFileFormat.cs
@@ -185,6 +185,11 @@ public interface IFileFormat
///
float PrintTime { get; }
+ ///
+ /// Gets the estimate print time in hours
+ ///
+ float PrintTimeHours { get; }
+
///
/// Gets the estimate used material in ml
///
diff --git a/UVtools.Core/Layer/LayerManager.cs b/UVtools.Core/Layer/LayerManager.cs
index ad3e1494..183a333c 100644
--- a/UVtools.Core/Layer/LayerManager.cs
+++ b/UVtools.Core/Layer/LayerManager.cs
@@ -1343,6 +1343,16 @@ public void Import(OperationLayerImport operation, OperationProgress progress =
{
var mat = CvInvoke.Imread(operation.Files[i], ImreadModes.Grayscale);
uint layerIndex = (uint) (startIndex + i);
+ if (operation.MergeImages)
+ {
+ if (!(this[layerIndex] is null))
+ {
+ using (var oldMat = this[layerIndex].LayerMat)
+ {
+ CvInvoke.Add(oldMat, mat, mat);
+ }
+ }
+ }
this[layerIndex] = new Layer(layerIndex, mat);
lock (progress.Mutex)
diff --git a/UVtools.Core/Operations/OperationLayerImport.cs b/UVtools.Core/Operations/OperationLayerImport.cs
index 89e31597..b7c8502f 100644
--- a/UVtools.Core/Operations/OperationLayerImport.cs
+++ b/UVtools.Core/Operations/OperationLayerImport.cs
@@ -80,6 +80,7 @@ public override StringTag Validate(params object[] parameters)
public bool ReplaceStartLayer { get; set; }
public bool ReplaceSubsequentLayers { get; set; }
public bool DiscardRemainingLayers { get; set; }
+ public bool MergeImages { get; set; }
public List Files { get; } = new List();
diff --git a/UVtools.Core/UVtools.Core.csproj b/UVtools.Core/UVtools.Core.csproj
index 9d1d0f17..df16c45c 100644
--- a/UVtools.Core/UVtools.Core.csproj
+++ b/UVtools.Core/UVtools.Core.csproj
@@ -10,12 +10,12 @@
https://github.com/sn4k3/UVtools
https://github.com/sn4k3/UVtools
MSLA/DLP, file analysis, repair, conversion and manipulation
- 0.8.2.3
+ 0.8.2.4
Copyright © 2020 PTRTECH
UVtools.png
AnyCPU;x64
- 0.8.2.3
- 0.8.2.3
+ 0.8.2.4
+ 0.8.2.4
diff --git a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs
index 66a17c1f..371dff4a 100644
--- a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs
+++ b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.Designer.cs
@@ -48,6 +48,7 @@ private void InitializeComponent()
this.pbSelectedImage = new System.Windows.Forms.PictureBox();
this.cbReplaceSubsequentLayers = new System.Windows.Forms.CheckBox();
this.cbDiscardRemainingLayers = new System.Windows.Forms.CheckBox();
+ this.cbMergeImages = new System.Windows.Forms.CheckBox();
((System.ComponentModel.ISupportInitialize)(this.nmInsertAfterLayer)).BeginInit();
this.tsBar.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.splitContainer)).BeginInit();
@@ -276,11 +277,25 @@ private void InitializeComponent()
this.cbDiscardRemainingLayers.UseVisualStyleBackColor = true;
this.cbDiscardRemainingLayers.CheckedChanged += new System.EventHandler(this.EventClick);
//
+ // cbMergeImages
+ //
+ this.cbMergeImages.AutoSize = true;
+ this.cbMergeImages.Enabled = false;
+ this.cbMergeImages.Location = new System.Drawing.Point(244, 83);
+ this.cbMergeImages.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5);
+ this.cbMergeImages.Name = "cbMergeImages";
+ this.cbMergeImages.Size = new System.Drawing.Size(128, 24);
+ this.cbMergeImages.TabIndex = 41;
+ this.cbMergeImages.Text = "Merge images";
+ this.toolTip.SetToolTip(this.cbMergeImages, "Merge source layer with target layer\r\n(Require \"Replace subsequent layers\")");
+ this.cbMergeImages.UseVisualStyleBackColor = true;
+ this.cbMergeImages.CheckedChanged += new System.EventHandler(this.EventClick);
+ //
// CtrlToolLayerImport
//
this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink;
+ this.Controls.Add(this.cbMergeImages);
this.Controls.Add(this.cbDiscardRemainingLayers);
this.Controls.Add(this.cbReplaceSubsequentLayers);
this.Controls.Add(this.lbResult);
@@ -330,5 +345,6 @@ private void InitializeComponent()
private System.Windows.Forms.PictureBox pbSelectedImage;
private System.Windows.Forms.CheckBox cbReplaceSubsequentLayers;
private System.Windows.Forms.CheckBox cbDiscardRemainingLayers;
+ private System.Windows.Forms.CheckBox cbMergeImages;
}
}
diff --git a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs
index 41b591ab..53090af3 100644
--- a/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs
+++ b/UVtools.GUI/Controls/Tools/CtrlToolLayerImport.cs
@@ -40,6 +40,7 @@ public override bool UpdateOperation()
Operation.ReplaceStartLayer = cbReplaceStartLayer.Checked;
Operation.ReplaceSubsequentLayers = cbReplaceSubsequentLayers.Checked;
Operation.DiscardRemainingLayers = cbDiscardRemainingLayers.Checked;
+ Operation.MergeImages = cbMergeImages.Checked;
return true;
}
@@ -79,10 +80,12 @@ private void EventClick(object sender, EventArgs e)
{
if (ReferenceEquals(sender, cbReplaceSubsequentLayers))
{
- cbDiscardRemainingLayers.Enabled = cbReplaceSubsequentLayers.Checked;
+ cbDiscardRemainingLayers.Enabled = cbDiscardRemainingLayers.Enabled = cbReplaceSubsequentLayers.Checked;
+ cbMergeImages.Enabled = cbReplaceSubsequentLayers.Checked;
if (!cbReplaceSubsequentLayers.Checked)
{
cbDiscardRemainingLayers.Checked = false;
+ cbMergeImages.Checked = false;
}
}
diff --git a/UVtools.GUI/FrmMain.cs b/UVtools.GUI/FrmMain.cs
index 12867f67..ac3fe8c2 100644
--- a/UVtools.GUI/FrmMain.cs
+++ b/UVtools.GUI/FrmMain.cs
@@ -2275,7 +2275,7 @@ void ShowLayer(bool direction)
/// Shows a layer number
///
/// Layer number
- void ShowLayer(uint layerNum)
+ unsafe void ShowLayer(uint layerNum)
{
if (SlicerFile is null) return;
@@ -2316,8 +2316,10 @@ void ShowLayer(uint layerNum)
CvInvoke.CvtColor(ActualLayerImage, ActualLayerImageBgr, ColorConversion.Gray2Bgr);
- var imageSpan = ActualLayerImage.GetPixelSpan();
- var imageBgrSpan = ActualLayerImageBgr.GetPixelSpan();
+ //var imageSpan = ActualLayerImage.GetPixelSpan();
+ //var imageBgrSpan = ActualLayerImageBgr.GetPixelSpan();
+ var imageSpan = ActualLayerImage.GetBytePointer();
+ var imageBgrSpan = ActualLayerImageBgr.GetBytePointer();
if (btnLayerImageLayerOutlineEdgeDetection.Checked)
@@ -2332,71 +2334,30 @@ void ShowLayer(uint layerNum)
{
if (layerNum > 0 && layerNum < SlicerFile.LayerCount - 1)
{
- using (var previousImage = SlicerFile[layerNum - 1].LayerMat)
- using (var nextImage = SlicerFile[layerNum + 1].LayerMat)
+ Mat previousImage = null;
+ Mat nextImage = null;
+
+ // Can improve performance on >4K images?
+ Parallel.Invoke(
+ () => { previousImage = SlicerFile[ActualLayer - 1].LayerMat; },
+ () => { nextImage = SlicerFile[ActualLayer + 1].LayerMat; });
+
+ /*using (var previousImage = SlicerFile[_actualLayer - 1].LayerMat)
+ using (var nextImage = SlicerFile[_actualLayer + 1].LayerMat)
+ {*/
+ //var previousSpan = previousImage.GetPixelSpan();
+ //var nextSpan = nextImage.GetPixelSpan();
+
+ var previousSpan = previousImage.GetBytePointer();
+ var nextSpan = nextImage.GetBytePointer();
+
+ int width = ActualLayerImage.Width;
+ int channels = ActualLayerImageBgr.NumberOfChannels;
+ Parallel.For(0, ActualLayerImageBgr.Height, y =>
{
- var previousSpan = previousImage.GetPixelSpan();
- var nextSpan = nextImage.GetPixelSpan();
-
- /*Parallel.For(0, imageSpan.Length, i =>
- {
- var currentByte = ActualLayerImage.GetSinglePixelSpan(i);
- var previousByte = previousImage.GetSinglePixelSpan(i);
- var nextByte = nextImage.GetSinglePixelSpan(i);
-
- if (currentByte[0] != 0) return;
- Color color = Color.Empty;
- if (previousByte[0] > 0 && nextByte[0] > 0)
- {
- color = Settings.Default.PreviousNextLayerColor;
- }
- else if (previousByte[0] > 0)
- {
- color = Settings.Default.PreviousLayerColor;
- }
- else if (nextByte[0] > 0)
- {
- color = Settings.Default.NextLayerColor;
- }
-
- if (color.IsEmpty) return;
- ActualLayerImageBgr.SetByte(i * 3, new[]{ color.B , color.G, color.R });
- });*/
-
- /*Parallel.For(0, ActualLayerImage.Height, y =>
- {
- var currentSpan = ActualLayerImage.GetPixelRowSpan(y);
- var currentRGBSpan = ActualLayerImageBgr.GetPixelRowSpan(y);
- var previousSpan = previousImage.GetPixelRowSpan(y);
- var nextSpan = nextImage.GetPixelRowSpan(y);
-
- for (int x = 0; x < currentSpan.Length; x++)
- {
- if (currentSpan[x] != 0) continue;
- Color color = Color.Empty;
- if (previousSpan[x] > 0 && nextSpan[x] > 0)
- {
- color = Settings.Default.PreviousNextLayerColor;
- }
- else if (previousSpan[x] > 0)
- {
- color = Settings.Default.PreviousLayerColor;
- }
- else if (nextSpan[x] > 0)
- {
- color = Settings.Default.NextLayerColor;
- }
-
- if (color.IsEmpty) continue;
- var bgrPixel = x * 3;
- currentRGBSpan[bgrPixel] = color.B; // B
- currentRGBSpan[++bgrPixel] = color.G; // G
- currentRGBSpan[++bgrPixel] = color.R; // R
- }
- });*/
-
- for (int pixel = 0; pixel < imageSpan.Length; pixel++)
+ for (int x = 0; x < width; x++)
{
+ int pixel = y * width + x;
if (imageSpan[pixel] != 0) continue;
Color color = Color.Empty;
if (previousSpan[pixel] > 0 && nextSpan[pixel] > 0)
@@ -2413,12 +2374,16 @@ void ShowLayer(uint layerNum)
}
if (color.IsEmpty) continue;
- var bgrPixel = pixel * 3;
+ var bgrPixel = pixel * channels;
imageBgrSpan[bgrPixel] = color.B; // B
imageBgrSpan[++bgrPixel] = color.G; // G
imageBgrSpan[++bgrPixel] = color.R; // R
+ //imageBgrSpan[++bgrPixel] = color.A; // A
}
- }
+ });
+
+ previousImage.Dispose();
+ nextImage.Dispose();
}
}
diff --git a/UVtools.GUI/Properties/AssemblyInfo.cs b/UVtools.GUI/Properties/AssemblyInfo.cs
index e011fe66..dd226f76 100644
--- a/UVtools.GUI/Properties/AssemblyInfo.cs
+++ b/UVtools.GUI/Properties/AssemblyInfo.cs
@@ -35,5 +35,5 @@
// 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.8.2.3")]
-[assembly: AssemblyFileVersion("0.8.2.3")]
+[assembly: AssemblyVersion("0.8.2.4")]
+[assembly: AssemblyFileVersion("0.8.2.4")]
diff --git a/UVtools.GUI/UVtools.GUI.csproj b/UVtools.GUI/UVtools.GUI.csproj
index b1269db5..4b2b1c56 100644
--- a/UVtools.GUI/UVtools.GUI.csproj
+++ b/UVtools.GUI/UVtools.GUI.csproj
@@ -41,6 +41,7 @@
prompt
4
false
+ true
AnyCPU
@@ -51,6 +52,7 @@
prompt
4
false
+ true
UVtools.ico
@@ -67,6 +69,7 @@
7.3
prompt
MinimumRecommendedRules.ruleset
+ true
bin\x64\Release\
@@ -77,6 +80,7 @@
7.3
prompt
MinimumRecommendedRules.ruleset
+ true
false
diff --git a/UVtools.WPF/MainWindow.axaml b/UVtools.WPF/MainWindow.axaml
index 76f60c14..b18591d4 100644
--- a/UVtools.WPF/MainWindow.axaml
+++ b/UVtools.WPF/MainWindow.axaml
@@ -108,35 +108,135 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+ SelectedIndex="{Binding TabSelectedIndex}">
+
Information
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -146,7 +246,7 @@
-
+
@@ -275,7 +375,6 @@
Name="Layer.Navigation.Slider"
Minimum="0"
Maximum="{Binding SliderMaximumValue}"
- Ticks="{Binding SliderMaximumValue}"
Value="{Binding ActualLayer}"
TickFrequency="1"
TickPlacement="Outside"
@@ -357,7 +456,7 @@
@@ -397,10 +496,11 @@