Source code for arcade.texture_atlas.helpers

from __future__ import annotations

import json
from pathlib import Path
from time import perf_counter
from typing import Dict, Tuple, cast

import PIL.Image

import arcade
from arcade import cache
from arcade.texture import ImageData, Texture

from .base import AtlasRegion, TextureAtlas


class FakeImage:
    """A fake PIL image"""
    def __init__(self, size):
        self.size = size

    @property
    def width(self):
        return self.size[0]

    @property
    def height(self):
        return self.size[1]


def _dump_region_info(region: AtlasRegion):
    return {
        "pos": [region.x, region.y],
        "size": [region.width, region.height],
        "uvs": region.texture_coordinates,
    }


[docs] def save_atlas(atlas: TextureAtlas, directory: Path, name: str, resource_root: Path): """ Dump the atlas to a file. This includes the atlas image and metadata. :param atlas: The atlas to dump :param directory: The directory to dump the atlas to :param name: The name of the atlas """ # Dump the image atlas.save(directory / f"{name}.png", flip=False) meta = { 'name': name, 'atlas_file': f"{name}.png", 'size': atlas.size, 'border': atlas.border, 'textures': [], 'images': [], } # Images images = [] for image in atlas._images: images.append({ "hash": image.hash, "region": _dump_region_info(atlas.get_image_region_info(image.hash)), }) meta['images'] = images # Textures textures = [] for texture in atlas._textures: if texture.file_path is None: raise ValueError("Can't save a texture not loaded from a file") textures.append({ "hash": texture.image_data.hash, "path": texture.file_path.relative_to(resource_root).as_posix(), "crop": texture.crop_values, "points": texture.hit_box_points, "region": _dump_region_info(atlas.get_texture_region_info(texture.atlas_name)), "vertex_order": texture._vertex_order, }) meta['textures'] = textures # Dump the metadata with open(directory / f"{name}.json", 'w') as fd: json.dump(meta, fd, indent=2)
[docs] def load_atlas( meta_file: Path, resource_root: Path ) -> Tuple[TextureAtlas, Dict[str, float]]: """ Load a texture atlas from disk. """ ctx = arcade.get_window().ctx perf_data = {} t = perf_counter() # Load metadata with open(meta_file, 'r') as fd: meta = json.load(fd) perf_data['load_meta'] = perf_counter() - t t = perf_counter() atlas = TextureAtlas( meta['size'], border=meta["border"], auto_resize=False, ) perf_data['create_atlas'] = perf_counter() - t # Inject the atlas image t = perf_counter() atlas._texture = ctx.load_texture(meta['atlas_file'], flip=False) atlas._fbo = ctx.framebuffer(color_attachments=[atlas._texture]) perf_data['load_texture'] = perf_counter() - t # Recreate images t = perf_counter() image_map: Dict[str, ImageData] = {} for im in meta['images']: image_data = ImageData( cast(PIL.Image.Image, FakeImage(im['region']['size'])), im['hash'], ) atlas._images.add(image_data) image_map[image_data.hash] = image_data # cache.image_data_cache.put() region = AtlasRegion( atlas, im['region']['pos'][0], im['region']['pos'][1], im['region']['size'][0], im['region']['size'][1], tuple(im['region']['uvs']), # type: ignore ) atlas._image_regions[image_data.hash] = region # Get a slot for the image and write the uv data slot = atlas._image_uv_slots_free.popleft() atlas._image_uv_slots[image_data.hash] = slot for i in range(8): atlas._image_uv_data[slot * 8 + i] = region.texture_coordinates[i] perf_data['create_images'] = perf_counter() - t # Recreate textures t = perf_counter() for tex in meta['textures']: texture = Texture( image_map[tex['hash']], hit_box_points=tex['points'], ) texture._vertex_order = tuple(tex['vertex_order']) # type: ignore texture._update_cache_names() atlas._textures.add(texture) # Cache the texture strongly so it doesn't get garbage collected cache.texture_cache.put(texture, file_path=resource_root / tex['hash']) texture.file_path = resource_root / tex['path'] texture.crop_values = tex['crop'] region = AtlasRegion( atlas, tex['region']['pos'][0], tex['region']['pos'][1], tex['region']['size'][0], tex['region']['size'][1], tuple(tex['region']['uvs']), # type: ignore ) atlas._texture_regions[texture.atlas_name] = region # Get a slot for the image and write the uv data slot = atlas._texture_uv_slots_free.popleft() atlas._texture_uv_slots[texture.atlas_name] = slot for i in range(8): atlas._texture_uv_data[slot * 8 + i] = region.texture_coordinates[i] perf_data['create_textures'] = perf_counter() - t # Write the uv data to vram atlas.use_uv_texture() return atlas, perf_data return atlas, perf_data