LoginLogin
Nintendo shutting down 3DS + Wii U online services, see our post

Storing data into a graphics page

Root / Submissions / [.]

SimeonCreated:
Have you ever considered saving data into a pixel's color, but never understood what magical compression operations go on in the background? Every pixel takes up 16 bits of storage, and this code lets you take advantage of that space.
'STORE A NUMBER FROM 0 TO 65535 (16 BITS) TO A PIXEL
DEF STORE X,Y,N
 DIM PIXEL%[1]
 PIXEL%[0]=N
 GLOAD X,Y,1,1,PIXEL%,TRUE,TRUE
END

'RETRIEVE A NUMBER FROM A PIXEL
DEF UNSTORE(X,Y)
 DIM PIXEL%[1]
 GSAVE X,Y,1,1,PIXEL%,TRUE
 RETURN PIXEL%[0]
END
Now, you could stop here, but we want to be able to make use of every single bit, allow larger numbers, and not have to worry about which pixel contains what data. Using these two functions, we can construct a data structure. Storing pixel to pixel, we will start from the bottom left hand corner of the graphics page, and work our way left to right, and then bottom to the top. So our goal here is to have all the pixels work together to let you store any bit amount without worrying about the 0 TO 65535 (16 BIT) limitation. Let's contruct a stack data structure:
'GLOBAL VARIABLES
VAR DATAX,DATAY=511,DATAP
DATAX is the x position of the current pixel DATAY is the y position of the current pixel DATAP is the bit position inside that current pixel
'PUSH SOME BITS ONTO THE GRAPHICS SCREEN
DEF PUSHDATA N,BITS
 VAR I,D%
 FOR I=1 TO BITS
  D%=UNSTORE(DATAX,DATAY)
  STORE DATAX,DATAY,D%*2+(N-2*FLOOR(N/2))
  DATAP=DATAP+1
  IF DATAP>15 THEN
   DATAP=0
   DATAX=DATAX+1
   IF DATAX>511 THEN DATAX=0 DATAY=DATAY-1
  ENDIF
  N=N/2
 NEXT
END

'POP SOME BITS OFF THE GRAPHICS SCREEN
DEF POPDATA(BITS)
 VAR I,D%,N
 FOR I=1 TO BITS
  DATAP=DATAP-1
  IF DATAP<0 THEN
   DATAP=15
   DATAX=DATAX-1
   IF DATAX<0 THEN DATAX=511 DATAY=DATAY+1
  ENDIF
  D%=UNSTORE(DATAX,DATAY)
  N=N*2+(D%-2*FLOOR(D%/2))
  STORE DATAX,DATAY,D%/2
 NEXT
 RETURN N
END
So now we have a fully functional stack that is stored inside the graphics page, and can be loaded and saved along with a sprite page Here is some test code
PUSHDATA 2147483648,31
PUSHDATA 2147483647,31
PUSHDATA 1,1
PUSHDATA 255,8
PUSHDATA 15,4

?POPDATA(4) 'BYTE
?POPDATA(8) 'CHARACTER
?POPDATA(1) 'BOOLEAN
?POPDATA(31) 'INTEGER
?POPDATA(31) 'OVERFLOWED INTEGER
The output should be 15, 255, 1, 2147483647, 0 Overflows work the exact same as they do in other programming languages Keep in mind that if you are going to save and load this graphics page, you will also need to save the ending DATAX, DATAY, and DATAP variables, then load them along with the graphics page in order to ensure that the state of the stack remains the same. This is the algorithm used in Fractal Canvas to save each design along with directions on how to navigate to that design (screenshot)

I hope you enjoyed this tutorial!

Thanks to 12Me21 for improving the algorithm.

I recommend using >> and << instead of * and /, and writing some of the numbers in hexadecimal so they're more readable.

I suggest that the method store and unstore should use an array and gload instead of gpset and gsave because graphic functions are slow. Here is a performance comparison between those methods.

Replying to:12Me21
I recommend using >> and << instead of * and /, and writing some of the numbers in hexadecimal so they're more readable.
Hexadecimal, i'll do that. But >> and << have overflow protectors which I was trying to avoid for this

Replying to:raimondz
I suggest that the method store and unstore should use an array and gload instead of gpset and gsave because graphic functions are slow. Here is a performance comparison between those methods.
That's when drawing graphics in bulk, like filling a whole graphics page. I updated Fractal Canvas to do this because it was a big performance improvement, but it would not help in this situation because it only draws one or two pixels per function call. Thanks for the suggestion!

Replying to:12Me21
I recommend using >> and << instead of * and /, and writing some of the numbers in hexadecimal so they're more readable.
Overflow protectors?

Replying to:12Me21
I recommend using >> and << instead of * and /, and writing some of the numbers in hexadecimal so they're more readable.
Yeah they give you an overflow error when the number is too big Same with MOD so I avoided that too

Replying to:12Me21
I recommend using >> and << instead of * and /, and writing some of the numbers in hexadecimal so they're more readable.
AND does the same thing, so it's not possible for >> and << to cause problems here. And you should store integers in integer variables anyway.

It might be faster to do something like:
DEF STORE X,Y,N
 DIM PIXEL%[1]
 PIXEL%[0]=N
 GLOAD X,Y,1,1,PIXEL%,TRUE,TRUE
END

DEF UNSTORE(X,Y)
 DIM PIXEL%[1]
 GSAVE X,Y,1,1,PIXEL%,TRUE
 RETURN PIXEL%[0]
END
We determined that it was faster for doing many values at once, but I'm not sure about single numbers.

Replying to:12Me21
It might be faster to do something like:
DEF STORE X,Y,N
 DIM PIXEL%[1]
 PIXEL%[0]=N
 GLOAD X,Y,1,1,PIXEL%,TRUE,TRUE
END

DEF UNSTORE(X,Y)
 DIM PIXEL%[1]
 GSAVE X,Y,1,1,PIXEL%,TRUE
 RETURN PIXEL%[0]
END
We determined that it was faster for doing many values at once, but I'm not sure about single numbers.
Oh my gosh you're right Add RETURN PIXEL%[0] to UNSTORE, and this works much better And it stores numbers up to 65535 (16 bits) Wow thank you, ill see if I can implement this!

Replying to:12Me21
It might be faster to do something like:
DEF STORE X,Y,N
 DIM PIXEL%[1]
 PIXEL%[0]=N
 GLOAD X,Y,1,1,PIXEL%,TRUE,TRUE
END

DEF UNSTORE(X,Y)
 DIM PIXEL%[1]
 GSAVE X,Y,1,1,PIXEL%,TRUE
 RETURN PIXEL%[0]
END
We determined that it was faster for doing many values at once, but I'm not sure about single numbers.
I'm certain there's a better algorithm for PUSHDATA and POPDATA, to update more than one bit at a time. But as of right now this seems to be a really fast algorithm, and I'll figure that part out in the future. Thanks so much for the help 12Me21!