/*
* 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 Emgu.CV;
using Emgu.CV.CvEnum;
using System;
using System.ComponentModel;
using System.IO;
using System.Threading.Tasks;
using UVtools.Core.Extensions;
using UVtools.Core.FileFormats;
namespace UVtools.Core.Operations;
public sealed class OperationLayerExportImage : Operation
{
#region Enums
public enum LayerExportImageTypes : byte
{
[Description("PNG: Portable Network Graphics")]
PNG,
[Description("JPG: Joint Photographic Experts Group")]
JPG,
[Description("JPEG: Joint Photographic Experts Group")]
JPEG,
[Description("JP2: Joint Photographic Experts Group (JPEG 2000)")]
JP2,
[Description("TIF: Tag Image File Format")]
TIF,
[Description("TIFF: Tag Image File Format")]
TIFF,
[Description("BMP: Bitmap")]
BMP,
[Description("PBM: Portable Bitmap")]
PBM,
[Description("PGM: Portable Greymap")]
PGM,
//[Description("PPM: Portable Pixmap")]
//PPM,
[Description("SR: Sun raster")]
SR,
[Description("RAS: Sun raster")]
RAS,
[Description("SVG: Scalable Vector Graphics")]
SVG
}
#endregion
#region Members
private string _outputFolder = null!;
private string _filename = "layer";
private LayerExportImageTypes _imageType = LayerExportImageTypes.PNG;
private RotateDirection _rotateDirection = RotateDirection.None;
private FlipDirection _flipDirection = FlipDirection.None;
private bool _padLayerIndex = true;
private bool _cropByRoi = true;
#endregion
#region Overrides
public override bool CanHaveProfiles => false;
public override string IconClass => "fa-solid fa-file-image";
public override string Title => "Export layers to images";
public override string Description =>
"Export a layer range to images.";
public override string ConfirmationText =>
$"export {_imageType} images from layers {LayerIndexStart} through {LayerIndexEnd}?";
public override string ProgressTitle =>
$"Exporting {_imageType} images from layers {LayerIndexStart} through {LayerIndexEnd}";
public override string ProgressAction => $"Exported {_imageType} images";
/*public override string ValidateInternally()
{
var sb = new StringBuilder();
if (LayerRangeCount < 2)
{
sb.AppendLine("To generate a heat map at least two layers are required.");
}
return sb.ToString();
}*/
public override string ToString()
{
var result = $"[Crop by ROI: {_cropByRoi}] [Pad index: {_padLayerIndex}]" +
LayerRangeString;
if (!string.IsNullOrEmpty(ProfileName)) result = $"{ProfileName}: {result}";
return result;
}
#endregion
#region Properties
public string OutputFolder
{
get => _outputFolder;
set => RaiseAndSetIfChanged(ref _outputFolder, value);
}
public string Filename
{
get => _filename;
set => RaiseAndSetIfChanged(ref _filename, value);
}
public LayerExportImageTypes ImageType
{
get => _imageType;
set => RaiseAndSetIfChanged(ref _imageType, value);
}
public RotateDirection RotateDirection
{
get => _rotateDirection;
set => RaiseAndSetIfChanged(ref _rotateDirection, value);
}
public FlipDirection FlipDirection
{
get => _flipDirection;
set => RaiseAndSetIfChanged(ref _flipDirection, value);
}
public bool PadLayerIndex
{
get => _padLayerIndex;
set => RaiseAndSetIfChanged(ref _padLayerIndex, value);
}
public bool CropByROI
{
get => _cropByRoi;
set => RaiseAndSetIfChanged(ref _cropByRoi, value);
}
#endregion
#region Constructor
public OperationLayerExportImage()
{ }
public OperationLayerExportImage(FileFormat slicerFile) : base(slicerFile)
{
_flipDirection = SlicerFile.DisplayMirror;
}
public override void InitWithSlicerFile()
{
_outputFolder = Path.Combine(Path.GetDirectoryName(SlicerFile.FileFullPath) ?? string.Empty, FileFormat.GetFileNameStripExtensions(SlicerFile.FileFullPath) ?? string.Empty);
}
#endregion
#region Methods
protected override bool ExecuteInternally(OperationProgress progress)
{
if (!Directory.Exists(_outputFolder))
{
Directory.CreateDirectory(_outputFolder);
}
var slicedFileNameNoExt = SlicerFile.FilenameNoExt;
Parallel.For(LayerIndexStart, LayerIndexEnd+1, CoreSettings.GetParallelOptions(progress), layerIndex =>
{
progress.PauseIfRequested();
using var mat = SlicerFile[layerIndex].LayerMat;
var matRoi = mat;
if (_cropByRoi && HaveROI)
{
matRoi = GetRoiOrDefault(mat);
}
if (_flipDirection != FlipDirection.None)
{
CvInvoke.Flip(matRoi, matRoi, (FlipType)_flipDirection);
}
if (_rotateDirection != RotateDirection.None)
{
CvInvoke.Rotate(matRoi, matRoi, (RotateFlags)_rotateDirection);
}
var filename = SlicerFile[layerIndex].FormatFileName(_filename, _padLayerIndex ? SlicerFile.LayerDigits : byte.MinValue, IndexStartNumber.Zero, string.Empty);
var fileFullPath = Path.Combine(_outputFolder, $"{filename}.{_imageType.ToString().ToLower()}");
if (_imageType != LayerExportImageTypes.SVG)
{
matRoi.Save(fileFullPath);
}
else
{
// SVG
var paths = matRoi.GetSvgPath(ChainApproxMethod.ChainApproxTc89Kcos);
using TextWriter tw = new StreamWriter(fileFullPath);
tw.WriteLine("");
tw.WriteLine("");
}
progress.LockAndIncrement();
});
return !progress.Token.IsCancellationRequested;
}
#endregion
#region Equality
private bool Equals(OperationLayerExportImage other)
{
return _outputFolder == other._outputFolder && _filename == other._filename && _imageType == other._imageType && _rotateDirection == other._rotateDirection && _flipDirection == other._flipDirection && _padLayerIndex == other._padLayerIndex && _cropByRoi == other._cropByRoi;
}
public override bool Equals(object? obj)
{
return ReferenceEquals(this, obj) || obj is OperationLayerExportImage other && Equals(other);
}
public override int GetHashCode()
{
return HashCode.Combine(_outputFolder, _filename, (int) _imageType, (int) _rotateDirection, (int) _flipDirection, _padLayerIndex, _cropByRoi);
}
#endregion
}