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> {
operator ()std::default_delete20   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 
SetLatitudeOrLongitudeData(unsigned char * data,double num)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 
ExifUtils()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 
~ExifUtils()54 ExifUtils::~ExifUtils() { Reset(); }
55 
Initialize(const uint8_t * buffer,uint16_t width,uint16_t height,int quality)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 
SetMaker(const std::string & maker)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 
SetModel(const std::string & model)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 
SetDateTime(const struct tm & t)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 
SetFocalLength(uint32_t numerator,uint32_t denominator)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 
SetGpsLatitude(double latitude)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 
SetGpsLongitude(double longitude)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 
SetGpsAltitude(double altitude)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 
SetGpsTimestamp(const struct tm & t)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 
SetGpsProcessingMethod(const std::string & method)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 
SetThumbnailSize(uint16_t width,uint16_t height)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 
SetOrientation(uint16_t orientation)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 
GenerateApp1()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 
GetApp1Buffer()358 const uint8_t* ExifUtils::GetApp1Buffer() { return app1_buffer_; }
359 
GetApp1Length()360 unsigned int ExifUtils::GetApp1Length() { return app1_length_; }
361 
Reset()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 
AddVariableLengthEntry(ExifIfd ifd,ExifTag tag,ExifFormat format,uint64_t components,unsigned int size)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 
AddEntry(ExifIfd ifd,ExifTag tag)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 
SetImageWidth(uint16_t width)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 
SetImageLength(uint16_t length)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 
GenerateThumbnail()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 
GenerateYuvThumbnail(std::vector<uint8_t> * scaled_buffer)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 
DestroyApp1()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