Erzeugung einer Graphikdatei mit C++


  • Administrator

    DocShoe schrieb:

    Dafür brauchst du nicht einmal externe Bibliotheken, das kannst du über die Klasse TBitmap erledigen:

    Witzbold! TBitmap ist kein Standard C++. TBitmap kommt von einer externen Bibliothek nämlich VCL.

    Grüssli



  • Dravere schrieb:

    DocShoe schrieb:

    Dafür brauchst du nicht einmal externe Bibliotheken, das kannst du über die Klasse TBitmap erledigen:

    Witzbold! TBitmap ist kein Standard C++. TBitmap kommt von einer externen Bibliothek nämlich VCL.

    Grüssli

    Daramos schrieb:

    Danke erstmal euch beiden fuer die Tipps. An eine Bibliothek hatte ich auch schon gedacht. Ich programmiere allerdings unter Windows mit dem Code Gear RAD Studio. Gibt es vielleicht zu der Linux-Bibliothek ein Windows-Aequivalent?

    Selber Witzbold! 😃


  • Administrator

    DocShoe schrieb:

    Daramos schrieb:

    Danke erstmal euch beiden fuer die Tipps. An eine Bibliothek hatte ich auch schon gedacht. Ich programmiere allerdings unter Windows mit dem Code Gear RAD Studio. Gibt es vielleicht zu der Linux-Bibliothek ein Windows-Aequivalent?

    Selber Witzbold! 😃

    Ehm, dir ist aber schon klar, dass die VCL trotzdem eine externe Bibliothek ist? Und zwar eine verdammt grosse, ein absolutes Schwergewicht! libpng ist da im Vergleich mikrig.

    Übrigens, wieso legst du TBitmap auf dem Heap an? 😕

    Grüssli



  • Wenn man das so pedantisch sieht hast du natürlich recht, aber bevor ich eine weitere Abhängigkeit in meine Projekte einführe benutze ich doch lieber das, was die Compilersuite von sich aus mitbringt.
    Leider kann man nichts, was TObject als Basisklasse hat, auf dem Stack erzeugen, und leider gehört TBitmap auch zu den TObject Derivaten. Ist ein "Feature" der VCL, das ich über alles schätze... naja, immerhin hat mich dieser Sachverhalt auf boost aufmerksam gemacht.



  • Hallo DocShoe,

    das sieht sehr nach etwas aus, das mir hilft. Dankeschoen!
    Leider hab ich bisher kaum mit Bibliotheken und Templates gearbeitet und bin mir deshalb nicht ganz sicher, was dein Code-Vorschlag tut.
    Ich hab mal versucht, in einzubauen, und ihn leicht veraendert.
    Matrix ist die Klasse, in der die Bitmap-Erzeugungsroutine liegen soll, cols und rows sind Spalten und Zeilen der Matrix.
    Die Pixel sollen dann entsprechend den Matrixeintraegen weiss oder schwarz gefaerbt werden.

    void Matrix::plot() const
    {
    boost::shared_ptr<Graphics::TBitmap> Bitmap(new Graphics::TBitmap);
    Bitmap->PixelFormat   = pf32bit;
    Bitmap->Width         = cols;
    Bitmap->Height        = rows;
    
    for (int i = 0; i < rows; i++) {
    	for (int j = 0; j < cols; j++) {
    	if (pv[i]->get(j)==0) {
    		Bitmap->Canvas->Pixels[i][j] = clWhite;
    	}
    else    Bitmap->Canvas->Pixels[i][j] = clBlack;
    	}
    }
    Bitmap->SaveToFile( "repgraph.bmp" );
    }
    

    Nun meckert der Compiler allerdings rum, er kenne den Qualifier 'Graphics' nicht. Wahrscheinlich muss ich noch irgendwas includen - aber was genau?


  • Administrator

    DocShoe schrieb:

    Wenn man das so pedantisch sieht hast du natürlich recht, aber bevor ich eine weitere Abhängigkeit in meine Projekte einführe benutze ich doch lieber das, was die Compilersuite von sich aus mitbringt.

    Wer sagt dir, dass er die VCL bereits verwendet? Nur weil man den Code Gear Kompiler verwendet, muss man doch nicht automatisch auch die VCL verwenden. Aber wenn er diese bereits verwendet, dann hast du natürlich schon recht, dass eine zusätzliche Abhängigkeit nicht unbedingt sinnvoll wäre. Kann TBitmap auch PNGs speichern oder sonst ein etwas besser komprimiertes Format als bmp?

    Grüssli



  • Stellt sich die Frage, ob die Komprimierung für ihn wichtig ist...

    Falls sie es nicht ist, hat er mit TBitmap eine sehr angenehme und leicht zu verwendende Klasse, die meines Wissens aber nur .bmp speichert.

    Falls seine Bilder sehr groß sind, ist er mit libpng wohl besser beraten.



  • Soweit ich weiss kann TBitmap nur das Bitmap Format schreiben, aber es gibt noch eine Klasse TJPEGImage, die (oh Wunder!) Grafiken im JPG Format speichert.

    @Daramos
    Du musst den Header <graphics.hpp> und <boost/smart_ptr.hpp> inkludieren.

    PS:
    Wenn du das ohne boost machen willst musst du dich selbst um das Zerstören der Bitmap kümmern:

    #include <vcl.h>
    #include <graphics.hpp>
    
    Graphics::Bitmap Bmp = new Graphics::TBitmap();
    
    // Code wie oben
    
    delete Bmp;
    


  • DocShoe schrieb:

    boost::shared_ptr<Graphics::TBitmap> Bitmap( new Graphics::TBitmap() );
    Bitmap->PixelFormat   = pf32bit;
    Bitmap->Width         = Width;
    Bitmap->Height        = Height;
    
    if( Bitmap->Canvas->Pixels[0][0] == clBlack )
    {
       Bitmap->Canvas->Pixels[0][0] = clWhite;
    }
    Bitmap->SaveToFile( "c:/bitmap.bmp" );
    

    Das ist wieder ein schönes Beispiel dafür, wie man shared_ptr nicht einsetzen sollte. Es gibt auch noch scoped_ptr , wenn kein geteilter Besitz nötig ist. Der ist hier viel sinnvoller. Selbst std::auto_ptr wäre noch geeigneter.



  • Nexus schrieb:

    Das ist wieder ein schönes Beispiel dafür, wie man shared_ptr nicht einsetzen sollte. Es gibt auch noch scoped_ptr , wenn kein geteilter Besitz nötig ist. Der ist hier viel sinnvoller. Selbst std::auto_ptr wäre noch geeigneter.

    Genau! Bis man sich irgendwann entschliesst (bzw. die Notwendigkeit eintritt), die Grafik doch irgendwie anzeigen zu wollen oder irgendwie anders weiter benutzt werden soll. Und baut dann von scoped_ptr/auto_ptr doch auf smart_ptr um. So ganz ohne Kontext kann man an fast jedem Code irgendwas aussetzen... weisst du, ob der vollständige Code nicht aus mehreren Funktionen besteht, von denen eine die Grafik erzeugt und eine andere die Grafik speichert? Und eine dritte vielleicht sogar die Grafik ausdruckt? Oder es mehrere Matrizen gibt, zu der jeweils eine Grafik in einer std::map abgelegt wird?
    Also manchmal glaube ich wirklich, dass hier nur gepostet wird, um irgendein Posting wegen einer Spitzfindigkeit anzuprangern...



  • Dieser Thread wurde von Moderator/in evilissimo aus dem Forum C++ in das Forum VCL (C++ Builder) verschoben.

    Im Zweifelsfall bitte auch folgende Hinweise beachten:
    C/C++ Forum :: FAQ - Sonstiges :: Wohin mit meiner Frage?

    Dieses Posting wurde automatisch erzeugt.



  • DocShoe schrieb:

    Also manchmal glaube ich wirklich, dass hier nur gepostet wird, um irgendein Posting wegen einer Spitzfindigkeit anzuprangern...

    Nur nicht gleich so empfindlich reagieren.

    Ich wollte dich nur darauf hinweisen, dass shared_ptr oftmals nicht angebracht ist, auch wenn er "praktisch" zu sein scheint (im Sinne von, man muss nichts überlegen und bei möglichen Änderungen nichts umstellen). Für die Übergabe an weitere Funktionen braucht es keinen shared_ptr , da reichen Referenzen. Im Übrigen kann es gut sein, dass man mit scoped_ptr Fehler vermeidet, weil man den Zeiger nicht aus Versehen kopiert. Als klassischer RAII-Smartpointer ist er sicher zu bevorzugen.

    Wenn du dir dieser Thematik bewusst bist, okay. Aber es gibt eben einige Programmierer, die grundsätzlich einfach mal shared_ptr einsetzen, ohne dass sie ihn überhaupt wirklich brauchen. Ein weiteres Beispiel dafür sind Smart-Pointers in Containern, wenn Boosts Pointer-Container sich viel besser eignen würden.

    Und bevor sich Daramos so eine schlechte Technik noch abschaut, möchte ich das lieber erwähnen. Wie gesagt: Es war als konstruktive Kritik gedacht, nicht als Haarspalterei.



  • Nexus schrieb:

    Und bevor sich Daramos so eine schlechte Technik noch abschaut, möchte ich das lieber erwähnen.

    So eideutige Feindbilder möchte ich mal haben.

    Ein smart_ptr<> ist nicht "so eine schlechte Technik", sondern lediglich gegenüber einem Scoped-Pointer ein kleiner Overhead, der angesichts des Overheads der einhergehenden Allokation eines Bitmaps vollkommen irrelevant ist.

    Von allen Argumenten für Garbage Collection, die ich bisher gelesen habe, ist dieses Smart-Pointer-/Pointer-Container-/Move-Semantik-Chaos bei weitem das eindrücklichste 😃



  • audacia schrieb:

    Ein smart_ptr<> ist nicht "so eine schlechte Technik", sondern lediglich gegenüber einem Scoped-Pointer ein kleiner Overhead, der angesichts des Overheads der einhergehenden Allokation eines Bitmaps vollkommen irrelevant ist.

    Hier vielleicht. Aber wie soll man in zeitkritischen Anwendungen auf einmal plötzlich das Richtige auswählen, wenn man es vorher nie gemacht hat? Abgesehen davon halte ich es nicht für verkehrt, auch bei kleineren Dingen das richtige Sprach- oder Bibliotheksmittel zu wählen. "Schlechte Technik" sagte ich im Hinblick auf ein ständiges Verwenden von shared_ptr als GC-Ersatz (das meine ich im Allgemeinen, nicht auf DocShoe bezogen).

    audacia schrieb:

    Von allen Argumenten für Garbage Collection, die ich bisher gelesen habe, ist dieses Smart-Pointer-/Pointer-Container-/Move-Semantik-Chaos bei weitem das eindrücklichste 😃

    Ja, sich Besitzverhältnisse genau zu überlegen scheint auch für viele Leute eine ziemliche Herausforderung zu sein. Aber wenn man wenig denken will, nimmt man sowieso nicht C++.



  • Nexus schrieb:

    in zeitkritischen Anwendungen

    ... benutzt man einen Profiler, um Bottlenecks zu finden. Wenn shared_ptr<> tatsächlich eines sein sollte, dann ist der Programmierer nur zu beglückwünschen - dann hat er auch die Kenntnisse, daraus einen scoped_ptr<> zu machen. Ein Anfänger hat in performancekritischen Anwendungen eher ganz andere Probleme.

    Nexus schrieb:

    audacia schrieb:

    Von allen Argumenten für Garbage Collection, die ich bisher gelesen habe, ist dieses Smart-Pointer-/Pointer-Container-/Move-Semantik-Chaos bei weitem das eindrücklichste 😃

    Ja, sich Besitzverhältnisse genau zu überlegen scheint auch für viele Leute eine ziemliche Herausforderung zu sein. Aber wenn man wenig denken will, nimmt man sowieso nicht C++.

    Was hat Garbage Collection jetzt mit "wenig denken" oder mit dem Bewußtmachen der Besitzverhältnisse zu tun?



  • audacia schrieb:

    benutzt man einen Profiler, um Bottlenecks zu finden. Wenn shared_ptr<> tatsächlich eines sein sollte, dann ist der Programmierer nur zu beglückwünschen - dann hat er auch die Kenntnisse, daraus einen scoped_ptr<> zu machen.

    Warum zuerst unnötigerweise shared_ptr einsetzen, Zeit messen und dann auf scoped_ptr zurückgreifen? Wenn man die Funktionalität von geteiltem Besitz nicht benötigt, kann man gleich scoped_ptr nehmen – und zwar völlig unabhängig von irgendwelchen Performance-Überlegungen. Das richtige Mittel auszuwählen hat doch nichts mit Premature Optimization zu tun. Solange man mit scoped_ptr keine Nachteile hat, spricht auch nichts gegen ihn. Und dass man möglicherweise später einmal doch geteilten Besitz braucht, sehe ich nicht als Anlass, von Beginn an shared_ptr einzusetzen. Mit typedef ist eine Umstellung problemlos möglich, zumal die Smart-Pointers von Boost und der Standardbibliothek die gleiche Schnittstelle aufweisen.

    audacia schrieb:

    Was hat Garbage Collection jetzt mit "wenig denken" oder mit dem Bewußtmachen der Besitzverhältnisse zu tun?

    Der C++-Programmierer muss sich Gedanken darüber machen, wer ein Objekt besitzt und für dessen Freigabe verantwortlich ist ( shared_ptr ist ja nicht der Normalfall). Es gibt besitzende und nur referenzierende Zeiger. Diese Unterscheidung existiert in Sprachen mit Garbage Collector nicht. Der GC kann ein Objekt aufräumen, sobald keine Referenz mehr darauf verweist. Das heisst zwar nicht, dass Zugehörigkeiten keine Rolle spielen, aber Shared-Ownership ist von der Speicherverwaltung her Standard.

    In C++ ist die Situation allerdings etwas komplizierter (du bezeichnest sie sogar als Chaos), man muss entsprechend etwas mehr überlegen und Fälle unterscheiden. Aber das trifft nicht nur auf Speicherverwaltung zu. Entweder man befasst sich mit der Materie und sieht die unterschiedlichen Smart-Pointer-Semantiken auch nicht als Problem an, oder man will sich das nicht antun und benutzt eine andere Sprache.



  • Nexus schrieb:

    Warum zuerst unnötigerweise shared_ptr einsetzen

    Wohlgemerkt fordere ich nicht, daß man es stets so machen solle. So ganz ohne Grund existieren auto_ptr<> und scoped_ptr<> ja nicht. Ich weise lediglich darauf hin, daß es nicht dramatisch, insbesondere in keiner Weise "so eine schlechte Technik" ist, wenn man das auf einen shared_ptr<> generalisiert.

    Nexus schrieb:

    In C++ ist die Situation allerdings etwas komplizierter (du bezeichnest sie sogar als Chaos)

    Sag bloß, du empfindest rvalue-Referenzen als Bereicherung 😃 Allein die Tatsache, daß man ein derart arrangiertes Konstrukt einführt - ich glaube, keine andere Mainstream-Sprache hat etwas vergleichbar Abgedrehtes -, deutet darauf hin, daß irgendwo etwas ganz gravierend schiefgelaufen ist. Aber das Thema hatten wir schonmal, das brauchen wir jetzt nicht auch noch ausführen.

    Nochmal zu

    Nexus schrieb:

    Ja, sich Besitzverhältnisse genau zu überlegen scheint auch für viele Leute eine ziemliche Herausforderung zu sein. Aber wenn man wenig denken will, nimmt man sowieso nicht C++.

    - ich sehe den Zusammenhang zum GC nicht. Ein GC nimmt dir nicht ab, über Besitzverhältnisse nachzudenken. Er verwaltet nur eine einzige Ressource, und das ist Speicher. Da es zufälligerweise die am häufigsten verwandte Ressource ist, nimmt er dir enorm viel Arbeit, Boilerplate-Code und die Konstruktion etwas herbeigeholter Besitztumsketten ab. Netter Nebeneffekt ist natürlich, daß der DAU keine Speicherlecks mehr produziert. Aber das wird natürlich nicht verhindern, daß er vergißt, das Datei-Handle, die Socket-Verbindung oder den Gerätekontext wieder freizugeben.

    Wenn man wenig denken will, benutzt man nicht einen GC, sondern irgendeine Sandkastensprache.

    Nexus schrieb:

    Entweder man befasst sich mit der Materie und sieht die unterschiedlichen Smart-Pointer-Semantiken auch nicht als Problem an, oder man will sich das nicht antun und benutzt eine andere Sprache.

    Wenn es nur immer so einfach wäre.

    Im übrigen ist es, glaube ich, an der Zeit, den Thread zweizuteilen. Es wäre sehr nett, wenn einer der Moderatoren behilflich sein könnte 🙂



  • audacia schrieb:

    Ich weise lediglich darauf hin, daß es nicht dramatisch, insbesondere in keiner Weise "so eine schlechte Technik" ist, wenn man das auf einen shared_ptr<> generalisiert.

    Ich bin eben relativ skeptisch, was solche Generalisierungen anbelangt. Denn sie haben die Tendenz, dass man Dinge nicht mehr hinterfragt und selbstverständlich anwendet, ohne sich der Nachteile bewusst zu sein. Natürlich muss es nicht immer tragisch enden, man sollte die Möglichkeiten einfach massvoll einsetzen.

    audacia schrieb:

    Sag bloß, du empfindest rvalue-Referenzen als Bereicherung 😃

    Für das momentane C++ sicher. Oder ist es nicht gut, wenn z.B. Funktionsrückgabewerte nicht mehr unnötig kopiert werden? Das ist bisher nur im Rahmen von (N)RVO möglich. Ob jetzt C++ bisher sonst einiges falsch gemacht hat, ist wieder eine andere Frage, welche nicht zuletzt auch sehr subjektiv ist.

    audacia schrieb:

    Allein die Tatsache, daß man ein derart arrangiertes Konstrukt einführt - ich glaube, keine andere Mainstream-Sprache hat etwas vergleichbar Abgedrehtes

    Könnte aber auch daran liegen, dass fast alle anderen Mainstream-Sprachen einen GC haben und Referenzen beliebig herumschubsen können.

    audacia schrieb:

    ich sehe den Zusammenhang zum GC nicht. Ein GC nimmt dir nicht ab, über Besitzverhältnisse nachzudenken.

    Nicht komplett, aber zumindest was den Speicherverwaltungsteil anbelangt. In C++ entscheiden sich die Besitzverhältnisse fast ausschliesslich dadurch, wem der Speicher zugeteilt wird. In GC-Sprachen gibts so eine Zuteilung nicht, Speicher ist überall. Zum Beispiel kannst du plötzlich entscheiden, dass eine Referenz nicht mehr einzigartig ist und ihr Objekt geteilt wird. Oder dass ein Objekt einfach so übertragen wird.

    In C++ musst du im Voraus wissen, welche Besitzstrategie du verfolgen willst. Normalerweise kommt man mit automatischen Objekten schon recht weit. Wenn man dann aber plötzlich vorhat, dass Zugriff von mehreren Orten benötigt wird und nicht klar ist, wer als letzter zugreift, braucht man Shared-Semantik und muss den Code anpassen. Das hat auch Vorteile, zum Beispiel kann man eine Strategie erzwingen und sieht oft bereits am Code, wie die Besitzverhältnisse aussehen.

    class MyClass
    {
        OtherClass o;             // besitzend
        OtherClass& p;            // nicht besitzend
        scoped_ptr<OtherClass> q; // besitzend, nicht kopierbar
        shared_ptr<OtherClass> r; // geteilter Besitz
        auto_ptr<OtherClass> s;   // transferierter Besitz
    };
    
    class MyClass
    {
        OtherClass o; // Man kann nicht direkt sagen, ob o zu diesem Objekt
        // gehört oder nur eine Referenz auf ein Objekt "ausserhalb" ist
        // (oft spielt es auch gar keine Rolle <- genau das meine ich)
    }
    

    Okay, bei Zeigern ist es in C++ auch nicht auf Anhieb eindeutig. Wenn man konsequent Smart-Pointers oder andere kapselnde Klassen für besitzende Zeiger einsetzt, allerdings schon.

    audacia schrieb:

    Wenn es nur immer so einfach wäre.

    Natürlich ist es das nicht. Aber wenn man bereits damit überfordert ist, den richtigen Smart-Pointer auszuwählen, dann kommt in C++ noch einiges auf einen zu. Gerade Smart-Pointer nehmen einem ja viel Arbeit ab, unter anderem den fehleranfälligen Boilerplatecode manueller Speicherverwaltung.

    Ich hoffe, ich konnte meine Aussagen einigermassen verständlich machen. 😉



  • Nexus schrieb:

    audacia schrieb:

    Allein die Tatsache, daß man ein derart arrangiertes Konstrukt einführt - ich glaube, keine andere Mainstream-Sprache hat etwas vergleichbar Abgedrehtes

    Könnte aber auch daran liegen, dass fast alle anderen Mainstream-Sprachen einen GC haben und Referenzen beliebig herumschubsen können.

    Delphi hat auch keinen GC und kommt prima ohne rvalue-Referenzen aus. Primär deshalb, weil Klassen dort absolut nichts mit Strukturen aka Records gemein haben und konsequenterweise auch nicht auf dem Stack landen.

    Nexus schrieb:

    // (oft spielt es auch gar keine Rolle <- genau das meine ich)

    Das meine ich auch.



  • audacia schrieb:

    Delphi hat auch keinen GC und kommt prima ohne rvalue-Referenzen aus. Primär deshalb, weil Klassen dort absolut nichts mit Strukturen aka Records gemein haben und konsequenterweise auch nicht auf dem Stack landen.

    Wie löst man das Problem in Delphi? Wenn eine Funktion ein Objekt zurückgibt, muss das der Aufrufer manuell freigeben?


Anmelden zum Antworten