图片拼图:将一个图像设置为与另一个图像相同的大小和位置

时间:2012-11-21 16:39:38

标签: c# wpf image bitmap grid

今天我已经问了一个关于我的图片拼图的问题(Original Question)。

我开始重写代码以获得更好的性能。我完成了最重要的部分!

但我有另一个问题..我生成一个灰色的叠加图像来隐藏图像,但因为我想处理图片的动态大小,我无法为“掩码”设置固定的宽度和高度值。所以可能不是整个画面都覆盖着我的“面具”。

有人有解决方案吗?我不知道如何将我的面具的位置和大小精确地设置到图片的位置和大小。

我附上了一个截屏视频。

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>

代码隐藏:

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;
            }
        }
    }
}

示例Images.txt(必须在您的输出文件夹中,图像也是):

Desert.jpg
Hydrangeas.jpg
Jellyfish.jpg
Koala.jpg
Lighthouse.jpg
Penguins.jpg
Tulips.jpg
androids.gif
Chrysanthemum.jpg

截屏视频:Screencast Link

感谢您的帮助!

1 个答案:

答案 0 :(得分:2)

您可以将面具图片的HorizontalAlignmentVerticalAlignment设置为Stretch,将Stretch设置为Fill,如下所示:

<Image HorizontalAlignment="Stretch" VerticalAlignment="Stretch" x:Name="imgMask" Stretch="Fill" RenderOptions.EdgeMode="Aliased" />

这将导致遮罩图像填充网格,网格将根据另一个图像调整大小。