ここにいくつかのコンテキストがあります。私は ShiftOS と呼ばれるこのゲームに取り組んでいます。このゲームは、多くの機能を備えた 80 年代のオペレーティング システムの最低限の実行として始まる OS で行われます。
ユーザーがバイナリ (2 色) の色深度で開始する必要があり、画面に白黒しか表示できないメカニズムを追加しようとしています。次に、色深度を 1 ビットから 2 ビット、4 ビット、さらには 24 ビットにアップグレードする必要があります。これは非常に優れたメカニズムですが、実際には非常に難しいようです。
もちろん、この頃の古いシステムは、画像を見栄えよくするために少なくとも試みましたが、もちろん、エンジニアによって与えられたカラー パレットによって制限されていたので、画像をディザリングして、次のように見えるようにピクセルを配置する必要がありました。実際には2色しか使用できないのに、画像はより多くの色を使用していました.
そこで私はいくつかの優れたディザリング アルゴリズムを調べ、Floyd-Steinberg アルゴリズムの学習を開始し、すぐにそれを and に移植しましC#
たSystem.Drawing
。
これが私が使用するコードです。
var bmp = new Bitmap(source.Width, source.Height);
var sourceBmp = (Bitmap)source;
int error = 0;
for (int y = 0; y < bmp.Height; y++)
{
for (int x = 0; x < bmp.Width; x++)
{
Color c = sourceBmp.GetPixel(x, y);
int gray = ((c.R + c.G + c.B) / 3);
if (gray >= 127)
{
error = gray - 255;
bmp.SetPixel(x, y, Color.White);
}
else
{
error = gray;
bmp.SetPixel(x, y, Color.Black);
}
/*
* Pixel error diffusion map: Floyd-Steinberg. Thanks to Wikipedia.
*
* pixel[x + 1][y ] := pixel[x + 1][y ] + quant_error * 7 / 16
* pixel[x - 1][y + 1] := pixel[x - 1][y + 1] + quant_error * 3 / 16
* pixel[x ][y + 1] := pixel[x ][y + 1] + quant_error * 5 / 16
* pixel[x + 1][y + 1] := pixel[x + 1][y + 1] + quant_error * 1 / 16
*/
if(x - 1 >= 0 && y + 1 != bmp.Height)
{
var bottomRightColor = sourceBmp.GetPixel(x - 1, y + 1);
int bottomRightGray = ((bottomRightColor.R + bottomRightColor.G + bottomRightColor.B) / 3) + ((error * 3) / 16);
if (bottomRightGray < 0)
bottomRightGray = 0;
if (bottomRightGray > 255)
bottomRightGray = 255;
sourceBmp.SetPixel(x - 1, y + 1, Color.FromArgb(bottomRightGray, bottomRightGray, bottomRightGray));
}
if (x + 1 != sourceBmp.Width)
{
var rightColor = sourceBmp.GetPixel(x + 1, y);
int rightGray = ((rightColor.R + rightColor.G + rightColor.B) / 3) + ((error * 7) / 16);
if (rightGray < 0)
rightGray = 0;
if (rightGray > 255)
rightGray = 255;
sourceBmp.SetPixel(x + 1, y, Color.FromArgb(rightGray, rightGray, rightGray));
}
if (x + 1 != sourceBmp.Width && y + 1 != sourceBmp.Height)
{
var bottomRightColor = sourceBmp.GetPixel(x + 1, y + 1);
int bottomRightGray = ((bottomRightColor.R + bottomRightColor.G + bottomRightColor.B) / 3) + ((error) / 16);
if (bottomRightGray < 0)
bottomRightGray = 0;
if (bottomRightGray > 255)
bottomRightGray = 255;
sourceBmp.SetPixel(x + 1, y + 1, Color.FromArgb(bottomRightGray, bottomRightGray, bottomRightGray));
}
if (y + 1 != sourceBmp.Height)
{
var bottomColor = sourceBmp.GetPixel(x, y + 1);
int bottomGray = ((bottomColor.R + bottomColor.G + bottomColor.B) / 3) + ((error * 5) / 16);
if (bottomGray < 0)
bottomGray = 0;
if (bottomGray > 255)
bottomGray = 255;
sourceBmp.SetPixel(x, y + 1, Color.FromArgb(bottomGray, bottomGray, bottomGray));
}
}
}
は、引数を介して関数に渡されることにsource
注意してください。Image
このコードはかなりうまく機能しますが、問題は、ディザリングが別のスレッドで発生して、ゲームのスローダウン/ラグを最小限に抑えていることです。ディザリングが発生している間、オペレーティング システムの通常の 24 ビット カラー/イメージが表示されます。ディザリングにそれほど時間がかからなければ、これで問題ありません。
ただし、このコードではアルゴリズムが非常に遅く、ディザリングしている画像のサイズによっては、ディザリング プロセスに 1 分以上かかる場合があります。
ゲームスレッドとは別のスレッドで物事を実行したり、スレッドが終了したときに関数に与えられたアクションを呼び出したりするなど、考えられるすべての最適化を適用しましたが、これはあったとしてもほんの少しの時間を削るだけです.
したがって、これをさらに高速に動作させるための最適化があるかどうか、可能であれば合計で数秒かどうか疑問に思っています。また、ディザリング操作が行われている間、顕著なシステム ラグが発生していることにも注意してください。必携の 60FPS PC マスターレース担当者にとってはクールではありません。