From 3f238c619eb1e8afbe0da3ecd2eb9fd524d1d8b0 Mon Sep 17 00:00:00 2001 From: jracek Date: Tue, 14 Jan 2025 20:47:01 +0100 Subject: [PATCH] Added dithering option to textures --- Editor/QuantizedPreviewWindow.cs | 9 +++++- Runtime/PSXTexture.cs | 53 ++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/Editor/QuantizedPreviewWindow.cs b/Editor/QuantizedPreviewWindow.cs index 1d56d23..fc7589b 100644 --- a/Editor/QuantizedPreviewWindow.cs +++ b/Editor/QuantizedPreviewWindow.cs @@ -14,8 +14,9 @@ public class QuantizedPreviewWindow : EditorWindow private int bpp = 4; private int targetWidth = 128; private int targetHeight = 128; + public bool dithering = true; private int maxKMeans = 100; - private int previewSize = 256; + private readonly int previewSize = 256; [MenuItem("Window/Quantized Preview")] public static void ShowWindow() @@ -33,6 +34,8 @@ public class QuantizedPreviewWindow : EditorWindow targetWidth = EditorGUILayout.IntField("Target Width", targetWidth); targetHeight = EditorGUILayout.IntField("Target Height", targetHeight); + dithering = EditorGUILayout.Toggle("Dithering", dithering); + bpp = EditorGUILayout.IntPopup("Bits Per Pixel", bpp, new[] { "4 bpp", "8 bpp", "16 bpp" }, new[] { 4, 8, 16 }); maxKMeans = EditorGUILayout.IntField("Max K-Means", maxKMeans); @@ -132,6 +135,10 @@ public class QuantizedPreviewWindow : EditorWindow { Texture2D resizedTexture = PSXTexture.ResizeTexture(originalTexture, targetWidth, targetHeight); + if(dithering) { + resizedTexture = PSXTexture.DitherTexture(resizedTexture); + } + if (bpp == 16) { quantizedTexture = null; diff --git a/Runtime/PSXTexture.cs b/Runtime/PSXTexture.cs index e126cf9..cd99f14 100644 --- a/Runtime/PSXTexture.cs +++ b/Runtime/PSXTexture.cs @@ -44,6 +44,10 @@ namespace PSXSplash.RuntimeCode Texture2D originalTexture = (Texture2D)texture; Texture2D newTexture = ResizeTexture(originalTexture, Width, Height); + if (Dithering) + { + newTexture = DitherTexture(newTexture); + } if (TextureType == PSXTextureType.TEX16_BPP) { ushort[] converted = ConvertTo16Bpp(newTexture); @@ -138,6 +142,55 @@ namespace PSXSplash.RuntimeCode return packedData; } + public static Texture2D DitherTexture(Texture2D sourceTexture, float threshold = 0.2f, float errorDiffusionStrength = 0.1f) + { + int width = sourceTexture.width; + int height = sourceTexture.height; + Color[] pixels = sourceTexture.GetPixels(); + Color[] ditheredPixels = new Color[pixels.Length]; + + for (int y = 0; y < height; y++) + { + for (int x = 0; x < width; x++) + { + int index = y * width + x; + Color pixel = pixels[index]; + + // Convert the pixel to grayscale + float gray = pixel.grayscale; + + // Apply threshold to determine if it's black or white + int dithered = (gray > threshold) ? 1 : 0; + + // Calculate the error as the difference between the grayscale value and the dithered result + float error = gray - dithered; + + // Store the dithered pixel + ditheredPixels[index] = new Color(dithered, dithered, dithered); + + // Spread the error to neighboring pixels with customizable error diffusion strength + if (x + 1 < width) pixels[(y * width) + (x + 1)] += new Color(error * 7f / 16f * errorDiffusionStrength, error * 7f / 16f * errorDiffusionStrength, error * 7f / 16f * errorDiffusionStrength); + if (y + 1 < height) pixels[((y + 1) * width) + x] += new Color(error * 3f / 16f * errorDiffusionStrength, error * 3f / 16f * errorDiffusionStrength, error * 3f / 16f * errorDiffusionStrength); + if (x - 1 >= 0 && y + 1 < height) pixels[((y + 1) * width) + (x - 1)] += new Color(error * 5f / 16f * errorDiffusionStrength, error * 5f / 16f * errorDiffusionStrength, error * 5f / 16f * errorDiffusionStrength); + if (x + 1 < width && y + 1 < height) pixels[((y + 1) * width) + (x + 1)] += new Color(error * 1f / 16f * errorDiffusionStrength, error * 1f / 16f * errorDiffusionStrength, error * 1f / 16f * errorDiffusionStrength); + } + } + + // Clamp the final pixel values to ensure they are valid colors + for (int i = 0; i < pixels.Length; i++) + { + pixels[i].r = Mathf.Clamp01(pixels[i].r); + pixels[i].g = Mathf.Clamp01(pixels[i].g); + pixels[i].b = Mathf.Clamp01(pixels[i].b); + } + + // Create the resulting dithered texture + Texture2D ditheredTexture = new Texture2D(width, height); + ditheredTexture.SetPixels(pixels); + ditheredTexture.Apply(); + + return ditheredTexture; + } } }