#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. */ #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; } } /// /// Starts exporting the image(s). Disables all controls and starts the thread that does the actual exporting. /// private void StartExport() { ICollection 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(); } /// /// Enables all controls. This has to be called when the export is finished (either successfully or unsuccessfully) /// /// Image export result 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; } /// /// Shows the current phase in textual form. Is called by the exporter /// /// private void ShowPhase(string text) { if (this.InvokeRequired) { CallStringMethodDeletage d = ShowPhase; this.Invoke(d, text); } else { lbPhase.Text = text; } } /// /// Adds progress to the progress bar. Is called by the exporter /// 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; } } /// /// Runs the actual exporter /// /// Export settings private void RunExport(ImageExportSettings settings) { ICollection 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 } }