axmol/extensions/fairygui/UIPackage.cpp

888 lines
24 KiB
C++

#include "UIPackage.h"
#include "UIObjectFactory.h"
#include "display/BitmapFont.h"
#include "event/HitTest.h"
#include "utils/ByteBuffer.h"
#include "utils/ToolSet.h"
NS_FGUI_BEGIN
USING_NS_AX;
using namespace std;
const string UIPackage::URL_PREFIX = "ui://";
int UIPackage::_constructing = 0;
std::string UIPackage::_branch;
std::unordered_map<std::string, std::string> UIPackage::_vars;
const unsigned char* emptyTextureData = new unsigned char[16]{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
std::unordered_map<std::string, UIPackage*> UIPackage::_packageInstById;
std::unordered_map<std::string, UIPackage*> UIPackage::_packageInstByName;
std::vector<UIPackage*> UIPackage::_packageList;
Texture2D* UIPackage::_emptyTexture;
struct AtlasSprite
{
PackageItem* atlas;
Rect rect;
Size originalSize;
Vec2 offset;
bool rotated;
};
UIPackage::UIPackage()
: _branchIndex(-1)
{
}
UIPackage::~UIPackage()
{
for (auto& it : _items)
it->release();
for (auto& it : _sprites)
delete it.second;
}
void UIPackage::setBranch(const std::string& value)
{
_branch = value;
for (auto& it : _packageList)
{
if (it->_branches.size() > 0)
{
it->_branchIndex = ToolSet::findInStringArray(it->_branches, value);
}
}
}
const std::string& UIPackage::getVar(const std::string& key)
{
auto it = _vars.find(key);
if (it != _vars.end())
return it->second;
else
return STD_STRING_EMPTY;
}
void UIPackage::setVar(const std::string& key, const std::string& value)
{
_vars[key] = value;
}
UIPackage* UIPackage::getById(const string& id)
{
auto it = _packageInstById.find(id);
if (it != _packageInstById.end())
return it->second;
else
return nullptr;
}
UIPackage* UIPackage::getByName(const string& name)
{
auto it = _packageInstByName.find(name);
if (it != _packageInstByName.end())
return it->second;
else
return nullptr;
}
UIPackage* UIPackage::addPackage(const string& assetPath)
{
auto it = _packageInstById.find(assetPath);
if (it != _packageInstById.end())
return it->second;
if (_emptyTexture == nullptr)
{
Image* emptyImage = new Image();
emptyImage->initWithRawData(emptyTextureData, 16, 2, 2, 4, false);
_emptyTexture = new Texture2D();
_emptyTexture->initWithImage(emptyImage);
delete emptyImage;
}
Data data;
if (FileUtils::getInstance()->getContents(assetPath + ".fui", &data) != FileUtils::Status::OK)
{
AXLOGERROR("FairyGUI: cannot load package from '%s'", assetPath.c_str());
return nullptr;
}
ssize_t size;
char* p = (char*)data.takeBuffer(&size);
ByteBuffer buffer(p, 0, (int)size, true);
UIPackage* pkg = new UIPackage();
pkg->_assetPath = assetPath;
if (!pkg->loadPackage(&buffer))
{
delete pkg;
return nullptr;
}
_packageInstById[pkg->getId()] = pkg;
_packageInstByName[pkg->getName()] = pkg;
_packageInstById[assetPath] = pkg;
_packageList.push_back(pkg);
return pkg;
}
void UIPackage::removePackage(const string& packageIdOrName)
{
UIPackage* pkg = UIPackage::getByName(packageIdOrName);
if (!pkg)
pkg = getById(packageIdOrName);
if (pkg)
{
auto it = std::find(_packageList.cbegin(), _packageList.cend(), pkg);
if (it != _packageList.cend())
_packageList.erase(it);
_packageInstById.erase(pkg->getId());
_packageInstById.erase(pkg->_assetPath);
_packageInstByName.erase(pkg->getName());
pkg->release();
}
else
AXLOGERROR("FairyGUI: invalid package name or id: %s", packageIdOrName.c_str());
}
void UIPackage::removeAllPackages()
{
for (auto& it : _packageList)
it->release();
_packageInstById.clear();
_packageInstByName.clear();
_packageList.clear();
}
GObject* UIPackage::createObject(const string& pkgName, const string& resName)
{
UIPackage* pkg = UIPackage::getByName(pkgName);
if (pkg)
return pkg->createObject(resName);
else
{
AXLOGERROR("FairyGUI: package not found - %s", pkgName.c_str());
return nullptr;
}
}
GObject* UIPackage::createObjectFromURL(const string& url)
{
PackageItem* pi = UIPackage::getItemByURL(url);
if (pi)
return pi->owner->createObject(pi);
else
{
AXLOGERROR("FairyGUI: resource not found - %s", url.c_str());
return nullptr;
}
}
string UIPackage::getItemURL(const string& pkgName, const string& resName)
{
UIPackage* pkg = UIPackage::getByName(pkgName);
if (pkg)
{
PackageItem* pi = pkg->getItemByName(resName);
if (pi)
return URL_PREFIX + pkg->getId() + pi->id;
}
return STD_STRING_EMPTY;
}
PackageItem* UIPackage::getItemByURL(std::string_view url)
{
if (url.size() == 0)
return nullptr;
ssize_t pos1 = url.find('/');
if (pos1 == -1)
return nullptr;
ssize_t pos2 = url.find('/', pos1 + 2);
if (pos2 == -1)
{
if (url.size() > 13)
{
std::string pkgId{url.substr(5, 8)};
UIPackage* pkg = getById(pkgId);
if (pkg != nullptr)
{
std::string srcId{url.substr(13)};
return pkg->getItem(srcId);
}
}
}
else
{
std::string pkgName{url.substr(pos1 + 2, pos2 - pos1 - 2)};
UIPackage* pkg = getByName(pkgName);
if (pkg != nullptr)
{
std::string srcName{url.substr(pos2 + 1)};
return pkg->getItemByName(srcName);
}
}
return nullptr;
}
string UIPackage::normalizeURL(const string& url)
{
if (url.size() == 0)
return url;
ssize_t pos1 = url.find('/');
if (pos1 == -1)
return url;
ssize_t pos2 = url.find('/', pos1 + 2);
if (pos2 == -1)
return url;
else
{
string pkgName = url.substr(pos1 + 2, pos2 - pos1 - 2);
string srcName = url.substr(pos2 + 1);
return getItemURL(pkgName, srcName);
}
}
void* UIPackage::getItemAsset(const std::string& pkgName, const std::string& resName, PackageItemType type)
{
UIPackage* pkg = UIPackage::getByName(pkgName);
if (pkg)
{
PackageItem* pi = pkg->getItemByName(resName);
if (pi)
{
if (type != PackageItemType::UNKNOWN && pi->type != type)
return nullptr;
else
return pkg->getItemAsset(pi);
}
}
return nullptr;
}
void* UIPackage::getItemAssetByURL(std::string_view url, PackageItemType type)
{
PackageItem* pi = UIPackage::getItemByURL(url);
if (pi)
{
if (type != PackageItemType::UNKNOWN && pi->type != type)
return nullptr;
else
return pi->owner->getItemAsset(pi);
}
else
return nullptr;
}
PackageItem* UIPackage::getItem(const string& itemId)
{
auto it = _itemsById.find(itemId);
if (it != _itemsById.end())
return it->second;
else
return nullptr;
}
PackageItem* UIPackage::getItemByName(const string& itemName)
{
auto it = _itemsByName.find(itemName);
if (it != _itemsByName.end())
return it->second;
else
return nullptr;
}
GObject* UIPackage::createObject(const string& resName)
{
PackageItem* pi = getItemByName(resName);
AXASSERT(pi, StringUtils::format("FairyGUI: resource not found - %s in %s",
resName.c_str(), _name.c_str())
.c_str());
return createObject(pi);
}
GObject* UIPackage::createObject(PackageItem* item)
{
GObject* g = UIObjectFactory::newObject(item);
if (g == nullptr)
return nullptr;
_constructing++;
g->constructFromResource();
_constructing--;
return g;
}
bool UIPackage::loadPackage(ByteBuffer* buffer)
{
if (buffer->readUint() != 0x46475549)
{
AXLOGERROR("FairyGUI: old package format found in '%s'", _assetPath.c_str());
return false;
}
buffer->version = buffer->readInt();
bool ver2 = buffer->version >= 2;
buffer->readBool(); //compressed
_id = buffer->readString();
_name = buffer->readString();
buffer->skip(20);
int indexTablePos = buffer->getPos();
int cnt;
buffer->seek(indexTablePos, 4);
cnt = buffer->readInt();
_stringTable.resize(cnt);
for (int i = 0; i < cnt; i++)
_stringTable[i] = buffer->readString();
buffer->setStringTable(&_stringTable);
buffer->seek(indexTablePos, 0);
cnt = buffer->readShort();
for (int i = 0; i < cnt; i++)
{
std::unordered_map<std::string, std::string> info;
info["id"] = buffer->readS();
info["name"] = buffer->readS();
_dependencies.push_back(info);
}
bool branchIncluded = false;
if (ver2)
{
cnt = buffer->readShort();
if (cnt > 0)
{
buffer->readSArray(_branches, cnt);
if (_branch.size() > 0)
_branchIndex = ToolSet::findInStringArray(_branches, _branch);
}
branchIncluded = cnt > 0;
}
buffer->seek(indexTablePos, 1);
PackageItem* pi;
string path = _assetPath;
size_t pos = path.find('/');
string shortPath = pos == -1 ? STD_STRING_EMPTY : path.substr(0, pos + 1);
path += "_";
cnt = buffer->readShort();
for (int i = 0; i < cnt; i++)
{
int nextPos = buffer->readInt();
nextPos += buffer->getPos();
pi = new PackageItem();
pi->owner = this;
pi->type = (PackageItemType)buffer->readByte();
pi->id = buffer->readS();
pi->name = buffer->readS();
buffer->skip(2); //path
pi->file = buffer->readS();
buffer->readBool(); //exported
pi->width = buffer->readInt();
pi->height = buffer->readInt();
switch (pi->type)
{
case PackageItemType::IMAGE:
{
pi->objectType = ObjectType::IMAGE;
int scaleOption = buffer->readByte();
if (scaleOption == 1)
{
pi->scale9Grid = new Rect();
pi->scale9Grid->origin.x = buffer->readInt();
pi->scale9Grid->origin.y = buffer->readInt();
pi->scale9Grid->size.width = buffer->readInt();
pi->scale9Grid->size.height = buffer->readInt();
pi->tileGridIndice = buffer->readInt();
}
else if (scaleOption == 2)
pi->scaleByTile = true;
buffer->readBool(); //smoothing
break;
}
case PackageItemType::MOVIECLIP:
{
buffer->readBool(); //smoothing
pi->objectType = ObjectType::MOVIECLIP;
pi->rawData = buffer->readBuffer();
break;
}
case PackageItemType::FONT:
{
pi->rawData = buffer->readBuffer();
break;
}
case PackageItemType::COMPONENT:
{
int extension = buffer->readByte();
if (extension > 0)
pi->objectType = (ObjectType)extension;
else
pi->objectType = ObjectType::COMPONENT;
pi->rawData = buffer->readBuffer();
UIObjectFactory::resolvePackageItemExtension(pi);
break;
}
case PackageItemType::ATLAS:
case PackageItemType::SOUND:
case PackageItemType::MISC:
{
pi->file = path + pi->file;
break;
}
case PackageItemType::SPINE:
case PackageItemType::DRAGONBONES:
{
pi->file = shortPath + pi->file;
pi->skeletonAnchor = new Vec2();
pi->skeletonAnchor->x = buffer->readFloat();
pi->skeletonAnchor->y = buffer->readFloat();
break;
}
default:
break;
}
if (ver2)
{
std::string str = buffer->readS(); //branch
if (!str.empty())
pi->name = str + "/" + pi->name;
int branchCnt = buffer->readUbyte();
if (branchCnt > 0)
{
if (branchIncluded)
{
pi->branches = new std::vector<std::string>();
buffer->readSArray(*pi->branches, branchCnt);
}
else
_itemsById[buffer->readS()] = pi;
}
int highResCnt = buffer->readUbyte();
if (highResCnt > 0)
{
pi->highResolution = new std::vector<std::string>();
buffer->readSArray(*pi->highResolution, highResCnt);
}
}
_items.push_back(pi);
_itemsById[pi->id] = pi;
if (!pi->name.empty())
_itemsByName[pi->name] = pi;
buffer->setPos(nextPos);
}
buffer->seek(indexTablePos, 2);
cnt = buffer->readShort();
for (int i = 0; i < cnt; i++)
{
int nextPos = buffer->readUshort();
nextPos += buffer->getPos();
const string& itemId = buffer->readS();
pi = _itemsById[buffer->readS()];
AtlasSprite* sprite = new AtlasSprite();
sprite->atlas = pi;
sprite->rect.origin.x = buffer->readInt();
sprite->rect.origin.y = buffer->readInt();
sprite->rect.size.width = buffer->readInt();
sprite->rect.size.height = buffer->readInt();
sprite->rotated = buffer->readBool();
if (ver2 && buffer->readBool())
{
sprite->offset.x = buffer->readInt();
sprite->offset.y = buffer->readInt();
sprite->originalSize.width = buffer->readInt();
sprite->originalSize.height = buffer->readInt();
}
else
{
sprite->offset.setZero();
sprite->originalSize.width = sprite->rect.size.width;
sprite->originalSize.height = sprite->rect.size.height;
}
_sprites[itemId] = sprite;
buffer->setPos(nextPos);
}
if (buffer->seek(indexTablePos, 3))
{
cnt = buffer->readShort();
for (int i = 0; i < cnt; i++)
{
int nextPos = buffer->readInt();
nextPos += buffer->getPos();
auto it = _itemsById.find(buffer->readS());
if (it != _itemsById.end())
{
pi = it->second;
if (pi->type == PackageItemType::IMAGE)
{
pi->pixelHitTestData = new PixelHitTestData();
pi->pixelHitTestData->load(buffer);
}
}
buffer->setPos(nextPos);
}
}
return true;
}
void* UIPackage::getItemAsset(PackageItem* item)
{
switch (item->type)
{
case PackageItemType::IMAGE:
if (item->spriteFrame == nullptr)
loadImage(item);
return item->spriteFrame;
case PackageItemType::ATLAS:
if (item->texture == nullptr)
loadAtlas(item);
return item->texture;
case PackageItemType::FONT:
if (item->bitmapFont == nullptr)
loadFont(item);
return item->bitmapFont;
case PackageItemType::MOVIECLIP:
if (item->animation == nullptr)
loadMovieClip(item);
return item->animation;
default:
return nullptr;
}
}
void UIPackage::loadAtlas(PackageItem* item)
{
Image* image = new Image();
#if COCOS2D_VERSION < 0x00031702
Image::setPNGPremultipliedAlphaEnabled(false);
#endif
if (!image->initWithImageFile(item->file))
{
item->texture = _emptyTexture;
_emptyTexture->retain();
delete image;
#if COCOS2D_VERSION < 0x00031702
Image::setPNGPremultipliedAlphaEnabled(true);
#endif
AXLOGWARN("FairyGUI: texture '%s' not found in %s", item->file.c_str(), _name.c_str());
return;
}
#if COCOS2D_VERSION < 0x00031702
Image::setPNGPremultipliedAlphaEnabled(true);
#endif
Texture2D* tex = new Texture2D();
tex->initWithImage(image);
item->texture = tex;
delete image;
string alphaFilePath;
string ext = FileUtils::getInstance()->getFileExtension(item->file);
size_t pos = item->file.find_last_of('.');
if (pos != -1)
alphaFilePath = item->file.substr(0, pos) + "!a" + ext;
else
alphaFilePath = item->file + "!a" + ext;
bool hasAlphaTexture = ToolSet::isFileExist(alphaFilePath);
if (hasAlphaTexture)
{
image = new Image();
if (!image->initWithImageFile(alphaFilePath))
{
delete image;
return;
}
#if defined(AX_VERSION)
if(image->getFileType() == Image::Format::ETC1)
tex->updateWithImage(image, Texture2D::getDefaultAlphaPixelFormat(), 1);
#else
tex = new Texture2D();
tex->initWithImage(image);
item->texture->setAlphaTexture(tex);
tex->release();
#endif
delete image;
}
}
AtlasSprite* UIPackage::getSprite(const std::string& spriteId)
{
auto it = _sprites.find(spriteId);
if (it != _sprites.end())
return it->second;
else
return nullptr;
}
//note: SpriteFrame.ref=1 not autorelease.
SpriteFrame* UIPackage::createSpriteTexture(AtlasSprite* sprite)
{
getItemAsset(sprite->atlas);
//not using createWithTexture for saving a autorelease call.
SpriteFrame* spriteFrame = new SpriteFrame();
spriteFrame->initWithTexture(sprite->atlas->texture, sprite->rect, sprite->rotated,
Vec2(sprite->offset.x - (sprite->originalSize.width - sprite->rect.size.width) / 2, -(sprite->offset.y - (sprite->originalSize.height - sprite->rect.size.height) / 2)),
sprite->originalSize);
return spriteFrame;
}
void UIPackage::loadImage(PackageItem* item)
{
AtlasSprite* sprite = getSprite(item->id);
if (sprite != nullptr)
item->spriteFrame = createSpriteTexture(sprite);
else
{
item->spriteFrame = new SpriteFrame();
item->spriteFrame->initWithTexture(_emptyTexture, Rect());
}
if (item->scaleByTile)
{
#if COCOS2D_VERSION >= 0x00040000
Texture2D::TexParams tp(backend::SamplerFilter::LINEAR, backend::SamplerFilter::LINEAR,
backend::SamplerAddressMode::REPEAT, backend::SamplerAddressMode::REPEAT);
#else
Texture2D::TexParams tp = { GL_LINEAR, GL_LINEAR, GL_REPEAT, GL_REPEAT };
#endif
item->spriteFrame->getTexture()->setTexParameters(tp);
}
}
void UIPackage::loadMovieClip(PackageItem* item)
{
item->animation = Animation::create();
item->animation->retain();
ByteBuffer* buffer = item->rawData;
buffer->seek(0, 0);
float interval = buffer->readInt() / 1000.0f;
item->swing = buffer->readBool();
item->repeatDelay = buffer->readInt() / 1000.0f;
buffer->seek(0, 1);
int frameCount = buffer->readShort();
Vector<AnimationFrame*> frames(frameCount);
Size mcSizeInPixels = Size(item->width, item->height);
Size mcSize = AX_SIZE_PIXELS_TO_POINTS(mcSizeInPixels);
AtlasSprite* sprite;
SpriteFrame* spriteFrame;
for (int i = 0; i < frameCount; i++)
{
int nextPos = buffer->readUshort();
nextPos += buffer->getPos();
Rect rect;
rect.origin.x = buffer->readInt();
rect.origin.y = buffer->readInt();
rect.size.width = buffer->readInt();
rect.size.height = buffer->readInt();
float addDelay = buffer->readInt() / 1000.0f;
const string& spriteId = buffer->readS();
if (!spriteId.empty() && (sprite = getSprite(spriteId)) != nullptr)
{
spriteFrame = createSpriteTexture(sprite);
spriteFrame->setOriginalSizeInPixels(mcSizeInPixels);
spriteFrame->setOriginalSize(mcSize);
}
else
{
//dont use createWithTexture
spriteFrame = new SpriteFrame();
spriteFrame->initWithTexture(_emptyTexture, Rect());
}
spriteFrame->setOffset(Vec2(rect.origin.x - (mcSize.width - rect.size.width) / 2, -(rect.origin.y - (mcSize.height - rect.size.height) / 2)));
AnimationFrame* frame = AnimationFrame::create(spriteFrame, addDelay / interval + 1, ValueMapNull);
frames.pushBack(frame);
//tranfer to AnimationFrame
spriteFrame->release();
buffer->setPos(nextPos);
}
item->animation->initWithAnimationFrames(frames, interval, 1);
delete buffer;
item->rawData = nullptr;
}
void UIPackage::loadFont(PackageItem* item)
{
item->bitmapFont = BitmapFont::create();
auto bitmapFont = item->bitmapFont;
#if defined(AX_VERSION)
auto fontAtlas = bitmapFont->resetFontAtlas(bitmapFont->newFontAtlas());
#else
auto fontAtlas = bitmapFont->resetFontAtlas(bitmapFont->createFontAtlas());
#endif
ByteBuffer* buffer = item->rawData;
buffer->seek(0, 0);
bool ttf = buffer->readBool();
item->bitmapFont->_canTint = buffer->readBool();
item->bitmapFont->_resizable = buffer->readBool();
buffer->readBool(); //hasChannel
int fontSize = buffer->readInt();
int xadvance = buffer->readInt();
int lineHeight = buffer->readInt();
Texture2D* mainTexture = nullptr;
AtlasSprite* mainSprite = nullptr;
if (ttf && (mainSprite = getSprite(item->id)) != nullptr)
mainTexture = (Texture2D*)getItemAsset(mainSprite->atlas);
buffer->seek(0, 1);
FontLetterDefinition def;
int bx = 0, by = 0;
int bw = 0, bh = 0;
PackageItem* charImg = nullptr;
int cnt = buffer->readInt();
for (int i = 0; i < cnt; i++)
{
int nextPos = buffer->readUshort();
nextPos += buffer->getPos();
memset(&def, 0, sizeof(def));
unsigned short ch = buffer->readUshort();
const string& img = buffer->readS();
bx = buffer->readInt();
by = buffer->readInt();
def.offsetX = buffer->readInt();
def.offsetY = buffer->readInt();
bw = buffer->readInt();
bh = buffer->readInt();
def.xAdvance = buffer->readInt();
buffer->readByte(); //channel
if (ttf)
{
Rect tempRect = Rect(bx + mainSprite->rect.origin.x, by + mainSprite->rect.origin.y, bw, bh);
tempRect = AX_RECT_PIXELS_TO_POINTS(tempRect);
def.U = tempRect.origin.x;
def.V = tempRect.origin.y;
def.width = tempRect.size.width;
def.height = tempRect.size.height;
def.validDefinition = true;
}
else
{
charImg = getItem(img);
if (charImg)
{
charImg = charImg->getBranch();
bw = charImg->width;
bh = charImg->height;
AtlasSprite* sprite = getSprite(img);
if (sprite != nullptr)
{
def.offsetX += sprite->offset.x;
def.offsetY += sprite->offset.y;
}
charImg = charImg->getHighResolution();
getItemAsset(charImg);
Rect tempRect = charImg->spriteFrame->getRectInPixels();
tempRect = AX_RECT_PIXELS_TO_POINTS(tempRect);
def.U = tempRect.origin.x;
def.V = tempRect.origin.y;
def.width = tempRect.size.width;
def.height = tempRect.size.height;
if (mainTexture == nullptr)
mainTexture = charImg->spriteFrame->getTexture();
def.validDefinition = true;
if (def.xAdvance == 0)
{
if (xadvance == 0)
def.xAdvance = def.offsetX + bw;
else
def.xAdvance = xadvance;
}
if (fontSize == 0)
fontSize = bh;
lineHeight = MAX(fontSize, lineHeight);
}
}
fontAtlas->addLetterDefinition(ch, def);
buffer->setPos(nextPos);
}
if (mainTexture != nullptr)
fontAtlas->setTexture(0, mainTexture);
fontAtlas->setLineHeight(lineHeight);
item->bitmapFont->_originalFontSize = fontSize;
delete buffer;
item->rawData = nullptr;
}
NS_FGUI_END