/**************************************************************************
 *{@C
 *      Copyright:      1988-2023 Paul Obermeier (obermeier@poSoft.de)
 *
 *      Module:         ImageProcessing
 *      Filename:       IP_Comp.c
 *
 *      Author:         Paul Obermeier
 *
 *      Description:    Image compositing functions.
 *
 *      Additional documentation:
 *                      None.
 *
 *      Exported functions:
 *                      IP_CompositeAlpha
 *                      IP_CompositeColorMatte
 *                      IP_CompositeDepth
 *                      IP_CrossDissolve
 *
 **************************************************************************/

#include <stdio.h>

#include "UT_Compat.h"
#include "UT_Error.h"
#include "UT_Memory.h"
#include "UT_Parallel.h"

#define USE_IMG_MACROS
#include "FF_Image.h"

#include "IP_Image.h"
#include "IP_ImagePrivate.h"

/* Determine if channel "chn" of image "img" can be used for a fast
   compositing operation, i.e. if the channel is either in FF_ImgFmtTypeNone
   or FF_ImgFmtTypeUByte format.  */

static UT_Bool isfast (IP_ImageId img, Int32 chn)
{
    return
        img->pimg.channel[chn] == FF_ImgFmtTypeNone ||
        img->pimg.channel[chn] == FF_ImgFmtTypeUByte;
}


/* Composite two images using a black and white matte: Decide whether to use
the fast or the slow compositing method, and invoke the right algorithm. */

static UT_Bool comp_bwmatte
        (IP_ImageId fgimg,
         IP_ImageId bgimg,
         IP_ImageId cmimg,
         Float32 f_r, Float32 f_g, Float32 f_b,
         Float32 b_r, Float32 b_g, Float32 b_b,
         IP_ImageId mtimg,
         UT_Bool fast)
{
    /* Make sure that we have a transparency channel. */

    if (mtimg)
    {
        if (!mtimg->pimg.channel[FF_ImgChanTypeMatte])
        {
            UT_ErrSetNum (UT_ErrParamInvalid, str_mt_bwtrans);
            return UT_False;
        }
    }
    else
    {
        if (!fgimg->pimg.channel[FF_ImgChanTypeMatte])
        {
            UT_ErrSetNum (UT_ErrParamInvalid, str_fg_bwtrans);
            return UT_False;
        }
    }

    /* Invoke the compositing algorithm. */

    if (mtimg)
        fast &= isfast (mtimg, FF_ImgChanTypeMatte);
    else
        fast &= isfast (fgimg, FF_ImgChanTypeMatte);
    if (IP_StateDrawMask[FF_ImgChanTypeGray] &&
        cmimg->pimg.channel[FF_ImgChanTypeGray])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeGray);
        fast &= isfast (bgimg, FF_ImgChanTypeGray);
        fast &= isfast (cmimg, FF_ImgChanTypeGray);
    }
    if (IP_StateDrawMask[FF_ImgChanTypeRed] &&
        cmimg->pimg.channel[FF_ImgChanTypeRed])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeRed);
        fast &= isfast (bgimg, FF_ImgChanTypeRed);
        fast &= isfast (cmimg, FF_ImgChanTypeRed);
    }
    if (IP_StateDrawMask[FF_ImgChanTypeGreen] &&
        cmimg->pimg.channel[FF_ImgChanTypeGreen])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeGreen);
        fast &= isfast (bgimg, FF_ImgChanTypeGreen);
        fast &= isfast (cmimg, FF_ImgChanTypeGreen);
    }
    if (IP_StateDrawMask[FF_ImgChanTypeBlue] &&
        cmimg->pimg.channel[FF_ImgChanTypeBlue])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeBlue);
        fast &= isfast (bgimg, FF_ImgChanTypeBlue);
        fast &= isfast (cmimg, FF_ImgChanTypeBlue);
    }
    if (IP_StateDrawMask[FF_ImgChanTypeMatte] &&
        cmimg->pimg.channel[FF_ImgChanTypeMatte])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeMatte);
        fast &= isfast (bgimg, FF_ImgChanTypeMatte);
        fast &= isfast (cmimg, FF_ImgChanTypeMatte);
    }

    return fast?
        IP_CompFastAlpha (fgimg, bgimg, cmimg,
                               f_r, f_g, f_b, b_r, b_g, b_b, mtimg):
        IP_CompSlowAlpha (fgimg, bgimg, cmimg,
                               f_r, f_g, f_b, b_r, b_g, b_b, mtimg);
}



/* Composite two images using a colored matte: Decide whether to use the
fast or the slow compositing method, and invoke the right algorithm. */

static UT_Bool comp_clrmatte
        (IP_ImageId fgimg,
         IP_ImageId bgimg,
         IP_ImageId cmimg,
         Float32 f_r, Float32 f_g, Float32 f_b,
         Float32 b_r, Float32 b_g, Float32 b_b,
         IP_ImageId mtimg,
         UT_Bool fast)
{
    /* Make sure that we have red, green and blue transparency channels. */

    if (mtimg)
    {
        if (!mtimg->pimg.channel[FF_ImgChanTypeRedMatte] ||
            !mtimg->pimg.channel[FF_ImgChanTypeRedMatte] ||
            !mtimg->pimg.channel[FF_ImgChanTypeRedMatte])
        {
            UT_ErrSetNum (UT_ErrParamInvalid, str_mt_clrtrans);
            return UT_False;
        }
    }
    else
    {
        if (!fgimg->pimg.channel[FF_ImgChanTypeRedMatte] ||
            !fgimg->pimg.channel[FF_ImgChanTypeRedMatte] ||
            !fgimg->pimg.channel[FF_ImgChanTypeRedMatte])
        {
            UT_ErrSetNum (UT_ErrParamInvalid, str_fg_clrtrans);
            return UT_False;
        }
    }

    /* Invoke the compositing algorithm. */

    if (mtimg)
    {
        fast &= isfast (mtimg, FF_ImgChanTypeRedMatte);
        fast &= isfast (mtimg, FF_ImgChanTypeGreenMatte);
        fast &= isfast (mtimg, FF_ImgChanTypeBlueMatte);
    }
    else
    {
        fast &= isfast (fgimg, FF_ImgChanTypeRedMatte);
        fast &= isfast (fgimg, FF_ImgChanTypeGreenMatte);
        fast &= isfast (fgimg, FF_ImgChanTypeBlueMatte);
    }
    if (IP_StateDrawMask[FF_ImgChanTypeRed] &&
        cmimg->pimg.channel[FF_ImgChanTypeRed])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeRed);
        fast &= isfast (bgimg, FF_ImgChanTypeRed);
        fast &= isfast (cmimg, FF_ImgChanTypeRed);
    }
    if (IP_StateDrawMask[FF_ImgChanTypeGreen] &&
        cmimg->pimg.channel[FF_ImgChanTypeGreen])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeGreen);
        fast &= isfast (bgimg, FF_ImgChanTypeGreen);
        fast &= isfast (cmimg, FF_ImgChanTypeGreen);
    }
    if (IP_StateDrawMask[FF_ImgChanTypeBlue] &&
        cmimg->pimg.channel[FF_ImgChanTypeBlue])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeBlue);
        fast &= isfast (bgimg, FF_ImgChanTypeBlue);
        fast &= isfast (cmimg, FF_ImgChanTypeBlue);
    }
    if (IP_StateDrawMask[FF_ImgChanTypeRedMatte] &&
        cmimg->pimg.channel[FF_ImgChanTypeRedMatte])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeRedMatte);
        fast &= isfast (bgimg, FF_ImgChanTypeRedMatte);
        fast &= isfast (cmimg, FF_ImgChanTypeRedMatte);
    }
    if (IP_StateDrawMask[FF_ImgChanTypeGreenMatte] &&
        cmimg->pimg.channel[FF_ImgChanTypeGreenMatte])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeGreenMatte);
        fast &= isfast (bgimg, FF_ImgChanTypeGreenMatte);
        fast &= isfast (cmimg, FF_ImgChanTypeGreenMatte);
    }
    if (IP_StateDrawMask[FF_ImgChanTypeBlueMatte] &&
        cmimg->pimg.channel[FF_ImgChanTypeBlueMatte])
    {
        fast &= isfast (fgimg, FF_ImgChanTypeBlueMatte);
        fast &= isfast (bgimg, FF_ImgChanTypeBlueMatte);
        fast &= isfast (cmimg, FF_ImgChanTypeBlueMatte);
    }

    return fast?
        IP_CompFastColorMatte (fgimg, bgimg, cmimg,
                                f_r, f_g, f_b, b_r, b_g, b_b, mtimg):
        IP_CompSlowColorMatte (fgimg, bgimg, cmimg,
                                f_r, f_g, f_b, b_r, b_g, b_b, mtimg);
}



/* Composite two images using depth mattes: Decide whether to use the
fast or the slow compositing method, and invoke the right algorithm. */

static UT_Bool comp_zmatte (IP_ImageId fgimg, IP_ImageId bgimg, IP_ImageId cmimg)
{
    Int32 chn;
    UT_Bool fast;

    /* Make sure that we have depth channels in both input images. */

    if (!fgimg->pimg.channel[FF_ImgChanTypeDepth])
    {
        UT_ErrSetNum (UT_ErrParamInvalid, str_fg_depth);
        return UT_False;
    }
    if (!bgimg->pimg.channel[FF_ImgChanTypeDepth])
    {
        UT_ErrSetNum (UT_ErrParamInvalid, str_bg_depth);
        return UT_False;
    }

    /* Invoke the compositing algorithm. */

    fast = UT_True;
    for (chn = FF_ImgChanTypeGray; chn <= FF_ImgChanTypeVertSnr; ++chn)
        if (IP_StateDrawMask[chn] && cmimg->pimg.channel[chn])
        {
            fast &= fgimg->pimg.channel[chn] == bgimg->pimg.channel[chn];
            fast &= bgimg->pimg.channel[chn] == cmimg->pimg.channel[chn];
        }
    fast &= fgimg->pimg.channel[FF_ImgChanTypeDepth] == FF_ImgFmtTypeFloat;
    fast &= bgimg->pimg.channel[FF_ImgChanTypeDepth] == FF_ImgFmtTypeFloat;

    return fast?
        IP_CompFastDepth (fgimg, bgimg, cmimg):
        IP_CompSlowDepth (fgimg, bgimg, cmimg);
}



/* Cross-dissove between two images: Decide whether to use the fast
or the slow compositing method, and invoke the right algorithm. */

static UT_Bool crossimage
        (IP_ImageId fgimg, IP_ImageId bgimg, IP_ImageId cmimg, Float32 mix)
{
    Int32 chn;
    UT_Bool fast;

    fast = UT_True;
    for (chn = FF_ImgChanTypeGray; chn <= FF_ImgChanTypeBlueMatte; ++chn)
        if (IP_StateDrawMask[chn] && cmimg->pimg.channel[chn])
        {
            fast &= isfast (fgimg, chn);
            fast &= isfast (bgimg, chn);
            fast &= isfast (cmimg, chn);
        }
    fast &= mix >= 0.0;
    fast &= mix <= 1.0;

    return fast?
        IP_CrossDissolveFast (fgimg, bgimg, cmimg, mix):
        IP_CrossDissolveSlow (fgimg, bgimg, cmimg, mix);
}

/***************************************************************************
 *[@e
 *      Name:           IP_CompositeAlpha
 *
 *      Usage:          Composite two images, using a black and white matte.
 *
 *      Synopsis:       UT_Bool IP_CompositeAlpha
 *                              (IP_ImageId fgimg,
 *                               IP_ImageId bgimg,
 *                               IP_ImageId cmimg,
 *                               Float32 f_r, Float32 f_g, Float32 f_b,
 *                               Float32 b_r, Float32 b_g, Float32 b_b,
 *                               IP_ImageId mtimg,
 *                               UT_Bool fast)
 *
 *      Description:    Image "fgimg" is composited in front of image
 *                      "bgimg". The result is stored in "cmimg".
 *                      If "mtimg" is NULL, the matte for compositing is
 *                      taken from image "fgimg". If "mtimg" is not NULL,
 *                      image "mtimg" supplies the matte.
 *                      
 *                      1. Any gamma-correction in the input data is
 *                         compensated by performing an inverse gamma-
 *                         correction on the pixel data from "fgimg"
 *                         and "bgimg".
 *
 *                      2. The FF_ImgChanTypeRed, FF_ImgChanTypeGreen and
 *                         FF_ImgChanTypeBlue channels of all pixels from 
 *                         "fgimg" are scaled by "fr", "fg" and "fb".
 *                         The FF_ImgChanTypeRed, FF_ImgChanTypeGreen
 *                         and FF_ImgChanTypeBlue channels of all pixels from
 *                         "bgimg" are scaled by "br", "bg" and "bb".
 *
 *                      3. The FF_ImgChanTypeGray, FF_ImgChanTypeRed, 
 *                         FF_ImgChanTypeGreen, and FF_ImgChanTypeBlue channels
 *                         for every pixel in "cmimg" are combined from the 
 *                         data in "fgimg" and "bgimg":
 *                          
 *                         if "mtimg" is NULL:
 *
 *                             cm.bright =
 *                                  fg.bright +
 *                                  bg.bright * (1-fg.trans);
 *                             cm.red =
 *                                  fg.red +
 *                                  bg.red * (1-fg.trans);
 *                             cm.green =
 *                                  fg.green +
 *                                  bg.green * (1-fg.trans);
 *                             cm.blue =
 *                                  fg.blue +
 *                                  bg.blue * (1-fg.trans);
 *                          else
 *
 *                             cm.bright =
 *                                  fg.bright * mt.trans +
 *                                  bg.bright * (1-mt.trans);
 *                             cm.red =
 *                                  fg.red * mt.trans +
 *                                  bg.red * (1-mt.trans);
 *                             cm.green =
 *                                  fg.green * mt.trans +
 *                                  bg.green * (1-mt.trans);
 *                             cm.blue =
 *                                  fg.blue * mt.trans +
 *                                  bg.blue * (1-mt.trans);
 *
 *                      4. The FF_ImgChanTypeGray, FF_ImgChanTypeRed, 
 *                         FF_ImgChanTypeGreen and FF_ImgChanTypeBlue channels
 *                         in "cmimg" are gamma corrected according to the
 *                         image descriptor of "cmimg".
 *
 *                      5. The FF_ImgChanTypeMatte channel for every pixel in
 *                         "cmimg" is computed:
 *
 *                         if "mtimg" is NULL:
 *
 *                             cm.trans = 1 - (1-fg.trans) * (1-bg.trans);
 *
 *                         else
 *
 *                             cm.trans = 1 - (1-mt.trans) * (1-bg.trans);
 *
 *                      Notes:
 *
 *                      - If necessary, "fgimg" or "bgimg" can be
 *                        identical to "cmimg", so that IP_CompositeAlpha
 *                        overwrites one of the input images with the result.
 *
 *                      - IP_CompositeAlpha does not write to any channels in
 *                        "cmimg" for which the drawing mask is "UT_False".
 *
 *                      - IP_CompositeAlpha does not write to the
 *                        FF_ImgChanTypeRedMatte, FF_ImgChanTypeGreenMatte,
 *                        FF_ImgChanTypeBlueMatte, FF_ImgChanTypeHoriSnr,
 *                        FF_ImgChanTypeVertSnr and FF_ImgChanTypeDepth channels
 *                        in "cmimg", even if the drawing mask for these 
 *                        channels is "UT_True".
 *
 *                      - For compositing, "fgimg", "bgimg" and
 *                        "cmimg" are aligned at the lower left corner.
 *                        If "cmimg" is wider or higher than "fgimg" or
 *                        "bgimg", IP_CompositeAlpha does not change areas
 *                        in "cmimg" which are not covered by "fgimg" or
 *                        "bgimg".
 *
 *                      - IP_CompositeAlpha can be run concurrently on
 *                        multiple processors; see IP_SetNumThreads.
 *
 *                      - If the "fast" flag is set to UT_True, and all pixel
 *                        data involved in the compositing are in 
 *                        FF_ImgFmtTypeUByte format, IP_CompositeAlpha will
 *                        switch to a faster, but less general compositing 
 *                        algorithm.
 *                        The results are usually the same as with the slower
 *                        algorithm; only if one of "fr", "fg", "fb", "br",
 *                        "bg" or "bb" is greater than 1.0 or less than 0.0,
 *                        color clipping may be different.
 *
 *      Return value:   UT_True if successful, else UT_False.
 *
 *      See also:       IP_CompositeColorMatte
 *                      IP_CompositeDepth
 *
 ***************************************************************************/

UT_Bool IP_CompositeAlpha
        (IP_ImageId fgimg,
         IP_ImageId bgimg,
         IP_ImageId cmimg,
         Float32 f_r, Float32 f_g, Float32 f_b,
         Float32 b_r, Float32 b_g, Float32 b_b,
         IP_ImageId mtimg,
         UT_Bool fast)
{
    UT_MemState memstate;
    UT_Bool     success;

    memstate = UT_MemRemember ();
    success = comp_bwmatte
            (fgimg, bgimg, cmimg,
             f_r, f_g, f_b,
             b_r, b_g, b_b,
             mtimg, fast);
    UT_MemRestore (memstate);
    return success;
}

/***************************************************************************
 *[@e
 *      Name:           IP_CompositeColorMatte
 *
 *      Usage:          Composite two images, using a colored matte.
 *
 *      Synopsis:       UT_Bool IP_CompositeColorMatte
 *                              (IP_ImageId fgimg,
 *                               IP_ImageId bgimg,
 *                               IP_ImageId cmimg,
 *                               Float32 f_r, Float32 f_g, Float32 f_b,
 *                               Float32 b_r, Float32 b_g, Float32 b_b,
 *                               IP_ImageId mtimg,
 *                               UT_Bool fast)
 *
 *      Description:    Image "fgimg" is composited in front of image
 *                      "bgimg". The result is stored in "cmimg".
 *                      If "mtimg" is NULL, the matte for compositing is
 *                      taken from image "fgimg". If "mtimg" is not NULL,
 *                      image "mtimg" supplies the matte.
 *                      
 *                      1. Any gamma-correction in the input data is
 *                         compensated by performing an inverse gamma-
 *                         correction on the pixel data from "fgimg"
 *                         and "bgimg".
 *
 *                      2. The FF_ImgChanTypeRed, FF_ImgChanTypeGreen and 
 *                         FF_ImgChanTypeBlue channels of all pixels from 
 *                         "fgimg" are scaled by "fr", "fg" and "fb".
 *                         The FF_ImgChanTypeRed, FF_ImgChanTypeGreen
 *                         and FF_ImgChanTypeBlue channels of all pixels from
 *                         "bgimg" are scaled by "br", "bg" and "bb".
 *
 *                      3. The FF_ImgChanTypeRed, FF_ImgChanTypeGreen and
 *                         FF_ImgChanTypeBlue channels for every pixel in 
 *                         "cmimg" are combined from the data in "fgimg" and 
 *                         "bgimg":
 *                          
 *                         if "mtimg" is not NUL:
 *
 *                             cm.red = fg.red * mt.red_trans +
 *                                  bg.red * (1-mt.red_trans);
 *
 *                             cm.green = fg.green * mt.green_trans +
 *                                  bg.green * (1-mt.green_trans);
 *
 *                             cm.blue = fg.blue * mt.blue_trans +
 *                                  bg.blue * (1-mt.blue_trans);
 *                         
 *                         else
 *
 *                             cm.red = fg.red +
 *                                  bg.red * (1-fg.red_trans);
 *
 *                             cm.green = fg.green +
 *                                  bg.green * (1-fg.green_trans);
 *
 *                             cm.blue = fg.blue +
 *                                  bg.blue * (1-fg.blue_trans);
 *
 *                      4. The FF_ImgChanTypeRed, FF_ImgChanTypeGreen and
 *                         FF_ImgChanTypeBlue channels in "cmimg" are gamma 
 *                         corrected according to the image descriptor of
 *                         "cmimg".
 *
 *                      5. The FF_ImgChanTypeRedMatte, FF_ImgChanTypeGreenMatte
 *                         and FF_ImgChanTypeBlueMatte channels for every pixel
 *                         in "cmimg" are combined from the data in "fgimg"
 *                         and "bgimg":
 *
 *                         if "mtimg" is not NULL:
 *
 *                             cm.red_trans = 1 -
 *                                  (1-mt.red_trans) * (1-bg.red_trans);
 *
 *                             cm.green_trans = 1 -
 *                                  (1-mt.green_trans) * (1-bg.green_trans);
 *
 *                             cm.blue_trans = 1 -
 *                                  (1-mt.blue_trans) * (1-bg.blue_trans);
 *                                  
 *                         else
 *
 *                             cm.red_trans = 1 -
 *                                  (1-fg.red_trans) * (1-bg.red_trans);
 *
 *                             cm.green_trans = 1 -
 *                                  (1-fg.green_trans) * (1-bg.green_trans);
 *
 *                             cm.blue_trans = 1 -
 *                                  (1-fg.blue_trans) * (1-bg.blue_trans);
 *                                  
 *                      Notes:
 *
 *                      - If necessary, "fgimg" or "bgimg" can be
 *                        identical to "cmimg", so that IP_CompositeColorMatte
 *                        overwrites one of the input images with the result.
 *
 *                      - IP_CompositeColorMatte does not write to any channels
 *                        in "cmimg" for which the drawing mask is "UT_False".
 *
 *                      - IP_CompositeColorMatte does not write to the
 *                        FF_ImgChanTypeGray, FF_ImgChanTypeMatte, 
 *                        FF_ImgChanTypeHoriSnr, FF_ImgChanTypeVertSnr and 
 *                        FF_ImgChanTypeDepth channels in "cmimg", even if the 
 *                        drawing mask for these channels is "UT_True".
 *
 *                      - For compositing, "fgimg", "bgimg" and
 *                        "cmimg" are aligned at the lower left corner.
 *                        If "cmimg" is wider or higher than "fgimg" or
 *                        "bgimg", IP_CompositeColorMatte does not change areas
 *                        in "cmimg" which are not covered by "fgimg" or
 *                        "bgimg".
 *
 *                      - IP_CompositeColorMatte can be run concurrently on
 *                        multiple processors; see IP_SetNumThreads.
 *
 *                      - If the "fast" flag is set to UT_True, and all pixel
 *                        data involved in the compositing are in
 *                        FF_ImgFmtTypeUByte format, IP_CompositeColorMatte will
 *                        switch to a faster, but less general compositing
 *                        algorithm.  
 *                        The results are usually the same as with the slower
 *                        algorithm; only if one of "fr", "fg", "fb", "br",
 *                        "bg" or "bb" is greater than 1.0 or less than 0.0,
 *                        color clipping may be different.
 *
 *      Return value:   UT_True if successful, else UT_False.
 *
 *      See also:       IP_CompositeAlpha
 *                      IP_CompositeDepth
 *
 ***************************************************************************/

UT_Bool IP_CompositeColorMatte
        (IP_ImageId fgimg,
         IP_ImageId bgimg,
         IP_ImageId cmimg,
         Float32 f_r, Float32 f_g, Float32 f_b,
         Float32 b_r, Float32 b_g, Float32 b_b,
         IP_ImageId mtimg,
         UT_Bool fast)
{
    UT_MemState memstate;
    UT_Bool     success;

    memstate = UT_MemRemember ();
    success = comp_clrmatte
            (fgimg, bgimg, cmimg,
             f_r, f_g, f_b,
             b_r, b_g, b_b,
             mtimg, fast);
    UT_MemRestore (memstate);
    return success;
}

/***************************************************************************
 *[@e
 *      Name:           IP_CompositeDepth
 *
 *      Usage:          Composite two images, using depth mattes.
 *
 *      Synopsis:       UT_Bool IP_CompositeDepth
 *                              (IP_ImageId fgimg,
 *                               IP_ImageId bgimg,
 *                               IP_ImageId cmimg)
 *
 *      Description:    Image "fgimg" and "bgimg" are combined into a new
 *                      image, "cmimg". Both, "fgimg" and "bgimg", must
 *                      include a FF_ImgChanTypeDepth channel, indicating the
 *                      distance of the pixels from the viewer.
 *
 *                      The data from "fgimg" and "bgimg" are combined as
 *                      follows:
 *
 *                          if fgimg.pixel is closer than bgimg.pixel
 *
 *                              cmimg.pixel = fgimg.pixel;
 *                          
 *                          else
 *                              
 *                              cmimg.pixel = bgimg.pixel;
 *
 *                      Notes:
 *
 *                      - If necessary, "fgimg" or "bgimg" can be identical to
 *                        "cmimg", so that IP_CompositeDepth overwrites one of
 *                        the input images with the result.
 *
 *                      - IP_CompositeDepth does not write to any channels in
 *                        "cmimg" for which the drawing mask is "UT_False".
 *
 *                      - For compositing, "fgimg", "bgimg" and "cmimg" are
 *                        aligned at the lower left corner. If "cmimg" is wider
 *                        or higher than "fgimg" or "bgimg", IP_CompositeDepth
 *                        does not change areas in "cmimg" which are not
 *                        covered by "fgimg" or "bgimg".
 *
 *                      - IP_CompositeDepth can be run concurrently on
 *                        multiple processors; see IP_SetNumThreads.
 *
 *      Return value:   UT_True if successful, else UT_False.
 *
 *      See also:       IP_CompositeAlpha
 *                      IP_CompositeColorMatte
 *
 ***************************************************************************/

UT_Bool IP_CompositeDepth (IP_ImageId fgimg, IP_ImageId bgimg, IP_ImageId cmimg)
{
    UT_MemState memstate;
    UT_Bool     success;

    memstate = UT_MemRemember ();
    success = comp_zmatte (fgimg, bgimg, cmimg);
    UT_MemRestore (memstate);
    return success;
}

/***************************************************************************
 *[@e
 *      Name:           IP_CrossDissolve
 *
 *      Usage:          Cross-dissolve between two images.
 *
 *      Synopsis:       UT_Bool IP_CrossDissolve
 *                              (IP_ImageId fgimg,
 *                               IP_ImageId bgimg,
 *                               IP_ImageId cmimg,
 *                               Float32 mix)
 *
 *      Description:    IP_CrossDissolve cross-dissolves between image "fgimg"
 *                      and "bgimg". The result is stored in "cmimg":
 *                      
 *                      1. Any gamma-correction in the input data is
 *                         compensated by performing an inverse gamma-
 *                         correction on the pixel data from "fgimg"
 *                         and "bgimg".
 *
 *                      2. The the pixel data for "cmimg" are combined from
 *                         the data in "fgimg" and "bgimg" ("mix" should be
 *                         in the range from 0.0 to 1.0):
 *                          
 *                             cm.pixel = fg.pixel * mix + bg.pixel * (1-mix)
 *
 *                      3. The FF_ImgChanTypeGray, FF_ImgChanTypeRed,
 *                         FF_ImgChanTypeGreen, and FF_ImgChanTypeBlue channels
 *                         in "cmimg" are gamma-corrected according to the image
 *                         descriptor of "cmimg".
 *
 *                      Notes:
 *
 *                      - If necessary, "fgimg" or "bgimg" can be
 *                        identical to "cmimg", so that IP_CrossDissolve
 *                        overwrites one of the input images with the
 *                        result.
 *
 *                      - IP_CrossDissolve does not write to any channels in
 *                        "cmimg" for which the drawing mask is "UT_False".
 *
 *                      - IP_CrossDissolve does not write to the 
 *                        FF_ImgChanTypeHoriSnr, FF_ImgChanTypeVertSnr and
 *                        FF_ImgChanTypeDepth channels in "cmimg",
 *                        even if the drawing mask for these channels is
 *                        "UT_True".
 *
 *                      - For cross-dissolving, "fgimg", "bgimg" and
 *                        "cmimg" are aligned at the lower left corner.
 *                        If "cmimg" is wider or higher than "fgimg" or
 *                        "bgimg", IP_CrossDissolve does not change areas
 *                        in "cmimg" which are not covered by "fgimg" or
 *                        "bgimg".
 *
 *                      - IP_CrossDissolve can be run concurrently on
 *                        multiple processors; see IP_SetNumThreads.
 *
 *      Return value:   UT_True if successful, else UT_False.
 *
 *      See also:
 *
 ***************************************************************************/

UT_Bool IP_CrossDissolve
        (IP_ImageId fgimg, IP_ImageId bgimg, IP_ImageId cmimg, Float32 mix)
{
    UT_MemState memstate;
    UT_Bool     success;

    memstate = UT_MemRemember ();
    success = crossimage (fgimg, bgimg, cmimg, mix);
    UT_MemRestore (memstate);
    return success;
}
