5.10 Drawing a sprite on another sprite

Advanced game related topics
Nubcake
Enthusiast
Enthusiast
Posts: 195
Joined: Thu Feb 03, 2011 7:44 pm

5.10 Drawing a sprite on another sprite

Post by Nubcake »

How would you draw a sprite onto another sprite without using UseBuffer ? ; It never worked for me and I read somewhere Fred said it's slow and should be avoided .
Ideas ?

Nubcake
User avatar
IceSoft
Addict
Addict
Posts: 1695
Joined: Thu Jun 24, 2004 8:51 am
Location: Germany

Re: 5.10 Drawing a sprite on another sprite

Post by IceSoft »

Yes. Use the MP3D engine. But it is Windows only.
Belive! C++ version of Puzzle of Mystralia
Bug Planet
<Wrapper>4PB, PB<game>, =QONK=, PetriDish, Movie2Image, PictureManager,...
Nubcake
Enthusiast
Enthusiast
Posts: 195
Joined: Thu Feb 03, 2011 7:44 pm

Re: 5.10 Drawing a sprite on another sprite

Post by Nubcake »

IceSoft wrote:Yes. Use the MP3D engine. But it is Windows only.
Well if that's the case , how does one draw a transparent image of selected color directly to the screen ? Is that possible ?
Nubcake
Enthusiast
Enthusiast
Posts: 195
Joined: Thu Feb 03, 2011 7:44 pm

Re: 5.10 Drawing a sprite on another sprite

Post by Nubcake »

Bump I figured it out instead of using sprites I use Images the way to draw a transparent image without any API calls would to write each pixel in the image to memory then when reading check if it's the transparent colour don't plot it else etc.

There was only a few things I did not understand in this procedure which I adapted from someone's code (It's incomplete as I was testing)

Code: Select all

Procedure DisplayTransparentImage(Output,Image,x,y,Color)
  *Memory = AllocateMemory((ImageWidth(Image) * ImageHeight(Image) * 4))
  
  StartDrawing(ImageOutput(Image))
  
  For x = 0 To ImageWidth(Image) - 1
    For y = 0 To ImageHeight(Image) - 1
      PokeL(*Memory + (y*ImageWidth(Image)+x)*4,Point(x,y))
    Next
  Next
  
  StopDrawing()
  
  StartDrawing(ScreenOutput())
  
  For x = 0 To ImageWidth(Image) - 1
    For y = 0 To ImageHeight(Image) - 1
      If PeekL(*Memory + (y*ImageWidth(Image)+x)*4) <> Color 
        Plot(x,y,PeekL(*Memory + (y*ImageWidth(Image)+x)*4))
      EndIf 
    Next
  Next
  
  StopDrawing()
   
  FreeMemory(*Memory)
EndProcedure 
The significance of Allocating memory ; Why does it need to be multiplied by 4 ? I'm sure it has something to do with the fact that Poke/PeekL is being used since it writes/reads 4 bytes.

Code: Select all

*Memory = AllocateMemory((ImageWidth(Image) * ImageHeight(Image) * 4))
Also by storing the pixel I don't understand the method behind the offset either.

Code: Select all

 PokeL(*Memory + (y*ImageWidth(Image)+x)*4,Point(x,y))


Can someone explain this to me ?

Thanks
Nubcake
User avatar
Fig
Enthusiast
Enthusiast
Posts: 352
Joined: Thu Apr 30, 2009 5:23 pm
Location: Côtes d'Azur, France

Re: 5.10 Drawing a sprite on another sprite

Post by Fig »

Nubcake wrote:

Code: Select all

*Memory = AllocateMemory((ImageWidth(Image) * ImageHeight(Image) * 4))
Also by storing the pixel I don't understand the method behind the offset either.

Code: Select all

 PokeL(*Memory + (y*ImageWidth(Image)+x)*4,Point(x,y))
As you can't adress memory with x and y because it's linear, you need to convert your x,y coordonate in a single adress. This code assumes your image is 32bits format=> 4 bytes per pixel.(it explains everything)

ie, you image starts at 800 adress, your image is 1024 pixels width.
your first line will begin at 800 and end at 800+1023x4
so you can calculate the begining adress of each line:

800+1023*y*4

Then, you just have to add your x coordonate to complete the calculation:

800+(1023*y*4)+(x*4)

you can reduce:
800+(1023*y+x)*4
There are 2 methods to program bugless.
But only the third works fine.

Win10, Pb x64 5.71 LTS
Thorium
Addict
Addict
Posts: 1305
Joined: Sat Aug 15, 2009 6:59 pm

Re: 5.10 Drawing a sprite on another sprite

Post by Thorium »

Fig wrote: Then, you just have to add your x coordonate to complete the calculation:

800+(1023*y*4)+(x*4)

you can reduce:
800+(1023*y+x)*4
That calculation is wrong because it disregards the pitch.
The pitch is the actual size in byte of a image line in memory and can be bigger than width*pixelsize.

The caluclation of a pixel is more like that: baseaddress + pitch * y + pixelsize * x.
While you start at X and Y = 0.

In a loop that processes all pixel you can greatly speed it up by using pointers and increment them like that:

Code: Select all

  StartDrawing(ScreenOutput())
 
  For y = 0 To ImageHeight(Image) - 1
    *Pixel = *Memory + Pitch * y
    For x = 0 To ImageWidth(Image) - 1
      If *Pixel\l <> Color
        Plot(x, y, *Pixel\l)
      EndIf
      *Pixel = *Pixel + PixelSize
    Next
  Next
 
  StopDrawing()
As you can see we have only one addition per pixel and one multiplication per line. Which is way faster than having to multiplicate for every pixel twice.
Nubcake
Enthusiast
Enthusiast
Posts: 195
Joined: Thu Feb 03, 2011 7:44 pm

Re: 5.10 Drawing a sprite on another sprite

Post by Nubcake »

Thorium wrote: That calculation is wrong because it disregards the pitch.
The pitch is the actual size in byte of a image line in memory and can be bigger than width*pixelsize.

The caluclation of a pixel is more like that: baseaddress + pitch * y + pixelsize * x.
While you start at X and Y = 0.

In a loop that processes all pixel you can greatly speed it up by using pointers and increment them like that:

Code: Select all

  StartDrawing(ScreenOutput())
 
  For y = 0 To ImageHeight(Image) - 1
    *Pixel = *Memory + Pitch * y
    For x = 0 To ImageWidth(Image) - 1
      If *Pixel\l <> Color
        Plot(x, y, *Pixel\l)
      EndIf
      *Pixel = *Pixel + PixelSize
    Next
  Next
 
  StopDrawing()
As you can see we have only one addition per pixel and one multiplication per line. Which is way faster than having to multiplicate for every pixel twice.
So this code would be faster than the original ?
User avatar
Fig
Enthusiast
Enthusiast
Posts: 352
Joined: Thu Apr 30, 2009 5:23 pm
Location: Côtes d'Azur, France

Re: 5.10 Drawing a sprite on another sprite

Post by Fig »

The calculation is correct for CPC6128 :mrgreen: :oops:

Fred made that code...

Code: Select all

;
; ------------------------------------------------------------
;
;   PureBasic - Drawing via Direct Screen Access (DSA) 
;
;    (c) 2006 - Fantaisie Software
;
; ------------------------------------------------------------
;
; Note: disable the debugger to run at full speed !
;


#ScreenWidth  = 800  ; Feel free to change this to see the pixel filling speed !
#ScreenHeight = 600

If InitSprite() = 0 Or InitKeyboard()=0
  MessageRequester("Error","DirectX 7+ is needed.",0)
EndIf

Structure Pixel
  Pixel.l
EndStructure

Procedure.f GSin(angle.f)
  ProcedureReturn Sin(angle*(2*3.14/360))
EndProcedure

; Pre-calculated values are faster than realtime calculated ones...
; ... so we save them in an array before starting gfx operations
Dim CosTable(#ScreenWidth*2)
Dim ColorTable(255)

For i = 0 To #ScreenWidth*2
  CosTable(i) = GSin(360*i/320)* 32 + 32
Next


If OpenScreen(#ScreenWidth, #ScreenHeight, 32, "PB Plasma")

  Repeat

    Wave+6
    If Wave > 320 : Wave = 0 : EndIf
    
    If StartDrawing(ScreenOutput())
      Buffer      = DrawingBuffer()             ; Get the start address of the screen buffer
      Pitch       = DrawingBufferPitch()        ; Get the length (in byte) took by one horizontal line
      PixelFormat = DrawingBufferPixelFormat()  ; Get the pixel format. 
      
      If PixelFormat = #PB_PixelFormat_32Bits_RGB
        For i = 0 To 255
          ColorTable(i) = i << 16 ; Blue is at the 3th pixel
        Next
      Else                        ; Else it's 32bits_BGR
        For i = 0 To 255
          ColorTable(i) = i       ; Blue is at the 1th pixel
        Next    
      EndIf
    
      For y = 0 To #ScreenHeight-1
        pos1 = CosTable(y+wave)
        
        *Line.Pixel = Buffer+Pitch*y
        
        For x = 0 To #ScreenWidth-1
          pos2 = (CosTable(x+Wave) + CosTable(x+y) + pos1)
          *Line\Pixel = ColorTable(pos2) ; Write the pixel directly to the memory !
          *Line+4
        Next
      Next
      
      StopDrawing()
    EndIf
    
    ExamineKeyboard()
    
    FlipBuffers()
     
  Until KeyboardPushed(#PB_Key_Escape)

Else
  MessageRequester("Error","Can't open the screen !",0)
EndIf

End
There are 2 methods to program bugless.
But only the third works fine.

Win10, Pb x64 5.71 LTS
Lebostein
Addict
Addict
Posts: 829
Joined: Fri Jun 11, 2004 7:07 am

Re: 5.10 Drawing a sprite on another sprite

Post by Lebostein »

Use the Screen! This is the easiest, safest and fastest method.

1. ClearScreen() with your transparent sprite color
2. DisplayTransparentSprite() your 1. Sprite
3. DisplayTransparentSprite() your 2. Sprite on the first
4. GrabSprite() the new Sprite from Screen
5. ClearScreen() with your standard background color

These actions are performed on the hidden screen buffer and will not be visible.
Nubcake
Enthusiast
Enthusiast
Posts: 195
Joined: Thu Feb 03, 2011 7:44 pm

Re: 5.10 Drawing a sprite on another sprite

Post by Nubcake »

Lebostein wrote:Use the Screen! This is the easiest, safest and fastest method.

1. ClearScreen() with your transparent sprite color
2. DisplayTransparentSprite() your 1. Sprite
3. DisplayTransparentSprite() your 2. Sprite on the first
4. GrabSprite() the new Sprite from Screen
5. ClearScreen() with your standard background color

These actions are performed on the hidden screen buffer and will not be visible.
I never thought of that it sure beats using Plot/Point for each pixel which slows down the program with large images , just a question ; are these functions faster than using the 2D drawing ones ? i.e DrawImage GrabImage etc...
Post Reply