Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 我的抖动算法非常慢_C#_Multithreading_Algorithm_Optimization_Dithering - Fatal编程技术网

C# 我的抖动算法非常慢

C# 我的抖动算法非常慢,c#,multithreading,algorithm,optimization,dithering,C#,Multithreading,Algorithm,Optimization,Dithering,这里有一些背景。我正在开发一款名为ShiftOS的游戏,它发生在一个操作系统中,一开始只是一个简单的80年代操作系统,没有多少功能 我正在尝试添加一个机制,用户必须从二进制(2色)颜色深度开始,并且只能在屏幕上显示黑白。然后他们必须将颜色深度从1位升级到2位,再升级到4位,一直升级到24位。这是一个非常好的机械师,但在实践中,这似乎是非常困难的 当然,当时的旧系统至少尝试过让图像看起来更漂亮,但当然它们受到工程师提供的调色板的限制,因此它们必须抖动图像以安排像素,使其看起来像图像使用了更多颜色,

这里有一些背景。我正在开发一款名为ShiftOS的游戏,它发生在一个操作系统中,一开始只是一个简单的80年代操作系统,没有多少功能

我正在尝试添加一个机制,用户必须从二进制(2色)颜色深度开始,并且只能在屏幕上显示黑白。然后他们必须将颜色深度从1位升级到2位,再升级到4位,一直升级到24位。这是一个非常好的机械师,但在实践中,这似乎是非常困难的

当然,当时的旧系统至少尝试过让图像看起来更漂亮,但当然它们受到工程师提供的调色板的限制,因此它们必须抖动图像以安排像素,使其看起来像图像使用了更多颜色,而实际上它只能使用2

因此,我查找了一些好的抖动算法,开始学习Floyd Steinberg算法,并很快将其移植到
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));
        }
    }
}
var bmp=新位图(source.Width、source.Height);
var sourceBmp=(位图)源;
整数误差=0;
对于(int y=0;y=127)
{
错误=灰色-255;
bmp.SetPixel(x,y,Color.White);
}
其他的
{
误差=灰色;
bmp.SetPixel(x,y,Color.Black);
}
/*
*像素误差扩散图:弗洛伊德·斯坦伯格。感谢维基百科。
* 
*像素[x+1][y]:=像素[x+1][y]+数量误差*7/16
*像素[x-1][y+1]:=像素[x-1][y+1]+数量误差*3/16
*像素[x][y+1]:=像素[x][y+1]+数量误差*5/16
*像素[x+1][y+1]:=像素[x+1][y+1]+数量误差*1/16
*/
如果(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;
如果(bottomRightGray>255)
bottomRightGray=255;
sourceBmp.SetPixel(x-1,y+1,Color.FromArgb(bottomRightGray,bottomRightGray,bottomRightGray));
}
如果(x+1!=sourceBmp.Width)
{
var rightColor=sourceBmp.GetPixel(x+1,y);
int rightGray=((rightColor.R+rightColor.G+rightColor.B)/3+((error*7)/16);
如果(右灰色<0)
rightGray=0;
如果(右灰色>255)
rightGray=255;
sourceBmp.SetPixel(x+1,y,Color.FromArgb(rightGray,rightGray,rightGray));
}
如果(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;
如果(bottomRightGray>255)
bottomRightGray=255;
sourceBmp.SetPixel(x+1,y+1,Color.FromArgb(bottomRightGray,bottomRightGray,bottomRightGray));
}
如果(y+1!=sourceBmp.Height)
{
var bottomColor=sourceBmp.GetPixel(x,y+1);
int bottomGray=((bottomColor.R+bottomColor.G+bottomColor.B)/3+((错误*5)/16);
如果(底部灰色<0)
底部灰色=0;
如果(底部灰色>255)
底部灰色=255;
sourceBmp.SetPixel(x,y+1,Color.FromArgb(bottomGray,bottomGray,bottomGray));
}
}
}
请注意,
source
是通过参数传递给函数的
图像

这段代码运行得很好,但问题是,抖动发生在一个单独的线程上,以最小化游戏中的减速/延迟,并且在抖动发生时,会显示操作系统的常规24位颜色/图像。如果抖动不需要这么长时间,这就可以了

然而,我注意到这段代码中的算法非常慢,根据我抖动的图像的大小,抖动过程可能需要一分钟以上

我已经应用了我能想到的所有优化——比如在游戏线程之外的一个单独线程中运行东西,并在线程结束时调用一个给定给函数的操作,但这只会减少一点点时间(如果有的话)


所以我想知道是否有任何进一步的优化,使其运行更快,如果可能的话,总共几秒钟。我还想指出,当抖动操作发生时,我有明显的系统延迟-鼠标有时甚至会抖动和跳跃。对于那些必须拥有60FPS速度的PC高手来说,这并不酷。

我脑子里首先想到的是如何处理位图,就像处理数组一样。默认情况下,这不是一个选项,因为没有接口可以做到这一点,但您可以通过一些技巧实现这一点。快速搜索跟着我到了。因此,您必须将您的方法设置为不安全,使用锁位获取像素值,并使用指针数学访问它们(完整代码请参阅原始答案):


pixel
将是一个数组,其中包含
字节中编码的颜色信息。正如您已经看到的,
GetPixel
SetPixel
速度很慢,因为它们实际上是调用
锁位来确保操作。阵列将帮助您删除读取操作,然而,“SetPixel”仍然可能是一个瓶颈
System.Drawing.Imaging.BitmapData bmpData =
    bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite,
    bmp.PixelFormat);
var pt = (byte*)bmpData.Scan0;
// for loop
var row = pt + (y * bmpData.Stride);
var pixel = row + x * bpp; // bpp is a number of dimensions for the bitmap