Почему я продолжаю получать исключения? - PullRequest
1 голос
/ 13 января 2011

Есть некоторый код:

struct BitmapDataAccessor
{
    private readonly byte[] data;
    private readonly int[] rowStarts;
    public readonly int Height;
    public readonly int Width;
    public readonly int Padding;

    public BitmapDataAccessor(byte[] data, int width, int height, int padding)
    {
        this.data = data;
        this.Height = height;
        this.Width = width + (4-padding)%4;
        this.Padding = padding;
        rowStarts = new int[Height];
        for (int y = 0; y < Height; y++)
            rowStarts[y] = y * Width;
    }

    public byte this[int x, int y, int color] // Maybe use an enum with Red = 0, Green = 1, and Blue = 2 members?
    {
        get { return data[(rowStarts[y] + x) * 3 + color]; }
        set { data[(rowStarts[y] + x) * 3 + color] = value; }
    }

    public byte[] Data
    {
        get { return data; }
    }
}

public static byte[, ,] Bitmap2Byte(Bitmap obraz)
{
    int h = obraz.Height;
    int w = obraz.Width;

    byte[, ,] wynik = new byte[w, h, 3];

    BitmapData bd = obraz.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

    int bytes  = Math.Abs(bd.Stride) * h;

    byte[] rgbValues = new byte[bytes];
    IntPtr ptr = bd.Scan0;

    System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);

    int padding = Math.Abs(bd.Stride) - (((w * 24) + 7) / 8);

    BitmapDataAccessor bda = new BitmapDataAccessor(rgbValues, w, h, padding);

    for (int i = 0; i < h; i++)
    {
        for (int j = 0; j < w; j++)
        {
            wynik[j, i, 0] = bda[j, i, 2];
            wynik[j, i, 1] = bda[j, i, 1];
            wynik[j, i, 2] = bda[j, i, 0];
        }
    }

    obraz.UnlockBits(bd);
    return wynik;
}

И это тест, который я запускаю:

private void btnLoad_Click(object sender, EventArgs e)
{
    string s = "";
    for (int i = 400; i < 409; i++)
    {
        Bitmap bmp = new Bitmap(i, i);
        bool ok = true;
        try
        {
            byte[,,] tab = Grafika.Bitmap2Byte(bmp);
        }
        catch
        {
            ok = false;
        }

        s += i + ": ";
        if (ok) s += "OK";
        else s += "NOT OK";
        s += "\n";
    }

    StreamWriter w = StreamWriter(@"C:\lalala.txt");
    w.Write(s);
    w.Close();
    return;
}

Выход:

400: OK
401: NOT OK
402: NOT OK
403: OK
404: OK
405: NOT OK
406: NOT OK
407: OK
408: OK

Почему я получаю исключения для w % 4 ==1 или w % 4 == 2?

EDIT:
Я знаю, что является исключением. Это «Индекс вне границ», и это происходит в строке
get { return data[(rowStarts[y] + x) * 3 + color]; }.
К сожалению, я не знаю, как этого избежать и почему это происходит только для w % 4 ==1 или w % 4 == 2. Вот почему я разместил свой вопрос. Мне не нужна помощь в идентификации исключения или его обработке. Мне просто нужно исправить BitmapDataAccessor и / или Bitmap2Byte, чтобы заставить его работать.

РЕДАКТИРОВАТЬ 2: Благодаря ответу Макса Bitmap2Byte не вызывает исключений. Но это делает мою другую функцию Byte2Bitmap Бросить их:

    public static Bitmap Byte2Bitmap(byte[, ,] tablica)
    {
        if (tablica.GetLength(2) != 3)
        {
            throw new NieprawidlowyWymiarTablicyException();
        }

        int w = tablica.GetLength(0);
        int h = tablica.GetLength(1);


        int padding = w % 4;
        int ww=w;

        if (padding != 0)
            ww += 4 - padding;

        int bytes = 3 * ww * h;
        byte[] rgbValues = new byte[bytes];

        int counter = -3;

        for (int j = 0; j < h; j++)
        {
            for (int i = 0; i < w; i++)
            {
                counter += 3;
                rgbValues[counter] = tablica[i, j, 2];
                rgbValues[counter + 1] = tablica[i, j, 1];
                rgbValues[counter + 2] = tablica[i, j, 0];                    
            }                

            if(padding!=0)
                counter+= padding;
        }


        Bitmap obraz = new Bitmap(w, h, PixelFormat.Format24bppRgb);
        BitmapData bd = obraz.LockBits(new Rectangle(0, 0, w, h), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);            
        IntPtr ptr = bd.Scan0;

        System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);

        obraz.UnlockBits(bd);

        return obraz;
    }

И мой вывод, чтобы показать, для какой ширины есть исключение:

380: OK
381: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
382: OK
383: OK
384: OK
385: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
386: OK
387: OK
388: OK
389: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
390: OK
391: OK
392: OK
393: OK
394: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
395: OK
396: OK
397: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
398: OK
399: OK
400: OK
401: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
402: OK
403: OK
404: OK
405: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
406: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
407: OK
408: OK
409: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
410: OK
411: OK
412: OK
413: OK
414: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
415: OK
416: OK
417: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
418: OK
419: OK
420: OK
421: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
422: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
423: OK
424: OK
425: NOT OK: Nastąpiła próba odczytu lub zapisu pamięci chronionej. Często wskazuje to, że inna pamięć jest uszkodzona.
426: OK
427: OK
428: OK

Исключение происходит в строке:
System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
и его текст означает: There was try to read or write to/from protected memory...
Я думаю, это потому, что bytes не рассчитан правильно. Как я могу сделать это правильно? Я не знаю, как получить сообщение об исключении на английском языке. Извините за это.

Ответы [ 3 ]

3 голосов
/ 13 января 2011

Попробуйте вычислить заполнение как

int padding = Math.Abs(bd.Stride) - (w * 3);

(по крайней мере, оно будет более читабельным)

И исправьте для BitmapDataAccessor:

public BitmapDataAccessor(byte[] data, int width, int height, int padding)
{
    this.data = data;
    this.Height = height;
    // width in bytes, not pixels
    this.Width = width * 3 + padding;
    this.Padding = padding;
    rowStarts = new int[Height];
    for(int y = 0; y < Height; y++)
        rowStarts[y] = y * Width;
}

public byte this[int x, int y, int color]
{
    // row offsets already in bytes, so don't myltiply them by 3
    get { return data[rowStarts[y] + x * 3 + color]; }
    set { data[rowStarts[y] + x * 3 + color] = value; }
}

Примечания о второмПроблема:

Аналогичная проблема здесь - вы вычисляете заполнение для пикселей, а не байтов.

int ww = w * 3;
int padding = (4 - (ww % 4)) % 4;
int bytes = (ww + padding) * h;
byte[] rgbValues = new byte[bytes];

int rowOffset = 0;
for (int j = 0; j < h; j++)
{
    int pixelOffset = rowOffset;
    for (int i = 0; i < w; i++)
    {
        rgbValues[pixelOffset + 0] = tablica[i, j, 2];
        rgbValues[pixelOffset + 1] = tablica[i, j, 1];
        rgbValues[pixelOffset + 2] = tablica[i, j, 0];                    
        pixelOffset += 3;
    }
    rowOffset += ww + padding;
}
3 голосов
/ 13 января 2011

Было бы более полезно вместо простого сбора 'ok' или 'not ok', чтобы фактически проверить выбрасываемое исключение, которое даст вам больше информации о том, что на самом деле было не в порядке.

Попробуйте добавить

Console.WriteLine(ex.ToString());

в блок catch.

1 голос
/ 13 января 2011

Заполнение не является числовым или пиксельным.Это число байтов.
Но, тем не менее, я не знаю, как исправить ваш код.Может быть, кто-то еще мог сделать это?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...