Hello! I've got two new patches for the cause.
The first one adds transparent PNG support to PNGWriter and SplashBitmap. The uses for this are varied and many, so I think it makes sense to add this to the mainline, even though I don't have the pdftoppm patch to go along with it. (We're using it in a custom SplashOutputDev.)
The second one fixes a nasty bug in SplashBitmap that causes "pdftoppm -jpeg" to randomly produce corrupt images. Currently SplashBitmap frees its temporary buffers and later calls ImgWriter::close(). Problem is libjpeg still uses these temporary buffers during jpeg_finish_compress(). So if they've been reclaimed - bad things happen. Usually libjpeg aborts and produces an image with the bottom fifty or so rows missing. The patch also fixes the SplashBitmap throwing a splashErrGeneric despite success, which was due to a missing question mark before writer->close().
Hope you find it useful. Cheers, Stefan Thomas
>From 4a535b3b4e5a95d90194a7a7075e0293d6b62156 Mon Sep 17 00:00:00 2001 From: Stefan Thomas <tho...@eload24.com> Date: Fri, 9 Jul 2010 22:59:04 +0100 Subject: [PATCH 1/2] Add support for transparent PNGs to PNGWriter and SplashBitmap. --- goo/PNGWriter.cc | 5 +- goo/PNGWriter.h | 4 +- splash/SplashBitmap.cc | 130 ++++++++++++++++++++++++++++++++++++++++++++---- splash/SplashBitmap.h | 5 ++- splash/SplashTypes.h | 3 +- 5 files changed, 132 insertions(+), 15 deletions(-) diff --git a/goo/PNGWriter.cc b/goo/PNGWriter.cc index aebab9e..1aa9532 100644 --- a/goo/PNGWriter.cc +++ b/goo/PNGWriter.cc @@ -18,8 +18,9 @@ #include "poppler/Error.h" -PNGWriter::PNGWriter() +PNGWriter::PNGWriter(GBool withAlphaChannel) { + this->withAlphaChannel = withAlphaChannel; } PNGWriter::~PNGWriter() @@ -59,7 +60,7 @@ bool PNGWriter::init(FILE *f, int width, int height, int hDPI, int vDPI) png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); png_byte bit_depth = 8; - png_byte color_type = PNG_COLOR_TYPE_RGB; + png_byte color_type = (withAlphaChannel) ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; png_byte interlace_type = PNG_INTERLACE_NONE; png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type, interlace_type, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); diff --git a/goo/PNGWriter.h b/goo/PNGWriter.h index 64ffc67..70bc86d 100644 --- a/goo/PNGWriter.h +++ b/goo/PNGWriter.h @@ -22,11 +22,12 @@ #include <cstdio> #include <png.h> #include "ImgWriter.h" +#include "goo/gtypes.h" class PNGWriter : public ImgWriter { public: - PNGWriter(); + PNGWriter(GBool withAlphaChannel = gFalse); ~PNGWriter(); bool init(FILE *f, int width, int height, int hDPI, int vDPI); @@ -39,6 +40,7 @@ class PNGWriter : public ImgWriter private: png_structp png_ptr; png_infop info_ptr; + GBool withAlphaChannel; }; #endif diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc index f983439..84d8666 100644 --- a/splash/SplashBitmap.cc +++ b/splash/SplashBitmap.cc @@ -288,13 +288,20 @@ SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, char *fileN SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, FILE *f, int hDPI, int vDPI) { ImgWriter *writer; - SplashError e; + SplashError e; + + GBool withAlpha = gFalse; switch (format) { #ifdef ENABLE_LIBPNG case splashFormatPng: writer = new PNGWriter(); break; + + case splashFormatPngAlpha: + writer = new PNGWriter(gTrue); + withAlpha = gTrue; + break; #endif #ifdef ENABLE_LIBJPEG @@ -310,12 +317,15 @@ SplashError SplashBitmap::writeImgFile(SplashImageFileFormat format, FILE *f, in return splashErrGeneric; } - e = writeImgFile(writer, f, hDPI, vDPI); - delete writer; - return e; + e = writeImgFile(writer, f, hDPI, vDPI, withAlpha); + delete writer; + return e; } -SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI) { +SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI, GBool withAlpha) +{ + SplashError e; + if (mode != splashModeRGB8 && mode != splashModeMono8 && mode != splashModeMono1 && mode != splashModeXBGR8) { error(-1, "unsupported SplashBitmap mode"); return splashErrGeneric; @@ -324,7 +334,24 @@ SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int if (!writer->init(f, width, height, hDPI, vDPI)) { return splashErrGeneric; } + + if (withAlpha) { + e = writeImgDataRGB(writer); + } else { + e = writeImgDataRGBA(writer); + } + + if (e) return e; + + if (writer->close()) { + return splashErrGeneric; + } + + return splashOk; +} +SplashError SplashBitmap::writeImgDataRGB(ImgWriter *writer) +{ switch (mode) { case splashModeRGB8: { @@ -408,10 +435,93 @@ SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int // can't happen break; } - - if (writer->close()) { - return splashErrGeneric; - } +} - return splashOk; +SplashError SplashBitmap::writeImgDataRGBA(ImgWriter *writer) +{ + unsigned char *row = new unsigned char[4 * width]; + + switch (mode) { + case splashModeRGB8: + { + for (int y = 0; y < height; y++) { + // Convert into a PNG row + for (int x = 0; x < width; x++) { + row[4*x] = data[y * rowSize + 3*x]; + row[4*x+1] = data[y * rowSize + 3*x + 1]; + row[4*x+2] = data[y * rowSize + 3*x + 2]; + row[4*x+3] = alpha[y * width + x]; + } + + if (!writer->writeRow(&row)) { + delete[] row; + return splashErrGeneric; + } + } + } + break; + + case splashModeXBGR8: + { + for (int y = 0; y < height; y++) { + // Convert into a PNG row + for (int x = 0; x < width; x++) { + row[4*x] = data[y * rowSize + x * 4 + 2]; + row[4*x+1] = data[y * rowSize + x * 4 + 1]; + row[4*x+2] = data[y * rowSize + x * 4]; + row[4*x+3] = alpha[y * width + x]; + } + + if (!writer->writeRow(&row)) { + delete[] row; + return splashErrGeneric; + } + } + } + break; + + case splashModeMono8: + { + for (int y = 0; y < height; y++) { + // Convert into a PNG row + for (int x = 0; x < width; x++) { + row[4*x] = data[y * rowSize + x]; + row[4*x+1] = data[y * rowSize + x]; + row[4*x+2] = data[y * rowSize + x]; + row[4*x+3] = alpha[y * width + x]; + } + + if (!writer->writeRow(&row)) { + delete[] row; + return splashErrGeneric; + } + } + } + break; + + case splashModeMono1: + { + for (int y = 0; y < height; y++) { + // Convert into a PNG row + for (int x = 0; x < width; x++) { + getPixel(x, y, &row[4*x]); + row[4*x+1] = row[4*x]; + row[4*x+2] = row[4*x]; + row[4*x+3] = alpha[y * width + x]; + } + + if (!writer->writeRow(&row)) { + delete[] row; + return splashErrGeneric; + } + } + } + break; + + default: + // can't happen + break; + } + + delete[] row; } diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h index e741a91..ec5b397 100644 --- a/splash/SplashBitmap.h +++ b/splash/SplashBitmap.h @@ -65,13 +65,16 @@ public: SplashError writeImgFile(SplashImageFileFormat format, char *fileName, int hDPI, int vDPI); SplashError writeImgFile(SplashImageFileFormat format, FILE *f, int hDPI, int vDPI); - SplashError writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI); + SplashError writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI, GBool withAlpha = gFalse); void getPixel(int x, int y, SplashColorPtr pixel); Guchar getAlpha(int x, int y); private: + SplashError writeImgDataRGB(ImgWriter *writer); + SplashError writeImgDataRGBA(ImgWriter *writer); + int width, height; // size of bitmap int rowSize; // size of one row of data, in bytes // - negative for bottom-up bitmaps diff --git a/splash/SplashTypes.h b/splash/SplashTypes.h index 993dd46..3e34ea7 100644 --- a/splash/SplashTypes.h +++ b/splash/SplashTypes.h @@ -160,7 +160,8 @@ typedef int SplashError; enum SplashImageFileFormat { splashFormatJpeg, - splashFormatPng + splashFormatPng, + splashFormatPngAlpha }; #endif -- 1.7.0.4
>From 6efd015f0f49da605caf74083bd48cc3e01df353 Mon Sep 17 00:00:00 2001 From: Stefan Thomas <tho...@eload24.com> Date: Fri, 9 Jul 2010 23:46:36 +0100 Subject: [PATCH 2/2] Fixes bug where libjpeg would create corrupted/incomplete images. This bug occurred when libjpeg tried to access temporary memory which had already been freed before ImgWriter->close() was called. --- splash/SplashBitmap.cc | 21 ++++++++++++++++----- 1 files changed, 16 insertions(+), 5 deletions(-) diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc index 84d8666..e0fa788 100644 --- a/splash/SplashBitmap.cc +++ b/splash/SplashBitmap.cc @@ -335,17 +335,13 @@ SplashError SplashBitmap::writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int return splashErrGeneric; } - if (withAlpha) { + if (!withAlpha) { e = writeImgDataRGB(writer); } else { e = writeImgDataRGBA(writer); } if (e) return e; - - if (writer->close()) { - return splashErrGeneric; - } return splashOk; } @@ -367,6 +363,9 @@ SplashError SplashBitmap::writeImgDataRGB(ImgWriter *writer) delete[] row_pointers; return splashErrGeneric; } + if (!writer->close()) { + return splashErrGeneric; + } delete[] row_pointers; } break; @@ -387,6 +386,9 @@ SplashError SplashBitmap::writeImgDataRGB(ImgWriter *writer) return splashErrGeneric; } } + if (!writer->close()) { + return splashErrGeneric; + } delete[] row; } break; @@ -407,6 +409,9 @@ SplashError SplashBitmap::writeImgDataRGB(ImgWriter *writer) return splashErrGeneric; } } + if (!writer->close()) { + return splashErrGeneric; + } delete[] row; } break; @@ -427,6 +432,9 @@ SplashError SplashBitmap::writeImgDataRGB(ImgWriter *writer) return splashErrGeneric; } } + if (!writer->close()) { + return splashErrGeneric; + } delete[] row; } break; @@ -523,5 +531,8 @@ SplashError SplashBitmap::writeImgDataRGBA(ImgWriter *writer) break; } + if (!writer->close()) { + return splashErrGeneric; + } delete[] row; } -- 1.7.0.4
_______________________________________________ poppler mailing list poppler@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/poppler