applyBoxFit function

FittedSizes applyBoxFit (BoxFit fit, Size inputSize, Size outputSize)

Apply a BoxFit value.

The arguments to this method, in addition to the BoxFit value to apply, are two sizes, ostensibly the sizes of an input box and an output box. Specifically, the inputSize argument gives the size of the complete source that is being fitted, and the outputSize gives the size of the rectangle into which the source is to be drawn.

This function then returns two sizes, combined into a single FittedSizes object.

The FittedSizes.source size is the subpart of the inputSize that is to be shown. If the entire input source is shown, then this will equal the inputSize, but if the input source is to be cropped down, this may be smaller.

The FittedSizes.destination size is the subpart of the outputSize in which to paint the (possibly cropped) source. If the FittedSizes.destination size is smaller than the outputSize then the source is being letterboxed (or pillarboxed).

This method does not express an opinion regarding the alignment of the source and destination sizes within the input and output rectangles. Typically they are centered (this is what BoxDecoration does, for instance, and is how BoxFit is defined). The Alignment class provides a convenience function, Alignment.inscribe, for resolving the sizes to rects, as shown in the example below.

This function paints a dart:ui.Image image onto the Rect outputRect on a Canvas canvas, using a Paint paint, applying the BoxFit algorithm fit:
void paintImage(ui.Image image, Rect outputRect, Canvas canvas, Paint paint, BoxFit fit) {
  final Size imageSize = Size(image.width.toDouble(), image.height.toDouble());
  final FittedSizes sizes = applyBoxFit(fit, imageSize, outputRect.size);
  final Rect inputSubrect = Alignment.center.inscribe(sizes.source, Offset.zero & imageSize);
  final Rect outputSubrect = Alignment.center.inscribe(sizes.destination, outputRect);
  canvas.drawImageRect(image, inputSubrect, outputSubrect, paint);
}

See also:

Implementation

FittedSizes applyBoxFit(BoxFit fit, Size inputSize, Size outputSize) {
  if (inputSize.height <= 0.0 || inputSize.width <= 0.0 || outputSize.height <= 0.0 || outputSize.width <= 0.0)
    return const FittedSizes(Size.zero, Size.zero);

  Size sourceSize, destinationSize;
  switch (fit) {
    case BoxFit.fill:
      sourceSize = inputSize;
      destinationSize = outputSize;
      break;
    case BoxFit.contain:
      sourceSize = inputSize;
      if (outputSize.width / outputSize.height > sourceSize.width / sourceSize.height)
        destinationSize = Size(sourceSize.width * outputSize.height / sourceSize.height, outputSize.height);
      else
        destinationSize = Size(outputSize.width, sourceSize.height * outputSize.width / sourceSize.width);
      break;
    case BoxFit.cover:
      if (outputSize.width / outputSize.height > inputSize.width / inputSize.height) {
        sourceSize = Size(inputSize.width, inputSize.width * outputSize.height / outputSize.width);
      } else {
        sourceSize = Size(inputSize.height * outputSize.width / outputSize.height, inputSize.height);
      }
      destinationSize = outputSize;
      break;
    case BoxFit.fitWidth:
      sourceSize = Size(inputSize.width, inputSize.width * outputSize.height / outputSize.width);
      destinationSize = Size(outputSize.width, sourceSize.height * outputSize.width / sourceSize.width);
      break;
    case BoxFit.fitHeight:
      sourceSize = Size(inputSize.height * outputSize.width / outputSize.height, inputSize.height);
      destinationSize = Size(sourceSize.width * outputSize.height / sourceSize.height, outputSize.height);
      break;
    case BoxFit.none:
      sourceSize = Size(math.min(inputSize.width, outputSize.width),
                            math.min(inputSize.height, outputSize.height));
      destinationSize = sourceSize;
      break;
    case BoxFit.scaleDown:
      sourceSize = inputSize;
      destinationSize = inputSize;
      final double aspectRatio = inputSize.width / inputSize.height;
      if (destinationSize.height > outputSize.height)
        destinationSize = Size(outputSize.height * aspectRatio, outputSize.height);
      if (destinationSize.width > outputSize.width)
        destinationSize = Size(outputSize.width, outputSize.width / aspectRatio);
      break;
  }
  return FittedSizes(sourceSize, destinationSize);
}