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