Powolne operacje na bitmapach

0

Piszę program wyświetlający obraz na oscyloskopie, jeśli chodzi o działanie, to wszystko jest w najlepszym porządku ale program ma tę wadę, że poniższa procedura trwa dość długo, bo aż 160-170ms, a pożądane jest skrócenie tego czasu do max 30-40ms.

        public void DrawScreen()
        {
            // Ustalanie pozycji myszki
            Int64 MouseZ;
            GetCursorPos(out MouseZ);
            Int64 MouseX0 = MouseZ % 4294967296;
            Int64 MouseY0 = MouseZ / 4294967296;
            int MouseX = (int)MouseX0;
            int MouseY = (int)MouseY0;

            // Wspolrzedne lewego gornego naroznika
            ScreenX = MouseX - (ScreenW / 2);
            ScreenY = MouseY - (ScreenH / 2);

            // Czyszczenie obrazka zrodlowego
            ScreenPicInputG.FillRectangle(Brushes.Black, new Rectangle(0, 0, ScreenW, ScreenH));

            // !!! 27ms - wykonanie zrzutu ekranu
            ScreenPicInputG.CopyFromScreen(ScreenX, ScreenY, 0, 0, Screen.AllScreens[ScreenNr].Bounds.Size, CopyPixelOperation.SourceCopy);

            // Dorysowania kursora myszki
            ScreenPicInputG.DrawImage(MouseCursor, MouseX - ScreenX - MouseHotX, MouseY - ScreenY - MouseHotY);
            
            // Naniesienie zrzutu obrazu na drugi obraz, ten drugi obraz jest juz obrazem o identycznych wymiarach, co obraz docelowy
            ScreenPicG.DrawImage(ScreenPicInput, new Rectangle(0, 0, Wscr, 256));

            // Czyszczenie obrazu docelowego
            ScreenOutG.FillRectangle(Brushes.Black, new Rectangle(0, 0, Wscr, H));

            Color C;
            Color C0;
            Color C1;

            // !!! 140ms - obliczanie i nanoszenie pikseli na obraz docelowy
            // Obraz zawsze ma 256 linii
            for (int Y = 0; Y < 256; Y++)
            {
                // Definiowanie kolorow
                if (IsNegative)
                {
                    C0 = Color.FromArgb(Y, Y, Y);
                    C1 = Color.FromArgb(Pattern[Y + PatternPageCounter], Pattern[Y + PatternPageCounter], Pattern[Y + PatternPageCounter]);
                }
                else
                {
                    C0 = Color.FromArgb(Pattern[Y + PatternPageCounter], Pattern[Y + PatternPageCounter], Pattern[Y + PatternPageCounter]);
                    C1 = Color.FromArgb(Y, Y, Y);
                }

                // Rsowanie jednej linii
                for (int X = 0; X < Wscr; X++)
                {
                    // !!! 60ms
                    // Pobieranie koloru piksela z obrazka pośredniego (zawierającego zrzut ekranu po dopasowaniu wymiarow)
                    C = ScreenPic.GetPixel(X, Y);

                    // 5ms - obliczanie odcienia szarosci i sprawdzaniie, czy dany piksel jest zakwalifikowany jako bialy, czy jako czarny
                    if ((((C.R * 211) + (C.G * 715) + (C.B * 74)) + R.Next(0 - RandomRange, RandomRange)) < BWThreshold)
                    {
                        // !!! 70ms - czas, o jaki wydluza sie przebieg petli przy isnieniu OBU polecen "ScreenOut.SetPixel"
                        // rysowanie piksela, gdy kolor jest ciemny
                        ScreenOut.SetPixel(X, Y, C0);
                    }
                    else
                    {
                        // rysowanie piksela, gdy kolor jest jasny
                        ScreenOut.SetPixel(X, Y, C1);
                    }
                }
            }
            PatternPageCounter = PatternPageCounter + 256;
            if (PatternPageCounter == PatternPages)
            {
                PatternPageCounter = 0;
            }
        }

Za pomocą wykrzykników zaznaczyłem te instrukcje, które są wąskim gardłem. Są programy graficzne i do video, które wykonują bardziej skomplikowane operacje graficzne w krótszym czasie (nawet bez wykorzystywania karty graficznej), więc na pewno da się skrócić czas rysowania i pobierania piksela z obrazka bitmapowego. Czym zastapić polecenia ScreenOut.SetPixel() i ScreenPic.GetPixel()? Obiekty ScreenOut i ScreenPic są klasy "Bitmap".

Ja umiałbym napisać identyczny program w Delphi 5.0 oraz w Javie przy użyciu NetBeans. Który język byłby najlepszy do tego celu? Interesuje mnie tylko działanie w systemie Windows XP.

0

GetPixel i SetPixel to najwolniejszy z możliwych sposób operowania na grafice. Dali te metody chyba tylko po to, żeby pokazać, że obiektowo też można operować na bitmapach. :D

Lepiej użyć np. wskaźników:

private static void ProcessBitmap(Bitmap bitmap)
{
    GraphicsUnit unit = GraphicsUnit.Pixel;
    BitmapData data = bitmap.LockBits(Rectangle.Round(bitmap.GetBounds(ref unit)),
        ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);

    unsafe
    {
        byte* scan0 = (byte*)data.Scan0.ToPointer();
        for (int y = 0; y < data.Height; y++)
        {
            byte* bits = scan0 + y * data.Stride;

            for (int x = 0; x < data.Width; x++)
            {
                byte value = (byte)((x ^ y) % (Byte.MaxValue + 1));
                *bits = value;
                *(bits + 1) = value;
                *(bits + 2) = value;
                bits += 3;
            }
        }
    }

    bitmap.UnlockBits(data);
}
0

https://codeguru.pl/frmThread.aspx?id=490512
Tutaj masz generalnie fachową ekspertyzę metod rysowania w C# ;).

0
somekind napisał(a)

GetPixel i SetPixel to najwolniejszy z możliwych sposób operowania na grafice. Dali te metody chyba tylko po to, żeby pokazać, że obiektowo też można operować na bitmapach. :D

Lepiej użyć np. wskaźników:

private static void ProcessBitmap(Bitmap bitmap)
{
    GraphicsUnit unit = GraphicsUnit.Pixel;
    BitmapData data = bitmap.LockBits(Rectangle.Round(bitmap.GetBounds(ref unit)),
        ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);

    unsafe
    {
        byte* scan0 = (byte*)data.Scan0.ToPointer();
        for (int y = 0; y < data.Height; y++)
        {
            byte* bits = scan0 + y * data.Stride;

            for (int x = 0; x < data.Width; x++)
            {
                byte value = (byte)((x ^ y) % (Byte.MaxValue + 1));
                *bits = value;
                *(bits + 1) = value;
                *(bits + 2) = value;
                bits += 3;
            }
        }
    }

    bitmap.UnlockBits(data);
}

Satysfakcjonujaca odpowiedź, wyeliminowalem wszystkie GetPixel i SetPixel i otrzymałem zamierzony skutek.

Czy da się zastąpić czymś szybszym wykonanie zrzutu ekranu, czyli metoda, która zwraca bitmapę z obrazem fragmentu ekranu mieszczącego się w prostokącie o zadanych współrzędnych?

Do "ScreenPicInputG.CopyFromScreen" doszedłem w www.google.com jak pisałem inny program, w którym wydajność nie była aż tak istotna, a tutaj jak da rade, to przydałoby się skrócić. Kiedyś w Delphi pisałem program wykonujący zrzuty ekranu i wyświetlający obraz po prostych przekształceniach, program działa na podobnej zasadzie, co program "Lupa Microsoft", w tamtym programie maksymalna możliwa częstotliwość (60Hz) dla obrazu 640x480 pikseli to żaden problem, obciążenie procka pojedyncze procenty, a tutaj walczę z działaniem na obrazie 400x256, jak da radę, to czym zastąpić "ScreenPicInputG.CopyFromScreen"? Jak widzę, ci od M$ zapomnieli o czymś takim, jak optymalizacja kodu.

0
                *bits = value;
                *(bits + 1) = value;
                *(bits + 2) = value;
bits[0] = value;
bits[1] = value;
bits[2] = value;

1 użytkowników online, w tym zalogowanych: 0, gości: 1