Теория
Понять полупрозрачность (semi-transparency) не сложно: вся работа заключается в добавлении некого процента одного цвета к некому проценту другого цвета. К примеру, Вы хотите отобразить оттенок красного, который является на 80% непрозрачен на жёлтом. Для этого нужно лишь взять по 80% красного, зелёного и синего компонентов первого цвета (оттенок красного) и добавить 20% красного, зелёного и синего компонентов жёлтого цвета.
Теперь представьте, что Вам нужно отобразить растровое изображение 80х80 с полупрозрачностью. Вы были бы должны вычислять значения RGB-компонент для каждого пикселя (и, если Вы работаете с 256 цветами, необходимо было бы искать самый лучшей цвет в палитре). Это, предположите Вы, очень медленно. Поэтому, чтобы увеличить быстродействие, нам придётся немного схитрить.
Введение
Поскольку вычисления результирующих компонент RGB и поиск наилучшего соответствия слишком медленны в реальном масштабе времени, мы посчитаем их заранее и сохраним в 2D массиве, так называемой таблице соответствия (look-up table).
В режиме 256 цветов имеется 2562 возможных вариантов комбинации цветов для каждого уровня прозрачности. Иными словами, Вам нужно 64 килобайта для каждой таблицы. Хорошая новость: каждая таблица может использоваться для двух уровней прозрачности. Но, скажете Вы, нужны хотя бы четыре уровня, к примеру, 80%, 60%, 40% и 20%. Эта информация может быть сохранена только в двух таблицах, так как lut[a][b] хранит прозрачность для n%, а lut[b][a] для (100-n)%.
Код
Вот код, чтобы посчитать результирующий цвет, найти лучшее соответствие этому цвету и создать таблице соответствия. Другими словами, Вы должны использовать полупрозрачность самостоятельно. Я не привожу, кода по наложению прозрачности (blitting), так как он очень зависим от компиляторов. Но вот псевдокод для наложения прозрачности на один пиксель:
void PutPixelTrans(int x, int y, char col, char *lut)
{
char bgCol;
bgCol=GetPixel(x,y);
PutPixel(x,y, lut[bgCol + 256*col]);
}
А вот остальной код:
/*
* Простой код для наложения полупрозрачности
* by Lennart Steinke <lennart_steinke@bigfoot.com>
*/
/* Немного определений... */
#define UBYTE unsigned char
#define RED(pal,a) (pal)[(a)*3+0]
#define GREEN(pal,a) (pal)[(a)*3+1]
#define BLUE(pal,a) (pal)[(a)*3+2]
#define SQR(x) ((x) * (x))
/*
* best_match
* Возвращает наилучшее соответствие заданным RGB компонентам (r,g,b)
* в заданной палитре (pal).
* pal является массивом byte с размером 768 (256 *3)
*/
UBYTE best_match(UBYTE* pal, UBYTE r, UBYTE g, UBYTE b)
{
int result=0;
int r2,g2,b2,a;
unsigned long f=MAXLONG;
int i, i2;
i=0.3*r + 0.59*g + 0.11*b;
for (a=0; a<256; a++)
{
r2=RED(pal, a);
g2=GREEN(pal, a);
b2=BLUE(pal, a);
i2 =0.3*r2 + 0.59*g2 + 0.11*b;
if (SQR(r2-r) + SQR(g2-g) + SQR(b2-b) + SQR(i2-i)< f)
{
result=a;
f=SQR(r2-r)+SQR(g2-g)+SQR(b2-b)+SQR(i2-i);
if (f==0) break;
}
}
return (UBYTE) result;
}
/*
* CreateLUT
* Создаёт таблицу просмотра для заданной палитры (pal)
* и уровня прозрачности (alpha), возвращает указатель на таблицу
*/
UBTYE *CreateLUT(UBYTE *pal, float alpha)
{
UBYTE r,g,b, r_,g_,b_*table;
int actcol, overaylcol, alpha_, pos=0;
alpha_ = 1- alpha;
table = (UBYTE *) malloc( 256 * 256);
for (actcol=0; actcol <256; actcol++)
{
r = RED(pal, actcol);
g = GREEN(pal, actcol);
b = BLUE(pal, actcol);
for (overlaycol =0; overlaycol < 256 overlaycol ++)
{
r_ = r * alpha_ + RED(pal, overlaycol) * alpha;
g_ = g * alpha_ + GREEN(pal, overlaycol) * alpha;
b_ = b * alpha_ + BLUE(pal, overlaycol) * alpha;
table[pos] = best_match(pal, r_, g_, b_);
pos++;
}
}
}
В заключение
Я хочу поблагодарить Amit Patel за напоминание, что одна таблица содержит два уровня полупрозрачности.
Верстка и поддержка Lennart Steinke