image_io_png.cpp
上传用户:yhdzpy8989
上传日期:2007-06-13
资源大小:13604k
文件大小:18k
源码类别:

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: image_io_png.cpp,v $
  4.  * PRODUCTION Revision 1000.3  2004/06/01 19:41:37  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.5
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: image_io_png.cpp,v 1000.3 2004/06/01 19:41:37 gouriano Exp $
  10.  * ===========================================================================
  11.  *
  12.  *                            PUBLIC DOMAIN NOTICE
  13.  *               National Center for Biotechnology Information
  14.  *
  15.  *  This software/database is a "United States Government Work" under the
  16.  *  terms of the United States Copyright Act.  It was written as part of
  17.  *  the author's official duties as a United States Government employee and
  18.  *  thus cannot be copyrighted.  This software/database is freely available
  19.  *  to the public for use. The National Library of Medicine and the U.S.
  20.  *  Government have not placed any restriction on its use or reproduction.
  21.  *
  22.  *  Although all reasonable efforts have been taken to ensure the accuracy
  23.  *  and reliability of the software and data, the NLM and the U.S.
  24.  *  Government do not and cannot warrant the performance or results that
  25.  *  may be obtained by using this software or data. The NLM and the U.S.
  26.  *  Government disclaim all warranties, express or implied, including
  27.  *  warranties of performance, merchantability or fitness for any particular
  28.  *  purpose.
  29.  *
  30.  *  Please cite the author in any work or product based on this material.
  31.  *
  32.  * ===========================================================================
  33.  *
  34.  * Authors:  Mike DiCuccio
  35.  *
  36.  * File Description:
  37.  *    CImageIOPng -- interface class for reading/writing PNG files
  38.  */
  39. #include <ncbi_pch.hpp>
  40. #include "image_io_png.hpp"
  41. #include <util/image/image.hpp>
  42. #include <util/image/image_exception.hpp>
  43. #ifdef HAVE_LIBPNG
  44. //
  45. //
  46. // PNG SUPPORT
  47. //
  48. //
  49. // libpng main header
  50. #include <png.h>
  51. BEGIN_NCBI_SCOPE
  52. //
  53. // internal message handler for PNG images
  54. //
  55. static void s_PngReadErrorHandler(png_structp png_ptr, png_const_charp msg)
  56. {
  57.     string str("Error reading PNG file: ");
  58.     str += msg;
  59.     NCBI_THROW(CImageException, eReadError, str);
  60. }
  61. //
  62. // internal message handler for PNG images
  63. //
  64. static void s_PngWriteErrorHandler(png_structp png_ptr, png_const_charp msg)
  65. {
  66.     string str("Error writing PNG file: ");
  67.     str += msg;
  68.     NCBI_THROW(CImageException, eWriteError, str);
  69. }
  70. //
  71. // internal handler: translate PNG warnings to NCBI warnings
  72. //
  73. static void s_PngWarningHandler(png_structp png_ptr, png_const_charp msg)
  74. {
  75.     LOG_POST(Warning << "Warning in PNG file: " << msg);
  76. }
  77. //
  78. // initialize PNG reading
  79. //
  80. static void s_PngReadInit(png_structp& png_ptr,
  81.                           png_infop&   info_ptr,
  82.                           png_infop&   end_info_ptr)
  83. {
  84.     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,
  85.                                      s_PngReadErrorHandler,
  86.                                      s_PngWarningHandler);
  87.     if ( !png_ptr ) {
  88.         NCBI_THROW(CImageException, eReadError,
  89.                    "CImageIOPng::ReadImage(): png_create_read_struct() failed");
  90.     }
  91.     info_ptr = png_create_info_struct(png_ptr);
  92.     if ( !info_ptr ) {
  93.         NCBI_THROW(CImageException, eReadError,
  94.                    "CImageIOPng::ReadImage(): png_create_info_struct() failed");
  95.     }
  96.     end_info_ptr = png_create_info_struct(png_ptr);
  97.     if ( !end_info_ptr ) {
  98.         NCBI_THROW(CImageException, eReadError,
  99.                    "CImageIOPng::ReadImage(): png_create_info_struct() failed");
  100.     }
  101. }
  102. //
  103. // initialize PNG writing
  104. //
  105. static void s_PngWriteInit(png_structp& png_ptr,
  106.                            png_infop&   info_ptr,
  107.                            const CImage& image,
  108.                            CImageIO::ECompress compress)
  109. {
  110.     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL,
  111.                                       s_PngWriteErrorHandler,
  112.                                       s_PngWarningHandler);
  113.     if ( !png_ptr ) {
  114.         NCBI_THROW(CImageException, eWriteError,
  115.                    "CImageIOPng::WriteImage(): png_create_read_struct() failed");
  116.     }
  117.     info_ptr = png_create_info_struct(png_ptr);
  118.     if ( !info_ptr ) {
  119.         NCBI_THROW(CImageException, eWriteError,
  120.                    "CImageIOPng::WriteImage(): png_create_info_struct() failed");
  121.     }
  122.     png_byte color_type = PNG_COLOR_TYPE_RGB;
  123.     if (image.GetDepth() == 4) {
  124.         color_type = PNG_COLOR_TYPE_RGBA;
  125.     }
  126.     png_set_IHDR(png_ptr, info_ptr,
  127.                  image.GetWidth(), image.GetHeight(), 8, color_type,
  128.                  PNG_INTERLACE_NONE,
  129.                  PNG_COMPRESSION_TYPE_BASE,
  130.                  PNG_FILTER_TYPE_BASE);
  131.     // set our compression quality
  132.     switch (compress) {
  133.     case CImageIO::eCompress_None:
  134.         png_set_compression_level(png_ptr, Z_NO_COMPRESSION);
  135.         break;
  136.     case CImageIO::eCompress_Low:
  137.         png_set_compression_level(png_ptr, Z_BEST_SPEED);
  138.         break;
  139.     case CImageIO::eCompress_Medium:
  140.         png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION);
  141.         break;
  142.     case CImageIO::eCompress_High:
  143.         png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
  144.         break;
  145.     default:
  146.         LOG_POST(Error << "unknown compression type: " << (int)compress);
  147.         break;
  148.     }
  149. }
  150. //
  151. // validate our input
  152. //
  153. static void s_PngReadValidate(png_structp png_ptr,
  154.                               png_infop info_ptr,
  155.                               size_t& width,
  156.                               size_t& height,
  157.                               size_t& depth,
  158.                               size_t& x, size_t& y, size_t& w, size_t& h)
  159. {
  160.     // store and validate our image's parameters
  161.     width        = info_ptr->width;
  162.     height       = info_ptr->height;
  163.     depth        = info_ptr->channels;
  164.     png_byte color_type = info_ptr->color_type;
  165.     png_byte bit_depth  = info_ptr->bit_depth;
  166.     // we support only RGB and RGBA images
  167.     if ( color_type != PNG_COLOR_TYPE_RGB  &&
  168.          color_type != PNG_COLOR_TYPE_RGB_ALPHA ) {
  169.         string msg("CImageIOPng::ReadImage(): unhandled color type: ");
  170.         msg += NStr::IntToString((int)color_type);
  171.         NCBI_THROW(CImageException, eReadError, msg);
  172.     }
  173.     // ...and only with a bit depth of 8
  174.     if (bit_depth != 8) {
  175.         string msg("CImageIOPng::ReadImage(): unhandled bit depth: ");
  176.         msg += NStr::IntToString((int)bit_depth);
  177.         NCBI_THROW(CImageException, eReadError, msg);
  178.     }
  179.     // this goes along with RGB or RGBA
  180.     if (depth != 3  &&  depth != 4) {
  181.         string msg("CImageIOPng::ReadImage(): unhandled image channels: ");
  182.         msg += NStr::IntToString((int)depth);
  183.         NCBI_THROW(CImageException, eReadError, msg);
  184.     }
  185.     if (x != -1  &&  y != -1  &&  w != -1  &&  h != -1) {
  186.         // further validation: make sure we're actually on the image
  187.         if (x >= width  ||  y >= height) {
  188.             string msg("CImageIOPng::ReadImage(): invalid starting position: ");
  189.             msg += NStr::IntToString(x);
  190.             msg += ", ";
  191.             msg += NStr::IntToString(y);
  192.             NCBI_THROW(CImageException, eReadError, msg);
  193.         }
  194.         // clamp our width and height to the image size
  195.         if (x + w >= width) {
  196.             w = width - x;
  197.             LOG_POST(Warning
  198.                      << "CImageIOPng::ReadImage(): clamped width to " << w);
  199.         }
  200.         if (y + h >= height) {
  201.             h = height - y;
  202.             LOG_POST(Warning
  203.                      << "CImageIOPng::ReadImage(): clamped height to " << h);
  204.         }
  205.     }
  206.     png_read_update_info(png_ptr, info_ptr);
  207. }
  208. //
  209. // our local i/o handlers
  210. //
  211. static void s_PngRead(png_structp png_ptr, png_bytep data, png_size_t len)
  212. {
  213.     CNcbiIfstream* istr =
  214.         reinterpret_cast<CNcbiIfstream*>(png_get_io_ptr(png_ptr));
  215.     if (istr) {
  216.         istr->read(reinterpret_cast<char*>(data), len);
  217.     }
  218. }
  219. static void s_PngWrite(png_structp png_ptr, png_bytep data, png_size_t len)
  220. {
  221.     CNcbiOfstream* ostr =
  222.         reinterpret_cast<CNcbiOfstream*>(png_get_io_ptr(png_ptr));
  223.     if (ostr) {
  224.         ostr->write(reinterpret_cast<char*>(data), len);
  225.     }
  226. }
  227. static void s_PngFlush(png_structp png_ptr)
  228. {
  229.     CNcbiOfstream* ostr =
  230.         reinterpret_cast<CNcbiOfstream*>(png_get_io_ptr(png_ptr));
  231.     if (ostr) {
  232.         ostr->flush();
  233.     }
  234. }
  235. //
  236. // finalize our structures
  237. //
  238. static void s_PngReadFinalize(png_structp& png_ptr,
  239.                               png_infop&   info_ptr,
  240.                               png_infop&   end_info_ptr)
  241. {
  242.     png_destroy_read_struct(&png_ptr, &info_ptr, &end_info_ptr);
  243. }
  244. static void s_PngWriteFinalize(png_structp& png_ptr,
  245.                               png_infop&    info_ptr)
  246. {
  247.     png_destroy_write_struct(&png_ptr, &info_ptr);
  248. }
  249. //
  250. // ReadImage()
  251. // read a PNG format image into memory, returning the image itself
  252. // This version reads the entire image
  253. //
  254. CImage* CImageIOPng::ReadImage(CNcbiIstream& istr)
  255. {
  256.     png_structp png_ptr  = NULL;
  257.     png_infop   info_ptr = NULL;
  258.     png_infop   end_ptr  = NULL;
  259.     CRef<CImage> image;
  260.     try {
  261.         // create our PNG structures
  262.         s_PngReadInit(png_ptr, info_ptr, end_ptr);
  263.         // begin reading our image
  264.         png_set_read_fn(png_ptr, &istr, s_PngRead);
  265.         png_read_info(png_ptr, info_ptr);
  266.         // store and validate our image's parameters
  267.         size_t width  = 0;
  268.         size_t height = 0;
  269.         size_t depth  = 0;
  270.         size_t x = -1;
  271.         size_t y = -1;
  272.         size_t w = -1;
  273.         size_t h = -1;
  274.         s_PngReadValidate(png_ptr, info_ptr,
  275.                           width, height, depth,
  276.                           x, y, w, h);
  277.         // allocate our image and read, line by line
  278.         image.Reset(new CImage(width, height, depth));
  279.         unsigned char* row_ptr = image->SetData();
  280.         for (size_t i = 0;  i < height;  ++i) {
  281.             png_read_row(png_ptr, row_ptr, NULL);
  282.             row_ptr += width * depth;
  283.         }
  284.         // read the end pointer
  285.         png_read_end(png_ptr, end_ptr);
  286.         // close and return
  287.         s_PngReadFinalize(png_ptr, info_ptr, end_ptr);
  288.     }
  289.     catch (...) {
  290.         // destroy everything
  291.         s_PngReadFinalize(png_ptr, info_ptr, end_ptr);
  292.         // rethrow
  293.         throw;
  294.     }
  295.     return image.Release();
  296. }
  297. //
  298. // ReadImage()
  299. // read a PNG format image into memory, returning the image itself
  300. // This version reads a subpart of an image
  301. //
  302. CImage* CImageIOPng::ReadImage(CNcbiIstream& istr,
  303.                                size_t x, size_t y, size_t w, size_t h)
  304. {
  305.     png_structp png_ptr  = NULL;
  306.     png_infop   info_ptr = NULL;
  307.     png_infop   end_ptr  = NULL;
  308.     CRef<CImage> image;
  309.     try {
  310.         // create our PNG structures
  311.         s_PngReadInit(png_ptr, info_ptr, end_ptr);
  312.         // begin reading our image
  313.         png_set_read_fn(png_ptr, &istr, s_PngRead);
  314.         png_read_info(png_ptr, info_ptr);
  315.         // store and validate our image's parameters
  316.         size_t width  = 0;
  317.         size_t height = 0;
  318.         size_t depth  = 0;
  319.         s_PngReadValidate(png_ptr, info_ptr, width, height, depth,
  320.                           x, y, w, h);
  321.         // allocate our image
  322.         image.Reset(new CImage(w, h, depth));
  323.         unsigned char* to_data   = image->SetData();
  324.         size_t         to_stride = image->GetWidth() * image->GetDepth();
  325.         size_t         to_offs   = x * image->GetDepth();
  326.         // allocate a scanline for this image
  327.         vector<unsigned char> row(width * depth);
  328.         unsigned char* row_ptr = &row[0];
  329.         size_t i;
  330.         // read up to our starting scan line
  331.         for (i = 0;  i < y;  ++i) {
  332.             png_read_row(png_ptr, row_ptr, NULL);
  333.         }
  334.         // read and convert the scanlines of interest
  335.         for ( ;  i < y + h;  ++i) {
  336.             png_read_row(png_ptr, row_ptr, NULL);
  337.             memcpy(to_data, row_ptr + to_offs, to_stride);
  338.             to_data += to_stride;
  339.         }
  340.         /**
  341.         // read the rest of the image (do we need this?)
  342.         for ( ;  i < height;  ++i) {
  343.             png_read_row(png_ptr, row_ptr, NULL);
  344.         }
  345.         // read the end pointer
  346.         png_read_end(png_ptr, end_ptr);
  347.         **/
  348.         // close and return
  349.         s_PngReadFinalize(png_ptr, info_ptr, end_ptr);
  350.     }
  351.     catch (...) {
  352.         // destroy everything
  353.         s_PngReadFinalize(png_ptr, info_ptr, end_ptr);
  354.         // rethrow
  355.         throw;
  356.     }
  357.     return image.Release();
  358. }
  359. //
  360. // WriteImage()
  361. // write an image to a file in PNG format
  362. // This version writes the entire image
  363. //
  364. void CImageIOPng::WriteImage(const CImage& image, CNcbiOstream& ostr,
  365.                              CImageIO::ECompress compress)
  366. {
  367.     // make sure we've got an image
  368.     if ( !image.GetData() ) {
  369.         NCBI_THROW(CImageException, eWriteError,
  370.                    "CImageIOPng::WriteImage(): "
  371.                    "attempt to write an empty image");
  372.     }
  373.     // validate our image - we need RGB or RGBA images
  374.     if (image.GetDepth() != 3  &&  image.GetDepth() != 4) {
  375.         string msg("CImageIOPng::WriteImage(): invalid image depth: ");
  376.         msg += NStr::IntToString(image.GetDepth());
  377.         NCBI_THROW(CImageException, eWriteError, msg);
  378.     }
  379.     png_structp png_ptr  = NULL;
  380.     png_infop   info_ptr = NULL;
  381.     try {
  382.         // initialize png stuff
  383.         s_PngWriteInit(png_ptr, info_ptr, image, compress);
  384.         // begin writing data
  385.         png_set_write_fn(png_ptr, &ostr, s_PngWrite, s_PngFlush);
  386.         png_write_info(png_ptr, info_ptr);
  387.         // write our image, line-by-line
  388.         unsigned char* row_ptr = const_cast<unsigned char*> (image.GetData());
  389.         size_t width  = image.GetWidth();
  390.         size_t height = image.GetHeight();
  391.         size_t depth  = image.GetDepth();
  392.         for (size_t i = 0;  i < height;  ++i) {
  393.             png_write_row(png_ptr, row_ptr);
  394.             row_ptr += width * depth;
  395.         }
  396.         // standard clean-up
  397.         png_write_end(png_ptr, info_ptr);
  398.         s_PngWriteFinalize(png_ptr, info_ptr);
  399.     }
  400.     catch (...) {
  401.         s_PngWriteFinalize(png_ptr, info_ptr);
  402.         throw;
  403.     }
  404. }
  405. //
  406. // WriteImage()
  407. // write an image to a file in PNG format
  408. // This version writes only a subpart of the image
  409. //
  410. void CImageIOPng::WriteImage(const CImage& image, CNcbiOstream& ostr,
  411.                              size_t x, size_t y, size_t w, size_t h,
  412.                              CImageIO::ECompress compress)
  413. {
  414.     // make sure we've got an image
  415.     if ( !image.GetData() ) {
  416.         NCBI_THROW(CImageException, eWriteError,
  417.                    "CImageIOPng::WriteImage(): "
  418.                    "attempt to write an empty image");
  419.     }
  420.     // validate our image - we need RGB or RGBA images
  421.     if (image.GetDepth() != 3  &&  image.GetDepth() != 4) {
  422.         string msg("CImageIOPng::WriteImage(): invalid image depth: ");
  423.         msg += NStr::IntToString(image.GetDepth());
  424.         NCBI_THROW(CImageException, eWriteError, msg);
  425.     }
  426.     png_structp png_ptr  = NULL;
  427.     png_infop   info_ptr = NULL;
  428.     try {
  429.         // initialize png stuff
  430.         s_PngWriteInit(png_ptr, info_ptr, image, compress);
  431.         // begin writing data
  432.         png_set_write_fn(png_ptr, &ostr, s_PngWrite, s_PngFlush);
  433.         png_write_info(png_ptr, info_ptr);
  434.         // write our image
  435.         // we plan to march through only part of our image
  436.         // get a pointer to the start of our scan line
  437.         //
  438.         // NB: the const cast is necessary as png_write_row takes a non-const
  439.         // pointer (go figure...)
  440.         unsigned char* from_data = const_cast<unsigned char*>(image.GetData());
  441.         from_data += (y * image.GetWidth() + x) * image.GetDepth();
  442.         size_t from_stride = image.GetWidth() * image.GetDepth();
  443.         // march out h scan lines
  444.         for (size_t i = 0;  i < h;  ++i) {
  445.             png_write_row(png_ptr, from_data);
  446.             from_data += from_stride;
  447.         }
  448.         // standard clean-up
  449.         png_write_end(png_ptr, info_ptr);
  450.         s_PngWriteFinalize(png_ptr, info_ptr);
  451.     }
  452.     catch (...) {
  453.         s_PngWriteFinalize(png_ptr, info_ptr);
  454.         throw;
  455.     }
  456. }
  457. END_NCBI_SCOPE
  458. #else // !HAVE_LIBPNG
  459. //
  460. // LIBPNG functionality not included - we stub out the various needed
  461. // functions
  462. //
  463. BEGIN_NCBI_SCOPE
  464. CImage* CImageIOPng::ReadImage(CNcbiIstream& file)
  465. {
  466.     NCBI_THROW(CImageException, eUnsupported,
  467.                "CImageIOPng::ReadImage(): PNG format not supported");
  468. }
  469. CImage* CImageIOPng::ReadImage(CNcbiIstream& file,
  470.                                size_t, size_t, size_t, size_t)
  471. {
  472.     NCBI_THROW(CImageException, eUnsupported,
  473.                "CImageIOPng::ReadImage(): PNG format not supported");
  474. }
  475. void CImageIOPng::WriteImage(const CImage& image, CNcbiOstream& file,
  476.                              CImageIO::ECompress)
  477. {
  478.     NCBI_THROW(CImageException, eUnsupported,
  479.                "CImageIOPng::WriteImage(): PNG format not supported");
  480. }
  481. void CImageIOPng::WriteImage(const CImage& image, CNcbiOstream& file,
  482.                              size_t, size_t, size_t, size_t,
  483.                              CImageIO::ECompress)
  484. {
  485.     NCBI_THROW(CImageException, eUnsupported,
  486.                "CImageIOPng::WriteImage(): PNG format not supported");
  487. }
  488. END_NCBI_SCOPE
  489. #endif  // HAVE_LIBPNG
  490. /*
  491.  * ===========================================================================
  492.  * $Log: image_io_png.cpp,v $
  493.  * Revision 1000.3  2004/06/01 19:41:37  gouriano
  494.  * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.5
  495.  *
  496.  * Revision 1.5  2004/05/17 21:07:58  gorelenk
  497.  * Added include of PCH ncbi_pch.hpp
  498.  *
  499.  * Revision 1.4  2003/12/20 17:50:53  dicuccio
  500.  * Fixed bugs in reading of subimage - don't declare image in try block; use
  501.  * correct height.  Dropped unnecessary finalization - don't need to scan whole
  502.  * image.
  503.  *
  504.  * Revision 1.3  2003/12/16 15:49:37  dicuccio
  505.  * Large re-write of image handling.  Added improved error-handling and support
  506.  * for streams-based i/o (via hooks into each client library).
  507.  *
  508.  * Revision 1.2  2003/11/03 15:19:57  dicuccio
  509.  * Added optional compression parameter
  510.  *
  511.  * Revision 1.1  2003/06/03 15:17:13  dicuccio
  512.  * Initial revision of image library
  513.  *
  514.  * ===========================================================================
  515.  */