mirror of
https://github.com/ClassiCube/MCGalaxy.git
synced 2025-09-23 04:32:50 -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)) {
|
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) {
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user