1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the libgltf project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9 
10 #include <PNGHelper.h>
11 #include <iostream>
12 #include <cstdio>
13 #include <cstdlib>
14 #include <png.h>
15 
16 namespace libgltf { namespace pnghelper
17 {
18 
ReadPNGFromFile(const std::string & sFileName,char ** pBuffer,int & nWidth,int & nHeight)19 bool ReadPNGFromFile(const std::string& sFileName, char** pBuffer, int& nWidth, int& nHeight)
20 {
21     unsigned char aHeader[8];    // 8 is the maximum size that can be checked
22 
23     /* open file and test for it being a png */
24     FILE* pFile = fopen(sFileName.c_str(), "rb");
25     if( !pFile )
26     {
27         std::cerr << "File " << sFileName.c_str() << " could not be opened for reading" << std::endl;
28         return false;
29     }
30 
31     fread(aHeader, 1, 8, pFile);
32     if (png_sig_cmp(aHeader, 0, 8))
33     {
34         std::cerr << "File " << sFileName.c_str() << " is not recognized as a PNG file" << std::endl;
35         return false;
36     }
37 
38 
39     /* initialization */
40     png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
41 
42     if (!png_ptr)
43     {
44         std::cerr << "png_create_read_struct failed" << std::endl;
45         return false;
46     }
47 
48     png_infop info_ptr = png_create_info_struct(png_ptr);
49     if (!info_ptr)
50     {
51         std::cerr << "png_create_info_struct failed" << std::endl;
52         return false;
53     }
54 
55     if (setjmp(png_jmpbuf(png_ptr)))
56     {
57         std::cerr << "Error during init_io" << std::endl;
58         return false;
59     }
60 
61     png_init_io(png_ptr, pFile);
62     png_set_sig_bytes(png_ptr, 8);
63 
64     png_read_info(png_ptr, info_ptr);
65 
66     nWidth = png_get_image_width(png_ptr, info_ptr);
67     nHeight = png_get_image_height(png_ptr, info_ptr);
68 
69     png_read_update_info(png_ptr, info_ptr);
70 
71     /* read file */
72     if (setjmp(png_jmpbuf(png_ptr)))
73     {
74         std::cerr << "Error during read_image" << std::endl;
75         return false;
76     }
77 
78     png_bytep* pRowPointers = (png_bytep*) malloc(sizeof(png_bytep) * nHeight);
79     for (int i = 0; i < nHeight; ++i)
80     {
81         pRowPointers[i] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));
82     }
83 
84     png_read_image(png_ptr, pRowPointers);
85 
86     int nColorType = png_get_color_type(png_ptr, info_ptr);
87 
88     if( nColorType != PNG_COLOR_TYPE_RGB && nColorType != PNG_COLOR_TYPE_RGBA )
89     {
90         std::cerr << "Input file must be PNG_COLOR_TYPE_RGB or PNG_COLOR_TYPE_RGBA" << std::endl;
91         return false;
92     }
93 
94     (*pBuffer) = new char[4*nWidth*nHeight];
95     unsigned nIndex = 0;
96     for (int i = 0; i < nHeight; ++i)
97     {
98         png_byte* pRow = pRowPointers[i];
99         for ( int j = 0; j < nWidth; ++j)
100         {
101             png_byte* pPtr;
102             if( nColorType == PNG_COLOR_TYPE_RGB )
103             {
104                 pPtr = &(pRow[j*3]);
105             }
106             else
107             {
108                 pPtr = &(pRow[j*4]);
109             }
110             (*pBuffer)[nIndex++] = pPtr[0];
111             (*pBuffer)[nIndex++] = pPtr[1];
112             (*pBuffer)[nIndex++] = pPtr[2];
113             if( nColorType == PNG_COLOR_TYPE_RGB )
114             {
115                 (*pBuffer)[nIndex++] = 127;
116             }
117             else
118             {
119                 (*pBuffer)[nIndex++] = pPtr[3];
120             }
121         }
122     }
123 
124     fclose(pFile);
125     return true;
126 }
127 
WritePNGToFile(const std::string & sFileName,const char * pBuffer,const int nWidth,const int nHeight)128 bool WritePNGToFile(const std::string& sFileName, const char* pBuffer, const int nWidth, const int nHeight)
129 {
130     /* create file */
131     FILE *pFile = fopen(sFileName.c_str(), "wb");
132     if (!pFile)
133     {
134         std::cerr << "File " << sFileName.c_str() << " could not be opened for writing" << std::endl;
135         return false;
136     }
137 
138     /* initialize stuff */
139     png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
140 
141     if (!png_ptr)
142     {
143         std::cerr << "png_create_write_struct failed" << std::endl;
144         return false;
145     }
146 
147     png_infop info_ptr = png_create_info_struct(png_ptr);
148     if (!info_ptr)
149     {
150         std::cerr << "png_create_info_struct failed" << std::endl;
151         return false;
152     }
153 
154     if (setjmp(png_jmpbuf(png_ptr)))
155     {
156         std::cerr << "Error during init_io" << std::endl;
157         return false;
158     }
159 
160     png_init_io(png_ptr, pFile);
161 
162     png_set_IHDR(png_ptr, info_ptr, nWidth, nHeight,
163             8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
164             PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
165 
166     png_bytep* pRowPointers = (png_bytep*)malloc( sizeof(png_bytep) * nHeight);
167     unsigned int nIndex = 0;
168     for (int i = 0; i < nHeight; ++i)
169     {
170         png_byte* pRow = (png_byte*)malloc(sizeof(png_byte) * nWidth * 4);
171         pRowPointers[i] = pRow;
172         for (int j = 0; j < nWidth; ++j)
173         {
174             *pRow++ = pBuffer[nIndex++];
175             *pRow++ = pBuffer[nIndex++];
176             *pRow++ = pBuffer[nIndex++];
177             ++nIndex;
178         }
179     }
180 
181     /* write header */
182     if (setjmp(png_jmpbuf(png_ptr)))
183     {
184         std::cerr << "Error during writing header" << std::endl;
185         return false;
186     }
187 
188     png_write_info(png_ptr, info_ptr);
189 
190     /* write bytes */
191     if (setjmp(png_jmpbuf(png_ptr)))
192     {
193         std::cerr << "Error during writing bytes" << std::endl;
194         return false;
195     }
196 
197     png_write_image(png_ptr, pRowPointers);
198 
199     /* end write */
200     if (setjmp(png_jmpbuf(png_ptr)))
201     {
202         std::cerr << "Error during end of write" << std::endl;
203         return false;
204     }
205 
206     png_write_end(png_ptr, NULL);
207 
208     /* cleanup heap allocation */
209     for (int i = 0; i < nHeight; ++i)
210     {
211         free(pRowPointers[i]);
212     }
213     free(pRowPointers);
214 
215     fclose(pFile);
216     return true;
217 }
218 
219 } // pnghelper
220 } // libgltf
221