Is it possible to only highlight certain part of image (opacity)?

半腔热情 提交于 2019-12-23 21:09:15

问题


I have applied opacity to the image. Here is the code :-

<UserControl xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"  x:Class="DelSilverlightApp.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="600" d:DesignWidth="800">

    <Canvas x:Name="LayoutRoot" Background="White">
        <Image Source="Vista.jpg"  Height="300" Width="310" Canvas.Left="200" Canvas.Top="200" Opacity="0.8"/>
        <Grid x:Name="rectToGetXAndY" Canvas.ZIndex="3" Width="254" Height="143"  Opacity="0.6" Canvas.Left="223" Canvas.Top="272" />
    </Canvas>
</UserControl>

I want that only the area of the image inside of Grid should be set to opacity of 1 else it should remain 0.8. Any ideas how can i do that? This is very vital to my application but somehow i can't find a solution to it.

Thanks in advance :)


回答1:


One way to do this in a Generic and re-usable way is to use an OpacityMask with a VisualBrush and bind the values within the VisualBrush to the Image and the Grid. This way, it will work when the Image and Grid move around and change in Size etc. The VisualBrush can contain a Canvas and a Rectangle to achieve the 0.8 and 1.0 Opacity. Opacity can't be used on the Canvas however since it will effect the Opacity of the Rectangle so Background will do instead. 0.8 is equal to #CC000000. I used #50000000 to show the effect more clearly.

Update
Some workarounds were needed for the Silverlight version of this so I uploaded my sample app here: http://www.mediafire.com/?8pbj5b9t72m5191

WPF Version (Silverlight version will also work in WPF)

<Canvas x:Name="LayoutRoot" Background="White">
    <Canvas.Resources>
        <local:SubtractMultiConverter x:Key="SubtractMultiConverter"/>
        <local:MaxValueMultiConverter x:Key="MaxValueMultiConverter"/>
    </Canvas.Resources>
    <Image Name="image" Source="C:\FG.png" Stretch="Fill" Height="300" Width="310" Canvas.Left="100" Canvas.Top="200">
        <Image.OpacityMask>
            <VisualBrush>
                <VisualBrush.Visual>
                    <Canvas Background="#50000000"
                            Width="{Binding ElementName=image, Path=ActualWidth}"
                            Height="{Binding ElementName=image, Path=ActualHeight}">
                        <Rectangle Fill="#FF000000">
                            <Rectangle.Width>
                                <MultiBinding Converter="{StaticResource MaxValueMultiConverter}">
                                    <Binding ElementName="rectToGetXAndY" Path="ActualWidth"/>
                                    <Binding RelativeSource="{RelativeSource self}" Path="(Canvas.Left)"/>
                                    <Binding ElementName="image" Path="ActualWidth"/>
                                </MultiBinding>
                            </Rectangle.Width>
                            <Rectangle.Height>
                                <MultiBinding Converter="{StaticResource MaxValueMultiConverter}">
                                    <Binding ElementName="rectToGetXAndY" Path="ActualHeight"/>
                                    <Binding RelativeSource="{RelativeSource self}" Path="(Canvas.Top)"/>
                                    <Binding ElementName="image" Path="ActualHeight"/>
                                </MultiBinding>
                            </Rectangle.Height>
                            <Canvas.Left>
                                <MultiBinding Converter="{StaticResource SubtractMultiConverter}">
                                    <Binding ElementName="rectToGetXAndY" Path="(Canvas.Left)"/>
                                    <Binding ElementName="image" Path="(Canvas.Left)"/>
                                </MultiBinding>
                            </Canvas.Left>
                            <Canvas.Top>
                                <MultiBinding Converter="{StaticResource SubtractMultiConverter}">
                                    <Binding ElementName="rectToGetXAndY" Path="(Canvas.Top)"/>
                                    <Binding ElementName="image" Path="(Canvas.Top)"/>
                                </MultiBinding>
                            </Canvas.Top>
                        </Rectangle>
                    </Canvas>
                </VisualBrush.Visual>
            </VisualBrush>
        </Image.OpacityMask>
    </Image>
    <Grid x:Name="rectToGetXAndY" Canvas.ZIndex="3" Width="254" Height="143" Canvas.Left="123" Canvas.Top="272" Opacity="0.6"/>
</Canvas>

SubtractMultiConverter

public class SubtractMultiConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double value = (double)values[0];
        double subtractValue = (double)values[1];
        return value - subtractValue;
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

MaxValueMultiConverter

public class MaxValueMultiConverter : IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        double desiredWidth = (double)values[0];
        double canvasValue = (double)values[1];
        double actualWidth = (double)values[2];
        return Math.Min(desiredWidth, actualWidth - canvasValue);
    }
    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture)
    {
        return null;
    }
}

Update
I noticed you wanted this to work in Silverlight as well. Silverlight doesn't have MultiBinding but fortuanetely Colin E. has a very nice solution for this.
VisualBrush is also missing, but Chris C. has a nice solution to that. I had to make some changes to VisualImage to make this work.

The changes were in OnVisualChanged, I added an EventHandler for LayoutUpdated and changed RenderSize to ActualWidth/ActualHeight

private FrameworkElement visual = null;
private void OnVisualChanged(DependencyPropertyChangedEventArgs args)
{
    //if (args.OldValue != null) ((FrameworkElement)args.OldValue).SizeChanged -= VisualImage_SizeChanged;
    if (args.NewValue != null)
    {
        visual = (FrameworkElement)args.NewValue;
        visual.SizeChanged += VisualImage_SizeChanged;
        visual.LayoutUpdated += new EventHandler(visual_LayoutUpdated);
        PrepareBitmap((int)visual.ActualWidth, (int)visual.ActualHeight);
    }
}
void visual_LayoutUpdated(object sender, EventArgs e)
{
    PrepareBitmap((int)visual.ActualWidth, (int)visual.ActualHeight);
}

Silverlight Xaml

<UserControl.Resources>
    <local:SubtractMultiConverter x:Key="SubtractMultiConverter"/>
    <local:MaxValueMultiConverter x:Key="MaxValueMultiConverter"/>

    <Canvas x:Key="testBorder"
            Background="#50000000"
            Width="{Binding ElementName=image, Path=ActualWidth}"
            Height="{Binding ElementName=image, Path=ActualHeight}">
        <Rectangle Fill="#FF000000">
            <binding:BindingUtil.MultiBindings>
                <binding:MultiBindings>
                    <binding:MultiBinding TargetProperty="Width"
                                          Converter="{StaticResource MaxValueMultiConverter}">
                        <binding:MultiBinding.Bindings>
                            <binding:BindingCollection>
                                <Binding ElementName="rectToGetXAndY" Path="ActualWidth"/>
                                <Binding RelativeSource="{RelativeSource self}" Path="(Canvas.Left)"/>
                                <Binding ElementName="image" Path="ActualWidth"/>
                            </binding:BindingCollection>
                        </binding:MultiBinding.Bindings>
                    </binding:MultiBinding>
                    <binding:MultiBinding TargetProperty="Height"
                                          Converter="{StaticResource MaxValueMultiConverter}">
                        <binding:MultiBinding.Bindings>
                            <binding:BindingCollection>
                                <Binding ElementName="rectToGetXAndY" Path="ActualHeight"/>
                                <Binding RelativeSource="{RelativeSource self}" Path="(Canvas.Top)"/>
                                <Binding ElementName="image" Path="ActualHeight"/>
                            </binding:BindingCollection>
                        </binding:MultiBinding.Bindings>
                    </binding:MultiBinding>
                    <binding:MultiBinding TargetProperty="Canvas.Left"
                                          Converter="{StaticResource SubtractMultiConverter}">
                        <binding:MultiBinding.Bindings>
                            <binding:BindingCollection>
                                <Binding ElementName="rectToGetXAndY" Path="(Canvas.Left)"/>
                                <Binding ElementName="image" Path="(Canvas.Left)"/>
                            </binding:BindingCollection>
                        </binding:MultiBinding.Bindings>
                    </binding:MultiBinding>
                    <binding:MultiBinding TargetProperty="Canvas.Top"
                                          Converter="{StaticResource SubtractMultiConverter}">
                        <binding:MultiBinding.Bindings>
                            <binding:BindingCollection>
                                <Binding ElementName="rectToGetXAndY" Path="(Canvas.Top)"/>
                                <Binding ElementName="image" Path="(Canvas.Top)"/>
                            </binding:BindingCollection>
                        </binding:MultiBinding.Bindings>
                    </binding:MultiBinding>
                </binding:MultiBindings>
            </binding:BindingUtil.MultiBindings>
        </Rectangle>
    </Canvas>

</UserControl.Resources>
<Canvas x:Name="LayoutRoot" Background="White">
    <local:VisualImage x:Name="visualImage"
                       Visual="{Binding Source={StaticResource testBorder}}"
                       ImageBrush="{Binding ElementName=brush}"/>
    <Image Name="image" Source="/GridImageOpacityMask;component/Images/FG.png" Stretch="Fill" Height="300" Width="310" Canvas.Left="200" Canvas.Top="200">
        <Image.OpacityMask>
            <ImageBrush x:Name="brush" />
        </Image.OpacityMask>
    </Image>
    <Grid x:Name="rectToGetXAndY" Canvas.ZIndex="3" Width="254" Height="143"  Opacity="0.6" Canvas.Left="223" Canvas.Top="272"/>
</Canvas>



回答2:


You can use another Image with an OpacityMask set to a VisualBrush that results in a clipping rectangle like this:

<Canvas x:Name="LayoutRoot" Background="White">
    <Image Source="http://thecybershadow.net/misc/hax.png" Height="300" Width="310" Canvas.Left="200" Canvas.Top="200" Opacity="0.5"/>
    <Image Source="http://thecybershadow.net/misc/hax.png" Height="300" Width="310" Canvas.Left="200" Canvas.Top="200">
        <Image.OpacityMask>
            <VisualBrush Stretch="None">
                <VisualBrush.Visual>
                    <Rectangle Width="254" Height="143" Fill="Black"/>
                </VisualBrush.Visual>
            </VisualBrush>
        </Image.OpacityMask>
    </Image>
    <Grid x:Name="rectToGetXAndY" Canvas.ZIndex="3" Width="254" Height="143"  Opacity="0.6" Canvas.Left="223" Canvas.Top="272" />
</Canvas>

and here's another method using Image.Clip:

<Canvas x:Name="LayoutRoot" Background="White">
    <Image Source="http://thecybershadow.net/misc/hax.png" Height="300" Width="310" Canvas.Left="200" Canvas.Top="200" Opacity="0.5"/>
    <Image Source="http://thecybershadow.net/misc/hax.png" Height="300" Width="310" Canvas.Left="200" Canvas.Top="200">
        <Image.Clip>
            <RectangleGeometry Rect="23,72,254,143"/>
        </Image.Clip>
    </Image>
    <Grid x:Name="rectToGetXAndY" Canvas.ZIndex="3" Width="254" Height="143"  Opacity="0.6" Canvas.Left="223" Canvas.Top="272" />
</Canvas>



回答3:


Here is a lateral solution:-

    <Canvas x:Name="LayoutRoot" Background="White">
        <Grid x:Name="ImageContainer" Height="300" Width="310" Canvas.Left="200" Canvas.Top="200">
            <Image Source="Vista.jpg" />
            <Border x:Name="rectToGetXAndY" Background="Transparent" BorderThickness="23, 72, 85, 33" BorderBrush="#99FFFFFF" />
        </Grid>
    </Canvas>

This provides the same visual appearance of a vibrant rectangle in a washed out image. The apparent rectangle can be manipulated by modifying the BorderThickness property. Events from this rectangle can be trapped on the Border because a Transparent background has been added so it can be dragged about for example. Other content could be added to internals of the Border, for example a Grid of transparent rectangles could provide some sizing features.

The accepted answer here is significantly more complex by comparison albeit a more direct answer to your actual question.



来源:https://stackoverflow.com/questions/4571059/is-it-possible-to-only-highlight-certain-part-of-image-opacity

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!