Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0daf5f8e authored by Dichen Zhang's avatar Dichen Zhang
Browse files

Update EXIF

Some tags contain offsets, and need to be modified after an insertion of
a "JR" tag

Test: manual
Bug: b/264715926
Change-Id: I273c43ca86ee2d089abeae84f65aa37dace1e4c4
parent 91dfc57a
Loading
Loading
Loading
Loading
+45 −4
Original line number Diff line number Diff line
@@ -107,7 +107,6 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta
std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);

/*
 * Helper function
 * Add J R entry to existing exif, or create a new one with J R entry if it's null.
 * EXIF syntax / change:
 * ori:
@@ -120,7 +119,7 @@ std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
 * 06 00 - 6 entries
 * 00 01 - Width Tag
 * 03 00 - 'Short' type
 * 01 00 00 00 - one entry
 * 01 00 00 00 - 1 component
 * 00 05 00 00 - image with 0x500
 *--------------------------------------------------------------------------
 * new:
@@ -133,14 +132,56 @@ std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);
 * 07 00 - +1 entry
 * 4A 52   Custom ('J''R') Tag
 * 07 00 - Unknown type
 * 01 00 00 00 - one element
 * 01 00 00 00 - 1 component
 * 00 00 00 00 - empty data
 * 00 01 - Width Tag
 * 03 00 - 'Short' type
 * 01 00 00 00 - one entry
 * 01 00 00 00 - 1 component
 * 00 05 00 00 - image with 0x500
 */
status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest);

/*
 * Modify offsets in EXIF in place.
 *
 * Each tag has the following structure:
 *
 * 00 01 - Tag
 * 03 00 - data format
 * 01 00 00 00 - number of components
 * 00 05 00 00 - value
 *
 * The value means offset if
 * (1) num_of_components * bytes_per_component > 4 bytes, or
 * (2) tag == 0x8769 (ExifOffset).
 * In both cases, the method will add EXIF_J_R_ENTRY_LENGTH (12) to the offsets.
 */
void updateExifOffsets(jr_exif_ptr exif, int pos, bool use_big_endian);
void updateExifOffsets(jr_exif_ptr exif, int pos, int num_entry, bool use_big_endian);

/*
 * Read data from the target position and target length in bytes;
 */
int readValue(uint8_t* data, int pos, int length, bool use_big_endian);

/*
 * Returns the length of data format in bytes
 *
 *  ----------------------------------------------------------------------------------------------
 *  |       value       |         1       |        2        |        3         |       4         |
 *  |       format      |  unsigned byte  |  ascii strings  |  unsigned short  |  unsigned long  |
 *  |  bytes/component  |         1       |        1        |        2         |       4         |
 *  ----------------------------------------------------------------------------------------------
 *  |       value       |         5       |        6        |        7         |       8         |
 *  |       format      |unsigned rational|   signed byte   |    undefined     |  signed short   |
 *  |  bytes/component  |         8       |        1        |        1         |       2         |
 *  ----------------------------------------------------------------------------------------------
 *  |       value       |         9       |        10       |        11        |       12        |
 *  |       format      |   signed long   | signed rational |   single float   |  double float   |
 *  |  bytes/component  |         4       |        8        |        4         |       8         |
 *  ----------------------------------------------------------------------------------------------
 */
int findFormatLengthInBytes(int data_format);
}

#endif //ANDROID_JPEGRECOVERYMAP_RECOVERYMAPUTILS_H
+117 −1
Original line number Diff line number Diff line
@@ -22,6 +22,8 @@
#include <image_io/xml/xml_handler.h>
#include <image_io/xml/xml_rule.h>

#include <utils/Log.h>

using namespace photos_editing_formats::image_io;
using namespace std;

@@ -406,7 +408,121 @@ status_t updateExif(jr_exif_ptr exif, jr_exif_ptr dest) {

  Write(dest, (uint8_t*)exif->data + 16, exif->length - 16, pos);

  updateExifOffsets(dest,
                    28, // start from the second tag, skip the "JR" tag
                    num_entry - 1,
                    use_big_endian);

  return NO_ERROR;
}

/*
 * Helper function
 * Modify offsets in EXIF in place.
 */
void updateExifOffsets(jr_exif_ptr exif, int pos, bool use_big_endian) {
  int num_entry = readValue(reinterpret_cast<uint8_t*>(exif->data), pos, 2, use_big_endian);
  updateExifOffsets(exif, pos + 2, num_entry, use_big_endian);
}

void updateExifOffsets(jr_exif_ptr exif, int pos, int num_entry, bool use_big_endian) {
  for (int i = 0; i < num_entry; pos += EXIF_J_R_ENTRY_LENGTH, i++) {
    int tag = readValue(reinterpret_cast<uint8_t*>(exif->data), pos, 2, use_big_endian);
    bool need_to_update_offset = false;
    if (tag == 0x8769) {
      need_to_update_offset = true;
      int sub_ifd_offset =
              readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 8, 4, use_big_endian)
              + 6  // "Exif\0\0";
              + EXIF_J_R_ENTRY_LENGTH;
      updateExifOffsets(exif, sub_ifd_offset, use_big_endian);
    } else {
      int data_format =
              readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 2, 2, use_big_endian);
      int num_of_components =
              readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 4, 4, use_big_endian);
      int data_length = findFormatLengthInBytes(data_format) * num_of_components;
      if (data_length > 4) {
        need_to_update_offset = true;
      }
    }

    if (!need_to_update_offset) {
      continue;
    }

    int offset = readValue(reinterpret_cast<uint8_t*>(exif->data), pos + 8, 4, use_big_endian);

    offset += EXIF_J_R_ENTRY_LENGTH;

    if (use_big_endian) {
      reinterpret_cast<uint8_t*>(exif->data)[pos + 11] = offset & 0xff;
      reinterpret_cast<uint8_t*>(exif->data)[pos + 10] = (offset >> 8) & 0xff;
      reinterpret_cast<uint8_t*>(exif->data)[pos + 9] = (offset >> 16) & 0xff;
      reinterpret_cast<uint8_t*>(exif->data)[pos + 8] = (offset >> 24) & 0xff;
    } else {
      reinterpret_cast<uint8_t*>(exif->data)[pos + 8] = offset & 0xff;
      reinterpret_cast<uint8_t*>(exif->data)[pos + 9] = (offset >> 8) & 0xff;
      reinterpret_cast<uint8_t*>(exif->data)[pos + 10] = (offset >> 16) & 0xff;
      reinterpret_cast<uint8_t*>(exif->data)[pos + 11] = (offset >> 24) & 0xff;
    }
  }
}

/*
 * Read data from the target position and target length in bytes;
 */
int readValue(uint8_t* data, int pos, int length, bool use_big_endian) {
  if (length == 2) {
    if (use_big_endian) {
      return (data[pos] << 8) | data[pos + 1];
    } else {
      return (data[pos + 1] << 8) | data[pos];
    }
  } else if (length == 4) {
    if (use_big_endian) {
      return (data[pos] << 24) | (data[pos + 1] << 16) | (data[pos + 2] << 8) | data[pos + 3];
    } else {
      return (data[pos + 3] << 24) | (data[pos + 2] << 16) | (data[pos + 1] << 8) | data[pos];
    }
  } else {
    // Not support for now.
    ALOGE("Error in readValue(): pos=%d, length=%d", pos, length);
    return -1;
  }
}

/*
 * Helper function
 * Returns the length of data format in bytes
 */
int findFormatLengthInBytes(int data_format) {
  switch (data_format) {
    case 1:  // unsigned byte
    case 2:  // ascii strings
    case 6:  // signed byte
    case 7:  // undefined
      return 1;

    case 3:  // unsigned short
    case 8:  // signed short
      return 2;

    case 4:  // unsigned long
    case 9:  // signed long
    case 11:  // single float
      return 4;

    case 5:  // unsigned rational
    case 10:  // signed rational
    case 12:  // double float
      return 8;

    default:
      // should not hit here
      ALOGE("Error in findFormatLengthInBytes(): data_format=%d", data_format);
      return -1;
  }
}

} // namespace android::recoverymap
 No newline at end of file