Drawing a picture.pixel by pixel

BlitzMax Forums/BlitzMax Beginners Area/Drawing a picture.pixel by pixel

Drex(Posted 2013) [#1]
I have read the attributes of my picture into an array,

i.e X and Y coordinates,and R,G,B values.

Now I want to plot the picture pixel by pixel so that I can see
it being drawn.

Whats happening at the moment is that the whole picture gets drawn to the backbuffer,and when I flip,the picture is displayed.

Is there anyway of doing away with the backbuffer,and just being able to plot pixels on the main screen ?


GfK(Posted 2013) [#2]
Nope. Your best bet is to draw it onto a pixmap, then draw the pixmap, then Flip.

DrawPixmap is really slow for 'real-world' use, but if you're just playing with stuff it'll do what you want.


ImaginaryHuman(Posted 2013) [#3]
Otherwise you need to plot all pixels from array indexes 0 up to a counter, flip the back buffer, then increase the counter each frame.


Drex(Posted 2013) [#4]
@Imaginary Human

Yeah,I already tried that,but it draws the image way to slow.

I will have to learn about pixmaps,as I don't know nothing about them :(

I presume using pixmaps is alot quicker than the way i'm trying to do it ?


matibee(Posted 2013) [#5]
Pixmaps will be quicker than just doing Width x Height number of single pixel plots with drawrect.

With pixmaps, you can draw entire regions of them in one call using PixmapWindow (which, IIRC, doesn't create any new data, it just provides a method of addressing a specific region of an existing pixmap.)

I cobbled this together to show how it could be done using two draw calls.



I had to do away with the VSync and if you're showing raytracing results or similar, limit the screen refresh to every 50 pixels or so by adding a "Step" value to the pixel loop.


Brucey(Posted 2013) [#6]
I don't recommend using DrawPixmap(). It can be very, very slow compared to other methods of drawing the image data.


ziggy(Posted 2013) [#7]
As Brucey points out, drawing a pixmap is very slow.
Once the processing is done, remember you can do LoadImage(myPixMap) and load the pixmap to the VRam for subsequent fast draw operations.

Edit:
Graphics(800, 600)
Local pix:TPixmap = New TPixmap.Create(100, 200, PF_BGRA8888)
For Local X:Int = 0 Until 100
	For Local Y:Int = 0 Until 200
		'We write a yellow pixel:
		pix.WritePixel(X, Y, $FFFFFF00)
	Next
Next
Local myImage:TImage = LoadImage(pix)
While Not KeyHit(KEY_ESCAPE)
	Cls
	DrawImage(myImage, 10, 20)
	Flip()
WEnd
Useful example (I hope)


GfK(Posted 2013) [#8]
Ziggy - I think he wants to see the image gradually being drawn, so can't really use the LoadImage approach. This is why I suggested DrawPixmap (as slow as it is) - is it any slower than using CreateImage and Lock/UnlockImage? Haven't tried but I assumed it wouldn't be.


matibee(Posted 2013) [#9]
is it any slower than using CreateImage and Lock/UnlockImage?


Before I posted the code above I tried using CreateImage with the new pixmap every time it was updated, then using drawsubimagerect. It was noticably slower.

Perhaps a more optimal route would be to create an image for every n scan lines.

I'm actually using glDrawPixels for my raytracing results with pretty good performance.


Derron(Posted 2013) [#10]
As matibee already used "drawsubimagerect".
I am not 100% sure that the OP wants a scanline-effect. If he only wants to see the image grow, the fastest option should be DrawSubImageRect with just the height of the "window" being increased. You could create various "window" effects with this ("zoom", "grow" etc.). But as soon as the scanline effect is wanted I would go with matibee's suggestion in a variated way:
there is a pixmap (width: imagewidth, height: 1) which is filled with the current "scanline". All lines "above" are drawn using DrawSubImageRect with the original Timage-image (seems we have that already - so use it).

If the width of the image is lower than a specific amount, the WritePixel-approach for the current horizontal line could be faster than writing to a pixmap and then drawing that pixmap.... individual solution.


bye
Ron


Bremer(Posted 2013) [#11]
Something like this perhaps?

SuperStrict

Framework BRL.GLMax2D
Import BRL.Random
Import BRL.PolledInput
Import BRL.PNGLoader

SetGraphicsDriver GLMax2DDriver()

Graphics(1280,720)
SetClsColor(32,32,64)

Global image:TImage = LoadImage("dragon.png")
Global iw:Int = image.width		' width
Global ih:Int = image.height	' height
Global is:Int[iw*ih]			' shown flag array
Global ic:Int = 0				' counter
Global it:Int = iw*ih			' total number of pixels

While Not KeyHit(KEY_ESCAPE)
	logic_update()
	graphics_render()
Wend

Function logic_update()
	is[ic] = 1					' show the next pixel
	If ic < it Then ic :+ 1		' add to counter if still less than total
End Function

Function graphics_render()
	Cls
	plotImage(100,100)
	Flip False
End Function

Function plotImage(tx:Int,ty:Int)
	For Local y:Int = 0 Until ih
		For Local x:Int = 0 Until iw
			If is[y*iw+x] = 1 Then DrawSubImageRect(image,tx+x,ty+y,1,1,x,y,1,1)
		Next
	Next
End Function



Derron(Posted 2013) [#12]
@zawran:
Globals like "iw" should not be needed, "image.width" is already a variable holding an information.

"is"-array should not be needed (huge memory hogger) . Just increase "iw" and "ih" each logical step should do the same.

You calculate twice times "iw*ih" - calculate "it" first and use that on "is". So in case of some refactoring you would save one change in a later stage.


And... i doubt "drawimagesubrect" for each pixel is faster than writepixel.

bye
Ron


Drex(Posted 2013) [#13]
Will have a pick through some of your code examples,and see what I make of it.

Buy yeah,basically what I want to achieve,is being able to see the image being drawn,and then once I have achieved this,then I want to be able to do various other things with the image like :

Fill the in randomly,or build the image left to right,and other image manipulation.

I hope this gives you an idea of what i'm trying to achieve ?

At the moment, I am drawing the first pixel to the backbuffer from my array then do the flip,then increase the array by 1,do another flip and so on,but this is way to slow.

What I don't really understand is why I can read the whole contents of my array into the backbuffer and flip it,and this takes just a second ?


grable(Posted 2013) [#14]
Thats because its copying the entire thing in one go, usually i highly optimized thing.
Writing individual pixels however, requires a little math and some pointer manipulations for each pixel on both sides, plus additional overhead of function calls etc.

Doing one pixel each frame as well is rather wastefull, you need to find a good pixel-frame ratio.

I would use scanlines instead... tracking how many have been completed and doing the indexing on only the currently drawing scanline (for the per-pixel effect).
This way you can copy the image already drawn in one go.

actually, matibee's example is close to what i had in mind.


Derron(Posted 2013) [#15]
What makes it slow is the "drawOnePixel;Flip"-approach. Each Flip is a "transfer to gpu"-command. In the case of a slow computer which may only be able to do "1000 transfers a second" this means only 1000 pixels/s can be drawn. A Pixel of 500x500 will then need 250 seconds to get drawn completely.

Above code was posted which changes this... "logic_updates" etc.


For all things concerning animations with "rectangles only" you should use DrawSubImageRect (means zoom, move, fade, curtain, ...) the only ones which should need the "per pixel" approach are: noise/random fade aka "dissolve", scanline and effects there kind of "burning through" takes place.
Just start with the rectangle ones - they are easier and way faster to achieve.


bye
Ron


Scaremonger(Posted 2013) [#16]
The code below draws a picture to the screen at the speed you specify in the "SHOW" constant (currently set to 1 second).

It uses frame and update timers to prevent pixel by pixel drawing.




Scaremonger(Posted 2013) [#17]
To generate your own defdata for the above code use this:




TomToad(Posted 2013) [#18]
Here are two possibly methods for drawing pixel by pixel.

after the initial image appears, press space bar to see the image draw in using the first method. When that one is finished, press space bar again to see the second method. Both methods use Millisecs() to calculate the number of pixels needed to be drawn so they will take 10 seconds to complete regardless the speed of your system

The first one works by creating a blank TImage. The source image is in a pixmap which gets copied pixel by pixel to the TImage, then the image gets drawn all at once. The second method uses DrawSubImageRect() to draw a part of the TImage pixel by pixel. Initially, the second method draws more smoothly, but then quickly lags as more pixels need to be drawn.

Edit: The example will draw the pixels randomly, shouldn't be too difficult to modify if you want a scanline effect.



Derron(Posted 2013) [#19]
@TomToad
DrawSubImageRect for individual pixels should be slower than writepixel ("should" -> did not test it but it has kind of more overhead at all).

If GPU is the bottleneck one could "rectpack" pixelsets. So you try to get as big as possible rectangles you can draw at once. At the beginning this is up to 0% the case (but that is less pixels to draw which means less performance needed). The later you get, the more "rects" you draw. If you do not have an fast algorithm for it, store the found rectangles for later use (but you sacrifice "better" rectangles).

bye
Ron