diff --git a/modules/imgproc/include/opencv2/imgproc.hpp b/modules/imgproc/include/opencv2/imgproc.hpp index 3d4bb60f57..e7ccf28450 100644 --- a/modules/imgproc/include/opencv2/imgproc.hpp +++ b/modules/imgproc/include/opencv2/imgproc.hpp @@ -4725,10 +4725,39 @@ public: the line is 8-connected or 4-connected If leftToRight=true, then the iteration is always done from the left-most point to the right most, - not to depend on the ordering of pt1 and pt2 parameters + not to depend on the ordering of pt1 and pt2 parameters; */ LineIterator( const Mat& img, Point pt1, Point pt2, - int connectivity = 8, bool leftToRight = false ); + int connectivity = 8, bool leftToRight = false ) + { + init(&img, Rect(0, 0, img.cols, img.rows), pt1, pt2, connectivity, leftToRight); + ptmode = false; + } + LineIterator( Point pt1, Point pt2, + int connectivity = 8, bool leftToRight = false ) + { + init(0, Rect(std::min(pt1.x, pt2.x), + std::min(pt1.y, pt2.y), + std::max(pt1.x, pt2.x) - std::min(pt1.x, pt2.x) + 1, + std::max(pt1.y, pt2.y) - std::min(pt1.y, pt2.y) + 1), + pt1, pt2, connectivity, leftToRight); + ptmode = true; + } + LineIterator( Size boundingAreaSize, Point pt1, Point pt2, + int connectivity = 8, bool leftToRight = false ) + { + init(0, Rect(0, 0, boundingAreaSize.width, boundingAreaSize.height), + pt1, pt2, connectivity, leftToRight); + ptmode = true; + } + LineIterator( Rect boundingAreaRect, Point pt1, Point pt2, + int connectivity = 8, bool leftToRight = false ) + { + init(0, boundingAreaRect, pt1, pt2, connectivity, leftToRight); + ptmode = true; + } + void init(const Mat* img, Rect boundingAreaRect, Point pt1, Point pt2, int connectivity, bool leftToRight); + /** @brief returns pointer to the current pixel */ uchar* operator *(); @@ -4748,6 +4777,9 @@ public: int err, count; int minusDelta, plusDelta; int minusStep, plusStep; + int minusShift, plusShift; + Point p; + bool ptmode; }; //! @cond IGNORED @@ -4757,7 +4789,7 @@ public: inline uchar* LineIterator::operator *() { - return ptr; + return ptmode ? 0 : ptr; } inline @@ -4765,7 +4797,15 @@ LineIterator& LineIterator::operator ++() { int mask = err < 0 ? -1 : 0; err += minusDelta + (plusDelta & mask); - ptr += minusStep + (plusStep & mask); + if(!ptmode) + { + ptr += minusStep + (plusStep & mask); + } + else + { + p.x += minusShift + (plusShift & mask); + p.y += minusStep + (plusStep & mask); + } return *this; } @@ -4780,9 +4820,13 @@ LineIterator LineIterator::operator ++(int) inline Point LineIterator::pos() const { - Point p; - p.y = (int)((ptr - ptr0)/step); - p.x = (int)(((ptr - ptr0) - p.y*step)/elemSize); + if(!ptmode) + { + size_t offset = (size_t)(ptr - ptr0); + int y = (int)(offset/step); + int x = (int)((offset - (size_t)y*step)/elemSize); + return Point(x, y); + } return p; } diff --git a/modules/imgproc/src/drawing.cpp b/modules/imgproc/src/drawing.cpp index 7d70aa7667..c7b85f31ea 100644 --- a/modules/imgproc/src/drawing.cpp +++ b/modules/imgproc/src/drawing.cpp @@ -156,96 +156,107 @@ bool clipLine( Rect img_rect, Point& pt1, Point& pt2 ) return inside; } -/* - Initializes line iterator. - Returns number of points on the line or negative number if error. -*/ -LineIterator::LineIterator(const Mat& img, Point pt1, Point pt2, - int connectivity, bool left_to_right) +void LineIterator::init( const Mat* img, Rect rect, Point pt1_, Point pt2_, int connectivity, bool leftToRight ) { - count = -1; - CV_Assert( connectivity == 8 || connectivity == 4 ); - if( (unsigned)pt1.x >= (unsigned)(img.cols) || - (unsigned)pt2.x >= (unsigned)(img.cols) || - (unsigned)pt1.y >= (unsigned)(img.rows) || - (unsigned)pt2.y >= (unsigned)(img.rows) ) + count = -1; + p = Point(0, 0); + ptr0 = ptr = 0; + step = elemSize = 0; + ptmode = !img; + + Point pt1 = pt1_ - rect.tl(); + Point pt2 = pt2_ - rect.tl(); + + if( (unsigned)pt1.x >= (unsigned)(rect.width) || + (unsigned)pt2.x >= (unsigned)(rect.width) || + (unsigned)pt1.y >= (unsigned)(rect.height) || + (unsigned)pt2.y >= (unsigned)(rect.height) ) { - if( !clipLine( img.size(), pt1, pt2 ) ) + if( !clipLine(Size(rect.width, rect.height), pt1, pt2) ) { - ptr = img.data; - err = plusDelta = minusDelta = plusStep = minusStep = count = 0; - ptr0 = 0; - step = 0; - elemSize = 0; + err = plusDelta = minusDelta = plusStep = minusStep = plusShift = minusShift = count = 0; return; } } - size_t bt_pix0 = img.elemSize(), bt_pix = bt_pix0; - size_t istep = img.step; + pt1 += rect.tl(); + pt2 += rect.tl(); + int delta_x = 1, delta_y = 1; int dx = pt2.x - pt1.x; int dy = pt2.y - pt1.y; - int s = dx < 0 ? -1 : 0; - if( left_to_right ) + if( dx < 0 ) { - dx = (dx ^ s) - s; - dy = (dy ^ s) - s; - pt1.x ^= (pt1.x ^ pt2.x) & s; - pt1.y ^= (pt1.y ^ pt2.y) & s; + if( leftToRight ) + { + dx = -dx; + dy = -dy; + pt1 = pt2; + } + else + { + dx = -dx; + delta_x = -1; + } } - else + + if( dy < 0 ) { - dx = (dx ^ s) - s; - bt_pix = (bt_pix ^ s) - s; + dy = -dy; + delta_y = -1; } - ptr = (uchar*)(img.data + pt1.y * istep + pt1.x * bt_pix0); - - s = dy < 0 ? -1 : 0; - dy = (dy ^ s) - s; - istep = (istep ^ s) - s; - - s = dy > dx ? -1 : 0; - - /* conditional swaps */ - dx ^= dy & s; - dy ^= dx & s; - dx ^= dy & s; + bool vert = dy > dx; + if( vert ) + { + std::swap(dx, dy); + std::swap(delta_x, delta_y); + } - bt_pix ^= istep & s; - istep ^= bt_pix & s; - bt_pix ^= istep & s; + CV_Assert( dx >= 0 && dy >= 0 ); if( connectivity == 8 ) { - assert( dx >= 0 && dy >= 0 ); - err = dx - (dy + dy); plusDelta = dx + dx; minusDelta = -(dy + dy); - plusStep = (int)istep; - minusStep = (int)bt_pix; + minusShift = delta_x; + plusShift = 0; + minusStep = 0; + plusStep = delta_y; count = dx + 1; } else /* connectivity == 4 */ { - assert( dx >= 0 && dy >= 0 ); - err = 0; plusDelta = (dx + dx) + (dy + dy); minusDelta = -(dy + dy); - plusStep = (int)(istep - bt_pix); - minusStep = (int)bt_pix; + minusShift = delta_x; + plusShift = -delta_x; + minusStep = 0; + plusStep = delta_y; count = dx + dy + 1; } - this->ptr0 = img.ptr(); - this->step = (int)img.step; - this->elemSize = (int)bt_pix0; + if( vert ) + { + std::swap(plusStep, plusShift); + std::swap(minusStep, minusShift); + } + + p = pt1; + if( !ptmode ) + { + ptr0 = img->ptr(); + step = (int)img->step; + elemSize = (int)img->elemSize(); + ptr = (uchar*)ptr0 + (size_t)p.y*step + (size_t)p.x*elemSize; + plusStep = plusStep*step + plusShift*elemSize; + minusStep = minusStep*step + minusShift*elemSize; + } } static void @@ -262,19 +273,26 @@ Line( Mat& img, Point pt1, Point pt2, int pix_size = (int)img.elemSize(); const uchar* color = (const uchar*)_color; - for( i = 0; i < count; i++, ++iterator ) + if( pix_size == 3 ) { - uchar* ptr = *iterator; - if( pix_size == 1 ) - ptr[0] = color[0]; - else if( pix_size == 3 ) + for( i = 0; i < count; i++, ++iterator ) { + uchar* ptr = *iterator; ptr[0] = color[0]; ptr[1] = color[1]; ptr[2] = color[2]; } - else - memcpy( *iterator, color, pix_size ); + } + else + { + for( i = 0; i < count; i++, ++iterator ) + { + uchar* ptr = *iterator; + if( pix_size == 1 ) + ptr[0] = color[0]; + else + memcpy( *iterator, color, pix_size ); + } } } diff --git a/modules/imgproc/test/test_drawing.cpp b/modules/imgproc/test/test_drawing.cpp index 2796d35ba8..4553045430 100644 --- a/modules/imgproc/test/test_drawing.cpp +++ b/modules/imgproc/test/test_drawing.cpp @@ -54,6 +54,7 @@ protected: void run( int ); virtual void draw( Mat& img ) = 0; virtual int checkLineIterator( Mat& img) = 0; + virtual int checkLineVirtualIterator() = 0; }; void CV_DrawingTest::run( int ) @@ -93,6 +94,7 @@ void CV_DrawingTest::run( int ) ts->set_failed_test_info(checkLineIterator( testImg )); } } + ts->set_failed_test_info(checkLineVirtualIterator()); ts->set_failed_test_info(cvtest::TS::OK); } @@ -103,6 +105,7 @@ public: protected: virtual void draw( Mat& img ); virtual int checkLineIterator( Mat& img); + virtual int checkLineVirtualIterator(); }; void CV_DrawingTest_CPP::draw( Mat& img ) @@ -245,6 +248,51 @@ int CV_DrawingTest_CPP::checkLineIterator( Mat& img ) return 0; } +int CV_DrawingTest_CPP::checkLineVirtualIterator( ) +{ + RNG randomGenerator(1); + for (size_t test = 0; test < 10000; ++test) + { + int width = randomGenerator.uniform(0, 512+1); + int height = randomGenerator.uniform(0, 512+1); + int x1 = randomGenerator.uniform(-512, 1024+1); + int y1 = randomGenerator.uniform(-512, 1024+1); + int x2 = randomGenerator.uniform(-512, 1024+1); + int y2 = randomGenerator.uniform(-512, 1024+1); + int x3 = randomGenerator.uniform(-512, 1024+1); + int y3 = randomGenerator.uniform(-512, 1024+1); + int channels = randomGenerator.uniform(1, 3+1); + Mat m(cv::Size(width, height), CV_MAKETYPE(8U, channels)); + Point p1(x1, y1); + Point p2(x2, y2); + Point offset(x3, y3); + LineIterator it( m, p1, p2 ); + LineIterator vit(Rect(offset.x, offset.y, width, height), p1 + offset, p2 + offset); + if (it.count != vit.count) + { + ts->printf( ts->LOG, "virtual LineIterator works incorrectly" ); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + break; + } + else + { + for(int i = 0; i < it.count; ++it, ++vit, i++ ) + { + Point pIt = it.pos(); + Point pVit = vit.pos() - offset; + if (pIt != pVit) + { + ts->printf( ts->LOG, "virtual LineIterator works incorrectly" ); + ts->set_failed_test_info(cvtest::TS::FAIL_INVALID_OUTPUT); + break; + } + } + } + } + ts->set_failed_test_info(cvtest::TS::OK); + return 0; +} + class CV_DrawingTest_Far : public CV_DrawingTest_CPP { public: