The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#include "imager.h"
#include "imrender.h"
#include "imageri.h"

int
i_compose_mask(i_img *out, i_img *src, i_img *mask, 
	       i_img_dim out_left, i_img_dim out_top,
	       i_img_dim src_left, i_img_dim src_top,
	       i_img_dim mask_left, i_img_dim mask_top,
	       i_img_dim width, i_img_dim height,
	       int combine,
	       double opacity) {
  i_render r;
  i_img_dim dy;
  i_fill_combine_f combinef_8;
  i_fill_combinef_f combinef_double;
  int channel_zero = 0;

  mm_log((1, "i_compose_mask(out %p, src %p, mask %p, out(" i_DFp "), "
	  "src(" i_DFp "), mask(" i_DFp "), size(" i_DFp "),"
	  " combine %d opacity %f\n", out, src, 
	  mask, i_DFcp(out_left, out_top), i_DFcp(src_left, src_top),
	  i_DFcp(mask_left, mask_top), i_DFcp(width, height),
	  combine, opacity));

  i_clear_error();
  if (out_left >= out->xsize
      || out_top >= out->ysize
      || src_left >= src->xsize
      || src_top >= src->ysize
      || width <= 0
      || height <= 0
      || out_left + width <= 0
      || out_top + height <= 0
      || src_left + width <= 0
      || src_top + height <= 0
      || mask_left >= mask->xsize
      || mask_top >= mask->ysize
      || mask_left + width <= 0
      || mask_top + height <= 0)
    return 0;

  if (out_left < 0) {
    width = out_left + width;
    src_left -= out_left;
    mask_left -= out_left;
    out_left = 0;
  }
  if (out_left + width > out->xsize)
    width = out->xsize - out_left;

  if (out_top < 0) {
    height = out_top + height;
    mask_top -= out_top;
    src_top -= out_top;
    out_top = 0;
  }
  if (out_top + height > out->ysize)
    height = out->ysize - out_top;

  if (src_left < 0) {
    width = src_left + width;
    out_left -= src_left;
    mask_left -= src_left;
    src_left = 0;
  }
  if (src_left + width > src->xsize)
    width = src->xsize - src_left;

  if (src_top < 0) {
    height = src_top + height;
    out_top -= src_top;
    mask_top -= src_top;
    src_top = 0;
  }
  if (src_top + height > src->ysize)
    height = src->ysize - src_top;

  if (mask_left < 0) {
    width = mask_left + width;
    out_left -= mask_left;
    src_left -= mask_left;
    mask_left = 0;
  }
  if (mask_left + width > mask->xsize)
    width = mask->xsize - mask_left;
  
  if (mask_top < 0) {
    height = mask_top + height;
    src_top -= mask_top;
    out_top -= mask_top;
    mask_top = 0;
  }
  if (mask_top + height > mask->ysize)
    height = mask->ysize - mask_top;

  if (opacity > 1.0)
    opacity = 1.0;
  else if (opacity <= 0) {
    i_push_error(0, "opacity must be positive");
    return 0;
  }

  mm_log((1, "after adjustments: (out(" i_DFp "), src(" i_DFp "),"
	  " mask(" i_DFp "), size(" i_DFp ")\n", 
	  i_DFcp(out_left, out_top), i_DFcp(src_left, src_top),
	  i_DFcp(mask_left, mask_top), i_DFcp(width, height)));

  i_get_combine(combine, &combinef_8, &combinef_double);

  i_render_init(&r, out, width);
#code out->bits <= 8 && src->bits<= 8 && mask->bits <= 8
  IM_COLOR *src_line = mymalloc(sizeof(IM_COLOR) * width);
  IM_SAMPLE_T *mask_line = mymalloc(sizeof(IM_SAMPLE_T) * width);
  int adapt_channels = out->channels;

  if (adapt_channels == 1 || adapt_channels == 3)
    ++adapt_channels;

  for (dy = 0; dy < height; ++dy) {
    IM_GLIN(src, src_left, src_left + width, src_top + dy, src_line);
    IM_ADAPT_COLORS(adapt_channels, src->channels, src_line, width);
    IM_GSAMP(mask, mask_left, mask_left + width, mask_top + dy, 
	     mask_line, &channel_zero, 1);
    if (opacity < 1.0) {
      i_img_dim i;
      IM_SAMPLE_T *maskp = mask_line;
      for (i = 0; i < width; ++i) {
	*maskp = IM_ROUND(*maskp * opacity);
	++maskp;
      }
    }
    IM_RENDER_LINE(&r, out_left, out_top+dy, width, mask_line, src_line,
		   IM_SUFFIX(combinef));
  }
  myfree(src_line);
  myfree(mask_line);
  
#/code
  i_render_done(&r);

  return 1;
}

int
i_compose(i_img *out, i_img *src,
	  i_img_dim out_left, i_img_dim out_top,
	  i_img_dim src_left, i_img_dim src_top,
	  i_img_dim width, i_img_dim height,
	  int combine,
	  double opacity) {
  i_render r;
  i_img_dim dy;
  i_fill_combine_f combinef_8;
  i_fill_combinef_f combinef_double;

  mm_log((1, "i_compose(out %p, src %p, out(" i_DFp "), src(" i_DFp "), "
	  "size(" i_DFp "), combine %d opacity %f\n", out, src,
	  i_DFcp(out_left, out_top), i_DFcp(src_left, src_top),
	  i_DFcp(width, height), combine, opacity));

  i_clear_error();
  if (out_left >= out->xsize
      || out_top >= out->ysize
      || src_left >= src->xsize
      || src_top >= src->ysize
      || width <= 0
      || height <= 0
      || out_left + width <= 0
      || out_top + height <= 0
      || src_left + width <= 0
      || src_top + height <= 0)
    return 0;

  if (out_left < 0) {
    width = out_left + width;
    src_left -= out_left;
    out_left = 0;
  }
  if (out_left + width > out->xsize)
    width = out->xsize - out_left;

  if (out_top < 0) {
    height = out_top + height;
    src_top -= out_top;
    out_top = 0;
  }
  if (out_top + height > out->ysize)
    height = out->ysize - out_top;

  if (src_left < 0) {
    width = src_left + width;
    out_left -= src_left;
    src_left = 0;
  }
  if (src_left + width > src->xsize)
    width = src->xsize - src_left;

  if (src_top < 0) {
    height = src_top + height;
    out_top -= src_top;
    src_top = 0;
  }
  if (src_top + height > src->ysize)
    height = src->ysize - src_top;

  if (opacity > 1.0)
    opacity = 1.0;
  else if (opacity <= 0) {
    i_push_error(0, "opacity must be positive");
    return 0;
  }

  i_get_combine(combine, &combinef_8, &combinef_double);

  i_render_init(&r, out, width);
#code out->bits <= 8 && src->bits <= 8
  IM_COLOR *src_line = mymalloc(sizeof(IM_COLOR) * width);
  IM_SAMPLE_T *mask_line = NULL;
  int adapt_channels = out->channels;

  if (opacity != 1.0) {
    i_img_dim i;
    IM_SAMPLE_T mask_value = IM_ROUND(opacity * IM_SAMPLE_MAX);
    mask_line = mymalloc(sizeof(IM_SAMPLE_T) * width);

    for (i = 0; i < width; ++i)
      mask_line[i] = mask_value;
  }

  if (adapt_channels == 1 || adapt_channels == 3)
    ++adapt_channels;

  for (dy = 0; dy < height; ++dy) {
    IM_GLIN(src, src_left, src_left + width, src_top + dy, src_line);
    IM_ADAPT_COLORS(adapt_channels, src->channels, src_line, width);
    IM_RENDER_LINE(&r, out_left, out_top+dy, width, mask_line, src_line,
		   IM_SUFFIX(combinef));
  }
  myfree(src_line);
  if (mask_line)
    myfree(mask_line);
  
#/code
  i_render_done(&r);

  return 1;
}