summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomas Bzatek <tbzatek@users.sourceforge.net>2016-09-26 14:56:06 +0200
committerTomas Bzatek <tbzatek@users.sourceforge.net>2016-10-02 17:11:34 +0200
commitce1ae1ff69309ec7f1905e928a9d43eb8ae86dbe (patch)
tree1333e1cf81b034681c4ac6b35665c17ccd984048
parentc80c60eb0623268112b5a0c37d80ada0384c3e90 (diff)
downloadcataract-ce1ae1ff69309ec7f1905e928a9d43eb8ae86dbe.tar.xz
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.
-rw-r--r--src/gallery-utils.c34
-rw-r--r--src/gallery-utils.h5
-rw-r--r--src/generators.c3
-rw-r--r--src/jpeg-utils.cpp115
-rw-r--r--src/jpeg-utils.h3
-rw-r--r--src/setup.c19
-rw-r--r--src/setup.h2
-rw-r--r--templates/fluid/fluid.xml16
8 files changed, 185 insertions, 12 deletions
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
@@ -94,6 +94,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
*/
gboolean
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
@@ -38,6 +38,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
*/
gboolean copy_file (const gchar *src, const gchar *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;
};
diff --git a/templates/fluid/fluid.xml b/templates/fluid/fluid.xml
index 9bdba19..376d56d 100644
--- a/templates/fluid/fluid.xml
+++ b/templates/fluid/fluid.xml
@@ -31,6 +31,22 @@
</size>
</image_sizes>
+ <resize>
+ <!-- Use this section to tweak resizing methods. For the moment ImageMagick
+ is the only supported method, being used from the beginning of the project,
+ however it offers nearly endless possibilities of image manipulation. -->
+ <ImageMagick>
+ <!-- For ImageMagick method, the option string is automatically prepended
+ with convert method name and input filename and appended with output
+ filename. The option string should however contain ${WIDTH} and ${HEIGHT}
+ arguments defining target dimensions. For the rest of arguments
+ standard convert(1) command syntax is used. -->
+ <!-- In case an option string is empty, internal defaults are used. -->
+ <resize_options>-set option:filter:blur 0.6 -filter Gaussian -thumbnail ${WIDTH}x${HEIGHT}</resize_options>
+ <thumbnail_options>-set option:filter:blur 0.6 -filter Gaussian -thumbnail ${WIDTH}x${HEIGHT}</thumbnail_options>
+ </ImageMagick>
+ </resize>
+
<supplemental_files><![CDATA[
styles.css
fonts.css