Bildtiefe ändern

Fragen zu Grafik- & Soundproblemen und zur Spieleprogrammierung haben hier ihren Platz.
Benutzeravatar
Batze
Beiträge: 1492
Registriert: 03.06.2005 21:58
Wohnort: Berlin
Kontaktdaten:

Bildtiefe ändern

Beitrag von Batze »

Ich muss für in meinem Programm, um die Geschwindigkeit einigermaßen im Rahmen zu halten, Bilder direkt im Speicher bearbeiten (mittels Inline-ASM-Code).
Dafür brauche ich nun aber alle Bilder im gleichen 24-Bit-Format. LoadImage() hat nun leider nur die Option #PB_Image_DisplayFormat weshalb ich dies nicht direkt erreichen kann.

Was ist die effektivste Methode um die Bilder in 24-Bit vorliegen zu haben?

Zur Zeit verwende ich einfach CreateImage() und anschließend DrawImage() was mir aber nicht besonders Effektiv erscheint. Vor allem muss ich dies selbst bei Bildern im richtigen Farb-Format tun, da die geladenen Bilder teilweise eine unterschiedliche Zeilenreihenfolge besitzen.
Wenn ich ich diese ermitteln könnte wäre zumindest dort ein Performance-Gewinn möglich (denn es werden wahrscheinlich hauptsächlich 24-Bit-Bilder importiert).

Ich weiß das mit der Änderung der Bildtiefe ist schon mehrfach gefragt worden (jedenfalls hat die Suche einiges ausgespuckt).
Allerdings habe ich dort keine Informationen für mein konkretes Problem gefunden.
Hier sind meine Codes (aber die Seite geht gerade nicht):
http://www.basicpure.de.vu
Kaeru Gaman
Beiträge: 17389
Registriert: 10.11.2004 03:22

Beitrag von Kaeru Gaman »

platform-spezifisch?

ein Image ist auch eine OS-spezifische Struktur, wie die aufgebaut ist findest du auf der MSDN.
(ich weiß nicht wie sie aufgebaut ist, ich weiß nur, dass ich auf der MSDN suchen müßte)
diese Struct enthält neben den eigentlichen Bilddaten noch eine Menge zusätzliches, u.a. auch die Bit-Tiefe.
da könntest du also gucken um zu entscheiden, ob du überhaupt konvertieren musst.

was das konvertieren selber betrifft...
dein Draw verfahren ist möglicherweise nicht das schnellste, aber bestimmt das für den Programmierer unkomplizierteste.
und da es sowieso nur in einem bruchteil der fälle überhaupt zur anwendung kommt,
wenn du vernünftig vorprüfst wie du ja planst, würde ichs dabei belassen.
Der Narr denkt er sei ein weiser Mann.
Der Weise weiß, dass er ein Narr ist.
Benutzeravatar
Batze
Beiträge: 1492
Registriert: 03.06.2005 21:58
Wohnort: Berlin
Kontaktdaten:

Beitrag von Batze »


>> platform-spezifisch?

Ja, im Moment kann ich es aus diesem Grund nur auf Windows ausrichten. Wobei es mich auch interessieren würde wie das ganze auf Mac-OS aussieht, aber das kann warten.

>> Diese Struct enthält neben den eigentlichen Bilddaten noch eine Menge zusätzliches, u.a. auch die Bit-Tiefe.
>> da könntest du also gucken um zu entscheiden, ob du überhaupt konvertieren musst.

Da liegt im Moment leider noch das Problem. Während ich Bittiefe, Breite, Höhe und den Speicherbereich der Pixel anhand von Beispielen im Forum herrausgefunden habe und so direkt im Speicher arbeiten kann, habe ich die Information welche Zeilenreihenfolge ein Bild hat bis jetzt nicht gefunden.

>> dein Draw verfahren ist möglicherweise nicht das schnellste, aber bestimmt das für den Programmierer unkomplizierteste.
>> und da es sowieso nur in einem bruchteil der fälle überhaupt zur anwendung kommt,
>> wenn du vernünftig vorprüfst wie du ja planst, würde ichs dabei belassen.

Wahrscheinlich lohnt da der geringe Performance-Gewinn den Aufwand nicht ... schon möglich.
Hier sind meine Codes (aber die Seite geht gerade nicht):
http://www.basicpure.de.vu
LCD
Beiträge: 107
Registriert: 23.01.2008 13:13
Wohnort: Wien

Beitrag von LCD »

Im CodeArchive gibt es eine Prozedur ComyImageToMem mit der man ein Image in den Memblock kopiert, und diese ist dann immer in 32Bit (allerdings nicht ABRG sondern ARGB). Schaut so aus:

Code: Alles auswählen

Procedure CopyImageToMem(img.l,mem.l)
  CompilerIf #PB_Compiler_OS=#PB_OS_Windows
    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
CompilerEndIf
EndProcedure
Vorteil: Sehr schnell, immer gleicher Format, etc. Verwende ich selber bei meinem Programm Retro-X und bekomme damit eine enorme Geschwindigkeit hin.
Zum zurückkopieren aus dem Memblock zu Image (um das veränderte Bild darzustellen oder zu speichern), das hier verwenden:

Code: Alles auswählen

Procedure CopyMemToImage(mem.l,img.l)
  CompilerIf #PB_Compiler_OS=#PB_OS_Windows
    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
CompilerEndIf
EndProcedure
Ich habe Abfragen für Windows engebaut, weil wenn ich Linux und MacOS besser kennenlerne, ähnliche Funktionen auch dort nutzen will. Du kannst sie natürlich auslassen.
PB 4.61Beta1 32/64Bit. AMD FX6100, 8 GB RAM, ATI Radeon 5750, Win7 64 (64 bit ist mist weil 16-Bit Programme wie MakeTZX nicht mehr darauf funktionieren).
Benutzeravatar
Batze
Beiträge: 1492
Registriert: 03.06.2005 21:58
Wohnort: Berlin
Kontaktdaten:

Beitrag von Batze »

Code: Alles auswählen

bmi\bmiHeader\biHeight      =-h
Kann mir das wer erklären, warum da "-Höhe" statt "Höhe" steht?
LCD hat geschrieben: Vorteil: Sehr schnell, immer gleicher Format, etc. Verwende ich selber bei meinem Programm Retro-X und bekomme damit eine enorme Geschwindigkeit hin.
Zum zurückkopieren aus dem Memblock zu Image (um das veränderte Bild darzustellen oder zu speichern), das hier verwenden: ...
Ich glaube da könnte das Problem liegen :(
Das anzeigen ist nämlich recht häufig gegenüber dem Laden. Trotzdam sieht mir das sehr interessant aus da es ja fast so aussieht als könnte dieser Speicherbereich auch ein mittels CreateImage() erstelltes Bild sein (also dessen Bits-Breich) :?
Werde ich mal ausprobieren, Danke. <)
Hier sind meine Codes (aber die Seite geht gerade nicht):
http://www.basicpure.de.vu
LCD
Beiträge: 107
Registriert: 23.01.2008 13:13
Wohnort: Wien

Beitrag von LCD »

Batze hat geschrieben:

Code: Alles auswählen

bmi\bmiHeader\biHeight      =-h
Kann mir das wer erklären, warum da "-Höhe" statt "Höhe" steht?
LCD hat geschrieben: Vorteil: Sehr schnell, immer gleicher Format, etc. Verwende ich selber bei meinem Programm Retro-X und bekomme damit eine enorme Geschwindigkeit hin.
Zum zurückkopieren aus dem Memblock zu Image (um das veränderte Bild darzustellen oder zu speichern), das hier verwenden: ...
Ich glaube da könnte das Problem liegen :(
Das anzeigen ist nämlich recht häufig gegenüber dem Laden. Trotzdam sieht mir das sehr interessant aus da es ja fast so aussieht als könnte dieser Speicherbereich auch ein mittels CreateImage() erstelltes Bild sein (also dessen Bits-Breich) :?
Werde ich mal ausprobieren, Danke. <)
zu Frage 1: Bitmaps werden standardmäßig von unten nach Oben gespeichert. Zeile 0 wäre damit wie es eigentlich üblich ist, ganz unten. Mit "h" behält man diese Richtung bei, mit "-h" kehrt man sie um, so dass das Bitmap von oben nach unten gespeichert wird, so dass Zeile 0 ganz oben ist, und es den Koordinatensystem des PC's PLOT entspricht. Das erste System kennen noch einige vom Koordinatensystem des Sinclair ZX Spectrum, obwohl das Bitmap dort von oben nach unten aufgebaut ist, entspricht PLOT 0,0 einem Punkt an Koordinaten die wir, PB Benutzer als Plot(0,175) kennen.
Zu 2: Das anzeigen ist auch mit SetGadgetState(ImageID(nr)) extrem schnell (Zumindest auf meinem System, obwohl man es sicher nicht als HighEnd bezeichnen kann). Immerhin erspart man sich mit den beiden Routinen das langsame StartDrawing()/StopDrawing(). Komischerweise ist bei mir dieses PokedImage, wie ich es gerne nenne, stark beschleunigt worden seit ich die alte GeForce 6200LE gegen Radeon X800Pro ausgetauscht habe.
Es ist jedenfalls so schnell dass ich meinen kompletten code des Retro-X bereits auf PokedImage umgestellt, und Geschwindigkeitsmäßig sehe ich gar keinen Unterschied zu Zeichnen auf Screen oder Sprite.
Zu 3: Ja, mittels CreateImage erstelltes Bild kannst du genauso wie mit LoadImage geladenes Bild immer im selben 32 Bit-Format in den Speicher holen. Das 32Bit-Format hat den Vorteil dass du jeden Pixel mit PeekL auslesen kannst (also das schnellste Peek auf 32-Bit Systemen), und einzelne RGB Werte mit PeekB.
PB 4.61Beta1 32/64Bit. AMD FX6100, 8 GB RAM, ATI Radeon 5750, Win7 64 (64 bit ist mist weil 16-Bit Programme wie MakeTZX nicht mehr darauf funktionieren).
Benutzeravatar
Batze
Beiträge: 1492
Registriert: 03.06.2005 21:58
Wohnort: Berlin
Kontaktdaten:

Beitrag von Batze »

LCD hat geschrieben:zu Frage 1: Bitmaps werden standardmäßig von unten nach Oben gespeichert. [...]
Zu 2: Das anzeigen ist auch mit SetGadgetState(ImageID(nr)) extrem schnell (Zumindest auf meinem System, obwohl man es sicher nicht als HighEnd bezeichnen kann). Immerhin erspart man sich mit den beiden Routinen das langsame StartDrawing()/StopDrawing().
(Zu 1) Genau diese Information fehlte mir. Da steckt also die Information über die Drehung. Wenn das Standardmäßig üblich ist werde ich es wohl auch so als meinen Standard verwenden.

(Zu 2) OK, wenn das so schnell ist werde ich das mal ausprobieren. Ist nur halt etwas schwer vorstellbar dass das schneller ist als die Image-Daten im Speicher direkt zu bearbeiten (Speicher bearbeiten ist doch gleich Speicher bearbeiten?) ohne sie dann zurück zu kopieren.

Die Peek/Poke-Befehl-Geschwindigkeit kann mir zum Glück ziemlich egal sein, da ich direkt mit ASM arbeite (was man da spart weil man keine Zwischenvariablen und Funktionsaufrufe benutzt ist unglaublich).



Edit: So ich habe das ganze mal Testweise verwendet. Funktioniert auch ganz gut allerdings musste ich feststellen dass ich keine möglichkeit habe bottom-up von top-down zu unterscheiden da mir die Funktionen GetDIBits_() und GetObject_() bei der Ausgabe der Struktur unter Höhe stets nur den Betrag liefern. :?

Code: Alles auswählen

UsePNGImageDecoder()


Procedure CopyImageToRGB(Image1.l, Image2.l, Depth.l)
  Protected bmi.BITMAPINFO, dib.DIBSECTION
  Protected W.l, H.l, hBmp.l, hDC.l
  
  W = ImageWidth (Image1)
  H = ImageHeight(Image1)

  ReturnImage = CreateImage(Image2, W, H, Depth)
  If Image2 = #PB_Any : Image2 = ReturnImage : EndIf

  hBmp = ImageID(Image1)
  bmi\bmiHeader\biSize        = SizeOf(BITMAPINFOHEADER)
  bmi\bmiHeader\biWidth       = W
  bmi\bmiHeader\biHeight      = H     ; -H, was viel häufiger ist geht leider nicht, da CreateImage keine BorderUp-Images erzeugt
  bmi\bmiHeader\biPlanes      = 1
  bmi\bmiHeader\biBitCount    = Depth
  bmi\bmiHeader\biCompression = #BI_RGB
  GetObject_(ImageID(Image2), SizeOf(DIBSECTION), dib)

  hdc = StartDrawing(ImageOutput(Image1))
  If GetDIBits_(hdc,hBmp, 0, h, dib\dsBm\bmBits, bmi, #DIB_RGB_COLORS)
    StopDrawing()
    ProcedureReturn ReturnImage
  Else
    StopDrawing()
    ProcedureReturn #False
  EndIf
  
EndProcedure 


OpenWindow(0, 0, 0, 210, 235, "Test", #PB_Window_MinimizeGadget)
CreateGadgetList(WindowID(0))
  ImageGadget (0, 5,   5,   1,   1, 0)
  ButtonGadget(1, 5, 210, 200,  20, "CopyImageToRGB()")
  
LoadImage(1, "bitmap_24.bmp")           ; Hier beliebiges Bild laden (Bitmaps sind meist bottom-up)
; LoadImage(1, "bitmap_24.png")         ; Als Beispiel: PNGs sind meist top-down (zumindest bei mir)
  
Repeat
  WEvent= WaitWindowEvent()
  
  If WEvent=#PB_Event_Gadget And EventGadget() = 1
    CopyImageToRGB(1, 0, 32)
    SetGadgetState(0, ImageID(0))
  EndIf

Until WEvent = #PB_Event_CloseWindow

End
Sollte das Bild also im korrekten Format vorliegen, muss ich es ungünstigerweise trotzdem damit umwandeln obwohl dies eventuell garnicht nötig wäre.
Hier sind meine Codes (aber die Seite geht gerade nicht):
http://www.basicpure.de.vu
LCD
Beiträge: 107
Registriert: 23.01.2008 13:13
Wohnort: Wien

Beitrag von LCD »

Batze hat geschrieben: (Zu 2) OK, wenn das so schnell ist werde ich das mal ausprobieren. Ist nur halt etwas schwer vorstellbar dass das schneller ist als die Image-Daten im Speicher direkt zu bearbeiten (Speicher bearbeiten ist doch gleich Speicher bearbeiten?) ohne sie dann zurück zu kopieren.
Oh, das hast du gemeint, nun, da muss ich dir recht geben. Ich dachte nur, du willst es nachher Pixelweise ausgeben, was langsamer wäre als es komplett aus dem Speicher zu holen und mit SetGadgetState auf ben Schirm zu setzen. Ich darf eben nicht von meinem Projekt auf andere schließen :mrgreen:.
Jedenfalls ist es so möglich flüssige Animationen damit zu erzeugen. Wenn du die Geschwindigkeit sehen willst, schau dir mal mein Retro-X an (auf http://lcd-one.da.ru zu finden). Der Painter und Importer nutzen beide diese Methode, und sind damit schnell genug.
PB 4.61Beta1 32/64Bit. AMD FX6100, 8 GB RAM, ATI Radeon 5750, Win7 64 (64 bit ist mist weil 16-Bit Programme wie MakeTZX nicht mehr darauf funktionieren).
Benutzeravatar
Batze
Beiträge: 1492
Registriert: 03.06.2005 21:58
Wohnort: Berlin
Kontaktdaten:

Beitrag von Batze »

LCD hat geschrieben:Jedenfalls ist es so möglich flüssige Animationen damit zu erzeugen. Wenn du die Geschwindigkeit sehen willst, schau dir mal mein Retro-X an (auf http://lcd-one.da.ru zu finden). Der Painter und Importer nutzen beide diese Methode, und sind damit schnell genug.
OK, da will ich auch hin mit der Geschwindigkeit (bei größeren Bildern und rechenintensiveren vorgängen versteht sich :wink: ).

Wie funktioniert denn dein Painter in etwa? Also was für Zwischenbilder / Zwischenspeicher verwendest du und wie wird es dann letztendlich angezeigt?

Und womit kriege ich die Maus zum passenden Cursor umgewandelt während sie über dem Bild ist?
Hier sind meine Codes (aber die Seite geht gerade nicht):
http://www.basicpure.de.vu
LCD
Beiträge: 107
Registriert: 23.01.2008 13:13
Wohnort: Wien

Beitrag von LCD »

Batze hat geschrieben:
LCD hat geschrieben:Jedenfalls ist es so möglich flüssige Animationen damit zu erzeugen. Wenn du die Geschwindigkeit sehen willst, schau dir mal mein Retro-X an (auf http://lcd-one.da.ru zu finden). Der Painter und Importer nutzen beide diese Methode, und sind damit schnell genug.
OK, da will ich auch hin mit der Geschwindigkeit (bei größeren Bildern und rechenintensiveren vorgängen versteht sich :wink: ).

Wie funktioniert denn dein Painter in etwa? Also was für Zwischenbilder / Zwischenspeicher verwendest du und wie wird es dann letztendlich angezeigt?

Und womit kriege ich die Maus zum passenden Cursor umgewandelt während sie über dem Bild ist?
Der Painter arbeitet mit einem Speicherpuffer (Ohne FreeSize: 6912 Bytes: 6144 Bytes für Monochrom Bitmap und 768 Bytes für Vorder- und Hintergrundfarbenin einem 8x8 großen Pixelfeld, also genau wie beim Sinclair ZX Spectrum. Dieser Puffer wird dann intern dekodiert und mittels PokeL direkt in ein Image reingeschrieben (Linker Teil des Painters). In der Mitte ist dann ein Zoom Bereich den ich früher auch gePokt habe, mittlerweile aber kommt GrabImage (Bereich) und ResizeImage zum Einsatz. Dieses wird mittels einer Prozedur die von SetTimer_() automatisch aufgerufen wirrd, regelmäßig refresht, um FLASH-Blinken zu simulieren (wenn vorhanden).
Alle Zeichneoperationen laufen in einem Puffer ab. Welcher auch mittels POKE und nicht PLOT beschrieben wird (Bits setzen), und dann in echtzeit dekodiert und gezeichnet.
Somit habe ich Speicherbereiche: Bild, Puffer und Zwischenspeicher für Images. Images die verwendet werden: Vorschau und Zoom.
Momentan auf meinem Athlon 64 3500+ kriege ich je nach Grafikkarte zwischen 500 und 1000 Redraws (also dekodierungen in ein Image rein) pro Sekunde hin, in der Standardauflösung von 256x192 mit Farbattributen. Währen der Benutzung der Zeichnewerkzeuge übergebe ich die Adresse des Puffers an meine PLOT- und dekodierroutine, ansonsten die Adresse des Bildspeicher-Puffers.

Was den Cursor betrifft (Muss nach jedem WaitWindowEvent() erneut aufgerufen werden), benutze ich das hier:

Code: Alles auswählen

SetCursor_(Cursor)
Als Cursor kann man die PB-Konstanten nehmen:

Code: Alles auswählen

Cursor_Cross=LoadCursor_(0,#IDC_CROSS)
Cursor_Hand=LoadCursor_(0,#IDC_HAND)
In der WinAPI-Hilfe kann man nachlesen wie man andere Cursors (z.B. animierte) laden kann
PB 4.61Beta1 32/64Bit. AMD FX6100, 8 GB RAM, ATI Radeon 5750, Win7 64 (64 bit ist mist weil 16-Bit Programme wie MakeTZX nicht mehr darauf funktionieren).
Antworten