From da62ac9b68d24a4e98ce1682e5bc4593f82baefc Mon Sep 17 00:00:00 2001 From: torta Date: Wed, 11 Dec 2019 19:01:43 +0800 Subject: [PATCH] Add filterQuality property to improve image quality after scale --- .../examples/full_screen_examples.dart | 18 +++++++++++++ lib/photo_view.dart | 7 +++++ lib/photo_view_gallery.dart | 7 +++++ lib/src/core/photo_view_core.dart | 27 ++++++++++++++----- 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/example/lib/screens/examples/full_screen_examples.dart b/example/lib/screens/examples/full_screen_examples.dart index 0402d10a..192dd111 100644 --- a/example/lib/screens/examples/full_screen_examples.dart +++ b/example/lib/screens/examples/full_screen_examples.dart @@ -30,6 +30,21 @@ class FullScreenExamples extends StatelessWidget { ); }, ), + ExampleButtonNode( + title: "Large Image (filter quality: medium)", + onPressed: () { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => const FullScreenWrapper( + imageProvider: + const AssetImage("assets/large-image.jpg"), + filterQuality: FilterQuality.medium, + ), + ), + ); + }, + ), ExampleButtonNode( title: "Small Image (custom background)", onPressed: () { @@ -200,6 +215,7 @@ class FullScreenWrapper extends StatelessWidget { this.maxScale, this.initialScale, this.basePosition = Alignment.center, + this.filterQuality = FilterQuality.none, }); final ImageProvider imageProvider; @@ -209,6 +225,7 @@ class FullScreenWrapper extends StatelessWidget { final dynamic maxScale; final dynamic initialScale; final Alignment basePosition; + final FilterQuality filterQuality; @override Widget build(BuildContext context) { @@ -224,6 +241,7 @@ class FullScreenWrapper extends StatelessWidget { maxScale: maxScale, initialScale: initialScale, basePosition: basePosition, + filterQuality: filterQuality, ), ); } diff --git a/lib/photo_view.dart b/lib/photo_view.dart index d80e3370..527994c4 100644 --- a/lib/photo_view.dart +++ b/lib/photo_view.dart @@ -244,6 +244,7 @@ class PhotoView extends StatefulWidget { this.customSize, this.gestureDetectorBehavior, this.tightMode, + this.filterQuality, }) : child = null, childSize = null, super(key: key); @@ -274,6 +275,7 @@ class PhotoView extends StatefulWidget { this.customSize, this.gestureDetectorBehavior, this.tightMode, + this.filterQuality, }) : loadingChild = null, imageProvider = null, gaplessPlayback = false, @@ -357,6 +359,9 @@ class PhotoView extends StatefulWidget { /// Useful when inside a [Dialog] final bool tightMode; + /// Quality levels for image filters. + final FilterQuality filterQuality; + @override State createState() { return _PhotoViewState(); @@ -516,6 +521,7 @@ class _PhotoViewState extends State { onTapDown: widget.onTapDown, gestureDetectorBehavior: widget.gestureDetectorBehavior, tightMode: widget.tightMode ?? false, + filterQuality: widget.filterQuality ?? FilterQuality.none, ); } @@ -570,6 +576,7 @@ class _PhotoViewState extends State { onTapDown: widget.onTapDown, gestureDetectorBehavior: widget.gestureDetectorBehavior, tightMode: widget.tightMode ?? false, + filterQuality: widget.filterQuality ?? FilterQuality.none, ); } diff --git a/lib/photo_view_gallery.dart b/lib/photo_view_gallery.dart index f15614fb..9e791c1a 100644 --- a/lib/photo_view_gallery.dart +++ b/lib/photo_view_gallery.dart @@ -237,6 +237,7 @@ class _PhotoViewGalleryState extends State { onTapDown: pageOption.onTapDown, gestureDetectorBehavior: pageOption.gestureDetectorBehavior, tightMode: pageOption.tightMode, + filterQuality: pageOption.filterQuality, ) : PhotoView( key: ObjectKey(index), @@ -258,6 +259,7 @@ class _PhotoViewGalleryState extends State { onTapDown: pageOption.onTapDown, gestureDetectorBehavior: pageOption.gestureDetectorBehavior, tightMode: pageOption.tightMode, + filterQuality: pageOption.filterQuality, ); return ClipRect( @@ -294,6 +296,7 @@ class PhotoViewGalleryPageOptions { this.onTapDown, this.gestureDetectorBehavior, this.tightMode, + this.filterQuality, }) : child = null, childSize = null, assert(imageProvider != null); @@ -313,6 +316,7 @@ class PhotoViewGalleryPageOptions { this.onTapDown, this.gestureDetectorBehavior, this.tightMode, + this.filterQuality, }) : imageProvider = null, assert(child != null), assert(childSize != null); @@ -361,4 +365,7 @@ class PhotoViewGalleryPageOptions { /// Mirror to [PhotoView.tightMode] final bool tightMode; + + /// Quality levels for image filters. + final FilterQuality filterQuality; } diff --git a/lib/src/core/photo_view_core.dart b/lib/src/core/photo_view_core.dart index ea50ad14..fec391ec 100644 --- a/lib/src/core/photo_view_core.dart +++ b/lib/src/core/photo_view_core.dart @@ -36,6 +36,7 @@ class PhotoViewCore extends StatefulWidget { @required this.scaleStateController, @required this.basePosition, @required this.tightMode, + @required this.filterQuality, }) : customChild = null, super(key: key); @@ -54,6 +55,7 @@ class PhotoViewCore extends StatefulWidget { @required this.scaleStateController, @required this.basePosition, @required this.tightMode, + @required this.filterQuality, }) : imageProvider = null, gaplessPlayback = false, super(key: key); @@ -77,6 +79,8 @@ class PhotoViewCore extends StatefulWidget { final HitTestBehavior gestureDetectorBehavior; final bool tightMode; + final FilterQuality filterQuality; + @override State createState() { return PhotoViewCoreState(); @@ -282,9 +286,10 @@ class PhotoViewCoreState extends State ) { if (snapshot.hasData) { final PhotoViewControllerValue value = snapshot.data; + final useImageScale = widget.filterQuality != FilterQuality.none; final matrix = Matrix4.identity() ..translate(value.position.dx, value.position.dy) - ..scale(scale); + ..scale(useImageScale ? 1.0 : scale); if (widget.enableRotation) { matrix..rotateZ(value.rotation); } @@ -293,6 +298,7 @@ class PhotoViewCoreState extends State delegate: _CenterWithOriginalSizeDelegate( scaleBoundaries.childSize, basePosition, + useImageScale, ), child: _buildHero(), ); @@ -343,28 +349,37 @@ class PhotoViewCoreState extends State : Image( image: widget.imageProvider, gaplessPlayback: widget.gaplessPlayback ?? false, + filterQuality: widget.filterQuality, + width: scaleBoundaries.childSize.width * scale, + fit: BoxFit.contain, ); } } class _CenterWithOriginalSizeDelegate extends SingleChildLayoutDelegate { - const _CenterWithOriginalSizeDelegate(this.subjectSize, this.basePosition); + const _CenterWithOriginalSizeDelegate( + this.subjectSize, this.basePosition, this.useImageScale); final Size subjectSize; final Alignment basePosition; + final bool useImageScale; @override Offset getPositionForChild(Size size, Size childSize) { final double offsetX = - ((size.width - subjectSize.width) / 2) * (basePosition.x + 1); - final double offsetY = - ((size.height - subjectSize.height) / 2) * (basePosition.y + 1); + ((size.width - (useImageScale ? childSize.width : subjectSize.width)) / + 2) * + (basePosition.x + 1); + final double offsetY = ((size.height - + (useImageScale ? childSize.height : subjectSize.height)) / + 2) * + (basePosition.y + 1); return Offset(offsetX, offsetY); } @override BoxConstraints getConstraintsForChild(BoxConstraints constraints) { - return BoxConstraints.tight(subjectSize); + return useImageScale ? BoxConstraints() : BoxConstraints.tight(subjectSize); } @override