android: jpeg: exif: Expand setString to support different encodings

GPSProcessingMethod and UserComment in EXIF tags can be in UTF-16.
Expand setString to take an encoding when the field type is undefined.
Update callers accordingly.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
This commit is contained in:
Paul Elder 2021-01-21 17:16:42 +09:00
parent 34897234d1
commit d81e26d360
2 changed files with 84 additions and 4 deletions

View file

@ -7,6 +7,9 @@
#include "exif.h"
#include <map>
#include <uchar.h>
#include "libcamera/internal/log.h"
#include "libcamera/internal/utils.h"
@ -178,11 +181,18 @@ void Exif::setRational(ExifIfd ifd, ExifTag tag, ExifRational item)
exif_entry_unref(entry);
}
void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::string &item)
static const std::map<Exif::StringEncoding, std::array<uint8_t, 8>> 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<uint8_t> buf;
if (format == EXIF_FORMAT_ASCII) {
ascii = utils::toAscii(item);
@ -191,13 +201,46 @@ void Exif::setString(ExifIfd ifd, ExifTag tag, ExifFormat format, const std::str
/* Pad 1 extra byte to null-terminate the ASCII string. */
length = ascii.length() + 1;
} else {
str = item.c_str();
std::u16string u16str;
auto encodingString = stringEncodingCodes.find(encoding);
if (encodingString != stringEncodingCodes.end()) {
buf = {
encodingString->second.begin(),
encodingString->second.end()
};
}
switch (encoding) {
case Unicode:
u16str = utf8ToUtf16(item);
buf.resize(8 + u16str.size() * 2);
for (size_t i = 0; i < u16str.size(); i++) {
if (order_ == EXIF_BYTE_ORDER_INTEL) {
buf[8 + 2 * i] = u16str[i] & 0xff;
buf[8 + 2 * i + 1] = (u16str[i] >> 8) & 0xff;
} else {
buf[8 + 2 * i] = (u16str[i] >> 8) & 0xff;
buf[8 + 2 * i + 1] = u16str[i] & 0xff;
}
}
break;
case ASCII:
case NoEncoding:
buf.insert(buf.end(), item.begin(), item.end());
break;
}
str = reinterpret_cast<const char *>(buf.data());
/*
* Strings stored in different formats (EXIF_FORMAT_UNDEFINED)
* are not null-terminated.
*/
length = item.length();
length = buf.size();
}
ExifEntry *entry = createEntry(ifd, tag, format, length, length);
@ -290,6 +333,34 @@ void Exif::setThumbnail(Span<const unsigned char> thumbnail,
setShort(EXIF_IFD_0, EXIF_TAG_COMPRESSION, compression);
}
/**
* \brief Convert UTF-8 string to UTF-16 string
* \param[in] str String to convert
*
* \return \a str in UTF-16
*/
std::u16string Exif::utf8ToUtf16(const std::string &str)
{
mbstate_t state{};
char16_t c16;
const char *ptr = str.data();
const char *end = ptr + str.size();
std::u16string ret;
while (size_t rc = mbrtoc16(&c16, ptr, end - ptr + 1, &state)) {
if (rc == static_cast<size_t>(-2) ||
rc == static_cast<size_t>(-1))
break;
ret.push_back(c16);
if (rc > 0)
ptr += rc;
}
return ret;
}
[[nodiscard]] int Exif::generate()
{
if (exifData_) {

View file

@ -26,6 +26,12 @@ public:
JPEG = 6,
};
enum StringEncoding {
NoEncoding = 0,
ASCII = 1,
Unicode = 2,
};
void setMake(const std::string &make);
void setModel(const std::string &model);
@ -46,9 +52,12 @@ private:
void setShort(ExifIfd ifd, ExifTag tag, uint16_t item);
void setLong(ExifIfd ifd, ExifTag tag, uint32_t item);
void setString(ExifIfd ifd, ExifTag tag, ExifFormat format,
const std::string &item);
const std::string &item,
StringEncoding encoding = NoEncoding);
void setRational(ExifIfd ifd, ExifTag tag, ExifRational item);
std::u16string utf8ToUtf16(const std::string &str);
bool valid_;
ExifData *data_;