SCGIFImageView.m 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. //
  2. // SCGIFImageView.m
  3. // TestGIF
  4. //
  5. // Created by shichangone on 11-7-12.
  6. // Copyright 2011 __MyCompanyName__. All rights reserved.
  7. //
  8. #import "SCGIFImageView.h"
  9. @implementation AnimatedGifFrame
  10. @synthesize data, delay, disposalMethod, area, header;
  11. - (void) dealloc
  12. {
  13. [data release];
  14. [header release];
  15. [super dealloc];
  16. }
  17. @end
  18. @implementation SCGIFImageView
  19. @synthesize GIF_frames;
  20. + (BOOL)isGifImage:(NSData*)imageData {
  21. const char* buf = (const char*)[imageData bytes];
  22. if (buf[0] == 0x47 && buf[1] == 0x49 && buf[2] == 0x46 && buf[3] == 0x38) {
  23. return YES;
  24. }
  25. return NO;
  26. }
  27. + (NSMutableArray*)getGifFrames:(NSData*)gifImageData {
  28. SCGIFImageView* gifImageView = [[SCGIFImageView alloc] initWithGIFData:gifImageData];
  29. if (!gifImageView) {
  30. return nil;
  31. }
  32. NSMutableArray* gifFrames = gifImageView.GIF_frames;
  33. [[gifFrames retain] autorelease];
  34. [gifImageView release];
  35. return gifFrames;
  36. }
  37. - (id)initWithGIFFile:(NSString*)gifFilePath {
  38. NSData* imageData = [NSData dataWithContentsOfFile:gifFilePath];
  39. return [self initWithGIFData:imageData];
  40. }
  41. - (id)initWithGIFData:(NSData*)gifImageData {
  42. if (gifImageData.length < 4) {
  43. return nil;
  44. }
  45. if (![SCGIFImageView isGifImage:gifImageData]) {
  46. UIImage* image = [UIImage imageWithData:gifImageData];
  47. return [super initWithImage:image];
  48. }
  49. [self decodeGIF:gifImageData];
  50. if (GIF_frames.count <= 0) {
  51. UIImage* image = [UIImage imageWithData:gifImageData];
  52. return [super initWithImage:image];
  53. }
  54. self = [super init];
  55. if (self) {
  56. [self loadImageData];
  57. }
  58. return self;
  59. }
  60. - (void)setGIF_frames:(NSMutableArray *)gifFrames {
  61. [gifFrames retain];
  62. if (GIF_frames) {
  63. [GIF_frames release];
  64. }
  65. GIF_frames = gifFrames;
  66. [self loadImageData];
  67. }
  68. - (void)loadImageData {
  69. // Add all subframes to the animation
  70. NSMutableArray *array = [[NSMutableArray alloc] init];
  71. for (NSUInteger i = 0; i < [GIF_frames count]; i++)
  72. {
  73. [array addObject: [self getFrameAsImageAtIndex:i]];
  74. }
  75. NSMutableArray *overlayArray = [[NSMutableArray alloc] init];
  76. UIImage *firstImage = [array objectAtIndex:0];
  77. CGSize size = firstImage.size;
  78. CGRect rect = CGRectZero;
  79. rect.size = size;
  80. UIGraphicsBeginImageContext(size);
  81. CGContextRef ctx = UIGraphicsGetCurrentContext();
  82. int i = 0;
  83. AnimatedGifFrame *lastFrame = nil;
  84. for (UIImage *image in array)
  85. {
  86. // Get Frame
  87. AnimatedGifFrame *frame = [GIF_frames objectAtIndex:i];
  88. // Initialize Flag
  89. UIImage *previousCanvas = nil;
  90. // Save Context
  91. CGContextSaveGState(ctx);
  92. // Change CTM
  93. CGContextScaleCTM(ctx, 1.0, -1.0);
  94. CGContextTranslateCTM(ctx, 0.0, -size.height);
  95. // Check if lastFrame exists
  96. CGRect clipRect;
  97. // Disposal Method (Operations before draw frame)
  98. switch (frame.disposalMethod)
  99. {
  100. case 1: // Do not dispose (draw over context)
  101. // Create Rect (y inverted) to clipping
  102. clipRect = CGRectMake(frame.area.origin.x, size.height - frame.area.size.height - frame.area.origin.y, frame.area.size.width, frame.area.size.height);
  103. // Clip Context
  104. CGContextClipToRect(ctx, clipRect);
  105. break;
  106. case 2: // Restore to background the rect when the actual frame will go to be drawed
  107. // Create Rect (y inverted) to clipping
  108. clipRect = CGRectMake(frame.area.origin.x, size.height - frame.area.size.height - frame.area.origin.y, frame.area.size.width, frame.area.size.height);
  109. // Clip Context
  110. CGContextClipToRect(ctx, clipRect);
  111. break;
  112. case 3: // Restore to Previous
  113. // Get Canvas
  114. previousCanvas = UIGraphicsGetImageFromCurrentImageContext();
  115. // Create Rect (y inverted) to clipping
  116. clipRect = CGRectMake(frame.area.origin.x, size.height - frame.area.size.height - frame.area.origin.y, frame.area.size.width, frame.area.size.height);
  117. // Clip Context
  118. CGContextClipToRect(ctx, clipRect);
  119. break;
  120. }
  121. // Draw Actual Frame
  122. CGContextDrawImage(ctx, rect, image.CGImage);
  123. // Restore State
  124. CGContextRestoreGState(ctx);
  125. //delay must larger than 0, the minimum delay in firefox is 10.
  126. if (frame.delay <= 0) {
  127. frame.delay = 10;
  128. }
  129. [overlayArray addObject:UIGraphicsGetImageFromCurrentImageContext()];
  130. // Set Last Frame
  131. lastFrame = frame;
  132. // Disposal Method (Operations afte draw frame)
  133. switch (frame.disposalMethod)
  134. {
  135. case 2: // Restore to background color the zone of the actual frame
  136. // Save Context
  137. CGContextSaveGState(ctx);
  138. // Change CTM
  139. CGContextScaleCTM(ctx, 1.0, -1.0);
  140. CGContextTranslateCTM(ctx, 0.0, -size.height);
  141. // Clear Context
  142. CGContextClearRect(ctx, clipRect);
  143. // Restore Context
  144. CGContextRestoreGState(ctx);
  145. break;
  146. case 3: // Restore to Previous Canvas
  147. // Save Context
  148. CGContextSaveGState(ctx);
  149. // Change CTM
  150. CGContextScaleCTM(ctx, 1.0, -1.0);
  151. CGContextTranslateCTM(ctx, 0.0, -size.height);
  152. // Clear Context
  153. CGContextClearRect(ctx, lastFrame.area);
  154. // Draw previous frame
  155. CGContextDrawImage(ctx, rect, previousCanvas.CGImage);
  156. // Restore State
  157. CGContextRestoreGState(ctx);
  158. break;
  159. }
  160. // Increment counter
  161. i++;
  162. }
  163. UIGraphicsEndImageContext();
  164. [self setImage:[overlayArray objectAtIndex:0]];
  165. [self setAnimationImages:overlayArray];
  166. [overlayArray release];
  167. [array release];
  168. // Count up the total delay, since Cocoa doesn't do per frame delays.
  169. double total = 0;
  170. for (AnimatedGifFrame *frame in GIF_frames) {
  171. total += frame.delay;
  172. }
  173. // GIFs store the delays as 1/100th of a second,
  174. // UIImageViews want it in seconds.
  175. [self setAnimationDuration:total/100];
  176. // Repeat infinite
  177. [self setAnimationRepeatCount:0];
  178. [self startAnimating];
  179. }
  180. - (void)dealloc {
  181. if (GIF_buffer != nil)
  182. {
  183. [GIF_buffer release];
  184. }
  185. if (GIF_screen != nil)
  186. {
  187. [GIF_screen release];
  188. }
  189. if (GIF_global != nil)
  190. {
  191. [GIF_global release];
  192. }
  193. [GIF_frames release];
  194. [super dealloc];
  195. }
  196. - (void) decodeGIF:(NSData *)GIFData {
  197. GIF_pointer = GIFData;
  198. if (GIF_buffer != nil)
  199. {
  200. [GIF_buffer release];
  201. }
  202. if (GIF_global != nil)
  203. {
  204. [GIF_global release];
  205. }
  206. if (GIF_screen != nil)
  207. {
  208. [GIF_screen release];
  209. }
  210. [GIF_frames release];
  211. GIF_buffer = [[NSMutableData alloc] init];
  212. GIF_global = [[NSMutableData alloc] init];
  213. GIF_screen = [[NSMutableData alloc] init];
  214. GIF_frames = [[NSMutableArray alloc] init];
  215. // Reset file counters to 0
  216. dataPointer = 0;
  217. [self GIFSkipBytes: 6]; // GIF89a, throw away
  218. [self GIFGetBytes: 7]; // Logical Screen Descriptor
  219. // Deep copy
  220. [GIF_screen setData: GIF_buffer];
  221. // Copy the read bytes into a local buffer on the stack
  222. // For easy byte access in the following lines.
  223. int length = [GIF_buffer length];
  224. unsigned char aBuffer[length];
  225. [GIF_buffer getBytes:aBuffer length:length];
  226. if (aBuffer[4] & 0x80) GIF_colorF = 1; else GIF_colorF = 0;
  227. if (aBuffer[4] & 0x08) GIF_sorted = 1; else GIF_sorted = 0;
  228. GIF_colorC = (aBuffer[4] & 0x07);
  229. GIF_colorS = 2 << GIF_colorC;
  230. if (GIF_colorF == 1)
  231. {
  232. [self GIFGetBytes: (3 * GIF_colorS)];
  233. // Deep copy
  234. [GIF_global setData:GIF_buffer];
  235. }
  236. unsigned char bBuffer[1];
  237. while ([self GIFGetBytes:1] == YES)
  238. {
  239. [GIF_buffer getBytes:bBuffer length:1];
  240. if (bBuffer[0] == 0x3B)
  241. { // This is the end
  242. break;
  243. }
  244. switch (bBuffer[0])
  245. {
  246. case 0x21:
  247. // Graphic Control Extension (#n of n)
  248. [self GIFReadExtensions];
  249. break;
  250. case 0x2C:
  251. // Image Descriptor (#n of n)
  252. [self GIFReadDescriptor];
  253. break;
  254. }
  255. }
  256. // clean up stuff
  257. [GIF_buffer release];
  258. GIF_buffer = nil;
  259. [GIF_screen release];
  260. GIF_screen = nil;
  261. [GIF_global release];
  262. GIF_global = nil;
  263. }
  264. - (void) GIFReadExtensions {
  265. // 21! But we still could have an Application Extension,
  266. // so we want to check for the full signature.
  267. unsigned char cur[1], prev[1];
  268. [self GIFGetBytes:1];
  269. [GIF_buffer getBytes:cur length:1];
  270. while (cur[0] != 0x00)
  271. {
  272. // TODO: Known bug, the sequence F9 04 could occur in the Application Extension, we
  273. // should check whether this combo follows directly after the 21.
  274. if (cur[0] == 0x04 && prev[0] == 0xF9)
  275. {
  276. [self GIFGetBytes:5];
  277. AnimatedGifFrame *frame = [[AnimatedGifFrame alloc] init];
  278. unsigned char buffer[5];
  279. [GIF_buffer getBytes:buffer length:5];
  280. frame.disposalMethod = (buffer[0] & 0x1c) >> 2;
  281. //NSLog(@"flags=%x, dm=%x", (int)(buffer[0]), frame.disposalMethod);
  282. // We save the delays for easy access.
  283. frame.delay = (buffer[1] | buffer[2] << 8);
  284. unsigned char board[8];
  285. board[0] = 0x21;
  286. board[1] = 0xF9;
  287. board[2] = 0x04;
  288. for(int i = 3, a = 0; a < 5; i++, a++)
  289. {
  290. board[i] = buffer[a];
  291. }
  292. frame.header = [NSData dataWithBytes:board length:8];
  293. [GIF_frames addObject:frame];
  294. [frame release];
  295. break;
  296. }
  297. prev[0] = cur[0];
  298. [self GIFGetBytes:1];
  299. [GIF_buffer getBytes:cur length:1];
  300. }
  301. }
  302. - (void) GIFReadDescriptor {
  303. [self GIFGetBytes:9];
  304. // Deep copy
  305. NSMutableData *GIF_screenTmp = [NSMutableData dataWithData:GIF_buffer];
  306. unsigned char aBuffer[9];
  307. [GIF_buffer getBytes:aBuffer length:9];
  308. CGRect rect;
  309. rect.origin.x = ((int)aBuffer[1] << 8) | aBuffer[0];
  310. rect.origin.y = ((int)aBuffer[3] << 8) | aBuffer[2];
  311. rect.size.width = ((int)aBuffer[5] << 8) | aBuffer[4];
  312. rect.size.height = ((int)aBuffer[7] << 8) | aBuffer[6];
  313. AnimatedGifFrame *frame = [GIF_frames lastObject];
  314. frame.area = rect;
  315. if (aBuffer[8] & 0x80) GIF_colorF = 1; else GIF_colorF = 0;
  316. unsigned char GIF_code = GIF_colorC, GIF_sort = GIF_sorted;
  317. if (GIF_colorF == 1)
  318. {
  319. GIF_code = (aBuffer[8] & 0x07);
  320. if (aBuffer[8] & 0x20)
  321. {
  322. GIF_sort = 1;
  323. }
  324. else
  325. {
  326. GIF_sort = 0;
  327. }
  328. }
  329. int GIF_size = (2 << GIF_code);
  330. size_t blength = [GIF_screen length];
  331. unsigned char bBuffer[blength];
  332. [GIF_screen getBytes:bBuffer length:blength];
  333. bBuffer[4] = (bBuffer[4] & 0x70);
  334. bBuffer[4] = (bBuffer[4] | 0x80);
  335. bBuffer[4] = (bBuffer[4] | GIF_code);
  336. if (GIF_sort)
  337. {
  338. bBuffer[4] |= 0x08;
  339. }
  340. NSMutableData *GIF_string = [NSMutableData dataWithData:[[NSString stringWithString:@"GIF89a"] dataUsingEncoding: NSUTF8StringEncoding]];
  341. [GIF_screen setData:[NSData dataWithBytes:bBuffer length:blength]];
  342. [GIF_string appendData: GIF_screen];
  343. if (GIF_colorF == 1)
  344. {
  345. [self GIFGetBytes:(3 * GIF_size)];
  346. [GIF_string appendData:GIF_buffer];
  347. }
  348. else
  349. {
  350. [GIF_string appendData:GIF_global];
  351. }
  352. // Add Graphic Control Extension Frame (for transparancy)
  353. [GIF_string appendData:frame.header];
  354. char endC = 0x2c;
  355. [GIF_string appendBytes:&endC length:sizeof(endC)];
  356. size_t clength = [GIF_screenTmp length];
  357. unsigned char cBuffer[clength];
  358. [GIF_screenTmp getBytes:cBuffer length:clength];
  359. cBuffer[8] &= 0x40;
  360. [GIF_screenTmp setData:[NSData dataWithBytes:cBuffer length:clength]];
  361. [GIF_string appendData: GIF_screenTmp];
  362. [self GIFGetBytes:1];
  363. [GIF_string appendData: GIF_buffer];
  364. while (true)
  365. {
  366. [self GIFGetBytes:1];
  367. [GIF_string appendData: GIF_buffer];
  368. unsigned char dBuffer[1];
  369. [GIF_buffer getBytes:dBuffer length:1];
  370. long u = (long) dBuffer[0];
  371. if (u != 0x00)
  372. {
  373. [self GIFGetBytes:u];
  374. [GIF_string appendData: GIF_buffer];
  375. }
  376. else
  377. {
  378. break;
  379. }
  380. }
  381. endC = 0x3b;
  382. [GIF_string appendBytes:&endC length:sizeof(endC)];
  383. // save the frame into the array of frames
  384. frame.data = GIF_string;
  385. }
  386. - (bool) GIFGetBytes:(int)length {
  387. if (GIF_buffer != nil)
  388. {
  389. [GIF_buffer release]; // Release old buffer
  390. GIF_buffer = nil;
  391. }
  392. if ((NSInteger)[GIF_pointer length] >= dataPointer + length) // Don't read across the edge of the file..
  393. {
  394. GIF_buffer = [[GIF_pointer subdataWithRange:NSMakeRange(dataPointer, length)] retain];
  395. dataPointer += length;
  396. return YES;
  397. }
  398. else
  399. {
  400. return NO;
  401. }
  402. }
  403. - (bool) GIFSkipBytes: (int) length {
  404. if ((NSInteger)[GIF_pointer length] >= dataPointer + length)
  405. {
  406. dataPointer += length;
  407. return YES;
  408. }
  409. else
  410. {
  411. return NO;
  412. }
  413. }
  414. - (NSData*) getFrameAsDataAtIndex:(int)index {
  415. if (index < (NSInteger)[GIF_frames count])
  416. {
  417. return ((AnimatedGifFrame *)[GIF_frames objectAtIndex:index]).data;
  418. }
  419. else
  420. {
  421. return nil;
  422. }
  423. }
  424. - (UIImage*) getFrameAsImageAtIndex:(int)index {
  425. NSData *frameData = [self getFrameAsDataAtIndex: index];
  426. UIImage *image = nil;
  427. if (frameData != nil)
  428. {
  429. image = [UIImage imageWithData:frameData];
  430. }
  431. return image;
  432. }
  433. @end