Preserve original brightness when remapping player colors.

This commit is contained in:
Paul Chote
2021-04-10 18:12:50 +01:00
committed by teinarss
parent 9d62ce214c
commit 4042d5b179
5 changed files with 34 additions and 43 deletions

View File

@@ -18,43 +18,34 @@ namespace OpenRA.Graphics
{
public class PlayerColorRemap : IPaletteRemap
{
Dictionary<int, Color> remapColors;
readonly int[] remapIndices;
readonly float hue;
readonly float saturation;
public static int GetRemapIndex(int[] ramp, int i)
public PlayerColorRemap(int[] remapIndices, float hue, float saturation)
{
return ramp[i];
}
public PlayerColorRemap(int[] ramp, Color c, float rampFraction)
{
var h = c.GetHue() / 360.0f;
var s = c.GetSaturation();
var l = c.GetBrightness();
// Increase luminosity if required to represent the full ramp
var rampRange = (byte)((1 - rampFraction) * l);
var c1 = Color.FromAhsl(h, s, Math.Max(rampRange, l));
var c2 = Color.FromAhsl(h, s, (byte)Math.Max(0, l - rampRange));
var baseIndex = ramp[0];
var remapRamp = ramp.Select(r => r - ramp[0]);
var rampMaxIndex = ramp.Length - 1;
// reversed remapping
if (ramp[0] > ramp[rampMaxIndex])
{
baseIndex = ramp[rampMaxIndex];
for (var i = rampMaxIndex; i > 0; i--)
remapRamp = ramp.Select(r => r - ramp[rampMaxIndex]);
}
remapColors = remapRamp.Select((x, i) => (baseIndex + i, Exts.ColorLerp(x / (float)ramp.Length, c1, c2)))
.ToDictionary(u => u.Item1, u => u.Item2);
this.remapIndices = remapIndices;
this.hue = hue;
this.saturation = saturation;
}
public Color GetRemappedColor(Color original, int index)
{
return remapColors.TryGetValue(index, out var c)
? c : original;
if (!remapIndices.Contains(index))
return original;
// Color remapping is applied in a linear color space, so start
// by undoing the pre-multiplied alpha and gamma corrections
var (r, g, b) = original.ToLinear();
// Calculate the brightness (i.e HSV value) of the original colour
var value = Math.Max(Math.Max(r, g), b);
// Construct the new RGB color
(r, g, b) = Color.HsvToRgb(hue, saturation, value);
// Convert linear back to SRGB and pre-multiply by the alpha
return Color.FromLinear(original.A, r, g, b);
}
}
}

View File

@@ -29,9 +29,6 @@ namespace OpenRA.Traits
[Desc("Remap these indices to player colors.")]
public readonly int[] RemapIndex = { };
[Desc("Luminosity range to span.")]
public readonly float Ramp = 0.05f;
[Desc("Allow palette modifiers to change the palette.")]
public readonly bool AllowModifiers = true;
@@ -49,7 +46,9 @@ namespace OpenRA.Traits
public void LoadPlayerPalettes(WorldRenderer wr, string playerName, Color color, bool replaceExisting)
{
var remap = new PlayerColorRemap(info.RemapIndex, color, info.Ramp);
color.ToAhsv(out _, out var h, out var s, out _);
var remap = new PlayerColorRemap(info.RemapIndex, h, s);
var pal = new ImmutablePalette(wr.Palette(info.BasePalette).Palette, remap);
wr.AddPalette(info.BaseName + playerName, pal, info.AllowModifiers, replaceExisting);
}

View File

@@ -31,9 +31,6 @@ namespace OpenRA.Traits
[Desc("The fixed color to remap.")]
public readonly Color Color;
[Desc("Luminosity range to span.")]
public readonly float Ramp = 0.05f;
[Desc("Allow palette modifiers to change the palette.")]
public readonly bool AllowModifiers = true;
@@ -51,7 +48,9 @@ namespace OpenRA.Traits
public void LoadPalettes(WorldRenderer wr)
{
var remap = new PlayerColorRemap(info.RemapIndex, info.Color, info.Ramp);
info.Color.ToAhsv(out _, out var h, out var s, out _);
var remap = new PlayerColorRemap(info.RemapIndex, h, s);
wr.AddPalette(info.Name, new ImmutablePalette(wr.Palette(info.Base).Palette, remap), info.AllowModifiers);
}
}

View File

@@ -54,8 +54,7 @@ namespace OpenRA.Mods.Cnc.UtilityCommands
// the remap range is always 16 entries, but their location and order changes
for (var i = 0; i < 16; i++)
remap[PlayerColorRemap.GetRemapIndex(srcRemapIndex, i)]
= PlayerColorRemap.GetRemapIndex(destRemapIndex, i);
remap[srcRemapIndex[i]] = destRemapIndex[i];
// map everything else to the best match based on channel-wise distance
var srcPalette = new ImmutablePalette(args[1].Split(':')[1], new[] { 0 }, shadowIndex);

View File

@@ -40,16 +40,19 @@ namespace OpenRA.Mods.Common.Traits
"A dictionary of [faction name]: [actor name].")]
public readonly Dictionary<string, string> FactionPreviewActors = new Dictionary<string, string>();
[Desc("Remap these indices to player colors.")]
public readonly int[] RemapIndices = { };
public readonly float Ramp = 0.05f;
public Color Color { get; private set; }
public void Update(WorldRenderer worldRenderer, Color color)
{
Color = color;
Color.ToAhsv(out _, out var h, out var s, out _);
var newPalette = new MutablePalette(worldRenderer.Palette(PaletteName).Palette);
newPalette.ApplyRemap(new PlayerColorRemap(RemapIndices, Color, Ramp));
newPalette.ApplyRemap(new PlayerColorRemap(RemapIndices, h, s));
worldRenderer.ReplacePalette(PaletteName, newPalette);
}