From ce1ae1ff69309ec7f1905e928a9d43eb8ae86dbe Mon Sep 17 00:00:00 2001 From: Tomas Bzatek Date: Mon, 26 Sep 2016 14:56:06 +0200 Subject: jpeg-utils: Add support for custom resize options This change brings the possibility to tweak resize options using standard ImageMagick `convert` command syntax. Separate options are offered for thumbnails. --- src/gallery-utils.c | 34 ++++++++++++++++ src/gallery-utils.h | 5 +++ src/generators.c | 3 +- src/jpeg-utils.cpp | 115 +++++++++++++++++++++++++++++++++++++++++++++++----- src/jpeg-utils.h | 3 +- src/setup.c | 19 +++++++++ src/setup.h | 2 + 7 files changed, 169 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/gallery-utils.c b/src/gallery-utils.c index d97cef3..403b3c6 100644 --- a/src/gallery-utils.c +++ b/src/gallery-utils.c @@ -93,6 +93,40 @@ str_replace (gchar **dst, const gchar *search, const gchar *replace) } +/* + * str_trim_inside: trims any leading, trailing and repeated whitespaces in the whole string + */ +#define IS_WHITESPACE(s) (s == ' ' || s == '\t') + +void +str_trim_inside (gchar **s) +{ + gchar *dst; + gboolean was_whitespace; + int pos; + gchar *src; + + if (! s || strlen (*s) == 0 ) + return; + + /* suppose the new string is not longer than the input string */ + dst = g_malloc0 (strlen (*s) + 1); + was_whitespace = TRUE; /* handles beginning of the string */ + pos = 0; + for (src = *s; *src != '\0'; src++) + if (! IS_WHITESPACE (*src) || ! was_whitespace) { + dst[pos++] = *src; + was_whitespace = IS_WHITESPACE (*src); + } + if (IS_WHITESPACE (dst[pos - 1])) + dst[pos - 1] = '\0'; + + /* return fixed string */ + g_free (*s); + *s = dst; +} + + /* * copy_file: copy file from src to dst */ diff --git a/src/gallery-utils.h b/src/gallery-utils.h index 1af8b84..131f4e2 100644 --- a/src/gallery-utils.h +++ b/src/gallery-utils.h @@ -37,6 +37,11 @@ G_BEGIN_DECLS */ void str_replace (gchar **dst, const gchar *search, const gchar *replace); +/* + * str_trim_inside: trims any leading, trailing and repeated whitespaces in the whole string + */ +void str_trim_inside (gchar **s); + /* * copy_file: copy file from src to dst */ diff --git a/src/generators.c b/src/generators.c index 6f64a6f..4cdbe0e 100644 --- a/src/generators.c +++ b/src/generators.c @@ -544,7 +544,8 @@ generate_image (TGallerySetup *setup, if (! copy_file (img_src, img_dst)) log_error (" Error copying image %s to %s\n", img_src, img_dst); } else { - if (! resize_image (img_src, img_dst, img_w, img_h, img_quality, image_size->is_thumbnail, setup->autorotate, exif_data)) + if (! resize_image (img_src, img_dst, img_w, img_h, img_quality, image_size->is_thumbnail, setup->autorotate, exif_data, + image_size->is_thumbnail ? setup->design->imgmagick_thumb_opts : setup->design->imgmagick_resize_opts)) log_error (" Error resizing image %s\n", img_src); } } else { diff --git a/src/jpeg-utils.cpp b/src/jpeg-utils.cpp index 11d2c4e..aaf59fc 100644 --- a/src/jpeg-utils.cpp +++ b/src/jpeg-utils.cpp @@ -467,6 +467,37 @@ autorotate_image (MagickWand *magick_wand) 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 */ @@ -476,14 +507,20 @@ resize_image (const gchar *src, const gchar *dst, int quality, gboolean thumbnail, gboolean autorotate, - ExifData *exif) + 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); @@ -494,6 +531,7 @@ resize_image (const gchar *src, const gchar *dst, description = MagickGetException (magick_wand, &severity); log_error ("Error reading image: %s %s %ld %s\n", GetMagickModule(), description); MagickRelinquishMemory (description); + DestroyMagickWand (magick_wand); return FALSE; } @@ -501,10 +539,9 @@ resize_image (const gchar *src, const gchar *dst, autorotate_image (magick_wand); /* Don't resize if smaller than desired size */ - if (MagickGetImageWidth (magick_wand) > size_x || - MagickGetImageHeight (magick_wand) > size_y) + if (MagickGetImageWidth (magick_wand) > size_x || MagickGetImageHeight (magick_wand) > size_y) { - /* Process thumbnail if required */ + /* Prepare image before resizing */ if (thumbnail) { if (exif->thumbnail_crop_style != CROP_STYLE_NORMAL) { w = MagickGetImageWidth (magick_wand); @@ -548,12 +585,64 @@ resize_image (const gchar *src, const gchar *dst, break; } } - MagickThumbnailImage (magick_wand, size_x, size_y); - /* FIXME: this strips image ICC profile, should do proper conversion first */ - MagickStripImage (magick_wand); } - else - MagickResizeImage (magick_wand, size_x, size_y, LanczosFilter, 1.0); + + if (resize_opts == NULL) { + /* Perform internal resizing */ + if (thumbnail) { + MagickThumbnailImage (magick_wand, size_x, size_y); + } else { + MagickResizeImage (magick_wand, size_x, size_y, LanczosFilter, 1.0); + } + } 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) @@ -563,10 +652,16 @@ resize_image (const gchar *src, const gchar *dst, if (MagickWriteImage (magick_wand, dst) == MagickFalse) { description = MagickGetException (magick_wand, &severity); log_error ("Error writing image: %s %s %ld %s\n", GetMagickModule(), description); - MagickRelinquishMemory (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; diff --git a/src/jpeg-utils.h b/src/jpeg-utils.h index 405d9b0..e23dee7 100644 --- a/src/jpeg-utils.h +++ b/src/jpeg-utils.h @@ -100,7 +100,8 @@ gboolean resize_image (const gchar *src, const gchar *dst, int quality, gboolean thumbnail, gboolean autorotate, - ExifData *exif); + ExifData *exif, + gchar *resize_opts); /* * get_image_sizes: retrieve image dimensions diff --git a/src/setup.c b/src/setup.c index 7b59030..9780807 100644 --- a/src/setup.c +++ b/src/setup.c @@ -184,6 +184,23 @@ parse_design_setup_xml (const gchar *filename) g_free (s2); } + design->imgmagick_resize_opts = xml_file_get_node_value (xml, "/design_setup/resize/ImageMagick/resize_options/text()"); + if (design->imgmagick_resize_opts) { + if (strlen (design->imgmagick_resize_opts) == 0) { + g_free (design->imgmagick_resize_opts); + design->imgmagick_resize_opts = NULL; + } else + design->imgmagick_resize_opts = g_strstrip (design->imgmagick_resize_opts); + } + design->imgmagick_thumb_opts = xml_file_get_node_value (xml, "/design_setup/resize/ImageMagick/thumbnail_options/text()"); + if (design->imgmagick_thumb_opts) { + if (strlen (design->imgmagick_thumb_opts) == 0) { + g_free (design->imgmagick_thumb_opts); + design->imgmagick_thumb_opts = NULL; + } else + design->imgmagick_thumb_opts = g_strstrip (design->imgmagick_thumb_opts); + } + /* image_sizes section */ count = xml_file_node_get_children_count (xml, "/design_setup/image_sizes/size"); @@ -657,6 +674,8 @@ free_design_setup_data (TGalleryDesign *design) g_list_foreach (design->themes, (GFunc) free_design_theme_data, NULL); g_list_free (design->themes); g_strfreev (design->supplemental_files); + g_free (design->imgmagick_resize_opts); + g_free (design->imgmagick_thumb_opts); g_free (design); } } diff --git a/src/setup.h b/src/setup.h index 54b89b2..c11efed 100644 --- a/src/setup.h +++ b/src/setup.h @@ -137,6 +137,8 @@ struct TGalleryDesign { GList *image_sizes; GList *themes; gchar **supplemental_files; + gchar *imgmagick_resize_opts; + gchar *imgmagick_thumb_opts; }; -- cgit v1.2.3