Unverified Commit 23499f40 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Fix typo in MediaQuery.fromWindow and add invertColors flag to images for smart invert. (#21457)

parent 5ea987db
...@@ -342,6 +342,11 @@ class DecorationImagePainter { ...@@ -342,6 +342,11 @@ class DecorationImagePainter {
/// when using this, to not flip images with integral shadows, text, or other /// when using this, to not flip images with integral shadows, text, or other
/// effects that will look incorrect when flipped. /// effects that will look incorrect when flipped.
/// ///
/// * `invertColors`: 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.
///
/// The `canvas`, `rect`, `image`, `scale`, `alignment`, `repeat`, and `flipHorizontally` /// The `canvas`, `rect`, `image`, `scale`, `alignment`, `repeat`, and `flipHorizontally`
/// arguments must not be null. /// arguments must not be null.
/// ///
...@@ -361,6 +366,7 @@ void paintImage({ ...@@ -361,6 +366,7 @@ void paintImage({
Rect centerSlice, Rect centerSlice,
ImageRepeat repeat = ImageRepeat.noRepeat, ImageRepeat repeat = ImageRepeat.noRepeat,
bool flipHorizontally = false, bool flipHorizontally = false,
bool invertColors = false,
}) { }) {
assert(canvas != null); assert(canvas != null);
assert(image != null); assert(image != null);
...@@ -406,6 +412,7 @@ void paintImage({ ...@@ -406,6 +412,7 @@ void paintImage({
// to nearest-neighbor. // to nearest-neighbor.
paint.filterQuality = FilterQuality.low; paint.filterQuality = FilterQuality.low;
} }
paint.invertColors = invertColors;
final double halfWidthDelta = (outputSize.width - destinationSize.width) / 2.0; final double halfWidthDelta = (outputSize.width - destinationSize.width) / 2.0;
final double halfHeightDelta = (outputSize.height - destinationSize.height) / 2.0; final double halfHeightDelta = (outputSize.height - destinationSize.height) / 2.0;
final double dx = halfWidthDelta + (flipHorizontally ? -alignment.x : alignment.x) * halfWidthDelta; final double dx = halfWidthDelta + (flipHorizontally ? -alignment.x : alignment.x) * halfWidthDelta;
......
...@@ -37,6 +37,7 @@ class RenderImage extends RenderBox { ...@@ -37,6 +37,7 @@ class RenderImage extends RenderBox {
Rect centerSlice, Rect centerSlice,
bool matchTextDirection = false, bool matchTextDirection = false,
TextDirection textDirection, TextDirection textDirection,
bool invertColors = false
}) : assert(scale != null), }) : assert(scale != null),
assert(repeat != null), assert(repeat != null),
assert(alignment != null), assert(alignment != null),
...@@ -52,6 +53,7 @@ class RenderImage extends RenderBox { ...@@ -52,6 +53,7 @@ class RenderImage extends RenderBox {
_repeat = repeat, _repeat = repeat,
_centerSlice = centerSlice, _centerSlice = centerSlice,
_matchTextDirection = matchTextDirection, _matchTextDirection = matchTextDirection,
_invertColors = invertColors,
_textDirection = textDirection { _textDirection = textDirection {
_updateColorFilter(); _updateColorFilter();
} }
...@@ -215,6 +217,20 @@ class RenderImage extends RenderBox { ...@@ -215,6 +217,20 @@ class RenderImage extends RenderBox {
markNeedsPaint(); markNeedsPaint();
} }
/// Whether to invert the colors of the image.
///
/// 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;
bool _invertColors;
set invertColors(bool value) {
if (value == _invertColors)
return;
_invertColors = value;
markNeedsPaint();
}
/// Whether to paint the image in the direction of the [TextDirection]. /// Whether to paint the image in the direction of the [TextDirection].
/// ///
/// If this is true, then in [TextDirection.ltr] contexts, the image will be /// If this is true, then in [TextDirection.ltr] contexts, the image will be
...@@ -331,6 +347,7 @@ class RenderImage extends RenderBox { ...@@ -331,6 +347,7 @@ class RenderImage extends RenderBox {
centerSlice: _centerSlice, centerSlice: _centerSlice,
repeat: _repeat, repeat: _repeat,
flipHorizontally: _flipHorizontally, flipHorizontally: _flipHorizontally,
invertColors: invertColors,
); );
} }
...@@ -349,5 +366,6 @@ class RenderImage extends RenderBox { ...@@ -349,5 +366,6 @@ class RenderImage extends RenderBox {
properties.add(DiagnosticsProperty<Rect>('centerSlice', centerSlice, defaultValue: null)); properties.add(DiagnosticsProperty<Rect>('centerSlice', centerSlice, defaultValue: null));
properties.add(FlagProperty('matchTextDirection', value: matchTextDirection, ifTrue: 'match text direction')); properties.add(FlagProperty('matchTextDirection', value: matchTextDirection, ifTrue: 'match text direction'));
properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null)); properties.add(EnumProperty<TextDirection>('textDirection', textDirection, defaultValue: null));
properties.add(DiagnosticsProperty<bool>('invertColors', invertColors));
} }
} }
...@@ -4494,6 +4494,7 @@ class RawImage extends LeafRenderObjectWidget { ...@@ -4494,6 +4494,7 @@ class RawImage extends LeafRenderObjectWidget {
this.repeat = ImageRepeat.noRepeat, this.repeat = ImageRepeat.noRepeat,
this.centerSlice, this.centerSlice,
this.matchTextDirection = false, this.matchTextDirection = false,
this.invertColors = false,
}) : assert(scale != null), }) : assert(scale != null),
assert(alignment != null), assert(alignment != null),
assert(repeat != null), assert(repeat != null),
...@@ -4595,6 +4596,17 @@ class RawImage extends LeafRenderObjectWidget { ...@@ -4595,6 +4596,17 @@ class RawImage extends LeafRenderObjectWidget {
/// scope. /// scope.
final bool matchTextDirection; final bool matchTextDirection;
/// 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;
@override @override
RenderImage createRenderObject(BuildContext context) { RenderImage createRenderObject(BuildContext context) {
assert((!matchTextDirection && alignment is Alignment) || debugCheckHasDirectionality(context)); assert((!matchTextDirection && alignment is Alignment) || debugCheckHasDirectionality(context));
...@@ -4611,6 +4623,7 @@ class RawImage extends LeafRenderObjectWidget { ...@@ -4611,6 +4623,7 @@ class RawImage extends LeafRenderObjectWidget {
centerSlice: centerSlice, centerSlice: centerSlice,
matchTextDirection: matchTextDirection, matchTextDirection: matchTextDirection,
textDirection: matchTextDirection || alignment is! Alignment ? Directionality.of(context) : null, textDirection: matchTextDirection || alignment is! Alignment ? Directionality.of(context) : null,
invertColors: invertColors,
); );
} }
...@@ -4628,7 +4641,8 @@ class RawImage extends LeafRenderObjectWidget { ...@@ -4628,7 +4641,8 @@ class RawImage extends LeafRenderObjectWidget {
..repeat = repeat ..repeat = repeat
..centerSlice = centerSlice ..centerSlice = centerSlice
..matchTextDirection = matchTextDirection ..matchTextDirection = matchTextDirection
..textDirection = matchTextDirection || alignment is! Alignment ? Directionality.of(context) : null; ..textDirection = matchTextDirection || alignment is! Alignment ? Directionality.of(context) : null
..invertColors = invertColors;
} }
@override @override
...@@ -4645,6 +4659,7 @@ class RawImage extends LeafRenderObjectWidget { ...@@ -4645,6 +4659,7 @@ class RawImage extends LeafRenderObjectWidget {
properties.add(EnumProperty<ImageRepeat>('repeat', repeat, defaultValue: ImageRepeat.noRepeat)); properties.add(EnumProperty<ImageRepeat>('repeat', repeat, defaultValue: ImageRepeat.noRepeat));
properties.add(DiagnosticsProperty<Rect>('centerSlice', centerSlice, defaultValue: null)); properties.add(DiagnosticsProperty<Rect>('centerSlice', centerSlice, defaultValue: null));
properties.add(FlagProperty('matchTextDirection', value: matchTextDirection, ifTrue: 'match text direction')); properties.add(FlagProperty('matchTextDirection', value: matchTextDirection, ifTrue: 'match text direction'));
properties.add(DiagnosticsProperty<bool>('invertColors', invertColors));
} }
} }
......
...@@ -9,6 +9,7 @@ import 'dart:typed_data'; ...@@ -9,6 +9,7 @@ import 'dart:typed_data';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart'; import 'package:flutter/painting.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter/semantics.dart';
import 'basic.dart'; import 'basic.dart';
import 'framework.dart'; import 'framework.dart';
...@@ -550,9 +551,12 @@ class _ImageState extends State<Image> { ...@@ -550,9 +551,12 @@ class _ImageState extends State<Image> {
ImageStream _imageStream; ImageStream _imageStream;
ImageInfo _imageInfo; ImageInfo _imageInfo;
bool _isListeningToStream = false; bool _isListeningToStream = false;
bool _invertColors;
@override @override
void didChangeDependencies() { void didChangeDependencies() {
_invertColors = MediaQuery.of(context, nullOk: true)?.invertColors
?? SemanticsBinding.instance.accessibilityFeatures.invertColors;
_resolveImage(); _resolveImage();
if (TickerMode.of(context)) if (TickerMode.of(context))
...@@ -645,6 +649,7 @@ class _ImageState extends State<Image> { ...@@ -645,6 +649,7 @@ class _ImageState extends State<Image> {
repeat: widget.repeat, repeat: widget.repeat,
centerSlice: widget.centerSlice, centerSlice: widget.centerSlice,
matchTextDirection: widget.matchTextDirection, matchTextDirection: widget.matchTextDirection,
invertColors: _invertColors,
); );
if (widget.excludeFromSemantics) if (widget.excludeFromSemantics)
return image; return image;
......
...@@ -62,7 +62,7 @@ class MediaQueryData { ...@@ -62,7 +62,7 @@ class MediaQueryData {
padding = EdgeInsets.fromWindowPadding(window.padding, window.devicePixelRatio), padding = EdgeInsets.fromWindowPadding(window.padding, window.devicePixelRatio),
viewInsets = EdgeInsets.fromWindowPadding(window.viewInsets, window.devicePixelRatio), viewInsets = EdgeInsets.fromWindowPadding(window.viewInsets, window.devicePixelRatio),
accessibleNavigation = window.accessibilityFeatures.accessibleNavigation, accessibleNavigation = window.accessibilityFeatures.accessibleNavigation,
invertColors = window.accessibilityFeatures.accessibleNavigation, invertColors = window.accessibilityFeatures.invertColors,
disableAnimations = window.accessibilityFeatures.disableAnimations, disableAnimations = window.accessibilityFeatures.disableAnimations,
boldText = window.accessibilityFeatures.boldText, boldText = window.accessibilityFeatures.boldText,
alwaysUse24HourFormat = window.alwaysUse24HourFormat; alwaysUse24HourFormat = window.alwaysUse24HourFormat;
......
...@@ -92,6 +92,7 @@ void main() { ...@@ -92,6 +92,7 @@ void main() {
' size: Size(25.0, 25.0)\n' ' size: Size(25.0, 25.0)\n'
' image: [10×10]\n' ' image: [10×10]\n'
' alignment: center\n' ' alignment: center\n'
' invertColors: false\n'
), ),
); );
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('InvertColors', (WidgetTester tester) async {
await tester.pumpWidget(const RepaintBoundary(
child: SizedBox(
width: 200.0,
height: 200.0,
child: InvertColorTestWidget(
color: Color.fromRGBO(255, 0, 0, 1.0),
),
),
));
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('invert_colors_test.0.png'),
skip: !Platform.isLinux,
);
});
testWidgets('InvertColors and ColorFilter', (WidgetTester tester) async {
await tester.pumpWidget(const RepaintBoundary(
child: SizedBox(
width: 200.0,
height: 200.0,
child: InvertColorTestWidget(
color: Color.fromRGBO(255, 0, 0, 1.0),
filter: ColorFilter.mode(Color.fromRGBO(0, 255, 0, 0.5), BlendMode.plus),
),
),
));
await expectLater(
find.byType(RepaintBoundary),
matchesGoldenFile('invert_colors_test.1.png'),
skip: !Platform.isLinux,
);
});
}
// Draws a rectangle sized by the parent widget with [color], [colorFilter],
// and [invertColors] applied for testing the invert colors.
class InvertColorTestWidget extends LeafRenderObjectWidget {
const InvertColorTestWidget({
this.color,
this.filter,
Key key
}) : super(key: key);
final Color color;
final ColorFilter filter;
@override
RenderInvertColorTest createRenderObject(BuildContext context) {
return RenderInvertColorTest(color, filter);
}
@override
void updateRenderObject(BuildContext context, covariant RenderInvertColorTest renderObject) {
renderObject
..color = color
..filter = filter;
}
}
class RenderInvertColorTest extends RenderProxyBox {
RenderInvertColorTest(this._color, this._filter);
Color get color => _color;
Color _color;
set color(Color value) {
if (color == value)
return;
_color = value;
markNeedsPaint();
}
ColorFilter get filter => _filter;
ColorFilter _filter;
set filter(ColorFilter value) {
if (filter == value)
return;
_filter = value;
markNeedsPaint();
}
@override
void paint(PaintingContext context, Offset offset) {
final Paint paint = Paint()
..style = PaintingStyle.fill
..color = color
..colorFilter = filter
..invertColors = true;
context.canvas.drawRect(offset & size, paint);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment