Загрузка данных
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}";
}
}
}
}