pro_image_editor icon indicating copy to clipboard operation
pro_image_editor copied to clipboard

[Bug]: Cropping clamp errors due to certain aspect ratios

Open diegotori opened this issue 7 months ago • 0 comments

Package Version

9.9.0

Flutter Version

3.29

Platforms

Linux, macOS, Windows, Web, iOS, Android

How to reproduce?

Using the crop_to_main_editor.dart example, create a CropRotateEditorConfigs with an initAspectRatio of 20/3.

  final ProImageEditorConfigs _editorConfigs = ProImageEditorConfigs(
    designMode: platformDesignMode,
    cropRotateEditor: const CropRotateEditorConfigs(
      initAspectRatio: 20/3,
      enableProvideImageInfos: true,
      showAspectRatioButton: false,
    ),
  );

Once you display this example, attempt to resize the crop area vertically.

Notice that the lowerLimit of the clamp is greater than its upperLimit.

The only way I was able to not get it to crash was to modify the switch statement in CropRotateEditorState._onScaleUpdate so that the clamp's lower bound was the smallest of either the original lower bound for that section or its upper bound:

          switch (_currentCropAreaPart) {
            case CropAreaPart.topLeft:
              cropRect = Rect.fromLTRB(
                dx.clamp(minLeft, maxRight),
                // fix
                dy.clamp(min(minTop, maxBottom), maxBottom),
                cropRect.right,
                cropRect.bottom,
              );

              break;
            case CropAreaPart.topRight:
              cropRect = Rect.fromLTRB(
                cropRect.left,
                // fix
                dy.clamp(min(minTop, maxBottom), maxBottom),
                dx.clamp(cornerGap + cropRect.left, minRight),
                cropRect.bottom,
              );

              break;
            case CropAreaPart.bottomLeft:
              cropRect = Rect.fromLTRB(
                dx.clamp(minLeft, maxRight),
                cropRect.top,
                cropRect.right,
                dy.clamp(min(cornerGap + cropRect.top, maxBottom), minBottom),
              );
              break;
            case CropAreaPart.bottomRight:
              cropRect = Rect.fromLTRB(
                cropRect.left,
                cropRect.top,
                dx.clamp(cornerGap + cropRect.left, minRight),
                // fix
                dy.clamp(min(cornerGap + cropRect.top, minBottom), minBottom),
              );
              break;
            case CropAreaPart.left:
              cropRect = Rect.fromLTRB(
                dx.clamp(minLeft, maxRight),
                cropRect.top,
                cropRect.right,
                cropRect.bottom,
              );
              _setOffsetLimits();
              break;
            case CropAreaPart.right:
              cropRect = Rect.fromLTRB(
                cropRect.left,
                cropRect.top,
                dx.clamp(cornerGap + cropRect.left, minRight),
                cropRect.bottom,
              );
              break;
            case CropAreaPart.top:
              cropRect = Rect.fromLTRB(
                cropRect.left,
                // fix
                dy.clamp(minTop, max(cornerGap + cropRect.bottom, minBottom)),
                cropRect.right,
                cropRect.bottom,
              );
              break;
            case CropAreaPart.bottom:
              cropRect = Rect.fromLTRB(
                cropRect.left,
                cropRect.top,
                cropRect.right,
                // fix
                dy.clamp(min(cornerGap + cropRect.top, minBottom), minBottom),
              );
              break;
            default:
              break;
          }

However, I dunno if this is the correct way to determine the clamp's true lower bounds.

Note that if you use a portrait based aspect ratio (e.g. 5/7), it doesn't run into this issue. Perhaps it's not accounting the orientation of the crop area.

Logs (optional)

======== Exception caught by gesture ===============================================================
The following ArgumentError was thrown while handling a gesture:
Invalid argument(s): 298.9714285714286

When the exception was thrown, this was the stack: 
#0      double.clamp (dart:core-patch/double.dart:232:7)
#1      CropRotateEditorState._onScaleUpdate (package:pro_image_editor/features/crop_rotate_editor/crop_rotate_editor.dart:1594:20)
#2      ScaleGestureRecognizer._advanceStateMachine.<anonymous closure> (package:flutter/src/gestures/scale.dart:746:20)
#3      GestureRecognizer.invokeCallback (package:flutter/src/gestures/recognizer.dart:357:24)
#4      ScaleGestureRecognizer._advanceStateMachine (package:flutter/src/gestures/scale.dart:745:9)
#5      ScaleGestureRecognizer.handleEvent (package:flutter/src/gestures/scale.dart:573:7)
#6      PointerRouter._dispatch (package:flutter/src/gestures/pointer_router.dart:97:12)
#7      PointerRouter._dispatchEventToRoutes.<anonymous closure> (package:flutter/src/gestures/pointer_router.dart:143:9)
#8      _LinkedHashMapMixin.forEach (dart:_compact_hash:763:13)
#9      PointerRouter._dispatchEventToRoutes (package:flutter/src/gestures/pointer_router.dart:141:18)
#10     PointerRouter.route (package:flutter/src/gestures/pointer_router.dart:131:7)
#11     GestureBinding.handleEvent (package:flutter/src/gestures/binding.dart:530:19)
#12     GestureBinding.dispatchEvent (package:flutter/src/gestures/binding.dart:499:22)
#13     RendererBinding.dispatchEvent (package:flutter/src/rendering/binding.dart:460:11)
#14     GestureBinding._handlePointerEventImmediately (package:flutter/src/gestures/binding.dart:437:7)
#15     GestureBinding.handlePointerEvent (package:flutter/src/gestures/binding.dart:394:5)
#16     GestureBinding._flushPointerEventQueue (package:flutter/src/gestures/binding.dart:341:7)
#17     GestureBinding._handlePointerDataPacket (package:flutter/src/gestures/binding.dart:308:9)
#18     _invoke1 (dart:ui/hooks.dart:332:13)
#19     PlatformDispatcher._dispatchPointerDataPacket (dart:ui/platform_dispatcher.dart:451:7)
#20     _dispatchPointerDataPacket (dart:ui/hooks.dart:267:31)
Handler: "onUpdate"
Recognizer: ScaleGestureRecognizer#c9ffc
  debugOwner: CropRotateGestureDetectorState#a5af0
====================================================================================================

diegotori avatar May 20 '25 22:05 diegotori