/// <summary> /// Creates and saves a palette image from the unique colors in another image. /// /// The palette image structure: /// /// | | | | | | | | row of 16 16x16 squares with the third mix color /// |__|__|__|__|__|__|__|__ /// | | | | | | | | row of 16 16x16 squares with the second mix color /// |__|__|__|__|__|__|__|__ /// | | | | | | | | row of 16 16x16 squares with the first mix color /// |__|__|__|__|__|__|__|__ /// /// Each horizontal square has a fixed Red component: 0/15 to 15/15. /// The blue component increases from 0 to 1 horizontally over a square. /// The green component increases from 0 to 1 vertically over a square. /// /// The palette image is used in the shaders to convert a truecolor to several dithered colors. /// The truecolor RGB components points to N colors in the palette, one color per row of squares. /// The N colors are mixed together in a dithering pattern to produce a close approximation of the original truecolor. /// /// The steps to create the palette image: /// /// 1. Load the color image to a Texture2D /// 2. Create a list of all the unique colors in the color image /// 3. Create the palette image /// 4. a) Loop through each pixel in the palette image and determine the truecolor for that pixel /// b) Device a mixing plan to achieve the truecolor /// c) Save the N colors in the square column /// 5. Save the palette image /// </summary> private void CreatePalette() { if (script.MixedColorCount < 1) return; // Load the color image to a Texture2D Texture2D colorTexture = LoadTexture(); if (colorTexture == null) return; // Create a list of all the unique colors in the color image List<Color> paletteColors = new List<Color>(); bool proceed = false; for (int x = 0; x < colorTexture.width; x++) for (int y = 0; y < colorTexture.height; y++) if (!paletteColors.Contains(colorTexture.GetPixel(x, y))) { paletteColors.Add(colorTexture.GetPixel(x, y)); if (paletteColors.Count > MaxColors && !proceed) { proceed = EditorUtility.DisplayDialog("Error", "Source image contains more than " + MaxColors + " colors. Continuing may lock up Unity for a long time", "Continue", "Stop"); if (!proceed) return; } } uint[] palette = new uint[paletteColors.Count]; for (int i = 0; i < paletteColors.Count; i++) palette[i] = ColorToInt(paletteColors[i]); MixingPlanner mixingPlanner = new MixingPlanner(palette); // Create the palette image int height = (int)Math.Pow(2, Math.Ceiling(Math.Log(ColorSquares * script.MixedColorCount - 1) / Math.Log(2))); Texture2D paletteTexture = new Texture2D(ColorSquares * ColorSquares, height, TextureFormat.RGB24, false); paletteTexture.name = colorTexture.name + "_pal_" + script.MixedColorCount; // Loop through each pixel in the palette image and determine the target color for that pixel for (int x = 0; x < ColorSquares * ColorSquares; x++) for (int y = 0; y < ColorSquares; y++) { byte r = (byte)((float)(x / ColorSquares) / (ColorSquares - 1) * 255); byte g = (byte)((float)y / (ColorSquares - 1) * 255); byte b = (byte)(((float)x % ColorSquares) / (ColorSquares - 1) * 255); uint targetColor = (uint)((r << 16) + (g << 8) + b); // Device a mixing plan to achieve the truecolor uint[] mixingPlan = mixingPlanner.DeviseBestMixingPlan(targetColor, (uint)script.MixedColorCount); // Save the N colors in the square column for (int c = 0; c < script.MixedColorCount; c++) paletteTexture.SetPixel(x, y + (script.MixedColorCount - c - 1) * ColorSquares, paletteColors[(int)mixingPlan[c]]); } // Save the palette image SaveTexture(paletteTexture); }
/* PALETTE GENERATION */ #region Palette generation /// <summary> /// Creates and saves a palette image from the unique colors in another image. /// /// The palette image structure: /// /// | | | | | | | | row of 16 16x16 squares with the third mix color /// |__|__|__|__|__|__|__|__ /// | | | | | | | | row of 16 16x16 squares with the second mix color /// |__|__|__|__|__|__|__|__ /// | | | | | | | | row of 16 16x16 squares with the first mix color /// |__|__|__|__|__|__|__|__ /// /// Each horizontal square has a fixed Red component: 0/15 to 15/15. /// The blue component increases from 0 to 1 horizontally over a square. /// The green component increases from 0 to 1 vertically over a square. /// /// The palette image is used in the shaders to convert a truecolor to several dithered colors. /// The truecolor RGB components points to N colors in the palette, one color per row of squares. /// The N colors are mixed together in a dithering pattern to produce a close approximation of the original truecolor. /// /// The steps to create the palette image: /// /// 1. Load the color image to a Texture2D /// 2. Create a list of all the unique colors in the color image /// 3. Create the palette image /// 4. a) Loop through each pixel in the palette image and determine the truecolor for that pixel /// b) Device a mixing plan to achieve the truecolor /// c) Save the N colors in the square column /// 5. Save the palette image /// </summary> private void GeneratePaletteTexture(Palette palette) { if (palette.Colors.Count == 0) { Debug.LogError(ContentText.emptyColorListError); return; } uint[] paletteColors = ConvertColorListToPaletteList(palette.Colors); MixingPlanner mixingPlanner = new MixingPlanner(paletteColors); // Create the palette image int height = (int)Math.Pow(2, Math.Ceiling(Math.Log(_colorSquares * CurrentPalette.MixedColorCount - 1) / Math.Log(2))); if (palette.Texture == null) { palette.Texture = new Texture2D(_colorSquares * _colorSquares, height, TextureFormat.RGB24, false); palette.Texture.name = "Palette Texture"; palette.Texture.filterMode = FilterMode.Point; palette.Texture.wrapMode = TextureWrapMode.Repeat; AssetDatabase.AddObjectToAsset(palette.Texture, palette); AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(palette.Texture)); } else if (palette.Texture.height != height) { palette.Texture.Resize(_colorSquares * _colorSquares, height); } // Fill empty rows with magenta int x = 0, y = 0; for (int i = 0; i < palette.Texture.height * palette.Texture.width; i++) { x = i / palette.Texture.height; y = i % palette.Texture.width; palette.Texture.SetPixel(x, y, Color.magenta); } // Loop through each pixel in the palette image and determine the target color for that pixel for (x = 0; x < _colorSquares * _colorSquares; x++) { for (y = 0; y < _colorSquares; y++) { byte r = (byte)((float)(x / _colorSquares) / (_colorSquares - 1) * 255); byte g = (byte)((float)y / (_colorSquares - 1) * 255); byte b = (byte)(((float)x % _colorSquares) / (_colorSquares - 1) * 255); uint targetColor = (uint)((r << 16) + (g << 8) + b); // Device a mixing plan to achieve the truecolor uint[] mixingPlan = mixingPlanner.DeviseBestMixingPlan(targetColor, (uint)CurrentPalette.MixedColorCount); // Save the N colors in the square column for (int c = 0; c < CurrentPalette.MixedColorCount; c++) { palette.Texture.SetPixel(x, y + (CurrentPalette.MixedColorCount - c - 1) * _colorSquares, palette.Colors[(int)mixingPlan[c]]); } } } palette.Texture.Apply(); palette.HasTexture = true; // Save the palette image // SaveTexture(texture); }
/// <summary> /// Creates and saves a palette image from the unique colors in another image. /// /// The palette image structure: /// /// | | | | | | | | row of 16 16x16 squares with the third mix color /// |__|__|__|__|__|__|__|__ /// | | | | | | | | row of 16 16x16 squares with the second mix color /// |__|__|__|__|__|__|__|__ /// | | | | | | | | row of 16 16x16 squares with the first mix color /// |__|__|__|__|__|__|__|__ /// /// Each horizontal square has a fixed Red component: 0/15 to 15/15. /// The blue component increases from 0 to 1 horizontally over a square. /// The green component increases from 0 to 1 vertically over a square. /// /// The palette image is used in the shaders to convert a truecolor to several dithered colors. /// The truecolor RGB components points to N colors in the palette, one color per row of squares. /// The N colors are mixed together in a dithering pattern to produce a close approximation of the original truecolor. /// /// The steps to create the palette image: /// /// 1. Load the color image to a Texture2D /// 2. Create a list of all the unique colors in the color image /// 3. Create the palette image /// 4. a) Loop through each pixel in the palette image and determine the truecolor for that pixel /// b) Device a mixing plan to achieve the truecolor /// c) Save the N colors in the square column /// 5. Save the palette image /// </summary> private void CreatePalette() { // Load the color image to a Texture2D Texture2D colorTexture = LoadTexture(); if (colorTexture == null) { return; } // Create a list of all the unique colors in the color image List <Color> paletteColors = new List <Color>(); for (int x = 0; x < colorTexture.width; x++) { for (int y = 0; y < colorTexture.height; y++) { if (!paletteColors.Contains(colorTexture.GetPixel(x, y))) { paletteColors.Add(colorTexture.GetPixel(x, y)); } } } uint[] palette = new uint[paletteColors.Count]; for (int i = 0; i < paletteColors.Count; i++) { palette[i] = ColorToInt(paletteColors[i]); } MixingPlanner mixingPlanner = new MixingPlanner(palette); // Create the palette image int height = (int)Math.Pow(2, Math.Ceiling(Math.Log(ColorSquares * script.MixedColorCount - 1) / Math.Log(2))); Texture2D paletteTexture = new Texture2D(ColorSquares * ColorSquares, height, TextureFormat.RGB24, false); paletteTexture.name = colorTexture.name + "_pal_" + script.MixedColorCount; // Loop through each pixel in the palette image and determine the target color for that pixel for (int x = 0; x < ColorSquares * ColorSquares; x++) { for (int y = 0; y < ColorSquares; y++) { byte r = (byte)((float)(x / ColorSquares) / (ColorSquares - 1) * 255); byte g = (byte)((float)y / (ColorSquares - 1) * 255); byte b = (byte)(((float)x % ColorSquares) / (ColorSquares - 1) * 255); uint targetColor = (uint)((r << 16) + (g << 8) + b); // Device a mixing plan to achieve the truecolor uint[] mixingPlan = mixingPlanner.DeviseBestMixingPlan(targetColor, (uint)script.MixedColorCount); // Save the N colors in the square column for (int c = 0; c < script.MixedColorCount; c++) { paletteTexture.SetPixel(x, y + (script.MixedColorCount - c - 1) * ColorSquares, paletteColors[(int)mixingPlan[c]]); } } } // Save the palette image SaveTexture(paletteTexture); }