/// <summary> Compresses a bitmap using TrigradCompression. </summary> /// <param name="bitmap"> The input bitmap.</param> /// <param name="options"> TrigradOptions specifying how the image will be compressed.</param> public static TrigradCompressed CompressBitmap(Bitmap bitmap, TrigradOptions options) { TrigradCompressed compressed = new TrigradCompressed { Height = bitmap.Height, Width = bitmap.Width }; List<Point> samplePoints = new List<Point>(); samplePoints.Add(new Point(0, 0)); samplePoints.Add(new Point(bitmap.Width - 1, 0)); samplePoints.Add(new Point(0, bitmap.Height - 1)); samplePoints.Add(new Point(bitmap.Width - 1, bitmap.Height - 1)); double baseChance = 1d / ((double)(bitmap.Width * bitmap.Height) / options.SampleCount / 8); for (int x = 0; x < bitmap.Width; x++) { for (int y = 0; y < bitmap.Height; y++) { double chance = ((options.FrequencyTable != null) ? options.FrequencyTable.Table[x, y] : 1d) * baseChance; if (options.Random.NextDouble() < chance) { samplePoints.Add(new Point(x, y)); } } } foreach (var sample in samplePoints) { List<Color> averageColors = new List<Color>(); for (int x = sample.X - options.SampleRadius; x < sample.X + options.SampleRadius + 1; x++) { for (int y = sample.Y - options.SampleRadius; y < sample.Y + options.SampleRadius + 1; y++) { if (y >= 0 && y < bitmap.Height && x >= 0 && x < bitmap.Width) { averageColors.Add(bitmap.GetPixel(x, y)); } } } byte R = (byte)averageColors.Average(c => c.R); byte G = (byte)averageColors.Average(c => c.G); byte B = (byte)averageColors.Average(c => c.B); compressed.SampleTable[sample] = Color.FromArgb(R, G, B); } return compressed; }
/// <summary> Decompresses a trigrad compressed bitmap. </summary> /// <param name="compressionData"> The TrigradCompressed data.</param> /// <param name="colorGrader"> The color grader that will be used to fill the output bitmap.</param> /// <param name="debug"> Bool specifying whether a debug output will be produced.</param> public static TrigradDecompressed DecompressBitmap(TrigradCompressed compressionData, IGrader colorGrader = null,bool debug = false) { if (colorGrader == null) colorGrader = new BarycentricGrader(); TrigradDecompressed decompressed = new TrigradDecompressed(compressionData.Width, compressionData.Height); //build the triangle mesh decompressed.Mesh = buildMesh(compressionData.SampleTable); Parallel.ForEach(decompressed.Mesh.Triangles, triangle => { var vU = triangle.GetVertex(0); var vV = triangle.GetVertex(1); var vW = triangle.GetVertex(2); //find sample points of triangle Color cU = compressionData.SampleTable[vU.Point()]; Color cV = compressionData.SampleTable[vV.Point()]; Color cW = compressionData.SampleTable[vW.Point()]; //rasterize triangle to find points to fill var points = TriangleRasterization.PointsInTriangle(vU.Point(), vV.Point(), vW.Point()); foreach (var point in points) { var coords = Barycentric.GetCoordinates(point, vU, vV, vW); Color gradedColor = colorGrader.Grade(cU, cV, cW, coords.U, coords.V, coords.W, point.X, point.Y); lock (decompressed.Output) decompressed.Output.SetPixel(point.X, point.Y, gradedColor); if(debug) lock (decompressed.DebugOutput) decompressed.DebugOutput.SetPixel(point.X, point.Y, Color.FromArgb((byte)(coords.U * 255), (byte)(coords.V * 255), (byte)(coords.W * 255))); } }); return decompressed; }