mirror of
https://github.com/opencv/opencv.git
synced 2026-07-25 21:33:04 +04:00
94de7e5d21
Fix harmless ASAN error. #24042 For an empty radius, &v[0] would be accessed (though the called functions would not use it due to v.size() being 0). Also add checks for emptyness and fix the first element checks, in case we get INT_MAX to compare to. ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
2922 lines
98 KiB
C++
2922 lines
98 KiB
C++
/*M///////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
|
|
//
|
|
// By downloading, copying, installing or using the software you agree to this license.
|
|
// If you do not agree to this license, do not download, install,
|
|
// copy or use the software.
|
|
//
|
|
//
|
|
// Intel License Agreement
|
|
// For Open Source Computer Vision Library
|
|
//
|
|
// Copyright (C) 2000, Intel Corporation, all rights reserved.
|
|
// Third party copyrights are property of their respective owners.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification,
|
|
// are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistribution's of source code must retain the above copyright notice,
|
|
// this list of conditions and the following disclaimer.
|
|
//
|
|
// * Redistribution's in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
//
|
|
// * The name of Intel Corporation may not be used to endorse or promote products
|
|
// derived from this software without specific prior written permission.
|
|
//
|
|
// This software is provided by the copyright holders and contributors "as is" and
|
|
// any express or implied warranties, including, but not limited to, the implied
|
|
// warranties of merchantability and fitness for a particular purpose are disclaimed.
|
|
// In no event shall the Intel Corporation or contributors be liable for any direct,
|
|
// indirect, incidental, special, exemplary, or consequential damages
|
|
// (including, but not limited to, procurement of substitute goods or services;
|
|
// loss of use, data, or profits; or business interruption) however caused
|
|
// and on any theory of liability, whether in contract, strict liability,
|
|
// or tort (including negligence or otherwise) arising in any way out of
|
|
// the use of this software, even if advised of the possibility of such damage.
|
|
//
|
|
//M*/
|
|
#include "precomp.hpp"
|
|
|
|
namespace cv
|
|
{
|
|
|
|
enum { XY_SHIFT = 16, XY_ONE = 1 << XY_SHIFT, DRAWING_STORAGE_BLOCK = (1<<12) - 256 };
|
|
|
|
static const int MAX_THICKNESS = 32767;
|
|
|
|
struct PolyEdge
|
|
{
|
|
PolyEdge() : y0(0), y1(0), x(0), dx(0), next(0) {}
|
|
//PolyEdge(int _y0, int _y1, int _x, int _dx) : y0(_y0), y1(_y1), x(_x), dx(_dx) {}
|
|
|
|
int y0, y1;
|
|
int64 x, dx;
|
|
PolyEdge *next;
|
|
};
|
|
|
|
static void
|
|
CollectPolyEdges( Mat& img, const Point2l* v, int npts,
|
|
std::vector<PolyEdge>& edges, const void* color, int line_type,
|
|
int shift, Point offset=Point() );
|
|
|
|
static void
|
|
FillEdgeCollection( Mat& img, std::vector<PolyEdge>& edges, const void* color, int line_type);
|
|
|
|
static void
|
|
PolyLine( Mat& img, const Point2l* v, int npts, bool closed,
|
|
const void* color, int thickness, int line_type, int shift );
|
|
|
|
static void
|
|
FillConvexPoly( Mat& img, const Point2l* v, int npts,
|
|
const void* color, int line_type, int shift );
|
|
|
|
/****************************************************************************************\
|
|
* Lines *
|
|
\****************************************************************************************/
|
|
|
|
bool clipLine( Size img_size, Point& pt1, Point& pt2 )
|
|
{
|
|
Point2l p1(pt1);
|
|
Point2l p2(pt2);
|
|
bool inside = clipLine(Size2l(img_size.width, img_size.height), p1, p2);
|
|
pt1.x = (int)p1.x;
|
|
pt1.y = (int)p1.y;
|
|
pt2.x = (int)p2.x;
|
|
pt2.y = (int)p2.y;
|
|
return inside;
|
|
}
|
|
|
|
bool clipLine( Size2l img_size, Point2l& pt1, Point2l& pt2 )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
int c1, c2;
|
|
int64 right = img_size.width-1, bottom = img_size.height-1;
|
|
|
|
if( img_size.width <= 0 || img_size.height <= 0 )
|
|
return false;
|
|
|
|
int64 &x1 = pt1.x, &y1 = pt1.y, &x2 = pt2.x, &y2 = pt2.y;
|
|
c1 = (x1 < 0) + (x1 > right) * 2 + (y1 < 0) * 4 + (y1 > bottom) * 8;
|
|
c2 = (x2 < 0) + (x2 > right) * 2 + (y2 < 0) * 4 + (y2 > bottom) * 8;
|
|
|
|
if( (c1 & c2) == 0 && (c1 | c2) != 0 )
|
|
{
|
|
int64 a;
|
|
if( c1 & 12 )
|
|
{
|
|
a = c1 < 8 ? 0 : bottom;
|
|
x1 += (int64)((double)(a - y1) * (x2 - x1) / (y2 - y1));
|
|
y1 = a;
|
|
c1 = (x1 < 0) + (x1 > right) * 2;
|
|
}
|
|
if( c2 & 12 )
|
|
{
|
|
a = c2 < 8 ? 0 : bottom;
|
|
x2 += (int64)((double)(a - y2) * (x2 - x1) / (y2 - y1));
|
|
y2 = a;
|
|
c2 = (x2 < 0) + (x2 > right) * 2;
|
|
}
|
|
if( (c1 & c2) == 0 && (c1 | c2) != 0 )
|
|
{
|
|
if( c1 )
|
|
{
|
|
a = c1 == 1 ? 0 : right;
|
|
y1 += (int64)((double)(a - x1) * (y2 - y1) / (x2 - x1));
|
|
x1 = a;
|
|
c1 = 0;
|
|
}
|
|
if( c2 )
|
|
{
|
|
a = c2 == 1 ? 0 : right;
|
|
y2 += (int64)((double)(a - x2) * (y2 - y1) / (x2 - x1));
|
|
x2 = a;
|
|
c2 = 0;
|
|
}
|
|
}
|
|
|
|
CV_Assert( (c1 & c2) != 0 || (x1 | y1 | x2 | y2) >= 0 );
|
|
}
|
|
|
|
return (c1 | c2) == 0;
|
|
}
|
|
|
|
bool clipLine( Rect img_rect, Point& pt1, Point& pt2 )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Point tl = img_rect.tl();
|
|
pt1 -= tl; pt2 -= tl;
|
|
bool inside = clipLine(img_rect.size(), pt1, pt2);
|
|
pt1 += tl; pt2 += tl;
|
|
|
|
return inside;
|
|
}
|
|
|
|
void LineIterator::init( const Mat* img, Rect rect, Point pt1_, Point pt2_, int connectivity, bool leftToRight )
|
|
{
|
|
CV_Assert( connectivity == 8 || connectivity == 4 );
|
|
|
|
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(Size(rect.width, rect.height), pt1, pt2) )
|
|
{
|
|
err = plusDelta = minusDelta = plusStep = minusStep = plusShift = minusShift = count = 0;
|
|
return;
|
|
}
|
|
}
|
|
|
|
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;
|
|
|
|
if( dx < 0 )
|
|
{
|
|
if( leftToRight )
|
|
{
|
|
dx = -dx;
|
|
dy = -dy;
|
|
pt1 = pt2;
|
|
}
|
|
else
|
|
{
|
|
dx = -dx;
|
|
delta_x = -1;
|
|
}
|
|
}
|
|
|
|
if( dy < 0 )
|
|
{
|
|
dy = -dy;
|
|
delta_y = -1;
|
|
}
|
|
|
|
bool vert = dy > dx;
|
|
if( vert )
|
|
{
|
|
std::swap(dx, dy);
|
|
std::swap(delta_x, delta_y);
|
|
}
|
|
|
|
CV_Assert( dx >= 0 && dy >= 0 );
|
|
|
|
if( connectivity == 8 )
|
|
{
|
|
err = dx - (dy + dy);
|
|
plusDelta = dx + dx;
|
|
minusDelta = -(dy + dy);
|
|
minusShift = delta_x;
|
|
plusShift = 0;
|
|
minusStep = 0;
|
|
plusStep = delta_y;
|
|
count = dx + 1;
|
|
}
|
|
else /* connectivity == 4 */
|
|
{
|
|
err = 0;
|
|
plusDelta = (dx + dx) + (dy + dy);
|
|
minusDelta = -(dy + dy);
|
|
minusShift = delta_x;
|
|
plusShift = -delta_x;
|
|
minusStep = 0;
|
|
plusStep = delta_y;
|
|
count = dx + dy + 1;
|
|
}
|
|
|
|
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
|
|
Line( Mat& img, Point pt1, Point pt2,
|
|
const void* _color, int connectivity = 8 )
|
|
{
|
|
if( connectivity == 0 )
|
|
connectivity = 8;
|
|
else if( connectivity == 1 )
|
|
connectivity = 4;
|
|
|
|
LineIterator iterator(img, pt1, pt2, connectivity, true);
|
|
int i, count = iterator.count;
|
|
int pix_size = (int)img.elemSize();
|
|
const uchar* color = (const uchar*)_color;
|
|
|
|
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
|
|
{
|
|
for( i = 0; i < count; i++, ++iterator )
|
|
{
|
|
uchar* ptr = *iterator;
|
|
if( pix_size == 1 )
|
|
ptr[0] = color[0];
|
|
else
|
|
memcpy( *iterator, color, pix_size );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Correction table depent on the slope */
|
|
static const uchar SlopeCorrTable[] = {
|
|
181, 181, 181, 182, 182, 183, 184, 185, 187, 188, 190, 192, 194, 196, 198, 201,
|
|
203, 206, 209, 211, 214, 218, 221, 224, 227, 231, 235, 238, 242, 246, 250, 254
|
|
};
|
|
|
|
/* Gaussian for antialiasing filter */
|
|
static const int FilterTable[] = {
|
|
168, 177, 185, 194, 202, 210, 218, 224, 231, 236, 241, 246, 249, 252, 254, 254,
|
|
254, 254, 252, 249, 246, 241, 236, 231, 224, 218, 210, 202, 194, 185, 177, 168,
|
|
158, 149, 140, 131, 122, 114, 105, 97, 89, 82, 75, 68, 62, 56, 50, 45,
|
|
40, 36, 32, 28, 25, 22, 19, 16, 14, 12, 11, 9, 8, 7, 5, 5
|
|
};
|
|
|
|
static void
|
|
LineAA( Mat& img, Point2l pt1, Point2l pt2, const void* color )
|
|
{
|
|
int64 dx, dy;
|
|
int ecount, scount = 0;
|
|
int slope;
|
|
int64 ax, ay;
|
|
int64 x_step, y_step;
|
|
int64 i, j;
|
|
int ep_table[9];
|
|
int cb = ((uchar*)color)[0], cg = ((uchar*)color)[1], cr = ((uchar*)color)[2], ca = ((uchar*)color)[3];
|
|
int _cb, _cg, _cr, _ca;
|
|
int nch = img.channels();
|
|
uchar* ptr = img.ptr();
|
|
size_t step = img.step;
|
|
Size2l size0(img.size()), size = size0;
|
|
|
|
if( !((nch == 1 || nch == 3 || nch == 4) && img.depth() == CV_8U) )
|
|
{
|
|
Line(img, Point((int)(pt1.x>>XY_SHIFT), (int)(pt1.y>>XY_SHIFT)), Point((int)(pt2.x>>XY_SHIFT), (int)(pt2.y>>XY_SHIFT)), color);
|
|
return;
|
|
}
|
|
|
|
size.width <<= XY_SHIFT;
|
|
size.height <<= XY_SHIFT;
|
|
if( !clipLine( size, pt1, pt2 ))
|
|
return;
|
|
|
|
dx = pt2.x - pt1.x;
|
|
dy = pt2.y - pt1.y;
|
|
|
|
j = dx < 0 ? -1 : 0;
|
|
ax = (dx ^ j) - j;
|
|
i = dy < 0 ? -1 : 0;
|
|
ay = (dy ^ i) - i;
|
|
|
|
if( ax > ay )
|
|
{
|
|
dy = (dy ^ j) - j;
|
|
pt1.x ^= pt2.x & j;
|
|
pt2.x ^= pt1.x & j;
|
|
pt1.x ^= pt2.x & j;
|
|
pt1.y ^= pt2.y & j;
|
|
pt2.y ^= pt1.y & j;
|
|
pt1.y ^= pt2.y & j;
|
|
|
|
x_step = XY_ONE;
|
|
y_step = (int64)((uint64_t)dy << XY_SHIFT) / (ax | 1);
|
|
pt2.x += XY_ONE;
|
|
ecount = (int)((pt2.x >> XY_SHIFT) - (pt1.x >> XY_SHIFT));
|
|
j = -(pt1.x & (XY_ONE - 1));
|
|
pt1.y += ((y_step * j) >> XY_SHIFT) + (XY_ONE >> 1);
|
|
slope = (y_step >> (XY_SHIFT - 5)) & 0x3f;
|
|
slope ^= (y_step < 0 ? 0x3f : 0);
|
|
|
|
/* Get 4-bit fractions for end-point adjustments */
|
|
i = (pt1.x >> (XY_SHIFT - 7)) & 0x78;
|
|
j = (pt2.x >> (XY_SHIFT - 7)) & 0x78;
|
|
}
|
|
else
|
|
{
|
|
dx = (dx ^ i) - i;
|
|
pt1.x ^= pt2.x & i;
|
|
pt2.x ^= pt1.x & i;
|
|
pt1.x ^= pt2.x & i;
|
|
pt1.y ^= pt2.y & i;
|
|
pt2.y ^= pt1.y & i;
|
|
pt1.y ^= pt2.y & i;
|
|
|
|
x_step = (int64)((uint64_t)dx << XY_SHIFT) / (ay | 1);
|
|
y_step = XY_ONE;
|
|
pt2.y += XY_ONE;
|
|
ecount = (int)((pt2.y >> XY_SHIFT) - (pt1.y >> XY_SHIFT));
|
|
j = -(pt1.y & (XY_ONE - 1));
|
|
pt1.x += ((x_step * j) >> XY_SHIFT) + (XY_ONE >> 1);
|
|
slope = (x_step >> (XY_SHIFT - 5)) & 0x3f;
|
|
slope ^= (x_step < 0 ? 0x3f : 0);
|
|
|
|
/* Get 4-bit fractions for end-point adjustments */
|
|
i = (pt1.y >> (XY_SHIFT - 7)) & 0x78;
|
|
j = (pt2.y >> (XY_SHIFT - 7)) & 0x78;
|
|
}
|
|
|
|
slope = (slope & 0x20) ? 0x100 : SlopeCorrTable[slope];
|
|
|
|
/* Calc end point correction table */
|
|
{
|
|
int t0 = slope << 7;
|
|
int t1 = ((0x78 - (int)i) | 4) * slope;
|
|
int t2 = ((int)j | 4) * slope;
|
|
|
|
ep_table[0] = 0;
|
|
ep_table[8] = slope;
|
|
ep_table[1] = ep_table[3] = ((((j - i) & 0x78) | 4) * slope >> 8) & 0x1ff;
|
|
ep_table[2] = (t1 >> 8) & 0x1ff;
|
|
ep_table[4] = ((((j - i) + 0x80) | 4) * slope >> 8) & 0x1ff;
|
|
ep_table[5] = ((t1 + t0) >> 8) & 0x1ff;
|
|
ep_table[6] = (t2 >> 8) & 0x1ff;
|
|
ep_table[7] = ((t2 + t0) >> 8) & 0x1ff;
|
|
}
|
|
|
|
if( nch == 3 )
|
|
{
|
|
#define ICV_PUT_POINT(x, y) \
|
|
{ \
|
|
uchar* tptr = ptr + (x)*3 + (y)*step; \
|
|
_cb = tptr[0]; \
|
|
_cb += ((cb - _cb)*a + 127)>> 8;\
|
|
_cb += ((cb - _cb)*a + 127)>> 8;\
|
|
_cg = tptr[1]; \
|
|
_cg += ((cg - _cg)*a + 127)>> 8;\
|
|
_cg += ((cg - _cg)*a + 127)>> 8;\
|
|
_cr = tptr[2]; \
|
|
_cr += ((cr - _cr)*a + 127)>> 8;\
|
|
_cr += ((cr - _cr)*a + 127)>> 8;\
|
|
tptr[0] = (uchar)_cb; \
|
|
tptr[1] = (uchar)_cg; \
|
|
tptr[2] = (uchar)_cr; \
|
|
}
|
|
if( ax > ay )
|
|
{
|
|
int x = (int)(pt1.x >> XY_SHIFT);
|
|
|
|
for( ; ecount >= 0; x++, pt1.y += y_step, scount++, ecount-- )
|
|
{
|
|
if( (unsigned)x >= (unsigned)size0.width )
|
|
continue;
|
|
int y = (int)((pt1.y >> XY_SHIFT) - 1);
|
|
|
|
int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 +
|
|
(((ecount >= 2) + 1) & (ecount | 2))];
|
|
int a, dist = (pt1.y >> (XY_SHIFT - 5)) & 31;
|
|
|
|
a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff;
|
|
if( (unsigned)y < (unsigned)size0.height )
|
|
ICV_PUT_POINT(x, y)
|
|
|
|
a = (ep_corr * FilterTable[dist] >> 8) & 0xff;
|
|
if( (unsigned)(y+1) < (unsigned)size0.height )
|
|
ICV_PUT_POINT(x, y+1)
|
|
|
|
a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff;
|
|
if( (unsigned)(y+2) < (unsigned)size0.height )
|
|
ICV_PUT_POINT(x, y+2)
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int y = (int)(pt1.y >> XY_SHIFT);
|
|
|
|
for( ; ecount >= 0; y++, pt1.x += x_step, scount++, ecount-- )
|
|
{
|
|
if( (unsigned)y >= (unsigned)size0.height )
|
|
continue;
|
|
int x = (int)((pt1.x >> XY_SHIFT) - 1);
|
|
int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 +
|
|
(((ecount >= 2) + 1) & (ecount | 2))];
|
|
int a, dist = (pt1.x >> (XY_SHIFT - 5)) & 31;
|
|
|
|
a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff;
|
|
if( (unsigned)x < (unsigned)size0.width )
|
|
ICV_PUT_POINT(x, y)
|
|
|
|
a = (ep_corr * FilterTable[dist] >> 8) & 0xff;
|
|
if( (unsigned)(x+1) < (unsigned)size0.width )
|
|
ICV_PUT_POINT(x+1, y)
|
|
|
|
a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff;
|
|
if( (unsigned)(x+2) < (unsigned)size0.width )
|
|
ICV_PUT_POINT(x+2, y)
|
|
}
|
|
}
|
|
#undef ICV_PUT_POINT
|
|
}
|
|
else if(nch == 1)
|
|
{
|
|
#define ICV_PUT_POINT(x, y) \
|
|
{ \
|
|
uchar* tptr = ptr + (x) + (y) * step; \
|
|
_cb = tptr[0]; \
|
|
_cb += ((cb - _cb)*a + 127)>> 8;\
|
|
_cb += ((cb - _cb)*a + 127)>> 8;\
|
|
tptr[0] = (uchar)_cb; \
|
|
}
|
|
|
|
if( ax > ay )
|
|
{
|
|
int x = (int)(pt1.x >> XY_SHIFT);
|
|
|
|
for( ; ecount >= 0; x++, pt1.y += y_step, scount++, ecount-- )
|
|
{
|
|
if( (unsigned)x >= (unsigned)size0.width )
|
|
continue;
|
|
int y = (int)((pt1.y >> XY_SHIFT) - 1);
|
|
|
|
int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 +
|
|
(((ecount >= 2) + 1) & (ecount | 2))];
|
|
int a, dist = (pt1.y >> (XY_SHIFT - 5)) & 31;
|
|
|
|
a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff;
|
|
if( (unsigned)y < (unsigned)size0.height )
|
|
ICV_PUT_POINT(x, y)
|
|
|
|
a = (ep_corr * FilterTable[dist] >> 8) & 0xff;
|
|
if( (unsigned)(y+1) < (unsigned)size0.height )
|
|
ICV_PUT_POINT(x, y+1)
|
|
|
|
a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff;
|
|
if( (unsigned)(y+2) < (unsigned)size0.height )
|
|
ICV_PUT_POINT(x, y+2)
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int y = (int)(pt1.y >> XY_SHIFT);
|
|
|
|
for( ; ecount >= 0; y++, pt1.x += x_step, scount++, ecount-- )
|
|
{
|
|
if( (unsigned)y >= (unsigned)size0.height )
|
|
continue;
|
|
int x = (int)((pt1.x >> XY_SHIFT) - 1);
|
|
int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 +
|
|
(((ecount >= 2) + 1) & (ecount | 2))];
|
|
int a, dist = (pt1.x >> (XY_SHIFT - 5)) & 31;
|
|
|
|
a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff;
|
|
if( (unsigned)x < (unsigned)size0.width )
|
|
ICV_PUT_POINT(x, y)
|
|
|
|
a = (ep_corr * FilterTable[dist] >> 8) & 0xff;
|
|
if( (unsigned)(x+1) < (unsigned)size0.width )
|
|
ICV_PUT_POINT(x+1, y)
|
|
|
|
a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff;
|
|
if( (unsigned)(x+2) < (unsigned)size0.width )
|
|
ICV_PUT_POINT(x+2, y)
|
|
}
|
|
}
|
|
#undef ICV_PUT_POINT
|
|
}
|
|
else
|
|
{
|
|
#define ICV_PUT_POINT(x, y) \
|
|
{ \
|
|
uchar* tptr = ptr + (x)*4 + (y)*step; \
|
|
_cb = tptr[0]; \
|
|
_cb += ((cb - _cb)*a + 127)>> 8;\
|
|
_cb += ((cb - _cb)*a + 127)>> 8;\
|
|
_cg = tptr[1]; \
|
|
_cg += ((cg - _cg)*a + 127)>> 8;\
|
|
_cg += ((cg - _cg)*a + 127)>> 8;\
|
|
_cr = tptr[2]; \
|
|
_cr += ((cr - _cr)*a + 127)>> 8;\
|
|
_cr += ((cr - _cr)*a + 127)>> 8;\
|
|
_ca = tptr[3]; \
|
|
_ca += ((ca - _ca)*a + 127)>> 8;\
|
|
_ca += ((ca - _ca)*a + 127)>> 8;\
|
|
tptr[0] = (uchar)_cb; \
|
|
tptr[1] = (uchar)_cg; \
|
|
tptr[2] = (uchar)_cr; \
|
|
tptr[3] = (uchar)_ca; \
|
|
}
|
|
if( ax > ay )
|
|
{
|
|
int x = (int)(pt1.x >> XY_SHIFT);
|
|
|
|
for( ; ecount >= 0; x++, pt1.y += y_step, scount++, ecount-- )
|
|
{
|
|
if( (unsigned)x >= (unsigned)size0.width )
|
|
continue;
|
|
int y = (int)((pt1.y >> XY_SHIFT) - 1);
|
|
|
|
int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 +
|
|
(((ecount >= 2) + 1) & (ecount | 2))];
|
|
int a, dist = (pt1.y >> (XY_SHIFT - 5)) & 31;
|
|
|
|
a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff;
|
|
if( (unsigned)y < (unsigned)size0.height )
|
|
ICV_PUT_POINT(x, y)
|
|
|
|
a = (ep_corr * FilterTable[dist] >> 8) & 0xff;
|
|
if( (unsigned)(y+1) < (unsigned)size0.height )
|
|
ICV_PUT_POINT(x, y+1)
|
|
|
|
a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff;
|
|
if( (unsigned)(y+2) < (unsigned)size0.height )
|
|
ICV_PUT_POINT(x, y+2)
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int y = (int)(pt1.y >> XY_SHIFT);
|
|
|
|
for( ; ecount >= 0; y++, pt1.x += x_step, scount++, ecount-- )
|
|
{
|
|
if( (unsigned)y >= (unsigned)size0.height )
|
|
continue;
|
|
int x = (int)((pt1.x >> XY_SHIFT) - 1);
|
|
int ep_corr = ep_table[(((scount >= 2) + 1) & (scount | 2)) * 3 +
|
|
(((ecount >= 2) + 1) & (ecount | 2))];
|
|
int a, dist = (pt1.x >> (XY_SHIFT - 5)) & 31;
|
|
|
|
a = (ep_corr * FilterTable[dist + 32] >> 8) & 0xff;
|
|
if( (unsigned)x < (unsigned)size0.width )
|
|
ICV_PUT_POINT(x, y)
|
|
|
|
a = (ep_corr * FilterTable[dist] >> 8) & 0xff;
|
|
if( (unsigned)(x+1) < (unsigned)size0.width )
|
|
ICV_PUT_POINT(x+1, y)
|
|
|
|
a = (ep_corr * FilterTable[63 - dist] >> 8) & 0xff;
|
|
if( (unsigned)(x+2) < (unsigned)size0.width )
|
|
ICV_PUT_POINT(x+2, y)
|
|
}
|
|
}
|
|
#undef ICV_PUT_POINT
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
Line2( Mat& img, Point2l pt1, Point2l pt2, const void* color)
|
|
{
|
|
int64 dx, dy;
|
|
int ecount;
|
|
int64 ax, ay;
|
|
int64 i, j;
|
|
int x, y;
|
|
int64 x_step, y_step;
|
|
int cb = ((uchar*)color)[0];
|
|
int cg = ((uchar*)color)[1];
|
|
int cr = ((uchar*)color)[2];
|
|
int pix_size = (int)img.elemSize();
|
|
uchar *ptr = img.ptr(), *tptr;
|
|
size_t step = img.step;
|
|
Size size = img.size();
|
|
|
|
//CV_Assert( img && (nch == 1 || nch == 3) && img.depth() == CV_8U );
|
|
|
|
Size2l sizeScaled(((int64)size.width) << XY_SHIFT, ((int64)size.height) << XY_SHIFT);
|
|
if( !clipLine( sizeScaled, pt1, pt2 ))
|
|
return;
|
|
|
|
dx = pt2.x - pt1.x;
|
|
dy = pt2.y - pt1.y;
|
|
|
|
j = dx < 0 ? -1 : 0;
|
|
ax = (dx ^ j) - j;
|
|
i = dy < 0 ? -1 : 0;
|
|
ay = (dy ^ i) - i;
|
|
|
|
if( ax > ay )
|
|
{
|
|
dy = (dy ^ j) - j;
|
|
pt1.x ^= pt2.x & j;
|
|
pt2.x ^= pt1.x & j;
|
|
pt1.x ^= pt2.x & j;
|
|
pt1.y ^= pt2.y & j;
|
|
pt2.y ^= pt1.y & j;
|
|
pt1.y ^= pt2.y & j;
|
|
|
|
x_step = XY_ONE;
|
|
y_step = dy * (1 << XY_SHIFT) / (ax | 1);
|
|
ecount = (int)((pt2.x - pt1.x) >> XY_SHIFT);
|
|
}
|
|
else
|
|
{
|
|
dx = (dx ^ i) - i;
|
|
pt1.x ^= pt2.x & i;
|
|
pt2.x ^= pt1.x & i;
|
|
pt1.x ^= pt2.x & i;
|
|
pt1.y ^= pt2.y & i;
|
|
pt2.y ^= pt1.y & i;
|
|
pt1.y ^= pt2.y & i;
|
|
|
|
x_step = dx * (1 << XY_SHIFT) / (ay | 1);
|
|
y_step = XY_ONE;
|
|
ecount = (int)((pt2.y - pt1.y) >> XY_SHIFT);
|
|
}
|
|
|
|
pt1.x += (XY_ONE >> 1);
|
|
pt1.y += (XY_ONE >> 1);
|
|
|
|
if( pix_size == 3 )
|
|
{
|
|
#define ICV_PUT_POINT(_x,_y) \
|
|
x = (_x); y = (_y); \
|
|
if( 0 <= x && x < size.width && \
|
|
0 <= y && y < size.height ) \
|
|
{ \
|
|
tptr = ptr + y*step + x*3; \
|
|
tptr[0] = (uchar)cb; \
|
|
tptr[1] = (uchar)cg; \
|
|
tptr[2] = (uchar)cr; \
|
|
}
|
|
|
|
ICV_PUT_POINT((int)((pt2.x + (XY_ONE >> 1)) >> XY_SHIFT),
|
|
(int)((pt2.y + (XY_ONE >> 1)) >> XY_SHIFT));
|
|
|
|
if( ax > ay )
|
|
{
|
|
pt1.x >>= XY_SHIFT;
|
|
|
|
while( ecount >= 0 )
|
|
{
|
|
ICV_PUT_POINT((int)(pt1.x), (int)(pt1.y >> XY_SHIFT));
|
|
pt1.x++;
|
|
pt1.y += y_step;
|
|
ecount--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pt1.y >>= XY_SHIFT;
|
|
|
|
while( ecount >= 0 )
|
|
{
|
|
ICV_PUT_POINT((int)(pt1.x >> XY_SHIFT), (int)(pt1.y));
|
|
pt1.x += x_step;
|
|
pt1.y++;
|
|
ecount--;
|
|
}
|
|
}
|
|
|
|
#undef ICV_PUT_POINT
|
|
}
|
|
else if( pix_size == 1 )
|
|
{
|
|
#define ICV_PUT_POINT(_x,_y) \
|
|
x = (_x); y = (_y); \
|
|
if( 0 <= x && x < size.width && \
|
|
0 <= y && y < size.height ) \
|
|
{ \
|
|
tptr = ptr + y*step + x;\
|
|
tptr[0] = (uchar)cb; \
|
|
}
|
|
|
|
ICV_PUT_POINT((int)((pt2.x + (XY_ONE >> 1)) >> XY_SHIFT),
|
|
(int)((pt2.y + (XY_ONE >> 1)) >> XY_SHIFT));
|
|
|
|
if( ax > ay )
|
|
{
|
|
pt1.x >>= XY_SHIFT;
|
|
|
|
while( ecount >= 0 )
|
|
{
|
|
ICV_PUT_POINT((int)(pt1.x), (int)(pt1.y >> XY_SHIFT));
|
|
pt1.x++;
|
|
pt1.y += y_step;
|
|
ecount--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pt1.y >>= XY_SHIFT;
|
|
|
|
while( ecount >= 0 )
|
|
{
|
|
ICV_PUT_POINT((int)(pt1.x >> XY_SHIFT), (int)(pt1.y));
|
|
pt1.x += x_step;
|
|
pt1.y++;
|
|
ecount--;
|
|
}
|
|
}
|
|
|
|
#undef ICV_PUT_POINT
|
|
}
|
|
else
|
|
{
|
|
#define ICV_PUT_POINT(_x,_y) \
|
|
x = (_x); y = (_y); \
|
|
if( 0 <= x && x < size.width && \
|
|
0 <= y && y < size.height ) \
|
|
{ \
|
|
tptr = ptr + y*step + x*pix_size;\
|
|
for( j = 0; j < pix_size; j++ ) \
|
|
tptr[j] = ((uchar*)color)[j]; \
|
|
}
|
|
|
|
ICV_PUT_POINT((int)((pt2.x + (XY_ONE >> 1)) >> XY_SHIFT),
|
|
(int)((pt2.y + (XY_ONE >> 1)) >> XY_SHIFT));
|
|
|
|
if( ax > ay )
|
|
{
|
|
pt1.x >>= XY_SHIFT;
|
|
|
|
while( ecount >= 0 )
|
|
{
|
|
ICV_PUT_POINT((int)(pt1.x), (int)(pt1.y >> XY_SHIFT));
|
|
pt1.x++;
|
|
pt1.y += y_step;
|
|
ecount--;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pt1.y >>= XY_SHIFT;
|
|
|
|
while( ecount >= 0 )
|
|
{
|
|
ICV_PUT_POINT((int)(pt1.x >> XY_SHIFT), (int)(pt1.y));
|
|
pt1.x += x_step;
|
|
pt1.y++;
|
|
ecount--;
|
|
}
|
|
}
|
|
|
|
#undef ICV_PUT_POINT
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************************\
|
|
* Antialiazed Elliptic Arcs via Antialiazed Lines *
|
|
\****************************************************************************************/
|
|
|
|
static const float SinTable[] =
|
|
{ 0.0000000f, 0.0174524f, 0.0348995f, 0.0523360f, 0.0697565f, 0.0871557f,
|
|
0.1045285f, 0.1218693f, 0.1391731f, 0.1564345f, 0.1736482f, 0.1908090f,
|
|
0.2079117f, 0.2249511f, 0.2419219f, 0.2588190f, 0.2756374f, 0.2923717f,
|
|
0.3090170f, 0.3255682f, 0.3420201f, 0.3583679f, 0.3746066f, 0.3907311f,
|
|
0.4067366f, 0.4226183f, 0.4383711f, 0.4539905f, 0.4694716f, 0.4848096f,
|
|
0.5000000f, 0.5150381f, 0.5299193f, 0.5446390f, 0.5591929f, 0.5735764f,
|
|
0.5877853f, 0.6018150f, 0.6156615f, 0.6293204f, 0.6427876f, 0.6560590f,
|
|
0.6691306f, 0.6819984f, 0.6946584f, 0.7071068f, 0.7193398f, 0.7313537f,
|
|
0.7431448f, 0.7547096f, 0.7660444f, 0.7771460f, 0.7880108f, 0.7986355f,
|
|
0.8090170f, 0.8191520f, 0.8290376f, 0.8386706f, 0.8480481f, 0.8571673f,
|
|
0.8660254f, 0.8746197f, 0.8829476f, 0.8910065f, 0.8987940f, 0.9063078f,
|
|
0.9135455f, 0.9205049f, 0.9271839f, 0.9335804f, 0.9396926f, 0.9455186f,
|
|
0.9510565f, 0.9563048f, 0.9612617f, 0.9659258f, 0.9702957f, 0.9743701f,
|
|
0.9781476f, 0.9816272f, 0.9848078f, 0.9876883f, 0.9902681f, 0.9925462f,
|
|
0.9945219f, 0.9961947f, 0.9975641f, 0.9986295f, 0.9993908f, 0.9998477f,
|
|
1.0000000f, 0.9998477f, 0.9993908f, 0.9986295f, 0.9975641f, 0.9961947f,
|
|
0.9945219f, 0.9925462f, 0.9902681f, 0.9876883f, 0.9848078f, 0.9816272f,
|
|
0.9781476f, 0.9743701f, 0.9702957f, 0.9659258f, 0.9612617f, 0.9563048f,
|
|
0.9510565f, 0.9455186f, 0.9396926f, 0.9335804f, 0.9271839f, 0.9205049f,
|
|
0.9135455f, 0.9063078f, 0.8987940f, 0.8910065f, 0.8829476f, 0.8746197f,
|
|
0.8660254f, 0.8571673f, 0.8480481f, 0.8386706f, 0.8290376f, 0.8191520f,
|
|
0.8090170f, 0.7986355f, 0.7880108f, 0.7771460f, 0.7660444f, 0.7547096f,
|
|
0.7431448f, 0.7313537f, 0.7193398f, 0.7071068f, 0.6946584f, 0.6819984f,
|
|
0.6691306f, 0.6560590f, 0.6427876f, 0.6293204f, 0.6156615f, 0.6018150f,
|
|
0.5877853f, 0.5735764f, 0.5591929f, 0.5446390f, 0.5299193f, 0.5150381f,
|
|
0.5000000f, 0.4848096f, 0.4694716f, 0.4539905f, 0.4383711f, 0.4226183f,
|
|
0.4067366f, 0.3907311f, 0.3746066f, 0.3583679f, 0.3420201f, 0.3255682f,
|
|
0.3090170f, 0.2923717f, 0.2756374f, 0.2588190f, 0.2419219f, 0.2249511f,
|
|
0.2079117f, 0.1908090f, 0.1736482f, 0.1564345f, 0.1391731f, 0.1218693f,
|
|
0.1045285f, 0.0871557f, 0.0697565f, 0.0523360f, 0.0348995f, 0.0174524f,
|
|
0.0000000f, -0.0174524f, -0.0348995f, -0.0523360f, -0.0697565f, -0.0871557f,
|
|
-0.1045285f, -0.1218693f, -0.1391731f, -0.1564345f, -0.1736482f, -0.1908090f,
|
|
-0.2079117f, -0.2249511f, -0.2419219f, -0.2588190f, -0.2756374f, -0.2923717f,
|
|
-0.3090170f, -0.3255682f, -0.3420201f, -0.3583679f, -0.3746066f, -0.3907311f,
|
|
-0.4067366f, -0.4226183f, -0.4383711f, -0.4539905f, -0.4694716f, -0.4848096f,
|
|
-0.5000000f, -0.5150381f, -0.5299193f, -0.5446390f, -0.5591929f, -0.5735764f,
|
|
-0.5877853f, -0.6018150f, -0.6156615f, -0.6293204f, -0.6427876f, -0.6560590f,
|
|
-0.6691306f, -0.6819984f, -0.6946584f, -0.7071068f, -0.7193398f, -0.7313537f,
|
|
-0.7431448f, -0.7547096f, -0.7660444f, -0.7771460f, -0.7880108f, -0.7986355f,
|
|
-0.8090170f, -0.8191520f, -0.8290376f, -0.8386706f, -0.8480481f, -0.8571673f,
|
|
-0.8660254f, -0.8746197f, -0.8829476f, -0.8910065f, -0.8987940f, -0.9063078f,
|
|
-0.9135455f, -0.9205049f, -0.9271839f, -0.9335804f, -0.9396926f, -0.9455186f,
|
|
-0.9510565f, -0.9563048f, -0.9612617f, -0.9659258f, -0.9702957f, -0.9743701f,
|
|
-0.9781476f, -0.9816272f, -0.9848078f, -0.9876883f, -0.9902681f, -0.9925462f,
|
|
-0.9945219f, -0.9961947f, -0.9975641f, -0.9986295f, -0.9993908f, -0.9998477f,
|
|
-1.0000000f, -0.9998477f, -0.9993908f, -0.9986295f, -0.9975641f, -0.9961947f,
|
|
-0.9945219f, -0.9925462f, -0.9902681f, -0.9876883f, -0.9848078f, -0.9816272f,
|
|
-0.9781476f, -0.9743701f, -0.9702957f, -0.9659258f, -0.9612617f, -0.9563048f,
|
|
-0.9510565f, -0.9455186f, -0.9396926f, -0.9335804f, -0.9271839f, -0.9205049f,
|
|
-0.9135455f, -0.9063078f, -0.8987940f, -0.8910065f, -0.8829476f, -0.8746197f,
|
|
-0.8660254f, -0.8571673f, -0.8480481f, -0.8386706f, -0.8290376f, -0.8191520f,
|
|
-0.8090170f, -0.7986355f, -0.7880108f, -0.7771460f, -0.7660444f, -0.7547096f,
|
|
-0.7431448f, -0.7313537f, -0.7193398f, -0.7071068f, -0.6946584f, -0.6819984f,
|
|
-0.6691306f, -0.6560590f, -0.6427876f, -0.6293204f, -0.6156615f, -0.6018150f,
|
|
-0.5877853f, -0.5735764f, -0.5591929f, -0.5446390f, -0.5299193f, -0.5150381f,
|
|
-0.5000000f, -0.4848096f, -0.4694716f, -0.4539905f, -0.4383711f, -0.4226183f,
|
|
-0.4067366f, -0.3907311f, -0.3746066f, -0.3583679f, -0.3420201f, -0.3255682f,
|
|
-0.3090170f, -0.2923717f, -0.2756374f, -0.2588190f, -0.2419219f, -0.2249511f,
|
|
-0.2079117f, -0.1908090f, -0.1736482f, -0.1564345f, -0.1391731f, -0.1218693f,
|
|
-0.1045285f, -0.0871557f, -0.0697565f, -0.0523360f, -0.0348995f, -0.0174524f,
|
|
-0.0000000f, 0.0174524f, 0.0348995f, 0.0523360f, 0.0697565f, 0.0871557f,
|
|
0.1045285f, 0.1218693f, 0.1391731f, 0.1564345f, 0.1736482f, 0.1908090f,
|
|
0.2079117f, 0.2249511f, 0.2419219f, 0.2588190f, 0.2756374f, 0.2923717f,
|
|
0.3090170f, 0.3255682f, 0.3420201f, 0.3583679f, 0.3746066f, 0.3907311f,
|
|
0.4067366f, 0.4226183f, 0.4383711f, 0.4539905f, 0.4694716f, 0.4848096f,
|
|
0.5000000f, 0.5150381f, 0.5299193f, 0.5446390f, 0.5591929f, 0.5735764f,
|
|
0.5877853f, 0.6018150f, 0.6156615f, 0.6293204f, 0.6427876f, 0.6560590f,
|
|
0.6691306f, 0.6819984f, 0.6946584f, 0.7071068f, 0.7193398f, 0.7313537f,
|
|
0.7431448f, 0.7547096f, 0.7660444f, 0.7771460f, 0.7880108f, 0.7986355f,
|
|
0.8090170f, 0.8191520f, 0.8290376f, 0.8386706f, 0.8480481f, 0.8571673f,
|
|
0.8660254f, 0.8746197f, 0.8829476f, 0.8910065f, 0.8987940f, 0.9063078f,
|
|
0.9135455f, 0.9205049f, 0.9271839f, 0.9335804f, 0.9396926f, 0.9455186f,
|
|
0.9510565f, 0.9563048f, 0.9612617f, 0.9659258f, 0.9702957f, 0.9743701f,
|
|
0.9781476f, 0.9816272f, 0.9848078f, 0.9876883f, 0.9902681f, 0.9925462f,
|
|
0.9945219f, 0.9961947f, 0.9975641f, 0.9986295f, 0.9993908f, 0.9998477f,
|
|
1.0000000f
|
|
};
|
|
|
|
|
|
static void
|
|
sincos( int angle, float& cosval, float& sinval )
|
|
{
|
|
angle += (angle < 0 ? 360 : 0);
|
|
sinval = SinTable[angle];
|
|
cosval = SinTable[450 - angle];
|
|
}
|
|
|
|
/*
|
|
constructs polygon that represents elliptic arc.
|
|
*/
|
|
void ellipse2Poly( Point center, Size axes, int angle,
|
|
int arcStart, int arcEnd,
|
|
int delta, CV_OUT std::vector<Point>& pts )
|
|
{
|
|
std::vector<Point2d> _pts;
|
|
ellipse2Poly(Point2d(center.x, center.y), Size2d(axes.width, axes.height), angle,
|
|
arcStart, arcEnd, delta, _pts);
|
|
Point prevPt(INT_MIN, INT_MIN);
|
|
pts.resize(0);
|
|
for (unsigned int i = 0; i < _pts.size(); ++i)
|
|
{
|
|
Point pt;
|
|
pt.x = cvRound(_pts[i].x);
|
|
pt.y = cvRound(_pts[i].y);
|
|
if (pt != prevPt) {
|
|
pts.push_back(pt);
|
|
prevPt = pt;
|
|
}
|
|
}
|
|
|
|
// If there are no points, it's a zero-size polygon
|
|
CV_Assert( !pts.empty() );
|
|
if (pts.size() == 1) {
|
|
pts.assign(2, center);
|
|
}
|
|
}
|
|
|
|
void ellipse2Poly( Point2d center, Size2d axes, int angle,
|
|
int arc_start, int arc_end,
|
|
int delta, std::vector<Point2d>& pts )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
CV_Assert(0 < delta && delta <= 180);
|
|
|
|
float alpha, beta;
|
|
int i;
|
|
|
|
while( angle < 0 )
|
|
angle += 360;
|
|
while( angle > 360 )
|
|
angle -= 360;
|
|
|
|
if( arc_start > arc_end )
|
|
{
|
|
i = arc_start;
|
|
arc_start = arc_end;
|
|
arc_end = i;
|
|
}
|
|
while( arc_start < 0 )
|
|
{
|
|
arc_start += 360;
|
|
arc_end += 360;
|
|
}
|
|
while( arc_end > 360 )
|
|
{
|
|
arc_end -= 360;
|
|
arc_start -= 360;
|
|
}
|
|
if( arc_end - arc_start > 360 )
|
|
{
|
|
arc_start = 0;
|
|
arc_end = 360;
|
|
}
|
|
sincos( angle, alpha, beta );
|
|
pts.resize(0);
|
|
|
|
for( i = arc_start; i < arc_end + delta; i += delta )
|
|
{
|
|
double x, y;
|
|
angle = i;
|
|
if( angle > arc_end )
|
|
angle = arc_end;
|
|
if( angle < 0 )
|
|
angle += 360;
|
|
|
|
x = axes.width * SinTable[450-angle];
|
|
y = axes.height * SinTable[angle];
|
|
Point2d pt;
|
|
pt.x = center.x + x * alpha - y * beta;
|
|
pt.y = center.y + x * beta + y * alpha;
|
|
pts.push_back(pt);
|
|
}
|
|
|
|
// If there are no points, it's a zero-size polygon
|
|
CV_Assert( !pts.empty() );
|
|
if( pts.size() == 1) {
|
|
pts.assign(2,center);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
EllipseEx( Mat& img, Point2l center, Size2l axes,
|
|
int angle, int arc_start, int arc_end,
|
|
const void* color, int thickness, int line_type )
|
|
{
|
|
axes.width = std::abs(axes.width), axes.height = std::abs(axes.height);
|
|
int delta = (int)((std::max(axes.width,axes.height)+(XY_ONE>>1))>>XY_SHIFT);
|
|
delta = delta < 3 ? 90 : delta < 10 ? 30 : delta < 15 ? 18 : 5;
|
|
|
|
std::vector<Point2d> _v;
|
|
ellipse2Poly( Point2d((double)center.x, (double)center.y), Size2d((double)axes.width, (double)axes.height), angle, arc_start, arc_end, delta, _v );
|
|
|
|
std::vector<Point2l> v;
|
|
Point2l prevPt(0xFFFFFFFFFFFFFFFF, 0xFFFFFFFFFFFFFFFF);
|
|
for (unsigned int i = 0; i < _v.size(); ++i)
|
|
{
|
|
Point2l pt;
|
|
pt.x = (int64)cvRound(_v[i].x / XY_ONE) << XY_SHIFT;
|
|
pt.y = (int64)cvRound(_v[i].y / XY_ONE) << XY_SHIFT;
|
|
pt.x += cvRound(_v[i].x - pt.x);
|
|
pt.y += cvRound(_v[i].y - pt.y);
|
|
if (pt != prevPt) {
|
|
v.push_back(pt);
|
|
prevPt = pt;
|
|
}
|
|
}
|
|
|
|
// If there are no points, it's a zero-size polygon
|
|
if (v.size() <= 1) {
|
|
v.assign(2, center);
|
|
}
|
|
|
|
if( thickness >= 0 )
|
|
PolyLine( img, &v[0], (int)v.size(), false, color, thickness, line_type, XY_SHIFT );
|
|
else if( arc_end - arc_start >= 360 )
|
|
FillConvexPoly( img, &v[0], (int)v.size(), color, line_type, XY_SHIFT );
|
|
else
|
|
{
|
|
v.push_back(center);
|
|
std::vector<PolyEdge> edges;
|
|
CollectPolyEdges( img, &v[0], (int)v.size(), edges, color, line_type, XY_SHIFT );
|
|
FillEdgeCollection( img, edges, color, line_type );
|
|
}
|
|
}
|
|
|
|
|
|
/****************************************************************************************\
|
|
* Polygons filling *
|
|
\****************************************************************************************/
|
|
|
|
static inline void ICV_HLINE_X(uchar* ptr, int64_t xl, int64_t xr, const uchar* color, int pix_size)
|
|
{
|
|
uchar* hline_min_ptr = (uchar*)(ptr) + (xl)*(pix_size);
|
|
uchar* hline_end_ptr = (uchar*)(ptr) + (xr+1)*(pix_size);
|
|
uchar* hline_ptr = hline_min_ptr;
|
|
if (pix_size == 1)
|
|
memset(hline_min_ptr, *color, hline_end_ptr-hline_min_ptr);
|
|
else//if (pix_size != 1)
|
|
{
|
|
if (hline_min_ptr < hline_end_ptr)
|
|
{
|
|
memcpy(hline_ptr, color, pix_size);
|
|
hline_ptr += pix_size;
|
|
}//end if (hline_min_ptr < hline_end_ptr)
|
|
size_t sizeToCopy = pix_size;
|
|
while(hline_ptr < hline_end_ptr)
|
|
{
|
|
memcpy(hline_ptr, hline_min_ptr, sizeToCopy);
|
|
hline_ptr += sizeToCopy;
|
|
sizeToCopy = std::min(2*sizeToCopy, static_cast<size_t>(hline_end_ptr-hline_ptr));
|
|
}//end while(hline_ptr < hline_end_ptr)
|
|
}//end if (pix_size != 1)
|
|
}
|
|
//end ICV_HLINE_X()
|
|
|
|
static inline void ICV_HLINE(uchar* ptr, int64_t xl, int64_t xr, const void* color, int pix_size)
|
|
{
|
|
ICV_HLINE_X(ptr, xl, xr, reinterpret_cast<const uchar*>(color), pix_size);
|
|
}
|
|
//end ICV_HLINE()
|
|
|
|
/* filling convex polygon. v - array of vertices, ntps - number of points */
|
|
static void
|
|
FillConvexPoly( Mat& img, const Point2l* v, int npts, const void* color, int line_type, int shift )
|
|
{
|
|
struct
|
|
{
|
|
int idx, di;
|
|
int64 x, dx;
|
|
int ye;
|
|
}
|
|
edge[2];
|
|
|
|
int delta = 1 << shift >> 1;
|
|
int i, y, imin = 0;
|
|
int edges = npts;
|
|
int64 xmin, xmax, ymin, ymax;
|
|
uchar* ptr = img.ptr();
|
|
Size size = img.size();
|
|
int pix_size = (int)img.elemSize();
|
|
Point2l p0;
|
|
int delta1, delta2;
|
|
|
|
if( line_type < cv::LINE_AA )
|
|
delta1 = delta2 = XY_ONE >> 1;
|
|
else
|
|
delta1 = XY_ONE - 1, delta2 = 0;
|
|
|
|
p0 = v[npts - 1];
|
|
p0.x <<= XY_SHIFT - shift;
|
|
p0.y <<= XY_SHIFT - shift;
|
|
|
|
CV_Assert( 0 <= shift && shift <= XY_SHIFT );
|
|
xmin = xmax = v[0].x;
|
|
ymin = ymax = v[0].y;
|
|
|
|
for( i = 0; i < npts; i++ )
|
|
{
|
|
Point2l p = v[i];
|
|
if( p.y < ymin )
|
|
{
|
|
ymin = p.y;
|
|
imin = i;
|
|
}
|
|
|
|
ymax = std::max( ymax, p.y );
|
|
xmax = std::max( xmax, p.x );
|
|
xmin = MIN( xmin, p.x );
|
|
|
|
p.x <<= XY_SHIFT - shift;
|
|
p.y <<= XY_SHIFT - shift;
|
|
|
|
if( line_type <= 8 )
|
|
{
|
|
if( shift == 0 )
|
|
{
|
|
Point pt0, pt1;
|
|
pt0.x = (int)(p0.x >> XY_SHIFT);
|
|
pt0.y = (int)(p0.y >> XY_SHIFT);
|
|
pt1.x = (int)(p.x >> XY_SHIFT);
|
|
pt1.y = (int)(p.y >> XY_SHIFT);
|
|
Line( img, pt0, pt1, color, line_type );
|
|
}
|
|
else
|
|
Line2( img, p0, p, color );
|
|
}
|
|
else
|
|
LineAA( img, p0, p, color );
|
|
p0 = p;
|
|
}
|
|
|
|
xmin = (xmin + delta) >> shift;
|
|
xmax = (xmax + delta) >> shift;
|
|
ymin = (ymin + delta) >> shift;
|
|
ymax = (ymax + delta) >> shift;
|
|
|
|
if( npts < 3 || (int)xmax < 0 || (int)ymax < 0 || (int)xmin >= size.width || (int)ymin >= size.height )
|
|
return;
|
|
|
|
ymax = MIN( ymax, size.height - 1 );
|
|
edge[0].idx = edge[1].idx = imin;
|
|
|
|
edge[0].ye = edge[1].ye = y = (int)ymin;
|
|
edge[0].di = 1;
|
|
edge[1].di = npts - 1;
|
|
|
|
edge[0].x = edge[1].x = -XY_ONE;
|
|
edge[0].dx = edge[1].dx = 0;
|
|
|
|
ptr += (int64_t)img.step*y;
|
|
|
|
do
|
|
{
|
|
if( line_type < cv::LINE_AA || y < (int)ymax || y == (int)ymin )
|
|
{
|
|
for( i = 0; i < 2; i++ )
|
|
{
|
|
if( y >= edge[i].ye )
|
|
{
|
|
int idx0 = edge[i].idx, di = edge[i].di;
|
|
int idx = idx0 + di;
|
|
if (idx >= npts) idx -= npts;
|
|
int ty = 0;
|
|
|
|
for (; edges-- > 0; )
|
|
{
|
|
ty = (int)((v[idx].y + delta) >> shift);
|
|
if (ty > y)
|
|
{
|
|
int64 xs = v[idx0].x;
|
|
int64 xe = v[idx].x;
|
|
if (shift != XY_SHIFT)
|
|
{
|
|
xs <<= XY_SHIFT - shift;
|
|
xe <<= XY_SHIFT - shift;
|
|
}
|
|
|
|
edge[i].ye = ty;
|
|
edge[i].dx = ((xe - xs)*2 + ((int64_t)ty - y)) / (2 * ((int64_t)ty - y));
|
|
edge[i].x = xs;
|
|
edge[i].idx = idx;
|
|
break;
|
|
}
|
|
idx0 = idx;
|
|
idx += di;
|
|
if (idx >= npts) idx -= npts;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (edges < 0)
|
|
break;
|
|
|
|
if (y >= 0)
|
|
{
|
|
int left = 0, right = 1;
|
|
if (edge[0].x > edge[1].x)
|
|
{
|
|
left = 1, right = 0;
|
|
}
|
|
|
|
int xx1 = (int)((edge[left].x + delta1) >> XY_SHIFT);
|
|
int xx2 = (int)((edge[right].x + delta2) >> XY_SHIFT);
|
|
|
|
if( xx2 >= 0 && xx1 < size.width )
|
|
{
|
|
if( xx1 < 0 )
|
|
xx1 = 0;
|
|
if( xx2 >= size.width )
|
|
xx2 = size.width - 1;
|
|
ICV_HLINE( ptr, xx1, xx2, color, pix_size );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO optimize scan for negative y
|
|
}
|
|
|
|
edge[0].x += edge[0].dx;
|
|
edge[1].x += edge[1].dx;
|
|
ptr += img.step;
|
|
}
|
|
while( ++y <= (int)ymax );
|
|
}
|
|
|
|
|
|
/******** Arbitrary polygon **********/
|
|
|
|
static void
|
|
CollectPolyEdges( Mat& img, const Point2l* v, int count, std::vector<PolyEdge>& edges,
|
|
const void* color, int line_type, int shift, Point offset )
|
|
{
|
|
int i, delta = offset.y + ((1 << shift) >> 1);
|
|
Point2l pt0 = v[count-1], pt1;
|
|
pt0.x = (pt0.x + offset.x) << (XY_SHIFT - shift);
|
|
pt0.y = (pt0.y + delta) >> shift;
|
|
|
|
edges.reserve( edges.size() + count );
|
|
|
|
for( i = 0; i < count; i++, pt0 = pt1 )
|
|
{
|
|
Point2l t0, t1;
|
|
PolyEdge edge;
|
|
|
|
pt1 = v[i];
|
|
pt1.x = (pt1.x + offset.x) << (XY_SHIFT - shift);
|
|
pt1.y = (pt1.y + delta) >> shift;
|
|
|
|
Point2l pt0c(pt0), pt1c(pt1);
|
|
|
|
if (line_type < cv::LINE_AA)
|
|
{
|
|
t0.y = pt0.y; t1.y = pt1.y;
|
|
t0.x = (pt0.x + (XY_ONE >> 1)) >> XY_SHIFT;
|
|
t1.x = (pt1.x + (XY_ONE >> 1)) >> XY_SHIFT;
|
|
Line(img, t0, t1, color, line_type);
|
|
|
|
// use clipped endpoints to create a more accurate PolyEdge
|
|
if ((unsigned)t0.x >= (unsigned)(img.cols) ||
|
|
(unsigned)t1.x >= (unsigned)(img.cols) ||
|
|
(unsigned)t0.y >= (unsigned)(img.rows) ||
|
|
(unsigned)t1.y >= (unsigned)(img.rows))
|
|
{
|
|
clipLine(img.size(), t0, t1);
|
|
|
|
if (t0.y != t1.y)
|
|
{
|
|
pt0c.y = t0.y; pt1c.y = t1.y;
|
|
pt0c.x = (int64)(t0.x) << XY_SHIFT;
|
|
pt1c.x = (int64)(t1.x) << XY_SHIFT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pt0c.x += XY_ONE >> 1;
|
|
pt1c.x += XY_ONE >> 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
t0.x = pt0.x; t1.x = pt1.x;
|
|
t0.y = pt0.y << XY_SHIFT;
|
|
t1.y = pt1.y << XY_SHIFT;
|
|
LineAA(img, t0, t1, color);
|
|
}
|
|
|
|
if (pt0.y == pt1.y)
|
|
continue;
|
|
|
|
edge.dx = (pt1c.x - pt0c.x) / (pt1c.y - pt0c.y);
|
|
if (pt0.y < pt1.y)
|
|
{
|
|
edge.y0 = (int)(pt0.y);
|
|
edge.y1 = (int)(pt1.y);
|
|
edge.x = pt0c.x + (pt0.y - pt0c.y) * edge.dx; // correct starting point for clipped lines
|
|
}
|
|
else
|
|
{
|
|
edge.y0 = (int)(pt1.y);
|
|
edge.y1 = (int)(pt0.y);
|
|
edge.x = pt1c.x + (pt1.y - pt1c.y) * edge.dx; // correct starting point for clipped lines
|
|
}
|
|
edges.push_back(edge);
|
|
}
|
|
}
|
|
|
|
struct CmpEdges
|
|
{
|
|
bool operator ()(const PolyEdge& e1, const PolyEdge& e2)
|
|
{
|
|
return e1.y0 - e2.y0 ? e1.y0 < e2.y0 :
|
|
e1.x - e2.x ? e1.x < e2.x : e1.dx < e2.dx;
|
|
}
|
|
};
|
|
|
|
/**************** helper macros and functions for sequence/contour processing ***********/
|
|
|
|
static void
|
|
FillEdgeCollection( Mat& img, std::vector<PolyEdge>& edges, const void* color, int line_type)
|
|
{
|
|
PolyEdge tmp;
|
|
int i, y, total = (int)edges.size();
|
|
Size size = img.size();
|
|
PolyEdge* e;
|
|
int y_max = INT_MIN, y_min = INT_MAX;
|
|
int64 x_max = 0xFFFFFFFFFFFFFFFF, x_min = 0x7FFFFFFFFFFFFFFF;
|
|
int pix_size = (int)img.elemSize();
|
|
int delta;
|
|
|
|
if (line_type < CV_AA)
|
|
delta = 0;
|
|
else
|
|
delta = XY_ONE - 1;
|
|
|
|
if( total < 2 )
|
|
return;
|
|
|
|
for( i = 0; i < total; i++ )
|
|
{
|
|
PolyEdge& e1 = edges[i];
|
|
CV_Assert( e1.y0 < e1.y1 );
|
|
// Determine x-coordinate of the end of the edge.
|
|
// (This is not necessary x-coordinate of any vertex in the array.)
|
|
int64 x1 = e1.x + (e1.y1 - e1.y0) * e1.dx;
|
|
y_min = std::min( y_min, e1.y0 );
|
|
y_max = std::max( y_max, e1.y1 );
|
|
x_min = std::min( x_min, e1.x );
|
|
x_max = std::max( x_max, e1.x );
|
|
x_min = std::min( x_min, x1 );
|
|
x_max = std::max( x_max, x1 );
|
|
}
|
|
|
|
if( y_max < 0 || y_min >= size.height || x_max < 0 || x_min >= ((int64)size.width<<XY_SHIFT) )
|
|
return;
|
|
|
|
std::sort( edges.begin(), edges.end(), CmpEdges() );
|
|
|
|
// start drawing
|
|
tmp.y0 = INT_MAX;
|
|
edges.push_back(tmp); // after this point we do not add
|
|
// any elements to edges, thus we can use pointers
|
|
i = 0;
|
|
tmp.next = 0;
|
|
e = &edges[i];
|
|
y_max = MIN( y_max, size.height );
|
|
|
|
for( y = e->y0; y < y_max; y++ )
|
|
{
|
|
PolyEdge *last, *prelast, *keep_prelast;
|
|
int draw = 0;
|
|
int clipline = y < 0;
|
|
|
|
prelast = &tmp;
|
|
last = tmp.next;
|
|
while( last || e->y0 == y )
|
|
{
|
|
if( last && last->y1 == y )
|
|
{
|
|
// exclude edge if y reaches its lower point
|
|
prelast->next = last->next;
|
|
last = last->next;
|
|
continue;
|
|
}
|
|
keep_prelast = prelast;
|
|
if( last && (e->y0 > y || last->x < e->x) )
|
|
{
|
|
// go to the next edge in active list
|
|
prelast = last;
|
|
last = last->next;
|
|
}
|
|
else if( i < total )
|
|
{
|
|
// insert new edge into active list if y reaches its upper point
|
|
prelast->next = e;
|
|
e->next = last;
|
|
prelast = e;
|
|
e = &edges[++i];
|
|
}
|
|
else
|
|
break;
|
|
|
|
if( draw )
|
|
{
|
|
if( !clipline )
|
|
{
|
|
// convert x's from fixed-point to image coordinates
|
|
uchar *timg = img.ptr(y);
|
|
int x1, x2;
|
|
|
|
if (keep_prelast->x > prelast->x)
|
|
{
|
|
x1 = (int)((prelast->x + delta) >> XY_SHIFT);
|
|
x2 = (int)(keep_prelast->x >> XY_SHIFT);
|
|
}
|
|
else
|
|
{
|
|
x1 = (int)((keep_prelast->x + delta) >> XY_SHIFT);
|
|
x2 = (int)(prelast->x >> XY_SHIFT);
|
|
}
|
|
|
|
// clip and draw the line
|
|
if( x1 < size.width && x2 >= 0 )
|
|
{
|
|
if( x1 < 0 )
|
|
x1 = 0;
|
|
if( x2 >= size.width )
|
|
x2 = size.width - 1;
|
|
ICV_HLINE( timg, x1, x2, color, pix_size );
|
|
}
|
|
}
|
|
keep_prelast->x += keep_prelast->dx;
|
|
prelast->x += prelast->dx;
|
|
}
|
|
draw ^= 1;
|
|
}
|
|
|
|
// sort edges (using bubble sort)
|
|
keep_prelast = 0;
|
|
|
|
do
|
|
{
|
|
prelast = &tmp;
|
|
last = tmp.next;
|
|
PolyEdge *last_exchange = 0;
|
|
|
|
while( last != keep_prelast && last->next != 0 )
|
|
{
|
|
PolyEdge *te = last->next;
|
|
|
|
// swap edges
|
|
if( last->x > te->x )
|
|
{
|
|
prelast->next = te;
|
|
last->next = te->next;
|
|
te->next = last;
|
|
prelast = te;
|
|
last_exchange = prelast;
|
|
}
|
|
else
|
|
{
|
|
prelast = last;
|
|
last = te;
|
|
}
|
|
}
|
|
if (last_exchange == NULL)
|
|
break;
|
|
keep_prelast = last_exchange;
|
|
} while( keep_prelast != tmp.next && keep_prelast != &tmp );
|
|
}
|
|
}
|
|
|
|
|
|
/* draws simple or filled circle */
|
|
static void
|
|
Circle( Mat& img, Point center, int radius, const void* color, int fill )
|
|
{
|
|
Size size = img.size();
|
|
size_t step = img.step;
|
|
int pix_size = (int)img.elemSize();
|
|
uchar* ptr = img.ptr();
|
|
int64_t err = 0, dx = radius, dy = 0, plus = 1, minus = (radius << 1) - 1;
|
|
int inside = center.x >= radius && center.x < size.width - radius &&
|
|
center.y >= radius && center.y < size.height - radius;
|
|
|
|
#define ICV_PUT_POINT( ptr, x ) \
|
|
memcpy( ptr + (x)*pix_size, color, pix_size );
|
|
|
|
while( dx >= dy )
|
|
{
|
|
int mask;
|
|
int64_t y11 = center.y - dy, y12 = center.y + dy, y21 = center.y - dx, y22 = center.y + dx;
|
|
int64_t x11 = center.x - dx, x12 = center.x + dx, x21 = center.x - dy, x22 = center.x + dy;
|
|
|
|
if( inside )
|
|
{
|
|
uchar *tptr0 = ptr + y11 * step;
|
|
uchar *tptr1 = ptr + y12 * step;
|
|
|
|
if( !fill )
|
|
{
|
|
ICV_PUT_POINT( tptr0, x11 );
|
|
ICV_PUT_POINT( tptr1, x11 );
|
|
ICV_PUT_POINT( tptr0, x12 );
|
|
ICV_PUT_POINT( tptr1, x12 );
|
|
}
|
|
else
|
|
{
|
|
ICV_HLINE( tptr0, x11, x12, color, pix_size );
|
|
ICV_HLINE( tptr1, x11, x12, color, pix_size );
|
|
}
|
|
|
|
tptr0 = ptr + y21 * step;
|
|
tptr1 = ptr + y22 * step;
|
|
|
|
if( !fill )
|
|
{
|
|
ICV_PUT_POINT( tptr0, x21 );
|
|
ICV_PUT_POINT( tptr1, x21 );
|
|
ICV_PUT_POINT( tptr0, x22 );
|
|
ICV_PUT_POINT( tptr1, x22 );
|
|
}
|
|
else
|
|
{
|
|
ICV_HLINE( tptr0, x21, x22, color, pix_size );
|
|
ICV_HLINE( tptr1, x21, x22, color, pix_size );
|
|
}
|
|
}
|
|
else if( x11 < size.width && x12 >= 0 && y21 < size.height && y22 >= 0)
|
|
{
|
|
if( fill )
|
|
{
|
|
x11 = std::max( x11, (int64_t)0 );
|
|
x12 = MIN( x12, size.width - 1 );
|
|
}
|
|
|
|
if( y11 >= 0 && y11 < size.height )
|
|
{
|
|
uchar *tptr = ptr + y11 * step;
|
|
|
|
if( !fill )
|
|
{
|
|
if( x11 >= 0 )
|
|
ICV_PUT_POINT( tptr, x11 );
|
|
if( x12 < size.width )
|
|
ICV_PUT_POINT( tptr, x12 );
|
|
}
|
|
else
|
|
ICV_HLINE( tptr, x11, x12, color, pix_size );
|
|
}
|
|
|
|
if( y12 >= 0 && y12 < size.height )
|
|
{
|
|
uchar *tptr = ptr + y12 * step;
|
|
|
|
if( !fill )
|
|
{
|
|
if( x11 >= 0 )
|
|
ICV_PUT_POINT( tptr, x11 );
|
|
if( x12 < size.width )
|
|
ICV_PUT_POINT( tptr, x12 );
|
|
}
|
|
else
|
|
ICV_HLINE( tptr, x11, x12, color, pix_size );
|
|
}
|
|
|
|
if( x21 < size.width && x22 >= 0 )
|
|
{
|
|
if( fill )
|
|
{
|
|
x21 = std::max( x21, (int64_t)0 );
|
|
x22 = MIN( x22, size.width - 1 );
|
|
}
|
|
|
|
if( y21 >= 0 && y21 < size.height )
|
|
{
|
|
uchar *tptr = ptr + y21 * step;
|
|
|
|
if( !fill )
|
|
{
|
|
if( x21 >= 0 )
|
|
ICV_PUT_POINT( tptr, x21 );
|
|
if( x22 < size.width )
|
|
ICV_PUT_POINT( tptr, x22 );
|
|
}
|
|
else
|
|
ICV_HLINE( tptr, x21, x22, color, pix_size );
|
|
}
|
|
|
|
if( y22 >= 0 && y22 < size.height )
|
|
{
|
|
uchar *tptr = ptr + y22 * step;
|
|
|
|
if( !fill )
|
|
{
|
|
if( x21 >= 0 )
|
|
ICV_PUT_POINT( tptr, x21 );
|
|
if( x22 < size.width )
|
|
ICV_PUT_POINT( tptr, x22 );
|
|
}
|
|
else
|
|
ICV_HLINE( tptr, x21, x22, color, pix_size );
|
|
}
|
|
}
|
|
}
|
|
dy++;
|
|
err += plus;
|
|
plus += 2;
|
|
|
|
mask = (err <= 0) - 1;
|
|
|
|
err -= minus & mask;
|
|
dx += mask;
|
|
minus -= mask & 2;
|
|
}
|
|
|
|
#undef ICV_PUT_POINT
|
|
}
|
|
|
|
|
|
static void
|
|
ThickLine( Mat& img, Point2l p0, Point2l p1, const void* color,
|
|
int thickness, int line_type, int flags, int shift )
|
|
{
|
|
static const double INV_XY_ONE = 1./XY_ONE;
|
|
|
|
p0.x <<= XY_SHIFT - shift;
|
|
p0.y <<= XY_SHIFT - shift;
|
|
p1.x <<= XY_SHIFT - shift;
|
|
p1.y <<= XY_SHIFT - shift;
|
|
|
|
if( thickness <= 1 )
|
|
{
|
|
if( line_type < cv::LINE_AA )
|
|
{
|
|
if( line_type == 1 || line_type == 4 || shift == 0 )
|
|
{
|
|
p0.x = (p0.x + (XY_ONE>>1)) >> XY_SHIFT;
|
|
p0.y = (p0.y + (XY_ONE>>1)) >> XY_SHIFT;
|
|
p1.x = (p1.x + (XY_ONE>>1)) >> XY_SHIFT;
|
|
p1.y = (p1.y + (XY_ONE>>1)) >> XY_SHIFT;
|
|
Line( img, p0, p1, color, line_type );
|
|
}
|
|
else
|
|
Line2( img, p0, p1, color );
|
|
}
|
|
else
|
|
LineAA( img, p0, p1, color );
|
|
}
|
|
else
|
|
{
|
|
Point2l pt[4], dp = Point2l(0,0);
|
|
double dx = (p0.x - p1.x)*INV_XY_ONE, dy = (p1.y - p0.y)*INV_XY_ONE;
|
|
double r = dx * dx + dy * dy;
|
|
int i, oddThickness = thickness & 1;
|
|
thickness <<= XY_SHIFT - 1;
|
|
|
|
if( fabs(r) > DBL_EPSILON )
|
|
{
|
|
r = (thickness + oddThickness*XY_ONE*0.5)/std::sqrt(r);
|
|
dp.x = cvRound( dy * r );
|
|
dp.y = cvRound( dx * r );
|
|
|
|
pt[0].x = p0.x + dp.x;
|
|
pt[0].y = p0.y + dp.y;
|
|
pt[1].x = p0.x - dp.x;
|
|
pt[1].y = p0.y - dp.y;
|
|
pt[2].x = p1.x - dp.x;
|
|
pt[2].y = p1.y - dp.y;
|
|
pt[3].x = p1.x + dp.x;
|
|
pt[3].y = p1.y + dp.y;
|
|
|
|
FillConvexPoly( img, pt, 4, color, line_type, XY_SHIFT );
|
|
}
|
|
|
|
for( i = 0; i < 2; i++ )
|
|
{
|
|
if( flags & (i+1) )
|
|
{
|
|
if( line_type < cv::LINE_AA )
|
|
{
|
|
Point center;
|
|
center.x = (int)((p0.x + (XY_ONE>>1)) >> XY_SHIFT);
|
|
center.y = (int)((p0.y + (XY_ONE>>1)) >> XY_SHIFT);
|
|
Circle( img, center, (thickness + (XY_ONE>>1)) >> XY_SHIFT, color, 1 );
|
|
}
|
|
else
|
|
{
|
|
EllipseEx( img, p0, Size2l(thickness, thickness),
|
|
0, 0, 360, color, -1, line_type );
|
|
}
|
|
}
|
|
p0 = p1;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
PolyLine( Mat& img, const Point2l* v, int count, bool is_closed,
|
|
const void* color, int thickness,
|
|
int line_type, int shift )
|
|
{
|
|
if( !v || count <= 0 )
|
|
return;
|
|
|
|
int i = is_closed ? count - 1 : 0;
|
|
int flags = 2 + !is_closed;
|
|
Point2l p0;
|
|
CV_Assert( 0 <= shift && shift <= XY_SHIFT && thickness >= 0 );
|
|
|
|
p0 = v[i];
|
|
for( i = !is_closed; i < count; i++ )
|
|
{
|
|
Point2l p = v[i];
|
|
ThickLine( img, p0, p, color, thickness, line_type, flags, shift );
|
|
p0 = p;
|
|
flags = 2;
|
|
}
|
|
}
|
|
|
|
/* ----------------------------------------------------------------------------------------- */
|
|
/* ADDING A SET OF PREDEFINED MARKERS WHICH COULD BE USED TO HIGHLIGHT POSITIONS IN AN IMAGE */
|
|
/* ----------------------------------------------------------------------------------------- */
|
|
|
|
void drawMarker(InputOutputArray img, Point position, const Scalar& color, int markerType, int markerSize, int thickness, int line_type)
|
|
{
|
|
switch(markerType)
|
|
{
|
|
// The cross marker case
|
|
case MARKER_CROSS:
|
|
line(img, Point(position.x-(markerSize/2), position.y), Point(position.x+(markerSize/2), position.y), color, thickness, line_type);
|
|
line(img, Point(position.x, position.y-(markerSize/2)), Point(position.x, position.y+(markerSize/2)), color, thickness, line_type);
|
|
break;
|
|
|
|
// The tilted cross marker case
|
|
case MARKER_TILTED_CROSS:
|
|
line(img, Point(position.x-(markerSize/2), position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x+(markerSize/2), position.y-(markerSize/2)), Point(position.x-(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
|
|
break;
|
|
|
|
// The star marker case
|
|
case MARKER_STAR:
|
|
line(img, Point(position.x-(markerSize/2), position.y), Point(position.x+(markerSize/2), position.y), color, thickness, line_type);
|
|
line(img, Point(position.x, position.y-(markerSize/2)), Point(position.x, position.y+(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x-(markerSize/2), position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x+(markerSize/2), position.y-(markerSize/2)), Point(position.x-(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
|
|
break;
|
|
|
|
// The diamond marker case
|
|
case MARKER_DIAMOND:
|
|
line(img, Point(position.x, position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y), color, thickness, line_type);
|
|
line(img, Point(position.x+(markerSize/2), position.y), Point(position.x, position.y+(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x, position.y+(markerSize/2)), Point(position.x-(markerSize/2), position.y), color, thickness, line_type);
|
|
line(img, Point(position.x-(markerSize/2), position.y), Point(position.x, position.y-(markerSize/2)), color, thickness, line_type);
|
|
break;
|
|
|
|
// The square marker case
|
|
case MARKER_SQUARE:
|
|
line(img, Point(position.x-(markerSize/2), position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y-(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x+(markerSize/2), position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x+(markerSize/2), position.y+(markerSize/2)), Point(position.x-(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x-(markerSize/2), position.y+(markerSize/2)), Point(position.x-(markerSize/2), position.y-(markerSize/2)), color, thickness, line_type);
|
|
break;
|
|
|
|
// The triangle up marker case
|
|
case MARKER_TRIANGLE_UP:
|
|
line(img, Point(position.x-(markerSize/2), position.y+(markerSize/2)), Point(position.x+(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x+(markerSize/2), position.y+(markerSize/2)), Point(position.x, position.y-(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x, position.y-(markerSize/2)), Point(position.x-(markerSize/2), position.y+(markerSize/2)), color, thickness, line_type);
|
|
break;
|
|
|
|
// The triangle down marker case
|
|
case MARKER_TRIANGLE_DOWN:
|
|
line(img, Point(position.x-(markerSize/2), position.y-(markerSize/2)), Point(position.x+(markerSize/2), position.y-(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x+(markerSize/2), position.y-(markerSize/2)), Point(position.x, position.y+(markerSize/2)), color, thickness, line_type);
|
|
line(img, Point(position.x, position.y+(markerSize/2)), Point(position.x-(markerSize/2), position.y-(markerSize/2)), color, thickness, line_type);
|
|
break;
|
|
|
|
// If any number that doesn't exist is entered as marker type, draw a cross marker, to avoid crashes
|
|
default:
|
|
drawMarker(img, position, color, MARKER_CROSS, markerSize, thickness, line_type);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************************\
|
|
* External functions *
|
|
\****************************************************************************************/
|
|
|
|
void line( InputOutputArray _img, Point pt1, Point pt2, const Scalar& color,
|
|
int thickness, int line_type, int shift )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Mat img = _img.getMat();
|
|
|
|
if( line_type == cv::LINE_AA && img.depth() != CV_8U )
|
|
line_type = 8;
|
|
|
|
CV_Assert( 0 < thickness && thickness <= MAX_THICKNESS );
|
|
CV_Assert( 0 <= shift && shift <= XY_SHIFT );
|
|
|
|
double buf[4];
|
|
scalarToRawData( color, buf, img.type(), 0 );
|
|
ThickLine( img, pt1, pt2, buf, thickness, line_type, 3, shift );
|
|
}
|
|
|
|
void arrowedLine(InputOutputArray img, Point pt1, Point pt2, const Scalar& color,
|
|
int thickness, int line_type, int shift, double tipLength)
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
const double tipSize = norm(pt1-pt2)*tipLength; // Factor to normalize the size of the tip depending on the length of the arrow
|
|
|
|
line(img, pt1, pt2, color, thickness, line_type, shift);
|
|
|
|
const double angle = atan2( (double) pt1.y - pt2.y, (double) pt1.x - pt2.x );
|
|
|
|
Point p(cvRound(pt2.x + tipSize * cos(angle + CV_PI / 4)),
|
|
cvRound(pt2.y + tipSize * sin(angle + CV_PI / 4)));
|
|
line(img, p, pt2, color, thickness, line_type, shift);
|
|
|
|
p.x = cvRound(pt2.x + tipSize * cos(angle - CV_PI / 4));
|
|
p.y = cvRound(pt2.y + tipSize * sin(angle - CV_PI / 4));
|
|
line(img, p, pt2, color, thickness, line_type, shift);
|
|
}
|
|
|
|
void rectangle( InputOutputArray _img, Point pt1, Point pt2,
|
|
const Scalar& color, int thickness,
|
|
int lineType, int shift )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Mat img = _img.getMat();
|
|
|
|
if( lineType == cv::LINE_AA && img.depth() != CV_8U )
|
|
lineType = 8;
|
|
|
|
CV_Assert( thickness <= MAX_THICKNESS );
|
|
CV_Assert( 0 <= shift && shift <= XY_SHIFT );
|
|
|
|
double buf[4];
|
|
scalarToRawData(color, buf, img.type(), 0);
|
|
|
|
Point2l pt[4];
|
|
|
|
pt[0] = pt1;
|
|
pt[1].x = pt2.x;
|
|
pt[1].y = pt1.y;
|
|
pt[2] = pt2;
|
|
pt[3].x = pt1.x;
|
|
pt[3].y = pt2.y;
|
|
|
|
if( thickness >= 0 )
|
|
PolyLine( img, pt, 4, true, buf, thickness, lineType, shift );
|
|
else
|
|
FillConvexPoly( img, pt, 4, buf, lineType, shift );
|
|
}
|
|
|
|
|
|
void rectangle( InputOutputArray img, Rect rec,
|
|
const Scalar& color, int thickness,
|
|
int lineType, int shift )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
CV_Assert( 0 <= shift && shift <= XY_SHIFT );
|
|
|
|
// Crop the rectangle to right around the mat.
|
|
rec &= Rect(-(1 << shift), -(1 << shift), ((img.cols() + 2) << shift),
|
|
((img.rows() + 2) << shift));
|
|
|
|
if( !rec.empty() )
|
|
rectangle( img, rec.tl(), rec.br() - Point(1<<shift,1<<shift),
|
|
color, thickness, lineType, shift );
|
|
}
|
|
|
|
|
|
void circle( InputOutputArray _img, Point center, int radius,
|
|
const Scalar& color, int thickness, int line_type, int shift )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Mat img = _img.getMat();
|
|
|
|
if( line_type == cv::LINE_AA && img.depth() != CV_8U )
|
|
line_type = 8;
|
|
|
|
CV_Assert( radius >= 0 && thickness <= MAX_THICKNESS &&
|
|
0 <= shift && shift <= XY_SHIFT );
|
|
|
|
double buf[4];
|
|
scalarToRawData(color, buf, img.type(), 0);
|
|
|
|
if( thickness > 1 || line_type != LINE_8 || shift > 0 )
|
|
{
|
|
Point2l _center(center);
|
|
int64 _radius(radius);
|
|
_center.x <<= XY_SHIFT - shift;
|
|
_center.y <<= XY_SHIFT - shift;
|
|
_radius <<= XY_SHIFT - shift;
|
|
EllipseEx( img, _center, Size2l(_radius, _radius),
|
|
0, 0, 360, buf, thickness, line_type );
|
|
}
|
|
else
|
|
Circle( img, center, radius, buf, thickness < 0 );
|
|
}
|
|
|
|
|
|
void ellipse( InputOutputArray _img, Point center, Size axes,
|
|
double angle, double start_angle, double end_angle,
|
|
const Scalar& color, int thickness, int line_type, int shift )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Mat img = _img.getMat();
|
|
|
|
if( line_type == cv::LINE_AA && img.depth() != CV_8U )
|
|
line_type = 8;
|
|
|
|
CV_Assert( axes.width >= 0 && axes.height >= 0 &&
|
|
thickness <= MAX_THICKNESS && 0 <= shift && shift <= XY_SHIFT );
|
|
|
|
double buf[4];
|
|
scalarToRawData(color, buf, img.type(), 0);
|
|
|
|
int _angle = cvRound(angle);
|
|
int _start_angle = cvRound(start_angle);
|
|
int _end_angle = cvRound(end_angle);
|
|
Point2l _center(center);
|
|
Size2l _axes(axes);
|
|
_center.x <<= XY_SHIFT - shift;
|
|
_center.y <<= XY_SHIFT - shift;
|
|
_axes.width <<= XY_SHIFT - shift;
|
|
_axes.height <<= XY_SHIFT - shift;
|
|
|
|
EllipseEx( img, _center, _axes, _angle, _start_angle,
|
|
_end_angle, buf, thickness, line_type );
|
|
}
|
|
|
|
void ellipse(InputOutputArray _img, const RotatedRect& box, const Scalar& color,
|
|
int thickness, int lineType)
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Mat img = _img.getMat();
|
|
|
|
if( lineType == cv::LINE_AA && img.depth() != CV_8U )
|
|
lineType = 8;
|
|
|
|
CV_Assert( box.size.width >= 0 && box.size.height >= 0 &&
|
|
thickness <= MAX_THICKNESS );
|
|
|
|
double buf[4];
|
|
scalarToRawData(color, buf, img.type(), 0);
|
|
|
|
int _angle = cvRound(box.angle);
|
|
Point2l center(cvRound(box.center.x),
|
|
cvRound(box.center.y));
|
|
center.x = (center.x << XY_SHIFT) + cvRound((box.center.x - center.x)*XY_ONE);
|
|
center.y = (center.y << XY_SHIFT) + cvRound((box.center.y - center.y)*XY_ONE);
|
|
Size2l axes(cvRound(box.size.width),
|
|
cvRound(box.size.height));
|
|
axes.width = (axes.width << (XY_SHIFT - 1)) + cvRound((box.size.width - axes.width)*(XY_ONE>>1));
|
|
axes.height = (axes.height << (XY_SHIFT - 1)) + cvRound((box.size.height - axes.height)*(XY_ONE>>1));
|
|
EllipseEx( img, center, axes, _angle, 0, 360, buf, thickness, lineType );
|
|
}
|
|
|
|
void fillConvexPoly( InputOutputArray _img, const Point* pts, int npts,
|
|
const Scalar& color, int line_type, int shift )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Mat img = _img.getMat();
|
|
|
|
if( !pts || npts <= 0 )
|
|
return;
|
|
|
|
if( line_type == cv::LINE_AA && img.depth() != CV_8U )
|
|
line_type = 8;
|
|
|
|
double buf[4];
|
|
CV_Assert( 0 <= shift && shift <= XY_SHIFT );
|
|
scalarToRawData(color, buf, img.type(), 0);
|
|
std::vector<Point2l> _pts(pts, pts + npts);
|
|
FillConvexPoly( img, _pts.data(), npts, buf, line_type, shift );
|
|
}
|
|
|
|
void fillPoly( InputOutputArray _img, const Point** pts, const int* npts, int ncontours,
|
|
const Scalar& color, int line_type,
|
|
int shift, Point offset )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Mat img = _img.getMat();
|
|
|
|
if( line_type == cv::LINE_AA && img.depth() != CV_8U )
|
|
line_type = 8;
|
|
|
|
CV_Assert( pts && npts && ncontours >= 0 && 0 <= shift && shift <= XY_SHIFT );
|
|
|
|
double buf[4];
|
|
scalarToRawData(color, buf, img.type(), 0);
|
|
|
|
std::vector<PolyEdge> edges;
|
|
|
|
int i, total = 0;
|
|
for( i = 0; i < ncontours; i++ )
|
|
total += npts[i];
|
|
|
|
edges.reserve( total + 1 );
|
|
for (i = 0; i < ncontours; i++)
|
|
{
|
|
std::vector<Point2l> _pts(pts[i], pts[i] + npts[i]);
|
|
CollectPolyEdges(img, _pts.data(), npts[i], edges, buf, line_type, shift, offset);
|
|
}
|
|
|
|
FillEdgeCollection(img, edges, buf, line_type);
|
|
}
|
|
|
|
void polylines( InputOutputArray _img, const Point* const* pts, const int* npts, int ncontours, bool isClosed,
|
|
const Scalar& color, int thickness, int line_type, int shift )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Mat img = _img.getMat();
|
|
|
|
if( line_type == cv::LINE_AA && img.depth() != CV_8U )
|
|
line_type = 8;
|
|
|
|
CV_Assert( pts && npts && ncontours >= 0 &&
|
|
0 <= thickness && thickness <= MAX_THICKNESS &&
|
|
0 <= shift && shift <= XY_SHIFT );
|
|
|
|
double buf[4];
|
|
scalarToRawData( color, buf, img.type(), 0 );
|
|
|
|
for( int i = 0; i < ncontours; i++ )
|
|
{
|
|
std::vector<Point2l> _pts(pts[i], pts[i]+npts[i]);
|
|
PolyLine( img, _pts.data(), npts[i], isClosed, buf, thickness, line_type, shift );
|
|
}
|
|
}
|
|
|
|
|
|
enum { FONT_SIZE_SHIFT=8, FONT_ITALIC_ALPHA=(1 << 8),
|
|
FONT_ITALIC_DIGIT=(2 << 8), FONT_ITALIC_PUNCT=(4 << 8),
|
|
FONT_ITALIC_BRACES=(8 << 8), FONT_HAVE_GREEK=(16 << 8),
|
|
FONT_HAVE_CYRILLIC=(32 << 8) };
|
|
|
|
static const int HersheyPlain[] = {
|
|
(5 + 4*16) + FONT_HAVE_GREEK,
|
|
199, 214, 217, 233, 219, 197, 234, 216, 221, 222, 228, 225, 211, 224, 210, 220,
|
|
200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 212, 213, 191, 226, 192,
|
|
215, 190, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
|
14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 193, 84,
|
|
194, 85, 86, 87, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111,
|
|
112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126,
|
|
195, 223, 196, 88 };
|
|
|
|
static const int HersheyPlainItalic[] = {
|
|
(5 + 4*16) + FONT_ITALIC_ALPHA + FONT_HAVE_GREEK,
|
|
199, 214, 217, 233, 219, 197, 234, 216, 221, 222, 228, 225, 211, 224, 210, 220,
|
|
200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 212, 213, 191, 226, 192,
|
|
215, 190, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
|
64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 193, 84,
|
|
194, 85, 86, 87, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161,
|
|
162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176,
|
|
195, 223, 196, 88 };
|
|
|
|
static const int HersheyComplexSmall[] = {
|
|
(6 + 7*16) + FONT_HAVE_GREEK,
|
|
1199, 1214, 1217, 1275, 1274, 1271, 1272, 1216, 1221, 1222, 1219, 1232, 1211, 1231, 1210, 1220,
|
|
1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1212, 2213, 1241, 1238, 1242,
|
|
1215, 1273, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013,
|
|
1014, 1015, 1016, 1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1223, 1084,
|
|
1224, 1247, 586, 1249, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111,
|
|
1112, 1113, 1114, 1115, 1116, 1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126,
|
|
1225, 1229, 1226, 1246 };
|
|
|
|
static const int HersheyComplexSmallItalic[] = {
|
|
(6 + 7*16) + FONT_ITALIC_ALPHA + FONT_HAVE_GREEK,
|
|
1199, 1214, 1217, 1275, 1274, 1271, 1272, 1216, 1221, 1222, 1219, 1232, 1211, 1231, 1210, 1220,
|
|
1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1212, 1213, 1241, 1238, 1242,
|
|
1215, 1273, 1051, 1052, 1053, 1054, 1055, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063,
|
|
1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076, 1223, 1084,
|
|
1224, 1247, 586, 1249, 1151, 1152, 1153, 1154, 1155, 1156, 1157, 1158, 1159, 1160, 1161,
|
|
1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176,
|
|
1225, 1229, 1226, 1246 };
|
|
|
|
static const int HersheySimplex[] = {
|
|
(9 + 12*16) + FONT_HAVE_GREEK,
|
|
2199, 714, 717, 733, 719, 697, 734, 716, 721, 722, 728, 725, 711, 724, 710, 720,
|
|
700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 712, 713, 691, 726, 692,
|
|
715, 690, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 511, 512, 513,
|
|
514, 515, 516, 517, 518, 519, 520, 521, 522, 523, 524, 525, 526, 693, 584,
|
|
694, 2247, 586, 2249, 601, 602, 603, 604, 605, 606, 607, 608, 609, 610, 611,
|
|
612, 613, 614, 615, 616, 617, 618, 619, 620, 621, 622, 623, 624, 625, 626,
|
|
695, 723, 696, 2246 };
|
|
|
|
static const int HersheyDuplex[] = {
|
|
(9 + 12*16) + FONT_HAVE_GREEK,
|
|
2199, 2714, 2728, 2732, 2719, 2733, 2718, 2727, 2721, 2722, 2723, 2725, 2711, 2724, 2710, 2720,
|
|
2700, 2701, 2702, 2703, 2704, 2705, 2706, 2707, 2708, 2709, 2712, 2713, 2730, 2726, 2731,
|
|
2715, 2734, 2501, 2502, 2503, 2504, 2505, 2506, 2507, 2508, 2509, 2510, 2511, 2512, 2513,
|
|
2514, 2515, 2516, 2517, 2518, 2519, 2520, 2521, 2522, 2523, 2524, 2525, 2526, 2223, 2084,
|
|
2224, 2247, 587, 2249, 2601, 2602, 2603, 2604, 2605, 2606, 2607, 2608, 2609, 2610, 2611,
|
|
2612, 2613, 2614, 2615, 2616, 2617, 2618, 2619, 2620, 2621, 2622, 2623, 2624, 2625, 2626,
|
|
2225, 2229, 2226, 2246 };
|
|
|
|
static const int HersheyComplex[] = {
|
|
(9 + 12*16) + FONT_HAVE_GREEK + FONT_HAVE_CYRILLIC,
|
|
2199, 2214, 2217, 2275, 2274, 2271, 2272, 2216, 2221, 2222, 2219, 2232, 2211, 2231, 2210, 2220,
|
|
2200, 2201, 2202, 2203, 2204, 2205, 2206, 2207, 2208, 2209, 2212, 2213, 2241, 2238, 2242,
|
|
2215, 2273, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013,
|
|
2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023, 2024, 2025, 2026, 2223, 2084,
|
|
2224, 2247, 587, 2249, 2101, 2102, 2103, 2104, 2105, 2106, 2107, 2108, 2109, 2110, 2111,
|
|
2112, 2113, 2114, 2115, 2116, 2117, 2118, 2119, 2120, 2121, 2122, 2123, 2124, 2125, 2126,
|
|
2225, 2229, 2226, 2246, 2801, 2802, 2803, 2804, 2805, 2806, 2807, 2808, 2809, 2810, 2811,
|
|
2812, 2813, 2814, 2815, 2816, 2817, 2818, 2819, 2820, 2821, 2822, 2823, 2824, 2825, 2826,
|
|
2827, 2828, 2829, 2830, 2831, 2832, 2901, 2902, 2903, 2904, 2905, 2906, 2907, 2908, 2909,
|
|
2910, 2911, 2912, 2913, 2914, 2915, 2916, 2917, 2918, 2919, 2920, 2921, 2922, 2923, 2924,
|
|
2925, 2926, 2927, 2928, 2929, 2930, 2931, 2932};
|
|
|
|
static const int HersheyComplexItalic[] = {
|
|
(9 + 12*16) + FONT_ITALIC_ALPHA + FONT_ITALIC_DIGIT + FONT_ITALIC_PUNCT +
|
|
FONT_HAVE_GREEK + FONT_HAVE_CYRILLIC,
|
|
2199, 2764, 2778, 2782, 2769, 2783, 2768, 2777, 2771, 2772, 2219, 2232, 2211, 2231, 2210, 2220,
|
|
2750, 2751, 2752, 2753, 2754, 2755, 2756, 2757, 2758, 2759, 2212, 2213, 2241, 2238, 2242,
|
|
2765, 2273, 2051, 2052, 2053, 2054, 2055, 2056, 2057, 2058, 2059, 2060, 2061, 2062, 2063,
|
|
2064, 2065, 2066, 2067, 2068, 2069, 2070, 2071, 2072, 2073, 2074, 2075, 2076, 2223, 2084,
|
|
2224, 2247, 587, 2249, 2151, 2152, 2153, 2154, 2155, 2156, 2157, 2158, 2159, 2160, 2161,
|
|
2162, 2163, 2164, 2165, 2166, 2167, 2168, 2169, 2170, 2171, 2172, 2173, 2174, 2175, 2176,
|
|
2225, 2229, 2226, 2246 };
|
|
|
|
static const int HersheyTriplex[] = {
|
|
(9 + 12*16) + FONT_HAVE_GREEK,
|
|
2199, 3214, 3228, 3232, 3219, 3233, 3218, 3227, 3221, 3222, 3223, 3225, 3211, 3224, 3210, 3220,
|
|
3200, 3201, 3202, 3203, 3204, 3205, 3206, 3207, 3208, 3209, 3212, 3213, 3230, 3226, 3231,
|
|
3215, 3234, 3001, 3002, 3003, 3004, 3005, 3006, 3007, 3008, 3009, 3010, 3011, 3012, 3013,
|
|
2014, 3015, 3016, 3017, 3018, 3019, 3020, 3021, 3022, 3023, 3024, 3025, 3026, 2223, 2084,
|
|
2224, 2247, 587, 2249, 3101, 3102, 3103, 3104, 3105, 3106, 3107, 3108, 3109, 3110, 3111,
|
|
3112, 3113, 3114, 3115, 3116, 3117, 3118, 3119, 3120, 3121, 3122, 3123, 3124, 3125, 3126,
|
|
2225, 2229, 2226, 2246 };
|
|
|
|
static const int HersheyTriplexItalic[] = {
|
|
(9 + 12*16) + FONT_ITALIC_ALPHA + FONT_ITALIC_DIGIT +
|
|
FONT_ITALIC_PUNCT + FONT_HAVE_GREEK,
|
|
2199, 3264, 3278, 3282, 3269, 3233, 3268, 3277, 3271, 3272, 3223, 3225, 3261, 3224, 3260, 3270,
|
|
3250, 3251, 3252, 3253, 3254, 3255, 3256, 3257, 3258, 3259, 3262, 3263, 3230, 3226, 3231,
|
|
3265, 3234, 3051, 3052, 3053, 3054, 3055, 3056, 3057, 3058, 3059, 3060, 3061, 3062, 3063,
|
|
2064, 3065, 3066, 3067, 3068, 3069, 3070, 3071, 3072, 3073, 3074, 3075, 3076, 2223, 2084,
|
|
2224, 2247, 587, 2249, 3151, 3152, 3153, 3154, 3155, 3156, 3157, 3158, 3159, 3160, 3161,
|
|
3162, 3163, 3164, 3165, 3166, 3167, 3168, 3169, 3170, 3171, 3172, 3173, 3174, 3175, 3176,
|
|
2225, 2229, 2226, 2246 };
|
|
|
|
static const int HersheyScriptSimplex[] = {
|
|
(9 + 12*16) + FONT_ITALIC_ALPHA + FONT_HAVE_GREEK,
|
|
2199, 714, 717, 733, 719, 697, 734, 716, 721, 722, 728, 725, 711, 724, 710, 720,
|
|
700, 701, 702, 703, 704, 705, 706, 707, 708, 709, 712, 713, 691, 726, 692,
|
|
715, 690, 551, 552, 553, 554, 555, 556, 557, 558, 559, 560, 561, 562, 563,
|
|
564, 565, 566, 567, 568, 569, 570, 571, 572, 573, 574, 575, 576, 693, 584,
|
|
694, 2247, 586, 2249, 651, 652, 653, 654, 655, 656, 657, 658, 659, 660, 661,
|
|
662, 663, 664, 665, 666, 667, 668, 669, 670, 671, 672, 673, 674, 675, 676,
|
|
695, 723, 696, 2246 };
|
|
|
|
static const int HersheyScriptComplex[] = {
|
|
(9 + 12*16) + FONT_ITALIC_ALPHA + FONT_ITALIC_DIGIT + FONT_ITALIC_PUNCT + FONT_HAVE_GREEK,
|
|
2199, 2764, 2778, 2782, 2769, 2783, 2768, 2777, 2771, 2772, 2219, 2232, 2211, 2231, 2210, 2220,
|
|
2750, 2751, 2752, 2753, 2754, 2755, 2756, 2757, 2758, 2759, 2212, 2213, 2241, 2238, 2242,
|
|
2215, 2273, 2551, 2552, 2553, 2554, 2555, 2556, 2557, 2558, 2559, 2560, 2561, 2562, 2563,
|
|
2564, 2565, 2566, 2567, 2568, 2569, 2570, 2571, 2572, 2573, 2574, 2575, 2576, 2223, 2084,
|
|
2224, 2247, 586, 2249, 2651, 2652, 2653, 2654, 2655, 2656, 2657, 2658, 2659, 2660, 2661,
|
|
2662, 2663, 2664, 2665, 2666, 2667, 2668, 2669, 2670, 2671, 2672, 2673, 2674, 2675, 2676,
|
|
2225, 2229, 2226, 2246 };
|
|
|
|
|
|
static const int* getFontData(int fontFace)
|
|
{
|
|
bool isItalic = (fontFace & FONT_ITALIC) != 0;
|
|
const int* ascii = 0;
|
|
|
|
switch( fontFace & 15 )
|
|
{
|
|
case FONT_HERSHEY_SIMPLEX:
|
|
ascii = HersheySimplex;
|
|
break;
|
|
case FONT_HERSHEY_PLAIN:
|
|
ascii = !isItalic ? HersheyPlain : HersheyPlainItalic;
|
|
break;
|
|
case FONT_HERSHEY_DUPLEX:
|
|
ascii = HersheyDuplex;
|
|
break;
|
|
case FONT_HERSHEY_COMPLEX:
|
|
ascii = !isItalic ? HersheyComplex : HersheyComplexItalic;
|
|
break;
|
|
case FONT_HERSHEY_TRIPLEX:
|
|
ascii = !isItalic ? HersheyTriplex : HersheyTriplexItalic;
|
|
break;
|
|
case FONT_HERSHEY_COMPLEX_SMALL:
|
|
ascii = !isItalic ? HersheyComplexSmall : HersheyComplexSmallItalic;
|
|
break;
|
|
case FONT_HERSHEY_SCRIPT_SIMPLEX:
|
|
ascii = HersheyScriptSimplex;
|
|
break;
|
|
case FONT_HERSHEY_SCRIPT_COMPLEX:
|
|
ascii = HersheyScriptComplex;
|
|
break;
|
|
default:
|
|
CV_Error( CV_StsOutOfRange, "Unknown font type" );
|
|
}
|
|
return ascii;
|
|
}
|
|
|
|
inline void readCheck(int &c, int &i, const String &text, int fontFace)
|
|
{
|
|
|
|
int leftBoundary = ' ', rightBoundary = 127;
|
|
|
|
if(c >= 0x80 && fontFace == FONT_HERSHEY_COMPLEX)
|
|
{
|
|
if(c == 0xD0 && (uchar)text[i + 1] >= 0x90 && (uchar)text[i + 1] <= 0xBF)
|
|
{
|
|
c = (uchar)text[++i] - 17;
|
|
leftBoundary = 127;
|
|
rightBoundary = 175;
|
|
}
|
|
else if(c == 0xD1 && (uchar)text[i + 1] >= 0x80 && (uchar)text[i + 1] <= 0x8F)
|
|
{
|
|
c = (uchar)text[++i] + 47;
|
|
leftBoundary = 175;
|
|
rightBoundary = 191;
|
|
}
|
|
else
|
|
{
|
|
if(c >= 0xC0 && text[i+1] != 0) //2 bytes utf
|
|
i++;
|
|
|
|
if(c >= 0xE0 && text[i+1] != 0) //3 bytes utf
|
|
i++;
|
|
|
|
if(c >= 0xF0 && text[i+1] != 0) //4 bytes utf
|
|
i++;
|
|
|
|
if(c >= 0xF8 && text[i+1] != 0) //5 bytes utf
|
|
i++;
|
|
|
|
if(c >= 0xFC && text[i+1] != 0) //6 bytes utf
|
|
i++;
|
|
|
|
c = '?';
|
|
}
|
|
}
|
|
|
|
if(c >= rightBoundary || c < leftBoundary)
|
|
c = '?';
|
|
}
|
|
|
|
extern const char* g_HersheyGlyphs[];
|
|
|
|
void putText( InputOutputArray _img, const String& text, Point org,
|
|
int fontFace, double fontScale, Scalar color,
|
|
int thickness, int line_type, bool bottomLeftOrigin )
|
|
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
if ( text.empty() )
|
|
{
|
|
return;
|
|
}
|
|
Mat img = _img.getMat();
|
|
const int* ascii = getFontData(fontFace);
|
|
|
|
double buf[4];
|
|
scalarToRawData(color, buf, img.type(), 0);
|
|
|
|
int base_line = -(ascii[0] & 15);
|
|
int hscale = cvRound(fontScale*XY_ONE), vscale = hscale;
|
|
|
|
if( line_type == CV_AA && img.depth() != CV_8U )
|
|
line_type = 8;
|
|
|
|
if( bottomLeftOrigin )
|
|
vscale = -vscale;
|
|
|
|
int64 view_x = (int64)org.x << XY_SHIFT;
|
|
int64 view_y = ((int64)org.y << XY_SHIFT) + base_line*vscale;
|
|
std::vector<Point2l> pts;
|
|
pts.reserve(1 << 10);
|
|
const char **faces = cv::g_HersheyGlyphs;
|
|
|
|
for( int i = 0; i < (int)text.size(); i++ )
|
|
{
|
|
int c = (uchar)text[i];
|
|
Point2l p;
|
|
|
|
readCheck(c, i, text, fontFace);
|
|
|
|
const char* ptr = faces[ascii[(c-' ')+1]];
|
|
p.x = (uchar)ptr[0] - 'R';
|
|
p.y = (uchar)ptr[1] - 'R';
|
|
int64 dx = p.y*hscale;
|
|
view_x -= p.x*hscale;
|
|
pts.resize(0);
|
|
|
|
for( ptr += 2;; )
|
|
{
|
|
if( *ptr == ' ' || !*ptr )
|
|
{
|
|
if( pts.size() > 1 )
|
|
PolyLine( img, &pts[0], (int)pts.size(), false, buf, thickness, line_type, XY_SHIFT );
|
|
if( !*ptr++ )
|
|
break;
|
|
pts.resize(0);
|
|
}
|
|
else
|
|
{
|
|
p.x = (uchar)ptr[0] - 'R';
|
|
p.y = (uchar)ptr[1] - 'R';
|
|
ptr += 2;
|
|
pts.push_back(Point2l(p.x*hscale + view_x, p.y*vscale + view_y));
|
|
}
|
|
}
|
|
view_x += dx;
|
|
}
|
|
}
|
|
|
|
Size getTextSize( const String& text, int fontFace, double fontScale, int thickness, int* _base_line)
|
|
{
|
|
Size size;
|
|
double view_x = 0;
|
|
const char **faces = cv::g_HersheyGlyphs;
|
|
const int* ascii = getFontData(fontFace);
|
|
|
|
int base_line = (ascii[0] & 15);
|
|
int cap_line = (ascii[0] >> 4) & 15;
|
|
size.height = cvRound((cap_line + base_line)*fontScale + (thickness+1)/2);
|
|
|
|
for( int i = 0; i < (int)text.size(); i++ )
|
|
{
|
|
int c = (uchar)text[i];
|
|
Point p;
|
|
|
|
readCheck(c, i, text, fontFace);
|
|
|
|
const char* ptr = faces[ascii[(c-' ')+1]];
|
|
p.x = (uchar)ptr[0] - 'R';
|
|
p.y = (uchar)ptr[1] - 'R';
|
|
view_x += (p.y - p.x)*fontScale;
|
|
}
|
|
|
|
size.width = cvRound(view_x + thickness);
|
|
if( _base_line )
|
|
*_base_line = cvRound(base_line*fontScale + thickness*0.5);
|
|
return size;
|
|
}
|
|
|
|
double getFontScaleFromHeight(const int fontFace, const int pixelHeight, const int thickness)
|
|
{
|
|
// By https://stackoverflow.com/a/27898487/1531708
|
|
const int* ascii = getFontData(fontFace);
|
|
|
|
int base_line = (ascii[0] & 15);
|
|
int cap_line = (ascii[0] >> 4) & 15;
|
|
|
|
return static_cast<double>(pixelHeight - static_cast<double>((thickness + 1)) / 2.0) / static_cast<double>(cap_line + base_line);
|
|
}
|
|
|
|
}
|
|
|
|
void cv::fillConvexPoly(InputOutputArray img, InputArray _points,
|
|
const Scalar& color, int lineType, int shift)
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Mat points = _points.getMat();
|
|
CV_Assert(points.checkVector(2, CV_32S) >= 0);
|
|
fillConvexPoly(img, points.ptr<Point>(), points.rows*points.cols*points.channels()/2, color, lineType, shift);
|
|
}
|
|
|
|
void cv::fillPoly(InputOutputArray img, InputArrayOfArrays pts,
|
|
const Scalar& color, int lineType, int shift, Point offset)
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
bool manyContours = pts.kind() == _InputArray::STD_VECTOR_VECTOR ||
|
|
pts.kind() == _InputArray::STD_VECTOR_MAT;
|
|
int i, ncontours = manyContours ? (int)pts.total() : 1;
|
|
if( ncontours == 0 )
|
|
return;
|
|
AutoBuffer<Point*> _ptsptr(ncontours);
|
|
AutoBuffer<int> _npts(ncontours);
|
|
Point** ptsptr = _ptsptr.data();
|
|
int* npts = _npts.data();
|
|
|
|
for( i = 0; i < ncontours; i++ )
|
|
{
|
|
Mat p = pts.getMat(manyContours ? i : -1);
|
|
CV_Assert(p.checkVector(2, CV_32S) >= 0);
|
|
ptsptr[i] = p.ptr<Point>();
|
|
npts[i] = p.rows*p.cols*p.channels()/2;
|
|
}
|
|
fillPoly(img, (const Point**)ptsptr, npts, (int)ncontours, color, lineType, shift, offset);
|
|
}
|
|
|
|
void cv::polylines(InputOutputArray img, InputArrayOfArrays pts,
|
|
bool isClosed, const Scalar& color,
|
|
int thickness, int lineType, int shift)
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
bool manyContours = pts.kind() == _InputArray::STD_VECTOR_VECTOR ||
|
|
pts.kind() == _InputArray::STD_VECTOR_MAT;
|
|
int i, ncontours = manyContours ? (int)pts.total() : 1;
|
|
if( ncontours == 0 )
|
|
return;
|
|
AutoBuffer<Point*> _ptsptr(ncontours);
|
|
AutoBuffer<int> _npts(ncontours);
|
|
Point** ptsptr = _ptsptr.data();
|
|
int* npts = _npts.data();
|
|
|
|
for( i = 0; i < ncontours; i++ )
|
|
{
|
|
Mat p = pts.getMat(manyContours ? i : -1);
|
|
if( p.total() == 0 )
|
|
{
|
|
ptsptr[i] = NULL;
|
|
npts[i] = 0;
|
|
continue;
|
|
}
|
|
CV_Assert(p.checkVector(2, CV_32S) >= 0);
|
|
ptsptr[i] = p.ptr<Point>();
|
|
npts[i] = p.rows*p.cols*p.channels()/2;
|
|
}
|
|
polylines(img, (const Point**)ptsptr, npts, (int)ncontours, isClosed, color, thickness, lineType, shift);
|
|
}
|
|
|
|
namespace
|
|
{
|
|
using namespace cv;
|
|
|
|
static void addChildContour(InputArrayOfArrays contours,
|
|
size_t ncontours,
|
|
const Vec4i* hierarchy,
|
|
int i, std::vector<CvSeq>& seq,
|
|
std::vector<CvSeqBlock>& block)
|
|
{
|
|
for( ; i >= 0; i = hierarchy[i][0] )
|
|
{
|
|
Mat ci = contours.getMat(i);
|
|
cvMakeSeqHeaderForArray(CV_SEQ_POLYGON, sizeof(CvSeq), sizeof(Point),
|
|
!ci.empty() ? (void*)ci.ptr() : 0, (int)ci.total(),
|
|
&seq[i], &block[i] );
|
|
|
|
int h_next = hierarchy[i][0], h_prev = hierarchy[i][1],
|
|
v_next = hierarchy[i][2], v_prev = hierarchy[i][3];
|
|
seq[i].h_next = (0 <= h_next && h_next < (int)ncontours) ? &seq[h_next] : 0;
|
|
seq[i].h_prev = (0 <= h_prev && h_prev < (int)ncontours) ? &seq[h_prev] : 0;
|
|
seq[i].v_next = (0 <= v_next && v_next < (int)ncontours) ? &seq[v_next] : 0;
|
|
seq[i].v_prev = (0 <= v_prev && v_prev < (int)ncontours) ? &seq[v_prev] : 0;
|
|
|
|
if( v_next >= 0 )
|
|
addChildContour(contours, ncontours, hierarchy, v_next, seq, block);
|
|
}
|
|
}
|
|
}
|
|
|
|
void cv::drawContours( InputOutputArray _image, InputArrayOfArrays _contours,
|
|
int contourIdx, const Scalar& color, int thickness,
|
|
int lineType, InputArray _hierarchy,
|
|
int maxLevel, Point offset )
|
|
{
|
|
CV_INSTRUMENT_REGION();
|
|
|
|
Mat image = _image.getMat(), hierarchy = _hierarchy.getMat();
|
|
CvMat _cimage = cvMat(image);
|
|
|
|
size_t ncontours = _contours.total();
|
|
size_t i = 0, first = 0, last = ncontours;
|
|
std::vector<CvSeq> seq;
|
|
std::vector<CvSeqBlock> block;
|
|
|
|
if( !last )
|
|
return;
|
|
|
|
seq.resize(last);
|
|
block.resize(last);
|
|
|
|
for( i = first; i < last; i++ )
|
|
seq[i].first = 0;
|
|
|
|
if( contourIdx >= 0 )
|
|
{
|
|
CV_Assert( 0 <= contourIdx && contourIdx < (int)last );
|
|
first = contourIdx;
|
|
last = contourIdx + 1;
|
|
}
|
|
|
|
for( i = first; i < last; i++ )
|
|
{
|
|
Mat ci = _contours.getMat((int)i);
|
|
if( ci.empty() )
|
|
continue;
|
|
int npoints = ci.checkVector(2, CV_32S);
|
|
CV_Assert( npoints > 0 );
|
|
cvMakeSeqHeaderForArray( CV_SEQ_POLYGON, sizeof(CvSeq), sizeof(Point),
|
|
ci.ptr(), npoints, &seq[i], &block[i] );
|
|
}
|
|
|
|
if( hierarchy.empty() || maxLevel == 0 )
|
|
for( i = first; i < last; i++ )
|
|
{
|
|
seq[i].h_next = i < last-1 ? &seq[i+1] : 0;
|
|
seq[i].h_prev = i > first ? &seq[i-1] : 0;
|
|
}
|
|
else
|
|
{
|
|
size_t count = last - first;
|
|
CV_Assert(hierarchy.total() == ncontours && hierarchy.type() == CV_32SC4 );
|
|
const Vec4i* h = hierarchy.ptr<Vec4i>();
|
|
|
|
if( count == ncontours )
|
|
{
|
|
for( i = first; i < last; i++ )
|
|
{
|
|
int h_next = h[i][0], h_prev = h[i][1],
|
|
v_next = h[i][2], v_prev = h[i][3];
|
|
seq[i].h_next = (size_t)h_next < count ? &seq[h_next] : 0;
|
|
seq[i].h_prev = (size_t)h_prev < count ? &seq[h_prev] : 0;
|
|
seq[i].v_next = (size_t)v_next < count ? &seq[v_next] : 0;
|
|
seq[i].v_prev = (size_t)v_prev < count ? &seq[v_prev] : 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int child = h[first][2];
|
|
if( child >= 0 )
|
|
{
|
|
addChildContour(_contours, ncontours, h, child, seq, block);
|
|
seq[first].v_next = &seq[child];
|
|
}
|
|
}
|
|
}
|
|
|
|
cvDrawContours( &_cimage, &seq[first], cvScalar(color), cvScalar(color), contourIdx >= 0 ?
|
|
-maxLevel : maxLevel, thickness, lineType, cvPoint(offset) );
|
|
}
|
|
|
|
|
|
|
|
static const int CodeDeltas[8][2] =
|
|
{ {1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1} };
|
|
|
|
#define CV_ADJUST_EDGE_COUNT( count, seq ) \
|
|
((count) -= ((count) == (seq)->total && !CV_IS_SEQ_CLOSED(seq)))
|
|
|
|
CV_IMPL void
|
|
cvDrawContours( void* _img, CvSeq* contour,
|
|
CvScalar _externalColor, CvScalar _holeColor,
|
|
int maxLevel, int thickness,
|
|
int line_type, CvPoint _offset )
|
|
{
|
|
CvSeq *contour0 = contour, *h_next = 0;
|
|
CvTreeNodeIterator iterator;
|
|
std::vector<cv::PolyEdge> edges;
|
|
std::vector<cv::Point2l> pts;
|
|
cv::Scalar externalColor = _externalColor, holeColor = _holeColor;
|
|
cv::Mat img = cv::cvarrToMat(_img);
|
|
cv::Point offset = _offset;
|
|
double ext_buf[4], hole_buf[4];
|
|
|
|
if( line_type == cv::LINE_AA && img.depth() != CV_8U )
|
|
line_type = 8;
|
|
|
|
if( !contour )
|
|
return;
|
|
|
|
CV_Assert( thickness <= MAX_THICKNESS );
|
|
|
|
scalarToRawData( externalColor, ext_buf, img.type(), 0 );
|
|
scalarToRawData( holeColor, hole_buf, img.type(), 0 );
|
|
|
|
maxLevel = MAX(maxLevel, INT_MIN+2);
|
|
maxLevel = MIN(maxLevel, INT_MAX-1);
|
|
|
|
if( maxLevel < 0 )
|
|
{
|
|
h_next = contour->h_next;
|
|
contour->h_next = 0;
|
|
maxLevel = -maxLevel+1;
|
|
}
|
|
|
|
cvInitTreeNodeIterator( &iterator, contour, maxLevel );
|
|
while( (contour = (CvSeq*)cvNextTreeNode( &iterator )) != 0 )
|
|
{
|
|
CvSeqReader reader;
|
|
int i, count = contour->total;
|
|
int elem_type = CV_MAT_TYPE(contour->flags);
|
|
void* clr = (contour->flags & CV_SEQ_FLAG_HOLE) == 0 ? ext_buf : hole_buf;
|
|
|
|
cvStartReadSeq( contour, &reader, 0 );
|
|
CV_Assert(reader.ptr != NULL);
|
|
if( thickness < 0 )
|
|
pts.resize(0);
|
|
|
|
if( CV_IS_SEQ_CHAIN_CONTOUR( contour ))
|
|
{
|
|
cv::Point pt = ((CvChain*)contour)->origin;
|
|
cv::Point prev_pt = pt;
|
|
char prev_code = reader.ptr ? reader.ptr[0] : '\0';
|
|
|
|
prev_pt += offset;
|
|
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
char code;
|
|
CV_READ_SEQ_ELEM( code, reader );
|
|
|
|
CV_Assert( (code & ~7) == 0 );
|
|
|
|
if( code != prev_code )
|
|
{
|
|
prev_code = code;
|
|
if( thickness >= 0 )
|
|
cv::ThickLine( img, prev_pt, pt, clr, thickness, line_type, 2, 0 );
|
|
else
|
|
pts.push_back(pt);
|
|
prev_pt = pt;
|
|
}
|
|
|
|
pt.x += CodeDeltas[(int)code][0];
|
|
pt.y += CodeDeltas[(int)code][1];
|
|
}
|
|
|
|
if( thickness >= 0 )
|
|
cv::ThickLine( img, prev_pt,
|
|
cv::Point(((CvChain*)contour)->origin) + offset,
|
|
clr, thickness, line_type, 2, 0 );
|
|
else
|
|
cv::CollectPolyEdges(img, &pts[0], (int)pts.size(),
|
|
edges, ext_buf, line_type, 0, offset);
|
|
}
|
|
else if( CV_IS_SEQ_POLYLINE( contour ))
|
|
{
|
|
CV_Assert( elem_type == CV_32SC2 );
|
|
cv::Point pt1, pt2;
|
|
int shift = 0;
|
|
|
|
count -= !CV_IS_SEQ_CLOSED(contour);
|
|
{ CvPoint pt_ = CV_STRUCT_INITIALIZER; CV_READ_SEQ_ELEM(pt_, reader); pt1 = pt_; }
|
|
pt1 += offset;
|
|
if( thickness < 0 )
|
|
pts.push_back(pt1);
|
|
|
|
for( i = 0; i < count; i++ )
|
|
{
|
|
{ CvPoint pt_ = CV_STRUCT_INITIALIZER; CV_READ_SEQ_ELEM(pt_, reader); pt2 = pt_; }
|
|
pt2 += offset;
|
|
if( thickness >= 0 )
|
|
cv::ThickLine( img, pt1, pt2, clr, thickness, line_type, 2, shift );
|
|
else
|
|
pts.push_back(pt2);
|
|
pt1 = pt2;
|
|
}
|
|
if( thickness < 0 )
|
|
cv::CollectPolyEdges( img, &pts[0], (int)pts.size(),
|
|
edges, ext_buf, line_type, 0, cv::Point() );
|
|
}
|
|
}
|
|
|
|
if( thickness < 0 )
|
|
cv::FillEdgeCollection( img, edges, ext_buf, line_type);
|
|
|
|
if( h_next && contour0 )
|
|
contour0->h_next = h_next;
|
|
}
|
|
|
|
CV_IMPL int
|
|
cvClipLine( CvSize size, CvPoint* pt1, CvPoint* pt2 )
|
|
{
|
|
CV_Assert( pt1 && pt2 );
|
|
return cv::clipLine( size, *(cv::Point*)pt1, *(cv::Point*)pt2 );
|
|
}
|
|
|
|
|
|
CV_IMPL int
|
|
cvEllipse2Poly( CvPoint center, CvSize axes, int angle,
|
|
int arc_start, int arc_end, CvPoint* _pts, int delta )
|
|
{
|
|
std::vector<cv::Point> pts;
|
|
cv::ellipse2Poly( Point(center), Size(axes), angle, arc_start, arc_end, delta, pts );
|
|
memcpy( _pts, &pts[0], pts.size()*sizeof(_pts[0]) );
|
|
return (int)pts.size();
|
|
}
|
|
|
|
CV_IMPL CvScalar
|
|
cvColorToScalar( double packed_color, int type )
|
|
{
|
|
cv::Scalar scalar;
|
|
|
|
if( CV_MAT_DEPTH( type ) == CV_8U )
|
|
{
|
|
int icolor = cvRound( packed_color );
|
|
if( CV_MAT_CN( type ) > 1 )
|
|
{
|
|
scalar.val[0] = icolor & 255;
|
|
scalar.val[1] = (icolor >> 8) & 255;
|
|
scalar.val[2] = (icolor >> 16) & 255;
|
|
scalar.val[3] = (icolor >> 24) & 255;
|
|
}
|
|
else
|
|
{
|
|
scalar.val[0] = cv::saturate_cast<uchar>( icolor );
|
|
scalar.val[1] = scalar.val[2] = scalar.val[3] = 0;
|
|
}
|
|
}
|
|
else if( CV_MAT_DEPTH( type ) == CV_8S )
|
|
{
|
|
int icolor = cvRound( packed_color );
|
|
if( CV_MAT_CN( type ) > 1 )
|
|
{
|
|
scalar.val[0] = (char)icolor;
|
|
scalar.val[1] = (char)(icolor >> 8);
|
|
scalar.val[2] = (char)(icolor >> 16);
|
|
scalar.val[3] = (char)(icolor >> 24);
|
|
}
|
|
else
|
|
{
|
|
scalar.val[0] = cv::saturate_cast<schar>( icolor );
|
|
scalar.val[1] = scalar.val[2] = scalar.val[3] = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
int cn = CV_MAT_CN( type );
|
|
switch( cn )
|
|
{
|
|
case 1:
|
|
scalar.val[0] = packed_color;
|
|
scalar.val[1] = scalar.val[2] = scalar.val[3] = 0;
|
|
break;
|
|
case 2:
|
|
scalar.val[0] = scalar.val[1] = packed_color;
|
|
scalar.val[2] = scalar.val[3] = 0;
|
|
break;
|
|
case 3:
|
|
scalar.val[0] = scalar.val[1] = scalar.val[2] = packed_color;
|
|
scalar.val[3] = 0;
|
|
break;
|
|
default:
|
|
scalar.val[0] = scalar.val[1] =
|
|
scalar.val[2] = scalar.val[3] = packed_color;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return cvScalar(scalar);
|
|
}
|
|
|
|
CV_IMPL int
|
|
cvInitLineIterator( const CvArr* img, CvPoint pt1, CvPoint pt2,
|
|
CvLineIterator* iterator, int connectivity,
|
|
int left_to_right )
|
|
{
|
|
CV_Assert( iterator != 0 );
|
|
cv::LineIterator li(cv::cvarrToMat(img), pt1, pt2, connectivity, left_to_right!=0);
|
|
|
|
iterator->err = li.err;
|
|
iterator->minus_delta = li.minusDelta;
|
|
iterator->plus_delta = li.plusDelta;
|
|
iterator->minus_step = li.minusStep;
|
|
iterator->plus_step = li.plusStep;
|
|
iterator->ptr = li.ptr;
|
|
|
|
return li.count;
|
|
}
|
|
|
|
CV_IMPL void
|
|
cvLine( CvArr* _img, CvPoint pt1, CvPoint pt2, CvScalar color,
|
|
int thickness, int line_type, int shift )
|
|
{
|
|
cv::Mat img = cv::cvarrToMat(_img);
|
|
cv::line( img, pt1, pt2, color, thickness, line_type, shift );
|
|
}
|
|
|
|
CV_IMPL void
|
|
cvRectangle( CvArr* _img, CvPoint pt1, CvPoint pt2,
|
|
CvScalar color, int thickness,
|
|
int line_type, int shift )
|
|
{
|
|
cv::Mat img = cv::cvarrToMat(_img);
|
|
cv::rectangle( img, pt1, pt2, color, thickness, line_type, shift );
|
|
}
|
|
|
|
CV_IMPL void
|
|
cvRectangleR( CvArr* _img, CvRect rec,
|
|
CvScalar color, int thickness,
|
|
int line_type, int shift )
|
|
{
|
|
cv::Mat img = cv::cvarrToMat(_img);
|
|
cv::rectangle( img, rec, color, thickness, line_type, shift );
|
|
}
|
|
|
|
CV_IMPL void
|
|
cvCircle( CvArr* _img, CvPoint center, int radius,
|
|
CvScalar color, int thickness, int line_type, int shift )
|
|
{
|
|
cv::Mat img = cv::cvarrToMat(_img);
|
|
cv::circle( img, center, radius, color, thickness, line_type, shift );
|
|
}
|
|
|
|
CV_IMPL void
|
|
cvEllipse( CvArr* _img, CvPoint center, CvSize axes,
|
|
double angle, double start_angle, double end_angle,
|
|
CvScalar color, int thickness, int line_type, int shift )
|
|
{
|
|
cv::Mat img = cv::cvarrToMat(_img);
|
|
cv::ellipse( img, center, axes, angle, start_angle, end_angle,
|
|
color, thickness, line_type, shift );
|
|
}
|
|
|
|
CV_IMPL void
|
|
cvFillConvexPoly( CvArr* _img, const CvPoint *pts, int npts,
|
|
CvScalar color, int line_type, int shift )
|
|
{
|
|
cv::Mat img = cv::cvarrToMat(_img);
|
|
cv::fillConvexPoly( img, (const cv::Point*)pts, npts,
|
|
color, line_type, shift );
|
|
}
|
|
|
|
CV_IMPL void
|
|
cvFillPoly( CvArr* _img, CvPoint **pts, const int *npts, int ncontours,
|
|
CvScalar color, int line_type, int shift )
|
|
{
|
|
cv::Mat img = cv::cvarrToMat(_img);
|
|
|
|
cv::fillPoly( img, (const cv::Point**)pts, npts, ncontours, color, line_type, shift );
|
|
}
|
|
|
|
CV_IMPL void
|
|
cvPolyLine( CvArr* _img, CvPoint **pts, const int *npts,
|
|
int ncontours, int closed, CvScalar color,
|
|
int thickness, int line_type, int shift )
|
|
{
|
|
cv::Mat img = cv::cvarrToMat(_img);
|
|
|
|
cv::polylines( img, (const cv::Point**)pts, npts, ncontours,
|
|
closed != 0, color, thickness, line_type, shift );
|
|
}
|
|
|
|
CV_IMPL void
|
|
cvPutText( CvArr* _img, const char *text, CvPoint org, const CvFont *_font, CvScalar color )
|
|
{
|
|
cv::Mat img = cv::cvarrToMat(_img);
|
|
CV_Assert( text != 0 && _font != 0);
|
|
cv::putText( img, text, org, _font->font_face, (_font->hscale+_font->vscale)*0.5,
|
|
color, _font->thickness, _font->line_type,
|
|
CV_IS_IMAGE(_img) && ((IplImage*)_img)->origin != 0 );
|
|
}
|
|
|
|
|
|
CV_IMPL void
|
|
cvInitFont( CvFont *font, int font_face, double hscale, double vscale,
|
|
double shear, int thickness, int line_type )
|
|
{
|
|
CV_Assert( font != 0 && hscale > 0 && vscale > 0 && thickness >= 0 );
|
|
|
|
font->ascii = cv::getFontData(font_face);
|
|
font->font_face = font_face;
|
|
font->hscale = (float)hscale;
|
|
font->vscale = (float)vscale;
|
|
font->thickness = thickness;
|
|
font->shear = (float)shear;
|
|
font->greek = font->cyrillic = 0;
|
|
font->line_type = line_type;
|
|
}
|
|
|
|
CV_IMPL void
|
|
cvGetTextSize( const char *text, const CvFont *_font, CvSize *_size, int *_base_line )
|
|
{
|
|
CV_Assert(text != 0 && _font != 0);
|
|
cv::Size size = cv::getTextSize( text, _font->font_face, (_font->hscale + _font->vscale)*0.5,
|
|
_font->thickness, _base_line );
|
|
if( _size )
|
|
*_size = cvSize(size);
|
|
}
|
|
|
|
/* End of file. */
|