mirror of
https://github.com/cyanfish/naps2.git
synced 2024-09-11 15:26:55 +03:00
Gtk: Tiff stream support
This commit is contained in:
parent
f01bf7e414
commit
e628127547
@ -1,6 +1,5 @@
|
||||
using System.Threading;
|
||||
using Gdk;
|
||||
using NAPS2.Images.Bitwise;
|
||||
|
||||
namespace NAPS2.Images.Gtk;
|
||||
|
||||
@ -44,25 +43,25 @@ public class GtkImageContext : ImageContext
|
||||
|
||||
public override IEnumerable<IMemoryImage> LoadFrames(Stream stream, out int count)
|
||||
{
|
||||
// TODO
|
||||
var format = GetFileFormatFromFirstBytes(stream);
|
||||
if (format == ImageFileFormat.Tiff)
|
||||
{
|
||||
return LoadTiff(stream, out count);
|
||||
}
|
||||
count = 1;
|
||||
return new[] { Load(stream) };
|
||||
}
|
||||
|
||||
public override IEnumerable<IMemoryImage> LoadFrames(string path, out int count)
|
||||
{
|
||||
// TODO
|
||||
var format = GetFileFormatFromExtension(path, true);
|
||||
if (format == ImageFileFormat.Tiff)
|
||||
{
|
||||
return LoadTiff(path, out count);
|
||||
}
|
||||
else
|
||||
{
|
||||
count = 1;
|
||||
return new[] { Load(path) };
|
||||
}
|
||||
}
|
||||
|
||||
public override bool SaveTiff(IList<IMemoryImage> images, string path, TiffCompressionType compression = TiffCompressionType.Auto,
|
||||
Action<int, int>? progressCallback = null, CancellationToken cancelToken = default)
|
||||
@ -76,28 +75,34 @@ public class GtkImageContext : ImageContext
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private IEnumerable<IMemoryImage> LoadTiff(Stream stream, out int count)
|
||||
{
|
||||
var c = new LibTiffStreamClient(stream);
|
||||
var tiff = c.TIFFClientOpen("r");
|
||||
count = LibTiff.TIFFNumberOfDirectories(tiff);
|
||||
return EnumerateTiffFrames(tiff, c);
|
||||
}
|
||||
|
||||
private IEnumerable<IMemoryImage> LoadTiff(string path, out int count)
|
||||
{
|
||||
var tiff = LibTiff.TIFFOpen(path, "r");
|
||||
count = LibTiff.TIFFNumberOfDirectories(tiff);
|
||||
return EnumerateTiffFrames(tiff);
|
||||
return EnumerateTiffFrames(tiff, null);
|
||||
}
|
||||
|
||||
private IEnumerable<IMemoryImage> EnumerateTiffFrames(IntPtr tiff)
|
||||
private IEnumerable<IMemoryImage> EnumerateTiffFrames(IntPtr tiff, LibTiffStreamClient? client)
|
||||
{
|
||||
// We keep a reference to the client to avoid garbage collection
|
||||
try
|
||||
{
|
||||
do
|
||||
{
|
||||
LibTiff.TIFFGetField(tiff, 256, out var w);
|
||||
LibTiff.TIFFGetField(tiff, 257, out var h);
|
||||
var buffer = new byte[w * h * 4];
|
||||
ReadTiffFrame(buffer, tiff, w, h);
|
||||
var bufferInfo = new PixelInfo(w, h, SubPixelType.Rgba) { InvertY = true };
|
||||
// TODO: Get pixel format from tiff
|
||||
var img = Create(w, h, ImagePixelFormat.ARGB32);
|
||||
img.OriginalFileFormat = ImageFileFormat.Tiff;
|
||||
new CopyBitwiseImageOp().Perform(buffer, bufferInfo, img);
|
||||
using var imageLock = img.Lock(LockMode.WriteOnly, out var data);
|
||||
ReadTiffFrame(data.safePtr, tiff, w, h);
|
||||
yield return img;
|
||||
} while (LibTiff.TIFFReadDirectory(tiff) == 1);
|
||||
}
|
||||
@ -107,14 +112,11 @@ public class GtkImageContext : ImageContext
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void ReadTiffFrame(byte[] buffer, IntPtr tiff, int w, int h)
|
||||
private static void ReadTiffFrame(IntPtr buffer, IntPtr tiff, int w, int h)
|
||||
{
|
||||
fixed (void* ptr = &buffer[0])
|
||||
{
|
||||
int r = LibTiff.TIFFReadRGBAImage(tiff, w, h, (IntPtr) ptr, 0);
|
||||
int r = LibTiff.TIFFReadRGBAImageOriented(tiff, w, h, buffer, 1, 0);
|
||||
// TODO: Check return value
|
||||
}
|
||||
}
|
||||
|
||||
public override IMemoryImage Create(int width, int height, ImagePixelFormat pixelFormat)
|
||||
{
|
||||
|
@ -1,4 +1,8 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using toff_t = System.IntPtr;
|
||||
using tsize_t = System.IntPtr;
|
||||
using thandle_t = System.IntPtr;
|
||||
using tdata_t = System.IntPtr;
|
||||
|
||||
namespace NAPS2.Images.Gtk;
|
||||
|
||||
@ -8,6 +12,38 @@ public static class LibTiff
|
||||
[DllImport("libtiff.so.5")]
|
||||
public static extern IntPtr TIFFOpen(string filename, string mode);
|
||||
|
||||
[DllImport("libtiff.so.5")]
|
||||
public static extern IntPtr TIFFSetErrorHandler(TIFFErrorHandler handler);
|
||||
|
||||
[DllImport("libtiff.so.5")]
|
||||
public static extern IntPtr TIFFSetWarningHandler(TIFFErrorHandler handler);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void TIFFErrorHandler(string x, string y, IntPtr va_args);
|
||||
|
||||
[DllImport("libtiff.so.5")]
|
||||
public static extern IntPtr TIFFClientOpen(string filename, string mode, IntPtr clientdata,
|
||||
TIFFReadWriteProc readproc, TIFFReadWriteProc writeproc, TIFFSeekProc seekproc, TIFFCloseProc closeproc,
|
||||
TIFFSizeProc sizeproc, TIFFMapFileProc mapproc, TIFFUnmapFileProc unmapproc);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate tsize_t TIFFReadWriteProc(thandle_t clientdata, tdata_t data, tsize_t size);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate toff_t TIFFSeekProc(thandle_t clientdata, toff_t off, int c);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int TIFFCloseProc(thandle_t clientdata);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate toff_t TIFFSizeProc(thandle_t clientdata);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate int TIFFMapFileProc(thandle_t clientdata, ref tdata_t a, ref toff_t b);
|
||||
|
||||
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
||||
public delegate void TIFFUnmapFileProc(thandle_t clientdata, tdata_t a, toff_t b);
|
||||
|
||||
[DllImport("libtiff.so.5")]
|
||||
public static extern IntPtr TIFFClose(IntPtr tiff);
|
||||
|
||||
@ -21,7 +57,12 @@ public static class LibTiff
|
||||
public static extern int TIFFGetField(IntPtr tiff, int tag, out int field);
|
||||
|
||||
[DllImport("libtiff.so.5")]
|
||||
public static extern int TIFFReadRGBAImage(IntPtr tiff, int w, int h, IntPtr raster, int stopOnError);
|
||||
public static extern int TIFFReadRGBAImage(
|
||||
IntPtr tiff, int w, int h, IntPtr raster, int stopOnError);
|
||||
|
||||
[DllImport("libtiff.so.5")]
|
||||
public static extern int TIFFReadRGBAImageOriented(
|
||||
IntPtr tiff, int w, int h, IntPtr raster, int orientation, int stopOnError);
|
||||
|
||||
// TODO: For streams
|
||||
// https://linux.die.net/man/3/tiffclientopen
|
||||
|
104
NAPS2.Images.Gtk/LibTiffStreamClient.cs
Normal file
104
NAPS2.Images.Gtk/LibTiffStreamClient.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using System.Runtime.InteropServices;
|
||||
using toff_t = System.IntPtr;
|
||||
using tsize_t = System.IntPtr;
|
||||
using thandle_t = System.IntPtr;
|
||||
using tdata_t = System.IntPtr;
|
||||
|
||||
namespace NAPS2.Images.Gtk;
|
||||
|
||||
public class LibTiffStreamClient
|
||||
{
|
||||
private readonly Stream _stream;
|
||||
private readonly LibTiff.TIFFErrorHandler _error;
|
||||
private readonly LibTiff.TIFFErrorHandler _warning;
|
||||
private readonly LibTiff.TIFFReadWriteProc _read;
|
||||
private readonly LibTiff.TIFFReadWriteProc _write;
|
||||
private readonly LibTiff.TIFFSeekProc _seek;
|
||||
private readonly LibTiff.TIFFCloseProc _close;
|
||||
private readonly LibTiff.TIFFSizeProc _size;
|
||||
private readonly LibTiff.TIFFMapFileProc _map;
|
||||
private readonly LibTiff.TIFFUnmapFileProc _unmap;
|
||||
|
||||
public LibTiffStreamClient(Stream stream)
|
||||
{
|
||||
_stream = stream;
|
||||
// We need to keep explicit references to the delegates to avoid garbage collection
|
||||
_error = Error;
|
||||
_warning = Warning;
|
||||
_read = Read;
|
||||
_write = Write;
|
||||
_seek = Seek;
|
||||
_close = Close;
|
||||
_size = Size;
|
||||
_map = Map;
|
||||
_unmap = UnMap;
|
||||
}
|
||||
|
||||
public IntPtr TIFFClientOpen(string mode)
|
||||
{
|
||||
LibTiff.TIFFSetErrorHandler(_error);
|
||||
LibTiff.TIFFSetWarningHandler(_warning);
|
||||
return LibTiff.TIFFClientOpen("placeholder", mode, IntPtr.Zero,
|
||||
_read, _write, _seek, _close, _size, _map, _unmap);
|
||||
}
|
||||
|
||||
private void Warning(string x, string y, IntPtr va_args)
|
||||
{
|
||||
}
|
||||
|
||||
private void Error(string x, string y, IntPtr va_args)
|
||||
{
|
||||
}
|
||||
|
||||
public tsize_t Read(thandle_t clientdata, tdata_t data, tsize_t size)
|
||||
{
|
||||
var buffer = new byte[(int) size];
|
||||
var count = _stream.Read(buffer);
|
||||
Marshal.Copy(buffer, 0, data, count);
|
||||
return (tsize_t) count;
|
||||
}
|
||||
|
||||
public tsize_t Write(thandle_t clientdata, tdata_t data, tsize_t size)
|
||||
{
|
||||
var buffer = new byte[(int) size];
|
||||
Marshal.Copy(data, buffer, 0, buffer.Length);
|
||||
_stream.Write(buffer);
|
||||
return (tsize_t) buffer.Length;
|
||||
}
|
||||
|
||||
public toff_t Seek(thandle_t clientdata, toff_t off, int c)
|
||||
{
|
||||
if (c == 0)
|
||||
{
|
||||
_stream.Seek((long) off, SeekOrigin.Begin);
|
||||
}
|
||||
if (c == 1)
|
||||
{
|
||||
_stream.Seek((long) off, SeekOrigin.Current);
|
||||
}
|
||||
if (c == 2)
|
||||
{
|
||||
_stream.Seek((long) off, SeekOrigin.End);
|
||||
}
|
||||
return (toff_t) _stream.Position;
|
||||
}
|
||||
|
||||
public int Close(thandle_t clientdata)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public toff_t Size(thandle_t clientdata)
|
||||
{
|
||||
return (toff_t) _stream.Length;
|
||||
}
|
||||
|
||||
public int Map(thandle_t clientdata, ref tdata_t a, ref toff_t b)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public void UnMap(thandle_t clientdata, tdata_t a, toff_t b)
|
||||
{
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user