import { BaseTexture , Texture } from '@pixi/core' ; import { GuilloteneAllocator } from '@pixi-essentials/area-allocator' ; import { Rectangle } from '@pixi/math' ; import type { TextureSlab } from './TextureSlab' ; const tempRect = new Rectangle ( ) ; public export class TextureAllocator < T extends Texture = Texture > { /**
     * The width of texture slabs.
     */ public readonly slabWidth : number ; /**
     * The height of texture slabs.
     */ public readonly slabHeight : number ; /**
     * The list of base-textures that are used to allocate texture space.
     */ protected textureSlabs : TextureSlab [ ] ; param slabWidth param slabHeight constructor ( slabWidth = 2048 , slabHeight = 2048 ) { this . slabWidth = slabWidth ; this . slabHeight = slabHeight ; this . textureSlabs = [ ] ; } get maxWidth ( ) { return this . slabWidth - ( 2 * this . calculatePadding ( this . slabWidth , this . slabHeight ) ) ; } get maxHeight ( ) { return this . slabHeight - ( 2 * this . calculatePadding ( this . slabWidth , this . slabHeight ) ) ; } /**
     * Allocates a texture from this allocator.
     *
     * If its existing slab pool has enough space, the texture is issued from one. Otherwise,
     * a new slab is created and the texture is issued from it. However, if the requested
     * dimensions are larger than slabs themselves, then `null` is always returned.
     *
     * To upload a texture source, you will have to create an atlas-managing  Resource * yourself on the base-texture. The  AtlasAllocator does this for you, while the
     *  CanvasTextureAllocator can be used to draw on a canvas-based atlas.
     *
     *  param width param height param padding return allocate ( width , height , padding = this . calculatePadding ( width , height ) ) { if ( width + 2 * padding > this . slabWidth || height + 2 * padding > this . slabHeight ) { return null ; } const slabs = this . textureSlabs ; for ( let i = 0 , j = slabs . length ; i < j ; i ++ ) { const slab = slabs [ i ] ; const texture = this . issueTexture ( slab , width , height , padding ) ; if ( texture ) { return texture ; } } const slab = this . createSlab ( ) ; this . textureSlabs . unshift ( slab ) ; return this . issueTexture ( slab , width , height , padding ) ; } param texture throws free ( texture ) { const baseTexture = ( texture as Texture ) . baseTexture ; const slab = this . textureSlabs . find ( sl => sl . slab === baseTexture ) ; if ( ! slab ) { throw new Error ( "The texture cannot be freed because " + "its base-texture is not pooled by this allocator. " + "This is either a bug in TextureAllocator or you tried to free a " + "texture that was never allocated by one." ) ; } const textureEntry = slab . managedTextures . find ( entry => entry . texture === texture ) ; if ( ! textureEntry ) { throw new Error ( "The texture cannot be freed because it was not found " + "in the managed list of issued textures on its slab. This may be because you " + "duplicated this texture or a bug in TextureAllocator" ) ; } slab . managedArea . free ( textureEntry . area ) ; slab . managedTextures . splice ( slab . managedTextures . indexOf ( textureEntry ) , 1 ) ; } protected calculatePadding ( width , height ) { const dimen = Math . max ( width , height ) ; if ( dimen < 64 ) { return 2 ; } else if ( dimen < 128 ) { return 4 ; } else if ( dimen < 1024 ) { return 8 ; } else { return 16 ; } } /**
     * Creates a texture slab. The slab's base-texture is not backed by any resource. You
     * will have to manage that yourself. See  AtlasAllocator or  CanvasTextureAllocator * for better resource semantics.
      protected createSlab ( ) { return { managedArea : new GuilloteneAllocator ( this . slabWidth , this . slabHeight ) , managedTextures : [ ] , slab : new BaseTexture ( null , { width : this . slabWidth , height : this . slabHeight , } ) , } ; } param baseTexture param frame protected createTexture ( baseTexture , frame ) { return new Texture ( baseTexture , frame ) as T ; } param slab param width param height param padding return protected issueTexture ( slab , width , height , padding = 0 ) { const area = slab . managedArea . allocate ( width + 2 * padding , height + 2 * padding ) ; if ( ! area ) { return null ; } tempRect . copyFrom ( area ) ; tempRect . pad ( - padding ) ; const baseTexture = slab . slab ; const issuedTexture = this . createTexture ( baseTexture , tempRect . clone ( ) ) ; slab . managedTextures . push ( { area , texture : issuedTexture , } ) ; return issuedTexture ; } }