1// tImageAPNG.cpp 
2// 
3// This knows how to load animated PNGs (APNGs). It knows the details of the apng file format and loads the data into 
4// multiple tPixel arrays, one for each frame. These arrays may be 'stolen' by tPictures. 
5// 
6// Copyright (c) 2020 Tristan Grimmer. 
7// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
8// granted, provided that the above copyright notice and this permission notice appear in all copies. 
9// 
10// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
11// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
12// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
13// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
14// PERFORMANCE OF THIS SOFTWARE. 
15 
16#include <Foundation/tStandard.h> 
17#include <Foundation/tString.h> 
18#include <System/tFile.h> 
19#include "Image/tImageAPNG.h" 
20#include "apngdis.h" 
21using namespace tSystem
22namespace tImage 
23
24 
25 
26bool tImageAPNG::IsAnimatedPNG(const tString& pngFile
27
28 int numBytes = 1024
29 uint8* headData = tSystem::tLoadFileHead(pngFile, numBytes); 
30 if (!headData
31 return false
32 
33 uint8 acTL[] = { 'a', 'c', 'T', 'L' }; 
34 uint8 IDAT[] = { 'I', 'D', 'A', 'T' }; 
35 uint8* actlLoc = (uint8*)tStd::tMemmem(headData, numBytes, acTL, sizeof(acTL)); 
36 if (!actlLoc
37 return false
38 
39 // Now for safety we also make sure there is an IDAT after the acTL. 
40 uint8* idatLoc = (uint8*)tStd::tMemmem(actlLoc+sizeof(acTL), numBytes - (actlLoc-headData) - sizeof(acTL), IDAT, sizeof(IDAT)); 
41 
42 bool found = idatLoc ? true : false
43 delete[] headData
44 return found
45
46 
47 
48bool tImageAPNG::Load(const tString& apngFile
49
50 Clear(); 
51 
52 // Note that many apng files still have a .png extension/filetype, so we support both here. 
53 tSystem::tFileType filetype = tSystem::tGetFileType(apngFile); 
54 if ((filetype != tSystem::tFileType::APNG) && (filetype != tSystem::tFileType::PNG)) 
55 return false
56 
57 if (!tFileExists(apngFile)) 
58 return false
59 
60 std::vector<APngDis::Image> frames
61 int result = APngDis::load_apng(apngFile.Chars(), frames); 
62 if (result < 0
63 return false
64 
65 // Now we load and populate the frames. 
66 SrcPixelFormat = tPixelFormat::R8G8B8A8
67 for (int f = 0; f < frames.size(); f++) 
68
69 APngDis::Image& srcFrame = frames[f]; 
70 Frame* newFrame = new Frame; 
71 newFrame->SrcPixelFormat = tPixelFormat::R8G8B8A8; 
72 int width = srcFrame.w; 
73 int height = srcFrame.h; 
74 newFrame->Width = width; 
75 newFrame->Height = height; 
76 newFrame->Pixels = new tPixel[width * height]; 
77 
78 // From the official apng spec: 
79 // The delay_num and delay_den parameters together specify a fraction indicating the time to display 
80 // the current frame, in seconds. If the denominator is 0, it is to be treated as if it were 100 (that 
81 // is, delay_num then specifies 1/100ths of a second). If the the value of the numerator is 0 the decoder 
82 // should render the next frame as quickly as possible, though viewers may impose a reasonable lower bound. 
83 uint dnum = srcFrame.delay_num; 
84 uint dden = srcFrame.delay_den; 
85 if (dden == 0
86 dden = 100
87 newFrame->Duration = (dnum == 0) ? 1.0f/60.0f : float(dnum) / float(dden); 
88 
89 tAssert(srcFrame.bpp == 4); 
90 for (int r = 0; r < height; r++) 
91
92 uint8* srcRowData = srcFrame.rows[r]; 
93 // uint8* dstRowData = (uint8*)newFrame->Pixels + r * (width*4); 
94 uint8* dstRowData = (uint8*)newFrame->Pixels + ((height-1)-r) * (width*4); 
95 tStd::tMemcpy(dstRowData, srcRowData, width*4); 
96
97 
98 Frames.Append(newFrame); 
99
100 
101 for (int f = 0; f < frames.size(); f++) 
102 frames[f].free(); 
103 frames.clear(); 
104 
105 return true
106
107 
108 
109
110