/*
UniGif
Copyright (c) 2015 WestHillApps (Hironari Nishioka)
This software is released under the MIT License.
http://opensource.org/licenses/mit-license.php
*/
using System;
using System.Collections.Generic;
using UnityEngine;
public static partial class UniGif
{
///
/// Set GIF data
///
/// GIF byte data
/// ref GIF data
/// Debug log flag
/// Result
private static bool SetGifData(byte[] gifBytes, ref GifData gifData, bool debugLog)
{
if (debugLog)
{
Debug.Log("SetGifData Start.");
}
if (gifBytes == null || gifBytes.Length <= 0)
{
Debug.LogError("bytes is nothing.");
return false;
}
int byteIndex = 0;
if (SetGifHeader(gifBytes, ref byteIndex, ref gifData) == false)
{
Debug.LogError("GIF header set error.");
return false;
}
if (SetGifBlock(gifBytes, ref byteIndex, ref gifData) == false)
{
Debug.LogError("GIF block set error.");
return false;
}
if (debugLog)
{
gifData.Dump();
Debug.Log("SetGifData Finish.");
}
return true;
}
private static bool SetGifHeader(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
{
// Signature(3 Bytes)
// 0x47 0x49 0x46 (GIF)
if (gifBytes[0] != 'G' || gifBytes[1] != 'I' || gifBytes[2] != 'F')
{
Debug.LogError("This is not GIF image.");
return false;
}
gifData.m_sig0 = gifBytes[0];
gifData.m_sig1 = gifBytes[1];
gifData.m_sig2 = gifBytes[2];
// Version(3 Bytes)
// 0x38 0x37 0x61 (87a) or 0x38 0x39 0x61 (89a)
if ((gifBytes[3] != '8' || gifBytes[4] != '7' || gifBytes[5] != 'a') &&
(gifBytes[3] != '8' || gifBytes[4] != '9' || gifBytes[5] != 'a'))
{
Debug.LogError("GIF version error.\nSupported only GIF87a or GIF89a.");
return false;
}
gifData.m_ver0 = gifBytes[3];
gifData.m_ver1 = gifBytes[4];
gifData.m_ver2 = gifBytes[5];
// Logical Screen Width(2 Bytes)
gifData.m_logicalScreenWidth = BitConverter.ToUInt16(gifBytes, 6);
// Logical Screen Height(2 Bytes)
gifData.m_logicalScreenHeight = BitConverter.ToUInt16(gifBytes, 8);
// 1 Byte
{
// Global Color Table Flag(1 Bit)
gifData.m_globalColorTableFlag = (gifBytes[10] & 128) == 128; // 0b10000000
// Color Resolution(3 Bits)
switch (gifBytes[10] & 112)
{
case 112: // 0b01110000
gifData.m_colorResolution = 8;
break;
case 96: // 0b01100000
gifData.m_colorResolution = 7;
break;
case 80: // 0b01010000
gifData.m_colorResolution = 6;
break;
case 64: // 0b01000000
gifData.m_colorResolution = 5;
break;
case 48: // 0b00110000
gifData.m_colorResolution = 4;
break;
case 32: // 0b00100000
gifData.m_colorResolution = 3;
break;
case 16: // 0b00010000
gifData.m_colorResolution = 2;
break;
default:
gifData.m_colorResolution = 1;
break;
}
// Sort Flag(1 Bit)
gifData.m_sortFlag = (gifBytes[10] & 8) == 8; // 0b00001000
// Size of Global Color Table(3 Bits)
int val = (gifBytes[10] & 7) + 1;
gifData.m_sizeOfGlobalColorTable = (int)Math.Pow(2, val);
}
// Background Color Index(1 Byte)
gifData.m_bgColorIndex = gifBytes[11];
// Pixel Aspect Ratio(1 Byte)
gifData.m_pixelAspectRatio = gifBytes[12];
byteIndex = 13;
if (gifData.m_globalColorTableFlag)
{
// Global Color Table(0~255×3 Bytes)
gifData.m_globalColorTable = new List();
for (int i = byteIndex; i < byteIndex + (gifData.m_sizeOfGlobalColorTable * 3); i += 3)
{
gifData.m_globalColorTable.Add(new byte[] { gifBytes[i], gifBytes[i + 1], gifBytes[i + 2] });
}
byteIndex = byteIndex + (gifData.m_sizeOfGlobalColorTable * 3);
}
return true;
}
private static bool SetGifBlock(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
{
try
{
int lastIndex = 0;
while (true)
{
int nowIndex = byteIndex;
if (gifBytes[nowIndex] == 0x2c)
{
// Image Block(0x2c)
SetImageBlock(gifBytes, ref byteIndex, ref gifData);
}
else if (gifBytes[nowIndex] == 0x21)
{
// Extension
switch (gifBytes[nowIndex + 1])
{
case 0xf9:
// Graphic Control Extension(0x21 0xf9)
SetGraphicControlExtension(gifBytes, ref byteIndex, ref gifData);
break;
case 0xfe:
// Comment Extension(0x21 0xfe)
SetCommentExtension(gifBytes, ref byteIndex, ref gifData);
break;
case 0x01:
// Plain Text Extension(0x21 0x01)
SetPlainTextExtension(gifBytes, ref byteIndex, ref gifData);
break;
case 0xff:
// Application Extension(0x21 0xff)
SetApplicationExtension(gifBytes, ref byteIndex, ref gifData);
break;
default:
break;
}
}
else if (gifBytes[nowIndex] == 0x3b)
{
// Trailer(1 Byte)
gifData.m_trailer = gifBytes[byteIndex];
byteIndex++;
break;
}
if (lastIndex == nowIndex)
{
Debug.LogError("Infinite loop error.");
return false;
}
lastIndex = nowIndex;
}
}
catch (Exception ex)
{
Debug.LogError(ex.Message);
return false;
}
return true;
}
private static void SetImageBlock(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
{
ImageBlock ib = new ImageBlock();
// Image Separator(1 Byte)
// 0x2c
ib.m_imageSeparator = gifBytes[byteIndex];
byteIndex++;
// Image Left Position(2 Bytes)
ib.m_imageLeftPosition = BitConverter.ToUInt16(gifBytes, byteIndex);
byteIndex += 2;
// Image Top Position(2 Bytes)
ib.m_imageTopPosition = BitConverter.ToUInt16(gifBytes, byteIndex);
byteIndex += 2;
// Image Width(2 Bytes)
ib.m_imageWidth = BitConverter.ToUInt16(gifBytes, byteIndex);
byteIndex += 2;
// Image Height(2 Bytes)
ib.m_imageHeight = BitConverter.ToUInt16(gifBytes, byteIndex);
byteIndex += 2;
// 1 Byte
{
// Local Color Table Flag(1 Bit)
ib.m_localColorTableFlag = (gifBytes[byteIndex] & 128) == 128; // 0b10000000
// Interlace Flag(1 Bit)
ib.m_interlaceFlag = (gifBytes[byteIndex] & 64) == 64; // 0b01000000
// Sort Flag(1 Bit)
ib.m_sortFlag = (gifBytes[byteIndex] & 32) == 32; // 0b00100000
// Reserved(2 Bits)
// Unused
// Size of Local Color Table(3 Bits)
int val = (gifBytes[byteIndex] & 7) + 1;
ib.m_sizeOfLocalColorTable = (int)Math.Pow(2, val);
byteIndex++;
}
if (ib.m_localColorTableFlag)
{
// Local Color Table(0~255×3 Bytes)
ib.m_localColorTable = new List();
for (int i = byteIndex; i < byteIndex + (ib.m_sizeOfLocalColorTable * 3); i += 3)
{
ib.m_localColorTable.Add(new byte[] { gifBytes[i], gifBytes[i + 1], gifBytes[i + 2] });
}
byteIndex = byteIndex + (ib.m_sizeOfLocalColorTable * 3);
}
// LZW Minimum Code Size(1 Byte)
ib.m_lzwMinimumCodeSize = gifBytes[byteIndex];
byteIndex++;
// Block Size & Image Data List
while (true)
{
// Block Size(1 Byte)
byte blockSize = gifBytes[byteIndex];
byteIndex++;
if (blockSize == 0x00)
{
// Block Terminator(1 Byte)
break;
}
var imageDataBlock = new ImageBlock.ImageDataBlock();
imageDataBlock.m_blockSize = blockSize;
// Image Data(? Bytes)
imageDataBlock.m_imageData = new byte[imageDataBlock.m_blockSize];
for (int i = 0; i < imageDataBlock.m_imageData.Length; i++)
{
imageDataBlock.m_imageData[i] = gifBytes[byteIndex];
byteIndex++;
}
if (ib.m_imageDataList == null)
{
ib.m_imageDataList = new List();
}
ib.m_imageDataList.Add(imageDataBlock);
}
if (gifData.m_imageBlockList == null)
{
gifData.m_imageBlockList = new List();
}
gifData.m_imageBlockList.Add(ib);
}
private static void SetGraphicControlExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
{
GraphicControlExtension gcEx = new GraphicControlExtension();
// Extension Introducer(1 Byte)
// 0x21
gcEx.m_extensionIntroducer = gifBytes[byteIndex];
byteIndex++;
// Graphic Control Label(1 Byte)
// 0xf9
gcEx.m_graphicControlLabel = gifBytes[byteIndex];
byteIndex++;
// Block Size(1 Byte)
// 0x04
gcEx.m_blockSize = gifBytes[byteIndex];
byteIndex++;
// 1 Byte
{
// Reserved(3 Bits)
// Unused
// Disposal Mothod(3 Bits)
// 0 (No disposal specified)
// 1 (Do not dispose)
// 2 (Restore to background color)
// 3 (Restore to previous)
switch (gifBytes[byteIndex] & 28)
{ // 0b00011100
case 4: // 0b00000100
gcEx.m_disposalMethod = 1;
break;
case 8: // 0b00001000
gcEx.m_disposalMethod = 2;
break;
case 12: // 0b00001100
gcEx.m_disposalMethod = 3;
break;
default:
gcEx.m_disposalMethod = 0;
break;
}
// User Input Flag(1 Bit)
// Unknown
// Transparent Color Flag(1 Bit)
gcEx.m_transparentColorFlag = (gifBytes[byteIndex] & 1) == 1; // 0b00000001
byteIndex++;
}
// Delay Time(2 Bytes)
gcEx.m_delayTime = BitConverter.ToUInt16(gifBytes, byteIndex);
byteIndex += 2;
// Transparent Color Index(1 Byte)
gcEx.m_transparentColorIndex = gifBytes[byteIndex];
byteIndex++;
// Block Terminator(1 Byte)
gcEx.m_blockTerminator = gifBytes[byteIndex];
byteIndex++;
if (gifData.m_graphicCtrlExList == null)
{
gifData.m_graphicCtrlExList = new List();
}
gifData.m_graphicCtrlExList.Add(gcEx);
}
private static void SetCommentExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
{
CommentExtension commentEx = new CommentExtension();
// Extension Introducer(1 Byte)
// 0x21
commentEx.m_extensionIntroducer = gifBytes[byteIndex];
byteIndex++;
// Comment Label(1 Byte)
// 0xfe
commentEx.m_commentLabel = gifBytes[byteIndex];
byteIndex++;
// Block Size & Comment Data List
while (true)
{
// Block Size(1 Byte)
byte blockSize = gifBytes[byteIndex];
byteIndex++;
if (blockSize == 0x00)
{
// Block Terminator(1 Byte)
break;
}
var commentDataBlock = new CommentExtension.CommentDataBlock();
commentDataBlock.m_blockSize = blockSize;
// Comment Data(n Byte)
commentDataBlock.m_commentData = new byte[commentDataBlock.m_blockSize];
for (int i = 0; i < commentDataBlock.m_commentData.Length; i++)
{
commentDataBlock.m_commentData[i] = gifBytes[byteIndex];
byteIndex++;
}
if (commentEx.m_commentDataList == null)
{
commentEx.m_commentDataList = new List();
}
commentEx.m_commentDataList.Add(commentDataBlock);
}
if (gifData.m_commentExList == null)
{
gifData.m_commentExList = new List();
}
gifData.m_commentExList.Add(commentEx);
}
private static void SetPlainTextExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
{
PlainTextExtension plainTxtEx = new PlainTextExtension();
// Extension Introducer(1 Byte)
// 0x21
plainTxtEx.m_extensionIntroducer = gifBytes[byteIndex];
byteIndex++;
// Plain Text Label(1 Byte)
// 0x01
plainTxtEx.m_plainTextLabel = gifBytes[byteIndex];
byteIndex++;
// Block Size(1 Byte)
// 0x0c
plainTxtEx.m_blockSize = gifBytes[byteIndex];
byteIndex++;
// Text Grid Left Position(2 Bytes)
// Not supported
byteIndex += 2;
// Text Grid Top Position(2 Bytes)
// Not supported
byteIndex += 2;
// Text Grid Width(2 Bytes)
// Not supported
byteIndex += 2;
// Text Grid Height(2 Bytes)
// Not supported
byteIndex += 2;
// Character Cell Width(1 Bytes)
// Not supported
byteIndex++;
// Character Cell Height(1 Bytes)
// Not supported
byteIndex++;
// Text Foreground Color Index(1 Bytes)
// Not supported
byteIndex++;
// Text Background Color Index(1 Bytes)
// Not supported
byteIndex++;
// Block Size & Plain Text Data List
while (true)
{
// Block Size(1 Byte)
byte blockSize = gifBytes[byteIndex];
byteIndex++;
if (blockSize == 0x00)
{
// Block Terminator(1 Byte)
break;
}
var plainTextDataBlock = new PlainTextExtension.PlainTextDataBlock();
plainTextDataBlock.m_blockSize = blockSize;
// Plain Text Data(n Byte)
plainTextDataBlock.m_plainTextData = new byte[plainTextDataBlock.m_blockSize];
for (int i = 0; i < plainTextDataBlock.m_plainTextData.Length; i++)
{
plainTextDataBlock.m_plainTextData[i] = gifBytes[byteIndex];
byteIndex++;
}
if (plainTxtEx.m_plainTextDataList == null)
{
plainTxtEx.m_plainTextDataList = new List();
}
plainTxtEx.m_plainTextDataList.Add(plainTextDataBlock);
}
if (gifData.m_plainTextExList == null)
{
gifData.m_plainTextExList = new List();
}
gifData.m_plainTextExList.Add(plainTxtEx);
}
private static void SetApplicationExtension(byte[] gifBytes, ref int byteIndex, ref GifData gifData)
{
// Extension Introducer(1 Byte)
// 0x21
gifData.m_appEx.m_extensionIntroducer = gifBytes[byteIndex];
byteIndex++;
// Extension Label(1 Byte)
// 0xff
gifData.m_appEx.m_extensionLabel = gifBytes[byteIndex];
byteIndex++;
// Block Size(1 Byte)
// 0x0b
gifData.m_appEx.m_blockSize = gifBytes[byteIndex];
byteIndex++;
// Application Identifier(8 Bytes)
gifData.m_appEx.m_appId1 = gifBytes[byteIndex];
byteIndex++;
gifData.m_appEx.m_appId2 = gifBytes[byteIndex];
byteIndex++;
gifData.m_appEx.m_appId3 = gifBytes[byteIndex];
byteIndex++;
gifData.m_appEx.m_appId4 = gifBytes[byteIndex];
byteIndex++;
gifData.m_appEx.m_appId5 = gifBytes[byteIndex];
byteIndex++;
gifData.m_appEx.m_appId6 = gifBytes[byteIndex];
byteIndex++;
gifData.m_appEx.m_appId7 = gifBytes[byteIndex];
byteIndex++;
gifData.m_appEx.m_appId8 = gifBytes[byteIndex];
byteIndex++;
// Application Authentication Code(3 Bytes)
gifData.m_appEx.m_appAuthCode1 = gifBytes[byteIndex];
byteIndex++;
gifData.m_appEx.m_appAuthCode2 = gifBytes[byteIndex];
byteIndex++;
gifData.m_appEx.m_appAuthCode3 = gifBytes[byteIndex];
byteIndex++;
// Block Size & Application Data List
while (true)
{
// Block Size (1 Byte)
byte blockSize = gifBytes[byteIndex];
byteIndex++;
if (blockSize == 0x00)
{
// Block Terminator(1 Byte)
break;
}
var appDataBlock = new ApplicationExtension.ApplicationDataBlock();
appDataBlock.m_blockSize = blockSize;
// Application Data(n Byte)
appDataBlock.m_applicationData = new byte[appDataBlock.m_blockSize];
for (int i = 0; i < appDataBlock.m_applicationData.Length; i++)
{
appDataBlock.m_applicationData[i] = gifBytes[byteIndex];
byteIndex++;
}
if (gifData.m_appEx.m_appDataList == null)
{
gifData.m_appEx.m_appDataList = new List();
}
gifData.m_appEx.m_appDataList.Add(appDataBlock);
}
}
}