mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-22 12:05:51 -04:00
Use optimised PixelGetter for heightmap too. Fixes #237.
This commit is contained in:
parent
c25e34fb29
commit
daca4b946f
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user