1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/log.hxx>
21 #include <vcl/BitmapReadAccess.hxx>
22 #include <tools/link.hxx>
23 #include <tools/poly.hxx>
24 #include <tools/helpers.hxx>
25 #include <vcl/gdimtf.hxx>
26 #include <vcl/metaact.hxx>
27 #include <vcl/virdev.hxx>
28 #include <vcl/bitmap/Vectorizer.hxx>
32 #define VECT_POLY_MAX 8192
34 #define VECT_FREE_INDEX 0
35 #define VECT_CONT_INDEX 1
36 #define VECT_DONE_INDEX 2
38 #define VECT_POLY_INLINE_INNER 1UL
39 #define VECT_POLY_INLINE_OUTER 2UL
40 #define VECT_POLY_OUTLINE_INNER 4UL
41 #define VECT_POLY_OUTLINE_OUTER 8UL
43 static void VECT_MAP( const std::unique_ptr
<sal_Int32
[]> & pMapIn
, const std::unique_ptr
<sal_Int32
[]>& pMapOut
, tools::Long nVal
)
45 pMapIn
[nVal
] = (nVal
* 4) + 1;
46 pMapOut
[nVal
] = pMapIn
[nVal
] + 5;
48 static constexpr tools::Long
BACK_MAP( tools::Long _def_nVal
)
50 return ((_def_nVal
+ 2) >> 2) - 1;
62 static void ImplExpand( std::optional
<ImplVectMap
>& rMap
, const BitmapReadAccess
* pRAcc
, const Color
& rColor
);
63 static void ImplCalculate( ImplVectMap
& rMap
, tools::PolyPolygon
& rPolyPoly
, sal_uInt8 cReduce
);
64 static bool ImplGetChain( ImplVectMap
& rMap
, const Point
& rStartPt
, ImplChain
& rChain
);
65 static bool ImplIsUp( ImplVectMap
const & rMap
, tools::Long nY
, tools::Long nX
);
66 static void ImplLimitPolyPoly( tools::PolyPolygon
& rPolyPoly
);
71 struct ChainMove
{ tools::Long nDX
; tools::Long nDY
; };
75 const ChainMove aImplMove
[ 8 ] = {
86 const ChainMove aImplMoveInner
[ 8 ] = {
97 const ChainMove aImplMoveOuter
[ 8 ] = {
113 sal_uInt16 mnIndex
= 0;
119 static bool ImplColorSetCmpFnc( const ImplColorSet
& lhs
, const ImplColorSet
& rhs
)
121 if( lhs
.mbSet
&& rhs
.mbSet
)
123 const sal_uInt8 cLum1
= lhs
.maColor
.GetLuminance();
124 const sal_uInt8 cLum2
= rhs
.maColor
.GetLuminance();
125 return cLum1
< cLum2
;
127 return lhs
.mbSet
> rhs
.mbSet
;
134 std::unique_ptr
<Point
[]> mpArray
;
136 sal_uLong mnRealSize
;
142 void ImplSetSize( sal_uLong nSize
);
143 sal_uLong
ImplGetRealSize() const { return mnRealSize
; }
144 void ImplSetRealSize( sal_uLong nRealSize
) { mnRealSize
= nRealSize
; }
145 void ImplCreatePoly( tools::Polygon
& rPoly
) const;
147 inline Point
& operator[]( sal_uLong nPos
);
148 inline const Point
& operator[]( sal_uLong nPos
) const;
154 ImplPointArray::ImplPointArray() :
161 void ImplPointArray::ImplSetSize( sal_uLong nSize
)
163 const sal_uLong nTotal
= nSize
* sizeof( Point
);
168 mpArray
= std::make_unique
<Point
[]>( nTotal
);
171 inline Point
& ImplPointArray::operator[]( sal_uLong nPos
)
173 SAL_WARN_IF( nPos
>= mnSize
, "vcl", "ImplPointArray::operator[]: nPos out of range!" );
174 return mpArray
[ nPos
];
177 inline const Point
& ImplPointArray::operator[]( sal_uLong nPos
) const
179 SAL_WARN_IF( nPos
>= mnSize
, "vcl", "ImplPointArray::operator[]: nPos out of range!" );
180 return mpArray
[ nPos
];
183 void ImplPointArray::ImplCreatePoly( tools::Polygon
& rPoly
) const
185 rPoly
= tools::Polygon( sal::static_int_cast
<sal_uInt16
>(mnRealSize
), mpArray
.get() );
197 tools::Long mnHeight
;
201 ImplVectMap( tools::Long nWidth
, tools::Long nHeight
);
204 tools::Long
Width() const { return mnWidth
; }
205 tools::Long
Height() const { return mnHeight
; }
207 inline void Set( tools::Long nY
, tools::Long nX
, sal_uInt8 cVal
);
208 inline sal_uInt8
Get( tools::Long nY
, tools::Long nX
) const;
210 inline bool IsFree( tools::Long nY
, tools::Long nX
) const;
211 inline bool IsCont( tools::Long nY
, tools::Long nX
) const;
212 inline bool IsDone( tools::Long nY
, tools::Long nX
) const;
218 ImplVectMap::ImplVectMap( tools::Long nWidth
, tools::Long nHeight
) :
219 mpBuf ( static_cast<Scanline
>(rtl_allocateZeroMemory(nWidth
* nHeight
)) ),
220 mpScan ( static_cast<Scanline
*>(std::malloc(nHeight
* sizeof(Scanline
))) ),
224 assert(mpScan
&& "Don't handle OOM conditions");
226 const tools::Long nWidthAl
= ( nWidth
>> 2 ) + 1;
227 Scanline pTmp
= mpBuf
;
229 for( tools::Long nY
= 0; nY
< nHeight
; pTmp
+= nWidthAl
)
230 mpScan
[ nY
++ ] = pTmp
;
233 ImplVectMap::~ImplVectMap()
239 inline void ImplVectMap::Set( tools::Long nY
, tools::Long nX
, sal_uInt8 cVal
)
241 const sal_uInt8 cShift
= sal::static_int_cast
<sal_uInt8
>(6 - ( ( nX
& 3 ) << 1 ));
242 auto & rPixel
= mpScan
[ nY
][ nX
>> 2 ];
243 rPixel
= (rPixel
& ~( 3 << cShift
) ) | ( cVal
<< cShift
);
246 inline sal_uInt8
ImplVectMap::Get( tools::Long nY
, tools::Long nX
) const
248 return sal::static_int_cast
<sal_uInt8
>( ( ( mpScan
[ nY
][ nX
>> 2 ] ) >> ( 6 - ( ( nX
& 3 ) << 1 ) ) ) & 3 );
251 inline bool ImplVectMap::IsFree( tools::Long nY
, tools::Long nX
) const
253 return( VECT_FREE_INDEX
== Get( nY
, nX
) );
256 inline bool ImplVectMap::IsCont( tools::Long nY
, tools::Long nX
) const
258 return( VECT_CONT_INDEX
== Get( nY
, nX
) );
261 inline bool ImplVectMap::IsDone( tools::Long nY
, tools::Long nX
) const
263 return( VECT_DONE_INDEX
== Get( nY
, nX
) );
272 tools::Polygon maPoly
;
274 sal_uLong mnArraySize
;
276 std::unique_ptr
<sal_uInt8
[]>
281 void ImplPostProcess( const ImplPointArray
& rArr
);
283 ImplChain(const ImplChain
&) = delete;
284 ImplChain
& operator=(const ImplChain
&) = delete;
290 void ImplBeginAdd( const Point
& rStartPt
);
291 inline void ImplAdd( sal_uInt8 nCode
);
292 void ImplEndAdd( sal_uLong nTypeFlag
);
294 const tools::Polygon
& ImplGetPoly() const { return maPoly
; }
299 ImplChain::ImplChain() :
300 mnArraySize ( 1024 ),
302 mpCodes ( new sal_uInt8
[mnArraySize
] )
306 void ImplChain::ImplGetSpace()
308 const sal_uLong nOldArraySize
= mnArraySize
;
309 sal_uInt8
* pNewCodes
;
311 mnArraySize
= mnArraySize
<< 1;
312 pNewCodes
= new sal_uInt8
[ mnArraySize
];
313 memcpy( pNewCodes
, mpCodes
.get(), nOldArraySize
);
314 mpCodes
.reset( pNewCodes
);
317 void ImplChain::ImplBeginAdd( const Point
& rStartPt
)
319 maPoly
= tools::Polygon();
320 maStartPt
= rStartPt
;
324 inline void ImplChain::ImplAdd( sal_uInt8 nCode
)
326 if( mnCount
== mnArraySize
)
329 mpCodes
[ mnCount
++ ] = nCode
;
332 void ImplChain::ImplEndAdd( sal_uLong nFlag
)
338 if( nFlag
& VECT_POLY_INLINE_INNER
)
340 tools::Long nFirstX
, nFirstY
;
341 tools::Long nLastX
, nLastY
;
343 nFirstX
= nLastX
= maStartPt
.X();
344 nFirstY
= nLastY
= maStartPt
.Y();
345 aArr
.ImplSetSize( mnCount
<< 1 );
349 for( i
= 0, nPolyPos
= 0; i
< ( mnCount
- 1 ); i
++ )
351 const sal_uInt8 cMove
= mpCodes
[ i
];
352 const sal_uInt8 cNextMove
= mpCodes
[ i
+ 1 ];
353 const ChainMove
& rMove
= aImplMove
[ cMove
];
354 const ChainMove
& rMoveInner
= aImplMoveInner
[ cMove
];
355 // Point& rPt = aArr[ nPolyPos ];
363 if( ( cMove
== 0 && cNextMove
== 3 ) ||
364 ( cMove
== 3 && cNextMove
== 2 ) ||
365 ( cMove
== 2 && cNextMove
== 1 ) ||
366 ( cMove
== 1 && cNextMove
== 0 ) )
369 else if( cMove
== 2 && cNextMove
== 3 )
371 aArr
[ nPolyPos
].setX( nLastX
);
372 aArr
[ nPolyPos
++ ].setY( nLastY
- 1 );
374 aArr
[ nPolyPos
].setX( nLastX
- 1 );
375 aArr
[ nPolyPos
++ ].setY( nLastY
- 1 );
377 aArr
[ nPolyPos
].setX( nLastX
- 1 );
378 aArr
[ nPolyPos
++ ].setY( nLastY
);
380 else if( cMove
== 3 && cNextMove
== 0 )
382 aArr
[ nPolyPos
].setX( nLastX
- 1 );
383 aArr
[ nPolyPos
++ ].setY( nLastY
);
385 aArr
[ nPolyPos
].setX( nLastX
- 1 );
386 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
388 aArr
[ nPolyPos
].setX( nLastX
);
389 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
391 else if( cMove
== 0 && cNextMove
== 1 )
393 aArr
[ nPolyPos
].setX( nLastX
);
394 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
396 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
397 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
399 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
400 aArr
[ nPolyPos
++ ].setY( nLastY
);
402 else if( cMove
== 1 && cNextMove
== 2 )
404 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
405 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
407 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
408 aArr
[ nPolyPos
++ ].setY( nLastY
- 1 );
410 aArr
[ nPolyPos
].setX( nLastX
);
411 aArr
[ nPolyPos
++ ].setY( nLastY
- 1 );
416 else if( cMove
== 7 && cNextMove
== 0 )
418 aArr
[ nPolyPos
].setX( nLastX
- 1 );
419 aArr
[ nPolyPos
++ ].setY( nLastY
);
421 aArr
[ nPolyPos
].setX( nLastX
);
422 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
424 else if( cMove
== 4 && cNextMove
== 1 )
426 aArr
[ nPolyPos
].setX( nLastX
);
427 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
429 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
430 aArr
[ nPolyPos
++ ].setY( nLastY
);
437 aArr
[ nPolyPos
].setX( nLastX
+ rMoveInner
.nDX
);
438 aArr
[ nPolyPos
++ ].setY( nLastY
+ rMoveInner
.nDY
);
442 aArr
[ nPolyPos
].setX( nFirstX
+ 1 );
443 aArr
[ nPolyPos
++ ].setY( nFirstY
+ 1 );
444 aArr
.ImplSetRealSize( nPolyPos
);
446 else if( nFlag
& VECT_POLY_INLINE_OUTER
)
448 tools::Long nFirstX
, nFirstY
;
449 tools::Long nLastX
, nLastY
;
451 nFirstX
= nLastX
= maStartPt
.X();
452 nFirstY
= nLastY
= maStartPt
.Y();
453 aArr
.ImplSetSize( mnCount
<< 1 );
457 for( i
= 0, nPolyPos
= 0; i
< ( mnCount
- 1 ); i
++ )
459 const sal_uInt8 cMove
= mpCodes
[ i
];
460 const sal_uInt8 cNextMove
= mpCodes
[ i
+ 1 ];
461 const ChainMove
& rMove
= aImplMove
[ cMove
];
462 const ChainMove
& rMoveOuter
= aImplMoveOuter
[ cMove
];
463 // Point& rPt = aArr[ nPolyPos ];
471 if( ( cMove
== 0 && cNextMove
== 1 ) ||
472 ( cMove
== 1 && cNextMove
== 2 ) ||
473 ( cMove
== 2 && cNextMove
== 3 ) ||
474 ( cMove
== 3 && cNextMove
== 0 ) )
477 else if( cMove
== 0 && cNextMove
== 3 )
479 aArr
[ nPolyPos
].setX( nLastX
);
480 aArr
[ nPolyPos
++ ].setY( nLastY
- 1 );
482 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
483 aArr
[ nPolyPos
++ ].setY( nLastY
- 1 );
485 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
486 aArr
[ nPolyPos
++ ].setY( nLastY
);
488 else if( cMove
== 3 && cNextMove
== 2 )
490 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
491 aArr
[ nPolyPos
++ ].setY( nLastY
);
493 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
494 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
496 aArr
[ nPolyPos
].setX( nLastX
);
497 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
499 else if( cMove
== 2 && cNextMove
== 1 )
501 aArr
[ nPolyPos
].setX( nLastX
);
502 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
504 aArr
[ nPolyPos
].setX( nLastX
- 1 );
505 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
507 aArr
[ nPolyPos
].setX( nLastX
- 1 );
508 aArr
[ nPolyPos
++ ].setY( nLastY
);
510 else if( cMove
== 1 && cNextMove
== 0 )
512 aArr
[ nPolyPos
].setX( nLastX
- 1 );
513 aArr
[ nPolyPos
++ ].setY( nLastY
);
515 aArr
[ nPolyPos
].setX( nLastX
- 1 );
516 aArr
[ nPolyPos
++ ].setY( nLastY
- 1 );
518 aArr
[ nPolyPos
].setX( nLastX
);
519 aArr
[ nPolyPos
++ ].setY( nLastY
- 1 );
524 else if( cMove
== 7 && cNextMove
== 3 )
526 aArr
[ nPolyPos
].setX( nLastX
);
527 aArr
[ nPolyPos
++ ].setY( nLastY
- 1 );
529 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
530 aArr
[ nPolyPos
++ ].setY( nLastY
);
532 else if( cMove
== 6 && cNextMove
== 2 )
534 aArr
[ nPolyPos
].setX( nLastX
+ 1 );
535 aArr
[ nPolyPos
++ ].setY( nLastY
);
537 aArr
[ nPolyPos
].setX( nLastX
);
538 aArr
[ nPolyPos
++ ].setY( nLastY
+ 1 );
545 aArr
[ nPolyPos
].setX( nLastX
+ rMoveOuter
.nDX
);
546 aArr
[ nPolyPos
++ ].setY( nLastY
+ rMoveOuter
.nDY
);
550 aArr
[ nPolyPos
].setX( nFirstX
- 1 );
551 aArr
[ nPolyPos
++ ].setY( nFirstY
- 1 );
552 aArr
.ImplSetRealSize( nPolyPos
);
556 tools::Long nLastX
= maStartPt
.X(), nLastY
= maStartPt
.Y();
558 aArr
.ImplSetSize( mnCount
+ 1 );
559 aArr
[ 0 ] = Point( nLastX
, nLastY
);
561 for( sal_uLong i
= 0; i
< mnCount
; )
563 const ChainMove
& rMove
= aImplMove
[ mpCodes
[ i
] ];
566 aArr
[ ++i
] = Point( nLastX
, nLastY
);
569 aArr
.ImplSetRealSize( mnCount
+ 1 );
572 ImplPostProcess( aArr
);
578 void ImplChain::ImplPostProcess( const ImplPointArray
& rArr
)
580 ImplPointArray aNewArr1
;
581 ImplPointArray aNewArr2
;
585 sal_uLong nCount
= rArr
.ImplGetRealSize();
589 aNewArr1
.ImplSetSize( nCount
);
590 pLast
= &( aNewArr1
[ 0 ] );
591 pLast
->setX( BACK_MAP( rArr
[ 0 ].X() ) );
592 pLast
->setY( BACK_MAP( rArr
[ 0 ].Y() ) );
594 for( n
= nNewPos
= 1; n
< nCount
; )
596 const Point
& rPt
= rArr
[ n
++ ];
597 const tools::Long nX
= BACK_MAP( rPt
.X() );
598 const tools::Long nY
= BACK_MAP( rPt
.Y() );
600 if( nX
!= pLast
->X() || nY
!= pLast
->Y() )
602 pLast
= pLeast
= &( aNewArr1
[ nNewPos
++ ] );
609 aNewArr1
.ImplSetRealSize( nCount
);
612 aNewArr2
.ImplSetSize( nCount
);
613 pLast
= &( aNewArr2
[ 0 ] );
614 *pLast
= aNewArr1
[ 0 ];
616 for( n
= nNewPos
= 1; n
< nCount
; )
618 pLeast
= &( aNewArr1
[ n
++ ] );
620 if( pLeast
->X() == pLast
->X() )
622 while( n
< nCount
&& aNewArr1
[ n
].X() == pLast
->X() )
623 pLeast
= &( aNewArr1
[ n
++ ] );
625 else if( pLeast
->Y() == pLast
->Y() )
627 while( n
< nCount
&& aNewArr1
[ n
].Y() == pLast
->Y() )
628 pLeast
= &( aNewArr1
[ n
++ ] );
632 aNewArr2
[ nNewPos
++ ] = *pLast
;
635 aNewArr2
.ImplSetRealSize( nNewPos
);
636 aNewArr2
.ImplCreatePoly( maPoly
);
641 void Vectorizer::updateProgress(tools::Long nProgress
)
644 mpProgress
->Call(nProgress
);
647 bool Vectorizer::vectorize(BitmapEx
const& rBitmap
, GDIMetaFile
& rMetafile
)
653 std::optional
<Bitmap
> xBmp(std::in_place
, rBitmap
.GetBitmap());
654 BitmapScopedReadAccess
pRAcc(*xBmp
);
658 double fPercent
= 0.0;
659 double fPercentStep_2
= 0.0;
660 const tools::Long nWidth
= pRAcc
->Width();
661 const tools::Long nHeight
= pRAcc
->Height();
662 const sal_uInt16 nColorCount
= pRAcc
->GetPaletteEntryCount();
664 std::array
<ImplColorSet
, 256> aColorSet
;
668 // get used palette colors and sort them from light to dark colors
669 for( n
= 0; n
< nColorCount
; n
++ )
671 aColorSet
[ n
].mnIndex
= n
;
672 aColorSet
[ n
].maColor
= pRAcc
->GetPaletteColor( n
);
675 for( tools::Long nY
= 0; nY
< nHeight
; nY
++ )
677 Scanline pScanlineRead
= pRAcc
->GetScanline( nY
);
678 for( tools::Long nX
= 0; nX
< nWidth
; nX
++ )
679 aColorSet
[ pRAcc
->GetIndexFromData( pScanlineRead
, nX
) ].mbSet
= true;
682 std::sort( aColorSet
.begin(), aColorSet
.end(), ImplColorSetCmpFnc
);
684 for( n
= 0; n
< 256; n
++ )
685 if( !aColorSet
[ n
].mbSet
)
689 fPercentStep_2
= 45.0 / n
;
692 updateProgress(basegfx::fround
<tools::Long
>(fPercent
));
694 for( sal_uInt16 i
= 0; i
< n
; i
++ )
696 const BitmapColor
aBmpCol( pRAcc
->GetPaletteColor( aColorSet
[ i
].mnIndex
) );
697 const Color
aFindColor( aBmpCol
.GetRed(), aBmpCol
.GetGreen(), aBmpCol
.GetBlue() );
698 std::optional
<ImplVectMap
> oMap
;
699 ImplExpand( oMap
, pRAcc
.get(), aFindColor
);
701 fPercent
+= fPercentStep_2
;
702 updateProgress(basegfx::fround
<tools::Long
>(fPercent
));
706 tools::PolyPolygon aPolyPoly
;
707 ImplCalculate(*oMap
, aPolyPoly
, mnReduce
);
710 if( aPolyPoly
.Count() )
712 ImplLimitPolyPoly( aPolyPoly
);
714 aPolyPoly
.Optimize( PolyOptimizeFlags::EDGES
);
716 if( aPolyPoly
.Count() )
718 rMetafile
.AddAction( new MetaLineColorAction( aFindColor
, true ) );
719 rMetafile
.AddAction( new MetaFillColorAction( aFindColor
, true ) );
720 rMetafile
.AddAction( new MetaPolyPolygonAction( std::move(aPolyPoly
) ) );
725 fPercent
+= fPercentStep_2
;
726 updateProgress(basegfx::fround
<tools::Long
>(fPercent
));
729 if (rMetafile
.GetActionSize())
731 MapMode
aMap( MapUnit::Map100thMM
);
732 ScopedVclPtrInstance
< VirtualDevice
> aVDev
;
733 const Size
aLogSize1( aVDev
->PixelToLogic( Size( 1, 1 ), aMap
) );
735 rMetafile
.SetPrefMapMode( aMap
);
736 rMetafile
.SetPrefSize( Size( nWidth
+ 2, nHeight
+ 2 ) );
737 rMetafile
.Move( 1, 1 );
738 rMetafile
.Scale( aLogSize1
.Width(), aLogSize1
.Height() );
750 void ImplLimitPolyPoly( tools::PolyPolygon
& rPolyPoly
)
752 if( rPolyPoly
.Count() <= VECT_POLY_MAX
)
755 tools::PolyPolygon aNewPolyPoly
;
756 tools::Long nReduce
= 0;
757 sal_uInt16 nNewCount
;
761 aNewPolyPoly
.Clear();
764 for( sal_uInt16 i
= 0, nCount
= rPolyPoly
.Count(); i
< nCount
; i
++ )
766 const tools::Rectangle
aBound( rPolyPoly
[ i
].GetBoundRect() );
768 if( aBound
.GetWidth() > nReduce
&& aBound
.GetHeight() > nReduce
)
770 if( rPolyPoly
[ i
].GetSize() )
771 aNewPolyPoly
.Insert( rPolyPoly
[ i
] );
775 nNewCount
= aNewPolyPoly
.Count();
777 while( nNewCount
> VECT_POLY_MAX
);
779 rPolyPoly
= std::move(aNewPolyPoly
);
782 void ImplExpand( std::optional
<ImplVectMap
>& oMap
, const BitmapReadAccess
* pRAcc
, const Color
& rColor
)
784 if( !pRAcc
|| !pRAcc
->Width() || !pRAcc
->Height() )
787 const tools::Long nOldWidth
= pRAcc
->Width();
788 const tools::Long nOldHeight
= pRAcc
->Height();
789 const tools::Long nNewWidth
= ( nOldWidth
<< 2 ) + 4;
790 const tools::Long nNewHeight
= ( nOldHeight
<< 2 ) + 4;
791 const BitmapColor
aTest( pRAcc
->GetBestMatchingColor( rColor
) );
792 std::unique_ptr
<sal_Int32
[]> pMapIn(new sal_Int32
[ std::max( nOldWidth
, nOldHeight
) ]);
793 std::unique_ptr
<sal_Int32
[]> pMapOut(new sal_Int32
[ std::max( nOldWidth
, nOldHeight
) ]);
794 tools::Long nX
, nY
, nTmpX
, nTmpY
;
796 oMap
.emplace( nNewWidth
, nNewHeight
);
798 for( nX
= 0; nX
< nOldWidth
; nX
++ )
799 VECT_MAP( pMapIn
, pMapOut
, nX
);
801 for( nY
= 0, nTmpY
= 5; nY
< nOldHeight
; nY
++, nTmpY
+= 4 )
803 Scanline pScanlineRead
= pRAcc
->GetScanline( nY
);
804 for( nX
= 0; nX
< nOldWidth
; )
806 if( pRAcc
->GetPixelFromData( pScanlineRead
, nX
) == aTest
)
808 nTmpX
= pMapIn
[ nX
++ ];
811 oMap
->Set( nTmpY
++, nTmpX
, VECT_CONT_INDEX
);
812 oMap
->Set( nTmpY
++, nTmpX
, VECT_CONT_INDEX
);
813 oMap
->Set( nTmpY
++, nTmpX
, VECT_CONT_INDEX
);
814 oMap
->Set( nTmpY
, nTmpX
, VECT_CONT_INDEX
);
816 while( nX
< nOldWidth
&& pRAcc
->GetPixelFromData( pScanlineRead
, nX
) == aTest
)
819 nTmpX
= pMapOut
[ nX
- 1 ];
822 oMap
->Set( nTmpY
++, nTmpX
, VECT_CONT_INDEX
);
823 oMap
->Set( nTmpY
++, nTmpX
, VECT_CONT_INDEX
);
824 oMap
->Set( nTmpY
++, nTmpX
, VECT_CONT_INDEX
);
825 oMap
->Set( nTmpY
, nTmpX
, VECT_CONT_INDEX
);
832 for( nY
= 0; nY
< nOldHeight
; nY
++ )
833 VECT_MAP( pMapIn
, pMapOut
, nY
);
835 for( nX
= 0, nTmpX
= 5; nX
< nOldWidth
; nX
++, nTmpX
+= 4 )
837 for( nY
= 0; nY
< nOldHeight
; )
839 if( pRAcc
->GetPixel( nY
, nX
) == aTest
)
842 nTmpY
= pMapIn
[ nY
++ ];
844 oMap
->Set( nTmpY
, nTmpX
++, VECT_CONT_INDEX
);
845 oMap
->Set( nTmpY
, nTmpX
++, VECT_CONT_INDEX
);
846 oMap
->Set( nTmpY
, nTmpX
++, VECT_CONT_INDEX
);
847 oMap
->Set( nTmpY
, nTmpX
, VECT_CONT_INDEX
);
849 while( nY
< nOldHeight
&& pRAcc
->GetPixel( nY
, nX
) == aTest
)
853 nTmpY
= pMapOut
[ nY
- 1 ];
855 oMap
->Set( nTmpY
, nTmpX
++, VECT_CONT_INDEX
);
856 oMap
->Set( nTmpY
, nTmpX
++, VECT_CONT_INDEX
);
857 oMap
->Set( nTmpY
, nTmpX
++, VECT_CONT_INDEX
);
858 oMap
->Set( nTmpY
, nTmpX
, VECT_CONT_INDEX
);
866 void ImplCalculate( ImplVectMap
& rMap
, tools::PolyPolygon
& rPolyPoly
, sal_uInt8 cReduce
)
868 const tools::Long nWidth
= rMap
.Width(), nHeight
= rMap
.Height();
870 for( tools::Long nY
= 0; nY
< nHeight
; nY
++ )
878 while( ( nX
< nWidth
) && rMap
.IsFree( nY
, nX
) )
884 if( rMap
.IsCont( nY
, nX
) )
888 const Point
aStartPt( nX
++, nY
);
891 aChain
.ImplBeginAdd( aStartPt
);
892 ImplGetChain( rMap
, aStartPt
, aChain
);
894 aChain
.ImplEndAdd( bInner
? VECT_POLY_OUTLINE_INNER
: VECT_POLY_OUTLINE_OUTER
);
896 const tools::Polygon
& rPoly
= aChain
.ImplGetPoly();
898 if( rPoly
.GetSize() > 2 )
902 const tools::Rectangle
aBound( rPoly
.GetBoundRect() );
904 if( aBound
.GetWidth() > cReduce
&& aBound
.GetHeight() > cReduce
)
905 rPolyPoly
.Insert( rPoly
);
908 rPolyPoly
.Insert( rPoly
);
911 // skip rest of detected contour
912 while( rMap
.IsCont( nY
, nX
) )
917 // process done segment
918 const tools::Long nStartSegX
= nX
++;
920 while( rMap
.IsDone( nY
, nX
) )
923 if( ( ( nX
- nStartSegX
) == 1 ) || ( ImplIsUp( rMap
, nY
, nStartSegX
) != ImplIsUp( rMap
, nY
, nX
- 1 ) ) )
930 bool ImplGetChain( ImplVectMap
& rMap
, const Point
& rStartPt
, ImplChain
& rChain
)
932 tools::Long nActX
= rStartPt
.X();
933 tools::Long nActY
= rStartPt
.Y();
935 sal_uLong nLastDir
= 0;
942 // first try last direction
943 tools::Long nTryX
= nActX
+ aImplMove
[ nLastDir
].nDX
;
944 tools::Long nTryY
= nActY
+ aImplMove
[ nLastDir
].nDY
;
946 if( rMap
.IsCont( nTryY
, nTryX
) )
948 rChain
.ImplAdd( static_cast<sal_uInt8
>(nLastDir
) );
951 rMap
.Set( nActY
, nActX
, VECT_DONE_INDEX
);
956 // try other directions
957 for( nDir
= 0; nDir
< 8; nDir
++ )
959 // we already tried nLastDir
960 if( nDir
!= nLastDir
)
962 nTryX
= nActX
+ aImplMove
[ nDir
].nDX
;
963 nTryY
= nActY
+ aImplMove
[ nDir
].nDY
;
965 if( rMap
.IsCont( nTryY
, nTryX
) )
967 rChain
.ImplAdd( static_cast<sal_uInt8
>(nDir
) );
970 rMap
.Set( nActY
, nActX
, VECT_DONE_INDEX
);
984 bool ImplIsUp( ImplVectMap
const & rMap
, tools::Long nY
, tools::Long nX
)
986 if( rMap
.IsDone( nY
- 1, nX
) )
988 else if( rMap
.IsDone( nY
+ 1, nX
) )
990 else if( rMap
.IsDone( nY
- 1, nX
- 1 ) || rMap
.IsDone( nY
- 1, nX
+ 1 ) )
998 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */