summaryrefslogtreecommitdiff
path: root/src/jpeg-utils.cpp
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@users.sourceforge.net>2019-05-13 22:16:28 +0200
committerTomas Bzatek <tbzatek@users.sourceforge.net>2019-05-13 22:16:28 +0200
commit0c3b218886342e44275b087c41faf3b6a2b7f664 (patch)
tree35191c66c8f20e6f9b245d81cd79398a435370d9 /src/jpeg-utils.cpp
parent86438b558aaeb758d8770144c9e2fe70fdc4ee8a (diff)
downloadcataract-0c3b218886342e44275b087c41faf3b6a2b7f664.tar.xz
jpeg-utils: Port to gexiv2
The gexiv2 library is just a GObject wrapper around exiv2 library. It's a healthy project that continually keeps up with exiv2 API changes. Adopted by e.g. GIMP there's certain guarantee of future maintenance. This allows us to get rid of C++ code, making it more readable and predictable.
Diffstat (limited to 'src/jpeg-utils.cpp')
-rw-r--r--src/jpeg-utils.cpp1105
1 files changed, 0 insertions, 1105 deletions
diff --git a/src/jpeg-utils.cpp b/src/jpeg-utils.cpp
deleted file mode 100644
index 2e214e4..0000000
--- a/src/jpeg-utils.cpp
+++ /dev/null
@@ -1,1105 +0,0 @@
-/* Cataract - Static web photo gallery generator
- * Copyright (C) 2008 Tomas Bzatek <tbzatek@users.sourceforge.net>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-#include "config.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include <exiv2/image.hpp>
-#include <exiv2/exif.hpp>
-
-#ifdef HAVE_IMAGEMAGICK_7
-# include <MagickWand/MagickWand.h>
-#else
-# include <wand/MagickWand.h>
-#endif
-
-#include <config.h>
-
-#include "jpeg-utils.h"
-#include "gallery-utils.h"
-
-
-struct ExifDataPrivate {
- Exiv2::Image::AutoPtr image;
-};
-
-
-/*
- * Thread-safe ImageMagick and exiv2 libraries initialization and cleanup
- */
-void
-init_jpeg_utils (void)
-{
- MagickWandGenesis();
- /* http://dev.exiv2.org/projects/exiv2/wiki/Thread_safety */
- /* https://bugs.kde.org/show_bug.cgi?id=166424 */
- Exiv2::XmpParser::initialize();
-}
-
-void
-destroy_jpeg_utils (void)
-{
- Exiv2::XmpParser::terminate();
- MagickWandTerminus();
-}
-
-
-static void
-shift_time (struct tm *tm, int offset_min)
-{
- time_t t;
-
- if (offset_min != 0) {
- /* FIXME: converting between time formats could make some data lost, better to operate over struct tm directly */
- t = mktime (tm);
- if (t == (time_t) -1) {
- log_error ("Cannot shift time %p by %d minutes\n", tm, offset_min);
- return;
- }
-
- t += offset_min * 60;
-
- localtime_r (&t, tm);
- }
-}
-
-static struct tm *
-parse_exif_date (const char *str)
-{
- struct tm *tm;
- char *res;
-
- tm = (struct tm *) g_malloc0 (sizeof (struct tm));
-
- res = strptime (str, "%Y:%m:%d %H:%M:%S", tm);
- if (res == NULL || *res != '\0')
- return NULL;
-
- mktime (tm);
-
- if (tm->tm_isdst)
- shift_time (tm, -60);
-
- return tm;
-}
-
-static gchar *
-format_exif_time (struct tm *tm)
-{
- char conv[1024];
-
- memset (&conv, 0, sizeof(conv));
- if (strftime (&conv[0], sizeof(conv), "%Y:%m:%d %H:%M:%S", tm))
- return g_strdup (&conv[0]);
-
- return NULL;
-}
-
-/*
- * EXIF and IPTC info retrieval, keeps the source file open until freed
- */
-ExifData *
-read_exif (const gchar *filename)
-{
- ExifData *data;
-
- g_assert (filename != NULL);
- data = exif_data_new_empty ();
-
- try {
- data->priv->image = Exiv2::ImageFactory::open (filename);
- g_assert (data->priv->image.get() != 0);
- data->priv->image->readMetadata();
- }
- catch (Exiv2::AnyError& e)
- {
- log_error ("read_exif: Caught Exiv2 exception: '%s'\n", e.what());
- exif_data_free (data);
- return NULL;
- }
-
- return data;
-}
-
-ExifData *
-exif_data_new_empty ()
-{
- ExifData *data;
-
- data = (ExifData*) g_malloc0 (sizeof (ExifData));
- data->priv = (ExifDataPrivate*) g_malloc0 (sizeof (ExifDataPrivate));
-
- return data;
-}
-
-void
-exif_data_free (ExifData *data)
-{
- if (data) {
- g_free (data->override_copyright);
- g_free (data->external_exif_data);
- g_free (data->override_artist_name);
- /* FIXME: free data->priv->image */
- g_free (data->priv);
- g_free (data);
- }
-}
-
-static const gchar *
-get_real_key_name (const gchar *key)
-{
- struct StrKeyPair {
- const gchar *from;
- const gchar *to;
- };
- static const struct StrKeyPair conv[] = {
- { EXIF_CANON_CAMERA_TEMP, "Exif.CanonSi.0x000c" },
- };
-
- guint i;
-
- for (i = 0; i < G_N_ELEMENTS (conv); i++)
- if (g_str_equal (conv[i].from, key))
- return conv[i].to;
-
- return key;
-}
-
-
-/* Returns newly allocated string */
-/* FIXME: for some reason exiv2 returns const strings with unknown lifetime, can't really trust the library */
-#define exiv2_str_val(data,key) g_strdup(data[key].toString().c_str())
-
-static gboolean
-iptc_has_key (Exiv2::IptcData iptcData, const char *key)
-{
- Exiv2::IptcData::const_iterator md;
-
- if (! iptcData.empty()) {
- md = iptcData.findKey(Exiv2::IptcKey(std::string(key)));
- if (md != iptcData.end() && iptcData[key].count() > 0)
- return TRUE;
- }
- return FALSE;
-}
-
-
-/*
- * Retrieves value of the specified key or NULL if the key does not exist.
- * The key argument belongs to Exiv2 namespace - see http://exiv2.org/tags.html
- */
-gchar *
-get_exif_data (ExifData *exif, const gchar *key)
-{
- g_return_val_if_fail (exif != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- key = get_real_key_name (key);
-
- try {
- if (g_strcmp0 (key, JPEG_COMMENT) == 0) {
- return g_strdup (exif->priv->image->comment().c_str());
- }
-
- if (g_str_has_prefix (key, "Exif.")) {
- Exiv2::ExifData &exifData = exif->priv->image->exifData();
- if (! exifData.empty()) {
- return exiv2_str_val (exifData, key);
- }
- }
-
- if (g_str_has_prefix (key, "Iptc.")) {
- Exiv2::IptcData &iptcData = exif->priv->image->iptcData();
- if (iptc_has_key (iptcData, key)) {
- return exiv2_str_val (iptcData, key);
- }
- }
-
- return NULL;
- }
- catch (...) {
- return NULL;
- }
-}
-
-gchar *
-get_exif_data_fixed (ExifData *exif, const gchar *key)
-{
- g_return_val_if_fail (exif != NULL, NULL);
- g_return_val_if_fail (key != NULL, NULL);
-
- try
- {
- if (g_str_has_prefix (key, "Exif.")) {
- Exiv2::ExifData &exifData = exif->priv->image->exifData();
- if (exifData.empty())
- return NULL;
-
- if (g_str_equal (key, EXIF_APERTURE)) {
- double val = -1;
- if (exif->override_aperture != -1)
- val = exif->override_aperture;
- else {
- if (exifData["Exif.Photo.FNumber"].count() > 0)
- val = exifData["Exif.Photo.FNumber"].toFloat();
- else
- if (exifData["Exif.Photo.ApertureValue"].count() > 0)
- val = exifData["Exif.Photo.ApertureValue"].toFloat();
- }
- if (val >= 0)
- return g_strdup_printf ("&#402;/%.1f", val);
- }
-
- if (g_str_equal (key, EXIF_DATETIME) ||
- g_str_equal (key, "Exif.Photo.DateTimeOriginal") ||
- g_str_equal (key, "Exif.Photo.DateTimeDigitized") ||
- g_str_equal (key, "Exif.Image.DateTime")) {
- gchar *val = NULL;
- try {
- val = exiv2_str_val (exifData, "Exif.Photo.DateTimeOriginal");
- } catch (...) { }
- if (! val || strlen (val) == 0)
- try {
- val = exiv2_str_val (exifData, "Exif.Photo.DateTimeDigitized");
- } catch (...) { }
- if (! val || strlen (val) == 0)
- try {
- /* usually a modification date */
- val = exiv2_str_val (exifData, "Exif.Image.DateTime");
- } catch (...) { }
-
- if (val && strlen (val) > 0) {
- struct tm *tt = NULL;
- char conv[1024];
- gchar *res = NULL;
-
- memset (&conv, 0, sizeof (conv));
-
- if (exif->override_datetime != (time_t) -1) {
- tt = (struct tm *) g_malloc0 (sizeof (struct tm));
- localtime_r (&exif->override_datetime, tt);
- if (strftime (&conv[0], sizeof (conv), exif->datetime_format ? exif->datetime_format : "%c", tt))
- res = g_strdup (&conv[0]);
- }
-
- if (! res) {
- tt = parse_exif_date (val);
- if (tt) {
- shift_time (tt, exif->timezone_shift);
- if (strftime (&conv[0], sizeof (conv), exif->datetime_format ? exif->datetime_format : "%c", tt))
- res = g_strdup (&conv[0]);
- }
- }
-
- g_free (tt);
- if (res)
- return res;
- }
- g_free (val);
- }
-
- if (g_str_equal (key, EXIF_EXPOSURE)) {
- float val = exifData["Exif.Photo.ExposureTime"].toFloat();
- if (val > 0) {
- if (val < 0.5)
- return g_strdup_printf ("1/%.0f s", 1/val);
- else
- return g_strdup_printf ("%.1f s", val);
- }
- }
-
- if (g_str_equal (key, EXIF_FLASH)) {
- long int val = exifData["Exif.Photo.Flash"].toLong();
- if (val > 0 && (val & 1) == 1)
- return g_strdup ("Flash fired");
- else
- return g_strdup ("--");
- }
-
- if (g_str_equal (key, EXIF_FOCAL_LENGTH)) {
- double val;
- val = exif->override_focal_length != -1 ? exif->override_focal_length : exifData["Exif.Photo.FocalLength"].toFloat();
- if (val >= 0)
- return g_strdup_printf ("%.0f mm", val);
- }
-
- if (g_str_equal (key, EXIF_ISO)) {
- long int val = exifData["Exif.Photo.ISOSpeedRatings"].toLong();
- if (val > 0)
- return g_strdup_printf ("%ld", val);
- }
-
- if (g_str_equal (key, EXIF_CANON_CAMERA_TEMP)) {
- if (exifData["Exif.CanonSi.0x000c"].count() > 0) {
- int long val = exifData["Exif.CanonSi.0x000c"].toLong();
- if (val > 0)
- return g_strdup_printf ("%ld &deg;C", val - 128);
- }
- }
- }
-
- if (g_str_has_prefix (key, "Iptc.")) {
- Exiv2::IptcData &iptcData = exif->priv->image->iptcData();
- if (iptcData.empty())
- return NULL;
- }
- }
- catch (...) {
- return NULL;
- }
-
- return get_exif_data (exif, key);
-}
-
-/*
- * Returns TRUE if the image contains the key specified
- */
-gboolean
-exif_has_key (ExifData *exif, const gchar *key)
-{
- const gchar *rkey;
-
- g_return_val_if_fail (exif != NULL, FALSE);
- g_return_val_if_fail (key != NULL, FALSE);
-
- rkey = get_real_key_name (key);
-
- try {
- if (g_strcmp0 (rkey, JPEG_COMMENT) == 0) {
- return (! exif->priv->image->comment().empty());
- }
-
- if (g_str_has_prefix (rkey, "Exif.")) {
- Exiv2::ExifData &exifData = exif->priv->image->exifData();
- if (exifData.empty() || exifData[rkey].count() <= 0)
- return FALSE;
-
- /* Special case for some keys */
- if (g_str_equal (key, EXIF_CANON_CAMERA_TEMP)) {
- int long val = exifData["Exif.CanonSi.0x000c"].toLong();
- return val > 0;
- }
-
- return TRUE;
- }
-
- if (g_str_has_prefix (rkey, "Iptc.")) {
- Exiv2::IptcData &iptcData = exif->priv->image->iptcData();
- return iptc_has_key (iptcData, rkey);
- }
-
- return FALSE;
- }
- catch (...) {
- return FALSE;
- }
-}
-
-
-static void
-autorotate_image (MagickWand *magick_wand)
-{
- MagickBooleanType b;
- PixelWand *pixel_wand;
- ExceptionType severity;
- gchar *description;
-
- pixel_wand = NewPixelWand ();
- b = PixelSetColor (pixel_wand, "#000000");
- if (b == MagickFalse) {
- description = MagickGetException (magick_wand, &severity);
- log_error ("autorotate_image: Error creating pixel wand: %s %s %ld %s\n", GetMagickModule(), description);
- MagickRelinquishMemory (description);
- }
-
- b = MagickTrue;
- switch (MagickGetImageOrientation (magick_wand))
- {
- case TopRightOrientation:
- b = MagickFlopImage (magick_wand);
- break;
- case BottomRightOrientation:
- b = MagickRotateImage (magick_wand, pixel_wand, 180.0);
- break;
- case BottomLeftOrientation:
- b = MagickFlipImage (magick_wand);
- break;
- case LeftTopOrientation:
- b = MagickTransposeImage (magick_wand);
- break;
- case RightTopOrientation:
- b = MagickRotateImage (magick_wand, pixel_wand, 90.0);
- break;
- case RightBottomOrientation:
- b = MagickTransverseImage (magick_wand);
- break;
- case LeftBottomOrientation:
- b = MagickRotateImage (magick_wand, pixel_wand, 270.0);
- break;
- default:
- break;
- }
-
- if (b == MagickFalse) {
- description = MagickGetException (magick_wand, &severity);
- log_error ("autorotate_image: Error rotating image: %s %s %ld %s\n", GetMagickModule(), description);
- MagickRelinquishMemory (description);
- }
-
- b = MagickSetImageOrientation (magick_wand, TopLeftOrientation);
- if (b == MagickFalse) {
- description = MagickGetException (magick_wand, &severity);
- log_error ("autorotate_image: Error saving orientation: %s %s %ld %s\n", GetMagickModule(), description);
- MagickRelinquishMemory (description);
- }
-
- DestroyPixelWand (pixel_wand);
-}
-
-static gchar **
-parse_cmd_args (const gchar *resize_opts, const gchar *prepend, const gchar *append, const gchar *file_in, const gchar *file_out, unsigned long size_x, unsigned long size_y)
-{
- gchar *in;
- gchar **s;
- gchar *f;
-
- in = g_strdup_printf ("%s %s %s %s %s",
- prepend ? prepend : "",
- file_in ? file_in : "",
- resize_opts,
- file_out ? file_out : "",
- append ? append : "");
- while (g_strstr_len (in, -1, "${WIDTH}")) {
- f = g_strdup_printf ("%lu", size_x);
- str_replace (&in, "${WIDTH}", f);
- g_free (f);
- }
- while (g_strstr_len (in, -1, "${HEIGHT}")) {
- f = g_strdup_printf ("%lu", size_y);
- str_replace (&in, "${HEIGHT}", f);
- g_free (f);
- }
- /* ImageMagick doesn't like empty elements */
- str_trim_inside (&in);
- s = g_strsplit (in, " ", -1);
- g_free (in);
-
- return s;
-}
-
-/*
- * resize_image: resize image pointed by src and save result to dst
- */
-gboolean
-resize_image (const gchar *src, const gchar *dst,
- unsigned long size_x, unsigned long size_y,
- int quality,
- gboolean thumbnail,
- gboolean autorotate,
- gboolean hidpi_strict_dimensions,
- ExifData *exif,
- gchar *resize_opts)
-{
- MagickWand *magick_wand;
- ImageInfo *image_info;
- ExceptionInfo *exception_info;
- ExceptionType severity;
- unsigned long w, h;
- unsigned long new_w, new_h;
- double source_aspect, target_aspect;
- gchar *description;
- gchar **cmd_args;
- gchar *res_id = NULL;
- gchar *mpr_res_id;
-
- g_assert (src != NULL);
- g_assert (dst != NULL);
-
- /* Read an image. */
- magick_wand = NewMagickWand();
- if (MagickReadImage (magick_wand, src) == MagickFalse) {
- description = MagickGetException (magick_wand, &severity);
- log_error ("Error reading image: %s %s %ld %s\n", GetMagickModule(), description);
- MagickRelinquishMemory (description);
- DestroyMagickWand (magick_wand);
- return FALSE;
- }
-
- if (autorotate)
- autorotate_image (magick_wand);
-
- /* Don't resize if smaller than desired size */
- if (hidpi_strict_dimensions || MagickGetImageWidth (magick_wand) > size_x || MagickGetImageHeight (magick_wand) > size_y || exif->shave_amount > 0)
- {
- /* Shave borders if requested */
- if (exif->shave_amount > 0)
- MagickShaveImage (magick_wand, exif->shave_amount, exif->shave_amount);
-
- /* Prepare image before resizing */
- if (thumbnail) {
- if (exif->thumbnail_crop_style != CROP_STYLE_NORMAL) {
- w = MagickGetImageWidth (magick_wand);
- h = MagickGetImageHeight (magick_wand);
- new_w = w;
- new_h = h;
- if (exif->thumbnail_crop_style == CROP_STYLE_SQUARED) {
- new_w = MAX (w, h) * CROP_SIMPLE_SHAVE_AMOUNT / 100;
- new_w = MIN (w - 2*new_w, h - 2*new_w);
- new_h = new_w;
- }
- if (exif->thumbnail_crop_style == CROP_STYLE_FIXED) {
- source_aspect = (double) w / (double) h;
- target_aspect = (double) size_x / (double) size_y;
- if (target_aspect >= source_aspect) {
- new_w = w;
- new_h = (int) ((double) w / target_aspect);
- } else {
- new_w = (int) ((double) h * target_aspect);
- new_h = h;
- }
- new_w = (int) ((double) new_w * (double) (100 - CROP_SIMPLE_SHAVE_AMOUNT) / 100);
- new_h = (int) ((double) new_h * (double) (100 - CROP_SIMPLE_SHAVE_AMOUNT) / 100);
- }
- switch (exif->thumbnail_crop_hint) {
- case CROP_HINT_UNDEFINED:
- case CROP_HINT_CENTER:
- MagickCropImage (magick_wand, new_w, new_h, (w - new_w) / 2, (h - new_h) / 2);
- break;
- case CROP_HINT_LEFT:
- MagickCropImage (magick_wand, new_w, new_h, 0, (h - new_h) / 2);
- break;
- case CROP_HINT_RIGHT:
- MagickCropImage (magick_wand, new_w, new_h, w - new_w, (h - new_h) / 2);
- break;
- case CROP_HINT_TOP:
- MagickCropImage (magick_wand, new_w, new_h, (w - new_w) / 2, 0);
- break;
- case CROP_HINT_BOTTOM:
- MagickCropImage (magick_wand, new_w, new_h, (w - new_w) / 2, h - new_h);
- break;
- }
- }
- }
-
- /* Shave the source image to match exact dimensions after resize */
- if (hidpi_strict_dimensions && (! thumbnail || exif->thumbnail_crop_style == CROP_STYLE_NORMAL)) {
- w = MagickGetImageWidth (magick_wand);
- h = MagickGetImageHeight (magick_wand);
- source_aspect = (double) w / (double) h;
- target_aspect = (double) size_x / (double) size_y;
- if (source_aspect != target_aspect) {
- if (target_aspect >= source_aspect) {
- new_w = w;
- new_h = lround ((double) w / target_aspect);
- } else {
- new_w = lround ((double) h * target_aspect);
- new_h = h;
- }
- MagickCropImage (magick_wand, new_w, new_h, (w - new_w) / 2, (h - new_h) / 2);
- g_warn_if_fail (MagickGetImageWidth (magick_wand) == new_w);
- g_warn_if_fail (MagickGetImageHeight (magick_wand) == new_h);
- }
- }
-
- if (resize_opts == NULL) {
- /* Perform internal resizing */
- /* Note: MagickResizeImage() does no aspect correction, stretching the image to the required dimensions */
- if (thumbnail) {
- MagickThumbnailImage (magick_wand, size_x, size_y);
- } else {
-#ifdef HAVE_IMAGEMAGICK_7
- MagickResizeImage (magick_wand, size_x, size_y, LanczosFilter);
-#else
- MagickResizeImage (magick_wand, size_x, size_y, LanczosFilter, 1.0);
-#endif
- }
- } else {
- /* Perform resizing through ImageMagick commandline parser */
- res_id = g_strdup_printf ("cgg_resize_image_%p", g_thread_self ());
- mpr_res_id = g_strdup_printf ("mpr:%s", res_id);
- if (MagickWriteImage (magick_wand, mpr_res_id) == MagickFalse) {
- description = MagickGetException (magick_wand, &severity);
- log_error ("Error writing mpr image: %s %s %ld %s\n", GetMagickModule(), description);
- MagickRelinquishMemory (description);
- DestroyMagickWand (magick_wand);
- g_free (res_id);
- g_free (mpr_res_id);
- return FALSE;
- }
- ClearMagickWand (magick_wand);
-
- cmd_args = parse_cmd_args (resize_opts, "convert", NULL, mpr_res_id, mpr_res_id, size_x, size_y);
- image_info = AcquireImageInfo ();
- g_assert (image_info != NULL);
- exception_info = AcquireExceptionInfo ();
- g_assert (exception_info != NULL);
- if (MagickCommandGenesis (image_info, ConvertImageCommand, g_strv_length (cmd_args), cmd_args, NULL, exception_info) == MagickFalse) {
- /* MagickCommandGenesis() should've printed verbose error message */
- DestroyImageInfo (image_info);
- DestroyExceptionInfo (exception_info);
- DestroyMagickWand (magick_wand);
- g_free (res_id);
- g_free (mpr_res_id);
- return FALSE;
- }
- DestroyImageInfo (image_info);
- DestroyExceptionInfo (exception_info);
- g_strfreev (cmd_args);
-
- if (MagickReadImage (magick_wand, mpr_res_id) == MagickFalse) {
- description = MagickGetException (magick_wand, &severity);
- printf ("Error reading mpr image: %s %s %ld %s\n", GetMagickModule(), description);
- MagickRelinquishMemory (description);
- DestroyMagickWand (magick_wand);
- g_free (res_id);
- g_free (mpr_res_id);
- return FALSE;
- }
- g_free (mpr_res_id);
- }
- }
-
- if (thumbnail) {
- /* FIXME: this strips image ICC profile, should do proper conversion first */
- MagickStripImage (magick_wand);
- }
-
- if ((int) MagickGetImageCompressionQuality (magick_wand) != quality)
- MagickSetImageCompressionQuality (magick_wand, quality);
-
- /* Write the image and destroy it. */
- if (MagickWriteImage (magick_wand, dst) == MagickFalse) {
- description = MagickGetException (magick_wand, &severity);
- log_error ("Error writing image: %s %s %ld %s\n", GetMagickModule(), description);
- DeleteImageRegistry (res_id);
- g_free (res_id);
- return FALSE;
- }
-
- if (res_id) {
- /* This is potentially dangerous operation - modifying ImageMagick's internal image registry */
- DeleteImageRegistry (res_id);
- g_free (res_id);
- }
- magick_wand = DestroyMagickWand (magick_wand);
-
- return TRUE;
-}
-
-
-/*
- * get_image_sizes: retrieve image dimensions
- */
-void
-get_image_sizes (const gchar *img,
- unsigned long *width, unsigned long *height,
- int *quality,
- gboolean autorotate)
-{
- MagickWand *magick_wand;
- MagickBooleanType b;
- ExceptionType severity;
- gchar *description;
-
- *width = 0;
- *height = 0;
- if (quality)
- *quality = -1;
-
- g_assert (img != NULL);
-
- /* Read an image. */
- magick_wand = NewMagickWand();
- if (autorotate)
- b = MagickReadImage (magick_wand, img);
- else
- b = MagickPingImage (magick_wand, img);
- if (b == MagickFalse) {
- description = MagickGetException (magick_wand, &severity);
- /* -- make it silent
- log_error ("Error reading image info: %s %s %ld %s\n", GetMagickModule(), description);
- */
- MagickRelinquishMemory(description);
- return;
- }
-
- if (autorotate)
- autorotate_image (magick_wand);
-
- *width = MagickGetImageWidth (magick_wand);
- *height = MagickGetImageHeight (magick_wand);
- if (quality)
- *quality = (int) MagickGetImageCompressionQuality (magick_wand);
-
- magick_wand = DestroyMagickWand (magick_wand);
-}
-
-
-/*
- * calculate_sizes: calculate maximal image sizes within specified limits keeping aspect ratio
- */
-void
-calculate_sizes (const unsigned long max_width, const unsigned long max_height,
- unsigned long *width, unsigned long *height)
-{
- if (max_width > *width && max_height > *height)
- return;
-
- double max_ratio = (double) max_width / (double) max_height;
- double real_ratio = (double) *width / (double) *height;
-
- if (*width > *height && max_ratio <= real_ratio) {
- *height = (unsigned long) (max_width / real_ratio);
- *width = max_width;
- } else {
- *width = (unsigned long) (max_height * real_ratio);
- *height = max_height;
- }
-}
-
-static gboolean
-shift_exif_time (Exiv2::ExifData& exifData, const char *key, int amount)
-{
- struct tm *tt;
- gchar *s;
- gchar *st;
- gboolean res;
-
- res = FALSE;
- try {
- if (exifData[key].count() > 0) {
- s = exiv2_str_val (exifData, key);
-
- if (s && strlen (s) > 0) {
- tt = parse_exif_date (s);
- if (tt) {
- shift_time (tt, amount);
- st = format_exif_time (tt);
- if (st)
- exifData[key] = st;
- g_free (st);
- g_free (tt);
- res = TRUE;
- }
- }
- g_free (s);
- }
- } catch (...) { }
-
- return res;
-}
-
-static gboolean
-shift_iptc_time (Exiv2::IptcData &iptcData, const char *date_key, const char *time_key, int amount)
-{
- struct tm tt = {0};
- long int orig_time;
- gboolean res;
- Exiv2::DateValue dval;
- Exiv2::TimeValue tval;
-
- res = FALSE;
- if (iptc_has_key (iptcData, date_key) || iptc_has_key (iptcData, time_key)) {
- orig_time = (iptc_has_key (iptcData, date_key) ? iptcData[date_key].toLong() : 0) +
- (iptc_has_key (iptcData, time_key) ? iptcData[time_key].toLong() : 0);
- if (orig_time > 0) {
- orig_time += amount * 60;
- localtime_r (&orig_time, &tt);
- mktime (&tt);
- if (tt.tm_isdst)
- shift_time (&tt, -60);
-
- dval = Exiv2::DateValue(tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday);
- tval = Exiv2::TimeValue(tt.tm_hour, tt.tm_min, tt.tm_sec, 0, 0);
- iptcData[date_key].setValue(&dval);
- iptcData[time_key].setValue(&tval);
- res = TRUE;
- }
- }
-
- return res;
-}
-
-static gboolean
-override_exif_time (Exiv2::ExifData &exifData, const char *key, time_t datetime)
-{
- struct tm tt = {0};
- gchar *st;
- gboolean res;
-
- res = FALSE;
- try {
- if (exifData[key].count() > 0) {
- localtime_r (&datetime, &tt);
- st = format_exif_time (&tt);
- if (st)
- exifData[key] = st;
- g_free (st);
- res = TRUE;
- }
- } catch (...) { }
-
- return res;
-}
-
-static gboolean
-override_iptc_time (Exiv2::IptcData &iptcData, const char *date_key, const char *time_key, time_t datetime)
-{
- struct tm tt = {0};
- Exiv2::DateValue dval;
- Exiv2::TimeValue tval;
-
- if (iptc_has_key (iptcData, date_key) || iptc_has_key (iptcData, time_key)) {
- localtime_r (&datetime, &tt);
-
- dval = Exiv2::DateValue(tt.tm_year + 1900, tt.tm_mon + 1, tt.tm_mday);
- tval = Exiv2::TimeValue(tt.tm_hour, tt.tm_min, tt.tm_sec, 0, 0);
- iptcData[date_key].setValue(&dval);
- iptcData[time_key].setValue(&tval);
-
- return TRUE;
- }
-
- return FALSE;
-}
-
-
-/* List of tags we don't want to copy from external EXIF data since they are related to the RAW file,
- * not the processed image. Note that this list is not by far complete.
- */
-static const gchar * image_size_tags[] = {
- "Exif.Image.ImageWidth",
- "Exif.Image.ImageHeight",
- "Exif.Image.ImageLength",
- "Exif.Image.Orientation",
- "Exif.Photo.PixelXDimension",
- "Exif.Photo.PixelYDimension",
- "Exif.Image.XResolution",
- "Exif.Image.YResolution",
- "Exif.Image.ResolutionUnit",
- "Exif.Image.Compression",
- "Exif.Image.BitsPerSample",
- "Exif.Image.SamplesPerPixel",
- "Exif.Photo.ComponentsConfiguration",
- "Exif.Photo.CompressedBitsPerPixel",
- "Exif.Image.JPEGTables",
- "Exif.Image.JPEGProc",
- "Exif.Image.JPEGInterchangeFormat",
- "Exif.Image.JPEGInterchangeFormatLength",
- "Exif.Image.JPEGRestartInterval",
- "Exif.Image.JPEGLosslessPredictors",
- "Exif.Image.JPEGPointTransforms",
- "Exif.Image.JPEGQTables",
- "Exif.Image.JPEGDCTables",
- "Exif.Image.JPEGACTables",
- "Exif.Image.YCbCrCoefficients",
- "Exif.Image.YCbCrSubSampling",
- "Exif.Image.YCbCrPositioning",
- "Exif.Image.ReferenceBlackWhite",
- "Exif.Image.PhotometricInterpretation",
- "Exif.Image.PlanarConfiguration",
-};
-
-static gboolean
-is_image_size_tag (const gchar *s)
-{
- unsigned int i;
-
- for (i = 0; i < G_N_ELEMENTS (image_size_tags); i++)
- if (g_str_equal (s, image_size_tags[i]))
- return TRUE;
- return FALSE;
-}
-
-static void
-copy_metadata (Exiv2::Image::AutoPtr &img, Exiv2::Image::AutoPtr &external_img)
-{
- Exiv2::ExifData exifData;
- Exiv2::ExifData &img_exifData = img->exifData();
- Exiv2::ExifData &ext_exifData = external_img->exifData();
-
- /* First copy metadata from the external image excluding size tags */
- Exiv2::ExifData::const_iterator end = ext_exifData.end();
- for (Exiv2::ExifData::const_iterator i = ext_exifData.begin(); i != end; ++i) {
- gchar *s = g_strdup (i->key().c_str());
- if (! is_image_size_tag (s))
- exifData[s] = ext_exifData[s];
- g_free (s);
- }
-
- /* Copy selected size tags from the processed image */
- end = img_exifData.end();
- for (Exiv2::ExifData::const_iterator i = img_exifData.begin(); i != end; ++i) {
- gchar *s = g_strdup (i->key().c_str());
- if (is_image_size_tag (s))
- exifData[s] = img_exifData[s];
- g_free (s);
- }
-
- img->setMetadata (*external_img);
- img->setExifData (exifData);
-}
-
-
-
-/*
- * modify_exif: - strip thumbnail stored in EXIF table
- * - write down overriden keys
- */
-void
-modify_exif (const gchar *filename, ExifData *exif, gboolean strip_thumbnail, gboolean strip_xmp)
-{
- gboolean modified;
- gboolean res;
-
- g_assert (filename != NULL);
-
- if (! strip_thumbnail && exif == NULL)
- return;
-
- modified = FALSE;
- try {
- Exiv2::Image::AutoPtr image = Exiv2::ImageFactory::open (filename);
- g_assert (image.get() != 0);
-
- image->readMetadata();
-
- /* Write down metadata from external file if supplied */
- if (exif && exif->external_exif_data) {
- Exiv2::Image::AutoPtr ext_image = Exiv2::ImageFactory::open (exif->external_exif_data);
- if (ext_image.get() != 0) {
- ext_image->clearMetadata();
- ext_image->readMetadata();
- copy_metadata (image, ext_image);
- modified = TRUE;
- }
- }
-
- Exiv2::ExifData &exifData = image->exifData();
- Exiv2::IptcData &iptcData = image->iptcData();
-
- if (exif) {
- if (exif->override_copyright) {
- if (! exifData.empty()) {
- exifData["Exif.Image.Copyright"] = exif->override_copyright;
- modified = TRUE;
- }
- if (! iptcData.empty()) {
- iptcData["Iptc.Application2.Copyright"] = exif->override_copyright;
- modified = TRUE;
- }
- }
-
- if (exif->timezone_shift != 0 && exif->override_datetime == (time_t) -1) {
- if (! exifData.empty()) {
- res = shift_exif_time (exifData, "Exif.Photo.DateTimeOriginal", exif->timezone_shift);
- res = shift_exif_time (exifData, "Exif.Photo.DateTimeDigitized", exif->timezone_shift) || res;
- res = shift_exif_time (exifData, "Exif.Image.DateTime", exif->timezone_shift) || res;
- modified = TRUE;
- }
- if (! iptcData.empty()) {
- res = shift_iptc_time (iptcData, "Iptc.Application2.DateCreated", "Iptc.Application2.TimeCreated", exif->timezone_shift);
- res = shift_iptc_time (iptcData, "Iptc.Application2.DigitizationDate", "Iptc.Application2.DigitizationTime", exif->timezone_shift) || res;
- if (res)
- modified = TRUE;
- }
- }
-
- if (exif->override_datetime != (time_t) -1 && !exifData.empty()) {
- if (! exifData.empty()) {
- res = override_exif_time (exifData, "Exif.Photo.DateTimeOriginal", exif->override_datetime);
- res = override_exif_time (exifData, "Exif.Photo.DateTimeDigitized", exif->override_datetime) || res;
- res = override_exif_time (exifData, "Exif.Image.DateTime", exif->override_datetime) || res;
- modified = TRUE;
- }
- if (! iptcData.empty()) {
- res = override_iptc_time (iptcData, "Iptc.Application2.DateCreated", "Iptc.Application2.TimeCreated", exif->override_datetime);
- res = override_iptc_time (iptcData, "Iptc.Application2.DigitizationDate", "Iptc.Application2.DigitizationTime", exif->override_datetime) || res;
- if (res)
- modified = TRUE;
- }
- }
-
- if (exif->override_aperture != -1) {
- if (! exifData.empty()) {
- exifData["Exif.Photo.FNumber"] = Exiv2::floatToRationalCast (exif->override_aperture);
- if (exifData["Exif.Photo.ApertureValue"].count() > 0)
- exifData["Exif.Photo.ApertureValue"] = Exiv2::floatToRationalCast (exif->override_aperture);
- modified = TRUE;
- }
- }
-
- if (exif->override_focal_length != -1) {
- if (! exifData.empty()) {
- exifData["Exif.Photo.FocalLength"] = Exiv2::floatToRationalCast (exif->override_focal_length);
- modified = TRUE;
- }
- }
-
- if (exif->override_artist_name) {
- if (! exifData.empty()) {
- exifData["Exif.Image.Artist"] = exif->override_artist_name;
- if (exifData["Exif.Photo.CameraOwnerName"].count() >= 1)
- exifData["Exif.Photo.CameraOwnerName"] = exif->override_artist_name;
- if (exifData["Exif.Canon.OwnerName"].count() >= 1)
- exifData["Exif.Canon.OwnerName"] = exif->override_artist_name;
- modified = TRUE;
- }
- }
- }
-
-
- if (strip_thumbnail && ! exifData.empty()) {
-#ifdef HAVE_EXIFTHUMB
- Exiv2::ExifThumb exifThumb(image->exifData());
- std::string thumbExt = exifThumb.extension();
-#else
- std::string thumbExt = exifData.thumbnailExtension();
-#endif
-
- if (! thumbExt.empty()) {
-#ifdef HAVE_EXIFTHUMB
- exifThumb.erase();
-#else
- exifData.eraseThumbnail();
-#endif
- modified = TRUE;
- }
- }
-
- if (strip_xmp) {
- if (! image->xmpData().empty()) {
- image->clearXmpData ();
- modified = TRUE;
- }
- if (! image->xmpPacket().empty()) {
- image->clearXmpPacket ();
- modified = TRUE;
- }
- }
-
- if (modified)
- image->writeMetadata();
- }
- catch (Exiv2::AnyError& e)
- {
- log_error ("modify_exif: Caught Exiv2 exception: '%s'\n", e.what());
- }
-}