/* -*- 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 <stdlib.h>
#include <sal/log.hxx>
#include <o3tl/make_unique.hxx>
#include <vcl/bitmapaccess.hxx>
#include <tools/poly.hxx>
#include <vcl/gdimtf.hxx>
#include <vcl/metaact.hxx>
#include <vcl/svapp.hxx>
#include <vcl/wrkwin.hxx>
#include <vcl/virdev.hxx>
#include "impvect.hxx"
#include <memory>
#define VECT_POLY_MAX 8192
#define VECT_FREE_INDEX 0
#define VECT_CONT_INDEX 1
#define VECT_DONE_INDEX 2
#define VECT_POLY_INLINE_INNER 1UL
#define VECT_POLY_INLINE_OUTER 2UL
#define VECT_POLY_OUTLINE_INNER 4UL
#define VECT_POLY_OUTLINE_OUTER 8UL
#define VECT_MAP( _def_pIn, _def_pOut, _def_nVal ) _def_pOut[_def_nVal]=(_def_pIn[_def_nVal]=((_def_nVal)*4)+1)+5;
#define BACK_MAP( _def_nVal ) ((((_def_nVal)+2)>>2)-1)
#define VECT_PROGRESS( _def_pProgress, _def_nVal ) \
if(_def_pProgress) \
(_def_pProgress->Call(_def_nVal));
class ImplVectMap;
class ImplChain;
namespace ImplVectorizer
{
ImplVectMap* ImplExpand( BitmapReadAccess* pRAcc, const Color& rColor );
void ImplCalculate( ImplVectMap* pMap, tools::PolyPolygon& rPolyPoly, sal_uInt8 cReduce );
bool ImplGetChain( ImplVectMap* pMap, const Point& rStartPt, ImplChain& rChain );
bool ImplIsUp( ImplVectMap const * pMap, long nY, long nX );
void ImplLimitPolyPoly( tools::PolyPolygon& rPolyPoly );
}
struct ChainMove { long nDX; long nDY; };
static const ChainMove aImplMove[ 8 ] = {
{ 1, 0 },
{ 0, -1 },
{ -1, 0 },
{ 0, 1 },
{ 1, -1 },
{ -1, -1 },
{ -1, 1 },
{ 1, 1 }
};
static const ChainMove aImplMoveInner[ 8 ] = {
{ 0, 1 },
{ 1, 0 },
{ 0, -1 },
{ -1, 0 },
{ 0, 1 },
{ 1, 0 },
{ 0, -1 },
{ -1, 0 }
};
static const ChainMove aImplMoveOuter[ 8 ] = {
{ 0, -1 },
{ -1, 0 },
{ 0, 1 },
{ 1, 0 },
{ -1, 0 },
{ 0, 1 },
{ 1, 0 },
{ 0, -1 }
};
struct ImplColorSet
{
BitmapColor maColor;
sal_uInt16 mnIndex = 0;
bool mbSet = false;
};
extern "C" int ImplColorSetCmpFnc( const void* p1, const void* p2 )
{
ImplColorSet const * pSet1 = static_cast<ImplColorSet const *>(p1);
ImplColorSet const * pSet2 = static_cast<ImplColorSet const *>(p2);
int nRet;
if( pSet1->mbSet && pSet2->mbSet )
{
const sal_uInt8 cLum1 = pSet1->maColor.GetLuminance();
const sal_uInt8 cLum2 = pSet2->maColor.GetLuminance();
nRet = ( ( cLum1 > cLum2 ) ? -1 : ( ( cLum1 == cLum2 ) ? 0 : 1 ) );
}
else if( pSet1->mbSet )
nRet = -1;
else if( pSet2->mbSet )
nRet = 1;
else
nRet = 0;
return nRet;
}
class ImplPointArray
{
std::unique_ptr<Point[]> mpArray;
sal_uLong mnSize;
sal_uLong mnRealSize;
public:
ImplPointArray();
void ImplSetSize( sal_uLong nSize );
sal_uLong ImplGetRealSize() const { return mnRealSize; }
void ImplSetRealSize( sal_uLong nRealSize ) { mnRealSize = nRealSize; }
void ImplCreatePoly( tools::Polygon& rPoly ) const;
inline Point& operator[]( sal_uLong nPos );
inline const Point& operator[]( sal_uLong nPos ) const;
};
ImplPointArray::ImplPointArray() :
mnSize ( 0 ),
mnRealSize ( 0 )
{
}
void ImplPointArray::ImplSetSize( sal_uLong nSize )
{
const sal_uLong nTotal = nSize * sizeof( Point );
mnSize = nSize;
mnRealSize = 0;
mpArray = o3tl::make_unique<Point[]>( nTotal );
}
inline Point& ImplPointArray::operator[]( sal_uLong nPos )
{
SAL_WARN_IF( nPos >= mnSize, "vcl", "ImplPointArray::operator[]: nPos out of range!" );
return mpArray[ nPos ];
}
inline const Point& ImplPointArray::operator[]( sal_uLong nPos ) const
{
SAL_WARN_IF( nPos >= mnSize, "vcl", "ImplPointArray::operator[]: nPos out of range!" );
return mpArray[ nPos ];
}
void ImplPointArray::ImplCreatePoly( tools::Polygon& rPoly ) const
{
rPoly = tools::Polygon( sal::static_int_cast<sal_uInt16>(mnRealSize), mpArray.get() );
}
class ImplVectMap
{
private:
Scanline mpBuf;
Scanline* mpScan;
long mnWidth;
long mnHeight;
public:
ImplVectMap( long nWidth, long nHeight );
~ImplVectMap();
long Width() const { return mnWidth; }
long Height() const { return mnHeight; }
inline void Set( long nY, long nX, sal_uInt8 cVal );
inline sal_uInt8 Get( long nY, long nX ) const;
inline bool IsFree( long nY, long nX ) const;
inline bool IsCont( long nY, long nX ) const;
inline bool IsDone( long nY, long nX ) const;
};
ImplVectMap::ImplVectMap( long nWidth, long nHeight ) :
mpBuf ( static_cast<Scanline>(rtl_allocateZeroMemory(nWidth * nHeight)) ),
mpScan ( static_cast<Scanline*>(std::malloc(nHeight * sizeof(Scanline))) ),
mnWidth ( nWidth ),
mnHeight( nHeight )
{
const long nWidthAl = ( nWidth >> 2 ) + 1;
Scanline pTmp = mpBuf;
for( long nY = 0; nY < nHeight; pTmp += nWidthAl )
mpScan[ nY++ ] = pTmp;
}
ImplVectMap::~ImplVectMap()
{
std::free( mpBuf );
std::free( mpScan );
}
inline void ImplVectMap::Set( long nY, long nX, sal_uInt8 cVal )
{
const sal_uInt8 cShift = sal::static_int_cast<sal_uInt8>(6 - ( ( nX & 3 ) << 1 ));
( ( mpScan[ nY ][ nX >> 2 ] ) &= ~( 3 << cShift ) ) |= ( cVal << cShift );
}
inline sal_uInt8 ImplVectMap::Get( long nY, long nX ) const
{
return sal::static_int_cast<sal_uInt8>( ( ( mpScan[ nY ][ nX >> 2 ] ) >> ( 6 - ( ( nX & 3 ) << 1 ) ) ) & 3 );
}
inline bool ImplVectMap::IsFree( long nY, long nX ) const
{
return( VECT_FREE_INDEX == Get( nY, nX ) );
}
inline bool ImplVectMap::IsCont( long nY, long nX ) const
{
return( VECT_CONT_INDEX == Get( nY, nX ) );
}
inline bool ImplVectMap::IsDone( long nY, long nX ) const
{
return( VECT_DONE_INDEX == Get( nY, nX ) );
}
class ImplChain
{
private:
tools::Polygon maPoly;
Point maStartPt;
sal_uLong mnArraySize;
sal_uLong mnCount;
std::unique_ptr<sal_uInt8[]>
mpCodes;
void ImplGetSpace();
void ImplPostProcess( const ImplPointArray& rArr );
ImplChain(const ImplChain&) = delete;
ImplChain& operator=(const ImplChain&) = delete;
public:
ImplChain();
void ImplBeginAdd( const Point& rStartPt );
inline void ImplAdd( sal_uInt8 nCode );
void ImplEndAdd( sal_uLong nTypeFlag );
const tools::Polygon& ImplGetPoly() const { return maPoly; }
};
ImplChain::ImplChain() :
mnArraySize ( 1024 ),
mnCount ( 0 ),
mpCodes ( new sal_uInt8[mnArraySize] )
{
}
void ImplChain::ImplGetSpace()
{
const sal_uLong nOldArraySize = mnArraySize;
sal_uInt8* pNewCodes;
mnArraySize = mnArraySize << 1;
pNewCodes = new sal_uInt8[ mnArraySize ];
memcpy( pNewCodes, mpCodes.get(), nOldArraySize );
mpCodes.reset( pNewCodes );
}
void ImplChain::ImplBeginAdd( const Point& rStartPt )
{
maPoly = tools::Polygon();
maStartPt = rStartPt;
mnCount = 0;
}
inline void ImplChain::ImplAdd( sal_uInt8 nCode )
{
if( mnCount == mnArraySize )
ImplGetSpace();
mpCodes[ mnCount++ ] = nCode;
}
void ImplChain::ImplEndAdd( sal_uLong nFlag )
{
if( mnCount )
{
ImplPointArray aArr;
if( nFlag & VECT_POLY_INLINE_INNER )
{
long nFirstX, nFirstY;
long nLastX, nLastY;
nFirstX = nLastX = maStartPt.X();
nFirstY = nLastY = maStartPt.Y();
aArr.ImplSetSize( mnCount << 1 );
sal_uInt16 i, nPolyPos;
for( i = 0, nPolyPos = 0; i < ( mnCount - 1 ); i++ )
{
const sal_uInt8 cMove = mpCodes[ i ];
const sal_uInt8 cNextMove = mpCodes[ i + 1 ];
const ChainMove& rMove = aImplMove[ cMove ];
const ChainMove& rMoveInner = aImplMoveInner[ cMove ];
// Point& rPt = aArr[ nPolyPos ];
bool bDone = true;
nLastX += rMove.nDX;
nLastY += rMove.nDY;
if( cMove < 4 )
{
if( ( cMove == 0 && cNextMove == 3 ) ||
( cMove == 3 && cNextMove == 2 ) ||
( cMove == 2 && cNextMove == 1 ) ||
( cMove == 1 && cNextMove == 0 ) )
{
}
else if( cMove == 2 && cNextMove == 3 )
{
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY - 1 );
aArr[ nPolyPos ].setX( nLastX - 1 );
aArr[ nPolyPos++ ].setY( nLastY - 1 );
aArr[ nPolyPos ].setX( nLastX - 1 );
aArr[ nPolyPos++ ].setY( nLastY );
}
else if( cMove == 3 && cNextMove == 0 )
{
aArr[ nPolyPos ].setX( nLastX - 1 );
aArr[ nPolyPos++ ].setY( nLastY );
aArr[ nPolyPos ].setX( nLastX - 1 );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
}
else if( cMove == 0 && cNextMove == 1 )
{
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY );
}
else if( cMove == 1 && cNextMove == 2 )
{
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY - 1 );
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY - 1 );
}
else
bDone = false;
}
else if( cMove == 7 && cNextMove == 0 )
{
aArr[ nPolyPos ].setX( nLastX - 1 );
aArr[ nPolyPos++ ].setY( nLastY );
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
}
else if( cMove == 4 && cNextMove == 1 )
{
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY );
}
else
bDone = false;
if( !bDone )
{
aArr[ nPolyPos ].setX( nLastX + rMoveInner.nDX );
aArr[ nPolyPos++ ].setY( nLastY + rMoveInner.nDY );
}
}
aArr[ nPolyPos ].setX( nFirstX + 1 );
aArr[ nPolyPos++ ].setY( nFirstY + 1 );
aArr.ImplSetRealSize( nPolyPos );
}
else if( nFlag & VECT_POLY_INLINE_OUTER )
{
long nFirstX, nFirstY;
long nLastX, nLastY;
nFirstX = nLastX = maStartPt.X();
nFirstY = nLastY = maStartPt.Y();
aArr.ImplSetSize( mnCount << 1 );
sal_uInt16 i, nPolyPos;
for( i = 0, nPolyPos = 0; i < ( mnCount - 1 ); i++ )
{
const sal_uInt8 cMove = mpCodes[ i ];
const sal_uInt8 cNextMove = mpCodes[ i + 1 ];
const ChainMove& rMove = aImplMove[ cMove ];
const ChainMove& rMoveOuter = aImplMoveOuter[ cMove ];
// Point& rPt = aArr[ nPolyPos ];
bool bDone = true;
nLastX += rMove.nDX;
nLastY += rMove.nDY;
if( cMove < 4 )
{
if( ( cMove == 0 && cNextMove == 1 ) ||
( cMove == 1 && cNextMove == 2 ) ||
( cMove == 2 && cNextMove == 3 ) ||
( cMove == 3 && cNextMove == 0 ) )
{
}
else if( cMove == 0 && cNextMove == 3 )
{
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY - 1 );
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY - 1 );
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY );
}
else if( cMove == 3 && cNextMove == 2 )
{
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY );
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
}
else if( cMove == 2 && cNextMove == 1 )
{
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
aArr[ nPolyPos ].setX( nLastX - 1 );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
aArr[ nPolyPos ].setX( nLastX - 1 );
aArr[ nPolyPos++ ].setY( nLastY );
}
else if( cMove == 1 && cNextMove == 0 )
{
aArr[ nPolyPos ].setX( nLastX - 1 );
aArr[ nPolyPos++ ].setY( nLastY );
aArr[ nPolyPos ].setX( nLastX - 1 );
aArr[ nPolyPos++ ].setY( nLastY - 1 );
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY - 1 );
}
else
bDone = false;
}
else if( cMove == 7 && cNextMove == 3 )
{
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY - 1 );
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY );
}
else if( cMove == 6 && cNextMove == 2 )
{
aArr[ nPolyPos ].setX( nLastX + 1 );
aArr[ nPolyPos++ ].setY( nLastY );
aArr[ nPolyPos ].setX( nLastX );
aArr[ nPolyPos++ ].setY( nLastY + 1 );
}
else
bDone = false;
if( !bDone )
{
aArr[ nPolyPos ].setX( nLastX + rMoveOuter.nDX );
aArr[ nPolyPos++ ].setY( nLastY + rMoveOuter.nDY );
}
}
aArr[ nPolyPos ].setX( nFirstX - 1 );
aArr[ nPolyPos++ ].setY( nFirstY - 1 );
aArr.ImplSetRealSize( nPolyPos );
}
else
{
long nLastX = maStartPt.X(), nLastY = maStartPt.Y();
aArr.ImplSetSize( mnCount + 1 );
aArr[ 0 ] = Point( nLastX, nLastY );
for( sal_uLong i = 0; i < mnCount; )
{
const ChainMove& rMove = aImplMove[ mpCodes[ i ] ];
aArr[ ++i ] = Point( nLastX += rMove.nDX, nLastY += rMove.nDY );
}
aArr.ImplSetRealSize( mnCount + 1 );
}
ImplPostProcess( aArr );
}
else
maPoly.SetSize( 0 );
}
void ImplChain::ImplPostProcess( const ImplPointArray& rArr )
{
ImplPointArray aNewArr1;
ImplPointArray aNewArr2;
Point* pLast;
Point* pLeast;
sal_uLong nNewPos;
sal_uLong nCount = rArr.ImplGetRealSize();
sal_uLong n;
// pass 1
aNewArr1.ImplSetSize( nCount );
pLast = &( aNewArr1[ 0 ] );
pLast->setX( BACK_MAP( rArr[ 0 ].X() ) );
pLast->setY( BACK_MAP( rArr[ 0 ].Y() ) );
for( n = nNewPos = 1; n < nCount; )
{
const Point& rPt = rArr[ n++ ];
const long nX = BACK_MAP( rPt.X() );
const long nY = BACK_MAP( rPt.Y() );
if( nX != pLast->X() || nY != pLast->Y() )
{
pLast = pLeast = &( aNewArr1[ nNewPos++ ] );
pLeast->setX( nX );
pLeast->setY( nY );
}
}
aNewArr1.ImplSetRealSize( nCount = nNewPos );
// pass 2
aNewArr2.ImplSetSize( nCount );
pLast = &( aNewArr2[ 0 ] );
*pLast = aNewArr1[ 0 ];
for( n = nNewPos = 1; n < nCount; )
{
pLeast = &( aNewArr1[ n++ ] );
if( pLeast->X() == pLast->X() )
{
while( n < nCount && aNewArr1[ n ].X() == pLast->X() )
pLeast = &( aNewArr1[ n++ ] );
}
else if( pLeast->Y() == pLast->Y() )
{
while( n < nCount && aNewArr1[ n ].Y() == pLast->Y() )
pLeast = &( aNewArr1[ n++ ] );
}
aNewArr2[ nNewPos++ ] = *( pLast = pLeast );
}
aNewArr2.ImplSetRealSize( nNewPos );
aNewArr2.ImplCreatePoly( maPoly );
}
namespace ImplVectorizer {
bool ImplVectorize( const Bitmap& rColorBmp, GDIMetaFile& rMtf,
sal_uInt8 cReduce, const Link<long,void>* pProgress )
{
bool bRet = false;
VECT_PROGRESS( pProgress, 0 );
std::unique_ptr<Bitmap> xBmp(new Bitmap( rColorBmp ));
Bitmap::ScopedReadAccess pRAcc(*xBmp);
if( pRAcc )
{
tools::PolyPolygon aPolyPoly;
double fPercent = 0.0;
double fPercentStep_2 = 0.0;
const long nWidth = pRAcc->Width();
const long nHeight = pRAcc->Height();
const sal_uInt16 nColorCount = pRAcc->GetPaletteEntryCount();
sal_uInt16 n;
ImplColorSet* pColorSet = new ImplColorSet[ 256 ];
rMtf.Clear();
// get used palette colors and sort them from light to dark colors
for( n = 0; n < nColorCount; n++ )
{
pColorSet[ n ].mnIndex = n;
pColorSet[ n ].maColor = pRAcc->GetPaletteColor( n );
}
for( long nY = 0; nY < nHeight; nY++ )
{
Scanline pScanlineRead = pRAcc->GetScanline( nY );
for( long nX = 0; nX < nWidth; nX++ )
pColorSet[ pRAcc->GetIndexFromData( pScanlineRead, nX ) ].mbSet = true;
}
qsort( pColorSet, 256, sizeof( ImplColorSet ), ImplColorSetCmpFnc );
for( n = 0; n < 256; n++ )
if( !pColorSet[ n ].mbSet )
break;
if( n )
fPercentStep_2 = 45.0 / n;
VECT_PROGRESS( pProgress, FRound( fPercent += 10.0 ) );
for( sal_uInt16 i = 0; i < n; i++ )
{
const BitmapColor aBmpCol( pRAcc->GetPaletteColor( pColorSet[ i ].mnIndex ) );
const Color aFindColor( aBmpCol.GetRed(), aBmpCol.GetGreen(), aBmpCol.GetBlue() );
std::unique_ptr<ImplVectMap> xMap(ImplExpand( pRAcc.get(), aFindColor ));
VECT_PROGRESS( pProgress, FRound( fPercent += fPercentStep_2 ) );
if( xMap )
{
aPolyPoly.Clear();
ImplCalculate( xMap.get(), aPolyPoly, cReduce );
xMap.reset();
if( aPolyPoly.Count() )
{
ImplLimitPolyPoly( aPolyPoly );
aPolyPoly.Optimize( PolyOptimizeFlags::EDGES );
if( aPolyPoly.Count() )
{
rMtf.AddAction( new MetaLineColorAction( aFindColor, true ) );
rMtf.AddAction( new MetaFillColorAction( aFindColor, true ) );
rMtf.AddAction( new MetaPolyPolygonAction( aPolyPoly ) );
}
}
}
VECT_PROGRESS( pProgress, FRound( fPercent += fPercentStep_2 ) );
}
delete[] pColorSet;
if( rMtf.GetActionSize() )
{
MapMode aMap( MapUnit::Map100thMM );
ScopedVclPtrInstance< VirtualDevice > aVDev;
const Size aLogSize1( aVDev->PixelToLogic( Size( 1, 1 ), aMap ) );
rMtf.SetPrefMapMode( aMap );
rMtf.SetPrefSize( Size( nWidth + 2, nHeight + 2 ) );
rMtf.Move( 1, 1 );
rMtf.Scale( aLogSize1.Width(), aLogSize1.Height() );
bRet = true;
}
}
pRAcc.reset();
xBmp.reset();
VECT_PROGRESS( pProgress, 100 );
return bRet;
}
void ImplLimitPolyPoly( tools::PolyPolygon& rPolyPoly )
{
if( rPolyPoly.Count() > VECT_POLY_MAX )
{
tools::PolyPolygon aNewPolyPoly;
long nReduce = 0;
sal_uInt16 nNewCount;
do
{
aNewPolyPoly.Clear();
nReduce++;
for( sal_uInt16 i = 0, nCount = rPolyPoly.Count(); i < nCount; i++ )
{
const tools::Rectangle aBound( rPolyPoly[ i ].GetBoundRect() );
if( aBound.GetWidth() > nReduce && aBound.GetHeight() > nReduce )
{
if( rPolyPoly[ i ].GetSize() )
aNewPolyPoly.Insert( rPolyPoly[ i ] );
}
}
nNewCount = aNewPolyPoly.Count();
}
while( nNewCount > VECT_POLY_MAX );
rPolyPoly = aNewPolyPoly;
}
}
ImplVectMap* ImplExpand( BitmapReadAccess* pRAcc, const Color& rColor )
{
ImplVectMap* pMap = nullptr;
if( pRAcc && pRAcc->Width() && pRAcc->Height() )
{
const long nOldWidth = pRAcc->Width();
const long nOldHeight = pRAcc->Height();
const long nNewWidth = ( nOldWidth << 2 ) + 4;
const long nNewHeight = ( nOldHeight << 2 ) + 4;
const BitmapColor aTest( pRAcc->GetBestMatchingColor( rColor ) );
std::unique_ptr<long[]> pMapIn(new long[ std::max( nOldWidth, nOldHeight ) ]);
std::unique_ptr<long[]> pMapOut(new long[ std::max( nOldWidth, nOldHeight ) ]);
long nX, nY, nTmpX, nTmpY;
pMap = new ImplVectMap( nNewWidth, nNewHeight );
for( nX = 0; nX < nOldWidth; nX++ )
VECT_MAP( pMapIn, pMapOut, nX );
for( nY = 0, nTmpY = 5; nY < nOldHeight; nY++, nTmpY += 4 )
{
Scanline pScanlineRead = pRAcc->GetScanline( nY );
for( nX = 0; nX < nOldWidth; )
{
if( pRAcc->GetPixelFromData( pScanlineRead, nX ) == aTest )
{
nTmpX = pMapIn[ nX++ ];
nTmpY -= 3;
pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX );
pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX );
pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX );
pMap->Set( nTmpY, nTmpX, VECT_CONT_INDEX );
while( nX < nOldWidth && pRAcc->GetPixelFromData( pScanlineRead, nX ) == aTest )
nX++;
nTmpX = pMapOut[ nX - 1 ];
nTmpY -= 3;
pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX );
pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX );
pMap->Set( nTmpY++, nTmpX, VECT_CONT_INDEX );
pMap->Set( nTmpY, nTmpX, VECT_CONT_INDEX );
}
else
nX++;
}
}
for( nY = 0; nY < nOldHeight; nY++ )
VECT_MAP( pMapIn, pMapOut, nY );
for( nX = 0, nTmpX = 5; nX < nOldWidth; nX++, nTmpX += 4 )
{
for( nY = 0; nY < nOldHeight; )
{
if( pRAcc->GetPixel( nY, nX ) == aTest )
{
nTmpX -= 3;
nTmpY = pMapIn[ nY++ ];
pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX );
pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX );
pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX );
pMap->Set( nTmpY, nTmpX, VECT_CONT_INDEX );
while( nY < nOldHeight && pRAcc->GetPixel( nY, nX ) == aTest )
nY++;
nTmpX -= 3;
nTmpY = pMapOut[ nY - 1 ];
pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX );
pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX );
pMap->Set( nTmpY, nTmpX++, VECT_CONT_INDEX );
pMap->Set( nTmpY, nTmpX, VECT_CONT_INDEX );
}
else
nY++;
}
}
}
return pMap;
}
void ImplCalculate( ImplVectMap* pMap, tools::PolyPolygon& rPolyPoly, sal_uInt8 cReduce )
{
const long nWidth = pMap->Width(), nHeight= pMap->Height();
for( long nY = 0; nY < nHeight; nY++ )
{
long nX = 0;
bool bInner = true;
while( nX < nWidth )
{
// skip free
while( ( nX < nWidth ) && pMap->IsFree( nY, nX ) )
nX++;
if( nX == nWidth )
break;
if( pMap->IsCont( nY, nX ) )
{
// new contour
ImplChain aChain;
const Point aStartPt( nX++, nY );
// get chain code
aChain.ImplBeginAdd( aStartPt );
ImplGetChain( pMap, aStartPt, aChain );
aChain.ImplEndAdd( bInner ? VECT_POLY_OUTLINE_INNER : VECT_POLY_OUTLINE_OUTER );
const tools::Polygon& rPoly = aChain.ImplGetPoly();
if( rPoly.GetSize() > 2 )
{
if( cReduce )
{
const tools::Rectangle aBound( rPoly.GetBoundRect() );
if( aBound.GetWidth() > cReduce && aBound.GetHeight() > cReduce )
rPolyPoly.Insert( rPoly );
}
else
rPolyPoly.Insert( rPoly );
}
// skip rest of detected contour
while( pMap->IsCont( nY, nX ) )
nX++;
}
else
{
// process done segment
const long nStartSegX = nX++;
while( pMap->IsDone( nY, nX ) )
nX++;
if( ( ( nX - nStartSegX ) == 1 ) || ( ImplIsUp( pMap, nY, nStartSegX ) != ImplIsUp( pMap, nY, nX - 1 ) ) )
bInner = !bInner;
}
}
}
}
bool ImplGetChain( ImplVectMap* pMap, const Point& rStartPt, ImplChain& rChain )
{
long nActX = rStartPt.X();
long nActY = rStartPt.Y();
sal_uLong nFound;
sal_uLong nLastDir = 0;
sal_uLong nDir;
do
{
nFound = 0;
// first try last direction
long nTryX = nActX + aImplMove[ nLastDir ].nDX;
long nTryY = nActY + aImplMove[ nLastDir ].nDY;
if( pMap->IsCont( nTryY, nTryX ) )
{
rChain.ImplAdd( static_cast<sal_uInt8>(nLastDir) );
pMap->Set( nActY = nTryY, nActX = nTryX, VECT_DONE_INDEX );
nFound = 1;
}
else
{
// try other directions
for( nDir = 0; nDir < 8; nDir++ )
{
// we already tried nLastDir
if( nDir != nLastDir )
{
nTryX = nActX + aImplMove[ nDir ].nDX;
nTryY = nActY + aImplMove[ nDir ].nDY;
if( pMap->IsCont( nTryY, nTryX ) )
{
rChain.ImplAdd( static_cast<sal_uInt8>(nDir) );
pMap->Set( nActY = nTryY, nActX = nTryX, VECT_DONE_INDEX );
nFound = 1;
nLastDir = nDir;
break;
}
}
}
}
}
while( nFound );
return true;
}
bool ImplIsUp( ImplVectMap const * pMap, long nY, long nX )
{
if( pMap->IsDone( nY - 1, nX ) )
return true;
else if( pMap->IsDone( nY + 1, nX ) )
return false;
else if( pMap->IsDone( nY - 1, nX - 1 ) || pMap->IsDone( nY - 1, nX + 1 ) )
return true;
else
return false;
}
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V522 There might be dereferencing of a potential null pointer 'mpScan'. Check lines: 217, 209.