shun-iwasawa af8f7a
#include "iwa_rainbowfx.h"
shun-iwasawa af8f7a
#include "iwa_cie_d65.h"
shun-iwasawa af8f7a
#include "iwa_xyz.h"
shun-iwasawa af8f7a
#include "iwa_rainbow_intensity.h"
shun-iwasawa af8f7a
#include "tparamuiconcept.h"
shun-iwasawa af8f7a
shun-iwasawa af8f7a
//--------------------------------------------------------------
shun-iwasawa af8f7a
double Iwa_RainbowFx::getSizePixelAmount(const double val,
shun-iwasawa af8f7a
                                         const TAffine affine) {
shun-iwasawa af8f7a
  /*--- Convert to vector --- */
shun-iwasawa af8f7a
  TPointD vect;
shun-iwasawa af8f7a
  vect.x = val;
shun-iwasawa af8f7a
  vect.y = 0.0;
shun-iwasawa af8f7a
  /*--- Apply geometrical transformation ---*/
shun-iwasawa af8f7a
  // For the following lines I referred to lines 586-592 of
shun-iwasawa af8f7a
  // sources/stdfx/motionblurfx.cpp
shun-iwasawa af8f7a
  TAffine aff(affine);
shun-iwasawa af8f7a
  aff.a13 = aff.a23 = 0; /* ignore translation */
shun-iwasawa af8f7a
  vect              = aff * vect;
shun-iwasawa af8f7a
  /*--- return the length of the vector ---*/
shun-iwasawa af8f7a
  return sqrt(vect.x * vect.x + vect.y * vect.y);
shun-iwasawa af8f7a
}
shun-iwasawa af8f7a
shun-iwasawa af8f7a
//------------------------------------------------------------
shun-iwasawa af8f7a
shun-iwasawa af8f7a
void Iwa_RainbowFx::buildRanbowColorMap(double3* core, double3* wide,
shun-iwasawa af8f7a
                                        double intensity, double inside,
shun-iwasawa af8f7a
                                        double secondary) {
shun-iwasawa af8f7a
  auto clamp01 = [](double val) { return std::min(1.0, std::max(0.0, val)); };
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  int mapSize[2] = {301, 91};
shun-iwasawa af8f7a
  // secondary rainbow : gradually darken from 133 to 136 degrees
shun-iwasawa af8f7a
  double secondary_grad_range[2] = {133, 136};
shun-iwasawa af8f7a
  // supernumerary rainbow inside the primary rainbow : gradually darken from
shun-iwasawa af8f7a
  // 139.75 to 139.2 degrees
shun-iwasawa af8f7a
  double inside_grad_width    = 0.57;
shun-iwasawa af8f7a
  double inside_grad_start[2] = {139.75, 139.2};
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  for (int m = 0; m < 2; m++) {
shun-iwasawa af8f7a
    double3* out_p = (m == 0) ? core : wide;
shun-iwasawa af8f7a
    double xyz_sum[3];
shun-iwasawa af8f7a
    for (int a = 0; a < mapSize[m]; a++, out_p++) {
shun-iwasawa af8f7a
      double angle = (m == 0) ? 120.0 + (double)a * 0.1 : 90.0 + (double)a;
shun-iwasawa af8f7a
shun-iwasawa af8f7a
      double second_ratio = 1.0;
shun-iwasawa af8f7a
      if (angle <= secondary_grad_range[0]) {
shun-iwasawa af8f7a
        second_ratio = secondary;
shun-iwasawa af8f7a
      } else if (angle < secondary_grad_range[1]) {
shun-iwasawa af8f7a
        double r = (angle - (double)secondary_grad_range[0]) /
shun-iwasawa af8f7a
                   (double)(secondary_grad_range[1] - secondary_grad_range[0]);
shun-iwasawa af8f7a
        second_ratio = (1.0 - r) * secondary + r;
shun-iwasawa af8f7a
      }
shun-iwasawa af8f7a
shun-iwasawa af8f7a
      xyz_sum[0] = 0.0;
shun-iwasawa af8f7a
      xyz_sum[1] = 0.0;
shun-iwasawa af8f7a
      xyz_sum[2] = 0.0;
shun-iwasawa af8f7a
      // sum for each wavelength (in the range of visible light, 380nm-710nm)
shun-iwasawa af8f7a
      for (int ram = 0; ram < 34; ram++) {
shun-iwasawa af8f7a
        double start =
shun-iwasawa af8f7a
            inside_grad_start[0] +
shun-iwasawa af8f7a
            (inside_grad_start[1] - inside_grad_start[0]) * (double)ram / 33.0;
shun-iwasawa af8f7a
        double end          = start + inside_grad_width;
shun-iwasawa af8f7a
        double inside_ratio = 1.0;
shun-iwasawa af8f7a
        if (angle >= end) {
shun-iwasawa af8f7a
          inside_ratio = inside;
shun-iwasawa af8f7a
        } else if (angle > start) {
shun-iwasawa af8f7a
          double r     = (angle - start) / inside_grad_width;
shun-iwasawa af8f7a
          inside_ratio = (1.0 - r) + r * inside;
shun-iwasawa af8f7a
        }
shun-iwasawa af8f7a
shun-iwasawa af8f7a
        double* data =
shun-iwasawa af8f7a
            (m == 0) ? &rainbow_core_data[a][0] : &rainbow_wide_data[a][0];
shun-iwasawa af8f7a
        // accumulate XYZ channel values
shun-iwasawa af8f7a
        for (int c = 0; c < 3; c++)
shun-iwasawa af8f7a
          xyz_sum[c] +=
shun-iwasawa af8f7a
              cie_d65[ram] * data[ram] * xyz[ram * 3 + c] * inside_ratio;
shun-iwasawa af8f7a
      }
shun-iwasawa af8f7a
      double tmp_intensity = intensity * 25000.0 * second_ratio;
shun-iwasawa af8f7a
      out_p->r = clamp01((3.240479 * xyz_sum[0] - 1.537150 * xyz_sum[1] -
shun-iwasawa af8f7a
                          0.498535 * xyz_sum[2]) *
shun-iwasawa af8f7a
                         tmp_intensity);
shun-iwasawa af8f7a
      out_p->g = clamp01((-0.969256 * xyz_sum[0] + 1.875992 * xyz_sum[1] +
shun-iwasawa af8f7a
                          0.041556 * xyz_sum[2]) *
shun-iwasawa af8f7a
                         tmp_intensity);
shun-iwasawa af8f7a
      out_p->b = clamp01((0.055648 * xyz_sum[0] - 0.204043 * xyz_sum[1] +
shun-iwasawa af8f7a
                          1.057311f * xyz_sum[2]) *
shun-iwasawa af8f7a
                         tmp_intensity);
shun-iwasawa af8f7a
    }
shun-iwasawa af8f7a
  }
shun-iwasawa af8f7a
}
shun-iwasawa af8f7a
shun-iwasawa af8f7a
//------------------------------------------------------------
shun-iwasawa af8f7a
shun-iwasawa af8f7a
template <typename pixel="" raster,="" typename=""></typename>
shun-iwasawa af8f7a
void Iwa_RainbowFx::setOutputRaster(const RASTER ras, TDimensionI dim,
shun-iwasawa af8f7a
                                    double3* outBuf_p) {
shun-iwasawa af8f7a
  bool withAlpha = m_alpha_rendering->getValue();
shun-iwasawa af8f7a
  double maxi    = static_cast<double>(PIXEL::maxChannelValue);  // 255or65535</double>
shun-iwasawa af8f7a
  double3* out_p = outBuf_p;
shun-iwasawa af8f7a
  for (int j = 0; j < dim.ly; j++) {
shun-iwasawa af8f7a
    PIXEL* pix = ras->pixels(j);
shun-iwasawa af8f7a
    for (int i = 0; i < dim.lx; i++, out_p++, pix++) {
shun-iwasawa af8f7a
      pix->r = (typename PIXEL::Channel)(out_p->r * (maxi + 0.999999));
shun-iwasawa af8f7a
      pix->g = (typename PIXEL::Channel)(out_p->g * (maxi + 0.999999));
shun-iwasawa af8f7a
      pix->b = (typename PIXEL::Channel)(out_p->b * (maxi + 0.999999));
shun-iwasawa af8f7a
shun-iwasawa af8f7a
      if (withAlpha) {
shun-iwasawa af8f7a
        double chan_a = std::max(std::max(out_p->r, out_p->g), out_p->b);
shun-iwasawa af8f7a
        pix->m        = (typename PIXEL::Channel)(chan_a * (maxi + 0.999999));
shun-iwasawa af8f7a
      } else
shun-iwasawa af8f7a
        pix->m = (typename PIXEL::Channel)(PIXEL::maxChannelValue);
shun-iwasawa af8f7a
    }
shun-iwasawa af8f7a
  }
shun-iwasawa af8f7a
}
shun-iwasawa af8f7a
shun-iwasawa af8f7a
//------------------------------------------------------------
shun-iwasawa af8f7a
shun-iwasawa af8f7a
Iwa_RainbowFx::Iwa_RainbowFx()
shun-iwasawa af8f7a
    : m_center(TPointD(0.0, 0.0))
shun-iwasawa af8f7a
    , m_radius(200.0)
shun-iwasawa af8f7a
    , m_intensity(1.0)
shun-iwasawa af8f7a
    , m_width_scale(1.0)
shun-iwasawa af8f7a
    , m_inside(1.0)
shun-iwasawa af8f7a
    , m_secondary_rainbow(1.0)
shun-iwasawa af8f7a
    , m_alpha_rendering(false) {
shun-iwasawa af8f7a
  bindParam(this, "center", m_center);
shun-iwasawa af8f7a
  bindParam(this, "radius", m_radius);
shun-iwasawa af8f7a
  bindParam(this, "intensity", m_intensity);
shun-iwasawa af8f7a
  bindParam(this, "width_scale", m_width_scale);
shun-iwasawa af8f7a
  bindParam(this, "inside", m_inside);
shun-iwasawa af8f7a
  bindParam(this, "secondary_rainbow", m_secondary_rainbow);
shun-iwasawa af8f7a
  bindParam(this, "alpha_rendering", m_alpha_rendering);
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  m_radius->setValueRange(0.0, std::numeric_limits<double>::max());</double>
shun-iwasawa af8f7a
  m_intensity->setValueRange(0.1, 10.0);
shun-iwasawa af8f7a
  m_inside->setValueRange(0.0, 1.0);
shun-iwasawa af8f7a
  m_secondary_rainbow->setValueRange(0.0, 10.0);
shun-iwasawa af8f7a
  m_width_scale->setValueRange(0.1, 50.0);
shun-iwasawa af8f7a
}
shun-iwasawa af8f7a
shun-iwasawa af8f7a
//------------------------------------------------------------
shun-iwasawa af8f7a
shun-iwasawa af8f7a
bool Iwa_RainbowFx::doGetBBox(double frame, TRectD& bBox,
shun-iwasawa af8f7a
                              const TRenderSettings& ri) {
shun-iwasawa af8f7a
  bBox = TConsts::infiniteRectD;
shun-iwasawa af8f7a
  return true;
shun-iwasawa af8f7a
}
shun-iwasawa af8f7a
shun-iwasawa af8f7a
//------------------------------------------------------------
shun-iwasawa af8f7a
shun-iwasawa af8f7a
inline double3 Iwa_RainbowFx::angleToColor(double angle, double3* core,
shun-iwasawa af8f7a
                                           double3* wide) {
shun-iwasawa af8f7a
  // boundary conditions
shun-iwasawa af8f7a
  if (angle <= 90.0)
shun-iwasawa af8f7a
    return wide[0];
shun-iwasawa af8f7a
  else if (angle >= 180.0)
shun-iwasawa af8f7a
    return wide[90];
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  // inside of the range of hi-res color table
shun-iwasawa af8f7a
  if (angle > 120.0 && angle < 150.0) {
shun-iwasawa af8f7a
    double tablePos = (angle - 120) / 0.1;
shun-iwasawa af8f7a
    int tableId     = (int)std::floor(tablePos);
shun-iwasawa af8f7a
    double ratio    = tablePos - (double)tableId;
shun-iwasawa af8f7a
    return {core[tableId].r * (1.0 - ratio) + core[tableId + 1].r * ratio,
shun-iwasawa af8f7a
            core[tableId].g * (1.0 - ratio) + core[tableId + 1].g * ratio,
shun-iwasawa af8f7a
            core[tableId].b * (1.0 - ratio) + core[tableId + 1].b * ratio};
shun-iwasawa af8f7a
  }
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  // low-res color table
shun-iwasawa af8f7a
  double tablePos = (angle - 90) / 1.0;
shun-iwasawa af8f7a
  int tableId     = (int)std::floor(tablePos);
shun-iwasawa af8f7a
  double ratio    = tablePos - (double)tableId;
shun-iwasawa af8f7a
  return {wide[tableId].r * (1.0 - ratio) + wide[tableId + 1].r * ratio,
shun-iwasawa af8f7a
          wide[tableId].g * (1.0 - ratio) + wide[tableId + 1].g * ratio,
shun-iwasawa af8f7a
          wide[tableId].b * (1.0 - ratio) + wide[tableId + 1].b * ratio};
shun-iwasawa af8f7a
}
shun-iwasawa af8f7a
shun-iwasawa af8f7a
//------------------------------------------------------------
shun-iwasawa af8f7a
void Iwa_RainbowFx::doCompute(TTile& tile, double frame,
shun-iwasawa af8f7a
                              const TRenderSettings& ri) {
shun-iwasawa af8f7a
  // build raibow color map
shun-iwasawa af8f7a
  TRasterGR8P rainbowColorCore_ras(sizeof(double3) * 301, 1);
shun-iwasawa af8f7a
  rainbowColorCore_ras->lock();
shun-iwasawa af8f7a
  double3* rainbowColorCore_p = (double3*)rainbowColorCore_ras->getRawData();
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  TRasterGR8P rainbowColorWide_ras(sizeof(double3) * 91, 1);
shun-iwasawa af8f7a
  rainbowColorWide_ras->lock();
shun-iwasawa af8f7a
  double3* rainbowColorWide_p = (double3*)rainbowColorWide_ras->getRawData();
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  double intensity = m_intensity->getValue(frame);
shun-iwasawa af8f7a
  double inside    = m_inside->getValue(frame);
shun-iwasawa af8f7a
  double secondary = m_secondary_rainbow->getValue(frame);
shun-iwasawa af8f7a
  buildRanbowColorMap(rainbowColorCore_p, rainbowColorWide_p, intensity, inside,
shun-iwasawa af8f7a
                      secondary);
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  // convert center position to render region coordinate
shun-iwasawa af8f7a
  TAffine aff = ri.m_affine;
shun-iwasawa af8f7a
  TDimensionI dimOut(tile.getRaster()->getLx(), tile.getRaster()->getLy());
shun-iwasawa af8f7a
  TPointD dimOffset((float)dimOut.lx / 2.0f, (float)dimOut.ly / 2.0f);
shun-iwasawa af8f7a
  TPointD centerPos = m_center->getValue(frame);
shun-iwasawa af8f7a
  centerPos = aff * centerPos - (tile.m_pos + tile.getRaster()->getCenterD()) +
shun-iwasawa af8f7a
              dimOffset;
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  // result image buffer
shun-iwasawa af8f7a
  TRasterGR8P outBuf_ras(sizeof(double3) * dimOut.lx * dimOut.ly, 1);
shun-iwasawa af8f7a
  outBuf_ras->lock();
shun-iwasawa af8f7a
  double3* outBuf_p = (double3*)outBuf_ras->getRawData();
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  double theta_peak = 41.3;
shun-iwasawa af8f7a
  double peakRadius =
shun-iwasawa af8f7a
      getSizePixelAmount(m_radius->getValue(frame), ri.m_affine);
shun-iwasawa af8f7a
  double anglePerPixel = theta_peak / peakRadius;
shun-iwasawa af8f7a
  double widthScale    = m_width_scale->getValue(frame);
shun-iwasawa af8f7a
  double3* out_p       = outBuf_p;
shun-iwasawa af8f7a
  // loop for all pixels
shun-iwasawa af8f7a
  for (int y = 0; y < dimOut.ly; y++) {
shun-iwasawa af8f7a
    for (int x = 0; x < dimOut.lx; x++, out_p++) {
shun-iwasawa af8f7a
      // convert pixel distance from the center to scattering angle (degrees)
shun-iwasawa af8f7a
      double s     = x - centerPos.x;
shun-iwasawa af8f7a
      double t     = y - centerPos.y;
shun-iwasawa af8f7a
      double theta = std::sqrt(s * s + t * t) * anglePerPixel;
shun-iwasawa af8f7a
      double phi   = 180.0 - theta_peak + (theta_peak - theta) / widthScale;
shun-iwasawa af8f7a
shun-iwasawa af8f7a
      *out_p = angleToColor(phi, rainbowColorCore_p, rainbowColorWide_p);
shun-iwasawa af8f7a
    }
shun-iwasawa af8f7a
  }
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  rainbowColorCore_ras->unlock();
shun-iwasawa af8f7a
  rainbowColorWide_ras->unlock();
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  // convert to channel values
shun-iwasawa af8f7a
  tile.getRaster()->clear();
shun-iwasawa af8f7a
  TRaster32P outRas32 = (TRaster32P)tile.getRaster();
shun-iwasawa af8f7a
  TRaster64P outRas64 = (TRaster64P)tile.getRaster();
shun-iwasawa af8f7a
  if (outRas32)
shun-iwasawa af8f7a
    setOutputRaster<traster32p, tpixel32="">(outRas32, dimOut, outBuf_p);</traster32p,>
shun-iwasawa af8f7a
  else if (outRas64)
shun-iwasawa af8f7a
    setOutputRaster<traster64p, tpixel64="">(outRas64, dimOut, outBuf_p);</traster64p,>
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  outBuf_ras->unlock();
shun-iwasawa af8f7a
}
shun-iwasawa af8f7a
shun-iwasawa af8f7a
//------------------------------------------------------------
shun-iwasawa af8f7a
shun-iwasawa af8f7a
void Iwa_RainbowFx::getParamUIs(TParamUIConcept*& concepts, int& length) {
shun-iwasawa af8f7a
  concepts = new TParamUIConcept[length = 3];
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  concepts[0].m_type  = TParamUIConcept::POINT;
shun-iwasawa af8f7a
  concepts[0].m_label = "Center";
shun-iwasawa af8f7a
  concepts[0].m_params.push_back(m_center);
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  concepts[1].m_type  = TParamUIConcept::RADIUS;
shun-iwasawa af8f7a
  concepts[1].m_label = "Radius";
shun-iwasawa af8f7a
  concepts[1].m_params.push_back(m_radius);
shun-iwasawa af8f7a
  concepts[1].m_params.push_back(m_center);
shun-iwasawa af8f7a
shun-iwasawa af8f7a
  concepts[2].m_type  = TParamUIConcept::RAINBOW_WIDTH;
shun-iwasawa af8f7a
  concepts[2].m_label = "Width";
shun-iwasawa af8f7a
  concepts[2].m_params.push_back(m_width_scale);
shun-iwasawa af8f7a
  concepts[2].m_params.push_back(m_radius);
shun-iwasawa af8f7a
  concepts[2].m_params.push_back(m_center);
shun-iwasawa af8f7a
}
shun-iwasawa af8f7a
shun-iwasawa af8f7a
//==============================================================================
shun-iwasawa af8f7a
shun-iwasawa af8f7a
FX_PLUGIN_IDENTIFIER(Iwa_RainbowFx, "iwa_RainbowFx");