Rename IMemoryStorage to IImage

This commit is contained in:
Ben Olden-Cooligan 2018-11-29 13:43:48 -05:00
parent d509d39de2
commit 676d2ae9da
34 changed files with 223 additions and 267 deletions

View File

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

View File

@ -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)
{

View File

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

View File

@ -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));

View File

@ -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);

View File

@ -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
{

View File

@ -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" />

View File

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

View File

@ -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:

View File

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

View File

@ -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());

View File

@ -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()));
}
}
}

View File

@ -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);

View File

@ -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?

View File

@ -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());
}
}
}

View File

@ -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)
{

View File

@ -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>

View File

@ -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();
}
}

View File

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

View File

@ -22,7 +22,7 @@ namespace NAPS2.Scan.Images.Storage
public RecoveryStorageManager(string recoveryFolderPath)
{
this.RecoveryFolderPath = recoveryFolderPath;
RecoveryFolderPath = recoveryFolderPath;
}
public string RecoveryFolderPath { get; }

View File

@ -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; }
}
}

View File

@ -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());
}
}
}

View File

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

View File

@ -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;

View File

@ -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)

View File

@ -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))

View File

@ -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;
}

View File

@ -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();

View File

@ -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
{

View File

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

View File

@ -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))

View File

@ -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)

View File

@ -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();

View File

@ -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();