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

View File

@ -22,79 +22,70 @@ using MCGalaxy.Drawing.Ops;
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;
BitmapData data;
public PixelGetter(Bitmap bmp) {
this.bmp = bmp;
byte* scan0;
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() {
bool fastPath = bmp.PixelFormat == PixelFormat.Format32bppRgb
|| bmp.PixelFormat == PixelFormat.Format32bppArgb
|| bmp.PixelFormat == PixelFormat.Format24bppRgb;
if (!fastPath) return;
|| bmp.PixelFormat == PixelFormat.Format32bppArgb
|| bmp.PixelFormat == PixelFormat.Format24bppRgb;
if (!fastPath) { Get = GetGenericPixel; return; }
// We can only use the fast path for 24bpp or 32bpp bitmaps
Rectangle r = new Rectangle(0, 0, bmp.Width, bmp.Height);
data = bmp.LockBits(r, ImageLockMode.ReadOnly, bmp.PixelFormat);
}
public void Iterate(DrawOpOutput output, PixelGetterCallback callback) {
if (data == null) IterateSlow(output, callback);
else IterateFast(output, callback);
}
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);
}
scan0 = (byte*)data.Scan0;
stride = data.Stride;
if (bmp.PixelFormat == PixelFormat.Format24bppRgb) {
Get = Get24BppPixel;
} else {
Get = Get32BppPixel;
}
}
void IterateSlow(DrawOpOutput output, PixelGetterCallback callback) {
public Pixel GetGenericPixel(int x, int y) {
Pixel pixel;
int width = bmp.Width, height = bmp.Height;
for (int y = 0; y < height; y++)
for (int x = 0; x < width; x++)
{
pixel.X = (ushort)x; pixel.Y = (ushort)y;
int argb = bmp.GetPixel(x, y).ToArgb(); // R/G/B properties incur overhead
pixel.A = (byte)(argb >> 24);
pixel.R = (byte)(argb >> 16);
pixel.G = (byte)(argb >> 8);
pixel.B = (byte)argb;
callback(pixel, output);
}
int argb = bmp.GetPixel(x, y).ToArgb(); // R/G/B properties incur overhead
pixel.A = (byte)(argb >> 24);
pixel.R = (byte)(argb >> 16);
pixel.G = (byte)(argb >> 8);
pixel.B = (byte)argb;
return pixel;
}
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() {
if (data != null) bmp.UnlockBits(data);
data = 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.Net;
using MCGalaxy.Network;
using MCGalaxy.Drawing;
namespace MCGalaxy.Generator {
public static class HeightmapGen {
@ -73,12 +74,12 @@ namespace MCGalaxy.Generator {
Level lvl = args.Level;
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;
Bitmap bmp = ReadBitmap("tempImage_" + user, "extra/heightmap/", p);
if (bmp == null) return false;
int index = 0, oneY = lvl.Width * lvl.Length;
int index = 0, oneY = lvl.Width * lvl.Length;
using (bmp) {
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);
@ -86,35 +87,38 @@ namespace MCGalaxy.Generator {
return false;
}
for (int z = 0; z < bmp.Height; z++)
for (int x = 0; x < bmp.Width; x++)
{
int height = bmp.GetPixel(x, z).R;
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))
using (PixelGetter pixels = new PixelGetter(bmp)) {
pixels.Init();
for (int z = 0; z < pixels.Height; z++)
for (int x = 0; x < pixels.Width; x++)
{
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;
}
static bool IsShorterBy(int height, Bitmap bmp, int x, int z) {
if (x >= bmp.Width || x < 0 || z >= bmp.Height || z < 0) return false;
int neighbourHeight = bmp.GetPixel(x, z).R;
static bool IsShorterBy(int height, PixelGetter pixels, int x, int z) {
if (x >= pixels.Width || x < 0 || z >= pixels.Height || z < 0) return false;
int neighbourHeight = pixels.Get(x, z).R;
return height >= neighbourHeight + 2;
}
}