| /* |
| * Copyright (C) 2012-2019 Intel Corporation |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "EXIFMaker" |
| |
| #include <limits.h> // LONG_MAX, LONG_MIN |
| #include <sstream> |
| #include <iomanip> |
| #include "EXIFMaker.h" |
| #include "LogHelper.h" |
| |
| #include "PlatformData.h" |
| #include "Utils.h" |
| #include "common/CommonUtilMacros.h" |
| |
| #define DEFAULT_ISO_SPEED 100 |
| #define EPSILON 0.00001 |
| |
| namespace cros { |
| namespace intel { |
| EXIFMaker::EXIFMaker() : |
| exifSize(-1) |
| ,initialized(false) |
| { |
| LOG1("@%s", __FUNCTION__); |
| CLEAR(exifAttributes); |
| } |
| |
| EXIFMaker::~EXIFMaker() |
| { |
| LOG1("@%s", __FUNCTION__); |
| } |
| |
| /** |
| * * Sets MakerNote field data. |
| * */ |
| void EXIFMaker::setMakerNote(const ia_binary_data &aaaMkNoteData) |
| { |
| LOG1("@%s: %d bytes", __FUNCTION__, aaaMkNoteData.size); |
| |
| if (aaaMkNoteData.data) { |
| exifAttributes.makerNoteDataSize = aaaMkNoteData.size; |
| exifAttributes.makerNoteData = (unsigned char*) aaaMkNoteData.data; |
| } |
| } |
| |
| uint32_t EXIFMaker::getMakerNoteDataSize() const |
| { |
| LOG1("@%s", __FUNCTION__); |
| // overhead: MAKERNOTE_ID + APP2 marker + length field |
| return exifAttributes.makerNoteDataSize + SIZEOF_APP2_OVERHEAD; |
| } |
| |
| /** |
| * Sets picture meta data retrieved from atomisp kernel driver. |
| * |
| * @param ispData struct retrieved with ATOMISP_IOC_ISP_MAKERNOTE |
| * kernel ioctl |
| */ |
| void EXIFMaker::setDriverData(const ExifMetaData::MakernoteType &ispData) |
| { |
| LOG1("@%s", __FUNCTION__); |
| |
| // f number |
| if (ispData.f_number_curr > 0) { |
| exifAttributes.fnumber.num = ispData.f_number_curr >> 16; |
| exifAttributes.fnumber.den = ispData.f_number_curr & 0xffff; |
| |
| double f_number = (double)exifAttributes.fnumber.num / |
| (double)exifAttributes.fnumber.den; |
| exifAttributes.max_aperture.num = |
| (uint32_t)(10000 * APEX_FNUM_TO_APERTURE(f_number)); |
| exifAttributes.max_aperture.den = 10000; |
| } else { |
| LOGW("Invalid fnumber %u from driver", ispData.f_number_curr); |
| } |
| |
| LOG1("EXIF: fnumber=%u (num=%d, den=%d)", ispData.f_number_curr, |
| exifAttributes.fnumber.num, exifAttributes.fnumber.den); |
| |
| // the actual focal length of the lens, in mm. |
| // there is no API for lens position. |
| if (ispData.focal_length > 0) { |
| //the focal length unit (mm * 100) from CMC |
| exifAttributes.focal_length.num = ispData.focal_length; |
| exifAttributes.focal_length.den = 100; |
| } else { |
| LOGW("Invalid focal length %u from driver", ispData.focal_length); |
| } |
| |
| LOG1("EXIF: focal length=%u (num=%d, den=%d)", ispData.focal_length, |
| exifAttributes.focal_length.num, exifAttributes.focal_length.den); |
| } |
| |
| /** |
| * Fills EXIF data after a picture has been taken to |
| * record the active sensor, 3A and ISP state to EXIF metadata. |
| * |
| * This function is intented to set EXIF tags belonging |
| * to the EXIF "Per Picture Camera Setting" group. |
| * |
| * @arg params active Android HAL parameters |
| */ |
| void EXIFMaker::pictureTaken(ExifMetaData& exifmetadata) |
| { |
| LOG1("@%s", __FUNCTION__); |
| |
| // brightness, -99.99 to 99.99. FFFFFFFF.H means unknown. |
| float brightness; |
| // TODO: The check for getAeManualBrightness of 3A should be moved |
| // to MetaData class, because the metadata collection happen |
| // at capture time |
| brightness = exifmetadata.mIa3ASetting.brightness; |
| exifAttributes.brightness.num = static_cast<int>(brightness * 100); |
| exifAttributes.brightness.den = 100; |
| LOG1("EXIF: brightness = %.2f", brightness); |
| |
| exifAttributes.contrast = exifmetadata.mIa3ASetting.contrast; |
| exifAttributes.saturation = exifmetadata.mIa3ASetting.saturation; |
| exifAttributes.sharpness = exifmetadata.mIa3ASetting.sharpness; |
| LOG1("EXIF: contrast=%d, saturation=%d, sharpness=%d (0:normal 1:low 2:high)", |
| exifAttributes.contrast, |
| exifAttributes.saturation, |
| exifAttributes.sharpness); |
| |
| // set the exposure program mode |
| AeMode aeMode = exifmetadata.mIa3ASetting.aeMode; |
| switch (aeMode) { |
| case CAM_AE_MODE_MANUAL: |
| exifAttributes.exposure_program = EXIF_EXPOSURE_PROGRAM_MANUAL; |
| LOG1("EXIF: Exposure Program = Manual"); |
| exifAttributes.exposure_mode = EXIF_EXPOSURE_MANUAL; |
| LOG1("EXIF: Exposure Mode = Manual"); |
| break; |
| case CAM_AE_MODE_SHUTTER_PRIORITY: |
| exifAttributes.exposure_program = EXIF_EXPOSURE_PROGRAM_SHUTTER_PRIORITY; |
| LOG1("EXIF: Exposure Program = Shutter Priority"); |
| break; |
| case CAM_AE_MODE_APERTURE_PRIORITY: |
| exifAttributes.exposure_program = EXIF_EXPOSURE_PROGRAM_APERTURE_PRIORITY; |
| LOG1("EXIF: Exposure Program = Aperture Priority"); |
| break; |
| case CAM_AE_MODE_AUTO: |
| default: |
| exifAttributes.exposure_program = EXIF_EXPOSURE_PROGRAM_NORMAL; |
| LOG1("EXIF: Exposure Program = Normal"); |
| exifAttributes.exposure_mode = EXIF_EXPOSURE_AUTO; |
| LOG1("EXIF: Exposure Mode = Auto"); |
| break; |
| } |
| |
| // indicates the ISO speed of the camera |
| if (exifmetadata.mIa3ASetting.isoSpeed > 0) { |
| exifAttributes.iso_speed_rating = exifmetadata.mIa3ASetting.isoSpeed; |
| } else { |
| LOGW("EXIF: Could not query ISO speed!"); |
| exifAttributes.iso_speed_rating = DEFAULT_ISO_SPEED; |
| } |
| LOG1("EXIF: ISO=%d", exifAttributes.iso_speed_rating); |
| |
| // the metering mode. |
| MeteringMode meteringMode = exifmetadata.mIa3ASetting.meteringMode; |
| switch (meteringMode) { |
| case CAM_AE_METERING_MODE_AUTO: |
| exifAttributes.metering_mode = EXIF_METERING_AVERAGE; |
| LOG1("EXIF: Metering Mode = Average"); |
| break; |
| case CAM_AE_METERING_MODE_SPOT: |
| exifAttributes.metering_mode = EXIF_METERING_SPOT; |
| LOG1("EXIF: Metering Mode = Spot"); |
| break; |
| case CAM_AE_METERING_MODE_CENTER: |
| exifAttributes.metering_mode = EXIF_METERING_CENTER; |
| LOG1("EXIF: Metering Mode = Center"); |
| break; |
| case CAM_AE_METERING_MODE_CUSTOMIZED: |
| default: |
| exifAttributes.metering_mode = EXIF_METERING_OTHER; |
| LOG1("EXIF: Metering Mode = Other"); |
| break; |
| } |
| |
| // white balance mode. 0: auto; 1: manual |
| AwbMode awbMode = exifmetadata.mAwbMode; |
| switch (awbMode) { |
| case CAM_AWB_MODE_AUTO: |
| case CAM_AWB_MODE_NOT_SET: |
| exifAttributes.white_balance = EXIF_WB_AUTO; |
| LOG1("EXIF: Whitebalance = Auto"); |
| break; |
| default: |
| exifAttributes.white_balance = EXIF_WB_MANUAL; |
| LOG1("EXIF: Whitebalance = Manual"); |
| break; |
| } |
| |
| // light source type. Refer to EXIF V2.2 |
| // TBD. Now light source is only set to UNKNOWN, when WB is auto mode. |
| if (CAM_AWB_MODE_AUTO == awbMode) { |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_UNKNOWN; |
| } |
| else { |
| AwbMode lightSource = exifmetadata.mIa3ASetting.lightSource; |
| switch (lightSource) { |
| case CAM_AWB_MODE_MANUAL_INPUT: |
| case CAM_AWB_MODE_AUTO: |
| case CAM_AWB_MODE_NOT_SET: |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_OTHER_LIGHT_SOURCE; |
| break; |
| case CAM_AWB_MODE_SUNSET: |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_TUNGSTEN; |
| break; |
| case CAM_AWB_MODE_DAYLIGHT: |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_FINE_WEATHER; |
| break; |
| case CAM_AWB_MODE_CLOUDY: |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_CLOUDY_WEATHER; |
| break; |
| case CAM_AWB_MODE_SHADOW: |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_SHADE; |
| break; |
| case CAM_AWB_MODE_TUNGSTEN: |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_TUNGSTEN; |
| break; |
| case CAM_AWB_MODE_FLUORESCENT: |
| case CAM_AWB_MODE_WARM_FLUORESCENT: |
| //Since EXIF V2.2 not defined the warm white fluorescent, using fluorescent TAG for it |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_FLUORESCENT; |
| break; |
| case CAM_AWB_MODE_WARM_INCANDESCENT: |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_TUNGSTEN; |
| break; |
| default: |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_OTHER_LIGHT_SOURCE; |
| break; |
| } |
| } |
| // Since hal will correct rotation for image, no need to assign orientation |
| exifAttributes.orientation = EXIF_ORIENTATION_UNDEFINED; |
| |
| exifAttributes.zoom_ratio.num = exifmetadata.mZoomRatio; |
| exifAttributes.zoom_ratio.den = 100; |
| // the unit of subjectDistance is meter, focus distance from 3A is mm. |
| exifAttributes.subject_distance.num = exifmetadata.mIa3ASetting.focusDistance; |
| exifAttributes.subject_distance.den = 1000; |
| LOG2("subject_distance is %d", exifAttributes.subject_distance.num); |
| } |
| |
| /** |
| * Called when the the camera static configuration is known. |
| * |
| * @arg width: width of the main JPEG picture. |
| * @arg height: height of the main JPEG picture. |
| */ |
| void EXIFMaker::initialize(int width, int height) |
| { |
| /* We clear the exif attributes, so we won't be using some old values |
| * from a previous EXIF generation. |
| */ |
| clear(); |
| |
| // Initialize the exifAttributes with specific values |
| // time information |
| time_t rawtime; |
| struct tm *timeinfo; |
| time(&rawtime); |
| timeinfo = localtime(&rawtime); |
| if (timeinfo) { |
| strftime((char *)exifAttributes.date_time, sizeof(exifAttributes.date_time), "%Y:%m:%d %H:%M:%S", timeinfo); |
| // fields: tm_sec, tm_min, tm_hour, tm_mday, tm_mon, tm_year, tm_wday, tm_yday, tm_isdst, tm_gmtoff, tm_zone |
| } else { |
| LOGW("nullptr timeinfo from localtime(), using defaults..."); |
| struct tm tmpTime = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "UTC"}; |
| strftime((char *)exifAttributes.date_time, sizeof(exifAttributes.date_time), "%Y:%m:%d %H:%M:%S", &tmpTime); |
| } |
| |
| // set default subsec time to 1000 |
| const char subsecTime[] = "1000"; |
| MEMCPY_S(exifAttributes.subsec_time, sizeof(exifAttributes.subsec_time), subsecTime, sizeof(subsecTime)); |
| |
| // conponents configuration. |
| // Default = 4 5 6 0(if RGB uncompressed), 1 2 3 0(other cases) |
| // 0 = does not exist; 1 = Y; 2 = Cb; 3 = Cr; 4 = R; 5 = G; 6 = B; other = reserved |
| exifAttributes.components_configuration[0] = 1; |
| exifAttributes.components_configuration[1] = 2; |
| exifAttributes.components_configuration[2] = 3; |
| exifAttributes.components_configuration[3] = 0; |
| |
| // set default values for fnumber and focal length |
| // (see EXIFMaker::setDriverData() how to override these) |
| exifAttributes.fnumber.num = EXIF_DEF_FNUMBER_NUM; |
| exifAttributes.fnumber.den = EXIF_DEF_FNUMBER_DEN; |
| exifAttributes.focal_length.num = EXIF_DEF_FOCAL_LEN_NUM; |
| exifAttributes.focal_length.den = EXIF_DEF_FOCAL_LEN_DEN; |
| |
| // TODO: should ISO be omitted if the value cannot be trusted? |
| exifAttributes.iso_speed_rating = DEFAULT_ISO_SPEED; |
| |
| // max aperture. the smallest F number of the lens. unit is APEX value. |
| // TBD, should get from driver |
| exifAttributes.max_aperture.num = exifAttributes.aperture.num; |
| exifAttributes.max_aperture.den = exifAttributes.aperture.den; |
| |
| // subject distance, 0 means distance unknown; (~0) means infinity. |
| exifAttributes.subject_distance.num = EXIF_DEF_SUBJECT_DISTANCE_UNKNOWN; |
| exifAttributes.subject_distance.den = 1; |
| |
| // light source, 0 means light source unknown |
| exifAttributes.light_source = 0; |
| // TODO: for awb mode |
| |
| |
| // gain control, 0 = none; |
| // 1 = low gain up; 2 = high gain up; 3 = low gain down; 4 = high gain down |
| exifAttributes.gain_control = 0; |
| |
| // contrast, 0 = normal; 1 = soft; 2 = hard; other = reserved |
| exifAttributes.contrast = EXIF_CONTRAST_NORMAL; |
| |
| // saturation, 0 = normal; 1 = Low saturation; 2 = High saturation; other = reserved |
| exifAttributes.saturation = EXIF_SATURATION_NORMAL; |
| |
| // sharpness, 0 = normal; 1 = soft; 2 = hard; other = reserved |
| exifAttributes.sharpness = EXIF_SHARPNESS_NORMAL; |
| |
| // the picture's width and height |
| exifAttributes.width = width; |
| exifAttributes.height = height; |
| |
| exifAttributes.orientation = 1; |
| |
| exifAttributes.custom_rendered = EXIF_DEF_CUSTOM_RENDERED; |
| |
| // metering mode, 0 = normal; 1 = soft; 2 = hard; other = reserved |
| exifAttributes.metering_mode = EXIF_METERING_UNKNOWN; |
| //initializeLocation(params); |
| initialized = true; |
| } |
| |
| void EXIFMaker::initializeLocation(ExifMetaData& metadata) |
| { |
| LOG1("@%s", __FUNCTION__); |
| // GIS information |
| bool gpsEnabled = false; |
| double latitude = metadata.mGpsSetting.latitude; |
| double longitude = metadata.mGpsSetting.longitude; |
| double altitude = metadata.mGpsSetting.altitude; |
| long timestamp = metadata.mGpsSetting.gpsTimeStamp; |
| char* pprocmethod = metadata.mGpsSetting.gpsProcessingMethod; |
| |
| // check whether the GIS Information is valid |
| if(!(latitude >= -EPSILON && latitude <= EPSILON) || |
| !(longitude >= -EPSILON && longitude <= EPSILON) || |
| !(altitude >= -EPSILON && altitude <= EPSILON) || |
| (timestamp != 0) || (STRLEN_S(pprocmethod) != 0)) |
| gpsEnabled = true; |
| |
| exifAttributes.enableGps = 0; |
| LOG1("EXIF: gpsEnabled: %d", gpsEnabled); |
| |
| // the version is given as 2.2.0.0, it is mandatory when GPSInfo tag is present |
| if(gpsEnabled) { |
| const unsigned char gpsversion[4] = {0x02, 0x02, 0x00, 0x00}; |
| MEMCPY_S(exifAttributes.gps_version_id, |
| sizeof(exifAttributes.gps_version_id), |
| gpsversion, |
| sizeof(gpsversion)); |
| } else { |
| return; |
| } |
| |
| // latitude, for example, 39.904214 degrees, N |
| if(latitude > 0) { |
| MEMCPY_S(exifAttributes.gps_latitude_ref, |
| sizeof(exifAttributes.gps_latitude_ref), |
| "N", |
| sizeof(exifAttributes.gps_latitude_ref)); |
| } else { |
| MEMCPY_S(exifAttributes.gps_latitude_ref, |
| sizeof(exifAttributes.gps_latitude_ref), |
| "S", |
| sizeof(exifAttributes.gps_latitude_ref)); |
| } |
| latitude = fabs(latitude); |
| exifAttributes.gps_latitude[0].num = (uint32_t)latitude; |
| exifAttributes.gps_latitude[0].den = 1; |
| exifAttributes.gps_latitude[1].num = (uint32_t)((latitude - exifAttributes.gps_latitude[0].num) * 60); |
| exifAttributes.gps_latitude[1].den = 1; |
| exifAttributes.gps_latitude[2].num = (uint32_t)(((latitude - exifAttributes.gps_latitude[0].num) * 60 - exifAttributes.gps_latitude[1].num) * 60 * 100); |
| exifAttributes.gps_latitude[2].den = 100; |
| exifAttributes.enableGps |= EXIF_GPS_LATITUDE; |
| LOG1("EXIF: latitude, ref:%s, dd:%d, mm:%d, ss:%d", |
| exifAttributes.gps_latitude_ref, exifAttributes.gps_latitude[0].num, |
| exifAttributes.gps_latitude[1].num, exifAttributes.gps_latitude[2].num); |
| |
| // longitude, for example, 116.407413 degrees, E |
| if(longitude > 0) |
| MEMCPY_S(exifAttributes.gps_longitude_ref, |
| sizeof(exifAttributes.gps_longitude_ref), |
| "E", |
| sizeof(exifAttributes.gps_longitude_ref)); |
| else |
| MEMCPY_S(exifAttributes.gps_longitude_ref, |
| sizeof(exifAttributes.gps_longitude_ref), |
| "W", |
| sizeof(exifAttributes.gps_longitude_ref)); |
| |
| longitude = fabs(longitude); |
| exifAttributes.gps_longitude[0].num = (uint32_t)longitude; |
| exifAttributes.gps_longitude[0].den = 1; |
| exifAttributes.gps_longitude[1].num = (uint32_t)((longitude - exifAttributes.gps_longitude[0].num) * 60); |
| exifAttributes.gps_longitude[1].den = 1; |
| exifAttributes.gps_longitude[2].num = (uint32_t)(((longitude - exifAttributes.gps_longitude[0].num) * 60 - exifAttributes.gps_longitude[1].num) * 60 * 100); |
| exifAttributes.gps_longitude[2].den = 100; |
| exifAttributes.enableGps |= EXIF_GPS_LONGITUDE; |
| LOG1("EXIF: longitude, ref:%s, dd:%d, mm:%d, ss:%d", |
| exifAttributes.gps_longitude_ref, exifAttributes.gps_longitude[0].num, |
| exifAttributes.gps_longitude[1].num, exifAttributes.gps_longitude[2].num); |
| |
| // altitude |
| // altitude, sea level or above sea level, set it to 0; below sea level, set it to 1 |
| exifAttributes.gps_altitude_ref = ((altitude > 0) ? 0 : 1); |
| altitude = fabs(altitude); |
| exifAttributes.gps_altitude.num = (uint32_t)altitude; |
| exifAttributes.gps_altitude.den = 1; |
| exifAttributes.enableGps |= EXIF_GPS_ALTITUDE; |
| LOG1("EXIF: altitude, ref:%d, height:%d", exifAttributes.gps_altitude_ref, exifAttributes.gps_altitude.num); |
| |
| // timestamp |
| if (timestamp >= LONG_MAX || timestamp <= LONG_MIN) |
| { |
| timestamp = 0; |
| LOGW("invalid timestamp was provided, defaulting to 0 (i.e. 1970)"); |
| } |
| struct tm time; |
| gmtime_r(×tamp, &time); |
| time.tm_year += 1900; |
| time.tm_mon += 1; |
| exifAttributes.gps_timestamp[0].num = time.tm_hour; |
| exifAttributes.gps_timestamp[0].den = 1; |
| exifAttributes.gps_timestamp[1].num = time.tm_min; |
| exifAttributes.gps_timestamp[1].den = 1; |
| exifAttributes.gps_timestamp[2].num = time.tm_sec; |
| exifAttributes.gps_timestamp[2].den = 1; |
| exifAttributes.enableGps |= EXIF_GPS_TIMESTAMP; |
| std::ostringstream stringStream; |
| stringStream << std::setfill('0') |
| << std::setw(4) << time.tm_year << ":" |
| << std::setw(2) << time.tm_mon << ":" |
| << std::setw(2) << time.tm_mday; |
| |
| std::string tmpString = stringStream.str(); |
| size_t destLen = sizeof(exifAttributes.gps_datestamp); |
| size_t srcLen = tmpString.length(); |
| size_t nullPos = std::min(destLen - 1, srcLen); |
| |
| const char *tmp = tmpString.c_str(); |
| MEMCPY_S((char *)exifAttributes.gps_datestamp, destLen, tmp, srcLen); |
| exifAttributes.gps_datestamp[nullPos] = '\0'; |
| |
| LOG1("EXIF: timestamp, year:%d,mon:%d,day:%d,hour:%d,min:%d,sec:%d", |
| time.tm_year, time.tm_mon, time.tm_mday, time.tm_hour, time.tm_min, time.tm_sec); |
| |
| // processing method |
| MEMCPY_S(exifAttributes.gps_processing_method, |
| sizeof(exifAttributes.gps_processing_method), |
| metadata.mGpsSetting.gpsProcessingMethod, |
| sizeof(metadata.mGpsSetting.gpsProcessingMethod)); |
| exifAttributes.gps_processing_method[sizeof(exifAttributes.gps_processing_method) - 1] = 0; |
| |
| exifAttributes.enableGps |= EXIF_GPS_PROCMETHOD; |
| LOG1("EXIF: GPS processing method:%s", exifAttributes.gps_processing_method); |
| } |
| |
| void EXIFMaker::setSensorAeConfig(const SensorAeConfig& aeConfig) |
| { |
| LOG1("@%s", __FUNCTION__); |
| |
| if (aeConfig.expTime > 0) { |
| if (aeConfig.aecApexTv != 0) { |
| int expTime = (int) (pow(2.0, -aeConfig.aecApexTv * (1.52587890625e-005)) * 10000); |
| exifAttributes.exposure_time.num = expTime; |
| exifAttributes.exposure_time.den = 10000; |
| // APEX shutter speed |
| exifAttributes.shutter_speed.num = aeConfig.aecApexTv; |
| exifAttributes.shutter_speed.den = 65536; |
| } else { |
| exifAttributes.exposure_time.num = aeConfig.expTime; |
| exifAttributes.exposure_time.den = 1000000; |
| |
| uint32_t tv = APEX_EXPOSURE_TO_SHUTTER( |
| (double)exifAttributes.exposure_time.num /exifAttributes.exposure_time.den); |
| exifAttributes.shutter_speed.num = tv * 65536; |
| exifAttributes.shutter_speed.den = 65536; |
| } |
| } else { |
| exifAttributes.exposure_time.num = 0; |
| exifAttributes.exposure_time.den = 1; |
| exifAttributes.shutter_speed.num = 0; |
| exifAttributes.shutter_speed.den = 1; |
| } |
| |
| // APEX aperture value |
| if (aeConfig.aecApexAv >= 65536) { |
| exifAttributes.aperture.num = aeConfig.aecApexAv; |
| exifAttributes.aperture.den = 65536; |
| if (aeConfig.fn_num != 0 && aeConfig.fn_denum != 0) { |
| exifAttributes.fnumber.num = aeConfig.fn_num; |
| exifAttributes.fnumber.den = aeConfig.fn_denum; |
| } |
| } else { |
| double f_number = (double)exifAttributes.fnumber.num / (double)exifAttributes.fnumber.den; |
| exifAttributes.aperture.num = (uint32_t)(10000 * APEX_FNUM_TO_APERTURE(f_number)); |
| exifAttributes.aperture.den = 10000; |
| } |
| // exposure bias. unit is APEX value. -99.99 to 99.99 |
| if (aeConfig.evBias > EV_LOWER_BOUND && aeConfig.evBias < EV_UPPER_BOUND) { |
| exifAttributes.exposure_bias.num = (int)(aeConfig.evBias * 100); |
| exifAttributes.exposure_bias.den = 100; |
| LOG1("EXIF: Ev = %.2f", aeConfig.evBias); |
| } else { |
| LOGW("EXIF: Invalid Ev!"); |
| exifAttributes.exposure_bias.num = 0; |
| exifAttributes.exposure_bias.den = 100; |
| } |
| |
| LOG1("EXIF: shutter speed=%u/%u", exifAttributes.shutter_speed.num, |
| exifAttributes.shutter_speed.den); |
| LOG1("EXIF: exposure time=%u/%u", exifAttributes.exposure_time.num, |
| exifAttributes.exposure_time.den); |
| LOG1("EXIF: aperture=%u/%u", exifAttributes.aperture.num, |
| exifAttributes.aperture.den); |
| } |
| |
| /* |
| * more secure attribute copy routine. |
| * \param dst pointer to dst buffer |
| * \param dstSize dst buffer size |
| * \param src pointer to src character buffer |
| * \param srcLength src buffer length in characters, not including null byte |
| */ |
| void EXIFMaker::copyAttribute(uint8_t *dst, |
| size_t dstSize, |
| const char *src, |
| size_t srcLength) |
| { |
| size_t dstMaxLength = dstSize - 1; // leave space for null |
| MEMCPY_S(dst, dstMaxLength, src, srcLength); // copy chars (not null) |
| // add null termination |
| size_t len = MIN(dstMaxLength, srcLength); |
| dst[len] = '\0'; |
| } |
| |
| void EXIFMaker::clear() |
| { |
| LOG1("@%s", __FUNCTION__); |
| // Reset all the attributes |
| CLEAR(exifAttributes); |
| // Initialize the common values |
| exifAttributes.enableThumb = false; |
| copyAttribute(exifAttributes.image_description, |
| sizeof(exifAttributes.image_description), |
| EXIF_DEF_IMAGE_DESCRIPTION, |
| STRLEN_S(EXIF_DEF_IMAGE_DESCRIPTION)); |
| |
| copyAttribute(exifAttributes.maker, |
| sizeof(exifAttributes.maker), |
| PlatformData::manufacturerName(), |
| STRLEN_S(PlatformData::manufacturerName())); |
| |
| copyAttribute(exifAttributes.model, |
| sizeof(exifAttributes.model), |
| PlatformData::productName(), |
| STRLEN_S(PlatformData::productName())); |
| |
| copyAttribute(exifAttributes.software, |
| sizeof(exifAttributes.software), |
| EXIF_DEF_SOFTWARE, |
| STRLEN_S(EXIF_DEF_SOFTWARE)); |
| |
| copyAttribute(exifAttributes.exif_version, |
| sizeof(exifAttributes.exif_version), |
| EXIF_DEF_EXIF_VERSION, |
| STRLEN_S(EXIF_DEF_EXIF_VERSION)); |
| |
| copyAttribute(exifAttributes.flashpix_version, |
| sizeof(exifAttributes.flashpix_version), |
| EXIF_DEF_FLASHPIXVERSION, |
| STRLEN_S(EXIF_DEF_FLASHPIXVERSION)); |
| |
| // initially, set default flash |
| exifAttributes.flash = EXIF_DEF_FLASH; |
| |
| // normally it is sRGB, 1 means sRGB. FFFF.H means uncalibrated |
| exifAttributes.color_space = EXIF_DEF_COLOR_SPACE; |
| |
| // the number of pixels per ResolutionUnit in the w or h direction |
| // 72 means the image resolution is unknown |
| exifAttributes.x_resolution.num = EXIF_DEF_RESOLUTION_NUM; |
| exifAttributes.x_resolution.den = EXIF_DEF_RESOLUTION_DEN; |
| exifAttributes.y_resolution.num = exifAttributes.x_resolution.num; |
| exifAttributes.y_resolution.den = exifAttributes.x_resolution.den; |
| // resolution unit, 2 means inch |
| exifAttributes.resolution_unit = EXIF_DEF_RESOLUTION_UNIT; |
| // when thumbnail uses JPEG compression, this tag 103H's value is set to 6 |
| exifAttributes.compression_scheme = EXIF_DEF_COMPRESSION; |
| |
| // the TIFF default is 1 (centered) |
| exifAttributes.ycbcr_positioning = EXIF_DEF_YCBCR_POSITIONING; |
| |
| // Clear the Intel 3A Makernote information |
| exifAttributes.makerNoteData = nullptr; |
| exifAttributes.makerNoteDataSize = 0; |
| |
| initialized = false; |
| } |
| |
| void EXIFMaker::enableFlash(bool enable, int8_t aeMode, int8_t flashMode) |
| { |
| // bit 0: flash fired; bit 1 to 2: flash return; bit 3 to 4: flash mode; |
| // bit 5: flash function; bit 6: red-eye mode; |
| // enable: flashfired, when aeMode set to ON or OFF, flashMode or flashfired been used. |
| LOG1("@%s", __FUNCTION__); |
| exifAttributes.flash = EXIF_DEF_FLASH; |
| if (enable) { |
| exifAttributes.flash = EXIF_FLASH_ON; |
| exifAttributes.light_source = EXIF_LIGHT_SOURCE_FLASH; |
| } |
| switch (aeMode) { |
| case ANDROID_CONTROL_AE_MODE_ON: |
| case ANDROID_CONTROL_AE_MODE_OFF: |
| if (enable || flashMode == ANDROID_FLASH_MODE_TORCH || flashMode == ANDROID_FLASH_MODE_SINGLE) { |
| exifAttributes.flash |= EXIF_FLASH_FORCED_ON; |
| } else { |
| exifAttributes.flash |= EXIF_FLASH_FORCED_OFF; |
| } |
| break; |
| case ANDROID_CONTROL_AE_MODE_ON_AUTO_FLASH: |
| exifAttributes.flash |= EXIF_FLASH_AUTO; |
| break; |
| case ANDROID_CONTROL_AE_MODE_ON_ALWAYS_FLASH: |
| exifAttributes.flash |= EXIF_FLASH_FORCED_ON; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| void EXIFMaker::setThumbnail(unsigned char *data, size_t size, int width, int height) |
| { |
| LOG1("@%s: data = %p, size = %zu", __FUNCTION__, data, size); |
| exifAttributes.enableThumb = true; |
| exifAttributes.widthThumb = width; |
| exifAttributes.heightThumb = height; |
| if (encoder.setThumbData(data, size) != EXIF_SUCCESS) { |
| LOGE("Error in setting EXIF thumbnail"); |
| } |
| } |
| |
| bool EXIFMaker::isThumbnailSet() const { |
| LOG1("@%s", __FUNCTION__); |
| return encoder.isThumbDataSet(); |
| } |
| |
| size_t EXIFMaker::makeExif(unsigned char *data) |
| { |
| LOG1("@%s", __FUNCTION__); |
| if (data == nullptr) { |
| LOGE("nullptr pointer passed for EXIF. Cannot generate EXIF!"); |
| return 0; |
| } |
| if (encoder.makeExif(data, &exifAttributes, &exifSize) == EXIF_SUCCESS) { |
| LOG1("Generated EXIF (@%p) of size: %zu", data, exifSize); |
| return exifSize; |
| } |
| return 0; |
| } |
| |
| size_t EXIFMaker::makeExifInPlace(unsigned char *bufferStartAddr, |
| unsigned char *dqtAddress, |
| size_t jpegSize, |
| bool usePadding) |
| { |
| LOG1("@%s", __FUNCTION__); |
| if (bufferStartAddr == nullptr || dqtAddress == nullptr) { |
| LOGE("nullptr pointer passed for EXIF. Cannot generate EXIF!"); |
| return 0; |
| } |
| if (encoder.makeExifInPlace(bufferStartAddr, dqtAddress, &exifAttributes, |
| jpegSize, usePadding, exifSize) == EXIF_SUCCESS) { |
| LOG1("Generated EXIF (@%p) of size: %zu", bufferStartAddr, exifSize); |
| return exifSize; |
| } |
| return 0; |
| } |
| |
| void EXIFMaker::setMaker(const char *data) |
| { |
| LOG1("@%s: data = %s", __FUNCTION__, data); |
| size_t srcLen = std::char_traits<char>::length(data); |
| size_t srcSize = srcLen + 1; // +1 = null termination |
| size_t dstSize(sizeof(exifAttributes.maker)); |
| MEMCPY_S(exifAttributes.maker, dstSize, data, srcSize); |
| exifAttributes.maker[dstSize - 1] = '\0'; |
| } |
| |
| void EXIFMaker::setModel(const char *data) |
| { |
| LOG1("@%s: data = %s", __FUNCTION__, data); |
| size_t srcLen = std::char_traits<char>::length(data); |
| size_t srcSize = srcLen + 1; // +1 = null termination |
| size_t dstSize(sizeof(exifAttributes.model)); |
| MEMCPY_S(exifAttributes.model, dstSize, data, srcSize); |
| exifAttributes.model[dstSize - 1] = '\0'; |
| } |
| |
| void EXIFMaker::setSoftware(const char *data) |
| { |
| LOG1("@%s: data = %s", __FUNCTION__, data); |
| size_t srcLen = std::char_traits<char>::length(data); |
| size_t srcSize = srcLen + 1; // +1 = null termination |
| size_t dstSize(sizeof(exifAttributes.software)); |
| MEMCPY_S(exifAttributes.software, dstSize, data, srcSize); |
| exifAttributes.software[dstSize - 1] = '\0'; |
| } |
| } /* namespace intel */ |
| } /* namespace cros */ |