/* blend.c */

/*
 * Mesa 3-D graphics library
 * Version:  1.2
 * Copyright (C) 1995  Brian Paul  (brianp@ssec.wisc.edu)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


/*
 * Pixel blending.
 */


/*
$Id: blend.c,v 1.15 1995/12/19 17:05:37 brianp Exp $

$Log: blend.c,v $
 * Revision 1.15  1995/12/19  17:05:37  brianp
 * added gl_save_blendcolor() and gl_save_blendequation() calls
 *
 * Revision 1.14  1995/10/30  15:30:33  brianp
 * pass mask array to DD.read_color_pixels
 *
 * Revision 1.13  1995/08/31  21:30:26  brianp
 * renamed blend() as dd_blend()
 * use *DD.read_color_pixels instead of dd_read_color_pixels
 * introduced CC.NewState convention
 *
 * Revision 1.12  1995/06/12  15:34:31  brianp
 * changed color arrays to GLubyte
 *
 * Revision 1.11  1995/05/31  14:57:10  brianp
 * call gl_read_color_span() instead of dd_read_color_span()
 *
 * Revision 1.10  1995/05/22  21:02:41  brianp
 * Release 1.2
 *
 * Revision 1.9  1995/05/12  19:20:19  brianp
 * replaced CC.Mode!=0 with INSIDE_BEGIN_END
 *
 * Revision 1.8  1995/03/30  21:09:04  brianp
 * fixed bugs in blend scale factors
 *
 * Revision 1.7  1995/03/24  20:03:44  brianp
 * added missing cases to glBlendEquationEXT
 *
 * Revision 1.6  1995/03/24  16:59:17  brianp
 * fixed blending/logicop bug
 *
 * Revision 1.5  1995/03/13  20:55:02  brianp
 * introduced blend function
 * added rest of logic ops
 * optimized common cases
 *
 * Revision 1.4  1995/03/10  16:45:52  brianp
 * updated for new blending extensions
 *
 * Revision 1.3  1995/03/07  18:58:42  brianp
 * added gl_blend_pixels()
 *
 * Revision 1.2  1995/03/04  19:29:44  brianp
 * 1.1 beta revision
 *
 * Revision 1.1  1995/02/24  14:17:11  brianp
 * Initial revision
 *
 */


#ifdef DEBUG
#  include <assert.h>
#endif
#include "context.h"
#include "dd.h"
#include "list.h"
#include "macros.h"
#include "pb.h"
#include "span.h"



void glBlendFunc( GLenum sfactor, GLenum dfactor )
{
   if (CC.CompileFlag) {
      gl_save_blendfunc( sfactor, dfactor );
   }
   if (CC.ExecuteFlag) {
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glBlendFunction" );
	 return;
      }

      switch (sfactor) {
	 case GL_ZERO:
	 case GL_ONE:
	 case GL_DST_COLOR:
	 case GL_ONE_MINUS_DST_COLOR:
	 case GL_SRC_ALPHA:
	 case GL_ONE_MINUS_SRC_ALPHA:
	 case GL_DST_ALPHA:
	 case GL_ONE_MINUS_DST_ALPHA:
	 case GL_SRC_ALPHA_SATURATE:
	 case GL_CONSTANT_COLOR_EXT:
	 case GL_ONE_MINUS_CONSTANT_COLOR_EXT:
	 case GL_CONSTANT_ALPHA_EXT:
	 case GL_ONE_MINUS_CONSTANT_ALPHA_EXT:
	    CC.Color.BlendSrc = sfactor;
	    break;
	 default:
	    gl_error( GL_INVALID_ENUM, "glBlendFunction" );
	    return;
      }

      switch (dfactor) {
	 case GL_ZERO:
	 case GL_ONE:
	 case GL_SRC_COLOR:
	 case GL_ONE_MINUS_SRC_COLOR:
	 case GL_SRC_ALPHA:
	 case GL_ONE_MINUS_SRC_ALPHA:
	 case GL_DST_ALPHA:
	 case GL_ONE_MINUS_DST_ALPHA:
	 case GL_CONSTANT_COLOR_EXT:
	 case GL_ONE_MINUS_CONSTANT_COLOR_EXT:
	 case GL_CONSTANT_ALPHA_EXT:
	 case GL_ONE_MINUS_CONSTANT_ALPHA_EXT:
	    CC.Color.BlendDst = dfactor;
	    break;
	 default:
	    gl_error( GL_INVALID_ENUM, "glBlendFunction" );
      }

      CC.NewState = GL_TRUE;
   }
}



/*
 * This is not a standard OpenGL function.  It is an extension!
 */
void glBlendEquationEXT( GLenum mode )
{
   if (CC.CompileFlag) {
      gl_save_blendequation( mode );
   }
   if (CC.ExecuteFlag) {
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glBlendEquation" );
	 return;
      }

      switch (mode) {
	 case GL_MIN_EXT:
	 case GL_MAX_EXT:
	 case GL_LOGIC_OP:
	 case GL_FUNC_ADD_EXT:
	 case GL_FUNC_SUBTRACT_EXT:
	 case GL_FUNC_REVERSE_SUBTRACT_EXT:
	    CC.Color.BlendEquation = mode;
	    break;
	default:
	    gl_error( GL_INVALID_ENUM, "glBlendEquation" );
      }
      CC.NewState = GL_TRUE;
   }
}



/*
 * This is not a standard OpenGL function.  It is an extension!
 */
void glBlendColorEXT( GLclampf red, GLclampf green,
		      GLclampf blue, GLclampf alpha )
{
   if (CC.CompileFlag) {
      gl_save_blendcolor( red, green, blue, alpha );
   }
   if (CC.ExecuteFlag) {
      CC.Color.BlendColor[0] = CLAMP( red,   0.0, 1.0 );
      CC.Color.BlendColor[1] = CLAMP( green, 0.0, 1.0 );
      CC.Color.BlendColor[2] = CLAMP( blue,  0.0, 1.0 );
      CC.Color.BlendColor[3] = CLAMP( alpha, 0.0, 1.0 );
   }
}



/*
 * Do the real work of gl_blend_span() and gl_blend_pixels().
 * Input:  n - number of pixels
 *         mask - the usual write mask
 * In/Out:  red, green, blue, alpha - the incoming and modified pixels
 * Input:  rdest, gdest, bdest, adest - the pixels from the dest color buffer
 */
static void do_blend( GLuint n, const GLubyte mask[],
                      GLubyte red[], GLubyte green[],
                      GLubyte blue[], GLubyte alpha[],
                      const GLubyte rdest[], const GLubyte gdest[],
                      const GLubyte bdest[], const GLubyte adest[] )
{
   GLfloat rscale, gscale, bscale, ascale;
   GLuint i;


   rscale = 1.0F / CC.RedScale;
   gscale = 1.0F / CC.GreenScale;
   bscale = 1.0F / CC.BlueScale;
   ascale = 1.0F / CC.AlphaScale;

   /* Common cases: */

   if (CC.Color.BlendEquation==GL_FUNC_ADD_EXT
       && CC.Color.BlendSrc==GL_SRC_ALPHA
       && CC.Color.BlendDst==GL_ONE_MINUS_SRC_ALPHA) {
      /* Alpha blending */
      for (i=0;i<n;i++) {
	 if (mask[i]) {
	    GLfloat r, g, b, a;
	    GLfloat t = (GLfloat) alpha[i] * ascale;
	    r = red[i]   * t + rdest[i] * (1.0F-t);
	    g = green[i] * t + gdest[i] * (1.0F-t);
	    b = blue[i]  * t + bdest[i] * (1.0F-t);
	    a = alpha[i] * t + adest[i] * (1.0F-t);
	    red[i]   = (GLint) MIN2( r, CC.RedScale );
	    green[i] = (GLint) MIN2( g, CC.GreenScale );
	    blue[i]  = (GLint) MIN2( b, CC.BlueScale );
	    alpha[i] = (GLint) MIN2( a, CC.AlphaScale );
	 }
      }
   }
   else if (CC.Color.BlendEquation==GL_LOGIC_OP
	    && CC.Color.LogicOp==GL_XOR) {
      /* XOR drawing */
      for (i=0;i<n;i++) {
	 if (mask[i]) {
	    red[i]   ^= rdest[i];
	    green[i] ^= gdest[i];
	    blue[i]  ^= bdest[i];
	    alpha[i] ^= adest[i];
	 }
      }
   }

   /* General cases: */

   else if (CC.Color.BlendEquation==GL_FUNC_ADD_EXT
         || CC.Color.BlendEquation==GL_FUNC_SUBTRACT_EXT
         || CC.Color.BlendEquation==GL_FUNC_REVERSE_SUBTRACT_EXT) {
      for (i=0;i<n;i++) {
	 if (mask[i]) {
	    GLint Rs, Gs, Bs, As;  /* Source colors */
	    GLint Rd, Gd, Bd, Ad;  /* Dest colors */
	    GLfloat sR, sG, sB, sA;  /* Source scaling */
	    GLfloat dR, dG, dB, dA;  /* Dest scaling */
	    GLfloat r, g, b, a;

	    /* Source Color */
	    Rs = red[i];
	    Gs = green[i];
	    Bs = blue[i];
	    As = alpha[i];

	    /* Frame buffer color */
	    Rd = rdest[i];
	    Gd = gdest[i];
	    Bd = bdest[i];
	    Ad = adest[i];

	    /* Source scaling */
	    switch (CC.Color.BlendSrc) {
	       case GL_ZERO:
		  sR = sG = sB = sA = 0.0F;
		  break;
	       case GL_ONE:
		  sR = sG = sB = sA = 1.0F;
		  break;
	       case GL_DST_COLOR:
		  sR = (GLfloat) Rd * rscale;
		  sG = (GLfloat) Gd * gscale;
		  sB = (GLfloat) Bd * bscale;
		  sA = (GLfloat) Ad * ascale;
		  break;
	       case GL_ONE_MINUS_DST_COLOR:
		  sR = 1.0F - (GLfloat) Rd * rscale;
		  sG = 1.0F - (GLfloat) Gd * gscale;
		  sB = 1.0F - (GLfloat) Bd * bscale;
		  sA = 1.0F - (GLfloat) Ad * ascale;
		  break;
	       case GL_SRC_ALPHA:
		  sR = sG = sB = sA = (GLfloat) As * ascale;
		  break;
	       case GL_ONE_MINUS_SRC_ALPHA:
		  sR = sG = sB = sA = (GLfloat) 1.0F - (GLfloat) As * ascale;
		  break;
	       case GL_DST_ALPHA:
		  sR = sG = sB = sA =(GLfloat) Ad * ascale;
		  break;
	       case GL_ONE_MINUS_DST_ALPHA:
		  sR = sG = sB = sA = 1.0F - (GLfloat) Ad * ascale;
		  break;
	       case GL_SRC_ALPHA_SATURATE:
		  if (As < 1.0F - (GLfloat) Ad * ascale) {
		     sR = sG = sB = (GLfloat) As * ascale;
		  }
		  else {
		     sR = sG = sB = 1.0F - (GLfloat) Ad * ascale;
		  }
		  sA = 1.0;
		  break;
	       case GL_CONSTANT_COLOR_EXT:
		  sR = CC.Color.BlendColor[0];
		  sG = CC.Color.BlendColor[1];
		  sB = CC.Color.BlendColor[2];
		  sA = CC.Color.BlendColor[3];
		  break;
	       case GL_ONE_MINUS_CONSTANT_COLOR_EXT:
		  sR = 1.0F - CC.Color.BlendColor[0];
		  sG = 1.0F - CC.Color.BlendColor[1];
		  sB = 1.0F - CC.Color.BlendColor[2];
		  sA = 1.0F - CC.Color.BlendColor[3];
		  break;
	       case GL_CONSTANT_ALPHA_EXT:
		  sR = sG = sB = sA = CC.Color.BlendColor[3];
		  break;
	       case GL_ONE_MINUS_CONSTANT_ALPHA_EXT:
		  sR = sG = sB = sA = 1.0F - CC.Color.BlendColor[3];
		  break;
	       default:
		  gl_error( GL_INVALID_ENUM, "gl_blend source" );
	    }

	    /* Dest scaling */
	    switch (CC.Color.BlendDst) {
	       case GL_ZERO:
		  dR = dG = dB = dA = 0.0F;
		  break;
	       case GL_ONE:
		  dR = dG = dB = dA = 1.0F;
		  break;
	       case GL_SRC_COLOR:
		  dR = (GLfloat) Rs * rscale;
		  dG = (GLfloat) Gs * gscale;
		  dB = (GLfloat) Bs * bscale;
		  dA = (GLfloat) As * ascale;
		  break;
	       case GL_ONE_MINUS_SRC_COLOR:
		  dR = 1.0F - (GLfloat) Rs * rscale;
		  dG = 1.0F - (GLfloat) Gs * gscale;
		  dB = 1.0F - (GLfloat) Bs * bscale;
		  dA = 1.0F - (GLfloat) As * ascale;
		  break;
	       case GL_SRC_ALPHA:
		  dR = dG = dB = dA = (GLfloat) As * ascale;
		  break;
	       case GL_ONE_MINUS_SRC_ALPHA:
		  dR = dG = dB = dA = (GLfloat) 1.0F - (GLfloat) As * ascale;
		  break;
	       case GL_DST_ALPHA:
		  dR = dG = dB = dA = (GLfloat) Ad * ascale;
		  break;
	       case GL_ONE_MINUS_DST_ALPHA:
		  dR = dG = dB = dA = 1.0F - (GLfloat) Ad * ascale;
		  break;
	       case GL_CONSTANT_COLOR_EXT:
		  dR = CC.Color.BlendColor[0];
		  dG = CC.Color.BlendColor[1];
		  dB = CC.Color.BlendColor[2];
		  dA = CC.Color.BlendColor[3];
		  break;
	       case GL_ONE_MINUS_CONSTANT_COLOR_EXT:
		  dR = 1.0F - CC.Color.BlendColor[0];
		  dG = 1.0F - CC.Color.BlendColor[1];
		  dB = 1.0F - CC.Color.BlendColor[2];
		  dA = 1.0F - CC.Color.BlendColor[3];
		  break;
	       case GL_CONSTANT_ALPHA_EXT:
		  dR = dG = dB = dA = CC.Color.BlendColor[3];
		  break;
	       case GL_ONE_MINUS_CONSTANT_ALPHA_EXT:
		  dR = dG = dB = dA = 1.0F - CC.Color.BlendColor[3] * ascale;
		  break;
	       default:
		  gl_error( GL_INVALID_ENUM, "gl_blend dest" );
	    }

#ifdef DEBUG
	    assert( sR>= 0.0 && sR<=1.0 );
	    assert( sG>= 0.0 && sG<=1.0 );
	    assert( sB>= 0.0 && sB<=1.0 );
	    assert( sA>= 0.0 && sA<=1.0 );
	    assert( dR>= 0.0 && dR<=1.0 );
	    assert( dG>= 0.0 && dG<=1.0 );
	    assert( dB>= 0.0 && dB<=1.0 );
	    assert( dA>= 0.0 && dA<=1.0 );
#endif

	    /* compute blended color */
	    if (CC.Color.BlendEquation==GL_FUNC_ADD_EXT) {
	       r = Rs * sR + Rd * dR;
	       g = Gs * sG + Gd * dG;
	       b = Bs * sB + Bd * dB;
	       a = As * sA + Ad * dA;
	    }
	    else if (CC.Color.BlendEquation==GL_FUNC_SUBTRACT_EXT) {
	       r = Rs * sR - Rd * dR;
	       g = Gs * sG - Gd * dG;
	       b = Bs * sB - Bd * dB;
	       a = As * sA - Ad * dA;
	    }
	    else if (CC.Color.BlendEquation==GL_FUNC_REVERSE_SUBTRACT_EXT) {
	       r = Rd * dR - Rs * sR;
	       g = Gd * dG - Gs * sG;
	       b = Bd * dB - Bs * sB;
	       a = Ad * dA - As * sA;
	    }
	    red[i]   = (GLint) CLAMP( r, 0.0, CC.RedScale );
	    green[i] = (GLint) CLAMP( g, 0.0, CC.GreenScale );
	    blue[i]  = (GLint) CLAMP( b, 0.0, CC.BlueScale );
	    alpha[i] = (GLint) CLAMP( a, 0.0, CC.AlphaScale );
	 }
      }
   }

   else if (CC.Color.BlendEquation==GL_LOGIC_OP) {
      /* EXTENSION */
      switch (CC.Color.LogicOp) {
	 case GL_CLEAR:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i] = green[i] = blue[i] = alpha[i] = 0;
	       }
	    }
	    break;
	 case GL_SET:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = (GLint) CC.RedScale;
		  green[i] = (GLint) CC.GreenScale;
		  blue[i]  = (GLint) CC.BlueScale;
		  alpha[i] = (GLint) CC.AlphaScale;
	       }
	    }
	    break;
	 case GL_COPY:
	    /* do nothing */
	    break;
	 case GL_COPY_INVERTED:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = !red[i];
		  green[i] = !green[i];
		  blue[i]  = !blue[i];
		  alpha[i] = !alpha[i];
	       }
	    }
	    break;
	 case GL_NOOP:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = rdest[i];
		  green[i] = gdest[i];
		  blue[i]  = bdest[i];
		  alpha[i] = adest[i];
	       }
	    }
	    break;
	 case GL_INVERT:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = !rdest[i];
		  green[i] = !gdest[i];
		  blue[i]  = !bdest[i];
		  alpha[i] = !adest[i];
	       }
	    }
	    break;
	 case GL_AND:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   &= rdest[i];
		  green[i] &= gdest[i];
		  blue[i]  &= bdest[i];
		  alpha[i] &= adest[i];
	       }
	    }
	    break;
	 case GL_NAND:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = !(red[i]   & rdest[i]);
		  green[i] = !(green[i] & gdest[i]);
		  blue[i]  = !(blue[i]  & bdest[i]);
		  alpha[i] = !(alpha[i] & adest[i]);
	       }
	    }
	    break;
	 case GL_OR:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   |= rdest[i];
		  green[i] |= gdest[i];
		  blue[i]  |= bdest[i];
		  alpha[i] |= adest[i];
	       }
	    }
	    break;
	 case GL_NOR:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = !(red[i]   | rdest[i]);
		  green[i] = !(green[i] | gdest[i]);
		  blue[i]  = !(blue[i]  | bdest[i]);
		  alpha[i] = !(alpha[i] | adest[i]);
	       }
	    }
	    break;
	 case GL_XOR:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   ^= rdest[i];
		  green[i] ^= gdest[i];
		  blue[i]  ^= bdest[i];
		  alpha[i] ^= adest[i];
	       }
	    }
	    break;
	 case GL_EQUIV:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = !(red[i]   ^ rdest[i]);
		  green[i] = !(green[i] ^ gdest[i]);
		  blue[i]  = !(blue[i]  ^ bdest[i]);
		  alpha[i] = !(alpha[i] ^ adest[i]);
	       }
	    }
	    break;
	 case GL_AND_REVERSE:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = red[i]   & !rdest[i];
		  green[i] = green[i] & !gdest[i];
		  blue[i]  = blue[i]  & !bdest[i];
		  alpha[i] = alpha[i] & !adest[i];
	       }
	    }
	    break;
	 case GL_AND_INVERTED:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = !red[i]   & rdest[i];
		  green[i] = !green[i] & gdest[i];
		  blue[i]  = !blue[i]  & bdest[i];
		  alpha[i] = !alpha[i] & adest[i];
	       }
	    }
	    break;
	 case GL_OR_REVERSE:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = red[i]   | !rdest[i];
		  green[i] = green[i] | !gdest[i];
		  blue[i]  = blue[i]  | !bdest[i];
		  alpha[i] = alpha[i] | !adest[i];
	       }
	    }
	    break;
	 case GL_OR_INVERTED:
	    for (i=0;i<n;i++) {
	       if (mask[i]) {
		  red[i]   = !red[i]   | rdest[i];
		  green[i] = !green[i] | gdest[i];
		  blue[i]  = !blue[i]  | bdest[i];
		  alpha[i] = !alpha[i] | adest[i];
	       }
	    }
	    break;
	 default:
	    ;  /* nothing */
      }
   }
   else if (CC.Color.BlendEquation==GL_MIN_EXT) {
      /* EXTENSION */
      for (i=0;i<n;i++) {
	 if (mask[i]) {
	    red[i]   = MIN2( red[i],   rdest[i] );
	    green[i] = MIN2( green[i], gdest[i] );
	    blue[i]  = MIN2( blue[i],  bdest[i] );
	    alpha[i] = MIN2( alpha[i], adest[i] );
	 }
      }
   }
   else if (CC.Color.BlendEquation==GL_MAX_EXT) {
      /* EXTENSION */
      for (i=0;i<n;i++) {
	 if (mask[i]) {
	    red[i]   = MAX2( red[i],   rdest[i] );
	    green[i] = MAX2( green[i], gdest[i] );
	    blue[i]  = MAX2( blue[i],  bdest[i] );
	    alpha[i] = MAX2( alpha[i], adest[i] );
	 }
      }
   }

}





/*
 * Apply the blending operator to a span of pixels.
 * Input:  n - number of pixels in span
 *         x, y - location of leftmost pixel in span in window coords.
 *         mask - boolean mask indicating which pixels to blend.
 * In/Out:  red, green, blue, alpha - pixel values
 */
void gl_blend_span( GLuint n, GLint x, GLint y,
		    GLubyte red[], GLubyte green[],
		    GLubyte blue[], GLubyte alpha[],
		    GLubyte mask[] )
{
   GLubyte rdest[MAX_WIDTH], gdest[MAX_WIDTH];
   GLubyte bdest[MAX_WIDTH], adest[MAX_WIDTH];

   /* Check if device driver can do the work */
   if (CC.Color.BlendEquation==GL_LOGIC_OP && !CC.Color.SWLogicOpEnabled) {
      return;
   }

   /* Read span of current frame buffer pixels */
   gl_read_color_span( n, x, y, rdest, gdest, bdest, adest );

   do_blend( n, mask, red, green, blue, alpha, rdest, gdest, bdest, adest );
}





/*
 * Apply the blending operator to an array of pixels.
 * Input:  n - number of pixels in span
 *         x, y - array of pixel locations
 *         mask - boolean mask indicating which pixels to blend.
 * In/Out:  red, green, blue, alpha - array of pixel values
 */
void gl_blend_pixels( GLuint n, const GLint x[], const GLint y[],
		      GLubyte red[], GLubyte green[],
		      GLubyte blue[], GLubyte alpha[],
		      GLubyte mask[] )
{
   GLubyte rdest[PB_SIZE], gdest[PB_SIZE], bdest[PB_SIZE], adest[PB_SIZE];

   /* Check if device driver can do the work */
   if (CC.Color.BlendEquation==GL_LOGIC_OP && !CC.Color.SWLogicOpEnabled) {
      return;
   }

   /* Read pixels from current color buffer */
   (*DD.read_color_pixels)( n, x, y, rdest, gdest, bdest, adest, mask );

   do_blend( n, mask, red, green, blue, alpha, rdest, gdest, bdest, adest );
}
