paintImageToo function

void paintImageToo({
  1. required Canvas canvas,
  2. required Rect rect,
  3. required Image image,
  4. String? debugImageLabel,
  5. double scale = 1.0,
  6. ColorFilter? colorFilter,
  7. BoxFit? fit,
  8. Alignment alignment = Alignment.center,
  9. Rect? centerSlice,
  10. Repeat repeat = Repeat.noRepeat,
  11. Offset mirrorOffset = Offset.zero,
  12. bool flipHorizontally = false,
  13. bool invertColors = false,
  14. FilterQuality filterQuality = FilterQuality.low,
  15. bool isAntiAlias = false,
})

Paints an image into the given rectangle on the canvas.

The arguments have the following meanings:

  • canvas: The canvas onto which the image will be painted.

  • rect: The region of the canvas into which the image will be painted. The image might not fill the entire rectangle (e.g., depending on the fit). If rect is empty, nothing is painted.

  • image: The image to paint onto the canvas.

  • scale: The number of image pixels for each logical pixel.

  • colorFilter: If non-null, the color filter to apply when painting the image.

  • fit: How the image should be inscribed into rect. If null, the default behavior depends on centerSlice. If centerSlice is also null, the default behavior is BoxFit.scaleDown. If centerSlice is non-null, the default behavior is BoxFit.fill. See BoxFit for details.

  • alignment: How the destination rectangle defined by applying fit is aligned within rect. For example, if fit is BoxFit.contain and alignment is Alignment.bottomRight, the image will be as large as possible within rect and placed with its bottom right corner at the bottom right corner of rect.
    Defaults to Alignment.center. Reverts to Alignment.center if repeat is a Repeat.mirror value. See then: mirrorOffset.

  • centerSlice: The image is drawn in nine portions described by splitting the image by drawing two horizontal lines and two vertical lines, where centerSlice describes the rectangle formed by the four points where these four lines intersect each other. (This forms a 3-by-3 grid of regions, the center region being described by centerSlice.) The four regions in the corners are drawn, without scaling, in the four corners of the destination rectangle defined by applying fit. The remaining five regions are drawn by stretching them to fit such that they exactly cover the destination rectangle while maintaining their relative positions.

  • repeat: If the image does not fill rect, whether and how the image should be repeated to fill rect. By default, the image is not repeated. See Repeat for details. This is the core difference between this method and the standard paintImage.

  • mirrorOffset: If the image is repeated with a Repeat.mirror value, then alignment is reverted to Alignment.center. In that case, use this Offset ranging 0..maxAxisResolution (resolution of image in px) to shift the mirroring tiles.

  • flipHorizontally: Whether to flip the image horizontally. This is occasionally used with images in right-to-left environments, for images that were designed for left-to-right locales (or vice versa). Be careful, when using this, to not flip images with integral shadows, text, or other effects that will look incorrect when flipped.

  • invertColors: Inverting the colors of an image applies a new color filter to the paint. If there is another specified color filter, the invert will be applied after it. This is primarily used for implementing smart invert on iOS.

  • filterQuality: Use this to change the quality when scaling an image. Use the FilterQuality.low quality setting to scale the image, which corresponds to bilinear interpolation, rather than the default FilterQuality.none which corresponds to nearest-neighbor.

The canvas, rect, image, scale, alignment, repeat, flipHorizontally and filterQuality arguments must not be null.

See also:

  • paintBorder, which paints a border around a rectangle on a canvas.
  • ImageToo, a Widget that creates a chain of rendering objects that eventually call this method.
  • DecorationImageToo, which holds a configuration for calling this function.
  • BoxDecoration, which uses this function to paint a DecorationImageToo.

Implementation

void paintImageToo({
  required Canvas canvas,
  required Rect rect,
  required ui.Image image,
  String? debugImageLabel,
  double scale = 1.0,
  ColorFilter? colorFilter,
  BoxFit? fit,
  Alignment alignment = Alignment.center,
  Rect? centerSlice,
  Repeat repeat = Repeat.noRepeat,
  Offset mirrorOffset = Offset.zero,
  bool flipHorizontally = false,
  bool invertColors = false,
  FilterQuality filterQuality = FilterQuality.low,
  bool isAntiAlias = false,
}) {
  assert(
    image.debugGetOpenHandleStackTraces()?.isNotEmpty ?? true,
    'Cannot paint an image that is disposed.\n'
    'The caller of paintImage is expected to wait to dispose the image until '
    'after painting has completed.',
  );
  if (rect.isEmpty) {
    return;
  }
  var sizeOut = rect.size;
  var sizeIn = Size(image.width.toDouble(), image.height.toDouble());
  Offset? sliceBorder;
  if (centerSlice != null) {
    sliceBorder = sizeIn / scale - centerSlice.size as Offset;
    sizeOut = sizeOut - sliceBorder as Size;
    sizeIn = sizeIn - sliceBorder * scale as Size;
  }
  fit ??= centerSlice == null ? BoxFit.scaleDown : BoxFit.fill;
  assert(centerSlice == null || (fit != BoxFit.none && fit != BoxFit.cover));
  final fittedSizes = applyBoxFit(fit, sizeIn / scale, sizeOut);
  final sizeSource = fittedSizes.source * scale;
  var sizeDest = fittedSizes.destination;
  if (centerSlice != null) {
    sizeOut += sliceBorder!;
    sizeDest += sliceBorder;
    // We don't have the ability to draw a subset of the image at the same time
    // as we apply a nine-patch stretch.
    assert(
        sizeSource == sizeIn,
        'centerSlice was used with a BoxFit that '
        'does not guarantee that the image is fully visible.');
  }

  if (repeat != Repeat.noRepeat && sizeDest == sizeOut) {
    repeat = Repeat.noRepeat;
  }

  /// TODO: Properly fix `alignment`.
  alignment = (repeat == Repeat.mirror ||
          repeat == Repeat.mirrorX ||
          repeat == Repeat.mirrorY)
      ? Alignment.center
      : alignment;

  final paint = Paint()
    ..isAntiAlias = isAntiAlias
    ..filterQuality = filterQuality
    ..invertColors = invertColors;
  if (colorFilter != null) {
    paint.colorFilter = colorFilter;
  }

  final halfWidthDelta = (sizeOut.width - sizeDest.width) / 2.0;
  final halfHeightDelta = (sizeOut.height - sizeDest.height) / 2.0;
  final dx = halfWidthDelta +
      (flipHorizontally ? -alignment.x : alignment.x) * halfWidthDelta;
  final dy = halfHeightDelta + alignment.y * halfHeightDelta;
  final alignedOffset = rect.topLeft.translate(dx, dy);
  final alignedRect = alignedOffset & sizeDest;

  // Set to true if we added a saveLayer to the canvas to invert/flip the image.
  var invertedCanvas = false;
  // Output size and destination rect are fully calculated.
  if (!kReleaseMode) {
    invertedCanvas = _debug(debugImageLabel, image, sizeOut, canvas,
        alignedRect, rect, invertedCanvas);
  }

  final needSave =
      centerSlice != null || repeat != Repeat.noRepeat || flipHorizontally;
  if (needSave) canvas.save();
  if (repeat != Repeat.noRepeat) canvas.clipRect(rect);

  final recenterX = -(rect.left + rect.width / 2.0);
  final recenterY = -(rect.top + rect.height / 2.0);
  if (flipHorizontally) {
    _mirrorX(canvas, recenterY);
  }

  /// No Center Slicing
  if (centerSlice == null) {
    final imageRect = alignment.inscribe(
      sizeSource,
      Offset.zero & sizeIn,
    );
    if (repeat == Repeat.noRepeat) {
      canvas.drawImageRect(image, imageRect, alignedRect, paint);
    } else {
      canvas.translate(mirrorOffset.dx, mirrorOffset.dy);
      for (final tileMap
          in _generateImageTileRects(rect, alignedRect, repeat, mirrorOffset)) {
        if (repeat == Repeat.mirror ||
            repeat == Repeat.mirrorX ||
            repeat == Repeat.mirrorY) {
          _drawWithMirroring(canvas, image, imageRect, tileMap,
              Offset(recenterX, recenterY), repeat, paint);

          /// Repeat.repeat || repeatX || repeatY
        } else {
          canvas.drawImageRect(image, imageRect, tileMap.keys.single, paint);
        }
      }
    }

    /// Center Slicing
  } else {
    canvas.scale(1 / scale);
    if (repeat == Repeat.noRepeat) {
      canvas.drawImageNine(image, _scaleRect(centerSlice, scale),
          _scaleRect(alignedRect, scale), paint);
    } else {
      for (final tileMap
          in _generateImageTileRects(rect, alignedRect, repeat)) {
        canvas.drawImageNine(image, _scaleRect(centerSlice, scale),
            _scaleRect(tileMap.keys.single, scale), paint);
      }
    }
  }
  if (needSave) canvas.restore();

  if (invertedCanvas) {
    canvas.restore();
  }
}