예제 #1
0
        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);
            }
        }
예제 #2
0
        /// <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 + "%");
        }
예제 #3
0
        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();
        }
예제 #4
0
        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);
            }
        }
예제 #5
0
        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++;
        }
예제 #6
0
        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);
            }
        }
예제 #7
0
        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);
                }
            }
        }
예제 #8
0
        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);
            }
        }
예제 #9
0
        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);
            }
        }
예제 #10
0
        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;
            }
        }
예제 #11
0
        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);
            }
        }
예제 #12
0
 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();
 }
예제 #13
0
        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);
                }
            }
        }
예제 #14
0
        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();
        }
예제 #15
0
        /// <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);
        }
예제 #16
0
        /// <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);
            }
        }
예제 #17
0
        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));
        }