bf1d82
bf1d82
#include <algorithm></algorithm>
bf1d82
bf1d82
#include "mypainttoonzbrush.h"
bf1d82
#include "tropcm.h"
bf1d82
#include "tpixelutils.h"
bf1d82
#include <toonz mypainthelpers.hpp=""></toonz>
bf1d82
bf1d82
#include <qcolor></qcolor>
bf1d82
bf1d82
bf1d82
//=======================================================
bf1d82
//
bf1d82
// Raster32PMyPaintSurface::Internal
bf1d82
//
bf1d82
//=======================================================
bf1d82
bf1d82
class Raster32PMyPaintSurface::Internal:
bf1d82
  public mypaint::helpers::SurfaceCustom<readpixel, askread,="" askwrite="" writepixel,=""></readpixel,>
bf1d82
{
bf1d82
public:
bf1d82
  typedef SurfaceCustom Parent;
bf1d82
  Internal(Raster32PMyPaintSurface &owner):
bf1d82
    SurfaceCustom( owner.m_ras->pixels(),
bf1d82
                   owner.m_ras->getLx(),
bf1d82
                   owner.m_ras->getLy(),
bf1d82
                   owner.m_ras->getPixelSize(),
bf1d82
                   owner.m_ras->getRowSize(),
bf1d82
                   &owner )
bf1d82
  { }
bf1d82
};
bf1d82
bf1d82
//=======================================================
bf1d82
//
bf1d82
// Raster32PMyPaintSurface
bf1d82
//
bf1d82
//=======================================================
bf1d82
bf1d82
Raster32PMyPaintSurface::Raster32PMyPaintSurface(const TRaster32P &ras):
bf1d82
  m_ras(ras),
bf1d82
  controller(),
bf1d82
  internal()
bf1d82
{
bf1d82
  assert(ras);
bf1d82
  internal = new Internal(*this);
bf1d82
}
bf1d82
bf1d82
Raster32PMyPaintSurface::Raster32PMyPaintSurface(const TRaster32P &ras, RasterController &controller):
bf1d82
  m_ras(ras),
bf1d82
  controller(&controller),
bf1d82
  internal()
bf1d82
{
bf1d82
  assert(ras);
bf1d82
  internal = new Internal(*this);
bf1d82
}
bf1d82
bf1d82
Raster32PMyPaintSurface::~Raster32PMyPaintSurface()
bf1d82
  { delete internal; }
bf1d82
bf1d82
bool Raster32PMyPaintSurface::getColor(float x, float y, float radius,
bf1d82
                                       float &colorR, float &colorG, float &colorB, float &colorA)
bf1d82
{ return internal->getColor(x, y, radius, colorR, colorG, colorB, colorA); }
bf1d82
bf1d82
bool Raster32PMyPaintSurface::drawDab(const mypaint::Dab &dab)
bf1d82
  { return internal->drawDab(dab); }
bf1d82
bf1d82
bool Raster32PMyPaintSurface::getAntialiasing() const
bf1d82
  { return internal->antialiasing; }
bf1d82
bf1d82
void Raster32PMyPaintSurface::setAntialiasing(bool value)
bf1d82
  { internal->antialiasing = value; }
bf1d82
bf1d82
//=======================================================
bf1d82
//
bf1d82
// MyPaintToonzBrush
bf1d82
//
bf1d82
//=======================================================
bf1d82
bf1d82
MyPaintToonzBrush::MyPaintToonzBrush(const TRaster32P &ras, RasterController &controller, const mypaint::Brush &brush):
bf1d82
  m_ras(ras),
bf1d82
  m_mypaintSurface(m_ras, controller),
bf1d82
  brush(brush),
bf1d82
  reset(true)
bf1d82
{
bf1d82
  // read brush antialiasing settings
bf1d82
  float aa = this->brush.getBaseValue(MYPAINT_BRUSH_SETTING_ANTI_ALIASING);
bf1d82
  m_mypaintSurface.setAntialiasing(aa > 0.5f);
bf1d82
bf1d82
  // reset brush antialiasing to zero to avoid radius and hardness correction
bf1d82
  this->brush.setBaseValue(MYPAINT_BRUSH_SETTING_ANTI_ALIASING, 0.f);
bf1d82
  for(int i = 0; i < MYPAINT_BRUSH_INPUTS_COUNT; ++i)
bf1d82
    this->brush.setMappingN(MYPAINT_BRUSH_SETTING_ANTI_ALIASING, (MyPaintBrushInput)i, 0);
bf1d82
}
bf1d82
bf1d82
void MyPaintToonzBrush::beginStroke() {
bf1d82
  brush.reset();
bf1d82
  brush.newStroke();
bf1d82
  reset = true;
bf1d82
}
bf1d82
bf1d82
void MyPaintToonzBrush::endStroke() {
bf1d82
  if (!reset) {
bf1d82
    strokeTo(TPointD(current.x, current.y), current.pressure, 0.f);
bf1d82
    beginStroke();
bf1d82
  }
bf1d82
}
bf1d82
bf1d82
void MyPaintToonzBrush::strokeTo(const TPointD &point, double pressure, double dtime) {
bf1d82
  Params next(point.x, point.y, pressure, 0.0);
bf1d82
bf1d82
  if (reset) {
bf1d82
    current = next;
bf1d82
    previous = current;
bf1d82
    reset = false;
bf1d82
    // we need to jump to initial point (heuristic)
bf1d82
    brush.setState(MYPAINT_BRUSH_STATE_X, current.x);
bf1d82
    brush.setState(MYPAINT_BRUSH_STATE_Y, current.y);
bf1d82
    brush.setState(MYPAINT_BRUSH_STATE_ACTUAL_X, current.x);
bf1d82
    brush.setState(MYPAINT_BRUSH_STATE_ACTUAL_Y, current.y);
bf1d82
    return;
bf1d82
  } else {
bf1d82
    next.time = current.time + dtime;
bf1d82
  }
bf1d82
bf1d82
  // accuracy
bf1d82
  const double threshold = 1.0;
bf1d82
  const double thresholdSqr = threshold*threshold;
bf1d82
  const int maxLevel = 16;
bf1d82
bf1d82
  // set initial segment
bf1d82
  Segment stack[maxLevel+1];
bf1d82
  Params p0;
bf1d82
  Segment *segment = stack;
bf1d82
  Segment *maxSegment = segment + maxLevel;
bf1d82
  p0.setMedian(previous, current);
bf1d82
  segment->p1 = current;
bf1d82
  segment->p2.setMedian(current, next);
bf1d82
bf1d82
  // process
bf1d82
  while(true) {
bf1d82
    double dx = segment->p2.x - p0.x;
bf1d82
    double dy = segment->p2.y - p0.y;
bf1d82
    if (dx*dx + dy*dy > thresholdSqr && segment != maxSegment) {
bf1d82
      Segment *sub = segment + 1;
bf1d82
      sub->p1.setMedian(p0, segment->p1);
bf1d82
      segment->p1.setMedian(segment->p1, segment->p2);
bf1d82
      sub->p2.setMedian(sub->p1, segment->p1);
bf1d82
      segment = sub;
bf1d82
    } else {
bf1d82
      brush.strokeTo(m_mypaintSurface, segment->p2.x, segment->p2.y, segment->p2.pressure, 0.f, 0.f, segment->p2.time - p0.time);
bf1d82
      if (segment == stack) break;
bf1d82
      p0 = segment->p2;
bf1d82
      --segment;
bf1d82
    }
bf1d82
  }
bf1d82
bf1d82
  // keep parameters for future interpolation
bf1d82
  previous = current;
bf1d82
  current  = next;
bf1d82
bf1d82
  // shift time
bf1d82
  previous.time = 0.0;
bf1d82
  current.time = dtime;
bf1d82
}
bf1d82