aboutsummaryrefslogblamecommitdiffstats
path: root/libart_lgpl/art_render.c
blob: 6c71e4a260bb25b6570c4e51174671ba1910337f (plain) (tree)

































                                                                    
                     

                              
                   


                                






                                                                         
                                                   
 
                                       














                                                                      
                                                
 



                                                                      





























                                                                       
                                                 
 



                                                                      




                                     
                                             
















                                                 





                                                                      
                                                             


                                      



                             

                                        
            


                    
                

                                
                               
                                               


                                                      
                                             

                                                         















                                 
                                                                 


















































































































































































                                                                                      
                                             

                                      



                             

                                        
            


                    

                               
                                               

                                                      
                                             

                                                         















                                 
                                                                 






































































































                                                                                      







                                                                        
                                                  

                                      



                             
                                        
            

                    
                














                                 
                                                                 
















                                             
 





                                                          
 
                               
 




                                                                                    
 



                                     
 






                                                                             
 






                          













                                                                        
                                                  

                                      



                             
                                        
            

                    
                















                                 
                                                                 








                                                                          
 


                                                                          
 
                                        
 



                                                                                      
 



                                                      
 



                                     
 






                                                                             
 












                                                                      
 
                                          
 





                                                          
 
                               
 



                                                                                      
 







                                                      
 



                                     
 






                                                                             
 












                                                         
/*
 * art_render.c: Modular rendering architecture.
 *
 * Libart_LGPL - library of basic graphic primitives
 * Copyright (C) 2000 Raph Levien
 *
 * 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., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

#include "config.h"
#include "art_render.h"

#include "art_rgb.h"

typedef struct _ArtRenderPriv ArtRenderPriv;

struct _ArtRenderPriv {
  ArtRender super;

  ArtImageSource *image_source;

  gint n_mask_source;
  ArtMaskSource **mask_source;

  gint n_callbacks;
  ArtRenderCallback **callbacks;
};

static void
art_render_nop_done (ArtRenderCallback *self, ArtRender *render)
{
}

static void
art_render_clear_render_rgb8 (ArtRenderCallback *self, ArtRender *render,
                  art_u8 *dest, gint y)
{
  gint width = render->x1 - render->x0;
  art_u8 r, g, b;
  ArtPixMaxDepth color_max;

  color_max = render->clear_color[0];
  r = ART_PIX_8_FROM_MAX (color_max);
  color_max = render->clear_color[1];
  g = ART_PIX_8_FROM_MAX (color_max);
  color_max = render->clear_color[2];
  b = ART_PIX_8_FROM_MAX (color_max);

  art_rgb_fill_run (dest, r, g, b, width);
}

static void
art_render_clear_render_8 (ArtRenderCallback *self, ArtRender *render,
               art_u8 *dest, gint y)
{
  gint width = render->x1 - render->x0;
  gint i, j;
  gint n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
  gint ix;
  art_u8 color[ART_MAX_CHAN + 1];

  for (j = 0; j < n_ch; j++)
    {
      ArtPixMaxDepth color_max = render->clear_color[j];
      color[j] = ART_PIX_8_FROM_MAX (color_max);
    }

  ix = 0;
  for (i = 0; i < width; i++)
    for (j = 0; j < n_ch; j++)
      dest[ix++] = color[j];
}

const ArtRenderCallback art_render_clear_rgb8_obj =
{
  art_render_clear_render_rgb8,
  art_render_nop_done
};

const ArtRenderCallback art_render_clear_8_obj =
{
  art_render_clear_render_8,
  art_render_nop_done
};

#if ART_MAX_DEPTH >= 16

static void
art_render_clear_render_16 (ArtRenderCallback *self, ArtRender *render,
                art_u8 *dest, gint y)
{
  gint width = render->x1 - render->x0;
  gint i, j;
  gint n_ch = render->n_chan + (render->alpha_type != ART_ALPHA_NONE);
  gint ix;
  art_u16 *dest_16 = (art_u16 *)dest;
  art_u8 color[ART_MAX_CHAN + 1];

  for (j = 0; j < n_ch; j++)
    {
      gint color_16 = render->clear_color[j];
      color[j] = color_16;
    }

  ix = 0;
  for (i = 0; i < width; i++)
    for (j = 0; j < n_ch; j++)
      dest_16[ix++] = color[j];
}

const ArtRenderCallback art_render_clear_16_obj =
{
  art_render_clear_render_16,
  art_render_nop_done
};

#endif /* ART_MAX_DEPTH >= 16 */

/* This is the most general form of the function. It is slow but
   (hopefully) correct. Actually, I'm still worried about roundoff
   errors in the premul case - it seems to me that an off-by-one could
   lead to overflow. */
static void
art_render_composite (ArtRenderCallback *self, ArtRender *render,
                    art_u8 *dest, gint y)
{
  ArtRenderMaskRun *run = render->run;
  art_u32 depth = render->depth;
  gint n_run = render->n_run;
  gint x0 = render->x0;
  gint x;
  gint run_x0, run_x1;
  art_u8 *alpha_buf = render->alpha_buf;
  art_u8 *image_buf = render->image_buf;
  gint i, j;
  art_u32 tmp;
  art_u32 run_alpha;
  art_u32 alpha;
  gint image_ix;
  art_u16 src[ART_MAX_CHAN + 1];
  art_u16 dst[ART_MAX_CHAN + 1];
  gint n_chan = render->n_chan;
  ArtAlphaType alpha_type = render->alpha_type;
  gint n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
  gint dst_pixstride = n_ch * (depth >> 3);
  gint buf_depth = render->buf_depth;
  ArtAlphaType buf_alpha = render->buf_alpha;
  gint buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
  gint buf_pixstride = buf_n_ch * (buf_depth >> 3);
  art_u8 *bufptr;
  art_u32 src_alpha;
  art_u32 src_mul;
  art_u8 *dstptr;
  art_u32 dst_alpha;
  art_u32 dst_mul;

  image_ix = 0;
  for (i = 0; i < n_run - 1; i++)
    {
      run_x0 = run[i].x;
      run_x1 = run[i + 1].x;
      tmp = run[i].alpha;
      if (tmp < 0x8100)
    continue;

      run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8;
      bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
      dstptr = dest + (run_x0 - x0) * dst_pixstride;
      for (x = run_x0; x < run_x1; x++)
    {
      if (alpha_buf)
        {
          if (depth == 8)
        {
          tmp = run_alpha * alpha_buf[x - x0] + 0x80;
          /* range 0x80 .. 0xff0080 */
          alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
        }
          else /* (depth == 16) */
        {
          tmp = ((art_u16 *)alpha_buf)[x - x0];
          tmp = (run_alpha * tmp + 0x8000) >> 8;
          /* range 0x80 .. 0xffff80 */
          alpha = (tmp + (tmp >> 16)) >> 8;
        }
        }
      else
        alpha = run_alpha;
      /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */

      /* convert (src pixel * alpha) to premul alpha form,
         store in src as 0..0xffff range */
      if (buf_alpha == ART_ALPHA_NONE)
        {
          src_alpha = alpha;
          src_mul = src_alpha;
        }
      else
        {
          if (buf_depth == 8)
        {
          tmp = alpha * bufptr[n_chan] + 0x80;
          /* range 0x80 .. 0xff0080 */
          src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
        }
          else /* (depth == 16) */
        {
          tmp = ((art_u16 *)bufptr)[n_chan];
          tmp = (alpha * tmp + 0x8000) >> 8;
          /* range 0x80 .. 0xffff80 */
          src_alpha = (tmp + (tmp >> 16)) >> 8;
        }
          if (buf_alpha == ART_ALPHA_SEPARATE)
        src_mul = src_alpha;
          else /* buf_alpha == (ART_ALPHA_PREMUL) */
        src_mul = alpha;
        }
      /* src_alpha is the (alpha of the source pixel * alpha),
         range 0..0x10000 */

      if (buf_depth == 8)
        {
          src_mul *= 0x101;
          for (j = 0; j < n_chan; j++)
        src[j] = (bufptr[j] * src_mul + 0x8000) >> 16;
        }
      else if (buf_depth == 16)
        {
          for (j = 0; j < n_chan; j++)
        src[j] = (((art_u16 *)bufptr)[j] * src_mul + 0x8000) >> 16;
        }
      bufptr += buf_pixstride;

      /* src[0..n_chan - 1] (range 0..0xffff) and src_alpha (range
             0..0x10000) now contain the source pixel with
             premultiplied alpha */

      /* convert dst pixel to premul alpha form,
         store in dst as 0..0xffff range */
      if (alpha_type == ART_ALPHA_NONE)
        {
          dst_alpha = 0x10000;
          dst_mul = dst_alpha;
        }
      else
        {
          if (depth == 8)
        {
          tmp = dstptr[n_chan];
          /* range 0..0xff */
          dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
        }
          else /* (depth == 16) */
        {
          tmp = ((art_u16 *)dstptr)[n_chan];
          dst_alpha = (tmp + (tmp >> 15));
        }
          if (alpha_type == ART_ALPHA_SEPARATE)
        dst_mul = dst_alpha;
          else /* (alpha_type == ART_ALPHA_PREMUL) */
        dst_mul = 0x10000;
        }
      /* dst_alpha is the alpha of the dest pixel,
         range 0..0x10000 */

      if (depth == 8)
        {
          dst_mul *= 0x101;
          for (j = 0; j < n_chan; j++)
        dst[j] = (dstptr[j] * dst_mul + 0x8000) >> 16;
        }
      else if (buf_depth == 16)
        {
          for (j = 0; j < n_chan; j++)
        dst[j] = (((art_u16 *)dstptr)[j] * dst_mul + 0x8000) >> 16;
        }

      /* do the compositing, dst = (src over dst) */
      for (j = 0; j < n_chan; j++)
        {
          art_u32 srcv, dstv;
          art_u32 tmp;

          srcv = src[j];
          dstv = dst[j];
          tmp = ((dstv * (0x10000 - src_alpha) + 0x8000) >> 16) + srcv;
          tmp -= tmp >> 16;
          dst[j] = tmp;
        }

      if (alpha_type == ART_ALPHA_NONE)
        {
          if (depth == 8)
        dst_mul = 0xff;
          else /* (depth == 16) */
        dst_mul = 0xffff;
        }
      else
        {
          if (src_alpha >= 0x10000)
        dst_alpha = 0x10000;
          else
        dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
          if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
        {
          if (depth == 8)
            dst_mul = 0xff;
          else /* (depth == 16) */
            dst_mul = 0xffff;
        }
          else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
        {
          if (depth == 8)
            dst_mul = 0xff0000 / dst_alpha;
          else /* (depth == 16) */
            dst_mul = 0xffff0000 / dst_alpha;
        }
        }
      if (depth == 8)
        {
          for (j = 0; j < n_chan; j++)
        dstptr[j] = (dst[j] * dst_mul + 0x8000) >> 16;
          if (alpha_type != ART_ALPHA_NONE)
        dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;
        }
      else if (depth == 16)
        {
          for (j = 0; j < n_chan; j++)
        ((art_u16 *)dstptr)[j] = (dst[j] * dst_mul + 0x8000) >> 16;
          if (alpha_type != ART_ALPHA_NONE)
        ((art_u16 *)dstptr)[n_chan] = (dst_alpha * 0xffff + 0x8000) >> 16;
        }
      dstptr += dst_pixstride;
    }
    }
}

const ArtRenderCallback art_render_composite_obj =
{
  art_render_composite,
  art_render_nop_done
};

static void
art_render_composite_8 (ArtRenderCallback *self, ArtRender *render,
            art_u8 *dest, gint y)
{
  ArtRenderMaskRun *run = render->run;
  gint n_run = render->n_run;
  gint x0 = render->x0;
  gint x;
  gint run_x0, run_x1;
  art_u8 *alpha_buf = render->alpha_buf;
  art_u8 *image_buf = render->image_buf;
  gint i, j;
  art_u32 tmp;
  art_u32 run_alpha;
  art_u32 alpha;
  gint image_ix;
  gint n_chan = render->n_chan;
  ArtAlphaType alpha_type = render->alpha_type;
  gint n_ch = n_chan + (alpha_type != ART_ALPHA_NONE);
  gint dst_pixstride = n_ch;
  ArtAlphaType buf_alpha = render->buf_alpha;
  gint buf_n_ch = n_chan + (buf_alpha != ART_ALPHA_NONE);
  gint buf_pixstride = buf_n_ch;
  art_u8 *bufptr;
  art_u32 src_alpha;
  art_u32 src_mul;
  art_u8 *dstptr;
  art_u32 dst_alpha;
  art_u32 dst_mul, dst_save_mul;

  image_ix = 0;
  for (i = 0; i < n_run - 1; i++)
    {
      run_x0 = run[i].x;
      run_x1 = run[i + 1].x;
      tmp = run[i].alpha;
      if (tmp < 0x10000)
    continue;

      run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8;
      bufptr = image_buf + (run_x0 - x0) * buf_pixstride;
      dstptr = dest + (run_x0 - x0) * dst_pixstride;
      for (x = run_x0; x < run_x1; x++)
    {
      if (alpha_buf)
        {
          tmp = run_alpha * alpha_buf[x - x0] + 0x80;
          /* range 0x80 .. 0xff0080 */
          alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
        }
      else
        alpha = run_alpha;
      /* alpha is run_alpha * alpha_buf[x], range 0 .. 0x10000 */

      /* convert (src pixel * alpha) to premul alpha form,
         store in src as 0..0xffff range */
      if (buf_alpha == ART_ALPHA_NONE)
        {
          src_alpha = alpha;
          src_mul = src_alpha;
        }
      else
        {
          tmp = alpha * bufptr[n_chan] + 0x80;
          /* range 0x80 .. 0xff0080 */
          src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;

          if (buf_alpha == ART_ALPHA_SEPARATE)
        src_mul = src_alpha;
          else /* buf_alpha == (ART_ALPHA_PREMUL) */
        src_mul = alpha;
        }
      /* src_alpha is the (alpha of the source pixel * alpha),
         range 0..0x10000 */

      src_mul *= 0x101;

      if (alpha_type == ART_ALPHA_NONE)
        {
          dst_alpha = 0x10000;
          dst_mul = dst_alpha;
        }
      else
        {
          tmp = dstptr[n_chan];
          /* range 0..0xff */
          dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
          if (alpha_type == ART_ALPHA_SEPARATE)
        dst_mul = dst_alpha;
          else /* (alpha_type == ART_ALPHA_PREMUL) */
        dst_mul = 0x10000;
        }
      /* dst_alpha is the alpha of the dest pixel,
         range 0..0x10000 */

      dst_mul *= 0x101;

      if (alpha_type == ART_ALPHA_NONE)
        {
          dst_save_mul = 0xff;
        }
      else
        {
          if (src_alpha >= 0x10000)
        dst_alpha = 0x10000;
          else
        dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;
          if (alpha_type == ART_ALPHA_PREMUL || dst_alpha == 0)
        {
          dst_save_mul = 0xff;
        }
          else /* (ALPHA_TYPE == ART_ALPHA_SEPARATE && dst_alpha != 0) */
        {
          dst_save_mul = 0xff0000 / dst_alpha;
        }
        }

      for (j = 0; j < n_chan; j++)
        {
          art_u32 src, dst;
          art_u32 tmp;

          src = (bufptr[j] * src_mul + 0x8000) >> 16;
          dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
          tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
          tmp -= tmp >> 16;
          dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
        }
      if (alpha_type != ART_ALPHA_NONE)
        dstptr[n_chan] = (dst_alpha * 0xff + 0x8000) >> 16;

      bufptr += buf_pixstride;
      dstptr += dst_pixstride;
    }
    }
}

const ArtRenderCallback art_render_composite_8_obj =
{
  art_render_composite_8,
  art_render_nop_done
};

/* Assumes:
 * alpha_buf is NULL
 * buf_alpha = ART_ALPHA_NONE  (source)
 * alpha_type = ART_ALPHA_SEPARATE (dest)
 * n_chan = 3;
 */
static void
art_render_composite_8_opt1 (ArtRenderCallback *self, ArtRender *render,
                 art_u8 *dest, gint y)
{
  ArtRenderMaskRun *run = render->run;
  gint n_run = render->n_run;
  gint x0 = render->x0;
  gint x;
  gint run_x0, run_x1;
  art_u8 *image_buf = render->image_buf;
  gint i, j;
  art_u32 tmp;
  art_u32 run_alpha;
  gint image_ix;
  art_u8 *bufptr;
  art_u32 src_mul;
  art_u8 *dstptr;
  art_u32 dst_alpha;
  art_u32 dst_mul, dst_save_mul;

  image_ix = 0;
  for (i = 0; i < n_run - 1; i++)
    {
      run_x0 = run[i].x;
      run_x1 = run[i + 1].x;
      tmp = run[i].alpha;
      if (tmp < 0x10000)
    continue;

      run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8;
      bufptr = image_buf + (run_x0 - x0) * 3;
      dstptr = dest + (run_x0 - x0) * 4;
      if (run_alpha == 0x10000)
    {
      for (x = run_x0; x < run_x1; x++)
        {
          *dstptr++ = *bufptr++;
          *dstptr++ = *bufptr++;
          *dstptr++ = *bufptr++;
          *dstptr++ = 0xff;
        }
    }
      else
    {
      for (x = run_x0; x < run_x1; x++)
        {
          src_mul = run_alpha * 0x101;

          tmp = dstptr[3];
          /* range 0..0xff */
          dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
          dst_mul = dst_alpha;
          /* dst_alpha is the alpha of the dest pixel,
         range 0..0x10000 */

          dst_mul *= 0x101;

          dst_alpha += ((((0x10000 - dst_alpha) * run_alpha) >> 8) + 0x80) >> 8;
          if (dst_alpha == 0)
          dst_save_mul = 0xff;
          else /* (dst_alpha != 0) */
          dst_save_mul = 0xff0000 / dst_alpha;

          for (j = 0; j < 3; j++)
        {
          art_u32 src, dst;
          art_u32 tmp;

          src = (bufptr[j] * src_mul + 0x8000) >> 16;
          dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
          tmp = ((dst * (0x10000 - run_alpha) + 0x8000) >> 16) + src;
          tmp -= tmp >> 16;
          dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
        }
          dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;

          bufptr += 3;
          dstptr += 4;
        }
    }
    }
}

const ArtRenderCallback art_render_composite_8_opt1_obj =
{
  art_render_composite_8_opt1,
  art_render_nop_done
};

/* Assumes:
 * alpha_buf is NULL
 * buf_alpha = ART_ALPHA_PREMUL  (source)
 * alpha_type = ART_ALPHA_SEPARATE (dest)
 * n_chan = 3;
 */
static void
art_render_composite_8_opt2 (ArtRenderCallback *self, ArtRender *render,
                 art_u8 *dest, gint y)
{
  ArtRenderMaskRun *run = render->run;
  gint n_run = render->n_run;
  gint x0 = render->x0;
  gint x;
  gint run_x0, run_x1;
  art_u8 *image_buf = render->image_buf;
  gint i, j;
  art_u32 tmp;
  art_u32 run_alpha;
  gint image_ix;
  art_u8 *bufptr;
  art_u32 src_alpha;
  art_u32 src_mul;
  art_u8 *dstptr;
  art_u32 dst_alpha;
  art_u32 dst_mul, dst_save_mul;

  image_ix = 0;
  for (i = 0; i < n_run - 1; i++)
    {
      run_x0 = run[i].x;
      run_x1 = run[i + 1].x;
      tmp = run[i].alpha;
      if (tmp < 0x10000)
    continue;

      run_alpha = (tmp + (tmp >> 8) + (tmp >> 16) - 0x8000) >> 8;
      bufptr = image_buf + (run_x0 - x0) * 4;
      dstptr = dest + (run_x0 - x0) * 4;
      if (run_alpha == 0x10000)
    {
      for (x = run_x0; x < run_x1; x++)
        {
          src_alpha = (bufptr[3] << 8) + bufptr[3] + (bufptr[3] >> 7);
          /* src_alpha is the (alpha of the source pixel),
         range 0..0x10000 */

          dst_alpha = (dstptr[3] << 8) + dstptr[3] + (dstptr[3] >> 7);
          /* dst_alpha is the alpha of the dest pixel,
         range 0..0x10000 */

          dst_mul = dst_alpha*0x101;

          if (src_alpha >= 0x10000)
        dst_alpha = 0x10000;
          else
        dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;

          if (dst_alpha == 0)
          dst_save_mul = 0xff;
          else /* dst_alpha != 0) */
          dst_save_mul = 0xff0000 / dst_alpha;

          for (j = 0; j < 3; j++)
        {
          art_u32 src, dst;
          art_u32 tmp;

          src = (bufptr[j] << 8) |  bufptr[j];
          dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
          tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
          tmp -= tmp >> 16;
          dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
        }
          dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;

          bufptr += 4;
          dstptr += 4;
        }
    }
      else
    {
      for (x = run_x0; x < run_x1; x++)
        {
          tmp = run_alpha * bufptr[3] + 0x80;
          /* range 0x80 .. 0xff0080 */
          src_alpha = (tmp + (tmp >> 8) + (tmp >> 16)) >> 8;
          /* src_alpha is the (alpha of the source pixel * alpha),
         range 0..0x10000 */

          src_mul = run_alpha * 0x101;

          tmp = dstptr[3];
          /* range 0..0xff */
          dst_alpha = (tmp << 8) + tmp + (tmp >> 7);
          dst_mul = dst_alpha;
          /* dst_alpha is the alpha of the dest pixel,
         range 0..0x10000 */

          dst_mul *= 0x101;

          if (src_alpha >= 0x10000)
        dst_alpha = 0x10000;
          else
        dst_alpha += ((((0x10000 - dst_alpha) * src_alpha) >> 8) + 0x80) >> 8;

          if (dst_alpha == 0)
        {
          dst_save_mul = 0xff;
        }
          else /* dst_alpha != 0) */
        {
          dst_save_mul = 0xff0000 / dst_alpha;
        }

          for (j = 0; j < 3; j++)
        {
          art_u32 src, dst;
          art_u32 tmp;

          src = (bufptr[j] * src_mul + 0x8000) >> 16;
          dst = (dstptr[j] * dst_mul + 0x8000) >> 16;
          tmp = ((dst * (0x10000 - src_alpha) + 0x8000) >> 16) + src;
          tmp -= tmp >> 16;
          dstptr[j] = (tmp * dst_save_mul + 0x8000) >> 16;
        }
          dstptr[3] = (dst_alpha * 0xff + 0x8000) >> 16;

          bufptr += 4;
          dstptr += 4;
        }
    }
    }
}

const ArtRenderCallback art_render_composite_8_opt2_obj =
{
  art_render_composite_8_opt2,
  art_render_nop_done
};