Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

フレームの範囲選択 #888

Merged
merged 3 commits into from
Jan 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
"request": "launch",
"preLaunchTask": "build",
// If you have changed target frameworks, make sure to update the program path.
"program": "${workspaceFolder}/src/Beutl/bin/Debug/net7.0/Beutl.dll",
"program": "${workspaceFolder}/src/Beutl/bin/Debug/net8.0/Beutl.dll",
"args": [],
"cwd": "${workspaceFolder}/src/Beutl",
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
Expand Down
18 changes: 18 additions & 0 deletions src/Beutl.Language/Strings.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/Beutl.Language/Strings.ja.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1006,4 +1006,10 @@ b-editorがダウンロードURLを管理します。</value>
<data name="ColorKey" xml:space="preserve">
<value>カラーキー</value>
</data>
<data name="SaveAsImage" xml:space="preserve">
<value>画像として保存</value>
</data>
<data name="CopyAsImage" xml:space="preserve">
<value>画像としてコピー</value>
</data>
</root>
6 changes: 6 additions & 0 deletions src/Beutl.Language/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -1006,4 +1006,10 @@ and b-editor maintains the download URL.</value>
<data name="ColorKey" xml:space="preserve">
<value>Color key</value>
</data>
<data name="SaveAsImage" xml:space="preserve">
<value>Save as image</value>
</data>
<data name="CopyAsImage" xml:space="preserve">
<value>Copy as image</value>
</data>
</root>
152 changes: 152 additions & 0 deletions src/Beutl/Helpers/WindowsClipboard.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
using System.Runtime.InteropServices;

using Beutl.Graphics;
using Beutl.Media;
using Beutl.Media.Pixel;

namespace Beutl.Helpers;

public static class WindowsClipboard
{
private const string CopyImagePowerShellCode = """
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.Windows.Forms

$data = New-Object Windows.Forms.DataObject
$pngstream = New-Object System.IO.MemoryStream
$dibstream = [System.IO.File]::OpenRead("{0}")
$image = New-Object System.Drawing.Bitmap("{1}")
$image.Save($pngstream, [System.Drawing.Imaging.ImageFormat]::Png)

$data.SetImage($image)
$data.SetData("PNG", $False, $pngstream)
$data.SetData([Windows.Forms.DataFormats]::Dib, $dibstream)

[Windows.Forms.Clipboard]::SetDataObject($data, $True)

$image.Dispose()
$pngstream.Dispose()
$dibstream.Dispose()
""";

public static async Task CopyImage(Bitmap<Bgra8888> image)
{
// pngファイルを作成
string pngFile = Path.GetTempFileName();
pngFile = Path.ChangeExtension(pngFile, "png");
string dibFile = Path.GetTempFileName();
string ps1File = Path.ChangeExtension(Path.GetTempFileName(), "ps1");

try
{
image.Save(pngFile, EncodedImageFormat.Png);

// dibファイルを作成
await File.WriteAllBytesAsync(dibFile, ConvertToDib(image));

// ps1ファイルを作成
File.WriteAllText(ps1File, string.Format(CopyImagePowerShellCode, dibFile, pngFile));


var startInfo = new ProcessStartInfo()
{
FileName = "powershell.exe",
Arguments = $"-NoProfile -ExecutionPolicy ByPass -File \"{ps1File}\"",
UseShellExecute = false,
CreateNoWindow = true
};
Process proc = Process.Start(startInfo) ?? throw new Exception("Failed to launch 'powershell.exe'.");
await proc.WaitForExitAsync();
}
finally
{
TryDeleteFile(pngFile);
TryDeleteFile(dibFile);
TryDeleteFile(ps1File);
}
}

private static void TryDeleteFile(string file)
{
try
{
File.Delete(file);
}
catch
{
}
}

public static byte[] ConvertToDib(Bitmap<Bgra8888> image)
{
byte[] bm32bData;
int width = image.Width;
int height = image.Height;
// Ensure image is 32bppARGB by painting it on a new 32bppARGB image.
using Bitmap<Bgra8888> bm32b = image.Clone();
bm32b.Flip(FlipMode.XY);
bm32bData = MemoryMarshal.AsBytes(bm32b.DataSpan).ToArray();

// BITMAPINFOHEADER struct for DIB.
const int hdrSize = 0x28;
byte[] fullImageArr = new byte[hdrSize + 12 + bm32bData.Length];
Span<byte> fullImage = fullImageArr;
//Int32 biSize;
BitConverter.TryWriteBytes(fullImage, (uint)hdrSize);
fullImage = fullImage.Slice(4);

//Int32 biWidth;
BitConverter.TryWriteBytes(fullImage, (uint)width);
fullImage = fullImage.Slice(4);

//Int32 biHeight;
BitConverter.TryWriteBytes(fullImage, (uint)height);
fullImage = fullImage.Slice(4);

//Int16 biPlanes;
BitConverter.TryWriteBytes(fullImage, (ushort)1);
fullImage = fullImage.Slice(2);

//Int16 biBitCount;
BitConverter.TryWriteBytes(fullImage, (ushort)32);
fullImage = fullImage.Slice(2);

//BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
BitConverter.TryWriteBytes(fullImage, (uint)3);
fullImage = fullImage.Slice(4);

//Int32 biSizeImage;
BitConverter.TryWriteBytes(fullImage, (uint)bm32bData.Length);
fullImage = fullImage.Slice(4);

// These are all 0. Since .net clears new arrays, don't bother writing them.
//Int32 biXPelsPerMeter = 0;
//Int32 biYPelsPerMeter = 0;
//Int32 biClrUsed = 0;
//Int32 biClrImportant = 0;
fullImage = fullImageArr;

// The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
fullImage = fullImage.Slice(hdrSize);
BitConverter.TryWriteBytes(fullImage, 0x00FF0000);
fullImage = fullImage.Slice(4);
BitConverter.TryWriteBytes(fullImage, 0x0000FF00);
fullImage = fullImage.Slice(4);
BitConverter.TryWriteBytes(fullImage, 0x000000FF);

Array.Copy(bm32bData, 0, fullImageArr, hdrSize + 12, bm32bData.Length);
return fullImageArr;
}

public static void WriteIntToByteArray(byte[] data, int startIndex, int bytes, bool littleEndian, uint value)
{
int lastByte = bytes - 1;
if (data.Length < startIndex + bytes)
throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
for (int index = 0; index < bytes; index++)
{
int offs = startIndex + (littleEndian ? index : lastByte - index);
data[offs] = (byte)(value >> (8 * index) & 0xFF);
}
}
}
15 changes: 15 additions & 0 deletions src/Beutl/ViewModels/PlayerViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,17 @@ public PlayerViewModel(EditViewModel editViewModel)

public ReactivePropertySlim<bool> IsHandMode { get; } = new(false);

public ReactivePropertySlim<bool> IsCropMode { get; } = new(false);

public ReactivePropertySlim<Matrix> FrameMatrix { get; } = new(Matrix.Identity);

public event EventHandler? PreviewInvalidated;

// View側から設定
public Size MaxFrameSize { get; set; }

public Rect LastSelectedRect { get; set; }

public async void Play()
{
if (!_isEnabled.Value || Scene == null)
Expand Down Expand Up @@ -541,6 +545,17 @@ public void Dispose()
Scene = null!;
}

public async Task<Rect> StartSelectRect()
{
TcsForCrop = new TaskCompletionSource<Rect>();
IsCropMode.Value = true;
Rect r = await TcsForCrop.Task;
TcsForCrop = null;
return r;
}

public TaskCompletionSource<Rect>? TcsForCrop { get; private set; }

public Task<Bitmap<Bgra8888>> DrawSelectedDrawable(Drawable drawable)
{
Pause();
Expand Down
1 change: 1 addition & 0 deletions src/Beutl/Views/Cursors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public static class Cursors
public static readonly Cursor DragMove = new(StandardCursorType.DragMove);
public static readonly Cursor DragCopy = new(StandardCursorType.DragCopy);
public static readonly Cursor DragLink = new(StandardCursorType.DragLink);
public static readonly Cursor Cross = new(StandardCursorType.Cross);
public static readonly Cursor Hand;
public static readonly Cursor HandGrab;

Expand Down
5 changes: 4 additions & 1 deletion src/Beutl/Views/EditView.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -122,13 +122,16 @@
<Setter Property="Padding" Value="7,6,5,6" />
</Style>
</StackPanel.Styles>
<!-- Todo: ローカライズ -->
<RadioButton IsChecked="{Binding Player.IsMoveMode.Value}" ToolTip.Tip="{x:Static lang:Strings.Move}">
<icons:SymbolIcon FontSize="16" Symbol="Cursor" />
</RadioButton>
<RadioButton IsChecked="{Binding Player.IsHandMode.Value}" ToolTip.Tip="{x:Static lang:Strings.Hand}">
<icons:SymbolIcon FontSize="16" Symbol="HandLeft" />
</RadioButton>
<!-- Todo: ローカライズ -->
<RadioButton IsChecked="{Binding Player.IsCropMode.Value}" ToolTip.Tip="範囲選択">
<icons:SymbolIcon FontSize="16" Symbol="Crop" />
</RadioButton>
</StackPanel>
</Player.InnerLeftContent>
</Player>
Expand Down
Loading
Loading