Code archives/Graphics/Seamless Tiles

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

Download source code

Seamless Tiles by impixi2006
Create a seamless tile pixmap from a source pixmap
Rem
	Seamless Texture Tiling Example
	
	ORIGINAL AUTHOR (Article and C Code):
		Paul Bourke
		January 2000
	http://local.wasp.uwa.edu.au/~pbourke/texture/tiling/
			
	REQUIREMENTS:
		BlitzMax 1.22
		Windows XP (Linux and MacOS untested)
		
	PURPOSE:
		Create a seamless 'tile' pixmap from a source pixmap
		
EndRem

SuperStrict

AppTitle = "Seamless Texture Tiling Example"

Graphics 1024, 768
SetBlend SOLIDBLEND

Local tex:TPixmap = LoadPixmap("tex.png")	'<--- Provide your own texture for this example. <---

If (tex.format <> PF_RGBA8888) Then tex = ConvertPixmap(tex, PF_RGBA8888)

Local tile:TPixmap = createSeamlessTile(tex, MASK_RADIAL)

Local img:TImage = LoadImage(tile)

SetClsColor 255, 255, 255

While Not KeyHit(KEY_ESCAPE)

	Cls

	SetViewport 0, 0, GraphicsWidth(), GraphicsHeight()

	DrawText "Source:", 2, 2
	DrawPixmap tex, 2, 22
	
	DrawText "Result:", 2, tex.height + 42
	DrawPixmap tile, 2, tex.height + 62

	DrawText "Tiled Result:", tex.width + 20, 2
	SetViewport tex.width + 20, 22, GraphicsWidth() - tex.width - 22, GraphicsHeight() - 26
	TileImage img
			
	Flip 1

Wend

End


'*************** SEAMLESS TILE FUNCTION AND RELATED CONSTANTS *************************************


Const MASK_LINEAR:Int = 0
Const MASK_RADIAL:Int = 1

Function createSeamlessTile:TPixmap(src:TPixmap, masktype:Int = MASK_LINEAR)

	'src: Source pixmap texture. Format should be PF_RGBA8888.
	'masktype: Mask type. MASK_LINEAR or MASK_RADIAL. Some textures tile better using a different mask.
	'Returns a new 'tileable' pixmap.

	Local outp:TPixmap = CreatePixmap(src.width, src.height, src.format)
	Local diag:TPixmap = CreatePixmap(src.width, src.height, src.format)

	Local temp:TPixmap = PixmapWindow(src, 0, 0, src.width / 2, src.height / 2)

	For Local x:Int = 0 To temp.width - 1
		For Local z:Int = 0 To temp.height - 1
			WritePixel diag, (src.width / 2) + x, (src.height / 2) + z, ReadPixel(temp, x, z)
		Next
	Next
	
	temp = PixmapWindow(src, src.width / 2, src.height / 2, src.width / 2, src.height / 2)
	
	For Local x:Int = 0 To temp.width - 1
		For Local z:Int = 0 To temp.height - 1
			WritePixel diag, x, z, ReadPixel(temp, x, z)
		Next
	Next

	temp = PixmapWindow(src, src.width / 2, 0, src.width / 2, src.height / 2)
	
	For Local x:Int = 0 To temp.width - 1
		For Local z:Int = 0 To temp.height - 1
			WritePixel diag, x, (src.height / 2) + z, ReadPixel(temp, x, z)
		Next
	Next

	temp = PixmapWindow(src, 0, src.height / 2, src.width / 2, src.height / 2)
	
	For Local x:Int = 0 To temp.width - 1
		For Local z:Int = 0 To temp.height - 1
			WritePixel diag, (src.width / 2) + x, z, ReadPixel(temp, x, z)
		Next
	Next

	Local masksize:Int
	If (src.width > src.height) Then masksize = src.width Else masksize = src.height 
		
	Local mask:TPixmap = CreatePixmap(masksize, masksize, PF_RGB888)

	For Local x:Int = 0 To masksize / 2
		For Local z:Int = 0 To masksize / 2

			Local d:Float = 0.0
			
			If masktype = MASK_RADIAL
				d = Sqr((x - (masksize / 2)) * (x - (masksize / 2)) + (z - (masksize / 2)) * (z - (masksize / 2))) / (masksize / 2)
			Else 
				If masktype = MASK_LINEAR
					d = Max(Float((masksize / 2) - x), Float((masksize / 2) - z)) / Float(masksize / 2)
				EndIf
			EndIf
			
			d = 255 - (255 * d)
			
			If d < 1 Then d = 1
			If d > 255 Then d = 255
			
			WritePixel mask, x, z, Int(Byte(d) Shl 16 | Byte(d) Shl 8 | Byte(d))
			WritePixel mask, x, (masksize - 1 - z), Int(Byte(d) Shl 16 | Byte(d) Shl 8 | Byte(d))
			WritePixel mask, (masksize - 1 - x), z, Int(Byte(d) Shl 16 | Byte(d) Shl 8 | Byte(d))
			WritePixel mask, (masksize - 1 - x), (masksize - 1 - z), Int(Byte(d) Shl 16 | Byte(d) Shl 8 | Byte(d))

		Next
	Next

	mask = ResizePixmap(mask, src.width, src.height)

	For Local z:Int = 0 To src.height - 1
		For Local x:Int = 0 To src.width - 1
		
			Local a1:Float = Float(Byte(ReadPixel(mask, x, z) Shr 16))
			Local a2:Float = Float(Byte(ReadPixel(mask, ((x + src.width / 2) Mod src.width), ((z + src.height / 2) Mod src.height)) Shr 16))
			
			Local px1:Int = ReadPixel(src, x, z)
			Local px1a:Byte = px1 Shr 24
			Local px1b:Byte = px1 Shr 16
			Local px1g:Byte = px1 Shr 8
			Local px1r:Byte = px1
			
			Local px2:Int = ReadPixel(diag, x, z)
			Local px2a:Byte = px2 Shr 24
			Local px2b:Byte = px2 Shr 16
			Local px2g:Byte = px2 Shr 8
			Local px2r:Byte = px2
			
			Local pxRa:Byte = px1a
			Local pxRb:Byte = (a1 * (px1b / (a1 + a2))) + (a2 * (px2b / (a1 + a2)))
			Local pxRg:Byte = (a1 * (px1g / (a1 + a2))) + (a2 * (px2g / (a1 + a2)))
			Local pxRr:Byte = (a1 * (px1r / (a1 + a2))) + (a2 * (px2r / (a1 + a2)))
			Local pxR:Int = Int(pxRa Shl 24 | pxRb Shl 16 | pxRg Shl 8 | pxRr)
						
			WritePixel outp, x, z, pxR
	
		Next
	Next

	Return outp

EndFunction

Comments

Booticus2006
Thats damned handy! I did notice on a few test runs that a horizontal black line appears on some images that I used. Just picked some random grass images from here:

http://www.injuryupdate.com.au/issues/grass_types.php

but other than that its a killer bit of code, thanks!


impixi2006
EDIT: Removed dead links.


Plantagenet2006
Tested With about half a dozen of my own images of varying sizes and sources and had no problems. Good work, and a useful utility.


impixi2006
As requested in another thread, here's the source for Ultra Simple Seamless Tiler V1.00. You will need to change the framework and possibly tweak the code if compiling for other platforms. The MaxGUI module is required.



EDIT: Added code line "Import BRL.EventQueue".


impixi2008
Here's the latest version of Ultra Simple Seamless Tiler 1.01, to be compiled against maxgui.mod revision 29:




Code Archives Forum