/// <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); } }
/// <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)); } }); }