diff --git a/app/app.pro b/app/app.pro
index 5ebdfa065..25b310639 100644
--- a/app/app.pro
+++ b/app/app.pro
@@ -59,6 +59,7 @@ INCLUDEPATH += \
PRECOMPILED_HEADER = src/app-pch.h
HEADERS += \
+ src/addtransparencytopaperdialog.h \
src/app-pch.h \
src/importlayersdialog.h \
src/importpositiondialog.h \
@@ -102,7 +103,8 @@ HEADERS += \
src/doubleprogressdialog.h \
src/colorslider.h \
src/checkupdatesdialog.h \
- src/presetdialog.h \
+ src/bitmapcoloring.h \
+ src/presetdialog.h \
src/repositionframesdialog.h \
src/commandlineparser.h \
src/commandlineexporter.h \
@@ -111,6 +113,7 @@ HEADERS += \
src/cameraoptionswidget.h
SOURCES += \
+ src/addtransparencytopaperdialog.cpp \
src/importlayersdialog.cpp \
src/importpositiondialog.cpp \
src/layeropacitydialog.cpp \
@@ -153,6 +156,7 @@ SOURCES += \
src/doubleprogressdialog.cpp \
src/colorslider.cpp \
src/checkupdatesdialog.cpp \
+ src/bitmapcoloring.cpp \
src/presetdialog.cpp \
src/repositionframesdialog.cpp \
src/app_util.cpp \
@@ -163,6 +167,7 @@ SOURCES += \
src/cameraoptionswidget.cpp
FORMS += \
+ ui/addtransparencytopaperdialog.ui \
ui/cameraoptionswidget.ui \
ui/camerapropertiesdialog.ui \
ui/importimageseqpreview.ui \
@@ -191,6 +196,7 @@ FORMS += \
ui/filespage.ui \
ui/toolspage.ui \
ui/toolboxwidget.ui \
+ ui/bitmapcoloringwidget.ui \
ui/presetdialog.ui
GIT {
diff --git a/app/data/app.qrc b/app/data/app.qrc
index 1c78e052b..b7573e0e0 100644
--- a/app/data/app.qrc
+++ b/app/data/app.qrc
@@ -54,6 +54,9 @@
icons/new/svg/smudge_detailed.svg
icons/new/svg/trash_detailed.svg
icons/new/checkerboard_smaller.png
+ icons/blue.png
+ icons/green.png
+ icons/red.png
icons/overlayCenter.png
icons/overlayGoldenRatio.png
icons/overlaySafe.png
diff --git a/app/data/icons/blue.png b/app/data/icons/blue.png
new file mode 100644
index 000000000..7bf25abce
Binary files /dev/null and b/app/data/icons/blue.png differ
diff --git a/app/data/icons/green.png b/app/data/icons/green.png
new file mode 100644
index 000000000..f442c52d7
Binary files /dev/null and b/app/data/icons/green.png differ
diff --git a/app/data/icons/red.png b/app/data/icons/red.png
new file mode 100644
index 000000000..7d230c92d
Binary files /dev/null and b/app/data/icons/red.png differ
diff --git a/app/src/addtransparencytopaperdialog.cpp b/app/src/addtransparencytopaperdialog.cpp
new file mode 100644
index 000000000..44ba86681
--- /dev/null
+++ b/app/src/addtransparencytopaperdialog.cpp
@@ -0,0 +1,250 @@
+#include "addtransparencytopaperdialog.h"
+#include "ui_addtransparencytopaperdialog.h"
+
+#include
+#include
+#include
+
+#include "editor.h"
+#include "layermanager.h"
+#include "selectionmanager.h"
+#include "layerbitmap.h"
+#include "bitmapimage.h"
+
+
+AddTransparencyToPaperDialog::AddTransparencyToPaperDialog(QDialog *parent) :
+ QDialog(parent),
+ ui(new Ui::AddTransparencyToPaperDialog)
+{
+ ui->setupUi(this);
+ ui->mainLayout->setStretchFactor(ui->optionsLayout, 1);
+ ui->mainLayout->setStretchFactor(ui->previewLayout, 20);
+
+ connect(this, &QDialog::finished, this, &AddTransparencyToPaperDialog::closeDialog);
+ connect(ui->sb_treshold, static_cast(&QSpinBox::valueChanged), this, &AddTransparencyToPaperDialog::SpinboxChanged);
+ connect(ui->sliderThreshold, &QSlider::valueChanged, this, &AddTransparencyToPaperDialog::SliderChanged);
+ connect(ui->cb_Red, &QCheckBox::stateChanged, this, &AddTransparencyToPaperDialog::updateDrawing);
+ connect(ui->cb_Green, &QCheckBox::stateChanged, this, &AddTransparencyToPaperDialog::updateDrawing);
+ connect(ui->cb_Blue, &QCheckBox::stateChanged, this, &AddTransparencyToPaperDialog::updateDrawing);
+ connect(ui->btnCancel, &QPushButton::clicked, this, &AddTransparencyToPaperDialog::closeDialog);
+ connect(ui->btnApply, &QPushButton::clicked, this, &AddTransparencyToPaperDialog::traceScannedDrawings);
+ connect(ui->testTransparencyCheckbox, &QCheckBox::stateChanged, this, &AddTransparencyToPaperDialog::checkerStateChanged);
+ connect(ui->zoomSlider, &QSlider::valueChanged, this, &AddTransparencyToPaperDialog::zoomChanged);
+}
+
+AddTransparencyToPaperDialog::~AddTransparencyToPaperDialog()
+{
+ delete ui;
+}
+
+void AddTransparencyToPaperDialog::setCore(Editor *editor)
+{
+ mEditor = editor;
+}
+
+void AddTransparencyToPaperDialog::initUI()
+{
+ if (mEditor->layers()->currentLayer()->type() != Layer::BITMAP)
+ this->setEnabled(false);
+ loadDrawing(mEditor->currentFrame());
+ connect(mEditor->layers(), &LayerManager::currentLayerChanged, this, &AddTransparencyToPaperDialog::layerChanged);
+ connect(mEditor, &Editor::scrubbedTo, this, &AddTransparencyToPaperDialog::updateDrawing);
+ connect(mEditor, &Editor::currentFrameUpdated, this, &AddTransparencyToPaperDialog::updateDrawing);
+
+ scene.setBackgroundBrush(Qt::white);
+ ui->preview->setScene(&scene);
+ ui->preview->show();
+
+ if (!mBitmap.bounds().isValid()) {
+ ui->btnApply->setEnabled(false);
+ }
+}
+
+void AddTransparencyToPaperDialog::SpinboxChanged(int value)
+{
+ mThreshold = value;
+ ui->sliderThreshold->setValue(value);
+ updateDrawing();
+}
+
+void AddTransparencyToPaperDialog::SliderChanged(int value)
+{
+ mThreshold = value;
+ ui->sb_treshold->setValue(value);
+ updateDrawing();
+}
+
+void AddTransparencyToPaperDialog::checkerStateChanged(bool state)
+{
+ if (state) {
+ scene.setBackgroundBrush(QBrush(QImage(":/background/checkerboard.png")));
+ } else {
+ scene.setBackgroundBrush(Qt::white);
+ }
+}
+
+void AddTransparencyToPaperDialog::zoomChanged(int zoomLevel)
+{
+ mZoomLevel = zoomLevel;
+ updatePreview();
+}
+
+void AddTransparencyToPaperDialog::resizeEvent(QResizeEvent*)
+{
+ updatePreview();
+}
+
+void AddTransparencyToPaperDialog::updatePreview()
+{
+ QImage loadedImage = *mBitmap.image();
+
+ QSize previewSize = ui->preview->size()*mZoomLevel;
+ QSize size = mBitmap.size().scaled(previewSize, Qt::KeepAspectRatioByExpanding);
+ mPixmapFromImage = QPixmap(size);
+ mPixmapFromImage.fill(Qt::transparent);
+
+ QPainter painter(&mPixmapFromImage);
+
+ painter.drawImage(QRect(QPoint(0,0),QSize(size)), loadedImage, loadedImage.rect());
+ mPreviewImageItem->setPixmap(mPixmapFromImage);
+
+ scene.setSceneRect(QRect(QPoint(), previewSize));
+}
+
+void AddTransparencyToPaperDialog::loadDrawing(int frame)
+{
+ if (mEditor->layers()->currentLayer()->type() != Layer::BITMAP) { return; }
+
+ LayerBitmap* layer = static_cast(mEditor->layers()->currentLayer());
+
+ if (!layer->keyExists(frame))
+ {
+ if (!layer->keyExistsWhichCovers(frame))
+ frame = layer->getPreviousKeyFramePosition(frame);
+ else
+ frame = layer->getNextKeyFramePosition(frame);
+ }
+
+ ui->labShowingFrame->setText(tr("Previewing frame %1").arg(QString::number(frame)));
+
+ BitmapImage* currentImage = layer->getBitmapImageAtFrame(frame);
+
+ if (!currentImage) { return; }
+
+ mBitmap = currentImage->copy();
+ mBitmap.setThreshold(mThreshold);
+
+ mBitmap = *mBitmap.scanToTransparent(&mBitmap,
+ ui->cb_Red->isChecked(),
+ ui->cb_Green->isChecked(),
+ ui->cb_Blue->isChecked());
+
+ if (mPreviewImageItem == nullptr) {
+ mPreviewImageItem = scene.addPixmap(mPixmapFromImage);
+ } else {
+ mPreviewImageItem->setPixmap(mPixmapFromImage);
+ }
+
+ ui->btnApply->setEnabled(true);
+
+ updatePreview();
+}
+
+void AddTransparencyToPaperDialog::updateDrawing()
+{
+ loadDrawing(mEditor->currentFrame());
+}
+
+void AddTransparencyToPaperDialog::layerChanged(int index)
+{
+ if (mEditor->layers()->getLayer(index)->type() == Layer::BITMAP)
+ {
+ this->setEnabled(true);
+ updateDrawing();
+ }
+ else
+ {
+ this->setEnabled(false);
+ }
+}
+
+void AddTransparencyToPaperDialog::traceScannedDrawings()
+{
+ if (mEditor->layers()->currentLayer()->type() != Layer::BITMAP) { return; }
+
+ LayerBitmap* layer = static_cast(mEditor->layers()->currentLayer());
+ BitmapImage* img = new BitmapImage();
+ bool somethingSelected = mEditor->select()->somethingSelected();
+
+ if (ui->rbCurrentKeyframe->isChecked())
+ {
+ int frame = mEditor->currentFrame();
+ if (!layer->keyExists(frame))
+ {
+ if (!layer->keyExistsWhichCovers(frame))
+ frame = layer->getPreviousKeyFramePosition(frame);
+ else
+ frame = layer->getNextKeyFramePosition(frame);
+ }
+ mEditor->scrubTo(frame);
+
+ if (somethingSelected)
+ {
+ mEditor->copy();
+ layer->removeKeyFrame(frame);
+ layer->addNewKeyFrameAt(frame);
+ mEditor->paste();
+ }
+ img = layer->getBitmapImageAtFrame(frame);
+ img->setThreshold(mThreshold);
+ img = img->scanToTransparent(img,
+ ui->cb_Red->isChecked(),
+ ui->cb_Green->isChecked(),
+ ui->cb_Blue->isChecked());
+ img->modification();
+ }
+ else
+ {
+ mEditor->setIsDoingRepeatColoring(true);
+ int count = mEditor->getAutoSaveCounter();
+ QProgressDialog* mProgress = new QProgressDialog(tr("Tracing scanned drawings..."), tr("Abort"), 0, 100, this);
+ mProgress->setWindowModality(Qt::WindowModal);
+ mProgress->show();
+ mProgress->setMaximum(layer->keyFrameCount());
+ mProgress->setValue(0);
+ int keysThinned = 0;
+ QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ for (int i = layer->firstKeyFramePosition(); i <= layer->getMaxKeyFramePosition(); i++)
+ {
+ if (layer->keyExists(i))
+ {
+ mProgress->setValue(keysThinned++);
+ mEditor->scrubTo(i);
+ count++;
+ if (mProgress->wasCanceled())
+ {
+ break;
+ }
+ if (somethingSelected)
+ {
+ mEditor->copy();
+ layer->removeKeyFrame(i);
+ layer->addNewKeyFrameAt(i);
+ mEditor->paste();
+ }
+ img = layer->getBitmapImageAtFrame(i);
+ img->setThreshold(mThreshold);
+ img = img->scanToTransparent(img,
+ ui->cb_Red->isChecked(),
+ ui->cb_Green->isChecked(),
+ ui->cb_Blue->isChecked());
+ img->modification();
+ }
+ }
+ mProgress->close();
+ mEditor->setIsDoingRepeatColoring(false);
+ mEditor->setAutoSaveCounter(count);
+ }
+ if (ui->rbAllKeyframes->isChecked())
+ emit closeDialog();
+}
diff --git a/app/src/addtransparencytopaperdialog.h b/app/src/addtransparencytopaperdialog.h
new file mode 100644
index 000000000..459ab2cfd
--- /dev/null
+++ b/app/src/addtransparencytopaperdialog.h
@@ -0,0 +1,60 @@
+#ifndef ADDTRANSPARENCYTOPAPERDIALOG_H
+#define ADDTRANSPARENCYTOPAPERDIALOG_H
+
+#include
+#include
+
+#include "bitmapimage.h"
+
+class Editor;
+class QGraphicsPixmapItem;
+
+namespace Ui {
+class AddTransparencyToPaperDialog;
+}
+
+class AddTransparencyToPaperDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit AddTransparencyToPaperDialog(QDialog *parent = nullptr);
+ ~AddTransparencyToPaperDialog() override;
+
+ void setCore(Editor* editor);
+
+ void initUI();
+
+signals:
+ void closeDialog();
+
+protected:
+ void resizeEvent(QResizeEvent*) override;
+
+private slots:
+ void SpinboxChanged(int value);
+ void SliderChanged(int value);
+ void traceScannedDrawings();
+ void updateDrawing();
+ void layerChanged(int index);
+ void checkerStateChanged(bool state);
+ void zoomChanged(int zoomLevel);
+
+private:
+ void updatePreview();
+ void loadDrawing(int frame);
+
+ int mZoomLevel = 1;
+
+ Ui::AddTransparencyToPaperDialog *ui;
+
+ QGraphicsScene scene;
+ QGraphicsPixmapItem* mPreviewImageItem = nullptr;
+
+ int mThreshold = 220;
+ BitmapImage mBitmap;
+ QPixmap mPixmapFromImage;
+ Editor* mEditor = nullptr;
+};
+
+#endif // ADDTRANSPARENCYTOPAPERDIALOG_H
diff --git a/app/src/bitmapcoloring.cpp b/app/src/bitmapcoloring.cpp
new file mode 100644
index 000000000..e064f92dd
--- /dev/null
+++ b/app/src/bitmapcoloring.cpp
@@ -0,0 +1,779 @@
+/*
+
+Pencil - Traditional Animation Software
+Copyright (C) 2012-2021 Matthew Chiawen Chang
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2 of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+*/
+
+#include
+#include
+#include "bitmapcoloring.h"
+#include "ui_bitmapcoloringwidget.h"
+#include "editor.h"
+#include "layerbitmap.h"
+#include "layermanager.h"
+#include "scribblearea.h"
+#include "app_util.h"
+#include "object.h"
+
+
+BitmapColoring::BitmapColoring(Editor* editor, QWidget *parent) :
+ BaseDockWidget(parent)
+{
+ QWidget* innerWidget = new QWidget;
+ setWindowTitle(tr("Advanced Coloring"));
+
+ ui = new Ui::BitmapColoringWidget;
+ ui->setupUi(innerWidget);
+ setWidget(innerWidget);
+
+ mEditor = editor;
+ mScribblearea = mEditor->getScribbleArea();
+ if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP)
+ mLayerBitmap = static_cast(mEditor->layers()->currentLayer());
+ mBitmapImage = mLayerBitmap->getBitmapImageAtFrame(mEditor->currentFrame());
+ checkRedBoxes();
+ checkGreenBoxes();
+ checkBlueBoxes();
+
+ connect(ui->cb2TraceRed, &QCheckBox::stateChanged, this, &BitmapColoring::checkRedBoxes);
+ connect(ui->cb2TraceGreen, &QCheckBox::stateChanged, this, &BitmapColoring::checkGreenBoxes);
+ connect(ui->cb2TraceBlue, &QCheckBox::stateChanged, this, &BitmapColoring::checkBlueBoxes);
+ connect(ui->cb3TraceAllKeyframes, &QCheckBox::stateChanged, this, &BitmapColoring::checkAllKeyframesBoxes);
+ connect(ui->btnResetTrace, &QPushButton::clicked, this, &BitmapColoring::resetColoringDock);
+ connect(ui->cbMethodSelector, QOverload::of(&QComboBox::currentIndexChanged), this, &BitmapColoring::enableTabs);
+ connect(ui->btnInfo, &QPushButton::clicked, this, &BitmapColoring::infoBox);
+
+ // Prepare
+ connect(ui->tabWidget, &QTabWidget::tabBarClicked, this, &BitmapColoring::tabWidgetClicked);
+ connect(ui->btnPrepareLines, &QPushButton::clicked, this, &BitmapColoring::prepareAndTraceLines);
+ connect(ui->btnApplyTrace, &QPushButton::clicked, this, &BitmapColoring::traceLines);
+ // Thin
+ connect(ui->sbSpotAreas, QOverload::of(&QSpinBox::valueChanged), this, &BitmapColoring::setSpotArea);
+ connect(ui->cbSpotAreas, &QCheckBox::stateChanged, this, &BitmapColoring::updateFillSpotsButton);
+ connect(ui->btnFillAreas, &QPushButton::clicked, this, &BitmapColoring::fillSpotAreas);
+ connect(ui->btnApplyThin, &QPushButton::clicked, this, &BitmapColoring::thinLines);
+ // Finish
+ connect(ui->btnApplyBlend, &QPushButton::clicked, this, &BitmapColoring::blendLines);
+ updateTraceButtons();
+
+}
+
+BitmapColoring::~BitmapColoring()
+{
+ delete ui;
+}
+
+void BitmapColoring::initUI()
+{
+ updateUI();
+}
+
+void BitmapColoring::updateUI()
+{
+ if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP)
+ mLayerBitmap = static_cast(mEditor->layers()->currentLayer());
+ if (mLayerBitmap == nullptr) { return; }
+
+ if (mEditor->layers()->currentLayer()->type() == Layer::BITMAP &&
+ ui->cbMethodSelector->currentIndex() > 0)
+ {
+ if (mLayerBitmap->getHasColorLayer())
+ {
+ ui->tab1->setEnabled(true);
+ ui->tab2->setEnabled(false);
+ ui->tab3->setEnabled(false);
+ }
+ else if (mLayerBitmap->getIsColorLayer())
+ {
+ ui->tab1->setEnabled(false);
+ ui->tab2->setEnabled(true);
+ ui->tab3->setEnabled(true);
+ }
+ else
+ {
+ ui->tab1->setEnabled(true);
+ ui->tab2->setEnabled(true);
+ ui->tab3->setEnabled(true);
+ }
+ }
+ else
+ { // If it is not a Bitmap Layer OR method not chosen - disable
+ ui->tab1->setEnabled(false);
+ ui->tab2->setEnabled(false);
+ ui->tab3->setEnabled(false);
+ }
+ mScribblearea->updateFrame();
+}
+
+void BitmapColoring::onVisibilityChanged(bool visibility)
+{
+ if (visibility)
+ updateUI();
+}
+
+void BitmapColoring::checkRedBoxes()
+{
+ if (ui->cb2TraceRed->isChecked())
+ {
+ ui->cb2ThinRed->setChecked(ui->cb2TraceRed->isChecked());
+ ui->cb2BlendRed->setChecked(ui->cb2TraceRed->isChecked());
+ mRedChecked = true;
+ }
+ else
+ {
+ mRedChecked = false;
+ }
+ updateTraceButtons();
+}
+
+void BitmapColoring::checkGreenBoxes()
+{
+ if (ui->cb2TraceGreen->isChecked())
+ {
+ ui->cb2ThinGreen->setChecked(ui->cb2TraceGreen->isChecked());
+ ui->cb2BlendGreen->setChecked(ui->cb2TraceGreen->isChecked());
+ mGreenChecked = true;
+ }
+ else
+ {
+ mGreenChecked = false;
+ }
+ updateTraceButtons();
+}
+
+void BitmapColoring::checkBlueBoxes()
+{
+ if (ui->cb2TraceBlue->isChecked())
+ {
+ ui->cb2ThinBlue->setChecked(ui->cb2TraceBlue->isChecked());
+ ui->cb2BlendBlue->setChecked(ui->cb2TraceBlue->isChecked());
+ mBlueChecked = true;
+ }
+ else
+ {
+ mBlueChecked = false;
+ }
+ updateTraceButtons();
+}
+
+void BitmapColoring::checkAllKeyframesBoxes()
+{
+ ui->cbThinAllKeyframes->setChecked(ui->cb3TraceAllKeyframes->isChecked());
+ ui->cb3BlendAllKeyframes->setChecked(ui->cb3TraceAllKeyframes->isChecked());
+}
+
+void BitmapColoring::tabWidgetClicked(int index)
+{
+ switch (index)
+ {
+ case 0:
+ updateTraceBoxes();
+ break;
+ case 1:
+ updateThinBoxes();
+ break;
+ case 2:
+ updateBlendBoxes();
+ break;
+ default:
+ updateTraceBoxes();
+ }
+}
+
+void BitmapColoring::resetColoringDock()
+{
+ ui->cbMethodSelector->setCurrentIndex(0);
+ ui->cbSpotAreas->setChecked(false);
+ ui->sbSpotAreas->setValue(6);
+ ui->cb2TraceRed->setChecked(false);
+ ui->cb2TraceGreen->setChecked(false);
+ ui->cb2TraceBlue->setChecked(false);
+ ui->cb3TraceAllKeyframes->setChecked(false);
+}
+
+void BitmapColoring::infoBox()
+{
+ QMessageBox::information(this, tr("Basics in Advanced Coloring")
+ ,tr("Advanced coloring is developed for users that use color-separation to achieve a shade-effect.\n"
+ "It is assumed that the animation is done with black/gray pencil,\n"
+ "and you can use red, blue and/or green for the color-separation.\n\n"
+ "In this example, we have a active layer called 'Bird'.\n"
+ "The Advanced coloring creates two new layers, called 'Bird_L' and 'Bird_C'.\n"
+ "'Bird_L' is a copy of the Line-art layer, and 'Bird_C' is the Coloring layer.\n"
+ "STEP 1: Trace:\n"
+ "Make sure that you are on the layer 'Bird'!\n"
+ "Check the relevant boxes for color-separation.\n"
+ "Check the 'all drawings' box, if it is all drawings on layer. It most times is.\n"
+ "Press 'Trace'\n"
+ "STEP 2: Thin:\n"
+ "Make sure that you are on the layer 'Bird_C'!\n"
+ "Here the traced lines are thinned to 1 pixel thickness.\n"
+ "To avoid 'holes' in your thinned line, you can run the 'Fill small areas' first.\n"
+ "Your original layer 'Bird' is un-altered through the process, and should be made hidden.\n"
+ "Press 'Thin'\n"
+ "STEP 3: Colorize:\n"
+ "Choose the Bucket-tool. Reference 'current layer', and method 'Replace'\n"
+ "NB! Uncheck 'Color tolerance' and 'Expand fill'!\n"
+ "STEP 4: Blend:\n"
+ "Press 'Blend and Finish', and the thinned lines will dissappear,\n"
+ "and be replaced with blending af neighboring colors. You're done!\n\n"));
+}
+
+void BitmapColoring::enableTabs(int index)
+{
+ Q_UNUSED(index)
+ updateUI();
+}
+
+// public Trace funtions
+void BitmapColoring::updateTraceBoxes()
+{
+ if (mLayerBitmap->getIsColorLayer())
+ {
+ ui->tab1->setEnabled(false);
+ }
+ else if (ui->cbMethodSelector->currentIndex() > 0)
+ {
+ ui->tab1->setEnabled(true);
+ ui->gb2Trace->setEnabled(true);
+ }
+}
+
+void BitmapColoring::traceLines()
+{
+ if (mLayerBitmap == nullptr || mLayerBitmap->type() != Layer::BITMAP) { return; }
+
+ if (ui->cb3TraceAllKeyframes->isChecked())
+ {
+ mEditor->setIsDoingRepeatColoring(true);
+ int count = mEditor->getAutoSaveCounter();
+ QProgressDialog mProgress(tr("Tracing lines in bitmaps..."), tr("Abort"), 0, 100, this);
+ mProgress.setWindowModality(Qt::WindowModal);
+ mProgress.show();
+ int keysToTrace = mLayerBitmap->keyFrameCount();
+ mProgress.setMaximum(keysToTrace);
+ mProgress.setValue(0);
+ int keysTraced = 0;
+ QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+
+ for (int i = mLayerBitmap->firstKeyFramePosition(); i <= mLayerBitmap->getMaxKeyFramePosition(); i++)
+ {
+ if (mLayerBitmap->keyExists(i))
+ {
+ mProgress.setValue(keysTraced++);
+ mEditor->scrubTo(i);
+ trace();
+ count++;
+ if (mProgress.wasCanceled())
+ {
+ break;
+ }
+ }
+ }
+ mProgress.close();
+
+ // move colorLayer beneath Animation layer
+ while (mColLayer > mAnimLayer)
+ {
+ mEditor->object()->swapLayers(mColLayer, mColLayer - 1);
+ mColLayer--;
+ }
+ mAnimLayer++;
+
+ mEditor->setIsDoingRepeatColoring(false);
+ mEditor->setAutoSaveCounter(count);
+ }
+ else if (mLayerBitmap->keyExists(mEditor->currentFrame()))
+ {
+ trace();
+ qDebug() << "TRACE one " << mEditor->currentFrame();
+ }
+ mEditor->deselectAll();
+ if (ui->cb3TraceAllKeyframes->isChecked())
+ {
+ ui->tabWidget->setCurrentIndex(1);
+ QMessageBox msgBox;
+ msgBox.setText(tr("Ready for thinning lines!"));
+ msgBox.exec();
+ }
+}
+
+void BitmapColoring::updateFillSpotsButton()
+{
+ if (ui->cbSpotAreas->isChecked())
+ {
+ ui->btnFillAreas->setEnabled(true);
+ ui->btnApplyThin->setEnabled(false);
+ ui->labReminder->setText(tr("Fill areas blocks Thin button!"));
+ }
+ else
+ {
+ ui->btnFillAreas->setEnabled(false);
+ ui->btnApplyThin->setEnabled(true);
+ ui->labReminder->setText("");
+ }
+}
+
+void BitmapColoring::fillSpotAreas()
+{
+ if (mEditor->layers()->currentLayer()->type() != Layer::BITMAP) { return; }
+
+ mLayerBitmap = static_cast(mEditor->layers()->currentLayer());
+
+ if (!ui->cbThinAllKeyframes->isChecked() && mLayerBitmap->keyExists(mEditor->currentFrame()))
+ {
+ mBitmapImage = mLayerBitmap->getBitmapImageAtFrame(mEditor->currentFrame());
+ mBitmapImage->setSpotArea(ui->sbSpotAreas->value());
+ mBitmapImage->fillSpotAreas(mBitmapImage);
+ mBitmapImage->modification();
+ mEditor->scrubTo(mEditor->currentFrame());
+ }
+ else
+ {
+ mEditor->setIsDoingRepeatColoring(true);
+ int count = mEditor->getAutoSaveCounter();
+ QProgressDialog mProgress(tr("Fill small areas in bitmaps..."), tr("Abort"), 0, 100, this);
+ mProgress.setWindowModality(Qt::WindowModal);
+ mProgress.show();
+ mProgress.setMaximum(mLayerBitmap->keyFrameCount());
+ mProgress.setValue(0);
+ int keysThinned = 0;
+ QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ for (int i = mLayerBitmap->firstKeyFramePosition(); i <= mLayerBitmap->getMaxKeyFramePosition(); i++)
+ {
+ if (mLayerBitmap->keyExists(i))
+ {
+ mEditor->scrubTo(i);
+ mProgress.setValue(keysThinned++);
+ mBitmapImage = mLayerBitmap->getBitmapImageAtFrame(mEditor->currentFrame());
+ mBitmapImage->setSpotArea(ui->sbSpotAreas->value());
+ mBitmapImage->fillSpotAreas(mBitmapImage);
+ mBitmapImage->modification();
+ count++;
+ }
+ if (mProgress.wasCanceled())
+ {
+ break;
+ }
+ }
+ mProgress.close();
+ mEditor->setIsDoingRepeatColoring(false);
+ mEditor->setAutoSaveCounter(count);
+ ui->cbSpotAreas->setChecked(false);
+ }
+ updateUI();
+}
+
+// public Thin functions
+void BitmapColoring::updateThinBoxes()
+{
+ if (mLayerBitmap->getHasColorLayer())
+ {
+ ui->tab2->setEnabled(false);
+ }
+ else if (ui->cbMethodSelector->currentIndex() > 0)
+ {
+ ui->tab2->setEnabled(true);
+ ui->gb2Thin->setEnabled(true);
+ }
+}
+
+void BitmapColoring::setSpotArea(int size)
+{
+ mBitmapImage->setSpotArea(size);
+}
+
+void BitmapColoring::thinLines()
+{
+ if (mLayerBitmap == nullptr) { return; }
+
+ if (!ui->cbThinAllKeyframes->isChecked() && mLayerBitmap->keyExists(mEditor->currentFrame()))
+ {
+ thin();
+ }
+ else
+ {
+ mEditor->setIsDoingRepeatColoring(true);
+ int count = mEditor->getAutoSaveCounter();
+ QProgressDialog mProgress(tr("Thinning lines in bitmaps..."), tr("Abort"), 0, 100, this);
+ mProgress.setWindowModality(Qt::WindowModal);
+ mProgress.show();
+ mProgress.setMaximum(mLayerBitmap->keyFrameCount());
+ mProgress.setValue(0);
+ int keysThinned = 0;
+ QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+ for (int i = mLayerBitmap->firstKeyFramePosition(); i <= mLayerBitmap->getMaxKeyFramePosition(); i++)
+ {
+ if (mLayerBitmap->keyExists(i))
+ {
+ mProgress.setValue(keysThinned++);
+ mEditor->scrubTo(i);
+ thin();
+ count++;
+ }
+ if (mProgress.wasCanceled())
+ {
+ break;
+ }
+ }
+ mProgress.close();
+ mEditor->setIsDoingRepeatColoring(false);
+ mEditor->setAutoSaveCounter(count);
+
+ ui->tabWidget->setCurrentIndex(2);
+ QMessageBox msgBox;
+ msgBox.setText(tr("Ready for coloring!"));
+ msgBox.exec();
+ }
+
+ mEditor->scrubTo(mEditor->currentFrame());
+}
+
+// public Blend functions
+void BitmapColoring::updateBlendBoxes()
+{
+ if (mLayerBitmap->getHasColorLayer())
+ {
+ ui->tab3->setEnabled(false);
+ }
+ else if (ui->cbMethodSelector->currentIndex() > 0)
+ {
+ ui->tab3->setEnabled(true);
+ }
+}
+
+void BitmapColoring::blendLines()
+{
+ if (mLayerBitmap == nullptr) { return; }
+
+ QString orgName = mLayerBitmap->name();
+ LayerBitmap* artLayer = nullptr;
+ orgName.chop(2);
+ QString artLayerName = orgName + "_L";
+ artLayer = static_cast(mEditor->layers()->findLayerByName(artLayerName));
+ if (!artLayer)
+ {
+ artLayerName.chop(2);
+ artLayer = static_cast(mEditor->layers()->findLayerByName(artLayerName));
+ }
+ if (artLayer == nullptr) { return; }
+
+ artLayer->setVisible(false);
+
+ if (!ui->cb3BlendAllKeyframes->isChecked() && mLayerBitmap->keyExists(mEditor->currentFrame()))
+ {
+ blend(artLayer);
+ }
+ else
+ {
+ mEditor->setIsDoingRepeatColoring(true);
+ int count = mEditor->getAutoSaveCounter();
+ QProgressDialog progress(tr("Blending lines in bitmaps..."), tr("Abort"), 0, 100, this);
+ hideQuestionMark(progress);
+ progress.setWindowModality(Qt::WindowModal);
+ progress.show();
+ int keysToBlend = mLayerBitmap->keyFrameCount();
+ progress.setMaximum(keysToBlend);
+ int keysBlended = 0;
+ QApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
+
+ for (int i = mLayerBitmap->firstKeyFramePosition(); i <= mLayerBitmap->getMaxKeyFramePosition(); i++)
+ {
+ if (mLayerBitmap->keyExists(i))
+ {
+ mEditor->scrubTo(i);
+ count++;
+ progress.setValue(keysBlended++);
+ blend(artLayer);
+ if (progress.wasCanceled())
+ {
+ break;
+ }
+ }
+ }
+ progress.close();
+ mEditor->setIsDoingRepeatColoring(false);
+ mEditor->setAutoSaveCounter(count);
+
+ ui->tabWidget->setCurrentIndex(0);
+ resetColoringDock();
+ QMessageBox msgBox;
+ msgBox.setText(tr("Coloring finished!\nDialog reset..."));
+ msgBox.exec();
+ }
+ mEditor->scrubTo(mEditor->currentFrame());
+}
+
+/*
+ * If drawings are made in Pencil2D, we need to preserve the originals by:
+ * - ...making a copy of the original layer, named 'name'_L (L for Line art)
+ * - ...making a new coloring layer named 'name'_C (C for Coloring)
+ * (unless copy and color layer already exists)
+*/
+void BitmapColoring::prepareAndTraceLines()
+{
+ bool black;
+ ui->cbMethodSelector->currentIndex() == 1 ? black = false: black = true;
+
+ LayerManager* lMgr = mEditor->layers();
+ LayerBitmap* sourceLayer = mLayerBitmap;
+ QString orgName = mLayerBitmap->name();
+ LayerBitmap* artLayer = nullptr;
+ LayerBitmap* colorLayer = nullptr;
+
+ if (!lMgr->findLayerByName(orgName + "_L"))
+ {
+ colorLayer = lMgr->createBitmapLayer(orgName + "_C");
+ artLayer = lMgr->createBitmapLayer(orgName + "_L");
+ }
+ else
+ {
+ colorLayer = static_cast(lMgr->findLayerByName(orgName + "_C"));
+ artLayer = static_cast(lMgr->findLayerByName(orgName + "_L"));
+ }
+ Q_ASSERT(artLayer && colorLayer);
+
+ artLayer->setHasColorLayer(true);
+ colorLayer->setIsColorLayer(true);
+
+ if (ui->cb3TraceAllKeyframes->isChecked())
+ {
+ for (int i = sourceLayer->firstKeyFramePosition(); i <= sourceLayer->getMaxKeyFramePosition(); i++)
+ {
+ if (sourceLayer->keyExists(i))
+ {
+ mEditor->scrubTo(i);
+ lMgr->setCurrentLayer(sourceLayer);
+ copyFrame(sourceLayer, artLayer, i);
+ BitmapImage* image = artLayer->getBitmapImageAtFrame(i);
+ image->prepDrawing(image,
+ mRedChecked,
+ mGreenChecked,
+ mBlueChecked);
+ copyFrame(artLayer, colorLayer, i);
+ BitmapImage* colorImage = colorLayer->getBitmapImageAtFrame(i);
+ colorImage->traceLine(colorImage,
+ black,
+ mRedChecked,
+ mGreenChecked,
+ mBlueChecked);
+ }
+ }
+ ui->tabWidget->setCurrentIndex(1);
+ QMessageBox msgBox;
+ msgBox.setText(tr("Ready for thinning lines!"));
+ msgBox.exec();
+ }
+ else
+ {
+ int i = mEditor->currentFrame();
+
+ if (sourceLayer->keyExists(i))
+ {
+ lMgr->setCurrentLayer(sourceLayer);
+ copyFrame(sourceLayer, artLayer, i);
+ BitmapImage* image = artLayer->getBitmapImageAtFrame(i);
+ image->prepDrawing(image,
+ mRedChecked,
+ mGreenChecked,
+ mBlueChecked);
+ copyFrame(artLayer, colorLayer, i);
+ BitmapImage* colorImage = colorLayer->getBitmapImageAtFrame(i);
+ colorImage->traceLine(colorImage,
+ black,
+ mRedChecked,
+ mGreenChecked,
+ mBlueChecked);
+ }
+ }
+ lMgr->setCurrentLayer(colorLayer);
+
+ mEditor->scrubTo(mEditor->currentFrame());
+}
+
+// protected functions
+void BitmapColoring::prepareLines()
+{
+ // Method selector can be 0, 1 or 2. 0 = No method selected
+ if (ui->cbMethodSelector->currentIndex() < 1)
+ {
+ return;
+ }
+
+ LayerManager* lMgr = mEditor->layers();
+ LayerBitmap* colorLayer = nullptr;
+ bool black;
+ ui->cbMethodSelector->currentIndex() == 1 ? black = false: black = true;
+ // Method selector 1 = Coloring on same layer
+ if (ui->cbMethodSelector->currentIndex() == 1)
+ {
+ colorLayer = mLayerBitmap;
+ }
+
+ // Method selector 2 = Coloring on separate layer
+ else
+ {
+ QString orgName = mLayerBitmap->name();
+ // is it a copy or is it a prepared scanned drawing?
+ if (orgName.endsWith("_L"))
+ orgName.chop(2);
+ if (!mLayerBitmap->getHasColorLayer())
+ {
+ mAnimLayer = mEditor->currentLayerIndex(); // necessary since new layer becomes currentlayer
+ colorLayer = lMgr->createBitmapLayer(orgName + "_C");
+ mColLayer = mEditor->object()->getLayerCount() - 1;
+ lMgr->setCurrentLayer(mAnimLayer);
+ mLayerBitmap->setHasColorLayer(true);
+ colorLayer->setIsColorLayer(true);
+ }
+ else
+ {
+ colorLayer = static_cast(lMgr->findLayerByName(orgName + "_C"));
+ }
+ }
+ Q_ASSERT(colorLayer);
+
+ if (ui->cbMethodSelector->currentIndex() == 2)
+ {
+ copyFrame(mLayerBitmap, colorLayer, mEditor->currentFrame());
+ }
+ mBitmapImage = colorLayer->getBitmapImageAtFrame(mEditor->currentFrame());
+ if (mBitmapImage == nullptr)
+ {
+ nonValidBitmap(mEditor->currentFrame());
+ return;
+ }
+
+ mBitmapImage->traceLine(mBitmapImage,
+ black,
+ mRedChecked,
+ mGreenChecked,
+ mBlueChecked);
+}
+
+void BitmapColoring::trace()
+{
+ mBitmapImage = mLayerBitmap->getBitmapImageAtFrame(mEditor->currentFrame());
+ if (mBitmapImage == nullptr)
+ {
+ nonValidBitmap(mEditor->currentFrame());
+ return;
+ }
+
+ prepareLines();
+ mEditor->backup("Trace lines");
+}
+
+void BitmapColoring::thin()
+{
+ bool black;
+ ui->cbMethodSelector->currentIndex() == 1 ? black = false: black = true;
+ mBitmapImage = mLayerBitmap->getBitmapImageAtFrame(mEditor->currentFrame());
+ if (mBitmapImage == nullptr)
+ {
+ nonValidBitmap(mEditor->currentFrame());
+ return;
+ }
+
+ mBitmapImage->toThinLine(mBitmapImage,
+ black,
+ ui->cb2ThinRed->isChecked(),
+ ui->cb2ThinGreen->isChecked(),
+ ui->cb2ThinBlue->isChecked());
+ mEditor->backup("Thin lines");
+ updateUI();
+}
+
+void BitmapColoring::blend(LayerBitmap *artLayer)
+{
+ bool black;
+ ui->cbMethodSelector->currentIndex() == 1 ? black = false: black = true;
+ mBitmapImage = mLayerBitmap->getBitmapImageAtFrame(mEditor->currentFrame());
+ if (mBitmapImage == nullptr)
+ {
+ nonValidBitmap(mEditor->currentFrame());
+ return;
+ }
+
+ mBitmapImage->blendLines(mLayerBitmap->getBitmapImageAtFrame(mEditor->currentFrame()),
+ black,
+ ui->cb2BlendRed->isChecked(),
+ ui->cb2BlendGreen->isChecked(),
+ ui->cb2BlendBlue->isChecked());
+ mEditor->backup("Blend lines");
+ if (ui->cbMethodSelector->currentIndex() == 2 && artLayer != nullptr)
+ {
+ mBitmapImage = artLayer->getBitmapImageAtFrame(mEditor->currentFrame());
+ if (mBitmapImage == nullptr)
+ {
+ nonValidBitmap(mEditor->currentFrame());
+ return;
+ }
+
+ mBitmapImage->eraseRedGreenBlueLines(mBitmapImage);
+ mEditor->backup("Blend lines");
+ }
+ updateUI();
+}
+
+void BitmapColoring::nonValidBitmap(int frame)
+{
+ QMessageBox msgBox;
+ msgBox.setText(tr("Frame %1 is not valid!\nAborting frame...").arg(frame));
+ msgBox.exec();
+}
+
+void BitmapColoring::updateTraceButtons()
+{
+ if (ui->cb2TraceRed->isChecked() || ui->cb2TraceGreen->isChecked() || ui->cb2TraceBlue->isChecked())
+ {
+ ui->btnPrepareLines->setEnabled(true);
+ ui->btnApplyTrace->setEnabled(false);
+ }
+ else
+ {
+ ui->btnPrepareLines->setEnabled(false);
+ ui->btnApplyTrace->setEnabled(true);
+ }
+}
+
+void BitmapColoring::copyFrame(LayerBitmap *fromLayer, LayerBitmap *toLayer, int frame)
+{
+ if (fromLayer->keyExists(frame))
+ {
+ KeyFrame* keyframe = fromLayer->getKeyFrameAt(frame);
+ KeyFrame* dupKey = keyframe->clone();
+ if (toLayer->keyExists(frame) && toLayer->keyFrameCount() == 1)
+ {
+ int i = toLayer->firstKeyFramePosition();
+ if (i == frame)
+ {
+ toLayer->addKeyFrame(frame + 1, dupKey);
+ toLayer->removeKeyFrame(frame);
+ toLayer->moveKeyFrame(frame + 1, -1);
+ } else
+ {
+ toLayer->addKeyFrame(frame, dupKey);
+ toLayer->removeKeyFrame(i);
+ }
+ }
+ else
+ {
+ toLayer->removeKeyFrame(frame);
+ toLayer->addKeyFrame(frame, dupKey);
+ }
+ toLayer->setModified(frame, true);
+ toLayer->getKeyFrameAt(frame)->modification();
+ }
+
+}
diff --git a/app/src/bitmapcoloring.h b/app/src/bitmapcoloring.h
new file mode 100644
index 000000000..c6f2191b7
--- /dev/null
+++ b/app/src/bitmapcoloring.h
@@ -0,0 +1,75 @@
+#ifndef BITMAPCOLORING_H
+#define BITMAPCOLORING_H
+
+#include "basedockwidget.h"
+
+class Object;
+class BitmapImage;
+class Editor;
+class LayerBitmap;
+class ScribbleArea;
+
+
+namespace Ui
+{
+class BitmapColoringWidget;
+}
+
+class BitmapColoring : public BaseDockWidget
+{
+ Q_OBJECT
+
+public:
+ explicit BitmapColoring(Editor* editor, QWidget *parent);
+ ~BitmapColoring() override;
+
+ void initUI() override;
+ void updateUI() override;
+ void onVisibilityChanged(bool visibility);
+
+private slots:
+ void checkRedBoxes();
+ void checkGreenBoxes();
+ void checkBlueBoxes();
+ void checkAllKeyframesBoxes();
+ void tabWidgetClicked(int index);
+ void resetColoringDock();
+ void infoBox();
+ void enableTabs(int index);
+ // 1: Trace
+ void updateTraceBoxes();
+ void prepareAndTraceLines();
+ void traceLines();
+ // 2: Thin
+ void updateFillSpotsButton();
+ void fillSpotAreas();
+ void updateThinBoxes();
+ void setSpotArea(int size);
+ void thinLines();
+ // 3: Blend
+ void updateBlendBoxes();
+ void blendLines();
+
+private:
+ void prepareLines();
+ void trace();
+ void thin();
+ void blend(LayerBitmap* artLayer);
+ void nonValidBitmap(int frame);
+ void updateTraceButtons();
+ void copyFrame(LayerBitmap *fromLayer, LayerBitmap *toLayer, int frame);
+
+ Ui::BitmapColoringWidget* ui = nullptr;
+ Editor* mEditor = nullptr;
+ ScribbleArea* mScribblearea = nullptr;
+ LayerBitmap* mLayerBitmap = nullptr;
+ BitmapImage* mBitmapImage = nullptr;
+ int mAnimLayer = 0; // Animation layer index
+ int mColLayer = 0; // Coloring layer index
+ bool mRedChecked = false;
+ bool mGreenChecked = false;
+ bool mBlueChecked = false;
+
+};
+
+#endif // BITMAPCOLORING_H
diff --git a/app/src/mainwindow2.cpp b/app/src/mainwindow2.cpp
index f59aaa32c..560b6edb6 100644
--- a/app/src/mainwindow2.cpp
+++ b/app/src/mainwindow2.cpp
@@ -64,8 +64,10 @@ GNU General Public License for more details.
#include "preferencesdialog.h"
#include "timeline.h"
#include "toolbox.h"
+#include "bitmapcoloring.h"
#include "onionskinwidget.h"
#include "pegbaralignmentdialog.h"
+#include "addtransparencytopaperdialog.h"
#include "repositionframesdialog.h"
//#include "preview.h"
@@ -171,6 +173,9 @@ void MainWindow2::createDockWidgets()
mToolBox = new ToolBoxWidget(this);
mToolBox->setObjectName("ToolBox");
+ mBitmapColoring = new BitmapColoring(mEditor, this);
+ mBitmapColoring->setObjectName("BitmapColoring");
+
mDockWidgets
<< mTimeLine
<< mColorBox
@@ -178,7 +183,8 @@ void MainWindow2::createDockWidgets()
<< mColorPalette
<< mOnionSkinWidget
<< mToolOptions
- << mToolBox;
+ << mToolBox
+ << mBitmapColoring;
mStartIcon = QIcon(":icons/controls/play.png");
mStopIcon = QIcon(":icons/controls/stop.png");
@@ -199,6 +205,7 @@ void MainWindow2::createDockWidgets()
addDockWidget(Qt::RightDockWidgetArea, mColorBox);
addDockWidget(Qt::RightDockWidgetArea, mColorInspector);
addDockWidget(Qt::RightDockWidgetArea, mColorPalette);
+ addDockWidget(Qt::RightDockWidgetArea, mBitmapColoring);
addDockWidget(Qt::LeftDockWidgetArea, mToolBox);
addDockWidget(Qt::LeftDockWidgetArea, mToolOptions);
addDockWidget(Qt::LeftDockWidgetArea, mOnionSkinWidget);
@@ -225,7 +232,10 @@ void MainWindow2::createDockWidgets()
for (BaseDockWidget* w : mDockWidgets)
{
w->setFloating(false);
- w->show();
+ if (w != mBitmapColoring)
+ {
+ w->show();
+ }
w->updateUI();
}
}
@@ -275,6 +285,7 @@ void MainWindow2::createMenus()
connect(ui->actionFlip_X, &QAction::triggered, mCommands, &ActionCommands::flipSelectionX);
connect(ui->actionFlip_Y, &QAction::triggered, mCommands, &ActionCommands::flipSelectionY);
connect(ui->actionPegbarAlignment, &QAction::triggered, this, &MainWindow2::openPegAlignDialog);
+ connect(ui->actionAdd_Transparency_to_paper, &QAction::triggered, this, &MainWindow2::openAddTranspToPaperDialog);
connect(ui->actionSelect_All, &QAction::triggered, mCommands, &ActionCommands::selectAll);
connect(ui->actionDeselect_All, &QAction::triggered, mCommands, &ActionCommands::deselectAll);
connect(ui->actionReposition_Selected_Frames, &QAction::triggered, this, &MainWindow2::openRepositionDialog);
@@ -432,6 +443,7 @@ void MainWindow2::createMenus()
mColorPalette->toggleViewAction(),
mTimeLine->toggleViewAction(),
mColorInspector->toggleViewAction(),
+ mBitmapColoring->toggleViewAction(),
mOnionSkinWidget->toggleViewAction()
};
@@ -523,6 +535,26 @@ void MainWindow2::openLayerOpacityDialog()
});
}
+void MainWindow2::openAddTranspToPaperDialog()
+{
+ if (mAddTranspToPaper == nullptr)
+ {
+ mAddTranspToPaper = new AddTransparencyToPaperDialog();
+ mAddTranspToPaper->setCore(mEditor);
+ mAddTranspToPaper->initUI();
+ mAddTranspToPaper->setWindowFlag(Qt::WindowStaysOnTopHint);
+ mAddTranspToPaper->show();
+
+ connect(mAddTranspToPaper, &AddTransparencyToPaperDialog::closeDialog, [=] {
+ mAddTranspToPaper->deleteLater();
+ mAddTranspToPaper = nullptr;
+ });
+
+ } else {
+ mAddTranspToPaper->raise();
+ }
+}
+
void MainWindow2::openRepositionDialog()
{
if (mEditor->layers()->currentLayer()->getSelectedFramesByPos().count() < 2)
@@ -694,6 +726,23 @@ bool MainWindow2::openObject(const QString& strFilePath)
setWindowModified(false);
ui->statusBar->updateModifiedStatus(false);
+ // identify color layers
+ for (int i = 1; i < mEditor->layers()->count(); i++)
+ {
+ Layer* color = mEditor->layers()->getLayer(i);
+ if (color->type() == Layer::BITMAP && color->name().endsWith("_C"))
+ {
+ QString tmp = color->name();
+ tmp.chop(2);
+ Layer* org = mEditor->layers()->findLayerByName(tmp);
+ if (org != nullptr)
+ {
+ color->setIsColorLayer(true);
+ org->setHasColorLayer(true);
+ }
+ }
+ }
+
progress.setValue(progress.maximum());
updateSaveState();
@@ -1502,6 +1551,8 @@ void MainWindow2::makeConnections(Editor* pEditor, TimeLine* pTimeline)
connect(pEditor->layers(), &LayerManager::currentLayerChanged, this, &MainWindow2::updateLayerMenu);
connect(pEditor->layers(), &LayerManager::currentLayerChanged, mToolOptions, &ToolOptionWidget::updateUI);
+ connect(pEditor->layers(), &LayerManager::currentLayerChanged, mBitmapColoring, &BitmapColoring::updateUI);
+ connect(mBitmapColoring, &QDockWidget::visibilityChanged, mBitmapColoring, &BitmapColoring::onVisibilityChanged);
}
void MainWindow2::makeConnections(Editor*, OnionSkinWidget*)
diff --git a/app/src/mainwindow2.h b/app/src/mainwindow2.h
index a9f9f7a0c..64f8444f3 100644
--- a/app/src/mainwindow2.h
+++ b/app/src/mainwindow2.h
@@ -19,8 +19,6 @@ GNU General Public License for more details.
#define MAINWINDOW2_H
#include
-#include "preferencemanager.h"
-
template class QList;
class QActionGroup;
@@ -30,6 +28,7 @@ class Editor;
class ScribbleArea;
class BaseDockWidget;
class ColorPaletteWidget;
+class BitmapColoring;
class OnionSkinWidget;
class ToolOptionWidget;
class TimeLine;
@@ -44,6 +43,7 @@ class ImportImageSeqDialog;
class BackupElement;
class LayerOpacityDialog;
class PegBarAlignmentDialog;
+class AddTransparencyToPaperDialog;
class RepositionFramesDialog;
class StatusBar;
enum class SETTING;
@@ -71,6 +71,7 @@ public slots:
void openRepositionDialog();
void closeRepositionDialog();
void openLayerOpacityDialog();
+ void openAddTranspToPaperDialog();
void currentLayerChanged();
void selectionChanged();
void viewFlipped();
@@ -162,6 +163,7 @@ private slots:
//PreviewWidget* mPreview = nullptr;
TimeLine* mTimeLine = nullptr;
ColorInspector* mColorInspector = nullptr;
+ BitmapColoring* mBitmapColoring = nullptr;
OnionSkinWidget* mOnionSkinWidget = nullptr;
QToolBar* mMainToolbar = nullptr;
QToolBar* mViewToolbar = nullptr;
@@ -173,6 +175,7 @@ private slots:
PegBarAlignmentDialog* mPegAlign = nullptr;
RepositionFramesDialog* mReposDialog = nullptr;
LayerOpacityDialog* mLayerOpacityDialog = nullptr;
+ AddTransparencyToPaperDialog* mAddTranspToPaper = nullptr;
void createToolbars();
private:
diff --git a/app/src/timeline.cpp b/app/src/timeline.cpp
index af5bd22c1..bf7119fa4 100644
--- a/app/src/timeline.cpp
+++ b/app/src/timeline.cpp
@@ -173,6 +173,7 @@ void TimeLine::initUI()
QGridLayout* rightLayout = new QGridLayout();
rightLayout->addWidget(rightToolBar, 0, 0);
+ rightLayout->setAlignment(Qt::AlignLeft);
rightLayout->addWidget(mTracks, 1, 0);
rightLayout->setContentsMargins(0, 0, 0, 0);
rightLayout->setSpacing(0);
diff --git a/app/ui/addtransparencytopaperdialog.ui b/app/ui/addtransparencytopaperdialog.ui
new file mode 100644
index 000000000..a8420ab84
--- /dev/null
+++ b/app/ui/addtransparencytopaperdialog.ui
@@ -0,0 +1,370 @@
+
+
+ AddTransparencyToPaperDialog
+
+
+
+ 0
+ 0
+ 671
+ 375
+
+
+
+ Add transparency to Paper
+
+
+ -
+
+
-
+
+
-
+
+
-
+
+
-
+
+
+ Threshold
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 13
+ 19
+
+
+
+
+
+
+ -
+
+
-
+
+
+ 150
+
+
+ 245
+
+
+ 220
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Color pick values above treshold, will become transparent
+
+
+ 150
+
+
+ 245
+
+
+ 220
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ 0
+
+
-
+
+
+ Zoom
+
+
+
+ -
+
+
+ 1
+
+
+ 10
+
+
+ Qt::Horizontal
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
-
+
+
+ Trace Red
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/red.png
+
+
+
+
+
+ -
+
+
-
+
+
+ Trace Green
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/green.png
+
+
+
+
+
+ -
+
+
-
+
+
+ Trace Blue
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/blue.png
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Apply to:
+
+
+
+ -
+
+
+ Current Keyframe
+
+
+
+ -
+
+
+ All Keyframes on layer
+
+
+ true
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Test transparency
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 40
+
+
+
+
+ -
+
+
-
+
+
+ Close
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Apply
+
+
+ true
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+ (Previewing frame: )
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ 400
+ 300
+
+
+
+ QAbstractScrollArea::AdjustToContents
+
+
+ QGraphicsView::NoAnchor
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/ui/bitmapcoloringwidget.ui b/app/ui/bitmapcoloringwidget.ui
new file mode 100644
index 000000000..abe9e598d
--- /dev/null
+++ b/app/ui/bitmapcoloringwidget.ui
@@ -0,0 +1,851 @@
+
+
+ BitmapColoringWidget
+
+
+
+ 0
+ 0
+ 282
+ 485
+
+
+
+ Advanced Coloring
+
+
+ -
+
+
-
+
+
+ Methods:
+
+
+
+ -
+
+
+ Colorize on Same or Separate layer?
+
+
-
+
+ Choose method...
+
+
+ -
+
+ On the Same Layer
+
+
+ -
+
+ On a Separate Layer
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Info
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+
+
+
+ 0
+
+
+
+ Trace
+
+
+
-
+
+
+ Filters
+
+
+
+ 2
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
-
+
+
-
+
+
+ Trace Red
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/red.png
+
+
+
+
+
+ -
+
+
-
+
+
+ Trace Green
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/green.png
+
+
+
+
+
+ -
+
+
-
+
+
+ Trace Blue
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/blue.png
+
+
+
+
+
+
+
+
+ -
+
+
+ Repeat Process
+
+
+
+ 2
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
-
+
+
+ All keyframes
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ <html><head/><body><p>Color separation lines must be prepared for coloring.<br/>Color separation, made in Pencil2d, are not prepared.<br/></p></body></html>
+
+
+ Prepare lines + Trace...
+
+
+
+
+
+ -
+
+
-
+
+
+ Reset
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Trace...
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 19
+
+
+
+
+
+
+
+
+ Thin
+
+
+ -
+
+
+ Fill small areas?
+
+
+
+ 2
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
-
+
+
-
+
+
+ Size of area to fill (1-15)
+
+
+ px
+
+
+ 1
+
+
+ 15
+
+
+ 6
+
+
+
+ -
+
+
+ false
+
+
+ Fill areas
+
+
+
+ -
+
+
+ Fill small, unwanted areas in lines
+
+
+ Fill
+
+
+
+
+
+
+
+
+ -
+
+
-
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+ -
+
+
+ Filters
+
+
+
+ 2
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
-
+
+
-
+
+
+ true
+
+
+ Thin Red
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/red.png
+
+
+
+
+
+ -
+
+
-
+
+
+ true
+
+
+ Thin Green
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/green.png
+
+
+
+
+
+ -
+
+
-
+
+
+ true
+
+
+ Thin Blue
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/blue.png
+
+
+
+
+
+
+
+
+ -
+
+
+ Repeat Process
+
+
+
+ 2
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
-
+
+
+ All keyframes
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Thin...
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 59
+
+
+
+
+
+
+
+
+ Blend
+
+
+
+ 2
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+ -
+
+
+ Filters
+
+
+
+ 2
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
-
+
+
-
+
+
+ true
+
+
+ Blend Red
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/red.png
+
+
+
+
+
+ -
+
+
-
+
+
+ true
+
+
+ Blend Green
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/green.png
+
+
+
+
+
+ -
+
+
-
+
+
+ true
+
+
+ Blend Blue
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+
+
+
+ :/icons/blue.png
+
+
+
+
+
+
+
+
+ -
+
+
+ Repeat Process
+
+
+
+ 2
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
+ 4
+
+
-
+
+
+ All keyframes
+
+
+
+
+
+
+ -
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Blend and Finish...
+
+
+
+
+
+ -
+
+
+ Qt::Vertical
+
+
+
+ 20
+ 39
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/ui/mainwindow2.ui b/app/ui/mainwindow2.ui
index 50b108997..57752c313 100644
--- a/app/ui/mainwindow2.ui
+++ b/app/ui/mainwindow2.ui
@@ -107,6 +107,13 @@
+
@@ -121,7 +128,7 @@
-
+
@@ -1078,6 +1085,16 @@
Reset Rotation
+
+
+ Peg bar Alignment
+
+
+
+
+ Add Transparency to paper
+
+
Add Exposure
diff --git a/core_lib/src/graphics/bitmap/bitmapimage.cpp b/core_lib/src/graphics/bitmap/bitmapimage.cpp
index e2be3641e..f49f1b401 100644
--- a/core_lib/src/graphics/bitmap/bitmapimage.cpp
+++ b/core_lib/src/graphics/bitmap/bitmapimage.cpp
@@ -728,6 +728,546 @@ void BitmapImage::drawPath(QPainterPath path, QPen pen, QBrush brush,
modification();
}
+BitmapImage* BitmapImage::scanToTransparent(BitmapImage *img, bool redEnabled, bool greenEnabled, bool blueEnabled)
+{
+ Q_ASSERT(img != nullptr);
+
+ QRgb rgba = img->constScanLine(img->left(), img->top());
+ if (qAlpha(rgba) == 0)
+ return img;
+
+ for (int x = img->left(); x <= img->right(); x++)
+ {
+ for (int y = img->top(); y <= img->bottom(); y++)
+ {
+ rgba = img->constScanLine(x, y);
+
+ int grayValue = qGray(rgba);
+ int redValue = qRed(rgba);
+ int greenValue = qGreen(rgba);
+ int blueValue = qBlue(rgba);
+ int alphaValue = qAlpha(rgba);
+ if (alphaValue == 0)
+ break;
+ if (grayValue >= mThreshold)
+ { // IF Threshold or above
+ img->scanLine(x, y, transp);
+ }
+ else if(redValue > greenValue + COLORDIFF && redValue > blueValue + COLORDIFF && redValue > grayValue + GRAYSCALEDIFF)
+ { // IF Red line
+ if (redEnabled)
+ {
+ img->scanLine(x, y, redline);
+ }
+ else
+ {
+ img->scanLine(x, y, transp);
+ }
+ }
+ else if(greenValue > redValue + COLORDIFF && greenValue > blueValue + COLORDIFF && greenValue > grayValue + GRAYSCALEDIFF)
+ { // IF Green line
+ if (greenEnabled)
+ {
+ img->scanLine(x, y, greenline);
+ }
+ else
+ {
+ img->scanLine(x, y, transp);
+ }
+ }
+ else if(blueValue > redValue + COLORDIFF && blueValue > greenValue + COLORDIFF && blueValue > grayValue + GRAYSCALEDIFF)
+ { // IF Blue line
+ if (blueEnabled)
+ {
+ img->scanLine(x, y, blueline);
+ }
+ else
+ {
+ img->scanLine(x, y, transp);
+ }
+ }
+ else
+ { // okay, so it is in grayscale graduation area
+ if( grayValue >= mLowThreshold && grayValue < mThreshold)
+ {
+ qreal factor = qreal(mThreshold - grayValue) / qreal(mThreshold - mLowThreshold);
+ img->scanLine(x , y, qRgba(0, 0, 0, static_cast(mThreshold * factor)));
+ }
+ else
+ {
+ img->scanLine(x , y, blackline);
+ }
+ }
+ }
+ }
+ img->modification();
+ return img;
+}
+
+BitmapImage* BitmapImage::prepDrawing(BitmapImage* img, bool redEnabled, bool greenEnabled, bool blueEnabled)
+{
+ Q_ASSERT(img != nullptr);
+
+ for (int x = img->left(); x <= img->right(); x++)
+ {
+ for (int y = img->top(); y <= img->bottom(); y++)
+ {
+ QRgb rgba = img->constScanLine(x, y);
+ if (qAlpha(rgba) > 0)
+ {
+ int redValue = qRed(rgba);
+ int greenValue = qGreen(rgba);
+ int blueValue = qBlue(rgba);
+
+ if(redValue > greenValue + COLORDIFF && redValue > blueValue + COLORDIFF)
+ { // IF Red line
+ if (redEnabled)
+ {
+ img->scanLine(x, y, redline);
+ }
+ else
+ {
+ img->scanLine(x, y, transp);
+ }
+ }
+
+ else if(greenValue > redValue + COLORDIFF && greenValue > blueValue + COLORDIFF)
+ { // IF Green line
+ if (greenEnabled)
+ {
+ img->scanLine(x, y, greenline);
+ }
+ else
+ {
+ img->scanLine(x, y, transp);
+ }
+ }
+ else if(blueValue > redValue + COLORDIFF && blueValue > greenValue + COLORDIFF)
+ { // IF Blue line
+ if (blueEnabled)
+ {
+ img->scanLine(x, y, blueline);
+ }
+ else
+ {
+ img->scanLine(x, y, transp);
+ }
+ }
+ }
+
+ }
+ }
+ img->modification();
+ return img;
+}
+
+void BitmapImage::traceLine(BitmapImage* img, bool blackEnabled, bool redEnabled, bool greenEnabled, bool blueEnabled)
+{
+ Q_ASSERT(img != nullptr);
+
+ QRgb rgba;
+ for (int x = img->left(); x <= img->right(); x++)
+ {
+ for (int y = img->top(); y <= img->bottom(); y++)
+ {
+ rgba = img->constScanLine(x, y);
+
+ int redValue = qRed(rgba);
+ int greenValue = qGreen(rgba);
+ int blueValue = qBlue(rgba);
+ int alphaValue = qAlpha(rgba);
+ if (alphaValue > 0)
+ {
+ if(redValue > greenValue && redValue > blueValue)
+ {
+ if(redEnabled)
+ img->scanLine(x, y, redline);
+ else
+ img->scanLine(x, y, transp);
+ }
+ else if(blueValue > redValue && blueValue > greenValue)
+ {
+ if(blueEnabled)
+ img->scanLine(x, y, blueline);
+ else
+ img->scanLine(x, y, transp);
+ }
+ else if(greenValue > redValue && greenValue > blueValue)
+ {
+ if(greenEnabled)
+ img->scanLine(x, y, greenline);
+ else
+ img->scanLine(x, y, transp);
+ }
+ else
+ {
+ if (blackEnabled && alphaValue > TRANSP_THRESHOLD)
+ {
+ img->scanLine(x, y, blackline);
+ }
+ else if (blackEnabled)
+ img->scanLine(x, y, transp);
+ }
+ }
+ }
+ }
+ img->modification();
+}
+
+void BitmapImage::eraseRedGreenBlueLines(BitmapImage *img)
+{
+ Q_ASSERT(img != nullptr);
+
+ QRgb rgba;
+ for (int x = img->left(); x <= img->right(); x++)
+ {
+ for (int y = img->top(); y <= img->bottom(); y++)
+ {
+ rgba = img->constScanLine(x, y);
+ if (rgba == redline || rgba == greenline || rgba == blueline)
+ {
+ img->scanLine(x, y, transp);
+ }
+ }
+ }
+ img->modification();
+}
+
+void BitmapImage::fillSpotAreas(BitmapImage *img)
+{
+ Q_ASSERT(img != nullptr);
+
+ // fill areas size 'area' or less with appropriate color
+ QVector points;
+ points.clear();
+ QRgb active;
+ QRgb previous = blackline;
+ for (int x = img->left() + 1; x < img->right(); x++)
+ {
+ for (int y = img->top() + 1; y < img->bottom(); y++)
+ {
+ active = img->constScanLine(x, y);
+ if (qAlpha(active) == 0)
+ {
+ points.append(QPoint(x, y));
+ int areaSize = fillWithColor(QPoint(x, y), transp, rosa, img);
+ if (areaSize <= mSpotArea)
+ { // replace rosa with last color
+ fillWithColor(points.last(), rosa, previous, img);
+ points.removeLast();
+ }
+ }
+ previous = active;
+ }
+ }
+ // replace rosa with trans
+ while (!points.isEmpty()) {
+ fillWithColor(points[0], rosa, transp, img);
+ points.removeFirst();
+ }
+ img->modification();
+}
+
+void BitmapImage::toThinLine(BitmapImage * img, bool black, bool red, bool green, bool blue)
+{
+ Q_ASSERT(img != nullptr);
+
+ bool N = true, E = true, S = true, W = true, pixelRemoved, search;
+
+ QList colors;
+ if (black) colors.append(blackline);
+ if (red) colors.append(redline);
+ if (green) colors.append(greenline);
+ if (blue) colors.append(blueline);
+
+ QRgb pixColor;
+
+ while (N || E || S || W)
+ {
+ if (N) // from NORTH
+ {
+ // set 'pixelRemoved' to false. 'pixelRemoved' is set to true whenever a pixel is removed
+ pixelRemoved = false;
+ // 'search' is true while pixels are transparent
+ // when blackline pixel is found, 'search' is set to false until next transparent pixel
+ search = true;
+ for (int x = img->left(); x < img->right(); x++)
+ {
+ for (int y = img->top(); y < img->bottom(); y++)
+ {
+ if (search)
+ {
+ pixColor = img->constScanLine(x,y);
+ if (qAlpha(pixColor) > 0)
+ {
+ search = false;
+ if (qAlpha(img->constScanLine(x,y+1)) > 0 && colors.contains(pixColor))
+ {
+ if ((qAlpha(img->constScanLine(x-1,y-1)) == 0 && qAlpha(img->constScanLine(x+1,y-1)) == 0) &&
+ (qAlpha(img->constScanLine(x+1, y)) > 0 || qAlpha(img->constScanLine(x-1, y)) >0 ||
+ qAlpha(img->constScanLine(x+1, y+1)) > 0 || qAlpha(img->constScanLine(x-1, y+1)) >0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ else if ((qAlpha(img->constScanLine(x-1,y-1)) > 0 && qAlpha(img->constScanLine(x+1,y-1)) > 0) &&
+ (qAlpha(img->constScanLine(x+1,y)) > 0 && qAlpha(img->constScanLine(x-1,y)) > 0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ else if ((qAlpha(img->constScanLine(x-1,y-1)) > 0 && qAlpha(img->constScanLine(x-1,y)) > 0) ||
+ (qAlpha(img->constScanLine(x+1,y-1)) > 0 && qAlpha(img->constScanLine(x+1,y)) > 0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (qAlpha(img->constScanLine(x,y)) == 0)
+ search = true;
+ }
+ }
+ }
+ N = pixelRemoved; // if none is removed, N = false
+ }
+ if (E) // from EAST
+ {
+ pixelRemoved = false;
+ search = true;
+ for (int y = img->top(); y < img->bottom(); y++)
+ {
+ for (int x = img->right(); x > img->left(); x--)
+ {
+ if (search)
+ {
+ pixColor = img->constScanLine(x,y);
+ if (qAlpha(pixColor) > 0)
+ {
+ search = false;
+ if (qAlpha(img->constScanLine(x-1,y)) > 0 && colors.contains(pixColor))
+ {
+ if ((qAlpha(img->constScanLine(x+1,y-1)) == 0 && qAlpha(img->constScanLine(x+1,y+1)) == 0) &&
+ (qAlpha(img->constScanLine(x,y-1)) > 0 || qAlpha(img->constScanLine(x,y+1)) >0 ||
+ qAlpha(img->constScanLine(x-1,y-1)) > 0 || qAlpha(img->constScanLine(x-1,y+1)) >0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ else if ((qAlpha(img->constScanLine(x+1,y+1)) > 0 && qAlpha(img->constScanLine(x+1,y-1)) > 0) &&
+ (qAlpha(img->constScanLine(x,y+1)) > 0 && qAlpha(img->constScanLine(x,y-1)) > 0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ else if ((qAlpha(img->constScanLine(x+1,y+1)) > 0 && qAlpha(img->constScanLine(x,y+1)) > 0) ||
+ (qAlpha(img->constScanLine(x+1,y-1)) > 0 && qAlpha(img->constScanLine(x,y-1)) > 0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (qAlpha(img->constScanLine(x,y)) == 0)
+ search = true;
+ }
+ }
+ }
+ E = pixelRemoved; // if none is removed, E = false
+ }
+ if (S) // from SOUTH
+ {
+ pixelRemoved = false;
+ search = true;
+ for (int x = img->left(); x < img->right(); x++)
+ {
+ for (int y = img->bottom(); y > img->top(); y--)
+ {
+ if (search)
+ {
+ pixColor = img->constScanLine(x,y);
+ if (qAlpha(pixColor) > 0)
+ {
+ search = false;
+ if (qAlpha(img->constScanLine(x,y-1)) > 0 && colors.contains(pixColor))
+ {
+ if ((qAlpha(img->constScanLine(x-1,y+1)) == 0 && qAlpha(img->constScanLine(x+1,y+1)) == 0) &&
+ (qAlpha(img->constScanLine(x-1, y)) > 0 || qAlpha(img->constScanLine(x+1, y)) >0 ||
+ qAlpha(img->constScanLine(x-1, y-1)) > 0 || qAlpha(img->constScanLine(x+1, y-1)) >0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ else if ((qAlpha(img->constScanLine(x-1,y+1)) > 0 && qAlpha(img->constScanLine(x+1,y+1)) > 0) &&
+ (qAlpha(img->constScanLine(x+1,y)) > 0 && qAlpha(img->constScanLine(x-1,y)) > 0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ else if ((qAlpha(img->constScanLine(x-1,y+1)) > 0 && qAlpha(img->constScanLine(x-1,y)) > 0) ||
+ (qAlpha(img->constScanLine(x+1,y+1)) > 0 && qAlpha(img->constScanLine(x+1,y)) > 0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (qAlpha(img->constScanLine(x,y)) == 0)
+ search = true;
+ }
+ }
+ }
+ S = pixelRemoved; // if none is removed, S = false
+ }
+ if (W) // from WEST
+ {
+ pixelRemoved = false;
+ search = true;
+ for (int y = img->top(); y <= img->bottom(); y++)
+ {
+ for (int x = img->left(); x < img->right(); x++)
+ {
+ if (search)
+ {
+ pixColor = img->constScanLine(x,y);
+ if (qAlpha(pixColor) > 0)
+ {
+ search = false;
+ if (qAlpha(img->constScanLine(x+1,y)) > 0 && colors.contains(pixColor))
+ {
+ if ((qAlpha(img->constScanLine(x-1,y-1)) == 0 && qAlpha(img->constScanLine(x-1,y+1)) == 0) &&
+ (qAlpha(img->constScanLine(x,y-1)) > 0 || qAlpha(img->constScanLine(x,y+1)) >0 ||
+ qAlpha(img->constScanLine(x+1,y-1)) > 0 || qAlpha(img->constScanLine(x+1,y+1)) >0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ else if ((qAlpha(img->constScanLine(x-1,y+1)) > 0 && qAlpha(img->constScanLine(x-1,y-1)) > 0) &&
+ (qAlpha(img->constScanLine(x,y+1)) > 0 && qAlpha(img->constScanLine(x,y-1)) > 0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ else if ((qAlpha(img->constScanLine(x-1,y+1)) > 0 && qAlpha(img->constScanLine(x,y+1)) > 0) ||
+ (qAlpha(img->constScanLine(x-1,y-1)) > 0 && qAlpha(img->constScanLine(x,y-1)) > 0))
+ {
+ img->scanLine(x, y, transp);
+ pixelRemoved = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (qAlpha(img->constScanLine(x,y)) == 0)
+ search = true;
+ }
+ }
+ }
+ W = pixelRemoved; // if none is removed, W = false
+ }
+ }
+ img->modification();
+}
+
+void BitmapImage::blendLines(BitmapImage *img, bool black, bool red, bool green, bool blue)
+{
+ Q_ASSERT(img != nullptr);
+
+ int r, g, b, a; //red, green, blue, alpha
+ QList points; // QPoints to add in calculation
+ QList rgblist; // QRgb's that should be excluded
+ if (black) rgblist << blackline;
+ if (red) rgblist << redline;
+ if (green) rgblist << greenline;
+ if (blue) rgblist << blueline;
+ for (int x = img->left(); x <= img->right(); x++)
+ {
+ for (int y = img->top(); y <= img->bottom(); y++)
+ {
+ points.clear();
+ r=0; g=0; b=0; a=0;
+ if (rgblist.contains(img->constScanLine(x,y)))
+ {
+ if (!rgblist.contains(img->pixel(x-1, y-1))) points.append(QPoint(x-1, y-1));
+ if (!rgblist.contains(img->pixel(x-1, y ))) points.append(QPoint(x-1, y ));
+ if (!rgblist.contains(img->pixel(x-1, y+1))) points.append(QPoint(x-1, y+1));
+ if (!rgblist.contains(img->pixel(x , y-1))) points.append(QPoint(x , y-1));
+ if (!rgblist.contains(img->pixel(x , y+1))) points.append(QPoint(x , y+1));
+ if (!rgblist.contains(img->pixel(x+1, y-1))) points.append(QPoint(x+1, y-1));
+ if (!rgblist.contains(img->pixel(x+1, y ))) points.append(QPoint(x+1, y ));
+ if (!rgblist.contains(img->pixel(x+1, y+1))) points.append(QPoint(x+1, y+1));
+ for (int i = 0; i < points.size(); i++)
+ {
+ r += static_cast(qPow(qRed(img->pixel(points.at(i))), 2));
+ g += static_cast(qPow(qGreen(img->pixel(points.at(i))), 2));
+ b += static_cast(qPow(qBlue(img->pixel(points.at(i))), 2));
+ a += static_cast(qPow(qAlpha(img->pixel(points.at(i))), 2));
+ }
+ r = static_cast(sqrt(r/points.size()));
+ g = static_cast(sqrt(g/points.size()));
+ b = static_cast(sqrt(b/points.size()));
+ a = static_cast(sqrt(a/points.size()));
+ img->scanLine(x, y, qRgba(r, g, b, a));
+ }
+ }
+ }
+ img->modification();
+}
+
+int BitmapImage::fillWithColor(QPoint point, QRgb orgColor, QRgb newColor, BitmapImage *img)
+{
+ Q_ASSERT(img != nullptr);
+
+ QList fillList;
+ fillList.clear();
+ // fill first pixel
+ img->scanLine(point.x(), point.y(), newColor);
+ int pixels = 1;
+ fillList.append(point);
+
+ QRect rect = img->bounds();
+ while (!fillList.isEmpty())
+ {
+ QPoint tmp = fillList.takeFirst();
+ if (rect.contains(QPoint(tmp.x() + 1, tmp.y())) && img->pixel(QPoint(tmp.x() + 1, tmp.y())) == orgColor)
+ {
+ img->scanLine(tmp.x() + 1, tmp.y(), newColor);
+ fillList.append(QPoint(tmp.x() + 1, tmp.y()));
+ pixels++;
+ }
+ if (rect.contains(QPoint(tmp.x(), tmp.y() + 1)) && img->pixel(QPoint(tmp.x(), tmp.y() + 1)) == orgColor)
+ {
+ img->scanLine(tmp.x(), tmp.y() + 1, newColor);
+ fillList.append(QPoint(tmp.x(), tmp.y() + 1));
+ pixels++;
+ }
+ if (rect.contains(QPoint(tmp.x() - 1, tmp.y())) && img->pixel(QPoint(tmp.x() - 1, tmp.y())) == orgColor)
+ {
+ img->scanLine(tmp.x() - 1, tmp.y(), newColor);
+ fillList.append(QPoint(tmp.x() - 1, tmp.y()));
+ pixels++;
+ }
+ if (rect.contains(QPoint(tmp.x(), tmp.y() - 1)) && img->pixel(QPoint(tmp.x(), tmp.y() - 1)) == orgColor)
+ {
+ img->scanLine(tmp.x(), tmp.y() - 1, newColor);
+ fillList.append(QPoint(tmp.x(), tmp.y() - 1));
+ pixels++;
+ }
+ }
+ img->modification();
+ return pixels;
+}
+
Status BitmapImage::writeFile(const QString& filename)
{
if (!mImage.isNull())
diff --git a/core_lib/src/graphics/bitmap/bitmapimage.h b/core_lib/src/graphics/bitmap/bitmapimage.h
index ead411bfb..7c96191f2 100644
--- a/core_lib/src/graphics/bitmap/bitmapimage.h
+++ b/core_lib/src/graphics/bitmap/bitmapimage.h
@@ -24,10 +24,16 @@ GNU General Public License for more details.
class TiledBuffer;
-
class BitmapImage : public KeyFrame
{
public:
+ const QRgb transp = qRgba(0, 0, 0, 0);
+ const QRgb rosa = qRgba(255,230,230,255);
+ const QRgb blackline = qRgba(1, 1, 1, 255);
+ const QRgb redline = qRgba(254,0,0,255);
+ const QRgb greenline = qRgba(0,254,0,255);
+ const QRgb blueline = qRgba(0,0,254,255);
+
BitmapImage();
BitmapImage(const BitmapImage&);
BitmapImage(const QRect &rectangle, const QColor& color);
@@ -107,6 +113,16 @@ class BitmapImage : public KeyFrame
QRect& bounds() { autoCrop(); return mBounds; }
+ // coloring methods
+ BitmapImage* scanToTransparent(BitmapImage* bitmapimage, bool redEnabled, bool greenEnabled, bool blueEnabled);
+ BitmapImage* prepDrawing(BitmapImage* img, bool redEnabled, bool greenEnabled, bool blueEnabled);
+ void traceLine(BitmapImage* bitmapimage, bool blackEnabled, bool redEnabled, bool greenEnabled, bool blueEnabled);
+ void eraseRedGreenBlueLines(BitmapImage* img);
+ void fillSpotAreas(BitmapImage* img);
+ void toThinLine(BitmapImage* colorImage, bool black, bool red, bool green, bool blue);
+ void blendLines(BitmapImage* bitmapimage, bool black, bool red, bool green, bool blue);
+ int fillWithColor(QPoint point, QRgb orgColor, QRgb newColor, BitmapImage* img);
+
/** Determines if the BitmapImage is minimally bounded.
*
* A BitmapImage is minimally bounded if all edges contain
@@ -124,6 +140,9 @@ class BitmapImage : public KeyFrame
Status writeFile(const QString& filename);
+public slots:
+ void setThreshold(int threshold) { mThreshold = threshold; }
+ void setSpotArea(int spotArea) { mSpotArea = spotArea; }
/** Compare colors for the purposes of flood filling
*
* Calculates the Eulcidian difference of the RGB channels
@@ -181,6 +200,14 @@ class BitmapImage : public KeyFrame
/** @see isMinimallyBounded() */
bool mMinBound = true;
bool mEnableAutoCrop = false;
+
+ int mSpotArea = 6;
+ int mThreshold = 200;
+ const int mLowThreshold = 30; // threshold for images to be given transparency
+ const int COLORDIFF = 5; // difference in color values to decide color
+ const int GRAYSCALEDIFF = 15; // difference in grasycale values to decide color
+ const int TRANSP_THRESHOLD = 60;// threshold when tracing black for two layer coloring
+
qreal mOpacity = 1.0;
};
diff --git a/core_lib/src/interface/editor.cpp b/core_lib/src/interface/editor.cpp
index 7c4d5bc60..6bf6c2d05 100644
--- a/core_lib/src/interface/editor.cpp
+++ b/core_lib/src/interface/editor.cpp
@@ -505,7 +505,7 @@ void Editor::clearUndoStack()
void Editor::updateAutoSaveCounter()
{
- if (mIsAutosave == false)
+ if (mIsAutosave == false || mIsDoingRepeatInColoring)
return;
mAutosaveCounter++;
@@ -1140,6 +1140,7 @@ void Editor::scrubTo(int frame)
emit updateTimeLineCached(); // needs to update the timeline to update onion skin positions
}
mObject->updateActiveFrames(frame);
+ emit scrubbedTo(frame);
}
void Editor::scrubForward()
diff --git a/core_lib/src/interface/editor.h b/core_lib/src/interface/editor.h
index eef05b266..48b2d8cde 100644
--- a/core_lib/src/interface/editor.h
+++ b/core_lib/src/interface/editor.h
@@ -154,14 +154,20 @@ class Editor : public QObject
void objectLoaded();
void fpsChanged(int fps);
+ void scrubbedTo(int frame);
void needSave();
void needDisplayInfo(const QString& title, const QString& body);
void needDisplayInfoNoTitle(const QString& body);
+
+ // Something was updated on the current frame, notify receivers
+ void currentFrameUpdated();
+
void canCopyChanged(bool enabled);
void canPasteChanged(bool enabled);
+
public: //slots
/** Will call update() and update the canvas
@@ -228,6 +234,10 @@ class Editor : public QObject
void dontAskAutoSave(bool b) { mAutosaveNeverAskAgain = b; }
bool autoSaveNeverAskAgain() const { return mAutosaveNeverAskAgain; }
void resetAutoSaveCounter();
+ int getAutoSaveCounter() { return mAutosaveCounter; }
+ void setAutoSaveCounter(int count) { mAutosaveCounter = count; }
+ void setIsDoingRepeatColoring(bool b) { mIsDoingRepeatInColoring = b; }
+
private:
Status importBitmapImage(const QString&, int space = 0);
@@ -266,6 +276,7 @@ class Editor : public QObject
int mAutosaveNumber = 12;
int mAutosaveCounter = 0;
bool mAutosaveNeverAskAgain = false;
+ bool mIsDoingRepeatInColoring = false;
void makeConnections();
KeyFrame* addKeyFrame(int layerNumber, int frameNumber);
diff --git a/core_lib/src/interface/scribblearea.h b/core_lib/src/interface/scribblearea.h
index c31ffca13..3761db879 100644
--- a/core_lib/src/interface/scribblearea.h
+++ b/core_lib/src/interface/scribblearea.h
@@ -30,7 +30,6 @@ GNU General Public License for more details.
#include
#include "movemode.h"
-#include "log.h"
#include "pencildef.h"
#include "bitmapimage.h"
#include "canvaspainter.h"
diff --git a/core_lib/src/managers/layermanager.cpp b/core_lib/src/managers/layermanager.cpp
index 927988f4b..3ddd378cc 100644
--- a/core_lib/src/managers/layermanager.cpp
+++ b/core_lib/src/managers/layermanager.cpp
@@ -324,6 +324,16 @@ Status LayerManager::deleteLayer(int index)
}
Q_ASSERT(object()->getLayerCount() >= 2);
+ // resets layer flag, if color layer is deleted
+ if (layer->getIsColorLayer())
+ {
+ QString s = layer->name();
+ s.chop(2);
+ Layer* artLayer = findLayerByName(s);
+ if (artLayer != nullptr)
+ artLayer->setHasColorLayer(false);
+ }
+
// current layer is the last layer && we are deleting it
if (index == object()->getLayerCount() - 1 &&
index == currentLayerIndex())
diff --git a/core_lib/src/structure/layer.h b/core_lib/src/structure/layer.h
index bbb857cf8..3dbce2eed 100644
--- a/core_lib/src/structure/layer.h
+++ b/core_lib/src/structure/layer.h
@@ -66,6 +66,11 @@ class Layer : public QObject
bool visible() const { return mVisible; }
void setVisible(bool b) { mVisible = b; }
+ void setHasColorLayer(bool b) { mHasColorLayer = b; }
+ bool getHasColorLayer() { return mHasColorLayer; }
+ void setIsColorLayer(bool b) { mIsColorLayer = b; }
+ bool getIsColorLayer() { return mIsColorLayer; }
+
/** Get selected keyframe positions sorted by position */
QList selectedKeyFramesPositions() const { return mSelectedFrames_byPosition; }
@@ -180,6 +185,8 @@ class Layer : public QObject
int mId = 0;
bool mVisible = true;
QString mName;
+ bool mHasColorLayer = false;
+ bool mIsColorLayer = false;
std::map> mKeyFrames;