Как правильно выполнять альфа-смешивание?(С) - PullRequest
3 голосов
/ 01 июля 2010

Я пишу очень простую графическую библиотеку и пытаюсь понять, как сделать альфа-смешивание.Я пробовал это несколько раз, но мои результаты были менее чем удовлетворительными.Согласно Википедии, я должен сделать:

Значение = (1-альфа) * ​​1005 * Значение0 + альфа значение1

Это, однако, не работает вообще.Может быть, я что-то не так делаю?

Код, который я включил, рисует красочную картинку (это функция «близости»), затем пытается нарисовать частично прозрачную рамку в (100,100).Однако вместо белой полупрозрачной рамки я получаю странное искажение изображения (я постараюсь, чтобы они были внизу моего поста).Какие-либо предложения?Вот мой код:

#include "hgl.h"

void proximity()
{
    int x = 0, y = 0, d1, d2, d3, dcenter;

    while(x < WIDTH){
        while(y < HEIGHT){
            d1 = distance(x, y, (WIDTH/2) - 200, (HEIGHT/2) + 200);
            d2 = distance(x, y, (WIDTH/2) + 200, (HEIGHT/2) + 200);
            d3 = distance(x, y, (WIDTH/2), (HEIGHT/2) - 150);
            dcenter = distance(x, y, WIDTH/2, HEIGHT/2);
            putpixel(x, y, d1, d2, d3);
            y++;
        }
        y = 0;
        x++;
    }
}

int alpha_transparency(float alpha, float value1, float value2)
{
    return (1-alpha) * value1 + alpha * value2;
}

void transparent_box(int pos_x, int pos_y, int width, int height, float alpha, char r, char g, char b)
{
    int x = 0, y = 0;
    while(x < width)
    {
        while(y < height)
        {
            int rr, rg, rb;
            rr = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].r, r);
            rg = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].g, g);
            rb = alpha_transparency(alpha, p.bitmap[x+pos_x][y+pos_y].b, b);
            putpixel(pos_x + x, pos_y + y, rr, rg, rb);
            y++;
        }
        x++;
        y = 0;
    }
}

int main()
{
    fp = fopen("out.bmp","wb");

    set_dimensions(1440, 900);
    insert_header();

    white_screen();

    proximity();
    transparent_box(100, 100, 500, 500, .9, 255, 255, 255);

    insert_image();
    fclose(fp);
    return 0;
}

Извините, я не смог включить вывод, потому что я новый пользователь.Тем не менее, вот ссылки:

Исходное изображение

Изображение с «прозрачной» рамкой

Ответы [ 3 ]

2 голосов
/ 02 июля 2010

Ваша функция альфа-смешивания верна;Другой способ думать об альфа-смешивании состоит в том, что он интерполирует между двумя значениями цвета, основанными на альфе, поэтому это должно быть значение в [0, 1].

Однако вы не должны передавать цветовые компоненты какchar, который подписан по умолчанию.Вы должны передать их как unsigned char или как более широкий целочисленный тип.Происходит то, что вместо того, чтобы передавать 255, как вы ожидаете, вы передаете -1.

Другими словами, сохраняйте компоненты цвета как unsigned char s, чтобы гарантировать, что у вас нетshenanigans подписи (см. РЕДАКТИРОВАТЬ 2).

РЕДАКТИРОВАТЬ: Обратите внимание, что если ваша альфа находится в [0, 255], вы должны нормализовать его до [0, 1] для выполнения операции альфа-смешивания.

EDIT2: Кроме того, если вы сохраняете ваши пиксели как char вместо unsigned char, это объяснило бы странный зажим, который я видел:

alpha_transparency(0.9, (char)255, (char)255)
== alpha_transparency(0.9f, -1.0f, -1.0f)
== -1.0f
== 0xff (cast to char)

alpha_transparency(0.9, (char)128, (char)255)
== alpha_transparency(0.9f, -128.0f, -1.0f)
== -13.7f
== 0xf3

alpha_transparency(0.9, (char)127, (char)255)
== alpha_transparency(0.9f, 127.0f, -1.0f)
== -11.80f
== 0x0b

alpha_transparency(0.9, (char)0, (char)255)
== alpha_transparency(0.9f, 0.0f, -1.0f)
== -0.9f
== 0x00
0 голосов
/ 02 июля 2010

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

Для всех целых необходимо помнить о необходимости масштабирования целого числа, чтобы результат соответствовал исходному диапазону:

int alpha_transparency(int alpha, int value1, int value2) {
  int unscaled= (255-alpha)*value1 + alpha*value2;
  return unscaled / 255;  /* integer division */
}

Для всех чисел с плавающей запятой необходимо помнить, чтобы нормализовать целочисленные входные данные от необработанного значения в [0..255] (или чего-либо еще) до значения с плавающей запятой в [0.0 .. 1.0], выполнить всю обработку, а затем преобразовать обратно целое число только в конце.

float input_raw_value(unsigned char value)  { return value/255.0; }
...
float alpha_transparency(float alpha, float value1, float value2) {
  return (1.0-alpha)*value1 + alpha*value2;
}
...
unsigned char output_raw_value(float value) { return value*255.0; }

Обратите внимание, что я игнорировал проблемы округления в каждом методе; как только вы освоите основную математику, вам следует обратить на это внимание. Кроме того, существуют различные приемы, позволяющие заменить деления (которые могут быть относительно медленными) умножением или изменением битов.

0 голосов
/ 01 июля 2010

Я думаю, что проблема в том, как вы справляетесь с цветами.Метод, используемый в Википедии, предполагает, что 0 - это черный, а 1 - белый, а 0,5 - в середине.Однако ваш код использует целочисленные значения, поэтому я предполагаю, что вы определяете 0 как черный и 255 как белый.

Итак, правильный код:

return (255-alpha)*value1+alpha*value2;

Возможно, вы также страдаете от округления компилятора, когда вы не думаете, что это произойдет.Я бы изменил код в вашей функции следующим образом:

float result = (255.0f-alpha)*value1+alpha*value2;
return (int) result;

В общем, очень часто работать с изображениями, используя плавающие вместо целых.Сегодня многие программы конвертируют все изображение в плавающие, обрабатывают его и затем конвертируют обратно.Таким образом вы можете избежать нескольких ошибок.

...