QgsGlobeTileImage::QgsGlobeTileImage( QgsGlobeTileSource* tileSource, const QgsRectangle& tileExtent, int tileSize , int tileLod )
    : osg::Image()
    , mTileSource( tileSource )
    , mTileExtent( tileExtent )
    , mTileSize( tileSize )
    , mLod( tileLod )
{
  mTileSource->addTile( this );
#ifdef GLOBE_SHOW_TILE_STATS
  QgsGlobeTileStatistics::instance()->updateTileCount( + 1 );
#endif
  mTileData = new unsigned char[mTileSize * mTileSize * 4];
  std::memset( mTileData, 0, mTileSize * mTileSize * 4 );
#if 0
  setImage( mTileSize, mTileSize, 1, 4, // width, height, depth, internal_format
            GL_BGRA, GL_UNSIGNED_BYTE,
            mTileData, osg::Image::NO_DELETE );

  mTileSource->mTileUpdateManager.addTile( const_cast<QgsGlobeTileImage*>( this ) );
  mDpi = 72;
#else
  QImage qImage( mTileData, mTileSize, mTileSize, QImage::Format_ARGB32_Premultiplied );
  QPainter painter( &qImage );
  QgsMapRendererCustomPainterJob job( createSettings( qImage.logicalDpiX(), mTileSource->mLayerSet ), &painter );
  job.renderSynchronously();

  setImage( mTileSize, mTileSize, 1, 4, // width, height, depth, internal_format
            GL_BGRA, GL_UNSIGNED_BYTE,
            mTileData, osg::Image::NO_DELETE );
  flipVertical();
  mDpi = qImage.logicalDpiX();
#endif
}
Example #2
0
static __inline__ void flip_fun(int flip, char * source, int srcWidth, int srcHeight, char * destination, int dstWidth, int dstHeight){
    int horiz = (flip & 1) != 0;
    int vert = (flip & 2) != 0;
    if (horiz && vert){
        int arr_len = dstWidth * dstHeight * sizeof(char) * 4;
        char* temp = (char *) malloc(arr_len);
        flipHorizontal(source, srcWidth, srcHeight, temp, dstWidth, dstHeight);
        flipVertical(temp, dstWidth, dstHeight, destination, dstWidth, dstHeight);
        free(temp);
        return;
    }
    if (horiz){
        flipHorizontal(source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
        return;
    }
    if (vert){
        flipVertical(source, srcWidth, srcHeight, destination, dstWidth, dstHeight);
        return;
    }
}
Example #3
0
Graphics::Surface *OpenGLSDriver::getViewportScreenshot() const {
	Graphics::Surface *s = new Graphics::Surface();
	s->create(_viewport.width(), _viewport.height(), getRGBAPixelFormat());

	glReadPixels(_viewport.left, g_system->getHeight() - _viewport.bottom, _viewport.width(), _viewport.height(),
	             GL_RGBA, GL_UNSIGNED_BYTE, s->getPixels());

	flipVertical(s);

	return s;
}
Example #4
0
Graphics::Surface *OpenGLRenderer::getScreenshot() {
	Common::Rect screen = viewport();

	Graphics::Surface *s = new Graphics::Surface();
	s->create(screen.width(), screen.height(), Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));

	glReadPixels(screen.left, screen.top, screen.width(), screen.height(), GL_RGBA, GL_UNSIGNED_BYTE, s->getPixels());

	flipVertical(s);

	return s;
}
	bool renderTheme(
		float                   s  = 8.0f,
		float                   lw = 2.0f,
		const osgWidget::Color& lc = osgWidget::Color(0.0f, 0.0f, 0.0f, 1.0f),
		const osgWidget::Color& fc = osgWidget::Color(1.0f, 1.0f, 1.0f, 1.0f)
	) {
		if(!allocateSurface(s * 8.0f, s)) return false;

		if(!createContext()) return false;

		// Some variables to make this easier to work with...
		float r      = s * 0.75f;
		float top    = osg::PI + osg::PI_2;
		float bottom = osg::PI_2;
		float left   = osg::PI;
		float right  = 0.0f;

		setLineWidth(lw * 2.0f);

		// Create the upper-left region.
		arc(s, s, r, left, top);

		_finalizeCorner(s, s, lc, fc);

		// Create the top border region.
		_drawBorder(s + (s - r), 0.0f, r, s, 0.0f, s, lc, fc);

		// Create the upper-right region.
		arc(s * 2.0f, s, r, top, right);
		_finalizeCorner(s * 2.0f, s, lc, fc);

		// Create the left border.
		_drawBorder((s * 3.0f) + (s - r), 0.0f, r, s, 0.0f, s, lc, fc);

		// Create the right border.
		_drawBorder((s * 4.0f), 0.0f, r, s, r, s, lc, fc);
		fill();

		// Create the bottom-left region.
		arc(s * 6.0f, 0.0f, r, bottom, left);
		_finalizeCorner(s * 6.0f, 0.0f, lc, fc);

		// Create the bottom border region.
		_drawBorder((s * 6.0f), 0.0f, r, s, r, s, lc, fc);

		// Create the bottom-right region.
		arc(s * 7.0f, 0.0f, r, right, bottom);
		_finalizeCorner(s * 7.0f, 0.0f, lc, fc);

		flipVertical();

		return true;
	}
void QgsGlobeTileImage::update( osg::NodeVisitor * )
{
  if ( !mUpdatedImage.isNull() )
  {
    QgsDebugMsg( QString( "Updating earth tile image: %1" ).arg( mTileExtent.toString( 5 ) ) );
    std::memcpy( mTileData, mUpdatedImage.bits(), mTileSize * mTileSize * 4 );
    setImage( mTileSize, mTileSize, 1, 4, // width, height, depth, internal_format
              GL_BGRA, GL_UNSIGNED_BYTE,
              mTileData, osg::Image::NO_DELETE );
    flipVertical();
    mUpdatedImage = QImage();
  }
}
Example #7
0
void
ImageSearch::search() {
    search(subImage);
    if (isMask) {
	PNG flippedMask = flipVertical(subImage);
	search(flippedMask);
	PNG rotImg = rotate90(subImage);
	search(rotImg);
	rotImg.write("rotated_mask.png");
	rotImg = rotate90(rotImg);
	rotImg = rotate90(rotImg);
	search(rotImg);
    }
}
Graphics::Surface *ShaderRenderer::getScreenshot() {
	Common::Rect screen = viewport();

	Graphics::Surface *s = new Graphics::Surface();
	s->create(screen.width(), screen.height(), Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24));

#if defined(USE_GLES2)
	GLenum format = GL_UNSIGNED_BYTE;
#else
	GLenum format = GL_UNSIGNED_INT_8_8_8_8_REV;
#endif

	glReadPixels(screen.left, screen.top, screen.width(), screen.height(), GL_RGBA, format, s->getPixels());

#if defined(USE_GLES2) && defined(SCUMM_BIG_ENDIAN)
	// OpenGL ES does not support the GL_UNSIGNED_INT_8_8_8_8_REV texture format, we need to byteswap the surface
	OpenGLTexture::byteswapSurface(s);
#endif

	flipVertical(s);

	return s;
}
Example #9
0
bool CTargaImage::load(char *filename) {
  FILE *pFile = fopen(filename, "rb");

  if (!pFile)
    return false;

  tgaheader_t tgaHeader;

  // read the TGA header
  fread(&tgaHeader, 1, sizeof(tgaheader_t), pFile);

  // see if the image type is one that we support (RGB, RGB RLE, GRAYSCALE, GRAYSCALE RLE)
  if (((tgaHeader.imageTypeCode != TGA_RGB)
      && (tgaHeader.imageTypeCode != TGA_GRAYSCALE)
      && (tgaHeader.imageTypeCode != TGA_RGB_RLE)
      && (tgaHeader.imageTypeCode != TGA_GRAYSCALE_RLE))
      || tgaHeader.colorMapType != 0) {
    fclose(pFile);
    return false;
  }

  // get image width and height
  m_width = tgaHeader.width;
  m_height = tgaHeader.height;

  // colormode -> 3 = BGR, 4 = BGRA
  int colorMode = tgaHeader.bpp / 8;

  // we don't handle less than 24 bit
  if (colorMode < 3) {
    fclose(pFile);
    return false;
  }

  m_imageSize = m_width * m_height * colorMode;

  // allocate memory for TGA image data
  m_pImageData = new unsigned char[m_imageSize];

  // skip past the id if there is one
  if (tgaHeader.idLength > 0)
    fseek(pFile, SEEK_CUR, tgaHeader.idLength);

  // read image data
  if (tgaHeader.imageTypeCode == TGA_RGB
      || tgaHeader.imageTypeCode == TGA_GRAYSCALE) {
    fread(m_pImageData, 1, m_imageSize, pFile);
  } else {
    // this is an RLE compressed image
    unsigned char id;
    unsigned char length;
    rgba_t color = { 0, 0, 0, 0 };
    unsigned int i = 0;

    while (i < m_imageSize) {
      id = fgetc(pFile);

      // see if this is run length data
      if (id >= 128)  // & 0x80)
          {
        // find the run length
        length = (unsigned char) (id - 127);

        // next 3 (or 4) bytes are the repeated values
        color.b = (unsigned char) fgetc(pFile);
        color.g = (unsigned char) fgetc(pFile);
        color.r = (unsigned char) fgetc(pFile);

        if (colorMode == 4)
          color.a = (unsigned char) fgetc(pFile);

        // save everything in this run
        while (length > 0) {
          m_pImageData[i++] = color.b;
          m_pImageData[i++] = color.g;
          m_pImageData[i++] = color.r;

          if (colorMode == 4)
            m_pImageData[i++] = color.a;

          --length;
        }
      } else {
        // the number of non RLE pixels
        length = (unsigned char) (id + 1);

        while (length > 0) {
          color.b = (unsigned char) fgetc(pFile);
          color.g = (unsigned char) fgetc(pFile);
          color.r = (unsigned char) fgetc(pFile);

          if (colorMode == 4)
            color.a = (unsigned char) fgetc(pFile);

          m_pImageData[i++] = color.b;
          m_pImageData[i++] = color.g;
          m_pImageData[i++] = color.r;

          if (colorMode == 4)
            m_pImageData[i++] = color.a;

          --length;
        }
      }
    }
  }

  fclose(pFile);

  switch (tgaHeader.imageTypeCode) {
    case TGA_RGB:
    case TGA_RGB_RLE:
      if (3 == colorMode) {
        m_imageDataFormat = IMAGE_RGB;
        m_imageDataType = IMAGE_DATA_UNSIGNED_BYTE;
        m_colorDepth = 24;
      } else {
        m_imageDataFormat = IMAGE_RGBA;
        m_imageDataType = IMAGE_DATA_UNSIGNED_BYTE;
        m_colorDepth = 32;
      }
      break;

    case TGA_GRAYSCALE:
    case TGA_GRAYSCALE_RLE:
      m_imageDataFormat = IMAGE_LUMINANCE;
      m_imageDataType = IMAGE_DATA_UNSIGNED_BYTE;
      m_colorDepth = 8;
      break;
  }

  if ((tgaHeader.imageDesc & TOP_LEFT) == TOP_LEFT)
    flipVertical();

  // swap the red and blue components in the image data
  swapRedBlue();

  return (m_pImageData != NULL);
}
Example #10
0
FITSViewer::FITSViewer (QWidget *parent)
        : KXmlGuiWindow (parent)
{
#ifdef Q_OS_OSX
    if(Options::independentWindowFITS())
         setWindowFlags(Qt::Window);
     else{
        setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint);
        connect(QApplication::instance(), SIGNAL(applicationStateChanged(Qt::ApplicationState)), this, SLOT(changeAlwaysOnTop(Qt::ApplicationState)));
     }
#endif

    fitsTab   = new QTabWidget(this);
    undoGroup = new QUndoGroup(this);

    fitsID = 0;
    debayerDialog= NULL;
    markStars = false;

    lastURL = QUrl(QDir::homePath());

    fitsTab->setTabsClosable(true);

    setWindowIcon(QIcon::fromTheme("kstars_fitsviewer", QIcon(":/icons/breeze/default/kstars_fitsviewer.svg")));

    setCentralWidget(fitsTab);

    connect(fitsTab, SIGNAL(currentChanged(int)), this, SLOT(tabFocusUpdated(int)));
    connect(fitsTab, SIGNAL(tabCloseRequested(int)), this, SLOT(closeTab(int)));

    //These two connections will enable or disable the scope button if a scope is available or not.
    //Of course this is also dependent on the presence of WCS data in the image.

    #ifdef HAVE_INDI
    connect(INDIListener::Instance(), SIGNAL(newTelescope(ISD::GDInterface *)), this, SLOT(updateWCSFunctions()));
    connect(INDIListener::Instance(), SIGNAL(deviceRemoved(ISD::GDInterface *)), this, SLOT(updateWCSFunctions()));
    #endif

    led.setColor(Qt::green);

    fitsPosition.setAlignment(Qt::AlignCenter);
    fitsValue.setAlignment(Qt::AlignCenter);

    //fitsPosition.setFixedWidth(100);
    //fitsValue.setFixedWidth(100);
    fitsWCS.setVisible(false);

    statusBar()->insertPermanentWidget(FITS_WCS, &fitsWCS);
    statusBar()->insertPermanentWidget(FITS_VALUE, &fitsValue);
    statusBar()->insertPermanentWidget(FITS_POSITION, &fitsPosition);
    statusBar()->insertPermanentWidget(FITS_ZOOM, &fitsZoom);
    statusBar()->insertPermanentWidget(FITS_RESOLUTION, &fitsResolution);
    statusBar()->insertPermanentWidget(FITS_LED, &led);

    QAction *action;

    action = actionCollection()->addAction("rotate_right", this, SLOT(rotateCW()));
    action->setText(i18n("Rotate Right"));
    action->setIcon(QIcon::fromTheme("object-rotate-right", QIcon(":/icons/breeze/default/object-rotate-right.svg")));

    action = actionCollection()->addAction("rotate_left", this, SLOT(rotateCCW()));
    action->setText(i18n("Rotate Left"));
    action->setIcon(QIcon::fromTheme("object-rotate-left", QIcon(":/icons/breeze/default/object-rotate-left.svg")));

    action = actionCollection()->addAction("flip_horizontal", this, SLOT(flipHorizontal()));
    action->setText(i18n("Flip Horizontal"));
    action->setIcon(QIcon::fromTheme("object-flip-horizontal", QIcon(":/icons/breeze/default/object-flip-horizontal.svg")));

    action = actionCollection()->addAction("flip_vertical", this, SLOT(flipVertical()));
    action->setText(i18n("Flip Vertical"));
    action->setIcon(QIcon::fromTheme("object-flip-vertical", QIcon(":/icons/breeze/default/object-flip-vertical.svg")));

    action = actionCollection()->addAction("image_histogram");
    action->setText(i18n("Histogram"));
    connect(action, SIGNAL(triggered(bool)), SLOT (histoFITS()));
    actionCollection()->setDefaultShortcut(action, QKeySequence::Replace);

    action->setIcon(QIcon(":/icons/histogram.png"));

    action = KStandardAction::open(this,   SLOT(openFile()),   actionCollection());
    action->setIcon(QIcon::fromTheme("document-open", QIcon(":/icons/breeze/default/document-open.svg")));
    
    saveFileAction    = KStandardAction::save(this,   SLOT(saveFile()),   actionCollection());
    saveFileAction->setIcon(QIcon::fromTheme("document-save", QIcon(":/icons/breeze/default/document-save.svg")));

    action=saveFileAsAction  = KStandardAction::saveAs(this, SLOT(saveFileAs()), actionCollection());
    saveFileAsAction->setIcon(QIcon::fromTheme("document-save_as", QIcon(":/icons/breeze/default/document-save-as.svg")));

    action = actionCollection()->addAction("fits_header");
    actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::Key_H));
    action->setIcon(QIcon::fromTheme("document-properties", QIcon(":/icons/breeze/default/document-properties.svg")));
    action->setText(i18n( "FITS Header"));
    connect(action, SIGNAL(triggered(bool) ), SLOT(headerFITS()));

    action = actionCollection()->addAction("fits_debayer");
    actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL+Qt::Key_D));
    action->setIcon(QIcon::fromTheme("view-preview", QIcon(":/icons/breeze/default/view-preview.svg")));
    action->setText(i18n( "Debayer..."));
    connect(action, SIGNAL(triggered(bool) ), SLOT(debayerFITS()));

    action = actionCollection()->addAction("image_stretch");
    action->setText(i18n("Auto stretch"));
    connect(action, SIGNAL(triggered(bool)), SLOT (stretchFITS()));
    actionCollection()->setDefaultShortcut(action, QKeySequence::SelectAll);
    action->setIcon(QIcon::fromTheme("transform-move", QIcon(":/icons/breeze/default/transform-move.svg")));

    action = KStandardAction::close(this,  SLOT(close()),  actionCollection());
    action->setIcon(QIcon::fromTheme("window-close", QIcon(":/icons/breeze/default/window-close.svg")));
    
    action = KStandardAction::copy(this,   SLOT(copyFITS()),   actionCollection());
    action->setIcon(QIcon::fromTheme("edit-copy", QIcon(":/icons/breeze/default/edit-copy.svg")));
    
    action=KStandardAction::zoomIn(this,     SLOT(ZoomIn()),      actionCollection());
    action->setIcon(QIcon::fromTheme("zoom-in", QIcon(":/icons/breeze/default/zoom-in.svg")));
    
    action=KStandardAction::zoomOut(this,    SLOT(ZoomOut()),     actionCollection());
    action->setIcon(QIcon::fromTheme("zoom-out", QIcon(":/icons/breeze/default/zoom-out.svg")));
    
    action=KStandardAction::actualSize(this, SLOT(ZoomDefault()), actionCollection());
    action->setIcon(QIcon::fromTheme("zoom-fit-best", QIcon(":/icons/breeze/default/zoom-fit-best.svg")));

    QAction *kundo = KStandardAction::undo(undoGroup, SLOT(undo()), actionCollection());
    kundo->setIcon(QIcon::fromTheme("edit-undo", QIcon(":/icons/breeze/default/edit-undo.svg")));
    
    QAction *kredo = KStandardAction::redo(undoGroup, SLOT(redo()), actionCollection());
    kredo->setIcon(QIcon::fromTheme("edit-redo", QIcon(":/icons/breeze/default/edit-redo.svg")));

    connect(undoGroup, SIGNAL(canUndoChanged(bool)), kundo, SLOT(setEnabled(bool)));
    connect(undoGroup, SIGNAL(canRedoChanged(bool)), kredo, SLOT(setEnabled(bool)));

    action = actionCollection()->addAction("image_stats");
    action->setIcon(QIcon::fromTheme("view-statistics", QIcon(":/icons/breeze/default/view-statistics.svg")));
    action->setText(i18n( "Statistics"));
    connect(action, SIGNAL(triggered(bool)), SLOT(statFITS()));

    action = actionCollection()->addAction("view_crosshair");
    action->setIcon(QIcon::fromTheme("crosshairs", QIcon(":/icons/breeze/default/crosshairs.svg")));
    action->setText(i18n( "Show Cross Hairs"));
    action->setCheckable(true);
    connect(action, SIGNAL(triggered(bool)), SLOT(toggleCrossHair()));

    action = actionCollection()->addAction("view_pixel_grid");
    action->setIcon(QIcon::fromTheme("map-flat", QIcon(":/icons/breeze/default/map-flat.svg")));
    action->setText(i18n( "Show Pixel Gridlines"));
    action->setCheckable(true);
    connect(action, SIGNAL(triggered(bool)), SLOT(togglePixelGrid()));

    action = actionCollection()->addAction("view_eq_grid");
    action->setIcon(QIcon::fromTheme("kstars_grid", QIcon(":/icons/breeze/default/kstars_grid.svg")));
    action->setText(i18n( "Show Equatorial Gridlines"));
    action->setCheckable(true);
    action->setDisabled(true);
    connect(action, SIGNAL(triggered(bool)), SLOT(toggleEQGrid()));

    action = actionCollection()->addAction("view_objects");
    action->setIcon(QIcon::fromTheme("help-hint", QIcon(":/icons/breeze/default/help-hint.svg")));
    action->setText(i18n( "Show Objects in Image"));
    action->setCheckable(true);
    action->setDisabled(true);
    connect(action, SIGNAL(triggered(bool)), SLOT(toggleObjects()));

    action = actionCollection()->addAction("center_telescope");
    action->setIcon(QIcon(":/icons/center_telescope.svg"));
    action->setText(i18n( "Center Telescope\n*No Telescopes Detected*"));
    action->setDisabled(true);
    action->setCheckable(true);
    connect(action, SIGNAL(triggered(bool)), SLOT(centerTelescope()));

    action = actionCollection()->addAction("view_zoom_fit");
    action->setIcon(QIcon::fromTheme("zoom-fit-width", QIcon(":/icons/breeze/default/zoom-fit-width.svg")));
    action->setText(i18n( "Zoom To Fit"));
    connect(action, SIGNAL(triggered(bool)), SLOT(ZoomToFit()));

    action = actionCollection()->addAction("mark_stars");
    action->setText(i18n( "Mark Stars"));
    connect(action, SIGNAL(triggered(bool)), SLOT(toggleStars()));

    QSignalMapper *filterMapper = new QSignalMapper(this);

    int filterCounter=1;

    foreach(QString filter, FITSViewer::filterTypes)
    {

        action = actionCollection()->addAction(QString("filter%1").arg(filterCounter));
        action->setText(i18n(filter.toUtf8().constData()));
        filterMapper->setMapping(action, filterCounter++);
        connect(action, SIGNAL(triggered()), filterMapper, SLOT(map()));


    }
Example #11
0
int main(void)
{

    try
    {


        glPixelStorei(GL_UNPACK_ALIGNMENT,1);
        glPixelStorei(GL_PACK_ALIGNMENT,1);

        GLWindow::Settings setts;

        setts.width = screenWidth;
        setts.height = screenHeight;

        setts.match_resolution_exactly=true;
        setts.is_fullscreen=false;

        setts.glversion_major= 2;
        setts.glversion_minor= 1.0;

        GLWindow win(setts);

        glewInit();

        LDRImage noiseImg = loadImage("noise.png",1);

        flipVertical(noiseImg);

        glPixelStorei(GL_UNPACK_ALIGNMENT,1);
        glPixelStorei(GL_PACK_ALIGNMENT,1);

        GLTexture inputTex(noiseImg);

        int width = noiseImg.getDimensions()[1];
        int height = noiseImg.getDimensions()[2];

        std::cout<<"W H "<<width<<" "<<height<<std::endl;

        glBindTexture(GL_TEXTURE_2D, inputTex.tex);

        writeImage(noiseImg, "noiseOut_test.png");

        GL::GLShader median2("median.vert", "medianfilter2.frag");
        median2.bind();
        median2.setTexture("tex", 0);
        median2.setUniform("invTexDimensions", 1.0f / float(width), 1.0f / float(height));

        GL::GLShader median3("median.vert", "medianfilter3.frag");
        median3.bind();
        median3.setTexture("tex", 0);
        median3.setUniform("invTexDimensions", 1.0f / float(width), 1.0f / float(height));

        GL::GLShader median4("median.vert", "medianfilter4.frag");
        median4.bind();
        median4.setTexture("tex", 0);
        median4.setUniform("invTexDimensions", 1.0f / float(width), 1.0f / float(height));

        GL::GLShader median5("median.vert", "medianfilter5.frag");
        median5.bind();
        median5.setTexture("tex", 0);
        median5.setUniform("invTexDimensions", 1.0f / float(width), 1.0f / float(height));

        median4.bind();


std::cout<<"BEGIN "<<std::endl;
        {

            while(win.isValid())
            {

      /*
#ifndef GL_ES_BUILD
                const GLenum buffers[]= {GL_COLOR_ATTACHMENT0, GL_BACK_LEFT};

                glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

                glDrawBuffers(1, &buffers[1]);

#else*/


                glBindFramebuffer(GL_FRAMEBUFFER, 0);
//#endif




            glClearColor(1,0,0,1);
            glClear(GL_COLOR_BUFFER_BIT);

            screenQuad();

            //    checkFBOErrors();


                win.update();
                win.flush();
            }

        }//scope

    }
    catch(const std::exception& e)
    {

        std::cout<<e.what()<<std::endl;
    }
    catch(const std::string& str)
    {
        std::cout<<str<<std::endl;
    }
    return 0;






}