blob: f34f4fde188b0e8dbbd26092f244743e0b98d6cd [file] [log] [blame]
/**************************************************************
*
* 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
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"
#include <sot/factory.hxx>
#include <tools/urlobj.hxx>
#include <unotools/ucbstreamhelper.hxx>
#include <vcl/bmpacc.hxx>
#include <tools/poly.hxx>
#include <vcl/virdev.hxx>
#include <vcl/wrkwin.hxx>
#include <svl/solar.hrc>
#include <sfx2/docfile.hxx>
#include <sfx2/app.hxx>
#include "svx/xoutbmp.hxx"
#include <svtools/FilterConfigItem.hxx>
#include <svtools/filter.hxx>
#include <vcl/dibtools.hxx>
// -----------
// - Defines -
// -----------
#define FORMAT_BMP String(RTL_CONSTASCII_USTRINGPARAM("bmp"))
#define FORMAT_GIF String(RTL_CONSTASCII_USTRINGPARAM("gif"))
#define FORMAT_JPG String(RTL_CONSTASCII_USTRINGPARAM("jpg"))
#define FORMAT_PNG String(RTL_CONSTASCII_USTRINGPARAM("png"))
// --------------
// - XOutBitmap -
// --------------
GraphicFilter* XOutBitmap::pGrfFilter = NULL;
// ------------------------------------------------------------------------
Animation XOutBitmap::MirrorAnimation( const Animation& rAnimation, sal_Bool bHMirr, sal_Bool bVMirr )
{
Animation aNewAnim( rAnimation );
if( bHMirr || bVMirr )
{
const Size& rGlobalSize = aNewAnim.GetDisplaySizePixel();
sal_uIntPtr nMirrorFlags = 0L;
if( bHMirr )
nMirrorFlags |= BMP_MIRROR_HORZ;
if( bVMirr )
nMirrorFlags |= BMP_MIRROR_VERT;
for( sal_uInt16 i = 0, nCount = aNewAnim.Count(); i < nCount; i++ )
{
AnimationBitmap aAnimBmp( aNewAnim.Get( i ) );
// BitmapEx spiegeln
aAnimBmp.aBmpEx.Mirror( nMirrorFlags );
// Die Positionen innerhalb der Gesamtbitmap
// muessen natuerlich auch angepasst werden
if( bHMirr )
aAnimBmp.aPosPix.X() = rGlobalSize.Width() - aAnimBmp.aPosPix.X() -
aAnimBmp.aSizePix.Width();
if( bVMirr )
aAnimBmp.aPosPix.Y() = rGlobalSize.Height() - aAnimBmp.aPosPix.Y() -
aAnimBmp.aSizePix.Height();
aNewAnim.Replace( aAnimBmp, i );
}
}
return aNewAnim;
}
// ------------------------------------------------------------------------
Graphic XOutBitmap::MirrorGraphic( const Graphic& rGraphic, const sal_uIntPtr nMirrorFlags )
{
Graphic aRetGraphic;
if( nMirrorFlags )
{
if( rGraphic.IsAnimated() )
{
aRetGraphic = MirrorAnimation( rGraphic.GetAnimation(),
( nMirrorFlags & BMP_MIRROR_HORZ ) == BMP_MIRROR_HORZ,
( nMirrorFlags & BMP_MIRROR_VERT ) == BMP_MIRROR_VERT );
}
else
{
if( rGraphic.IsTransparent() )
{
BitmapEx aBmpEx( rGraphic.GetBitmapEx() );
aBmpEx.Mirror( nMirrorFlags );
aRetGraphic = aBmpEx;
}
else
{
Bitmap aBmp( rGraphic.GetBitmap() );
aBmp.Mirror( nMirrorFlags );
aRetGraphic = aBmp;
}
}
}
else
aRetGraphic = rGraphic;
return aRetGraphic;
}
// ------------------------------------------------------------------------
sal_uInt16 XOutBitmap::WriteGraphic( const Graphic& rGraphic, String& rFileName,
const String& rFilterName, const sal_uIntPtr nFlags,
const Size* pMtfSize_100TH_MM )
{
if( rGraphic.GetType() != GRAPHIC_NONE )
{
INetURLObject aURL( rFileName );
Graphic aGraphic;
String aExt;
GraphicFilter* pFilter = GraphicFilter::GetGraphicFilter();
sal_uInt16 nErr = GRFILTER_FILTERERROR, nFilter = GRFILTER_FORMAT_NOTFOUND;
sal_Bool bTransparent = rGraphic.IsTransparent(), bAnimated = rGraphic.IsAnimated();
DBG_ASSERT( aURL.GetProtocol() != INET_PROT_NOT_VALID, "XOutBitmap::WriteGraphic(...): invalid URL" );
// calculate correct file name
if( !( nFlags & XOUTBMP_DONT_EXPAND_FILENAME ) )
{
String aName( aURL.getBase() );
aName += '_';
aName += String(aURL.getExtension());
aName += '_';
String aStr( String::CreateFromInt32( rGraphic.GetChecksum(), 16 ) );
if ( aStr.GetChar(0) == '-' )
aStr.SetChar(0,'m');
aName += aStr;
aURL.setBase( aName );
}
// #121128# use shortcut to write SVG data in original form (if possible)
const SvgDataPtr aSvgDataPtr(rGraphic.getSvgData());
if(aSvgDataPtr.get()
&& aSvgDataPtr->getSvgDataArrayLength()
&& rFilterName.EqualsIgnoreCaseAscii("svg"))
{
if(!(nFlags & XOUTBMP_DONT_ADD_EXTENSION))
{
aURL.setExtension(rFilterName);
}
rFileName = aURL.GetMainURL(INetURLObject::NO_DECODE);
SfxMedium aMedium(aURL.GetMainURL(INetURLObject::NO_DECODE), STREAM_WRITE|STREAM_SHARE_DENYNONE|STREAM_TRUNC, true);
SvStream* pOStm = aMedium.GetOutStream();
if(pOStm)
{
pOStm->Write(aSvgDataPtr->getSvgDataArray().get(), aSvgDataPtr->getSvgDataArrayLength());
aMedium.Commit();
if(!aMedium.GetError())
{
nErr = GRFILTER_OK;
}
}
}
if( GRFILTER_OK != nErr )
{
if( ( nFlags & XOUTBMP_USE_NATIVE_IF_POSSIBLE ) &&
!( nFlags & XOUTBMP_MIRROR_HORZ ) &&
!( nFlags & XOUTBMP_MIRROR_VERT ) &&
( rGraphic.GetType() != GRAPHIC_GDIMETAFILE ) && rGraphic.IsLink() )
{
// try to write native link
const GfxLink aGfxLink( ( (Graphic&) rGraphic ).GetLink() );
switch( aGfxLink.GetType() )
{
case( GFX_LINK_TYPE_NATIVE_GIF ): aExt = FORMAT_GIF; break;
// #15508# added BMP type for better exports (no call/trigger found, prob used in HTML export)
case( GFX_LINK_TYPE_NATIVE_BMP ): aExt = FORMAT_BMP; break;
case( GFX_LINK_TYPE_NATIVE_JPG ): aExt = FORMAT_JPG; break;
case( GFX_LINK_TYPE_NATIVE_PNG ): aExt = FORMAT_PNG; break;
default:
break;
}
if( aExt.Len() )
{
if( 0 == (nFlags & XOUTBMP_DONT_ADD_EXTENSION))
aURL.setExtension( aExt );
rFileName = aURL.GetMainURL( INetURLObject::NO_DECODE );
SfxMedium aMedium( aURL.GetMainURL( INetURLObject::NO_DECODE ), STREAM_WRITE | STREAM_SHARE_DENYNONE | STREAM_TRUNC, sal_True );
SvStream* pOStm = aMedium.GetOutStream();
if( pOStm && aGfxLink.GetDataSize() && aGfxLink.GetData() )
{
pOStm->Write( aGfxLink.GetData(), aGfxLink.GetDataSize() );
aMedium.Commit();
if( !aMedium.GetError() )
nErr = GRFILTER_OK;
}
}
}
}
if( GRFILTER_OK != nErr )
{
String aFilter( rFilterName );
sal_Bool bWriteTransGrf = ( aFilter.EqualsIgnoreCaseAscii( "transgrf" ) ) ||
( aFilter.EqualsIgnoreCaseAscii( "gif" ) ) ||
( nFlags & XOUTBMP_USE_GIF_IF_POSSIBLE ) ||
( ( nFlags & XOUTBMP_USE_GIF_IF_SENSIBLE ) && ( bAnimated || bTransparent ) );
// get filter and extension
if( bWriteTransGrf )
aFilter = FORMAT_GIF;
nFilter = pFilter->GetExportFormatNumberForShortName( aFilter );
if( GRFILTER_FORMAT_NOTFOUND == nFilter )
{
nFilter = pFilter->GetExportFormatNumberForShortName( FORMAT_JPG );
if( GRFILTER_FORMAT_NOTFOUND == nFilter )
nFilter = pFilter->GetExportFormatNumberForShortName( FORMAT_BMP );
}
if( GRFILTER_FORMAT_NOTFOUND != nFilter )
{
aExt = pFilter->GetExportFormatShortName( nFilter ).ToLowerAscii();
if( bWriteTransGrf )
{
if( bAnimated )
aGraphic = rGraphic;
else
{
if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GRAPHIC_BITMAP ) )
{
VirtualDevice aVDev;
const Size aSize( aVDev.LogicToPixel( *pMtfSize_100TH_MM, MAP_100TH_MM ) );
if( aVDev.SetOutputSizePixel( aSize ) )
{
const Wallpaper aWallpaper( aVDev.GetBackground() );
const Point aPt;
aVDev.SetBackground( Wallpaper( Color( COL_BLACK ) ) );
aVDev.Erase();
rGraphic.Draw( &aVDev, aPt, aSize );
const Bitmap aBitmap( aVDev.GetBitmap( aPt, aSize ) );
aVDev.SetBackground( aWallpaper );
aVDev.Erase();
rGraphic.Draw( &aVDev, aPt, aSize );
aVDev.SetRasterOp( ROP_XOR );
aVDev.DrawBitmap( aPt, aSize, aBitmap );
aGraphic = BitmapEx( aBitmap, aVDev.GetBitmap( aPt, aSize ) );
}
else
aGraphic = rGraphic.GetBitmapEx();
}
else
aGraphic = rGraphic.GetBitmapEx();
}
}
else
{
if( pMtfSize_100TH_MM && ( rGraphic.GetType() != GRAPHIC_BITMAP ) )
{
VirtualDevice aVDev;
const Size aSize( aVDev.LogicToPixel( *pMtfSize_100TH_MM, MAP_100TH_MM ) );
if( aVDev.SetOutputSizePixel( aSize ) )
{
rGraphic.Draw( &aVDev, Point(), aSize );
aGraphic = aVDev.GetBitmap( Point(), aSize );
}
else
aGraphic = rGraphic.GetBitmap();
}
else
aGraphic = rGraphic.GetBitmap();
}
// mirror?
if( ( nFlags & XOUTBMP_MIRROR_HORZ ) || ( nFlags & XOUTBMP_MIRROR_VERT ) )
aGraphic = MirrorGraphic( aGraphic, nFlags );
if( ( GRFILTER_FORMAT_NOTFOUND != nFilter ) && ( aGraphic.GetType() != GRAPHIC_NONE ) )
{
if( 0 == (nFlags & XOUTBMP_DONT_ADD_EXTENSION))
aURL.setExtension( aExt );
rFileName = aURL.GetMainURL( INetURLObject::NO_DECODE );
nErr = ExportGraphic( aGraphic, aURL, *pFilter, nFilter, NULL );
}
}
}
return nErr;
}
else
{
return GRFILTER_OK;
}
}
// ------------------------------------------------------------------------
#ifdef _MSC_VER
#pragma optimize ( "", off )
#endif
sal_uInt16 XOutBitmap::ExportGraphic( const Graphic& rGraphic, const INetURLObject& rURL,
GraphicFilter& rFilter, const sal_uInt16 nFormat,
const com::sun::star::uno::Sequence< com::sun::star::beans::PropertyValue >* pFilterData )
{
DBG_ASSERT( rURL.GetProtocol() != INET_PROT_NOT_VALID, "XOutBitmap::ExportGraphic(...): invalid URL" );
SfxMedium aMedium( rURL.GetMainURL( INetURLObject::NO_DECODE ), STREAM_WRITE | STREAM_SHARE_DENYNONE | STREAM_TRUNC, sal_True );
SvStream* pOStm = aMedium.GetOutStream();
sal_uInt16 nRet = GRFILTER_IOERROR;
if( pOStm )
{
pGrfFilter = &rFilter;
nRet = rFilter.ExportGraphic( rGraphic, rURL.GetMainURL( INetURLObject::NO_DECODE ), *pOStm, nFormat, pFilterData );
pGrfFilter = NULL;
aMedium.Commit();
if( aMedium.GetError() && ( GRFILTER_OK == nRet ) )
nRet = GRFILTER_IOERROR;
}
return nRet;
}
#ifdef _MSC_VER
#pragma optimize ( "", on )
#endif
// ------------------------------------------------------------------------
Bitmap XOutBitmap::DetectEdges( const Bitmap& rBmp, const sal_uInt8 cThreshold )
{
const Size aSize( rBmp.GetSizePixel() );
Bitmap aRetBmp;
sal_Bool bRet = sal_False;
if( ( aSize.Width() > 2L ) && ( aSize.Height() > 2L ) )
{
Bitmap aWorkBmp( rBmp );
if( aWorkBmp.Convert( BMP_CONVERSION_8BIT_GREYS ) )
{
Bitmap aDstBmp( aSize, 1 );
BitmapReadAccess* pReadAcc = aWorkBmp.AcquireReadAccess();
BitmapWriteAccess* pWriteAcc = aDstBmp.AcquireWriteAccess();
if( pReadAcc && pWriteAcc )
{
const long nWidth = aSize.Width();
const long nWidth2 = nWidth - 2L;
const long nHeight = aSize.Height();
const long nHeight2 = nHeight - 2L;
const long lThres2 = (long) cThreshold * cThreshold;
const sal_uInt8 nWhitePalIdx(static_cast< sal_uInt8 >(pWriteAcc->GetBestPaletteIndex(Color(COL_WHITE))));
const sal_uInt8 nBlackPalIdx(static_cast< sal_uInt8 >(pWriteAcc->GetBestPaletteIndex(Color(COL_BLACK))));
long nSum1;
long nSum2;
long lGray;
// initialize border with white pixels
pWriteAcc->SetLineColor( Color( COL_WHITE) );
pWriteAcc->DrawLine( Point(), Point( nWidth - 1L, 0L ) );
pWriteAcc->DrawLine( Point( nWidth - 1L, 0L ), Point( nWidth - 1L, nHeight - 1L ) );
pWriteAcc->DrawLine( Point( nWidth - 1L, nHeight - 1L ), Point( 0L, nHeight - 1L ) );
pWriteAcc->DrawLine( Point( 0, nHeight - 1L ), Point() );
for( long nY = 0L, nY1 = 1L, nY2 = 2; nY < nHeight2; nY++, nY1++, nY2++ )
{
for( long nX = 0L, nXDst = 1L, nXTmp; nX < nWidth2; nX++, nXDst++ )
{
nXTmp = nX;
nSum1 = -( nSum2 = lGray = pReadAcc->GetPixelIndex( nY, nXTmp++ ) );
nSum2 += ( (long) pReadAcc->GetPixelIndex( nY, nXTmp++ ) ) << 1;
nSum1 += ( lGray = pReadAcc->GetPixelIndex( nY, nXTmp ) );
nSum2 += lGray;
nSum1 += ( (long) pReadAcc->GetPixelIndex( nY1, nXTmp ) ) << 1;
nSum1 -= ( (long) pReadAcc->GetPixelIndex( nY1, nXTmp -= 2 ) ) << 1;
nSum1 += ( lGray = -(long) pReadAcc->GetPixelIndex( nY2, nXTmp++ ) );
nSum2 += lGray;
nSum2 -= ( (long) pReadAcc->GetPixelIndex( nY2, nXTmp++ ) ) << 1;
nSum1 += ( lGray = (long) pReadAcc->GetPixelIndex( nY2, nXTmp ) );
nSum2 -= lGray;
if( ( nSum1 * nSum1 + nSum2 * nSum2 ) < lThres2 )
pWriteAcc->SetPixelIndex( nY1, nXDst, nWhitePalIdx );
else
pWriteAcc->SetPixelIndex( nY1, nXDst, nBlackPalIdx );
}
}
bRet = sal_True;
}
aWorkBmp.ReleaseAccess( pReadAcc );
aDstBmp.ReleaseAccess( pWriteAcc );
if( bRet )
aRetBmp = aDstBmp;
}
}
if( !aRetBmp )
aRetBmp = rBmp;
else
{
aRetBmp.SetPrefMapMode( rBmp.GetPrefMapMode() );
aRetBmp.SetPrefSize( rBmp.GetPrefSize() );
}
return aRetBmp;
};
// ------------------------------------------------------------------------
Polygon XOutBitmap::GetCountour( const Bitmap& rBmp, const sal_uIntPtr nFlags,
const sal_uInt8 cEdgeDetectThreshold, const Rectangle* pWorkRectPixel )
{
Bitmap aWorkBmp;
Polygon aRetPoly;
Point aTmpPoint;
Rectangle aWorkRect( aTmpPoint, rBmp.GetSizePixel() );
if( pWorkRectPixel )
aWorkRect.Intersection( *pWorkRectPixel );
aWorkRect.Justify();
if( ( aWorkRect.GetWidth() > 4 ) && ( aWorkRect.GetHeight() > 4 ) )
{
// falls Flag gesetzt, muessen wir Kanten detektieren
if( nFlags & XOUTBMP_CONTOUR_EDGEDETECT )
aWorkBmp = DetectEdges( rBmp, cEdgeDetectThreshold );
else
aWorkBmp = rBmp;
BitmapReadAccess* pAcc = aWorkBmp.AcquireReadAccess();
if( pAcc )
{
const Size& rPrefSize = aWorkBmp.GetPrefSize();
const long nWidth = pAcc->Width();
const long nHeight = pAcc->Height();
const double fFactorX = (double) rPrefSize.Width() / nWidth;
const double fFactorY = (double) rPrefSize.Height() / nHeight;
const long nStartX1 = aWorkRect.Left() + 1L;
const long nEndX1 = aWorkRect.Right();
const long nStartX2 = nEndX1 - 1L;
// const long nEndX2 = nStartX1 - 1L;
const long nStartY1 = aWorkRect.Top() + 1L;
const long nEndY1 = aWorkRect.Bottom();
const long nStartY2 = nEndY1 - 1L;
// const long nEndY2 = nStartY1 - 1L;
Point* pPoints1 = NULL;
Point* pPoints2 = NULL;
long nX, nY;
sal_uInt16 nPolyPos = 0;
const BitmapColor aBlack = pAcc->GetBestMatchingColor( Color( COL_BLACK ) );
if( nFlags & XOUTBMP_CONTOUR_VERT )
{
pPoints1 = new Point[ nWidth ];
pPoints2 = new Point[ nWidth ];
for( nX = nStartX1; nX < nEndX1; nX++ )
{
nY = nStartY1;
// zunaechst Zeile von Links nach Rechts durchlaufen
while( nY < nEndY1 )
{
if( aBlack == pAcc->GetPixel( nY, nX ) )
{
pPoints1[ nPolyPos ] = Point( nX, nY );
nY = nStartY2;
// diese Schleife wird immer gebreaked da hier ja min. ein Pixel ist
while( sal_True )
{
if( aBlack == pAcc->GetPixel( nY, nX ) )
{
pPoints2[ nPolyPos ] = Point( nX, nY );
break;
}
nY--;
}
nPolyPos++;
break;
}
nY++;
}
}
}
else
{
pPoints1 = new Point[ nHeight ];
pPoints2 = new Point[ nHeight ];
for ( nY = nStartY1; nY < nEndY1; nY++ )
{
nX = nStartX1;
// zunaechst Zeile von Links nach Rechts durchlaufen
while( nX < nEndX1 )
{
if( aBlack == pAcc->GetPixel( nY, nX ) )
{
pPoints1[ nPolyPos ] = Point( nX, nY );
nX = nStartX2;
// diese Schleife wird immer gebreaked da hier ja min. ein Pixel ist
while( sal_True )
{
if( aBlack == pAcc->GetPixel( nY, nX ) )
{
pPoints2[ nPolyPos ] = Point( nX, nY );
break;
}
nX--;
}
nPolyPos++;
break;
}
nX++;
}
}
}
const sal_uInt16 nNewSize1 = nPolyPos << 1;
aRetPoly = Polygon( nPolyPos, pPoints1 );
aRetPoly.SetSize( nNewSize1 + 1 );
aRetPoly[ nNewSize1 ] = aRetPoly[ 0 ];
for( sal_uInt16 j = nPolyPos; nPolyPos < nNewSize1; )
aRetPoly[ nPolyPos++ ] = pPoints2[ --j ];
if( ( fFactorX != 0. ) && ( fFactorY != 0. ) )
aRetPoly.Scale( fFactorX, fFactorY );
delete[] pPoints1;
delete[] pPoints2;
}
}
return aRetPoly;
};
// ----------------
// - DitherBitmap -
// ----------------
sal_Bool DitherBitmap( Bitmap& rBitmap )
{
sal_Bool bRet = sal_False;
if( ( rBitmap.GetBitCount() >= 8 ) && ( Application::GetDefaultDevice()->GetColorCount() < 257 ) )
bRet = rBitmap.Dither( BMP_DITHER_FLOYD );
else
bRet = sal_False;
return bRet;
}