diff --git a/Examples/SDWebImage Demo/MasterViewController.m b/Examples/SDWebImage Demo/MasterViewController.m index 58fd5742..242b329d 100644 --- a/Examples/SDWebImage Demo/MasterViewController.m +++ b/Examples/SDWebImage Demo/MasterViewController.m @@ -72,6 +72,7 @@ @"http://littlesvr.ca/apng/images/world-cup-2014-42.webp", @"https://isparta.github.io/compare-webp/image/gif_webp/webp/2.webp", @"https://nokiatech.github.io/heif/content/images/ski_jump_1440x960.heic", + @"https://nokiatech.github.io/heif/content/image_sequences/starfield_animation.heic", @"https://nr-platform.s3.amazonaws.com/uploads/platform/published_extension/branding_icon/275/AmazonS3.png", @"http://via.placeholder.com/200x200.jpg", nil]; diff --git a/SDWebImage.xcodeproj/project.pbxproj b/SDWebImage.xcodeproj/project.pbxproj index 01e969f7..b0a739e3 100644 --- a/SDWebImage.xcodeproj/project.pbxproj +++ b/SDWebImage.xcodeproj/project.pbxproj @@ -149,6 +149,11 @@ 32935D2D22A4FEDE0049C068 /* UIImageView+WebCache.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 53922D95148C56230056699D /* UIImageView+WebCache.h */; }; 32935D2E22A4FEDE0049C068 /* UIView+WebCache.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 4369C2751D9807EC007E863A /* UIView+WebCache.h */; }; 32935D2F22A4FEE50049C068 /* SDWebImage.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 4A2CAE031AB4BB5400B6BC39 /* SDWebImage.h */; }; + 3298655C2337230C0071958B /* SDImageHEICCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = 3298655A2337230C0071958B /* SDImageHEICCoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 3298655D2337230C0071958B /* SDImageHEICCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 3298655B2337230C0071958B /* SDImageHEICCoder.m */; }; + 3298655E2337230C0071958B /* SDImageHEICCoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 3298655B2337230C0071958B /* SDImageHEICCoder.m */; }; + 3298655F233723220071958B /* SDImageHEICCoder.h in Copy Headers */ = {isa = PBXBuildFile; fileRef = 3298655A2337230C0071958B /* SDImageHEICCoder.h */; }; + 32986562233737C70071958B /* SDImageHEICCoderInternal.h in Headers */ = {isa = PBXBuildFile; fileRef = 32986560233737C70071958B /* SDImageHEICCoderInternal.h */; settings = {ATTRIBUTES = (Private, ); }; }; 329A185B1FFF5DFD008C9A2F /* UIImage+Metadata.h in Headers */ = {isa = PBXBuildFile; fileRef = 329A18571FFF5DFD008C9A2F /* UIImage+Metadata.h */; settings = {ATTRIBUTES = (Public, ); }; }; 329A185F1FFF5DFD008C9A2F /* UIImage+Metadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 329A18581FFF5DFD008C9A2F /* UIImage+Metadata.m */; }; 329A18611FFF5DFD008C9A2F /* UIImage+Metadata.m in Sources */ = {isa = PBXBuildFile; fileRef = 329A18581FFF5DFD008C9A2F /* UIImage+Metadata.m */; }; @@ -275,6 +280,7 @@ dstPath = include/SDWebImage; dstSubfolderSpec = 16; files = ( + 3298655F233723220071958B /* SDImageHEICCoder.h in Copy Headers */, 32C78E3823336FC800C6B7F8 /* SDImageIOAnimatedCoder.h in Copy Headers */, 32E5690822B1FFCA00CBABC6 /* SDWebImageOptionsProcessor.h in Copy Headers */, 32935D2F22A4FEE50049C068 /* SDWebImage.h in Copy Headers */, @@ -388,6 +394,9 @@ 328BB6C02082581100760D6C /* SDMemoryCache.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SDMemoryCache.m; path = Core/SDMemoryCache.m; sourceTree = ""; }; 3290FA021FA478AF0047D20C /* SDImageFrame.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDImageFrame.h; path = Core/SDImageFrame.h; sourceTree = ""; }; 3290FA031FA478AF0047D20C /* SDImageFrame.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDImageFrame.m; path = Core/SDImageFrame.m; sourceTree = ""; }; + 3298655A2337230C0071958B /* SDImageHEICCoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SDImageHEICCoder.h; path = Core/SDImageHEICCoder.h; sourceTree = ""; }; + 3298655B2337230C0071958B /* SDImageHEICCoder.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; name = SDImageHEICCoder.m; path = Core/SDImageHEICCoder.m; sourceTree = ""; }; + 32986560233737C70071958B /* SDImageHEICCoderInternal.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SDImageHEICCoderInternal.h; sourceTree = ""; }; 329A18571FFF5DFD008C9A2F /* UIImage+Metadata.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "UIImage+Metadata.h"; path = "Core/UIImage+Metadata.h"; sourceTree = ""; }; 329A18581FFF5DFD008C9A2F /* UIImage+Metadata.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = "UIImage+Metadata.m"; path = "Core/UIImage+Metadata.m"; sourceTree = ""; }; 329F1235223FAA3B00B309FD /* SDmetamacros.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SDmetamacros.h; sourceTree = ""; }; @@ -512,6 +521,8 @@ 321E60A11F38E8F600405457 /* SDImageGIFCoder.m */, 327054D2206CD8B3006EA328 /* SDImageAPNGCoder.h */, 327054D3206CD8B3006EA328 /* SDImageAPNGCoder.m */, + 3298655A2337230C0071958B /* SDImageHEICCoder.h */, + 3298655B2337230C0071958B /* SDImageHEICCoder.m */, 3290FA021FA478AF0047D20C /* SDImageFrame.h */, 3290FA031FA478AF0047D20C /* SDImageFrame.m */, 32CF1C051FA496B000004BD1 /* SDImageCoderHelper.h */, @@ -582,6 +593,7 @@ 325C460C223394D8004CAE11 /* SDImageCachesManagerOperation.h */, 325C460D223394D8004CAE11 /* SDImageCachesManagerOperation.m */, 32C78E39233371AD00C6B7F8 /* SDImageIOAnimatedCoderInternal.h */, + 32986560233737C70071958B /* SDImageHEICCoderInternal.h */, 325C461E2233A02E004CAE11 /* UIColor+HexString.h */, 325C461F2233A02E004CAE11 /* UIColor+HexString.m */, 325C46242233A0A8004CAE11 /* NSBezierPath+RoundedCorners.h */, @@ -789,7 +801,9 @@ buildActionMask = 2147483647; files = ( 32B5CC60222F89C2005EB74E /* SDAsyncBlockOperation.h in Headers */, + 32986562233737C70071958B /* SDImageHEICCoderInternal.h in Headers */, 32D122202080B2EB003685A3 /* SDImageCacheDefine.h in Headers */, + 3298655C2337230C0071958B /* SDImageHEICCoder.h in Headers */, 32B9B539206ED4230026769D /* SDWebImageDownloaderConfig.h in Headers */, 3257EAFA21898AED0097B271 /* SDImageGraphics.h in Headers */, 32D3CDD121DDE87300C4DB49 /* UIImage+MemoryCacheCost.h in Headers */, @@ -1056,6 +1070,7 @@ 321B37952083290E00C0EA77 /* SDImageLoadersManager.m in Sources */, 4A2CAE361AB4BB7500B6BC39 /* UIImageView+WebCache.m in Sources */, 4A2CAE1E1AB4BB6800B6BC39 /* SDWebImageDownloaderOperation.m in Sources */, + 3298655E2337230C0071958B /* SDImageHEICCoder.m in Sources */, 32F7C0802030719600873181 /* UIImage+Transform.m in Sources */, 327054DC206CD8B3006EA328 /* SDImageAPNGCoder.m in Sources */, 325312D0200F09910046BF1E /* SDWebImageTransition.m in Sources */, @@ -1120,6 +1135,7 @@ 5376130B155AD0D5005750A4 /* SDWebImageDownloader.m in Sources */, 321B37932083290E00C0EA77 /* SDImageLoadersManager.m in Sources */, 32F7C07E2030719600873181 /* UIImage+Transform.m in Sources */, + 3298655D2337230C0071958B /* SDImageHEICCoder.m in Sources */, 321E609A1F38E8ED00405457 /* SDImageIOCoder.m in Sources */, 327054DA206CD8B3006EA328 /* SDImageAPNGCoder.m in Sources */, 325312CE200F09910046BF1E /* SDWebImageTransition.m in Sources */, diff --git a/SDWebImage/Core/SDImageCodersManager.m b/SDWebImage/Core/SDImageCodersManager.m index e9364646..676fc7e0 100644 --- a/SDWebImage/Core/SDImageCodersManager.m +++ b/SDWebImage/Core/SDImageCodersManager.m @@ -10,6 +10,7 @@ #import "SDImageIOCoder.h" #import "SDImageGIFCoder.h" #import "SDImageAPNGCoder.h" +#import "SDImageHEICCoder.h" #import "SDInternalMacros.h" @interface SDImageCodersManager () @@ -36,6 +37,9 @@ if (self = [super init]) { // initialize with default coders _imageCoders = [NSMutableArray arrayWithArray:@[[SDImageIOCoder sharedCoder], [SDImageGIFCoder sharedCoder], [SDImageAPNGCoder sharedCoder]]]; + if (@available(iOS 11, macOS 10.14, tvOS 11, watchOS 4, *)) { + [_imageCoders addObject:[SDImageHEICCoder sharedCoder]]; + } _codersLock = dispatch_semaphore_create(1); } return self; diff --git a/SDWebImage/Core/SDImageHEICCoder.h b/SDWebImage/Core/SDImageHEICCoder.h new file mode 100644 index 00000000..8253ed86 --- /dev/null +++ b/SDWebImage/Core/SDImageHEICCoder.h @@ -0,0 +1,23 @@ +/* +* This file is part of the SDWebImage package. +* (c) Olivier Poitrey +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +#import +#import "SDImageIOAnimatedCoder.h" + +/** + This coder is used for HEIC (HEIF with HEVC container codec) image format. + Image/IO provide the static HEIC (.heic) support in iOS 11/macOS 10.13/tvOS 11/watchOS 4+. + Image/IO provide the animated HEIC (.heics) support in iOS 13/macOS 10.15/tvOS 13/watchOS 6+. + See https://nokiatech.github.io/heif/technical.html for the standard. + @note If you need to support lower firmware version for HEIF, you can have a try at https://github.com/SDWebImage/SDWebImageHEIFCoder + */ +@interface SDImageHEICCoder : SDImageIOAnimatedCoder + +@property (nonatomic, class, readonly, nonnull) SDImageHEICCoder *sharedCoder; + +@end diff --git a/SDWebImage/Core/SDImageHEICCoder.m b/SDWebImage/Core/SDImageHEICCoder.m new file mode 100644 index 00000000..086c606c --- /dev/null +++ b/SDWebImage/Core/SDImageHEICCoder.m @@ -0,0 +1,161 @@ +#import "SDImageHEICCoder.h" + +// AVFileTypeHEIC/AVFileTypeHEIF is defined in AVFoundation via iOS 11, we use this without import AVFoundation +#define kSDUTTypeHEIC ((__bridge CFStringRef)@"public.heic") + +// These constantce are available from iOS 13+ and Xcode 11. This raw value is used for toolchain and firmware compatiblitiy +static CFStringRef kSDCGImagePropertyHEICSDictionary = (__bridge CFStringRef)@"{HEICS}"; +static CFStringRef kSDCGImagePropertyHEICSLoopCount = (__bridge CFStringRef)@"LoopCount"; +static CFStringRef kSDCGImagePropertyHEICSDelayTime = (__bridge CFStringRef)@"DelayTime"; +static CFStringRef kSDCGImagePropertyHEICSUnclampedDelayTime = (__bridge CFStringRef)@"UnclampedDelayTime"; + +@implementation SDImageHEICCoder + ++ (void)initialize { +#if __IPHONE_13_0 || __TVOS_13_0 || __MAC_10_15 || __WATCHOS_6_0 + // Xcode 11 + if (@available(iOS 13, tvOS 13, macOS 10.15, watchOS 6, *)) { + // Use SDK instead of raw value + kSDCGImagePropertyHEICSDictionary = kCGImagePropertyHEICSDictionary; + kSDCGImagePropertyHEICSLoopCount = kCGImagePropertyHEICSLoopCount; + kSDCGImagePropertyHEICSDelayTime = kCGImagePropertyHEICSDelayTime; + kSDCGImagePropertyHEICSUnclampedDelayTime = kCGImagePropertyHEICSUnclampedDelayTime; + } +#endif +} + ++ (instancetype)sharedCoder { + static SDImageHEICCoder *coder; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + coder = [[SDImageHEICCoder alloc] init]; + }); + return coder; +} + +#pragma mark - SDImageCoder + +- (BOOL)canDecodeFromData:(nullable NSData *)data { + switch ([NSData sd_imageFormatForImageData:data]) { + case SDImageFormatHEIC: + // Check HEIC decoding compatibility + return [SDImageHEICCoder canDecodeFromHEICFormat]; + case SDImageFormatHEIF: + // Check HEIF decoding compatibility + return [SDImageHEICCoder canDecodeFromHEIFFormat]; + default: + return NO; + } +} + +- (BOOL)canIncrementalDecodeFromData:(NSData *)data { + return [self canDecodeFromData:data]; +} + +- (BOOL)canEncodeToFormat:(SDImageFormat)format { + switch (format) { + case SDImageFormatHEIC: + // Check HEIC encoding compatibility + return [SDImageHEICCoder canEncodeToHEICFormat]; + case SDImageFormatHEIF: + // Check HEIF encoding compatibility + return [SDImageHEICCoder canEncodeToHEIFFormat]; + default: + return NO; + } +} + +#pragma mark - HEIF Format + ++ (BOOL)canDecodeFromFormat:(SDImageFormat)format { + CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format]; + NSArray *imageUTTypes = (__bridge_transfer NSArray *)CGImageSourceCopyTypeIdentifiers(); + if ([imageUTTypes containsObject:(__bridge NSString *)(imageUTType)]) { + return YES; + } + return NO; +} + ++ (BOOL)canDecodeFromHEICFormat { + static BOOL canDecode = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + canDecode = [self canDecodeFromFormat:SDImageFormatHEIC]; + }); + return canDecode; +} + ++ (BOOL)canDecodeFromHEIFFormat { + static BOOL canDecode = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + canDecode = [self canDecodeFromFormat:SDImageFormatHEIF]; + }); + return canDecode; +} + ++ (BOOL)canEncodeToFormat:(SDImageFormat)format { + NSMutableData *imageData = [NSMutableData data]; + CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format]; + + // Create an image destination. + CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL); + if (!imageDestination) { + // Can't encode to HEIC + return NO; + } else { + // Can encode to HEIC + CFRelease(imageDestination); + return YES; + } +} + ++ (BOOL)canEncodeToHEICFormat { + static BOOL canEncode = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + canEncode = [self canEncodeToFormat:SDImageFormatHEIC]; + }); + return canEncode; +} + ++ (BOOL)canEncodeToHEIFFormat { + static BOOL canEncode = NO; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + canEncode = [self canEncodeToFormat:SDImageFormatHEIF]; + }); + return canEncode; +} + +#pragma mark - Subclass Override + ++ (SDImageFormat)imageFormat { + return SDImageFormatHEIC; +} + ++ (NSString *)imageUTType { + return (__bridge NSString *)kSDUTTypeHEIC; +} + ++ (NSString *)dictionaryProperty { + return (__bridge NSString *)kSDCGImagePropertyHEICSDictionary; +} + ++ (NSString *)unclampedDelayTimeProperty { + return (__bridge NSString *)kSDCGImagePropertyHEICSUnclampedDelayTime; +} + ++ (NSString *)delayTimeProperty { + return (__bridge NSString *)kSDCGImagePropertyHEICSDelayTime; +} + ++ (NSString *)loopCountProperty { + return (__bridge NSString *)kSDCGImagePropertyHEICSLoopCount; +} + ++ (NSUInteger)defaultLoopCount { + return 0; +} + +@end diff --git a/SDWebImage/Core/SDImageIOAnimatedCoder.h b/SDWebImage/Core/SDImageIOAnimatedCoder.h index 4e73ca39..4d651e8d 100644 --- a/SDWebImage/Core/SDImageIOAnimatedCoder.h +++ b/SDWebImage/Core/SDImageIOAnimatedCoder.h @@ -25,6 +25,7 @@ @property (class, readonly) SDImageFormat imageFormat; /** The supported image format UTI Type. Such as `kUTTypeGIF`. + This can be used for cases when we can not detect `SDImageFormat. Such as progressive decoding's hint format `kCGImageSourceTypeIdentifierHint`. @note Subclass override. */ @property (class, readonly, nonnull) NSString *imageUTType; diff --git a/SDWebImage/Core/SDImageIOAnimatedCoder.m b/SDWebImage/Core/SDImageIOAnimatedCoder.m index 632bd172..b554fceb 100644 --- a/SDWebImage/Core/SDImageIOAnimatedCoder.m +++ b/SDWebImage/Core/SDImageIOAnimatedCoder.m @@ -301,12 +301,12 @@ } NSMutableData *imageData = [NSMutableData data]; - NSString *imageUTType = self.class.imageUTType; + CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format]; NSArray *frames = [SDImageCoderHelper framesFromAnimatedImage:image]; // Create an image destination. Animated Image does not support EXIF image orientation TODO // The `CGImageDestinationCreateWithData` will log a warning when count is 0, use 1 instead. - CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, (__bridge CFStringRef)imageUTType, frames.count ?: 1, NULL); + CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, frames.count ?: 1, NULL); if (!imageDestination) { // Handle failure. return nil; diff --git a/SDWebImage/Core/SDImageIOCoder.m b/SDWebImage/Core/SDImageIOCoder.m index 54666696..a6fa10af 100644 --- a/SDWebImage/Core/SDImageIOCoder.m +++ b/SDWebImage/Core/SDImageIOCoder.m @@ -11,6 +11,7 @@ #import "NSImage+Compatibility.h" #import #import "UIImage+Metadata.h" +#import "SDImageHEICCoderInternal.h" @implementation SDImageIOCoder { size_t _width, _height; @@ -54,10 +55,10 @@ return NO; case SDImageFormatHEIC: // Check HEIC decoding compatibility - return [[self class] canDecodeFromHEICFormat]; + return [SDImageHEICCoder canDecodeFromHEICFormat]; case SDImageFormatHEIF: // Check HEIF decoding compatibility - return [[self class] canDecodeFromHEIFFormat]; + return [SDImageHEICCoder canDecodeFromHEIFFormat]; default: return YES; } @@ -170,10 +171,10 @@ return NO; case SDImageFormatHEIC: // Check HEIC encoding compatibility - return [[self class] canEncodeToHEICFormat]; + return [SDImageHEICCoder canEncodeToHEICFormat]; case SDImageFormatHEIF: // Check HEIF encoding compatibility - return [[self class] canEncodeToHEIFFormat]; + return [SDImageHEICCoder canEncodeToHEIFFormat]; default: return YES; } @@ -230,65 +231,4 @@ return [imageData copy]; } -+ (BOOL)canDecodeFromFormat:(SDImageFormat)format { - CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format]; - NSArray *imageUTTypes = (__bridge_transfer NSArray *)CGImageSourceCopyTypeIdentifiers(); - if ([imageUTTypes containsObject:(__bridge NSString *)(imageUTType)]) { - return YES; - } - return NO; -} - -+ (BOOL)canDecodeFromHEICFormat { - static BOOL canDecode = NO; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - canDecode = [self canDecodeFromFormat:SDImageFormatHEIC]; - }); - return canDecode; -} - -+ (BOOL)canDecodeFromHEIFFormat { - static BOOL canDecode = NO; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - canDecode = [self canDecodeFromFormat:SDImageFormatHEIF]; - }); - return canDecode; -} - -+ (BOOL)canEncodeToFormat:(SDImageFormat)format { - NSMutableData *imageData = [NSMutableData data]; - CFStringRef imageUTType = [NSData sd_UTTypeFromImageFormat:format]; - - // Create an image destination. - CGImageDestinationRef imageDestination = CGImageDestinationCreateWithData((__bridge CFMutableDataRef)imageData, imageUTType, 1, NULL); - if (!imageDestination) { - // Can't encode to HEIC - return NO; - } else { - // Can encode to HEIC - CFRelease(imageDestination); - return YES; - } -} - -+ (BOOL)canEncodeToHEICFormat { - static BOOL canEncode = NO; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - canEncode = [self canEncodeToFormat:SDImageFormatHEIC]; - }); - return canEncode; -} - -+ (BOOL)canEncodeToHEIFFormat { - static BOOL canEncode = NO; - static dispatch_once_t onceToken; - dispatch_once(&onceToken, ^{ - canEncode = [self canEncodeToFormat:SDImageFormatHEIF]; - }); - return canEncode; -} - @end diff --git a/SDWebImage/Private/SDImageHEICCoderInternal.h b/SDWebImage/Private/SDImageHEICCoderInternal.h new file mode 100644 index 00000000..2e423906 --- /dev/null +++ b/SDWebImage/Private/SDImageHEICCoderInternal.h @@ -0,0 +1,19 @@ +/* +* This file is part of the SDWebImage package. +* (c) Olivier Poitrey +* +* For the full copyright and license information, please view the LICENSE +* file that was distributed with this source code. +*/ + +#import +#import "SDImageHEICCoder.h" + +@interface SDImageHEICCoder () + ++ (BOOL)canDecodeFromHEICFormat; ++ (BOOL)canDecodeFromHEIFFormat; ++ (BOOL)canEncodeToHEICFormat; ++ (BOOL)canEncodeToHEIFFormat; + +@end diff --git a/WebImage/SDWebImage.h b/WebImage/SDWebImage.h index b20fb152..7dfe22f2 100644 --- a/WebImage/SDWebImage.h +++ b/WebImage/SDWebImage.h @@ -68,6 +68,7 @@ FOUNDATION_EXPORT const unsigned char WebImageVersionString[]; #import #import #import +#import // Mac #if __has_include()