paintExtendedImage function

void paintExtendedImage({
  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. ImageRepeat repeat = ImageRepeat.noRepeat,
  11. bool flipHorizontally = false,
  12. bool invertColors = false,
  13. FilterQuality filterQuality = FilterQuality.low,
  14. Rect? customSourceRect,
  15. BeforePaintImage? beforePaintImage,
  16. AfterPaintImage? afterPaintImage,
  17. GestureDetails? gestureDetails,
  18. EditActionDetails? editActionDetails,
  19. bool isAntiAlias = false,
})

Implementation

void paintExtendedImage({
  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,
  ImageRepeat repeat = ImageRepeat.noRepeat,
  bool flipHorizontally = false,
  bool invertColors = false,
  FilterQuality filterQuality = FilterQuality.low,
  Rect? customSourceRect,
  //you can paint anything if you want before paint image.
  BeforePaintImage? beforePaintImage,
  //you can paint anything if you want after paint image.
  AfterPaintImage? afterPaintImage,
  GestureDetails? gestureDetails,
  EditActionDetails? editActionDetails,
  bool isAntiAlias = false,
}) {
  if (rect.isEmpty) {
    return;
  }

  Size outputSize = rect.size;
  Size inputSize = Size(image.width.toDouble(), image.height.toDouble());

  final Offset topLeft = rect.topLeft;

  // if (editActionDetails != null && editActionDetails.isHalfPi) {
  //   outputSize = Size(outputSize.height, outputSize.width);
  //   var center = rect.center;
  //   topLeft = Rect.fromLTWH(center.dx - rect.height / 2.0,
  //           center.dy - rect.width / 2.0, rect.height, rect.width)
  //       .topLeft;
  // }

  late Offset sliceBorder;
  if (centerSlice != null) {
    sliceBorder = Offset(centerSlice.left + inputSize.width - centerSlice.right,
        centerSlice.top + inputSize.height - centerSlice.bottom);
    outputSize = outputSize - sliceBorder as Size;
    inputSize = inputSize - sliceBorder as Size;
  }
  fit ??= centerSlice == null ? BoxFit.scaleDown : BoxFit.fill;
  assert(centerSlice == null || (fit != BoxFit.none && fit != BoxFit.cover));
  final FittedSizes fittedSizes =
      applyBoxFit(fit, inputSize / scale, outputSize);
  final Size sourceSize = fittedSizes.source * scale;
  Size destinationSize = fittedSizes.destination;
  if (centerSlice != null) {
    outputSize += sliceBorder;
    destinationSize += 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(sourceSize == inputSize,
        'centerSlice was used with a BoxFit that does not guarantee that the image is fully visible.');
  }
  if (repeat != ImageRepeat.noRepeat && destinationSize == outputSize) {
    // There's no need to repeat the image because we're exactly filling the
    // output rect with the image.
    repeat = ImageRepeat.noRepeat;
  }
  final Paint paint = Paint()..isAntiAlias = isAntiAlias;
  if (colorFilter != null) {
    paint.colorFilter = colorFilter;
  }
  if (sourceSize != destinationSize) {
    paint.filterQuality = filterQuality;
  }
  paint.invertColors = invertColors;
  final double halfWidthDelta =
      (outputSize.width - destinationSize.width) / 2.0;
  final double halfHeightDelta =
      (outputSize.height - destinationSize.height) / 2.0;
  final double dx = halfWidthDelta +
      (flipHorizontally ? -alignment.x : alignment.x) * halfWidthDelta;
  final double dy = halfHeightDelta + alignment.y * halfHeightDelta;
  final Offset destinationPosition = topLeft.translate(dx, dy);
  Rect destinationRect = destinationPosition & destinationSize;

  bool needClip = false;

  if (gestureDetails != null) {
    destinationRect =
        gestureDetails.calculateFinalDestinationRect(rect, destinationRect);

    ///outside and need clip
    needClip = outRect(rect, destinationRect);

    if (gestureDetails.slidePageOffset != null) {
      destinationRect = destinationRect.shift(gestureDetails.slidePageOffset!);
      rect = rect.shift(gestureDetails.slidePageOffset!);
    }

    if (needClip) {
      canvas.save();
      canvas.clipRect(rect);
    }
  }
  bool hasEditAction = false;
  if (editActionDetails != null) {
    if (editActionDetails.cropRectPadding != null) {
      destinationRect = getDestinationRect(
          inputSize: inputSize,
          rect: editActionDetails.cropRectPadding!.deflateRect(rect),
          fit: fit,
          flipHorizontally: false,
          scale: scale,
          centerSlice: centerSlice,
          alignment: alignment);
    }

    editActionDetails.initRect(rect, destinationRect);

    destinationRect = editActionDetails.getFinalDestinationRect();

    ///outside and need clip
    needClip = outRect(rect, destinationRect);

    hasEditAction = editActionDetails.hasEditAction;

    if (needClip || hasEditAction) {
      canvas.save();
      if (needClip) {
        canvas.clipRect(rect);
      }
    }

    if (hasEditAction) {
      final Offset origin =
          editActionDetails.screenCropRect?.center ?? destinationRect.center;

      final Matrix4 result = Matrix4.identity();

      final EditActionDetails editAction = editActionDetails;

      result.translate(
        origin.dx,
        origin.dy,
      );

      if (editAction.hasRotateAngle) {
        result.multiply(Matrix4.rotationZ(editAction.rotateRadian));
      }

      if (editAction.flipY) {
        result.multiply(Matrix4.rotationY(pi));
      }

      if (editAction.flipX) {
        result.multiply(Matrix4.rotationX(pi));
      }

      result.translate(-origin.dx, -origin.dy);
      canvas.transform(result.storage);
      destinationRect = editAction.paintRect(destinationRect);
    }
  }

  if (beforePaintImage != null) {
    final bool handle = beforePaintImage(canvas, destinationRect, image, paint);
    if (handle) {
      return;
    }
  }

  final bool needSave = repeat != ImageRepeat.noRepeat || flipHorizontally;
  if (needSave) {
    canvas.save();
  }
  if (repeat != ImageRepeat.noRepeat) {
    canvas.clipRect(rect);
  }
  if (flipHorizontally) {
    final double dx = -(rect.left + rect.width / 2.0);
    canvas.translate(-dx, 0.0);
    canvas.scale(-1.0, 1.0);
    canvas.translate(dx, 0.0);
  }

  if (centerSlice == null) {
    final Rect sourceRect = customSourceRect ??
        alignment.inscribe(sourceSize, Offset.zero & inputSize);
    for (final Rect tileRect
        in _generateImageTileRects(rect, destinationRect, repeat)) {
      canvas.drawImageRect(image, sourceRect, tileRect, paint);
    }
  } else {
    for (final Rect tileRect
        in _generateImageTileRects(rect, destinationRect, repeat)) {
      canvas.drawImageNine(image, centerSlice, tileRect, paint);
    }
  }

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

  if (needClip || hasEditAction) {
    canvas.restore();
  }

  if (afterPaintImage != null) {
    afterPaintImage(canvas, destinationRect, image, paint);
  }
}