├── .gitattributes ├── .gitignore ├── AtkinsonDithering.cs ├── BurkesDithering.cs ├── DitheringBase.cs ├── FakeDithering.cs ├── FloydSteinbergDithering.cs ├── JarvisJudiceNinkeDithering.cs ├── PUBLICDOMAIN ├── README.md ├── SierraDithering.cs ├── SierraLiteDithering.cs ├── SierraTwoRowDithering.cs ├── StuckiDithering.cs └── birds.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear on external disk 35 | .Spotlight-V100 36 | .Trashes 37 | 38 | # Directories potentially created on remote AFP share 39 | .AppleDB 40 | .AppleDesktop 41 | Network Trash Folder 42 | Temporary Items 43 | .apdisk 44 | -------------------------------------------------------------------------------- /AtkinsonDithering.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This file implements error pushing of dithering via Atkinson kernel. 3 | 4 | This is free and unencumbered software released into the public domain. 5 | */ 6 | 7 | class AtkinsonDithering : DitheringBase 8 | { 9 | public AtkinsonDithering(FindColor colorfunc) : base(colorfunc) 10 | { 11 | this.methodLongName = "Atkinson"; 12 | this.fileNameAddition = "_ATK"; 13 | } 14 | 15 | override protected void PushError(int x, int y, short[] quantError) 16 | { 17 | // Push error 18 | // X 1/8 1/8 19 | // 1/8 1/8 1/8 20 | // 1/8 21 | 22 | int xMinusOne = x - 1; 23 | int xPlusOne = x + 1; 24 | int xPlusTwo = x + 2; 25 | int yPlusOne = y + 1; 26 | int yPlusTwo = y + 2; 27 | 28 | float multiplier = 1.0f / 8.0f; // Atkinson Dithering has same multiplier for every item 29 | 30 | // Current row 31 | int currentRow = y; 32 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 33 | { 34 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, multiplier); 35 | } 36 | 37 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 38 | { 39 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, multiplier); 40 | } 41 | 42 | // Next row 43 | currentRow = yPlusOne; 44 | if (this.IsValidCoordinate(xMinusOne, currentRow)) 45 | { 46 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, currentRow, quantError, multiplier); 47 | } 48 | 49 | if (this.IsValidCoordinate(x, currentRow)) 50 | { 51 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, multiplier); 52 | } 53 | 54 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 55 | { 56 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, multiplier); 57 | } 58 | 59 | // Next row 60 | currentRow = yPlusTwo; 61 | if (this.IsValidCoordinate(x, currentRow)) 62 | { 63 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, multiplier); 64 | } 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /BurkesDithering.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This file implements error pushing of dithering via (Daniel) Burkes kernel. 3 | 4 | This is free and unencumbered software released into the public domain. 5 | */ 6 | 7 | class BurkesDithering : DitheringBase 8 | { 9 | public BurkesDithering(FindColor colorfunc) : base(colorfunc) 10 | { 11 | this.methodLongName = "Burkes"; 12 | this.fileNameAddition = "_BUR"; 13 | } 14 | 15 | override protected void PushError(int x, int y, short[] quantError) 16 | { 17 | // Push error 18 | // X 8/32 4/32 19 | // 2/32 4/32 8/32 4/32 2/32 20 | 21 | int xMinusOne = x - 1; 22 | int xMinusTwo = x - 2; 23 | int xPlusOne = x + 1; 24 | int xPlusTwo = x + 2; 25 | int yPlusOne = y + 1; 26 | 27 | // Current row 28 | int currentRow = y; 29 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 30 | { 31 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 8.0f / 32.0f); 32 | } 33 | 34 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 35 | { 36 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 4.0f / 32.0f); 37 | } 38 | 39 | // Next row 40 | currentRow = yPlusOne; 41 | if (this.IsValidCoordinate(xMinusTwo, currentRow)) 42 | { 43 | this.ModifyImageWithErrorAndMultiplier(xMinusTwo, currentRow, quantError, 2.0f / 32.0f); 44 | } 45 | 46 | if (this.IsValidCoordinate(xMinusOne, currentRow)) 47 | { 48 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, currentRow, quantError, 4.0f / 32.0f); 49 | } 50 | 51 | if (this.IsValidCoordinate(x, currentRow)) 52 | { 53 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, 8.0f / 32.0f); 54 | } 55 | 56 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 57 | { 58 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 4.0f / 32.0f); 59 | } 60 | 61 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 62 | { 63 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 2.0f / 32.0f); 64 | } 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /DitheringBase.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | // This Delegate is used to find best suitable color from limited set of colors 5 | public delegate Color32 FindColor(Color32 original); 6 | 7 | public abstract class DitheringBase { 8 | 9 | protected int width; 10 | protected int height; 11 | 12 | protected FindColor colorFunction = null; 13 | 14 | protected string methodLongName = ""; 15 | protected string fileNameAddition = ""; 16 | 17 | private Color32[] pixels = null; 18 | 19 | public DitheringBase(FindColor colorfunc) 20 | { 21 | this.colorFunction = colorfunc; 22 | } 23 | 24 | // 25 | public Texture2D DoDithering(Texture2D input) 26 | { 27 | this.width = input.width; 28 | this.height = input.height; 29 | 30 | // Copy all pixels of input Texture2D to new array, which we are going to edit 31 | this.pixels = input.GetPixels32(); 32 | 33 | Color32 originalPixel = Color.white; // Default value isn't used 34 | Color32 newPixel = Color.white; // Default value isn't used 35 | short[] quantError = null; // Default values aren't used 36 | 37 | for (int y = 0; y < this.height; y++) 38 | { 39 | for (int x = 0; x < this.width; x++) 40 | { 41 | originalPixel = this.pixels[GetIndexWith(x, y)]; 42 | newPixel = this.colorFunction(originalPixel); 43 | 44 | this.pixels[GetIndexWith(x, y)] = newPixel; 45 | 46 | quantError = GetQuantError(originalPixel, newPixel); 47 | this.PushError(x, y, quantError); 48 | } 49 | } 50 | 51 | // Create the texture we are going to return from pixels array 52 | Texture2D returnTexture = new Texture2D(width, height, TextureFormat.ARGB32, false); 53 | returnTexture.SetPixels32(this.pixels); 54 | returnTexture.Apply(); 55 | 56 | return returnTexture; 57 | } 58 | 59 | // Implement this for every dithering method 60 | protected abstract void PushError(int x, int y, short[] quantError); 61 | 62 | protected bool IsValidCoordinate(int x, int y) 63 | { 64 | return (0 <= x && x < this.width && 0 <= y && y < this.height); 65 | } 66 | 67 | protected int GetIndexWith(int x, int y) 68 | { 69 | return y * this.width + x; 70 | } 71 | 72 | public string GetMethodName() 73 | { 74 | return this.methodLongName; 75 | } 76 | 77 | public string GetFilenameAddition() 78 | { 79 | return this.fileNameAddition; 80 | } 81 | 82 | protected short[] GetQuantError(Color32 originalPixel, Color32 newPixel) 83 | { 84 | short[] returnValue = new short[4]; 85 | 86 | returnValue [0] = (short)(originalPixel.r - newPixel.r); 87 | returnValue [1] = (short)(originalPixel.g - newPixel.g); 88 | returnValue [2] = (short)(originalPixel.b - newPixel.b); 89 | returnValue [3] = (short)(originalPixel.a - newPixel.a); 90 | 91 | return returnValue; 92 | } 93 | 94 | public void ModifyImageWithErrorAndMultiplier(int x, int y, short[] quantError, float multiplier) 95 | { 96 | Color32 oldColor = this.pixels[GetIndexWith(x, y)]; 97 | 98 | // We limit the color here because we don't want the value go over 255 or under 0 99 | Color32 newColor = new Color32( 100 | GetLimitedValue(oldColor.r, Mathf.RoundToInt(quantError[0] * multiplier)), 101 | GetLimitedValue(oldColor.g, Mathf.RoundToInt(quantError[1] * multiplier)), 102 | GetLimitedValue(oldColor.b, Mathf.RoundToInt(quantError[2] * multiplier)), 103 | GetLimitedValue(oldColor.a, Mathf.RoundToInt(quantError[3] * multiplier))); 104 | 105 | this.pixels[GetIndexWith(x, y)] = newColor; 106 | } 107 | 108 | private static byte GetLimitedValue(byte original, int error) 109 | { 110 | int newValue = original + error; 111 | return (byte)Mathf.Clamp(newValue, byte.MinValue, byte.MaxValue); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /FakeDithering.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This file implements fake dithering, meaning NO dithering is done. 3 | 4 | This is free and unencumbered software released into the public domain. 5 | */ 6 | 7 | class FakeDithering : DitheringBase 8 | { 9 | public FakeDithering(FindColor colorfunc) : base(colorfunc) 10 | { 11 | this.methodLongName = "No dithering"; 12 | this.fileNameAddition = "_NONE"; 13 | } 14 | 15 | override protected void PushError(int x, int y, short[] quantError) 16 | { 17 | // Don't do anything 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /FloydSteinbergDithering.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This file implements error pushing of dithering via (Robert) Floyd and (Louis) Steinberg kernel. 3 | This is free and unencumbered software released into the public domain. 4 | */ 5 | 6 | public class FloydSteinbergDithering : DitheringBase 7 | { 8 | public FloydSteinbergDithering(FindColor colorfunc) : base(colorfunc) 9 | { 10 | this.methodLongName = "Floyd-Steinberg"; 11 | this.fileNameAddition = "_FS"; 12 | } 13 | 14 | override protected void PushError(int x, int y, short[] quantError) 15 | { 16 | // Push error 17 | // X 7/16 18 | // 3/16 5/16 1/16 19 | 20 | int xMinusOne = x - 1; 21 | int xPlusOne = x + 1; 22 | int yPlusOne = y + 1; 23 | 24 | // Current row 25 | if (this.IsValidCoordinate(xPlusOne, y)) 26 | { 27 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, y, quantError, 7.0f / 16.0f); 28 | } 29 | 30 | // Next row 31 | if (this.IsValidCoordinate(xMinusOne, yPlusOne)) 32 | { 33 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, yPlusOne, quantError, 3.0f / 16.0f); 34 | } 35 | 36 | if (this.IsValidCoordinate(x, yPlusOne)) 37 | { 38 | this.ModifyImageWithErrorAndMultiplier(x, yPlusOne, quantError, 5.0f / 16.0f); 39 | } 40 | 41 | if (this.IsValidCoordinate(xPlusOne, yPlusOne)) 42 | { 43 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, yPlusOne, quantError, 1.0f / 16.0f); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /JarvisJudiceNinkeDithering.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This file implements error pushing of dithering via Jarvis, Judice and Ninke kernel. 3 | 4 | This is free and unencumbered software released into the public domain. 5 | */ 6 | 7 | public class JarvisJudiceNinkeDithering : DitheringBase 8 | { 9 | public JarvisJudiceNinkeDithering(FindColor colorfunc) : base(colorfunc) 10 | { 11 | this.methodLongName = "Jarvis-Judice-Ninke"; 12 | this.fileNameAddition = "_JJN"; 13 | } 14 | 15 | override protected void PushError(int x, int y, short[] quantError) 16 | { 17 | // Push error 18 | // X 7/48 5/48 19 | // 3/48 5/48 7/48 5/48 3/48 20 | // 1/48 3/48 5/48 3/48 1/48 21 | 22 | int xMinusOne = x - 1; 23 | int xMinusTwo = x - 2; 24 | int xPlusOne = x + 1; 25 | int xPlusTwo = x + 2; 26 | int yPlusOne = y + 1; 27 | int yPlusTwo = y + 2; 28 | 29 | // Current row 30 | int currentRow = y; 31 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 32 | { 33 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 7.0f / 48.0f); 34 | } 35 | 36 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 37 | { 38 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 5.0f / 48.0f); 39 | } 40 | 41 | // Next row 42 | currentRow = yPlusOne; 43 | if (this.IsValidCoordinate(xMinusTwo, currentRow)) 44 | { 45 | this.ModifyImageWithErrorAndMultiplier(xMinusTwo, currentRow, quantError, 3.0f / 48.0f); 46 | } 47 | 48 | if (this.IsValidCoordinate(xMinusOne, currentRow)) 49 | { 50 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, currentRow, quantError, 5.0f / 48.0f); 51 | } 52 | 53 | if (this.IsValidCoordinate(x, currentRow)) 54 | { 55 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, 7.0f / 48.0f); 56 | } 57 | 58 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 59 | { 60 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 5.0f / 48.0f); 61 | } 62 | 63 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 64 | { 65 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 3.0f / 48.0f); 66 | } 67 | 68 | // Next row 69 | currentRow = yPlusTwo; 70 | if (this.IsValidCoordinate(xMinusTwo, currentRow)) 71 | { 72 | this.ModifyImageWithErrorAndMultiplier(xMinusTwo, currentRow, quantError, 1.0f / 48.0f); 73 | } 74 | 75 | if (this.IsValidCoordinate(xMinusOne, currentRow)) 76 | { 77 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, currentRow, quantError, 3.0f / 48.0f); 78 | } 79 | 80 | if (this.IsValidCoordinate(x, currentRow)) 81 | { 82 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, 5.0f / 48.0f); 83 | } 84 | 85 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 86 | { 87 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 3.0f / 48.0f); 88 | } 89 | 90 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 91 | { 92 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 1.0f / 48.0f); 93 | } 94 | } 95 | } -------------------------------------------------------------------------------- /PUBLICDOMAIN: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dithering-Unity3d 2 | Dithering algorithms for Unity3d 3 | 4 | This project contains source code and sample images of dithering algorithms that can be used in Unity3d. There are total of 8 different dithering algorithms. You can find more info about the algorithms from [blog post](http://www.tannerhelland.com/4660/dithering-eleven-algorithms-source-code/) made by **Tanner Helland**. 5 | 6 | ##Definition 7 | *"Dither is an intentionally applied form of noise used to randomize quantization error, preventing large-scale patterns such as color banding in images."* [Wikipedia](https://en.wikipedia.org/wiki/Dither) 8 | 9 | ##What can I do with it 10 | With Unity you can use dithering e.g. to convert 32 bit textures to 16/8 bit textures in such way that new image doesn't look so bad to the human eye than it would if just color reduction was done to it. 11 | 12 | You can do this on Editor or on Device. 13 | 14 | ##How do I use it 15 | It is very simple. Just follow following example 16 | ```cs 17 | private static Color32 TrueColorToWebSafeColor(Color32 inputColor) 18 | { 19 | Color32 returnColor = new Color32( (byte)(Mathf.RoundToInt(inputColor.r / 51.0f) * 51), 20 | (byte)(Mathf.RoundToInt(inputColor.g / 51.0f) * 51), 21 | (byte)(Mathf.RoundToInt(inputColor.b / 51.0f) * 51), 22 | byte.MaxValue); 23 | return returnColor; 24 | } 25 | 26 | DitheringBase method = new FloydSteinbergDithering(TrueColorToWebSafeColor); 27 | Texture2D dithered = method.DoDithering(input); 28 | ``` 29 | First you have to have a color reduction function (in this case **TrueColorToWebSafeColor**). Then you create new instance of chosen algorithm (in this case **FloydSteinbergDithering**) and finally call DoDithering of dithering instance with chosen input Texture2D. It then returns the dithered Texture2D. 30 | 31 | ## License 32 | Text in this document and source code files are released into the public domain. See [PUBLICDOMAIN](https://github.com/mcraiha/Dithering-Unity3d/blob/master/PUBLICDOMAIN) file. 33 | 34 | Parrot image (half.png) is made from image that comes from [Kodak Lossless True Color Image Suite](http://r0k.us/graphics/kodak/) and it doesn't have any specific license. 35 | 36 | ##Sample image 37 | I took the famous [parrot image](http://r0k.us/graphics/kodak/kodim23.html) and reduced its size to 384x256. Then I ran the image (which has 64655 different colors) with all dithering methods and using Web safe colors as palette. I also calculated PSNR and SSIM values for dithered images. 38 | 39 | Resized image has filesize of 244 kB as PNG. All dithered files have filesize between 63-70 kB. 40 | 41 | ![Parrots](https://github.com/mcraiha/Dithering-Unity3d/blob/master/birds.png) 42 | 43 | ##.NET version 44 | If you need same functionality as .NET version, then head to https://github.com/mcraiha/CSharp-Dithering 45 | -------------------------------------------------------------------------------- /SierraDithering.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This file implements error pushing of dithering via (Frankie) Sierra kernel. 3 | 4 | This is free and unencumbered software released into the public domain. 5 | */ 6 | 7 | class SierraDithering : DitheringBase 8 | { 9 | public SierraDithering(FindColor colorfunc) : base(colorfunc) 10 | { 11 | this.methodLongName = "Sierra"; 12 | this.fileNameAddition = "_SIE"; 13 | } 14 | 15 | override protected void PushError(int x, int y, short[] quantError) 16 | { 17 | // Push error 18 | // X 5/32 3/32 19 | // 2/32 4/32 5/32 4/32 2/32 20 | // 2/32 3/32 2/32 21 | 22 | int xMinusOne = x - 1; 23 | int xMinusTwo = x - 2; 24 | int xPlusOne = x + 1; 25 | int xPlusTwo = x + 2; 26 | int yPlusOne = y + 1; 27 | int yPlusTwo = y + 2; 28 | 29 | // Current row 30 | int currentRow = y; 31 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 32 | { 33 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 5.0f / 32.0f); 34 | } 35 | 36 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 37 | { 38 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 3.0f / 32.0f); 39 | } 40 | 41 | // Next row 42 | currentRow = yPlusOne; 43 | if (this.IsValidCoordinate(xMinusTwo, currentRow)) 44 | { 45 | this.ModifyImageWithErrorAndMultiplier(xMinusTwo, currentRow, quantError, 2.0f / 32.0f); 46 | } 47 | 48 | if (this.IsValidCoordinate(xMinusOne, currentRow)) 49 | { 50 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, currentRow, quantError, 4.0f / 32.0f); 51 | } 52 | 53 | if (this.IsValidCoordinate(x, currentRow)) 54 | { 55 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, 5.0f / 32.0f); 56 | } 57 | 58 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 59 | { 60 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 4.0f / 32.0f); 61 | } 62 | 63 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 64 | { 65 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 2.0f / 32.0f); 66 | } 67 | 68 | // Next row 69 | currentRow = yPlusTwo; 70 | if (this.IsValidCoordinate(xMinusOne, currentRow)) 71 | { 72 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, currentRow, quantError, 2.0f / 32.0f); 73 | } 74 | 75 | if (this.IsValidCoordinate(x, currentRow)) 76 | { 77 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, 3.0f / 32.0f); 78 | } 79 | 80 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 81 | { 82 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 2.0f / 32.0f); 83 | } 84 | } 85 | } 86 | 87 | -------------------------------------------------------------------------------- /SierraLiteDithering.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This file implements error pushing of dithering via (Frankie) Sierra Lite kernel. 3 | 4 | This is free and unencumbered software released into the public domain. 5 | */ 6 | 7 | class SierraLiteDithering : DitheringBase 8 | { 9 | public SierraLiteDithering(FindColor colorfunc) : base(colorfunc) 10 | { 11 | this.methodLongName = "SierraLite"; 12 | this.fileNameAddition = "_SIEL"; 13 | } 14 | 15 | override protected void PushError(int x, int y, short[] quantError) 16 | { 17 | // Push error 18 | // X 2/4 19 | // 1/4 1/4 20 | 21 | int xMinusOne = x - 1; 22 | int xPlusOne = x + 1; 23 | int yPlusOne = y + 1; 24 | 25 | // Current row 26 | int currentRow = y; 27 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 28 | { 29 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 2.0f / 4.0f); 30 | } 31 | 32 | // Next row 33 | currentRow = yPlusOne; 34 | if (this.IsValidCoordinate(xMinusOne, currentRow)) 35 | { 36 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, currentRow, quantError, 1.0f / 4.0f); 37 | } 38 | 39 | if (this.IsValidCoordinate(x, currentRow)) 40 | { 41 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, 1.0f / 4.0f); 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /SierraTwoRowDithering.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This file implements error pushing of dithering via (Frankie) Sierra Two Row kernel. 3 | 4 | This is free and unencumbered software released into the public domain. 5 | */ 6 | 7 | class SierraTwoRowDithering : DitheringBase 8 | { 9 | public SierraTwoRowDithering(FindColor colorfunc) : base(colorfunc) 10 | { 11 | this.methodLongName = "SierraTwoRow"; 12 | this.fileNameAddition = "_SIE2R"; 13 | } 14 | 15 | override protected void PushError(int x, int y, short[] quantError) 16 | { 17 | // Push error 18 | // X 4/16 3/16 19 | // 1/16 2/16 3/16 2/16 1/16 20 | 21 | int xMinusOne = x - 1; 22 | int xMinusTwo = x - 2; 23 | int xPlusOne = x + 1; 24 | int xPlusTwo = x + 2; 25 | int yPlusOne = y + 1; 26 | 27 | // Current row 28 | int currentRow = y; 29 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 30 | { 31 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 4.0f / 16.0f); 32 | } 33 | 34 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 35 | { 36 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 3.0f / 16.0f); 37 | } 38 | 39 | // Next row 40 | currentRow = yPlusOne; 41 | if (this.IsValidCoordinate(xMinusTwo, currentRow)) 42 | { 43 | this.ModifyImageWithErrorAndMultiplier(xMinusTwo, currentRow, quantError, 1.0f / 16.0f); 44 | } 45 | 46 | if (this.IsValidCoordinate(xMinusOne, currentRow)) 47 | { 48 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, currentRow, quantError, 2.0f / 16.0f); 49 | } 50 | 51 | if (this.IsValidCoordinate(x, currentRow)) 52 | { 53 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, 3.0f / 16.0f); 54 | } 55 | 56 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 57 | { 58 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 2.0f / 16.0f); 59 | } 60 | 61 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 62 | { 63 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 1.0f / 16.0f); 64 | } 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /StuckiDithering.cs: -------------------------------------------------------------------------------- 1 | /* 2 | This file implements error pushing of dithering via (Peter) Stucki kernel. 3 | 4 | This is free and unencumbered software released into the public domain. 5 | */ 6 | 7 | class StuckiDithering : DitheringBase 8 | { 9 | public StuckiDithering(FindColor colorfunc) : base(colorfunc) 10 | { 11 | this.methodLongName = "Stucki"; 12 | this.fileNameAddition = "_STU"; 13 | } 14 | 15 | override protected void PushError(int x, int y, short[] quantError) 16 | { 17 | // Push error 18 | // X 8/42 4/42 19 | // 2/42 4/42 8/42 4/42 2/42 20 | // 1/42 2/42 4/42 2/42 1/42 21 | 22 | int xMinusOne = x - 1; 23 | int xMinusTwo = x - 2; 24 | int xPlusOne = x + 1; 25 | int xPlusTwo = x + 2; 26 | int yPlusOne = y + 1; 27 | int yPlusTwo = y + 2; 28 | 29 | // Current row 30 | int currentRow = y; 31 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 32 | { 33 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 8.0f / 42.0f); 34 | } 35 | 36 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 37 | { 38 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 4.0f / 42.0f); 39 | } 40 | 41 | // Next row 42 | currentRow = yPlusOne; 43 | if (this.IsValidCoordinate(xMinusTwo, currentRow)) 44 | { 45 | this.ModifyImageWithErrorAndMultiplier(xMinusTwo, currentRow, quantError, 2.0f / 42.0f); 46 | } 47 | 48 | if (this.IsValidCoordinate(xMinusOne, currentRow)) 49 | { 50 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, currentRow, quantError, 4.0f / 42.0f); 51 | } 52 | 53 | if (this.IsValidCoordinate(x, currentRow)) 54 | { 55 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, 8.0f / 42.0f); 56 | } 57 | 58 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 59 | { 60 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 4.0f / 42.0f); 61 | } 62 | 63 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 64 | { 65 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 2.0f / 42.0f); 66 | } 67 | 68 | // Next row 69 | currentRow = yPlusTwo; 70 | if (this.IsValidCoordinate(xMinusTwo, currentRow)) 71 | { 72 | this.ModifyImageWithErrorAndMultiplier(xMinusTwo, currentRow, quantError, 1.0f / 42.0f); 73 | } 74 | 75 | if (this.IsValidCoordinate(xMinusOne, currentRow)) 76 | { 77 | this.ModifyImageWithErrorAndMultiplier(xMinusOne, currentRow, quantError, 2.0f / 42.0f); 78 | } 79 | 80 | if (this.IsValidCoordinate(x, currentRow)) 81 | { 82 | this.ModifyImageWithErrorAndMultiplier(x, currentRow, quantError, 4.0f / 42.0f); 83 | } 84 | 85 | if (this.IsValidCoordinate(xPlusOne, currentRow)) 86 | { 87 | this.ModifyImageWithErrorAndMultiplier(xPlusOne, currentRow, quantError, 2.0f / 42.0f); 88 | } 89 | 90 | if (this.IsValidCoordinate(xPlusTwo, currentRow)) 91 | { 92 | this.ModifyImageWithErrorAndMultiplier(xPlusTwo, currentRow, quantError, 1.0f / 42.0f); 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /birds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcraiha/Dithering-Unity3d/e967be7257011da120be95c05dd2b4db84445a53/birds.png --------------------------------------------------------------------------------