Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "texception.h"
Toshihiro Shimizu 890ddd
#include "stdfx.h"
Toshihiro Shimizu 890ddd
#include "tpixelutils.h"
Toshihiro Shimizu 890ddd
#include "tbasefx.h"
Toshihiro Shimizu 890ddd
#include "tfxparam.h"
Toshihiro Shimizu 890ddd
#include "trop.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "tparamset.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//===================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//    Local namespace stuff
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
enum Status {
Shinya Kitaoka 120a6e
  NoPortsConnected = 0,
Shinya Kitaoka 120a6e
  Port0Connected   = 1 << 1,
Shinya Kitaoka 120a6e
  Port1Connected   = 1 << 2,
Shinya Kitaoka 120a6e
  StatusGood       = Port0Connected | Port1Connected
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
inline Status operator|(const Status &l, const Status &r) {
Shinya Kitaoka 120a6e
  return Status(((int)l | (int)r));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
inline int operator&(const Status &l) { return int(l); }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
Status getFxStatus(const TRasterFxPort &port0, const TRasterFxPort &port1) {
Shinya Kitaoka 120a6e
  Status status                   = NoPortsConnected;
Shinya Kitaoka 120a6e
  if (port0.isConnected()) status = status | Port0Connected;
Shinya Kitaoka 120a6e
  if (port1.isConnected()) status = status | Port1Connected;
Shinya Kitaoka 120a6e
  return status;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//---------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void makeRectCoherent(TRectD &rect, const TPointD &pos) {
Shinya Kitaoka 120a6e
  rect -= pos;
Shinya Kitaoka 120a6e
  rect.x0 = tfloor(rect.x0);
Shinya Kitaoka 120a6e
  rect.y0 = tfloor(rect.y0);
Shinya Kitaoka 120a6e
  rect.x1 = tceil(rect.x1);
Shinya Kitaoka 120a6e
  rect.y1 = tceil(rect.y1);
Shinya Kitaoka 120a6e
  rect += pos;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//===================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//========================
Toshihiro Shimizu 890ddd
//    Glow functions
Toshihiro Shimizu 890ddd
//------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
template <typename t=""></typename>
Shinya Kitaoka 120a6e
void fade(TRasterPT<t> ras, double fade, T color)  // Why it is not in TRop..??</t>
Toshihiro Shimizu 890ddd
{
Shinya Kitaoka 120a6e
  if (fade <= 0.0) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  double maxChannelValueD = T::maxChannelValue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  assert(fade <= 1.0);
Shinya Kitaoka 120a6e
  ras->lock();
Shinya Kitaoka 120a6e
  for (int j = 0; j < ras->getLy(); j++) {
Shinya Kitaoka 120a6e
    T *pix    = ras->pixels(j);
Shinya Kitaoka 120a6e
    T *endPix = pix + ras->getLx();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (; pix < endPix; ++pix) {
Shinya Kitaoka 120a6e
      if (pix->m > 0) {
Shinya Kitaoka 120a6e
        if (pix->m == T::maxChannelValue) {
Shinya Kitaoka 120a6e
          pix->r = troundp(pix->r + fade * (color.r - pix->r));
Shinya Kitaoka 120a6e
          pix->g = troundp(pix->g + fade * (color.g - pix->g));
Shinya Kitaoka 120a6e
          pix->b = troundp(pix->b + fade * (color.b - pix->b));
Shinya Kitaoka 120a6e
          pix->m = troundp(pix->m + fade * (color.m - pix->m));
Shinya Kitaoka 120a6e
        } else {
Shinya Kitaoka 120a6e
          int val;
Shinya Kitaoka 120a6e
          double factor = pix->m / maxChannelValueD;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          val    = troundp(pix->r + fade * (color.r * factor - pix->r));
Shinya Kitaoka 120a6e
          pix->r = (val > T::maxChannelValue) ? T::maxChannelValue : val;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          val    = troundp(pix->g + fade * (color.g * factor - pix->g));
Shinya Kitaoka 120a6e
          pix->g = (val > T::maxChannelValue) ? T::maxChannelValue : val;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          val    = troundp(pix->b + fade * (color.b * factor - pix->b));
Shinya Kitaoka 120a6e
          pix->b = (val > T::maxChannelValue) ? T::maxChannelValue : val;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          val    = troundp(pix->m + fade * (color.m * factor - pix->m));
Shinya Kitaoka 120a6e
          pix->m = (val > T::maxChannelValue) ? T::maxChannelValue : val;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  ras->unlock();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=====================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=================
Toshihiro Shimizu 890ddd
//    Glow Fx
Toshihiro Shimizu 890ddd
//-----------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka d1f6c4
class GlowFx final : public TBaseRasterFx {
Shinya Kitaoka 120a6e
  FX_DECLARATION(GlowFx)
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TRasterFxPort m_lighted, m_light;
Shinya Kitaoka 120a6e
  TDoubleParamP m_value;
Shinya Kitaoka 120a6e
  TDoubleParamP m_brightness;
Shinya Kitaoka 120a6e
  TDoubleParamP m_fade;
Shinya Kitaoka 120a6e
  TPixelParamP m_color;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  GlowFx()
Shinya Kitaoka 120a6e
      : m_value(10.0)
Shinya Kitaoka 120a6e
      , m_brightness(100.0)
Shinya Kitaoka 120a6e
      , m_color(TPixel::White)
Shinya Kitaoka 120a6e
      , m_fade(0.0) {
Shinya Kitaoka 120a6e
    m_value->setMeasureName("fxLength");
Shinya Kitaoka 120a6e
    m_color->enableMatte(true);
Shinya Kitaoka 120a6e
    m_value->setValueRange(0, (std::numeric_limits<double>::max)());</double>
Shinya Kitaoka 120a6e
    m_brightness->setValueRange(0, (std::numeric_limits<double>::max)());</double>
Shinya Kitaoka 120a6e
    m_fade->setValueRange(0.0, 100.0);
Shinya Kitaoka 120a6e
    bindParam(this, "value", m_value);
Shinya Kitaoka 120a6e
    bindParam(this, "brightness", m_brightness);
Shinya Kitaoka 120a6e
    bindParam(this, "color", m_color);
Shinya Kitaoka 120a6e
    bindParam(this, "fade", m_fade);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    addInputPort("Light", m_light);
Shinya Kitaoka 120a6e
    addInputPort("Source", m_lighted);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  //---------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  ~GlowFx() {}
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  //---------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 38fd86
  bool doGetBBox(double frame, TRectD &bbox,
Shinya Kitaoka 38fd86
                 const TRenderSettings &info) override {
Rozhuk Ivan 823a31
    if (getActiveTimeRegion().contains(frame)) {
Shinya Kitaoka 120a6e
      if (m_light.isConnected()) {
Shinya Kitaoka 120a6e
        TRectD b0, b1;
Shinya Kitaoka 120a6e
        bool ret = m_light->doGetBBox(frame, b0, info);
Shinya Kitaoka 120a6e
        bbox     = b0.enlarge(tceil(m_value->getValue(frame)));
Shinya Kitaoka 120a6e
        if (m_lighted.isConnected()) {
Shinya Kitaoka 120a6e
          ret = ret && m_lighted->doGetBBox(frame, b1, info);
Shinya Kitaoka 120a6e
          bbox += b1;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
        return ret;
Rozhuk Ivan 823a31
      } else if (m_lighted.isConnected()) {
Shinya Kitaoka 120a6e
        return m_lighted->doGetBBox(frame, bbox, info);
Rozhuk Ivan 823a31
      }
Rozhuk Ivan 823a31
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    return false;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  //---------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  inline void buildLightRects(const TRectD &tileRect, TRectD &inRect,
Shinya Kitaoka 120a6e
                              TRectD &outRect, double blur) {
Shinya Kitaoka 120a6e
    if (inRect !=
Shinya Kitaoka 120a6e
        TConsts::infiniteRectD)  // Could be, if the input light is a zerary Fx
Shinya Kitaoka 120a6e
      makeRectCoherent(inRect, tileRect.getP00());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    int blurI = tceil(blur);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // It seems that the TRop::blur does wrong with these (cuts at the borders).
Shinya Kitaoka 120a6e
    // I don't know why - they would be best...
Shinya Kitaoka 120a6e
    // TRectD blurOutRect((lightRect).enlarge(blurI) * tileRect);
Shinya Kitaoka 120a6e
    // lightRect = ((tileRect).enlarge(blurI) * lightRect);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // So we revert to the sum of the two
Shinya Kitaoka 120a6e
    outRect = inRect = ((tileRect).enlarge(blurI) * inRect) +
Shinya Kitaoka 120a6e
                       ((inRect).enlarge(blurI) * tileRect);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  //---------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 38fd86
  void doCompute(TTile &tile, double frame,
Shinya Kitaoka 38fd86
                 const TRenderSettings &ri) override {
Shinya Kitaoka 120a6e
    Status status = getFxStatus(m_light, m_lighted);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (status & NoPortsConnected)
Shinya Kitaoka 120a6e
      // If no port, just do nothing :)
Shinya Kitaoka 120a6e
      return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Calculate source
Shinya Kitaoka 120a6e
    if (status & Port1Connected) m_lighted->compute(tile, frame, ri);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    // Calculate light
Shinya Kitaoka 120a6e
    if (status & Port0Connected) {
Shinya Kitaoka 120a6e
      // Init light infos
Shinya Kitaoka 120a6e
      TDimension tileSize(tile.getRaster()->getSize());
Shinya Kitaoka 120a6e
      TRectD tileRect(tile.m_pos, TDimensionD(tileSize.lx, tileSize.ly));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      double scale = sqrt(fabs(ri.m_affine.det()));
Shinya Kitaoka 120a6e
      double blur  = m_value->getValue(frame) * scale;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Build the light interesting rect
Shinya Kitaoka 120a6e
      TRectD lightRect, blurOutRect;
Shinya Kitaoka 120a6e
      m_light->getBBox(frame, lightRect, ri);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      buildLightRects(tileRect, lightRect, blurOutRect, blur);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if ((lightRect.getLx() <= 0) || (lightRect.getLy() <= 0)) return;
Shinya Kitaoka 120a6e
      if ((blurOutRect.getLx() <= 0) || (blurOutRect.getLy() <= 0)) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Calculate the light tile
Shinya Kitaoka 120a6e
      TTile lightTile;
Shinya Kitaoka 120a6e
      TDimension lightSize(tround(lightRect.getLx()),
Shinya Kitaoka 120a6e
                           tround(lightRect.getLy()));
Shinya Kitaoka 120a6e
      m_light->allocateAndCompute(lightTile, lightRect.getP00(), lightSize,
Shinya Kitaoka 120a6e
                                  tile.getRaster(), frame, ri);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Init glow parameters
Shinya Kitaoka 120a6e
      TPixel32 color    = m_color->getValue(frame);
Shinya Kitaoka 120a6e
      double brightness = m_brightness->getValue(frame) / 100.0;
Shinya Kitaoka 120a6e
      double fade       = m_fade->getValue(frame) / 100.0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Now, apply the glow
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // First, deal with the fade
Shinya Kitaoka 120a6e
      {
Shinya Kitaoka 120a6e
        TRasterP light     = lightTile.getRaster();
Shinya Kitaoka 120a6e
        TRaster32P light32 = light;
Shinya Kitaoka 120a6e
        TRaster64P light64 = light;
Shinya Kitaoka 120a6e
        if (light32)
Shinya Kitaoka 120a6e
          ::fade(light32, fade, color);
Shinya Kitaoka 120a6e
        else if (light64)
Shinya Kitaoka 120a6e
          ::fade(light64, fade, toPixel64(color));
Shinya Kitaoka 120a6e
        else
Shinya Kitaoka 120a6e
          assert(false);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Then, build the blur
Shinya Kitaoka 120a6e
      TRasterP blurOut;
Shinya Kitaoka 120a6e
      if (blur > 0) {
Shinya Kitaoka 120a6e
        // Build a temporary output to the blur
Shinya Kitaoka 120a6e
        {
Shinya Kitaoka 120a6e
          TRasterP light(lightTile.getRaster());
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          blurOut = light->create(tround(blurOutRect.getLx()),
Shinya Kitaoka 120a6e
                                  tround(blurOutRect.getLy()));
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
          // Apply the blur. Please note that SSE2 should not be used for now -
Shinya Kitaoka 120a6e
          // I've seen it
Shinya Kitaoka 120a6e
          // doing strange things to the blur...
Shinya Kitaoka 120a6e
          TPointD displacement(lightRect.getP00() - blurOutRect.getP00());
Shinya Kitaoka 120a6e
          TRop::blur(blurOut, light, blur, tround(displacement.x),
Shinya Kitaoka 120a6e
                     tround(displacement.y), false);
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      } else
Shinya Kitaoka 120a6e
        blurOut = lightTile.getRaster();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Apply the rgbm scale
Shinya Kitaoka 120a6e
      TRop::rgbmScale(blurOut, blurOut, 1, 1, 1, brightness);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      // Apply the add
Shinya Kitaoka 120a6e
      {
Shinya Kitaoka 120a6e
        TRectD interestingRect(tileRect * blurOutRect);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        TRect interestingTileRect(tround(interestingRect.x0 - tileRect.x0),
Shinya Kitaoka 120a6e
                                  tround(interestingRect.y0 - tileRect.y0),
Shinya Kitaoka 120a6e
                                  tround(interestingRect.x1 - tileRect.x0) - 1,
Shinya Kitaoka 120a6e
                                  tround(interestingRect.y1 - tileRect.y0) - 1);
Shinya Kitaoka 120a6e
        TRect interestingBlurRect(
Shinya Kitaoka 120a6e
            tround(interestingRect.x0 - blurOutRect.x0),
Shinya Kitaoka 120a6e
            tround(interestingRect.y0 - blurOutRect.y0),
Shinya Kitaoka 120a6e
            tround(interestingRect.x1 - blurOutRect.x0) - 1,
Shinya Kitaoka 120a6e
            tround(interestingRect.y1 - blurOutRect.y0) - 1);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        if ((interestingTileRect.getLx() <= 0) ||
Shinya Kitaoka 120a6e
            (interestingTileRect.getLy() <= 0))
Shinya Kitaoka 120a6e
          return;
Shinya Kitaoka 120a6e
        if ((interestingBlurRect.getLx() <= 0) ||
Shinya Kitaoka 120a6e
            (interestingBlurRect.getLy() <= 0))
Shinya Kitaoka 120a6e
          return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
        TRasterP tileInterestRas(
Shinya Kitaoka 120a6e
            tile.getRaster()->extract(interestingTileRect));
Shinya Kitaoka 120a6e
        TRasterP blurInterestRas(blurOut->extract(interestingBlurRect));
Shinya Kitaoka 120a6e
        TRop::add(blurInterestRas, tileInterestRas, tileInterestRas);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  //---------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  void doDryCompute(TRectD &rect, double frame,
Shinya Kitaoka 38fd86
                    const TRenderSettings &info) override {
Shinya Kitaoka 120a6e
    Status status = getFxStatus(m_light, m_lighted);
Shinya Kitaoka 120a6e
    if (status & NoPortsConnected) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (status & Port1Connected) m_lighted->dryCompute(rect, frame, info);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    if (status & Port0Connected) {
Shinya Kitaoka 120a6e
      double scale = sqrt(fabs(info.m_affine.det()));
Shinya Kitaoka 120a6e
      double blur  = m_value->getValue(frame) * scale;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      TRectD lightRect, blurOutRect;
Shinya Kitaoka 120a6e
      m_light->getBBox(frame, lightRect, info);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      buildLightRects(rect, lightRect, blurOutRect, blur);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      if ((lightRect.getLx() <= 0) || (lightRect.getLy() <= 0)) return;
Shinya Kitaoka 120a6e
      if ((blurOutRect.getLx() <= 0) || (blurOutRect.getLy() <= 0)) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      m_light->dryCompute(lightRect, frame, info);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  //---------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // Just like the blur
Shinya Kitaoka 473e70
  bool canHandle(const TRenderSettings &info, double frame) override {
Shinya Kitaoka 120a6e
    if (m_light.isConnected())
Shinya Kitaoka 120a6e
      return (m_value->getValue(frame) == 0) ? true
Shinya Kitaoka 120a6e
                                             : isAlmostIsotropic(info.m_affine);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    return true;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  //---------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int getMemoryRequirement(const TRectD &rect, double frame,
Shinya Kitaoka 473e70
                           const TRenderSettings &info) override {
Shinya Kitaoka 120a6e
    double scale = sqrt(fabs(info.m_affine.det()));
Shinya Kitaoka 120a6e
    double blur  = m_value->getValue(frame) * scale;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    return TRasterFx::memorySize(rect.enlarge(blur), info.m_bpp);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  //---------------------------------------------------------------------------
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  TFxPort *getXsheetPort() const override { return getInputPort(1); }
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//==================================================================
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
FX_PLUGIN_IDENTIFIER(GlowFx, "glowFx")
Toshihiro Shimizu 890ddd
//==================================================================