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
26namespace OHOS::Camera {
27static const unsigned int IMAGE_DATA_OFFSET = 20;
28
29// Raw exif header data
30static const unsigned char EXIF_HEADER[] = {0xff, 0xd8, 0xff, 0xe1};
31
32static const unsigned int EXIF_HEADER_LENGTH = sizeof(EXIF_HEADER);
33
34#define FILE_BYTE_ORDER EXIF_BYTE_ORDER_INTEL
35
36static 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
64uint32_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
105uint32_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
155uint32_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
182uint32_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
201uint32_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
238uint32_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
260void 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
273uint32_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
323error:
324    FreeResource(dataBuffer, tempBuffer, exif, exifData);
325
326    return ret;
327}
328
329void 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
359void 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