UniGifFormatter.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. /*
  2. UniGif
  3. Copyright (c) 2015 WestHillApps (Hironari Nishioka)
  4. This software is released under the MIT License.
  5. http://opensource.org/licenses/mit-license.php
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using UnityEngine;
  10. public static partial class UniGif
  11. {
  12. /// <summary>
  13. /// Set GIF data
  14. /// </summary>
  15. /// <param name="gifBytes">GIF byte data</param>
  16. /// <param name="gifData">ref GIF data</param>
  17. /// <param name="debugLog">Debug log flag</param>
  18. /// <returns>Result</returns>
  19. private static bool SetGifData(byte[] gifBytes, ref GifData gifData, bool debugLog)
  20. {
  21. if (debugLog)
  22. {
  23. Debug.Log("SetGifData Start.");
  24. }
  25. if (gifBytes == null || gifBytes.Length <= 0)
  26. {
  27. Debug.LogError("bytes is nothing.");
  28. return false;
  29. }
  30. int byteIndex = 0;
  31. if (SetGifHeader(gifBytes, ref byteIndex, ref gifData) == false)
  32. {
  33. Debug.LogError("GIF header set error.");
  34. return false;
  35. }
  36. if (SetGifBlock(gifBytes, ref byteIndex, ref gifData) == false)
  37. {
  38. Debug.LogError("GIF block set error.");
  39. return false;
  40. }
  41. if (debugLog)
  42. {
  43. gifData.Dump();
  44. Debug.Log("SetGifData Finish.");
  45. }
  46. return true;
  47. }
  48. private static bool SetGifHeader(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
  49. {
  50. // Signature(3 Bytes)
  51. // 0x47 0x49 0x46 (GIF)
  52. if (gifBytes[0] != 'G' || gifBytes[1] != 'I' || gifBytes[2] != 'F')
  53. {
  54. Debug.LogError("This is not GIF image.");
  55. return false;
  56. }
  57. gifData.m_sig0 = gifBytes[0];
  58. gifData.m_sig1 = gifBytes[1];
  59. gifData.m_sig2 = gifBytes[2];
  60. // Version(3 Bytes)
  61. // 0x38 0x37 0x61 (87a) or 0x38 0x39 0x61 (89a)
  62. if ((gifBytes[3] != '8' || gifBytes[4] != '7' || gifBytes[5] != 'a') &&
  63. (gifBytes[3] != '8' || gifBytes[4] != '9' || gifBytes[5] != 'a'))
  64. {
  65. Debug.LogError("GIF version error.\nSupported only GIF87a or GIF89a.");
  66. return false;
  67. }
  68. gifData.m_ver0 = gifBytes[3];
  69. gifData.m_ver1 = gifBytes[4];
  70. gifData.m_ver2 = gifBytes[5];
  71. // Logical Screen Width(2 Bytes)
  72. gifData.m_logicalScreenWidth = BitConverter.ToUInt16(gifBytes, 6);
  73. // Logical Screen Height(2 Bytes)
  74. gifData.m_logicalScreenHeight = BitConverter.ToUInt16(gifBytes, 8);
  75. // 1 Byte
  76. {
  77. // Global Color Table Flag(1 Bit)
  78. gifData.m_globalColorTableFlag = (gifBytes[10] & 128) == 128; // 0b10000000
  79. // Color Resolution(3 Bits)
  80. switch (gifBytes[10] & 112)
  81. {
  82. case 112: // 0b01110000
  83. gifData.m_colorResolution = 8;
  84. break;
  85. case 96: // 0b01100000
  86. gifData.m_colorResolution = 7;
  87. break;
  88. case 80: // 0b01010000
  89. gifData.m_colorResolution = 6;
  90. break;
  91. case 64: // 0b01000000
  92. gifData.m_colorResolution = 5;
  93. break;
  94. case 48: // 0b00110000
  95. gifData.m_colorResolution = 4;
  96. break;
  97. case 32: // 0b00100000
  98. gifData.m_colorResolution = 3;
  99. break;
  100. case 16: // 0b00010000
  101. gifData.m_colorResolution = 2;
  102. break;
  103. default:
  104. gifData.m_colorResolution = 1;
  105. break;
  106. }
  107. // Sort Flag(1 Bit)
  108. gifData.m_sortFlag = (gifBytes[10] & 8) == 8; // 0b00001000
  109. // Size of Global Color Table(3 Bits)
  110. int val = (gifBytes[10] & 7) + 1;
  111. gifData.m_sizeOfGlobalColorTable = (int)Math.Pow(2, val);
  112. }
  113. // Background Color Index(1 Byte)
  114. gifData.m_bgColorIndex = gifBytes[11];
  115. // Pixel Aspect Ratio(1 Byte)
  116. gifData.m_pixelAspectRatio = gifBytes[12];
  117. byteIndex = 13;
  118. if (gifData.m_globalColorTableFlag)
  119. {
  120. // Global Color Table(0~255×3 Bytes)
  121. gifData.m_globalColorTable = new List<byte[]>();
  122. for (int i = byteIndex; i < byteIndex + (gifData.m_sizeOfGlobalColorTable * 3); i += 3)
  123. {
  124. gifData.m_globalColorTable.Add(new byte[] { gifBytes[i], gifBytes[i + 1], gifBytes[i + 2] });
  125. }
  126. byteIndex = byteIndex + (gifData.m_sizeOfGlobalColorTable * 3);
  127. }
  128. return true;
  129. }
  130. private static bool SetGifBlock(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
  131. {
  132. try
  133. {
  134. int lastIndex = 0;
  135. while (true)
  136. {
  137. int nowIndex = byteIndex;
  138. if (gifBytes[nowIndex] == 0x2c)
  139. {
  140. // Image Block(0x2c)
  141. SetImageBlock(gifBytes, ref byteIndex, ref gifData);
  142. }
  143. else if (gifBytes[nowIndex] == 0x21)
  144. {
  145. // Extension
  146. switch (gifBytes[nowIndex + 1])
  147. {
  148. case 0xf9:
  149. // Graphic Control Extension(0x21 0xf9)
  150. SetGraphicControlExtension(gifBytes, ref byteIndex, ref gifData);
  151. break;
  152. case 0xfe:
  153. // Comment Extension(0x21 0xfe)
  154. SetCommentExtension(gifBytes, ref byteIndex, ref gifData);
  155. break;
  156. case 0x01:
  157. // Plain Text Extension(0x21 0x01)
  158. SetPlainTextExtension(gifBytes, ref byteIndex, ref gifData);
  159. break;
  160. case 0xff:
  161. // Application Extension(0x21 0xff)
  162. SetApplicationExtension(gifBytes, ref byteIndex, ref gifData);
  163. break;
  164. default:
  165. break;
  166. }
  167. }
  168. else if (gifBytes[nowIndex] == 0x3b)
  169. {
  170. // Trailer(1 Byte)
  171. gifData.m_trailer = gifBytes[byteIndex];
  172. byteIndex++;
  173. break;
  174. }
  175. if (lastIndex == nowIndex)
  176. {
  177. Debug.LogError("Infinite loop error.");
  178. return false;
  179. }
  180. lastIndex = nowIndex;
  181. }
  182. }
  183. catch (Exception ex)
  184. {
  185. Debug.LogError(ex.Message);
  186. return false;
  187. }
  188. return true;
  189. }
  190. private static void SetImageBlock(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
  191. {
  192. ImageBlock ib = new ImageBlock();
  193. // Image Separator(1 Byte)
  194. // 0x2c
  195. ib.m_imageSeparator = gifBytes[byteIndex];
  196. byteIndex++;
  197. // Image Left Position(2 Bytes)
  198. ib.m_imageLeftPosition = BitConverter.ToUInt16(gifBytes, byteIndex);
  199. byteIndex += 2;
  200. // Image Top Position(2 Bytes)
  201. ib.m_imageTopPosition = BitConverter.ToUInt16(gifBytes, byteIndex);
  202. byteIndex += 2;
  203. // Image Width(2 Bytes)
  204. ib.m_imageWidth = BitConverter.ToUInt16(gifBytes, byteIndex);
  205. byteIndex += 2;
  206. // Image Height(2 Bytes)
  207. ib.m_imageHeight = BitConverter.ToUInt16(gifBytes, byteIndex);
  208. byteIndex += 2;
  209. // 1 Byte
  210. {
  211. // Local Color Table Flag(1 Bit)
  212. ib.m_localColorTableFlag = (gifBytes[byteIndex] & 128) == 128; // 0b10000000
  213. // Interlace Flag(1 Bit)
  214. ib.m_interlaceFlag = (gifBytes[byteIndex] & 64) == 64; // 0b01000000
  215. // Sort Flag(1 Bit)
  216. ib.m_sortFlag = (gifBytes[byteIndex] & 32) == 32; // 0b00100000
  217. // Reserved(2 Bits)
  218. // Unused
  219. // Size of Local Color Table(3 Bits)
  220. int val = (gifBytes[byteIndex] & 7) + 1;
  221. ib.m_sizeOfLocalColorTable = (int)Math.Pow(2, val);
  222. byteIndex++;
  223. }
  224. if (ib.m_localColorTableFlag)
  225. {
  226. // Local Color Table(0~255×3 Bytes)
  227. ib.m_localColorTable = new List<byte[]>();
  228. for (int i = byteIndex; i < byteIndex + (ib.m_sizeOfLocalColorTable * 3); i += 3)
  229. {
  230. ib.m_localColorTable.Add(new byte[] { gifBytes[i], gifBytes[i + 1], gifBytes[i + 2] });
  231. }
  232. byteIndex = byteIndex + (ib.m_sizeOfLocalColorTable * 3);
  233. }
  234. // LZW Minimum Code Size(1 Byte)
  235. ib.m_lzwMinimumCodeSize = gifBytes[byteIndex];
  236. byteIndex++;
  237. // Block Size & Image Data List
  238. while (true)
  239. {
  240. // Block Size(1 Byte)
  241. byte blockSize = gifBytes[byteIndex];
  242. byteIndex++;
  243. if (blockSize == 0x00)
  244. {
  245. // Block Terminator(1 Byte)
  246. break;
  247. }
  248. var imageDataBlock = new ImageBlock.ImageDataBlock();
  249. imageDataBlock.m_blockSize = blockSize;
  250. // Image Data(? Bytes)
  251. imageDataBlock.m_imageData = new byte[imageDataBlock.m_blockSize];
  252. for (int i = 0; i < imageDataBlock.m_imageData.Length; i++)
  253. {
  254. imageDataBlock.m_imageData[i] = gifBytes[byteIndex];
  255. byteIndex++;
  256. }
  257. if (ib.m_imageDataList == null)
  258. {
  259. ib.m_imageDataList = new List<ImageBlock.ImageDataBlock>();
  260. }
  261. ib.m_imageDataList.Add(imageDataBlock);
  262. }
  263. if (gifData.m_imageBlockList == null)
  264. {
  265. gifData.m_imageBlockList = new List<ImageBlock>();
  266. }
  267. gifData.m_imageBlockList.Add(ib);
  268. }
  269. private static void SetGraphicControlExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
  270. {
  271. GraphicControlExtension gcEx = new GraphicControlExtension();
  272. // Extension Introducer(1 Byte)
  273. // 0x21
  274. gcEx.m_extensionIntroducer = gifBytes[byteIndex];
  275. byteIndex++;
  276. // Graphic Control Label(1 Byte)
  277. // 0xf9
  278. gcEx.m_graphicControlLabel = gifBytes[byteIndex];
  279. byteIndex++;
  280. // Block Size(1 Byte)
  281. // 0x04
  282. gcEx.m_blockSize = gifBytes[byteIndex];
  283. byteIndex++;
  284. // 1 Byte
  285. {
  286. // Reserved(3 Bits)
  287. // Unused
  288. // Disposal Mothod(3 Bits)
  289. // 0 (No disposal specified)
  290. // 1 (Do not dispose)
  291. // 2 (Restore to background color)
  292. // 3 (Restore to previous)
  293. switch (gifBytes[byteIndex] & 28)
  294. { // 0b00011100
  295. case 4: // 0b00000100
  296. gcEx.m_disposalMethod = 1;
  297. break;
  298. case 8: // 0b00001000
  299. gcEx.m_disposalMethod = 2;
  300. break;
  301. case 12: // 0b00001100
  302. gcEx.m_disposalMethod = 3;
  303. break;
  304. default:
  305. gcEx.m_disposalMethod = 0;
  306. break;
  307. }
  308. // User Input Flag(1 Bit)
  309. // Unknown
  310. // Transparent Color Flag(1 Bit)
  311. gcEx.m_transparentColorFlag = (gifBytes[byteIndex] & 1) == 1; // 0b00000001
  312. byteIndex++;
  313. }
  314. // Delay Time(2 Bytes)
  315. gcEx.m_delayTime = BitConverter.ToUInt16(gifBytes, byteIndex);
  316. byteIndex += 2;
  317. // Transparent Color Index(1 Byte)
  318. gcEx.m_transparentColorIndex = gifBytes[byteIndex];
  319. byteIndex++;
  320. // Block Terminator(1 Byte)
  321. gcEx.m_blockTerminator = gifBytes[byteIndex];
  322. byteIndex++;
  323. if (gifData.m_graphicCtrlExList == null)
  324. {
  325. gifData.m_graphicCtrlExList = new List<GraphicControlExtension>();
  326. }
  327. gifData.m_graphicCtrlExList.Add(gcEx);
  328. }
  329. private static void SetCommentExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
  330. {
  331. CommentExtension commentEx = new CommentExtension();
  332. // Extension Introducer(1 Byte)
  333. // 0x21
  334. commentEx.m_extensionIntroducer = gifBytes[byteIndex];
  335. byteIndex++;
  336. // Comment Label(1 Byte)
  337. // 0xfe
  338. commentEx.m_commentLabel = gifBytes[byteIndex];
  339. byteIndex++;
  340. // Block Size & Comment Data List
  341. while (true)
  342. {
  343. // Block Size(1 Byte)
  344. byte blockSize = gifBytes[byteIndex];
  345. byteIndex++;
  346. if (blockSize == 0x00)
  347. {
  348. // Block Terminator(1 Byte)
  349. break;
  350. }
  351. var commentDataBlock = new CommentExtension.CommentDataBlock();
  352. commentDataBlock.m_blockSize = blockSize;
  353. // Comment Data(n Byte)
  354. commentDataBlock.m_commentData = new byte[commentDataBlock.m_blockSize];
  355. for (int i = 0; i < commentDataBlock.m_commentData.Length; i++)
  356. {
  357. commentDataBlock.m_commentData[i] = gifBytes[byteIndex];
  358. byteIndex++;
  359. }
  360. if (commentEx.m_commentDataList == null)
  361. {
  362. commentEx.m_commentDataList = new List<CommentExtension.CommentDataBlock>();
  363. }
  364. commentEx.m_commentDataList.Add(commentDataBlock);
  365. }
  366. if (gifData.m_commentExList == null)
  367. {
  368. gifData.m_commentExList = new List<CommentExtension>();
  369. }
  370. gifData.m_commentExList.Add(commentEx);
  371. }
  372. private static void SetPlainTextExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
  373. {
  374. PlainTextExtension plainTxtEx = new PlainTextExtension();
  375. // Extension Introducer(1 Byte)
  376. // 0x21
  377. plainTxtEx.m_extensionIntroducer = gifBytes[byteIndex];
  378. byteIndex++;
  379. // Plain Text Label(1 Byte)
  380. // 0x01
  381. plainTxtEx.m_plainTextLabel = gifBytes[byteIndex];
  382. byteIndex++;
  383. // Block Size(1 Byte)
  384. // 0x0c
  385. plainTxtEx.m_blockSize = gifBytes[byteIndex];
  386. byteIndex++;
  387. // Text Grid Left Position(2 Bytes)
  388. // Not supported
  389. byteIndex += 2;
  390. // Text Grid Top Position(2 Bytes)
  391. // Not supported
  392. byteIndex += 2;
  393. // Text Grid Width(2 Bytes)
  394. // Not supported
  395. byteIndex += 2;
  396. // Text Grid Height(2 Bytes)
  397. // Not supported
  398. byteIndex += 2;
  399. // Character Cell Width(1 Bytes)
  400. // Not supported
  401. byteIndex++;
  402. // Character Cell Height(1 Bytes)
  403. // Not supported
  404. byteIndex++;
  405. // Text Foreground Color Index(1 Bytes)
  406. // Not supported
  407. byteIndex++;
  408. // Text Background Color Index(1 Bytes)
  409. // Not supported
  410. byteIndex++;
  411. // Block Size & Plain Text Data List
  412. while (true)
  413. {
  414. // Block Size(1 Byte)
  415. byte blockSize = gifBytes[byteIndex];
  416. byteIndex++;
  417. if (blockSize == 0x00)
  418. {
  419. // Block Terminator(1 Byte)
  420. break;
  421. }
  422. var plainTextDataBlock = new PlainTextExtension.PlainTextDataBlock();
  423. plainTextDataBlock.m_blockSize = blockSize;
  424. // Plain Text Data(n Byte)
  425. plainTextDataBlock.m_plainTextData = new byte[plainTextDataBlock.m_blockSize];
  426. for (int i = 0; i < plainTextDataBlock.m_plainTextData.Length; i++)
  427. {
  428. plainTextDataBlock.m_plainTextData[i] = gifBytes[byteIndex];
  429. byteIndex++;
  430. }
  431. if (plainTxtEx.m_plainTextDataList == null)
  432. {
  433. plainTxtEx.m_plainTextDataList = new List<PlainTextExtension.PlainTextDataBlock>();
  434. }
  435. plainTxtEx.m_plainTextDataList.Add(plainTextDataBlock);
  436. }
  437. if (gifData.m_plainTextExList == null)
  438. {
  439. gifData.m_plainTextExList = new List<PlainTextExtension>();
  440. }
  441. gifData.m_plainTextExList.Add(plainTxtEx);
  442. }
  443. private static void SetApplicationExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
  444. {
  445. // Extension Introducer(1 Byte)
  446. // 0x21
  447. gifData.m_appEx.m_extensionIntroducer = gifBytes[byteIndex];
  448. byteIndex++;
  449. // Extension Label(1 Byte)
  450. // 0xff
  451. gifData.m_appEx.m_extensionLabel = gifBytes[byteIndex];
  452. byteIndex++;
  453. // Block Size(1 Byte)
  454. // 0x0b
  455. gifData.m_appEx.m_blockSize = gifBytes[byteIndex];
  456. byteIndex++;
  457. // Application Identifier(8 Bytes)
  458. gifData.m_appEx.m_appId1 = gifBytes[byteIndex];
  459. byteIndex++;
  460. gifData.m_appEx.m_appId2 = gifBytes[byteIndex];
  461. byteIndex++;
  462. gifData.m_appEx.m_appId3 = gifBytes[byteIndex];
  463. byteIndex++;
  464. gifData.m_appEx.m_appId4 = gifBytes[byteIndex];
  465. byteIndex++;
  466. gifData.m_appEx.m_appId5 = gifBytes[byteIndex];
  467. byteIndex++;
  468. gifData.m_appEx.m_appId6 = gifBytes[byteIndex];
  469. byteIndex++;
  470. gifData.m_appEx.m_appId7 = gifBytes[byteIndex];
  471. byteIndex++;
  472. gifData.m_appEx.m_appId8 = gifBytes[byteIndex];
  473. byteIndex++;
  474. // Application Authentication Code(3 Bytes)
  475. gifData.m_appEx.m_appAuthCode1 = gifBytes[byteIndex];
  476. byteIndex++;
  477. gifData.m_appEx.m_appAuthCode2 = gifBytes[byteIndex];
  478. byteIndex++;
  479. gifData.m_appEx.m_appAuthCode3 = gifBytes[byteIndex];
  480. byteIndex++;
  481. // Block Size & Application Data List
  482. while (true)
  483. {
  484. // Block Size (1 Byte)
  485. byte blockSize = gifBytes[byteIndex];
  486. byteIndex++;
  487. if (blockSize == 0x00)
  488. {
  489. // Block Terminator(1 Byte)
  490. break;
  491. }
  492. var appDataBlock = new ApplicationExtension.ApplicationDataBlock();
  493. appDataBlock.m_blockSize = blockSize;
  494. // Application Data(n Byte)
  495. appDataBlock.m_applicationData = new byte[appDataBlock.m_blockSize];
  496. for (int i = 0; i < appDataBlock.m_applicationData.Length; i++)
  497. {
  498. appDataBlock.m_applicationData[i] = gifBytes[byteIndex];
  499. byteIndex++;
  500. }
  501. if (gifData.m_appEx.m_appDataList == null)
  502. {
  503. gifData.m_appEx.m_appDataList = new List<ApplicationExtension.ApplicationDataBlock>();
  504. }
  505. gifData.m_appEx.m_appDataList.Add(appDataBlock);
  506. }
  507. }
  508. }