Use optimised PixelGetter for heightmap too. Fixes #237.

This commit is contained in:
UnknownShadow200 2017-10-28 12:40:17 +11:00
parent c25e34fb29
commit daca4b946f
3 changed files with 92 additions and 93 deletions

View File

@ -45,7 +45,7 @@ namespace MCGalaxy.Drawing.Ops {
using (PixelGetter getter = new PixelGetter(Source)) { using (PixelGetter getter = new PixelGetter(Source)) {
getter.Init(); getter.Init();
getter.Iterate(output, OutputPixel); OutputPixels(getter, output);
} }
selector = null; selector = null;
@ -106,26 +106,30 @@ namespace MCGalaxy.Drawing.Ops {
return entry; return entry;
} }
void OutputPixel(Pixel P, DrawOpOutput output) { void OutputPixels(PixelGetter pixels, DrawOpOutput output) {
ushort x = (ushort)(Origin.X + dx.X * P.X + dy.X * P.Y); int width = pixels.Width, height = pixels.Height;
ushort y = (ushort)(Origin.Y + dx.Y * P.X + dy.Y * P.Y); for (int yy = 0; yy < height; yy++)
ushort z = (ushort)(Origin.Z + dx.Z * P.X + dy.Z * P.Y); for (int xx = 0; xx < width; xx++)
if (P.A < 20) { output(Place(x, y, z, ExtBlock.Air)); return; } {
Pixel P = pixels.Get(xx, yy);
byte raw = 0; ushort x = (ushort)(Origin.X + dx.X * xx + dy.X * yy);
if (!DualLayer) { ushort y = (ushort)(Origin.Y + dx.Y * xx + dy.Y * yy);
raw = selector.BestMatch(P.R, P.G, P.B); ushort z = (ushort)(Origin.Z + dx.Z * xx + dy.Z * yy);
} else { if (P.A < 20) { output(Place(x, y, z, ExtBlock.Air)); continue; }
bool backLayer;
raw = selector.BestMatch(P.R, P.G, P.B, out backLayer);
if (backLayer) { byte raw = 0;
x = (ushort)(x + adj.X); if (!DualLayer) {
z = (ushort)(z + adj.Z); raw = selector.BestMatch(P.R, P.G, P.B);
} else {
bool backLayer;
raw = selector.BestMatch(P.R, P.G, P.B, out backLayer);
if (backLayer) {
x = (ushort)(x + adj.X);
z = (ushort)(z + adj.Z);
}
} }
output(Place(x, y, z, ExtBlock.FromRaw(raw)));
} }
output(Place(x, y, z, ExtBlock.FromRaw(raw)));
} }
public void CalcState(int dir) { public void CalcState(int dir) {

View File

@ -22,79 +22,70 @@ using MCGalaxy.Drawing.Ops;
namespace MCGalaxy.Drawing { namespace MCGalaxy.Drawing {
public delegate void PixelGetterCallback(Pixel pixel, DrawOpOutput output); public delegate Pixel PixelGet(int x, int y);
public struct Pixel { public byte A, R, G, B; }
public sealed class PixelGetter : IDisposable { public unsafe sealed class PixelGetter : IDisposable {
Bitmap bmp; Bitmap bmp;
BitmapData data; BitmapData data;
public PixelGetter(Bitmap bmp) { byte* scan0;
this.bmp = bmp; int stride;
public PixelGet Get;
public readonly int Width, Height;
public PixelGetter(Bitmap bmp) {
this.bmp = bmp;
Width = bmp.Width; Height = bmp.Height;
} }
public void Init() { public void Init() {
bool fastPath = bmp.PixelFormat == PixelFormat.Format32bppRgb bool fastPath = bmp.PixelFormat == PixelFormat.Format32bppRgb
|| bmp.PixelFormat == PixelFormat.Format32bppArgb || bmp.PixelFormat == PixelFormat.Format32bppArgb
|| bmp.PixelFormat == PixelFormat.Format24bppRgb; || bmp.PixelFormat == PixelFormat.Format24bppRgb;
if (!fastPath) return; if (!fastPath) { Get = GetGenericPixel; return; }
// We can only use the fast path for 24bpp or 32bpp bitmaps // We can only use the fast path for 24bpp or 32bpp bitmaps
Rectangle r = new Rectangle(0, 0, bmp.Width, bmp.Height); Rectangle r = new Rectangle(0, 0, bmp.Width, bmp.Height);
data = bmp.LockBits(r, ImageLockMode.ReadOnly, bmp.PixelFormat); data = bmp.LockBits(r, ImageLockMode.ReadOnly, bmp.PixelFormat);
} scan0 = (byte*)data.Scan0;
stride = data.Stride;
public void Iterate(DrawOpOutput output, PixelGetterCallback callback) {
if (data == null) IterateSlow(output, callback); if (bmp.PixelFormat == PixelFormat.Format24bppRgb) {
else IterateFast(output, callback); Get = Get24BppPixel;
} } else {
Get = Get32BppPixel;
unsafe void IterateFast(DrawOpOutput output, PixelGetterCallback callback) {
Pixel pixel;
int width = bmp.Width, height = bmp.Height;
byte* scan0 = (byte*)data.Scan0;
pixel.A = 255;
bool hasA = bmp.PixelFormat != PixelFormat.Format24bppRgb;
for (int y = 0; y < height; y++) {
pixel.Y = (ushort)y;
byte* row = (scan0 + y * data.Stride);
for (int x = 0; x < width; x++) {
pixel.X = (ushort)x;
pixel.B = *row; row++;
pixel.G = *row; row++;
pixel.R = *row; row++;
if (hasA) { pixel.A = *row; row++; }
callback(pixel, output);
}
} }
} }
void IterateSlow(DrawOpOutput output, PixelGetterCallback callback) { public Pixel GetGenericPixel(int x, int y) {
Pixel pixel; Pixel pixel;
int width = bmp.Width, height = bmp.Height; int argb = bmp.GetPixel(x, y).ToArgb(); // R/G/B properties incur overhead
for (int y = 0; y < height; y++) pixel.A = (byte)(argb >> 24);
for (int x = 0; x < width; x++) pixel.R = (byte)(argb >> 16);
{ pixel.G = (byte)(argb >> 8);
pixel.X = (ushort)x; pixel.Y = (ushort)y; pixel.B = (byte)argb;
int argb = bmp.GetPixel(x, y).ToArgb(); // R/G/B properties incur overhead return pixel;
pixel.A = (byte)(argb >> 24);
pixel.R = (byte)(argb >> 16);
pixel.G = (byte)(argb >> 8);
pixel.B = (byte)argb;
callback(pixel, output);
}
} }
public Pixel Get24BppPixel(int x, int y) {
Pixel pixel;
byte* ptr = (scan0 + y * data.Stride) + (x * 3);
pixel.B = ptr[0]; pixel.G = ptr[1]; pixel.R = ptr[2]; pixel.A = 255;
return pixel;
}
public Pixel Get32BppPixel(int x, int y) {
Pixel pixel;
byte* ptr = (scan0 + y * data.Stride) + (x * 4);
pixel.B = ptr[0]; pixel.G = ptr[1]; pixel.R = ptr[2]; pixel.A = ptr[3];
return pixel;
}
public void Dispose() { public void Dispose() {
if (data != null) bmp.UnlockBits(data); if (data != null) bmp.UnlockBits(data);
data = null; data = null;
bmp = null; bmp = null;
} }
} }
public struct Pixel {
public ushort X, Y;
public byte A, R, G, B;
}
} }

View File

@ -20,6 +20,7 @@ using System.Drawing;
using System.IO; using System.IO;
using System.Net; using System.Net;
using MCGalaxy.Network; using MCGalaxy.Network;
using MCGalaxy.Drawing;
namespace MCGalaxy.Generator { namespace MCGalaxy.Generator {
public static class HeightmapGen { public static class HeightmapGen {
@ -73,12 +74,12 @@ namespace MCGalaxy.Generator {
Level lvl = args.Level; Level lvl = args.Level;
if (args.Args.Length == 0) { Player.Message(p, "You need to provide a url for the image."); return false; } if (args.Args.Length == 0) { Player.Message(p, "You need to provide a url for the image."); return false; }
if (!DownloadImage(args.Args, "extra/heightmap/", p )) return false; if (!DownloadImage(args.Args, "extra/heightmap/", p )) return false;
string user = p == null ? "(console)" : p.name; string user = p == null ? "(console)" : p.name;
Bitmap bmp = ReadBitmap("tempImage_" + user, "extra/heightmap/", p); Bitmap bmp = ReadBitmap("tempImage_" + user, "extra/heightmap/", p);
if (bmp == null) return false; if (bmp == null) return false;
int index = 0, oneY = lvl.Width * lvl.Length; int index = 0, oneY = lvl.Width * lvl.Length;
using (bmp) { using (bmp) {
if (lvl.Width != bmp.Width || lvl.Length != bmp.Height) { if (lvl.Width != bmp.Width || lvl.Length != bmp.Height) {
Player.Message(p, "The size of the heightmap is {0} by {1}.", bmp.Width, bmp.Height); Player.Message(p, "The size of the heightmap is {0} by {1}.", bmp.Width, bmp.Height);
@ -86,35 +87,38 @@ namespace MCGalaxy.Generator {
return false; return false;
} }
for (int z = 0; z < bmp.Height; z++) using (PixelGetter pixels = new PixelGetter(bmp)) {
for (int x = 0; x < bmp.Width; x++) pixels.Init();
{ for (int z = 0; z < pixels.Height; z++)
int height = bmp.GetPixel(x, z).R; for (int x = 0; x < pixels.Width; x++)
byte layer = Block.Dirt, top = Block.Grass;
if (
IsShorterBy(height, bmp, x - 1, z) ||
IsShorterBy(height, bmp, x + 1, z) ||
IsShorterBy(height, bmp, x, z - 1) ||
IsShorterBy(height, bmp, x, z + 1))
{ {
layer = Block.Stone; top = Block.Stone; int height = pixels.Get(x, z).R;
byte layer = Block.Dirt, top = Block.Grass;
if (
IsShorterBy(height, pixels, x - 1, z) ||
IsShorterBy(height, pixels, x + 1, z) ||
IsShorterBy(height, pixels, x, z - 1) ||
IsShorterBy(height, pixels, x, z + 1))
{
layer = Block.Stone; top = Block.Stone;
}
height = height * lvl.Height / 255;
for (int y = 0; y < height - 1; y++)
lvl.blocks[index + oneY * y] = layer;
if (height > 0)
lvl.blocks[index + oneY * (height - 1)] = top;
index++;
} }
height = height * lvl.Height / 255;
for (int y = 0; y < height - 1; y++)
lvl.blocks[index + oneY * y] = layer;
if (height > 0)
lvl.blocks[index + oneY * (height - 1)] = top;
index++;
} }
} }
return true; return true;
} }
static bool IsShorterBy(int height, Bitmap bmp, int x, int z) { static bool IsShorterBy(int height, PixelGetter pixels, int x, int z) {
if (x >= bmp.Width || x < 0 || z >= bmp.Height || z < 0) return false; if (x >= pixels.Width || x < 0 || z >= pixels.Height || z < 0) return false;
int neighbourHeight = bmp.GetPixel(x, z).R; int neighbourHeight = pixels.Get(x, z).R;
return height >= neighbourHeight + 2; return height >= neighbourHeight + 2;
} }
} }