How can I write a rational tag like 200/1 using the LibTiff.Net 2.3 Library
-
16-06-2021 - |
Question
I have no clue why this is so hard to do but I can not get LibTiff.Net 2.3 to set a rational value correctly... Over the years I have always used values like "200/1" in tiff tag number 282(XRESOLUTION) and 283(YRESOLUTION). But when using the LibTiff.Net library it seems impossible to get that results. I always get things like "419430400/2097152" instead. Anyone know how I can resolve this issue?
Notes About My Question:
This is the libtiff library (pre .Net) and it looks like the first else if does account for something like 200/1.
TIFFWriteDirectoryTagCheckedRationalArray(TIFF* tif, uint32* ndir, TIFFDirEntry* dir, uint16 tag, uint32 count, float* value)
{
static const char module[] = "TIFFWriteDirectoryTagCheckedRationalArray";
uint32* m;
float* na;
uint32* nb;
uint32 nc;
int o;
assert(sizeof(uint32)==4);
m=_TIFFmalloc(count*2*sizeof(uint32));
if (m==NULL)
{
TIFFErrorExt(tif->tif_clientdata,module,"Out of memory");
return(0);
}
for (na=value, nb=m, nc=0; nc<count; na++, nb+=2, nc++)
{
if (*na<=0.0)
{
nb[0]=0;
nb[1]=1;
}
else if (*na==(float)(uint32)(*na))
{
nb[0]=(uint32)(*na);
nb[1]=1;
}
else if (*na<1.0)
{
nb[0]=(uint32)((*na)*0xFFFFFFFF);
nb[1]=0xFFFFFFFF;
}
else
{
nb[0]=0xFFFFFFFF;
nb[1]=(uint32)(0xFFFFFFFF/(*na));
}
}
if (tif->tif_flags&TIFF_SWAB)
TIFFSwabArrayOfLong(m,count*2);
o=TIFFWriteDirectoryTagData(tif,ndir,dir,tag,TIFF_RATIONAL,count,count*8,&m[0]);
_TIFFfree(m);
return(o);
}
This is the new .Net version...
private bool writeRationalArray(ref TiffDirEntry dir, float[] v)
{
int[] t = new int [2 * dir.tdir_count];
for (int i = 0; i < dir.tdir_count; i++)
{
int sign = 1;
float fv = v[i];
if (fv < 0)
{
if (dir.tdir_type == TiffType.RATIONAL)
{
WarningExt(this, m_clientdata, m_name,
"\"{0}\": Information lost writing value ({1:G}) as (unsigned) RATIONAL",
FieldWithTag(dir.tdir_tag).Name, fv);
fv = 0;
}
else
{
fv = -fv;
sign = -1;
}
}
int den = 1;
if (fv > 0)
{
while (fv < (1L << (31 - 3)) && den < (1L << (31 - 3)))
{
fv *= 1 << 3;
den *= 1 << 3;
}
}
t[2 * i + 0] = (int)(sign * (fv + 0.5));
t[2 * i + 1] = den;
}
return writeData(ref dir, t, 2 * dir.tdir_count);
}
Solution
Be sure to give me a 1UP if this helped you please. Thanks!
OK so I decided to take on editing the library to account for the reduced fraction. Below is the code I changed in the .Net library to make it work. And work it does!
I hope Bobrovsky includes this in his next release. I'm sure someone else will be thankful other then me! ;)
In case you a little unsure about how to edit the library here are the steps I used as detailed as possible...
1) Download the library source located here.
2) Open the project file. I used the LibTiff.NET_NoSilverlight.sln solution file to open the project.
3) Expand the LibTiff project node.
4) Expand the Internals node.
5) Find the Tiff_DirWrite.cs class file and open it.
6) Inside that class file I found the function called writeRationalArray and this is what it looked like...
/// <summary>
/// Setup a directory entry of an array of RATIONAL or SRATIONAL and
/// write the associated indirect values.
/// </summary>
private bool writeRationalArray(ref TiffDirEntry dir, float[] v)
{
int[] t = new int [2 * dir.tdir_count];
for (int i = 0; i < dir.tdir_count; i++)
{
int sign = 1;
float fv = v[i];
if (fv < 0)
{
if (dir.tdir_type == TiffType.RATIONAL)
{
WarningExt(this, m_clientdata, m_name,
"\"{0}\": Information lost writing value ({1:G}) as (unsigned) RATIONAL",
FieldWithTag(dir.tdir_tag).Name, fv);
fv = 0;
}
else
{
fv = -fv;
sign = -1;
}
}
int den = 1;
if (fv > 0)
{
while (fv < (1L << (31 - 3)) && den < (1L << (31 - 3)))
{
fv *= 1 << 3;
den *= 1 << 3;
}
}
t[2 * i + 0] = (int)(sign * (fv + 0.5));
t[2 * i + 1] = den;
}
return writeData(ref dir, t, 2 * dir.tdir_count);
}
7) Edit the writeRationalArray function to look like the following...
/// <summary>
/// Setup a directory entry of an array of RATIONAL or SRATIONAL and
/// write the associated indirect values.
/// </summary>
private bool writeRationalArray(ref TiffDirEntry dir, float[] v)
{
int[] t = new int [2 * dir.tdir_count];
for (int i = 0; i < dir.tdir_count; i++)
{
int sign = 1;
float fv = v[i];
if (fv < 0)
{
if (dir.tdir_type == TiffType.RATIONAL)
{
WarningExt(this, m_clientdata, m_name,
"\"{0}\": Information lost writing value ({1:G}) as (unsigned) RATIONAL",
FieldWithTag(dir.tdir_tag).Name, fv);
fv = 0;
}
else
{
fv = -fv;
sign = -1;
}
}
int den = 1;
if (fv > 0)
{
while (fv < (1L << (31 - 3)) && den < (1L << (31 - 3)))
{
fv *= 1 << 3;
den *= 1 << 3;
}
}
t[2 * i + 0] = (int)(sign * (fv + 0.5));
t[2 * i + 1] = den;
//Reduce the fraction
int a = t[2 * i + 0];
int b = t[2 * i + 1];
while (b > 0) { int rem = a % b; a = b; b = rem; }
for (int ind = 0; ind < 2; ind++) { t[2 * i + ind] /= a; }
}
return writeData(ref dir, t, 2 * dir.tdir_count);
}
All I did to it was add three lines of code to reduce the fraction at the end.
OTHER TIPS
When using LibTiff.Net you are supposed to set rational values like this:
image.SetField(TiffTag.XRESOLUTION, 150.0);
image.SetField(TiffTag.YRESOLUTION, 150.0);
The library will write a rational value as pair of integer values (dividend and divisor) in a TIFF. You don't need to help it with that.