diff --git a/Source/Plugins/BuilderModes/General/BuilderPlug.cs b/Source/Plugins/BuilderModes/General/BuilderPlug.cs
index f8e81a7..94131b1 100644
--- a/Source/Plugins/BuilderModes/General/BuilderPlug.cs
+++ b/Source/Plugins/BuilderModes/General/BuilderPlug.cs
@@ -892,7 +892,7 @@ namespace CodeImp.DoomBuilder.BuilderModes
ImageExportSettingsForm form = new ImageExportSettingsForm();
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);
string text = "The following images will be created:\n\n" + string.Join("\n", exporter.GetImageNames().ToArray());
diff --git a/Source/Plugins/BuilderModes/IO/ImageExporter.cs b/Source/Plugins/BuilderModes/IO/ImageExporter.cs
index a2c1ecc..9906a1f 100644
--- a/Source/Plugins/BuilderModes/IO/ImageExporter.cs
+++ b/Source/Plugins/BuilderModes/IO/ImageExporter.cs
@@ -29,6 +29,7 @@ using System.Drawing.Drawing2D;
using System.IO;
using System.Linq;
using System.Text;
+using CodeImp.DoomBuilder.Data;
using CodeImp.DoomBuilder.Geometry;
using CodeImp.DoomBuilder.Map;
using System.Diagnostics;
@@ -48,8 +49,9 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
public bool Tiles;
public PixelFormat PixelFormat;
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;
Name = name;
@@ -60,6 +62,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
Fullbright = fullbright;
PixelFormat = pformat;
ImageFormat = iformat;
+ Scale = scale;
}
}
@@ -97,17 +100,47 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
///
public void Export()
{
- Bitmap texturebitmap = null;
- Bitmap brightmapbitmap = null;
- Graphics gbrightmap = null;
- Graphics gtexture = null;
- Vector2D offset;
Vector2D size;
+ Vector2D offset;
GetSizeAndOffset(out size, out offset);
- // Normal texture
- texturebitmap = new Bitmap((int)size.x, (int)size.y, settings.PixelFormat);
+ // Use the same image for the normal texture and the brightmap because of memory concerns
+ using (Bitmap image = new Bitmap((int)(size.x * settings.Scale), (int)(size.y * settings.Scale), settings.PixelFormat))
+ {
+ // Normal texture image
+ CreateImage(image, offset, settings.Scale, false);
+
+ if (settings.Tiles)
+ SaveImageAsTiles(image);
+ else
+ image.Save(Path.Combine(settings.Path, settings.Name) + settings.Extension, settings.ImageFormat);
+
+ // The brightmap
+ if (settings.Brightmap)
+ {
+ CreateImage(image, offset, settings.Scale, true);
+
+ if (settings.Tiles)
+ SaveImageAsTiles(image, "_brightmap");
+ else
+ image.Save(Path.Combine(settings.Path, settings.Name) + "_brightmap" + settings.Extension, settings.ImageFormat);
+ }
+ }
+ }
+
+ ///
+ /// Create the image ready to be exported
+ ///
+ /// The image the graphics will be drawn to
+ /// The offset of the selection in map space
+ /// True if the image should be a brightmap, false if normally textured
+ /// The image to be exported
+ private void CreateImage(Bitmap texturebitmap, Vector2D offset, float scale, bool asbrightmap)
+ {
+ Graphics gtexture = null;
+
+ // The texture
gtexture = Graphics.FromImage(texturebitmap);
gtexture.Clear(Color.Black); // If we don't clear to black we'll see seams where the sectors touch, due to the AA
gtexture.InterpolationMode = InterpolationMode.HighQualityBilinear;
@@ -115,21 +148,10 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
gtexture.PixelOffsetMode = PixelOffsetMode.HighQuality;
gtexture.SmoothingMode = SmoothingMode.AntiAlias; // Without AA the sector edges will be quite rough
- // Brightmap
- if (settings.Brightmap)
- {
- brightmapbitmap = new Bitmap((int)size.x, (int)size.y, settings.PixelFormat);
- gbrightmap = Graphics.FromImage(brightmapbitmap);
- gbrightmap.Clear(Color.Black); // If we don't clear to black we'll see seams where the sectors touch, due to the AA
- gbrightmap.InterpolationMode = InterpolationMode.HighQualityBilinear;
- gbrightmap.CompositingQuality = CompositingQuality.HighQuality;
- gbrightmap.PixelOffsetMode = PixelOffsetMode.HighQuality;
- gbrightmap.SmoothingMode = SmoothingMode.AntiAlias; // Without AA the sector edges will be quite rough
- }
+ GraphicsPath gpath = new GraphicsPath();
foreach (Sector s in sectors)
{
- GraphicsPath p = new GraphicsPath();
float rotation = (float)s.Fields.GetValue("rotationfloor", 0.0);
// If a sector is rotated any offset is on the rotated axes. But we need to offset by
@@ -140,66 +162,94 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
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
- Vector2D v1 = s.Triangles.Vertices[i * 3] - offset; v1.y *= -1.0f;
- Vector2D v2 = s.Triangles.Vertices[i * 3 + 1] - offset; v2.y *= -1.0f;
- Vector2D v3 = s.Triangles.Vertices[i * 3 + 2] - offset; v3.y *= -1.0f;
-
- p.AddLine((float)v1.x, (float)v1.y, (float)v2.x, (float)v2.y);
- p.AddLine((float)v2.x, (float)v2.y, (float)v3.x, (float)v3.y);
- p.CloseFigure();
+ Vector2D v1 = (s.Triangles.Vertices[i * 3] - offset) * scale; v1.y *= -1.0f;
+ Vector2D v2 = (s.Triangles.Vertices[i * 3 + 1] - offset) * scale; v2.y *= -1.0f;
+ Vector2D v3 = (s.Triangles.Vertices[i * 3 + 2] - offset) * scale; v3.y *= -1.0f;
+
+ 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.CloseFigure();
}
- Bitmap brushtexture;
-
- if (settings.Floor)
- brushtexture = General.Map.Data.GetFlatImage(s.FloorTexture).GetBitmap();
- else
- brushtexture = General.Map.Data.GetFlatImage(s.CeilTexture).GetBitmap();
-
- if (!settings.Fullbright)
- brushtexture = AdjustBrightness(brushtexture, s.Brightness > 0 ? s.Brightness / 255.0f : 0.0f);
-
- Vector2D textureoffset = new Vector2D();
- textureoffset.x = s.Fields.GetValue("xpanningfloor", 0.0f);
- textureoffset.y = s.Fields.GetValue("ypanningfloor", 0.0f);
-
- // Create the transformation matrix
- Matrix matrix = new Matrix();
- 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.y * rotationvector.y), (float)(offset.y * rotationvector.x)); // Up/down offset from the map origin
- matrix.Translate(-(float)textureoffset.x, -(float)textureoffset.y); // Texture offset
-
- // Create the texture brush and apply the matrix
- TextureBrush tbrush = new TextureBrush(brushtexture);
- tbrush.Transform = matrix;
-
- // Draw the islands of the sector
- gtexture.FillPath(tbrush, p);
-
- // Create the brightmap based on the sector brightness
- if (settings.Brightmap)
+ if (asbrightmap)
{
- SolidBrush sbrush = new SolidBrush(Color.FromArgb(255, s.Brightness, s.Brightness, s.Brightness));
- gbrightmap.FillPath(sbrush, p);
+ // Create the brightmap based on the sector brightness
+ using (SolidBrush sbrush = new SolidBrush(Color.FromArgb(255, s.Brightness, s.Brightness, s.Brightness)))
+ gtexture.FillPath(sbrush, gpath);
}
+ else
+ {
+ Bitmap brushtexture;
+ Vector2D textureoffset = new Vector2D();
+ Vector2D texturescale = new Vector2D();
+
+ if (settings.Floor)
+ {
+ // The image might have a color correction applied, but we need it without. So we use LocalGetBitmap, because it reloads the image,
+ // but doesn't applie the color correction if we set UseColorCorrection to false first
+ ImageData imagedata = General.Map.Data.GetFlatImage(s.FloorTexture);
+ imagedata.UseColorCorrection = false;
+ brushtexture = new Bitmap(imagedata.GetBitmap());
+ imagedata.UseColorCorrection = true;
+
+ textureoffset.x = s.Fields.GetValue("xpanningfloor", 0.0f) * scale;
+ textureoffset.y = s.Fields.GetValue("ypanningfloor", 0.0f) * 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.0f / s.Fields.GetValue("xscalefloor", 1.0f);
+ texturescale.y = 1.0f / s.Fields.GetValue("yscalefloor", 1.0f);
+ }
+ else
+ {
+ // The image might have a color correction applied, but we need it without. So we use LocalGetBitmap, because it reloads the image,
+ // but doesn't applie the color correction if we set UseColorCorrection to false first
+ ImageData imagedata = General.Map.Data.GetFlatImage(s.CeilTexture);
+ imagedata.UseColorCorrection = false;
+ brushtexture = new Bitmap(imagedata.GetBitmap());
+ imagedata.UseColorCorrection = true;
+
+ textureoffset.x = s.Fields.GetValue("xpanningceiling", 0.0f) * scale;
+ textureoffset.y = s.Fields.GetValue("ypanningceiling", 0.0f) * 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.0f / s.Fields.GetValue("xscaleceiling", 1.0f);
+ texturescale.y = 1.0f / s.Fields.GetValue("yscaleceiling", 1.0f);
+ }
+
+ // Create the transformation matrix
+ Matrix matrix = new Matrix();
+ matrix.Rotate(rotation);
+ 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*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.Scale((float)texturescale.x, (float)texturescale.y);
+
+ if (!settings.Fullbright)
+ 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
+ TextureBrush tbrush = new TextureBrush(brushtexture);
+ tbrush.Transform = matrix;
+
+ // Draw the islands of the sector
+ gtexture.FillPath(tbrush, gpath);
+
+ // Dispose unneeded objects
+ brushtexture.Dispose();
+ tbrush.Dispose();
+ matrix.Dispose();
+ }
+
+ // Reset the graphics path
+ gpath.Reset();
}
- // Finally save the image(s)
- if (settings.Tiles)
- {
- SaveImageAsTiles(texturebitmap);
-
- if (settings.Brightmap)
- SaveImageAsTiles(brightmapbitmap, "_brightmap");
- }
- else
- {
- texturebitmap.Save(Path.Combine(settings.Path, settings.Name) + settings.Extension, settings.ImageFormat);
-
- if (settings.Brightmap)
- brightmapbitmap.Save(Path.Combine(settings.Path, settings.Name) + "_brightmap" + settings.Extension, settings.ImageFormat);
- }
+ // Dispose unneeded objects
+ gpath.Dispose();
+ gtexture.Dispose();
}
///
@@ -228,12 +278,14 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
height = image.Size.Height - (y * TILE_SIZE);
- Bitmap bitmap = new Bitmap(TILE_SIZE, TILE_SIZE);
- Graphics g = Graphics.FromImage(bitmap);
- g.Clear(Color.Black);
- g.DrawImage(image, new Rectangle(0, 0, width, height), new Rectangle(x*TILE_SIZE, y*TILE_SIZE, width, height), GraphicsUnit.Pixel);
+ using (Bitmap bitmap = new Bitmap(TILE_SIZE, TILE_SIZE))
+ using (Graphics g = Graphics.FromImage(bitmap))
+ {
+ g.Clear(Color.Black);
+ g.DrawImage(image, new Rectangle(0, 0, width, height), new Rectangle(x * TILE_SIZE, y * TILE_SIZE, width, height), GraphicsUnit.Pixel);
- bitmap.Save(string.Format("{0}{1}{2}{3}", Path.Combine(settings.Path, settings.Name), suffix, imagenum, settings.Extension));
+ bitmap.Save(string.Format("{0}{1}{2}{3}", Path.Combine(settings.Path, settings.Name), suffix, imagenum, settings.Extension));
+ }
imagenum++;
}
@@ -317,10 +369,9 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
///
/// Adjusts the brightness of an image. Code by Rod Stephens http://csharphelper.com/blog/2014/10/use-an-imageattributes-object-to-adjust-an-images-brightness-in-c/
///
- /// Base image
+ /// The image to adjust
/// Brightness between 0.0f and 1.0f
- /// The new image with changed brightness
- private Bitmap AdjustBrightness(Image image, float brightness)
+ private void AdjustBrightness(ref Bitmap image, float brightness)
{
// Make the ColorMatrix.
float b = brightness;
@@ -349,8 +400,45 @@ namespace CodeImp.DoomBuilder.BuilderModes.IO
gr.DrawImage(image, points, rect, GraphicsUnit.Pixel, attributes);
}
- // Return the result.
- return bm;
+ // Dispose the original...
+ image.Dispose();
+
+ // ... and set it as the adjusted image
+ image = bm;
+ }
+
+ ///
+ /// Resize the image to the specified width and height. Taken from https://stackoverflow.com/a/24199315 (with some modifications)
+ ///
+ /// The image to resize.
+ /// The width to resize to.
+ /// The height to resize to.
+ /// The resized image.
+ 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
diff --git a/Source/Plugins/BuilderModes/Interface/ImageExportSettingsForm.Designer.cs b/Source/Plugins/BuilderModes/Interface/ImageExportSettingsForm.Designer.cs
index 88093ca..b23fde0 100644
--- a/Source/Plugins/BuilderModes/Interface/ImageExportSettingsForm.Designer.cs
+++ b/Source/Plugins/BuilderModes/Interface/ImageExportSettingsForm.Designer.cs
@@ -43,6 +43,8 @@
this.cbFullbright = new System.Windows.Forms.CheckBox();
this.cbBrightmap = 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();
//
// tbExportPath
@@ -194,6 +196,29 @@
this.cbTiles.Text = "Create 64x64 tiles";
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
//
this.AcceptButton = this.export;
@@ -201,6 +226,8 @@
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancel;
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.cbBrightmap);
this.Controls.Add(this.cbFullbright);
@@ -242,5 +269,7 @@
private System.Windows.Forms.CheckBox cbFullbright;
private System.Windows.Forms.CheckBox cbBrightmap;
private System.Windows.Forms.CheckBox cbTiles;
+ private System.Windows.Forms.ComboBox cbScale;
+ private System.Windows.Forms.Label label4;
}
}
\ No newline at end of file
diff --git a/Source/Plugins/BuilderModes/Interface/ImageExportSettingsForm.cs b/Source/Plugins/BuilderModes/Interface/ImageExportSettingsForm.cs
index eee7653..1a37d75 100644
--- a/Source/Plugins/BuilderModes/Interface/ImageExportSettingsForm.cs
+++ b/Source/Plugins/BuilderModes/Interface/ImageExportSettingsForm.cs
@@ -43,6 +43,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Interface
public bool Fullbright { get { return cbFullbright.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
@@ -72,6 +73,7 @@ namespace CodeImp.DoomBuilder.BuilderModes.Interface
cbFullbright.Checked = General.Settings.ReadPluginSetting("imageexportfullbright", true);
cbBrightmap.Checked = General.Settings.ReadPluginSetting("imageexportbrightmap", false);
cbTiles.Checked = General.Settings.ReadPluginSetting("imageexporttiles", false);
+ cbScale.SelectedIndex = General.Settings.ReadPluginSetting("imageexportscale", 0);
}
#endregion
@@ -132,9 +134,10 @@ namespace CodeImp.DoomBuilder.BuilderModes.Interface
private void export_Click(object sender, EventArgs e)
{
- General.Settings.WritePluginSetting("imageexportfullbright", cbFullbright.Checked);
- General.Settings.WritePluginSetting("imageexportbrightmap", cbBrightmap.Checked);
- General.Settings.WritePluginSetting("imageexporttiles", cbTiles.Checked);
+ General.Settings.WritePluginSetting("imageexportfullbright", cbFullbright.Checked);
+ General.Settings.WritePluginSetting("imageexportbrightmap", cbBrightmap.Checked);
+ General.Settings.WritePluginSetting("imageexporttiles", cbTiles.Checked);
+ General.Settings.WritePluginSetting("imageexportscale", cbScale.SelectedIndex);
this.DialogResult = DialogResult.OK;
this.Close();