0
# Texture Management
1
2
Comprehensive texture loading, caching, atlas management, procedural texture generation, and sprite sheet handling for efficient graphics resource management and optimal rendering performance.
3
4
## Capabilities
5
6
### Core Texture Classes
7
8
Base texture classes for managing image data and rendering properties.
9
10
```python { .api }
11
class Texture:
12
"""
13
Main texture class for managing image data, rendering properties, and GPU resources.
14
Textures can be loaded from files, created programmatically, or extracted from sprite sheets.
15
"""
16
def __init__(self, image: PIL.Image.Image, name: str = None, hit_box_algorithm: str = "Simple",
17
hit_box_detail: float = 4.5, hit_box_points: list[tuple[float, float]] = None):
18
"""
19
Create a texture from a PIL Image.
20
21
Args:
22
image: PIL Image object containing texture data
23
name: Optional name for the texture (used for caching/debugging)
24
hit_box_algorithm: Algorithm for calculating hit box ("Simple", "Detailed", or "None")
25
hit_box_detail: Detail level for "Detailed" hit box algorithm
26
hit_box_points: Pre-calculated hit box points
27
"""
28
29
# Properties
30
name: str
31
width: int
32
height: int
33
image: PIL.Image.Image
34
hit_box_points: list[tuple[float, float]]
35
36
# Atlas properties
37
atlas: arcade.TextureAtlasBase
38
atlas_name: str
39
40
def create_empty(name: str, size: tuple[int, int], color: tuple[int, int, int, int] = (0, 0, 0, 0)) -> arcade.Texture:
41
"""
42
Create an empty texture with specified size and color.
43
44
Args:
45
name: Name for the texture
46
size: (width, height) in pixels
47
color: RGBA color tuple (0-255)
48
49
Returns:
50
New Texture instance
51
"""
52
53
def create_filled(name: str, size: tuple[int, int], color: tuple[int, int, int, int]) -> arcade.Texture:
54
"""
55
Create a texture filled with a solid color.
56
57
Args:
58
name: Name for the texture
59
size: (width, height) in pixels
60
color: RGBA color tuple (0-255)
61
62
Returns:
63
New Texture instance
64
"""
65
66
def flip_horizontally(self) -> arcade.Texture:
67
"""
68
Create a horizontally flipped version of this texture.
69
70
Returns:
71
New Texture instance with flipped image
72
"""
73
74
def flip_vertically(self) -> arcade.Texture:
75
"""
76
Create a vertically flipped version of this texture.
77
78
Returns:
79
New Texture instance with flipped image
80
"""
81
82
def flip_diagonally(self) -> arcade.Texture:
83
"""
84
Create a diagonally flipped version of this texture.
85
86
Returns:
87
New Texture instance with flipped image
88
"""
89
90
def rotate_90(self) -> arcade.Texture:
91
"""
92
Create a texture rotated 90 degrees clockwise.
93
94
Returns:
95
New Texture instance with rotated image
96
"""
97
98
def crop(self, x: int, y: int, width: int, height: int) -> arcade.Texture:
99
"""
100
Create a cropped version of this texture.
101
102
Args:
103
x: Left coordinate of crop area
104
y: Bottom coordinate of crop area
105
width: Width of crop area
106
height: Height of crop area
107
108
Returns:
109
New Texture instance with cropped image
110
"""
111
112
def resize(self, new_size: tuple[int, int], resample: int = PIL.Image.LANCZOS) -> arcade.Texture:
113
"""
114
Create a resized version of this texture.
115
116
Args:
117
new_size: (width, height) for resized texture
118
resample: PIL resampling algorithm
119
120
Returns:
121
New Texture instance with resized image
122
"""
123
124
class TextureCacheManager:
125
"""
126
Manages texture caching and memory usage to prevent duplicate texture loading
127
and optimize GPU memory usage.
128
"""
129
def __init__(self):
130
"""Create a texture cache manager."""
131
132
def get_texture_by_name(self, name: str) -> arcade.Texture:
133
"""
134
Get a cached texture by name.
135
136
Args:
137
name: Name of the texture to retrieve
138
139
Returns:
140
Cached Texture instance or None if not found
141
"""
142
143
def put_texture(self, name: str, texture: arcade.Texture) -> None:
144
"""
145
Store a texture in the cache.
146
147
Args:
148
name: Cache key for the texture
149
texture: Texture instance to cache
150
"""
151
152
def clear_cache(self) -> None:
153
"""Clear all cached textures from memory."""
154
155
def get_cache_size(self) -> int:
156
"""
157
Get the number of textures currently cached.
158
159
Returns:
160
Number of cached textures
161
"""
162
```
163
164
### Texture Loading Functions
165
166
Functions for loading textures from files and resources with various options and transformations.
167
168
```python { .api }
169
def load_texture(file_path: str, x: int = 0, y: int = 0, width: int = 0, height: int = 0,
170
flipped_horizontally: bool = False, flipped_vertically: bool = False,
171
flipped_diagonally: bool = False, can_cache: bool = True,
172
hit_box_algorithm: str = "Simple", hit_box_detail: float = 4.5) -> arcade.Texture:
173
"""
174
Load a texture from a file with optional transformations and cropping.
175
176
Args:
177
file_path: Path to image file or resource handle (e.g., ":resources:images/...")
178
x: X offset for cropping (pixels from left)
179
y: Y offset for cropping (pixels from bottom)
180
width: Width of crop area (0 = full width)
181
height: Height of crop area (0 = full height)
182
flipped_horizontally: Mirror texture horizontally
183
flipped_vertically: Mirror texture vertically
184
flipped_diagonally: Mirror texture diagonally
185
can_cache: Whether this texture can be cached
186
hit_box_algorithm: Hit box calculation method
187
hit_box_detail: Detail level for detailed hit boxes
188
189
Returns:
190
Loaded Texture instance
191
"""
192
193
def load_image(file_path: str) -> PIL.Image.Image:
194
"""
195
Load an image file as a PIL Image object.
196
197
Args:
198
file_path: Path to image file
199
200
Returns:
201
PIL Image object
202
"""
203
204
def load_spritesheet(file_path: str, sprite_width: int, sprite_height: int,
205
columns: int, count: int, margin: int = 0, spacing: int = 0) -> list[arcade.Texture]:
206
"""
207
Load a sprite sheet and return individual sprites as textures.
208
209
Args:
210
file_path: Path to sprite sheet image
211
sprite_width: Width of each sprite in pixels
212
sprite_height: Height of each sprite in pixels
213
columns: Number of columns in the sprite sheet
214
count: Total number of sprites to load
215
margin: Margin around the entire sprite sheet
216
spacing: Spacing between individual sprites
217
218
Returns:
219
List of Texture instances, one for each sprite
220
"""
221
222
def get_default_texture() -> arcade.Texture:
223
"""
224
Get the default fallback texture (used when texture loading fails).
225
226
Returns:
227
Default Texture instance (usually a colored square)
228
"""
229
230
def get_default_image() -> PIL.Image.Image:
231
"""
232
Get the default fallback image.
233
234
Returns:
235
Default PIL Image object
236
"""
237
```
238
239
### Procedural Texture Generation
240
241
Functions for creating textures programmatically with various shapes, patterns, and effects.
242
243
```python { .api }
244
def make_circle_texture(diameter: int, color: arcade.types.Color, name: str = "circle") -> arcade.Texture:
245
"""
246
Generate a circular texture with solid color.
247
248
Args:
249
diameter: Diameter of the circle in pixels
250
color: Circle color as RGB or RGBA tuple (0-255)
251
name: Name for the generated texture
252
253
Returns:
254
Generated Texture instance
255
"""
256
257
def make_soft_circle_texture(diameter: int, color: arcade.types.Color,
258
center_alpha: int = 255, outer_alpha: int = 0,
259
name: str = "soft_circle") -> arcade.Texture:
260
"""
261
Generate a circular texture with soft, gradient edges.
262
263
Args:
264
diameter: Diameter of the circle in pixels
265
color: Base color as RGB tuple (0-255)
266
center_alpha: Alpha value at circle center (0-255)
267
outer_alpha: Alpha value at circle edge (0-255)
268
name: Name for the generated texture
269
270
Returns:
271
Generated Texture instance with gradient alpha
272
"""
273
274
def make_soft_square_texture(size: int, color: arcade.types.Color,
275
center_alpha: int = 255, outer_alpha: int = 0,
276
name: str = "soft_square") -> arcade.Texture:
277
"""
278
Generate a square texture with soft, gradient edges.
279
280
Args:
281
size: Size of the square in pixels
282
color: Base color as RGB tuple (0-255)
283
center_alpha: Alpha value at square center (0-255)
284
outer_alpha: Alpha value at square edges (0-255)
285
name: Name for the generated texture
286
287
Returns:
288
Generated Texture instance with gradient alpha
289
"""
290
```
291
292
### Sprite Sheet Management
293
294
Classes and functions for working with sprite sheets and texture atlases.
295
296
```python { .api }
297
class SpriteSheet:
298
"""
299
Manages sprite sheets for extracting individual sprites and optimizing texture usage.
300
"""
301
def __init__(self, file_path: str, sprite_width: int, sprite_height: int,
302
columns: int = None, rows: int = None, margin: int = 0, spacing: int = 0):
303
"""
304
Create a sprite sheet manager.
305
306
Args:
307
file_path: Path to sprite sheet image file
308
sprite_width: Width of each individual sprite
309
sprite_height: Height of each individual sprite
310
columns: Number of columns (auto-calculated if None)
311
rows: Number of rows (auto-calculated if None)
312
margin: Margin around the entire sheet
313
spacing: Spacing between individual sprites
314
"""
315
316
# Properties
317
file_path: str
318
sprite_width: int
319
sprite_height: int
320
columns: int
321
rows: int
322
margin: int
323
spacing: int
324
325
def get_texture(self, column: int, row: int) -> arcade.Texture:
326
"""
327
Extract a single sprite texture from the sheet.
328
329
Args:
330
column: Column index (0-based)
331
row: Row index (0-based)
332
333
Returns:
334
Texture for the sprite at the specified position
335
"""
336
337
def get_textures(self, start_column: int = 0, start_row: int = 0,
338
end_column: int = None, end_row: int = None) -> list[arcade.Texture]:
339
"""
340
Extract multiple sprite textures from a range.
341
342
Args:
343
start_column: Starting column (inclusive)
344
start_row: Starting row (inclusive)
345
end_column: Ending column (inclusive, None = last column)
346
end_row: Ending row (inclusive, None = last row)
347
348
Returns:
349
List of Texture instances for the specified range
350
"""
351
352
def get_texture_list(self) -> list[arcade.Texture]:
353
"""
354
Get all sprites from the sheet as a list.
355
356
Returns:
357
List of all Texture instances in the sheet
358
"""
359
```
360
361
### Texture Atlas System
362
363
Advanced texture management for optimizing GPU memory and rendering performance.
364
365
```python { .api }
366
class DefaultTextureAtlas:
367
"""
368
Default texture atlas implementation for batching textures into a single GPU texture
369
to improve rendering performance and reduce GPU memory usage.
370
"""
371
def __init__(self, size: tuple[int, int] = (2048, 2048), auto_resize: bool = True,
372
ctx: arcade.ArcadeContext = None):
373
"""
374
Create a texture atlas.
375
376
Args:
377
size: (width, height) of the atlas texture in pixels
378
auto_resize: Whether to automatically resize when full
379
ctx: OpenGL context (uses current context if None)
380
"""
381
382
# Properties
383
size: tuple[int, int]
384
auto_resize: bool
385
ctx: arcade.ArcadeContext
386
387
def add(self, texture: arcade.Texture) -> bool:
388
"""
389
Add a texture to the atlas.
390
391
Args:
392
texture: Texture to add
393
394
Returns:
395
True if successfully added, False if atlas is full
396
"""
397
398
def remove(self, texture: arcade.Texture) -> bool:
399
"""
400
Remove a texture from the atlas.
401
402
Args:
403
texture: Texture to remove
404
405
Returns:
406
True if successfully removed, False if not found
407
"""
408
409
def clear(self) -> None:
410
"""Remove all textures from the atlas."""
411
412
def resize(self, new_size: tuple[int, int]) -> None:
413
"""
414
Resize the atlas texture.
415
416
Args:
417
new_size: New (width, height) for the atlas
418
"""
419
420
def get_texture_count(self) -> int:
421
"""
422
Get the number of textures currently in the atlas.
423
424
Returns:
425
Number of textures in the atlas
426
"""
427
428
def get_usage_percent(self) -> float:
429
"""
430
Get the percentage of atlas space currently used.
431
432
Returns:
433
Usage percentage (0.0 to 100.0)
434
"""
435
436
class TextureAtlasBase:
437
"""
438
Base class for texture atlas implementations.
439
"""
440
def __init__(self):
441
"""Create base texture atlas."""
442
443
def add(self, texture: arcade.Texture) -> bool:
444
"""Add a texture to the atlas."""
445
446
def remove(self, texture: arcade.Texture) -> bool:
447
"""Remove a texture from the atlas."""
448
449
def clear(self) -> None:
450
"""Clear all textures from the atlas."""
451
452
class AtlasRegion:
453
"""
454
Represents a region within a texture atlas containing a specific texture.
455
"""
456
def __init__(self, atlas: arcade.TextureAtlasBase, x: int, y: int, width: int, height: int):
457
"""
458
Create an atlas region.
459
460
Args:
461
atlas: The texture atlas containing this region
462
x: X coordinate within the atlas
463
y: Y coordinate within the atlas
464
width: Width of the region
465
height: Height of the region
466
"""
467
468
atlas: arcade.TextureAtlasBase
469
x: int
470
y: int
471
width: int
472
height: int
473
```
474
475
## Usage Examples
476
477
### Basic Texture Loading and Usage
478
479
```python
480
import arcade
481
482
class TextureExample(arcade.Window):
483
def __init__(self):
484
super().__init__(800, 600, "Texture Example")
485
486
self.texture_list = []
487
self.sprite_list = arcade.SpriteList()
488
489
def setup(self):
490
# Load individual textures
491
player_texture = arcade.load_texture(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png")
492
493
# Load with transformations
494
flipped_texture = arcade.load_texture(
495
":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png",
496
flipped_horizontally=True
497
)
498
499
# Load cropped portion
500
cropped_texture = arcade.load_texture(
501
":resources:images/tiles/grassMid.png",
502
x=0, y=0, width=32, height=32
503
)
504
505
# Create sprites using loaded textures
506
player = arcade.Sprite(texture=player_texture, scale=0.5)
507
player.center_x = 200
508
player.center_y = 300
509
self.sprite_list.append(player)
510
511
flipped_player = arcade.Sprite(texture=flipped_texture, scale=0.5)
512
flipped_player.center_x = 400
513
flipped_player.center_y = 300
514
self.sprite_list.append(flipped_player)
515
516
# Generate procedural textures
517
circle_texture = arcade.make_circle_texture(50, arcade.color.RED)
518
circle_sprite = arcade.Sprite(texture=circle_texture)
519
circle_sprite.center_x = 600
520
circle_sprite.center_y = 300
521
self.sprite_list.append(circle_sprite)
522
523
# Soft circle with gradient
524
soft_texture = arcade.make_soft_circle_texture(80, arcade.color.BLUE, 255, 50)
525
soft_sprite = arcade.Sprite(texture=soft_texture)
526
soft_sprite.center_x = 200
527
soft_sprite.center_y = 150
528
self.sprite_list.append(soft_sprite)
529
530
def on_draw(self):
531
self.clear()
532
self.sprite_list.draw()
533
534
def main():
535
game = TextureExample()
536
game.setup()
537
arcade.run()
538
539
if __name__ == "__main__":
540
main()
541
```
542
543
### Sprite Sheet Animation
544
545
```python
546
import arcade
547
548
class AnimationExample(arcade.Window):
549
def __init__(self):
550
super().__init__(800, 600, "Sprite Sheet Animation")
551
552
self.player_list = arcade.SpriteList()
553
self.walk_textures = []
554
555
def setup(self):
556
# Load walking animation frames from sprite sheet
557
self.walk_textures = arcade.load_spritesheet(
558
":resources:images/animated_characters/female_adventurer/femaleAdventurer_walk.png",
559
sprite_width=128,
560
sprite_height=128,
561
columns=8,
562
count=8
563
)
564
565
# Alternative: Load individual frames
566
# self.walk_textures = []
567
# for i in range(8):
568
# texture = arcade.load_texture(f":resources:images/animated_characters/female_adventurer/femaleAdventurer_walk{i}.png")
569
# self.walk_textures.append(texture)
570
571
# Create animated player
572
self.player = arcade.Sprite()
573
self.player.center_x = 400
574
self.player.center_y = 300
575
self.player.scale = 0.5
576
577
# Set initial texture
578
self.player.texture = self.walk_textures[0]
579
self.player_list.append(self.player)
580
581
# Animation state
582
self.current_frame = 0
583
self.frame_timer = 0
584
self.frame_duration = 0.1 # seconds per frame
585
586
def on_draw(self):
587
self.clear()
588
self.player_list.draw()
589
590
# Draw frame counter
591
arcade.draw_text(f"Frame: {self.current_frame}", 10, 550, arcade.color.WHITE, 16)
592
593
def on_update(self, delta_time):
594
# Update animation
595
self.frame_timer += delta_time
596
597
if self.frame_timer >= self.frame_duration:
598
self.frame_timer = 0
599
self.current_frame = (self.current_frame + 1) % len(self.walk_textures)
600
self.player.texture = self.walk_textures[self.current_frame]
601
602
def main():
603
game = AnimationExample()
604
game.setup()
605
arcade.run()
606
607
if __name__ == "__main__":
608
main()
609
```
610
611
### Texture Atlas Management
612
613
```python
614
import arcade
615
616
class AtlasExample(arcade.Window):
617
def __init__(self):
618
super().__init__(800, 600, "Texture Atlas Example")
619
620
self.sprite_list = arcade.SpriteList(use_spatial_hash=True)
621
self.atlas = None
622
623
def setup(self):
624
# Create texture atlas for optimal rendering
625
self.atlas = arcade.DefaultTextureAtlas(size=(1024, 1024))
626
627
# Load various textures
628
textures = [
629
arcade.load_texture(":resources:images/animated_characters/female_adventurer/femaleAdventurer_idle.png"),
630
arcade.load_texture(":resources:images/animated_characters/male_adventurer/maleAdventurer_idle.png"),
631
arcade.load_texture(":resources:images/enemies/bee.png"),
632
arcade.load_texture(":resources:images/items/coinGold.png"),
633
]
634
635
# Add textures to atlas
636
for texture in textures:
637
success = self.atlas.add(texture)
638
print(f"Added texture '{texture.name}' to atlas: {success}")
639
640
# Create sprites using atlas textures
641
for i, texture in enumerate(textures):
642
for j in range(10):
643
sprite = arcade.Sprite(texture=texture, scale=0.3)
644
sprite.center_x = 100 + (i * 150) + (j * 15)
645
sprite.center_y = 300
646
self.sprite_list.append(sprite)
647
648
# Print atlas statistics
649
print(f"Atlas usage: {self.atlas.get_usage_percent():.1f}%")
650
print(f"Textures in atlas: {self.atlas.get_texture_count()}")
651
652
def on_draw(self):
653
self.clear()
654
self.sprite_list.draw()
655
656
# Draw atlas info
657
if self.atlas:
658
usage = self.atlas.get_usage_percent()
659
texture_count = self.atlas.get_texture_count()
660
arcade.draw_text(f"Atlas Usage: {usage:.1f}%", 10, 550, arcade.color.WHITE, 16)
661
arcade.draw_text(f"Textures: {texture_count}", 10, 525, arcade.color.WHITE, 16)
662
663
def main():
664
game = AtlasExample()
665
game.setup()
666
arcade.run()
667
668
if __name__ == "__main__":
669
main()
670
```