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