Загрузка данных


using Microsoft.Win32;
using System;
using System.IO;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;

namespace StegoImage
{
    public partial class MainWindow : Window
    {
        private BitmapSource coverSrc;
        private BitmapSource secretSrc;
        private WriteableBitmap stegoBitmap;
        private WriteableBitmap extractedBitmap;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void UpdateInfo()
        {
            if (coverSrc != null && secretSrc != null)
            {
                TxtInfo.Text =
                    $"Фон: {coverSrc.PixelWidth}×{coverSrc.PixelHeight}. " +
                    $"Секрет: {secretSrc.PixelWidth}×{secretSrc.PixelHeight}";
            }
            else if (coverSrc != null)
            {
                TxtInfo.Text =
                    $"Фон: {coverSrc.PixelWidth}×{coverSrc.PixelHeight}";
            }
            else if (secretSrc != null)
            {
                TxtInfo.Text =
                    $"Секрет: {secretSrc.PixelWidth}×{secretSrc.PixelHeight}";
            }
        }

        private void TryEnableEmbed()
        {
            BtnEmbed.IsEnabled =
                coverSrc != null && secretSrc != null;
        }

        private int GetSelectedBits()
        {
            if (CbBits.SelectedItem is ComboBoxItem item)
            {
                if (int.TryParse(item.Content.ToString(), out int bits))
                {
                    return bits;
                }
            }

            return 2;
        }

        private static BitmapSource ConvertToBgra32(BitmapSource src)
        {
            if (src.Format == PixelFormats.Bgra32)
                return src;

            var formatted =
                new FormatConvertedBitmap(src,
                    PixelFormats.Bgra32,
                    null,
                    0);

            formatted.Freeze();

            return formatted;
        }

        public static void EmbedImage(
            WriteableBitmap coverBitmap,
            BitmapSource secret,
            int bitsPerChannel)
        {
            if (coverBitmap == null)
                throw new ArgumentNullException(nameof(coverBitmap));

            if (secret == null)
                throw new ArgumentNullException(nameof(secret));

            if (bitsPerChannel < 1 || bitsPerChannel > 3)
                throw new ArgumentOutOfRangeException(nameof(bitsPerChannel));

            if (coverBitmap.PixelWidth != secret.PixelWidth ||
                coverBitmap.PixelHeight != secret.PixelHeight)
            {
                throw new ArgumentException(
                    "Исходное изображение и скрываемое должны быть одинакового размера.");
            }

            int width = coverBitmap.PixelWidth;
            int height = coverBitmap.PixelHeight;
            int stride = coverBitmap.BackBufferStride;

            int bytesCount = stride * height;

            byte[] coverPixels = new byte[bytesCount];

            coverBitmap.CopyPixels(coverPixels, stride, 0);

            int secretStride =
                (secret.PixelWidth * secret.Format.BitsPerPixel + 7) / 8;

            byte[] secretPixels = new byte[secretStride * height];

            secret.CopyPixels(secretPixels, secretStride, 0);

            int clearMask = 0xFF << bitsPerChannel;

            int secretShift = 8 - bitsPerChannel;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int pixIndex = y * width + x;

                    int offCover = y * stride + x * 4;

                    int offSecret = y * secretStride + x * 4;

                    for (int c = 0; c < 3; c++)
                    {
                        byte coverByte = coverPixels[offCover + c];

                        byte secretByte = secretPixels[offSecret + c];

                        int secretTopBits =
                            (secretByte >> secretShift) &
                            ((1 << bitsPerChannel) - 1);

                        byte newCover =
                            (byte)((coverByte & clearMask) | secretTopBits);

                        coverPixels[offCover + c] = newCover;
                    }
                }
            }

            var rect = new Int32Rect(0, 0, width, height);

            coverBitmap.WritePixels(rect, coverPixels, stride, 0);
        }

        public static WriteableBitmap ExtractImage(
            WriteableBitmap stegoBitmap,
            int bitsPerChannel)
        {
            if (stegoBitmap == null)
                throw new ArgumentNullException(nameof(stegoBitmap));

            if (bitsPerChannel < 1 || bitsPerChannel > 3)
                throw new ArgumentOutOfRangeException(nameof(bitsPerChannel));

            int width = stegoBitmap.PixelWidth;
            int height = stegoBitmap.PixelHeight;
            int stride = stegoBitmap.BackBufferStride;

            int bytesCount = stride * height;

            byte[] stegoPixels = new byte[bytesCount];

            stegoBitmap.CopyPixels(stegoPixels, stride, 0);

            byte[] outPixels = new byte[bytesCount];

            int mask = (1 << bitsPerChannel) - 1;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    int off = y * stride + x * 4;

                    for (int c = 0; c < 3; c++)
                    {
                        int lsb = stegoPixels[off + c] & mask;

                        byte scaled =
                            (byte)((lsb * 255) / mask);

                        outPixels[off + c] = scaled;
                    }

                    outPixels[off + 3] = 255;
                }
            }

            var result = new WriteableBitmap(
                width,
                height,
                stegoBitmap.DpiX,
                stegoBitmap.DpiY,
                PixelFormats.Bgra32,
                null);

            result.WritePixels(
                new Int32Rect(0, 0, width, height),
                outPixels,
                stride,
                0);

            return result;
        }

        private void BtnLoadCover_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new OpenFileDialog
            {
                Filter =
                "Images|*.png;*.bmp;*.jpg;*.jpeg|All files|*.*"
            };

            if (dlg.ShowDialog() == true)
            {
                var bmp = new BitmapImage(new Uri(dlg.FileName));

                coverSrc = ConvertToBgra32(bmp);

                CoverPreview.Source = coverSrc;

                UpdateInfo();

                TryEnableEmbed();
            }
        }

        private void BtnLoadSecret_Click(object sender, RoutedEventArgs e)
        {
            var dlg = new OpenFileDialog
            {
                Filter =
                "Images|*.png;*.bmp;*.jpg;*.jpeg|All files|*.*"
            };

            if (dlg.ShowDialog() == true)
            {
                var bmp = new BitmapImage(new Uri(dlg.FileName));

                secretSrc = ConvertToBgra32(bmp);

                SecretPreview.Source = secretSrc;

                UpdateInfo();

                TryEnableEmbed();
            }
        }

        private void BtnEmbed_Click(object sender, RoutedEventArgs e)
        {
            if (coverSrc == null || secretSrc == null)
            {
                MessageBox.Show(
                    "Загрузите обе картинки.",
                    "Внимание",
                    MessageBoxButton.OK,
                    MessageBoxImage.Warning);

                return;
            }

            if (coverSrc.PixelWidth != secretSrc.PixelWidth ||
                coverSrc.PixelHeight != secretSrc.PixelHeight)
            {
                MessageBox.Show(
                    "Картинки должны быть одинакового размера.",
                    "Внимание",
                    MessageBoxButton.OK,
                    MessageBoxImage.Warning);

                return;
            }

            int bits = GetSelectedBits();

            try
            {
                stegoBitmap = new WriteableBitmap(coverSrc);

                EmbedImage(stegoBitmap, secretSrc, bits);

                StegoPreview.Source = stegoBitmap;

                TxtInfo.Text =
                    $"Встроено. Bits/channel = {bits}.";

                BtnSaveStego.IsEnabled = true;

                BtnExtract.IsEnabled = true;
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    "Ошибка при встраивании: " + ex.Message,
                    "Ошибка",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
            }
        }

        private void BtnExtract_Click(object sender, RoutedEventArgs e)
        {
            if (stegoBitmap == null)
            {
                MessageBox.Show(
                    "Сначала создайте стего-изображение (Embed).",
                    "Внимание",
                    MessageBoxButton.OK,
                    MessageBoxImage.Warning);

                return;
            }

            int bits = GetSelectedBits();

            try
            {
                extractedBitmap =
                    ExtractImage(stegoBitmap, bits);

                SecretPreview.Source = extractedBitmap;

                BtnSaveExtracted.IsEnabled = true;

                TxtInfo.Text +=
                    $"\nИзвлечено изображение (bits/channel = {bits}).";
            }
            catch (Exception ex)
            {
                MessageBox.Show(
                    "Ошибка при извлечении: " + ex.Message,
                    "Ошибка",
                    MessageBoxButton.OK,
                    MessageBoxImage.Error);
            }
        }

        private void BtnSaveStego_Click(object sender, RoutedEventArgs e)
        {
            if (stegoBitmap == null)
                return;

            var dlg = new SaveFileDialog
            {
                Filter = "PNG Image|*.png"
            };

            if (dlg.ShowDialog() == true)
            {
                using (var fs =
                    new FileStream(dlg.FileName, FileMode.Create))
                {
                    var encoder = new PngBitmapEncoder();

                    encoder.Frames.Add(
                        BitmapFrame.Create(stegoBitmap));

                    encoder.Save(fs);
                }

                TxtInfo.Text +=
                    $"\nСтего сохранено: {dlg.FileName}";
            }
        }

        private void BtnSaveExtracted_Click(
            object sender,
            RoutedEventArgs e)
        {
            if (extractedBitmap == null)
                return;

            var dlg = new SaveFileDialog
            {
                Filter = "PNG Image|*.png"
            };

            if (dlg.ShowDialog() == true)
            {
                using (var fs =
                    new FileStream(dlg.FileName, FileMode.Create))
                {
                    var encoder = new PngBitmapEncoder();

                    encoder.Frames.Add(
                        BitmapFrame.Create(extractedBitmap));

                    encoder.Save(fs);
                }

                TxtInfo.Text +=
                    $"\nИзвлечённое сохранено: {dlg.FileName}";
            }
        }
    }
}