[Libreoffice-commits] core.git: vcl/inc vcl/source

Armin Le Grand (CIB) Armin.Le.Grand at cib.de
Mon Apr 2 09:35:10 UTC 2018


 vcl/inc/printdlg.hxx           |    6 -
 vcl/source/window/printdlg.cxx |  168 +++++++++++++++++++++++++++++------------
 2 files changed, 123 insertions(+), 51 deletions(-)

New commits:
commit d51bc7b3813ccbdf41a50664d41b2fa3d25c6954
Author: Armin Le Grand <Armin.Le.Grand at cib.de (CIB)>
Date:   Fri Mar 30 13:02:02 2018 +0200

    PrintPreviewWindow dynamic preview Bitmap
    
    The PrintDialog includes a preview (PrintPreviewWindow)
    that currently used a fixed-pixel-size preview that takes
    the aspect ratio into account (which may already explode
    due to just growing vertically - try a much higher than
    wide page). It is not dynamic and by using the high-quality
    scale (BmpScaleFlag::BestQuality) for output it can get
    rather slow in repainting. Also holds the VDev for creating
    the preview all the time without need.
    Made that process dynamic and responding to real resizing,
    thus not using that huge number of pixels from the start.
    Also use a VDev only temporary and use intelligent size
    management.
    Still keeping the high-quality scale due to ::DrawBitmap
    with a target size not scaling well (still) and also
    ::DrawOutDev not good in quality. Those two should work
    better nowadays, but adaption would be risky.
    
    Change-Id: I211412a063def33a4e8f40c7442702770cd11a8e
    Reviewed-on: https://gerrit.libreoffice.org/52150
    Tested-by: Jenkins <ci at libreoffice.org>
    Reviewed-by: Armin Le Grand <Armin.Le.Grand at cib.de>

diff --git a/vcl/inc/printdlg.hxx b/vcl/inc/printdlg.hxx
index 2371eaa5469b..f8c799e7ff97 100644
--- a/vcl/inc/printdlg.hxx
+++ b/vcl/inc/printdlg.hxx
@@ -39,12 +39,11 @@ namespace vcl
     public:
         class PrintPreviewWindow : public vcl::Window
         {
-            static const sal_Int32 PREVIEW_BITMAP_WIDTH;
-
             GDIMetaFile         maMtf;
             Size                maOrigSize;
             Size                maPreviewSize;
-            VclPtr<VirtualDevice> maPageVDev;
+            sal_Int32           mnDPIX;
+            sal_Int32           mnDPIY;
             Bitmap              maPreviewBitmap;
             OUString            maReplacementString;
             OUString            maToolTipString;
@@ -62,7 +61,6 @@ namespace vcl
             virtual void Paint( vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect ) override;
             virtual void Command( const CommandEvent& ) override;
             virtual void Resize() override;
-            virtual void DataChanged( const DataChangedEvent& ) override;
 
             void setPreview( const GDIMetaFile&, const Size& i_rPaperSize,
                              const OUString& i_rPaperName,
diff --git a/vcl/source/window/printdlg.cxx b/vcl/source/window/printdlg.cxx
index 5c10cc161d0b..4fba6f5e5d6b 100644
--- a/vcl/source/window/printdlg.cxx
+++ b/vcl/source/window/printdlg.cxx
@@ -66,8 +66,13 @@ extern "C" SAL_DLLPUBLIC_EXPORT void makeShowNupOrderWindow(VclPtr<vcl::Window>
 
 PrintDialog::PrintPreviewWindow::PrintPreviewWindow( vcl::Window* i_pParent )
     : Window( i_pParent, 0 )
+    , maMtf()
     , maOrigSize( 10, 10 )
-    , maPageVDev( VclPtr<VirtualDevice>::Create(*this) )
+    , maPreviewSize()
+    , mnDPIX(Application::GetDefaultDevice()->GetDPIX())
+    , mnDPIY(Application::GetDefaultDevice()->GetDPIY())
+    , maPreviewBitmap()
+    , maReplacementString()
     , maToolTipString(VclResId( SV_PRINT_PRINTPREVIEW_TXT))
     , mbGreyscale( false )
     , maHorzDim(VclPtr<FixedLine>::Create(this, WB_HORZ | WB_CENTER))
@@ -75,7 +80,6 @@ PrintDialog::PrintPreviewWindow::PrintPreviewWindow( vcl::Window* i_pParent )
 {
     SetPaintTransparent( true );
     SetBackground();
-    maPageVDev->SetBackground( Wallpaper(COL_WHITE) );
     maHorzDim->Show();
     maVertDim->Show();
 
@@ -92,22 +96,9 @@ void PrintDialog::PrintPreviewWindow::dispose()
 {
     maHorzDim.disposeAndClear();
     maVertDim.disposeAndClear();
-    maPageVDev.disposeAndClear();
     Window::dispose();
 }
 
-const sal_Int32 PrintDialog::PrintPreviewWindow::PREVIEW_BITMAP_WIDTH = 1600;
-
-void PrintDialog::PrintPreviewWindow::DataChanged( const DataChangedEvent& i_rDCEvt )
-{
-    // react on settings changed
-    if( i_rDCEvt.GetType() == DataChangedEventType::SETTINGS )
-    {
-        maPageVDev->SetBackground( Wallpaper(COL_WHITE) );
-    }
-    Window::DataChanged( i_rDCEvt );
-}
-
 void PrintDialog::PrintPreviewWindow::Resize()
 {
     Size aNewSize( GetSizePixel() );
@@ -141,19 +132,6 @@ void PrintDialog::PrintPreviewWindow::Resize()
 
     maPreviewSize = aScaledSize;
 
-    // #i104784# if we render the page too small then rounding issues result in
-    // layout artifacts looking really bad. So scale the page unto a device that is not
-    // full page size but not too small either. This also results in much better visual
-    // quality of the preview, e.g. when its height approaches the number of text lines
-    // find a good scaling factor
-
-    double aAspectRatio = aScaledSize.Height() / static_cast<double>(aScaledSize.Width());
-
-    aScaledSize.setWidth( PREVIEW_BITMAP_WIDTH );
-    aScaledSize.setHeight( PREVIEW_BITMAP_WIDTH * aAspectRatio );
-
-    maPageVDev->SetOutputSizePixel( aScaledSize, false );
-
     // position dimension lines
     Point aRef( nTextHeight + (aNewSize.Width() - maPreviewSize.Width())/2,
                 nTextHeight + (aNewSize.Height() - maPreviewSize.Height())/2 );
@@ -162,6 +140,8 @@ void PrintDialog::PrintPreviewWindow::Resize()
     maVertDim->SetPosSizePixel( Point( aRef.X() - nTextHeight, aRef.Y() ),
                                Size( nTextHeight, maPreviewSize.Height() ) );
 
+    // check and evtl. recreate preview bitmap
+    preparePreviewBitmap();
 }
 
 void PrintDialog::PrintPreviewWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
@@ -186,6 +166,11 @@ void PrintDialog::PrintPreviewWindow::Paint(vcl::RenderContext& rRenderContext,
     else
     {
         Bitmap aPreviewBitmap(maPreviewBitmap);
+
+        // This explicit force-to-scale allows us to get the
+        // mentioned best quality here. Unfortunately this is
+        // currently not sure when using just ::DrawBitmap with
+        // a defined size or ::DrawOutDev
         aPreviewBitmap.Scale(maPreviewSize, BmpScaleFlag::BestQuality);
         rRenderContext.DrawBitmap(aOffset, aPreviewBitmap);
     }
@@ -224,12 +209,11 @@ void PrintDialog::PrintPreviewWindow::setPreview( const GDIMetaFile& i_rNewPrevi
     aBuf.append( maToolTipString );
     SetQuickHelpText( aBuf.makeStringAndClear() );
     maMtf = i_rNewPreview;
-
+    mnDPIX = i_nDPIX;
+    mnDPIY = i_nDPIY;
     maOrigSize = i_rOrigSize;
     maReplacementString = i_rReplacement;
     mbGreyscale = i_bGreyscale;
-    maPageVDev->SetReferenceDevice( i_nDPIX, i_nDPIY );
-    maPageVDev->EnableOutput();
 
     // use correct measurements
     const LocaleDataWrapper& rLocWrap( GetSettings().GetLocaleDataWrapper() );
@@ -259,17 +243,107 @@ void PrintDialog::PrintPreviewWindow::setPreview( const GDIMetaFile& i_rNewPrevi
     aBuf.appendAscii( eUnit == MapUnit::MapMM ? "mm" : "in" );
     maVertDim->SetText( aBuf.makeStringAndClear() );
 
+    // sets/calculates e.g. maPreviewSize
+    // also triggers preparePreviewBitmap()
     Resize();
-    preparePreviewBitmap();
+
     Invalidate();
 }
 
 void PrintDialog::PrintPreviewWindow::preparePreviewBitmap()
 {
+    if(maPreviewSize.getWidth() < 0 || maPreviewSize.getHeight() < 0)
+    {
+        // not yet fully initialized, no need to prepare anything
+        return;
+    }
+
+    // define an allowed number of pixels, also see
+    // defaults for primitive renderers and similar. This
+    // might be centralized and made dependent of 32/64bit
+    const sal_uInt32 nMaxSquarePixels(500000);
+
+    // check how big (squarePixels) the preview is currently (with
+    // max value of MaxSquarePixels)
+    const sal_uInt32 nCurrentSquarePixels(
+        std::min(
+            nMaxSquarePixels,
+            static_cast<sal_uInt32>(maPreviewBitmap.GetSizePixel().getWidth())
+            * static_cast<sal_uInt32>(maPreviewBitmap.GetSizePixel().getHeight())));
+
+    // check how big (squarePixels) the preview needs to be (with
+    // max value of MaxSquarePixels)
+    const sal_uInt32 nRequiredSquarePixels(
+        std::min(
+            nMaxSquarePixels,
+            static_cast<sal_uInt32>(maPreviewSize.getWidth())
+            * static_cast<sal_uInt32>(maPreviewSize.getHeight())));
+
+    // check if preview is big enough. Use a scaling value in
+    // the comparison to not get bigger at the last possible moment
+    // what may look awkward and pixelated (again). This means
+    // to use a percentage value - if we have at least
+    // that value of required pixels, we are good.
+    static double fPreventAwkwardFactor(1.35); // 35%
+    if(nCurrentSquarePixels >= static_cast<sal_uInt32>(nRequiredSquarePixels * fPreventAwkwardFactor))
+    {
+        // at this place we also could add a mechanism to let the preview
+        // bitmap 'shrink' again if it is currently 'too big' -> bigger
+        // than required. I think this is not necessary for now.
+
+        // already sufficient, done.
+        return;
+    }
+
+    // check if we have enough square pixels e.g for 8x8 pixels
+    if(nRequiredSquarePixels < 64)
+    {
+        // too small preview - let it empty
+        return;
+    }
+
+    // Calculate nPlannedSquarePixels which is the required size
+    // expanded by a percentage (with max value of MaxSquarePixels)
+    static double fExtraSpaceFactor(1.65); // 65%
+    const sal_uInt32 nPlannedSquarePixels(
+        std::min(
+            nMaxSquarePixels,
+            static_cast<sal_uInt32>(maPreviewSize.getWidth() * fExtraSpaceFactor)
+            * static_cast<sal_uInt32>(maPreviewSize.getHeight() * fExtraSpaceFactor)));
+
+    // calculate back new width and height - it might have been
+    // truncated by MaxSquarePixels.
+    // We know that w*h == nPlannedSquarePixels and w/h == ratio
+    const double fRatio(static_cast<double>(maPreviewSize.getWidth()) / static_cast<double>(maPreviewSize.getHeight()));
+    const double fNewWidth(sqrt(static_cast<double>(nPlannedSquarePixels) * fRatio));
+    const double fNewHeight(sqrt(static_cast<double>(nPlannedSquarePixels) / fRatio));
+    const Size aScaledSize(basegfx::fround(fNewWidth), basegfx::fround(fNewHeight));
+
+    // check if this eventual maximum is already reached
+    // due to having hit the MaxSquarePixels. Due to using
+    // an integer AspectRatio, we cannot make a numeric exact
+    // comparison - we need to compare if we are close
+    const double fScaledSizeSquare(static_cast<double>(aScaledSize.getWidth() * aScaledSize.getHeight()));
+    const double fPreviewSizeSquare(static_cast<double>(maPreviewBitmap.GetSizePixel().getWidth() * maPreviewBitmap.GetSizePixel().getHeight()));
+
+    // test as equal up to 0.1% (0.001)
+    if(fabs((fScaledSizeSquare / fPreviewSizeSquare) - 1.0) < 0.001)
+    {
+        // maximum is reached, avoid bigger scaling
+        return;
+    }
+
+    // create temporary VDev and render to it
+    ScopedVclPtrInstance<VirtualDevice> pPrerenderVDev(*Application::GetDefaultDevice());
+    pPrerenderVDev->SetOutputSizePixel(aScaledSize, false);
+    pPrerenderVDev->SetReferenceDevice( mnDPIX, mnDPIY );
+    pPrerenderVDev->EnableOutput();
+    pPrerenderVDev->SetBackground( Wallpaper(COL_WHITE) );
+
     GDIMetaFile aMtf( maMtf );
 
-    Size aVDevSize( maPageVDev->GetOutputSizePixel() );
-    const Size aLogicSize( maPageVDev->PixelToLogic( aVDevSize, MapMode( MapUnit::Map100thMM ) ) );
+    Size aVDevSize( pPrerenderVDev->GetOutputSizePixel() );
+    const Size aLogicSize( pPrerenderVDev->PixelToLogic( aVDevSize, MapMode( MapUnit::Map100thMM ) ) );
     Size aOrigSize( maOrigSize );
     if( aOrigSize.Width() < 1 )
         aOrigSize.setWidth( aLogicSize.Width() );
@@ -277,31 +351,31 @@ void PrintDialog::PrintPreviewWindow::preparePreviewBitmap()
         aOrigSize.setHeight( aLogicSize.Height() );
     double fScale = double(aLogicSize.Width())/double(aOrigSize.Width());
 
-    maPageVDev->Erase();
-    maPageVDev->Push();
-    maPageVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
-    DrawModeFlags nOldDrawMode = maPageVDev->GetDrawMode();
+    pPrerenderVDev->Erase();
+    pPrerenderVDev->Push();
+    pPrerenderVDev->SetMapMode(MapMode(MapUnit::Map100thMM));
+    DrawModeFlags nOldDrawMode = pPrerenderVDev->GetDrawMode();
     if( mbGreyscale )
-        maPageVDev->SetDrawMode( maPageVDev->GetDrawMode() |
+        pPrerenderVDev->SetDrawMode( pPrerenderVDev->GetDrawMode() |
                                 ( DrawModeFlags::GrayLine | DrawModeFlags::GrayFill | DrawModeFlags::GrayText |
                                   DrawModeFlags::GrayBitmap | DrawModeFlags::GrayGradient ) );
     aMtf.WindStart();
     aMtf.Scale( fScale, fScale );
     aMtf.WindStart();
 
-    const AntialiasingFlags nOriginalAA(maPageVDev->GetAntialiasing());
-    maPageVDev->SetAntialiasing(nOriginalAA | AntialiasingFlags::EnableB2dDraw);
-    aMtf.Play( maPageVDev.get(), Point( 0, 0 ), aLogicSize );
-    maPageVDev->SetAntialiasing(nOriginalAA);
+    const AntialiasingFlags nOriginalAA(pPrerenderVDev->GetAntialiasing());
+    pPrerenderVDev->SetAntialiasing(nOriginalAA | AntialiasingFlags::EnableB2dDraw);
+    aMtf.Play( pPrerenderVDev.get(), Point( 0, 0 ), aLogicSize );
+    pPrerenderVDev->SetAntialiasing(nOriginalAA);
 
-    maPageVDev->Pop();
+    pPrerenderVDev->Pop();
 
     SetMapMode(MapMode(MapUnit::MapPixel));
-    maPageVDev->SetMapMode(MapMode(MapUnit::MapPixel));
+    pPrerenderVDev->SetMapMode(MapMode(MapUnit::MapPixel));
 
-    maPreviewBitmap = maPageVDev->GetBitmap(Point(0, 0), aVDevSize);
+    maPreviewBitmap = pPrerenderVDev->GetBitmap(Point(0, 0), aVDevSize);
 
-    maPageVDev->SetDrawMode( nOldDrawMode );
+    pPrerenderVDev->SetDrawMode( nOldDrawMode );
 }
 
 PrintDialog::ShowNupOrderWindow::ShowNupOrderWindow( vcl::Window* i_pParent )


More information about the Libreoffice-commits mailing list