--- /dev/null
- #include "intern/IMB_bmp.h"
+
+//
+// Copyright (C) : Please refer to the COPYRIGHT file distributed
+// with this source distribution.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "StrokeRenderer.h"
+#include <vector>
+#include "../system/FreestyleConfig.h"
+#include "../system/TimeStamp.h"
+#include "../system/PseudoNoise.h"
+#include "Canvas.h"
+#include "../image/Image.h"
+#include "../image/GaussianFilter.h"
+#include "../image/ImagePyramid.h"
+#include "../view_map/SteerableViewMap.h"
+#include "StyleModule.h"
+
+//soc #include <qimage.h>
+//soc #include <QString>
+#include <sstream>
+
+extern "C" {
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
- imb_savebmp(qtmp, const_cast<char *>(filename.str().c_str()), 0);
+}
+
+using namespace std;
+
+LIB_STROKE_EXPORT
+Canvas * Canvas::_pInstance = 0;
+
+LIB_STROKE_EXPORT
+const char * Canvas::_MapsPath = 0;
+
+using namespace std;
+
+Canvas::Canvas()
+{
+ _SelectedFEdge = 0;
+ _pInstance = this;
+ PseudoNoise::init(42);
+ _Renderer = 0;
+ _current_sm = NULL;
+ _steerableViewMap = new SteerableViewMap(NB_STEERABLE_VIEWMAP-1);
+ _basic = false;
+}
+
+Canvas::Canvas(const Canvas& iBrother)
+{
+ _SelectedFEdge = iBrother._SelectedFEdge;
+ _pInstance = this;
+ PseudoNoise::init(42);
+ _Renderer = iBrother._Renderer;
+ _current_sm = iBrother._current_sm;
+ _steerableViewMap = new SteerableViewMap(*(iBrother._steerableViewMap));
+ _basic = iBrother._basic;
+}
+
+Canvas::~Canvas()
+{
+ _pInstance = 0;
+
+ Clear();
+ if(_Renderer)
+ {
+ delete _Renderer;
+ _Renderer = 0;
+ }
+ // FIXME: think about an easy control
+ // for the maps memory management...
+ if(!_maps.empty()){
+ for(mapsMap::iterator m=_maps.begin(), mend=_maps.end();
+ m!=mend;
+ ++m){
+ delete ((*m).second);
+ }
+ _maps.clear();
+ }
+ if(_steerableViewMap)
+ delete _steerableViewMap;
+}
+
+void Canvas::preDraw() {}
+
+void Canvas::Draw()
+{
+ if(_StyleModules.empty())
+ return;
+ preDraw();
+ TimeStamp *timestamp = TimeStamp::instance();
+
+ for(unsigned i = 0; i < _StyleModules.size(); i++) {
+ _current_sm = _StyleModules[i];
+
+ if (i < _Layers.size() && _Layers[i])
+ delete _Layers[i];
+
+ _Layers[i] = _StyleModules[i]->execute();
+ if (!_Layers[i])
+ continue;
+
+ stroke_count += _Layers[i]->strokes_size();
+
+ timestamp->increment();
+ }
+ postDraw();
+}
+
+void Canvas::postDraw()
+{
+ update();
+}
+
+
+void Canvas::Clear()
+{
+ if(!_Layers.empty()) {
+ for (deque<StrokeLayer*>::iterator sl=_Layers.begin(), slend=_Layers.end();
+ sl != slend;
+ ++sl)
+ if (*sl)
+ delete (*sl);
+ _Layers.clear();
+ }
+
+ if(!_StyleModules.empty()) {
+ for (deque<StyleModule*>::iterator s=_StyleModules.begin(), send=_StyleModules.end();
+ s != send;
+ ++s)
+ if (*s)
+ delete (*s);
+ _StyleModules.clear();
+ }
+ if(_steerableViewMap)
+ _steerableViewMap->Reset();
+
+ stroke_count = 0;
+}
+
+void Canvas::Erase()
+{
+ if(!_Layers.empty())
+ {
+ for (deque<StrokeLayer*>::iterator sl=_Layers.begin(), slend=_Layers.end();
+ sl != slend;
+ ++sl)
+ if (*sl)
+ (*sl)->clear();
+ }
+ if(_steerableViewMap)
+ _steerableViewMap->Reset();
+ update();
+
+stroke_count = 0;
+}
+
+void Canvas::PushBackStyleModule(StyleModule *iStyleModule) {
+ StrokeLayer* layer = new StrokeLayer();
+ _StyleModules.push_back(iStyleModule);
+ _Layers.push_back(layer);
+}
+
+void Canvas::InsertStyleModule(unsigned index, StyleModule *iStyleModule) {
+ unsigned size = _StyleModules.size();
+ StrokeLayer* layer = new StrokeLayer();
+ if((_StyleModules.empty()) || (index == size)) {
+ _StyleModules.push_back(iStyleModule);
+ _Layers.push_back(layer);
+ return;
+ }
+ _StyleModules.insert(_StyleModules.begin() + index, iStyleModule);
+ _Layers.insert(_Layers.begin()+index, layer);
+}
+
+void Canvas::RemoveStyleModule(unsigned index)
+{
+ unsigned i=0;
+ if (!_StyleModules.empty())
+ {
+ for(deque<StyleModule*>::iterator s=_StyleModules.begin(), send=_StyleModules.end();
+ s!=send;
+ ++s)
+ {
+ if(i == index)
+ {
+ // remove shader
+ if (*s)
+ delete *s;
+ _StyleModules.erase(s);
+ break;
+ }
+ ++i;
+ }
+ }
+ i=0;
+ if(!_Layers.empty())
+ {
+ for(deque<StrokeLayer*>::iterator sl=_Layers.begin(), slend=_Layers.end();
+ sl!=slend;
+ ++sl)
+ {
+ if(i == index)
+ {
+ // remove layer
+ if (*sl)
+ delete *sl;
+ _Layers.erase(sl);
+ break;
+ }
+ ++i;
+ }
+ }
+}
+
+
+void Canvas::SwapStyleModules(unsigned i1, unsigned i2)
+{
+ StyleModule* tmp;
+ tmp = _StyleModules[i1];
+ _StyleModules[i1] = _StyleModules[i2];
+ _StyleModules[i2] = tmp;
+
+ StrokeLayer* tmp2;
+ tmp2 = _Layers[i1];
+ _Layers[i1] = _Layers[i2];
+ _Layers[i2] = tmp2;
+}
+
+void Canvas::ReplaceStyleModule(unsigned index, StyleModule *iStyleModule)
+{
+ unsigned i=0;
+ for(deque<StyleModule*>::iterator s=_StyleModules.begin(), send=_StyleModules.end();
+ s != send;
+ ++s)
+ {
+ if(i == index)
+ {
+ if (*s)
+ delete *s;
+ *s = iStyleModule;
+ break;
+ }
+ ++i;
+ }
+}
+
+void Canvas::setVisible(unsigned index, bool iVisible) {
+ _StyleModules[index]->setDisplayed(iVisible);
+}
+
+void Canvas::setModified(unsigned index, bool iMod)
+{
+ _StyleModules[index]->setModified(iMod);
+}
+
+void Canvas::resetModified(bool iMod/* =false */)
+{
+ unsigned size = _StyleModules.size();
+ for(unsigned i = 0; i < size; ++i)
+ setModified(i,iMod);
+}
+
+void Canvas::causalStyleModules(vector<unsigned>& vec, unsigned index) {
+ unsigned size = _StyleModules.size();
+
+ for(unsigned i = index; i < size; ++i)
+ if (_StyleModules[i]->getCausal())
+ vec.push_back(i);
+}
+
+void Canvas::Render(const StrokeRenderer *iRenderer)
+{
+ for (unsigned i = 0; i < _StyleModules.size(); i++) {
+ if(!_StyleModules[i]->getDisplayed() || !_Layers[i])
+ continue;
+ _Layers[i]->Render(iRenderer);
+ }
+}
+
+void Canvas::RenderBasic(const StrokeRenderer *iRenderer)
+
+{
+ for (unsigned i = 0; i < _StyleModules.size(); i++) {
+ if(!_StyleModules[i]->getDisplayed() || !_Layers[i])
+ continue;
+ _Layers[i]->RenderBasic(iRenderer);
+ }
+}
+
+void Canvas::loadMap(const char *iFileName, const char *iMapName, unsigned int iNbLevels, float iSigma){
+ // check whether this map was already loaded:
+ if(!_maps.empty()){
+ mapsMap::iterator m = _maps.find(iMapName);
+ if(m!=_maps.end()){
+ // lazy check for size changes
+ ImagePyramid * pyramid = (*m).second;
+ if((pyramid->width() != width()) || (pyramid->height() != height())){
+ delete pyramid;
+ }else{
+ return;
+ }
+ }
+ }
+ string filePath;
+ if(_MapsPath){
+ filePath = _MapsPath;
+ filePath += iFileName;
+ }else{
+ filePath = iFileName;
+ }
+
+ //soc
+ // QImage *qimg;
+ // QImage newMap(filePath.c_str());
+ // if(newMap.isNull()){
+ // cout << "Could not load image file " << filePath << endl;
+ // return;
+ // }
+ // qimg = &newMap;
+ ImBuf *qimg = IMB_loadiffname(filePath.c_str(), 0);;
+ if( qimg == 0 ){
+ cout << "Could not load image file " << filePath << endl;
+ return;
+ }
+
+ // soc
+ //resize
+ // QImage scaledImg;
+ // if((newMap.width()!=width()) || (newMap.height()!=height())){
+ // scaledImg = newMap.scaled(width(), height(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
+ // qimg = &scaledImg;
+ // }
+ ImBuf *scaledImg;
+ if( ( qimg->x != width() ) || ( qimg->y != height() ) ){
+ scaledImg = IMB_dupImBuf(qimg);
+ IMB_scaleImBuf(scaledImg, width(), height());
+ }
+
+
+ // deal with color image
+ // if(newMap->depth() != 8){
+ // int w = newMap->width();
+ // int h = newMap->height();
+ // QImage *tmp = new QImage(w, h, 8);
+ // for(unsigned y=0;y<h;++y){
+ // for(unsigned x=0;x<w;++x){
+ // int c = qGray(newMap->pixel(x,y));
+ // tmp->setPixel(x,y,c);
+ // }
+ // }
+ // delete newMap;
+ // newMap = tmp;
+ // }
+
+ int x,y;
+ int w = qimg->x;
+ int h = qimg->y;
+int rowbytes = w*4;
+ GrayImage tmp(w,h);
+ char *pix;
+
+ for(y=0; y<h;++y){
+ for(x=0;x<w;++x){
+ pix = (char*)qimg->rect + y*rowbytes + x*4;
+ float c = (pix[0]*11 + pix[1]*16 + pix[2]*5)/32;
+ tmp.setPixel(x,y,c);
+ }
+ }
+
+ // GrayImage blur(w,h);
+ // GaussianFilter gf(4.f);
+ // //int bound = gf.getBound();
+ // for(y=0; y<h;++y){
+ // for(x=0;x<w;++x){
+ // int c = gf.getSmoothedPixel<GrayImage>(&tmp, x,y);
+ // blur.setPixel(x,y,c);
+ // }
+ // }
+
+ GaussianPyramid *pyramid = new GaussianPyramid(tmp, iNbLevels, iSigma);
+ int ow = pyramid->width(0);
+ int oh = pyramid->height(0);
+ string base(iMapName); //soc
+ for(int i=0; i<pyramid->getNumberOfLevels(); ++i){
+ // save each image:
+ // w = pyramid.width(i);
+ // h = pyramid.height(i);
+
+ //soc QImage qtmp(ow, oh, QImage::Format_RGB32);
+ ImBuf *qtmp = IMB_allocImBuf(ow, oh, 32, IB_rect, 0);
+
+//int k = (1<<i);
+ for(y=0;y<oh;++y){
+ for(x=0;x<ow;++x){
+ int c = pyramid->pixel(x,y,i);//255*pyramid->pixel(x,y,i);
+ //soc qtmp.setPixel(x,y,qRgb(c,c,c));
+ pix = (char*)qtmp->rect + y*rowbytes + x*4;
+ pix[0] = pix [1] = pix[2] = c;
+ }
+ }
+ //soc qtmp.save(base+QString::number(i)+".bmp", "BMP");
+ stringstream filename;
+ filename << base;
+ filename << i << ".bmp";
++ qtmp->ftype= BMP;
++ IMB_saveiff(qtmp, const_cast<char *>(filename.str().c_str()), IB_rect);
+
+ }
+ // QImage *qtmp = new QImage(w, h, 32);
+ // for(y=0;y<h;++y){
+ // for(x=0;x<w;++x){
+ // int c = (int)blur.pixel(x,y);
+ // qtmp->setPixel(x,y,qRgb(c,c,c));
+ // }
+ // }
+ // delete newMap;
+ // newMap = qtmp;
+ //
+ _maps[iMapName] = pyramid;
+ // newMap->save("toto.bmp", "BMP");
+}
+
+float Canvas::readMapPixel(const char *iMapName, int level, int x, int y){
+ if(_maps.empty()){
+ cout << "readMapPixel warning: no map was loaded "<< endl;
+ return -1;
+ }
+ mapsMap::iterator m = _maps.find(iMapName);
+ if(m==_maps.end()){
+ cout << "readMapPixel warning: no map was loaded with the name " << iMapName << endl;
+ return -1;
+ }
+ ImagePyramid *pyramid = (*m).second;
+ if((x<0) || (x>=pyramid->width()) || (y<0) || (y>=pyramid->height()))
+ return 0;
+
+ return pyramid->pixel(x,height()-1-y,level);
+}
--- /dev/null
- #include "intern/IMB_png.h"
+//
+// Copyright (C) : Please refer to the COPYRIGHT file distributed
+// with this source distribution.
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+//
+///////////////////////////////////////////////////////////////////////////////
+
+#include "Silhouette.h"
+#include "SteerableViewMap.h"
+#include "../image/ImagePyramid.h"
+#include "../image/Image.h"
+#include <math.h>
+#include "../geometry/Geom.h"
+using namespace Geometry;
+
+//soc #include <qstring.h>
+//soc #include <qimage.h>
+#include <sstream>
+
+extern "C" {
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
-
- imb_savepng(ibuf, const_cast<char *>(filename.str().c_str()), 0);
+}
+
+SteerableViewMap::SteerableViewMap(unsigned int nbOrientations){
+ _nbOrientations = nbOrientations;
+ _bound = cos(M_PI/(float)_nbOrientations);
+ for(unsigned i=0; i<_nbOrientations; ++i){
+ _directions.push_back(Vec2d(cos((float)i*M_PI/(float)_nbOrientations), sin((float)i*M_PI/(float)_nbOrientations)));
+ }
+ Build();
+}
+
+void SteerableViewMap::Build(){
+ _imagesPyramids = new ImagePyramid*[_nbOrientations+1]; // one more map to store the complete visible VM
+ memset((_imagesPyramids),0,(_nbOrientations+1)*sizeof(ImagePyramid*));
+}
+
+SteerableViewMap::SteerableViewMap(const SteerableViewMap& iBrother){
+ _nbOrientations = iBrother._nbOrientations;
+ unsigned i;
+ _bound = iBrother._bound;
+ _directions = iBrother._directions;
+ _mapping = iBrother._mapping;
+ _imagesPyramids = new ImagePyramid*[_nbOrientations+1]; // one more map to store the complete visible VM
+ for(i=0;i<_nbOrientations+1;++i)
+ _imagesPyramids[i] = new GaussianPyramid(*(dynamic_cast<GaussianPyramid*>(iBrother._imagesPyramids[i])));
+}
+
+SteerableViewMap::~SteerableViewMap(){
+ Clear();
+}
+
+void SteerableViewMap::Clear(){
+ unsigned i;
+ if(_imagesPyramids){
+ for(i=0; i<=_nbOrientations; ++i){
+ if(_imagesPyramids[i])
+ delete (_imagesPyramids)[i];
+ }
+ delete [] _imagesPyramids;
+ _imagesPyramids = 0;
+ }
+ if(!_mapping.empty()){
+ for(map<unsigned int, double*>::iterator m=_mapping.begin(), mend=_mapping.end();
+ m!=mend;
+ ++m){
+ delete [] (*m).second;
+ }
+ _mapping.clear();
+ }
+}
+
+void SteerableViewMap::Reset(){
+ Clear();
+ Build();
+}
+
+double SteerableViewMap::ComputeWeight(const Vec2d& dir, unsigned i){
+ double dotp = fabs(dir*_directions[i]);
+ if(dotp < _bound)
+ return 0;
+ if(dotp>1)
+ dotp = 1;
+
+ return cos((float)_nbOrientations/2.0*acos(dotp));
+}
+
+double * SteerableViewMap::AddFEdge(FEdge *iFEdge){
+ unsigned i;
+ unsigned id = iFEdge->getId().getFirst();
+ map<unsigned int, double* >::iterator o = _mapping.find(id);
+ if(o!=_mapping.end()){
+ return (*o).second;
+ }
+ double * res = new double[_nbOrientations];
+ for(i=0; i<_nbOrientations; ++i){
+ res[i] = 0;
+ }
+ Vec3r o2d3 = iFEdge->orientation2d();
+ Vec2r o2d2(o2d3.x(), o2d3.y());
+ real norm = o2d2.norm();
+ if(norm < 1e-6){
+ return res;
+ }
+ o2d2/=norm;
+
+ for(i=0; i<_nbOrientations; ++i){
+ res[i] = ComputeWeight(o2d2, i);
+ }
+ _mapping[id] = res;
+ return res;
+}
+
+unsigned SteerableViewMap::getSVMNumber(const Vec2f& orient){
+ Vec2f dir(orient);
+ //soc unsigned res = 0;
+ real norm = dir.norm();
+ if(norm < 1e-6){
+ return _nbOrientations+1;
+ }
+ dir/=norm;
+ double maxw = 0.f;
+ unsigned winner = _nbOrientations+1;
+ for(unsigned i=0; i<_nbOrientations; ++i){
+ double w = ComputeWeight(dir, i);
+ if(w>maxw){
+ maxw = w;
+ winner = i;
+ }
+ }
+ return winner;
+}
+
+
+unsigned SteerableViewMap::getSVMNumber(unsigned id){
+ map<unsigned int, double* >::iterator o = _mapping.find(id);
+ if(o!=_mapping.end()){
+ double* wvalues= (*o).second;
+ double maxw = 0.f;
+ unsigned winner = _nbOrientations+1;
+ for(unsigned i=0; i<_nbOrientations; ++i){
+ double w = wvalues[i];
+ if(w>maxw){
+ maxw = w;
+ winner = i;
+ }
+ }
+ return winner;
+ }
+ return _nbOrientations+1;
+}
+
+void SteerableViewMap::buildImagesPyramids(GrayImage **steerableBases, bool copy, unsigned iNbLevels, float iSigma){
+ for(unsigned i=0; i<=_nbOrientations; ++i){
+ ImagePyramid * svm = (_imagesPyramids)[i];
+ if(svm)
+ delete svm;
+ if(copy)
+ svm = new GaussianPyramid(*(steerableBases[i]), iNbLevels, iSigma);
+ else
+ svm = new GaussianPyramid(steerableBases[i], iNbLevels, iSigma);
+ _imagesPyramids[i] = svm;
+ }
+}
+
+float SteerableViewMap::readSteerableViewMapPixel(unsigned iOrientation, int iLevel, int x, int y){
+ ImagePyramid *pyramid = _imagesPyramids[iOrientation];
+ if(pyramid==0){
+ cout << "Warning: this steerable ViewMap level doesn't exist" << endl;
+ return 0;
+ }
+ if((x<0) || (x>=pyramid->width()) || (y<0) || (y>=pyramid->height()))
+ return 0;
+ //float v = pyramid->pixel(x,pyramid->height()-1-y,iLevel)*255.f;
+ float v = pyramid->pixel(x,pyramid->height()-1-y,iLevel)/32.f; // we encode both the directionality and the lines counting on 8 bits
+ // (because of frame buffer). Thus, we allow until 8 lines to pass through
+ // the same pixel, so that we can discretize the Pi/_nbOrientations angle into
+ // 32 slices. Therefore, for example, in the vertical direction, a vertical line
+ // will have the value 32 on each pixel it passes through.
+ return v;
+}
+
+float SteerableViewMap::readCompleteViewMapPixel(int iLevel, int x, int y){
+ return readSteerableViewMapPixel(_nbOrientations,iLevel,x,y);
+}
+
+unsigned int SteerableViewMap::getNumberOfPyramidLevels() const{
+ if(_imagesPyramids[0])
+ return _imagesPyramids[0]->getNumberOfLevels();
+ return 0;
+}
+
+void SteerableViewMap::saveSteerableViewMap() const {
+ for(unsigned i=0; i<=_nbOrientations; ++i){
+ if(_imagesPyramids[i] == 0){
+ cerr << "SteerableViewMap warning: orientation " << i <<" of steerable View Map whas not been computed yet" << endl;
+ continue;
+ }
+ int ow = _imagesPyramids[i]->width(0);
+ int oh = _imagesPyramids[i]->height(0);
+
+ //soc QString base("SteerableViewMap");
+ string base("SteerableViewMap");
+ stringstream filename;
+
+ for(int j=0; j<_imagesPyramids[i]->getNumberOfLevels(); ++j){ //soc
+ float coeff = 1;//1/255.f; //100*255;//*pow(2,j);
+ //soc QImage qtmp(ow, oh, QImage::Format_RGB32);
+ ImBuf *ibuf = IMB_allocImBuf(ow, oh, 32, IB_rect, 0);
+ int rowbytes = ow*4;
+ char *pix;
+
+ for(int y=0;y<oh;++y){ //soc
+ for(int x=0;x<ow;++x){ //soc
+ int c = (int)(coeff*_imagesPyramids[i]->pixel(x,y,j));
+ if(c>255)
+ c=255;
+ //int c = (int)(_imagesPyramids[i]->pixel(x,y,j));
+
+ //soc qtmp.setPixel(x,y,qRgb(c,c,c));
+ pix = (char*)ibuf->rect + y*rowbytes + x*4;
+ pix[0] = pix [1] = pix[2] = c;
+ }
+ }
+
+ //soc qtmp.save(base+QString::number(i)+"-"+QString::number(j)+".png", "PNG");
+ filename << base;
+ filename << i << "-" << j << ".png";
++ ibuf->ftype= PNG;
++ IMB_saveiff(ibuf, const_cast<char *>(filename.str().c_str()), IB_rect);
+
+ }
+ // QString base("SteerableViewMap");
+ // for(unsigned j=0; j<_imagesPyramids[i]->getNumberOfLevels(); ++j){
+ // GrayImage * img = _imagesPyramids[i]->getLevel(j);
+ // int ow = img->width();
+ // int oh = img->height();
+ // float coeff = 1; //100*255;//*pow(2,j);
+ // QImage qtmp(ow, oh, 32);
+ // for(unsigned y=0;y<oh;++y){
+ // for(unsigned x=0;x<ow;++x){
+ // int c = (int)(coeff*img->pixel(x,y));
+ // if(c>255)
+ // c=255;
+ // //int c = (int)(_imagesPyramids[i]->pixel(x,y,j));
+ // qtmp.setPixel(x,y,qRgb(c,c,c));
+ // }
+ // }
+ // qtmp.save(base+QString::number(i)+"-"+QString::number(j)+".png", "PNG");
+ // }
+ //
+ }
+}