Re: [AD] memory bitmap blending

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


On Wed, 2008-12-10 at 18:07 +0100, Elias Pschernig wrote:
> 
> It shouldn't be hard to implement - just add two more blender variables
> to the state for alpha, replace glBlendFunc with glBlendFuncSeparate for
> the OpenGL implementation, do something analogous for DirectX, and
> update the memory blenders.
> 

Indeed, it was very simple to implement for OpenGL and (un-optimized)
memory. For DirectX it likely is only a small change as well (unless
it's really not supported there). Optimizing memory blending again might
be a somewhat bigger task (ex_blend drops from 21 FPS with current SVN
to 18 FPS after applying the patch for me).

In any case, the patch includes an updated ex_blend2 which shows what
can be done with this which can't be done currently. The additional
blending modes are for the alpha channel - I couldn't find space where
to put "color" and "alpha" labels to make it clear - so just have to
know the four lists with ONE/ZERO/ALPHA/INVERSE are source-color,
source-alpha, dest-color, dest-alpha.

I'd obviously like to have this feature in now after making the patch,
even though I'll always use OpenGL directly myself. Only drawback really
is that it gets a bit complicated, but now that I finally understand the
blending formulas myself it actually all makes sense.

One interesting point is that OpenGL and memory versions still sometimes
differ slightly - my guess so far is some kind of internal rounding, or
maybe OpenGL also clamps intermediate values to [0;1] and not just the
end result, or something similar. Would have to check some actual
numeric examples to see what's happening.

If DirectX doesn't support separate alpha, we could fall back to memory
bending for the separate alpha modes there and add a way for users to
query if accelerated separate-alpha blending is supported. I forgot if
we already do this for something else, if not, then if DX doesn't
support it it's probably an argument to not apply it.

-- 
Elias Pschernig <elias@xxxxxxxxxx>
Index: src/blenders.c
===================================================================
--- src/blenders.c	(revision 11291)
+++ src/blenders.c	(working copy)
@@ -20,7 +20,7 @@
 #include <string.h>
 
 
-
+#if 0
 void _al_blender_zero_zero(ALLEGRO_COLOR *scol,
    ALLEGRO_COLOR *dcol, ALLEGRO_COLOR *result)
 {
@@ -238,14 +238,57 @@
 }
 
 
-void _al_blend(ALLEGRO_COLOR *scol, ALLEGRO_BITMAP *dest,
+// FIXME
+void _al_blend_combined_optimized(ALLEGRO_COLOR *scol,
+   ALLEGRO_BITMAP *dest,
    int dx, int dy, ALLEGRO_COLOR *result)
 {
    ALLEGRO_MEMORY_BLENDER blender = _al_get_memory_blender();
    ALLEGRO_COLOR dcol;
 
+   // FIXME: some blenders won't need this and it may be slow, should
+   // optimize it away for those
    dcol = al_get_pixel(dest, dx, dy);
 
    blender(scol, &dcol, result);
 }
+#endif
 
+
+static float get_factor(int operation, float alpha)
+{
+   switch(operation) {
+       case ALLEGRO_ZERO: return 0;
+       case ALLEGRO_ONE: return 1;
+       case ALLEGRO_ALPHA: return alpha;
+       case ALLEGRO_INVERSE_ALPHA: return 1 - alpha;
+   }
+   ASSERT(false);
+   return 0; /* silence warning in release build */
+}
+
+
+// FIXME: un-optimized reference implementation
+void _al_blend(ALLEGRO_COLOR *scol,
+   ALLEGRO_BITMAP *dest,
+   int dx, int dy, ALLEGRO_COLOR *result)
+{
+   float src, dst, asrc, adst;
+   int src_, dst_, asrc_, adst_;
+   ALLEGRO_COLOR dcol;
+   ALLEGRO_COLOR bc;
+   dcol = al_get_pixel(dest, dx, dy);
+   al_get_separate_blender(&src_, &dst_, &asrc_, &adst_, &bc);
+   result->r = scol->r * bc.r;
+   result->g = scol->g * bc.g;
+   result->b = scol->b * bc.b;
+   result->a = scol->a * bc.a;
+   src = get_factor(src_, result->a);
+   dst = get_factor(dst_, result->a);
+   asrc = get_factor(asrc_, result->a);
+   adst = get_factor(adst_, result->a);
+   result->r = MIN(1, result->r * src + dcol.r * dst);
+   result->g = MIN(1, result->g * src + dcol.g * dst);
+   result->b = MIN(1, result->b * src + dcol.b * dst);
+   result->a = MIN(1, result->a * asrc + dcol.a * adst);
+}
Index: src/tls.c
===================================================================
--- src/tls.c	(revision 11292)
+++ src/tls.c	(working copy)
@@ -48,8 +48,11 @@
    /* Blending modes and color */
    int blend_source;
    int blend_dest;
+   int blend_alpha_source;
+   int blend_alpha_dest;
    ALLEGRO_COLOR blend_color;
-   ALLEGRO_MEMORY_BLENDER memory_blender;
+   //FIXME: Might need this again for optimization purposes.
+   //ALLEGRO_MEMORY_BLENDER memory_blender;
    /* Error code */
    int allegro_errno;
 } thread_local_state;
@@ -100,9 +103,11 @@
              // data->new_bitmap_format = ALLEGRO_PIXEL_FORMAT_RGB_888;
              data->blend_source = ALLEGRO_ALPHA;
              data->blend_dest = ALLEGRO_INVERSE_ALPHA;
+             data->blend_alpha_source = ALLEGRO_ONE;
+             data->blend_alpha_dest = ALLEGRO_ONE;
              data->blend_color.r = data->blend_color.g = data->blend_color.b
                 = data->blend_color.a = 1.0f;
-             data->memory_blender = _al_blender_alpha_inverse_alpha;
+             //data->memory_blender = _al_blender_alpha_inverse_alpha;
              TlsSetValue(tls_index, data); 
           }
              break; 
@@ -199,8 +204,10 @@
    0,                                     /* new_bitmap_flags */
    ALLEGRO_ALPHA,                         /* blend_source */
    ALLEGRO_INVERSE_ALPHA,                 /* blend_dest */
+   ALLEGRO_ONE,                           /* blend_alpha_source */
+   ALLEGRO_ONE,                           /* blend_alpha_dest */
    { 1.0f, 1.0f, 1.0f, 1.0f },            /* blend_color  */
-   _al_blender_alpha_inverse_alpha,       /* memory_blender */
+   //_al_blender_alpha_inverse_alpha,       /* memory_blender */
    0                                      /* errno */
 };
 
@@ -546,8 +553,10 @@
    if (flags & ALLEGRO_STATE_BLENDER) {
       _STORE(blend_source);
       _STORE(blend_dest);
+      _STORE(blend_alpha_source);
+      _STORE(blend_alpha_dest);
       _STORE(blend_color);
-      _STORE(memory_blender);
+      //_STORE(memory_blender);
    }
 };
 #undef _STORE
@@ -596,8 +605,10 @@
    if (flags & ALLEGRO_STATE_BLENDER) {
       _STORE(blend_source);
       _STORE(blend_dest);
+      _STORE(blend_alpha_source);
+      _STORE(blend_alpha_dest);
       _STORE(blend_color);
-      _STORE(memory_blender);
+      //_STORE(memory_blender);
    }
 };
 #undef _STORE
@@ -654,6 +665,19 @@
  */
 void al_set_blender(int src, int dst, ALLEGRO_COLOR color)
 {
+   al_set_separate_blender(src, dst, src, dst, color);
+}
+
+
+
+/* Function: al_set_separate_blender
+ * 
+ * Like [al_set_blender], but allows specifying a separate blending
+ * operation for the alpha channel.
+ */
+void al_set_separate_blender(int src, int dst, int alpha_src,
+   int alpha_dst, ALLEGRO_COLOR color)
+{
    thread_local_state *tls;
 
    if ((tls = tls_get()) == NULL)
@@ -661,20 +685,36 @@
 
    tls->blend_source = src;
    tls->blend_dest = dst;
+   tls->blend_alpha_source = alpha_src;
+   tls->blend_alpha_dest = alpha_dst;
 
    memcpy(&tls->blend_color, &color, sizeof(ALLEGRO_COLOR));
 
-   _al_set_memory_blender(src, dst, &color);
+   //_al_set_memory_blender(src, dst, alpha_src, alpha_dst, &color);
 }
 
 
 
 /* Function: al_get_blender
  *
- * Returns the active blender for the current thread.
+ * Returns the active blender for the current thread. You can pass
+ * NULL for values you are not interested in.
  */
 void al_get_blender(int *src, int *dst, ALLEGRO_COLOR *color)
 {
+   al_get_separate_blender(src, dst, NULL, NULL, color);
+}
+
+
+
+/* Function: al_get_separate_blender
+ *
+ * Returns the active blender for the current thread. You can pass
+ * NULL for values you are not interested in.
+ */
+void al_get_separate_blender(int *src, int *dst, int *alpha_src,
+   int *alpha_dst, ALLEGRO_COLOR *color)
+{
    thread_local_state *tls;
 
    if ((tls = tls_get()) == NULL)
@@ -686,6 +726,12 @@
    if (dst)
       *dst = tls->blend_dest;
 
+   if (alpha_src)
+      *alpha_src = tls->blend_alpha_source;
+
+   if (alpha_dst)
+      *alpha_dst = tls->blend_alpha_dest;
+
    if (color)
       memcpy(color, &tls->blend_color, sizeof(ALLEGRO_COLOR));
 }
@@ -703,6 +749,8 @@
 
 
 
+// FIXME: Do we need this for optimization?
+#if 0
 void _al_set_memory_blender(int src, int dst, ALLEGRO_COLOR *color)
 {
    thread_local_state *tls;
@@ -792,9 +840,9 @@
       return NULL;
    return tls->memory_blender;
 }
+#endif
 
 
-
 /* Function: al_get_errno
  *  Some Allegro functions will set an error number as well as returning an
  *  error code.  Call this function to retrieve the last error number set
Index: src/opengl/ogl_draw.c
===================================================================
--- src/opengl/ogl_draw.c	(revision 11291)
+++ src/opengl/ogl_draw.c	(working copy)
@@ -26,14 +26,16 @@
       GL_ZERO, GL_ONE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
    };
    ALLEGRO_COLOR *bc;
-   int src_mode, dst_mode;
+   int src_color, dst_color, src_alpha, dst_alpha;
    float r, g, b, a;
 
    al_unmap_rgba_f(*color, &r, &g, &b, &a);
 
-   al_get_blender(&src_mode, &dst_mode, NULL);
+   al_get_separate_blender(&src_color, &dst_color, &src_alpha,
+      &dst_alpha, NULL);
    glEnable(GL_BLEND);
-   glBlendFunc(blend_modes[src_mode], blend_modes[dst_mode]);
+   glBlendFuncSeparate(blend_modes[src_color], blend_modes[dst_color],
+      blend_modes[src_alpha], blend_modes[dst_alpha]);
    bc = _al_get_blend_color();
    glColor4f(r * bc->r, g * bc->g, b * bc->b, a * bc->a);
 }
Index: src/opengl/ogl_bitmap.c
===================================================================
--- src/opengl/ogl_bitmap.c	(revision 11291)
+++ src/opengl/ogl_bitmap.c	(working copy)
@@ -68,14 +68,16 @@
    GLboolean on;
    GLuint current_texture;
    ALLEGRO_COLOR *bc;
-   int src_mode, dst_mode;
+   int src_color, dst_color, src_alpha, dst_alpha;
    int blend_modes[4] = {
       GL_ZERO, GL_ONE, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA
    };
 
-   al_get_blender(&src_mode, &dst_mode, NULL);
+   al_get_separate_blender(&src_color, &dst_color, &src_alpha,
+      &dst_alpha, NULL);
    glEnable(GL_BLEND);
-   glBlendFunc(blend_modes[src_mode], blend_modes[dst_mode]);
+   glBlendFuncSeparate(blend_modes[src_color], blend_modes[dst_color],
+      blend_modes[src_alpha], blend_modes[dst_alpha]);
 
    glGetBooleanv(GL_TEXTURE_2D, &on);
    if (!on) {
Index: include/allegro5/bitmap_new.h
===================================================================
--- include/allegro5/bitmap_new.h	(revision 11291)
+++ include/allegro5/bitmap_new.h	(working copy)
@@ -240,6 +240,8 @@
 /* Blending */
 AL_FUNC(void, al_set_blender, (int source, int dest, ALLEGRO_COLOR color));
 AL_FUNC(void, al_get_blender, (int *source, int *dest, ALLEGRO_COLOR *color));
+AL_FUNC(void, al_set_separate_blender, (int source, int dest, int alpha_source, int alpha_dest, ALLEGRO_COLOR color));
+AL_FUNC(void, al_get_separate_blender, (int *source, int *dest, int *alpha_src, int *alpha_dest, ALLEGRO_COLOR *color));
 
 #ifdef __cplusplus
    }
Index: include/allegro5/internal/aintern_bitmap.h
===================================================================
--- include/allegro5/internal/aintern_bitmap.h	(revision 11291)
+++ include/allegro5/internal/aintern_bitmap.h	(working copy)
@@ -154,9 +154,10 @@
 
 /* Blending stuff (should this be somewhere else? */
 ALLEGRO_COLOR *_al_get_blend_color(void);
-void _al_set_memory_blender(int src, int dst, ALLEGRO_COLOR *color);
+void _al_blend(ALLEGRO_COLOR *src_color, ALLEGRO_BITMAP *dest, int dx, int dy, ALLEGRO_COLOR *result);
+# if 0
+void _al_set_memory_blender(int src, int dst, int asrc, int adst, ALLEGRO_COLOR *color);
 ALLEGRO_MEMORY_BLENDER _al_get_memory_blender(void);
-void _al_blend(ALLEGRO_COLOR *src_color, ALLEGRO_BITMAP *dest, int dx, int dy, ALLEGRO_COLOR *result);
 void _al_blender_zero_zero(ALLEGRO_COLOR *src_color, ALLEGRO_COLOR *dst_color, ALLEGRO_COLOR *result);
 void _al_blender_zero_one(ALLEGRO_COLOR *src_color, ALLEGRO_COLOR *dst_color, ALLEGRO_COLOR *result);
 void _al_blender_zero_alpha(ALLEGRO_COLOR *src_color, ALLEGRO_COLOR *dst_color, ALLEGRO_COLOR *result);
@@ -173,6 +174,7 @@
 void _al_blender_inverse_alpha_one(ALLEGRO_COLOR *src_color, ALLEGRO_COLOR *dst_color, ALLEGRO_COLOR *result);
 void _al_blender_inverse_alpha_alpha(ALLEGRO_COLOR *src_color, ALLEGRO_COLOR *dst_color, ALLEGRO_COLOR *result);
 void _al_blender_inverse_alpha_inverse_alpha(ALLEGRO_COLOR *src_color, ALLEGRO_COLOR *dst_color, ALLEGRO_COLOR *result);
+#endif
 
 #ifdef __cplusplus
 }
Index: examples/ex_blend2.cpp
===================================================================
--- examples/ex_blend2.cpp	(revision 11291)
+++ examples/ex_blend2.cpp	(working copy)
@@ -28,8 +28,7 @@
    Label blending_label;
    List source_image;
    List destination_image;
-   List slst;
-   List dlst;
+   List operations[4];
    VSlider r[3];
    VSlider g[3];
    VSlider b[3];
@@ -69,18 +68,15 @@
       d.add(image, 1 + i * 6, 11, 4, 2);
    }
 
-   slst.append_item("ONE");
-   slst.append_item("ZERO");
-   slst.append_item("ALPHA");
-   slst.append_item("INVERSE_ALPHA");
-   d.add(slst, 1, 13, 4, 3);
+   for (int i = 0; i < 4; i++) {
+      List &l = operations[i];
+      l.append_item("ONE");
+      l.append_item("ZERO");
+      l.append_item("ALPHA");
+      l.append_item("INVERSE");
+      d.add(l, 1 + i * 3, 13, 3, 3);
+   }
 
-   dlst.append_item("ONE");
-   dlst.append_item("ZERO");
-   dlst.append_item("ALPHA");
-   dlst.append_item("INVERSE_ALPHA");
-   d.add(dlst, 7, 13, 4, 3);
-
    for (int i = 0; i < 3; i++) {
       r[i] = VSlider(255, 255);
       g[i] = VSlider(255, 255);
@@ -117,7 +113,7 @@
       return ALLEGRO_ONE;
    if (str == "ALPHA")
       return ALLEGRO_ALPHA;
-   if (str == "INVERSE_ALPHA")
+   if (str == "INVERSE")
       return ALLEGRO_INVERSE_ALPHA;
 
    ASSERT(false);
@@ -161,8 +157,10 @@
 void Prog::blending_test(bool memory)
 {
    ALLEGRO_COLOR opaque_white = al_map_rgba_f(1, 1, 1, 1);
-   int src = str_to_blend_mode(slst.get_selected_item_text());
-   int dst = str_to_blend_mode(dlst.get_selected_item_text());
+   int src = str_to_blend_mode(operations[0].get_selected_item_text());
+   int asrc = str_to_blend_mode(operations[1].get_selected_item_text());
+   int dst = str_to_blend_mode(operations[2].get_selected_item_text());
+   int adst = str_to_blend_mode(operations[3].get_selected_item_text());
    int rv = r[2].get_cur_value();
    int gv = g[2].get_cur_value();
    int bv = b[2].get_cur_value();
@@ -174,7 +172,8 @@
    draw_bitmap(destination_image.get_selected_item_text(), memory, true);
 
    /* Now draw the blended source over it. */
-   al_set_blender(src, dst, al_map_rgba(rv, gv, bv, av));
+   al_set_separate_blender(src, dst, asrc, adst,
+      al_map_rgba(rv, gv, bv, av));
    draw_bitmap(source_image.get_selected_item_text(), memory, false);
 }
 


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/