tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / vcl / source / bitmap / Vectorizer.cxx
blobf9ffa6ef618d175d57c485601483357810192b2c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
29 #include <array>
30 #include <memory>
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;
53 namespace {
55 class ImplVectMap;
56 class ImplChain;
60 namespace vcl
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 );
69 namespace {
71 struct ChainMove { tools::Long nDX; tools::Long nDY; };
75 const ChainMove aImplMove[ 8 ] = {
76 { 1, 0 },
77 { 0, -1 },
78 { -1, 0 },
79 { 0, 1 },
80 { 1, -1 },
81 { -1, -1 },
82 { -1, 1 },
83 { 1, 1 }
86 const ChainMove aImplMoveInner[ 8 ] = {
87 { 0, 1 },
88 { 1, 0 },
89 { 0, -1 },
90 { -1, 0 },
91 { 0, 1 },
92 { 1, 0 },
93 { 0, -1 },
94 { -1, 0 }
97 const ChainMove aImplMoveOuter[ 8 ] = {
98 { 0, -1 },
99 { -1, 0 },
100 { 0, 1 },
101 { 1, 0 },
102 { -1, 0 },
103 { 0, 1 },
104 { 1, 0 },
105 { 0, -1 }
108 namespace {
110 struct ImplColorSet
112 BitmapColor maColor;
113 sal_uInt16 mnIndex = 0;
114 bool mbSet = false;
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;
130 namespace {
132 class ImplPointArray
134 std::unique_ptr<Point[]> mpArray;
135 sal_uLong mnSize;
136 sal_uLong mnRealSize;
138 public:
140 ImplPointArray();
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() :
155 mnSize ( 0 ),
156 mnRealSize ( 0 )
161 void ImplPointArray::ImplSetSize( sal_uLong nSize )
163 const sal_uLong nTotal = nSize * sizeof( Point );
165 mnSize = nSize;
166 mnRealSize = 0;
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() );
188 namespace {
190 class ImplVectMap
192 private:
194 Scanline mpBuf;
195 Scanline* mpScan;
196 tools::Long mnWidth;
197 tools::Long mnHeight;
199 public:
201 ImplVectMap( tools::Long nWidth, tools::Long nHeight );
202 ~ImplVectMap();
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))) ),
221 mnWidth ( nWidth ),
222 mnHeight( nHeight )
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()
235 std::free( mpBuf );
236 std::free( mpScan );
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 ) );
266 namespace {
268 class ImplChain
270 private:
272 tools::Polygon maPoly;
273 Point maStartPt;
274 sal_uLong mnArraySize;
275 sal_uLong mnCount;
276 std::unique_ptr<sal_uInt8[]>
277 mpCodes;
279 void ImplGetSpace();
281 void ImplPostProcess( const ImplPointArray& rArr );
283 ImplChain(const ImplChain&) = delete;
284 ImplChain& operator=(const ImplChain&) = delete;
286 public:
288 ImplChain();
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 ),
301 mnCount ( 0 ),
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;
321 mnCount = 0;
324 inline void ImplChain::ImplAdd( sal_uInt8 nCode )
326 if( mnCount == mnArraySize )
327 ImplGetSpace();
329 mpCodes[ mnCount++ ] = nCode;
332 void ImplChain::ImplEndAdd( sal_uLong nFlag )
334 if( mnCount )
336 ImplPointArray aArr;
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 );
347 sal_uInt16 nPolyPos;
348 sal_uLong i;
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 ];
356 bool bDone = true;
358 nLastX += rMove.nDX;
359 nLastY += rMove.nDY;
361 if( cMove < 4 )
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 );
413 else
414 bDone = false;
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 );
432 else
433 bDone = false;
435 if( !bDone )
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 );
455 sal_uInt16 nPolyPos;
456 sal_uLong i;
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 ];
464 bool bDone = true;
466 nLastX += rMove.nDX;
467 nLastY += rMove.nDY;
469 if( cMove < 4 )
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 );
521 else
522 bDone = false;
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 );
540 else
541 bDone = false;
543 if( !bDone )
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 );
554 else
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 ] ];
564 nLastX += rMove.nDX;
565 nLastY += rMove.nDY;
566 aArr[ ++i ] = Point( nLastX, nLastY );
569 aArr.ImplSetRealSize( mnCount + 1 );
572 ImplPostProcess( aArr );
574 else
575 maPoly.SetSize( 0 );
578 void ImplChain::ImplPostProcess( const ImplPointArray& rArr )
580 ImplPointArray aNewArr1;
581 ImplPointArray aNewArr2;
582 Point* pLast;
583 Point* pLeast;
584 sal_uLong nNewPos;
585 sal_uLong nCount = rArr.ImplGetRealSize();
586 sal_uLong n;
588 // pass 1
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++ ] );
603 pLeast->setX( nX );
604 pLeast->setY( nY );
608 nCount = nNewPos;
609 aNewArr1.ImplSetRealSize( nCount );
611 // pass 2
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++ ] );
631 pLast = pLeast;
632 aNewArr2[ nNewPos++ ] = *pLast;
635 aNewArr2.ImplSetRealSize( nNewPos );
636 aNewArr2.ImplCreatePoly( maPoly );
639 namespace vcl
641 void Vectorizer::updateProgress(tools::Long nProgress)
643 if (mpProgress)
644 mpProgress->Call(nProgress);
647 bool Vectorizer::vectorize(BitmapEx const& rBitmap, GDIMetaFile& rMetafile)
649 bool bRet = false;
651 updateProgress(0);
653 std::optional<Bitmap> xBmp(std::in_place, rBitmap.GetBitmap());
654 BitmapScopedReadAccess pRAcc(*xBmp);
656 if( pRAcc )
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();
663 sal_uInt16 n;
664 std::array<ImplColorSet, 256> aColorSet;
666 rMetafile.Clear();
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 )
686 break;
688 if( n )
689 fPercentStep_2 = 45.0 / n;
691 fPercent += 10.0;
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));
704 if( oMap )
706 tools::PolyPolygon aPolyPoly;
707 ImplCalculate(*oMap, aPolyPoly, mnReduce);
708 oMap.reset();
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() );
739 bRet = true;
743 pRAcc.reset();
744 xBmp.reset();
745 updateProgress(100);
747 return bRet;
750 void ImplLimitPolyPoly( tools::PolyPolygon& rPolyPoly )
752 if( rPolyPoly.Count() <= VECT_POLY_MAX )
753 return;
755 tools::PolyPolygon aNewPolyPoly;
756 tools::Long nReduce = 0;
757 sal_uInt16 nNewCount;
761 aNewPolyPoly.Clear();
762 nReduce++;
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() )
785 return;
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++ ];
809 nTmpY -= 3;
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 )
817 nX++;
819 nTmpX = pMapOut[ nX - 1 ];
820 nTmpY -= 3;
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 );
827 else
828 nX++;
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 )
841 nTmpX -= 3;
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 )
850 nY++;
852 nTmpX -= 3;
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 );
860 else
861 nY++;
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++ )
872 tools::Long nX = 0;
873 bool bInner = true;
875 while( nX < nWidth )
877 // skip free
878 while( ( nX < nWidth ) && rMap.IsFree( nY, nX ) )
879 nX++;
881 if( nX == nWidth )
882 break;
884 if( rMap.IsCont( nY, nX ) )
886 // new contour
887 ImplChain aChain;
888 const Point aStartPt( nX++, nY );
890 // get chain code
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 )
900 if( cReduce )
902 const tools::Rectangle aBound( rPoly.GetBoundRect() );
904 if( aBound.GetWidth() > cReduce && aBound.GetHeight() > cReduce )
905 rPolyPoly.Insert( rPoly );
907 else
908 rPolyPoly.Insert( rPoly );
911 // skip rest of detected contour
912 while( rMap.IsCont( nY, nX ) )
913 nX++;
915 else
917 // process done segment
918 const tools::Long nStartSegX = nX++;
920 while( rMap.IsDone( nY, nX ) )
921 nX++;
923 if( ( ( nX - nStartSegX ) == 1 ) || ( ImplIsUp( rMap, nY, nStartSegX ) != ImplIsUp( rMap, nY, nX - 1 ) ) )
924 bInner = !bInner;
930 bool ImplGetChain( ImplVectMap& rMap, const Point& rStartPt, ImplChain& rChain )
932 tools::Long nActX = rStartPt.X();
933 tools::Long nActY = rStartPt.Y();
934 sal_uLong nFound;
935 sal_uLong nLastDir = 0;
936 sal_uLong nDir;
940 nFound = 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) );
949 nActY = nTryY;
950 nActX = nTryX;
951 rMap.Set( nActY, nActX, VECT_DONE_INDEX );
952 nFound = 1;
954 else
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) );
968 nActY = nTryY;
969 nActX = nTryX;
970 rMap.Set( nActY, nActX, VECT_DONE_INDEX );
971 nFound = 1;
972 nLastDir = nDir;
973 break;
979 while( nFound );
981 return true;
984 bool ImplIsUp( ImplVectMap const & rMap, tools::Long nY, tools::Long nX )
986 if( rMap.IsDone( nY - 1, nX ) )
987 return true;
988 else if( rMap.IsDone( nY + 1, nX ) )
989 return false;
990 else if( rMap.IsDone( nY - 1, nX - 1 ) || rMap.IsDone( nY - 1, nX + 1 ) )
991 return true;
992 else
993 return false;
998 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */