JPEGR Decoder. Inital implementation

* Implements JPEGR Decoding
* Doesn't implement XMP data parsing
* Implements retrieval of image data

Bug: b/252835416
Test: build

Change-Id: I62c7c323842b8d87866c43e85c1d7238e5b9b0d5
diff --git a/libs/jpegrecoverymap/recoverymap.cpp b/libs/jpegrecoverymap/recoverymap.cpp
index ad41839..b685294 100644
--- a/libs/jpegrecoverymap/recoverymap.cpp
+++ b/libs/jpegrecoverymap/recoverymap.cpp
@@ -18,15 +18,23 @@
 #include <jpegrecoverymap/jpegencoder.h>
 #include <jpegrecoverymap/jpegdecoder.h>
 #include <jpegrecoverymap/recoverymapmath.h>
+#include <jpegrecoverymap/recoverymaputils.h>
 
 #include <image_io/jpeg/jpeg_marker.h>
 #include <image_io/xml/xml_writer.h>
+#include <image_io/jpeg/jpeg_info.h>
+#include <image_io/jpeg/jpeg_scanner.h>
+#include <image_io/jpeg/jpeg_info_builder.h>
+#include <image_io/base/data_segment_data_source.h>
+#include <utils/Log.h>
 
 #include <memory>
 #include <sstream>
 #include <string>
+#include <cmath>
 
 using namespace std;
+using namespace photos_editing_formats::image_io;
 
 namespace android::recoverymap {
 
@@ -234,17 +242,41 @@
   return NO_ERROR;
 }
 
+status_t RecoveryMap::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;
+  }
+
+  jpegr_compressed_struct primary_image, recovery_map;
+  JPEGR_CHECK(extractPrimaryImageAndRecoveryMap(compressed_jpegr_image,
+                                                &primary_image, &recovery_map));
+
+  JpegDecoder jpeg_decoder;
+  if (!jpeg_decoder.getCompressedImageParameters(primary_image.data, primary_image.length,
+                                                 &jpegr_info->width, &jpegr_info->height,
+                                                 jpegr_info->iccData, jpegr_info->exifData)) {
+    return ERROR_JPEGR_DECODE_ERROR;
+  }
+
+  return NO_ERROR;
+}
+
+
 status_t RecoveryMap::decodeJPEGR(jr_compressed_ptr compressed_jpegr_image,
                                   jr_uncompressed_ptr dest,
-                                  jr_exif_ptr /* exif */,
-                                  bool /* request_sdr */) {
+                                  jr_exif_ptr exif,
+                                  bool request_sdr) {
   if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
+  // TODO: fill EXIF data
+  (void) exif;
+
   jpegr_compressed_struct compressed_map;
   jpegr_metadata metadata;
-  JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map, &metadata));
+  JPEGR_CHECK(extractRecoveryMap(compressed_jpegr_image, &compressed_map));
 
   jpegr_uncompressed_struct map;
   JPEGR_CHECK(decompressRecoveryMap(&compressed_map, &map));
@@ -259,7 +291,19 @@
   uncompressed_yuv_420_image.width = jpeg_decoder.getDecompressedImageWidth();
   uncompressed_yuv_420_image.height = jpeg_decoder.getDecompressedImageHeight();
 
-  JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
+  if (!getMetadataFromXMP(static_cast<uint8_t*>(jpeg_decoder.getXMPPtr()),
+                                       jpeg_decoder.getXMPSize(), &metadata)) {
+    return ERROR_JPEGR_DECODE_ERROR;
+  }
+
+  if (request_sdr) {
+    memcpy(dest->data, uncompressed_yuv_420_image.data,
+            uncompressed_yuv_420_image.width*uncompressed_yuv_420_image.height *3 / 2);
+    dest->width = uncompressed_yuv_420_image.width;
+    dest->height = uncompressed_yuv_420_image.height;
+  } else {
+    JPEGR_CHECK(applyRecoveryMap(&uncompressed_yuv_420_image, &map, &metadata, dest));
+  }
 
   return NO_ERROR;
 }
@@ -467,19 +511,66 @@
       reinterpret_cast<uint32_t*>(dest->data)[pixel_idx] = rgba1010102;
     }
   }
+  return NO_ERROR;
+}
+
+status_t RecoveryMap::extractPrimaryImageAndRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
+                                               jr_compressed_ptr primary_image,
+                                               jr_compressed_ptr recovery_map) {
+  if (compressed_jpegr_image == nullptr) {
+    return ERROR_JPEGR_INVALID_NULL_PTR;
+  }
+
+  MessageHandler msg_handler;
+  std::shared_ptr<DataSegment> seg =
+                  DataSegment::Create(DataRange(0, compressed_jpegr_image->length),
+                                      static_cast<const uint8_t*>(compressed_jpegr_image->data),
+                                      DataSegment::BufferDispositionPolicy::kDontDelete);
+  DataSegmentDataSource data_source(seg);
+  JpegInfoBuilder jpeg_info_builder;
+  jpeg_info_builder.SetImageLimit(2);
+  JpegScanner jpeg_scanner(&msg_handler);
+  jpeg_scanner.Run(&data_source, &jpeg_info_builder);
+  data_source.Reset();
+
+  if (jpeg_scanner.HasError()) {
+    return ERROR_JPEGR_INVALID_INPUT_TYPE;
+  }
+
+  const auto& jpeg_info = jpeg_info_builder.GetInfo();
+  const auto& image_ranges = jpeg_info.GetImageRanges();
+  if (image_ranges.empty()) {
+    return ERROR_JPEGR_INVALID_INPUT_TYPE;
+  }
+
+  if (image_ranges.size() != 2) {
+    // Must be 2 JPEG Images
+    return ERROR_JPEGR_INVALID_INPUT_TYPE;
+  }
+
+  if (primary_image != nullptr) {
+    primary_image->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
+                                               image_ranges[0].GetBegin();
+    primary_image->length = image_ranges[0].GetLength();
+  }
+
+  if (recovery_map != nullptr) {
+    recovery_map->data = static_cast<uint8_t*>(compressed_jpegr_image->data) +
+                                              image_ranges[1].GetBegin();
+    recovery_map->length = image_ranges[1].GetLength();
+  }
 
   return NO_ERROR;
 }
 
+
 status_t RecoveryMap::extractRecoveryMap(jr_compressed_ptr compressed_jpegr_image,
-                                         jr_compressed_ptr dest,
-                                         jr_metadata_ptr metadata) {
-  if (compressed_jpegr_image == nullptr || dest == nullptr || metadata == nullptr) {
+                                         jr_compressed_ptr dest) {
+  if (compressed_jpegr_image == nullptr || dest == nullptr) {
     return ERROR_JPEGR_INVALID_NULL_PTR;
   }
 
-  // TBD
-  return NO_ERROR;
+  return extractPrimaryImageAndRecoveryMap(compressed_jpegr_image, nullptr, dest);
 }
 
 status_t RecoveryMap::appendRecoveryMap(jr_compressed_ptr compressed_jpeg_image,