Export selection to image: added option to scale the output image to a higher resolution

This commit is contained in:
biwa 2020-11-21 17:44:10 +01:00
parent cf61c74e35
commit f2f0e1bae1
4 changed files with 93 additions and 22 deletions

View file

@ -674,7 +674,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
ImageExportSettingsForm form = new ImageExportSettingsForm(); ImageExportSettingsForm form = new ImageExportSettingsForm();
if (form.ShowDialog() == DialogResult.OK) if (form.ShowDialog() == DialogResult.OK)
{ {
ImageExportSettings settings = new ImageExportSettings(Path.GetDirectoryName(form.FilePath), Path.GetFileNameWithoutExtension(form.FilePath), Path.GetExtension(form.FilePath), form.Floor, form.Fullbright, form.Brightmap, form.Tiles, form.GetPixelFormat(), form.GetImageFormat()); ImageExportSettings settings = new ImageExportSettings(Path.GetDirectoryName(form.FilePath), Path.GetFileNameWithoutExtension(form.FilePath), Path.GetExtension(form.FilePath), form.Floor, form.Fullbright, form.Brightmap, form.Tiles, form.ImageScale, form.GetPixelFormat(), form.GetImageFormat());
ImageExporter exporter = new ImageExporter(sectors, settings); ImageExporter exporter = new ImageExporter(sectors, settings);
string text = "The following images will be created:\n\n" + string.Join("\n", exporter.GetImageNames()); string text = "The following images will be created:\n\n" + string.Join("\n", exporter.GetImageNames());

View file

@ -50,8 +50,9 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
public bool Tiles; public bool Tiles;
public PixelFormat PixelFormat; public PixelFormat PixelFormat;
public ImageFormat ImageFormat; public ImageFormat ImageFormat;
public float Scale;
public ImageExportSettings(string path, string name, string extension, bool floor, bool fullbright, bool brightmap, bool tiles, PixelFormat pformat, ImageFormat iformat) public ImageExportSettings(string path, string name, string extension, bool floor, bool fullbright, bool brightmap, bool tiles, float scale, PixelFormat pformat, ImageFormat iformat)
{ {
Path = path; Path = path;
Name = name; Name = name;
@ -62,6 +63,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
Fullbright = fullbright; Fullbright = fullbright;
PixelFormat = pformat; PixelFormat = pformat;
ImageFormat = iformat; ImageFormat = iformat;
Scale = scale;
} }
} }
@ -105,10 +107,10 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
GetSizeAndOffset(out size, out offset); GetSizeAndOffset(out size, out offset);
// Use the same image for the normal texture and the brightmap because of memory concerns // Use the same image for the normal texture and the brightmap because of memory concerns
using (Bitmap image = new Bitmap((int)size.x, (int)size.y, settings.PixelFormat)) using (Bitmap image = new Bitmap((int)(size.x * settings.Scale), (int)(size.y * settings.Scale), settings.PixelFormat))
{ {
// Normal texture image // Normal texture image
CreateImage(image, offset, false); CreateImage(image, offset, settings.Scale, false);
if (settings.Tiles) if (settings.Tiles)
SaveImageAsTiles(image); SaveImageAsTiles(image);
@ -118,7 +120,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
// The brightmap // The brightmap
if (settings.Brightmap) if (settings.Brightmap)
{ {
CreateImage(image, offset, true); CreateImage(image, offset, settings.Scale, true);
if (settings.Tiles) if (settings.Tiles)
SaveImageAsTiles(image, "_brightmap"); SaveImageAsTiles(image, "_brightmap");
@ -135,7 +137,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
/// <param name="offset">The offset of the selection in map space</param> /// <param name="offset">The offset of the selection in map space</param>
/// <param name="asbrightmap">True if the image should be a brightmap, false if normally textured</param> /// <param name="asbrightmap">True if the image should be a brightmap, false if normally textured</param>
/// <returns>The image to be exported</returns> /// <returns>The image to be exported</returns>
private void CreateImage(Bitmap texturebitmap, Vector2D offset, bool asbrightmap) private void CreateImage(Bitmap texturebitmap, Vector2D offset, float scale, bool asbrightmap)
{ {
Graphics gtexture = null; Graphics gtexture = null;
@ -161,9 +163,9 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
for (int i = 0; i < s.Triangles.Vertices.Count / 3; i++) for (int i = 0; i < s.Triangles.Vertices.Count / 3; i++)
{ {
// The GDI image has the 0/0 coordinate in the top left, so invert the y component // The GDI image has the 0/0 coordinate in the top left, so invert the y component
Vector2D v1 = s.Triangles.Vertices[i * 3] - offset; v1.y *= -1.0; Vector2D v1 = (s.Triangles.Vertices[i * 3] - offset) * scale; v1.y *= -1.0;
Vector2D v2 = s.Triangles.Vertices[i * 3 + 1] - offset; v2.y *= -1.0; Vector2D v2 = (s.Triangles.Vertices[i * 3 + 1] - offset) * scale; v2.y *= -1.0;
Vector2D v3 = s.Triangles.Vertices[i * 3 + 2] - offset; v3.y *= -1.0; Vector2D v3 = (s.Triangles.Vertices[i * 3 + 2] - offset) * scale; v3.y *= -1.0;
gpath.AddLine((float)v1.x, (float)v1.y, (float)v2.x, (float)v2.y); gpath.AddLine((float)v1.x, (float)v1.y, (float)v2.x, (float)v2.y);
gpath.AddLine((float)v2.x, (float)v2.y, (float)v3.x, (float)v3.y); gpath.AddLine((float)v2.x, (float)v2.y, (float)v3.x, (float)v3.y);
@ -188,11 +190,11 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
// but doesn't applie the color correction if we set UseColorCorrection to false first // but doesn't applie the color correction if we set UseColorCorrection to false first
ImageData imagedata = General.Map.Data.GetFlatImage(s.FloorTexture); ImageData imagedata = General.Map.Data.GetFlatImage(s.FloorTexture);
imagedata.UseColorCorrection = false; imagedata.UseColorCorrection = false;
brushtexture = imagedata.LocalGetBitmap(); brushtexture = new Bitmap(imagedata.LocalGetBitmap());
imagedata.UseColorCorrection = true; imagedata.UseColorCorrection = true;
textureoffset.x = s.Fields.GetValue("xpanningfloor", 0.0); textureoffset.x = s.Fields.GetValue("xpanningfloor", 0.0) * scale;
textureoffset.y = s.Fields.GetValue("ypanningfloor", 0.0); textureoffset.y = s.Fields.GetValue("ypanningfloor", 0.0) * scale;
// GZDoom uses bigger numbers for smaller scales (i.e. a scale of 2 will halve the size), so we need to change the scale // GZDoom uses bigger numbers for smaller scales (i.e. a scale of 2 will halve the size), so we need to change the scale
texturescale.x = 1.0 / s.Fields.GetValue("xscalefloor", 1.0); texturescale.x = 1.0 / s.Fields.GetValue("xscalefloor", 1.0);
@ -204,11 +206,11 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
// but doesn't applie the color correction if we set UseColorCorrection to false first // but doesn't applie the color correction if we set UseColorCorrection to false first
ImageData imagedata = General.Map.Data.GetFlatImage(s.CeilTexture); ImageData imagedata = General.Map.Data.GetFlatImage(s.CeilTexture);
imagedata.UseColorCorrection = false; imagedata.UseColorCorrection = false;
brushtexture = imagedata.LocalGetBitmap(); brushtexture = new Bitmap(imagedata.LocalGetBitmap());
imagedata.UseColorCorrection = true; imagedata.UseColorCorrection = true;
textureoffset.x = s.Fields.GetValue("xpanningceiling", 0.0); textureoffset.x = s.Fields.GetValue("xpanningceiling", 0.0) * scale;
textureoffset.y = s.Fields.GetValue("ypanningceiling", 0.0); textureoffset.y = s.Fields.GetValue("ypanningceiling", 0.0) * scale;
// GZDoom uses bigger numbers for smaller scales (i.e. a scale of 2 will halve the size), so we need to change the scale // GZDoom uses bigger numbers for smaller scales (i.e. a scale of 2 will halve the size), so we need to change the scale
texturescale.x = 1.0 / s.Fields.GetValue("xscaleceiling", 1.0); texturescale.x = 1.0 / s.Fields.GetValue("xscaleceiling", 1.0);
@ -218,13 +220,16 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
// Create the transformation matrix // Create the transformation matrix
Matrix matrix = new Matrix(); Matrix matrix = new Matrix();
matrix.Rotate(rotation); matrix.Rotate(rotation);
matrix.Translate((float)(-offset.x * rotationvector.x), (float)(offset.x * rotationvector.y)); // Left/right offset from the map origin matrix.Translate((float)(-offset.x*scale * rotationvector.x), (float)(offset.x*scale * rotationvector.y)); // Left/right offset from the map origin
matrix.Translate((float)(offset.y * rotationvector.y), (float)(offset.y * rotationvector.x)); // Up/down offset from the map origin matrix.Translate((float)(offset.y*scale * rotationvector.y), (float)(offset.y*scale * rotationvector.x)); // Up/down offset from the map origin
matrix.Translate(-(float)textureoffset.x, -(float)textureoffset.y); // Texture offset matrix.Translate(-(float)textureoffset.x, -(float)textureoffset.y); // Texture offset
matrix.Scale((float)texturescale.x, (float)texturescale.y); matrix.Scale((float)texturescale.x, (float)texturescale.y);
if (!settings.Fullbright) if (!settings.Fullbright)
AdjustBrightness(brushtexture, s.Brightness > 0 ? s.Brightness / 255.0f : 0.0f); AdjustBrightness(ref brushtexture, s.Brightness > 0 ? s.Brightness / 255.0f : 0.0f);
if (scale > 1.0f)
ResizeImage(ref brushtexture, brushtexture.Width * (int)scale, brushtexture.Height * (int)scale);
// Create the texture brush and apply the matrix // Create the texture brush and apply the matrix
TextureBrush tbrush = new TextureBrush(brushtexture); TextureBrush tbrush = new TextureBrush(brushtexture);
@ -367,7 +372,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
/// </summary> /// </summary>
/// <param name="image">The image to adjust</param> /// <param name="image">The image to adjust</param>
/// <param name="brightness">Brightness between 0.0f and 1.0f</param> /// <param name="brightness">Brightness between 0.0f and 1.0f</param>
private void AdjustBrightness(Image image, float brightness) private void AdjustBrightness(ref Bitmap image, float brightness)
{ {
// Make the ColorMatrix. // Make the ColorMatrix.
float b = brightness; float b = brightness;
@ -403,6 +408,40 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
image = bm; image = bm;
} }
/// <summary>
/// Resize the image to the specified width and height. Taken from https://stackoverflow.com/a/24199315 (with some modifications)
/// </summary>
/// <param name="image">The image to resize.</param>
/// <param name="width">The width to resize to.</param>
/// <param name="height">The height to resize to.</param>
/// <returns>The resized image.</returns>
private void ResizeImage(ref Bitmap image, int width, int height)
{
var destRect = new Rectangle(0, 0, width, height);
var destImage = new Bitmap(width, height);
destImage.SetResolution(image.HorizontalResolution, image.VerticalResolution);
using (var graphics = Graphics.FromImage(destImage))
{
graphics.CompositingMode = CompositingMode.SourceCopy;
graphics.CompositingQuality = CompositingQuality.HighQuality;
graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
graphics.SmoothingMode = SmoothingMode.HighQuality;
graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
using (var wrapMode = new ImageAttributes())
{
wrapMode.SetWrapMode(WrapMode.TileFlipXY);
graphics.DrawImage(image, destRect, 0, 0, image.Width, image.Height, GraphicsUnit.Pixel, wrapMode);
}
}
image.Dispose();
image = destImage;
}
#endregion #endregion
} }
} }

View file

@ -43,6 +43,8 @@
this.cbFullbright = new System.Windows.Forms.CheckBox(); this.cbFullbright = new System.Windows.Forms.CheckBox();
this.cbBrightmap = new System.Windows.Forms.CheckBox(); this.cbBrightmap = new System.Windows.Forms.CheckBox();
this.cbTiles = new System.Windows.Forms.CheckBox(); this.cbTiles = new System.Windows.Forms.CheckBox();
this.cbScale = new System.Windows.Forms.ComboBox();
this.label4 = new System.Windows.Forms.Label();
this.SuspendLayout(); this.SuspendLayout();
// //
// tbExportPath // tbExportPath
@ -194,6 +196,29 @@
this.cbTiles.Text = "Create 64x64 tiles"; this.cbTiles.Text = "Create 64x64 tiles";
this.cbTiles.UseVisualStyleBackColor = true; this.cbTiles.UseVisualStyleBackColor = true;
// //
// cbScale
//
this.cbScale.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this.cbScale.FormattingEnabled = true;
this.cbScale.Items.AddRange(new object[] {
"100%",
"200%",
"400%",
"800%"});
this.cbScale.Location = new System.Drawing.Point(102, 89);
this.cbScale.Name = "cbScale";
this.cbScale.Size = new System.Drawing.Size(71, 21);
this.cbScale.TabIndex = 17;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(12, 92);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(37, 13);
this.label4.TabIndex = 18;
this.label4.Text = "Scale:";
//
// ImageExportSettingsForm // ImageExportSettingsForm
// //
this.AcceptButton = this.export; this.AcceptButton = this.export;
@ -201,6 +226,8 @@
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancel; this.CancelButton = this.cancel;
this.ClientSize = new System.Drawing.Size(447, 188); this.ClientSize = new System.Drawing.Size(447, 188);
this.Controls.Add(this.label4);
this.Controls.Add(this.cbScale);
this.Controls.Add(this.cbTiles); this.Controls.Add(this.cbTiles);
this.Controls.Add(this.cbBrightmap); this.Controls.Add(this.cbBrightmap);
this.Controls.Add(this.cbFullbright); this.Controls.Add(this.cbFullbright);
@ -242,5 +269,7 @@
private System.Windows.Forms.CheckBox cbFullbright; private System.Windows.Forms.CheckBox cbFullbright;
private System.Windows.Forms.CheckBox cbBrightmap; private System.Windows.Forms.CheckBox cbBrightmap;
private System.Windows.Forms.CheckBox cbTiles; private System.Windows.Forms.CheckBox cbTiles;
private System.Windows.Forms.ComboBox cbScale;
private System.Windows.Forms.Label label4;
} }
} }

View file

@ -44,6 +44,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Interface
public bool Fullbright { get { return cbFullbright.Checked; } } public bool Fullbright { get { return cbFullbright.Checked; } }
public bool Brightmap { get { return cbBrightmap.Checked; } } public bool Brightmap { get { return cbBrightmap.Checked; } }
public bool Tiles { get { return cbTiles.Checked; } } public bool Tiles { get { return cbTiles.Checked; } }
public float ImageScale { get { return (float)Math.Pow(2, cbScale.SelectedIndex); } }
#endregion #endregion
@ -73,6 +74,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Interface
cbFullbright.Checked = General.Settings.ReadPluginSetting("imageexportfullbright", true); cbFullbright.Checked = General.Settings.ReadPluginSetting("imageexportfullbright", true);
cbBrightmap.Checked = General.Settings.ReadPluginSetting("imageexportbrightmap", false); cbBrightmap.Checked = General.Settings.ReadPluginSetting("imageexportbrightmap", false);
cbTiles.Checked = General.Settings.ReadPluginSetting("imageexporttiles", false); cbTiles.Checked = General.Settings.ReadPluginSetting("imageexporttiles", false);
cbScale.SelectedIndex = General.Settings.ReadPluginSetting("imageexportscale", 0);
} }
#endregion #endregion
@ -133,9 +135,10 @@ namespace CodeImp.DoomBuilder.BuilderModes.Interface
private void export_Click(object sender, EventArgs e) private void export_Click(object sender, EventArgs e)
{ {
General.Settings.WritePluginSetting("imageexportfullbright", cbFullbright.Checked); General.Settings.WritePluginSetting("imageexportfullbright", cbFullbright.Checked);
General.Settings.WritePluginSetting("imageexportbrightmap", cbBrightmap.Checked); General.Settings.WritePluginSetting("imageexportbrightmap", cbBrightmap.Checked);
General.Settings.WritePluginSetting("imageexporttiles", cbTiles.Checked); General.Settings.WritePluginSetting("imageexporttiles", cbTiles.Checked);
General.Settings.WritePluginSetting("imageexportscale", cbScale.SelectedIndex);
this.DialogResult = DialogResult.OK; this.DialogResult = DialogResult.OK;
this.Close(); this.Close();