575 lines
18 KiB
C++
575 lines
18 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;
|
|
//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, completeBaseName gets file.tar
|
|
result=baseName;
|
|
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) 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);
|
|
QString result=baseName.right(digitCount);
|
|
return result;
|
|
}
|
|
|
|
QString RotoCanvas::getSeqPaddedFrameNumber()
|
|
{
|
|
if (this->loadedFI!=nullptr) 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=frameFI.dir().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)
|
|
{
|
|
//QString result;
|
|
QFileInfo frameFI(framePath);
|
|
return RotoCanvas::getLayerImagePathMostRecent(frameFI, frameNumber, createEnable);
|
|
}
|
|
|
|
QString RotoCanvas::getLayerImagePathMostRecent(QFileInfo frameFI, int frameNumber, int layerNumber)
|
|
{
|
|
QString result="";
|
|
QString layersPath=RotoCanvas::getLayersFolderPath(frameFI,frameNumber,false);
|
|
QDir layersDir(layersPath);
|
|
QString thisLayerImagePath=layersDir.filePath(QString::number(layerNumber)+".png");
|
|
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)+".png");
|
|
thisLayerImageFI=QFileInfo(thisLayerImagePath);
|
|
thisFrameNumber--;
|
|
}
|
|
if (thisLayerImageFI.exists()) result=thisLayerImageFI.path();
|
|
return result;
|
|
}
|
|
|
|
QString RotoCanvas::getLayerImagePathMostRecent(int frameNumber, int layerNumber)
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getLayerImagePathMostRecent(*this->loadedFI, frameNumber, layerNumber);
|
|
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);
|
|
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()
|
|
{
|
|
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=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::getSeqFramePath(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::getSeqFramePath(int frameNumber)
|
|
{
|
|
if (this->loadedFI!=nullptr) return RotoCanvas::getSeqFramePath(this->loadedFI->dir().path(), getSeqName(),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;
|
|
}
|
|
//! [0]
|
|
|
|
//! [1]
|
|
bool RotoCanvas::openImage(const QString &fileName)
|
|
//! [1] //! [2]
|
|
{
|
|
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(&displayImage, newSize); //resizeImage(&)
|
|
//backgroundImage = loadedImage;
|
|
backgroundImage = QImage(loadedImage.size(),QImage::Format_RGB32);
|
|
|
|
backgroundImage.fill(Qt::transparent);
|
|
QPainter bgPainter(&backgroundImage);
|
|
bgPainter.drawImage(QPoint(0,0),loadedImage);
|
|
|
|
QPainter painter(&displayImage);
|
|
|
|
isModified = false;
|
|
while (layerPtrs.length()>0) {
|
|
RotoCanvasLayer* thisLayer=layerPtrs.takeLast();
|
|
if (thisLayer!=nullptr) delete thisLayer;
|
|
}
|
|
QString layersPath=getLayersFolderPath(getSeqFrameNumber(),false);
|
|
QDir layersDir=QDir(layersPath);
|
|
int thisLayerNumber=0;
|
|
QString thisLayerPath=layersDir.filePath(QString::number(thisLayerNumber)+".png");
|
|
QFileInfo thisLayerFI=QFileInfo(thisLayerPath);
|
|
int thisLayerFrameNumber=getSeqFrameNumber();
|
|
while (thisLayerFI.exists()) {
|
|
qInfo()<<"Found layer file: "<<thisLayerPath;
|
|
//QImage newImage(loadedImage.size(), QImage::Format_RGB32);
|
|
//QImage* thisLayerImagePtr=new QImage();
|
|
//thisLayerImagePtr->load(thisLayerPath);
|
|
RotoCanvasLayer* thisLayerPtr=new RotoCanvasLayer();
|
|
thisLayerPtr->frameNumber=thisLayerFrameNumber;
|
|
thisLayerPtr->image.load(thisLayerPath);
|
|
//TODO: seek backward to get image from previous keyframe
|
|
layerPtrs.append(thisLayerPtr);
|
|
painter.drawImage(QPoint(0, 0), thisLayerPtr->image);
|
|
thisLayerNumber++;
|
|
thisLayerPath=layersDir.filePath(QString::number(thisLayerNumber));
|
|
thisLayerFI=QFileInfo(thisLayerPath);
|
|
}
|
|
this->selectedLayerIndex=0;
|
|
update();
|
|
return true;
|
|
}
|
|
//! [2]
|
|
|
|
//! [3]
|
|
bool RotoCanvas::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;
|
|
}
|
|
}
|
|
//! [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]
|
|
{
|
|
displayImage.fill(qRgb(255, 255, 255));
|
|
isModified = true;
|
|
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, displayImage, dirtyRect);
|
|
}
|
|
//! [14]
|
|
|
|
//! [15]
|
|
void RotoCanvas::resizeEvent(QResizeEvent *event)
|
|
//! [15] //! [16]
|
|
{
|
|
if (width() > displayImage.width() || height() > displayImage.height()) {
|
|
int newWidth = qMax(width() + 128, displayImage.width());
|
|
int newHeight = qMax(height() + 128, displayImage.height());
|
|
resizeImage(&displayImage, QSize(newWidth, newHeight));
|
|
update();
|
|
}
|
|
QWidget::resizeEvent(event);
|
|
}
|
|
//! [16]
|
|
|
|
//! [17]
|
|
void RotoCanvas::drawLineTo(const QPoint &endPoint)
|
|
//! [17] //! [18]
|
|
{
|
|
if (outputSize.width()>0&&outputSize.height()>0) {
|
|
if (selectedLayerIndex>=0) {
|
|
QString layersPath=getLayersFolderPath(getSeqFrameNumber(),true);
|
|
QDir layersDir=QDir(layersPath);
|
|
int thisLayerNumber=selectedLayerIndex;
|
|
QString thisLayerPath=layersDir.filePath(QString::number(thisLayerNumber)+".png");
|
|
QFileInfo thisLayerFI=QFileInfo(thisLayerPath);
|
|
|
|
while (layerPtrs.length()<selectedLayerIndex+1) {
|
|
RotoCanvasLayer* newLayer;
|
|
newLayer = new RotoCanvasLayer;
|
|
newLayer->frameNumber=getSeqFrameNumber();
|
|
newLayer->image=QImage(outputSize, QImage::Format_RGB32);
|
|
layerPtrs.append(newLayer);
|
|
}
|
|
QPainter painter(&displayImage);
|
|
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);
|
|
painter.setPen(brushColor);
|
|
QColor thisColor;
|
|
|
|
QPoint thisPoint;
|
|
brushColor.setAlpha(this->brushOpacity);
|
|
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;
|
|
QColor destColor = displayImage.pixelColor(x,y);
|
|
//do alpha formula (byte)((source-dest)*alpha/255.0f+dest+.5f)
|
|
int r = (int)((this->brushColor.red()-destColor.red())*this_opacity+destColor.red()+.5); //+.5 for rounding
|
|
int g = (int)((this->brushColor.green()-destColor.green())*this_opacity+destColor.green()+.5); //+.5 for rounding
|
|
int b = (int)((this->brushColor.blue()-destColor.blue())*this_opacity+destColor.blue()+.5); //+.5 for rounding
|
|
int a = (int)((this->brushColor.alpha()-destColor.alpha())*this_opacity+destColor.alpha()+.5); //+.5 for rounding
|
|
thisColor.setRed(r);
|
|
thisColor.setGreen(g);
|
|
thisColor.setBlue(b);
|
|
thisColor.setAlpha(a);
|
|
displayImage.setPixelColor(x, y, thisColor);
|
|
//painter.setOpacity(this_opacity);
|
|
//painter.drawPoint(x,y);
|
|
}
|
|
}
|
|
}
|
|
isModified = true;
|
|
update(rectBrush);
|
|
lastPoint = endPoint;
|
|
}
|
|
//else no layer is selected
|
|
}
|
|
//else a dimension is 0--no video loaded
|
|
}
|
|
|
|
void RotoCanvas::fillCheckered(QImage *thisImage)
|
|
{
|
|
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%2==1) {
|
|
if (x%2==1) thisImage->setPixelColor(x, y, this->checkerLightColor);
|
|
}
|
|
else {
|
|
if ((x+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]
|
|
{
|
|
if (image->size() == newSize)
|
|
return;
|
|
|
|
QImage newImage(newSize, QImage::Format_RGB32);
|
|
fillCheckered(&newImage);
|
|
|
|
QPainter painter(&newImage);
|
|
painter.drawImage(QPoint(0, 0), *image);
|
|
*image = newImage;
|
|
}
|
|
|
|
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 = displayImage.size();
|
|
size.scale(rect.size(), Qt::KeepAspectRatio);
|
|
painter.setViewport(rect.x(), rect.y(), size.width(), size.height());
|
|
painter.setWindow(displayImage.rect());
|
|
painter.drawImage(0, 0, displayImage);
|
|
}
|
|
#endif // QT_NO_PRINTER
|
|
}
|
|
//! [22]
|