diff --git a/lib/widgets/editor/transform/cropper.dart b/lib/widgets/editor/transform/cropper.dart index f4538970e..a160ef408 100644 --- a/lib/widgets/editor/transform/cropper.dart +++ b/lib/widgets/editor/transform/cropper.dart @@ -90,7 +90,7 @@ class _CropperState extends State with SingleTickerProviderStateMixin { void _registerWidget(Cropper widget) { _subscriptions.add(widget.magnifierController.stateStream.listen(_onViewStateChanged)); - _subscriptions.add(widget.magnifierController.scaleBoundariesStream.map((v) => v.viewportSize).listen(_onViewportSizeChanged)); + _subscriptions.add(widget.magnifierController.scaleBoundariesStream.map((v) => v.viewportSize).distinct().listen(_onViewportSizeChanged)); _subscriptions.add(widget.transformController.activityStream.listen(_onTransformActivity)); _subscriptions.add(widget.transformController.transformationStream.map((v) => v.orientation).distinct().listen(_onOrientationChanged)); _subscriptions.add(widget.transformController.transformationStream.map((v) => v.straightenDegrees).distinct().listen(_onStraightenDegreesChanged)); @@ -454,13 +454,41 @@ class _CropperState extends State with SingleTickerProviderStateMixin { if (boundaries != null) { magnifierController.setScaleBoundaries( boundaries.copyWith( - padding: const EdgeInsets.all(double.infinity), + padding: _getBoundariesPadding, ), ); } _showRegion(); } + EdgeInsets _getBoundariesPadding(double scale) { + // TODO TLAD handle orientation + if (transformation.orientation != TransformOrientation.normal) { + return const EdgeInsets.all(double.infinity); + } + // TODO TLAD handle straightening + if (transformation.straightenDegrees != 0) { + return const EdgeInsets.all(double.infinity); + } + + final viewState = _getViewState(); + if (viewState != null) { + final viewportSize = viewState.viewportSize; + final contentSize = viewState.contentSize; + if (viewportSize != null && contentSize != null) { + final fullRegion = CropRegion.fromRect(Offset.zero & contentSize); + final fullOutline = _containingOutlineFromRegion(viewState, fullRegion); + final cropOutline = _outlineNotifier.value; + + final paddingWidth = max(0.0, (min(fullOutline.width, viewportSize.width) - cropOutline.width) / 2); + final paddingHeight = max(0.0, (min(fullOutline.height, viewportSize.height) - cropOutline.height) / 2); + return EdgeInsets.symmetric(vertical: paddingHeight, horizontal: paddingWidth); + } + } + + return EdgeInsets.zero; + } + ViewState? _getViewState() { final scaleBoundaries = magnifierController.scaleBoundaries; if (scaleBoundaries == null) return null; @@ -560,6 +588,16 @@ class _CropperState extends State with SingleTickerProviderStateMixin { return clampedRegion; } + Rect _containingOutlineFromRegion(ViewState viewState, CropRegion region) { + final regionToOutlineMatrix = _getRegionToOutlineMatrix(viewState); + final points = region.corners.map(regionToOutlineMatrix.transformOffset).toList(); + final dxSet = points.map((v) => v.dx).toSet(); + final dySet = points.map((v) => v.dy).toSet(); + final topLeft = Offset(dxSet.reduce(min), dySet.reduce(min)); + final bottomRight = Offset(dxSet.reduce(max), dySet.reduce(max)); + return Rect.fromPoints(topLeft, bottomRight); + } + Rect _containedOutlineFromRegion(ViewState viewState, CropRegion region) { final regionToOutlineMatrix = _getRegionToOutlineMatrix(viewState); final points = region.corners.map(regionToOutlineMatrix.transformOffset).toList(); diff --git a/plugins/aves_magnifier/lib/src/scale/scale_boundaries.dart b/plugins/aves_magnifier/lib/src/scale/scale_boundaries.dart index d9516d610..d738edb6b 100644 --- a/plugins/aves_magnifier/lib/src/scale/scale_boundaries.dart +++ b/plugins/aves_magnifier/lib/src/scale/scale_boundaries.dart @@ -16,7 +16,7 @@ class ScaleBoundaries extends Equatable { final ScaleLevel _initialScale; final Size viewportSize; final Size contentSize; - final EdgeInsets padding; + final EdgeInsets Function(double scale)? padding; final Matrix4? externalTransform; static const Alignment basePosition = Alignment.center; @@ -31,7 +31,7 @@ class ScaleBoundaries extends Equatable { required ScaleLevel initialScale, required this.viewportSize, required this.contentSize, - this.padding = EdgeInsets.zero, + this.padding, this.externalTransform, }) : _allowOriginalScaleBeyondRange = allowOriginalScaleBeyondRange, _minScale = minScale, @@ -45,7 +45,7 @@ class ScaleBoundaries extends Equatable { initialScale: ScaleLevel(ref: ScaleReference.contained), viewportSize: Size.zero, contentSize: Size.zero, - padding: EdgeInsets.zero, + padding: null, ); ScaleBoundaries copyWith({ @@ -55,7 +55,7 @@ class ScaleBoundaries extends Equatable { ScaleLevel? initialScale, Size? viewportSize, Size? contentSize, - EdgeInsets? padding, + EdgeInsets Function(double scale)? padding, Matrix4? externalTransform, }) { return ScaleBoundaries( @@ -115,7 +115,8 @@ class ScaleBoundaries extends Equatable { final minX = ((positionX - 1).abs() / 2) * widthDiff * -1; final maxX = ((positionX + 1).abs() / 2) * widthDiff; - return EdgeRange(minX - padding.left, maxX + padding.right); + final _padding = padding?.call(scale) ?? EdgeInsets.zero; + return EdgeRange(minX - _padding.left, maxX + _padding.right); } EdgeRange getYEdges({required double scale}) { @@ -127,7 +128,8 @@ class ScaleBoundaries extends Equatable { final minY = ((positionY - 1).abs() / 2) * heightDiff * -1; final maxY = ((positionY + 1).abs() / 2) * heightDiff; - return EdgeRange(minY - padding.top, maxY + padding.bottom); + final _padding = padding?.call(scale) ?? EdgeInsets.zero; + return EdgeRange(minY - _padding.top, maxY + _padding.bottom); } double clampScale(double scale) {