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_t* y_plane = yu12_buffer_;
479 const uint8_t* u_plane = y_plane + y_plane_size;
480 const uint8_t* 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_t* scaled_y_plane = scaled_buffer->data();
485 uint8_t* scaled_u_plane = scaled_y_plane + scaled_y_plane_size;
486 uint8_t* 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