private static RGBColour[] OptimiseRGB(RGBColour[] Colour, int uSteps)
{
float[] pC = uSteps == 3 ? pC3 : pC4;
float[] pD = uSteps == 3 ? pD3 : pD4;
// Find min max
RGBColour X = Luminance;
RGBColour Y = new RGBColour();
for (int i = 0; i < Colour.Length; i++)
{
RGBColour current = Colour[i];
// X = min, Y = max
if (current.r < X.r)
X.r = current.r;
if (current.g < X.g)
X.g = current.g;
if (current.b < X.b)
X.b = current.b;
if (current.r > Y.r)
Y.r = current.r;
if (current.g > Y.g)
Y.g = current.g;
if (current.b > Y.b)
Y.b = current.b;
}
// Diagonal axis - starts with difference between min and max
RGBColour diag = new RGBColour()
{
r = Y.r - X.r,
g = Y.g - X.g,
b = Y.b - X.b
};
float fDiag = diag.r * diag.r + diag.g * diag.g + diag.b * diag.b;
if (fDiag < 1.175494351e-38F)
{
RGBColour min1 = new RGBColour()
{
r = X.r,
g = X.g,
b = X.b
};
RGBColour max1 = new RGBColour()
{
r = Y.r,
g = Y.g,
b = Y.b
};
return new RGBColour[] { min1, max1 };
}
float FdiagInv = 1f / fDiag;
RGBColour Dir = new RGBColour()
{
r = diag.r * FdiagInv,
g = diag.g * FdiagInv,
b = diag.b * FdiagInv
};
RGBColour Mid = new RGBColour()
{
r = (X.r + Y.r) * .5f,
g = (X.g + Y.g) * .5f,
b = (X.b + Y.b) * .5f
};
float[] fDir = new float[4];
for (int i = 0; i < Colour.Length; i++)
{
RGBColour pt = new RGBColour()
{
r = Dir.r * (Colour[i].r - Mid.r),
g = Dir.g * (Colour[i].g - Mid.g),
b = Dir.b * (Colour[i].b - Mid.b)
};
float f = 0;
f = pt.r + pt.g + pt.b;
fDir[0] += f * f;
f = pt.r + pt.g - pt.b;
fDir[1] += f * f;
f = pt.r - pt.g + pt.b;
fDir[2] += f * f;
f = pt.r - pt.g - pt.b;
fDir[3] += f * f;
}
float fDirMax = fDir[0];
int iDirMax = 0;
for (int iDir = 1; iDir < 4; iDir++)
{
if (fDir[iDir] > fDirMax)
{
fDirMax = fDir[iDir];
iDirMax = iDir;
}
}
if ((iDirMax & 2) != 0)
{
float f = X.g;
X.g = Y.g;
Y.g = f;
}
if ((iDirMax & 1) != 0)
{
float f = X.b;
X.b = Y.b;
Y.b = f;
}
if (fDiag < 1f / 4096f)
{
RGBColour min1 = new RGBColour()
{
r = X.r,
g = X.g,
b = X.b
};
RGBColour max1 = new RGBColour()
{
r = Y.r,
g = Y.g,
b = Y.b
};
return new RGBColour[] { min1, max1 };
}
// newtons method for local min of sum of squares error.
float fsteps = uSteps - 1;
for (int iteration = 0; iteration < 8; iteration++)
{
RGBColour[] pSteps = new RGBColour[4];
for (int iStep = 0; iStep < uSteps; iStep++)
{
pSteps[iStep].r = X.r * pC[iStep] + Y.r * pD[iStep];
pSteps[iStep].g = X.g * pC[iStep] + Y.g * pD[iStep];
pSteps[iStep].b = X.b * pC[iStep] + Y.b * pD[iStep];
}
// colour direction
Dir.r = Y.r - X.r;
Dir.g = Y.g - X.g;
Dir.b = Y.b - X.b;
float fLen = Dir.r * Dir.r + Dir.g * Dir.g + Dir.b * Dir.b;
if (fLen < (1f / 4096f))
break;
float fScale = fsteps / fLen;
Dir.r *= fScale;
Dir.g *= fScale;
Dir.b *= fScale;
// Evaluate function and derivatives
float d2X = 0, d2Y = 0;
RGBColour dX, dY;
dX = new RGBColour();
dY = new RGBColour();
for (int i = 0; i < Colour.Length; i++)
{
RGBColour current = Colour[i];
float fDot = (current.r - X.r) * Dir.r + (current.g - X.g) * Dir.g + (current.b - X.b) * Dir.b;
int iStep = 0;
if (fDot <= 0)
iStep = 0;
else if (fDot >= fsteps)
iStep = uSteps - 1;
else
iStep = (int)(fDot + .5f);
RGBColour diff = new RGBColour()
{
r = pSteps[iStep].r - current.r,
g = pSteps[iStep].g - current.g,
b = pSteps[iStep].b - current.b
};
float fC = pC[iStep] * 1f / 8f;
float fD = pD[iStep] * 1f / 8f;
d2X += fC * pC[iStep];
dX.r += fC * diff.r;
dX.g += fC * diff.g;
dX.b += fC * diff.b;
d2Y += fD * pD[iStep];
dY.r += fD * diff.r;
dY.g += fD * diff.g;
dY.b += fD * diff.b;
}
// Move endpoints
if (d2X > 0f)
{
float f = -1f / d2X;
X.r += dX.r * f;
X.g += dX.g * f;
X.b += dX.b * f;
}
if (d2Y > 0f)
{
float f = -1f / d2Y;
Y.r += dY.r * f;
Y.g += dY.g * f;
Y.b += dY.b * f;
}
float fEpsilon = (0.25f / 64.0f) * (0.25f / 64.0f);
if ((dX.r * dX.r < fEpsilon) && (dX.g * dX.g < fEpsilon) && (dX.b * dX.b < fEpsilon) &&
(dY.r * dY.r < fEpsilon) && (dY.g * dY.g < fEpsilon) && (dY.b * dY.b < fEpsilon))
{
break;
}
}
RGBColour min = new RGBColour()
{
r = X.r,
g = X.g,
b = X.b
};
RGBColour max = new RGBColour()
{
r = Y.r,
g = Y.g,
b = Y.b
};
return new RGBColour[] { min, max };
}