private void ApplyControlValues(AnimationClipOptions value, bool isNew)
        {
            var controls = GetInputGroup(isNew);

            if (value != null)
            {
                controls[0].ValueObject = value.StartTime;
                controls[1].ValueObject = value.StopTime;
                controls[2].ValueObject = value.Left;
                controls[3].ValueObject = value.Top;
                controls[4].ValueObject = value.Right;
                controls[5].ValueObject = value.Bottom;
                controls[6].ValueObject = value.OutputWidth;
                controls[7].ValueObject = value.OutputHeight;
            }
            else
            {
                foreach (var txt in controls)
                {
                    txt.ValueObject = null;
                }
            }
        }
Exemple #2
0
        public bool SaveAsGif(AnimationItem aniItem, string fileName, ImageHandlerConfig config, bool options)
        {
            var rec = new AnimationRecoder(this.GraphicsDevice);

            rec.Items.Add(aniItem);
            int length   = rec.GetMaxLength();
            int delay    = Math.Max(10, config.MinDelay);
            var timeline = rec.GetGifTimeLine(delay, 655350);

            // calc available canvas area
            rec.ResetAll();
            Microsoft.Xna.Framework.Rectangle bounds = aniItem.Measure();
            if (length > 0)
            {
                IEnumerable <int> delays = timeline?.Take(timeline.Length - 1)
                                           ?? Enumerable.Range(0, (int)Math.Ceiling(1.0 * length / delay) - 1);

                foreach (var frameDelay in delays)
                {
                    rec.Update(TimeSpan.FromMilliseconds(frameDelay));
                    var rect = aniItem.Measure();
                    bounds = Microsoft.Xna.Framework.Rectangle.Union(bounds, rect);
                }
            }
            bounds.Offset(aniItem.Position);

            // customize clip/scale options
            AnimationClipOptions clipOptions = new AnimationClipOptions()
            {
                StartTime    = 0,
                StopTime     = length,
                Left         = bounds.Left,
                Top          = bounds.Top,
                Right        = bounds.Right,
                Bottom       = bounds.Bottom,
                OutputWidth  = bounds.Width,
                OutputHeight = bounds.Height,
            };

            if (options)
            {
                var frmOptions = new FrmGifClipOptions()
                {
                    ClipOptions    = clipOptions,
                    ClipOptionsNew = clipOptions,
                };
                if (frmOptions.ShowDialog() == DialogResult.OK)
                {
                    var clipOptionsNew = frmOptions.ClipOptionsNew;
                    clipOptions.StartTime = clipOptionsNew.StartTime ?? clipOptions.StartTime;
                    clipOptions.StopTime  = clipOptionsNew.StopTime ?? clipOptions.StopTime;

                    clipOptions.Left   = clipOptionsNew.Left ?? clipOptions.Left;
                    clipOptions.Top    = clipOptionsNew.Top ?? clipOptions.Top;
                    clipOptions.Right  = clipOptionsNew.Right ?? clipOptions.Right;
                    clipOptions.Bottom = clipOptionsNew.Bottom ?? clipOptions.Bottom;

                    clipOptions.OutputWidth  = clipOptionsNew.OutputWidth ?? (clipOptions.Right - clipOptions.Left);
                    clipOptions.OutputHeight = clipOptionsNew.OutputHeight ?? (clipOptions.Bottom - clipOptions.Top);
                }
                else
                {
                    return(false);
                }
            }

            // validate params
            bounds = new Rectangle(
                clipOptions.Left.Value,
                clipOptions.Top.Value,
                clipOptions.Right.Value - clipOptions.Left.Value,
                clipOptions.Bottom.Value - clipOptions.Top.Value
                );
            var targetSize = new Point(clipOptions.OutputWidth.Value, clipOptions.OutputHeight.Value);
            var startTime  = clipOptions.StartTime.Value;
            var stopTime   = clipOptions.StopTime.Value;

            if (bounds.Width <= 0 || bounds.Height <= 0 ||
                targetSize.X <= 0 || targetSize.Y <= 0 ||
                startTime < 0 || stopTime > length ||
                stopTime - startTime < 0)
            {
                return(false);
            }
            length = stopTime - startTime;

            // create output dir
            string framesDirName = Path.Combine(Path.GetDirectoryName(fileName), Path.GetFileNameWithoutExtension(fileName) + ".frames");

            if (config.SavePngFramesEnabled && !Directory.Exists(framesDirName))
            {
                Directory.CreateDirectory(framesDirName);
            }

            // pre-render
            rec.ResetAll();
            switch (config.BackgroundType.Value)
            {
            default:
            case ImageBackgroundType.Transparent:
                rec.BackgroundColor = Color.TransparentBlack;
                break;

            case ImageBackgroundType.Color:
                rec.BackgroundColor = System.Drawing.Color.FromArgb(255, config.BackgroundColor.Value).ToXnaColor();
                break;

            case ImageBackgroundType.Mosaic:
                rec.BackgroundImage = MonogameUtils.CreateMosaic(GraphicsDevice,
                                                                 config.MosaicInfo.Color0.ToXnaColor(),
                                                                 config.MosaicInfo.Color1.ToXnaColor(),
                                                                 Math.Max(1, config.MosaicInfo.BlockSize));
                break;
            }

            // select encoder
            GifEncoder enc       = AnimateEncoderFactory.CreateEncoder(fileName, targetSize.X, targetSize.Y, config);
            var        encParams = AnimateEncoderFactory.GetEncoderParams(config.GifEncoder.Value);

            // pipeline functions
            IEnumerable <Tuple <byte[], int> > MergeFrames(IEnumerable <Tuple <byte[], int> > frames)
            {
                byte[] prevFrame = null;
                int    prevDelay = 0;

                foreach (var frame in frames)
                {
                    byte[] currentFrame = frame.Item1;
                    int    currentDelay = frame.Item2;

                    if (prevFrame == null)
                    {
                        prevFrame = currentFrame;
                        prevDelay = currentDelay;
                    }
                    else if (memcmp(prevFrame, currentFrame, (IntPtr)prevFrame.Length) == 0)
                    {
                        prevDelay += currentDelay;
                    }
                    else
                    {
                        yield return(Tuple.Create(prevFrame, prevDelay));

                        prevFrame = currentFrame;
                        prevDelay = currentDelay;
                    }
                }

                if (prevFrame != null)
                {
                    yield return(Tuple.Create(prevFrame, prevDelay));
                }
            }

            IEnumerable <int> RenderDelay()
            {
                int t = 0;

                while (t < length)
                {
                    int frameDelay = Math.Min(length - t, delay);
                    t += frameDelay;
                    yield return(frameDelay);
                }
            }

            IEnumerable <int> ClipTimeline(int[] _timeline)
            {
                int t = 0;

                for (int i = 0; i < timeline.Length; i++)
                {
                    var frameDelay = timeline[i];
                    if (t < startTime)
                    {
                        if (t + frameDelay > startTime)
                        {
                            frameDelay = t + frameDelay - startTime;
                            t          = startTime;
                        }
                        else
                        {
                            t += frameDelay;
                            continue;
                        }
                    }

                    if (t + frameDelay < stopTime)
                    {
                        yield return(frameDelay);

                        t += frameDelay;
                    }
                    else
                    {
                        frameDelay = stopTime - t;
                        yield return(frameDelay);

                        break;
                    }
                }
            }

            int prevTime = 0;

            async Task <int> ApplyFrame(byte[] frameData, int frameDelay)
            {
                byte[] gifData = null;
                if (!encParams.SupportAlphaChannel && config.BackgroundType.Value == ImageBackgroundType.Transparent)
                {
                    using (var rt2 = rec.GetGifTexture(config.BackgroundColor.Value.ToXnaColor(), config.MinMixedAlpha))
                    {
                        if (gifData == null)
                        {
                            gifData = new byte[frameData.Length];
                        }
                        rt2.GetData(gifData);
                    }
                }
                else
                {
                    gifData = frameData;
                }

                var tasks = new List <Task>();

                // save each frame as png
                if (config.SavePngFramesEnabled)
                {
                    tasks.Add(Task.Run(() =>
                    {
                        string pngFileName = Path.Combine(framesDirName, $"{prevTime}_{prevTime + frameDelay}.png");
                        unsafe
                        {
                            fixed(byte *pFrameBuffer = frameData)
                            {
                                using (var bmp = new System.Drawing.Bitmap(targetSize.X, targetSize.Y, targetSize.X * 4, System.Drawing.Imaging.PixelFormat.Format32bppArgb, new IntPtr(pFrameBuffer)))
                                {
                                    bmp.Save(pngFileName, System.Drawing.Imaging.ImageFormat.Png);
                                }
                            }
                        }
                    }));
                }

                // append frame data to gif stream
                tasks.Add(Task.Run(() =>
                {
                    // TODO: only for gif here?
                    frameDelay = Math.Max(10, (int)(Math.Round(frameDelay / 10.0) * 10));
                    unsafe
                    {
                        fixed(byte *pGifBuffer = gifData)
                        {
                            enc.AppendFrame(new IntPtr(pGifBuffer), frameDelay);
                        }
                    }
                }));

                await Task.WhenAll(tasks);

                prevTime += frameDelay;
                return(prevTime);
            }

            async Task RenderJob(IProgressDialogContext context, CancellationToken cancellationToken)
            {
                bool isCompareAndMergeFrames = timeline == null;

                // build pipeline
                IEnumerable <int> delayEnumerator = timeline == null?RenderDelay() : ClipTimeline(timeline);

                var step1 = delayEnumerator.TakeWhile(_ => !cancellationToken.IsCancellationRequested);
                var frameRenderEnumerator = step1.Select(frameDelay =>
                {
                    rec.Draw();
                    rec.Update(TimeSpan.FromMilliseconds(frameDelay));
                    return(frameDelay);
                });
                var step2        = frameRenderEnumerator.TakeWhile(_ => !cancellationToken.IsCancellationRequested);
                var getFrameData = step2.Select(frameDelay =>
                {
                    using (var t2d = rec.GetPngTexture())
                    {
                        byte[] frameData = new byte[t2d.Width * t2d.Height * 4];
                        t2d.GetData(frameData);
                        return(Tuple.Create(frameData, frameDelay));
                    }
                });
                var step3 = getFrameData.TakeWhile(_ => !cancellationToken.IsCancellationRequested);

                if (isCompareAndMergeFrames)
                {
                    var mergedFrameData = MergeFrames(step3);
                    step3 = mergedFrameData.TakeWhile(_ => !cancellationToken.IsCancellationRequested);
                }

                var step4 = step3.Select(item => ApplyFrame(item.Item1, item.Item2));

                // run pipeline
                bool isPlaying = this.IsPlaying;

                try
                {
                    this.IsPlaying = false;
                    rec.Begin(bounds, targetSize);
                    if (startTime > 0)
                    {
                        rec.Update(TimeSpan.FromMilliseconds(startTime));
                    }
                    context.ProgressMin = 0;
                    context.ProgressMax = length;
                    foreach (var task in step4)
                    {
                        int currentTime = await task;
                        context.Progress = currentTime;
                    }
                }
                catch (Exception ex)
                {
                    context.Message = $"Error: {ex.Message}";
                    throw;
                }
                finally
                {
                    rec.End();
                    enc.Dispose();
                    this.IsPlaying = isPlaying;
                }
            }

            var dialogResult = ProgressDialog.Show(this.FindForm(), "Exporting...", "Save animation file...", true, false, RenderJob);

            return(dialogResult == DialogResult.OK);
        }