TDME2  1.9.200
PNGTextureReader.cpp
Go to the documentation of this file.
2 
3 #include <memory>
4 #include <new>
5 #include <string>
6 #include <vector>
7 
8 #include <tdme/tdme.h>
10 #include <tdme/engine/Texture.h>
12 #include <tdme/utilities/Console.h>
13 
14 #include <ext/libpng/png.h>
15 
16 using std::nothrow;
17 using std::string;
18 using std::to_string;
19 using std::unique_ptr;
20 using std::vector;
21 
23 
28 
29 void PNGTextureReader::readDataFromMemory(png_structp png_ptr, png_bytep outBytes, png_size_t outBytesToRead) {
30  auto io_ptr = png_get_io_ptr(png_ptr);
31  if (io_ptr == nullptr) return;
32 
33  auto pngInputStream = static_cast<PNGInputStream*>(io_ptr);
34  pngInputStream->readBytes((int8_t*)outBytes, outBytesToRead);
35 }
36 
37 bool PNGTextureReader::readHeader(const vector<uint8_t>& pngData, int& width, int& height, uint8_t& bytesPerPixel) {
38  // create PNG input stream
39  PNGInputStream pngInputStream(&pngData);
40 
41  // check that the PNG signature is in the file header
42  unsigned char sig[8];
43  pngInputStream.readBytes((int8_t*)sig, sizeof(sig));
44  if (png_sig_cmp(sig, 0, 8)) {
45  return false;
46  }
47 
48  // create two data structures: 'png_struct' and 'png_info'
49  auto png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
50  if (png == nullptr) {
51  return false;
52  }
53  auto info = png_create_info_struct(png);
54  if (info == nullptr) {
55  png_destroy_read_struct(&png, nullptr, nullptr);
56  return false;
57  }
58 
59  // set up custom read function
60  png_set_read_fn(png, &pngInputStream, PNGTextureReader::readDataFromMemory);
61 
62  // set libpng error handling mechanism
63  if (setjmp(png_jmpbuf(png))) {
64  png_destroy_read_struct(&png, &info, nullptr);
65  return false;
66  }
67 
68  // tell libpng we already read the signature
69  png_set_sig_bytes(png, sizeof(sig));
70 
71  // get image information
72  png_read_info(png, info);
73 
74  // dimensions
75  width = png_get_image_width(png, info);
76  height = png_get_image_height(png, info);
77 
78  // determine bytes per pixel
79  bytesPerPixel = 0;
80  switch(png_get_color_type(png, info)) {
81  case PNG_COLOR_TYPE_GRAY:
82  {
83  bytesPerPixel = 3;
84  break;
85  }
86  case PNG_COLOR_TYPE_GRAY_ALPHA:
87  {
88  bytesPerPixel = 4;
89  break;
90  }
91  case PNG_COLOR_TYPE_PALETTE:
92  {
93  // if transparency, convert it to alpha
94  bool alpha = false;
95  if (png_get_valid(png, info, PNG_INFO_tRNS)) {
96  alpha = true;
97  }
98  bytesPerPixel = alpha?4:3;
99  break;
100  }
101  case PNG_COLOR_TYPE_RGB:
102  {
103  bytesPerPixel = 3;
104  break;
105  }
106  case PNG_COLOR_TYPE_RGBA:
107  {
108  bytesPerPixel = 4;
109  break;
110  }
111  default:
112  {
113  png_destroy_read_struct(&png, &info, nullptr);
114  return false;
115  }
116  }
117 
118  // done
119  png_destroy_read_struct(&png, &info, nullptr);
120 
121  //
122  return true;
123 }
124 
125 bool PNGTextureReader::read(const vector<uint8_t>& pngData, ByteBuffer& textureByteBuffer) {
126  // see: http://devcry.heiho.net/html/2015/20150517-libpng.html
127 
128  // create PNG input stream
129  PNGInputStream pngInputStream(&pngData);
130 
131  // check that the PNG signature is in the file header
132  unsigned char sig[8];
133  pngInputStream.readBytes((int8_t*)sig, sizeof(sig));
134  if (png_sig_cmp(sig, 0, 8)) {
135  return false;
136  }
137 
138  // create two data structures: 'png_struct' and 'png_info'
139  auto png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
140  if (png == nullptr) {
141  return false;
142  }
143  auto info = png_create_info_struct(png);
144  if (info == nullptr) {
145  png_destroy_read_struct(&png, nullptr, nullptr);
146  return false;
147  }
148 
149  // set up custom read function
150  png_set_read_fn(png, &pngInputStream, PNGTextureReader::readDataFromMemory);
151 
152  // set libpng error handling mechanism
153  if (setjmp(png_jmpbuf(png))) {
154  png_destroy_read_struct(&png, &info, nullptr);
155  return false;
156  }
157 
158  // tell libpng we already read the signature
159  png_set_sig_bytes(png, sizeof(sig));
160 
161  // get image information
162  png_read_info(png, info);
163 
164  // dimensions
165  auto width = png_get_image_width(png, info);
166  auto height = png_get_image_height(png, info);
167 
168  // set one byte per channel
169  if (png_get_bit_depth(png, info) < 8) png_set_packing(png);
170  if (png_get_bit_depth(png, info) == 16) png_set_strip_16(png);
171 
172  // determine bytes per pixel
173  auto bytesPerPixel = -1;
174  switch(png_get_color_type(png, info)) {
175  case PNG_COLOR_TYPE_GRAY:
176  {
177  bytesPerPixel = 3;
178  png_set_gray_to_rgb(png);
179  break;
180  }
181  case PNG_COLOR_TYPE_GRAY_ALPHA:
182  {
183  bytesPerPixel = 4;
184  png_set_gray_to_rgb(png);
185  break;
186  }
187  case PNG_COLOR_TYPE_PALETTE:
188  {
189  // if transparency, convert it to alpha
190  bool alpha = false;
191  if (png_get_valid(png, info, PNG_INFO_tRNS)) {
192  alpha = true;
193  png_set_tRNS_to_alpha(png);
194  }
195  bytesPerPixel = alpha?4:3;
196  png_set_expand(png);
197  break;
198  }
199  case PNG_COLOR_TYPE_RGB:
200  {
201  bytesPerPixel = 3;
202  break;
203  }
204  case PNG_COLOR_TYPE_RGBA:
205  {
206  bytesPerPixel = 4;
207  break;
208  }
209  default:
210  {
211  png_destroy_read_struct(&png, &info, nullptr);
212  return false;
213  }
214  }
215 
216  // ... what ever :DDD
217  png_set_interlace_handling(png);
218  png_read_update_info(png, info);
219 
220  // read png
221  auto success = false;
222  auto rowPtrs = new(nothrow)png_bytep[height];
223  if (rowPtrs != nullptr) {
224  //
225  success = true;
226  // create pointers to each line beginning in texture byte buffer
227  auto bufferPtr = (uint8_t*)textureByteBuffer.getBuffer();
228  for(auto i = 0; i < height; i++) {
229  rowPtrs[i] = bufferPtr;
230  bufferPtr += width * bytesPerPixel;
231  }
232 
233  // read all rows (data goes into 'pixels' buffer)
234  // Note that any decoding errors will jump to the
235  // setjmp point and eventually return nullptr
236  png_read_image(png, rowPtrs);
237  png_read_end(png, nullptr);
238 
239  //
240  delete [] rowPtrs;
241  }
242 
243  // done
244  png_destroy_read_struct(&png, &info, nullptr);
245 
246  //
247  return success;
248 }
249 
250 Texture* PNGTextureReader::read(const string& textureId, const vector<uint8_t>& pngData, bool powerOfTwo, const string& idPrefix) {
251  int width;
252  int height;
253  uint8_t bytesPerPixel;
254  if (readHeader(pngData, width, height, bytesPerPixel) == false) {
255  Console::println("PNGTextureReader::read(): " + idPrefix + textureId + ": Could not read PNG header");
256  return nullptr;
257  }
258  // make width, height a power of 2
259  auto textureWidth = width;
260  auto textureHeight = height;
261  if (powerOfTwo == true) {
262  textureWidth = 1;
263  while (textureWidth < width) textureWidth*= 2;
264  textureHeight = 1;
265  while (textureHeight < height) textureHeight*= 2;
266  if (textureWidth != width || textureHeight != height) {
267  auto textureByteBuffer = ByteBuffer(width * height * bytesPerPixel);
268  if (read(pngData, textureByteBuffer) == false) {
269  Console::println("PNGTextureReader::read(): " + idPrefix + textureId + ": Could not read PNG bitmap data");
270  return nullptr;
271  }
272  Console::println("PNGTextureReader::read(): " + idPrefix + textureId + ": scaling to fit power of 2: " + to_string(width) + "x" + to_string(height) + " --> " + to_string(textureWidth) + "x" + to_string(textureHeight));
273  auto textureByteBufferScaled = ByteBuffer(textureWidth * textureHeight * bytesPerPixel);
274  auto textureYIncrement = (float)textureHeight / (float)height;
275  auto textureYPixelRest = 0.0f;
276  auto textureY = 0;
277  for (auto y = 0; y < height; y++) {
278  for (auto i = 0; i < (int)textureYIncrement + (int)textureYPixelRest; i++) {
279  TextureReader::scaleTextureLine(textureByteBuffer, textureByteBufferScaled, width, textureWidth, bytesPerPixel, y);
280  textureY++;
281  }
282  textureYPixelRest-= (int)textureYPixelRest;
283  textureYPixelRest+= textureYIncrement - (int)textureYIncrement;
284 
285  }
286  while (textureY < textureHeight) {
287  TextureReader::scaleTextureLine(textureByteBuffer, textureByteBufferScaled, width, textureWidth, bytesPerPixel, height - 1);
288  textureY++;
289  }
290  // thats it, return the scaled version
291  auto texture =
292  unique_ptr<
293  Texture,
294  decltype([](Texture* texture){ texture->releaseReference(); })
295  >(
296  new Texture(
297  idPrefix + textureId,
298  Texture::getRGBDepthByPixelBitsPerPixel(bytesPerPixel * 8),
299  Texture::getPNGFormatByPixelBitsPerPixel(bytesPerPixel * 8),
300  width,
301  height,
302  textureWidth,
303  textureHeight,
304  Texture::getRGBFormatByPixelBitsPerPixel(bytesPerPixel * 8),
305  textureByteBufferScaled
306  )
307  );
308  texture->acquireReference();
309  return texture.release();
310  }
311  }
312 
313  // thats it, return unscaled version
314  auto texture =
315  unique_ptr<
316  Texture,
317  decltype([](Texture* texture){ texture->releaseReference(); })
318  >(
319  new Texture(
320  idPrefix + textureId,
321  Texture::getRGBDepthByPixelBitsPerPixel(bytesPerPixel * 8),
322  Texture::getPNGFormatByPixelBitsPerPixel(bytesPerPixel * 8),
323  width,
324  height,
325  textureWidth,
326  textureHeight,
327  Texture::getPNGFormatByPixelBitsPerPixel(bytesPerPixel * 8),
328  ByteBuffer(pngData)
329  )
330  );
331  texture->acquireReference();
332  return texture.release();
333 }
334 
Texture entity.
Definition: Texture.h:24
static TextureFormat getRGBFormatByPixelBitsPerPixel(int bpp)
Return RGB/A texture format by bits per pixel.
Definition: Texture.h:72
static TextureFormat getPNGFormatByPixelBitsPerPixel(int bpp)
Return PNG RGB/A texture format by bits per pixel.
Definition: Texture.h:85
static TextureDepth getRGBDepthByPixelBitsPerPixel(int bpp)
Return RGB/A texture depth by bits per pixel.
Definition: Texture.h:59
void readBytes(int8_t *outBytes, int32_t outBytesToRead)
Read bytes.
static bool read(const vector< uint8_t > &pngData, ByteBuffer &textureByteBuffer)
Read PNG from memory into texture byte buffer.
static void readDataFromMemory(png_structp png_ptr, png_bytep outBytes, png_size_t outBytesToRead)
Read PNG data from memory.
static bool readHeader(const vector< uint8_t > &pngData, int &width, int &height, uint8_t &bytesPerPixel)
Read PNG header from memory.
static void scaleTextureLine(const ByteBuffer &pixelByteBuffer, ByteBuffer &pixelByteBufferScaled, int width, int textureWidth, int bytesPerPixel, int y)
Scales a texture line.
const uint8_t * getBuffer() const
Definition: Buffer.h:150
Byte buffer class.
Definition: ByteBuffer.h:27
Console class.
Definition: Console.h:29
virtual void releaseReference()
Releases a reference, thus decrementing the counter and delete it if reference counter is zero.
Definition: Reference.h:38
virtual void acquireReference()
Acquires a reference, incrementing the counter.
Definition: Reference.h:31