[libcamera-devel] [PATCH 1/4] android: jpeg: exif: Fix and expand setRational
Laurent Pinchart
laurent.pinchart at ideasonboard.com
Mon Mar 8 14:51:09 CET 2021
Hello,
On Mon, Mar 08, 2021 at 10:41:43AM +0000, Kieran Bingham wrote:
> On 08/03/2021 10:13, Paul Elder wrote:
> > setRational was not working properly for EXIF tags in the GPS IFD.
> > Manually specify the size of the EXIF entry to fix this. While at it,
> > add support for setting multiple rationals, as that is a common use case
> > for rational EXIF tags.
>
> setRational itself was working fine, but it is the lack of support for
> the tags used within
>
> libexif::exif-entry.c::exif_entry_initialize(ExifEntry *e, ExifTag tag)
>
> which is the issue here.
>
> exif_entry_initialize() goes to great lengths to initialise tags for
> their size and even defaults. Removing that (by using our direct
> allocation with the 4 argument version of createEntry() means that we
> are no longer handling those intialisations for Rational types.
>
> That will affect at least the following, which seem to be the only
> rational types initialised (yes, I see one of those groups is SRATIONAL
> so we don't handle those correctly yet either).
>
> >
> > /* SRATIONAL, 1 component, no default */
> > case EXIF_TAG_EXPOSURE_BIAS_VALUE:
> > case EXIF_TAG_BRIGHTNESS_VALUE:
> > case EXIF_TAG_SHUTTER_SPEED_VALUE:
> > e->components = 1;
> > e->format = EXIF_FORMAT_SRATIONAL;
> > e->size = exif_format_get_size (e->format) * e->components;
> > e->data = exif_entry_alloc (e, e->size);
> > if (!e->data) { clear_entry(e); break; }
> > break;
> >
> > /* RATIONAL, 1 component, no default */
> > case EXIF_TAG_EXPOSURE_TIME:
> > case EXIF_TAG_FOCAL_PLANE_X_RESOLUTION:
> > case EXIF_TAG_FOCAL_PLANE_Y_RESOLUTION:
> > case EXIF_TAG_EXPOSURE_INDEX:
> > case EXIF_TAG_FLASH_ENERGY:
> > case EXIF_TAG_FNUMBER:
> > case EXIF_TAG_FOCAL_LENGTH:
> > case EXIF_TAG_SUBJECT_DISTANCE:
> > case EXIF_TAG_MAX_APERTURE_VALUE:
> > case EXIF_TAG_APERTURE_VALUE:
> > case EXIF_TAG_COMPRESSED_BITS_PER_PIXEL:
> > case EXIF_TAG_PRIMARY_CHROMATICITIES:
> > case EXIF_TAG_DIGITAL_ZOOM_RATIO:
> > e->components = 1;
> > e->format = EXIF_FORMAT_RATIONAL;
> > e->size = exif_format_get_size (e->format) * e->components;
> > e->data = exif_entry_alloc (e, e->size);
> > if (!e->data) { clear_entry(e); break; }
> > break;
> >
> > /* RATIONAL, 1 component, default 72/1 */
> > case EXIF_TAG_X_RESOLUTION:
> > case EXIF_TAG_Y_RESOLUTION:
> > e->components = 1;
> > e->format = EXIF_FORMAT_RATIONAL;
> > e->size = exif_format_get_size (e->format) * e->components;
> > e->data = exif_entry_alloc (e, e->size);
> > if (!e->data) { clear_entry(e); break; }
> > r.numerator = 72;
> > r.denominator = 1;
> > exif_set_rational (e->data, o, r);
> > break;
> >
> > /* RATIONAL, 2 components, no default */
> > case EXIF_TAG_WHITE_POINT:
> > e->components = 2;
> > e->format = EXIF_FORMAT_RATIONAL;
> > e->size = exif_format_get_size (e->format) * e->components;
> > e->data = exif_entry_alloc (e, e->size);
> > if (!e->data) { clear_entry(e); break; }
> > break;
> >
> > /* RATIONAL, 6 components */
> > case EXIF_TAG_REFERENCE_BLACK_WHITE:
> > e->components = 6;
> > e->format = EXIF_FORMAT_RATIONAL;
> > e->size = exif_format_get_size (e->format) * e->components;
> > e->data = exif_entry_alloc (e, e->size);
> > if (!e->data) { clear_entry(e); break; }
> > r.denominator = 1;
> > r.numerator = 0;
> > exif_set_rational (e->data, o, r);
> > r.numerator = 255;
> > exif_set_rational (
> > e->data + exif_format_get_size (e->format), o, r);
> > r.numerator = 0;
> > exif_set_rational (
> > e->data + 2 * exif_format_get_size (e->format), o, r);
> > r.numerator = 255;
> > exif_set_rational (
> > e->data + 3 * exif_format_get_size (e->format), o, r);
> > r.numerator = 0;
> > exif_set_rational (
> > e->data + 4 * exif_format_get_size (e->format), o, r);
> > r.numerator = 255;
> > exif_set_rational (
> > e->data + 5 * exif_format_get_size (e->format), o, r);
> > break;
>
> As the reality is that most of them are not initialised with a default
> value, I don't think that's too much of a problem, but we should be
> aware of it.
>
> Given that this leads us to better support for Rational generically, I
> think this is a good way forwards, but could you add a note in the
> commit message to say that Rational types are no longer initialised by
> the exif library directly (as that has the consequence of not populating
> default values for EXIF_TAG_{X,Y}_RESOLUTION.
>
> We won't use those defaults, so it doesn't matter - but we're changing
> the behaviour - so we should note that.
It's a bit annoying, it would be nice if we could still benefit from the
initialization, although I don't expect lots of changes in libexif that
would cause breakages later.
Maybe we could, in setRational(), check the tag, and call the simple or
extended version of createEntry() accordingly ? It's a bit of a layering
violation and isn't very nice. Up to you, I'd be fine either way.
> > This allows the GPS altitude to be set properly, and is part of the fix
> > to allow the following CTS test to pass:
> > - android.hardware.cts.CameraTest#testJpegExif
> >
> > Signed-off-by: Paul Elder <paul.elder at ideasonboard.com>
> > ---
> > src/android/jpeg/exif.cpp | 13 +++++++++++--
> > src/android/jpeg/exif.h | 2 ++
> > 2 files changed, 13 insertions(+), 2 deletions(-)
> >
> > diff --git a/src/android/jpeg/exif.cpp b/src/android/jpeg/exif.cpp
> > index fdd46070..e5a3e448 100644
> > --- a/src/android/jpeg/exif.cpp
> > +++ b/src/android/jpeg/exif.cpp
> > @@ -187,11 +187,20 @@ void Exif::setLong(ExifIfd ifd, ExifTag tag, uint32_t item)
> >
> > void Exif::setRational(ExifIfd ifd, ExifTag tag, ExifRational item)
> > {
> > - ExifEntry *entry = createEntry(ifd, tag);
> > + ExifRational items[] = { item };
> > + setRational(ifd, tag, 1, items);
> > +}
> > +
> > +void Exif::setRational(ExifIfd ifd, ExifTag tag, uint32_t count, ExifRational items[])
> > +{
> > + ExifEntry *entry = createEntry(ifd, tag, EXIF_FORMAT_RATIONAL,
> > + count, count * sizeof(ExifRational));
>
> The library internally uses:
> exif_format_get_size (EXIF_FORMAT_RATIONAL);
>
> That involves a lookup in a table, which I think we can avoid here, by
> using sizeof(ExifRational) directly, but could you make sure they are
> the same expected size (8?) and that nothing crazy is going on with any
> padding or such?
>
> Otherwise
>
> Reviewed-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
>
> > if (!entry)
> > return;
> >
> > - exif_set_rational(entry->data, order_, item);
> > + for (size_t i = 0; i < count; i++)
> > + exif_set_rational(entry->data + i * sizeof(ExifRational),
> > + order_, items[i]);
> > exif_entry_unref(entry);
> > }
> >
> > diff --git a/src/android/jpeg/exif.h b/src/android/jpeg/exif.h
> > index b0d61402..e283d3fc 100644
> > --- a/src/android/jpeg/exif.h
> > +++ b/src/android/jpeg/exif.h
> > @@ -89,6 +89,8 @@ private:
> > const std::string &item,
> > StringEncoding encoding = NoEncoding);
> > void setRational(ExifIfd ifd, ExifTag tag, ExifRational item);
> > + void setRational(ExifIfd ifd, ExifTag tag, uint32_t count,
> > + ExifRational items[]);
> >
> > std::tuple<int, int, int> degreesToDMS(double decimalDegrees);
> > void setGPSDMS(ExifIfd ifd, ExifTag tag, int deg, int min, int sec);
--
Regards,
Laurent Pinchart
More information about the libcamera-devel
mailing list