![]() |
Kombi von 2D mit 3D Elementen, auch ohne OpenGL ?
Hey Freunde der Kunst,
ich würde gerne ein Bild erstellen, auf dem ich kleinere Sachen animieren kann. Wie zum Beispiel in irgend welchen Adventuren wo ein Zimmer gezeigt wird auf dem sich die Uhr bewegt. Nur würde ich gerne anstelle der Uhr mehrere Möbel sich drehen lassen wollen (symbolisches Beispiel). Die Bilder für die Animation liegen als einzelne Bilder vor. Die würde ich gerne in einer Datei zusammenfügen(gleich wie eine Video Datei). Sind eine Ansammlung von vorgerenderten JPegs. Ich mag sie nicht in eine Videodatei packen, weil die Verlustrate zu hoch ist, find ich zu mindest. Mit TBitmap kann ich nun schon arbeiten, laden und so...auch schon Dateien dort zwischen lagern und später mit bitblt auf ein Image kopieren. Nur klappt das mit den JPegs nicht :-( Und ich weis nicht wie ich die JPegs in eine Datei reinbekomme und später wieder raus, damit ich sie anzeigen kann. Von JPeg zu dem Image, sollte ja auch irgend wie gehen. Ich weis ich kling nun ein wenig so, als ob ich nun gar nicht programmieren kann, aber ich habe mich mit Grafik, per Programmierung, bie dato noch gar nicht beschäftigt und wollte nun mal doch anfangen. Ich wäre froh über ein paar Antworten. |
Re: Kombi von 2D mit 3D Elementen, auch ohne OpenGL ?
Zitat:
Ob es sich so anhört, ob du programmieren kannst oder nicht, ist doch egal! Klingt eigentlich so, als ob du ein bestimmtes Problem hast (JPEGs anzeigen und in einen Container speichern bzw. wieder laden), nicht nach mehr oder weniger. Und da bist du schon richtig in der DP, da wird dir schon geholfen werden. Da trete ich auch gerne den Beweis für an: Zitat:
Zitat:
Ja, damit hast du schon alles um ein JPEG in eine Bitmap zu packen und mit der kannst du dann wie gewohnt arbeiten. Alternativ kannst du auch der Picture Eigenschaft des TImage direkt ein TJpegImage zuweisen. Ja, was das speicher in einer Datei angeht, so hast du auch hier mehr als eine Möglichkeit. Die einfachste besteht darin, dass du einfach auf eine Komponente zurückgreifst, die die gesuchte Logik implementiert. In deinem Fall wäre das ja eigentlich nur das speichern von mehr als einer Datei in einem Container. Da würde sich dann z.B. eine Zip-Kapselung (z.B. Abbrevia) anbieten. Der Vorteil ist klar, du bekommst eine fertige Lösung. Ganz allgemein kannst du ein JPEG laden, indem du die Methode TJPEGImage.LoadFromFile ode TJPEGImage.LoadFromStream verwendest. Solltest du Daten aus einer Archiv entpacken, so kannst du hier mit einem TMemoryStream arbeiten, die Daten in diesen rein entpacken und dann an die LoadFromStream Methode übergeben. Dabei solltest du die Position des TMemorystream (gleichnamige Eigenschaft) vor dem Laden wieder auf 0 setzen (ist ein Zeiger auf die stelle ab der gelesen werden soll). Alternativ kannst du natürlich auch ein properitäres Format erstellen. Da benötigst du dann ein paar Meta-Informationen. Eigentlich musst du dir nur überlegen, wie du den Anfang und das Ende eines JPEGs in dieser Datei markierst. Das kannst du mit speziellen Auszeichnungen oder einem Inhaltsverzeichnis oder beidem machen (in einer Zip Datei wird Ähnliches gemacht, allerdings halt auch eine Menge mehr, z.B. eine CRC32 Checksumme berechnet). Frag einfach, wenn du irgendwo nicht weiter kommst! Gruß Der Unwissende |
Re: Kombi von 2D mit 3D Elementen, auch ohne OpenGL ?
Wow - das war ja mal eine schnelle Antwort. Deshalb erst mal rechtherzlichen Dank dafür und auch gleichzeitig rechtherzlichen Dank für die begrüßenden Worte.
Die Idee mit den JPGs in Bitmaps klingt logisch und so werd ich es auch machen. Dann hab ich nur "einmal" die Ladezeit und dann ruhe, wenn es im Speicher liegt und muß den Rechner nicht unnötig umrechnen lassen. Leider hab ich auch noch keine Ahnung von Streams :-( Wie könnte ich zB an Hand eines Beispiels 2 Bilder in so einen Stream legen? |
Re: Kombi von 2D mit 3D Elementen, auch ohne OpenGL ?
Meine idee wäre:
Alle Bilder nebeneinander in eine Datei. Das bild in den speicher laden, in ein TBitmap konvertieren, und die einzelnen bilder im programm trennen. (z.b. mit nem array of bitmap und bitblt) |
Re: Kombi von 2D mit 3D Elementen, auch ohne OpenGL ?
Und wie sollte das technisch Funktionieren(in Delphi)?
Bilder nebeneinander legen, also vorher alle Bilder in ein Bildbearbeitungprogramm nebeneinander legen und speichern. Dann im Programm per Maske(?) über eine Positionsangabe ein Bild holen und es dann ausgeben. hmm...in der Theorie klingt es gut. |
Re: Kombi von 2D mit 3D Elementen, auch ohne OpenGL ?
etwa so:
Delphi-Quellcode:
Dann kannst du auf alle einzelnen Frames zugreifen. Ungetestet, müsste aber gehen...
var bilder: array of TBitmap;
{...} procedure bilderladen; var i: integer; breite: integer; bild: TBitmap; const filename = 'c:\animation.jpg'; //z.b. anzahl = 16; //z.b. begin bild := TBitmap.create; setlength(bilder,anzahl); try bild.loadfromfile(filename); // Funktioniert, weil TBitmap Nachfahre von tGraphic ist breite := bild.width div anzahl; for i := 0 to anzahl-1 do begin bilder[i] := tBitmap.create; bilder[i].width := breite; bilder[i].height := bild.height; bitblt(bilder[i].canvas.handle,0,0,breite,bild.height,bild.canvas.handle, i*breite,0,srccopy); end; finally bild.free; end; end; edit: achja, du musst natürlich die unit jpeg einbinden :!: |
Re: Kombi von 2D mit 3D Elementen, auch ohne OpenGL ?
Zitat:
Das ist schon fast alles was man über einen Stream wissen muss. Hier liegen die Vorteile (sehr flinkes Arbeiten, sehr hohe Flexibilität), aber auch die Nachteile. So kannst du zwar eine Menge von Bytes auslesen, aber das bleiben halt Typlose Bytes, die musst du schon selbst interpretieren. Auch wenn man eine Menge vorgefertigter Komponenten findet, kann es schnell mal sein, dass du auf ein Dateiformat triffst, für dass du noch keine Delphi-Komponente findest. In einem solchen Fall wirst du dann (wahrscheinlich) einen Stream verwenden, die Daten einlesen und interpretieren. Ist also das, was auch gemacht wird, wenn du z.B. eine Bitmap lädtst. Jetzt gibt es schon fertige Dateitypen und viele davon haben eine Version > 1.0, das kommt nicht von ungefähr. Man lernt einfach aus den Problemen, die andere Formate mit sich brachten. Du findest hier häufig eine Menge Meta-Informationen, die nötig sind um verschiedene Dateien möglichst flexibel zu interpretieren. So kannst du z.B. in einem Tiff mehr als ein Bild speichern (ein Multipage-Tiff), so haben Zip-Archive für jede gespeicherte Datei auch die CRC32 Checksumme gespeichert usw. Deshalb ist meine Empfehlung vorab, dass du hier eher auf eine existierende Lösung zurückgreifst, da du hier a) nichts selbst lösen musst und b) von den Erfahrungen der Entwickler profitierst. Schauen wir uns aber trotzdem mal dein Problem mit Streams an: Du möchtest mehr als eine Datei in einer Art Container speichern. Jetzt gibt es verschiedene Möglichkeiten, wie du die Datei organisierst. Das Hauptproblem ist, dass du nicht weißt wie viele Dateien in so einem Container gespeichert sind. Hast du mehr als eine Datei im Container, so kannst du nicht mit Sicherheit sagen, wo eine Datei anfängt und eine andere endet. Der einfachste Weg um hier Abhilfe zu schaffen besteht darin, dass du einfach ein Tupel in den Container schreibst. Schreibst du erst die Länge als Integer/Cardinal und dann die Datei. Der Vorteil liegt darin, dass ein Integer (auf 32 Bit maschinen) immer genau 4 Byte groß ist. Liest du also die ersten 4 Byte, weißt du wie groß die gespeicherte (folgende) Datei ist. Wenn hinter dieser Datei noch mind. 4 Byte im Container stehen, geben diese folgenden 4 Byte die größe der nächsten Datei an. Das ist schon alles. Das ganze in Delphi
Delphi-Quellcode:
Das wäre der Code, um einen Stream in den Container einzufügen. Bei beiden Parametern handelt es sich um Streams, das ermöglicht hier viel Flexibilität. Ein solcher Stream kann mit der SaveToStream Methode von einer Komponente gefüllt werden oder halt aus einer Datei stammen. Ein einfaches Beispiel:
// kopieren der Bytes aus dem Stream in den Container
procedure addToContainer(const Stream : TStream; const Container : TStream); begin // prüfen ob die Streams zugewiesen wurden if assigned(Stream) and assigned(Container) then begin // schreiben der Länge des folgenden Streams Container.write(Stream.Size, sizeOf(Integer)); // schreiben der eigentlichen Daten des Streams // Der Parameter 0 als Länge ist hier speziell belegt // es wird der komplette Stream vom Anfang bis Ende kopiert Container.copyFrom(Stream, 0); end; // if assigned(Stream) and assigned(Container) end;
Delphi-Quellcode:
So ungefähr kann das hinzufügen aussehen. Natürlich wirkt es immer etwas aufgefüllt mit den ganzen Schutzblöcken, aber diese stellen nun mal sicher, dass du Speicher auch bei einem Fehler wieder frei gibst. Gerade wenn du ganz generell mal das .Free vergisst, kann es bei der Arbeit mit Bildern schnell sein dass der gesamte Speicher belegt ist.
var Container : TStream;
buffer : TStream; bitmap : TBitmap; begin Container := TFileStream.Create('PFAD\ZUR\DATEI.xyz', fmCreate or fmShareExclusive); // Ressourcen-Schutzblöcke stellen sicher, dass eine erzeugte Ressource auch // wieder frei gegeben wird try bitmap := TBitmap.Create; try bitmap.LoadFromFile('Pfad zu irgendeiner Bitmap); buffer := TMemoryStream.Create; try bitmap.SaveToStream(buffer); addToContainer(buffer, Container); finally buffer.Free; end; // hier könntest du jetzt noch beliebige andere Bilder Laden und so einfügen // oder auch Dateien oder oder oder finally bitmap.Free; end; finally Container.Free; end; end; Ja, zu guter Letzt kommt noch das Laden aus dieser Datei. Der Aufbau ist dabei einfach, du liest erst 4 Byte (die Länge) und danach die Anzahl von Bytes, die du halt als Länge gelesen hast.
Delphi-Quellcode:
Ja, damit bekommst du einen Stream aus dieser Funktion zurück. Ist dieser nil, heißt dass das es keine weiteren gespeicherten Daten im Container gibt. Wie du hier vielleicht schon bemerkt hast, der Positionszeiger eines Streams wird aut. inkrementiert. Liest du x Bytes, dann zeigt der nun auf das 11 Byte. Somit muss man sich nicht um das inkrementieren beim Lesen/Schreiben kümmern, sollte ihn aber immer auf einen definierten Wert setzen. Beim Anlegen ist diese Position aut. 0.
function readNextFromContainer(const Container : TStream) : TStream;
var length : Integer; begin // prüfen ob es noch mindest 4 Byte im Stream gibt if Container.Position + sizeOf(Integer) < Container.Size then begin // lesen der Länge Container.Read(length, sizeOf(Integer)); // prüfen ob die Länge gültig ist if Container.Position + length < Container.Size then begin result := TMemoryStream.Create; result.CopyFrom(Container, length); end // if Container.Position + length < Container.Size else begin result := nil; end; end // if Container.Position + sizeOf(Integer) < Container.Size else begin // keine weitere Datei im Container result := nil; end; end; Ja, hoffe es hilft etwas weiter. Gruß Der Unwissende |
Re: Kombi von 2D mit 3D Elementen, auch ohne OpenGL ?
Wow - rechtherzlichen Dank!!!!!
Ich setz mich gerade an den Rechner und werd es gleich mal probieren. |
Re: Kombi von 2D mit 3D Elementen, auch ohne OpenGL ?
@NamenLozer:
Hab deine Variante ausprobiert und bis auf den Punkt das ich das JPeg nicht laden kann, weil er das in das Bitmap nicht reinladen kann, gehts. Ich habe das Bild einfach als Bitmap gespeichert und schon konnte ich alles anwenden. Nur eine Sache geht nicht. Ich habe nun alle Bilder in dem Feld drin. Und genau ein Bild kann ich der TImage Komponente zuweisen, alle weiteren werden scheinbar ignoriert. Hab ich nun irgend was falsch gemacht? Ändern tue ich das ganze per:
Delphi-Quellcode:
Danke für die Hilfe bisher!
procedure TForm1.Button1Click(Sender: TObject);
begin bitblt(Image1.Canvas.Handle, 0, 0, breite, hoehe, Bilder[pos].Canvas.Handle, 0, 0, srccopy); inc(pos); if pos > anzahl then pos := 0; end; Edit: Hab es gefunden, per Image1.Repaint. Das hatte ich vergessen gehabt :-( |
Re: Kombi von 2D mit 3D Elementen, auch ohne OpenGL ?
@Der_Unwissende:
Ich habe nun auch deines Ausprobiert und mit dem was du geschrieben hast, klappt es prima, kann sogar sehen das er die Datei anlegt. Was jedoch nicht klappt ist, das ich ein Bild draus lade und dieses dann per TImage ausgeben kann :-( Meine Prozedure sieht so dazu aus:
Delphi-Quellcode:
Die Fehlermeldung sagt etwas von einer "Privilegierte Anweisung".
procedure TForm1.Button2Click(Sender: TObject);
var Stream : TStream; begin bitmap := TBitmap.Create; Stream := TStream.Create; try Stream := readNextFromContainer(Container); if Stream <> NIL then begin bitmap.LoadFromStream(Stream); bitblt(Image1.Canvas.Handle, 0, 0, bitmap.Width, bitmap.Height, bitmap.Canvas.Handle, 0, 0, srccopy); end; finally Stream.Free; bitmap.Free; end; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 16:14 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz