📄 tiff_filewriter.cpp
字号:
return true; } // FlipDSDTable// =================================================================================================// FlipOECFSFRTable// ================//// The OECF and SFR tables have the same layout:// 2 short counts, columns and rows// c ASCII strings, null terminated, column names// c*r rationalsstatic bool FlipOECFSFRTable ( void* voidPtr, XMP_Uns32 tagLen, GetUns16_Proc GetUns16 ){ XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr; Flip2 ( &u16Ptr[0] ); // Flip the data to match the master TIFF. Flip2 ( &u16Ptr[1] ); XMP_Uns16 columns = GetUns16 ( &u16Ptr[0] ); // Fetch using the master TIFF's routine. XMP_Uns16 rows = GetUns16 ( &u16Ptr[1] ); XMP_Uns32 minLen = 4 + columns + (8 * columns * rows); // Minimum legit tag size. if ( tagLen < minLen ) return false; // Compute the start of the rationals from the end of value. No need to walk through the names. XMP_Uns32* u32Ptr = (XMP_Uns32*) ((XMP_Uns8*)voidPtr + tagLen - (8 * columns * rows)); for ( size_t i = 2*columns*rows; i > 0; --i, ++u32Ptr ) Flip4 ( u32Ptr ); return true; } // FlipOECFSFRTable// =================================================================================================// TIFF_FileWriter::ProcessPShop6IFD// =================================//// Photoshop 6 wrote wacky TIFF files that have much of the Exif metadata buried inside of image// resource 1058, which is itself within tag 34377 in the 0th IFD. This routine moves the buried// tags up to the parent file. Existing tags are not replaced.//// While it is tempting to try to directly use the TIFF_MemoryReader's tweaked IFD info, making that// visible would compromise implementation separation. Better to pay the modest runtime cost of // using the official GetIFD method, letting it build the map.//// The tags that get moved are marked as being changed, as is the IFD they are moved into, but the// overall TIFF_FileWriter object is not. We don't want this integration on its own to force a file// update, but a file update should include these changes.// ! Be careful to not move tags that are the nasty Exif explicit offsets, e.g. the Exif or GPS IFD// ! "pointers". These are tags with a LONG type and count of 1, whose value is an offset into the// ! buried TIFF stream. We can't reliably plant that offset into the outer IFD structure.// ! To make things even more fun, the buried Exif might not have the same endianness as the outer!void TIFF_FileWriter::ProcessPShop6IFD ( const TIFF_MemoryReader& buriedExif, XMP_Uns8 ifd ){ bool ok, found; TagInfoMap ps6IFD; found = buriedExif.GetIFD ( ifd, &ps6IFD ); if ( ! found ) return; bool needsFlipping = (this->bigEndian != buriedExif.IsBigEndian()); InternalIFDInfo* masterIFD = &this->containedIFDs[ifd]; TagInfoMap::const_iterator ps6Pos = ps6IFD.begin(); TagInfoMap::const_iterator ps6End = ps6IFD.end(); for ( ; ps6Pos != ps6End; ++ps6Pos ) { // Copy buried tags to the master IFD if they don't already exist there. const TagInfo& ps6Tag = ps6Pos->second; if ( this->FindTagInIFD ( ifd, ps6Tag.id ) != 0 ) continue; // Keep existing master tags. if ( needsFlipping && (ps6Tag.id == 37500) ) continue; // Don't copy an unflipped MakerNote. if ( (ps6Tag.id == kTIFF_ExifIFDPointer) || // Skip the tags that are explicit offsets. (ps6Tag.id == kTIFF_GPSInfoIFDPointer) || (ps6Tag.id == kTIFF_JPEGInterchangeFormat) || (ps6Tag.id == kTIFF_InteroperabilityIFDPointer) ) continue; void* voidPtr = CopyTagToMasterIFD ( ps6Tag, masterIFD ); if ( needsFlipping ) { switch ( ps6Tag.type ) { case kTIFF_ByteType: case kTIFF_SByteType: case kTIFF_ASCIIType: // Nothing more to do. break; case kTIFF_ShortType: case kTIFF_SShortType: { XMP_Uns16* u16Ptr = (XMP_Uns16*)voidPtr; for ( size_t i = ps6Tag.count; i > 0; --i, ++u16Ptr ) Flip2 ( u16Ptr ); } break; case kTIFF_LongType: case kTIFF_SLongType: case kTIFF_FloatType: { XMP_Uns32* u32Ptr = (XMP_Uns32*)voidPtr; for ( size_t i = ps6Tag.count; i > 0; --i, ++u32Ptr ) Flip4 ( u32Ptr ); } break; case kTIFF_RationalType: case kTIFF_SRationalType: { XMP_Uns32* ratPtr = (XMP_Uns32*)voidPtr; for ( size_t i = (2 * ps6Tag.count); i > 0; --i, ++ratPtr ) Flip4 ( ratPtr ); } break; case kTIFF_DoubleType: { XMP_Uns64* u64Ptr = (XMP_Uns64*)voidPtr; for ( size_t i = ps6Tag.count; i > 0; --i, ++u64Ptr ) Flip8 ( u64Ptr ); } break; case kTIFF_UndefinedType: // Fix up the few kinds of special tables that Exif 2.2 defines. ok = true; // Keep everything that isn't a special table. if ( ps6Tag.id == kTIFF_CFAPattern ) { ok = FlipCFATable ( voidPtr, ps6Tag.dataLen, this->GetUns16 ); } else if ( ps6Tag.id == kTIFF_DeviceSettingDescription ) { ok = FlipDSDTable ( voidPtr, ps6Tag.dataLen, this->GetUns16 ); } else if ( (ps6Tag.id == kTIFF_OECF) || (ps6Tag.id == kTIFF_SpatialFrequencyResponse) ) { ok = FlipOECFSFRTable ( voidPtr, ps6Tag.dataLen, this->GetUns16 ); } if ( ! ok ) this->DeleteTag ( ifd, ps6Tag.id ); break; default: // ? XMP_Throw ( "Unexpected tag type", kXMPErr_InternalFailure ); this->DeleteTag ( ifd, ps6Tag.id ); break; } } } } // TIFF_FileWriter::ProcessPShop6IFD// =================================================================================================// TIFF_FileWriter::DetermineAppendInfo// ====================================#ifndef Trace_DetermineAppendInfo #define Trace_DetermineAppendInfo 0#endifXMP_Uns32 TIFF_FileWriter::DetermineAppendInfo ( XMP_Uns32 appendedOrigin, bool appendedIFDs[kTIFF_KnownIFDCount], XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount], bool appendAll /* = false */ ){ XMP_Uns32 appendedLength = 0; XMP_Assert ( (appendedOrigin & 1) == 0 ); // Make sure it is even. #if Trace_DetermineAppendInfo { printf ( "\nEntering TIFF_FileWriter::DetermineAppendInfo%s\n", (appendAll ? ", append all" : "") ); for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; printf ( "\n IFD %d, origCount %d, map.size %d, origOffset %d (0x%X), origNextIFD %d (0x%X)", ifd, thisIFD.origCount, thisIFD.tagMap.size(), thisIFD.origOffset, thisIFD.origOffset, thisIFD.origNextIFD, thisIFD.origNextIFD ); if ( thisIFD.changed ) printf ( ", changed" ); if ( thisIFD.origCount < thisIFD.tagMap.size() ) printf ( ", should get appended" ); printf ( "\n" ); InternalTagMap::iterator tagPos; InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { InternalTagInfo & thisTag = tagPos->second; printf ( " Tag %d, dataOrOffset 0x%X, origLen %d, origOffset %d (0x%X)", thisTag.id, thisTag.dataOrOffset, thisTag.origLen, thisTag.origOffset, thisTag.origOffset ); if ( thisTag.changed ) printf ( ", changed" ); if ( (thisTag.dataLen > thisTag.origLen) && (thisTag.dataLen > 4) ) printf ( ", should get appended" ); printf ( "\n" ); } } printf ( "\n" ); } #endif // Determine which of the IFDs will be appended. If the Exif, GPS, or Interoperability IFDs are // appended, set dummy values for their offsets in the "owning" IFD. This must be done first // since this might cause the owning IFD to grow. if ( ! appendAll ) { for ( int i = 0; i < kTIFF_KnownIFDCount ;++i ) appendedIFDs[i] = false; } else { for ( int i = 0; i < kTIFF_KnownIFDCount ;++i ) appendedIFDs[i] = (this->containedIFDs[i].tagMap.size() > 0); } appendedIFDs[kTIFF_InteropIFD] |= (this->containedIFDs[kTIFF_InteropIFD].origCount < this->containedIFDs[kTIFF_InteropIFD].tagMap.size()); if ( appendedIFDs[kTIFF_InteropIFD] ) { this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, 0xABADABAD ); } appendedIFDs[kTIFF_GPSInfoIFD] |= (this->containedIFDs[kTIFF_GPSInfoIFD].origCount < this->containedIFDs[kTIFF_GPSInfoIFD].tagMap.size()); if ( appendedIFDs[kTIFF_GPSInfoIFD] ) { this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, 0xABADABAD ); } appendedIFDs[kTIFF_ExifIFD] |= (this->containedIFDs[kTIFF_ExifIFD].origCount < this->containedIFDs[kTIFF_ExifIFD].tagMap.size()); if ( appendedIFDs[kTIFF_ExifIFD] ) { this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, 0xABADABAD ); } appendedIFDs[kTIFF_TNailIFD] |= (this->containedIFDs[kTIFF_TNailIFD].origCount < this->containedIFDs[kTIFF_TNailIFD].tagMap.size()); appendedIFDs[kTIFF_PrimaryIFD] |= (this->containedIFDs[kTIFF_PrimaryIFD].origCount < this->containedIFDs[kTIFF_PrimaryIFD].tagMap.size()); // The appended data (if any) will be a sequence of an IFD followed by its large values. // Determine the new offsets for the appended IFDs and tag values, and the total amount of // appended stuff. for ( int ifd = 0; ifd < kTIFF_KnownIFDCount ;++ifd ) { InternalIFDInfo& ifdInfo ( this->containedIFDs[ifd] ); size_t tagCount = ifdInfo.tagMap.size(); if ( ! (appendAll | ifdInfo.changed) ) continue; if ( tagCount == 0 ) continue; newIFDOffsets[ifd] = ifdInfo.origOffset; if ( appendedIFDs[ifd] ) { newIFDOffsets[ifd] = appendedOrigin + appendedLength; appendedLength += 6 + (12 * tagCount); } InternalTagMap::iterator tagPos = ifdInfo.tagMap.begin(); InternalTagMap::iterator tagEnd = ifdInfo.tagMap.end(); for ( ; tagPos != tagEnd; ++tagPos ) { InternalTagInfo & currTag ( tagPos->second ); if ( (! (appendAll | currTag.changed)) || (currTag.dataLen <= 4) ) continue; if ( (currTag.dataLen <= currTag.origLen) && (! appendAll) ) { this->PutUns32 ( currTag.origOffset, &currTag.dataOrOffset ); // Reuse the old space. } else { this->PutUns32 ( (appendedOrigin + appendedLength), &currTag.dataOrOffset ); // Set the appended offset. appendedLength += ((currTag.dataLen + 1) & 0xFFFFFFFEUL); // Round to an even size. } } } // If the Exif, GPS, or Interoperability IFDs get appended, update the tag values for their new offsets. if ( appendedIFDs[kTIFF_ExifIFD] ) { this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_ExifIFDPointer, newIFDOffsets[kTIFF_ExifIFD] ); } if ( appendedIFDs[kTIFF_GPSInfoIFD] ) { this->SetTag_Long ( kTIFF_PrimaryIFD, kTIFF_GPSInfoIFDPointer, newIFDOffsets[kTIFF_GPSInfoIFD] ); } if ( appendedIFDs[kTIFF_InteropIFD] ) { this->SetTag_Long ( kTIFF_ExifIFD, kTIFF_InteroperabilityIFDPointer, newIFDOffsets[kTIFF_InteropIFD] ); } #if Trace_DetermineAppendInfo { printf ( "Exiting TIFF_FileWriter::DetermineAppendInfo\n" ); for ( int ifd = 0; ifd < kTIFF_KnownIFDCount; ++ifd ) { InternalIFDInfo & thisIFD = this->containedIFDs[ifd]; printf ( "\n IFD %d, origCount %d, map.size %d, origOffset %d (0x%X), origNextIFD %d (0x%X)", ifd, thisIFD.origCount, thisIFD.tagMap.size(), thisIFD.origOffset, thisIFD.origOffset, thisIFD.origNextIFD, thisIFD.origNextIFD ); if ( thisIFD.changed ) printf ( ", changed" ); if ( appendedIFDs[ifd] ) printf ( ", will be appended at %d (0x%X)", newIFDOffsets[ifd], newIFDOffsets[ifd] ); printf ( "\n" ); InternalTagMap::iterator tagPos; InternalTagMap::iterator tagEnd = thisIFD.tagMap.end(); for ( tagPos = thisIFD.tagMap.begin(); tagPos != tagEnd; ++tagPos ) { InternalTagInfo & thisTag = tagPos->second; printf ( " Tag %d, dataOrOffset 0x%X, origLen %d, origOffset %d (0x%X)", thisTag.id, thisTag.dataOrOffset, thisTag.origLen, thisTag.origOffset, thisTag.origOffset ); if ( thisTag.changed ) printf ( ", changed" ); if ( (thisTag.dataLen > thisTag.origLen) && (thisTag.dataLen > 4) ) { XMP_Uns32 newOffset = this->GetUns32 ( &thisTag.dataOrOffset ); printf ( ", will be appended at %d (0x%X)", newOffset, newOffset ); } printf ( "\n" ); } } printf ( "\n" ); } #endif return appendedLength; } // TIFF_FileWriter::DetermineAppendInfo// =================================================================================================// TIFF_FileWriter::UpdateMemByAppend// ==================================//// Normally we update TIFF in a conservative "by-append" manner. Changes are written in-place where// they fit, anything requiring growth is appended to the end and the old space is abandoned. The// end for memory-based TIFF is the end of the data block, the end for file-based TIFF is the end of// the file. This update-by-append model has the advantage of not perturbing any hidden offsets, a// common feature of proprietary MakerNotes.//// When doing the update-by-append we're only going to be modifying things that have changed. This// means IFDs with changed, added, or deleted tags, and large values for changed or added tags. The// IFDs and tag values are updated in-place if they fit, leaving holes in the stream if the new// value is smaller than the old.// ** Someday we might want to use the FreeOffsets and FreeByteCounts tags to track free space.// ** Probably not a huge win in practice though, and the TIFF spec says they are not recommended// ** for general interchange use.void TIFF_FileWriter::UpdateMemByAppend ( XMP_Uns8** newStream_out, XMP_Uns32* newLength_out, bool appendAll /* = false */, XMP_Uns32 extraSpace /* = 0 */ ){ bool appendedIFDs[kTIFF_KnownIFDCount]; XMP_Uns32 newIFDOffsets[kTIFF_KnownIFDCount]; XMP_Uns32 appendedOrigin = ((this->tiffLength + 1) & 0xFFFFFFFEUL); // Start at an even offset. XMP_Uns32 appendedLength = DetermineAppendInfo ( appendedOrigin, appendedIFDs, newIFDOffsets, appendAll );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -