Skip to content

Commit

Permalink
Gtk: Support libtiff.so.6 as well as libtiff.so.5
Browse files Browse the repository at this point in the history
  • Loading branch information
cyanfish committed Jan 14, 2024
1 parent 7895a6f commit 07a38b8
Show file tree
Hide file tree
Showing 2 changed files with 119 additions and 56 deletions.
143 changes: 103 additions & 40 deletions NAPS2.Images.Gtk/LibTiff.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Runtime.InteropServices;
using NAPS2.Util;
using toff_t = System.IntPtr;
using tsize_t = System.IntPtr;
using thandle_t = System.IntPtr;
Expand All @@ -8,24 +9,64 @@ namespace NAPS2.Images.Gtk;

internal static class LibTiff
{
// TODO: String marshalling?
[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);
private const int RTLD_LAZY = 1;
private const int RTLD_GLOBAL = 8;

private static readonly Dictionary<Type, object> FuncCache = new();

private static readonly Lazy<IntPtr> LibraryHandle = new(() =>
{
var handle = dlopen("libtiff.so.5", RTLD_LAZY | RTLD_GLOBAL);
if (handle == IntPtr.Zero)
{
handle = dlopen("libtiff.so.6", RTLD_LAZY | RTLD_GLOBAL);
}
if (handle == IntPtr.Zero)
{
var error = dlerror();
throw new InvalidOperationException($"Could not load library: \"libtiff\". Error: {error}");
}
return handle;
});

public static T Load<T>()
{
return (T) FuncCache.Get(typeof(T), () => Marshal.GetDelegateForFunctionPointer<T>(LoadFunc<T>())!);
}

private static IntPtr LoadFunc<T>()
{
var symbol = typeof(T).Name.Split("_")[0];
var ptr = dlsym(LibraryHandle.Value, symbol);
if (ptr == IntPtr.Zero)
{
var error = dlerror();
throw new InvalidOperationException($"Could not load symbol: \"{symbol}\". Error: {error}");
}
return ptr;
}

public delegate IntPtr TIFFOpen_d(string filename, string mode);

public static TIFFOpen_d TIFFOpen => Load<TIFFOpen_d>();

public delegate IntPtr TIFFSetErrorHandler_d(TIFFErrorHandler handler);

public static TIFFSetErrorHandler_d TIFFSetErrorHandler => Load<TIFFSetErrorHandler_d>();

public delegate IntPtr TIFFSetWarningHandler_d(TIFFErrorHandler handler);

public static TIFFSetWarningHandler_d TIFFSetWarningHandler => Load<TIFFSetWarningHandler_d>();

[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,
public delegate IntPtr TIFFClientOpen_d(string filename, string mode, IntPtr clientdata,
TIFFReadWriteProc readproc, TIFFReadWriteProc writeproc, TIFFSeekProc seekproc, TIFFCloseProc closeproc,
TIFFSizeProc sizeproc, TIFFMapFileProc mapproc, TIFFUnmapFileProc unmapproc);

public static TIFFClientOpen_d TIFFClientOpen => Load<TIFFClientOpen_d>();

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate tsize_t TIFFReadWriteProc(thandle_t clientdata, tdata_t data, tsize_t size);

Expand All @@ -44,49 +85,71 @@ public static extern IntPtr TIFFClientOpen(string filename, string mode, IntPtr
[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);
public delegate IntPtr TIFFClose_d(IntPtr tiff);

public static TIFFClose_d TIFFClose => Load<TIFFClose_d>();

public delegate short TIFFNumberOfDirectories_d(IntPtr tiff);

public static TIFFNumberOfDirectories_d TIFFNumberOfDirectories => Load<TIFFNumberOfDirectories_d>();

public delegate int TIFFReadDirectory_d(IntPtr tiff);

public static TIFFReadDirectory_d TIFFReadDirectory => Load<TIFFReadDirectory_d>();

public delegate int TIFFWriteDirectory_d(IntPtr tiff);

public static TIFFWriteDirectory_d TIFFWriteDirectory => Load<TIFFWriteDirectory_d>();

[DllImport("libtiff.so.5")]
public static extern short TIFFNumberOfDirectories(IntPtr tiff);
public delegate int TIFFGetField_d1(IntPtr tiff, TiffTag tag, out int field);

[DllImport("libtiff.so.5")]
public static extern int TIFFReadDirectory(IntPtr tiff);
public static TIFFGetField_d1 TIFFGetFieldInt => Load<TIFFGetField_d1>();

[DllImport("libtiff.so.5")]
public static extern int TIFFWriteDirectory(IntPtr tiff);
public delegate int TIFFGetField_d2(IntPtr tiff, TiffTag tag, out float field);

// TODO: Clean these overloads up
[DllImport("libtiff.so.5")]
public static extern int TIFFGetField(IntPtr tiff, TiffTag tag, out int field);
public static TIFFGetField_d2 TIFFGetFieldFloat => Load<TIFFGetField_d2>();

[DllImport("libtiff.so.5")]
public static extern int TIFFGetField(IntPtr tiff, TiffTag tag, out float field);
public delegate int TIFFGetField_d3(IntPtr tiff, TiffTag tag, out double field);

[DllImport("libtiff.so.5")]
public static extern int TIFFGetField(IntPtr tiff, TiffTag tag, out double field);
public static TIFFGetField_d3 TIFFGetFieldDouble => Load<TIFFGetField_d3>();

[DllImport("libtiff.so.5")]
public static extern int TIFFSetField(IntPtr tiff, TiffTag tag, int field);
public delegate int TIFFSetField_d1(IntPtr tiff, TiffTag tag, int field);

[DllImport("libtiff.so.5")]
public static extern int TIFFSetField(IntPtr tiff, TiffTag tag, float field);
public static TIFFSetField_d1 TIFFSetFieldInt => Load<TIFFSetField_d1>();

[DllImport("libtiff.so.5")]
public static extern int TIFFSetField(IntPtr tiff, TiffTag tag, double field);
public delegate int TIFFSetField_d2(IntPtr tiff, TiffTag tag, float field);

[DllImport("libtiff.so.5")]
public static extern int TIFFSetField(IntPtr tiff, TiffTag tag, short field, short[] array);
public static TIFFSetField_d2 TIFFSetFieldFloat => Load<TIFFSetField_d2>();

[DllImport("libtiff.so.5")]
public static extern int TIFFWriteScanline(
public delegate int TIFFSetField_d3(IntPtr tiff, TiffTag tag, double field);

public static TIFFSetField_d3 TIFFSetFieldDouble => Load<TIFFSetField_d3>();

public delegate int TIFFSetField_d4(IntPtr tiff, TiffTag tag, short field, short[] array);

public static TIFFSetField_d4 TIFFSetFieldShortArray => Load<TIFFSetField_d4>();

public delegate int TIFFWriteScanline_d(
IntPtr tiff, tdata_t buf, int row, short sample);

[DllImport("libtiff.so.5")]
public static extern int TIFFReadRGBAImage(
public static TIFFWriteScanline_d TIFFWriteScanline => Load<TIFFWriteScanline_d>();

public delegate int TIFFReadRGBAImage_d(
IntPtr tiff, int w, int h, IntPtr raster, int stopOnError);

[DllImport("libtiff.so.5")]
public static extern int TIFFReadRGBAImageOriented(
public static TIFFReadRGBAImage_d TIFFReadRGBAImage => Load<TIFFReadRGBAImage_d>();

public delegate int TIFFReadRGBAImageOriented_d(
IntPtr tiff, int w, int h, IntPtr raster, int orientation, int stopOnError);

public static TIFFReadRGBAImageOriented_d TIFFReadRGBAImageOriented => Load<TIFFReadRGBAImageOriented_d>();

[DllImport("libdl.so.2")]
public static extern IntPtr dlopen(string filename, int flags);

[DllImport("libdl.so.2")]
public static extern string dlerror();

[DllImport("libdl.so.2")]
public static extern IntPtr dlsym(IntPtr handle, string symbol);
}
32 changes: 16 additions & 16 deletions NAPS2.Images.Gtk/LibTiffIo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,10 @@ private static void WriteTiffMetadata(IntPtr tiff, ImagePixelFormat pixelFormat,
{
// TODO: A lot of these types are wrong (e.g. int32 instead of int16)
// http://www.libtiff.org/man/TIFFSetField.3t.html
LibTiff.TIFFSetField(tiff, TiffTag.ImageWidth, image.Width);
LibTiff.TIFFSetField(tiff, TiffTag.ImageHeight, image.Height);
LibTiff.TIFFSetField(tiff, TiffTag.PlanarConfig, 1);
LibTiff.TIFFSetField(tiff, TiffTag.Compression, (int) (compression switch
LibTiff.TIFFSetFieldInt(tiff, TiffTag.ImageWidth, image.Width);
LibTiff.TIFFSetFieldInt(tiff, TiffTag.ImageHeight, image.Height);
LibTiff.TIFFSetFieldInt(tiff, TiffTag.PlanarConfig, 1);
LibTiff.TIFFSetFieldInt(tiff, TiffTag.Compression, (int) (compression switch
{
TiffCompressionType.Ccitt4 => TiffCompression.G4,
TiffCompressionType.Lzw => TiffCompression.Lzw,
Expand All @@ -97,29 +97,29 @@ private static void WriteTiffMetadata(IntPtr tiff, ImagePixelFormat pixelFormat,
? TiffCompression.G4
: TiffCompression.Lzw
}));
LibTiff.TIFFSetField(tiff, TiffTag.Orientation, 1);
LibTiff.TIFFSetField(tiff, TiffTag.BitsPerSample, pixelFormat == ImagePixelFormat.BW1 ? 1 : 8);
LibTiff.TIFFSetField(tiff, TiffTag.SamplesPerPixel, pixelFormat switch
LibTiff.TIFFSetFieldInt(tiff, TiffTag.Orientation, 1);
LibTiff.TIFFSetFieldInt(tiff, TiffTag.BitsPerSample, pixelFormat == ImagePixelFormat.BW1 ? 1 : 8);
LibTiff.TIFFSetFieldInt(tiff, TiffTag.SamplesPerPixel, pixelFormat switch
{
ImagePixelFormat.RGB24 => 3,
ImagePixelFormat.ARGB32 => 4,
_ => 1
});
LibTiff.TIFFSetField(tiff, TiffTag.Photometric, (int) (pixelFormat switch
LibTiff.TIFFSetFieldInt(tiff, TiffTag.Photometric, (int) (pixelFormat switch
{
ImagePixelFormat.RGB24 or ImagePixelFormat.ARGB32 => TiffPhotometric.Rgb,
_ => TiffPhotometric.MinIsBlack
}));
if (pixelFormat == ImagePixelFormat.ARGB32)
{
LibTiff.TIFFSetField(tiff, TiffTag.ExtraSamples, 1, new short[] { 2 });
LibTiff.TIFFSetFieldShortArray(tiff, TiffTag.ExtraSamples, 1, new short[] { 2 });
}
if (image.HorizontalResolution != 0 && image.VerticalResolution != 0)
{
LibTiff.TIFFSetField(tiff, TiffTag.ResolutionUnit, 2);
LibTiff.TIFFSetFieldInt(tiff, TiffTag.ResolutionUnit, 2);
// TODO: Why do we need to write as a double? It's supposed to be a float.
LibTiff.TIFFSetField(tiff, TiffTag.XResolution, (double) image.HorizontalResolution);
LibTiff.TIFFSetField(tiff, TiffTag.YResolution, (double) image.VerticalResolution);
LibTiff.TIFFSetFieldDouble(tiff, TiffTag.XResolution, image.HorizontalResolution);
LibTiff.TIFFSetFieldDouble(tiff, TiffTag.YResolution, image.VerticalResolution);
}
}

Expand All @@ -143,11 +143,11 @@ private void EnumerateTiffFrames(Action<IMemoryImage> produceImage, IntPtr tiff,
do
{
if (progress.IsCancellationRequested) break;
LibTiff.TIFFGetField(tiff, TiffTag.ImageWidth, out int w);
LibTiff.TIFFGetField(tiff, TiffTag.ImageHeight, out int h);
LibTiff.TIFFGetFieldInt(tiff, TiffTag.ImageWidth, out int w);
LibTiff.TIFFGetFieldInt(tiff, TiffTag.ImageHeight, out int h);
// TODO: Check return values
LibTiff.TIFFGetField(tiff, TiffTag.XResolution, out float xres);
LibTiff.TIFFGetField(tiff, TiffTag.YResolution, out float yres);
LibTiff.TIFFGetFieldFloat(tiff, TiffTag.XResolution, out float xres);
LibTiff.TIFFGetFieldFloat(tiff, TiffTag.YResolution, out float yres);
var img = _imageContext.Create(w, h, ImagePixelFormat.ARGB32);
img.SetResolution(xres, yres);
img.OriginalFileFormat = ImageFileFormat.Tiff;
Expand Down

0 comments on commit 07a38b8

Please sign in to comment.