private static void CheckExtension(ApplicationExtension ext) { // Check application identifier / authentication code Assert.AreEqual("BIGPANTS", ext.ApplicationIdentifier); Assert.AreEqual("1.3", ext.ApplicationAuthenticationCode); Assert.AreEqual(ErrorState.Ok, ext.ConsolidatedState); // Check number of blocks in application data Assert.AreEqual(3, ext.ApplicationData.Count); // Check application data block 1 Assert.AreEqual(5, ext.ApplicationData[0].DeclaredBlockSize); Assert.AreEqual(5, ext.ApplicationData[0].ActualBlockSize); Assert.AreEqual((byte)'H', ext.ApplicationData[0][0]); Assert.AreEqual((byte)'E', ext.ApplicationData[0][1]); Assert.AreEqual((byte)'L', ext.ApplicationData[0][2]); Assert.AreEqual((byte)'L', ext.ApplicationData[0][3]); Assert.AreEqual((byte)'O', ext.ApplicationData[0][4]); Assert.AreEqual(ErrorState.Ok, ext.ApplicationData[0].ConsolidatedState); // Check application data block 2 Assert.AreEqual(5, ext.ApplicationData[1].DeclaredBlockSize); Assert.AreEqual(5, ext.ApplicationData[1].ActualBlockSize); Assert.AreEqual((byte)'W', ext.ApplicationData[1][0]); Assert.AreEqual((byte)'O', ext.ApplicationData[1][1]); Assert.AreEqual((byte)'R', ext.ApplicationData[1][2]); Assert.AreEqual((byte)'L', ext.ApplicationData[1][3]); Assert.AreEqual((byte)'D', ext.ApplicationData[1][4]); Assert.AreEqual(ErrorState.Ok, ext.ApplicationData[1].ConsolidatedState); }
private void ConstructorStreamTest(bool xmlDebugging) { Stream s = new MemoryStream(); WriteApplicationIdentifier(s); WriteHello(s); WriteWorld(s); WriteBlockTerminator(s); s.Seek(0, SeekOrigin.Begin); // point to beginning of stream _ext = new ApplicationExtension(s, xmlDebugging); CheckExtension(_ext); // Check block terminator Assert.AreEqual(0, _ext.ApplicationData[2].DeclaredBlockSize); Assert.AreEqual(0, _ext.ApplicationData[2].ActualBlockSize); Assert.AreEqual(ErrorState.Ok, _ext.ApplicationData[2].ConsolidatedState); if (xmlDebugging) { Assert.AreEqual(ExpectedDebugXml, _ext.DebugXml); } }
public void ConstructorIdentificationBlockTooLongTest() { ReportStart(); byte[] identification = new byte[] { (byte)'N', (byte)'E', (byte)'T', (byte)'S', (byte)'C', (byte)'A', (byte)'P', (byte)'E', (byte)'2', (byte)'.', (byte)'0', (byte)'1', }; DataBlock identificationBlock = new DataBlock(11, identification); DataBlock data = new DataBlock(1, new byte[] { 1 }); Collection <DataBlock> applicationData = new Collection <DataBlock>(); applicationData.Add(data); _ext = new ApplicationExtension(identificationBlock, applicationData); Assert.AreEqual("NETSCAPE", _ext.ApplicationIdentifier); Assert.AreEqual("2.0", _ext.ApplicationAuthenticationCode); Assert.AreEqual(identificationBlock, _ext.IdentificationBlock); Assert.AreEqual(applicationData, _ext.ApplicationData); Assert.AreEqual(ErrorState.IdentificationBlockTooLong, _ext.ErrorState); ReportEnd(); }
private static void CheckNetscapeExtension(Stream s, int loopCount) { // 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(loopCount, ne.LoopCount); }
public void StartGif() { Width = (ushort)(Frames[0].Texture.width); Height = (ushort)(Frames[0].Texture.height); globalColorTable = new List <Color>(); applicationExtension = new ApplicationExtension(); // byteList = new List<byte>(); encoded = new Dictionary <int, List <byte> >(); colorTables = new List <Color> [Frames.Count]; distinctColors = new Dictionary <int, List <Color> >(); currentStep++; }
// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { //services.AddDbContext<TestDBContext>(options => // options.UseSqlServer(Configuration.GetConnectionString("TestDBConnection"))); //services.AddDatabaseDeveloperPageExceptionFilter(); services.AddControllersWithViews(); // In production, the Angular files will be served from this directory services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp/dist"; }); ApplicationExtension.RegisterApplicationDependencies(services, this.Configuration); }
public void WriteToStreamTest() { ReportStart(); byte[] identificationData = new byte[] { (byte)'B', (byte)'I', (byte)'G', (byte)'P', (byte)'A', (byte)'N', (byte)'T', (byte)'S', (byte)'1', (byte)'.', (byte)'3' }; DataBlock identificationBlock = new DataBlock(11, identificationData); byte[] helloData = new byte[] { (byte)'H', (byte)'E', (byte)'L', (byte)'L', (byte)'O' }; DataBlock helloBlock = new DataBlock(5, helloData); byte[] worldData = new byte[] { (byte)'W', (byte)'O', (byte)'R', (byte)'L', (byte)'D' }; DataBlock worldBlock = new DataBlock(5, worldData); DataBlock blockTerminator = new DataBlock(0, new byte[] { 0 }); Collection <DataBlock> applicationData = new Collection <DataBlock>(); applicationData.Add(helloBlock); applicationData.Add(worldBlock); applicationData.Add(blockTerminator); _ext = new ApplicationExtension(identificationBlock, applicationData); MemoryStream s = new MemoryStream(); _ext.WriteToStream(s); s.Seek(0, SeekOrigin.Begin); ApplicationExtension e = new ApplicationExtension(s); CheckExtension(e); ReportEnd(); }
public override void ShowDialog(PropertyItemValue propertyValue, IInputElement commandSource) { if (propertyValue == null) { return; } if (propertyValue.ParentProperty.IsReadOnly) { return; } OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.AllowMultiple = false; openFileDialog.Filters = new List <FileDialogFilter> { new FileDialogFilter() { Name = "Image Files (*.jpg, *.png, *.bmp)" , Extensions = new List <string>() { "jpg", "png", "bmp" } } }; var mainWindow = ApplicationExtension.GetMainWindow(); openFileDialog.ShowAsync(mainWindow).ContinueWith(x => { if (x.IsFaulted == false) { string result = x.Result.FirstOrDefault(); if (string.IsNullOrEmpty(result) == false) { propertyValue.StringValue = result; } } }, TaskScheduler.FromCurrentSynchronizationContext()); }
public FileBrowserDialogPropertyValueEditor() { //little ugly search for the resource var mainWindow = ApplicationExtension.GetMainWindow(); if (mainWindow != null) { object res = null; foreach (var item in mainWindow.FindChildren <UserControl>(true)) { if (item.TryFindResource(LocalResources.FileBrowserEditorKey, out res)) { break; } } InlineTemplate = res; } }
public override void ShowDialog(PropertyItemValue propertyValue, IInputElement commandSource) { if (propertyValue == null) { return; } if (propertyValue.ParentProperty.IsReadOnly) { return; } OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.AllowMultiple = false; var property = propertyValue.ParentProperty; if (property != null) { var optionsAttribute = (OpenFileDialogOptionsAttribute)property.Attributes[typeof(OpenFileDialogOptionsAttribute)]; if (optionsAttribute != null) { optionsAttribute.ConfigureDialog(openFileDialog); } } var mainWindow = ApplicationExtension.GetMainWindow(); openFileDialog.ShowAsync(mainWindow).ContinueWith(x => { if (x.IsFaulted == false) { string result = x.Result.FirstOrDefault(); if (string.IsNullOrEmpty(result) == false) { propertyValue.StringValue = result; } } }, TaskScheduler.FromCurrentSynchronizationContext()); }
/// <summary> /// Populates the application extension byte[] from the source image, and performs lexical parsing on the array. /// </summary> /// <param name="bytes"></param> public static ApplicationExtension ParseApplicationExtension(byte[] bytes) { ApplicationExtension ae = new ApplicationExtension() { Bytes = (byte[])bytes.Clone(), TotalBlockLength = bytes.Length, ExtensionIntroducer = bytes[0], ExtensionLabel = bytes[1], MainBlockSize = bytes[2], MainBlock = new byte[bytes[2]] }; Array.Copy(bytes, 3, ae.MainBlock, 0, ae.MainBlockSize); ae.SubBlockSize = ae.Bytes[ae.MainBlockSize + 3]; if (ae.IsNetscapeExtension()) { ae.SubBlock = new byte[ae.SubBlockSize]; Array.Copy(bytes, ae.MainBlockSize + 3, ae.SubBlock, 0, ae.SubBlockSize); } else if (ae.IsXmpDataExtension()) { int blockTerminatorIndex = ApplicationExtension.GetXmpExtensionBlockTerminatorIndex(bytes).Value; // todo: offer propert support for XMP Data extensions // ae.SubBlockSize = blockTerminatorIndex - (ae.MainBlock.Length + 3); // ae.SubBlock = new byte[ae.SubBlockSize]; // Array.Copy(bytes, ae.SubBlock, ae.SubBlockSize); // ae.TotalBlockLength = ApplicationExtension.MainBlockSize + ApplicationExtension.SubBlockSize + 4; } if (bytes[ae.TotalBlockLength - 1] != (byte)Global.BlockTerminator) { throw new ApplicationExtensionParserException(); } else { return(ae); } }
private void ConstructorStreamTestNoBlockTerminator(bool xmlDebugging) { Stream s = new MemoryStream(); WriteApplicationIdentifier(s); WriteHello(s); WriteWorld(s); // Don't write a block terminator - that's the point of this test s.Seek(0, SeekOrigin.Begin); // point to start of stream // Instantiate the ApplicationExtension _ext = new ApplicationExtension(s, xmlDebugging); Assert.AreEqual(ErrorState.EndOfInputStream, _ext.ConsolidatedState); if (xmlDebugging) { Assert.AreEqual(ExpectedDebugXml, _ext.DebugXml); } }
public void ConstructorTest() { byte[] idData = new byte[] { (byte)'N', (byte)'E', (byte)'T', (byte)'S', (byte)'C', (byte)'A', (byte)'P', (byte)'E', (byte)'2', (byte)'.', (byte)'0', }; DataBlock idBlock = new DataBlock(11, idData); Collection <DataBlock> appData = new Collection <DataBlock>(); // First byte in the data block is always 1 for a netscape extension // Second and third bytes are the loop count, lsb first byte[] loopCount = new byte[] { 1, 4, 0 }; appData.Add(new DataBlock(3, loopCount)); // Add the block terminator appData.Add(new DataBlock(0, new byte[0])); ApplicationExtension appExt = new ApplicationExtension(idBlock, appData); _ne = new NetscapeExtension(appExt); Assert.AreEqual(4, _ne.LoopCount); Assert.AreEqual("NETSCAPE", _ne.ApplicationIdentifier); Assert.AreEqual("2.0", _ne.ApplicationAuthenticationCode); Assert.AreEqual(ErrorState.Ok, _ne.ConsolidatedState); }
public void ConstructorIdentificationBlockTooShortTest() { ReportStart(); byte[] identification = new byte[] { (byte)'N', (byte)'E', (byte)'T', (byte)'S', (byte)'C', (byte)'A', (byte)'P', (byte)'E', (byte)'2', (byte)'.', }; DataBlock identificationBlock = new DataBlock(11, identification); DataBlock data = new DataBlock(1, new byte[] { 1 }); Collection <DataBlock> applicationData = new Collection <DataBlock>(); applicationData.Add(data); try { _ext = new ApplicationExtension(identificationBlock, applicationData); } catch (ArgumentException ex) { string message = "The identification block should be 11 bytes long but " + "is only 10 bytes."; StringAssert.Contains(message, ex.Message); ReportEnd(); throw; } }
public void ConstructorTestNotNetscape() { byte[] idData = new byte[] { (byte)'B', (byte)'I', (byte)'G', (byte)'P', (byte)'A', (byte)'N', (byte)'T', (byte)'S', (byte)'2', (byte)'.', (byte)'0', }; DataBlock idBlock = new DataBlock(11, idData); ApplicationExtension appExt = new ApplicationExtension(idBlock, new Collection <DataBlock>()); try { _ne = new NetscapeExtension(appExt); } catch (ArgumentException ex) { string message = "The application identifier is not 'NETSCAPE' " + "therefore this application extension is not a " + "Netscape extension. Application identifier: BIGPANTS"; StringAssert.Contains(message, ex.Message); Assert.AreEqual("applicationExtension", ex.ParamName); throw; } }
public void ConstructorTestNot2Point0() { byte[] idData = new byte[] { (byte)'N', (byte)'E', (byte)'T', (byte)'S', (byte)'C', (byte)'A', (byte)'P', (byte)'E', (byte)'3', (byte)'.', (byte)'0', }; DataBlock idBlock = new DataBlock(11, idData); ApplicationExtension appExt = new ApplicationExtension(idBlock, new Collection <DataBlock>()); try { _ne = new NetscapeExtension(appExt); } catch (ArgumentException ex) { string message = "The application authentication code is not '2.0' " + "therefore this application extension is not a " + "Netscape extension. Application authentication code: 3.0"; StringAssert.Contains(message, ex.Message); Assert.AreEqual("applicationExtension", ex.ParamName); throw; } }
/// <summary> /// Main file parser. Reads GIF content blocks. /// </summary> /// <param name="inputStream"> /// Input stream from which the GIF data is to be read. /// </param> private void ReadContents(Stream inputStream) { // read GIF file content blocks bool done = false; GraphicControlExtension lastGce = null; string message; // for error conditions while (!done && ConsolidatedState == ErrorState.Ok) { int code = Read(inputStream); MyProgressCounters[_readStreamCounterText].Value = (int)inputStream.Position; WriteCodeToDebugXml(code); switch (code) { case CodeImageSeparator: WriteDebugXmlComment("0x2C - image separator"); AddFrame(inputStream, lastGce); MyProgressCounters[_readStreamCounterText].Value = (int)inputStream.Position; break; case CodeExtensionIntroducer: WriteDebugXmlComment("0x21 - extension introducer"); code = Read(inputStream); MyProgressCounters[_readStreamCounterText].Value = (int)inputStream.Position; WriteCodeToDebugXml(code); switch (code) { case CodePlaintextLabel: // FEATURE: handle plain text extension WriteDebugXmlComment("0x01 - plain text extension"); SkipBlocks(inputStream); MyProgressCounters[_readStreamCounterText].Value = (int)inputStream.Position; break; case CodeGraphicControlLabel: WriteDebugXmlComment("0xF9 - graphic control label"); lastGce = new GraphicControlExtension(inputStream, XmlDebugging); MyProgressCounters[_readStreamCounterText].Value = (int)inputStream.Position; WriteDebugXmlNode(lastGce.DebugXmlReader); break; case CodeCommentLabel: // FEATURE: handle comment extension WriteDebugXmlComment("0xFE - comment extension"); SkipBlocks(inputStream); MyProgressCounters[_readStreamCounterText].Value = (int)inputStream.Position; break; case CodeApplicationExtensionLabel: WriteDebugXmlComment("0xFF - application extension label"); ApplicationExtension ext = new ApplicationExtension(inputStream, XmlDebugging); MyProgressCounters[_readStreamCounterText].Value = (int)inputStream.Position; WriteDebugXmlNode(ext.DebugXmlReader); if (ext.ApplicationIdentifier == "NETSCAPE" && ext.ApplicationAuthenticationCode == "2.0") { _netscapeExtension = new NetscapeExtension(ext); } else { // TESTME: ReadContents - non-Netscape application extension _applicationExtensions.Add(ext); } break; default: // uninteresting extension // TESTME: ReadContents - uninteresting extension WriteDebugXmlComment("Ignoring this extension"); SkipBlocks(inputStream); MyProgressCounters[_readStreamCounterText].Value = (int)inputStream.Position; break; } break; case CodeTrailer: // We've reached an explicit end-of-data marker, so stop // processing the stream. WriteDebugXmlComment("0x3B - end of data"); done = true; break; case 0x00: // bad byte, but keep going and see what happens WriteDebugXmlComment("0x00 - unexpected code"); message = "Unexpected block terminator encountered at " + "position " + inputStream.Position + " after reading " + _frames.Count + " frames."; SetStatus(ErrorState.UnexpectedBlockTerminator, message); break; case -1: // reached the end of the input stream WriteDebugXmlComment("-1 - end of input stream"); message = "Reached the end of the input stream without " + "encountering trailer 0x3b"; SetStatus(ErrorState.EndOfInputStream, message); break; default: WriteDebugXmlComment("Not a recognised code"); message = "Bad data block introducer: 0x" + code.ToString("X", CultureInfo.InvariantCulture).PadLeft(2, '0') + " (" + code + ") at position " + inputStream.Position + " (" + inputStream.Position.ToString("X", CultureInfo.InvariantCulture) + " hex) after reading " + _frames.Count + " frames."; SetStatus(ErrorState.BadDataBlockIntroducer, message); break; } } }
/// <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); } }
/// <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> /// Compiles the parsed tokens/components back into a byte array. /// </summary> public void CompileByteArray() { List <byte> output = new List <byte>(); output.AddRange(DataStream.Header.Bytes); output.AddRange(DataStream.LogicalScreen.LogicalScreenDescriptor.Bytes); if (DataStream.LogicalScreen.GlobalColorTable != null) { output.AddRange(DataStream.LogicalScreen.GlobalColorTable.Bytes); } foreach (DataBlock db in DataStream.DataBlocks) { if (db is GraphicBlock) { GraphicBlock gb = (GraphicBlock)db; if (gb.GraphicControlExtension != null) { output.AddRange(gb.GraphicControlExtension.Bytes); } if (gb.GraphicRenderingBlock is PlainTextExtension) { PlainTextExtension pte = (PlainTextExtension)gb.GraphicRenderingBlock; output.AddRange(pte.Bytes); } else if (gb.GraphicRenderingBlock is TableBasedImage) { TableBasedImage tbi = (TableBasedImage)gb.GraphicRenderingBlock; output.AddRange(tbi.ImageDescriptor.Bytes); if (tbi.LocalColorTable != null) { output.AddRange(tbi.LocalColorTable.Bytes); } output.AddRange(tbi.ImageData.Bytes); } } else if (db is SpecialPurposeBlock) { SpecialPurposeBlock spb = (SpecialPurposeBlock)db; if (spb is ApplicationExtension) { ApplicationExtension ae = (ApplicationExtension)spb; output.AddRange(ae.Bytes); } else if (spb is CommentExtension) { CommentExtension ce = (CommentExtension)spb; output.AddRange(ce.Bytes); } } } output.AddRange(DataStream.Trailer.Bytes); DataStream.Bytes = output.ToArray(); }
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(); }
private static List <Block> ReadBlocks(byte[] bytes, ref int startIndex) { var blocks = new List <Block>(); var index = startIndex; while (true) { switch (bytes[index]) { case Block.ExtensionIntroducer: { Block extension; switch (bytes[index + 1]) { case Block.PlainTextExtensionLabel: extension = new PlainTextExtension(bytes, ref index); break; case Block.GraphicControlExtensionLabel: extension = new GraphicControlExtension(bytes, ref index); break; case Block.CommentExtensionLabel: extension = new CommentExtension(bytes, ref index); break; case Block.ApplicationExtensionLabel: extension = new ApplicationExtension(bytes, ref index); break; default: throw new NotSupportedException("Unknown extension!"); } blocks.Add(extension); break; } case Block.ImageDescriptorLabel: { var descriptor = new ImageDescriptor(bytes, ref index); blocks.Add(descriptor); if (descriptor.LocalColorTableFlag == 1) { var localColorTable = new ColorTable(descriptor.LocalColorTableSize, bytes, ref index); blocks.Add(localColorTable); } var data = new TableBasedImageData(bytes, ref index); blocks.Add(data); break; } case 0x3B: // End { return(blocks); } default: throw new NotSupportedException($"Unsupported GIF block: {bytes[index]:X}."); } } }
/// <summary> /// Tokenizes the GIF image's byte[] into constituent components. /// </summary> public static DataStream ParseDataStream(byte[] data) { DataStream ds = new DataStream(); using (MemoryStream ms = new MemoryStream(data)) { using (BinaryReader br = new BinaryReader(ms)) { ds.Header = VcvjParser.ParseHeader(br.ReadBytes(Header.TotalBlockLength)); ds.LogicalScreen = new LogicalScreen() { LogicalScreenDescriptor = VcvjParser.ParseLogicalScreenDescriptor(br.ReadBytes(LogicalScreenDescriptor.TotalBlockLength)) }; if (ds.LogicalScreen.LogicalScreenDescriptor.HasGlobalColorTable) { ds.LogicalScreen.GlobalColorTable = VcvjParser.ParseGlobalColorTable( br.ReadBytes(ds.LogicalScreen.LogicalScreenDescriptor.GlobalColorTableLength) ); } while (br.PeekChar() != -1) { long currentIndex = br.BaseStream.Position; byte currentByte = data[currentIndex]; byte?nextByte = currentIndex + 1 < data.Length ? (byte?)data[currentIndex + 1] : (byte?)null; if (ParserUtils.IsTrailerMarker(currentByte)) { ds.Trailer = new Trailer(br.ReadByte()); continue; } else if (ParserUtils.IsApplicationExtensionBlock(currentByte, nextByte)) { ApplicationExtension ae = VcvjParser.ParseApplicationExtension( br.ReadBytes(VcvjParser.GetApplicationExtensionBlockLength(currentIndex, ref data)) ); ds.DataBlocks.Add(ae); continue; } else if (ParserUtils.IsCommentExtensionBlock(currentByte, nextByte)) { int totalBlockLength = data[currentIndex + 2] + 4; CommentExtension ce = VcvjParser.ParseCommentExtension(br.ReadBytes(totalBlockLength)); ds.DataBlocks.Add(ce); continue; } else { GraphicBlock gb = new GraphicBlock(); if (ParserUtils.IsGraphicControlExtensionBlock(currentByte, nextByte)) { gb.GraphicControlExtension = VcvjParser.ParseGraphicControlExtension( br.ReadBytes(8) ); currentIndex = br.BaseStream.Position; currentByte = data[currentIndex]; nextByte = data[currentIndex + 1]; } if (ParserUtils.IsImageDescriptor(currentByte)) { TableBasedImage tbi = new TableBasedImage() { ImageDescriptor = VcvjParser.ParseImageDescriptor( br.ReadBytes(10) ) }; if (tbi.ImageDescriptor.LocalColorTableFlag) { tbi.LocalColorTable = VcvjParser.ParseLocalColorTable( br.ReadBytes(tbi.ImageDescriptor.LocalColorTableLength) ); } currentIndex = br.BaseStream.Position; currentByte = data[currentIndex]; nextByte = data[currentIndex + 1]; tbi.ImageData = VcvjParser.ParseImageData( br.ReadBytes(VcvjParser.GetImageDataBlockLength(currentIndex, ref data)) ); gb.GraphicRenderingBlock = tbi; ds.DataBlocks.Add(gb); continue; } else if (ParserUtils.IsPlaintextExtension(currentByte, nextByte)) { //GraphicBlock gb = new GraphicBlock() //{ //}; } else if (ParserUtils.IsApplicationExtensionBlock(currentByte, nextByte)) { ApplicationExtension ae = VcvjParser.ParseApplicationExtension( br.ReadBytes(VcvjParser.GetApplicationExtensionBlockLength(currentIndex, ref data)) ); ds.DataBlocks.Add(ae); continue; } else if (ParserUtils.IsCommentExtensionBlock(currentByte, nextByte)) { int totalBlockLength = data[currentIndex + 2] + 4; CommentExtension ce = VcvjParser.ParseCommentExtension(br.ReadBytes(totalBlockLength)); ds.DataBlocks.Add(ce); continue; } else { throw new UnidentifiedBlockException( string.Format( "The current byte is {0} and the next one is {1}. Not really sure what to make of this.", currentByte.ToString(), String.IsNullOrEmpty(nextByte.ToString()) ? "null" : nextByte.ToString() ) ); } } } } } return(ds); }
/// <summary> /// Returns the length of the parsed data stream byte[]. /// </summary> /// <returns></returns> public int GetLength() { int length = 0; length += Header.TotalBlockLength + LogicalScreenDescriptor.TotalBlockLength + Trailer.TotalBlockLength; if (LogicalScreen.GlobalColorTable != null) { length += LogicalScreen.GlobalColorTable.TotalBlockLength; } foreach (DataBlock db in DataBlocks) { if (db is GraphicBlock) { GraphicBlock gb = (GraphicBlock)db; if (gb.GraphicControlExtension != null) { length += GraphicControlExtension.TotalBlockLength; } if (gb.GraphicRenderingBlock is PlainTextExtension) { PlainTextExtension pte = (PlainTextExtension)gb.GraphicRenderingBlock; length += pte.TotalBlockLength; } else if (gb.GraphicRenderingBlock is TableBasedImage) { TableBasedImage tbi = (TableBasedImage)gb.GraphicRenderingBlock; length += tbi.ImageData.TotalBlockLength + ImageDescriptor.TotalBlockLength; if (tbi.LocalColorTable != null) { length += tbi.LocalColorTable.TotalBlockLength; } } } else if (db is SpecialPurposeBlock) { SpecialPurposeBlock spb = (SpecialPurposeBlock)db; if (spb is ApplicationExtension) { ApplicationExtension ae = (ApplicationExtension)spb; length += ae.TotalBlockLength; } else if (spb is CommentExtension) { CommentExtension ce = (CommentExtension)spb; length += ce.TotalBlockLength; } } } return(length); }