mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-21 04:37:47 +03:00
Implement 24bit twain memory transfer
This commit is contained in:
parent
618af42360
commit
933c9be910
@ -4,62 +4,70 @@ namespace NAPS2.Scan.Internal.Twain;
|
||||
|
||||
public class TwainMemoryBufferReader
|
||||
{
|
||||
private readonly ScanningContext _scanningContext;
|
||||
|
||||
public TwainMemoryBufferReader(ScanningContext scanningContext)
|
||||
public unsafe void ReadBuffer(TwainMemoryBuffer memoryBuffer, ImagePixelFormat pixelFormat, IMemoryImage outputImage)
|
||||
{
|
||||
_scanningContext = scanningContext;
|
||||
var data = outputImage.Lock(LockMode.WriteOnly, out var scan0, out var dstBytesPerRow);
|
||||
try
|
||||
{
|
||||
byte[] source = memoryBuffer.Buffer.ToByteArray();
|
||||
if (pixelFormat == ImagePixelFormat.BW1)
|
||||
{
|
||||
// TODO: Handle BW1 and also grayscale
|
||||
// // No 8-bit greyscale format, so we have to transform into 24-bit
|
||||
// int rowWidth = stride;
|
||||
// int originalRowWidth = source.Length / imageHeight;
|
||||
// byte[] source2 = new byte[rowWidth * imageHeight];
|
||||
// for (int row = 0; row < imageHeight; row++)
|
||||
// {
|
||||
// for (int col = 0; col < imageWidth; col++)
|
||||
// {
|
||||
// source2[row * rowWidth + col * 3] = source[row * originalRowWidth + col];
|
||||
// source2[row * rowWidth + col * 3 + 1] = source[row * originalRowWidth + col];
|
||||
// source2[row * rowWidth + col * 3 + 2] = source[row * originalRowWidth + col];
|
||||
// }
|
||||
// }
|
||||
// source = source2;
|
||||
}
|
||||
else if (pixelFormat == ImagePixelFormat.RGB24)
|
||||
{
|
||||
if (memoryBuffer.BytesPerRow < memoryBuffer.Columns * 3 || source.Length < memoryBuffer.BytesPerRow * memoryBuffer.Rows)
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
var srcBytesPerRow = memoryBuffer.BytesPerRow;
|
||||
var bytesPerPixel = 3;
|
||||
byte* dstPtr = (byte*) scan0.ToPointer();
|
||||
fixed (byte* srcPtr = &source[0])
|
||||
{
|
||||
for (int dy = 0; dy < memoryBuffer.Rows; dy++)
|
||||
{
|
||||
for (int dx = 0; dx < memoryBuffer.Columns; dx++)
|
||||
{
|
||||
int x = memoryBuffer.XOffset + dx;
|
||||
int y = memoryBuffer.YOffset + dy;
|
||||
// Colors are provided as BGR, they need to be swapped to RGB
|
||||
// R
|
||||
*(dstPtr + y * dstBytesPerRow + x * bytesPerPixel) =
|
||||
*(srcPtr + dy * srcBytesPerRow + dx * bytesPerPixel + 2);
|
||||
// G
|
||||
*(dstPtr + y * dstBytesPerRow + x * bytesPerPixel + 1) =
|
||||
*(srcPtr + dy * srcBytesPerRow + dx * bytesPerPixel + 1);
|
||||
// B
|
||||
*(dstPtr + y * dstBytesPerRow + x * bytesPerPixel + 2) =
|
||||
*(srcPtr + dy * srcBytesPerRow + dx * bytesPerPixel);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
outputImage.Unlock(data);
|
||||
}
|
||||
}
|
||||
|
||||
public IMemoryImage ReadBuffer(MemoryStream buffer, TwainImageData imageData)
|
||||
{
|
||||
//Log.Error($"memoryData.Length: {memoryData.Length}, ImageWidth: {imageInfo.ImageWidth}, ImageLength: {imageInfo.ImageLength}, BitsPerPixel: {imageInfo.BitsPerPixel}, SamplesPerPixel: {imageInfo.SamplesPerPixel}, BitsPerSample[0]: {imageInfo.BitsPerSample[0]}, Compression: {imageInfo.Compression}, PixelType: {imageInfo.PixelType}, XRes: {imageInfo.XResolution}, YRes: {imageInfo.YResolution}");
|
||||
throw new Exception("blah");
|
||||
// int bytesPerPixel = memoryData.Length / (imageInfo.ImageWidth * imageInfo.ImageLength);
|
||||
// var pixelFormat = bytesPerPixel == 0 ? ImagePixelFormat.BW1 : ImagePixelFormat.RGB24;
|
||||
// int imageWidth = imageInfo.ImageWidth;
|
||||
// int imageHeight = imageInfo.ImageLength;
|
||||
// var bitmap = _scanningContext.ImageContext.Create(imageWidth, imageHeight, pixelFormat);
|
||||
// var data = bitmap.Lock(LockMode.WriteOnly, out var scan0, out var stride);
|
||||
// try
|
||||
// {
|
||||
// byte[] source = memoryData;
|
||||
// if (bytesPerPixel == 1)
|
||||
// {
|
||||
// // No 8-bit greyscale format, so we have to transform into 24-bit
|
||||
// int rowWidth = stride;
|
||||
// int originalRowWidth = source.Length / imageHeight;
|
||||
// byte[] source2 = new byte[rowWidth * imageHeight];
|
||||
// for (int row = 0; row < imageHeight; row++)
|
||||
// {
|
||||
// for (int col = 0; col < imageWidth; col++)
|
||||
// {
|
||||
// source2[row * rowWidth + col * 3] = source[row * originalRowWidth + col];
|
||||
// source2[row * rowWidth + col * 3 + 1] = source[row * originalRowWidth + col];
|
||||
// source2[row * rowWidth + col * 3 + 2] = source[row * originalRowWidth + col];
|
||||
// }
|
||||
// }
|
||||
// source = source2;
|
||||
// }
|
||||
// else if (bytesPerPixel == 3)
|
||||
// {
|
||||
// // Colors are provided as BGR, they need to be swapped to RGB
|
||||
// int rowWidth = stride;
|
||||
// for (int row = 0; row < imageHeight; row++)
|
||||
// {
|
||||
// for (int col = 0; col < imageWidth; col++)
|
||||
// {
|
||||
// (source[row * rowWidth + col * 3], source[row * rowWidth + col * 3 + 2]) =
|
||||
// (source[row * rowWidth + col * 3 + 2], source[row * rowWidth + col * 3]);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Marshal.Copy(source, 0, scan0, source.Length);
|
||||
// }
|
||||
// finally
|
||||
// {
|
||||
// bitmap.Unlock(data);
|
||||
// }
|
||||
// return bitmap;
|
||||
}
|
||||
}
|
@ -75,10 +75,11 @@ internal class TwainScanDriver : IScanDriver
|
||||
}
|
||||
}
|
||||
|
||||
public async Task Scan(ScanOptions options, CancellationToken cancelToken, IScanEvents scanEvents, Action<IMemoryImage> callback)
|
||||
public async Task Scan(ScanOptions options, CancellationToken cancelToken, IScanEvents scanEvents,
|
||||
Action<IMemoryImage> callback)
|
||||
{
|
||||
var controller = GetTwainController(options);
|
||||
var state = new TwainState(_scanningContext, scanEvents, callback);
|
||||
using var state = new TwainState(_scanningContext, scanEvents, callback);
|
||||
await controller.StartScan(options, state, cancelToken);
|
||||
}
|
||||
|
||||
@ -91,13 +92,13 @@ internal class TwainScanDriver : IScanDriver
|
||||
return new LocalTwainController();
|
||||
}
|
||||
|
||||
private class TwainState : ITwainEvents
|
||||
private class TwainState : ITwainEvents, IDisposable
|
||||
{
|
||||
private readonly ScanningContext _scanningContext;
|
||||
private readonly IScanEvents _scanEvents;
|
||||
private readonly Action<IMemoryImage> _callback;
|
||||
private TwainImageData? _currentImageData;
|
||||
private MemoryStream? _currentImageBuffer;
|
||||
private IMemoryImage? _currentMemoryImage;
|
||||
private long _transferredPixels;
|
||||
private long _totalPixels;
|
||||
|
||||
@ -111,7 +112,8 @@ internal class TwainScanDriver : IScanDriver
|
||||
public void PageStart(TwainPageStart pageStart)
|
||||
{
|
||||
_currentImageData = pageStart.ImageData;
|
||||
_currentImageBuffer = new MemoryStream();
|
||||
_currentMemoryImage?.Dispose();
|
||||
_currentMemoryImage = null;
|
||||
_transferredPixels = 0;
|
||||
_totalPixels = _currentImageData == null ? 0 : _currentImageData.Width * (long) _currentImageData.Height;
|
||||
_scanEvents.PageStart();
|
||||
@ -125,23 +127,36 @@ internal class TwainScanDriver : IScanDriver
|
||||
|
||||
public void MemoryBufferTransferred(TwainMemoryBuffer memoryBuffer)
|
||||
{
|
||||
if (_currentImageData == null || _currentImageBuffer == null)
|
||||
if (_currentImageData == null)
|
||||
{
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
// TODO: Verify samples etc.
|
||||
// TODO: Also support grayscale
|
||||
var pixelFormat = _currentImageData.BitsPerPixel == 1 ? ImagePixelFormat.BW1 :
|
||||
_currentImageData.BitsPerPixel == 24 ? ImagePixelFormat.RGB24 :
|
||||
throw new InvalidOperationException($"Unsupported bits per pixel: {_currentImageData.BitsPerPixel}");
|
||||
_currentMemoryImage ??= _scanningContext.ImageContext.Create(
|
||||
_currentImageData.Width, _currentImageData.Height, pixelFormat);
|
||||
|
||||
_transferredPixels += memoryBuffer.Columns * (long) memoryBuffer.Rows;
|
||||
_scanEvents.PageProgress(_transferredPixels / (double) _totalPixels);
|
||||
// TODO: Use Span on netcore?
|
||||
var buffer = memoryBuffer.Buffer.ToByteArray();
|
||||
// TODO: This doesn't handle tiles
|
||||
_currentImageBuffer.Write(buffer, 0, buffer.Length);
|
||||
|
||||
var bufferReader = new TwainMemoryBufferReader();
|
||||
bufferReader.ReadBuffer(memoryBuffer, pixelFormat, _currentMemoryImage);
|
||||
|
||||
if (_transferredPixels == _totalPixels)
|
||||
{
|
||||
// TODO: Throw an error if there's a pixel mismatch, i.e. we go to the next page / finish with too few, or have too many
|
||||
var bufferReader = new TwainMemoryBufferReader(_scanningContext);
|
||||
var image = bufferReader.ReadBuffer(_currentImageBuffer, _currentImageData);
|
||||
_callback(image);
|
||||
_callback(_currentMemoryImage);
|
||||
_currentMemoryImage = null;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_currentMemoryImage?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user