About Monkey 2 › Forums › Monkey 2 Programming Help › Faster way to read in pixels?
This topic contains 11 replies, has 5 voices, and was last updated by 
 therevills 1 year, 4 months ago.
- 
		AuthorPosts
 - 
		
			
				
December 17, 2017 at 6:02 am #12329
Currently I’m doing the following:
Monkey123456789101112131415161718192021tileSet = LoadSpriteSheet(tileSetPath, tileCount, tileWidth, tileHeight)tileSetMask = New Int[tileCount, tileWidth, tileHeight]For Local i:Int = 0 Until tileSet.LengthLocal tmpCanvas := New Canvas(tileSet[i])Local px := tmpCanvas.CopyPixmap(tmpCanvas.Viewport)For Local y := 0 To px.Height - 1For Local x := 0 To px.Width - 1Local color:Color = px.GetPixel(x, y)If color.A > 0tileSetMask[i, x, y] = 1ElsetileSetMask[i, x, y] = 0EndEndEndNextThe tileset is an array of images (16×16), just 32 of them at the moment and I can see the delay already… is there a better / faster way?
Cheers,
SteveDecember 17, 2017 at 8:32 am #12330I would start to try with someting like that (not tested) (might create problems with lighnting and image shaders..?):
Monkey12345678910111213141516171819For Local i:Int = 0 Until tileSet.LengthLocal tmpCanvas := New Canvas(tileSet[i])Local px := tmpCanvas.CopyPixmap(tmpCanvas.Viewport)Local pixPtr:UInt Ptr 'to declare only once so i's not created each time in the loop (more usefull with structs (like Color) or classes or arrays intanciation...'RGBA8(per channel) is the default pixel format but can be different. 4*8=32bit long thus UIntFor Local y := 0 To px.Height - 1pixPtr=Cast<UInt Ptr>( px.PixelPtr( 0,y ) )For Local x := 0 To px.Width - 1If pptr[x]&$FF000000>0 'Bitwise opertor to check if there is some alpha (8 leftside bits of RGBA32(read ABGR! Endianess?) thus &$FF000000 to clear the BGR 24 bits. (is there a problem with the pixel format documentation? or is it reading small from big?)tileSetMask[i, x, y] = 1ElsetileSetMask[i, x, y] = 0EndEndEndNextBut I would also try to work with pixmaps until the last moment and as few canvas work as possible (using PastePixmap at the end can be usefull sometimes). I think it is generally faster but i’m not shure about that (and what about lighnint and shaders integrity). Not faster for drawing plain bitmaps/inmages to the target render/mojocanvas I guess…
If you can, try to use an array of Pixmaps instead of array of Images and then use pointer to read/write directly in the memory (like in the example) without using methods (even functions ?) when you are in an intensive loop spot. Reading/Writing pix per pix can be intense.
There’s also a shader approach as an option (more technical but might use GPU’s parallel computing ability, with pure mx2 you have no (parallel)multi-threading).December 18, 2017 at 3:42 am #12341Note: GetPixel() and GetPixelARGB() look at format of pixmap to return correct value.
December 18, 2017 at 6:44 am #12346Not sure why you’re creating a canvas and then copying the viewport pixmap of it.
You can just reference the tile in the sprite sheet. Copying a pixmap in a loop is a bit taxing.
Idk. xDDecember 18, 2017 at 7:30 am #12347I’d probably do it like this:
* Load the sprite sheet/atlas into a pixmap.
* Create the masks in one hit and store them in a single I8 pixmap.
* Utilize the Pixmap Data pointers for performance reasons.
* Hope that Mark doesn’t say it is “unsafe” to do it like that.Example (provide your own suitable PNG file):
Monkey12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182#Import "<std>"#Import "<mojo>"#Import "spritesheet2.png"Using std..Using mojo..Function Main()New AppInstanceNew MyWindowApp.Run()EndClass MyWindow Extends WindowMethod New( title:String="Simple mojo app",width:Int=1024,height:Int=768,flags:WindowFlags=Null )Super.New( title,width,height,flags )tileSet = New TileSet("asset::spritesheet2.png", 32, 32)EndMethod OnRender( canvas:Canvas ) OverrideApp.RequestRender()canvas.DrawImage(tileSet.imgAtlas, 0, 0)canvas.DrawImage(tileSet.imgAtlasMask, tileSet.imgAtlas.Width, 0)canvas.Flush()EndField tileSet:TileSetEnd'==============================Class TileSetPublicMethod New(url:String, tileWidth:Int, tileHeight:Int)Local pmAtlas := Pixmap.Load(url, PixelFormat.Unknown)If Not pmAtlas Then RuntimeError("Could not load spritesheet: " + url)Local tileCount := (pmAtlas.Width / tileWidth) * (pmAtlas.Height / tileHeight)Local pmAtlasMask := New Pixmap(pmAtlas.Width, pmAtlas.Height, PixelFormat.I8)Local pmAtlasDataPtr:UByte Ptr = pmAtlas.Data + Cast<UByte>(pmAtlas.Depth - 1)Local pmAtlasMaskDataPtr:UByte Ptr = pmAtlasMask.Data + Cast<UByte>(pmAtlasMask.Depth - 1)Local pidx:ULong = 0While pidx < (pmAtlas.Width * pmAtlas.Height)If pmAtlasDataPtr[0] > 0pmAtlasMaskDataPtr[0] = 255ElsepmAtlasMaskDataPtr[0] = 0EndifpmAtlasDataPtr += Cast<UByte>(pmAtlas.Depth)pmAtlasMaskDataPtr += Cast<UByte>(pmAtlasMask.Depth)pidx += 1WendimgAtlas = Image.Load(url)imgAtlasMask = New Image(pmAtlasMask)pmAtlas.Discard()EndField imgAtlas:ImageField imgAtlasMask:ImageEndOkay, I’m back to “retirement”…
.
December 18, 2017 at 7:43 am #12348Thanks guys! I’ll run some bench marking to see what is faster, I was considering of doing it “offline” too by creating a simple app which reads in the pixel data and spit it out to a JSON file.
Not sure why you’re creating a canvas and then copying the viewport pixmap of it.
To allow me to read the pixel data from an Image, do you know another way?
December 18, 2017 at 9:07 am #12355Okay bench marking is done
Debug Timing
Monkey12345678910OriginalVersion = 381msOriginalVersion = 381002μsAbakoboVersion = 382msAbakoboVersion = 382499μsimpixiVersion = 5msimpixiVersion = 5000μsNewVersion = 2msNewVersion = 2499μsRelease Timing
Monkey12345678910OriginalVersion = 20msOriginalVersion = 20000μsAbakoboVersion = 18msAbakoboVersion = 18022μsimpixiVersion = 1msimpixiVersion = 501μsNewVersion = 0msNewVersion = 499μsAnd here is the full code:
Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234Namespace myapp#Import "<std>"#Import "<mojo>"#Import "assets/"Using std..Using mojo..Const Size:=New Vec2i( 320,200 )Class MyWindow Extends WindowField tileSet:Image[]Field tileSetMask:Int[,,]Field tileSetClass:TileSetField tileSetClass2:TileSet2Method New()Super.New( "My Window",1024,768,WindowFlags.Resizable )Window.ClearColor = Color.BlackLayout="letterbox"tileSet = LoadSpriteSheet("tileset.png", 32, 16, 16)tileSetMask = New Int[32, 16, 16]Local s := Millisecs()Local n := Microsecs()OriginalVersion()Print "OriginalVersion = " +(Millisecs()-s)Print "OriginalVersion = " +(Microsecs()-n)s = Millisecs()n = Microsecs()AbakoboVersion()Print "AbakoboVersion = " +(Millisecs()-s)Print "AbakoboVersion = " +(Microsecs()-n)s = Millisecs()n = Microsecs()tileSetClass = New TileSet("asset::tileset.png", 16, 16)Print "impixiVersion = " +(Millisecs()-s)Print "impixiVersion = " +(Microsecs()-n)s = Millisecs()n = Microsecs()tileSetClass2 = New TileSet2("tileset.png", 32, 16, 16)Print "NewVersion = " +(Millisecs()-s)Print "NewVersion = " +(Microsecs()-n)EndMethod OnRender( canvas:Canvas ) Overridecanvas.TextureFilteringEnabled = FalseApp.RequestRender()canvas.DrawImage(tileSetClass.imgAtlas, 0, 0)canvas.DrawImage(tileSetClass.imgAtlasMask, tileSetClass.imgAtlas.Width, 0)Local drawTileIndex:Int = 29For Local y := 0 To 16 - 1For Local x := 0 To 16 - 1If drawTileIndex > - 1If tileSetClass2.tileSetMask[drawTileIndex, x, y] > 0canvas.Color = Color.Orangecanvas.DrawRect(x + 150 , y + 150, 1, 1)canvas.Color = Color.WhiteEndEndEndEndEndMethod OnMeasure:Vec2i() OverrideReturn SizeEndMethod AbakoboVersion()For Local i:Int = 0 Until tileSet.LengthLocal tmpCanvas := New Canvas(tileSet[i])Local px := tmpCanvas.CopyPixmap(tmpCanvas.Viewport)Local pixPtr:UInt Ptr 'to declare only once so i's not created each time in the loop (more usefull with structs (like Color) or classes or arrays intanciation...'RGBA8(per channel) is the default pixel format but can be different. 4*8=32bit long thus UIntFor Local y := 0 To px.Height - 1pixPtr=Cast<UInt Ptr>( px.PixelPtr( 0,y ) )For Local x := 0 To px.Width - 1If pixPtr[x]&$FF000000>0 'Bitwise opertor to check if there is some alpha (8 leftside bits of RGBA32(read ABGR! Endianess?) thus &$FF000000 to clear the BGR 24 bits. (is there a problem with the pixel format documentation? or is it reading small from big?)tileSetMask[i, x, y] = 1ElsetileSetMask[i, x, y] = 0EndEndEndNextEndMethod OriginalVersion()For Local i:Int = 0 Until tileSet.LengthLocal tmpCanvas := New Canvas(tileSet[i])Local px := tmpCanvas.CopyPixmap(tmpCanvas.Viewport)For Local y := 0 To px.Height - 1For Local x := 0 To px.Width - 1Local color:Color = px.GetPixel(x, y)If color.A > 0tileSetMask[i, x, y] = 1ElsetileSetMask[i, x, y] = 0EndEndEndNextEndEndClass TileSetPublicMethod New(url:String, tileWidth:Int, tileHeight:Int)Local pmAtlas := Pixmap.Load(url, PixelFormat.Unknown)If Not pmAtlas Then RuntimeError("Could not load spritesheet: " + url)Local tileCount := (pmAtlas.Width / tileWidth) * (pmAtlas.Height / tileHeight)Local pmAtlasMask := New Pixmap(pmAtlas.Width, pmAtlas.Height, PixelFormat.I8)Local pmAtlasDataPtr:UByte Ptr = pmAtlas.Data + Cast<UByte>(pmAtlas.Depth - 1)Local pmAtlasMaskDataPtr:UByte Ptr = pmAtlasMask.Data + Cast<UByte>(pmAtlasMask.Depth - 1)Local pidx:ULong = 0While pidx < (pmAtlas.Width * pmAtlas.Height)If pmAtlasDataPtr[0] > 0pmAtlasMaskDataPtr[0] = 255ElsepmAtlasMaskDataPtr[0] = 0EndifpmAtlasDataPtr += Cast<UByte>(pmAtlas.Depth)pmAtlasMaskDataPtr += Cast<UByte>(pmAtlasMask.Depth)pidx += 1WendimgAtlas = Image.Load(url)imgAtlasMask = New Image(pmAtlasMask)pmAtlas.Discard()EndField imgAtlas:ImageField imgAtlasMask:ImageEndClass TileSet2Field tileSetMask:Int[,,]Field tileSet:Image[]Method New(path:String, numFrames:Int, cellWidth:Int, cellHeight:Int, filter:Bool = True, preScale:Float = 1.0, padding:Int = 0, border:Int = 0, prefix:String = "asset::", format:PixelFormat = PixelFormat.Unknown)Local pixmap := Pixmap.Load(prefix + path, format )Assert( pixmap, " ~n ~nGameGraphics: Pixmap " + path + " not found.~n ~n" )tileSetMask = New Int[numFrames, cellWidth, cellHeight]Local imgs := New Image[ numFrames ]Local atlasImg := New Image( pixmap )Local paddedWidth := cellWidth + ( padding * 2 )Local paddedHeight := cellHeight + ( padding * 2 )Local columns:Int = ( atlasImg.Width - border - border ) / paddedWidthFor Local i := 0 Until numFramesLocal col := i Mod columnsLocal x := ( col * paddedWidth ) + padding + borderLocal y := ( ( i / columns ) * paddedHeight ) + padding + borderimgs[i] = New Image( atlasImg, New Recti( x , y, x + cellWidth, y + cellHeight ) )imgs[i].Scale = New Vec2f( preScale, preScale )For Local k := 0 To cellHeight - 1For Local j := 0 To cellWidth - 1Local color:Color = pixmap.GetPixel(x + j, y + k)'Print "i = " + i + " j = " + j + " k = " + kIf color.A > 0tileSetMask[i, j, k] = 1ElsetileSetMask[i, j, k] = 0EndEndEndNextatlasImg = NulltileSet = imgsEndEndFunction LoadSpriteSheet:Image[] (path:String, numFrames:Int, cellWidth:Int, cellHeight:Int, filter:Bool = True, preScale:Float = 1.0, padding:Int = 0, border:Int = 0, prefix:String = "asset::")Local atlasTexture := Texture.Load(prefix + path, Null )Assert( atlasTexture, " ~n ~nGameGraphics: Image " + path + " not found.~n ~n" )Local imgs := New Image[ numFrames ]Local atlasImg := New Image( atlasTexture )Local paddedWidth := cellWidth + ( padding * 2 )Local paddedHeight := cellHeight + ( padding * 2 )Local columns:Int = ( atlasImg.Width - border - border ) / paddedWidthFor Local i := 0 Until numFramesLocal col := i Mod columnsLocal x := ( col * paddedWidth ) + padding + borderLocal y := ( ( i / columns ) * paddedHeight ) + padding + borderimgs[i] = New Image( atlasImg, New Recti( x , y, x + cellWidth, y + cellHeight ) )imgs[i].Scale = New Vec2f( preScale, preScale )NextatlasImg = NullReturn imgsEndFunction Main()New AppInstanceNew MyWindowApp.Run()EndDecember 18, 2017 at 10:31 am #12357OriginalVersion = 20ms
OriginalVersion = 20000μs
AbakoboVersion = 18ms
AbakoboVersion = 18022μsMmm! Direct pointer usage was not a great gain (but still a gain ? or just cache work? ;). It was the key to speed in a Julia set fractal generator..
Not using Canvas operation was the major key for shure!Note that Image and Pixmap (extends Ressource) are not GCed so you you have to .Discard() them before replacing/loosing them or you’ll leak memory. But as the are not GCed Mark won’t tell it’s not safe to point to them!?December 18, 2017 at 3:24 pm #12358Some small pimping..
now debug:
Monkey12345678910111213141516171819OriginalVersion = 67OriginalVersion = 67176AbakoboVersion = 65AbakoboVersion = 65788impixiVersion = 4impixiVersion = 3503NewVersion = 2NewVersion = 2501Tileset3 = 2Tileset3 = 2002Tileset4 = 1Tileset4 = 501Total microsecs for 100 loops :orig: 6746617abak: 6623185impi: 356210tileset2: 236657tileset3: 189626tileset4: 64550Release:
Monkey12345678910111213141516171819OriginalVersion = 28OriginalVersion = 27316AbakoboVersion = 29AbakoboVersion = 29003impixiVersion = 1impixiVersion = 1004NewVersion = 0NewVersion = 500Tileset3 = 1Tileset3 = 500Tileset4 = 0Tileset4 = 0Total microsecs after 100 loops:orig: 2945291abak: 2924291impi: 101610tileset2: 76025tileset3: 45032tileset4: 26521For the “new” Tileset class I loaded a Padded png (545*18)
Tileset2 is the original “NewVersion”
Tileset3 is just moving color declaration outise of the loop. sometimes it has no/bad effect depending on how the loop is looped (100 times a list of each one or a list of 100 times each one)
Tileset4 uses Pointer to read into memory (sometimes returns 0 microsec (or 500 or 501!?) not shure it work ok but looks like.Monkey123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416Namespace myapp#Import "<std>"#Import "<mojo>"#Import "assets/"Using std..Using mojo..Const Size:=New Vec2i( 320,200 )Class MyWindow Extends WindowField tileSet:Image[]Field tileSetMask:Int[,,]Field tileSetClass:TileSetField tileSetClass2:TileSet2Field tileSetClass3:TileSet3Field tileSetClass4:TileSet4Method New()Super.New( "My Window",1024,768,WindowFlags.Resizable )Window.ClearColor = Color.BlackLayout="letterbox"tileSet = LoadSpriteSheet("tileset.png", 32, 16, 16)tileSetMask = New Int[32, 16, 16]Local s := Millisecs()Local n := Microsecs()Local averageFloor:=100Local orig: LongLocal abak: LongLocal impi: LongLocal new1: LongLocal new2: LongLocal new3: LongFor Local averageCount:=1 To averageFloors = Millisecs()n = Microsecs()OriginalVersion()orig+=Microsecs()-nPrint "OriginalVersion = " +(Millisecs()-s)Print "OriginalVersion = " +(Microsecs()-n)s = Millisecs()n = Microsecs()AbakoboVersion()abak+=Microsecs()-nPrint "AbakoboVersion = " +(Millisecs()-s)Print "AbakoboVersion = " +(Microsecs()-n)s = Millisecs()n = Microsecs()tileSetClass = New TileSet("asset::tileset.png", 16, 16)impi+=Microsecs()-nPrint "impixiVersion = " +(Millisecs()-s)Print "impixiVersion = " +(Microsecs()-n)s = Millisecs()n = Microsecs()tileSetClass2 = New TileSet2("tilesetpad.png", 32, 16, 16)new1+=Microsecs()-nPrint "NewVersion = " +(Millisecs()-s)Print "NewVersion = " +(Microsecs()-n)s = Millisecs()n = Microsecs()tileSetClass3 = New TileSet3("tilesetpad.png", 32, 16, 16)new2+=Microsecs()-nPrint "Tileset3 = " +(Millisecs()-s)Print "Tileset3 = " +(Microsecs()-n)s = Millisecs()n = Microsecs()tileSetClass4 = New TileSet4("tilesetpad.png", 32, 16, 16)new3+=Microsecs()-nPrint "Tileset4 = " +(Millisecs()-s)Print "Tileset4 = " +(Microsecs()-n)Print averageCount+" percent done."Print averageCount+" percent done."Print averageCount+" percent done."Print averageCount+" percent done."Print averageCount+" percent done."NextPrint "orig: "+origPrint "abak: "+abakPrint "impi: "+impiPrint "tileset2: "+new1Print "tileset3: "+new2Print "tileset4: "+new3EndMethod OnRender( canvas:Canvas ) Overridecanvas.TextureFilteringEnabled = FalseApp.RequestRender()canvas.DrawImage(tileSetClass.imgAtlas, 0, 0)canvas.DrawImage(tileSetClass.imgAtlasMask, tileSetClass.imgAtlas.Width, 0)Local drawTileIndex:Int = 29For Local y := 0 To 16 - 1For Local x := 0 To 16 - 1If drawTileIndex > - 1If tileSetClass2.tileSetMask[drawTileIndex, x, y] > 0canvas.Color = Color.Orangecanvas.DrawRect(x + 150 , y + 150, 1, 1)canvas.Color = Color.WhiteEndEndEndEndEndMethod OnMeasure:Vec2i() OverrideReturn SizeEndMethod AbakoboVersion()For Local i:Int = 0 Until tileSet.LengthLocal tmpCanvas := New Canvas(tileSet[i])Local px := tmpCanvas.CopyPixmap(tmpCanvas.Viewport)Local pixPtr:UInt Ptr 'to declare only once so i's not created each time in the loop (more usefull with structs (like Color) or classes or arrays intanciation...'RGBA8(per channel) is the default pixel format but can be different. 4*8=32bit long thus UIntFor Local y := 0 To px.Height - 1pixPtr=Cast<UInt Ptr>( px.PixelPtr( 0,y ) )For Local x := 0 To px.Width - 1If pixPtr[x]&$FF000000>0 'Bitwise opertor to check if there is some alpha (8 leftside bits of RGBA32(read ABGR! Endianess?) thus &$FF000000 to clear the BGR 24 bits. (is there a problem with the pixel format documentation? or is it reading small from big?)tileSetMask[i, x, y] = 1ElsetileSetMask[i, x, y] = 0EndEndEndNextEndMethod OriginalVersion()For Local i:Int = 0 Until tileSet.LengthLocal tmpCanvas := New Canvas(tileSet[i])Local px := tmpCanvas.CopyPixmap(tmpCanvas.Viewport)For Local y := 0 To px.Height - 1For Local x := 0 To px.Width - 1Local color:Color = px.GetPixel(x, y)If color.A > 0tileSetMask[i, x, y] = 1ElsetileSetMask[i, x, y] = 0EndEndEndNextEndEndClass TileSetPublicMethod New(url:String, tileWidth:Int, tileHeight:Int)Local pmAtlas := Pixmap.Load(url, PixelFormat.Unknown)If Not pmAtlas Then RuntimeError("Could not load spritesheet: " + url)Local tileCount := (pmAtlas.Width / tileWidth) * (pmAtlas.Height / tileHeight)Local pmAtlasMask := New Pixmap(pmAtlas.Width, pmAtlas.Height, PixelFormat.I8)Local pmAtlasDataPtr:UByte Ptr = pmAtlas.Data + Cast<UByte>(pmAtlas.Depth - 1)Local pmAtlasMaskDataPtr:UByte Ptr = pmAtlasMask.Data + Cast<UByte>(pmAtlasMask.Depth - 1)Local pidx:ULong = 0While pidx < (pmAtlas.Width * pmAtlas.Height)If pmAtlasDataPtr[0] > 0pmAtlasMaskDataPtr[0] = 255ElsepmAtlasMaskDataPtr[0] = 0EndifpmAtlasDataPtr += Cast<UByte>(pmAtlas.Depth)pmAtlasMaskDataPtr += Cast<UByte>(pmAtlasMask.Depth)pidx += 1WendimgAtlas = Image.Load(url)imgAtlasMask = New Image(pmAtlasMask)pmAtlas.Discard()EndField imgAtlas:ImageField imgAtlasMask:ImageEndClass TileSet2Field tileSetMask:Int[,,]Field tileSet:Image[]Method New(path:String, numFrames:Int, cellWidth:Int, cellHeight:Int, filter:Bool = True, preScale:Float = 1.0, padding:Int = 0, border:Int = 0, prefix:String = "asset::", format:PixelFormat = PixelFormat.Unknown)Local pixmap := Pixmap.Load(prefix + path, format )Assert( pixmap, " ~n ~nGameGraphics: Pixmap " + path + " not found.~n ~n" )tileSetMask = New Int[numFrames, cellWidth, cellHeight]Local imgs := New Image[ numFrames ]Local atlasImg := New Image( pixmap )Local paddedWidth := cellWidth + ( padding * 2 )Local paddedHeight := cellHeight + ( padding * 2 )Local columns:Int = ( atlasImg.Width - border - border ) / paddedWidthFor Local i := 0 Until numFramesLocal col := i Mod columnsLocal x := ( col * paddedWidth ) + padding + borderLocal y := ( ( i / columns ) * paddedHeight ) + padding + borderimgs[i] = New Image( atlasImg, New Recti( x , y, x + cellWidth, y + cellHeight ) )imgs[i].Scale = New Vec2f( preScale, preScale )For Local k := 0 To cellHeight - 1For Local j := 0 To cellWidth - 1Local color:Color = pixmap.GetPixel(x + j, y + k)'Print "i = " + i + " j = " + j + " k = " + kIf color.A > 0tileSetMask[i, j, k] = 1ElsetileSetMask[i, j, k] = 0EndEndEndNextatlasImg = NulltileSet = imgsEndEndClass TileSet3Field tileSetMask:Int[,,]Field tileSet:Image[]Method New(path:String, numFrames:Int, cellWidth:Int, cellHeight:Int, filter:Bool = True, preScale:Float = 1.0, padding:Int = 0, border:Int = 0, prefix:String = "asset::", format:PixelFormat = PixelFormat.Unknown)Local pixmap := Pixmap.Load(prefix + path, format )Assert( pixmap, " ~n ~nGameGraphics: Pixmap " + path + " not found.~n ~n" )If pixmap.Format<>PixelFormat.RGBA8Print "wrong pixel format need RGBA8"ElsetileSetMask = New Int[numFrames, cellWidth, cellHeight]Local imgs := New Image[ numFrames ]Local atlasImg := New Image( pixmap )Local paddedWidth := cellWidth + ( padding * 2 )Local paddedHeight := cellHeight + ( padding * 2 )Local columns:Int = ( atlasImg.Width - border - border ) / paddedWidthFor Local i := 0 Until numFramesLocal col := i Mod columnsLocal x := ( col * paddedWidth ) + padding + borderLocal y := ( ( i / columns ) * paddedHeight ) + padding + borderimgs[i] = New Image( atlasImg, New Recti( x , y, x + cellWidth, y + cellHeight ) )imgs[i].Scale = New Vec2f( preScale, preScale )Local color:ColorFor Local k := 0 To cellHeight - 1For Local j := 0 To cellWidth - 1color = pixmap.GetPixel(x + j, y + k)'Print "i = " + i + " j = " + j + " k = " + kIf color.A > 0tileSetMask[i, j, k] = 1ElsetileSetMask[i, j, k] = 0EndEndEndNextatlasImg = NulltileSet = imgsEndEndEndClass TileSet4Field tileSetMask:Int[,,]Field tileSet:Image[]Method New(path:String, numFrames:Int, cellWidth:Int, cellHeight:Int, filter:Bool = True, preScale:Float = 1.0, padding:Int = 0, border:Int = 0, prefix:String = "asset::", format:PixelFormat = PixelFormat.Unknown)Local pixmap := Pixmap.Load(prefix + path, format )Assert( pixmap, " ~n ~nGameGraphics: Pixmap " + path + " not found.~n ~n" )If pixmap.Format<>PixelFormat.RGBA8Print "wrong pixel format need RGBA8"ElsetileSetMask = New Int[numFrames, cellWidth, cellHeight]Local imgs := New Image[ numFrames ]Local atlasImg := New Image( pixmap )Local paddedWidth := cellWidth + ( padding * 2 )Local paddedHeight := cellHeight + ( padding * 2 )Local columns:Int = ( atlasImg.Width - border - border ) / paddedWidthFor Local i := 0 Until numFramesLocal col := i Mod columnsLocal x := ( col * paddedWidth ) + padding + borderLocal y := ( ( i / columns ) * paddedHeight ) + padding + borderimgs[i] = New Image( atlasImg, New Recti( x , y, x + cellWidth, y + cellHeight ) )imgs[i].Scale = New Vec2f( preScale, preScale )'Local color:ColorLocal pixPtr:UInt PtrFor Local k := 0 To cellHeight - 1pixPtr=Cast<UInt Ptr>( pixmap.PixelPtr( x,y+k ) )For Local j := 0 To cellWidth - 1'color = pixmap.GetPixel(x + j, y + k)'Print "i = " + i + " j = " + j + " k = " + kIf pixPtr[j]&$FF000000>0tileSetMask[i, j, k] = 1ElsetileSetMask[i, j, k] = 0EndEndEnd#remLocal pixPtr:UInt Ptr 'to declare only once so i's not created each time in the loop (more usefull with structs (like Color) or classes or arrays intanciation...'RGBA8(per channel) is the default pixel format but can be different. 4*8=32bit long thus UIntFor Local y := 0 To px.Height - 1pixPtr=Cast<UInt Ptr>( px.PixelPtr( 0,y ) )For Local x := 0 To px.Width - 1If pixPtr[x]&$FF000000>0 'Bitwise opertor to check if there is some alpha (8 leftside bits of RGBA32(read ABGR! Endianess?) thus &$FF000000 to clear the BGR 24 bits. (is there a problem with the pixel format documentation? or is it reading small from big?)tileSetMask[i, x, y] = 1ElsetileSetMask[i, x, y] = 0EndEnd#EndNextatlasImg = NulltileSet = imgsEndEndEndFunction LoadSpriteSheet:Image[] (path:String, numFrames:Int, cellWidth:Int, cellHeight:Int, filter:Bool = True, preScale:Float = 1.0, padding:Int = 0, border:Int = 0, prefix:String = "asset::")Local atlasTexture := Texture.Load(prefix + path, Null )Assert( atlasTexture, " ~n ~nGameGraphics: Image " + path + " not found.~n ~n" )Local imgs := New Image[ numFrames ]Local atlasImg := New Image( atlasTexture )Local paddedWidth := cellWidth + ( padding * 2 )Local paddedHeight := cellHeight + ( padding * 2 )Local columns:Int = ( atlasImg.Width - border - border ) / paddedWidthFor Local i := 0 Until numFramesLocal col := i Mod columnsLocal x := ( col * paddedWidth ) + padding + borderLocal y := ( ( i / columns ) * paddedHeight ) + padding + borderimgs[i] = New Image( atlasImg, New Recti( x , y, x + cellWidth, y + cellHeight ) )imgs[i].Scale = New Vec2f( preScale, preScale )NextatlasImg = NullReturn imgsEndFunction Main()New AppInstanceNew MyWindowApp.Run()EndAttachments:
December 19, 2017 at 7:27 am #12369Thanks Abakobo!
Note that Image and Pixmap (extends Ressource) are not GCed so you you have to .Discard() them before replacing/loosing them or you’ll leak memory. But as the are not GCed Mark won’t tell it’s not safe to point to them!?
Hmmm, I thought I read Mark had changed the GC…
Edit:
On Mark’s Blog:This means it’s not strictly necessary to use Discard to cleanup resources any more, GC will eventually do it for you. GCCollect() is still slow so Discard is recommended in realtime situations, but on the whole it should be possible to write apps without worrying about using Discard at all now.
December 19, 2017 at 9:18 am #12371Hmmm, I thought I read Mark had changed the GC…
Oops my bad, was not aware of that! Might be unsafe to play with pointers then. Using a very local pointer should do the trick though.
December 20, 2017 at 7:33 am #12383No worries! Sometimes its hard to keep up with all the changes – thanks for helping!
 - 
		AuthorPosts
 
You must be logged in to reply to this topic.