Как сделать эту небезопасную структуру с фиксированным массивом динамической? (или альтернатива?)

У меня сейчас это;

    private const int PixelSizeBGR = 3;

    [StructLayout(LayoutKind.Explicit)]
    private unsafe struct BGR5
    {
        [FieldOffset(0)]
        private fixed byte bgr[PixelSizeBGR * 5];

        public BGR5(byte b, byte g, byte r)
        {
            fixed (byte* v = bgr) 
            {
                int num = 0;
                do
                {
                    v[num++] = b; v[num++] = g; v[num++] = r;
                } while (num < (PixelSizeBGR * 5));
            }
        }
    }

    [StructLayout(LayoutKind.Explicit)]
    private unsafe struct BGR3
    {
        [FieldOffset(0)]
        private fixed byte bgr[PixelSizeBGR * 3];

        public BGR3(byte b, byte g, byte r)
        {
            fixed (byte* v = bgr)
            {
                int num = 0;
                do
                {
                    v[num++] = b; v[num++] = g; v[num++] = r;
                } while (num < (PixelSizeBGR * 3));
            }
        }
    }

вы можете увидеть образец, я думаю.

Есть ли способ сделать его динамичным, так как я могу узнать, что мне нужно больше из них?

или есть альтернатива?

практический пример;

ДО, растровое изображение 24000 на 24000 пикселей, 2151 миллисекунда

            byte* row = (byte*)bmd.Scan0;

            /*** stuff ***/

            Offset1 = ((CurrentPos / GridX) * FullRow) + ((CurrentPos % GridX) * FullSquare);
            for (k = PixelSize; k <= w; k += PixelSize)
            {
                Offset1 += PixelSize;
                for (l = Stride; l <= h; l += Stride)
                {
                    row[l + Offset1] = 0; //b
                    row[l + Offset1 + 1] = 255; //g
                    row[l + Offset1 + 2] = 255; //r
                }
            }
            /*** more stuff ***/

ПОСЛЕ, растровое изображение 24000 на 24000 пикселей, 944 миллисекунды

            byte* row = (byte*)bmd.Scan0;

            /*** stuff ***/

            np5.Set(0, 255, 255);
            Offset1 = ((CurrentPos / GridX) * FullRow) + ((CurrentPos % GridX) * FullSquare) + PixelSizeBGR;
            h = Stride;
            do
            {
                *((BGR5*)(row + h + Offset1)) = np5;
                h += Stride;
            } while (h < FullRow);

            /*** more stuff ***/

ПОСЛЕ более чем на 50% быстрее


person Fredou    schedule 14.08.2013    source источник
comment
Можете ли вы объяснить причину всего этого небезопасного / фиксированного кода / кода указателя? Внутри ваших структур нет ничего, что, кажется, требует этого, но я предполагаю, что вам это нужно для того, как они используются.   -  person nicholas    schedule 14.08.2013
comment
@nicholas, я динамически создаю растровое изображение, и вместо цикла для установки 3 или 5 пикселей подряд одного цвета я использую эту структуру. Я сделал некоторые тесты, и это того стоит, я хотел бы знать, можно ли сделать это немного более общим, я не очень знаком с небезопасным/указателем, поэтому не стесняйтесь предлагать альтернативу :-)   -  person Fredou    schedule 14.08.2013
comment
я до сих пор не уверен, почему вы не можете просто использовать обычный массив байтов, а затем проанализировать его в файл с помощью ряда встроенных методов, которые включает C #. если кто-то еще не опередил меня, я отправлю ответ сегодня вечером. Кроме того, как насчет использования некоторых встроенных библиотек изображений и растровых изображений, уже поставляемых .NET?   -  person nicholas    schedule 14.08.2013
comment
@nicholas, посмотрите на один из моих предыдущих вопросов, stackoverflow.com/questions/18083318 , создав его вручную с указателем, ускоренным в 70 раз. создание растрового изображения, эти структуры, приведенные выше, устраняют необходимость в этом цикле for (k = 0; k <= w; ++k), что экономит от 15% до 20% времени обработки. в конце структура ведет себя как разворачивание цикла.   -  person Fredou    schedule 14.08.2013
comment
Вы просто не получите никакого пробега от таких заявлений. Пишите метод, а не структуру. Передайте байт* и размер, напишите цикл for().   -  person Hans Passant    schedule 14.08.2013
comment
@HansPassant, раньше у меня был цикл for, и, выполнив эту структуру, я смог удалить цикл for и увеличить скорость обработки примерно на 20–30%, я использую структуру как способ развертывания. Я собираюсь обновить свой вопрос с некоторым фактическим кодом   -  person Fredou    schedule 14.08.2013
comment
@Fredou, я подозреваю, что ваше увеличение скорости может быть связано с уменьшением количества выполняемых арифметических операций, таких как добавления в ваши индексаторы массива. Я готов поспорить, что вы сделали отдельные петли R, G и B с подходящими для инициализации, вы бы увидели аналогичный выигрыш. Я сделаю тест позже сегодня утром на нем.   -  person nicholas    schedule 15.08.2013


Ответы (2)


Не вдаваясь в вопрос о том, следует ли это сделать или есть ли лучшие способы сделать это, я попытаюсь ответить на желание OP создать структуры размера во время выполнения, которые можно использовать для блока памяти переменной длины. копии. Короче говоря, выполнение высокопроизводительных манипуляций с данными на уровне байтов больше подходит для C++, чем для C#, но, похоже, технически это все еще возможно.

Для этого мы можем использовать сортировку для динамического создания неуправляемой памяти переменного размера для хранения наших временных BGRn данных, а затем использовать P/Invoke для копирования блочной памяти.

public unsafe class BGRn
{
    IntPtr bgrPtr;
    int totalSize;
    const int pixelSizeBGR = 3;

    public BGRn(byte b, byte g, byte r, int size)
    {
        totalSize = pixelSizeBGR * size;
        bgrPtr = Marshal.AllocHGlobal(totalSize);

        byte* v = (byte*)bgrPtr;

        int num = 0;

        do
        {
            v[num++] = b;
            v[num++] = g;
            v[num++] = r;
        } while (num < (totalSize));
    } 

    public void CopyTo(IntPtr buffer)
    {
        CopyMemory(buffer, bgrPtr, (uint) totalSize);
    }

    [DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
    private static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);

}

Затем вы можете оставить другой код как есть, за исключением замены:

BGR5 np5 = new BGR5(0, 255, 255);
// ...
*((BGR5*)(row + h + Offset1)) = np5;

с:

BGRn np5 = new BGRn(0, 255, 255, 5);
// ...
np5.CopyTo((IntPtr)(row + h + Offset1));

Конечно, я пропустил Marshal.FreeHGlobal, который должен сопровождать этот класс, но пользователь должен решить, как и когда освобождать память (вероятно, реализуя операторы IDisposable и using).

На самом деле я не проверял, как работает этот код и работает ли он вообще, но он компилируется.

person nicholas    schedule 16.08.2013
comment
Я собираюсь сделать некоторые тесты позже сегодня, +1 сейчас, спасибо :-) - person Fredou; 16.08.2013
comment
к сожалению, этот код кажется в 3 раза медленнее, чем использование структуры :-( кстати, вы должны исправить предоставленный код, вы переключили copyTo dest/src, должно быть CopyMemory(buffer, bgrPtr, (uint) totalSize); и totasize должен быть totalSize = pixelSizeBGR * размер; - person Fredou; 16.08.2013

Использование базового класса absract может упростить работу с растровыми изображениями любой ширины, хотя вам все равно придется писать отдельный класс для каждой ширины растрового изображения. Вам не нужны структуры — вы можете писать прямо в растровую память.

using System.Drawing.Imaging;

namespace TestProject2
{
public abstract class BGRBase
{
    //init to your bitmap's BitmapData, obtained by calling Bitmap.LockBits
    protected readonly BitmapData data;

    public abstract void SetRow(int rowIndex, byte b, byte g, byte r);
}

public class BGR3 : BGRBase
{
    //use constructor to ensure that the bitmap's width is compatible

    public unsafe override void SetRow(int rowIndex, byte b, byte g, byte r)
    {
        byte* row = (byte*)data.Scan0 + rowIndex * data.Stride;
        row[0] = row[3] = row[6] = b;
        //etc
    }
}

public class BGR5 : BGRBase
{
    public override unsafe void SetRow(int rowIndex, byte b, byte g, byte r)
    {
        //code as adove
    }
}
}

Или используйте делегат, чтобы инкапсулировать соответствующий метод и вызвать его в цикле.

public static void Set5(byte* p, byte b, byte g, byte r)

public static void Set3(byte* p, byte b, byte g, byte r)

//usage
public void Draw(Rectangle rect, byte b, byte g, byte r)
{
    Action<byte*, byte, byte, byte> setRow = null;
    switch(rect.Width)
    {
        case 3: setRow = Set3; break;
        case 5: setRow = Set5; break;
        //etc
    }
    byte* p=(byte*)bmd.Scan0 + bmd.Stride * rect.Y + 3 * rect.X;
    while(p < endAddress)
    {
        setRow(p, b, g, r);
        p+=bmd.Stride;  
    }
}
person RoadBump    schedule 14.08.2013