diff --git a/submodules/Display/Display/Navigation/NavigationController.swift b/submodules/Display/Display/Navigation/NavigationController.swift index ef8a17806b..037b2fc65d 100644 --- a/submodules/Display/Display/Navigation/NavigationController.swift +++ b/submodules/Display/Display/Navigation/NavigationController.swift @@ -194,6 +194,8 @@ open class NavigationController: UINavigationController, ContainableController, } var keyboardViewManager: KeyboardViewManager? + var updateSupportedOrientations: (() -> Void)? + public func updateMasterDetailsBlackout(_ blackout: MasterDetailLayoutBlackout?, transition: ContainedViewLayoutTransition) { self.masterDetailsBlackout = blackout if isViewLoaded { @@ -928,6 +930,8 @@ open class NavigationController: UINavigationController, ContainableController, if notifyGlobalOverlayControllersUpdated { self.globalOverlayControllersUpdated?() } + + self.updateSupportedOrientations?() } private func controllerRemoved(_ controller: ViewController) { diff --git a/submodules/Display/Display/WindowContent.swift b/submodules/Display/Display/WindowContent.swift index b172ffe213..868b94b6eb 100644 --- a/submodules/Display/Display/WindowContent.swift +++ b/submodules/Display/Display/WindowContent.swift @@ -725,6 +725,36 @@ public class Window1 { if let rootController = self._rootController { if let rootController = rootController as? NavigationController { rootController.statusBarHost = self.statusBarHost + rootController.updateSupportedOrientations = { [weak self] in + guard let strongSelf = self else { + return + } + + var supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .all) + let orientationToLock: UIInterfaceOrientationMask + if strongSelf.windowLayout.size.width < strongSelf.windowLayout.size.height { + orientationToLock = .portrait + } else { + orientationToLock = .landscape + } + if let _rootController = strongSelf._rootController { + supportedOrientations = supportedOrientations.intersection(_rootController.combinedSupportedOrientations(currentOrientationToLock: orientationToLock)) + } + supportedOrientations = supportedOrientations.intersection(strongSelf.presentationContext.combinedSupportedOrientations(currentOrientationToLock: orientationToLock)) + supportedOrientations = supportedOrientations.intersection(strongSelf.overlayPresentationContext.combinedSupportedOrientations(currentOrientationToLock: orientationToLock)) + + var resolvedOrientations: UIInterfaceOrientationMask + switch strongSelf.windowLayout.metrics.widthClass { + case .regular: + resolvedOrientations = supportedOrientations.regularSize + case .compact: + resolvedOrientations = supportedOrientations.compactSize + } + if resolvedOrientations.isEmpty { + resolvedOrientations = [.portrait] + } + strongSelf.hostView.updateSupportedInterfaceOrientations(resolvedOrientations) + } rootController.keyboardViewManager = self.keyboardViewManager rootController.inCallNavigate = { [weak self] in self?.inCallNavigate?() diff --git a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift index b06086f7ae..a3ee302d57 100644 --- a/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift +++ b/submodules/SettingsUI/Sources/Stickers/InstalledStickerPacksController.swift @@ -514,7 +514,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta controller?.dismissAnimated() } let removeAction: (RemoveStickerPackOption) -> Void = { action in - let _ = (removeStickerPackInteractively(postbox: context.account.postbox, id: archivedItem.info.id, option: .archive) + let _ = (removeStickerPackInteractively(postbox: context.account.postbox, id: archivedItem.info.id, option: action) |> deliverOnMainQueue).start(next: { indexAndItems in guard let (positionInList, items) = indexAndItems else { return @@ -530,7 +530,7 @@ public func installedStickerPacksController(context: AccountContext, mode: Insta } } - navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: presentationData.strings.StickerPackActionInfo_ArchivedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(archivedItem.info.title).0, undo: true, info: archivedItem.info, topItem: archivedItem.topItems.first, account: context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in + navigationControllerImpl?()?.presentOverlay(controller: UndoOverlayController(presentationData: presentationData, content: .stickersModified(title: action == .archive ? presentationData.strings.StickerPackActionInfo_ArchivedTitle : presentationData.strings.StickerPackActionInfo_RemovedTitle, text: presentationData.strings.StickerPackActionInfo_RemovedText(archivedItem.info.title).0, undo: true, info: archivedItem.info, topItem: archivedItem.topItems.first, account: context.account), elevatedLayout: false, animateInAsReplacement: animateInAsReplacement, action: { action in if case .undo = action { let _ = addStickerPackInteractively(postbox: context.account.postbox, info: archivedItem.info, items: items, positionInList: positionInList).start() } diff --git a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift index 535d4aed3a..8442bb3095 100644 --- a/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift +++ b/submodules/SettingsUI/Sources/Themes/WallpaperGalleryItem.swift @@ -640,9 +640,10 @@ final class WallpaperGalleryItemNode: GalleryItemNode { private func preparePatternEditing() { if let entry = self.entry, case let .wallpaper(wallpaper, _) = entry, case let .file(file) = wallpaper { - if let size = file.file.dimensions?.cgSize.fitted(CGSize(width: 1280.0, height: 1280.0)) { - let _ = self.context.account.postbox.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: true).start() - } + let dimensions = file.file.dimensions ?? PixelDimensions(width: 1440, height: 2960) + + let size = dimensions.cgSize.fitted(CGSize(width: 1280.0, height: 1280.0)) + let _ = self.context.account.postbox.mediaBox.cachedResourceRepresentation(file.file.resource, representation: CachedPatternWallpaperMaskRepresentation(size: size), complete: false, fetch: true).start() } } diff --git a/submodules/Svg/BUCK b/submodules/Svg/BUCK index cafb4d41e0..29bf614a72 100644 --- a/submodules/Svg/BUCK +++ b/submodules/Svg/BUCK @@ -24,4 +24,7 @@ static_library( exported_linker_flags = [ "-lxml2", ], + deps = [ + "//submodules/GZip:GZip", + ], ) diff --git a/submodules/Svg/Sources/Svg.m b/submodules/Svg/Sources/Svg.m index 0d346a212f..d4d1ffbdc5 100755 --- a/submodules/Svg/Sources/Svg.m +++ b/submodules/Svg/Sources/Svg.m @@ -10,19 +10,119 @@ CGSize aspectFillSize(CGSize size, CGSize bounds) { return CGSizeMake(floor(size.width * scale), floor(size.height * scale)); } +@interface SvgXMLParsingDelegate : NSObject { + NSString *_elementName; + NSString *_currentStyleString; +} + +@property (nonatomic, strong, readonly) NSMutableDictionary *styles; + +@end + +@implementation SvgXMLParsingDelegate + +- (instancetype)init { + self = [super init]; + if (self != nil) { + _styles = [[NSMutableDictionary alloc] init]; + } + return self; +} + +- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict { + _elementName = elementName; +} + +- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName { + if ([_elementName isEqualToString:@"style"]) { + int currentClassNameStartIndex = -1; + int currentClassContentsStartIndex = -1; + + NSString *currentClassName = nil; + + NSCharacterSet *alphanumeric = [NSCharacterSet alphanumericCharacterSet]; + + for (int i = 0; i < _currentStyleString.length; i++) { + unichar c = [_currentStyleString characterAtIndex:i]; + if (currentClassNameStartIndex != -1) { + if (![alphanumeric characterIsMember:c]) { + currentClassName = [_currentStyleString substringWithRange:NSMakeRange(currentClassNameStartIndex, i - currentClassNameStartIndex)]; + currentClassNameStartIndex = -1; + } + } else if (currentClassContentsStartIndex != -1) { + if (c == '}') { + NSString *classContents = [_currentStyleString substringWithRange:NSMakeRange(currentClassContentsStartIndex, i - currentClassContentsStartIndex)]; + if (currentClassName != nil && classContents != nil) { + _styles[currentClassName] = classContents; + currentClassName = nil; + } + currentClassContentsStartIndex = -1; + } + } + + if (currentClassNameStartIndex == -1 && currentClassContentsStartIndex == -1) { + if (c == '.') { + currentClassNameStartIndex = i + 1; + } else if (c == '{') { + currentClassContentsStartIndex = i + 1; + } + } + } + } + _elementName = nil; +} + +- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string { + if ([_elementName isEqualToString:@"style"]) { + if (_currentStyleString == nil) { + _currentStyleString = string; + } else { + _currentStyleString = [_currentStyleString stringByAppendingString:string]; + } + } +} + +@end + UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size) { - char *zeroTerminatedData = malloc(data.length + 1); - [data getBytes:zeroTerminatedData length:data.length]; - zeroTerminatedData[data.length] = 0; + NSDate *startTime = [NSDate date]; + + NSXMLParser *parser = [[NSXMLParser alloc] initWithData:data]; + if (parser == nil) { + return nil; + } + SvgXMLParsingDelegate *delegate = [[SvgXMLParsingDelegate alloc] init]; + parser.delegate = delegate; + [parser parse]; + + NSMutableString *xmlString = [[NSMutableString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + if (xmlString == nil) { + return nil; + } + + for (NSString *styleName in delegate.styles) { + NSString *styleValue = delegate.styles[styleName]; + [xmlString replaceOccurrencesOfString:[NSString stringWithFormat:@"class=\"%@\"", styleName] withString:[NSString stringWithFormat:@"style=\"%@\"", styleValue] options:0 range:NSMakeRange(0, xmlString.length)]; + } + + char *zeroTerminatedData = xmlString.UTF8String; NSVGimage *image = nsvgParse(zeroTerminatedData, "px", 96); if (image == nil || image->width < 1.0f || image->height < 1.0f) { return nil; } + double deltaTime = -1.0f * [startTime timeIntervalSinceNow]; + printf("parseTime = %f\n", deltaTime); + + startTime = [NSDate date]; + + UIColor *backgroundColor = [UIColor blackColor]; + UIColor *foregroundColor = [UIColor whiteColor]; + UIGraphicsBeginImageContextWithOptions(size, true, 1.0); CGContextRef context = UIGraphicsGetCurrentContext(); - CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor); + CGContextSetFillColorWithColor(context, backgroundColor.CGColor); CGContextFillRect(context, CGRectMake(0.0f, 0.0f, size.width, size.height)); CGContextScaleCTM(context, size.width / image->width, size.height / image->height); @@ -32,7 +132,7 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size) { } if (shape->fill.type != NSVG_PAINT_NONE) { - CGContextSetFillColorWithColor(context, [[UIColor blackColor] colorWithAlphaComponent:shape->opacity].CGColor); + CGContextSetFillColorWithColor(context, [foregroundColor colorWithAlphaComponent:shape->opacity].CGColor); for (NSVGpath *path = shape->paths; path != NULL; path = path->next) { CGContextBeginPath(context); @@ -40,7 +140,6 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size) { for (int i = 0; i < path->npts - 1; i += 3) { float *p = &path->pts[i * 2]; CGContextAddCurveToPoint(context, p[2], p[3], p[4], p[5], p[6], p[7]); - //drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); } switch (shape->fillRule) { @@ -55,8 +154,8 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size) { } if (shape->stroke.type != NSVG_PAINT_NONE) { - CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] colorWithAlphaComponent:shape->opacity].CGColor); - //CGContextSetMiterLimit(context, shape->miterLimit); + CGContextSetStrokeColorWithColor(context, [foregroundColor colorWithAlphaComponent:shape->opacity].CGColor); + CGContextSetMiterLimit(context, shape->miterLimit); CGContextSetLineWidth(context, shape->strokeWidth); switch (shape->strokeLineCap) { @@ -92,7 +191,6 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size) { for (int i = 0; i < path->npts - 1; i += 3) { float *p = &path->pts[i * 2]; CGContextAddCurveToPoint(context, p[2], p[3], p[4], p[5], p[6], p[7]); - //drawCubicBez(p[0],p[1], p[2],p[3], p[4],p[5], p[6],p[7]); } if (path->closed) { @@ -106,6 +204,9 @@ UIImage * _Nullable drawSvgImage(NSData * _Nonnull data, CGSize size) { UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); + deltaTime = -1.0f * [startTime timeIntervalSinceNow]; + printf("drawingTime = %f\n", deltaTime); + nsvgDelete(image); return resultImage; diff --git a/submodules/Svg/Sources/nanosvg.h b/submodules/Svg/Sources/nanosvg.h index 00bc8574f6..e5f6900614 100644 --- a/submodules/Svg/Sources/nanosvg.h +++ b/submodules/Svg/Sources/nanosvg.h @@ -29,9 +29,11 @@ #ifndef NANOSVG_H #define NANOSVG_H +#ifndef NANOSVG_CPLUSPLUS #ifdef __cplusplus extern "C" { #endif +#endif // NanoSVG is a simple stupid single-header-file SVG parse. The output of the parser is a list of cubic bezier shapes. // @@ -45,15 +47,15 @@ extern "C" { // NanoSVG can return the paths in few different units. For example if you want to render an image, you may choose // to get the paths in pixels, or if you are feeding the data into a CNC-cutter, you may want to use millimeters. // -// The units passed to NanoVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. +// The units passed to NanoSVG should be one of: 'px', 'pt', 'pc' 'mm', 'cm', or 'in'. // DPI (dots-per-inch) controls how the unit conversion is done. // // If you don't know or care about the units stuff, "px" and 96 should get you going. /* Example Usage: - // Load - NSVGImage* image; + // Load SVG + NSVGimage* image; image = nsvgParseFromFile("test.svg", "px", 96); printf("size: %f x %f\n", image->width, image->height); // Use... @@ -73,30 +75,30 @@ enum NSVGpaintType { NSVG_PAINT_NONE = 0, NSVG_PAINT_COLOR = 1, NSVG_PAINT_LINEAR_GRADIENT = 2, - NSVG_PAINT_RADIAL_GRADIENT = 3, + NSVG_PAINT_RADIAL_GRADIENT = 3 }; enum NSVGspreadType { NSVG_SPREAD_PAD = 0, NSVG_SPREAD_REFLECT = 1, - NSVG_SPREAD_REPEAT = 2, + NSVG_SPREAD_REPEAT = 2 }; enum NSVGlineJoin { NSVG_JOIN_MITER = 0, NSVG_JOIN_ROUND = 1, - NSVG_JOIN_BEVEL = 2, + NSVG_JOIN_BEVEL = 2 }; enum NSVGlineCap { NSVG_CAP_BUTT = 0, NSVG_CAP_ROUND = 1, - NSVG_CAP_SQUARE = 2, + NSVG_CAP_SQUARE = 2 }; enum NSVGfillRule { NSVG_FILLRULE_NONZERO = 0, - NSVG_FILLRULE_EVENODD = 1, + NSVG_FILLRULE_EVENODD = 1 }; enum NSVGflags { @@ -145,10 +147,10 @@ typedef struct NSVGshape char strokeDashCount; // Number of dash values in dash array. char strokeLineJoin; // Stroke join type. char strokeLineCap; // Stroke cap type. + float miterLimit; // Miter limit char fillRule; // Fill rule, see NSVGfillRule. unsigned char flags; // Logical or of NSVG_FLAGS_* flags float bounds[4]; // Tight bounding box of the shape [minx,miny,maxx,maxy]. - NSVGpath* paths; // Linked list of paths in the image. struct NSVGshape* next; // Pointer to next shape, or NULL if last element. } NSVGshape; @@ -157,7 +159,6 @@ typedef struct NSVGimage { float width; // Width of the image. float height; // Height of the image. - float realBounds[4]; NSVGshape* shapes; // Linked list of shapes in the image. } NSVGimage; @@ -168,11 +169,16 @@ NSVGimage* nsvgParseFromFile(const char* filename, const char* units, float dpi) // Important note: changes the string. NSVGimage* nsvgParse(char* input, const char* units, float dpi); -// Deletes list of paths. +// Duplicates a path. +NSVGpath* nsvgDuplicatePath(NSVGpath* p); + +// Deletes an image. void nsvgDelete(NSVGimage* image); +#ifndef NANOSVG_CPLUSPLUS #ifdef __cplusplus -}; +} +#endif #endif #endif // NANOSVG_H @@ -184,7 +190,7 @@ void nsvgDelete(NSVGimage* image); #include #define NSVG_PI (3.14159265358979323846264338327f) -#define NSVG_KAPPA90 (0.5522847493f) // Lenght proportional to radius of a cubic bezier handle for 90deg arcs. +#define NSVG_KAPPA90 (0.5522847493f) // Length proportional to radius of a cubic bezier handle for 90deg arcs. #define NSVG_ALIGN_MIN 0 #define NSVG_ALIGN_MID 1 @@ -216,7 +222,7 @@ static int nsvg__isspace(char c) static int nsvg__isdigit(char c) { - return strchr("0123456789", c) != 0; + return c >= '0' && c <= '9'; } static int nsvg__isnum(char c) @@ -280,6 +286,9 @@ static void nsvg__parseElement(char* s, // Get attribs while (!end && *s && nattr < NSVG_XML_MAX_ATTRIBS-3) { + char* name = NULL; + char* value = NULL; + // Skip white space before the attrib name while (*s && nsvg__isspace(*s)) s++; if (!*s) break; @@ -287,7 +296,7 @@ static void nsvg__parseElement(char* s, end = 1; break; } - attr[nattr++] = s; + name = s; // Find end of the attrib name. while (*s && !nsvg__isspace(*s) && *s != '=') s++; if (*s) { *s++ = '\0'; } @@ -297,9 +306,15 @@ static void nsvg__parseElement(char* s, quote = *s; s++; // Store value and find the end of it. - attr[nattr++] = s; + value = s; while (*s && *s != quote) s++; if (*s) { *s++ = '\0'; } + + // Store only well formed attributes + if (name && value) { + attr[nattr++] = name; + attr[nattr++] = value; + } } // List terminator @@ -350,7 +365,7 @@ int nsvg__parseXML(char* input, enum NSVGgradientUnits { NSVG_USER_SPACE = 0, - NSVG_OBJECT_SPACE = 1, + NSVG_OBJECT_SPACE = 1 }; #define NSVG_MAX_DASHES 8 @@ -365,7 +380,7 @@ enum NSVGunits { NSVG_UNITS_IN, NSVG_UNITS_PERCENT, NSVG_UNITS_EM, - NSVG_UNITS_EX, + NSVG_UNITS_EX }; typedef struct NSVGcoordinate { @@ -415,6 +430,7 @@ typedef struct NSVGattrib int strokeDashCount; char strokeLineJoin; char strokeLineCap; + float miterLimit; char fillRule; float fontSize; unsigned int stopColor; @@ -425,13 +441,6 @@ typedef struct NSVGattrib char visible; } NSVGattrib; -typedef struct NSVGstyles -{ - char* name; - char* description; - struct NSVGstyles* next; -} NSVGstyles; - typedef struct NSVGparser { NSVGattrib attr[NSVG_MAX_ATTR]; @@ -441,14 +450,13 @@ typedef struct NSVGparser int cpts; NSVGpath* plist; NSVGimage* image; - NSVGstyles* styles; NSVGgradientData* gradients; + NSVGshape* shapesTail; float viewMinx, viewMiny, viewWidth, viewHeight; int alignX, alignY, alignType; float dpi; char pathFlag; char defsFlag; - char styleFlag; } NSVGparser; static void nsvg__xformIdentity(float* t) @@ -631,6 +639,7 @@ static NSVGparser* nsvg__createParser() p->attr[0].strokeWidth = 1; p->attr[0].strokeLineJoin = NSVG_JOIN_MITER; p->attr[0].strokeLineCap = NSVG_CAP_BUTT; + p->attr[0].miterLimit = 4; p->attr[0].fillRule = NSVG_FILLRULE_NONZERO; p->attr[0].hasFill = 1; p->attr[0].visible = 1; @@ -644,17 +653,6 @@ error: } return NULL; } -static void nsvg__deleteStyles(NSVGstyles* style) { - while (style) { - NSVGstyles *next = style->next; - if (style->name!= NULL) - free(style->name); - if (style->description != NULL) - free(style->description); - free(style); - style = next; - } -} static void nsvg__deletePaths(NSVGpath* path) { @@ -687,7 +685,6 @@ static void nsvg__deleteGradientData(NSVGgradientData* grad) static void nsvg__deleteParser(NSVGparser* p) { if (p != NULL) { - nsvg__deleteStyles(p->styles); nsvg__deletePaths(p->plist); nsvg__deleteGradientData(p->gradients); nsvgDelete(p->image); @@ -941,7 +938,7 @@ static void nsvg__addShape(NSVGparser* p) { NSVGattrib* attr = nsvg__getAttr(p); float scale = 1.0f; - NSVGshape *shape, *cur, *prev; + NSVGshape* shape; NSVGpath* path; int i; @@ -956,11 +953,12 @@ static void nsvg__addShape(NSVGparser* p) scale = nsvg__getAverageScale(attr->xform); shape->strokeWidth = attr->strokeWidth * scale; shape->strokeDashOffset = attr->strokeDashOffset * scale; - shape->strokeDashCount = attr->strokeDashCount; + shape->strokeDashCount = (char)attr->strokeDashCount; for (i = 0; i < attr->strokeDashCount; i++) shape->strokeDashArray[i] = attr->strokeDashArray[i] * scale; shape->strokeLineJoin = attr->strokeLineJoin; shape->strokeLineCap = attr->strokeLineCap; + shape->miterLimit = attr->miterLimit; shape->fillRule = attr->fillRule; shape->opacity = attr->opacity; @@ -1016,16 +1014,11 @@ static void nsvg__addShape(NSVGparser* p) shape->flags = (attr->visible ? NSVG_FLAGS_VISIBLE : 0x00); // Add to tail - prev = NULL; - cur = p->image->shapes; - while (cur != NULL) { - prev = cur; - cur = cur->next; - } - if (prev == NULL) + if (p->image->shapes == NULL) p->image->shapes = shape; else - prev->next = shape; + p->shapesTail->next = shape; + p->shapesTail = shape; return; @@ -1089,6 +1082,66 @@ error: } } +// We roll our own string to float because the std library one uses locale and messes things up. +static double nsvg__atof(const char* s) +{ + char* cur = (char*)s; + char* end = NULL; + double res = 0.0, sign = 1.0; + long long intPart = 0, fracPart = 0; + char hasIntPart = 0, hasFracPart = 0; + + // Parse optional sign + if (*cur == '+') { + cur++; + } else if (*cur == '-') { + sign = -1; + cur++; + } + + // Parse integer part + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + intPart = strtoll(cur, &end, 10); + if (cur != end) { + res = (double)intPart; + hasIntPart = 1; + cur = end; + } + } + + // Parse fractional part. + if (*cur == '.') { + cur++; // Skip '.' + if (nsvg__isdigit(*cur)) { + // Parse digit sequence + fracPart = strtoll(cur, &end, 10); + if (cur != end) { + res += (double)fracPart / pow(10.0, (double)(end - cur)); + hasFracPart = 1; + cur = end; + } + } + } + + // A valid number should have integer or fractional part. + if (!hasIntPart && !hasFracPart) + return 0.0; + + // Parse optional exponent + if (*cur == 'e' || *cur == 'E') { + long expPart = 0; + cur++; // skip 'E' + expPart = strtol(cur, &end, 10); // Parse digit sequence with sign + if (cur != end) { + res *= pow(10.0, (double)expPart); + } + } + + return res * sign; +} + + static const char* nsvg__parseNumber(const char* s, char* it, const int size) { const int last = size-1; @@ -1115,7 +1168,7 @@ static const char* nsvg__parseNumber(const char* s, char* it, const int size) } } // exponent - if (*s == 'e' || *s == 'E') { + if ((*s == 'e' || *s == 'E') && (s[1] != 'm' && s[1] != 'x')) { if (i < last) it[i++] = *s; s++; if (*s == '-' || *s == '+') { @@ -1369,13 +1422,19 @@ static unsigned int nsvg__parseColor(const char* str) static float nsvg__parseOpacity(const char* str) { - float val = 0; - sscanf(str, "%f", &val); + float val = nsvg__atof(str); if (val < 0.0f) val = 0.0f; if (val > 1.0f) val = 1.0f; return val; } +static float nsvg__parseMiterLimit(const char* str) +{ + float val = nsvg__atof(str); + if (val < 0.0f) val = 0.0f; + return val; +} + static int nsvg__parseUnits(const char* units) { if (units[0] == 'p' && units[1] == 'x') @@ -1402,9 +1461,9 @@ static int nsvg__parseUnits(const char* units) static NSVGcoordinate nsvg__parseCoordinateRaw(const char* str) { NSVGcoordinate coord = {0, NSVG_UNITS_USER}; - char units[32]=""; - sscanf(str, "%f%s", &coord.value, units); - coord.units = nsvg__parseUnits(units); + char buf[64]; + coord.units = nsvg__parseUnits(nsvg__parseNumber(str, buf, 64)); + coord.value = nsvg__atof(buf); return coord; } @@ -1440,7 +1499,7 @@ static int nsvg__parseTransformArgs(const char* str, float* args, int maxNa, int if (*ptr == '-' || *ptr == '+' || *ptr == '.' || nsvg__isdigit(*ptr)) { if (*na >= maxNa) return 0; ptr = nsvg__parseNumber(ptr, it, 64); - args[(*na)++] = (float)atof(it); + args[(*na)++] = (float)nsvg__atof(it); } else { ++ptr; } @@ -1596,7 +1655,7 @@ static char nsvg__parseLineJoin(const char* str) else if (strcmp(str, "bevel") == 0) return NSVG_JOIN_BEVEL; // TODO: handle inherit. - return NSVG_CAP_BUTT; + return NSVG_JOIN_MITER; } static char nsvg__parseFillRule(const char* str) @@ -1702,6 +1761,8 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) attr->strokeLineCap = nsvg__parseLineCap(value); } else if (strcmp(name, "stroke-linejoin") == 0) { attr->strokeLineJoin = nsvg__parseLineJoin(value); + } else if (strcmp(name, "stroke-miterlimit") == 0) { + attr->miterLimit = nsvg__parseMiterLimit(value); } else if (strcmp(name, "fill-rule") == 0) { attr->fillRule = nsvg__parseFillRule(value); } else if (strcmp(name, "font-size") == 0) { @@ -1718,21 +1779,9 @@ static int nsvg__parseAttr(NSVGparser* p, const char* name, const char* value) } else if (strcmp(name, "id") == 0) { strncpy(attr->id, value, 63); attr->id[63] = '\0'; - } else if (strcmp(name, "class") == 0) { - NSVGstyles* style = p->styles; - while (style) { - if (strcmp(style->name + 1, value) == 0) { - break; - } - style = style->next; - } - if (style) { - nsvg__parseStyle(p, style->description); - } - } - else { + } else { return 0; - } + } return 1; } @@ -2030,7 +2079,7 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, rx = fabsf(args[0]); // y radius ry = fabsf(args[1]); // x radius - rotx = args[2] / 180.0f * NSVG_PI; // x rotation engle + rotx = args[2] / 180.0f * NSVG_PI; // x rotation angle fa = fabsf(args[3]) > 1e-6 ? 1 : 0; // Large arc fs = fabsf(args[4]) > 1e-6 ? 1 : 0; // Sweep direction x1 = *cpx; // start point @@ -2095,13 +2144,10 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, // if (vecrat(ux,uy,vx,vy) <= -1.0f) da = NSVG_PI; // if (vecrat(ux,uy,vx,vy) >= 1.0f) da = 0; - if (fa) { - // Choose large arc - if (da > 0.0f) - da = da - 2*NSVG_PI; - else - da = 2*NSVG_PI + da; - } + if (fs == 0 && da > 0) + da -= 2 * NSVG_PI; + else if (fs == 1 && da < 0) + da += 2 * NSVG_PI; // Approximate the arc using cubic spline segments. t[0] = cosrx; t[1] = sinrx; @@ -2117,7 +2163,7 @@ static void nsvg__pathArcTo(NSVGparser* p, float* cpx, float* cpy, float* args, kappa = -kappa; for (i = 0; i <= ndivs; i++) { - a = a1 + da * (i/(float)ndivs); + a = a1 + da * ((float)i/(float)ndivs); dx = cosf(a); dy = sinf(a); nsvg__xformPoint(&x, &y, dx*rx, dy*ry, t); // position @@ -2171,7 +2217,7 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr) if (!*item) break; if (nsvg__isnum(item[0])) { if (nargs < 10) - args[nargs++] = (float)atof(item); + args[nargs++] = (float)nsvg__atof(item); if (nargs >= rargs) { switch (cmd) { case 'm': @@ -2180,23 +2226,23 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr) // Moveto can be followed by multiple coordinate pairs, // which should be treated as linetos. cmd = (cmd == 'm') ? 'l' : 'L'; - rargs = nsvg__getArgsPerElement(cmd); - cpx2 = cpx; cpy2 = cpy; + rargs = nsvg__getArgsPerElement(cmd); + cpx2 = cpx; cpy2 = cpy; break; case 'l': case 'L': nsvg__pathLineTo(p, &cpx, &cpy, args, cmd == 'l' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; + cpx2 = cpx; cpy2 = cpy; break; case 'H': case 'h': nsvg__pathHLineTo(p, &cpx, &cpy, args, cmd == 'h' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; + cpx2 = cpx; cpy2 = cpy; break; case 'V': case 'v': nsvg__pathVLineTo(p, &cpx, &cpy, args, cmd == 'v' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; + cpx2 = cpx; cpy2 = cpy; break; case 'C': case 'c': @@ -2217,13 +2263,13 @@ static void nsvg__parsePath(NSVGparser* p, const char** attr) case 'A': case 'a': nsvg__pathArcTo(p, &cpx, &cpy, args, cmd == 'a' ? 1 : 0); - cpx2 = cpx; cpy2 = cpy; + cpx2 = cpx; cpy2 = cpy; break; default: if (nargs >= 2) { cpx = args[nargs-2]; cpy = args[nargs-1]; - cpx2 = cpx; cpy2 = cpy; + cpx2 = cpx; cpy2 = cpy; } break; } @@ -2428,7 +2474,7 @@ static void nsvg__parsePoly(NSVGparser* p, const char** attr, int closeFlag) nargs = 0; while (*s) { s = nsvg__getNextPathItem(s, item); - args[nargs++] = (float)atof(item); + args[nargs++] = (float)nsvg__atof(item); if (nargs >= 2) { if (npts == 0) nsvg__moveTo(p, args[0], args[1]); @@ -2453,11 +2499,26 @@ static void nsvg__parseSVG(NSVGparser* p, const char** attr) for (i = 0; attr[i]; i += 2) { if (!nsvg__parseAttr(p, attr[i], attr[i + 1])) { if (strcmp(attr[i], "width") == 0) { - p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f); + p->image->width = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); } else if (strcmp(attr[i], "height") == 0) { - p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 1.0f); + p->image->height = nsvg__parseCoordinate(p, attr[i + 1], 0.0f, 0.0f); } else if (strcmp(attr[i], "viewBox") == 0) { - sscanf(attr[i + 1], "%f%*[%%, \t]%f%*[%%, \t]%f%*[%%, \t]%f", &p->viewMinx, &p->viewMiny, &p->viewWidth, &p->viewHeight); + const char *s = attr[i + 1]; + char buf[64]; + s = nsvg__parseNumber(s, buf, 64); + p->viewMinx = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewMiny = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewWidth = nsvg__atof(buf); + while (*s && (nsvg__isspace(*s) || *s == '%' || *s == ',')) s++; + if (!*s) return; + s = nsvg__parseNumber(s, buf, 64); + p->viewHeight = nsvg__atof(buf); } else if (strcmp(attr[i], "preserveAspectRatio") == 0) { if (strstr(attr[i + 1], "none") != 0) { // No uniform scaling @@ -2658,8 +2719,6 @@ static void nsvg__startElement(void* ud, const char* el, const char** attr) p->defsFlag = 1; } else if (strcmp(el, "svg") == 0) { nsvg__parseSVG(p, attr); - } else if (strcmp(el, "style") == 0) { - p->styleFlag = 1; } } @@ -2673,76 +2732,13 @@ static void nsvg__endElement(void* ud, const char* el) p->pathFlag = 0; } else if (strcmp(el, "defs") == 0) { p->defsFlag = 0; - } else if (strcmp(el, "style") == 0) { - p->styleFlag = 0; } } -static char *nsvg__strndup(const char *s, size_t n) -{ - char *result; - size_t len = strlen(s); - - if (n < len) - len = n; - - result = (char *)malloc(len + 1); - if (!result) - return 0; - - result[len] = '\0'; - return (char *)memcpy(result, s, len); -} - static void nsvg__content(void* ud, const char* s) { - NSVGparser* p = (NSVGparser*)ud; - if (p->styleFlag) { - - int state = 0; - const char* start; - while (*s) { - char c = *s; - if (nsvg__isspace(c) || c == '{') { - if (state == 1) { - NSVGstyles* next = p->styles; - - p->styles = (NSVGstyles*)malloc(sizeof(NSVGstyles)); - p->styles->next = next; - p->styles->name = nsvg__strndup(start, (size_t)(s - start)); - start = s + 1; - state = 2; - } - } else if (state == 2 && c == '}') { - p->styles->description = nsvg__strndup(start, (size_t)(s - start)); - state = 0; - } - else if (state == 0) { - start = s; - state = 1; - } - s++; - } - // if (*s == '{' && state == NSVG_XML_CONTENT) { - // // Start of a tag - // *s++ = '\0'; - // nsvg__parseContent(mark, contentCb, ud); - // mark = s; - // state = NSVG_XML_TAG; - // } - // else if (*s == '>' && state == NSVG_XML_TAG) { - // // Start of a content or new tag. - // *s++ = '\0'; - // nsvg__parseElement(mark, startelCb, endelCb, ud); - // mark = s; - // state = NSVG_XML_CONTENT; - // } - // else { - // s++; - // } - //} - - } + NSVG_NOTUSED(ud); + NSVG_NOTUSED(s); // empty } @@ -2750,25 +2746,19 @@ static void nsvg__imageBounds(NSVGparser* p, float* bounds) { NSVGshape* shape; shape = p->image->shapes; - int count = 0; - - bounds[0] = FLT_MAX; - bounds[1] = FLT_MAX; - bounds[2] = -FLT_MAX; - bounds[3] = -FLT_MAX; - - for (; shape != NULL; shape = shape->next) { - if ( (shape->flags & NSVG_FLAGS_VISIBLE) == NSVG_FLAGS_VISIBLE) { - bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); - bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); - bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); - bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); - ++count; - } - } - - if (count == 0) { + if (shape == NULL) { bounds[0] = bounds[1] = bounds[2] = bounds[3] = 0.0; + return; + } + bounds[0] = shape->bounds[0]; + bounds[1] = shape->bounds[1]; + bounds[2] = shape->bounds[2]; + bounds[3] = shape->bounds[3]; + for (shape = shape->next; shape != NULL; shape = shape->next) { + bounds[0] = nsvg__minf(bounds[0], shape->bounds[0]); + bounds[1] = nsvg__minf(bounds[1], shape->bounds[1]); + bounds[2] = nsvg__maxf(bounds[2], shape->bounds[2]); + bounds[3] = nsvg__maxf(bounds[3], shape->bounds[3]); } } @@ -2784,12 +2774,12 @@ static float nsvg__viewAlign(float content, float container, int type) static void nsvg__scaleGradient(NSVGgradient* grad, float tx, float ty, float sx, float sy) { - grad->xform[0] *= sx; - grad->xform[1] *= sx; - grad->xform[2] *= sy; - grad->xform[3] *= sy; - grad->xform[4] += tx*sx; - grad->xform[5] += ty*sx; + float t[6]; + nsvg__xformSetTranslation(t, tx, ty); + nsvg__xformMultiply (grad->xform, t); + + nsvg__xformSetScale(t, sx, sy); + nsvg__xformMultiply (grad->xform, t); } static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) @@ -2802,11 +2792,7 @@ static void nsvg__scaleToViewbox(NSVGparser* p, const char* units) // Guess image size if not set completely. nsvg__imageBounds(p, bounds); - // Patch: save real bounds. - for (i = 0; i < 4; ++i) { - p->image->realBounds[i] = bounds[i]; - } - + if (p->viewWidth == 0) { if (p->image->width > 0) { p->viewWidth = p->image->width; @@ -2940,6 +2926,36 @@ error: return NULL; } +NSVGpath* nsvgDuplicatePath(NSVGpath* p) +{ + NSVGpath* res = NULL; + + if (p == NULL) + return NULL; + + res = (NSVGpath*)malloc(sizeof(NSVGpath)); + if (res == NULL) goto error; + memset(res, 0, sizeof(NSVGpath)); + + res->pts = (float*)malloc(p->npts*2*sizeof(float)); + if (res->pts == NULL) goto error; + memcpy(res->pts, p->pts, p->npts * sizeof(float) * 2); + res->npts = p->npts; + + memcpy(res->bounds, p->bounds, sizeof(p->bounds)); + + res->closed = p->closed; + + return res; + +error: + if (res != NULL) { + free(res->pts); + free(res); + } + return NULL; +} + void nsvgDelete(NSVGimage* image) { NSVGshape *snext, *shape; diff --git a/submodules/TelegramCore/Sources/AddPeerMember.swift b/submodules/TelegramCore/Sources/AddPeerMember.swift index 138d4b3029..4bc8e65ca0 100644 --- a/submodules/TelegramCore/Sources/AddPeerMember.swift +++ b/submodules/TelegramCore/Sources/AddPeerMember.swift @@ -98,7 +98,7 @@ public func addChannelMember(account: Account, peerId: PeerId, memberId: PeerId) return .fail(.tooMuchJoined) case "USERS_TOO_MUCH": return .fail(.limitExceeded) - case "USER_PRIVACY_RESTRICTED": + case "USER_PRIVACY_RESTRICTED", "USER_NOT_MUTUAL_CONTACT": return .fail(.restricted) case "USER_BOT": return .fail(.bot(memberId)) @@ -194,7 +194,7 @@ public func addChannelMembers(account: Account, peerId: PeerId, memberIds: [Peer switch error.errorDescription { case "CHANNELS_TOO_MUCH": return .tooMuchJoined - case "USER_PRIVACY_RESTRICTED": + case "USER_PRIVACY_RESTRICTED", "USER_NOT_MUTUAL_CONTACT": return .restricted case "USERS_TOO_MUCH": return .limitExceeded diff --git a/submodules/TelegramCore/Sources/Wallpaper.swift b/submodules/TelegramCore/Sources/Wallpaper.swift index 1ee03d182b..4d41fa0368 100644 --- a/submodules/TelegramCore/Sources/Wallpaper.swift +++ b/submodules/TelegramCore/Sources/Wallpaper.swift @@ -47,7 +47,7 @@ extension TelegramWallpaper { } self = .file(id: id, accessHash: accessHash, isCreator: (flags & 1 << 0) != 0, isDefault: (flags & 1 << 1) != 0, isPattern: (flags & 1 << 3) != 0, isDark: (flags & 1 << 4) != 0, slug: slug, file: file, settings: wallpaperSettings) } else { - assertionFailure() + //assertionFailure() self = .color(0xffffff) } case let .wallPaperNoFile(flags, settings): diff --git a/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift b/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift index 7b5bcfcfd6..1f7b5607f5 100644 --- a/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift +++ b/submodules/TelegramUI/TelegramUI/FetchCachedRepresentations.swift @@ -21,6 +21,7 @@ import ImageBlur import TelegramAnimatedStickerNode import WallpaperResources import Svg +import GZip public func fetchCachedResourceRepresentation(account: Account, resource: MediaResource, representation: CachedMediaResourceRepresentation) -> Signal { if let representation = representation as? CachedStickerAJpegRepresentation { @@ -409,10 +410,14 @@ private func fetchCachedBlurredWallpaperRepresentation(resource: MediaResource, private func fetchCachedPatternWallpaperMaskRepresentation(resource: MediaResource, resourceData: MediaResourceData, representation: CachedPatternWallpaperMaskRepresentation) -> Signal { return Signal({ subscriber in - if let data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) { + if var data = try? Data(contentsOf: URL(fileURLWithPath: resourceData.path), options: [.mappedIfSafe]) { let path = NSTemporaryDirectory() + "\(arc4random64())" let url = URL(fileURLWithPath: path) + if let unzippedData = TGGUnzipData(data, 2 * 1024 * 1024) { + data = unzippedData + } + if data.count > 5, let string = String(data: data.subdata(in: 0 ..< 5), encoding: .utf8), string == "