Laden...

Bilder ohne Flackern anzeigen/scrollen

Erstellt von Animal21 vor 11 Jahren Letzter Beitrag vor 11 Jahren 3.404 Views
A
Animal21 Themenstarter:in
144 Beiträge seit 2008
vor 11 Jahren
Bilder ohne Flackern anzeigen/scrollen

Hallo Leute,

ich habe ein Control "alGallery", welches, wie der Name sagt, eine Gallerie ist.
Diese greift auf eine Liste von Bilder zu, welche zuvor geladen, auf die richtige Größe angepasst und mittels JPEG komprimiert wurden, so dass die Bilder eine Breite von 1105px haben eine größe von 100 bis 500 KB.
Einige der Bilder waren vorher kleiner (Auflösung) und andere hatte eine Auflösung von so 3000x oder 4000x.

Die Gallerie soll eine SlideShow sein, also es werden immer 3 Bilder angezeigt (eins voll, die anderen halb) und das ganze läuft dann von links nach rechts durch. Ist ein Bild rechts "aus dem Bildschirm gelaufen", wird das nächste geladen und es landet wieder links.

Die alGallery ist erbt von Control und die Bilder sind in einer PictureBox (gesamt 3 auf der alGallery).
Der Slide funktionert gut und bei den Bildern mit geringer Auflösung auch flüssig, nur die Bilder, welche vorher eine sehr hohe Auflösung hatten, bei denen wird der Slide langsamer und es flackert leicht.

Auf dem alGallery Control ist DoubleBuffer an, sowie AllPaintingInWmPaint und OptimizedDoubleBuffer.

Hättet ihr evtl. noch ein paar Tips zum optimieren?

PS: Auf die Bilder habe ich keinen Einfluss, da diese von einem Netzlaufwerk kommen und die werden immer mal geändert, neue hochgeladen, gelöscht, ...
Also kann ich sie nicht vorher schon bearbeiten.

Zum Schluss noch etwas Code:

Resize:


    public static Image ResizePicByWidth(Image sourceImage, double newWidth) {
        double sizeFactor = newWidth / sourceImage.Width;
        double newHeigth = sizeFactor * sourceImage.Height;

        Bitmap newImage = new Bitmap((int)newWidth, (int)newHeigth, PixelFormat.Format24bppRgb);
        newImage.SetResolution(sourceImage.HorizontalResolution,
                                sourceImage.VerticalResolution);

        using (Graphics g = Graphics.FromImage(newImage)) {
            g.InterpolationMode = InterpolationMode.HighQualityBicubic;
            g.DrawImage(sourceImage, new Rectangle(0, 0, (int)newWidth, (int)newHeigth));
        }
        return newImage;
    }

Komprimieren:


    public static void JpegBildKomprimieren(Image quellBild, int qualitaet, string speicherPfad) {
        try {
            //Ein ImageCodecInfo-Objekt für den JPEG-Codec anlegen
            ImageCodecInfo jpegCodec = null;

            //Den Qualitätsarameter konfigurieren (Qualitätsfaktor in
            //Prozent angeben)
            EncoderParameter qualitaetsParameter = new EncoderParameter(
                        System.Drawing.Imaging.Encoder.Quality, qualitaet);

            //Alle im System verfügbaren Codecs auflisten
            ImageCodecInfo[] alleCodecs = ImageCodecInfo.GetImageEncoders();

            EncoderParameters codecParameter = new EncoderParameters(1);
            codecParameter.Param[0] = qualitaetsParameter;

            //Den JPEG-Codec unter allen Codecs finden und dem
            //Codec-Info-Objekt zuweisen
            for (int i = 0; i < alleCodecs.Length; i++) {
                if (alleCodecs[i].MimeType == "image/jpeg") {
                    jpegCodec = alleCodecs[i];
                    break;
                }
            }

            //Das Bild abspeichern
            quellBild.Save(speicherPfad, jpegCodec, codecParameter);
        } catch (Exception e) {
            throw e;
        }
    }

alGallery (ausschnitte)


public class alGallery : Control {
    private Queue<Image> imgList;
    private bool bInitImages;
    private Timer timerSliding;
    private PictureBox imgBox1;
    private PictureBox imgBox2;
    private PictureBox imgBox3;
    StringFormat sf = new StringFormat();
    int links;
    int mitte;
    int rechts;

    public Queue<Image> ImageList { get { return imgList; } }

    public alGallery() {
        init();
    }
    public alGallery(Queue<Image> imgList) {
        this.imgList = imgList;
    }
    private void init() {
        this.DoubleBuffered = true;
        this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);

        imgList = new Queue<Image>();

        timerSliding = new Timer();
        timerSliding.Interval = 10;
        timerSliding.Tick += new EventHandler(timerSliding_Tick);
        timerSliding.Start();
        sf = new StringFormat();

        sf.Trimming = StringTrimming.EllipsisCharacter;
        sf.Alignment = StringAlignment.Center;
        sf.LineAlignment = StringAlignment.Far;
    }

    private void timerSliding_Tick(object sender, EventArgs e) {
        if (imgList.Count > 2) {
            imgBox1.Left+=2;
            imgBox2.Left+=2;
            imgBox3.Left+=2;

            if (imgBox1.Left > this.Width) resfreshPictureBox(imgBox1, imgBox2);
            if (imgBox2.Left > this.Width) resfreshPictureBox(imgBox2, imgBox3);
            if (imgBox3.Left > this.Width) resfreshPictureBox(imgBox3, imgBox1);
        }
    }
    public void initImages(bool force) {
        if (!bInitImages || (bInitImages && force)) {

            links = (int)(this.Width * 0.2);
            mitte = (int)(this.Width * 0.6);
            rechts = (int)(this.Width * 0.2);

            if (imgList.Count > 2) {

                imgBox1 = new PictureBox();
                imgBox1.SizeMode = PictureBoxSizeMode.StretchImage;
                imgBox1.Image = imgList.Dequeue();
                imgBox1.Parent = this;

                imgBox2 = new PictureBox();
                imgBox2.SizeMode = PictureBoxSizeMode.StretchImage;
                imgBox2.Image = imgList.Dequeue();
                imgBox2.Parent = this;

                imgBox3 = new PictureBox();
                imgBox3.SizeMode = PictureBoxSizeMode.StretchImage;
                imgBox3.Image = imgList.Dequeue();
                imgBox3.Parent = this;

                imgBox1.Width = imgBox2.Width = imgBox3.Width = mitte;
                imgBox1.Height = imgBox2.Height = imgBox3.Height = this.Height;

                imgBox1.Left = -imgBox1.Width + links - 10;
                imgBox2.Left = links;
                imgBox3.Left = links + mitte + 10;

            } else if (imgList.Count > 0) {
                ...
            }
            bInitImages = true;
        }
    }
    private void resfreshPictureBox(PictureBox imgBox, PictureBox to) {
        imgList.Enqueue(imgBox.Image);
        imgBox.Image = imgList.Dequeue();
        imgBox.Left = to.Left - imgBox.Width - 10;
    }
}

2.891 Beiträge seit 2004
vor 11 Jahren

Hat das einen Grund, warum du die Thumbnails auf der Platte speicherst? Laut deiner Beschreibung kannst du ja nicht cachen...

Wenn du Tipps zum Optimieren haben möchtest, solltest du uns erst einmal sagen, was du optimieren möchtest. 😃 Speicherverbrauch, Rechenlast, IO, ...?

5.658 Beiträge seit 2006
vor 11 Jahren

Hi Animal21,

wenn es bei der Animation von großen Bildern flackert, dann reduziere doch die Größe der Bilder vorher so, daß sie nicht bei jedem Zeichnen neu skaliert werden müssen. Also nicht auf eine bestimmte Auflösung ändern, wie du das momentan machst, sondern auf die Größe, die für die Anzeige gerade gebraucht wird.

Christian

Weeks of programming can save you hours of planning

A
Animal21 Themenstarter:in
144 Beiträge seit 2008
vor 11 Jahren

Hallo,
danke für eure Antworten...

OK etwas Hintergrund zu dem Programm.

Es läuft auf einem Rechner der im Essbereich steht und mit einem großen Monitor verbunden ist.
Unsere Sekretärin soll die Möglichkeit haben auf ein Vordefiniertes Netzlaufwerk Bilder hochzuladen und dann eine eMail an den Benutzer (für das Programm) schicken, dass eine neue Gallerie mit den und den Parametern zur Verfügung steht.
Das Programm liest die Parameter der eMail, zieht sich die Bilder auf die Platte (damit die Gallerie erhalten bleibt, auch wenn eine neue erstellt wird), skaliert die Bilder und zeigt die aktuelle Gallerie an.

@dN!3L, Optimierung ist vll das falsche Wort, ich möchte mein Problem aus der Welt schaffen, dass bei ehemals großen Bildern, der Slide trotzdem langsamer wird und leicht (an den Rändern) flackert.

@MrSparkle, ich skaliere die Bilder nur einmal:
Das Programm liest die Bilder ein, skaliert diese auf eine feste Breite und speichert sie dann komprimiert auf der lokalen Platte ab. Danach werden die Bilder nicht mehr skaliert. Oder verstehe ich dich falsch?

grüße
ani

B
357 Beiträge seit 2010
vor 11 Jahren

Hat deine Anzeige so eine Auflösung, dass die Bilder in ihrer vollen Breite dargestellt werden (1105px)? Wenn nein, dann werden sie für die Anzeige im Control nochmal skaliert (vom Framework dann). Und genau diese Arbeit sollte dann vermieden werden, wenn man noch Ruckeln drin hat.

6.911 Beiträge seit 2009
vor 11 Jahren

Hallo Animal21,

wie schon erwähnt, solltest du die Bilder so skalieren, dass die PictureBox die Bilder nur mehr anzeigt und nicht nochmal skalieren muss.

Schau dir auch das zweite Beispiel in Pipelines an, das ist ziemlich ähnlich deiner Anforderung.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"

A
Animal21 Themenstarter:in
144 Beiträge seit 2008
vor 11 Jahren

Danke für eure Antworten.
Die pictureBoxen haben eine Breite von 1105x aber ich hatte den Mode auf ScaleImage gesetzt, damit auch höhe gleich ist.

Hab es jetzt geändert, mit einem Scaling VOR dem aufbau jedweder Form/PictureBoxen... auf eine feste größe und den Mode auf CenterImage.

Das verlangsamen ist nun komplett weg, alle Bilder bewegen sich gleich schnell, nur flackern die Ränder der PictureBoxen ein ganz klein wenig (also echt nur 2 pixel oder so am rand). Es ist wenig, aber man sieht es. ich befürchte dass ich dies nicht wegbekomme ohne DirectX oder XNA oder so was, oder?

Das Konzept mit den Pipline ist sehr interessant und aber ich ganz treffend für mich (dnek ich), da ich die Bilder ja schon or dem Anzeigen lange bearbeitet habe.

grüße
Ani

5.658 Beiträge seit 2006
vor 11 Jahren

Hab es jetzt geändert, mit einem Scaling VOR dem aufbau jedweder Form/PictureBoxen... auf eine feste größe und den Mode auf CenterImage.

Warum eigentlich immer ein fester Wert? Wäre es nicht besser, auf _genau _die Größe zu skalieren, mit der das Bild nachher angezeigt wird?

Wieso verwendest du PictureBoxen, dafür gibt es eigentlich keinen Grund? Siehe [Tutorial] Zeichnen in Windows-Forms-Programmen (Paint/OnPaint, PictureBox)

ich befürchte dass ich dies nicht wegbekomme ohne DirectX oder XNA oder so was, oder?

Ich sehe ehrlich gesagt keinen Grund, die Technologie zu wechseln, solange man noch nicht alles ausprobiert hat, was zu einer Performancesteigerung führt.

Christian

Weeks of programming can save you hours of planning

A
Animal21 Themenstarter:in
144 Beiträge seit 2008
vor 11 Jahren

Fester Wert, da ich die Größe des Ferhnsehers weis und somit auch die Abmaße für die SlideShow festlegen kann ohne eine dynamische größe berechnen zu müssen.

OK, die PictureBox hatte ich genommen, um das Bild automatrisch nach-Scalieren zu lassen, aber das hat sich ja nun erübrigt. Ich werde die Bilder mal selbst zeichnen, mal sehn ob es dann besser ist 😃

Danke.

49.485 Beiträge seit 2005
vor 11 Jahren

Hallo Animal21,

was weitere Optimierungstipps angeht, siehe [FAQ] Flackernde Controls vermeiden / Schnelles, flackerfreies Zeichnen.

herbivore

A
Animal21 Themenstarter:in
144 Beiträge seit 2008
vor 11 Jahren

Also anstatt der 3 PictureBoxen habe ich eine kleine strukture genommen, welche nur das bild, sowie left und width.

Es flackert nicht mehr an den Rändern und ist auch nicht langsam oder sonst irgendwas, das einzige ist, dass es leiche ruckler zwischendurch gibt. Es ist ein bischen schwer zu beschreiben, deswegen bin ich mal so frei und poste nochmal den gesamten Code für die Klasse.


public class alGallery : Control {
    private struct imgBox {
        public int left;
        public int width;
        public Image img;

        public void draw(Graphics g) {
            g.DrawImage(img, new Rectangle(new Point(left, 0), new Size(width, (int)g.ClipBounds.Height)));
        }
    }
    private Queue<Image> imgList;
    private int imgCount;
    private bool bInitImages;
    private Timer timerSliding;
    private imgBox imgBox1;
    private imgBox imgBox2;
    private imgBox imgBox3;
    int mitte;

    public Queue<Image> ImageList { get { return imgList; } }

    public alGallery() {
        init();
    }
    public alGallery(Queue<Image> imgList) {
        this.imgList = imgList;
    }
    private void init() {
        this.DoubleBuffered = true;
        this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);

        imgList = new Queue<Image>();

        timerSliding = new Timer();
        timerSliding.Interval = 10;
        timerSliding.Tick += new EventHandler(timerSliding_Tick);

        sf = new StringFormat();
        sf.Trimming = StringTrimming.EllipsisCharacter;
        sf.Alignment = StringAlignment.Center;
        sf.LineAlignment = StringAlignment.Far;
    }

    private void timerSliding_Tick(object sender, EventArgs e) {
        if (imgCount > 2) {
            imgBox1.left++;
            imgBox2.left++;
            imgBox3.left++;

            if (imgBox1.left > this.Width) resfreshPictureBox(ref imgBox1, imgBox2);
            if (imgBox2.left > this.Width) resfreshPictureBox(ref imgBox2, imgBox3);
            if (imgBox3.left > this.Width) resfreshPictureBox(ref imgBox3, imgBox1);

            Invalidate();
        }
    }
    public void initImages(bool force) {
        if (!bInitImages || (bInitImages && force)) {
            mitte = (int)(this.Width * 0.6);

            imgCount = imgList.Count;

            if (imgCount > 2) {

                imgBox1 = new imgBox();
                imgBox1.img = imgList.Dequeue();

                imgBox2 = new imgBox();
                imgBox2.img = imgList.Dequeue();

                imgBox3 = new imgBox();
                imgBox3.img = imgList.Dequeue();

                imgBox1.width = imgBox1.img.Width;
                imgBox2.width = imgBox2.img.Width;
                imgBox3.width = imgBox3.img.Width;

                imgBox1.left = -imgBox1.width + links - 10;
                imgBox2.left = links;
                imgBox3.left = links + mitte + 10;
            } else if (imgCount > 0) {
                imgBox1 = new imgBox();
                imgBox1.img = imgList.Dequeue();
                imgList.Enqueue(imgBox1.img);
                imgBox1.width = this.Size.Width;
            }
            bInitImages = true;
        }
    }
    public void stopPerformance() {
        timerSliding.Stop();
    }
    public void startPerformance() {
        timerSliding.Start();
    }

    private void resfreshPictureBox(ref imgBox imgBox, imgBox to) {
        imgList.Enqueue(imgBox.img);
        imgBox.img = imgList.Dequeue();
        imgBox.width = imgBox.img.Width;
        imgBox.left = to.left - imgBox.width - 10;
    }
    protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) {
        imgBox1.draw(e.Graphics);
        imgBox2.draw(e.Graphics);
        imgBox3.draw(e.Graphics);
    }
}

Meine Vermutung ist, dass die aktualisierungsrate (aller 10 Millisekunden) zu viel für die CPU ist?! Aber bei Höheren oder auch geringeren Raten kommt es zu diesen "rucklern" (die echt minimal sind, aber dem Auge doch auffallen)

grüße
Ani

@Herbivore, ich hab mit alle Links zu dem Thema durchgelesen und soweit ich konnte, bzw es sinn machte auch umgesetzt. Es ist auch weniger ein echtes Flackern.