// Spludlow Software // Copyright © Samuel P. Ludlow 2020 All Rights Reserved // Distributed under the terms of the GNU General Public License version 3 // Distributed WITHOUT ANY WARRANTY; without implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE // https://www.spludlow.co.uk/LICENCE.TXT // The Spludlow logo is a registered trademark of Samuel P. Ludlow and may not be used without permission // v1.14 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.IO; using System.Data; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; namespace Spludlow.Drawing { public class Fractals { [Serializable] public class FractalsInfo { public int PixelWidth; // Fixed public int PixelHeight; public int Dpi; public string FractalType; public double X; public double Y; public double Width; public double Height; public int Iterations; public double PalleteFactor; public int RandomSeed; //public string[] Paramters; public string Paramters = ""; } public class RenderInfo { public FractalsInfo Fractal; public int Total; public int Offset; public int Length; public int ThreadIndex; public BitmapData BitmapData; } public static string Queue = "Run"; public static DataSet QueryHosts() { string[] hosts = Spludlow.Config.Hosts(); DataTable table = Spludlow.Data.TextTable.ReadText(new string[] { "HostId AvailableCores UseCores Info", "String* Int32 Int32 String", }); Task[] tasks = new Task[hosts.Length]; for (int threadIndex = 0; threadIndex < hosts.Length; ++threadIndex) { string host = hosts[threadIndex]; tasks[threadIndex] = new Task(() => QueryHostsWork(table, host)); table.Rows.Add(new object[] { host }); } foreach (Task task in tasks) task.Start(); Task.WaitAll(tasks); return Spludlow.Data.ADO.WireDataSet(table); } private static void QueryHostsWork(DataTable table, string host) { int cores = 0; string info = null; try { cores = (int)Spludlow.Call.Now(host, "System", "System.Environment", "ProcessorCount"); info = cores + " Cores Available"; } catch (Exception ee) { info = ee.Message; } lock (table) { DataRow row = table.Rows.Find(host); row["AvailableCores"] = cores; if (cores > 1) cores--; if (cores > 0) row["UseCores"] = cores; if (info != null) row["Info"] = info; } } public static string[] FractalTypes() { return Spludlow.Reflections.MethodNames(typeof(Spludlow.Drawing.FractalMaths)); } public static string DefaultParameters(string fractalName) { return (string)Spludlow.Reflections.Invoke("Spludlow", "Spludlow.Drawing.FractalMaths", fractalName, new object[] { null }, null); } public static void Run(string resultHost, string[] hostCores, FractalsInfo fractalInfo, string outputDirectory) { List hosts = new List(); List hostOffsets = new List(); List hostCoreCounts = new List(); int totalLength = 0; foreach (string hostCore in hostCores) { string[] words = hostCore.Split(new char[] { '\t' }); string host = words[0]; int cores = Int32.Parse(words[1]); hosts.Add(host); hostOffsets.Add(totalLength); hostCoreCounts.Add(cores); totalLength += cores; } List methods = new List(); for (int index = 0; index < hosts.Count; ++index) { string host = hosts[index]; int offset = hostOffsets[index]; int length = hostCoreCounts[index]; methods.Add(new CallMethod(host + ":" + Queue, "Spludlow", "Spludlow.Drawing.Fractals", "RenderHost", new object[] { fractalInfo, totalLength, offset, length })); } CallMethod resultMethod = new CallMethod(resultHost + ":" + Queue, "Spludlow", "Spludlow.Drawing.Fractals", "ReceiveResults", new object[] { null, fractalInfo, hosts.ToArray(), hostOffsets.ToArray(), hostCoreCounts.ToArray(), outputDirectory }, CallFlags.None, 0); Spludlow.Call.Parallel(methods.ToArray(), resultMethod); } public static byte[] RenderHost(string fractalInfoText) { FractalsInfo fractalInfo = new FractalsInfo(); Spludlow.Reflections.ReadTextFields(fractalInfo, fractalInfoText); return RenderHost(fractalInfo); } public static byte[] RenderHost(FractalsInfo fractalInfo) { int threads = Environment.ProcessorCount; if (threads > 1) --threads; return RenderHost(fractalInfo, threads); } public static byte[] RenderHost(FractalsInfo fractalInfo, int threads) { return RenderHost(fractalInfo, threads, 0, threads); } public static byte[] RenderHost(FractalsInfo fractalInfo, int total, int offset, int length) { DateTime startTime = DateTime.Now; PixelFormat pixelFormat = PixelFormat.Format24bppRgb; using (Bitmap bitmap = new Bitmap(fractalInfo.PixelWidth, fractalInfo.PixelHeight, pixelFormat)) { bitmap.SetResolution(fractalInfo.Dpi, fractalInfo.Dpi); BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.WriteOnly, pixelFormat); Task[] tasks = new Task[length]; for (int threadIndex = 0; threadIndex < length; ++threadIndex) { RenderInfo renderInfo = new RenderInfo(); renderInfo.Fractal = fractalInfo; renderInfo.Total = total; renderInfo.Offset = offset; renderInfo.Length = length; renderInfo.ThreadIndex = threadIndex; renderInfo.BitmapData = bitmapData; tasks[threadIndex] = new Task(() => RenderThread(renderInfo)); } foreach (Task task in tasks) task.Start(); Task.WaitAll(tasks); bitmap.UnlockBits(bitmapData); using (MemoryStream stream = new MemoryStream()) { bitmap.Save(stream, ImageFormat.Png); byte[] data = stream.ToArray(); Spludlow.Log.Report("Fractal; Cores:" + length + ", Time:" + Spludlow.Text.TimeTook(startTime) + ", Size:" + Spludlow.Text.DataSize(data.Length)); return data; } } } public static void RenderThread(RenderInfo renderInfo) { Spludlow.Reflections.Invoke("Spludlow", "Spludlow.Drawing.FractalMaths", renderInfo.Fractal.FractalType, new object[] { renderInfo }, null); } public static int ReceiveResults(object[] results, FractalsInfo fractalInfo, string[] hosts, int[] hostOffsets, int[] hostCores, string outputDirectory) { int totalCores = 0; foreach (int cores in hostCores) totalCores += cores; PixelFormat pixelFormat = PixelFormat.Format24bppRgb; using (MemoryStream finalStream = new MemoryStream((byte[])results[0])) { using (Bitmap finalBitmap = new Bitmap(finalStream)) { BitmapData finalBitmapData = finalBitmap.LockBits(new Rectangle(0, 0, finalBitmap.Width, finalBitmap.Height), ImageLockMode.WriteOnly, pixelFormat); int stride = finalBitmapData.Stride; byte[] lineData = new byte[stride]; for (int hostIndex = 1; hostIndex < results.Length; ++hostIndex) { using (MemoryStream partStream = new MemoryStream((byte[])results[hostIndex])) { using (Bitmap partBitmap = new Bitmap(partStream)) { BitmapData partBitmapData = partBitmap.LockBits(new Rectangle(0, 0, partBitmap.Width, partBitmap.Height), ImageLockMode.ReadOnly, pixelFormat); for (int y = 0; y < partBitmap.Height; ++y) { if (OnTheLine(y, totalCores, hostOffsets[hostIndex], hostCores[hostIndex], -1) == false) continue; int index = y * stride; Marshal.Copy(partBitmapData.Scan0 + index, lineData, 0, stride); // ?? direct copy Marshal.Copy(lineData, 0, finalBitmapData.Scan0 + index, stride); } } } } finalBitmap.UnlockBits(finalBitmapData); string timeStamp = Spludlow.Text.TimeStamp(); string filename = outputDirectory + @"\" + timeStamp; finalBitmap.Save(filename + ".png", ImageFormat.Png); File.WriteAllText(filename + ".txt", Spludlow.Reflections.WriteTextFields(fractalInfo)); } } //Spludlow.Email.Send(); Spludlow.Log.Report("ReceiveResults:\t" + results.Length); return results.Length; } public static bool OnTheLine(int line, RenderInfo info) { return OnTheLine(line, info.Total, info.Offset, info.Length, info.ThreadIndex); } public static bool OnTheLine(int line, int total, int offset, int length, int threadIndex) { int modulo = line % total; if (modulo >= offset && modulo < (offset + length)) { if (threadIndex == -1 || threadIndex == (modulo - offset)) return true; } return false; } public static void Plot(int x, int y, int iteration, RenderInfo info) { int bytesPerPixel = 3; int index = (y * info.BitmapData.Stride) + (x * bytesPerPixel); double colourIndex = Math.Round((double)iteration * info.Fractal.PalleteFactor, 0); Color colour = Spludlow.Drawing.Colour.FromHSV((int)colourIndex % 360, 1, 1); Marshal.WriteByte(info.BitmapData.Scan0, index, colour.B); Marshal.WriteByte(info.BitmapData.Scan0, index + 1, colour.G); Marshal.WriteByte(info.BitmapData.Scan0, index + 2, colour.R); } } }