I'm encountering similiar issues with pixmaps vs images vs alpha vs scrolling.
My attempts to implement an 'area map' that utilises an alpha-based, pixel-based 'fog of war' effect have ended poorly, mainly because TImage pixels cannot be manipulated directly. I have to use a TPixmap, alter every pixel, and then 'load' it into a TImage for
every render pass. For the scrolling effect I modify the viewport based on the player's position, because using TPixmaps for scrolling proved far too slow. Here's some rough sample code:
SuperStrict
Global ScreenWidth:Int = 1024
Global ScreenHeight:Int = 768
Graphics ScreenWidth, ScreenHeight, 32
SetBlend ALPHABLEND
Local AreaMap:TAreaMap = New TAreaMap
AreaMap.create(512, 512)
'AreaMap.createFromPixmap(LoadPixmap("map.png"))
AreaMap.createFogPat(50, 50)
AreaMap.setPlayerXZ(AreaMap.Map.width * 0.5, AreaMap.Map.height * 0.5)
SetClsColor 255, 255, 255
While Not KeyHit(KEY_ESCAPE)
Local oldtime:Int = MilliSecs()
Cls
If KeyDown(KEY_LEFT) Then AreaMap.setPlayerX(AreaMap.PlayerX - 2)
If KeyDown(KEY_RIGHT) Then AreaMap.setPlayerX(AreaMap.PlayerX + 2)
If KeyDown(KEY_UP) Then AreaMap.setPlayerZ(AreaMap.PlayerZ - 2)
If KeyDown(KEY_DOWN) Then AreaMap.setPlayerZ(AreaMap.PlayerZ + 2)
AreaMap.renderMap()
SetColor 0, 0, 0
DrawText "Press cursor keys to move. ESC exits.", AreaMap.ViewPortX, AreaMap.ViewPortZ + AreaMap.ViewPortHeight + 10
DrawText "Time taken:" + String(MilliSecs() - oldtime), AreaMap.ViewPortX, AreaMap.ViewPortZ + AreaMap.ViewPortHeight + 30
Flip
Delay 10
Wend
End
Type TAreaMap
Field Map:TPixmap
Field FogPatArr:Byte[,]
Field FogPatWidth:Int
Field FogPatHeight:Int
Field PlayerX:Int
Field PlayerZ:Int
Field ViewportX:Int = 50
Field ViewportZ:Int = 50
Field ViewportWidth:Int = 256
Field ViewportHeight:Int = 256
Method create(w:Int, h:Int)
Map = CreatePixmap(w, h, PF_RGBA8888)
SeedRnd MilliSecs()
For Local x:Int = 0 To Map.width - 1
For Local z:Int = 0 To Map.height - 1
WritePixel(Map, x, z, Int((0 Shl 24) | (Rand(0, 255) Shl 16) | (Rand(0, 255) Shl 8) | (Rand(0, 255))))
Next
Next
For Local x:Int = 0 To Map.width - 1
For Local z:Int = 0 To 2
WritePixel(Map, x, z, 0)
Next
Next
For Local x:Int = 0 To Map.width - 1
For Local z:Int = Map.height - 3 To Map.height - 1
WritePixel(Map, x, z, 0)
Next
Next
For Local z:Int = 0 To Map.height - 1
For Local x:Int = 0 To 2
WritePixel(Map, x, z, 0)
Next
Next
For Local z:Int = 0 To Map.height - 1
For Local x:Int = Map.width - 3 To Map.width - 1
WritePixel(Map, x, z, 0)
Next
Next
EndMethod
Method createFromPixmap(pm:TPixmap)
Map = CreatePixmap(pm.width, pm.height, PF_RGBA8888)
Map = ConvertPixmap(pm, PF_RGBA8888)
For Local x:Int = 0 To Map.width - 1
For Local z:Int = 0 To Map.height - 1
Local px:Int = ReadPixel(Map, x, z)
Local pxb:Byte = px Shr 16
Local pxg:Byte = px Shr 8
Local pxr:Byte = px
WritePixel(Map, x, z, Int(0 Shl 24 | pxb Shl 16 | pxg Shl 8 | pxr))
Next
Next
EndMethod
Method createFogPat(w:Int, h:Int)
FogPatWidth = w
FogPatHeight = h
FogPatArr = New Byte[FogPatWidth, FogPatHeight]
Cls
SetColor 0, 0, 255
DrawOval 0, 0, FogPatWidth, FogPatHeight
Local pm:TPixmap = GrabPixmap(0, 0, FogPatWidth, FogPatHeight)
For Local x:Int = 0 To FogPatWidth - 1
For Local z:Int = 0 To FogPatWidth - 1
FogPatArr[x, z] = Byte(ReadPixel(pm, x, z))
Next
Next
EndMethod
Method applyFogPat(cx:Int, cz:Int)
Local mapx:Int = cx - (FogPatWidth * 0.5)
For Local x:Int = 0 To (FogPatWidth - 1)
Local mapz:Int = cz - (FogPatHeight * 0.5)
For Local z:Int = 0 To (FogPatHeight - 1)
If (mapx >= 0) And (mapx < (Map.width - 1))
If (mapz >= 0) And (mapz < (Map.height - 1))
WritePixel(Map, mapx, mapz, ReadPixel(Map, mapx, mapz) | (FogPatArr[x, z] Shl 24))
EndIf
EndIf
mapz :+ 1
Next
mapx :+ 1
Next
EndMethod
Method setPlayerXZ(posx:Int, posz:Int)
PlayerX = posx
If PlayerX < 0 Then PlayerX = 0
If PlayerX >= Map.width Then PlayerX = (Map.width - 1)
PlayerZ = posz
If PlayerZ < 0 Then PlayerZ = 0
If PlayerZ >= Map.height Then PlayerZ = (Map.height - 1)
For Local n:Int = 1 To 5
applyFogPat(PlayerX, PlayerZ)
Next
EndMethod
Method setPlayerX(posx:Int)
PlayerX = posx
If PlayerX < 0 Then PlayerX = 0
If PlayerX >= Map.width Then PlayerX = (Map.width - 1)
applyFogPat(PlayerX, PlayerZ)
EndMethod
Method setPlayerZ(posz:Int)
PlayerZ = posz
If PlayerZ < 0 Then PlayerZ = 0
If PlayerZ >= Map.height Then PlayerZ = (Map.height - 1)
applyFogPat(PlayerX, PlayerZ)
EndMethod
Method renderMap()
SetAlpha 1.0
SetColor 128, 128, 128
DrawRect ViewPortX, ViewportZ, ViewportWidth, ViewportHeight
SetColor 255, 255, 255
SetViewport ViewPortX, ViewportZ, ViewportWidth, ViewportHeight
DrawImage LoadImage(Map), ViewPortX - PlayerX + ViewportWidth * 0.5, ViewportZ - PlayerZ + ViewportHeight * 0.5
SetAlpha 0.75
DrawOval ViewPortX + (ViewportWidth * 0.5) - 5, ViewportZ + (ViewportHeight * 0.5) - 5, 10, 10
SetViewport 0, 0, ScreenWidth, ScreenHeight
EndMethod
End Type
The visual results are what I wanted to achieve, but the performance is poor (though less so when not compiled in 'debug' mode).
In light of this, I have to use a less pleasing visual workaround, but the performance is far better because the code only renders TImages and rectangles. Example:
SuperStrict
Global ScreenWidth:Int = 1024
Global ScreenHeight:Int = 768
Graphics ScreenWidth, ScreenHeight, 32
Local AreaMap:TAreaMap = New TAreaMap
AreaMap.create(512, 512)
'AreaMap.createFromPixmap(LoadPixmap("map.png"))
AreaMap.setPlayerXZ(AreaMap.Map.width * 0.5, AreaMap.Map.height * 0.5)
SetClsColor 255, 255, 255
While Not KeyHit(KEY_ESCAPE)
Local oldtime:Int = MilliSecs()
Cls
If KeyDown(KEY_LEFT) Then AreaMap.setPlayerX(AreaMap.PlayerX - 2)
If KeyDown(KEY_RIGHT) Then AreaMap.setPlayerX(AreaMap.PlayerX + 2)
If KeyDown(KEY_UP) Then AreaMap.setPlayerZ(AreaMap.PlayerZ - 2)
If KeyDown(KEY_DOWN) Then AreaMap.setPlayerZ(AreaMap.PlayerZ + 2)
AreaMap.renderMap()
SetColor 0, 0, 0
DrawText "Press cursor keys to move. ESC exits.", AreaMap.ViewPortX, AreaMap.ViewPortZ + AreaMap.ViewPortHeight + 10
DrawText "Time taken:" + String(MilliSecs() - oldtime), AreaMap.ViewPortX, AreaMap.ViewPortZ + AreaMap.ViewPortHeight + 30
Flip
Delay 10
Wend
End
Type TAreaMap
Field Map:TImage
Field PlayerX:Int
Field PlayerZ:Int
Field ViewportX:Int = 50
Field ViewportZ:Int = 50
Field ViewportWidth:Int = 256
Field ViewportHeight:Int = 256
Field CellWidth:Int = 16
Field CellHeight:Int = 16
Field Grid:Int[,]
Field GridWidth:Int
Field GridHeight:Int
Method create(w:Int, h:Int)
Local pm:TPixmap = CreatePixmap(w, h, PF_RGBA8888)
SeedRnd MilliSecs()
For Local x:Int = 0 To pm.width - 1
For Local z:Int = 0 To pm.height - 1
WritePixel(pm, x, z, Int((0 Shl 24) | (Rand(0, 255) Shl 16) | (Rand(0, 255) Shl 8) | (Rand(0, 255))))
Next
Next
For Local x:Int = 0 To pm.width - 1
For Local z:Int = 0 To 2
WritePixel(pm, x, z, 0)
Next
Next
For Local x:Int = 0 To pm.width - 1
For Local z:Int = pm.height - 3 To pm.height - 1
WritePixel(pm, x, z, 0)
Next
Next
For Local z:Int = 0 To pm.height - 1
For Local x:Int = 0 To 2
WritePixel(pm, x, z, 0)
Next
Next
For Local z:Int = 0 To pm.height - 1
For Local x:Int = pm.width - 3 To pm.width - 1
WritePixel(pm, x, z, 0)
Next
Next
Map = LoadImage(pm)
initGrid()
EndMethod
Method createFromPixmap(pm:TPixmap)
Map = LoadImage(pm)
initGrid()
EndMethod
Method initGrid()
GridWidth = (Map.Width / CellWidth)
GridHeight = (Map.Height / CellHeight)
Grid = New Int[GridWidth, GridHeight]
EndMethod
Method setPlayerXZ(posx:Int, posz:Int)
PlayerX = posx
If PlayerX < 0 Then PlayerX = 0
If PlayerX >= Map.width Then PlayerX = (Map.width - 1)
PlayerZ = posz
If PlayerZ < 0 Then PlayerZ = 0
If PlayerZ >= Map.height Then PlayerZ = (Map.height - 1)
setGridArea()
EndMethod
Method setPlayerX(posx:Int)
PlayerX = posx
If PlayerX < 0 Then PlayerX = 0
If PlayerX >= Map.width Then PlayerX = (Map.width - 1)
setGridArea()
EndMethod
Method setPlayerZ(posz:Int)
PlayerZ = posz
If PlayerZ < 0 Then PlayerZ = 0
If PlayerZ >= Map.height Then PlayerZ = (Map.height - 1)
setGridArea()
EndMethod
Method setGridArea()
Local gx:Int = Floor(PlayerX / CellWidth)
Local gz:Int = Floor(PlayerZ / CellHeight)
setgridXZ(gx, gz)
setgridXZ(gx + 1, gz)
setgridXZ(gx - 1, gz)
setgridXZ(gx, gz + 1)
setgridXZ(gx, gz - 1)
setgridXZ(gx, gz + 1)
setgridXZ(gx + 1, gz - 1)
setgridXZ(gx + 1, gz + 1)
setgridXZ(gx - 1, gz - 1)
setgridXZ(gx - 1, gz + 1)
EndMethod
Method setGridXZ(gx:Int, gz:Int)
If (gx >= 0) And (gz >= 0)
If (gx < GridWidth) And (gz < GridHeight)
Grid[gx, gz] = True
EndIf
EndIf
EndMethod
Method renderMap()
SetBlend SOLIDBLEND
SetColor 128, 128, 128
DrawRect ViewPortX, ViewportZ, ViewportWidth, ViewportHeight
SetColor 255, 255, 255
SetViewport ViewPortX, ViewportZ, ViewportWidth, ViewportHeight
DrawImage Map, ViewPortX - PlayerX + ViewportWidth * 0.5, ViewportZ - PlayerZ + ViewportHeight * 0.5
'SetBlend SHADEBLEND
SetColor 128, 128, 128
For Local gx:Int = 0 To GridWidth - 1
For Local gz:Int = 0 To GridHeight - 1
If Not Grid[gx, gz] Then DrawRect ViewPortX - PlayerX + ViewportWidth * 0.5 + (gx * CellWidth), ViewportZ - PlayerZ + ViewportHeight * 0.5 + (gz * CellHeight), CellWidth, CellHeight
Next
Next
SetBlend ALPHABLEND
SetColor 255, 255, 255
SetAlpha 0.75
DrawOval ViewPortX + (ViewportWidth * 0.5) - 5, ViewportZ + (ViewportHeight * 0.5) - 5, 10, 10
SetViewport 0, 0, ScreenWidth, ScreenHeight
EndMethod
End Type
Hopefully my post has not been too off-topic...
I suppose the lesson here is you
will need to re-think/compromise your designs to workaround BlitzMax's limitations and exploit its strenths. However, this is true for all computer languages.