szybsze malowanie

0

Cześć.
Mam mały problem.
A mianowicie chcę zrobić kursor do mojego mini painta i robię to przez rysowanie

private void RefreshCursor(MouseEventArgs e)
        {
            this.pictureBox1.Refresh();
            Graphics g =  this.pictureBox1.CreateGraphics();
            g.DrawEllipse(new Pen(Color.Black, 3), e.X - 50, e.Y - 50, 100, 100);
        }

Niestety taki sposób jest dość wolny.
Czy istnieje lepszy sposób by zrobić kursor nad PictureBox1?
Kursor ma zmieniać swoją wielkość w trakcie działania programu, więc zwyczajny kursor własnej roboty raczej odpa

0

Może nie twórz za każdym razem Graphics?

0

To nic nie daje.

Działam na obrazku 3264x2448
a szybkość rysowania wynosi : 0.125 sekundy czyli 125 milisekund.
To niestety daje wyraźny efekt przycinania.

0

tu najwięcej zżera refresh - w tym momencie pokrywasz całe płótno (czyli 3264x2448 = 8 milionów pikseli) jednolitym kolorem
jeśli chcesz szybciej to musisz zamalować tylko fragment w którym była poprzednia elipsa i narysować nową
jeżeli pod tą elipsą nic nie ma to po prostu narysuj jeszcze raz poprzednią elipsę kolorem tła

żeby uniknąć migania użyj doublebuffered albo vsync jak się da (ale to nie tą metodą)

0

W taki sposób też to zrobiłem, ale wydajność mimo tego, że wzrosła ( 62 milisekundy ) to daje to i tak ewidentny widok przycinania.
Na zasadzie ruszam myszką, a "kursor", leci zaraz za myszką.
Gdyby udało się dojść do 20-30 milisekund to byłoby cudownie.

0

Fragment mojego programu:

private void pictureBox1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
        {
            if (movingPoints == true)
            {
                Point l = GetCoordinates(e.Location);

                for (int i = 0; i < cpoints.Count; i++)
                {
                    if (cpoints.Count == 1)
                    {
                        cpoints[0].p = l;
                    }
                    else if (cpoints.Count > 1)
                    {
                        cpoints[i].p = new Point(l.X + cpoints[i].X - loc.X, l.Y + cpoints[i].Y - loc.Y);
                    }
                }
                loc = l;
                pictureBox1.Refresh();
            }
        }

Przesuwajac mysza przesuwasz punkty, ale latwo zmienisz to na kursor.
A rysowanie punktu w metodzie picturebox paint wyglada tak

private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {

for (int i = 0; i < points.Count; i++)
            {
                if (cpoints.Contains(points[i]))
                    e.Graphics.FillEllipse(brushGreen, points[i].X - 2, points[i].Y - 2, 4, 4);
                else
                    e.Graphics.FillEllipse(brushWhite, points[i].X - 2, points[i].Y - 2, 4, 4);
            }
}

Oczywiscie doublebuffered wlaczone. Zobacz jak ci dziala, u mnie jest to bardzo maly timestep

0

Twój sposób u mnie nie zadziała bo ja mam dodatkowo stretch włączone by ten obrazek normalnie skalować.

Mój kod to:

private Graphics g;

        public Form1()
        {
            InitializeComponent();
            g = this.pictureBox1.CreateGraphics();
        }

private void SetNormal(ref int x1, ref int x2, int max)
        {
            if (x1 < 0) x1 = 0;
            if (x2 > max) x2 = max;
        }

private void RefreshCursor2(MouseEventArgs e)
        {
            //this.pictureBox1.Refresh();

            int procX1 = ((e.X) * this.BMP.Width / this.pictureBox1.Width) - (300 * this.BMP.Width / this.pictureBox1.Width);
            int procX2 = ((e.X) * this.BMP.Width / this.pictureBox1.Width) + (300 * this.BMP.Width / this.pictureBox1.Width);

            int procY1 = ((e.Y) * this.BMP.Height / this.pictureBox1.Height) - (300 * this.BMP.Height / this.pictureBox1.Height); ;
            int procY2 = ((e.Y) * this.BMP.Height / this.pictureBox1.Height) + (300 * this.BMP.Height / this.pictureBox1.Height); ;

            this.SetNormal(ref procX1, ref procX2, this.BMP.Width);
            this.SetNormal(ref procY1, ref procY2, this.BMP.Height);

            int NewX1 = e.X - 300;
            int NewX2 = e.X + 300;
            int NewY1 = e.Y - 300;
            int NewY2 = e.Y + 300;

            this.SetNormal(ref NewX1, ref NewX2, this.pictureBox1.Width);
            this.SetNormal(ref NewY1, ref NewY2, this.pictureBox1.Height);
            
            g.DrawImage(this.CursorPreview.Image,
                new Rectangle(NewX1, NewY1, NewX2, NewY2),
                new Rectangle(procX1, procY1, procX2, procY2),
                GraphicsUnit.Pixel);

            g.DrawEllipse(new Pen(Color.Black, 3), e.X - 50, e.Y - 50, 100, 100);
        }

Mediana wynosi 62 milisekundy.

Ktoś ma może jakieś jeszcze rady?

0

Na zasadzie ruszam myszką, a "kursor", leci zaraz za myszką.
jaki to jest kursor? może by podmienić kursor myszy na inny, wtedy jego rysowanie miałbyś sprzętowo załatwione.

0

Kursor to zwykłe koło.
Jeśli miałby być to kursor sam w sobie to byłoby super, ale on ma zmieniać swój rozmiar, a chyba "prawdziwy" kursor tego nie potrafi.

0

ale możęsz zmieniać kursor w zaleznosci od potrzeby. stwórz pliki .cur dla różnej wielkości kółek.
Nie wiem, czy da sie to latwo zrobic programowoo, bo nigdy tego nie robilem, ale zawsze mozesz po prostu miec kilka plikow

0

Nie uznajcie mnie za marudę, ale takie rozwiązanie mnie nie ciekawi :)
Po prostu nie estetyczne, a poza tym w większości programów kursory działają "normalnie". Więc i ja mogę taki zrobić, tylko ciekawi mnie właśnie w jaki sposób.

0

a poza tym w większości programów kursory działają "normalnie".
co to znaczy „normalnie”? widziałeś ich kod źródłowy, i wiesz że rysują ręcznie a nie podmieniają kursora myszy?
chyba nie, bo wtedy byś nie musiał pytać...

0

Tak, już wyjaśniam.
Sądzę, że np Photoshop korzysta z normalnego kursora lub maluje go, ale z pewnością nie na obrazku.

Popatrz:
http://www20.speedy.sh/NPeG3/download/Bez-nazwy-1.jpg
user image

Jak widać kursor wychodzi poza obszar obrazka. wniosek? Kursor z pewnością nie działa na obrazku.
W takim razie gdzie?
Na formie? Raczej nie, bo obraz przykrywałby kursor.
"Normalny" kursor? Ale jak w takim razie poradzili sobie z rozmiarem kursora?

Może jakiś komponent to jest? Myślałem też o tym, ale w C# każdy komponent używa swojego transparentu do co najwyżej nie przykrywania koloru.
Czyli - jeśli mamy panel1, a pod nim panel2, to panel1 mimo, że ma ustawiony BackColor na transparent to i tak wyświetlony będzie miał na sobie kolor swojego rodzica.
Przykład:
http://www18.speedy.sh/y3Cjx/download/Bez-nazwy-3.jpg
user image
Jak widać panel przybrał kolor formatki (swojego rodzica).

Można zrobić tak by komponent był dzieckiem PictureBox-a. I tak też zrobiłem ,ale wydajność jest dużo gorsza, na dodatek występuje zjawisko bufforowania.

0

photoshop zapewne wykorzystuje directx lub opengl
na GDI zazwyczaj wszystko miga, jest powolne, brzydkie i kłopotliwe

żeby panel był przeźroczysty z tego co pamiętam trzeba zrobić swoją kontrolkę dziedziczącą po nim i w metodzie odrysowania zaniechać rysowania tła
albo wpisać "transparent panel c#" w google i skorzystać z gotowego rozwiązania

0

Można takie coś wykonać. Różnica polega na tym, że malujemy nie na kontrolce, lecz na bitmapie. Najpierw tworzymy obrazek - bitmapę, z niej wyciągamy obiekt Graphics, a następnie ustawiamy tą bitmapę jako tło, lub obrazek pictureBoxa. Wszystko wykonujemy w pętli. Widocznej różnicy nie będzie żadnej - oprócz właśnie - wydajności.

Przykładowo można to zrobić tak:

Zaczynamy wszystko robić dopiero po pokazaniu się formatki

private bool operating;
        private Bitmap surface;
        private Graphics graph;

        ...

        protected override void OnShown(EventArgs e)
        {
            base.OnShown(e);

            using (surface = new Bitmap(pictureBox1.Width, pictureBox1.Height))
            {
                graph = Graphics.FromImage(surface);

                while (operating)
                {
                    Application.DoEvents();

                    graph.Clear(Color.White);
                    pictureBox1.Image = surface;

                    //TUTAJ WYKONUJEMY WSZELKIE OPERACJE GRAFICZNE
                }
            }
        }

W zaznaczonym miejscu w tym kodzie możemy wykonywać przeróżne operacje graficzne, na zmiennej graph oczywiście. Żadne ścinanie nie będzie widoczne. Mam nadzieję, że to o coś podobnego chodzi :)

0

Najmocniej wszystkim dziękuję za pomoc. Jest jeszcze jeden mały problem, ale o nim za chwilę.
Chciałbym jednak podziękować, że mieliście tyle cierpliwości nad - bądź co bądź - człowiekiem co kręcił nosem, by wszystko było ok :)

Problem rozwiązałem jednak w nieco inny sposób niż proponowaliście. Stworzyłem przezroczysty PictureBox.
Robię to tak:

Class TransPictureBox : Control
        {
            private Image _image = null;
            public Image Image
            {
                get
                {
                    return _image;
                }
                set
                {
                    _image = value;
                }
            }
            public TransPictureBox()
            {
            }

            protected override void OnPaintBackground(PaintEventArgs pevent)
            {
            }

            protected override void OnPaint(PaintEventArgs pe)
            {
                if (Image != null)
                    pe.Graphics.DrawImage(Image, 0, 0);
            }
            protected override CreateParams CreateParams
            {
                get
                {
                    CreateParams cp = base.CreateParams;
                    cp.ExStyle |= 0x20;
                    return cp;
                }
            }
        }

I wydaje się, że działa to doskonale. Spójrzcie na obrazek:
http://www19.speedy.sh/vkq7B/download/Bez-nazwy-2.jpg
user image

Niestety, po przesunięciu obrazka (w tym przypadku takiego kalendarzyka) okazuje się, że PictureBox, który stworzyłem działa na zasadzie "kameleona". Dopasowuje się do otoczenia podczas tworzenia, a potem już nie.
I efekt po przesunięciu:
http://www19.speedy.sh/Ubfad/download/Bez-nazwy-1.jpg
user image

Przezroczysty PictureBox tworze w taki sposób:

private TransPictureBox pan;
        public Form1()
        {
            InitializeComponent();
            pan = new TransPictureBox();
            
            pan.Parent = this;
            pan.Width = 100;
            pan.Height = 100;
            pan.Left = 220;
            pan.Image = Image.FromFile(@"G:\ikonki\calendar_month.png");
            pan.Top = 220;
            pan.BringToFront();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            Point mousePt = this.PointToClient(Cursor.Position);
            this.pan.Left = mousePt.X - (this.pan.Width / 2);
            this.pan.Top = mousePt.Y - (this.pan.Height / 2);
        }

Próbowałem również refreshować. Nic to nie dało.
Sądzę, że trzeba jakoś poprawić funkcję odrysowania. Ma ktoś może jakiś pomysł?

Interesuje mnie ten sposób bo kody podane tutaj osiągają wydajność niezłą, ale przy tym moim sposobie byłoby idealnie.

0

Ok działa :)
Wystarczyło jeszcze przykryć takim czymś :

protected override void OnMove(EventArgs e)
            {
                RecreateHandle();
            }

I wszystko działa :)

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