1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <vcl/vclenum.hxx>
21 #include <vcl/BitmapReadAccess.hxx>
22 #include <vcl/metaact.hxx>
23 #include <vcl/BitmapSimpleColorQuantizationFilter.hxx>
24 #include <vcl/svapp.hxx>
25 
26 #include <DrawDocShell.hxx>
27 #include <sdmod.hxx>
28 #include <sdiocmpt.hxx>
29 #include <vectdlg.hxx>
30 
31 #define VECTORIZE_MAX_EXTENT 512
32 
SdVectorizeDlg(weld::Window * pParent,const Bitmap & rBmp,::sd::DrawDocShell * pDocShell)33 SdVectorizeDlg::SdVectorizeDlg(weld::Window* pParent, const Bitmap& rBmp, ::sd::DrawDocShell* pDocShell)
34     : GenericDialogController(pParent, "modules/sdraw/ui/vectorize.ui", "VectorizeDialog")
35     , m_pDocSh(pDocShell)
36     , aBmp(rBmp)
37     , m_aBmpWin(m_xDialog.get())
38     , m_aMtfWin(m_xDialog.get())
39     , m_xNmLayers(m_xBuilder->weld_spin_button("colors"))
40     , m_xMtReduce(m_xBuilder->weld_metric_spin_button("points", FieldUnit::PIXEL))
41     , m_xFtFillHoles(m_xBuilder->weld_label("tilesft"))
42     , m_xMtFillHoles(m_xBuilder->weld_metric_spin_button("tiles", FieldUnit::PIXEL))
43     , m_xCbFillHoles(m_xBuilder->weld_check_button("fillholes"))
44     , m_xBmpWin(new weld::CustomWeld(*m_xBuilder, "source", m_aBmpWin))
45     , m_xMtfWin(new weld::CustomWeld(*m_xBuilder, "vectorized", m_aMtfWin))
46     , m_xPrgs(m_xBuilder->weld_progress_bar("progressbar"))
47     , m_xBtnOK(m_xBuilder->weld_button("ok"))
48     , m_xBtnPreview(m_xBuilder->weld_button("preview"))
49 {
50     const int nWidth = m_xFtFillHoles->get_approximate_digit_width() * 32;
51     const int nHeight = m_xFtFillHoles->get_text_height() * 16;
52     m_xBmpWin->set_size_request(nWidth, nHeight);
53     m_xMtfWin->set_size_request(nWidth, nHeight);
54 
55     m_xBtnPreview->connect_clicked( LINK( this, SdVectorizeDlg, ClickPreviewHdl ) );
56     m_xBtnOK->connect_clicked( LINK( this, SdVectorizeDlg, ClickOKHdl ) );
57     m_xNmLayers->connect_value_changed( LINK( this, SdVectorizeDlg, ModifyHdl ) );
58     m_xMtReduce->connect_value_changed( LINK( this, SdVectorizeDlg, MetricModifyHdl ) );
59     m_xMtFillHoles->connect_value_changed( LINK( this, SdVectorizeDlg, MetricModifyHdl ) );
60     m_xCbFillHoles->connect_toggled( LINK( this, SdVectorizeDlg, ToggleHdl ) );
61 
62     LoadSettings();
63     InitPreviewBmp();
64 }
65 
~SdVectorizeDlg()66 SdVectorizeDlg::~SdVectorizeDlg()
67 {
68 }
69 
GetRect(const Size & rDispSize,const Size & rBmpSize)70 ::tools::Rectangle SdVectorizeDlg::GetRect( const Size& rDispSize, const Size& rBmpSize )
71 {
72     ::tools::Rectangle aRect;
73 
74     if( rBmpSize.Width() && rBmpSize.Height() && rDispSize.Width() && rDispSize.Height() )
75     {
76         Size         aBmpSize( rBmpSize );
77         const double fGrfWH = static_cast<double>(aBmpSize.Width()) / aBmpSize.Height();
78         const double fWinWH = static_cast<double>(rDispSize.Width()) / rDispSize.Height();
79 
80         if( fGrfWH < fWinWH )
81         {
82             aBmpSize.setWidth( static_cast<tools::Long>( rDispSize.Height() * fGrfWH ) );
83             aBmpSize.setHeight( rDispSize.Height() );
84         }
85         else
86         {
87             aBmpSize.setWidth( rDispSize.Width() );
88             aBmpSize.setHeight( static_cast<tools::Long>( rDispSize.Width() / fGrfWH) );
89         }
90 
91         const Point aBmpPos( ( rDispSize.Width()  - aBmpSize.Width() ) >> 1,
92                              ( rDispSize.Height() - aBmpSize.Height() ) >> 1 );
93 
94         aRect = ::tools::Rectangle( aBmpPos, aBmpSize );
95     }
96 
97     return aRect;
98 }
99 
InitPreviewBmp()100 void SdVectorizeDlg::InitPreviewBmp()
101 {
102     const ::tools::Rectangle aRect( GetRect( m_aBmpWin.GetOutputSizePixel(), aBmp.GetSizePixel() ) );
103 
104     aPreviewBmp = aBmp;
105     aPreviewBmp.Scale( aRect.GetSize() );
106     m_aBmpWin.SetGraphic(BitmapEx(aPreviewBmp));
107 }
108 
GetPreparedBitmap(Bitmap const & rBmp,Fraction & rScale)109 Bitmap SdVectorizeDlg::GetPreparedBitmap( Bitmap const & rBmp, Fraction& rScale )
110 {
111     Bitmap      aNew( rBmp );
112     const Size  aSizePix( aNew.GetSizePixel() );
113 
114     if( aSizePix.Width() > VECTORIZE_MAX_EXTENT || aSizePix.Height() > VECTORIZE_MAX_EXTENT )
115     {
116         const ::tools::Rectangle aRect( GetRect( Size( VECTORIZE_MAX_EXTENT, VECTORIZE_MAX_EXTENT ), aSizePix ) );
117         rScale = Fraction( aSizePix.Width(), aRect.GetWidth() );
118         aNew.Scale( aRect.GetSize() );
119     }
120     else
121         rScale = Fraction( 1, 1 );
122 
123     BitmapEx aNewBmpEx(aNew);
124     BitmapFilter::Filter(aNewBmpEx, BitmapSimpleColorQuantizationFilter(m_xNmLayers->get_value()));
125     aNew = aNewBmpEx.GetBitmap();
126 
127     return aNew;
128 }
129 
Calculate(Bitmap const & rBmp,GDIMetaFile & rMtf)130 void SdVectorizeDlg::Calculate( Bitmap const & rBmp, GDIMetaFile& rMtf )
131 {
132     m_pDocSh->SetWaitCursor( true );
133     m_xPrgs->set_percentage(0);
134 
135     Fraction    aScale;
136     Bitmap      aTmp( GetPreparedBitmap( rBmp, aScale ) );
137 
138     if( !aTmp.IsEmpty() )
139     {
140         const Link<::tools::Long,void> aPrgsHdl( LINK( this, SdVectorizeDlg, ProgressHdl ) );
141         aTmp.Vectorize( rMtf, static_cast<sal_uInt8>(m_xMtReduce->get_value(FieldUnit::NONE)), &aPrgsHdl );
142 
143         if (m_xCbFillHoles->get_active())
144         {
145             GDIMetaFile                 aNewMtf;
146             Bitmap::ScopedReadAccess    pRAcc(aTmp);
147 
148             if( pRAcc )
149             {
150                 const tools::Long      nWidth = pRAcc->Width();
151                 const tools::Long      nHeight = pRAcc->Height();
152                 const tools::Long      nTileX = m_xMtFillHoles->get_value(FieldUnit::NONE);
153                 const tools::Long      nTileY = m_xMtFillHoles->get_value(FieldUnit::NONE);
154                 assert(nTileX && "div-by-zero");
155                 const tools::Long      nCountX = nWidth / nTileX;
156                 assert(nTileY && "div-by-zero");
157                 const tools::Long      nCountY = nHeight / nTileY;
158                 const tools::Long      nRestX = nWidth % nTileX;
159                 const tools::Long      nRestY = nHeight % nTileY;
160 
161                 MapMode aMap( rMtf.GetPrefMapMode() );
162                 aNewMtf.SetPrefSize( rMtf.GetPrefSize() );
163                 aNewMtf.SetPrefMapMode( aMap );
164 
165                 for( tools::Long nTY = 0; nTY < nCountY; nTY++ )
166                 {
167                     const tools::Long nY = nTY * nTileY;
168 
169                     for( tools::Long nTX = 0; nTX < nCountX; nTX++ )
170                         AddTile( pRAcc.get(), aNewMtf, nTX * nTileX, nTY * nTileY, nTileX, nTileY );
171 
172                     if( nRestX )
173                         AddTile( pRAcc.get(), aNewMtf, nCountX * nTileX, nY, nRestX, nTileY );
174                 }
175 
176                 if( nRestY )
177                 {
178                     const tools::Long nY = nCountY * nTileY;
179 
180                     for( tools::Long nTX = 0; nTX < nCountX; nTX++ )
181                         AddTile( pRAcc.get(), aNewMtf, nTX * nTileX, nY, nTileX, nRestY );
182 
183                     if( nRestX )
184                         AddTile( pRAcc.get(), aNewMtf, nCountX * nTileX, nCountY * nTileY, nRestX, nRestY );
185                 }
186 
187                 pRAcc.reset();
188 
189                 for( size_t n = 0, nCount = rMtf.GetActionSize(); n < nCount; n++ )
190                     aNewMtf.AddAction( rMtf.GetAction( n )->Clone() );
191 
192                 aMap.SetScaleX( aMap.GetScaleX() * aScale );
193                 aMap.SetScaleY( aMap.GetScaleY() * aScale );
194                 aNewMtf.SetPrefMapMode( aMap );
195                 rMtf = aNewMtf;
196             }
197         }
198     }
199 
200     m_xPrgs->set_percentage(0);
201     m_pDocSh->SetWaitCursor( false );
202 }
203 
AddTile(BitmapReadAccess const * pRAcc,GDIMetaFile & rMtf,tools::Long nPosX,tools::Long nPosY,tools::Long nWidth,tools::Long nHeight)204 void SdVectorizeDlg::AddTile( BitmapReadAccess const * pRAcc, GDIMetaFile& rMtf,
205                               tools::Long nPosX, tools::Long nPosY, tools::Long nWidth, tools::Long nHeight )
206 {
207     sal_uLong           nSumR = 0, nSumG = 0, nSumB = 0;
208     const tools::Long      nRight = nPosX + nWidth - 1;
209     const tools::Long      nBottom = nPosY + nHeight - 1;
210     const double    fMult = 1.0 / ( nWidth * nHeight );
211 
212     for( tools::Long nY = nPosY; nY <= nBottom; nY++ )
213     {
214         for( tools::Long nX = nPosX; nX <= nRight; nX++ )
215         {
216             const BitmapColor aPixel( pRAcc->GetColor( nY, nX ) );
217 
218             nSumR += aPixel.GetRed();
219             nSumG += aPixel.GetGreen();
220             nSumB += aPixel.GetBlue();
221         }
222     }
223 
224     const Color aColor( static_cast<sal_uInt8>(FRound( nSumR * fMult )),
225                         static_cast<sal_uInt8>(FRound( nSumG * fMult )),
226                         static_cast<sal_uInt8>(FRound( nSumB * fMult )) );
227 
228     ::tools::Rectangle   aRect( Point( nPosX, nPosY ), Size( nWidth + 1, nHeight + 1 ) );
229     const Size& rMaxSize = rMtf.GetPrefSize();
230 
231     aRect = Application::GetDefaultDevice()->PixelToLogic(aRect, rMtf.GetPrefMapMode());
232 
233     if( aRect.Right() > ( rMaxSize.Width() - 1 ) )
234         aRect.SetRight( rMaxSize.Width() - 1 );
235 
236     if( aRect.Bottom() > ( rMaxSize.Height() - 1 ) )
237         aRect.SetBottom( rMaxSize.Height() - 1 );
238 
239     rMtf.AddAction( new MetaLineColorAction( aColor, true ) );
240     rMtf.AddAction( new MetaFillColorAction( aColor, true ) );
241     rMtf.AddAction( new MetaRectAction( aRect ) );
242 }
243 
IMPL_LINK(SdVectorizeDlg,ProgressHdl,tools::Long,nData,void)244 IMPL_LINK( SdVectorizeDlg, ProgressHdl, tools::Long, nData, void )
245 {
246     m_xPrgs->set_percentage(nData);
247 }
248 
IMPL_LINK_NOARG(SdVectorizeDlg,ClickPreviewHdl,weld::Button &,void)249 IMPL_LINK_NOARG(SdVectorizeDlg, ClickPreviewHdl, weld::Button&, void)
250 {
251     Calculate( aBmp, aMtf );
252     m_aMtfWin.SetGraphic( aMtf );
253     m_xBtnPreview->set_sensitive(false);
254 }
255 
IMPL_LINK_NOARG(SdVectorizeDlg,ClickOKHdl,weld::Button &,void)256 IMPL_LINK_NOARG(SdVectorizeDlg, ClickOKHdl, weld::Button&, void)
257 {
258     if (m_xBtnPreview->get_sensitive())
259         Calculate( aBmp, aMtf );
260 
261     SaveSettings();
262     m_xDialog->response(RET_OK);
263 }
264 
IMPL_LINK(SdVectorizeDlg,ToggleHdl,weld::Toggleable &,rCb,void)265 IMPL_LINK( SdVectorizeDlg, ToggleHdl, weld::Toggleable&, rCb, void )
266 {
267     if (rCb.get_active())
268     {
269         m_xFtFillHoles->set_sensitive(true);
270         m_xMtFillHoles->set_sensitive(true);
271     }
272     else
273     {
274         m_xFtFillHoles->set_sensitive(false);
275         m_xMtFillHoles->set_sensitive(false);
276     }
277 
278     m_xBtnPreview->set_sensitive(true);
279 }
280 
IMPL_LINK_NOARG(SdVectorizeDlg,ModifyHdl,weld::SpinButton &,void)281 IMPL_LINK_NOARG(SdVectorizeDlg, ModifyHdl, weld::SpinButton&, void)
282 {
283     m_xBtnPreview->set_sensitive(true);
284 }
285 
IMPL_LINK_NOARG(SdVectorizeDlg,MetricModifyHdl,weld::MetricSpinButton &,void)286 IMPL_LINK_NOARG(SdVectorizeDlg, MetricModifyHdl, weld::MetricSpinButton&, void)
287 {
288     m_xBtnPreview->set_sensitive(true);
289 }
290 
LoadSettings()291 void SdVectorizeDlg::LoadSettings()
292 {
293     tools::SvRef<SotStorageStream>  xIStm( SD_MOD()->GetOptionStream(
294                                SD_OPTION_VECTORIZE ,
295                                SdOptionStreamMode::Load ) );
296     sal_uInt16              nLayers;
297     sal_uInt16              nReduce;
298     sal_uInt16              nFillHoles;
299     bool                bFillHoles;
300 
301     if( xIStm.is() )
302     {
303         SdIOCompat aCompat( *xIStm, StreamMode::READ );
304         xIStm->ReadUInt16( nLayers ).ReadUInt16( nReduce ).ReadUInt16( nFillHoles ).ReadCharAsBool( bFillHoles );
305     }
306     else
307     {
308         nLayers = 8;
309         nReduce = 0;
310         nFillHoles = 32;
311         bFillHoles = false;
312     }
313 
314     m_xNmLayers->set_value(nLayers);
315     m_xMtReduce->set_value(nReduce, FieldUnit::NONE);
316     m_xMtFillHoles->set_value(nFillHoles, FieldUnit::NONE);
317     m_xCbFillHoles->set_active(bFillHoles);
318 
319     ToggleHdl(*m_xCbFillHoles);
320 }
321 
SaveSettings() const322 void SdVectorizeDlg::SaveSettings() const
323 {
324     tools::SvRef<SotStorageStream> xOStm( SD_MOD()->GetOptionStream(
325                               SD_OPTION_VECTORIZE  ,
326                               SdOptionStreamMode::Store ) );
327 
328     if( xOStm.is() )
329     {
330         SdIOCompat aCompat( *xOStm, StreamMode::WRITE, 1 );
331         xOStm->WriteUInt16( m_xNmLayers->get_value() ).WriteUInt16(m_xMtReduce->get_value(FieldUnit::NONE));
332         xOStm->WriteUInt16( m_xMtFillHoles->get_value(FieldUnit::NONE) ).WriteBool(m_xCbFillHoles->get_active());
333     }
334 }
335 
336 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
337