Seite 1 von 2

Bild in Speicher speichern?

Verfasst: 13.05.2008 10:13
von Programie
Hi,

Wie kann man ein Bild, welches mit "CreateImage()" erstellt wurde in den Speicher reinschreiben, damit man es später mit "WriteData()" zusammen mit anderen Daten in eine Datei speichern kann. Momentan speicher ich das Bild zuerst als Temp-Datei auf die Festplatte und lese die dann wieder aus und schreibe die Daten aus der Datei zusammen mit ein paar Informationen über das Bild in eine andere Datei. Aber wenn es eine Möglichkeit geben würde, könnte ich das Bild direkt in die neue Datei speichern. Das programm hätte dadurch auch einen großen Performancegewinn, da die Funktion "SaveImage()" kurzzeitig 100% CPU-Auslastung braucht. :wink:

Thx :allright:

Verfasst: 13.05.2008 10:42
von STARGÅTE
das Image befindet sich doch schon im Speicher, du musst nur die Position und Länge des Image ermittel und mit Memory Befehlen auslesen

Verfasst: 13.05.2008 10:57
von Programie
Ich wollte das jetzt mal so machen:

Code: Alles auswählen

If CreateImage(1,1024,768)
 If StartDrawing(ImageOutput(1))
   Box(100,100,500,300,RGB(255,255,255))
  Buffer=DrawingBuffer()
  Size=DrawingBufferPitch()*768
   If Buffer
    If CreateFile(1,"A:\Bild.bmp")
      WriteData(1,Buffer,Size)
     CloseFile(1)
    EndIf
   EndIf
  StopDrawing()
 EndIf
EndIf
Aber Buffer ist immer "0". :freak:
Was mache ich falsch? :(

PS:
Ich möchte das Ganze mit dem zusätzlichen Datenreinschreiben mit dem Code machen:

Code: Alles auswählen

 ScreenDM.DEVMODE
 Width=GetSystemMetrics_(#SM_CXSCREEN)
 Height=GetSystemMetrics_(#SM_CYSCREEN)
 ScreenDC=CreateDC_("DISPLAY","","",ScreenDM)
 DC=CreateCompatibleDC_(ScreenDC)
 ImageID=CreateCompatibleBitmap_(ScreenDC,Width,Height)
 SelectObject_(DC,ImageID)
 BitBlt_(DC,0,0,Width,Height,ScreenDC,0,0,#SRCCOPY)
 DeleteDC_(DC)
 ReleaseDC_(ImageID,ScreenDC)
 Image=CreateImage(#PB_Any,Width,Height)
  If IsImage(Image)
    If StartDrawing(ImageOutput(Image))
      DrawImage(ImageID,0,0)
     StopDrawing()
    EndIf
   ProcedureReturn SaveImage(Image,FileName$,ImageTyp,Flags)
  EndIf
Gibt es vielleicht WinAPI-Befehle, mit denen man direkt die Speicheradresse und Datengröße des Bildes bekommt?

Verfasst: 13.05.2008 15:59
von Bax
Hi Programmie,

vielleicht hilft dir der Code von Freedimension in diesem Thread weiter:
http://www.purebasic.fr/german/viewtopic.php?t=1164

DrawingBuffer() geht nur bei SpriteOutput() und ScreenOutput (steht auch in der Hilfe) :wink: .

Grüße
Bax

Verfasst: 13.05.2008 16:45
von Programie
Bax hat geschrieben:Hi Programmie,

vielleicht hilft dir der Code von Freedimension in diesem Thread weiter:
http://www.purebasic.fr/german/viewtopic.php?t=1164

DrawingBuffer() geht nur bei SpriteOutput() und ScreenOutput (steht auch in der Hilfe) :wink: .

Grüße
Bax
Das sieht so langsam aus. :lol:

Code: Alles auswählen

For x=1 to Width
 For y=1 to Height
  ; ...
 Next
Next
Das kann dauern.
Es soll nicht langsamer als SaveImage() sein! :wink:
Aber Thx :allright:

Verfasst: 13.05.2008 18:02
von Bax
Hi Programie,

die beiden Prozeduren CopyImageToMem() und CopyMemToImage() scheinen genau dein Anliegen zu erfüllen. Die dritte Prozedur Filtern() dient lediglich dazu einen Weichzeichnen-Effekt zu realisieren, wonach der Thread-Ersteller gefragt hatte.

Ich habe mal einen Code zusammengebastelt, der ein Bild erstellt, in einen Speicherbereich schreibt, es zusammen mit anderen Daten in eine Datei schreibt, dann wieder lädt und anzeigt.

Code: Alles auswählen

Procedure CopyImageToMem(Img.l, mem.l)
  Protected bmi.BITMAPINFO
  Protected w.l, h.l, hBmp.l, hDC.l

  w = ImageWidth(Img)
  h = ImageHeight(Img)
  hBmp = ImageID(Img)
 
  bmi\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  bmi\bmiHeader\biWidth       =  w
  bmi\bmiHeader\biHeight      = -h
  bmi\bmiHeader\biPlanes      =  1
  bmi\bmiHeader\biBitCount    = 32
  bmi\bmiHeader\biCompression = #BI_RGB
 
  hDC  = StartDrawing( ImageOutput(Img) )
  If GetDIBits_(hDC, hBmp, 0, h, mem, bmi, #DIB_RGB_COLORS)
    StopDrawing()
    ProcedureReturn #True
  Else
    StopDrawing()
    ProcedureReturn #False
  EndIf
EndProcedure

Procedure CopyMemToImage(mem.l, Img.l)
  Protected bmi.BITMAPINFO
  Protected w.l, h.l, hBmp.l, hDC.l

  w    = ImageWidth(Img)
  h    = ImageHeight(Img)
  hBmp = ImageID(Img)
 
  bmi\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  bmi\bmiHeader\biWidth       =  w
  bmi\bmiHeader\biHeight      = -h
  bmi\bmiHeader\biPlanes      =  1
  bmi\bmiHeader\biBitCount    = 32
  bmi\bmiHeader\biCompression = #BI_RGB
 
  hDC  = StartDrawing( ImageOutput(Img) )
  If SetDIBits_(hDC, hBmp, 0, h, mem, bmi, #DIB_RGB_COLORS)
    StopDrawing()
    ProcedureReturn #True
  Else
    StopDrawing()
    ProcedureReturn #False
  EndIf
EndProcedure 


;- Image erstellen

CreateImage(0, 8, 8, 32)

StartDrawing(ImageOutput(0))
  Box(0, 0, 8, 8, $FFFF00)
  Plot(0, 0, $FF0000)
  Plot(7, 7, $0000FF)
StopDrawing()


;- Image anzeigen

OpenWindow(0, #PB_Ignore, #PB_Ignore, 200, 200, "Image (1) - direkt nach Erstellung", #PB_Window_SystemMenu)
CreateGadgetList(WindowID(0))
ImageGadget(0, 0, 0, 200, 200, ImageID(0))

Repeat  :  Until WaitWindowEvent() = #PB_Event_CloseWindow

CloseWindow(0)


;- Image in Speicher kopieren

*mem = AllocateMemory(ImageWidth(0)*ImageHeight(0)*4)

If CopyImageToMem(0, *mem) = 0
  MessageRequester("Fehler", "Image -> Mem fehlgeschlagen")
  End
EndIf


;- zur Kontrolle ob das geklappt hat, Farbwerte der einzelnen Pixel ausgeben

For i=0 To 8*8 - 1
  Debug PeekL(*mem + i*4)
Next

Debug "-----"


;- Image zusammen mit anderen Daten in Datei schreiben

CreateFile(0, "img.dat")

WriteLong(0, MemorySize(*mem))         ; Größe des Speichers
WriteLong(0, ImageWidth(0))            ; Breite
WriteLong(0, ImageHeight(0))           ; Höhe
WriteLong(0, 32)                       ; Farbtiefe
WriteData(0, *mem, MemorySize(*mem))   ; Pixel des Bilds

WriteString(0, "übrige Informationen...")

CloseFile(0)


;- Image und Speicher freigeben (um zu zeigen, dass es tatsächlich wieder geladen wird)

FreeImage(0)
FreeMemory(*mem)


;- Datei öffnen und Bild laden

ReadFile(0, "img.dat")

*mem = AllocateMemory(ReadLong(0))   ; Speicher erstellen

width = ReadLong(0)
height = ReadLong(0)
depth = ReadLong(0)
CreateImage(0, width, height, depth)   ; Image erstellen

ReadData(0, *mem, MemorySize(*mem))   ; Daten in Speicher kopieren

Debug ReadString(0)   ; übrige Daten auslesen ...

CopyMemToImage(*mem, 0)   ; Daten aus Speicher wieder auf Image kopieren


;- Image noch einmal anzeigen

OpenWindow(0, #PB_Ignore, #PB_Ignore, 200, 200, "Image (2) - nach dem Laden aus der Datei", #PB_Window_SystemMenu)
CreateGadgetList(WindowID(0))
ImageGadget(0, 0, 0, 200, 200, ImageID(0))

Repeat  :  Until WaitWindowEvent() = #PB_Event_CloseWindow

CloseWindow(0)
Ebenfalls interessant: Aufbau von Bitmaps allgemein:
http://www.fortunecity.com/skyscraper/w ... ffrmt.html

Edit: Habe gerade die Geschwindigkeit von SaveImage() und dem Weg über Memory verglichen.
Zumindest beim Speichern ist die Memory-Variante ein bisschen schneller (42807ms statt 48673).

Verfasst: 13.05.2008 21:33
von Programie
@Bax: Die Dateigröße der dat-datei ist etwas groß. Geht das nicht einfach im JPEG-Format nur dass hinten drann noch 16 Bytes stehen?
Bax hat geschrieben:Edit: Habe gerade die Geschwindigkeit von SaveImage() und dem Weg über Memory verglichen.
Zumindest beim Speichern ist die Memory-Variante ein bisschen schneller (42807ms statt 48673).
Äh??? 42 Sekunden bzw 48 Sekunden?????? :mrgreen: :lol:

Bischen langsam :wink:
Bei mir ging es in Bruchteilen von Sekunden! :allright:

Verfasst: 13.05.2008 23:04
von Bax
Hi Programie,

die sechs Sekunden Unterschied waren bei 100000 Durchgängen :wink: .

Habe mir mal den Code mit der Win-API angeschaut und die Speichern-Prozedur damit zusammen gebaut.
Es scheint als ob du ein Programm schreiben wolltest, dass in Echtzeit die Sachen, die auf dem Bildschirm passieren, aufnehmen soll.

Code: Alles auswählen

ScreenDM.DEVMODE
BMI.BITMAPINFO

Width    = GetSystemMetrics_(#SM_CXSCREEN)
Height   = GetSystemMetrics_(#SM_CYSCREEN)

BMI\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
BMI\bmiHeader\biWidth       = Width
BMI\bmiHeader\biHeight      = -Height
BMI\bmiHeader\biPlanes      = 1
BMI\bmiHeader\biBitCount    = 32
BMI\bmiHeader\biCompression = #BI_RGB

*mem = AllocateMemory(Width*Height*4)


ScreenDC = CreateDC_("DISPLAY", "", "", ScreenDM)
DC       = CreateCompatibleDC_(ScreenDC)
ImageID  = CreateCompatibleBitmap_(ScreenDC, Width, Height)

SelectObject_(DC, ImageID)
BitBlt_(DC, 0, 0, Width, Height, ScreenDC, 0, 0, #SRCCOPY)

GetDIBits_(DC, ImageID, 0, Height, *mem, BMI, #DIB_RGB_COLORS)


File = CreateFile(#PB_Any, "img.dat")

WriteLong(File, MemorySize(*mem))
WriteLong(File, Width)
WriteLong(File, Height)
WriteLong(File, 32)
WriteData(File, *mem, MemorySize(*mem))

CloseFile(File)


FreeMemory(*mem)

DeleteObject_(ImageID)
DeleteDC_(DC)
DeleteDC_(ScreenDC)
Ist etwa ein Achtel schneller als die Variante mit DrawImage und SaveImage. Aber für Echtzeit etwas langsam.

Wenn du die Daten als jpg speichern willst, musst du sie dafür erst komprimieren - das braucht auch Zeit.

Verfasst: 13.05.2008 23:26
von Programie
Was ich machen will ist ja nur ein Screenshotbefehl für meine GTA San Andreas ToolBox. Da möchste ich am Schluss der Datei noch die Position in GTA San Andreas, an der das Bild gemacht wurde, dazu schreiben. Das währe x,y,z und Blickwinkel (Angle) mit jeweils 4 Byte (Float).
Wenn es nicht so geht oder zu aufwändig für eine Screenshoterweiterung ist, mache ich es halt mit dem SaveImage() und lese dann die Datei erneut aus, um die Daten einzufügen. Problem ist das mit den unterstützten Formaten: BMP, JPG und PNG
Alle 3 Formate sollen das können. :wink:

Aber danke für den Code! :allright:
Kann man bestimmt doch mal gebrauchen.

Verfasst: 14.05.2008 00:30
von edel
Du brauchst die Datei uberhaupt nicht neu einlesen, Datei oeffnen und an das
Ende springen mit FileSeek reicht schon. Von da aus kannst du dann deine
Daten direkt in die Datei schreiben.