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
shun-iwasawa 98926d
namespace {
manongjohn 6c9506
void putOnRasterCM(const TRasterCM32P &out, const TRaster32P &in, int styleId,
manongjohn 6c9506
                   bool lockAlpha) {
shun-iwasawa 98926d
  if (!out.getPointer() || !in.getPointer()) return;
shun-iwasawa 98926d
  assert(out->getSize() == in->getSize());
shun-iwasawa 98926d
  int x, y;
shun-iwasawa 98926d
  for (y = 0; y < out->getLy(); y++) {
shun-iwasawa 98926d
    for (x = 0; x < out->getLx(); x++) {
shun-iwasawa 98926d
#ifdef _DEBUG
shun-iwasawa 98926d
      assert(x >= 0 && x < in->getLx());
shun-iwasawa 98926d
      assert(y >= 0 && y < in->getLy());
shun-iwasawa 98926d
      assert(x >= 0 && x < out->getLx());
shun-iwasawa 98926d
      assert(y >= 0 && y < out->getLy());
shun-iwasawa 98926d
#endif
shun-iwasawa 98926d
      TPixel32 *inPix = &in->pixels(y)[x];
shun-iwasawa 98926d
      if (inPix->m == 0) continue;
shun-iwasawa 98926d
      TPixelCM32 *outPix = &out->pixels(y)[x];
manongjohn 6c9506
      if (lockAlpha && !outPix->isPureInk() && outPix->getPaint() == 0 &&
manongjohn 6c9506
          outPix->getTone() == 255) {
manongjohn 6c9506
        *outPix =
manongjohn 6c9506
            TPixelCM32(outPix->getInk(), outPix->getPaint(), outPix->getTone());
manongjohn 6c9506
        continue;
manongjohn 6c9506
      }
manongjohn 6c9506
      bool sameStyleId = styleId == outPix->getInk();
justburner 5aabe8
      // line with lock alpha : use original pixel's tone
shun-iwasawa 98926d
      // line with the same style : multiply tones
shun-iwasawa 98926d
      // line with different style : pick darker tone
justburner 5aabe8
      int tone = lockAlpha     ? outPix->getTone()
justburner 5aabe8
                 : sameStyleId ? outPix->getTone() * (255 - inPix->m) / 255
justburner 5aabe8
                               : std::min(255 - inPix->m, outPix->getTone());
shun-iwasawa 98926d
      int ink = !sameStyleId && outPix->getTone() < 255 - inPix->m
shun-iwasawa 98926d
                    ? outPix->getInk()
shun-iwasawa 98926d
                    : styleId;
shun-iwasawa 98926d
      *outPix = TPixelCM32(ink, outPix->getPaint(), tone);
shun-iwasawa 98926d
    }
shun-iwasawa 98926d
  }
shun-iwasawa 98926d
}
shun-iwasawa 98926d
}  // namespace
bf1d82
bf1d82
//=======================================================
bf1d82
//
bf1d82
// Raster32PMyPaintSurface::Internal
bf1d82
//
bf1d82
//=======================================================
bf1d82
shun-iwasawa 98926d
class Raster32PMyPaintSurface::Internal
shun-iwasawa 98926d
    : public mypaint::helpers::SurfaceCustom
shun-iwasawa 98926d
                                             askWrite> {
bf1d82
public:
bf1d82
  typedef SurfaceCustom Parent;
shun-iwasawa 98926d
  Internal(Raster32PMyPaintSurface &owner)
7a5892
      : SurfaceCustom(owner.ras->pixels(), owner.ras->getLx(),
7a5892
                      owner.ras->getLy(), owner.ras->getPixelSize(),
7a5892
                      owner.ras->getRowSize(), &owner) {}
bf1d82
};
bf1d82
bf1d82
//=======================================================
bf1d82
//
bf1d82
// Raster32PMyPaintSurface
bf1d82
//
bf1d82
//=======================================================
bf1d82
shun-iwasawa 98926d
Raster32PMyPaintSurface::Raster32PMyPaintSurface(const TRaster32P &ras)
7a5892
    : ras(ras), controller(), internal() {
bf1d82
  assert(ras);
bf1d82
  internal = new Internal(*this);
bf1d82
}
bf1d82
shun-iwasawa 98926d
Raster32PMyPaintSurface::Raster32PMyPaintSurface(const TRaster32P &ras,
shun-iwasawa 98926d
                                                 RasterController &controller)
7a5892
    : ras(ras), controller(&controller), internal() {
bf1d82
  assert(ras);
bf1d82
  internal = new Internal(*this);
bf1d82
}
bf1d82
shun-iwasawa 98926d
Raster32PMyPaintSurface::~Raster32PMyPaintSurface() { delete internal; }
bf1d82
bf1d82
bool Raster32PMyPaintSurface::getColor(float x, float y, float radius,
shun-iwasawa 98926d
                                       float &colorR, float &colorG,
shun-iwasawa 98926d
                                       float &colorB, float &colorA) {
shun-iwasawa 98926d
  return internal->getColor(x, y, radius, colorR, colorG, colorB, colorA);
shun-iwasawa 98926d
}
bf1d82
shun-iwasawa 98926d
bool Raster32PMyPaintSurface::drawDab(const mypaint::Dab &dab) {
shun-iwasawa 98926d
  return internal->drawDab(dab);
shun-iwasawa 98926d
}
bf1d82
shun-iwasawa 98926d
bool Raster32PMyPaintSurface::getAntialiasing() const {
shun-iwasawa 98926d
  return internal->antialiasing;
shun-iwasawa 98926d
}
bf1d82
shun-iwasawa 98926d
void Raster32PMyPaintSurface::setAntialiasing(bool value) {
shun-iwasawa 98926d
  internal->antialiasing = value;
shun-iwasawa 98926d
}
bf1d82
bf1d82
//=======================================================
bf1d82
//
bf1d82
// MyPaintToonzBrush
bf1d82
//
bf1d82
//=======================================================
bf1d82
d8eddc
MyPaintToonzBrush::MyPaintToonzBrush(
d8eddc
  const TRaster32P &ras,
d8eddc
  RasterController &controller,
d8eddc
  const mypaint::Brush &brush,
d8eddc
  bool interpolation
d8eddc
)
7a5892
    : ras(ras)
7a5892
    , mypaintSurface(ras, controller)
shun-iwasawa 98926d
    , brush(brush)
d8eddc
    , reset(true)
d8eddc
    , interpolation(interpolation)
d8eddc
{
bf1d82
  // read brush antialiasing settings
bf1d82
  float aa = this->brush.getBaseValue(MYPAINT_BRUSH_SETTING_ANTI_ALIASING);
7a5892
  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);
shun-iwasawa 98926d
  for (int i = 0; i < MYPAINT_BRUSH_INPUTS_COUNT; ++i)
shun-iwasawa 98926d
    this->brush.setMappingN(MYPAINT_BRUSH_SETTING_ANTI_ALIASING,
shun-iwasawa 98926d
                            (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) {
d8eddc
    if (interpolation)
7a5892
      strokeTo(TPointD(current.x, current.y), current.pressure, TPointD(current.tx, current.ty), 0.f);
bf1d82
    beginStroke();
bf1d82
  }
bf1d82
}
bf1d82
7a5892
void MyPaintToonzBrush::strokeTo(const TPointD &position, double pressure,
7a5892
                                 const TPointD &tilt, double dtime) {
7a5892
  Params next(position.x, position.y, pressure, tilt.x, tilt.y, 0.0);
bf1d82
bf1d82
  if (reset) {
shun-iwasawa 98926d
    current  = next;
bf1d82
    previous = current;
shun-iwasawa 98926d
    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
  }
bf1d82
d8eddc
  if (interpolation) {
d8eddc
    next.time = current.time + dtime;
d8eddc
  
d8eddc
    // accuracy
d8eddc
    const double threshold    = 1.0;
d8eddc
    const double thresholdSqr = threshold * threshold;
d8eddc
    const int maxLevel        = 16;
d8eddc
d8eddc
    // set initial segment
d8eddc
    Segment stack[maxLevel + 1];
d8eddc
    Params p0;
d8eddc
    Segment *segment    = stack;
d8eddc
    Segment *maxSegment = segment + maxLevel;
d8eddc
    p0.setMedian(previous, current);
d8eddc
    segment->p1 = current;
d8eddc
    segment->p2.setMedian(current, next);
d8eddc
d8eddc
    // process
d8eddc
    while (true) {
d8eddc
      double dx = segment->p2.x - p0.x;
d8eddc
      double dy = segment->p2.y - p0.y;
d8eddc
      if (dx * dx + dy * dy > thresholdSqr && segment != maxSegment) {
d8eddc
        Segment *sub = segment + 1;
d8eddc
        sub->p1.setMedian(p0, segment->p1);
d8eddc
        segment->p1.setMedian(segment->p1, segment->p2);
d8eddc
        sub->p2.setMedian(sub->p1, segment->p1);
d8eddc
        segment = sub;
d8eddc
      } else {
7a5892
        brush.strokeTo(mypaintSurface, segment->p2.x, segment->p2.y,
7a5892
                      segment->p2.pressure, segment->p2.tx, segment->p2.ty,
d8eddc
                      segment->p2.time - p0.time);
d8eddc
        if (segment == stack) break;
d8eddc
        p0 = segment->p2;
d8eddc
        --segment;
d8eddc
      }
bf1d82
    }
bf1d82
d8eddc
    // keep parameters for future interpolation
d8eddc
    previous = current;
d8eddc
    current  = next;
bf1d82
d8eddc
    // shift time
d8eddc
    previous.time = 0.0;
d8eddc
    current.time  = dtime;
d8eddc
  } else {
7a5892
    brush.strokeTo(mypaintSurface, position.x, position.y, pressure, tilt.x, tilt.y, dtime);
d8eddc
  }
bf1d82
}
bf1d82
shun-iwasawa 98926d
//----------------------------------------------------------------------------------
shun-iwasawa 98926d
shun-iwasawa 98926d
void MyPaintToonzBrush::updateDrawing(const TRasterCM32P rasCM,
shun-iwasawa 98926d
                                      const TRasterCM32P rasBackupCM,
manongjohn 6c9506
                                      const TRect &bbox, int styleId,
manongjohn 6c9506
                                      bool lockAlpha) const {
shun-iwasawa 98926d
  if (!rasCM) return;
shun-iwasawa 98926d
shun-iwasawa 98926d
  TRect rasRect    = rasCM->getBounds();
shun-iwasawa 98926d
  TRect targetRect = bbox * rasRect;
shun-iwasawa 98926d
  if (targetRect.isEmpty()) return;
shun-iwasawa 98926d
shun-iwasawa 98926d
  rasCM->copy(rasBackupCM->extract(targetRect), targetRect.getP00());
7a5892
  putOnRasterCM(rasCM->extract(targetRect), ras->extract(targetRect), styleId,
manongjohn 6c9506
                lockAlpha);
d8eddc
}