Sprite als tga speichern

Fragen zu Grafik- & Soundproblemen und zur Spieleprogrammierung haben hier ihren Platz.
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Sprite als tga speichern

Beitrag von Syntacks_Error »

Guten Tag,

ich arbeite an einem kleinen Grafikprogramm. Das soll nicht Gimp ersetzen, sondern dient nur einem einzigen Zweck, für den es nichts gibt. Als "Pinsel" verwende ich ein Sprite, weil man das rotieren kann. Weil ich es nicht anders hinbekommen habe, benutze ich für das Bild, das bemalt werden soll, auch ein Sprite. Das mit dem Pinsel und dem Malen klappt auch wunderbar.

Problem ist nur: Sprites kann man nicht als tga speichern (Ist das Absicht oder ein Versehen?) sondern nur als bmp,jpg, jpeg2000 (nie gehört) und png:

SaveSprite(#Sprite, DateiName$ [, ImagePlugin [, Flags]])
#PB_ImagePlugin_BMP : Speichert das Bild als BMP. (Standard-Format)
#PB_ImagePlugin_JPEG : Speichert das Bild als JPEG (der Befehl UseJPEGImageEncoder() muss verwendet werden)
#PB_ImagePlugin_JPEG2000 : Speichert das Bild als JPEG2000 (der Befehl UseJPEG2000ImageEncoder() muss verwendet werden)
#PB_ImagePlugin_PNG : Speichert das Bild als PNG (der Befehl UsePNGImageEncoder() muss verwendet werden)

Ich habe es einfach mal "SaveSprite(#Sprite, DateiName$#PB_ImagePlugin_TGA)" versucht, geht aber nicht.

Ich brauche es aber unbeding als tga. Dass ich das mit Gimp umwandeln könnte, ist klar, aber wie sieht das denn aus ...

Ich dachte mir nun, das Sprite liegt ja im Speicher, also lese ich den einfach aus. 24 bit, also 3 * 3 byte über die Bildgröße 1600 * 1600 macht 3 * 1600 * 1600. Die byte-Reihenfolge kriege ich schon hin, wenn die nicht stimmen sollte. Den tga-Header kopiere ich einfach aus einer anderen tga im selben Format (18 Byte, hat schon 10.000 mal geklappt). Von Speicher verstehe ich aber leider überhaupt nichts.

Die Speicheraddresse des Spites, kriegt man ja, habe ich hier gelesen, mit drawingbuffer() heraus. Also habe ich geschrieben:

Code: Alles auswählen


;Kopieren des tga-Headers
file1 = ReadFile(#PB_Any,"kopier.tga")
file2 = CreateFile(#PB_Any,"sprite.tga")
For x = 1 To 18
  a = ReadAsciiCharacter(file1)
  WriteAsciiCharacter(file2,a)
Next

; jetzt geht's los

StartDrawing(SpriteOutput(spriteBig))
*Buffer = DrawingBuffer() 
StopDrawing()
For x = 1 To 1600 * 1600 * 3
  a = PeekA(*buffer)
  *buffer = *buffer + 8
  WriteAsciiCharacter(file2,a)
Next
Klappt natürlich nicht und gibt einen Speicherfehler. Wie geht's richtig?
Benutzeravatar
Bisonte
Beiträge: 2427
Registriert: 01.04.2007 20:18

Re: Sprite als tga speichern

Beitrag von Bisonte »

TGA Bildformat

Bei Wikipedia ist eine Formatbeschreibung vorhanden. Allerdings, wenn ich diese Beschreibung lese, frage ich mich, warum es tga sein muss...
Png scheint da doch mehr zu bieten...
PureBasic 6.04 LTS (Windows x86/x64) | Windows10 Pro x64 | Asus TUF X570 Gaming Plus | R9 5900X | 64GB RAM | GeForce RTX 3080 TI iChill X4 | HAF XF Evo | build by vannicom​​
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Re: Sprite als tga speichern

Beitrag von Syntacks_Error »

Tga muss es sein, das nur das Format dann weiterverarbeitet werden kann. Warum ich den Header nicht selber mache? Mir ist es zu lästig, da 18 Bytes zu definieren und auch noch die Art und Größe des Bildes zu berücksichtigen. Kopieren geht leichter. Wenn ich verstünde, wie das mit "DATA" funktioniert, wäre es vielleicht anderes, tue ich aber nicht.

Das Programm ist eigentlich fertig und läuft prächtig, nur das Speichern geht halt nicht.

Habe bei der Gelegenheit übrigens festgestellt, dass PB kein Greyscale-TGA kann (Indexed colour habe ich nicht ausprobiert). Ist sicherlich nicht wichtig, sollte aber vielleicht erwähnt werden, ich war schon etwas enttäuscht.
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Re: Sprite als tga speichern

Beitrag von Syntacks_Error »

Ich habe es inzwischen hinbekommen:

Code: Alles auswählen

StartDrawing(SpriteOutput(spriteBig))
 adresse = DrawingBuffer()
 StopDrawing()
 *adresse = adresse
 
 For x = 1 To 1600 * 1600
  rot  = PeekA(*adresse)
  *adresse + 1
  gruen = PeekA(*adresse)
  *adresse + 1
  blau = PeekA(*adresse)
  *adresse + 1
  *adresse + 1 ; <= PB benutzt vier Byte/Pixel und nicht drei
  WriteAsciiCharacter(file1,rot)
  WriteAsciiCharacter(file1,gruen) 
  WriteAsciiCharacter(file1,blau)
Next
Allerdings steht das Bild auf dem Kopf, da die y-Achse im Original von unten nach oben läuft. Die Zeilen vom Ende nach vorne auszulesen, habe ich dann nicht mehr geschafft ;-) Als Ersatz lade ich nach dem Speichern erneut und speichere nochmal. Es ist die Funktionalität, die zählt ;-)
blastar
Beiträge: 25
Registriert: 10.06.2011 17:23

Re: Sprite als tga speichern

Beitrag von blastar »

Hi, ich bin zwar nicht der PB-Crack aber da ich auch schon viel Hilfe in den Foren gefunden habe werfe ich mal mein Wissen in die Runde!

Das 'SaveSprite()' nicht als TGA speichern kann hat einen einfach Grund, es gibt kein 'UseTGAImageEncoder()', ist wohl einfach zu alt und zu ineffizient.

Aber dein Ansatz ging schon einmal in die richtige Richtung:
Den Header eines anderen TGA-Files aus Bequemlichkeit (es sind ja nur 12 Werte) ist unguenstig... das Problem daran ist das du erstmal nicht weisst welches Format der Drawingbuffer hat und dieser nicht mit dem urspruenglichen Image identisch sein muss, dafuer gibt es 'DrawingBufferPixelFormat()'.
Bei mir gibt diese Funktion den Wert '#PB_PixelFormat_32Bits_BGR' zurueck und der entsprechende funktionierende Code (32bit uncompressed RGB Image) schaut dann so aus:

Code: Alles auswählen

StartDrawing(SpriteOutput(spriteBig))

*Buffer = DrawingBuffer() 
file2 = CreateFile(#PB_Any, "sprite.tga", 0)

WriteByte(file2, 0) ; no id field
WriteByte(file2, 0) ; no color map
WriteByte(file2, 2) ; uncompressed true-color image
WriteWord(file2, 0) ; first colour Map entry in palette
WriteWord(file2, 0) ; number of colours in palette
WriteByte(file2, 0) ; number of bits per palette entry 15,16,24,32
WriteWord(file2, 0) ; x-origin
WriteWord(file2, 0) ; y-origin
WriteWord(file2, SpriteWidth(spriteBig)) ; width
WriteWord(file2, SpriteHeight(spriteBig)) ; height
WriteByte(file2, 32) ; bits per pixel
WriteByte(file2, %00100000) ; image descriptor bits ( descriptor: 00vhaaaa / h horizontal flip / v vertical flip / a alpha bits)

For y = 0 To SpriteHeight(spriteBig)-1
  For x = 0 To SpriteWidth(spriteBig)-1
    
    wert0.b = PeekB(*buffer + (y*SpriteWidth(spriteBig)*4)+(x*4+0)) ; blue
    wert1.b = PeekB(*buffer + (y*SpriteWidth(spriteBig)*4)+(x*4+1)) ; green
    wert2.b = PeekB(*buffer + (y*SpriteWidth(spriteBig)*4)+(x*4+2)) ; red
    wert3.b = PeekB(*buffer + (y*SpriteWidth(spriteBig)*4)+(x*4+3)) ; | $ff ; alpha <- wenn 24bit TGA geladen wurde!
    
    WriteByte(file2, wert0)
    WriteByte(file2, wert1)
    WriteByte(file2, wert2)
    WriteByte(file2, wert3)
    
  Next
Next

CloseFile(file2)

StopDrawing()
Das ist jetzt der Code fuer meinen Rechner, da ist alles 'hart verdrahtet', um flexibel zu sein muessten alle Rueckgabewerte von 'DrawingBufferPixelFormat()' entsprechend behandelt werden.

Weiterhin scheint die Farbreihenfolge im RGB-Mode eines TGA Files NICHT R-G-B zu sein sondern B-G-R (24Bit) bzw. B-G-R-A (32bit). Das Pixelformat in deinem DrawingBuffer scheint also ebenfalls '#PB_PixelFormat_32Bits_BGR' zu sein, daher liest du 4 Bytes (statt der erwarteten 3) und schreibst sie zufaelligerweise auch in der richtigen Reihenfolge, das kann auf anderen Rechnern aber schief gehen.

Code: Alles auswählen

 TGA Legend:
 x - unsed bit
 A - alpha channel bit
 R - red channel bit
 G - green channel bit
 B - blue channel bit
 
15-bit pixel depth
 color value: xRRRRRGG GGGBBBBB
 byte[0] = GGGBBBBB; 
 byte[1] = xRRRRRGG;
 
16-bit pixel depth
 color value: ARRRRRGG GGGBBBBB
 byte[0] = GGGBBBBB; 
 byte[1] = ARRRRRGG;
 
24-bit pixel depth
 color value: RRRRRRRR GGGGGGGG BBBBBBBB
 byte[0] = BBBBBBBB; 
 byte[1] = GGGGGGGG;
 byte[2] = RRRRRRRR;
 
32-bit pixel depth
 color value: AAAAAAAA RRRRRRRR GGGGGGGG BBBBBBBB
 byte[0] = BBBBBBBB; 
 byte[1] = GGGGGGGG;
 byte[2] = RRRRRRRR;
 byte[3] = AAAAAAAA;
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Re: Sprite als tga speichern

Beitrag von Syntacks_Error »

Vielen Dank! Ich werde das anpassen und verwenden. Alleine das doppelte Speichern hat mich schon genervt, so ein Notbehelf ist ja nicht gerade schön.

Zufällig war die Farbreihenfolge nicht, das war Versuche und Irrtum :-) Warum sie dann stimmte, war mir allerdings nicht klar. Hardwareabhängigkeit ist natürlich ganz schlecht, schon deshalb muss da etwas richtiges stehen. Hast mir sehr geholfen, ich werde mich aber wohl kaum revanchieren können ...
Benutzeravatar
Kurzer
Beiträge: 1614
Registriert: 25.04.2006 17:29
Wohnort: Nähe Hamburg

Re: Sprite als tga speichern

Beitrag von Kurzer »

Syntacks_Error hat geschrieben:...Das soll nicht Gimp ersetzen, sondern dient nur einem einzigen Zweck, für den es nichts gibt.
Hallo Syntacks Error, hast Du Dir mal "Pro Motion" von Cosmigo angesehen?
https://www.cosmigo.com/

Evtl. kennst Du ja von früher vom Amiga das Grafikprogramm "DeluxePaint". Das Pro Motion ist quasi DeluxePaint im Godmode. Das kann genau das was Du suchst. Es gibt auch eine eingeschränkte Free-Version, die trotzdem schon recht umfangreich ist.

Gruß Kurzer
"Never run a changing system!" | "Unterhalten sich zwei Alleinunterhalter... Paradox, oder?"
PB 6.02 x64, OS: Win 7 Pro x64 & Win 11 x64, Desktopscaling: 125%, CPU: I7 6500, RAM: 16 GB, GPU: Intel Graphics HD 520
Useralter in 2023: 56 Jahre.
Benutzeravatar
Mijikai
Beiträge: 754
Registriert: 25.09.2016 01:42

Re: Sprite als tga speichern

Beitrag von Mijikai »

Hier ein TGA - Loader den ich mal angefangen habe :)
Unterstützt bisher nur unkomprimierte 24 & 32 Bit TGAs.

TGA:: Load() gibt ein PureBasic BMP (24 bzw. 32 Bit) ImageHandle zurück.

Hier das Module:

Code: Alles auswählen

;TGA loader by Mijikai (wip)

DeclareModule TGA
  Declare.i Load(*Buffer,File.s = #Null$);as bitmap (internal 24 or 32 bits)
  Declare.i SaveAsBMP(Image.i,File.s)
  Declare.i Free(Image.i)
EndDeclareModule

Module TGA
  
  Structure TGA_STRUCT
    ImageLengthId.a
    ColorMapType.a
    DataCodeType.a
    ColorMapOrigin.w
    ColorMapLength.w
    ColorBits.a
    OriginX.w
    OriginY.w
    Width.w
    Height.w
    BitsPerPixel.a
    ImageDescriptor.a
  EndStructure
  
  Procedure.i Load(*Buffer,File.s = #Null$)
    Protected *TGA.TGA_STRUCT
    Protected FileHandle.i
    Protected *ImageData
    Protected PixelSize.i
    Protected ImageBlob.i
    Protected ImageOffset.i
    Protected ImageSize.i
    Protected Image.i
    Protected PixelIndex.i
    Protected PixelOffset.i
    Protected *DrawingBuffer
    *TGA = AllocateMemory(SizeOf(TGA_STRUCT))
    If *TGA
      If *Buffer
        ;\\ load from memory / not implemented
      Else
        FileHandle = OpenFile(#PB_Any,File)  
        If FileHandle
          If ReadData(FileHandle,*TGA,SizeOf(TGA_STRUCT)) = SizeOf(TGA_STRUCT)
            If *TGA\Width > 0 And *TGA\Height > 0
              If *TGA\BitsPerPixel = 24 Or *TGA\BitsPerPixel = 32
                If *TGA\DataCodeType = 2 ;Or *TGA\DataCodeType = 10
                  PixelSize = *TGA\BitsPerPixel / 8
                  ImageSize = (*TGA\Width * *TGA\Height) * PixelSize
                  ImageBlob = *TGA\ImageLengthId + *TGA\ColorMapLength
                  ImageOffset = SizeOf(TGA_STRUCT) + ImageBlob
                  If Lof(FileHandle) - SizeOf(TGA_STRUCT) = ImageSize + ImageBlob
                    FileSeek(FileHandle,ImageOffset)
                    *ImageData = ReAllocateMemory(*TGA,Lof(FileHandle))
                    If *ImageData
                      *TGA = *ImageData
                      If ReadData(FileHandle,*TGA + ImageOffset,ImageSize) = ImageSize
                        If *TGA\DataCodeType = 2;uncompressed
                          Image = CreateImage(#PB_Any,*TGA\Width,*TGA\Height,*TGA\BitsPerPixel)
                          If Image
                            If StartDrawing(ImageOutput(Image))
                              *DrawingBuffer = DrawingBuffer()
                              For PixelIndex = 0 To (*TGA\Width * *TGA\Height) - 1
                                PixelOffset = ImageOffset + (PixelIndex * PixelSize)
                                CopyMemory(*TGA + PixelOffset,*DrawingBuffer + (PixelIndex * PixelSize),PixelSize)
                              Next
                              StopDrawing()            
                            Else
                              FreeImage(Image)
                              Image = #Null
                            EndIf  
                          EndIf
                        ElseIf *TGA\DataCodeType = 10;compressed
                          ;\\ not implemented
                        EndIf
                      EndIf
                    EndIf
                  EndIf
                EndIf
              EndIf
            EndIf
          EndIf  
          CloseFile(FileHandle)
        EndIf
      EndIf
      FreeMemory(*TGA)
    EndIf
    ProcedureReturn Image
  EndProcedure
  
  Procedure.i SaveAsBMP(Image.i,File.s)
    If IsImage(Image)
      ProcedureReturn SaveImage(Image,File)
    EndIf
  EndProcedure
  
  Procedure.i Free(Image.i)
    If IsImage(Image)
      FreeImage(Image)
    EndIf
  EndProcedure
  
EndModule

;Example:
Global Image.i

Image = TGA::Load(#Null,"test.tga")
Debug Image
Debug TGA::SaveAsBMP(Image,"test_tga_bmp.bmp")
TGA::Free(Image)
Syntacks_Error
Beiträge: 107
Registriert: 08.03.2009 16:08

Re: Sprite als tga speichern

Beitrag von Syntacks_Error »

Das ist ja gewaltig, da muss ich erst einmal verstehen, wie das funktioniert. Aber ich habe noch ein anderes klitzekleines Problem: Ich habe nicht nur ein Sprite ("spriteBig") dass als Malgrund dient, sondern auch noch ein paar andere kleine Sprites. Die sind in einer Liste gespeichert. Die will ich nun auf das spriteBig malen, das dann ja abgespeichert wird. Das sieht im Prinzip etwa so aus:

Code: Alles auswählen

EnableExplicit

UsePNGImageDecoder() 
InitSprite()
InitKeyboard()
Define window.i,screenwindow,spriteBig, spriteNr,x,y,painted
Global NewList spriteList.i()

window = OpenWindow(#PB_Any, 200,200,800,800,"Test")
screenwindow = OpenWindowedScreen(WindowID(window),0,0,800,800)

Procedure createspriteBig()
 Define spriteBig,x,y,
 spriteBig = CreateSprite(#PB_Any,800,800)
 StartDrawing(SpriteOutput(spriteBig)) 
 FillArea(0,0,-1,$FFCC66)
 StopDrawing()
 ProcedureReturn spriteBig
EndProcedure

Procedure createSprites()
 Define spriteNr,x,y
 For x = 1 To 20
  spriteNr =  CreateSprite(#PB_Any,15,15)
  RotateSprite(spriteNr,x*18,#PB_Absolute)
  AddElement(spriteList())
  spriteList() = spriteNr 
 Next  
EndProcedure

spriteBig = createspriteBig()
createSprites()

Repeat
  DisplaySprite(spriteBig,0,0)
  If painted = 0 ;               bis eine Taste gedrückt und gemalt wird
   ForEach spriteList()
    x + 1 : y + 1
    DisplaySprite(spriteList(), x*35,y*35)
   Next
   x = 0 : y = 0
   FlipBuffers()
   ClearScreen(RGB(0, 0, 0))
  EndIf

 ExamineKeyboard()
 If KeyboardInkey() ;    Dann sollen die 20 kleinen Sprites auf das spritBig gemalt werden
  StartDrawing(SpriteOutput(spriteBig))
  DrawingMode(#PB_2DDrawing_Default )
  ForEach spriteList();
   x + 1 : y + 1
   DrawImage(SpriteID(spriteList()),x*35,y*35)
   ;Box(x*35,y*35,15,15,0)
  Next
  StopDrawing()
  MessageRequester("Bild speichern und schließen?","",#PB_MessageRequester_YesNo)
  If #PB_MessageRequester_Yes
   SaveSprite(spriteBig,"sprite.png") ; spriteBig wird gespeichert, ich nehme hier mal png :-)
   End ;           Ende
  EndIf
 EndIf 
 Until WaitWindowEvent() =  #PB_Event_CloseWindow 
Bitte nach dem Start also einmal eine Taste drücken, sonst tut sich nichts.

Das bigSprite wird auch gespeichert, aber von den kleinen Sprites ist nicht zu sehen. Malt drawimage() nicht auf Sprites oder was ist hier falsch? Die Boxen werden schon gemalt, da fehlt aber die Roatation.
Benutzeravatar
diceman
Beiträge: 347
Registriert: 06.07.2017 12:24
Kontaktdaten:

Re: Sprite als tga speichern

Beitrag von diceman »

Bin grad auf der Arbeit, kann also nix testen, aber ich das verstehe, möchtest du "in ein Sprite" andere Bilddaten hineinmalen. Das ist prinzipiell kein Problem, allerdings müßten eben andere Bilddaten als Image vorliegen. Wenn du ein Sprite darstellst mit DisplaySprite (oder DisplayTransparentSprite), wird das IMMER direkt auf den Bildschirm ausgegeben; du kannst keine Sprites in andere Bildpuffer hineinzeichnen.
"Image <> Sprite", das hat auch bei mir etwas gedauert, bis ich das verstanden habe. :)
Now these points of data make a beautiful line,
And we're out of Beta, we're releasing on time.
Antworten