/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2020, Google Inc. * * EXIF tag creation using libexif */ #include "exif.h" #include #include #include #include #include #include #include #include using namespace libcamera; LOG_DEFINE_CATEGORY(EXIF) /* * List of EXIF tags that we set directly because they are not supported * by libexif version 0.6.21. */ enum class _ExifTag { OFFSET_TIME = 0x9010, OFFSET_TIME_ORIGINAL = 0x9011, OFFSET_TIME_DIGITIZED = 0x9012, }; /* * The Exif class should be instantiated and specific properties set * through the exposed public API. * * Once all desired properties have been set, the user shall call * generate() to process the entries and generate the Exif data. * * Calls to generate() must check the return code to determine if any error * occurred during the construction of the Exif data, and if successful the * data can be obtained using the data() function. */ Exif::Exif() : valid_(false), data_(nullptr), order_(EXIF_BYTE_ORDER_INTEL), exifData_(0), size_(0) { /* Create an ExifMem allocator to construct entries. */ mem_ = exif_mem_new_default(); if (!mem_) { LOG(EXIF, Error) << "Failed to allocate ExifMem Allocator"; return; } data_ = exif_data_new_mem(mem_); if (!data_) { LOG(EXIF, Error) << "Failed to allocate an ExifData structure"; return; } valid_ = true; exif_data_set_option(data_, EXIF_DATA_OPTION_FOLLOW_SPECIFICATION); exif_data_set_data_type(data_, EXIF_DATA_TYPE_COMPRESSED); /* * Big-Endian: EXIF_BYTE_ORDER_MOTOROLA * Little Endian: EXIF_BYTE_ORDER_INTEL */ exif_data_set_byte_order(data_, order_); setString(EXIF_IFD_EXIF, EXIF_TAG_EXIF_VERSION, EXIF_FORMAT_UNDEFINED, "0231"); /* Create the mandatory EXIF fields with default data. */ exif_data_fix(data_); } Exif::~Exif() { if (exifData_) free(exifData_); if (data_) { /* * Reset thumbnail data to avoid getting double-freed by * libexif. It is owned by the caller (i.e. PostProcessorJpeg). */ data_->data = nullptr; data_->size = 0; exif_data_unref(data_); } if (mem_) exif_mem_unref(mem_); } ExifEntry *Exif::createEntry(ExifIfd ifd, ExifTag tag) { ExifContent *content = data_->ifd[ifd]; ExifEntry *entry = exif_content_get_entry(content, tag); if (entry) { exif_entry_ref(entry); return entry; } entry = exif_entry_new_mem(mem_); if (!entry) { LOG(EXIF, Error) << "Failed to allocated new entry"; valid_ = false; return nullptr; } exif_content_add_entry(content, entry); exif_entry_initialize(entry, tag); return entry; } ExifEntry *Exif::createEntry(ExifIfd ifd, ExifTag tag, ExifFormat format, unsigned long components, unsigned int size) { ExifContent *content = data_->ifd[ifd]; /* Replace any existing entry with the same tag. */ ExifEntry *existing = exif_content_get_entry(content, tag); exif_content_remove_entry(content, existing); ExifEntry *entry = exif_entry_new_mem(mem_); if (!entry) { LOG(EXIF, Error) << "Failed to allocated new entry"; valid_ = false; return nullptr; } void *buffer = exif_mem_alloc(mem_, size); if (!buffer) { LOG(EXIF, Error) << "Failed to allocate buffer for variable entry"; exif_mem_unref(mem_); valid_ = false; return nullptr; } entry->data = static_cast(buffer); entry->components = components; entry->format = format; entry->size = size; entry->tag = tag; exif_content_add_entry(content, entry); return entry; } void Exif::setByte(ExifIfd ifd, ExifTag tag, uint8_t item) { ExifEntry *entry = createEntry(ifd, tag, EXIF_FORMAT_BYTE, 1, 1); if (!entry) return; entry->data[0] = item; exif_entry_unref(entry); } void Exif::setShort(ExifIfd ifd, ExifTag tag, uint16_t item) { ExifEntry *entry = createEntry(ifd, tag); if (!entry) return; exif_set_short(entry->data, order_, item); exif_entry_unref(entry); } void Exif::setLong(ExifIfd ifd, ExifTag tag, uint32_t item) { ExifEntry *entry = createEntry(ifd, tag); if (!entry) return; exif_set_long(entry->data, order_, item); exif_entry_unref(entry); } void Exif::setRational(ExifIfd ifd, ExifTag tag, ExifRational item) { setRational(ifd, tag, { &item, 1 }); } void Exif::setRational(ExifIfd ifd, ExifTag tag, Span items) { ExifEntry *entry = createEntry(ifd, tag, EXIF_FORMAT_RATIONAL, items.size(), items.size() * sizeof(ExifRational)); if (!entry) return; for (size_t i = 0; i < items.size(); i++) exif_set_rational(entry->data + i * sizeof(ExifRational), order_, items[i]); exif_entry_unref(entry); } static const std::map> stringEncodingCodes = { { Exif::ASCII, { 0x41, 0x53, 0x43, 0x49, 0x49, 0x00, 0x00, 0x00 } }, { Exif::Unicode, { 0x55, 0x4e, 0x49, 0x43, 0x4f, 0x44, 0x45, 0x00 } }, }; void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::string &item, StringEncoding encoding) { std::string ascii; size_t length; const char *str; std::vector buf; if (format == EXIF_FORMAT_ASCII) { ascii = utils::toAscii(item); str = ascii.c_str(); /* Pad 1 extra byte to null-terminate the ASCII string. */ length = ascii.length() + 1; } else { std::u16string u16str; auto encodingString = stringEncodingCodes.find(encoding); if (encodingString != stringEncodingCodes.end()) { buf = { encodingString->second.begin(), encodingString->second.end() }; } switch (encoding) { /* SPDX-License-Identifier: BSD-2-Clause */ /* * Copyright (C) 2019, Raspberry Pi (Trading) Limited * * histogram.cpp - histogram calculations */ #include <math.h> #include <stdio.h> #include "histogram.hpp" using namespace RPiController; uint64_t Histogram::CumulativeFreq(double bin) const { if (bin <= 0) return 0; else if (bin >= Bins()) return Total(); int b = (int)bin; return cumulative_[b] + (bin - b) * (cumulative_[b + 1] - cumulative_[b]); } double Histogram::Quantile(double q, int first, int last) const { if (first == -1) first = 0; if (last == -1) last = cumulative_.size() - 2; assert(first <= last); uint64_t items = q * Total(); while (first < last) // binary search to find the right bin { int middle = (first + last) / 2; if (cumulative_[middle + 1] > items) last = middle; // between first and middle else first = middle + 1; // after middle } assert(items >= cumulative_[first] && items <= cumulative_[last + 1]); double frac = cumulative_[first + 1] == cumulative_[first] ? 0 : (double)(items - cumulative_[first]) / (cumulative_[first + 1] - cumulative_[first]); return first + frac; } double Histogram::InterQuantileMean(double q_lo, double q_hi) const { assert(q_hi > q_lo); double p_lo = Quantile(q_lo); double p_hi = Quantile(q_hi, (int)p_lo); double sum_bin_freq = 0, cumul_freq = 0; for (double p_next = floor(p_lo) + 1.0; p_next <= ceil(p_hi); p_lo = p_next, p_next += 1.0) { int bin = floor(p_lo); double freq = (cumulative_[bin + 1] - cumulative_[bin]) * (std::min(p_next, p_hi) - p_lo); sum_bin_freq += bin * freq; cumul_freq += freq; } // add 0.5 to give an average for bin mid-points return sum_bin_freq / cumul_freq + 0.5; }