예제 #1
0
        /// <summary>
        /// Encodes the provided BGRA, Y flipped, pixel data to a PNG image.
        /// </summary>
        /// <param name="pixels">The pixel date to encode.</param>
        /// <param name="width">The width of the image in the data.</param>
        /// <param name="height">The height of the image in the data.</param>
        /// <returns>A PNG image as bytes.</returns>
        public static byte[] Encode(byte[] pixels, int width, int height)
        {
            var       pngHeader    = new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
            const int maxBlockSize = 0xFFFF;

            using var stream = new MemoryStream();

            // Write the png header.
            stream.Write(pngHeader, 0, 8);

            var header = new PngFileHeader
            {
                Width             = width,
                Height            = height,
                ColorType         = 6,
                BitDepth          = 8,
                FilterMethod      = 0,
                CompressionMethod = 0,
                InterlaceMethod   = 0
            };

            // Write header chunk.
            var chunkData = new byte[13];

            WriteInteger(chunkData, 0, header.Width);
            WriteInteger(chunkData, 4, header.Height);

            chunkData[8]  = header.BitDepth;
            chunkData[9]  = header.ColorType;
            chunkData[10] = header.CompressionMethod;
            chunkData[11] = header.FilterMethod;
            chunkData[12] = header.InterlaceMethod;

            WriteChunk(stream, PngChunkTypes.HEADER, chunkData);

            // Write data chunks.
            var data = new byte[width * height * 4 + height];

            int rowLength = width * 4 + 1;

            for (var y = 0; y < height; y++)
            {
                data[y * rowLength] = 0;

                for (var x = 0; x < width; x++)
                {
                    // Calculate the offset for the new array.
                    int dataOffset = y * rowLength + x * 4 + 1;

                    // Calculate the offset for the original pixel array.
                    int pixelOffset = (y * width + x) * 4;

                    data[dataOffset + 0] = pixels[pixelOffset + 2];
                    data[dataOffset + 1] = pixels[pixelOffset + 1];
                    data[dataOffset + 2] = pixels[pixelOffset + 0];
                    data[dataOffset + 3] = pixels[pixelOffset + 3];
                }
            }

            byte[] buffer    = ZlibStreamUtility.Compress(data, 6);
            int    numChunks = buffer.Length / maxBlockSize;

            if (buffer.Length % maxBlockSize != 0)
            {
                numChunks++;
            }

            for (var i = 0; i < numChunks; i++)
            {
                int length = buffer.Length - i * maxBlockSize;

                if (length > maxBlockSize)
                {
                    length = maxBlockSize;
                }
                WriteChunk(stream, PngChunkTypes.DATA, buffer, i * maxBlockSize, length);
            }

            // Write end chunk.
            WriteChunk(stream, PngChunkTypes.END, null);

            stream.Flush();
            return(stream.ToArray());
        }
예제 #2
0
        /// <summary>
        /// Encodes the provided Y flipped pixel data to a PNG image.
        /// </summary>
        /// <param name="pixels">The pixel date to encode.</param>
        /// <param name="size">The size of the image..</param>
        /// <param name="format">The format of the pixel data.</param>
        /// <returns>A PNG image as bytes.</returns>
        public static byte[] Encode(byte[] pixels, Vector2 size, PixelFormat format)
        {
            using var stream = new MemoryStream();

            stream.Write(_pngHeader, 0, 8);

            // Write header chunk.
            var chunkData = new byte[13];
            var width     = (int)size.X;
            var height    = (int)size.Y;

            WriteInteger(chunkData, 0, width);
            WriteInteger(chunkData, 4, height);
            var header = new PngFileHeader
            {
                Size              = size,
                ColorType         = (byte)(format == PixelFormat.Red ? 0 : 6), // Greyscale, otherwise RGBA
                BitDepth          = 8,
                FilterMethod      = 0,
                CompressionMethod = 0,
                InterlaceMethod   = 0,
                PixelFormat       = format
            };

            chunkData[8]  = header.BitDepth;
            chunkData[9]  = header.ColorType;
            chunkData[10] = header.CompressionMethod;
            chunkData[11] = header.FilterMethod;
            chunkData[12] = header.InterlaceMethod;
            WriteChunk(stream, PngChunkTypes.HEADER, chunkData);

            // Write data chunks.
            int bytesPerPixel = Gl.PixelFormatToComponentCount(format);
            var data          = new byte[width * height * bytesPerPixel + height];
            int rowLength     = width * bytesPerPixel + 1; // One byte for the filter

            for (var y = 0; y < height; y++)
            {
                data[y * rowLength] = 0;

                for (var x = 0; x < width; x++)
                {
                    // Calculate the offset for the new array.
                    int dataOffset = y * rowLength + x * bytesPerPixel + 1;

                    // Calculate the offset for the original pixel array.
                    int pixelOffset = (y * width + x) * bytesPerPixel;

                    if (format == PixelFormat.Rgba)
                    {
                        data[dataOffset + 0] = pixels[pixelOffset + 0];
                        data[dataOffset + 1] = pixels[pixelOffset + 1];
                        data[dataOffset + 2] = pixels[pixelOffset + 2];
                        data[dataOffset + 3] = pixels[pixelOffset + 3];
                    }
                    else if (format == PixelFormat.Bgra)
                    {
                        data[dataOffset + 0] = pixels[pixelOffset + 2];
                        data[dataOffset + 1] = pixels[pixelOffset + 1];
                        data[dataOffset + 2] = pixels[pixelOffset + 0];
                        data[dataOffset + 3] = pixels[pixelOffset + 3];
                    }
                    else if (format == PixelFormat.Red)
                    {
                        data[dataOffset] = pixels[pixelOffset];
                    }
                    else
                    {
                        throw new Exception($"Unsupported encoding pixel format - {format}");
                    }
                }
            }

            // Compress data.
            byte[] buffer    = ZlibStreamUtility.Compress(data, 6);
            int    numChunks = buffer.Length / MAX_BLOCK_SIZE;

            if (buffer.Length % MAX_BLOCK_SIZE != 0)
            {
                numChunks++;
            }
            for (var i = 0; i < numChunks; i++)
            {
                int length = buffer.Length - i * MAX_BLOCK_SIZE;
                if (length > MAX_BLOCK_SIZE)
                {
                    length = MAX_BLOCK_SIZE;
                }
                WriteChunk(stream, PngChunkTypes.DATA, buffer, i * MAX_BLOCK_SIZE, length);
            }

            // Write end chunk.
            WriteChunk(stream, PngChunkTypes.END, null);
            stream.Flush();
            return(stream.ToArray());
        }