2

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);
}
Arvo Bowen
  • 4,524
  • 6
  • 51
  • 109

2 Answers2

6

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.

Jamie Kitson
  • 3,973
  • 4
  • 37
  • 50
Arvo Bowen
  • 4,524
  • 6
  • 51
  • 109
4

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.

Bobrovsky
  • 13,789
  • 19
  • 80
  • 130
  • Yea and i do. ;) But it doesn't change the fact it's showing up on the tiff as "419430400/2097152" instead of "200/1". For some reason LibTiff is not reducing the fraction to the lowest common denominator. Why would this be happening? – Arvo Bowen Jun 25 '12 at 18:30
  • I don't know why authors of libtiff decided to not reduce the fraction (performance, maybe). If you are absolutely need to reduce the fraction then I suggest you to use modified source code. `writeRationalArray` in Tiff_DirWrite.cs is the method you might want to modify. – Bobrovsky Jun 25 '12 at 18:50
  • Thanks Bobrovsky! Do you know of anyone who has done this before? I have never altered the source code of the LibTiff.Net before, anything I should know before I go hacking at things? – Arvo Bowen Jun 25 '12 at 18:55
  • I am personally ported libtiff to LibTiff.Net, so I altered the source code a lot :-) The source code package of LibTiff.Net contains some unit tests so you'll have some sort of protection when you start hacking. Other than that it's trial and error I guess. The purpose of the port is to provide exactly the same functionality, so if libtiff writes fractions without reduction (worth to check, btw) then LibTiff.Net will do the same. If there is a difference between these libraries then the difference is considered to be a bug and I will try to fix it. – Bobrovsky Jun 25 '12 at 19:02
  • Where can the libtiff source be found at? – Arvo Bowen Jun 25 '12 at 19:19
  • Bobrovsky I think it is a bug as the original library looks as if it support it. See my altered question above (notes). – Arvo Bowen Jun 25 '12 at 19:37
  • Thanks. I'll take a look. Judging from the code snippet it seems like the support was added in 4.x branch of libtiff. LibTiff.Net is based on a 3.x branch. Unfortunately, I have no plans to upgrade to newer version in near time because I don't need support for BigTIFF (this is the most important improvement in 4.x branch) and there is no one else who is helping me with the port. – Bobrovsky Jun 26 '12 at 05:06
  • 1
    It would make at least one person VERY happy if you added that small bit of code to the .Net version. ;) Or at least tell me what I should add and let me build my own. Just looking at the snippets I can't seem to understand where I would put an if () {} at... or what I would use to make the "if" directive work... – Arvo Bowen Jun 26 '12 at 12:34