Hello, Am 04.07.2015 um 02:09 schrieb Adam Reichold: >> But i guess feeding that to QImage won't be nice since it basically means >> reconstructing a XRGB8 buffer with the RGB8 buffer and the alpha channel, is >> that right? > > Exactly, you still get the RGB8 and alpah channel buffers from Splash, > but you really have to convert RGB8 to XBGR8 again to make use of the > alpha channel so it seems more sensible to render within XBGR8 from the > beginning if you intend you have an alpha channel available to QImage in > the end. (As just inserting the alpha channel into XBGR8 images is > almost as slow as RGB8 rendering, I cannot imagine rendering RGB8 and > then transfering this into a ARGB32 QImage using the additional alpha > channel being any faster than rendering XBGR8 in the first place?)
After further testing, I think the case with an intact alpha channel can be improved further by modifying the alpha channel in-place within Splash's pipeline instead copying it only afterwards. The additional writes won't be noticed by the current consumers since they will now render RGB8 instead of XBGR8. But for consumers who request an intact alpha channel the attached version of the patch removes the overhead of the additional bulk copy. Concerning correctness, I have not yet found an input where the in-place generated alpha channel was different from what we would have copied afterwards. Of course, I would still be glad if someone with more experience with Splash's internals could confirm that I changed all the necessary format-dependent destination writes. In any case, the effect this has will be limited to consumers who render XBGR8 and do not supply a paper colour. Best regards, Adam.
From d3c6dc18206aa7ac3853b5bb8ceeefc72883928a Mon Sep 17 00:00:00 2001 From: Adam Reichold <[email protected]> Date: Thu, 2 Jul 2015 21:00:16 +0200 Subject: [PATCH 1/6] Make SplashBitmap XBGR transfer alpha channel Adds an option to SplashBitmap::convertToXBGR and SplashBitmap::getXBGRLine so that both optionally transfer Splash's internal alpha channel into the fourth component of the resulting bitmap data. --- splash/SplashBitmap.cc | 27 ++++++++++++++++++++++----- splash/SplashBitmap.h | 4 ++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc index e886683..7da373b 100644 --- a/splash/SplashBitmap.cc +++ b/splash/SplashBitmap.cc @@ -460,7 +460,7 @@ void SplashBitmap::getRGBLine(int yl, SplashColorPtr line) { } } -void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line) { +void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line, bool useAlpha) { SplashColor col; double c, m, y, k, c1, m1, y1, k1, r, g, b; @@ -503,20 +503,37 @@ void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line) { *line++ = dblToByte(clip01(b)); *line++ = dblToByte(clip01(g)); *line++ = dblToByte(clip01(r)); - *line++ = 255; + *line++ = useAlpha ? getAlpha(x, yl) : 255; } } -GBool SplashBitmap::convertToXBGR() { - if (mode == splashModeXBGR8) +GBool SplashBitmap::convertToXBGR(bool useAlpha) { + if (mode == splashModeXBGR8) { + if (useAlpha) { + // Copy the alpha channel into the fourth component so that XBGR becomes ABGR. + const SplashColorPtr dbegin = data; + const SplashColorPtr dend = data + rowSize * height; + + Guchar *const abegin = alpha; + Guchar *const aend = alpha + width * height; + + SplashColorPtr d = dbegin + 3; + Guchar *a = abegin; + + for(; d < dend && a < aend; d += 4, a += 1) { + *d = *a; + } + } + return gTrue; + } int newrowSize = width * 4; SplashColorPtr newdata = (SplashColorPtr)gmallocn_checkoverflow(newrowSize, height); if (newdata != NULL) { for (int y = 0; y < height; y++) { unsigned char *row = newdata + y * newrowSize; - getXBGRLine(y, row); + getXBGRLine(y, row, useAlpha); } if (rowSize < 0) { gfree(data + (height - 1) * rowSize); diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h index 70509ab..99879fb 100644 --- a/splash/SplashBitmap.h +++ b/splash/SplashBitmap.h @@ -75,11 +75,11 @@ public: SplashError writeImgFile(SplashImageFileFormat format, FILE *f, int hDPI, int vDPI, const char *compressionString = ""); SplashError writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI); - GBool convertToXBGR(); + GBool convertToXBGR(bool useAlpha = false); void getPixel(int x, int y, SplashColorPtr pixel); void getRGBLine(int y, SplashColorPtr line); - void getXBGRLine(int y, SplashColorPtr line); + void getXBGRLine(int y, SplashColorPtr line, bool useAlpha = false); #if SPLASH_CMYK void getCMYKLine(int y, SplashColorPtr line); #endif -- 2.4.5 From 08a73cf4c03ef42770c819b6777c18bb046f4aeb Mon Sep 17 00:00:00 2001 From: Adam Reichold <[email protected]> Date: Thu, 2 Jul 2015 21:02:59 +0200 Subject: [PATCH 2/6] Add KeepAlphaChannel render flag to Qt frontends Adds a new render flag which will indicate that the image return by Poppler::Page::renderToImage will not be opaque and alpha blended with the paper colour, but retain its actually background transparency. This improves performance in synthentic rendering benchmarks by almost five percent and the additional alpha blending that is then done by the consuming application is often a hardware-accellerated operation. --- qt4/src/poppler-page.cc | 6 ++++-- qt4/src/poppler-qt4.h | 3 ++- qt5/src/poppler-page.cc | 6 ++++-- qt5/src/poppler-qt5.h | 3 ++- 4 files changed, 12 insertions(+), 6 deletions(-) diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc index 49ad871..55abf8a 100644 --- a/qt4/src/poppler-page.cc +++ b/qt4/src/poppler-page.cc @@ -331,13 +331,15 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, if (m_page->parentDoc->m_hints & Document::ThinLineShape) thinLineMode = splashThinLineShape; if (m_page->parentDoc->m_hints & Document::ThinLineSolid) thinLineMode = splashThinLineSolid; + const bool keepAlphaChannel = m_page->parentDoc->m_hints & Document::KeepAlphaChannel; + SplashOutputDev * splash_output = new SplashOutputDev( #if defined(SPLASH_CMYK) (overprint) ? splashModeDeviceN8 : splashModeXBGR8, #else splashModeXBGR8, #endif - 4, gFalse, bgColor, gTrue, thinLineMode, overprint); + 4, gFalse, keepAlphaChannel ? NULL : bgColor, gTrue, thinLineMode, overprint); splash_output->setFontAntialias(m_page->parentDoc->m_hints & Document::TextAntialiasing ? gTrue : gFalse); splash_output->setVectorAntialias(m_page->parentDoc->m_hints & Document::Antialiasing ? gTrue : gFalse); @@ -354,7 +356,7 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, int bw = bitmap->getWidth(); int bh = bitmap->getHeight(); - if (bitmap->convertToXBGR()) + if (bitmap->convertToXBGR(keepAlphaChannel)) { SplashColorPtr dataPtr = bitmap->getDataPtr(); diff --git a/qt4/src/poppler-qt4.h b/qt4/src/poppler-qt4.h index e5e808d..b4494e3 100644 --- a/qt4/src/poppler-qt4.h +++ b/qt4/src/poppler-qt4.h @@ -901,7 +901,8 @@ delete it; TextSlightHinting = 0x00000008, ///< Lighter hinting for text when combined with TextHinting \since 0.18 OverprintPreview = 0x00000010, ///< Overprint preview \since 0.22 ThinLineSolid = 0x00000020, ///< Enhance thin lines solid \since 0.24 - ThinLineShape = 0x00000040 ///< Enhance thin lines shape. Wins over ThinLineSolid \since 0.24 + ThinLineShape = 0x00000040, ///< Enhance thin lines shape. Wins over ThinLineSolid \since 0.24 + KeepAlphaChannel = 0x00000080 ///< Do not compose with the paper color \since 0.34 }; Q_DECLARE_FLAGS( RenderHints, RenderHint ) diff --git a/qt5/src/poppler-page.cc b/qt5/src/poppler-page.cc index 408099c..07ad9b6 100644 --- a/qt5/src/poppler-page.cc +++ b/qt5/src/poppler-page.cc @@ -331,13 +331,15 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, if (m_page->parentDoc->m_hints & Document::ThinLineShape) thinLineMode = splashThinLineShape; if (m_page->parentDoc->m_hints & Document::ThinLineSolid) thinLineMode = splashThinLineSolid; + const bool keepAlphaChannel = m_page->parentDoc->m_hints & Document::KeepAlphaChannel; + SplashOutputDev * splash_output = new SplashOutputDev( #if defined(SPLASH_CMYK) (overprint) ? splashModeDeviceN8 : splashModeXBGR8, #else splashModeXBGR8, #endif - 4, gFalse, bgColor, gTrue, thinLineMode, overprint); + 4, gFalse, keepAlphaChannel ? NULL : bgColor, gTrue, thinLineMode, overprint); splash_output->setFontAntialias(m_page->parentDoc->m_hints & Document::TextAntialiasing ? gTrue : gFalse); splash_output->setVectorAntialias(m_page->parentDoc->m_hints & Document::Antialiasing ? gTrue : gFalse); @@ -354,7 +356,7 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, int bw = bitmap->getWidth(); int bh = bitmap->getHeight(); - if (bitmap->convertToXBGR()) + if (bitmap->convertToXBGR(keepAlphaChannel)) { SplashColorPtr dataPtr = bitmap->getDataPtr(); diff --git a/qt5/src/poppler-qt5.h b/qt5/src/poppler-qt5.h index 79c3f74..fa23844 100644 --- a/qt5/src/poppler-qt5.h +++ b/qt5/src/poppler-qt5.h @@ -891,7 +891,8 @@ delete it; TextSlightHinting = 0x00000008, ///< Lighter hinting for text when combined with TextHinting \since 0.18 OverprintPreview = 0x00000010, ///< Overprint preview \since 0.22 ThinLineSolid = 0x00000020, ///< Enhance thin lines solid \since 0.24 - ThinLineShape = 0x00000040 ///< Enhance thin lines shape. Wins over ThinLineSolid \since 0.24 + ThinLineShape = 0x00000040, ///< Enhance thin lines shape. Wins over ThinLineSolid \since 0.24 + KeepAlphaChannel = 0x00000080 ///< Do not compose with the paper color \since 0.34 }; Q_DECLARE_FLAGS( RenderHints, RenderHint ) -- 2.4.5 From 5043e146de24312fb0cd9b46cc5d061fb8e990ae Mon Sep 17 00:00:00 2001 From: Adam Reichold <[email protected]> Date: Thu, 2 Jul 2015 21:08:38 +0200 Subject: [PATCH 3/6] Improve efficiency of Poppler::Page::renderToImage Improves the efficiency of rendering into a QImage using the Splash output device by removing a copy of the raw bitmap data since Qt5 will properly free this data using a function supplied during construction, i.e. in this case gfree. This improves performance in synthentic rendering benchmarks by approximately four percent and reduces the maximum working set size. --- qt5/src/poppler-page.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qt5/src/poppler-page.cc b/qt5/src/poppler-page.cc index 07ad9b6..d2fa3dd 100644 --- a/qt5/src/poppler-page.cc +++ b/qt5/src/poppler-page.cc @@ -358,7 +358,7 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, if (bitmap->convertToXBGR(keepAlphaChannel)) { - SplashColorPtr dataPtr = bitmap->getDataPtr(); + SplashColorPtr dataPtr = bitmap->takeData(); if (QSysInfo::BigEndian == QSysInfo::ByteOrder) { @@ -376,9 +376,8 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, } } - // construct a qimage SHARING the raw bitmap data in memory - QImage tmpimg( dataPtr, bw, bh, QImage::Format_ARGB32 ); - img = tmpimg.copy(); + // Construct a Qt image holding (and also owning) the raw bitmap data. + img = QImage(dataPtr, bw, bh, QImage::Format_ARGB32, gfree, dataPtr); } delete splash_output; #endif -- 2.4.5 From a17778b027c19f7cf2bd59b7b3f53503452127d9 Mon Sep 17 00:00:00 2001 From: Adam Reichold <[email protected]> Date: Fri, 3 Jul 2015 00:31:56 +0200 Subject: [PATCH 4/6] Switch default image format of Qt frontends This changes the Poppler::Page::renderToImage method w.r.t. to the image formats used to render using Splash, i.e. the default will be RGB8. If overprint preview is requested, DeviceN8 will be used and converted to XBGR8 for display. If the internal alpha channel is requested, XBGR8 will be used and augmented by Splash's separate alpha channel. This yields some improvements w.r.t. to rendering including alpha blending with the paper colour, i.e. synthetic benchmarks using structurally simple documents yield more than two percent improvement. --- qt4/src/poppler-page.cc | 115 +++++++++++++++++++++++++++++------------------- qt5/src/poppler-page.cc | 114 ++++++++++++++++++++++++++++------------------- 2 files changed, 138 insertions(+), 91 deletions(-) diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc index 55abf8a..c99048f 100644 --- a/qt4/src/poppler-page.cc +++ b/qt4/src/poppler-page.cc @@ -294,10 +294,10 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, { #if defined(HAVE_SPLASH) SplashColor bgColor; - GBool overprint = gFalse; + GBool overprintPreview = gFalse; #if defined(SPLASH_CMYK) - overprint = m_page->parentDoc->m_hints & Document::OverprintPreview ? gTrue : gFalse; - if (overprint) + overprintPreview = m_page->parentDoc->m_hints & Document::OverprintPreview ? gTrue : gFalse; + if (overprintPreview) { Guchar c, m, y, k; @@ -326,61 +326,84 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, bgColor[1] = m_page->parentDoc->paperColor.green(); bgColor[2] = m_page->parentDoc->paperColor.red(); } + + SplashColorMode colorMode = splashModeRGB8; + + const bool keepAlphaChannel = m_page->parentDoc->m_hints & Document::KeepAlphaChannel; + if (keepAlphaChannel) colorMode = splashModeXBGR8; + +#if defined(SPLASH_CMYK) + if (overprintPreview) colorMode = splashModeDeviceN8; +#endif SplashThinLineMode thinLineMode = splashThinLineDefault; if (m_page->parentDoc->m_hints & Document::ThinLineShape) thinLineMode = splashThinLineShape; if (m_page->parentDoc->m_hints & Document::ThinLineSolid) thinLineMode = splashThinLineSolid; - const bool keepAlphaChannel = m_page->parentDoc->m_hints & Document::KeepAlphaChannel; - - SplashOutputDev * splash_output = new SplashOutputDev( -#if defined(SPLASH_CMYK) - (overprint) ? splashModeDeviceN8 : splashModeXBGR8, -#else - splashModeXBGR8, -#endif - 4, gFalse, keepAlphaChannel ? NULL : bgColor, gTrue, thinLineMode, overprint); - - splash_output->setFontAntialias(m_page->parentDoc->m_hints & Document::TextAntialiasing ? gTrue : gFalse); - splash_output->setVectorAntialias(m_page->parentDoc->m_hints & Document::Antialiasing ? gTrue : gFalse); - splash_output->setFreeTypeHinting(m_page->parentDoc->m_hints & Document::TextHinting ? gTrue : gFalse, + SplashOutputDev splash_output( + colorMode, 4, + gFalse, + keepAlphaChannel ? NULL : bgColor, + gTrue, + thinLineMode, + overprintPreview); + + splash_output.setFontAntialias(m_page->parentDoc->m_hints & Document::TextAntialiasing ? gTrue : gFalse); + splash_output.setVectorAntialias(m_page->parentDoc->m_hints & Document::Antialiasing ? gTrue : gFalse); + splash_output.setFreeTypeHinting(m_page->parentDoc->m_hints & Document::TextHinting ? gTrue : gFalse, m_page->parentDoc->m_hints & Document::TextSlightHinting ? gTrue : gFalse); - splash_output->startDoc(m_page->parentDoc->doc); + splash_output.startDoc(m_page->parentDoc->doc); - m_page->parentDoc->doc->displayPageSlice(splash_output, m_page->index + 1, xres, yres, + m_page->parentDoc->doc->displayPageSlice(&splash_output, m_page->index + 1, xres, yres, rotation, false, true, false, x, y, w, h, NULL, NULL, NULL, NULL, gTrue); - SplashBitmap *bitmap = splash_output->getBitmap(); - int bw = bitmap->getWidth(); - int bh = bitmap->getHeight(); - - if (bitmap->convertToXBGR(keepAlphaChannel)) - { - SplashColorPtr dataPtr = bitmap->getDataPtr(); - - if (QSysInfo::BigEndian == QSysInfo::ByteOrder) - { - uchar c; - int count = bw * bh * 4; - for (int k = 0; k < count; k += 4) - { - c = dataPtr[k]; - dataPtr[k] = dataPtr[k+3]; - dataPtr[k+3] = c; - - c = dataPtr[k+1]; - dataPtr[k+1] = dataPtr[k+2]; - dataPtr[k+2] = c; - } - } - - // construct a qimage SHARING the raw bitmap data in memory - QImage tmpimg( dataPtr, bw, bh, QImage::Format_ARGB32 ); - img = tmpimg.copy(); + SplashBitmap *bitmap = splash_output.getBitmap(); + + const int bw = bitmap->getWidth(); + const int bh = bitmap->getHeight(); + const int brs = bitmap->getRowSize(); + + // If we use DeviceN8, convert to XBGR8. + // If requested, also transfer Splash's internal alpha channel. + if (overprintPreview || keepAlphaChannel) { + if (bitmap->convertToXBGR(keepAlphaChannel)) { + SplashColorPtr data = bitmap->getDataPtr(); + + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + // Convert byte order from RGBX to XBGR. + for (int i = 0; i < bh; ++i) { + for (int j = 0; j < bw; ++j) { + SplashColorPtr pixel = &data[i * brs + j]; + + qSwap(pixel[0], pixel[3]); + qSwap(pixel[1], pixel[2]); + } + } + } + + // Construct a Qt image sharing the raw bitmap data. + img = QImage(data, bw, bh, brs, QImage::Format_ARGB32).copy(); + } + } + else { + SplashColorPtr data = bitmap->getDataPtr(); + + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + // Convert byte order from BGR to RGB. + for (int i = 0; i < bh; ++i) { + for (int j = 0; j < bw; ++j) { + SplashColorPtr pixel = &data[i * brs + j]; + + qSwap(pixel[0], pixel[2]); + } + } + } + + // Construct a Qt image sharing the raw bitmap data. + img = QImage(data, bw, bh, brs, QImage::Format_RGB888).copy(); } - delete splash_output; #endif break; } diff --git a/qt5/src/poppler-page.cc b/qt5/src/poppler-page.cc index d2fa3dd..7ae3c71 100644 --- a/qt5/src/poppler-page.cc +++ b/qt5/src/poppler-page.cc @@ -294,10 +294,10 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, { #if defined(HAVE_SPLASH) SplashColor bgColor; - GBool overprint = gFalse; + GBool overprintPreview = gFalse; #if defined(SPLASH_CMYK) - overprint = m_page->parentDoc->m_hints & Document::OverprintPreview ? gTrue : gFalse; - if (overprint) + overprintPreview = m_page->parentDoc->m_hints & Document::OverprintPreview ? gTrue : gFalse; + if (overprintPreview) { Guchar c, m, y, k; @@ -326,60 +326,84 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, bgColor[1] = m_page->parentDoc->paperColor.green(); bgColor[2] = m_page->parentDoc->paperColor.red(); } + + SplashColorMode colorMode = splashModeRGB8; + const bool keepAlphaChannel = m_page->parentDoc->m_hints & Document::KeepAlphaChannel; + if (keepAlphaChannel) colorMode = splashModeXBGR8; + +#if defined(SPLASH_CMYK) + if (overprintPreview) colorMode = splashModeDeviceN8; +#endif + SplashThinLineMode thinLineMode = splashThinLineDefault; if (m_page->parentDoc->m_hints & Document::ThinLineShape) thinLineMode = splashThinLineShape; if (m_page->parentDoc->m_hints & Document::ThinLineSolid) thinLineMode = splashThinLineSolid; - const bool keepAlphaChannel = m_page->parentDoc->m_hints & Document::KeepAlphaChannel; - - SplashOutputDev * splash_output = new SplashOutputDev( -#if defined(SPLASH_CMYK) - (overprint) ? splashModeDeviceN8 : splashModeXBGR8, -#else - splashModeXBGR8, -#endif - 4, gFalse, keepAlphaChannel ? NULL : bgColor, gTrue, thinLineMode, overprint); - - splash_output->setFontAntialias(m_page->parentDoc->m_hints & Document::TextAntialiasing ? gTrue : gFalse); - splash_output->setVectorAntialias(m_page->parentDoc->m_hints & Document::Antialiasing ? gTrue : gFalse); - splash_output->setFreeTypeHinting(m_page->parentDoc->m_hints & Document::TextHinting ? gTrue : gFalse, + SplashOutputDev splash_output( + colorMode, 4, + gFalse, + keepAlphaChannel ? NULL : bgColor, + gTrue, + thinLineMode, + overprintPreview); + + splash_output.setFontAntialias(m_page->parentDoc->m_hints & Document::TextAntialiasing ? gTrue : gFalse); + splash_output.setVectorAntialias(m_page->parentDoc->m_hints & Document::Antialiasing ? gTrue : gFalse); + splash_output.setFreeTypeHinting(m_page->parentDoc->m_hints & Document::TextHinting ? gTrue : gFalse, m_page->parentDoc->m_hints & Document::TextSlightHinting ? gTrue : gFalse); - splash_output->startDoc(m_page->parentDoc->doc); + splash_output.startDoc(m_page->parentDoc->doc); - m_page->parentDoc->doc->displayPageSlice(splash_output, m_page->index + 1, xres, yres, + m_page->parentDoc->doc->displayPageSlice(&splash_output, m_page->index + 1, xres, yres, rotation, false, true, false, x, y, w, h, NULL, NULL, NULL, NULL, gTrue); - SplashBitmap *bitmap = splash_output->getBitmap(); - int bw = bitmap->getWidth(); - int bh = bitmap->getHeight(); - - if (bitmap->convertToXBGR(keepAlphaChannel)) - { - SplashColorPtr dataPtr = bitmap->takeData(); - - if (QSysInfo::BigEndian == QSysInfo::ByteOrder) - { - uchar c; - int count = bw * bh * 4; - for (int k = 0; k < count; k += 4) - { - c = dataPtr[k]; - dataPtr[k] = dataPtr[k+3]; - dataPtr[k+3] = c; - - c = dataPtr[k+1]; - dataPtr[k+1] = dataPtr[k+2]; - dataPtr[k+2] = c; - } - } - - // Construct a Qt image holding (and also owning) the raw bitmap data. - img = QImage(dataPtr, bw, bh, QImage::Format_ARGB32, gfree, dataPtr); + SplashBitmap *bitmap = splash_output.getBitmap(); + + const int bw = bitmap->getWidth(); + const int bh = bitmap->getHeight(); + const int brs = bitmap->getRowSize(); + + // If we use DeviceN8, convert to XBGR8. + // If requested, also transfer Splash's internal alpha channel. + if (overprintPreview || keepAlphaChannel) { + if (bitmap->convertToXBGR(keepAlphaChannel)) { + SplashColorPtr data = bitmap->takeData(); + + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + // Convert byte order from RGBX to XBGR. + for (int i = 0; i < bh; ++i) { + for (int j = 0; j < bw; ++j) { + SplashColorPtr pixel = &data[i * brs + j]; + + qSwap(pixel[0], pixel[3]); + qSwap(pixel[1], pixel[2]); + } + } + } + + // Construct a Qt image holding (and also owning) the raw bitmap data. + img = QImage(data, bw, bh, brs, QImage::Format_ARGB32, gfree, data); + } + } + else { + SplashColorPtr data = bitmap->takeData(); + + if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { + // Convert byte order from BGR to RGB. + for (int i = 0; i < bh; ++i) { + for (int j = 0; j < bw; ++j) { + SplashColorPtr pixel = &data[i * brs + j]; + + qSwap(pixel[0], pixel[2]); + } + } + } + + // Construct a Qt image holding (and also owning) the raw bitmap data. + img = QImage(data, bw, bh, brs, QImage::Format_RGB888, gfree, data); } - delete splash_output; #endif break; } -- 2.4.5 From 68611362ddf4cd4ce5b73ac596939d6d9df6e562 Mon Sep 17 00:00:00 2001 From: Adam Reichold <[email protected]> Date: Sat, 4 Jul 2015 17:42:10 +0200 Subject: [PATCH 5/6] Add simple rendering benchmark using Qt5 Adds a simple rendering benchmark using the Qt5 frontend together with QtTest's automatic benchmark infrastructure. --- qt5/tests/CMakeLists.txt | 3 +- qt5/tests/bench_render.cpp | 97 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 qt5/tests/bench_render.cpp diff --git a/qt5/tests/CMakeLists.txt b/qt5/tests/CMakeLists.txt index ae0bd6c..9e680d1 100644 --- a/qt5/tests/CMakeLists.txt +++ b/qt5/tests/CMakeLists.txt @@ -24,7 +24,7 @@ macro(QT5_ADD_SIMPLETEST exe source) ${source} ) poppler_add_test(${exe} BUILD_QT5_TESTS ${${test_name}_SOURCES}) - target_link_libraries(${exe} poppler-qt5 ${Qt5Widgets_LIBRARIES}) + target_link_libraries(${exe} poppler-qt5 ${Qt5Widgets_LIBRARIES} ${Qt5Test_LIBRARIES}) if(MSVC) target_link_libraries(${exe} poppler ${poppler_LIBS}) endif(MSVC) @@ -55,6 +55,7 @@ qt5_add_simpletest(stress-poppler-qt5 stress-poppler-qt5.cpp) qt5_add_simpletest(stress-poppler-dir stress-poppler-dir.cpp) qt5_add_simpletest(stress-threads-qt5 stress-threads-qt5.cpp) qt5_add_simpletest(poppler-texts poppler-texts.cpp) +qt5_add_simpletest(bench_render bench_render.cpp) qt5_add_qtest(check_attachments check_attachments.cpp) qt5_add_qtest(check_dateConversion check_dateConversion.cpp) diff --git a/qt5/tests/bench_render.cpp b/qt5/tests/bench_render.cpp new file mode 100644 index 0000000..1fa87a6 --- /dev/null +++ b/qt5/tests/bench_render.cpp @@ -0,0 +1,97 @@ +#include <QtTest/QtTest> + +#include <poppler-qt5.h> + +class BenchRender : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + + void benchRenderToImage_data(); + void benchRenderToImage(); + +}; + +void trivialDebugErrorFunction(const QString&, const QVariant&) +{ +} + +void BenchRender::initTestCase() +{ + Poppler::setDebugErrorFunction(trivialDebugErrorFunction, QVariant()); +} + +void BenchRender::benchRenderToImage_data() +{ + QTest::addColumn< QString >("filePath"); + QTest::addColumn< double >("resolution"); + QTest::addColumn< bool >("antialiasing"); + QTest::addColumn< bool >("textAntialiasing"); + QTest::addColumn< bool >("textHinting"); + QTest::addColumn< bool >("keepAlphaChannel"); + + QTest::newRow("text--72dpi--full_antialias--background") << QString(TESTDATADIR "/benchs/text.pdf") << 72.0 << true << true << true << false; + QTest::newRow("text--144dpi--full_antialias--background") << QString(TESTDATADIR "/benchs/text.pdf") << 144.0 << true << true << true << false; + QTest::newRow("text--72dpi--full_antialias--alpha_channel") << QString(TESTDATADIR "/benchs/text.pdf") << 72.0 << true << true << true << true; + QTest::newRow("text--144dpi--full_antialias--alpha_channel") << QString(TESTDATADIR "/benchs/text.pdf") << 144.0 << true << true << true << true; + + QTest::newRow("maths--72dpi--full_antialias--background") << QString(TESTDATADIR "/benchs/maths.pdf") << 72.0 << true << true << true << false; + QTest::newRow("maths--144dpi--full_antialias--background") << QString(TESTDATADIR "/benchs/maths.pdf") << 144.0 << true << true << true << false; + QTest::newRow("maths--72dpi--full_antialias--alpha_channel") << QString(TESTDATADIR "/benchs/maths.pdf") << 72.0 << true << true << true << true; + QTest::newRow("maths--144dpi--full_antialias--alpha_channel") << QString(TESTDATADIR "/benchs/maths.pdf") << 144.0 << true << true << true << true; + + QTest::newRow("scanned--72dpi--full_antialias--background") << QString(TESTDATADIR "/benchs/scanned.pdf") << 72.0 << true << true << true << false; + QTest::newRow("scanned--144dpi--full_antialias--background") << QString(TESTDATADIR "/benchs/scanned.pdf") << 144.0 << true << true << true << false; + QTest::newRow("scanned--72dpi--full_antialias--alpha_channel") << QString(TESTDATADIR "/benchs/scanned.pdf") << 72.0 << true << true << true << true; + QTest::newRow("scanned--144dpi--full_antialias--alpha_channel") << QString(TESTDATADIR "/benchs/scanned.pdf") << 144.0 << true << true << true << true; +} + +void BenchRender::benchRenderToImage() +{ + QFETCH(QString, filePath); + QFETCH(double, resolution); + QFETCH(bool, antialiasing); + QFETCH(bool, textAntialiasing); + QFETCH(bool, textHinting); + QFETCH(bool, keepAlphaChannel); + + QScopedPointer< Poppler::Document > document(Poppler::Document::load(filePath)); + QVERIFY( document ); + + document->setRenderHint(Poppler::Document::Antialiasing, antialiasing); + document->setRenderHint(Poppler::Document::TextAntialiasing, textAntialiasing); + document->setRenderHint(Poppler::Document::TextHinting, textHinting); + document->setRenderHint(Poppler::Document::KeepAlphaChannel, keepAlphaChannel); + + // Take around ten evenly distributed pages from the document. + const int period = document->numPages() / 10; + + QVector< Poppler::Page* > pages; + + for(int index = 0, count = document->numPages(); index < count; ++index) + { + QScopedPointer< Poppler::Page > page(document->page(index)); + QVERIFY( page ); + + if(index % period == 0) + { + pages.append(page.take()); + } + } + + QBENCHMARK + { + foreach(Poppler::Page* page, pages) + { + page->renderToImage(resolution, resolution); + } + } + + qDeleteAll(pages); +} + +QTEST_MAIN(BenchRender) +#include "bench_render.moc" + -- 2.4.5 From 0a3f523ca0a00035cdd95d7e8f7639be5f488890 Mon Sep 17 00:00:00 2001 From: Adam Reichold <[email protected]> Date: Sun, 5 Jul 2015 10:36:38 +0200 Subject: [PATCH 6/6] Transfer alpha channel within of Splash pipeline Instead of copying the complete alpha channel into ARGB images only after rendering is done, this patch changes it so that the Splash pipeline will always update the alpha values within the ARGB bitmap data immediately. This has no additional cost for consumers who composite with the paper colour since we use RGB8 images for that now and even consumers who do use ARGB images with a non-null paper colour will get the same result since Splash::compositePage will still overwrite the complete alpha channel. Consumers who do request ARGB images with an intact alpha however won't have to pay the cost of copying it again after the fact. --- qt4/src/poppler-page.cc | 2 +- qt5/src/poppler-page.cc | 2 +- splash/Splash.cc | 132 ++++++++++++++++++++++++++---------------------- splash/SplashBitmap.cc | 29 +++-------- splash/SplashBitmap.h | 6 +-- 5 files changed, 82 insertions(+), 89 deletions(-) diff --git a/qt4/src/poppler-page.cc b/qt4/src/poppler-page.cc index c99048f..1591ae9 100644 --- a/qt4/src/poppler-page.cc +++ b/qt4/src/poppler-page.cc @@ -368,7 +368,7 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, // If we use DeviceN8, convert to XBGR8. // If requested, also transfer Splash's internal alpha channel. if (overprintPreview || keepAlphaChannel) { - if (bitmap->convertToXBGR(keepAlphaChannel)) { + if (bitmap->convertToXBGR()) { SplashColorPtr data = bitmap->getDataPtr(); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { diff --git a/qt5/src/poppler-page.cc b/qt5/src/poppler-page.cc index 7ae3c71..ef9441c 100644 --- a/qt5/src/poppler-page.cc +++ b/qt5/src/poppler-page.cc @@ -368,7 +368,7 @@ QImage Page::renderToImage(double xres, double yres, int x, int y, int w, int h, // If we use DeviceN8, convert to XBGR8. // If requested, also transfer Splash's internal alpha channel. if (overprintPreview || keepAlphaChannel) { - if (bitmap->convertToXBGR(keepAlphaChannel)) { + if (bitmap->convertToXBGR()) { SplashColorPtr data = bitmap->takeData(); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { diff --git a/splash/Splash.cc b/splash/Splash.cc index 276cc8f..d788069 100644 --- a/splash/Splash.cc +++ b/splash/Splash.cc @@ -819,7 +819,7 @@ void Splash::pipeRun(SplashPipe *pipe) { *pipe->destColorPtr++ = cResult2; *pipe->destColorPtr++ = cResult1; *pipe->destColorPtr++ = cResult0; - *pipe->destColorPtr++ = 255; + *pipe->destColorPtr++ = aResult; break; case splashModeBGR8: *pipe->destColorPtr++ = cResult2; @@ -1152,7 +1152,7 @@ void Splash::pipeRunAAXBGR8(SplashPipe *pipe) { *pipe->destColorPtr++ = cResult2; *pipe->destColorPtr++ = cResult1; *pipe->destColorPtr++ = cResult0; - *pipe->destColorPtr++ = 255; + *pipe->destColorPtr++ = aResult; *pipe->destAlphaPtr++ = aResult; ++pipe->x; @@ -4256,6 +4256,19 @@ void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, d = d0; } + // process alpha + if (srcAlpha) { + alpha = 0; + for (i = 0; i < xStep; ++i, ++xxa) { + alpha += alphaPixBuf[xxa]; + } + // alpha / xStep * yStep + alpha = (alpha * d) >> 23; + *destAlphaPtr++ = (Guchar)alpha; + } + else + alpha = 255; + switch (srcMode) { case splashModeMono8: @@ -4312,7 +4325,7 @@ void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, *destPtr++ = (Guchar)pix2; *destPtr++ = (Guchar)pix1; *destPtr++ = (Guchar)pix0; - *destPtr++ = (Guchar)255; + *destPtr++ = (Guchar)alpha; break; case splashModeBGR8: @@ -4386,17 +4399,6 @@ void Splash::scaleImageYdXd(SplashImageSource src, void *srcData, default: break; } - - // process alpha - if (srcAlpha) { - alpha = 0; - for (i = 0; i < xStep; ++i, ++xxa) { - alpha += alphaPixBuf[xxa]; - } - // alpha / xStep * yStep - alpha = (alpha * d) >> 23; - *destAlphaPtr++ = (Guchar)alpha; - } } } @@ -4490,6 +4492,17 @@ void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, pix[i] = (pixBuf[x * nComps + i] * d) >> 23; } + // process alpha + if (srcAlpha) { + // alphaPixBuf[] / yStep + alpha = (alphaPixBuf[x] * d) >> 23; + for (i = 0; i < xStep; ++i) { + *destAlphaPtr++ = (Guchar)alpha; + } + } + else + alpha = 255; + // store the pixel switch (srcMode) { case splashModeMono1: // mono1 is not allowed @@ -4511,7 +4524,7 @@ void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)255; + *destPtr++ = (Guchar)alpha; } break; case splashModeBGR8: @@ -4538,15 +4551,6 @@ void Splash::scaleImageYdXu(SplashImageSource src, void *srcData, break; #endif } - - // process alpha - if (srcAlpha) { - // alphaPixBuf[] / yStep - alpha = (alphaPixBuf[x] * d) >> 23; - for (i = 0; i < xStep; ++i) { - *destAlphaPtr++ = (Guchar)alpha; - } - } } } @@ -4636,6 +4640,22 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, pix[i] = (pix[i] * d) >> 23; } + // process alpha + if (srcAlpha) { + alpha = 0; + for (i = 0; i < xStep; ++i, ++xxa) { + alpha += alphaLineBuf[xxa]; + } + // alpha / xStep + alpha = (alpha * d) >> 23; + for (i = 0; i < yStep; ++i) { + destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; + *destAlphaPtr = (Guchar)alpha; + } + } + else + alpha = 255; + // store the pixel switch (srcMode) { case splashModeMono1: // mono1 is not allowed @@ -4660,7 +4680,7 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)255; + *destPtr++ = (Guchar)alpha; } break; case splashModeBGR8: @@ -4690,20 +4710,6 @@ void Splash::scaleImageYuXd(SplashImageSource src, void *srcData, break; #endif } - - // process alpha - if (srcAlpha) { - alpha = 0; - for (i = 0; i < xStep; ++i, ++xxa) { - alpha += alphaLineBuf[xxa]; - } - // alpha / xStep - alpha = (alpha * d) >> 23; - for (i = 0; i < yStep; ++i) { - destAlphaPtr = destAlphaPtr0 + i * scaledWidth + x; - *destAlphaPtr = (Guchar)alpha; - } - } } destPtr0 += yStep * scaledWidth * nComps; @@ -4781,6 +4787,19 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, pix[i] = lineBuf[x * nComps + i]; } + // process alpha + if (srcAlpha) { + alpha = alphaLineBuf[x]; + for (i = 0; i < yStep; ++i) { + for (j = 0; j < xStep; ++j) { + destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j; + *destAlphaPtr = (Guchar)alpha; + } + } + } + else + alpha = 255; + // store the pixel switch (srcMode) { case splashModeMono1: // mono1 is not allowed @@ -4810,7 +4829,7 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)255; + *destPtr++ = (Guchar)alpha; } } break; @@ -4847,18 +4866,6 @@ void Splash::scaleImageYuXu(SplashImageSource src, void *srcData, break; #endif } - - // process alpha - if (srcAlpha) { - alpha = alphaLineBuf[x]; - for (i = 0; i < yStep; ++i) { - for (j = 0; j < xStep; ++j) { - destAlphaPtr = destAlphaPtr0 + i * scaledWidth + xx + j; - *destAlphaPtr = (Guchar)alpha; - } - } - } - xx += xStep; } @@ -4904,6 +4911,7 @@ void Splash::scaleImageYuXuBilinear(SplashImageSource src, void *srcData, SplashBitmap *dest) { Guchar *srcBuf, *lineBuf1, *lineBuf2, *alphaSrcBuf, *alphaLineBuf1, *alphaLineBuf2; Guint pix[splashMaxColorComps]; + Guint alpha; Guchar *destPtr0, *destPtr, *destAlphaPtr0, *destAlphaPtr; int i; @@ -4961,6 +4969,15 @@ void Splash::scaleImageYuXuBilinear(SplashImageSource src, void *srcData, pix[i] = lineBuf1[x*nComps + i]*(1.0 - yFrac) + lineBuf2[x*nComps + i]*yFrac; } + // process alpha + if (srcAlpha) { + destAlphaPtr = destAlphaPtr0 + y*scaledWidth + x; + alpha = alphaLineBuf1[x]*(1.0 - yFrac) + alphaLineBuf2[x]*yFrac; + *destAlphaPtr = alpha; + } + else + alpha = 255; + // store the pixel destPtr = destPtr0 + (y * scaledWidth + x) * nComps; switch (srcMode) { @@ -4978,7 +4995,7 @@ void Splash::scaleImageYuXuBilinear(SplashImageSource src, void *srcData, *destPtr++ = (Guchar)pix[2]; *destPtr++ = (Guchar)pix[1]; *destPtr++ = (Guchar)pix[0]; - *destPtr++ = (Guchar)255; + *destPtr++ = (Guchar)alpha; break; case splashModeBGR8: *destPtr++ = (Guchar)pix[2]; @@ -4998,12 +5015,6 @@ void Splash::scaleImageYuXuBilinear(SplashImageSource src, void *srcData, break; #endif } - - // process alpha - if (srcAlpha) { - destAlphaPtr = destAlphaPtr0 + y*scaledWidth + x; - *destAlphaPtr = alphaLineBuf1[x]*(1.0 - yFrac) + alphaLineBuf2[x]*yFrac; - } } ySrc += yStep; @@ -5854,8 +5865,7 @@ SplashError Splash::blitTransparent(SplashBitmap *src, int xSrc, int ySrc, *p++ = *sp++; *p++ = *sp++; *p++ = *sp++; - *p++ = 255; - sp++; + *p++ = *sp++; } } break; diff --git a/splash/SplashBitmap.cc b/splash/SplashBitmap.cc index 7da373b..5f65f01 100644 --- a/splash/SplashBitmap.cc +++ b/splash/SplashBitmap.cc @@ -324,7 +324,7 @@ void SplashBitmap::getPixel(int x, int y, SplashColorPtr pixel) { } Guchar SplashBitmap::getAlpha(int x, int y) { - return alpha[y * width + x]; + return alpha ? alpha[y * width + x] : 255; } SplashColorPtr SplashBitmap::takeData() { @@ -460,7 +460,7 @@ void SplashBitmap::getRGBLine(int yl, SplashColorPtr line) { } } -void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line, bool useAlpha) { +void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line) { SplashColor col; double c, m, y, k, c1, m1, y1, k1, r, g, b; @@ -503,37 +503,20 @@ void SplashBitmap::getXBGRLine(int yl, SplashColorPtr line, bool useAlpha) { *line++ = dblToByte(clip01(b)); *line++ = dblToByte(clip01(g)); *line++ = dblToByte(clip01(r)); - *line++ = useAlpha ? getAlpha(x, yl) : 255; + *line++ = getAlpha(x, yl); } } -GBool SplashBitmap::convertToXBGR(bool useAlpha) { - if (mode == splashModeXBGR8) { - if (useAlpha) { - // Copy the alpha channel into the fourth component so that XBGR becomes ABGR. - const SplashColorPtr dbegin = data; - const SplashColorPtr dend = data + rowSize * height; - - Guchar *const abegin = alpha; - Guchar *const aend = alpha + width * height; - - SplashColorPtr d = dbegin + 3; - Guchar *a = abegin; - - for(; d < dend && a < aend; d += 4, a += 1) { - *d = *a; - } - } - +GBool SplashBitmap::convertToXBGR() { + if (mode == splashModeXBGR8) return gTrue; - } int newrowSize = width * 4; SplashColorPtr newdata = (SplashColorPtr)gmallocn_checkoverflow(newrowSize, height); if (newdata != NULL) { for (int y = 0; y < height; y++) { unsigned char *row = newdata + y * newrowSize; - getXBGRLine(y, row, useAlpha); + getXBGRLine(y, row); } if (rowSize < 0) { gfree(data + (height - 1) * rowSize); diff --git a/splash/SplashBitmap.h b/splash/SplashBitmap.h index 99879fb..64a26d2 100644 --- a/splash/SplashBitmap.h +++ b/splash/SplashBitmap.h @@ -17,7 +17,7 @@ // Copyright (C) 2009 Stefan Thomas <[email protected]> // Copyright (C) 2010 Adrian Johnson <[email protected]> // Copyright (C) 2010 Harry Roberts <[email protected]> -// Copyright (C) 2010 Christian Feuersänger <[email protected]> +// Copyright (C) 2010 Christian Feuers�nger <[email protected]> // Copyright (C) 2010 William Bader <[email protected]> // Copyright (C) 2012 Thomas Freitag <[email protected]> // @@ -75,11 +75,11 @@ public: SplashError writeImgFile(SplashImageFileFormat format, FILE *f, int hDPI, int vDPI, const char *compressionString = ""); SplashError writeImgFile(ImgWriter *writer, FILE *f, int hDPI, int vDPI); - GBool convertToXBGR(bool useAlpha = false); + GBool convertToXBGR(); void getPixel(int x, int y, SplashColorPtr pixel); void getRGBLine(int y, SplashColorPtr line); - void getXBGRLine(int y, SplashColorPtr line, bool useAlpha = false); + void getXBGRLine(int y, SplashColorPtr line); #if SPLASH_CMYK void getCMYKLine(int y, SplashColorPtr line); #endif -- 2.4.5
signature.asc
Description: OpenPGP digital signature
_______________________________________________ poppler mailing list [email protected] http://lists.freedesktop.org/mailman/listinfo/poppler
