diff --git a/packages/flutter/lib/src/painting/decoration_image.dart b/packages/flutter/lib/src/painting/decoration_image.dart index 56a356290c5..95e25580383 100644 --- a/packages/flutter/lib/src/painting/decoration_image.dart +++ b/packages/flutter/lib/src/painting/decoration_image.dart @@ -53,6 +53,10 @@ class DecorationImage { this.repeat = ImageRepeat.noRepeat, this.matchTextDirection = false, this.scale = 1.0, + this.opacity = 1.0, + this.filterQuality = FilterQuality.low, + this.invertColors = false, + this.isAntiAlias = false, }) : assert(image != null), assert(alignment != null), assert(repeat != null), @@ -142,6 +146,36 @@ class DecorationImage { /// calculated by multiplying [scale] with `scale` of the given [ImageProvider]. final double scale; + /// If non-null, the value is multiplied with the opacity of each image + /// pixel before painting onto the canvas. + /// + /// This is more efficient than using [Opacity] or [FadeTransition] to + /// change the opacity of an image. + final double opacity; + + /// Used to set the filterQuality of the image. + /// + /// Use the "low" quality setting to scale the image, which corresponds to + /// bilinear interpolation, rather than the default "none" which corresponds + /// to nearest-neighbor. + final FilterQuality filterQuality; + + /// Whether the colors of the image are inverted when drawn. + /// + /// 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. + /// + /// See also: + /// + /// * [Paint.invertColors], for the dart:ui implementation. + final bool invertColors; + + /// Whether to paint the image with anti-aliasing. + /// + /// Anti-aliasing alleviates the sawtooth artifact when the image is rotated. + final bool isAntiAlias; + /// Creates a [DecorationImagePainter] for this [DecorationImage]. /// /// The `onChanged` argument must not be null. It will be called whenever the @@ -166,11 +200,28 @@ class DecorationImage { && other.centerSlice == centerSlice && other.repeat == repeat && other.matchTextDirection == matchTextDirection - && other.scale == scale; + && other.scale == scale + && other.opacity == opacity + && other.filterQuality == filterQuality + && other.invertColors == invertColors + && other.isAntiAlias == isAntiAlias; } @override - int get hashCode => hashValues(image, colorFilter, fit, alignment, centerSlice, repeat, matchTextDirection, scale); + int get hashCode => hashValues( + image, + colorFilter, + fit, + alignment, + centerSlice, + repeat, + matchTextDirection, + scale, + opacity, + filterQuality, + invertColors, + isAntiAlias, + ); @override String toString() { @@ -189,7 +240,13 @@ class DecorationImage { '$repeat', if (matchTextDirection) 'match text direction', - 'scale: $scale', + 'scale $scale', + 'opacity $opacity', + '$filterQuality', + if (invertColors) + 'invert colors', + if (isAntiAlias) + 'use anti-aliasing', ]; return '${objectRuntimeType(this, 'DecorationImage')}(${properties.join(", ")})'; } @@ -286,7 +343,10 @@ class DecorationImagePainter { centerSlice: _details.centerSlice, repeat: _details.repeat, flipHorizontally: flipHorizontally, - filterQuality: FilterQuality.low, + opacity: _details.opacity, + filterQuality: _details.filterQuality, + invertColors: _details.invertColors, + isAntiAlias: _details.isAntiAlias, ); if (clipPath != null) diff --git a/packages/flutter/lib/src/rendering/image.dart b/packages/flutter/lib/src/rendering/image.dart index fe0d73343a7..146492541d6 100644 --- a/packages/flutter/lib/src/rendering/image.dart +++ b/packages/flutter/lib/src/rendering/image.dart @@ -182,7 +182,8 @@ class RenderImage extends RenderBox { value?.addListener(markNeedsPaint); } - /// Used to set the filterQuality of the image + /// Used to set the filterQuality of the 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. @@ -271,7 +272,7 @@ class RenderImage extends RenderBox { /// Whether to invert the colors of the image. /// - /// inverting the colors of an image applies a new color filter to the paint. + /// 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. bool get invertColors => _invertColors; diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index 351338bd192..e074f2475f7 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -5685,7 +5685,8 @@ class RawImage extends LeafRenderObjectWidget { /// of an image. final Animation? opacity; - /// Used to set the filterQuality of the image + /// Used to set the filterQuality of the image. + /// /// Use the "low" quality setting to scale the image, which corresponds to /// bilinear interpolation, rather than the default "none" which corresponds /// to nearest-neighbor. @@ -5765,7 +5766,7 @@ class RawImage extends LeafRenderObjectWidget { /// Whether the colors of the image are inverted when drawn. /// - /// inverting the colors of an image applies a new color filter to the paint. + /// 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. /// diff --git a/packages/flutter/test/painting/decoration_test.dart b/packages/flutter/test/painting/decoration_test.dart index 4a944b8568b..6206a18da44 100644 --- a/packages/flutter/test/painting/decoration_test.dart +++ b/packages/flutter/test/painting/decoration_test.dart @@ -302,6 +302,10 @@ void main() { alignment: Alignment.bottomLeft, centerSlice: const Rect.fromLTWH(10.0, 20.0, 30.0, 40.0), repeat: ImageRepeat.repeatY, + opacity: 0.5, + filterQuality: FilterQuality.high, + invertColors: true, + isAntiAlias: true, ); final BoxDecoration boxDecoration = BoxDecoration(image: backgroundImage); @@ -316,19 +320,31 @@ void main() { expect(call.positionalArguments[1], const Rect.fromLTRB(10.0, 20.0, 40.0, 60.0)); expect(call.positionalArguments[2], const Rect.fromLTRB(0.0, 0.0, 100.0, 100.0)); expect(call.positionalArguments[3], isA()); - // ignore: avoid_dynamic_calls - expect(call.positionalArguments[3].isAntiAlias, false); - // ignore: avoid_dynamic_calls - expect(call.positionalArguments[3].colorFilter, colorFilter); - // ignore: avoid_dynamic_calls - expect(call.positionalArguments[3].filterQuality, FilterQuality.low); + final Paint paint = call.positionalArguments[3] as Paint; + expect(paint.colorFilter, colorFilter); + expect(paint.color, const Color(0x7F000000)); // 0.5 opacity + expect(paint.filterQuality, FilterQuality.high); + expect(paint.isAntiAlias, true); + // TODO(craiglabenz): change to true when https://github.com/flutter/flutter/issues/88909 is fixed + expect(paint.invertColors, !kIsWeb); }); test('DecorationImage with null textDirection configuration should throw Error', () async { + const ColorFilter colorFilter = ui.ColorFilter.mode(Color(0xFF00FF00), BlendMode.src); final ui.Image image = await createTestImage(width: 100, height: 100); final DecorationImage backgroundImage = DecorationImage( image: SynchronousTestImageProvider(image), + colorFilter: colorFilter, + fit: BoxFit.contain, + alignment: Alignment.center, + centerSlice: const Rect.fromLTWH(10.0, 20.0, 30.0, 40.0), + repeat: ImageRepeat.repeatY, matchTextDirection: true, + scale: 0.5, + opacity: 0.5, + filterQuality: FilterQuality.low, + invertColors: true, + isAntiAlias: true, ); final BoxDecoration boxDecoration = BoxDecoration(image: backgroundImage); final BoxPainter boxPainter = boxDecoration.createBoxPainter(() { @@ -356,7 +372,11 @@ void main() { ' direction provided in the ImageConfiguration object to match.\n' ' The DecorationImage was:\n' ' DecorationImage(SynchronousTestImageProvider(),\n' - ' Alignment.center, match text direction, scale: 1.0)\n' + ' ColorFilter.mode(Color(0xff00ff00), BlendMode.src),\n' + ' BoxFit.contain, Alignment.center, centerSlice:\n' + ' Rect.fromLTRB(10.0, 20.0, 40.0, 60.0), ImageRepeat.repeatY,\n' + ' match text direction, scale 0.5, opacity 0.5,\n' + ' FilterQuality.low, invert colors, use anti-aliasing)\n' ' The ImageConfiguration was:\n' ' ImageConfiguration(size: Size(100.0, 100.0))\n', );