mirror of
https://github.com/microsoft/PowerToys
synced 2025-08-23 10:37:43 +00:00
212 lines
7.5 KiB
C#
212 lines
7.5 KiB
C#
|
// Copyright (c) Brice Lambson
|
|||
|
// The Brice Lambson licenses this file to you under the MIT license.
|
|||
|
// See the LICENSE file in the project root for more information. Code forked from Brice Lambson's https://github.com/bricelam/ImageResizer/
|
|||
|
|
|||
|
using System;
|
|||
|
using System.IO;
|
|||
|
using System.Linq;
|
|||
|
using System.Windows;
|
|||
|
using System.Windows.Media;
|
|||
|
using System.Windows.Media.Imaging;
|
|||
|
using ImageResizer.Properties;
|
|||
|
using ImageResizer.Utilities;
|
|||
|
using Microsoft.VisualBasic.FileIO;
|
|||
|
|
|||
|
namespace ImageResizer.Models
|
|||
|
{
|
|||
|
internal class ResizeOperation
|
|||
|
{
|
|||
|
private readonly string _file;
|
|||
|
private readonly string _destinationDirectory;
|
|||
|
private readonly Settings _settings;
|
|||
|
|
|||
|
public ResizeOperation(string file, string destinationDirectory, Settings settings)
|
|||
|
{
|
|||
|
_file = file;
|
|||
|
_destinationDirectory = destinationDirectory;
|
|||
|
_settings = settings;
|
|||
|
}
|
|||
|
|
|||
|
public void Execute()
|
|||
|
{
|
|||
|
string path;
|
|||
|
using (var inputStream = File.OpenRead(_file))
|
|||
|
{
|
|||
|
var decoder = BitmapDecoder.Create(
|
|||
|
inputStream,
|
|||
|
BitmapCreateOptions.PreservePixelFormat,
|
|||
|
BitmapCacheOption.None);
|
|||
|
|
|||
|
var encoder = BitmapEncoder.Create(decoder.CodecInfo.ContainerFormat);
|
|||
|
if (!encoder.CanEncode())
|
|||
|
{
|
|||
|
encoder = BitmapEncoder.Create(_settings.FallbackEncoder);
|
|||
|
}
|
|||
|
|
|||
|
ConfigureEncoder(encoder);
|
|||
|
|
|||
|
if (decoder.Metadata != null)
|
|||
|
{
|
|||
|
try
|
|||
|
{
|
|||
|
encoder.Metadata = decoder.Metadata;
|
|||
|
}
|
|||
|
catch (InvalidOperationException)
|
|||
|
{
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (decoder.Palette != null)
|
|||
|
{
|
|||
|
encoder.Palette = decoder.Palette;
|
|||
|
}
|
|||
|
|
|||
|
foreach (var originalFrame in decoder.Frames)
|
|||
|
{
|
|||
|
encoder.Frames.Add(
|
|||
|
BitmapFrame.Create(
|
|||
|
Transform(originalFrame),
|
|||
|
thumbnail: null,
|
|||
|
(BitmapMetadata)originalFrame.Metadata, // TODO: Add an option to strip any metadata that doesn't affect rendering (issue #3)
|
|||
|
colorContexts: null));
|
|||
|
}
|
|||
|
|
|||
|
path = GetDestinationPath(encoder);
|
|||
|
Directory.CreateDirectory(Path.GetDirectoryName(path));
|
|||
|
using (var outputStream = File.Open(path, FileMode.CreateNew, FileAccess.Write))
|
|||
|
{
|
|||
|
encoder.Save(outputStream);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (_settings.KeepDateModified)
|
|||
|
{
|
|||
|
File.SetLastWriteTimeUtc(path, File.GetLastWriteTimeUtc(_file));
|
|||
|
}
|
|||
|
|
|||
|
if (_settings.Replace)
|
|||
|
{
|
|||
|
var backup = GetBackupPath();
|
|||
|
File.Replace(path, _file, backup, ignoreMetadataErrors: true);
|
|||
|
FileSystem.DeleteFile(backup, UIOption.OnlyErrorDialogs, RecycleOption.SendToRecycleBin);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private void ConfigureEncoder(BitmapEncoder encoder)
|
|||
|
{
|
|||
|
switch (encoder)
|
|||
|
{
|
|||
|
case JpegBitmapEncoder jpegEncoder:
|
|||
|
jpegEncoder.QualityLevel = MathHelpers.Clamp(_settings.JpegQualityLevel, 1, 100);
|
|||
|
break;
|
|||
|
|
|||
|
case PngBitmapEncoder pngBitmapEncoder:
|
|||
|
pngBitmapEncoder.Interlace = _settings.PngInterlaceOption;
|
|||
|
break;
|
|||
|
|
|||
|
case TiffBitmapEncoder tiffEncoder:
|
|||
|
tiffEncoder.Compression = _settings.TiffCompressOption;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private BitmapSource Transform(BitmapSource source)
|
|||
|
{
|
|||
|
var originalWidth = source.PixelWidth;
|
|||
|
var originalHeight = source.PixelHeight;
|
|||
|
var width = _settings.SelectedSize.GetPixelWidth(originalWidth, source.DpiX);
|
|||
|
var height = _settings.SelectedSize.GetPixelHeight(originalHeight, source.DpiY);
|
|||
|
|
|||
|
if (_settings.IgnoreOrientation
|
|||
|
&& !_settings.SelectedSize.HasAuto
|
|||
|
&& _settings.SelectedSize.Unit != ResizeUnit.Percent
|
|||
|
&& originalWidth < originalHeight != (width < height))
|
|||
|
{
|
|||
|
var temp = width;
|
|||
|
width = height;
|
|||
|
height = temp;
|
|||
|
}
|
|||
|
|
|||
|
var scaleX = width / originalWidth;
|
|||
|
var scaleY = height / originalHeight;
|
|||
|
|
|||
|
if (_settings.SelectedSize.Fit == ResizeFit.Fit)
|
|||
|
{
|
|||
|
scaleX = Math.Min(scaleX, scaleY);
|
|||
|
scaleY = scaleX;
|
|||
|
}
|
|||
|
else if (_settings.SelectedSize.Fit == ResizeFit.Fill)
|
|||
|
{
|
|||
|
scaleX = Math.Max(scaleX, scaleY);
|
|||
|
scaleY = scaleX;
|
|||
|
}
|
|||
|
|
|||
|
if (_settings.ShrinkOnly
|
|||
|
&& _settings.SelectedSize.Unit != ResizeUnit.Percent
|
|||
|
&& (scaleX >= 1 || scaleY >= 1))
|
|||
|
{
|
|||
|
return source;
|
|||
|
}
|
|||
|
|
|||
|
var scaledBitmap = new TransformedBitmap(source, new ScaleTransform(scaleX, scaleY));
|
|||
|
if (_settings.SelectedSize.Fit == ResizeFit.Fill
|
|||
|
&& (scaledBitmap.PixelWidth > width
|
|||
|
|| scaledBitmap.PixelHeight > height))
|
|||
|
{
|
|||
|
var x = (int)(((originalWidth * scaleX) - width) / 2);
|
|||
|
var y = (int)(((originalHeight * scaleY) - height) / 2);
|
|||
|
|
|||
|
return new CroppedBitmap(scaledBitmap, new Int32Rect(x, y, (int)width, (int)height));
|
|||
|
}
|
|||
|
|
|||
|
return scaledBitmap;
|
|||
|
}
|
|||
|
|
|||
|
private string GetDestinationPath(BitmapEncoder encoder)
|
|||
|
{
|
|||
|
var directory = _destinationDirectory ?? Path.GetDirectoryName(_file);
|
|||
|
var originalFileName = Path.GetFileNameWithoutExtension(_file);
|
|||
|
|
|||
|
var supportedExtensions = encoder.CodecInfo.FileExtensions.Split(',');
|
|||
|
var extension = Path.GetExtension(_file);
|
|||
|
if (!supportedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
|
|||
|
{
|
|||
|
extension = supportedExtensions.FirstOrDefault();
|
|||
|
}
|
|||
|
|
|||
|
var fileName = string.Format(
|
|||
|
_settings.FileNameFormat,
|
|||
|
originalFileName,
|
|||
|
_settings.SelectedSize.Name,
|
|||
|
_settings.SelectedSize.Width,
|
|||
|
_settings.SelectedSize.Height,
|
|||
|
encoder.Frames[0].PixelWidth,
|
|||
|
encoder.Frames[0].PixelHeight);
|
|||
|
var path = Path.Combine(directory, fileName + extension);
|
|||
|
var uniquifier = 1;
|
|||
|
while (File.Exists(path))
|
|||
|
{
|
|||
|
path = Path.Combine(directory, fileName + " (" + uniquifier++ + ")" + extension);
|
|||
|
}
|
|||
|
|
|||
|
return path;
|
|||
|
}
|
|||
|
|
|||
|
private string GetBackupPath()
|
|||
|
{
|
|||
|
var directory = Path.GetDirectoryName(_file);
|
|||
|
var fileName = Path.GetFileNameWithoutExtension(_file);
|
|||
|
var extension = Path.GetExtension(_file);
|
|||
|
|
|||
|
var path = Path.Combine(directory, fileName + ".bak" + extension);
|
|||
|
var uniquifier = 1;
|
|||
|
while (File.Exists(path))
|
|||
|
{
|
|||
|
path = Path.Combine(directory, fileName + " (" + uniquifier++ + ")" + ".bak" + extension);
|
|||
|
}
|
|||
|
|
|||
|
return path;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|