コード例 #1
0
        void WriteFrame(Frame frame)
        {
            Trace.WriteLine(string.Format("Gif89a.WriteFrame [{0}]", System.Threading.Thread.CurrentThread.ManagedThreadId));

            var sw = new Stopwatch();

            sw.Start();

            if (frame.OpaqueFramePixelBytes == null)
            {
                return;
            }

            // if we're discarding duplicate frames
            if (frame.OpaqueFramePixelBytes.Length == 0 && this.optimization.HasFlag(FrameOptimization.DiscardDuplicates))
            {
                // hang on to where we currently are in the output stream
                var here = this.output.Position;

                // the last written GCE might be more than one frame back, if there's a bunch of duplicates
                var prev = frame;
                while (prev.OutputStreamGCEIndex == 0)
                {
                    prev = this.frames[this.frames.IndexOf(prev) - 1];
                }

                // jump back to the GCE in the previous frame
                this.output.Position = prev.OutputStreamGCEIndex;

                prev.Delay += frame.Delay;
                GifFileFormat.WriteGraphicControlExtension(output, prev, prev.transIndex);

                // jump forward to where we just were to continue on normally
                this.output.Position = here;

                return;
            }

            if (frame.IndexedPixels.Length == 0)
            {
                return;
            }

            if (frame == this.frames.First())
            {
                // logical screen descriptor
                GifFileFormat.WriteLogicalScreenDescriptor(this.output, (ushort)this.Size.Width, (ushort)this.Size.Height, frame.ColorTableBytes.Length);

                // global color table
                GifFileFormat.WriteColorTable(this.output, frame.ColorTableBytes);

                if (this.Repeat >= 0)
                {
                    // use NS app extension to indicate reps
                    GifFileFormat.WriteNetscapeApplicationExtension(this.output, this.Repeat);
                }
            }

            // write graphic control extension
            frame.OutputStreamGCEIndex = this.output.Position;
            GifFileFormat.WriteGraphicControlExtension(output, frame, frame.transIndex);

            // image descriptor
            GifFileFormat.WriteImageDescriptor(this.output, frame.ChangeRect, frame.ColorTableBytes.Length, frame == this.frames.First());

            if (frame != this.frames.First())
            {
                // local color table
                GifFileFormat.WriteColorTable(this.output, frame.ColorTableBytes);
            }

            // encode and write pixel data
            var lzw = new LZWEncoder(frame.ChangeRect.Width, frame.ChangeRect.Height, frame.IndexedPixels, 8);

            lzw.encode(this.output);

            sw.Stop();
            Trace.WriteLine($"Done ({ sw.Elapsed })", string.Format("Gif89a.WriteFrame [{0}]", System.Threading.Thread.CurrentThread.ManagedThreadId));
        }
コード例 #2
0
        void WriteGifToStream()
        {
            Trace.WriteLine(string.Format("Gif89a.WriteGifToStream [{0}]", System.Threading.Thread.CurrentThread.ManagedThreadId));

            GifFileFormat.WriteFileHeader(this.output);

            var nextFrameIndex = 0;
            var pendingFrames  = new List <Frame>();

            while (!FrameWriteQueue.IsCompleted)
            {
                Frame someFrame = null;
                try
                {
                    someFrame = FrameWriteQueue.Take();
                }
                catch (InvalidOperationException)
                {
                    Trace.WriteLine("No more frames to write", string.Format("Gif89a.WriteGifToStream [{0}]", System.Threading.Thread.CurrentThread.ManagedThreadId));
                    break;
                }

                // frame analysis could can (and does!) occur out-of-order
                // so every time a frame is popped from the FrameWriteQueue, it might not be the "next" frame
                // no problem: just hang on to the writable frames and then write as much as possible in order
                pendingFrames.Add(someFrame);

                while (nextFrameIndex < this.frames.Count && pendingFrames.Contains(this.frames[nextFrameIndex]))
                {
                    this.WriteFrame(this.frames[nextFrameIndex]);

                    // no need to hang on to it here any more
                    pendingFrames.Remove(someFrame);

                    // we're done with the raw frame data, so let it unload
                    if (nextFrameIndex > 0)
                    {
                        // don't dispose the current frame, since it could be a "prev" frame for a frame that's currently being analyzed
                        this.frames[nextFrameIndex - 1].Dispose();
                    }

                    // allow more frames to be loaded
                    FrameLoadingDelay.Set();

                    this.OnFrameWriteProgress?.Invoke(this, new FrameWriteProgressEventArgs((float)nextFrameIndex * 100.0f / (float)(this.frames.Count + this.FrameLoadingQueue.Count)));

                    nextFrameIndex++;
                }

                output.Flush();
            }

            // the final frame is now safe to dispose since there's no "next" for it to be a "prev" for
            if (this.frames.Any())
            {
                this.frames.Last().Dispose();
            }

            GifFileFormat.WriteFileTrailer(output);
            output.Flush();

            Trace.WriteLine("Done", string.Format("Gif89a.WriteGifToStream [{0}]", System.Threading.Thread.CurrentThread.ManagedThreadId));

            // announce that stream writing is done
            FrameWriteComplete.Set();
        }