void ModelOpened::ExportPNG(wxString val, wxString suffix) { if (val == wxEmptyString) return; wxFileName fn = fixMPQPath(val); if (fn.GetExt().Lower() != wxT("blp")) return; TextureID temptex = texturemanager.add(val); Texture &tex = *((Texture*)texturemanager.items[temptex]); if (tex.w == 0 || tex.h == 0) return; wxString temp; unsigned char *tempbuf = (unsigned char*)malloc(tex.w*tex.h*4); tex.getPixels(tempbuf, GL_BGRA_EXT); CxImage *newImage = new CxImage(0); newImage->AlphaCreate(); // Create the alpha layer newImage->IncreaseBpp(32); // set image to 32bit newImage->CreateFromArray(tempbuf, tex.w, tex.h, 32, (tex.w*4), true); if (bPathPreserved) { wxFileName::Mkdir(wxGetCwd()+SLASH+wxT("Export")+SLASH+fn.GetPath(), 0755, wxPATH_MKDIR_FULL); temp = wxGetCwd()+SLASH+wxT("Export")+SLASH+fn.GetPath()+SLASH+fn.GetName()+wxT(".")+suffix; } else { temp = wxGetCwd()+SLASH+wxT("Export")+SLASH+fn.GetName()+wxT(".")+suffix; } //wxLogMessage(wxT("Info: Exporting texture to %s..."), temp.c_str()); if (suffix == wxT("tga")) #ifndef _MINGW newImage->Save(temp.mb_str(), CXIMAGE_FORMAT_TGA); #else newImage->Save(temp.wc_str(), CXIMAGE_FORMAT_TGA); #endif else
// This must be called before any frame-saving is attempted. void CAnimationExporter::CreateGif() { if(!g_canvas || !g_canvas->model || !g_canvas->model->animManager) { wxMessageBox(_T("Unable to create animated GIF!"), _T("Error") ); wxLogMessage(_T( "Error: Unable to created animated GIF. A required objects pointer was null!") ); Show(false); return ; } CxImage **gifImages = NULL; // Our pointer array of images // Reset the state of our GUI objects btnStart->Enable(false); btnCancel->Enable(false); cbGrey->Enable(false); cbTrans->Enable(false); cbDither->Enable(false); cbShrink->Enable(false); txtFrames->Enable(false); txtSizeX->Enable(false); txtSizeY->Enable(false); txtDelay->Enable(false); // Pause our rendering to screen so we can focus on making the animated image g_videoSetting.render = false; m_fAnimSpeed = g_canvas->model->animManager->GetSpeed(); // Save the old animation speed g_canvas->model->animManager->SetSpeed(1.0f); // Set it to the normal speed. m_iTotalAnimFrames = g_canvas->model->animManager->GetFrameCount(); wxString(txtFrames->GetValue() ).ToLong( (long*) &m_iTotalFrames); wxString(txtDelay->GetValue() ).ToLong( (long*) &m_iDelay); // will crash program - prevent this from happening if(m_iTotalFrames > m_iTotalAnimFrames) { wxMessageBox(_T( "Impossible to make a gif with more frames than the model animation.\nClosing gif exporter."), _T("Error") ); wxLogMessage(_T( "Error: Unable to make a gif with more frames than the model animation.") ); this->Show(false); return ; } if(m_iDelay < 1) { m_iDelay = 1; } if(m_iDelay > 100) { m_iDelay = 100; } m_iTimeStep = int(m_iTotalAnimFrames / m_iTotalFrames); // Total number of frames in the animation / total frames going into our exported animation image if(m_bShrink) { wxString(txtSizeX->GetValue() ).ToLong( (long*) &m_iNewWidth); wxString(txtSizeY->GetValue() ).ToLong( (long*) &m_iNewHeight); // Just a minor check, final image size can not be smaller than 32x32 pixels. if(m_iNewWidth < 32 || m_iNewHeight < 32) { m_iNewWidth = 32; m_iNewHeight = 32; } } // CREATE OUR RENDERTOTEXTURE OBJECT // ------------------------------------------- // if either are supported use our 'RenderTexture' object. if(g_videoSetting.supportPBO || g_videoSetting.supportVBO) { g_canvas->rt = new RenderTexture(); if(!g_canvas->rt) { wxLogMessage(_T("Error: RenderToTexture object is null!") ); this->Show(false); return ; } g_canvas->rt->Init( (HWND)g_canvas->GetHandle(), 0, 0, g_videoSetting.supportFBO) ; m_iWidth = g_canvas->rt->nWidth; m_iHeight = g_canvas->rt->nHeight; g_canvas->rt->BeginRender(); } else { glReadBuffer(GL_BACK); int screenSize[4]; glGetIntegerv(GL_VIEWPORT, screenSize); // get the width/height of the canvas m_iWidth = screenSize[2]; m_iHeight = screenSize[3]; return ; } // Stop our animation g_canvas->model->animManager->Pause(true); g_canvas->model->animManager->Stop(); g_canvas->model->animManager->AnimateParticles(); // Size of our buffer to hold the pixel data m_iSize = m_iWidth * m_iHeight * 4; // (width*height*bytesPerPixel) // Create one frame to make our optimal colour palette from. unsigned char *buffer = new unsigned char[m_iSize]; gifImages = new CxImage *[m_iTotalFrames]; for(unsigned int i = 0; i < m_iTotalFrames; i++) { lblCurFrame->SetLabel(wxString::Format(_T("Current Frame: %i"), i) ); this->Refresh(); this->Update(); CxImage *newImage = new CxImage(0); g_canvas->RenderToBuffer(); glReadPixels(0, 0, m_iWidth, m_iHeight, GL_BGRA_EXT, GL_UNSIGNED_BYTE, buffer); newImage->CreateFromArray(buffer, m_iWidth, m_iHeight, 32, (m_iWidth *4) , false); // not needed due to the code just below, which fixes the issue with particles //g_canvas->model->animManager->SetTimeDiff(m_iTimeStep); //g_canvas->model->animManager->Tick(m_iTimeStep); if(g_canvas->root) { g_canvas->root->tick( (float)m_iTimeStep); } if(g_canvas->sky) { g_canvas->sky->tick( (float)m_iTimeStep); } #ifdef _WIN32 if(m_bGreyscale) { newImage->GrayScale(); } #endif //_WIN32 if(m_bShrink && m_iNewWidth != m_iWidth && m_iNewHeight != m_iHeight) { newImage->Resample(m_iNewWidth, m_iNewHeight, 2); } // if (Optimise) { if(!m_pPal) { CQuantizer q(256, 8); q.ProcessImage( (HANDLE)newImage->GetDIB() ); m_pPal = (RGBQUAD*)calloc(256 *sizeof(RGBQUAD), 1); //This creates our gifs optimised global colour palette q.SetColorTable(m_pPal); } newImage->DecreaseBpp(8, m_bDiffuse, m_pPal, 256); newImage->SetCodecOption(2); // for LZW compression if(m_bTransparent) { newImage->SetTransIndex(newImage->GetPixelIndex(0, 0) ); } newImage->SetFrameDelay(m_iDelay); gifImages[i] = newImage; // All the memory that we allocate for newImage gets cleared at the end } wxDELETEA(buffer); if(g_videoSetting.supportPBO || g_videoSetting.supportVBO) { g_canvas->rt->EndRender(); // Clear RenderTexture object. g_canvas->rt->Shutdown(); wxDELETE(g_canvas->rt); } // CREATE THE ACTUAL MULTI-IMAGE GIF ANIMATION // ------------------------------------------------------ // Create the file and write all the data // Open/Create the file that were going to save to FILE *hFile = NULL; hFile = fopen(m_strFilename.fn_str(), "wb"); // Set gif options CxImageGIF multiImage; multiImage.SetComment("Exported from WoW Model Viewer"); if(m_bTransparent) { multiImage.SetDisposalMethod(2); } else { multiImage.SetDisposalMethod(0); } multiImage.SetFrameDelay(m_iDelay); multiImage.SetCodecOption(2); // LZW multiImage.SetLoops(0); // Set the animation to loop indefinately. // Create/Compose the animated gif multiImage.Encode(hFile, gifImages, m_iTotalFrames, false); // ALL DONE, START THE CLEAN UP // -------------------------------------------------------- // Close file fclose(hFile); // Free the memory used by all the images to create the GIF for(unsigned int i = 0; i < m_iTotalFrames; i++) { gifImages[i]->Destroy(); wxDELETE(gifImages[i]); } wxDELETEA(gifImages); // Free memory used by the colour palette if(m_pPal) { free(m_pPal); m_pPal = NULL; } wxLogMessage(_T("Info: GIF Animation successfully created.") ); g_canvas->model->animManager->SetSpeed(m_fAnimSpeed); // Return the animation speed back to whatever it was previously set as g_canvas->model->animManager->Play(); Show(false); g_videoSetting.render = true; g_canvas->InitView(); }