C#、Winform
編集座標配列の塗りつぶし方法を変更すると、さまざまなパターンを使用できます-以下を参照
この種のパターンが好きですか?
ボーナス:
上半分のすべてのピクセルと下半分のすべてのピクセルを1回だけランダムに交換します。スクランブル解除のために同じ手順を繰り返します(ボーナス)。
コード
Scramble.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.IO;
namespace Palette
{
public partial class Scramble : Form
{
public Scramble()
{
InitializeComponent();
}
public struct Coord
{
public int x, y;
}
private void Work(Bitmap srcb, Bitmap outb)
{
int w = srcb.Width, h = srcb.Height;
Coord[] coord = new Coord[w * h];
FastBitmap fsb = new FastBitmap(srcb);
FastBitmap fob = new FastBitmap(outb);
fsb.LockImage();
fob.LockImage();
ulong seed = 0;
int numpix = 0;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; numpix++, x++)
{
coord[numpix].x = x;
coord[numpix].y = y;
uint color = fsb.GetPixel(x, y);
seed += color;
fob.SetPixel(x, y, color);
}
fsb.UnlockImage();
fob.UnlockImage();
pbOutput.Refresh();
Application.DoEvents();
int half = numpix / 2;
int limit = half;
XorShift rng = new XorShift(seed);
progressBar.Visible = true;
progressBar.Maximum = limit;
fob.LockImage();
while (limit > 0)
{
int p = (int)(rng.next() % (uint)limit);
int q = (int)(rng.next() % (uint)limit);
uint color = fob.GetPixel(coord[p].x, coord[p].y);
fob.SetPixel(coord[p].x, coord[p].y, fob.GetPixel(coord[half+q].x, coord[half+q].y));
fob.SetPixel(coord[half+q].x, coord[half+q].y, color);
limit--;
if (p < limit)
{
coord[p]=coord[limit];
}
if (q < limit)
{
coord[half+q]=coord[half+limit];
}
if ((limit & 0xfff) == 0)
{
progressBar.Value = limit;
fob.UnlockImage();
pbOutput.Refresh();
fob.LockImage();
}
}
fob.UnlockImage();
pbOutput.Refresh();
progressBar.Visible = false;
}
void DupImage(PictureBox s, PictureBox d)
{
if (d.Image != null)
d.Image.Dispose();
d.Image = new Bitmap(s.Image.Width, s.Image.Height);
}
void GetImagePB(PictureBox pb, string file)
{
Bitmap bms = new Bitmap(file, false);
Bitmap bmp = bms.Clone(new Rectangle(0, 0, bms.Width, bms.Height), PixelFormat.Format32bppArgb);
bms.Dispose();
if (pb.Image != null)
pb.Image.Dispose();
pb.Image = bmp;
}
private void btnOpen_Click(object sender, EventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.InitialDirectory = "c:\\temp\\";
openFileDialog.Filter = "Image Files(*.BMP;*.JPG;*.PNG)|*.BMP;*.JPG;*.PNG|All files (*.*)|*.*";
openFileDialog.FilterIndex = 1;
openFileDialog.RestoreDirectory = true;
if (openFileDialog.ShowDialog() == DialogResult.OK)
{
try
{
string file = openFileDialog.FileName;
GetImagePB(pbInput, file);
pbInput.Tag = file;
DupImage(pbInput, pbOutput);
Work(pbInput.Image as Bitmap, pbOutput.Image as Bitmap);
file = Path.GetDirectoryName(file) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(file) + ".scr.png";
pbOutput.Image.Save(file);
}
catch (Exception ex)
{
MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
}
}
}
}
//Adapted from Visual C# Kicks - http://www.vcskicks.com/
unsafe public class FastBitmap
{
private Bitmap workingBitmap = null;
private int width = 0;
private BitmapData bitmapData = null;
private Byte* pBase = null;
public FastBitmap(Bitmap inputBitmap)
{
workingBitmap = inputBitmap;
}
public BitmapData LockImage()
{
Rectangle bounds = new Rectangle(Point.Empty, workingBitmap.Size);
width = (int)(bounds.Width * 4 + 3) & ~3;
//Lock Image
bitmapData = workingBitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
pBase = (Byte*)bitmapData.Scan0.ToPointer();
return bitmapData;
}
private uint* pixelData = null;
public uint GetPixel(int x, int y)
{
pixelData = (uint*)(pBase + y * width + x * 4);
return *pixelData;
}
public uint GetNextPixel()
{
return *++pixelData;
}
public void GetPixelArray(int x, int y, uint[] Values, int offset, int count)
{
pixelData = (uint*)(pBase + y * width + x * 4);
while (count-- > 0)
{
Values[offset++] = *pixelData++;
}
}
public void SetPixel(int x, int y, uint color)
{
pixelData = (uint*)(pBase + y * width + x * 4);
*pixelData = color;
}
public void SetNextPixel(uint color)
{
*++pixelData = color;
}
public void UnlockImage()
{
workingBitmap.UnlockBits(bitmapData);
bitmapData = null;
pBase = null;
}
}
public class XorShift
{
private ulong x; /* The state must be seeded with a nonzero value. */
public XorShift(ulong seed)
{
x = seed;
}
public ulong next()
{
x ^= x >> 12; // a
x ^= x << 25; // b
x ^= x >> 27; // c
return x * 2685821657736338717L;
}
}
}
Scramble.designer.cs
namespace Palette
{
partial class Scramble
{
private System.ComponentModel.IContainer components = null;
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private void InitializeComponent()
{
this.panel = new System.Windows.Forms.FlowLayoutPanel();
this.pbInput = new System.Windows.Forms.PictureBox();
this.pbOutput = new System.Windows.Forms.PictureBox();
this.progressBar = new System.Windows.Forms.ProgressBar();
this.btnOpen = new System.Windows.Forms.Button();
this.panel.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.pbInput)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.pbOutput)).BeginInit();
this.SuspendLayout();
//
// panel
//
this.panel.AutoScroll = true;
this.panel.AutoSize = true;
this.panel.Controls.Add(this.pbInput);
this.panel.Controls.Add(this.pbOutput);
this.panel.Dock = System.Windows.Forms.DockStyle.Top;
this.panel.Location = new System.Drawing.Point(0, 0);
this.panel.Name = "panel";
this.panel.Size = new System.Drawing.Size(748, 306);
this.panel.TabIndex = 3;
//
// pbInput
//
this.pbInput.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pbInput.Location = new System.Drawing.Point(3, 3);
this.pbInput.MinimumSize = new System.Drawing.Size(100, 100);
this.pbInput.Name = "pbInput";
this.pbInput.Size = new System.Drawing.Size(100, 300);
this.pbInput.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.pbInput.TabIndex = 3;
this.pbInput.TabStop = false;
//
// pbOutput
//
this.pbOutput.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.pbOutput.Location = new System.Drawing.Point(109, 3);
this.pbOutput.MinimumSize = new System.Drawing.Size(100, 100);
this.pbOutput.Name = "pbOutput";
this.pbOutput.Size = new System.Drawing.Size(100, 300);
this.pbOutput.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
this.pbOutput.TabIndex = 4;
this.pbOutput.TabStop = false;
//
// progressBar
//
this.progressBar.Dock = System.Windows.Forms.DockStyle.Bottom;
this.progressBar.Location = new System.Drawing.Point(0, 465);
this.progressBar.Name = "progressBar";
this.progressBar.Size = new System.Drawing.Size(748, 16);
this.progressBar.TabIndex = 5;
//
// btnOpen
//
this.btnOpen.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.btnOpen.Location = new System.Drawing.Point(12, 429);
this.btnOpen.Name = "btnOpen";
this.btnOpen.Size = new System.Drawing.Size(53, 30);
this.btnOpen.TabIndex = 6;
this.btnOpen.Text = "Start";
this.btnOpen.UseVisualStyleBackColor = true;
this.btnOpen.Click += new System.EventHandler(this.btnOpen_Click);
//
// Scramble
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.SystemColors.ControlDark;
this.ClientSize = new System.Drawing.Size(748, 481);
this.Controls.Add(this.btnOpen);
this.Controls.Add(this.progressBar);
this.Controls.Add(this.panel);
this.Name = "Scramble";
this.Text = "Form1";
this.panel.ResumeLayout(false);
this.panel.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.pbInput)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.pbOutput)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
private System.Windows.Forms.FlowLayoutPanel panel;
private System.Windows.Forms.PictureBox pbOutput;
private System.Windows.Forms.ProgressBar progressBar;
private System.Windows.Forms.PictureBox pbInput;
private System.Windows.Forms.Button btnOpen;
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace Palette
{
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Scramble());
}
}
}
コンパイルするプロジェクトプロパティの「安全でないコード」をチェックします。
複雑なパターン
作業関数の最初の部分をApplication.DoEventsまで変更します。
int w = srcb.Width, h = srcb.Height;
string Msg = "Scramble";
Graphics gr = Graphics.FromImage(outb);
Font f = new Font("Arial", 100, FontStyle.Bold);
var size = gr.MeasureString(Msg, f);
f = new Font("Arial", w / size.Width * 110, FontStyle.Bold);
size = gr.MeasureString(Msg, f);
gr.DrawString(Msg, f, new SolidBrush(Color.White), (w - size.Width) / 2, (h - size.Height) / 2);
gr.Dispose();
Coord[] coord = new Coord[w * h];
FastBitmap fsb = new FastBitmap(srcb);
FastBitmap fob = new FastBitmap(outb);
fsb.LockImage();
fob.LockImage();
ulong seed = 1;
int numpix = h * w;
int c1 = 0, c2 = numpix;
int y2 = h / 2;
int p2 = numpix/2;
for (int p = 0; p < p2; p++)
{
for (int s = 1; s > -2; s -= 2)
{
int y = (p2+s*p) / w;
int x = (p2+s*p) % w;
uint d = fob.GetPixel(x, y);
if (d != 0)
{
c2--;
coord[c2].x = x;
coord[c2].y = y;
}
else
{
coord[c1].x = x;
coord[c1].y = y;
c1++;
}
fob.SetPixel(x, y, fsb.GetPixel(x, y));
}
}
fsb.UnlockImage();
fob.UnlockImage();
pbOutput.Refresh();
Application.DoEvents();