| |
| |
| #include "toonzrasterbrushtool.h" |
| |
| |
| #include "tools/toolhandle.h" |
| #include "tools/toolutils.h" |
| #include "tools/tooloptions.h" |
| |
| |
| #include "toonzqt/dvdialog.h" |
| #include "toonzqt/imageutils.h" |
| |
| |
| #include "toonz/tobjecthandle.h" |
| #include "toonz/txsheethandle.h" |
| #include "toonz/txshlevelhandle.h" |
| #include "toonz/tframehandle.h" |
| #include "toonz/tcolumnhandle.h" |
| #include "toonz/txsheet.h" |
| #include "toonz/tstageobject.h" |
| #include "toonz/tstageobjectspline.h" |
| #include "toonz/ttileset.h" |
| #include "toonz/txshsimplelevel.h" |
| #include "toonz/toonzimageutils.h" |
| #include "toonz/palettecontroller.h" |
| #include "toonz/stage2.h" |
| #include "toonz/preferences.h" |
| #include "toonz/tpalettehandle.h" |
| #include "toonz/mypaintbrushstyle.h" |
| |
| |
| #include "tstream.h" |
| #include "tcolorstyles.h" |
| #include "tvectorimage.h" |
| #include "tenv.h" |
| #include "tregion.h" |
| #include "tinbetween.h" |
| |
| #include "tgl.h" |
| #include "trop.h" |
| |
| |
| #include <QPainter> |
| |
| using namespace ToolUtils; |
| |
| TEnv::DoubleVar RasterBrushMinSize("InknpaintRasterBrushMinSize", 1); |
| TEnv::DoubleVar RasterBrushMaxSize("InknpaintRasterBrushMaxSize", 5); |
| TEnv::DoubleVar BrushSmooth("InknpaintBrushSmooth", 0); |
| TEnv::IntVar BrushDrawOrder("InknpaintBrushDrawOrder", 0); |
| TEnv::IntVar RasterBrushPencilMode("InknpaintRasterBrushPencilMode", 0); |
| TEnv::IntVar BrushPressureSensitivity("InknpaintBrushPressureSensitivity", 1); |
| TEnv::DoubleVar RasterBrushHardness("RasterBrushHardness", 100); |
| TEnv::DoubleVar RasterBrushModifierSize("RasterBrushModifierSize", 0); |
| TEnv::StringVar RasterBrushPreset("RasterBrushPreset", "<custom>"); |
| TEnv::IntVar BrushLockAlpha("InknpaintBrushLockAlpha", 0); |
| |
| |
| #define CUSTOM_WSTR L"<custom>" |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void split(TStroke *stroke, const std::vector<double> ¶meterValues, |
| std::vector<TStroke *> &strokes) { |
| TThickPoint p2; |
| std::vector<TThickPoint> points; |
| TThickPoint lastPoint = stroke->getControlPoint(0); |
| int n = parameterValues.size(); |
| int chunk; |
| double t; |
| int last_chunk = -1, startPoint = 0; |
| double lastLocT = 0; |
| |
| for (int i = 0; i < n; i++) { |
| points.push_back(lastPoint); |
| double w = |
| parameterValues[i]; |
| stroke->getChunkAndT(w, chunk, |
| t); |
| |
| if (i == 0) |
| startPoint = 1; |
| else { |
| int indexAfterLastT = |
| stroke->getControlPointIndexAfterParameter(parameterValues[i - 1]); |
| startPoint = indexAfterLastT; |
| if ((indexAfterLastT & 1) && lastLocT != 1) startPoint++; |
| } |
| int endPoint = 2 * chunk + 1; |
| if (lastLocT != 1 && i > 0) { |
| if (last_chunk != chunk || t == 1) |
| points.push_back(p2); |
| |
| } |
| |
| for (int j = startPoint; j < endPoint; j++) |
| points.push_back(stroke->getControlPoint(j)); |
| |
| TThickPoint p, A, B, C; |
| p = stroke->getPoint(w); |
| C = stroke->getControlPoint(2 * chunk + 2); |
| B = stroke->getControlPoint(2 * chunk + 1); |
| A = stroke->getControlPoint(2 * chunk); |
| p.thick = A.thick; |
| |
| if (last_chunk != chunk) { |
| TThickPoint p1 = (1 - t) * A + t * B; |
| points.push_back(p1); |
| p.thick = p1.thick; |
| } else { |
| if (t != 1) { |
| |
| |
| double tInters = lastLocT / t; |
| TThickPoint p11 = (1 - t) * A + t * B; |
| TThickPoint p1 = (1 - tInters) * p11 + tInters * p; |
| points.push_back(p1); |
| p.thick = p1.thick; |
| } |
| } |
| |
| points.push_back(p); |
| |
| if (t != 1) p2 = (1 - t) * B + t * C; |
| |
| assert(points.size() & 1); |
| |
| |
| TStroke *strokeAdd = new TStroke(points); |
| strokeAdd->setStyle(stroke->getStyle()); |
| strokeAdd->outlineOptions() = stroke->outlineOptions(); |
| strokes.push_back(strokeAdd); |
| |
| lastPoint = p; |
| last_chunk = chunk; |
| lastLocT = t; |
| points.clear(); |
| } |
| |
| points.push_back(lastPoint); |
| |
| if (lastLocT != 1) points.push_back(p2); |
| |
| startPoint = |
| stroke->getControlPointIndexAfterParameter(parameterValues[n - 1]); |
| if ((stroke->getControlPointIndexAfterParameter(parameterValues[n - 1]) & |
| 1) && |
| lastLocT != 1) |
| startPoint++; |
| for (int j = startPoint; j < stroke->getControlPointCount(); j++) |
| points.push_back(stroke->getControlPoint(j)); |
| |
| assert(points.size() & 1); |
| TStroke *strokeAdd = new TStroke(points); |
| strokeAdd->setStyle(stroke->getStyle()); |
| strokeAdd->outlineOptions() = stroke->outlineOptions(); |
| strokes.push_back(strokeAdd); |
| points.clear(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static double curvature(TPointD dp, TPointD ddp) { |
| if (dp == TPointD(0, 0)) |
| return 0; |
| else |
| return fabs(cross(dp, ddp) / pow(norm2(dp), 1.5)); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static void findMaxCurvPoints(TStroke *stroke, const float &angoloLim, |
| const float &curvMaxLim, |
| std::vector<double> ¶meterValues) { |
| TPointD tg1, tg2; |
| |
| TPointD dp, ddp; |
| |
| parameterValues.clear(); |
| int cpn = stroke ? stroke->getControlPointCount() : 0; |
| for (int j = 2; j < cpn; j += 2) { |
| TPointD p0 = stroke->getControlPoint(j - 2); |
| TPointD p1 = stroke->getControlPoint(j - 1); |
| TPointD p2 = stroke->getControlPoint(j); |
| |
| TPointD q = p1 - (p0 + p2) * 0.5; |
| |
| |
| if (j > 2) { |
| tg2 = -p0 + p2 + 2 * q; |
| double prod_scal = |
| tg2 * tg1; |
| assert(tg1 != TPointD(0, 0) || tg2 != TPointD(0, 0)); |
| |
| double angolo = |
| acos(prod_scal / (pow(norm2(tg2), 0.5) * pow(norm2(tg1), 0.5))); |
| |
| |
| if (angolo > angoloLim) { |
| double w = getWfromChunkAndT(stroke, (UINT)(0.5 * (j - 2)), |
| 0); |
| parameterValues.push_back(w); |
| } |
| } |
| tg1 = -p0 + p2 - 2 * q; |
| |
| |
| |
| |
| |
| |
| double estremo_int = 0; |
| double t = -1; |
| if (q != TPointD(0, 0)) { |
| t = 0.25 * |
| (2 * q.x * q.x + 2 * q.y * q.y - q.x * p0.x + q.x * p2.x - |
| q.y * p0.y + q.y * p2.y) / |
| (q.x * q.x + q.y * q.y); |
| |
| dp = -p0 + p2 + 2 * q - 4 * t * q; |
| ddp = -4 * q; |
| estremo_int = curvature(dp, ddp); |
| |
| double h = 0.01; |
| dp = -p0 + p2 + 2 * q - 4 * (t + h) * q; |
| double c_dx = curvature(dp, ddp); |
| dp = -p0 + p2 + 2 * q - 4 * (t - h) * q; |
| double c_sx = curvature(dp, ddp); |
| |
| if (estremo_int < c_dx && estremo_int < c_sx) { |
| estremo_int = 0; |
| } |
| } |
| double curv_max = estremo_int; |
| |
| |
| |
| dp = -p0 + p2 + 2 * q; |
| double estremo_sx = curvature(dp, ddp); |
| |
| |
| dp = -p0 + p2 - 2 * q; |
| double estremo_dx = curvature(dp, ddp); |
| |
| |
| |
| double t_ext; |
| if (estremo_sx >= estremo_dx) |
| t_ext = 0; |
| else |
| t_ext = 1; |
| double maxEstremi = std::max(estremo_dx, estremo_sx); |
| if (maxEstremi > estremo_int) { |
| t = t_ext; |
| curv_max = maxEstremi; |
| } |
| |
| |
| if (t >= 0 && t <= 1 && curv_max > curvMaxLim) { |
| double w = getWfromChunkAndT(stroke, (UINT)(0.5 * (j - 2)), |
| t); |
| parameterValues.push_back(w); |
| } |
| |
| } |
| |
| |
| if ((int)parameterValues.size() > 1) { |
| std::sort(parameterValues.begin(), parameterValues.end()); |
| parameterValues.erase( |
| std::unique(parameterValues.begin(), parameterValues.end()), |
| parameterValues.end()); |
| } |
| } |
| |
| static void addStroke(TTool::Application *application, const TVectorImageP &vi, |
| TStroke *stroke, bool breakAngles, bool frameCreated, |
| bool levelCreated, TXshSimpleLevel *sLevel = NULL, |
| TFrameId fid = TFrameId::NO_FRAME) { |
| QMutexLocker lock(vi->getMutex()); |
| |
| if (application->getCurrentObject()->isSpline()) { |
| application->getCurrentXsheet()->notifyXsheetChanged(); |
| return; |
| } |
| |
| std::vector<double> corners; |
| std::vector<TStroke *> strokes; |
| |
| const float angoloLim = |
| 1; |
| |
| const float curvMaxLim = 0.8; |
| |
| |
| findMaxCurvPoints(stroke, angoloLim, curvMaxLim, corners); |
| TXshSimpleLevel *sl; |
| if (!sLevel) { |
| sl = application->getCurrentLevel()->getSimpleLevel(); |
| } else { |
| sl = sLevel; |
| } |
| TFrameId id = application->getCurrentTool()->getTool()->getCurrentFid(); |
| if (id == TFrameId::NO_FRAME && fid != TFrameId::NO_FRAME) id = fid; |
| if (!corners.empty()) { |
| if (breakAngles) |
| split(stroke, corners, strokes); |
| else |
| strokes.push_back(new TStroke(*stroke)); |
| |
| int n = strokes.size(); |
| |
| TUndoManager::manager()->beginBlock(); |
| for (int i = 0; i < n; i++) { |
| std::vector<TFilledRegionInf> *fillInformation = |
| new std::vector<TFilledRegionInf>; |
| ImageUtils::getFillingInformationOverlappingArea(vi, *fillInformation, |
| stroke->getBBox()); |
| TStroke *str = new TStroke(*strokes[i]); |
| vi->addStroke(str); |
| TUndoManager::manager()->add(new UndoPencil(str, fillInformation, sl, id, |
| frameCreated, levelCreated)); |
| } |
| TUndoManager::manager()->endBlock(); |
| } else { |
| std::vector<TFilledRegionInf> *fillInformation = |
| new std::vector<TFilledRegionInf>; |
| ImageUtils::getFillingInformationOverlappingArea(vi, *fillInformation, |
| stroke->getBBox()); |
| TStroke *str = new TStroke(*stroke); |
| vi->addStroke(str); |
| TUndoManager::manager()->add(new UndoPencil(str, fillInformation, sl, id, |
| frameCreated, levelCreated)); |
| } |
| |
| |
| |
| |
| |
| vi->findRegions(); |
| |
| for (int k = 0; k < (int)strokes.size(); k++) delete strokes[k]; |
| strokes.clear(); |
| |
| application->getCurrentTool()->getTool()->notifyImageChanged(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| namespace { |
| |
| |
| |
| void addStrokeToImage(TTool::Application *application, const TVectorImageP &vi, |
| TStroke *stroke, bool breakAngles, bool frameCreated, |
| bool levelCreated, TXshSimpleLevel *sLevel = NULL, |
| TFrameId id = TFrameId::NO_FRAME) { |
| QMutexLocker lock(vi->getMutex()); |
| addStroke(application, vi.getPointer(), stroke, breakAngles, frameCreated, |
| levelCreated, sLevel, id); |
| |
| |
| } |
| |
| |
| |
| enum DrawOrder { OverAll = 0, UnderAll, PaletteOrder }; |
| |
| void getAboveStyleIdSet(int styleId, TPaletteP palette, |
| QSet<int> &aboveStyles) { |
| if (!palette) return; |
| for (int p = 0; p < palette->getPageCount(); p++) { |
| TPalette::Page *page = palette->getPage(p); |
| for (int s = 0; s < page->getStyleCount(); s++) { |
| int tmpId = page->getStyleId(s); |
| if (tmpId == styleId) return; |
| if (tmpId != 0) aboveStyles.insert(tmpId); |
| } |
| } |
| } |
| |
| |
| |
| class RasterBrushUndo final : public TRasterUndo { |
| std::vector<TThickPoint> m_points; |
| int m_styleId; |
| bool m_selective; |
| bool m_isPaletteOrder; |
| bool m_isPencil; |
| bool m_isStraight; |
| bool m_modifierLockAlpha; |
| |
| public: |
| RasterBrushUndo(TTileSetCM32 *tileSet, const std::vector<TThickPoint> &points, |
| int styleId, bool selective, TXshSimpleLevel *level, |
| const TFrameId &frameId, bool isPencil, bool isFrameCreated, |
| bool isLevelCreated, bool isPaletteOrder, bool lockAlpha, |
| bool isStraight = false) |
| : TRasterUndo(tileSet, level, frameId, isFrameCreated, isLevelCreated, 0) |
| , m_points(points) |
| , m_styleId(styleId) |
| , m_selective(selective) |
| , m_isPaletteOrder(isPaletteOrder) |
| , m_isPencil(isPencil) |
| , m_isStraight(isStraight) |
| , m_modifierLockAlpha(lockAlpha) {} |
| |
| void redo() const override { |
| insertLevelAndFrameIfNeeded(); |
| TToonzImageP image = getImage(); |
| TRasterCM32P ras = image->getRaster(); |
| RasterStrokeGenerator rasterTrack( |
| ras, BRUSH, NONE, m_styleId, m_points[0], m_selective, 0, |
| m_modifierLockAlpha, !m_isPencil, m_isPaletteOrder); |
| if (m_isPaletteOrder) { |
| QSet<int> aboveStyleIds; |
| getAboveStyleIdSet(m_styleId, image->getPalette(), aboveStyleIds); |
| rasterTrack.setAboveStyleIds(aboveStyleIds); |
| } |
| rasterTrack.setPointsSequence(m_points); |
| rasterTrack.generateStroke(m_isPencil, m_isStraight); |
| image->setSavebox(image->getSavebox() + |
| rasterTrack.getBBox(rasterTrack.getPointsSequence())); |
| ToolUtils::updateSaveBox(); |
| TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| int getSize() const override { |
| return sizeof(*this) + TRasterUndo::getSize(); |
| } |
| QString getToolName() override { return QString("Brush Tool"); } |
| int getHistoryType() override { return HistoryType::BrushTool; } |
| }; |
| |
| |
| |
| class RasterBluredBrushUndo final : public TRasterUndo { |
| std::vector<TThickPoint> m_points; |
| int m_styleId; |
| DrawOrder m_drawOrder; |
| int m_maxThick; |
| double m_hardness; |
| bool m_isStraight; |
| bool m_modifierLockAlpha; |
| |
| public: |
| RasterBluredBrushUndo(TTileSetCM32 *tileSet, |
| const std::vector<TThickPoint> &points, int styleId, |
| DrawOrder drawOrder, bool lockAlpha, |
| TXshSimpleLevel *level, const TFrameId &frameId, |
| int maxThick, double hardness, bool isFrameCreated, |
| bool isLevelCreated, bool isStraight = false) |
| : TRasterUndo(tileSet, level, frameId, isFrameCreated, isLevelCreated, 0) |
| , m_points(points) |
| , m_styleId(styleId) |
| , m_drawOrder(drawOrder) |
| , m_maxThick(maxThick) |
| , m_hardness(hardness) |
| , m_isStraight(isStraight) |
| , m_modifierLockAlpha(lockAlpha) {} |
| |
| void redo() const override { |
| if (m_points.size() == 0) return; |
| insertLevelAndFrameIfNeeded(); |
| TToonzImageP image = getImage(); |
| TRasterCM32P ras = image->getRaster(); |
| TRasterCM32P backupRas = ras->clone(); |
| TRaster32P workRaster(ras->getSize()); |
| QRadialGradient brushPad = ToolUtils::getBrushPad(m_maxThick, m_hardness); |
| workRaster->clear(); |
| BluredBrush brush(workRaster, m_maxThick, brushPad, false); |
| |
| if (m_drawOrder == PaletteOrder) { |
| QSet<int> aboveStyleIds; |
| getAboveStyleIdSet(m_styleId, image->getPalette(), aboveStyleIds); |
| brush.setAboveStyleIds(aboveStyleIds); |
| } |
| |
| std::vector<TThickPoint> points; |
| points.push_back(m_points[0]); |
| TRect bbox = brush.getBoundFromPoints(points); |
| brush.addPoint(m_points[0], 1); |
| brush.updateDrawing(ras, ras, bbox, m_styleId, (int)m_drawOrder, |
| m_modifierLockAlpha); |
| if (m_isStraight) { |
| points.clear(); |
| points.push_back(m_points[0]); |
| points.push_back(m_points[1]); |
| points.push_back(m_points[2]); |
| bbox = brush.getBoundFromPoints(points); |
| brush.addArc(m_points[0], m_points[1], m_points[2], 1, 1); |
| brush.updateDrawing(ras, backupRas, bbox, m_styleId, (int)m_drawOrder, |
| m_modifierLockAlpha); |
| } else if (m_points.size() > 1) { |
| points.clear(); |
| points.push_back(m_points[0]); |
| points.push_back(m_points[1]); |
| bbox = brush.getBoundFromPoints(points); |
| brush.addArc(m_points[0], (m_points[1] + m_points[0]) * 0.5, m_points[1], |
| 1, 1); |
| brush.updateDrawing(ras, backupRas, bbox, m_styleId, (int)m_drawOrder, |
| m_modifierLockAlpha); |
| int i; |
| for (i = 1; i + 2 < (int)m_points.size(); i = i + 2) { |
| points.clear(); |
| points.push_back(m_points[i]); |
| points.push_back(m_points[i + 1]); |
| points.push_back(m_points[i + 2]); |
| bbox = brush.getBoundFromPoints(points); |
| brush.addArc(m_points[i], m_points[i + 1], m_points[i + 2], 1, 1); |
| brush.updateDrawing(ras, backupRas, bbox, m_styleId, (int)m_drawOrder, |
| m_modifierLockAlpha); |
| } |
| } |
| ToolUtils::updateSaveBox(); |
| TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| int getSize() const override { |
| return sizeof(*this) + TRasterUndo::getSize(); |
| } |
| |
| QString getToolName() override { return QString("Brush Tool"); } |
| int getHistoryType() override { return HistoryType::BrushTool; } |
| }; |
| |
| |
| |
| class MyPaintBrushUndo final : public TRasterUndo { |
| TPoint m_offset; |
| QString m_id; |
| |
| public: |
| MyPaintBrushUndo(TTileSetCM32 *tileSet, TXshSimpleLevel *level, |
| const TFrameId &frameId, bool isFrameCreated, |
| bool isLevelCreated, const TRasterCM32P &ras, |
| const TPoint &offset) |
| : TRasterUndo(tileSet, level, frameId, isFrameCreated, isLevelCreated, 0) |
| , m_offset(offset) { |
| static int counter = 0; |
| m_id = QString("MyPaintBrushUndo") + QString::number(counter++); |
| TImageCache::instance()->add(m_id.toStdString(), |
| TToonzImageP(ras, TRect(ras->getSize()))); |
| } |
| |
| ~MyPaintBrushUndo() { TImageCache::instance()->remove(m_id); } |
| |
| void redo() const override { |
| insertLevelAndFrameIfNeeded(); |
| |
| TToonzImageP image = getImage(); |
| TRasterCM32P ras = image->getRaster(); |
| |
| TImageP srcImg = |
| TImageCache::instance()->get(m_id.toStdString(), false)->cloneImage(); |
| TToonzImageP tSrcImg = srcImg; |
| assert(tSrcImg); |
| ras->copy(tSrcImg->getRaster(), m_offset); |
| ToolUtils::updateSaveBox(); |
| TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged(); |
| notifyImageChanged(); |
| } |
| |
| int getSize() const override { |
| return sizeof(*this) + TRasterUndo::getSize(); |
| } |
| |
| QString getToolName() override { return QString("Brush Tool"); } |
| int getHistoryType() override { return HistoryType::BrushTool; } |
| }; |
| |
| |
| |
| double computeThickness(double pressure, const TDoublePairProperty &property) { |
| double t = pressure * pressure * pressure; |
| double thick0 = property.getValue().first; |
| double thick1 = property.getValue().second; |
| |
| if (thick1 < 0.0001) thick0 = thick1 = 0.0; |
| return (thick0 + (thick1 - thick0) * t) * 0.5; |
| } |
| |
| |
| |
| int computeThickness(double pressure, const TIntPairProperty &property) { |
| double t = pressure * pressure * pressure; |
| int thick0 = property.getValue().first; |
| int thick1 = property.getValue().second; |
| return tround(thick0 + (thick1 - thick0) * t); |
| } |
| |
| } |
| |
| |
| |
| static void CatmullRomInterpolate(const TThickPoint &P0, const TThickPoint &P1, |
| const TThickPoint &P2, const TThickPoint &P3, |
| int samples, |
| std::vector<TThickPoint> &points) { |
| double x0 = P1.x; |
| double x1 = (-P0.x + P2.x) * 0.5f; |
| double x2 = P0.x - 2.5f * P1.x + 2.0f * P2.x - 0.5f * P3.x; |
| double x3 = -0.5f * P0.x + 1.5f * P1.x - 1.5f * P2.x + 0.5f * P3.x; |
| |
| double y0 = P1.y; |
| double y1 = (-P0.y + P2.y) * 0.5f; |
| double y2 = P0.y - 2.5f * P1.y + 2.0f * P2.y - 0.5f * P3.y; |
| double y3 = -0.5f * P0.y + 1.5f * P1.y - 1.5f * P2.y + 0.5f * P3.y; |
| |
| double z0 = P1.thick; |
| double z1 = (-P0.thick + P2.thick) * 0.5f; |
| double z2 = P0.thick - 2.5f * P1.thick + 2.0f * P2.thick - 0.5f * P3.thick; |
| double z3 = |
| -0.5f * P0.thick + 1.5f * P1.thick - 1.5f * P2.thick + 0.5f * P3.thick; |
| |
| for (int i = 1; i <= samples; ++i) { |
| double t = i / (double)(samples + 1); |
| double t2 = t * t; |
| double t3 = t2 * t; |
| TThickPoint p; |
| p.x = x0 + x1 * t + x2 * t2 + x3 * t3; |
| p.y = y0 + y1 * t + y2 * t2 + y3 * t3; |
| p.thick = z0 + z1 * t + z2 * t2 + z3 * t3; |
| points.push_back(p); |
| } |
| } |
| |
| |
| |
| static void Smooth(std::vector<TThickPoint> &points, const int radius, |
| const int readIndex, const int level) { |
| int n = (int)points.size(); |
| if (radius < 1 || n < 3) { |
| return; |
| } |
| |
| std::vector<TThickPoint> result; |
| |
| float d = 1.0f / (radius * 2 + 1); |
| |
| int endSamples = 10; |
| int startId = std::max(readIndex - endSamples * 3 - radius * level, 1); |
| |
| for (int i = startId; i < n - 1; ++i) { |
| int lower = i - radius; |
| int upper = i + radius; |
| |
| TThickPoint total; |
| total.x = 0; |
| total.y = 0; |
| total.thick = 0; |
| |
| for (int j = lower; j <= upper; ++j) { |
| int idx = j; |
| if (idx < 0) { |
| idx = 0; |
| } else if (idx >= n) { |
| idx = n - 1; |
| } |
| total.x += points[idx].x; |
| total.y += points[idx].y; |
| total.thick += points[idx].thick; |
| } |
| |
| total.x *= d; |
| total.y *= d; |
| total.thick *= d; |
| result.push_back(total); |
| } |
| |
| auto result_itr = result.begin(); |
| for (int i = startId; i < n - 1; ++i, ++result_itr) { |
| points[i].x = (*result_itr).x; |
| points[i].y = (*result_itr).y; |
| points[i].thick = (*result_itr).thick; |
| } |
| |
| if (points.size() >= 3) { |
| std::vector<TThickPoint> pts; |
| CatmullRomInterpolate(points[0], points[0], points[1], points[2], |
| endSamples, pts); |
| std::vector<TThickPoint>::iterator it = points.begin() + 1; |
| points.insert(it, pts.begin(), pts.end()); |
| |
| pts.clear(); |
| CatmullRomInterpolate(points[n - 3], points[n - 2], points[n - 1], |
| points[n - 1], endSamples, pts); |
| it = points.begin(); |
| it += n - 1; |
| points.insert(it, pts.begin(), pts.end()); |
| } |
| } |
| |
| |
| |
| void SmoothStroke::beginStroke(int smooth) { |
| m_smooth = smooth; |
| m_outputIndex = 0; |
| m_readIndex = -1; |
| m_rawPoints.clear(); |
| m_outputPoints.clear(); |
| m_resampledIndex = 0; |
| m_resampledPoints.clear(); |
| } |
| |
| |
| |
| void SmoothStroke::addPoint(const TThickPoint &point) { |
| if (m_rawPoints.size() > 0 && m_rawPoints.back().x == point.x && |
| m_rawPoints.back().y == point.y) { |
| return; |
| } |
| m_rawPoints.push_back(point); |
| generatePoints(); |
| } |
| |
| |
| |
| void SmoothStroke::endStroke() { |
| generatePoints(); |
| |
| m_outputIndex = m_outputPoints.size() - 1; |
| } |
| |
| |
| |
| void SmoothStroke::clearPoints() { |
| m_outputIndex = 0; |
| m_readIndex = -1; |
| m_outputPoints.clear(); |
| m_rawPoints.clear(); |
| m_resampledIndex = 0; |
| m_resampledPoints.clear(); |
| } |
| |
| |
| |
| void SmoothStroke::getSmoothPoints(std::vector<TThickPoint> &smoothPoints) { |
| int n = m_outputPoints.size(); |
| for (int i = m_readIndex + 1; i <= m_outputIndex && i < n; ++i) { |
| smoothPoints.push_back(m_outputPoints[i]); |
| } |
| m_readIndex = m_outputIndex; |
| } |
| |
| |
| |
| void SmoothStroke::generatePoints() { |
| int n = (int)m_rawPoints.size(); |
| if (n == 0) { |
| return; |
| } |
| |
| |
| if (m_smooth == 0) { |
| for (int i = m_outputIndex; i < (int)m_outputPoints.size(); ++i) { |
| if (m_outputPoints[i] != m_rawPoints[i]) { |
| break; |
| } |
| ++m_outputIndex; |
| } |
| m_outputPoints = m_rawPoints; |
| return; |
| } |
| |
| std::vector<TThickPoint> smoothedPoints = m_resampledPoints; |
| |
| |
| |
| |
| int resampleStartId = m_resampledIndex; |
| for (int i = resampleStartId; i < n - 1; ++i) { |
| const TThickPoint &p1 = m_rawPoints[i]; |
| const TThickPoint &p2 = m_rawPoints[i + 1]; |
| const TThickPoint &p0 = i - 1 >= 0 ? m_rawPoints[i - 1] : p1; |
| const TThickPoint &p3 = i + 2 < n ? m_rawPoints[i + 2] : p2; |
| |
| std::vector<TThickPoint> tmpResampled; |
| tmpResampled.push_back(p1); |
| |
| int samples = std::min((int)tdistance(p1, p2), 8); |
| if (samples >= 1) |
| CatmullRomInterpolate(p0, p1, p2, p3, samples, tmpResampled); |
| |
| if (i + 2 < n) { |
| m_resampledIndex = i + 1; |
| std::copy(tmpResampled.begin(), tmpResampled.end(), |
| std::back_inserter(m_resampledPoints)); |
| } |
| std::copy(tmpResampled.begin(), tmpResampled.end(), |
| std::back_inserter(smoothedPoints)); |
| } |
| smoothedPoints.push_back(m_rawPoints.back()); |
| |
| |
| |
| |
| for (int level = 2; level >= 0; --level) { |
| Smooth(smoothedPoints, m_smooth, m_readIndex, level); |
| } |
| |
| |
| int outputNum = (int)m_outputPoints.size(); |
| for (int i = m_outputIndex; i < outputNum; ++i) { |
| if (m_outputPoints[i] != smoothedPoints[i]) { |
| break; |
| } |
| ++m_outputIndex; |
| } |
| m_outputPoints = smoothedPoints; |
| } |
| |
| |
| |
| |
| |
| |
| |
| ToonzRasterBrushTool::ToonzRasterBrushTool(std::string name, int targetType) |
| : TTool(name) |
| , m_rasThickness("Size", 1, 1000, 1, 5) |
| , m_smooth("Smooth:", 0, 50, 0) |
| , m_hardness("Hardness:", 0, 100, 100) |
| , m_preset("Preset:") |
| , m_drawOrder("Draw Order:") |
| , m_pencil("Pencil", false) |
| , m_pressure("Pressure", true) |
| , m_modifierSize("ModifierSize", -3, 3, 0, true) |
| , m_modifierLockAlpha("Lock Alpha", false) |
| , m_targetType(targetType) |
| , m_enabled(false) |
| , m_isPrompting(false) |
| , m_firstTime(true) |
| , m_presetsLoaded(false) |
| , m_notifier(0) |
| { |
| bind(targetType); |
| |
| m_rasThickness.setNonLinearSlider(); |
| |
| m_prop[0].bind(m_rasThickness); |
| m_prop[0].bind(m_hardness); |
| m_prop[0].bind(m_smooth); |
| m_prop[0].bind(m_drawOrder); |
| m_prop[0].bind(m_modifierSize); |
| m_prop[0].bind(m_modifierLockAlpha); |
| m_prop[0].bind(m_pencil); |
| m_pencil.setId("PencilMode"); |
| |
| m_drawOrder.addValue(L"Over All"); |
| m_drawOrder.addValue(L"Under All"); |
| m_drawOrder.addValue(L"Palette Order"); |
| m_drawOrder.setId("DrawOrder"); |
| |
| m_prop[0].bind(m_pressure); |
| |
| m_prop[0].bind(m_preset); |
| m_preset.setId("BrushPreset"); |
| m_preset.addValue(CUSTOM_WSTR); |
| m_pressure.setId("PressureSensitivity"); |
| m_modifierLockAlpha.setId("LockAlpha"); |
| |
| m_inputmanager.setHandler(this); |
| m_modifierLine = new TModifierLine(); |
| m_modifierTangents = new TModifierTangents(); |
| m_modifierAssistants = new TModifierAssistants(); |
| m_modifierSegmentation = new TModifierSegmentation(); |
| m_modifierSmoothSegmentation = new TModifierSegmentation(TPointD(1, 1), 3); |
| for(int i = 0; i < 3; ++i) |
| m_modifierSmooth[i] = new TModifierSmooth(); |
| #ifndef NDEBUG |
| m_modifierTest = new TModifierTest(5, 40); |
| #endif |
| |
| m_inputmanager.addModifier( |
| TInputModifierP(m_modifierAssistants.getPointer())); |
| } |
| |
| |
| |
| ToolOptionsBox *ToonzRasterBrushTool::createOptionsBox() { |
| TPaletteHandle *currPalette = |
| TTool::getApplication()->getPaletteController()->getCurrentLevelPalette(); |
| ToolHandle *currTool = TTool::getApplication()->getCurrentTool(); |
| return new BrushToolOptionsBox(0, this, currPalette, currTool); |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::drawLine(const TPointD &point, const TPointD ¢re, |
| bool horizontal, bool isDecimal) { |
| if (!isDecimal) { |
| if (horizontal) { |
| tglDrawSegment(TPointD(point.x - 1.5, point.y + 0.5) + centre, |
| TPointD(point.x - 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y - 0.5, -point.x + 1.5) + centre, |
| TPointD(point.y - 0.5, -point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, -point.y + 0.5) + centre, |
| TPointD(-point.x - 0.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x + 0.5) + centre); |
| |
| tglDrawSegment(TPointD(point.y - 0.5, point.x + 0.5) + centre, |
| TPointD(point.y - 0.5, point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 0.5, -point.y + 0.5) + centre, |
| TPointD(point.x - 1.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, -point.x + 0.5) + centre, |
| TPointD(-point.y - 0.5, -point.x + 1.5) + centre); |
| tglDrawSegment(TPointD(-point.x - 0.5, point.y + 0.5) + centre, |
| TPointD(-point.x + 0.5, point.y + 0.5) + centre); |
| } else { |
| tglDrawSegment(TPointD(point.x - 1.5, point.y + 1.5) + centre, |
| TPointD(point.x - 1.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 1.5, point.y + 0.5) + centre, |
| TPointD(point.x - 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 0.5, -point.x + 1.5) + centre, |
| TPointD(point.y - 0.5, -point.x + 1.5) + centre); |
| tglDrawSegment(TPointD(point.y - 0.5, -point.x + 1.5) + centre, |
| TPointD(point.y - 0.5, -point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, -point.y - 0.5) + centre, |
| TPointD(-point.x + 0.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, -point.y + 0.5) + centre, |
| TPointD(-point.x - 0.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 1.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x + 0.5) + centre); |
| |
| tglDrawSegment(TPointD(point.y + 0.5, point.x - 0.5) + centre, |
| TPointD(point.y - 0.5, point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(point.y - 0.5, point.x - 0.5) + centre, |
| TPointD(point.y - 0.5, point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 1.5, -point.y - 0.5) + centre, |
| TPointD(point.x - 1.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 1.5, -point.y + 0.5) + centre, |
| TPointD(point.x - 0.5, -point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 1.5, -point.x + 1.5) + centre, |
| TPointD(-point.y - 0.5, -point.x + 1.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, -point.x + 1.5) + centre, |
| TPointD(-point.y - 0.5, -point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, point.y + 1.5) + centre, |
| TPointD(-point.x + 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, point.y + 0.5) + centre, |
| TPointD(-point.x - 0.5, point.y + 0.5) + centre); |
| } |
| } else { |
| if (horizontal) { |
| tglDrawSegment(TPointD(point.x - 0.5, point.y + 0.5) + centre, |
| TPointD(point.x + 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 0.5, point.x - 0.5) + centre, |
| TPointD(point.y + 0.5, point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 0.5, -point.x + 0.5) + centre, |
| TPointD(point.y + 0.5, -point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(point.x + 0.5, -point.y - 0.5) + centre, |
| TPointD(point.x - 0.5, -point.y - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x - 0.5, -point.y - 0.5) + centre, |
| TPointD(-point.x + 0.5, -point.y - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, -point.x + 0.5) + centre, |
| TPointD(-point.y - 0.5, -point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, point.y + 0.5) + centre, |
| TPointD(-point.x - 0.5, point.y + 0.5) + centre); |
| } else { |
| tglDrawSegment(TPointD(point.x - 0.5, point.y + 1.5) + centre, |
| TPointD(point.x - 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 0.5, point.y + 0.5) + centre, |
| TPointD(point.x + 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 1.5, point.x - 0.5) + centre, |
| TPointD(point.y + 0.5, point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 0.5, point.x - 0.5) + centre, |
| TPointD(point.y + 0.5, point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 1.5, -point.x + 0.5) + centre, |
| TPointD(point.y + 0.5, -point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(point.y + 0.5, -point.x + 0.5) + centre, |
| TPointD(point.y + 0.5, -point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 0.5, -point.y - 1.5) + centre, |
| TPointD(point.x - 0.5, -point.y - 0.5) + centre); |
| tglDrawSegment(TPointD(point.x - 0.5, -point.y - 0.5) + centre, |
| TPointD(point.x + 0.5, -point.y - 0.5) + centre); |
| |
| tglDrawSegment(TPointD(-point.x + 0.5, -point.y - 1.5) + centre, |
| TPointD(-point.x + 0.5, -point.y - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, -point.y - 0.5) + centre, |
| TPointD(-point.x - 0.5, -point.y - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 1.5, -point.x + 0.5) + centre, |
| TPointD(-point.y - 0.5, -point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, -point.x + 0.5) + centre, |
| TPointD(-point.y - 0.5, -point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 1.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x - 0.5) + centre); |
| tglDrawSegment(TPointD(-point.y - 0.5, point.x - 0.5) + centre, |
| TPointD(-point.y - 0.5, point.x + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, point.y + 1.5) + centre, |
| TPointD(-point.x + 0.5, point.y + 0.5) + centre); |
| tglDrawSegment(TPointD(-point.x + 0.5, point.y + 0.5) + centre, |
| TPointD(-point.x - 0.5, point.y + 0.5) + centre); |
| } |
| } |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::drawEmptyCircle(TPointD pos, int thick, |
| bool isLxEven, bool isLyEven, |
| bool isPencil) { |
| if (isLxEven) pos.x += 0.5; |
| if (isLyEven) pos.y += 0.5; |
| |
| if (!isPencil) |
| tglDrawCircle(pos, (thick + 1) * 0.5); |
| else { |
| int x = 0, y = tround((thick * 0.5) - 0.5); |
| int d = 3 - 2 * (int)(thick * 0.5); |
| bool horizontal = true, isDecimal = thick % 2 != 0; |
| drawLine(TPointD(x, y), pos, horizontal, isDecimal); |
| while (y > x) { |
| if (d < 0) { |
| d = d + 4 * x + 6; |
| horizontal = true; |
| } else { |
| d = d + 4 * (x - y) + 10; |
| horizontal = false; |
| y--; |
| } |
| x++; |
| drawLine(TPointD(x, y), pos, horizontal, isDecimal); |
| } |
| } |
| } |
| |
| |
| |
| TPointD ToonzRasterBrushTool::getCenteredCursorPos( |
| const TPointD &originalCursorPos) { |
| if (m_isMyPaintStyleSelected) return originalCursorPos; |
| TXshLevelHandle *levelHandle = m_application->getCurrentLevel(); |
| TXshSimpleLevel *level = levelHandle ? levelHandle->getSimpleLevel() : 0; |
| TDimension resolution = |
| level ? level->getProperties()->getImageRes() : TDimension(0, 0); |
| |
| bool xEven = (resolution.lx % 2 == 0); |
| bool yEven = (resolution.ly % 2 == 0); |
| |
| TPointD centeredCursorPos = originalCursorPos; |
| |
| if (xEven) centeredCursorPos.x -= 0.5; |
| if (yEven) centeredCursorPos.y -= 0.5; |
| |
| return centeredCursorPos; |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::updateTranslation() { |
| m_rasThickness.setQStringName(tr("Size")); |
| m_hardness.setQStringName(tr("Hardness:")); |
| m_smooth.setQStringName(tr("Smooth:")); |
| m_drawOrder.setQStringName(tr("Draw Order:")); |
| m_drawOrder.setItemUIName(L"Over All", tr("Over All")); |
| m_drawOrder.setItemUIName(L"Under All", tr("Under All")); |
| m_drawOrder.setItemUIName(L"Palette Order", tr("Palette Order")); |
| m_modifierSize.setQStringName(tr("Size")); |
| |
| |
| m_preset.setQStringName(tr("Preset:")); |
| m_preset.setItemUIName(CUSTOM_WSTR, tr("<custom>")); |
| m_pencil.setQStringName(tr("Pencil")); |
| m_pressure.setQStringName(tr("Pressure")); |
| m_modifierLockAlpha.setQStringName(tr("Lock Alpha")); |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::onActivate() { |
| if (!m_notifier) m_notifier = new ToonzRasterBrushToolNotifier(this); |
| |
| if (m_firstTime) { |
| m_firstTime = false; |
| |
| std::wstring wpreset = |
| QString::fromStdString(RasterBrushPreset.getValue()).toStdWString(); |
| if (wpreset != CUSTOM_WSTR) { |
| initPresets(); |
| if (!m_preset.isValue(wpreset)) wpreset = CUSTOM_WSTR; |
| m_preset.setValue(wpreset); |
| RasterBrushPreset = m_preset.getValueAsString(); |
| loadPreset(); |
| } else |
| loadLastBrush(); |
| } |
| m_brushPad = ToolUtils::getBrushPad(m_rasThickness.getValue().second, |
| m_hardness.getValue() * 0.01); |
| setWorkAndBackupImages(); |
| |
| m_brushTimer.start(); |
| |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::onDeactivate() { |
| m_inputmanager.finishTracks(); |
| m_enabled = false; |
| m_workRas = TRaster32P(); |
| m_backupRas = TRasterCM32P(); |
| } |
| |
| |
| |
| bool ToonzRasterBrushTool::askRead(const TRect &rect) { return askWrite(rect); } |
| |
| |
| |
| bool ToonzRasterBrushTool::askWrite(const TRect &rect) { |
| if (rect.isEmpty()) return true; |
| m_painting.myPaint.strokeSegmentRect += rect; |
| updateWorkAndBackupRasters(rect); |
| m_painting.tileSaver->save(rect); |
| return true; |
| } |
| |
| |
| |
| bool ToonzRasterBrushTool::preLeftButtonDown() { |
| int smoothRadius = (int)round(m_smooth.getValue()); |
| m_modifierAssistants->drawOnly = true; |
| m_inputmanager.drawPreview = !m_modifierAssistants->drawOnly; |
| |
| m_inputmanager.clearModifiers(); |
| m_inputmanager.addModifier(TInputModifierP(m_modifierTangents.getPointer())); |
| if (smoothRadius > 0) { |
| m_inputmanager.addModifier(TInputModifierP(m_modifierSmoothSegmentation.getPointer())); |
| for(int i = 0; i < 3; ++i) { |
| m_modifierSmooth[i]->setRadius(smoothRadius); |
| m_inputmanager.addModifier(TInputModifierP(m_modifierSmooth[i].getPointer())); |
| } |
| } |
| m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer())); |
| #ifndef NDEBUG |
| m_inputmanager.addModifier(TInputModifierP(m_modifierTest.getPointer())); |
| #endif |
| m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); |
| |
| touchImage(); |
| if (m_isFrameCreated) { |
| setWorkAndBackupImages(); |
| |
| |
| |
| if (m_application->getCurrentFrame()->isEditingLevel()) invalidate(); |
| } |
| return true; |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::handleMouseEvent(MouseEventType type, |
| const TPointD &pos, |
| const TMouseEvent &e) { |
| TTimerTicks t = TToolTimer::ticks(); |
| bool alt = e.getModifiersMask() & TMouseEvent::ALT_KEY; |
| bool shift = e.getModifiersMask() & TMouseEvent::SHIFT_KEY; |
| bool control = e.getModifiersMask() & TMouseEvent::CTRL_KEY; |
| |
| if (shift && type == ME_DOWN && e.button() == Qt::LeftButton && !m_painting.active) { |
| m_modifierAssistants->drawOnly = true; |
| m_inputmanager.clearModifiers(); |
| m_inputmanager.addModifier(TInputModifierP(m_modifierLine.getPointer())); |
| m_inputmanager.addModifier(TInputModifierP(m_modifierAssistants.getPointer())); |
| m_inputmanager.addModifier(TInputModifierP(m_modifierSegmentation.getPointer())); |
| m_inputmanager.drawPreview = true; |
| } |
| |
| if (alt != m_inputmanager.state.isKeyPressed(TKey::alt)) |
| m_inputmanager.keyEvent(alt, TKey::alt, t, nullptr); |
| if (shift != m_inputmanager.state.isKeyPressed(TKey::shift)) |
| m_inputmanager.keyEvent(shift, TKey::shift, t, nullptr); |
| if (control != m_inputmanager.state.isKeyPressed(TKey::control)) |
| m_inputmanager.keyEvent(control, TKey::control, t, nullptr); |
| |
| if (type == ME_MOVE) { |
| THoverList hovers(1, pos); |
| m_inputmanager.hoverEvent(hovers); |
| } else { |
| m_inputmanager.trackEvent(e.isTablet(), 0, pos, |
| e.isTablet() ? &e.m_pressure : nullptr, nullptr, |
| type == ME_UP, t); |
| m_inputmanager.processTracks(); |
| } |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::leftButtonDown(const TPointD &pos, |
| const TMouseEvent &e) { |
| handleMouseEvent(ME_DOWN, pos, e); |
| } |
| void ToonzRasterBrushTool::leftButtonDrag(const TPointD &pos, |
| const TMouseEvent &e) { |
| handleMouseEvent(ME_DRAG, pos, e); |
| } |
| void ToonzRasterBrushTool::leftButtonUp(const TPointD &pos, |
| const TMouseEvent &e) { |
| handleMouseEvent(ME_UP, pos, e); |
| } |
| void ToonzRasterBrushTool::mouseMove(const TPointD &pos, const TMouseEvent &e) { |
| handleMouseEvent(ME_MOVE, pos, e); |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::inputSetBusy(bool busy) { |
| if (m_painting.active == busy) |
| return; |
| |
| if (busy) { |
| |
| |
| TTool::Application *app = TTool::getApplication(); |
| if (!app) return; |
| |
| int col = app->getCurrentColumn()->getColumnIndex(); |
| m_enabled = col >= 0 || app->getCurrentFrame()->isEditingLevel(); |
| |
| if (!m_enabled) return; |
| |
| m_painting.active = !!getImage(true); |
| if (!m_painting.active) |
| m_painting.active = !!touchImage(); |
| if (!m_painting.active) |
| return; |
| |
| |
| |
| updateCurrentStyle(); |
| if (TColorStyle *cs = app->getCurrentLevelStyle()) { |
| m_painting.styleId = app->getCurrentLevelStyleIndex(); |
| TRasterStyleFx *rfx = cs->getRasterStyleFx(); |
| if (!cs->isStrokeStyle() && (!rfx || !rfx->isInkStyle())) |
| { m_painting.active = false; return; } |
| } else { |
| m_painting.styleId = 1; |
| } |
| |
| m_painting.frameId = getFrameId(); |
| |
| TImageP img = getImage(true); |
| TToonzImageP ri(img); |
| TRasterCM32P ras = ri->getRaster(); |
| if (!ras) |
| { m_painting.active = false; return; } |
| |
| m_painting.tileSet = new TTileSetCM32(ras->getSize()); |
| m_painting.tileSaver = new TTileSaverCM32(ras, m_painting.tileSet); |
| m_painting.affectedRect.empty(); |
| setWorkAndBackupImages(); |
| |
| if (m_isMyPaintStyleSelected) { |
| |
| |
| m_painting.myPaint.isActive = true; |
| m_painting.myPaint.strokeSegmentRect.empty(); |
| |
| m_workRas->lock(); |
| |
| |
| if ( TMyPaintBrushStyle *mypaintStyle = dynamic_cast<TMyPaintBrushStyle *>(app->getCurrentLevelStyle()) ) |
| m_painting.myPaint.baseBrush.fromBrush( mypaintStyle->getBrush() ); else |
| m_painting.myPaint.baseBrush.fromDefaults(); |
| double modifierSize = m_modifierSize.getValue() * log(2.0); |
| float baseSize = m_painting.myPaint.baseBrush.getBaseValue( MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC ); |
| m_painting.myPaint.baseBrush.setBaseValue( MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC, baseSize + modifierSize ); |
| } else |
| if (m_hardness.getValue() == 100 || m_pencil.getValue()) { |
| |
| |
| m_painting.pencil.isActive = true; |
| m_painting.pencil.realPencil = m_pencil.getValue(); |
| } else { |
| |
| |
| m_painting.blured.isActive = true; |
| } |
| |
| } else { |
| |
| |
| if (m_painting.myPaint.isActive) { |
| |
| m_workRas->unlock(); |
| } |
| |
| delete m_painting.tileSaver; |
| m_painting.tileSaver = nullptr; |
| |
| |
| TFrameId frameId = m_painting.frameId.isEmptyFrame() ? getCurrentFid() : m_painting.frameId; |
| if (m_painting.tileSet->getTileCount() > 0) { |
| TTool::Application *app = TTool::getApplication(); |
| TXshLevel *level = app->getCurrentLevel()->getLevel(); |
| TXshSimpleLevelP simLevel = level->getSimpleLevel(); |
| TRasterCM32P ras = TToonzImageP( getImage(true) )->getRaster(); |
| TRasterCM32P subras = ras->extract(m_painting.affectedRect)->clone(); |
| TUndoManager::manager()->add(new MyPaintBrushUndo( |
| m_painting.tileSet, simLevel.getPointer(), frameId, m_isFrameCreated, |
| m_isLevelCreated, subras, m_painting.affectedRect.getP00())); |
| |
| } else { |
| |
| delete m_painting.tileSet; |
| } |
| m_painting.tileSet = nullptr; |
| |
| |
| m_painting.frameId = TFrameId(); |
| |
| |
| m_painting.myPaint.isActive = false; |
| m_painting.pencil.isActive = false; |
| m_painting.blured.isActive = false; |
| m_painting.active = false; |
| |
| |
| |
| notifyImageChanged(frameId); |
| ToolUtils::updateSaveBox(); |
| } |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::inputPaintTrackPoint(const TTrackPoint &point, const TTrack &track, bool firstTrack, bool preview) { |
| if (!m_painting.active || preview) |
| return; |
| |
| TImageP img = getImage(true); |
| TToonzImageP ri(img); |
| TRasterCM32P ras = ri->getRaster(); |
| if (!ras) |
| return; |
| TPointD rasCenter = ras->getCenterD(); |
| |
| |
| TRectD invalidateRect; |
| bool firstPoint = track.size() == track.pointsAdded; |
| bool lastPoint = track.pointsAdded == 1 && track.finished(); |
| |
| |
| |
| assert(firstPoint == !track.toolHandler); |
| if (firstPoint != !track.toolHandler) |
| return; |
| |
| if (m_painting.myPaint.isActive) { |
| |
| |
| |
| MyPaintStroke *handler; |
| if (firstPoint) { |
| handler = new MyPaintStroke(m_workRas, *this, m_painting.myPaint.baseBrush, false); |
| handler->brush.beginStroke(); |
| track.toolHandler = handler; |
| } |
| handler = dynamic_cast<MyPaintStroke*>(track.toolHandler.getPointer()); |
| if (!handler) return; |
| |
| |
| m_painting.myPaint.strokeSegmentRect.empty(); |
| handler->brush.strokeTo( point.position + rasCenter, point.pressure, |
| point.tilt, point.time - track.previous().time ); |
| if (lastPoint) |
| handler->brush.endStroke(); |
| |
| |
| TRect updateRect = m_painting.myPaint.strokeSegmentRect * ras->getBounds(); |
| m_painting.affectedRect += updateRect; |
| if (!updateRect.isEmpty()) |
| handler->brush.updateDrawing( ras, m_backupRas, m_painting.myPaint.strokeSegmentRect, |
| m_painting.styleId, m_modifierLockAlpha.getValue() ); |
| |
| |
| invalidateRect += convert(m_painting.myPaint.strokeSegmentRect) - rasCenter; |
| } else |
| if (m_painting.pencil.isActive) { |
| |
| |
| |
| double thickness = computeThickness(point.pressure, m_rasThickness)*2; |
| |
| |
| TThickPoint thickPoint(point.position + rasCenter, thickness); |
| |
| |
| PencilStroke *handler; |
| if (firstPoint) { |
| DrawOrder drawOrder = (DrawOrder)m_drawOrder.getIndex(); |
| handler = new PencilStroke( ras, BRUSH, NONE, m_painting.styleId, thickPoint, drawOrder != OverAll, 0, |
| m_modifierLockAlpha.getValue(), !m_painting.pencil.realPencil, |
| drawOrder == PaletteOrder ); |
| |
| |
| |
| if (drawOrder == PaletteOrder) { |
| QSet<int> aboveStyleIds; |
| getAboveStyleIdSet(m_painting.styleId, ri->getPalette(), aboveStyleIds); |
| handler->brush.setAboveStyleIds(aboveStyleIds); |
| } |
| track.toolHandler = handler; |
| } |
| handler = dynamic_cast<PencilStroke*>(track.toolHandler.getPointer()); |
| if (!handler) return; |
| |
| |
| if (!firstPoint) |
| handler->brush.add(thickPoint); |
| |
| |
| TRect strokeRect = handler->brush.getLastRect() * ras->getBounds(); |
| m_painting.tileSaver->save( strokeRect ); |
| m_painting.affectedRect += strokeRect; |
| handler->brush.generateLastPieceOfStroke( m_painting.pencil.realPencil ); |
| |
| |
| invalidateRect += convert(strokeRect) - rasCenter; |
| } else |
| if (m_painting.blured.isActive) { |
| |
| |
| double maxThick = m_rasThickness.getValue().second; |
| DrawOrder drawOrder = (DrawOrder)m_drawOrder.getIndex(); |
| |
| |
| BluredStroke *handler; |
| if (firstPoint) { |
| handler = new BluredStroke(m_workRas, maxThick, m_brushPad, false); |
| |
| |
| if (drawOrder == PaletteOrder) { |
| QSet<int> aboveStyleIds; |
| getAboveStyleIdSet(m_painting.styleId, ri->getPalette(), aboveStyleIds); |
| handler->brush.setAboveStyleIds(aboveStyleIds); |
| } |
| track.toolHandler = handler; |
| } |
| handler = dynamic_cast<BluredStroke*>(track.toolHandler.getPointer()); |
| if (!handler) return; |
| |
| |
| double radius = computeThickness(point.pressure, m_rasThickness); |
| TThickPoint thickPoint(point.position + rasCenter, radius*2); |
| TRect strokeRect( tfloor(thickPoint.x - maxThick - 0.999), |
| tfloor(thickPoint.y - maxThick - 0.999), |
| tceil(thickPoint.x + maxThick + 0.999), |
| tceil(thickPoint.y + maxThick + 0.999) ); |
| strokeRect *= ras->getBounds(); |
| m_painting.affectedRect += strokeRect; |
| updateWorkAndBackupRasters(m_painting.affectedRect); |
| m_painting.tileSaver->save(strokeRect); |
| handler->brush.addPoint(thickPoint, 1, !lastPoint); |
| handler->brush.updateDrawing( ras, m_backupRas, strokeRect, |
| m_painting.styleId, drawOrder, |
| m_modifierLockAlpha.getValue() ); |
| |
| invalidateRect += convert(strokeRect) - rasCenter; |
| } |
| |
| |
| if (firstTrack) { |
| |
| |
| TPointD thickOffset(m_maxCursorThick*0.5, m_maxCursorThick*0.5); |
| invalidateRect += TRectD(m_brushPos - thickOffset, m_brushPos + thickOffset); |
| invalidateRect += TRectD(point.position - thickOffset, point.position + thickOffset); |
| m_brushPos = m_mousePos = point.position; |
| } |
| invalidate(invalidateRect.enlarge(2)); |
| } |
| |
| |
| |
| |
| void ToonzRasterBrushTool::inputMouseMove(const TPointD &position, const TInputState &state) { |
| struct Locals { |
| ToonzRasterBrushTool *m_this; |
| |
| void setValue(TDoublePairProperty &prop, |
| const TDoublePairProperty::Value &value) { |
| prop.setValue(value); |
| |
| m_this->onPropertyChanged(prop.getName()); |
| TTool::getApplication()->getCurrentTool()->notifyToolChanged(); |
| } |
| |
| void addMinMax(TDoublePairProperty &prop, double add) { |
| if (add == 0.0) return; |
| const TDoublePairProperty::Range &range = prop.getRange(); |
| |
| TDoublePairProperty::Value value = prop.getValue(); |
| value.first = tcrop(value.first + add, range.first, range.second); |
| value.second = tcrop(value.second + add, range.first, range.second); |
| |
| setValue(prop, value); |
| } |
| |
| void addMinMaxSeparate(TDoublePairProperty &prop, double min, double max) { |
| if (min == 0.0 && max == 0.0) return; |
| const TDoublePairProperty::Range &range = prop.getRange(); |
| |
| TDoublePairProperty::Value value = prop.getValue(); |
| value.first += min; |
| value.second += max; |
| if (value.first > value.second) value.first = value.second; |
| value.first = tcrop(value.first, range.first, range.second); |
| value.second = tcrop(value.second, range.first, range.second); |
| |
| setValue(prop, value); |
| } |
| |
| } locals = {this}; |
| |
| double thickness = |
| (m_isMyPaintStyleSelected) ? (double)(m_maxCursorThick + 1) : m_maxThick; |
| TPointD halfThick(thickness * 0.5, thickness * 0.5); |
| TRectD invalidateRect(m_brushPos - halfThick, m_brushPos + halfThick); |
| |
| if ( Preferences::instance()->useCtrlAltToResizeBrushEnabled() |
| && state.isKeyPressed(TKey::control) |
| && state.isKeyPressed(TKey::alt) |
| && !state.isKeyPressed(TKey::shift) ) |
| { |
| |
| const TPointD &diff = position - m_mousePos; |
| double max = diff.x / 2; |
| double min = diff.y / 2; |
| |
| locals.addMinMaxSeparate(m_rasThickness, min, max); |
| |
| double radius = m_rasThickness.getValue().second * 0.5; |
| invalidateRect += TRectD(m_brushPos - TPointD(radius, radius), |
| m_brushPos + TPointD(radius, radius)); |
| |
| } else { |
| m_mousePos = position; |
| m_brushPos = getCenteredCursorPos(position); |
| |
| invalidateRect += TRectD(position - halfThick, position + halfThick); |
| } |
| |
| invalidate(invalidateRect.enlarge(2)); |
| |
| if (m_minThick == 0 && m_maxThick == 0) { |
| m_minThick = m_rasThickness.getValue().first; |
| m_maxThick = m_rasThickness.getValue().second; |
| } |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::draw() { |
| m_inputmanager.draw(); |
| |
| |
| if (m_minThick == 0 && m_maxThick == 0 && |
| !Preferences::instance()->getShow0ThickLines()) |
| return; |
| |
| TImageP img = getImage(false, 1); |
| |
| if (getApplication()->getCurrentObject()->isSpline()) return; |
| |
| |
| if (!Preferences::instance()->isCursorOutlineEnabled()) return; |
| |
| |
| |
| if ((ToonzCheck::instance()->getChecks() & ToonzCheck::eInk) || |
| (ToonzCheck::instance()->getChecks() & ToonzCheck::ePaint) || |
| (ToonzCheck::instance()->getChecks() & ToonzCheck::eInk1)) |
| glColor3d(0.5, 0.8, 0.8); |
| |
| else |
| glColor3d(1.0, 0.0, 0.0); |
| |
| if (m_isMyPaintStyleSelected) { |
| tglDrawCircle(m_brushPos, (m_minCursorThick + 1) * 0.5); |
| tglDrawCircle(m_brushPos, (m_maxCursorThick + 1) * 0.5); |
| return; |
| } |
| |
| if (TToonzImageP ti = img) { |
| TRasterP ras = ti->getRaster(); |
| int lx = ras->getLx(); |
| int ly = ras->getLy(); |
| drawEmptyCircle(m_brushPos, tround(m_minThick), lx % 2 == 0, ly % 2 == 0, |
| m_pencil.getValue()); |
| drawEmptyCircle(m_brushPos, tround(m_maxThick), lx % 2 == 0, ly % 2 == 0, |
| m_pencil.getValue()); |
| } else { |
| drawEmptyCircle(m_brushPos, tround(m_minThick), true, true, |
| m_pencil.getValue()); |
| drawEmptyCircle(m_brushPos, tround(m_maxThick), true, true, |
| m_pencil.getValue()); |
| } |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::onEnter() { |
| m_minThick = m_rasThickness.getValue().first; |
| m_maxThick = m_rasThickness.getValue().second; |
| updateCurrentStyle(); |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::onLeave() { |
| m_minThick = 0; |
| m_maxThick = 0; |
| m_minCursorThick = 0; |
| m_maxCursorThick = 0; |
| } |
| |
| |
| |
| TPropertyGroup *ToonzRasterBrushTool::getProperties(int idx) { |
| if (!m_presetsLoaded) initPresets(); |
| |
| return &m_prop[idx]; |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::onImageChanged() { |
| if (!isEnabled()) return; |
| setWorkAndBackupImages(); |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::setWorkAndBackupImages() { |
| TToonzImageP ti = (TToonzImageP)getImage(false, 1); |
| if (!ti) return; |
| TRasterP ras = ti->getRaster(); |
| TDimension dim = ras->getSize(); |
| |
| if (!m_workRas || m_workRas->getLx() < dim.lx || m_workRas->getLy() < dim.ly) |
| m_workRas = TRaster32P(dim); |
| if (!m_backupRas || m_backupRas->getLx() < dim.lx || m_backupRas->getLy() < dim.ly) |
| m_backupRas = TRasterCM32P(dim); |
| |
| m_workBackupRect.empty(); |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::updateWorkAndBackupRasters(const TRect &rect) { |
| TToonzImageP ti = TImageP(getImage(false, 1)); |
| if (!ti) return; |
| TRasterCM32P ras = ti->getRaster(); |
| |
| |
| |
| const int denominator = 8; |
| TRect enlargedRect = rect + m_workBackupRect; |
| int dx = (enlargedRect.getLx() - 1) / denominator + 1; |
| int dy = (enlargedRect.getLy() - 1) / denominator + 1; |
| |
| if (m_workBackupRect.isEmpty()) { |
| enlargedRect.x0 -= dx; |
| enlargedRect.y0 -= dy; |
| enlargedRect.x1 += dx; |
| enlargedRect.y1 += dy; |
| |
| enlargedRect *= ras->getBounds(); |
| if (enlargedRect.isEmpty()) return; |
| |
| m_workRas->extract(enlargedRect)->copy(ras->extract(enlargedRect)); |
| m_backupRas->extract(enlargedRect)->copy(ras->extract(enlargedRect)); |
| } else { |
| if (enlargedRect.x0 < m_workBackupRect.x0) enlargedRect.x0 -= dx; |
| if (enlargedRect.y0 < m_workBackupRect.y0) enlargedRect.y0 -= dy; |
| if (enlargedRect.x1 > m_workBackupRect.x1) enlargedRect.x1 += dx; |
| if (enlargedRect.y1 > m_workBackupRect.y1) enlargedRect.y1 += dy; |
| |
| enlargedRect *= ras->getBounds(); |
| if (enlargedRect.isEmpty()) return; |
| |
| TRect lastRect = m_workBackupRect * ras->getBounds(); |
| QList<TRect> rects = ToolUtils::splitRect(enlargedRect, lastRect); |
| for (int i = 0; i < rects.size(); i++) { |
| m_workRas->extract(rects[i])->copy(ras->extract(rects[i])); |
| m_backupRas->extract(rects[i])->copy(ras->extract(rects[i])); |
| } |
| } |
| |
| m_workBackupRect = enlargedRect; |
| } |
| |
| |
| |
| bool ToonzRasterBrushTool::onPropertyChanged(std::string propertyName) { |
| if (m_propertyUpdating) return true; |
| |
| if (propertyName == m_preset.getName()) { |
| if (m_preset.getValue() != CUSTOM_WSTR) |
| loadPreset(); |
| else |
| loadLastBrush(); |
| |
| RasterBrushPreset = m_preset.getValueAsString(); |
| m_propertyUpdating = true; |
| getApplication()->getCurrentTool()->notifyToolChanged(); |
| m_propertyUpdating = false; |
| return true; |
| } |
| |
| RasterBrushMinSize = m_rasThickness.getValue().first; |
| RasterBrushMaxSize = m_rasThickness.getValue().second; |
| BrushSmooth = m_smooth.getValue(); |
| BrushDrawOrder = m_drawOrder.getIndex(); |
| RasterBrushPencilMode = m_pencil.getValue(); |
| BrushPressureSensitivity = m_pressure.getValue(); |
| RasterBrushHardness = m_hardness.getValue(); |
| RasterBrushModifierSize = m_modifierSize.getValue(); |
| BrushLockAlpha = m_modifierLockAlpha.getValue(); |
| |
| |
| if (propertyName == m_rasThickness.getName()) { |
| m_minThick = m_rasThickness.getValue().first; |
| m_maxThick = m_rasThickness.getValue().second; |
| } |
| |
| if (propertyName == m_hardness.getName()) setWorkAndBackupImages(); |
| |
| if (propertyName == m_hardness.getName() || |
| propertyName == m_rasThickness.getName()) { |
| m_brushPad = getBrushPad(m_rasThickness.getValue().second, |
| m_hardness.getValue() * 0.01); |
| TRectD rect(m_mousePos - TPointD(m_maxThick + 2, m_maxThick + 2), |
| m_mousePos + TPointD(m_maxThick + 2, m_maxThick + 2)); |
| invalidate(rect); |
| } |
| |
| if (m_preset.getValue() != CUSTOM_WSTR) { |
| m_preset.setValue(CUSTOM_WSTR); |
| RasterBrushPreset = m_preset.getValueAsString(); |
| m_propertyUpdating = true; |
| getApplication()->getCurrentTool()->notifyToolChanged(); |
| m_propertyUpdating = false; |
| } |
| |
| return true; |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::initPresets() { |
| if (!m_presetsLoaded) { |
| |
| m_presetsLoaded = true; |
| m_presetsManager.load(TEnv::getConfigDir() + "brush_toonzraster.txt"); |
| } |
| |
| |
| const std::set<BrushData> &presets = m_presetsManager.presets(); |
| |
| m_preset.deleteAllValues(); |
| m_preset.addValue(CUSTOM_WSTR); |
| m_preset.setItemUIName(CUSTOM_WSTR, tr("<custom>")); |
| |
| std::set<BrushData>::const_iterator it, end = presets.end(); |
| for (it = presets.begin(); it != end; ++it) m_preset.addValue(it->m_name); |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::loadPreset() { |
| const std::set<BrushData> &presets = m_presetsManager.presets(); |
| std::set<BrushData>::const_iterator it; |
| |
| it = presets.find(BrushData(m_preset.getValue())); |
| if (it == presets.end()) return; |
| |
| const BrushData &preset = *it; |
| |
| try |
| { |
| m_rasThickness.setValue( |
| TDoublePairProperty::Value(std::max(preset.m_min, 1.0), preset.m_max)); |
| m_hardness.setValue(preset.m_hardness, true); |
| m_smooth.setValue(preset.m_smooth, true); |
| m_drawOrder.setIndex(preset.m_drawOrder); |
| m_pencil.setValue(preset.m_pencil); |
| m_pressure.setValue(preset.m_pressure); |
| m_modifierSize.setValue(preset.m_modifierSize); |
| m_modifierLockAlpha.setValue(preset.m_modifierLockAlpha); |
| |
| |
| m_minThick = m_rasThickness.getValue().first; |
| m_maxThick = m_rasThickness.getValue().second; |
| |
| setWorkAndBackupImages(); |
| |
| m_brushPad = ToolUtils::getBrushPad(preset.m_max, preset.m_hardness * 0.01); |
| } catch (...) { |
| } |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::addPreset(QString name) { |
| |
| BrushData preset(name.toStdWString()); |
| |
| preset.m_min = m_rasThickness.getValue().first; |
| preset.m_max = m_rasThickness.getValue().second; |
| |
| preset.m_smooth = m_smooth.getValue(); |
| preset.m_hardness = m_hardness.getValue(); |
| preset.m_drawOrder = m_drawOrder.getIndex(); |
| preset.m_pencil = m_pencil.getValue(); |
| preset.m_pressure = m_pressure.getValue(); |
| preset.m_modifierSize = m_modifierSize.getValue(); |
| preset.m_modifierLockAlpha = m_modifierLockAlpha.getValue(); |
| |
| |
| m_presetsManager.addPreset(preset); |
| |
| |
| initPresets(); |
| |
| |
| m_preset.setValue(preset.m_name); |
| RasterBrushPreset = m_preset.getValueAsString(); |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::removePreset() { |
| std::wstring name(m_preset.getValue()); |
| if (name == CUSTOM_WSTR) return; |
| |
| m_presetsManager.removePreset(name); |
| initPresets(); |
| |
| |
| m_preset.setValue(CUSTOM_WSTR); |
| RasterBrushPreset = m_preset.getValueAsString(); |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::loadLastBrush() { |
| m_rasThickness.setValue( |
| TDoublePairProperty::Value(RasterBrushMinSize, RasterBrushMaxSize)); |
| m_drawOrder.setIndex(BrushDrawOrder); |
| m_pencil.setValue(RasterBrushPencilMode ? 1 : 0); |
| m_hardness.setValue(RasterBrushHardness); |
| m_pressure.setValue(BrushPressureSensitivity ? 1 : 0); |
| m_smooth.setValue(BrushSmooth); |
| m_modifierSize.setValue(RasterBrushModifierSize); |
| m_modifierLockAlpha.setValue(BrushLockAlpha ? 1 : 0); |
| |
| |
| m_minThick = m_rasThickness.getValue().first; |
| m_maxThick = m_rasThickness.getValue().second; |
| |
| setWorkAndBackupImages(); |
| |
| m_brushPad = getBrushPad(m_rasThickness.getValue().second, |
| m_hardness.getValue() * 0.01); |
| } |
| |
| |
| |
| |
| bool ToonzRasterBrushTool::isPencilModeActive() { |
| return getTargetType() == TTool::ToonzImage && m_pencil.getValue(); |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::onColorStyleChanged() { |
| m_inputmanager.finishTracks(); |
| m_enabled = false; |
| |
| TTool::Application *app = getApplication(); |
| TMyPaintBrushStyle *mpbs = |
| dynamic_cast<TMyPaintBrushStyle *>(app->getCurrentLevelStyle()); |
| m_isMyPaintStyleSelected = (mpbs) ? true : false; |
| setWorkAndBackupImages(); |
| getApplication()->getCurrentTool()->notifyToolChanged(); |
| } |
| |
| |
| |
| double ToonzRasterBrushTool::restartBrushTimer() { |
| double dtime = m_brushTimer.nsecsElapsed() * 1e-9; |
| m_brushTimer.restart(); |
| return dtime; |
| } |
| |
| |
| |
| void ToonzRasterBrushTool::updateCurrentStyle() { |
| if (m_isMyPaintStyleSelected) { |
| TTool::Application *app = TTool::getApplication(); |
| TMyPaintBrushStyle *brushStyle = |
| dynamic_cast<TMyPaintBrushStyle *>(app->getCurrentLevelStyle()); |
| if (!brushStyle) { |
| |
| onColorStyleChanged(); |
| return; |
| } |
| double radiusLog = brushStyle->getBrush().getBaseValue( |
| MYPAINT_BRUSH_SETTING_RADIUS_LOGARITHMIC) + |
| m_modifierSize.getValue() * log(2.0); |
| double radius = exp(radiusLog); |
| m_minCursorThick = m_maxCursorThick = (int)std::round(2.0 * radius); |
| } |
| } |
| |
| |
| ToonzRasterBrushToolNotifier::ToonzRasterBrushToolNotifier( |
| ToonzRasterBrushTool *tool) |
| : m_tool(tool) { |
| if (TTool::Application *app = m_tool->getApplication()) { |
| if (TPaletteHandle *paletteHandle = app->getCurrentPalette()) { |
| bool ret; |
| ret = connect(paletteHandle, SIGNAL(colorStyleChanged(bool)), this, |
| SLOT(onColorStyleChanged())); |
| ret = ret && connect(paletteHandle, SIGNAL(colorStyleSwitched()), this, |
| SLOT(onColorStyleChanged())); |
| ret = ret && connect(paletteHandle, SIGNAL(paletteSwitched()), this, |
| SLOT(onColorStyleChanged())); |
| assert(ret); |
| } |
| } |
| onColorStyleChanged(); |
| } |
| |
| |
| |
| |
| |
| ToonzRasterBrushTool toonzPencil("T_Brush", |
| TTool::ToonzImage | TTool::EmptyTarget); |
| |
| |
| |
| |
| |
| BrushData::BrushData() |
| : m_name() |
| , m_min(0.0) |
| , m_max(0.0) |
| , m_smooth(0.0) |
| , m_hardness(0.0) |
| , m_opacityMin(0.0) |
| , m_opacityMax(0.0) |
| , m_pencil(false) |
| , m_pressure(false) |
| , m_modifierSize(0.0) |
| , m_drawOrder(0) |
| , m_modifierOpacity(0.0) |
| , m_modifierEraser(0.0) |
| , m_modifierLockAlpha(0.0) |
| , m_assistants(false) {} |
| |
| |
| |
| BrushData::BrushData(const std::wstring &name) |
| : m_name(name) |
| , m_min(0.0) |
| , m_max(0.0) |
| , m_smooth(0.0) |
| , m_hardness(0.0) |
| , m_opacityMin(0.0) |
| , m_opacityMax(0.0) |
| , m_pencil(false) |
| , m_pressure(false) |
| , m_drawOrder(0) |
| , m_modifierSize(0.0) |
| , m_modifierOpacity(0.0) |
| , m_modifierEraser(0.0) |
| , m_modifierLockAlpha(0.0) |
| , m_assistants(false) {} |
| |
| |
| |
| void BrushData::saveData(TOStream &os) { |
| os.openChild("Name"); |
| os << m_name; |
| os.closeChild(); |
| os.openChild("Thickness"); |
| os << m_min << m_max; |
| os.closeChild(); |
| os.openChild("Smooth"); |
| os << m_smooth; |
| os.closeChild(); |
| os.openChild("Hardness"); |
| os << m_hardness; |
| os.closeChild(); |
| os.openChild("Opacity"); |
| os << m_opacityMin << m_opacityMax; |
| os.closeChild(); |
| os.openChild("Draw_Order"); |
| os << m_drawOrder; |
| os.closeChild(); |
| os.openChild("Pencil"); |
| os << (int)m_pencil; |
| os.closeChild(); |
| os.openChild("Pressure_Sensitivity"); |
| os << (int)m_pressure; |
| os.closeChild(); |
| os.openChild("Modifier_Size"); |
| os << m_modifierSize; |
| os.closeChild(); |
| os.openChild("Modifier_Opacity"); |
| os << m_modifierOpacity; |
| os.closeChild(); |
| os.openChild("Modifier_Eraser"); |
| os << (int)m_modifierEraser; |
| os.closeChild(); |
| os.openChild("Modifier_LockAlpha"); |
| os << (int)m_modifierLockAlpha; |
| os.closeChild(); |
| os.openChild("Assistants"); |
| os << (int)m_assistants; |
| os.closeChild(); |
| } |
| |
| |
| |
| void BrushData::loadData(TIStream &is) { |
| std::string tagName; |
| int val; |
| |
| while (is.matchTag(tagName)) { |
| if (tagName == "Name") |
| is >> m_name, is.matchEndTag(); |
| else if (tagName == "Thickness") |
| is >> m_min >> m_max, is.matchEndTag(); |
| else if (tagName == "Smooth") |
| is >> m_smooth, is.matchEndTag(); |
| else if (tagName == "Hardness") |
| is >> m_hardness, is.matchEndTag(); |
| else if (tagName == "Opacity") |
| is >> m_opacityMin >> m_opacityMax, is.matchEndTag(); |
| else if (tagName == "Selective" || |
| tagName == "Draw_Order") |
| |
| is >> m_drawOrder, is.matchEndTag(); |
| else if (tagName == "Pencil") |
| is >> val, m_pencil = val, is.matchEndTag(); |
| else if (tagName == "Pressure_Sensitivity") |
| is >> val, m_pressure = val, is.matchEndTag(); |
| else if (tagName == "Modifier_Size") |
| is >> m_modifierSize, is.matchEndTag(); |
| else if (tagName == "Modifier_Opacity") |
| is >> m_modifierOpacity, is.matchEndTag(); |
| else if (tagName == "Modifier_Eraser") |
| is >> val, m_modifierEraser = val, is.matchEndTag(); |
| else if (tagName == "Modifier_LockAlpha") |
| is >> val, m_modifierLockAlpha = val, is.matchEndTag(); |
| else if (tagName == "Assistants") |
| is >> val, m_assistants = val, is.matchEndTag(); |
| else |
| is.skipCurrentTag(); |
| } |
| } |
| |
| |
| |
| PERSIST_IDENTIFIER(BrushData, "BrushData"); |
| |
| |
| |
| |
| |
| void BrushPresetManager::load(const TFilePath &fp) { |
| m_fp = fp; |
| |
| std::string tagName; |
| BrushData data; |
| |
| TIStream is(m_fp); |
| try { |
| while (is.matchTag(tagName)) { |
| if (tagName == "version") { |
| VersionNumber version; |
| is >> version.first >> version.second; |
| |
| is.setVersion(version); |
| is.matchEndTag(); |
| } else if (tagName == "brushes") { |
| while (is.matchTag(tagName)) { |
| if (tagName == "brush") { |
| is >> data, m_presets.insert(data); |
| is.matchEndTag(); |
| } else |
| is.skipCurrentTag(); |
| } |
| |
| is.matchEndTag(); |
| } else |
| is.skipCurrentTag(); |
| } |
| } catch (...) { |
| } |
| } |
| |
| |
| |
| void BrushPresetManager::save() { |
| TOStream os(m_fp); |
| |
| os.openChild("version"); |
| os << 1 << 19; |
| os.closeChild(); |
| |
| os.openChild("brushes"); |
| |
| std::set<BrushData>::iterator it, end = m_presets.end(); |
| for (it = m_presets.begin(); it != end; ++it) { |
| os.openChild("brush"); |
| os << (TPersist &)*it; |
| os.closeChild(); |
| } |
| |
| os.closeChild(); |
| } |
| |
| |
| |
| void BrushPresetManager::addPreset(const BrushData &data) { |
| m_presets.erase(data); |
| m_presets.insert(data); |
| save(); |
| } |
| |
| |
| |
| void BrushPresetManager::removePreset(const std::wstring &name) { |
| m_presets.erase(BrushData(name)); |
| save(); |
| } |
| |