759 lines
26 KiB
C++
759 lines
26 KiB
C++
#include <QtWidgets>
|
|
#ifndef QT_NO_PRINTER
|
|
#include <QPrinter>
|
|
#include <QPrintDialog>
|
|
#endif
|
|
|
|
#include "rotocanvas.h"
|
|
|
|
//! [0]
|
|
RotoCanvas::RotoCanvas(QWidget *parent)
|
|
: QWidget(parent)
|
|
{
|
|
setAttribute(Qt::WA_StaticContents);
|
|
//isModified = false;
|
|
isToolActive = false;
|
|
selectedLayerIndex = -1;
|
|
layerCount = 1; // always have at least one layer (layer 0, the nondestructive edits to the background)
|
|
//frameNumber = -1;
|
|
//minDigitCount = 4;
|
|
cacheMaxMB = 800.0; //NOTE: 1920*1080*4 = 8294400 = ~7.91 MB, so 800MB is around 100 HD frames
|
|
brushRadius = 5.0;
|
|
brushHardRadius = 0.0;
|
|
brushOpacity = 1.0;
|
|
//formatString = "";
|
|
brushColor = QColor(35,97,20,255); //Qt::green;
|
|
//Pigment Ajans green screen color: 0,203,27
|
|
//Expert Multimedia Green Screen Color (LED lighting, muslin green screen): 35,97,20
|
|
checkerDarkColor = QColor(128,128,128);
|
|
checkerLightColor = QColor(192,192,192);
|
|
outputSize.setWidth(0);
|
|
outputSize.setHeight(0);
|
|
loadedFI=nullptr;
|
|
selectedLayerIndex=0;
|
|
}
|
|
|
|
QString RotoCanvas::getSeqName(QString framePath)
|
|
{//static
|
|
//QString result;
|
|
QFileInfo frameFI(framePath);
|
|
return RotoCanvas::getSeqName(frameFI);
|
|
}
|
|
|
|
QString RotoCanvas::getSeqName(QFileInfo frameFI)
|
|
{
|
|
QString result;
|
|
QString baseName=frameFI.completeBaseName(); //baseName would get file from file.tar.gz or from file.myvideo0000.png, where completeBaseName gets file.tar or file.myvideo0000
|
|
result=baseName;
|
|
// now remove any trailing digits to get the "sequence name":
|
|
int newLength=result.length();
|
|
while ( newLength>0 && baseName.at(newLength-1).isDigit() ) {
|
|
newLength--;
|
|
}
|
|
if (newLength!=result.length()) result = result.left(newLength);
|
|
return result;
|
|
}
|
|
|
|
QString RotoCanvas::getSeqName()
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getSeqName(*this->loadedFI);
|
|
else return "";
|
|
}
|
|
|
|
QString RotoCanvas::getSeqFormatString(QString framePath)
|
|
{
|
|
//QString result;
|
|
QFileInfo frameFI(framePath);
|
|
return RotoCanvas::getSeqFormatString(frameFI);
|
|
}
|
|
|
|
QString RotoCanvas::getSeqFormatString(QFileInfo frameFI)
|
|
{
|
|
QString result=frameFI.suffix();
|
|
return result;
|
|
}
|
|
|
|
QString RotoCanvas::getSeqFormatString()
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getSeqFormatString(*this->loadedFI);
|
|
else return "";
|
|
}
|
|
|
|
QString RotoCanvas::getFolderPath(QString framePath)
|
|
{
|
|
QString result;
|
|
QFileInfo frameFI(framePath);
|
|
return RotoCanvas::getFolderPath(frameFI);
|
|
}
|
|
|
|
QString RotoCanvas::getFolderPath(QFileInfo frameFI)
|
|
{
|
|
QString result=frameFI.absoluteDir().path();
|
|
return result;
|
|
}
|
|
|
|
QString RotoCanvas::getFolderPath()
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getFolderPath(*this->loadedFI);
|
|
else return "";
|
|
}
|
|
|
|
QString RotoCanvas::getSeqPaddedFrameNumber(QString framePath)
|
|
{
|
|
//QString result;
|
|
QFileInfo frameFI(framePath);
|
|
return RotoCanvas::getSeqPaddedFrameNumber(frameFI);
|
|
}
|
|
|
|
QString RotoCanvas::getSeqPaddedFrameNumber(QFileInfo frameFI)
|
|
{
|
|
QString baseName=frameFI.completeBaseName();
|
|
int digitCount=RotoCanvas::getSeqDigitCount(frameFI);
|
|
//qInfo()<<"getSeqPaddedFrameNumber digitCount:"<<digitCount;
|
|
QString result=baseName.right(digitCount);
|
|
return result;
|
|
}
|
|
|
|
QString RotoCanvas::getSeqPaddedFrameNumber()
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getSeqPaddedFrameNumber(*this->loadedFI);
|
|
else return "";
|
|
}
|
|
|
|
QString RotoCanvas::getLayersFolderPath(QString framePath, int frameNumber, bool createEnable)
|
|
{
|
|
//QString result;
|
|
QFileInfo frameFI(framePath);
|
|
return RotoCanvas::getLayersFolderPath(frameFI, frameNumber, createEnable);
|
|
}
|
|
|
|
QString RotoCanvas::getLayersFolderPath(QFileInfo frameFI, int frameNumber, bool createEnable)
|
|
{
|
|
//such as <sequenceName>/frames/<frameNumber>/layers
|
|
QString seqName=RotoCanvas::getSeqName(frameFI);
|
|
//qInfo() << "seqName:" << seqName; //see also qInfo,qDebug,qWarning,qCritical,qFatal
|
|
QString seqPath=frameFI.dir().filePath(seqName);
|
|
QDir seqDir(seqPath);
|
|
if (createEnable) {
|
|
if (!seqDir.exists()) frameFI.dir().mkdir(seqName);
|
|
}
|
|
QString framesPath=seqDir.filePath("frames");
|
|
//qInfo()<<"framesPath:"<<framesPath;
|
|
QDir framesDir(framesPath);
|
|
if (createEnable) {
|
|
if (!framesDir.exists()) seqDir.mkdir("frames");
|
|
}
|
|
QString thisFramePath=framesDir.filePath(QString::number(frameNumber));
|
|
//qInfo()<<"thisFramePath:"<<thisFramePath;
|
|
QDir thisFrameDir=QDir(thisFramePath);
|
|
if (createEnable) {
|
|
if (!thisFrameDir.exists()) framesDir.mkdir(QString::number(frameNumber));
|
|
}
|
|
QString layersPath=QDir(thisFramePath).filePath("layers");
|
|
//qInfo()<<"layersPath:"<<layersPath;
|
|
QDir layersDir=QDir(layersPath);
|
|
if (createEnable) {
|
|
if (!layersDir.exists()) thisFrameDir.mkdir("layers");
|
|
}
|
|
return layersPath;
|
|
}
|
|
|
|
QString RotoCanvas::getLayersFolderPath(int frameNumber, bool createEnable)
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getLayersFolderPath(*this->loadedFI, frameNumber, createEnable);
|
|
else return "";
|
|
}
|
|
|
|
QString RotoCanvas::getLayerImagePathMostRecent(QString framePath, int frameNumber, int layerNumber, int* returnFrameNumber)
|
|
{
|
|
//QString result;
|
|
QFileInfo frameFI(framePath);
|
|
return RotoCanvas::getLayerImagePathMostRecent(frameFI, frameNumber, layerNumber, returnFrameNumber);
|
|
}
|
|
|
|
QString RotoCanvas::getLayerImagePathMostRecent(QFileInfo frameFI, int frameNumber, int layerNumber, int* returnFrameNumber)
|
|
{
|
|
QString result="";
|
|
QString layersPath=RotoCanvas::getLayersFolderPath(frameFI,frameNumber,false);
|
|
QDir layersDir(layersPath);
|
|
QString thisLayerImagePath=layersDir.filePath(QString::number(layerNumber)+"."+RotoCanvas::getSeqFormatString(frameFI));
|
|
QFileInfo thisLayerImageFI(thisLayerImagePath);
|
|
int thisFrameNumber=frameNumber;
|
|
while (thisFrameNumber>=0 && !thisLayerImageFI.exists()) {
|
|
layersPath=RotoCanvas::getLayersFolderPath(frameFI,frameNumber,false);
|
|
layersDir=QDir(layersPath);
|
|
thisLayerImagePath=layersDir.filePath(QString::number(layerNumber)+"."+RotoCanvas::getSeqFormatString(frameFI));
|
|
thisLayerImageFI=QFileInfo(thisLayerImagePath);
|
|
thisFrameNumber--;
|
|
}
|
|
thisFrameNumber++; // go back to last used value
|
|
if (thisLayerImageFI.exists()) {
|
|
result=thisLayerImageFI.path();
|
|
if (returnFrameNumber!=nullptr) *returnFrameNumber=thisFrameNumber;
|
|
}
|
|
else {
|
|
if (returnFrameNumber!=nullptr) *returnFrameNumber=-1;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
QString RotoCanvas::getLayerImagePathMostRecent(int frameNumber, int layerNumber, int* returnFrameNumber)
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getLayerImagePathMostRecent(*this->loadedFI, frameNumber, layerNumber, returnFrameNumber);
|
|
else return "";
|
|
}
|
|
|
|
QString RotoCanvas::getFrameName(QString framePath, int frameNumber)
|
|
{
|
|
QFileInfo frameFI(framePath);
|
|
return RotoCanvas::getFrameName(frameFI, frameNumber);
|
|
}
|
|
|
|
QString RotoCanvas::getFrameName(QFileInfo frameFI, int frameNumber)
|
|
{
|
|
return RotoCanvas::getSeqName(frameFI) + RotoCanvas::getZeroPadded(frameNumber, RotoCanvas::getSeqDigitCount(frameFI)) + "." + frameFI.suffix();
|
|
}
|
|
|
|
QString RotoCanvas::getFrameName(int frameNumber)
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getFrameName(*this->loadedFI, frameNumber);
|
|
else return "";
|
|
}
|
|
|
|
QString RotoCanvas::getFramePath(QString framePath, int frameNumber)
|
|
{
|
|
QFileInfo frameFI(framePath);
|
|
return RotoCanvas::getFramePath(frameFI, frameNumber);
|
|
}
|
|
|
|
QString RotoCanvas::getFramePath(QFileInfo frameFI, int frameNumber)
|
|
{
|
|
return frameFI.dir().filePath(RotoCanvas::getFrameName(frameFI, frameNumber));
|
|
}
|
|
|
|
QString RotoCanvas::getFramePath(int frameNumber)
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getFramePath(*this->loadedFI, frameNumber);
|
|
else return "";
|
|
}
|
|
|
|
int RotoCanvas::getSeqFrameNumber(QString framePath)
|
|
{
|
|
int result=-1;
|
|
QString resultString=RotoCanvas::getSeqPaddedFrameNumber(framePath);
|
|
while (resultString.length()>0 && resultString.left(1)=="0") {
|
|
resultString=resultString.right(resultString.length()-1);
|
|
}
|
|
bool ok=false;
|
|
result = resultString.toInt(&ok);
|
|
if (!ok) result=-1;
|
|
return result;
|
|
}
|
|
|
|
int RotoCanvas::getSeqFrameNumber(QFileInfo frameFI)
|
|
{
|
|
int result=-1;
|
|
QString resultString=RotoCanvas::getSeqPaddedFrameNumber(frameFI);
|
|
if (resultString.length()>0) {
|
|
int newLength=resultString.length();
|
|
int index=0;
|
|
while (index<resultString.length() && resultString.at(index)=='0') {
|
|
newLength--;
|
|
index++;
|
|
}
|
|
if (newLength<1) newLength=1;
|
|
resultString=resultString.right(newLength);
|
|
}
|
|
bool ok=false;
|
|
//qInfo()<<"getSeqFrameNumber resultString: "<<resultString;
|
|
result = resultString.toInt(&ok);
|
|
if (!ok) result=-1;
|
|
return result;
|
|
}
|
|
|
|
int RotoCanvas::getSeqFrameNumber()
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getSeqFrameNumber(*this->loadedFI);
|
|
else return -1;
|
|
}
|
|
|
|
int RotoCanvas::getSeqDigitCount(QString framePath)
|
|
{
|
|
QString result;
|
|
QFileInfo frameFI(framePath);
|
|
return RotoCanvas::getSeqDigitCount(frameFI);
|
|
}
|
|
|
|
int RotoCanvas::getSeqDigitCount(QFileInfo frameFI)
|
|
{
|
|
int result=-1;
|
|
QString baseName=frameFI.completeBaseName();
|
|
int digitCount=0;
|
|
int index=baseName.length()-1;
|
|
while (index>=0) {
|
|
if (baseName.at(index).isDigit()) {
|
|
index--;
|
|
digitCount++;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
if (digitCount>0) result=digitCount; // result=baseName.left(baseName.length()-digitCount).toInt();
|
|
return result;
|
|
}
|
|
|
|
int RotoCanvas::getSeqDigitCount()
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getSeqDigitCount(*this->loadedFI);
|
|
else return -1;
|
|
}
|
|
|
|
QString RotoCanvas::getFramePath(QString folderPath, QString seqName, int frameNumber, int minDigitCount, QString format)
|
|
{
|
|
QString fileName=seqName+RotoCanvas::getZeroPadded(frameNumber,minDigitCount)+"."+format;
|
|
return QDir::cleanPath(folderPath+QDir::separator()+fileName);
|
|
}
|
|
|
|
//QString RotoCanvas::getFramePath(int frameNumber)
|
|
//{
|
|
// if (this->loadedFI!=nullptr) return RotoCanvas::getFramePath(this->loadedFI->dir().path(), getSeqName(),frameNumber,getSeqDigitCount(),getSeqFormatString());
|
|
// else return "";
|
|
//}
|
|
|
|
QString RotoCanvas::getZeroPadded(int frameNumber, int minDigitCount)
|
|
{
|
|
QString result=QString::number(frameNumber);
|
|
QString zeroString=QString::number(0);
|
|
while (result.length()<minDigitCount) {
|
|
result=zeroString+result;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void RotoCanvas::drawAlphaPix(QImage *destImage, int x, int y, QColor sourceColor, double this_opacity)
|
|
{
|
|
QColor thisColor;
|
|
if (destImage!=nullptr) {
|
|
QColor destColor = destImage->pixelColor(x,y);
|
|
//do alpha formula (byte)((source-dest)*alpha/255.0f+dest+.5f)
|
|
int r = (int)((sourceColor.red()-destColor.red())*this_opacity+destColor.red()+.5); //+.5 for rounding
|
|
int g = (int)((sourceColor.green()-destColor.green())*this_opacity+destColor.green()+.5); //+.5 for rounding
|
|
int b = (int)((sourceColor.blue()-destColor.blue())*this_opacity+destColor.blue()+.5); //+.5 for rounding
|
|
int a = (int)((sourceColor.alpha()-destColor.alpha())*this_opacity+destColor.alpha()+.5); //+.5 for rounding
|
|
thisColor.setRed(r);
|
|
thisColor.setGreen(g);
|
|
thisColor.setBlue(b);
|
|
thisColor.setAlpha(a);
|
|
destImage->setPixelColor(x, y, thisColor);
|
|
//painter.setOpacity(this_opacity);
|
|
//painter.drawPoint(x,y);
|
|
}
|
|
}
|
|
|
|
bool RotoCanvas::getIsModified()
|
|
{
|
|
bool result=false;
|
|
for (int i=0; i<layerPtrs.length(); i++) {
|
|
if (layerPtrs[i]!=nullptr && layerPtrs[i]->isModified) {
|
|
result=true;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
//! [0]
|
|
|
|
//! [1]
|
|
bool RotoCanvas::openImage(const QString &fileName)
|
|
//! [1] //! [2]
|
|
{
|
|
qInfo()<<"RotoCanvas::openImage...";
|
|
QImage loadedImage;
|
|
if (!loadedImage.load(fileName))
|
|
return false;
|
|
this->outputSize = loadedImage.size();
|
|
if (this->loadedFI!=nullptr) {
|
|
delete this->loadedFI;
|
|
this->loadedFI=nullptr;
|
|
}
|
|
this->loadedFI = new QFileInfo(fileName);
|
|
|
|
//this->formatString = loadedFI.suffix();
|
|
QSize newSize = loadedImage.size().expandedTo(size());
|
|
//resizeImage(&loadedImage, this->outputSize);
|
|
resizeImage(&panelImage, newSize); // resizeImage(&)
|
|
fillCheckered(&panelImage);
|
|
|
|
/// Load the source frame as originalImage (converting to 32-bit):
|
|
originalImage = QImage(loadedImage.size(), QImage::Format_ARGB32);
|
|
originalImage.fill(qRgba(0,0,0,0));
|
|
QPainter bgPainter(&originalImage);
|
|
bgPainter.drawImage(QPoint(0,0), loadedImage); // originalImage = loadedImage;
|
|
|
|
QPainter displayPainter(&panelImage);
|
|
displayPainter.drawImage(QPoint(0,0), originalImage);
|
|
//isModified = false;
|
|
while (layerPtrs.length()>0) {
|
|
RotoCanvasLayer* thisLayer=layerPtrs.takeLast();
|
|
if (thisLayer!=nullptr) delete thisLayer;
|
|
}
|
|
int frameNumber=getSeqFrameNumber();
|
|
qInfo()<<"frameNumber: "<<frameNumber;
|
|
QString layersPath=getLayersFolderPath(frameNumber,false);
|
|
qInfo()<<"layersPath: "<<layersPath;
|
|
QDir layersDir=QDir(layersPath);
|
|
for (int thisLayerNumber=0; thisLayerNumber<layerCount; thisLayerNumber++) {
|
|
//QString thisLayerPath=layersDir.filePath(QString::number(thisLayerNumber)+"."+RotoCanvas::getSeqFormatString(fileName));
|
|
int thisLayerFrameNumber=-1;
|
|
QString thisLayerPath=getLayerImagePathMostRecent(frameNumber,thisLayerNumber,&thisLayerFrameNumber);
|
|
QFileInfo thisLayerFI=QFileInfo(thisLayerPath);
|
|
if (thisLayerPath!="" && thisLayerFI.exists()) {
|
|
//while (thisLayerFI.exists()) {
|
|
qInfo()<<"Found layer file: "<<thisLayerPath;
|
|
//QImage newImage(loadedImage.size(), QImage::Format_ARGB32);
|
|
//QImage* thisLayerImagePtr=new QImage();
|
|
//thisLayerImagePtr->load(thisLayerPath);
|
|
RotoCanvasLayer* thisLayerPtr=new RotoCanvasLayer();
|
|
thisLayerPtr->frameNumber=thisLayerFrameNumber;
|
|
thisLayerPtr->isModified=false;
|
|
thisLayerPtr->image.load(thisLayerPath);
|
|
//TODO: seek backward to get image from previous keyframe
|
|
layerPtrs.append(thisLayerPtr);
|
|
displayPainter.drawImage(QPoint(0, 0), thisLayerPtr->image);
|
|
//thisLayerNumber++;
|
|
//thisLayerPath=layersDir.filePath(QString::number(thisLayerNumber));
|
|
//thisLayerFI=QFileInfo(thisLayerPath);
|
|
//}
|
|
}
|
|
else {
|
|
layerPtrs.append((RotoCanvasLayer*)nullptr);
|
|
qInfo()<<"No layer file: "<<thisLayerPath;
|
|
}
|
|
}
|
|
//bgPainter.drawImage(QPoint(0,0), displayImage);
|
|
this->selectedLayerIndex=0;
|
|
update();
|
|
return true;
|
|
}
|
|
//! [2]
|
|
|
|
//! [3]
|
|
bool RotoCanvas::saveFrame() //saveImage(const QString &fileName, const char *fileFormat)
|
|
//! [3] //! [4]
|
|
{
|
|
// QImage visibleImage = displayImage;
|
|
// resizeImage(&visibleImage, size());
|
|
|
|
// if (visibleImage.save(fileName, fileFormat)) {
|
|
// isModified = false;
|
|
// return true;
|
|
// } else {
|
|
// return false;
|
|
// }
|
|
int thisFrameNumber=getSeqFrameNumber();
|
|
QString layersPath=getLayersFolderPath(thisFrameNumber,true);
|
|
QDir layersDir;
|
|
bool result=true;
|
|
for (int i=0; i<layerCount; i++) {
|
|
if (i<layerPtrs.length()) {
|
|
if (layerPtrs[i]!=nullptr) {
|
|
if (layerPtrs[i]->isModified) {
|
|
layerPtrs[i]->frameNumber=thisFrameNumber; // add a keyframe
|
|
if (!layerPtrs[i]->image.save(layersDir.filePath(QString::number(i)+".png"),"png")) {
|
|
result=false;
|
|
}
|
|
layerPtrs[i]->isModified=false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
bool RotoCanvas::exportFrame(QDir destinationDir, const QString &sequenceName, const char *fileFormat, int frameNumber)
|
|
{
|
|
bool result=false;
|
|
QString formatString=QString(fileFormat).toLower();
|
|
if (this->loadedFI!=nullptr) {
|
|
QString destPath=getFramePath(frameNumber);//destinationDir.filePath(sequenceName+RotoCanvas::getZeroPadded(frameNumber, this->getSeqDigitCount())+"."+QString(fileFormat));
|
|
QImage destImage(originalImage.size(), QImage::Format_ARGB32);
|
|
destImage.fill(qRgba(0,0,0,0));
|
|
QPainter destPainter(&destImage);
|
|
for (int i=0; i<layerCount; i++) {
|
|
if (i<layerPtrs.length()) {
|
|
if (layerPtrs[i]!=nullptr) {
|
|
destPainter.drawImage(0,0,layerPtrs[i]->image);
|
|
}
|
|
}
|
|
}
|
|
//(formatString=="png")?QImage::Format_ARGB32:QImage::Format_RGB888
|
|
result=destImage.save(destPath, fileFormat);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int RotoCanvas::exportFrames(QDir destinationDir, const QString &sequenceName, const char *fileFormat)
|
|
{
|
|
bool result=false;
|
|
int resultCount=0;
|
|
if (this->loadedFI!=nullptr) {
|
|
int frameNumber=0;
|
|
QString framePath=getFramePath(frameNumber);
|
|
QFileInfo frameFI(framePath);
|
|
if (frameFI.exists()) {
|
|
if (!exportFrame(destinationDir, sequenceName, fileFormat, frameNumber)) result=false;
|
|
else resultCount++;
|
|
}
|
|
frameNumber++; //manually advance to 1 in case starts at 1 instead of 0
|
|
while (QFileInfo(getFramePath(frameNumber)).exists()) {
|
|
//while there is an original frame, export (edited) frame
|
|
QFileInfo frameFI=QFileInfo(getFramePath(frameNumber));
|
|
if (!exportFrame(destinationDir, sequenceName, fileFormat, frameNumber)) result=false;
|
|
else resultCount++;
|
|
frameNumber++;
|
|
}
|
|
}
|
|
return resultCount;
|
|
}
|
|
//! [4]
|
|
|
|
//! [5]
|
|
void RotoCanvas::setBrushColor(const QColor &newColor)
|
|
//! [5] //! [6]
|
|
{
|
|
brushColor = newColor;
|
|
}
|
|
//! [6]
|
|
|
|
//! [7]
|
|
void RotoCanvas::setBrushRadius(double newWidth)
|
|
//! [7] //! [8]
|
|
{
|
|
double temp_hardness = getBrushHardness();
|
|
brushRadius = newWidth;
|
|
setBrushHardness(temp_hardness); //repair brushHardRadius
|
|
}
|
|
//! [8]
|
|
|
|
void RotoCanvas::setBrushHardness(double new_value)
|
|
{
|
|
if (new_value>1.0) new_value=1.0;
|
|
else if (new_value<0.0) new_value=0.0f;
|
|
brushHardRadius = brushRadius-(brushRadius*(1.0-new_value));
|
|
}
|
|
|
|
void RotoCanvas::setBrushOpacity(double new_value)
|
|
{
|
|
if (new_value>1.0) new_value=1.0;
|
|
else if (new_value<0.0) new_value=0.0f;
|
|
brushOpacity = new_value;
|
|
}
|
|
|
|
//! [9]
|
|
void RotoCanvas::clearImage()
|
|
//! [9] //! [10]
|
|
{
|
|
panelImage.fill(qRgba(255, 255, 255, 255));
|
|
update();
|
|
}
|
|
//! [10]
|
|
|
|
//! [11]
|
|
void RotoCanvas::mousePressEvent(QMouseEvent *event)
|
|
//! [11] //! [12]
|
|
{
|
|
if (event->button() == Qt::LeftButton) {
|
|
lastPoint = event->pos();
|
|
isToolActive = true;
|
|
}
|
|
}
|
|
|
|
void RotoCanvas::mouseMoveEvent(QMouseEvent *event)
|
|
{
|
|
if ((event->buttons() & Qt::LeftButton) && isToolActive)
|
|
drawLineTo(event->pos());
|
|
}
|
|
|
|
void RotoCanvas::mouseReleaseEvent(QMouseEvent *event)
|
|
{
|
|
if (event->button() == Qt::LeftButton && isToolActive) {
|
|
drawLineTo(event->pos());
|
|
isToolActive = false;
|
|
}
|
|
}
|
|
|
|
//! [12] //! [13]
|
|
void RotoCanvas::paintEvent(QPaintEvent *event)
|
|
//! [13] //! [14]
|
|
{
|
|
QPainter painter(this);
|
|
QRect dirtyRect = event->rect();
|
|
painter.drawImage(dirtyRect, panelImage, dirtyRect);
|
|
}
|
|
//! [14]
|
|
|
|
//! [15]
|
|
void RotoCanvas::resizeEvent(QResizeEvent *event)
|
|
//! [15] //! [16]
|
|
{
|
|
if (width() > panelImage.width() || height() > panelImage.height()) {
|
|
int newWidth = qMax(width() + 128, panelImage.width());
|
|
int newHeight = qMax(height() + 128, panelImage.height());
|
|
resizeImage(&panelImage, QSize(newWidth, newHeight));
|
|
QPainter painter(&panelImage);
|
|
painter.drawImage(QPoint(0,0), originalImage);
|
|
update();
|
|
}
|
|
QWidget::resizeEvent(event);
|
|
}
|
|
//! [16]
|
|
|
|
//! [17]
|
|
void RotoCanvas::drawLineTo(const QPoint &endPoint)
|
|
//! [17] //! [18]
|
|
{
|
|
if (outputSize.width()>0&&outputSize.height()>0) {
|
|
if (selectedLayerIndex>=0) {
|
|
//qInfo()<<"Draw line on layer "<<selectedLayerIndex<<"...";
|
|
QString layersPath=getLayersFolderPath(getSeqFrameNumber(),true);
|
|
QDir layersDir=QDir(layersPath);
|
|
int thisLayerNumber=selectedLayerIndex;
|
|
QString thisLayerPath=layersDir.filePath(QString::number(thisLayerNumber)+"."+RotoCanvas::getSeqFormatString(*this->loadedFI));
|
|
QFileInfo thisLayerFI=QFileInfo(thisLayerPath);
|
|
//qInfo()<<"Layer list length is "<<layerPtrs.length();
|
|
while (this->layerPtrs.length()<selectedLayerIndex+1) {
|
|
layerPtrs.append((RotoCanvasLayer*)nullptr);
|
|
//qInfo()<<"Had to add blank layer (now list length is "<<layerPtrs.length()<<")";
|
|
}
|
|
if (this->layerPtrs[selectedLayerIndex]==nullptr) {
|
|
RotoCanvasLayer* newLayer;
|
|
newLayer = new RotoCanvasLayer;
|
|
newLayer->frameNumber=getSeqFrameNumber();
|
|
newLayer->image=QImage(outputSize, QImage::Format_ARGB32);
|
|
this->layerPtrs[selectedLayerIndex]=newLayer; // layerPtrs.append(newLayer);
|
|
}
|
|
//qInfo()<<"this->layerPtrs[selectedLayerIndex]: "<<((this->layerPtrs[selectedLayerIndex]!=nullptr)?"<non-null>":"nullptr");
|
|
if (this->layerPtrs[selectedLayerIndex]!=nullptr) {
|
|
//qInfo()<<"this->layerPtrs[selectedLayerIndex].image.isNull(): "<<((this->layerPtrs[selectedLayerIndex]->image.isNull())?"Null":"non-Null");
|
|
QPainter painter(&this->layerPtrs[selectedLayerIndex]->image);
|
|
//QPainter painter(&panelImage);
|
|
int rad = (brushRadius) + 2;
|
|
QRect rectBrush = QRect(lastPoint, endPoint).normalized()
|
|
.adjusted(-rad, -rad, +rad, +rad);
|
|
//QPen pen;
|
|
//pen.setStyle(Qt::SolidLine);
|
|
//pen.setWidth(1);
|
|
//pen.setColor(brushColor);
|
|
QColor thisColor;
|
|
|
|
QPoint thisPoint;
|
|
//qInfo()<<"this->brushOpacity: "<<this->brushOpacity;
|
|
//painter.setPen(brushColor);
|
|
//brushColor.setAlpha(this->brushOpacity); // sets to GRAY
|
|
if (this->brushOpacity<0.0) this->brushOpacity=0.0;
|
|
else if (this->brushOpacity>255.0) this->brushOpacity=255.0;
|
|
int opacity_int = (int)(this->brushOpacity*255.0+.5); //+.5 for rounding
|
|
brushColor = QColor(brushColor.red(),brushColor.green(),brushColor.blue(),opacity_int);
|
|
//qInfo()<<"brushColor r,g,b,a: "<< brushColor.red()<<","<<brushColor.green()<<","<<brushColor.blue()<<","<<brushColor.alpha();
|
|
for (int y=rectBrush.top(); y<rectBrush.bottom(); y++) {
|
|
for (int x=rectBrush.left(); x<rectBrush.right(); x++) {
|
|
//todo: distance from line instead of points
|
|
if (x>=0 && y>=0) {
|
|
//thisPoint.setX(x);
|
|
//thisPoint.setY(y);
|
|
double this_distance = sqrt(pow(endPoint.x()-x, 2) + pow(endPoint.y()-y, 2));
|
|
double fade_length = brushRadius-brushHardRadius;
|
|
double this_opacity = (fade_length>0.0) ? ((brushRadius-this_distance) / fade_length) : ((brushRadius-this_distance) / 0.000001) ;
|
|
if (this_opacity>1.0) this_opacity=1.0;
|
|
else if (this_opacity<0.0) this_opacity=0.0;
|
|
if (selectedLayerIndex>=0&&layerPtrs[selectedLayerIndex]!=nullptr) {
|
|
RotoCanvas::drawAlphaPix(&panelImage, x, y, this->brushColor, this_opacity);
|
|
RotoCanvas::drawAlphaPix(&layerPtrs[selectedLayerIndex]->image, x, y, this->brushColor, this_opacity);
|
|
layerPtrs[selectedLayerIndex]->isModified=true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//isModified = true;
|
|
update(rectBrush);
|
|
lastPoint = endPoint;
|
|
}
|
|
else {
|
|
qInfo()<<"image "<<selectedLayerIndex<<" on layer list is null";
|
|
}
|
|
}
|
|
else qInfo()<<"no layer is selected";
|
|
}
|
|
else qInfo()<<"a dimension is 0--no video loaded";
|
|
}
|
|
|
|
void RotoCanvas::fillCheckered(QImage *thisImage)
|
|
{
|
|
int checkerPxCount=((thisImage->width()>thisImage->height())?thisImage->width():thisImage->height()) / 200;
|
|
if (checkerPxCount<8) checkerPxCount=8;
|
|
if (thisImage!=nullptr) {
|
|
//QSize newSize=thisImage->size();
|
|
thisImage->fill(this->checkerDarkColor); //newImage.fill(qRgb(255, 255, 255));
|
|
for (int y=0; y<thisImage->height(); y++) {
|
|
for (int x=0; x<thisImage->width(); x++) {
|
|
//debug optimization: try lines instead of pixel
|
|
if ((y/checkerPxCount)%2==1) {
|
|
if ((x/checkerPxCount)%2==1) thisImage->setPixelColor(x, y, this->checkerLightColor);
|
|
}
|
|
else {
|
|
if (((x/checkerPxCount)+1)%2==1) thisImage->setPixelColor(x, y, this->checkerLightColor);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else qDebug()<<"fillCheckered Error: thisImage is nullptr";
|
|
}
|
|
//! [18]
|
|
|
|
//! [19]
|
|
void RotoCanvas::resizeImage(QImage *image, const QSize &newSize)
|
|
//! [19] //! [20]
|
|
{
|
|
//qInfo()<<"resizing...";
|
|
if (image->size() == newSize)
|
|
return;
|
|
|
|
QImage newImage(newSize, QImage::Format_ARGB32);
|
|
//fillCheckered(&newImage);
|
|
|
|
QPainter painter(&newImage);
|
|
painter.drawImage(QPoint(0, 0), *image);
|
|
*image = newImage;
|
|
//qInfo()<<"done (resizing)";
|
|
}
|
|
|
|
QImage RotoCanvas::getCacheableImage(QString filePath)
|
|
{
|
|
//TODO: load from cache instead if possible
|
|
QImage thisImage(filePath);
|
|
return thisImage;
|
|
}
|
|
//! [20]
|
|
|
|
//! [21]
|
|
void RotoCanvas::print()
|
|
{
|
|
#if !defined(QT_NO_PRINTER) && !defined(QT_NO_PRINTDIALOG)
|
|
QPrinter printer(QPrinter::HighResolution);
|
|
|
|
QPrintDialog printDialog(&printer, this);
|
|
//! [21] //! [22]
|
|
if (printDialog.exec() == QDialog::Accepted) {
|
|
QPainter painter(&printer);
|
|
QRect rect = painter.viewport();
|
|
QSize size = panelImage.size();
|
|
size.scale(rect.size(), Qt::KeepAspectRatio);
|
|
painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
|
|
painter.setWindow(panelImage.rect());
|
|
painter.drawImage(0, 0, panelImage);
|
|
}
|
|
#endif // QT_NO_PRINTER
|
|
}
|
|
//! [22]
|