-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathrenderthread.cpp
More file actions
115 lines (99 loc) · 5.82 KB
/
renderthread.cpp
File metadata and controls
115 lines (99 loc) · 5.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
// Copyright 2023 by Linwood Ferguson, licensed under GNU GPLv3
#include <QPainter>
#include <QDebug>
#include "renderthread.h"
#include "pdfdocument.h"
#include "mainwindow.h"
#include "oursettings.h"
#include <cassert>
#include <cmath>
#include "piconstants.h"
#include "poppler/qt6/poppler-qt6.h"
renderThread::renderThread(PDFDocument *parent, int which, MainWindow* mp ): QThread(parent)
{
// This routine is used for actual graphics page rendering, in separate threads
// Note constructor is running in the parent thread
qDebug() << "in constructor for thread " << which;
abort = false;
running = false; // Used as a code logic sanity check, this does not control the loop, just aborts if inconsistent
ourParent = parent; // the PDF object
mParent = mp; // MainWindow object
mWhich = which; // the number of the thread array this is attached to
mWidth = 0; // the target width we were asked to scale to (don't do anything with it zero)
mHeight = 0; // the target height we were asked to scale to (don't do anything with it zero)
pageHighlightHeight = mParent->ourSettingsPtr->getSetting("pageHighlightHeight").toInt();
// mutex.unlock(); // make sure we are in an unlocked state
}
renderThread::~renderThread()
{
qDebug() << "In destructor";
abort=true;
condition.wakeOne();
qDebug() << "Entering wait";
wait(); // Since constructor/destructor are in the parent thread, this waits for the worker thread to exit before the base class destructor is called;
// mutex.unlock(); // Why is this needed? Otherwise it gives warnings
qDebug() << "Wait finished, leaving destructor";
}
void renderThread::render(QImage** image, int thePage, int maxWidth, int maxHeight)
{
// This is also run in the parent thread -- it starts a render
// Note that we never do another render call until any running one is done, so in
// theory here running has to be false, check it with the assert, since if it fails we just screwed up the logic.
mutex.lock(); // just in case
assert(!running);
// Our local variables (because of the above) ar valid in thread and parent, since we synchronize with the parent's
// tracking of running state (not the "running" variable) so we don't need a mutex lock here.
targetImagePtr = image;
mPage = thePage;
mWidth = maxWidth;
mHeight = maxHeight;
if(this->isRunning()) condition.wakeOne(); // thread is sleeping (since !running) so wake it up.
else start(QThread::LowPriority); // first time through start thread
mutex.unlock();
return;
}
void renderThread::run()
{
forever
{
running = true;
qDebug()<<"Opening PDF document inside of thread now " << ourParent->filepath;
document = Poppler::Document::load(ourParent->filepath);
document->setRenderBackend(MUSICALPI_POPPLER_BACKEND);
assert(document && !document->isLocked());
std::unique_ptr<Poppler::Page> tmpPage = document->page(mPage - 1);
assert(tmpPage!=NULL);
QSizeF thisPageSize = tmpPage->pageSizeF(); // in 72's of inch
double scaleX = (double)mWidth / ((double)thisPageSize.width() / (double)72.0);
double scaleY = (double)mHeight / ((double)thisPageSize.height() / (double)72.0);
double desiredScale = std::trunc(std::min(scaleX, scaleY)); // For notational scores integers seem to give better alignment, sometimes.
qDebug() << "Starting render on thread " << mWhich << " id " << currentThreadId() << " for page " << mPage << ", pt size " << thisPageSize.width() << "x" << thisPageSize.height() << " at scale " << desiredScale << " targeting " << mWidth << "x" << mHeight;
document->setRenderHint(Poppler::Document::Antialiasing, true); // Note you can't ignore paper color as some PDF's apparently come up black backgrounds
document->setRenderHint(Poppler::Document::TextAntialiasing, true);
document->setRenderHint(Poppler::Document::TextHinting, false);
document->setRenderHint(Poppler::Document::OverprintPreview, false);
document->setRenderHint(Poppler::Document::ThinLineSolid,true);
QImage* theImage = new QImage(tmpPage->renderToImage(desiredScale,desiredScale));
assert(theImage);
qDebug() << "Page " << mPage << " was rendered on thread " << mWhich << " produced size " << theImage->width() << "x" << theImage->height();
{ // Put in a block so it will remove the painter and not leave it attached to the passed-out QImage
QPainter painter(theImage);
painter.setFont(QFont("Arial", QString(MUSICALPI_SETTINGS_PAGENUMBER_FONT_SIZE).replace("px","").toInt(), 1, false)); // This breaks if we aren't using pixels ???
painter.setPen(QColor("green"));
painter.drawText(QPoint(pageHighlightHeight + 10,pageHighlightHeight + 20),QString("%1").arg(mPage)); // extra space is room for number, in addition to highlight
}
// critical section: This interlock is with the parent thread for returning the image; the parent records we are not running afterwards
ourParent->lockOrUnlockMutex(true);
// not needed to delete as managed now >> DELETE_LOG(document); // also closes this copy
*targetImagePtr = theImage;
emit renderedImage( mWhich, mPage, mWidth, mHeight);
mutex.lock();
running = false; // Mark we were done - note we do this inside parent's critical section AND this thread's to prevent race conditions
ourParent->lockOrUnlockMutex(false);
// End of critical section
if(abort) { qDebug() << "Returning with abort"; return; } // will hit this one first if requested while running but
condition.wait(&mutex);
mutex.unlock();
if(abort) { qDebug() << "Returning with abort"; return; }; // will hit this one if requested while not running
}
}