' ----------------------------------------------------------------------------
' Load a greyscale TGA Imagebank (8bit, top left, uncompressed)
' ----------------------------------------------------------------------------
Function LoadTGABank:TBank(filename:String, offset:Int = 0)

	Local f:TStream = OpenStream(filename)
	Local bank:TBank = CreateBank(GAME_LOD * GAME_LOD / 2)

	' seek to dimension offset
	f.seek(12)

	' read image dimensions
	Local width:Int = ReadShort(f)
	Local height:Int = ReadShort(f)
	
	If f Then

		' seek to data offset
		f.Seek(18)

		' copy stream to bank
		ReadBank(bank, f, 0, width * height)

	EndIf

	Return bank
	
	CloseFile f

End Function

' ----------------------------------------------------------------------------
' Load a normal Imagebank
' ----------------------------------------------------------------------------
Function LoadPixmapBank:TBank(filename:String)

	Local pixmap:TPixmap = LoadPixmap(filename)
	Local width:Int = pixmap.width
	Local height:Int = pixmap.height
	Local bank:TBank = CreateBank(width * height)

	For Local y:Int = 0 To height - 1

		For Local x:Int = 0 To width - 1

			SetBankPixel(bank, ReadPixel(pixmap, x, y) Shr 16 & %11111111, x, y, width)

		Next

	Next

	pixmap = Null

	Return bank
	
End Function

' ----------------------------------------------------------------------------
' Save a RAW Imagebank
' ----------------------------------------------------------------------------
Function SaveImageBank:TBank(bank:TBank, filename:String)
	
	Local f:TStream = WriteFile(filename)
	
	WriteBank(bank, f, 0, BankSize(bank))
	
	CloseFile f
	
End Function

' ----------------------------------------------------------------------------
' Reads a RAW Imagebank Pixel at a given position
' ----------------------------------------------------------------------------
Function GetBankPixel:Int(bank:TBank, px:Int, py:Int, width:Int, offset:Int = 0)
	
	Return PeekByte(bank, ((py * width) + px + offset) Mod BankSize(bank))
	
End Function

' ----------------------------------------------------------------------------
' Writes a RAW Imagebank Pixel at a given position
' ----------------------------------------------------------------------------
Function SetBankPixel(bank:TBank, value:Int, px:Int, py:Int, width:Int)
	
	PokeByte(bank, (py * width) + px, value Mod BankSize(bank))
	
End Function

' ----------------------------------------------------------------------------
' Create a greyscale Image from a RAW Imagebank
' ----------------------------------------------------------------------------
Function CreateImageFromBank:TPixmap(bank:TBank, width:Int, height:Int, alphaflag:Int = False)
	
	Local x:Int, y:Int, h:Int, alpha:Int
	Local pixmap:TPixmap
	
	If alphaflag = True Then

		pixmap = CreatePixmap(width, height, PF_A8)

	Else

		pixmap = CreatePixmap(width, height, PF_I8)

	EndIf
	
	For x = 0 To width - 1
		
		For y = 0 To height - 1
			
			h = GetBankPixel(bank, x, y, width)
			
			If alphaflag Then alpha = alphaflag Else alpha = h
			WritePixel(pixmap, x, y, h | (h Shl 8) | (h Shl 16) | (alpha Shl 24))
			
		Next
		
	Next
	
	Return pixmap
	
End Function

' ----------------------------------------------------------------------------
' Bilinear scale a RAW Imagebank To a different size using a ratio variable
' ----------------------------------------------------------------------------
Function ScaleBank:TBank(bank:TBank, width:Int, height:Int, ratio:Float)
	
	Local x:Int, y:Int, c:Int
	Local ratio2w:Float = (ratio:Float * width - 1) / (width - 1.0)
	Local ratio2h:Float = (ratio:Float * height - 1) / (height - 1.0)
	
	Local newbank:TBank = CreateBank((width * ratio) * (height * ratio))
	
	For y = 0 To (height * ratio) - 1
		
		For x = 0 To (width * ratio) - 1
			
			Local u:Float = x * 1.0 / ratio2w:Float
			Local v:Float = y * 1.0 / ratio2h:Float
			
			c:Int = Bilinear(bank, width, height, u, v)
			
			SetBankPixel(newbank, c, x, y, width * ratio)
			
		Next
		
	Next
	
	Return newbank
	
End Function

' ----------------------------------------------------------------------------
' Bilinear Scale Function
' ----------------------------------------------------------------------------
Function Bilinear:Int(bank:TBank, width:Int, height:Int, x:Float, y:Float)
	
	Local x0:Float = Floor(x)
	Local y0:Float = Floor(y)
	Local x1:Float = Floor(x + 1)
	Local y1:Float = Floor(y + 1)
	Local dx:Float = x - x0
	Local dy:Float = y - y0
	
	Local x02x:Int = 1
	Local x03x:Int = 1
	Local y02x:Int = 1
	Local y03x:Int = 1
	
	If x0 < 0 Then x02x = 0
	If x1 > width - 1 Then x03x = 0
	If y0 < 0 Then y02x = 0
	If y1 > height - 1 Then y03x = 0
	
	If x02x = 0 Then x0 = 0
	If y02x = 0 Then y0 = 0
	If x03x = 0 Then x1 = 0
	If y03x = 0 Then y1 = 0
	
	Local r0:Int = GetBankPixel(bank, x0, y0, width) * x02x * y02x
	Local r1:Int = GetBankPixel(bank, x1, y0, width) * x03x * y02x
	Local r2:Int = GetBankPixel(bank, x1, y1, width) * x03x * y03x
	Local r3:Int = GetBankPixel(bank, x0, y1, width) * x02x * y03x
	
	Return LinearInterpolate(LinearInterpolate(r0, r1, dx), LinearInterpolate(r3, r2, dx), dy)
		
End Function

' ----------------------------------------------------------------------------
' Linear Interpolation
' ----------------------------------------------------------------------------
Function LinearInterpolate:Float(x1:Float, x2:Float, mu:Float = 0.5)
	
	Return (x1 * (1.0 - mu) + x2 * mu)
	
End Function

' ----------------------------------------------------------------------------
' Cosine Interpolation
' ----------------------------------------------------------------------------
Function CosineInterpolate:Float(x1:Float, x2:Float, mu:Float = 0.5)

	Local mu2:Float = 1.0 - ((Cos(mu * 180.0) + 1.0) / 2.0)
	Return (x1 * (1.0 - mu2) + x2 * mu2)
	
End Function

' ----------------------------------------------------------------------------
' Cubic Interpolation
' ----------------------------------------------------------------------------
Function CubicInterpolate:Float(v0:Float, v1:Float, v2:Float, v3:Float, mu:Float = 0.5)
	
	Local P:Float = (v3 - v2) - (v0 - v1)
	Local Q:Float = (v0 - v1) - P
	Local R:Float = v2 - v0
	Local S:Float = v1
	
	Return P * mu ^ 3 + Q * mu ^ 2 + r * mu + S
	
End Function

' ----------------------------------------------------------------------------
' Hermite Interpolation
' ----------------------------------------------------------------------------
Function HermiteInterpolate:Float(y0:Float, y1:Float, y2:Float, y3:Float, t:Float = 0.5, tension:Float = 0.0, bias:Float = 0.0)

	Local t2:Float = t * t
	Local t3:Float = t2 * t
	Local tb:Float = (1 + bias) * (1 - tension) / 2

	Return (2 * t3 - 3 * t2 + 1) * y1 + (((t3 - 2 * t2 + t) * (y1 - y0 + y2 - y1)) + (t3 - t2) * (y2 - y1 + y3 - y2)) * tb + (3 * t2 - 2 * t3) * y2
	
End Function

' ----------------------------------------------------------------------------
' Calc Normal of a point
' ----------------------------------------------------------------------------
Function CalcNormal:Int(bank:TBank, width:Int, height:Int, x:Int, y:Int, normalsfactor:Float = 1.0, inverse:Int = False)
	
	Local xm1:Int, xp1:Int, ym1:Int, yp1:Int
	Local tl:Int, tm:Int, tr:Int, ml:Int, mm:Int, mr:Int, bl:Int, bm:Int, br:Int
	Local vx:Float, vy:Float, vz:Float
	Local isq2:Float, sum:Float
	Local al:Float, ar:Float, at:Float, ab:Float
	Local m:Float
	Local r:Int, g:Int, b:Int, rgb:Int
	Local inv:Int
	
	If x > 0 Then xm1 = x - 1 Else xm1 = 0
	If x < width - 1 Then xp1 = x + 1 Else xp1 = width - 1
	If y > 0 Then ym1 = y - 1 Else ym1 = 0
	If y < height - 1 Then yp1 = y + 1 Else yp1 = height - 1
	
	tl = GetBankPixel(bank, xm1, ym1, width)
	tm = GetBankPixel(bank, x, ym1, width)
	tr = GetBankPixel(bank, xp1, ym1, width)
	ml = GetBankPixel(bank, xm1, y, width)
	mm = GetBankPixel(bank, x, y, width)
	mr = GetBankPixel(bank, xp1, y, width)
	bl = GetBankPixel(bank, xm1, yp1, width)
	bm = GetBankPixel(bank, x, yp1, width)
	br = GetBankPixel(bank, xp1, yp1, width)
	
	vx = 0.0
	vy = 0.0
	vz = 1.0
	
	isq2 = 1.0 / Sqr(2.0)
	sum = 1.0 + isq2 + isq2
	
	al = (tl * isq2 + ml + bl * isq2) / sum
	ar = (tr * isq2 + mr + br * isq2) / sum
	at = (tl * isq2 + tm + tr * isq2) / sum
	ab = (bl * isq2 + bm + br * isq2) / sum
	
	vx = (al - ar) / (255.0)
	vy = (at - ab) / (255.0)
		
	m = Max(0, vx * vx + vy * vy)
	m = Min(m, 1.0)
		
	vz = Sqr(1.0 - m)
		
	If normalsfactor <> 0.0
		vz = vz / normalsfactor
		m = Sqr(vx * vx + vy * vy + vz * vz)
		vx = vx / m
		vy = vy / m
		vz = vz / m
	EndIf

	If inverse Then

		r = ((1.0 - vx) * 127.5)
		g = ((1.0 - vy) * 127.5)
		b = 255 - ((1.0 - vz) * 127.5)

	Else

		r = 255 - ((1.0 - vx) * 127.5)
		g = ((1.0 - vy) * 127.5)
		b = 255 - ((1.0 - vz) * 127.5)

	EndIf
	
	Return b | (g Shl 8) | (r Shl 16)
	
End Function

' ----------------------------------------------------------------------------
' Return Float Minimum of two values
' ----------------------------------------------------------------------------
Function FloatMin:Float(a:Float, b:Float)
	
	If a < b Then Return a Else Return b
	
End Function

' ----------------------------------------------------------------------------
' Return Float Maximum of two values
' ----------------------------------------------------------------------------
Function FloatMax:Float(a:Float, b:Float)
	
	If a > b Then Return a Else Return b
	
End Function