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