Пример #1
0
        // TODO: Break this up into several methods for different types of images. Palletized images
        // require a slightly different process than truecolor, and grayscale and alpha require a yet
        // again different process. Currently, this only handles truecolor images of 8/16 bit depth.

        public async Task <byte[]> GetCompressedDataAsync()
        {
            if (compressedData != null)
            {
                return(compressedData);
            }

            int pixelWidth = (imageInfo.ColorType == ColorType.Truecolor ? 3 : 4) *
                             imageInfo.BitDepth / 8;

            // Set up a couple of streams where we're going to hold our data.
            // We need a stream for the final data and a deflate wrapper over it
            var compressedStream = new MemoryStream();
            var deflateStream    = new DeflateStream(compressedStream, CompressionLevel.Optimal, true);

            // Write the zlib stream header. :-). 0x78 indicates DEFLATE compression (8) with
            // a sliding window of 2^7 (32 kB). 0xDA includes a check bit for the header and
            // an indication that we're using the optimal compression algorithm and default
            // Huffman tree.
            compressedStream.Write(new byte[] { 0x78, 0xDA }, 0, 2);

            var adler = new Adler32();

            // Apply filtering. Both byte[]s always represent the unfiltered scanline. Allocations
            // and copies are the devil, so we do as few as possible here. We do no more than 3 allocations
            // no matter what. We do 2*Height and change (some filters might copy a few bytes)
            // copies, as well, but dynamic filtering repeats a lot of filter work. Luckily, filters
            // are fast.
            byte[] previousScanline = null, scanline = new byte[imageInfo.Width * pixelWidth],
            scanlineToWrite = new byte[1 + imageInfo.Width * pixelWidth];

            var tempStream = new MemoryStream();

            // TODO: Someday, instead of writing to a stream, do this all with direct offsets
            // into an array. We should be able to preallocate an array of the correct length
            // and simply write the bytes and update offsets.
            for (int i = 0; i < imageInfo.Height; i++)
            {
                // Copy raw RGB data to scanline.
                Buffer.BlockCopy(rawRgbData, i * scanline.Length, scanline, 0, scanline.Length);

                // Filter scanline into scanlineToWrite.
                FilterInto(scanlineToWrite, 1, scanline, previousScanline, pixelWidth);

                // Write scanline to stream.
                await tempStream.WriteAsync(scanlineToWrite, 0, scanlineToWrite.Length).ConfigureAwait(false);

                // Allocate previous scanline if needed.
                previousScanline = previousScanline ?? new byte[scanline.Length];

                // Copy current scanline onto previous scanline.
                Buffer.BlockCopy(scanline, 0, previousScanline, 0, scanline.Length);
            }

            var data = tempStream.ToArray();

            adler.FeedBlock(data);
            await deflateStream.WriteAsync(data, 0, data.Length).ConfigureAwait(false);

            tempStream.Dispose();
            deflateStream.Dispose();

            // Write the ADLER32 Zlib checksum
            var adlerBytes = GetBytesForInteger(adler.Hash);

            compressedStream.Write(adlerBytes, 0, adlerBytes.Length);

            // Store the compressed data and length.
            compressedData   = compressedStream.ToArray();
            compressedLength = compressedData.Length;

            compressedStream.Dispose();

            return(compressedData);
        }