混合抗锯齿圆圈
Blending antialiased circles
我已经实现了吴小林算法来画一个抗锯齿的圆。它有效。但是,在我的应用程序中,我可以在屏幕上绘制许多圆圈,但它们没有完全不透明。所以,我想混合它们。在实施抗锯齿 Xiaolin Wu 算法之前,我的混合方法奏效了。我使用非常简单的混合:
int blendColors(int a, int b, float t) {
double s = sqrt((1 - t) * a * a + t * b * b);
return s;
}
void setPixel(int index, int r, int g, int b, int a, unsigned char* data) {
int oldR = data[index];
int oldG = data[index + 1];
int oldB = data[index + 2];
int oldA = data[index + 3];
int newA = min((int) (oldA + a * 0.25f), 255);
int newR = blendColors(oldR, r, 0.5f);
int newG = blendColors(oldG, g, 0.5f);
int newB = blendColors(oldB, b, 0.5f);
data[index] = newR;
data[index + 1] = newG;
data[index + 2] = newB;
data[index + 3] = newA;
}
Alpha 混合就像变暗一样。
现在,如果我从透明背景开始,它看起来像这样:
但是,当我从不透明背景开始时是这样的:
如您所见,缺少抗锯齿功能。那是因为不透明背景已经有 255 的不透明度。所以混合算法有问题。当背景不透明时,我必须找到另一种混合颜色的方法。我该怎么做?
圆算法在这里:
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);
}
}
}
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);
}
}
}
首先,alpha blending是线性,所以blendColors
是不正确的。
混合像素时,您还必须考虑叠加层颜色的 Alpha 通道。
假设以下的 (RGB, A)
值为:
- 当前像素 -
(b, 255)
(即不透明)
- 叠加颜色 -
(c, a)
- 输出像素 -
(d, 255)
混合方程为d = [ c * a + b * (255 - a) ] / 255
。
代码:
int blendColorAlpha(int c, int b, int a) {
return (c * a + b * (255 - a)) / 255;
}
int newA = 255; // always set the new alpha to 100%
int newR = blendColors(r, oldR, a);
int newG = blendColors(g, oldG, a);
int newB = blendColors(b, oldB, a);
在非不透明背景的情况下,混合方程稍微复杂一些;特别是,在混合不正确时仅使用最小 alpha。
(有关推导,请参阅 。)
代码:
struct rgba { unsigned char r, g, b, a; };
rgba blendRGBA(rgba x, rgba y)
{
int s = (int)(x.a), t = (int)(y.a);
int u = 255*255 - (255-s)*(255-t);
rgba z;
z.a = u / 255;
z.r = (255*t*y.r + (255-t)*s*x.r) / u;
z.g = (255*t*y.g + (255-t)*s*x.g) / u;
z.b = (255*t*y.b + (255-t)*s*x.b) / u;
return z;
}
...
rgba inpC = { r, g, b, a };
rgba* pixel = (rgba*)(&data[index]);
*pixel = blendRGBA(*pixel, inpC);
我已经实现了吴小林算法来画一个抗锯齿的圆。它有效。但是,在我的应用程序中,我可以在屏幕上绘制许多圆圈,但它们没有完全不透明。所以,我想混合它们。在实施抗锯齿 Xiaolin Wu 算法之前,我的混合方法奏效了。我使用非常简单的混合:
int blendColors(int a, int b, float t) {
double s = sqrt((1 - t) * a * a + t * b * b);
return s;
}
void setPixel(int index, int r, int g, int b, int a, unsigned char* data) {
int oldR = data[index];
int oldG = data[index + 1];
int oldB = data[index + 2];
int oldA = data[index + 3];
int newA = min((int) (oldA + a * 0.25f), 255);
int newR = blendColors(oldR, r, 0.5f);
int newG = blendColors(oldG, g, 0.5f);
int newB = blendColors(oldB, b, 0.5f);
data[index] = newR;
data[index + 1] = newG;
data[index + 2] = newB;
data[index + 3] = newA;
}
Alpha 混合就像变暗一样。
现在,如果我从透明背景开始,它看起来像这样:
但是,当我从不透明背景开始时是这样的:
如您所见,缺少抗锯齿功能。那是因为不透明背景已经有 255 的不透明度。所以混合算法有问题。当背景不透明时,我必须找到另一种混合颜色的方法。我该怎么做?
圆算法在这里:
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);
}
}
}
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);
}
}
}
首先,alpha blending是线性,所以blendColors
是不正确的。
混合像素时,您还必须考虑叠加层颜色的 Alpha 通道。
假设以下的 (RGB, A)
值为:
- 当前像素 -
(b, 255)
(即不透明) - 叠加颜色 -
(c, a)
- 输出像素 -
(d, 255)
混合方程为d = [ c * a + b * (255 - a) ] / 255
。
代码:
int blendColorAlpha(int c, int b, int a) {
return (c * a + b * (255 - a)) / 255;
}
int newA = 255; // always set the new alpha to 100%
int newR = blendColors(r, oldR, a);
int newG = blendColors(g, oldG, a);
int newB = blendColors(b, oldB, a);
在非不透明背景的情况下,混合方程稍微复杂一些;特别是,在混合不正确时仅使用最小 alpha。
(有关推导,请参阅
代码:
struct rgba { unsigned char r, g, b, a; };
rgba blendRGBA(rgba x, rgba y)
{
int s = (int)(x.a), t = (int)(y.a);
int u = 255*255 - (255-s)*(255-t);
rgba z;
z.a = u / 255;
z.r = (255*t*y.r + (255-t)*s*x.r) / u;
z.g = (255*t*y.g + (255-t)*s*x.g) / u;
z.b = (255*t*y.b + (255-t)*s*x.b) / u;
return z;
}
...
rgba inpC = { r, g, b, a };
rgba* pixel = (rgba*)(&data[index]);
*pixel = blendRGBA(*pixel, inpC);