170 lines
5.8 KiB
C
170 lines
5.8 KiB
C
|
|
#pragma once
|
|||
|
|
|
|||
|
|
#include <easy2d/animation/sprite_frame.h>
|
|||
|
|
#include <easy2d/graphics/texture_pool.h>
|
|||
|
|
#include <unordered_map>
|
|||
|
|
#include <mutex>
|
|||
|
|
#include <string>
|
|||
|
|
|
|||
|
|
namespace easy2d {
|
|||
|
|
|
|||
|
|
// ============================================================================
|
|||
|
|
// SpriteFrameCache - 精灵帧全局缓存(借鉴 Cocos SpriteFrameCache)
|
|||
|
|
// 全局单例管理所有精灵帧,避免重复创建,支持图集自动切割
|
|||
|
|
// ============================================================================
|
|||
|
|
class SpriteFrameCache {
|
|||
|
|
public:
|
|||
|
|
static SpriteFrameCache& getInstance() {
|
|||
|
|
static SpriteFrameCache instance;
|
|||
|
|
return instance;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ------ 添加帧 ------
|
|||
|
|
|
|||
|
|
/// 添加单个精灵帧
|
|||
|
|
void addSpriteFrame(Ptr<SpriteFrame> frame, const std::string& name) {
|
|||
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|||
|
|
frames_[name] = std::move(frame);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 从纹理和矩形区域创建并添加帧
|
|||
|
|
void addSpriteFrameFromTexture(Ptr<Texture> texture, const Rect& rect,
|
|||
|
|
const std::string& name) {
|
|||
|
|
auto frame = SpriteFrame::create(std::move(texture), rect);
|
|||
|
|
frame->setName(name);
|
|||
|
|
addSpriteFrame(std::move(frame), name);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 从纹理图集批量切割添加(等宽等高网格)
|
|||
|
|
void addSpriteFramesFromGrid(const std::string& texturePath,
|
|||
|
|
int frameWidth, int frameHeight,
|
|||
|
|
int frameCount = -1,
|
|||
|
|
int spacing = 0, int margin = 0) {
|
|||
|
|
auto texture = TexturePool::getInstance().get(texturePath);
|
|||
|
|
if (!texture) return;
|
|||
|
|
addSpriteFramesFromGrid(texture, texturePath, frameWidth, frameHeight,
|
|||
|
|
frameCount, spacing, margin);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 从纹理对象批量切割添加(等宽等高网格,无需走 TexturePool)
|
|||
|
|
void addSpriteFramesFromGrid(Ptr<Texture> texture, const std::string& keyPrefix,
|
|||
|
|
int frameWidth, int frameHeight,
|
|||
|
|
int frameCount = -1,
|
|||
|
|
int spacing = 0, int margin = 0) {
|
|||
|
|
if (!texture) return;
|
|||
|
|
|
|||
|
|
int texW = texture->getWidth();
|
|||
|
|
int texH = texture->getHeight();
|
|||
|
|
int usableW = texW - 2 * margin;
|
|||
|
|
int usableH = texH - 2 * margin;
|
|||
|
|
int cols = (usableW + spacing) / (frameWidth + spacing);
|
|||
|
|
int rows = (usableH + spacing) / (frameHeight + spacing);
|
|||
|
|
int total = (frameCount > 0) ? frameCount : cols * rows;
|
|||
|
|
|
|||
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|||
|
|
for (int i = 0; i < total; ++i) {
|
|||
|
|
int col = i % cols;
|
|||
|
|
int row = i / cols;
|
|||
|
|
if (row >= rows) break;
|
|||
|
|
|
|||
|
|
Rect rect(
|
|||
|
|
static_cast<float>(margin + col * (frameWidth + spacing)),
|
|||
|
|
static_cast<float>(margin + row * (frameHeight + spacing)),
|
|||
|
|
static_cast<float>(frameWidth),
|
|||
|
|
static_cast<float>(frameHeight)
|
|||
|
|
);
|
|||
|
|
|
|||
|
|
std::string name = keyPrefix + "#" + std::to_string(i);
|
|||
|
|
auto frame = SpriteFrame::create(texture, rect);
|
|||
|
|
frame->setName(name);
|
|||
|
|
frames_[name] = std::move(frame);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ------ 获取帧 ------
|
|||
|
|
|
|||
|
|
/// 按名称获取
|
|||
|
|
Ptr<SpriteFrame> getSpriteFrame(const std::string& name) const {
|
|||
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|||
|
|
auto it = frames_.find(name);
|
|||
|
|
if (it != frames_.end()) return it->second;
|
|||
|
|
return nullptr;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 通过路径+索引获取或创建(ANI 格式的定位方式)
|
|||
|
|
Ptr<SpriteFrame> getOrCreateFromFile(const std::string& texturePath,
|
|||
|
|
int index = 0) {
|
|||
|
|
std::string key = texturePath + "#" + std::to_string(index);
|
|||
|
|
|
|||
|
|
{
|
|||
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|||
|
|
auto it = frames_.find(key);
|
|||
|
|
if (it != frames_.end()) return it->second;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 缓存未命中,从 TexturePool 加载纹理并创建 SpriteFrame
|
|||
|
|
auto texture = TexturePool::getInstance().get(texturePath);
|
|||
|
|
if (!texture) return nullptr;
|
|||
|
|
|
|||
|
|
// 默认整张纹理作为一帧(index=0),或用整张纹理
|
|||
|
|
Rect rect(0.0f, 0.0f,
|
|||
|
|
static_cast<float>(texture->getWidth()),
|
|||
|
|
static_cast<float>(texture->getHeight()));
|
|||
|
|
|
|||
|
|
auto frame = SpriteFrame::create(texture, rect);
|
|||
|
|
frame->setName(key);
|
|||
|
|
|
|||
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|||
|
|
frames_[key] = frame;
|
|||
|
|
return frame;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ------ 缓存管理 ------
|
|||
|
|
|
|||
|
|
bool has(const std::string& name) const {
|
|||
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|||
|
|
return frames_.find(name) != frames_.end();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void removeSpriteFrame(const std::string& name) {
|
|||
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|||
|
|
frames_.erase(name);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// 移除未被外部引用的精灵帧(use_count == 1 表示仅缓存自身持有)
|
|||
|
|
void removeUnusedSpriteFrames() {
|
|||
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|||
|
|
for (auto it = frames_.begin(); it != frames_.end(); ) {
|
|||
|
|
if (it->second.use_count() == 1) {
|
|||
|
|
it = frames_.erase(it);
|
|||
|
|
} else {
|
|||
|
|
++it;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void clear() {
|
|||
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|||
|
|
frames_.clear();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
size_t count() const {
|
|||
|
|
std::lock_guard<std::mutex> lock(mutex_);
|
|||
|
|
return frames_.size();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private:
|
|||
|
|
SpriteFrameCache() = default;
|
|||
|
|
~SpriteFrameCache() = default;
|
|||
|
|
SpriteFrameCache(const SpriteFrameCache&) = delete;
|
|||
|
|
SpriteFrameCache& operator=(const SpriteFrameCache&) = delete;
|
|||
|
|
|
|||
|
|
mutable std::mutex mutex_;
|
|||
|
|
std::unordered_map<std::string, Ptr<SpriteFrame>> frames_;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 便捷宏
|
|||
|
|
#define E2D_SPRITE_FRAME_CACHE() ::easy2d::SpriteFrameCache::getInstance()
|
|||
|
|
|
|||
|
|
} // namespace easy2d
|