static bool TIFFWriteByteArray(TIFF tif, TIFFDirEntry dir, sbyte[] cp) { byte[] buf=new byte[dir.tdir_count]; for(int i=0; i<dir.tdir_count; i++) buf[i]=(byte)cp[i]; return TIFFWriteByteArray(tif, dir, buf); }
// Fetch and set the SubjectDistance EXIF tag. static bool TIFFFetchSubjectDistance(TIFF tif, TIFFDirEntry dir) { if(dir.tdir_count!=1||dir.tdir_type!=(ushort)TIFFDataType.TIFF_RATIONAL) { TIFFWarningExt(tif.tif_clientdata, tif.tif_name, "incorrect count or type for SubjectDistance, tag ignored"); return false; } bool ok=false; byte[] buf=new byte[8]; if(TIFFFetchData(tif, dir, buf)!=0) { uint l0=BitConverter.ToUInt32(buf, 0); uint l1=BitConverter.ToUInt32(buf, 4); double v; if(cvtRational(tif, dir, l0, l1, out v)) { // XXX: Numerator 0xFFFFFFFF means that we have infinite // distance. Indicate that with a negative floating point // SubjectDistance value. ok=TIFFSetField(tif, (TIFFTAG)dir.tdir_tag, (l0!=0xFFFFFFFF)?v:-v); } } return ok; }
// Convert numerator+denominator to double. static bool cvtRational(TIFF tif, TIFFDirEntry dir, uint num, uint denom, out double rv) { if(num==0&&denom==0) { rv=0; return true; } if(denom==0) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "{0}: Rational with zero denominator (num = {1})", TIFFFieldWithTag(tif, (TIFFTAG)dir.tdir_tag).field_name, num); rv=double.NaN; return false; } if((TIFFDataType)dir.tdir_type==TIFFDataType.TIFF_RATIONAL) rv=((double)num/(double)denom); else rv=((double)(int)num/(double)(int)denom); return true; }
// Fetch an array of SSHORT values. static bool TIFFFetchShortArray(TIFF tif, TIFFDirEntry dir, short[] v) { if(dir.tdir_count<=2) { if(tif.tif_header.tiff_magic==TIFF_BIGENDIAN) { switch(dir.tdir_count) { case 2: v[1]=(short)(dir.tdir_offset&0xffff); goto case 1; case 1: v[0]=(short)(dir.tdir_offset>>16); break; } } else { switch(dir.tdir_count) { case 2: v[1]=(short)(dir.tdir_offset>>16); goto case 1; case 1: v[0]=(short)(dir.tdir_offset&0xffff); break; } } return true; } byte[] buf=new byte[dir.tdir_count*2]; if(TIFFFetchData(tif, dir, buf)==0) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=BitConverter.ToInt16(buf, i*2); return true; }
// Fetch an ASCII item from the file. static int TIFFFetchString(TIFF tif, TIFFDirEntry dir, out string cp) { if(dir.tdir_count<=4) { uint l=dir.tdir_offset; if((tif.tif_flags&TIF_FLAGS.TIFF_SWAB)!=0) TIFFSwab(ref l); // swab 'back' see in TIFFReadDirectory & TIFFReadCustomDirectory cp=System.Text.Encoding.ASCII.GetString(BitConverter.GetBytes(l)); return 1; } cp=null; byte[] buf=new byte[dir.tdir_count]; int ret=TIFFFetchData(tif, dir, buf); if(ret==0) return 0; cp=System.Text.Encoding.ASCII.GetString(buf); return ret; }
// Fetch samples/pixel short values for // the specified tag and verify that // all values are the same. static bool TIFFFetchPerSampleShorts(TIFF tif, TIFFDirEntry dir, out ushort pl) { ushort samples=tif.tif_dir.td_samplesperpixel; pl=0; if(!CheckDirCount(tif, dir, samples)) return false; ushort[] v=null; try { v=new ushort[dir.tdir_count]; } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "No space to fetch per-sample values"); return false; } if(TIFFFetchShortArray(tif, dir, v)) { uint check_count=dir.tdir_count; if(samples<check_count) check_count=samples; for(uint i=1; i<check_count; i++) { if(v[i]!=v[0]) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Cannot handle different per-sample values for field \"{0}\"", TIFFFieldWithTag(tif, (TIFFTAG)dir.tdir_tag).field_name); return false; } } pl=v[0]; } return true; }
// Fetch an array of RATIONAL or SRATIONAL values. static bool TIFFFetchRationalArray(TIFF tif, TIFFDirEntry dir, double[] v) { byte[] buf=null; try { buf=new byte[dir.tdir_count*8]; } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "No space to fetch array of rationals"); return false; } for(int i=0; i<dir.tdir_count; i++) { if(!cvtRational(tif, dir, BitConverter.ToUInt32(buf, i*8), BitConverter.ToUInt32(buf, i*8+4), out v[i])) return false; } return true; }
// Process tags that are not special cased. static bool TIFFWriteNormalTag(TIFF tif, TIFFDirEntry dir, TIFFFieldInfo fip) { uint wc=(uint)fip.field_writecount; dir.tdir_tag=(ushort)fip.field_tag; dir.tdir_type=(ushort)fip.field_type; dir.tdir_count=wc; switch(fip.field_type) { case TIFFDataType.TIFF_SHORT: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); if(fip.field_passcount) { // Assume TIFF_VARIABLE dir.tdir_count=wc=__GetAsUint(ap, 0); ushort[] wp=ap[1] as ushort[]; if(wp==null) return false; if(!TIFFWriteShortArray(tif, dir, wp)) return false; } else { if(wc==1) { ushort sv=__GetAsUshort(ap, 0); //was dir.tdir_offset=TIFFInsertData(tif, dir.tdir_type, sv); dir.tdir_offset=((uint)(tif.tif_header.tiff_magic==TIFF_BIGENDIAN?(sv&tif_typemask[dir.tdir_type])<<tif_typeshift[dir.tdir_type]:sv&tif_typemask[dir.tdir_type])); } else { ushort[] wp=ap[0] as ushort[]; if(wp==null) return false; if(!TIFFWriteShortArray(tif, dir, wp)) return false; } } } break; case TIFFDataType.TIFF_SSHORT: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); if(fip.field_passcount) { // Assume TIFF_VARIABLE dir.tdir_count=wc=__GetAsUint(ap, 0); short[] wp=ap[1] as short[]; if(wp==null) return false; if(!TIFFWriteShortArray(tif, dir, wp)) return false; } else { if(wc==1) { short sv=__GetAsShort(ap, 0); //was dir.tdir_offset=TIFFInsertData(tif, dir.tdir_type, sv); dir.tdir_offset=((uint)(tif.tif_header.tiff_magic==TIFF_BIGENDIAN?(sv&tif_typemask[dir.tdir_type])<<tif_typeshift[dir.tdir_type]:sv&tif_typemask[dir.tdir_type])); } else { short[] wp=ap[0] as short[]; if(wp==null) return false; if(!TIFFWriteShortArray(tif, dir, wp)) return false; } } } break; case TIFFDataType.TIFF_SLONG: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); if(fip.field_passcount) { // Assume TIFF_VARIABLE dir.tdir_count=wc=__GetAsUint(ap, 0); int[] lp=ap[1] as int[]; if(lp==null) return false; if(!TIFFWriteLongArray(tif, dir, lp)) return false; } else { if(wc==1) { // XXX handle LONG=>SHORT conversion dir.tdir_offset=__GetAsUint(ap, 0); } else { int[] lp=ap[0] as int[]; if(lp==null) return false; if(!TIFFWriteLongArray(tif, dir, lp)) return false; } } } break; case TIFFDataType.TIFF_LONG: case TIFFDataType.TIFF_IFD: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); if(fip.field_passcount) { // Assume TIFF_VARIABLE dir.tdir_count=wc=__GetAsUint(ap, 0); uint[] lp=ap[1] as uint[]; if(lp==null) return false; if(!TIFFWriteLongArray(tif, dir, lp)) return false; } else { if(wc==1) { // XXX handle LONG=>SHORT conversion dir.tdir_offset=__GetAsUint(ap, 0); } else { uint[] lp=ap[0] as uint[]; if(lp==null) return false; if(!TIFFWriteLongArray(tif, dir, lp)) return false; } } } break; case TIFFDataType.TIFF_RATIONAL: case TIFFDataType.TIFF_SRATIONAL: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); if(fip.field_passcount) { // Assume TIFF_VARIABLE dir.tdir_count=wc=__GetAsUint(ap, 0); double[] fp=ap[1] as double[]; if(fp==null) return false; if(!TIFFWriteRationalArray(tif, dir, fp)) return false; } else { if(wc==1) { double fv=__GetAsDouble(ap, 0); if(!TIFFWriteRational(tif, fip.field_type, fip.field_tag, dir, fv)) return false; } else { double[] fp=ap[0] as double[]; if(fp==null) return false; if(!TIFFWriteRationalArray(tif, dir, fp)) return false; } } } break; case TIFFDataType.TIFF_FLOAT: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); if(fip.field_passcount) { // Assume TIFF_VARIABLE dir.tdir_count=wc=__GetAsUint(ap, 0); float[] fp=ap[1] as float[]; if(fp==null) return false; if(!TIFFWriteFloatArray(tif, dir, fp)) return false; } else { if(wc==1) { float[] fv=new float[1]; fv[0]=__GetAsFloat(ap, 0); if(!TIFFWriteFloatArray(tif, dir, fv)) return false; } else { float[] fp=ap[0] as float[]; if(fp==null) return false; if(!TIFFWriteFloatArray(tif, dir, fp)) return false; } } } break; case TIFFDataType.TIFF_DOUBLE: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); if(fip.field_passcount) { // Assume TIFF_VARIABLE dir.tdir_count=wc=__GetAsUint(ap, 0); double[] dp=ap[1] as double[]; if(dp==null) return false; if(!TIFFWriteDoubleArray(tif, dir, dp)) return false; } else { if(wc==1) { double[] dv=new double[1]; dv[0]=__GetAsDouble(ap, 0); if(!TIFFWriteDoubleArray(tif, dir, dv)) return false; } else { double[] dp=ap[0] as double[]; if(dp==null) return false; if(!TIFFWriteDoubleArray(tif, dir, dp)) return false; } } } break; case TIFFDataType.TIFF_ASCII: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); string cp; if(fip.field_passcount) { // Assume TIFF_VARIABLE wc=__GetAsUint(ap, 0); cp=ap[1] as string; if(cp==null) return false; } else { cp=ap[0] as string; if(cp==null) return false; } cp=cp.TrimEnd('\0'); cp+='\0'; dir.tdir_count=(uint)cp.Length; if(!TIFFWriteByteArray(tif, dir, Encoding.ASCII.GetBytes(cp))) return false; } break; case TIFFDataType.TIFF_BYTE: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); if(fip.field_passcount) { // Assume TIFF_VARIABLE dir.tdir_count=wc=__GetAsUint(ap, 0); byte[] cp=ap[1] as byte[]; if(cp==null) return false; if(!TIFFWriteByteArray(tif, dir, cp)) return false; } else { if(wc==1) { byte[] cv=new byte[1]; cv[0]=__GetAsByte(ap, 0); if(!TIFFWriteByteArray(tif, dir, cv)) return false; } else { byte[] cp=ap[0] as byte[]; if(cp==null) return false; if(!TIFFWriteByteArray(tif, dir, cp)) return false; } } } break; case TIFFDataType.TIFF_SBYTE: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); if(fip.field_passcount) { // Assume TIFF_VARIABLE dir.tdir_count=wc=__GetAsUint(ap, 0); sbyte[] cp=ap[1] as sbyte[]; if(cp==null) return false; if(!TIFFWriteByteArray(tif, dir, cp)) return false; } else { if(wc==1) { sbyte[] cv=new sbyte[1]; cv[0]=__GetAsSbyte(ap, 0); if(!TIFFWriteByteArray(tif, dir, cv)) return false; } else { sbyte[] cp=ap[0] as sbyte[]; if(cp==null) return false; if(!TIFFWriteByteArray(tif, dir, cp)) return false; } } } break; case TIFFDataType.TIFF_UNDEFINED: { object[] ap=new object[2]; TIFFGetField(tif, fip.field_tag, ap); byte[] cp; if((int)wc==TIFF_VARIABLE) { // Assume TIFF_VARIABLE dir.tdir_count=wc=__GetAsUint(ap, 0); cp=ap[1] as byte[]; if(cp==null) return false; } else { cp=ap[0] as byte[]; if(cp==null) return false; } if(!TIFFWriteByteArray(tif, dir, cp)) return false; } break; case TIFFDataType.TIFF_NOTYPE: break; } return true; }
// Fetch an array of BYTE values. static bool TIFFFetchByteArray(TIFF tif, TIFFDirEntry dir, byte[] v) { if(dir.tdir_count<=4) { // Extract data from offset field. if(tif.tif_header.tiff_magic==TIFF_BIGENDIAN) { switch(dir.tdir_count) { case 4: v[3]=(byte)(dir.tdir_offset&0xff); goto case 3; case 3: v[2]=(byte)((dir.tdir_offset>>8)&0xff); goto case 2; case 2: v[1]=(byte)((dir.tdir_offset>>16)&0xff); goto case 1; case 1: v[0]=(byte)(dir.tdir_offset>>24); break; } } else { switch(dir.tdir_count) { case 4: v[3]=(byte)(dir.tdir_offset>>24); goto case 3; case 3: v[2]=(byte)((dir.tdir_offset>>16)&0xff); goto case 2; case 2: v[1]=(byte)((dir.tdir_offset>>8)&0xff); goto case 1; case 1: v[0]=(byte)(dir.tdir_offset&0xff); break; } } return true; } return TIFFFetchData(tif, dir, v)!=0; }
static bool TIFFWriteInkNames(TIFF tif, TIFFDirEntry dir) { TIFFDirectory td=tif.tif_dir; dir.tdir_tag=(ushort)TIFFTAG.INKNAMES; dir.tdir_type=(ushort)TIFFDataType.TIFF_ASCII; string cp=td.td_inknames; cp=cp.TrimEnd('\0'); cp+='\0'; dir.tdir_count=(uint)cp.Length; return TIFFWriteByteArray(tif, dir, Encoding.ASCII.GetBytes(cp)); }
// Setup a directory entry of an array of // SLONG and write the associated indirect values. static bool TIFFWriteLongArray(TIFF tif, TIFFDirEntry dir, int[] v) { if(dir.tdir_count==1) { dir.tdir_offset=(uint)v[0]; return true; } byte[] buf=new byte[dir.tdir_count*4]; for(int i=0; i<dir.tdir_count; i++) BitConverter.GetBytes(v[i]).CopyTo(buf, i*4); return TIFFWriteData(tif, dir, buf); }
static bool TIFFWriteFloatArray(TIFF tif, TIFFDirEntry dir, float[] v) { byte[] buf=new byte[dir.tdir_count*4]; for(int i=0; i<dir.tdir_count; i++) BitConverter.GetBytes(v[i]).CopyTo(buf, i*4); if(dir.tdir_count==1) { dir.tdir_offset=((uint)buf[3]<<24)+((uint)buf[2]<<16)+((uint)buf[1]<<8)+(uint)buf[0]; return true; } return TIFFWriteData(tif, dir, buf); }
static bool TIFFWriteDoubleArray(TIFF tif, TIFFDirEntry dir, double[] v) { byte[] buf=new byte[dir.tdir_count*8]; for(int i=0; i<dir.tdir_count; i++) BitConverter.GetBytes(v[i]).CopyTo(buf, i*8); return TIFFWriteData(tif, dir, buf); }
// Write a contiguous directory item. static bool TIFFWriteData(TIFF tif, TIFFDirEntry dir, byte[] cp) { if((tif.tif_flags&TIF_FLAGS.TIFF_SWAB)!=0) { switch((TIFFDataType)dir.tdir_type) { case TIFFDataType.TIFF_SHORT: case TIFFDataType.TIFF_SSHORT: TIFFSwabArrayOfShort(cp, dir.tdir_count); break; case TIFFDataType.TIFF_LONG: case TIFFDataType.TIFF_SLONG: case TIFFDataType.TIFF_FLOAT: TIFFSwabArrayOfLong(cp, dir.tdir_count); break; case TIFFDataType.TIFF_RATIONAL: case TIFFDataType.TIFF_SRATIONAL: TIFFSwabArrayOfLong(cp, 2*dir.tdir_count); break; case TIFFDataType.TIFF_DOUBLE: TIFFSwabArrayOfDouble(cp, dir.tdir_count); break; } } dir.tdir_offset=tif.tif_dataoff; int cc=(int)dir.tdir_count*TIFFDataWidth((TIFFDataType)dir.tdir_type); if(SeekOK(tif, dir.tdir_offset)&&WriteOK(tif, cp, cc)) { tif.tif_dataoff+=(uint)((cc+1)&~1); return true; } TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error writing data for field \"{0}\"", TIFFFieldWithTag(tif, (TIFFTAG)dir.tdir_tag).field_name); return false; }
// Fetch an array of SLONG values. static bool TIFFFetchLongArray(TIFF tif, TIFFDirEntry dir, int[] v) { if(dir.tdir_count==1) { v[0]=(int)dir.tdir_offset; return true; } byte[] buf=new byte[dir.tdir_count*4]; if(TIFFFetchData(tif, dir, buf)==0) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=BitConverter.ToInt32(buf, i*4); return true; }
// Fetch an array of SBYTE values. static bool TIFFFetchByteArray(TIFF tif, TIFFDirEntry dir, sbyte[] v) { if(dir.tdir_count<=4) { // Extract data from offset field. if(tif.tif_header.tiff_magic==TIFF_BIGENDIAN) { switch(dir.tdir_count) { case 4: v[3]=(sbyte)(dir.tdir_offset&0xff); goto case 3; case 3: v[2]=(sbyte)((dir.tdir_offset>>8)&0xff); goto case 2; case 2: v[1]=(sbyte)((dir.tdir_offset>>16)&0xff); goto case 1; case 1: v[0]=(sbyte)(dir.tdir_offset>>24); break; } } else { switch(dir.tdir_count) { case 4: v[3]=(sbyte)(dir.tdir_offset>>24); goto case 3; case 3: v[2]=(sbyte)((dir.tdir_offset>>16)&0xff); goto case 2; case 2: v[1]=(sbyte)((dir.tdir_offset>>8)&0xff); goto case 1; case 1: v[0]=(sbyte)(dir.tdir_offset&0xff); break; } } return true; } byte[] buf=new byte[dir.tdir_count]; if(TIFFFetchData(tif, dir, buf)==0) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=(sbyte)buf[i]; return true; }
// Fetch a tag that is not handled by special case code. static bool TIFFFetchNormalTag(TIFF tif, TIFFDirEntry dp) { bool ok=false; TIFFFieldInfo fip=TIFFFieldWithTag(tif, (TIFFTAG)dp.tdir_tag); if(dp.tdir_count>1) { // array of values object cp=null; try { switch((TIFFDataType)dp.tdir_type) { case TIFFDataType.TIFF_UNDEFINED: case TIFFDataType.TIFF_BYTE: cp=new byte[dp.tdir_count]; ok=TIFFFetchByteArray(tif, dp, (byte[])cp); break; case TIFFDataType.TIFF_SBYTE: cp=new sbyte[dp.tdir_count]; ok=TIFFFetchByteArray(tif, dp, (sbyte[])cp); break; case TIFFDataType.TIFF_SHORT: cp=new ushort[dp.tdir_count]; ok=TIFFFetchShortArray(tif, dp, (ushort[])cp); break; case TIFFDataType.TIFF_SSHORT: cp=new short[dp.tdir_count]; ok=TIFFFetchShortArray(tif, dp, (short[])cp); break; case TIFFDataType.TIFF_LONG: cp=new uint[dp.tdir_count]; ok=TIFFFetchLongArray(tif, dp, (uint[])cp); break; case TIFFDataType.TIFF_SLONG: cp=new int[dp.tdir_count]; ok=TIFFFetchLongArray(tif, dp, (int[])cp); break; case TIFFDataType.TIFF_RATIONAL: case TIFFDataType.TIFF_SRATIONAL: cp=new double[dp.tdir_count]; ok=TIFFFetchRationalArray(tif, dp, (double[])cp); break; case TIFFDataType.TIFF_FLOAT: cp=new float[dp.tdir_count]; ok=TIFFFetchFloatArray(tif, dp, (float[])cp); break; case TIFFDataType.TIFF_DOUBLE: cp=new double[dp.tdir_count]; ok=TIFFFetchDoubleArray(tif, dp, (double[])cp); break; case TIFFDataType.TIFF_ASCII: // Some vendors write strings w/o the trailing // NIL byte, so always append one just in case. string str; ok=TIFFFetchString(tif, dp, out str)!=0; if(ok) { str=str.TrimEnd('\0'); str+='\0'; // XXX paranoid cp=str; } break; } } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "No space to fetch tag value"); } if(ok) { if(fip.field_passcount) ok=TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, dp.tdir_count, cp); else { //if(cp is Array) //{ // Array arr=cp as Array; // object[] ap=new object[arr.Length]; // int i=0; // foreach(object a in arr) ap[i++]=a; // ok=TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, ap); //} //else ok=TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, cp); } } } else if(CheckDirCount(tif, dp, 1)) { // singleton value switch((TIFFDataType)dp.tdir_type) { case TIFFDataType.TIFF_UNDEFINED: case TIFFDataType.TIFF_BYTE: case TIFFDataType.TIFF_SBYTE: case TIFFDataType.TIFF_SHORT: case TIFFDataType.TIFF_SSHORT: // If the tag is also acceptable as a LONG or SLONG // then TIFFSetField will expect an uint parameter // passed to it. // // NB: We used TIFFFieldWithTag here (see above) knowing that // it returns us the first entry in the table // for the tag and that that entry is for the // widest potential data type the tag may have. TIFFDataType type=fip.field_type; if(type!=TIFFDataType.TIFF_LONG&&type!=TIFFDataType.TIFF_SLONG) { ushort v=(ushort)((tif.tif_header.tiff_magic==TIFF_BIGENDIAN)?(dp.tdir_offset>>tif_typeshift[dp.tdir_type])&tif_typemask[dp.tdir_type]:dp.tdir_offset&tif_typemask[dp.tdir_type]); ok=(fip.field_passcount?TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, 1, v):TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, v)); break; } goto case TIFFDataType.TIFF_LONG; // fall thru... case TIFFDataType.TIFF_LONG: case TIFFDataType.TIFF_SLONG: { uint v32=(uint)((tif.tif_header.tiff_magic==TIFF_BIGENDIAN)?(dp.tdir_offset>>tif_typeshift[dp.tdir_type])&tif_typemask[dp.tdir_type]:dp.tdir_offset&tif_typemask[dp.tdir_type]); ok=(fip.field_passcount?TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, 1, v32):TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, v32)); } break; case TIFFDataType.TIFF_RATIONAL: case TIFFDataType.TIFF_SRATIONAL: { double v=TIFFFetchRational(tif, dp); ok=(fip.field_passcount?TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, 1, v):TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, v)); } break; case TIFFDataType.TIFF_FLOAT: { float v=TIFFFetchFloat(tif, dp); ok=(fip.field_passcount?TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, 1, v):TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, v)); } break; case TIFFDataType.TIFF_DOUBLE: { double v=TIFFFetchDouble(tif, dp); ok=(fip.field_passcount?TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, 1, v):TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, v)); } break; case TIFFDataType.TIFF_ASCII: { string str; ok=TIFFFetchString(tif, dp, out str)!=0; if(ok) { str=str.TrimEnd('\0'); str+='\0'; // XXX paranoid ok=(fip.field_passcount?TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, 1, str):TIFFSetField(tif, (TIFFTAG)dp.tdir_tag, (TIFFDataType)dp.tdir_type, str)); } } break; } } return ok; }
// Fetch a contiguous directory item. static int TIFFFetchData(TIFF tif, TIFFDirEntry dir, byte[] cp) { uint w=(uint)TIFFDataWidth((TIFFDataType)dir.tdir_type); // FIXME: butecount should have tsize_t type, but for now libtiff // defines tsize_t as a signed 32-bit integer and we are losing // ability to read arrays larger than 2^31 bytes. So we are using // uint32 instead of tsize_t here. uint cc=dir.tdir_count*w; // Check for overflow. if(dir.tdir_count==0||w==0||cc/w!=dir.tdir_count) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error fetching data for field \"{0}\"", TIFFFieldWithTag(tif, (TIFFTAG)dir.tdir_tag).field_name); return 0; } if(!SeekOK(tif, dir.tdir_offset)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error fetching data for field \"{0}\"", TIFFFieldWithTag(tif, (TIFFTAG)dir.tdir_tag).field_name); return 0; } if(!ReadOK(tif, cp, (int)cc)) { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "Error fetching data for field \"{0}\"", TIFFFieldWithTag(tif, (TIFFTAG)dir.tdir_tag).field_name); return 0; } if((tif.tif_flags&TIF_FLAGS.TIFF_SWAB)==0) return (int)cc; switch((TIFFDataType)dir.tdir_type) { case TIFFDataType.TIFF_SHORT: case TIFFDataType.TIFF_SSHORT: TIFFSwabArrayOfShort(cp, dir.tdir_count); break; case TIFFDataType.TIFF_LONG: case TIFFDataType.TIFF_SLONG: case TIFFDataType.TIFF_FLOAT: TIFFSwabArrayOfLong(cp, dir.tdir_count); break; case TIFFDataType.TIFF_RATIONAL: case TIFFDataType.TIFF_SRATIONAL: TIFFSwabArrayOfLong(cp, 2*dir.tdir_count); break; case TIFFDataType.TIFF_DOUBLE: TIFFSwabArrayOfDouble(cp, dir.tdir_count); break; } return (int)cc; }
// Fetch a rational item from the file // at offset off and return the value // as a floating point number. static double TIFFFetchRational(TIFF tif, TIFFDirEntry dir) { byte[] buf=new byte[8]; if(TIFFFetchData(tif, dir, buf)==0) return 1.0; double v; return !cvtRational(tif, dir, BitConverter.ToUInt32(buf, 0), BitConverter.ToUInt32(buf, 4), out v)?1.0:v; }
static int TIFFFetchData(TIFF tif, TIFFDirEntry dir, ushort[] cp) { byte[] buf=new byte[dir.tdir_count*2]; int ret=TIFFFetchData(tif, dir, buf); if(ret==0) return 0; for(int i=0; i<dir.tdir_count; i++) cp[i]=BitConverter.ToUInt16(buf, i*2); return ret; }
// Fetch and set the RefBlackWhite tag. static bool TIFFFetchRefBlackWhite(TIFF tif, TIFFDirEntry dir) { if(dir.tdir_type==(ushort)TIFFDataType.TIFF_RATIONAL) return TIFFFetchNormalTag(tif, dir); // Handle LONG's for backward compatibility. uint[] cp=null; double[] fp=null; try { cp=new uint[dir.tdir_count]; fp=new double[dir.tdir_count]; } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "No space for \"ReferenceBlackWhite\" array"); return false; } if(!TIFFFetchLongArray(tif, dir, cp)) return false; for(int i=0; i<dir.tdir_count; i++) fp[i]=cp[i]; if(!TIFFSetField(tif, (TIFFTAG)dir.tdir_tag, (TIFFDataType)dir.tdir_type, fp)) return false; return true; }
// Fetch a double item from the file // at offset off and return the value. static double TIFFFetchDouble(TIFF tif, TIFFDirEntry dir) { byte[] buf=new byte[8]; if(TIFFFetchData(tif, dir, buf)==0) return 1.0; return BitConverter.ToDouble(buf, 0); }
// Fetch a pair of SHORT or BYTE values. Some tags may have either BYTE // or SHORT type and this function works with both ones. static bool TIFFFetchShortPair(TIFF tif, TIFFDirEntry dir) { // Prevent overflowing the v stack arrays below by performing a sanity // check on tdir_count, this should never be greater than two. if(dir.tdir_count>2) { TIFFWarningExt(tif.tif_clientdata, tif.tif_name, "unexpected count for field \"{0}\", {1}, expected 2; ignored", TIFFFieldWithTag(tif, (TIFFTAG)dir.tdir_tag).field_name, dir.tdir_count); return false; } switch((TIFFDataType)dir.tdir_type) { case TIFFDataType.TIFF_BYTE: { byte[] v=new byte[4]; return TIFFFetchByteArray(tif, dir, v)&&TIFFSetField(tif, (TIFFTAG)dir.tdir_tag, (TIFFDataType)dir.tdir_type, v[0], v[1]); } case TIFFDataType.TIFF_SBYTE: { sbyte[] v=new sbyte[4]; return TIFFFetchByteArray(tif, dir, v)&&TIFFSetField(tif, (TIFFTAG)dir.tdir_tag, (TIFFDataType)dir.tdir_type, v[0], v[1]); } case TIFFDataType.TIFF_SHORT: { ushort[] v=new ushort[2]; return TIFFFetchShortArray(tif, dir, v)&&TIFFSetField(tif, (TIFFTAG)dir.tdir_tag, (TIFFDataType)dir.tdir_type, v[0], v[1]); } case TIFFDataType.TIFF_SSHORT: { short[] v=new short[2]; return TIFFFetchShortArray(tif, dir, v)&&TIFFSetField(tif, (TIFFTAG)dir.tdir_tag, (TIFFDataType)dir.tdir_type, v[0], v[1]); } default: return false; } }
// Fetch an array of DOUBLE values. static bool TIFFFetchDoubleArray(TIFF tif, TIFFDirEntry dir, double[] v) { byte[] buf=new byte[dir.tdir_count*8]; if(TIFFFetchData(tif, dir, buf)==0) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=BitConverter.ToDouble(buf, i*8); return true; }
// Fetch a set of offsets or lengths. // While this routine says "strips", in fact it's also used for tiles. static bool TIFFFetchStripThing(TIFF tif, TIFFDirEntry dir, uint nstrips, ref uint[] lp) { if(!CheckDirCount(tif, dir, nstrips)) return false; // Allocate space for strip information. if(lp==null) { try { lp=new uint[nstrips]; } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "No space for strip array"); return false; } } else for(int i=0; i<nstrips; i++) lp[i]=0; if(dir.tdir_type==(ushort)TIFFDataType.TIFF_SHORT) { // Handle uint16=>uint32 expansion. ushort[] dp=null; try { dp=new ushort[dir.tdir_count]; } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "No space to fetch strip tag"); return false; } if(TIFFFetchShortArray(tif, dir, dp)) { for(int i=0; i<nstrips&&i<(int)dir.tdir_count; i++) lp[i]=dp[i]; } return true; } if(nstrips!=(int)dir.tdir_count) { // Special case to incorrect length uint[] dp=null; try { dp=new uint[dir.tdir_count]; } catch { TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "No space to fetch strip tag"); return false; } if(TIFFFetchLongArray(tif, dir, dp)) { for(int i=0; i<nstrips&&i<(int)dir.tdir_count; i++) lp[i]=dp[i]; } } return TIFFFetchLongArray(tif, dir, lp); }
// Fetch a single floating point value // from the offset field and return it // as a native float. static float TIFFFetchFloat(TIFF tif, TIFFDirEntry dir) { return BitConverter.ToSingle(BitConverter.GetBytes(dir.tdir_offset), 0); }
// Check the count field of a directory // entry against a known value. The caller // is expected to skip/ignore the tag if // there is a mismatch. static bool CheckDirCount(TIFF tif, TIFFDirEntry dir, uint count) { if(count>dir.tdir_count) { TIFFWarningExt(tif.tif_clientdata, tif.tif_name, "incorrect count for field \"{0}\" ({1}, expecting {2}); tag ignored", TIFFFieldWithTag(tif, (TIFFTAG)dir.tdir_tag).field_name, dir.tdir_count, count); return false; } if(count<dir.tdir_count) { TIFFWarningExt(tif.tif_clientdata, tif.tif_name, "incorrect count for field \"{0}\" ({1}, expecting {2}); tag trimmed", TIFFFieldWithTag(tif, (TIFFTAG)dir.tdir_tag).field_name, dir.tdir_count, count); return true; } return true; }
// Fetch an array of FLOAT values. static bool TIFFFetchFloatArray(TIFF tif, TIFFDirEntry dir, float[] v) { if(dir.tdir_count==1) { v[0]=BitConverter.ToSingle(BitConverter.GetBytes(dir.tdir_offset), 0); return true; } byte[] buf=new byte[dir.tdir_count*4]; if(TIFFFetchData(tif, dir, buf)==0) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=BitConverter.ToSingle(buf, i*4); return true; }
// Fetch an array of ANY values. The actual values are // returned as doubles which should be able hold all the // types. Yes, there really should be an tany_t to avoid // this potential non-portability ... Note in particular // that we assume that the double return value vector is // large enough to read in any fundamental type. We use // that vector as a buffer to read in the base type vector // and then convert it in place to double (from end // to front of course). static bool TIFFFetchAnyArray(TIFF tif, TIFFDirEntry dir, double[] v) { switch((TIFFDataType)dir.tdir_type) { case TIFFDataType.TIFF_BYTE: { byte[] buf=new byte[dir.tdir_count]; if(!TIFFFetchByteArray(tif, dir, buf)) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=buf[i]; } break; case TIFFDataType.TIFF_SBYTE: { sbyte[] buf=new sbyte[dir.tdir_count]; if(!TIFFFetchByteArray(tif, dir, buf)) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=buf[i]; } break; case TIFFDataType.TIFF_SHORT: { ushort[] buf=new ushort[dir.tdir_count]; if(!TIFFFetchShortArray(tif, dir, buf)) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=buf[i]; } break; case TIFFDataType.TIFF_SSHORT: { short[] buf=new short[dir.tdir_count]; if(!TIFFFetchShortArray(tif, dir, buf)) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=buf[i]; } break; case TIFFDataType.TIFF_LONG: { uint[] buf=new uint[dir.tdir_count]; if(!TIFFFetchLongArray(tif, dir, buf)) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=buf[i]; } break; case TIFFDataType.TIFF_SLONG: { int[] buf=new int[dir.tdir_count]; if(!TIFFFetchLongArray(tif, dir, buf)) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=buf[i]; } break; case TIFFDataType.TIFF_RATIONAL: case TIFFDataType.TIFF_SRATIONAL: return TIFFFetchRationalArray(tif, dir, v); case TIFFDataType.TIFF_FLOAT: { float[] buf=new float[dir.tdir_count]; if(!TIFFFetchFloatArray(tif, dir, buf)) return false; for(int i=0; i<dir.tdir_count; i++) v[i]=buf[i]; } break; case TIFFDataType.TIFF_DOUBLE: return TIFFFetchDoubleArray(tif, dir, v); default: // TIFF_NOTYPE // TIFF_ASCII // TIFF_UNDEFINED TIFFErrorExt(tif.tif_clientdata, tif.tif_name, "cannot read TIFF_ANY type {0} for field \"{1}\"", dir.tdir_type, TIFFFieldWithTag(tif, (TIFFTAG)dir.tdir_tag).field_name); return false; } return true; }
// Write/copy data associated with an ASCII or opaque tag value. static bool TIFFWriteByteArray(TIFF tif, TIFFDirEntry dir, byte[] cp) { if(dir.tdir_count<=4) { //was _TIFFmemcpy(&dir.tdir_offset, cp, dir.tdir_count); dir.tdir_offset=0; if(tif.tif_header.tiff_magic==TIFF_BIGENDIAN) { dir.tdir_offset=(uint)cp[0]<<24; if(dir.tdir_count>=2) dir.tdir_offset|=(uint)cp[1]<<16; if(dir.tdir_count>=3) dir.tdir_offset|=(uint)cp[2]<<8; if(dir.tdir_count==4) dir.tdir_offset|=cp[3]; } else { switch(dir.tdir_count) { case 4: dir.tdir_offset=cp[3]; dir.tdir_offset<<=8; goto case 3; case 3: dir.tdir_offset+=cp[2]; dir.tdir_offset<<=8; goto case 2; case 2: dir.tdir_offset+=cp[1]; dir.tdir_offset<<=8; goto case 1; case 1: dir.tdir_offset+=cp[0]; break; } } return true; } return TIFFWriteData(tif, dir, cp); }