Home | History | Annotate | Download | only in arc
      1 /*
      2  * Copyright 2017 The Chromium OS Authors. All rights reserved.
      3  * Use of this source code is governed by a BSD-style license that can be
      4  * found in the LICENSE file.
      5  */
      6 
      7 #include "arc/exif_utils.h"
      8 
      9 #include <cstdlib>
     10 #include <ctime>
     11 
     12 #include <libyuv.h>
     13 #include "arc/common.h"
     14 
     15 namespace std {
     16 
     17 template <>
     18 struct default_delete<ExifEntry> {
     19   inline void operator()(ExifEntry* entry) const { exif_entry_unref(entry); }
     20 };
     21 
     22 }  // namespace std
     23 
     24 namespace arc {
     25 
     26 // This comes from the Exif Version 2.3 standard table 9.
     27 const uint8_t gExifAsciiPrefix[] = {0x41, 0x53, 0x43, 0x49,
     28                                     0x49, 0x0,  0x0,  0x0};
     29 
     30 static void SetLatitudeOrLongitudeData(unsigned char* data, double num) {
     31   // Take the integer part of |num|.
     32   ExifLong degrees = static_cast<ExifLong>(num);
     33   ExifLong minutes = static_cast<ExifLong>(60 * (num - degrees));
     34   ExifLong microseconds =
     35       static_cast<ExifLong>(3600000000u * (num - degrees - minutes / 60.0));
     36   exif_set_rational(data, EXIF_BYTE_ORDER_INTEL, {degrees, 1});
     37   exif_set_rational(data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
     38                     {minutes, 1});
     39   exif_set_rational(data + 2 * sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
     40                     {microseconds, 1000000});
     41 }
     42 
     43 ExifUtils::ExifUtils()
     44     : yu12_buffer_(nullptr),
     45       yu12_width_(0),
     46       yu12_height_(0),
     47       thumbnail_width_(0),
     48       thumbnail_height_(0),
     49       exif_data_(nullptr),
     50       app1_buffer_(nullptr),
     51       app1_length_(0) {}
     52 
     53 ExifUtils::~ExifUtils() { Reset(); }
     54 
     55 bool ExifUtils::Initialize(const uint8_t* buffer, uint16_t width,
     56                            uint16_t height, int quality) {
     57   Reset();
     58 
     59   if (width % 2 != 0 || height % 2 != 0) {
     60     LOGF(ERROR) << "invalid image size " << width << "x" << height;
     61     return false;
     62   }
     63   if (quality < 1 || quality > 100) {
     64     LOGF(ERROR) << "invalid jpeg quality " << quality;
     65     return false;
     66   }
     67   thumbnail_jpeg_quality_ = quality;
     68   yu12_buffer_ = buffer;
     69   yu12_width_ = width;
     70   yu12_height_ = height;
     71 
     72   exif_data_ = exif_data_new();
     73   if (exif_data_ == nullptr) {
     74     LOGF(ERROR) << "allocate memory for exif_data_ failed";
     75     return false;
     76   }
     77   // Set the image options.
     78   exif_data_set_option(exif_data_, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
     79   exif_data_set_data_type(exif_data_, EXIF_DATA_TYPE_COMPRESSED);
     80   exif_data_set_byte_order(exif_data_, EXIF_BYTE_ORDER_INTEL);
     81 
     82   // Set image width and length.
     83   SetImageWidth(width);
     84   SetImageLength(height);
     85 
     86   return true;
     87 }
     88 
     89 bool ExifUtils::SetMaker(const std::string& maker) {
     90   size_t entrySize = maker.length() + 1;
     91   std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
     92       EXIF_IFD_0, EXIF_TAG_MAKE, EXIF_FORMAT_ASCII, entrySize, entrySize);
     93   if (!entry) {
     94     LOGF(ERROR) << "Adding Make exif entry failed";
     95     return false;
     96   }
     97   memcpy(entry->data, maker.c_str(), entrySize);
     98   return true;
     99 }
    100 
    101 bool ExifUtils::SetModel(const std::string& model) {
    102   size_t entrySize = model.length() + 1;
    103   std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
    104       EXIF_IFD_0, EXIF_TAG_MODEL, EXIF_FORMAT_ASCII, entrySize, entrySize);
    105   if (!entry) {
    106     LOGF(ERROR) << "Adding Model exif entry failed";
    107     return false;
    108   }
    109   memcpy(entry->data, model.c_str(), entrySize);
    110   return true;
    111 }
    112 
    113 bool ExifUtils::SetDateTime(const struct tm& t) {
    114   // The length is 20 bytes including NULL for termination in Exif standard.
    115   char str[20];
    116   int result = snprintf(str, sizeof(str), "%04i:%02i:%02i %02i:%02i:%02i",
    117                         t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour,
    118                         t.tm_min, t.tm_sec);
    119   if (result != sizeof(str) - 1) {
    120     LOGF(WARNING) << "Input time is invalid";
    121     return false;
    122   }
    123   std::unique_ptr<ExifEntry> entry =
    124       AddVariableLengthEntry(EXIF_IFD_0, EXIF_TAG_DATE_TIME, EXIF_FORMAT_ASCII,
    125                              sizeof(str), sizeof(str));
    126   if (!entry) {
    127     LOGF(ERROR) << "Adding DateTime exif entry failed";
    128     return false;
    129   }
    130   memcpy(entry->data, str, sizeof(str));
    131   return true;
    132 }
    133 
    134 bool ExifUtils::SetFocalLength(uint32_t numerator, uint32_t denominator) {
    135   std::unique_ptr<ExifEntry> entry =
    136       AddEntry(EXIF_IFD_EXIF, EXIF_TAG_FOCAL_LENGTH);
    137   if (!entry) {
    138     LOGF(ERROR) << "Adding FocalLength exif entry failed";
    139     return false;
    140   }
    141   exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
    142                     {numerator, denominator});
    143   return true;
    144 }
    145 
    146 bool ExifUtils::SetGpsLatitude(double latitude) {
    147   const ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE_REF);
    148   std::unique_ptr<ExifEntry> refEntry =
    149       AddVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
    150   if (!refEntry) {
    151     LOGF(ERROR) << "Adding GPSLatitudeRef exif entry failed";
    152     return false;
    153   }
    154   if (latitude >= 0) {
    155     memcpy(refEntry->data, "N", sizeof("N"));
    156   } else {
    157     memcpy(refEntry->data, "S", sizeof("S"));
    158     latitude *= -1;
    159   }
    160 
    161   const ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LATITUDE);
    162   std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
    163       EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
    164   if (!entry) {
    165     exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
    166     LOGF(ERROR) << "Adding GPSLatitude exif entry failed";
    167     return false;
    168   }
    169   SetLatitudeOrLongitudeData(entry->data, latitude);
    170 
    171   return true;
    172 }
    173 
    174 bool ExifUtils::SetGpsLongitude(double longitude) {
    175   ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE_REF);
    176   std::unique_ptr<ExifEntry> refEntry =
    177       AddVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_ASCII, 2, 2);
    178   if (!refEntry) {
    179     LOGF(ERROR) << "Adding GPSLongitudeRef exif entry failed";
    180     return false;
    181   }
    182   if (longitude >= 0) {
    183     memcpy(refEntry->data, "E", sizeof("E"));
    184   } else {
    185     memcpy(refEntry->data, "W", sizeof("W"));
    186     longitude *= -1;
    187   }
    188 
    189   ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_LONGITUDE);
    190   std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
    191       EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 3, 3 * sizeof(ExifRational));
    192   if (!entry) {
    193     exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
    194     LOGF(ERROR) << "Adding GPSLongitude exif entry failed";
    195     return false;
    196   }
    197   SetLatitudeOrLongitudeData(entry->data, longitude);
    198 
    199   return true;
    200 }
    201 
    202 bool ExifUtils::SetGpsAltitude(double altitude) {
    203   ExifTag refTag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE_REF);
    204   std::unique_ptr<ExifEntry> refEntry =
    205       AddVariableLengthEntry(EXIF_IFD_GPS, refTag, EXIF_FORMAT_BYTE, 1, 1);
    206   if (!refEntry) {
    207     LOGF(ERROR) << "Adding GPSAltitudeRef exif entry failed";
    208     return false;
    209   }
    210   if (altitude >= 0) {
    211     *refEntry->data = 0;
    212   } else {
    213     *refEntry->data = 1;
    214     altitude *= -1;
    215   }
    216 
    217   ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_ALTITUDE);
    218   std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
    219       EXIF_IFD_GPS, tag, EXIF_FORMAT_RATIONAL, 1, sizeof(ExifRational));
    220   if (!entry) {
    221     exif_content_remove_entry(exif_data_->ifd[EXIF_IFD_GPS], refEntry.get());
    222     LOGF(ERROR) << "Adding GPSAltitude exif entry failed";
    223     return false;
    224   }
    225   exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
    226                     {static_cast<ExifLong>(altitude * 1000), 1000});
    227 
    228   return true;
    229 }
    230 
    231 bool ExifUtils::SetGpsTimestamp(const struct tm& t) {
    232   const ExifTag dateTag = static_cast<ExifTag>(EXIF_TAG_GPS_DATE_STAMP);
    233   const size_t kGpsDateStampSize = 11;
    234   std::unique_ptr<ExifEntry> entry =
    235       AddVariableLengthEntry(EXIF_IFD_GPS, dateTag, EXIF_FORMAT_ASCII,
    236                              kGpsDateStampSize, kGpsDateStampSize);
    237   if (!entry) {
    238     LOGF(ERROR) << "Adding GPSDateStamp exif entry failed";
    239     return false;
    240   }
    241   int result =
    242       snprintf(reinterpret_cast<char*>(entry->data), kGpsDateStampSize,
    243                "%04i:%02i:%02i", t.tm_year + 1900, t.tm_mon + 1, t.tm_mday);
    244   if (result != kGpsDateStampSize - 1) {
    245     LOGF(WARNING) << "Input time is invalid";
    246     return false;
    247   }
    248 
    249   const ExifTag timeTag = static_cast<ExifTag>(EXIF_TAG_GPS_TIME_STAMP);
    250   entry = AddVariableLengthEntry(EXIF_IFD_GPS, timeTag, EXIF_FORMAT_RATIONAL, 3,
    251                                  3 * sizeof(ExifRational));
    252   if (!entry) {
    253     LOGF(ERROR) << "Adding GPSTimeStamp exif entry failed";
    254     return false;
    255   }
    256   exif_set_rational(entry->data, EXIF_BYTE_ORDER_INTEL,
    257                     {static_cast<ExifLong>(t.tm_hour), 1});
    258   exif_set_rational(entry->data + sizeof(ExifRational), EXIF_BYTE_ORDER_INTEL,
    259                     {static_cast<ExifLong>(t.tm_min), 1});
    260   exif_set_rational(entry->data + 2 * sizeof(ExifRational),
    261                     EXIF_BYTE_ORDER_INTEL,
    262                     {static_cast<ExifLong>(t.tm_sec), 1});
    263 
    264   return true;
    265 }
    266 
    267 bool ExifUtils::SetGpsProcessingMethod(const std::string& method) {
    268   ExifTag tag = static_cast<ExifTag>(EXIF_TAG_GPS_PROCESSING_METHOD);
    269   size_t size = sizeof(gExifAsciiPrefix) + method.length();
    270   std::unique_ptr<ExifEntry> entry = AddVariableLengthEntry(
    271       EXIF_IFD_GPS, tag, EXIF_FORMAT_UNDEFINED, size, size);
    272   if (!entry) {
    273     LOGF(ERROR) << "Adding GPSProcessingMethod exif entry failed";
    274     return false;
    275   }
    276   memcpy(entry->data, gExifAsciiPrefix, sizeof(gExifAsciiPrefix));
    277   // Since the exif format is undefined, NULL termination is not necessary.
    278   memcpy(entry->data + sizeof(gExifAsciiPrefix), method.c_str(),
    279          method.length());
    280 
    281   return true;
    282 }
    283 
    284 bool ExifUtils::SetThumbnailSize(uint16_t width, uint16_t height) {
    285   if (width % 2 != 0 || height % 2 != 0) {
    286     LOGF(ERROR) << "Invalid thumbnail size " << width << "x" << height;
    287     return false;
    288   }
    289   thumbnail_width_ = width;
    290   thumbnail_height_ = height;
    291   return true;
    292 }
    293 
    294 bool ExifUtils::SetOrientation(uint16_t orientation) {
    295   std::unique_ptr<ExifEntry> entry = AddEntry(EXIF_IFD_0, EXIF_TAG_ORIENTATION);
    296   if (!entry) {
    297     LOGF(ERROR) << "Adding Orientation exif entry failed";
    298     return false;
    299   }
    300   /*
    301    * Orientation value:
    302    *  1      2      3      4      5          6          7          8
    303    *
    304    *  888888 888888     88 88     8888888888 88                 88 8888888888
    305    *  88         88     88 88     88  88     88  88         88  88     88  88
    306    *  8888     8888   8888 8888   88         8888888888 8888888888         88
    307    *  88         88     88 88
    308    *  88         88 888888 888888
    309    */
    310   int value = 1;
    311   switch (orientation) {
    312     case 90:
    313       value = 6;
    314       break;
    315     case 180:
    316       value = 3;
    317       break;
    318     case 270:
    319       value = 8;
    320       break;
    321     default:
    322       break;
    323   }
    324   exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, value);
    325   return true;
    326 }
    327 
    328 bool ExifUtils::GenerateApp1() {
    329   DestroyApp1();
    330   if (thumbnail_width_ > 0 && thumbnail_height_ > 0) {
    331     if (!GenerateThumbnail()) {
    332       LOGF(ERROR) << "Generate thumbnail image failed";
    333       return false;
    334     }
    335     exif_data_->data = const_cast<uint8_t*>(
    336         static_cast<const uint8_t*>(compressor_.GetCompressedImagePtr()));
    337     exif_data_->size = compressor_.GetCompressedImageSize();
    338   }
    339   // Save the result into |app1_buffer_|.
    340   exif_data_save_data(exif_data_, &app1_buffer_, &app1_length_);
    341   if (!app1_length_) {
    342     LOGF(ERROR) << "Allocate memory for app1_buffer_ failed";
    343     return false;
    344   }
    345   /*
    346    * The JPEG segment size is 16 bits in spec. The size of APP1 segment should
    347    * be smaller than 65533 because there are two bytes for segment size field.
    348    */
    349   if (app1_length_ > 65533) {
    350     DestroyApp1();
    351     LOGF(ERROR) << "The size of APP1 segment is too large";
    352     return false;
    353   }
    354   return true;
    355 }
    356 
    357 const uint8_t* ExifUtils::GetApp1Buffer() { return app1_buffer_; }
    358 
    359 unsigned int ExifUtils::GetApp1Length() { return app1_length_; }
    360 
    361 void ExifUtils::Reset() {
    362   yu12_buffer_ = nullptr;
    363   yu12_width_ = 0;
    364   yu12_height_ = 0;
    365   thumbnail_width_ = 0;
    366   thumbnail_height_ = 0;
    367   DestroyApp1();
    368   if (exif_data_) {
    369     /*
    370      * Since we decided to ignore the original APP1, we are sure that there is
    371      * no thumbnail allocated by libexif. |exif_data_->data| is actually
    372      * allocated by JpegCompressor. Sets |exif_data_->data| to nullptr to
    373      * prevent exif_data_unref() destroy it incorrectly.
    374      */
    375     exif_data_->data = nullptr;
    376     exif_data_->size = 0;
    377     exif_data_unref(exif_data_);
    378     exif_data_ = nullptr;
    379   }
    380 }
    381 
    382 std::unique_ptr<ExifEntry> ExifUtils::AddVariableLengthEntry(
    383     ExifIfd ifd, ExifTag tag, ExifFormat format, uint64_t components,
    384     unsigned int size) {
    385   // Remove old entry if exists.
    386   exif_content_remove_entry(exif_data_->ifd[ifd],
    387                             exif_content_get_entry(exif_data_->ifd[ifd], tag));
    388   ExifMem* mem = exif_mem_new_default();
    389   if (!mem) {
    390     LOGF(ERROR) << "Allocate memory for exif entry failed";
    391     return nullptr;
    392   }
    393   std::unique_ptr<ExifEntry> entry(exif_entry_new_mem(mem));
    394   if (!entry) {
    395     LOGF(ERROR) << "Allocate memory for exif entry failed";
    396     exif_mem_unref(mem);
    397     return nullptr;
    398   }
    399   void* tmpBuffer = exif_mem_alloc(mem, size);
    400   if (!tmpBuffer) {
    401     LOGF(ERROR) << "Allocate memory for exif entry failed";
    402     exif_mem_unref(mem);
    403     return nullptr;
    404   }
    405 
    406   entry->data = static_cast<unsigned char*>(tmpBuffer);
    407   entry->tag = tag;
    408   entry->format = format;
    409   entry->components = components;
    410   entry->size = size;
    411 
    412   exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
    413   exif_mem_unref(mem);
    414 
    415   return entry;
    416 }
    417 
    418 std::unique_ptr<ExifEntry> ExifUtils::AddEntry(ExifIfd ifd, ExifTag tag) {
    419   std::unique_ptr<ExifEntry> entry(
    420       exif_content_get_entry(exif_data_->ifd[ifd], tag));
    421   if (entry) {
    422     // exif_content_get_entry() won't ref the entry, so we ref here.
    423     exif_entry_ref(entry.get());
    424     return entry;
    425   }
    426   entry.reset(exif_entry_new());
    427   if (!entry) {
    428     LOGF(ERROR) << "Allocate memory for exif entry failed";
    429     return nullptr;
    430   }
    431   entry->tag = tag;
    432   exif_content_add_entry(exif_data_->ifd[ifd], entry.get());
    433   exif_entry_initialize(entry.get(), tag);
    434   return entry;
    435 }
    436 
    437 bool ExifUtils::SetImageWidth(uint16_t width) {
    438   std::unique_ptr<ExifEntry> entry = AddEntry(EXIF_IFD_0, EXIF_TAG_IMAGE_WIDTH);
    439   if (!entry) {
    440     LOGF(ERROR) << "Adding ImageWidth exif entry failed";
    441     return false;
    442   }
    443   exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, width);
    444   return true;
    445 }
    446 
    447 bool ExifUtils::SetImageLength(uint16_t length) {
    448   std::unique_ptr<ExifEntry> entry =
    449       AddEntry(EXIF_IFD_0, EXIF_TAG_IMAGE_LENGTH);
    450   if (!entry) {
    451     LOGF(ERROR) << "Adding ImageLength exif entry failed";
    452     return false;
    453   }
    454   exif_set_short(entry->data, EXIF_BYTE_ORDER_INTEL, length);
    455   return true;
    456 }
    457 
    458 bool ExifUtils::GenerateThumbnail() {
    459   // Resize yuv image to |thumbnail_width_| x |thumbnail_height_|.
    460   std::vector<uint8_t> scaled_buffer;
    461   if (!GenerateYuvThumbnail(&scaled_buffer)) {
    462     LOGF(ERROR) << "Generate YUV thumbnail failed";
    463     return false;
    464   }
    465 
    466   // Compress thumbnail to JPEG.
    467   if (!compressor_.CompressImage(scaled_buffer.data(), thumbnail_width_,
    468                                  thumbnail_height_, thumbnail_jpeg_quality_,
    469                                  NULL, 0)) {
    470     LOGF(ERROR) << "Compress thumbnail failed";
    471     return false;
    472   }
    473   return true;
    474 }
    475 
    476 bool ExifUtils::GenerateYuvThumbnail(std::vector<uint8_t>* scaled_buffer) {
    477   size_t y_plane_size = yu12_width_ * yu12_height_;
    478   const uint8* y_plane = yu12_buffer_;
    479   const uint8* u_plane = y_plane + y_plane_size;
    480   const uint8* v_plane = u_plane + y_plane_size / 4;
    481 
    482   size_t scaled_y_plane_size = thumbnail_width_ * thumbnail_height_;
    483   scaled_buffer->resize(scaled_y_plane_size * 3 / 2);
    484   uint8* scaled_y_plane = scaled_buffer->data();
    485   uint8* scaled_u_plane = scaled_y_plane + scaled_y_plane_size;
    486   uint8* scaled_v_plane = scaled_u_plane + scaled_y_plane_size / 4;
    487 
    488   int result = libyuv::I420Scale(
    489       y_plane, yu12_width_, u_plane, yu12_width_ / 2, v_plane, yu12_width_ / 2,
    490       yu12_width_, yu12_height_, scaled_y_plane, thumbnail_width_,
    491       scaled_u_plane, thumbnail_width_ / 2, scaled_v_plane,
    492       thumbnail_width_ / 2, thumbnail_width_, thumbnail_height_,
    493       libyuv::kFilterNone);
    494   if (result != 0) {
    495     LOGF(ERROR) << "Scale I420 image failed";
    496     return false;
    497   }
    498   return true;
    499 }
    500 
    501 void ExifUtils::DestroyApp1() {
    502   /*
    503    * Since there is no API to access ExifMem in ExifData->priv, we use free
    504    * here, which is the default free function in libexif. See
    505    * exif_data_save_data() for detail.
    506    */
    507   free(app1_buffer_);
    508   app1_buffer_ = nullptr;
    509   app1_length_ = 0;
    510 }
    511 
    512 }  // namespace arc
    513