From 46dba2d14d7c677d1bd7b9c20b983500016d5b76 Mon Sep 17 00:00:00 2001 From: Colin Clark Date: Mon, 22 Jul 2019 12:40:14 +0100 Subject: [PATCH] Fix #277: DjVu image support for geeqie https://github.com/BestImageViewer/geeqie/issues/277 --- README.md | 5 +- configure.ac | 26 +++ .../GuideReferenceSupportedFormats.xml | 5 +- src/Makefile.am | 4 +- src/image-load.c | 24 +++ src/image_load_djvu.c | 191 ++++++++++++++++++ src/image_load_djvu.h | 29 +++ web/geeqie-install-debian.sh | 4 +- 8 files changed, 283 insertions(+), 5 deletions(-) create mode 100644 src/image_load_djvu.c create mode 100644 src/image_load_djvu.h diff --git a/README.md b/README.md index be2afd5a..f018af34 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ Geeqie is a graphics file viewer. Basic features: * output: single image, anaglyph, SBS, mirror, SBS half size (3DTV) * Viewing raster and vector images, in the following formats: -3FR, ANI, APM, ARW, BMP, CR2, CRW, CUR, DNG, ERF, GIF, ICNS, ICO, JPE/JPEG/JPG, JPS, KDC, MEF, MPO, MOS, MRW, NEF, ORF, PEF, PTX, PBM/PGM/PNM/PPM, PNG, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WMF, XBM, XPM, HEIF (primary image only), WEBP. Animated GIFs are supported. +3FR, ANI, APM, ARW, BMP, CR2, CRW, CUR, DNG, ERF, GIF, ICNS, ICO, JPE/JPEG/JPG, JPS, KDC, MEF, MPO, MOS, MRW, NEF, ORF, PEF, PTX, PBM/PGM/PNM/PPM, PNG, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WMF, XBM, XPM, HEIF (primary image only), WEBP, DjVu. Animated GIFs are supported. * Preview and thumbnails of video clips can be displayed. Clips can be run via a defined external program. @@ -266,6 +266,9 @@ And either the ChangeLog file or [Geeqie ChangeLog](http://geeqie.org/cgi-bin/gi libwebp For displaying webp images + libdjvulibre + For displaying DjVu images + ### Code hackers: If you plan on making any major changes to the code that will be offered for diff --git a/configure.ac b/configure.ac index f7a532ca..caf56fac 100644 --- a/configure.ac +++ b/configure.ac @@ -626,6 +626,31 @@ AM_CONDITIONAL(HAVE_WEBP, [test "x$HAVE_WEBP" = xyes]) AC_SUBST(WEBP_CFLAGS) AC_SUBST(WEBP_LIBS) +# DjVu support +# ---------------------------------------------------------------------- + +AC_ARG_ENABLE([djvu], + AC_HELP_STRING([--disable-djvu], [disable djvu support]), + [libdjvulibre=$enableval], [libdjvulibre=auto]) + +if test "x${libdjvulibre}" != "xno"; then + PKG_CHECK_MODULES(DJVU, ddjvuapi >= 3.5.27, + [ + HAVE_DJVU=yes + AC_DEFINE(HAVE_DJVU, 1, [define to enable DjVu support]) + ], + [ + HAVE_DJVU=no + AC_MSG_WARN([$DJVU_PKG_ERRORS]) + ]) +else + HAVE_DJVU=disabled +fi + +AM_CONDITIONAL(HAVE_DJVU, [test "x$HAVE_DJVU" = xyes]) +AC_SUBST(DJVU_CFLAGS) +AC_SUBST(DJVU_LIBS) + # Markdown support # ---------------------------------------------------------------------- @@ -733,6 +758,7 @@ Support: Pdf: $HAVE_PDF HEIF: $HAVE_HEIF WebP: $HAVE_WEBP + DjVu: $HAVE_DJVU Documentation: Doxygen: $DX_DOXYGEN diff --git a/doc/docbook/GuideReferenceSupportedFormats.xml b/doc/docbook/GuideReferenceSupportedFormats.xml index 6f422912..a6c69bde 100644 --- a/doc/docbook/GuideReferenceSupportedFormats.xml +++ b/doc/docbook/GuideReferenceSupportedFormats.xml @@ -1,7 +1,7 @@
Supported File Formats - 3FR, ANI, APM, ARW, BMP, CR2, CRW, CUR, DDS, DNG, ERF, GIF, ICNS, ICO, JPE/JPEG/JPG, JPS, KDC, MEF, MPO, MOS, MRW, NEF, ORF, PEF, PTX, PBM/PGM/PNM/PPM, PNG, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WMF, XBM, XPM, HEIF (primary image only), WebP. Animated GIFs are supported. + 3FR, ANI, APM, ARW, BMP, CR2, CRW, CUR, DDS, DNG, ERF, GIF, ICNS, ICO, JPE/JPEG/JPG, JPS, KDC, MEF, MPO, MOS, MRW, NEF, ORF, PEF, PTX, PBM/PGM/PNM/PPM, PNG, QIF/QTIF (QuickTime Image Format), RAF, RAW, RW2, SR2, SRF, SVG/SVGZ, TGA/TARGA, TIF/TIFF, WMF, XBM, XPM, HEIF (primary image only), WebP, DjVu. Animated GIFs are supported. Refer to @@ -9,6 +9,7 @@ Preview and thumbnails of video clips can be displayed. Clips can be run via a defined external program - see - Play video by left click on image. + Play video by left click on image + .
diff --git a/src/Makefile.am b/src/Makefile.am index 32565e04..ff475c51 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -195,6 +195,8 @@ geeqie_SOURCES = \ image_load_heif.h\ image_load_webp.c\ image_load_webp.h\ + image_load_djvu.c\ + image_load_djvu.h\ image_load_ffmpegthumbnailer.c\ image_load_ffmpegthumbnailer.h\ image-overlay.c \ @@ -284,7 +286,7 @@ geeqie_SOURCES = \ zonedetect.c \ zonedetect.h -geeqie_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(INTLLIBS) $(JPEG_LIBS) $(TIFF_LIBS) $(LCMS_LIBS) $(EXIV2_LIBS) $(LIBCHAMPLAIN_LIBS) $(LIBCHAMPLAIN_GTK_LIBS) $(LUA_LIBS) $(CLUTTER_LIBS) $(CLUTTER_GTK_LIBS) $(FFMPEGTHUMBNAILER_LIBS) $(PDF_LIBS) $(HEIF_LIBS) $(WEBP_LIBS) +geeqie_LDADD = $(GTK_LIBS) $(GLIB_LIBS) $(INTLLIBS) $(JPEG_LIBS) $(TIFF_LIBS) $(LCMS_LIBS) $(EXIV2_LIBS) $(LIBCHAMPLAIN_LIBS) $(LIBCHAMPLAIN_GTK_LIBS) $(LUA_LIBS) $(CLUTTER_LIBS) $(CLUTTER_GTK_LIBS) $(FFMPEGTHUMBNAILER_LIBS) $(PDF_LIBS) $(HEIF_LIBS) $(WEBP_LIBS) $(DJVU_LIBS) EXTRA_DIST = \ $(extra_SLIK) diff --git a/src/image-load.c b/src/image-load.c index d407a811..c9ddb5f5 100644 --- a/src/image-load.c +++ b/src/image-load.c @@ -25,6 +25,7 @@ #include "image_load_jpeg.h" #include "image_load_tiff.h" #include "image_load_dds.h" +#include "image_load_djvu.h" #include "image_load_pdf.h" #include "image_load_heif.h" #include "image_load_ffmpegthumbnailer.h" @@ -648,6 +649,15 @@ static void image_loader_setup_loader(ImageLoader *il) } else #endif +#ifdef HAVE_DJVU + if (il->bytes_total >= 16 && + (memcmp(il->mapped_file + 12, "DJV", 3) == 0)) + { + DEBUG_1("Using custom djvu loader"); + image_loader_backend_set_djvu(&il->backend); + } + else +#endif #ifdef HAVE_JPEG if (il->bytes_total >= 2 && il->mapped_file[0] == 0xff && il->mapped_file[1] == 0xd8) { @@ -691,6 +701,13 @@ static void image_loader_setup_loader(ImageLoader *il) } #endif +#ifdef HAVE_DJVU + if (g_strcmp0(il->fd->extension, ".djvu") == 0) + { + il->backend.set_page_num(il->loader, il->fd->page_num); + } +#endif + g_mutex_unlock(il->data_mutex); } @@ -781,6 +798,13 @@ static gboolean image_loader_begin(ImageLoader *il) file_data_set_page_total(il->fd, i); } #endif +#ifdef HAVE_DJVU + if (g_strcmp0(il->fd->extension, ".djvu") == 0) + { + gint i = il->backend.get_page_total(il->loader); + file_data_set_page_total(il->fd, i); + } +#endif il->bytes_read += b; diff --git a/src/image_load_djvu.c b/src/image_load_djvu.c new file mode 100644 index 00000000..46b1a5db --- /dev/null +++ b/src/image_load_djvu.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 20019 - The Geeqie Team + * + * Author: Colin Clark + * + * 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 + * (at your option) 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 "main.h" + +#include "image-load.h" +#include "image_load_djvu.h" + +#ifdef HAVE_DJVU + +#include +#include + +typedef struct _ImageLoaderDJVU ImageLoaderDJVU; +struct _ImageLoaderDJVU { + ImageLoaderBackendCbAreaUpdated area_updated_cb; + ImageLoaderBackendCbSize size_cb; + ImageLoaderBackendCbAreaPrepared area_prepared_cb; + gpointer data; + GdkPixbuf *pixbuf; + guint requested_width; + guint requested_height; + gboolean abort; + gint page_num; + gint page_total; +}; + +static void free_buffer(guchar *pixels, gpointer data) +{ + g_free (pixels);; +} + +static gboolean image_loader_djvu_load(gpointer loader, const guchar *buf, gsize count, GError **error) +{ + ImageLoaderDJVU *ld = (ImageLoaderDJVU *) loader; + ddjvu_context_t *ctx; + ddjvu_document_t *doc; + ddjvu_page_t *page; + ddjvu_rect_t rrect; + ddjvu_rect_t prect; + ddjvu_format_t *fmt; + gint width, height; + gint stride; + gboolean alpha = FALSE; + cairo_surface_t *surface; + gchar *pixels; + + ctx = ddjvu_context_create(NULL); + + doc = ddjvu_document_create(ctx, NULL, FALSE); + + ddjvu_stream_write(doc, 0, buf, count ); + while (!ddjvu_document_decoding_done(doc)); + + ld->page_total = ddjvu_document_get_pagenum(doc); + + page = ddjvu_page_create_by_pageno(doc, ld->page_num); + while (!ddjvu_page_decoding_done(page)); + + fmt = ddjvu_format_create(DDJVU_FORMAT_RGB24, 0, 0); + + width = ddjvu_page_get_width(page); + height = ddjvu_page_get_height(page); + stride = width * 4; + + pixels = (gchar *)g_malloc(height * stride); + + prect.x = 0; + prect.y = 0; + prect.w = width; + prect.h = height; + rrect = prect; + + surface = cairo_image_surface_create_for_data((guchar *)pixels, CAIRO_FORMAT_RGB24, width, height, stride); + + ddjvu_page_render(page, DDJVU_RENDER_COLOR, &prect, &rrect, fmt, stride, pixels); + + ld->pixbuf = gdk_pixbuf_new_from_data(pixels, GDK_COLORSPACE_RGB, alpha, 8, width, height, stride, free_buffer, NULL); + + ld->area_updated_cb(loader, 0, 0, width, height, ld->data); + + cairo_surface_destroy(surface); + ddjvu_page_release(page); + ddjvu_document_release(doc); + ddjvu_context_release(ctx); + + return TRUE; +} + +static gpointer image_loader_djvu_new(ImageLoaderBackendCbAreaUpdated area_updated_cb, ImageLoaderBackendCbSize size_cb, ImageLoaderBackendCbAreaPrepared area_prepared_cb, gpointer data) +{ + ImageLoaderDJVU *loader = g_new0(ImageLoaderDJVU, 1); + loader->area_updated_cb = area_updated_cb; + loader->size_cb = size_cb; + loader->area_prepared_cb = area_prepared_cb; + loader->data = data; + return (gpointer) loader; +} + +static void image_loader_djvu_set_size(gpointer loader, int width, int height) +{ + ImageLoaderDJVU *ld = (ImageLoaderDJVU *) loader; + ld->requested_width = width; + ld->requested_height = height; +} + +static GdkPixbuf* image_loader_djvu_get_pixbuf(gpointer loader) +{ + ImageLoaderDJVU *ld = (ImageLoaderDJVU *) loader; + return ld->pixbuf; +} + +static gchar* image_loader_djvu_get_format_name(gpointer loader) +{ + return g_strdup("djvu"); +} + +static gchar** image_loader_djvu_get_format_mime_types(gpointer loader) +{ + static gchar *mime[] = {"image/vnd.djvu", NULL}; + return g_strdupv(mime); +} + +static void image_loader_djvu_set_page_num(gpointer loader, gint page_num) +{ + ImageLoader *il = (ImageLoader *) loader; + ImageLoaderDJVU *ld = (ImageLoaderDJVU *) loader; + + ld->page_num = page_num; +} + +static gint image_loader_djvu_get_page_total(gpointer loader) +{ + ImageLoaderDJVU *ld = (ImageLoaderDJVU *) loader; + + return ld->page_total; +} + +static gboolean image_loader_djvu_close(gpointer loader, GError **error) +{ + return TRUE; +} + +static void image_loader_djvu_abort(gpointer loader) +{ + ImageLoaderDJVU *ld = (ImageLoaderDJVU *) loader; + ld->abort = TRUE; +} + +static void image_loader_djvu_free(gpointer loader) +{ + ImageLoaderDJVU *ld = (ImageLoaderDJVU *) loader; + if (ld->pixbuf) g_object_unref(ld->pixbuf); + g_free(ld); +} + +void image_loader_backend_set_djvu(ImageLoaderBackend *funcs) +{ + funcs->loader_new = image_loader_djvu_new; + funcs->set_size = image_loader_djvu_set_size; + funcs->load = image_loader_djvu_load; + funcs->write = NULL; + funcs->get_pixbuf = image_loader_djvu_get_pixbuf; + funcs->close = image_loader_djvu_close; + funcs->abort = image_loader_djvu_abort; + funcs->free = image_loader_djvu_free; + funcs->get_format_name = image_loader_djvu_get_format_name; + funcs->get_format_mime_types = image_loader_djvu_get_format_mime_types; + funcs->set_page_num = image_loader_djvu_set_page_num; + funcs->get_page_total = image_loader_djvu_get_page_total; +} + +#endif +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/src/image_load_djvu.h b/src/image_load_djvu.h new file mode 100644 index 00000000..83b97860 --- /dev/null +++ b/src/image_load_djvu.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 20019 - The Geeqie Team + * + * Author: Colin Clark + * + * 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 + * (at your option) 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. + */ + +#ifndef IMAGE_LOAD_DJVU_H +#define IMAGE_LOAD_DJVU_H + +#ifdef HAVE_DJVU +void image_loader_backend_set_djvu(ImageLoaderBackend *funcs); +#endif + +#endif +/* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */ diff --git a/web/geeqie-install-debian.sh b/web/geeqie-install-debian.sh index a5a414ba..3c45d646 100755 --- a/web/geeqie-install-debian.sh +++ b/web/geeqie-install-debian.sh @@ -1,5 +1,5 @@ #!/bin/bash -version="2019-07-21" +version="2019-07-22" description=$' Geeqie is an image viewer. This script will download, compile, and install Geeqie on Debian-based systems. @@ -65,6 +65,8 @@ optional_array=( "libheif-dev" "libwebp (for WebP images)" "libwebp-dev" +"libdjvulibre (for DjVu images)" +"libdjvulibre-dev" ) # Optional for GTK3 only -- 2.20.1