GPSET is slow. I don't know what the solution for that is, but there is a better way to write images to the screen.
As for displaying the full image quickly, use GPAGE (/and/or GCOPY). Do all the drawing hidden, then switch the visible graphic page. I wish I could explain it better but that's all I know.
Efficient way of drawing an image to the screen?
Root / Programming Questions / [.]
amihartCreated:
I need to draw an image to the screen for an RPG I'm working on. The issue is, I have no clue how to do this efficiently. The image I am currently trying to draw to the screen is a PPM image file I simply transferred over to my 3DS using PetitModem.
I wrote some code that can do it, but there's two issues with it. (1) It requires time for the image to load, and (2) it doesn't draw it instantly. Both of these are not too big of deal on the n3DS, but on the old 3DS it draws really slowly and the loading time is pretty long. (12 seconds for one 400x240 image! As opposed to 3.5 seconds on the n3DS.)
This is currently how it runs on the n3DS:
It runs fast enough on the n3DS for the purpose I need it for, but nowhere near fast enough on the old 3DS. There's a 12 second loading time then the image draws onto the screen significantly slower.
This is the code I'm currently using:
It works by first I have the "@LOADPPM" subroutine that loads the PPM file into an RGB array. PPM is an image format like JPEG and PNG but a lot simpler. It reads the file byte-by-byte and builds up an array containing simply the RGB data, which "@DRAWIMAGE" can then draw to the screen.
As you can see, my code works, but is inefficient and I'm wondering if anyone has done something similar to this but came up with a much more efficient method.
Spoiler
GCLS CLS 'LOAD IMAGE PRINT "Loading..." DIM OUTDATA[400*240] FNAME$="DAT:PIC1" GOSUB @LOADPPM 'DRAW IMAGE CLS GOSUB @DRAWIMAGE 'WAIT FOR USER TO PRESS A WHILE BUTTON()!=#A:WEND GCLS END 'IN: FNAME$ 'OUT: OUTDATA[] @LOADPPM 'SIZE OF ANY GIVEN PPM OF 400x240 IN BYTES SIZE=288015 'LOAD IMAGE INTO INTEGER ARRAY 'EACH INTEGER CONTAINS 4 BYTES DIM INDATA[SIZE/4] LOAD FNAME$,INDATA,FALSE 'TEMPORARY STORAGE FOR INDIVIDUAL BYTES DIM TMPDATA[288015] 'BREAK THE INTEGER ARRAY UP INTO ITS RESPECTIVE BYTES FOR I=0 TO (SIZE/4)-1 'MOD IS NOT USED HERE ON PURPOSE! X0=INDATA[I] X1=X0 X1=((X1/256)-FLOOR(X1/256))*256 X2=((X0-X1)/POW(2,8)) X2=((X2/256)-FLOOR(X2/256))*256 X3=((X0-X1-X2*POW(2,8))/POW(2,16)) X3=((X3/256)-FLOOR(X3/256))*256 X4=((X0-X1-X2*POW(2,8)-X3*POW(2,16))/POW(2,24)) X4=((X4/256)-FLOOR(X4/256))*256 TMPDATA[I*4]=X1 TMPDATA[I*4+1]=X2 TMPDATA[I*4+2]=X3 TMPDATA[I*4+3]=X4 NEXT 'CALCULATE WHEN DATA SECTION STARTS NLC=0 START=0 WHILE NLC<3 IF TMPDATA[START]==10 THEN NLC=NLC+1 START=START+1 WEND 'COPY ALL RGB DATA FROM DATA SECTION INTO OUTDATA ARRAY FOR I=0 TO LEN(OUTDATA)-1 R=TMPDATA[START+I*3] G=TMPDATA[START+I*3+1] B=TMPDATA[START+I*3+2] OUTDATA[I]=RGB(R,G,B) NEXT RETURN 'IN: OUTDATA[SIZE] 'OUT: DRAWS IMAGE @DRAWIMAGE X=0 Y=0 FOR I=0 TO LEN(OUTDATA)-1 GCOLOR OUTDATA[I] GPSET X,Y X=X+1 IF X>=400 THEN X=0 Y=Y+1 ENDIF NEXT RETURN
GPSET is slow. I don't know what the solution for that is, but there is a better way to write images to the screen. As for displaying the full image quickly, use GPAGE (/and/or GCOPY). Do all the drawing hidden, then switch the visible graphic page. I wish I could explain it better but that's all I know.Ah, okay. That solves the issue with it drawing the image slowly, thanks. But now I just need to figure out how to make these loading times more efficient, or else old 3DS users are going to have to deal with really long loading times. .-.
Yeah you definitely shouldn't be putting pixels on the screen individually! You'll want to look into background-based commands. Super fast.
http://smilebasic.com/en/reference/#bg
I think you can load a GRP to GRP pages 0 through 3 and it will just display directly, which would happen instantly. But I haven't messed with that as much, and it's not very flexible, just *bam* there's a full screen image.
A more traditional way is to load your graphics in GRP format to GRP5, which by default is the page the BG commands draw from. Then you can draw the screen tile by tile to one of the background layers using BGPUT. With this you would be able to compress the amount of graphics you use if some tiles are repeated, large areas of solid color, etc. This is typically how 2D top down/sidescrollers build their maps, tile by tile, many of them repeated. And this is a very fast process even done tile by tile, even on an old 3DS.
You could even get the screen all set up the way you like it, BGSAVE it to an array, and then BGLOAD that array anytime.
As for getting your graphics into GRP format, you can either draw it and save it within the Smile Tool, or use transfer methods to go between PC and SB. One of the users here posted a PC GRP editor that you could use: https://smilebasicsource.com/page?pid=69
Can you explain the PPM format? Is it a plain bitmap or are the channel values stored in a sequence (first R, then G, then B)?
The command you should use is GLOAD. What it does is takes an array of pixel values and writes it to the GRP layer. Of course it will still take time to unpack the PPM and turn it into image data, but it should be faster and you can write the whole image at once. Check the help for more deets and ask me anything you need to know.
Hope I could help!
Yeah you definitely shouldn't be putting pixels on the screen individually! You'll want to look into background-based commands. Super fast. http://smilebasic.com/en/reference/#bg I think you can load a GRP to GRP pages 0 through 3 and it will just display directly, which would happen instantly. But I haven't messed with that as much, and it's not very flexible, just *bam* there's a full screen image. A more traditional way is to load your graphics in GRP format to GRP5, which by default is the page the BG commands draw from. Then you can draw the screen tile by tile to one of the background layers using BGPUT. With this you would be able to compress the amount of graphics you use if some tiles are repeated, large areas of solid color, etc. This is typically how 2D top down/sidescrollers build their maps, tile by tile, many of them repeated. And this is a very fast process even done tile by tile, even on an old 3DS. You could even get the screen all set up the way you like it, BGSAVE it to an array, and then BGLOAD that array anytime. As for getting your graphics into GRP format, you can either draw it and save it within the Smile Tool, or use transfer methods to go between PC and SB. One of the users here posted a PC GRP editor that you could use: https://smilebasicsource.com/page?pid=69Ah, okay, thanks, this makes a lot of sense. I don't know why I was unpacking the entire PPM then displaying it on the screen at runtime. It makes a lot more sense to unpack it before hand and save it as something like a GRP.
Can you explain the PPM format? Is it a plain bitmap or are the channel values stored in a sequence (first R, then G, then B)? The command you should use is GLOAD. What it does is takes an array of pixel values and writes it to the GRP layer. Of course it will still take time to unpack the PPM and turn it into image data, but it should be faster and you can write the whole image at once. Check the help for more deets and ask me anything you need to know. Hope I could help!This seems to actually be what I need, thanks! It makes a lot more sense to unpack my PPM beforehand and save an array with GSAVE and then simply load that array at runtime. If you still want to know what the PPM format is, it's basically this: The PPM format looks like this:
- Type
- Width Height
- Maximum color value
- Data Section
I think I've got it figured out guys. The image is loading nearly instantly and drawing instantly. Thanks!How fast does it load now? (Another GIF ples?)
The image first loads with LOAD, which takes a fraction of the a second, and then I store it into an array to draw with GLOAD later. GLOAD loads images fast enough that you can actually draw them every frame without flickering.I think I've got it figured out guys. The image is loading nearly instantly and drawing instantly. Thanks!How fast does it load now? (Another GIF ples?)
Cool!The image first loads with LOAD, which takes a fraction of the a second, and then I store it into an array to draw with GLOAD later. GLOAD loads images fast enough that you can actually draw them every frame without flickering.I think I've got it figured out guys. The image is loading nearly instantly and drawing instantly. Thanks!How fast does it load now? (Another GIF ples?)