public Color[] GetNearestColors(Color[] originalColors, ColorMatchMode colorMatchMode, float threshold, ColorEntry[] colorOperations, bool enableColorAdjustments, ColorAdjustments colorAdjustments) { if (originalColors == null) { return(null); } int len = originalColors.Length; Color[] newColors = new Color[len]; match.Clear(); if (colorOperations != null) { for (int k = 0; k < colorOperations.Length; k++) { int r = (int)(colorOperations[k].color.r * 255); int g = (int)(colorOperations[k].color.g * 255); int b = (int)(colorOperations[k].color.b * 255); int colorHash = (r << 16) + (g << 8) + b; if (colorOperations[k].operation == ColorOperation.Preserve) { match[colorHash] = colorOperations[k].color; } else if (colorOperations[k].operation == ColorOperation.Replace) { match[colorHash] = colorOperations[k].replaceColor; } } } Color[] palette = BuildPaletteColors(); for (int k = 0; k < MAX_THREADS; k++) { if (matchPool[k] == null) { matchPool[k] = new FastHashSet <Color>(); } matchPool[k].Clear(); } int threadCount = Mathf.Clamp(Mathf.CeilToInt((float)len / MAX_THREADS), 1, MAX_THREADS); Parallel.For(0, threadCount, index => { int start = len * index / threadCount; int end = (index == threadCount - 1) ? len : len * (index + 1) / threadCount; int lastHash = -1; Color nearest = Color.white; FastHashSet <Color> thisMatch = matchPool[index]; for (int k = start; k < end; k++) { int r = (int)(originalColors[k].r * 255); int g = (int)(originalColors[k].g * 255); int b = (int)(originalColors[k].b * 255); int colorHash = (r << 16) + (g << 8) + b; if (colorHash != lastHash) { if (!thisMatch.TryGetValue(colorHash, out nearest)) { lastHash = colorHash; nearest = GetNearestColor(palette, originalColors[k], colorMatchMode, threshold, colorOperations, enableColorAdjustments, colorAdjustments); thisMatch.Add(colorHash, nearest); } } nearest.a = originalColors[k].a; newColors[k] = nearest; } }); return(newColors); }
public Color[] GetNearestColors(Texture tex, ColorMatchMode colorMatchMode, float threshold, ColorEntry[] colorOperations, bool enableColorAdjustments, ColorAdjustments colorAdjustments) { if (tex == null || !(tex is Texture2D)) { return(null); } if (lastTexture == null || tex != lastTexture) { lastTexture = (Texture2D)tex; lastTexture.EnsureTextureIsReadable(); originalColors = lastTexture.GetPixels(); } return(GetNearestColors(originalColors, colorMatchMode, threshold, colorOperations, enableColorAdjustments, colorAdjustments)); }
/// <summary> /// Returns the nearest color in the palette /// </summary> public Color GetNearestColor(Color color, ColorMatchMode colorMatchMode, float threshold, ColorEntry[] colorOperations, bool enableColorAdjustments, ColorAdjustments colorAdjustments) { Color[] palette = BuildPaletteColors(); return(GetNearestColor(palette, color, colorMatchMode, threshold, colorOperations, enableColorAdjustments, colorAdjustments)); }
/// <summary> /// Returns the nearest color in the palette /// </summary> public Color GetNearestColor(Color[] palette, Color color, ColorMatchMode colorMatchMode, float threshold, ColorEntry[] colorOperations, bool enableColorAdjustments, ColorAdjustments colorAdjustments) { if (colorOperations != null && colorOperations.Length > 0) { HSLColor hsl = ColorConversion.GetHSLFromRGB(color.r, color.g, color.b); for (int k = 0; k < colorOperations.Length; k++) { switch (colorMatchMode) { case ColorMatchMode.RGB: { float dr = colorOperations[k].color.r < color.r ? color.r - colorOperations[k].color.r : colorOperations[k].color.r - color.r; float dg = colorOperations[k].color.g < color.g ? color.g - colorOperations[k].color.g : colorOperations[k].color.g - color.g; float db = colorOperations[k].color.b < color.b ? color.b - colorOperations[k].color.b : colorOperations[k].color.b - color.b; if (dr <= threshold && dg <= threshold && db <= threshold) { if (colorOperations[k].operation == ColorOperation.Preserve) { return(color); } else if (colorOperations[k].operation == ColorOperation.Replace) { return(colorOperations[k].replaceColor); } } } break; case ColorMatchMode.Red: { float dr = colorOperations[k].color.r < color.r ? color.r - colorOperations[k].color.r : colorOperations[k].color.r - color.r; if (dr <= threshold) { if (colorOperations[k].operation == ColorOperation.Preserve) { return(color); } else if (colorOperations[k].operation == ColorOperation.Replace) { return(colorOperations[k].replaceColor); } } } break; case ColorMatchMode.Green: { float dg = colorOperations[k].color.g < color.g ? color.g - colorOperations[k].color.g : colorOperations[k].color.g - color.g; if (dg <= threshold) { if (colorOperations[k].operation == ColorOperation.Preserve) { return(color); } else if (colorOperations[k].operation == ColorOperation.Replace) { return(colorOperations[k].replaceColor); } } } break; case ColorMatchMode.Blue: { float db = colorOperations[k].color.b < color.b ? color.b - colorOperations[k].color.b : colorOperations[k].color.b - color.b; if (db <= threshold) { if (colorOperations[k].operation == ColorOperation.Preserve) { return(color); } else if (colorOperations[k].operation == ColorOperation.Replace) { return(colorOperations[k].replaceColor); } } } break; case ColorMatchMode.HSL: { HSLColor hslOp = ColorConversion.GetHSLFromRGB(colorOperations[k].color.r, colorOperations[k].color.g, colorOperations[k].color.b); float dh = hslOp.h < hsl.h ? hsl.h - hslOp.h : hslOp.h - hsl.h; float ds = hslOp.s < hsl.s ? hsl.s - hslOp.s : hslOp.s - hsl.s; float dl = hslOp.l < hsl.l ? hsl.l - hslOp.l : hslOp.l - hsl.l; if (dh <= threshold && ds <= threshold && dl <= threshold) { if (colorOperations[k].operation == ColorOperation.Preserve) { return(color); } else if (colorOperations[k].operation == ColorOperation.Replace) { return(colorOperations[k].replaceColor); } } } break; case ColorMatchMode.Hue: { float hue = ColorConversion.GetHue(colorOperations[k].color.r, colorOperations[k].color.g, colorOperations[k].color.b); float dh = hue < hsl.h ? hsl.h - hue : hue - hsl.h; if (dh <= threshold) { if (colorOperations[k].operation == ColorOperation.Preserve) { return(color); } else if (colorOperations[k].operation == ColorOperation.Replace) { return(colorOperations[k].replaceColor); } } } break; case ColorMatchMode.Saturation: { float sat = ColorConversion.GetSaturation(colorOperations[k].color.r, colorOperations[k].color.g, colorOperations[k].color.b); float ds = sat < hsl.s ? hsl.s - sat : sat - hsl.s; if (ds <= threshold) { if (colorOperations[k].operation == ColorOperation.Preserve) { return(color); } else if (colorOperations[k].operation == ColorOperation.Replace) { return(colorOperations[k].replaceColor); } } } break; case ColorMatchMode.Lightness: { float l = ColorConversion.GetLightness(colorOperations[k].color.r, colorOperations[k].color.g, colorOperations[k].color.b); float dl = l < hsl.l ? hsl.l - l : l - hsl.l; if (dl <= threshold) { if (colorOperations[k].operation == ColorOperation.Preserve) { return(color); } else if (colorOperations[k].operation == ColorOperation.Replace) { return(colorOperations[k].replaceColor); } } } break; } } } float md = float.MaxValue; Color nearest = color; switch (colorMatchMode) { case ColorMatchMode.RGB: { for (int c = 0; c < palette.Length; c++) { float dr = (palette[c].r - color.r) * 0.299f; float dg = (palette[c].g - color.g) * 0.587f; float db = (palette[c].b - color.b) * 0.114f; float d = dr * dr + dg * dg + db * db; if (d < md) { md = d; nearest = palette[c]; } } } break; case ColorMatchMode.Red: { for (int c = 0; c < palette.Length; c++) { float dr = palette[c].r - color.r; float d = dr * dr; if (d < md) { md = d; nearest = palette[c]; } } } break; case ColorMatchMode.Green: { for (int c = 0; c < palette.Length; c++) { float dg = palette[c].g - color.g; float d = dg * dg; if (d < md) { md = d; nearest = palette[c]; } } } break; case ColorMatchMode.Blue: { for (int c = 0; c < palette.Length; c++) { float db = palette[c].b - color.b; float d = db * db; if (d < md) { md = d; nearest = palette[c]; } } } break; case ColorMatchMode.HSL: { HSLColor hsl = ColorConversion.GetHSLFromRGB(color.r, color.g, color.b); for (int c = 0; c < palette.Length; c++) { HSLColor paletteHSL = ColorConversion.GetHSLFromRGB(palette[c].r, palette[c].g, palette[c].b); float dh = (paletteHSL.h - hsl.h); float ds = (paletteHSL.s - hsl.s); float dl = (paletteHSL.l - hsl.l); float d = dh * dh + ds * ds + dl * dl; if (d < md) { md = d; nearest = palette[c]; } } } break; case ColorMatchMode.Hue: { float h = ColorConversion.GetHue(color.r, color.g, color.b); for (int c = 0; c < palette.Length; c++) { float paletteH = ColorConversion.GetHue(palette[c].r, palette[c].g, palette[c].b); float dh = (paletteH - h); float d = dh * dh; if (d < md) { md = d; nearest = palette[c]; } } } break; case ColorMatchMode.Saturation: { float s = ColorConversion.GetSaturation(color.r, color.g, color.b); for (int c = 0; c < palette.Length; c++) { float paletteS = ColorConversion.GetSaturation(palette[c].r, palette[c].g, palette[c].b); float dh = (paletteS - s); float d = dh * dh; if (d < md) { md = d; nearest = palette[c]; } } } break; case ColorMatchMode.Lightness: { float l = ColorConversion.GetLightness(color.r, color.g, color.b); for (int c = 0; c < palette.Length; c++) { float paletteL = ColorConversion.GetLightness(palette[c].r, palette[c].g, palette[c].b); float dh = (paletteL - l); float d = dh * dh; if (d < md) { md = d; nearest = palette[c]; } } } break; } nearest.a = color.a; color = nearest; if (enableColorAdjustments) { if (colorAdjustments.applyLUT && colorAdjustments.LUT != null) { color = ApplyLUT(color, colorAdjustments.LUT); } // vibrance float vibrance = colorAdjustments.vibrance; if (vibrance > 0) { float maxComponent = color.r > color.g ? color.r : color.g; maxComponent = color.b > maxComponent ? color.b : maxComponent; float minComponent = color.r < color.g ? color.r : color.g; minComponent = color.b < minComponent ? color.b : minComponent; float sat = maxComponent - minComponent; if (sat < 0) { sat = 0; } else if (sat > 1f) { sat = 1f; } float luma = color.GetLuma(); color.r *= 1f + vibrance * (1f - sat) * (color.r - luma); color.g *= 1f + vibrance * (1f - sat) * (color.g - luma); color.b *= 1f + vibrance * (1f - sat) * (color.b - luma); } // tinting float tintAmount = colorAdjustments.tintAmount; if (tintAmount > 0) { Color tintedColor = new Color(color.r * colorAdjustments.tintColor.r, color.g * colorAdjustments.tintColor.g, color.b * colorAdjustments.tintColor.b); color = Color.Lerp(color, tintedColor, tintAmount); } // contrast float contrast = colorAdjustments.contrast + 1f; if (contrast != 1f) { color.r = (color.r - 0.5f) * contrast + 0.5f; color.g = (color.g - 0.5f) * contrast + 0.5f; color.b = (color.b - 0.5f) * contrast + 0.5f; } // brightness float brightness = colorAdjustments.brightness + 1f; if (brightness != 1f) { color.r *= brightness; color.g *= brightness; color.b *= brightness; } } return(color); }
/// <summary> /// Returns a texture with colors that match the current palette /// </summary> public Texture2D GetNearestTexture(Texture tex, ColorMatchMode colorMatchMode, float threshold, ColorEntry[] colorOperations, bool enableColorAdjustments, ColorAdjustments colorAdjustments) { Color[] newColors = GetNearestColors(tex, colorMatchMode, threshold, colorOperations, enableColorAdjustments, colorAdjustments); if (newColors == null) { return(null); } Texture2D texture = (Texture2D)tex; Texture2D t = new Texture2D(texture.width, texture.height, TextureFormat.ARGB32, false); t.name = texture.name + "_ColorStudio"; t.filterMode = tex.filterMode; t.wrapMode = tex.wrapMode; #if UNITY_EDITOR t.alphaIsTransparency = texture.alphaIsTransparency; #endif t.SetPixels(newColors); t.Apply(); return(t); }