PNG 动画






3.81/5 (7投票s)
一种 32 位动画,工作方式类似于 8 位 GIF 动画格式
引言
已经存在 .APNG 格式,可以执行类似于 GIF 动画的动画,但是由于其使用的块模式,在打开/写入文件时使用起来比较复杂。
因此,我决定创建自己的格式,该格式可以被任何应用程序显示,包括 Windows 资源管理器,即使它们无法以动画模式显示它。
我使用复合文件以非常简单的方式存储数据,如下所示
其中
第一个 PNG
是一个常规 PNG 文件,与动画的第一帧匹配。
第二个 PNG
是一个常规 PNG 文件,与动画的所有其他帧匹配。
footer
是一个 ANIHEADER 结构,使用这些成员
const int ANIM = 1296649793;
struct ANIHEADER {
WORD nFrame; // 动画中的帧数
WORD nWidth; // 单帧的宽度
WORD nHeight; // 单帧的高度
WORD nSpeed; // 动画速度
DWORD noffset; // 第二个 PNG 文件开始的偏移量
DWORD reserved; // 用于进一步扩展
DWORD nSign; // ANIM 签名,用于检测文件是否是有效的 PNG 动画
} ;
注意: 所有帧的大小都与“第一个 PNG”完全相同,即引导帧。
此格式的缺点是,如果您将其加载到标准的图形应用程序中,您只会获得第一帧,
并且如果您保存它,您将丢失动画,因为“第二个 PNG”部分和“页脚”将会丢失。
以下是此 PNG 动画格式在浏览器中的渲染方式
除了文件大小之外,它看起来就像一个普通的 377 x 512 像素的静态 PNG 文件。
使用代码
以下是如何在 C++ 中读取它
long ZI_IsPNGanimation(IN WCHAR* szFile) long nRet = 0; if (zExist(szFile)) { HANDLE hFileIn = 0; if (zFOpen(szFile, 0, 0, hFileIn) == 0) { DWORD nSize = 4; string sBuffer; sBuffer.assign(nSize, 0); if (zFGetAt(hFileIn, zFlof(hFileIn) - nSize, sBuffer) == 0) { DWORD nSign = 0; MoveMemory(&nSign, (CHAR*) sBuffer.c_str(), nSize); if (nSign == ANIM) { nRet = -1; } } zFClose(hFileIn); } } return nRet; } long ZI_GetPNGanimation(IN WCHAR* szFile, OUT ANIHEADER &anih) { long nRet = 0; // First clear the header DWORD nSize = sizeof(anih); ZeroMemory(&anih, nSize); if (zExist(szFile)) { HANDLE hFileIn = 0; if (zFOpen(szFile, 0, 0, hFileIn) == 0) { string sBuffer; sBuffer.assign(nSize, 0); if (zFGetAt(hFileIn, zFlof(hFileIn) - nSize, sBuffer) == 0) { MoveMemory(&anih, (CHAR*) sBuffer.c_str(), nSize); if (anih.nSign == ANIM) { nRet = -1; } else { ZeroMemory(&anih, nSize); } } zFClose(hFileIn); } } return nRet; } HBITMAP ZI_LoadPNGanimation(IN WCHAR* szFile, OUT ANIHEADER &anih) { HBITMAP hDIB = 0; DWORD BufferSize = 0; HANDLE hFileIn = 0; LPSTREAM pImageStream = 0; HGLOBAL hGlobal = 0; LPVOID pGlobalBuffer = 0; LONG_PTR hImage = 0; if (ZI_GetPNGanimation(szFile, anih)) { if (zFOpen(szFile, 0, 0, hFileIn) == 0) { LONG_PTR graphics = 0; DWORD nSize = 0; HDC hDC = zDisplayDC(); HDC ImgHDC = CreateCompatibleDC(hDC); hDIB = zCreateDIBSection(ImgHDC, anih.nWidth * anih.nFrame, anih.nHeight, 32); SelectObject(ImgHDC, hDIB); if (GdipCreateFromHDC(ImgHDC, graphics) == 0) { BufferSize = zFlof(hFileIn) - anih.noffset - sizeof(anih) + 1; string sBuffer; sBuffer.assign(BufferSize, 0); if (zFGetAt(hFileIn, anih.noffset - 1, sBuffer) == 0) { // sBuffer 2 loaded hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, BufferSize); if (hGlobal) { pGlobalBuffer = GlobalLock(hGlobal); if (pGlobalBuffer) { MoveMemory(pGlobalBuffer, (CHAR*) sBuffer.c_str(), BufferSize); if (CreateStreamOnHGlobal(hGlobal, NULL, &pImageStream) == 0) { if (GdipCreateBitmapFromStream(pImageStream, hImage) == 0) { GdipDrawImageRectI(graphics, hImage, anih.nWidth, 0, anih.nWidth * anih.nFrame - anih.nWidth, anih.nHeight); GdipDisposeImage(hImage); } ReleaseObject((PFUNKNOWN*) pImageStream); } GlobalUnlock(pGlobalBuffer); } GlobalFree(hGlobal); } } BufferSize = anih.noffset - 1; sBuffer.assign(BufferSize, 0); if (zFGetAt(hFileIn, 0, sBuffer) == 0) { // sBuffer 1 loaded hGlobal = GlobalAlloc(GMEM_MOVEABLE | GMEM_NODISCARD, BufferSize); if (hGlobal) { pGlobalBuffer = GlobalLock(hGlobal); if (pGlobalBuffer) { MoveMemory(pGlobalBuffer, (CHAR*) sBuffer.c_str(), BufferSize); if (CreateStreamOnHGlobal(hGlobal, NULL, &pImageStream) == 0) { if (GdipCreateBitmapFromStream(pImageStream, hImage) == 0) { GdipDrawImageRectI(graphics, hImage, 0, 0, anih.nWidth, anih.nHeight); GdipDisposeImage(hImage); } ReleaseObject((PFUNKNOWN*) pImageStream); } GlobalUnlock(pGlobalBuffer); } GlobalFree(hGlobal); } } zFClose(hFileIn); } GdipDeleteGraphics(graphics); DeleteDC(ImgHDC); DeleteDC(hDC); } } return hDIB; }
由于演示项目提供了 7 个 PNG 动画文件,因此其大小超过了此处允许的 10 Mb 限制,因此您必须使用此链接下载 C++ 64 位 Visual Studio 2015 Community 项目。
http://www.objreader.com/download/demo/RedHawk.zip
此演示旨在向 George Redhawk 的出色工作致敬,请在此处了解更多信息
http://www.objreader.com/index.php?topic=67.0
您可以在我的 www.objreader.com 专用论坛的此专用部分中了解有关此 PNG 动画专有格式的更多信息:http://www.objreader.com/index.php?topic=69.0
另请参阅
GIFtoPNG 实用程序(VS2015 C++ 项目)
http://www.objreader.com/index.php?topic=69.msg302#msg302
和
PNGanim(VS2015 C++ 项目)用于更改 PNG 动画的速度
http://www.objreader.com/index.php?topic=69.msg309#msg309
为了避免垃圾邮件机器人和各种非法、自动注册已被禁用。
如果您想注册,请使用您的真实姓名,并发送请求至
objreader (at) gmail.com
注意:附件仅供注册用户使用。