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