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 #ifndef INCLUDED_BASEBMP_POLYPOLYGONRENDERER_HXX
21 #define INCLUDED_BASEBMP_POLYPOLYGONRENDERER_HXX
23 #include <basegfx/point/b2dpoint.hxx>
24 #include <basegfx/range/b2drange.hxx>
25 #include <basegfx/range/b2ibox.hxx>
26 #include <basegfx/polygon/b2dpolypolygon.hxx>
27 #include <basegfx/polygon/b2dpolypolygontools.hxx>
28 #include <basegfx/polygon/b2dpolypolygonfillrule.hxx>
29 #include <basegfx/numeric/ftools.hxx>
31 #include <vigra/diff2d.hxx>
32 #include <vigra/iteratortraits.hxx>
41 /// convert int32 to 32:32 fixed point
42 inline sal_Int64
toFractional( sal_Int32 v
) { return sal_Int64(sal_uInt64(v
) << 32); }
43 /// convert double to 32:32 fixed point
44 inline sal_Int64
toFractional( double v
) { return (sal_Int64
)(v
*SAL_MAX_UINT32
+ (v
< 0.0 ? -0.5 : 0.5 )); }
45 /// convert 32:32 fixed point to int32 (truncate)
46 inline sal_Int32
toInteger( sal_Int64 v
) { return (sal_Int32
)(v
< 0 ? ~((~v
) >> 32) : v
>> 32); }
47 /// convert 32:32 fixed point to int32 (properly rounded)
48 inline sal_Int32
toRoundedInteger( sal_Int64 v
) { return toInteger(v
) + (sal_Int32
)((v
&0x80000000) >> 31); }
50 /** internal vertex store -
52 Different from B2DPoint, since we don't need floating
53 point coords, but orientation of vertex and y counter
61 bool mbDownwards
; // needed for nonzero winding rule
70 Vertex( basegfx::B2DPoint
const& rPt1
,
71 basegfx::B2DPoint
const& rPt2
,
73 mnYCounter( basegfx::fround(rPt2
.getY()) -
74 basegfx::fround(rPt1
.getY()) ),
75 mnX( toFractional( basegfx::fround(rPt1
.getX()) )),
76 mnXDelta( toFractional(
77 ((rPt2
.getX() - rPt1
.getX()) /
78 (double)mnYCounter
) )),
79 mbDownwards(bDownwards
)
83 typedef std::vector
< std::vector
<Vertex
> > VectorOfVectorOfVertices
;
84 typedef std::vector
< Vertex
* > VectorOfVertexPtr
;
86 /// non-templated setup of GET
87 sal_uInt32
setupGlobalEdgeTable( VectorOfVectorOfVertices
& rGET
,
88 basegfx::B2DPolyPolygon
const& rPoly
,
90 /// sort rAETSrc, copy not-yet-ended edges over to rAETDest
91 void sortAET( VectorOfVertexPtr
& rAETSrc
,
92 VectorOfVertexPtr
& rAETDest
);
94 /// For the STL algorithms
95 struct RasterConvertVertexComparator
97 RasterConvertVertexComparator() {}
99 bool operator()( const Vertex
& rLHS
,
100 const Vertex
& rRHS
) const
102 return rLHS
.mnX
< rRHS
.mnX
;
105 bool operator()( const Vertex
* pLHS
,
106 const Vertex
* pRHS
) const
108 return pLHS
->mnX
< pRHS
->mnX
;
112 } // namespace detail
115 /** Raster-convert a poly-polygon.
117 This algorithm does not perform antialiasing, and thus
118 internally works with integer vertex coordinates.
121 Left, top edge of the destination bitmap. This position is
122 considered (0,0) relative to all polygon vertices
125 Accessor to set pixel values
128 Color to use for filling
131 Clipping rectangle, relative to the begin iterator. No pixel outside
132 this clip rect will be modified.
137 template< class DestIterator
, class DestAccessor
, typename T
>
138 void renderClippedPolyPolygon( DestIterator begin
,
141 const basegfx::B2IBox
& rClipRect
,
142 basegfx::B2DPolyPolygon
const& rPoly
,
143 basegfx::FillRule eFillRule
)
145 const sal_Int32
nClipX1( std::max((sal_Int32
)0,rClipRect
.getMinX()) );
146 const sal_Int32
nClipX2( rClipRect
.getMaxX() );
147 const sal_Int32
nClipY1( std::max((sal_Int32
)0,rClipRect
.getMinY()) );
148 const sal_Int32
nClipY2( rClipRect
.getMaxY() );
149 const sal_Int64
nClipX1_frac( detail::toFractional(nClipX1
) );
150 const sal_Int64
nClipX2_frac( detail::toFractional(nClipX2
) );
152 basegfx::B2DRange
const aPolyBounds( basegfx::tools::getRange(rPoly
) );
154 const sal_Int32
nMinY( basegfx::fround(aPolyBounds
.getMinY()) );
155 const sal_Int32
nMaxY(
158 basegfx::fround(aPolyBounds
.getMaxY())));
161 return; // really, nothing to do then.
163 detail::VectorOfVectorOfVertices aGET
; // the Global Edge Table
164 aGET
.resize( nMaxY
- nMinY
+ 1 );
166 sal_uInt32
const nVertexCount(
167 detail::setupGlobalEdgeTable( aGET
, rPoly
, nMinY
) );
170 // Perform actual scan conversion
171 //----------------------------------------------------------------------
176 detail::VectorOfVertexPtr aAET1
; // the Active Edge Table
177 detail::VectorOfVertexPtr aAET2
;
178 detail::VectorOfVertexPtr
* pAET
= &aAET1
;
179 detail::VectorOfVertexPtr
* pAETOther
= &aAET2
;
180 aAET1
.reserve( nVertexCount
);
181 aAET2
.reserve( nVertexCount
);
183 // current scanline - initially, points to first scanline
184 // within the clip rect, or to the polygon's first scanline
185 // (whichever is greater)
186 DestIterator
aScanline( begin
+
191 detail::RasterConvertVertexComparator aComp
;
194 // now process each of the nMaxY - nMinY + 1 scanlines
195 // ------------------------------------------------------------
197 for( sal_Int32 y
=nMinY
; y
<= nMaxY
; ++y
)
199 if( !aGET
[y
-nMinY
].empty() )
201 // merge AET with current scanline's new vertices (both
202 // are already correctly sorted)
203 detail::VectorOfVectorOfVertices::value_type::iterator vertex
=aGET
[y
-nMinY
].begin();
204 detail::VectorOfVectorOfVertices::value_type::iterator
const end
=aGET
[y
-nMinY
].end();
205 while( vertex
!= end
)
207 // find insertion pos by binary search, and put ptr
208 // into active edge vector
209 pAET
->insert( std::lower_bound( pAET
->begin(),
219 // with less than two active edges, no fill visible
220 if( pAET
->size() >= 2 )
222 typename
vigra::IteratorTraits
<DestIterator
>::row_iterator
223 rowIter( aScanline
.rowIterator() );
225 // process each span in current scanline, with
226 // even-odd fill rule
227 detail::VectorOfVertexPtr::iterator
currVertex( pAET
->begin() );
228 detail::VectorOfVertexPtr::iterator
const lastVertex( pAET
->end()-1 );
229 sal_uInt32
nCrossedEdges(0);
230 sal_Int32
nWindingNumber(0);
231 while( currVertex
!= lastVertex
)
233 // TODO(P1): might be worth a try to extend the
234 // size()==2 case also to the actual filling
236 detail::Vertex
& rV1( **currVertex
);
237 detail::Vertex
const& rV2( **++currVertex
);
239 nWindingNumber
+= -1 + 2*rV1
.mbDownwards
;
241 // calc fill status for both rules. might save a
242 // few percent runtime to specialize here...
243 const bool bEvenOddFill(
244 eFillRule
== basegfx::FillRule_EVEN_ODD
&& !(nCrossedEdges
& 0x01) );
245 const bool bNonZeroWindingFill(
246 eFillRule
== basegfx::FillRule_NONZERO_WINDING_NUMBER
&& nWindingNumber
!= 0 );
249 if( (bEvenOddFill
|| bNonZeroWindingFill
) &&
251 rV1
.mnX
< nClipX2_frac
&&
252 rV2
.mnX
> nClipX1_frac
)
254 // clip span to horizontal bounds
255 sal_Int32
const nStartX(
258 detail::toRoundedInteger(rV1
.mnX
) )));
259 sal_Int32
const nEndX(
262 detail::toRoundedInteger(rV2
.mnX
) )));
264 typename
vigra::IteratorTraits
<DestIterator
>::row_iterator
265 currPix( rowIter
+ nStartX
);
266 typename
vigra::IteratorTraits
<DestIterator
>::row_iterator
267 rowEnd( rowIter
+ nEndX
);
269 // TODO(P2): Provide specialized span fill methods on the
271 while( currPix
!= rowEnd
)
272 ad
.set(fillColor
, currPix
++);
276 rV1
.mnX
+= rV1
.mnXDelta
;
282 // step vertex also for the last one
283 detail::Vertex
& rLastV( **currVertex
);
284 rLastV
.mnX
+= rLastV
.mnXDelta
;
288 // prune AET from ended edges, and keep it sorted
289 // ---------------------------------------------------------
292 if( pAET
->size() == 2 )
294 // the case of exactly two active edges is both
295 // sufficiently common (all 'simple' polygons have
296 // it), and further more would complicate the
297 // generic case below (which works with a sliding
298 // triple of vertices).
299 if( !aComp(*(*pAET
)[0], *(*pAET
)[1]) )
300 std::swap(*(*pAET
)[0], *(*pAET
)[1]);
302 if( (*pAET
)[0]->mnYCounter
> 0 )
303 pAETOther
->push_back( (*pAET
)[0] );
304 if( (*pAET
)[1]->mnYCounter
> 0 )
305 pAETOther
->push_back( (*pAET
)[1] );
309 bool bFallbackTaken(false);
310 currVertex
= pAET
->begin();
311 detail::VectorOfVertexPtr::iterator
prevVertex( currVertex
);
312 while( currVertex
!= lastVertex
)
314 // try to get away with one linear swoop and
315 // simple neighbor swapping. this is an
316 // overwhelmingly common case - polygons with
317 // excessively crisscrossing edges (which on
318 // top of that cross more than one other edge
319 // per scanline) are seldom. And even if we
320 // get such a beast here, this extra loop has
321 // still only linear cost
322 if( aComp(**(currVertex
+1),**currVertex
) )
324 std::swap(*currVertex
, *(currVertex
+1));
326 if( aComp(**currVertex
,**prevVertex
) )
328 // one swap was not sufficient -
329 // fallback to generic sort algo, then
330 detail::sortAET(*pAET
, *pAETOther
);
331 bFallbackTaken
= true;
336 if( (*currVertex
)->mnYCounter
> 0 )
337 pAETOther
->push_back( *currVertex
);
339 prevVertex
= currVertex
++;
342 // don't forget to add last vertex (loop above
343 // only deals with n-1 vertices)
344 if( !bFallbackTaken
&& (*currVertex
)->mnYCounter
> 0 )
345 pAETOther
->push_back( *currVertex
);
348 std::swap( pAET
, pAETOther
);
356 } // namespace basebmp
358 #endif /* INCLUDED_BASEBMP_POLYPOLYGONRENDERER_HXX */
360 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */