Simple example of tile based graphics and scaler

Post your FreeBASIC source, examples, tips and tricks here. Please don’t post code without including an explanation.
Post Reply
sero
Posts: 59
Joined: Mar 06, 2018 13:26
Location: USA

Simple example of tile based graphics and scaler

Post by sero »

The purpose of this example is to demonstrate tile based graphics with unfiltered upscale to larger resolutions. Unfiltered upscale means that larger resolutions preserve the pixelated appearance of the source image buffer workspace. The source image buffer that is our workspace in this example is 320x180 which comfortably scale to larger resolutions like 1280x720 and 1920x1080. The scaling portion relies on previous work provided by D.J. Peters.

The included example is set for 10,000 put commands of 16x16 tiles using the more intense parameters like add and xor. Add and xor could be substituted by trans or pset for more realistic usage and faster render times. This code is compatible for 32 and 64 bit. As a heads up, 32 bit runs approximately twice as fast on my machine.

Code: Select all

' This demonstration sets in place a small image buffer
' that becomes our workspace. That workspace then gets
' scaled to the screen.
' 
' Thanks to D.J. Peters for providing the scale routine
' 
' #####################################################
' 
' These dimensions are for the original buffer before
' it gets scaled to the screen
const screen_source_width = 320
const screen_source_height = 180

' Change this multiplier to scale the screen size
'  Some example are :
' 1 = 320,180    4 = 1280,720
' 2 = 640,360    6 = 1920,1080
' 3 = 960,540    8 = 2560,1440
dim as integer screen_multiplier = 2

' Here the dimensions for the scaling are determined
dim as integer screen_width = screen_source_width * screen_multiplier
dim as integer screen_height = screen_source_height  * screen_multiplier

' Initialize the screen to match the multiplier
screenres screen_width, screen_height, 32

' Create our image buffer that we work directly in
' before it gets scaled to the screen
dim screen_source as any ptr = imagecreate( screen_source_width, screen_source_height, 0, 32 )


' Set our workspace image buffer to black
line screen_source, ( 0, 0 )-( screen_source_width - 1, screen_source_height - 1 ), rgb( 0, 0, 0), bf

' Create some 16x16 buffers as part of our example
dim ex_buffer_XOR as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_RED as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_GRN as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_BLU as any ptr = imagecreate( 16, 16, 0, 32 )

' Fill our example buffers to different colors
dim as ubyte ex_color_value = 11
line ex_buffer_XOR, ( 0, 0 )-( 15, 15 ), rgb( ex_color_value, ex_color_value, ex_color_value ), bf
line ex_buffer_RED, ( 0, 0 )-( 15, 15 ), rgb( ex_color_value, 0, 0 ), bf
line ex_buffer_GRN, ( 0, 0 )-( 15, 15 ), rgb( 0, ex_color_value, 0 ), bf
line ex_buffer_BLU, ( 0, 0 )-( 15, 15 ), rgb( 0, 0, ex_color_value ), bf

' Set some conditions for our example
dim as integer ex_counter
dim as integer ex_x_range = 319 - 16
dim as integer ex_y_range = 179 - 16

' Scale routine stuff
dim as integer scale_counter_x, scale_counter_y
dim as integer scale_step = ( 1 / screen_multiplier ) * 65536
dim as integer scale_x_step = 0
dim as integer scale_y_step = 0
dim as ulong ptr scale_source_ptr = cptr( ulong ptr, screen_source ) + 8
dim as ulong ptr scale_source_temp_ptr = scale_source_ptr
dim as ulong ptr screen_ptr = screenptr()

dim as double timer_start, timer_end

randomize, 3
timer_start = timer
screenlock
  ' Render our example of 10,000 put commands using
  ' 16x16 tiles by xor and add parameters
	for ex_counter = 0 to 2500
		put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_XOR, xor
		put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_RED, add
		put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_GRN, add
		put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_BLU, add
	next ex_counter
  
  ' Scale our workspace buffer directly to the screen
	select case screen_multiplier
	case is = 1
		put ( 0, 0 ), screen_source
	case is > 1
		for scale_counter_y = 0 to ( screen_height - 1 )
			scale_source_temp_ptr = scale_source_ptr + ( scale_y_step shr 16 ) * screen_source_width
			for scale_counter_x = 0 to ( screen_width - 1 )
				*screen_ptr = scale_source_temp_ptr[ scale_x_step shr 16 ]
				screen_ptr += 1
				scale_x_step += scale_step
			next scale_counter_x
			scale_y_step += scale_step
			scale_x_step = 0
		next scale_counter_y
	case is < 1
		line ( 0, 0 )-( screen_width - 1, screen_height - 1 ), rgb( 255, 0, 255 ), bf
	end select
screenunlock
timer_end = timer

print "render time : " & ( timer_end - timer_start) & " milliseconds"
print " resolution : " & screen_width & "," & screen_height 
print " multiplier : x" & screen_multiplier
sleep

imagedestroy screen_source
imagedestroy ex_buffer_XOR
imagedestroy ex_buffer_RED
imagedestroy ex_buffer_GRN
imagedestroy ex_buffer_BLU
Below is an example modified to only use pset instead of the more processor heavy xor and add parameters. Draw string places "Hello World" in outline so that the effect of scaling can be seen more easily.

Code: Select all

' This demonstration sets in place a small image buffer
' that becomes our workspace. That workspace then gets
' scaled to the screen.
' 
' Thanks to D.J. Peters for providing the scale routine
' 
' #####################################################
' 
' These dimensions are for the original buffer before
' it gets scaled to the screen
const screen_source_width = 320
const screen_source_height = 180

' Change this multiplier to scale the screen size
'  Some example are :
' 1 = 320,180    4 = 1280,720
' 2 = 640,360    6 = 1920,1080
' 3 = 960,540    8 = 2560,1440
dim as integer screen_multiplier = 2

' Here the dimensions for the scaling are determined
dim as integer screen_width = screen_source_width * screen_multiplier
dim as integer screen_height = screen_source_height  * screen_multiplier

' Initialize the screen to match the multiplier
screenres screen_width, screen_height, 32

' Create our image buffer that we work directly in
' before it gets scaled to the screen
dim screen_source as any ptr = imagecreate( screen_source_width, screen_source_height, 0, 32 )


' Set our workspace image buffer to black
line screen_source, ( 0, 0 )-( screen_source_width - 1, screen_source_height - 1 ), rgb( 0, 0, 0), bf

' Create some 16x16 buffers as part of our example
dim ex_buffer_WHT as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_RED as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_GRN as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_BLU as any ptr = imagecreate( 16, 16, 0, 32 )

' Fill our example buffers to different colors
dim as ubyte ex_color_value = 255
line ex_buffer_WHT, ( 0, 0 )-( 15, 15 ), rgb( ex_color_value, ex_color_value, ex_color_value ), bf
line ex_buffer_RED, ( 0, 0 )-( 15, 15 ), rgb( ex_color_value, 0, 0 ), bf
line ex_buffer_GRN, ( 0, 0 )-( 15, 15 ), rgb( 0, ex_color_value, 0 ), bf
line ex_buffer_BLU, ( 0, 0 )-( 15, 15 ), rgb( 0, 0, ex_color_value ), bf

' Set some conditions for our example
dim as integer ex_counter
dim as integer ex_x_range = 319 - 16
dim as integer ex_y_range = 179 - 16

' Scale routine stuff
dim as integer scale_counter_x, scale_counter_y
dim as integer scale_step = ( 1 / screen_multiplier ) * 65536
dim as integer scale_x_step = 0
dim as integer scale_y_step = 0
dim as ulong ptr scale_source_ptr = cptr( ulong ptr, screen_source ) + 8
dim as ulong ptr scale_source_temp_ptr = scale_source_ptr
dim as ulong ptr screen_ptr = screenptr()

dim as double timer_start, timer_end

randomize, 3
timer_start = timer
screenlock
  ' Render our example placing 4 16x16 tiles using pset
  ' and draw string
  for ex_counter = 0 to 2500
		put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_WHT, pset
		put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_RED, pset
		put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_GRN, pset
		put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_BLU, pset
  next ex_counter
  draw string screen_source, ( 115, 85 ), "Hello World", rgb( 0, 0, 0 )
  draw string screen_source, ( 116, 85 ), "Hello World", rgb( 0, 0, 0 )
  draw string screen_source, ( 117, 85 ), "Hello World", rgb( 0, 0, 0 )
  draw string screen_source, ( 115, 86 ), "Hello World", rgb( 0, 0, 0 )
  draw string screen_source, ( 117, 86 ), "Hello World", rgb( 0, 0, 0 )
  draw string screen_source, ( 115, 87 ), "Hello World", rgb( 0, 0, 0 )
  draw string screen_source, ( 116, 87 ), "Hello World", rgb( 0, 0, 0 )
  draw string screen_source, ( 117, 87 ), "Hello World", rgb( 0, 0, 0 )
  draw string screen_source, ( 116, 86 ), "Hello World", rgb( 255, 127, 0 )
  
  ' Scale our workspace buffer directly to the screen
	select case screen_multiplier
	case is = 1
		put ( 0, 0 ), screen_source
	case is > 1
		for scale_counter_y = 0 to ( screen_height - 1 )
			scale_source_temp_ptr = scale_source_ptr + ( scale_y_step shr 16 ) * screen_source_width
			for scale_counter_x = 0 to ( screen_width - 1 )
				*screen_ptr = scale_source_temp_ptr[ scale_x_step shr 16 ]
				screen_ptr += 1
				scale_x_step += scale_step
			next scale_counter_x
			scale_y_step += scale_step
			scale_x_step = 0
		next scale_counter_y
	case is < 1
		line ( 0, 0 )-( screen_width - 1, screen_height - 1 ), rgb( 255, 0, 255 ), bf
	end select
screenunlock
timer_end = timer

print "render time : " & ( timer_end - timer_start) & " milliseconds"
print " resolution : " & screen_width & "," & screen_height 
print " multiplier : x" & screen_multiplier
sleep

imagedestroy screen_source
imagedestroy ex_buffer_WHT
imagedestroy ex_buffer_RED
imagedestroy ex_buffer_GRN
imagedestroy ex_buffer_BLU
Below is an example using trans.

Code: Select all

' This demonstration sets in place a small image buffer
' that becomes our workspace. That workspace then gets
' scaled to the screen.
' 
' Thanks to D.J. Peters for providing the scale routine
' 
' #####################################################
' 
' These dimensions are for the original buffer before
' it gets scaled to the screen
const screen_source_width = 320
const screen_source_height = 180

' Change this multiplier to scale the screen size
'  Some example are :
' 1 = 320,180    4 = 1280,720
' 2 = 640,360    6 = 1920,1080
' 3 = 960,540    8 = 2560,1440
dim as integer screen_multiplier = 2

' Here the dimensions for the scaling are determined
dim as integer screen_width = screen_source_width * screen_multiplier
dim as integer screen_height = screen_source_height  * screen_multiplier

' Initialize the screen to match the multiplier
screenres screen_width, screen_height, 32

' Create our image buffer that we work directly in
' before it gets scaled to the screen
dim screen_source as any ptr = imagecreate( screen_source_width, screen_source_height, 0, 32 )


' Set our workspace image buffer to black
line screen_source, ( 0, 0 )-( screen_source_width - 1, screen_source_height - 1 ), rgb( 0, 0, 0), bf

' Create some 16x16 buffers as part of our example
dim ex_buffer_TRN as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_RED as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_GRN as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_BLU as any ptr = imagecreate( 16, 16, 0, 32 )

' Fill our example buffers with colored circles on top
' of a transparent background
line ex_buffer_TRN, ( 0, 0 )-( 15, 15 ), rgb( 255, 0, 255 ), bf
circle ex_buffer_TRN, (8, 8), 7, RGB( 255, 255, 255 ), , , 1, f
line ex_buffer_RED, ( 0, 0 )-( 15, 15 ), rgb( 255, 0, 255 ), bf
circle ex_buffer_RED, (8, 8), 7, RGB( 255, 0, 0 ), , , 1, f
line ex_buffer_GRN, ( 0, 0 )-( 15, 15 ), rgb( 255, 0, 255 ), bf
circle ex_buffer_GRN, (8, 8), 7, RGB( 0, 255, 0 ), , , 1, f
line ex_buffer_BLU, ( 0, 0 )-( 15, 15 ), rgb( 255, 0, 255 ), bf
circle ex_buffer_BLU, (8, 8), 7, RGB( 0, 0, 255 ), , , 1, f

' Set some conditions for our example
dim as integer ex_counter
dim as integer ex_x_range = 319 - 16
dim as integer ex_y_range = 179 - 16

' Scale routine stuff
dim as integer scale_counter_x, scale_counter_y
dim as integer scale_step = ( 1 / screen_multiplier ) * 65536
dim as integer scale_x_step = 0
dim as integer scale_y_step = 0
dim as ulong ptr scale_source_ptr = cptr( ulong ptr, screen_source ) + 8
dim as ulong ptr scale_source_temp_ptr = scale_source_ptr
dim as ulong ptr screen_ptr = screenptr()

dim as double timer_start, timer_end

randomize, 3
timer_start = timer
screenlock
  ' Render our example placing 4 16x16 tiles using pset
  ' and draw string
  for ex_counter = 0 to 2500
    put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_TRN, trans
    put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_RED, trans
    put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_GRN, trans
    put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_BLU, trans
  next ex_counter
  
  ' Scale our workspace buffer to the screen
	select case screen_multiplier
	case is = 1
		put ( 0, 0 ), screen_source
	case is > 1
		for scale_counter_y = 0 to ( screen_height - 1 )
			scale_source_temp_ptr = scale_source_ptr + ( scale_y_step shr 16 ) * screen_source_width
			for scale_counter_x = 0 to ( screen_width - 1 )
				*screen_ptr = scale_source_temp_ptr[ scale_x_step shr 16 ]
				screen_ptr += 1
				scale_x_step += scale_step
			next scale_counter_x
			scale_y_step += scale_step
			scale_x_step = 0
		next scale_counter_y
	case is < 1
		line ( 0, 0 )-( screen_width - 1, screen_height - 1 ), rgb( 255, 0, 255 ), bf
	end select
screenunlock
timer_end = timer

print "render time : " & ( timer_end - timer_start) & " milliseconds"
print " resolution : " & screen_width & "," & screen_height 
print " multiplier : x" & screen_multiplier
sleep

imagedestroy screen_source
imagedestroy ex_buffer_TRN
imagedestroy ex_buffer_RED
imagedestroy ex_buffer_GRN
imagedestroy ex_buffer_BLU
Below is an example using trans within a loop.

Code: Select all

' This demonstration sets in place a small image buffer
' that becomes our workspace. That workspace then gets
' scaled to the screen.
' 
' Thanks to D.J. Peters for providing the scale routine
' and thanks to Dodicat for his frame regulator.
' 
' #####################################################
' 
type frame_rate_type
  as integer fps_cap
  as double fps_resolution
  as double old_timer
  as double old_frame_timer
  as double new_timer
  as double timer_difference
  as double timer_overlap
  as integer old_counter
  as integer new_counter
  as double old_sleep_time
  as double new_sleep_time
  declare constructor()
  declare function regulate() as integer
end type

constructor frame_rate_type()
  fps_cap = 30
  timer_overlap = 1
  fps_resolution = 1 / fps_cap
  old_sleep_time = fps_resolution * 1000
end constructor

function frame_rate_type.regulate() as integer
  new_counter += 1
  new_timer = timer
  timer_difference = new_timer - old_timer
  if timer_difference > timer_overlap then
    timer_overlap = 1 - ( timer_difference - timer_overlap )
    old_timer = new_timer
    old_counter = new_counter
    new_counter = 0
  end if
  new_sleep_time = old_sleep_time + ( ( fps_resolution - new_timer + old_frame_timer ) * 1000 )
  if new_sleep_time < 1 then new_sleep_time = 1
  old_sleep_time = new_sleep_time
  old_frame_timer = new_timer
  
  return cast( integer, new_sleep_time )
end function

dim as frame_rate_type frame_rate

' These dimensions are for the original buffer before
' it gets scaled to the screen
const screen_source_width = 320
const screen_source_height = 180

' Change this multiplier to scale the screen size
'  Some example are :
' 1 = 320,180    4 = 1280,720
' 2 = 640,360    6 = 1920,1080
' 3 = 960,540    8 = 2560,1440
dim as integer screen_multiplier = 2

' Here the dimensions for the scaling are determined
dim as integer screen_width = screen_source_width * screen_multiplier
dim as integer screen_height = screen_source_height  * screen_multiplier

' Initialize the screen to match the multiplier
screenres screen_width, screen_height, 32

' Create our image buffer that we work directly in
' before it gets scaled to the screen
dim screen_source as any ptr = imagecreate( screen_source_width, screen_source_height, 0, 32 )


' Set our workspace image buffer to black
line screen_source, ( 0, 0 )-( screen_source_width - 1, screen_source_height - 1 ), rgb( 0, 0, 0), bf

' Create some 16x16 buffers as part of our example
dim ex_buffer_WHT as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_RED as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_GRN as any ptr = imagecreate( 16, 16, 0, 32 )
dim ex_buffer_BLU as any ptr = imagecreate( 16, 16, 0, 32 )

' Fill our example buffers with colored circles on top
' of a transparent background
line ex_buffer_WHT, ( 0, 0 )-( 15, 15 ), rgb( 255, 0, 255 ), bf
circle ex_buffer_WHT, (8, 8), 7, RGB( 255, 255, 255 ), , , 1, f
line ex_buffer_RED, ( 0, 0 )-( 15, 15 ), rgb( 255, 0, 255 ), bf
circle ex_buffer_RED, (8, 8), 7, RGB( 255, 0, 0 ), , , 1, f
line ex_buffer_GRN, ( 0, 0 )-( 15, 15 ), rgb( 255, 0, 255 ), bf
circle ex_buffer_GRN, (8, 8), 7, RGB( 0, 255, 0 ), , , 1, f
line ex_buffer_BLU, ( 0, 0 )-( 15, 15 ), rgb( 255, 0, 255 ), bf
circle ex_buffer_BLU, (8, 8), 7, RGB( 0, 0, 255 ), , , 1, f

' Set some conditions for our example
dim as integer ex_counter
dim as integer ex_x_range = 319 - 16
dim as integer ex_y_range = 179 - 16

' Scale routine stuff
dim as integer scale_counter_x, scale_counter_y
dim as integer scale_step = ( 1 / screen_multiplier ) * 65536
dim as integer scale_x_step = 0
dim as integer scale_y_step = 0
dim as ulong ptr scale_source_ptr = cptr( ulong ptr, screen_source ) + 8
dim as ulong ptr scale_source_temp_ptr = scale_source_ptr
dim as ulong ptr screen_ptr = screenptr()

dim as string key_press
dim as double timer_start, timer_end

randomize, 3
frame_rate.old_timer = timer
do
  key_press = inkey
	timer_start = timer
	screenlock
		' Render our example placing 4 16x16 tiles using pset
		' and draw string
		for ex_counter = 0 to 2500
			put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_WHT, trans
			put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_RED, trans
			put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_GRN, trans
			put screen_source, ( rnd * ex_x_range, rnd * ex_y_range ), ex_buffer_BLU, trans
		next ex_counter

		line screen_source, ( 4, 3 )-(99, 35), rgb( 0, 0, 0 ), bf
		draw string screen_source, ( 4, 4 ), "FPS cap : " & frame_rate.fps_cap, rgb( 255, 127, 0 )
		draw string screen_source, ( 4, 16 ),  "FPS old : " & frame_rate.old_counter, rgb( 255, 127, 0 )
		draw string screen_source, ( 4, 28 ),  "FPS new : " & frame_rate.new_counter, rgb( 255, 127, 0 )
		
		' Scale our workspace buffer to the screen
		select case screen_multiplier
		case is = 1
			put ( 0, 0 ), screen_source, pset
		case is > 1
			for scale_counter_y = 0 to ( screen_height - 1 )
				scale_source_temp_ptr = scale_source_ptr + ( scale_y_step shr 16 ) * screen_source_width
				for scale_counter_x = 0 to ( screen_width - 1 )
					*screen_ptr = scale_source_temp_ptr[ scale_x_step shr 16 ]
					screen_ptr += 1
					scale_x_step += scale_step
				next scale_counter_x
				scale_y_step += scale_step
				scale_x_step = 0
			next scale_counter_y
      scale_source_ptr = cptr( ulong ptr, screen_source ) + 8
      screen_ptr = screenptr()
      scale_y_step = 0
		case is < 1
			line ( 0, 0 )-( screen_width - 1, screen_height - 1 ), rgb( 255, 0, 255 ), bf
		end select
	screenunlock
  line screen_source, ( 0, 0 )-( screen_source_width - 1, screen_source_height - 1 ), rgb( 0, 0, 0), bf
	timer_end = timer
  sleep frame_rate.regulate, 1
loop until( key_press <> "" )

imagedestroy screen_source
imagedestroy ex_buffer_WHT
imagedestroy ex_buffer_RED
imagedestroy ex_buffer_GRN
imagedestroy ex_buffer_BLU
Post Reply