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 System.Threading;
|
||||||
using Gdk;
|
using Gdk;
|
||||||
using NAPS2.Images.Bitwise;
|
|
||||||
|
|
||||||
namespace NAPS2.Images.Gtk;
|
namespace NAPS2.Images.Gtk;
|
||||||
|
|
||||||
@ -44,24 +43,24 @@ public class GtkImageContext : ImageContext
|
|||||||
|
|
||||||
public override IEnumerable<IMemoryImage> LoadFrames(Stream stream, out int count)
|
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;
|
count = 1;
|
||||||
return new[] { Load(stream) };
|
return new[] { Load(stream) };
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IEnumerable<IMemoryImage> LoadFrames(string path, out int count)
|
public override IEnumerable<IMemoryImage> LoadFrames(string path, out int count)
|
||||||
{
|
{
|
||||||
// TODO
|
|
||||||
var format = GetFileFormatFromExtension(path, true);
|
var format = GetFileFormatFromExtension(path, true);
|
||||||
if (format == ImageFileFormat.Tiff)
|
if (format == ImageFileFormat.Tiff)
|
||||||
{
|
{
|
||||||
return LoadTiff(path, out count);
|
return LoadTiff(path, out count);
|
||||||
}
|
}
|
||||||
else
|
count = 1;
|
||||||
{
|
return new[] { Load(path) };
|
||||||
count = 1;
|
|
||||||
return new[] { Load(path) };
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool SaveTiff(IList<IMemoryImage> images, string path, TiffCompressionType compression = TiffCompressionType.Auto,
|
public override bool SaveTiff(IList<IMemoryImage> images, string path, TiffCompressionType compression = TiffCompressionType.Auto,
|
||||||
@ -76,28 +75,34 @@ public class GtkImageContext : ImageContext
|
|||||||
throw new NotImplementedException();
|
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)
|
private IEnumerable<IMemoryImage> LoadTiff(string path, out int count)
|
||||||
{
|
{
|
||||||
var tiff = LibTiff.TIFFOpen(path, "r");
|
var tiff = LibTiff.TIFFOpen(path, "r");
|
||||||
count = LibTiff.TIFFNumberOfDirectories(tiff);
|
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
|
try
|
||||||
{
|
{
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
LibTiff.TIFFGetField(tiff, 256, out var w);
|
LibTiff.TIFFGetField(tiff, 256, out var w);
|
||||||
LibTiff.TIFFGetField(tiff, 257, out var h);
|
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);
|
var img = Create(w, h, ImagePixelFormat.ARGB32);
|
||||||
img.OriginalFileFormat = ImageFileFormat.Tiff;
|
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;
|
yield return img;
|
||||||
} while (LibTiff.TIFFReadDirectory(tiff) == 1);
|
} while (LibTiff.TIFFReadDirectory(tiff) == 1);
|
||||||
}
|
}
|
||||||
@ -107,13 +112,10 @@ 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.TIFFReadRGBAImageOriented(tiff, w, h, buffer, 1, 0);
|
||||||
{
|
// TODO: Check return value
|
||||||
int r = LibTiff.TIFFReadRGBAImage(tiff, w, h, (IntPtr) ptr, 0);
|
|
||||||
// TODO: Check return value
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override IMemoryImage Create(int width, int height, ImagePixelFormat pixelFormat)
|
public override IMemoryImage Create(int width, int height, ImagePixelFormat pixelFormat)
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
using System.Runtime.InteropServices;
|
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;
|
namespace NAPS2.Images.Gtk;
|
||||||
|
|
||||||
@ -8,6 +12,38 @@ public static class LibTiff
|
|||||||
[DllImport("libtiff.so.5")]
|
[DllImport("libtiff.so.5")]
|
||||||
public static extern IntPtr TIFFOpen(string filename, string mode);
|
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")]
|
[DllImport("libtiff.so.5")]
|
||||||
public static extern IntPtr TIFFClose(IntPtr tiff);
|
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);
|
public static extern int TIFFGetField(IntPtr tiff, int tag, out int field);
|
||||||
|
|
||||||
[DllImport("libtiff.so.5")]
|
[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
|
// TODO: For streams
|
||||||
// https://linux.die.net/man/3/tiffclientopen
|
// 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