GLEGram 12.5 — Initial public release

Based on Swiftgram 12.5 (Telegram iOS 12.5).
All GLEGram features ported and organized in GLEGram/ folder.

Features: Ghost Mode, Saved Deleted Messages, Content Protection Bypass,
Font Replacement, Fake Profile, Chat Export, Plugin System, and more.

See CHANGELOG_12.5.md for full details.
This commit is contained in:
Leeksov
2026-04-06 09:48:12 +03:00
commit 4647310322
39685 changed files with 11052678 additions and 0 deletions
@@ -0,0 +1,187 @@
#include <LottieCpp/CGPath.h>
#include <cassert>
namespace lottie {
namespace {
void addPointToBoundingRect(bool *isFirst, CGRect *rect, Vector2D const *point) {
if (*isFirst) {
*isFirst = false;
rect->x = point->x;
rect->y = point->y;
rect->width = 0.0;
rect->height = 0.0;
return;
}
if (point->x > rect->x + rect->width) {
rect->width = point->x - rect->x;
}
if (point->y > rect->y + rect->height) {
rect->height = point->y - rect->y;
}
if (point->x < rect->x) {
rect->width += rect->x - point->x;
rect->x = point->x;
}
if (point->y < rect->y) {
rect->height += rect->y - point->y;
rect->y = point->y;
}
}
}
Vector2D transformVector(Vector2D const &v, Transform2D const &m) {
float transformedX = m.rows().columns[0][0] * v.x + m.rows().columns[1][0] * v.y + m.rows().columns[2][0] * 1.0f;
float transformedY = m.rows().columns[0][1] * v.x + m.rows().columns[1][1] * v.y + m.rows().columns[2][1] * 1.0f;
return Vector2D(transformedX, transformedY);
}
class CGPathImpl: public CGPath {
public:
CGPathImpl();
virtual ~CGPathImpl();
virtual CGRect boundingBox() const override;
virtual bool empty() const override;
virtual std::shared_ptr<CGPath> copyUsingTransform(Transform2D const &transform) const override;
virtual void addLineTo(Vector2D const &point) override;
virtual void addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) override;
virtual void moveTo(Vector2D const &point) override;
virtual void closeSubpath() override;
virtual void addPath(std::shared_ptr<CGPath> const &path) override;
virtual bool isEqual(CGPath *other) const override;
virtual void enumerate(std::function<void(CGPathItem const &)>) override;
private:
std::vector<CGPathItem> _items;
};
CGPathImpl::CGPathImpl() {
}
CGPathImpl::~CGPathImpl() {
}
CGRect CGPathImpl::boundingBox() const {
bool isFirst = true;
CGRect result(0.0, 0.0, 0.0, 0.0);
for (const auto &item : _items) {
switch (item.type) {
case CGPathItem::Type::MoveTo: {
addPointToBoundingRect(&isFirst, &result, &item.points[0]);
break;
}
case CGPathItem::Type::LineTo: {
addPointToBoundingRect(&isFirst, &result, &item.points[0]);
break;
}
case CGPathItem::Type::CurveTo: {
addPointToBoundingRect(&isFirst, &result, &item.points[0]);
addPointToBoundingRect(&isFirst, &result, &item.points[1]);
addPointToBoundingRect(&isFirst, &result, &item.points[2]);
break;
}
case CGPathItem::Type::Close: {
break;
}
default: {
break;
}
}
}
return result;
}
bool CGPathImpl::empty() const {
return _items.empty();
}
std::shared_ptr<CGPath> CGPathImpl::copyUsingTransform(Transform2D const &transform) const {
auto result = std::make_shared<CGPathImpl>();
if (transform == Transform2D::identity()) {
result->_items = _items;
return result;
}
result->_items.reserve(_items.capacity());
for (auto &sourceItem : _items) {
CGPathItem &item = result->_items.emplace_back(sourceItem.type);
item.points[0] = transformVector(sourceItem.points[0], transform);
item.points[1] = transformVector(sourceItem.points[1], transform);
item.points[2] = transformVector(sourceItem.points[2], transform);
}
return result;
}
void CGPathImpl::addLineTo(Vector2D const &point) {
CGPathItem &item = _items.emplace_back(CGPathItem::Type::LineTo);
item.points[0] = point;
}
void CGPathImpl::addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) {
CGPathItem &item = _items.emplace_back(CGPathItem::Type::CurveTo);
item.points[0] = control1;
item.points[1] = control2;
item.points[2] = point;
}
void CGPathImpl::moveTo(Vector2D const &point) {
CGPathItem &item = _items.emplace_back(CGPathItem::Type::MoveTo);
item.points[0] = point;
}
void CGPathImpl::closeSubpath() {
_items.emplace_back(CGPathItem::Type::Close);
}
void CGPathImpl::addPath(std::shared_ptr<CGPath> const &path) {
if (_items.size() == 0) {
_items = std::static_pointer_cast<CGPathImpl>(path)->_items;
} else {
size_t totalItemCount = _items.size() + std::static_pointer_cast<CGPathImpl>(path)->_items.size();
if (_items.capacity() < totalItemCount) {
_items.reserve(totalItemCount);
}
for (const auto &item : std::static_pointer_cast<CGPathImpl>(path)->_items) {
_items.push_back(item);
}
}
}
bool CGPathImpl::isEqual(CGPath *other) const {
if (_items.size() != ((CGPathImpl *)other)->_items.size()) {
return false;
}
for (size_t i = 0; i < _items.size(); i++) {
if (_items[i] != ((CGPathImpl *)other)->_items[i]) {
return false;
}
}
return true;
}
void CGPathImpl::enumerate(std::function<void(CGPathItem const &)> f) {
for (const auto &item : _items) {
f(item);
}
}
std::shared_ptr<CGPath> CGPath::makePath() {
return std::static_pointer_cast<CGPath>(std::make_shared<CGPathImpl>());
}
}
@@ -0,0 +1,219 @@
#include <LottieCpp/CGPath.h>
#include <LottieCpp/CGPathCocoa.h>
#import <QuartzCore/QuartzCore.h>
namespace {
void addPointToBoundingRect(bool *isFirst, CGRect *rect, CGPoint *point) {
if (*isFirst) {
*isFirst = false;
rect->origin.x = point->x;
rect->origin.y = point->y;
rect->size.width = 0.0;
rect->size.height = 0.0;
return;
}
if (point->x > rect->origin.x + rect->size.width) {
rect->size.width = point->x - rect->origin.x;
}
if (point->y > rect->origin.y + rect->size.height) {
rect->size.height = point->y - rect->origin.y;
}
if (point->x < rect->origin.x) {
rect->size.width += rect->origin.x - point->x;
rect->origin.x = point->x;
}
if (point->y < rect->origin.y) {
rect->size.height += rect->origin.y - point->y;
rect->origin.y = point->y;
}
}
}
CGRect calculatePathBoundingBox(CGPathRef path) {
__block CGRect result = CGRectMake(0.0, 0.0, 0.0, 0.0);
__block bool isFirst = true;
CGPathApplyWithBlock(path, ^(const CGPathElement * _Nonnull element) {
switch (element->type) {
case kCGPathElementMoveToPoint: {
addPointToBoundingRect(&isFirst, &result, &element->points[0]);
break;
}
case kCGPathElementAddLineToPoint: {
addPointToBoundingRect(&isFirst, &result, &element->points[0]);
break;
}
case kCGPathElementAddCurveToPoint: {
addPointToBoundingRect(&isFirst, &result, &element->points[0]);
addPointToBoundingRect(&isFirst, &result, &element->points[1]);
addPointToBoundingRect(&isFirst, &result, &element->points[2]);
break;
}
case kCGPathElementAddQuadCurveToPoint: {
addPointToBoundingRect(&isFirst, &result, &element->points[0]);
addPointToBoundingRect(&isFirst, &result, &element->points[1]);
break;
}
case kCGPathElementCloseSubpath: {
break;
}
}
});
return result;
}
namespace lottie {
CGPathCocoaImpl::CGPathCocoaImpl() {
_path = CGPathCreateMutable();
}
CGPathCocoaImpl::CGPathCocoaImpl(CGMutablePathRef path) {
CFRetain(path);
_path = path;
}
CGPathCocoaImpl::~CGPathCocoaImpl() {
CGPathRelease(_path);
}
CGRect CGPathCocoaImpl::boundingBox() const {
auto rect = calculatePathBoundingBox(_path);
return CGRect(rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
}
bool CGPathCocoaImpl::empty() const {
return CGPathIsEmpty(_path);
}
std::shared_ptr<CGPath> CGPathCocoaImpl::copyUsingTransform(Transform2D const &transform) const {
CGAffineTransform affineTransform = CGAffineTransformMake(
transform.rows().columns[0][0], transform.rows().columns[0][1],
transform.rows().columns[1][0], transform.rows().columns[1][1],
transform.rows().columns[2][0], transform.rows().columns[2][1]
);
CGPathRef resultPath = CGPathCreateCopyByTransformingPath(_path, &affineTransform);
if (resultPath == nil) {
return nullptr;
}
CGMutablePathRef resultMutablePath = CGPathCreateMutableCopy(resultPath);
CGPathRelease(resultPath);
auto result = std::make_shared<CGPathCocoaImpl>(resultMutablePath);
CGPathRelease(resultMutablePath);
return result;
}
void CGPathCocoaImpl::addLineTo(Vector2D const &point) {
CGPathAddLineToPoint(_path, nil, point.x, point.y);
}
void CGPathCocoaImpl::addCurveTo(Vector2D const &point, Vector2D const &control1, Vector2D const &control2) {
CGPathAddCurveToPoint(_path, nil, control1.x, control1.y, control2.x, control2.y, point.x, point.y);
}
void CGPathCocoaImpl::moveTo(Vector2D const &point) {
CGPathMoveToPoint(_path, nil, point.x, point.y);
}
void CGPathCocoaImpl::closeSubpath() {
CGPathCloseSubpath(_path);
}
void CGPathCocoaImpl::addPath(std::shared_ptr<CGPath> const &path) {
if (CGPathIsEmpty(_path)) {
_path = CGPathCreateMutableCopy(std::static_pointer_cast<CGPathCocoaImpl>(path)->_path);
} else {
CGPathAddPath(_path, nil, std::static_pointer_cast<CGPathCocoaImpl>(path)->_path);
}
}
CGPathRef CGPathCocoaImpl::nativePath() const {
return _path;
}
bool CGPathCocoaImpl::isEqual(CGPath *other) const {
CGPathCocoaImpl *otherImpl = (CGPathCocoaImpl *)other;
return CGPathEqualToPath(_path, otherImpl->_path);
}
void CGPathCocoaImpl::enumerate(std::function<void(CGPathItem const &)> f) {
CGPathApplyWithBlock(_path, ^(const CGPathElement * _Nonnull element) {
CGPathItem item(CGPathItem::Type::MoveTo);
switch (element->type) {
case kCGPathElementMoveToPoint: {
item.type = CGPathItem::Type::MoveTo;
item.points[0] = Vector2D(element->points[0].x, element->points[0].y);
f(item);
break;
}
case kCGPathElementAddLineToPoint: {
item.type = CGPathItem::Type::LineTo;
item.points[0] = Vector2D(element->points[0].x, element->points[0].y);
f(item);
break;
}
case kCGPathElementAddCurveToPoint: {
item.type = CGPathItem::Type::CurveTo;
item.points[0] = Vector2D(element->points[0].x, element->points[0].y);
item.points[1] = Vector2D(element->points[1].x, element->points[1].y);
item.points[2] = Vector2D(element->points[2].x, element->points[2].y);
f(item);
break;
}
case kCGPathElementAddQuadCurveToPoint: {
break;
}
case kCGPathElementCloseSubpath: {
item.type = CGPathItem::Type::Close;
f(item);
break;
}
}
});
}
void CGPathCocoaImpl::withNativePath(std::shared_ptr<CGPath> const &path, std::function<void(CGPathRef)> f) {
CGMutablePathRef result = CGPathCreateMutable();
path->enumerate([result](CGPathItem const &element) {
switch (element.type) {
case CGPathItem::Type::MoveTo: {
CGPathMoveToPoint(result, nullptr, element.points[0].x, element.points[0].y);
break;
}
case CGPathItem::Type::LineTo: {
CGPathAddLineToPoint(result, nullptr, element.points[0].x, element.points[0].y);
break;
}
case CGPathItem::Type::CurveTo: {
CGPathAddCurveToPoint(result, nullptr, element.points[0].x, element.points[0].y, element.points[1].x, element.points[1].y, element.points[2].x, element.points[2].y);
break;
}
case CGPathItem::Type::Close: {
CGPathCloseSubpath(result);
break;
}
default:
break;
}
});
f(result);
CFRelease(result);
}
/*std::shared_ptr<CGPath> CGPath::makePath() {
return std::static_pointer_cast<CGPath>(std::make_shared<CGPathCocoaImpl>());
}*/
}
@@ -0,0 +1,27 @@
#include <LottieCpp/VectorsCocoa.h>
#import <QuartzCore/QuartzCore.h>
namespace lottie {
::CATransform3D nativeTransform(Transform2D const &value) {
CGAffineTransform at = CGAffineTransformMake(
value.rows().columns[0][0], value.rows().columns[0][1],
value.rows().columns[1][0], value.rows().columns[1][1],
value.rows().columns[2][0], value.rows().columns[2][1]
);
return CATransform3DMakeAffineTransform(at);
}
Transform2D fromNativeTransform(::CATransform3D const &value) {
CGAffineTransform at = CATransform3DGetAffineTransform(value);
return Transform2D(
LottieFloat3x3({
lottieSimdMakeFloat3(at.a, at.b, 0.0),
lottieSimdMakeFloat3(at.c, at.d, 0.0),
lottieSimdMakeFloat3(at.tx, at.ty, 1.0)
})
);
}
}