📄 cn3d_png.cpp
字号:
cInterlace->SetValue(false); } else cInterlace->SetValue(initialInterlaced); // bring up file selector first wxCommandEvent browse(wxEVT_COMMAND_BUTTON_CLICKED, ID_B_BROWSE); OnButton(browse); if (tName->GetValue().size() == 0) return false; // cancelled // return dialog result return (ShowModal() == wxOK);}bool PNGOptionsDialog::GetValues(wxString *outputFilename, int *width, int *height, bool *interlaced){ DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(tName, ID_T_NAME, wxTextCtrl, false) DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(tWidth, ID_T_WIDTH, wxTextCtrl, false) DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(tHeight, ID_T_HEIGHT, wxTextCtrl, false) DECLARE_AND_FIND_WINDOW_RETURN_RESULT_ON_ERR(cInterlace, ID_C_INTERLACE, wxCheckBox, false) *outputFilename = tName->GetValue(); double val; if (!GET_AND_IS_VALID_SIZE(tWidth, val)) return false; *width = (int) val; if (!GET_AND_IS_VALID_SIZE(tHeight, val)) return false; *height = (int) val; *interlaced = (cInterlace->IsEnabled() && cInterlace->GetValue()); return true;}static bool GetOutputParameters(wxString *outputFilename, int *width, int *height, bool *interlaced){ PNGOptionsDialog dialog(NULL); bool ok = dialog.Activate(*width, *height, *interlaced); if (ok) dialog.GetValues(outputFilename, width, height, interlaced); return ok;}// callback used by PNG library to report errorsstatic void writepng_error_handler(png_structp png_ptr, png_const_charp msg){ ERRORMSG("PNG library error: " << msg);}#ifdef __WXGTK__static bool gotAnXError;int X_error_handler(Display *dpy, XErrorEvent *error){ gotAnXError = true; return 0;}#endif// called after each row is written to the filestatic void write_row_callback(png_structp png_ptr, png_uint_32 row, int pass){ if (!progressMeter) return; int progress = 0; if (nRows < 0) { /* if negative, then we're doing interlacing */ double start, end; switch (pass + 1) { case 1: start = 0; end = 1; break; case 2: start = 1; end = 2; break; case 3: start = 2; end = 3; break; case 4: start = 3; end = 5; break; case 5: start = 5; end = 7; break; case 6: start = 7; end = 11; break; case 7: start = 11; end = 15; break; default: return; // png lib gives final pass=7,row=0 to signal completion } progress = (int) (1.0 * PROGRESS_RESOLUTION * ((start / 15) + (((double) row) / (-nRows)) * ((end - start) / 15))); } else { /* not interlaced */ progress = (int) (1.0 * PROGRESS_RESOLUTION * row / nRows); } progressMeter->SetValue(progress);}bool ExportPNG(Cn3DGLCanvas *glCanvas){#if !defined(__WXMSW__) && !defined(__WXGTK__) && !defined(__WXMAC__) ERRORMSG("PNG export not (yet) implemented on this platform"); return false;#endif if (!glCanvas || !glCanvas->renderer) { ERRORMSG("ExportPNG() - bad glCanvas parameter"); return false; } bool success = false, shareDisplayLists = true, interlaced = true; int outputWidth, outputHeight, bufferHeight, bytesPerPixel = 3, nChunks = 1; wxString filename; FILE *out = NULL; unsigned char *rowStorage = NULL; png_structp png_ptr = NULL; png_infop info_ptr = NULL; outputWidth = glCanvas->GetClientSize().GetWidth(); // initial size outputHeight = glCanvas->GetClientSize().GetHeight(); if (!GetOutputParameters(&filename, &outputWidth, &outputHeight, &interlaced)) return true; // cancelled try { INFOMSG("saving PNG file '" << filename.c_str() << "'"); // need to avoid any GL calls in glCanvas while off-screen rendering is happening; so // temporarily prevent glCanvas from responding to window resize/exposure, etc. glCanvas->SuspendRendering(true); int windowViewport[4]; glCanvas->renderer->GetViewport(windowViewport); TRACEMSG("window viewport: x,y: " << windowViewport[0] << ',' << windowViewport[1] << " size: " << windowViewport[2] << ',' << windowViewport[3]); INFOMSG("output size: " << outputWidth << ',' << outputHeight); // decide whether the in-memory image buffer can fit the whole drawing, // or whether we need to split it up into separate horizontal chunks bufferHeight = outputHeight; if (outputWidth * outputHeight > MAX_BUFFER_PIXELS) { bufferHeight = MAX_BUFFER_PIXELS / outputWidth; nChunks = outputHeight / bufferHeight; // whole chunks + if (outputHeight % bufferHeight != 0) ++nChunks; // partially occupied chunk interlaced = false; } // create and show progress meter wxString message; message.Printf("Writing PNG file %s (%ix%i)", (wxString(wxFileNameFromPath(filename.c_str()))).c_str(), outputWidth, outputHeight); progressMeter = new ProgressMeter(NULL, message, "Saving...", PROGRESS_RESOLUTION); // open the output file for writing out = fopen(filename.c_str(), "wb"); if (!out) throw "can't open file for writing";#if defined(__WXMSW__) HDC hdc = NULL, current_hdc = NULL; HGLRC hglrc = NULL, current_hglrc = NULL; HGDIOBJ current_hgdiobj = NULL; HBITMAP hbm = NULL; PIXELFORMATDESCRIPTOR pfd; int nPixelFormat; current_hglrc = wglGetCurrentContext(); // save to restore later current_hdc = wglGetCurrentDC(); // create bitmap of same color type as current rendering context hbm = CreateCompatibleBitmap(current_hdc, outputWidth, bufferHeight); if (!hbm) throw "failed to create rendering BITMAP"; hdc = CreateCompatibleDC(current_hdc); if (!hdc) throw "CreateCompatibleDC failed"; current_hgdiobj = SelectObject(hdc, hbm); if (!current_hgdiobj) throw "SelectObject failed"; memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR)); pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); pfd.nVersion = 1; pfd.dwFlags = PFD_DRAW_TO_BITMAP | PFD_SUPPORT_OPENGL; // NOT doublebuffered, to save memory pfd.iPixelType = PFD_TYPE_RGBA; pfd.cColorBits = GetDeviceCaps(current_hdc, BITSPIXEL); pfd.iLayerType = PFD_MAIN_PLANE; nPixelFormat = ChoosePixelFormat(hdc, &pfd); if (!nPixelFormat) { ERRORMSG("ChoosePixelFormat failed"); throw GetLastError(); } if (!SetPixelFormat(hdc, nPixelFormat, &pfd)) { ERRORMSG("SetPixelFormat failed"); throw GetLastError(); } hglrc = wglCreateContext(hdc); if (!hglrc) { ERRORMSG("wglCreateContext failed"); throw GetLastError(); } // try to share display lists with regular window, to save memory and draw time if (!wglShareLists(current_hglrc, hglrc)) { WARNINGMSG("wglShareLists failed: " << GetLastError()); shareDisplayLists = false; } if (!wglMakeCurrent(hdc, hglrc)) { ERRORMSG("wglMakeCurrent failed"); throw GetLastError(); }#elif defined(__WXGTK__) GLint glSize; int nAttribs, attribs[20]; XVisualInfo *visinfo = NULL; bool localVI = false; Pixmap xPixmap = 0; GLXContext currentCtx = NULL, glCtx = NULL; GLXPixmap glxPixmap = 0; GLXDrawable currentXdrw = 0; Display *display; int (*currentXErrHandler)(Display *, XErrorEvent *) = NULL; currentCtx = glXGetCurrentContext(); // save current context info currentXdrw = glXGetCurrentDrawable(); display = GDK_DISPLAY(); currentXErrHandler = XSetErrorHandler(X_error_handler); gotAnXError = false; // first, try to get a non-doublebuffered visual, to economize on memory nAttribs = 0; attribs[nAttribs++] = GLX_USE_GL; attribs[nAttribs++] = GLX_RGBA; attribs[nAttribs++] = GLX_RED_SIZE; glGetIntegerv(GL_RED_BITS, &glSize); attribs[nAttribs++] = glSize; attribs[nAttribs++] = GLX_GREEN_SIZE; attribs[nAttribs++] = glSize; attribs[nAttribs++] = GLX_BLUE_SIZE; attribs[nAttribs++] = glSize; attribs[nAttribs++] = GLX_DEPTH_SIZE; glGetIntegerv(GL_DEPTH_BITS, &glSize); attribs[nAttribs++] = glSize; attribs[nAttribs++] = None; visinfo = glXChooseVisual(display, DefaultScreen(display), attribs); // if that fails, just revert to the one used for the regular window if (visinfo) localVI = true; else visinfo = (XVisualInfo *) (glCanvas->m_vi); // create pixmap xPixmap = XCreatePixmap(display, RootWindow(display, DefaultScreen(display)), outputWidth, bufferHeight, visinfo->depth); if (!xPixmap) throw "failed to create Pixmap"; glxPixmap = glXCreateGLXPixmap(display, visinfo, xPixmap); if (!glxPixmap) throw "failed to create GLXPixmap"; if (gotAnXError) throw "Got an X error creating GLXPixmap"; // try to share display lists with "regular" context // ... seems to be too flaky - fails on Linux/Mesa, Solaris// glCtx = glXCreateContext(display, visinfo, currentCtx, False);// if (!glCtx || !glXMakeCurrent(display, glxPixmap, glCtx)) {// WARNINGMSG("failed to make GLXPixmap rendering context with shared display lists");// if (glCtx) glXDestroyContext(display, glCtx); shareDisplayLists = false; // try to create context without shared lists glCtx = glXCreateContext(display, visinfo, NULL, False); if (!glCtx || !glXMakeCurrent(display, glxPixmap, glCtx)) throw "failed to make GLXPixmap rendering context without shared display lists";// } if (gotAnXError) throw "Got an X error setting GLX context";#elif defined(__WXMAC__) unsigned char *base = NULL; GLint attrib[20], glSize; int na = 0; AGLPixelFormat fmt = NULL; AGLContext ctx = NULL, currentCtx; currentCtx = aglGetCurrentContext(); // Mac pixels seem to always be 32-bit bytesPerPixel = 4; base = new unsigned char[outputWidth * bufferHeight * bytesPerPixel]; if (!base) throw "failed to allocate image buffer"; // create an off-screen rendering context (NOT doublebuffered) attrib[na++] = AGL_OFFSCREEN; attrib[na++] = AGL_RGBA; glGetIntegerv(GL_RED_BITS, &glSize); attrib[na++] = AGL_RED_SIZE; attrib[na++] = glSize; attrib[na++] = AGL_GREEN_SIZE; attrib[na++] = glSize; attrib[na++] = AGL_BLUE_SIZE; attrib[na++] = glSize; glGetIntegerv(GL_DEPTH_BITS, &glSize); attrib[na++] = AGL_DEPTH_SIZE; attrib[na++] = glSize; attrib[na++] = AGL_NONE; if ((fmt=aglChoosePixelFormat(NULL, 0, attrib)) == NULL) throw "aglChoosePixelFormat failed"; // try to share display lists with current "regular" context if ((ctx=aglCreateContext(fmt, currentCtx)) == NULL) { WARNINGMSG("aglCreateContext with shared lists failed"); shareDisplayLists = false; if ((ctx=aglCreateContext(fmt, NULL)) == NULL) throw "aglCreateContext without shared lists failed"; } // attach off-screen buffer to this context if (!aglSetOffScreen(ctx, outputWidth, bufferHeight, bytesPerPixel * outputWidth, base)) throw "aglSetOffScreen failed"; if (!aglSetCurrentContext(ctx)) throw "aglSetCurrentContext failed"; glCanvas->renderer->RecreateQuadric(); // Macs have context-sensitive quadrics...#endif TRACEMSG("interlaced: " << interlaced << ", nChunks: " << nChunks << ", buffer height: " << bufferHeight << ", shared: " << shareDisplayLists); // allocate a row of pixel storage rowStorage = new unsigned char[outputWidth * bytesPerPixel]; if (!rowStorage) throw "failed to allocate pixel row buffer"; /* set up the PNG writing (see libpng.txt) */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, writepng_error_handler, NULL); if (!png_ptr) throw "can't create PNG write structure"; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) throw "can't create PNG info structure"; if (setjmp(png_ptr->jmpbuf)) throw "setjmp failed"; png_init_io(png_ptr, out);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -