Commit 92e40b5c authored by Hans Muller's avatar Hans Muller Committed by GitHub

Image color blend mode (#9486)

parent da7867be
......@@ -33,12 +33,11 @@ class SectionCard extends StatelessWidget {
],
),
),
child: new Opacity(
opacity: 0.075,
child: new Image.asset(
section.backgroundAsset,
fit: BoxFit.cover,
),
child: new Image.asset(
section.backgroundAsset,
color: const Color.fromRGBO(255, 255, 255, 0.075),
colorBlendMode: BlendMode.modulate,
fit: BoxFit.cover,
),
),
);
......
......@@ -26,6 +26,7 @@ class RenderImage extends RenderBox {
double height,
double scale: 1.0,
Color color,
BlendMode colorBlendMode,
BoxFit fit,
FractionalOffset alignment,
ImageRepeat repeat: ImageRepeat.noRepeat,
......@@ -35,6 +36,7 @@ class RenderImage extends RenderBox {
_height = height,
_scale = scale,
_color = color,
_colorBlendMode = colorBlendMode,
_fit = fit,
_alignment = alignment,
_repeat = repeat,
......@@ -95,15 +97,14 @@ class RenderImage extends RenderBox {
ColorFilter _colorFilter;
// Should we make the blend mode configurable?
void _updateColorFilter() {
if (_color == null)
_colorFilter = null;
else
_colorFilter = new ColorFilter.mode(_color, BlendMode.srcIn);
_colorFilter = new ColorFilter.mode(_color, _colorBlendMode ?? BlendMode.srcIn);
}
/// If non-null, apply this color filter to the image before painting.
/// If non-null, this color is blended with each image pixel using [colorBlendMode].
Color get color => _color;
Color _color;
set color(Color value) {
......@@ -114,6 +115,24 @@ class RenderImage extends RenderBox {
markNeedsPaint();
}
/// Used to combine [color] with this image.
///
/// The default is [BlendMode.srcIn]. In terms of the blend mode, [color] is
/// the source and this image is the destination.
///
/// See also:
///
/// * [BlendMode], which includes an illustration of the effect of each blend mode.
BlendMode get colorBlendMode => _colorBlendMode;
BlendMode _colorBlendMode;
set colorBlendMode(BlendMode value) {
if (value == _colorBlendMode)
return;
_colorBlendMode;
_updateColorFilter();
markNeedsPaint();
}
/// How to inscribe the image into the space allocated during layout.
///
/// The default varies based on the other fields. See the discussion at
......@@ -251,6 +270,8 @@ class RenderImage extends RenderBox {
description.add('scale: $scale');
if (color != null)
description.add('color: $color');
if (colorBlendMode != null)
description.add('colorBlendMode: $colorBlendMode');
if (fit != null)
description.add('fit: $fit');
if (alignment != null)
......
......@@ -2558,6 +2558,7 @@ class RawImage extends LeafRenderObjectWidget {
this.height,
this.scale: 1.0,
this.color,
this.colorBlendMode,
this.fit,
this.alignment,
this.repeat: ImageRepeat.noRepeat,
......@@ -2586,9 +2587,19 @@ class RawImage extends LeafRenderObjectWidget {
/// Used when determining the best display size for the image.
final double scale;
/// If non-null, apply this color filter to the image before painting.
/// If non-null, this color is blended with each image pixel using [colorBlendMode].
final Color color;
/// Used to combine [color] with this image.
///
/// The default is [BlendMode.srcIn]. In terms of the blend mode, [color] is
/// the source and this image is the destination.
///
/// See also:
///
/// * [BlendMode], which includes an illustration of the effect of each blend mode.
final BlendMode colorBlendMode;
/// How to inscribe the image into the space allocated during layout.
///
/// The default varies based on the other fields. See the discussion at
......@@ -2621,6 +2632,7 @@ class RawImage extends LeafRenderObjectWidget {
height: height,
scale: scale,
color: color,
colorBlendMode: colorBlendMode,
fit: fit,
alignment: alignment,
repeat: repeat,
......@@ -2635,6 +2647,7 @@ class RawImage extends LeafRenderObjectWidget {
..height = height
..scale = scale
..color = color
..colorBlendMode = colorBlendMode
..alignment = alignment
..fit = fit
..repeat = repeat
......@@ -2653,6 +2666,8 @@ class RawImage extends LeafRenderObjectWidget {
description.add('scale: $scale');
if (color != null)
description.add('color: $color');
if (colorBlendMode != null)
description.add('colorBlendMode: $colorBlendMode');
if (fit != null)
description.add('fit: $fit');
if (alignment != null)
......
......@@ -69,6 +69,7 @@ class Image extends StatefulWidget {
this.width,
this.height,
this.color,
this.colorBlendMode,
this.fit,
this.alignment,
this.repeat: ImageRepeat.noRepeat,
......@@ -87,6 +88,7 @@ class Image extends StatefulWidget {
this.width,
this.height,
this.color,
this.colorBlendMode,
this.fit,
this.alignment,
this.repeat: ImageRepeat.noRepeat,
......@@ -107,6 +109,7 @@ class Image extends StatefulWidget {
this.width,
this.height,
this.color,
this.colorBlendMode,
this.fit,
this.alignment,
this.repeat: ImageRepeat.noRepeat,
......@@ -135,6 +138,7 @@ class Image extends StatefulWidget {
this.width,
this.height,
this.color,
this.colorBlendMode,
this.fit,
this.alignment,
this.repeat: ImageRepeat.noRepeat,
......@@ -153,6 +157,7 @@ class Image extends StatefulWidget {
this.width,
this.height,
this.color,
this.colorBlendMode,
this.fit,
this.alignment,
this.repeat: ImageRepeat.noRepeat,
......@@ -176,9 +181,19 @@ class Image extends StatefulWidget {
/// aspect ratio.
final double height;
/// If non-null, apply this color filter to the image before painting.
/// If non-null, this color is blended with each image pixel using [colorBlendMode].
final Color color;
/// Used to combine [color] with this image.
///
/// The default is [BlendMode.srcIn]. In terms of the blend mode, [color] is
/// the source and this image is the destination.
///
/// See also:
///
/// * [BlendMode], which includes an illustration of the effect of each blend mode.
final BlendMode colorBlendMode;
/// How to inscribe the image into the space allocated during layout.
///
/// The default varies based on the other fields. See the discussion at
......@@ -221,6 +236,8 @@ class Image extends StatefulWidget {
description.add('height: $height');
if (color != null)
description.add('color: $color');
if (colorBlendMode != null)
description.add('colorBlendMode: $colorBlendMode');
if (fit != null)
description.add('fit: $fit');
if (alignment != null)
......@@ -291,6 +308,7 @@ class _ImageState extends State<Image> {
height: widget.height,
scale: _imageInfo?.scale ?? 1.0,
color: widget.color,
colorBlendMode: widget.colorBlendMode,
fit: widget.fit,
alignment: widget.alignment,
repeat: widget.repeat,
......
......@@ -26,7 +26,7 @@ void main() {
)
),
null,
EnginePhase.layout
EnginePhase.layout,
);
RenderImage renderImage = key.currentContext.findRenderObject();
expect(renderImage.image, isNull);
......@@ -305,6 +305,19 @@ void main() {
testWidgets('Image.memory control test', (WidgetTester tester) async {
await tester.pumpWidget(new Image.memory(new Uint8List.fromList(kTransparentImage)));
});
testWidgets('Image color and colorBlend parameters', (WidgetTester tester) async {
await tester.pumpWidget(
new Image(
image: new TestImageProvider(),
color: const Color(0xFF00FF00),
colorBlendMode: BlendMode.clear
)
);
final RenderImage renderer = tester.renderObject<RenderImage>(find.byType(Image));
expect(renderer.color, const Color(0xFF00FF00));
expect(renderer.colorBlendMode, BlendMode.clear);
});
}
class TestImageProvider extends ImageProvider<TestImageProvider> {
......
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