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

Commit c8490774 authored by Dichen Zhang's avatar Dichen Zhang Committed by Android (Google) Code Review
Browse files

Merge "Revert "Update EXIF""

parents 098cde65 50ff1299
Loading
Loading
Loading
Loading
+1 −5
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ public:
     */
    void* getDecompressedImagePtr();
    /*
     * Returns the decompressed raw image buffer size. This mgit ethod must be called only after
     * Returns the decompressed raw image buffer size. This method must be called only after
     * calling decompressImage().
     */
    size_t getDecompressedImageSize();
@@ -92,10 +92,6 @@ public:
                                      size_t* pWidth, size_t* pHeight,
                                      std::vector<uint8_t>* iccData,
                                      std::vector<uint8_t>* exifData);
    /*
     * Extracts EXIF package and updates the EXIF position / length without decoding the image.
     */
    bool extractEXIF(const void* image, int length);

private:
    bool decode(const void* image, int length, bool decodeToRGBA);
+8 −4
Original line number Diff line number Diff line
@@ -321,13 +321,17 @@ private:
                                jr_compressed_ptr dest);

    /*
     * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image
     * and the compressed recovery map as input, and update the XMP metadata with the end of JPEG
     * marker, and append the compressed gian map after the JPEG.
     * This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
     * the compressed recovery map and optionally the exif package as inputs, and generate the XMP
     * metadata, and finally append everything in the order of:
     *     SOI, APP2(EXIF) (if EXIF is from outside), APP2(XMP), primary image, recovery map
     * Note that EXIF package is only available for encoding API-0 and API-1. For encoding API-2 and
     * API-3 this parameter is null, but the primary image in JPEG/R may still have EXIF as long as
     * the input JPEG has EXIF.
     *
     * @param compressed_jpeg_image compressed 8-bit JPEG image
     * @param compress_recovery_map compressed recover map
     * @param exif EXIF package
     * @param (nullable) exif EXIF package
     * @param metadata JPEG/R metadata to encode in XMP of the jpeg
     * @param dest compressed JPEGR image
     * @return NO_ERROR if calculation succeeds, error code if error occurs.
+0 −78
Original line number Diff line number Diff line
@@ -45,7 +45,6 @@ const size_t EXIF_J_R_ENTRY_LENGTH = 12;
 * @return status of succeed or error code.
 */
status_t Write(jr_compressed_ptr destination, const void* source, size_t length, int &position);
status_t Write(jr_exif_ptr destination, const void* source, size_t length, int &position);


/*
@@ -105,83 +104,6 @@ bool getMetadataFromXMP(uint8_t* xmp_data, size_t xmp_size, jpegr_metadata* meta
 * @return XMP metadata in type of string
 */
std::string generateXmp(int secondary_image_length, jpegr_metadata& metadata);

/*
 * Add J R entry to existing exif, or create a new one with J R entry if it's null.
 * EXIF syntax / change:
 * ori:
 * FF E1 - APP1
 * 01 FC - size of APP1 (to be calculated)
 * -----------------------------------------------------
 * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
 * 49 49 2A 00 - TIFF Header
 * 08 00 00 00 - offset to the IFD (image file directory)
 * 06 00 - 6 entries
 * 00 01 - Width Tag
 * 03 00 - 'Short' type
 * 01 00 00 00 - 1 component
 * 00 05 00 00 - image with 0x500
 *--------------------------------------------------------------------------
 * new:
 * FF E1 - APP1
 * 02 08 - new size, equals to old size + EXIF_J_R_ENTRY_LENGTH (12)
 *-----------------------------------------------------
 * 45 78 69 66 00 00 - Exif\0\0 "Exif header"
 * 49 49 2A 00 - TIFF Header
 * 08 00 00 00 - offset to the IFD (image file directory)
 * 07 00 - +1 entry
 * 4A 52   Custom ('J''R') Tag
 * 07 00 - Unknown type
 * 01 00 00 00 - 1 component
 * 00 00 00 00 - empty data
 * 00 01 - Width Tag
 * 03 00 - 'Short' type
 * 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
+0 −54
Original line number Diff line number Diff line
@@ -248,60 +248,6 @@ bool JpegDecoder::decode(const void* image, int length, bool decodeToRGBA) {
    return true;
}

// TODO (Fyodor/Dichen): merge this method with getCompressedImageParameters() since they have
// similar functionality. Yet Dichen is not familiar with who's calling
// getCompressedImageParameters(), looks like it's used by some pending CLs.
bool JpegDecoder::extractEXIF(const void* image, int length) {
    jpeg_decompress_struct cinfo;
    jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
    jpegrerror_mgr myerr;

    cinfo.err = jpeg_std_error(&myerr.pub);
    myerr.pub.error_exit = jpegrerror_exit;

    if (setjmp(myerr.setjmp_buffer)) {
        jpeg_destroy_decompress(&cinfo);
        return false;
    }
    jpeg_create_decompress(&cinfo);

    jpeg_save_markers(&cinfo, kAPP0Marker, 0xFFFF);
    jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
    jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);

    cinfo.src = &mgr;
    jpeg_read_header(&cinfo, TRUE);

    bool exifAppears = false;
    size_t pos = 2;  // position after SOI
    for (jpeg_marker_struct* marker = cinfo.marker_list;
         marker && !exifAppears;
         marker = marker->next) {

        pos += 4;
        pos += marker->original_length;

        if (marker->marker != kAPP1Marker) {
            continue;
        }

        const unsigned int len = marker->data_length;
        if (!exifAppears &&
            len > kExifIdCode.size() &&
            !strncmp(reinterpret_cast<const char*>(marker->data),
                     kExifIdCode.c_str(),
                     kExifIdCode.size())) {
            mEXIFBuffer.resize(len, 0);
            memcpy(static_cast<void*>(mEXIFBuffer.data()), marker->data, len);
            exifAppears = true;
            mExifPos = pos - marker->original_length;
        }
    }

    jpeg_destroy_decompress(&cinfo);
    return true;
}

bool JpegDecoder::decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest,
        bool isSingleChannel) {
    if (isSingleChannel) {
+13 −105
Original line number Diff line number Diff line
@@ -175,18 +175,7 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
  jpeg.data = jpeg_encoder.getCompressedImagePtr();
  jpeg.length = jpeg_encoder.getCompressedImageSize();

  jpegr_exif_struct new_exif;
  if (exif == nullptr || exif->data == nullptr) {
      new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
  } else {
      new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
  }
  new_exif.data = new uint8_t[new_exif.length];
  std::unique_ptr<uint8_t[]> new_exif_data;
  new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
  JPEGR_CHECK(updateExif(exif, &new_exif));

  JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
  JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, exif, &metadata, dest));

  return NO_ERROR;
}
@@ -250,19 +239,7 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
  jpeg.data = jpeg_encoder.getCompressedImagePtr();
  jpeg.length = jpeg_encoder.getCompressedImageSize();

  jpegr_exif_struct new_exif;
  if (exif == nullptr || exif->data == nullptr) {
      new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
  } else {
      new_exif.length = exif->length + EXIF_J_R_ENTRY_LENGTH;
  }

  new_exif.data = new uint8_t[new_exif.length];
  std::unique_ptr<uint8_t[]> new_exif_data;
  new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
  JPEGR_CHECK(updateExif(exif, &new_exif));

  JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, &new_exif, &metadata, dest));
  JPEGR_CHECK(appendRecoveryMap(&jpeg, &compressed_map, exif, &metadata, dest));

  return NO_ERROR;
}
@@ -311,47 +288,7 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
  compressed_map.data = compressed_map_data.get();
  JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));

  // Extract EXIF from JPEG without decoding.
  JpegDecoder jpeg_decoder;
  if (!jpeg_decoder.extractEXIF(compressed_jpeg_image->data, compressed_jpeg_image->length)) {
    return ERROR_JPEGR_DECODE_ERROR;
  }

  // Update exif.
  jpegr_exif_struct exif;
  exif.data = nullptr;
  exif.length = 0;
  jpegr_compressed_struct new_jpeg_image;
  new_jpeg_image.data = nullptr;
  new_jpeg_image.length = 0;
  if (jpeg_decoder.getEXIFPos() != 0) {
    copyJpegWithoutExif(&new_jpeg_image,
                        compressed_jpeg_image,
                        jpeg_decoder.getEXIFPos(),
                        jpeg_decoder.getEXIFSize());
    exif.data = jpeg_decoder.getEXIFPtr();
    exif.length = jpeg_decoder.getEXIFSize();
  }

  jpegr_exif_struct new_exif;
  if (exif.data == nullptr) {
      new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
  } else {
      new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
  }

  new_exif.data = new uint8_t[new_exif.length];
  std::unique_ptr<uint8_t[]> new_exif_data;
  new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
  JPEGR_CHECK(updateExif(&exif, &new_exif));

  JPEGR_CHECK(appendRecoveryMap(
          new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image,
          &compressed_map, &new_exif, &metadata, dest));

  if (new_jpeg_image.data != nullptr) {
    free(new_jpeg_image.data);
  }
  JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, nullptr, &metadata, dest));

  return NO_ERROR;
}
@@ -384,33 +321,6 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
  uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
  uncompressed_yuv_420_image.colorGamut = compressed_jpeg_image->colorGamut;

  // Update exif.
  jpegr_exif_struct exif;
  exif.data = nullptr;
  exif.length = 0;
  jpegr_compressed_struct new_jpeg_image;
  new_jpeg_image.data = nullptr;
  new_jpeg_image.length = 0;
  if (jpeg_decoder.getEXIFPos() != 0) {
    copyJpegWithoutExif(&new_jpeg_image,
                        compressed_jpeg_image,
                        jpeg_decoder.getEXIFPos(),
                        jpeg_decoder.getEXIFSize());
    exif.data = jpeg_decoder.getEXIFPtr();
    exif.length = jpeg_decoder.getEXIFSize();
  }

  jpegr_exif_struct new_exif;
  if (exif.data == nullptr) {
      new_exif.length = PSEUDO_EXIF_PACKAGE_LENGTH;
  } else {
      new_exif.length = exif.length + EXIF_J_R_ENTRY_LENGTH;
  }
  new_exif.data = new uint8_t[new_exif.length];
  std::unique_ptr<uint8_t[]> new_exif_data;
  new_exif_data.reset(reinterpret_cast<uint8_t*>(new_exif.data));
  JPEGR_CHECK(updateExif(&exif, &new_exif));

  if (uncompressed_p010_image->width != uncompressed_yuv_420_image.width
   || uncompressed_p010_image->height != uncompressed_yuv_420_image.height) {
    return ERROR_JPEGR_RESOLUTION_MISMATCH;
@@ -435,13 +345,7 @@ status_t RecoveryMap::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
  compressed_map.data = compressed_map_data.get();
  JPEGR_CHECK(compressRecoveryMap(&map, &compressed_map));

  JPEGR_CHECK(appendRecoveryMap(
          new_jpeg_image.data == nullptr ? compressed_jpeg_image : &new_jpeg_image,
          &compressed_map, &new_exif, &metadata, dest));

  if (new_jpeg_image.data != nullptr) {
    free(new_jpeg_image.data);
  }
  JPEGR_CHECK(appendRecoveryMap(compressed_jpeg_image, &compressed_map, nullptr, &metadata, dest));

  return NO_ERROR;
}
@@ -967,15 +871,20 @@ status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_imag

// JPEG/R structure:
// SOI (ff d8)
//
// (Optional, only if EXIF package is from outside)
// APP1 (ff e1)
// 2 bytes of length (2 + length of exif package)
// EXIF package (this includes the first two bytes representing the package length)
// APP1 (ff e1)
//
// (Required, XMP package) APP1 (ff e1)
// 2 bytes of length (2 + 29 + length of xmp package)
// name space ("http://ns.adobe.com/xap/1.0/\0")
// xmp
// primary image (without the first two bytes (SOI) and without EXIF, may have other packages)
// secondary image (the recovery map)
//
// (Required) primary image (without the first two bytes (SOI), may have other packages)
//
// (Required) secondary image (the recovery map)
//
// Metadata versions we are using:
// ECMA TR-98 for JFIF marker
@@ -989,7 +898,6 @@ status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
                                        jr_compressed_ptr dest) {
  if (compressed_jpeg_image == nullptr
   || compressed_recovery_map == nullptr
   || exif == nullptr
   || metadata == nullptr
   || dest == nullptr) {
    return ERROR_JPEGR_INVALID_NULL_PTR;
@@ -1002,7 +910,7 @@ status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
  JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));

  // Write EXIF
  {
  if (exif != nullptr) {
    const int length = 2 + exif->length;
    const uint8_t lengthH = ((length >> 8) & 0xff);
    const uint8_t lengthL = (length & 0xff);
Loading