Antialiasing in self-drawn pixel array

Hi,
for various reasons, in one of my projects, I don’t use DrawNode to draw shapes on screen, but instead I create unsigned char* data array. In other words it’s a pixel array. Using this array I simply put it into Texture class and then into Sprite. It works nicely and I have full control of what I draw. Of course, I had to create drawing methods from scratch. From simple setPixel through drawLine to drawFilledCircle. The problem is, especially for circles, that it has jagged edges. In other words, I need antialiasing. There are a lot of algorithms. For example: Drawing Antialiased Circles and Ellipses Before that I’ve even created my own very dumb algorithm - simply the outer circle is drawn with lower transparency. And it somehow works, but only if there’s one circle. But when there are 2 circles intersecting each other, no matter if it’s mine dumb method or something better, the outer line of circle is too “transparent”. In other words you can see a line in the intersection part, which shouldn’t be there:

Image-1

It’s not really cocos2d-x problem, but a logical one.

Another example will be something like a brush, which draws a filled circle when user moves a finger. When you don’t antialias the “line” looks nice, but of course you have jagged edges, however antialiasing this circle creates the issue I’ve described above.

Have someone maybe encountered this problem and knows how to solve it? Any links or resources will be appreciated.

In short, you use strange antialiasing algorithm which cause problems like mentioned one. Just change antialiasing method and problems with blending will disappear. :wink:

Simplest possible solution is overspaling - just draw bigger graphics and scale it down using avarge values of rescaled pixels. You doesn’t must alloc bigger memory, you can for example calculate 4 samples for every single pixel to achieve this same effect.

Another good solution is calculating transparency relative to how far is center of pixel to drawed figure.

I’ve did it… partially…

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);
}

Looks fine, but I need filled circle. So I thought I’d just iterate from 1 to radius and use max transparency expect last iteration:

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);
    }
}

However, it looks like this:

Image-1-2

more detailed question: https://stackoverflow.com/questions/54594822/xiaolin-wu-circle-algorithm-renders-circle-with-holes-inside