mirror of
https://github.com/zhigang1992/PINRemoteImage.git
synced 2026-03-27 22:53:53 +08:00
Support varying pixel lengths (#213)
* Add support for varying pixel lengths. On some devices the pixel length returned from CGImageCreate sadly isn't 4 bytes per pixel. Might be a good idea to support differing lengths. Encoding this into the format would be a good thing for sure. Anyway, that's what this does. * Add in methods for reading out bitsPerPixel too.
This commit is contained in:
@@ -50,7 +50,6 @@ typedef NS_ENUM(NSUInteger, PINAnimatedImageStatus) {
|
||||
PINAnimatedImageStatusError,
|
||||
};
|
||||
|
||||
extern const size_t kPINAnimatedImageComponentsPerPixel;
|
||||
extern const Float32 kPINAnimatedImageDefaultDuration;
|
||||
extern const Float32 kPINAnimatedImageMinimumDuration;
|
||||
extern const NSTimeInterval kPINAnimatedImageDisplayRefreshRate;
|
||||
|
||||
@@ -16,7 +16,6 @@ NSString *kPINAnimatedImageErrorDomain = @"kPINAnimatedImageErrorDomain";
|
||||
const Float32 kPINAnimatedImageDefaultDuration = 0.1;
|
||||
|
||||
static const size_t kPINAnimatedImageBitsPerComponent = 8;
|
||||
const size_t kPINAnimatedImageComponentsPerPixel = 4;
|
||||
|
||||
const NSTimeInterval kPINAnimatedImageDisplayRefreshRate = 60.0;
|
||||
//http://nullsleep.tumblr.com/post/16524517190/animated-gif-minimum-frame-delay-browser
|
||||
@@ -111,9 +110,9 @@ const Float32 kPINAnimatedImageMinimumDuration = 1 / kPINAnimatedImageDisplayRef
|
||||
}];
|
||||
}
|
||||
|
||||
- (PINImage *)coverImageWithMemoryMap:(NSData *)memoryMap width:(UInt32)width height:(UInt32)height bitmapInfo:(CGBitmapInfo)bitmapInfo
|
||||
- (PINImage *)coverImageWithMemoryMap:(NSData *)memoryMap width:(UInt32)width height:(UInt32)height bitsPerPixel:(UInt32)bitsPerPixel bitmapInfo:(CGBitmapInfo)bitmapInfo
|
||||
{
|
||||
CGImageRef imageRef = [[self class] imageAtIndex:0 inMemoryMap:memoryMap width:width height:height bitmapInfo:bitmapInfo];
|
||||
CGImageRef imageRef = [[self class] imageAtIndex:0 inMemoryMap:memoryMap width:width height:height bitsPerPixel:bitsPerPixel bitmapInfo:bitmapInfo];
|
||||
#if PIN_TARGET_IOS
|
||||
return [UIImage imageWithCGImage:imageRef];
|
||||
#elif PIN_TARGET_MAC
|
||||
@@ -128,7 +127,7 @@ void releaseData(void *data, const void *imageData, size_t size)
|
||||
CFRelease(data);
|
||||
}
|
||||
|
||||
- (CGImageRef)imageAtIndex:(NSUInteger)index inSharedImageFiles:(NSArray <PINSharedAnimatedImageFile *>*)imageFiles width:(UInt32)width height:(UInt32)height bitmapInfo:(CGBitmapInfo)bitmapInfo
|
||||
- (CGImageRef)imageAtIndex:(NSUInteger)index inSharedImageFiles:(NSArray <PINSharedAnimatedImageFile *>*)imageFiles width:(UInt32)width height:(UInt32)height bitsPerPixel:(UInt32)bitsPerPixel bitmapInfo:(CGBitmapInfo)bitmapInfo
|
||||
{
|
||||
if (self.status == PINAnimatedImageStatusError) {
|
||||
return nil;
|
||||
@@ -147,7 +146,7 @@ void releaseData(void *data, const void *imageData, size_t size)
|
||||
}];
|
||||
});
|
||||
}];
|
||||
return [[self class] imageAtIndex:index inMemoryMap:memoryMappedData width:width height:height bitmapInfo:bitmapInfo];
|
||||
return [[self class] imageAtIndex:index inMemoryMap:memoryMappedData width:width height:height bitsPerPixel:bitsPerPixel bitmapInfo:bitmapInfo];
|
||||
} else {
|
||||
index -= imageFile.frameCount;
|
||||
}
|
||||
@@ -161,7 +160,7 @@ void releaseData(void *data, const void *imageData, size_t size)
|
||||
return self.durations[index];
|
||||
}
|
||||
|
||||
+ (CGImageRef)imageAtIndex:(NSUInteger)index inMemoryMap:(NSData *)memoryMap width:(UInt32)width height:(UInt32)height bitmapInfo:(CGBitmapInfo)bitmapInfo
|
||||
+ (CGImageRef)imageAtIndex:(NSUInteger)index inMemoryMap:(NSData *)memoryMap width:(UInt32)width height:(UInt32)height bitsPerPixel:(UInt32)bitsPerPixel bitmapInfo:(CGBitmapInfo)bitmapInfo
|
||||
{
|
||||
if (memoryMap == nil) {
|
||||
return nil;
|
||||
@@ -169,7 +168,7 @@ void releaseData(void *data, const void *imageData, size_t size)
|
||||
|
||||
Float32 outDuration;
|
||||
|
||||
size_t imageLength = width * height * kPINAnimatedImageComponentsPerPixel;
|
||||
const size_t imageLength = width * height * bitsPerPixel / 8;
|
||||
|
||||
//frame duration + previous images
|
||||
NSUInteger offset = sizeof(UInt32) + (index * (imageLength + sizeof(outDuration)));
|
||||
@@ -183,13 +182,13 @@ void releaseData(void *data, const void *imageData, size_t size)
|
||||
|
||||
//retain the memory map, it will be released when releaseData is called
|
||||
CFRetain((CFDataRef)memoryMap);
|
||||
CGDataProviderRef dataProvider = CGDataProviderCreateWithData((void *)memoryMap, imageData, width * height * kPINAnimatedImageComponentsPerPixel, releaseData);
|
||||
CGDataProviderRef dataProvider = CGDataProviderCreateWithData((void *)memoryMap, imageData, imageLength, releaseData);
|
||||
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
|
||||
CGImageRef imageRef = CGImageCreate(width,
|
||||
height,
|
||||
kPINAnimatedImageBitsPerComponent,
|
||||
kPINAnimatedImageBitsPerComponent * kPINAnimatedImageComponentsPerPixel,
|
||||
kPINAnimatedImageComponentsPerPixel * width,
|
||||
bitsPerPixel,
|
||||
bitsPerPixel / 8 * width,
|
||||
colorSpace,
|
||||
bitmapInfo,
|
||||
dataProvider,
|
||||
@@ -221,17 +220,24 @@ void releaseData(void *data, const void *imageData, size_t size)
|
||||
return height;
|
||||
}
|
||||
|
||||
+ (UInt32)bitsPerPixelFromMemoryMap:(NSData *)memoryMap
|
||||
{
|
||||
UInt32 bitsPerPixel;
|
||||
[memoryMap getBytes:&bitsPerPixel range:NSMakeRange(10, sizeof(bitsPerPixel))];
|
||||
return bitsPerPixel;
|
||||
}
|
||||
|
||||
+ (UInt32)loopCountFromMemoryMap:(NSData *)memoryMap
|
||||
{
|
||||
UInt32 loopCount;
|
||||
[memoryMap getBytes:&loopCount range:NSMakeRange(10, sizeof(loopCount))];
|
||||
[memoryMap getBytes:&loopCount range:NSMakeRange(14, sizeof(loopCount))];
|
||||
return loopCount;
|
||||
}
|
||||
|
||||
+ (UInt32)frameCountFromMemoryMap:(NSData *)memoryMap
|
||||
{
|
||||
UInt32 frameCount;
|
||||
[memoryMap getBytes:&frameCount range:NSMakeRange(14, sizeof(frameCount))];
|
||||
[memoryMap getBytes:&frameCount range:NSMakeRange(18, sizeof(frameCount))];
|
||||
return frameCount;
|
||||
}
|
||||
|
||||
@@ -239,7 +245,7 @@ void releaseData(void *data, const void *imageData, size_t size)
|
||||
+ (Float32 *)createDurations:(Float32 *)durations fromMemoryMap:(NSData *)memoryMap frameCount:(UInt32)frameCount frameSize:(NSUInteger)frameSize totalDuration:(nonnull CFTimeInterval *)totalDuration
|
||||
{
|
||||
*totalDuration = 0;
|
||||
[memoryMap getBytes:&durations range:NSMakeRange(18, sizeof(Float32) * frameCount)];
|
||||
[memoryMap getBytes:&durations range:NSMakeRange(22, sizeof(Float32) * frameCount)];
|
||||
|
||||
for (NSUInteger idx = 0; idx < frameCount; idx++) {
|
||||
*totalDuration += durations[idx];
|
||||
@@ -303,6 +309,7 @@ void releaseData(void *data, const void *imageData, size_t size)
|
||||
inSharedImageFiles:self.sharedAnimatedImage.maps
|
||||
width:(UInt32)self.sharedAnimatedImage.width
|
||||
height:(UInt32)self.sharedAnimatedImage.height
|
||||
bitsPerPixel:(UInt32)self.sharedAnimatedImage.bitsPerPixel
|
||||
bitmapInfo:self.sharedAnimatedImage.bitmapInfo];
|
||||
}
|
||||
|
||||
@@ -392,7 +399,7 @@ static NSUInteger gcd(NSUInteger a, NSUInteger b)
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setInfoProcessedWithCoverImage:(PINImage *)coverImage UUID:(NSUUID *)UUID durations:(Float32 *)durations totalDuration:(CFTimeInterval)totalDuration loopCount:(size_t)loopCount frameCount:(size_t)frameCount width:(size_t)width height:(size_t)height bitmapInfo:(CGBitmapInfo)bitmapInfo
|
||||
- (void)setInfoProcessedWithCoverImage:(PINImage *)coverImage UUID:(NSUUID *)UUID durations:(Float32 *)durations totalDuration:(CFTimeInterval)totalDuration loopCount:(size_t)loopCount frameCount:(size_t)frameCount width:(size_t)width height:(size_t)height bitsPerPixel:(size_t)bitsPerPixel bitmapInfo:(CGBitmapInfo)bitmapInfo
|
||||
{
|
||||
NSAssert(_status == PINAnimatedImageStatusUnprocessed, @"Status should be unprocessed.");
|
||||
[_coverImageLock lockWithBlock:^{
|
||||
@@ -406,6 +413,7 @@ static NSUInteger gcd(NSUInteger a, NSUInteger b)
|
||||
_frameCount = frameCount;
|
||||
_width = width;
|
||||
_height = height;
|
||||
_bitsPerPixel = bitsPerPixel;
|
||||
_bitmapInfo = bitmapInfo;
|
||||
_status = PINAnimatedImageStatusInfoProcessed;
|
||||
}
|
||||
@@ -438,7 +446,7 @@ static NSUInteger gcd(NSUInteger a, NSUInteger b)
|
||||
__block PINImage *coverImage = nil;
|
||||
[_coverImageLock lockWithBlock:^{
|
||||
if (_coverImage == nil) {
|
||||
CGImageRef imageRef = [PINAnimatedImage imageAtIndex:0 inMemoryMap:self.maps[0].memoryMappedData width:(UInt32)self.width height:(UInt32)self.height bitmapInfo:self.bitmapInfo];
|
||||
CGImageRef imageRef = [PINAnimatedImage imageAtIndex:0 inMemoryMap:self.maps[0].memoryMappedData width:(UInt32)self.width height:(UInt32)self.height bitsPerPixel:(UInt32)self.bitsPerPixel bitmapInfo:self.bitmapInfo];
|
||||
#if PIN_TARGET_IOS
|
||||
coverImage = [UIImage imageWithCGImage:imageRef];
|
||||
#elif PIN_TARGET_MAC
|
||||
|
||||
@@ -53,6 +53,7 @@ typedef void(^PINAnimatedImageDecodedPath)(BOOL finished, NSString *path, NSErro
|
||||
frameCount:(size_t)frameCount
|
||||
width:(size_t)width
|
||||
height:(size_t)height
|
||||
bitsPerPixel:(size_t)bitsPerPixel
|
||||
bitmapInfo:(CGBitmapInfo)bitmapInfo;
|
||||
|
||||
@property (nonatomic, readonly) NSUUID *UUID;
|
||||
@@ -62,6 +63,7 @@ typedef void(^PINAnimatedImageDecodedPath)(BOOL finished, NSString *path, NSErro
|
||||
@property (nonatomic, readonly) size_t frameCount;
|
||||
@property (nonatomic, readonly) size_t width;
|
||||
@property (nonatomic, readonly) size_t height;
|
||||
@property (nonatomic, readonly) size_t bitsPerPixel;
|
||||
@property (nonatomic, readonly) CGBitmapInfo bitmapInfo;
|
||||
|
||||
@end
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
static const NSUInteger maxFileSize = 50000000; //max file size in bytes
|
||||
static const Float32 maxFileDuration = 1; //max duration of a file in seconds
|
||||
|
||||
typedef void(^PINAnimatedImageInfoProcessed)(PINImage *coverImage, NSUUID *UUID, Float32 *durations, CFTimeInterval totalDuration, size_t loopCount, size_t frameCount, size_t width, size_t height, UInt32 bitmapInfo);
|
||||
typedef void(^PINAnimatedImageInfoProcessed)(PINImage *coverImage, NSUUID *UUID, Float32 *durations, CFTimeInterval totalDuration, size_t loopCount, size_t frameCount, size_t width, size_t height, size_t bitsPerPixel, UInt32 bitmapInfo);
|
||||
|
||||
BOOL PINStatusCoverImageCompleted(PINAnimatedImageStatus status);
|
||||
BOOL PINStatusCoverImageCompleted(PINAnimatedImageStatus status) {
|
||||
@@ -166,12 +166,12 @@ static dispatch_once_t startupCleanupOnce;
|
||||
|
||||
if (startProcessing) {
|
||||
dispatch_async(self.serialProcessingQueue, ^{
|
||||
[[self class] processAnimatedImage:animatedImageData temporaryDirectory:[PINAnimatedImageManager temporaryDirectory] infoCompletion:^(PINImage *coverImage, NSUUID *UUID, Float32 *durations, CFTimeInterval totalDuration, size_t loopCount, size_t frameCount, size_t width, size_t height, UInt32 bitmapInfo) {
|
||||
[[self class] processAnimatedImage:animatedImageData temporaryDirectory:[PINAnimatedImageManager temporaryDirectory] infoCompletion:^(PINImage *coverImage, NSUUID *UUID, Float32 *durations, CFTimeInterval totalDuration, size_t loopCount, size_t frameCount, size_t width, size_t height, size_t bitsPerPixel, UInt32 bitmapInfo) {
|
||||
__block NSArray *infoCompletions = nil;
|
||||
__block PINSharedAnimatedImage *sharedAnimatedImage = nil;
|
||||
[_lock lockWithBlock:^{
|
||||
sharedAnimatedImage = [self.animatedImages objectForKey:animatedImageData];
|
||||
[sharedAnimatedImage setInfoProcessedWithCoverImage:coverImage UUID:UUID durations:durations totalDuration:totalDuration loopCount:loopCount frameCount:frameCount width:width height:height bitmapInfo:bitmapInfo];
|
||||
[sharedAnimatedImage setInfoProcessedWithCoverImage:coverImage UUID:UUID durations:durations totalDuration:totalDuration loopCount:loopCount frameCount:frameCount width:width height:height bitsPerPixel:bitsPerPixel bitmapInfo:bitmapInfo];
|
||||
infoCompletions = sharedAnimatedImage.infoCompletions;
|
||||
sharedAnimatedImage.infoCompletions = @[];
|
||||
}];
|
||||
@@ -252,6 +252,7 @@ ERROR;}) \
|
||||
HANDLE_PROCESSING_ERROR(fileHandleError);
|
||||
UInt32 width;
|
||||
UInt32 height;
|
||||
UInt32 bitsPerPixel;
|
||||
UInt32 bitmapInfo;
|
||||
NSUInteger fileCount = 0;
|
||||
UInt32 frameCountForFile = 0;
|
||||
@@ -295,6 +296,7 @@ ERROR;}) \
|
||||
|
||||
width = (UInt32)CGImageGetWidth(frameImage);
|
||||
height = (UInt32)CGImageGetHeight(frameImage);
|
||||
bitsPerPixel = (UInt32)CGImageGetBitsPerPixel(frameImage);
|
||||
|
||||
#if PIN_TARGET_IOS
|
||||
coverImage = [UIImage imageWithCGImage:frameImage];
|
||||
@@ -312,13 +314,13 @@ ERROR;}) \
|
||||
if (PROCESSING_ERROR == nil) {
|
||||
//Get size, write file header get coverImage
|
||||
dispatch_group_async(diskGroup, diskWriteQueue, ^{
|
||||
NSError *fileHeaderError = [self writeFileHeader:fileHandle width:width height:height loopCount:loopCount frameCount:frameCount bitmapInfo:bitmapInfo durations:durations];
|
||||
NSError *fileHeaderError = [self writeFileHeader:fileHandle width:width height:height bitsPerPixel:bitsPerPixel loopCount:loopCount frameCount:frameCount bitmapInfo:bitmapInfo durations:durations];
|
||||
HANDLE_PROCESSING_ERROR(fileHeaderError);
|
||||
if (fileHeaderError == nil) {
|
||||
[fileHandle closeFile];
|
||||
|
||||
PINLog(@"notifying info");
|
||||
infoCompletion(coverImage, UUID, durations, totalDuration, loopCount, frameCount, width, height, bitmapInfo);
|
||||
infoCompletion(coverImage, UUID, durations, totalDuration, loopCount, frameCount, width, height, bitsPerPixel, bitmapInfo);
|
||||
}
|
||||
});
|
||||
fileCount = 1;
|
||||
@@ -396,7 +398,7 @@ ERROR;}) \
|
||||
}
|
||||
|
||||
NSData *frameData = (__bridge_transfer NSData *)CGDataProviderCopyData(CGImageGetDataProvider(frameImage));
|
||||
NSAssert(frameData.length == width * height * kPINAnimatedImageComponentsPerPixel, @"data should be width * height * 4 bytes");
|
||||
NSAssert(frameData.length == width * height * bitsPerPixel / 8, @"data should be width * height * bytes per pixel");
|
||||
NSError *frameWriteError = [self writeFrameToFile:fileHandle duration:duration frameData:frameData];
|
||||
HANDLE_PROCESSING_ERROR(frameWriteError);
|
||||
|
||||
@@ -517,14 +519,15 @@ ERROR;}) \
|
||||
|
||||
*/
|
||||
|
||||
+ (NSError *)writeFileHeader:(NSFileHandle *)fileHandle width:(UInt32)width height:(UInt32)height loopCount:(UInt32)loopCount frameCount:(UInt32)frameCount bitmapInfo:(UInt32)bitmapInfo durations:(Float32*)durations
|
||||
+ (NSError *)writeFileHeader:(NSFileHandle *)fileHandle width:(UInt32)width height:(UInt32)height bitsPerPixel:(UInt32)bitsPerPixel loopCount:(UInt32)loopCount frameCount:(UInt32)frameCount bitmapInfo:(UInt32)bitmapInfo durations:(Float32*)durations
|
||||
{
|
||||
NSError *error = nil;
|
||||
@try {
|
||||
UInt16 version = 1;
|
||||
UInt16 version = 2;
|
||||
[fileHandle writeData:[NSData dataWithBytes:&version length:sizeof(version)]];
|
||||
[fileHandle writeData:[NSData dataWithBytes:&width length:sizeof(width)]];
|
||||
[fileHandle writeData:[NSData dataWithBytes:&height length:sizeof(height)]];
|
||||
[fileHandle writeData:[NSData dataWithBytes:&bitsPerPixel length:sizeof(bitsPerPixel)]];
|
||||
[fileHandle writeData:[NSData dataWithBytes:&loopCount length:sizeof(loopCount)]];
|
||||
[fileHandle writeData:[NSData dataWithBytes:&frameCount length:sizeof(frameCount)]];
|
||||
[fileHandle writeData:[NSData dataWithBytes:&bitmapInfo length:sizeof(bitmapInfo)]];
|
||||
|
||||
Reference in New Issue
Block a user