| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #ifndef INCLUDED_BASEBMP_CLIPPEDLINERENDERER_HXX |
| #define INCLUDED_BASEBMP_CLIPPEDLINERENDERER_HXX |
| |
| #include <basegfx/tools/rectcliptools.hxx> |
| #include <basegfx/point/b2ipoint.hxx> |
| #include <basegfx/range/b2irange.hxx> |
| |
| #include <vigra/diff2d.hxx> |
| #include <vigra/iteratortraits.hxx> |
| |
| namespace basebmp |
| { |
| |
| // factored-out bresenham setup code, which is used from two different |
| // places in renderClippedLine() below. Admittedly messy for the long |
| // parameter list... |
| inline bool prepareClip( sal_Int32 a1, |
| sal_Int32 a2, |
| sal_Int32 b1, |
| sal_Int32 da, |
| sal_Int32 db, |
| sal_Int32& o_as, |
| sal_Int32& o_bs, |
| int sa, |
| int sb, |
| sal_Int32& io_rem, |
| int& o_n, |
| sal_uInt32 clipCode1, |
| sal_uInt32 clipCount1, |
| sal_uInt32 clipCode2, |
| sal_uInt32 clipCount2, |
| sal_Int32 aMin, |
| sal_uInt32 aMinFlag, |
| sal_Int32 aMax, |
| sal_uInt32 aMaxFlag, |
| sal_Int32 bMin, |
| sal_uInt32 bMinFlag, |
| sal_Int32 bMax, |
| sal_uInt32 bMaxFlag, |
| bool bRoundTowardsPt2 ) |
| { |
| int ca(0), cb(0); |
| if( clipCode1 ) |
| { |
| if( clipCode1 & aMinFlag ) |
| { |
| ca = 2*db*(aMin - a1); |
| o_as = aMin; |
| } |
| else if( clipCode1 & aMaxFlag ) |
| { |
| ca = 2*db*(a1 - aMax); |
| o_as = aMax; |
| } |
| |
| if( clipCode1 & bMinFlag ) |
| { |
| cb = 2*da*(bMin - b1); |
| o_bs = bMin; |
| } |
| else if( clipCode1 & bMaxFlag ) |
| { |
| cb = 2*da*(b1 - bMax); |
| o_bs = bMax; |
| } |
| |
| if( clipCount1 == 2 ) |
| clipCode1 &= (ca + da < cb + !bRoundTowardsPt2) ? ~(aMinFlag|aMaxFlag) : ~(bMinFlag|bMaxFlag); |
| |
| if( clipCode1 & (aMinFlag|aMaxFlag) ) |
| { |
| cb = (ca + da - !bRoundTowardsPt2) / (2*da); |
| |
| if( sb >= 0 ) |
| { |
| o_bs = b1 + cb; |
| if( o_bs > bMax ) |
| return false; |
| } |
| else |
| { |
| o_bs = b1 - cb; |
| if( o_bs < bMin ) |
| return false; |
| } |
| |
| io_rem += ca - 2*da*cb; |
| } |
| else |
| { |
| ca = (cb - da + 2*db - bRoundTowardsPt2) / (2*db); |
| if( sa >= 0 ) |
| { |
| o_as = a1 + ca; |
| if( o_as > aMax ) |
| return false; |
| } |
| else |
| { |
| o_as = a1 - ca; |
| if( o_as < aMin ) |
| return false; |
| } |
| |
| io_rem += 2*db*ca - cb; |
| } |
| } |
| else |
| { |
| o_as = a1; o_bs = b1; |
| } |
| |
| bool bRetVal = false; |
| if( clipCode2 ) |
| { |
| if( clipCount2 == 2 ) |
| { |
| ca = 2*db*((clipCode2 & aMinFlag) ? a1 - aMin : aMax - a1); |
| cb = 2*da*((clipCode2 & bMinFlag) ? b1 - bMin : bMax - b1); |
| clipCode2 &= (cb + da < ca + bRoundTowardsPt2) ? ~(aMinFlag|aMaxFlag) : ~(bMinFlag|bMaxFlag); |
| } |
| |
| if( clipCode2 & (aMinFlag|aMaxFlag) ) |
| o_n = (clipCode2 & aMinFlag) ? o_as - aMin : aMax - o_as; |
| else |
| { |
| o_n = (clipCode2 & bMinFlag) ? o_bs - bMin : bMax - o_bs; |
| bRetVal = true; |
| } |
| } |
| else |
| o_n = (a2 >= o_as) ? a2 - o_as : o_as - a2; |
| |
| return bRetVal; |
| } |
| |
| |
| /** Render line to image iterators, clip against given rectangle |
| |
| This method renders a line from aPt1 to aPt2, clipped against |
| rClipRect (the clipping will take place pixel-perfect, i.e. as if |
| the original bresenham-rendered line would have been clipped each |
| pixel individually. No slight shifts compared to unclipped lines). |
| |
| @param aPt1 |
| Start point of the line |
| |
| @param aPt2 |
| End point of the line |
| |
| @param rClipRect |
| Rectangle to clip against |
| |
| @param color |
| Color value to render the line with |
| |
| @param begin |
| left-top image iterator |
| |
| @param end |
| right-bottom image iterator |
| |
| @param acc |
| Image accessor |
| |
| @param bRoundTowardsPt2 |
| Rounding mode to use. Giving false here results in line pixel tend |
| towards pt1, i.e. when a pixel exactly hits the middle between two |
| pixel, the pixel closer to pt1 will be chosen. Giving true here |
| makes renderClippedLine() choose pt2 in those cases. |
| */ |
| template< class Iterator, class Accessor > |
| void renderClippedLine( basegfx::B2IPoint aPt1, |
| basegfx::B2IPoint aPt2, |
| const basegfx::B2IRange& rClipRect, |
| typename Accessor::value_type color, |
| Iterator begin, |
| Accessor acc, |
| bool bRoundTowardsPt2=false ) |
| { |
| // Algorithm according to Steven Eker's 'Pixel-perfect line clipping', |
| // Graphics Gems V, pp. 314-322 |
| sal_uInt32 clipCode1 = basegfx::tools::getCohenSutherlandClipFlags(aPt1, |
| rClipRect); |
| sal_uInt32 clipCode2 = basegfx::tools::getCohenSutherlandClipFlags(aPt2, |
| rClipRect); |
| |
| if( clipCode1 & clipCode2 ) |
| return; // line fully clipped away |
| |
| sal_uInt32 clipCount1 = basegfx::tools::getNumberOfClipPlanes(clipCode1); |
| sal_uInt32 clipCount2 = basegfx::tools::getNumberOfClipPlanes(clipCode2); |
| |
| if( (clipCode1 != 0 && clipCode2 == 0) |
| || (clipCount1 == 2 && clipCount2 == 1) ) |
| { |
| std::swap(clipCount2,clipCount1); |
| std::swap(clipCode2,clipCode1); |
| std::swap(aPt1,aPt2); |
| bRoundTowardsPt2 = !bRoundTowardsPt2; |
| } |
| |
| const sal_Int32 x1 = aPt1.getX(); |
| const sal_Int32 x2 = aPt2.getX(); |
| const sal_Int32 y1 = aPt1.getY(); |
| const sal_Int32 y2 = aPt2.getY(); |
| |
| // TODO(E1): This might overflow |
| sal_Int32 adx = x2 - x1; |
| int sx = 1; |
| if( adx < 0 ) |
| { |
| adx *= -1; |
| sx = -1; |
| } |
| |
| // TODO(E1): This might overflow |
| sal_Int32 ady = y2 - y1; |
| int sy = 1; |
| if( ady < 0 ) |
| { |
| ady *= -1; |
| sy = -1; |
| } |
| |
| int n = 0; |
| sal_Int32 xs = x1; |
| sal_Int32 ys = y1; |
| if( adx >= ady ) |
| { |
| // semi-horizontal line |
| sal_Int32 rem = 2*ady - adx - !bRoundTowardsPt2; |
| |
| const bool bUseAlternateBresenham( |
| prepareClip(x1, x2, y1, adx, ady, xs, ys, sx, sy, |
| rem, n, clipCode1, clipCount1, clipCode2, clipCount2, |
| rClipRect.getMinX(), basegfx::tools::RectClipFlags::LEFT, |
| rClipRect.getMaxX(), basegfx::tools::RectClipFlags::RIGHT, |
| rClipRect.getMinY(), basegfx::tools::RectClipFlags::TOP, |
| rClipRect.getMaxY(), basegfx::tools::RectClipFlags::BOTTOM, |
| bRoundTowardsPt2 )); |
| |
| Iterator currIter( begin + vigra::Diff2D(0,ys) ); |
| typename vigra::IteratorTraits<Iterator>::row_iterator |
| rowIter( currIter.rowIterator() + xs ); |
| |
| adx *= 2; |
| ady *= 2; |
| |
| if( bUseAlternateBresenham ) |
| { |
| while(true) |
| { |
| acc.set(color, rowIter); |
| |
| if( rem >= 0 ) |
| { |
| if( --n < 0 ) |
| break; |
| |
| ys += sy; |
| xs += sx; |
| rem -= adx; |
| |
| currIter.y += sy; |
| rowIter = currIter.rowIterator() + xs; |
| } |
| else |
| { |
| xs += sx; |
| rowIter += sx; |
| } |
| |
| rem += ady; |
| } |
| } |
| else |
| { |
| while(true) |
| { |
| acc.set(color, rowIter); |
| |
| if( --n < 0 ) |
| break; |
| |
| if( rem >= 0 ) |
| { |
| ys += sy; |
| xs += sx; |
| rem -= adx; |
| |
| currIter.y += sy; |
| rowIter = currIter.rowIterator() + xs; |
| } |
| else |
| { |
| xs += sx; |
| rowIter += sx; |
| } |
| |
| rem += ady; |
| } |
| } |
| } |
| else |
| { |
| // semi-vertical line |
| sal_Int32 rem = 2*adx - ady - !bRoundTowardsPt2; |
| |
| const bool bUseAlternateBresenham( |
| prepareClip(y1, y2, x1, ady, adx, ys, xs, sy, sx, |
| rem, n, clipCode1, clipCount1, clipCode2, clipCount2, |
| rClipRect.getMinY(), basegfx::tools::RectClipFlags::TOP, |
| rClipRect.getMaxY(), basegfx::tools::RectClipFlags::BOTTOM, |
| rClipRect.getMinX(), basegfx::tools::RectClipFlags::LEFT, |
| rClipRect.getMaxX(), basegfx::tools::RectClipFlags::RIGHT, |
| bRoundTowardsPt2 )); |
| |
| Iterator currIter( begin + vigra::Diff2D(xs,0) ); |
| typename vigra::IteratorTraits<Iterator>::column_iterator |
| colIter( currIter.columnIterator() + ys ); |
| |
| adx *= 2; |
| ady *= 2; |
| |
| if( bUseAlternateBresenham ) |
| { |
| while(true) |
| { |
| acc.set(color, colIter); |
| |
| if( rem >= 0 ) |
| { |
| if( --n < 0 ) |
| break; |
| |
| xs += sx; |
| ys += sy; |
| rem -= ady; |
| |
| currIter.x += sx; |
| colIter = currIter.columnIterator() + ys; |
| } |
| else |
| { |
| ys += sy; |
| colIter += sy; |
| } |
| |
| rem += adx; |
| } |
| } |
| else |
| { |
| while(true) |
| { |
| acc.set(color, colIter); |
| |
| if( --n < 0 ) |
| break; |
| |
| if( rem >= 0 ) |
| { |
| xs += sx; |
| ys += sy; |
| rem -= ady; |
| |
| currIter.x += sx; |
| colIter = currIter.columnIterator() + ys; |
| } |
| else |
| { |
| ys += sy; |
| colIter += sy; |
| } |
| |
| rem += adx; |
| } |
| } |
| } |
| } |
| |
| } // namespace basebmp |
| |
| #endif /* INCLUDED_BASEBMP_CLIPPEDLINERENDERER_HXX */ |