tinydisplay: Reimplement color blending, supporting more options

Separate blend modes/operands for rgb/alpha are now supported, and min/max blend
modes are supported too.  To avoid code bloat, the operands are now no
longer implemented as separate permutations in the table, but using a
small table and a mask.  I haven't tested performance but it shouldn't
be much worse (and this is the "general" fallback case anyway, which
should prioritize correctness) and the number of functions is significantly reduced.

A bug was fixed regarding sRGB behavior when only the blue channel is
enabled.

M_subtract and M_inv_subtract are still left unimplemented.
This commit is contained in:
rdb 2026-01-29 19:22:46 +01:00
parent b7ffef7338
commit dbe05e079b
7 changed files with 1131 additions and 37765 deletions

View File

@ -24,10 +24,50 @@ FNAME(store_pixel) (ZBuffer *zb, PIXEL &result, int r, int g, int b, int a) {
unsigned int fb = PIXEL_B(result);
unsigned int fa = PIXEL_A(result);
r = STORE_PIXEL_0(fr, ((unsigned int)r * OP_A(fr, r) >> 16) + ((unsigned int)fr * OP_B(fr, r) >> 16));
g = STORE_PIXEL_1(fg, ((unsigned int)g * OP_A(fg, g) >> 16) + ((unsigned int)fg * OP_B(fg, g) >> 16));
b = STORE_PIXEL_2(fb, ((unsigned int)b * OP_A(fb, b) >> 16) + ((unsigned int)fb * OP_B(fb, b) >> 16));
a = STORE_PIXEL_3(fa, ((unsigned int)a * OP_A(fa, a) >> 16) + ((unsigned int)fa * OP_B(fa, a) >> 16));
#if HAVE_R
unsigned int r_ops[] = {0, (unsigned int)r, fr, (unsigned int)a, fa};
unsigned int r_opa = r_ops[zb->blenda.op_rgb] ^ zb->blenda.xor_r;
unsigned int r_opb = r_ops[zb->blendb.op_rgb] ^ zb->blendb.xor_r;
(void)r_opa;
(void)r_opb;
r = MODE_RGB(r, r_opa, fr, r_opb);
#else
r = fr;
#endif
#if HAVE_G
unsigned int g_ops[] = {0, (unsigned int)g, fg, (unsigned int)a, fa};
unsigned int g_opa = g_ops[zb->blenda.op_rgb] ^ zb->blenda.xor_g;
unsigned int g_opb = g_ops[zb->blendb.op_rgb] ^ zb->blendb.xor_g;
(void)g_opa;
(void)g_opb;
g = MODE_RGB(g, g_opa, fg, g_opb);
#else
g = fg;
#endif
#if HAVE_B
unsigned int b_ops[] = {0, (unsigned int)b, fb, (unsigned int)a, fa};
unsigned int b_opa = b_ops[zb->blenda.op_rgb] ^ zb->blenda.xor_b;
unsigned int b_opb = b_ops[zb->blendb.op_rgb] ^ zb->blendb.xor_b;
(void)b_opa;
(void)b_opb;
b = MODE_RGB(b, b_opa, fb, b_opb);
#else
b = fb;
#endif
#if HAVE_A
unsigned int a_ops[] = {0, (unsigned int)a, fa, (unsigned int)a, fa};
unsigned int a_opa = a_ops[zb->blenda.op_alpha] ^ zb->blenda.xor_a;
unsigned int a_opb = a_ops[zb->blendb.op_alpha] ^ zb->blendb.xor_a;
(void)a_opa;
(void)a_opb;
a = MODE_ALPHA(a, a_opa, fa, a_opb);
#else
a = fa;
#endif
result = RGBA_TO_PIXEL(r, g, b, a);
}
@ -36,25 +76,67 @@ FNAME(store_pixel) (ZBuffer *zb, PIXEL &result, int r, int g, int b, int a) {
static void
FNAME_S(store_pixel) (ZBuffer *zb, PIXEL &result, int r, int g, int b, int a) {
#if HAVE_R || HAVE_G || HAVE_B || HAVE_A
unsigned int fr = PIXEL_SR(result);
unsigned int fg = PIXEL_SG(result);
unsigned int fb = PIXEL_SB(result);
unsigned int fa = PIXEL_A(result);
r = STORE_PIXEL_0(fr, ((unsigned int)r * OP_A(fr, r) >> 16) + ((unsigned int)fr * OP_B(fr, r) >> 16));
g = STORE_PIXEL_1(fg, ((unsigned int)g * OP_A(fg, g) >> 16) + ((unsigned int)fg * OP_B(fg, g) >> 16));
b = STORE_PIXEL_2(fb, ((unsigned int)b * OP_A(fb, b) >> 16) + ((unsigned int)fb * OP_B(fb, b) >> 16));
a = STORE_PIXEL_3(fa, ((unsigned int)a * OP_A(fa, a) >> 16) + ((unsigned int)fa * OP_B(fa, a) >> 16));
#if HAVE_R
unsigned int r_ops[] = {0, (unsigned int)r, fr, (unsigned int)a, fa};
unsigned int r_opa = r_ops[zb->blenda.op_rgb] ^ zb->blenda.xor_r;
unsigned int r_opb = r_ops[zb->blendb.op_rgb] ^ zb->blendb.xor_r;
(void)r_opa;
(void)r_opb;
r = MODE_RGB(r, r_opa, fr, r_opb);
#else
r = fr;
#endif
#if HAVE_G
unsigned int g_ops[] = {0, (unsigned int)g, fg, (unsigned int)a, fa};
unsigned int g_opa = g_ops[zb->blenda.op_rgb] ^ zb->blenda.xor_g;
unsigned int g_opb = g_ops[zb->blendb.op_rgb] ^ zb->blendb.xor_g;
(void)g_opa;
(void)g_opb;
g = MODE_RGB(g, g_opa, fg, g_opb);
#else
g = fg;
#endif
#if HAVE_B
unsigned int b_ops[] = {0, (unsigned int)b, fb, (unsigned int)a, fa};
unsigned int b_opa = b_ops[zb->blenda.op_rgb] ^ zb->blenda.xor_b;
unsigned int b_opb = b_ops[zb->blendb.op_rgb] ^ zb->blendb.xor_b;
(void)b_opa;
(void)b_opb;
b = MODE_RGB(b, b_opa, fb, b_opb);
#else
b = fb;
#endif
#if HAVE_A
unsigned int a_ops[] = {0, (unsigned int)a, fa, (unsigned int)a, fa};
unsigned int a_opa = a_ops[zb->blenda.op_alpha] ^ zb->blenda.xor_a;
unsigned int a_opb = a_ops[zb->blendb.op_alpha] ^ zb->blendb.xor_a;
(void)a_opa;
(void)a_opb;
a = MODE_ALPHA(a, a_opa, fa, a_opb);
#else
a = fa;
#endif
result = SRGBA_TO_PIXEL(r, g, b, a);
#endif
}
#undef FNAME_S
#endif
#undef FNAME
#undef OP_A
#undef OP_B
#undef STORE_PIXEL_0
#undef STORE_PIXEL_1
#undef STORE_PIXEL_2
#undef STORE_PIXEL_3
#undef MODE_RGB
#undef MODE_ALPHA
#undef HAVE_R
#undef HAVE_G
#undef HAVE_B
#undef HAVE_A

View File

@ -7,44 +7,32 @@ Each different combination of options is compiled to a different
inner-loop store function. The code in tinyGraphicsStateGuardian.cxx
will select the appropriate function pointer at draw time. """
Operands = [
'zero', 'one',
'icolor', 'micolor',
'fcolor', 'mfcolor',
'ialpha', 'mialpha',
'falpha', 'mfalpha',
'ccolor', 'mccolor',
'calpha', 'mcalpha',
Modes = [
'add',
'min',
'max',
]
CodeTable = {
'zero' : '0',
'one' : '0x10000',
'icolor' : 'i',
'micolor' : '0xffff - i',
'fcolor' : 'f',
'mfcolor' : '0xffff - f',
'ialpha' : 'a',
'mialpha' : '0xffff - a',
'falpha' : 'fa',
'mfalpha' : '0xffff - fa',
'ccolor' : 'zb->blend_ ## i',
'mccolor' : '0xffff - zb->blend_ ## i',
'calpha' : 'zb->blend_a',
'mcalpha' : '0xffff - zb->blend_a',
}
# We handle alpha write as a special "off" mode, reducing redundancy.
AlphaModes = Modes + ['off']
CodeTable = {
'add': 'STORE_PIX_CLAMP(((unsigned int)c * c_opa >> 16) + ((unsigned int)fc * c_opb >> 16))',
'min': 'std::min((unsigned int)c, (unsigned int)fc)',
'max': 'std::max((unsigned int)c, (unsigned int)fc)',
'off': '(unsigned int)fc',
}
bitnames = 'rgba'
def getFname(op_a, op_b, mask):
def get_fname(rgb_mode, alpha_mode, mask):
maskname = ''
for b in range(4):
for b in range(3):
if (mask & (1 << b)):
maskname += bitnames[b]
else:
maskname += '0'
return 'store_pixel_%s_%s_%s' % (op_a, op_b, maskname)
return 'store_pixel_%s_%s_%s' % (rgb_mode, alpha_mode, maskname)
# We write the code that actually instantiates the various
# pixel-storing functions to store_pixel_code.h.
@ -58,58 +46,44 @@ table = open('store_pixel_table.h', 'w')
print('/* This file is generated code--do not edit. See store_pixel.py. */', file=table)
print('', file=table)
for op_a in Operands:
for op_b in Operands:
for mask in range(0, 16):
fname = getFname(op_a, op_b, mask)
for rgb_mode in Modes:
for alpha_mode in AlphaModes:
for mask in range(0, 8):
fname = get_fname(rgb_mode, alpha_mode, mask)
print('#define FNAME(name) %s' % (fname), file=code)
if mask & (1 | 2 | 3):
if mask != 0:
print('#define FNAME_S(name) %s_s' % (fname), file=code)
print('#define OP_A(f, i) ((unsigned int)(%s))' % (CodeTable[op_a]), file=code)
print('#define OP_B(f, i) ((unsigned int)(%s))' % (CodeTable[op_b]), file=code)
for b in range(0, 4):
print('#define MODE_RGB(c, c_opa, fc, c_opb) %s' % (CodeTable[rgb_mode]), file=code)
print('#define MODE_ALPHA(c, c_opa, fc, c_opb) %s' % (CodeTable[alpha_mode]), file=code)
for b in range(0, 3):
if (mask & (1 << b)):
print("#define STORE_PIXEL_%s(fr, r) STORE_PIX_CLAMP(r)" % (b), file=code)
else:
print("#define STORE_PIXEL_%s(fr, r) (fr)" % (b), file=code)
print("#define HAVE_%s 1" % (bitnames[b].upper()), file=code)
if alpha_mode != 'off':
print("#define HAVE_A 1", file=code)
print('#include "store_pixel.h"', file=code)
print('', file=code)
# Now, generate the table of function pointers.
arraySize = '[%s][%s][16]' % (len(Operands), len(Operands))
arraySize = '[%s][%s][8][2]' % (len(Modes), len(AlphaModes))
print('extern const ZB_storePixelFunc store_pixel_funcs%s;' % (arraySize), file=table)
print('const ZB_storePixelFunc store_pixel_funcs%s = {' % (arraySize), file=code)
for op_a in Operands:
for rgb_mode in Modes:
print(' {', file=code)
for op_b in Operands:
for alpha_mode in AlphaModes:
print(' {', file=code)
for mask in range(0, 16):
fname = getFname(op_a, op_b, mask)
print(' %s,' % (fname), file=code)
for mask in range(0, 8):
fname = get_fname(rgb_mode, alpha_mode, mask)
if mask != 0:
fname_s = fname + '_s'
else:
fname_s = fname
print(' {%s, %s},' % (fname, fname_s), file=code)
print(' },', file=code)
print(' },', file=code)
print('};', file=code)
print('', file=code)
# Now do this again, but for the sRGB function pointers.
print('extern const ZB_storePixelFunc store_pixel_funcs_sRGB%s;' % (arraySize), file=table)
print('const ZB_storePixelFunc store_pixel_funcs_sRGB%s = {' % (arraySize), file=code)
for op_a in Operands:
print(' {', file=code)
for op_b in Operands:
print(' {', file=code)
for mask in range(0, 16):
fname = getFname(op_a, op_b, mask)
if mask & (1 | 2 | 3):
print(' %s_s,' % (fname), file=code)
else:
print(' %s,' % (fname), file=code)
print(' },', file=code)
print(' },', file=code)
print('};', file=code)

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,3 @@
/* This file is generated code--do not edit. See store_pixel.py. */
extern const ZB_storePixelFunc store_pixel_funcs[14][14][16];
extern const ZB_storePixelFunc store_pixel_funcs_sRGB[14][14][16];
extern const ZB_storePixelFunc store_pixel_funcs[3][4][8][2];

View File

@ -770,96 +770,100 @@ begin_draw_primitives(const GeomPipelineReader *geom_reader,
int color_write_state = 0; // cstore
const ColorWriteAttrib *target_color_write = DCAST(ColorWriteAttrib, _target_rs->get_attrib_def(ColorWriteAttrib::get_class_slot()));
const ColorWriteAttrib *target_color_write;
_target_rs->get_attrib_def(target_color_write);
unsigned int color_channels =
target_color_write->get_channels() & _color_write_mask;
unsigned int rgb_channels = color_channels & ColorWriteAttrib::C_rgb;
if (color_channels == ColorWriteAttrib::C_all) {
if (srgb_blend) {
color_write_state = 4; // csstore
} else {
color_write_state = 0; // cstore
}
} else {
// Implement a color mask.
int op_a = get_color_blend_op(ColorBlendAttrib::O_one);
int op_b = get_color_blend_op(ColorBlendAttrib::O_zero);
if (srgb_blend) {
_c->zb->store_pix_func = store_pixel_funcs_sRGB[op_a][op_b][color_channels];
} else {
_c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
}
color_write_state = 2; // cgeneral
}
const TransparencyAttrib *target_transparency = DCAST(TransparencyAttrib, _target_rs->get_attrib_def(TransparencyAttrib::get_class_slot()));
switch (target_transparency->get_mode()) {
case TransparencyAttrib::M_alpha:
case TransparencyAttrib::M_dual:
if (color_channels == ColorWriteAttrib::C_all) {
if (srgb_blend) {
color_write_state = 5; // csblend
} else {
color_write_state = 1; // cblend
}
} else {
// Implement a color mask, with alpha blending.
int op_a = get_color_blend_op(ColorBlendAttrib::O_incoming_alpha);
int op_b = get_color_blend_op(ColorBlendAttrib::O_one_minus_incoming_alpha);
if (srgb_blend) {
_c->zb->store_pix_func = store_pixel_funcs_sRGB[op_a][op_b][color_channels];
} else {
_c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
}
color_write_state = 2; // cgeneral
}
break;
case TransparencyAttrib::M_premultiplied_alpha:
{
// Implement a color mask, with pre-multiplied alpha blending.
int op_a = get_color_blend_op(ColorBlendAttrib::O_one);
int op_b = get_color_blend_op(ColorBlendAttrib::O_one_minus_incoming_alpha);
if (srgb_blend) {
_c->zb->store_pix_func = store_pixel_funcs_sRGB[op_a][op_b][color_channels];
} else {
_c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
}
color_write_state = 2; // cgeneral
}
break;
default:
break;
}
const ColorBlendAttrib *target_color_blend = DCAST(ColorBlendAttrib, _target_rs->get_attrib_def(ColorBlendAttrib::get_class_slot()));
if (target_color_blend->get_mode() == ColorBlendAttrib::M_add) {
// If we have a color blend set that we can support, it overrides the
// transparency set.
LColor c = target_color_blend->get_color();
_c->zb->blend_r = (int)(c[0] * ZB_POINT_RED_MAX);
_c->zb->blend_g = (int)(c[1] * ZB_POINT_GREEN_MAX);
_c->zb->blend_b = (int)(c[2] * ZB_POINT_BLUE_MAX);
_c->zb->blend_a = (int)(c[3] * ZB_POINT_ALPHA_MAX);
int op_a = get_color_blend_op(target_color_blend->get_operand_a());
int op_b = get_color_blend_op(target_color_blend->get_operand_b());
if (srgb_blend) {
_c->zb->store_pix_func = store_pixel_funcs_sRGB[op_a][op_b][color_channels];
} else {
_c->zb->store_pix_func = store_pixel_funcs[op_a][op_b][color_channels];
}
color_write_state = 2; // cgeneral
}
const ColorBlendAttrib *target_color_blend;
_target_rs->get_attrib_def(target_color_blend);
if (color_channels == ColorWriteAttrib::C_off) {
color_write_state = 3; // coff
}
else if (target_color_blend->get_mode() != ColorBlendAttrib::M_none) {
// If we have a color blend set that we can support, it overrides the
// transparency set.
int rgb_mode = get_color_blend_mode(target_color_blend->get_mode());
int alpha_mode;
if (color_channels & ColorWriteAttrib::C_alpha) {
alpha_mode = get_color_blend_mode(target_color_blend->get_alpha_mode());
} else {
// Disabled alpha write is encoded as a separate alpha mode, to cut down
// on redundancy in the store_pixel table.
alpha_mode = 3;
}
LColor c = target_color_blend->get_color();
unsigned int cr = (int)(c[0] * ZB_POINT_RED_MAX);
unsigned int cg = (int)(c[1] * ZB_POINT_GREEN_MAX);
unsigned int cb = (int)(c[2] * ZB_POINT_BLUE_MAX);
unsigned int ca = (int)(c[3] * ZB_POINT_ALPHA_MAX);
setup_color_blend(_c->zb->blenda, target_color_blend->get_operand_a(),
target_color_blend->get_alpha_operand_a(),
cr, cg, cb, ca);
setup_color_blend(_c->zb->blendb, target_color_blend->get_operand_b(),
target_color_blend->get_alpha_operand_b(),
cr, cg, cb, ca);
_c->zb->store_pix_func = store_pixel_funcs[rgb_mode][alpha_mode][rgb_channels][srgb_blend];
color_write_state = 2; // cgeneral
}
else {
const TransparencyAttrib *target_transparency;
_target_rs->get_attrib_def(target_transparency);
switch (target_transparency->get_mode()) {
case TransparencyAttrib::M_alpha:
case TransparencyAttrib::M_dual:
if (color_channels == ColorWriteAttrib::C_all) {
if (srgb_blend) {
color_write_state = 5; // csblend
} else {
color_write_state = 1; // cblend
}
} else {
// Implement a color mask, with alpha blending.
setup_color_blend(_c->zb->blenda, ColorBlendAttrib::O_incoming_alpha, ColorBlendAttrib::O_one);
setup_color_blend(_c->zb->blendb, ColorBlendAttrib::O_one_minus_incoming_alpha, ColorBlendAttrib::O_one_minus_incoming_alpha);
int alpha_mode = (color_channels & ColorWriteAttrib::C_alpha) ? 0 : 3;
_c->zb->store_pix_func = store_pixel_funcs[0][alpha_mode][rgb_channels][srgb_blend];
color_write_state = 2; // cgeneral
}
break;
case TransparencyAttrib::M_premultiplied_alpha:
{
// Implement a color mask, with pre-multiplied alpha blending.
setup_color_blend(_c->zb->blenda, ColorBlendAttrib::O_one, ColorBlendAttrib::O_one);
setup_color_blend(_c->zb->blendb, ColorBlendAttrib::O_one_minus_incoming_alpha, ColorBlendAttrib::O_one_minus_incoming_alpha);
int alpha_mode = (color_channels & ColorWriteAttrib::C_alpha) ? 0 : 3;
_c->zb->store_pix_func = store_pixel_funcs[0][alpha_mode][rgb_channels][srgb_blend];
color_write_state = 2; // cgeneral
}
break;
default:
if (color_channels == ColorWriteAttrib::C_all) {
if (srgb_blend) {
color_write_state = 4; // csstore
} else {
color_write_state = 0; // cstore
}
} else {
// Implement a color mask.
setup_color_blend(_c->zb->blenda, ColorBlendAttrib::O_one, ColorBlendAttrib::O_one);
setup_color_blend(_c->zb->blendb, ColorBlendAttrib::O_zero, ColorBlendAttrib::O_zero);
int alpha_mode = (color_channels & ColorWriteAttrib::C_alpha) ? 0 : 3;
_c->zb->store_pix_func = store_pixel_funcs[0][alpha_mode][rgb_channels][srgb_blend];
color_write_state = 2; // cgeneral
}
break;
}
}
int alpha_test_state = 0; // anone
const AlphaTestAttrib *target_alpha_test = DCAST(AlphaTestAttrib, _target_rs->get_attrib_def(AlphaTestAttrib::get_class_slot()));
@ -3098,66 +3102,141 @@ load_matrix(M4 *matrix, const TransformState *transform) {
}
/**
* Returns the integer element of store_pixel_funcs (as defined by
* store_pixel.py) that corresponds to the indicated ColorBlendAttrib operand
* code.
* Returns the integer index into the array of store_pixel functions
* corresponding to the given blend mode.
*/
int TinyGraphicsStateGuardian::
get_color_blend_mode(ColorBlendAttrib::Mode mode) {
switch (mode) {
case ColorBlendAttrib::M_none:
case ColorBlendAttrib::M_add:
case ColorBlendAttrib::M_subtract:
case ColorBlendAttrib::M_inv_subtract:
return 0;
case ColorBlendAttrib::M_min:
return 1;
case ColorBlendAttrib::M_max:
return 2;
}
return 0;
}
/**
* Returns the integer index into the array of operands computed by store_pixel
* that corresponds to the indicated ColorBlendAttrib operand code.
*/
int TinyGraphicsStateGuardian::
get_color_blend_op(ColorBlendAttrib::Operand operand) {
switch (operand) {
case ColorBlendAttrib::O_zero:
return 0;
case ColorBlendAttrib::O_one:
return 1;
case ColorBlendAttrib::O_incoming_color:
return 2;
case ColorBlendAttrib::O_one_minus_incoming_color:
return 3;
case ColorBlendAttrib::O_fbuffer_color:
return 4;
case ColorBlendAttrib::O_one_minus_fbuffer_color:
return 5;
case ColorBlendAttrib::O_incoming_alpha:
return 6;
case ColorBlendAttrib::O_one_minus_incoming_alpha:
return 7;
case ColorBlendAttrib::O_fbuffer_alpha:
return 8;
case ColorBlendAttrib::O_one_minus_fbuffer_alpha:
return 9;
case ColorBlendAttrib::O_constant_color:
return 10;
case ColorBlendAttrib::O_one_minus_constant_color:
return 11;
case ColorBlendAttrib::O_constant_alpha:
return 12;
case ColorBlendAttrib::O_one_minus_constant_alpha:
return 13;
case ColorBlendAttrib::O_incoming_color_saturate:
return 1;
case ColorBlendAttrib::O_incoming1_color:
return 1;
case ColorBlendAttrib::O_one_minus_incoming1_color:
return 0;
case ColorBlendAttrib::O_incoming1_alpha:
return 1;
case ColorBlendAttrib::O_one_minus_incoming1_alpha:
return 0;
case ColorBlendAttrib::O_color_scale:
return 10;
case ColorBlendAttrib::O_one_minus_color_scale:
return 11;
case ColorBlendAttrib::O_alpha_scale:
return 12;
case ColorBlendAttrib::O_one_minus_alpha_scale:
return 13;
return 0;
case ColorBlendAttrib::O_incoming_color:
case ColorBlendAttrib::O_one_minus_incoming_color:
case ColorBlendAttrib::O_incoming_color_saturate:
case ColorBlendAttrib::O_incoming1_color:
case ColorBlendAttrib::O_one_minus_incoming1_color:
case ColorBlendAttrib::O_incoming1_alpha:
case ColorBlendAttrib::O_one_minus_incoming1_alpha:
return 1;
case ColorBlendAttrib::O_fbuffer_color:
case ColorBlendAttrib::O_one_minus_fbuffer_color:
return 2;
case ColorBlendAttrib::O_incoming_alpha:
case ColorBlendAttrib::O_one_minus_incoming_alpha:
return 3;
case ColorBlendAttrib::O_fbuffer_alpha:
case ColorBlendAttrib::O_one_minus_fbuffer_alpha:
return 4;
}
return 0;
}
/**
* Returns true if the color blend operand should be inverted.
*/
bool TinyGraphicsStateGuardian::
is_color_blend_op_inverted(ColorBlendAttrib::Operand operand) {
switch (operand) {
case ColorBlendAttrib::O_zero:
case ColorBlendAttrib::O_constant_color:
case ColorBlendAttrib::O_constant_alpha:
case ColorBlendAttrib::O_color_scale:
case ColorBlendAttrib::O_alpha_scale:
case ColorBlendAttrib::O_incoming_color:
case ColorBlendAttrib::O_incoming_color_saturate:
case ColorBlendAttrib::O_incoming1_color:
case ColorBlendAttrib::O_incoming1_alpha:
case ColorBlendAttrib::O_fbuffer_color:
case ColorBlendAttrib::O_incoming_alpha:
case ColorBlendAttrib::O_fbuffer_alpha:
return false;
case ColorBlendAttrib::O_one:
case ColorBlendAttrib::O_one_minus_constant_color:
case ColorBlendAttrib::O_one_minus_constant_alpha:
case ColorBlendAttrib::O_one_minus_color_scale:
case ColorBlendAttrib::O_one_minus_alpha_scale:
case ColorBlendAttrib::O_one_minus_incoming_color:
case ColorBlendAttrib::O_one_minus_incoming1_color:
case ColorBlendAttrib::O_one_minus_incoming1_alpha:
case ColorBlendAttrib::O_one_minus_fbuffer_color:
case ColorBlendAttrib::O_one_minus_incoming_alpha:
case ColorBlendAttrib::O_one_minus_fbuffer_alpha:
return true;
}
return false;
}
/**
* Sets up the color blending state for the given blend operand.
*/
void TinyGraphicsStateGuardian::
setup_color_blend(ZBlend &blend, ColorBlendAttrib::Operand op_rgb,
ColorBlendAttrib::Operand op_alpha,
int cr, int cg, int cb, int ca) {
blend.op_rgb = get_color_blend_op(op_rgb);
blend.op_alpha = get_color_blend_op(op_alpha);
// If it's inverted (one minus), we xor the value with 0xffff
unsigned int mask_rgb = is_color_blend_op_inverted(op_rgb) ? 0xffff : 0x0;
unsigned int mask_alpha = is_color_blend_op_inverted(op_alpha) ? 0xffff : 0x0;
blend.xor_r = mask_rgb;
blend.xor_g = mask_rgb;
blend.xor_b = mask_rgb;
blend.xor_a = mask_alpha;
// A constant factor is implemented by doing effectively O_zero ^ constant,
// so we can conveniently reuse the xor mask field to store it!
if (op_rgb == ColorBlendAttrib::O_constant_color ||
op_rgb == ColorBlendAttrib::O_one_minus_constant_color) {
blend.xor_r ^= cr;
blend.xor_g ^= cg;
blend.xor_b ^= cb;
}
if (op_rgb == ColorBlendAttrib::O_constant_alpha ||
op_rgb == ColorBlendAttrib::O_one_minus_constant_alpha) {
blend.xor_r ^= ca;
blend.xor_g ^= ca;
blend.xor_b ^= ca;
}
if (op_alpha == ColorBlendAttrib::O_constant_color ||
op_alpha == ColorBlendAttrib::O_constant_alpha ||
op_alpha == ColorBlendAttrib::O_one_minus_constant_color ||
op_alpha == ColorBlendAttrib::O_one_minus_constant_alpha) {
blend.xor_a ^= ca;
}
}
/**
* Returns the pointer to the appropriate filter function according to the
* texture's filter type.

View File

@ -125,7 +125,13 @@ private:
void setup_material(GLMaterial *gl_material, const Material *material);
void do_auto_rescale_normal();
static void load_matrix(M4 *matrix, const TransformState *transform);
static int get_color_blend_mode(ColorBlendAttrib::Mode mode);
static int get_color_blend_op(ColorBlendAttrib::Operand operand);
static bool is_color_blend_op_inverted(ColorBlendAttrib::Operand operand);
static void setup_color_blend(ZBlend &blend,
ColorBlendAttrib::Operand op_rgb,
ColorBlendAttrib::Operand op_alpha,
int cr=0, int cg=0, int cb=0, int ca=0);
static ZB_lookupTextureFunc get_tex_filter_func(SamplerState::FilterType filter);
static ZB_texWrapFunc get_tex_wrap_func(SamplerState::WrapMode wrap_mode);

View File

@ -158,6 +158,15 @@ struct ZTextureDef {
PIXEL border_color;
};
struct ZBlend {
int op_rgb;
int op_alpha;
unsigned int xor_r;
unsigned int xor_g;
unsigned int xor_b;
unsigned int xor_a;
};
struct ZBuffer {
int xsize,ysize;
int linesize; /* line size, in bytes */
@ -172,7 +181,8 @@ struct ZBuffer {
int *ctable;
ZTextureDef current_textures[MAX_TEXTURE_STAGES];
int reference_alpha;
int blend_r, blend_g, blend_b, blend_a;
ZBlend blenda;
ZBlend blendb;
ZB_storePixelFunc store_pix_func;
};