1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "exif_utils.h"
17 #include <cassert>
18 #include <cstdio>
19 #include <cstdint>
20 #include <cmath>
21 #include <cstring>
22 #include <iostream>
23 #include <camera.h>
24 #include "securec.h"
25 
26 namespace OHOS::Camera {
27 static const unsigned int IMAGE_DATA_OFFSET = 20;
28 
29 // Raw exif header data
30 static const unsigned char EXIF_HEADER[] = {0xff, 0xd8, 0xff, 0xe1};
31 
32 static const unsigned int EXIF_HEADER_LENGTH = sizeof(EXIF_HEADER);
33 
34 #define FILE_BYTE_ORDER EXIF_BYTE_ORDER_INTEL
35 
CreateTag(ExifData * exif,ExifIfd ifd,ExifTag tag,size_t len,ExifFormat format)36 static ExifEntry *CreateTag(ExifData *exif, ExifIfd ifd, ExifTag tag, size_t len, ExifFormat format)
37 {
38     void *buf = nullptr;
39     ExifEntry *entry = nullptr;
40 
41     ExifMem *mem = exif_mem_new_default();
42     assert(mem != NULL);
43 
44     entry = exif_entry_new_mem(mem);
45     assert(entry != nullptr);
46 
47     buf = exif_mem_alloc(mem, len);
48     assert(buf != nullptr);
49 
50     entry->data = static_cast<unsigned char*>(buf);
51     entry->size = len;
52     entry->tag = tag;
53     entry->components = len;
54     entry->format = format;
55 
56     exif_content_add_entry(exif->ifd[ifd], entry);
57 
58     exif_mem_unref(mem);
59     exif_entry_unref(entry);
60 
61     return entry;
62 }
63 
GetGpsRef(LatOrLong latOrLongType,double number,char * gpsRef,int length)64 uint32_t ExifUtils::GetGpsRef(LatOrLong latOrLongType, double number, char *gpsRef, int length)
65 {
66     char north[2] = "N";
67     char south[2] = "S";
68     char east[2] = "E";
69     char west[2] = "W";
70 
71     if (gpsRef == nullptr) {
72         CAMERA_LOGE("%{public}s gpsRef is null.", __FUNCTION__);
73         return RC_ERROR;
74     }
75 
76     if (latOrLongType == LATITUDE_TYPE) {
77         if (number > 0) {
78             if (strncpy_s(gpsRef, length, north, strlen(north)) != 0) {
79                 CAMERA_LOGE("%{public}s exif strncpy_s failed.", __FUNCTION__);
80                 return RC_ERROR;
81             }
82         } else {
83             if (strncpy_s(gpsRef, length, south, strlen(south)) != 0) {
84                 CAMERA_LOGE("%{public}s exif strncpy_s failed.", __FUNCTION__);
85                 return RC_ERROR;
86             }
87         }
88     } else {
89         if (number > 0) {
90             if (strncpy_s(gpsRef, length, east, strlen(east)) != 0) {
91                 CAMERA_LOGE("%{public}s exif strncpy_s failed.", __FUNCTION__);
92                 return RC_ERROR;
93             }
94         } else {
95             if (strncpy_s(gpsRef, length, west, strlen(west)) != 0) {
96                 CAMERA_LOGE("%{public}s exif strncpy_s failed.", __FUNCTION__);
97                 return RC_ERROR;
98             }
99         }
100     }
101 
102     return RC_OK;
103 }
104 
AddLatOrLongInfo(ExifData * exif,double number,LatOrLong latOrLongType)105 uint32_t ExifUtils::AddLatOrLongInfo(ExifData *exif,
106     double number, LatOrLong latOrLongType)
107 {
108     ExifEntry *entry = nullptr;
109     char gpsRef[2] = {0}; // Index
110     ExifRational gpsRational[3]; // Index
111     int32_t degree = 0;
112     int32_t minute = 0;
113     int32_t second = 0;
114 
115     if (GetGpsRef(latOrLongType, number, gpsRef, sizeof(gpsRef)) != RC_OK) {
116         CAMERA_LOGE("%{public}s exif GetGpsRef failed.", __FUNCTION__);
117         return RC_ERROR;
118     }
119 
120     ConvertGpsDataToDms(number, &degree, &minute, &second);
121     gpsRational[0].numerator = degree; // Index
122     gpsRational[0].denominator = 1;
123     gpsRational[1].numerator = minute; // Index
124     gpsRational[1].denominator = 1;
125     gpsRational[2].numerator = second; // Index
126     gpsRational[2].denominator = 1;
127 
128     // LATITUDE_TYPE/LONGITUDE_TYPE reference
129     if (latOrLongType == LATITUDE_TYPE) {
130         entry = CreateTag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE_REF, sizeof(gpsRef), EXIF_FORMAT_ASCII);
131     } else {
132         entry = CreateTag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE_REF, sizeof(gpsRef), EXIF_FORMAT_ASCII);
133     }
134     if (memcpy_s(entry->data, entry->size, gpsRef, sizeof(gpsRef)) != 0) {
135         CAMERA_LOGE("%{public}s exif memcpy_s failed.", __FUNCTION__);
136         return RC_ERROR;
137     }
138     // LATITUDE_TYPE/LONGITUDE_TYPE value
139     constexpr uint32_t gpsDmsCount = 3;
140     if (latOrLongType == LATITUDE_TYPE) {
141         entry = CreateTag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LATITUDE,
142             gpsDmsCount * exif_format_get_size(EXIF_FORMAT_RATIONAL),
143             EXIF_FORMAT_RATIONAL);
144     } else {
145         entry = CreateTag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_LONGITUDE,
146             gpsDmsCount * exif_format_get_size(EXIF_FORMAT_RATIONAL),
147             EXIF_FORMAT_RATIONAL);
148     }
149     exif_set_rational(entry->data, FILE_BYTE_ORDER, gpsRational[0]);
150     exif_set_rational(entry->data + 8, FILE_BYTE_ORDER, gpsRational[1]); // 8bit
151     exif_set_rational(entry->data + 16, FILE_BYTE_ORDER, gpsRational[2]); // 16bit
152     return RC_OK;
153 }
154 
AddAltitudeInfo(ExifData * exif,double altitude)155 uint32_t ExifUtils::AddAltitudeInfo(ExifData *exif, double altitude)
156 {
157     unsigned char seaLevelFlag = 0;
158     ExifEntry *entry = nullptr;
159     ExifRational gpsAltitudeRational;
160     exif_rational altitudeRational;
161 
162     if (altitude > 0) {
163         seaLevelFlag = 0;
164     } else {
165         altitude = abs(altitude);
166         seaLevelFlag = 1;
167     }
168     ConvertAltitudeToRational(altitude, altitudeRational);
169     gpsAltitudeRational.numerator = altitudeRational.numerator;
170     gpsAltitudeRational.denominator = altitudeRational.denominator;
171     // Altitude reference
172     entry = CreateTag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE_REF, sizeof(seaLevelFlag), EXIF_FORMAT_BYTE);
173     exif_set_short(entry->data, FILE_BYTE_ORDER, seaLevelFlag);
174 
175     // Altitude value
176     entry = CreateTag(exif, EXIF_IFD_GPS, EXIF_TAG_GPS_ALTITUDE, exif_format_get_size(EXIF_FORMAT_RATIONAL),
177         EXIF_FORMAT_RATIONAL);
178     exif_set_rational(entry->data, FILE_BYTE_ORDER, gpsAltitudeRational);
179     return RC_OK;
180 }
181 
IsJpegPicture(unsigned char * dataBuffer,int32_t dataBufferSize,void * address)182 uint32_t ExifUtils::IsJpegPicture(unsigned char *dataBuffer, int32_t dataBufferSize, void *address)
183 {
184     if (memcpy_s(dataBuffer, dataBufferSize, address, dataBufferSize) != 0) {
185         CAMERA_LOGE("%{public}s exif memcpy_s failed.", __FUNCTION__);
186         return RC_ERROR;
187     }
188 
189     if (!(dataBuffer[0] == 0xFF && dataBuffer[1] == 0xD8)) {
190         CAMERA_LOGE("%{public}s not jpeg file,won't add exif for it.", __FUNCTION__);
191         return RC_ERROR;
192     }
193 
194     if ((dataBuffer[6] == 'E' && dataBuffer[7] == 'x' && dataBuffer[8] == 'i' && dataBuffer[9] == 'f')) { // Index
195         CAMERA_LOGE("%{public}s already add exif, won't overwrite exif info.", __FUNCTION__);
196         return RC_ERROR;
197     }
198     return RC_OK;
199 }
200 
PackageJpeg(unsigned char * tempBuffer,int32_t totalTempBufferSize,unsigned char * exifData,unsigned int exifDataLength,data_info sourceData)201 uint32_t ExifUtils::PackageJpeg(unsigned char *tempBuffer, int32_t totalTempBufferSize, unsigned char *exifData,
202     unsigned int exifDataLength, data_info sourceData)
203 {
204     unsigned char orderValue = 0;
205     unsigned char value = 0;
206     constexpr uint32_t exifBlockLength = 2;
207     orderValue = (exifDataLength + exifBlockLength) >> 8; // 8bit
208     value = (exifDataLength + exifBlockLength) & 0xff;
209     if (memcpy_s(tempBuffer, totalTempBufferSize, EXIF_HEADER, EXIF_HEADER_LENGTH) != 0) {
210         CAMERA_LOGE("%{public}s exif memcpy_s failed.", __FUNCTION__);
211         return RC_ERROR;
212     }
213     if (memcpy_s(tempBuffer + EXIF_HEADER_LENGTH, totalTempBufferSize, &orderValue,
214         sizeof(orderValue)) != 0) {
215         CAMERA_LOGE("%{public}s exif memcpy_s failed.", __FUNCTION__);
216         return RC_ERROR;
217     }
218     if (memcpy_s(tempBuffer + EXIF_HEADER_LENGTH + sizeof(orderValue), totalTempBufferSize, &value,
219         sizeof(value)) != 0) {
220         CAMERA_LOGE("%{public}s exif memcpy_s failed.", __FUNCTION__);
221         return RC_ERROR;
222     }
223     if (memcpy_s(tempBuffer + EXIF_HEADER_LENGTH + sizeof(orderValue) + sizeof(value), totalTempBufferSize,
224         exifData, exifDataLength) != 0) {
225         CAMERA_LOGE("%{public}s exif memcpy_s failed.", __FUNCTION__);
226         return RC_ERROR;
227     }
228     if (memcpy_s(tempBuffer + EXIF_HEADER_LENGTH + sizeof(orderValue) + sizeof(value) + exifDataLength,
229         totalTempBufferSize,
230         sourceData.dataBuffer + IMAGE_DATA_OFFSET,
231         sourceData.dataBufferSize - IMAGE_DATA_OFFSET) != 0) {
232         CAMERA_LOGE("%{public}s exif memcpy_s failed.", __FUNCTION__);
233         return RC_ERROR;
234     }
235     return RC_OK;
236 }
237 
SetExifData(exif_data info,ExifData * exif,unsigned char ** exifData,unsigned int * exifDataLength)238 uint32_t ExifUtils::SetExifData(exif_data info, ExifData *exif,
239     unsigned char **exifData, unsigned int *exifDataLength)
240 {
241     CHECK_IF_PTR_NULL_RETURN_VALUE(exif, RC_ERROR);
242 
243     exif_data_set_option(exif, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
244     exif_data_set_data_type(exif, EXIF_DATA_TYPE_COMPRESSED);
245     exif_data_set_byte_order(exif, FILE_BYTE_ORDER);
246     if (AddLatOrLongInfo(exif, info.latitude, LATITUDE_TYPE) != RC_OK) {
247         return RC_ERROR;
248     }
249     if (AddLatOrLongInfo(exif, info.longitude, LONGITUDE_TYPE) != RC_OK) {
250         return RC_ERROR;
251     }
252     if (AddAltitudeInfo(exif, info.altitude) != RC_OK) {
253         return RC_ERROR;
254     }
255     exif_data_save_data(exif, exifData, exifDataLength);
256 
257     return RC_OK;
258 }
259 
FreeResource(unsigned char * dataBuffer,unsigned char * tempBuffer,ExifData * exif,unsigned char * exifData)260 void ExifUtils::FreeResource(unsigned char *dataBuffer, unsigned char *tempBuffer,
261     ExifData *exif, unsigned char *exifData)
262 {
263     if (dataBuffer != nullptr) {
264         free(dataBuffer);
265     }
266     if (tempBuffer != nullptr) {
267         free(tempBuffer);
268     }
269     free(exifData);
270     exif_data_unref(exif);
271 }
272 
AddCustomExifInfo(exif_data info,void * address,int32_t & outPutSize)273 uint32_t ExifUtils::AddCustomExifInfo(exif_data info, void *address, int32_t &outPutSize)
274 {
275     int32_t ret = RC_ERROR;
276     unsigned char *exifData = nullptr;
277     unsigned int exifDataLength = 0;
278     ExifData *exif = nullptr;
279     unsigned char *dataBuffer = nullptr;
280     unsigned char *tempBuffer = nullptr;
281     int32_t totalTempBufferSize = 0;
282     int32_t dataBufferSize = info.frame_size;
283     constexpr uint32_t exifBlockLength = 2;
284 
285     exif = exif_data_new();
286     if (!exif) {
287         CAMERA_LOGE("%{public}s exif new failed.", __FUNCTION__);
288         return ret;
289     }
290 
291     if (SetExifData(info, exif, &exifData, &exifDataLength) != RC_OK) {
292         CAMERA_LOGE("%{public}s exif SetExifData failed.", __FUNCTION__);
293         return ret;
294     }
295 
296     dataBuffer = static_cast<unsigned char *>(malloc(dataBufferSize));
297     if (!dataBuffer) {
298         CAMERA_LOGE("%{public}s Allocate data buf failed.", __FUNCTION__);
299         return ret;
300     }
301     data_info sourceData;
302     sourceData.dataBuffer = dataBuffer;
303     sourceData.dataBufferSize = dataBufferSize;
304 
305     // Check buffer whether is valid
306     if (IsJpegPicture(dataBuffer, dataBufferSize, address) == RC_ERROR) {
307         goto error;
308     }
309     totalTempBufferSize = EXIF_HEADER_LENGTH + exifBlockLength +  exifDataLength +
310         (static_cast<uint32_t>(dataBufferSize) - IMAGE_DATA_OFFSET);
311     tempBuffer = static_cast<unsigned char *>(malloc(totalTempBufferSize));
312     if (!tempBuffer) {
313         CAMERA_LOGE("%{public}s Allocate temp buf failed.", __FUNCTION__);
314         return ret;
315     }
316     ret = PackageJpeg(tempBuffer, totalTempBufferSize, exifData, exifDataLength, sourceData);
317     outPutSize = totalTempBufferSize;
318     if (memcpy_s(address, totalTempBufferSize, tempBuffer, totalTempBufferSize) != 0) {
319         CAMERA_LOGE("%{public}s exif memcpy_s failed.", __FUNCTION__);
320         return RC_ERROR;
321     }
322 
323 error:
324     FreeResource(dataBuffer, tempBuffer, exif, exifData);
325 
326     return ret;
327 }
328 
ConvertGpsDataToDms(double number,int32_t * degrees,int32_t * minutes,int32_t * seconds)329 void ExifUtils::ConvertGpsDataToDms(double number, int32_t *degrees, int32_t *minutes, int32_t *seconds)
330 {
331     number = abs(number);
332     double  approximateNumber = 0.0;
333     constexpr uint32_t timePeriod = 60;
334     constexpr uint32_t roundingValue = 5;
335     constexpr uint32_t precision = 10;
336     int32_t hour = static_cast<int32_t>(number);
337     int32_t minute = static_cast<int32_t>((number - hour) * timePeriod);
338     int32_t second = static_cast<int32_t>(((number - hour) * timePeriod - minute) * timePeriod);
339 
340     approximateNumber = ((number - hour) * timePeriod - minute) * timePeriod - second;
341     if (static_cast<int32_t>(approximateNumber * precision) >= roundingValue) {
342         second = second + 1;
343     }
344     if (second == timePeriod) {
345         second = 0;
346         minute = minute + 1;
347     }
348     if (minute == timePeriod) {
349         minute = 0;
350         hour = hour + 1;
351     }
352     *degrees = hour;
353     *minutes = minute;
354     *seconds = second;
355 
356     return;
357 }
358 
ConvertAltitudeToRational(double altitude,exif_rational & outPutAltitude)359 void ExifUtils::ConvertAltitudeToRational(double altitude, exif_rational &outPutAltitude)
360 {
361     long long numerator = 0;
362     long long denominator = 1;
363     bool isSeparator = false;
364     int count = 0;
365     std::string strData = "";
366     strData = std::to_string(altitude);
367     CAMERA_LOGI("%{public}s strData = %{public}s", __FUNCTION__, strData.c_str());
368 
369     count = strData.length();
370     CAMERA_LOGI("%{public}s count = %{public}d", __FUNCTION__, count);
371     constexpr uint32_t digitPosition = 10;
372     for (int i = 0; i < count; i++) {
373         char character = strData[i];
374         if (character == '.') {
375             isSeparator = true;
376         } else {
377             numerator = numerator * digitPosition + (character  - '0');
378             CAMERA_LOGI("%{public}s numerator =  %{public}lld", __FUNCTION__, numerator);
379             if (isSeparator) {
380                 denominator *= digitPosition;
381                 CAMERA_LOGI("%{public}s denominator =  %{public}lld", __FUNCTION__, denominator);
382             }
383         }
384     }
385     constexpr uint32_t commonDivisor = 2;
386     constexpr uint32_t resetValue = 1;
387     for (int i = commonDivisor; static_cast<long long>(i) < numerator; i++) {
388         if ((numerator % i == 0) && (denominator % i == 0)) {
389             numerator /= i;
390             denominator /= i;
391             i = resetValue;
392         }
393     }
394 
395     outPutAltitude.numerator = numerator;
396     outPutAltitude.denominator = denominator;
397     CAMERA_LOGI("%{public}s outPutAltitude.numerator =  %{public}d and outPutAltitude.denominator =  %{public}d",
398         __FUNCTION__, outPutAltitude.numerator, outPutAltitude.denominator);
399 }
400 }  // namespace OHOS::Camera
401