misc performance improvements

Preallocate the terrain-building objects and reuse

changed:
- grid alternates with a slightly-darker to help indicate the larger 2x2 tile grid.
- put plaza & buildings behind the gridlines
This commit is contained in:
Kurt 2020-05-02 23:02:37 -07:00
parent 5b5d1b2bce
commit 928e699168
12 changed files with 165 additions and 120 deletions

View File

@ -2,26 +2,34 @@
namespace NHSE.Core
{
public class MapManager
public class MapManager : MapTerrainStructure
{
public readonly FieldItemManager Items;
public int MapLayer { get; set; } // 0 or 1
public MapManager(MainSave sav) : base(sav)
{
Items = new FieldItemManager(sav.GetFieldItems());
}
public FieldItemLayer CurrentLayer => MapLayer == 0 ? Items.Layer1 : Items.Layer2;
}
public class MapTerrainStructure
{
public readonly TerrainManager Terrain;
public readonly IReadOnlyList<Building> Buildings;
public uint PlazaX { get; set; }
public uint PlazaY { get; set; }
public int MapLayer { get; set; } // 0 or 1
public MapManager(MainSave sav)
public MapTerrainStructure(MainSave sav)
{
Items = new FieldItemManager(sav.GetFieldItems());
Terrain = new TerrainManager(sav.GetTerrainTiles());
Buildings = sav.Buildings;
PlazaX = sav.PlazaX;
PlazaY = sav.PlazaY;
}
public FieldItemLayer CurrentLayer => MapLayer == 0 ? Items.Layer1 : Items.Layer2;
}
}

View File

@ -5,10 +5,11 @@ namespace NHSE.Core
public class MapView
{
private const int ViewInterval = 2;
protected readonly MapManager Map;
public readonly MapManager Map;
public int MapScale { get; set; } = 1;
public int AcreScale { get; set; } = 16;
public int MapScale { get; } = 1;
public int AcreScale { get; } = 16;
public int TerrainScale => AcreScale * 2;
// Top Left Anchor Coordinates
public int X { get; set; }

View File

@ -5,7 +5,7 @@ namespace NHSE.Sprites
{
public static class FieldItemSpriteDrawer
{
public static Bitmap GetBitmapLayer(FieldItemLayer layer)
public static Bitmap GetBitmapItemLayer(FieldItemLayer layer)
{
var items = layer.Tiles;
var height = layer.MapHeight;
@ -31,17 +31,6 @@ private static void LoadBitmapLayer(FieldItem[] items, int[] bmpData, int width,
}
}
private static int[] GetBitmapLayerAcre(FieldItemLayer layer, int x0, int y0, out int width, out int height)
{
height = layer.GridWidth;
width = layer.GridWidth;
var bmpData = new int[width * height];
LoadPixelsFromLayer(layer, x0, y0, width, bmpData);
return bmpData;
}
private static void LoadPixelsFromLayer(FieldItemLayer layer, int x0, int y0, int width, int[] bmpData)
{
var stride = layer.GridWidth;
@ -60,7 +49,7 @@ private static void LoadPixelsFromLayer(FieldItemLayer layer, int x0, int y0, in
}
// non-allocation image generator
public static Bitmap GetBitmapLayerAcre(FieldItemLayer layer, int x0, int y0, int scale, int[] acre1, int[] acreScale, Bitmap dest, int transparency = -1)
public static Bitmap GetBitmapItemLayerAcre(FieldItemLayer layer, int x0, int y0, int scale, int[] acre1, int[] acreScale, Bitmap dest, int transparency = -1)
{
var w = layer.GridWidth;
var h = layer.GridHeight;
@ -76,29 +65,14 @@ public static Bitmap GetBitmapLayerAcre(FieldItemLayer layer, int x0, int y0, in
DrawDirectionals(acreScale, layer, w, x0, y0, scale);
// Slap on a grid
DrawGrid(acreScale, w, h, scale);
const int gridlineColor = 0; // let the underlying image grid show instead
DrawGrid(acreScale, w, h, scale, gridlineColor);
// Return final data
ImageUtil.SetBitmapData(dest, acreScale);
return dest;
}
// unused -- allocates!
public static Bitmap GetBitmapLayerAcre(FieldItemLayer layer, int x0, int y0, int scale)
{
var map = GetBitmapLayerAcre(layer, x0, y0, out int mh, out int mw);
var data = ImageUtil.ScalePixelImage(map, scale, mw, mh, out var w, out var h);
// draw symbols over special items now?
DrawDirectionals(data, layer, w, x0, y0, scale);
// Slap on a grid
DrawGrid(data, w, h, scale);
// Return final data
return ImageUtil.GetBitmap(data, w, h);
}
private static void DrawDirectionals(int[] data, FieldItemLayer layer, int w, int x0, int y0, int scale)
{
for (int x = x0; x < x0 + layer.GridWidth; x++)
@ -152,10 +126,8 @@ private static void DrawDirectional(int[] data, FieldItem tile, int x0, int y0,
}
}
private static void DrawGrid(int[] data, int w, int h, int scale)
public static void DrawGrid(int[] data, int w, int h, int scale, int gridlineColor)
{
const int grid = -0x777778; // 0xFF888888u
// Horizontal Lines
for (int y = scale; y < h; y += scale)
{
@ -163,7 +135,7 @@ private static void DrawGrid(int[] data, int w, int h, int scale)
for (int x = 0; x < w; x++)
{
var index = baseIndex + x;
data[index] = grid;
data[index] = gridlineColor;
}
}
@ -174,21 +146,12 @@ private static void DrawGrid(int[] data, int w, int h, int scale)
for (int x = scale; x < w; x += scale)
{
var index = baseIndex + x;
data[index] = grid;
data[index] = gridlineColor;
}
}
}
public static Bitmap GetBitmapLayer(FieldItemLayer layer, int x, int y, int scale = 1)
{
var map = GetBitmapLayer(layer);
if (scale > 1)
map = ImageUtil.ResizeImage(map, map.Width, map.Height);
return DrawViewReticle(map, layer, x, y, scale);
}
public static Bitmap GetBitmapLayer(FieldItemLayer layer, int x, int y, int[] data, Bitmap dest, int transparency = -1)
public static Bitmap GetBitmapItemLayer(FieldItemLayer layer, int x, int y, int[] data, Bitmap dest, int transparency = -1)
{
LoadBitmapLayer(layer.Tiles, data, layer.MapWidth, layer.MapHeight);
if (transparency >> 24 != 0xFF)

View File

@ -16,33 +16,33 @@ public static class TerrainSprite
private const int PlazaWidth = 6 * 2;
private const int PlazaHeight = 5 * 2;
public static Bitmap CreateMap(TerrainManager mgr)
public static void CreateMap(TerrainManager mgr, int[] pixels)
{
var bmp = new Bitmap(mgr.MapWidth, mgr.MapHeight);
for (int x = 0; x < mgr.MapWidth; x++)
int i = 0;
for (int y = 0; y < mgr.MapHeight; y++)
{
for (int y = 0; y < mgr.MapHeight; y++)
for (int x = 0; x < mgr.MapWidth; x++, i++)
{
var tile = mgr.GetTile(x, y);
var color = TerrainTileColor.GetTileColor(tile);
bmp.SetPixel(x, y, color);
pixels[i] = color.ToArgb();
}
}
return bmp;
}
public static Bitmap CreateMap(TerrainManager mgr, int scale, int x, int y)
public static Bitmap CreateMap(TerrainManager mgr, int scale, int x, int y, int[] scale1, int[] scaleX, Bitmap map)
{
var img = CreateMap(mgr);
var map = ImageUtil.ResizeImage(img, img.Width * scale, img.Height * scale);
CreateMap(mgr, scale1);
ImageUtil.ScalePixelImage(scale1, scaleX, map.Width, map.Height, scale);
ImageUtil.SetBitmapData(map, scaleX);
return DrawReticle(map, mgr, x, y, scale);
}
public static Bitmap CreateMap(TerrainManager mgr, int scale, int acreIndex = -1)
public static Bitmap CreateMap(TerrainManager mgr, int[] scale1, int[] scaleX, Bitmap map, int scale, int acreIndex = -1)
{
var img = CreateMap(mgr);
var map = ImageUtil.ResizeImage(img, img.Width * scale, img.Height * scale);
CreateMap(mgr, scale1);
ImageUtil.ScalePixelImage(scale1, scaleX, map.Width, map.Height, scale);
ImageUtil.SetBitmapData(map, scaleX);
if (acreIndex < 0)
return map;
@ -65,13 +65,13 @@ private static Bitmap DrawReticle(Bitmap map, MapGrid mgr, int x, int y, int sca
return map;
}
public static Bitmap GetMapWithBuildings(TerrainManager mgr, IReadOnlyList<Building> buildings, ushort plazaX, ushort plazaY, Font? f, int scale = 4, int index = -1)
public static Bitmap GetMapWithBuildings(MapTerrainStructure m, Font? f, int[] scale1, int[] scaleX, Bitmap map, int scale = 4, int index = -1)
{
var map = CreateMap(mgr, scale);
CreateMap(m.Terrain, scale1, scaleX, map, scale);
using var gfx = Graphics.FromImage(map);
gfx.DrawPlaza(mgr, plazaX, plazaY, scale);
gfx.DrawBuildings(mgr, buildings, f, scale, index);
gfx.DrawPlaza(m.Terrain, (ushort)m.PlazaX, (ushort)m.PlazaY, scale);
gfx.DrawBuildings(m.Terrain, m.Buildings, f, scale, index);
return map;
}
@ -110,9 +110,14 @@ private static void DrawBuilding(Graphics gfx, Font? f, int scale, Brush pen, in
}
}
public static Bitmap GetAcre(in int topX, in int topY, TerrainManager t, int acreScale)
private static void SetAcreTerrainPixels(int x, int y, TerrainManager t, int[] data, int[] scaleX, int scale)
{
GetAcre1(x, y, t, data);
ImageUtil.ScalePixelImage(data, scaleX, 16 * scale, 16 * scale, scale);
}
private static void GetAcre1(int topX, int topY, TerrainManager t, int[] data)
{
int[] data = new int[16 * 16];
int index = 0;
for (int y = 0; y < 16; y++)
{
@ -123,33 +128,48 @@ public static Bitmap GetAcre(in int topX, in int topY, TerrainManager t, int acr
data[index] = TerrainTileColor.GetTileColor(tile).ToArgb();
}
}
var final = ImageUtil.ScalePixelImage(data, acreScale, 16, 16, out int fw, out int fh);
return ImageUtil.GetBitmap(final, fw, fh);
}
public static Bitmap GetAcre(in int topX, in int topY, TerrainManager t, int acreScale, IReadOnlyList<Building> buildings, ushort plazaX, ushort plazaY, Font f, int index = -1)
public static Bitmap GetAcre(MapView m, Font f, int[] scale1, int[] scaleX, Bitmap acre, int index = -1)
{
var img = GetAcre(topX, topY, t, acreScale);
using var gfx = Graphics.FromImage(img);
int mx = m.X / 2;
int my = m.Y / 2;
SetAcreTerrainPixels(mx, my, m.Map.Terrain, scale1, scaleX, m.TerrainScale);
gfx.DrawAcrePlaza(t, topX, topY, plazaX, plazaY, acreScale);
const int grid1 = unchecked((int)0xFF888888u);
const int grid2 = unchecked((int)0xFF666666u);
ImageUtil.SetBitmapData(acre, scaleX);
using var gfx = Graphics.FromImage(acre);
gfx.DrawAcrePlaza(m.Map.Terrain, mx, my, (ushort)m.Map.PlazaX, (ushort)m.Map.PlazaY, m.TerrainScale);
var buildings = m.Map.Buildings;
var t = m.Map.Terrain;
for (var i = 0; i < buildings.Count; i++)
{
var b = buildings[i];
t.GetBuildingRelativeCoordinates(topX, topY, acreScale, b.X, b.Y, out var x, out var y);
t.GetBuildingRelativeCoordinates(mx, my, m.TerrainScale, b.X, b.Y, out var x, out var y);
var pen = index == i ? Selected : Others;
DrawBuilding(gfx, null, acreScale, pen, x, y, b, Text);
if (!t.IsWithinGrid(acreScale, x, y))
continue;
var name = b.BuildingType.ToString();
gfx.DrawString(name, f, Text, new PointF(x, y - (acreScale * 2)), BuildingTextFormat);
DrawBuilding(gfx, null, m.TerrainScale, pen, x, y, b, Text);
}
return img;
ImageUtil.GetBitmapData(acre, scaleX);
FieldItemSpriteDrawer.DrawGrid(scaleX, acre.Width, acre.Height, m.AcreScale, grid1);
FieldItemSpriteDrawer.DrawGrid(scaleX, acre.Width, acre.Height, m.TerrainScale, grid2);
ImageUtil.SetBitmapData(acre, scaleX);
foreach (var b in buildings)
{
t.GetBuildingRelativeCoordinates(mx, my, m.TerrainScale, b.X, b.Y, out var x, out var y);
if (!t.IsWithinGrid(m.TerrainScale, x, y))
continue;
var name = b.BuildingType.ToString();
gfx.DrawString(name, f, Text, new PointF(x, y - (m.TerrainScale * 2)), BuildingTextFormat);
}
return acre;
}
private static void DrawAcrePlaza(this Graphics gfx, TerrainManager g, int topX, int topY, ushort px, ushort py, int scale)

View File

@ -54,6 +54,14 @@ public static void SetBitmapData(Bitmap bmp, int[] data, PixelFormat format = Pi
bmp.UnlockBits(bmpData);
}
public static void GetBitmapData(Bitmap bmp, int[] data, PixelFormat format = PixelFormat.Format32bppArgb)
{
var bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, format);
var ptr = bmpData.Scan0;
Marshal.Copy(ptr, data, 0, data.Length);
bmp.UnlockBits(bmpData);
}
// https://stackoverflow.com/a/24199315
public static Bitmap ResizeImage(Image image, int width, int height)
{

View File

@ -346,10 +346,8 @@ private void Menu_SavePNG_Click(object sender, EventArgs e)
private void B_EditBuildings_Click(object sender, EventArgs e)
{
var buildings = SAV.Main.Buildings;
using var editor = new BuildingEditor(buildings, SAV.Main);
if (editor.ShowDialog() == DialogResult.OK)
SAV.Main.Buildings = buildings;
using var editor = new BuildingEditor(SAV.Main);
editor.ShowDialog();
}
private void B_EditTurnipExchange_Click(object sender, EventArgs e)

View File

@ -16,6 +16,7 @@ protected override void Dispose(bool disposing)
if (disposing && (components != null))
{
components.Dispose();
Map.Dispose();
}
base.Dispose(disposing);
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using NHSE.Core;
using NHSE.Sprites;
@ -10,21 +11,29 @@ public partial class BuildingEditor : Form
{
private readonly IReadOnlyList<Building> Buildings;
private readonly MainSave SAV;
private readonly TerrainManager Terrain;
private static readonly IReadOnlyDictionary<string, string[]> HelpDictionary = StructureUtil.GetStructureHelpList();
public BuildingEditor(IReadOnlyList<Building> buildings, MainSave sav)
private readonly Bitmap Map;
private readonly int[] Scale1;
private readonly int[] ScaleX;
private readonly MapTerrainStructure Manager;
private const int scale = 4;
public BuildingEditor(MainSave sav)
{
InitializeComponent();
this.TranslateInterface(GameInfo.CurrentLanguage);
Manager = new MapTerrainStructure(sav);
SAV = sav;
Buildings = buildings;
Terrain = new TerrainManager(sav.GetTerrainTiles());
Buildings = Manager.Buildings;
Scale1 = new int[Manager.Terrain.MapWidth * Manager.Terrain.MapHeight];
ScaleX = new int[Scale1.Length * scale * scale];
Map = new Bitmap(Manager.Terrain.MapWidth * scale, Manager.Terrain.MapHeight * scale);
NUD_PlazaX.Value = sav.PlazaX;
NUD_PlazaY.Value = sav.PlazaY;
foreach (var obj in buildings)
foreach (var obj in Manager.Buildings)
LB_Items.Items.Add(obj.ToString());
LB_Items.SelectedIndex = 0;
@ -43,6 +52,7 @@ private void B_Save_Click(object sender, EventArgs e)
SAV.PlazaY = (uint)NUD_PlazaY.Value;
DialogResult = DialogResult.OK;
SAV.Buildings = Manager.Buildings;
Close();
}
@ -52,10 +62,9 @@ private void B_Save_Click(object sender, EventArgs e)
private void DrawMap(in int index)
{
var font = B_Save.Font;
const int scale = 4;
var px = (ushort) NUD_PlazaX.Value;
var py = (ushort) NUD_PlazaY.Value;
PB_Map.Image = TerrainSprite.GetMapWithBuildings(Terrain, Buildings, px, py, font, scale, index);
Manager.PlazaX = (ushort) NUD_PlazaX.Value;
Manager.PlazaY = (ushort) NUD_PlazaY.Value;
PB_Map.Image = TerrainSprite.GetMapWithBuildings(Manager, font, Scale1, ScaleX, Map, scale, index);
}
private void LB_Items_SelectedIndexChanged(object sender, EventArgs e)

View File

@ -66,8 +66,19 @@ private void LoadItemGridAcre()
}
private int GetItemTransparency() => ((int)(0xFF * TR_Transparency.Value / 100d) << 24) | 0x00FF_FFFF;
private void ReloadMapBackground() => PB_Map.BackgroundImage = View.GetBackgroundTerrain(SelectedBuildingIndex);
private void ReloadAcreBackground() => PB_Acre.BackgroundImage = View.GetBackgroundAcre(L_Coordinates.Font, SelectedBuildingIndex);
private void ReloadMapBackground()
{
PB_Map.BackgroundImage = View.GetBackgroundTerrain(SelectedBuildingIndex);
PB_Map.Invalidate(); // background image reassigning to same img doesn't redraw; force it
}
private void ReloadAcreBackground()
{
PB_Acre.BackgroundImage = View.GetBackgroundAcre(L_Coordinates.Font, SelectedBuildingIndex);
PB_Acre.Invalidate(); // background image reassigning to same img doesn't redraw; force it
}
private void ReloadMapItemGrid() => PB_Map.Image = View.GetMapWithReticle(GetItemTransparency());
private void ReloadAcreItemGrid() => PB_Acre.Image = View.GetLayerAcre(GetItemTransparency());
@ -392,7 +403,7 @@ private void Menu_SavePNG_Click(object sender, EventArgs e)
}
const string name = "map";
var bmp = FieldItemSpriteDrawer.GetBitmapLayer(Map.Items.Layer1);
var bmp = FieldItemSpriteDrawer.GetBitmapItemLayer(Map.Items.Layer1);
using var sfd = new SaveFileDialog
{
Filter = "png file (*.png)|*.png|All files (*.*)|*.*",

View File

@ -8,53 +8,68 @@ namespace NHSE.WinForms
public sealed class MapViewer : MapView, IDisposable
{
// Cached acre view objects to remove allocation/GC
private readonly int[] Scale1;
private readonly int[] ScaleX;
private readonly int[] PixelsItemAcre1;
private readonly int[] PixelsItemAcreX;
private readonly Bitmap ScaleAcre;
private readonly int[] MapPixels;
private readonly int[] PixelsItemMap;
private readonly Bitmap MapReticle;
private readonly int[] PixelsBackgroundAcre1;
private readonly int[] PixelsBackgroundAcreX;
private readonly Bitmap BackgroundAcre;
private readonly int[] PixelsBackgroundMap1;
private readonly int[] PixelsBackgroundMapX;
private readonly Bitmap BackgroundMap;
public MapViewer(MapManager m) : base(m)
{
var l1 = m.Items.Layer1;
Scale1 = new int[l1.GridWidth * l1.GridHeight];
ScaleX = new int[Scale1.Length * AcreScale * AcreScale];
PixelsItemAcre1 = new int[l1.GridWidth * l1.GridHeight];
PixelsItemAcreX = new int[PixelsItemAcre1.Length * AcreScale * AcreScale];
ScaleAcre = new Bitmap(l1.GridWidth * AcreScale, l1.GridHeight * AcreScale);
MapPixels = new int[l1.MapWidth * l1.MapHeight * MapScale * MapScale];
PixelsItemMap = new int[l1.MapWidth * l1.MapHeight * MapScale * MapScale];
MapReticle = new Bitmap(l1.MapWidth * MapScale, l1.MapHeight * MapScale);
PixelsBackgroundAcre1 = new int[16 * 16];
PixelsBackgroundAcreX = new int[PixelsItemAcreX.Length];
BackgroundAcre = new Bitmap(ScaleAcre.Width, ScaleAcre.Height);
PixelsBackgroundMap1 = new int[PixelsItemMap.Length / 4];
PixelsBackgroundMapX = new int[PixelsItemMap.Length];
BackgroundMap = new Bitmap(MapReticle.Width, MapReticle.Height);
}
public void Dispose()
{
ScaleAcre.Dispose();
MapReticle.Dispose();
BackgroundAcre.Dispose();
BackgroundMap.Dispose();
}
public Bitmap GetLayerAcre(int t) => GetLayerAcre(X, Y, t);
public Bitmap GetBackgroundAcre(Font f, int index = -1) => GetBackgroundAcre(X, Y, f, index);
public Bitmap GetMapWithReticle(int t) => GetMapWithReticle(X, Y, t, Map.CurrentLayer);
public Bitmap GetBackgroundTerrain(int index = -1)
{
return TerrainSprite.GetMapWithBuildings(Map.Terrain, Map.Buildings, (ushort)Map.PlazaX, (ushort)Map.PlazaY, null, 2, index);
return TerrainSprite.GetMapWithBuildings(Map, null, PixelsBackgroundMap1, PixelsBackgroundMapX, BackgroundMap, 2, index);
}
private Bitmap GetLayerAcre(int topX, int topY, int t)
{
var layer = Map.CurrentLayer;
return FieldItemSpriteDrawer.GetBitmapLayerAcre(layer, topX, topY, AcreScale, Scale1, ScaleX, ScaleAcre, t);
return FieldItemSpriteDrawer.GetBitmapItemLayerAcre(layer, topX, topY, AcreScale, PixelsItemAcre1, PixelsItemAcreX, ScaleAcre, t);
}
private Bitmap GetBackgroundAcre(int topX, int topY, Font f, int index = -1)
public Bitmap GetBackgroundAcre(Font f, int index = -1)
{
return TerrainSprite.GetAcre(topX / 2, topY / 2, Map.Terrain, AcreScale * 2, Map.Buildings,
(ushort)Map.PlazaX, (ushort)Map.PlazaY, f, index);
return TerrainSprite.GetAcre(this, f, PixelsBackgroundAcre1, PixelsBackgroundAcreX, BackgroundAcre, index);
}
private Bitmap GetMapWithReticle(int topX, int topY, int t, FieldItemLayer layer)
{
return FieldItemSpriteDrawer.GetBitmapLayer(layer, topX, topY, MapPixels, MapReticle, t);
return FieldItemSpriteDrawer.GetBitmapItemLayer(layer, topX, topY, PixelsItemMap, MapReticle, t);
}
}
}

View File

@ -16,6 +16,7 @@ protected override void Dispose(bool disposing)
if (disposing && (components != null))
{
components.Dispose();
Map.Dispose();
}
base.Dispose(disposing);
}

View File

@ -19,6 +19,10 @@ public partial class TerrainEditor : Form
private const int SquareSize = 50;
private const int MapScale = 2;
private readonly int[] Scale1;
private readonly int[] ScaleX;
private readonly Bitmap Map;
public TerrainEditor(MainSave sav)
{
InitializeComponent();
@ -31,6 +35,10 @@ public TerrainEditor(MainSave sav)
foreach (var acre in MapGrid.Acres)
CB_Acre.Items.Add(acre.Name);
Scale1 = new int[Terrain.MapWidth * Terrain.MapHeight];
ScaleX = new int[Scale1.Length * MapScale * MapScale];
Map = new Bitmap(Terrain.MapWidth * MapScale, Terrain.MapHeight * MapScale);
PG_Tile.SelectedObject = new TerrainTile();
CB_Acre.SelectedIndex = 0;
ReloadMap();
@ -50,7 +58,7 @@ private void ChangeViewToAcre(int acre)
UpdateArrowVisibility(acre);
}
private void ReloadMap() => PB_Map.Image = TerrainSprite.CreateMap(Terrain, MapScale, X, Y);
private void ReloadMap() => PB_Map.Image = TerrainSprite.CreateMap(Terrain, MapScale, X, Y, Scale1, ScaleX, Map);
private void LoadGrid(int topX, int topY)
{
@ -251,7 +259,7 @@ private void Menu_SavePNG_Click(object sender, EventArgs e)
}
const string name = "map";
var bmp = TerrainSprite.CreateMap(Terrain);
using var sfd = new SaveFileDialog
{
Filter = "png file (*.png)|*.png|All files (*.*)|*.*",
@ -260,6 +268,8 @@ private void Menu_SavePNG_Click(object sender, EventArgs e)
if (sfd.ShowDialog() != DialogResult.OK)
return;
var bmp = new Bitmap(Terrain.MapWidth, Terrain.MapHeight);
ImageUtil.SetBitmapData(bmp, Scale1);
bmp.Save(sfd.FileName, ImageFormat.Png);
}