private void MinimumCodeSizeTooLarge(bool xmlDebugging) { byte[] bytes = new byte[] { 12, // LZW minimum code size }; MemoryStream s = new MemoryStream(); s.Write(bytes, 0, bytes.Length); s.Seek(0, SeekOrigin.Begin); _tbid = new TableBasedImageData(s, 25, xmlDebugging); // Processing will be abandoned before any pixels are set Assert.AreEqual(ErrorState.LzwMinimumCodeSizeTooLarge, _tbid.ConsolidatedState); // TBID will still return the number of pixels passed to the constructor Assert.AreEqual(25, _tbid.Pixels.Count); Assert.AreEqual(12, _tbid.LzwMinimumCodeSize); Assert.AreEqual(13, _tbid.InitialCodeSize); Assert.AreEqual(Math.Pow(2, 12), _tbid.ClearCode); Assert.AreEqual(Math.Pow(2, 12) + 1, _tbid.EndOfInformation); if (xmlDebugging) { Assert.AreEqual(ExpectedDebugXml, _tbid.DebugXml); } }
/// <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 BlockTerminatorTest() { ReportStart(); byte[] bytes = new byte[] { 0x08, // LZW minimum code size 0x05, // block size = 5 // 5 bytes of LZW encoded data follows 0x00, 0x51, 0xFC, 0x1B, 0x28, 0x06, // block size = 6 // 6 bytes of LZW encoded data follows 0x70, 0xA0, 0xC1, 0x83, 0x01, 0x01, 0x00, // block terminator - end of table based image data }; MemoryStream s = new MemoryStream(); s.Write(bytes, 0, bytes.Length); s.Seek(0, SeekOrigin.Begin); int pixelCount = WikipediaExample.FrameSize.Width * WikipediaExample.FrameSize.Height; _tbid = new TableBasedImageData(s, pixelCount); Assert.AreEqual(ErrorState.Ok, _tbid.ConsolidatedState); Assert.AreEqual(15, _tbid.Pixels.Count); Assert.AreEqual(ErrorState.Ok, _tbid.ConsolidatedState); Assert.AreEqual(8, _tbid.LzwMinimumCodeSize); Assert.AreEqual(9, _tbid.InitialCodeSize); Assert.AreEqual(Math.Pow(2, 8), _tbid.ClearCode); Assert.AreEqual(Math.Pow(2, 8) + 1, _tbid.EndOfInformation); IndexedPixels expectedIndices = new IndexedPixels(); expectedIndices.Add(40); // first pixel is black - index 0 in colour table expectedIndices.Add(255); // 2nd pixel is white - index 255 in colour table expectedIndices.Add(255); // 3rd pixel expectedIndices.Add(255); // 4th pixel expectedIndices.Add(40); // 5th pixel expectedIndices.Add(255); // 6th pixel expectedIndices.Add(255); // 7th pixel expectedIndices.Add(255); // 8th pixel expectedIndices.Add(255); // 9th pixel expectedIndices.Add(255); // 10th pixel expectedIndices.Add(255); // 11th pixel expectedIndices.Add(255); // 12th pixel expectedIndices.Add(255); // 13th pixel expectedIndices.Add(255); // 14th pixel expectedIndices.Add(255); // 15th pixel for (int i = 0; i < 15; i++) { Assert.AreEqual(expectedIndices[i], _tbid.Pixels[i], "pixel " + i); } ReportEnd(); }
private void DataBlockTooShort(bool xmlDebugging) { byte[] bytes = new byte[] { 0x08, // LZW minimum code size 0x0b, // block size = 11 // 11 bytes of LZW encoded data follows (actually there's only 10 for this test) 0x00, 0x51, 0xFC, 0x1B, 0x28, 0x70, 0xA0, 0xC1, 0x83, 0x01, //0x01, // 0x00 // block terminator }; MemoryStream s = new MemoryStream(); s.Write(bytes, 0, bytes.Length); s.Seek(0, SeekOrigin.Begin); int pixelCount = WikipediaExample.FrameSize.Width * WikipediaExample.FrameSize.Height; _tbid = new TableBasedImageData(s, pixelCount, xmlDebugging); Assert.AreEqual(15, _tbid.Pixels.Count); Assert.AreEqual(ErrorState.DataBlockTooShort | ErrorState.TooFewPixelsInImageData, _tbid.ConsolidatedState); Assert.AreEqual(8, _tbid.LzwMinimumCodeSize); Assert.AreEqual(9, _tbid.InitialCodeSize); Assert.AreEqual(Math.Pow(2, 8), _tbid.ClearCode); Assert.AreEqual(Math.Pow(2, 8) + 1, _tbid.EndOfInformation); IndexedPixels expectedIndices = new IndexedPixels(); expectedIndices.Add(0); // first pixel expectedIndices.Add(0); // 2nd pixel expectedIndices.Add(0); // 3rd pixel expectedIndices.Add(0); // 4th pixel expectedIndices.Add(0); // 5th pixel expectedIndices.Add(0); // 6th pixel expectedIndices.Add(0); // 7th pixel expectedIndices.Add(0); // 8th pixel expectedIndices.Add(0); // 9th pixel expectedIndices.Add(0); // 10th pixel expectedIndices.Add(0); // 11th pixel expectedIndices.Add(0); // 12th pixel expectedIndices.Add(0); // 13th pixel expectedIndices.Add(0); // 14th pixel expectedIndices.Add(0); // 15th pixel for (int i = 0; i < 15; i++) { Assert.AreEqual(expectedIndices[i], _tbid.Pixels[i], "pixel " + i); } if (xmlDebugging) { Assert.AreEqual(ExpectedDebugXml, _tbid.DebugXml); } }
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++; }
private void CodeNotInDictionary(bool xmlDebugging) { byte[] bytes = WikipediaExample.ImageDataBytes; bytes[4] = 0xFF; // put an unexpected code into the stream MemoryStream s = new MemoryStream(); s.Write(bytes, 0, bytes.Length); s.Seek(0, SeekOrigin.Begin); int pixelCount = WikipediaExample.FrameSize.Width * WikipediaExample.FrameSize.Height; _tbid = new TableBasedImageData(s, pixelCount, xmlDebugging); Assert.IsTrue(_tbid.TestState(ErrorState.CodeNotInDictionary)); Assert.AreEqual(15, _tbid.Pixels.Count); Assert.AreEqual(8, _tbid.LzwMinimumCodeSize); Assert.AreEqual(9, _tbid.InitialCodeSize); Assert.AreEqual(Math.Pow(2, 8), _tbid.ClearCode); Assert.AreEqual(Math.Pow(2, 8) + 1, _tbid.EndOfInformation); IndexedPixels expectedIndices = new IndexedPixels(); // Check all the pixels have been zero-filled expectedIndices.Add(0); // first pixel expectedIndices.Add(0); // 2nd pixel expectedIndices.Add(0); // 3rd pixel expectedIndices.Add(0); // 4th pixel expectedIndices.Add(0); // 5th pixel expectedIndices.Add(0); // 6th pixel expectedIndices.Add(0); // 7th pixel expectedIndices.Add(0); // 8th pixel expectedIndices.Add(0); // 9th pixel expectedIndices.Add(0); // 10th pixel expectedIndices.Add(0); // 11th pixel expectedIndices.Add(0); // 12th pixel expectedIndices.Add(0); // 13th pixel expectedIndices.Add(0); // 14th pixel expectedIndices.Add(0); // 15th pixel for (int i = 0; i < 15; i++) { Assert.AreEqual(expectedIndices[i], _tbid.Pixels[i], "pixel " + i); } if (xmlDebugging) { Assert.AreEqual(ExpectedDebugXml, _tbid.DebugXml); } }
internal static void CheckImageData(TableBasedImageData imageData) { TableBasedImageData expectedData = ImageData; Assert.AreEqual(expectedData.ClearCode, imageData.ClearCode); Assert.AreEqual(expectedData.DataBlocks.Length, imageData.DataBlocks.Length); Assert.AreEqual(expectedData.EndOfInformation, imageData.EndOfInformation); Assert.AreEqual(expectedData.InitialCodeSize, imageData.InitialCodeSize); Assert.AreEqual(expectedData.LzwMinimumCodeSize, imageData.LzwMinimumCodeSize); Assert.AreEqual(expectedData.Pixels.Count, imageData.Pixels.Count); string info; for (int i = 0; i < expectedData.Pixels.Count; i++) { info = "Pixel index: " + i; Assert.AreEqual(expectedData.Pixels[i], imageData.Pixels[i], info); } for (int i = 0; i < expectedData.DataBlocks.Length; i++) { info = "Data block number: " + i; DataBlock expectedBlock = expectedData.DataBlocks[i]; DataBlock actualBlock = imageData.DataBlocks[i]; Assert.AreEqual(expectedBlock.ActualBlockSize, actualBlock.ActualBlockSize, info); Assert.AreEqual(expectedBlock.DeclaredBlockSize, actualBlock.DeclaredBlockSize, info); for (int b = 0; b < expectedBlock.ActualBlockSize; b++) { Assert.AreEqual(expectedBlock.Data[i], actualBlock.Data[i], info + ". Block index " + b); } } }
private void MissingPixels(bool xmlDebugging) { byte[] bytes = WikipediaExample.ImageDataBytes; MemoryStream s = new MemoryStream(); s.Write(bytes, 0, bytes.Length); s.Seek(0, SeekOrigin.Begin); // The stream contains 15 pixels, this image size implies 18 pixels _tbid = new TableBasedImageData(s, 18, xmlDebugging); Assert.AreEqual(18, _tbid.Pixels.Count); Assert.AreEqual(ErrorState.TooFewPixelsInImageData, _tbid.ConsolidatedState); Assert.AreEqual(8, _tbid.LzwMinimumCodeSize); Assert.AreEqual(9, _tbid.InitialCodeSize); Assert.AreEqual(Math.Pow(2, 8), _tbid.ClearCode); Assert.AreEqual(Math.Pow(2, 8) + 1, _tbid.EndOfInformation); IndexedPixels expectedIndices = new IndexedPixels(); expectedIndices.Add(0); // first pixel is black - index 0 in colour table expectedIndices.Add(1); // 2nd pixel is white - index 1 in colour table expectedIndices.Add(1); // 3rd pixel expectedIndices.Add(1); // 4th pixel expectedIndices.Add(0); // 5th pixel expectedIndices.Add(1); // 6th pixel expectedIndices.Add(1); // 7th pixel expectedIndices.Add(1); // 8th pixel expectedIndices.Add(1); // 9th pixel expectedIndices.Add(1); // 10th pixel expectedIndices.Add(1); // 11th pixel expectedIndices.Add(1); // 12th pixel expectedIndices.Add(1); // 13th pixel expectedIndices.Add(1); // 14th pixel expectedIndices.Add(1); // 15th pixel expectedIndices.Add(0); // 16th pixel expectedIndices.Add(0); // 17th pixel expectedIndices.Add(0); // 18th pixel for (int i = 0; i < 18; i++) { Assert.AreEqual(expectedIndices[i], _tbid.Pixels[i], "pixel " + i); } }
private void EmptyDataBlock(bool xmlDebugging) { MemoryStream s = new MemoryStream(); s.WriteByte(8); // write a valid LZW min code size s.WriteByte(0); s.WriteByte(0); s.Seek(0, SeekOrigin.Begin); _tbid = new TableBasedImageData(s, 100, xmlDebugging); Assert.AreEqual(ErrorState.TooFewPixelsInImageData, _tbid.ConsolidatedState); if (xmlDebugging) { Assert.AreEqual(ExpectedDebugXml, _tbid.DebugXml); } }
public void PixelCountTooSmall() { ReportStart(); int pixelCount = 0; try { _tbid = new TableBasedImageData(new MemoryStream(), pixelCount); } catch (ArgumentOutOfRangeException ex) { string message = "The pixel count must be greater than zero. " + "Supplied value was " + pixelCount; StringAssert.Contains(message, ex.Message); Assert.AreEqual("pixelCount", ex.ParamName); ReportEnd(); throw; } }
private void ConstructorTest(bool xmlDebugging) { byte[] bytes = WikipediaExample.ImageDataBytes; MemoryStream s = new MemoryStream(); s.Write(bytes, 0, bytes.Length); s.Seek(0, SeekOrigin.Begin); int pixelCount = WikipediaExample.FrameSize.Width * WikipediaExample.FrameSize.Height; _tbid = new TableBasedImageData(s, pixelCount, xmlDebugging); Assert.AreEqual(ErrorState.Ok, _tbid.ConsolidatedState); WikipediaExample.CheckImageData(_tbid); if (xmlDebugging) { Assert.AreEqual(ExpectedDebugXml, _tbid.DebugXml); } }
public void HandleRubbish() { ReportStart(); string[] files = Directory.GetFiles(Directory.GetCurrentDirectory()); foreach (string file in files) { byte[] bytes = File.ReadAllBytes(file); MemoryStream s = new MemoryStream(); s.WriteByte(8); // write a valid LZW min code size s.Write(bytes, 0, bytes.Length); s.Seek(0, SeekOrigin.Begin); try { _tbid = new TableBasedImageData(s, 1000000); } catch (Exception ex) { throw new InvalidOperationException(file + ": ", ex); } } ReportEnd(); }
private static void CheckImageData(Stream s, ColourTable act, ImageDescriptor id, Bitmap expectedBitmap) { // read, decode and check image data // Cannot compare encoded LZW data directly as different encoders // will create different colour tables, so even if the bitmaps are // identical, the colour indices will be different int pixelCount = id.Size.Width * id.Size.Height; TableBasedImageData tbid = new TableBasedImageData(s, pixelCount); Assert.AreEqual(ErrorState.Ok, tbid.ConsolidatedState); for (int y = 0; y < id.Size.Height; y++) { for (int x = 0; x < id.Size.Width; x++) { int i = (y * id.Size.Width) + x; Assert.AreEqual(expectedBitmap.GetPixel(x, y), act[tbid.Pixels[i]], "X: " + x + ", Y: " + y); } } }
public void WikipediaExampleTest() { ReportStart(); _e = new AnimatedGifEncoder(); GifFrame frame = new GifFrame(WikipediaExample.ExpectedBitmap); frame.Delay = WikipediaExample.DelayTime; _e.AddFrame(frame); // TODO: some way of creating/testing a UseLocal version of WikipediaExample string fileName = "WikipediaExampleUseGlobal.gif"; _e.WriteToFile(fileName); Stream s = File.OpenRead(fileName); int code; // check GIF header GifHeader gh = new GifHeader(s); Assert.AreEqual(ErrorState.Ok, gh.ConsolidatedState); // check logical screen descriptor LogicalScreenDescriptor lsd = new LogicalScreenDescriptor(s); Assert.AreEqual(ErrorState.Ok, lsd.ConsolidatedState); WikipediaExample.CheckLogicalScreenDescriptor(lsd); // read global colour table ColourTable gct = new ColourTable(s, WikipediaExample.GlobalColourTableSize); Assert.AreEqual(ErrorState.Ok, gct.ConsolidatedState); // cannot compare global colour table as different encoders will // produce difference colour tables. // WikipediaExample.CheckGlobalColourTable( gct ); // check for extension introducer code = ExampleComponent.CallRead(s); Assert.AreEqual(GifComponent.CodeExtensionIntroducer, code); // check for app extension label code = ExampleComponent.CallRead(s); Assert.AreEqual(GifComponent.CodeApplicationExtensionLabel, code); // check netscape extension ApplicationExtension ae = new ApplicationExtension(s); Assert.AreEqual(ErrorState.Ok, ae.ConsolidatedState); NetscapeExtension ne = new NetscapeExtension(ae); Assert.AreEqual(ErrorState.Ok, ne.ConsolidatedState); Assert.AreEqual(0, ne.LoopCount); // check for extension introducer code = ExampleComponent.CallRead(s); Assert.AreEqual(GifComponent.CodeExtensionIntroducer, code); // check for gce label code = ExampleComponent.CallRead(s); Assert.AreEqual(GifComponent.CodeGraphicControlLabel, code); // check graphic control extension GraphicControlExtension gce = new GraphicControlExtension(s); Assert.AreEqual(ErrorState.Ok, gce.ConsolidatedState); WikipediaExample.CheckGraphicControlExtension(gce); // check for image separator code = ExampleComponent.CallRead(s); Assert.AreEqual(GifComponent.CodeImageSeparator, code); // check for image descriptor ImageDescriptor id = new ImageDescriptor(s); Assert.AreEqual(ErrorState.Ok, id.ConsolidatedState); WikipediaExample.CheckImageDescriptor(id); // read, decode and check image data // Cannot compare encoded LZW data directly as different encoders // will create different colour tables, so even if the bitmaps are // identical, the colour indices will be different int pixelCount = WikipediaExample.FrameSize.Width * WikipediaExample.FrameSize.Height; TableBasedImageData tbid = new TableBasedImageData(s, pixelCount); for (int y = 0; y < WikipediaExample.LogicalScreenSize.Height; y++) { for (int x = 0; x < WikipediaExample.LogicalScreenSize.Width; x++) { int i = (y * WikipediaExample.LogicalScreenSize.Width) + x; Assert.AreEqual(WikipediaExample.ExpectedBitmap.GetPixel(x, y), gct[tbid.Pixels[i]], "X: " + x + ", Y: " + y); } } // Check for block terminator after image data code = ExampleComponent.CallRead(s); Assert.AreEqual(0x00, code); // check for GIF trailer code = ExampleComponent.CallRead(s); Assert.AreEqual(GifComponent.CodeTrailer, code); // check we're at the end of the stream code = ExampleComponent.CallRead(s); Assert.AreEqual(-1, code); s.Close(); _d = new GifDecoder(fileName); _d.Decode(); Assert.AreEqual(ErrorState.Ok, _d.ConsolidatedState); BitmapAssert.AreEqual(WikipediaExample.ExpectedBitmap, (Bitmap)_d.Frames[0].TheImage, ""); ReportEnd(); }
/// <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); } }
private static GifFrame DecodeFrame(GraphicControlExtension extension, ImageDescriptor descriptor, TableBasedImageData data, bool filled, int width, int height, Color32[] state, Color32[] colorTable) { var colorIndexes = LzwDecoder.Decode(data.ImageData, data.LzwMinimumCodeSize); return(DecodeFrame(extension, descriptor, colorIndexes, filled, width, height, state, colorTable)); }