├── .gitattributes ├── .gitignore ├── LICENSE.md ├── README.md ├── Triangle.NET.md ├── Triangle.dll ├── Trigrad.sln ├── Trigrad ├── ColorGraders │ ├── AverageGrader.cs │ ├── BarycentricGrader.cs │ ├── BlindDitherGrader.cs │ ├── DitherGrader.cs │ ├── FillGrader.cs │ ├── IGrader.cs │ ├── TopGrader.cs │ └── TriGrader.cs ├── DataTypes │ ├── Barycentric.cs │ ├── Calculation.cs │ ├── Compression │ │ ├── FileCompression.cs │ │ ├── FileDecompression.cs │ │ └── TrigradCompressed.cs │ ├── DrawPoint.cs │ ├── FrequencyTable.cs │ ├── Sample.cs │ ├── SampleTri.cs │ ├── TrigradDecompressed.cs │ └── TrigradOptions.cs ├── Extensions.cs ├── Filters │ ├── AreaFilter.cs │ ├── AverageFilter.cs │ ├── Filter.cs │ ├── GridFilter.cs │ ├── MedianFilter.cs │ └── NoiseFilter.cs ├── GPUT.c ├── GPUT.cs ├── MeshBuilder.cs ├── Properties │ └── AssemblyInfo.cs ├── Renderers │ ├── CenterFill.cs │ ├── GradeFill.cs │ ├── IFill.cs │ └── ShapeFill.cs ├── TriangleRasterisation.cs ├── Trigrad.csproj ├── TrigradCompressor.cs ├── TrigradDecompressor.cs ├── TrigradOptimiser.cs └── packages.config └── TrigradTesting ├── App.config ├── Program.cs ├── Properties └── AssemblyInfo.cs ├── TrigradTesting.csproj └── packages.config /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # Roslyn cache directories 20 | *.ide/ 21 | 22 | # MSTest test Results 23 | [Tt]est[Rr]esult*/ 24 | [Bb]uild[Ll]og.* 25 | 26 | #NUNIT 27 | *.VisualState.xml 28 | TestResult.xml 29 | 30 | # Build Results of an ATL Project 31 | [Dd]ebugPS/ 32 | [Rr]eleasePS/ 33 | dlldata.c 34 | 35 | *_i.c 36 | *_p.c 37 | *_i.h 38 | *.ilk 39 | *.meta 40 | *.obj 41 | *.pch 42 | *.pdb 43 | *.pgc 44 | *.pgd 45 | *.rsp 46 | *.sbr 47 | *.tlb 48 | *.tli 49 | *.tlh 50 | *.tmp 51 | *.tmp_proj 52 | *.log 53 | *.vspscc 54 | *.vssscc 55 | .builds 56 | *.pidb 57 | *.svclog 58 | *.scc 59 | 60 | # Chutzpah Test files 61 | _Chutzpah* 62 | 63 | # Visual C++ cache files 64 | ipch/ 65 | *.aps 66 | *.ncb 67 | *.opensdf 68 | *.sdf 69 | *.cachefile 70 | 71 | # Visual Studio profiler 72 | *.psess 73 | *.vsp 74 | *.vspx 75 | 76 | # TFS 2012 Local Workspace 77 | $tf/ 78 | 79 | # Guidance Automation Toolkit 80 | *.gpState 81 | 82 | # ReSharper is a .NET coding add-in 83 | _ReSharper*/ 84 | *.[Rr]e[Ss]harper 85 | *.DotSettings.user 86 | 87 | # JustCode is a .NET coding addin-in 88 | .JustCode 89 | 90 | # TeamCity is a build add-in 91 | _TeamCity* 92 | 93 | # DotCover is a Code Coverage Tool 94 | *.dotCover 95 | 96 | # NCrunch 97 | _NCrunch_* 98 | .*crunch*.local.xml 99 | 100 | # MightyMoose 101 | *.mm.* 102 | AutoTest.Net/ 103 | 104 | # Web workbench (sass) 105 | .sass-cache/ 106 | 107 | # Installshield output folder 108 | [Ee]xpress/ 109 | 110 | # DocProject is a documentation generator add-in 111 | DocProject/buildhelp/ 112 | DocProject/Help/*.HxT 113 | DocProject/Help/*.HxC 114 | DocProject/Help/*.hhc 115 | DocProject/Help/*.hhk 116 | DocProject/Help/*.hhp 117 | DocProject/Help/Html2 118 | DocProject/Help/html 119 | 120 | # Click-Once directory 121 | publish/ 122 | 123 | # Publish Web Output 124 | *.[Pp]ublish.xml 125 | *.azurePubxml 126 | ## TODO: Comment the next line if you want to checkin your 127 | ## web deploy settings but do note that will include unencrypted 128 | ## passwords 129 | #*.pubxml 130 | 131 | # NuGet Packages Directory 132 | packages/* 133 | ## TODO: If the tool you use requires repositories.config 134 | ## uncomment the next line 135 | #!packages/repositories.config 136 | 137 | # Enable "build/" folder in the NuGet Packages folder since 138 | # NuGet packages use it for MSBuild targets. 139 | # This line needs to be after the ignore of the build folder 140 | # (and the packages folder if the line above has been uncommented) 141 | !packages/build/ 142 | 143 | # Windows Azure Build Output 144 | csx/ 145 | *.build.csdef 146 | 147 | # Windows Store app package directory 148 | AppPackages/ 149 | 150 | # Others 151 | sql/ 152 | *.Cache 153 | ClientBin/ 154 | [Ss]tyle[Cc]op.* 155 | ~$* 156 | *~ 157 | *.dbmdl 158 | *.dbproj.schemaview 159 | *.pfx 160 | *.publishsettings 161 | node_modules/ 162 | 163 | # RIA/Silverlight projects 164 | Generated_Code/ 165 | 166 | # Backup & report files from converting an old project file 167 | # to a newer Visual Studio version. Backup files are not needed, 168 | # because we have git ;-) 169 | _UpgradeReport_Files/ 170 | Backup*/ 171 | UpgradeLog*.XML 172 | UpgradeLog*.htm 173 | 174 | # SQL Server files 175 | *.mdf 176 | *.ldf 177 | 178 | # Business Intelligence projects 179 | *.rdl.data 180 | *.bim.layout 181 | *.bim_*.settings 182 | 183 | # Microsoft Fakes 184 | FakesAssemblies/ 185 | 186 | # LightSwitch generated files 187 | GeneratedArtifacts/ 188 | _Pvt_Extensions/ 189 | ModelManifest.xml -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 ruarai 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Trigrad 2 | Triangle Gradient Image Compression 3 | See http://ruarai.github.io/Trigrad/ for a rundown of the algorithm. 4 | 5 | ## Requirements 6 | ### Triangle 7 | Trigrad uses the Triangle.NET library for generating the triangle mesh. The DLL file has been included under the MIT license. 8 | https://triangle.codeplex.com/license 9 | ### AForge.NET 10 | Trigrad uses the AForge.NET library for edge detection. AForge.NET can be downloaded automatically through NuGet. 11 | -------------------------------------------------------------------------------- /Triangle.NET.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Christian Woltering 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Triangle.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruarai/Trigrad/e07ada125cada6e930c910fd9db4bd2d8a23b6ff/Triangle.dll -------------------------------------------------------------------------------- /Trigrad.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Trigrad", "Trigrad\Trigrad.csproj", "{3E743CD4-6FB3-43DA-AA7B-F4B2EEEAFA85}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrigradTesting", "TrigradTesting\TrigradTesting.csproj", "{E984EACE-46E7-485E-8A1C-FCB5884698F0}" 9 | EndProject 10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{14766101-0358-464C-AC12-5539CAB2D697}" 11 | ProjectSection(SolutionItems) = preProject 12 | LICENSE.md = LICENSE.md 13 | Triangle.NET.md = Triangle.NET.md 14 | EndProjectSection 15 | EndProject 16 | Global 17 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 18 | Debug|Any CPU = Debug|Any CPU 19 | Debug|x64 = Debug|x64 20 | Release|Any CPU = Release|Any CPU 21 | Release|x64 = Release|x64 22 | EndGlobalSection 23 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 24 | {3E743CD4-6FB3-43DA-AA7B-F4B2EEEAFA85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {3E743CD4-6FB3-43DA-AA7B-F4B2EEEAFA85}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {3E743CD4-6FB3-43DA-AA7B-F4B2EEEAFA85}.Debug|x64.ActiveCfg = Debug|Any CPU 27 | {3E743CD4-6FB3-43DA-AA7B-F4B2EEEAFA85}.Debug|x64.Build.0 = Debug|Any CPU 28 | {3E743CD4-6FB3-43DA-AA7B-F4B2EEEAFA85}.Release|Any CPU.ActiveCfg = Release|Any CPU 29 | {3E743CD4-6FB3-43DA-AA7B-F4B2EEEAFA85}.Release|Any CPU.Build.0 = Release|Any CPU 30 | {3E743CD4-6FB3-43DA-AA7B-F4B2EEEAFA85}.Release|x64.ActiveCfg = Release|Any CPU 31 | {E984EACE-46E7-485E-8A1C-FCB5884698F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {E984EACE-46E7-485E-8A1C-FCB5884698F0}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {E984EACE-46E7-485E-8A1C-FCB5884698F0}.Debug|x64.ActiveCfg = Debug|x64 34 | {E984EACE-46E7-485E-8A1C-FCB5884698F0}.Debug|x64.Build.0 = Debug|x64 35 | {E984EACE-46E7-485E-8A1C-FCB5884698F0}.Release|Any CPU.ActiveCfg = Release|Any CPU 36 | {E984EACE-46E7-485E-8A1C-FCB5884698F0}.Release|Any CPU.Build.0 = Release|Any CPU 37 | {E984EACE-46E7-485E-8A1C-FCB5884698F0}.Release|x64.ActiveCfg = Release|x64 38 | {E984EACE-46E7-485E-8A1C-FCB5884698F0}.Release|x64.Build.0 = Release|x64 39 | EndGlobalSection 40 | GlobalSection(SolutionProperties) = preSolution 41 | HideSolutionNode = FALSE 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /Trigrad/ColorGraders/AverageGrader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | using Trigrad.DataTypes; 9 | 10 | namespace Trigrad.ColorGraders 11 | { 12 | /// Color grader that fills the triangle with an average of the three vertice samples. 13 | public class AverageGrader : IGrader 14 | { 15 | /// Produces a color from the specified coordinates and colors. 16 | public Pixel Grade(Sample u, Sample v, Sample w, DrawPoint p) 17 | { 18 | int R = u.Color.R + v.Color.R + w.Color.R; 19 | int G = u.Color.G + v.Color.G + w.Color.G; 20 | int B = u.Color.B + v.Color.B + w.Color.B; 21 | 22 | return new Pixel((byte) (R/3), (byte) (G/3), (byte) (B/3)); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Trigrad/ColorGraders/BarycentricGrader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | using Trigrad.DataTypes; 9 | 10 | namespace Trigrad.ColorGraders 11 | { 12 | /// Color grader that fills a triangle with a triangle gradient. 13 | public class BarycentricGrader : IGrader 14 | { 15 | /// Produces a color from the specified coordinates and colors. 16 | public Pixel Grade(Sample u, Sample v, Sample w, DrawPoint p) 17 | { 18 | byte R = (byte)(u.Color.R * p.BarycentricCoordinates.U + v.Color.R * p.BarycentricCoordinates.V + w.Color.R * p.BarycentricCoordinates.W); 19 | byte G = (byte)(u.Color.G * p.BarycentricCoordinates.U + v.Color.G * p.BarycentricCoordinates.V + w.Color.G * p.BarycentricCoordinates.W); 20 | byte B = (byte)(u.Color.B * p.BarycentricCoordinates.U + v.Color.B * p.BarycentricCoordinates.V + w.Color.B * p.BarycentricCoordinates.W); 21 | 22 | return new Pixel(R, G, B); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Trigrad/ColorGraders/BlindDitherGrader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | using Trigrad.DataTypes; 9 | 10 | namespace Trigrad.ColorGraders 11 | { 12 | /// Color grader that fills a triangle with a dithering that ignores the barycentric coordinates. 13 | public class BlindDitherGrader : IGrader 14 | { 15 | /// Produces a color from the specified coordinates and colors. 16 | public Pixel Grade(Sample u, Sample v, Sample w, DrawPoint p) 17 | { 18 | int val = (p.Point.X + p.Point.Y) % 3; 19 | if (val == 0) 20 | return u.Color; 21 | if (val == 1) 22 | return v.Color; 23 | return w.Color; 24 | 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Trigrad/ColorGraders/DitherGrader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | using Trigrad.DataTypes; 9 | 10 | namespace Trigrad.ColorGraders 11 | { 12 | /// Color grader that fills a triangle with a dithered output. 13 | public class DitherGrader : IGrader 14 | { 15 | private static Random r = new Random(); 16 | 17 | /// Produces a color from the specified coordinates and colors. 18 | public Pixel Grade(Sample u, Sample v, Sample w, DrawPoint p) 19 | { 20 | if (p.BarycentricCoordinates.U >= 0.9) 21 | return u.Color; 22 | if (p.BarycentricCoordinates.V >= 0.9) 23 | return v.Color; 24 | if (p.BarycentricCoordinates.W >= 0.9) 25 | return w.Color; 26 | 27 | 28 | int valU = (int)(p.Point.X + p.Point.Y + p.BarycentricCoordinates.U + r.Next(0, 4)) % 4; 29 | int valV = (int)(p.Point.X + p.Point.Y + p.BarycentricCoordinates.V + r.Next(0, 4)) % 4; 30 | int valW = (int)(p.Point.X + p.Point.Y + p.BarycentricCoordinates.W + r.Next(0, 4)) % 4; 31 | 32 | if (p.BarycentricCoordinates.U >= p.BarycentricCoordinates.V && p.BarycentricCoordinates.U >= p.BarycentricCoordinates.W) 33 | return ditherFurther(u.Color, v.Color, w.Color, valU); 34 | if (p.BarycentricCoordinates.V >= p.BarycentricCoordinates.W) 35 | return ditherFurther(v.Color, w.Color, w.Color, valV); 36 | 37 | return ditherFurther(w.Color, v.Color, u.Color, valW); 38 | } 39 | private static Pixel ditherFurther(Pixel a, Pixel b, Pixel c, int val) 40 | { 41 | switch (val) 42 | { 43 | case 0: 44 | return a; 45 | case 1: 46 | return b; 47 | case 2: 48 | return a; 49 | case 3: 50 | return c; 51 | default: 52 | return a; 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Trigrad/ColorGraders/FillGrader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | using Trigrad.DataTypes; 9 | 10 | namespace Trigrad.ColorGraders 11 | { 12 | /// Color grader that fills a triangle with each point's nearest color. 13 | public class FillGrader : IGrader 14 | { 15 | /// Produces a color from the specified coordinates and colors. 16 | public Pixel Grade(Sample u, Sample v, Sample w, DrawPoint p) 17 | { 18 | if (p.BarycentricCoordinates.U >= p.BarycentricCoordinates.V && p.BarycentricCoordinates.U >= p.BarycentricCoordinates.W) 19 | return u.Color; 20 | if (p.BarycentricCoordinates.V >= p.BarycentricCoordinates.W) 21 | return v.Color; 22 | return w.Color; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Trigrad/ColorGraders/IGrader.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | using PixelMapSharp; 3 | using Trigrad.DataTypes; 4 | 5 | namespace Trigrad.ColorGraders 6 | { 7 | /// Interface for producing color graders. 8 | public interface IGrader 9 | { 10 | /// Base method for color grading. 11 | Pixel Grade(Sample u, Sample v, Sample w, DrawPoint p); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Trigrad/ColorGraders/TopGrader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | using Trigrad.DataTypes; 9 | 10 | namespace Trigrad.ColorGraders 11 | { 12 | /// Color grader that fills the triangle with the highest valued vertice sample. 13 | public class TopGrader : IGrader 14 | { 15 | /// Produces a color from the specified coordinates and colors. 16 | public Pixel Grade(Sample u, Sample v, Sample w, DrawPoint p) 17 | { 18 | int uSum = u.Color.R + u.Color.G + u.Color.B; 19 | int vSum = v.Color.R + v.Color.G + v.Color.B; 20 | int wSum = w.Color.R + w.Color.G + w.Color.B; 21 | 22 | if (uSum > vSum && uSum > wSum) 23 | { 24 | return u.Color; 25 | } 26 | else if (vSum > wSum) 27 | return v.Color; 28 | else 29 | return w.Color; 30 | 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Trigrad/ColorGraders/TriGrader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | using Trigrad.DataTypes; 9 | 10 | namespace Trigrad.ColorGraders 11 | { 12 | /// Color grader that fills the triangle with the first sample color. 13 | public class TriGrader : IGrader 14 | { 15 | /// Produces a color from the specified coordinates and colors. 16 | public Pixel Grade(Sample u, Sample v, Sample w, DrawPoint p) 17 | { 18 | return u.Color; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Trigrad/DataTypes/Barycentric.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace Trigrad.DataTypes 4 | { 5 | public static class Barycentric 6 | { 7 | public static BarycentricCoordinates GetCoordinates(Point Pp, Point Pa, Point Pb, Point Pc) 8 | { 9 | double[] v0 = { Pb.X - Pa.X, Pb.Y - Pa.Y }; 10 | double[] v1 = { Pc.X - Pa.X, Pc.Y - Pa.Y }; 11 | double[] v2 = { Pp.X - Pa.X, Pp.Y - Pa.Y }; 12 | double d00 = dotProduct(v0, v0); 13 | double d01 = dotProduct(v0, v1); 14 | double d11 = dotProduct(v1, v1); 15 | double d20 = dotProduct(v2, v0); 16 | double d21 = dotProduct(v2, v1); 17 | double denom = d00 * d11 - d01 * d01; 18 | double v = ((d11 * d20 - d01 * d21) / denom); 19 | double w = ((d00 * d21 - d01 * d20) / denom); 20 | double u = (1.0f - v - w); 21 | 22 | return new BarycentricCoordinates(u, v, w); 23 | } 24 | 25 | public static bool ValidCoords(BarycentricCoordinates coords) 26 | { 27 | return coords.U >= 0 && coords.V >= 0 && coords.W >= 0; 28 | } 29 | 30 | private static double dotProduct(double[] vec1, double[] vec2) 31 | { 32 | return vec1[0]*vec2[0] + vec1[1]*vec2[1]; 33 | } 34 | } 35 | public struct BarycentricCoordinates 36 | { 37 | public BarycentricCoordinates(double u, double v, double w) 38 | { 39 | U = u; 40 | V = v; 41 | W = w; 42 | } 43 | public double U; 44 | public double V; 45 | public double W; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Trigrad/DataTypes/Calculation.cs: -------------------------------------------------------------------------------- 1 | using System.Drawing; 2 | 3 | namespace Trigrad.DataTypes 4 | { 5 | public class Calculation 6 | { 7 | public Point P; 8 | 9 | public Point A; 10 | public Point B; 11 | public Point C; 12 | 13 | public BarycentricCoordinates Coords; 14 | 15 | public SampleTri Tri; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Trigrad/DataTypes/Compression/FileCompression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using ICSharpCode.SharpZipLib.BZip2; 9 | 10 | namespace Trigrad.DataTypes.Compression 11 | { 12 | public partial class TrigradCompressed 13 | { 14 | /// Saves a TrigradCompressed image to a stream. 15 | public void Save(Stream s) 16 | { 17 | using (BZip2OutputStream zipper = new BZip2OutputStream(s)) 18 | using (BinaryWriter writer = new BinaryWriter(zipper)) 19 | { 20 | writer.Write((ushort)Width); 21 | writer.Write((ushort)Height); 22 | 23 | var samples = Mesh.SelectMany(t => t.Samples).Distinct().ToList(); 24 | 25 | writer.Write((uint)samples.Count); 26 | 27 | var sorted = samples.OrderBy(kvp => kvp.Point.X * ushort.MaxValue + kvp.Point.Y).ToList(); 28 | foreach (var sample in sorted) 29 | writer.Write((ushort)sample.Point.X); 30 | foreach (var sample in sorted) 31 | writer.Write((ushort)sample.Point.Y); 32 | 33 | foreach (var sample in sorted) 34 | writer.Write(sample.Color.R); 35 | foreach (var sample in sorted) 36 | writer.Write(sample.Color.G); 37 | foreach (var sample in sorted) 38 | writer.Write(sample.Color.B); 39 | 40 | writer.Write((uint)Mesh.Count); 41 | 42 | foreach (var tri in Mesh) 43 | writer.Write(sorted.FindIndex(p => p.Point == tri.U.Point)); 44 | foreach (var tri in Mesh) 45 | writer.Write(sorted.FindIndex(p => p.Point == tri.V.Point)); 46 | foreach (var tri in Mesh) 47 | writer.Write(sorted.FindIndex(p => p.Point == tri.W.Point)); 48 | 49 | 50 | writer.Flush(); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Trigrad/DataTypes/Compression/FileDecompression.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using ICSharpCode.SharpZipLib.BZip2; 9 | using PixelMapSharp; 10 | 11 | namespace Trigrad.DataTypes.Compression 12 | { 13 | public partial class TrigradCompressed 14 | { 15 | /// Loads a TrigradCompressed image from a stream. 16 | public TrigradCompressed(Stream s) 17 | { 18 | using (BZip2InputStream dezipper = new BZip2InputStream(s)) 19 | using (BinaryReader reader = new BinaryReader(dezipper)) 20 | { 21 | Width = reader.ReadUInt16(); 22 | Height = reader.ReadUInt16(); 23 | 24 | uint count = reader.ReadUInt32(); 25 | 26 | Point[] points = new Point[count]; 27 | 28 | for (int i = 0; i < count; i++) 29 | points[i].X = reader.ReadUInt16(); 30 | for (int i = 0; i < count; i++) 31 | points[i].Y = reader.ReadUInt16(); 32 | 33 | ColorStruct[] colors = new ColorStruct[count]; 34 | 35 | for (int i = 0; i < count; i++) 36 | colors[i].R = reader.ReadByte(); 37 | for (int i = 0; i < count; i++) 38 | colors[i].G = reader.ReadByte(); 39 | for (int i = 0; i < count; i++) 40 | colors[i].B = reader.ReadByte(); 41 | 42 | for (int i = 0; i < count; i++) 43 | { 44 | SampleTable.Add(points[i],colors[i].Color); 45 | } 46 | 47 | uint meshCount = reader.ReadUInt32(); 48 | 49 | SampleTri[] tris = new SampleTri[meshCount]; 50 | 51 | 52 | for (int i = 0; i < meshCount; i++) 53 | tris[i] = new SampleTri(); 54 | 55 | for (int i = 0; i < meshCount; i++) 56 | tris[i].U = new Sample(points[reader.ReadInt32()], new Pixel(Color.Black)); 57 | 58 | for (int i = 0; i < meshCount; i++) 59 | tris[i].V = new Sample(points[reader.ReadInt32()], new Pixel(Color.Black)); 60 | 61 | for (int i = 0; i < meshCount; i++) 62 | tris[i].W = new Sample(points[reader.ReadInt32()], new Pixel(Color.Black)); 63 | 64 | foreach (var tri in tris) 65 | { 66 | tri.U.Color = SampleTable[tri.U.Point]; 67 | tri.V.Color = SampleTable[tri.V.Point]; 68 | tri.W.Color = SampleTable[tri.W.Point]; 69 | } 70 | 71 | Mesh = tris.ToList(); 72 | } 73 | } 74 | 75 | internal struct ColorStruct 76 | { 77 | public byte R; 78 | public byte G; 79 | public byte B; 80 | 81 | public Pixel Color { get { return new Pixel(R, G, B); } } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Trigrad/DataTypes/Compression/TrigradCompressed.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Drawing; 3 | using System.IO; 4 | using System.IO.Compression; 5 | using System.Linq; 6 | using System.Security.Cryptography.X509Certificates; 7 | using ICSharpCode.SharpZipLib.BZip2; 8 | using PixelMapSharp; 9 | using TriangleNet; 10 | 11 | namespace Trigrad.DataTypes.Compression 12 | { 13 | /// The TrigradCompressed form of a bitmap. 14 | public partial class TrigradCompressed 15 | { 16 | /// Constructs a TrigradCompressed without any initial data. 17 | public TrigradCompressed() 18 | { 19 | 20 | } 21 | /// A dictionary of sampled points to their corresponding colors. 22 | public Dictionary SampleTable = new Dictionary(); 23 | 24 | public List Mesh = new List(); 25 | /// The width of the bitmap. 26 | public int Width; 27 | /// The height of the bitmap. 28 | public int Height; 29 | /// Provides a visualisation of the SampleTable. 30 | public PixelMap DebugVisualisation() 31 | { 32 | PixelMap map = new PixelMap(Width, Height); 33 | 34 | for (int x = 0; x < Width; x++) 35 | { 36 | for (int y = 0; y < Height; y++) 37 | { 38 | map[x, y] = new Pixel(10, 10, 10); 39 | } 40 | } 41 | 42 | foreach (var value in SampleTable) 43 | { 44 | Point p = value.Key; 45 | 46 | map[p]= value.Value; 47 | } 48 | return map; 49 | } 50 | 51 | public PixelMap MeshOutput(PixelMap original) 52 | { 53 | return Mesh.DrawMesh(Width, Height); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Trigrad/DataTypes/DrawPoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Trigrad.DataTypes 9 | { 10 | public class DrawPoint 11 | { 12 | public DrawPoint(BarycentricCoordinates coords, Point p) 13 | { 14 | BarycentricCoordinates = coords; 15 | Point = p; 16 | } 17 | 18 | public BarycentricCoordinates BarycentricCoordinates; 19 | public Point Point; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Trigrad/DataTypes/FrequencyTable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Drawing; 3 | using PixelMapSharp; 4 | 5 | namespace Trigrad.DataTypes 6 | { 7 | /// A frequency table defining how likely a sample will be formed during a TrigradCompression. 8 | public class FrequencyTable 9 | { 10 | /// The underlying 2D frequency table, with values lying from 0 to 1. 11 | public double[,] Table; 12 | 13 | /// Constructs a frequency table using sobel edge detection. 14 | public FrequencyTable(PixelMap pixelmap,double power = 1.7) 15 | { 16 | Table = sobelFilter(pixelmap); 17 | 18 | for (int x = 0; x < Table.GetLength(0); x++) 19 | { 20 | for (int y = 0; y < Table.GetLength(1); y++) 21 | { 22 | Table[x, y] = Math.Pow(Table[x, y], power); 23 | } 24 | } 25 | } 26 | 27 | /// The sum of the FrequencyTable. 28 | public double Sum 29 | { 30 | get 31 | { 32 | double chanceSum = 0; 33 | for (int x = 0; x < Table.GetLength(0); x++) 34 | { 35 | for (int y = 0; y < Table.GetLength(1); y++) 36 | { 37 | chanceSum += Table[x, y]; 38 | } 39 | } 40 | return chanceSum; 41 | } 42 | } 43 | 44 | /// Constructs a frequency table using a specified table of values. 45 | public FrequencyTable(double[,] table) 46 | { 47 | Table = table; 48 | } 49 | 50 | private double[,] sobelFilter(PixelMap map) 51 | { 52 | double[,] output = new double[map.Width, map.Height]; 53 | 54 | double[,] kernelX = 55 | { 56 | {-1,0,1 }, 57 | {-2,0,2 }, 58 | {-1,0,1 } 59 | }; 60 | 61 | double[,] kernelY = 62 | { 63 | {-1,-2,-1}, 64 | {0,0,0}, 65 | {1,2,1} 66 | }; 67 | 68 | 69 | double[,] xFiltered = sobelPass(map, kernelX); 70 | double[,] yFiltered = sobelPass(map, kernelY); 71 | 72 | for (int x = 0; x < map.Width; x++) 73 | { 74 | for (int y = 0; y < map.Height; y++) 75 | { 76 | double xVal = xFiltered[x, y]; 77 | double yVal = yFiltered[x, y]; 78 | 79 | output[x, y] = Math.Sqrt(xVal * xVal + yVal * yVal); 80 | } 81 | } 82 | 83 | return output; 84 | } 85 | 86 | private double[,] sobelPass(PixelMap map, double[,] kernel) 87 | { 88 | double[,] output = new double[map.Width, map.Height]; 89 | 90 | for (int x = 0; x < map.Width; x++) 91 | { 92 | for (int y = 0; y < map.Height; y++) 93 | { 94 | double sum = 0; 95 | for (int u = 0; u < 3; u++) 96 | { 97 | for (int v = 0; v < 3; v++) 98 | { 99 | Point samplePoint = new Point(x - 1 + u, y - 1 + v); 100 | 101 | if (map.Inside(samplePoint)) 102 | { 103 | double kVal = kernel[u, v]; 104 | double imgVal = map[samplePoint].Lightness; 105 | 106 | sum += kVal * imgVal; 107 | } 108 | else 109 | { 110 | sum += 0.5; 111 | } 112 | } 113 | } 114 | output[x, y] = sum; 115 | } 116 | } 117 | return output; 118 | } 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /Trigrad/DataTypes/Sample.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | 9 | namespace Trigrad.DataTypes 10 | { 11 | 12 | public class Sample 13 | { 14 | public Sample(Point p, Pixel c) 15 | { 16 | Point = p; 17 | Color = c; 18 | } 19 | 20 | public Point Point; 21 | public Pixel Color; 22 | public Pixel NewColor; 23 | 24 | public bool Optimised = true; 25 | 26 | public List Triangles = new List(); 27 | 28 | public IEnumerable Samples 29 | { 30 | get { return Triangles.SelectMany(t => t.Samples); } 31 | } 32 | 33 | public List Points { get { return Triangles.SelectMany(t => t.Points).ToList(); } } 34 | 35 | public List DepthNeighbours(int depth) 36 | { 37 | return depthSelect(this, depth); 38 | } 39 | 40 | private static List depthSelect(Sample sample, int depth, List parents = null) 41 | { 42 | if (depth <= 0) 43 | return new List(); 44 | 45 | List samples = new List(); 46 | foreach (var child in sample.Samples) 47 | { 48 | if (parents != null && parents.Contains(child)) 49 | continue; 50 | 51 | samples.Add(child); 52 | var result = depthSelect(child, depth - 1, samples); 53 | 54 | samples.AddRange(result); 55 | } 56 | return samples; 57 | } 58 | 59 | 60 | public override bool Equals(object obj) 61 | { 62 | // If parameter is null return false. 63 | if (obj == null) 64 | { 65 | return false; 66 | } 67 | 68 | // If parameter cannot be cast to Point return false. 69 | Sample s = obj as Sample; 70 | if (s == null) 71 | return false; 72 | 73 | // Return true if the fields match: 74 | return (Point == s.Point); 75 | } 76 | public override int GetHashCode() 77 | { 78 | return Point.GetHashCode(); 79 | } 80 | 81 | public bool OnEdge(int width, int height) 82 | { 83 | return (Point.X == 0 || Point.Y == 0 || 84 | Point.Y == width - 1 || Point.Y == height - 1); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /Trigrad/DataTypes/SampleTri.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | using TriangleNet.Data; 9 | using TriangleNet.Geometry; 10 | using Point = System.Drawing.Point; 11 | 12 | namespace Trigrad.DataTypes 13 | { 14 | public class SampleTri 15 | { 16 | public SampleTri() 17 | { 18 | 19 | } 20 | 21 | public SampleTri(ITriangle t) 22 | { 23 | Vertex u = t.GetVertex(0); 24 | Vertex v = t.GetVertex(1); 25 | Vertex w = t.GetVertex(2); 26 | 27 | 28 | U = new Sample(u.Point(), new Pixel(Color.Black)); 29 | V = new Sample(v.Point(), new Pixel(Color.Black)); 30 | W = new Sample(w.Point(), new Pixel(Color.Black)); 31 | } 32 | 33 | public IEnumerable Samples 34 | { 35 | get { return new[] {U, V, W}.ToList(); } 36 | } 37 | 38 | public List Points = new List(); 39 | 40 | public Sample U; 41 | public Sample V; 42 | public Sample W; 43 | 44 | public bool Busy; 45 | 46 | public Point CenterPoint 47 | { 48 | get { return new Point((int) Samples.Average(s => s.Point.X), (int) Samples.Average(s => s.Point.Y)); } 49 | } 50 | public Pixel CenterColor; 51 | 52 | public List TriangleNeighbours = new List(); 53 | public List SampleTriNeighbours = new List(); 54 | 55 | public override string ToString() 56 | { 57 | return string.Format("({0}),({1}),({2})", U.Point, V.Point, W.Point); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Trigrad/DataTypes/TrigradDecompressed.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Drawing; 3 | using PixelMapSharp; 4 | using TriangleNet; 5 | 6 | namespace Trigrad.DataTypes 7 | { 8 | /// The results of a TrigradDecompression, containing both the output and debug bitmaps. 9 | public class TrigradDecompressed 10 | { 11 | /// Constructor for a TrigradDecompressed object, defining the width and height of output bitmaps. 12 | public TrigradDecompressed(int width, int height) 13 | { 14 | Output = new PixelMap(width, height); 15 | DebugOutput = new PixelMap(width, height); 16 | } 17 | /// The decompressed output bitmap. 18 | public PixelMap Output; 19 | /// The debug output bitmap, showing calculated barycentric coordinates. 20 | public PixelMap DebugOutput; 21 | 22 | internal List Mesh; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Trigrad/DataTypes/TrigradOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Trigrad.ColorGraders; 7 | using Trigrad.Renderers; 8 | 9 | namespace Trigrad.DataTypes 10 | { 11 | /// Options for the usage of the TrigradCompressor. 12 | public class TrigradOptions 13 | { 14 | /// Constructs a TrigradOptions with a random seed. 15 | public TrigradOptions() 16 | { 17 | Random = new Random(); 18 | } 19 | 20 | /// Constructs a TrigradOptions with a specified seed. 21 | public TrigradOptions(int seed) 22 | { 23 | Random = new Random(seed); 24 | } 25 | 26 | /// The goal number of samples to be achieved. 27 | public int SampleCount = 1000; 28 | 29 | /// The frequency table providing the TrigradCompressor values regarding the chance a pixel will be sampled. 30 | public FrequencyTable FrequencyTable = null; 31 | 32 | /// The random number generator to be used by the TrigradCompressor. 33 | public Random Random; 34 | 35 | /// The number of resamples. 36 | public int Resamples = 25; 37 | 38 | /// The number of iterations performed during minimisation. 39 | public int Iterations = 4; 40 | 41 | public IFill Renderer; 42 | 43 | public bool ResampleColors = true; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Trigrad/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using TriangleNet.Data; 4 | using System.Drawing; 5 | using PixelMapSharp; 6 | using TriangleNet; 7 | using Trigrad.DataTypes; 8 | 9 | namespace Trigrad 10 | { 11 | public static class Extensions 12 | { 13 | public static Point Point(this Vertex t) 14 | { 15 | return new Point((int)t.X, (int)t.Y); 16 | } 17 | 18 | public static PixelMap DrawMesh(this List m,int width,int height) 19 | { 20 | Bitmap b = new Bitmap(width,height); 21 | Graphics g = Graphics.FromImage(b); 22 | 23 | 24 | foreach (var sampleTri in m) 25 | { 26 | g.DrawLine(new Pen(sampleTri.U.Color.Color), sampleTri.U.Point, sampleTri.V.Point); 27 | g.DrawLine(new Pen(sampleTri.U.Color.Color), sampleTri.V.Point, sampleTri.W.Point); 28 | g.DrawLine(new Pen(sampleTri.U.Color.Color), sampleTri.W.Point, sampleTri.U.Point); 29 | } 30 | 31 | return new PixelMap(b); 32 | } 33 | 34 | public static void Shuffle(this IList list,Random r) 35 | { 36 | int n = list.Count; 37 | while (n > 1) 38 | { 39 | n--; 40 | int k = r.Next(n + 1); 41 | T value = list[k]; 42 | list[k] = list[n]; 43 | list[n] = value; 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Trigrad/Filters/AreaFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Trigrad.DataTypes; 8 | 9 | namespace Trigrad.Filters 10 | { 11 | public class AreaFilter : ITriGradFilter 12 | { 13 | public AreaFilter(int depth) 14 | { 15 | Depth = depth; 16 | } 17 | 18 | public int Depth; 19 | 20 | public void Run(List mesh) 21 | { 22 | double s = 2; 23 | Parallel.ForEach(mesh.SelectMany(m => m.Samples).ToList(), sample => 24 | { 25 | Point p = sample.Point; 26 | 27 | sample.Point = new Point(p.X + (int)(Math.Sin(p.X / s) * s), p.Y); 28 | }); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Trigrad/Filters/AverageFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | using Trigrad.DataTypes; 9 | 10 | namespace Trigrad.Filters 11 | { 12 | public class AverageFilter : ITriGradFilter 13 | { 14 | public AverageFilter(int depth) 15 | { 16 | Depth = depth; 17 | } 18 | 19 | public int Depth; 20 | 21 | public void Run(List mesh) 22 | { 23 | Parallel.ForEach(mesh.SelectMany(m => m.Samples).ToList(), sample => 24 | { 25 | var neighbours = sample.DepthNeighbours(Depth); 26 | 27 | var colors = neighbours.Select(s => s.Color); 28 | 29 | sample.Color = new Pixel((int)colors.Average(c => c.R), (int)colors.Average(c => c.G),(int)colors.Average(c => c.B)); 30 | }); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Trigrad/Filters/Filter.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Trigrad.DataTypes; 3 | 4 | namespace Trigrad.Filters 5 | { 6 | interface ITriGradFilter 7 | { 8 | void Run(List Mesh); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Trigrad/Filters/GridFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Trigrad.DataTypes; 8 | 9 | namespace Trigrad.Filters 10 | { 11 | public class GridFilter : ITriGradFilter 12 | { 13 | public GridFilter(int size) 14 | { 15 | Size = size; 16 | } 17 | 18 | public int Size; 19 | public void Run(List mesh) 20 | { 21 | Parallel.ForEach(mesh.SelectMany(m => m.Samples).Distinct().ToList(), sample => 22 | { 23 | sample.Point = new Point(sample.Point.X - sample.Point.X % Size, sample.Point.Y - sample.Point.Y % Size); 24 | //sample.Point = new Point(sample.Point.X + (int)(Math.Sin(sample.Point.X + sample.Point.Y) * (Size / 2)), sample.Point.Y + (int)(Math.Sin(sample.Point.X + sample.Point.Y) * (Size / 2))); 25 | }); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Trigrad/Filters/MedianFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Trigrad.DataTypes; 7 | using System.Drawing; 8 | 9 | namespace Trigrad.Filters 10 | { 11 | public class MedianFilter : ITriGradFilter 12 | { 13 | public MedianFilter(int depth) 14 | { 15 | Depth = depth; 16 | } 17 | 18 | public int Depth; 19 | public void Run(List mesh) 20 | { 21 | Parallel.ForEach(mesh.SelectMany(m => m.Samples).Distinct().ToList(), sample => 22 | { 23 | var neighbours = sample.DepthNeighbours(Depth); 24 | 25 | var colors = neighbours.Select(s => s.Color).OrderByDescending(c => c.Saturation).ToList(); 26 | 27 | sample.NewColor = colors.Last(); 28 | }); 29 | Parallel.ForEach(mesh.SelectMany(m => m.Samples).Distinct().ToList(), sample => 30 | { 31 | sample.Color = sample.NewColor; 32 | }); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Trigrad/Filters/NoiseFilter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Trigrad.DataTypes; 7 | 8 | namespace Trigrad.Filters 9 | { 10 | public class NoiseFilter :ITriGradFilter 11 | { 12 | public NoiseFilter(int depth) 13 | { 14 | Depth = depth; 15 | } 16 | 17 | public int Depth; 18 | private static Random r = new Random(); 19 | public void Run(List mesh) 20 | { 21 | Parallel.ForEach(mesh.SelectMany(m => m.Samples).Distinct().ToList(), sample => 22 | { 23 | var neighbours = sample.DepthNeighbours(Depth); 24 | 25 | var colors = neighbours.ToList(); 26 | 27 | sample.Color = colors[r.Next(0, colors.Count)].Color; 28 | }); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Trigrad/GPUT.c: -------------------------------------------------------------------------------- 1 | kernel void Barycentric( 2 | global read_only int2* p_p, 3 | global read_only int2* p_a, 4 | global read_only int2* p_b, 5 | global read_only int2* p_c, 6 | global write_only float4* c, 7 | global write_only int* c_valid) 8 | { 9 | int i = get_global_id(0); 10 | 11 | float v0[] = { p_b[i].x - p_a[i].x, p_b[i].y - p_a[i].y }; 12 | float v1[] = { p_c[i].x - p_a[i].x, p_c[i].y - p_a[i].y }; 13 | float v2[] = { p_p[i].x - p_a[i].x, p_p[i].y - p_a[i].y }; 14 | 15 | float d00 = v0[0] * v0[0] + v0[1] * v0[1]; 16 | float d01 = v0[0] * v1[0] + v0[1] * v1[1]; 17 | float d11 = v1[0] * v1[0] + v1[1] * v1[1]; 18 | float d20 = v2[0] * v0[0] + v2[1] * v0[1]; 19 | float d21 = v2[0] * v1[0] + v2[1] * v1[1]; 20 | 21 | float denom = d00 * d11 - d01 * d01; 22 | 23 | float v = ((d11 * d20 - d01 * d21) / denom); 24 | float w = ((d00 * d21 - d01 * d20) / denom); 25 | float u = (1 - v - w); 26 | 27 | c[i] = (float4)(u, v, w,0); 28 | 29 | if (u >= 0 && v >= 0 && w >= 0) 30 | { 31 | c_valid[i] = 1; 32 | } 33 | else 34 | { 35 | c_valid[i] = 0; 36 | } 37 | } -------------------------------------------------------------------------------- /Trigrad/GPUT.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using Cloo; 5 | using Trigrad.DataTypes; 6 | using Parallel = System.Threading.Tasks.Parallel; 7 | using System.Drawing; 8 | 9 | namespace Trigrad 10 | { 11 | public class GPUT 12 | { 13 | static GPUT() 14 | { 15 | program.Build(new[] { context.Devices[0] }, null, null, IntPtr.Zero); 16 | } 17 | 18 | public static void CalculateMesh(List mesh) 19 | { 20 | List calculations = new List(); 21 | foreach (var sampleTri in mesh) 22 | { 23 | sampleTri.Points.Clear(); 24 | calculations.AddRange(TriangleRasterization.BuildTriCalculations(sampleTri)); 25 | } 26 | 27 | Calculate(calculations); 28 | } 29 | 30 | private static ComputePlatform platform = ComputePlatform.Platforms[0]; 31 | private static ComputeContextPropertyList properties = new ComputeContextPropertyList(platform); 32 | private static ComputeContext context = new ComputeContext(platform.Devices, properties, null, IntPtr.Zero); 33 | 34 | private static ComputeProgram program = new ComputeProgram(context, System.IO.File.ReadAllText("GPUT.c")); 35 | 36 | public static void Calculate(List calculations) 37 | { 38 | Stopwatch s = new Stopwatch(); 39 | s.Start(); 40 | 41 | int count = calculations.Count; 42 | 43 | IntVec2[] p_p = new IntVec2[count]; 44 | 45 | IntVec2[] p_a = new IntVec2[count]; 46 | IntVec2[] p_b = new IntVec2[count]; 47 | IntVec2[] p_c = new IntVec2[count]; 48 | 49 | FloatVec3[] c = new FloatVec3[count]; 50 | 51 | int[] c_valid = new int[count]; 52 | 53 | Parallel.For(0, count, i => 54 | { 55 | var calc = calculations[i]; 56 | 57 | p_p[i] = new IntVec2(calc.P); 58 | p_a[i] = new IntVec2(calc.A); 59 | p_b[i] = new IntVec2(calc.B); 60 | p_c[i] = new IntVec2(calc.C); 61 | }); 62 | 63 | mark(s, "memory init"); 64 | 65 | ComputeBuffer _p_p = new ComputeBuffer(context, ComputeMemoryFlags.ReadOnly | ComputeMemoryFlags.CopyHostPointer, p_p); 66 | 67 | ComputeBuffer _p_a = new ComputeBuffer(context, ComputeMemoryFlags.ReadOnly | ComputeMemoryFlags.CopyHostPointer, p_a); 68 | 69 | ComputeBuffer _p_b = new ComputeBuffer(context, ComputeMemoryFlags.ReadOnly | ComputeMemoryFlags.CopyHostPointer, p_b); 70 | 71 | ComputeBuffer _p_c = new ComputeBuffer(context, ComputeMemoryFlags.ReadOnly | ComputeMemoryFlags.CopyHostPointer, p_c); 72 | 73 | ComputeBuffer _c = new ComputeBuffer(context, ComputeMemoryFlags.WriteOnly, c.Length); 74 | ComputeBuffer _c_valid = new ComputeBuffer(context, ComputeMemoryFlags.WriteOnly, c_valid.Length); 75 | 76 | mark(s, "memory buffer init"); 77 | 78 | ComputeKernel kernel = program.CreateKernel("Barycentric"); 79 | kernel.SetMemoryArgument(0, _p_p); 80 | 81 | kernel.SetMemoryArgument(1, _p_a); 82 | 83 | kernel.SetMemoryArgument(2, _p_b); 84 | 85 | kernel.SetMemoryArgument(3, _p_c); 86 | 87 | kernel.SetMemoryArgument(4, _c); 88 | kernel.SetMemoryArgument(5, _c_valid); 89 | 90 | mark(s, "memory init 2"); 91 | 92 | ComputeEventList eventList = new ComputeEventList(); 93 | 94 | ComputeCommandQueue commands = new ComputeCommandQueue(context, context.Devices[0], ComputeCommandQueueFlags.None); 95 | 96 | commands.Execute(kernel, null, new long[] { count }, null, eventList); 97 | 98 | mark(s, "execute"); 99 | 100 | commands.ReadFromBuffer(_c, ref c, false, eventList); 101 | commands.ReadFromBuffer(_c_valid, ref c_valid, false, eventList); 102 | commands.Finish(); 103 | 104 | mark(s, "read 1"); 105 | 106 | Parallel.For(0, count, i => 107 | { 108 | var calc = calculations[i]; 109 | calc.Coords = new BarycentricCoordinates(c[i].U,c[i].V,c[i].W); 110 | 111 | if (c_valid[i] == 1) 112 | { 113 | lock (calc.Tri) 114 | calc.Tri.Points.Add(new DrawPoint(calc.Coords, calc.P)); 115 | } 116 | }); 117 | 118 | mark(s, "read 2"); 119 | 120 | 121 | // cleanup commands 122 | commands.Dispose(); 123 | 124 | // cleanup events 125 | foreach (ComputeEventBase eventBase in eventList) 126 | { 127 | eventBase.Dispose(); 128 | } 129 | eventList.Clear(); 130 | 131 | // cleanup kernel 132 | kernel.Dispose(); 133 | 134 | _p_p.Dispose(); 135 | 136 | _p_a.Dispose(); 137 | _p_b.Dispose(); 138 | _p_c.Dispose(); 139 | 140 | _c.Dispose(); 141 | _c_valid.Dispose(); 142 | 143 | mark(s, "dispose"); 144 | } 145 | 146 | private static void mark(Stopwatch s, string str) 147 | { 148 | Console.WriteLine(str + " completed in " + s.ElapsedMilliseconds); 149 | s.Restart(); 150 | } 151 | 152 | private struct IntVec2 153 | { 154 | public IntVec2(Point p) 155 | { 156 | X = p.X; 157 | Y = p.Y; 158 | } 159 | 160 | public int X; 161 | public int Y; 162 | } 163 | 164 | private struct FloatVec3 165 | { 166 | public float U; 167 | public float V; 168 | public float W; 169 | public float nil;//dummy value for memory alignment 170 | } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /Trigrad/MeshBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using PixelMapSharp; 8 | using TriangleNet; 9 | using TriangleNet.Geometry; 10 | using Trigrad.DataTypes; 11 | using Point = System.Drawing.Point; 12 | 13 | namespace Trigrad 14 | { 15 | public static class MeshBuilder 16 | { 17 | public static List BuildMesh(Dictionary pointIndex) 18 | { 19 | InputGeometry g = new InputGeometry(); 20 | foreach (var value in pointIndex) 21 | { 22 | g.AddPoint(value.Key.X, value.Key.Y); 23 | } 24 | 25 | Mesh m = new Mesh(); 26 | m.Triangulate(g); 27 | 28 | List sampleMesh = new List(); 29 | 30 | Dictionary table = new Dictionary(); 31 | 32 | Dictionary sampleTable = new Dictionary(); 33 | 34 | foreach (var mTri in m.Triangles) 35 | { 36 | SampleTri tri = new SampleTri(mTri); 37 | 38 | for (int i = 0; i < 3; i++) 39 | tri.TriangleNeighbours.Add(mTri.GetNeighbor(i)); 40 | 41 | sampleMesh.Add(tri); 42 | table.Add(mTri, tri); 43 | 44 | if (sampleTable.ContainsKey(tri.U.Point)) 45 | tri.U = sampleTable[tri.U.Point]; 46 | else 47 | sampleTable[tri.U.Point] = tri.U; 48 | 49 | if (sampleTable.ContainsKey(tri.V.Point)) 50 | tri.V = sampleTable[tri.V.Point]; 51 | else 52 | sampleTable[tri.V.Point] = tri.V; 53 | 54 | if (sampleTable.ContainsKey(tri.W.Point)) 55 | tri.W = sampleTable[tri.W.Point]; 56 | else 57 | sampleTable[tri.W.Point] = tri.W; 58 | } 59 | 60 | foreach (var tri in sampleMesh) 61 | { 62 | foreach (var triangleNeighbour in tri.TriangleNeighbours) 63 | { 64 | if (triangleNeighbour != null) 65 | tri.SampleTriNeighbours.Add(table[triangleNeighbour]); 66 | } 67 | tri.U.Triangles.Add(tri); 68 | tri.V.Triangles.Add(tri); 69 | tri.W.Triangles.Add(tri); 70 | 71 | tri.U.Color = pointIndex[tri.U.Point]; 72 | tri.V.Color = pointIndex[tri.V.Point]; 73 | tri.W.Color = pointIndex[tri.W.Point]; 74 | 75 | tri.CenterColor = tri.U.Color; 76 | } 77 | 78 | return sampleMesh; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /Trigrad/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Trigrad")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("Trigrad")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b3554048-7521-4e2a-983e-5ce14ddbf86c")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /Trigrad/Renderers/CenterFill.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using PixelMapSharp; 7 | using Trigrad.DataTypes; 8 | 9 | namespace Trigrad.Renderers 10 | { 11 | public class CenterFill : IFill 12 | { 13 | public void Fill(SampleTri t, PixelMap map) 14 | { 15 | foreach (var drawPoint in t.Points) 16 | { 17 | Pixel gradedColor = t.CenterColor; 18 | 19 | map[drawPoint.Point] = gradedColor; 20 | } 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Trigrad/Renderers/GradeFill.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using PixelMapSharp; 7 | using Trigrad.ColorGraders; 8 | using Trigrad.DataTypes; 9 | 10 | namespace Trigrad.Renderers 11 | { 12 | public class GradeFill : IFill 13 | { 14 | public GradeFill(IGrader grader) 15 | { 16 | Grader = grader; 17 | } 18 | 19 | public IGrader Grader; 20 | 21 | public void Fill(SampleTri t, PixelMap map) 22 | { 23 | foreach (var drawPoint in t.Points) 24 | { 25 | Pixel gradedColor = Grader.Grade(t.U, t.V, t.W, drawPoint); 26 | 27 | map[drawPoint.Point] = gradedColor; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Trigrad/Renderers/IFill.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using PixelMapSharp; 7 | using Trigrad.DataTypes; 8 | 9 | namespace Trigrad.Renderers 10 | { 11 | public interface IFill 12 | { 13 | void Fill(SampleTri t, PixelMap map); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Trigrad/Renderers/ShapeFill.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using PixelMapSharp; 6 | using Trigrad.DataTypes; 7 | 8 | namespace Trigrad.Renderers 9 | { 10 | public class ShapeFill : IFill 11 | { 12 | public ShapeFill(int sides) 13 | { 14 | ShapeFunction = t => Math.Cos(Math.PI/sides)/Math.Cos(t%((2*Math.PI)/sides) - Math.PI/sides); 15 | } 16 | 17 | public ShapeFill(Func function) 18 | { 19 | ShapeFunction = function; 20 | } 21 | 22 | public Func ShapeFunction = t => 1; 23 | 24 | public void Fill(SampleTri t, PixelMap map) 25 | { 26 | Point center = t.CenterPoint; 27 | 28 | int radius = (int)t.Samples.Max(s => dist(s.Point, center)); 29 | 30 | for (int x = center.X - radius; x < center.X + radius; x++) 31 | { 32 | for (int y = center.Y - radius; y < center.Y + radius; y++) 33 | { 34 | Point fillPoint = new Point(x, y); 35 | double theta = Math.PI+Math.Atan2(fillPoint.Y - center.Y,fillPoint.X - center.X); 36 | double distance = dist(fillPoint, center); 37 | 38 | if (map.Inside(fillPoint) && distance < ShapeFunction(theta)*radius) 39 | { 40 | map[fillPoint] = t.CenterColor; 41 | 42 | //map[fillPoint] = new Pixel(theta*180/Math.PI,0.5,0.5); 43 | } 44 | 45 | if (distance > 1000) 46 | { 47 | Console.WriteLine("wat"); 48 | } 49 | } 50 | } 51 | 52 | } 53 | 54 | private double dist(Point a, Point b) 55 | { 56 | int dX = a.X - b.X; 57 | int dY = a.Y - b.Y; 58 | 59 | return Math.Sqrt(dX * dX + dY * dY); 60 | } 61 | 62 | 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /Trigrad/TriangleRasterisation.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using Trigrad.DataTypes; 6 | 7 | namespace Trigrad 8 | { 9 | internal static class TriangleRasterization 10 | { 11 | public static IEnumerable BuildTriCalculations(SampleTri t) 12 | { 13 | int minX = t.U.Point.X < t.V.Point.X && t.U.Point.X < t.W.Point.X ? t.U.Point.X : (t.V.Point.X < t.W.Point.X ? t.V.Point.X : t.W.Point.X); 14 | int minY = t.U.Point.Y < t.V.Point.Y && t.U.Point.Y < t.W.Point.Y ? t.U.Point.Y : (t.V.Point.Y < t.W.Point.Y ? t.V.Point.Y : t.W.Point.Y); 15 | 16 | int maxX = t.U.Point.X > t.V.Point.X && t.U.Point.X > t.W.Point.X ? t.U.Point.X : (t.V.Point.X > t.W.Point.X ? t.V.Point.X : t.W.Point.X); 17 | int maxY = t.U.Point.Y > t.V.Point.Y && t.U.Point.Y > t.W.Point.Y ? t.U.Point.Y : (t.V.Point.Y > t.W.Point.Y ? t.V.Point.Y : t.W.Point.Y); 18 | 19 | for (int x = minX; x < maxX + 1; x++) 20 | { 21 | for (int y = minY; y < maxY + 1; y++) 22 | { 23 | Point p = new Point(x, y); 24 | 25 | Calculation c = new Calculation(); 26 | c.A = t.U.Point; 27 | c.B = t.V.Point; 28 | c.C = t.W.Point; 29 | 30 | c.P = p; 31 | c.Tri = t; 32 | 33 | yield return c; 34 | } 35 | } 36 | } 37 | public static IEnumerable PointsInTriangle(SampleTri t) 38 | { 39 | int minX = t.U.Point.X < t.V.Point.X && t.U.Point.X < t.W.Point.X ? t.U.Point.X : (t.V.Point.X < t.W.Point.X ? t.V.Point.X : t.W.Point.X); 40 | int minY = t.U.Point.Y < t.V.Point.Y && t.U.Point.Y < t.W.Point.Y ? t.U.Point.Y : (t.V.Point.Y < t.W.Point.Y ? t.V.Point.Y : t.W.Point.Y); 41 | 42 | int maxX = t.U.Point.X > t.V.Point.X && t.U.Point.X > t.W.Point.X ? t.U.Point.X : (t.V.Point.X > t.W.Point.X ? t.V.Point.X : t.W.Point.X); 43 | int maxY = t.U.Point.Y > t.V.Point.Y && t.U.Point.Y > t.W.Point.Y ? t.U.Point.Y : (t.V.Point.Y > t.W.Point.Y ? t.V.Point.Y : t.W.Point.Y); 44 | 45 | for (int x = minX; x < maxX + 1; x++) 46 | { 47 | for (int y = minY; y < maxY + 1; y++) 48 | { 49 | Point p = new Point(x, y); 50 | 51 | var coords = Barycentric.GetCoordinates(p, t.U.Point,t.V.Point,t.W.Point); 52 | 53 | if (Barycentric.ValidCoords(coords)) 54 | { 55 | yield return new DrawPoint(coords, p); 56 | } 57 | } 58 | } 59 | } 60 | 61 | public static void CalculateMesh(List mesh) 62 | { 63 | foreach (var t in mesh) 64 | { 65 | t.Points = PointsInTriangle(t).ToList(); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Trigrad/Trigrad.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {3E743CD4-6FB3-43DA-AA7B-F4B2EEEAFA85} 8 | Library 9 | Properties 10 | Trigrad 11 | Trigrad 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | true 24 | bin\Debug\Trigrad.XML 25 | 26 | 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | ..\packages\Sourceforge.Cloo.0.9.1\lib\net35\Cloo.dll 37 | 38 | 39 | ..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll 40 | 41 | 42 | ..\packages\MathNet.Numerics.3.7.0\lib\net40\MathNet.Numerics.dll 43 | 44 | 45 | ..\packages\PixelMap.1.0.1\lib\PixelMap.dll 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | ..\Triangle.dll 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | Always 105 | 106 | 107 | 108 | 115 | -------------------------------------------------------------------------------- /Trigrad/TrigradCompressor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using PixelMapSharp; 9 | using TriangleNet; 10 | using TriangleNet.Data; 11 | using TriangleNet.Geometry; 12 | using Trigrad.DataTypes; 13 | using Trigrad.DataTypes.Compression; 14 | using Point = System.Drawing.Point; 15 | 16 | namespace Trigrad 17 | { 18 | public delegate void CompressorProgressUpdate(double progress); 19 | 20 | /// Holds methods for compressing trigrad compressed imagery. 21 | public static class TrigradCompressor 22 | { 23 | public static event CompressorProgressUpdate OnUpdate; 24 | 25 | /// Compresses a bitmap using TrigradCompression. 26 | /// The input bitmap. 27 | /// TrigradOptions specifying how the image will be compressed. 28 | public static TrigradCompressed CompressBitmap(PixelMap pixelmap, TrigradOptions options) 29 | { 30 | TrigradCompressed compressed = new TrigradCompressed { Height = pixelmap.Height, Width = pixelmap.Width }; 31 | 32 | double baseChance = options.SampleCount / (options.FrequencyTable.Sum); 33 | 34 | int i = 0; 35 | int count = pixelmap.Width * pixelmap.Height; 36 | for (int x = 0; x < pixelmap.Width; x++) 37 | { 38 | for (int y = 0; y < pixelmap.Height; y++) 39 | { 40 | if ((x == 0 && y == 0) || 41 | (x == pixelmap.Width - 1 && y == 0) || 42 | (x == 0 && y == pixelmap.Height - 1) || 43 | (x == pixelmap.Width - 1 && y == pixelmap.Height - 1)) 44 | { 45 | compressed.SampleTable[new Point(x, y)] = pixelmap[new Point(x, y)]; 46 | continue; 47 | } 48 | 49 | double chance = ((options.FrequencyTable != null) 50 | ? options.FrequencyTable.Table[x, y] 51 | : 1d) * baseChance; 52 | 53 | lock(options.Random) 54 | if (options.Random.NextDouble() < chance) 55 | { 56 | lock (compressed.SampleTable) 57 | compressed.SampleTable[new Point(x, y)] = pixelmap[new Point(x, y)]; 58 | } 59 | 60 | 61 | if (i % 50 == 0 && OnUpdate != null) 62 | OnUpdate((double)i / count); 63 | 64 | i++; 65 | } 66 | } 67 | 68 | 69 | return compressed; 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /Trigrad/TrigradDecompressor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Drawing; 5 | using System.Drawing.Imaging; 6 | using System.Linq; 7 | using System.Threading.Tasks; 8 | using PixelMapSharp; 9 | using TriangleNet; 10 | using TriangleNet.Data; 11 | using TriangleNet.Geometry; 12 | using Trigrad.ColorGraders; 13 | using Trigrad.DataTypes; 14 | using Trigrad.DataTypes.Compression; 15 | using Point = System.Drawing.Point; 16 | 17 | namespace Trigrad 18 | { 19 | public delegate void DecompressorProgressUpdate(double progress); 20 | /// Holds methods for decompressing trigrad compressed imagery. 21 | public static class TrigradDecompressor 22 | { 23 | public static event DecompressorProgressUpdate OnUpdate; 24 | 25 | /// Decompresses a trigrad compressed bitmap. 26 | /// The TrigradCompressed data. 27 | /// The original image to determine the most effect fill mode. 28 | /// Options dictating the decompression. 29 | public static TrigradDecompressed DecompressBitmap(TrigradCompressed compressionData, TrigradOptions options) 30 | { 31 | TrigradDecompressed decompressed = new TrigradDecompressed(compressionData.Width, compressionData.Height); 32 | 33 | for (int x = 0; x < compressionData.Width; x++) 34 | for (int y = 0; y < compressionData.Height; y++) 35 | decompressed.Output[x, y] = new Pixel(Color.HotPink); 36 | 37 | drawMesh(compressionData.Mesh, decompressed.Output, options); 38 | 39 | 40 | fillGaps(decompressed.Output); 41 | 42 | return decompressed; 43 | } 44 | 45 | 46 | private static void drawMesh(List mesh, PixelMap output, TrigradOptions options) 47 | { 48 | int i = 0; 49 | int count = mesh.Count; 50 | foreach (var triangle in mesh) 51 | { 52 | options.Renderer.Fill(triangle,output); 53 | 54 | 55 | if (i % 50 == 0 && OnUpdate != null) 56 | OnUpdate((double)i / count); 57 | 58 | i++; 59 | } 60 | } 61 | 62 | private static void fillGaps(PixelMap p) 63 | { 64 | Pixel lastColor = p[0]; 65 | Pixel pink = new Pixel(Color.HotPink); 66 | 67 | for (int x = 0; x < p.Width; x++) 68 | { 69 | for (int y = 0; y < p.Height; y++) 70 | { 71 | if (alike(p[x, y],pink)) 72 | { 73 | p[x, y] = lastColor; 74 | } 75 | else 76 | { 77 | lastColor = p[x, y]; 78 | } 79 | } 80 | } 81 | } 82 | 83 | static bool alike(Pixel a, Pixel b) 84 | { 85 | return a.R == b.R && a.G == b.G && a.B == b.B; 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /Trigrad/TrigradOptimiser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Drawing; 5 | using System.Linq; 6 | using System.Net; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using PixelMapSharp; 11 | using TriangleNet; 12 | using TriangleNet.Data; 13 | using TriangleNet.Geometry; 14 | using Trigrad.ColorGraders; 15 | using Trigrad.DataTypes; 16 | using Trigrad.DataTypes.Compression; 17 | using Point = System.Drawing.Point; 18 | 19 | namespace Trigrad 20 | { 21 | public delegate void OptimiserProgressUpdate(double progress); 22 | 23 | public static class TrigradOptimiser 24 | { 25 | public static event OptimiserProgressUpdate OnUpdate; 26 | 27 | 28 | public static void OptimiseMesh(TrigradCompressed compressionData, PixelMap original, TrigradOptions options) 29 | { 30 | var mesh = compressionData.Mesh; 31 | GPUT.CalculateMesh(mesh); 32 | 33 | var samples = mesh.SelectMany(t => t.Samples).Distinct().ToList(); 34 | 35 | for (int i = 0; i < options.Iterations; i++) 36 | { 37 | minimiseMesh(samples, options, original); 38 | 39 | Console.WriteLine("{0}/{1}", i, options.Iterations); 40 | } 41 | 42 | compressionData.Mesh = mesh; 43 | } 44 | 45 | 46 | static void minimiseMesh(List samples, TrigradOptions options, PixelMap original) 47 | { 48 | tempMap = new PixelMap(original.Width,original.Height); 49 | 50 | int o = 0; 51 | int count = samples.Count; 52 | foreach (var sample in samples) 53 | { 54 | minimiseSample(sample, options.Resamples, original, options); 55 | 56 | o++; 57 | 58 | if (o%1000 == 0) 59 | Console.WriteLine("{0}/{1}", o, samples.Count); 60 | 61 | if (o%100 == 0 && OnUpdate != null) 62 | OnUpdate((double)o/count); 63 | } 64 | } 65 | 66 | 67 | private static void minimiseSample(Sample s, int resamples, PixelMap original, TrigradOptions options) 68 | { 69 | if (s.Point.X == 0 || s.Point.Y == 0) 70 | return; 71 | 72 | if (s.Point.X == original.Width - 1 || s.Point.Y == original.Height - 1) 73 | return; 74 | 75 | 76 | var curPoints = s.Points; 77 | 78 | double minError = errorPolygon(s, original, options); 79 | Point bestPoint = s.Point; 80 | 81 | if (polygonConvex(s)) 82 | return; 83 | 84 | int count = curPoints.Count; 85 | int skip = count / resamples; 86 | if (skip == 0) 87 | skip = 1; 88 | 89 | foreach (var drawPoint in curPoints.Where((x, i) => i % skip == 0)) 90 | { 91 | s.Point = drawPoint.Point; 92 | 93 | TriangleRasterization.CalculateMesh(s.Triangles); 94 | 95 | double error = errorPolygon(s, original, options); 96 | if (error < minError) 97 | { 98 | bestPoint = drawPoint.Point; 99 | minError = error; 100 | } 101 | } 102 | 103 | s.Point = bestPoint; 104 | TriangleRasterization.CalculateMesh(s.Triangles); 105 | } 106 | 107 | private static PixelMap tempMap; 108 | private static double errorPolygon(Sample s, PixelMap original, TrigradOptions options) 109 | { 110 | if(options.ResampleColors) 111 | foreach (var sample in s.Samples) 112 | { 113 | sample.Color = original[sample.Point]; 114 | } 115 | 116 | double error = 0d; 117 | foreach (var t in s.Triangles) 118 | { 119 | t.CenterColor = original[t.CenterPoint]; 120 | 121 | options.Renderer.Fill(t,tempMap); 122 | 123 | foreach (var drawPoint in t.Points) 124 | { 125 | Pixel a = original[drawPoint.Point]; 126 | Pixel b = tempMap[drawPoint.Point]; 127 | 128 | Pixel diff = a - b; 129 | 130 | error += diff.R; 131 | error += diff.G; 132 | error += diff.B; 133 | } 134 | } 135 | return error; 136 | } 137 | 138 | private static bool polygonConvex(Sample s) 139 | { 140 | List outerPolygonPoints = s.Samples.Except(new[]{s}).Select(sample=>sample.Point).ToList(); 141 | 142 | bool got_negative = false; 143 | bool got_positive = false; 144 | int num_points = outerPolygonPoints.Count(); 145 | int B, C; 146 | for (int A = 0; A < num_points; A++) 147 | { 148 | B = (A + 1) % num_points; 149 | C = (B + 1) % num_points; 150 | 151 | float cross_product = 152 | crossProductMagnitude( 153 | outerPolygonPoints[A].X, outerPolygonPoints[A].Y, 154 | outerPolygonPoints[B].X, outerPolygonPoints[B].Y, 155 | outerPolygonPoints[C].X, outerPolygonPoints[C].Y); 156 | if (cross_product < 0) 157 | { 158 | got_negative = true; 159 | } 160 | else if (cross_product > 0) 161 | { 162 | got_positive = true; 163 | } 164 | if (got_negative && got_positive) return false; 165 | } 166 | 167 | // If we got this far, the polygon is convex. 168 | return true; 169 | } 170 | private static float crossProductMagnitude(float Ax, float Ay, 171 | float Bx, float By, float Cx, float Cy) 172 | { 173 | // Get the vectors' coordinates. 174 | float BAx = Ax - Bx; 175 | float BAy = Ay - By; 176 | float BCx = Cx - Bx; 177 | float BCy = Cy - By; 178 | 179 | // Calculate the Z coordinate of the cross product. 180 | return (BAx * BCy - BAy * BCx); 181 | } 182 | 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Trigrad/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /TrigradTesting/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /TrigradTesting/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Drawing; 5 | using System.IO; 6 | using System.Linq; 7 | using PixelMapSharp; 8 | using Trigrad; 9 | using Trigrad.ColorGraders; 10 | using Trigrad.DataTypes; 11 | using Trigrad.DataTypes.Compression; 12 | using Trigrad.Filters; 13 | using Trigrad.Renderers; 14 | 15 | namespace TrigradTesting 16 | { 17 | class Program 18 | { 19 | private static string backup = @"E:\deepstorage"; 20 | 21 | static void Main(string[] args) 22 | { 23 | Stopwatch s = new Stopwatch(); 24 | s.Start(); 25 | Console.WriteLine("Trigrad"); 26 | string input = "tests\\input\\Tulips.jpg"; 27 | 28 | PixelMap inputBitmap = PixelMap.SlowLoad(new Bitmap(input)); 29 | FrequencyTable table = new FrequencyTable(inputBitmap,1); 30 | 31 | var options = new TrigradOptions 32 | { 33 | SampleCount = 40000, 34 | FrequencyTable = table, 35 | Resamples = 4, 36 | Iterations =0, 37 | Random = new Random(0), 38 | ResampleColors = true, 39 | Renderer = new ShapeFill(3) 40 | }; 41 | 42 | var results = TrigradCompressor.CompressBitmap(inputBitmap, options); 43 | 44 | results.DebugVisualisation().GetBitmap().Save("tests\\points.png"); 45 | 46 | results.Mesh = MeshBuilder.BuildMesh(results.SampleTable); 47 | results.MeshOutput(inputBitmap).GetBitmap().Save("tests\\mesh_a.png"); 48 | 49 | TrigradOptimiser.OptimiseMesh(results, inputBitmap, options); 50 | 51 | 52 | GPUT.CalculateMesh(results.Mesh); 53 | 54 | //results.Save(new FileStream("tests\\out.tri", FileMode.Create)); 55 | 56 | results.MeshOutput(inputBitmap).GetBitmap().Save("tests\\mesh_b.png"); 57 | 58 | Console.WriteLine(results.SampleTable.Count); 59 | 60 | //var loaded = new TrigradCompressed(new FileStream("tests\\out.tri", FileMode.Open)); 61 | 62 | //results.Mesh = results.Mesh.OrderBy(t => t.CenterColor.Lightness).ToList(); 63 | results.Mesh.Shuffle(options.Random); 64 | 65 | var returned = TrigradDecompressor.DecompressBitmap(results, options); 66 | 67 | returned.Output.GetBitmap().Save("tests\\output.png"); 68 | returned.DebugOutput.GetBitmap().Save("tests\\debug_output.png"); 69 | 70 | int error = errorBitmap(inputBitmap, returned.Output); 71 | double avgError = Math.Round((double)error / (inputBitmap.Width * inputBitmap.Height * 3), 2); 72 | 73 | Console.WriteLine("{0} error", avgError); 74 | Console.WriteLine("{0} s", Math.Round(s.ElapsedMilliseconds / 1000d, 2)); 75 | 76 | saveBackup(avgError, Path.GetFileNameWithoutExtension(input), options); 77 | 78 | Console.Beep(); 79 | Console.ReadKey(); 80 | } 81 | 82 | static void saveBackup(double error, string file, TrigradOptions options) 83 | { 84 | Random r = new Random(); 85 | 86 | var path = Path.Combine(backup, string.Format("{0} Test #{1}, {2} err, {3} samples, {4} resamples, {5} iterations", file, r.Next(), error, options.SampleCount, options.Resamples, options.Iterations)); 87 | Directory.CreateDirectory(path); 88 | 89 | File.Copy("tests\\points.png", Path.Combine(path, "points.png")); 90 | File.Copy("tests\\output.png", Path.Combine(path, "output.png")); 91 | //File.Copy("tests\\debug_output.png", Path.Combine(path, "debug_output.png")); 92 | File.Copy("tests\\mesh_a.png", Path.Combine(path, "mesh_a.png")); 93 | File.Copy("tests\\mesh_b.png", Path.Combine(path, "mesh_b.png")); 94 | File.Copy("tests\\error.png", Path.Combine(path, "error.png")); 95 | 96 | } 97 | 98 | static int errorBitmap(PixelMap a, PixelMap b) 99 | { 100 | int error = 0; 101 | PixelMap output = new PixelMap(a.Width, a.Height); 102 | for (int x = 0; x < a.Width; x++) 103 | { 104 | for (int y = 0; y < a.Height; y++) 105 | { 106 | Pixel cA = a[x, y]; 107 | Pixel cB = b[x, y]; 108 | 109 | Pixel diff = cA - cB; 110 | 111 | error += (diff.R + diff.G + diff.B); 112 | 113 | output[x, y] = diff; 114 | } 115 | } 116 | output.GetBitmap().Save("tests\\error.png"); 117 | 118 | return error; 119 | } 120 | 121 | static TrigradCompressed fauxResults(PixelMap input) 122 | { 123 | var results = new TrigradCompressed(); 124 | 125 | results.Width = input.Width; 126 | results.Height = input.Height; 127 | 128 | List samplePoints = new List(); 129 | 130 | samplePoints.Add(new Point(0, 0)); 131 | samplePoints.Add(new Point(input.Width - 1, 0)); 132 | samplePoints.Add(new Point(0, input.Height - 1)); 133 | samplePoints.Add(new Point(input.Width - 1, input.Height - 1)); 134 | 135 | int cellsize = 8; 136 | 137 | 138 | 139 | for (int x = 0; x < input.Width / cellsize; x++) 140 | { 141 | for (int y = 0; y < input.Height / cellsize; y++) 142 | { 143 | samplePoints.Add(new Point(x * cellsize, y * cellsize)); 144 | } 145 | } 146 | 147 | foreach (var samplePoint in samplePoints) 148 | { 149 | results.SampleTable[samplePoint] = input[samplePoint]; 150 | } 151 | 152 | results.Mesh = MeshBuilder.BuildMesh(results.SampleTable); 153 | 154 | return results; 155 | } 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /TrigradTesting/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TrigradTesting")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("TrigradTesting")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2015")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("b638631e-4e61-419e-a561-7eae4a4d7102")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TrigradTesting/TrigradTesting.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {E984EACE-46E7-485E-8A1C-FCB5884698F0} 8 | Exe 9 | Properties 10 | TrigradTesting 11 | TrigradTesting 12 | v4.5 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | true 36 | bin\x64\Debug\ 37 | DEBUG;TRACE 38 | full 39 | x64 40 | prompt 41 | MinimumRecommendedRules.ruleset 42 | true 43 | 44 | 45 | bin\x64\Release\ 46 | TRACE 47 | true 48 | pdbonly 49 | x64 50 | prompt 51 | MinimumRecommendedRules.ruleset 52 | true 53 | 54 | 55 | 56 | ..\packages\PixelMap.1.0.1\lib\PixelMap.dll 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | {3e743cd4-6fb3-43da-aa7b-f4b2eeeafa85} 78 | Trigrad 79 | 80 | 81 | 82 | 89 | -------------------------------------------------------------------------------- /TrigradTesting/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | --------------------------------------------------------------------------------