Laden...

[gelöst] Rahmen ins BitmapImage setzen

Erstellt von dennisspohr vor 13 Jahren Letzter Beitrag vor 13 Jahren 2.432 Views
dennisspohr Themenstarter:in
420 Beiträge seit 2007
vor 13 Jahren
[gelöst] Rahmen ins BitmapImage setzen

Hallo zusammen,

ich hatte vor einer Weile mal ein kleines Bildbearbeitungsprogramm in .NET 2.0 geschrieben. Nun bin ich gerade dran, dieses in .NET 4.0 - in einer WPF-Anwendung - zu entwickeln.

Es gibt eine Methode, die ein BitmapImage übergeben bekommt und auch ein solches zurückliefert. In der Methode soll in (!) dem BitmapImage ein Rahmen mit bestimmter Farbe/Größe gesetzt werden.

Weiß jemand, wie ich das am besten lösen könnte? Im Internet finde ich leider nichts dazu.
Vielen Dank für eure Hilfe.

Grüße
Dennis

643 Beiträge seit 2006
vor 13 Jahren

Zeichne eine Rechteck um dein Bild.

Graphics.DrawRectangle...

Achte darauf das der Startpunkt zum zeichenen mit +(Lienenbreite / 2) beginnt und -(Lienenbreite / 2) am Endpunkt.

dennisspohr Themenstarter:in
420 Beiträge seit 2007
vor 13 Jahren

Graphics ist im Namespace System.Drawing drin. Ist dieses überhaupt zu einer BitmapImage (!= Bitmap) kompatibel? Bzw. wäre das denn sauber?

643 Beiträge seit 2006
vor 13 Jahren

Die Image Klasse hat die Eigenschaft Source dort kanst du deine BitmapImage zuweisen.

Image image = new Image();    
image.Width = 200;

BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri(@"/sample/myimage.jpg",UriKind.RelativeOrAbsolute);
bi.EndInit();

image.Source = bi;
dennisspohr Themenstarter:in
420 Beiträge seit 2007
vor 13 Jahren

Ich weiß, wie ich ein BitmapImage einem Image zuweise 😃

Ein Image ist doch ein Control, was eigentlich nur zum Anzeigen auf einer Oberfläche gedacht ist, oder?

Und damit hätte ich ja immernoch das Problem, einen Rahmen ums Bild zu setzen. Am liebsten würde ich das ohne die Verwendung von System.Drawing machen, was glaube ich sinnvoller ist.

L
862 Beiträge seit 2006
vor 13 Jahren

Möglichkeit 1:
Ein Border-Control um das Image-Control herumlegen. Das verändert natürlich nicht das Bild sondern nur die Ansicht.
Möglichkeit 2:
Einen DrawingContext verwenden um ein Rechteck darüberzumalen so wie Ayke das bereits genannt hat.
Möglichkeit 3:
Du verwendest eine WritableBitmap und schreibst auf Low-Level-Ebene die Pixel am Rand so um dass sie einen Rahmen ergeben.

dennisspohr Themenstarter:in
420 Beiträge seit 2007
vor 13 Jahren

zu Möglichkeit 1:
Nützt mir nichts, weil ich eben nicht anzeigen möchte, sondern als neue Datei speichern möchte.

zu Möglichkeit 2:
Nochmal meine Frage: Ist das ganze mit dem BitmapImage kompatibel?

zu Möglichkeit 3:
Wäre eine Möglichkeit, hört sich aber langsam an.

L
862 Beiträge seit 2006
vor 13 Jahren

Mit Möglichkeit 2 habe ich mich selbst noch nicht wirklich beschäftigt. Das war eher eine Idee. Darum kann ich leider nicht mehr dazu sagen.

Möglichkeit 3:
Wenn das was du machst ein 'Bildbearbeitungsprogramm' sein soll wird dir eh nichts anderes übrig bleiben. Das schreiben auf die Bitmap selbst kann man durchaus performant umsetzen. Was hier ausbremst ist eher der Bildschirm-Aktualisierungs-Mechanismus von WPF und die Tatsache dass managed C#-Code eben doch langsamer ist als nativer Code.
Ich glaube jedoch nicht dass du das bei der Erzeugung eines Bildrahmens merken wirst (Ich weis ja nicht was dein Bildbearbeitungsprogramm sonst so macht). Es ist IMHO die performanteste Variante unter WPF ein Bild zu manipulieren. Abspeichern ist damit auch überhaupt kein Problem da WritableBitmap von ImageSource ableitet.

dennisspohr Themenstarter:in
420 Beiträge seit 2007
vor 13 Jahren

Das Programm soll viele einzelne Bildbearbeitungsschritte auf einen Bilderstapel machen. Das heißt, es geht in einer Schleife alle Bilder durch und wendet die einzelnen Bearbeitungsschritte (z.B. Rahmen setzen, Größe ändern, S/W, ...) an. Deshalb versuche ich sehr auf Geschwindigkeit zu achten, auch wegen der Erstellung eines Vorschaubildes.

Schade ist, dass es im Internet kaum Beispiele gibt, wie man BitmapImages bearbeiten kann.

L
862 Beiträge seit 2006
vor 13 Jahren

Vermutlich weil niemand soetwas mit WPF macht...

Ich rate dir zu einer WritableBitmap. Der Performance-Flaschenhals in WPF ist hier lediglich die Bildschirmaktualisierung. Wenn du während des Bearbeitens der Bitmap, diese nicht am Bildschirm aktualisierst wirst du es relativ performant hinbekommen. Nachteil (ode Vorteil?) der WritableBitmap ist natürlich dass du mit absoluten Low-Level-Operationen arbeitest. Aber wenn ich mir deine Anwendungsgebiete (S/W=Schwarz-Weis-Filter?) ansehe wirst du eh alle Pixel als Byte-Array durchschleifen müssen. Zum anzeigen der Bilder kannst du einfach die Writable-Bitmap in ein Image-Control stecken. Für Thumbnails gibt es auch eine Klasse Transformed-Bitmap. Hier muss ich dich allerdings warnen. Unter .NET 3.5 kannst du leicht ein Speicherleck erzeugen wenn du eine TransformedBitmap in ein Image-Control packst. Wie es unter .NET 4.0 ist kann ich dir leider nicht sagen aber teste das am besten mal selbst. Wenn du die Writable-Bitmap direkt anzeigen willst kannst du noch einen Aktualisierungsmechanismus steuern mit dem du angibst wann die GUI aktualisiert werden soll (wenn du während der Anzeige das Bild verändern willst).

dennisspohr Themenstarter:in
420 Beiträge seit 2007
vor 13 Jahren

Warum macht das niemand mit WPF? Wenn ich mit System.Drawing mehr Vorteile habe, kann ich das auch damit machen. Ich dachte nur, dass mir WPF (bzw. .NET 4.0) mehr Funktionalität und Performance bietet.

Das Vorschaubild soll erst am Ende angezeigt werden. Es können sich beliebig viele PlugIns _einschalten _und einen Bearbeitungsschritt zur Verfügung stellen. Ist die Frage, ob ich den PlugIns eine System.Drawing.Bitmap oder ein System.Windows.Media.Imaging.BitmapImage übergebe.

Eventuell könnten die einzelnen Bearbeitungsschritte mit einer Bitmap arbeiten und ganz am Ende wird diese zur Anzeige in ein BitmapImage umwandelt.

L
862 Beiträge seit 2006
vor 13 Jahren

Nunja... WPF bietet dir einfach verwendbare Hardwareunterstützung. Damit kannst du einfach eine GUI designen ohne dass die Controls im Winows-Style sind.
Du kannst einfach Animationen integrieren. Die GUI ist schnell da hardware gestützt.
Du hast eine nette Default-Architektur die du leicht erweitern kannst. (Behaviors, Panels...)
Du hast eine tolle Command- und Binding-Unterstützung mit denen du super schnell irgendwelche Business-Anwendungen mit MVVM implementieren kannst.
Allerdings ist WPF nicht wirklich als Grafik-Library zu gebrauchen. Du kannst zwar mithilfe von PixelShadern tolle Bild-Filter implementieren die dann auch auf der Grafikkarte laufen und verdammt schnell sind allerdings ist das eher für Effekte vorgesehen. Wenn du damit eine Bitmap bearbeiten willst müsstest du einen solchen Shader auf ein ImageControl anwenden und dieses dann mit einer RenderTargetBitmap 'fotografieren'.

Soviel zur WPF-Unterstützung.

Ich weis nicht wie viel Unterstützung du von WinForms bekommst. War schon lange her als ich das letzte mal damit Programmiert habe. Ich kann mich allerdings noch grob daran erinnern dass ich damit Bilder mit DrawLine(), FillRect()... bearbeitet werden können. Die gleichen Methoden gibt es mit WPF im DrawingContext. Ob man damit Bitmaps bearbeiten kann kann ich dir leider nicht beantworten.

Fakt ist jedoch dass dir für einen S/W-Filter WinForms wohl genausowenig Unterstützung wie WPF bietet. Von daher ist es, unter diesen Gesichtspunkt betrachtet, egal auf welche Technologie du setzt.

dennisspohr Themenstarter:in
420 Beiträge seit 2007
vor 13 Jahren

WPF kann ich ja trotzdem weiter benutzen, eben nur nicht für die eigentliche Bearbeitung. Diese könnte ich dann mit System.Drawing machen.

Auf die Pixelshader bin ich auch gestoßen, allerdings habe ich dort sehr wenige Tutorials gefunden, wie man diese genau anwendet. Für Filter wären diese aufgrund ihrer hohen Geschwindigkeit eigentlich sehr interessant.

Ich weiß, dass das mit System.Drawing alles relativ einfach funktioniert. Ich habe die einzelnen Bearbeitungsschritte damit ja alle schon mal so ähnlich gemacht.

L
862 Beiträge seit 2006
vor 13 Jahren

Wenn du dich für die Shader interressierst kannst du dir mal das Tool Shazzam ansehen.

Damit solltest du einen groben Überblick bekommen was man damit alles machen kann. Bei solchen Effekten handelt es sich allerdings NICHT um Bitmap-Operationen auch wenn man diese per RenderTargetBitmap theoretisch dazu zwingen könnte eine Bitmap zu bearbeiten.

Ich habe selbst auch schon einige Shader implementiert um div. GUI-Effekte zu erzielen. Evtl. lässt sich dass ja auf Filter umbiegen. Vorteil ist wie gesagt dass die Grafikkarte Unterstützung anbietet (sofern diese das unterstützt). Wenn dieser Ansatz klappt hättest du sicher einen mörderschnellen Filter-Mechanismus 😉.

dennisspohr Themenstarter:in
420 Beiträge seit 2007
vor 13 Jahren

Vielen Dank für den Link. Ich werde mir das Tool heute Abend zu Hause mal anschauen.

Das mit man mit den Shaders sehr viel machen kann, habe ich schon bemerkt 😃 Ich habe nur nichts gefunden, wie ich diese auf ein BitmapImage anwenden kann. Bei den Beispielen im Internet sind diese immer direkt an ein Image-Control gebunden, was mir an dieser Stelle ja nichts bringt.

Ich könnte ein PlugIn für Filter und ein PlugIn für Sonstiges (also bsp. Rahmen) anbieten. Das klingt gut 😃

dennisspohr Themenstarter:in
420 Beiträge seit 2007
vor 13 Jahren

So, hier kommt die Lösung 😃 Nachdem ich tagelang gesucht und gesucht habe.. die Lösung ist so verdammt einfach. Folgender Quellcode-Schnispel sollte Erklärung genug sein:


        public BitmapSource Execute(BitmapSource bitmap)
        {
            double newWidth = bitmap.PixelWidth + config.Thickness * 2;
            double newHeight = bitmap.PixelHeight + config.Thickness * 2;

            RenderTargetBitmap renderTarget = new RenderTargetBitmap(
                (int)newWidth,
                (int)newHeight,
                bitmap.DpiX,
                bitmap.DpiY,
                PixelFormats.Default);

            DrawingVisual drawingVisual = new DrawingVisual();
            DrawingContext drawingContext = drawingVisual.RenderOpen();

            Rect imageRect = new Rect(config.Thickness, config.Thickness, bitmap.PixelWidth, bitmap.PixelHeight);
            Pen pen = new Pen(Brushes.Black, config.Thickness);

            using (drawingContext)
            {
                drawingContext.DrawRectangle(Brushes.Black, pen, new Rect(0, 0, newWidth, newHeight));
                drawingContext.DrawImage(bitmap, imageRect);
            }

            renderTarget.Render(drawingVisual);
            return renderTarget as BitmapSource;
        }