Code archives/Graphics/image cache across canvas (alternative to GLShareContexts on multiple canvas)
This code has been declared by its author to be Public Domain code.
Download source code
| With this module you can use it to load images and cache them. Subsequent calls to load an image of the same path or pixmap memory address will recognise the previously loaded resource. Depending on the mode the module will either increase a reference count for the image or create an entirely new copy in memory. This is perfect for "sharing" the same image across multiple canvas when you want to support a wider range of hardware. |
The alternative is to use glsharecontexts but this is not very well supported on some hardware!
If you enable glsharecontexts in your code remember to call SetCacheShared(True). This will instruct the cache module to reuse image data instead of duplicating it. SetCacheShared() should be called before loading any images.
Local pointer1:Skn3CachePointer = LoadCache("image.png") Local pointer2:Skn3CachePointer = pointer1.copy() 'on canvas 1 pointer1.Draw(5,5) 'on canvas 2 pointer2.Draw(5,5) 'change the image 'this will update all images that are related (even if those images are infact copies in memory) pointer1.Change("iamge2.png") 'on canvas 1 pointer1.Draw(5,5) 'on canvas 2 pointer2.Draw(5,5)
Module skn3.cache SuperStrict 'imports Import brl.map Import brl.linkedlist Import brl.pixmap Import brl.glmax2d 'internal Private Global cacheGroups:TMap = CreateMap() Global cacheShared:Int = False Global cacheImageFlags:Int = 0 Public 'classes Type Skn3CacheGroup Field path:String Field pixmap:Int = False Field instances:TList = CreateList() End Type Type Skn3CacheInstance Field group:Skn3CacheGroup Field image:TImage Field flags:Int Field refCount:Int Field link:TLink Field pointers:TList = CreateList() 'internal Method AddPointer:Skn3CachePointer() ' --- return a new pointer to this instance --- Local pointer:Skn3CachePointer = New Skn3CachePointer pointer.instance = Self pointer.link = pointers.AddLast(pointer) Return pointer End Method Method copy:Skn3CacheInstance() ' --- create a copy of this particular instance --- 'reuse or copy instances If cacheShared = True 'reuse 'increase reference count refCount :+ 1 'return instance Return Self Else 'create new instance Local instance:Skn3CacheInstance = New Skn3CacheInstance instance.group = group instance.image = LoadImage(image.pixmaps,flags) instance.flags = flags instance.refCount = 1 instance.link = group.instances.AddLast(instance) 'return instance Return instance EndIf End Method End Type Type Skn3CachePointer 'this is a "handle" which should be stored by things using the cache. 'by storing this handle it means the internal data within can change without having to update the code using cache 'eg a new image, instance, blah can be swapped in and code can still use cache.image to reference the image 'the module will handle the pointers and such! 'the pointer should be used for all external operations as that way we can prevent any issues with referenceing a nulled instance Field instance:Skn3CacheInstance Field link:TLink 'api Method Free() ' --- this will free the instance the pointer points to --- If instance 'remove the pointer from instance link.remove() 'decrease instance reference count and see if the instance should then be freed instance.refCount :- 1 If instance.refCount = 0 'remove all other pointers from the instance If instance.pointers.isEmpty() = False For Local pointer:Skn3CachePointer = EachIn instance.pointers pointer.instance = Null pointer.link = Null Next instance.pointers.Clear() EndIf 'free the image instance.image = Null instance.flags = 0 'remove from cache group instance.link.remove() instance.group = Null 'remove cache group if its instances are empty If instance.group.instances.isEmpty() cacheGroups.remove(instance.group.path) EndIf 'null the pointer instance = Null link = Null EndIf End Method Method copy:Skn3CachePointer() ' --- this will create a copy of the instance and return a new pointer --- If instance Return instance.copy().AddPointer() End Method Method Change(url:Object) ' --- this will change the cached image using the supplied pixmap --- 'this function will go through and make sweeping changes, all instances and pointers to instances will now be altered! 'we can only change if the pointer is still pointing If instance Local newGroup:Skn3CacheGroup Local oldInstance:Skn3CacheInstance Local oldPointer:Skn3CachePointer Local newInstance:Skn3CacheInstance 'check if it is a pixmap or file Local cachePath:String Local pixmap:TPixmap = TPixmap(url) Local path:String If pixmap = Null 'get cache path path = String(url) cachePath = "file::"+path.ToLower() Else 'get cache path cachePath = "pixmap::"+String(Long(pixmap.Pixels)) EndIf 'check to see if there is an existing group and that teh group is not the same as the current one newGroup = Skn3CacheGroup(cacheGroups.ValueForKey(cachePath)) If newGroup <> Null And instance.group = newGroup Return 'check if we are changing to the group or creating a new one If newGroup 'the image is currently cached so we just need to do a mass alteration and reinstance Local oldGroup:Skn3CacheGroup = instance.group 'scan all group instances (connected to THIS pointer .. not the existing one... ok .. got that ? :D) For oldInstance = EachIn oldGroup.instances 'create a new instance from the existing group newInstance = Skn3CacheInstance(newGroup.instances.First()).copy() 'update all of the pointers For oldPointer = EachIn oldInstance.pointers 'update oldPointer.instance = newInstance oldPointer.link = newInstance.pointers.AddLast(oldPointer) Next Next 'get rid of the old group cacheGroups.remove(oldGroup.path) Else 'the image is not currently cached so we have to load it and alter the existing cache 'if there is no pixmap Then Load it now, this is if a path is provided If pixmap = Null pixmap = LoadPixmap(path) 'update all the instance images For oldInstance = EachIn instance.group.instances oldInstance.image = LoadImage(pixmap,oldInstance.flags) Next 'update the group cache cacheGroups.remove(instance.group.path) instance.group.path = cachePath cacheGroups.Insert(cachePath,instance.group) EndIf EndIf End Method Method Draw(x:Float,y:Float) ' --- draw the image --- If instance DrawImage(instance.image,x,y) End Method Method DrawPortion(destinationX:Float,destinationY:Float,destinationWidth:Float,destinationHeight:Float,sourceX:Float,sourceY:Float,sourceWidth:Float=0,sourceHeight:Float=0) ' --- draw a portion of the image --- If instance DrawSubImageRect(instance.image,destinationX,destinationY,destinationWidth,destinationHeight,sourceX,sourceY,sourceWidth,sourceHeight) End Method Method Tile(x:Float,y:Float) ' --- tile the image --- If instance TileImage(instance.image,x,y) End Method Method Width:Int() ' -- get width of image --- If instance Return instance.image.Width End Method Method Height:Int() ' -- get height of image --- If instance Return instance.image.Height End Method End Type 'api Function LoadCache:Skn3CachePointer(url:Object) ' --- cache an image from disk --- Local cachePath:String 'check if it is a pixmap or file Local pixmap:TPixmap = TPixmap(url) Local path:String If pixmap = Null 'get cache path path = String(url) cachePath = "file::"+path.ToLower() Else 'get cache path cachePath = "pixmap::"+String(Long(pixmap.Pixels)) EndIf 'look for existing map Local group:Skn3CacheGroup = Skn3CacheGroup(cachegroups.ValueForKey(cachePath)) 'create new one if it doesn't exist If group = Null 'load the timage object from pixmap or path Local image:TImage If pixmap = Null image = LoadImage(path,cacheImageFlags) Else image = LoadImage(pixmap,cacheImageFlags) EndIf 'make sure the image loaded If image = Null Return Null 'create the cache group group = New Skn3CacheGroup group.pixmap = pixmap <> Null group.path = cachePath 'add the group to the global cache map cacheGroups.Insert(group.path,group) 'create first instance Local instance:Skn3CacheInstance = New Skn3CacheInstance instance.group = group instance.image = image instance.flags = cacheImageFlags instance.refCount = 1 'add the instance to the group instance.link = group.instances.AddLast(instance) 'return a pointer to the instance Return instance.AddPointer() Else 'reuse or copy instances and return a new pointer to it 'all in one swoop we will return a pointer to duplicated/referenced instance Return Skn3CacheInstance(group.instances.First()).copy().AddPointer() EndIf End Function Function SetCacheShared(shared:Int) ' --- specify if images should be reference counted instead of duplicated --- 'this will only alter image data loaded after calling teh command cacheShared = shared End Function
Code Archives Forum