问题
today I already asked you a question for my picture puzzle (Original Question).
I started to rewrite my code for better performance. And I got the most important part done!
But I have another problem.. I generate a gray overlay image to hide the image, but because I want to handle dynamic size of pictures I cannot set fixed width and height values for the "mask". So probably not the whole picture is overlayed with my "mask".
Does somebody have a solution for this? I don't know how to set the position and size of my mask exactly to the position and size of the picture.
I attached a screencast for demonstration.
XAML:
<Window x:Class="PicturePuzzle.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow"
        Loaded="WindowLoaded">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*" />
            <RowDefinition Height="80" />
        </Grid.RowDefinitions>
        <Grid x:Name="grid"
              Margin="5"
              HorizontalAlignment="Center"
              VerticalAlignment="Top">
            <Image x:Name="imgPicture"
                   HorizontalAlignment="Stretch"
                   VerticalAlignment="Stretch"
                   Source="Images/puzzle.gif"
                   Stretch="Uniform" />
            <Image x:Name="imgMask" RenderOptions.EdgeMode="Aliased" />
        </Grid>
        <StackPanel Grid.Row="1"
                    HorizontalAlignment="Center"
                    VerticalAlignment="Center">
            <StackPanel Margin="0,0,0,10" Orientation="Horizontal">
                <Button x:Name="btnStart"
                        Width="60"
                        Margin="0,0,5,0"
                        Click="BtnStartClick"
                        Content="Start" />
                <Button x:Name="btnStop"
                        Width="60"
                        Click="BtnStopClick"
                        Content="Stop"
                        IsEnabled="False" />
                <ToggleButton x:Name="btnSolution"
                              Margin="5,0,0,0"
                              Checked="btnSolution_Checked"
                              Content="Lösung anzeigen"
                              Unchecked="btnSolution_Unchecked" />
            </StackPanel>
            <Slider x:Name="slSpeed"
                    IsDirectionReversed="True"
                    Maximum="10"
                    Minimum="1"
                    ValueChanged="SlSpeedValueChanged"
                    Value="10" />
        </StackPanel>
    </Grid>
</Window>
Codebehind:
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
namespace PicturePuzzle
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow
    {
        public MainWindow()
        {
            InitializeComponent();
            _positionAlphaValues = new Dictionary<Point, byte>();
            _images = new List<FileInfo>();
            using (var s = new StreamReader("Images.txt"))
            {
                while (!s.EndOfStream)
                {
                    var line = s.ReadLine();
                    if (string.IsNullOrWhiteSpace(line))
                    {
                        continue;
                    }
                    var fi = new FileInfo(line);
                    if (!fi.Exists)
                    {
                        continue;
                    }
                    _images.Add(fi);
                }
            }
        }
        private const int MaxQuadsX = 5;
        private const int MaxQuadsY = 5;
        private readonly List<FileInfo> _images;
        private DispatcherTimer _timer;
        private DispatcherTimer _alphaTimer;
        private WriteableBitmap _bitmap;
        private Size _size;
        private List<Point> _positions;
        private Dictionary<Point, byte> _positionAlphaValues;
        private int _tickCounter;
        private int _imageCounter;
        private int _quadWidth;
        private int _quadHeight;
        private void WindowLoaded(object sender, RoutedEventArgs e)
        {
            _size = imgPicture.RenderSize;
            var width = (int)Math.Ceiling(_size.Width);
            var height = (int)Math.Ceiling(_size.Height);
            _quadWidth = width / MaxQuadsX;
            _quadHeight = height / MaxQuadsY;
            imgPicture.Width = _quadWidth * MaxQuadsX - 5;
            imgPicture.Height = _quadHeight * MaxQuadsY - 5;
            _bitmap = new WriteableBitmap(width, height, 96, 96, PixelFormats.Bgra32, null);
            imgMask.Source = _bitmap;
        }
        #region Click handlers
        private void BtnStartClick(object sender, RoutedEventArgs e)
        {
            btnStart.IsEnabled = false;
            btnStop.IsEnabled = true;
            btnSolution.IsChecked = false;
            // set the real picture
            _imageCounter = 0;
            _images.Shuffle();
            SetPuzzlePicture();
            _timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(slSpeed.Value / 10) };
            _timer.Tick += TimerTick;
            _timer.Start();
            _alphaTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(0.1) };
            _alphaTimer.Tick += AlphaTimerOnTick;
            _alphaTimer.Start();
        }
        private void BtnStopClick(object sender, RoutedEventArgs e)
        {
            btnStart.IsEnabled = true;
            btnStop.IsEnabled = false;
            _timer.Stop();
            _alphaTimer.Stop();
        }
        private void SlSpeedValueChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {
            if (_timer != null)
            {
                _timer.Interval = TimeSpan.FromSeconds(slSpeed.Value / 10);
            }
        }
        private void btnSolution_Checked(object sender, RoutedEventArgs e)
        {
            btnStop.IsEnabled = false;
            StopTimers();
            imgMask.Visibility = Visibility.Hidden;
        }
        private void btnSolution_Unchecked(object sender, RoutedEventArgs e)
        {
            btnStart.IsEnabled = true;
            btnStop.IsEnabled = false;
            ResetMaskImage();
        }
        #endregion
        private void SetPuzzlePicture()
        {
            _positionAlphaValues.Clear();
            ResetMaskImage();
            var imgFile = _images[_imageCounter++];
            var image = new BitmapImage();
            image.BeginInit();
            image.UriSource = new Uri(imgFile.FullName, UriKind.Absolute);
            image.EndInit();
            imgPicture.Source = image;
        }
        private void TimerTick(object sender, EventArgs e)
        {
            if (_tickCounter >= _positions.Count)
            {
                if (_imageCounter >= _images.Count)
                {
                    _timer.Stop();
                    btnStart.IsEnabled = true;
                    btnStop.IsEnabled = false;
                    return;
                }
                SetPuzzlePicture();
            }
            var randomPoint = _positions[_tickCounter++];
            _positionAlphaValues.Add(randomPoint, 255);
        }
        private void AlphaTimerOnTick(object sender, EventArgs eventArgs)
        {
            var updatedList = new Dictionary<Point, byte>();
            foreach (var e in _positionAlphaValues)
            {
                var newValue = e.Value - (11 - slSpeed.Value) * 5;
                if (newValue <= 0)
                {
                    continue;
                }
                SetAlphaChannel(e.Key, (byte)newValue);
                updatedList.Add(e.Key, (byte)newValue);
            }
            _positionAlphaValues = updatedList;
        }
        private void StopTimers()
        {
            if (_timer != null)
            {
                _timer.Stop();
            }
            if (_alphaTimer != null)
            {
                _alphaTimer.Stop();
            }
        }
        private void ResetMaskImage()
        {
            imgMask.Visibility = Visibility.Visible;
            var width = _quadWidth * MaxQuadsX;
            var height = _quadHeight * MaxQuadsY;
            var size = width * height * 4;
            var buffer = new byte[size];
            for (int i = 0; i < size; i++)
            {
                buffer[i++] = 128;
                buffer[i++] = 128;
                buffer[i++] = 128;
                buffer[i] = 255;
            }
            var area = new Int32Rect(0, 0, width, height);
            _bitmap.WritePixels(area, buffer, width * 4, 0);
            _positions = GetPositions();
            _tickCounter = 0;
        }
        private void SetAlphaChannel(Point point, byte alpha)
        {
            var size = _quadWidth * _quadHeight * 4;
            var buffer = new byte[size];
            for (int i = 0; i < size; i++)
            {
                buffer[i++] = 128;
                buffer[i++] = 128;
                buffer[i++] = 128;
                buffer[i] = alpha;
            }
            var startX = (int)point.X * _quadWidth;
            var startY = (int)point.Y * _quadHeight;
            var area = new Int32Rect(startX, startY, _quadWidth, _quadHeight);
            _bitmap.WritePixels(area, buffer, _quadWidth * 4, 0);
        }
        private List<Point> GetPositions()
        {
            var generated = new List<Point>();
            for (int y = 0; y < MaxQuadsY; y++)
            {
                for (int x = 0; x < MaxQuadsX; x++)
                {
                    var point = new Point(x, y);
                    generated.Add(point);
                }
            }
            generated.Shuffle();
            return generated;
        }
    }
}
Extensions.cs
using System;
using System.Collections.Generic;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace PicturePuzzle
{
    public static class Extensions
    {
        public static Color GetPixel(this WriteableBitmap wbm, int x, int y)
        {
            if (y > wbm.PixelHeight - 1 || x > wbm.PixelWidth - 1)
                return Color.FromArgb(0, 0, 0, 0);
            if (y < 0 || x < 0)
                return Color.FromArgb(0, 0, 0, 0);
            if (!wbm.Format.Equals(PixelFormats.Bgra32))
                return Color.FromArgb(0, 0, 0, 0);
            IntPtr buff = wbm.BackBuffer;
            int stride = wbm.BackBufferStride;
            Color c;
            unsafe
            {
                var pbuff = (byte*)buff.ToPointer();
                int loc = y * stride + x * 4;
                c = Color.FromArgb(
                  pbuff[loc + 3],
                  pbuff[loc + 2], pbuff[loc + 1],
                  pbuff[loc]);
            }
            return c;
        }
        public static void Shuffle<T>(this IList<T> list)
        {
            var rng = new Random();
            int n = list.Count;
            while (n > 1)
            {
                n--;
                int k = rng.Next(n + 1);
                T value = list[k];
                list[k] = list[n];
                list[n] = value;
            }
        }
    }
}
Example Images.txt (has to be in your output folder, the images also):
Desert.jpg
Hydrangeas.jpg
Jellyfish.jpg
Koala.jpg
Lighthouse.jpg
Penguins.jpg
Tulips.jpg
androids.gif
Chrysanthemum.jpg
And the screencast: Screencast Link
Thanks for any help!
回答1:
You can set the HorizontalAlignment and VerticalAlignment of your mask Image to Stretch and the Stretch to Fill like so:
<Image HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="imgMask" Stretch="Fill" RenderOptions.EdgeMode="Aliased" />
This will cause the mask image to fill the grid, which will be sized to the other image.
来源:https://stackoverflow.com/questions/13497896/picture-puzzle-set-one-image-to-the-same-size-and-position-to-another-image