Code archives/Graphics/Palette Rotation

This code has been declared by its author to be Public Domain code.

Download source code

Palette Rotation by TomToad2016
This program will simulate the palette rotation used to animate back in 8 bit days. By creating "color planes" out of images and layering them, you can assign colors to each plane and alter them quickly. Hopefully my example code is clear. It draws a "bullseye" of concentric colored rings, then rotates the colors.

If you really want to have fun, stare at the middle of the "bullseye" for 30 seconds, then look at something like the back of your hand or someone's face.

Edit: Changed Pallet to Palette in the description. :)

Edit 2: Optimised the PlotPixel routine. Cuts plotting time by as much as 50%. Also changed all instances of Pallet to Palette. :)
SuperStrict

Graphics 800,600

'We need to create the color planes.  We will use 16 fullscreen TImages.  Note that you only need the images to
'  Be big enough to cover the area you wish to have a Palette
Global Planes:TImage[16]

'Next, we need to create a Palette.  Each color corrisponds to one of the color planes.  You can use any color you want\
'  Here, I fill it with some default values
Global Palette:Int[] = [$000000,$FFFFFF,$FF0000,$00FF00,$0000FF,$FF00FF,$FFFF00,$00FFFF, ..
						$7F7F7F,$7f0000,$007F00,$00007F,$7F007F,$7F7F00,$007F7F,$FF7F00]



'This is a more optimized PlotPixel than before.  It will queue all the changes, but only commit the changes when rendering to the screen
'   for the first time
Type TPixel 'a pixel type
	Field x:Int, y:Int
	
	Function Create:TPixel(x:Int,y:Int)
		Local Pixel:TPixel = New TPixel
		Pixel.x = x
		Pixel.y = y
		Return Pixel
	End Function
End Type

Type TQueue 'Creating a type for the queued pixels
	Field Dirty:Int = False 'A flag that is set when pixels are queued
	
	Field Pixels:Byte[800,600] 'An array to hold the indexes for the Planes pixel queue.  To make searching the queue faster.
	Field Pixmaps:TPixmap[16] 'All rendering will be done to pixmaps then transfered to the Planes
	Field Queue:TList[16] 'An array of lists to hold the queued pixels
	Field ClearQueue:TList = CreateList() 'Pixels to be cleared in each plane
	
	Method New() 'Initialize the values
		For Local i:Int = 0 To 15
			Queue[i] = CreateList() 'Initialize the queue
			Pixmaps[i] = CreatePixmap(800,600,PF_RGBA8888) 'Create the pixmap
			If i = 0 'Pixmap 0 will be all set, the rest clear
				ClearPixels(Pixmaps[0],$FFFFFFFF)
			Else
				ClearPixels(Pixmaps[i],$00FFFFFF)
			End If
			Planes[i] = LoadImage(Pixmaps[i]) 'Transfer the pixmap to the Plane
		Next
		For Local y:Int = 0 To 599
			For Local x:Int = 0 To 799
				Pixels[x,y] = 16 'Using 16 to signify that this pixel hasn't been queued anywhere
			Next
		Next
	End Method
	
	Method QueuePixel(x:Int, y:Int, Plane:Int) 'queues the pixel for writing
		Local Queued:Int = Pixels[x,y] 'Which plane is this pixel queued in?
		If Plane = Queued Then Return 'This pixel is already queued.  No need to do it twice
		
		'We must first remove the queued pixel before changing planes
		If Queued < 16 'If 16, then pixel is not queued and does not need to be removed
			For Local Pixel:TPixel = EachIn Queue[Queued]
				If Pixel.x = x And Pixel.y = y 'We have found our queued pixel
					Queue[Queued].Remove(Pixel) 'Remove this pixel from the queue
					Exit 'exit the loop
				End If
			Next
		End If
		
		'Now we must queue the pixel into the new plane
		Pixels[x,y] = Plane
		Queue[Plane].AddLast(TPixel.Create(x,y))
		
		'last, we need to mark the queue dirty so the renderer knows it needs to commit
		Dirty = True
	End Method
	
	'Here we commit the changes made and clear the queue
	Method Commit()
		If Not Dirty Then Return 'no need to commit if no changes are made
		
		For Local i:Int = 0 To 15 'Go through each plane's queue
			For Local Pixel:TPixel = EachIn ClearQueue 'First we will set alpha to 0 on pixels that should be clear
				Pixmaps[i].pixels[Pixel.y*Pixmaps[i].pitch+Pixel.x*4+3] = 0 'A little bit of math for faster access to the pixmap's pixels
			Next
			For Local Pixel:TPixel = EachIn Queue[i] 'now we will write the pixels in the queue
				Pixmaps[i].pixels[Pixel.y*pixmaps[i].pitch+Pixel.x*4+3] = $ff 'same math as before :)
				ClearQueue.AddLast(Pixel) 'Add this pixel to the Clear queue so all planes above will clear this pixel
				Pixels[Pixel.x,Pixel.y] = 16 'this pixel no longer needs to be indexed
			Next
			Queue[i].Clear() 'Clear this plane's queue
			Planes[i] = LoadImage(Pixmaps[i]) 'transfer the pixmap to the image
		Next
		
		ClearQueue.Clear() 'Clear the clear queue.
		Dirty = False 'now everything is reset to be filled again :)
	End Method
End Type
Global Queue:TQueue = New TQueue 'now to create the queue
		
		
' Place a pixel into the queue
Function PlotPixel(x:Int,y:Int,Plane:Int)
	If x > 799 Or x < 0 Or y > 599 Or y < 0 Or plane > 15 Or plane < 0 Then Return 'Pixel out of range
	Queue.QueuePixel(x,y,Plane)
End Function

'Here we render the planes.  We grab the color from the Palette and seperate the components for SetColor.  Then draw the plane to the screen
Function RenderScreen()
	Queue.Commit() 'commit any changes to the planes
	For Local i:Int = 0 To 15
		Local Red:Int = (Palette[i] & $FF0000) Shr 16
		Local Green:Int = (Palette[i] & $FF00) Shr 8
		Local Blue:Int = Palette[i] & $FF
		
		SetColor Red,Green,Blue
		DrawImage Planes[i],0,0
	Next
End Function

'A little message to display to let people know something is going on.
Cls
DrawText "Please wait, setting up graphics",10,10
Flip
'We shall plot some concentric rings. Distance is modded to 16 to corrispond to one of the 16 planes
For Local y:Int = 0 To 599
	For Local x:Int = 0 To 799
		Local Distance:Int = Sqr((x-400)^2+(y-300)^2)/5
		PlotPixel(x,y,Distance Mod 16)
	Next
Next


While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
	RenderScreen 'Here we render the color planes, no need for Cls as the planes will cover the entire screen
	Flip 'Then flip, If you get a lot of tearing on the screen, change this to Flip 1
'Rem	
	'Here we rotate the Palette entries by one. :-)
	Local Temp:Int = Palette[0]
	For Local i:Int = 0 To 14
		Palette[i] = Palette[i+1]
	Next
	Palette[15] = temp	
'End Rem

Rem 'rem out the previous lines and remove the rem/endrem here to see Palette being manipulated in a different way
	For Local i:Int = 0 To 15
		Palette[i] = (Palette[i] + 1) &$FFFFFF
	Next
End Rem	
Wend

Comments

dw8172016
Good Old School graphics ! You even have the palettes right, I recognize that color set. :)

There MIGHT be a faster way to do this. How about using drawing your circles with DrawOval() and using GrabImage() ?


TomToad2016
I'm sure there are faster ways to plot the pixels, but it is not as easy as just using DrawOval. In order for this to work, for every pixel set on a plane, the alpha must be 0 on the corresponding pixel on every other plane. I guess you can create each plane in a paint program and use Loadimage to load them in.


skidracer2016
TomToad: from your spelling I was a bit confused. I was expecting code using shipping pallets not colour palettes.


TomToad2016
@simonarmstrong: Fixed in my update :)


Code Archives Forum