|
|
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 |
|