ファイルを読み込む
まず、<asp:FileUpload>コントロールを設置して、ファイルのアップロードを受け取れるようにします。
このコントロール、クライアントの情報を一部とはいえ引っこ抜いてしまうUIであるからなのかわかりませんが、他のコントロールと比べて制御できる幅が少ないですね。
また、僕の知識不足かも知れませんが、送信ボタンを押すたびに選択したファイルがクリアされてしまうので、HasFileプロパティだけを見て処理を分ける実装にしてしまうとパラメータを変更する度にファイルを選び直す必要があるようです。
ユーザが送信したいかなる情報もサーバに保持しないことを是とする本サイトですから、都度ファイルを選び直す実装にしてリリースしようかと思ったのですが、うちの人から激しい突っ込みが入りましたので、Session変数(つまりサーバのメモリ上)に画像を保持する方法を採用しました。
当然ですが、セッションが切れればサーバのメモリ上から自動的に消滅します。
具体的には、HasFileプロパティがtrueのときはアップロードしたファイルを読み込んでSession変数に格納して使用し、HasFileプロパティがfalseかつSession変数がNULLでないときはSession変数に格納された画像を使用する実装としております。
コードで書くと、次のような具合です。
if (this.SelectImageFile.HasFile || Session["画像データ"] != null)
{
try
{
if (this.SelectImageFile.HasFile)
using (var __stream = this.SelectImageFile.FileContent)
Session["画像データ"] = new System.Drawing.Bitmap(__stream);
// 画像処理
}
catch
{
Session.Remove("画像データ");
// 読み込めない形式です画像表示
}
}
else
{
Session.Remove("画像データ");
// 画像を選択してください画像表示
}
オリジナル画像を表示する
オリジナル画像を表示したければ上記コードのSession["画像データ"]を単にSaveメソッドでResponse.OutputStreamにSystem.Drawing.Imaging.ImageFormat.Pngとかで流せばよい、と安直に考えてしまうところですが、元の画像形式と異なる形式で流そうとすると、真っ黒な画像が出たり例外が生じて表示されなかったりします。
それでも安直な発想から抜け切れない僕は、次のようなコードで対処してしまうのでした。
protected void Page_Load(object sender, EventArgs e)
{
if (Session["画像データ"] != null)
{
var __image = (System.Drawing.Bitmap)Session["画像データ"];
using(var __outImage = new System.Drawing.Bitmap(__image.Size.Width, __image.Size.Height))
{
for (var __x = 0; __x < __image.Size.Width; __x++)
for (var __y = 0; __y < __image.Size.Height; __y++)
__outImage.SetPixel(__x, __y, __image.GetPixel(__x, __y));
__outImage.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Png);
}
}
else
{
// 元のaspxでSession["original-image-bitmap"]がNULLでないことが保証されているので、特にエラー処理はいらないと思う。
}
}
安直って素晴らしい。。。
メッシュ画像を表示する
メッシュ画像を作るには、前項で書いた安直なコードを元に、パラメータで指定したとおりに点を間引いて表示すればよい、ということになります。
アップロードする画像ファイルの大きさは、実際に受け取ってみないとわかりません。
どんな大きさのファイルを受け取っても、パラメータで指定したとおりにバランスよく間引いてあげたいところです。
そこで、まず最初に、余白を決めます。幅(高さ)をブロックサイズで割った剰余とブロックサイズから横(縦)方向の値を差し引いた値とを足した数を、2で割った値を左(上)余白としています。
また、幅(高さ)から左(上)余白の値を差し引いた値でforループを止めることで、右(下)余白を設けています。
余白が決まったら、点を間引きながらコピーするだけです。
コードで書くとこんな感じです。
protected void Page_Load(object sender, EventArgs e)
{
if (Session["画像データ"] != null)
{
var __orgImage = (System.Drawing.Bitmap)Session["画像データ"];
var __blockPixels = (int)Session["ブロックサイズ"];
var __horizontalMaxPixels = __blockPixels;
var __verticalMaxPixels = __blockPixels;
var __horizontalPixels = (int)Session["横方向"];
var __verticalPixels = (int)Session["縦方向"];
var __colorBackGround = (DrawingCustomClasses.ARGB)Session["color-back-ground"];
using (var __meshImage = new System.Drawing.Bitmap(__orgImage.Size.Width, __orgImage.Size.Height))
{
// 背景色を塗るためだけにGraphicsクラスを使用する。
using (var __graph = System.Drawing.Graphics.FromImage(__meshImage))
__graph.Clear(System.Drawing.Color.FromArgb(__colorBackGround.R, __colorBackGround.G, __colorBackGround.B));
if (__horizontalMaxPixels > __orgImage.Size.Width)
__horizontalMaxPixels = __orgImage.Size.Width;
if (__verticalMaxPixels > __orgImage.Size.Height)
__verticalMaxPixels = __orgImage.Size.Height;
// 余白を決める
var __horizontalOffset = Convert.ToInt32(Math.Floor((double)((__orgImage.Size.Width % __horizontalMaxPixels + (__blockPixels - __horizontalPixels)) / 2)));
var __verticalOffset = Convert.ToInt32(Math.Floor((double)((__orgImage.Size.Height % __verticalMaxPixels + (__blockPixels - __verticalPixels)) / 2)));
// 点を間引いてコピー
for (var __x = __horizontalOffset; __x < __orgImage.Size.Width - __horizontalOffset; __x++)
if ((__x - __horizontalOffset) % __horizontalMaxPixels < __horizontalPixels)
for (var __y = __verticalOffset; __y < __orgImage.Size.Height - __verticalOffset; __y++)
if ((__y - __verticalOffset) % __verticalMaxPixels < __verticalPixels)
__meshImage.SetPixel(__x, __y, __orgImage.GetPixel(__x, __y));
__meshImage.Save(Response.OutputStream, System.Drawing.Imaging.ImageFormat.Png);
}
}
else
{
// 元のaspxでSession["original-image-bitmap"]がNULLでないことが保証されているので、特にエラー処理はいらないと思う。
}
}
う~ん、安直ですねぇ。
これをロジックと呼んでよいかどうかは、みなさまにお任せします。。。