#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 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 UIPackage::_packageInstById; std::unordered_map UIPackage::_packageInstByName; std::vector 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 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(); buffer->readSArray(*pi->branches, branchCnt); } else _itemsById[buffer->readS()] = pi; } int highResCnt = buffer->readUbyte(); if (highResCnt > 0) { pi->highResolution = new std::vector(); 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 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