// The following contains classes and prototypes for most code of centerline vectorization. #include "tcenterlinevectP.h" // TnzLib includes #include "toonz/tcenterlinevectorizer.h" #include "toonz/Naa2TlvConverter.h" // TnzCore includes #include "tpalette.h" #include "tcolorstyles.h" #include "trastercm.h" #include "ttoonzimage.h" #include "trasterimage.h" #include "tvectorimage.h" #include "tgeometry.h" #include "tstroke.h" #include "tropcm.h" // STD includes #include #include #include #include #include #include #include #include //========================================================================== //********************************* //* Further miscellaneous * //********************************* //-------------------------------------------------------------------------- //Reduces strokes of image by currConfig->m_reduceThicknessRatio/100 inline void reduceThickness(TVectorImageP image, double thicknessRatio) { thicknessRatio *= 0.01; unsigned int i; int j; for (i = 0; i < image->getStrokeCount(); ++i) { for (j = 0; j < image->getStroke(i)->getControlPointCount(); ++j) { TThickPoint newCP = image->getStroke(i)->getControlPoint(j); newCP.thick *= thicknessRatio; image->getStroke(i)->setControlPoint(j, newCP); } } } //======================================================================== //Delete Skeleton graphs and list inline void deleteSkeletonList(SkeletonList *skeleton) { unsigned int i; for (i = 0; i < skeleton->size(); ++i) delete (*skeleton)[i]; delete skeleton; } //======================================================================== TVectorImageP copyStrokes(std::vector &strokes) { unsigned int i; TVectorImageP result = new TVectorImage; for (i = 0; i < strokes.size(); ++i) if (strokes[i]->getStyle() >= 0) result->addStroke(strokes[i]); return result; } //======================================================================== inline TThickPoint randomized(const TThickPoint &P) { TThickPoint Q = P; Q.x += ((((double)rand()) / RAND_MAX) - 0.5) * 0.1; Q.y += ((((double)rand()) / RAND_MAX) - 0.5) * 0.1; return Q; } //------------------------------------------------------------------------ //Give stroke extremities a random shake. May help for region computing. void randomizeExtremities(TVectorImageP vi) { unsigned int i; for (i = 0; i < vi->getStrokeCount(); ++i) { TThickPoint P = vi->getStroke(i)->getControlPoint(0); vi->getStroke(i)->setControlPoint(0, randomized(P)); int n = vi->getStroke(i)->getControlPointCount(); P = vi->getStroke(i)->getControlPoint(n - 1); vi->getStroke(i)->setControlPoint(n - 1, randomized(P)); } } //======================================================================== //Looped frame inline void addFrameStrokes(TVectorImageP vi, TRasterP ras, TPalette *palette) { const double epsThick = 1.0; TStroke *frameStroke; std::vector CPs; CPs.push_back(TThickPoint(0, 0, epsThick)); CPs.push_back(TThickPoint(ras->getLx() / 2.0, 0, epsThick)); CPs.push_back(TThickPoint(ras->getLx(), 0, epsThick)); CPs.push_back(TThickPoint(ras->getLx(), 0, epsThick)); CPs.push_back(TThickPoint(ras->getLx(), 0, epsThick)); CPs.push_back(TThickPoint(ras->getLx(), ras->getLy() / 2.0, epsThick)); CPs.push_back(TThickPoint(ras->getLx(), ras->getLy(), epsThick)); CPs.push_back(TThickPoint(ras->getLx(), ras->getLy(), epsThick)); CPs.push_back(TThickPoint(ras->getLx(), ras->getLy(), epsThick)); CPs.push_back(TThickPoint(ras->getLx() / 2.0, ras->getLy(), epsThick)); CPs.push_back(TThickPoint(0, ras->getLy(), epsThick)); CPs.push_back(TThickPoint(0, ras->getLy(), epsThick)); CPs.push_back(TThickPoint(0, ras->getLy(), epsThick)); CPs.push_back(TThickPoint(0, ras->getLy() / 2.0, epsThick)); CPs.push_back(TThickPoint(0, 0, epsThick)); frameStroke = new TStroke(CPs); frameStroke->setStyle(0); frameStroke->setSelfLoop(true); vi->addStroke(frameStroke); } //======================================================================== //Returns if input stroke is edge boundary of an ink-filled region bool VectorizerCore::isInkRegionEdge(TStroke *stroke) { return stroke->getFlag(SkeletonArc::SS_OUTLINE); } //Similar as above bool VectorizerCore::isInkRegionEdgeReversed(TStroke *stroke) { return stroke->getFlag(SkeletonArc::SS_OUTLINE_REVERSED); } void VectorizerCore::clearInkRegionFlags(TVectorImageP vi) { int flag = SkeletonArc::SS_OUTLINE | SkeletonArc::SS_OUTLINE_REVERSED; for (int i = 0; i < (int)vi->getStrokeCount(); i++) vi->getStroke(i)->setFlag(flag, false); } //========================================================================== //************************ //* Vectorizer Main * //************************ //-------------------------------------------------------------------------- TVectorImageP VectorizerCore::centerlineVectorize( TImageP &image, const CenterlineConfiguration &configuration, TPalette *palette) { TRasterImageP ri = image; TToonzImageP ti = image; TRasterP ras; if (ri) ras = ri->getRaster(); else { ras = ti->getRaster()->clone(); TRop::expandPaint(ras); } if (configuration.m_naaSource) { if (TRaster32P ras32 = ras) { Naa2TlvConverter converter; converter.process(ras32); converter.setPalette(palette); if (ti = converter.makeTlv(true)) // Transparent synthetic inks { image = ti; ras = ti->getRaster(); TRop::expandPaint(ras); } } } VectorizerCoreGlobals globals; globals.currConfig = &configuration; Contours polygons; polygonize(ras, polygons, globals); //Most time-consuming part of vectorization, 'this' is passed to inform of partial progresses SkeletonList *skeletons = skeletonize(polygons, this, globals); if (isCanceled()) { //Clean and return 0 at cancel command deleteSkeletonList(skeletons); return TVectorImageP(); } organizeGraphs(skeletons, globals); //junctionRecovery(polygons); //Da' problemi per maxThickness sortibleResult; TVectorImageP result; calculateSequenceColors(ras, globals); //Extract stroke colors here conversionToStrokes(sortibleResult, globals); applyStrokeColors(sortibleResult, ras, palette, globals); //Strokes get sorted here result = copyStrokes(sortibleResult); //Further misc adjustments if (globals.currConfig->m_thicknessRatio < 100) reduceThickness(result, configuration.m_thicknessRatio); if (globals.currConfig->m_maxThickness == 0.0) for (unsigned int i = 0; i < result->getStrokeCount(); ++i) result->getStroke(i)->setSelfLoop(true); if (globals.currConfig->m_makeFrame) addFrameStrokes(result, ras, palette); //randomizeExtremities(result); //Cuccio random - non serve... deleteSkeletonList(skeletons); return result; }