mirror of
https://github.com/cyanfish/naps2.git
synced 2024-11-11 02:45:19 +03:00
Rename IMemoryStorage to IImage
This commit is contained in:
parent
d509d39de2
commit
676d2ae9da
@ -41,7 +41,7 @@ namespace NAPS2.ImportExport
|
||||
try
|
||||
{
|
||||
ScannedImage img;
|
||||
using (var storage = StorageManager.ConvertToMemory(new FileStorage(Path.Combine(data.RecoveryFolder, ir.FileName)), new StorageConvertParams()))
|
||||
using (var storage = StorageManager.ConvertToImage(new FileStorage(Path.Combine(data.RecoveryFolder, ir.FileName)), new StorageConvertParams()))
|
||||
{
|
||||
img = new ScannedImage(storage, ir.BitDepth, ir.HighQuality, -1);
|
||||
}
|
||||
|
@ -36,11 +36,11 @@ namespace NAPS2.ImportExport.Images
|
||||
return;
|
||||
}
|
||||
|
||||
IEnumerable<IMemoryStorage> toImport;
|
||||
IEnumerable<IImage> toImport;
|
||||
int frameCount;
|
||||
try
|
||||
{
|
||||
toImport = StorageManager.MemoryStorageFactory.DecodeMultiple(filePath, out frameCount);
|
||||
toImport = StorageManager.ImageFactory.DecodeMultiple(filePath, out frameCount);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -175,14 +175,14 @@ namespace NAPS2.ImportExport.Images
|
||||
var encoderParams = new EncoderParameters(1);
|
||||
encoderParams.Param[0] = new EncoderParameter(Encoder.Quality, quality);
|
||||
// TODO: Something more generic
|
||||
using (Bitmap bitmap = ((GdiStorage) await scannedImageRenderer.Render(snapshot)).Bitmap)
|
||||
using (Bitmap bitmap = ((GdiImage) await scannedImageRenderer.Render(snapshot)).Bitmap)
|
||||
{
|
||||
bitmap.Save(path, encoder, encoderParams);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
using (Bitmap bitmap = ((GdiStorage)await scannedImageRenderer.Render(snapshot)).Bitmap)
|
||||
using (Bitmap bitmap = ((GdiImage)await scannedImageRenderer.Render(snapshot)).Bitmap)
|
||||
{
|
||||
bitmap.Save(path, format);
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ namespace NAPS2.ImportExport.Images
|
||||
var iparams = new EncoderParameters(1);
|
||||
Encoder iparam = Encoder.Compression;
|
||||
// TODO: More generic (?)
|
||||
using (var bitmap = ((GdiStorage)await scannedImageRenderer.Render(snapshots[0])).Bitmap)
|
||||
using (var bitmap = ((GdiImage)await scannedImageRenderer.Render(snapshots[0])).Bitmap)
|
||||
{
|
||||
ValidateBitmap(bitmap);
|
||||
var iparamPara = new EncoderParameter(iparam, (long)GetEncoderValue(compression, bitmap));
|
||||
@ -56,7 +56,7 @@ namespace NAPS2.ImportExport.Images
|
||||
var compressionEncoder = Encoder.Compression;
|
||||
|
||||
File.Delete(location);
|
||||
using (var bitmap0 = ((GdiStorage)await scannedImageRenderer.Render(snapshots[0])).Bitmap)
|
||||
using (var bitmap0 = ((GdiImage)await scannedImageRenderer.Render(snapshots[0])).Bitmap)
|
||||
{
|
||||
ValidateBitmap(bitmap0);
|
||||
encoderParams.Param[0] = new EncoderParameter(compressionEncoder, (long)GetEncoderValue(compression, bitmap0));
|
||||
@ -76,7 +76,7 @@ namespace NAPS2.ImportExport.Images
|
||||
return false;
|
||||
}
|
||||
|
||||
using (var bitmap = ((GdiStorage)await scannedImageRenderer.Render(snapshots[i])).Bitmap)
|
||||
using (var bitmap = ((GdiImage)await scannedImageRenderer.Render(snapshots[i])).Bitmap)
|
||||
{
|
||||
ValidateBitmap(bitmap);
|
||||
encoderParams.Param[0] = new EncoderParameter(compressionEncoder, (long)GetEncoderValue(compression, bitmap));
|
||||
|
@ -212,7 +212,7 @@ namespace NAPS2.ImportExport.Pdf
|
||||
// Fortunately JPEG has native support in PDF and exporting an image is just writing the stream to a file.
|
||||
using (var memoryStream = new MemoryStream(imageBytes))
|
||||
{
|
||||
using (var storage = StorageManager.MemoryStorageFactory.Decode(memoryStream, ".jpg"))
|
||||
using (var storage = StorageManager.ImageFactory.Decode(memoryStream, ".jpg"))
|
||||
{
|
||||
storage.SetResolution(storage.Width / (float)page.Width.Inch, storage.Height / (float)page.Height.Inch);
|
||||
var image = new ScannedImage(storage, ScanBitDepth.C24Bit, false, -1);
|
||||
@ -237,17 +237,17 @@ namespace NAPS2.ImportExport.Pdf
|
||||
|
||||
var buffer = imageObject.Stream.UnfilteredValue;
|
||||
|
||||
IMemoryStorage storage;
|
||||
IImage storage;
|
||||
ScanBitDepth bitDepth;
|
||||
switch (bitsPerComponent)
|
||||
{
|
||||
case 8:
|
||||
storage = StorageManager.MemoryStorageFactory.FromDimensions(width, height, StoragePixelFormat.RGB24);
|
||||
storage = StorageManager.ImageFactory.FromDimensions(width, height, StoragePixelFormat.RGB24);
|
||||
bitDepth = ScanBitDepth.C24Bit;
|
||||
RgbToBitmapUnmanaged(storage, buffer);
|
||||
break;
|
||||
case 1:
|
||||
storage = StorageManager.MemoryStorageFactory.FromDimensions(width, height, StoragePixelFormat.BW1);
|
||||
storage = StorageManager.ImageFactory.FromDimensions(width, height, StoragePixelFormat.BW1);
|
||||
bitDepth = ScanBitDepth.BlackWhite;
|
||||
BlackAndWhiteToBitmapUnmanaged(storage, buffer);
|
||||
break;
|
||||
@ -271,17 +271,17 @@ namespace NAPS2.ImportExport.Pdf
|
||||
}
|
||||
}
|
||||
|
||||
private static void RgbToBitmapUnmanaged(IMemoryStorage storage, byte[] rgbBuffer)
|
||||
private static void RgbToBitmapUnmanaged(IImage image, byte[] rgbBuffer)
|
||||
{
|
||||
var data = storage.Lock(out var scan0, out var stride);
|
||||
var data = image.Lock(out var scan0, out var stride);
|
||||
try
|
||||
{
|
||||
for (int y = 0; y < storage.Height; y++)
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < storage.Width; x++)
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
{
|
||||
IntPtr pixelData = scan0 + y * stride + x * 3;
|
||||
int bufferIndex = (y * storage.Width + x) * 3;
|
||||
int bufferIndex = (y * image.Width + x) * 3;
|
||||
Marshal.WriteByte(pixelData, rgbBuffer[bufferIndex + 2]);
|
||||
Marshal.WriteByte(pixelData + 1, rgbBuffer[bufferIndex + 1]);
|
||||
Marshal.WriteByte(pixelData + 2, rgbBuffer[bufferIndex]);
|
||||
@ -290,17 +290,17 @@ namespace NAPS2.ImportExport.Pdf
|
||||
}
|
||||
finally
|
||||
{
|
||||
storage.Unlock(data);
|
||||
image.Unlock(data);
|
||||
}
|
||||
}
|
||||
|
||||
private static void BlackAndWhiteToBitmapUnmanaged(IMemoryStorage storage, byte[] bwBuffer)
|
||||
private static void BlackAndWhiteToBitmapUnmanaged(IImage image, byte[] bwBuffer)
|
||||
{
|
||||
var data = storage.Lock(out var scan0, out var stride);
|
||||
var data = image.Lock(out var scan0, out var stride);
|
||||
try
|
||||
{
|
||||
int bytesPerRow = (storage.Width - 1) / 8 + 1;
|
||||
for (int y = 0; y < storage.Height; y++)
|
||||
int bytesPerRow = (image.Width - 1) / 8 + 1;
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
{
|
||||
for (int x = 0; x < bytesPerRow; x++)
|
||||
{
|
||||
@ -311,7 +311,7 @@ namespace NAPS2.ImportExport.Pdf
|
||||
}
|
||||
finally
|
||||
{
|
||||
storage.Unlock(data);
|
||||
image.Unlock(data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,7 +362,7 @@ namespace NAPS2.ImportExport.Pdf
|
||||
Write(stream, TiffTrailer);
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using (var storage = StorageManager.MemoryStorageFactory.Decode(stream, ".tiff"))
|
||||
using (var storage = StorageManager.ImageFactory.Decode(stream, ".tiff"))
|
||||
{
|
||||
storage.SetResolution(storage.Width / (float)page.Width.Inch, storage.Height / (float)page.Height.Inch);
|
||||
|
||||
|
@ -97,7 +97,7 @@ namespace NAPS2.ImportExport
|
||||
? new Rectangle(pb.Left, pb.Top, image.Width * pb.Height / image.Height, pb.Height)
|
||||
: new Rectangle(pb.Left, pb.Top, pb.Width, image.Height * pb.Width / image.Width);
|
||||
|
||||
e.Graphics.DrawImage(StorageManager.Convert<GdiStorage>(image).Bitmap, rect);
|
||||
e.Graphics.DrawImage(StorageManager.Convert<GdiImage>(image).Bitmap, rect);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -191,20 +191,20 @@
|
||||
<Compile Include="Platform\DefaultRuntimeCompat.cs" />
|
||||
<Compile Include="Scan\Exceptions\DriverNotSupportedException.cs" />
|
||||
<Compile Include="Scan\Images\ScannedImageSource.cs" />
|
||||
<Compile Include="Scan\Images\Storage\GdiTransformer.cs" />
|
||||
<Compile Include="Scan\Images\Storage\GdiTransformers.cs" />
|
||||
<Compile Include="Scan\Images\Storage\IFileStorage.cs" />
|
||||
<Compile Include="Scan\Images\Storage\IImageMetadata.cs" />
|
||||
<Compile Include="Scan\Images\Storage\IImageMetadataFactory.cs" />
|
||||
<Compile Include="Scan\Images\Storage\MemoryStreamStorage.cs" />
|
||||
<Compile Include="Scan\Images\Storage\PdfFileStorage.cs" />
|
||||
<Compile Include="Scan\Images\Storage\FileStorageManager.cs" />
|
||||
<Compile Include="Scan\Images\Storage\GdiFileConverter.cs" />
|
||||
<Compile Include="Scan\Images\Storage\GdiStorage.cs" />
|
||||
<Compile Include="Scan\Images\Storage\GdiStorageFactory.cs" />
|
||||
<Compile Include="Scan\Images\Storage\GdiConverters.cs" />
|
||||
<Compile Include="Scan\Images\Storage\GdiImage.cs" />
|
||||
<Compile Include="Scan\Images\Storage\GdiImageFactory.cs" />
|
||||
<Compile Include="Scan\Images\Storage\FileStorage.cs" />
|
||||
<Compile Include="Scan\Images\Storage\IMemoryStorage.cs" />
|
||||
<Compile Include="Scan\Images\Storage\IImage.cs" />
|
||||
<Compile Include="Scan\Images\Storage\IStorage.cs" />
|
||||
<Compile Include="Scan\Images\Storage\IMemoryStorageFactory.cs" />
|
||||
<Compile Include="Scan\Images\Storage\IImageFactory.cs" />
|
||||
<Compile Include="Scan\Images\Storage\RecoverableImageMetadata.cs" />
|
||||
<Compile Include="Scan\Images\Storage\RecoveryStorageManager.cs" />
|
||||
<Compile Include="Scan\Images\Storage\StorageConvertParams.cs" />
|
||||
|
@ -143,7 +143,7 @@ namespace NAPS2.Recovery
|
||||
}
|
||||
else
|
||||
{
|
||||
using (var bitmap = StorageManager.MemoryStorageFactory.Decode(imagePath))
|
||||
using (var bitmap = StorageManager.ImageFactory.Decode(imagePath))
|
||||
{
|
||||
scannedImage = new ScannedImage(bitmap, indexImage.BitDepth, indexImage.HighQuality, -1);
|
||||
}
|
||||
|
@ -11,9 +11,9 @@ namespace NAPS2.Scan.Images
|
||||
{
|
||||
public static class AutoDeskewExtensions
|
||||
{
|
||||
public static double GetSkewAngle(this IMemoryStorage bmp)
|
||||
public static double GetSkewAngle(this IImage image)
|
||||
{
|
||||
var sk = new gmseDeskew(bmp);
|
||||
var sk = new gmseDeskew(image);
|
||||
return sk.GetSkewAngle();
|
||||
}
|
||||
}
|
||||
@ -120,21 +120,21 @@ namespace NAPS2.Scan.Images
|
||||
return hl;
|
||||
}
|
||||
|
||||
public gmseDeskew(IMemoryStorage bitmap)
|
||||
public gmseDeskew(IImage image)
|
||||
{
|
||||
width = bitmap.Width;
|
||||
height = bitmap.Height;
|
||||
pf = bitmap.PixelFormat;
|
||||
LoadBitmap(bitmap);
|
||||
width = image.Width;
|
||||
height = image.Height;
|
||||
pf = image.PixelFormat;
|
||||
LoadImage(image);
|
||||
}
|
||||
|
||||
private void LoadBitmap(IMemoryStorage bitmap)
|
||||
private void LoadImage(IImage image)
|
||||
{
|
||||
var data = bitmap.Lock(out var scan0, out stride);
|
||||
var data = image.Lock(out var scan0, out stride);
|
||||
stride = Math.Abs(stride);
|
||||
bitmapBytes = new byte[stride * bitmap.Height];
|
||||
bitmapBytes = new byte[stride * image.Height];
|
||||
Marshal.Copy(scan0, bitmapBytes, 0, bitmapBytes.Length);
|
||||
bitmap.Unlock(data);
|
||||
image.Unlock(data);
|
||||
}
|
||||
|
||||
// Hough Transforamtion:
|
||||
|
@ -7,8 +7,8 @@ namespace NAPS2.Scan.Images
|
||||
{
|
||||
public interface IBlankDetector
|
||||
{
|
||||
bool IsBlank(IMemoryStorage bitmap, int whiteThresholdNorm, int coverageThresholdNorm);
|
||||
bool IsBlank(IImage image, int whiteThresholdNorm, int coverageThresholdNorm);
|
||||
|
||||
bool ExcludePage(IMemoryStorage bitmap, ScanProfile scanProfile);
|
||||
bool ExcludePage(IImage image, ScanProfile scanProfile);
|
||||
}
|
||||
}
|
||||
|
@ -1,69 +1,23 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Drawing;
|
||||
using System.Drawing.Imaging;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
using NAPS2.Recovery;
|
||||
using NAPS2.Scan.Images.Storage;
|
||||
using NAPS2.Scan.Images.Transforms;
|
||||
using NAPS2.Util;
|
||||
|
||||
namespace NAPS2.Scan.Images
|
||||
{
|
||||
public class ScannedImage : IDisposable
|
||||
{
|
||||
private IMemoryStorage thumbnail;
|
||||
private IImage thumbnail;
|
||||
private int thumbnailState;
|
||||
private int transformState;
|
||||
|
||||
private bool disposed;
|
||||
private int snapshotCount;
|
||||
|
||||
//public static ScannedImage FromSinglePagePdf(string pdfPath, bool copy)
|
||||
//{
|
||||
// return new ScannedImage(pdfPath, copy);
|
||||
//}
|
||||
|
||||
//public ScannedImage(Bitmap img, ScanBitDepth bitDepth, bool highQuality, int quality)
|
||||
//{
|
||||
// string tempFilePath = ScannedImageHelper.SaveSmallestBitmap(img, bitDepth, highQuality, quality, out ImageFormat fileFormat);
|
||||
|
||||
// transformList = new List<Transform>();
|
||||
// recoveryImage = RecoveryImage.CreateNew(fileFormat, bitDepth, highQuality, transformList);
|
||||
|
||||
// File.Move(tempFilePath, recoveryImage.FilePath);
|
||||
|
||||
// recoveryImage.Save();
|
||||
//}
|
||||
|
||||
//public ScannedImage(RecoveryIndexImage recoveryIndexImage)
|
||||
//{
|
||||
// recoveryImage = RecoveryImage.LoadExisting(recoveryIndexImage);
|
||||
// transformList = recoveryImage.IndexImage.TransformList;
|
||||
//}
|
||||
|
||||
//private ScannedImage(string pdfPath, bool copy)
|
||||
//{
|
||||
// transformList = new List<Transform>();
|
||||
// recoveryImage = RecoveryImage.CreateNew(null, ScanBitDepth.C24Bit, false, transformList);
|
||||
|
||||
// if (copy)
|
||||
// {
|
||||
// File.Copy(pdfPath, recoveryImage.FilePath);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// File.Move(pdfPath, recoveryImage.FilePath);
|
||||
// }
|
||||
|
||||
// recoveryImage.Save();
|
||||
//}
|
||||
|
||||
|
||||
public ScannedImage(IStorage storage) : this(storage, new StorageConvertParams())
|
||||
{
|
||||
}
|
||||
@ -147,7 +101,7 @@ namespace NAPS2.Scan.Images
|
||||
ThumbnailInvalidated?.Invoke(this, new EventArgs());
|
||||
}
|
||||
|
||||
public IMemoryStorage GetThumbnail()
|
||||
public IImage GetThumbnail()
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
@ -155,12 +109,12 @@ namespace NAPS2.Scan.Images
|
||||
}
|
||||
}
|
||||
|
||||
public void SetThumbnail(IMemoryStorage bitmap, int? state = null)
|
||||
public void SetThumbnail(IImage image, int? state = null)
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
thumbnail?.Dispose();
|
||||
thumbnail = bitmap;
|
||||
thumbnail = image;
|
||||
thumbnailState = state ?? transformState;
|
||||
}
|
||||
ThumbnailChanged?.Invoke(this, new EventArgs());
|
||||
|
@ -115,7 +115,7 @@ namespace NAPS2.Scan.Images
|
||||
this.ocrManager = ocrManager;
|
||||
}
|
||||
|
||||
public IMemoryStorage PostProcessStep1(IMemoryStorage output, ScanProfile profile, bool supportsNativeUI = true)
|
||||
public IImage PostProcessStep1(IImage output, ScanProfile profile, bool supportsNativeUI = true)
|
||||
{
|
||||
double scaleFactor = 1;
|
||||
if (!profile.UseNativeUI || !supportsNativeUI)
|
||||
@ -171,11 +171,11 @@ namespace NAPS2.Scan.Images
|
||||
return result;
|
||||
}
|
||||
|
||||
public void PostProcessStep2(ScannedImage image, IMemoryStorage bitmap, ScanProfile profile, ScanParams scanParams, int pageNumber, bool supportsNativeUI = true)
|
||||
public void PostProcessStep2(ScannedImage scannedImage, IImage image, ScanProfile profile, ScanParams scanParams, int pageNumber, bool supportsNativeUI = true)
|
||||
{
|
||||
if (!scanParams.NoThumbnails)
|
||||
{
|
||||
image.SetThumbnail(StorageManager.PerformTransform(bitmap, new ThumbnailTransform()));
|
||||
scannedImage.SetThumbnail(StorageManager.PerformTransform(image, new ThumbnailTransform()));
|
||||
}
|
||||
if (scanParams.SkipPostProcessing)
|
||||
{
|
||||
@ -185,29 +185,29 @@ namespace NAPS2.Scan.Images
|
||||
{
|
||||
if (profile.Brightness != 0)
|
||||
{
|
||||
AddTransformAndUpdateThumbnail(image, ref bitmap, new BrightnessTransform { Brightness = profile.Brightness });
|
||||
AddTransformAndUpdateThumbnail(scannedImage, ref image, new BrightnessTransform { Brightness = profile.Brightness });
|
||||
}
|
||||
if (profile.Contrast != 0)
|
||||
{
|
||||
AddTransformAndUpdateThumbnail(image, ref bitmap, new TrueContrastTransform { Contrast = profile.Contrast });
|
||||
AddTransformAndUpdateThumbnail(scannedImage, ref image, new TrueContrastTransform { Contrast = profile.Contrast });
|
||||
}
|
||||
}
|
||||
if (profile.FlipDuplexedPages && pageNumber % 2 == 0)
|
||||
{
|
||||
AddTransformAndUpdateThumbnail(image, ref bitmap, new RotationTransform(RotateFlipType.Rotate180FlipNone));
|
||||
AddTransformAndUpdateThumbnail(scannedImage, ref image, new RotationTransform(RotateFlipType.Rotate180FlipNone));
|
||||
}
|
||||
if (profile.AutoDeskew)
|
||||
{
|
||||
var op = operationFactory.Create<DeskewOperation>();
|
||||
if (op.Start(new[] { image }))
|
||||
if (op.Start(new[] { scannedImage }))
|
||||
{
|
||||
operationProgress.ShowProgress(op);
|
||||
op.Wait();
|
||||
}
|
||||
}
|
||||
if (scanParams.DetectPatchCodes && image.PatchCode == PatchCode.None)
|
||||
if (scanParams.DetectPatchCodes && scannedImage.PatchCode == PatchCode.None)
|
||||
{
|
||||
image.PatchCode = PatchCodeDetector.Detect(bitmap);
|
||||
scannedImage.PatchCode = PatchCodeDetector.Detect(image);
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,7 +220,7 @@ namespace NAPS2.Scan.Images
|
||||
return scanParams.DoOcr ?? (ocrEnabled && afterScanning);
|
||||
}
|
||||
|
||||
public string SaveForBackgroundOcr(IMemoryStorage bitmap, ScanParams scanParams)
|
||||
public string SaveForBackgroundOcr(IImage bitmap, ScanParams scanParams)
|
||||
{
|
||||
if (ShouldDoBackgroundOcr(scanParams))
|
||||
{
|
||||
@ -249,14 +249,14 @@ namespace NAPS2.Scan.Images
|
||||
}
|
||||
}
|
||||
|
||||
private void AddTransformAndUpdateThumbnail(ScannedImage image, ref IMemoryStorage bitmap, Transform transform)
|
||||
private void AddTransformAndUpdateThumbnail(ScannedImage scannedImage, ref IImage image, Transform transform)
|
||||
{
|
||||
image.AddTransform(transform);
|
||||
var thumbnail = image.GetThumbnail();
|
||||
scannedImage.AddTransform(transform);
|
||||
var thumbnail = scannedImage.GetThumbnail();
|
||||
if (thumbnail != null)
|
||||
{
|
||||
bitmap = StorageManager.PerformTransform(bitmap, transform);
|
||||
image.SetThumbnail(StorageManager.PerformTransform(bitmap, new ThumbnailTransform()));
|
||||
image = StorageManager.PerformTransform(image, transform);
|
||||
scannedImage.SetThumbnail(StorageManager.PerformTransform(image, new ThumbnailTransform()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ namespace NAPS2.Scan.Images
|
||||
this.pdfRenderer = pdfRenderer;
|
||||
}
|
||||
|
||||
public async Task<IMemoryStorage> Render(ScannedImage image, int outputSize = 0)
|
||||
public async Task<IImage> Render(ScannedImage image, int outputSize = 0)
|
||||
{
|
||||
using (var snapshot = image.Preserve())
|
||||
{
|
||||
@ -29,11 +29,11 @@ namespace NAPS2.Scan.Images
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IMemoryStorage> Render(ScannedImage.Snapshot snapshot, int outputSize = 0)
|
||||
public async Task<IImage> Render(ScannedImage.Snapshot snapshot, int outputSize = 0)
|
||||
{
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
var storage = StorageManager.ConvertToMemory(snapshot.Source.BackingStorage, new StorageConvertParams());
|
||||
var storage = StorageManager.ConvertToImage(snapshot.Source.BackingStorage, new StorageConvertParams());
|
||||
if (outputSize > 0)
|
||||
{
|
||||
double scaleFactor = Math.Min(outputSize / (double)storage.Height, outputSize / (double)storage.Width);
|
||||
|
@ -7,10 +7,10 @@ using System.Linq;
|
||||
|
||||
namespace NAPS2.Scan.Images.Storage
|
||||
{
|
||||
public class GdiFileConverter
|
||||
public class GdiConverters
|
||||
{
|
||||
[StorageConverter]
|
||||
public FileStorage ConvertToFile(GdiStorage input, StorageConvertParams convertParams)
|
||||
public FileStorage ConvertToFile(GdiImage input, StorageConvertParams convertParams)
|
||||
{
|
||||
if (convertParams.Temporary)
|
||||
{
|
||||
@ -20,19 +20,19 @@ namespace NAPS2.Scan.Images.Storage
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Save smallest
|
||||
string ext = convertParams.Lossless ? ".png" : ".jpg";
|
||||
var tempPath = ScannedImageHelper.SaveSmallestBitmap(input.Bitmap, convertParams.BitDepth, convertParams.Lossless, convertParams.LossyQuality, out ImageFormat fileFormat);
|
||||
string ext = Equals(fileFormat, ImageFormat.Png) ? ".png" : ".jpg";
|
||||
var path = FileStorageManager.Default.NextFilePath() + ext;
|
||||
input.Bitmap.Save(path);
|
||||
File.Move(tempPath, path);
|
||||
return new FileStorage(path);
|
||||
}
|
||||
}
|
||||
|
||||
[StorageConverter]
|
||||
public GdiStorage ConvertToGdi(FileStorage input, StorageConvertParams convertParams) => new GdiStorage(new Bitmap(input.FullPath));
|
||||
public GdiImage ConvertToGdi(FileStorage input, StorageConvertParams convertParams) => new GdiImage(new Bitmap(input.FullPath));
|
||||
|
||||
[StorageConverter]
|
||||
public MemoryStreamStorage ConvertToMemoryStream(GdiStorage input, StorageConvertParams convertParams)
|
||||
public MemoryStreamStorage ConvertToMemoryStream(GdiImage input, StorageConvertParams convertParams)
|
||||
{
|
||||
var stream = new MemoryStream();
|
||||
// TODO: Better format choice?
|
@ -6,9 +6,9 @@ using System.Linq;
|
||||
|
||||
namespace NAPS2.Scan.Images.Storage
|
||||
{
|
||||
public class GdiStorage : IMemoryStorage
|
||||
public class GdiImage : IImage
|
||||
{
|
||||
public GdiStorage(Bitmap bitmap)
|
||||
public GdiImage(Bitmap bitmap)
|
||||
{
|
||||
Bitmap = bitmap ?? throw new ArgumentNullException(nameof(bitmap));
|
||||
}
|
||||
@ -70,9 +70,9 @@ namespace NAPS2.Scan.Images.Storage
|
||||
Bitmap.Dispose();
|
||||
}
|
||||
|
||||
public IMemoryStorage Clone()
|
||||
public IImage Clone()
|
||||
{
|
||||
return new GdiStorage((Bitmap)Bitmap.Clone());
|
||||
return new GdiImage((Bitmap)Bitmap.Clone());
|
||||
}
|
||||
}
|
||||
}
|
@ -7,39 +7,39 @@ using System.Linq;
|
||||
|
||||
namespace NAPS2.Scan.Images.Storage
|
||||
{
|
||||
public class GdiStorageFactory : IMemoryStorageFactory
|
||||
public class GdiImageFactory : IImageFactory
|
||||
{
|
||||
public IMemoryStorage Decode(Stream stream, string ext) => new GdiStorage(new Bitmap(stream));
|
||||
public IImage Decode(Stream stream, string ext) => new GdiImage(new Bitmap(stream));
|
||||
|
||||
public IMemoryStorage Decode(string path) => new GdiStorage(new Bitmap(path));
|
||||
public IImage Decode(string path) => new GdiImage(new Bitmap(path));
|
||||
|
||||
public IEnumerable<IMemoryStorage> DecodeMultiple(Stream stream, string ext, out int count)
|
||||
public IEnumerable<IImage> DecodeMultiple(Stream stream, string ext, out int count)
|
||||
{
|
||||
var bitmap = new Bitmap(stream);
|
||||
count = bitmap.GetFrameCount(FrameDimension.Page);
|
||||
return EnumerateFrames(bitmap, count);
|
||||
}
|
||||
|
||||
public IEnumerable<IMemoryStorage> DecodeMultiple(string path, out int count)
|
||||
public IEnumerable<IImage> DecodeMultiple(string path, out int count)
|
||||
{
|
||||
var bitmap = new Bitmap(path);
|
||||
count = bitmap.GetFrameCount(FrameDimension.Page);
|
||||
return EnumerateFrames(bitmap, count);
|
||||
}
|
||||
|
||||
private IEnumerable<IMemoryStorage> EnumerateFrames(Bitmap bitmap, int count)
|
||||
private IEnumerable<IImage> EnumerateFrames(Bitmap bitmap, int count)
|
||||
{
|
||||
using (bitmap)
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
bitmap.SelectActiveFrame(FrameDimension.Page, i);
|
||||
yield return new GdiStorage((Bitmap) bitmap.Clone());
|
||||
yield return new GdiImage((Bitmap) bitmap.Clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IMemoryStorage FromDimensions(int width, int height, StoragePixelFormat pixelFormat) => new GdiStorage(new Bitmap(width, height, GdiPixelFormat(pixelFormat)));
|
||||
public IImage FromDimensions(int width, int height, StoragePixelFormat pixelFormat) => new GdiImage(new Bitmap(width, height, GdiPixelFormat(pixelFormat)));
|
||||
|
||||
private PixelFormat GdiPixelFormat(StoragePixelFormat pixelFormat)
|
||||
{
|
@ -10,24 +10,24 @@ using NAPS2.Util;
|
||||
|
||||
namespace NAPS2.Scan.Images.Storage
|
||||
{
|
||||
public class GdiTransformer
|
||||
public class GdiTransformers
|
||||
{
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, BrightnessTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, BrightnessTransform transform)
|
||||
{
|
||||
var bitmap = storage.Bitmap;
|
||||
var bitmap = image.Bitmap;
|
||||
float brightnessAdjusted = transform.Brightness / 1000f;
|
||||
EnsurePixelFormat(ref bitmap);
|
||||
UnsafeImageOps.ChangeBrightness(bitmap, brightnessAdjusted);
|
||||
return new GdiStorage(bitmap);
|
||||
return new GdiImage(bitmap);
|
||||
}
|
||||
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, ContrastTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, ContrastTransform transform)
|
||||
{
|
||||
float contrastAdjusted = transform.Contrast / 1000f + 1.0f;
|
||||
|
||||
var bitmap = storage.Bitmap;
|
||||
var bitmap = image.Bitmap;
|
||||
EnsurePixelFormat(ref bitmap);
|
||||
using (var g = Graphics.FromImage(bitmap))
|
||||
{
|
||||
@ -47,31 +47,31 @@ namespace NAPS2.Scan.Images.Storage
|
||||
GraphicsUnit.Pixel,
|
||||
attrs);
|
||||
}
|
||||
return storage;
|
||||
return image;
|
||||
}
|
||||
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, TrueContrastTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, TrueContrastTransform transform)
|
||||
{
|
||||
// convert +/-1000 input range to a logarithmic scaled multiplier
|
||||
float contrastAdjusted = (float)Math.Pow(2.718281f, transform.Contrast / 500.0f);
|
||||
// see http://docs.rainmeter.net/tips/colormatrix-guide/ for offset & matrix calculation
|
||||
float offset = (1.0f - contrastAdjusted) / 2.0f;
|
||||
|
||||
var bitmap = storage.Bitmap;
|
||||
var bitmap = image.Bitmap;
|
||||
EnsurePixelFormat(ref bitmap);
|
||||
UnsafeImageOps.ChangeContrast(bitmap, contrastAdjusted, offset);
|
||||
// TODO: Actually need to create a new storage. Change signature of EnsurePixelFormat.
|
||||
return storage;
|
||||
return image;
|
||||
}
|
||||
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, HueTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, HueTransform transform)
|
||||
{
|
||||
if (storage.PixelFormat != StoragePixelFormat.RGB24 && storage.PixelFormat != StoragePixelFormat.ARGB32)
|
||||
if (image.PixelFormat != StoragePixelFormat.RGB24 && image.PixelFormat != StoragePixelFormat.ARGB32)
|
||||
{
|
||||
// No need to handle 1bpp since hue shifts are null transforms
|
||||
return storage;
|
||||
return image;
|
||||
}
|
||||
|
||||
float hueShiftAdjusted = transform.HueShift / 2000f * 360;
|
||||
@ -80,17 +80,17 @@ namespace NAPS2.Scan.Images.Storage
|
||||
hueShiftAdjusted += 360;
|
||||
}
|
||||
|
||||
UnsafeImageOps.HueShift(storage.Bitmap, hueShiftAdjusted);
|
||||
UnsafeImageOps.HueShift(image.Bitmap, hueShiftAdjusted);
|
||||
|
||||
return storage;
|
||||
return image;
|
||||
}
|
||||
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, SaturationTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, SaturationTransform transform)
|
||||
{
|
||||
double saturationAdjusted = transform.Saturation / 1000.0 + 1;
|
||||
|
||||
var bitmap = storage.Bitmap;
|
||||
var bitmap = image.Bitmap;
|
||||
EnsurePixelFormat(ref bitmap);
|
||||
int bytesPerPixel;
|
||||
if (bitmap.PixelFormat == PixelFormat.Format24bppRgb)
|
||||
@ -103,7 +103,7 @@ namespace NAPS2.Scan.Images.Storage
|
||||
}
|
||||
else
|
||||
{
|
||||
return storage;
|
||||
return image;
|
||||
}
|
||||
|
||||
var data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
|
||||
@ -130,15 +130,15 @@ namespace NAPS2.Scan.Images.Storage
|
||||
}
|
||||
bitmap.UnlockBits(data);
|
||||
|
||||
return storage;
|
||||
return image;
|
||||
}
|
||||
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, SharpenTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, SharpenTransform transform)
|
||||
{
|
||||
double sharpnessAdjusted = transform.Sharpness / 1000.0;
|
||||
|
||||
var bitmap = storage.Bitmap;
|
||||
var bitmap = image.Bitmap;
|
||||
EnsurePixelFormat(ref bitmap);
|
||||
int bytesPerPixel;
|
||||
if (bitmap.PixelFormat == PixelFormat.Format24bppRgb)
|
||||
@ -151,7 +151,7 @@ namespace NAPS2.Scan.Images.Storage
|
||||
}
|
||||
else
|
||||
{
|
||||
return storage;
|
||||
return image;
|
||||
}
|
||||
|
||||
// From https://stackoverflow.com/a/17596299
|
||||
@ -241,119 +241,119 @@ namespace NAPS2.Scan.Images.Storage
|
||||
// Release image bits.
|
||||
bitmap.UnlockBits(pbits);
|
||||
|
||||
return storage;
|
||||
return image;
|
||||
}
|
||||
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, RotationTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, RotationTransform transform)
|
||||
{
|
||||
|
||||
if (Math.Abs(transform.Angle - 0.0) < RotationTransform.TOLERANCE)
|
||||
{
|
||||
return storage;
|
||||
return image;
|
||||
}
|
||||
if (Math.Abs(transform.Angle - 90.0) < RotationTransform.TOLERANCE)
|
||||
{
|
||||
storage.Bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
|
||||
return storage;
|
||||
image.Bitmap.RotateFlip(RotateFlipType.Rotate90FlipNone);
|
||||
return image;
|
||||
}
|
||||
if (Math.Abs(transform.Angle - 180.0) < RotationTransform.TOLERANCE)
|
||||
{
|
||||
storage.Bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
|
||||
return storage;
|
||||
image.Bitmap.RotateFlip(RotateFlipType.Rotate180FlipNone);
|
||||
return image;
|
||||
}
|
||||
if (Math.Abs(transform.Angle - 270.0) < RotationTransform.TOLERANCE)
|
||||
{
|
||||
storage.Bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
|
||||
return storage;
|
||||
image.Bitmap.RotateFlip(RotateFlipType.Rotate270FlipNone);
|
||||
return image;
|
||||
}
|
||||
Bitmap result;
|
||||
if (transform.Angle > 45.0 && transform.Angle < 135.0 || transform.Angle > 225.0 && transform.Angle < 315.0)
|
||||
{
|
||||
result = new Bitmap(storage.Height, storage.Width);
|
||||
result.SafeSetResolution(storage.VerticalResolution, storage.HorizontalResolution);
|
||||
result = new Bitmap(image.Height, image.Width);
|
||||
result.SafeSetResolution(image.VerticalResolution, image.HorizontalResolution);
|
||||
}
|
||||
else
|
||||
{
|
||||
result = new Bitmap(storage.Width, storage.Height);
|
||||
result.SafeSetResolution(storage.HorizontalResolution, storage.VerticalResolution);
|
||||
result = new Bitmap(image.Width, image.Height);
|
||||
result.SafeSetResolution(image.HorizontalResolution, image.VerticalResolution);
|
||||
}
|
||||
using (var g = Graphics.FromImage(result))
|
||||
{
|
||||
g.Clear(Color.White);
|
||||
g.TranslateTransform(result.Width / 2.0f, result.Height / 2.0f);
|
||||
g.RotateTransform((float)transform.Angle);
|
||||
g.TranslateTransform(-storage.Width / 2.0f, -storage.Height / 2.0f);
|
||||
g.DrawImage(storage.Bitmap, new Rectangle(0, 0, storage.Width, storage.Height));
|
||||
g.TranslateTransform(-image.Width / 2.0f, -image.Height / 2.0f);
|
||||
g.DrawImage(image.Bitmap, new Rectangle(0, 0, image.Width, image.Height));
|
||||
}
|
||||
OptimizePixelFormat(storage.Bitmap, ref result);
|
||||
storage.Dispose();
|
||||
return new GdiStorage(result);
|
||||
OptimizePixelFormat(image.Bitmap, ref result);
|
||||
image.Dispose();
|
||||
return new GdiImage(result);
|
||||
}
|
||||
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, CropTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, CropTransform transform)
|
||||
{
|
||||
double xScale = storage.Width / (double)(transform.OriginalWidth ?? storage.Width),
|
||||
yScale = storage.Height / (double)(transform.OriginalHeight ?? storage.Height);
|
||||
double xScale = image.Width / (double)(transform.OriginalWidth ?? image.Width),
|
||||
yScale = image.Height / (double)(transform.OriginalHeight ?? image.Height);
|
||||
|
||||
int width = Math.Max(storage.Width - (int)Math.Round((transform.Left + transform.Right) * xScale), 1);
|
||||
int height = Math.Max(storage.Height - (int)Math.Round((transform.Top + transform.Bottom) * yScale), 1);
|
||||
int width = Math.Max(image.Width - (int)Math.Round((transform.Left + transform.Right) * xScale), 1);
|
||||
int height = Math.Max(image.Height - (int)Math.Round((transform.Top + transform.Bottom) * yScale), 1);
|
||||
var result = new Bitmap(width, height, PixelFormat.Format24bppRgb);
|
||||
result.SafeSetResolution(storage.HorizontalResolution, storage.VerticalResolution);
|
||||
result.SafeSetResolution(image.HorizontalResolution, image.VerticalResolution);
|
||||
using (var g = Graphics.FromImage(result))
|
||||
{
|
||||
g.Clear(Color.White);
|
||||
int x = (int) Math.Round(-transform.Left * xScale);
|
||||
int y = (int) Math.Round(-transform.Top * yScale);
|
||||
g.DrawImage(storage.Bitmap, new Rectangle(x, y, storage.Width, storage.Height));
|
||||
g.DrawImage(image.Bitmap, new Rectangle(x, y, image.Width, image.Height));
|
||||
}
|
||||
OptimizePixelFormat(storage.Bitmap, ref result);
|
||||
storage.Dispose();
|
||||
return new GdiStorage(result);
|
||||
OptimizePixelFormat(image.Bitmap, ref result);
|
||||
image.Dispose();
|
||||
return new GdiImage(result);
|
||||
}
|
||||
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, ScaleTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, ScaleTransform transform)
|
||||
{
|
||||
double realWidth = storage.Width / transform.ScaleFactor;
|
||||
double realHeight = storage.Height / transform.ScaleFactor;
|
||||
double realWidth = image.Width / transform.ScaleFactor;
|
||||
double realHeight = image.Height / transform.ScaleFactor;
|
||||
|
||||
double horizontalRes = storage.HorizontalResolution / transform.ScaleFactor;
|
||||
double verticalRes = storage.VerticalResolution / transform.ScaleFactor;
|
||||
double horizontalRes = image.HorizontalResolution / transform.ScaleFactor;
|
||||
double verticalRes = image.VerticalResolution / transform.ScaleFactor;
|
||||
|
||||
var result = new Bitmap((int)realWidth, (int)realHeight, PixelFormat.Format24bppRgb);
|
||||
using (Graphics g = Graphics.FromImage(result))
|
||||
{
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
g.DrawImage(storage.Bitmap, 0, 0, (int)realWidth, (int)realHeight);
|
||||
g.DrawImage(image.Bitmap, 0, 0, (int)realWidth, (int)realHeight);
|
||||
result.SafeSetResolution((float)horizontalRes, (float)verticalRes);
|
||||
return new GdiStorage(result);
|
||||
return new GdiImage(result);
|
||||
}
|
||||
}
|
||||
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, BlackWhiteTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, BlackWhiteTransform transform)
|
||||
{
|
||||
if (storage.PixelFormat != StoragePixelFormat.RGB24 && storage.PixelFormat != StoragePixelFormat.ARGB32)
|
||||
if (image.PixelFormat != StoragePixelFormat.RGB24 && image.PixelFormat != StoragePixelFormat.ARGB32)
|
||||
{
|
||||
return storage;
|
||||
return image;
|
||||
}
|
||||
|
||||
var monoBitmap = UnsafeImageOps.ConvertTo1Bpp(storage.Bitmap, transform.Threshold);
|
||||
storage.Dispose();
|
||||
var monoBitmap = UnsafeImageOps.ConvertTo1Bpp(image.Bitmap, transform.Threshold);
|
||||
image.Dispose();
|
||||
|
||||
return new GdiStorage(monoBitmap);
|
||||
return new GdiImage(monoBitmap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a bitmap resized to fit within a thumbnail rectangle, including a border around the picture.
|
||||
/// </summary>
|
||||
/// <param name="storage">The bitmap to resize.</param>
|
||||
/// <param name="image">The bitmap to resize.</param>
|
||||
/// <param name="transform">The maximum width and height of the thumbnail.</param>
|
||||
/// <returns>The thumbnail bitmap.</returns>
|
||||
[Transformer]
|
||||
public GdiStorage PerformTransform(GdiStorage storage, ThumbnailTransform transform)
|
||||
public GdiImage PerformTransform(GdiImage image, ThumbnailTransform transform)
|
||||
{
|
||||
var result = new Bitmap(transform.Size, transform.Size);
|
||||
using (Graphics g = Graphics.FromImage(result))
|
||||
@ -364,13 +364,13 @@ namespace NAPS2.Scan.Images.Storage
|
||||
// We want a nice thumbnail, so use the maximum quality interpolation
|
||||
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
|
||||
|
||||
if (storage.Width > storage.Height)
|
||||
if (image.Width > image.Height)
|
||||
{
|
||||
// Fill the new bitmap's width
|
||||
width = transform.Size;
|
||||
left = 0;
|
||||
// Scale the drawing height to match the original bitmap's aspect ratio
|
||||
height = (int)(storage.Height * (transform.Size / (double)storage.Width));
|
||||
height = (int)(image.Height * (transform.Size / (double)image.Width));
|
||||
// Center the drawing vertically
|
||||
top = (transform.Size - height) / 2;
|
||||
}
|
||||
@ -380,7 +380,7 @@ namespace NAPS2.Scan.Images.Storage
|
||||
height = transform.Size;
|
||||
top = 0;
|
||||
// Scale the drawing width to match the original bitmap's aspect ratio
|
||||
width = (int)(storage.Width * (transform.Size / (double)storage.Height));
|
||||
width = (int)(image.Width * (transform.Size / (double)image.Height));
|
||||
// Center the drawing horizontally
|
||||
left = (transform.Size - width) / 2;
|
||||
}
|
||||
@ -388,13 +388,13 @@ namespace NAPS2.Scan.Images.Storage
|
||||
// Draw the original bitmap onto the new bitmap, using the calculated location and dimensions
|
||||
// Note that there may be some padding if the aspect ratios don't match
|
||||
var destRect = new RectangleF(left, top, width, height);
|
||||
var srcRect = new RectangleF(0, 0, storage.Width, storage.Height);
|
||||
g.DrawImage(storage.Bitmap, destRect, srcRect, GraphicsUnit.Pixel);
|
||||
var srcRect = new RectangleF(0, 0, image.Width, image.Height);
|
||||
g.DrawImage(image.Bitmap, destRect, srcRect, GraphicsUnit.Pixel);
|
||||
// Draw a border around the orignal bitmap's content, inside the padding
|
||||
g.DrawRectangle(Pens.Black, left, top, width - 1, height - 1);
|
||||
}
|
||||
|
||||
return new GdiStorage(result);
|
||||
return new GdiImage(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
@ -5,7 +5,7 @@ using System.Linq;
|
||||
namespace NAPS2.Scan.Images.Storage
|
||||
{
|
||||
// TODO: Maybe just call this IImage.
|
||||
public interface IMemoryStorage : IStorage
|
||||
public interface IImage : IStorage
|
||||
{
|
||||
int Width { get; }
|
||||
|
||||
@ -25,6 +25,6 @@ namespace NAPS2.Scan.Images.Storage
|
||||
|
||||
void Unlock(object state);
|
||||
|
||||
IMemoryStorage Clone();
|
||||
IImage Clone();
|
||||
}
|
||||
}
|
@ -5,7 +5,7 @@ using System.Linq;
|
||||
|
||||
namespace NAPS2.Scan.Images.Storage
|
||||
{
|
||||
public interface IMemoryStorageFactory
|
||||
public interface IImageFactory
|
||||
{
|
||||
/// <summary>
|
||||
/// Decodes an image from the given stream and file extension.
|
||||
@ -13,14 +13,14 @@ namespace NAPS2.Scan.Images.Storage
|
||||
/// <param name="stream">The image data, in a common format (JPEG, PNG, etc).</param>
|
||||
/// <param name="ext">A file extension hinting at the image format. When possible, the contents of the stream should be used to definitively determine the image format.</param>
|
||||
/// <returns></returns>
|
||||
IMemoryStorage Decode(Stream stream, string ext);
|
||||
IImage Decode(Stream stream, string ext);
|
||||
|
||||
/// <summary>
|
||||
/// Decodes an image from the given file path.
|
||||
/// </summary>
|
||||
/// <param name="path">The image path.</param>
|
||||
/// <returns></returns>
|
||||
IMemoryStorage Decode(string path);
|
||||
IImage Decode(string path);
|
||||
|
||||
/// <summary>
|
||||
/// Decodes an image from the given stream and file extension.
|
||||
@ -31,7 +31,7 @@ namespace NAPS2.Scan.Images.Storage
|
||||
/// <param name="ext">A file extension hinting at the image format. When possible, the contents of the stream should be used to definitively determine the image format.</param>
|
||||
/// <param name="count">The number of returned images.</param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IMemoryStorage> DecodeMultiple(Stream stream, string ext, out int count);
|
||||
IEnumerable<IImage> DecodeMultiple(Stream stream, string ext, out int count);
|
||||
|
||||
/// <summary>
|
||||
/// Decodes an image from the given file path.
|
||||
@ -39,8 +39,8 @@ namespace NAPS2.Scan.Images.Storage
|
||||
/// <param name="path">The image path.</param>
|
||||
/// <param name="count">The number of returned images.</param>
|
||||
/// <returns></returns>
|
||||
IEnumerable<IMemoryStorage> DecodeMultiple(string path, out int count);
|
||||
IEnumerable<IImage> DecodeMultiple(string path, out int count);
|
||||
|
||||
IMemoryStorage FromDimensions(int width, int height, StoragePixelFormat pixelFormat);
|
||||
IImage FromDimensions(int width, int height, StoragePixelFormat pixelFormat);
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ namespace NAPS2.Scan.Images.Storage
|
||||
|
||||
public RecoveryStorageManager(string recoveryFolderPath)
|
||||
{
|
||||
this.RecoveryFolderPath = recoveryFolderPath;
|
||||
RecoveryFolderPath = recoveryFolderPath;
|
||||
}
|
||||
|
||||
public string RecoveryFolderPath { get; }
|
||||
|
@ -11,5 +11,8 @@ namespace NAPS2.Scan.Images.Storage
|
||||
public bool Lossless { get; set; }
|
||||
|
||||
public int LossyQuality { get; set; }
|
||||
|
||||
// TODO: Move bit depth out of Scan namespace?
|
||||
public ScanBitDepth BitDepth { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using NAPS2.Scan.Images.Transforms;
|
||||
using NAPS2.Util;
|
||||
|
||||
namespace NAPS2.Scan.Images.Storage
|
||||
{
|
||||
@ -13,11 +12,11 @@ namespace NAPS2.Scan.Images.Storage
|
||||
|
||||
public static Type PreferredBackingStorageType { get; set; } = typeof(FileStorage);
|
||||
|
||||
public static Type PreferredMemoryStorageType { get; set; } = typeof(GdiStorage);
|
||||
public static Type PreferredImageType { get; set; } = typeof(GdiImage);
|
||||
|
||||
public static HashSet<Type> BackingStorageTypes { get; set; } = new HashSet<Type> { typeof(FileStorage), typeof(PdfFileStorage) };
|
||||
|
||||
public static IMemoryStorageFactory MemoryStorageFactory { get; set; } = new GdiStorageFactory();
|
||||
public static IImageFactory ImageFactory { get; set; } = new GdiImageFactory();
|
||||
|
||||
public static IImageMetadataFactory ImageMetadataFactory { get; set; }
|
||||
|
||||
@ -25,9 +24,9 @@ namespace NAPS2.Scan.Images.Storage
|
||||
|
||||
public static void RegisterTransformers(Type imageType, object transformerObj)
|
||||
{
|
||||
if (!typeof(IMemoryStorage).IsAssignableFrom(imageType))
|
||||
if (!typeof(IImage).IsAssignableFrom(imageType))
|
||||
{
|
||||
throw new ArgumentException($"The image type must implement {nameof(IMemoryStorage)}.", nameof(imageType));
|
||||
throw new ArgumentException($"The image type must implement {nameof(IImage)}.", nameof(imageType));
|
||||
}
|
||||
foreach (var method in transformerObj.GetType().GetMethods().Where(x => x.GetCustomAttributes(typeof(TransformerAttribute), true).Any()))
|
||||
{
|
||||
@ -35,7 +34,7 @@ namespace NAPS2.Scan.Images.Storage
|
||||
var storageType = methodParams[0].ParameterType;
|
||||
var transformType = methodParams[1].ParameterType;
|
||||
if (methodParams.Length == 2 &&
|
||||
typeof(IMemoryStorage).IsAssignableFrom(method.ReturnType) &&
|
||||
typeof(IImage).IsAssignableFrom(method.ReturnType) &&
|
||||
storageType.IsAssignableFrom(imageType) &&
|
||||
typeof(Transform).IsAssignableFrom(transformType))
|
||||
{
|
||||
@ -44,22 +43,22 @@ namespace NAPS2.Scan.Images.Storage
|
||||
}
|
||||
}
|
||||
|
||||
public static IMemoryStorage PerformTransform(IMemoryStorage storage, Transform transform)
|
||||
public static IImage PerformTransform(IImage image, Transform transform)
|
||||
{
|
||||
try
|
||||
{
|
||||
var (transformer, perform) = Transformers[(storage.GetType(), transform.GetType())];
|
||||
return (IMemoryStorage)perform.Invoke(transformer, new object[] { storage, transform });
|
||||
var (transformer, perform) = Transformers[(image.GetType(), transform.GetType())];
|
||||
return (IImage)perform.Invoke(transformer, new object[] { image, transform });
|
||||
}
|
||||
catch (KeyNotFoundException)
|
||||
{
|
||||
throw new ArgumentException($"No transformer exists for {storage.GetType().Name} and {transform.GetType().Name}");
|
||||
throw new ArgumentException($"No transformer exists for {image.GetType().Name} and {transform.GetType().Name}");
|
||||
}
|
||||
}
|
||||
|
||||
public static IMemoryStorage PerformAllTransforms(IMemoryStorage storage, IEnumerable<Transform> transforms)
|
||||
public static IImage PerformAllTransforms(IImage image, IEnumerable<Transform> transforms)
|
||||
{
|
||||
return transforms.Aggregate(storage, PerformTransform);
|
||||
return transforms.Aggregate(image, PerformTransform);
|
||||
}
|
||||
|
||||
private static readonly Dictionary<(Type, Type), (object, MethodInfo)> Converters = new Dictionary<(Type, Type), (object, MethodInfo)>();
|
||||
@ -91,13 +90,13 @@ namespace NAPS2.Scan.Images.Storage
|
||||
return Convert(storage, PreferredBackingStorageType, convertParams);
|
||||
}
|
||||
|
||||
public static IMemoryStorage ConvertToMemory(IStorage storage, StorageConvertParams convertParams)
|
||||
public static IImage ConvertToImage(IStorage storage, StorageConvertParams convertParams)
|
||||
{
|
||||
if (storage is IMemoryStorage memStorage)
|
||||
if (storage is IImage memStorage)
|
||||
{
|
||||
return memStorage;
|
||||
}
|
||||
return (IMemoryStorage)Convert(storage, PreferredMemoryStorageType, convertParams);
|
||||
return (IImage)Convert(storage, PreferredImageType, convertParams);
|
||||
}
|
||||
|
||||
public static TStorage Convert<TStorage>(IStorage storage)
|
||||
@ -130,8 +129,8 @@ namespace NAPS2.Scan.Images.Storage
|
||||
|
||||
static StorageManager()
|
||||
{
|
||||
RegisterConverters(new GdiFileConverter());
|
||||
RegisterTransformers(typeof(GdiStorage), new GdiTransformer());
|
||||
RegisterConverters(new GdiConverters());
|
||||
RegisterTransformers(typeof(GdiImage), new GdiTransformers());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,42 +17,42 @@ namespace NAPS2.Scan.Images
|
||||
private const double COVERAGE_THRESHOLD_MIN = 0.00;
|
||||
private const double COVERAGE_THRESHOLD_MAX = 0.01;
|
||||
|
||||
public bool IsBlank(IMemoryStorage bitmap, int whiteThresholdNorm, int coverageThresholdNorm)
|
||||
public bool IsBlank(IImage image, int whiteThresholdNorm, int coverageThresholdNorm)
|
||||
{
|
||||
if (bitmap.PixelFormat == StoragePixelFormat.BW1)
|
||||
if (image.PixelFormat == StoragePixelFormat.BW1)
|
||||
{
|
||||
// TODO: Make more generic
|
||||
if (!(bitmap is GdiStorage gdiStorage))
|
||||
if (!(image is GdiImage gdiImage))
|
||||
{
|
||||
throw new InvalidOperationException("Patch code detection only supported for GdiStorage");
|
||||
}
|
||||
using (var bitmap2 = BitmapHelper.CopyToBpp(gdiStorage.Bitmap, 8))
|
||||
using (var bitmap2 = BitmapHelper.CopyToBpp(gdiImage.Bitmap, 8))
|
||||
{
|
||||
return IsBlankRGB(new GdiStorage(bitmap2), whiteThresholdNorm, coverageThresholdNorm);
|
||||
return IsBlankRGB(new GdiImage(bitmap2), whiteThresholdNorm, coverageThresholdNorm);
|
||||
}
|
||||
}
|
||||
if (bitmap.PixelFormat != StoragePixelFormat.RGB24)
|
||||
if (image.PixelFormat != StoragePixelFormat.RGB24)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return IsBlankRGB(bitmap, whiteThresholdNorm, coverageThresholdNorm);
|
||||
return IsBlankRGB(image, whiteThresholdNorm, coverageThresholdNorm);
|
||||
}
|
||||
|
||||
private static bool IsBlankRGB(IMemoryStorage bitmap, int whiteThresholdNorm, int coverageThresholdNorm)
|
||||
private static bool IsBlankRGB(IImage image, int whiteThresholdNorm, int coverageThresholdNorm)
|
||||
{
|
||||
var whiteThreshold = (int)Math.Round(WHITE_THRESHOLD_MIN + (whiteThresholdNorm / 100.0) * (WHITE_THRESHOLD_MAX - WHITE_THRESHOLD_MIN));
|
||||
var coverageThreshold = COVERAGE_THRESHOLD_MIN + (coverageThresholdNorm / 100.0) * (COVERAGE_THRESHOLD_MAX - COVERAGE_THRESHOLD_MIN);
|
||||
|
||||
long totalPixels = bitmap.Width * bitmap.Height;
|
||||
long totalPixels = image.Width * image.Height;
|
||||
long matchPixels = 0;
|
||||
|
||||
var data = bitmap.Lock(out var scan0, out var stride);
|
||||
var bytes = new byte[stride * bitmap.Height];
|
||||
var data = image.Lock(out var scan0, out var stride);
|
||||
var bytes = new byte[stride * image.Height];
|
||||
Marshal.Copy(scan0, bytes, 0, bytes.Length);
|
||||
bitmap.Unlock(data);
|
||||
for (int x = 0; x < bitmap.Width; x++)
|
||||
image.Unlock(data);
|
||||
for (int x = 0; x < image.Width; x++)
|
||||
{
|
||||
for (int y = 0; y < bitmap.Height; y++)
|
||||
for (int y = 0; y < image.Height; y++)
|
||||
{
|
||||
int r = bytes[stride * y + x * 3];
|
||||
int g = bytes[stride * y + x * 3 + 1];
|
||||
@ -70,9 +70,9 @@ namespace NAPS2.Scan.Images
|
||||
return coverage < coverageThreshold;
|
||||
}
|
||||
|
||||
public bool ExcludePage(IMemoryStorage bitmap, ScanProfile scanProfile)
|
||||
public bool ExcludePage(IImage image, ScanProfile scanProfile)
|
||||
{
|
||||
return scanProfile.ExcludeBlankPages && IsBlank(bitmap, scanProfile.BlankPageWhiteThreshold, scanProfile.BlankPageCoverageThreshold);
|
||||
return scanProfile.ExcludeBlankPages && IsBlank(image, scanProfile.BlankPageWhiteThreshold, scanProfile.BlankPageCoverageThreshold);
|
||||
}
|
||||
}
|
||||
}
|
@ -21,9 +21,9 @@ namespace NAPS2.Scan.Images.Transforms
|
||||
return mod;
|
||||
}
|
||||
|
||||
public static RotationTransform Auto(IMemoryStorage bitmap)
|
||||
public static RotationTransform Auto(IImage image)
|
||||
{
|
||||
return new RotationTransform(-bitmap.GetSkewAngle());
|
||||
return new RotationTransform(-image.GetSkewAngle());
|
||||
}
|
||||
|
||||
private double angle;
|
||||
|
@ -13,15 +13,15 @@ namespace NAPS2.Scan
|
||||
/// </summary>
|
||||
public class PatchCodeDetector
|
||||
{
|
||||
public static PatchCode Detect(IMemoryStorage bitmap)
|
||||
public static PatchCode Detect(IImage image)
|
||||
{
|
||||
// TODO: Make more generic
|
||||
if (!(bitmap is GdiStorage gdiStorage))
|
||||
if (!(image is GdiImage gdiImage))
|
||||
{
|
||||
throw new InvalidOperationException("Patch code detection only supported for GdiStorage");
|
||||
}
|
||||
IBarcodeReader reader = new BarcodeReader();
|
||||
var barcodeResult = reader.Decode(gdiStorage.Bitmap);
|
||||
var barcodeResult = reader.Decode(gdiImage.Bitmap);
|
||||
if (barcodeResult != null)
|
||||
{
|
||||
switch (barcodeResult.Text)
|
||||
|
@ -254,7 +254,7 @@ namespace NAPS2.Scan.Sane
|
||||
return (null, true);
|
||||
}
|
||||
using (stream)
|
||||
using (var output = StorageManager.MemoryStorageFactory.Decode(stream, ".bmp"))
|
||||
using (var output = StorageManager.ImageFactory.Decode(stream, ".bmp"))
|
||||
using (var result = scannedImageHelper.PostProcessStep1(output, ScanProfile, false))
|
||||
{
|
||||
if (blankDetector.ExcludePage(result, ScanProfile))
|
||||
|
@ -75,7 +75,7 @@ namespace NAPS2.Scan.Stub
|
||||
g.FillRectangle(Brushes.LightGray, 0, 0, bitmap.Width, bitmap.Height);
|
||||
g.DrawString((_number++).ToString("G"), new Font("Times New Roman", 80), Brushes.Black, 0, 350);
|
||||
}
|
||||
var image = new ScannedImage(new GdiStorage(bitmap), ScanBitDepth.C24Bit, ScanProfile.MaxQuality, ScanProfile.Quality);
|
||||
var image = new ScannedImage(new GdiImage(bitmap), ScanBitDepth.C24Bit, ScanProfile.MaxQuality, ScanProfile.Quality);
|
||||
return image;
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,7 @@ namespace NAPS2.Scan.Twain.Legacy
|
||||
|
||||
using (Bitmap bmp = DibUtils.BitmapFromDib(img, out bitcount))
|
||||
{
|
||||
Bitmaps.Add(new ScannedImage(new GdiStorage(bmp), bitcount == 1 ? ScanBitDepth.BlackWhite : ScanBitDepth.C24Bit, settings.MaxQuality, settings.Quality));
|
||||
Bitmaps.Add(new ScannedImage(new GdiImage(bmp), bitcount == 1 ? ScanBitDepth.BlackWhite : ScanBitDepth.C24Bit, settings.MaxQuality, settings.Quality));
|
||||
}
|
||||
}
|
||||
form.Close();
|
||||
|
@ -148,7 +148,7 @@ namespace NAPS2.Scan.Twain
|
||||
pageNumber++;
|
||||
using (var output = twainImpl == TwainImpl.MemXfer
|
||||
? GetBitmapFromMemXFer(eventArgs.MemoryData, eventArgs.ImageInfo)
|
||||
: StorageManager.MemoryStorageFactory.Decode(eventArgs.GetNativeImageStream(), ".bmp"))
|
||||
: StorageManager.ImageFactory.Decode(eventArgs.GetNativeImageStream(), ".bmp"))
|
||||
{
|
||||
using (var result = scannedImageHelper.PostProcessStep1(output, scanProfile))
|
||||
{
|
||||
@ -319,13 +319,13 @@ namespace NAPS2.Scan.Twain
|
||||
}
|
||||
}
|
||||
|
||||
private static IMemoryStorage GetBitmapFromMemXFer(byte[] memoryData, TWImageInfo imageInfo)
|
||||
private static IImage GetBitmapFromMemXFer(byte[] memoryData, TWImageInfo imageInfo)
|
||||
{
|
||||
int bytesPerPixel = memoryData.Length / (imageInfo.ImageWidth * imageInfo.ImageLength);
|
||||
var pixelFormat = bytesPerPixel == 0 ? StoragePixelFormat.BW1: StoragePixelFormat.RGB24;
|
||||
int imageWidth = imageInfo.ImageWidth;
|
||||
int imageHeight = imageInfo.ImageLength;
|
||||
var bitmap = StorageManager.MemoryStorageFactory.FromDimensions(imageWidth, imageHeight, pixelFormat);
|
||||
var bitmap = StorageManager.ImageFactory.FromDimensions(imageWidth, imageHeight, pixelFormat);
|
||||
var data = bitmap.Lock(out var scan0, out var stride);
|
||||
try
|
||||
{
|
||||
|
@ -138,7 +138,7 @@ namespace NAPS2.Scan.Wia
|
||||
}
|
||||
}
|
||||
|
||||
private void ProduceImage(ScannedImageSource.Concrete source, IMemoryStorage output, ref int pageNumber)
|
||||
private void ProduceImage(ScannedImageSource.Concrete source, IImage output, ref int pageNumber)
|
||||
{
|
||||
using (var result = scannedImageHelper.PostProcessStep1(output, ScanProfile))
|
||||
{
|
||||
@ -181,7 +181,7 @@ namespace NAPS2.Scan.Wia
|
||||
{
|
||||
using (var stream = new FileStream(path, FileMode.Open))
|
||||
{
|
||||
foreach (var storage in StorageManager.MemoryStorageFactory.DecodeMultiple(stream, Path.GetExtension(path), out _))
|
||||
foreach (var storage in StorageManager.ImageFactory.DecodeMultiple(stream, Path.GetExtension(path), out _))
|
||||
{
|
||||
using (storage)
|
||||
{
|
||||
@ -229,7 +229,7 @@ namespace NAPS2.Scan.Wia
|
||||
try
|
||||
{
|
||||
using (args.Stream)
|
||||
using (var storage = StorageManager.MemoryStorageFactory.Decode(args.Stream, ".bmp"))
|
||||
using (var storage = StorageManager.ImageFactory.Decode(args.Stream, ".bmp"))
|
||||
{
|
||||
ProduceImage(source, storage, ref pageNumber);
|
||||
}
|
||||
|
@ -1703,7 +1703,7 @@ namespace NAPS2.WinForms
|
||||
}
|
||||
if (includeBitmap)
|
||||
{
|
||||
using (var firstBitmap = ((GdiStorage)await scannedImageRenderer.Render(imageList[0])).Bitmap)
|
||||
using (var firstBitmap = ((GdiImage)await scannedImageRenderer.Render(imageList[0])).Bitmap)
|
||||
{
|
||||
ido.SetData(DataFormats.Bitmap, true, new Bitmap(firstBitmap));
|
||||
ido.SetData(DataFormats.Rtf, true, await RtfEncodeImages(firstBitmap, imageList));
|
||||
@ -1724,7 +1724,7 @@ namespace NAPS2.WinForms
|
||||
}
|
||||
foreach (var img in images.Skip(1))
|
||||
{
|
||||
using (var bitmap = ((GdiStorage)await scannedImageRenderer.Render(img)).Bitmap)
|
||||
using (var bitmap = ((GdiImage)await scannedImageRenderer.Render(img)).Bitmap)
|
||||
{
|
||||
// TODO: Is this the right format?
|
||||
if (!AppendRtfEncodedImage(bitmap, bitmap.RawFormat, sb, true))
|
||||
@ -1860,7 +1860,7 @@ namespace NAPS2.WinForms
|
||||
using (var snapshot = next.Preserve())
|
||||
{
|
||||
var thumb = worker != null
|
||||
? StorageManager.MemoryStorageFactory.Decode(new MemoryStream(worker.Service.RenderThumbnail(snapshot, thumbnailList1.ThumbnailSize.Height)), ".jpg")
|
||||
? StorageManager.ImageFactory.Decode(new MemoryStream(worker.Service.RenderThumbnail(snapshot, thumbnailList1.ThumbnailSize.Height)), ".jpg")
|
||||
: scannedImageRenderer.Render(snapshot, thumbnailList1.ThumbnailSize.Height).Result;
|
||||
|
||||
if (!ThumbnailStillNeedsRendering(next))
|
||||
|
@ -110,7 +110,7 @@ namespace NAPS2.WinForms
|
||||
tiffViewer1.Image?.Dispose();
|
||||
tiffViewer1.Image = null;
|
||||
var newImage = await scannedImageRenderer.Render(ImageList.Images[ImageIndex]);
|
||||
tiffViewer1.Image = ((GdiStorage)newImage).Bitmap;
|
||||
tiffViewer1.Image = ((GdiImage)newImage).Bitmap;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
|
@ -56,7 +56,7 @@ namespace NAPS2.WinForms
|
||||
if (!transform.IsNull)
|
||||
{
|
||||
// TODO: Maybe the working images etc. should be storage
|
||||
result = ((GdiStorage)StorageManager.PerformTransform(new GdiStorage(result), transform)).Bitmap;
|
||||
result = ((GdiImage)StorageManager.PerformTransform(new GdiImage(result), transform)).Bitmap;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@ -92,7 +92,7 @@ namespace NAPS2.WinForms
|
||||
Size = new Size(600, 600);
|
||||
|
||||
var maxDimen = Screen.AllScreens.Max(s => Math.Max(s.WorkingArea.Height, s.WorkingArea.Width));
|
||||
workingImage = ((GdiStorage)await scannedImageRenderer.Render(Image, maxDimen * 2)).Bitmap;
|
||||
workingImage = ((GdiImage)await scannedImageRenderer.Render(Image, maxDimen * 2)).Bitmap;
|
||||
if (closed)
|
||||
{
|
||||
workingImage?.Dispose();
|
||||
|
@ -196,7 +196,7 @@ namespace NAPS2.WinForms
|
||||
{
|
||||
lock (this)
|
||||
{
|
||||
var thumb = ((GdiStorage)img.GetThumbnail()).Bitmap;
|
||||
var thumb = ((GdiImage)img.GetThumbnail()).Bitmap;
|
||||
if (thumb == null)
|
||||
{
|
||||
return RenderPlaceholder();
|
||||
|
Loading…
Reference in New Issue
Block a user