1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_basegfx.hxx"
31 #include <basegfx/polygon/b2dpolypolygonrasterconverter.hxx>
33 #include <basegfx/numeric/ftools.hxx>
34 #include <basegfx/polygon/b2dpolygon.hxx>
35 #include <basegfx/polygon/b2dpolygontools.hxx>
36 #include <basegfx/polygon/b2dpolypolygontools.hxx>
38 #include <boost/mem_fn.hpp>
49 //! default constructor
55 bool sort( const float *pInput
, sal_uInt32 nNumElements
, sal_uInt32 dwStride
);
57 inline sal_uInt32
*indices( void ) const { return m_indices1
; }
59 //! private attributes
62 // current size of index list
63 sal_uInt32 m_current_size
;
65 // last known size of index list
66 sal_uInt32 m_previous_size
;
69 sal_uInt32
*m_indices1
;
70 sal_uInt32
*m_indices2
;
72 sal_uInt32 m_counter
[256*4];
73 sal_uInt32 m_offset
[256];
78 bool resize( sal_uInt32 nNumElements
);
79 inline void reset_indices( void );
80 bool prepareCounters( const float *pInput
, sal_uInt32 nNumElements
, sal_uInt32 dwStride
);
83 inline radixSort::radixSort( void ) {
93 inline radixSort::~radixSort( void ) {
99 bool radixSort::resize( sal_uInt32 nNumElements
) {
101 if(nNumElements
==m_previous_size
)
104 if(nNumElements
> m_current_size
) {
106 // release index lists
108 delete [] m_indices2
;
110 delete [] m_indices1
;
112 // allocate new index lists
113 m_indices1
= new sal_uInt32
[nNumElements
];
114 m_indices2
= new sal_uInt32
[nNumElements
];
116 // check for out of memory situation
117 if(!m_indices1
|| !m_indices2
) {
118 delete [] m_indices1
;
119 delete [] m_indices2
;
126 m_current_size
= nNumElements
;
129 m_previous_size
= nNumElements
;
131 // initialize indices
137 inline void radixSort::reset_indices( void ) {
139 for(sal_uInt32 i
=0;i
<m_current_size
;i
++)
143 bool radixSort::prepareCounters( const float *pInput
, sal_uInt32 nNumElements
, sal_uInt32 dwStride
) {
146 sal_uInt32
*ptr
= m_counter
;
147 for(int i
=0; i
<64; ++i
)
167 // prepare pointers to relevant memory addresses
168 sal_uInt8
*p
= (sal_uInt8
*)pInput
;
169 sal_uInt8
*pe
= p
+(nNumElements
*dwStride
);
170 sal_uInt32
*h0
= &m_counter
[0];
171 sal_uInt32
*h1
= &m_counter
[256];
172 sal_uInt32
*h2
= &m_counter
[512];
173 sal_uInt32
*h3
= &m_counter
[768];
175 sal_uInt32
*Indices
= m_indices1
;
176 float previous_value
= *(float *)(((sal_uInt8
*)pInput
)+(m_indices1
[0]*dwStride
));
179 float value
= *(float *)(((sal_uInt8
*)pInput
)+((*Indices
++)*dwStride
));
180 if(value
<previous_value
) {
184 previous_value
= value
;
203 bool radixSort::sort( const float *pInput
, sal_uInt32 nNumElements
, sal_uInt32 dwStride
) {
209 if(!(resize(nNumElements
)))
212 // prepare radix counters, return if already sorted
213 if(prepareCounters(pInput
,nNumElements
,dwStride
))
216 // count number of negative values
217 sal_uInt32 num_negatives
= 0;
218 sal_uInt32
*h3
= &m_counter
[768];
219 for(sal_uInt32 i
=128;i
<256;i
++)
220 num_negatives
+= h3
[i
];
222 // perform passes, one for each byte
223 for(sal_uInt32 j
=0;j
<4;j
++) {
225 // ignore this pass if all values have the same byte
227 sal_uInt32
*current_counter
= &m_counter
[j
<<8];
228 sal_uInt8 unique_value
= *(((sal_uInt8
*)pInput
)+j
);
229 if(current_counter
[unique_value
]==nNumElements
)
232 // does the incoming byte contain the sign bit?
238 m_offset
[i
] = m_offset
[i
-1] + current_counter
[i
-1];
239 sal_uInt8
*InputBytes
= (sal_uInt8
*)pInput
;
240 sal_uInt32
*Indices
= m_indices1
;
241 sal_uInt32
*IndicesEnd
= &m_indices1
[nNumElements
];
243 while(Indices
!=IndicesEnd
) {
244 sal_uInt32 id
= *Indices
++;
245 m_indices2
[m_offset
[InputBytes
[id
*dwStride
]]++] = id
;
247 sal_uInt32
*Tmp
= m_indices1
;
248 m_indices1
= m_indices2
;
254 m_offset
[0] = num_negatives
;
256 m_offset
[i
] = m_offset
[i
-1] + current_counter
[i
-1];
259 m_offset
[254-i
] = m_offset
[255-i
] + current_counter
[255-i
];
261 m_offset
[i
] += current_counter
[i
];
262 for(i
=0;i
<nNumElements
;i
++) {
263 sal_uInt32 Radix
= (*(sal_uInt32
*)(((sal_uInt8
*)pInput
)+(m_indices1
[i
]*dwStride
)))>>24;
264 if(Radix
<128) m_indices2
[m_offset
[Radix
]++] = m_indices1
[i
];
265 else m_indices2
[--m_offset
[Radix
]] = m_indices1
[i
];
267 sal_uInt32
*Tmp
= m_indices1
;
268 m_indices1
= m_indices2
;
272 if(unique_value
>=128) {
273 for(i
=0;i
<nNumElements
;i
++)
274 m_indices2
[i
] = m_indices1
[nNumElements
-i
-1];
275 sal_uInt32
*Tmp
= m_indices1
;
276 m_indices1
= m_indices2
;
286 //************************************************************
287 // Internal vertex storage of B2DPolyPolygonRasterConverter
288 //************************************************************
290 inline B2DPolyPolygonRasterConverter::Vertex::Vertex() :
297 inline B2DPolyPolygonRasterConverter::Vertex::Vertex( const B2DPoint
& rP1
, const B2DPoint
& rP2
, bool bDown
) :
305 //************************************************************
306 // Helper class for holding horizontal line segments during raster
308 //************************************************************
315 sal_Int32 mnYCounter
;
321 /**rP1 and rP2 must not have equal y values, when rounded
324 ImplLineNode(const B2DPoint
& rP1
, const B2DPoint
& rP2
, bool bDown
) :
325 mnYCounter( fround(rP2
.getY()) - fround(rP1
.getY()) ),
326 mfXPos( (float)(rP1
.getX()) ),
327 mfXDelta((float) ((rP2
.getX() - rP1
.getX()) / mnYCounter
) ),
332 /// get current x position
333 const float& getXPos() const
338 /// returns true, if line ends on this Y value
354 return mnYCounter
<=0;
364 typedef ::std::vector
<ImplLineNode
> VectorOfLineNodes
;
367 //************************************************************
368 // Base2D PolyPolygon Raster Converter (Rasterizer)
369 //************************************************************
373 struct VertexComparator
375 bool operator()( const B2DPolyPolygonRasterConverter::Vertex
& rLHS
,
376 const B2DPolyPolygonRasterConverter::Vertex
& rRHS
)
378 return rLHS
.aP1
.getX() < rRHS
.aP1
.getX();
383 void B2DPolyPolygonRasterConverter::init()
385 if(!maPolyPolyRectangle
.isEmpty())
387 const sal_Int32
nMinY( fround(maPolyPolyRectangle
.getMinY()) );
388 const sal_Int32
nScanlines(fround(maPolyPolyRectangle
.getMaxY()) - nMinY
);
390 maScanlines
.resize( nScanlines
+1 );
393 for( sal_uInt32
i(0), nCount(maPolyPolygon
.count());
398 const B2DPolygon
& rPoly( maPolyPolygon
.getB2DPolygon(i
) );
399 for( sal_uInt32
k(0), nVertices(rPoly
.count());
403 const B2DPoint
& rP1( rPoly
.getB2DPoint(k
) );
404 const B2DPoint
& rP2( rPoly
.getB2DPoint( (k
+ 1) % nVertices
) );
406 const sal_Int32
nVertexYP1( fround(rP1
.getY()) );
407 const sal_Int32
nVertexYP2( fround(rP2
.getY()) );
409 // insert only vertices which are not strictly
410 // horizontal. Note that the ImplLineNode relies on
412 if(nVertexYP1
!= nVertexYP2
)
414 if( nVertexYP2
< nVertexYP1
)
416 const sal_Int32
nStartScanline(nVertexYP2
- nMinY
);
419 maScanlines
[ nStartScanline
].push_back( Vertex(rP2
, rP1
, false) );
423 const sal_Int32
nStartScanline(nVertexYP1
- nMinY
);
425 maScanlines
[ nStartScanline
].push_back( Vertex(rP1
, rP2
, true) );
431 // now sort all scanlines, with increasing x coordinates
432 VectorOfVertexVectors::iterator
aIter( maScanlines
.begin() );
433 VectorOfVertexVectors::iterator
aEnd( maScanlines
.end() );
434 while( aIter
!= aEnd
)
436 ::std::sort( aIter
->begin(),
438 VertexComparator() );
444 B2DPolyPolygonRasterConverter::B2DPolyPolygonRasterConverter( const B2DPolyPolygon
& rPolyPoly
) :
445 maPolyPolygon( rPolyPoly
),
446 maPolyPolyRectangle( tools::getRange( rPolyPoly
) ),
454 B2DRectangle
getCombinedBounds( const B2DPolyPolygon
& rPolyPolyRaster
,
455 const B2DRectangle
& rRasterArea
)
457 B2DRectangle
aRect( tools::getRange( rPolyPolyRaster
) );
458 aRect
.expand( rRasterArea
);
464 B2DPolyPolygonRasterConverter::B2DPolyPolygonRasterConverter( const B2DPolyPolygon
& rPolyPolyRaster
,
465 const B2DRectangle
& rRasterArea
) :
466 maPolyPolygon( rPolyPolyRaster
),
468 getCombinedBounds( rPolyPolyRaster
,
475 B2DPolyPolygonRasterConverter::~B2DPolyPolygonRasterConverter()
481 class LineNodeGenerator
484 LineNodeGenerator( VectorOfLineNodes
& rActiveVertices
) :
485 mrActiveVertices( rActiveVertices
)
489 void operator()( const B2DPolyPolygonRasterConverter::Vertex
& rVertex
)
491 mrActiveVertices
.push_back( ImplLineNode(rVertex
.aP1
,
493 rVertex
.bDownwards
) );
497 VectorOfLineNodes
& mrActiveVertices
;
500 struct LineNodeComparator
502 bool operator()( const ImplLineNode
& rLHS
, const ImplLineNode
& rRHS
)
504 return rLHS
.getXPos() < rRHS
.getXPos();
509 void B2DPolyPolygonRasterConverter::rasterConvert( FillRule eFillRule
)
511 if( maScanlines
.empty() )
512 return; // no scanlines at all -> bail out
514 const sal_Int32
nMinY( fround(maPolyPolyRectangle
.getMinY()) );
515 const sal_Int32
nScanlines(fround(maPolyPolyRectangle
.getMaxY()) - nMinY
);
517 // Vector of currently active vertices. A vertex is active, if
518 // it crosses or touches the current scanline.
519 VectorOfLineNodes aActiveVertices
;
521 // mickey's optimized version...
524 std::size_t nb_previous(0);
527 // process each scanline
528 for( sal_Int32
y(0); y
<= nScanlines
; ++y
)
530 // add vertices which start at current scanline into
531 // active vertex vector
532 ::std::for_each( maScanlines
[y
].begin(),
533 maScanlines
[y
].end(),
534 LineNodeGenerator( aActiveVertices
) );
535 nb
= aActiveVertices
.size();
536 if(nb
!= nb_previous
)
542 // sort with increasing X
549 rs
.sort(&aActiveVertices
[0].mfXPos
,
551 sizeof(ImplLineNode
));
555 const std::size_t nLen( nb
);
558 // empty scanline - call derived with an 'off' span
559 // for the full width
560 span( maPolyPolyRectangle
.getMinX(),
561 maPolyPolyRectangle
.getMaxX(),
567 const sal_Int32
nCurrY( nMinY
+ y
);
569 // scanline not empty - forward all scans to derived,
570 // according to selected fill rule
572 // TODO(P1): Maybe allow these 'off' span calls to be
573 // switched off (or all 'on' span calls, depending on
574 // use case scenario)
576 // sorting didn't change the order of the elements
577 // in memory but prepared a list of indices in sorted order.
578 // thus we now process the nodes with an additional indirection.
579 sal_uInt32
*sorted
= rs
.indices();
581 // call derived with 'off' span for everything left of first active span
582 if( aActiveVertices
[sorted
[0]].getXPos() > maPolyPolyRectangle
.getMinX() )
584 span( maPolyPolyRectangle
.getMinX(),
585 aActiveVertices
[sorted
[0]].getXPos(),
594 "B2DPolyPolygonRasterConverter::rasterConvert(): Unexpected fill rule");
597 case FillRule_EVEN_ODD
:
598 // process each span in current scanline, with
599 // even-odd fill rule
600 for( ::std::size_t i(0), nLength(aActiveVertices
.size());
604 sal_uInt32 nIndex
= sorted
[i
];
605 sal_uInt32 nNextIndex
= sorted
[i
+1];
606 span( aActiveVertices
[nIndex
].getXPos(),
607 aActiveVertices
[nNextIndex
].getXPos(),
611 float delta
= aActiveVertices
[nIndex
].nextLine();
614 if(aActiveVertices
[nIndex
].getXPos() > aActiveVertices
[nNextIndex
].getXPos())
617 else if(delta
< 0.0f
)
621 sal_uInt32 nPrevIndex
= sorted
[i
-1];
622 if(aActiveVertices
[nIndex
].getXPos() < aActiveVertices
[nPrevIndex
].getXPos())
629 case FillRule_NONZERO_WINDING_NUMBER
:
630 // process each span in current scanline, with
631 // non-zero winding numbe fill rule
632 sal_Int32
nWindingNumber(0);
633 for( ::std::size_t i(0), nLength(aActiveVertices
.size());
637 sal_uInt32 nIndex
= sorted
[i
];
638 sal_uInt32 nNextIndex
= sorted
[i
+1];
639 nWindingNumber
+= -1 + 2*aActiveVertices
[nIndex
].isDownwards();
641 span( aActiveVertices
[nIndex
].getXPos(),
642 aActiveVertices
[nNextIndex
].getXPos(),
644 nWindingNumber
!= 0 );
646 float delta
= aActiveVertices
[nIndex
].nextLine();
649 if(aActiveVertices
[nIndex
].getXPos() > aActiveVertices
[nNextIndex
].getXPos())
652 else if(delta
< 0.0f
)
656 sal_uInt32 nPrevIndex
= sorted
[i
-1];
657 if(aActiveVertices
[nIndex
].getXPos() < aActiveVertices
[nPrevIndex
].getXPos())
665 // call derived with 'off' span for everything right of last active span
666 if( aActiveVertices
[sorted
[nb
-1]].getXPos()+1.0 < maPolyPolyRectangle
.getMaxX() )
668 span( aActiveVertices
[sorted
[nb
-1]].getXPos()+1.0,
669 maPolyPolyRectangle
.getMaxX(),
674 // also call nextLine on very last line node
675 sal_uInt32 nIndex
= sorted
[nb
-1];
676 float delta
= aActiveVertices
[nIndex
].nextLine();
681 sal_uInt32 nPrevIndex
= sorted
[nb
-2];
682 if(aActiveVertices
[nIndex
].getXPos() < aActiveVertices
[nPrevIndex
].getXPos())
688 // remove line nodes which have ended on the current scanline
689 aActiveVertices
.erase( ::std::remove_if( aActiveVertices
.begin(),
690 aActiveVertices
.end(),
691 ::boost::mem_fn( &ImplLineNode::isEnded
) ),
692 aActiveVertices
.end() );
693 nb
= aActiveVertices
.size();
694 if(nb
!= nb_previous
)