Es++ Class: OpenCVImageView
/******************************************************************************
 *
 * Copyright (c) 2017 Antillia.com TOSHIYUKI ARAI. ALL RIGHTS RESERVED.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *
 *  OpenCVImageView.h
 *
 *****************************************************************************/

#pragma once

//#include <es++/Exception.h>
#include <es++/ModuleFileName.h>
#include <es++/gtkmm-3.0/ImageView.h>
#include <es++/opencv-4.0/OpenCVImageConverter.h>

namespace Es {

class OpenCVImageView: public ImageView {

private:
  cv::Mat rgbImage;
  cv::Mat scaledImage;
  double  scale;

  static const int SCALING_MIN =  10; //10%
  static const int SCALING_MAX = 300; //300%

public:
  static int validateScale(int scalingRatio)
  {
    if (scalingRatio < SCALING_MIN) {
      scalingRatio = SCALING_MIN;
    }
    if (scalingRatio > SCALING_MAX) {
      scalingRatio = SCALING_MAX;
    }
    return scalingRatio;
  }

public:
  virtual void scaleImage(int scalingRatio)
  {
    try {
      scalingRatio = validateScale(scalingRatio);

      scale = (float)scalingRatio/100.0f;

      cv::resize(rgbImage, scaledImage, cv::Size(), 
        scale, scale);
      Glib::RefPtr<Gdk::Pixbuf> pixbuf(
           OpenCVImageConverter::convertToPixbuf(scaledImage) );

      if (pixbuf) {
        set_pixbuf(pixbuf) ;
        int w = pixbuf -> get_width();
        int h = pixbuf -> get_height();
        set_size_request(w, h);
      }
      refresh();

     } catch (cv::Exception& ex) {
       throw IException("Failed to cv::resize."); 
     }
  }

  virtual void scaleImage(cv::Mat& image, int scalingRatio)
  {
    try {
      scalingRatio = validateScale(scalingRatio);

      scale = (float)scalingRatio/100.0f;

      cv::resize(image, scaledImage, cv::Size(),
                scale, scale);
      Glib::RefPtr<Gdk::Pixbuf> pixbuf(
           OpenCVImageConverter::convertToPixbuf(scaledImage) );

      if (pixbuf) {
        set_pixbuf(pixbuf) ;
        int w = pixbuf -> get_width();
        int h = pixbuf -> get_height();
        set_size_request(w, h);
      }
      refresh();

    } catch (cv::Exception& ex) {
      throw Es::IException("Failed to cv::resize.");
    }
  }

  virtual void loadImage(const std::string& filename, int loadingFlag, 
                          int scalingRatio=100)
  {
    rgbImage = readImage(filename, loadingFlag);

    if (scalingRatio == 100) {
    
      Glib::RefPtr<Gdk::Pixbuf> pixbuf(  
           OpenCVImageConverter::convertToPixbuf(rgbImage) );

      if (pixbuf) {
        set_pixbuf(pixbuf) ;
        int w = pixbuf -> get_width();
        int h = pixbuf -> get_height();
        set_size_request(w, h);
      } else {
      }
    } else {
       //We have to resize the cv::Mat rgbImage to the scaledImage method, 
       //and get the corresponding Gdk::Pixbuf to the resized the cv::Mat.

       scaleImage(rgbImage, scalingRatio);
    }
  }
  
  cv::Mat readImage(const std::string& filename, int loadingFlag)
  {
    std::string abspath = filename;

    ModuleFileName module;

    Es::File file(filename);
   
    if (file.exists() == false) {
      const char* name = filename.c_str();
      if (name[0] != '/') {
        abspath = module.getPath();
        abspath += "/";
        abspath += filename;
      }
      Es::File afile(abspath);
      if (afile.exists() == false) {
        throw Es::IException("imagefile not found %s", abspath.c_str());
      } 
    }

    //printf("OpenCVImageView: imagefile %s\n", abspath.c_str());
    cv::Mat image = imread(abspath.c_str(), loadingFlag);
    if (image.empty()) {
      throw Es::IException("Failed to imread: %s", abspath.c_str() );
    }
    return image;
  }

public:
  OpenCVImageView()
  :ImageView()
  {
  }

  ~OpenCVImageView()
  {
    rgbImage.release();
  }
    
  virtual void save(const char* filename, const cv::Mat& image)
  {
    if (!image.empty()) {
      if (!imwrite(filename, image)) {
        throw IException("Failed to write image file: %s", filename);
      }
    }
  }

  cv::Mat& getOriginalImage()
  {
    return rgbImage;
  }

  cv::Mat getClone()
  {
    return rgbImage.clone();  
  }

  cv::Mat& getScaledImage()
  {
    return scaledImage;
  }

  void refresh()
  {
    queue_draw();    
  }

  void setImage(cv::Mat& image)
  {
    rgbImage = image;
  }
};

}