FateViewer/Assets/Scripts/Apng/SharpApng.cs

281 lines
7.9 KiB
C#

/* HaRepacker - WZ extractor and repacker
* Copyright (C) 2009, 2010 haha01haha01
* This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
* This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.*/
namespace SharpApng
{
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using uGIF;
using UnityEngine;
public class Frame : IDisposable
{
#region Fields
private Image m_bmp;
private int m_den;
private int m_num;
#endregion Fields
#region Constructors
public Frame(Image bmp, int num, int den)
{
this.m_num = num;
this.m_den = den;
this.m_bmp = bmp;
}
#endregion Constructors
#region Properties
public Image Bitmap
{
get
{
return m_bmp;
}
set
{
m_bmp = value;
}
}
public int DelayDen
{
get
{
return m_den;
}
set
{
m_den = value;
}
}
public int DelayNum
{
get
{
return m_num;
}
set
{
m_num = value;
}
}
#endregion Properties
#region Methods
public void Dispose()
{
}
#endregion Methods
}
internal class Apng : IDisposable
{
#region Fields
private List<Frame> m_frames = new List<Frame>();
#endregion Fields
#region Constructors
public Apng()
{
}
#endregion Constructors
#region Indexers
public Frame this[int index]
{
get
{
if (index < m_frames.Count) return m_frames[index];
else return null;
}
set
{
if (index < m_frames.Count) m_frames[index] = value;
}
}
#endregion Indexers
#region Methods
public void AddFrame(Frame frame)
{
m_frames.Add(frame);
}
public void AddFrame(Image bmp, int num, int den)
{
m_frames.Add(new Frame(bmp, num, den));
}
public void Dispose()
{
foreach (Frame frame in m_frames)
frame.Dispose();
m_frames.Clear();
}
public void WriteApng(string path, bool firstFrameHidden, bool disposeAfter)
{
Size maxSize = new Size(m_frames.Max(f => f.Bitmap.width), m_frames.Max(f => f.Bitmap.height));
for (int i = 0; i < m_frames.Count; i++)
{
Frame frame = m_frames[i];
//if (frame.Bitmap.Width != maxSize.Width || frame.Bitmap.Height != maxSize.Height)
//frame.Bitmap = ExtendImage(frame.Bitmap, maxSize);
ApngBasicWrapper.CreateFrameManaged(frame.Bitmap, frame.DelayNum, frame.DelayDen, i);
}
ApngBasicWrapper.SaveApngManaged(path, m_frames.Count, maxSize.Width, maxSize.Height, firstFrameHidden);
if (disposeAfter) Dispose();
}
private static Image ExtendImage(Image source, Size newSize)
{
Image result = new Image(newSize.Width, newSize.Height);
//using (Graphics g = Graphics.FromImage(result))
//{
// g.DrawImageUnscaled(source, 0, 0);
//}
return result;
}
#endregion Methods
}
internal static class ApngBasicWrapper
{
#region Fields
private const string apngdll = "apng64.dll";
private const int PIXEL_DEPTH = 4;
#endregion Fields
#region Methods
internal static void CreateFrameManaged(Image source, int num, int den, int i)
{
IntPtr ptr = MarshalByteArray(TranslateImage(source));
CreateFrame(ptr, num, den, i, source.width * source.height * PIXEL_DEPTH);
ReleaseData(ptr);
}
internal static void SaveApngManaged(string path, int frameCount, int width, int height, bool firstFrameHidden)
{
IntPtr pathPtr = MarshalString(path);
byte firstFrame = firstFrameHidden ? (byte)1 : (byte)0;
SaveAPNG(pathPtr, frameCount, width, height, PIXEL_DEPTH, firstFrame);
var bufferSize = GetBufferSize();
//Call and return the pointer
IntPtr returnedPtr = GetBufferContent();
//Create new Variable to Store the result
byte[] returnedResult = new byte[bufferSize];
//Copy from result pointer to the C# variable
Marshal.Copy(returnedPtr, returnedResult, 0, bufferSize);
File.WriteAllBytes(path, returnedResult);
Debug.Log("saved");
ReleaseData(pathPtr);
}
[DllImport(apngdll, CallingConvention = CallingConvention.StdCall)]
private static extern void CreateFrame(IntPtr pdata, int num, int den, int i, int len);
[DllImport(apngdll)]
private static extern void SaveAPNG(IntPtr path, int frameCount, int width, int height, int bytesPerPixel, byte firstFrameHidden);
[DllImport(apngdll)]
private static extern int GetBufferSize();
[DllImport(apngdll)]
private static extern IntPtr GetBufferContent();
private static IntPtr MarshalByteArray(byte[] source)
{
int size = Marshal.SizeOf(source[0]) * source.Length;
IntPtr pnt = Marshal.AllocHGlobal(size);
Marshal.Copy(source, 0, pnt, source.Length);
return pnt;
}
private static IntPtr MarshalString(string source)
{
byte[] toMarshal = Encoding.ASCII.GetBytes(source);
int size = Marshal.SizeOf(source[0]) * source.Length;
IntPtr pnt = Marshal.AllocHGlobal(size + Marshal.SizeOf(source[0]));
Marshal.Copy(toMarshal, 0, pnt, source.Length);
//Marshal.Copy(new byte[] { 0 }, 0, new IntPtr(pnt.ToInt32() + size), 1);
return pnt;
}
private static void ReleaseData(IntPtr ptr)
{
Marshal.FreeHGlobal(ptr);
}
static byte[] TranslateImage(Image texture)
{
int width = texture.width, height = texture.height;
Color32[] original = new Color32[texture.pixels.Length];
texture.pixels.CopyTo(original, 0);
byte[] result = new byte[texture.width * texture.height * PIXEL_DEPTH];
int p = 0;
Debug.Log("1");
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int index = (y * width + x);
int index2 = ((height-y-1) * width + x);
result[index * PIXEL_DEPTH] = original[index2].b;
result[index * PIXEL_DEPTH + 1] = original[index2].g;
result[index * PIXEL_DEPTH + 2] = original[index2].r;
result[index * PIXEL_DEPTH + 3] = original[index2].a;
}
p += 1;
}
Debug.Log("2");
return result;
}
#endregion Methods
}
}