1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: b2dpolypolygonrasterconverter.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_basegfx.hxx"
34 #include <basegfx/polygon/b2dpolypolygonrasterconverter.hxx>
36 #include <basegfx/numeric/ftools.hxx>
37 #include <basegfx/polygon/b2dpolygon.hxx>
38 #include <basegfx/polygon/b2dpolygontools.hxx>
39 #include <basegfx/polygon/b2dpolypolygontools.hxx>
41 #include <boost/mem_fn.hpp>
52 //! default constructor
58 bool sort( const float *pInput
, sal_uInt32 nNumElements
, sal_uInt32 dwStride
);
60 inline sal_uInt32
*indices( void ) const { return m_indices1
; }
62 //! private attributes
65 // current size of index list
66 sal_uInt32 m_current_size
;
68 // last known size of index list
69 sal_uInt32 m_previous_size
;
72 sal_uInt32
*m_indices1
;
73 sal_uInt32
*m_indices2
;
75 sal_uInt32 m_counter
[256*4];
76 sal_uInt32 m_offset
[256];
81 bool resize( sal_uInt32 nNumElements
);
82 inline void reset_indices( void );
83 bool prepareCounters( const float *pInput
, sal_uInt32 nNumElements
, sal_uInt32 dwStride
);
86 inline radixSort::radixSort( void ) {
96 inline radixSort::~radixSort( void ) {
102 bool radixSort::resize( sal_uInt32 nNumElements
) {
104 if(nNumElements
==m_previous_size
)
107 if(nNumElements
> m_current_size
) {
109 // release index lists
111 delete [] m_indices2
;
113 delete [] m_indices1
;
115 // allocate new index lists
116 m_indices1
= new sal_uInt32
[nNumElements
];
117 m_indices2
= new sal_uInt32
[nNumElements
];
119 // check for out of memory situation
120 if(!m_indices1
|| !m_indices2
) {
121 delete [] m_indices1
;
122 delete [] m_indices2
;
129 m_current_size
= nNumElements
;
132 m_previous_size
= nNumElements
;
134 // initialize indices
140 inline void radixSort::reset_indices( void ) {
142 for(sal_uInt32 i
=0;i
<m_current_size
;i
++)
146 bool radixSort::prepareCounters( const float *pInput
, sal_uInt32 nNumElements
, sal_uInt32 dwStride
) {
149 sal_uInt32
*ptr
= m_counter
;
150 for(int i
=0; i
<64; ++i
)
170 // prepare pointers to relevant memory addresses
171 sal_uInt8
*p
= (sal_uInt8
*)pInput
;
172 sal_uInt8
*pe
= p
+(nNumElements
*dwStride
);
173 sal_uInt32
*h0
= &m_counter
[0];
174 sal_uInt32
*h1
= &m_counter
[256];
175 sal_uInt32
*h2
= &m_counter
[512];
176 sal_uInt32
*h3
= &m_counter
[768];
178 sal_uInt32
*Indices
= m_indices1
;
179 float previous_value
= *(float *)(((sal_uInt8
*)pInput
)+(m_indices1
[0]*dwStride
));
182 float value
= *(float *)(((sal_uInt8
*)pInput
)+((*Indices
++)*dwStride
));
183 if(value
<previous_value
) {
187 previous_value
= value
;
206 bool radixSort::sort( const float *pInput
, sal_uInt32 nNumElements
, sal_uInt32 dwStride
) {
212 if(!(resize(nNumElements
)))
215 // prepare radix counters, return if already sorted
216 if(prepareCounters(pInput
,nNumElements
,dwStride
))
219 // count number of negative values
220 sal_uInt32 num_negatives
= 0;
221 sal_uInt32
*h3
= &m_counter
[768];
222 for(sal_uInt32 i
=128;i
<256;i
++)
223 num_negatives
+= h3
[i
];
225 // perform passes, one for each byte
226 for(sal_uInt32 j
=0;j
<4;j
++) {
228 // ignore this pass if all values have the same byte
230 sal_uInt32
*current_counter
= &m_counter
[j
<<8];
231 sal_uInt8 unique_value
= *(((sal_uInt8
*)pInput
)+j
);
232 if(current_counter
[unique_value
]==nNumElements
)
235 // does the incoming byte contain the sign bit?
241 m_offset
[i
] = m_offset
[i
-1] + current_counter
[i
-1];
242 sal_uInt8
*InputBytes
= (sal_uInt8
*)pInput
;
243 sal_uInt32
*Indices
= m_indices1
;
244 sal_uInt32
*IndicesEnd
= &m_indices1
[nNumElements
];
246 while(Indices
!=IndicesEnd
) {
247 sal_uInt32 id
= *Indices
++;
248 m_indices2
[m_offset
[InputBytes
[id
*dwStride
]]++] = id
;
250 sal_uInt32
*Tmp
= m_indices1
;
251 m_indices1
= m_indices2
;
257 m_offset
[0] = num_negatives
;
259 m_offset
[i
] = m_offset
[i
-1] + current_counter
[i
-1];
262 m_offset
[254-i
] = m_offset
[255-i
] + current_counter
[255-i
];
264 m_offset
[i
] += current_counter
[i
];
265 for(i
=0;i
<nNumElements
;i
++) {
266 sal_uInt32 Radix
= (*(sal_uInt32
*)(((sal_uInt8
*)pInput
)+(m_indices1
[i
]*dwStride
)))>>24;
267 if(Radix
<128) m_indices2
[m_offset
[Radix
]++] = m_indices1
[i
];
268 else m_indices2
[--m_offset
[Radix
]] = m_indices1
[i
];
270 sal_uInt32
*Tmp
= m_indices1
;
271 m_indices1
= m_indices2
;
275 if(unique_value
>=128) {
276 for(i
=0;i
<nNumElements
;i
++)
277 m_indices2
[i
] = m_indices1
[nNumElements
-i
-1];
278 sal_uInt32
*Tmp
= m_indices1
;
279 m_indices1
= m_indices2
;
289 //************************************************************
290 // Internal vertex storage of B2DPolyPolygonRasterConverter
291 //************************************************************
293 inline B2DPolyPolygonRasterConverter::Vertex::Vertex() :
300 inline B2DPolyPolygonRasterConverter::Vertex::Vertex( const B2DPoint
& rP1
, const B2DPoint
& rP2
, bool bDown
) :
308 //************************************************************
309 // Helper class for holding horizontal line segments during raster
311 //************************************************************
318 sal_Int32 mnYCounter
;
324 /**rP1 and rP2 must not have equal y values, when rounded
327 ImplLineNode(const B2DPoint
& rP1
, const B2DPoint
& rP2
, bool bDown
) :
328 mnYCounter( fround(rP2
.getY()) - fround(rP1
.getY()) ),
329 mfXPos( (float)(rP1
.getX()) ),
330 mfXDelta((float) ((rP2
.getX() - rP1
.getX()) / mnYCounter
) ),
335 /// get current x position
336 const float& getXPos() const
341 /// returns true, if line ends on this Y value
357 return mnYCounter
<=0;
367 typedef ::std::vector
<ImplLineNode
> VectorOfLineNodes
;
370 //************************************************************
371 // Base2D PolyPolygon Raster Converter (Rasterizer)
372 //************************************************************
376 struct VertexComparator
378 bool operator()( const B2DPolyPolygonRasterConverter::Vertex
& rLHS
,
379 const B2DPolyPolygonRasterConverter::Vertex
& rRHS
)
381 return rLHS
.aP1
.getX() < rRHS
.aP1
.getX();
386 void B2DPolyPolygonRasterConverter::init()
388 if(!maPolyPolyRectangle
.isEmpty())
390 const sal_Int32
nMinY( fround(maPolyPolyRectangle
.getMinY()) );
391 const sal_Int32
nScanlines(fround(maPolyPolyRectangle
.getMaxY()) - nMinY
);
393 maScanlines
.resize( nScanlines
+1 );
396 for( sal_uInt32
i(0), nCount(maPolyPolygon
.count());
401 const B2DPolygon
& rPoly( maPolyPolygon
.getB2DPolygon(i
) );
402 for( sal_uInt32
k(0), nVertices(rPoly
.count());
406 const B2DPoint
& rP1( rPoly
.getB2DPoint(k
) );
407 const B2DPoint
& rP2( rPoly
.getB2DPoint( (k
+ 1) % nVertices
) );
409 const sal_Int32
nVertexYP1( fround(rP1
.getY()) );
410 const sal_Int32
nVertexYP2( fround(rP2
.getY()) );
412 // insert only vertices which are not strictly
413 // horizontal. Note that the ImplLineNode relies on
415 if(nVertexYP1
!= nVertexYP2
)
417 if( nVertexYP2
< nVertexYP1
)
419 const sal_Int32
nStartScanline(nVertexYP2
- nMinY
);
422 maScanlines
[ nStartScanline
].push_back( Vertex(rP2
, rP1
, false) );
426 const sal_Int32
nStartScanline(nVertexYP1
- nMinY
);
428 maScanlines
[ nStartScanline
].push_back( Vertex(rP1
, rP2
, true) );
434 // now sort all scanlines, with increasing x coordinates
435 VectorOfVertexVectors::iterator
aIter( maScanlines
.begin() );
436 VectorOfVertexVectors::iterator
aEnd( maScanlines
.end() );
437 while( aIter
!= aEnd
)
439 ::std::sort( aIter
->begin(),
441 VertexComparator() );
447 B2DPolyPolygonRasterConverter::B2DPolyPolygonRasterConverter( const B2DPolyPolygon
& rPolyPoly
) :
448 maPolyPolygon( rPolyPoly
),
449 maPolyPolyRectangle( tools::getRange( rPolyPoly
) ),
457 B2DRectangle
getCombinedBounds( const B2DPolyPolygon
& rPolyPolyRaster
,
458 const B2DRectangle
& rRasterArea
)
460 B2DRectangle
aRect( tools::getRange( rPolyPolyRaster
) );
461 aRect
.expand( rRasterArea
);
467 B2DPolyPolygonRasterConverter::B2DPolyPolygonRasterConverter( const B2DPolyPolygon
& rPolyPolyRaster
,
468 const B2DRectangle
& rRasterArea
) :
469 maPolyPolygon( rPolyPolyRaster
),
471 getCombinedBounds( rPolyPolyRaster
,
478 B2DPolyPolygonRasterConverter::~B2DPolyPolygonRasterConverter()
484 class LineNodeGenerator
487 LineNodeGenerator( VectorOfLineNodes
& rActiveVertices
) :
488 mrActiveVertices( rActiveVertices
)
492 void operator()( const B2DPolyPolygonRasterConverter::Vertex
& rVertex
)
494 mrActiveVertices
.push_back( ImplLineNode(rVertex
.aP1
,
496 rVertex
.bDownwards
) );
500 VectorOfLineNodes
& mrActiveVertices
;
503 struct LineNodeComparator
505 bool operator()( const ImplLineNode
& rLHS
, const ImplLineNode
& rRHS
)
507 return rLHS
.getXPos() < rRHS
.getXPos();
512 void B2DPolyPolygonRasterConverter::rasterConvert( FillRule eFillRule
)
514 if( maScanlines
.empty() )
515 return; // no scanlines at all -> bail out
517 const sal_Int32
nMinY( fround(maPolyPolyRectangle
.getMinY()) );
518 const sal_Int32
nScanlines(fround(maPolyPolyRectangle
.getMaxY()) - nMinY
);
520 // Vector of currently active vertices. A vertex is active, if
521 // it crosses or touches the current scanline.
522 VectorOfLineNodes aActiveVertices
;
524 // mickey's optimized version...
527 std::size_t nb_previous(0);
530 // process each scanline
531 for( sal_Int32
y(0); y
<= nScanlines
; ++y
)
533 // add vertices which start at current scanline into
534 // active vertex vector
535 ::std::for_each( maScanlines
[y
].begin(),
536 maScanlines
[y
].end(),
537 LineNodeGenerator( aActiveVertices
) );
538 nb
= aActiveVertices
.size();
539 if(nb
!= nb_previous
)
545 // sort with increasing X
552 rs
.sort(&aActiveVertices
[0].mfXPos
,
554 sizeof(ImplLineNode
));
558 const std::size_t nLen( nb
);
561 // empty scanline - call derived with an 'off' span
562 // for the full width
563 span( maPolyPolyRectangle
.getMinX(),
564 maPolyPolyRectangle
.getMaxX(),
570 const sal_Int32
nCurrY( nMinY
+ y
);
572 // scanline not empty - forward all scans to derived,
573 // according to selected fill rule
575 // TODO(P1): Maybe allow these 'off' span calls to be
576 // switched off (or all 'on' span calls, depending on
577 // use case scenario)
579 // sorting didn't change the order of the elements
580 // in memory but prepared a list of indices in sorted order.
581 // thus we now process the nodes with an additional indirection.
582 sal_uInt32
*sorted
= rs
.indices();
584 // call derived with 'off' span for everything left of first active span
585 if( aActiveVertices
[sorted
[0]].getXPos() > maPolyPolyRectangle
.getMinX() )
587 span( maPolyPolyRectangle
.getMinX(),
588 aActiveVertices
[sorted
[0]].getXPos(),
597 "B2DPolyPolygonRasterConverter::rasterConvert(): Unexpected fill rule");
600 case FillRule_EVEN_ODD
:
601 // process each span in current scanline, with
602 // even-odd fill rule
603 for( ::std::size_t i(0), nLength(aActiveVertices
.size());
607 sal_uInt32 nIndex
= sorted
[i
];
608 sal_uInt32 nNextIndex
= sorted
[i
+1];
609 span( aActiveVertices
[nIndex
].getXPos(),
610 aActiveVertices
[nNextIndex
].getXPos(),
614 float delta
= aActiveVertices
[nIndex
].nextLine();
617 if(aActiveVertices
[nIndex
].getXPos() > aActiveVertices
[nNextIndex
].getXPos())
620 else if(delta
< 0.0f
)
624 sal_uInt32 nPrevIndex
= sorted
[i
-1];
625 if(aActiveVertices
[nIndex
].getXPos() < aActiveVertices
[nPrevIndex
].getXPos())
632 case FillRule_NONZERO_WINDING_NUMBER
:
633 // process each span in current scanline, with
634 // non-zero winding numbe fill rule
635 sal_Int32
nWindingNumber(0);
636 for( ::std::size_t i(0), nLength(aActiveVertices
.size());
640 sal_uInt32 nIndex
= sorted
[i
];
641 sal_uInt32 nNextIndex
= sorted
[i
+1];
642 nWindingNumber
+= -1 + 2*aActiveVertices
[nIndex
].isDownwards();
644 span( aActiveVertices
[nIndex
].getXPos(),
645 aActiveVertices
[nNextIndex
].getXPos(),
647 nWindingNumber
!= 0 );
649 float delta
= aActiveVertices
[nIndex
].nextLine();
652 if(aActiveVertices
[nIndex
].getXPos() > aActiveVertices
[nNextIndex
].getXPos())
655 else if(delta
< 0.0f
)
659 sal_uInt32 nPrevIndex
= sorted
[i
-1];
660 if(aActiveVertices
[nIndex
].getXPos() < aActiveVertices
[nPrevIndex
].getXPos())
668 // call derived with 'off' span for everything right of last active span
669 if( aActiveVertices
[sorted
[nb
-1]].getXPos()+1.0 < maPolyPolyRectangle
.getMaxX() )
671 span( aActiveVertices
[sorted
[nb
-1]].getXPos()+1.0,
672 maPolyPolyRectangle
.getMaxX(),
677 // also call nextLine on very last line node
678 sal_uInt32 nIndex
= sorted
[nb
-1];
679 float delta
= aActiveVertices
[nIndex
].nextLine();
684 sal_uInt32 nPrevIndex
= sorted
[nb
-2];
685 if(aActiveVertices
[nIndex
].getXPos() < aActiveVertices
[nPrevIndex
].getXPos())
691 // remove line nodes which have ended on the current scanline
692 aActiveVertices
.erase( ::std::remove_if( aActiveVertices
.begin(),
693 aActiveVertices
.end(),
694 ::boost::mem_fn( &ImplLineNode::isEnded
) ),
695 aActiveVertices
.end() );
696 nb
= aActiveVertices
.size();
697 if(nb
!= nb_previous
)