/// <inheritdoc />
        /// <summary>
        ///   Sets this action's bitmap data.
        /// </summary>
        /// <param name="orgData">An instance of <see cref="T:Captain.Common.BitmapData" /> containing capture information.</param>
        public void SetBitmapData(BitmapData orgData)
        {
            using (var bmp = new Bitmap(orgData.Width, orgData.Height, PixelFormat.Format32bppRgb)) {
                // lock target bitmap
                System.Drawing.Imaging.BitmapData data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height),
                                                                      ImageLockMode.WriteOnly,
                                                                      bmp.PixelFormat);

                // copy data and unlock target bitmap
                Utilities.CopyMemory(data.Scan0, orgData.Scan0, orgData.Height * orgData.Stride);
                bmp.UnlockBits(data);

                // copy to clipboard
                Clipboard.SetImage(bmp);
                SetStatus(ActionStatus.Success);
            }
        }
Beispiel #2
0
        /// <summary>
        ///   Captures an still image, encodes it and executes each bound action.
        /// </summary>
        /// <param name="task">Task to be run.</param>
        /// <param name="rect">Rectangle to be captured.</param>
        /// <returns>An enumeration of the <see cref="Common.Action" />s running.</returns>
        private static void StartScreenshotTask(Task task, Rectangle rect)
        {
            IBitmapVideoProvider provider = null;
            BitmapData           bmpData  = default;
            Bitmap thumbnail;

            try {
                // create video provider
                provider = VideoProviderFactory.Create(rect);

                // acquire frame and lock frame bits for reading so that codecs can work over it
                provider.AcquireFrame();
                bmpData = provider.LockFrameBitmap();

                // generate thumbnail
                thumbnail = new Bitmap(bmpData.Width, bmpData.Height);
                // TODO: debug this
                System.Drawing.Imaging.BitmapData data = thumbnail.LockBits(new Rectangle(Point.Empty, thumbnail.Size),
                                                                            ImageLockMode.WriteOnly,
                                                                            PixelFormat.Format32bppArgb);
                Utilities.CopyMemory(data.Scan0, bmpData.Scan0, bmpData.Height * bmpData.Stride);
                thumbnail.UnlockBits(data);

                using (var tempBmp = new Bitmap(4 * (Resources.NeutralResultOverlay.Width - 48),
                                                4 * (Resources.NeutralResultOverlay.Height - 48))) {
                    using (Graphics graphics = Graphics.FromImage(tempBmp)) {
                        graphics.DrawImage(thumbnail,
                                           new Rectangle(Point.Empty, tempBmp.Size),
                                           new Rectangle(Point.Empty, thumbnail.Size),
                                           GraphicsUnit.Pixel);
                    }

                    thumbnail.Dispose();
                    thumbnail = tempBmp.Clone() as Bitmap;
                }
            } catch (Exception exception) {
                Log.WriteLine(LogLevel.Error, $"video provider exception: ${exception}");
                if (bmpData.Scan0 == default)
                {
                    provider?.UnlockFrameBitmap(bmpData);
                }

                provider?.ReleaseFrame();
                provider?.Dispose();
                throw new TaskException(Resources.TaskHelper_CaptureFailedCaption,
                                        Resources.TaskHelper_CaptureFailedContent,
                                        exception);
            }

            // initialize codec
            IStillImageCodec codec;

            try {
                // get uninitialized object in case we need to set Options property first
                codec = Activator.CreateInstance(Type.GetType(task.Codec.CodecType, true, true) ??
                                                 throw new InvalidOperationException("No such codec loaded.")) as
                        IStillImageCodec;

                if (task.Codec.Options is Dictionary <string, object> userOptions &&
                    codec is IHasOptions configurableObject)
                {
                    // set user options
                    configurableObject.Options = configurableObject.Options ?? new Dictionary <string, object>();

                    foreach (KeyValuePair <string, object> pair in userOptions)
                    {
                        configurableObject.Options[pair.Key] = pair.Value;
                    }
                }
            } catch (Exception exception) {
                Log.WriteLine(LogLevel.Error, $"error initializing codec {task.Codec.CodecType}: {exception}");
                throw new TaskException(Resources.TaskHelper_EncodingFailedCaption,
                                        Resources.TaskHelper_EncodingInitializationFailedContent,
                                        exception);
            }

            // create temporary memory stream for holding the encoded data
            var stream = new MemoryStream();

            try {
                // encode the still image
                codec?.Encode(bmpData, stream);
            } catch (Exception exception) {
                Log.WriteLine(LogLevel.Error, $"error encoding still image: {exception}");

                stream.Dispose();
                provider.UnlockFrameBitmap(bmpData);
                provider.ReleaseFrame();
                provider.Dispose();
                thumbnail?.Dispose();

                throw new TaskException(Resources.TaskHelper_EncodingFailedCaption,
                                        Resources.TaskHelper_EncodingFailedContent,
                                        exception);
            }

            List <Action> actions = task.Actions.Select(a => {
                try {
                    // set options if needed
                    if (a.Options != null &&
                        Application.PluginManager.Actions.First(a2 => a2.Type.Name == a.ActionType).Configurable)
                    {
                        // create uninitialized instance
                        var action = FormatterServices.GetSafeUninitializedObject(
                            Type.GetType(a.ActionType, true, true) ??
                            throw new InvalidOperationException(Resources.TaskHelper_NoSuchActionMessage)) as Action;

                        // set options property
                        a.GetType().GetProperty("Options")?.SetValue(action, a.Options);

                        // call parameterless constructor
                        action?.GetType()
                        .GetConstructor(
                            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic,
                            null,
                            Type.EmptyTypes,
                            null)
                        ?.Invoke(action, new object[] { codec });
                    }

                    return(Activator.CreateInstance(Type.GetType(a.ActionType) ??
                                                    throw new InvalidOperationException(
                                                        Resources.TaskHelper_NoSuchActionMessage),
                                                    codec) as Action);
                } catch (Exception exception) {
                    Log.WriteLine(LogLevel.Warning, $"error initializing action {a.ActionType}: {exception}");

                    // create dummy action for displaying error
                    var action = new Action(codec);
                    action.SetStatus(ActionStatus.Failed,
                                     new Exception(Resources.TaskHelper_ActionInitializationFailedCaption, exception));
                    return(action);
                }
            })
                                    .Where(a => a != null)
                                    .ToList();

            actions.ForEach(a => {
                Application.ActionManager.AddAction(a);

                void Release()
                {
                    if (a == actions.Last())
                    {
                        // this was the last action, release the temporary stream. It's not disposed unless the last stream
                        // fails to initialize
                        // ReSharper disable once AccessToDisposedClosure
                        Log.WriteLine(LogLevel.Debug, "releasing resources");
                        stream.Dispose();
                        provider.UnlockFrameBitmap(bmpData);
                        provider.ReleaseFrame();
                        provider.Dispose();
                        GC.Collect();
                    }
                }

                // set preview bitmap
                a.Thumbnail = thumbnail;

                try {
                    if (a is IFiltered filteredAction)
                    {
                        if (!filteredAction.GetMediaAcceptance(task.TaskType, codec, task.Codec.Options as ICodecParameters))
                        {
                            Log.WriteLine(LogLevel.Warning, "media filter did not accept this capture");
                            throw new Exception(Resources.TaskHelper_UnsupportedMediaMessage);
                        }
                    }

                    if (a is IPreBitmapEncodingAction preEncodingAction)
                    {
                        // set bitmap data instead of copying to the stream
                        Log.WriteLine(LogLevel.Debug, $"setting bitmap data for {a.GetType().Name}");
                        preEncodingAction.SetBitmapData(bmpData);
                        Release();
                        a.SetStatus(ActionStatus.Success);
                        Log.WriteLine(LogLevel.Informational, $"action {a.GetType().Name} is done");
                    }
                    else
                    {
                        // copy data to the action stream
                        Log.WriteLine(LogLevel.Debug, $"writing {stream.Length} bytes to {a.GetType().Name}");
                        a.SetLength(stream.Length);

                        new Thread(() => {
                            using (var target = new BufferedStream(a, a.BufferSize)) { stream.WriteTo(target); }
                            a.Flush();
                            a.Dispose();
                            Release();
                            Log.WriteLine(LogLevel.Informational, $"action {a.GetType().Name} is ready");
                        }).Start();
                    }
                } catch (Exception exception) {
                    Release();
                    Log.WriteLine(LogLevel.Warning, $"error copying to action stream {a.GetType().Name}: {exception}");
                    a.SetStatus(ActionStatus.Failed, new Exception(Resources.TaskHelper_ActionFailedCaption, exception));
                }
            });
        }