![]() |
Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Ich habe eine generische TObjectList, in der u.a. auch Vorschaubilder als TBitmap gespeichert sind. Ich möchte diese Vorschaubilder gern auf der Platte speichern, damit sie nicht jedes Mal unnötiger Weise erstellt werden müssen.
Ich habe jetzt eine funktionierende Lösung, die mit Streams arbeitet. Nachteil nur, dass nach dem Wiedereinlesen in die TObjectList bestimmte Bilder auf dem Kopf stehen. Das hängt meiner Vermutung nach damit zusammen, dass bei Bitmaps gern bei der untersten Scanline angefangen wird und nicht bei der obersten. Ich weiß, dass ich den Bitmap-Header auslesen könnte, um die Leserichtung zu ermitteln. Meine Idee wäre jetzt aber: Die Bitmap liegt doch im Speicher exakt so vor, wie sie richtig ist. Kann ich sie nicht einfach als einfache Bytes unter Umgehung aller Bitmap-Routinen in einen Stream schreiben und - das vor allem - aus dem (File-)Stream wieder in die Objektliste? |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Wie speicherst du die Bitmaps denn jetzt?
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Delphi-Quellcode:
Die exakte Größe der Bitmap im Speicher müsste nach
procedure SchreibeBilderListeInDB;
var BildNr:integer; Writer: TWriter; Stream : TFileStream; begin Try Stream := TFileStream.Create(Dateiname,fmCreate); Except exit; End; Writer := TWriter.Create(Stream,4096); Try Writer.WriteListBegin; // Anzahl Datensätze Writer.WriteInteger(Bilderliste.Count); For BildNr := 1 to BilderListe.Count do WriteBitmapToWriter(Bilderliste[BildNr - 1].VSB,Writer); Writer.WriteListEnd; Writer.FlushBuffer; Finally Stream.Free; Writer.Free; End; end; procedure WriteBitmapToWriter(Bmp: TBitmap; Writer: TWriter); var Stream: TMemoryStream; //----------------------------------------------------------------------------------------------------------------------------- procedure WriteStreamToWriter(Stream: TMemoryStream; Writer: TWriter); const BufBytes = 1024*16; // 16 Kb var ReadBytes: Integer; Buf: array [0..BufBytes-1] of Byte; begin Stream.Position := 0; Writer.WriteInteger(Int64(Stream.Size)); while Stream.Position < Stream.Size do begin ReadBytes := Stream.Read(Buf, BufBytes); Writer.Write(Buf, ReadBytes); end; end; //----------------------------------------------------------------------------------------------------------------------------- begin Stream := TMemoryStream.Create; try Bmp.SaveToStream(Stream); WriteStreamToWriter(Stream, Writer); finally Stream.Free; end; end;
Delphi-Quellcode:
InfoHeaderSize + ImageSize sein. Ich stelle mir also vor, diese Anzahl Bytes ab Addr(Bilderliste[BildNr - 1].VSB) auszulesen und in eine Datei zu schreiben. Lesen kann auch kein Problem sein. Aber wie schreibe ich die Bytes in den Speicher? Speicher reservieren, Bytes reinschreiben, und dann den Pointer von Bilderliste[BildNr - 1].VSB auf diese Adresse setzen oder wie?
GetDIBSizes(Bmp.Handle, InfoHeaderSize, ImageSize);
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Also wenn ich Bitmaps aus einer Liste in einen Stream speichern wollte, dann würde ich das so machen:
Delphi-Quellcode:
Das Lesen ist etwas aufwändiger (aber nicht wirklich)
procedure TBitmapList.SaveToStream( AStream: TStream );
var idx: Integer; begin AStream.WriteData( Count ); for idx := 0 to Count - 1 do Self[ idx ].SaveToStream( AStream ); end;
Delphi-Quellcode:
und zur Vollständigkeit
procedure TBitmapList.LoadFromStream( AStream: TStream );
var idx, lCount: Integer; lBitmap : TBitmap; begin Clear; AStream.ReadData( lCount ); for idx := 0 to lCount - 1 do begin lBitmap := TBitmap.Create; try lBitmap.LoadFromStream( AStream ); Add( lBitmap ); lBitmap := nil; finally lBitmap.Free; end; end; end;
Delphi-Quellcode:
type
TBitmapList = class( TObjectList<TBitmap> ) public procedure SaveToStream( AStream: TStream ); procedure LoadFromStream( AStream: TStream ); end; |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Eine sehr schöne, kurze und elegante Lösung - aber die Bilder stehen nach dem Einlesen aus der Datei auf dem Kopf.
Das muss mit den Bitmap-Routinen zu tun haben, die ich aus diesem Grund ja vermeiden wollte. Ich habe mir mittlerweile einen Weg gebastelt, die Bytes direkt in einen Stream zu schreiben und sie dann auch wieder auszulesen. Das Schreiben funktioniert auch, aber - obwohl die Anzahl der Bytes genau stimmt - meldet Delphi beim Einlesen "Ungültiges Bitmap". Ich werde morgen die Routinen mal nach deinem Vorbild anpassen und dann sehen, ob es funktioniert. Vielen Dank einstweilen. |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Die eigentlichen Daten verwaltet das GDI (BitmapInfoHeader, Palette, Buffer der Pixeldaten), die liegen also nicht alle zusammen schön geordnet in einem Speicherbereich. Wenn man die Pixeldaten per Scanline einlesen möchte, vorher also mindestens diese Schritte: - Erzeugen der TBitmap-Instance - Setzen von Pixelformat, Breite, Höhe |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Danke für die Antworten, ich komme heute leider nicht mehr zu weiteren Erforschungen.
Zitat:
|
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
Zitat:
Und manchmal auch noch in einer anderen Reihenfolge, als man denkt, z.B. die Zeilen gern von unten nach oben. :stupid: |
AW: Objekte einer TObjectList direkt aus dem Speicher lesen / in ihn schreiben
So, und für Folgendes bitte ich um eine Erklärung.
Ich lade ein Bitmap aus einer Datei, zeichne sie auf die Form, speichere sie per Stream in einer Datei, lese sie wieder aus und zeichne sie erneut. Ergebnis: Beide Bilder werden richtig angezeigt. Danach lade ich ein JPG aus einer Datei, extrahiere eine Bitmap und zeige sie an. Ergebnis: Sie wird richtig angezeigt. Danach speichere sie per Stream in einer Datei, lese sie wieder aus und zeichne sie erneut. Ergebnis: Das Bild wird auf dem Kopf stehend angezeigt. In allen 4 Fällen ist im InfoHeader der Wert von biHeight positiv.
Delphi-Quellcode:
procedure TForm1.ZeigeBitmap;
var Bmp:TBitmap; Stream : TMemoryStream; FStream:TFileStream; BildAnz:integer; const Verz = 'C:\Temp\'; JPGDatei = 'Test.JPG'; BMPDatei = 'Test.bmp'; DBDatei = 'Test.db'; begin // Bitmap laden Bmp := TBitmap.Create; Bmp.LoadFromFile(Verz + BMPDatei); // Auf Form zeichnen Self.Canvas.Draw(10,10,Bmp); // In DB speichern FStream := TFileStream.Create(Verz + DBDatei,fmCreate); BildAnz := 1; FStream.WriteData(BildAnz); Bmp.SaveToStream(FStream); FStream.Free; Bmp.Free; // Wieder laden und anzeigen Bmp := TBitmap.Create; Stream := TMemoryStream.Create; Stream.LoadFromFile(Verz + DBDatei); Stream.ReadData(BildAnz); Bmp.LoadFromStream(Stream); Self.Canvas.Draw(10,250,Bmp); Bmp.Free; // Thumbnail aus JPG-Datei Bmp := TBitmap.Create; ExtractThumbnail(Bmp,Verz + JPGDatei,400,0); // Auf Form zeichnen Self.Canvas.Draw(450,10,Bmp); // In DB speichern FStream := TFileStream.Create(Verz + DBDatei,fmCreate); BildAnz := 1; FStream.WriteData(BildAnz); Bmp.SaveToStream(FStream); FStream.Free; Bmp.Free; // Wieder laden und anzeigen Bmp := TBitmap.Create; Stream := TMemoryStream.Create; Stream.LoadFromFile(Verz + DBDatei); Stream.ReadData(BildAnz); Bmp.LoadFromStream(Stream); Self.Canvas.Draw(450,250,Bmp); Bmp.Free; Stream.Free; end; procedure ExtractThumbnail(Bitmap:TBitmap; FileName:string; DesiredWidth:integer; DesiredHeight:integer); var Malloc:IMalloc; DesktopFolder,SourceFolder:IShellFolder; eaten,flags,prio:cardinal; id:PItemIDList; ex:IExtractImage; s:TSize; h:HBITMAP; w:WideString; begin try OleCheck(SHGetMalloc(Malloc)); OleCheck(SHGetDesktopFolder(DesktopFolder)); flags:=0; w:=ExtractFilePath(FileName); OleCheck(DesktopFolder.ParseDisplayName(0,nil,PWideChar(w),eaten,id,flags)); try OleCheck(DesktopFolder.BindToObject(id,nil,IShellFolder,SourceFolder)); finally Malloc.Free(id); end; w:=ExtractFileName(FileName); OleCheck(SourceFolder.ParseDisplayName(0,nil,PWideChar(w),eaten,id,flags)); If SourceFolder.GetUIObjectOf(0,1,id,IExtractImage,nil,ex) <> S_OK then exit; s.cx:=DesiredWidth; s.cy:=DesiredHeight; If (s.cx > 160) or (s.cy > 90) then flags:=IEIFLAG_SCREEN or IEIFLAG_OFFLINE or IEIFLAG_QUALITY or IEIFLAG_ORIGSIZE else flags:=IEIFLAG_SCREEN or IEIFLAG_OFFLINE or IEIFLAG_QUALITY; prio:=0; SetLength(w,MAX_PATH); OleCheck(ex.GetLocation(PWideChar(w),Length(w)*2,prio,s,32,flags)); OleCheck(ex.Extract(h)); Bitmap.Handle:=h; finally Malloc.Free(id); DesktopFolder:=nil; SourceFolder:=nil; Malloc:=nil; end; end; |
Alle Zeitangaben in WEZ +1. Es ist jetzt 15:20 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