Skip to content
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
5 changes: 2 additions & 3 deletions PhotoLocator/BitmapOperations/IIRSmoothOperation.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
using System.Diagnostics;
using System.Threading.Tasks;
using System.Runtime.Intrinsics;
using System.Runtime.Intrinsics.X86;
using System.Numerics;
using System.Runtime.Intrinsics;
using System.Threading.Tasks;

namespace PhotoLocator.BitmapOperations
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@

namespace PhotoLocator
{
public sealed class JpegTransformCommands
public sealed class ImageTransformCommands
{
public const string AstroCommandParameter = "Astro";

private readonly IMainViewModel _mainViewModel;

private bool HasFileSelected(object? o) => _mainViewModel.SelectedItem is not null && _mainViewModel.SelectedItem.IsFile;

public JpegTransformCommands(IMainViewModel mainViewModel)
public ImageTransformCommands(IMainViewModel mainViewModel)
{
_mainViewModel = mainViewModel;
}
Expand Down Expand Up @@ -200,5 +200,46 @@ await _mainViewModel.RunProcessWithProgressBarAsync((progressCallback, ct) => Ta
}
}, ct), "Batch process");
}

public ICommand ConvertFileFormatCommand => new RelayCommand(async o =>
{
var allSelected = _mainViewModel.GetSelectedItems(true).ToArray();
if (allSelected.Length == 0)
return;
var targetType = o as string ?? throw new ArgumentException("Invalid target type");

var browser = new System.Windows.Forms.FolderBrowserDialog();
browser.InitialDirectory = Path.GetDirectoryName(allSelected[0].FullPath)!;
browser.Description = $"Select target folder for converted {targetType} files";
browser.UseDescriptionForTitle = true;
if (browser.ShowDialog() != System.Windows.Forms.DialogResult.OK)
return;
var targetDir = browser.SelectedPath;
var targetIsSourceDir = string.Equals(targetDir, browser.InitialDirectory, StringComparison.OrdinalIgnoreCase);

await _mainViewModel.RunProcessWithProgressBarAsync(async (progressCallback, ct) =>
{
var overwriteAll = false;
int i = 0;
foreach (var item in allSelected)
{
var targetFileName = targetIsSourceDir ? Path.ChangeExtension(item.GetProcessedFileName(), targetType)
: Path.Combine(targetDir, Path.GetFileNameWithoutExtension(item.Name) + "." + targetType);
Comment thread
meesoft marked this conversation as resolved.
if (!overwriteAll && File.Exists(targetFileName))
{
if (MessageBox.Show(App.Current.MainWindow, $"File {targetFileName} already exists. Overwrite all conflicting files?",
"Confirm Overwrite All", MessageBoxButton.OKCancel, MessageBoxImage.Question) != MessageBoxResult.OK)
break;
overwriteAll = true;
}

var (image, itemMetadata) = await LoadImageWithMetadataAsync(item);
await Task.Run(() => GeneralFileFormatHandler.SaveToFile(image, targetFileName,
ExifHandler.ResetOrientation(itemMetadata), _mainViewModel.Settings.JpegQuality), ct);

progressCallback((double)(++i) / allSelected.Length);
}
}, "Convert to " + targetType);
});
Comment thread
meesoft marked this conversation as resolved.
}
}
4 changes: 2 additions & 2 deletions PhotoLocator/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ await ExifTool.SetTimestampAsync(item.FullPath, item.GetProcessedFileName(), tim
if (SelectedItem is not null && SelectedItem.IsVideo)
VideoTransformCommandsShared.CropSelected(cropRectangle);
else
await JpegTransformCommands.CropSelectedItemAsync(PreviewPictureSource, cropRectangle);
await ImageTransformCommands.CropSelectedItemAsync(PreviewPictureSource, cropRectangle);
}
else
{
Expand All @@ -1087,7 +1087,7 @@ await ExifTool.SetTimestampAsync(item.FullPath, item.GetProcessedFileName(), tim

public ICommand ToggleLogCommand => new RelayCommand(o => LogViewHeight = LogViewHeight > 4 ? 4 : 100);

public JpegTransformCommands JpegTransformCommands => field ??= new(this);
public ImageTransformCommands ImageTransformCommands => field ??= new(this);

public VideoTransformCommands VideoTransformCommands => new(this);

Expand Down
31 changes: 19 additions & 12 deletions PhotoLocator/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@
<KeyBinding Key="D4" Modifiers="Ctrl" Command="{Binding Zoom400Command}" />
<KeyBinding Key="E" Modifiers="Ctrl" Command="{Binding ExploreCommand}" />
<KeyBinding Key="F" Modifiers="Ctrl" Command="{Binding QuickSearchCommand}" />
<KeyBinding Key="L" Modifiers="Ctrl" Command="{Binding JpegTransformCommands.LocalContrastCommand}" />
<KeyBinding Key="L" Modifiers="Ctrl" Command="{Binding ImageTransformCommands.LocalContrastCommand}" />
<KeyBinding Key="O" Modifiers="Ctrl" Command="{Binding BrowseForPhotosCommand}" />
<KeyBinding Key="P" Modifiers="Ctrl" Command="{Binding VideoTransformCommandsShared.ProcessSelected}" />
<KeyBinding Key="Q" Modifiers="Ctrl" Command="{Binding ViewModeCommand}" />
<KeyBinding Key="R" Modifiers="Ctrl" Command="{Binding JpegTransformCommands.LocalContrastCommand}" CommandParameter="{x:Static local:JpegTransformCommands.AstroCommandParameter}"/>
<KeyBinding Key="R" Modifiers="Ctrl" Command="{Binding ImageTransformCommands.LocalContrastCommand}" CommandParameter="{x:Static local:ImageTransformCommands.AstroCommandParameter}"/>
<KeyBinding Key="S" Modifiers="Ctrl" Command="{Binding SaveGeotagsCommand}" />
<KeyBinding Key="T" Modifiers="Ctrl" Command="{Binding AutoGeotagCommand}" />
<KeyBinding Key="V" Modifiers="Ctrl" Command="{Binding PasteLocationCommand}" />
Expand Down Expand Up @@ -119,7 +119,7 @@
ToolTip="Rename (F2)" Width="32" />
<Button Content="&#xECC5;" FontFamily="Segoe MDL2 Assets" FontSize="18" Command="{Binding AdjustTimestampsCommand}"
ToolTip="Adjust timestamps" Width="32" />
<Button Content="&#xE790;" FontFamily="Segoe MDL2 Assets" FontSize="18" Command="{Binding JpegTransformCommands.LocalContrastCommand}"
<Button Content="&#xE790;" FontFamily="Segoe MDL2 Assets" FontSize="18" Command="{Binding ImageTransformCommands.LocalContrastCommand}"
ToolTip="Local contrast, brightness and colors (Ctrl+L)" Width="32" />
<Button Content="&#xE714;" FontFamily="Segoe MDL2 Assets" FontSize="18" Command="{Binding VideoTransformCommandsShared.ProcessSelected}"
ToolTip="Process video (Ctrl+P)" Width="32" />
Expand Down Expand Up @@ -201,7 +201,7 @@
<TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xE77F;" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Reset rotation tag" Command="{Binding JpegTransformCommands.Rotate0Command}" />
<MenuItem Header="Reset rotation tag" Command="{Binding ImageTransformCommands.Rotate0Command}" />
<MenuItem Header="Show metadata" Command="{Binding ShowMetadataCommand}" />
</MenuItem>
<Separator />
Expand All @@ -214,18 +214,18 @@
<MenuItem Header="Open Explorer (Ctrl+E)" Command="{Binding ExploreCommand}" />
<MenuItem Header="Shell context menu (Shift+right click)" Command="{Binding ShellContextMenuCommand}" />
<Separator />
<MenuItem Header="Local contrast, brightness and colors (Ctrl+L)" Command="{Binding JpegTransformCommands.LocalContrastCommand}">
<MenuItem Header="Local contrast, brightness and colors (Ctrl+L)" Command="{Binding ImageTransformCommands.LocalContrastCommand}">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xE790;" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Astrophoto stretch (Ctrl+R)" Command="{Binding JpegTransformCommands.LocalContrastCommand}" CommandParameter="{x:Static local:JpegTransformCommands.AstroCommandParameter}">
<MenuItem Header="Astrophoto stretch (Ctrl+R)" Command="{Binding ImageTransformCommands.LocalContrastCommand}" CommandParameter="{x:Static local:ImageTransformCommands.AstroCommandParameter}">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xE734;" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Transform jpeg" >
<MenuItem Header="Rotate left" Command="{Binding JpegTransformCommands.RotateLeftCommand}">
<MenuItem Header="Rotate left" Command="{Binding ImageTransformCommands.RotateLeftCommand}">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xE7AD;" FontSize="14" >
<TextBlock.RenderTransform>
Expand All @@ -234,12 +234,12 @@
</TextBlock>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Rotate right" Command="{Binding JpegTransformCommands.RotateRightCommand}">
<MenuItem Header="Rotate right" Command="{Binding ImageTransformCommands.RotateRightCommand}">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xE7AD;" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Rotate 180°" Command="{Binding JpegTransformCommands.Rotate180Command}" />
<MenuItem Header="Rotate 180°" Command="{Binding ImageTransformCommands.Rotate180Command}" />
<MenuItem Header="Crop (Alt-C)" Command="{Binding CropCommand}" IsChecked="{Binding IsCropControlVisible}">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xE7A8;" FontSize="14" />
Expand All @@ -262,6 +262,13 @@
<MenuItem Header="Generate max frame" Command="{Binding VideoTransformCommands.GenerateMaxFrame}" />
<MenuItem Header="Generate time slice video" Command="{Binding VideoTransformCommands.GenerateTimeSliceVideo}" />
</MenuItem>
<MenuItem Header="Convert to" >
<MenuItem Header="JPEG (jpegli)" Command="{Binding ImageTransformCommands.ConvertFileFormatCommand}" CommandParameter="jpg"/>
<MenuItem Header="TIFF (LZW)" Command="{Binding ImageTransformCommands.ConvertFileFormatCommand}" CommandParameter="tif"/>
<MenuItem Header="PNG" Command="{Binding ImageTransformCommands.ConvertFileFormatCommand}" CommandParameter="png"/>
<MenuItem Header="JPEG XR (lossless)" Command="{Binding ImageTransformCommands.ConvertFileFormatCommand}" CommandParameter="jxr"/>
<MenuItem Header="BMP" Command="{Binding ImageTransformCommands.ConvertFileFormatCommand}" CommandParameter="bmp"/>
</MenuItem>
<Separator />
<MenuItem Header="Rename (F2)" Command="{Binding RenameCommand}">
<MenuItem.Icon>
Expand Down Expand Up @@ -346,7 +353,7 @@
<MenuItem Header="400% (Ctrl+4)" IsCheckable="True" Name="Zoom400Item" Command="{Binding Zoom400Command}" />
<Separator />
<MenuItem Header="Transform jpeg" >
<MenuItem Header="Rotate left" Command="{Binding JpegTransformCommands.RotateLeftCommand}">
<MenuItem Header="Rotate left" Command="{Binding ImageTransformCommands.RotateLeftCommand}">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xE7AD;" FontSize="14" >
<TextBlock.RenderTransform>
Expand All @@ -355,12 +362,12 @@
</TextBlock>
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Rotate right" Command="{Binding JpegTransformCommands.RotateRightCommand}">
<MenuItem Header="Rotate right" Command="{Binding ImageTransformCommands.RotateRightCommand}">
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xE7AD;" FontSize="14" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Rotate 180°" Command="{Binding JpegTransformCommands.Rotate180Command}" />
<MenuItem Header="Rotate 180°" Command="{Binding ImageTransformCommands.Rotate180Command}" />
<MenuItem Header="Crop (Alt-C)" Command="{Binding CropCommand}" IsChecked="{Binding IsCropControlVisible}" >
<MenuItem.Icon>
<TextBlock FontFamily="Segoe MDL2 Assets" Text="&#xE7A8;" FontSize="14" />
Expand Down
2 changes: 1 addition & 1 deletion PhotoLocator/Metadata/ExifHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ void TransferValue(string query1, string query2, string? query3 = null)
if (source.TryGetFormat() == "wmphoto")
return source;
result = new BitmapMetadata("wmphoto");
setValue = 1;
setValue = 2;

}
else if (encoder is TiffBitmapEncoder)
Expand Down
12 changes: 6 additions & 6 deletions PhotoLocator/PictureFileFormats/GeneralFileFormatHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ public static BitmapSource LoadFromStream(Stream source, Rotation rotation, int

public static void SaveToFile(BitmapSource image, string targetPath, BitmapMetadata? metadata = null, int jpegQuality = 95)
{
var ext = Path.GetExtension(targetPath).ToUpperInvariant();
var ext = Path.GetExtension(targetPath).ToLowerInvariant();
BitmapEncoder encoder;
if (ext is ".JPG" or ".JPEG")
if (ext is ".jpg" or ".jpeg")
{
if (!_jpegliChecked)
{
Expand All @@ -67,13 +67,13 @@ public static void SaveToFile(BitmapSource image, string targetPath, BitmapMetad
}
encoder = new JpegBitmapEncoder() { QualityLevel = jpegQuality };
}
else if (ext is ".TIF" or ".TIFF")
else if (ext is ".tif" or ".tiff")
encoder = new TiffBitmapEncoder(); // Default is best compression
else if (ext is ".PNG")
else if (ext is ".png")
encoder = new PngBitmapEncoder();
else if (ext is ".BMP")
else if (ext is ".bmp")
encoder = new BmpBitmapEncoder();
else if (ext is ".JXR")
else if (ext is ".jxr")
encoder = new WmpBitmapEncoder() { Lossless = true };
else
throw new UserMessageException("Unsupported file format " + ext);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static bool CanLoad(string extension)
return extension == ".psd";
}

public static BitmapSource LoadFromStream(Stream stream, Rotation rotation, int maxWidth, bool preservePixelFormat, CancellationToken ct)
public static BitmapSource LoadFromStream(Stream stream, CancellationToken ct)
{
var psd = new PsdFile(stream, new LoadContext());
foreach (var psdLayer in (new[] { psd.BaseLayer }).Concat(psd.Layers))
Expand Down
2 changes: 1 addition & 1 deletion PhotoLocator/PictureItemViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ private BitmapSource LoadPreviewInternal(int maxPixelWidth, bool preservePixelFo
if (CR3FileFormatHandler.CanLoad(ext))
return CR3FileFormatHandler.LoadFromStream(fileStream, Orientation, maxPixelWidth, preservePixelFormat, ct);
if (PhotoshopFileFormatHandler.CanLoad(ext))
return PhotoshopFileFormatHandler.LoadFromStream(fileStream, Orientation, maxPixelWidth, preservePixelFormat, ct);
return PhotoshopFileFormatHandler.LoadFromStream(fileStream, ct);
}
catch (OperationCanceledException)
{
Expand Down
6 changes: 3 additions & 3 deletions PhotoLocatorTest/Metadata/ExifHandlerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ public void SetMetadata_ShouldSetJpegMetadataOnJpegXr()
{
const string TestFileName = "fromJpeg.jxr";

using var stream = GetType().Assembly.GetManifestResourceStream(@"PhotoLocator.TestData.2022-06-17_19.03.02.jpg")
using var sourceStream = GetType().Assembly.GetManifestResourceStream(@"PhotoLocator.TestData.2022-06-17_19.03.02.jpg")
?? throw new FileNotFoundException("Resource not found");
var source = ExifHandler.LoadMetadata(stream) ?? throw new Exception("Unable to load metadata");
var source = ExifHandler.LoadMetadata(sourceStream) ?? throw new Exception("Unable to load metadata");
Assert.IsFalse(string.IsNullOrEmpty(source.CameraModel));

var bitmap = BitmapSource.Create(2, 2, 96, 96, PixelFormats.Gray8, null, new byte[4], 2);
Expand All @@ -72,7 +72,7 @@ public void SetMetadata_ShouldSetJpegMetadataOnJpegXr()
using var targetStream = File.OpenRead(TestFileName);
var target = ExifHandler.LoadMetadata(targetStream)!;
Assert.AreEqual(source.CameraModel, target.CameraModel);
Assert.AreEqual("FC7303, " + JpegTestDataTimestamp, ExifHandler.GetMetadataString(target, targetStream));
Assert.AreEqual(ExifHandler.GetMetadataString(source, sourceStream), ExifHandler.GetMetadataString(target, targetStream));
//Assert.AreEqual(ExifHandler.GetGeotag(source), ExifHandler.GetGeotag(target));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,31 @@ public class PhotoshopFileFormatHandlerTest
public void LoadFromStream_ShouldLoadG8()
{
using var stream = File.OpenRead(@"TestData\G8.psd");
var image = PhotoshopFileFormatHandler.LoadFromStream(stream, Rotation.Rotate0, 100, false, default);
var image = PhotoshopFileFormatHandler.LoadFromStream(stream, default);
_ = new FloatBitmap(image, 1);
}

[TestMethod]
public void LoadFromStream_ShouldLoadG16()
{
using var stream = File.OpenRead(@"TestData\G16.psd");
var image = PhotoshopFileFormatHandler.LoadFromStream(stream, Rotation.Rotate0, 100, false, default);
var image = PhotoshopFileFormatHandler.LoadFromStream(stream, default);
_ = new FloatBitmap(image, 1);
}

[TestMethod]
public void LoadFromStream_ShouldLoadRGB8()
{
using var stream = File.OpenRead(@"TestData\RGB8.psd");
var image = PhotoshopFileFormatHandler.LoadFromStream(stream, Rotation.Rotate0, 100, false, default);
var image = PhotoshopFileFormatHandler.LoadFromStream(stream, default);
_ = new FloatBitmap(image, 1);
}

[TestMethod]
public void LoadFromStream_ShouldLoadRGB16()
{
using var stream = File.OpenRead(@"TestData\RGB16.psd");
var image = PhotoshopFileFormatHandler.LoadFromStream(stream, Rotation.Rotate0, 100, false, default);
var image = PhotoshopFileFormatHandler.LoadFromStream(stream, default);
_ = new FloatBitmap(image, 1);
}
}
Expand Down
Loading