/// <summary>
        /// Compresses a 4x4 block of pixels.
        /// </summary>
        /// <remarks>
        /// The source pixels should be presented as a contiguous array of 16 rgba
        /// values, with each component as 1 byte each. In memory this should be:
        ///
        ///     { r1, g1, b1, a1, .... , r16, g16, b16, a16 }
        ///
        /// The mask parameter enables only certain pixels within the block. The lowest
        /// bit enables the first pixel and so on up to the 16th bit. Bits beyond the
        /// 16th bit are ignored. Pixels that are not enabled are allowed to take
        /// arbitrary colours in the output block. An example of how this can be used
        /// is in the CompressImage function to disable pixels outside the bounds of
        /// the image when the width or height is not divisible by 4.
        ///
        /// The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression,
        /// however, DXT1 will be used by default if none is specified. When using DXT1
        /// compression, 8 bytes of storage are required for the compressed DXT block.
        /// DXT3 and DXT5 compression require 16 bytes of storage per block.
        ///
        /// The flags parameter can also specify a preferred colour compressor and
        /// colour error metric to use when fitting the RGB components of the data.
        /// Possible colour compressors are: kColourClusterFit (the default),
        /// kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics
        /// are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no
        /// flags are specified in any particular category then the default will be
        /// used. Unknown flags are ignored.
        ///
        /// When using kColourClusterFit, an additional flag can be specified to
        /// weight the colour of each pixel by its alpha value. For images that are
        /// rendered using alpha blending, this can significantly increase the
        /// perceived quality.
        /// </remarks>
        /// <param name="rgba">The rgba values of the 16 source pixels.</param>
        /// <param name="mask">The valid pixel mask.</param>
        /// <param name="flags">Compression flags.</param>
        public void CompressMasked(Byte[] rgba, int mask, CompressionOptions options)
        {
            System.Diagnostics.Debug.Assert(rgba != null && rgba.Length == 64, nameof(rgba));

            // fix any bad flags
            options = options.FixFlags();

            // create the minimal point set
            var colours = new ColourSet(rgba, mask, _Mode, options);

            // check the compression type and compress colour
            if (colours.Count == 1)
            {
                // always do a single colour fit
                var fit = new SingleColourFit(colours, options);
                fit.Compress(this);
            }
            else if ((options & CompressionOptions.ColourRangeFit) != 0 || colours.Count == 0)
            {
                // do a range fit
                var fit = new RangeFit(colours, options);
                fit.Compress(this);
            }
            else
            {
                if ((options & CompressionOptions.ColourClusterFitAlt) != 0)
                {
                    var fit = new ClusterFitAlt(colours, options);
                    fit.Compress(this);
                }
                else
                {
                    // default to a cluster fit (could be iterative or not)
                    var fit = new ClusterFit(colours, options);
                    fit.Compress(this);
                }
            }

            // compress alpha separately if necessary
            if ((_Mode & CompressionMode.Dxt3) != 0)
            {
                this.CompressAlphaDxt3(rgba, mask);
            }
            else if ((_Mode & CompressionMode.Dxt5) != 0)
            {
                this.CompressAlphaDxt5(rgba, mask);
            }
        }
Esempio n. 2
0
        private static void CompressImage(Bitmap srcImage, Byte[] blocks, CompressionMode mode, CompressionOptions options)
        {
            // fix any bad flags
            options = options.FixFlags();

            int block_width  = (srcImage.Width + 3) / 4;
            int block_height = (srcImage.Height + 3) / 4;

            // if the number of chunks to process is not very large, we better skip parallel processing
            if (block_width * block_height < 16)
            {
                options &= ~CompressionOptions.UseParallelProcessing;
            }

            if ((options & CompressionOptions.UseParallelProcessing) != 0)
            {
                System.Threading.Tasks.Parallel.For
                (
                    0,
                    block_height,
                    (y, state) =>
                {
                    // initialise the block output
                    var block = new BlockWindow(blocks, mode);

                    block.Offset += block.ByteLength * y * block_width;

                    // build the 4x4 block of pixels
                    var sourceRgba = new Byte[16 * 4];

                    for (int x = 0; x < block_width; x++)
                    {
                        srcImage.CopyBlockTo(x * 4, y * 4, sourceRgba, out int mask);

                        // compress it into the output
                        block.CompressMasked(sourceRgba, mask, options);

                        // advance
                        block.Offset += block.ByteLength;
                    }
                }
                );
            }
            else
            {
                // initialise the block output
                var block = new BlockWindow(blocks, mode);

                // build the 4x4 block of pixels
                var sourceRgba = new Byte[16 * 4];

                // loop over blocks
                for (int y = 0; y < block_height; ++y)
                {
                    for (int x = 0; x < block_width; ++x)
                    {
                        srcImage.CopyBlockTo(x * 4, y * 4, sourceRgba, out int mask);

                        // compress it into the output
                        block.CompressMasked(sourceRgba, mask, options);

                        // advance
                        block.Offset += block.ByteLength;
                    }
                }
            }
        }