diff --git a/DungeonShooting_Godot/src/framework/map/image/ImageCanvas.cs b/DungeonShooting_Godot/src/framework/map/image/ImageCanvas.cs index 5a1d349..c320f91 100644 --- a/DungeonShooting_Godot/src/framework/map/image/ImageCanvas.cs +++ b/DungeonShooting_Godot/src/framework/map/image/ImageCanvas.cs @@ -1,11 +1,22 @@ +using System; +using System.Collections.Generic; using Godot; -public partial class ImageCanvas : Sprite2D +public partial class ImageCanvas : Sprite2D, IDestroy { + /// + /// 画布大小 + /// public int Width { get; } + + /// + /// 画布高度 + /// public int Height { get; } + public bool IsDestroyed { get; private set; } + private Image _canvas; private ImageTexture _texture; @@ -15,6 +26,7 @@ Height = height; _canvas = Image.Create(width, height, false, Image.Format.Rgba8); + _texture = ImageTexture.CreateFromImage(_canvas); _canvas.Fill(Colors.Gray); var w = _canvas.GetWidth(); @@ -33,39 +45,93 @@ public override void _Ready() { Centered = false; - - var imageTexture = ImageTexture.CreateFromImage(_canvas); - Texture = imageTexture; + Texture = _texture; } + /// + /// 添加到预渲染队列中 + /// + /// 需要渲染的纹理 + /// 离画布左上角x坐标 + /// 离画布左上角y坐标 + /// 旋转角度, 角度制 + /// 旋转中心点x + /// 旋转中心点y + /// 是否翻转y轴 public void DrawImageInCanvas(Texture2D texture, int x, int y, float angle, int centerX, int centerY, bool flipY) { - var image = texture.GetImage(); - if (flipY) + var item = new EnqueueItem(); + item.Canvas = this; + item.Image = texture.GetImage(); + item.X = x; + item.Y = y; + item.Angle = angle; + item.CenterX = centerX; + item.CenterY = centerY; + item.FlipY = flipY; + + _enqueueItems.Enqueue(item); + } + + public void Destroy() + { + if (IsDestroyed) { - image.FlipY(); + return; } - var newAngle = Mathf.RoundToInt(Utils.ConvertAngle(angle)); + IsDestroyed = true; + } + + private void Redraw() + { + _texture.Update(_canvas); + } + + private void HandlerDrawImageInCanvas(EnqueueItem item) + { + var image = item.Image; + var newAngle = Mathf.RoundToInt(Utils.ConvertAngle(item.Angle)); if (newAngle == 0) //原图, 直接画上去 { - _canvas.BlitRect(image, new Rect2I(0, 0, image.GetWidth(), image.GetHeight()), - new Vector2I(x - centerX, y - centerY)); + if (item.FlipY) + { + image.FlipY(); + } + + _canvas.BlendRect(image, new Rect2I(0, 0, image.GetWidth(), image.GetHeight()), + new Vector2I(item.X - item.CenterX, item.Y - item.CenterY)); } else //其他角度 { if (newAngle <= 180) { - DrawRotateImage(image, x, y, newAngle, centerX, centerY); + if (item.FlipY) + { + image.FlipY(); + } + + DrawRotateImage(image, item.X, item.Y, newAngle, item.CenterX, item.CenterY); } else { image.FlipX(); - image.FlipY(); - DrawRotateImage(image, x, y, (newAngle - 180), image.GetWidth() - centerX - 1, image.GetHeight() - centerY - 1); + if (!item.FlipY) + { + image.FlipY(); + } + + DrawRotateImage( + image, item.X, item.Y, + newAngle - 180, + image.GetWidth() - item.CenterX - 1, image.GetHeight() - item.CenterY - 1 + ); } } + + image.Dispose(); + item.Image = null; } private void DrawRotateImage(Image origin, int x, int y, int angle, int centerX, int centerY) @@ -90,43 +156,112 @@ var newHeight = Mathf.RoundToInt(width * sinAngle + height * Mathf.Abs(cosAngle)); var offsetX = - (int)((centerX / sinAngle + - (0.5f * newWidth * sinAngle - 0.5f * newHeight * cosAngle + 0.5f * height) / - cosAngle - - (-0.5f * newWidth * cosAngle - 0.5f * newHeight * sinAngle + 0.5f * width) / - sinAngle - centerY / cosAngle) / - (cosAngle / sinAngle + sinAngle / cosAngle)); + Mathf.RoundToInt((centerX / sinAngle + + (0.5f * newWidth * sinAngle - 0.5f * newHeight * cosAngle + 0.5f * height) / + cosAngle - + (-0.5f * newWidth * cosAngle - 0.5f * newHeight * sinAngle + 0.5f * width) / + sinAngle - centerY / cosAngle) / + (cosAngle / sinAngle + sinAngle / cosAngle)); var offsetY = - (int)((centerX / cosAngle - - (-0.5f * newWidth * cosAngle - 0.5f * newHeight * sinAngle + 0.5f * width) / - cosAngle - - (0.5f * newWidth * sinAngle - 0.5f * newHeight * cosAngle + 0.5f * height) / - sinAngle + centerY / sinAngle) / - (sinAngle / cosAngle + cosAngle / sinAngle)); + Mathf.RoundToInt((centerX / cosAngle - + (-0.5f * newWidth * cosAngle - 0.5f * newHeight * sinAngle + 0.5f * width) / + cosAngle - + (0.5f * newWidth * sinAngle - 0.5f * newHeight * cosAngle + 0.5f * height) / + sinAngle + centerY / sinAngle) / + (sinAngle / cosAngle + cosAngle / sinAngle)); var num1 = -0.5f * newWidth * cosAngle - 0.5f * newHeight * sinAngle + 0.5f * width; var num2 = 0.5f * newWidth * sinAngle - 0.5f * newHeight * cosAngle + 0.5f * height; - for (int x2 = 0; x2 < newWidth; x2++) + var cw = _canvas.GetWidth(); + var ch = _canvas.GetHeight(); + for (var x2 = 0; x2 < newWidth; x2++) { - for (int y2 = 0; y2 < newHeight; y2++) + for (var y2 = 0; y2 < newHeight; y2++) { - //如果(x1,y1)不在原图宽高所表示范围内,则(x2,y2)处的像素值设置为0或255 var x1 = Mathf.RoundToInt(x2 * cosAngle + y2 * sinAngle + num1); var y1 = Mathf.RoundToInt(-x2 * sinAngle + y2 * cosAngle + num2); if (x1 < 0 || x1 >= width || y1 < 0 || y1 >= height) { - //image.SetPixel(x, y, new Color(0, 0, 0, 0)); //在图片外 continue; } - //如果(x1,y1)在原图宽高所表示范围内,使用最近邻插值或双线性插值,求出(x2,y2)处的像素值 - _canvas.SetPixel(x2 + x - offsetX, y2 + y - offsetY, origin.GetPixel(x1, y1)); + var cx = x2 + x - offsetX; + if (cx < 0 || cx >= cw) + { + continue; + } + + var cy = y2 + y - offsetY; + if (cy < 0 || cy >= ch) + { + continue; + } + + _canvas.SetPixel(cx, cy, _canvas.GetPixel(cx, cy).Blend(origin.GetPixel(x1, y1))); } } } + + //-------------------------------------------------------------------------------------------------------------- + private class EnqueueItem + { + public ImageCanvas Canvas; + public Image Image; + public int X; + public int Y; + public float Angle; + public int CenterX; + public int CenterY; + public bool FlipY; + } + + private static readonly Queue _enqueueItems = new Queue(); + private static readonly HashSet _redrawCanvas = new HashSet(); + + /// + /// 根据重绘队列更新画布 + /// + public static void UpdateImageCanvas(float delta) + { + if (_enqueueItems.Count > 0) + { + _redrawCanvas.Clear(); + var startTime = DateTime.Now; + + var c = _enqueueItems.Count; + //执行绘制操作 + //绘制的总时间不能超过2毫秒, 如果超过了, 那就下一帧再画其它的吧 + do + { + var item = _enqueueItems.Dequeue(); + if (!item.Canvas.IsDestroyed) + { + item.Canvas.HandlerDrawImageInCanvas(item); + _redrawCanvas.Add(item.Canvas); + } + } while (_enqueueItems.Count > 0 && (DateTime.Now - startTime).TotalMilliseconds < 2); + + if (_enqueueItems.Count > 0) + { + GD.Print($"ImageCanvas: 还剩下{_enqueueItems.Count}个, 已经处理了{c - _enqueueItems.Count}个, 重绘画布{_redrawCanvas.Count}张"); + } + else + { + GD.Print($"ImageCanvas: 全部画完, 已经处理了{c - _enqueueItems.Count}个, 重绘画布{_redrawCanvas.Count}张"); + } + + //重绘画布 + foreach (var drawCanvas in _redrawCanvas) + { + drawCanvas.Redraw(); + } + + _redrawCanvas.Clear(); + } + } } \ No newline at end of file diff --git a/DungeonShooting_Godot/src/test/TestOptimizeSprite.cs b/DungeonShooting_Godot/src/test/TestOptimizeSprite.cs index 95cbda4..490f373 100644 --- a/DungeonShooting_Godot/src/test/TestOptimizeSprite.cs +++ b/DungeonShooting_Godot/src/test/TestOptimizeSprite.cs @@ -1,4 +1,3 @@ -using System; using Godot; public partial class TestOptimizeSprite : Node2D @@ -7,60 +6,38 @@ public override void _Ready() { - var imageCanvas = new ImageCanvas(200, 200); + var imageCanvas = new ImageCanvas(1920, 1080); imageCanvas.Scale = new Vector2(4, 4); - imageCanvas.DrawImageInCanvas(Texture2D, 10, 30, 0, 0, 0, false); - var time = DateTime.Now; + //imageCanvas.DrawImageInCanvas(Texture2D, 10, 30, 0, 0, 0, false); + //var time = DateTime.Now; //imageCanvas.DrawImageInCanvas(Texture2D, 50, 30, 30, 0, 0, true); - GD.Print("useTime: " + (DateTime.Now - time).TotalMilliseconds); - var time2 = DateTime.Now; - //imageCanvas.DrawImageInCanvas(Texture2D, 100, 100, 0, 0, 0, false); - //imageCanvas.DrawImageInCanvas(Texture2D, 100, 100, 90, Texture2D.GetWidth() / 2, Texture2D.GetHeight() / 2, false); - imageCanvas.DrawImageInCanvas(Texture2D, 100, 100, 300, 0, 0, false); - //imageCanvas.DrawImageInCanvas(Texture2D, 100, 100, 145, (int)(Texture2D.GetWidth() * 0.2f), (int)(Texture2D.GetHeight() * 0.2f), false); - //imageCanvas.DrawImageInCanvas(Texture2D, 140, 30, 270, 0, 0, true); + // GD.Print("useTime: " + (DateTime.Now - time).TotalMilliseconds); + // var time2 = DateTime.Now; + // //imageCanvas.DrawImageInCanvas(Texture2D, 100, 100, 0, 0, 0, false); + // //imageCanvas.DrawImageInCanvas(Texture2D, 100, 100, 90, Texture2D.GetWidth() / 2, Texture2D.GetHeight() / 2, false); + // imageCanvas.DrawImageInCanvas(Texture2D, 100, 100, 270, 0, 0, false); + // //imageCanvas.DrawImageInCanvas(Texture2D, 100, 100, 90, 0, 0, false); + // //imageCanvas.DrawImageInCanvas(Texture2D, 100, 100, 145, (int)(Texture2D.GetWidth() * 0.2f), (int)(Texture2D.GetHeight() * 0.2f), false); + // //imageCanvas.DrawImageInCanvas(Texture2D, 140, 30, 270, 0, 0, true); + // + // GD.Print("useTime: " + (DateTime.Now - time2).TotalMilliseconds); - GD.Print("useTime: " + (DateTime.Now - time2).TotalMilliseconds); + for (int i = 0; i < 50; i++) + { + for (int j = 0; j < 50; j++) + { + var centerX = Utils.RandomRangeInt(0, Texture2D.GetWidth() - 1); + var centerY = Utils.RandomRangeInt(0, Texture2D.GetHeight() - 1); + imageCanvas.DrawImageInCanvas(Texture2D, 30 + (i + 1) * 10, 30 + (j + 1) * 10, Utils.RandomRangeInt(0, 360), centerX, centerY, Utils.RandomBoolean()); + } + } + AddChild(imageCanvas); } - private void Test1() + + public override void _Process(double delta) { - var canvas = Image.Create(150, 150, false, Image.Format.Rgba8); - - for (int x = 0; x < 150; x++) - { - for (int y = 0; y < 150; y++) - { - canvas.SetPixel(x, y, Colors.Gray); - } - } - - var image = Texture2D.GetImage(); - image.FlipX(); - image.Rotate180(); - canvas.BlitRect(image, new Rect2I(0, 0, image.GetWidth(), image.GetHeight()), new Vector2I(10, 10)); - //RotateImage(image, canvas, 50, 50, 30, image.GetWidth(), image.GetHeight()); - - //RotateImage(imgData, image, 0); - - for (int i = 0; i < canvas.GetWidth(); i++) - { - canvas.SetPixel(i, 50, Colors.Black); - canvas.SetPixel(i, 50 + image.GetHeight() / 2, Colors.Green); - } - - for (int i = 0; i < canvas.GetHeight(); i++) - { - canvas.SetPixel(50, i, Colors.Black); - canvas.SetPixel(50 + image.GetWidth() / 2, i, Colors.Green); - } - - var imageTexture = ImageTexture.CreateFromImage(canvas); - var sprite2D = new Sprite2D(); - sprite2D.Texture = imageTexture; - sprite2D.Position = new Vector2(900, 500); - sprite2D.Scale = new Vector2(5, 5); - AddChild(sprite2D); + ImageCanvas.UpdateImageCanvas((float)delta); } }