applyBoxFit function
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.
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:
- FittedBox, a widget that applies this algorithm to another widget.
- paintImage, a function that applies this algorithm to images for painting.
- DecoratedBox, BoxDecoration, and DecorationImage, which together provide access to paintImage at the widgets layer.
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;
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);
}
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;
case BoxFit.fitWidth:
if (outputSize.width / outputSize.height > inputSize.width / inputSize.height) {
// Like "cover"
sourceSize = Size(inputSize.width, inputSize.width * outputSize.height / outputSize.width);
destinationSize = outputSize;
} else {
// Like "contain"
sourceSize = inputSize;
destinationSize = Size(outputSize.width, sourceSize.height * outputSize.width / sourceSize.width);
}
case BoxFit.fitHeight:
if (outputSize.width / outputSize.height > inputSize.width / inputSize.height) {
// Like "contain"
sourceSize = inputSize;
destinationSize = Size(sourceSize.width * outputSize.height / sourceSize.height, outputSize.height);
} else {
// Like "cover"
sourceSize = Size(inputSize.height * outputSize.width / outputSize.height, inputSize.height);
destinationSize = outputSize;
}
case BoxFit.none:
sourceSize = Size(math.min(inputSize.width, outputSize.width), math.min(inputSize.height, outputSize.height));
destinationSize = sourceSize;
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);
}
}
return FittedSizes(sourceSize, destinationSize);
}