GDI+函数怎么这么快?
How are GDI+ functions so fast?
我正在尝试重新创建非常简单的 GDI+ 功能,例如缩放和旋转图像。原因是一些 GDI 函数不能在多线程上完成(我发现了一个使用进程的工作,但不想进入那个),并且在一个线程上处理数千个图像几乎没有削减它。
另外我的图像是灰度的,所以自定义函数只需要担心一个值而不是 4 个。
无论我尝试重新创建什么样的函数,即使在高度优化的情况下,它总是慢几倍,尽管与 GDI 正在做的相比已经大大简化了(我在一维字节数组上操作,一个每像素字节)
我想也许我旋转每个点的方式可能是不同的,所以我把它完全拿出来,基本上有一个函数遍历每个像素并将它设置为它已经是的,那就是仅与 GDI 的速度大致相关,即使 GDI 正在执行实际旋转并每个像素更改 4 个不同的值。
是什么让这成为可能?有没有办法用自己的函数来匹配呢?
GDI+ 代码是用 C/C++ 编写的,甚至可能部分是用汇编编写的。一些 GDI+ 调用可能会使用 GDI,这是一个旧的且优化良好的 API。即使您了解所有像素操作技巧,您也会发现性能难以匹敌。
我将自己的答案与我的代码一起添加,以帮助可能希望这样做的其他人。
通过结合使用指针并使用正弦和余弦的近似值而不是调用外部函数进行旋转,我已经非常接近达到 GDI 速度了。根本没有调用任何外部函数。
它仍然比 GDI 多花 50% 左右的时间,但我早期的实现比 GDI 多花 10 倍多的时间。当你考虑多线程时,这种方法可以比 GDI 快 10 倍。这个函数在我的机器上可以在3毫秒内旋转一张300x400的图片。
请记住,这是针对灰度图像的,输入数组中的每个字节代表一个像素。
如果您有任何让它更快的想法,请分享!
private unsafe byte[] rotate(byte[] input, int inputWidth, int inputHeight, int cx, int cy, double angle)
{
byte[] result = new byte[input.Length];
int
tx, ty, ix, iy, x1, y1;
double
px, py, fx, fy, sin, cos, v;
byte a, b;
//Approximate Sine and Cosine of the angle
if (angle < 0)
sin = 1.27323954 * angle + 0.405284735 * angle * angle;
else
sin = 1.27323954 * angle - 0.405284735 * angle * angle;
angle += 1.57079632;
if (angle > 3.14159265)
angle -= 6.28318531;
if (angle < 0)
cos = 1.27323954 * angle + 0.405284735 * angle * angle;
else
cos = 1.27323954 * angle - 0.405284735 * angle * angle;
angle -= 1.57079632;
fixed (byte* pInput = input, pResult = result)
{
byte* pi = pInput;
byte* pr = pResult;
for (int x = 0; x < inputWidth; x++)
for (int y = 0; y < inputHeight; y++)
{
tx = x - cx;
ty = y - cy;
px = tx * cos - ty * sin + cx;
py = tx * sin + ty * cos + cy;
ix = (int)px;
iy = (int)py;
fx = px - ix;
fy = py - iy;
if (ix < inputWidth && iy < inputHeight && ix >= 0 && iy >= 0)
{
//keep in array bounds
x1 = ix + 1;
y1 = iy + 1;
if (x1 >= inputWidth)
x1 = ix;
if (y1 >= inputHeight)
y1 = iy;
//bilinear interpolation using pointers
a = *(pInput + (iy * inputWidth + ix));
b = *(pInput + (y1 * inputWidth + ix));
v = a + ((*(pInput + (iy * inputWidth + x1)) - a) * fx);
pr = (pResult + (y * inputWidth + x));
*pr = (byte)(v + (((b + ((*(pInput + (y1 * inputWidth + x1)) - b) * fx)) - v) * fy));
}
}
}
return result;
}
我正在尝试重新创建非常简单的 GDI+ 功能,例如缩放和旋转图像。原因是一些 GDI 函数不能在多线程上完成(我发现了一个使用进程的工作,但不想进入那个),并且在一个线程上处理数千个图像几乎没有削减它。 另外我的图像是灰度的,所以自定义函数只需要担心一个值而不是 4 个。
无论我尝试重新创建什么样的函数,即使在高度优化的情况下,它总是慢几倍,尽管与 GDI 正在做的相比已经大大简化了(我在一维字节数组上操作,一个每像素字节)
我想也许我旋转每个点的方式可能是不同的,所以我把它完全拿出来,基本上有一个函数遍历每个像素并将它设置为它已经是的,那就是仅与 GDI 的速度大致相关,即使 GDI 正在执行实际旋转并每个像素更改 4 个不同的值。
是什么让这成为可能?有没有办法用自己的函数来匹配呢?
GDI+ 代码是用 C/C++ 编写的,甚至可能部分是用汇编编写的。一些 GDI+ 调用可能会使用 GDI,这是一个旧的且优化良好的 API。即使您了解所有像素操作技巧,您也会发现性能难以匹敌。
我将自己的答案与我的代码一起添加,以帮助可能希望这样做的其他人。
通过结合使用指针并使用正弦和余弦的近似值而不是调用外部函数进行旋转,我已经非常接近达到 GDI 速度了。根本没有调用任何外部函数。
它仍然比 GDI 多花 50% 左右的时间,但我早期的实现比 GDI 多花 10 倍多的时间。当你考虑多线程时,这种方法可以比 GDI 快 10 倍。这个函数在我的机器上可以在3毫秒内旋转一张300x400的图片。
请记住,这是针对灰度图像的,输入数组中的每个字节代表一个像素。 如果您有任何让它更快的想法,请分享!
private unsafe byte[] rotate(byte[] input, int inputWidth, int inputHeight, int cx, int cy, double angle)
{
byte[] result = new byte[input.Length];
int
tx, ty, ix, iy, x1, y1;
double
px, py, fx, fy, sin, cos, v;
byte a, b;
//Approximate Sine and Cosine of the angle
if (angle < 0)
sin = 1.27323954 * angle + 0.405284735 * angle * angle;
else
sin = 1.27323954 * angle - 0.405284735 * angle * angle;
angle += 1.57079632;
if (angle > 3.14159265)
angle -= 6.28318531;
if (angle < 0)
cos = 1.27323954 * angle + 0.405284735 * angle * angle;
else
cos = 1.27323954 * angle - 0.405284735 * angle * angle;
angle -= 1.57079632;
fixed (byte* pInput = input, pResult = result)
{
byte* pi = pInput;
byte* pr = pResult;
for (int x = 0; x < inputWidth; x++)
for (int y = 0; y < inputHeight; y++)
{
tx = x - cx;
ty = y - cy;
px = tx * cos - ty * sin + cx;
py = tx * sin + ty * cos + cy;
ix = (int)px;
iy = (int)py;
fx = px - ix;
fy = py - iy;
if (ix < inputWidth && iy < inputHeight && ix >= 0 && iy >= 0)
{
//keep in array bounds
x1 = ix + 1;
y1 = iy + 1;
if (x1 >= inputWidth)
x1 = ix;
if (y1 >= inputHeight)
y1 = iy;
//bilinear interpolation using pointers
a = *(pInput + (iy * inputWidth + ix));
b = *(pInput + (y1 * inputWidth + ix));
v = a + ((*(pInput + (iy * inputWidth + x1)) - a) * fx);
pr = (pResult + (y * inputWidth + x));
*pr = (byte)(v + (((b + ((*(pInput + (y1 * inputWidth + x1)) - b) * fx)) - v) * fy));
}
}
}
return result;
}