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:
Garrett Moon
2016-07-15 14:51:58 -07:00
committed by GitHub
parent 2cfc232d11
commit 855d457bcf
4 changed files with 36 additions and 24 deletions

View File

@@ -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;

View File

@@ -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

View File

@@ -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

View File

@@ -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)]];