shun-iwasawa 13c4cf
#include "iwa_flowpaintbrushfx.h"
shun-iwasawa 13c4cf
#include "tparamuiconcept.h"
shun-iwasawa 13c4cf
#include "tlevel.h"
shun-iwasawa 13c4cf
#include "trop.h"
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
#include <random>
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
#include "tgl.h"
shun-iwasawa 13c4cf
#include <QOpenGLFramebufferObject>
shun-iwasawa 13c4cf
#include <QOffscreenSurface>
shun-iwasawa 13c4cf
#include <QSurfaceFormat>
shun-iwasawa 13c4cf
#include <QOpenGLContext>
shun-iwasawa 13c4cf
#include <QImage>
shun-iwasawa 13c4cf
#include <QOpenGLTexture>
shun-iwasawa 13c4cf
#include <QColor>
shun-iwasawa 13c4cf
#include <QPainter>
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
namespace {
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
inline double lerp(DoublePair range, double t) {
shun-iwasawa 13c4cf
  return range.first * (1.0 - t) + range.second * t;
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
bool strokeStackGraterThan(const BrushStroke &stroke1,
shun-iwasawa 13c4cf
                           const BrushStroke &stroke2) {
shun-iwasawa 13c4cf
  return stroke1.stack > stroke2.stack;
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
}  // namespace
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
// obtain raster data of brush tips ブラシタッチのラスターデータを取得
shun-iwasawa 13c4cf
void Iwa_FlowPaintBrushFx::getBrushRasters(std::vector<TRasterP> &brushRasters,
shun-iwasawa 13c4cf
                                           TDimension &b_size, int &lastFrame,
shun-iwasawa 13c4cf
                                           TTile &tile,
shun-iwasawa 13c4cf
                                           const TRenderSettings &ri) {
shun-iwasawa 13c4cf
  // ブラシテクスチャ情報
shun-iwasawa 13c4cf
  TPointD b_offset;
shun-iwasawa 13c4cf
  const TFxTimeRegion &tr = m_brush->getTimeRegion();
shun-iwasawa 13c4cf
  lastFrame               = tr.getLastFrame() + 1;
shun-iwasawa 13c4cf
  TLevelP partLevel       = new TLevel();
shun-iwasawa 13c4cf
  partLevel->setName(m_brush->getAlias(0, ri));
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // The particles offset must be calculated without considering the
shun-iwasawa 13c4cf
  // affine's translational component
shun-iwasawa 13c4cf
  TRenderSettings riZero(ri);
shun-iwasawa 13c4cf
  riZero.m_affine.a13 = riZero.m_affine.a23 = 0;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // Calculate the bboxes union
shun-iwasawa 13c4cf
  TRectD brushBox;
shun-iwasawa 13c4cf
  for (int t = 0; t < lastFrame; ++t) {
shun-iwasawa 13c4cf
    TRectD inputBox;
shun-iwasawa 13c4cf
    m_brush->getBBox(t, inputBox, riZero);
shun-iwasawa 13c4cf
    brushBox += inputBox;
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
  if (brushBox.isEmpty()) {
shun-iwasawa 13c4cf
    lastFrame = 0;
shun-iwasawa 13c4cf
    return;
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
  if (brushBox == TConsts::infiniteRectD) brushBox *= ri.m_cameraBox;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  b_size.lx = (int)brushBox.getLx() + 1;
shun-iwasawa 13c4cf
  b_size.ly = (int)brushBox.getLy() + 1;
shun-iwasawa 13c4cf
  b_offset  = TPointD(0.5 * (brushBox.x0 + brushBox.x1),
shun-iwasawa 13c4cf
                      0.5 * (brushBox.y0 + brushBox.y1));
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  for (int t = 0; t < lastFrame; ++t) {
shun-iwasawa 13c4cf
    TRasterP ras;
shun-iwasawa 13c4cf
    std::string alias;
shun-iwasawa 13c4cf
    TRasterImageP rimg;
shun-iwasawa 13c4cf
    rimg = partLevel->frame(t);
shun-iwasawa 13c4cf
    if (rimg) {
shun-iwasawa 13c4cf
      ras = rimg->getRaster();
shun-iwasawa 13c4cf
    } else {
shun-iwasawa 13c4cf
      alias = "BRUSH: " + m_brush->getAlias(t, ri);
shun-iwasawa 13c4cf
      rimg  = TImageCache::instance()->get(alias, false);
shun-iwasawa 13c4cf
      if (rimg) {
shun-iwasawa 13c4cf
        ras = rimg->getRaster();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
        // Check that the raster resolution is sufficient for our purposes
shun-iwasawa 13c4cf
        if (ras->getLx() < b_size.lx || ras->getLy() < b_size.ly)
shun-iwasawa 13c4cf
          ras = 0;
shun-iwasawa 13c4cf
        else
shun-iwasawa 13c4cf
          b_size = TDimension(ras->getLx(), ras->getLy());
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
    if (!ras) {
shun-iwasawa 13c4cf
      TTile auxTile;
shun-iwasawa 13c4cf
      TRenderSettings auxRi(ri);
shun-iwasawa 13c4cf
      auxRi.m_bpp = 32;
shun-iwasawa 13c4cf
      m_brush->allocateAndCompute(auxTile, brushBox.getP00(), b_size, 0, t,
shun-iwasawa 13c4cf
                                  auxRi);
shun-iwasawa 13c4cf
      ras = auxTile.getRaster();
shun-iwasawa 13c4cf
      addRenderCache(alias, TRasterImageP(ras));
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
    if (ras) brushRasters.push_back(ras);
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
template <typename RASTER, typename PIXEL>
shun-iwasawa 13c4cf
void Iwa_FlowPaintBrushFx::setFlowTileToBuffer(const RASTER flowRas,
shun-iwasawa 13c4cf
                                               double2 *buf) {
shun-iwasawa 13c4cf
  double2 *buf_p = buf;
shun-iwasawa 13c4cf
  for (int j = 0; j < flowRas->getLy(); j++) {
shun-iwasawa 13c4cf
    PIXEL *pix = flowRas->pixels(j);
shun-iwasawa 13c4cf
    for (int i = 0; i < flowRas->getLx(); i++, pix++, buf_p++) {
shun-iwasawa 13c4cf
      double val = double(pix->r) / double(PIXEL::maxChannelValue);
shun-iwasawa 13c4cf
      (*buf_p).x = val * 2.0 - 1.0;
shun-iwasawa 13c4cf
      val        = double(pix->g) / double(PIXEL::maxChannelValue);
shun-iwasawa 13c4cf
      (*buf_p).y = val * 2.0 - 1.0;
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
template <typename RASTER, typename PIXEL>
shun-iwasawa 13c4cf
void Iwa_FlowPaintBrushFx::setAreaTileToBuffer(const RASTER areaRas,
shun-iwasawa 13c4cf
                                               double *buf) {
shun-iwasawa 13c4cf
  double *buf_p = buf;
shun-iwasawa 13c4cf
  for (int j = 0; j < areaRas->getLy(); j++) {
shun-iwasawa 13c4cf
    PIXEL *pix = areaRas->pixels(j);
shun-iwasawa 13c4cf
    for (int i = 0; i < areaRas->getLx(); i++, pix++, buf_p++) {
shun-iwasawa 13c4cf
      (*buf_p) = double(pix->m) / double(PIXEL::maxChannelValue);
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
template <typename RASTER, typename PIXEL>
shun-iwasawa 13c4cf
void Iwa_FlowPaintBrushFx::setColorTileToBuffer(const RASTER colorRas,
shun-iwasawa 13c4cf
                                                colorRGBA *buf) {
shun-iwasawa 13c4cf
  colorRGBA *buf_p = buf;
shun-iwasawa 13c4cf
  for (int j = 0; j < colorRas->getLy(); j++) {
shun-iwasawa 13c4cf
    PIXEL *pix = colorRas->pixels(j);
shun-iwasawa 13c4cf
    for (int i = 0; i < colorRas->getLx(); i++, pix++, buf_p++) {
shun-iwasawa 13c4cf
      (*buf_p).r = double(pix->r) / double(PIXEL::maxChannelValue);
shun-iwasawa 13c4cf
      (*buf_p).g = double(pix->g) / double(PIXEL::maxChannelValue);
shun-iwasawa 13c4cf
      (*buf_p).b = double(pix->b) / double(PIXEL::maxChannelValue);
shun-iwasawa 13c4cf
      (*buf_p).a = double(pix->m) / double(PIXEL::maxChannelValue);
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
template <typename RASTER, typename PIXEL>
shun-iwasawa 13c4cf
void Iwa_FlowPaintBrushFx::setOutRaster(const RASTER outRas, double *buf) {
shun-iwasawa 13c4cf
  double *buf_p = buf;
shun-iwasawa 13c4cf
  for (int j = 0; j < outRas->getLy(); j++) {
shun-iwasawa 13c4cf
    PIXEL *pix = outRas->pixels(j);
shun-iwasawa 13c4cf
    for (int i = 0; i < outRas->getLx(); i++, pix++, buf_p++) {
shun-iwasawa 13c4cf
      typename PIXEL::Channel val =
shun-iwasawa 13c4cf
          (typename PIXEL::Channel)(*buf_p * double(PIXEL::maxChannelValue));
shun-iwasawa 13c4cf
      pix->r = val;
shun-iwasawa 13c4cf
      pix->g = val;
shun-iwasawa 13c4cf
      pix->b = val;
shun-iwasawa 13c4cf
      pix->m = PIXEL::maxChannelValue;
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
Iwa_FlowPaintBrushFx::Iwa_FlowPaintBrushFx()
shun-iwasawa 13c4cf
    : m_h_density(10.0)
shun-iwasawa 13c4cf
    , m_v_density(10.0)
shun-iwasawa 13c4cf
    , m_pos_randomness(1.0)
shun-iwasawa 13c4cf
    , m_pos_wobble(0.0)
shun-iwasawa 13c4cf
    , m_tip_width(DoublePair(0.02, 0.05))
shun-iwasawa 13c4cf
    , m_tip_length(DoublePair(0.08, 0.2))
shun-iwasawa 13c4cf
    , m_tip_alpha(DoublePair(0.8, 1.0))
shun-iwasawa 13c4cf
    , m_tip_joints(3)
shun-iwasawa 13c4cf
    , m_bidirectional(true)
shun-iwasawa 13c4cf
    , m_width_randomness(0.0)
shun-iwasawa 13c4cf
    , m_length_randomness(0.0)
shun-iwasawa 13c4cf
    , m_angle_randomness(0.0)
shun-iwasawa 13c4cf
    , m_sustain_width_to_skew(0.0)
shun-iwasawa 13c4cf
    , m_anti_jaggy(false)
shun-iwasawa 13c4cf
    , m_origin_pos(TPointD(0.0, 0.0))
shun-iwasawa 13c4cf
    , m_horizontal_pos(TPointD(100.0, 0.0))
shun-iwasawa 13c4cf
    , m_vertical_pos(TPointD(0.0, 100.0))
shun-iwasawa 13c4cf
    , m_curve_point(TPointD(0.0, 0.0))
shun-iwasawa 13c4cf
    , m_fill_gap_size(0.0)
shun-iwasawa 13c4cf
    , m_reference_frame(0.0)
shun-iwasawa 13c4cf
    , m_reference_prevalence(0.0)
shun-iwasawa 13c4cf
    , m_random_seed(1)
shun-iwasawa 13c4cf
    , m_sortBy(new TIntEnumParam(NoSort, "None")) {
shun-iwasawa 13c4cf
  addInputPort("Brush", m_brush);
shun-iwasawa 13c4cf
  addInputPort("Flow", m_flow);
shun-iwasawa 13c4cf
  addInputPort("Area", m_area);
shun-iwasawa 13c4cf
  addInputPort("Color", m_color);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  bindParam(this, "h_density", m_h_density);
shun-iwasawa 13c4cf
  bindParam(this, "v_density", m_v_density);
shun-iwasawa 13c4cf
  bindParam(this, "pos_randomness", m_pos_randomness);
shun-iwasawa 13c4cf
  bindParam(this, "pos_wobble", m_pos_wobble);
shun-iwasawa 13c4cf
  bindParam(this, "tip_width", m_tip_width);
shun-iwasawa 13c4cf
  bindParam(this, "tip_length", m_tip_length);
shun-iwasawa 13c4cf
  bindParam(this, "tip_alpha", m_tip_alpha);
shun-iwasawa 13c4cf
  bindParam(this, "tip_joints", m_tip_joints);
shun-iwasawa 13c4cf
  bindParam(this, "bidirectional", m_bidirectional);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  bindParam(this, "width_randomness", m_width_randomness);
shun-iwasawa 13c4cf
  bindParam(this, "length_randomness", m_length_randomness);
shun-iwasawa 13c4cf
  bindParam(this, "angle_randomness", m_angle_randomness);
shun-iwasawa 13c4cf
  bindParam(this, "sustain_width_to_skew", m_sustain_width_to_skew);
shun-iwasawa 13c4cf
  bindParam(this, "anti_jaggy", m_anti_jaggy);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  bindParam(this, "origin_pos", m_origin_pos);
shun-iwasawa 13c4cf
  bindParam(this, "horizontal_pos", m_horizontal_pos);
shun-iwasawa 13c4cf
  bindParam(this, "vertical_pos", m_vertical_pos);
shun-iwasawa 13c4cf
  bindParam(this, "curve_point", m_curve_point);
shun-iwasawa 13c4cf
  bindParam(this, "fill_gap_size", m_fill_gap_size);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  bindParam(this, "reference_frame", m_reference_frame);
shun-iwasawa 13c4cf
  bindParam(this, "reference_prevalence", m_reference_prevalence);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  bindParam(this, "random_seed", m_random_seed);
shun-iwasawa 13c4cf
  bindParam(this, "sort_by", m_sortBy);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  m_h_density->setValueRange(1.0, 300.0);
shun-iwasawa 13c4cf
  m_v_density->setValueRange(1.0, 300.0);
shun-iwasawa 13c4cf
  m_pos_randomness->setValueRange(0.0, 2.0);
shun-iwasawa 13c4cf
  m_pos_wobble->setValueRange(0.0, 1.0);
shun-iwasawa 13c4cf
  m_tip_width->getMin()->setValueRange(0.0, 1.0);
shun-iwasawa 13c4cf
  m_tip_width->getMax()->setValueRange(0.0, 1.0);
shun-iwasawa 13c4cf
  m_tip_length->getMin()->setValueRange(0.0, 1.0);
shun-iwasawa 13c4cf
  m_tip_length->getMax()->setValueRange(0.0, 1.0);
shun-iwasawa 13c4cf
  m_tip_alpha->getMin()->setValueRange(0.0, 1.0);
shun-iwasawa 13c4cf
  m_tip_alpha->getMax()->setValueRange(0.0, 1.0);
shun-iwasawa 13c4cf
  m_tip_joints->setValueRange(0, 20);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  m_width_randomness->setValueRange(0.0, 0.9);
shun-iwasawa 13c4cf
  m_length_randomness->setValueRange(0.0, 0.9);
shun-iwasawa 13c4cf
  m_angle_randomness->setValueRange(0.0, 180.0);  // degree
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  m_sustain_width_to_skew->setValueRange(0.0, 1.0);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  m_origin_pos->getX()->setMeasureName("fxLength");
shun-iwasawa 13c4cf
  m_origin_pos->getY()->setMeasureName("fxLength");
shun-iwasawa 13c4cf
  m_horizontal_pos->getX()->setMeasureName("fxLength");
shun-iwasawa 13c4cf
  m_horizontal_pos->getY()->setMeasureName("fxLength");
shun-iwasawa 13c4cf
  m_vertical_pos->getX()->setMeasureName("fxLength");
shun-iwasawa 13c4cf
  m_vertical_pos->getY()->setMeasureName("fxLength");
shun-iwasawa 13c4cf
  m_curve_point->getX()->setValueRange(-0.5, 0.5);
shun-iwasawa 13c4cf
  m_curve_point->getY()->setValueRange(-0.5, 0.5);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  m_fill_gap_size->setMeasureName("fxLength");
shun-iwasawa 13c4cf
  m_fill_gap_size->setValueRange(0.0, 50.0);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  m_reference_frame->setValueRange(0, (std::numeric_limits<double>::max)());
shun-iwasawa 13c4cf
  m_reference_prevalence->setValueRange(0.0, 1.0);
shun-iwasawa 13c4cf
  m_random_seed->setValueRange(0, (std::numeric_limits<int>::max)());
shun-iwasawa 13c4cf
  m_sortBy->addItem(Smaller, "Size - Smaller on Top");
shun-iwasawa 13c4cf
  m_sortBy->addItem(Larger, "Size - Larger on Top");
shun-iwasawa 13c4cf
  m_sortBy->addItem(Darker, "Brightness - Darker on Top");
shun-iwasawa 13c4cf
  m_sortBy->addItem(Brighter, "Brightness - Brighter on Top");
shun-iwasawa 13c4cf
  m_sortBy->addItem(Random, "Random");
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // Version 1 (development version) : strokes are placed upward direction if
shun-iwasawa 13c4cf
  // there is no Flow input.
shun-iwasawa 13c4cf
  // Version 2: strokes are directed parallel to the
shun-iwasawa 13c4cf
  // vertical vector of the parallelogram if there is no Flow input.
shun-iwasawa 13c4cf
  setFxVersion(2);
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
bool Iwa_FlowPaintBrushFx::doGetBBox(double frame, TRectD &bBox,
shun-iwasawa 13c4cf
                                     const TRenderSettings &info) {
shun-iwasawa 13c4cf
  if (!m_brush.isConnected()) {
shun-iwasawa 13c4cf
    bBox = TRectD();
shun-iwasawa 13c4cf
    return false;
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
  TPointD origin_pos   = m_origin_pos->getValue(frame);
shun-iwasawa 13c4cf
  TPointD horiz_pos    = m_horizontal_pos->getValue(frame);
shun-iwasawa 13c4cf
  TPointD vert_pos     = m_vertical_pos->getValue(frame);
shun-iwasawa 13c4cf
  TPointD opposite_pos = horiz_pos + vert_pos - origin_pos;
shun-iwasawa 13c4cf
  bBox = boundingBox(origin_pos, horiz_pos, vert_pos, opposite_pos);
shun-iwasawa 13c4cf
  return true;
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//--------------------------------------------------------------
shun-iwasawa 13c4cf
double Iwa_FlowPaintBrushFx::getSizePixelAmount(const double val,
shun-iwasawa 13c4cf
                                                const TAffine affine) {
shun-iwasawa 13c4cf
  /*--- Convert to vector --- */
shun-iwasawa 13c4cf
  TPointD vect;
shun-iwasawa 13c4cf
  vect.x = val;
shun-iwasawa 13c4cf
  vect.y = 0.0;
shun-iwasawa 13c4cf
  /*--- Apply geometrical transformation ---*/
shun-iwasawa 13c4cf
  // For the following lines I referred to lines 586-592 of
shun-iwasawa 13c4cf
  // sources/stdfx/motionblurfx.cpp
shun-iwasawa 13c4cf
  TAffine aff(affine);
shun-iwasawa 13c4cf
  aff.a13 = aff.a23 = 0; /* ignore translation */
shun-iwasawa 13c4cf
  vect              = aff * vect;
shun-iwasawa 13c4cf
  /*--- return the length of the vector ---*/
shun-iwasawa 13c4cf
  return sqrt(vect.x * vect.x + vect.y * vect.y);
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
FlowPaintBrushFxParam Iwa_FlowPaintBrushFx::getParam(
shun-iwasawa 13c4cf
    TTile &tile, double frame, const TRenderSettings &ri) {
shun-iwasawa 13c4cf
  FlowPaintBrushFxParam p;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  TAffine aff          = ri.m_affine;
shun-iwasawa 13c4cf
  p.origin_pos         = aff * m_origin_pos->getValue(frame);
shun-iwasawa 13c4cf
  p.horiz_pos          = aff * m_horizontal_pos->getValue(frame);
shun-iwasawa 13c4cf
  p.vert_pos           = aff * m_vertical_pos->getValue(frame);
shun-iwasawa 13c4cf
  TPointD opposite_pos = p.horiz_pos + p.vert_pos - p.origin_pos;
shun-iwasawa 13c4cf
  p.bbox   = boundingBox(p.origin_pos, p.horiz_pos, p.vert_pos, opposite_pos);
shun-iwasawa 13c4cf
  p.dim.lx = (int)p.bbox.getLx() + 1;
shun-iwasawa 13c4cf
  p.dim.ly = (int)p.bbox.getLy() + 1;
shun-iwasawa 13c4cf
  p.origin_pos -= p.bbox.getP00();
shun-iwasawa 13c4cf
  p.horiz_pos -= p.bbox.getP00();
shun-iwasawa 13c4cf
  p.vert_pos -= p.bbox.getP00();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  p.fill_gap_size =
shun-iwasawa 13c4cf
      (int)getSizePixelAmount(m_fill_gap_size->getValue(frame), aff);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  p.h_density      = m_h_density->getValue(frame);
shun-iwasawa 13c4cf
  p.v_density      = m_v_density->getValue(frame);
shun-iwasawa 13c4cf
  p.pos_randomness = m_pos_randomness->getValue(frame);
shun-iwasawa 13c4cf
  p.pos_wobble     = m_pos_wobble->getValue(frame);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  p.random_seed   = m_random_seed->getValue();
shun-iwasawa 13c4cf
  p.tipLength     = m_tip_length->getValue(frame);
shun-iwasawa 13c4cf
  p.tipWidth      = m_tip_width->getValue(frame);
shun-iwasawa 13c4cf
  p.tipAlpha      = m_tip_alpha->getValue(frame);
shun-iwasawa 13c4cf
  p.width_random  = m_width_randomness->getValue(frame);
shun-iwasawa 13c4cf
  p.length_random = m_length_randomness->getValue(frame);
shun-iwasawa 13c4cf
  p.angle_random  = m_angle_randomness->getValue(frame);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  p.reso       = m_tip_joints->getValue();
shun-iwasawa 13c4cf
  p.anti_jaggy = m_anti_jaggy->getValue();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  p.hVec      = p.horiz_pos - p.origin_pos;
shun-iwasawa 13c4cf
  p.vVec      = p.vert_pos - p.origin_pos;
shun-iwasawa 13c4cf
  p.vVec_unit = double2{normalize(p.vVec).x, normalize(p.vVec).y};
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  p.brushAff = TAffine(p.hVec.x, p.vVec.x, p.origin_pos.x, p.hVec.y, p.vVec.y,
shun-iwasawa 13c4cf
                       p.origin_pos.y);
shun-iwasawa 13c4cf
  return p;
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
void Iwa_FlowPaintBrushFx::fillGapByDilateAndErode(double *buf,
shun-iwasawa 13c4cf
                                                   const TDimension &dim,
shun-iwasawa 13c4cf
                                                   const int fill_gap_size) {
shun-iwasawa 13c4cf
  TRasterGR8P tmp_buf_ras = TRasterGR8P(dim.lx * dim.ly * sizeof(double), 1);
shun-iwasawa 13c4cf
  tmp_buf_ras->lock();
shun-iwasawa 13c4cf
  double *tmp_buf = (double *)tmp_buf_ras->getRawData();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // dilate, then erode
shun-iwasawa 13c4cf
  for (int mode = 0; mode < 2; mode++) {
shun-iwasawa 13c4cf
    for (int i = 0; i < fill_gap_size; i++) {
shun-iwasawa 13c4cf
      double *fromBuf = (i % 2 == 0) ? buf : tmp_buf;
shun-iwasawa 13c4cf
      double *toBuf   = (i % 2 == 0) ? tmp_buf : buf;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      double *to_p  = toBuf;
shun-iwasawa 13c4cf
      double *frm_p = fromBuf;
shun-iwasawa 13c4cf
      for (int y = 0; y < dim.ly; y++) {
shun-iwasawa 13c4cf
        double *n_up =
shun-iwasawa 13c4cf
            (y == 0) ? &fromBuf[y * dim.lx] : &fromBuf[(y - 1) * dim.lx];
shun-iwasawa 13c4cf
        double *n_dn = (y == dim.ly - 1) ? &fromBuf[y * dim.lx]
shun-iwasawa 13c4cf
                                         : &fromBuf[(y + 1) * dim.lx];
shun-iwasawa 13c4cf
        for (int x = 0; x < dim.lx; x++, n_up++, n_dn++, to_p++, frm_p++) {
shun-iwasawa 13c4cf
          if (mode == 0) {
shun-iwasawa 13c4cf
            *to_p = std::max(*frm_p, *n_up);
shun-iwasawa 13c4cf
            *to_p = std::max(*to_p, *n_dn);
shun-iwasawa 13c4cf
            if (x != 0) *to_p = std::max(*to_p, *(frm_p - 1));
shun-iwasawa 13c4cf
            if (x != dim.lx - 1) *to_p = std::max(*to_p, *(frm_p + 1));
shun-iwasawa 13c4cf
          } else {
shun-iwasawa 13c4cf
            *to_p = std::min(*frm_p, *n_up);
shun-iwasawa 13c4cf
            *to_p = std::min(*to_p, *n_dn);
shun-iwasawa 13c4cf
            if (x != 0) *to_p = std::min(*to_p, *(frm_p - 1));
shun-iwasawa 13c4cf
            if (x != dim.lx - 1) *to_p = std::min(*to_p, *(frm_p + 1));
shun-iwasawa 13c4cf
          }
shun-iwasawa 13c4cf
        }
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // 奇数回のフィルタリングの場合、計算結果はtmp_bufに入っているのでbufにコピーする
shun-iwasawa 13c4cf
  if (fill_gap_size % 2 == 1) {
shun-iwasawa 13c4cf
    memcpy(buf, tmp_buf, dim.lx * dim.ly * sizeof(double));
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  tmp_buf_ras->unlock();
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
void Iwa_FlowPaintBrushFx::computeBrushVertices(
shun-iwasawa 13c4cf
    QVector<BrushVertex> &brushVertices, QList<BrushStroke> &brushStrokes,
shun-iwasawa 13c4cf
    FlowPaintBrushFxParam &p, TTile &tile, double frame,
shun-iwasawa 13c4cf
    const TRenderSettings &ri) {
shun-iwasawa 13c4cf
  // 固定するタッチ:基準フレームのArea、Colorを用いて生成、カレントフレームのFlowを用いて流す
shun-iwasawa 13c4cf
  // 動かすタッチ:Area、Color、Flowすべてカレントフレームを用いる。
shun-iwasawa 13c4cf
  int referenceFrame = (int)std::round(m_reference_frame->getValue(frame)) - 1;
shun-iwasawa 13c4cf
  double reference_prevalence = m_reference_prevalence->getValue(frame);
shun-iwasawa 13c4cf
  bool bidirectional          = m_bidirectional->getValue();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  FlowPaintBrushFxParam pivP =
shun-iwasawa 13c4cf
      getParam(tile, referenceFrame, ri);  // TODO: ここ、二度手間防げる
shun-iwasawa 13c4cf
  pivP.lastFrame                  = p.lastFrame;
shun-iwasawa 13c4cf
  FlowPaintBrushFxParam *param[2] = {&p, &pivP};
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  double ref_frame[2]     = {frame, (double)referenceFrame};
shun-iwasawa 13c4cf
  double2 *flow_buf[2]    = {nullptr};
shun-iwasawa 13c4cf
  double *area_buf[2]     = {nullptr};
shun-iwasawa 13c4cf
  colorRGBA *color_buf[2] = {nullptr};
shun-iwasawa 13c4cf
  TRasterGR8P flow_buf_ras[2], area_buf_ras[2], color_buf_ras[2];
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  TRaster32P ras32 = tile.getRaster();
shun-iwasawa 13c4cf
  TRaster64P ras64 = tile.getRaster();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // それぞれの参照画像を取得
shun-iwasawa 13c4cf
  for (int f = 0; f < 2; f++) {
shun-iwasawa 13c4cf
    // Referenceフレームはフレームが-1又はprevalenceが0の場合計算しない
shun-iwasawa 13c4cf
    int tmp_f = ref_frame[f];
shun-iwasawa 13c4cf
    if (f == 1 && (tmp_f < 0 || reference_prevalence == 0.0)) continue;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    // まず、Flowを計算
shun-iwasawa 13c4cf
    if (m_flow.isConnected()) {
shun-iwasawa 13c4cf
      // obtain Flow memory buffer (XY)
shun-iwasawa 13c4cf
      TTile flowTile;
shun-iwasawa 13c4cf
      m_flow->allocateAndCompute(flowTile, param[f]->bbox.getP00(),
shun-iwasawa 13c4cf
                                 param[f]->dim, tile.getRaster(), tmp_f, ri);
shun-iwasawa 13c4cf
      // allocate buffer
shun-iwasawa 13c4cf
      flow_buf_ras[f] =
shun-iwasawa 13c4cf
          TRasterGR8P(param[f]->dim.lx * param[f]->dim.ly * sizeof(double2), 1);
shun-iwasawa 13c4cf
      flow_buf_ras[f]->lock();
shun-iwasawa 13c4cf
      flow_buf[f] = (double2 *)flow_buf_ras[f]->getRawData();
shun-iwasawa 13c4cf
      if (ras32)
shun-iwasawa 13c4cf
        setFlowTileToBuffer<TRaster32P, TPixel32>(flowTile.getRaster(),
shun-iwasawa 13c4cf
                                                  flow_buf[f]);
shun-iwasawa 13c4cf
      else if (ras64)
shun-iwasawa 13c4cf
        setFlowTileToBuffer<TRaster64P, TPixel64>(flowTile.getRaster(),
shun-iwasawa 13c4cf
                                                  flow_buf[f]);
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
    // カレントフレームはprevalenceが1の場合Flowのみ計算
shun-iwasawa 13c4cf
    if (f == 0 && reference_prevalence == 1.0) continue;
shun-iwasawa 13c4cf
    // AreaとColorを計算
shun-iwasawa 13c4cf
    if (m_area.isConnected()) {
shun-iwasawa 13c4cf
      TTile areaTile;
shun-iwasawa 13c4cf
      m_area->allocateAndCompute(areaTile, param[f]->bbox.getP00(),
shun-iwasawa 13c4cf
                                 param[f]->dim, tile.getRaster(), tmp_f, ri);
shun-iwasawa 13c4cf
      // allocate buffer
shun-iwasawa 13c4cf
      area_buf_ras[f] =
shun-iwasawa 13c4cf
          TRasterGR8P(param[f]->dim.lx * param[f]->dim.ly * sizeof(double), 1);
shun-iwasawa 13c4cf
      area_buf_ras[f]->lock();
shun-iwasawa 13c4cf
      area_buf[f] = (double *)area_buf_ras[f]->getRawData();
shun-iwasawa 13c4cf
      if (ras32)
shun-iwasawa 13c4cf
        setAreaTileToBuffer<TRaster32P, TPixel32>(areaTile.getRaster(),
shun-iwasawa 13c4cf
                                                  area_buf[f]);
shun-iwasawa 13c4cf
      else if (ras64)
shun-iwasawa 13c4cf
        setAreaTileToBuffer<TRaster64P, TPixel64>(areaTile.getRaster(),
shun-iwasawa 13c4cf
                                                  area_buf[f]);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      // 間をうめる
shun-iwasawa 13c4cf
      if (param[f]->fill_gap_size > 0)
shun-iwasawa 13c4cf
        fillGapByDilateAndErode(area_buf[f], param[f]->dim,
shun-iwasawa 13c4cf
                                param[f]->fill_gap_size);
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
    if (m_color.isConnected()) {
shun-iwasawa 13c4cf
      TTile colorTile;
shun-iwasawa 13c4cf
      m_color->allocateAndCompute(colorTile, param[f]->bbox.getP00(),
shun-iwasawa 13c4cf
                                  param[f]->dim, tile.getRaster(), tmp_f, ri);
shun-iwasawa 13c4cf
      // allocate buffer
shun-iwasawa 13c4cf
      color_buf_ras[f] = TRasterGR8P(
shun-iwasawa 13c4cf
          param[f]->dim.lx * param[f]->dim.ly * sizeof(colorRGBA), 1);
shun-iwasawa 13c4cf
      color_buf_ras[f]->lock();
shun-iwasawa 13c4cf
      color_buf[f] = (colorRGBA *)color_buf_ras[f]->getRawData();
shun-iwasawa 13c4cf
      if (ras32)
shun-iwasawa 13c4cf
        setColorTileToBuffer<TRaster32P, TPixel32>(colorTile.getRaster(),
shun-iwasawa 13c4cf
                                                   color_buf[f]);
shun-iwasawa 13c4cf
      else if (ras64)
shun-iwasawa 13c4cf
        setColorTileToBuffer<TRaster64P, TPixel64>(colorTile.getRaster(),
shun-iwasawa 13c4cf
                                                   color_buf[f]);
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  enum FRAMETYPE { CURRENT = 0, REFERENCE };
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  auto getFlow = [&](TPointD pos, FRAMETYPE fType) {
shun-iwasawa 13c4cf
    if (!flow_buf[fType]) {
shun-iwasawa 13c4cf
      if (getFxVersion() == 1)
shun-iwasawa 13c4cf
        return double2{0, 1};
shun-iwasawa 13c4cf
      else
shun-iwasawa 13c4cf
        return param[CURRENT]->vVec_unit;
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
    int u = (int)std::round(pos.x);
shun-iwasawa 13c4cf
    int v = (int)std::round(pos.y);
shun-iwasawa 13c4cf
    if (u < 0 || u >= param[fType]->dim.lx || v < 0 ||
shun-iwasawa 13c4cf
        v >= param[fType]->dim.ly) {
shun-iwasawa 13c4cf
      if (getFxVersion() == 1)
shun-iwasawa 13c4cf
        return double2{0, 1};
shun-iwasawa 13c4cf
      else
shun-iwasawa 13c4cf
        return param[CURRENT]->vVec_unit;
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
    return flow_buf[fType][v * param[fType]->dim.lx + u];
shun-iwasawa 13c4cf
  };
shun-iwasawa 13c4cf
  auto getArea = [&](TPointD pos, FRAMETYPE fType) {
shun-iwasawa 13c4cf
    if (!area_buf[fType]) return 1.0;
shun-iwasawa 13c4cf
    int u = (int)std::round(pos.x);
shun-iwasawa 13c4cf
    int v = (int)std::round(pos.y);
shun-iwasawa 13c4cf
    if (u < 0 || u >= param[fType]->dim.lx || v < 0 ||
shun-iwasawa 13c4cf
        v >= param[fType]->dim.ly)
shun-iwasawa 13c4cf
      return 0.0;
shun-iwasawa 13c4cf
    return area_buf[fType][v * param[fType]->dim.lx + u];
shun-iwasawa 13c4cf
  };
shun-iwasawa 13c4cf
  auto getColor = [&](TPointD pos, FRAMETYPE fType) {
shun-iwasawa 13c4cf
    if (!color_buf[fType]) return colorRGBA{1.0, 1.0, 1.0, 1.0};
shun-iwasawa 13c4cf
    int u = (int)std::round(pos.x);
shun-iwasawa 13c4cf
    int v = (int)std::round(pos.y);
shun-iwasawa 13c4cf
    if (u < 0 || u >= param[fType]->dim.lx || v < 0 ||
shun-iwasawa 13c4cf
        v >= param[fType]->dim.ly)
shun-iwasawa 13c4cf
      return colorRGBA{1.0, 1.0, 1.0, 0.0};
shun-iwasawa 13c4cf
    return color_buf[fType][v * param[fType]->dim.lx + u];
shun-iwasawa 13c4cf
  };
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // ランダムの設定
shun-iwasawa 13c4cf
  std::mt19937_64 mt, mt_cur;  // , mt_ref;
shun-iwasawa 13c4cf
  mt.seed(p.random_seed);
shun-iwasawa 13c4cf
  mt_cur.seed(
shun-iwasawa 13c4cf
      (unsigned int)(p.random_seed +
shun-iwasawa 13c4cf
                     (int)std::round(frame)));  // フレーム毎にバラつかせる動き
shun-iwasawa 13c4cf
  std::uniform_real_distribution<> random01(0.0, 1.0);
shun-iwasawa 13c4cf
  std::uniform_int_distribution<> random_textureId(0, p.lastFrame - 1);
shun-iwasawa 13c4cf
  std::uniform_real_distribution<> random_plusminus1(-1.0, 1.0);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // タッチの頂点座標を登録していく
shun-iwasawa 13c4cf
  double v_incr        = 1.0 / p.v_density;
shun-iwasawa 13c4cf
  double h_incr        = 1.0 / p.h_density;
shun-iwasawa 13c4cf
  double v_base_pos    = v_incr * 0.5;
shun-iwasawa 13c4cf
  double segmentAmount = (double)p.reso * 2 - 1;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  int id = 0;  // Mapのキーになる通し番号
shun-iwasawa 13c4cf
  for (int v = 0; v < (int)p.v_density; v++, v_base_pos += v_incr) {
shun-iwasawa 13c4cf
    double h_base_pos = h_incr * 0.5;
shun-iwasawa 13c4cf
    for (int h = 0; h < (int)p.h_density; h++, h_base_pos += h_incr, id++) {
shun-iwasawa 13c4cf
      BrushStroke brushStroke;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      // referenceかカレントか
shun-iwasawa 13c4cf
      FRAMETYPE frameType =
shun-iwasawa 13c4cf
          (referenceFrame >= 0 && random01(mt) <= reference_prevalence)
shun-iwasawa 13c4cf
              ? REFERENCE
shun-iwasawa 13c4cf
              : CURRENT;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      double pos_x = h_base_pos;
shun-iwasawa 13c4cf
      double pos_y = v_base_pos;
shun-iwasawa 13c4cf
      if (frameType == CURRENT) {
shun-iwasawa 13c4cf
        pos_x += (random01(mt) - 0.5) * p.pos_randomness * h_incr;
shun-iwasawa 13c4cf
        pos_y += (random01(mt) - 0.5) * p.pos_randomness * v_incr;
shun-iwasawa 13c4cf
      } else {  // REFERENCE case
shun-iwasawa 13c4cf
        pos_x += (random01(mt) - 0.5) * pivP.pos_randomness * h_incr;
shun-iwasawa 13c4cf
        pos_y += (random01(mt) - 0.5) * pivP.pos_randomness * v_incr;
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
      if (pos_x < 0.0 || pos_x > 1.0 || pos_y < 0.0 || pos_y > 1.0) {
shun-iwasawa 13c4cf
        mt.discard(7);  // ここ、この後のランダム生成回数分が入る
shun-iwasawa 13c4cf
        mt_cur.discard(2);
shun-iwasawa 13c4cf
        continue;
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      //  Area値を拾う座標
shun-iwasawa 13c4cf
      TPointD areaSamplePos =
shun-iwasawa 13c4cf
          param[frameType]->brushAff * TPointD(pos_x, pos_y);
shun-iwasawa 13c4cf
      // 生成範囲の参照画像の輝度を取得
shun-iwasawa 13c4cf
      double areaVal = getArea(areaSamplePos, frameType);
shun-iwasawa 13c4cf
      // 発生するかしないか
shun-iwasawa 13c4cf
      if (random01(mt) > areaVal) {
shun-iwasawa 13c4cf
        mt.discard(6);  // ここにも、この後のランダム生成回数分が入る
shun-iwasawa 13c4cf
        mt_cur.discard(2);
shun-iwasawa 13c4cf
        continue;
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      double2 wobble;
shun-iwasawa 13c4cf
      wobble.x = (random01(mt_cur) - 0.5) * p.pos_wobble * h_incr;
shun-iwasawa 13c4cf
      wobble.y = (random01(mt_cur) - 0.5) * p.pos_wobble * v_incr;
shun-iwasawa 13c4cf
      // 現在のセグメントのレンダリング座標
shun-iwasawa 13c4cf
      brushStroke.originPos = TPointD(pos_x + wobble.x, pos_y + wobble.y);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      brushStroke.color = getColor(areaSamplePos, frameType);
shun-iwasawa 13c4cf
      double alpha      = lerp(param[frameType]->tipAlpha, areaVal);
shun-iwasawa 13c4cf
      // premultiply なのでRGB値にもかける
shun-iwasawa 13c4cf
      brushStroke.color.r *= alpha;
shun-iwasawa 13c4cf
      brushStroke.color.g *= alpha;
shun-iwasawa 13c4cf
      brushStroke.color.b *= alpha;
shun-iwasawa 13c4cf
      brushStroke.color.a *= alpha;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      brushStroke.length =
shun-iwasawa 13c4cf
          lerp(param[frameType]->tipLength, areaVal) *
shun-iwasawa 13c4cf
          (1.0 + random_plusminus1(mt) * param[frameType]->length_random);
shun-iwasawa 13c4cf
      brushStroke.widthHalf =
shun-iwasawa 13c4cf
          lerp(param[frameType]->tipWidth, areaVal) *
shun-iwasawa 13c4cf
          (1.0 + random_plusminus1(mt) * param[frameType]->width_random) * 0.5;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      if (p.anti_jaggy) brushStroke.widthHalf *= 3.0;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      brushStroke.angle =
shun-iwasawa 13c4cf
          random_plusminus1(mt) * param[frameType]->angle_random;
shun-iwasawa 13c4cf
      TAffine flow_rot = TRotation(brushStroke.angle);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      brushStroke.textureId = random_textureId(mt);
shun-iwasawa 13c4cf
      brushStroke.randomVal = random01(mt);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      bool inv           = random01(mt) < 0.5;
shun-iwasawa 13c4cf
      brushStroke.invert = (bidirectional) ? inv : false;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      // まず、芯になる部分の座標を計算する
shun-iwasawa 13c4cf
      QVector<TPointD> centerPosHalf[2];
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      double segLen =
shun-iwasawa 13c4cf
          brushStroke.length /
shun-iwasawa 13c4cf
          segmentAmount;  // セグメントひとつ分の長さ。(ブラシ座標系)
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      bool tooCurve[2] = {false, false};
shun-iwasawa 13c4cf
      TPointD originFlow;
shun-iwasawa 13c4cf
      // 前後方向
shun-iwasawa 13c4cf
      for (int dir = 0; dir < 2; dir++) {
shun-iwasawa 13c4cf
        // curPosを初期位置に
shun-iwasawa 13c4cf
        TPointD curPos(brushStroke.originPos);
shun-iwasawa 13c4cf
        TPointD preFlowUV;
shun-iwasawa 13c4cf
        TPointD startFlow;
shun-iwasawa 13c4cf
        for (int s = 0; s < param[frameType]->reso; s++) {
shun-iwasawa 13c4cf
          //  現在のセグメントのレンダリング座標
shun-iwasawa 13c4cf
          TPointD samplePos = param[CURRENT]->brushAff * curPos;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
          // レンダリング座標から流れベクトル場を取得
shun-iwasawa 13c4cf
          double2 tmp_flow = getFlow(samplePos, CURRENT);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
          //  流れベクトル場をブラシ座標系に変換
shun-iwasawa 13c4cf
          TPointD flow_uv =
shun-iwasawa 13c4cf
              param[CURRENT]->brushAff.inv() * TPointD(tmp_flow.x, tmp_flow.y) -
shun-iwasawa 13c4cf
              param[CURRENT]->brushAff.inv() * TPointD(0., 0.);
shun-iwasawa 13c4cf
          if (s == 0 && dir == 0) originFlow = flow_uv;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
          // ベクトルを正規化
shun-iwasawa 13c4cf
          flow_uv = normalize(flow_uv);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
          // 戻る方向のときはベクトルを反転
shun-iwasawa 13c4cf
          if (dir == 1) flow_uv = -flow_uv;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
          // 流れの方向を回転
shun-iwasawa 13c4cf
          flow_uv = flow_rot * flow_uv;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
          if (s != 0) {
shun-iwasawa 13c4cf
            double dot = flow_uv.x * preFlowUV.x + flow_uv.y * preFlowUV.y;
shun-iwasawa 13c4cf
            if (dot < 0.0) flow_uv = -flow_uv;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
            double dotVsStart =
shun-iwasawa 13c4cf
                startFlow.x * flow_uv.x + startFlow.y * flow_uv.y;
shun-iwasawa 13c4cf
            if (dotVsStart < 0.3) {
shun-iwasawa 13c4cf
              tooCurve[dir] = true;
shun-iwasawa 13c4cf
              break;
shun-iwasawa 13c4cf
            }
shun-iwasawa 13c4cf
          }
shun-iwasawa 13c4cf
          preFlowUV = flow_uv;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
          if (s == 0) startFlow = flow_uv;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
          // ベクトルをセグメント長分のばし、次の点とする。最初のセグメントは半分の長さ。
shun-iwasawa 13c4cf
          TPointD nextPos = curPos + flow_uv * segLen * ((s == 0) ? 0.5 : 1.0);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
          // 頂点を格納
shun-iwasawa 13c4cf
          centerPosHalf[dir].push_back(nextPos);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
          // curPosを進める
shun-iwasawa 13c4cf
          curPos = nextPos;
shun-iwasawa 13c4cf
        }
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      if (tooCurve[0] && tooCurve[1]) continue;
shun-iwasawa 13c4cf
      // 両方滑らかな場合
shun-iwasawa 13c4cf
      if (!tooCurve[0] && !tooCurve[1]) {
shun-iwasawa 13c4cf
        brushStroke.centerPos = centerPosHalf[1];
shun-iwasawa 13c4cf
        for (auto pos : centerPosHalf[0]) brushStroke.centerPos.push_front(pos);
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
      // 片方だけ急カーブの場合、滑らかな方のカーブを反対側に反転して延長する
shun-iwasawa 13c4cf
      else {
shun-iwasawa 13c4cf
        int OkId = (tooCurve[0]) ? 1 : 0;
shun-iwasawa 13c4cf
        TPointD origin(pos_x, pos_y);
shun-iwasawa 13c4cf
        TPointD okInitialVec = centerPosHalf[OkId].at(0) - origin;
shun-iwasawa 13c4cf
        double thetaDeg = std::atan2(okInitialVec.y, okInitialVec.x) / M_PI_180;
shun-iwasawa 13c4cf
        TAffine reflectAff = TTranslation(origin) * TRotation(thetaDeg) *
shun-iwasawa 13c4cf
                             TScale(-1.0, 1.0) * TRotation(-thetaDeg) *
shun-iwasawa 13c4cf
                             TTranslation(-origin);
shun-iwasawa 13c4cf
        brushStroke.centerPos = centerPosHalf[OkId];
shun-iwasawa 13c4cf
        for (auto pos : centerPosHalf[OkId]) {
shun-iwasawa 13c4cf
          TPointD refPos = reflectAff * pos;
shun-iwasawa 13c4cf
          brushStroke.centerPos.push_front(reflectAff * pos);
shun-iwasawa 13c4cf
        }
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      // ここで、基準フレームの流れの向きに従ってテクスチャの向きをそろえてみる
shun-iwasawa 13c4cf
      if (referenceFrame >= 0) {
shun-iwasawa 13c4cf
        TPointD referenceSamplePos =
shun-iwasawa 13c4cf
            param[REFERENCE]->brushAff * TPointD(pos_x, pos_y);
shun-iwasawa 13c4cf
        double2 reference_flow = getFlow(referenceSamplePos, REFERENCE);
shun-iwasawa 13c4cf
        //  流れベクトル場をブラシ座標系に変換
shun-iwasawa 13c4cf
        TPointD reference_flow_uv =
shun-iwasawa 13c4cf
            param[REFERENCE]->brushAff.inv() *
shun-iwasawa 13c4cf
                TPointD(reference_flow.x, reference_flow.y) -
shun-iwasawa 13c4cf
            param[REFERENCE]->brushAff.inv() * TPointD(0., 0.);
shun-iwasawa 13c4cf
        // 内積を取り、反転するか判断する
shun-iwasawa 13c4cf
        double dot = originFlow.x * reference_flow_uv.x +
shun-iwasawa 13c4cf
                     originFlow.y * reference_flow_uv.y;
shun-iwasawa 13c4cf
        if (dot < 0.0) {
shun-iwasawa 13c4cf
          brushStroke.invert = !brushStroke.invert;
shun-iwasawa 13c4cf
        }
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
      // 登録
shun-iwasawa 13c4cf
      brushStrokes.append(brushStroke);
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // ラスターメモリ解放
shun-iwasawa 13c4cf
  for (int f = 0; f < 2; f++) {
shun-iwasawa 13c4cf
    if (flow_buf[f]) flow_buf_ras[f]->unlock();
shun-iwasawa 13c4cf
    if (area_buf[f]) area_buf_ras[f]->unlock();
shun-iwasawa 13c4cf
    if (color_buf[f]) color_buf_ras[f]->unlock();
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // ソート
shun-iwasawa 13c4cf
  StackMode stackMode = (StackMode)m_sortBy->getValue();
shun-iwasawa 13c4cf
  if (stackMode != NoSort) {
shun-iwasawa 13c4cf
    // 大きい方が配列で前、すなわち先(下)に描かれるようにする
shun-iwasawa 13c4cf
    for (auto &stroke : brushStrokes) {
shun-iwasawa 13c4cf
      switch (stackMode) {
shun-iwasawa 13c4cf
      case Smaller:
shun-iwasawa 13c4cf
        stroke.stack = stroke.length * stroke.widthHalf;
shun-iwasawa 13c4cf
        break;
shun-iwasawa 13c4cf
      case Larger:
shun-iwasawa 13c4cf
        stroke.stack = -stroke.length * stroke.widthHalf;
shun-iwasawa 13c4cf
        break;
shun-iwasawa 13c4cf
      // Value = 0.3R 0.59G 0.11B
shun-iwasawa 13c4cf
      case Darker:
shun-iwasawa 13c4cf
        stroke.stack = 0.3 * stroke.color.r + 0.59 * stroke.color.g +
shun-iwasawa 13c4cf
                       0.11 * stroke.color.b;
shun-iwasawa 13c4cf
        break;
shun-iwasawa 13c4cf
      case Brighter:
shun-iwasawa 13c4cf
        stroke.stack = -(0.3 * stroke.color.r + 0.59 * stroke.color.g +
shun-iwasawa 13c4cf
                         0.11 * stroke.color.b);
shun-iwasawa 13c4cf
        break;
shun-iwasawa 13c4cf
      case Random:
shun-iwasawa 13c4cf
        stroke.stack = stroke.randomVal;
shun-iwasawa 13c4cf
        break;
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
    }
shun-iwasawa 443318
    std::sort(brushStrokes.begin(), brushStrokes.end(), strokeStackGraterThan);
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // タッチをせん断に対してこっち向けて太くする割合
shun-iwasawa 13c4cf
  double sustain_width_to_skew = m_sustain_width_to_skew->getValue(frame);
shun-iwasawa 13c4cf
  TPointD curve_point          = m_curve_point->getValue(frame);
shun-iwasawa 13c4cf
  // 頂点座標を登録していく
shun-iwasawa 13c4cf
  for (auto stroke : brushStrokes) {
shun-iwasawa 13c4cf
    int jointCount = stroke.centerPos.size();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    // 頂点位置をカーブさせてゆがめるテスト
shun-iwasawa 13c4cf
    if (curve_point != TPointD()) {
shun-iwasawa 13c4cf
      for (int s = 0; s < stroke.centerPos.size(); s++) {
shun-iwasawa 13c4cf
        TPointD p = stroke.centerPos[s];
shun-iwasawa 13c4cf
        TPointD A = (1.0 - p.x) * (1.0 - p.x) * TPointD(0.0, 0.5) +
shun-iwasawa 13c4cf
                    2.0 * (1.0 - p.x) * p.x *
shun-iwasawa 13c4cf
                        TPointD(0.5 + curve_point.x, 0.5 + curve_point.y) +
shun-iwasawa 13c4cf
                    p.x * p.x * TPointD(1.0, 0.5);
shun-iwasawa 13c4cf
        stroke.centerPos[s] = (1.0 - p.y) * (1.0 - p.y) * TPointD(p.x, 0.0) +
shun-iwasawa 13c4cf
                              2.0 * (1.0 - p.y) * p.y * TPointD(A.x, A.y) +
shun-iwasawa 13c4cf
                              p.y * p.y * TPointD(p.x, 1.0);
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    for (int s = 0; s < jointCount; s++) {
shun-iwasawa 13c4cf
      // 前後の点
shun-iwasawa 13c4cf
      TPointD back = (s == 0) ? stroke.centerPos[0] : stroke.centerPos[s - 1];
shun-iwasawa 13c4cf
      TPointD fore = (s == jointCount - 1) ? stroke.centerPos[jointCount - 1]
shun-iwasawa 13c4cf
                                           : stroke.centerPos[s + 1];
shun-iwasawa 13c4cf
      TPointD vec  = normalize(fore - back);
shun-iwasawa 13c4cf
      TPointD n(-vec.y * stroke.widthHalf, vec.x * stroke.widthHalf);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      // ここで、テクスチャをこっちに向ける処理をいれる
shun-iwasawa 13c4cf
      if (sustain_width_to_skew > 0.0) {
shun-iwasawa 13c4cf
        // 画面座標に変換
shun-iwasawa 13c4cf
        TPointD nr = param[CURRENT]->brushAff * n -
shun-iwasawa 13c4cf
                     param[CURRENT]->brushAff * TPointD(0, 0);
shun-iwasawa 13c4cf
        TPointD vr = param[CURRENT]->brushAff * vec -
shun-iwasawa 13c4cf
                     param[CURRENT]->brushAff * TPointD(0, 0);
shun-iwasawa 13c4cf
        // nr と vrが垂直にちかづくように、nrを回転させる
shun-iwasawa 13c4cf
        double nr_len = norm(nr);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
        nr = (1.0 / nr_len) * nr;
shun-iwasawa 13c4cf
        vr = normalize(vr);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
        // 内積
shun-iwasawa 13c4cf
        double theta     = std::acos(nr * vr);
shun-iwasawa 13c4cf
        double new_theta = sustain_width_to_skew * M_PI * 0.5 +
shun-iwasawa 13c4cf
                           (1.0 - sustain_width_to_skew) * theta;
shun-iwasawa 13c4cf
        // 外積の正負から、nrがvrのどっち側にあるか判断
shun-iwasawa 13c4cf
        if (cross(vr, nr) < 0) new_theta = -new_theta;
shun-iwasawa 13c4cf
        TPointD new_nr(vr.x * cos(new_theta) - vr.y * sin(new_theta),
shun-iwasawa 13c4cf
                       vr.x * sin(new_theta) + vr.y * cos(new_theta));
shun-iwasawa 13c4cf
        new_nr = nr_len * new_nr;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
        // ブラシ座標にもどす
shun-iwasawa 13c4cf
        n = param[CURRENT]->brushAff.inv() *
shun-iwasawa 13c4cf
            (new_nr + param[CURRENT]->brushAff * TPointD(0, 0));
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      // はじっこ
shun-iwasawa 13c4cf
      if (p.anti_jaggy && s == 0) {
shun-iwasawa 13c4cf
        TPointD edgePos = stroke.centerPos[0] * 2.0 - stroke.originPos;
shun-iwasawa 13c4cf
        brushVertices.append(
shun-iwasawa 13c4cf
            BrushVertex(edgePos + n, 0.0, (stroke.invert) ? 1.0 : 0.0));
shun-iwasawa 13c4cf
        brushVertices.append(
shun-iwasawa 13c4cf
            BrushVertex(edgePos - n, 1.0, (stroke.invert) ? 1.0 : 0.0));
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      double texCoord_v = (stroke.invert) ? 1.0 - (double(s) / segmentAmount)
shun-iwasawa 13c4cf
                                          : double(s) / segmentAmount;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      if (p.anti_jaggy) texCoord_v = 0.25 + 0.5 * texCoord_v;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      brushVertices.append(
shun-iwasawa 13c4cf
          BrushVertex(stroke.centerPos[s] + n, 0.0, texCoord_v));
shun-iwasawa 13c4cf
      brushVertices.append(
shun-iwasawa 13c4cf
          BrushVertex(stroke.centerPos[s] - n, 1.0, texCoord_v));
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      // 反対のはじっこ
shun-iwasawa 13c4cf
      if (p.anti_jaggy && s == jointCount - 1) {
shun-iwasawa 13c4cf
        TPointD edgePos =
shun-iwasawa 13c4cf
            stroke.centerPos[jointCount - 1] * 2.0 - stroke.originPos;
shun-iwasawa 13c4cf
        brushVertices.append(
shun-iwasawa 13c4cf
            BrushVertex(edgePos + n, 0.0, (stroke.invert) ? 0.0 : 1.0));
shun-iwasawa 13c4cf
        brushVertices.append(
shun-iwasawa 13c4cf
            BrushVertex(edgePos - n, 1.0, (stroke.invert) ? 0.0 : 1.0));
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
void Iwa_FlowPaintBrushFx::doCompute(TTile &tile, double frame,
shun-iwasawa 13c4cf
                                     const TRenderSettings &ri) {
shun-iwasawa 13c4cf
  if (!m_brush.isConnected()) {
shun-iwasawa 13c4cf
    tile.getRaster()->clear();
shun-iwasawa 13c4cf
    return;
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  TDimension b_size(0, 0);
shun-iwasawa 13c4cf
  int lastFrame = 0;
shun-iwasawa 13c4cf
  std::vector<TRasterP> brushRasters;
shun-iwasawa 13c4cf
  // ブラシタッチのラスターデータを取得
shun-iwasawa 13c4cf
  getBrushRasters(brushRasters, b_size, lastFrame, tile, ri);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  if (lastFrame == 0) {
shun-iwasawa 13c4cf
    tile.getRaster()->clear();
shun-iwasawa 13c4cf
    return;
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // パラメータ取得
shun-iwasawa 13c4cf
  FlowPaintBrushFxParam p = getParam(tile, frame, ri);
shun-iwasawa 13c4cf
  p.lastFrame             = lastFrame;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  int vCount = p.reso * 4;
shun-iwasawa 13c4cf
  // 上下にマージン用の頂点を追加
shun-iwasawa 13c4cf
  if (p.anti_jaggy) vCount += 4;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  QVector<BrushVertex> brushVertices;
shun-iwasawa 13c4cf
  QList<BrushStroke> brushStrokes;
shun-iwasawa 13c4cf
  computeBrushVertices(brushVertices, brushStrokes, p, tile, frame, ri);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  GLfloat m[16] = {(float)p.hVec.x,
shun-iwasawa 13c4cf
                   (float)p.hVec.y,
shun-iwasawa 13c4cf
                   0.f,
shun-iwasawa 13c4cf
                   0.f,
shun-iwasawa 13c4cf
                   (float)p.vVec.x,
shun-iwasawa 13c4cf
                   (float)p.vVec.y,
shun-iwasawa 13c4cf
                   0.f,
shun-iwasawa 13c4cf
                   0.f,
shun-iwasawa 13c4cf
                   0.f,
shun-iwasawa 13c4cf
                   0.f,
shun-iwasawa 13c4cf
                   1.f,
shun-iwasawa 13c4cf
                   0.f,
shun-iwasawa 13c4cf
                   (float)p.origin_pos.x,
shun-iwasawa 13c4cf
                   (float)p.origin_pos.y,
shun-iwasawa 13c4cf
                   0.f,
shun-iwasawa 13c4cf
                   1.f};
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  QOpenGLContext *context = new QOpenGLContext();
shun-iwasawa 13c4cf
  if (QOpenGLContext::currentContext())
shun-iwasawa 13c4cf
    context->setShareContext(QOpenGLContext::currentContext());
shun-iwasawa 13c4cf
  context->setFormat(QSurfaceFormat::defaultFormat());
shun-iwasawa 13c4cf
  context->create();
shun-iwasawa 13c4cf
  context->makeCurrent(ri.m_offScreenSurface.get());
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // テクスチャの確保
shun-iwasawa 13c4cf
  std::vector<QOpenGLTexture *> brushTextures;
shun-iwasawa 13c4cf
  for (auto texRas : brushRasters) {
shun-iwasawa 13c4cf
    QImage texImg(texRas->getRawData(), b_size.lx, b_size.ly,
shun-iwasawa 13c4cf
                  QImage::Format_RGBA8888);
shun-iwasawa 13c4cf
    // 横幅を3倍にする(左右にx1のマージン)
shun-iwasawa 13c4cf
    if (p.anti_jaggy) {
shun-iwasawa 13c4cf
      QImage resizedImage(texImg.width() * 3, texImg.height() * 2,
shun-iwasawa 13c4cf
                          QImage::Format_RGBA8888);
shun-iwasawa 13c4cf
      QPainter painter(&resizedImage);
shun-iwasawa 13c4cf
      painter.setCompositionMode(QPainter::CompositionMode_Source);
shun-iwasawa 13c4cf
      painter.fillRect(resizedImage.rect(), Qt::transparent);
shun-iwasawa 13c4cf
      painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
shun-iwasawa 13c4cf
      painter.drawImage(QPoint(texImg.width(), texImg.height() / 2), texImg);
shun-iwasawa 13c4cf
      painter.end();
shun-iwasawa 13c4cf
      texImg = resizedImage;
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    QOpenGLTexture *texture = new QOpenGLTexture(texImg.rgbSwapped());
shun-iwasawa 13c4cf
    texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
shun-iwasawa 13c4cf
    texture->setMagnificationFilter(QOpenGLTexture::Linear);
shun-iwasawa 13c4cf
    brushTextures.push_back(texture);
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  TDimensionI outDim = tile.getRaster()->getSize();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // 描画
shun-iwasawa 13c4cf
  {
shun-iwasawa 13c4cf
    std::unique_ptr<QOpenGLFramebufferObject> fb(
shun-iwasawa 13c4cf
        new QOpenGLFramebufferObject(p.dim.lx, p.dim.ly));
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    fb->bind();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    glViewport(0, 0, p.dim.lx, p.dim.ly);
shun-iwasawa 13c4cf
    glClearColor(0, 0, 0, 0);
shun-iwasawa 13c4cf
    glClear(GL_COLOR_BUFFER_BIT);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    glMatrixMode(GL_PROJECTION);
shun-iwasawa 13c4cf
    glLoadIdentity();
shun-iwasawa 13c4cf
    gluOrtho2D(0, p.dim.lx, 0, p.dim.ly);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    glMatrixMode(GL_MODELVIEW);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    glLoadIdentity();
shun-iwasawa 13c4cf
    glLoadMatrixf(m);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    glDisable(GL_POLYGON_SMOOTH);
shun-iwasawa 13c4cf
    glEnable(GL_BLEND);
shun-iwasawa 13c4cf
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
shun-iwasawa 13c4cf
    glEnable(GL_TEXTURE_2D);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    glEnableClientState(GL_VERTEX_ARRAY);
shun-iwasawa 13c4cf
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
shun-iwasawa 13c4cf
    {
shun-iwasawa 13c4cf
      glVertexPointer(2, GL_DOUBLE, sizeof(BrushVertex), brushVertices.data());
shun-iwasawa 13c4cf
      glTexCoordPointer(2, GL_DOUBLE, sizeof(BrushVertex),
shun-iwasawa 13c4cf
                        (double *)brushVertices.data() + 2);
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    int first = 0;
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    for (auto stroke : brushStrokes) {
shun-iwasawa 13c4cf
      brushTextures[stroke.textureId]->bind();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
      glColor4d(stroke.color.r, stroke.color.g, stroke.color.b, stroke.color.a);
shun-iwasawa 13c4cf
      glDrawArrays(GL_TRIANGLE_STRIP, first, vCount);
shun-iwasawa 13c4cf
      first += vCount;
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    glFlush();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    glDisableClientState(GL_VERTEX_ARRAY);
shun-iwasawa 13c4cf
    glDisableClientState(GL_TEXTURE_COORD_ARRAY);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    QImage img =
shun-iwasawa 13c4cf
        fb->toImage().scaled(QSize(p.dim.lx, p.dim.ly), Qt::IgnoreAspectRatio,
shun-iwasawa 13c4cf
                             Qt::SmoothTransformation);
shun-iwasawa 13c4cf
    // y座標は上下反転していることに注意!!
shun-iwasawa 13c4cf
    QRect subRect(tile.m_pos.x - (int)std::round(p.bbox.getP00().x),
shun-iwasawa 13c4cf
                  (int)std::round(p.bbox.getP01().y) - tile.m_pos.y - outDim.ly,
shun-iwasawa 13c4cf
                  outDim.lx, outDim.ly);
shun-iwasawa 13c4cf
    QImage subImg = img.copy(subRect);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    TRaster32P ras32 = tile.getRaster();
shun-iwasawa 13c4cf
    TRaster64P ras64 = tile.getRaster();
shun-iwasawa 13c4cf
    TRaster32P resultRas;
shun-iwasawa 13c4cf
    if (ras32)
shun-iwasawa 13c4cf
      resultRas = ras32;
shun-iwasawa 13c4cf
    else if (ras64) {
shun-iwasawa 13c4cf
      resultRas = TRaster32P(outDim.lx, outDim.ly);
shun-iwasawa 13c4cf
      resultRas->lock();
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    for (int y = 0; y < outDim.ly; y++) {
shun-iwasawa 13c4cf
      QRgb *rgb_p =
shun-iwasawa 13c4cf
          reinterpret_cast<QRgb *>(subImg.scanLine(outDim.ly - 1 - y));
shun-iwasawa 13c4cf
      TPixel32 *dst_p = resultRas->pixels(y);
shun-iwasawa 13c4cf
      for (int x = 0; x < outDim.lx; x++, rgb_p++, dst_p++) {
shun-iwasawa 13c4cf
        (*dst_p).r = (unsigned char)qRed(*rgb_p);
shun-iwasawa 13c4cf
        (*dst_p).g = (unsigned char)qGreen(*rgb_p);
shun-iwasawa 13c4cf
        (*dst_p).b = (unsigned char)qBlue(*rgb_p);
shun-iwasawa 13c4cf
        (*dst_p).m = (unsigned char)qAlpha(*rgb_p);
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    if (ras64) {
shun-iwasawa 13c4cf
      TRop::convert(ras64, resultRas);
shun-iwasawa 13c4cf
      resultRas->unlock();
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
    fb->release();
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
  context->deleteLater();
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  for (auto tex : brushTextures) {
shun-iwasawa 13c4cf
    delete tex;
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
void Iwa_FlowPaintBrushFx::getParamUIs(TParamUIConcept *&concepts,
shun-iwasawa 13c4cf
                                       int &length) {
shun-iwasawa 13c4cf
  concepts = new TParamUIConcept[length = 4];
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  concepts[0].m_type  = TParamUIConcept::POINT;
shun-iwasawa 13c4cf
  concepts[0].m_label = "Origin";
shun-iwasawa 13c4cf
  concepts[0].m_params.push_back(m_origin_pos);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  concepts[1].m_type  = TParamUIConcept::POINT;
shun-iwasawa 13c4cf
  concepts[1].m_label = "Horizontal Range";
shun-iwasawa 13c4cf
  concepts[1].m_params.push_back(m_horizontal_pos);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  concepts[2].m_type  = TParamUIConcept::POINT;
shun-iwasawa 13c4cf
  concepts[2].m_label = "Vertical Range";
shun-iwasawa 13c4cf
  concepts[2].m_params.push_back(m_vertical_pos);
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  concepts[3].m_type = TParamUIConcept::PARALLELOGRAM;
shun-iwasawa 13c4cf
  concepts[3].m_params.push_back(m_origin_pos);
shun-iwasawa 13c4cf
  concepts[3].m_params.push_back(m_horizontal_pos);
shun-iwasawa 13c4cf
  concepts[3].m_params.push_back(m_vertical_pos);
shun-iwasawa 13c4cf
  concepts[3].m_params.push_back(m_curve_point);
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
//------------------------------------------------------------
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
std::string Iwa_FlowPaintBrushFx::getAlias(double frame,
shun-iwasawa 13c4cf
                                           const TRenderSettings &info) const {
shun-iwasawa 13c4cf
  double refFrame   = m_reference_frame->getValue(frame);
shun-iwasawa 13c4cf
  double prevalence = m_reference_prevalence->getValue(frame);
shun-iwasawa 13c4cf
  if (refFrame < 0 || prevalence == 0.0) {
shun-iwasawa 13c4cf
    // check if the brush is wobbled
shun-iwasawa 13c4cf
    double wobble     = m_pos_wobble->getValue(frame);
shun-iwasawa 13c4cf
    std::string alias = TStandardRasterFx::getAlias(frame, info);
shun-iwasawa 13c4cf
    if (areAlmostEqual(wobble, 0.0))
shun-iwasawa 13c4cf
      return alias;
shun-iwasawa 13c4cf
    else {
shun-iwasawa 13c4cf
      alias.insert(getFxType().length() + 1, std::to_string(frame) + ",");
shun-iwasawa 13c4cf
      return alias;
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  std::string alias = getFxType();
shun-iwasawa 13c4cf
  alias += "[";
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  // alias degli effetti connessi alle porte di input separati da virgole
shun-iwasawa 13c4cf
  // una porta non connessa da luogo a un alias vuoto (stringa vuota)
shun-iwasawa 13c4cf
  for (int i = 0; i < getInputPortCount(); ++i) {
shun-iwasawa 13c4cf
    TFxPort *port = getInputPort(i);
shun-iwasawa 13c4cf
    if (port->isConnected()) {
shun-iwasawa 13c4cf
      TRasterFxP ifx = port->getFx();
shun-iwasawa 13c4cf
      assert(ifx);
shun-iwasawa 13c4cf
      alias += ifx->getAlias(frame, info);
shun-iwasawa 13c4cf
      // add alias of flow, area and color map in the reference frame
shun-iwasawa 13c4cf
      if (getInputPortName(i) != "Brush") {
shun-iwasawa 13c4cf
        alias += ",";
shun-iwasawa 13c4cf
        alias += ifx->getAlias(refFrame, info);
shun-iwasawa 13c4cf
      }
shun-iwasawa 13c4cf
    }
shun-iwasawa 13c4cf
    alias += ",";
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  std::string paramalias("");
shun-iwasawa 13c4cf
  for (int i = 0; i < getParams()->getParamCount(); ++i) {
shun-iwasawa 13c4cf
    TParam *param = getParams()->getParam(i);
shun-iwasawa 13c4cf
    paramalias += param->getName() + "=" + param->getValueAlias(frame, 3);
shun-iwasawa 13c4cf
  }
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
  return alias + std::to_string(frame) + "," + std::to_string(getIdentifier()) +
shun-iwasawa 13c4cf
         paramalias + "]";
shun-iwasawa 13c4cf
}
shun-iwasawa 13c4cf
shun-iwasawa 13c4cf
FX_PLUGIN_IDENTIFIER(Iwa_FlowPaintBrushFx, "iwa_FlowPaintBrushFx")