|
|
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 |
87c65e |
int ink = !sameStyleId && outPix->getTone() < 255 - inPix->m
|
|
shun-iwasawa |
87c65e |
? outPix->getInk()
|
|
shun-iwasawa |
87c65e |
: styleId;
|
|
shun-iwasawa |
87c65e |
*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 |
|
|
shun-iwasawa |
87c65e |
MyPaintToonzBrush::MyPaintToonzBrush(const TRaster32P &ras,
|
|
shun-iwasawa |
87c65e |
RasterController &controller,
|
|
shun-iwasawa |
87c65e |
const mypaint::Brush &brush,
|
|
shun-iwasawa |
87c65e |
bool interpolation)
|
|
shun-iwasawa |
87c65e |
: m_ras(ras)
|
|
shun-iwasawa |
87c65e |
, m_mypaintSurface(ras, controller)
|
|
shun-iwasawa |
87c65e |
, m_brush(brush)
|
|
shun-iwasawa |
87c65e |
, m_reset(true)
|
|
shun-iwasawa |
87c65e |
, m_interpolation(interpolation) {
|
|
|
bf1d82 |
// read brush antialiasing settings
|
|
shun-iwasawa |
87c65e |
float aa = this->m_brush.getBaseValue(MYPAINT_BRUSH_SETTING_ANTI_ALIASING);
|
|
shun-iwasawa |
87c65e |
m_mypaintSurface.setAntialiasing(aa > 0.5f);
|
|
|
bf1d82 |
|
|
|
bf1d82 |
// reset brush antialiasing to zero to avoid radius and hardness correction
|
|
shun-iwasawa |
87c65e |
this->m_brush.setBaseValue(MYPAINT_BRUSH_SETTING_ANTI_ALIASING, 0.f);
|
|
shun-iwasawa |
98926d |
for (int i = 0; i < MYPAINT_BRUSH_INPUTS_COUNT; ++i)
|
|
shun-iwasawa |
87c65e |
this->m_brush.setMappingN(MYPAINT_BRUSH_SETTING_ANTI_ALIASING,
|
|
shun-iwasawa |
87c65e |
(MyPaintBrushInput)i, 0);
|
|
|
bf1d82 |
}
|
|
|
bf1d82 |
|
|
|
bf1d82 |
void MyPaintToonzBrush::beginStroke() {
|
|
shun-iwasawa |
87c65e |
m_brush.reset();
|
|
shun-iwasawa |
87c65e |
m_brush.newStroke();
|
|
shun-iwasawa |
87c65e |
m_reset = true;
|
|
|
bf1d82 |
}
|
|
|
bf1d82 |
|
|
|
bf1d82 |
void MyPaintToonzBrush::endStroke() {
|
|
shun-iwasawa |
87c65e |
if (!m_reset) {
|
|
shun-iwasawa |
87c65e |
if (m_interpolation)
|
|
shun-iwasawa |
87c65e |
strokeTo(TPointD(m_current.x, m_current.y), m_current.pressure,
|
|
shun-iwasawa |
87c65e |
TPointD(m_current.tx, m_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 |
|
|
shun-iwasawa |
87c65e |
if (m_reset) {
|
|
shun-iwasawa |
87c65e |
m_current = next;
|
|
shun-iwasawa |
87c65e |
m_previous = m_current;
|
|
shun-iwasawa |
87c65e |
m_reset = false;
|
|
|
bf1d82 |
// we need to jump to initial point (heuristic)
|
|
shun-iwasawa |
87c65e |
m_brush.setState(MYPAINT_BRUSH_STATE_X, m_current.x);
|
|
shun-iwasawa |
87c65e |
m_brush.setState(MYPAINT_BRUSH_STATE_Y, m_current.y);
|
|
shun-iwasawa |
87c65e |
m_brush.setState(MYPAINT_BRUSH_STATE_ACTUAL_X, m_current.x);
|
|
shun-iwasawa |
87c65e |
m_brush.setState(MYPAINT_BRUSH_STATE_ACTUAL_Y, m_current.y);
|
|
|
bf1d82 |
return;
|
|
|
bf1d82 |
}
|
|
|
bf1d82 |
|
|
shun-iwasawa |
87c65e |
if (m_interpolation) {
|
|
shun-iwasawa |
87c65e |
next.time = m_current.time + dtime;
|
|
shun-iwasawa |
87c65e |
|
|
|
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;
|
|
shun-iwasawa |
87c65e |
p0.setMedian(m_previous, m_current);
|
|
shun-iwasawa |
87c65e |
segment->p1 = m_current;
|
|
shun-iwasawa |
87c65e |
segment->p2.setMedian(m_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 {
|
|
shun-iwasawa |
87c65e |
m_brush.strokeTo(m_mypaintSurface, segment->p2.x, segment->p2.y,
|
|
shun-iwasawa |
87c65e |
segment->p2.pressure, segment->p2.tx, segment->p2.ty,
|
|
shun-iwasawa |
87c65e |
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
|
|
shun-iwasawa |
87c65e |
m_previous = m_current;
|
|
shun-iwasawa |
87c65e |
m_current = next;
|
|
|
bf1d82 |
|
|
|
d8eddc |
// shift time
|
|
shun-iwasawa |
87c65e |
m_previous.time = 0.0;
|
|
shun-iwasawa |
87c65e |
m_current.time = dtime;
|
|
|
d8eddc |
} else {
|
|
shun-iwasawa |
87c65e |
m_brush.strokeTo(m_mypaintSurface, position.x, position.y, pressure, tilt.x,
|
|
shun-iwasawa |
87c65e |
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());
|
|
shun-iwasawa |
87c65e |
putOnRasterCM(rasCM->extract(targetRect), m_ras->extract(targetRect), styleId,
|
|
manongjohn |
6c9506 |
lockAlpha);
|
|
|
d8eddc |
}
|