我已经在这里实现了Xiaolin Wu圈算法:https://create.stephan-brumme.com/antialiased-circle/在c ++中:
float radiusX = endRadius;
float radiusY = endRadius;
float radiusX2 = radiusX * radiusX;
float radiusY2 = radiusY * radiusY;
float maxTransparency = 127;
float quarter = roundf(radiusX2 / sqrtf(radiusX2 + radiusY2));
for(float _x = 0; _x <= quarter; _x++) {
float _y = radiusY * sqrtf(1 - _x * _x / radiusX2);
float error = _y - floorf(_y);
float transparency = roundf(error * maxTransparency);
int alpha = transparency;
int alpha2 = maxTransparency - transparency;
setPixel4(x, y, _x, floorf(_y), r, g, b, alpha, data, areasData, false);
setPixel4(x, y, _x, floorf(_y) - 1, r, g, b, alpha2, data, areasData, false);
}
quarter = roundf(radiusY2 / sqrtf(radiusX2 + radiusY2));
for(float _y = 0; _y <= quarter; _y++) {
float _x = radiusX * sqrtf(1 - _y * _y / radiusY2);
float error = _x - floorf(_x);
float transparency = roundf(error * maxTransparency);
int alpha = transparency;
int alpha2 = maxTransparency - transparency;
setPixel4(x, y, floorf(_x), _y, r, g, b, alpha, data, areasData, false);
setPixel4(x, y, floorf(_x) - 1, _y, r, g, b, alpha2, data, areasData, false);
}
x,y是圆心的坐标。
我认为它看起来不错:
但是,我需要填写圆圈。也许我错了,但是我开发了一个简单的算法:从1迭代到半径,然后画一个圆。看起来像这样:
奇怪。因此,为了解决此问题,我还将透明度设置为最大值,直到到达最后一个半径(所以它是一个外圆):
如您所见,外层和其他层之间有奇怪的孔。我尝试制作两个外层和类似的东西,但是没有得到正确的结果。
这是代码的最终版本:
for(int cradius = startRadius; cradius <= endRadius; cradius++) {
bool last = cradius == endRadius;
float radiusX = cradius;
float radiusY = cradius;
float radiusX2 = radiusX * radiusX;
float radiusY2 = radiusY * radiusY;
float maxTransparency = 127;
float quarter = roundf(radiusX2 / sqrtf(radiusX2 + radiusY2));
for(float _x = 0; _x <= quarter; _x++) {
float _y = radiusY * sqrtf(1 - _x * _x / radiusX2);
float error = _y - floorf(_y);
float transparency = roundf(error * maxTransparency);
int alpha = transparency;
int alpha2 = maxTransparency - transparency;
if(!last) {
alpha = maxTransparency;
alpha2 = maxTransparency;
}
setPixel4(x, y, _x, floorf(_y), r, g, b, alpha, data, areasData, false);
setPixel4(x, y, _x, floorf(_y) - 1, r, g, b, alpha2, data, areasData, false);
}
quarter = roundf(radiusY2 / sqrtf(radiusX2 + radiusY2));
for(float _y = 0; _y <= quarter; _y++) {
float _x = radiusX * sqrtf(1 - _y * _y / radiusY2);
float error = _x - floorf(_x);
float transparency = roundf(error * maxTransparency);
int alpha = transparency;
int alpha2 = maxTransparency - transparency;
if(!last) {
alpha = maxTransparency;
alpha2 = maxTransparency;
}
setPixel4(x, y, floorf(_x), _y, r, g, b, alpha, data, areasData, false);
setPixel4(x, y, floorf(_x) - 1, _y, r, g, b, alpha2, data, areasData, false);
}
}
我该如何解决?
编辑:
因为我无法使用泛洪填充来填充圆(我绘制的区域可能不是单色背景,并且需要混合这些颜色),所以我实现了一种简单的方法来将点与线连接起来:
我在setPixel4方法中添加了2个drawLine调用:
void setPixel4(int x, int y, int deltaX, int deltaY, int r, int g, int b, int a, unsigned char* data, unsigned char* areasData, bool blendColor) {
drawLine(x - deltaX, y - deltaY, x + deltaX, y + deltaY, r, g, b, 127, data, areasData); //maxTransparency
drawLine(x + deltaX, y - deltaY, x - deltaX, y + deltaY, r, g, b, 127, data, areasData); //maxTransparency
setPixelWithCheckingArea(x + deltaX, y + deltaY, r, g, b, a, data, areasData, blendColor);
setPixelWithCheckingArea(x - deltaX, y + deltaY, r, g, b, a, data, areasData, blendColor);
setPixelWithCheckingArea(x + deltaX, y - deltaY, r, g, b, a, data, areasData, blendColor);
setPixelWithCheckingArea(x - deltaX, y - deltaY, r, g, b, a, data, areasData, blendColor);
}
,它看起来与第三张图片完全相同。我认为里面的这些白色像素是由外圆(来自xiaolin wu算法)本身引起的。
修改2:
由于@JaMiT,我已经改进了代码,它可以工作一圈,但是当我彼此重叠时,它会失败。首先,新代码:
void drawFilledCircle(int x, int y, int startRadius, int endRadius, int r, int g, int b, int a, unsigned char* data, unsigned char* areasData, int startAngle, int endAngle, bool blendColor) {
assert(startAngle <= endAngle);
assert(startRadius <= endRadius);
dfBufferCounter = 0;
for(int i = 0; i < DRAW_FILLED_CIRCLE_BUFFER_SIZE; i++) {
drawFilledCircleBuffer[i] = -1;
}
for(int cradius = endRadius; cradius >= startRadius; cradius--) {
bool last = cradius == endRadius;
bool first = cradius == startRadius && cradius != 0;
float radiusX = cradius;
float radiusY = cradius;
float radiusX2 = radiusX * radiusX;
float radiusY2 = radiusY * radiusY;
float maxTransparency = 127;
float quarter = roundf(radiusX2 / sqrtf(radiusX2 + radiusY2));
for(float _x = 0; _x <= quarter; _x++) {
float _y = radiusY * sqrtf(1 - _x * _x / radiusX2);
float error = _y - floorf(_y);
float transparency = roundf(error * maxTransparency);
int alpha = last ? transparency : maxTransparency;
int alpha2 = first ? maxTransparency - transparency : maxTransparency;
setPixel4(x, y, _x, floorf(_y), r, g, b, alpha, cradius, endRadius, data, areasData, blendColor);
setPixel4(x, y, _x, floorf(_y) - 1, r, g, b, alpha2, cradius, endRadius, data, areasData, blendColor);
}
quarter = roundf(radiusY2 / sqrtf(radiusX2 + radiusY2));
for(float _y = 0; _y <= quarter; _y++) {
float _x = radiusX * sqrtf(1 - _y * _y / radiusY2);
float error = _x - floorf(_x);
float transparency = roundf(error * maxTransparency);
int alpha = last ? transparency : maxTransparency;
int alpha2 = first ? maxTransparency - transparency : maxTransparency;
setPixel4(x, y, floorf(_x), _y, r, g, b, alpha, cradius, endRadius, data, areasData, blendColor);
setPixel4(x, y, floorf(_x) - 1, _y, r, g, b, alpha2, cradius, endRadius, data, areasData, blendColor);
}
}
}
在setPixel4中没有drawLine调用,它看起来像这样:
我改进了setPixel4方法,以避免再次重画相同的像素:
void setPixel4(int x, int y, int deltaX, int deltaY, int r, int g, int b, int a, int radius, int maxRadius, unsigned char* data, unsigned char* areasData, bool blendColor) {
for(int j = 0; j < 4; j++) {
int px, py;
if(j == 0) {
px = x + deltaX;
py = y + deltaY;
} else if(j == 1) {
px = x - deltaX;
py = y + deltaY;
} else if(j == 2) {
px = x + deltaX;
py = y - deltaY;
} else if(j == 3) {
px = x - deltaX;
py = y - deltaY;
}
int index = (px + (img->getHeight() - py - 1) * img->getWidth()) * 4;
bool alreadyInBuffer = false;
for(int i = 0; i < dfBufferCounter; i++) {
if(i >= DRAW_FILLED_CIRCLE_BUFFER_SIZE) break;
if(drawFilledCircleBuffer[i] == index) {
alreadyInBuffer = true;
break;
}
}
if(!alreadyInBuffer) {
if(dfBufferCounter < DRAW_FILLED_CIRCLE_BUFFER_SIZE) {
drawFilledCircleBuffer[dfBufferCounter++] = index;
}
setPixelWithCheckingArea(px, py, r, g, b, a, data, areasData, blendColor);
}
}
}
然后,最后:
这几乎是完美的。但是,我花了很多时间才能摆脱白色轮廓,但我做不到。
答案 0 :(得分:3)
由于您离散圆,所以某些像素必定丢失。您获得的图片显示出莫尔效应,这是众所周知的。
最好的解决方案是使用任何泛洪填充算法或综合一些琐碎的算法,这些算法会在同一条水平线上(如果需要,也可以是垂直)上的圆点之间绘制线。
答案 1 :(得分:2)
考虑要做什么以获取第三张图像(在圆周内有“奇怪的孔”的图像)。您已经绘制了内部磁盘,并且想要在其周围绘制一个圆以使其稍大一些。好主意。 (您的微积分老师应该批准。)
但是,您不能简单地在它周围画一个圆。您会在其周围绘制一个 antialiased 圆圈。这意味着什么?这意味着您可以绘制两个不同透明度的图形,而不是简单地绘制一个点,以使人们以为它只是一个。这些点之一(内部的点)将覆盖您已经绘制的磁盘点。
当外部点更透明时,除了可能有点模糊之外没有其他问题。但是,当 inner 点更透明时,您会遇到这种奇怪的行为,磁盘开始时大多是不透明的,变得更加透明,然后又恢复为完全不透明。您从磁盘上获取了一个完全不透明的点,并使它几乎是透明的。您的眼睛将其解释为一个孔。
那么如何解决这个问题?
1)只要您的磁盘应该是均匀着色的(考虑到透明性),那么如果您反转外环,则最后一次尝试应该会起作用-从最大半径变为零。由于仅对最外层的圆进行抗锯齿处理,因此只有此反向循环的第一次迭代才会覆盖透明度更高的像素。在那个阶段没有什么可以覆盖的。
OR
2)在您设置alpha2
的两个地方,将其设置为maxTransparency
。这是内部像素的透明度,并且您不希望对内部边缘进行抗锯齿处理。继续前进,并在任一方向上循环通过半径,使磁盘不成圈。不绘制最外圆时,将两个透明度都设置为最大值。这种方法的优点是可以在磁盘中间放置一个孔。 startRadius
不必为零。当您位于startRadius
(且startRadius
不为零)时,请根据反斜杠算法将alpha2
设置为alpha
到maxTransparency
。
所以您的Alpha设置逻辑看起来像
bool first = cradius == startRadius && cRadius != 0; // Done earlier
int alpha = last ? transparency : maxTransparency;
int alpha2 = first ? maxTransparency - transparency : maxTransparency;
编辑:考虑一下,如果cRadius
为零,则将被零除。既然您显然已经考虑了这一点,那么您应该可以将“第一个”的概念改写为“最内层的圆,实际上我们正在留下一个空洞”。
OR
3)您可以按照建议的方式绘制线条,但是需要进行一些调整以最小化伪像。首先,删除每对中第二个对setPixel4
的呼叫;我们将用线条介绍这种情况。这消除了使用alpha2
的需要(这始终是造成漏洞的原因)。其次,尝试绘制一个方框(四行)而不是两条平行线。使用此算法,一半的图形基于水平线,另一半的图形基于垂直线。通过始终绘制两者,可以覆盖基础。第三,如果仍然看到工件,请尝试在第一个方框内绘制第二个方框。
答案 2 :(得分:0)
您应该只画一个外圆,并用实线水平简单线从左至右像素侧进行连接。
] 1