Смешивание сглаженных кругов - PullRequest
0 голосов
/ 14 февраля 2019

Я реализовал алгоритм Xiaolin Wu, чтобы нарисовать сглаженный круг.И это работает.Однако в моем приложении я могу нарисовать на экране много кругов, и они не имеют полной непрозрачности.Итак, я хочу смешать их.До реализации алгоритма сглаживания 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;

}

Альфа-смешение работает как потемнение.

Теперь, если я начинаю с прозрачного фона, это выглядит так:

enter image description here

Но, когда я начинаю с непрозрачного фона, это выглядиткак это:

enter image description here

Как видите, сглаживание отсутствует.Это потому, что непрозрачный фон уже имеет 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);
        }
    }

}

1 Ответ

0 голосов
/ 14 февраля 2019

Во-первых, альфа-смешение равно линейному , поэтому blendColors неверно.

При смешивании пикселя необходимо также учитывать альфа-каналцвет наложения.

Предположим, что значение (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);

В случае непрозрачных фонов уравнение смешивания несколько сложнее;в частности, просто использовать минимальный альфа, когда смешивание некорректно.

(См. мой ответ здесь для деривации.)

Код:

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