/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #define VECTORIZE_MAX_EXTENT 512 SdVectorizeDlg::SdVectorizeDlg(weld::Window* pParent, const Bitmap& rBmp, ::sd::DrawDocShell* pDocShell) : GenericDialogController(pParent, "modules/sdraw/ui/vectorize.ui", "VectorizeDialog") , m_pDocSh(pDocShell) , aBmp(rBmp) , m_aBmpWin(m_xDialog.get()) , m_aMtfWin(m_xDialog.get()) , m_xNmLayers(m_xBuilder->weld_spin_button("colors")) , m_xMtReduce(m_xBuilder->weld_metric_spin_button("points", FieldUnit::PIXEL)) , m_xFtFillHoles(m_xBuilder->weld_label("tilesft")) , m_xMtFillHoles(m_xBuilder->weld_metric_spin_button("tiles", FieldUnit::PIXEL)) , m_xCbFillHoles(m_xBuilder->weld_check_button("fillholes")) , m_xBmpWin(new weld::CustomWeld(*m_xBuilder, "source", m_aBmpWin)) , m_xMtfWin(new weld::CustomWeld(*m_xBuilder, "vectorized", m_aMtfWin)) , m_xPrgs(m_xBuilder->weld_progress_bar("progressbar")) , m_xBtnOK(m_xBuilder->weld_button("ok")) , m_xBtnPreview(m_xBuilder->weld_button("preview")) { const int nWidth = m_xFtFillHoles->get_approximate_digit_width() * 32; const int nHeight = m_xFtFillHoles->get_text_height() * 16; m_xBmpWin->set_size_request(nWidth, nHeight); m_xMtfWin->set_size_request(nWidth, nHeight); m_xBtnPreview->connect_clicked( LINK( this, SdVectorizeDlg, ClickPreviewHdl ) ); m_xBtnOK->connect_clicked( LINK( this, SdVectorizeDlg, ClickOKHdl ) ); m_xNmLayers->connect_value_changed( LINK( this, SdVectorizeDlg, ModifyHdl ) ); m_xMtReduce->connect_value_changed( LINK( this, SdVectorizeDlg, MetricModifyHdl ) ); m_xMtFillHoles->connect_value_changed( LINK( this, SdVectorizeDlg, MetricModifyHdl ) ); m_xCbFillHoles->connect_toggled( LINK( this, SdVectorizeDlg, ToggleHdl ) ); LoadSettings(); InitPreviewBmp(); } SdVectorizeDlg::~SdVectorizeDlg() { } ::tools::Rectangle SdVectorizeDlg::GetRect( const Size& rDispSize, const Size& rBmpSize ) { ::tools::Rectangle aRect; if( rBmpSize.Width() && rBmpSize.Height() && rDispSize.Width() && rDispSize.Height() ) { Size aBmpSize( rBmpSize ); const double fGrfWH = static_cast(aBmpSize.Width()) / aBmpSize.Height(); const double fWinWH = static_cast(rDispSize.Width()) / rDispSize.Height(); if( fGrfWH < fWinWH ) { aBmpSize.setWidth( static_cast( rDispSize.Height() * fGrfWH ) ); aBmpSize.setHeight( rDispSize.Height() ); } else { aBmpSize.setWidth( rDispSize.Width() ); aBmpSize.setHeight( static_cast( rDispSize.Width() / fGrfWH) ); } const Point aBmpPos( ( rDispSize.Width() - aBmpSize.Width() ) >> 1, ( rDispSize.Height() - aBmpSize.Height() ) >> 1 ); aRect = ::tools::Rectangle( aBmpPos, aBmpSize ); } return aRect; } void SdVectorizeDlg::InitPreviewBmp() { const ::tools::Rectangle aRect( GetRect( m_aBmpWin.GetOutputSizePixel(), aBmp.GetSizePixel() ) ); aPreviewBmp = aBmp; aPreviewBmp.Scale( aRect.GetSize() ); m_aBmpWin.SetGraphic(BitmapEx(aPreviewBmp)); } Bitmap SdVectorizeDlg::GetPreparedBitmap( Bitmap const & rBmp, Fraction& rScale ) { Bitmap aNew( rBmp ); const Size aSizePix( aNew.GetSizePixel() ); if( aSizePix.Width() > VECTORIZE_MAX_EXTENT || aSizePix.Height() > VECTORIZE_MAX_EXTENT ) { const ::tools::Rectangle aRect( GetRect( Size( VECTORIZE_MAX_EXTENT, VECTORIZE_MAX_EXTENT ), aSizePix ) ); rScale = Fraction( aSizePix.Width(), aRect.GetWidth() ); aNew.Scale( aRect.GetSize() ); } else rScale = Fraction( 1, 1 ); BitmapEx aNewBmpEx(aNew); BitmapFilter::Filter(aNewBmpEx, BitmapSimpleColorQuantizationFilter(m_xNmLayers->get_value())); aNew = aNewBmpEx.GetBitmap(); return aNew; } void SdVectorizeDlg::Calculate( Bitmap const & rBmp, GDIMetaFile& rMtf ) { m_pDocSh->SetWaitCursor( true ); m_xPrgs->set_percentage(0); Fraction aScale; Bitmap aTmp( GetPreparedBitmap( rBmp, aScale ) ); if( !aTmp.IsEmpty() ) { const Link<::tools::Long,void> aPrgsHdl( LINK( this, SdVectorizeDlg, ProgressHdl ) ); aTmp.Vectorize( rMtf, static_cast(m_xMtReduce->get_value(FieldUnit::NONE)), &aPrgsHdl ); if (m_xCbFillHoles->get_active()) { GDIMetaFile aNewMtf; Bitmap::ScopedReadAccess pRAcc(aTmp); if( pRAcc ) { const tools::Long nWidth = pRAcc->Width(); const tools::Long nHeight = pRAcc->Height(); const tools::Long nTileX = m_xMtFillHoles->get_value(FieldUnit::NONE); const tools::Long nTileY = m_xMtFillHoles->get_value(FieldUnit::NONE); assert(nTileX && "div-by-zero"); const tools::Long nCountX = nWidth / nTileX; assert(nTileY && "div-by-zero"); const tools::Long nCountY = nHeight / nTileY; const tools::Long nRestX = nWidth % nTileX; const tools::Long nRestY = nHeight % nTileY; MapMode aMap( rMtf.GetPrefMapMode() ); aNewMtf.SetPrefSize( rMtf.GetPrefSize() ); aNewMtf.SetPrefMapMode( aMap ); for( tools::Long nTY = 0; nTY < nCountY; nTY++ ) { const tools::Long nY = nTY * nTileY; for( tools::Long nTX = 0; nTX < nCountX; nTX++ ) AddTile( pRAcc.get(), aNewMtf, nTX * nTileX, nTY * nTileY, nTileX, nTileY ); if( nRestX ) AddTile( pRAcc.get(), aNewMtf, nCountX * nTileX, nY, nRestX, nTileY ); } if( nRestY ) { const tools::Long nY = nCountY * nTileY; for( tools::Long nTX = 0; nTX < nCountX; nTX++ ) AddTile( pRAcc.get(), aNewMtf, nTX * nTileX, nY, nTileX, nRestY ); if( nRestX ) AddTile( pRAcc.get(), aNewMtf, nCountX * nTileX, nCountY * nTileY, nRestX, nRestY ); } pRAcc.reset(); for( size_t n = 0, nCount = rMtf.GetActionSize(); n < nCount; n++ ) aNewMtf.AddAction( rMtf.GetAction( n )->Clone() ); aMap.SetScaleX( aMap.GetScaleX() * aScale ); aMap.SetScaleY( aMap.GetScaleY() * aScale ); aNewMtf.SetPrefMapMode( aMap ); rMtf = aNewMtf; } } } m_xPrgs->set_percentage(0); m_pDocSh->SetWaitCursor( false ); } void SdVectorizeDlg::AddTile( BitmapReadAccess const * pRAcc, GDIMetaFile& rMtf, tools::Long nPosX, tools::Long nPosY, tools::Long nWidth, tools::Long nHeight ) { sal_uLong nSumR = 0, nSumG = 0, nSumB = 0; const tools::Long nRight = nPosX + nWidth - 1; const tools::Long nBottom = nPosY + nHeight - 1; const double fMult = 1.0 / ( nWidth * nHeight ); for( tools::Long nY = nPosY; nY <= nBottom; nY++ ) { for( tools::Long nX = nPosX; nX <= nRight; nX++ ) { const BitmapColor aPixel( pRAcc->GetColor( nY, nX ) ); nSumR += aPixel.GetRed(); nSumG += aPixel.GetGreen(); nSumB += aPixel.GetBlue(); } } const Color aColor( static_cast(FRound( nSumR * fMult )), static_cast(FRound( nSumG * fMult )), static_cast(FRound( nSumB * fMult )) ); ::tools::Rectangle aRect( Point( nPosX, nPosY ), Size( nWidth + 1, nHeight + 1 ) ); const Size& rMaxSize = rMtf.GetPrefSize(); aRect = Application::GetDefaultDevice()->PixelToLogic(aRect, rMtf.GetPrefMapMode()); if( aRect.Right() > ( rMaxSize.Width() - 1 ) ) aRect.SetRight( rMaxSize.Width() - 1 ); if( aRect.Bottom() > ( rMaxSize.Height() - 1 ) ) aRect.SetBottom( rMaxSize.Height() - 1 ); rMtf.AddAction( new MetaLineColorAction( aColor, true ) ); rMtf.AddAction( new MetaFillColorAction( aColor, true ) ); rMtf.AddAction( new MetaRectAction( aRect ) ); } IMPL_LINK( SdVectorizeDlg, ProgressHdl, tools::Long, nData, void ) { m_xPrgs->set_percentage(nData); } IMPL_LINK_NOARG(SdVectorizeDlg, ClickPreviewHdl, weld::Button&, void) { Calculate( aBmp, aMtf ); m_aMtfWin.SetGraphic( aMtf ); m_xBtnPreview->set_sensitive(false); } IMPL_LINK_NOARG(SdVectorizeDlg, ClickOKHdl, weld::Button&, void) { if (m_xBtnPreview->get_sensitive()) Calculate( aBmp, aMtf ); SaveSettings(); m_xDialog->response(RET_OK); } IMPL_LINK( SdVectorizeDlg, ToggleHdl, weld::Toggleable&, rCb, void ) { if (rCb.get_active()) { m_xFtFillHoles->set_sensitive(true); m_xMtFillHoles->set_sensitive(true); } else { m_xFtFillHoles->set_sensitive(false); m_xMtFillHoles->set_sensitive(false); } m_xBtnPreview->set_sensitive(true); } IMPL_LINK_NOARG(SdVectorizeDlg, ModifyHdl, weld::SpinButton&, void) { m_xBtnPreview->set_sensitive(true); } IMPL_LINK_NOARG(SdVectorizeDlg, MetricModifyHdl, weld::MetricSpinButton&, void) { m_xBtnPreview->set_sensitive(true); } void SdVectorizeDlg::LoadSettings() { tools::SvRef xIStm( SD_MOD()->GetOptionStream( SD_OPTION_VECTORIZE , SdOptionStreamMode::Load ) ); sal_uInt16 nLayers; sal_uInt16 nReduce; sal_uInt16 nFillHoles; bool bFillHoles; if( xIStm.is() ) { SdIOCompat aCompat( *xIStm, StreamMode::READ ); xIStm->ReadUInt16( nLayers ).ReadUInt16( nReduce ).ReadUInt16( nFillHoles ).ReadCharAsBool( bFillHoles ); } else { nLayers = 8; nReduce = 0; nFillHoles = 32; bFillHoles = false; } m_xNmLayers->set_value(nLayers); m_xMtReduce->set_value(nReduce, FieldUnit::NONE); m_xMtFillHoles->set_value(nFillHoles, FieldUnit::NONE); m_xCbFillHoles->set_active(bFillHoles); ToggleHdl(*m_xCbFillHoles); } void SdVectorizeDlg::SaveSettings() const { tools::SvRef xOStm( SD_MOD()->GetOptionStream( SD_OPTION_VECTORIZE , SdOptionStreamMode::Store ) ); if( xOStm.is() ) { SdIOCompat aCompat( *xOStm, StreamMode::WRITE, 1 ); xOStm->WriteUInt16( m_xNmLayers->get_value() ).WriteUInt16(m_xMtReduce->get_value(FieldUnit::NONE)); xOStm->WriteUInt16( m_xMtFillHoles->get_value(FieldUnit::NONE) ).WriteBool(m_xCbFillHoles->get_active()); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */