Sprite Mirroring

Advanced game related topics
HienTau
User
User
Posts: 16
Joined: Thu Feb 24, 2005 9:31 am
Location: Bremen, Germany
Contact:

Sprite Mirroring

Post by HienTau »

I am trying to mirror (a lot of) sprites during runtime to safe some hard disk space, but unfortunately using an array and the Plot command is very slow. Is there a fast way to do this?

The current code looks like this:

; get source pixels
StartDrawing(SpriteOutput(srcImg))
For i2=0 To SpriteWidth(srcImg)-1
For i3=0 To SpriteHeight(srcImg)-1
arr(i2,i3)=Point(i2,i3)
Next i3
Next i2
StopDrawing()

; set destination pixels
StartDrawing(SpriteOutput(destImg))
For i2=0 To SpriteWidth(destImg)-1
For i3=0 To SpriteHeight(destImg)-1
If arr(i2,i3)<>0
Plot(SpriteWidth(destImg)-i2,i3,arr(i2,i3))
EndIf
Next i3
Next i2
StopDrawing()

The pixels of a sprite are copied into an array. The array is then copied to the new sprite.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

i think you can use the plgblt api to mirror stuff, but that would not work on all windows versions, do a search on the forum for plgblt_
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
HienTau
User
User
Posts: 16
Joined: Thu Feb 24, 2005 9:31 am
Location: Bremen, Germany
Contact:

Post by HienTau »

plgblt and some other blitting functions are great for Windows development (BitBlt was my best friend some years ago), but I am planning to do a game that works on Linux and Windows (at least the base; advanced stuff must be done with DirectX). I wondered, whether it is possible to get the Sprite buffer of an image, to do some memory copying like in C (memcpy). Nevertheless it seems that PureBasic does not allow this?! Do you see an alternative?
User avatar
Hroudtwolf
Addict
Addict
Posts: 803
Joined: Sat Feb 12, 2005 3:35 am
Location: Germany(Hessen)
Contact:

Post by Hroudtwolf »

There is a procedure in the german PureBasic-Lounge(Forum).
I think that can help you.

PureBasic-Lounge\Prozeduren\Grafik,Sound&Sprites
HienTau
User
User
Posts: 16
Joined: Thu Feb 24, 2005 9:31 am
Location: Bremen, Germany
Contact:

Post by HienTau »

Thank you for your suggestion. I guess you meant the flip sprite code. Unfortunately it is still Windows only. Or did I miss something?
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

hmm... you may want to figure out where the bitmap is in memory, the directly read and write it... then again, that's window based...

another approach would be to write the image to the disk, then you know exactly what is where, read it back in, swap bytes (8 bits = 1 byte, 24 or 32 bits are 3 or 4 bytes as you are perfectly aware :-)) and rewrite the bmp file... it's kind of a work around though...

another attempt may be to read from memory, adding the file as part of a datasection, then reading it from the datasection and rewriting it back to the datasection in a different sequence (that actually may work)
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
HienTau
User
User
Posts: 16
Joined: Thu Feb 24, 2005 9:31 am
Location: Bremen, Germany
Contact:

Post by HienTau »

Thank you again. Sounds interesting. I will give it a go... and tell you if one of your suggestions works for the game.
bumblebee
New User
New User
Posts: 5
Joined: Tue Feb 22, 2005 8:09 pm
Location: England
Contact:

flipping great!

Post by bumblebee »

well, with the huge amount of memory a PC has nowadays, its not likely memory you're looking to save, is it? But hard-disk space, and basically trying to keep the project as small as possible?

You sound like me, if thats the case!

So, I'd pre-mirror all your images, saving them in a new image / sprite.
Your current code would do that just fine - but the code on the german site would probably do it a lot faster, and maybe is usable in real-time?

The choice is yours!
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

What about the PureBasic function TransformSprite3D() ?
It seems to be able to swap/warp/transform it in any shape you want.

Code: Select all

Syntax

TransformSprite3D(#Sprite3D, x1, y1, [z1], x2, y2, [z2], x3, y3, [z3], x4, y4, [z4])
Description

Transforms the #Sprite3D to the new given coordinates. This is tipically used to perform real time transformations. Warning, as a Sprite3D is 2 vertex (2 triangles), the tranformation could looks very strange... If one of the optional 'z' parameter is specified, all need to be specified. 
  
  ;
  ; x1         x2
  ;   ---------
  ;   |      /|
  ;   |    /  |     
  ;   |  /    |
  ;   |/      |
  ;   ---------
  ; x4         x3
  ;

So basically just "swap" the coordinates/values. so that x1 become x2,
and x4 become x3.
I.E. performing a "flip".

I haven't tested it tough, I just peeked in the PureBasic help.
As the doc say it's ment for realtime. however.
There is nothing preventing you from displaying a "loading" screen with buffer 1,
and then in the 2nd buffer render/flip/grab/whatever you want,
and when done, clear the 2nd buffer and continue with whatever
you originaly intended to render in the 2nd buffer before the buffer flip.
HienTau
User
User
Posts: 16
Joined: Thu Feb 24, 2005 9:31 am
Location: Bremen, Germany
Contact:

Post by HienTau »

Thank you guys. I'll use Hroudtwolf's suggestion (StretchBlt), even though this solution is Windows only. Using Linux compatible code seems to be too much of a mess. TransformSprite3D is a nice idea, but it doesn't work (I tested it). Using TransformSprite3D the way it should work results in a black image.
The new (and quite short) code looks like this:
oldW=SpriteWidth(srcImg)-1
oldH=SpriteHeight(srcImg)-1

CopySprite(srcImg,destImg)
handle.l=StartDrawing(SpriteOutput(destImg))
StretchBlt_(handle, oldW, 0, -oldW, oldH, handle, 0, 0, oldW, oldH, #SRCCOPY)
StopDrawing()

For those asking themselves what StrechBlt is: You can find documentation on StretchBlt and the Windows API in general in the infamous win32.hlp, which can be found here: http://www.codingcrew.de/programmierung/win32hlp.php, for example.
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

hehe... did it :-) flip a sprite or image, and works on linux just as well...

Code: Select all

; purebasic survival guide - pb3.92
; graphics 6.pb - 27.02.2005 ejn (blueznl)
; http://www.xs4alnl/~bluez/datatalk/pure1.htm
;
; - mirroring a bitmap in memory
; - reading a bitmap file structure without purebasic commands
; - using images in a datasection
;
w_main_nr = 1
w_main_h = OpenWindow(w_main_nr,10,10,245,200,#PB_Window_SystemMenu|#PB_Window_ScreenCentered,"test")
;
; remember, reading from a file or from memory is basically the same thing, now if the image
; is already in memory, i can rotate it in memory, at least that's the theory :-)
; 
; let's draw something first
;
image1_h = CatchImage(2,?image1_data)
StartDrawing(WindowOutput())
DrawImage(image1_h,10,10)
StopDrawing()
;
;
; checking the size of the image told me, that it was exactly the same size as the file, as expected :-)
;
;   Debug ?image2_data_end - ?image2_data
;
; now the trick is: what's inside that file ie. inside that bitmap? a little search on the internet
; showed the following...
;
; a bitmap is always build up with four parts:
;
; - bitmapfileheader
; - bitmapinfoheader
; - rgbquad
; - byte
;
; let's start with the file header
;
*bitmapfileheader.BITMAPFILEHEADER            ; actually, i only care about the offset
*bitmapfileheader = ?image1_data
;
; Debug *bitmapfileheader\bfType              ; should always report 19778
; size = *bitmapfileheader\bfSize             ; size of whole structure including headers
;
offset = *bitmapfileheader\bfOffBits          ; this is where bitmap data is supposed to be
;
*bitmapinfoheader.BITMAPINFOHEADER            
*bitmapinfoheader = *bitmapfileheader+SizeOf(BITMAPFILEHEADER)
; 
; If *bitmapinfoheader\biCompression = 0      ; check for compressed images
; EndIf                                       ; not implemented 
;
image1_width = *bitmapinfoheader\biWidth      ; size
image1_height = *bitmapinfoheader\biHeight    ; 
bits_pixel = *bitmapinfoheader\biBitCount     ; bits per pixel
bytes_pixel = bits_pixel/8                    ; bytes per pixel
bytes_row = image1_width*bytes_pixel          ; bytes per row (unadjusted)
bytes_row = bytes_row+(bytes_row % 4)         ; padd to multiples of 4
;
; colour table size depends on the number of bits per pixel
;
; - 1     - colour table has two entries
; - 4     - colour table has 16 entries
; - 8     - colour table has 256 entries
; - 24    - colour table has no entries
;
; Select pixel_bits
;   Case 24
;     colourtable_size = 0    
;   Default
;     colourtable_size = Pow(2,pixel_bits) * 4
; EndSelect
; offset = 54+colourtable_size
;
; you can calculate the position of the first pixel, or use the value we found before
;
; so the first pixel must be at image2_data+offset
; well we now know where it is so we can change it!
;
; let's give the image a blue stripe on one side, why not?
;
*p = *bitmapfileheader+offset
For y = 0 To image1_width-1
  For x = 0 To image1_width / 2
    PokeB(*p+y*bytes_row+x*bytes_pixel,255)
  Next x
Next y
;
; and show it...
;
image1_h = CatchImage(2,?image1_data)
StartDrawing(WindowOutput())
DrawImage(image1_h,90,10)
StopDrawing()
;
;
; now let's mirror it
;
For y = 0 To image1_height-1
  For x = 0 To Int(image1_width/2)
    Select bits_pixel
      Case 1                              ; you add the variants yourself
      Case 2
      Case 4
      Case 8
      Case 24                             ; 24 bits rgb
        ;
        ; where are those darn pixels
        ;
        *p1.BYTE = *p+y*bytes_row+x*3
        *p2.BYTE = *p+y*bytes_row+(image1_width-x-1)*3
        ;
        ; now swap 'm... blue...
        ;
        b = *p1\b
        *p1\b = *p2\b
        *p2\b = b
        ;
        ; green...
        ;
        g = *p1\b
        *p1\b = *p2\b
        *p2\b = g
        ;
        ; and red...
        ;
        r = *p1\b
        *p1\b = *p2\b
        *p2\b = r
        ;
    EndSelect
  Next x
Next y
;
; and show it one more time
;
image1_h = CatchImage(2,?image1_data)
StartDrawing(WindowOutput())
DrawImage(image1_h,170,10)
StopDrawing()
;
Repeat
  event = WaitWindowEvent()
Until event = #PB_Event_CloseWindow Or event = 513
;
; store the image in a datasection
;
DataSection
;
image1_data:
IncludeBinary("graphics\lightcircle.bmp")
image1_data_end:
;
EndDataSection
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
HienTau
User
User
Posts: 16
Joined: Thu Feb 24, 2005 9:31 am
Location: Bremen, Germany
Contact:

Post by HienTau »

Good job blueznl 8), even though I can't use the code. :cry: If I would use BMPs instead of JPGs the game would become about 7x larger than now. Even if I mirror my JPGs I use less space. I also can't use DataSections, because this would require a rebuild as soon as one single image changes. Nevertheless your solution could well be used under certain conditions.
In this case, though, I will probably make a standard Windows- and a double sized Linux version (where mirror images are precalculated).
User avatar
blueznl
PureBasic Expert
PureBasic Expert
Posts: 6166
Joined: Sat May 17, 2003 11:31 am
Contact:

Post by blueznl »

jpeg hmm... the problem is, i do not know if the linux version stores bitmaps in memory the same way as the windows version does, you see, you could also find out where the bitmap is in memory, then change it there directory, via the handle of the bitmap

however, i am afraid that that may not work under linux (although it just might)

does anybody know?
( PB6.00 LTS Win11 x64 Asrock AB350 Pro4 Ryzen 5 3600 32GB GTX1060 6GB)
( The path to enlightenment and the PureBasic Survival Guide right here... )
Post Reply