diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b07470..43b3eb2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,7 @@ if(WITH_FONTCONFIGURATION_FONTCONFIG) endif(WITH_FONTCONFIGURATION_FONTCONFIG) macro_optional_find_package(JPEG) macro_optional_find_package(PNG) +macro_optional_find_package(ImageMagick COMPONENTS Magick++) macro_optional_find_package(TIFF) if(JPEG_FOUND) set(ENABLE_LIBJPEG ${JPEG_FOUND}) @@ -194,6 +195,10 @@ if(PNG_FOUND) include_directories(${PNG_INCLUDE_DIR}) set(ENABLE_LIBPNG ON) endif(PNG_FOUND) +if(ImageMagick_FOUND) + include_directories(${ImageMagick_INCLUDE_DIRS}) + set(ENABLE_IMAGEMAGICK ON) +endif(ImageMagick_FOUND) if(TIFF_FOUND) include_directories(${TIFF_INCLUDE_DIR}) set(ENABLE_LIBTIFF ON) @@ -381,6 +386,9 @@ endif(WIN32) if(PNG_FOUND) set(poppler_LIBS ${poppler_LIBS} ${PNG_LIBRARIES}) endif(PNG_FOUND) +if(ImageMagick_FOUND) + set(poppler_LIBS ${poppler_LIBS} ${ImageMagick_LIBRARIES}) +endif(ImageMagick_FOUND) if(TIFF_FOUND) set(poppler_LIBS ${poppler_LIBS} ${TIFF_LIBRARIES}) endif(TIFF_FOUND) @@ -610,6 +618,7 @@ show_end_message_yesno("cpp wrapper" ENABLE_CPP) show_end_message("use gtk-doc" "not supported with this CMake build system") show_end_message_yesno("use libjpeg" ENABLE_LIBJPEG) show_end_message_yesno("use libpng" ENABLE_LIBPNG) +show_end_message_yesno("use imagemagick" ENABLE_IMAGEMAGICK) show_end_message_yesno("use libtiff" ENABLE_LIBTIFF) show_end_message_yesno("use zlib" ENABLE_ZLIB) show_end_message_yesno("use curl" ENABLE_LIBCURL) diff --git a/config.h.cmake b/config.h.cmake index 3c7b968..396bffa 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -15,6 +15,9 @@ /* Build against libpng. */ #cmakedefine ENABLE_LIBPNG 1 +/* Build against imagemagick. */ +#cmakedefine ENABLE_IMAGEMAGICK 1 + /* Do not hardcode the library location */ #cmakedefine ENABLE_RELOCATABLE 1 diff --git a/configure.ac b/configure.ac index 62a27b9..6659ef9 100644 --- a/configure.ac +++ b/configure.ac @@ -395,6 +395,24 @@ fi AM_CONDITIONAL(BUILD_LIBPNG, test x$enable_libpng = xyes) +dnl Test for ImageMagick Magick++ +AC_ARG_ENABLE(imagemagick, + AC_HELP_STRING([--disable-imagemagick], + [Do not use ImageMagick for image scaling in the Splash backend.]), + enable_imagemagick=$enableval, + enable_imagemagick="try") + +if test x$enable_imagemagick != xno; then + PKG_CHECK_MODULES(IMAGEMAGICK, ImageMagick++, [enable_imagemagick="yes"], + [enable_imagemagick="no"]) +fi + +if test x$enable_imagemagick = xyes; then + AC_DEFINE(ENABLE_IMAGEMAGICK, 1, [Build against ImageMagick.]) +fi + +AM_CONDITIONAL(BUILD_IMAGEMAGICK, test x$enable_imagemagick = xyes) + dnl Check for freetype headers PKG_CHECK_MODULES(FREETYPE, freetype2, @@ -819,6 +837,7 @@ echo " use zlib: $enable_zlib" echo " use libcurl: $enable_libcurl" echo " use libopenjpeg: $enable_libopenjpeg" echo " use cms: $enable_cms" +echo " use imagemagick: $enable_imagemagick" if test x$enable_cms = xyes;then if test x$lcms1 = xyes;then echo " with lcms1" diff --git a/poppler/Makefile.am b/poppler/Makefile.am index 8920f8e..cb01cc7 100644 --- a/poppler/Makefile.am +++ b/poppler/Makefile.am @@ -138,6 +138,15 @@ cms_includes = $(LCMS_CFLAGS) cms_libs = $(LCMS_LIBS) endif +if BUILD_IMAGEMAGICK + +imagemagick_libs = \ + $(IMAGEMAGICK_LIBS) +imagemagick_includes = \ + $(IMAGEMAGICK_CFLAGS) + +endif + INCLUDES = \ -I$(top_srcdir) \ -I$(top_srcdir)/goo \ @@ -150,6 +159,7 @@ INCLUDES = \ $(libjpeg2000_includes) \ $(libpng_includes) \ $(libcurl_includes) \ + $(imagemagick_includes) \ $(FREETYPE_CFLAGS) \ $(FONTCONFIG_CFLAGS) @@ -172,6 +182,7 @@ libpoppler_la_LIBADD = \ $(zlib_libs) \ $(libcurl_libs) \ $(libjpeg2000_libs) \ + $(imagemagick_libs) \ $(FREETYPE_LIBS) \ $(FONTCONFIG_LIBS) \ $(PTHREAD_LIBS) \ diff --git a/splash/Makefile.am b/splash/Makefile.am index 4330c53..3617b9e 100644 --- a/splash/Makefile.am +++ b/splash/Makefile.am @@ -4,6 +4,7 @@ INCLUDES = \ $(libjpeg_includes) \ $(libtiff_includes) \ $(libpng_includes) \ + $(imagemagick_includes) \ $(FREETYPE_CFLAGS) # SplashBitmap includes JpegWriter.h, TiffWriter.h, PNGWriter.h @@ -19,6 +20,10 @@ if BUILD_LIBPNG libpng_includes = $(LIBPNG_CFLAGS) endif +if BUILD_IMAGEMAGICK +imagemagick_includes = $(IMAGEMAGICK_CFLAGS) +endif + noinst_LTLIBRARIES = libsplash.la if ENABLE_XPDF_HEADERS diff --git a/splash/Splash.cc b/splash/Splash.cc index e6559f4..8959987 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -2963,6 +2963,10 @@ void Splash::arbitraryTransformMask(SplashImageMaskSource src, void *srcData, SplashBitmap *Splash::scaleMask(SplashImageMaskSource src, void *srcData, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight) { +#ifdef ENABLE_IMAGEMAGICK + return scaleMaskMagick(src, srcData, srcWidth, srcHeight, scaledWidth, scaledHeight); +#endif + SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, splashModeMono8, @@ -3293,6 +3297,35 @@ void Splash::scaleMaskYuXu(SplashImageMaskSource src, void *srcData, gfree(lineBuf); } +void Splash::bitmapFromMaskSource(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + SplashBitmap *dest) { + Guchar *lineBuf; + Guint pix; + Guchar *destPtr0, *destPtr; + int y, x; + + // allocate buffers + lineBuf = (Guchar *)gmalloc(srcWidth); + + destPtr0 = dest->data; + for (y = 0; y < srcHeight; ++y) { + // read row from image + (*src)(srcData, lineBuf); + + for (x = 0; x < srcWidth; ++x) { + // compute the final pixel + pix = lineBuf[x] ? 255 : 0; + + // store the pixel + destPtr = destPtr0 + x; + *destPtr++ = (Guchar)pix; + } + destPtr0 += srcWidth; + } + gfree(lineBuf); +} + void Splash::blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes) { SplashPipe pipe; @@ -3800,6 +3833,13 @@ SplashBitmap *Splash::scaleImage(SplashImageSource src, void *srcData, SplashColorMode srcMode, int nComps, GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight) { +#ifdef ENABLE_IMAGEMAGICK + return scaleImageMagick(src, srcData, + srcMode, nComps, + srcAlpha, srcWidth, srcHeight, + scaledWidth, scaledHeight); +#endif + SplashBitmap *dest; dest = new SplashBitmap(scaledWidth, scaledHeight, 1, srcMode, srcAlpha); @@ -4486,6 +4526,89 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, gfree(lineBuf); } +void Splash::bitmapFromSource(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + SplashBitmap *dest) { + Guchar *lineBuf, *alphaLineBuf; + Guint pix[splashMaxColorComps]; + Guint alpha; + Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; + int x, y; + int i; + + // allocate buffers + lineBuf = (Guchar *)gmallocn(srcWidth, nComps); + if (srcAlpha) { + alphaLineBuf = (Guchar *)gmalloc(srcWidth); + } else { + alphaLineBuf = NULL; + } + + destPtr0 = dest->data; + destAlphaPtr0 = dest->alpha; + for (y = 0; y < srcHeight; ++y) { + // read row from image + (*src)(srcData, lineBuf, alphaLineBuf); + + for (x = 0; x < srcWidth; ++x) { + // compute the final pixel + for (i = 0; i < nComps; ++i) { + pix[i] = lineBuf[x * nComps + i]; + } + + // store the pixel + destPtr = destPtr0 + x * nComps; + switch (srcMode) { + case splashModeMono1: // mono1 is not allowed + break; + case splashModeMono8: + *destPtr++ = (Guchar)pix[0]; + break; + case splashModeRGB8: + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + break; + case splashModeXBGR8: + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)255; + break; + case splashModeBGR8: + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[0]; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + *destPtr++ = (Guchar)pix[0]; + *destPtr++ = (Guchar)pix[1]; + *destPtr++ = (Guchar)pix[2]; + *destPtr++ = (Guchar)pix[3]; + break; +#endif + } + + // process alpha + if (srcAlpha) { + alpha = alphaLineBuf[x]; + destAlphaPtr = destAlphaPtr0 + x; + *destAlphaPtr = (Guchar)alpha; + } + } + + destPtr0 += srcWidth * nComps; + if (srcAlpha) { + destAlphaPtr0 += srcWidth; + } + } + + gfree(alphaLineBuf); + gfree(lineBuf); +} + void Splash::vertFlipImage(SplashBitmap *img, int width, int height, int nComps) { Guchar *lineBuf; @@ -5864,3 +5987,187 @@ SplashError Splash::shadedFill(SplashPath *path, GBool hasBBox, delete xPath; return splashOk; } + +#ifdef ENABLE_IMAGEMAGICK +void Splash::bitmapToMagickImage(SplashBitmap *src, Magick::Image **dst) { + using namespace Magick; + char *buffer; + size_t size; + FILE *file; + Blob *blob; + int x, y; + PixelPacket *pixel_cache, *pixel; + + file = open_memstream(&buffer, &size); + src->writeImgFile(splashFormatPng, file, 72, 72); + fclose(file); + blob = new Blob(buffer, size); + free(buffer); + *dst = new Image(*blob); + (*dst)->modifyImage(); + //(*dst)->verbose(1); + delete blob; + if (src->alpha) { + pixel_cache = (*dst)->getPixels(0, 0, src->width, src->height); + (*dst)->matte(1); + for (x = 0; x < src->width; x++) { + for (y = 0; y < src->height; y++) { + pixel = pixel_cache + src->width * y + x; + pixel->opacity = 255 - (src->alpha[y * src->width + x] * MaxRGB / 255); + } + } + (*dst)->syncPixels(); + } +} + +void Splash::magickImageToBitmap(Magick::Image *src, SplashBitmap **dst, SplashColorMode mode) { + using namespace Magick; + int width = src->columns(); + int height = src->rows(); + GBool alpha = src->matte(); + int x, y; + SplashColorPtr p; + PixelPacket *pixel_cache, *pixel; + Guchar red, green, blue; + int pixels_per_color = 1; + + src->modifyImage(); +#if SPLASH_CMYK + src->colorSpace(CMYKColorspace); +#endif + // we need to write the image somewhere otherwise the transformations aren't applied + // TODO: figure out a cheaper way to do it + Blob *blob; + blob = new Blob(); + src->write(blob, "PNG"); + delete blob; + pixel_cache = src->getPixels(0, 0, width, height); + *dst = new SplashBitmap(width, height, 1, mode, alpha); + switch (mode) { + case splashModeMono1: + // not handled + break; + case splashModeMono8: + pixels_per_color = 1; + break; + case splashModeRGB8: + case splashModeBGR8: + pixels_per_color = 3; + break; +#if SPLASH_CMYK + case splashModeCMYK8: +#endif + case splashModeXBGR8: + pixels_per_color = 4; + break; + } + for (x = 0; x < width; x++) { + for (y = 0; y < height; y++) { + p = &((*dst)->data[y * (*dst)->rowSize + pixels_per_color * x]); + pixel = pixel_cache + width * y + x; + red = pixel->red * 255 / MaxRGB; + green = pixel->green * 255 / MaxRGB; + blue = pixel->blue * 255 / MaxRGB; + switch (mode) { + case splashModeMono1: + // not handled + break; + case splashModeMono8: + p[0] = red; + //p[0] = 0.2126 * red + 0.7152 * green + 0.0722 * blue; + break; + case splashModeRGB8: + p[0] = red; + p[1] = green; + p[2] = blue; + break; + case splashModeXBGR8: + p[0] = blue; + p[1] = green; + p[2] = red; + p[3] = 255; + break; + case splashModeBGR8: + p[0] = blue; + p[1] = green; + p[2] = red; + break; +#if SPLASH_CMYK + case splashModeCMYK8: + p[0] = red; // C + p[1] = green; // M + p[2] = blue; // Y + p[3] = pixel->black * 255 / MaxRGB; // K + break; +#endif + } + if (alpha && (*dst)->alpha) { + (*dst)->alpha[y * width + x] = 255 - (pixel->opacity * 255 / MaxRGB); + } + } + } +} + +SplashBitmap *Splash::scaleBitmapMagick(SplashBitmap *src_bitmap, int scaledWidth, int scaledHeight, GBool force_sample=gFalse) { + SplashBitmap *dest; + Magick::Image *magick_image; + + bitmapToMagickImage(src_bitmap, &magick_image); + SplashBitmap *soft_mask; + soft_mask = getSoftMask(); + Magick::Geometry geom(scaledWidth, scaledHeight); + geom.aspect(gTrue); + if (soft_mask || force_sample) { + // avoid the black halo on images with soft masks + magick_image->sample(geom); + } else { +#if 0 +//#if MagickLibVersion > 0x669 + // the resize distortion is slightly better but slower and only available from version 6.6.9-2 + double args[] = {scaledWidth, scaledHeight}; + magick_image->distort(MagickCore::ResizeDistortion, 2, args); +#else + magick_image->resize(geom); +#endif + } + magickImageToBitmap(magick_image, &dest, src_bitmap->mode); + return dest; +} + +SplashBitmap *Splash::scaleImageMagick(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight) { + SplashBitmap *src_bitmap, *dest; + + src_bitmap = new SplashBitmap(srcWidth, srcHeight, 1, srcMode, srcAlpha); + bitmapFromSource(src, srcData, srcMode, nComps, srcAlpha, + srcWidth, srcHeight, src_bitmap); + // if we don't have to scale anything, just return src_bitmap + if (srcWidth == scaledWidth && srcHeight == scaledHeight) { + return src_bitmap; + } + + dest = scaleBitmapMagick(src_bitmap, scaledWidth, scaledHeight); + delete src_bitmap; + return dest; +} + +SplashBitmap *Splash::scaleMaskMagick(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight) { + SplashBitmap *src_bitmap, *dest; + + src_bitmap = new SplashBitmap(srcWidth, srcHeight, 1, splashModeMono8, gFalse); + bitmapFromMaskSource(src, srcData, srcWidth, srcHeight, src_bitmap); + // if we don't have to scale anything, just return src_bitmap + if (srcWidth == scaledWidth && srcHeight == scaledHeight) { + return src_bitmap; + } + + dest = scaleBitmapMagick(src_bitmap, scaledWidth, scaledHeight, gTrue); + delete src_bitmap; + return dest; +} +#endif + diff --git a/splash/Splash.h b/splash/Splash.h index f4fb542..81e4440 100644 --- a/splash/Splash.h +++ b/splash/Splash.h @@ -32,6 +32,9 @@ #include "SplashTypes.h" #include "SplashClip.h" #include "SplashPattern.h" +#ifdef ENABLE_IMAGEMAGICK +#include +#endif class SplashBitmap; struct SplashGlyphBitmap; @@ -342,6 +345,9 @@ private: int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); + void bitmapFromMaskSource(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + SplashBitmap *dest); void blitMask(SplashBitmap *src, int xDest, int yDest, SplashClipResult clipRes); SplashError arbitraryTransformImage(SplashImageSource src, void *srcData, @@ -373,6 +379,10 @@ private: GBool srcAlpha, int srcWidth, int srcHeight, int scaledWidth, int scaledHeight, SplashBitmap *dest); + void bitmapFromSource(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + SplashBitmap *dest); void vertFlipImage(SplashBitmap *img, int width, int height, int nComps); void blitImage(SplashBitmap *src, GBool srcAlpha, int xDest, int yDest, @@ -382,6 +392,19 @@ private: int w, int h); void dumpPath(SplashPath *path); void dumpXPath(SplashXPath *path); + #ifdef ENABLE_IMAGEMAGICK + void bitmapToMagickImage(SplashBitmap *src, Magick::Image **dst); + void magickImageToBitmap(Magick::Image *src, SplashBitmap **dst, SplashColorMode mode); + SplashBitmap *scaleBitmapMagick(SplashBitmap *src_bitmap, int scaledWidth, int scaledHeight, + GBool force_sample); + SplashBitmap *scaleImageMagick(SplashImageSource src, void *srcData, + SplashColorMode srcMode, int nComps, + GBool srcAlpha, int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight); + SplashBitmap *scaleMaskMagick(SplashImageMaskSource src, void *srcData, + int srcWidth, int srcHeight, + int scaledWidth, int scaledHeight); + #endif static SplashPipeResultColorCtrl pipeResultColorNoAlphaBlend[]; static SplashPipeResultColorCtrl pipeResultColorAlphaNoBlend[]; diff --git a/utils/Makefile.am b/utils/Makefile.am index d56cc9c..45becc1 100644 --- a/utils/Makefile.am +++ b/utils/Makefile.am @@ -15,9 +15,14 @@ INCLUDES = \ -I$(top_srcdir)/goo \ -I$(top_srcdir)/utils \ -I$(top_srcdir)/poppler \ + $(imagemagick_includes) \ $(UTILS_CFLAGS) \ $(PDFTOCAIRO_CFLAGS) +if BUILD_IMAGEMAGICK +imagemagick_includes = $(IMAGEMAGICK_CFLAGS) +endif + LDADD = \ $(top_builddir)/poppler/libpoppler.la \ $(UTILS_LIBS)