home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / wxos2240.zip / wxWindows-2.4.0 / src / common / imagpng.cpp < prev    next >
C/C++ Source or Header  |  2002-11-04  |  13KB  |  434 lines

  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name:        imagepng.cpp
  3. // Purpose:     wxImage PNG handler
  4. // Author:      Robert Roebling
  5. // RCS-ID:      $Id: imagpng.cpp,v 1.27.2.2 2002/10/29 21:47:45 RR Exp $
  6. // Copyright:   (c) Robert Roebling
  7. // Licence:     wxWindows licence
  8. /////////////////////////////////////////////////////////////////////////////
  9.  
  10. #ifdef __GNUG__
  11. #pragma implementation "imagpng.h"
  12. #endif
  13.  
  14. // For compilers that support precompilation, includes "wx.h".
  15. #include "wx/wxprec.h"
  16.  
  17. #ifdef __BORLANDC__
  18.   #pragma hdrstop
  19. #endif
  20.  
  21. #ifndef WX_PRECOMP
  22.   #include "wx/defs.h"
  23. #endif
  24.  
  25. #if wxUSE_IMAGE && wxUSE_LIBPNG
  26.  
  27. #include "wx/imagpng.h"
  28. #include "wx/bitmap.h"
  29. #include "wx/debug.h"
  30. #include "wx/log.h"
  31. #include "wx/app.h"
  32. #include "png.h"
  33. #include "wx/filefn.h"
  34. #include "wx/wfstream.h"
  35. #include "wx/intl.h"
  36. #include "wx/module.h"
  37.  
  38. // For memcpy
  39. #include <string.h>
  40.  
  41. #ifdef __SALFORDC__
  42. #ifdef FAR
  43. #undef FAR
  44. #endif
  45. #endif
  46.  
  47. #ifdef __WXMSW__
  48. #include <windows.h>
  49. #endif
  50.  
  51. //-----------------------------------------------------------------------------
  52. // wxPNGHandler
  53. //-----------------------------------------------------------------------------
  54.  
  55. IMPLEMENT_DYNAMIC_CLASS(wxPNGHandler,wxImageHandler)
  56.  
  57. #if wxUSE_LIBPNG
  58.  
  59. #ifndef PNGLINKAGEMODE
  60.   #define PNGLINKAGEMODE LINKAGEMODE
  61. #endif
  62.  
  63.  
  64. // VS: wxPNGInfoStruct declared below is a hack that needs some explanation.
  65. //     First, let me describe what's the problem: libpng uses jmp_buf in 
  66. //     its png_struct structure. Unfortunately, this structure is 
  67. //     compiler-specific and may vary in size, so if you use libpng compiled 
  68. //     as DLL with another compiler than the main executable, it may not work
  69. //     (this is for example the case with wxMGL port and SciTech MGL library 
  70. //     that provides custom runtime-loadable libpng implementation with jmpbuf
  71. //     disabled altogether). Luckily, it is still possible to use setjmp() & 
  72. //     longjmp() as long as the structure is not part of png_struct.
  73. //
  74. //     Sadly, there's no clean way to attach user-defined data to png_struct.
  75. //     There is only one customizable place, png_struct.io_ptr, which is meant
  76. //     only for I/O routines and is set with png_set_read_fn or 
  77. //     png_set_write_fn. The hacky part is that we use io_ptr to store
  78. //     a pointer to wxPNGInfoStruct that holds I/O structures _and_ jmp_buf.
  79.  
  80. struct wxPNGInfoStruct
  81. {
  82.     jmp_buf jmpbuf;
  83.     bool verbose;
  84.     
  85.     union
  86.     {
  87.         wxInputStream  *in;
  88.         wxOutputStream *out;
  89.     } stream;
  90. };
  91.  
  92. #define WX_PNG_INFO(png_ptr) ((wxPNGInfoStruct*)png_get_io_ptr(png_ptr))
  93.  
  94.  
  95. extern "C"
  96. {
  97.  
  98. void PNGLINKAGEMODE _PNG_stream_reader( png_structp png_ptr, png_bytep data, png_size_t length )
  99. {
  100.     WX_PNG_INFO(png_ptr)->stream.in->Read(data, length);
  101. }
  102.  
  103. void PNGLINKAGEMODE _PNG_stream_writer( png_structp png_ptr, png_bytep data, png_size_t length )
  104. {
  105.     WX_PNG_INFO(png_ptr)->stream.out->Write(data, length);
  106. }
  107.  
  108. // from pngerror.c
  109. // so that the libpng doesn't send anything on stderr
  110. void
  111. PNGLINKAGEMODE wx_png_error(png_structp png_ptr, png_const_charp message)
  112. {
  113.     wxPNGInfoStruct *info = WX_PNG_INFO(png_ptr);
  114.     if (info->verbose)
  115.         wxLogError( wxString::FromAscii(message) );
  116.  
  117. #ifdef USE_FAR_KEYWORD
  118.     {
  119.        jmp_buf jmpbuf;
  120.        png_memcpy(jmpbuf,info->jmpbuf,sizeof(jmp_buf));
  121.        longjmp(jmpbuf, 1);
  122.     }
  123. #else
  124.     longjmp(info->jmpbuf, 1);
  125. #endif
  126. }
  127.  
  128. void
  129. PNGLINKAGEMODE wx_png_warning(png_structp png_ptr, png_const_charp message)
  130. {
  131.     wxPNGInfoStruct *info = WX_PNG_INFO(png_ptr);
  132.     if (info->verbose)
  133.         wxLogWarning( wxString::FromAscii(message) );
  134. }
  135.  
  136. } // extern "C"
  137.  
  138. // temporarily disable the warning C4611 (interaction between '_setjmp' and
  139. // C++ object destruction is non-portable) - I don't see any dtors here
  140. #ifdef __VISUALC__
  141.     #pragma warning(disable:4611)
  142. #endif /* VC++ */
  143.  
  144. bool wxPNGHandler::LoadFile( wxImage *image, wxInputStream& stream, bool verbose, int WXUNUSED(index) )
  145. {
  146.     // VZ: as this function uses setjmp() the only fool proof error handling
  147.     //     method is to use goto (setjmp is not really C++ dtors friendly...)
  148.  
  149.     unsigned char **lines;
  150.     unsigned int i;
  151.     png_infop info_ptr = (png_infop) NULL;   
  152.     wxPNGInfoStruct wxinfo;
  153.  
  154.     wxinfo.verbose = verbose;
  155.     wxinfo.stream.in = &stream;
  156.  
  157.     image->Destroy();
  158.  
  159.     png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING,
  160.         (voidp) NULL,
  161.         (png_error_ptr) NULL,
  162.         (png_error_ptr) NULL );
  163.     if (!png_ptr)
  164.         goto error_nolines;
  165.  
  166.     png_set_error_fn(png_ptr, (png_voidp)NULL, wx_png_error, wx_png_warning);
  167.  
  168.     // NB: please see the comment near wxPNGInfoStruct declaration for
  169.     //     explanation why this line is mandatory
  170.     png_set_read_fn( png_ptr, &wxinfo, _PNG_stream_reader);
  171.  
  172.     info_ptr = png_create_info_struct( png_ptr );
  173.     if (!info_ptr)
  174.         goto error_nolines;
  175.  
  176.     if (setjmp(wxinfo.jmpbuf))
  177.         goto error_nolines;
  178.  
  179.     if (info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
  180.         goto error_nolines;
  181.  
  182.     png_uint_32 width,height;
  183.     int bit_depth,color_type,interlace_type;
  184.  
  185.     png_read_info( png_ptr, info_ptr );
  186.     png_get_IHDR( png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, (int*) NULL, (int*) NULL );
  187.  
  188.     if (color_type == PNG_COLOR_TYPE_PALETTE)
  189.         png_set_expand( png_ptr );
  190.  
  191.     // Fix for Bug [ 439207 ] Monochrome PNG images come up black
  192.     if (bit_depth < 8)
  193.         png_set_expand( png_ptr );
  194.  
  195.     png_set_strip_16( png_ptr );
  196.     png_set_packing( png_ptr );
  197.     if (png_get_valid( png_ptr, info_ptr, PNG_INFO_tRNS))
  198.         png_set_expand( png_ptr );
  199.     png_set_filler( png_ptr, 0xff, PNG_FILLER_AFTER );
  200.  
  201.     image->Create( (int)width, (int)height );
  202.  
  203.     if (!image->Ok())
  204.         goto error_nolines;
  205.  
  206.     lines = (unsigned char **)malloc( (size_t)(height * sizeof(unsigned char *)) );
  207.     if (lines == NULL)
  208.         goto error_nolines;
  209.  
  210.     for (i = 0; i < height; i++)
  211.     {
  212.         if ((lines[i] = (unsigned char *)malloc( (size_t)(width * (sizeof(unsigned char) * 4)))) == NULL)
  213.         {
  214.             for ( unsigned int n = 0; n < i; n++ )
  215.                 free( lines[n] );
  216.             goto error;
  217.         }
  218.     }
  219.  
  220.     // loaded successfully!
  221.     {
  222.         int transp = 0;
  223.         png_read_image( png_ptr, lines );
  224.         png_read_end( png_ptr, info_ptr );
  225.         png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL );
  226.         unsigned char *ptr = image->GetData();
  227.         if ((color_type == PNG_COLOR_TYPE_GRAY) ||
  228.             (color_type == PNG_COLOR_TYPE_GRAY_ALPHA))
  229.         {
  230.             for (unsigned int y = 0; y < height; y++)
  231.             {
  232.                 unsigned char *ptr2 = lines[y];
  233.                 for (unsigned int x = 0; x < width; x++)
  234.                 {
  235.                     unsigned char r = *ptr2++;
  236.                     unsigned char a = *ptr2++;
  237.                     if (a < 128)
  238.                     {
  239.                         *ptr++ = 255;
  240.                         *ptr++ = 0;
  241.                         *ptr++ = 255;
  242.                         transp = 1;
  243.                     }
  244.                     else
  245.                     {
  246.                         *ptr++ = r;
  247.                         *ptr++ = r;
  248.                         *ptr++ = r;
  249.                     }
  250.                 }
  251.             }
  252.         }
  253.         else
  254.         {
  255.             for (unsigned int y = 0; y < height; y++)
  256.             {
  257.                 unsigned char *ptr2 = lines[y];
  258.                 for (unsigned int x = 0; x < width; x++)
  259.                 {
  260.                     unsigned char r = *ptr2++;
  261.                     unsigned char g = *ptr2++;
  262.                     unsigned char b = *ptr2++;
  263.                     unsigned char a = *ptr2++;
  264.                     if (a < 128)
  265.                     {
  266.                         *ptr++ = 255;
  267.                         *ptr++ = 0;
  268.                         *ptr++ = 255;
  269.                         transp = 1;
  270.                     }
  271.                     else
  272.                     {
  273.                         if ((r == 255) && (g == 0) && (b == 255)) r = 254;
  274.                         *ptr++ = r;
  275.                         *ptr++ = g;
  276.                         *ptr++ = b;
  277.                     }
  278.                 }
  279.             }
  280.         }
  281.  
  282.         for ( unsigned int j = 0; j < height; j++ )
  283.             free( lines[j] );
  284.         free( lines );
  285.  
  286.         if (transp)
  287.         {
  288.             image->SetMaskColour( 255, 0, 255 );
  289.         }
  290.         else
  291.         {
  292.             image->SetMask( FALSE );
  293.         }
  294.     }
  295.  
  296.     return TRUE;
  297.  
  298.  error_nolines:
  299.     lines = NULL; // called from before it was set
  300.  error:
  301.     if (verbose)
  302.        wxLogError(_("Couldn't load a PNG image - file is corrupted or not enough memory."));
  303.  
  304.     if ( image->Ok() )
  305.     {
  306.         image->Destroy();
  307.     }
  308.  
  309.     if ( lines )
  310.     {
  311.         free( lines );
  312.     }
  313.  
  314.     if ( png_ptr )
  315.     {
  316.         if ( info_ptr )
  317.         {
  318.             png_destroy_read_struct( &png_ptr, &info_ptr, (png_infopp) NULL );
  319.             free(info_ptr);
  320.         }
  321.         else
  322.             png_destroy_read_struct( &png_ptr, (png_infopp) NULL, (png_infopp) NULL );
  323.     }
  324.     return FALSE;
  325. }
  326.  
  327. bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose )
  328. {
  329.     wxPNGInfoStruct wxinfo;
  330.  
  331.     wxinfo.verbose = verbose;
  332.     wxinfo.stream.out = &stream;
  333.  
  334.     png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  335.     if (!png_ptr)
  336.     {
  337.         if (verbose)
  338.            wxLogError(_("Couldn't save PNG image."));
  339.         return FALSE;
  340.     }
  341.  
  342.     png_set_error_fn(png_ptr, (png_voidp)NULL, wx_png_error, wx_png_warning);
  343.  
  344.     png_infop info_ptr = png_create_info_struct(png_ptr);
  345.     if (info_ptr == NULL)
  346.     {
  347.         png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
  348.         if (verbose)
  349.            wxLogError(_("Couldn't save PNG image."));
  350.         return FALSE;
  351.     }
  352.  
  353.     if (setjmp(wxinfo.jmpbuf))
  354.     {
  355.         png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
  356.         if (verbose)
  357.            wxLogError(_("Couldn't save PNG image."));
  358.         return FALSE;
  359.     }
  360.  
  361.     // NB: please see the comment near wxPNGInfoStruct declaration for
  362.     //     explanation why this line is mandatory
  363.     png_set_write_fn( png_ptr, &wxinfo, _PNG_stream_writer, NULL);
  364.  
  365.     png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), 8,
  366.         PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
  367.         PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
  368.  
  369.     png_color_8 sig_bit;
  370.     sig_bit.red = 8;
  371.     sig_bit.green = 8;
  372.     sig_bit.blue = 8;
  373.     sig_bit.alpha = 8;
  374.     png_set_sBIT( png_ptr, info_ptr, &sig_bit );
  375.     png_write_info( png_ptr, info_ptr );
  376.     png_set_shift( png_ptr, &sig_bit );
  377.     png_set_packing( png_ptr );
  378.  
  379.     unsigned char *data = (unsigned char *)malloc( image->GetWidth()*4 );
  380.     if (!data)
  381.     {
  382.         png_destroy_write_struct( &png_ptr, (png_infopp)NULL );
  383.         return FALSE;
  384.     }
  385.  
  386.     for (int y = 0; y < image->GetHeight(); y++)
  387.     {
  388.         unsigned char *ptr = image->GetData() + (y * image->GetWidth() * 3);
  389.         for (int x = 0; x < image->GetWidth(); x++)
  390.         {
  391.             data[(x << 2) + 0] = *ptr++;
  392.             data[(x << 2) + 1] = *ptr++;
  393.             data[(x << 2) + 2] = *ptr++;
  394.             if (( !image->HasMask() ) || \
  395.                 (data[(x << 2) + 0] != image->GetMaskRed()) || \
  396.                 (data[(x << 2) + 1] != image->GetMaskGreen()) || \
  397.                 (data[(x << 2) + 2] != image->GetMaskBlue()))
  398.             {
  399.                 data[(x << 2) + 3] = 255;
  400.             }
  401.             else
  402.             {
  403.                 data[(x << 2) + 3] = 0;
  404.             }
  405.         }
  406.         png_bytep row_ptr = data;
  407.         png_write_rows( png_ptr, &row_ptr, 1 );
  408.     }
  409.  
  410.     free(data);
  411.     png_write_end( png_ptr, info_ptr );
  412.     png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr );
  413.  
  414.     return TRUE;
  415. }
  416.  
  417. #ifdef __VISUALC__
  418.     #pragma warning(default:4611)
  419. #endif /* VC++ */
  420.  
  421. bool wxPNGHandler::DoCanRead( wxInputStream& stream )
  422. {
  423.     unsigned char hdr[4];
  424.  
  425.     if ( !stream.Read(hdr, WXSIZEOF(hdr)) )
  426.         return FALSE;
  427.  
  428.     return memcmp(hdr, "\211PNG", WXSIZEOF(hdr)) == 0;
  429. }
  430.  
  431. #endif  // wxUSE_STREAMS
  432.  
  433. #endif  // wxUSE_LIBPNG
  434.