So, I'm building a console application to generate 2D perlin(ish) noise that is split among multiple image files. The algorithm I'm using requires that I do the generation in two passes, one to create the seed for the noise, then to generate the noise. I'm saving the generated noise to the same file that the seed is generated to in order to cut down on disk-space, and to make it clean in the end. I'm running into an issue though, during the second pass, I can't actually access the file, as it's already being used by another procces. Here's the full code to see if there is anything that I'm missing:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
namespace MapGenerator
{
class Program
{
public static Random rng = new Random();
public static void Seed(int px, int py)
{
Bitmap seed = new Bitmap(256, 256, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
for (int x = 0; x < seed.Width; x++)
{
for (int y = 0; y < seed.Height; y++)
{
var val = rng.Next(0, 255);
seed.SetPixel(x, y, Color.FromArgb(255, val, val, val));
}
}
if (File.Exists("chunk," + (px - 1) + "," + (py) + ".jpeg"))
{
Bitmap source = new Bitmap("chunk," + (px - 1) + "," + (py) + ".jpeg");
for (int y = 0; y < 256; y++)
{
seed.SetPixel(0, y, Color.FromArgb(255, source.GetPixel(255, y).R, source.GetPixel(255, y).G, source.GetPixel(255, y).B));
}
}
if (File.Exists("chunk," + (px + 1) + "," + (py) + ".jpeg"))
{
Bitmap source = new Bitmap("chunk," + (px + 1) + "," + (py) + ".jpeg");
for (int y = 0; y < 256; y++)
{
seed.SetPixel(255, y, Color.FromArgb(255, source.GetPixel(0, y).R, source.GetPixel(0, y).G, source.GetPixel(0, y).B));
}
}
if (File.Exists("chunk," + (px) + "," + (py - 1) + ".jpeg"))
{
Bitmap source = new Bitmap("chunk," + (px) + "," + (py - 1) + ".jpeg");
for (int x = 0; x < 256; x++)
{
seed.SetPixel(x, 0, Color.FromArgb(255, source.GetPixel(x, 255).R, source.GetPixel(x, 255).G, source.GetPixel(x, 255).B));
}
}
if (File.Exists("chunk," + (px) + "," + (py + 1) + ".jpeg"))
{
Bitmap source = new Bitmap("chunk," + (px) + "," + (py + 1) + ".jpeg");
for (int x = 0; x < 256; x++)
{
seed.SetPixel(x, 255, Color.FromArgb(255, source.GetPixel(x, 0).R, source.GetPixel(x, 0).G, source.GetPixel(x, 0).B));
}
}
seed.Save("chunk," + px + "," + py + ".jpeg", System.Drawing.Imaging.ImageFormat.Jpeg);
seed.Dispose();
}
public static void Perlin(int px, int py)
{
Bitmap seed = new Bitmap("chunk," + px + "," + py + ".jpeg");
Bitmap output = new Bitmap(256, 256, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
for (int y = 0; y < 256; y++)
{
for (int x = 0; x < 256; x++)
{
double noise = 0.0;
double scale = 1.0;
double acc = 0.0;
for (int o = 0; o < 8; o++)
{
int pitch = 256 >> o;
int sampleX1 = (x / pitch) * pitch;
int sampleY1 = (y / pitch) * pitch;
int sampleX2 = (sampleX1 + pitch);
int sampleY2 = (sampleY1 + pitch);
sampleX2 = (sampleX2 == 256) ? 255 : sampleX2;
sampleY2 = (sampleY2 == 256) ? 255 : sampleY2;
double Xblend = (double)(x - sampleX1) / (double)pitch;
double Yblend = (double)(y - sampleY1) / (double)pitch;
// interpolate between the two points
double Tsample = ((1 - Xblend) * ((double)seed.GetPixel(sampleX1, sampleY1).R / 255.0)) + (Xblend * ((double)seed.GetPixel(sampleX2, sampleY1).R / 255.0));
double Bsample = ((1 - Xblend) * ((double)seed.GetPixel(sampleX1, sampleY2).R / 255.0)) + (Xblend * ((double)seed.GetPixel(sampleX2, sampleY2).R / 255.0));
noise += (((1 - Yblend) * Tsample) + (Yblend * Bsample)) * scale;
acc += scale;
scale = scale * 0.6;
}
noise = noise / acc;
noise = noise * 255.0;
output.SetPixel(x, y, Color.FromArgb(255, (int)(noise), (int)(noise), (int)(noise)));
}
}
seed.Dispose();
File.Delete("chunk," + px + "," + py + ".jpeg");
output.Save("chunk," + px + "," + py + ".jpeg", System.Drawing.Imaging.ImageFormat.Jpeg);
output.Dispose();
}
static void Main(string[] args)
{
int depth = 1;
for(int x = -depth; x <= depth; x++)
{
for(int y = -depth; y <= depth; y++)
{
Seed(x, y);
}
}
for (int x = -depth; x <= depth; x++)
{
for (int y = -depth; y <= depth; y++)
{
Perlin(x, y);
}
}
}
}
}
As you can see, I've tried a lot of disposing of the variables, but to no avail. Any ideas what I'm doing wrong? The odd thing is, when it was only a single function running, I could freely overwrite the base image file, but now that I split it up into two different functions, it's all downhill, and it won't let me manipulate the files.
You need to dispose your bitmaps, as below
Also i would use Lockbits
instead of GetPixel
and SetPixel
and you will get factors better performance
public static void Seed(int px, int py)
{
using (var seed = new Bitmap(256, 256, PixelFormat.Format24bppRgb))
{
for (var x = 0; x < seed.Width; x++)
for (var y = 0; y < seed.Height; y++)
{
var val = rng.Next(0, 255);
seed.SetPixel(x, y, Color.FromArgb(255, val, val, val));
}
if (File.Exists("chunk," + (px - 1) + "," + py + ".jpeg"))
using (var source = new Bitmap("chunk," + (px - 1) + "," + py + ".jpeg"))
for (var y = 0; y < 256; y++)
seed.SetPixel(0, y, Color.FromArgb(255, source.GetPixel(255, y).R, source.GetPixel(255, y).G, source.GetPixel(255, y).B));
if (File.Exists("chunk," + (px + 1) + "," + py + ".jpeg"))
using (var source = new Bitmap("chunk," + (px + 1) + "," + py + ".jpeg"))
for (var y = 0; y < 256; y++)
seed.SetPixel(255, y, Color.FromArgb(255, source.GetPixel(0, y).R, source.GetPixel(0, y).G, source.GetPixel(0, y).B));
if (File.Exists("chunk," + px + "," + (py - 1) + ".jpeg"))
using (var source = new Bitmap("chunk," + px + "," + (py - 1) + ".jpeg"))
for (var x = 0; x < 256; x++)
seed.SetPixel(x, 0, Color.FromArgb(255, source.GetPixel(x, 255).R, source.GetPixel(x, 255).G, source.GetPixel(x, 255).B));
if (File.Exists("chunk," + px + "," + (py + 1) + ".jpeg"))
using (var source = new Bitmap("chunk," + px + "," + (py + 1) + ".jpeg"))
for (var x = 0; x < 256; x++)
seed.SetPixel(x, 255, Color.FromArgb(255, source.GetPixel(x, 0).R, source.GetPixel(x, 0).G, source.GetPixel(x, 0).B));
seed.Save("chunk," + px + "," + py + ".jpeg", ImageFormat.Jpeg);
}
}
Example how to use LockBits
and pointers using the unsafe
keyword. Basically you are accessing your picture by scan lines in a pinned array using pointers directly accessing the 32 bit ARBG values in memory
using (var seed = new Bitmap(256, 256, PixelFormat.Format24bppRgb))
{
// lock the array for direct access
var bitmapData = seed.LockBits(new Rectangle(0,0,seed.Width,seed.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppPArgb);
// get the pointer
var scan0Ptr = (int*)bitmapData.Scan0;
// get the stride
var stride = bitmapData.Stride / 4;
for (var x = 0; x < seed.Width; x++)
for (var y = 0; y < seed.Height; y++)
{
var val = rng.Next(0, 255);
*(scan0Ptr + x + y * stride) = Color.FromArgb(255, val, val, val).ToArgb();
}
seed.UnlockBits(bitmapData);
...