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:
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:
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.
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.