UltimateZoneBuilder/Source/Plugins/BuilderModes/Interface/ImageExportSettingsForm.cs

367 lines
10 KiB
C#

#region ================== Copyright (c) 2020 Boris Iwanski
/*
* This program is free software: you can redistribute it and/or modify
*
* it under the terms of the GNU General Public License as published by
*
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
*
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.If not, see<http://www.gnu.org/licenses/>.
*/
#endregion
using System;
using System.Drawing.Imaging;
using System.IO;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using CodeImp.DoomBuilder.Map;
using CodeImp.DoomBuilder.BuilderModes.IO;
namespace CodeImp.DoomBuilder.BuilderModes.Interface
{
enum ImageExportResult
{
OK,
Canceled,
OutOfMemory,
ImageTooBig
}
public partial class ImageExportSettingsForm : Form
{
#region ================== Properties
public string FilePath { get { return tbExportPath.Text.Trim(); } }
public bool Floor { get { return rbFloor.Checked; } }
public bool Fullbright { get { return cbFullbright.Checked; } }
public bool ApplySectorColors { get { return cbApplySectorColors.Checked; } }
public bool Brightmap { get { return cbBrightmap.Checked; } }
public bool Tiles { get { return cbTiles.Checked; } }
public float ImageScale { get { return (float)Math.Pow(2, cbScale.SelectedIndex); } }
#endregion
#region ================== Delegates
private delegate void CallVoidMethodDeletage();
private delegate void CallStringMethodDeletage(string s);
private delegate void CallImageExportResultMethodDeletage(ImageExportResult ier);
#endregion
#region ================== Variables
Thread exportthread;
bool exporting;
bool cancelexport;
#endregion
#region ================== Constructor
public ImageExportSettingsForm()
{
InitializeComponent();
cbImageFormat.SelectedIndex = 0;
cbPixelFormat.SelectedIndex = 0;
exporting = false;
cancelexport = false;
string name = Path.GetFileNameWithoutExtension(General.Map.FileTitle) + "_" + General.Map.Options.LevelName + "_" + Path.GetFileNameWithoutExtension(Path.GetRandomFileName());
if (string.IsNullOrEmpty(General.Map.FilePathName))
{
saveFileDialog.FileName = name;
}
else
{
saveFileDialog.InitialDirectory = Path.GetDirectoryName(General.Map.FilePathName);
saveFileDialog.FileName = Path.GetDirectoryName(General.Map.FilePathName) + Path.DirectorySeparatorChar + name + ".png";
tbExportPath.Text = saveFileDialog.FileName;
}
cbFullbright.Checked = General.Settings.ReadPluginSetting("imageexportfullbright", true);
cbApplySectorColors.Checked = General.Settings.ReadPluginSetting("imageexportapplysectorcolors", true);
cbBrightmap.Checked = General.Settings.ReadPluginSetting("imageexportbrightmap", false);
cbTiles.Checked = General.Settings.ReadPluginSetting("imageexporttiles", false);
cbScale.SelectedIndex = General.Settings.ReadPluginSetting("imageexportscale", 0);
}
#endregion
#region ================== Methods
public ImageFormat GetImageFormat()
{
switch(cbImageFormat.SelectedIndex)
{
case 1: // JPG
return ImageFormat.Jpeg;
default: // PNG
return ImageFormat.Png;
}
}
public PixelFormat GetPixelFormat()
{
switch(cbPixelFormat.SelectedIndex)
{
case 1: // 24 bit
return PixelFormat.Format24bppRgb;
case 2: // 16 bit
return PixelFormat.Format16bppRgb555;
default: // 32 bit
return PixelFormat.Format32bppArgb;
}
}
/// <summary>
/// Starts exporting the image(s). Disables all controls and starts the thread that does the actual exporting.
/// </summary>
private void StartExport()
{
ICollection<Sector> sectors = General.Map.Map.SelectedSectorsCount == 0 ? General.Map.Map.Sectors : General.Map.Map.GetSelectedSectors(true);
exporting = true;
cancelexport = false;
progress.Maximum = 100; //sectors.Count * (Brightmap ? 2 : 1);
progress.Value = 0;
progress.Visible = true;
lbPhase.Text = "";
lbPhase.Visible = true;
foreach (Control c in Controls)
{
if (!(c is ProgressBar || c is Label))
c.Enabled = false;
}
export.Enabled = true;
export.Text = "Cancel";
ImageExportSettings settings = new ImageExportSettings(Path.GetDirectoryName(FilePath), Path.GetFileNameWithoutExtension(FilePath), Path.GetExtension(FilePath), Floor, Fullbright, ApplySectorColors, Brightmap, Tiles, ImageScale, GetPixelFormat(), GetImageFormat());
exportthread = new Thread(() => RunExport(settings));
exportthread.Name = "Image export";
exportthread.Priority = ThreadPriority.Normal;
exportthread.Start();
}
/// <summary>
/// Enables all controls. This has to be called when the export is finished (either successfully or unsuccessfully)
/// </summary>
/// <param name="ier">Image export result</param>
private void StopExport(ImageExportResult ier)
{
if (this.InvokeRequired)
{
CallImageExportResultMethodDeletage d = StopExport;
this.Invoke(d, ier);
}
else
{
progress.Visible = false;
lbPhase.Visible = false;
foreach (Control c in Controls)
{
if (!(c is ProgressBar || c is Label))
c.Enabled = true;
}
export.Text = "Export";
if (ier == ImageExportResult.OK)
MessageBox.Show("Export successful.", "Export to image", MessageBoxButtons.OK, MessageBoxIcon.Information);
else if(ier == ImageExportResult.Canceled)
MessageBox.Show("Export canceled.", "Export to image", MessageBoxButtons.OK, MessageBoxIcon.Information);
else if (ier == ImageExportResult.OutOfMemory)
MessageBox.Show("Exporting failed. There's likely not enough consecutive free memory to create the image. Try a lower color depth or file format", "Export failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
else if(ier == ImageExportResult.ImageTooBig)
MessageBox.Show("Exporting failed. The image is likely too big for the current settings. Try a lower color depth or file format", "Export failed", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
exporting = false;
}
private bool CheckCancelExport()
{
return cancelexport;
}
/// <summary>
/// Shows the current phase in textual form. Is called by the exporter
/// </summary>
/// <param name="text"></param>
private void ShowPhase(string text)
{
if (this.InvokeRequired)
{
CallStringMethodDeletage d = ShowPhase;
this.Invoke(d, text);
}
else
{
lbPhase.Text = text;
}
}
/// <summary>
/// Adds progress to the progress bar. Is called by the exporter
/// </summary>
private void AddProgress()
{
if (progress.InvokeRequired)
{
CallVoidMethodDeletage d = AddProgress;
try { progress.Invoke(d); }
catch (ThreadInterruptedException) { }
}
else
{
// Just winforms things to make the progress bar animation not lag behind
int value = progress.Value + 1;
progress.Value = value;
progress.Value = value - 1;
progress.Value = value;
}
}
/// <summary>
/// Runs the actual exporter
/// </summary>
/// <param name="settings">Export settings</param>
private void RunExport(ImageExportSettings settings)
{
ICollection<Sector> sectors = General.Map.Map.SelectedSectorsCount == 0 ? General.Map.Map.Sectors : General.Map.Map.GetSelectedSectors(true);
ImageExporter exporter = new ImageExporter(sectors, settings, AddProgress, ShowPhase, CheckCancelExport);
try
{
exporter.Export();
}
catch (ArgumentException) // Happens if there's not enough consecutive memory to create the file
{
StopExport(ImageExportResult.OutOfMemory);
return;
}
catch(ImageExportCanceledException)
{
StopExport(ImageExportResult.Canceled);
return;
}
catch(ImageExportImageTooBigException)
{
StopExport(ImageExportResult.ImageTooBig);
return;
}
StopExport(ImageExportResult.OK);
}
#endregion
#region ================== Events
private void browse_Click(object sender, EventArgs e)
{
if (saveFileDialog.ShowDialog() == DialogResult.OK)
{
tbExportPath.Text = saveFileDialog.FileName;
string extension = Path.GetExtension(saveFileDialog.FileName);
switch(extension)
{
case ".jpg":
cbImageFormat.SelectedIndex = 1;
break;
default:
cbImageFormat.SelectedIndex = 0;
break;
}
}
}
private void close_Click(object sender, EventArgs e)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
private void export_Click(object sender, EventArgs e)
{
if (exporting)
{
cancelexport = true;
export.Enabled = false;
}
else
{
General.Settings.WritePluginSetting("imageexportfullbright", cbFullbright.Checked);
General.Settings.WritePluginSetting("imageexportapplysectorcolors", cbApplySectorColors.Checked);
General.Settings.WritePluginSetting("imageexportbrightmap", cbBrightmap.Checked);
General.Settings.WritePluginSetting("imageexporttiles", cbTiles.Checked);
General.Settings.WritePluginSetting("imageexportscale", cbScale.SelectedIndex);
// Exporting works like this:
// In here StartExport() is called
// StartExport() disables all controls and creates a thread that runs RunExport() in the background; then the StartExport method ends
// RunExport() creates an instance of ImageExporter and starts the actual export
// When ImageExporter finishes its job it runs StopExport()
// StopExport() enables all controls again
StartExport();
}
}
private void cbImageFormat_SelectedIndexChanged(object sender, EventArgs e)
{
string newextension = "";
switch (cbImageFormat.SelectedIndex)
{
case 1: // JPG
newextension = ".jpg";
break;
default: // PNG
newextension = ".png";
break;
}
tbExportPath.Text = Path.ChangeExtension(tbExportPath.Text, newextension);
}
private void ImageExportSettingsForm_FormClosing(object sender, FormClosingEventArgs e)
{
// Do not allow closing the form while the export is running
if (exporting)
e.Cancel = true;
}
#endregion
}
}