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_CLIPPEDLINERENDERER_HXX
21 #define INCLUDED_BASEBMP_CLIPPEDLINERENDERER_HXX
23 #include <basegfx/tools/rectcliptools.hxx>
24 #include <basegfx/point/b2ipoint.hxx>
25 #include <basegfx/range/b2ibox.hxx>
27 #include <vigra/diff2d.hxx>
28 #include <vigra/iteratortraits.hxx>
33 // factored-out bresenham setup code, which is used from two different
34 // places in renderClippedLine() below. Admittedly messy for the long
36 inline bool prepareClip( sal_Int32 a1
,
48 sal_uInt32 clipCount1
,
50 sal_uInt32 clipCount2
,
59 bool bRoundTowardsPt2
,
60 bool& o_bUseAlternateBresenham
)
65 if( clipCode1
& aMinFlag
)
67 ca
= 2*db
*(aMin
- a1
);
70 else if( clipCode1
& aMaxFlag
)
72 ca
= 2*db
*(a1
- aMax
);
76 if( clipCode1
& bMinFlag
)
78 cb
= 2*da
*(bMin
- b1
);
81 else if( clipCode1
& bMaxFlag
)
83 cb
= 2*da
*(b1
- bMax
);
88 clipCode1
&= (ca
+ da
< cb
+ !bRoundTowardsPt2
) ? ~(aMinFlag
|aMaxFlag
) : ~(bMinFlag
|bMaxFlag
);
90 if( clipCode1
& (aMinFlag
|aMaxFlag
) )
92 cb
= (ca
+ da
- !bRoundTowardsPt2
) / (2*da
);
98 return false; // fully clipped
104 return false; // fully clipped
107 io_rem
+= ca
- 2*da
*cb
;
111 ca
= (cb
- da
+ 2*db
- bRoundTowardsPt2
) / (2*db
);
116 return false; // fully clipped
122 return false; // fully clipped
125 io_rem
+= 2*db
*ca
- cb
;
130 o_as
= a1
; o_bs
= b1
;
135 if( clipCount2
== 2 )
137 ca
= 2*db
*((clipCode2
& aMinFlag
) ? a1
- aMin
: aMax
- a1
);
138 cb
= 2*da
*((clipCode2
& bMinFlag
) ? b1
- bMin
: bMax
- b1
);
139 clipCode2
&= (cb
+ da
< ca
+ bRoundTowardsPt2
) ? ~(aMinFlag
|aMaxFlag
) : ~(bMinFlag
|bMaxFlag
);
142 if( clipCode2
& (aMinFlag
|aMaxFlag
) )
143 o_n
= (clipCode2
& aMinFlag
) ? o_as
- aMin
: aMax
- o_as
;
146 o_n
= (clipCode2
& bMinFlag
) ? o_bs
- bMin
: bMax
- o_bs
;
147 o_bUseAlternateBresenham
= true;
151 o_n
= (a2
>= o_as
) ? a2
- o_as
: o_as
- a2
;
153 return true; // at least one pixel to render
157 /** Render line to image iterators, clip against given rectangle
159 This method renders a line from aPt1 to aPt2, clipped against
160 rClipRect (the clipping will take place pixel-perfect, i.e. as if
161 the original bresenham-rendered line would have been clipped each
162 pixel individually. No slight shifts compared to unclipped lines).
165 Start point of the line
168 End point of the line
171 Rectangle to clip against
174 Color value to render the line with
177 left-top image iterator
180 right-bottom image iterator
185 @param bRoundTowardsPt2
186 Rounding mode to use. Giving false here results in line pixel tend
187 towards pt1, i.e. when a pixel exactly hits the middle between two
188 pixel, the pixel closer to pt1 will be chosen. Giving true here
189 makes renderClippedLine() choose pt2 in those cases.
191 template< class Iterator
, class Accessor
>
192 void renderClippedLine( basegfx::B2IPoint aPt1
,
193 basegfx::B2IPoint aPt2
,
194 const basegfx::B2IBox
& rClipRect
,
195 typename
Accessor::value_type color
,
198 bool bRoundTowardsPt2
=false )
200 // Algorithm according to Steven Eker's 'Pixel-perfect line clipping',
201 // Graphics Gems V, pp. 314-322
202 sal_uInt32 clipCode1
= basegfx::tools::getCohenSutherlandClipFlags(aPt1
,
204 sal_uInt32 clipCode2
= basegfx::tools::getCohenSutherlandClipFlags(aPt2
,
207 if( clipCode1
& clipCode2
)
208 return; // line fully clipped away, both endpoints share a half-plane
210 sal_uInt32 clipCount1
= basegfx::tools::getNumberOfClipPlanes(clipCode1
);
211 sal_uInt32 clipCount2
= basegfx::tools::getNumberOfClipPlanes(clipCode2
);
213 if( (clipCode1
!= 0 && clipCode2
== 0)
214 || (clipCount1
== 2 && clipCount2
== 1) )
216 std::swap(clipCount2
,clipCount1
);
217 std::swap(clipCode2
,clipCode1
);
218 std::swap(aPt1
,aPt2
);
219 bRoundTowardsPt2
= !bRoundTowardsPt2
;
222 const sal_Int32 x1
= aPt1
.getX();
223 const sal_Int32 x2
= aPt2
.getX();
224 const sal_Int32 y1
= aPt1
.getY();
225 const sal_Int32 y2
= aPt2
.getY();
227 // TODO(E1): This might overflow
228 sal_Int32 adx
= x2
- x1
;
236 // TODO(E1): This might overflow
237 sal_Int32 ady
= y2
- y1
;
248 bool bUseAlternateBresenham
=false;
251 // semi-horizontal line
252 sal_Int32 rem
= 2*ady
- adx
- !bRoundTowardsPt2
;
254 if( !prepareClip(x1
, x2
, y1
, adx
, ady
, xs
, ys
, sx
, sy
,
255 rem
, n
, clipCode1
, clipCount1
, clipCode2
, clipCount2
,
256 rClipRect
.getMinX(), basegfx::tools::RectClipFlags::LEFT
,
257 rClipRect
.getMaxX()-1, basegfx::tools::RectClipFlags::RIGHT
,
258 rClipRect
.getMinY(), basegfx::tools::RectClipFlags::TOP
,
259 rClipRect
.getMaxY()-1, basegfx::tools::RectClipFlags::BOTTOM
,
260 bRoundTowardsPt2
, bUseAlternateBresenham
) )
261 return; // line fully clipped away, no active pixel inside rect
263 Iterator
currIter( begin
+ vigra::Diff2D(0,ys
) );
264 typename
vigra::IteratorTraits
<Iterator
>::row_iterator
265 rowIter( currIter
.rowIterator() + xs
);
270 if( bUseAlternateBresenham
)
274 acc
.set(color
, rowIter
);
278 // this is intended - we clip endpoint against y
279 // plane, so n here denotes y range to render
288 rowIter
= currIter
.rowIterator() + xs
;
303 acc
.set(color
, rowIter
);
315 rowIter
= currIter
.rowIterator() + xs
;
329 // semi-vertical line
330 sal_Int32 rem
= 2*adx
- ady
- !bRoundTowardsPt2
;
332 if( !prepareClip(y1
, y2
, x1
, ady
, adx
, ys
, xs
, sy
, sx
,
333 rem
, n
, clipCode1
, clipCount1
, clipCode2
, clipCount2
,
334 rClipRect
.getMinY(), basegfx::tools::RectClipFlags::TOP
,
335 rClipRect
.getMaxY()-1, basegfx::tools::RectClipFlags::BOTTOM
,
336 rClipRect
.getMinX(), basegfx::tools::RectClipFlags::LEFT
,
337 rClipRect
.getMaxX()-1, basegfx::tools::RectClipFlags::RIGHT
,
338 bRoundTowardsPt2
, bUseAlternateBresenham
) )
339 return; // line fully clipped away, no active pixel inside rect
341 Iterator
currIter( begin
+ vigra::Diff2D(xs
,0) );
342 typename
vigra::IteratorTraits
<Iterator
>::column_iterator
343 colIter( currIter
.columnIterator() + ys
);
348 if( bUseAlternateBresenham
)
352 acc
.set(color
, colIter
);
356 // this is intended - we clip endpoint against x
357 // plane, so n here denotes x range to render
366 colIter
= currIter
.columnIterator() + ys
;
381 acc
.set(color
, colIter
);
393 colIter
= currIter
.columnIterator() + ys
;
407 } // namespace basebmp
409 #endif /* INCLUDED_BASEBMP_CLIPPEDLINERENDERER_HXX */
411 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */