Загрузка данных
using Microsoft.Win32;
using System;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace Steganography
{
public partial class MainWindow : Window
{
private BitmapSource originalBitmap;
private WriteableBitmap workingBitmap;
public MainWindow()
{
InitializeComponent();
}
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 EncryptMessage(WriteableBitmap bmp, string message)
{
if (bmp == null)
throw new ArgumentNullException(nameof(bmp));
if (bmp.Format != PixelFormats.Bgra32)
throw new ArgumentException("Bitmap должен быть в формате Bgra32.");
byte[] msgBytes = Encoding.UTF8.GetBytes(message);
int msgBits = msgBytes.Length * 8;
int headerBits = 32;
int totalBits = headerBits + msgBits;
int pixelCount = bmp.PixelWidth * bmp.PixelHeight;
if (totalBits > pixelCount)
throw new InvalidOperationException(
$"Недостаточно места: требуется {totalBits} бит, доступно {pixelCount} бит.");
int stride = bmp.BackBufferStride;
int bytesCount = stride * bmp.PixelHeight;
byte[] pixels = new byte[bytesCount];
bmp.CopyPixels(pixels, stride, 0);
int msgLengthBytes = msgBytes.Length;
byte[] lenBytes = BitConverter.GetBytes(msgLengthBytes);
bool[] bits = new bool[headerBits + msgBits];
for (int i = 0; i < headerBits; i++)
{
int byteIndex = i / 8;
int bitIndex = i % 8;
bits[i] = ((lenBytes[byteIndex] >> bitIndex) & 1) == 1;
}
for (int i = 0; i < msgBytes.Length; i++)
{
for (int b = 0; b < 8; b++)
{
bits[headerBits + i * 8 + b] =
((msgBytes[i] >> b) & 1) == 1;
}
}
for (int bitIndex = 0; bitIndex < bits.Length; bitIndex++)
{
int pixelIndex = bitIndex;
int offset = pixelIndex * 4;
int row = pixelIndex / bmp.PixelWidth;
int col = pixelIndex % bmp.PixelWidth;
offset = row * stride + col * 4;
byte blue = pixels[offset];
blue = (byte)((blue & 0xFE) | (bits[bitIndex] ? 1 : 0));
pixels[offset] = blue;
}
var rect = new Int32Rect(0, 0, bmp.PixelWidth, bmp.PixelHeight);
bmp.WritePixels(rect, pixels, stride, 0);
}
public static string DecryptMessage(WriteableBitmap bmp)
{
if (bmp == null)
throw new ArgumentNullException(nameof(bmp));
if (bmp.Format != PixelFormats.Bgra32)
throw new ArgumentException("Bitmap должен быть в формате Bgra32.");
int stride = bmp.BackBufferStride;
int bytesCount = stride * bmp.PixelHeight;
byte[] pixels = new byte[bytesCount];
bmp.CopyPixels(pixels, stride, 0);
int pixelCount = bmp.PixelWidth * bmp.PixelHeight;
if (pixelCount < 32)
throw new InvalidOperationException(
"Картинка слишком мала для хранения заголовка.");
byte[] lenBytes = new byte[4];
for (int i = 0; i < 32; i++)
{
int pixelIndex = i;
int row = pixelIndex / bmp.PixelWidth;
int col = pixelIndex % bmp.PixelWidth;
int offset = row * stride + col * 4;
byte blue = pixels[offset];
int bit = blue & 1;
int byteIndex = i / 8;
int bitIndex = i % 8;
lenBytes[byteIndex] |= (byte)(bit << bitIndex);
}
int msgLengthBytes = BitConverter.ToInt32(lenBytes, 0);
if (msgLengthBytes < 0)
throw new InvalidOperationException(
"Неверная длина сообщения (отрицательная).");
int totalBitsNeeded = 32 + msgLengthBytes * 8;
if (totalBitsNeeded > pixelCount)
throw new InvalidOperationException(
$"Заявленная длина ({msgLengthBytes} байт) не помещается в изображении.");
byte[] msgBytes = new byte[msgLengthBytes];
for (int i = 0; i < msgLengthBytes * 8; i++)
{
int bitPos = 32 + i;
int pixelIndex = bitPos;
int row = pixelIndex / bmp.PixelWidth;
int col = pixelIndex % bmp.PixelWidth;
int offset = row * stride + col * 4;
byte blue = pixels[offset];
int bit = blue & 1;
int byteIndex = i / 8;
int bitIndex = i % 8;
msgBytes[byteIndex] |= (byte)(bit << bitIndex);
}
string message = Encoding.UTF8.GetString(msgBytes);
return message;
}
private void BtnLoad_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));
originalBitmap = ConvertToBgra32(bmp);
workingBitmap = new WriteableBitmap(originalBitmap);
ImagePreview.Source = workingBitmap;
BtnEncrypt.IsEnabled = true;
BtnDecrypt.IsEnabled = true;
BtnSave.IsEnabled = true;
}
}
private void BtnSave_Click(object sender, RoutedEventArgs e)
{
if (workingBitmap == 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(workingBitmap));
encoder.Save(fs);
}
}
}
private void BtnEncrypt_Click(object sender, RoutedEventArgs e)
{
if (workingBitmap == null)
return;
var message = TxtMessage.Text ?? "";
if (string.IsNullOrWhiteSpace(message))
{
MessageBox.Show(
"Введите сообщение (слово).",
"Внимание",
MessageBoxButton.OK,
MessageBoxImage.Warning);
return;
}
try
{
EncryptMessage(workingBitmap, message);
ImagePreview.Source = workingBitmap;
}
catch (Exception ex)
{
MessageBox.Show(
"Ошибка при шифровании: " + ex.Message,
"Ошибка",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
private void BtnDecrypt_Click(object sender, RoutedEventArgs e)
{
if (workingBitmap == null)
return;
try
{
var msg = DecryptMessage(workingBitmap);
MessageBox.Show(
$"Извлечённое сообщение:\n{msg}",
"Результат",
MessageBoxButton.OK,
MessageBoxImage.Information);
}
catch (Exception ex)
{
MessageBox.Show(
"Ошибка при дешифровке: " + ex.Message,
"Ошибка",
MessageBoxButton.OK,
MessageBoxImage.Error);
}
}
}
}