jpegr library: add multi-picture format support

test: jpegr_test
bug: b/264715926
Change-Id: I1bd299ddc0435e54f7c8554b92b3cd6eb2f6eb2b
(cherry picked from commit 283c64b190e74e63064524f6eb598654cbe98f4c)
diff --git a/libs/jpegrecoverymap/jpegr.cpp b/libs/jpegrecoverymap/jpegr.cpp
index 828af2d..09a4315 100644
--- a/libs/jpegrecoverymap/jpegr.cpp
+++ b/libs/jpegrecoverymap/jpegr.cpp
@@ -19,6 +19,7 @@
 #include <jpegrecoverymap/jpegdecoderhelper.h>
 #include <jpegrecoverymap/recoverymapmath.h>
 #include <jpegrecoverymap/jpegrutils.h>
+#include <jpegrecoverymap/multipictureformat.h>
 
 #include <image_io/jpeg/jpeg_marker.h>
 #include <image_io/jpeg/jpeg_info.h>
@@ -105,10 +106,10 @@
 
 /* Encode API-0 */
 status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
-                                  jpegr_transfer_function hdr_tf,
-                                  jr_compressed_ptr dest,
-                                  int quality,
-                                  jr_exif_ptr exif) {
+                            jpegr_transfer_function hdr_tf,
+                            jr_compressed_ptr dest,
+                            int quality,
+                            jr_exif_ptr exif) {
   if (uncompressed_p010_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
@@ -167,11 +168,11 @@
 
 /* Encode API-1 */
 status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
-                                  jr_uncompressed_ptr uncompressed_yuv_420_image,
-                                  jpegr_transfer_function hdr_tf,
-                                  jr_compressed_ptr dest,
-                                  int quality,
-                                  jr_exif_ptr exif) {
+                            jr_uncompressed_ptr uncompressed_yuv_420_image,
+                            jpegr_transfer_function hdr_tf,
+                            jr_compressed_ptr dest,
+                            int quality,
+                            jr_exif_ptr exif) {
   if (uncompressed_p010_image == nullptr
    || uncompressed_yuv_420_image == nullptr
    || dest == nullptr) {
@@ -231,10 +232,10 @@
 
 /* Encode API-2 */
 status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
-                                  jr_uncompressed_ptr uncompressed_yuv_420_image,
-                                  jr_compressed_ptr compressed_jpeg_image,
-                                  jpegr_transfer_function hdr_tf,
-                                  jr_compressed_ptr dest) {
+                            jr_uncompressed_ptr uncompressed_yuv_420_image,
+                            jr_compressed_ptr compressed_jpeg_image,
+                            jpegr_transfer_function hdr_tf,
+                            jr_compressed_ptr dest) {
   if (uncompressed_p010_image == nullptr
    || uncompressed_yuv_420_image == nullptr
    || compressed_jpeg_image == nullptr
@@ -276,9 +277,9 @@
 
 /* Encode API-3 */
 status_t JpegR::encodeJPEGR(jr_uncompressed_ptr uncompressed_p010_image,
-                                  jr_compressed_ptr compressed_jpeg_image,
-                                  jpegr_transfer_function hdr_tf,
-                                  jr_compressed_ptr dest) {
+                            jr_compressed_ptr compressed_jpeg_image,
+                            jpegr_transfer_function hdr_tf,
+                            jr_compressed_ptr dest) {
   if (uncompressed_p010_image == nullptr
    || compressed_jpeg_image == nullptr
    || dest == nullptr) {
@@ -327,8 +328,7 @@
   return NO_ERROR;
 }
 
-status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image,
-                                   jr_info_ptr jpegr_info) {
+status_t JpegR::getJPEGRInfo(jr_compressed_ptr compressed_jpegr_image, jr_info_ptr jpegr_info) {
   if (compressed_jpegr_image == nullptr || jpegr_info == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
@@ -349,9 +349,9 @@
 
 /* Decode API */
 status_t JpegR::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
-                                  jr_uncompressed_ptr dest,
-                                  jr_exif_ptr exif,
-                                  bool request_sdr) {
+                            jr_uncompressed_ptr dest,
+                            jr_exif_ptr exif,
+                            bool request_sdr) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
@@ -399,8 +399,8 @@
   uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
   uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
 
-  if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
-                          jpeg_decoder.getXMPSize(), &metadata)) {
+  if (!getMetadataFromXMP(static_cast<uint8_t*>(recovery_map_decoder.getXMPPtr()),
+                          recovery_map_decoder.getXMPSize(), &metadata)) {
     return ERROR_JPEGR_DECODE_ERROR;
   }
 
@@ -409,7 +409,7 @@
 }
 
 status_t JpegR::compressRecoveryMap(jr_uncompressed_ptr uncompressed_recovery_map,
-                                          jr_compressed_ptr dest) {
+                                    jr_compressed_ptr dest) {
   if (uncompressed_recovery_map == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
@@ -493,10 +493,10 @@
 }
 
 status_t JpegR::generateRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
-                                          jr_uncompressed_ptr uncompressed_p010_image,
-                                          jpegr_transfer_function hdr_tf,
-                                          jr_metadata_ptr metadata,
-                                          jr_uncompressed_ptr dest) {
+                                    jr_uncompressed_ptr uncompressed_p010_image,
+                                    jpegr_transfer_function hdr_tf,
+                                    jr_metadata_ptr metadata,
+                                    jr_uncompressed_ptr dest) {
   if (uncompressed_yuv_420_image == nullptr
    || uncompressed_p010_image == nullptr
    || metadata == nullptr
@@ -637,9 +637,9 @@
 }
 
 status_t JpegR::applyRecoveryMap(jr_uncompressed_ptr uncompressed_yuv_420_image,
-                                       jr_uncompressed_ptr uncompressed_recovery_map,
-                                       jr_metadata_ptr metadata,
-                                       jr_uncompressed_ptr dest) {
+                                 jr_uncompressed_ptr uncompressed_recovery_map,
+                                 jr_metadata_ptr metadata,
+                                 jr_uncompressed_ptr dest) {
   if (uncompressed_yuv_420_image == nullptr
    || uncompressed_recovery_map == nullptr
    || metadata == nullptr
@@ -721,8 +721,8 @@
 }
 
 status_t JpegR::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
-                                                        jr_compressed_ptr primary_image,
-                                                        jr_compressed_ptr recovery_map) {
+                                                  jr_compressed_ptr primary_image,
+                                                  jr_compressed_ptr recovery_map) {
   if (compressed_jpegr_image == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
@@ -771,7 +771,7 @@
 
 
 status_t JpegR::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
-                                         jr_compressed_ptr dest) {
+                                   jr_compressed_ptr dest) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
@@ -790,11 +790,22 @@
 // (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
+// XMP
+//
+// (Required, MPF package) APP2 (ff e2)
+// 2 bytes of length
+// MPF
 //
 // (Required) primary image (without the first two bytes (SOI), may have other packages)
 //
-// (Required) secondary image (the recovery map)
+// SOI (ff d8)
+//
+// (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
+//
+// (Required) secondary image (the recovery map, without the first two bytes (SOI))
 //
 // Metadata versions we are using:
 // ECMA TR-98 for JFIF marker
@@ -802,10 +813,10 @@
 // Adobe XMP spec part 3 for XMP marker
 // ICC v4.3 spec for ICC
 status_t JpegR::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,
-                                        jr_compressed_ptr compressed_recovery_map,
-                                        jr_exif_ptr exif,
-                                        jr_metadata_ptr metadata,
-                                        jr_compressed_ptr dest) {
+                                  jr_compressed_ptr compressed_recovery_map,
+                                  jr_exif_ptr exif,
+                                  jr_metadata_ptr metadata,
+                                  jr_compressed_ptr dest) {
   if (compressed_jpeg_image == nullptr
    || compressed_recovery_map == nullptr
    || metadata == nullptr
@@ -815,6 +826,10 @@
 
   int pos = 0;
 
+  const string xmp_primary = generateXmpForPrimaryImage(compressed_recovery_map->length);
+  const string xmp_secondary = generateXmpForSecondaryImage(*metadata);
+
+  // Begin primary image
   // Write SOI
   JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
   JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
@@ -833,13 +848,12 @@
 
   // Prepare and write XMP
   {
-    const string xmp = generateXmp(compressed_recovery_map->length, *metadata);
     const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
     const int nameSpaceLength = nameSpace.size() + 1;  // need to count the null terminator
     // 2 bytes: representing the length of the package
     // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
     // x bytes: length of xmp packet
-    const int length = 2 + nameSpaceLength + xmp.size();
+    const int length = 2 + nameSpaceLength + xmp_primary.size();
     const uint8_t lengthH = ((length >> 8) & 0xff);
     const uint8_t lengthL = (length & 0xff);
     JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
@@ -847,15 +861,59 @@
     JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
     JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
     JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
-    JPEGR_CHECK(Write(dest, (void*)xmp.c_str(), xmp.size(), pos));
+    JPEGR_CHECK(Write(dest, (void*)xmp_primary.c_str(), xmp_primary.size(), pos));
+  }
+
+  // Prepare and write MPF
+  {
+      const int length = 2 + calculateMpfSize();
+      const uint8_t lengthH = ((length >> 8) & 0xff);
+      const uint8_t lengthL = (length & 0xff);
+      int primary_image_size = pos + length + compressed_jpeg_image->length;
+      int secondary_image_offset = primary_image_size;
+      int secondary_image_size = xmp_secondary.size() + compressed_recovery_map->length;
+      sp<DataStruct> mpf = generateMpf(0, /* primary_image_offset */
+                                       primary_image_size,
+                                       secondary_image_offset,
+                                       secondary_image_size);
+      JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+      JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP2, 1, pos));
+      JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+      JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+      JPEGR_CHECK(Write(dest, (void*)mpf->getData(), mpf->getLength(), pos));
   }
 
   // Write primary image
   JPEGR_CHECK(Write(dest,
       (uint8_t*)compressed_jpeg_image->data + 2, compressed_jpeg_image->length - 2, pos));
+  // Finish primary image
+
+  // Begin secondary image (recovery map)
+  // Write SOI
+  JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+  JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kSOI, 1, pos));
+
+  // Prepare and write XMP
+  {
+    const string nameSpace = "http://ns.adobe.com/xap/1.0/\0";
+    const int nameSpaceLength = nameSpace.size() + 1;  // need to count the null terminator
+    // 2 bytes: representing the length of the package
+    // 29 bytes: length of name space "http://ns.adobe.com/xap/1.0/\0",
+    // x bytes: length of xmp packet
+    const int length = 2 + nameSpaceLength + xmp_secondary.size();
+    const uint8_t lengthH = ((length >> 8) & 0xff);
+    const uint8_t lengthL = (length & 0xff);
+    JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kStart, 1, pos));
+    JPEGR_CHECK(Write(dest, &photos_editing_formats::image_io::JpegMarker::kAPP1, 1, pos));
+    JPEGR_CHECK(Write(dest, &lengthH, 1, pos));
+    JPEGR_CHECK(Write(dest, &lengthL, 1, pos));
+    JPEGR_CHECK(Write(dest, (void*)nameSpace.c_str(), nameSpaceLength, pos));
+    JPEGR_CHECK(Write(dest, (void*)xmp_secondary.c_str(), xmp_secondary.size(), pos));
+  }
 
   // Write secondary image
-  JPEGR_CHECK(Write(dest, compressed_recovery_map->data, compressed_recovery_map->length, pos));
+  JPEGR_CHECK(Write(dest,
+        (uint8_t*)compressed_recovery_map->data + 2, compressed_recovery_map->length - 2, pos));
 
   // Set back length
   dest->length = pos;
@@ -864,8 +922,7 @@
   return NO_ERROR;
 }
 
-status_t JpegR::toneMap(jr_uncompressed_ptr src,
-                              jr_uncompressed_ptr dest) {
+status_t JpegR::toneMap(jr_uncompressed_ptr src, jr_uncompressed_ptr dest) {
   if (src == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }