1/*M///////////////////////////////////////////////////////////////////////////////////////
2//
3//  IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4//
5//  By downloading, copying, installing or using the software you agree to this license.
6//  If you do not agree to this license, do not download, install,
7//  copy or use the software.
8//
9//
10//                           License Agreement
11//                For Open Source Computer Vision Library
12//
13// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15// Third party copyrights are property of their respective owners.
16//
17// Redistribution and use in source and binary forms, with or without modification,
18// are permitted provided that the following conditions are met:
19//
20//   * Redistribution's of source code must retain the above copyright notice,
21//     this list of conditions and the following disclaimer.
22//
23//   * Redistribution's in binary form must reproduce the above copyright notice,
24//     this list of conditions and the following disclaimer in the documentation
25//     and/or other materials provided with the distribution.
26//
27//   * The name of the copyright holders may not be used to endorse or promote products
28//     derived from this software without specific prior written permission.
29//
30// This software is provided by the copyright holders and contributors "as is" and
31// any express or implied warranties, including, but not limited to, the implied
32// warranties of merchantability and fitness for a particular purpose are disclaimed.
33// In no event shall the Intel Corporation or contributors be liable for any direct,
34// indirect, incidental, special, exemplary, or consequential damages
35// (including, but not limited to, procurement of substitute goods or services;
36// loss of use, data, or profits; or business interruption) however caused
37// and on any theory of liability, whether in contract, strict liability,
38// or tort (including negligence or otherwise) arising in any way out of
39// the use of this software, even if advised of the possibility of such damage.
40//
41//M*/
42
43#include "precomp.hpp"
44
45#ifdef HAVE_PNG
46
47/****************************************************************************************\
48    This part of the file implements PNG codec on base of libpng library,
49    in particular, this code is based on example.c from libpng
50    (see otherlibs/_graphics/readme.txt for copyright notice)
51    and png2bmp sample from libpng distribution (Copyright (C) 1999-2001 MIYASAKA Masaru)
52\****************************************************************************************/
53
54#ifndef _LFS64_LARGEFILE
55#  define _LFS64_LARGEFILE 0
56#endif
57#ifndef _FILE_OFFSET_BITS
58#  define _FILE_OFFSET_BITS 0
59#endif
60
61#ifdef HAVE_LIBPNG_PNG_H
62#include <libpng/png.h>
63#else
64#include <png.h>
65#endif
66#include <zlib.h>
67
68#include "grfmt_png.hpp"
69
70#if defined _MSC_VER && _MSC_VER >= 1200
71    // interaction between '_setjmp' and C++ object destruction is non-portable
72    #pragma warning( disable: 4611 )
73#endif
74
75// the following defines are a hack to avoid multiple problems with frame ponter handling and setjmp
76// see http://gcc.gnu.org/ml/gcc/2011-10/msg00324.html for some details
77#define mingw_getsp(...) 0
78#define __builtin_frame_address(...) 0
79
80namespace cv
81{
82
83/////////////////////// PngDecoder ///////////////////
84
85PngDecoder::PngDecoder()
86{
87    m_signature = "\x89\x50\x4e\x47\xd\xa\x1a\xa";
88    m_color_type = 0;
89    m_png_ptr = 0;
90    m_info_ptr = m_end_info = 0;
91    m_f = 0;
92    m_buf_supported = true;
93    m_buf_pos = 0;
94}
95
96
97PngDecoder::~PngDecoder()
98{
99    close();
100}
101
102ImageDecoder PngDecoder::newDecoder() const
103{
104    return makePtr<PngDecoder>();
105}
106
107void  PngDecoder::close()
108{
109    if( m_f )
110    {
111        fclose( m_f );
112        m_f = 0;
113    }
114
115    if( m_png_ptr )
116    {
117        png_structp png_ptr = (png_structp)m_png_ptr;
118        png_infop info_ptr = (png_infop)m_info_ptr;
119        png_infop end_info = (png_infop)m_end_info;
120        png_destroy_read_struct( &png_ptr, &info_ptr, &end_info );
121        m_png_ptr = m_info_ptr = m_end_info = 0;
122    }
123}
124
125
126void  PngDecoder::readDataFromBuf( void* _png_ptr, uchar* dst, size_t size )
127{
128    png_structp png_ptr = (png_structp)_png_ptr;
129    PngDecoder* decoder = (PngDecoder*)(png_get_io_ptr(png_ptr));
130    CV_Assert( decoder );
131    const Mat& buf = decoder->m_buf;
132    if( decoder->m_buf_pos + size > buf.cols*buf.rows*buf.elemSize() )
133    {
134        png_error(png_ptr, "PNG input buffer is incomplete");
135        return;
136    }
137    memcpy( dst, decoder->m_buf.ptr() + decoder->m_buf_pos, size );
138    decoder->m_buf_pos += size;
139}
140
141bool  PngDecoder::readHeader()
142{
143    bool result = false;
144    close();
145
146    png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
147
148    if( png_ptr )
149    {
150        png_infop info_ptr = png_create_info_struct( png_ptr );
151        png_infop end_info = png_create_info_struct( png_ptr );
152
153        m_png_ptr = png_ptr;
154        m_info_ptr = info_ptr;
155        m_end_info = end_info;
156        m_buf_pos = 0;
157
158        if( info_ptr && end_info )
159        {
160            if( setjmp( png_jmpbuf( png_ptr ) ) == 0 )
161            {
162                if( !m_buf.empty() )
163                    png_set_read_fn(png_ptr, this, (png_rw_ptr)readDataFromBuf );
164                else
165                {
166                    m_f = fopen( m_filename.c_str(), "rb" );
167                    if( m_f )
168                        png_init_io( png_ptr, m_f );
169                }
170
171                if( !m_buf.empty() || m_f )
172                {
173                    png_uint_32 wdth, hght;
174                    int bit_depth, color_type, num_trans=0;
175                    png_bytep trans;
176                    png_color_16p trans_values;
177
178                    png_read_info( png_ptr, info_ptr );
179
180                    png_get_IHDR( png_ptr, info_ptr, &wdth, &hght,
181                                  &bit_depth, &color_type, 0, 0, 0 );
182
183                    m_width = (int)wdth;
184                    m_height = (int)hght;
185                    m_color_type = color_type;
186                    m_bit_depth = bit_depth;
187
188                    if( bit_depth <= 8 || bit_depth == 16 )
189                    {
190                        switch(color_type)
191                        {
192                            case PNG_COLOR_TYPE_RGB:
193                                m_type = CV_8UC3;
194                                break;
195                            case PNG_COLOR_TYPE_PALETTE:
196                                png_get_tRNS( png_ptr, info_ptr, &trans, &num_trans, &trans_values);
197                                //Check if there is a transparency value in the palette
198                                if ( num_trans > 0 )
199                                    m_type = CV_8UC4;
200                                else
201                                    m_type = CV_8UC3;
202                                break;
203                            case PNG_COLOR_TYPE_RGB_ALPHA:
204                                m_type = CV_8UC4;
205                                break;
206                            default:
207                                m_type = CV_8UC1;
208                        }
209                        if( bit_depth == 16 )
210                            m_type = CV_MAKETYPE(CV_16U, CV_MAT_CN(m_type));
211                        result = true;
212                    }
213                }
214            }
215        }
216    }
217
218    if( !result )
219        close();
220
221    return result;
222}
223
224
225bool  PngDecoder::readData( Mat& img )
226{
227    bool result = false;
228    AutoBuffer<uchar*> _buffer(m_height);
229    uchar** buffer = _buffer;
230    int color = img.channels() > 1;
231    uchar* data = img.ptr();
232    int step = (int)img.step;
233
234    if( m_png_ptr && m_info_ptr && m_end_info && m_width && m_height )
235    {
236        png_structp png_ptr = (png_structp)m_png_ptr;
237        png_infop info_ptr = (png_infop)m_info_ptr;
238        png_infop end_info = (png_infop)m_end_info;
239
240        if( setjmp( png_jmpbuf ( png_ptr ) ) == 0 )
241        {
242            int y;
243
244            if( img.depth() == CV_8U && m_bit_depth == 16 )
245                png_set_strip_16( png_ptr );
246            else if( !isBigEndian() )
247                png_set_swap( png_ptr );
248
249            if(img.channels() < 4)
250            {
251                /* observation: png_read_image() writes 400 bytes beyond
252                 * end of data when reading a 400x118 color png
253                 * "mpplus_sand.png".  OpenCV crashes even with demo
254                 * programs.  Looking at the loaded image I'd say we get 4
255                 * bytes per pixel instead of 3 bytes per pixel.  Test
256                 * indicate that it is a good idea to always ask for
257                 * stripping alpha..  18.11.2004 Axel Walthelm
258                 */
259                 png_set_strip_alpha( png_ptr );
260            }
261
262            if( m_color_type == PNG_COLOR_TYPE_PALETTE )
263                png_set_palette_to_rgb( png_ptr );
264
265            if( m_color_type == PNG_COLOR_TYPE_GRAY && m_bit_depth < 8 )
266#if (PNG_LIBPNG_VER_MAJOR*10000 + PNG_LIBPNG_VER_MINOR*100 + PNG_LIBPNG_VER_RELEASE >= 10209) || \
267    (PNG_LIBPNG_VER_MAJOR == 1 && PNG_LIBPNG_VER_MINOR == 0 && PNG_LIBPNG_VER_RELEASE >= 18)
268                png_set_expand_gray_1_2_4_to_8( png_ptr );
269#else
270                png_set_gray_1_2_4_to_8( png_ptr );
271#endif
272
273            if( CV_MAT_CN(m_type) > 1 && color )
274                png_set_bgr( png_ptr ); // convert RGB to BGR
275            else if( color )
276                png_set_gray_to_rgb( png_ptr ); // Gray->RGB
277            else
278                png_set_rgb_to_gray( png_ptr, 1, 0.299, 0.587 ); // RGB->Gray
279
280            png_set_interlace_handling( png_ptr );
281            png_read_update_info( png_ptr, info_ptr );
282
283            for( y = 0; y < m_height; y++ )
284                buffer[y] = data + y*step;
285
286            png_read_image( png_ptr, buffer );
287            png_read_end( png_ptr, end_info );
288
289            result = true;
290        }
291    }
292
293    close();
294    return result;
295}
296
297
298/////////////////////// PngEncoder ///////////////////
299
300
301PngEncoder::PngEncoder()
302{
303    m_description = "Portable Network Graphics files (*.png)";
304    m_buf_supported = true;
305}
306
307
308PngEncoder::~PngEncoder()
309{
310}
311
312
313bool  PngEncoder::isFormatSupported( int depth ) const
314{
315    return depth == CV_8U || depth == CV_16U;
316}
317
318ImageEncoder PngEncoder::newEncoder() const
319{
320    return makePtr<PngEncoder>();
321}
322
323
324void PngEncoder::writeDataToBuf(void* _png_ptr, uchar* src, size_t size)
325{
326    if( size == 0 )
327        return;
328    png_structp png_ptr = (png_structp)_png_ptr;
329    PngEncoder* encoder = (PngEncoder*)(png_get_io_ptr(png_ptr));
330    CV_Assert( encoder && encoder->m_buf );
331    size_t cursz = encoder->m_buf->size();
332    encoder->m_buf->resize(cursz + size);
333    memcpy( &(*encoder->m_buf)[cursz], src, size );
334}
335
336
337void PngEncoder::flushBuf(void*)
338{
339}
340
341bool  PngEncoder::write( const Mat& img, const std::vector<int>& params )
342{
343    png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, 0, 0, 0 );
344    png_infop info_ptr = 0;
345    FILE* f = 0;
346    int y, width = img.cols, height = img.rows;
347    int depth = img.depth(), channels = img.channels();
348    bool result = false;
349    AutoBuffer<uchar*> buffer;
350
351    if( depth != CV_8U && depth != CV_16U )
352        return false;
353
354    if( png_ptr )
355    {
356        info_ptr = png_create_info_struct( png_ptr );
357
358        if( info_ptr )
359        {
360            if( setjmp( png_jmpbuf ( png_ptr ) ) == 0 )
361            {
362                if( m_buf )
363                {
364                    png_set_write_fn(png_ptr, this,
365                        (png_rw_ptr)writeDataToBuf, (png_flush_ptr)flushBuf);
366                }
367                else
368                {
369                    f = fopen( m_filename.c_str(), "wb" );
370                    if( f )
371                        png_init_io( png_ptr, f );
372                }
373
374                int compression_level = -1; // Invalid value to allow setting 0-9 as valid
375                int compression_strategy = Z_RLE; // Default strategy
376                bool isBilevel = false;
377
378                for( size_t i = 0; i < params.size(); i += 2 )
379                {
380                    if( params[i] == CV_IMWRITE_PNG_COMPRESSION )
381                    {
382                        compression_level = params[i+1];
383                        compression_level = MIN(MAX(compression_level, 0), Z_BEST_COMPRESSION);
384                    }
385                    if( params[i] == CV_IMWRITE_PNG_STRATEGY )
386                    {
387                        compression_strategy = params[i+1];
388                        compression_strategy = MIN(MAX(compression_strategy, 0), Z_FIXED);
389                    }
390                    if( params[i] == CV_IMWRITE_PNG_BILEVEL )
391                    {
392                        isBilevel = params[i+1] != 0;
393                    }
394                }
395
396                if( m_buf || f )
397                {
398                    if( compression_level >= 0 )
399                    {
400                        png_set_compression_level( png_ptr, compression_level );
401                    }
402                    else
403                    {
404                        // tune parameters for speed
405                        // (see http://wiki.linuxquestions.org/wiki/Libpng)
406                        png_set_filter(png_ptr, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);
407                        png_set_compression_level(png_ptr, Z_BEST_SPEED);
408                    }
409                    png_set_compression_strategy(png_ptr, compression_strategy);
410
411                    png_set_IHDR( png_ptr, info_ptr, width, height, depth == CV_8U ? isBilevel?1:8 : 16,
412                        channels == 1 ? PNG_COLOR_TYPE_GRAY :
413                        channels == 3 ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA,
414                        PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT,
415                        PNG_FILTER_TYPE_DEFAULT );
416
417                    png_write_info( png_ptr, info_ptr );
418
419                    if (isBilevel)
420                        png_set_packing(png_ptr);
421
422                    png_set_bgr( png_ptr );
423                    if( !isBigEndian() )
424                        png_set_swap( png_ptr );
425
426                    buffer.allocate(height);
427                    for( y = 0; y < height; y++ )
428                        buffer[y] = img.data + y*img.step;
429
430                    png_write_image( png_ptr, buffer );
431                    png_write_end( png_ptr, info_ptr );
432
433                    result = true;
434                }
435            }
436        }
437    }
438
439    png_destroy_write_struct( &png_ptr, &info_ptr );
440    if(f) fclose( f );
441
442    return result;
443}
444
445}
446
447#endif
448
449/* End of file. */
450