Page 1 of 1

5.10 Drawing a sprite on another sprite

Posted: Mon Feb 18, 2013 9:00 pm
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

Re: 5.10 Drawing a sprite on another sprite

Posted: Mon Feb 18, 2013 9:49 pm
by IceSoft
Yes. Use the MP3D engine. But it is Windows only.

Re: 5.10 Drawing a sprite on another sprite

Posted: Mon Feb 18, 2013 11:02 pm
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 ?

Re: 5.10 Drawing a sprite on another sprite

Posted: Thu Feb 21, 2013 12:28 am
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

Re: 5.10 Drawing a sprite on another sprite

Posted: Thu Feb 21, 2013 11:30 am
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

Re: 5.10 Drawing a sprite on another sprite

Posted: Fri Feb 22, 2013 8:17 pm
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.

Re: 5.10 Drawing a sprite on another sprite

Posted: Fri Feb 22, 2013 10:32 pm
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 ?

Re: 5.10 Drawing a sprite on another sprite

Posted: Sat Feb 23, 2013 11:03 am
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

Re: 5.10 Drawing a sprite on another sprite

Posted: Tue Feb 26, 2013 11:05 am
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.

Re: 5.10 Drawing a sprite on another sprite

Posted: Thu Feb 28, 2013 10:30 pm
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...