---@class ImageCache ---@field _cache table local ImageCache = {} ImageCache._cache = {} --- Normalize a file path for consistent cache keys ---@param path string -- File path to normalize ---@return string -- Normalized path local function normalizePath(path) path = path:match("^%s*(.-)%s*$") path = path:gsub("\\", "/") path = path:gsub("/+", "/") return path end --- Load an image from file path with caching --- Returns cached image if already loaded, otherwise loads and caches it ---@param imagePath string -- Path to image file ---@param loadImageData boolean? -- Optional: also load ImageData for pixel access (default: false) ---@return love.Image|nil -- Image object or nil on error ---@return string|nil -- Error message if loading failed function ImageCache.load(imagePath, loadImageData) if not imagePath or type(imagePath) ~= "string" or imagePath == "" then return nil, "Invalid image path: path must be a non-empty string" end local normalizedPath = normalizePath(imagePath) if ImageCache._cache[normalizedPath] then return ImageCache._cache[normalizedPath].image, nil end local success, imageOrError = pcall(love.graphics.newImage, normalizedPath) if not success then return nil, string.format("Failed to load image '%s': %s", imagePath, tostring(imageOrError)) end local image = imageOrError local imgData = nil if loadImageData then local dataSuccess, dataOrError = pcall(love.image.newImageData, normalizedPath) if dataSuccess then imgData = dataOrError end end ImageCache._cache[normalizedPath] = { image = image, imageData = imgData, } return image, nil end --- Get a cached image without loading ---@param imagePath string -- Path to image file ---@return love.Image|nil -- Cached image or nil if not found function ImageCache.get(imagePath) if not imagePath or type(imagePath) ~= "string" then return nil end local normalizedPath = normalizePath(imagePath) local cached = ImageCache._cache[normalizedPath] return cached and cached.image or nil end --- Get cached ImageData for an image ---@param imagePath string -- Path to image file ---@return love.ImageData|nil -- Cached ImageData or nil if not found function ImageCache.getImageData(imagePath) if not imagePath or type(imagePath) ~= "string" then return nil end local normalizedPath = normalizePath(imagePath) local cached = ImageCache._cache[normalizedPath] return cached and cached.imageData or nil end --- Remove a specific image from cache ---@param imagePath string -- Path to image file to remove ---@return boolean -- True if image was removed, false if not found function ImageCache.remove(imagePath) if not imagePath or type(imagePath) ~= "string" then return false end local normalizedPath = normalizePath(imagePath) if ImageCache._cache[normalizedPath] then local cached = ImageCache._cache[normalizedPath] if cached.image then cached.image:release() end if cached.imageData then cached.imageData:release() end ImageCache._cache[normalizedPath] = nil return true end return false end --- Clear all cached images function ImageCache.clear() for path, cached in pairs(ImageCache._cache) do if cached.image then cached.image:release() end if cached.imageData then cached.imageData:release() end end ImageCache._cache = {} end --- Get cache statistics ---@return {count: number, memoryEstimate: number} -- Cache stats function ImageCache.getStats() local count = 0 local memoryEstimate = 0 for path, cached in pairs(ImageCache._cache) do count = count + 1 if cached.image then local w, h = cached.image:getDimensions() -- Estimate: 4 bytes per pixel (RGBA) memoryEstimate = memoryEstimate + (w * h * 4) end end return { count = count, memoryEstimate = memoryEstimate, } end return ImageCache