Example #1
0
        private void SpanValues(Bc1BlockData block, out RgbF32 r0, out RgbF32 r1)
        {
            bool hasAlpha = block.AlphaMask != 0;

            int cSteps = hasAlpha ? 3 : 4;
            var pC     = hasAlpha ? pC3 : pC4;
            var pD     = hasAlpha ? pD3 : pD4;

            var values = block.QuantizedValues;

            // Find Min and Max points, as starting point
            RgbF32 X = UseUniformWeighting ? new RgbF32(1, 1, 1) :
                       new RgbF32(RWeight, GWeight, BWeight);
            RgbF32 Y = new RgbF32(0, 0, 0);

            for (int i = 0; i < values.Length; i++)
            {
                var v = values[i];

#if COLOR_WEIGHTS
                if ((block.AlphaMask & (1 << i)) != 0)
#endif
                {
                    if (v.R < X.R)
                    {
                        X.R = v.R;
                    }
                    if (v.G < X.G)
                    {
                        X.G = v.G;
                    }

                    if (v.B < X.B)
                    {
                        X.B = v.B;
                    }
                    if (v.R > Y.R)
                    {
                        Y.R = v.R;
                    }

                    if (v.G > Y.G)
                    {
                        Y.G = v.G;
                    }
                    if (v.B > Y.B)
                    {
                        Y.B = v.B;
                    }
                }
            }

            // Diagonal axis
            RgbF32 AB;

            AB.R = Y.R - X.R;
            AB.G = Y.G - X.G;
            AB.B = Y.B - X.B;

            float fAB = AB.R * AB.R + AB.G * AB.G + AB.B * AB.B;

            // Single color block.. no need to root-find
            if (fAB < float.Epsilon)
            {
                r0 = X;
                r1 = Y;
                return;
            }

            // Try all four axis directions, to determine which diagonal best fits data
            float fABInv = 1.0f / fAB;

            RgbF32 Dir;
            Dir.R = AB.R * fABInv;
            Dir.G = AB.G * fABInv;
            Dir.B = AB.B * fABInv;

            RgbF32 Mid;
            Mid.R = (X.R + Y.R) * 0.5f;
            Mid.G = (X.G + Y.G) * 0.5f;
            Mid.B = (X.B + Y.B) * 0.5f;

            block.FDir[0] = block.FDir[1] = block.FDir[2] = block.FDir[3] = 0.0F;

            for (int i = 0; i < values.Length; i++)
            {
                var v = values[i];

                RgbF32 Pt;
                Pt.R = (v.R - Mid.R) * Dir.R;
                Pt.G = (v.G - Mid.G) * Dir.G;
                Pt.B = (v.B - Mid.B) * Dir.B;

                float f;

#if COLOR_WEIGHTS
                f              = Pt.R + Pt.G + Pt.B;
                block.FDir[0] += v.a * f * f;

                f              = Pt.R + Pt.G - Pt.B;
                block.FDir[1] += v.a * f * f;

                f              = Pt.R - Pt.G + Pt.B;
                block.FDir[2] += v.a * f * f;

                f              = Pt.R - Pt.G - Pt.B;
                block.FDir[3] += v.a * f * f;
#else
                f              = Pt.R + Pt.G + Pt.B;
                block.FDir[0] += f * f;

                f              = Pt.R + Pt.G - Pt.B;
                block.FDir[1] += f * f;

                f              = Pt.R - Pt.G + Pt.B;
                block.FDir[2] += f * f;

                f              = Pt.R - Pt.G - Pt.B;
                block.FDir[3] += f * f;
#endif
            }

            float fDirMax = block.FDir[0];
            int   iDirMax = 0;

            for (int iDir = 1; iDir < block.FDir.Length; iDir++)
            {
                var d = block.FDir[iDir];
                if (d > fDirMax)
                {
                    fDirMax = d;
                    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;
            }


            // Two color block.. no need to root-find
            if (fAB < 1.0f / 4096.0f)
            {
                r0 = X;
                r1 = Y;
                return;
            }

            // Use Newton's Method to find local minima of sum-of-squares Error.
            float fSteps = (float)(cSteps - 1);

            for (int iIteration = 0; iIteration < 8; iIteration++)
            {
                // Calculate new steps

                for (int iStep = 0; iStep < cSteps; iStep++)
                {
                    block.InterpValues[iStep].R = X.R * pC[iStep] + Y.R * pD[iStep];
                    block.InterpValues[iStep].G = X.G * pC[iStep] + Y.G * pD[iStep];
                    block.InterpValues[iStep].B = X.B * pC[iStep] + Y.B * pD[iStep];
                }

                // Calculate color 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 < (1.0f / 4096.0f))
                {
                    break;
                }

                float fScale = fSteps / fLen;

                Dir.R *= fScale;
                Dir.G *= fScale;
                Dir.B *= fScale;


                // Evaluate function, and derivatives
                float  d2X, d2Y;
                RgbF32 dX, dY;
                d2X = d2Y = dX.R = dX.G = dX.B = dY.R = dY.G = dY.B = 0.0f;

                for (int i = 0; i < values.Length; i++)
                {
                    var v = values[i];

                    float fDot = (v.R - X.R) * Dir.R +
                                 (v.G - X.G) * Dir.G +
                                 (v.B - X.B) * Dir.B;

                    int iStep;
                    if (fDot <= 0.0f)
                    {
                        iStep = 0;
                    }
                    else if (fDot >= fSteps)
                    {
                        iStep = cSteps - 1;
                    }
                    else
                    {
                        iStep = (int)(fDot + 0.5f);
                    }


                    RgbF32 Diff;
                    Diff.R = block.InterpValues[iStep].R - v.R;
                    Diff.G = block.InterpValues[iStep].G - v.G;
                    Diff.B = block.InterpValues[iStep].B - v.B;

#if COLOR_WEIGHTS
                    float fC = pC[iStep] * v.a * (1.0f / 8.0f);
                    float fD = pD[iStep] * v.a * (1.0f / 8.0f);
#else
                    float fC = pC[iStep] * (1.0f / 8.0f);
                    float fD = pD[iStep] * (1.0f / 8.0f);
#endif // COLOR_WEIGHTS

                    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 > 0.0f)
                {
                    float f = -1.0f / d2X;

                    X.R += dX.R * f;
                    X.G += dX.G * f;
                    X.B += dX.B * f;
                }

                if (d2Y > 0.0f)
                {
                    float f = -1.0f / d2Y;

                    Y.R += dY.R * f;
                    Y.G += dY.G * f;
                    Y.B += dY.B * f;
                }

                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;
                }
            }

            r0 = X;
            r1 = Y;
        }
Example #2
0
        public BC1Block Encode(Bc1BlockData block)
        {
            BC1Block ret;

            if (block.AlphaMask == 0xFFFF)
            {
                ret.PackedValue = BC1Block.TransparentValue;
                return(ret);
            }

            QuantizeValues(block);

            SpanValues(block, out var r0, out var r1);

            //quantize the endpoints

            bool weightValues = !UseUniformWeighting;

            if (weightValues)
            {
                r0.R *= RInvWeight;
                r0.G *= GInvWeight;
                r0.B *= BInvWeight;

                r1.R *= RInvWeight;
                r1.G *= GInvWeight;
                r1.B *= BInvWeight;
            }

            var pr0 = Rgb565.Pack(r0);
            var pr1 = Rgb565.Pack(r1);

            if (block.AlphaMask == 0 && pr0.PackedValue == pr1.PackedValue)
            {
                return(new BC1Block(pr0, pr1));
            }

            pr0.Unpack(out r0);
            pr1.Unpack(out r1);

            if (weightValues)
            {
                r0.R *= RWeight;
                r0.G *= GWeight;
                r0.B *= BWeight;

                r1.R *= RWeight;
                r1.G *= GWeight;
                r1.B *= BWeight;
            }

            //interp out the steps

            RgbF32 s0;

            if ((block.AlphaMask != 0) == (pr0.PackedValue <= pr1.PackedValue))
            {
                ret = new BC1Block(pr0, pr1);
                block.InterpValues[0] = s0 = r0;
                block.InterpValues[1] = r1;
            }
            else
            {
                ret = new BC1Block(pr1, pr0);
                block.InterpValues[0] = s0 = r1;
                block.InterpValues[1] = r0;
            }

            uint[] pSteps;

            if (block.AlphaMask != 0)
            {
                pSteps = pSteps3;

                RgbF32.Lerp(out block.InterpValues[2], block.InterpValues[0], block.InterpValues[1], 0.5F);
            }
            else
            {
                pSteps = pSteps4;

                RgbF32.Lerp(out block.InterpValues[2], block.InterpValues[0], block.InterpValues[1], 1.0F / 3.0F);
                RgbF32.Lerp(out block.InterpValues[3], block.InterpValues[0], block.InterpValues[1], 2.0F / 3.0F);
            }

            //find the best Values

            RgbF32 dir;

            dir.R = block.InterpValues[1].R - s0.R;
            dir.G = block.InterpValues[1].G - s0.G;
            dir.B = block.InterpValues[1].B - s0.B;

            float fSteps = block.AlphaMask != 0 ? 2 : 3;
            float fScale = (pr0.PackedValue != pr1.PackedValue) ?
                           (fSteps / (dir.R * dir.R + dir.G * dir.G + dir.B * dir.B)) : 0.0F;

            dir.R *= fScale;
            dir.G *= fScale;
            dir.B *= fScale;

            bool dither = DitherRgb;

            if (dither)
            {
                if (block.Error == null)
                {
                    block.Error = new RgbF32[16];
                }
                else
                {
                    Array.Clear(block.Error, 0, 16);
                }
            }

            for (int i = 0; i < block.Values.Length; i++)
            {
                if ((block.AlphaMask & (1 << i)) != 0)
                {
                    ret.PackedValue |= 3U << (32 + i * 2);
                }
                else
                {
                    var cl = block.Values[i];

                    if (weightValues)
                    {
                        cl.R *= RWeight;
                        cl.G *= GWeight;
                        cl.B *= BWeight;
                    }

                    if (dither)
                    {
                        var e = block.Error[i];

                        cl.R += e.R;
                        cl.G += e.G;
                        cl.B += e.B;
                    }

                    float fDot =
                        (cl.R - s0.R) * dir.R +
                        (cl.G - s0.G) * dir.G +
                        (cl.B - s0.B) * dir.B;

                    uint iStep;

                    if (fDot <= 0)
                    {
                        iStep = 0;
                    }
                    else if (fDot >= fSteps)
                    {
                        iStep = 1;
                    }
                    else
                    {
                        iStep = pSteps[(int)(fDot + 0.5F)];
                    }

                    ret.PackedValue |= (ulong)iStep << (32 + i * 2);

                    if (dither)
                    {
                        RgbF32 e, d, interp = block.InterpValues[iStep];

                        d.R = cl.R - interp.R;
                        d.G = cl.G - interp.G;
                        d.B = cl.B - interp.B;

                        if ((i & 3) != 3)
                        {
                            e = block.Error[i + 1];

                            e.R += d.R * (7.0F / 16.0F);
                            e.G += d.G * (7.0F / 16.0F);
                            e.B += d.B * (7.0F / 16.0F);

                            block.Error[i + 1] = e;
                        }

                        if (i < 12)
                        {
                            if ((i & 3) != 0)
                            {
                                e = block.Error[i + 3];

                                e.R += d.R * (3.0F / 16.0F);
                                e.G += d.G * (3.0F / 16.0F);
                                e.B += d.B * (3.0F / 16.0F);

                                block.Error[i + 3] = e;
                            }

                            e = block.Error[i + 4];

                            e.R += d.R * (5.0F / 16.0F);
                            e.G += d.G * (5.0F / 16.0F);
                            e.B += d.B * (5.0F / 16.0F);

                            block.Error[i + 4] = e;

                            if (3 != (i & 3))
                            {
                                e = block.Error[i + 5];

                                e.R += d.R * (1.0F / 16.0F);
                                e.G += d.G * (1.0F / 16.0F);
                                e.B += d.B * (1.0F / 16.0F);

                                block.Error[i + 5] = e;
                            }
                        }
                    }
                }
            }

            return(ret);
        }
Example #3
0
        private void QuantizeValues(Bc1BlockData block)
        {
            bool dither       = DitherRgb;
            bool weightColors = !UseUniformWeighting;

            if (dither)
            {
                Array.Clear(block.Error, 0, 16);
            }

            for (int i = 0; i < block.Values.Length; i++)
            {
                var cl = block.Values[i];

                if (dither)
                {
                    var e = block.Error[i];

                    cl.R += e.R;
                    cl.G += e.G;
                    cl.B += e.B;
                }

                RgbF32 qcl;

                qcl.R = (int)(cl.R * 31F + 0.5F) * (1F / 31F);
                qcl.G = (int)(cl.G * 63F + 0.5F) * (1F / 63F);
                qcl.B = (int)(cl.B * 31F + 0.5F) * (1F / 31F);

                if (dither)
                {
                    RgbF32 e, d;

                    d.R = cl.R - qcl.R;
                    d.G = cl.G - qcl.G;
                    d.B = cl.B - qcl.B;

                    if ((i & 3) != 3)
                    {
                        e = block.Error[i + 1];

                        e.R += d.R * (7F / 16F);
                        e.G += d.G * (7F / 16F);
                        e.B += d.B * (7F / 16F);

                        block.Error[i + 1] = e;
                    }

                    if (i < 12)
                    {
                        if ((i & 3) != 0)
                        {
                            e = block.Error[i + 3];

                            e.R += d.R * (3F / 16F);
                            e.G += d.G * (3F / 16F);
                            e.B += d.B * (3F / 16F);

                            block.Error[i + 3] = e;
                        }

                        e = block.Error[i + 4];

                        e.R += d.R * (5F / 16F);
                        e.G += d.G * (5F / 16F);
                        e.B += d.B * (5F / 16F);

                        block.Error[i + 4] = e;

                        if ((i & 3) != 3)
                        {
                            e = block.Error[i + 5];

                            e.R += d.R * (1F / 16F);
                            e.G += d.G * (1F / 16F);
                            e.B += d.B * (1F / 16F);

                            block.Error[i + 5] = e;
                        }
                    }
                }

                if (weightColors)
                {
                    qcl.R *= RWeight;
                    qcl.G *= GWeight;
                    qcl.B *= BWeight;
                }

                block.QuantizedValues[i] = qcl;
            }
        }
Example #4
0
        /// <summary>
        /// Loads the alpha mask from floating-point alpha Values.
        /// </summary>
        /// <param name="aValues">The array to read alpha Values from.</param>
        /// <param name="aIndex">The index at which to start reading <paramref name="aValues"/>.</param>
        /// <param name="alphaRef">The alpha reference value. Alpha Values smaller than this will be considered transparent.</param>
        /// <param name="rowPitch">The number of array elements between rows.</param>
        /// <param name="colPitch">The number of array elements between pixels within a row.</param>
        public void LoadAlphaMask(
            Bc1BlockData block,
            float[] aValues, int aIndex = 0, float alphaRef = 0.5F,
            int rowPitch = 4, int colPitch = 1)
        {
            block.AlphaMask = 0;

            int aIdx;

            if (DitherAlpha)
            {
                //when dithering, we load into a temporary array,
                //apply dithering, and then continue loading from
                //our temporary storage

                //create the temporary storage the first time it's needed

                if (block.AlphaValues == null)
                {
                    block.AlphaValues = new float[16];
                    block.AlphaErrors = new float[16];
                }
                else
                {
                    Array.Clear(block.AlphaErrors, 0, 16);
                }

                //load the Values

                if (rowPitch == 4 && colPitch == 1)
                {
                    Array.Copy(aValues, aIndex, block.AlphaValues, 0, 16);
                }
                else
                {
                    aIdx = aIndex;

                    block.AlphaValues[0] = aValues[aIdx];
                    block.AlphaValues[1] = aValues[aIdx += colPitch];
                    block.AlphaValues[2] = aValues[aIdx += colPitch];
                    block.AlphaValues[3] = aValues[aIdx + colPitch];

                    aIdx = aIndex += rowPitch;

                    block.AlphaValues[4] = aValues[aIdx];
                    block.AlphaValues[5] = aValues[aIdx += colPitch];
                    block.AlphaValues[6] = aValues[aIdx += colPitch];
                    block.AlphaValues[7] = aValues[aIdx + colPitch];

                    aIdx = aIndex += rowPitch;

                    block.AlphaValues[8]  = aValues[aIdx];
                    block.AlphaValues[9]  = aValues[aIdx += colPitch];
                    block.AlphaValues[10] = aValues[aIdx += colPitch];
                    block.AlphaValues[11] = aValues[aIdx + colPitch];

                    aIdx = aIndex + rowPitch;

                    block.AlphaValues[12] = aValues[aIdx];
                    block.AlphaValues[13] = aValues[aIdx += colPitch];
                    block.AlphaValues[14] = aValues[aIdx += colPitch];
                    block.AlphaValues[15] = aValues[aIdx + colPitch];
                }

                //apply the dithering

                for (int i = 0; i < block.AlphaValues.Length; i++)
                {
                    var v = block.AlphaValues[i];
                    var e = block.AlphaErrors[i];

                    float a = (int)(v + e);
                    float b = (int)(a + 0.5F);
                    float d = a - b;

                    if ((i & 3) != 3)
                    {
                        block.AlphaErrors[i + 1] += d * (7F / 16F);
                    }

                    if (i < 12)
                    {
                        if ((i & 3) != 0)
                        {
                            block.AlphaErrors[i + 3] += d * (3F / 16F);
                        }

                        block.AlphaErrors[i + 4] += d * (5F / 16F);

                        if ((i & 3) != 3)
                        {
                            block.AlphaErrors[i + 5] += d * (1F / 16F);
                        }
                    }

                    block.AlphaValues[i] = b;
                }

                //and continue loading from our temporary storage

                aValues = block.AlphaValues;
                aIndex  = 0;

                rowPitch = 4;
                colPitch = 1;
            }

            aIdx = aIndex;

            if (aValues[aIdx] < alphaRef)
            {
                block.AlphaMask |= 0x0001;
            }
            if (aValues[aIdx += colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x0002;
            }
            if (aValues[aIdx += colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x0004;
            }
            if (aValues[aIdx + colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x0008;
            }

            aIdx = aIndex += rowPitch;

            if (aValues[aIdx] < alphaRef)
            {
                block.AlphaMask |= 0x0010;
            }
            if (aValues[aIdx += colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x0020;
            }
            if (aValues[aIdx += colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x0040;
            }
            if (aValues[aIdx + colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x0080;
            }

            aIdx = aIndex += rowPitch;

            if (aValues[aIdx] < alphaRef)
            {
                block.AlphaMask |= 0x0100;
            }
            if (aValues[aIdx += colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x0200;
            }
            if (aValues[aIdx += colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x0400;
            }
            if (aValues[aIdx + colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x0800;
            }

            aIdx = aIndex + rowPitch;

            if (aValues[aIdx] < alphaRef)
            {
                block.AlphaMask |= 0x1000;
            }
            if (aValues[aIdx += colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x2000;
            }
            if (aValues[aIdx += colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x4000;
            }
            if (aValues[aIdx + colPitch] < alphaRef)
            {
                block.AlphaMask |= 0x8000;
            }
        }