/// <summary> /// Encodes and writes pixel data to the supplied stream /// </summary> /// <param name="indexedPixels"> /// Collection of indices of the pixel colours within the active colour /// table. /// </param> /// <param name="outputStream"> /// The stream to write to. /// </param> private static void WritePixels(IndexedPixels indexedPixels, Stream outputStream) { LzwEncoder encoder = new LzwEncoder(indexedPixels); encoder.Encode(outputStream); }
/// <summary> /// Encodes _ip and decodes the encoded data, then compares the decoded /// data against the original. /// Also calculates the compression rate. /// </summary> private void TestIt() { MemoryStream s = new MemoryStream(); _e = new LzwEncoder(_ip); _e.Encode(s); int encodedByteCount = (int)s.Position; s.Seek(0, SeekOrigin.Begin); TableBasedImageData tbid = new TableBasedImageData(s, _ip.Count); s.Seek(0, SeekOrigin.Begin); byte[] encodedBytes = new byte[encodedByteCount]; s.Read(encodedBytes, 0, encodedByteCount); Assert.AreEqual(_ip.Count, tbid.Pixels.Count, "Pixel counts differ"); for (int i = 0; i < _ip.Count; i++) { Assert.AreEqual(_ip[i], tbid.Pixels[i], "pixel index " + i); } float compression = 100 - (100 * encodedByteCount / _ip.Count); WriteMessage("Original byte count: " + _ip.Count + ". Encoded byte count: " + encodedByteCount + ". Compression rate: " + compression + "%"); }
public void ConvertFrame() { for (var i = 0; i < Frames.Count; i++) { var index = i;//(int)context; var colorTable = colorTables[index]; var localColorTableFlag = (byte)(colorTable == globalColorTable ? 0 : 1); var localColorTableSize = GetColorTableSize(colorTable); byte transparentColorFlag = 0, transparentColorIndex = 0; byte max; var colorIndexes = GetColorIndexes(Frames[index].Texture, scale, colorTable, localColorTableFlag, ref transparentColorFlag, ref transparentColorIndex, out max); var graphicControlExtension = new GraphicControlExtension(4, 0, (byte)Frames[index].DisposalMethod, 0, transparentColorFlag, (ushort)(100 * Frames[index].Delay), transparentColorIndex); var imageDescriptor = new ImageDescriptor(0, 0, Width, Height, localColorTableFlag, 0, 0, 0, localColorTableSize); var minCodeSize = LzwEncoder.GetMinCodeSize(max); var lzw = LzwEncoder.Encode(colorIndexes, minCodeSize); var tableBasedImageData = new TableBasedImageData(minCodeSize, lzw); var tmpBytes = new List <byte>(); tmpBytes.AddRange(graphicControlExtension.GetBytes()); tmpBytes.AddRange(imageDescriptor.GetBytes()); if (localColorTableFlag == 1) { tmpBytes.AddRange(ColorTableToBytes(colorTable, localColorTableSize)); } tmpBytes.AddRange(tableBasedImageData.GetBytes()); encoded.Add(index, tmpBytes); // encodeProgress.Progress++; if (encoded.Count == Frames.Count) { var globalColorTableSize = GetColorTableSize(globalColorTable); var logicalScreenDescriptor = new LogicalScreenDescriptor(Width, Height, 1, 7, 0, globalColorTableSize, 0, 0); var binary = new List <byte>(); binary.AddRange(Encoding.UTF8.GetBytes(header)); binary.AddRange(logicalScreenDescriptor.GetBytes()); binary.AddRange(ColorTableToBytes(globalColorTable, globalColorTableSize)); binary.AddRange(applicationExtension.GetBytes()); binary.AddRange(encoded.OrderBy(j => j.Key).SelectMany(j => j.Value)); binary.Add(0x3B); // GIF Trailer. bytes = binary.ToArray(); // encodeProgress.Completed = true; } // onProgress(encodeProgress); } currentStep++; }
public void NullStream() { _e = new LzwEncoder(_ip); try { _e.Encode(null); } catch (ArgumentNullException ex) { Assert.AreEqual("outputStream", ex.ParamName); throw; } }
private static void WriteFrames(IEnumerable<ImageFrame> frames, Stream stream) { foreach (var frame in frames) { var frameBytes = new List<byte>(); if (frame.GraphicControlExtension != null) { frameBytes.AddRange(frame.GraphicControlExtension.ToBytes()); } frame.ImageDescriptor.SortFlag = false; frame.ImageDescriptor.InterlaceFlag = false; frameBytes.AddRange(frame.ImageDescriptor.ToBytes()); if (frame.ImageDescriptor.LocalColorTableFlag) { frameBytes.AddRange(frame.LocalColorTable); } stream.WriteBytes(frameBytes.ToArray()); var transparentColorIndex = -1; if (frame.GraphicControlExtension != null && frame.GraphicControlExtension.TransparentColorFlag) { transparentColorIndex = frame.GraphicControlExtension.TransparentColorIndex; } var imageData = GetImageData(frame.Bitmap, frame.LocalColorTable, transparentColorIndex); var lzwEncoder = new LzwEncoder(imageData, (byte) frame.ColorDepth); lzwEncoder.Encode(stream); stream.WriteByte(Const.BlockTerminator); } stream.WriteByte(Const.EndIntroducer); }
// Encodes and writes pixel data. private void WritePixels() { LzwEncoder encoder = new LzwEncoder(_width, _height, _indexedPixels, _colorDepth); encoder.Encode(_memoryStream); }
/// <summary> /// Iterator can be used for large GIF-files in order to display progress bar. /// </summary> public IEnumerable <List <byte> > EncodeIterator(int scale = 1) { if (_free) { if (Frames[0].Texture.width > 256 || Frames[0].Texture.height > 256) { throw new Exception("The free version has maximum supported size 256x256 px. Please consider buying the Full version of Power GIF."); } if (Frames.Count > 20) { throw new Exception("The Free version is limited by 20 frames. Please consider buying the Full version of Power GIF."); } } const string header = "GIF89a"; var width = (ushort)(Frames[0].Texture.width * scale); var height = (ushort)(Frames[0].Texture.height * scale); var globalColorTable = new List <Color32>(); var applicationExtension = new ApplicationExtension(); var bytes = new List <byte>(); var colorTables = new List <Color32> [Frames.Count]; var distinctColors = new Dictionary <int, List <Color32> >(); var manualResetEvent = new ManualResetEvent(false); for (var i = 0; i < Frames.Count; i++) { var frame = Frames[i]; ThreadPool.QueueUserWorkItem(context => { var distinct = frame.Texture.GetPixels32().Distinct().ToList(); lock (distinctColors) { distinctColors.Add((int)context, distinct); if (distinctColors.Count == Frames.Count) { manualResetEvent.Set(); } } }, i); } manualResetEvent.WaitOne(); for (var i = 0; i < Frames.Count; i++) { var colors = distinctColors[i]; var add = colors.Where(j => !globalColorTable.Contains(j)).ToList(); if (globalColorTable.Count + add.Count <= 256) { globalColorTable.AddRange(add); colorTables[i] = globalColorTable; } else if (add.Count <= 256) // Introducing local color table { colorTables[i] = colors; } else { throw new Exception($"Frame #{i} contains more than 256 colors!"); } } ReplaceTransparentColor(ref globalColorTable); for (var i = 0; i < Frames.Count; i++) { var frame = Frames[i]; var colorTable = colorTables[i]; var localColorTableFlag = (byte)(colorTable == globalColorTable ? 0 : 1); var localColorTableSize = GetColorTableSize(colorTable); byte transparentColorFlag = 0, transparentColorIndex = 0; byte max; var colorIndexes = GetColorIndexes(frame.Texture, scale, colorTable, localColorTableFlag, ref transparentColorFlag, ref transparentColorIndex, out max); var graphicControlExtension = new GraphicControlExtension(4, 0, (byte)frame.DisposalMethod, 0, transparentColorFlag, (ushort)(100 * frame.Delay), transparentColorIndex); var imageDescriptor = new ImageDescriptor(0, 0, width, height, localColorTableFlag, 0, 0, 0, localColorTableSize); var minCodeSize = LzwEncoder.GetMinCodeSize(max); var lzw = LzwEncoder.Encode(colorIndexes, minCodeSize); var tableBasedImageData = new TableBasedImageData(minCodeSize, lzw); bytes.Clear(); bytes.AddRange(graphicControlExtension.GetBytes()); bytes.AddRange(imageDescriptor.GetBytes()); if (localColorTableFlag == 1) { bytes.AddRange(ColorTableToBytes(colorTable, localColorTableSize)); } bytes.AddRange(tableBasedImageData.GetBytes()); yield return(bytes); } yield return(new List <byte> { 0x3B }); // GIF Trailer. // Then output GIF header as last iterator element! This way we can build global color table "on fly" instead of expensive building operation. var globalColorTableSize = GetColorTableSize(globalColorTable); var logicalScreenDescriptor = new LogicalScreenDescriptor(width, height, 1, 7, 0, globalColorTableSize, 0, 0); bytes.Clear(); bytes.AddRange(Encoding.UTF8.GetBytes(header)); bytes.AddRange(logicalScreenDescriptor.GetBytes()); bytes.AddRange(ColorTableToBytes(globalColorTable, globalColorTableSize)); bytes.AddRange(applicationExtension.GetBytes()); yield return(bytes); }
/// <summary> /// Encode GIF in multiple threads. /// </summary> public void EncodeParallel(Action <EncodeProgress> onProgress, int scale = 1) // TODO: Refact. { if (_free) { throw new Exception("The Free version doesn't support this feature. Please consider buying the Full version of Power GIF."); } const string header = "GIF89a"; var width = (ushort)(Frames[0].Texture.width * scale); var height = (ushort)(Frames[0].Texture.height * scale); var globalColorTable = new List <Color32>(); var applicationExtension = new ApplicationExtension(); var encoded = new Dictionary <int, List <byte> >(); var encodeProgress = new EncodeProgress { FrameCount = Frames.Count }; var colorTables = new List <Color32> [Frames.Count]; var distinctColors = new Dictionary <int, List <Color32> >(); var manualResetEvent = new ManualResetEvent(false); for (var i = 0; i < Frames.Count; i++) { var frame = Frames[i]; ThreadPool.QueueUserWorkItem(context => { var distinct = frame.Texture.GetPixels32().Distinct().ToList(); lock (distinctColors) { distinctColors.Add((int)context, distinct); if (distinctColors.Count == Frames.Count) { manualResetEvent.Set(); } } }, i); } manualResetEvent.WaitOne(); for (var i = 0; i < Frames.Count; i++) { var colors = distinctColors[i]; var add = colors.Where(j => !globalColorTable.Contains(j)).ToList(); if (globalColorTable.Count + add.Count <= 256) { globalColorTable.AddRange(add); colorTables[i] = globalColorTable; } else if (colors.Count <= 256) // Introduce local color table. { colorTables[i] = colors; } else { onProgress(new EncodeProgress { Completed = true, Exception = new Exception($"Frame #{i} contains more than 256 colors!") }); return; } } ReplaceTransparentColor(ref globalColorTable); for (var i = 0; i < Frames.Count; i++) // Don't use Parallel.For to leave .NET compatibility. { ThreadPool.QueueUserWorkItem(context => { var index = (int)context; var colorTable = colorTables[index]; var localColorTableFlag = (byte)(colorTable == globalColorTable ? 0 : 1); var localColorTableSize = GetColorTableSize(colorTable); byte transparentColorFlag = 0, transparentColorIndex = 0; byte max; var colorIndexes = GetColorIndexes(Frames[index].Texture, scale, colorTable, localColorTableFlag, ref transparentColorFlag, ref transparentColorIndex, out max); var graphicControlExtension = new GraphicControlExtension(4, 0, (byte)Frames[index].DisposalMethod, 0, transparentColorFlag, (ushort)(100 * Frames[index].Delay), transparentColorIndex); var imageDescriptor = new ImageDescriptor(0, 0, width, height, localColorTableFlag, 0, 0, 0, localColorTableSize); var minCodeSize = LzwEncoder.GetMinCodeSize(max); var lzw = LzwEncoder.Encode(colorIndexes, minCodeSize); var tableBasedImageData = new TableBasedImageData(minCodeSize, lzw); var bytes = new List <byte>(); bytes.AddRange(graphicControlExtension.GetBytes()); bytes.AddRange(imageDescriptor.GetBytes()); if (localColorTableFlag == 1) { bytes.AddRange(ColorTableToBytes(colorTable, localColorTableSize)); } bytes.AddRange(tableBasedImageData.GetBytes()); lock (encoded) { encoded.Add(index, bytes); encodeProgress.Progress++; if (encoded.Count == Frames.Count) { var globalColorTableSize = GetColorTableSize(globalColorTable); var logicalScreenDescriptor = new LogicalScreenDescriptor(width, height, 1, 7, 0, globalColorTableSize, 0, 0); var binary = new List <byte>(); binary.AddRange(Encoding.UTF8.GetBytes(header)); binary.AddRange(logicalScreenDescriptor.GetBytes()); binary.AddRange(ColorTableToBytes(globalColorTable, globalColorTableSize)); binary.AddRange(applicationExtension.GetBytes()); binary.AddRange(encoded.OrderBy(j => j.Key).SelectMany(j => j.Value)); binary.Add(0x3B); // GIF Trailer. encodeProgress.Bytes = binary.ToArray(); encodeProgress.Completed = true; } onProgress(encodeProgress); } }, i); } }