I have a DNG file from an Autel drone, and it contains some Opcodes in a SubIFD. In the OpcodeList2
tag it has FixVignetteRadial
, and in OpcodeList3
it has WarpRectilinear
. I would like to process the files using Darktable, but it only looks in OpcodeList3
for the FixVignetteRadial
.
Ideally I would like to modify the DNG in place by copying the FixVignetteRadial
parameters to OpcodeList3
. I have managed to do this and updated the sub-directory, but when I call TIFFRewriteDirectory
it creates a regular directory at then end of the file and keeps the original SubIFD.
How can I create a new SubIFD in the main directory with all the fields of the original, but with the modified opcode list, and unlink the original SubIFD? My guess is to get the offset of the new IFD, set the SubIFD tag to that offset, and unlink the directory. But I can't quite find the right incantations to achieve this.
As is often the case, rubber-duck debugging through the medium of StackExchange has yielded the answer. And it was as I suspected:
void modify_tiff_tag(input_file)
const char *input_file;
{
int old_offset_idx = 0;
TIFF *tif;
tdir_t currentDirNumber;
int number_of_sub_IFDs;
/* array for SubIFD tag */
toff_t sub_IFDs_offsets[NUMBER_OF_SUBIFDs];
int blnRead = 0;
// Open the input TIFF file
tif = TIFFOpen(input_file, "r+");
if (!tif) {
printf("Failed to open input file: %s\n", input_file);
return;
}
currentDirNumber = TIFFCurrentDirectory(tif);
/* The first directory is already read through TIFFOpen() */
do {
/*Check if there are SubIFD subfiles */
void *ptr;
if (TIFFGetField(tif, TIFFTAG_SUBIFD, &number_of_sub_IFDs, &ptr)) {
/* Copy SubIFD array from pointer */
memcpy(sub_IFDs_offsets, ptr, number_of_sub_IFDs * sizeof(toff_t));
for (int i = 0; i < number_of_sub_IFDs; ++i) {
char *opcode2_data = nullptr;
/* Read first SubIFD directory */
if (!TIFFSetSubDirectory(tif, sub_IFDs_offsets[i]))
break;
/* Check if there is a SubIFD chain behind the first one from
* the array, as specified by Adobe */
while (TIFFReadDirectory(tif))
printf("Shouldn't be here\n");
if (TIFFGetField(tif, TIFFTAG_OPCODELIST2, &opcode2_size_bytes, &opcode2_data)) {
old_offset_idx = i;
uint32_t opcode3_size_bytes;
char *opcode3_data = nullptr;
if (TIFFGetField(tif, TIFFTAG_OPCODELIST3, &opcode3_size_bytes, &opcode3_data)) {
char *new_opcode3_data = nullptr;
uint32_t new_opcode3_size_bytes = parse_opcodes(opcode2_data, opcode2_size_bytes, opcode3_data,
opcode3_size_bytes, &new_opcode3_data);
TIFFSetField(tif, TIFFTAG_OPCODELIST3, new_opcode3_size_bytes, new_opcode3_data);
free(new_opcode3_data);
}
if (TIFFRewriteDirectory(tif))
printf("Rewritten\n");
else
printf("Failed to rewrite");
TIFFFlush(tif);
}
}
/* Go back to main-IFD chain and re-read that main-IFD directory */
if (!TIFFSetDirectory(tif, currentDirNumber))
return;
}
TIFFFlush(tif);
/* Read next main-IFD directory (subfile) */
blnRead = TIFFReadDirectory(tif);
if (blnRead) {
sub_IFDs_offsets[old_offset_idx] = TIFFCurrentDirOffset(tif);
TIFFSetDirectory(tif, 0);
TIFFSetField(tif, TIFFTAG_SUBIFD, number_of_sub_IFDs, sub_IFDs_offsets);
TIFFRewriteDirectory(tif);
// A bit hacky, but TIFFUnlinkDirectory() uses 1-based indexing.
TIFFUnlinkDirectory(tif, 2);
}
} while (blnRead);
TIFFClose(tif);
}
The code is a bit rough and ready, and would benefit from some clean-up and error checking. But for the files I am dealing with it seems to work as expected.