private ulong FindClosest(Bc4BlockData data) { ulong ret = 0; for (int i = 0; i < data.Values.Length; ++i) { var v = data.Values[i]; int iBest = 0; float bestDelta = Math.Abs(data.InterpretedValues[0] - v); for (int j = 1; j < data.InterpretedValues.Length; j++) { float delta = Math.Abs(data.InterpretedValues[j] - v); if (delta < bestDelta) { iBest = j; bestDelta = delta; } } int shift = 16 + 3 * i; ret |= (ulong)iBest << shift; } return(ret); }
/// <summary> /// Encode a block of unsigned Values. /// </summary> /// <returns></returns> public BC4UBlock EncodeUnsigned(Bc4BlockData data) { //load the input and scan for the boundary condition ClampAndFindRange(data, 0F, 1F); var hasEndPoint = data.MinValue == 0F || data.MaxValue == 1F; //find a span across the space SpanValues(hasEndPoint, false, data, out var r0, out var r1); //roundtrip it through integer format var ret = new BC4UBlock { R0 = Helpers.FloatToUNorm(r0), R1 = Helpers.FloatToUNorm(r1) }; ret.GetPalette(data.InterpretedValues); ret.PackedValue |= FindClosest(data); return(ret); }
private void ClampAndFindRange(Bc4BlockData data, float clampMin, float clampMax) { var target = data.Values; var v0 = target[0]; if (v0 < clampMin) { target[0] = v0 = clampMin; } else if (v0 > clampMax) { target[0] = v0 = clampMax; } data.MinValue = data.MaxValue = v0; for (int i = 1; i < target.Length; i++) { var v = target[i]; if (v < clampMin) { target[i] = v = clampMin; } else if (v > clampMax) { target[i] = v = clampMax; } if (v < data.MinValue) { data.MinValue = v; } else if (v > data.MaxValue) { data.MaxValue = v; } } }
private void SpanValues(bool isSixPointInterpreter, bool isSigned, Bc4BlockData data, out float r0, out float r1) { //pulled from the original OptimizeAlpha code in the D3DX sample code float[] pC, pD; if (isSixPointInterpreter) { pC = pC6; pD = pD6; } else { pC = pC8; pD = pD8; } float rangeMin = isSigned ? -1F : 0F; const float rangeMax = 1F; //find min and max points as a starting solution float vMin, vMax; if (isSixPointInterpreter) { vMin = rangeMax; vMax = rangeMin; for (int i = 0; i < data.Values.Length; i++) { var v = data.Values[i]; if (v == rangeMin || v == rangeMax) { continue; } if (v < vMin) { vMin = v; } if (v > vMax) { vMax = v; } } if (vMin == vMax) { vMax = rangeMax; } } else { vMin = data.MinValue; vMax = data.MaxValue; } // Use Newton's Method to find local minima of sum-of-squares Error. int numSteps = isSixPointInterpreter ? 6 : 8; float fSteps = numSteps - 1; for (int iteration = 0; iteration < 8; iteration++) { if ((vMax - vMin) < (1.0f / 256.0f)) { break; } float fScale = fSteps / (vMax - vMin); // Calculate new steps for (int i = 0; i < numSteps; i++) { data.InterpretedValues[i] = pC[i] * vMin + pD[i] * vMax; } if (isSixPointInterpreter) { data.InterpretedValues[6] = rangeMin; data.InterpretedValues[7] = rangeMax; } // Evaluate function, and derivatives float dX = 0F; float dY = 0F; float d2X = 0F; float d2Y = 0F; for (int iPoint = 0; iPoint < data.Values.Length; iPoint++) { float dot = (data.Values[iPoint] - vMin) * fScale; int iStep; if (dot <= 0.0f) { iStep = ((6 == numSteps) && (data.Values[iPoint] <= vMin * 0.5f)) ? 6 : 0; } else if (dot >= fSteps) { iStep = ((6 == numSteps) && (data.Values[iPoint] >= (vMax + 1.0f) * 0.5f)) ? 7 : (numSteps - 1); } else { iStep = (int)(dot + 0.5f); } if (iStep < numSteps) { // D3DX had this computation backwards (pPoints[iPoint] - pSteps[iStep]) // this fix improves RMS of the alpha component float fDiff = data.InterpretedValues[iStep] - data.Values[iPoint]; dX += pC[iStep] * fDiff; d2X += pC[iStep] * pC[iStep]; dY += pD[iStep] * fDiff; d2Y += pD[iStep] * pD[iStep]; } } // Move endpoints if (d2X > 0.0f) { vMin -= dX / d2X; } if (d2Y > 0.0f) { vMax -= dY / d2Y; } if (vMin > vMax) { float f = vMin; vMin = vMax; vMax = f; } if ((dX * dX < (1.0f / 64.0f)) && (dY * dY < (1.0f / 64.0f))) { break; } } vMin = (vMin < rangeMin) ? rangeMin : (vMin > rangeMax) ? rangeMax : vMin; vMax = (vMax < rangeMin) ? rangeMin : (vMax > rangeMax) ? rangeMax : vMax; if (isSixPointInterpreter) { r0 = vMin; r1 = vMax; } else { r0 = vMax; r1 = vMin; } }