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

生物技术

开发平台:

C/C++

  1. /*
  2.  * ===========================================================================
  3.  * PRODUCTION $Log: cn3d_png.cpp,v $
  4.  * PRODUCTION Revision 1000.3  2004/06/01 18:28:18  gouriano
  5.  * PRODUCTION PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.21
  6.  * PRODUCTION
  7.  * ===========================================================================
  8.  */
  9. /*  $Id: cn3d_png.cpp,v 1000.3 2004/06/01 18:28:18 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:  Paul Thiessen
  35. *
  36. * File Description:
  37. *      save structure window to PNG file
  38. *
  39. * ===========================================================================
  40. */
  41. #ifdef _MSC_VER
  42. #pragma warning(disable:4018)   // disable signed/unsigned mismatch warning in MSVC
  43. #endif
  44. #include <ncbi_pch.hpp>
  45. #include <corelib/ncbistd.hpp>
  46. #include <corelib/ncbi_limits.h>
  47. // need GL headers for off-screen rendering
  48. #if defined(__WXMSW__)
  49. #include <windows.h>
  50. #include <GL/gl.h>
  51. #include <GL/glu.h>
  52. #elif defined(__WXGTK__)
  53. #include <GL/gl.h>
  54. #include <GL/glu.h>
  55. #include <GL/glx.h>
  56. #include <gdk/gdkx.h>
  57. #elif defined(__WXMAC__)
  58. #include <OpenGL/gl.h>
  59. #include <OpenGL/glu.h>
  60. #endif
  61. #include <wx/platform.h>
  62. #if defined(__WXGTK__) && defined(__LINUX__)
  63. // use system headers/libs for linux builds
  64. #include <png.h>
  65. #else
  66. // otherwise, use libs built into wxWindows
  67. #include "../cn3d/png.h"
  68. #endif
  69. #include "cn3d_png.hpp"
  70. #include "cn3d_glcanvas.hpp"
  71. #include "opengl_renderer.hpp"
  72. #include "progress_meter.hpp"
  73. #include "cn3d_tools.hpp"
  74. #include "messenger.hpp"
  75. ////////////////////////////////////////////////////////////////////////////////////////////////
  76. // The following is taken unmodified from wxDesigner's C++ code from png_dialog.wdr
  77. ////////////////////////////////////////////////////////////////////////////////////////////////
  78. #include <wx/image.h>
  79. #include <wx/statline.h>
  80. #include <wx/spinbutt.h>
  81. #include <wx/spinctrl.h>
  82. #include <wx/splitter.h>
  83. #include <wx/listctrl.h>
  84. #include <wx/treectrl.h>
  85. #include <wx/notebook.h>
  86. #include <wx/grid.h>
  87. // Declare window functions
  88. #define ID_TEXT 10000
  89. #define ID_B_BROWSE 10001
  90. #define ID_T_NAME 10002
  91. #define ID_T_WIDTH 10003
  92. #define ID_T_HEIGHT 10004
  93. #define ID_C_ASPECT 10005
  94. #define ID_C_INTERLACE 10006
  95. #define ID_B_OK 10007
  96. #define ID_B_CANCEL 10008
  97. wxSizer *SetupPNGOptionsDialog( wxPanel *parent, bool call_fit = TRUE, bool set_sizer = TRUE );
  98. ////////////////////////////////////////////////////////////////////////////////////////////////
  99. // fix jmpbuf symbol problem on aix
  100. #if defined(_AIX43) && defined(jmpbuf)
  101. #undef jmpbuf
  102. #endif
  103. USING_NCBI_SCOPE;
  104. BEGIN_SCOPE(Cn3D)
  105. static ProgressMeter *progressMeter = NULL;
  106. static const int PROGRESS_RESOLUTION = 100, MAX_BUFFER_PIXELS = 1000000;
  107. static int nRows;
  108. class PNGOptionsDialog : private wxDialog
  109. {
  110. public:
  111.     PNGOptionsDialog(wxWindow *parent);
  112.     bool Activate(int initialWidth, int initialHeight, bool initialInterlaced);
  113.     bool GetValues(wxString *outputFilename, int *width, int *height, bool *interlaced);
  114. private:
  115.     double initialAspect;
  116.     bool dontProcessChange;
  117.     // event callbacks
  118.     void OnButton(wxCommandEvent& event);
  119.     void OnChangeSize(wxCommandEvent& event);
  120.     void OnCheckbox(wxCommandEvent& event);
  121.     void OnCloseWindow(wxCloseEvent& event);
  122.     DECLARE_EVENT_TABLE()
  123. };
  124. #define DECLARE_AND_FIND_WINDOW_RETURN_ON_ERR(var, id, type) 
  125.     type *var; 
  126.     var = wxDynamicCast(FindWindow(id), type); 
  127.     if (!var) { 
  128.         ERRORMSG("Can't find window with id " << id); 
  129.         return; 
  130.     }
  131. #define DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(var, id, type, errResult) 
  132.     type *var; 
  133.     var = wxDynamicCast(FindWindow(id), type); 
  134.     if (!var) { 
  135.         ERRORMSG("Can't find window with id " << id); 
  136.         return errResult; 
  137.     }
  138. // to make sure values are legitimate integers; use wxString::ToDouble to avoid parsing
  139. // strings as octal or hexadecimal...
  140. #define GET_AND_IS_VALID_SIZE(textctrl, var) 
  141.     (textctrl->GetValue().ToDouble(&var) && var >= 1 && fmod(var, 1.0) == 0.0 && var <= kMax_Int)
  142. BEGIN_EVENT_TABLE(PNGOptionsDialog, wxDialog)
  143.     EVT_BUTTON      (-1,    PNGOptionsDialog::OnButton)
  144.     EVT_TEXT        (-1,    PNGOptionsDialog::OnChangeSize)
  145.     EVT_CHECKBOX    (-1,    PNGOptionsDialog::OnCheckbox)
  146.     EVT_CLOSE       (       PNGOptionsDialog::OnCloseWindow)
  147. END_EVENT_TABLE()
  148. PNGOptionsDialog::PNGOptionsDialog(wxWindow *parent) :
  149.     wxDialog(parent, -1, "Export Options", wxPoint(50, 50), wxDefaultSize,
  150.         wxCAPTION | wxSYSTEM_MENU | wxFRAME_NO_TASKBAR), // not resizable
  151.     dontProcessChange(false)
  152. {
  153.     // construct the panel
  154.     wxPanel *panel = new wxPanel(this, -1);
  155.     wxSizer *topSizer = SetupPNGOptionsDialog(panel, false);
  156.     // call sizer stuff
  157.     topSizer->Fit(panel);
  158.     SetClientSize(topSizer->GetMinSize());
  159. }
  160. void PNGOptionsDialog::OnButton(wxCommandEvent& event)
  161. {
  162.     switch (event.GetId()) {
  163.         case ID_B_BROWSE: {
  164.             DECLARE_AND_FIND_WINDOW_RETURN_ON_ERR(tName, ID_T_NAME, wxTextCtrl)
  165.             wxString filename = wxFileSelector(
  166.                 "Choose a filename for output", GetUserDir().c_str(), "",
  167.                 ".png", "All Files|*.*|PNG files (*.png)|*.png",
  168.                 wxSAVE | wxOVERWRITE_PROMPT);
  169.             if (filename.size() > 0) tName->SetValue(filename);
  170.             break;
  171.         }
  172.         case ID_B_OK: {
  173.             wxString str;
  174.             int w, h;
  175.             bool i;
  176.             if (GetValues(&str, &w, &h, &i))
  177.                 EndModal(wxOK);
  178.             else
  179.                 wxBell();
  180.             break;
  181.         }
  182.         case ID_B_CANCEL:
  183.             EndModal(wxCANCEL);
  184.             break;
  185.     }
  186. }
  187. void PNGOptionsDialog::OnChangeSize(wxCommandEvent& event)
  188. {
  189.     // avoid recursive calls to this, since SetValue() triggers this event; only process size fields
  190.     if (dontProcessChange || !(event.GetId() == ID_T_WIDTH || event.GetId() == ID_T_HEIGHT)) return;
  191.     DECLARE_AND_FIND_WINDOW_RETURN_ON_ERR(tWidth, ID_T_WIDTH, wxTextCtrl)
  192.     DECLARE_AND_FIND_WINDOW_RETURN_ON_ERR(tHeight, ID_T_HEIGHT, wxTextCtrl)
  193.     DECLARE_AND_FIND_WINDOW_RETURN_ON_ERR(cAspect, ID_C_ASPECT, wxCheckBox)
  194.     DECLARE_AND_FIND_WINDOW_RETURN_ON_ERR(cInterlace, ID_C_INTERLACE, wxCheckBox)
  195.     // post refreshes for both, in case background color changes
  196.     tWidth->Refresh(true);
  197.     tHeight->Refresh(true);
  198.     double w, h;
  199.     if (!GET_AND_IS_VALID_SIZE(tWidth, w)) {
  200.         tWidth->SetBackgroundColour(*wxRED);
  201.         return;
  202.     }
  203.     tWidth->SetBackgroundColour(*wxWHITE);
  204.     if (!GET_AND_IS_VALID_SIZE(tHeight, h)) {
  205.         tHeight->SetBackgroundColour(*wxRED);
  206.         return;
  207.     }
  208.     tHeight->SetBackgroundColour(*wxWHITE);
  209.     // adjust to aspect ratio if indicated
  210.     dontProcessChange = true;
  211.     if (cAspect->GetValue()) {
  212.         wxString num;
  213.         if (event.GetId() == ID_T_WIDTH) {
  214.             h = floor(w / initialAspect + 0.5);
  215.             num.Printf("%i", (int) h);
  216.             tHeight->SetValue(num);
  217.         } else {
  218.             w = floor(h * initialAspect + 0.5);
  219.             num.Printf("%i", (int) w);
  220.             tWidth->SetValue(num);
  221.         }
  222.     }
  223.     dontProcessChange = false;
  224.     // only allow interlacing if image is small enough
  225.     if (w * h > MAX_BUFFER_PIXELS) {
  226.         if (cInterlace->IsEnabled()) {
  227.             cInterlace->Enable(false);
  228.             cInterlace->SetValue(false);
  229.         }
  230.     } else {
  231.         if (!cInterlace->IsEnabled()) {
  232.             cInterlace->Enable(true);
  233.             cInterlace->SetValue(true);
  234.         }
  235.     }
  236. }
  237. void PNGOptionsDialog::OnCheckbox(wxCommandEvent& event)
  238. {
  239.     if (event.GetId() == ID_C_ASPECT) {
  240.         // adjust height when aspect checkbox is turned on
  241.         DECLARE_AND_FIND_WINDOW_RETURN_ON_ERR(cAspect, ID_C_ASPECT, wxCheckBox)
  242.         if (cAspect->GetValue()) {
  243.             DECLARE_AND_FIND_WINDOW_RETURN_ON_ERR(tWidth, ID_T_WIDTH, wxTextCtrl)
  244.             DECLARE_AND_FIND_WINDOW_RETURN_ON_ERR(tHeight, ID_T_HEIGHT, wxTextCtrl)
  245.             double w, h;
  246.             if (GET_AND_IS_VALID_SIZE(tWidth, w)) {
  247.                 wxString num;
  248.                 h = floor(w / initialAspect + 0.5);
  249.                 num.Printf("%i", (int) h);
  250.                 tHeight->SetValue(num);
  251.             }
  252.         }
  253.     }
  254. }
  255. void PNGOptionsDialog::OnCloseWindow(wxCloseEvent& event)
  256. {
  257.     EndModal(wxCANCEL);
  258. }
  259. bool PNGOptionsDialog::Activate(int initialWidth, int initialHeight, bool initialInterlaced)
  260. {
  261.     DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(tName, ID_T_NAME, wxTextCtrl, false)
  262.     DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(tWidth, ID_T_WIDTH, wxTextCtrl, false)
  263.     DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(tHeight, ID_T_HEIGHT, wxTextCtrl, false)
  264.     DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(cAspect, ID_C_ASPECT, wxCheckBox, false)
  265.     DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(cInterlace, ID_C_INTERLACE, wxCheckBox, false)
  266.     dontProcessChange = true;
  267.     wxString num;
  268.     num.Printf("%i", initialWidth);
  269.     tWidth->SetValue(num);
  270.     num.Printf("%i", initialHeight);
  271.     tHeight->SetValue(num);
  272.     initialAspect = ((double) initialWidth) / initialHeight;
  273.     cAspect->SetValue(true);
  274.     dontProcessChange = false;
  275.     if (initialWidth*initialHeight > MAX_BUFFER_PIXELS) {
  276.         cInterlace->Enable(false);
  277.         cInterlace->SetValue(false);
  278.     } else
  279.         cInterlace->SetValue(initialInterlaced);
  280.     // bring up file selector first
  281.     wxCommandEvent browse(wxEVT_COMMAND_BUTTON_CLICKED, ID_B_BROWSE);
  282.     OnButton(browse);
  283.     if (tName->GetValue().size() == 0) return false;    // cancelled
  284.     // return dialog result
  285.     return (ShowModal() == wxOK);
  286. }
  287. bool PNGOptionsDialog::GetValues(wxString *outputFilename, int *width, int *height, bool *interlaced)
  288. {
  289.     DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(tName, ID_T_NAME, wxTextCtrl, false)
  290.     DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(tWidth, ID_T_WIDTH, wxTextCtrl, false)
  291.     DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(tHeight, ID_T_HEIGHT, wxTextCtrl, false)
  292.     DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(cInterlace, ID_C_INTERLACE, wxCheckBox, false)
  293.     *outputFilename = tName->GetValue();
  294.     double val;
  295.     if (!GET_AND_IS_VALID_SIZE(tWidth, val)) return false;
  296.     *width = (int) val;
  297.     if (!GET_AND_IS_VALID_SIZE(tHeight, val)) return false;
  298.     *height = (int) val;
  299.     *interlaced = (cInterlace->IsEnabled() && cInterlace->GetValue());
  300.     return true;
  301. }
  302. static bool GetOutputParameters(wxString *outputFilename, int *width, int *height, bool *interlaced)
  303. {
  304.     PNGOptionsDialog dialog(NULL);
  305.     bool ok = dialog.Activate(*width, *height, *interlaced);
  306.     if (ok) dialog.GetValues(outputFilename, width, height, interlaced);
  307.     return ok;
  308. }
  309. // callback used by PNG library to report errors
  310. static void writepng_error_handler(png_structp png_ptr, png_const_charp msg)
  311. {
  312.     ERRORMSG("PNG library error: " << msg);
  313. }
  314. #ifdef __WXGTK__
  315. static bool gotAnXError;
  316. int X_error_handler(Display *dpy, XErrorEvent *error)
  317. {
  318.     gotAnXError = true;
  319.     return 0;
  320. }
  321. #endif
  322. // called after each row is written to the file
  323. static void write_row_callback(png_structp png_ptr, png_uint_32 row, int pass)
  324. {
  325.     if (!progressMeter) return;
  326.     int progress = 0;
  327.     if (nRows < 0) { /* if negative, then we're doing interlacing */
  328.         double start, end;
  329.         switch (pass + 1) {
  330.             case 1: start = 0;  end = 1;  break;
  331.             case 2: start = 1;  end = 2;  break;
  332.             case 3: start = 2;  end = 3;  break;
  333.             case 4: start = 3;  end = 5;  break;
  334.             case 5: start = 5;  end = 7;  break;
  335.             case 6: start = 7;  end = 11; break;
  336.             case 7: start = 11; end = 15; break;
  337.             default: return;    // png lib gives final pass=7,row=0 to signal completion
  338.         }
  339.         progress = (int) (1.0 * PROGRESS_RESOLUTION *
  340.             ((start / 15) + (((double) row) / (-nRows)) * ((end - start) / 15)));
  341.     } else { /* not interlaced */
  342.         progress = (int) (1.0 * PROGRESS_RESOLUTION * row / nRows);
  343.     }
  344.     progressMeter->SetValue(progress);
  345. }
  346. bool ExportPNG(Cn3DGLCanvas *glCanvas)
  347. {
  348. #if !defined(__WXMSW__) && !defined(__WXGTK__) && !defined(__WXMAC__)
  349.     ERRORMSG("PNG export not (yet) implemented on this platform");
  350.     return false;
  351. #endif
  352.     if (!glCanvas || !glCanvas->renderer) {
  353.         ERRORMSG("ExportPNG() - bad glCanvas parameter");
  354.         return false;
  355.     }
  356.     bool success = false, shareDisplayLists = true, interlaced = true;
  357.     int outputWidth, outputHeight, bufferHeight, bytesPerPixel = 3, nChunks = 1;
  358.     wxString filename;
  359.     FILE *out = NULL;
  360.     unsigned char *rowStorage = NULL;
  361.     png_structp png_ptr = NULL;
  362.     png_infop info_ptr = NULL;
  363.     outputWidth = glCanvas->GetClientSize().GetWidth(); // initial size
  364.     outputHeight = glCanvas->GetClientSize().GetHeight();
  365.     if (!GetOutputParameters(&filename, &outputWidth, &outputHeight, &interlaced))
  366.         return true; // cancelled
  367.     try {
  368.         INFOMSG("saving PNG file '" << filename.c_str() << "'");
  369.         // need to avoid any GL calls in glCanvas while off-screen rendering is happening; so
  370.         // temporarily prevent glCanvas from responding to window resize/exposure, etc.
  371.         glCanvas->SuspendRendering(true);
  372.         int windowViewport[4];
  373.         glCanvas->renderer->GetViewport(windowViewport);
  374.         TRACEMSG("window viewport: x,y: " << windowViewport[0] << ',' << windowViewport[1]
  375.             << " size: " << windowViewport[2] << ',' << windowViewport[3]);
  376.         INFOMSG("output size: " << outputWidth << ',' << outputHeight);
  377.         // decide whether the in-memory image buffer can fit the whole drawing,
  378.         // or whether we need to split it up into separate horizontal chunks
  379.         bufferHeight = outputHeight;
  380.         if (outputWidth * outputHeight > MAX_BUFFER_PIXELS) {
  381.             bufferHeight = MAX_BUFFER_PIXELS / outputWidth;
  382.             nChunks = outputHeight / bufferHeight;              // whole chunks +
  383.             if (outputHeight % bufferHeight != 0) ++nChunks;    // partially occupied chunk
  384.             interlaced = false;
  385.         }
  386.         // create and show progress meter
  387.         wxString message;
  388.         message.Printf("Writing PNG file %s (%ix%i)",
  389.             (wxString(wxFileNameFromPath(filename.c_str()))).c_str(),
  390.             outputWidth, outputHeight);
  391.         progressMeter = new ProgressMeter(NULL, message, "Saving...", PROGRESS_RESOLUTION);
  392.         // open the output file for writing
  393.         out = fopen(filename.c_str(), "wb");
  394.         if (!out) throw "can't open file for writing";
  395. #if defined(__WXMSW__)
  396.         HDC hdc = NULL, current_hdc = NULL;
  397.         HGLRC hglrc = NULL, current_hglrc = NULL;
  398.         HGDIOBJ current_hgdiobj = NULL;
  399.         HBITMAP hbm = NULL;
  400.         PIXELFORMATDESCRIPTOR pfd;
  401.         int nPixelFormat;
  402.         current_hglrc = wglGetCurrentContext(); // save to restore later
  403.         current_hdc = wglGetCurrentDC();
  404.         // create bitmap of same color type as current rendering context
  405.         hbm = CreateCompatibleBitmap(current_hdc, outputWidth, bufferHeight);
  406.         if (!hbm) throw "failed to create rendering BITMAP";
  407.         hdc = CreateCompatibleDC(current_hdc);
  408.         if (!hdc) throw "CreateCompatibleDC failed";
  409.         current_hgdiobj = SelectObject(hdc, hbm);
  410.         if (!current_hgdiobj) throw "SelectObject failed";
  411.         memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
  412.         pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
  413.         pfd.nVersion = 1;
  414.         pfd.dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL;  // NOT doublebuffered, to save memory
  415.         pfd.iPixelType = PFD_TYPE_RGBA;
  416.         pfd.cColorBits = GetDeviceCaps(current_hdc, BITSPIXEL);
  417.         pfd.iLayerType = PFD_MAIN_PLANE;
  418.         nPixelFormat = ChoosePixelFormat(hdc, &pfd);
  419.         if (!nPixelFormat) {
  420.             ERRORMSG("ChoosePixelFormat failed");
  421.             throw GetLastError();
  422.         }
  423.         if (!SetPixelFormat(hdc, nPixelFormat, &pfd)) {
  424.             ERRORMSG("SetPixelFormat failed");
  425.             throw GetLastError();
  426.         }
  427.         hglrc = wglCreateContext(hdc);
  428.         if (!hglrc) {
  429.             ERRORMSG("wglCreateContext failed");
  430.             throw GetLastError();
  431.         }
  432.         // try to share display lists with regular window, to save memory and draw time
  433.         if (!wglShareLists(current_hglrc, hglrc)) {
  434.             WARNINGMSG("wglShareLists failed: " << GetLastError());
  435.             shareDisplayLists = false;
  436.         }
  437.         if (!wglMakeCurrent(hdc, hglrc)) {
  438.             ERRORMSG("wglMakeCurrent failed");
  439.             throw GetLastError();
  440.         }
  441. #elif defined(__WXGTK__)
  442.         GLint glSize;
  443.         int nAttribs, attribs[20];
  444.         XVisualInfo *visinfo = NULL;
  445.         bool localVI = false;
  446.         Pixmap xPixmap = 0;
  447.         GLXContext currentCtx = NULL, glCtx = NULL;
  448.         GLXPixmap glxPixmap = 0;
  449.         GLXDrawable currentXdrw = 0;
  450.         Display *display;
  451.         int (*currentXErrHandler)(Display *, XErrorEvent *) = NULL;
  452.         currentCtx = glXGetCurrentContext(); // save current context info
  453.         currentXdrw = glXGetCurrentDrawable();
  454.         display = GDK_DISPLAY();
  455.         currentXErrHandler = XSetErrorHandler(X_error_handler);
  456.         gotAnXError = false;
  457.         // first, try to get a non-doublebuffered visual, to economize on memory
  458.         nAttribs = 0;
  459.         attribs[nAttribs++] = GLX_USE_GL;
  460.         attribs[nAttribs++] = GLX_RGBA;
  461.         attribs[nAttribs++] = GLX_RED_SIZE;
  462.         glGetIntegerv(GL_RED_BITS, &glSize);
  463.         attribs[nAttribs++] = glSize;
  464.         attribs[nAttribs++] = GLX_GREEN_SIZE;
  465.         attribs[nAttribs++] = glSize;
  466.         attribs[nAttribs++] = GLX_BLUE_SIZE;
  467.         attribs[nAttribs++] = glSize;
  468.         attribs[nAttribs++] = GLX_DEPTH_SIZE;
  469.         glGetIntegerv(GL_DEPTH_BITS, &glSize);
  470.         attribs[nAttribs++] = glSize;
  471.         attribs[nAttribs++] = None;
  472.         visinfo = glXChooseVisual(display, DefaultScreen(display), attribs);
  473.         // if that fails, just revert to the one used for the regular window
  474.         if (visinfo)
  475.             localVI = true;
  476.         else
  477.             visinfo = (XVisualInfo *) (glCanvas->m_vi);
  478.         // create pixmap
  479.         xPixmap = XCreatePixmap(display,
  480.             RootWindow(display, DefaultScreen(display)),
  481.             outputWidth, bufferHeight, visinfo->depth);
  482.         if (!xPixmap) throw "failed to create Pixmap";
  483.         glxPixmap = glXCreateGLXPixmap(display, visinfo, xPixmap);
  484.         if (!glxPixmap) throw "failed to create GLXPixmap";
  485.         if (gotAnXError) throw "Got an X error creating GLXPixmap";
  486.         // try to share display lists with "regular" context
  487. // ... seems to be too flaky - fails on Linux/Mesa, Solaris
  488. //        glCtx = glXCreateContext(display, visinfo, currentCtx, False);
  489. //        if (!glCtx || !glXMakeCurrent(display, glxPixmap, glCtx)) {
  490. //            WARNINGMSG("failed to make GLXPixmap rendering context with shared display lists");
  491. //            if (glCtx) glXDestroyContext(display, glCtx);
  492.             shareDisplayLists = false;
  493.             // try to create context without shared lists
  494.             glCtx = glXCreateContext(display, visinfo, NULL, False);
  495.             if (!glCtx || !glXMakeCurrent(display, glxPixmap, glCtx))
  496.                 throw "failed to make GLXPixmap rendering context without shared display lists";
  497. //        }
  498.         if (gotAnXError) throw "Got an X error setting GLX context";
  499. #elif defined(__WXMAC__)
  500.         unsigned char *base = NULL;
  501.         GLint attrib[20], glSize;
  502.         int na = 0;
  503.         AGLPixelFormat fmt = NULL;
  504.         AGLContext ctx = NULL, currentCtx;
  505.      currentCtx = aglGetCurrentContext();
  506.      // Mac pixels seem to always be 32-bit
  507.      bytesPerPixel = 4;
  508.      base = new unsigned char[outputWidth * bufferHeight * bytesPerPixel];
  509.      if (!base) throw "failed to allocate image buffer";
  510.      // create an off-screen rendering context (NOT doublebuffered)
  511.      attrib[na++] = AGL_OFFSCREEN;
  512.      attrib[na++] = AGL_RGBA;
  513.      glGetIntegerv(GL_RED_BITS, &glSize);
  514.         attrib[na++] = AGL_RED_SIZE;
  515.         attrib[na++] = glSize;
  516.         attrib[na++] = AGL_GREEN_SIZE;
  517.         attrib[na++] = glSize;
  518.         attrib[na++] = AGL_BLUE_SIZE;
  519.         attrib[na++] = glSize;
  520.      glGetIntegerv(GL_DEPTH_BITS, &glSize);
  521.         attrib[na++] = AGL_DEPTH_SIZE;
  522.         attrib[na++] = glSize;
  523.         attrib[na++] = AGL_NONE;
  524.         if ((fmt=aglChoosePixelFormat(NULL, 0, attrib)) == NULL)
  525.          throw "aglChoosePixelFormat failed";
  526.         // try to share display lists with current "regular" context
  527.         if ((ctx=aglCreateContext(fmt, currentCtx)) == NULL) {
  528.             WARNINGMSG("aglCreateContext with shared lists failed");
  529.          shareDisplayLists = false;
  530.          if ((ctx=aglCreateContext(fmt, NULL)) == NULL)
  531.              throw "aglCreateContext without shared lists failed";
  532.         }
  533.         // attach off-screen buffer to this context
  534.         if (!aglSetOffScreen(ctx, outputWidth, bufferHeight, bytesPerPixel * outputWidth, base))
  535.          throw "aglSetOffScreen failed";
  536.         if (!aglSetCurrentContext(ctx))
  537.          throw "aglSetCurrentContext failed";
  538.         glCanvas->renderer->RecreateQuadric(); // Macs have context-sensitive quadrics...
  539. #endif
  540.         TRACEMSG("interlaced: " << interlaced << ", nChunks: " << nChunks
  541.             << ", buffer height: " << bufferHeight << ", shared: " << shareDisplayLists);
  542.         // allocate a row of pixel storage
  543.         rowStorage = new unsigned char[outputWidth * bytesPerPixel];
  544.         if (!rowStorage) throw "failed to allocate pixel row buffer";
  545.         /* set up the PNG writing (see libpng.txt) */
  546.         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, writepng_error_handler, NULL);
  547.         if (!png_ptr) throw "can't create PNG write structure";
  548.         info_ptr = png_create_info_struct(png_ptr);
  549.         if (!info_ptr) throw "can't create PNG info structure";
  550.         if (setjmp(png_ptr->jmpbuf)) throw "setjmp failed";
  551.         png_init_io(png_ptr, out);
  552.         // sets callback that's called by PNG after each written row
  553.         png_set_write_status_fn(png_ptr, write_row_callback);
  554.         // set various PNG parameters
  555.         png_set_compression_level(png_ptr, Z_BEST_COMPRESSION);
  556.         png_set_IHDR(png_ptr, info_ptr, outputWidth, outputHeight,
  557.             8, PNG_COLOR_TYPE_RGB,
  558.             interlaced ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE,
  559.             PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  560.         png_write_info(png_ptr, info_ptr);
  561.         // set up camera so that it's the same view as it is in the window
  562.         glCanvas->renderer->Init();
  563.         glViewport(0, 0, outputWidth, outputHeight);
  564.         glCanvas->renderer->NewView();
  565.         // Redraw the model into the new off-screen context, then use glReadPixels
  566.         // to retrieve pixel data. It's much easier to use glReadPixels rather than
  567.         // trying to read directly from the off-screen buffer, since GL can do all
  568.         // the potentially tricky work of translating from whatever pixel format
  569.         // the buffer uses into "regular" RGB byte triples. If fonts need scaling,
  570.         // have to reconstruct lists regardless of whether display lists are shared.
  571.         if (!shareDisplayLists || outputHeight != glCanvas->GetClientSize().GetHeight()) {
  572.             glCanvas->SetGLFontFromRegistry(((double) outputHeight) / glCanvas->GetClientSize().GetHeight());
  573.             glCanvas->renderer->Construct();
  574.         }
  575.         glPixelStorei(GL_PACK_ALIGNMENT, 1);
  576.         // Write image row by row to avoid having to allocate another full image's
  577.         // worth of memory. Do multiple passes for interlacing, if necessary. Note
  578.         // that PNG's rows are stored top down, while GL's are bottom up.
  579.         if (interlaced) {
  580.             // need to draw only once
  581.             glCanvas->renderer->Display();
  582.             int pass, r;
  583.             nRows = -outputHeight; // signal to monitor that we're interlacing
  584.             if (png_set_interlace_handling(png_ptr) != 7) throw "confused by unkown PNG interlace scheme";
  585.             for (pass = 1; pass <= 7; ++pass) {
  586.                 for (int i = outputHeight - 1; i >= 0; --i) {
  587.                     r = outputHeight - i - 1;
  588.                     // when interlacing, only certain rows are actually read
  589.                     // during certain passes - avoid unncessary reads
  590.                     if (
  591.                         ((pass == 1 || pass == 2) && (r % 8 == 0)) ||
  592.                         ((pass == 3) && ((r - 4) % 8 == 0)) ||
  593.                         ((pass == 4) && (r % 4 == 0)) ||
  594.                         ((pass == 5) && ((r - 2) % 4 == 0)) ||
  595.                         ((pass == 6) && (r % 2 == 0)) ||
  596.                         ((pass == 7) && ((r - 1) % 2 == 0))
  597.                     )
  598.                         glReadPixels(0, i, outputWidth, 1, GL_RGB, GL_UNSIGNED_BYTE, rowStorage);
  599.                     // ... but still have to call this for each row in each pass */
  600.                     png_write_row(png_ptr, rowStorage);
  601.                 }
  602.             }
  603.         }
  604.         // not interlaced, but possibly chunked
  605.         else {
  606.             int bufferRow, bufferRowStart;
  607.             nRows = outputHeight;
  608.             for (int chunk = nChunks - 1; chunk >= 0; --chunk) {
  609.                 // set viewport for this chunk and redraw
  610.                 if (nChunks > 1) {
  611.                     TRACEMSG("drawing chunk #" << (chunk + 1));
  612.                     glViewport(0, -chunk*bufferHeight, outputWidth, outputHeight);
  613.                 }
  614.                 glCanvas->renderer->Display();
  615.                 // only draw "visible" part of top chunk
  616.                 if (chunk == nChunks - 1)
  617.                     bufferRowStart = outputHeight - 1 - bufferHeight * (nChunks - 1);
  618.                 else
  619.                     bufferRowStart = bufferHeight - 1;
  620.                 // dump chunk to PNG file
  621.                 for (bufferRow = bufferRowStart; bufferRow >= 0; --bufferRow) {
  622.                     glReadPixels(0, bufferRow, outputWidth, 1, GL_RGB, GL_UNSIGNED_BYTE, rowStorage);
  623.                     png_write_row(png_ptr, rowStorage);
  624.                 }
  625.             }
  626.         }
  627.         // finish up PNG write
  628.         png_write_end(png_ptr, info_ptr);
  629.         success = true;
  630.         // general cleanup
  631.         if (out) fclose(out);
  632.         if (rowStorage) delete rowStorage;
  633.         if (png_ptr) {
  634.             if (info_ptr)
  635.                 png_destroy_write_struct(&png_ptr, &info_ptr);
  636.             else
  637.                 png_destroy_write_struct(&png_ptr, NULL);
  638.         }
  639.         if (progressMeter) {
  640.             progressMeter->Close(true);
  641.             progressMeter->Destroy();
  642.             progressMeter = NULL;
  643.         }
  644.     // platform-specific cleanup, restore context
  645. #if defined(__WXMSW__)
  646.         if (current_hdc && current_hglrc) wglMakeCurrent(current_hdc, current_hglrc);
  647.         if (hglrc) wglDeleteContext(hglrc);
  648.         if (hbm) DeleteObject(hbm);
  649.         if (hdc) DeleteDC(hdc);
  650. #elif defined(__WXGTK__)
  651.         gotAnXError = false;
  652.         if (glCtx) {
  653.             glXMakeCurrent(display, currentXdrw, currentCtx);
  654.             glXDestroyContext(display, glCtx);
  655.         }
  656.         if (glxPixmap) glXDestroyGLXPixmap(display, glxPixmap);
  657.         if (xPixmap) XFreePixmap(display, xPixmap);
  658.         if (localVI && visinfo) XFree(visinfo);
  659.         if (gotAnXError) WARNINGMSG("Got an X error destroying GLXPixmap context");
  660.         XSetErrorHandler(currentXErrHandler);
  661. #elif defined(__WXMAC__)
  662.         aglSetCurrentContext(currentCtx);
  663.         if (ctx) aglDestroyContext(ctx);
  664.         if (fmt) aglDestroyPixelFormat(fmt);
  665.         if (base) delete[] base;
  666.         glCanvas->renderer->RecreateQuadric(); // Macs have context-sensitive quadrics...
  667. #endif
  668.     } catch (const char *err) {
  669.         ERRORMSG("Error creating PNG: " << err);
  670.     } catch (exception& e) {
  671.         ERRORMSG("Uncaught exception while creating PNG: " << e.what());
  672.     }
  673.     // reset font after "regular" context restore
  674.     glCanvas->SuspendRendering(false);
  675.     if (outputHeight != glCanvas->GetClientSize().GetHeight()) {
  676.         glCanvas->SetCurrent();
  677.         glCanvas->SetGLFontFromRegistry(1.0);
  678.         if (shareDisplayLists)
  679.             GlobalMessenger()->PostRedrawAllStructures();
  680.     }
  681.     glCanvas->Refresh(false);
  682.     return success;
  683. }
  684. END_SCOPE(Cn3D)
  685. ////////////////////////////////////////////////////////////////////////////////////////////////
  686. // The following is taken unmodified from wxDesigner's C++ code from png_dialog.wdr
  687. ////////////////////////////////////////////////////////////////////////////////////////////////
  688. wxSizer *SetupPNGOptionsDialog( wxPanel *parent, bool call_fit, bool set_sizer )
  689. {
  690.     wxBoxSizer *item0 = new wxBoxSizer( wxVERTICAL );
  691.     wxStaticBox *item2 = new wxStaticBox( parent, -1, "Output Settings" );
  692.     wxStaticBoxSizer *item1 = new wxStaticBoxSizer( item2, wxVERTICAL );
  693.     wxFlexGridSizer *item3 = new wxFlexGridSizer( 1, 0, 0, 0 );
  694.     item3->AddGrowableCol( 1 );
  695.     wxStaticText *item4 = new wxStaticText( parent, ID_TEXT, "File name:", wxDefaultPosition, wxDefaultSize, 0 );
  696.     item3->Add( item4, 0, wxALIGN_CENTRE|wxALL, 5 );
  697.     item3->Add( 20, 20, 0, wxALIGN_CENTRE|wxALL, 5 );
  698.     wxButton *item5 = new wxButton( parent, ID_B_BROWSE, "Browse", wxDefaultPosition, wxDefaultSize, 0 );
  699.     item3->Add( item5, 0, wxALIGN_CENTRE|wxLEFT|wxRIGHT|wxTOP, 5 );
  700.     item1->Add( item3, 0, wxGROW|wxALIGN_CENTER_VERTICAL, 5 );
  701.     wxTextCtrl *item6 = new wxTextCtrl( parent, ID_T_NAME, "", wxDefaultPosition, wxDefaultSize, 0 );
  702.     item1->Add( item6, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
  703.     wxFlexGridSizer *item7 = new wxFlexGridSizer( 2, 0, 0, 0 );
  704.     item7->AddGrowableCol( 1 );
  705.     item7->AddGrowableRow( 1 );
  706.     wxStaticText *item8 = new wxStaticText( parent, ID_TEXT, "Width:", wxDefaultPosition, wxDefaultSize, 0 );
  707.     item7->Add( item8, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 );
  708.     item7->Add( 20, 20, 0, wxALIGN_CENTRE|wxALL, 5 );
  709.     wxStaticText *item9 = new wxStaticText( parent, ID_TEXT, "Height:", wxDefaultPosition, wxDefaultSize, 0 );
  710.     item7->Add( item9, 0, wxALIGN_CENTER_VERTICAL|wxLEFT|wxRIGHT|wxTOP, 5 );
  711.     wxTextCtrl *item10 = new wxTextCtrl( parent, ID_T_WIDTH, "", wxDefaultPosition, wxDefaultSize, 0 );
  712.     item7->Add( item10, 0, wxALIGN_CENTRE|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
  713.     wxStaticText *item11 = new wxStaticText( parent, ID_TEXT, "x", wxDefaultPosition, wxDefaultSize, 0 );
  714.     item7->Add( item11, 0, wxALIGN_CENTRE|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
  715.     wxTextCtrl *item12 = new wxTextCtrl( parent, ID_T_HEIGHT, "", wxDefaultPosition, wxDefaultSize, 0 );
  716.     item7->Add( item12, 0, wxALIGN_CENTRE|wxLEFT|wxRIGHT|wxBOTTOM, 5 );
  717.     item1->Add( item7, 0, wxGROW|wxALIGN_CENTER_VERTICAL|wxALL, 5 );
  718.     wxBoxSizer *item13 = new wxBoxSizer( wxHORIZONTAL );
  719.     wxCheckBox *item14 = new wxCheckBox( parent, ID_C_ASPECT, "Maintain original aspect ratio", wxDefaultPosition, wxDefaultSize, 0 );
  720.     item14->SetValue( TRUE );
  721.     item13->Add( item14, 0, wxALIGN_CENTRE|wxALL, 5 );
  722.     item13->Add( 20, 20, 0, wxALIGN_CENTRE|wxALL, 5 );
  723.     wxCheckBox *item15 = new wxCheckBox( parent, ID_C_INTERLACE, "Interlaced", wxDefaultPosition, wxDefaultSize, 0 );
  724.     item15->SetValue( TRUE );
  725.     item13->Add( item15, 0, wxALIGN_CENTRE|wxALL, 5 );
  726.     item1->Add( item13, 0, wxALIGN_CENTRE|wxLEFT|wxRIGHT|wxTOP, 5 );
  727.     item0->Add( item1, 0, wxALIGN_CENTRE|wxALL, 5 );
  728.     wxBoxSizer *item16 = new wxBoxSizer( wxHORIZONTAL );
  729.     wxButton *item17 = new wxButton( parent, ID_B_OK, "OK", wxDefaultPosition, wxDefaultSize, 0 );
  730.     item17->SetDefault();
  731.     item16->Add( item17, 0, wxALIGN_CENTRE|wxALL, 5 );
  732.     item16->Add( 20, 20, 0, wxALIGN_CENTRE|wxALL, 5 );
  733.     wxButton *item18 = new wxButton( parent, ID_B_CANCEL, "Cancel", wxDefaultPosition, wxDefaultSize, 0 );
  734.     item16->Add( item18, 0, wxALIGN_CENTRE|wxALL, 5 );
  735.     item0->Add( item16, 0, wxALIGN_CENTRE|wxALL, 5 );
  736.     if (set_sizer)
  737.     {
  738.         parent->SetAutoLayout( TRUE );
  739.         parent->SetSizer( item0 );
  740.         if (call_fit)
  741.         {
  742.             item0->Fit( parent );
  743.             item0->SetSizeHints( parent );
  744.         }
  745.     }
  746.     return item0;
  747. }
  748. /*
  749. * ---------------------------------------------------------------------------
  750. * $Log: cn3d_png.cpp,v $
  751. * Revision 1000.3  2004/06/01 18:28:18  gouriano
  752. * PRODUCTION: UPGRADED [GCC34_MSVC7] Dev-tree R1.21
  753. *
  754. * Revision 1.21  2004/05/21 21:41:39  gorelenk
  755. * Added PCH ncbi_pch.hpp
  756. *
  757. * Revision 1.20  2004/03/15 17:30:06  thiessen
  758. * prefer prefix vs. postfix ++/-- operators
  759. *
  760. * Revision 1.19  2004/02/19 17:04:50  thiessen
  761. * remove cn3d/ from include paths; add pragma to disable annoying msvc warning
  762. *
  763. * Revision 1.18  2003/12/04 15:49:39  thiessen
  764. * fix stereo and PNG export problems on Mac
  765. *
  766. * Revision 1.17  2003/11/15 16:08:35  thiessen
  767. * add stereo
  768. *
  769. * Revision 1.16  2003/03/13 14:26:18  thiessen
  770. * add file_messaging module; split cn3d_main_wxwin into cn3d_app, cn3d_glcanvas, structure_window, cn3d_tools
  771. *
  772. * Revision 1.15  2003/02/03 19:20:03  thiessen
  773. * format changes: move CVS Log to bottom of file, remove std:: from .cpp files, and use new diagnostic macros
  774. *
  775. * Revision 1.14  2002/10/21 15:11:29  thiessen
  776. * don't share lists in wxGTK build
  777. *
  778. * Revision 1.13  2002/10/18 20:33:54  thiessen
  779. * workaround for linux/Mesa bug
  780. *
  781. * Revision 1.12  2002/10/18 15:42:59  thiessen
  782. * work around png header issue for linux
  783. *
  784. * Revision 1.11  2002/10/11 17:21:39  thiessen
  785. * initial Mac OSX build
  786. *
  787. * Revision 1.10  2002/08/15 22:13:14  thiessen
  788. * update for wx2.3.2+ only; add structure pick dialog; fix MultitextDialog bug
  789. *
  790. * Revision 1.9  2002/04/21 12:21:21  thiessen
  791. * minor fixes for AIX
  792. *
  793. * Revision 1.8  2001/10/25 17:16:43  thiessen
  794. * add PNG output to Mac version
  795. *
  796. * Revision 1.7  2001/10/25 14:19:44  thiessen
  797. * move png.h to top
  798. *
  799. * Revision 1.6  2001/10/25 00:06:29  thiessen
  800. * fix concurrent rendering problem in wxMSW PNG output
  801. *
  802. * Revision 1.5  2001/10/24 22:02:02  thiessen
  803. * fix wxGTK concurrent rendering problem
  804. *
  805. * Revision 1.4  2001/10/24 17:07:30  thiessen
  806. * add PNG output for wxGTK
  807. *
  808. * Revision 1.3  2001/10/24 11:25:20  thiessen
  809. * fix wxString problem
  810. *
  811. * Revision 1.2  2001/10/23 20:10:23  thiessen
  812. * fix scaling of fonts in high-res PNG output
  813. *
  814. * Revision 1.1  2001/10/23 13:53:38  thiessen
  815. * add PNG export
  816. *
  817. */