§ Квантование изображения

eebd9f0703ef6813c5815ecaab4136c1.png
#include <qblib.c>

int main(int argc, char* argv[]) {

    screen(13);
    ditherpal();

    float t = 0;
    while (sdlevent(EVT_REDRAW)) {

        cls();
        useindex(0);

        for (int i = 0; i < 192; i++)
        for (int j = 0; j < 320; j++) {

            vec2 src_r = {160 + sin(t)*100, 100 + cos(t)*100};
            vec2 src_g = {160 - sin(t)*100, 100 + cos(t)*100};
            vec2 src_b = {160 + sin(t)*100, 100 - cos(t)*100};

            int r = 255 - sqrt(pow(j-src_r.x, 2) + pow(i-src_r.y, 2));
            int g = 255 - sqrt(pow(j-src_g.x, 2) + pow(i-src_g.y, 2));
            int b = 255 - sqrt(pow(j-src_b.x, 2) + pow(i-src_b.y, 2));

            pset(j, i, rgb(r, g, b));
        }

        t = t + 0.01;
        int middle = 160 + 160*sin(t);
        line(middle, 0, middle, 192, 0xffffff);

        // Сам алгоритм
        ditherect(0, 0, middle, 192, 1);
    }

    return 0;
}
Найти ближайший цвет
int dither_search_nearest(rgbf& incl) {

    float dist_min = 4 * pow(256, 2);
    int   dist_col = 0;

    // dospal
    for (int i = 0; i < 256; i++) {

        // Сравниваемый цвет
        struct rgbf cl = palette[i];

        // Дистанция между точками (r`-r)^2 + (g`-g)^2 + (b`-b)^2
        float dist = pow(incl.r - cl.r, 2) +
                     pow(incl.g - cl.g, 2) +
                     pow(incl.b - cl.b, 2);

        if (dist < dist_min) {

            dist_min = dist;
            dist_col = i;
        }
    }

    return dist_col;
}
Процедура дизеринга (left, top)-(right, bottom), dith=0 (nearest) 1(dithered)
void ditherect(int left, int top, int right, int bottom, int dith) {

    rgbf ws[QB_MAX_WIDTH][QB_MAX_HEIGHT];
    int  idx[QB_MAX_WIDTH][QB_MAX_HEIGHT];

    // Копировать целочисленные точки в не целочисленные
    for (int y = top; y < bottom; y++)
    for (int x = left; x < right;  x++) {

        ws[x][y] = {
            (float) qb_pixels[x][y].r,
            (float) qb_pixels[x][y].g,
            (float) qb_pixels[x][y].b
        };
    }

    // Выполнить дизеринг
    for (int y = top;  y < bottom; y++)
    for (int x = left; x < right;  x++) {

        // Старые цвета
        rgbf old = ws[x][y];

        // Поиск ближайшего цвета из палитры
        int color_id = dither_search_nearest(old);

        // Полученный индекс цвета
        idx[x][y] = color_id;

        // Учесть ошибки квантования
        if (dith) {

            // Заменить на новый цвет (из палитры)
            rgbf ncl = palette[];

            // Отметить цвет @todo может это вообще не надо
            ws[x][y] = ncl;

            // Вычисляем ошибку квантования
            rgbf quant;

            quant.r = (old.r - ncl.r);
            quant.g = (old.g - ncl.g);
            quant.b = (old.b - ncl.b);

            //   x 7
            // 3 5 1

            // [+1, +0] 7/16
            if (x + 1 < right) {

                ws[x + 1][y].r += (quant.r * 7.0/16.0);
                ws[x + 1][y].g += (quant.g * 7.0/16.0);
                ws[x + 1][y].b += (quant.b * 7.0/16.0);
            }

            // [-1, +1] 3/16
            if (x - 1 >= 0 && y + 1 < bottom) {

                ws[x - 1][y + 1].r += (quant.r * 3.0/16.0);
                ws[x - 1][y + 1].g += (quant.g * 3.0/16.0);
                ws[x - 1][y + 1].b += (quant.b * 3.0/16.0);
            }

            // [+0, +1] 5/16
            if (y + 1 < bottom) {

                ws[x][y + 1].r += (quant.r * 5.0/16.0);
                ws[x][y + 1].g += (quant.g * 5.0/16.0);
                ws[x][y + 1].b += (quant.b * 5.0/16.0);
            }

            // [+1, +1] 1/16
            if (x + 1 < right && y + 1 < bottom) {

                ws[x + 1][y + 1].r += (quant.r * 1.0/16.0);
                ws[x + 1][y + 1].g += (quant.g * 1.0/16.0);
                ws[x + 1][y + 1].b += (quant.b * 1.0/16.0);
            }
        }
    }
}