Dali 3D User Interface Engine
loader-png.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 #include "loader-png.h"
19 
20 #include <cstring>
21 #include <cstdlib>
22 
23 #include <zlib.h>
24 #include <png.h>
25 
31 #include "platform-capabilities.h"
32 
33 namespace Dali
34 {
35 
36 using Integration::Bitmap;
38 
39 namespace TizenPlatform
40 {
41 
42 namespace
43 {
44 
45 // simple class to enforce clean-up of PNG structures
46 struct auto_png
47 {
48  auto_png(png_structp& _png, png_infop& _info)
49  : png(_png),
50  info(_info)
51  {
52  }
53 
55  {
56  if(NULL != png)
57  {
58  png_destroy_read_struct(&png, &info, NULL);
59  }
60  }
61 
62  png_structp& png;
63  png_infop& info;
64 }; // struct auto_png;
65 
66 bool LoadPngHeader(FILE *fp, unsigned int &width, unsigned int &height, png_structp &png, png_infop &info)
67 {
68  png_byte header[8] = { 0 };
69 
70  // Check header to see if it is a PNG file
71  size_t size = fread(header, 1, 8, fp);
72  if(size != 8)
73  {
74  return false;
75  }
76 
77  if(png_sig_cmp(header, 0, 8))
78  {
79  return false;
80  }
81 
82  png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
83 
84  if(!png)
85  {
86  DALI_LOG_WARNING("Can't create PNG read structure\n");
87  return false;
88  }
89 
90  info = png_create_info_struct(png);
91  if(!info)
92  {
93  DALI_LOG_WARNING("png_create_info_struct failed\n");
94  return false;
95  }
96 
97  png_set_expand(png);
98 
99  if(setjmp(png_jmpbuf(png)))
100  {
101  DALI_LOG_WARNING("error during png_init_io\n");
102  return false;
103  }
104 
105  png_init_io(png, fp);
106  png_set_sig_bytes(png, 8);
107 
108  // read image info
109  png_read_info(png, info);
110 
111  // dimensions
112  width = png_get_image_width(png, info);
113  height = png_get_image_height(png, info);
114 
115  return true;
116 }
117 
118 } // namespace - anonymous
119 
120 bool LoadPngHeader( const ImageLoader::Input& input, unsigned int& width, unsigned int& height )
121 {
122  png_structp png = NULL;
123  png_infop info = NULL;
124  auto_png autoPng(png, info);
125 
126  bool success = LoadPngHeader( input.file, width, height, png, info );
127 
128  return success;
129 }
130 
132 {
133  png_structp png = NULL;
134  png_infop info = NULL;
135  auto_png autoPng(png, info);
136 
138  unsigned int y;
139  unsigned int width, height;
140  unsigned char *pixels;
141  png_bytep *rows;
142  unsigned int bpp = 0; // bytes per pixel
143  bool valid = false;
144 
145  // Load info from the header
146  if( !LoadPngHeader( input.file, width, height, png, info ) )
147  {
148  return false;
149  }
150 
151  Pixel::Format pixelFormat = Pixel::RGBA8888;
152 
153  // decide pixel format
154  unsigned int colordepth = png_get_bit_depth(png, info);
155 
156  // Ask PNGLib to convert high precision images into something we can use:
157  if (colordepth == 16)
158  {
159  png_set_strip_16(png);
160  colordepth = 8;
161  }
162 
163  png_byte colortype = png_get_color_type(png, info);
164 
165  if(colortype == PNG_COLOR_TYPE_GRAY)
166  {
167  switch( colordepth )
168  {
169  case 8:
170  {
171  pixelFormat = Pixel::L8;
172  valid = true;
173  break;
174  }
175  default:
176  {
177  break;
178  }
179  }
180  }
181  else if(colortype == PNG_COLOR_TYPE_GRAY_ALPHA)
182  {
183  switch(colordepth)
184  {
185  case 8:
186  {
187  pixelFormat = Pixel::LA88;
188  valid = true;
189  break;
190  }
191  default:
192  {
193  break;
194  }
195  }
196  }
197  else if(colortype == PNG_COLOR_TYPE_RGB )
198  {
199  switch(colordepth)
200  {
201  case 8:
202  {
203  pixelFormat = Pixel::RGB888;
204  valid = true;
205  break;
206  }
207  case 5:
208  {
209  pixelFormat = Pixel::RGB565;
210  valid = true;
211  break;
212  }
213  default:
214  {
215  break;
216  }
217  }
218  }
219  else if(colortype == PNG_COLOR_TYPE_RGBA)
220  {
221  switch(colordepth)
222  {
223  case 8:
224  {
225  pixelFormat = Pixel::RGBA8888;
226  valid = true;
227  break;
228  }
229  default:
230  {
231  break;
232  }
233  }
234  }
235  else if(colortype == PNG_COLOR_TYPE_PALETTE)
236  {
237  switch(colordepth)
238  {
239  case 1:
240  {
241  pixelFormat = Pixel::LA88;
242  valid = true;
243  break;
244  }
245 
246  case 2:
247  case 4:
248  case 8:
249  {
250  /* Expand paletted or RGB images with transparency to full alpha channels
251  * so the data will be available as RGBA quartets. PNG_INFO_tRNS = 0x10
252  */
253  if(png_get_valid(png, info, PNG_INFO_tRNS) == 0x10)
254  {
255  pixelFormat = Pixel::RGBA8888;
256  valid = true;
257  }
258  else
259  {
260  pixelFormat = Pixel::RGB888;
261  png_set_packing(png);
262  png_set_packswap(png);
263  png_set_palette_to_rgb(png);
264  valid = true;
265  }
266  break;
267  }
268  default:
269  {
270  break;
271  }
272  }
273  }
274 
275  if( !valid )
276  {
277  DALI_LOG_WARNING( "Unsupported png format\n" );
278  return false;
279  }
280 
281  // bytes per pixel
282  bpp = Pixel::GetBytesPerPixel(pixelFormat);
283 
284  png_read_update_info(png, info);
285 
286  if(setjmp(png_jmpbuf(png)))
287  {
288  DALI_LOG_WARNING("error during png_read_image\n");
289  return false;
290  }
291 
292  unsigned int rowBytes = png_get_rowbytes(png, info);
293 
294  unsigned int bufferWidth = GetTextureDimension(width);
295  unsigned int bufferHeight = GetTextureDimension(height);
296  unsigned int stride = bufferWidth*bpp;
297 
298  // not sure if this ever happens
299  if( rowBytes > stride )
300  {
301  stride = GetTextureDimension(rowBytes);
302  bufferWidth = stride / bpp;
303  }
304 
305  // decode the whole image into bitmap buffer
306  pixels = bitmap.GetPackedPixelsProfile()->ReserveBuffer(pixelFormat, width, height, bufferWidth, bufferHeight);
307 
308  DALI_ASSERT_DEBUG(pixels);
309  rows = (png_bytep*) malloc(sizeof(png_bytep) * height);
310  for(y=0; y<height; y++)
311  {
312  rows[y] = (png_byte*) (pixels + y * stride);
313  }
314 
315  // decode image
316  png_read_image(png, rows);
317 
318  free(rows);
319 
320  return true;
321 }
322 
323 // simple class to enforce clean-up of PNG structures
325 {
326  AutoPngWrite(png_structp& _png, png_infop& _info)
327  : png(_png),
328  info(_info)
329  {
330  DALI_ASSERT_DEBUG(&_png != 0 && &_info != 0);
331  }
332 
334  {
335  if(NULL != png)
336  {
337  png_destroy_write_struct(&png, &info);
338  }
339  }
340 
341  png_structp& png;
342  png_infop& info;
343 }; // struct AutoPngWrite;
344 
345 namespace
346 {
347  // Custom libpng write callbacks that buffer to a vector instead of a file:
348 
355  extern "C" void WriteData(png_structp png_ptr, png_bytep data, png_size_t length)
356  {
357  DALI_ASSERT_DEBUG(png_ptr && data);
358  if(!png_ptr || !data)
359  {
360  return;
361  }
362  // Make sure we don't try to propagate a C++ exception up the call stack of a pure C library:
363  try
364  {
365  // Recover our buffer for writing into:
366  Vector<unsigned char>* const encoded_img = static_cast< Vector<unsigned char>* >( png_get_io_ptr(png_ptr) );
367  if(encoded_img)
368  {
369  const Vector<unsigned char>::SizeType bufferSize = encoded_img->Count();
370  encoded_img->Reserve( bufferSize + length ); //< Can throw OOM.
371  unsigned char* const bufferBack = encoded_img->Begin() + bufferSize;
372  memcpy(bufferBack, data, length);
373  }
374  else
375  {
376  DALI_LOG_ERROR("PNG buffer for write to memory was passed from libpng as null.");
377  }
378  }
379  catch(...)
380  {
381  DALI_LOG_ERROR("C++ Exception caught");
382  }
383  }
384 
386  extern "C" void FlushData(png_structp png_ptr)
387  {
388 #ifdef DEBUG_ENABLED
389  Debug::LogMessage(Debug::DebugInfo, "PNG Flush");
390 #endif // DEBUG_ENABLED
391  }
392 }
393 
406 bool EncodeToPng( const unsigned char* const pixelBuffer, Vector<unsigned char>& encodedPixels, std::size_t width, std::size_t height, Pixel::Format pixelFormat )
407 {
408  // Translate pixel format enum:
409  int pngPixelFormat = -1;
410  unsigned pixelBytes = 0;
411  bool rgbaOrder = true;
412 
413  // Account for RGB versus BGR and presence of alpha in input pixels:
414  switch( pixelFormat )
415  {
416  case Pixel::RGB888:
417  {
418  pngPixelFormat = PNG_COLOR_TYPE_RGB;
419  pixelBytes = 3;
420  break;
421  }
422  case Pixel::BGRA8888:
423  {
424  rgbaOrder = false;
426  }
427  case Pixel::RGBA8888:
428  {
429  pngPixelFormat = PNG_COLOR_TYPE_RGB_ALPHA;
430  pixelBytes = 4;
431  break;
432  }
433  default:
434  {
435  DALI_LOG_ERROR( "Unsupported pixel format for encoding to PNG." );
436  return false;
437  }
438  }
439 
440  const int interlace = PNG_INTERLACE_NONE;
441 
442  png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
443  if(!png_ptr)
444  {
445  return false;
446  }
447  /* Allocate/initialize the image information data. REQUIRED */
448  png_infop info_ptr = png_create_info_struct( png_ptr );
449  if(!info_ptr)
450  {
451  png_destroy_write_struct(&png_ptr, NULL);
452  return false;
453  }
454 
455  /* Set error handling. REQUIRED if you aren't supplying your own
456  * error handling functions in the png_create_write_struct() call.
457  */
458  if(setjmp(png_jmpbuf(png_ptr)))
459  {
460  png_destroy_write_struct(&png_ptr, &info_ptr);
461  return false;
462  }
463 
464  // Since we are going to write to memory instead of a file, lets provide
465  // libpng with a custom write function and ask it to pass back our
466  // Vector buffer each time it calls back to flush data to "file":
467  png_set_write_fn(png_ptr, &encodedPixels, WriteData, FlushData);
468 
469  // png_set_compression_level( png_ptr, Z_BEST_COMPRESSION);
470  png_set_compression_level(png_ptr, Z_BEST_SPEED);
471  // png_set_compression_level( png_ptr, Z_NO_COMPRESSION); //! We could just generate png directly without libpng in this case.
472 
473  // Explicitly limit the number of filters used per scanline to speed us up:
474  // png_set_filter(png_ptr, 0, PNG_FILTER_NONE); ///!ToDo: Try this once baseline profile is in place.
475  // PNG_FILTER_SUB |
476  // PNG_FILTER_UP |
477  // PNG_FILTER_AVE |
478  // PNG_FILTER_PAETH |
479  // PNG_ALL_FILTERS);
480  // Play with Zlib parameters in optimisation phase:
481  // png_set_compression_mem_level(png_ptr, 8);
482  // png_set_compression_strategy(png_ptr,
483  // Z_DEFAULT_STRATEGY);
484  // png_set_compression_window_bits(png_ptr, 15);
485  // png_set_compression_method(png_ptr, 8);
486  // png_set_compression_buffer_size(png_ptr, 8192)
487 
488  // Let lib_png know if the pixel bytes are in BGR(A) order:
489  if(!rgbaOrder)
490  {
491  png_set_bgr( png_ptr );
492  }
493 
494  // Set the image information:
495  png_set_IHDR(png_ptr, info_ptr, width, height, 8,
496  pngPixelFormat, interlace,
497  PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
498 
499  // Start to output the PNG data to our buffer:
500  png_write_info(png_ptr, info_ptr);
501 
502  // Walk the rows:
503  const unsigned row_step = width * pixelBytes;
504  png_bytep row_ptr = const_cast<png_bytep>(pixelBuffer);
505  const png_bytep row_end = row_ptr + height * row_step;
506  for(; row_ptr < row_end; row_ptr += row_step)
507  {
508  png_write_row(png_ptr, row_ptr);
509  }
510 
511  /* It is REQUIRED to call this to finish writing the rest of the file */
512  png_write_end(png_ptr, info_ptr);
513  /* Clean up after the write, and free any memory allocated */
514  png_destroy_write_struct(&png_ptr, &info_ptr);
515  return true;
516 }
517 
518 } // namespace TizenPlatform
519 
520 } // namespace Dali
Dali Docs Home
Read more about Dali