Повышение скорости отрисовки Bitmap с масштабированием в WinForms

Нужен совет по оптимизации. Есть винформа, в которой отображается картинка. Картинка хранится в памяти как Bitmap. Картинка используется для отображения карты (поверхности), в которой в пределах прямоугольной сетки размерами NX на NY для каждого узла сетки задано числовое значение Z, и разные значения Z отображаются разными цветами. Количество пикселей битмапа равно количеству узлов сетки. При увеличении масштаба, цвета просто интерполируются между узлами. Можно увеличивать или уменьшать масштаб формы, а также менять размер самой формы с соответствующим изменением размера картинки. Пытался сделать отрисовку битмапа через стандартный класс System.Drawing.Graphics. Проблема в том, что он очень медленно работает при масштабировании картинки. Отрисовка картинки с масштабированием на весь экран занимает 250-350 мс на 4к мониторе на моём компьютере. Кроме того, правый столбец и нижняя строка пикселей интерполируется Graphics'ом криво (цвет смешивается с фоновым цветом). В качестве эксперимента написал реализацию масштабирования битмапа (с билинейной интерполяцией) с использованием unsafe, векторами и параллелизмом. На вход подаем указатель на данные первого пикселя исходного битмапа и его размеры и также указатель целевого битмапа и его размеры. Указатель получаем через метод Bitmap.LockBits().

public static void Rescale_bmp_parallel_optimised2(
    IntPtr SrcPtr, int SrcNx, int SrcNy,
    IntPtr DstPtr, int DstNx, int DstNy)
{
    int* ᴬSrcClr = (int*)SrcPtr;
    int* ᴬDstClr = (int*)DstPtr;

    int SrcN = SrcNx * SrcNy;

    Vector3[] ᴬOldClrVec3 = new Vector3[SrcN];

    ParallelOptions po = new();
    po.MaxDegreeOfParallelism = Environment.ProcessorCount;


    Action<int> col_to_vec = (int i) =>
    {
        int color = ᴬSrcClr[i];
        float R = (color & 0x0000FF);
        float G = ((color & 0x00FF00) >> 8);
        float B = ((color & 0xFF0000) >> 16);

        ᴬOldClrVec3[i] = new Vector3(R, G, B);
    };
    Parallel.For(0, SrcN, po, col_to_vec);

    int DstN = DstNx * DstNy;

    float kx = (float)(SrcNx - 2) / (DstNx - 1);
    float ky = (float)(SrcNy - 2) / (DstNy - 1);


    if (true)
    {
        Action<int> interp = (int i) =>
        {
            int iynew = Math.DivRem(i, DstNx, out int ixnew);

            float ixoldf = ixnew * kx;
            float iyoldf = iynew * ky;

            int ix1 = (int)(ixoldf);
            int iy1 = (int)(iyoldf);

            //if (ix1 > SrcNx - 2) ix1 = SrcNx - 2;
            //if (iy1 > SrcNy - 2) iy1 = SrcNy - 2;

            int i11 = iy1 * SrcNx + ix1;
            int i12 = (iy1 + 1) * SrcNx + ix1;
            int i21 = i11 + 1;
            int i22 = i12 + 1;

            Vector3 z11 = ᴬOldClrVec3[i11];
            Vector3 z12 = ᴬOldClrVec3[i12];
            Vector3 z21 = ᴬOldClrVec3[i21];
            Vector3 z22 = ᴬOldClrVec3[i22];

            Vector3 z_on_y1_edge = Vector3.Lerp(z11, z21, ixoldf - ix1);
            Vector3 z_on_y2_edge = Vector3.Lerp(z12, z22, ixoldf - ix1);
            Vector3 z = Vector3.Lerp(z_on_y1_edge, z_on_y2_edge, iyoldf - iy1);

            ᴬDstClr[i] =
                (byte)(z.X)
                | (byte)(z.Y) << 8
                | (byte)(z.Z) << 16;
        };

        Parallel.For(0, DstN, po, interp);
    }
}

Эта реализация работает в 3-4 раза быстрее (80-90 мс на отрисовку), что уже терпимо, но всё равно долго. Можно ли сделать это за 10-20 мс без сторонних либ-обёрток вокруг DirectX и без WFP? Либо же может быть что-то похожее на приложенную ниже картинку можно нарисовать не через Bitmap? пример


Ответы (0 шт):