mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-11 15:26:55 +03:00
Mostly fix image serialization
This commit is contained in:
parent
a59019eb87
commit
deb5b0335b
@ -27,7 +27,7 @@ namespace NAPS2.Images
|
||||
double scaleFactor = Math.Min(outputSize / (double)storage.Height, outputSize / (double)storage.Width);
|
||||
storage = Transform.Perform(storage, new ScaleTransform(scaleFactor));
|
||||
}
|
||||
return Transform.PerformAll(storage, snapshot.TransformList);
|
||||
return Transform.PerformAll(storage, snapshot.Metadata.TransformList);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using NAPS2.Images.Storage;
|
||||
using NAPS2.Images.Transforms;
|
||||
using NAPS2.Recovery;
|
||||
using NAPS2.Scan;
|
||||
|
||||
namespace NAPS2.Images
|
||||
@ -14,7 +11,6 @@ namespace NAPS2.Images
|
||||
{
|
||||
private IImage thumbnail;
|
||||
private int thumbnailState;
|
||||
private int transformState;
|
||||
|
||||
private bool disposed;
|
||||
private int snapshotCount;
|
||||
@ -36,6 +32,14 @@ namespace NAPS2.Images
|
||||
Metadata = metadata;
|
||||
}
|
||||
|
||||
public ScannedImage(IStorage storage, string serializedMetadata, StorageConvertParams convertParams)
|
||||
{
|
||||
BackingStorage = StorageManager.ConvertToBacking(storage, convertParams);
|
||||
Metadata = StorageManager.ImageMetadataFactory.CreateMetadata(BackingStorage);
|
||||
Metadata.Deserialize(serializedMetadata);
|
||||
Metadata.Commit();
|
||||
}
|
||||
|
||||
public ScannedImage(IStorage storage, ScanBitDepth bitDepth, bool highQuality, int quality)
|
||||
{
|
||||
var convertParams = new StorageConvertParams { Lossless = highQuality, LossyQuality = quality };
|
||||
@ -83,7 +87,7 @@ namespace NAPS2.Images
|
||||
{
|
||||
// Also updates the recovery index since they reference the same list
|
||||
Transform.AddOrSimplify(Metadata.TransformList, transform);
|
||||
transformState++;
|
||||
Metadata.TransformState++;
|
||||
}
|
||||
Metadata.Commit();
|
||||
ThumbnailInvalidated?.Invoke(this, new EventArgs());
|
||||
@ -98,7 +102,7 @@ namespace NAPS2.Images
|
||||
return;
|
||||
}
|
||||
Metadata.TransformList.Clear();
|
||||
transformState++;
|
||||
Metadata.TransformState++;
|
||||
}
|
||||
Metadata.Commit();
|
||||
ThumbnailInvalidated?.Invoke(this, new EventArgs());
|
||||
@ -118,12 +122,12 @@ namespace NAPS2.Images
|
||||
{
|
||||
thumbnail?.Dispose();
|
||||
thumbnail = image;
|
||||
thumbnailState = state ?? transformState;
|
||||
thumbnailState = state ?? Metadata.TransformState;
|
||||
}
|
||||
ThumbnailChanged?.Invoke(this, new EventArgs());
|
||||
}
|
||||
|
||||
public bool IsThumbnailDirty => thumbnailState != transformState;
|
||||
public bool IsThumbnailDirty => thumbnailState != Metadata.TransformState;
|
||||
|
||||
public EventHandler ThumbnailChanged;
|
||||
|
||||
@ -139,9 +143,7 @@ namespace NAPS2.Images
|
||||
|
||||
public Snapshot Preserve() => new Snapshot(this);
|
||||
|
||||
[Serializable]
|
||||
[KnownType("KnownTypes")]
|
||||
public class Snapshot : IDisposable, ISerializable
|
||||
public class Snapshot : IDisposable
|
||||
{
|
||||
private bool disposed;
|
||||
|
||||
@ -155,16 +157,13 @@ namespace NAPS2.Images
|
||||
}
|
||||
source.snapshotCount++;
|
||||
Source = source;
|
||||
TransformList = source.Metadata.TransformList.ToList();
|
||||
TransformState = source.transformState;
|
||||
Metadata = source.Metadata.Clone();
|
||||
}
|
||||
}
|
||||
|
||||
public ScannedImage Source { get; }
|
||||
|
||||
public List<Transform> TransformList { get; }
|
||||
|
||||
public int TransformState { get; }
|
||||
public IImageMetadata Metadata { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
@ -179,29 +178,6 @@ namespace NAPS2.Images
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
// TODO
|
||||
//info.AddValue("RecoveryIndexImage", Source.RecoveryIndexImage);
|
||||
info.AddValue("TransformList", TransformList);
|
||||
info.AddValue("TransformState", TransformState);
|
||||
}
|
||||
|
||||
private Snapshot(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
// TODO
|
||||
//Source = new ScannedImage((RecoveryIndexImage)info.GetValue("RecoveryIndexImage", typeof(RecoveryIndexImage)));
|
||||
TransformList = (List<Transform>)info.GetValue("TransformList", typeof(List<Transform>));
|
||||
TransformState = (int)info.GetValue("TransformState", typeof(int));
|
||||
}
|
||||
|
||||
// ReSharper disable once UnusedMember.Local
|
||||
private static Type[] KnownTypes()
|
||||
{
|
||||
var transformTypes = Assembly.GetExecutingAssembly().GetTypes().Where(x => x.IsSubclassOf(typeof(Transform)));
|
||||
return transformTypes.Concat(new[] { typeof(List<Transform>), typeof(RecoveryIndexImage) }).ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,21 +7,31 @@ namespace NAPS2.Images.Storage
|
||||
{
|
||||
public class FileStorage : IStorage
|
||||
{
|
||||
public FileStorage(string fullPath)
|
||||
private readonly bool shared;
|
||||
|
||||
public FileStorage(string fullPath) : this(fullPath, false)
|
||||
{
|
||||
}
|
||||
|
||||
public FileStorage(string fullPath, bool shared)
|
||||
{
|
||||
FullPath = fullPath ?? throw new ArgumentNullException(nameof(fullPath));
|
||||
this.shared = shared;
|
||||
}
|
||||
|
||||
public string FullPath { get; }
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(FullPath);
|
||||
}
|
||||
catch (IOException)
|
||||
if (!shared)
|
||||
{
|
||||
try
|
||||
{
|
||||
File.Delete(FullPath);
|
||||
}
|
||||
catch (IOException)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ namespace NAPS2.Images.Storage
|
||||
{
|
||||
List<Transform> TransformList { get; set; }
|
||||
|
||||
int TransformState { get; set; }
|
||||
|
||||
int Index { get; set; }
|
||||
|
||||
ScanBitDepth BitDepth { get; set; }
|
||||
@ -23,5 +25,7 @@ namespace NAPS2.Images.Storage
|
||||
string Serialize();
|
||||
|
||||
void Deserialize(string serializedData);
|
||||
|
||||
IImageMetadata Clone();
|
||||
}
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ namespace NAPS2.Images.Storage
|
||||
public class OwnershipConverters
|
||||
{
|
||||
[StorageConverter]
|
||||
public FileStorage ConvertToFile(UnownedTransferStorage input, StorageConvertParams convertParams)
|
||||
public FileStorage ConvertToFile(UnownedFileStorage input, StorageConvertParams convertParams)
|
||||
{
|
||||
string newPath = FileStorageManager.Current.NextFilePath();
|
||||
File.Copy(input.FilePath, newPath);
|
||||
|
@ -27,6 +27,8 @@ namespace NAPS2.Images.Storage
|
||||
set => indexImage.TransformList = value;
|
||||
}
|
||||
|
||||
public int TransformState { get; set; }
|
||||
|
||||
public int Index
|
||||
{
|
||||
get => rsm.Index.Images.IndexOf(indexImage);
|
||||
@ -61,6 +63,16 @@ namespace NAPS2.Images.Storage
|
||||
|
||||
public void Deserialize(string serializedData) => indexImage = serializedData.FromXml<RecoveryIndexImage>();
|
||||
|
||||
public IImageMetadata Clone() =>
|
||||
new StubImageMetadata
|
||||
{
|
||||
TransformList = TransformList.ToList(),
|
||||
TransformState = TransformState,
|
||||
Index = Index,
|
||||
BitDepth = BitDepth,
|
||||
Lossless = Lossless
|
||||
};
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
rsm.Index.Images.Remove(indexImage);
|
||||
|
@ -11,6 +11,8 @@ namespace NAPS2.Images.Storage
|
||||
{
|
||||
public List<Transform> TransformList { get; set; } = new List<Transform>();
|
||||
|
||||
public int TransformState { get; set; }
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public ScanBitDepth BitDepth { get; set; }
|
||||
@ -37,6 +39,16 @@ namespace NAPS2.Images.Storage
|
||||
Lossless = other.Lossless;
|
||||
}
|
||||
|
||||
public IImageMetadata Clone() =>
|
||||
new StubImageMetadata
|
||||
{
|
||||
TransformList = TransformList.ToList(),
|
||||
TransformState = TransformState,
|
||||
Index = Index,
|
||||
BitDepth = BitDepth,
|
||||
Lossless = Lossless
|
||||
};
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
@ -8,14 +8,14 @@ namespace NAPS2.Images.Storage
|
||||
/// Represents an image received across the wire where we must copy the backing
|
||||
/// file before it can be used.
|
||||
/// </summary>
|
||||
public class UnownedTransferStorage : IStorage
|
||||
public class UnownedFileStorage : IStorage
|
||||
{
|
||||
static UnownedTransferStorage()
|
||||
static UnownedFileStorage()
|
||||
{
|
||||
StorageManager.RegisterConverters(new OwnershipConverters());
|
||||
}
|
||||
|
||||
public UnownedTransferStorage(string filePath)
|
||||
public UnownedFileStorage(string filePath)
|
||||
{
|
||||
FilePath = filePath ?? throw new ArgumentNullException(nameof(filePath));
|
||||
}
|
@ -78,7 +78,7 @@ namespace NAPS2.Images
|
||||
{
|
||||
outputSize = UserConfig.Current.ThumbnailSize;
|
||||
}
|
||||
using (var bitmap = await imageRenderer.Render(snapshot, snapshot.TransformList.Count == 0 ? 0 : outputSize * OVERSAMPLE))
|
||||
using (var bitmap = await imageRenderer.Render(snapshot, snapshot.Metadata.TransformList.Count == 0 ? 0 : outputSize * OVERSAMPLE))
|
||||
{
|
||||
return Transform.Perform(bitmap, new ThumbnailTransform(outputSize));
|
||||
}
|
||||
|
@ -2,29 +2,31 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using NAPS2.Recovery;
|
||||
using Google.Protobuf;
|
||||
using NAPS2.Images;
|
||||
using NAPS2.Serialization;
|
||||
|
||||
namespace NAPS2.ImportExport
|
||||
{
|
||||
[Serializable]
|
||||
public class DirectImageTransfer
|
||||
{
|
||||
public DirectImageTransfer(IEnumerable<ScannedImage> selectedImages)
|
||||
public DirectImageTransfer(IEnumerable<ScannedImage> images)
|
||||
{
|
||||
ProcessID = Process.GetCurrentProcess().Id;
|
||||
// TODO
|
||||
//ImageRecovery = selectedImages.Select(x => x.RecoveryIndexImage).ToArray();
|
||||
//if (ImageRecovery.Length > 0)
|
||||
//{
|
||||
// RecoveryFolder = RecoveryImage.RecoveryFolder.FullName;
|
||||
//}
|
||||
var serializedImages = images.Select(x => SerializedImageHelper.Serialize(x, new SerializedImageHelper.SerializeOptions()));
|
||||
SerializedImages = serializedImages.Select(x => x.ToByteArray()).ToList();
|
||||
}
|
||||
|
||||
public DirectImageTransfer(IEnumerable<ScannedImage.Snapshot> snapshots)
|
||||
{
|
||||
ProcessID = Process.GetCurrentProcess().Id;
|
||||
var serializedImages = snapshots.Select(x => SerializedImageHelper.Serialize(x, new SerializedImageHelper.SerializeOptions()));
|
||||
SerializedImages = serializedImages.Select(x => x.ToByteArray()).ToList();
|
||||
}
|
||||
|
||||
public int ProcessID { get; }
|
||||
|
||||
public RecoveryIndexImage[] ImageRecovery { get; }
|
||||
|
||||
public string RecoveryFolder { get; }
|
||||
public List<byte[]> SerializedImages { get; }
|
||||
}
|
||||
}
|
||||
|
@ -2,12 +2,14 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Google.Protobuf;
|
||||
using NAPS2.Lang.Resources;
|
||||
using NAPS2.Logging;
|
||||
using NAPS2.Operation;
|
||||
using NAPS2.Images;
|
||||
using NAPS2.Images.Storage;
|
||||
using NAPS2.Images.Transforms;
|
||||
using NAPS2.Serialization;
|
||||
|
||||
namespace NAPS2.ImportExport
|
||||
{
|
||||
@ -29,25 +31,19 @@ namespace NAPS2.ImportExport
|
||||
Status = new OperationStatus
|
||||
{
|
||||
StatusText = copy ? MiscResources.Copying : MiscResources.Importing,
|
||||
MaxProgress = data.ImageRecovery.Length
|
||||
MaxProgress = data.SerializedImages.Count
|
||||
};
|
||||
|
||||
RunAsync(async () =>
|
||||
{
|
||||
Exception error = null;
|
||||
foreach (var ir in data.ImageRecovery)
|
||||
foreach (var serializedImageBytes in data.SerializedImages)
|
||||
{
|
||||
try
|
||||
{
|
||||
ScannedImage img;
|
||||
using (var storage = StorageManager.ConvertToImage(new FileStorage(Path.Combine(data.RecoveryFolder, ir.FileName)), new StorageConvertParams()))
|
||||
{
|
||||
img = new ScannedImage(storage, ir.BitDepth, ir.HighQuality, -1);
|
||||
}
|
||||
foreach (var transform in ir.TransformList)
|
||||
{
|
||||
img.AddTransform(transform);
|
||||
}
|
||||
var serializedImage = new SerializedImage();
|
||||
serializedImage.MergeFrom(serializedImageBytes);
|
||||
ScannedImage img = SerializedImageHelper.Deserialize(serializedImage, new SerializedImageHelper.DeserializeOptions());
|
||||
// TODO: Don't bother, here, in recovery, etc.
|
||||
img.SetThumbnail(Transform.Perform(await imageRenderer.Render(img), new ThumbnailTransform()));
|
||||
imageCallback(img);
|
||||
@ -66,7 +62,7 @@ namespace NAPS2.ImportExport
|
||||
}
|
||||
if (error != null)
|
||||
{
|
||||
Log.ErrorException(string.Format(MiscResources.ImportErrorCouldNot, data.RecoveryFolder), error);
|
||||
Log.ErrorException(string.Format(MiscResources.ImportErrorCouldNot, "<data>"), error);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
@ -138,7 +138,7 @@ namespace NAPS2.ImportExport.Pdf
|
||||
progressCallback(progress, snapshots.Count);
|
||||
foreach (var snapshot in snapshots)
|
||||
{
|
||||
if (snapshot.Source.BackingStorage is FileStorage fileStorage && IsPdfFile(fileStorage) && !snapshot.TransformList.Any())
|
||||
if (snapshot.Source.BackingStorage is FileStorage fileStorage && IsPdfFile(fileStorage) && !snapshot.Metadata.TransformList.Any())
|
||||
{
|
||||
CopyPdfPageToDoc(document, fileStorage);
|
||||
}
|
||||
@ -182,7 +182,7 @@ namespace NAPS2.ImportExport.Pdf
|
||||
PdfPage page;
|
||||
bool importedPdfPassThrough = false;
|
||||
|
||||
if (snapshot.Source.BackingStorage is FileStorage fileStorage && IsPdfFile(fileStorage) && !snapshot.TransformList.Any())
|
||||
if (snapshot.Source.BackingStorage is FileStorage fileStorage && IsPdfFile(fileStorage) && !snapshot.Metadata.TransformList.Any())
|
||||
{
|
||||
importedPdfPassThrough = true;
|
||||
page = CopyPdfPageToDoc(document, fileStorage);
|
||||
|
@ -193,7 +193,7 @@
|
||||
<Compile Include="Images\ScannedImageSink.cs" />
|
||||
<Compile Include="Images\Storage\OwnershipConverters.cs" />
|
||||
<Compile Include="Images\Storage\PdfConverters.cs" />
|
||||
<Compile Include="Images\Storage\UnownedTransferStorage.cs" />
|
||||
<Compile Include="Images\Storage\UnownedFileStorage.cs" />
|
||||
<Compile Include="Images\Storage\PdfStorage.cs" />
|
||||
<Compile Include="ImportExport\Email\Mapi\IMapiWrapper.cs" />
|
||||
<Compile Include="ImportExport\Email\Mapi\MapiWrapper.cs" />
|
||||
@ -287,6 +287,7 @@
|
||||
<Compile Include="Scan\Wia\WiaConfiguration.cs" />
|
||||
<Compile Include="Scan\Wia\WiaScanErrors.cs" />
|
||||
<Compile Include="Scan\Wia\WiaScanOperation.cs" />
|
||||
<Compile Include="Serialization\SerializedImageHelper.cs" />
|
||||
<Compile Include="Update\UpdateOperation.cs" />
|
||||
<Compile Include="Update\UpdateInfo.cs" />
|
||||
<Compile Include="Update\UpdateChecker.cs" />
|
||||
@ -297,6 +298,7 @@
|
||||
<Compile Include="Platform\IRuntimeCompat.cs" />
|
||||
<Compile Include="Platform\MonoRuntimeCompat.cs" />
|
||||
<Compile Include="Util\ExpFallback.cs" />
|
||||
<Compile Include="WinForms\ImageClipboard.cs" />
|
||||
<Compile Include="Worker\GrpcHelper.cs" />
|
||||
<Compile Include="Util\IInvoker.cs" />
|
||||
<Compile Include="Util\ImageExtensions.cs" />
|
||||
@ -6203,6 +6205,7 @@
|
||||
<None Include="naps2-public.cer" />
|
||||
<None Include="Icons\accept-small.png" />
|
||||
<None Include="packages.config" />
|
||||
<ProtoBuf Include="Serialization\common.proto" />
|
||||
<Protobuf Include="Worker\worker.proto" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -10,7 +10,7 @@ namespace NAPS2.Ocr
|
||||
public OcrRequestParams(ScannedImage.Snapshot snapshot, IOcrEngine ocrEngine, OcrParams ocrParams)
|
||||
{
|
||||
ScannedImage = snapshot.Source;
|
||||
TransformState = snapshot.TransformList.Count == 0 ? -1 : snapshot.TransformState;
|
||||
TransformState = snapshot.Metadata.TransformList.Count == 0 ? -1 : snapshot.Metadata.TransformState;
|
||||
Engine = ocrEngine;
|
||||
OcrParams = ocrParams;
|
||||
}
|
||||
|
112
NAPS2.Sdk/Serialization/SerializedImageHelper.cs
Normal file
112
NAPS2.Sdk/Serialization/SerializedImageHelper.cs
Normal file
@ -0,0 +1,112 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Google.Protobuf;
|
||||
using NAPS2.Images;
|
||||
using NAPS2.Images.Storage;
|
||||
|
||||
namespace NAPS2.Serialization
|
||||
{
|
||||
public static class SerializedImageHelper
|
||||
{
|
||||
public static SerializedImage Serialize(ScannedImage image, SerializeOptions options) =>
|
||||
Serialize(image, image.Metadata, options);
|
||||
|
||||
public static SerializedImage Serialize(ScannedImage.Snapshot snapshot, SerializeOptions options) =>
|
||||
Serialize(snapshot.Source, snapshot.Metadata, options);
|
||||
|
||||
private static SerializedImage Serialize(ScannedImage image, IImageMetadata metadata, SerializeOptions options)
|
||||
{
|
||||
MemoryStream thumbStream = null;
|
||||
var thumb = image.GetThumbnail();
|
||||
if (thumb != null && options.IncludeThumbnail)
|
||||
{
|
||||
thumbStream = StorageManager.Convert<MemoryStreamStorage>(thumb, new StorageConvertParams { Lossless = true }).Stream;
|
||||
}
|
||||
|
||||
var fileStorage = image.BackingStorage as FileStorage;
|
||||
if (fileStorage == null && options.RequireFileStorage)
|
||||
{
|
||||
throw new InvalidOperationException("FileStorage is required for serialization.");
|
||||
}
|
||||
|
||||
MemoryStream imageStream = null;
|
||||
if (fileStorage == null)
|
||||
{
|
||||
imageStream = StorageManager.Convert<MemoryStreamStorage>(image.BackingStorage, new StorageConvertParams()).Stream;
|
||||
}
|
||||
|
||||
var result = new SerializedImage
|
||||
{
|
||||
TransferOwnership = options.TransferOwnership,
|
||||
MetadataXml = image.Metadata.Serialize(),
|
||||
Thumbnail = thumbStream != null ? ByteString.FromStream(thumbStream) : ByteString.Empty,
|
||||
RenderedFilePath = options.RenderedFilePath
|
||||
};
|
||||
if (fileStorage != null)
|
||||
{
|
||||
result.FilePath = fileStorage.FullPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.FileContent = ByteString.FromStream(imageStream);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ScannedImage Deserialize(SerializedImage serializedImage, DeserializeOptions options)
|
||||
{
|
||||
IStorage storage;
|
||||
if (!string.IsNullOrEmpty(serializedImage.FilePath))
|
||||
{
|
||||
if (serializedImage.TransferOwnership)
|
||||
{
|
||||
storage = new FileStorage(serializedImage.FilePath);
|
||||
}
|
||||
else if (options.ShareFileStorage)
|
||||
{
|
||||
storage = new FileStorage(serializedImage.FilePath, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: With this logic centralized, maybe we can remove UnownedFileStorage and just move the file copy logic here?
|
||||
storage = new UnownedFileStorage(serializedImage.FilePath);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var memoryStream = new MemoryStream(serializedImage.FileContent.ToByteArray());
|
||||
storage = new MemoryStreamStorage(memoryStream);
|
||||
}
|
||||
var scannedImage = new ScannedImage(storage, serializedImage.MetadataXml, new StorageConvertParams());
|
||||
var thumbnail = serializedImage.Thumbnail.ToByteArray();
|
||||
if (thumbnail.Length > 0)
|
||||
{
|
||||
var thumbnailStorage = new MemoryStreamStorage(new MemoryStream(thumbnail));
|
||||
scannedImage.SetThumbnail(StorageManager.ConvertToImage(thumbnailStorage, new StorageConvertParams()));
|
||||
}
|
||||
return scannedImage;
|
||||
}
|
||||
|
||||
public class SerializeOptions
|
||||
{
|
||||
public bool TransferOwnership { get; set; }
|
||||
|
||||
public bool IncludeThumbnail { get; set; }
|
||||
|
||||
public bool RequireFileStorage { get; set; }
|
||||
|
||||
public string RenderedFilePath { get; set; }
|
||||
}
|
||||
|
||||
public class DeserializeOptions
|
||||
{
|
||||
/// <summary>
|
||||
/// If true, the deserializer guarantees that the file storage will not be used for longer than the duration of the RPC call.
|
||||
/// In this way, files can be reused even if ownership isn't transferred to the callee.
|
||||
/// </summary>
|
||||
public bool ShareFileStorage { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
20
NAPS2.Sdk/Serialization/common.proto
Normal file
20
NAPS2.Sdk/Serialization/common.proto
Normal file
@ -0,0 +1,20 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package NAPS2.Serialization;
|
||||
|
||||
message Error {
|
||||
string type = 1;
|
||||
string message = 2;
|
||||
string stackTrace = 3;
|
||||
}
|
||||
|
||||
message SerializedImage {
|
||||
bool transferOwnership = 1;
|
||||
oneof image {
|
||||
string filePath = 2;
|
||||
bytes fileContent = 3;
|
||||
}
|
||||
string metadataXml = 4;
|
||||
bytes thumbnail = 5;
|
||||
string renderedFilePath = 6;
|
||||
}
|
@ -1885,7 +1885,7 @@ namespace NAPS2.WinForms
|
||||
continue;
|
||||
}
|
||||
|
||||
next.SetThumbnail(thumb, snapshot.TransformState);
|
||||
next.SetThumbnail(thumb, snapshot.Metadata.TransformState);
|
||||
}
|
||||
fallback.Reset();
|
||||
}
|
||||
|
22
NAPS2.Sdk/WinForms/ImageClipboard.cs
Normal file
22
NAPS2.Sdk/WinForms/ImageClipboard.cs
Normal file
@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NAPS2.Images;
|
||||
|
||||
namespace NAPS2.WinForms
|
||||
{
|
||||
public class ImageClipboard
|
||||
{
|
||||
private readonly BitmapRenderer bitmapRenderer;
|
||||
|
||||
public ImageClipboard()
|
||||
{
|
||||
bitmapRenderer = new BitmapRenderer();
|
||||
}
|
||||
|
||||
public ImageClipboard(BitmapRenderer bitmapRenderer)
|
||||
{
|
||||
this.bitmapRenderer = bitmapRenderer;
|
||||
}
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using Grpc.Core;
|
||||
using NAPS2.Scan.Exceptions;
|
||||
using NAPS2.Serialization;
|
||||
using NAPS2.Util;
|
||||
|
||||
namespace NAPS2.Worker
|
||||
|
@ -1,16 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Grpc.Core;
|
||||
using NAPS2.Images;
|
||||
using NAPS2.Images.Storage;
|
||||
using NAPS2.ImportExport.Email;
|
||||
using NAPS2.ImportExport.Email.Mapi;
|
||||
using NAPS2.Scan;
|
||||
using NAPS2.Scan.Wia;
|
||||
using NAPS2.Serialization;
|
||||
using NAPS2.Util;
|
||||
|
||||
namespace NAPS2.Worker
|
||||
@ -65,17 +64,8 @@ namespace NAPS2.Worker
|
||||
{
|
||||
var resp = streamingCall.ResponseStream.Current;
|
||||
GrpcHelper.HandleErrors(resp.Error);
|
||||
var storage = new FileStorage(resp.FilePath);
|
||||
var metadata = StorageManager.ImageMetadataFactory.CreateMetadata(storage);
|
||||
metadata.Deserialize(resp.MetadataXml);
|
||||
var scannedImage = new ScannedImage(storage, metadata, new StorageConvertParams());
|
||||
var thumbnail = resp.Thumbnail.ToByteArray();
|
||||
if (thumbnail.Length > 0)
|
||||
{
|
||||
var thumbnailStorage = new MemoryStreamStorage(new MemoryStream(thumbnail));
|
||||
scannedImage.SetThumbnail(StorageManager.ConvertToImage(thumbnailStorage, new StorageConvertParams()));
|
||||
}
|
||||
imageCallback?.Invoke(scannedImage, resp.RenderedFilePath);
|
||||
var scannedImage = SerializedImageHelper.Deserialize(resp.Image, new SerializedImageHelper.DeserializeOptions());
|
||||
imageCallback?.Invoke(scannedImage, resp.Image.RenderedFilePath);
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +81,10 @@ namespace NAPS2.Worker
|
||||
{
|
||||
var req = new RenderThumbnailRequest
|
||||
{
|
||||
SnapshotXml = snapshot.ToXml(),
|
||||
Image = SerializedImageHelper.Serialize(snapshot, new SerializedImageHelper.SerializeOptions
|
||||
{
|
||||
RequireFileStorage = true
|
||||
}),
|
||||
Size = size
|
||||
};
|
||||
var resp = client.RenderThumbnail(req);
|
||||
|
@ -14,6 +14,7 @@ using NAPS2.Scan;
|
||||
using NAPS2.Scan.Twain;
|
||||
using NAPS2.Scan.Wia;
|
||||
using NAPS2.Scan.Wia.Native;
|
||||
using NAPS2.Serialization;
|
||||
using NAPS2.Util;
|
||||
|
||||
namespace NAPS2.Worker
|
||||
@ -109,12 +110,19 @@ namespace NAPS2.Worker
|
||||
GrpcHelper.WrapFunc(
|
||||
async () =>
|
||||
{
|
||||
var thumbnail = await thumbnailRenderer.Render(request.SnapshotXml.FromXml<ScannedImage.Snapshot>(), request.Size);
|
||||
var stream = StorageManager.Convert<MemoryStreamStorage>(thumbnail, new StorageConvertParams { Lossless = true }).Stream;
|
||||
return new RenderThumbnailResponse
|
||||
var deserializeOptions = new SerializedImageHelper.DeserializeOptions
|
||||
{
|
||||
Thumbnail = ByteString.FromStream(stream)
|
||||
ShareFileStorage = true
|
||||
};
|
||||
using (var image = SerializedImageHelper.Deserialize(request.Image, deserializeOptions))
|
||||
{
|
||||
var thumbnail = await thumbnailRenderer.Render(image, request.Size);
|
||||
var stream = StorageManager.Convert<MemoryStreamStorage>(thumbnail, new StorageConvertParams { Lossless = true }).Stream;
|
||||
return new RenderThumbnailResponse
|
||||
{
|
||||
Thumbnail = ByteString.FromStream(stream)
|
||||
};
|
||||
}
|
||||
},
|
||||
err => new RenderThumbnailResponse { Error = err });
|
||||
|
||||
@ -132,22 +140,14 @@ namespace NAPS2.Worker
|
||||
public override void PutImage(ScannedImage image)
|
||||
{
|
||||
// TODO: Ideally this shouldn't be inheriting ScannedImageSink, some other cleaner mechanism
|
||||
MemoryStream stream = null;
|
||||
var thumb = image.GetThumbnail();
|
||||
if (thumb != null)
|
||||
{
|
||||
stream = StorageManager.Convert<MemoryStreamStorage>(thumb, new StorageConvertParams { Lossless = true }).Stream;
|
||||
}
|
||||
if (!(image.BackingStorage is FileStorage fileStorage))
|
||||
{
|
||||
throw new InvalidOperationException("The worker can only be used with IFileStorage backing storage.");
|
||||
}
|
||||
callback.WriteAsync(new TwainScanResponse
|
||||
{
|
||||
FilePath = fileStorage.FullPath,
|
||||
MetadataXml = image.Metadata.Serialize(),
|
||||
Thumbnail = stream != null ? ByteString.FromStream(stream) : ByteString.Empty,
|
||||
RenderedFilePath = imagePathDict.Get(image) ?? ""
|
||||
Image = SerializedImageHelper.Serialize(image, new SerializedImageHelper.SerializeOptions
|
||||
{
|
||||
TransferOwnership = true,
|
||||
IncludeThumbnail = true,
|
||||
RenderedFilePath = imagePathDict.Get(image) ?? ""
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
package NAPS2.Worker;
|
||||
|
||||
import "Serialization/common.proto";
|
||||
|
||||
service GrpcWorkerService {
|
||||
rpc Init (InitRequest) returns (InitResponse) {}
|
||||
rpc Wia10NativeUi (Wia10NativeUiRequest) returns (Wia10NativeUiResponse) {}
|
||||
@ -11,18 +13,12 @@ service GrpcWorkerService {
|
||||
rpc RenderThumbnail (RenderThumbnailRequest) returns (RenderThumbnailResponse) {}
|
||||
}
|
||||
|
||||
message Error {
|
||||
string type = 1;
|
||||
string message = 2;
|
||||
string stackTrace = 3;
|
||||
}
|
||||
|
||||
message InitRequest {
|
||||
string recoveryFolderPath = 1;
|
||||
}
|
||||
|
||||
message InitResponse {
|
||||
Error error = 1;
|
||||
NAPS2.Serialization.Error error = 1;
|
||||
}
|
||||
|
||||
message Wia10NativeUiRequest {
|
||||
@ -31,7 +27,7 @@ message Wia10NativeUiRequest {
|
||||
}
|
||||
|
||||
message Wia10NativeUiResponse {
|
||||
Error error = 1;
|
||||
NAPS2.Serialization.Error error = 1;
|
||||
string wiaConfigurationXml = 2;
|
||||
}
|
||||
|
||||
@ -40,7 +36,7 @@ message TwainGetDeviceListRequest {
|
||||
}
|
||||
|
||||
message TwainGetDeviceListResponse {
|
||||
Error error = 1;
|
||||
NAPS2.Serialization.Error error = 1;
|
||||
string deviceListXml = 2;
|
||||
}
|
||||
|
||||
@ -52,11 +48,8 @@ message TwainScanRequest {
|
||||
}
|
||||
|
||||
message TwainScanResponse {
|
||||
Error error = 1;
|
||||
string filePath = 2;
|
||||
string metadataXml = 3;
|
||||
bytes thumbnail = 4;
|
||||
string renderedFilePath = 5;
|
||||
NAPS2.Serialization.Error error = 1;
|
||||
NAPS2.Serialization.SerializedImage image = 2;
|
||||
}
|
||||
|
||||
message SendMapiEmailRequest {
|
||||
@ -64,16 +57,16 @@ message SendMapiEmailRequest {
|
||||
}
|
||||
|
||||
message SendMapiEmailResponse {
|
||||
Error error = 1;
|
||||
NAPS2.Serialization.Error error = 1;
|
||||
string returnCodeXml = 2;
|
||||
}
|
||||
|
||||
message RenderThumbnailRequest {
|
||||
string snapshotXml = 1;
|
||||
NAPS2.Serialization.SerializedImage image = 1;
|
||||
int32 size = 2;
|
||||
}
|
||||
|
||||
message RenderThumbnailResponse {
|
||||
Error error = 1;
|
||||
NAPS2.Serialization.Error error = 1;
|
||||
bytes thumbnail = 2;
|
||||
}
|
||||
|
@ -11,5 +11,6 @@
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=hwnd/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Jpegs/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lineart/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Lossless/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=mapi/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pdfs/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
Loading…
Reference in New Issue
Block a user