function composite (images) {
  if (!Array.isArray(images)) {
    throw is.invalidParameterError('images to composite', 'array', images);
  }
  this.options.composite = images.map(image => {
    if (!is.object(image)) {
      throw is.invalidParameterError('image to composite', 'object', image);
    }
    const { raw, density } = image;
    const inputOptions = (raw || density) ? { raw, density } : undefined;
    const composite = {
      input: this._createInputDescriptor(image.input, inputOptions, { allowStream: false }),
      blend: 'over',
      tile: false,
      left: -1,
      top: -1,
      gravity: 0
    };
    if (is.defined(image.blend)) {
      if (is.string(blend[image.blend])) {
        composite.blend = blend[image.blend];
      } else {
        throw is.invalidParameterError('blend', 'valid blend name', image.blend);
      }
    }
    if (is.defined(image.tile)) {
      if (is.bool(image.tile)) {
        composite.tile = image.tile;
      } else {
        throw is.invalidParameterError('tile', 'boolean', image.tile);
      }
    }
    if (is.defined(image.left)) {
      if (is.integer(image.left) && image.left >= 0) {
        composite.left = image.left;
      } else {
        throw is.invalidParameterError('left', 'positive integer', image.left);
      }
    }
    if (is.defined(image.top)) {
      if (is.integer(image.top) && image.top >= 0) {
        composite.top = image.top;
      } else {
        throw is.invalidParameterError('top', 'positive integer', image.top);
      }
    }
    if (composite.left !== composite.top && Math.min(composite.left, composite.top) === -1) {
      throw new Error('Expected both left and top to be set');
    }
    if (is.defined(image.gravity)) {
      if (is.integer(image.gravity) && is.inRange(image.gravity, 0, 8)) {
        composite.gravity = image.gravity;
      } else if (is.string(image.gravity) && is.integer(this.constructor.gravity[image.gravity])) {
        composite.gravity = this.constructor.gravity[image.gravity];
      } else {
        throw is.invalidParameterError('gravity', 'valid gravity', image.gravity);
      }
    }
    return composite;
  });
  return this;
}
function composite (images) {
  if (!Array.isArray(images)) {
    throw is.invalidParameterError('images to composite', 'array', images);
  }
  this.options.composite = images.map(image => {
    if (!is.object(image)) {
      throw is.invalidParameterError('image to composite', 'object', image);
    }
    const { raw, density } = image;
    const inputOptions = (raw || density) ? { raw, density } : undefined;
    const composite = {
      input: this._createInputDescriptor(image.input, inputOptions, { allowStream: false }),
      blend: 'over',
      tile: false,
      left: -1,
      top: -1,
      gravity: 0
    };
    if (is.defined(image.blend)) {
      if (is.string(blend[image.blend])) {
        composite.blend = blend[image.blend];
      } else {
        throw is.invalidParameterError('blend', 'valid blend name', image.blend);
      }
    }
    if (is.defined(image.tile)) {
      if (is.bool(image.tile)) {
        composite.tile = image.tile;
      } else {
        throw is.invalidParameterError('tile', 'boolean', image.tile);
      }
    }
    if (is.defined(image.left)) {
      if (is.integer(image.left) && image.left >= 0) {
        composite.left = image.left;
      } else {
        throw is.invalidParameterError('left', 'positive integer', image.left);
      }
    }
    if (is.defined(image.top)) {
      if (is.integer(image.top) && image.top >= 0) {
        composite.top = image.top;
      } else {
        throw is.invalidParameterError('top', 'positive integer', image.top);
      }
    }
    if (composite.left !== composite.top && Math.min(composite.left, composite.top) === -1) {
      throw new Error('Expected both left and top to be set');
    }
    if (is.defined(image.gravity)) {
      if (is.integer(image.gravity) && is.inRange(image.gravity, 0, 8)) {
        composite.gravity = image.gravity;
      } else if (is.string(image.gravity) && is.integer(this.constructor.gravity[image.gravity])) {
        composite.gravity = this.constructor.gravity[image.gravity];
      } else {
        throw is.invalidParameterError('gravity', 'valid gravity', image.gravity);
      }
    }
    return composite;
  });
  return this;
}
function composite ( images ) { if ( ! Array . isArray ( images ) ) { throw is . invalidParameterError ( 'images to composite' , 'array' , images ) ; } this . options . composite = images . map ( image => { if ( ! is . object ( image ) ) { throw is . invalidParameterError ( 'image to composite' , 'object' , image ) ; } const { raw , density } = image ; const inputOptions = ( raw || density ) ? { raw , density } : undefined ; const composite = { input : this . _createInputDescriptor ( image . input , inputOptions , { allowStream : false } ) , blend : 'over' , tile : false , left : - 1 , top : - 1 , gravity : 0 } ; if ( is . defined ( image . blend ) ) { if ( is . string ( blend [ image . blend ] ) ) { composite . blend = blend [ image . blend ] ; } else { throw is . invalidParameterError ( 'blend' , 'valid blend name' , image . blend ) ; } } if ( is . defined ( image . tile ) ) { if ( is . bool ( image . tile ) ) { composite . tile = image . tile ; } else { throw is . invalidParameterError ( 'tile' , 'boolean' , image . tile ) ; } } if ( is . defined ( image . left ) ) { if ( is . integer ( image . left ) && image . left >= 0 ) { composite . left = image . left ; } else { throw is . invalidParameterError ( 'left' , 'positive integer' , image . left ) ; } } if ( is . defined ( image . top ) ) { if ( is . integer ( image . top ) && image . top >= 0 ) { composite . top = image . top ; } else { throw is . invalidParameterError ( 'top' , 'positive integer' , image . top ) ; } } if ( composite . left !== composite . top && Math . min ( composite . left , composite . top ) === - 1 ) { throw new Error ( 'Expected both left and top to be set' ) ; } if ( is . defined ( image . gravity ) ) { if ( is . integer ( image . gravity ) && is . inRange ( image . gravity , 0 , 8 ) ) { composite . gravity = image . gravity ; } else if ( is . string ( image . gravity ) && is . integer ( this . constructor . gravity [ image . gravity ] ) ) { composite . gravity = this . constructor . gravity [ image . gravity ] ; } else { throw is . invalidParameterError ( 'gravity' , 'valid gravity' , image . gravity ) ; } } return composite ; } ) ; return this ; }
Composite image(s) over the processed (resized, extracted etc.) image.

The images to composite must be the same size or smaller than the processed image.
If both `top` and `left` options are provided, they take precedence over `gravity`.

The `blend` option can be one of `clear`, `source`, `over`, `in`, `out`, `atop`,
`dest`, `dest-over`, `dest-in`, `dest-out`, `dest-atop`,
`xor`, `add`, `saturate`, `multiply`, `screen`, `overlay`, `darken`, `lighten`,
`colour-dodge`, `color-dodge`, `colour-burn`,`color-burn`,
`hard-light`, `soft-light`, `difference`, `exclusion`.

More information about blend modes can be found at
https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsBlendMode
and https://www.cairographics.org/operators/

@example
sharp('input.png')
.rotate(180)
.resize(300)
.flatten( { background: '#ff6600' } )
.composite([{ input: 'overlay.png', gravity: 'southeast' }])
.sharpen()
.withMetadata()
.webp( { quality: 90 } )
.toBuffer()
.then(function(outputBuffer) {
// outputBuffer contains upside down, 300px wide, alpha channel flattened
// onto orange background, composited with overlay.png with SE gravity,
// sharpened, with metadata, 90% quality WebP image data. Phew!
});

@param {Object[]} images - Ordered list of images to composite
@param {Buffer|String} [images[].input] - Buffer containing image data, String containing the path to an image file, or Create object (see bellow)
@param {Object} [images[].input.create] - describes a blank overlay to be created.
@param {Number} [images[].input.create.width]
@param {Number} [images[].input.create.height]
@param {Number} [images[].input.create.channels] - 3-4
@param {String|Object} [images[].input.create.background] - parsed by the [color](https://www.npmjs.org/package/color) module to extract values for red, green, blue and alpha.
@param {String} [images[].blend='over'] - how to blend this image with the image below.
@param {String} [images[].gravity='centre'] - gravity at which to place the overlay.
@param {Number} [images[].top] - the pixel offset from the top edge.
@param {Number} [images[].left] - the pixel offset from the left edge.
@param {Boolean} [images[].tile=false] - set to true to repeat the overlay image across the entire image with the given `gravity`.
@param {Number} [images[].density=72] - number representing the DPI for vector overlay image.
@param {Object} [images[].raw] - describes overlay when using raw pixel data.
@param {Number} [images[].raw.width]
@param {Number} [images[].raw.height]
@param {Number} [images[].raw.channels]
@returns {Sharp}
@throws {Error} Invalid parameters
Composite image ( s ) over the processed ( resized extracted etc . ) image .
composite image